6 Replies Latest reply on Sep 6, 2018 7:46 PM by LinglingG_46

    Correct way to reset I2C SCB and recover stuck bus?

    mimi_1891471

      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

        • 1. Re: Correct way to reset I2C SCB and recover stuck bus?
          LinglingG_46

          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

          • 2. Re: Correct way to reset I2C SCB and recover stuck bus?
            mimi_1891471

            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?

            • 3. Re: Correct way to reset I2C SCB and recover stuck bus?
              mimi_1891471

              LinglingG_46 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.

              • 4. Re: Correct way to reset I2C SCB and recover stuck bus?
                JoMe_264151

                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

                • 5. Re: Correct way to reset I2C SCB and recover stuck bus?
                  mimi_1891471

                  JoMe_264151 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);

                      }

                  }

                  • 6. Re: Correct way to reset I2C SCB and recover stuck bus?
                    LinglingG_46

                    Hi,

                     

                    Do you solve this problem now?

                     

                    Thanks