STM32F 单片机硬件I2C Busy标志导致的I2C卡死的处理办法
原文链接:https://blog.csdn.net/PP_hui/article/details/112229696
在调试多用户表的时候,发现如果人为短接I2C的SDA或SLK脚后,I2C的SR2的Busy标志将会置1,并且试了很多种办法也无法清除该标志位,只能复位芯片后I2C才能恢复正常。
导致这个问题的原因是STM32芯片的硬件I2C接口是支持多个主设备同时使用的,STM32的I2C接口会一直检查SDA和SLK的状态,当出现非自己发出的电平变化等情况后,STM32芯片则判定为是有其它I2C的主在操作总线,这样STM32的Busy(总线忙标志)则会置位,只有在检查到一次I2C协议的停止位后才会硬件清除该标志,如下说明:
如果Busy标志为1,则STM32的硬件I2C将不会发送数据,也就导致了I2C异常。
芯片手册上有说明CR1的SWRST(软件复位I2C)可以清除Busy标志,如下图:
但是经过测试发现并不能解决这个问题,虽然能够复位I2C的所有寄存器,但是在读一次任何寄存器后所有寄存器的值又会恢复原来的样子,Busy标志也没有清除:
设置SWRST后:,任意操作后:
在网上搜索了很久最终还是解决了这个问题:
链接《https://blog.csdn.net/jatamatadada/article/details/40860619》
经过代码修改后,处理逻辑为:
a.在操作I2C前,检测SR2的Busy是否为1,如下:
//检查是否有Busy标志及配置是否正确
if(READ_BIT(hi2c1.Instance->SR2,I2C_SR2_BUSY) || READ_BIT(hi2c1.Instance->CR1,I2C_CR1_PE) == 0){
I2C_Busy_C();
return 0;
}
b.I2C_Busy_C()函数内容:
//===================================================
//清除总线忙标志
//Xcp:2021/1/5
void I2C_Busy_C(void)
{
SET_BIT(hi2c1.Instance->CR1,I2C_CR1_SWRST);
CLEAR_BIT(hi2c1.Instance->CR1,I2C_CR1_SWRST);
I2Cx_MspInit(&hi2c1); //自定义函数
HAL_I2C_Init(&hi2c1);
}
c. I2Cx_MspInit()函数内容:
//---------------------------------------------------------
//复位I2c所有参数
static void I2Cx_MspInit(I2C_HandleTypeDef *hi2c)
{
GPIO_InitTypeDef GPIO_InitStruct;
if (hi2c->Instance == I2C1)
{
/* Configure the GPIOs ---------------------------------------------------*/
/* Enable GPIO clock */
__I2C1_CLK_ENABLE();
/* Configure I2C Tx as alternate function */
GPIO_InitStruct.Pin = GPIO_PIN_6;
GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FAST;
GPIO_InitStruct.Alternate = GPIO_AF4_I2C1;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
/* Configure I2C Rx as alternate function */
GPIO_InitStruct.Pin = GPIO_PIN_7;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
/* Configure the Discovery I2Cx peripheral -------------------------------*/
/* Enable I2C1 clock */
__I2C1_FORCE_RESET(); //通过总复位模块对I2C一次复位操作
__I2C1_RELEASE_RESET();
}
}
以上代码中,和库函数HAL_I2C_MspDeInit()不同的是增加了
__I2C1_FORCE_RESET(); //通过总复位模块对I2C一次复位操作
__I2C1_RELEASE_RESET();
这两句代码,它是通过系统的总复位对I2C进线一次复位操作,这样就能够清掉Busy的标志。
————————————————
版权声明:本文为CSDN博主「PP_hui」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/PP_hui/article/details/112229696