2016-03-15 10:16 AM
I'm trying to debug an I2C interrupt handler that I didn't write, on an STM32F207ZG. My problem is that after sending a Start, when waiting to send the Address byte, a Stop happens. What am I doing wrong that makes the STM32F2xx do that?
The I2C1 interface is talking to two slave devices, a Texas Instruments TMP101 temperature sensor, and a Fujitsu MB85RC128 FRAM. The TMP101 is occasionally (once a second) read. The FRAM is written maybe 3 times in 5 seconds in my test setup. When neither of these is happening, the FRAM is repeatedly read. The code works for perhaps 20 minutes, perhaps 9 hours. But sooner or later, the interrupts stop happening. There is code outside the interrupt handler, to queue Transmit, Receive or Transmit+receive operations for the handler. Then the frames are then processed as interrupts occur - stepping through states to send address, send or receive bytes, signal stop. I have managed to capture information about the crash, six times now. I find that the problem always happens after a read of 2 bytes from the TMP I don't know if the 2 bytes are significant: Reference manual RM0033 has special mention about reading 2 bytes, but as far as I could see, the code does what the manual recommends. The read of 2 bytes is also the last operation in the queue, which might also be relevant. The read of 2 bytes is successful, but the next operation, which will be a read of the FRAM (because that happens so often) does not get started. Here is what happens on SCL and SDA:After the last of the two received bytes, there is a start condition, then one low pulse of SCL, then a stop condition.
I added a Log() function, to record the position in the interrupt handler in a circular buffer, and code to dump the circular buffer when the handler fails. So I know that this is the last part of the interrupt handler that gets run:uint16_t sr = port->regs->SR1;
if((sr & I2C_SR1_ERRFLAGS) == 0u) {
switch(port->state) {
case I2C_MXX_START:
if((sr & I2C_SR1_SB) != 0u) {
Log(isTxRxMxxToMtxAddr, sr);
port->state = I2C_MTX_ADDR;
port->regs->DR = frame->addr << 1; /* master write mode */
}
break;
I2C_SR1_ERRFLAGS is 0x1F00, all the error conditions.
State I2C_MXX_START means the interrupt handler has been requested to transfer data. When port->state is set to I2C_MXX_START, the value 0x0700 (ITBUFEN | ITEVTEN | ITERREN) is ORed into the CR2 register, then the START and ACK bits are ORed into CR1. This happens in non-interrupt code, but only when the queue is empty.
The SR1 register, in sr, which is also logged here, has the value 1, i.e. only the SB bit is set.
This is the state EV5 in the Reference Manual. Reading SR1 to sr and writing the address to the DR register should move us to the next state, where the address is output to the I2C bus.But that is not happening. Only a Stop condition is happening on the bus. Since the other two devices connected are slaves, I think the STM32F2xx is putting out the Stop signal.
Is this a known behaviour of the STM32F2xx? Or does it arise if I have written the wrong thing to a register - could anyone suggest what the needle looks like, that I am searching for in this haystack?
In addition to reading and trying to understand RM0033, I also tried looking for an example interrupt handler, to see if anything was being done differently. It's the stm32f2xx_it.c file in I2C\I2C_TwoBoards\MasterReceiverInterrupt in the standard peripheral library (the author of this code did not use the standard peripheral library functions, unfortunately). It has special handling for when 3 bytes are being received, which does not seem to match RM0 RM0008, which is for the STM32F1xx, does mention things to do when there are 3 bytes to come in. Do I need to follow both reference manuals? #stm32f2-i2c-rm00332020-03-03 12:30 PM
@ERol.1 Thanks for the update! I am reading my device every 2ms and have run for over 24 hours with no issues.
2020-03-03 12:31 PM
You agree with you on the statement about the errata.
2020-03-03 12:56 PM
@ERol.1 Thanks for the update! I am reading my I2C device every 2ms for over 24 hours with no issues.
2020-03-07 02:02 AM
After setting STOP flag in CR1, hardware is supposed to clear it automatically. But it takes time for the internal I2C state machine to generate STOP (depending on previous state of the lines), it takes time the levels on the lines actually rise, and then there are some filtering and sampling delays. That's why a transaction should end by waiting for the STOP bit to be cleared (or, alternatively, software has to wait for this before starting a new transaction).
In any case, I'd recommend to implement (non-blocking) timeouts to every I2C process, so it won't remain hanging in any state if the internal machine for any reason (usually noise/induced glitches on the bus) won't respond in the way one would expect.
JW
2020-03-07 02:13 AM
When debugging this problem I had a check for the STOP flag in my code, and it was never cleared, so just "waiting" for STOP to go away does not work. And just adding a timeout will cause the next transaction to be corrupted in the way that is described in the original post.
Clearing the STOP flag before a new transaction seems to work for me, I am still not sure why the STOP flag doesn't always get cleared (about 1 in 300 times it isn't cleared in my case).