Correct way to reset I2C SCB and recover stuck bus?

Tip / Sign in to post questions, reply, level up, and achieve exciting badges. Know more

cross mob
Panometric
Level 5
Level 5
100 sign-ins 100 replies posted 10 solutions authored

Using PSoc4 224110, seeing some issues with I2C Errors and looking to recover the bus. I'm seeing conflicting responses and wondering what the recommended method is.

Two Issues:

1. A slave device is in mid-transaction and holding the bus down. Typically the response is to manually toggle SCL until it releases the bus.

2. Of course when this happens the I2C Master is also busy and that must be reset as well. Is it sufficient to just I2C_Stop() and I2C_Start()?

Read all of the following without finding a good answer:

Trouble resetting I2C Master after glitches.

Reset I2C

I2C Master stuck - MCSR stuck with START_GEN set

I2c_BUS_BUSY

CYW20719 and CYW20735 I2C Compatible Master

I2C Discussion

0 Likes
1 Solution

Here are some hints to recover a hanging I2C. method for recovering I2C bus · Issue #1025 · esp8266/Arduino · GitHub

I once tried the solution with 8 or 9 forced clock cycles. Needed to re-program the scl drive mode, forcing 8 cycles and re-program again.

Bob

View solution in original post

0 Likes
6 Replies
LinglingG_46
Moderator
Moderator
Moderator
500 solutions authored 1000 replies posted 10 questions asked

Hi

Is it sufficient to just I2C_Stop() and I2C_Start()?

No, you need to call "I2C_Init();" before" I2C_Enable();". instead of I2C_Start();.

Thanks

0 Likes

The sequence I2C_Stop(), 2C_Init(),  I2C_Enable() does not clear I2C_I2C_MSTR_BUS_BUSY.

In this case the flag I2C_I2C_MSTR_BUS_BUSY is erroneous, SCL and SDA are high.

I2C_I2CMasterClearStatus() has also been called,  I2C_I2C_STATUS_REG=0 has also been performed.

So I have run out of options to clear this flag, Ideas?

0 Likes

Here are some hints to recover a hanging I2C. method for recovering I2C bus · Issue #1025 · esp8266/Arduino · GitHub

I once tried the solution with 8 or 9 forced clock cycles. Needed to re-program the scl drive mode, forcing 8 cycles and re-program again.

Bob

0 Likes

bob.marlowe​ thanks, I'm aware of that technique, have actually used it an older PSoc project and it worked well, but it isn't working this time, either with this module or this particular fault. There have been changes to the SCB code since then, and that method does not seem to work anymore.

I'm seeing two issues:

1. The SCB says it's busy when it is not.

2. SDA will read low as GPIO when it is measured high. See BUGBUG comment in code below.

FYI: For 224110 module, SDA is on 5.0 and SCL is on 5.1.

Also note there is some references to similar issues that appear to be disabled in recent versions of the generated code: See I2C_Master.c

     void I2C_I2CReStartGeneration(void)

     {

     #if(I2C_CY_SCBIP_V0)

    /* Generates Restart use GPIO and scb IP functionality. Ticket ID#143715,

    * ID#145238 and ID#173656.

    * In case of timeout the I2C_I2CMasterSendRestart handles this

    * case using own timeout and execute appropriate recovery.

    */

.....

Here's my current attempt at solving both problems based on the previous posts.

void I2C_Recover(uint32 status)

{

    if (status==I2C_I2C_MSTR_NOT_READY){

        CyDelay(2); // Give it a little extra time just in case called right after unfinished trans. 2ms exceeds all expected ops.

    }

    if (status==I2C_I2C_MSTR_BUS_BUSY || status==I2C_I2C_MSTR_NOT_READY || status==I2C_I2C_MSTR_ERR_TIMEOUT||  status==1){

        I2C_Stop();

        I2C_I2C_STATUS_REG=0;        

        I2C_Init();

        I2C_Enable();

    }

   

     for(int tries=0;tries<=11;tries++) // Try several times..

     {

        //Recover from an I2C stall - perhaps  comm was in process during reset

        int solved=0;

        CY_SET_REG32(CYREG_HSIOM_PORT_SEL5,  0x00000000); // Set both scl and sda to  GPIO temporarily

        I2C_sda_SetDriveMode(I2C_sda_DM_OD_LO);

        I2C_sda_Write(1); // Make sure we are not making it low ourselves.

        if (I2C_sda_Read()==0){ //BUGBUG: This often says SDA is low when SDA is measured high externally.

            CyDelay(1);

            for(int clocks=0;!I2C_sda_Read() && clocks <27; clocks++ )

            {

                I2C_scl_SetDriveMode(I2C_scl_DM_OD_LO);

                I2C_scl_Write(0);

                CyDelay(1);

                I2C_scl_Write(1);

                CyDelay(1);

            }

            int solved=I2C_sda_Read();

            if (solved){

                I2C_Stop();

                CyDelay(RESET_WAIT_DELAY);

                I2C_Start();

                CyDelay(RESET_WAIT_DELAY);

                return;                         // Good Exit

            }

        } else solved=1;

        CY_SET_REG32((void *)(CYREG_HSIOM_PORT_SEL5), 0x000000EEu); // Return scl sda to SCB block

        if (solved) return;

        else if (tries==10) My_CySoftwareReset(1); //Reboot if no success

    }

   

    if (I2C_I2CMasterStatus()!=I2C_I2C_MSTR_NO_ERROR){

        My_CySoftwareReset(1);

    }

}

0 Likes

Hi,

Do you solve this problem now?

Thanks

0 Likes

ring​ Perhaps it would help to explain my goal. I need to be able to recover the bus if a device is hot-plugged. I have a mux on the local bus, and it has a hard reset. But the devices on the external muxed bus do not have an external reset, so it is possible that when hotplugged, they can get a partial transaction as I am polling for their presence.  This leaves the bus and the Master is a stuck state.

0 Likes