孔憲青
(威海職業學院,山東 威海264200)
用C 語言在Keil 軟件上進行單片機程序的編譯是比較有代表性的。并且隨著單片機小系統控制模塊越來越多,發展出了模塊化程序結構。但不可避免的是,單片機控制的模塊越多,模塊間數據交換和函數的相互調用也越復雜。傳統上的main 主函數所在文件是所以模塊調用和數據匯總的集中區域,同時main 主函數的while(1)循環要對總線模塊進行多任務的時序分配,本文通過附加一個模塊化程序Assistant.c 文件使時序分配和模塊管理分離,使之各自獨立化,提高了程序的條理和可讀性,并使改錯和程序擴展進一步簡化。
傳統的KeilC51 模塊化程序就是有一個main.c 文件和若干子模塊構成。而其中子模塊由一組*.c 和*.h 共同構成。Main.c 文件調用各自子模塊的頭文件,而子模塊頭文件里帶有可外部調用的函數和數據。這樣通過頭文件建立和主函數文件的溝通橋梁,事實上也是唯一的一個橋梁。結構見圖一,程序構成如下:
#include "h_a.h" ......
void main(void){ while(1){//模塊時序分配} }
#ifndef _H_A_H_ #define _H_A_H_
//模塊函數的外調函數
#endif
各子模塊滿足的條件是:
(1)硬件驅動模塊,一種特定硬件對應一個模塊(圖中以h 開頭的模塊);
(2)軟件功能模塊,其模塊的劃分應滿足低偶合、高內聚[1]的要求(圖中以s 開頭的模塊)。
主函數和子模塊頭文件的結構類似于圖一中右側程序。
子函數的.c 文件的結構是和mian.c 主函數文件是一致的,注意要把自身頭文件包含進去。除了外調的函數外,包括全局常變量部分,所有函數都要用static 靜態關鍵字封閉函數本體。這樣才能滿足高內聚和獨立化的要求,下面通過Ds1302 模塊說明的聲明形式:

可以看出需要外調的都是不帶static 關鍵字的,內聚函數都是靜態的,這樣也可以避免不同模塊的函數同名問題。
Main.c 文件內部while(1){……}循環體內要進行模塊的時序分配,我個人的解決方式是通過節拍控制來管理。具體是采用一個定時器來產生節拍,例如AT89S52 的16 位T2 定時器,方式是設置T2 為自動重裝,然后每50ms 產生一個節拍。這樣在程序的前臺[2]總有一個節拍來控制模塊的啟停。下面是程序段:
(1)定義節拍
char g_Beat[6]=0; //全局節拍的個數由并行模塊的數量決定
(2)設置節拍

(3)中斷方式激活節拍

(4)調用節拍控制并行模塊
while(1)
{if(g_Beat[1]>=10){g_Beat[1]=0;NecKey(); } //模塊一0.5 秒執行一次
if(g_Beat[2]>=20){g_Beat[2]=0;CalendarShow(); } //模塊二1 秒執行一次
if(g_Beat[3]>=30){g_Beat[3]=0;TimeDot(); } //模塊三1.5 秒執行一次
if(g_Beat[4]>=50){g_Beat[4]=0;Ds18b20TempN5110();} } //模 塊 四2.5 秒執行一次
節拍控制是占用一個定時器且利用中斷產生的節拍,因此把節拍設置的長一些比較好,例如50ms,同時定時器也設置為低優先級。通過節拍main 函數的并行模式不是一個真正的并行序列,因為單片機指令的執行微觀上是串行的,只有FPGA 這種器件才能實現真正的并行。但是宏觀上節拍的引入,使模塊的發生僅僅出現在時間軸的若干點上,這樣模塊的執行就相似于并行序列,而且時鐘越快,這種串行模擬越能逼近并行的工作模式。
優化是因為,main 主函數要把所有采集回來的數據進行處理,并進行時序分配,這些任務對于模塊較少時候可以,但是模塊多于6 個以上后,這種控制變得的極其繁瑣。例如做一個萬年歷的小系統,內部使用了h_N5110.c(顯示)、h_Ds18b20.c(溫度)、h_Ds1302.c(時 間/日歷)、h_Isd1420.c(放音)、s_IrNecRxd(設置通訊)、s_ChinaLunar.h (陽陰歷轉換)共6 個模塊。其中軟件模塊和硬件模塊都有,并且相互調用時候數據交互復雜,如N5110 模塊中顯示字符串這個函數就要被不同模塊中的很多函數調用。
優化的方式是建立一個Assistant.c 模塊,稱為主函數的助手模塊。這個模塊的主要任務就是把所有子模塊的外出數據和外調函數都集中到本模塊內,在這里進行匯總材料和組成被主函數main 調用的純函數(即輸入輸出都是void 的函數)。假設子模塊是把原料合成半成品的加工廠,那么Assistant 模塊就是把半成品組裝成商品的組裝廠,而main 函數就是使用這些商品的客戶,而客戶的唯一任務就是使用這些商品并分配利益即分配多任務下的總線時間,具體程序優化后的結構見圖1。



Assistant 模塊對于子模塊來說其實是個管理者。它管理和接受各個子模塊部門的數據和函數。這里注意一點就是子模塊的外調函數也是純函數,它們使用時不調用任何非自身模塊的數據和函數,即使模塊內部需要相同的某個函數例如延時,也應該設置成靜態函數。總之,子模塊是非常純粹的數據和函數包體。
而Assistant 和main 主函數的關系更像主管和秘書,它最后形成的函數必須是輸入輸出都是void 的函數,這樣才能斷開main 和子模塊的所有聯系。Assistant 模塊是一個承上啟下的一個模塊,通過它把子模塊的若干問題解決后,提供個main 若干單一純粹的函數。Assistant 的出現使并行模塊的時間分配和數據處理分離開來。使時序的問題和函數的問題解耦并各自獨立化。
子模塊構建要求它不能外調其他模塊的函數,完全靠自身的函數來實現基本驅動的功能。如果模塊內部需要調用來自外部的值,應該設置成入口參數形式和返回值形式,不能把外部輸入量直接寫入函數內部。只有子模塊內部有中斷并且在中斷中產生了數值才允許使用extern 輸入輸出中間值。Assistant 是個混合模塊,事實上它是所有模塊中最復雜的,在這個模塊中有大量的全局變量和常量,它的目標就是組合子模塊各種資源并形成純void 函數給main 調用。換句話說,優化模塊化程序設計KeilC51,集中的任務就是如何寫Assistant。Main 函數的唯一任務就是通過節拍控制Assistant 提供的函數以實現多任務并行運行,高效率的完成總線周期。
本文從KeilC51 的模塊化程序出發,介紹了在多模塊下如何組織和優化程序結構方法。其中要點是建立Asssitant 模塊使多種模塊的功能單一化。這種組織形式為類似的程序處理提供了參考。
[1][美]Michael J.Pont.C 語言嵌入式系統開發[M].中國電力出版社,2003-12-01.
[2]侯殿有.基于八位單片機的C 語言程序設計[M].北京大學出版社,2012-09-01