I2C 异步通讯导致 HardFault 问题
-
1. 问题背景
- 软件:ME0 SDK1_3_1 I2c_Master_Demo
- 硬件:客户板子 or ME05 EVB
- 问题描述:
- 客户调用 I2C_DRV_MasterReceiveData 函数发送会触发 HardFault。
- 客户代码逻辑是当识别到芯片有报 fault 持续 1-3s 然后重新走上下电时序(处 MCU 外,其他外设芯片执行上下电逻辑),I2C 4.7K 上拉电阻的电源也会重新上下电,此时 I2C 总线 SDK 和 SDA 也会同步拉低再拉高,拉低持续时间有 800ms 左右。
- I2C 外设芯片上下电后,MCU 会调用 I2C_DRV_MasterReceiveData 函数,此时若使用 I2C_DRV_MasterGetTransferStatus + while,需要获取返回值为 STATUS_SUCCESS 才能跳出循环,但此时由于 SDK 和 SDA 拉低时间超过寄存器 TOCFG -> LOW 设置的最大时间,寄存器 MSTS -> TOIF 标志位会置 1,I2C_DRV_MasterIRQHandler 函数里会将 master->status = STATUS_I2C_LINE_LOW_TIMEOUT,此时会一直卡在 while 函数里出不来。
- 如果把调用 I2C_DRV_MasterReceiveData 函数后,I2C_DRV_MasterGetTransferStatus + while 等待接收完成去掉,则会触发 HardFault。但当 RXBUFF 数组由局部变量改为静态局部变量或全局变量后,不会触发 HardFault。
2. 问题分析
2.1.1 Line low timeout detected 超时分析
- 先确定 TOCFG -> LOW 设置的最大时间是多久,是否真的是拉低时间超时后,导致的 MSTS -> TOIF 标志位置 1。
- 在 I2C 初始化的时候,I2C_Set_MasterLineLowTimeoutPeriod 函数会将 LOW 的值设为 0xFFF。

- 时钟为 8M,LOW 值为 0xFFF,根据公式计算,(4096 * 256)* (1 / 8M) = 131ms,而实际拉低时间是 800ms,确实触发超时,MSTS -> TOIF 标志位置 1,理论与实践现象一致。

2.2 HardFault 原因分析
- 使用 I2c_Master_Demo 例程,模仿客户代码调用方式对问题进行复现

- 实际 Debug 确定可以看到,当出现 HardFault 时,I2C 未接收完成


- 当更改 RXBUFF 为静态局部变量后,就不会出现 HardFault ,其 I2C 通讯波形是完整的,有起始位和停止位。


- 出现 HardFault 后观察栈空间以及修改栈空间大小均无效果,可以推断堆栈未出现溢出。RXBUFF 数组仅修改变量存储位置,即可避免 HardFault 出现,推测跟变量的存储位置及生命周期有关系,由于局部变量在函数执行结束后就释放了,可能是 i2c_Read 函数已经执行结束了,将 RXBUFF 数组释放了,但由于 I2C 接收还未完成,但接收 BUFF 已经被释放了,但此时还在给 RXBUFF 收数据,所以触发了 HardFault 。
- 在 i2c_Read 函数里和函数执行完成后分别设一个标志位,当出现 HardFault 的情况时 asd[1] 已经置 1 了,此时 i2c_Read 函数执行完毕 RXBUFF 数组已经被释放了,但此时 I2C 通讯还未结束,还在给 FIFO 写数据,所以触发了 HardFault 。

3. 问题总结
- 使用 I2C_DRV_MasterReceiveData 函数时,必须使用 I2C_DRV_MasterGetTransferStatus 获取 I2C 状态,同时记得 I2C_DRV_MasterGetTransferStatus + while 等待的时候记得加上超时机制。

- 也可以将 RXBUFF 设置为全局变量或静态变量。
快速上手云途开发生态
发帖前请查看
帮助改进和优化YT CONFIG TOOL,有机会抽取YTM32B1ME0 EVB哦...