本節我們來學習異步串口uart的應用,使用輪詢和中斷兩種方式,來實現計算機向單片機發送數據,單片機處理之后再將數據返回。
1)cubemx生成代碼
首先還是在cubemx中生成代碼,選擇器件、設置SYS(調試接口)、設置RCC(外部晶振時鍾源)這幾個步驟和前一節一樣(也可以復制前面閃燈的工程,在上面修改)。
然后設置串口引腳,這里我們選擇uart1,異步串口,選完后,已經使用的串口引腳PA9和PA10會變成綠色;然后選擇開啟串口全局中斷:

之后,和上一節一樣,在時鍾選項卡設置主時鍾為72M;在project Manager選項卡設置工程名和路徑,生成工程代碼。
2)uart的輪詢模式
在keil中打開生成的工程,可以看到軟件生成的代碼中,main函數中已經有uart硬件初始化的代碼。
如果我們使用串口的輪詢模式,那么已經可以直接使用hal函數了:
HAL_UART_Transmit()和HAL_UART_Receive()。
如下圖,是實現等待計算機發送16個字節數據,收到后,每個數據加1后再返回給計算機:


輪詢模式使用時,函數的最后一個參數是超時數值,圖中設置的是0xffff,也就是說函數的執行超時是65535ms。
在接收函數中,如果65535ms還未接收完16個字節的數據,則會向后執行,如果接收完則立即向后執行。發送數據的函數也一樣。
輪詢模式使用起來最簡單,但是會占用大量的cpu時間,在等待接收和等待發送完畢時,cpu不能去做別的運算,只能在這里空等,運行的效率很低。
3)uart的中斷模式
中斷模式可以提高cpu的使用效率,在等待數據接收和發送時,cpu可以取做別的運算,只需在發送/接收完的一點時間進入中斷做一些處理。
由於我們在cubemx中配置串口時,已經設置了串口的全局中斷,所以這里可以很方便地使用下面這兩個函數:
HAL_UART_Transmit_IT();發送函數好理解一些,就是將要發送的數據和長度填入,然后程序可以執行后面的任務,系統會自動使用中斷模式將數據發完(之后只在每發完一個字節時進一次中斷,占用很少的時間)。
HAL_UART_Receive_IT();接收函數,將要接收的數據和長度填入,然后程序可以執行后面的任務,系統會自動使用中斷模式接收數據,當接收完指定長度的數據后,會產生一個中斷;在實際使用時,要在中斷回調函數中設置一個標志位,循環去檢測標志位,才能知道它什么時候已收完。
我們以一個例子來看一下它們的使用方法:
先定義變量:

再重寫接收函數的回調函數,在回調函數中設置標志位:

主循環中,先設置中斷接收16個字節數據,然后等待,當接收滿16個字節后,回調函數置標志位,主循環中檢測到后,可以作進一步處理:

4)HAL庫中斷發送和接收的缺點
先講講HAL_UART_Transmit_IT()發送的這個函數。要注意這個函數使用時,如果前一次要發送的數據還未發完,又進行了一次新的發送,那么它會繼續發送前一次的數據,本次發的數據不會執行。如果需要確保兩次都發送出去,可以這樣使用:
while(HAL_BUSY == HAL_UART_Transmit_IT(&huart1, str, 12));
while(HAL_BUSY == HAL_UART_Transmit_IT(&huart1, str2, 4));
這里,如果前一次未發完,會一直等待到發完,再發送本次的數據,和輪詢時的等待差不多。
HAL_UART_Receive_IT()接收的這個函數,最大的不方便就是你得事先設定接收多少數據,這個是比較違反直覺的,因為你並不會知道外部什么時候發數據過來,也不知道外部一次會發多少數據。這會使得處理接收數據產生一定的困難。
當然,你可以設置每次接收一個字節數據就產生一次中斷,再對收到的數做處理,這是可以的,但是,這樣的做的效率很低,尤其HAL庫每進一次中斷要判斷很多情況,會占用大量的cpu時間;以前測試過用921600波特率,中斷占用的cpu時間大約有1/3之多!另外,這個方法在同時有收和發時,可能會進入溢出錯誤的死鎖;可能是由於同時出現了溢出中斷和收數中斷時,處理不完全的問題;有網友遇到過,沒有深究。
一般來講,串口使用fifo環形緩沖隊列是很好的解決方案,下一節將詳細講講,如何改寫HAL庫的中斷函數,實現高效的串口收發。
歡迎關注我的公眾號,文章同步更新,可留言獲取相關資料和軟件:

