That's probably the best example we have. There is also one for MPR121 and a port of an Arduino library for an IMU integrated with libpd on Bela. Another I2C application is Bela's audio codec driver.
Ultimately, there is nothing Bela-specific to how I2C works on Bela: it just leverages the Linux driver which exposes the I2C bus in
/dev/i2c*, does one
ioctl() to set the device address, then calls
write() on it. As long as that all happens in a dedicated thread (doesn't have to be a Bela
AuxiliaryTask*), this is completely generic Linux. What could become useful on RT Linux systems (e.g.: Bela) is a way of safely exchanging data between an I/O thread and a RT thread (see below).
A thing with I2C is that not all devices behave the same in terms of how to interact with them. The I2C protocol provides the transport, addressing and arbitration, but it does not specify what the bytes mean. Many devices (e.g.: Bela audio codec), for instance, expect that the first byte written by the host in each transaction determines the memory location to which the successive bytes in the same transmission will be read from / written to. Some others (e.g.: Trill) do not work that way: you first do a transaction where you write once to give it the memory address you are interested in, then you keep reading from the device without writing the address any more. The first behaviour is the one the
writeRegister() functions in
I2c_Codec.cpp rely upon and it's probably common enough that it would be worth having it as a library function.
I made an attempt a few years ago at exposing decently complete I2C functionalities that would be accessible from a RT thread. This used two lock-free ringbuffers to communicate between the RT thread and another thread which would actually do the processing (again, all of this was Bela-independent). I also integrated into Bela's libpd so that one could use Pd to control an I2C device in a RT-safe way. See here (recently rebased): https://github.com/BelaPlatform/Bela/tree/better-i2c-rebased-2020
* a Bela
AuxiliaryTask is just a wrapper for a Xenomai wrapper for a
pthread which gets managed internally so that:
- you don't have to destroy/join it
- the callback runs in a loop which waits on a condition variable signalled from the audio thread
The convenience of it is that it can be
scheduled from the audio thread with minimal overhead in a RT-safe way and it can be assigned a "Xenomai" priority which is powerful for keeping DSP co-routines (e.g.: FFT processing) at high priority. However, in the case where it is used for I/O, given how you are going through the Linux driver, its priority becomes much less relevant, in that it will still have to revert to the Linux scheduler in order to access the driver. In many cases, having a
std::thread with a
usleep in there would yield a very similar behaviour, while being more portable.