楊一萌,楊勇,楊遠(yuǎn)聰
(中國地質(zhì)大學(xué) 數(shù)學(xué)與物理學(xué)院,武漢 430074)
?
Protothreads在提高系統(tǒng)響應(yīng)方面的應(yīng)用*
楊一萌,楊勇,楊遠(yuǎn)聰
(中國地質(zhì)大學(xué) 數(shù)學(xué)與物理學(xué)院,武漢 430074)
采用基于Protothreads的輕量靈活的多任務(wù)編程方式,使資源緊張的小型微控制器可支持多任務(wù),改善了系統(tǒng)綜合性能。測試證明,運(yùn)用該技術(shù)后C51系統(tǒng)對按鍵響應(yīng)的速度最高提高了10倍。該方法為在小型微控制器上運(yùn)行多任務(wù)系統(tǒng)提供了一種新的思路。
Protothreads;多任務(wù);低延遲;STC90C516RD+
基于微控制器的儀器設(shè)備在進(jìn)行數(shù)據(jù)采集時,往往還需要支持通信、顯示和按鍵等,這要求微控制器能夠在短時間內(nèi)處理多個任務(wù),并且具有較好的實(shí)時性。Free RTOS和μC/OS II等完整的嵌入式操作系統(tǒng),往往需要較多的硬件資源,對于小型微控制器來說,其過于龐大。許多應(yīng)用既希望采用輕巧價廉的微控制器,又希望響應(yīng)迅速,尤其是航天和交通等系統(tǒng)更是如此[1]。這種情況下,需根據(jù)任務(wù)和微控制器的特點(diǎn)來設(shè)計編寫控制程序,才能充分發(fā)揮微控制器的性能。在輕量級多任務(wù)框架中,由Protothreads衍生出了各具特點(diǎn)的修改版本[2],但對某些微控制器來說仍然有些復(fù)雜,使用受限。本文結(jié)合已有的采集系統(tǒng),嘗試使用簡單的Protothreads框架來提升系統(tǒng)實(shí)時性能,并評估Protothreads框架在資源緊張的多任務(wù)嵌入式系統(tǒng)中的可行性。
Protothreads由瑞典皇家理工學(xué)院的Adam Dunkels博士開發(fā),在BSD許可證下發(fā)行[3]。它是一個非常輕量的協(xié)程庫,被應(yīng)用到很多開源軟件中,例如嵌入式網(wǎng)絡(luò)操作系統(tǒng)Contiki、TCP/IP協(xié)議棧μIP和LwIP等。
1.1Protothreads特點(diǎn)及基本函數(shù)
Protothreads具有如下優(yōu)點(diǎn):①完全由C語言實(shí)現(xiàn),沒有匯編代碼,不依賴任何庫和系統(tǒng)特性,在任何平臺都可移植;②極少的資源需求,每個Protothreads函數(shù)僅需要2個字節(jié);③支持條件阻塞機(jī)制和協(xié)程間通信等功能;④不存在調(diào)用開銷,沒有棧切換,系統(tǒng)資源占用極少。
Protothreads十分輕量的特點(diǎn)使它非常適合用于內(nèi)存受限系統(tǒng)、事件驅(qū)動協(xié)議棧、深度嵌入式系統(tǒng)和傳感器網(wǎng)絡(luò)節(jié)點(diǎn)等場景[4]。Protothreads進(jìn)入阻塞狀態(tài)時,不保存堆棧和局部變量,這意味著,在使用Protothreads的系統(tǒng)中要謹(jǐn)慎對待本地變量,如果不確定是否可行,就使用全局變量[5]。
1.2用于定義協(xié)程的函數(shù)主體
Protothreads協(xié)程由4種基本操作組成,初始化:PT_INIT();執(zhí)行:PT_BEGIN();條件阻塞:PT_WAIT_UNTIL(),PT_WAIT_WHILE();退出:PT_END()。
這4個基本操作函數(shù)均是宏,在代碼預(yù)編譯階段會展開為實(shí)際代碼。每一次發(fā)生調(diào)用時,Protothreads將一直運(yùn)行,直到它主動進(jìn)入阻塞狀態(tài)或退出。因此,由使用Protothreads的應(yīng)用程序完成Protothreads的調(diào)度[5]。
Protothreads使用pt.lc記錄阻塞位置,協(xié)程剛初始化和任務(wù)結(jié)束時,pt.lc均等于0。
該數(shù)據(jù)采集系統(tǒng)主要用于電流檢測,結(jié)構(gòu)圖如圖1所示。
圖1 采樣系統(tǒng)基本結(jié)構(gòu)
微控制器使用STC90C516RD+,系統(tǒng)主要任務(wù)有A/D轉(zhuǎn)換和數(shù)據(jù)處理、結(jié)果顯示、量程切換、PC通信、菜單處理、快捷按鍵處理和數(shù)據(jù)記錄。A/D轉(zhuǎn)換使用AD7710芯片,可以設(shè)置10 Hz或50 Hz陷波頻率。不同陷波頻率下的轉(zhuǎn)換速度不同,單片機(jī)對轉(zhuǎn)換結(jié)果進(jìn)一步處理。菜單鍵按下會觸發(fā)外部中斷,中斷函數(shù)置位標(biāo)志位,然后在主函數(shù)中處理該事件。
2.1存在的問題
經(jīng)過后期測試發(fā)現(xiàn),單片機(jī)大部分時間花費(fèi)在A/D獲取數(shù)據(jù)及處理這個部分。在不同的設(shè)置下,花費(fèi)的時間在20~4 000 ms。由于系統(tǒng)任務(wù)是順序執(zhí)行,導(dǎo)致其他任務(wù)響應(yīng)很慢,最壞的情況下,按下菜單鍵后幾秒鐘后屏幕才會顯示出來。
如圖2描述的情況,在任務(wù)A執(zhí)行過程中,黑色標(biāo)記處相繼發(fā)生了任務(wù)B、C對應(yīng)的事件,發(fā)生事件后進(jìn)入相應(yīng)中斷,置位相應(yīng)標(biāo)志位,但是要等到任務(wù)A結(jié)束之后才會執(zhí)行任務(wù)B、C。
圖2 CPU使用情況
2.2解決方法
A/D轉(zhuǎn)換和數(shù)據(jù)處理任務(wù)使用Protothreads協(xié)程,其他任務(wù)不變。A/D轉(zhuǎn)換和數(shù)據(jù)處理任務(wù),下文均稱作任務(wù)A。任務(wù)A每運(yùn)行200 ms就會主動進(jìn)入阻塞狀態(tài),讓出CPU使用權(quán),然后檢查是否有事件需要處理,相關(guān)任務(wù)處理完成后,任務(wù)A獲得CPU使用權(quán),繼續(xù)執(zhí)行。任務(wù)A每次停留在阻塞狀態(tài)的時間是不固定的,例如,當(dāng)把保存的數(shù)據(jù)從儀器發(fā)送到上位機(jī)時,任務(wù)A在發(fā)送完畢前將一直處于阻塞狀態(tài)。改善后的CPU的使用情況如圖3所示。
圖3 改善后CPU使用情況
圖4描述了任務(wù)A的內(nèi)部流程。任務(wù)A主要由采集數(shù)據(jù)和處理數(shù)據(jù)兩個子任務(wù)構(gòu)成。這兩個子任務(wù)都不是一次性完成的,每次運(yùn)行一小部分,然后阻塞任務(wù)A,讓出CPU使用權(quán)。如圖4所示,任務(wù)A運(yùn)行后,首先根據(jù)pt.lc的值判斷運(yùn)行狀態(tài),如果是從阻塞態(tài)恢復(fù),將回到上次結(jié)束位置繼續(xù)運(yùn)行。當(dāng)pt.lc值對應(yīng)采集任務(wù)程序塊(行號在采集任務(wù)程序塊)時,進(jìn)入采集任務(wù);當(dāng)pt.lc值對應(yīng)數(shù)據(jù)處理程序塊(行號在數(shù)據(jù)處理程序塊)時,進(jìn)入數(shù)據(jù)處理任務(wù)。采集數(shù)據(jù)和處理數(shù)據(jù)兩個子任務(wù)按順序先后運(yùn)行,子任務(wù)運(yùn)行超過200 ms后,阻塞任務(wù)A。當(dāng)數(shù)據(jù)處理結(jié)束后,任務(wù)A結(jié)束。
圖4 任務(wù)A流程圖
系統(tǒng)中使用定時器計時,每10 ms觸發(fā)定時中斷,作為系統(tǒng)的TickClock,變量sclk記錄中斷次數(shù)。當(dāng)sclk等于20時,表示至少已經(jīng)過去了200 ms。程序如下:
void Timer0_ISR() interrupt 1{
sclk++;
}
使用PT_THREAD()宏聲明任務(wù)A的主體函數(shù)。當(dāng)從A/D轉(zhuǎn)換器中讀出一個轉(zhuǎn)換結(jié)果后檢測sclk的值,當(dāng)sclk大于等于20時,主動阻塞自己,讓出CPU使用權(quán)。程序如下:
PT_THREAD(dataobj_acquire(dataobj *dataobj, struct pt *pt)){
//變量聲明及初始化…
sclk=0;//重置計時
PT_BEGIN(pt);
if (dataobj->number_to_average >= 4){
for (i = dataobj_raw_data_start_addr; i < dataobj->raw_data_end_addr;){
AD7710Read();
/*如果已經(jīng)運(yùn)行至少200 ms,阻塞自己,退出本函數(shù)。當(dāng)再進(jìn)入本函數(shù)時,從當(dāng)前位置繼續(xù)執(zhí)行*/
PT_WAIT_WHILE(pt, (sclk > 20));
}
數(shù)據(jù)處理……
PT_END(pt);
}
在主函數(shù)中,通過pt.lc的值是否為0來判斷任務(wù)A是否已經(jīng)執(zhí)行完成。某些任務(wù),比如顯示數(shù)據(jù)只有在任務(wù)A正常結(jié)束后才能刷新。某些任務(wù)執(zhí)行后會改變系統(tǒng)當(dāng)前設(shè)置,已經(jīng)采集到的緩存數(shù)據(jù)需要重新開始采集,可以通過PT_INIT()宏重置任務(wù)A。程序如下:
void main(){
變量聲明及初始化…
PT_INIT(&data_acquire_pt);
模塊初始化…
while (1){
if (deviceobj->key_isr_happen){
KeyInterruptProc(dataobj, deviceobj, uartobj);
deviceobj->key_isr_happen = 0;
/*重置任務(wù)A,使其從頭開始執(zhí)行*/
PT_INIT(&data_acquire_pt);
}
dataobj_acquire (dataobj, deviceobj, &data_acquire_pt);
if (data_acquire_pt.lc == 0){
RangeCheckProc(dataobj, deviceobj, uartobj);
}
if (deviceobj->range_changed){
deviceobj->range_changed = 0;
/*重置任務(wù)A,使其從頭開始執(zhí)行*/
PT_INIT(&data_acquire_pt);
dataobj_acquire (dataobj, deviceobj, &data_acquire_pt);
}
if (deviceobj->over_range == 0 && data_acquire_pt.lc == 0){
LCDDisplay (dataobj);
}
if (deviceobj->autotest_run && data_acquire_pt.lc == 0){
AutoTestProc(&tmp_addr, &toi2c_num, dataobj, deviceobj, uartobj);
}
uart_loop:
if (uartobj->command_received){
EX0=0;
UARTProcessComm(dataobj, deviceobj, uartobj, &data_acquire_pt);
EX0=1;
}
if (uartobj->uartloop_flag){ goto uart_loop;}
if (QuicKeyProc(dataobj, deviceobj, uartobj)){
/*重置任務(wù)A,使其從頭開始執(zhí)行*/
PT_INIT(&data_acquire_pt);
}
}
}
根據(jù)不同的設(shè)置,在沒有事件發(fā)生時,任務(wù)A總的執(zhí)行時間在20~4 000 ms之間,使用Protothreads之后總的執(zhí)行時間并沒有明顯增加。在沒有使用Protothreads協(xié)程框架之前,按下按鍵使按鍵標(biāo)志位置位后,要等待較長時間才能獲得響應(yīng)。使用框架后,從最嚴(yán)重的延遲3 060 ms下降到270 ms。比較結(jié)果見表1。
表1 使用Protothreads前后結(jié)果比較
每個Protothreads協(xié)程僅需要2字節(jié)的存儲空間,使用協(xié)程的函數(shù)內(nèi)的局部變量全部要改為靜態(tài)變量。任務(wù)A相比之前增加20字節(jié)的內(nèi)存占用,程序大小增加1 KB左右。
本文分析了數(shù)據(jù)采集系統(tǒng)中存在任務(wù)響應(yīng)不及時的問題,并根據(jù)其使用的微控制器資源緊缺和采集系統(tǒng)的特點(diǎn),提出了使用Protothreads來實(shí)現(xiàn)多任務(wù)的編程方式;簡要介紹了Protothreads基本功能,詳細(xì)闡述了改進(jìn)系統(tǒng)響應(yīng)性能的實(shí)現(xiàn)方法。結(jié)合改進(jìn)前后的數(shù)據(jù),經(jīng)過對比發(fā)現(xiàn),該方法可以明顯提升系統(tǒng)性能,并且沒有明顯增加內(nèi)存和程序空間占用,對于更復(fù)雜的系統(tǒng)需求,可以根據(jù)情況設(shè)計一個調(diào)度程序。本文對于其他嵌入式軟件開發(fā)具有較高的參考價值。
[1] 榮國平,劉天宇,謝明娟,等.嵌入式系統(tǒng)開發(fā)中敏捷方法的應(yīng)用研究綜述[J].軟件學(xué)報,2014(2):267-283.
[2] 樓亮亮,周苗,鮑星合.一種適用于物聯(lián)網(wǎng)節(jié)點(diǎn)的高效輕量級嵌入式系統(tǒng)設(shè)計[J].單片機(jī)與嵌入式系統(tǒng)應(yīng)用,2014(11):67-70.
[3] Dunkels A,Schmidt O,Voigt T,et al.Protothreads: Simplifying event-driven programming of memory-constrained embedded systems[C]//Proceedings of the Fourth ACM Conference on Embedded Networked Sensor Systems,2006:29-42.
[4] Dunkels A,Schmidt O.Protothreads-lightweight stackless threads in C [EB/OL].[2016-03].http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.60.2455&rep=repl&type=pdf.
[5] 閆石,馬潮.時間觸發(fā)模式下的Protothreads設(shè)計應(yīng)用[J].單片機(jī)與嵌入式系統(tǒng)應(yīng)用,2009(1):15-17.
楊一萌(研究生),研究方向?yàn)楣怆娦盘枡z測與嵌入式系統(tǒng)應(yīng)用;楊勇(教授),主要從事微弱信號檢測相關(guān)工作;楊遠(yuǎn)聰(研究生),研究方向?yàn)楣怆娦盘枡z測。
(責(zé)任編輯:薛士然收修改稿日期:2016-03-26)
Protothreads Application in Terms of Improving System Response
Yang Yimeng,Yang Yong,Yang Yuancong
(China University of Geosciences,Wuhan 430074,China)
The resource intensive small microcontroller can support multitasking by using the lightweight flexible multitask programming based on Protothreads,that improves the performance of the system.The experiment results show that the method can speed up 10 times maximum for the button response on a C51 system obviously.The method provides a new idea for running multitask on small microcontroller.
Protothreads;multitask;low-latency;STC90C516RD+
TP311
A