ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

ESP-IDF I2C主模式读取随机崩溃排查:FIFO边界、状态机复位与ISR让出的三个坑

ESP-IDF I2C主模式读取随机崩溃排查:FIFO边界、状态机复位与ISR让出的三个坑 ESP-IDF I2C主模式读取随机崩溃排查FIFO边界、状态机复位与ISR让出的三个坑【免费下载链接】esp-idfEspressif IoT Development Framework. Official development framework for Espressif SoCs.项目地址: https://gitcode.com/GitHub_Trending/es/esp-idf用ESP-IDF的I2C主模式做读取时崩溃往往不挑日子长读偶发超时、连续读数据错位、并发场景直接HardFault重启后一切正常复现成了玄学。这些现象背后大多是同一组原因。本文围绕驱动源码 components/esp_driver_i2c/i2c_master.c 的读路径按复现—原理—最小改动的路线走一遍最后给一份排查清单。 复现路径三种最容易踩中的崩溃条件崩溃通常集中在三类操作里对号入座比看日志更快。长读一次读超过32字节超过硬件FIFO容量的数据比如EEPROM寄存器块、Flash ID序列号。特征是从某次开始偶发I2C transaction timeout detected重试几次又好了。连续读同一设备连续多段读。一旦中间某次出过NACK或超时后续传输读出来的数据整体错位像被毒化换设备或断电重启才恢复。多任务/多设备并发读两个任务轮流读写同一条总线偶发HardFault或总线卡死日志里出现clear bus failedSDA被从机拉低放不开。定位入口开 I2C 的 debug 日志后盯i2c.master这一行标签timeout 和 clear bus failed 分别指向后文的根因方向。⚙️ 根因一FIFO剩余空间怎么算才不溢出先看硬件。ESP32的I2C接收FIFO是一条固定32格的传送带从机每发一个字节占一格ISR来取货腾格子读长数据时硬件靠中断分批收一车、再发车。读命令下发前驱动要用传送带总长减去已预约但还没取走的格数算出本批能塞多少字节这个预约量就是read_len_static。踩坑点在这个减法上uint32_t fifo_len I2C_FIFO_LEN(i2c_master-base-port_num); *fifo_fill MIN(remaining_bytes, fifo_len - i2c_master-read_len_static);两个uint32_t相减一旦read_len_static残留了一个偏大的值结果不是变负而是回绕成一个天文数字MIN()直接放行全部剩余字节传送带当场灌爆表现就是超时或数据错乱。异常恢复路径没把预约量归零残留就会一直跟着走。 根因二状态机复位后传送带里的旧货清没清i2c_master.c里s_i2c_hw_fsm_reset有两条分支i2c_ll_master_fsm_rst(hal-dev); if (clear_bus) { ret s_i2c_master_clear_bus(i2c_master-base); }硬件FSM复位分支只重置状态机。状态机是发令员FIFO是传送带——发令员归位了传送带上的旧货还在。另一条软复位分支会做整片寄存器复位加重新初始化两条路径的彻底程度不一致。残留字节留在接收FIFO里下一笔传输取数时先捞到旧数据这就是毒化后越读越错的来源清总线超时后只关掉清总线状态机同样没碰FIFO。 根因三ISR里释放信号量后让出做没做长读的交接流程ISR从FIFO取数后释放二值信号量cmd_semphr任务醒来续发下一批读命令。ISR里释放信号量必须走xSemaphoreGiveFromISR它通过参数带回是否该让出CPU的标志标志为真却不让出高优先级任务只能干等下一个调度时机。这期间FIFO还在收数没人取货溢出就变成硬件超时。if (xPortInIsrContext()) { xSemaphoreGiveFromISR(i2c_master-cmd_semphr, do_yield); } else { xSemaphoreGive(i2c_master-cmd_semphr); }检查重点不是这一行本身而是整个ISR里每个return点任何提前退出都可能跳过末尾那次让出判断。✂️ 最小改动三处每处不超过五行一、钳住无符号下溢。把减法挪到有符号临时量里再和0取大int32_t avail (int32_t)fifo_len - (int32_t)i2c_master-read_len_static; *fifo_fill MIN(remaining_bytes, (uint32_t)MAX(avail, 0));余量算不出来就按0处理宁可这一批短一点也不让FIFO超装。二、复位后清掉两条FIFO。在硬件FSM复位分支补两行i2c_ll_master_fsm_rst(hal-dev); i2c_ll_txfifo_rst(hal-dev); i2c_ll_rxfifo_rst(hal-dev);和软复位分支的彻底程度对齐异常后的下一笔传输不再先读到旧字节。三、让出检查只留一处出口。把ISR里散落的提前return改成跳到一个统一出口出口处统一检查让出标志if (HPTaskAwoken pdTRUE) { portYIELD_FROM_ISR(); }原则一句话ISR内每释放一次信号量退出时都要有一次让出机会任务恢复快一拍FIFO灌爆的窗口就小一圈。 防坑清单回归测试必须包含读长度正好跨越FIFO边界32字节上下各若干的长读连读上千次逐字节核对数据。异常恢复路径只认一种完整复位寄存器级复位或FSM复位之后清中断、清FIFO、清总线一步不少。审计ISR数一遍所有return确认每笔FromISR释放之后都能走到让出检查。搜驱动里所有无符号减法凡是算FIFO余量、分片长度的确认被减数不会小于减数。连续多段读的场景确认预约字节数在每笔传输结束和出错时都会被归零。总线卡死时clear bus failed检查从机是否停在中途读上必要时按清总线逻辑拉9个SCL时钟把从机同步出来。多任务共用一条I2C总线时确认所有传输都在总线锁bus_lock_mux保护内别让裸i2c_master_trans绕过互斥。调试期打开 I2C debug 日志并考虑把ISR放进内部RAMCONFIG_I2C_MASTER_ISR_HANDLER_IN_IRAM避免Flash竞争放大时序问题。✅ 怎么确认修好了不追求具体数字看三件定性的事长读从偶发超时变成连续稳定通过异常恢复后的下一笔传输数据逐字节正确不再错位多任务并发压测不再出现HardFaultclear bus failed日志消失。三条都满足说明FIFO、复位、让出三个环节都补上了。排查这类问题最后记住三行FIFO余量的减法会不会下溢错误路径复位后FIFO和中断位清没清ISR里每笔释放后能不能走到让出。三个问题各回答一遍比通读整个驱动快得多。【免费下载链接】esp-idfEspressif IoT Development Framework. Official development framework for Espressif SoCs.项目地址: https://gitcode.com/GitHub_Trending/es/esp-idf创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表