一、概述
I2C(Inter-Integrated Circuit)是一个串行通信总线,它由飞利浦公司在1980年代为了让主机连接其它外设而定制的一种低速通信协议,目前广泛应用于手机、电脑、家电等嵌入式设备。
I2C使用两根数据总线,一根是数据线(SDA),一根是时钟线(SCL),采用双向漏极开路,并使用电阻进行上拉,I2C允许比较大的工作电压范围,但一般采用3.3V或者5V。设备地址使用7位长度和10位长度。I2C 传输速率有不同的模式:
·标准模式:100Kbit/s
·快速模式:400Kbit/s
·高速模式:3.4Mbit/s
I2C死锁是指在I2C通信过程中,由于两个或多个设备之间的竞争或错误操作导致I2C总线上的数据传输无法正常完成,从而导致整个系统无法继续正常工作。I2C死锁是一个非常常见的问题,可能会影响到整个电路板或系统的稳定性和可靠性。
二、问题现象
电池(芯片:BQ40Z80)与MCU之间采样I2C通信,设备在正常运行的情况下突然拔出一个电池,再插回去,导致电池一直通信不成功。逻辑分析仪查看实际波形如下所示:
1.设备正常运行过程中拔出电池再插回去产生如下波形
图2.0 电池拔插产生的波形
2.在拔插电池产生异常的情况下,把电池拔出MCU产生如下波形
图2.1 在产生异常的情况下拔出电池MCU产生的波形
3.电池拔出再插回去产生如下波形
图2.2 拔出电池再插回去产生的波形
根据上面的实测波形发现,设备正常通信的情况下突然拔插电池,会导致通信状态错乱,且无法恢复。
三、解决方法
I2C设备有些是支持热插拔,有些是不支持热插拔,而电池管理芯片有明确说明是支持热插拔,根据这个就可以从软件层面试着解决这个问题。由于这个芯片对时序要求比较严格,所以采用硬件I2C跟它通信。当遇到通信不正常的情况下,就复位I2C外设,代码如下所示:
if(Bq40z801ReadByte(BQ40Z80_DEV1_ADDR,STATUS_REG,&battery[0].status) == ESP_OK)
{
Bq40z801ReadByte(BQ40Z80_DEV1_ADDR,TEMPERATURE_REG,(uint16_t*)&battery[0].temperature);
Bq40z801ReadByte(BQ40Z80_DEV1_ADDR,VOLTAGE_REG,&battery[0].voltage);
Bq40z801ReadByte(BQ40Z80_DEV1_ADDR,CURRENT_REG,(uint16_t*)&battery[0].current);
Bq40z801ReadByte(BQ40Z80_DEV1_ADDR,PERCENT_REG,&battery[0].percent);
// Bq40z802ReadByte(BQ40Z80_DEV2_ADDR,0x0D,&battery[0].percent);
battery[0].charge_state = GetBatteryStatus(battery[0].status,battery[0].current);
}
else
{
Battery[0].temperature = 0;
Battery[0].voltage = 0;
Battery[0].current = 0;
Battery[0].percent = 0;
Battery[0].charge_state = 0;
Battery[0].status = 0;
// memset((char*)&Battery[0],0,sizeof(&Battery[0]));
ESP_LOGI(TAG,"byttery1 read fail\r\n");
I2cReset(I2C0_NUM,I2C0_SCL_PIN,I2C0_SDA_PIN);
}
其实I2C死锁的原因有很多种,包括硬件故障、软件编程错误、通信协议不一致等。为了避免I2C死锁问题,我们可以采取以下几种措施:
1.确保各个I2C设备的地址唯一,以避免地址冲突。
2.设计时合理规划好I2C总线的拓扑结构,避免过长的传输线路和过多的设备导致信号失真和干扰。
3.在设计I2C通信时,合理设置I2C时序参数,以确保数据传输的正确性和稳定性。
4.对于需要进行复杂数据传输的场景,可以利用I2C的仲裁机制,避免设备之间的竞争和死锁现象发生。
5.在程序设计上,应该合理使用锁和中断处理机制,以避免由于程序逻辑错误而导致I2C死锁问题发生。
6.为了避免I2C死锁问题,我们需要在硬件和软件两方面重视I2C通信的稳定性和可靠性,并进行合理规划和设计。
7.如果I2C设备对时序没有严格的要求,建议使用软件模拟I2C,这样可以避免很多问题。