打印

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

[复制链接]
843|1
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
大家在使用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)将SDASCL配置为推挽输出,强制输出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配置为推挽输出,强制输出9clk
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方案商聚沃科技原创发布,了解更多GD32 MCU教程,关注聚沃科技官网,GD32MCU技术交流群:859440462

使用特权

评论回复
沙发
chenqianqian| | 2024-8-7 08:37 | 只看该作者
IIC很少卡死吧,如果卡死多半也是程序有问题,导致溢出进入hardfault了。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

170

主题

190

帖子

9

粉丝