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