ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

I2C从模式设计实战:时钟延展与死锁恢复机制详解

I2C从模式设计实战:时钟延展与死锁恢复机制详解 1. 从模式设计与总线鲁棒性到底在解决什么问题I2C 总线用两根线挂一堆设备看起来简单实际做产品的时候最让人头疼的往往不是“怎么把数据发出去”而是“从设备被主设备按在地上摩擦时怎么保证不丢数据、不锁死、还能自己爬起来”。这次要聊的就是 I2C 从模式设计里两个非常硬核的话题时钟延展的落地实现以及死锁恢复机制。先说清楚适用人群。如果你只是用现成的传感器模块拿 Arduino 或者 STM32 HAL 库调个HAL_I2C_Master_Transmit就能跑那这篇文章的前半部分你可以快速扫过后半部分的死锁恢复对你会有用——因为哪怕你只做主机也会遇到总线被从设备拉死的情况。但如果你需要自己写从机固件比如用一颗 MCU 模拟 I2C 从设备去对接另一个主控或者你要做 I2C 扩展芯片、OLED 驱动板、编码器接口板这类东西那时钟延展和死锁恢复就是绕不过去的两座山。我做过好几个从模式的项目最典型的是一个用国产 MCU 模拟 I2C 从机的板子主控是另一颗跑 Linux 的芯片。一开始没做时钟延展主控速率拉到 400kHz从机固件稍微忙一点就丢字节。后来加了时钟延展又遇到主控不支持延展导致总线卡死的问题。再后来加了死锁恢复才算真正稳定。这一路踩的坑正好对应今天要讲的两个核心点。时钟延展的本质是从设备在处理不过来的时候把 SCL 线拉低强制主设备等待。这听起来简单但落地时涉及 GPIO 方向切换、中断优先级、时序窗口计算一个细节没处理好延展就变成“延死”。死锁恢复则是当总线因为异常情况比如从机在发 ACK 时被复位、电源抖动导致状态机跑飞被某根线永久拉低时如何通过发送 9 个时钟脉冲把从机状态机打回空闲。这两个机制配合起来才能让 I2C 从模式设计真正达到“总线鲁棒性”的要求。下面我会按照实际项目落地的顺序把设计思路、关键细节、实操步骤、排查技巧全部拆开讲。每个部分都会解释“为什么这么做”而不只是“怎么做”。2. 从模式设计的整体思路与方案选型2.1 为什么从模式比主模式难做很多人觉得 I2C 主模式难因为要处理多设备仲裁、时钟同步。但从模式其实更考验设计功底原因有三个。第一从模式是被动响应的。主设备什么时候发 START、什么时候给时钟从机无法预知。你只能在边沿中断里以最快速度反应反应慢了就丢数据。这就对中断延迟和代码路径有极高要求。第二从模式需要双向控制 SDA。发送 ACK 的时候要拉低 SDA接收数据的时候要释放 SDA。如果 GPIO 方向切换时机不对要么产生毛刺被主设备误判为 START/STOP要么总线冲突。第三从模式往往需要时钟延展。主设备给的时钟节奏是固定的但从机内部可能正在写 Flash、处理上一个字节、或者刚被中断打断。这时候唯一合法的“拖时间”手段就是拉低 SCL。我见过不少项目为了省事直接用硬件 I2C 外设做从机。硬件外设确实省心但灵活性差。比如某些 MCU 的硬件 I2C 从模式不支持时钟延展或者延展深度有限。一旦主设备速率高、从机任务重就很容易出问题。所以如果你的项目对鲁棒性要求高用 GPIO 模拟从模式反而是更可控的方案——虽然代码量大但每个时序点你都能自己掌控。2.2 时钟延展的两种实现路径对比时钟延展在从机侧有两种常见实现方式我列个表对比一下。实现方式原理优点缺点适用场景硬件外设自动延展I2C 外设内部在需要时自动拉低 SCL不占 CPU时序精准延展深度固定部分 MCU 不支持主频高、任务轻的场合GPIO 手动延展在中断里主动拉低 SCL处理完再释放灵活可控延展时间任意占 CPU时序依赖中断响应从机任务重、需要长延展的场合我选的是 GPIO 手动延展。原因很简单我的从机要在接收命令后去操作 SPI Flash这个操作可能持续几百微秒。硬件外设的自动延展通常只覆盖“数据寄存器未准备好”这种短时间场景几百微秒的延展它撑不住。手动延展虽然土但能精确控制延展多久、什么时候释放。2.3 死锁恢复为什么必须做死锁的典型场景是这样的主设备发了一个字节从机准备回 ACK结果从机在这一刻被看门狗复位了。从机复位后 GPIO 恢复默认状态SDA 可能被外部上拉电阻拉高但 SCL 如果之前被从机拉低过复位后释放主设备却已经进入等待 ACK 的状态。更糟的情况是从机在传输过程中被复位SDA 被从机拉低后没释放主设备认为总线一直忙永远发不出 START。还有一种情况是电源抖动。从机供电不稳状态机跑飞到某个中间状态SCL 或 SDA 被异常拉低。主设备侧如果只做超时重试而不做总线恢复就会一直卡在那里。死锁恢复的标准做法是主设备检测到总线异常后把 SCL 配置为开漏输出发送 9 个时钟脉冲。每发一个脉冲检查 SDA 是否释放。如果 9 个脉冲后 SDA 恢复高电平再发一个 STOP 条件总线就回到空闲状态。这个机制必须做在主设备侧但从机侧也要配合——从机在检测到自己状态异常时应该主动释放 SDA 和 SCL不要死拽着不放。3. 时钟延展落地的核心细节与实操要点3.1 时钟延展的时序窗口计算时钟延展不是随便拉低 SCL 就行它有一个合法的时序窗口。以标准模式 100kHz 为例一个 SCL 周期是 10 微秒高电平时间和低电平时间各约 5 微秒。从机只能在 SCL 为低电平期间拉低它如果在 SCL 为高电平期间拉低主设备会认为这是总线冲突或者 START/STOP 条件。具体来说从机在检测到 SCL 下降沿后有一个很短的时间窗口可以决定是否延展。这个窗口取决于主设备的时钟低电平时间。如果主设备低电平时间是 4.7 微秒标准模式最小值从机必须在 SCL 下降沿后的 3.5 微秒内把 SCL 拉低否则主设备可能已经准备释放 SCL 了。我在实际项目里用的策略是在 SCL 下降沿中断里立刻判断当前是否需要延展。如果需要马上把 SCL 对应的 GPIO 切换为输出并拉低。这个操作要在 1 微秒内完成所以中断优先级要设到最高中断服务函数里不能有浮点运算、不能有函数调用开销大的操作。计算一下假设 MCU 主频 72MHz一条指令约 14 纳秒。从进中断到执行 GPIO 拉低大概需要 20 到 30 条指令也就是 300 到 400 纳秒。加上中断响应延迟总共 1 微秒以内可以完成。这个余量是够的但如果你用的是 8 位 MCU 跑 16MHz那就要仔细优化了。3.2 GPIO 方向切换的坑时钟延展涉及 SCL 线的方向切换。正常接收时SCL 是输入需要延展时SCL 要变成输出并拉低延展结束后要变回输入释放 SCL。这里有个大坑切换方向时会产生毛刺。如果你先把 GPIO 配置为输出再写数据寄存器拉低中间会有一个短暂的高电平输出这个毛刺会被主设备误判。正确的顺序是先把输出数据寄存器写 0再把 GPIO 配置为输出。这样切换瞬间输出就是低电平没有毛刺。同理释放 SCL 时先把 GPIO 配置为输入再写数据寄存器。顺序反了也会出问题。我用的是 STM32 的 BSRR 寄存器来操作因为它可以原子性地设置和清除引脚避免读改写带来的竞争。具体代码大概是这样// 拉低 SCL 进行延展 GPIOB-BSRR (uint32_t)GPIO_PIN_6 16; // 先写清除位 GPIOB-CRL ~(0xF 24); // 清除配置 GPIOB-CRL | (0x3 24); // 配置为通用推挽输出 // 释放 SCL GPIOB-CRL ~(0xF 24); GPIOB-CRL | (0x4 24); // 配置为浮空输入注意这里用的是推挽输出而不是开漏。因为延展期间从机是唯一拉低 SCL 的设备主设备此时已经释放了 SCL所以推挽输出不会冲突。但如果你不确定主设备是否完全释放用开漏输出更安全只是需要外部上拉电阻配合。3.3 延展时长的控制策略延展多久是个策略问题。延展太短从机没处理完就释放了主设备继续给时钟从机还是跟不上。延展太长主设备可能触发超时或者整个系统响应变慢。我的做法是在需要延展的操作开始前先估算最坏情况耗时。比如写一个字节到 SPI Flash页编程最坏 3 毫秒。那我就把延展时间设为 3.5 毫秒留 0.5 毫秒余量。延展结束后从机继续处理如果还没完再延展一次。但这里有个问题主设备可能不支持无限延展。有些主设备的 I2C 控制器有超时机制比如 25 毫秒没收到 ACK 就报错。所以延展时间不能超过主设备的超时阈值。我一般把单次延展控制在 5 毫秒以内如果操作确实需要更久就分多次延展每次延展后释放 SCL 让主设备走一个时钟然后再延展。还有一种更优雅的做法从机在接收完命令后先回一个 ACK然后在主设备发下一个时钟之前拉低 SCL。这样主设备认为从机在准备数据会一直等。等从机处理完释放 SCL主设备继续。这个做法对主设备最友好因为它符合 I2C 协议的标准延展语义。3.4 中断优先级与临界区保护时钟延展的实时性要求极高所以 SCL 边沿检测中断必须是最高优先级。但这里有个矛盾如果从机还有其他高优先级中断比如 SPI 传输完成中断可能会打断 I2C 中断导致延展不及时。我的解决方案是把 I2C 从机中断设为最高优先级SPI 中断设为次高。在 I2C 中断里只做最紧急的操作——判断是否需要延展、拉低 SCL。其他处理放到主循环或者低优先级任务里。这样即使 SPI 中断来了也要等 I2C 中断处理完才能响应而 I2C 中断处理时间很短不会影响 SPI 的实时性。另外在操作 SCL 和 SDA 的代码段里要关中断。因为如果在这段代码执行过程中被其他中断打断而那个中断也操作了 GPIO就可能产生时序错误。关中断的时间要尽可能短一般不超过 2 微秒。4. 死锁恢复的完整实现与排查技巧4.1 死锁检测的三种方法死锁检测是恢复的前提。你得先知道总线卡住了才能去恢复。我常用的检测方法有三种。第一种是超时检测。主设备发起传输后如果在一定时间内没有完成就认为总线异常。这个时间阈值要根据实际传输长度和速率来定。比如传输 10 个字节400kHz 速率理论耗时约 250 微秒。那超时阈值可以设为 5 毫秒留足够余量。第二种是总线电平检测。在发起传输前先检查 SDA 和 SCL 是否都是高电平。如果 SCL 被拉低说明总线忙或者死锁。如果 SDA 被拉低而 SCL 是高说明有设备在拉 SDA也可能是死锁。第三种是ACK 检测。主设备发送地址后如果从机没有回 ACK可能是从机不在或者从机状态异常。这时候可以尝试恢复。我一般把三种方法结合起来传输前检查总线电平传输中超时检测传输后 ACK 检测。任何一步异常都触发恢复流程。4.2 9 个时钟脉冲的恢复流程恢复流程的核心是发送 9 个 SCL 脉冲。为什么是 9 个因为 I2C 传输一个字节是 8 个时钟加 1 个 ACK 时钟。如果从机在某个字节的中间状态卡住最多需要 9 个时钟就能把它推完一个完整字节回到空闲状态。具体步骤把 SCL 配置为开漏输出SDA 配置为输入。循环 9 次拉低 SCL延时半个周期释放 SCL延时半个周期。每次释放 SCL 后读取 SDA 电平。如果 SDA 变成高电平说明从机已经释放了 SDA可以提前结束。9 个脉冲后发送 STOP 条件拉低 SDA拉高 SCL再拉高 SDA。检查 SDA 和 SCL 是否都恢复高电平。如果是恢复成功如果不是重复上述流程最多重试 3 次。这里的关键是延时时间。恢复时的时钟频率不能太高否则从机可能反应不过来。我一般用 100kHz 对应的周期也就是 10 微秒。拉低 5 微秒释放 5 微秒。这个速率足够慢任何从机都能跟上。还有一个细节发送 STOP 条件时要确保 SCL 是高电平。如果 SCL 被从机拉低STOP 条件无效。所以第 4 步之前要先确认 SCL 已经释放。4.3 从机侧配合恢复的注意事项死锁恢复不只是主设备的事从机侧也要配合。从机在以下情况下应该主动释放总线检测到复位或者看门狗触发时在复位前把 SCL 和 SDA 配置为输入释放总线。检测到状态机异常时比如收到了非法命令、或者内部处理超时主动复位 I2C 状态机释放总线。电源电压低于阈值时提前释放总线避免在低压下产生错误时序。我在从机固件里加了一个“总线释放”函数在系统复位前、低电压检测中断里、以及状态机异常处理里都会调用。这个函数就是把 SCL 和 SDA 的 GPIO 配置为输入然后延时一段时间确保电平稳定。另外从机在正常传输过程中如果发现自己需要延展但延展时间可能超过主设备超时阈值应该主动放弃本次传输释放总线让主设备超时后触发恢复。这比死拽着总线不放要好。4.4 常见问题速查表现象可能原因排查方法解决方案主设备发 START 后无响应从机 SCL 被拉低用示波器看 SCL 电平执行 9 脉冲恢复传输中随机丢字节时钟延展不及时测中断响应时间提高中断优先级优化代码从机回 ACK 时主设备报错SDA 方向切换毛刺示波器看 SDA 波形先写数据寄存器再切方向恢复后仍然死锁从机电源异常测从机供电电压检查电源加复位电路高速率下延展失效延展窗口计算错误计算低电平时间余量降低速率或优化延展逻辑多从机场景下恢复失败其他从机干扰逐个断开从机测试确认故障从机单独恢复这个表是我在实际调试中总结的基本上覆盖了 90% 的 I2C 从模式问题。遇到问题的时候先查表再用示波器确认效率会高很多。5. 实操过程与核心环节实现5.1 硬件准备与引脚配置我用的硬件平台是一颗 STM32F103 作为 I2C 从机主控是一颗跑 Linux 的 SoC。从机的 I2C 引脚是 PB6SCL和 PB7SDA外部接了 4.7k 上拉电阻到 3.3V。引脚配置的关键点默认状态SCL 和 SDA 都配置为浮空输入让外部上拉电阻把电平拉高。需要输出时配置为开漏输出这样不会和主设备冲突。需要延展时SCL 配置为推挽输出拉低因为此时主设备已经释放了 SCL。我试过用开漏输出做延展发现拉低速度不够快因为开漏输出依赖外部上拉电阻上升沿和下降沿都有 RC 延迟。推挽输出拉低是主动的速度快很多。但推挽输出只能在确认主设备释放 SCL 后使用否则会冲突。5.2 从机状态机设计从机状态机是整个固件的核心。我设计的状态包括IDLE等待 START 条件。ADDR接收地址字节判断是否匹配。ACK_ADDR回 ACK 或 NACK。DATA_RX接收数据字节。ACK_DATA回 ACK 或 NACK。DATA_TX发送数据字节。WAIT_ACK等待主设备 ACK。STRETCH时钟延展状态。状态转移由 SCL 边沿中断驱动。每个 SCL 上升沿采样 SDA每个 SCL 下降沿准备下一个数据位。这里有个设计决策状态机用 switch-case 还是函数指针我选的是 switch-case因为代码量小执行路径确定不会有函数调用开销。在中断里执行每一纳秒都很宝贵。5.3 时钟延展的具体代码实现延展逻辑放在 SCL 下降沿中断里。伪代码void I2C_SCL_FallingEdge_ISR(void) { // 判断是否需要延展 if (need_stretch) { // 拉低 SCL GPIOB-BSRR GPIO_PIN_6 16; GPIOB-CRL ~(0xF 24); GPIOB-CRL | (0x3 24); // 执行需要延展的操作 do_stretch_work(); // 释放 SCL GPIOB-CRL ~(0xF 24); GPIOB-CRL | (0x4 24); need_stretch 0; } // 正常的状态机处理 i2c_state_machine(); }do_stretch_work()里放的是耗时操作比如写 Flash、计算校验和。这个函数执行时间就是延展时间。如果执行时间不确定可以在里面加超时保护超过一定时间就强制释放 SCL。5.4 死锁恢复的代码实现恢复函数在主设备侧调用从机侧不需要主动调用但需要配合释放总线。void I2C_Bus_Recovery(void) { // 配置 SCL 为开漏输出SDA 为输入 GPIO_InitTypeDef gpio; gpio.Mode GPIO_MODE_OUTPUT_OD; gpio.Pin GPIO_PIN_6; HAL_GPIO_Init(GPIOB, gpio); gpio.Mode GPIO_MODE_INPUT; gpio.Pin GPIO_PIN_7; HAL_GPIO_Init(GPIOB, gpio); // 发送 9 个时钟脉冲 for (int i 0; i 9; i) { HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, GPIO_PIN_RESET); delay_us(5); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, GPIO_PIN_SET); delay_us(5); // 检查 SDA 是否释放 if (HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_7) GPIO_PIN_SET) { break; } } // 发送 STOP 条件 gpio.Mode GPIO_MODE_OUTPUT_OD; gpio.Pin GPIO_PIN_7; HAL_GPIO_Init(GPIOB, gpio); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_RESET); delay_us(5); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, GPIO_PIN_SET); delay_us(5); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_SET); delay_us(5); // 恢复默认配置 gpio.Mode GPIO_MODE_INPUT; gpio.Pin GPIO_PIN_6 | GPIO_PIN_7; HAL_GPIO_Init(GPIOB, gpio); }这个函数我实测下来很稳基本上 3 次重试以内都能恢复。唯一需要注意的是delay_us的精度如果系统主频高用循环延时可能不准最好用定时器。5.5 实测数据与性能评估我在实验室里做了几组测试数据如下测试项条件结果无延展传输400kHz连续 1000 次传输丢包率 2.3%加延展传输400kHz连续 1000 次传输丢包率 0%延展响应时间72MHz 主频平均 0.8 微秒死锁恢复时间9 脉冲 STOP平均 120 微秒恢复成功率100 次模拟死锁100%从数据看加延展后丢包率从 2.3% 降到 0效果非常明显。延展响应时间 0.8 微秒远小于 3.5 微秒的窗口要求余量充足。死锁恢复 120 微秒完成对系统整体响应影响很小。6. 常见问题与排查技巧实录6.1 延展导致主设备超时怎么办这是最常见的问题。主设备的 I2C 控制器有超时机制从机延展时间太长主设备直接报错。我的解决思路是先查主设备的超时阈值然后把单次延展时间控制在阈值的一半以内。如果操作确实需要更久就分多次延展。比如主设备超时是 25 毫秒那单次延展不超过 10 毫秒。如果 Flash 写需要 30 毫秒就分 3 次延展每次 10 毫秒中间释放 SCL 让主设备走一个时钟。这样主设备不会超时从机也能完成操作。6.2 从机复位后总线卡死怎么预防从机复位后 GPIO 恢复默认状态如果默认状态是输出低电平就会把总线拉死。预防措施是在从机固件的启动代码里第一时间把 I2C 引脚配置为输入。这个操作要在初始化任何外设之前完成。另外可以在硬件上加一个复位电路从机复位时同时拉低主设备的复位引脚让主从一起复位。但这个方案成本高一般用软件解决就够了。6.3 多从机场景下的恢复策略多从机场景下死锁恢复要更小心。因为 9 个时钟脉冲会发给总线上所有从机如果某个从机正在正常传输这 9 个脉冲会干扰它。我的做法是先通过地址扫描确认哪个从机异常然后单独恢复。如果无法单独恢复就逐个断开从机找到故障设备后再恢复。恢复完成后重新初始化所有从机。还有一种情况是某个从机持续拉低 SDA导致总线一直忙。这时候 9 个脉冲可能不够需要更多脉冲。我试过最多发 27 个脉冲3 轮基本上都能恢复。6.4 示波器抓波形的技巧调试 I2C 问题示波器是必备工具。我一般用双通道一个通道看 SCL一个通道看 SDA。触发方式设为 SCL 下降沿触发这样能抓到完整的传输过程。看波形的时候重点关注START 条件是否干净SDA 下降沿时 SCL 必须是高电平。ACK 位是否被正确拉低拉低时间是否足够。时钟延展时 SCL 被拉低的时间长度。STOP 条件是否干净SDA 上升沿时 SCL 必须是高电平。如果波形上有毛刺多半是 GPIO 方向切换的问题。如果 SCL 被拉低时间过长检查延展逻辑是否有死循环。6.5 独家避坑经验第一个坑延展时不要关全局中断。我一开始为了保时序在延展代码里关了全局中断结果系统其他任务全部卡住看门狗都喂不了。后来改成只关 I2C 相关中断其他中断正常响应。第二个坑SDA 采样时机。I2C 协议规定 SDA 在 SCL 高电平期间必须稳定所以采样应该在 SCL 上升沿。我一开始在下降沿采样数据全是错的。后来改成上升沿采样下降沿准备下一个数据位就对了。第三个坑上拉电阻阻值。4.7k 是常用值但如果总线电容大上升沿会变缓。我遇到过 400kHz 下上升沿超过 1 微秒的情况导致数据错误。后来把上拉电阻改成 2.2k问题解决。计算方法是上升时间 0.847 × R × C其中 C 是总线电容。400kHz 下上升时间要小于 300 纳秒反推 R 和 C 的乘积要小于 354 欧姆·微法。第四个坑从机地址冲突。多从机场景下如果两个从机地址相同会同时回 ACK导致 SDA 电平异常。排查方法是逐个断开从机看哪个从机在单独工作时正常。7. 从模式设计的扩展思考7.1 时钟延展与中断延迟的权衡时钟延展的可靠性直接取决于中断延迟。如果系统里有很多高优先级中断I2C 中断可能被延迟导致延展不及时。我的做法是把 I2C 中断设为最高优先级其他中断设为次高。但这样又会影响其他中断的实时性。权衡的方法是评估各个中断的最坏响应时间要求。I2C 延展窗口是 3.5 微秒所以 I2C 中断延迟必须小于 3.5 微秒。其他中断如果要求更宽松就可以让 I2C 优先。如果其他中断也要求很严格那就需要考虑用硬件 I2C 外设或者提高 MCU 主频。7.2 死锁恢复的自动化策略死锁恢复最好是自动化的不需要人工干预。我在主设备侧加了一个监控任务定期检查 I2C 总线状态。如果发现总线忙超过一定时间就自动触发恢复流程。恢复完成后重新初始化 I2C 控制器继续正常传输。这个监控任务的周期我设为 100 毫秒。太短了会频繁检查浪费 CPU太长了恢复不及时。100 毫秒是个比较平衡的值。7.3 从模式设计的测试方案从模式设计的测试比主模式复杂因为从机是被动的你很难模拟各种异常情况。我的测试方案包括正常传输测试连续传输 10000 次检查丢包率。延展测试在从机里人为加入延时测试延展是否正常。死锁测试人为拉低 SCL 或 SDA测试恢复是否成功。电源抖动测试用可调电源模拟电压波动测试从机是否异常。复位测试在传输过程中复位从机测试总线是否卡死。这些测试跑一遍基本上能覆盖 95% 的异常场景。剩下的 5% 靠现场调试和日志分析。7.4 从模式设计的未来优化方向从模式设计还有几个可以优化的方向。一是用 DMA 辅助数据传输减少 CPU 占用。二是用硬件 I2C 外设做从机配合 GPIO 做延展兼顾性能和灵活性。三是加入错误计数和自诊断功能提前发现潜在问题。我目前在做的一个优化是把从机状态机做成可配置的支持不同的 I2C 速率和延展策略。这样同一套固件可以适配不同的主设备减少重复开发。8. 个人实操体会与建议做 I2C 从模式设计这几年我最大的体会是协议看起来简单细节全是坑。时钟延展和死锁恢复这两个机制文档里往往一笔带过但实际落地时每个细节都要自己抠。我的建议是如果你要做从模式设计先把示波器准备好把 I2C 协议时序图打印出来贴在墙上。每写一段代码就用示波器验证一下波形。不要相信仿真仿真和实际硬件差距很大。另外延展时间宁短勿长。短了可以多次延展长了主设备直接超时。死锁恢复宁多勿少9 个脉冲不够就 18 个18 个不够就 27 个。恢复流程多跑几轮总比总线卡死强。最后分享一个小技巧在从机固件里加一个“总线状态寄存器”记录最近一次传输的状态、延展次数、恢复次数。出问题的时候读这个寄存器能快速定位是延展问题还是恢复问题。这个寄存器我用了两年帮我省了很多调试时间。
返回列表