Recently I have been annoyed with bugs in the I2C implementation in ST's F1/L1/F40/F2 series, and any microcontroller made by ST before 2012. There appears to be, what I can only describe as, a race condition when attempting to receive data in I2C interrupt(low priority) or polling mode.
Just to be clear a race condition is defined as , verbatim from wiki:
"the behavior of an electronics, software, or other system where the system's substantive behavior is dependent on the sequence or timing of other uncontrollable events. It becomes a bug when one or more of the possible behaviors is undesirable."
I was humming along trying to make my YouTube tutorials when I arrived to the I2C protocol implementation. I have used I2C with the F1 series plenty of times, however I have never had the need to receive data from a slave. However for the sake of completeness I decided to implement an RXing routine in I2C for my tutorial. Only to find statements like this in the reference manual:
"The EV7 software sequence must complete before the end of the current byte transfer.In case EV7 software sequence can not be managed before the current byte end of transfer, it is recommended to use..."
Basically it requires my firmware to complete a task before the slave finishes sending me the next byte, hence the performance of this depends on events beyond my control. So then the reference manual goes on to describe a work around. I was annoyed. So I did the next best thing, I checked out to see how CubeMx HAL does it.
I set up an I2C based application in CubeMX. When I traced back their infinite function calls I found that they disable interrupts, complete the task required and re-enable interrupts and then finish the RXing. That seems like a sloppy fix. What if I NEED interrupts? What if I do not care if I lose data and I absolutely need to handle a certain interrupt if it happens during an I2C transaction? Clearly this microcontroller would not be suitable in an application with such requirements. Then I did the next logical thing, I checked out their other MCUs because I know from series to series some of the registers change.
And sure enough after some digging I found a what I can best describe as elegant I2C implementation on their F0 / L0 / G0 / F3 / F4x where (x > 0) and F7 . What do all of these microcontrollers have in common? They were made on or after 2012. Well technically speaking they were "released" on or after 2012. Anything before that has a less than desirable I2C implementation. In fact the STM32F1 was their first Cortex M3 based microcontroller announced in June of 2007.
To be fair, once they had a few years to play with their designs they did a marvelous job. Their implementation of the I2C protocol on post 2012 chips is a programmers dream. There are no race condition. The first image below is that of Control Register 2 for I2C in the L0 series. There is a dedicated area to specify the number of bytes you want to send. There is also a dedicated area to enter the slave address. The second image shows that there is also two dedicated registers for TX and RX of data. In the older I2C implementations there is no such area for a slave address or number of bytes to send. The older implementation also has no dedicated register for RX and TX, there is only one Data register that serves for slave address, TX and RX.
Furthermore the older implantation requires certain steps when receiving one byte, two bytes and anything greater than 2 bytes. That is horrid. After working with their newer microcontrollers I can only say that the F1 feels primitive in this regard.
I must add, I still cannot wrap my head around their timing register in the new implementation of I2C. Honestly, one look at the datasheet and I said NOPE! I just open up CubeMx and set up the clocks how I intend to have them then I set up the I2C and I copy the value it assigns in the CubeMX software, but then I do not generate the project and just keep working with my bare metal/ register version. Is it cheating? Fuck no.
EDIT: Some STM32F4 series have what they call Fast-Mode-Plus I2C (FMPI2C ) which is a different peripheral from their normal crappy I2C. FMPI2C is exactly the same thing that they eventually started implementing in their newer microcontrollers and just made it standard I2C and dropped the FMP naming nonsense. Any STM32F4x where x is greater than 0 has the additional FMPI2C and thus a good implementation of the protocol.