STM32之I2C_FLAG_BUSY置位解决办法

stm32f429-disco上的触摸屏IC是STMPE811,使用I2C通信,该ic的使用还不算难,里面包括8通道12bit AD,8个GPIO口,加128set FIFO以及几个寄存器。

问题是出在STM32 的I2C IP核上,网上大家也吐槽了不少,最主要的就是各种当机,当当当当,哈哈。

我第一次出现当机的地方是在

if(__HAL_I2C_GET_FLAG(hi2c, I2C_FLAG_BUSY) == SET)
    {
      return HAL_BUSY;
    }

返回的BUSY值是一直置H的,这就奇怪了,用的是官方的STM32CubeMX生成的初始化代码,刚初始完就BUSY,太坑了吧。


查看datasheet,有两点可以参考


这渣渣IP核的BUSY位是判断sda,scl两线的stop位,没检测到就置H;而不是判断I2C的状态及超时,置位BUSY,坑!

解决的办法是有的:


可以通过CR1位的rst位,手动将其清零。

网上不少童鞋也是通过该位清BUSY

hi2c->Instance->CR1 |= 0x8000;
hi2c->Instance->CR1 &= ~0x8000;

要注意的是,此复位操作要放置于,所有I2C寄存器配置之前!!!以上是 方法一



方法二:

查看了例程,例程的I2C是这样初始化的

I2Cx_MspInit(&I2cHandle);
HAL_I2C_Init(&I2cHandle);

其中
static void I2Cx_MspInit(I2C_HandleTypeDef *hi2c)
{
  GPIO_InitTypeDef  GPIO_InitStruct;  

  if (hi2c->Instance == DISCOVERY_I2Cx)
  {
    /* Configure the GPIOs ---------------------------------------------------*/ 
    /* Enable GPIO clock */
    DISCOVERY_I2Cx_SDA_GPIO_CLK_ENABLE();
    DISCOVERY_I2Cx_SCL_GPIO_CLK_ENABLE();
      
    /* Configure I2C Tx as alternate function  */
    GPIO_InitStruct.Pin       = DISCOVERY_I2Cx_SCL_PIN;
    GPIO_InitStruct.Mode      = GPIO_MODE_AF_OD;
    GPIO_InitStruct.Pull      = GPIO_NOPULL;
    GPIO_InitStruct.Speed     = GPIO_SPEED_FAST;
    GPIO_InitStruct.Alternate = DISCOVERY_I2Cx_SCL_SDA_AF;
    HAL_GPIO_Init(DISCOVERY_I2Cx_SCL_GPIO_PORT, &GPIO_InitStruct);
      
    /* Configure I2C Rx as alternate function  */
    GPIO_InitStruct.Pin = DISCOVERY_I2Cx_SDA_PIN;
    HAL_GPIO_Init(DISCOVERY_I2Cx_SDA_GPIO_PORT, &GPIO_InitStruct);
    
    
    /* Configure the Discovery I2Cx peripheral -------------------------------*/ 
    /* Enable I2C3 clock */
    DISCOVERY_I2Cx_CLOCK_ENABLE();
    
    /* Force the I2C Periheral Clock Reset */  
    DISCOVERY_I2Cx_FORCE_RESET();<span style="white-space:pre">		</span>//########## look here #############
      
    /* Release the I2C Periheral Clock Reset */  
    DISCOVERY_I2Cx_RELEASE_RESET(); <span style="white-space:pre">		</span>//########## look here #############
   }
}
STM自己也受不鸟这I2C,手动复位了,那么解决办法就变得很明显了, 在我们自己的i2c初始化函数中加入

void HAL_I2C_MspInit(I2C_HandleTypeDef* hi2c)
{
    GPIO_InitTypeDef GPIO_InitStruct;
    if(hi2c->Instance==I2C3)
    {
        __I2C3_CLK_ENABLE();
        
        /**I2C3 GPIO Configuration    
        PC9     ------> I2C3_SDA
        PA8     ------> I2C3_SCL 
        */
        GPIO_InitStruct.Pin = GPIO_PIN_9;
        GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;
        GPIO_InitStruct.Pull = GPIO_NOPULL;
        GPIO_InitStruct.Speed = GPIO_SPEED_FAST;
        GPIO_InitStruct.Alternate = GPIO_AF4_I2C3;
        HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
        
        GPIO_InitStruct.Pin = GPIO_PIN_8;
        GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;
        GPIO_InitStruct.Pull = GPIO_NOPULL;
        GPIO_InitStruct.Speed = GPIO_SPEED_FAST;
        GPIO_InitStruct.Alternate = GPIO_AF4_I2C3;
        HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
        
        __I2C3_FORCE_RESET();<span style="white-space:pre">			</span>//######## look here #########
        __I2C3_RELEASE_RESET();<span style="white-space:pre">			</span>//######## look here #########
        
        
        HAL_NVIC_SetPriority(I2C3_EV_IRQn, 13, 0);
        HAL_NVIC_EnableIRQ(I2C3_EV_IRQn);
        HAL_NVIC_SetPriority(I2C3_ER_IRQn, 13, 0);
        HAL_NVIC_EnableIRQ(I2C3_ER_IRQn);
    }
}

说实话,i2c3_force_reset()是STM32的RCC(时钟与复位)模块的功能函数,并不是I2C模块的函数,即不是操作I2C的CR1位,而是通过总复位模块对I2C来了一次复位操作


总结一下i2c的初始化方式:

1.配置GPIO口 HAL_I2C_MspInit();

2.复位一下(CR1位复位)或(RCC对i2c模块复位)

3.配置i2c寄存器 MX_I2C3_Init();



至此,初始化后的I2C就不会将BUSY位置位了。

后期,假如I2C通信出错,该IP核有可能也会将BUSY置位,且不自动复位,此步骤也可作为除错的参考。




  • 6
    点赞
  • 42
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值