Volatile variable incorrect after interrupt callback
I'm currently attempting to get I2C and UART communication up between a chain of processors like so NXP chip <- UART -> STM32L063 <- (slave) I2C (master) -> STM32F030. I am using interrupts to handle UART and I2C Rx and Tx callbacks, you can see my code below. UART is working no problem, the issue I am having is with I2C. My I2C protocol has master writing to slave and then reading from slave. This repeats every 10ms. It will work for a while and then stop after the master sends the read request with the slave's address over the I2C bus. Using a logic analyzer and oscilloscope, it's clear that the slave is clock stretching the bus as it waits to respond but never does. Resetting the slave generates a nack on the line which further shows the slave is holding up the bus.
I've narrowed down the issue to being that the app_flags |= APP_FLAG_PROCESS_I2C is not persisting outside of the HAL_I2C_SlaveRxCpltCallback callback when I2C fails. I verify the line gets called by seeing that both GPIO toggles surrounding it occur on the logic analyzer. If I set a breakpoint where this flag is checked after I2C fails I can see that the flag is not set. Jumping into the block with GDB gets I2C communication to continue as normal so it is clear that this flag getting set is not persisting and is causing the I2C bus to halt since the HAL_I2C_Slave_Transmit_IT function in the block is not getting called. There are also GPIO toggles in the block that is not being entered that are not triggered when I2C stops which also shows this block is not being entered.
Other info:
All interrupts share the same priority.
I am using STM32Cube
My question is this, why isn't this flag persisting? app_flags is an uint16_t which should perform reads/writes atomically on a 32bit system. Its also volatile so it should be getting read directly from memory every time it is used.
You can also see the app_flags_i2c variable which directly mirrors app_flags with the only difference is that it is not checked against in the main while(1) loop. Oddly, this variable does have the flag set correctly when i2c fails and app_flags doesn't have the flag set. Changing to check against app_flags_i2c instead of app_flags causes their behavior to switch (i.e. app_flags set correctly, app_flags_i2c not).
Code in comments
