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,表示此時串口接收空閑;

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM