?? 在51系列單片機上移植ucos.htm
字號:
;指針占3字節(jié)。+0類型+1高8位數(shù)據(jù)+2低8位數(shù)據(jù)<BR>MOVX A,@DPTR ;.OSTCBStkPtr是void指針<BR>MOV
R0,A<BR>INC DPTR<BR>MOVX A,@DPTR<BR>MOV R1,A<BR>MOV DPH,R0<BR>MOV
DPL,R1<BR><BR>;*UserStkPtr ===> R5 用戶堆棧起始地址內(nèi)容(即用戶堆棧長度放在此處) 詳見文檔說明
指針用法詳見C51.PDF第178頁 <BR>MOVX A,@DPTR ;用戶堆棧中是unsigned char類型數(shù)據(jù)<BR>MOV R5,A
;R5=用戶堆棧長度<BR><BR>;恢復現(xiàn)場堆棧內(nèi)容<BR>MOV
R0,#OSStkStart<BR><BR>restore_stack:<BR><BR>INC DPTR<BR>INC R0<BR>MOVX
A,@DPTR<BR>MOV @R0,A<BR>DJNZ R5,restore_stack<BR><BR>;恢復堆棧指針SP<BR>MOV
SP,R0<BR><BR>;OSRunning=TRUE<BR>MOV R0,#LOW (OSRunning)<BR>MOV
@R0,#01<BR><BR>POPALL<BR>SETB EA
;開中斷<BR>RETI<BR>;-------------------------------------------------------------------------<BR>RSEG
?PR?OSCtxSw?OS_CPU_A<BR>OSCtxSw:
<BR>PUSHALL<BR><BR>OSIntCtxSw_in:<BR><BR>;獲得堆棧長度和起址<BR>MOV A,SP<BR>CLR
C<BR>SUBB A,#OSStkStart<BR>MOV R5,A ;獲得堆棧長度 <BR><BR>;OSTCBCur ===> DPTR
獲得當前TCB指針,詳見C51.PDF第178頁<BR>MOV R0,#LOW (OSTCBCur)
;獲得OSTCBCur指針低地址,指針占3字節(jié)。+0類型+1高8位數(shù)據(jù)+2低8位數(shù)據(jù)<BR>INC R0<BR>MOV DPH,@R0
;全局變量OSTCBCur在IDATA中<BR>INC R0<BR>MOV
DPL,@R0<BR><BR>;OSTCBCur->OSTCBStkPtr ===> DPTR 獲得用戶堆棧指針<BR>INC DPTR
;指針占3字節(jié)。+0類型+1高8位數(shù)據(jù)+2低8位數(shù)據(jù)<BR>MOVX A,@DPTR ;.OSTCBStkPtr是void指針<BR>MOV
R0,A<BR>INC DPTR<BR>MOVX A,@DPTR<BR>MOV R1,A<BR>MOV DPH,R0<BR>MOV
DPL,R1<BR><BR>;保存堆棧長度<BR>MOV A,R5<BR>MOVX @DPTR,A<BR><BR>MOV
R0,#OSStkStart ;獲得堆棧起址<BR>save_stack:<BR><BR>INC DPTR<BR>INC R0<BR>MOV
A,@R0<BR>MOVX @DPTR,A<BR>DJNZ R5,save_stack<BR><BR>;調(diào)用用戶程序<BR>LCALL
_?OSTaskSwHook<BR><BR>;OSTCBCur = OSTCBHighRdy<BR>MOV R0,#OSTCBCur<BR>MOV
R1,#OSTCBHighRdy<BR>MOV A,@R1<BR>MOV @R0,A<BR>INC R0<BR>INC R1<BR>MOV
A,@R1<BR>MOV @R0,A<BR>INC R0<BR>INC R1<BR>MOV A,@R1<BR>MOV
@R0,A<BR><BR>;OSPrioCur = OSPrioHighRdy
使用這兩個變量主要目的是為了使指針比較變?yōu)樽止?jié)比較,以便節(jié)省時間。<BR>MOV R0,#OSPrioCur<BR>MOV
R1,#OSPrioHighRdy<BR>MOV A,@R1<BR>MOV @R0,A<BR><BR>LJMP
OSCtxSw_in<BR>;-------------------------------------------------------------------------<BR>RSEG
?PR?OSIntCtxSw?OS_CPU_A<BR><BR>OSIntCtxSw:</FONT></P>
<P><FONT
color=#000080>;調(diào)整SP指針去掉在調(diào)用OSIntExit(),OSIntCtxSw()過程中壓入堆棧的多余內(nèi)容<BR>;SP=SP-4</FONT></P>
<P><FONT color=#000080>MOV A,SP<BR>CLR C<BR>SUBB A,#4<BR>MOV
SP,A<BR><BR>LJMP
OSIntCtxSw_in<BR>;-------------------------------------------------------------------------<BR>CSEG
AT 000BH ;OSTickISR<BR>LJMP OSTickISR ;使用定時器0<BR>RSEG
?PR?OSTickISR?OS_CPU_A</FONT></P>
<P><FONT color=#000080>OSTickISR: <BR><BR>USING 0 <BR>PUSHALL<BR><BR>CLR
TR0<BR>MOV TH0,#70H ;定義Tick=50次/秒(即0.02秒/次)<BR>MOV TL0,#00H ;OS_CPU_C.C 和
OS_TICKS_PER_SEC<BR>SETB TR0<BR><BR>LCALL _?OSIntEnter<BR>LCALL
_?OSTimeTick<BR>LCALL _?OSIntExit<BR>POPALL
<BR>RETI<BR>;-------------------------------------------------------------------------<BR>CSEG
AT 0023H ;串口中斷<BR>LJMP SerialISR ;工作于系統(tǒng)態(tài),無任務切換。<BR>RSEG
?PR?_?serial?OS_CPU_A<BR><BR>SerialISR:<BR><BR>USING 0 <BR>PUSHALL<BR>CLR
EA<BR>LCALL _?serial <BR>SETB EA<BR>POPALL
<BR>RETI<BR>;-------------------------------------------------------------------------<BR>END<BR>;-------------------------------------------------------------------------</FONT></P>
<P><FONT color=#000080>文件名 : OS_CPU_C.C</FONT></P>
<P><FONT color=#000080>void *OSTaskStkInit (void (*task)(void *pd), void
*ppdata, void *ptos, INT16U opt) reentrant<BR>{ <BR>OS_STK
*stk;</FONT></P>
<P><FONT color=#000080>ppdata = ppdata;<BR>opt = opt;
//opt沒被用到,保留此語句防止告警產(chǎn)生 <BR>stk = (OS_STK *)ptos; //用戶堆棧最低有效地址<BR>*stk++ =
15; //用戶堆棧長度<BR>*stk++ = (INT16U)task & 0xFF; //任務地址低8位<BR>*stk++ =
(INT16U)task >> 8; //任務地址高8位 <BR>*stk++ = 0x00; //PSW<BR>*stk++ =
0x0A; //ACC<BR>*stk++ = 0x0B; //B<BR>*stk++ = 0x00; //DPL<BR>*stk++ =
0x00; //DPH<BR>*stk++ = 0x00; //R0<BR>*stk++ = 0x01; //R1<BR>*stk++ =
0x02; //R2<BR>*stk++ = 0x03; //R3<BR>*stk++ = 0x04; //R4<BR>*stk++ = 0x05;
//R5<BR>*stk++ = 0x06; //R6<BR>*stk++ = 0x07;
//R7<BR>//不用保存SP,任務切換時根據(jù)用戶堆棧長度計算得出。 <BR>return ((void
*)ptos);<BR>}</FONT></P>
<P><FONT color=#000080>#if OS_CPU_HOOKS_EN<BR>void OSTaskCreateHook
(OS_TCB *ptcb) reentrant<BR>{<BR>ptcb = ptcb; /* Prevent compiler warning
*/<BR>}</FONT></P>
<P><FONT color=#000080>void OSTaskDelHook (OS_TCB *ptcb)
reentrant<BR>{<BR>ptcb = ptcb; /* Prevent compiler warning
*/<BR>}</FONT></P>
<P><FONT color=#000080>void OSTimeTickHook (void)
reentrant<BR>{<BR>}<BR>#endif</FONT></P>
<P><FONT color=#000080>//初始化定時器0<BR>void InitTimer0(void)
reentrant<BR>{<BR>TMOD=TMOD&0xF0;<BR>TMOD=TMOD|0x01;
//模式1(16位定時器),僅受TR0控制<BR>TH0=0x70; //定義Tick=50次/秒(即0.02秒/次)<BR>TL0=0x00;
//OS_CPU_A.ASM 和 OS_TICKS_PER_SEC<BR>ET0=1; //允許T0中斷<BR>TR0=1;
<BR>}</FONT></P>
<P><FONT color=#000080>文件名 : YY.C</FONT></P>
<P><FONT color=#000080>#include <INCLUDES.H></FONT></P>
<P><FONT color=#000080>#define MAX_STK_SIZE 64</FONT></P>
<P><FONT color=#000080>void TaskStartyya(void *yydata) reentrant;<BR>void
TaskStartyyb(void *yydata) reentrant;<BR>void TaskStartyyc(void *yydata)
reentrant;</FONT></P>
<P><FONT color=#000080>OS_STK
TaskStartStkyya[MAX_STK_SIZE+1];//注意:我在ASM文件中設置?STACK空間為40H即64,不要超出范圍。<BR>OS_STK
TaskStartStkyyb[MAX_STK_SIZE+1];//用戶棧多一個字節(jié)存長度<BR>OS_STK
TaskStartStkyyc[MAX_STK_SIZE+1];</FONT></P>
<P><FONT color=#000080>void
main(void)<BR>{<BR>OSInit();<BR><BR>InitTimer0();<BR>InitSerial();<BR>InitSerialBuffer();<BR><BR>OSTaskCreate(TaskStartyya,
(void *)0, &TaskStartStkyya[0],2);<BR>OSTaskCreate(TaskStartyyb, (void
*)0, &TaskStartStkyyb[0],3);<BR>OSTaskCreate(TaskStartyyc, (void *)0,
&TaskStartStkyyc[0],4);<BR><BR>OSStart();<BR>}</FONT></P>
<P><FONT color=#000080><BR>void TaskStartyya(void *yydata)
reentrant<BR>{<BR>yydata=yydata;<BR>clrscr();<BR>PrintStr("\n\t\t*******************************\n");<BR>PrintStr("\t\t*
Hello! The world.
*\n");<BR>PrintStr("\t\t*******************************\n\n\n");<BR><BR>for(;;){<BR>PrintStr("\tAAAAAA111111
is active.\n");<BR>OSTimeDly(OS_TICKS_PER_SEC); <BR>} <BR>}</FONT></P>
<P><FONT color=#000080>void TaskStartyyb(void *yydata)
reentrant<BR>{<BR>yydata=yydata;
<BR><BR>for(;;){<BR>PrintStr("\tBBBBBB333333 is
active.\n");<BR>OSTimeDly(3*OS_TICKS_PER_SEC); <BR>} <BR>}</FONT></P>
<P><FONT color=#000080>void TaskStartyyc(void *yydata)
reentrant<BR>{<BR>yydata=yydata;
<BR><BR>for(;;){<BR>PrintStr("\tCCCCCC666666 is
active.\n");<BR>OSTimeDly(6*OS_TICKS_PER_SEC); <BR>} <BR>}</FONT></P>
<P><FONT
color=#000080>重入問題的解決:<BR>任務函數(shù)中帶有形參和局部變量時若使用reentrant關鍵字會引起重入,從C51.PDF
129-131頁的內(nèi)容知:為了函數(shù)重入,形參和局部變量必須保存在堆棧里,由于51硬件堆棧太小,KEIL將根據(jù)內(nèi)存模式在相應內(nèi)存空間仿真堆棧(生長方向由上向下,與硬件棧相反)。對于大模式編譯,函數(shù)返回地址保存在硬件堆棧里,形參和局部變量放在仿真堆棧中,棧指針為?C_XBP,XBPSTACK=1時,起始值在startup.a51中初始化為FFFFH+1。仿真堆棧效率低下,KEIL建議盡量不用,但為了重入操作必須使用。KEIL可以混合使用3種仿真堆棧(大、中、小模式),為了提高效率,針對51推薦統(tǒng)一使用大模式編譯。<BR>為了支持重入,重新設計了堆棧結構(如下圖)。增加了保存仿真堆棧指針?C_XBP和堆棧內(nèi)容的數(shù)據(jù)結構。相應改變的文件有:OS_CPU_A.ASM、OS_CPU_C.C、OS_CPU.H、YY.C。由圖可知,用戶棧中保存的仿真棧與硬件棧相向生長,中間為空閑間隔,顯然uCOSII的堆棧檢測函數(shù)失效。硬件棧的保存恢復詳見上節(jié),仿真堆棧的保存與8086移植中的一樣,OS只提供堆??臻g和只操作堆棧指針,不進行內(nèi)存拷貝,效率相對很高。<BR>建議使用統(tǒng)一的固定大小的堆??臻g,盡管uCOSII原作者把不同任務使用不同空間看成是優(yōu)點,但為了在51上有效實現(xiàn)任務重入,針對51筆者還是堅持不使用這個優(yōu)點。<BR>用戶堆??臻g的大小是可以精確計算出來的。用戶堆??臻g=硬件堆棧空間+仿真堆??臻g。硬件棧占用內(nèi)部RAM,內(nèi)部RAM執(zhí)行效率高,如果堆??臻g過大,會影響KEIL編譯的程序性能。如果堆??臻g小,在中斷嵌套和程序調(diào)用時會造成系統(tǒng)崩潰。綜合考慮,我把硬件堆??臻g大小定成了64字節(jié),用戶根據(jù)實際情況可以自行設定。仿真堆棧大小取決于形參和局部變量的類型及數(shù)量,可以精確算出。因為所有用戶棧使用相同空間大小,所以取占用空間最大的任務函數(shù)的空間大小為仿真堆棧空間大小。這樣用戶堆??臻g大小就唯一確定了。我將用戶堆棧空間大小用宏定義在OS_CFG.H文件中,宏名為MaxStkSize。<BR>51的SP只有8位,無法在64K空間中自由移動,只好采用拷貝全部硬件堆棧內(nèi)容的笨辦法。51
本來就弱,這么一來缺點更明顯了。其實,引入OS必然要付出代價,一般OS要占用CPU10%-20%的負荷能力,請權衡利弊決定。切換頻率決定了CPU的耗費,頻率越高耗費越大,大到一定程度就該換更強的CPU了。我選了50Hz的切換頻率,不高也不低,用戶可以根據(jù)需要自行定奪。在耗費無法避免的情況下,我采取了幾個措施來提高效率:1。ret和reti混用減少代碼;2。IE、SP不入出棧,通過另外方式解決;3。用IDATA關鍵字聲明在匯編中用到的全局變量,變DPTR操作為Ri操作;4。設計堆棧結構,簡化算法;5。讓串口輸入輸出工作在系統(tǒng)態(tài),不占用任務TCB和優(yōu)先級,增加彈性緩沖區(qū),減少等待。</FONT></P>
<P><FONT
color=#000080>在51單片機上硬件仿真uCOS51的說明:<BR>zyware網(wǎng)友2002/11/22來信詢問uCOS51在單片機上的硬件仿真問題,具體情況是“在51上用uCOS51核,以及一些構件,keilc上仿真通過,用wave接硬件仿真程序亂飛,wave仿真以前的程序沒有問題,不知是何緣故”。<BR>由于我的OS程序已經(jīng)在KEIL軟件仿真和硬件上實際測試過,所以不可能是程序錯??赡艿脑蛑荒苁怯布抡孳浖O置問題。本人用的是Medwin軟件,在Insight上調(diào)試,使用uCOS51編譯測試程序一樣跑飛。即使添加修改后的startup.a51(詳見《在51單片機上固化uCOS51的說明》)也不正常。我發(fā)現(xiàn)Medwin似乎沒有編譯startup.a51,因為它把該文件加在了other
Files目錄下而不是source Files目錄,于是我猜測只有放在source
Files目錄下的文件才被編譯。由觀察知,以.c和.asm做后綴的文件均被放在此目錄下且被編譯。于是我立即將startup.a51改成startup.asm并加入項目編譯,結果測試正常。不必擔心startup改名造成沖突,KEIL在鏈接目標文件時會自動處理重名段,本目錄的文件優(yōu)先級高(我是這么理解的,具體原理不清楚,這只是根據(jù)實踐得到的結論,希望了解此處理過程的朋友能告之,不勝感激。)。<BR><BR>具體做法如下:<BR>1。按《在51單片機上固化uCOS51的說明》一文修改startup.a51,并將其更名為startup.asm。<BR>2。將startup.asm、yy1.c、os_cpu_c.c、ucos_ii.c、os_cpu_a.asm五個文件加入項目編譯。<BR>3。運行</FONT></P>
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -