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