開始本篇之前我想先談一下為什么要把一個ModBus主機分成4篇來寫,一是代碼的分層理念,隨著工作時間的增長以及工作的深入你會發現分層是那么的至關重要不僅僅是代碼的可讀性更重要的是更方便的維護。初期寫代碼一個文件中甚至一個函數中既有功能代碼又有底層代碼,當增刪功能或者平臺移植的時候都不知道去哪里哭,分層也許在初期會增加代碼量顯得很麻煩但是當你的架構建立起來之后會變得十分方便,解決問題得心應手。操作系統幫你做好了底層的分層和軟件層面的任務調度,但是應用層面依然需要個人來做好。分層理念需要時時有處處有。二是ModBus的一對多特性,當一個主機任務讀取某個從機的數據并等待從機應答時,必須保證別的主機任務不要來動ModBus總線,如果有個不長眼的主機任務過來咔咔操作了總線那么之前等待從機應答的任務就崩潰了。可能有人說了等待應答的時候我直接while死等不就行了,直接斷了別的主機任務的念想,當然這樣是可以解決問題的不過要是某個從機不在線就會導致整個系統卡死一段時間,要是你用了操作系統還好如果是裸機那就非常影響用戶體驗并且會使其他任務比如刷屏按鍵出現一些莫名其妙的問題,我們都知道人的新陳代謝越快身體越強壯,同樣任務輪詢越快系統也越強壯。無論你使用操作系統還是裸機都要避免使用阻塞式的寫法。所以我們才大費周章分4層來解決一個ModBus主機問題。本篇調度篇內容很少但是整個ModBus主機系統的重中之重。
我們先來看代碼。
- #ifndef __RS485_H
- #define __RS485_H
- #include "Header.h"
- #include "THP.h"
- #include "EPC.h"
- #include "Delay.h"
- extern uint16_t RS485BusSilentTime1;
- void RS485Device1Init(void);
- void RS485Device1Handle(void);
- #endif
復制代碼- #include "RS485Device.h"
- uint16_t RS485BusSilentTime1;
- void RS485Device1Init(void)
- {
- THPInit();
- EPCInit();
- }
- static uint8_t TaskHandle(uint8_t En, uint8_t (*Task)(void*), void *Dev, uint8_t *BusTake, uint8_t TaskID, uint16_t *DelayTime)
- {
- if((En!=0) && ((*BusTake==0)||(*BusTake==TaskID)))
- {
- if(Task(Dev) != 0)
- {
- /*釋放總線*/
- *BusTake = 0;
- Set_Delay_Time(10,DelayTime);
- }
- else
- {
- /*占用總線*/
- *BusTake = TaskID;
- return 1;
- }
- }
- return 0;
- }
- void RS485Device1Handle(void)
- {
- static uint8_t BusTake=0;
-
- if(CheckDelay(&RS485BusSilentTime1) == 0)
- {
- if(TaskHandle(EPC1.S_P_En,SetPreVal,&EPC1,&BusTake,1,&RS485BusSilentTime1) != 0)
- {
- return;
- }
- if(TaskHandle(THP1.Enable,ReadTHP,&THP1,&BusTake,2,&RS485BusSilentTime1) != 0)
- {
- return;
- }
- if(TaskHandle(EPC1.R_En,ReadEPC,&EPC1,&BusTake,3,&RS485BusSilentTime1) != 0)
- {
- return;
- }
- }
- }
復制代碼 通過代碼可以看到該層引用了任務篇創建的"THP.h"和"EPC.h",在這里我們要對三個主機任務進行調度。
首先來看函數“static uint8_t TaskHandle(uint8_t En, uint8_t (Task)(void), void *Dev, uint8_t *BusTake, uint8_t TaskID, uint16_t *DelayTime)”;
該函數參數比較多,分別為任務使能信號(uint8_t En)、任務主體(uint8_t (Task)(void))、設備名稱(void *Dev)、是誰在使用總線(uint8_t *BusTake)、任務編號(uint8_t TaskID)、總線靜默時間控制(uint16_t *DelayTime);
該函數邏輯很簡單,就是判斷任務使能信號是否置位了并查看總線被哪個任務占用,若總線空閑或者被自己占用就去占用總線去執行任務主體函數,若任務主體函數返回0說明正在等待從機應答接著占用總線并返回1告知調度器我還沒用完,否則說明任務完成了釋放總線并給總線插入10ms的靜默時間然后返回0告訴調度器我完事了讓別人來吧。
函數“void RS485Device1Handle(void)”就是最終的調度器它在main函數的while循環中被執行,它首先實現10ms的總線靜默然后挨個詢問主機任務的當前狀態并滿足他們的需求。
至此,我的ModBus主機就完結了。
|