STM32CbueMX之I2C HAL_ERROR 和 DMA 不启动

有些话、适合烂在心里 提交于 2019-12-31 13:44:45

如果是使用STM32CbueMX生成 I2C 的代码,使用 I2C 读写 过程会出现HAL_ERROR。

这个时候一般有两种选择,

          方式一:是选择IO模拟I2C;

          方式二:是排查代码问题解决硬件I2C。

 

使用DMA的时候有时候DMA启动失败,往往这是时候我们就方式使用DMA了,转而使用更低效率的方式发接数据。

 

方式一:问度娘。

方式二:

像这种情况,很多人说ST的I2C有bug,可能由于专利问题。但是我觉得吧,那么多人在用ST的MCU,怎么可能会。

原因就是初始化流程的问题。

首先要想到的是看ST的官方源码是怎么使用硬件I2C的,和STM32CbueMX生成的代码比对一下。

 

下面是STM32CbueMX生成的I2C初始化代码

int main(void)
{
  HAL_Init();
  SystemClock_Config();
  MX_GPIO_Init();
  MX_I2C1_Init();
  MX_DMA_Init();

  while (1)
  {
  }
}

static void MX_I2C1_Init(void)
{
  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;
  if (HAL_I2C_Init(&hi2c1) != HAL_OK)
  {
    Error_Handler();
  }
  if (HAL_I2CEx_ConfigAnalogFilter(&hi2c1, I2C_ANALOGFILTER_ENABLE) != HAL_OK)
  {
    Error_Handler();
  }
  if (HAL_I2CEx_ConfigDigitalFilter(&hi2c1, 0) != HAL_OK)
  {
    Error_Handler();
  }
}

static void MX_DMA_Init(void) 
{
  __HAL_RCC_DMA1_CLK_ENABLE();
  HAL_NVIC_SetPriority(DMA1_Stream0_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(DMA1_Stream0_IRQn);
  HAL_NVIC_SetPriority(DMA1_Stream6_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(DMA1_Stream6_IRQn);
}

void HAL_I2C_MspInit(I2C_HandleTypeDef* hi2c)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};
  if(hi2c->Instance==I2C1)
  {
    __HAL_RCC_GPIOB_CLK_ENABLE();
    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);

    __HAL_RCC_I2C1_CLK_ENABLE();
  
    hdma_i2c1_rx.Instance = DMA1_Stream0;
    hdma_i2c1_rx.Init.Channel = DMA_CHANNEL_1;
    hdma_i2c1_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
    hdma_i2c1_rx.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_i2c1_rx.Init.MemInc = DMA_MINC_ENABLE;
    hdma_i2c1_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
    hdma_i2c1_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
    hdma_i2c1_rx.Init.Mode = DMA_NORMAL;
    hdma_i2c1_rx.Init.Priority = DMA_PRIORITY_LOW;
    hdma_i2c1_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
    if (HAL_DMA_Init(&hdma_i2c1_rx) != HAL_OK)
    {
      Error_Handler();
    }

    __HAL_LINKDMA(hi2c,hdmarx,hdma_i2c1_rx);

    hdma_i2c1_tx.Instance = DMA1_Stream6;
    hdma_i2c1_tx.Init.Channel = DMA_CHANNEL_1;
    hdma_i2c1_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
    hdma_i2c1_tx.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_i2c1_tx.Init.MemInc = DMA_MINC_ENABLE;
    hdma_i2c1_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
    hdma_i2c1_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
    hdma_i2c1_tx.Init.Mode = DMA_NORMAL;
    hdma_i2c1_tx.Init.Priority = DMA_PRIORITY_LOW;
    hdma_i2c1_tx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
    if (HAL_DMA_Init(&hdma_i2c1_tx) != HAL_OK)
    {
      Error_Handler();
    }

    __HAL_LINKDMA(hi2c,hdmatx,hdma_i2c1_tx);
  }
}

MX_I2C1_Init抽象层初始化函数执行的时候会调用HAL_I2C_MspInit硬件层初始化函数。

上面代码初始化流程:

1、初始化填充I2C结构体

2、初始化I2C引脚,配置成内部开漏。

3、使能I2C时钟。

4、初始化I2C的DMA,关联DMA。

5、使能DMA时钟,初始化及使能中断。

 

 

 

看一下HAL库的I2C驱动stm32f4xx_hal_i2c.c,如何教我们使用I2C的。

看到图上的初始化流程:

#)声明一个I2C_HandleTypeDef句柄结构,例如:
        I2C_HandleTypeDef hi2c;

    (#)通过实现@ref HAL_I2C_MspInit()API来初始化I2C低级资源:
        (##)启用I2Cx接口时钟
        (##)I2C引脚配置
            (+++)启用I2C GPIO的时钟
            (+++)将I2C引脚配置为备用功能漏极开路
        (##)NVIC配置,如果您需要使用中断过程
            (+++)配置I2Cx中断优先级
            (+++)启用NVIC I2C IRQ通道
        (##)DMA配置(如果需要使用DMA进程)
            (+++)为传输或接收流声明DMA_HandleTypeDef句柄结构
            (+++)使用以下命令启用DMAx接口时钟
            (+++)配置DMA句柄参数
            (+++)配置DMA Tx或Rx流
            (+++)将初始化的DMA句柄与hi2c DMA Tx或Rx句柄相关联
            (+++)配置优先级并启用NVIC以进行传输完成中断DMA Tx或Rx流

 

 

对比发现:STM32CubeMX的I2C初始化与HAL的初始化流程不一样,我之前也遇到过HAL_ERROR的问题,最后我按HAL库的流程初始化就没问题了。做法就是用STM32CubeMX生成代码后,先I2C的时钟和DMA的时钟先打开。

  __HAL_RCC_I2C1_CLK_ENABLE();
  __HAL_RCC_DMA1_CLK_ENABLE();    

 

int main(void)
{
  HAL_Init();
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */
  __HAL_RCC_I2C1_CLK_ENABLE();
  __HAL_RCC_DMA1_CLK_ENABLE();    
  /* USER CODE END SysInit */

  MX_GPIO_Init();
  MX_I2C1_Init();
  MX_DMA_Init();

  while (1)
  {
  }
}

 

 

全篇完。

本人博客仅仅代表我个人见解方便记录成长笔记。

若有与 看官老爷见解有冲突,我坚信看官老爷见解是对的,我的是错的。

感谢~!

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!