STM32F072从零配置工程-串口DMA实现

话不多说,先贴上主要的外设初始化流程:

int main(void)
{
  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();
  /* Configure the system clock */
  SystemClock_Config();
  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_USART2_UART_Init();

  /* Infinite loop */
  while (1)
  {
    if(UART2_Length && Tx2_Complete_Flag == 0)
    {
        DMA_Tx2_Data(User_UART2_Buffer, UART2_Length);
        UART2_Length = 0;
    }

  }
}

 

分析一下初始化流程:

GPIO的初始化:由于没有使用到GPIO的初始化,因此GPIO的初始化还是以时钟使能为主;

void MX_GPIO_Init(void)
{
  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOF_CLK_ENABLE();
  __HAL_RCC_GPIOA_CLK_ENABLE();
  __HAL_RCC_GPIOB_CLK_ENABLE();
}

 

DMA初始化:使能了DMA时钟,配置了DMA通道中断的中断优先级并使能了DMA中断函数;

void MX_DMA_Init(void) 
{
  /* DMA controller clock enable */
  __HAL_RCC_DMA1_CLK_ENABLE();
  /* DMA interrupt init */
  /* DMA1_Channel4_5_6_7_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA1_Channel4_5_6_7_IRQn, 2, 0);
  HAL_NVIC_EnableIRQ(DMA1_Channel4_5_6_7_IRQn);
}

 

串口UART初始化:也是整个流程的重点配置;

       首先是对UART2串口的配置,一如往常的配置;

       接下来在HAL_UART_MspInit是对串口引脚和DMA的配置:

              使能了UART2和GPIOA的时钟;

              配置PA2和PA3为引脚复用;

              配置DMA的TX和RX,这里添加了两行代码来设定DMA源地址和目标地址;

              链接DMA句柄和UART2的DMA句柄;

              设置串口中断函数的中断优先级;

              开启UART的IDLE中断并使能串口;

              清除串口的TC标志位,防止第一次接收数据产生错误;

              分别使能DMARx/Tx的TC中断,并使能其对应的串口DMA中断使能位;

              开启DMARx中断,并关闭DMATx中断;

uint8_t UART2_Tx_Buffer[TX_BUF_LEN];
uint8_t UART2_Rx_Buffer[RX_BUF_LEN]; 

void MX_USART2_UART_Init(void)
{
  huart2.Instance = USART2;
  huart2.Init.BaudRate = 115200;
  huart2.Init.WordLength = UART_WORDLENGTH_8B;
  huart2.Init.StopBits = UART_STOPBITS_1;
  huart2.Init.Parity = UART_PARITY_NONE;
  huart2.Init.Mode = UART_MODE_TX_RX;
  huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  huart2.Init.OverSampling = UART_OVERSAMPLING_16;
  huart2.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
  huart2.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
  if (HAL_UART_Init(&huart2) != HAL_OK)
  {
    Error_Handler();
  }
}

void HAL_UART_MspInit(UART_HandleTypeDef* uartHandle)
{

  GPIO_InitTypeDef GPIO_InitStruct = {0};
  if(uartHandle->Instance==USART2)
  {
    /* USART2 clock enable */
    __HAL_RCC_USART2_CLK_ENABLE();
  
    __HAL_RCC_GPIOA_CLK_ENABLE();
    /**USART2 GPIO Configuration    
    PA2     ------> USART2_TX
    PA3     ------> USART2_RX 
    */
    GPIO_InitStruct.Pin = GPIO_PIN_2|GPIO_PIN_3;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_PULLUP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF1_USART2;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    /* USART2 DMA Init */
    /* USART2_RX Init */
    hdma_usart2_rx.Instance = DMA1_Channel5;
    hdma_usart2_rx.Instance->CPAR = (uint32_t)(&(USART2->RDR));
    hdma_usart2_rx.Instance->CMAR = (uint32_t)UART2_Rx_Buffer;
    hdma_usart2_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
    hdma_usart2_rx.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_usart2_rx.Init.MemInc = DMA_MINC_ENABLE;
    hdma_usart2_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
    hdma_usart2_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
    hdma_usart2_rx.Init.Mode = DMA_NORMAL;
    hdma_usart2_rx.Init.Priority = DMA_PRIORITY_HIGH;
    if (HAL_DMA_Init(&hdma_usart2_rx) != HAL_OK)
    {
      Error_Handler();
    }

    __HAL_LINKDMA(uartHandle,hdmarx,hdma_usart2_rx);
    
    /* USART2_TX Init */
    hdma_usart2_tx.Instance = DMA1_Channel4;
    hdma_usart2_tx.Instance->CPAR = (uint32_t)(&(USART2->TDR));
    hdma_usart2_tx.Instance->CMAR = (uint32_t)UART2_Tx_Buffer;
    hdma_usart2_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
    hdma_usart2_tx.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_usart2_tx.Init.MemInc = DMA_MINC_ENABLE;
    hdma_usart2_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
    hdma_usart2_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
    hdma_usart2_tx.Init.Mode = DMA_NORMAL;
    hdma_usart2_tx.Init.Priority = DMA_PRIORITY_VERY_HIGH;
    if (HAL_DMA_Init(&hdma_usart2_tx) != HAL_OK)
    {
      Error_Handler();
    }

    __HAL_LINKDMA(uartHandle,hdmatx,hdma_usart2_tx);

    /* USART2 interrupt Init */
    HAL_NVIC_SetPriority(USART2_IRQn, 1, 0);
    HAL_NVIC_EnableIRQ(USART2_IRQn);
  /* USER CODE BEGIN USART2_MspInit 1 */

    __HAL_UART_ENABLE_IT(uartHandle, UART_IT_IDLE);
    __HAL_UART_ENABLE(uartHandle);
    
    __HAL_UART_CLEAR_FLAG(uartHandle, UART_CLEAR_TCF);
    __HAL_UART_CLEAR_IT(uartHandle, UART_CLEAR_TCF);
    
    __HAL_DMA_ENABLE_IT(&hdma_usart2_rx, DMA_IT_TC);
    SET_BIT(uartHandle->Instance->CR3, USART_CR3_DMAR);
    __HAL_DMA_DISABLE(&hdma_usart2_tx);
    
    __HAL_DMA_ENABLE_IT(&hdma_usart2_tx, DMA_IT_TC);
    SET_BIT(uartHandle->Instance->CR3, USART_CR3_DMAT);
    __HAL_DMA_ENABLE(&hdma_usart2_rx);
    
  /* USER CODE END USART2_MspInit 1 */
  }
}

 

有一段代码很有意思:

这段代码将uartHandle.hdmarx与hdma_usart2_rx的句柄链接在一起,意味着两者可以在以后的配置和使用中是对等的,用哪个进行配置都可以;

__HAL_LINKDMA(uartHandle,hdmarx,hdma_usart2_rx);

 

重点:

由于HAL库中并没有包含对IDLE中断的回调处理函数,因此可以自己通过修改HAL库函数来实现IDLE回调函数;

首先在HAL_UART_IRQHandler中添加对IDLE回调函数的判断;

void HAL_UART_IRQHandler(UART_HandleTypeDef *huart)
{
  uint32_t isrflags   = READ_REG(huart->Instance->ISR);
  uint32_t cr1its     = READ_REG(huart->Instance->CR1);
  uint32_t cr3its;
  uint32_t errorflags;

  /* If no error occurs */
  errorflags = (isrflags & (uint32_t)(USART_ISR_PE | USART_ISR_FE | USART_ISR_ORE | USART_ISR_NE));
  if (errorflags == RESET)
  {
    /* UART in mode Receiver ---------------------------------------------------*/
    if(((isrflags & USART_ISR_RXNE) != RESET) && ((cr1its & USART_CR1_RXNEIE) != RESET))
    {
      UART_Receive_IT(huart);
      return;
    }
  }  

  /* If some errors occur */
  cr3its = READ_REG(huart->Instance->CR3);
  if(   (errorflags != RESET)
     && (   ((cr3its & USART_CR3_EIE) != RESET)
         || ((cr1its & (USART_CR1_RXNEIE | USART_CR1_PEIE)) != RESET)) )
  {
    /* UART parity error interrupt occurred -------------------------------------*/
    if(((isrflags & USART_ISR_PE) != RESET) && ((cr1its & USART_CR1_PEIE) != RESET))
    {
      __HAL_UART_CLEAR_IT(huart, UART_CLEAR_PEF);

      huart->ErrorCode |= HAL_UART_ERROR_PE;
    }

    /* UART frame error interrupt occurred --------------------------------------*/
    if(((isrflags & USART_ISR_FE) != RESET) && ((cr3its & USART_CR3_EIE) != RESET))
    {
      __HAL_UART_CLEAR_IT(huart, UART_CLEAR_FEF);

      huart->ErrorCode |= HAL_UART_ERROR_FE;
    }

    /* UART noise error interrupt occurred --------------------------------------*/
    if(((isrflags & USART_ISR_NE) != RESET) && ((cr3its & USART_CR3_EIE) != RESET))
    {
      __HAL_UART_CLEAR_IT(huart, UART_CLEAR_NEF);

      huart->ErrorCode |= HAL_UART_ERROR_NE;
    }
    
    /* UART Over-Run interrupt occurred -----------------------------------------*/
    if(((isrflags & USART_ISR_ORE) != RESET) &&
       (((cr1its & USART_CR1_RXNEIE) != RESET) || ((cr3its & USART_CR3_EIE) != RESET)))
    {
      __HAL_UART_CLEAR_IT(huart, UART_CLEAR_OREF);

      huart->ErrorCode |= HAL_UART_ERROR_ORE;
    }

    /* Call UART Error Call back function if need be --------------------------*/
    if(huart->ErrorCode != HAL_UART_ERROR_NONE)
    {
      /* UART in mode Receiver ---------------------------------------------------*/
      if(((isrflags & USART_ISR_RXNE) != RESET) && ((cr1its & USART_CR1_RXNEIE) != RESET))
      {
        UART_Receive_IT(huart);
      }

      /* If Overrun error occurs, or if any error occurs in DMA mode reception,
         consider error as blocking */
      if (((huart->ErrorCode & HAL_UART_ERROR_ORE) != RESET) ||
          (HAL_IS_BIT_SET(huart->Instance->CR3, USART_CR3_DMAR)))
      {  
        /* Blocking error : transfer is aborted
           Set the UART state ready to be able to start again the process,
           Disable Rx Interrupts, and disable Rx DMA request, if ongoing */
        UART_EndRxTransfer(huart);

        /* Disable the UART DMA Rx request if enabled */
        if (HAL_IS_BIT_SET(huart->Instance->CR3, USART_CR3_DMAR))
        {
          CLEAR_BIT(huart->Instance->CR3, USART_CR3_DMAR);

          /* Abort the UART DMA Rx channel */
          if(huart->hdmarx != NULL)
          {
            /* Set the UART DMA Abort callback : 
               will lead to call HAL_UART_ErrorCallback() at end of DMA abort procedure */
            huart->hdmarx->XferAbortCallback = UART_DMAAbortOnError;

            /* Abort DMA RX */
            if(HAL_DMA_Abort_IT(huart->hdmarx) != HAL_OK)
            {
              /* Call Directly huart->hdmarx->XferAbortCallback function in case of error */
              huart->hdmarx->XferAbortCallback(huart->hdmarx);
            }
          }
          else
          {
            /* Call user error callback */
            HAL_UART_ErrorCallback(huart);
          }
        }
        else
        {
          /* Call user error callback */
          HAL_UART_ErrorCallback(huart);
        }
      }
      else
      {
        /* Non Blocking error : transfer could go on. 
           Error is notified to user through user error callback */
        HAL_UART_ErrorCallback(huart);
        huart->ErrorCode = HAL_UART_ERROR_NONE;
      }
    }
    return;

  } /* End if some error occurs */

#if !defined(STM32F030x6) && !defined(STM32F030x8)&& !defined(STM32F070xB)&& !defined(STM32F070x6)&& !defined(STM32F030xC)
  /* UART wakeup from Stop mode interrupt occurred ---------------------------*/
  if(((isrflags & USART_ISR_WUF) != RESET) && ((cr3its & USART_CR3_WUFIE) != RESET))
  {
    __HAL_UART_CLEAR_IT(huart, UART_CLEAR_WUF);
    /* Set the UART state ready to be able to start again the process */
    huart->gState  = HAL_UART_STATE_READY;
    huart->RxState = HAL_UART_STATE_READY;
    HAL_UARTEx_WakeupCallback(huart);
    return;
  }
#endif /* !defined(STM32F030x6) && !defined(STM32F030x8)&& !defined(STM32F070xB)&& !defined(STM32F070x6)&& !defined(STM32F030xC) */

  /* UART in mode Transmitter ------------------------------------------------*/
  if(((isrflags & USART_ISR_TXE) != RESET) && ((cr1its & USART_CR1_TXEIE) != RESET))
  {
    UART_Transmit_IT(huart);
    return;
  }
    
    if(((isrflags & USART_ISR_IDLE) != RESET) && ((cr1its & USART_CR1_IDLEIE) != RESET))
    {
        HAL_UART_IdleCpltCallback(huart);      
        return;
    } 
  
  /* UART in mode Transmitter (transmission end) -----------------------------*/
  if(((isrflags & USART_ISR_TC) != RESET) && ((cr1its & USART_CR1_TCIE) != RESET))
  {
    UART_EndTransmit_IT(huart);
    return;
  }

}

 

