This post is a tutorial post on implementing an I2C driver. The post is a reply to @NicoMiguel10 's question asking how to create a driver for the AS5600 angular position sensor. I’ve included some resources I’ve found very helpful when building a similar driver, but I am by no means an expert – I’d appreciate any feedback or additional tips folks have. Hopefully this can hope other new developers find the resources they need!
Sensors like the AS5600 or AS5048B use the I2C serial protocol to communicate with the flight controller. If you aren’t familiar with interfacing with sensors via I2C, I recommend reading this brief and informative tutorial. The tutorial explains I2C basics such as reading from and writing to registers in order to get or set data on a connected device.
To read an angular sensor, your driver should:
- Create an
AP_HAL::I2CDevicewith the correct address. For the AS5600, this address is available in the datasheet on page 10. It is 0x36. Sometimes, this address can be changed with physical jumpers.
- First set the zero position if necessary by writing
0x00to the registers corresponding to the angle position.
- Repeatedly read the angle from the registers corresponding to the angle position.
I was fortunate enough to find an example Python sketch on an online forum where someone did these steps for the AS5048B. Though it is possible to piece together these steps solely by reading the datasheet, it is very, very helpful to see someone else’s implementation so that you know which values to look for. For instance, after looking at the code, I knew that I should search in the datasheet to verify the slave device address and the addresses of the registers holding the angle. For the AS5048, I found this information on pages 24 and 25. Lo and behold, they were the same as in the Python sketch. I believe that referencing publicly available code to build your own driver is consistent with Ardupilot’s license and developer Code of Conduct.
It looks like there is at least one existing open-source driver for the AS5600 you can reference. By looking at the addresses defined in the header file and seeing how they’re used in the angle- and status-related methods and in the examples, you can decide what functionality you want to build into your driver and what items you want to double-check in the datasheet.
The above information should help you determine how to interact with the device. However, there’s a bit more to be done to fully integrate it with Ardupilot. Sensor drivers in Ardupilot employ a frontend/backend split. This means that, for instance, a call to
AP_Airspeed::update() will internally query all of the available Airspeed backends – instances of classes with names like
AP_Airspeed_SD3PX which inherit from the
AP_Airspeed_Backend class. To be honest, I’m a bit shaky on this aspect of Ardupilot’s organization so I simply made sure to model my Windvane code on other drivers in the library.
An additional stumbling block for me was the necessity to request and release the bus semaphore. This is a step that is necessary to ensure that drivers do not attempt to simultaneously access the shared I2C resource given Ardupilot’s multi-threaded architecture. While looking through different libraries, I saw lots of different ways to do this – some with concise and incomprehensible macros, some with
sem->take_nonblocking , and some with
take(x) . I have not found a satisfying source of documentation on these and would certainly defer to anyone who actually understands multithreading.
I hope this was helpful! I recommend studying existing libraries and modeling your code after those. There are plenty to go around. If you have not already, you should definitely try to interface with the sensor via a Raspberry Pi or Arduino so you can be certain the registers are correct and the device is functioning prior to writing a driver, so you can work iteratively.