1. 程式人生 > >點點滴滴學習STM32微控制器系列 (二)

點點滴滴學習STM32微控制器系列 (二)

本部落格的所有原創文章要求署名、非商業用途和保持一致。要求署名必須包含我的網名(geokai)以及文章來源(選擇部落格首地址或者具體博文地址)。

商業性使用須預先徵得本人同意(傳送Email到 [email protected]).

 

因為最近在做Modbus方面的東西,所以準備就這個話題邊做邊寫。

1.寫在之前

epoch1

作為一個非電子學專業的人,至少在兩年前我是不知道有ModBus這種高層級的通訊協議的。2年前開始學習電子是從接觸arduino開始的,探索中弄清了什麼是UART,什麼又是RS232,RS485.就在這種薄弱的認知下開始自己稍微複雜一點的arduino專案。多感測器資料的獲取,然後通過433的模組進行無線傳輸。

其實僅僅是傳輸資料還並不困難,我最早的做法是將資料打包成如下的樣子。

S:1:25.00:E:S:2:100:E

所有的資料均以S開頭E結尾,並且使用:符號隔開。緊隨S其後的是感測器編號,而後是感測器資料。這種資料的編碼方式比較簡單,同時接收方也比較容易解譯這些資料。其實這已經有了ModBus或者說通訊協議的思想了。(當時覺得自己簡直太棒了)可是隨後問題就來了,隨著我的感測器資料越來越多,433模組單次就無法傳輸這麼多的資料了。會被自動分包,這樣就會造成頭尾的資料在解譯的時候丟失。除此之外傳輸效率問題也凸顯了,因為是字串傳輸數字浪費了大量的資源。至此我在傳輸資料這條路上的第一個時期結束了。總得來說這種方式學習專案湊活,稍微要求可靠性高一點就會出現嚴重的問題。

epoch2

有了第一個時期的工作經驗,我開始探索更加Robust的資料傳輸方式。這一階段我開始使用433模組進行雙向的Arduino通訊。我開始認識到查詢方式的存在。將無腦使勁傳資料的方式改為要什麼請求什麼資料的方式。同時我可以通過指令遠端控制另一臺機器進行特定操作。我將傳輸的資料均打包成如下的樣子

S:1:255:E

所有的資料均只有簡短的一串。還是S開頭E結尾。中間分為兩部分。不同的是我將中間的第一部分設定為功能碼,這些功能碼我預先寫在程式裡。比方說1就是讀感測器1的資料,2就是讀感測器2的資料,3是開啟或關閉繼電器1的功能。諸如此類。由於傳輸的資料少不會出現頭尾丟失。同時我的資料獲取採用查詢方式,所以不會漏資料。此外通訊是雙向的,可以達到獲取和控制兩種功能。這種方式我使用了很久。但我仍然意識到資料傳輸的低效性。但總得來說it works well for a long time.

epoch3

第二階段基本上在我接觸arduino後的半年就結束了。加上很多東西我都做好了,因此很長一段時間我沒有再設計什麼新的東西。之前我買感測器多買5v或者20ma模擬量輸出的感測器,然後自己在用ADC讀出來。直到我買了一個含有ModBus協議的感測器,我瞬間明白沒文化真可怕。我自己想的協議是多麼的幼稚,這種無腦協議70年代前工程師們就使用並拋棄了。並誕生了ModBUS這種robust的工業協議。有查詢有響應,由資料有效判斷的CRC,由資料傳輸開始與結束的3.5幀間隔,有充分抽象的功能表。總之robust and extensible.

當然好的東西是有代價的,那就是使用的門檻高了,確切的說是編寫微控制器程式的複雜度增加了。Arduino上有方便使用的modbus包,可以參看我的其他博文。雖然Arduino已經可以搞定一大堆的工程需求了。但是對於複雜的專案來說還是得上M3,M4核心,AVR似乎還是適用於小專案。

 

2.程式碼編寫的基本思路

(本人並非專業,只是業餘研究,因此思路僅供參考。能不能用到實際工程還需要我驗證後繼續談論。)

首先解決主機的程式碼

1.資料準備的函式

2.使用IT方式傳送資料

3.等待從機響應

4.使用DMA方式讀取資料,每次1幀,同時將讀到的1幀存放在快取陣列中,快取陣列指標後移1位

5.每讀完1幀資料就啟用一次定時器,定時時間為3.5幀長

6.3.5幀時長超時啟用中斷

7.中斷中將快取陣列轉存到modbus接收陣列中,同時快取陣列清零

 

3.CubeMx配置基本程式碼

使用Stm32F407VET6 微控制器進行開發。

1)配置時鐘

2)開啟uart1的DMA_RX模式,並且為迴圈接收,設定優先順序為6

3)開啟TIM9,設定預分頻為16799,開啟NVIC,設定優先順序為5,高於uart的DMA_RX優先順序即可

4)開啟FreeRTOS

 

4.KeilV5程式碼編寫關鍵點

執行HAL_UART_Receive_DMA(huart1, buf, 1)後,系統每接收到一個數據就會進入一次HAL_RXCpltCallBack()函式。這個回撥函式是需要自己編寫的。既將buf中的資料轉存到rec_buff中,並且指標加1.然後重置TIM9定時器,並激活TIM9定時器。正常接收的時候由於接收間隔小於3.5幀,TIM9會還沒來得及中斷就被重置。

當接收完成後TIM9會引發中斷,同時進入中斷回撥函式。在定時器中斷回撥函式中我們暫停定時器,同時將rec_buff中的資料再轉存到modbus_rec_buff中,並重置rec_buff陣列。如果不轉存,那麼在後續解譯接收到的資料的時候可能會接收到新的資料,使得資料無效化。

具體的程式碼在下期貼上。