GD32 MCU碰到IIC总线卡死怎么办?

大家在使用MCU IIC通信时,若碰到设备复位或者总线干扰等情况,可能会导致IIC总线卡死,表现上总线上SDA或者SCL其中一根线为低电平,IIC总线一直处于busy状态。此时若代码上一直等待总线空闲,则可能导致软件死机,为解决该问题,本视频提供了软件配置释放IIC总线的方法。

首先为大家介绍发生IIC总线卡死的两种情况:(1)主机在发送 START 信号后, 控制 SCL 产生 8 个时钟脉冲,然后拉低 SCL 信号为低电平,在这个时候,从设备输出应答信号,将 SDA 信号拉为低电平。如果这个时候主机异常复位, SCL 就会被释放为高电平。此时,如果从机没有复位,就会继续 I2C 的应答,将 SDA一直拉为低电平,直到 SCL 变为低电平,才会结束应答信号。 而由于 I2C 主机复位后检测总线的状态,如果 SDA 信号为低电平,则 I2C 总线被占用,会一直等待 SCL 和 SDA信号变为高电平,因此,在 I2C 主机等待从机释放 SDA 信号时, I2C 从机又在等待主机将 SCL 信号拉低以释放应答信号,两者相互等待, I2C 总线进入死锁状态 ;(2)主机在发送 START 信号后, 控制 SCL 产生 8 个时钟脉冲,然后拉低 SCL 信号为低电平,在这个时候,从设备输出应答信号,将 SDA 信号拉为低电平。如果这个时候主机异常复位, SCL 就会被释放为高电平。此时,如果从机没有复位,就会继续 I2C 的应答,将 SDA一直拉为低电平,直到 SCL 变为低电平,才会结束应答信号。 而由于 I2C 主机复位后检测总线的状态,如果 SDA 信号为低电平,则 I2C 总线被占用,会一直等待 SCL 和 SDA信号变为高电平。因此,在 I2C 主机等待从机释放 SDA 信号时, I2C 从机又在等待主机将 SCL 信号拉低以释放应答信号,两者相互等待, I2C 总线进入死锁状态 。

以下为两种复位IIC总线卡死的软件方法,大家可以尝试使用:

(1)将SDA和SCL配置为推挽输出,强制输出stop信号

在 I2C 主机复位后,主机检测 I2C 总线一直为 BUSY 状态,且超过设定的时间,则总线被锁死。可通过将 I2C 的 SCL 和 SDA 引脚初始化成普通 GPIO 功能,配置成推挽输出。 先拉高SCL 信号,在拉高 SDA 信号,模拟产生一个 STOP 信号,然后再配置为 I2C 的引脚复用功能。配置代码如下所示。

|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| C /*! \brief reset i2c bus \param[in] none \param[out] none \retval none */ void i2c_bus_reset() { GPIO_BC(GPIOB) |= GPIO_PIN_6 | GPIO_PIN_7; gpio_init(GPIOB, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_6|GPIO_PIN_7); __nop(); __nop(); __nop(); __nop(); __nop(); GPIO_BOP(GPIOB) |= GPIO_PIN_6; __nop(); __nop(); __nop(); __nop(); __nop(); GPIO_BOP(GPIOB) |= GPIO_PIN_7; gpio_init(GPIOB, GPIO_MODE_AF_OD, GPIO_OSPEED_50MHZ, GPIO_PIN_6 | GPIO_PIN_7); } /*! \brief check the I2C is or not busy \param[in] none \param[out] none \retval none */ void check_bus_status(void) { while(i2c_flag_get(I2C0,I2C_FLAG_I2CBSY)) { if(--time_out == 0){ i2c_bus_reset(); } } } |

(2)将SCL配置为推挽输出,强制输出9个clk

在 I2C 主机中增加 I2C 总线恢复程序。每次 I2C 主设备复位后,如果检测到 SDA 数据线被拉低,则控制 I2C 中的 SCL 时钟线产生 9 个时钟脉冲(针对 8 位数据的情况),这样 I2C 从设备就可以完成被挂起的操作,从死锁状态中恢复过来。

I2C 主机通过将 SCL 引脚初始化为普通 GPIO 功能,配置成推挽输出。保证连续发送 9 个时钟脉冲,为保证后续 I2C 正常通信,先将 I2C 模块复位,再置位,最后再配置为 I2C 的引脚复用功能。配置代码如下所示。

|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| C /*! \brief reset i2c bus \param[in] none \param[out] none \retval none */ void i2c_bus_reset() { uint8_t I = 0; gpio_init(GPIOB, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_6); /* SCL output clock signal */ for(I = 0; I < 10; i++){ gpio_bit_reset(GPIOB, GPIO_PIN_6); delay_1us(2); gpio_bit_set(GPIOB, GPIO_PIN_6); delay_1us(2); } /* reset I2C */ i2c_software_reset_config(I2C0, I2C_SRESET_RESET); i2c_software_reset_config(I2C0, I2C_SRESET_SET); gpio_init(GPIOB, GPIO_MODE_AF_OD, GPIO_OSPEED_50MHZ, GPIO_PIN_6 | GPIO_PIN_7); } /*! \brief check the I2C is or not busy \param[in] none \param[out] none \retval none */ void check_bus_status(void) { while(i2c_flag_get(I2C0,I2C_FLAG_I2CBSY)) { if(--time_out == 0){ i2c_bus_reset(); } } } |

如有其他问题或建议,欢迎评论区讨论。

更多GD32 MCU相关咨询:https://www.gd32bbs.com/

相关推荐
物联网嵌入式小冉学长10 小时前
10.C S编程错误分析
c语言·stm32·单片机·算法·嵌入式
蝈蝈(GuoGuo)1 天前
STM32103CBT6显示ST7789通过SPI方式显示柬埔寨文
stm32·单片机·嵌入式硬件
Ronin-Lotus1 天前
嵌入式硬件篇---常见电平标准
嵌入式硬件·fpga开发·常见的电平标准
大鼻噶喳1 天前
使用VSCode开发MCU,FreeRTOS进Hard_Fault调试
vscode·单片机
我命由我123451 天前
嵌入式 STM32 开发问题:烧录 STM32CubeMX 创建的 Keil 程序没有反应
c语言·开发语言·c++·stm32·单片机·嵌入式硬件·嵌入式
麦德泽特1 天前
超高速总线CDCTL01A 芯片在机器人领域的应用解析
人工智能·嵌入式硬件·物联网·机器人·信息与通信
vicorv251 天前
电流传感器在汽车中的应用:从BMS电池管理到电机控制的工程解析
stm32·单片机·嵌入式硬件
JINX的诅咒1 天前
FPGA多通道卷积加速器:从零构建手写识别的硬件引擎
嵌入式硬件·fpga开发·cnn·开源
阿让啊2 天前
Notepad++如何列选
c语言·嵌入式硬件·notepad++
Archer-2 天前
解决STM32H7系列串口DMA发送一次卡死
stm32·单片机·嵌入式硬件