- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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, ®_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.
- Labels:
-
PSoC 5 Device Programming
-
PSoC 5LP
- Tags:
- i2c master
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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.
If you have chance to try it, please let us know if it works for your case.
moto
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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?
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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...
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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
Bragadeesh
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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);
}
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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.