Enable I2C timeout?

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

cross mob
euggersh
Level 5
Level 5
5 sign-ins First solution authored 50 replies posted

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 Solution
Hari
Moderator
Moderator
Moderator
750 replies posted 500 replies posted 250 solutions authored

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

View solution in original post

4 Replies
MotooTanaka
Level 9
Level 9
Distributor - Marubun (Japan)
First comment on blog Beta tester First comment on KBA

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

0 Likes
MaBu_976791
Level 1
Level 1
First like given

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?

0 Likes
lock attach
Attachments are accessible only for community members.
MotooTanaka
Level 9
Level 9
Distributor - Marubun (Japan)
First comment on blog Beta tester First comment on KBA

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

0 Likes
Hari
Moderator
Moderator
Moderator
750 replies posted 500 replies posted 250 solutions authored

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