cancel
Showing results for 
Search instead for 
Did you mean: 

Recovering from a failed I2C transfer

Frank Pilhofer
Associate II

I am using HAL_I2C_Master_Transmit(). That generally works well. However, I sometimes run into an issue when the slave device is not responding. HAL_I2C_MasterTransmit() obviously returns an error after running into timeout, as it should. But then I am unable to recover from this condition. In my logic analyzer, I see that SCL remains low, and all future transfers fail. What can I do to recover? Do I have to disable and re-enable the peripheral every time? The device is an STM32H7A3.

7 REPLIES 7
Foued_KH
ST Employee

Hello @Frank Pilhofer​ ,

Try to get the error (HAL_I2C_GetError)

If the error is :

  • OVR, ARLO, NACK : you can just do a Transmit again
  • BERR : You have to re-enable the peripheral

Please I would know if you are transmitting more than 255 bytes or not ?

Foued

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.

Frank Pilhofer
Associate II

Hi Foued,

the error is TIMEOUT, no other bits are set. No, I am not transmitting more than 255 bytes. Since the slave device is not responding at all, no bytes are transmitted; the master does not receive an ACK and then seems to be stuck in that state, forever waiting for an ACK and never recovering.

Frank

S.Ma
Principal

Test before doing an I2C communicsstion that SDA pin is HIGH, and if it is, place a NOP and try to breakpoint onto it.... if you do, bingo !

This past millenium 1982 I2C bus has an error recovery which is to generate 9 or 18 stop bits manually by GPIOs.

Here is a full SW + GPIO example which was created past millenium and saved me lots of debug time. Rugged.... I2C_MasterIO.c

You should start with resetting the I2C module - it may have a reset bit inside I2C registers, I don't use 'H7 and am not familiar with its I2C - or just simply in RCC. If that does not release SCL then it's failure of the slave, and you can't do anything about it except reset the slave (if it as a reset pin), or power it down and up.

Then you should check SDA and if it's down, it's the slave which is holding it, so perform the up-to-9 SCK pulses ("manually", with SCL set to Output in GPIO), as stipulated by the I2C specification, and then perform a STOP.

Then proceed with setting up I2C again, as you've did originally.

Also, such failed transfers should not happen at the first place. Check the integrity of SDA/SCL signals, and decrease pullup resistor values if necessary.

JW

S.Ma
Principal

Since 1999, the I2C bus hang situation is well known by people who have experimented it rather than only reading the official standard spec.

Take an EEPROM which data is full of 0x00

Read the EEPROM and reset the MCU while reading. Bus has high probability to hang.

Use ESD gun to zap the bus lanes, same.

Practice by few turns into future consulting fee profit ?

Frank Pilhofer
Associate II

Thank you all for your answers. I have gathered more data, and I see that my original question was incomplete.

First, if I simply disconnect master and slave, i.e., if the master's SCL and SDA pins are not connected to anything, then I can simply re-start a new transaction without hanging the bus. That works fine.

In my current use case, I am actually looping back to from I2C1 (acting as master) to I2C4 (acting as slave, using interrupt-driven I/O) on the same MCU. (This is for testing out a more complex use case involving separate boards in the future.) And having a slave connected to the bus changes the behavior.

if I program I2C4 to listen to one address (e.g., 42), and then send a write request to a different address (e.g., 24), then the bus does "hang"; I am then unable to start a new transmission without resetting the master (setting peripheral enable to 0, waiting, and back to 1).

If I send a write request to "my" slave (i.e., to address 42), but the slave misbehaves (e.g., if it does a __HAL_I2C_GENERATE_NACK() in the address match callback -- I thought that ought to work), then apparently the slave also gets stuck in a bad place.

With that background, I now have two questions:

  • Is there something that I can do, master-side, to mark a failed request as "done" so that the slaves on the bus get un-stuck, without resetting the bus? After all, the slave in my set-up is never aware that there was a transaction, it just sees the bus as "busy."
  • Is there something that a slave can do to reject a request? I thought GENERATE_NACK() was supposed to do that, but apparently I misunderstood.

Thank you,

Frank

S.Ma
Principal

Disconnecting a bus... depends where are located the pull-up resistors. Does it power down the slave (=reset) ?

Does it happen that SDA = 0 just before trying to do a Master data transfer ?