这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » 嵌入式开发 » 国产MCU » APM32F103硬件IIC错误恢复

共2条 1/1 1 跳转至

APM32F103硬件IIC错误恢复

院士
2025-09-10 17:53:38     打赏

一、 引言
    背景介绍:
        APM32F103系列MCU作为一款与STM32F103高度兼容的国产芯片,其应用广泛性。
        硬件IIC外设的优势(效率高、节省CPU资源)与常见痛点(易锁死、抗干扰能力弱)。
    问题阐述:
        描述硬件IIC在通信过程中可能出现的错误场景:总线忙(BUSY)、仲裁丢失、地址无应答(ACK Failure)、数据无应答、总线错误等。
        这些错误如何导致IIC控制器进入“挂起”或“锁死”状态,表现为SDA或SCL线被拉低,整个通信瘫痪。
    本文目标:
        提供一套针对APM32F103硬件IIC的、系统性的错误检测、诊断和恢复方案,帮助开发者构建鲁棒的IIC通信程序。
    软硬件IIC对比:

硬件I2C错误恢复01.jpg

二、 APM32F103硬件IIC基础与错误寄存器分析
    硬件IIC主要寄存器概览:
        控制寄存器(IIC_CTLR1, IIC_CTLR2):配置使能、中断、ACK等。

硬件I2C错误恢复02.jpg

状态寄存器(IIC_STAR1, IIC_STAR2):核心重点,包含各种错误和事件状态标志。

硬件I2C错误恢复03.jpg

关键错误状态标志详解:

    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]):超时错误。

硬件I2C错误恢复05.jpg

三、 硬件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处理。










关键词: APM32F103     硬件     错误     恢复     I2C    

专家
2025-09-11 17:36:11     打赏
2楼

强!很少考虑IIC通讯异常导致死机的状况。学习了。


共2条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]