cancel
Showing results for 
Search instead for 
Did you mean: 

STM32F407 I2C master: BTF flag often fails to set with multi-byte TX

Rob Meades
Associate III

I am using an STM32F407 MCU as an I2C master, my code built on the LL API of the STM32F4Cube IDE (version 1.25.0). I find that quite often, i.e. always within a handful of bytes, the BTF flag fails to set when I am doing a multi-byte TX. Single-byte TXs always work, it is only multi-byte TXs that suffer from this problem. I do not appear to receive a NACK from the far end (i.e. I2C_FLAG_AF is not set while I'm waiting for BTF to set), just not a "set-BTF".

What reasons might there be for this?

My code follows the pattern of stm32f4xx_hal_i2c.c as closely as possible and can be found here, where this is the send() function that gets stuck. Below is a sample log of a problem case (you can match the log points with the code), a millisecond timestamp on the left: you can see that the BTF flag is normally set almost immediately (within the millisecond resolution of my timestamp) until suddenly it is not, even after waiting for 10 milliseconds.

24876:  EVENT_i2c1:  TXing this many bytes 8
24876:  EVENT_i2c10: TX sent a byte because TXE is set
24876:  EVENT_i2c8:  TX BTF flag set after waiting, all is good with the world
24876:  EVENT_i2c10: TX sent a byte because TXE is set
24876:  EVENT_i2c8:  TX BTF flag set after waiting, all is good with the world
24876:  EVENT_i2c10: TX sent a byte because TXE is set
24876:  EVENT_i2c8:  TX BTF flag set after waiting, all is good with the world
24876:  EVENT_i2c10: TX sent a byte because TXE is set
24876:  EVENT_i2c8:  TX BTF flag set after waiting, all is good with the world
24876:  EVENT_i2c10: TX sent a byte because TXE is set
24876:  EVENT_i2c8:  TX BTF flag set after waiting, all is good with the world
24876:  EVENT_i2c10: TX sent a byte because TXE is set
24888:* EVENT_i2c4:  TX wait for BTF flag failed to set

1 ACCEPTED SOLUTION

Accepted Solutions

It's not entirely clear from your picture, but it appears that your GND comes from a different point than the Disco board. It's more important to keep ground short and perhaps give each signal its own ground, you can try to twist it around the signal wire.

3V/3.3V shouldn't be important with I2C.

I'm not entirely sure why you observe what you observe in the I2C flags, but the I2C module is prone to surprising changes in its internal state machine when the I2C signals are subject to externally induced disturbances (you can try to induce them deliberately by momentarily shorting them, it's a good exercise in handling surprises). I recommend using timeouts throughout and recovery routines as appropriate.

In some cases, it's easier and better to bit-bang the I2C master, although it still has to cope with consequences of externally injected noise on the transmitted data and on the slave.

Priority is to get rid of the noise, of course. Note, that the I2C bus is relatively high impedance - and as high as your pullup resistors are.

JW

View solution in original post

6 REPLIES 6
Rob Meades
Associate III

Another clue: reading the manual, and looking at how others have coded an I2C multi-byte transmit on STM32F407, I tried moving the BTF check out of the transmit loop, so the write to the data registers is done based on TXE and the BTF is only checked when the last byte has been transmitted.

With this done the same failure occurs before a handful of bytes have been transmitted, but this time because TXE is not being set within the timeout (10 ms), it doesn't even get as far as the BTF check.

It is almost like the I2C HW block is locking-up underneath me...?

What's your hardware (and by that I mean, post photo and description).

Do you have SDA/SCL waveforms measured by oscilloscope?

JW

Rob Meades
Associate III

Thanks for responding, see below, but you're right: further investigation suggests that this is a HW issue rather than a SW one. A combination of factors:

  1. I hadn't realized that the Discovery board is 3V output rather than 3.3V: I have now modified things [in the picture below] so that the 3.3V board I am talking to with I2C, AND the Discovery board itself, are jointly powered from a separate 3V3 supply, hence everything is now running at the right logic level.
  2. I have made the SCL/SDA wires as short as I possibly can (~3 cm).
  3. There is a lot of EMC about: this is in a test rack with a lot of other kit, there's not a whole lot I can do about that aside from creating a shielded box just for these boards, which seems overkill since nothing else in the set up (I have ESP32, NRF52840 and NRF5340 doing the same thing) suffers from the problem. If I run just the Discovery and I2C board it tends not to fail, so EMC is certainly a factor.

With the modifications in (1) and (2) the instances of the problem occurring have reduced; not gone away entirely, but reduced. I might try shielding just the SCL/SDA wires next. This is happening about half way through a ~40 minute test suite inside an automated test system in a rack in a lab, hence it is quite difficult to get a live capture of the problem occurring on a scope or analyzer unfortunately.

0693W00000Sw14gQAB.jpgSo, given that it is a HW thing, I will fight-the-good-fight on that front and consider this closed.

It's not entirely clear from your picture, but it appears that your GND comes from a different point than the Disco board. It's more important to keep ground short and perhaps give each signal its own ground, you can try to twist it around the signal wire.

3V/3.3V shouldn't be important with I2C.

I'm not entirely sure why you observe what you observe in the I2C flags, but the I2C module is prone to surprising changes in its internal state machine when the I2C signals are subject to externally induced disturbances (you can try to induce them deliberately by momentarily shorting them, it's a good exercise in handling surprises). I recommend using timeouts throughout and recovery routines as appropriate.

In some cases, it's easier and better to bit-bang the I2C master, although it still has to cope with consequences of externally injected noise on the transmitted data and on the slave.

Priority is to get rid of the noise, of course. Note, that the I2C bus is relatively high impedance - and as high as your pullup resistors are.

JW

Rob Meades
Associate III

That's excellent feedback, thanks. I will do what I can to fix the grounding and try the twisted-pairs approach also. Thankfully all of this is only in an automated test system, non-critical, but still irritating when things go red unnecessarily. I will report back here on what makes a difference.

Rob Meades
Associate III

And the answer was... exactly as @Community member​ suggested: a shorter GND wire between the I2C board and the Discovery board, wind it around the SDA/SCL lines and connect it to a GND pin that is on the Discovery board itself, rather than on the thing that is supplying the power.

Very sweet, thanks again for the great advice.