應用篇-在STM32L051上使用RT-Thread 第四篇,巧妙的使用信號量處理串口通訊。
目錄
前言
一、設計思路說明
二、驅動移植
三、信號量的處理
3.1 釋放信號量
3.2 獲取信號量
四、基本測試
4.1 接收測試
4.2 串口通訊細節問題
__HAL_UART_ENABLE_IT
HAL_UART_Receive_IT
4.3 發送測試
五、時刻關注占RAM大小
結語
前言
在上一篇文章,我們實現了溫濕度驅動移植,根據我們最初的基本設計思路,還有必須要實現的無線模塊串口通訊,本文就來移植一下無線模塊的串口通訊驅動。
再次說明一下,本應用篇重點在于理解在 RT-Thread 上的設計思路 以及 在小內存芯片上的注意事項,所以基礎的驅動代碼的實現并不會詳細的分析說明,但是博主在把本系列更新完以后會把最后的整個項目上傳,所以實在想看驅動實現的朋友到時候也可以去下載。
??
本 RT-Thread 專欄記錄的開發環境:
RT-Thread記錄(一、RT-Thread 版本、RT-Thread Studio開發環境 及 配合CubeMX開發快速上手)
RT-Thread記錄(二、RT-Thread內核啟動流程 — 啟動文件和源碼分析)
??
RT-Thread 內核篇系列博文鏈接:
RT-Thread記錄(三、RT-Thread 線程操作函數及線程管理與FreeRTOS的比較)
RT-Thread記錄(四、RT-Thread 時鐘節拍和軟件定時器)
RT-Thread記錄(五、RT-Thread 臨界區保護)
RT-Thread記錄(六、IPC機制之信號量、互斥量和事件集)
RT-Thread記錄(七、IPC機制之郵箱、消息隊列)
RT-Thread記錄(八、理解 RT-Thread 內存管理)
RT-Thread記錄(九、RT-Thread 中斷處理與階段小結)
??
在STM32L051C8 上使用 RT-Thread 應用篇系列博文連接:
RT-Thread 應用篇 — 在STM32L051上使用 RT-Thread (一、無線溫濕度傳感器 之 新建項目)
RT-Thread 應用篇 — 在STM32L051上使用 RT-Thread (二、無線溫濕度傳感器 之 CubeMX配置)
RT-Thread 應用篇 — 在STM32L051上使用 RT-Thread (三、無線溫濕度傳感器 之 I2C通訊)
一、設計思路說明
我們STM32L051C8與無線模塊通訊的串口是LPUART1(對應 pin to pin 的STM32F103C8 是串口3),使用的是中斷方式接收,所以當時在CubeMX 設置的時候我們就需要使能中斷。
STM32串口中斷接收是很基礎問題,本文的目的不在于說明STM32如何進行串口通訊,所以并不會詳細闡述如何使用串口接收,我們只做簡單說明:
對于STM32 的串口接收中斷,我們一般會使能UART_IT_RXNE或者UART_IT_IDLE。
簡單說明一下,如果串口收到一個字節就會產生RXNE中斷,如果接收完成一幀數據,就會產生IDLE中斷。
根據我自己使用的經驗,這個 IDLE 中斷所謂的一幀數據,是 stm32 內部自身根據自己主頻,波特率等一些參數作為判斷依據,本質上也是認為在多長的時間內沒有收到新的數據就當成一幀。
再根據以前的使用經驗和測試,我使用的無線通訊模塊給 STM32 發送一幀數據,會觸發兩次IDLE中斷,這個具體也沒有深究,因為本身無線通訊模塊也是靠自己的單片機內核發送數據,或許模塊內部有點特殊處理,通過這個問題我也發現IDLE不是萬能的一幀數據,在遇到一些特殊模塊的時候也會有問題。當然,在我使用的其他設備,所有串口通訊的傳感器上,使用IDLE作為一幀數據接收完成的判斷都是沒問題的。
所以,雖然我們本次模塊接收的是不定長度,但是我們這里還是不使用IDLE
作為一幀數據的判斷,我們只使用RXNE
中斷。
為了更加直觀的理解我們的設計思路,我也不準備使用 DMA 通方式。
綜上,本次應用的設計思路是:只使能RXNE中斷,當接收到第一個字節,釋放一個信號量。
另外一邊,串口接收線程一直在等待這個信號量,如果獲取到信號量,等待一定時間(等待一定時間是為了保證接收到完整的一幀數據,一般就是幾個ms),然后進行數據處理。
發送數據的話就簡單,寫好驅動,組包發送即可。
為了接收數據,我們需要定義個全局變量數組作為緩沖區。
后來在實際使用的時候這個思路稍微修改了一下,因為每次產生中斷,都會發送一次信號量,那么一幀數據有多少個字節,就會發送多少個信號量,那么等待信號量的線程就會不停的獲取到信號量,那么我們怎么來處理?且看下文……
二、驅動移植
因為我使用的Enocean模塊并不是通用主流的通訊模塊,是公司需要使用的,考慮到各種問題,也不方便把模塊詳細的介紹一遍,但是這并不影響我說明程序的移植和串口的使用思路,大家不需要在意內部的具體實現,只要明白我在 STM32L051C8 上 RT-Thread 是如何處理串口數據的即可。
首先我們把需要保存接收數據的數組定義一下:
這個全局數據可是直接占用了一大塊 RAM 空間,enoncean_buff多大就占用多大,可是這是沒有辦法的!
然后根據上一篇文章的說明,我們直接把.c 和.h文件拷貝到我們的 mydrivers 目錄下:
編譯一下看看,
其中,上面的警告提示我們還需要實現1個函數,就是串口發送數據的函數,我們也移植過來,加在CubMX串口驅動文件中:
實現好這些基本上整體上沒問題了,然后需要做一些移植過來代碼的細節修改,細節修改這里就不一一說明,我們使用一個典型的地方舉個例子。裸機中,除了中斷所有的操作都是先后進行的,所以有很多延時函數是干等,在使用 RT-Thread 的時候這些函數都得去掉,比如
三、信號量的處理
信號量的處理算得上本文的重點了,本來我們在 RT-Thread Nano 上使用串口通訊,完全可以按照裸機的方式來,定義全局變量然后線程輪詢接收函數,但是這里我還是想著沒有消息的時候線程輪詢完全是浪費資源,我得發揮操作系統的優勢。 至少做到,只有消息來的時候線程才會喚醒去執行,其他時候都是阻塞狀態。
雖然信號量處理的方式并不是最優的,當然也是本著測試的原則,來嘗試一下。
再次注意,我們在本應用第一篇就說明過,只能使用靜態初始化的方式創建對象,信號量也不例外,測試時候還在這里出錯了。 = =!
3.1 釋放信號量
首先初始化信號量,中斷響應函數中做基本的處理:
注意,有一個HAL庫的基本使用問題,什么時候才會調用HAL_UART_RxCpltCallback 中處理,我們用戶的處理可以在原始的中斷向量表對應的函數LPUART1_IRQHandler中處理,也可以在上圖的HAL_UART_RxCpltCallback 中處理。
同時,數據處理的方式要注意,標志著緩沖數組個數的 Enocean_Data是否需要先++,也是需要注意。
這些其實是STM32 HAL庫使用的基本知識,在下面《4.2 串口通訊細節問題》會有說明。
如果在LPUART1_IRQHandler中處理也可以,數據處理是放在HAL_UART_IRQHandler(&hlpuart1);前面還是后面也是有講究的:
3.2 獲取信號量
考慮到內存不夠,不想再新建線程作為數據接收處理了,直接把數據接收處理放在主函數線程里面,這里給出基本的框架:
??(其實兩句代碼就是本文核心框架~ ~)??
四、基本測試
我們先測試基本的接收函數移植是否正常,然后再測試發送函數。
4.1 接收測試
完成上面的步驟,我們基于上面的框架,應該是可以用起來了,比如最初的上電需要讀取通訊模塊的ID,得到ID以后發送一次無線報文,實現的代碼如下:
結果如下:
在解決了上圖所說的ID讀取異常問題之后(就在下面《4.2 串口通訊細節問題》,這是STM32 HAL庫的使用問題),我們再添加一些框架代碼:
看下測試結果,上電ID讀取正確,按鍵線程正常,接收報文也正常:
4.2 串口通訊細節問題
具體問題描述:
上面的第一次的ID讀取截圖有問題,檢查了一段時間,后來發現接收額數據與實際的有一位的差別,然后解決了這個問題,在接收數據的時候還是發現每次接收數據會丟失第一個字節。
這是STM32 HAL庫基本使用導致的,因為博主開始直接使用程序移植,有些細節的地方沒有第一時間發現。
其實最根本的原因在于,串口開啟之時是如何使能中斷接收的!
是用__HAL_UART_ENABLE_IT
宏定義使能中斷
還是HAL_UART_Receive_IT
這個函數使能中斷?
這是STM32的基礎使用問題,復制的分析調整過程這里就省略了,我只把最后的結論和使用方法說明一下,其實使用HAL_UART_Receive_IT
內部會調用__HAL_UART_ENABLE_IT
。
__HAL_UART_ENABLE_IT
先來說說使用__HAL_UART_ENABLE_IT
的情況,正確的流程圖如下:
這是一種效率比較高的方式,使用__HAL_UART_ENABLE_IT
使能無法進入HAL_UART_RxCpltCallback
函數(有問題請指出),所以我們得在LPUART1_IRQHandler
進行數據處理。
HAL_UART_Receive_IT
一般使用方式
如果串口初始化以后就使用函數HAL_UART_Receive_IT
開啟接收中斷,大部分網絡文章教程說明使用流程如下圖:
其他方式說明一
當然,我們的數據處理可以不在HAL_UART_RxCpltCallback
函數中,也可以學習上面在LPUART1_IRQHandler
中處理,比如:
上面圖中的注意事項,原因是因為HAL_UART_IRQHandler(&hlpuart1);處理過程會關閉一些中斷,之后才調用HAL_UART_RxCpltCallback,我們在HAL_UART_RxCpltCallback最后的函數HAL_UART_Receive_IT又會重新打開,所以可以正常走流程。
如果我們在LPUART1_IRQHandler中處理,在執行 HAL_UART_IRQHandler(&hlpuart1);的時候會關閉中斷,如果在此之后不再次使能,就無法繼續響應下次中斷了!
其他方式說明二
開始使用函數HAL_UART_Receive_IT
開啟接收中斷,其他地方使用完全和使用__HAL_UART_ENABLE_IT
的情況一樣也是可以的:
至于原因,是在HAL_UART_Receive_IT
函數最后會使能RXNE
中斷,就和使用__HAL_UART_ENABLE_IT
是一樣的:
兩種方式都可以實現串口中斷處理,然后兩種方式結合也是可以的,但是并不建議,除非你完全理解HAL庫的內部實現方式,你完全知道自己在做什么!
4.3 發送測試
發送測試其實在我們前面發送學習報文已經得到過驗證了,能夠正常的發送學習報文表明發送功能沒有問題。
我們這里要做的就是把溫濕度的數據封包至無線報文中發送出去,這里發送函數的話按理來說也可以新建一個線程專門處理,收到特定的信號量進行發送,但是考慮到內存問題而且我們本應用功能比較簡單,所以我們直接在溫濕度讀取線程里面進行,以前是讀取了數據打印出來,現在是封包至無線協議通過報文發送出去。
發送操作直接放在溫濕度讀取線程里面進行處理:
測試結果正常,如圖:
還好我把發送功能加入到溫濕度讀取的線程中,并不需要增加線程??臻g就可以正常運行。
經過上面的折騰,在串口細節處理上花了不少的時間,不過好在結局還算圓滿,接收和發送都測試正常!
五、時刻關注占RAM大小
串口的應用我們并沒有新建線程,但是因為串口需要緩存區和與串口處理相關的一些全局變量,還有信號量也需要占用RAM空間,所我們的內存占用又變大了。
那么還是老樣子,今天測試完成以后和以前占用空間的對比圖上一下:
串口通訊的流程實現完后,程序運行時候需要占用 RAM的大?。?7416 字節,我們的芯片 RAM:8192字節。
結語
本文我們使用信號量實現了串口通訊,雖然也不是復雜的過程,還是遇到了不少的問題,使得本來昨天能夠完成的博文,不得不晚一天,在基本的 STM32 串口通訊問題上畫了一些時間調試,移植雖然可以省去大部分工作,但是細節問題不容忽視。
本次測試,也算是讓自己再次總結了一下STM32 HAL庫中的串口中斷接收方式。然后信號量接收的方式居然和自己考慮的一樣完美的實現接收一幀數據,還是有點小驚喜的!
其實本次應用篇到這里已經算是實現了一個單品傳感器了,結束? 既然是應用篇,那么當初計劃的功能還是得完善一下,比如,按鍵操作,短按長按的動作,至少把按鍵驅動移植完。定時器,使用定時器作為傳感器采集的時間機制,那么下一篇就決定了,按鍵驅動移植,如果順利把簡單的定時器也順帶加上~ ~!
沒想到本次測試比上一篇還累 = =! 繼續希望小伙伴多多支持,多多指教!
好了,本文就到這,謝謝大家!
審核編輯:湯梓紅
-
STM32
+關注
關注
2270文章
10923瀏覽量
357035 -
串口通訊
+關注
關注
1文章
261瀏覽量
24994 -
RT-Thread
+關注
關注
31文章
1305瀏覽量
40303
發布評論請先 登錄
相關推薦
評論