??? 摘? 要: 通過實例說明將事件驅(qū)動機制應(yīng)用到單片機程序中,使中斷響應(yīng)與處理程序分離,可較理想地用硬件定時代替處理程序中的軟件定時,從而大幅提高系統(tǒng)對多中斷的實時響應(yīng)能力,降低多中斷系統(tǒng)調(diào)試的難度。?
??? 關(guān)鍵詞: 事件驅(qū)動?? 單片機?? 程序設(shè)計?? 實時響應(yīng)
?
1 傳統(tǒng)單片機程序開發(fā)的不足?
??? 在傳統(tǒng)的單片機程序中,通常是以“過程”和“操作”為中心的結(jié)構(gòu),程序按規(guī)定的過程順序地執(zhí)行,與外設(shè)的連接一般采用中斷方式,在中斷服務(wù)程序中完成外設(shè)的全部處理工作,主程序一般只是初始化系統(tǒng)并等待中斷的發(fā)生。這種結(jié)構(gòu)成熟、易于理解,但有如下不足:?
??? (1)受單片機性能的限制,容易造成系統(tǒng)對其它中斷的響應(yīng)變得遲緩,特別是對于中斷源較多、中斷處理耗時較多的系統(tǒng) (如:LED顯示、鍵盤掃描等);?
??? (2)中斷服務(wù)程序過長,在中斷服務(wù)期間系統(tǒng)無法響應(yīng)同級的中斷;?
??? (3)可能導(dǎo)致代碼重入,增大堆棧開銷,造成難以預(yù)料的結(jié)果;?
??? (4)程序調(diào)試時,花在各模塊定時協(xié)調(diào)方面的時間、精力隨系統(tǒng)的復(fù)雜程度大幅增加。?
??? 如果在編寫單片機程序時,引入Windows程序中的事件驅(qū)動機制,把中斷響應(yīng)與事件處理程序分離,中斷服務(wù)程序的任務(wù)只是產(chǎn)生一個中斷發(fā)生的標志,而事件處理則由處理程序來完成,主程序則負責判斷標志和調(diào)度處理程序。這樣,可大幅縮短中斷服務(wù)程序的長度,減少中斷服務(wù)程序的耗時,提高系統(tǒng)對多中斷的響應(yīng)能力,從而較好地解決上述矛盾。?
2 Windows的事件驅(qū)動機制?
??? 在Windows系統(tǒng)中,程序的設(shè)計圍繞事件驅(qū)動來進行。當對象有相關(guān)的事件發(fā)生時(如按下鼠標鍵),對象產(chǎn)生一條特定的標識事件發(fā)生的消息,消息被送入消息隊列,或不進入隊列而直接發(fā)送給處理對象,主程序負責組織消息隊列,將消息發(fā)送給相應(yīng)的處理程序,使相應(yīng)的處理程序執(zhí)行相應(yīng)的動作,做完相應(yīng)的處理后將控制權(quán)交還給主程序。?
??? 在這種機制中,對象的請求僅僅是向隊列中添加相應(yīng)的消息,耗時的處理則被分離給處理函數(shù)。這種結(jié)構(gòu)的程序中各功能模塊界限分明,便于擴充,能充分利用CPU的處理能力,使系統(tǒng)對外界的響應(yīng)準確而及時。?
3 事件驅(qū)動的單片機程序設(shè)計?
??? 與Windows系統(tǒng)相比,單片機的資源非常有限,因此,單片機程序中的事件驅(qū)動機制只能采取一種簡化的方式。當某個中斷發(fā)生時,中斷服務(wù)程序設(shè)置相應(yīng)的標志,不同的標志代表不同的中斷發(fā)生的消息,而主程序不斷地判別這些標志,以決定啟動哪一個處理函數(shù)。相應(yīng)的處理函數(shù)被啟動處理完相關(guān)的任務(wù)后,清除此標志,然后把控制權(quán)交還給主程序。采用這種機制,可合理地利用有限資源,使程序調(diào)試的工作量大幅下降。對于延時、定時處理(如LED顯示、鍵盤掃描等),更可方便地使用一定時器來完成延時、定時的任務(wù),從而把CPU從這種耗時的任務(wù)中解放出來,確保系統(tǒng)對多中斷有足夠的響應(yīng)能力。?
??? 本文以一IC卡讀寫機為例,說明事件驅(qū)動機制在單片機程序設(shè)計中的具體應(yīng)用。?
3.1 硬件結(jié)構(gòu)?
??? 本系統(tǒng)以ATMEL公司的89C51為核心(如圖1)。89C51價格低廉,性能較好,片內(nèi)有4KB的可擦寫程序存儲器,可滿足本系統(tǒng)的要求。為簡化硬件結(jié)構(gòu)及系統(tǒng)能耗,鍵盤采用軟件掃描的矩陣鍵盤。LED顯示采用段位動態(tài)掃描,在任一時刻LED中最多只有一段被點亮。具體是在位選信號送某位LED的公共極時,每隔一個時間片依次輸出該位LED的段碼(含小數(shù)點),輸出完成一位后,再逐次輸出下一位。從第一位至第N位LED依次分成8×N個時間片循環(huán)掃描顯示。串口UART作為系統(tǒng)與外部數(shù)據(jù)通訊的通道,IC卡的讀寫由MCU模擬I2C協(xié)議來實現(xiàn)。?
?
?
3.2 事件驅(qū)動機制的單片機程序設(shè)計?
??? 中斷申請標志?
??? 在系統(tǒng)中定義一個可位尋址的單元,在此把它命名為Message_Flag,用來記錄描述中斷事件發(fā)生的情況。各位的定義如下:?
?
?
??? LED顯示的實現(xiàn)?
??? 顯示模塊結(jié)構(gòu)見圖2。以定時器T0作為LED的動態(tài)掃描的定時基準,T0的定時時間最大值Tseg=20ms/(8×N)(其中N為LED位數(shù)),改變Tseg的值可改變顯示的亮度。T0每隔Tseg時間向MCU申請中斷,在T0的中斷服務(wù)程序中置位相應(yīng)的標志位(Message_Flag中的D0位)。主程序檢測到此標志位被置位后,啟動顯示模塊實現(xiàn)位段的顯示輸出。?
?
?
??? 鍵盤輸入的實現(xiàn)?
??? 鍵盤模塊結(jié)構(gòu)見圖3。在LED動態(tài)掃描期間,只有被點亮的LED相應(yīng)的位選線維持大約3ms的低電平,而在系統(tǒng)工作的絕大部分時間內(nèi)LED的位選線(即鍵盤的列線)維持高電平。當有鍵被按下時,將把鍵盤的行線中某一根拉成高電平,經(jīng)或非門后,向MCU申請INT1中斷,在INT1的中斷服務(wù)程序中啟動定時時間為20ms的定時器T1。T1的定時時間到后向MCU申請T1中斷,在T1的中斷服務(wù)程序中置位相應(yīng)的中斷申請標志(Message_Flag中的D1位)。主程序檢測到此標志位被置位后,啟動鍵盤掃描模塊實現(xiàn)鍵盤輸入。鍵盤輸入完成(用戶按“確認”鍵),置位鍵盤輸入確認標志(Message_Flag中的D7位)。
?
?
??? IC卡的讀寫?
??? IC卡的SDA、SCL經(jīng)卡座分別通過P1.0、P1.1與MCU相連。當IC卡插入卡座時,座上的微動開關(guān)使INT0變?yōu)榈碗娖?向MCU申請INT0中斷。在INT0中斷服務(wù)程序中置位相應(yīng)的中斷申請標志(Message_Flag中的D2位),主程序檢測到此標志位被置位后,啟動IC卡的讀模塊,以軟件模擬I2C協(xié)議來實現(xiàn)讀卡操作。在數(shù)據(jù)處理完成后,同樣通過軟件模擬I2C協(xié)議來完成寫卡的操作。?
??? 串口通訊?
??? 實際應(yīng)用中可把UART轉(zhuǎn)換成RS232C與PC相連或轉(zhuǎn)換成RS485等其它協(xié)議組成單片機網(wǎng)。MCU與外部的通訊采用中斷方式,在串口的中斷服務(wù)程序中置位相應(yīng)的中斷申請標志(Message_Flag中的D4位)。主程序檢測到此標志位被置位后,啟動串口通訊模塊,實現(xiàn)與外部的數(shù)據(jù)通訊。?
??? 主程序的設(shè)計?
??? 綜上所述,主程序首先完成系統(tǒng)的初始化,然后循環(huán)檢測各中斷的中斷申請標志,如有某標志被置位,則啟動相應(yīng)的處理模塊完成相應(yīng)的任務(wù)。程序結(jié)構(gòu)如下(用C51編寫):?
??? unsigned bdata message_flag;?
??? sbit t0_int=message_flag^0;?
??? sbit t1_int=message_flag^1;?
??? sbit int0_int=message_flag^2;?
??? sbit uart_int=message_flag^4;?
??? sbit kb_enter=message_flag^7;?
??? unsigned char kb_buf[8];?
??? unsigned char led_buf[8];?
??? unsigned char ic_buf[8];?
??? unsigned char num_buf[8];?
??? void num_proc(void);????? ? /*數(shù)據(jù)處理模塊*/?
??? void ledbuf_write(unsigned ,unsigned int); /*數(shù)據(jù)處理*/?
??? void system_init(void);?? ? /*系統(tǒng)初始化*/?
??? void uart_commune(void);? ? /*串口通訊模塊*/?
??? void led_display(void);?? ? /*LED顯示*/?
??? void kb_scan(void);???? ??? /*鍵盤掃描*/?
??? void ic_reader(void);???? ? /*讀IC卡*/?
??? void ic_writer(void);???? ? /*寫IC卡*/?
??? void set_timer(unsigned int time_len,unsigned char type,unsigned char id);/*設(shè)置定時器*/?
??? void t0_int_sever(void);? ? /*定時器T0中斷服務(wù)*/?
??? void t1_int_sever(void);?? /*定時器T1中斷服務(wù)*/?
??? void int0_int_sever(void);? ??? /*INT0中斷服務(wù)*/?
??? void int1_int_sever(void);? ??? /*INT1中斷服務(wù)*/?
??? void uart_int_sever(void);? ??? /*串口中斷服務(wù)*/?
??? void main(void)?
??? {?
??????? system_init();?
? ? while(1) {?
??? ????? if? (t0_int)? ??? led_display();?
??? ????? if? (int0_int)? ? ic_reader();?
??? ????? if? (t1_int)??? ? kb_scan();?
??? ????? if? (uart_int)?? uart_commune();?
??? ????? if? (kb_enter){?
?????? ???? ?????? num_proc();?
??????? ?????????? ic_writer();?
??????? ?????????? ledbuf_write(num_buf,8);?
???? ????????? }?
???? ???? }?
??? }?
??? 事件驅(qū)動的單片機程序設(shè)計是通過在中斷服務(wù)程序中置位相應(yīng)標志,把耗時的中斷服務(wù)中的處理部分分離出來,中斷返回后,再由主程序根據(jù)標志啟動相應(yīng)的處理模塊。在任務(wù)處理完成后,清除相應(yīng)的標志。由于中斷服務(wù)程序短小,所以一般能實時地響應(yīng)各種中斷;而處理程序之間不會被相互調(diào)用,所以不會產(chǎn)生代碼重入;各模塊界限分明,給程序中各模塊的統(tǒng)調(diào)帶來很大的方便。?
??? 實踐證明,運用事件驅(qū)動機制來組織單片機程序,即使對于要求定時準、耗時多的多中斷、多模塊系統(tǒng),也可輕松地完成。?
參考文獻?
1 何立民.MCS-51系列單片機應(yīng)用系統(tǒng)配置與接口技術(shù).北京:北京航空航天大學(xué)出版社,1990?
2 馬忠梅等.單片機的C語言應(yīng)用程序設(shè)計.北京:北京航空航天大學(xué)出版社,1997.3?
3 黃維通.Visual C++面向?qū)ο笈c可視化程序設(shè)計.北京:清華大學(xué)出版社,2000.5