4 Replies Latest reply on Jul 18, 2019 11:44 PM by MoTa_728816

    Enable I2C timeout?

    EuGe_296116

      Someone else asked for help with this in another thread three years ago but didn't get any useful replies, so I'm hoping I have better luck.

       

      I have a system where an I2C device with which the PSoC is communicating may, because of a partial power failure, disappear from the I2C bus.  The PSoC itself has a battery backup and shouldn't crash when this happens.  Unfortunately, the following code exists in the generated I2C ISR:

       

                      else if(I2CBus_CHECK_ADDR_NAK(tmpCsr))
                      {
                          /* Set Address NAK error */
                          I2CBus_mstrStatus |= (I2CBus_MSTAT_ERR_XFER |
                                                          I2CBus_MSTAT_ERR_ADDR_NAK);
      
      
                          if(I2CBus_CHECK_NO_STOP(I2CBus_mstrControl))
                          {
                              I2CBus_mstrStatus |= (I2CBus_MSTAT_XFER_HALT |
                                                              I2CBus_GET_MSTAT_CMPLT);
      
      
                              I2CBus_state = I2CBus_SM_MSTR_HALT; /* Expect RESTART */
                              I2CBus_DisableInt();
                          }
                          else  /* Do normal Stop */
                          {
                              I2CBus_ENABLE_INT_ON_STOP; /* Enable interrupt on Stop, to catch it */
                              I2CBus_GENERATE_STOP;
                          }
                      }
                      else
                      {
                          /* Address phase is not set for some reason: error */
                          #if(I2CBus_TIMEOUT_ENABLED)
                              /* Exit interrupt to take chance for timeout timer to handle this case */
                              I2CBus_DisableInt();
                              I2CBus_ClearPendingInt();
                          #else
                              /* Block execution flow: unexpected condition */
                              CYASSERT(0u != 0u);
                          #endif /* (I2CBus_TIMEOUT_ENABLED) */
                      }
                      break;
      

       

      If this partial power failure happens at just the wrong time, we end up in the CYASSERT toward the end of the pasted code.  There appears to be a TIMEOUT_ENABLED define that's checked for, but it's not defined so this condition hits the assert and hangs the CPU.

       

      I could, of course just define that in my project settings, but I'm wondering if there's some setting somewhere that I'm missing that would define that in generated code.

       

      It's also not entirely clear to me what would happen in this case with a release build where there's no CYASSERT.  As far as I can tell, the IRQ doesn't get cleared in that case, so we'd end up in an endless loop of servicing the same IRQ.  But again, maybe I'm missing something?

        • 1. Re: Enable I2C timeout?
          MoTa_728816

          Hi,

           

          I just created a simple project with one "I2C" component and ran grep for TIMEOUT

          in the <project>/Generated_Source/PSoC5 folder.

          010-schematic.JPG

           

          May be I'm missing some points of your question. but anyway,

          I think that I2C.h has the definitions

           

           

          I2C.h:#define I2C_TIMEOUT_ENABLE             (0u)

           

          ========================

          $ grep TIMEOUT I2C*.[ch]

          I2C.c:#if (I2C_TIMEOUT_ENABLED)

          I2C.c:#endif /* (I2C_TIMEOUT_ENABLED) */

          I2C.c:#if (I2C_TIMEOUT_ENABLED)

          I2C.c:#endif /* (I2C_TIMEOUT_ENABLED) */

          I2C.c:#if (I2C_TIMEOUT_ENABLED)

          I2C.c:#endif  /* End (I2C_TIMEOUT_ENABLED) */

          I2C.h:#define I2C_TIMEOUT_ENABLE             (0u)

          I2C.h:#define I2C_TIMEOUT_SCL_TMOUT_ENABLE   (0u)

          I2C.h:#define I2C_TIMEOUT_SDA_TMOUT_ENABLE   (0u)

          I2C.h:#define I2C_TIMEOUT_PRESCALER_ENABLE   (0u)

          I2C.h:#define I2C_TIMEOUT_IMPLEMENTATION     (0u)

          I2C.h:#define I2C_TIMEOUT_ENABLED            (0u != I2C_TIMEOUT_ENABLE)

          I2C.h:#define I2C_TIMEOUT_SCL_TMOUT_ENABLED  (0u != I2C_TIMEOUT_SCL_TMOUT_ENABLE)

          I2C.h:#define I2C_TIMEOUT_SDA_TMOUT_ENABLED  (0u != I2C_TIMEOUT_SDA_TMOUT_ENABLE)

          I2C.h:#define I2C_TIMEOUT_PRESCALER_ENABLED  (0u != I2C_TIMEOUT_PRESCALER_ENABLE)

          I2C.h:#define I2C_TIMEOUT_UDB    (0x00u)

          I2C.h:#define I2C_TIMEOUT_FF     (0x01u)

          I2C.h:#define I2C_TIMEOUT_FF_IMPLEMENTED     (I2C_TIMEOUT_FF  == \

          I2C.h:                                                        I2C_TIMEOUT_IMPLEMENTATION)

          I2C.h:#define I2C_TIMEOUT_UDB_IMPLEMENTED    (I2C_TIMEOUT_UDB == \

          I2C.h:                                                        I2C_TIMEOUT_IMPLEMENTATION)

          I2C.h:#define I2C_TIMEOUT_FF_ENABLED         (I2C_TIMEOUT_ENABLED && \

          I2C.h:                                                     I2C_TIMEOUT_FF_IMPLEMENTED)

          I2C.h:#define I2C_TIMEOUT_UDB_ENABLED        (I2C_TIMEOUT_ENABLED && \

          I2C.h:                                                     I2C_TIMEOUT_UDB_IMPLEMENTED)

          I2C.h:#if (I2C_TIMEOUT_ENABLED)

          I2C.h:#endif /* (I2C_TIMEOUT_ENABLED) */

          I2C.h:#define I2C_RESTORE_TIMEOUT    (255u)

          I2C_BOOT.c:        status = CYRET_TIMEOUT;

          I2C_BOOT.c:        status = CYRET_TIMEOUT;

          I2C_INT.c:#if(I2C_TIMEOUT_FF_ENABLED)

          I2C_INT.c:#endif /* (I2C_TIMEOUT_FF_ENABLED) */

          I2C_INT.c:                    #if(I2C_TIMEOUT_ENABLED)

          I2C_INT.c:                    #endif /* (I2C_TIMEOUT_ENABLED) */

          I2C_INT.c:            #if(I2C_TIMEOUT_ENABLED)

          I2C_INT.c:            #endif /* (I2C_TIMEOUT_ENABLED) */

          I2C_INT.c:            #if(I2C_TIMEOUT_ENABLED)

          I2C_INT.c:            #endif /* (I2C_TIMEOUT_ENABLED) */

          I2C_PM.c:#if (I2C_TIMEOUT_ENABLED)

          I2C_PM.c:#endif /* (I2C_TIMEOUT_ENABLED) */

          I2C_PM.c:#if (I2C_TIMEOUT_ENABLED)

          I2C_PM.c:#endif /* (I2C_TIMEOUT_ENABLED) */

          I2C_PM.c:    #if (I2C_TIMEOUT_ENABLED)

          I2C_PM.c:    #endif /* (I2C_TIMEOUT_ENABLED) */

          I2C_PM.c:#if (I2C_TIMEOUT_ENABLED)

          I2C_PM.c:#endif /* (I2C_TIMEOUT_ENABLED) */

          I2C_PM.c:    #if (I2C_TIMEOUT_ENABLED)

          I2C_PM.c:    #endif /* (I2C_TIMEOUT_ENABLED) */

          I2C_PVT.h:#define I2C_TIMEOUT_ENABLED_INC    (0u)

          I2C_PVT.h:#if (0u != I2C_TIMEOUT_ENABLED_INC)

          I2C_PVT.h:#endif /* (0u != I2C_TIMEOUT_ENABLED_INC) */

          ========================

           

          moto

          • 2. Re: Enable I2C timeout?
            MaBu_976791

            I have the same problem a few times if there is a problem with the i2c line the full system hangs and I have to wait a watchdog reset.How can I recover the i2c funcionality without rebooting my system?, in other systems I can recover the i2c generating SCL pulses until the slave that produces the error releases the SDA line...Have anyone tested something like this in a Psoc5 board?

            • 3. Re: Enable I2C timeout?
              AH_96

              Hi EuGe_296116

               

              We have not implemented timeout as the default standard as this is not part of the official I2C specification. Also the state of the I2C transfer is not consistently the same at the time of hanging.

               

              You can, however, configure I2C timeout by enabling the feature in Expression view.

              This can be configured in Creator. This can be done by

              1)Tools -> Options -> Component Catalog and enable Param Edit Values

              2)Then right click on the Advanced Configuration Tab of the I2C component and click on Show Expression View.

              Now the options will be visible.You can implement the time out in your design so that if the SCL or SDA is being pulled low for a long time then I2C component will reset automatically after timeout.

               

              Do let us know in case any further clarification is required.

               

              Thanks and regards

              Harigovind

              1 of 1 people found this helpful
              • 4. Re: Enable I2C timeout?
                MoTa_728816

                Hi,

                 

                I tried with CY8CKIT-059

                 

                Schematic

                001-schematic.JPG

                Pin assignment

                003-pin-list.JPG

                I connected LM75B (slave address = 0x48)

                 

                Tera Term Log

                 

                000-tera-term.JPG

                 

                At first reading temperature from LM75B

                TEK0000.jpg

                Then forced clock toggle 16 times

                TEK0001.jpg

                Returning to normal I2C mode and keep reading temperature.

                TEK0002.jpg

                 

                main.c

                ================

                #include "project.h"

                #include "stdio.h"

                 

                #define LM75B_I2C_SLAVE_ADDR (0x48)

                 

                char str[128] ; /* print buffer */

                void print(char *str)

                {

                    UART_PutString(str) ;

                }

                 

                void init_hardware(void)

                {

                    UART_Start() ;   

                    I2C_Start() ; 

                    CyGlobalIntEnable; /* Enable global interrupts. */   

                }

                 

                void splash(void)

                {

                    sprintf(str, "I2C Reset Test (%s %s)\n", __DATE__, __TIME__) ;

                    print(str) ;

                }

                 

                uint8_t myI2C_ReadByte(uint8_t reg_addr)

                {

                    uint8_t status ;

                    uint8_t value = 0 ;

                   

                    I2C_MasterClearStatus();

                 

                    status = I2C_MasterSendStart(LM75B_I2C_SLAVE_ADDR, I2C_WRITE_XFER_MODE) ;

                   

                    if (I2C_MSTR_NO_ERROR == status) {

                        I2C_MasterWriteByte(reg_addr);

                        status = I2C_MasterSendRestart(LM75B_I2C_SLAVE_ADDR, I2C_READ_XFER_MODE); 

                    }

                   

                    if (I2C_MSTR_NO_ERROR == status) {  

                        value = I2C_MasterReadByte(I2C_ACK_DATA); // read manufacturers ID from device

                    }

                    I2C_MasterSendStop(); 

                   

                    return(value) ;

                }

                 

                /**

                * From 10.1.1 General Call Address The I2C-BUS SPECIFICTIAON Version 2.1

                * General call address 0x00 and comand 0x06

                * is supposed to reset devices on the bus

                * if they are capable of responding to this command.

                */

                uint8_t i2c_software_reset(void)

                {

                    uint8_t status ;

                    uint8_t global_address = 0x00 ;

                    uint8_t sw_reset = 0x06 ;

                   

                    I2C_MasterClearStatus();

                 

                 

                    status = I2C_MasterSendStart(global_address, I2C_WRITE_XFER_MODE) ;

                   

                    if (I2C_MSTR_NO_ERROR == status) {

                        status = I2C_MasterWriteByte(sw_reset);

                    }

                   

                    I2C_MasterSendStop(); 

                   

                    return(status) ;

                }

                 

                 

                #define I2C_CLK_DELAY     5 /* 10us for 100KHz */

                void toggle_scl(int num)

                {

                    int i ;

                    for (i = 0 ; i < num ; i++ ) {

                        SCL_Write(1) ;

                        CyDelayUs(I2C_CLK_DELAY) ;

                        SCL_Write(0) ;

                        CyDelayUs(I2C_CLK_DELAY) ;

                    }

                    SCL_Write(1) ;   

                }

                 

                int i2c_bus_reset(void)

                {

                    int result = 0 ;

                    int sda_bypass, scl_bypass ;

                    int try_count = 10 ;

                       

                    scl_bypass = SCL_BYP ;             /* save scl bypass value */

                    sda_bypass = SDA_BYP ;             /* save sda bypass value */

                   

                    SCL_SetDriveMode(PIN_DM_STRONG) ;  /* Strong CMOS Output */

                    SCL_BYP = 0 ;                      /* disconnect SCL pin from I2C and enable DR */ 

                    SDA_SetDriveMode(PIN_DM_DIG_HIZ) ; /* Digital HiZ input */

                    SDA_BYP = 0 ;                      /* disconnect SDA pin from I2C */

                   

                    do {

                        toggle_scl(16) ;

                 

                 

                        result = SDA_Read() && SCL_Read() ;

                        try_count-- ;

                        if (try_count <= 0) {

                            break ;

                        }

                    } while(result == 0) ;

                       

                    SCL_SetDriveMode(PIN_DM_OD_LO) ; /* Open drain - drive low */

                    SCL_BYP = scl_bypass ;           /* reconnect SCL to I2C   */

                    SDA_SetDriveMode(PIN_DM_OD_LO) ; /* Open drain - drive low */

                    SDA_BYP = sda_bypass ;           /* reconnect SDA to I2C   */

                   

                //  i2c_software_reset() ; /* may not be necessary */

                       

                    return(result) ;

                }

                 

                int main(void)

                {

                    uint8_t data ;

                    uint8_t addr = 0 ;

                    int i ;

                   

                    init_hardware() ;

                 

                 

                    splash() ;

                    

                    for(;;)

                    {      

                        for (i = 0 ; i < 5 ; i++ ) {

                            data = myI2C_ReadByte(addr) ;       

                            sprintf(str, "%d\n", data) ;

                            print(str) ;

                            CyDelay(1000) ;

                        }

                       

                        print("Resetting I2C Bus ... ") ;

                        if (i2c_bus_reset()) {

                            print("OK!\n") ;

                        } else {

                            print("Failed!\n") ;

                        }

                        CyDelay(1000) ;

                    }

                }

                 

                ============================

                 

                For me the hardest part was restoring I2C function after taking control of SCL (and/or SDA).

                The PIN_BYE register needed be restored, but I failed to read the value from the datasheet,

                so I saved the value (it was 3) then restored.

                 

                Anyway, it seems to be working, but unfortunately (or fortunately?), currently I don't have

                an I2C device, which causes bus hang, so I could not test "real" scenario.

                 

                 

                moto