STM32 HAL庫利用DMA實現串口不定長度接收方法


參考:https://blog.csdn.net/u014470361/article/details/79206352

我這里使用的芯片是 F1 系列的,主要是利用 DMA 數據傳輸方式實現的,在配置工程的時候要注意配置好 DMA,並開啟中斷。

  

  如果出現數據長度對,可是數據接收不完整,把Memory勾選即可:

  

1、利用STM32 cubemx 建立一個工程,工程建立請參考我以前的文章:https://www.cnblogs.com/xingboy/p/9597464.html

2、利用STM32 cubemx 生成代碼后,我們先定義一些變量來使用

/*    自己添加代碼部分    */
volatile uint8_t rx_len=0;                            //接收數據長度
volatile uint8_t recv_end_flag=0;                     //接收完成標記位
uint8_t  rx_buffer[100];                              //接收緩存
char          BUFFER_SIZE=100;                        //不定長數據的最大長度,設置為100則最大長度為100

這里為什么要定義volatile 關鍵字呢?

       主要是因為volatile 關鍵字提醒編譯器定義的變量是易變的,編譯后的程序每次需要存儲或讀取該變量時,會直接從變量地址讀取數據。在中斷或多線程中使用volatile關鍵字可以避免不同優化等級時程序出錯,提高程序的魯棒性。

接着對串口初始化添加一些代碼,程序如下:

/* USART2 init function */
static 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;
  if (HAL_UART_Init(&huart2) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }
    
    /*    自己添加代碼部分    */
   __HAL_UART_ENABLE_IT(&huart2, UART_IT_IDLE);        //使能idle中斷
   HAL_UART_Receive_DMA(&huart2,rx_buffer,BUFFER_SIZE);  //打開DMA接收,數據存入rx_buffer數組中。

}

3、接收函數我寫在了另一個文件上,其他文件要用到上面 main文件里面定義的變量就要聲明一個外部變量

extern volatile uint8_t rx_len;
extern volatile uint8_t recv_end_flag;
extern uint8_t  rx_buffer[100];
extern char      BUFFER_SIZE;

4、接着修改串口中斷服務函數,在串口中斷服務函數里添加接收代碼,代碼如下:

void USART2_IRQHandler(void)
{
  /* USER CODE BEGIN USART2_IRQn 0 */
    
    /*    自己添加代碼部分    */
    uint32_t tmp_flag = 0;
    uint32_t temp;
    tmp_flag =__HAL_UART_GET_FLAG(&huart2,UART_FLAG_IDLE);   //獲取IDLE標志位
    if((tmp_flag != RESET))  //idle標志被置位
    { 
        __HAL_UART_CLEAR_IDLEFLAG(&huart2);  //清除標志位
        temp = huart2.Instance->SR;    //清除狀態寄存器SR(F0的HAL庫USART_TypeDef結構體中名字為ISR:USART Interrupt and status register),讀取SR可以清楚該寄存器
        temp = huart2.Instance->DR;    //讀取數據寄存器中的數據,讀取DR(F0中為RDR:USART Receive Data register)
        HAL_UART_DMAStop(&huart2); 
     temp = hdma_usart2_rx.Instance->CNDTR;   //獲取DMA中未傳輸的數據個數,NDTR寄存器分析見下面 rx_len = BUFFER_SIZE - temp;         //總計數減去未傳輸的數據個數,得到已經接收的數據個數 recv_end_flag = 1;               //接受完成標志位置1 } /* USER CODE END USART2_IRQn 0 */ HAL_UART_IRQHandler(&huart2); /* USER CODE BEGIN USART2_IRQn 1 */ /* USER CODE END USART2_IRQn 1 */ }

  上面的 CNDTR 寄存器在 DMA 通道結構體中定義了 CNDTR 寄存器,這個不同的芯片HAL庫里面定義的命名有點不同,有興趣的可以自己去查看一下,那為什么是未傳輸的數據數呢,STM32的中文手冊給出了該寄存器的具體說明。(注意:建立工程的時候要添加串口TX RX 的DMA通道,以及打開DMA中斷)

/** 
  * @brief DMA Controller
  */
typedef struct
{
  __IO uint32_t CCR;
  __IO uint32_t CNDTR;
  __IO uint32_t CPAR;
  __IO uint32_t CMAR;
} DMA_Channel_TypeDef;

寄存器說明如下:

5、接着編寫接收處理函數,代碼如下:

/***************************************************************
    *函數名:Data_Turn
    *輸  入:無
    *說  明:串口接收完成,返回串口查看接收情況
    *返回值:無
  **/
void Data_Turn(void)
{

  if(recv_end_flag ==1)        
  {
      printf("rx_len=%d\r\n",rx_len);                           //打印接收長度
      HAL_UART_Transmit(&huart2,rx_buffer, rx_len,200);        //接收數據打印出來
      for(uint8_t i=0;i<rx_len;i++)
      {
         rx_buffer[i]=0;   //清接收緩存
      }
      rx_len=0;        //清除計數
      recv_end_flag=0;    //清除接收結束標志位
   }
}

 6.再主函數里的的while循環里再次打開DMA中斷接收

HAL_UART_Receive_DMA(&huart2,rx_buffer,BUFFER_SIZE);            //重新打開DMA接收 

 

運行效果如下圖,我的代碼是接收到Do-0:1字符串,判斷字符串,返回我需要的字符串,效果正確。

 

補充一點最近新發現的關於串口中斷接收的問題:

  串口中斷接收如果使用HAL庫的中斷接收函數,接收到的數據量遠小於設定要接收的數據量,串口一直處於Busy狀態,會出現接收死循環的情況,接收的數據跟設定不符會出錯

注意:

  測試過程中發現,用接收串口助手的所有數據都沒問題,不過接收模塊的不定長數據時,如果這個數據之間包含回車換行符會接收不全,例如:AT\r\nOK\r\n,這個就只能接收到AT\r,具體什么原因造成的還沒找出原因,有知道的可以告訴我一下。

  不過如果是串口助手發這串數據下來,卻又可以全部接收完成,想不通。為了解決這個問題,我又找出了一個中斷接收的方法,可以實現了不管中間有沒有回車,都可以接收完成。傳輸門:https://www.cnblogs.com/xingboy/p/10154475.html

 


免責聲明!

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



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