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-rm00332016-03-16 10:34 AM
Hi peelo.frank,
You may need to look tohttp://www.st.com/st-web-ui/static/active/en/resource/technical/document/errata_sheet/DM00027213.pdf
besides to the reference manual: mainly this limitation ''Start cannot be generated after a misplaced Stop''.This one may occur when a stop condition is generated in the Bus when the STM32 I2C tries to generate a START.-Mayla-To give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.
2016-03-16 11:25 AM
void I2C1_EV_IRQHandler(void)
{ uint32_t SR1Register; uint32_t SR2Register; uint32_t temp; SR1Register = I2C1->SR1; SR2Register = I2C1->SR2; if (SR1Register & I2C_SR1_SB) { I2C1->DR = I2C1_data.ICAddress; SR1Register = 0; SR2Register = 0; } if (SR2Register & I2C_SR2_MSL) /* If I2C1 is Master (MSL flag = 1) */ { if (SR1Register & I2C_SR1_ADDR) /* Master Transmiter */ { if(SR2Register & I2C_SR2_TRA) { I2C1->DR = I2C1_data.Address; if(I2C1_data.NumbOfBytes == 0) { I2C1_CR2_ITBUFEN_bb = 0; } } else /* Master Receiver */ { I2C1_data.BufferIdx = 0; if (I2C1_data.NumbOfBytes == 1) { I2C1_CR1_ACK_bb = 0; I2C1_CR1_STOP_bb = 1; } } SR1Register = 0; SR2Register = 0; } if ((SR1Register & (I2C_SR1_TXE | I2C_SR1_BTF)) == I2C_SR1_TXE) { temp = I2C1_data.NumbOfBytes; if(temp) { I2C1->DR = I2C1_data.Buffer[I2C1_data.BufferIdx++]; temp--; I2C1_data.NumbOfBytes = temp; if (temp == 0) { I2C1_CR2_ITBUFEN_bb = 0; } } SR1Register = 0; SR2Register = 0; } if ((SR1Register & (I2C_SR1_TXE | I2C_SR1_BTF)) == (I2C_SR1_TXE | I2C_SR1_BTF)) { I2C1_CR1_STOP_bb = 1; I2C1_CR2_ITEVTEN_bb = 0; SR1Register = 0; SR2Register = 0; } if ((SR1Register & I2C_SR1_RXNE) == I2C_SR1_RXNE) { temp = I2C1_data.BufferIdx; I2C1_data.Buffer[temp] = I2C1->DR; temp++; I2C1_data.BufferIdx = temp; temp = I2C1_data.NumbOfBytes; temp--; I2C1_data.NumbOfBytes = temp; if (temp == 1) { I2C1_CR1_ACK_bb = 0; I2C1_CR1_STOP_bb = 1; } SR1Register = 0; SR2Register = 0; } } }2016-03-22 03:52 AM
Thanks Mayla
So it looks like the strange stop condition after one SCL, instead of the ADDR coming out, means the STM32F207's I2C interface is in need of being reset. I still don't know what is causing that - the STM32F207 should be the only master on the bus - but I'll now look at recovery after the interface stops, since occasional stopping looks inevitable, especially in a noisy environment. The 2-byte read looks like a red herring. I stopped the process that was requesting those transfers (the TM101 thermometer) and the problem still occurred (after 8 hours - and the logic analyser had run out of memory). The transfer before the failure, in this case, was a write of 13 bytes. So it's not connected to the 2-byte read. Frank (This is my 3rd attempt to post a reply - hoping I don't get another Sharepoint error message...)2016-03-22 04:17 AM
Thanks Radoslaw
Your codeif (SR1Register & I2C_SR1_SB)
{
I2C1->DR = I2C1_data.ICAddress;
does the same as the snippet that I gave earlier. This code is executing. The problem is that the address byte doesn't get out on the wire. When I stop the program in the debugger, I see that DR still has the address. But the counter of interrupts that have happened since DR was written, is 0. I think I'll have to accept that the STM32 has errata, stuff can happen, and just focus on getting the machine up and running again after it happens.
Frank
2017-08-18 06:26 AM
I got the same problem, did you ever fix this, and how, just by resetting the i2c?
2019-02-27 12:40 PM
Found this thread while debugging exactly this issue. I'm running on an STM32F446ZE and am getting seemingly-random START-STOP conditions, exactly as shown above, after seemingly-random reads and writes. I was having a really tough time getting I2C to start working again, even with full resets of the peripheral, and found that if I just issued another START and ADDRESS operation, it would resume working correctly.
So, if anyone else ever finds this thread and has this issue, that's how I ended up working around it. I'm going to still prod ST about this to see why the I2C peripheral is doing this in the first place... Like the original poster, this would occur after tens of thousands of successful transactions.
2020-02-21 01:31 AM
I am running into exactly the same problem on a F407, every 100ms I read a I2C sensor and from time to time it does only generate a start-stop sequence.
I am running Zephyr, so it seems it is not a software issue since all kinds of different software seems to have the problem.
just wanted to "boost" this thread because it is an irritating problem, and the errata doesn't really describe it correctly, because a start is generated, it is just directly followed by a stop.
2020-03-03 10:38 AM
I got the same issue. I was able to get around it by checking if the stop bit was set before writing the start bit.
2020-03-03 11:02 AM
@TSosi.1 I just have happened to implemented that workaround (checking and clearing STOP before starting a new transaction) and am doing a test run since yesterday (now ran for about 26 hours without causing a problem). I have a log message in my software and I see with random intervals (but on average about every 30 seconds) that the STOP flag wasn't cleared (I do 10 transactions per second) which still makes me wonder if there is something fundamentally wrong with the Zephyr I2c driver, or if it is really a hardware problem.