[STM32] I2C HAL driver problems and fixes
Posted: Sat Jul 28, 2012 9:17 pm
Hello Everyone,
I was playing around with my custom board and ChibiOS since that OS is everything I ever wanted from RTOS. First thing I was trying to run is I2C communication since I've got bunch of devices attached to that interface. And it wasn't working. The I2C HAL driver seamed to stop (hang) on reading, not receiving any "useful" event in i2c_lld_serve_event_interrupt().
Since I2C implementation in STM32, especially the F1 series, had a really bad reputation as far as my field of interests goes (namely UAV, so a lot of I2C communication to sensors), I went on to investigate, looking for other implementations of I2C driver for STM32, and the most convoluted one found in OpenPilot, had a workaround for probably every single strange event they got on I2C ISR handler, half of them leading to transaction repeat. And they are not using DMA...
There was something strange with that and as I'm a kind of person that reads Reference Manual, I found a lot of warnings in STM32F4 RM about ADDR flag that is reset on read of SR2. The same kind of warnings show up in RM for STM32F1. The funny thing is that, according to manual, controlling the resetting of ADDR flag is required for properly handling SR1_ADDR and SR1_ADD10 flags in ISR, and ADDR flag is cleared automatically after reading SR2 after reading SR1. And every single implementation I saw (including the one in ChibiOS and the STM32 peripheral library) uses so called "event" made up of SR1 and SR2 to define the state. And the event is constructed by reading SR1 and SR2 in sequence, which clears ADDR flag before properly configuring transfer (ie. clearing CR2_ITEVTEN and enabling DMA).
The workaround was to just read SR2 before reading SR1 - ADDR was not cleared and it was possible to clear ADDR after transfer is properly setup (enabled DMA stream).
The other problem with ChibiOS I2C driver that I found reading RM is clearing of CR1_ACK flag and setting of CR1_POS flag depending on the size of transaction. Namely I2C on STM32F4 (and F1 AFAIK) requires that for receiving of 2 or less bytes to clear ACK bit in CR1, and when receiving excacly 2 bytes to set POS flag in CR1.
After applying above modifications to I2C HAL driver in ChibiOS I was able to run I2C transactions one after another for a whole week with a speed of 300kHz (I have too strong pull-ups, need to resolder them
) with no hang nor lockup nor error.
See the attached patch for making I2C reliable on STM32F4.
I was playing around with my custom board and ChibiOS since that OS is everything I ever wanted from RTOS. First thing I was trying to run is I2C communication since I've got bunch of devices attached to that interface. And it wasn't working. The I2C HAL driver seamed to stop (hang) on reading, not receiving any "useful" event in i2c_lld_serve_event_interrupt().
Since I2C implementation in STM32, especially the F1 series, had a really bad reputation as far as my field of interests goes (namely UAV, so a lot of I2C communication to sensors), I went on to investigate, looking for other implementations of I2C driver for STM32, and the most convoluted one found in OpenPilot, had a workaround for probably every single strange event they got on I2C ISR handler, half of them leading to transaction repeat. And they are not using DMA...
There was something strange with that and as I'm a kind of person that reads Reference Manual, I found a lot of warnings in STM32F4 RM about ADDR flag that is reset on read of SR2. The same kind of warnings show up in RM for STM32F1. The funny thing is that, according to manual, controlling the resetting of ADDR flag is required for properly handling SR1_ADDR and SR1_ADD10 flags in ISR, and ADDR flag is cleared automatically after reading SR2 after reading SR1. And every single implementation I saw (including the one in ChibiOS and the STM32 peripheral library) uses so called "event" made up of SR1 and SR2 to define the state. And the event is constructed by reading SR1 and SR2 in sequence, which clears ADDR flag before properly configuring transfer (ie. clearing CR2_ITEVTEN and enabling DMA).
The workaround was to just read SR2 before reading SR1 - ADDR was not cleared and it was possible to clear ADDR after transfer is properly setup (enabled DMA stream).
The other problem with ChibiOS I2C driver that I found reading RM is clearing of CR1_ACK flag and setting of CR1_POS flag depending on the size of transaction. Namely I2C on STM32F4 (and F1 AFAIK) requires that for receiving of 2 or less bytes to clear ACK bit in CR1, and when receiving excacly 2 bytes to set POS flag in CR1.
After applying above modifications to I2C HAL driver in ChibiOS I was able to run I2C transactions one after another for a whole week with a speed of 300kHz (I have too strong pull-ups, need to resolder them
See the attached patch for making I2C reliable on STM32F4.