Software Reset

iIC software rest

 

核心问题

当通信中的 I²C 设备(从设备)因为电源中断、系统复位或通信数据错误等原因而“卡住”或进入一种不可预知的状态时,它可能不再响应主控制器(通常是单片机或CPU)的命令。这时,物理上断电再上电(硬件复位)可以解决,但很不方便。

这个“软件复位”流程就是为了在不切断电源的情况下,通过一系列特定的信号操作,让卡住的设备恢复到正常的待机状态,以便重新开始通信。

步骤解析

这个过程可以理解为给设备发送一个“强制重启”的信号序列:

  1. Create a Start bit condition(产生一个起始条件)

    • 含义:在 I²C 协议中,起始条件(Start Condition) 是通信开始的信号。具体做法是在 SCL 为高电平期间,将 SDA 从高电平拉低。

    • 目的:这是启动任何 I²C 通信的标准第一步,用来唤醒总线上的设备,告诉它们“注意,要开始传输数据了”。

  2. Clock nine cycles(时钟九个周期)

    • 含义:主控制器继续产生 9 个时钟脉冲(SCL 高低电平切换)。在此期间,主控制器可以忽略 SDA 数据线上的状态(即不读取数据)。

    • 目的:这是整个流程的关键。卡住的设备可能正卡在等待发送或接收某个数据位的状态(比如它正在输出一个字节的第 7 位)。连续提供 9 个时钟脉冲(比一个完整字节 8 位多一位)可以确保:

      • 无论设备卡在什么状态,都能被“推着”完成当前数据的传输。

      • 这相当于“喂”给它足够多的时钟,让它把内部可能存在的错误状态“吐完”或“消化掉”,从而将其内部的状态机(State Machine)复位到一个已知的起点。

  3. Create another Start bit followed by stop bit condition(再产生一个起始条件,后跟一个停止条件)

    • 含义:

      • 另一个起始条件:再次产生一个标准的起始信号(SCL高时SDA由高变低)。

      • 停止条件(Stop Condition):在 SCL 为高电平期间,将 SDA 从低电平拉高。这是 I²C 协议中结束通信的信号。

    • 目的:

      • 这个 “起始信号 + 停止信号” 的组合是一个强制的、明确的终止命令。它向总线上所有的设备表明,之前的任何未完成的通信都被强制中止,总线现在被释放,并准备好开始一个全新的、干净的通信会话。

二、在RT-thread 中也有一个“stm32_i2c_bus_unlock”,路径libraries/HAL_Drivers/drv_soft_i2c.c文件

static rt_err_t stm32_i2c_bus_unlock(const struct stm32_soft_i2c_config *cfg)
{
    rt_int32_t i = 0;

    if (PIN_LOW == rt_pin_read(cfg->sda))
    {
        while (i++ < 9)
        {
            rt_pin_write(cfg->scl, PIN_HIGH);
            stm32_udelay(100);
            rt_pin_write(cfg->scl, PIN_LOW);
            stm32_udelay(100);
        }
    }
    if (PIN_LOW == rt_pin_read(cfg->sda))
    {
        return -RT_ERROR;
    }

    return RT_EOK;
}
 

为什么SDA会一直为低电平?

这通常是由于以下原因之一:

  1. 从设备故障:I²C从设备(如传感器、EEPROM等)内部逻辑卡死,其输出驱动器异常地将SDA线拉低且不再释放。

  2. 通信中断:在之前的通信中,主设备在从设备正在发送数据(特别是发送一个字节的“0”位,即拉低SDA)时突然停止了提供时钟(SCL),导致从设备“挂起”在输出低电平的状态。

  3. 硬件问题:PCB短路或硬件设计缺陷(如上拉电阻开路/未安装)也可能导致类似现象,但最常见的是前两种。

解决方案:先“解锁”总线

当检测到SDA被意外拉低时,在执行协议复位流程之前,必须先通过时钟同步(Clock Stretching Recovery) 的方式来尝试释放SDA线。具体步骤如下:

  1. 检测到SDA为低:主控制器在尝试发起起始条件前,先读取SDA线的电平状态,发现其为低。

  2. 发送时钟脉冲(最关键的一步):

    • 主控制器不再尝试控制SDA线,而是将SDA线配置为输入模式(高阻抗),只专注于控制SCL线。

    • 主控制器开始连续产生时钟脉冲(在SCL线上产生高低电平的变化),通常也是连续产生9个或更多的脉冲。

    • 目的:这个动作是“提醒”那个正在拉低SDA的从设备:“请继续完成你未完成的数据传输”。每个时钟脉冲都允许从设备继续发送它的下一个数据位。由于一个字节是8位,发送9个脉冲可以保证至少完成一整个字节的传输,并进入到应答位(ACK/NACK)阶段。在这个过程的最后,从设备应该会释放SDA线(将SDA拉高),以等待主设备发送应答位。

  3. 检查SDA是否释放:

    • 在发送时钟脉冲的过程中或之后,主控制器持续监控SDA线。

    • 如果成功:SDA线被从设备释放,变为高电平。此时总线已被“解锁”,主设备重新获得了对SDA线的控制权。

  4. 执行标准的软件复位流程:

    • 现在SDA线可以自由拉高拉低了,主设备便可以成功地执行您最初提到的三步复位流程:

      • a) 产生一个起始条件(SCL高时,SDA高→低)。

      • b) 时钟9个周期。

      • c) 产生一个起始条件后紧跟一个停止条件(SCL高时,SDA低→高)。

总结与比喻

这就像一个对话被意外打断,一个人(从设备)正张着嘴说半个字卡住了(拉低SDA)。

  • 标准软件复位:适用于对方只是走神没反应,但没妨碍你说话(SDA可被拉高)。

  • SDA被拉低:相当于对方用手捂住了你的嘴(SDA被拉低),你没法开口说“开始”(Start)或“结束”(Stop)。

  • 发送时钟脉冲:相当于你不对他说话,而是连续拍他的肩膀(发送SCL时钟),催促他:“快说!快说!把你那句话说完!”。直到他把手从你嘴上拿开(释放SDA),你才能重新开始正常的对话(执行起始/停止条件)。

因此,完整的恢复流程应该是:

  1. 判断:发现SDA被意外拉低。

  2. 解锁:(在不对SDA进行操作的情况下)连续产生SCL时钟脉冲(例如9个),直到SDA被释放变为高电平。

  3. 复位:一旦SDA恢复高电平,立即执行标准的“起始 -> 时钟9次 -> 起始+停止”软件复位序列。

很多现代的单片机I²C硬件控制器(如STM32的I²C外设)都有内置的硬件机制来自动检测和处理这种总线锁死的情况,通常称为“总线清除(Bus Clear)”或“时钟超时(Clock Timeout)”功能。

 
 
 
posted @ 2025-09-12 09:19  北溟有渔  阅读(14)  评论(0)    收藏  举报