在上节文章已经完成了对GPIO的配置, 以及对一些必要的DMA函数进行了讲解

(新手向详解)通过STM32CubeMx使用STM32F411的DMA发送数据翻转GPIO-CSDN博客

接下来将会完成对DMA中断设置的工作, 以及相关函数的理解

HAL_StatusTypeDef HAL_DMA_Start_IT

(DMA_HandleTypeDef *hdma, uint32_t SrcAddress, uint32_t DstAddress, uint32_t DataLength)

HAL_StatusTypeDef HAL_DMA_Start_IT(DMA_HandleTypeDef *hdma, uint32_t SrcAddress, uint32_t DstAddress, uint32_t DataLength)
{
  HAL_StatusTypeDef status = HAL_OK;

  /* calculate DMA base and stream number */
  DMA_Base_Registers *regs = (DMA_Base_Registers *)hdma->StreamBaseAddress;
  
  /* Check the parameters */
  assert_param(IS_DMA_BUFFER_SIZE(DataLength));
 
  /* Process locked */
  __HAL_LOCK(hdma);
  
  if(HAL_DMA_STATE_READY == hdma->State)
  {
    /* Change DMA peripheral state */
    hdma->State = HAL_DMA_STATE_BUSY;
    
    /* Initialize the error code */
    hdma->ErrorCode = HAL_DMA_ERROR_NONE;
    
    /* Configure the source, destination address and the data length */
    DMA_SetConfig(hdma, SrcAddress, DstAddress, DataLength);
    
    /* Clear all interrupt flags at correct offset within the register */
    regs->IFCR = 0x3FU << hdma->StreamIndex;
    
    /* Enable Common interrupts*/
    hdma->Instance->CR  |= DMA_IT_TC | DMA_IT_TE | DMA_IT_DME;
    
    if(hdma->XferHalfCpltCallback != NULL)
    {
      hdma->Instance->CR  |= DMA_IT_HT;
    }
    
    /* Enable the Peripheral */
    __HAL_DMA_ENABLE(hdma);
  }
  else
  {
    /* Process unlocked */
    __HAL_UNLOCK(hdma);          
    
    /* Return error status */
    status = HAL_BUSY;
  }
  
  return status;
}

此函数可以以中断的方式开启DMA传输

如果传输完成 , 中断就会触发 , 属于事件中断的一种

但是现在有一个问题

我们并没有给DMA配置中断

CubeMx配置

生成代码

void DMA2_Stream0_IRQHandler(void)
{
  /* USER CODE BEGIN DMA2_Stream0_IRQn 0 */

  /* USER CODE END DMA2_Stream0_IRQn 0 */
  HAL_DMA_IRQHandler(&hdma_memtomem_dma2_stream0);
  /* USER CODE BEGIN DMA2_Stream0_IRQn 1 */

  /* USER CODE END DMA2_Stream0_IRQn 1 */
}

这是中断向量表直接映射过来的函数

发生DMA中断后 , CPU是先去中断向量表里面 , 找到了DMA2的

再从此处跳转

跳转到这里

回调函数:

HAL_StatusTypeDef HAL_DMA_RegisterCallback

(DMA_HandleTypeDef hdma, HAL_DMA_CallbackIDTypeDef CallbackID, void ( pCallback)(DMA_HandleTypeDef *_hdma))

HAL_StatusTypeDef HAL_DMA_RegisterCallback(DMA_HandleTypeDef *hdma, HAL_DMA_CallbackIDTypeDef CallbackID, void (* pCallback)(DMA_HandleTypeDef *_hdma))
{

  HAL_StatusTypeDef status = HAL_OK;

  /* Process locked */
  __HAL_LOCK(hdma);

  if(HAL_DMA_STATE_READY == hdma->State)
  {
    switch (CallbackID)
    {
    case  HAL_DMA_XFER_CPLT_CB_ID:
      hdma->XferCpltCallback = pCallback;
      break;

    case  HAL_DMA_XFER_HALFCPLT_CB_ID:
      hdma->XferHalfCpltCallback = pCallback;
      break;

    case  HAL_DMA_XFER_M1CPLT_CB_ID:
      hdma->XferM1CpltCallback = pCallback;
      break;

    case  HAL_DMA_XFER_M1HALFCPLT_CB_ID:
      hdma->XferM1HalfCpltCallback = pCallback;
      break;

    case  HAL_DMA_XFER_ERROR_CB_ID:
      hdma->XferErrorCallback = pCallback;
      break;

    case  HAL_DMA_XFER_ABORT_CB_ID:
      hdma->XferAbortCallback = pCallback;
      break;

    default:
      /* Return error status */
      status =  HAL_ERROR;
      break;
    }
  }
  else
  {
    /* Return error status */
    status =  HAL_ERROR;
  }

  /* Release Lock */
  __HAL_UNLOCK(hdma);
  
  return status;
}

感兴趣可以慢慢研究 , 粗略来讲

此函数的作用就是 , 先找到DMA的句柄 , 然后再通过你给的DMA句柄注册一个回调

这个回调的类型

typedef enum
{
  HAL_DMA_XFER_CPLT_CB_ID         = 0x00U,  /*!< Full transfer     */
  HAL_DMA_XFER_HALFCPLT_CB_ID     = 0x01U,  /*!< Half Transfer     */
  HAL_DMA_XFER_M1CPLT_CB_ID       = 0x02U,  /*!< M1 Full Transfer  */
  HAL_DMA_XFER_M1HALFCPLT_CB_ID   = 0x03U,  /*!< M1 Half Transfer  */
  HAL_DMA_XFER_ERROR_CB_ID        = 0x04U,  /*!< Error             */
  HAL_DMA_XFER_ABORT_CB_ID        = 0x05U,  /*!< Abort             */
  HAL_DMA_XFER_ALL_CB_ID          = 0x06U   /*!< All               */
}HAL_DMA_CallbackIDTypeDef;

如果你想要一个传输完成后的回调

那么就把这个参数传到 CallbackID的形参位置

 HAL_DMA_XFER_CPLT_CB_ID

最后一个形参是你要传入的回调函数 , 就是前面设置的, 当DMA传输完成后 , 你要进入哪个回调函数

代码实战:

 void my_dma_TC_callback(DMA_HandleTypeDef *_hdma)
 {
        
 }

/* USER CODE BEGIN 2 */
       HAL_DMA_RegisterCallback(&hdma_memtomem_dma2_stream0,
                                    HAL_DMA_XFER_CPLT_CB_ID,
                                        my_dma_TC_callback);                                                                                          my_dma_TC_callback);
  /* USER CODE END 2 */
 while (1)
  {
                HAL_DMA_Start_IT(&hdma_memtomem_dma2_stream0, (uint32_t)data_gpio[counter % 2] , (uint32_t)&GPIOA->ODR , 1);
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }

那么想要在中断里持续反转

就只用定义一个变量

然后对2取余

uint32_t counter = 0 ;

 void my_dma_TC_callback(DMA_HandleTypeDef *_hdma)
 {
                counter ++ ;
                HAL_DMA_Start_IT(&hdma_memtomem_dma2_stream0, (uint32_t)data_gpio[counter % 2] , (uint32_t)&GPIOA->ODR , 1);

 }

这样偶数时 取余是高电平 , 奇数时取余是低电平 , 然后循环调用, 就实现了高低电平转换

那么这样反转有什么用呢?

那么不妨假设数组变大

这样GPIO就可以按照你想要的任何时序去变化 , 这只是一个基础 , 输出的波形变化可以利用在各种领域各种地方

比如说ADC上电的时候要去写寄存器 , 就可以交给DMA去同步并发

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