(4)STM32使用HAL庫實現串口通訊——理論講解


一、查詢模式

1.

二、中斷模式

1.中斷接收。

1.1先看中斷接收的流程(以 USART2 為例)

在啟動文件中找到中斷向量

USART2_IRQHandler

找到USART2_IRQHandler的函數定義

 

可以看到這里又轉到另一個函數里去了,再找下去:

該函數的源碼:

/**
  * @brief  This function handles UART interrupt request.
  * @param  huart: pointer to a UART_HandleTypeDef structure that contains
  *                the configuration information for the specified UART module.
  * @retval None
  */
void HAL_UART_IRQHandler(UART_HandleTypeDef *huart)
{
   uint32_t isrflags   = READ_REG(huart->Instance->SR);
   uint32_t cr1its     = READ_REG(huart->Instance->CR1);
   uint32_t cr3its     = READ_REG(huart->Instance->CR3);
   uint32_t errorflags = 0x00U;
   uint32_t dmarequest = 0x00U;

  /* If no error occurs */
  errorflags = (isrflags & (uint32_t)(USART_SR_PE | USART_SR_FE | USART_SR_ORE | USART_SR_NE));
  if(errorflags == RESET)
  {
    /* UART in mode Receiver -------------------------------------------------*/
    if(((isrflags & USART_SR_RXNE) != RESET) && ((cr1its & USART_CR1_RXNEIE) != RESET))
    {
      UART_Receive_IT(huart);
      return;
    }
  }  

  /* If some errors occur */
  if((errorflags != RESET) && (((cr3its & USART_CR3_EIE) != RESET) || ((cr1its & (USART_CR1_RXNEIE | USART_CR1_PEIE)) != RESET)))
  {
    /* UART parity error interrupt occurred ----------------------------------*/
    if(((isrflags & USART_SR_PE) != RESET) && ((cr1its & USART_CR1_PEIE) != RESET))
    {
      huart->ErrorCode |= HAL_UART_ERROR_PE;
    }
    
    /* UART noise error interrupt occurred -----------------------------------*/
    if(((isrflags & USART_SR_NE) != RESET) && ((cr3its & USART_CR3_EIE) != RESET))
    {
      huart->ErrorCode |= HAL_UART_ERROR_NE;
    }
    
    /* UART frame error interrupt occurred -----------------------------------*/
    if(((isrflags & USART_SR_FE) != RESET) && ((cr3its & USART_CR3_EIE) != RESET))
    {
      huart->ErrorCode |= HAL_UART_ERROR_FE;
    }
    
    /* UART Over-Run interrupt occurred --------------------------------------*/
    if(((isrflags & USART_SR_ORE) != RESET) && ((cr3its & USART_CR3_EIE) != RESET))
    { 
      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_SR_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 */
      dmarequest = HAL_IS_BIT_SET(huart->Instance->CR3, USART_CR3_DMAR);
      if(((huart->ErrorCode & HAL_UART_ERROR_ORE) != RESET) || dmarequest)
      {
        /* 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;
            if(HAL_DMA_Abort_IT(huart->hdmarx) != HAL_OK)
            {
              /* Call Directly 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 */

  /* UART in mode Transmitter ------------------------------------------------*/
  if(((isrflags & USART_SR_TXE) != RESET) && ((cr1its & USART_CR1_TXEIE) != RESET))
  {
    UART_Transmit_IT(huart);
    return;
  }
  
  /* UART in mode Transmitter end --------------------------------------------*/
  if(((isrflags & USART_SR_TC) != RESET) && ((cr1its & USART_CR1_TCIE) != RESET))
  {
    UART_EndTransmit_IT(huart);
    return;
  }
}

 

也就是說,當串口中斷觸發以后,幾經周轉到了這里,該函數功能是讀取寄存器的幾個狀態,判斷無誤后再轉到另一個函數,就是上圖小矩形框出來的UART_Receive_IT(huart);

 然后我們再去看UART_Receive_IT(huart)這個函數原型:

回調函數就在這個UART_Receive_IT(huart)函數里:

在回調函數上邊有兩行很重要的代碼:

這兩行代碼的作用是關閉串口接收中斷,也就是說,在一次串口中斷接收過程的最后,即串口接收完一組數據之后會關閉串口接收中斷。(這個后面還會再講,先記住)。

總結一下,串口中斷接收的流程:

USART2_IRQHandler(void)    ->    HAL_UART_IRQHandler(UART_HandleTypeDef *huart)    ->    UART_Receive_IT(UART_HandleTypeDef *huart)    ->    HAL_UART_RxCpltCallback(huart);

Callback函數就是用戶要重寫在main.c里的回調函數。

 再說明一下一個很重要的問題:STM32的每個串口中斷有好幾個(發送接收等),但是只要是與串口相關的中斷發生系統都會先調用同一個函數,也就是中斷向量表中的那個,比如usart2的話就是USART2_IRQHandler(void),然后這個函數再調用HAL_UART_IRQHandler,在HAL_UART_IRQHandler中去讀取寄存器判斷究竟是那幾個位被置為1,確定好是哪個中斷之后(接收還是發送)再調用不同的回調函數。

1.2如何使用接收中斷。

在cube中配置完了之后並沒有使能串口中斷(有一個串口初始化函數,但是在這個函數中並未使能串口中斷)需要用戶手動使能。使能代碼如下:

HAL_UART_Receive_IT(&huart2, (uint8_t *)kRxBuffer, 10);

 什么意思呢?

HAL庫的串口接收思路是這樣的:用戶你可以隨便定義一個緩存區,大小隨意,然后通過上邊這個函數把這個緩存區對應到串口的接收,上面函數的意思就是把kRxBuffer(這是一個數組)作為緩存區,指定大小為10。然后usart2接收數據的時候就防到kRxBuffer這個數組中,只有當接收到10個數據之后才調用一次callback函數(回調函數)。當然不要忘了該函數的使能串口接收中斷功能, 在:二、中斷模式 的1.1節中說到了串口接收完數據后會關閉使能,所以,在回調函數中一定要再寫一次HAL_UART_Receive_IT(&huart2, (uint8_t *)kRxBuffer, 10),使能接收中斷。

小小的總結下串口中斷接收怎么用:

(1)指定一個緩存區(串口接收到的數據會全部堆到這個緩存區)

(2)使能串口接收中斷,並把緩存區對應到串口

(3)在回調函數中實現接收到數據之后的操作(比如處理數據)並再次使能串口接收中斷。

所以更具體一下串口接收的流程就是這樣的:

(1)串口一個接一個的接收到數據填充到緩存區

(2)緩存區滿(大小是用戶定義的)程序幾經輾轉最后會調用到回調函數。

(3)執行用戶在回調函數中實現的功能。

2.中斷發送。

2.1發送中斷的觸發流程。

由於在STM32中usart2的入口中斷只有一個:

就是上圖的中斷向量表中紅框標出來的。其他的所有中斷其實都是從這里出發的,我們再和捋接收一樣捋一遍發送。

首先是USART2_IRQHandler,找到這個函數原型(這一步和接收完全一樣):

再找HAL_UART_IRQHandler(&huart2);原型:

到這里還是和接收完全一樣,注意是完全一樣,源碼也就是上面接收貼出來的一樣。這次我們主要注意該函數最后幾行(可以翻上去看源碼):

把中間代碼收起來以后看最后紅框,這就很明顯了,這里觸發了發送中斷(軟件觸發)

接着去找這個UART_EndTransmit_IT(huart)的函數原型:

第一個紅框里清除了發送中斷使能(同接收一樣,在用完之后就關掉,但是不同於接收,發送完成就不用再在回調函數中使能了,因為在中斷發送的時候就會使能),第二個紅框調用回調函數。

 2.2如何使用發送中斷。

中斷發送的意思,非常類似於中斷接收,但其中有一些不同,看下面這個函數:

HAL_UART_Transmit_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)

 一個非常類似於中斷接收使能的函數。接收中斷使能函數的作用是綁定接收緩存區使能接收中斷,但是對於發送,該函數的作用是發送指定長度的指定數據使能發送中斷

比如有一個unsigned char 數組a[10],HAL_UART_Transmit_IT(&huart2, a, 10),這一句的意思是用usart2(串口2)發送a數組中的10個數據,然后使能發送中斷。

當發送完成之后(或者發送一半,發送一半也有個中斷)就會執行回調函數。

總結一下發送中斷:

使用HAL_UART_Transmit_IT函數發送指定長度的數據,並使能發送中斷,發送到一半和發送結束會觸發中斷(相關的回調函數是HAL_UART_TxHalfCpltCallback()HAL_UART_TxCpltCallback())中斷觸發后發送中斷使能會被清除,然后調用回調函數,回調函數執行完成之后結束本次發送。

 


免責聲明!

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



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