接下来在stm32f0xx_hal_uart.c的库代码中添加IDLE的回调函数HAL_UART_IdleCpltCallback()的定义;

__weak void HAL_UART_IdleCpltCallback(UART_HandleTypeDef *huart)
{
  /* Prevent unused argument(s) compilation warning */
  UNUSED(huart);
  /* NOTE: This function Should not be modified, when the callback is needed,
           the HAL_UART_TxCpltCallback could be implemented in the user file
   */
}

 

最后别忘了在对应的stm32f0xx_hal_uart.h中添加对HAL_UART_IdleCpltCallback()的声明;

void HAL_UART_IdleCpltCallback(UART_HandleTypeDef *huart);

 

接下来看如何使用这个添加的IDLE回调函数:

       在串口中断函数USART2_IRQHandler中调用HAL_UART_IRQHandler(&huart2);以实现在IDLE中断标志位置位后调用HAL_UART_IdleCpltCallback回调函数;

       然后串口传输完成中断TC的判断直接在串口中断函数中实现,而不引用相应的TC回调函数;

void HAL_UART_IdleCpltCallback(UART_HandleTypeDef *huart)
{
    uint16_t len = 0;
    
    if((__HAL_UART_GET_FLAG(&huart2,UART_FLAG_IDLE) != RESET))
    { 
        __HAL_UART_CLEAR_IDLEFLAG(huart);   
        
        __HAL_DMA_DISABLE(huart->hdmarx);
        
        len = RX_BUF_LEN - huart2.hdmarx->Instance->CNDTR;

        memcpy(User_UART2_Buffer, UART2_Rx_Buffer, len);
        
        huart2.hdmarx->Instance->CNDTR = RX_BUF_LEN;
        
        __HAL_DMA_ENABLE(huart->hdmarx);
        
        UART2_Length = len;
    }else
    {
        UART2_Length = 0;
    }
}

/**
  * @brief This function handles USART2 global interrupt / USART2 wake-up interrupt through EXTI line 26.
  */
void USART2_IRQHandler(void)
{
  /* USER CODE BEGIN USART2_IRQn 0 */
    UART_ErrorFlag_Clear(&huart2);
  /* USER CODE END USART2_IRQn 0 */
  HAL_UART_IRQHandler(&huart2);
  /* USER CODE BEGIN USART2_IRQn 1 */
    
    if(__HAL_UART_GET_IT(&huart2, UART_IT_TC) != RESET)
    {
        __HAL_UART_CLEAR_IT(&huart2, UART_IT_TC);
        
        __HAL_UART_DISABLE_IT(&huart2, UART_IT_TC);
        
        Tx2_Complete_Flag = 0;
    }
  /* USER CODE END USART2_IRQn 1 */
}

 

这里大致讲解一下DMA传输的流程:

当F072接收到一串数据后,进入串口中断函数,清除IDLE中断标志位,关闭串口DMA接收rx,获取接收到的数据len,将接收到的数据UART2_Rx_Buffer拷贝到缓冲User_UART2_Buffer中,重新设置串口DMA接收长度,开启串口DMA接收,将接收到的数据长度赋值给UART2_Length,如果没有接收到数据,就将UART2_Length赋值为0;

接下来在main中判断,若接收到数据UART2_Length不为0,同时Tx2_Complete_Flag为0表示此时串口Tx传输空闲,就执行传输命令;

while (1)
  {
    if(UART2_Length && Tx2_Complete_Flag == 0)
    {
        DMA_Tx2_Data(User_UART2_Buffer, UART2_Length);
        UART2_Length = 0;
    }
  }

 

将接收到的数据User_UART2_Buffer和传送给UART2_Tx_Buffer,同时置位Tx2_Complete_Flag标志表示此时开始传输,同时重新设置串口DMATX的传输数据长度,然后开启串口DMA的传输;

void DMA_Tx2_Data(uint8_t *data, uint16_t size)
{
    while(Tx2_Complete_Flag);
    
    Tx2_Complete_Flag = 1;
    
    memcpy(UART2_Tx_Buffer, data, size);
    
    huart2.hdmatx->Instance->CNDTR = TX_BUF_LEN;
    
    __HAL_DMA_ENABLE(&hdma_usart2_tx);
}

 

当DMA传输完成后,触发DMA的TC中断标志进入DMA中断函数,清除DMA通道的TC中断标志位,关闭DMATX通道,置位Tx2_Complete_Flag标志表示传输开始,同时开启串口TC中断;

void DMA1_Channel4_5_6_7_IRQHandler(void)
{
    if(__HAL_DMA_GET_FLAG(&hdma_usart2_tx, DMA_FLAG_TC4) != RESET)
    {
        __HAL_DMA_CLEAR_FLAG(&hdma_usart2_tx, DMA_FLAG_TC4);
        __HAL_DMA_DISABLE(&hdma_usart2_tx);
        Tx2_Complete_Flag = 1;
        __HAL_UART_ENABLE_IT(&huart2, UART_IT_TC);
    }
}

 

当串口UART传输完成后,接下来在串口中断函数中检测到串口TC传输完成标志置位,清除TC标志,同时关闭串口TC中断,置位Tx2_Complete_Flag标志为0,表示此时串口接收空闲;

 

你可能感兴趣的:(STM32F072从零配置工程-串口DMA实现)