STM32F 单片机硬件I2C Busy标志导致的I2C卡死的处理办法

STM32F 单片机硬件I2C Busy标志导致的I2C卡死的处理办法

 

  1. 在调试多用户表的时候,发现如果人为短接I2C的SDA或SLK脚后,I2C的SR2的Busy标志将会置1,并且试了很多种办法也无法清除该标志位,只能复位芯片后I2C才能恢复正常。
  2. 导致这个问题的原因是STM32芯片的硬件I2C接口是支持多个主设备同时使用的,STM32的I2C接口会一直检查SDA和SLK的状态,当出现非自己发出的电平变化等情况后,STM32芯片则判定为是有其它I2C的主在操作总线,这样STM32的Busy(总线忙标志)则会置位,只有在检查到一次I2C协议的停止位后才会硬件清除该标志,如下说明:

       如果Busy标志为1,则STM32的硬件I2C将不会发送数据,也就导致了I2C异常。

  1. 芯片手册上有说明CR1的SWRST(软件复位I2C)可以清除Busy标志,如下图:

但是经过测试发现并不能解决这个问题,虽然能够复位I2C的所有寄存器,但是在读一次任何寄存器后所有寄存器的值又会恢复原来的样子,Busy标志也没有清除:

设置SWRST后:,任意操作后:

  1. 在网上搜索了很久最终还是解决了这个问题:

链接《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的标志。

 

 

 

编辑:徐朝攀              日期:2021年1月5日星期二

  • 19
    点赞
  • 44
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是基于HAL库的STM32F407硬件I2C1控制PCA9685的代码示例: ```c #include "stm32f4xx_hal.h" #define PCA9685_ADDRESS 0x40 I2C_HandleTypeDef hi2c1; void PCA9685_Write(I2C_HandleTypeDef *hi2c, uint8_t reg, uint8_t *data, uint16_t size) { while (HAL_I2C_Master_Transmit(hi2c, PCA9685_ADDRESS << 1, &reg, 1, HAL_MAX_DELAY) != HAL_OK); while (HAL_I2C_Master_Transmit(hi2c, PCA9685_ADDRESS << 1, data, size, HAL_MAX_DELAY) != HAL_OK); } void PCA9685_SetPWM(I2C_HandleTypeDef *hi2c, uint8_t channel, uint16_t on_time, uint16_t off_time) { uint8_t data[4]; data[0] = on_time & 0xFF; data[1] = on_time >> 8; data[2] = off_time & 0xFF; data[3] = off_time >> 8; PCA9685_Write(hi2c, 0x06 + 4 * channel, data, sizeof(data)); } void PCA9685_SetFrequency(I2C_HandleTypeDef *hi2c, uint16_t frequency) { uint8_t prescale = (uint8_t)((25000000.0f / (4096 * frequency)) - 1); PCA9685_Write(hi2c, 0xFE, &prescale, 1); } int main(void) { HAL_Init(); __HAL_RCC_I2C1_CLK_ENABLE(); GPIO_InitTypeDef GPIO_InitStruct = {0}; GPIO_InitStruct.Pin = GPIO_PIN_6 | GPIO_PIN_7; GPIO_InitStruct.Mode = GPIO_MODE_AF_OD; GPIO_InitStruct.Pull = GPIO_PULLUP; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; GPIO_InitStruct.Alternate = GPIO_AF4_I2C1; HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); hi2c1.Instance = I2C1; hi2c1.Init.ClockSpeed = 100000; hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2; hi2c1.Init.OwnAddress1 = 0; hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT; hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE; hi2c1.Init.OwnAddress2 = 0; hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE; hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE; HAL_I2C_Init(&hi2c1); PCA9685_SetFrequency(&hi2c1, 50); PCA9685_SetPWM(&hi2c1, 0, 0, 4095); // Set channel 0 to full brightness PCA9685_SetPWM(&hi2c1, 1, 0, 2048); // Set channel 1 to half brightness while (1) { // Main loop } } ``` 这个例子中,我们使用了I2C1总线,PB6和PB7引脚作为I2C数据和时钟线。PCA9685的I2C地址是0x40。我们设置了频率为50Hz,并设置了通道0和通道1的PWM输出。可以根据实际需求修改频率和PWM输出的值。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值