APM32芯得 EP.34 | 告别I2C“假死”——APM32F103硬件IIC防锁死设计
如遇开发技术问题,欢迎前往开发者社区,极海技术团队将在线为您解答~
极海官方开发者社区https://community.geehy.cn/
《APM32芯得》系列内容为用户使用APM32系列产品的经验总结,均转载自21ic论坛极海半导体专区,全文未作任何修改,未经原文作者授权禁止转载。
目录
一、 引言
二、 APM32F103硬件IIC基础与错误寄存器分析
三、 硬件IIC错误恢复的核心思想
四、 详细的错误恢复步骤与代码实现
五、 预防优于治疗:IIC通信的稳定性设计建议
六、 总结
一、 引言
-
背景介绍:
- APM32F103系列MCU作为一款高度兼容的国产芯片,具有应用广泛性。
- 硬件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. 重新初始化I2CI2C_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处理。