I2C error causes component to report Bus Busy

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
MotooTanaka
Level 9
Level 9
Distributor - Marubun (Japan)
First comment on blog Beta tester First comment on KBA

Hi,

Although I don't know if this applies to your case,

when one of the I2C device hangs, they keep pulling SDA to low

and the status of I2C bus will be kept busy.

For some device(s), letting the master send clock 8~20 times clears the situation.

For that purpose, I wrote a sample for 5LP.

PSoC 5LP I2C Bus Reset Sample

If you have chance to try it, please let us know if it works for your case.

moto

0 Likes

I didn't think that this would help, since the error only happens with one device, but then the component can't communicate regardless of which of the eight buses (which are all on different pins and not electrically connected until the muxes in the MPU).  But I did add code to toggle the SCL line on all eight of the buses as part of the reset, and the problem is still there.

Is there some what of performing "more" of a Reset of the Fixed-Function I2C block than Stop-Init-Start, but short of resetting the full MCU?

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

Dear ChVa-san,

I'm sorry that my idea does not work.

And I noticed that you wrote "master" hangs.

I was thinking that the "slave" was holding the bus.

In case the master hangs, I think that you can use the debugger to locate the point where the "hang" takes place.

coincidentally I wrote a sample to separate some functions to utilize timeout in PSoC 4,

I wonder if similar trick can be applied to PSoC 5 fixed function.

I2C Connection between PSoC 4.2 (CY8CKIT-042) and PSoC 4.0 (CY8CKIT-040)

There what I did was/were

I replaced

=======

    status = I2CM_I2CMasterWriteBuf(I2C_SLAVE_ADDRESS, &tx_data, 1, I2CM_I2C_MODE_COMPLETE_XFER) ;

=======

with

=======

        status  = I2CM_I2CMasterSendStart(I2C_SLAVE_ADDRESS, I2CM_I2C_WRITE_XFER_MODE, TIMEOUT_MSEC) ;

        status |= I2CM_I2CMasterWriteByte(tx_data, TIMEOUT_MSEC) ;

        status |= I2CM_I2CMasterSendStop(TIMEOUT_MSEC) ;

=======

I replaced

=======

           status = I2CM_I2CMasterReadBuf(I2C_SLAVE_ADDRESS, &rx_data, 1, I2CM_I2C_MODE_COMPLETE_XFER) ;

=======

with

=======

            status  = I2CM_I2CMasterSendStart(I2C_SLAVE_ADDRESS, I2CM_I2C_READ_XFER_MODE, TIMEOUT_MSEC) ;

            if (status == I2CM_I2C_MSTR_NO_ERROR) {

                status = I2CM_I2CMasterReadByte(I2CM_I2C_NAK_DATA, &rx_data, TIMEOUT_MSEC) ;

            }

            status |= I2CM_I2CMasterSendStop(TIMEOUT_MSEC) ;

=======

I replaced

=======

        while(I2CM_I2CMasterStatus() == I2CM_I2C_MSTAT_XFER_INP)  {

                ; /* wait for the transfer complete */

        }

=======

with

=======

            timeout_count = 0 ;

            while((I2CM_I2CMasterStatus() == I2CM_I2C_MSTAT_XFER_INP)&&(timeout_count < TIMEOUT_MSEC))  {

                /* wait for the transfer complete */

                CyDelay(1) ; /* wait 1ms */

                timeout_count++ ;

            }   

=======

Best Regards,

27-Jul-2019

Motoo Tanaka

0 Likes

I moved away fro using the SendStart/Read-Write/SendStop because they have internal loops that have no timeout

I also avoid changing the generated code at all costs...

Looking at I2C_1_Master.c, the "hang-up" in line 93 (in WriteBuf's case):

            /* Master is ready for transaction: check if bus is free */

            if(I2C_1_CHECK_BUS_FREE(I2C_1_MCSR_REG))

            {

                errStatus = I2C_1_MSTR_NO_ERROR;

            }

            else

            {

                errStatus = I2C_1_MSTR_BUS_BUSY;

            }

CHECK_BUS_FREE simple chcks to see if 'mcsr' has BUS_BUSY set...

As far as I can tell, mcsr is an internal register in the I2C component...

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

Dear ChVa-san,

I'm very sorry that there were not TIMEOUT enabled I2C functions for 5LP.

> I also avoid changing the generated code at all costs...

Generally, I agree with you. But I'll change the generated code if I have no other choice.

If it's the case, probably I will copy the generated source in to my private project folder

and rename functions not to conflict with the original ones, then add timeout trick

in the loop.

So you mean the program hangs at line 93?

>             if(I2C_1_CHECK_BUS_FREE(I2C_1_MCSR_REG))

I wonder if the bus is actually busy or if bus is idle (both SCL and SDA are high)

and still the module is reporting problem or causing hang...

Best Regards,

29-Jul-2019

Motoo Tanaka

0 Likes
BragadeeshV
Moderator
Moderator
Moderator
First question asked 1000 replies posted 750 replies posted

Hi ChVa_3548136​,

Can you please share your project file so that we can try to reproduce your issue from our side and see where the issue is?

Regards,

Bragadeesh

Regards,
Bragadeesh
0 Likes

I'm not sure that would help.  I would have to strip most of the project out (it's for a production system), but even then it wouldn't help without one of our boards and the corresponding I2C devices.  The problem has only happened when using Sensirion SCD30 sensors as well as our Artesyn CNS650-MU power supplies (and only when under full load).  We've stopped using the Sensirion sensors, and the design of our product is changing so that the AC-DC power supplies will not be required.

0 Likes
user_1324871
Level 1
Level 1

The datasheet states that if you have bus error at transfer start then no other commands would be effective. But it isn't true. The I2C bus would hang with bus busy until you call I2C_MasterSendStop(). You can't walk around this problem with bus reset, status clear, i2c module reset/restart/sleep+wake up. I've run into this bug and spent several days to figure out. After sending stop condition it will work properly, you don't have to take any further actions.

Try like this:

    if (ret != I2C_1_MSTR_NO_ERROR){

        I2C_1_MasterSendStop();

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

        return(I2C_COMM_ERROR);

    }

0 Likes

Thank you for the suggestion.  I've already made the change, but I just need to wait for a system to be built that we can cause the failures in to test it.

0 Likes

Unfortunately, that didn't work.  In the I2C_1_MasterSendStop(), there's the following loop:

        /* Wait until stop has been generated */

        while(I2C_1_WAIT_STOP_COMPLETE(I2C_1_CSR_REG))

        {

        }

from which the call never returns.  This hangs the sensor task.

At this point, when I have the time, I'm going to create an I2C slave using an Arduino board and bitbanging, so I can stop the slave at certain points and try to recreate the error situation.  Right now I'm just adding a reboot call to get the board out of the situation.

0 Likes