如何配置STM32 HAL DMA中断

STM32 HAL DMA中断配置

  • 前言
  • MXCUBE配置
  • DMA初始化
  • I2C初始化
  • I2C+DMA+中断通信过程
  • 总结
  • 前言

    使用HAL库方式DMA中断时,在网上找了好多资料都没有怎么介绍。所以就自己研究了一下,并做个记录。我的芯片型号是STM32G030。下面我以I2C传数据为例介绍下HAL库是如何使用DMA中断的。

    MXCUBE配置

    我使用的是I2C2,简单配置下参数,加上DMA通道。


    DMA貌似默认开启了中断,蓝色的勾勾是我自己勾上的,没有用到I2C的中断不勾也可以。

    DMA初始化

    打开工程后主函数里有个MX_DMA_Init();它的内部就是开启DMA的RCC和NVIC,这里就不放图了。

    I2C初始化

    打开这个文件的第一眼,看到最上端的全局变量hdma_i2c2_rx和hdma_i2c2_tx,实际上DMA的初始化我更感觉是放在这里的。

    I2C_HandleTypeDef hi2c2;
    DMA_HandleTypeDef hdma_i2c2_rx;
    DMA_HandleTypeDef hdma_i2c2_tx;
    

    I2C的初始化我直接略过,和只用I2C通讯配置没区别。拉到最下边的函数HAL_I2C_MspInit

    	/* I2C2 DMA Init */
        /* I2C2_RX Init */
        hdma_i2c2_rx.Instance = DMA1_Channel1;
        hdma_i2c2_rx.Init.Request = DMA_REQUEST_I2C2_RX;
        hdma_i2c2_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
        hdma_i2c2_rx.Init.PeriphInc = DMA_PINC_DISABLE;
        hdma_i2c2_rx.Init.MemInc = DMA_MINC_ENABLE;
        hdma_i2c2_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
        hdma_i2c2_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
        hdma_i2c2_rx.Init.Mode = DMA_NORMAL;
        hdma_i2c2_rx.Init.Priority = DMA_PRIORITY_LOW;
        if (HAL_DMA_Init(&hdma_i2c2_rx) != HAL_OK)
        {
          Error_Handler();
        }
    
        __HAL_LINKDMA(i2cHandle,hdmarx,hdma_i2c2_rx);
            /* I2C2_TX Init */
        hdma_i2c2_tx.Instance = DMA1_Channel2;
        hdma_i2c2_tx.Init.Request = DMA_REQUEST_I2C2_TX;
        hdma_i2c2_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
        hdma_i2c2_tx.Init.PeriphInc = DMA_PINC_DISABLE;
        hdma_i2c2_tx.Init.MemInc = DMA_MINC_ENABLE;
        hdma_i2c2_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
        hdma_i2c2_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
        hdma_i2c2_tx.Init.Mode = DMA_NORMAL;
        hdma_i2c2_tx.Init.Priority = DMA_PRIORITY_LOW;
        if (HAL_DMA_Init(&hdma_i2c2_tx) != HAL_OK)
        {
          Error_Handler();
        }
    
        __HAL_LINKDMA(i2cHandle,hdmatx,hdma_i2c2_tx);
    

    这里实际上跟I2C+DMA通信也没什么区别。

    I2C+DMA+中断通信过程

    在我看了他的驱动函数后,我发现只要是后缀带有DMA的函数,都自动有DMA中断的处理过程。有这几个函数:

  • HAL_I2C_Mem_Write_DMA
  • HAL_I2C_Master_Receive_DMA
  • HAL_I2C_Master_Seq_Receive_DMA
  • HAL_I2C_Master_Seq_Transmit_DMA
  • HAL_I2C_Master_Transmit_DMA
  • HAL_I2C_Mem_Read_DMA
  • HAL_I2C_Mem_Write_DMA
  • HAL_I2C_Slave_Receive_DMA
  • 我随便点开一个HAL_I2C_Mem_Write_DMA,这几个都是差不多,只是用途不一样,带Mem的方便与EEPROM类似的通讯,他需要发从机的设备地址和内存地址,就可以用这个。
    在该函数内我只列出我觉得重要的代码,

    1. hi2c->XferISR用于绑定中断处理函数

      hi2c->XferISR     = I2C_Master_ISR_DMA;
      
    2. 这里dma绑定了相关的传输处理函数,传输完成和传输错误,然后开启DMA的中断使能,当传输完成后就会进入I2C_DMAMasterTransmitCplt。

      if (hi2c->hdmatx != NULL)
      {
        /* Set the I2C DMA transfer complete callback */
        hi2c->hdmatx->XferCpltCallback = I2C_DMAMasterTransmitCplt;
      
        /* Set the DMA error callback */
        hi2c->hdmatx->XferErrorCallback = I2C_DMAError;
      
        /* Set the unused DMA callbacks to NULL */
        hi2c->hdmatx->XferHalfCpltCallback = NULL;
        hi2c->hdmatx->XferAbortCallback = NULL;
      
        /* Enable the DMA channel */
        dmaxferstatus = HAL_DMA_Start_IT(hi2c->hdmatx, (uint32_t)pData, (uint32_t)&hi2c->Instance->TXDR, hi2c->XferSize);
      }
      
    3. 该函数最后开启DMA搬运。

      /* Enable DMA Request */
        hi2c->Instance->CR1 |= I2C_CR1_TXDMAEN;
      

    最后是通讯过程的DMA中断了,当触发了DMA的中断类型就会进入该函数,就只有三种传输完成一半、传输完成以及传输错误。

    void DMA1_Channel1_IRQHandler(void)
    {
      /* USER CODE BEGIN DMA1_Channel1_IRQn 0 */
    
      /* USER CODE END DMA1_Channel1_IRQn 0 */
      HAL_DMA_IRQHandler(&hdma_i2c2_rx);
      /* USER CODE BEGIN DMA1_Channel1_IRQn 1 */
    
      /* USER CODE END DMA1_Channel1_IRQn 1 */
    }
    

    进入到HAL_DMA_IRQHandler,假设触发了传输完成,就会执行相应关闭DMA中断、清空DMA中断标志位最后调用传输完成的回调函数。

    	...
    	if ((hdma->Instance->CCR & DMA_CCR_CIRC) == 0U)
          {
            /* Disable the transfer complete and error interrupt */
            __HAL_DMA_DISABLE_IT(hdma, DMA_IT_TE | DMA_IT_TC);
    
            /* Change the DMA state */
            hdma->State = HAL_DMA_STATE_READY;
          }
          /* Clear the transfer complete flag */
          __HAL_DMA_CLEAR_FLAG(hdma, (DMA_FLAG_TC1 << (hdma->ChannelIndex & 0x1CU)));
    
          /* Process Unlocked */
          __HAL_UNLOCK(hdma);
    
          if (hdma->XferCpltCallback != NULL)
          {
            /* Transfer complete callback */
            hdma->XferCpltCallback(hdma);
          }
        }
        ...
    

    这个hdma->XferCpltCallback()函数指针在我们在开始时就赋值了

     /* Set the I2C DMA transfer complete callback */
      hi2c->hdmatx->XferCpltCallback = I2C_DMAMasterTransmitCplt;
    

    进入到该函数发现关闭了I2C的DMA请求

    /* Disable DMA Request */
      hi2c->Instance->CR1 &= ~I2C_CR1_TXDMAEN;
    

    但是函数内我并没有找到用户可以自定义的回调函数,可供我们自己使用。我自己在里边加入了些代码,实测发现是可以进去的。

    总结

    最后总结HAL库方式的DMA中断默认就是开启状态,只是没有给用户开启自定义回调函数使用,也不知道是没必要还是我没找到。

    作者:小小豆芽菜丶

    物联沃分享整理
    物联沃-IOTWORD物联网 » 如何配置STM32 HAL DMA中断

    发表回复