STM32串列埠接收中斷——基於HAL庫
寫在前面
最近需要使用一款STM32L4系列的晶片進行開發,需要學習使用HAL庫。在進行串列埠中斷使用的時候遇到了一些小麻煩,寫下解決方案供大家參考。
1.UART相關的標頭檔案引用錯誤
由於本人直接使用MDK進行開發,沒有使用CubeMX,所以一些初始化需要手動進行。在引用UART相關的標頭檔案時,記得將"stm32l4xx_hal_conf.h"檔案中的相關引用取消註釋,如下圖:

2.如何接收字串(多次進入中斷)
接收字串主要有兩種方法,一種是對中斷函式進行改造,另一種是對接收回調函式進行改造。
在闡述這兩種方法之前,需要介紹函式“HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)”。該函式的作用是使用者自定義一個緩衝區(即引數pData),接受一定數量(由引數Size決定)的字元存入緩衝區中。同時,引數Size還決定著進入回撥函式的頻率,即每接收Size個字元,就進入一次回撥函式。需要注意的是,Size只決定進入回撥函式的頻率,而不能影響進入接收中斷的頻率,無論Size是多少,每接收完成一個字元都會進入一次接收中斷。
方法1:改造回撥函式
①首先在主函式中進入主迴圈前的位置呼叫一次 HAL_UART_Receive_IT函式,定義一個字元陣列getBuffer[]作為緩衝區,引數Size設定為10。即每接收10個字元,就進入一次回撥函式。

②註冊中斷函式
1void USART1_IRQHandler(void) 2{ 3HAL_UART_IRQHandler(&UartHandle); //該函式會清空中斷標誌,取消中斷使能,並間接呼叫回撥函式 4}
③在檔案“stm32l4xx_hal_uart.h”中,我們可以看到串列埠接收回調函式的定義。使用“_weak”關鍵字定義的函式,其具有如下特性: 一般情況下和一般函式相同。但是當有一個同名函式但是不帶__weak被定義時,所有對這個函式的呼叫都是指向後者(不帶__weak那個)。也就是說,ST官方提供的這個回撥函式需要我們自己進行改寫。
1/** 2* @brief Rx Transfer completed callback. 3* @param huart UART handle. 4* @retval None 5*/ 6__weak void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) 7{ 8/* Prevent unused argument(s) compilation warning */ 9UNUSED(huart); 10 11/* NOTE : This function should not be modified, when the callback is needed, 12the HAL_UART_RxCpltCallback can be implemented in the user file. 13*/ 14}
我們在主函式所在的檔案中對回撥函式進行改寫:
1uint8_t myBuffer[] = "I have gotten your message: "; //使用者提示資訊 2uint8_t Enter[] = "\r\n"; //回車換行 3uint8_t getBuffer[100]; //使用者自定義的緩衝區 4void HAL_UART_RxCpltCallback(UART_HandleTypeDef *UartHandle) 5{ 6while(HAL_UART_Transmit(UartHandle, (uint8_t*)myBuffer, COUNTOF(myBuffer), 5000)!= HAL_OK); //傳送字串,使用者提示資訊 7while(HAL_UART_Transmit(UartHandle, (uint8_t*)getBuffer, 10, 5000)!= HAL_OK); //傳送使用者自定義緩衝區中的資料 8while(HAL_UART_Transmit(UartHandle, (uint8_t*)Enter, COUNTOF(Enter), 5000)!= HAL_OK); //傳送回車換行 9}
以上程式碼的作用是把使用者傳送給微控制器資料再返回給使用者。執行效果如下圖:

我們可以看到,使用者向單片機發送了10個字元,微控制器向串列埠助手返回了這10個數據。但是以上程式只能實現一次,當我們再次向單片機發送資料時,微控制器卻不再返回資料。這是因為我們在中斷函式中取消了中斷使能,所以導致了進入一次中斷後,中斷被關閉,無法再次進入中斷的現象。為了實現多次資料返回,我們要在中斷處理函式中新增一行程式碼:
1void USART1_IRQHandler(void) 2{ 3HAL_UART_IRQHandler(&UartHandle); //該函式會清空中斷標誌,取消中斷使能,並間接呼叫回撥函式 4HAL_UART_Receive_IT(&UartHandle, (uint8_t *)&value,1);//新增的一行程式碼 5}
這樣就可以實現多次資料返回了,新的執行結果如下圖:

可見,函式HAL_UART_Receive_IT還有中斷使能的作用。這一功能的實現我們可以在HAL_UART_Receive_IT函式中找到。
方法2:改造中斷處理函式
①首先在主函式中進入主迴圈前的位置呼叫一次 HAL_UART_Receive_IT函式,定義一個字元value作為緩衝區,引數Size設定為1。即每接收1個字元,就進入一次回撥函式。使得進入回撥函式的頻率與進入中斷處理函式的頻率相同。這樣,我們就可以直接在中斷函式中對接收的資料進行處理了。

②註冊中斷函式
1uint8_t myBuffer[] = "I have gotten your message: "; 2uint8_t getBuffer[10]; 3uint8_t Enter[] = "\r\n"; 4void USARTx_IRQHandler(void) 5{ 6HAL_UART_IRQHandler(&UartHandle); //該函式會清空中斷標誌,取消中斷使能,並間接呼叫回撥函式 7 8getBuffer[countOfGetBuffer++] = value; 9if(countOfGetBuffer == 10) 10{ 11while(HAL_UART_Transmit(&UartHandle, (uint8_t*)myBuffer, COUNTOF(myBuffer), 5000)!= HAL_OK); 12while(HAL_UART_Transmit(&UartHandle, (uint8_t*)getBuffer, countOfGetBuffer, 5000)!= HAL_OK); 13while(HAL_UART_Transmit(&UartHandle, (uint8_t*)Enter, COUNTOF(Enter), 5000)!= HAL_OK); 14countOfGetBuffer = 0; 15} 16HAL_UART_Receive_IT(&UartHandle, (uint8_t *)&value,1);//由於接收中斷是每接收一個字元便進入一次,所以這一行程式碼必須新增,否則只能接收一個字元,而無法接收整個字串 17}
以上程式碼的作用是接收每個來自使用者的字元,並依次存入使用者自定義的緩衝區中,數量達到10個後,將緩衝區中的所有資料返回給使用者,同時清空計數,準備接下來10個字元的接收。執行效果如下圖:

寫在最後
看完本文,大家可能對 回撥函式 和 中斷處理函式 的關係產生了疑問。其實是這樣的,微控制器每完成接收一個字元,就會進入一次 中斷處理函式 ,而在 中斷處理函式 中,我們又呼叫了函式“void HAL_UART_IRQHandler(UART_HandleTypeDef *huart)”,該函式會間接呼叫 回撥函式 ,也就是說 回撥函式 是由 中斷處理函式 間接呼叫的。而函式“HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)”決定了 中斷處理函式 呼叫 回撥函式 的頻率,若Size為1,則每進入一次 中斷處理函式 都會呼叫一次 回撥函式 ;若Size為10,則每第十次進入 中斷處理函式 時,才會呼叫 回撥函式 。