一、 引言
背景介绍:
APM32F103系列MCU作为一款与STM32F103高度兼容的国产芯片,其应用广泛性。
硬件IIC外设的优势(效率高、节省CPU资源)与常见痛点(易锁死、抗干扰能力弱)。
问题阐述:
描述硬件IIC在通信过程中可能出现的错误场景:总线忙(BUSY)、仲裁丢失、地址无应答(ACK Failure)、数据无应答、总线错误等。
这些错误如何导致IIC控制器进入“挂起”或“锁死”状态,表现为SDA或SCL线被拉低,整个通信瘫痪。
本文目标:
提供一套针对APM32F103硬件IIC的、系统性的错误检测、诊断和恢复方案,帮助开发者构建鲁棒的IIC通信程序。
软硬件IIC对比:
二、 APM32F103硬件IIC基础与错误寄存器分析
硬件IIC主要寄存器概览:
控制寄存器(IIC_CTLR1, IIC_CTLR2):配置使能、中断、ACK等。
状态寄存器(IIC_STAR1, IIC_STAR2):核心重点,包含各种错误和事件状态标志。
关键错误状态标志详解:
BUSY** (IIC_STAR2[1]):总线忙标志,指示IIC总线是否正处于通信状态。
BERR** (IIC_STAR1[8]):总线错误,检测到非法的起始或停止条件。
AL** (IIC_STAR1[9]):仲裁丢失,多主模式下失去总线控制权。
AE** (IIC_STAR1[10]):应答失败,发送地址或数据后未收到ACK。
OVRUR** (IIC_STAR1[11]):超载/溢出错误,数据寄存器读写过快。
TTE** (IIC_STAR1[14]):超时错误。
三、 硬件IIC错误恢复的核心思想
根本原因:时钟线(SCL)或被错误拉低的数据线(SDA)导致总线死锁。
恢复核心:模拟时钟脉冲。通过软件控制GPIO,产生一定数量的时钟信号(SCL),迫使从设备释放SDA线,从而解除总线锁死状态。
恢复流程三步走:
第1步:检测与诊断 - 通过状态寄存器判断错误类型。
第2步:尝试软复位 - 操作IIC外设的SWRST位,尝试软件复位IIC控制器。
第3步:硬件恢复(最后手段) - 如果软复位无效,则切换到GPIO模式,模拟时钟序列“撬开”总线。
四、 详细的错误恢复步骤与代码实现
错误检测:
在IIC中断服务程序(ISR)或状态查询中,检测上述错误标志位(BERR, AL, AE等)。
初步处理与软件复位:
清除错误标志。
关闭IIC外设(I2CEN=0)。
置位软件复位位(IIC_CTLR1中的SWRST位)。
延时片刻后,清除SWRST位,重新配置并使能IIC外设(I2CEN=1)。
代码示例:
void I2C_Isr(void) { uint8_t det; char dat; if ( I2C_ReadIntFlag(I2C1,I2C_INT_FLAG_BERR) == SET) { //清除错误标志。 I2C_ClearIntFlag(I2C1,I2C_INT_FLAG_BERR); //关闭IIC外设(I2CEN=0) I2C_Disable(I2C1); //置位软件复位位(IIC_CTLR1中的SWRST位) I2C_EnableSoftwareReset(I2C1); Delay(5); //清除SWRST位 I2C_DisableSoftwareReset(I2C1); //重新配置并使能IIC外设(I2CEN=1) I2C_Enable(I2C1); } if ( I2C_ReadIntFlag(I2C1,I2C_INT_FLAG_AE) == SET) { //清除错误标志。 I2C_ClearIntFlag(I2C1,I2C_INT_FLAG_AE); //关闭IIC外设(I2CEN=0) I2C_Disable(I2C1); //置位软件复位位(IIC_CTLR1中的SWRST位) I2C_EnableSoftwareReset(I2C1); Delay(5); //清除SWRST位 I2C_DisableSoftwareReset(I2C1); //重新配置并使能IIC外设(I2CEN=1) I2C_Enable(I2C1); } if ( I2C_ReadIntFlag(I2C1,I2C_INT_FLAG_AL) == SET) { //清除错误标志。 I2C_ClearIntFlag(I2C1,I2C_INT_FLAG_AL); //关闭IIC外设(I2CEN=0) I2C_Disable(I2C1); //置位软件复位位(IIC_CTLR1中的SWRST位) I2C_EnableSoftwareReset(I2C1); Delay(5); //清除SWRST位 I2C_DisableSoftwareReset(I2C1); //重新配置并使能IIC外设(I2CEN=1) I2C_Enable(I2C1); } }
终极硬件恢复(GPIO模拟时钟):
前提:如果软复位后总线BUSY标志仍无法清除。
步骤:
a. 将IIC的SCL和SDA引脚配置为开漏输出模式的GPIO。
b. 确保初始状态:SCL输出高,SDA输出高(释放)。
c. 如果SDA被拉低(总线仍锁死),则开始模拟时钟:
将SCL拉低,延时。
将SCL释放(拉高),延时。
重复以上过程若干次(如9-16次),同时监测SDA是否变为高电平。
d. 一旦SDA变高,立即发送一个“停止条件”(先拉低SDA->拉低SCL->释放SCL->释放SDA)。
e. 将GPIO重新映射回IIC外设。
f. 重新初始化IIC外设。
代码示例:
void IIC_Recovery() { GPIO_Config_T gpioConfigStruct = {0}; // 1. 配置SCL为开漏输出 gpioConfigStruct.pin = GPIO_PIN_6; gpioConfigStruct.mode = GPIO_MODE_AF_OD; GPIO_Config(GPIOB, &gpioConfigStruct); // 2. 发生9个时钟脉冲 for(int i=0; i<9; i++) { GPIO_SetBit(GPIOB, GPIO_PIN_6); Delay(5); GPIO_ResetBit(GPIOB, GPIO_PIN_6); Delay(5); } // 3. 发送停止条件 GPIO_SetBit(GPIOB, GPIO_PIN_6); Delay(5); GPIO_SetBit(GPIOB, GPIO_PIN_7); // 4. 重新初始化I2C I2C_Init(); }
五、 预防优于治疗:IIC通信的稳定性设计建议
硬件设计:
确保上拉电阻阻值合适(通常4.7kΩ)。
布线时注意远离干扰源,必要时添加屏蔽,走线距离不应该过长。
在SCL和SDA线上添加适当的RC滤波或ESD保护器件。
软件设计:
添加超时机制:在任何等待标志位(如EV5, EV6)的地方加入超时判断,避免无限等待。
uint8_t I2C_Write(char * pBuffer) { uint16_t I2CTimeout = I2CT_LONG_TIMEOUT; while(I2C_ReadStatusFlag(I2C1, I2C_FLAG_BUSBSY)) { I2C_Init(); if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(4); } I2C_DisableInterrupt(I2C1, I2C_INT_EVT); /* Send START condition */ I2C_EnableGenerateStart(I2C1); I2CTimeout = I2CT_FLAG_TIMEOUT; /* EV5 */ while(!I2C_ReadEventStatus(I2C1, I2C_EVENT_MASTER_MODE_SELECT)) { if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(5); } /* Send address for write */ I2C_Tx7BitAddress(I2C1, 0xB0, I2C_DIRECTION_TX); I2CTimeout = I2CT_FLAG_TIMEOUT; /* EV6 */ while(!I2C_ReadEventStatus(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)) { if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(6); } /* While there is data to be written */ while(*pBuffer != '\0') { /* Send the current byte */ I2C_TxData(I2C1, *pBuffer); /* Point to the next byte to be written */ pBuffer++; I2CTimeout = I2CT_LONG_TIMEOUT; /* EV8 */ while (!I2C_ReadEventStatus(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTING)) { if((I2CTimeout--) == 0) { return I2C_TIMEOUT_UserCallback(8); } } } return 1; }
错误重试机制:在发生非致命错误(如AF)时,进行有限次数的重试。
定期监控:在多主系统中,定期检查总线状态。
六、 总结
硬件IIC高效但配置复杂,软件模拟灵活却消耗CPU资源。同时使用介绍硬件IIC错误的常见类型、核心恢复原理(模拟时钟)和分层恢复策略(软复位->硬件恢复)。因此建议必须添加总线锁死保护机制。同时如果处理数据量大,建议使用DMA处理。