### STM32 I2C接口进入Busy状态不能退出的问题分析及解决方案
#### 问题背景
在STM32F103VDT6芯片的应用中,一位客户反馈了一个关于I2C接口的问题。具体表现为:在产品可靠性测试过程中,经过长时间运行后,STM32无法正常与EEPROM进行通信。即使通过复位引脚NRST对STM32进行复位,也无法解决此问题;唯有切断电源并重新上电才能恢复正常。通过进一步测试发现,反复对STM32进行复位操作可以轻易复现此故障。
#### 问题调研
为了诊断这个问题,工程师们修改了软件代码,通过打印输出的方式监控I2C通信过程以及I2C接口的寄存器状态。当出现通信故障时,发现状态寄存器SR2中的Busy位被设置为1,同时状态寄存器SR1中的ARLO位也被设置为1。使用示波器观察I2C总线时,可以看到SCL保持在高电平,而SDA则保持在低电平。即使将STM32的复位脚拉低至地,SCL和SDA的状态也未发生变化。经检查原理图,确认I2C总线上仅连接了STM32和EEPROM两个器件。
#### 问题分析
初步分析认为,问题可能在于EEPROM驱动I2C总线进入了一种非空闲状态,这导致STM32在试图接管总线时发生总线仲裁失败,从而失去了对总线的控制能力,进而无法启动数据传输。这种非空闲状态可能是由于通信过程中的意外中断造成的。通过对STM32进行复位而重现此现象,一定程度上验证了这种可能性。然而,目前尚无充分的实验数据或理论支持来证实这一点,因此不排除还有其他潜在因素导致此问题。
#### 解决方案
为了解决这个问题,工程师们在软件中添加了I2C总线修复功能。具体步骤如下:
1. **检测Busy状态**:在每次尝试发送起始条件前先检测状态寄存器SR2中的Busy位。如果Busy位为1,则表示总线上存在异常情况。
2. **接管SCL和SDA**:使用GPIO的开漏模式(Open Drain)代替I2C通信接口来接管SCL和SDA两个引脚。
3. **发送时钟脉冲**:通过翻转GPIO向SCL信号线上发送一个高电平脉冲,脉冲宽度及间隔均为10μs。每次发送脉冲后,检查SDA信号是否变为高电平。
4. **SDA信号校正**:如果SDA信号已经变为高电平,则将SCL拉高,并向SDA信号线发送一个10μs宽的低电平脉冲。
5. **恢复I2C接口**:将SCL和SDA两个引脚交还给I2C接口,并通过将CR1寄存器中的SWRST位置1然后再清0来复位I2C接口,使其退出Busy状态。
#### 技术细节解释
- **状态寄存器SR1和SR2**:这两个寄存器分别用于存储I2C接口的状态信息。其中,SR2中的Busy位表示I2C总线当前是否忙碌;SR1中的ARLO位则表示地址丢失(即总线仲裁失败)。
- **总线仲裁**:当多个I2C主机尝试同时访问同一总线时会发生总线仲裁冲突。在这种情况下,具有较低地址的主机将获得总线控制权。
- **I2C接口复位**:通过设置CR1寄存器中的SWRST位可以实现I2C接口的软件复位,清除所有状态寄存器中的状态位,使接口返回初始状态。
#### 结论
针对STM32F103VDT6芯片在与EEPROM通信过程中出现的I2C接口进入Busy状态不能退出的问题,本文详细分析了问题的原因,并提供了一套有效的解决方案。通过软件层面的改进,成功解决了此故障,提高了系统的稳定性和可靠性。未来,对于类似问题的处理,可以考虑增加更多的容错机制或者优化硬件设计,以进一步提升系统的健壮性。