1. 程式人生 > >點點滴滴學習STM32單片機系列 (二)

點點滴滴學習STM32單片機系列 (二)

這一 需要 href 抽象 完成後 查詢方式 long 二階 復雜度

本博客的所有原創文章要求署名、非商業用途和保持一致。要求署名必須包含我的網名(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數組。如果不轉存,那麽在後續解譯接收到的數據的時候可能會接收到新的數據,使得數據無效化。

具體的代碼在下期貼上。

點點滴滴學習STM32單片機系列 (二)