,,,
(1.齊魯工業大學(山東省科學院),濟南 250353;2.山東省科學院自動化研究所;3.山東省汽車電子技術重點實驗室)
在嵌入式系統的軟件設計中,要根據具體應用要求和系統資源大小,選擇合適的嵌入式操作系統,根據應用邏輯劃分任務,然后利用操作系統提供的一系列API建立任務隊列,以消息或事件的形式進行任務間通信,實現任務調度[1],這種方式可以讓開發人員將精力集中在應用邏輯的開發上,不僅可以實現軟件的模塊化,還可以靈活地進行修改和維護。
在這種方式下,MCU上電初始化時首先建立任務隊列,為每個任務設定合適的優先級,并分配一定的堆棧用于存儲任務上下文。在系統運行階段,系統滴答、發送消息或事件、各種系統中斷都會觸發任務調度。執行任務切換時,首先將當前任務的上下文數據保存到任務堆棧中,然后將新任務堆?;謴偷組CU各類寄存器和系統棧中[2],根據新的PC(程序計數器)指針執行新任務。每個任務堆棧都會消耗寶貴的RAM資源,而且系統運行階段存在大量頻繁的中斷時,上下文的存儲和恢復會極大消耗MCU的計算資源,因此,只有在RAM資源豐富、主頻高的高端MCU中才選擇使用操作系統。在那些由于成本限制只能選擇RAM資源較小、主頻較低的中低端MCU的嵌入式系統的軟件設計中,只能采用不加操作系統的裸機方式。
在不帶操作系統的裸機嵌入式系統中,軟件系統在一個主循環體中運行。MCU循環調用由各種軟件模塊組成的主循環體,各個軟件模塊不存在各自的任務堆棧,共享一個系統棧。執行中斷處理程序或調用子函數時,MCU將一些局部變量、中間計算結果和寄存器存入系統棧,執行完中斷處理程序或子函數后恢復系統棧。在這種方式下,一般以大量的全局變量和標志位實現各個軟件模塊的交互,造成各個軟件模塊之間耦合性強,修改和維護不靈活。
本文借鑒操作系統的任務調度思想,提出一種裸機嵌入式系統的任務調度方法[3]。在裸機開發方式中,設計一種不帶任務堆棧的邏輯任務,按照具體應用劃分若干邏輯任務,這些邏輯任務共享一個系統棧,每個邏輯任務都有自己的事件隊列和任務處理程序,任務之間通過發送事件的形式進行通信。這種方法實現了類似于操作系統的任務調度機制,能夠清晰反映應用實現邏輯,同時提高了軟件模塊的內聚性,降低了軟件模塊之間的耦合性[4]。
為了清晰地反映應用的實現邏輯,以邏輯任務的形式實現各個軟件模塊,每個邏輯任務都有相應的任務處理函數和事件隊列,任務之間通過事件的形式進行通信,發送的事件填充到任務的事件隊列中,任務處理函數根據任務事件隊列中的事件執行相關操作。
為了便于管理,以結構體的形式描述邏輯任務,結構體成員變量包括任務ID、事件隊列、事件產生索引、事件消費索引。邏輯任務結構體如下所示:
typedef struct{
e_Event event[DEFAULT_EVENTQ_SIZE];
e_TaskId task_id;
uint8_ttick_idx;
uint8_ttalk_idx;
}s_Task;
以能夠反映任務具體功能的枚舉類型定義任務ID,以ID查找對應的邏輯任務結構體。同時,以任務ID作為任務優先級,ID值越大,優先級越高。筆者為某汽車廠開發的BCM的任務包括輸入信號檢測、RKE、CAN通信、LIN通信、網絡管理、門鎖控制、車燈控制、雨刮控制、車窗防盜報警、車身防盜報警、故障診斷、發動機防盜、定時器管理[2],定義任務ID枚舉類型如下:
typedef enum{
MIN_TASK_PRIO,
INPUT_DETECT_TASK_PRIO = MIN_TASK_PRIO,
RKE_TASK_PRIO,
CAN_TASK_PRIO,
LIN_TASK_PRIO,
NM_TASK_PRIO,
LOCK_CTRL_TASK_PRIO,
LGT_CTRL_TASK_PRIO,
WIPER_CTRL_TASK_PRIO,
WDW_CTRL_TASK_PRIO,
ALARMSTATE_TASK_PRIO,
SYS_MONITOR_TASK_PRIO,
IMMO_TASK_PRIO,
TIMER_TASK_PRIO,
MAX_TASK_PRIO = TIMER_TASK_PRIO,
}e_TaskId;
事件隊列從邏輯上來說是一個環形隊列,從實現上來說是一個枚舉類型的數組,包括兩個索引:事件產生索引和事件消費索引,事件產生索引以tick_idx表示,事件消費索引以talk_idx表示。事件隊列的大小根據實際應用而定。
MCU上電初始化時,按照任務優先級從低到高的順序將各個邏輯任務結構體的事件隊列數組成員初始化為0,事件產生索引和事件消費索引初始化為0,然后進入主循環體。進入主循環體,在主循環體中執行任務調度程序,按照邏輯任務優先級從高到低的順序依次調用每個邏輯任務的任務處理程序,直至當前邏輯任務的優先級為最低時,退出任務調度程序,返回進入主循環體。任務調度流程如圖1所示。

圖1 任務調度流程圖
當中斷處理程序或任務處理程序向某個任務發送事件時,向該任務結構體中的事件隊列中填充事件,將事件賦值給以該任務結構體的事件產生索引為下標的事件隊列數組成員,然后將事件產生索引加1,如果索引值等于事件隊列數組的長度,將事件產生索引置0。
執行任務處理程序時,如果事件產生索引和事件消費索引兩者相等,說明事件隊列中不存在未被處理的事件,如果不相等,說明存在未被處理的事件,讀取以該任務結構體的事件消費索引為下標的事件隊列數組成員,根據具體事件執行相關操作,然后將事件消費索引加1,如果索引值等于事件隊列數組的長度,將事件消費索引置0。再次判斷事件產生索引和事件消費索引是否相等,循環處理,直到事件隊列中不再存在未被處理的事件。任務處理程序流程如圖2所示。

圖2 任務處理程序流程圖

[1] 常華利,尹震宇.基于MicroBlaze的μC/OS-II操作系統移植[J].計算機系統應用,2017(5):239-246.
[2] 陳發堂,主父文剛,童慶.Nucleus PLUS操作系統在TMS320C81 68上的移植及TD-LTE中的應用[J].廣東通信技術,2016(2):22-25,33.
[3] 山東省科學院自動化研究所.一種嵌入式軟件的任務調度方法及裝置:中國,201810128162.1[P].2018-2-8.
[4] 張智慧.C語言嵌入式系統編程軟件設計架構研究[J].單片機與嵌入式系統應用,2018(1):3-5,10.
[5] 馬建輝,王知學,李研強.車身控制系統BCM的設計與實現[J].中國科技成果,2011(13):49-51.