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

cross mob
ChVa_3548136
Level 2
Level 2
Welcome!

We have a PSoC 5LP powered board, and are having trouble where errors on the I2C communication cause the I2C master component to "lockup" in a way that the only recovery I can find is to reset the board.

Here's the setup:  The fixed function I2C is wired to internal Muxs, that connect it to one of eight external I2C buses.  Reads from the I2C bus take the following form (this one writes a byte, then reads two bytes...):

uint8 i2c_1_Register_RD_No_MUTEX (uint8 reg_address, uint16 * value) {

    uint8 ret;

   

    if (value == NULL) return I2C_COMM_PARAM_ERROR;

   

    i2c_1_Bus_Select(A_BUS_ADDR);

   

    I2C_1_MasterClearStatus();

   

    vTaskSuspendAll();

    if ((ret = I2C_1_MasterWriteBuf(I2C_ADDRESS, &reg_address, 1, I2C_1_MODE_COMPLETE_XFER)) == I2C_1_MSTR_NO_ERROR){

        timeout_count = 0;

        do {

            CyDelayUs(80u);

            timeout_count++;

        } while ((I2C_1_MasterStatus() & I2C_1_MSTAT_XFER_INP) == I2C_1_MSTAT_XFER_INP && timeout_count < TIMEOUT);

        if (timeout_count >= TIMEOUT){

            ret = I2C_COMM_BUS_TIMEOUT;

        }

        else{

            ret = I2C_1_MasterReadBuf(I2C_ADDRESS, buffer, 2, I2C_1_MODE_COMPLETE_XFER);

            timeout_count = 0;

            do {

                CyDelayUs(80u);

                timeout_count++;

            } while ((I2C_1_MasterStatus() & I2C_1_MSTAT_XFER_INP) == I2C_1_MSTAT_XFER_INP && timeout_count < TIMEOUT);

            if (timeout_count >= TIMEOUT){

                ret = I2C_COMM_BUS_TIMEOUT;

            }

        }

    }

    xTaskResumeAll();

   

    if (ret != I2C_1_MSTR_NO_ERROR){

        bus_reset();

        logSpecial("%d error from i2c bus in i2c_1_Register_RD_No_MUTEX", ret);

        return(I2C_COMM_ERROR);

    }

    *value = (buffer[0] << 😎 + buffer[1];

   

    return i2c_CheckStatus();

}

i2c_1_Bus_Select takes an address identifier to hand to the mux control register.  It's sets the pins for that bus open-driain, drives low.  It sets the other pins on all other buses High Impedance Digital.

If there's an error, bus_reset() shuts down the FreeRTOS scheduler, disabled interrupts, sets all buses to High Impedance Digital, calls I2C_1_Stop(), then I2C_1_Init(), then I2C_Start().  Then it turns interrupts and the scheduler back on.

i2c_CheckStatus() simply checks the status, and returns the error (and logs it) unless the status is No_error or read/write complete.

Most of the time, this setup will handle the odd transient error from I2C devices without issue.  But occasionally, most notably with timeout errors, the I2C component will refuse to communicate, no matter what bus it's hooked to, or how many times the bus_reset() function is called. It will always report "Bus Busy."  The only way out of that situation is to perform a full reset of MPU.

I am looking into switching the I2C Master component from the fixed-function version to the UDB version, but it'll be a test only because we'll need to remove functionality from the system to make room for it.  The idea is getting access to the Reset pin, and seeing if hitting that will allow the system to recover without restart the software.

Is there something I'm missing, or is this just the nature of the beast.  We're working to minimize the errors over the I2C bus, but one of the more troublesome applications is a PMBus based power supply.  When it's running at full power, it seems to get touchy.

0 Likes
10 Replies