After using the "interrupt driven" I2C driver (ii2c) intensively on a MCF52259, in master-only mode, I had the following problems :
- the task doing I2C accesses heavily was consuming nearly all CPU time
- the bus sometimes hanged but as no timeout was supported by the driver, this was fatal
After some investigation I discovered that :
- the driver was not fully interrupt driven : even if data is received or sent in interrupt mode using internal buffer, this is not the
case when you call IO_IOCTL_FLUSH_OUTPUT ioctl (or fflush()) where polling at full speed is performed; as also does IO_IOCTL_I2C_STOP ioctl. As a result :
- when you want to receive some data, you have to call read() multiple times until some data is available, which is actually a polling if you do that in a loop as in MQX I2C examples (you do nothing else except calling read())
- when you send some data and wait for the end of transfer before going further you have to call IO_IOCTL_FLUSH_OUTPUT but this call also does full speed polling.
- there"s no timeout for polling in IO_IOCTL_FLUSH_OUTPUT nor IO_IOCTL_I2C_STOP so this can be deathlock. I had the problem when probing some temperature sensors on which power supply was removed at any time (including during a transfer), while MCF52259 remained powered.
To solve that, I made the following changes to the I2C driver :
- added support for sleeping in read() / write() / IO_IOCTL_FLUSH_OUTPUT calls, and removed polling in IO_IOCTL_I2C_STOP
- when read() is called the task sleeps in this call until all requested data has been received or RX buffer full
- write() call only sleeps if the amount of data to be writtent is bigger than the buffer.
- IO_IOCTL_FLUSH_OUTPUT ioctl (or fflush()) sleeps until data has been transfered
- in any case check the return value to know the amount of data processed or if a timeout occured.
- added support for a settable timeout (default 20mS) for these calls, using two new ioctls : IO_IOCTL_I2C_SET_TIMEOUT_MS and IO_IOCTL_I2C_GET_TIMEOUT_MS. In cas of timeout :
- read() and write() return -IO_ERROR_TIMEOUT (negative value; easier than returing IO_ERROR and setting errno to IO_ERROR_TIMEOUT)
- IO_IOCTL_FLUSH_OUTPUT returns IO_ERROR_TIMEOUT (positive value)
Below is a zip file containing modified files for MQX 3.7 under mqx/source/io/i2c
WARNING : this breaks support for slave mode I2C : the interrupt handler has to be modified for its slave part to support these changes and I did not have the opprtunity to do that (however this should be very simple). So only use this zip if you use I2C as master-only.
NOTE : in case of timeout, if you retry and also get another timeout, the bus is probably hang. You may unlock the bus as described in this post :
- add the required code to support slave-only mode
- fix the code to support both master and slave mode at the same time : this may require to use separate buffers for master and slave, separate semaphore. TBD : use same file descriptor for master and slave or open ii2c device twice ?