uC/OS-II在51單片機上的移植2
#i nclude
#define MAX_STK_SIZE 64
void TaskStartyya(void *yydata) reentrant;
void TaskStartyyb(void *yydata) reentrant;
void TaskStartyyc(void *yydata) reentrant;
OS_STK TaskStartStkyya[MAX_STK_SIZE+1];//注意:我在ASM文件中設置?STACK空間為40H即64,不要超出范圍。
OS_STK TaskStartStkyyb[MAX_STK_SIZE+1];//用戶棧多一個字節存長度
OS_STK TaskStartStkyyc[MAX_STK_SIZE+1];
void main(void)
{
OSInit();
InitTimer0();
InitSerial();
InitSerialBuffer();
OSTaskCreate(TaskStartyya, (void *)0, &TaskStartStkyya[0],2);
OSTaskCreate(TaskStartyyb, (void *)0, &TaskStartStkyyb[0],3);
OSTaskCreate(TaskStartyyc, (void *)0, &TaskStartStkyyc[0],4);
OSStart();
}
void TaskStartyya(void *yydata) reentrant
{
yydata=yydata;
clrscr();
PrintStr("ntt*******************************n");
PrintStr("tt* Hello! The world. *n");
PrintStr("tt*******************************nnn");
for(;;){
PrintStr("tAAAAAA111111 is active.n");
OSTimeDly(OS_TICKS_PER_SEC);
}
}
void TaskStartyyb(void *yydata) reentrant
{
yydata=yydata;
for(;;){
PrintStr("tBBBBBB333333 is active.n");
OSTimeDly(3*OS_TICKS_PER_SEC);
}
}
void TaskStartyyc(void *yydata) reentrant
{
yydata=yydata;
for(;;){
PrintStr("tCCCCCC666666 is active.n");
OSTimeDly(6*OS_TICKS_PER_SEC);
}
}
重入問題的解決:
任務函數中帶有形參和局部變量時若使用reentrant關鍵字會引起重入,從C51.PDF 129-131頁的內容知:為了函數重入,形參和局部變量必須保存在堆棧里,由于51硬件堆棧太小,KEIL將根據內存模式在相應內存空間仿真堆棧(生長方向由上向下,與硬件棧相反)。對于大模式編譯,函數返回地址保存在硬件堆棧里,形參和局部變量放在仿真堆棧中,棧指針為?C_XBP,XBPSTACK=1時,起始值在startup.a51中初始化為FFFFH+1。仿真堆棧效率低下,KEIL建議盡量不用,但為了重入操作必須使用。KEIL可以混合使用3種仿真堆棧(大、中、小模式),為了提高效率,針對51推薦統一使用大模式編譯。
為了支持重入,重新設計了堆棧結構(如下圖)。增加了保存仿真堆棧指針?C_XBP和堆棧內容的數據結構。相應改變的文件有:OS_CPU_A.ASM、OS_CPU_C.C、OS_CPU.H、YY.C。由圖可知,用戶棧中保存的仿真棧與硬件棧相向生長,中間為空閑間隔,顯然uCOSII的堆棧檢測函數失效。硬件棧的保存恢復詳見上節,仿真堆棧的保存與8086移植中的一樣,OS只提供堆棧空間和只操作堆棧指針,不進行內存拷貝,效率相對很高。
建議使用統一的固定大小的堆棧空間,盡管uCOSII原作者把不同任務使用不同空間看成是優點,但為了在51上有效實現任務重入,針對51筆者還是堅持不使用這個優點。
用戶堆棧空間的大小是可以精確計算出來的。用戶堆棧空間=硬件堆棧空間+仿真堆棧空間。硬件棧占用內部RAM,內部RAM執行效率高,如果堆棧空間過大,會影響KEIL編譯的程序性能。如果堆棧空間小,在中斷嵌套和程序調用時會造成系統崩潰。綜合考慮,我把硬件堆棧空間大小定成了64字節,用戶根據實際情況可以自行設定。仿真堆棧大小取決于形參和局部變量的類型及數量,可以精確算出。因為所有用戶棧使用相同空間大小,所以取占用空間最大的任務函數的空間大小為仿真堆棧空間大小。這樣用戶堆棧空間大小就唯一確定了。我將用戶堆棧空間大小用宏定義在OS_CFG.H文件中,宏名為MaxStkSize。
51的SP只有8位,無法在64K空間中自由移動,只好采用拷貝全部硬件堆棧內容的笨辦法。51 本來就弱,這么一來缺點更明顯了。其實,引入OS必然要付出代價,一般OS要占用CPU10%-20%的負荷能力,請權衡利弊決定。切換頻率決定了CPU的耗費,頻率越高耗費越大,大到一定程度就該換更強的CPU了。我選了50Hz的切換頻率,不高也不低,用戶可以根據需要自行定奪。在耗費無法避免的情況下,我采取了幾個措施來提高效率:1。ret和reti混用減少代碼;2。IE、SP不入出棧,通過另外方式解決;3。用IDATA關鍵字聲明在匯編中用到的全局變量,變DPTR操作為Ri操作;4。設計堆棧結構,簡化算法;5。讓串口輸入輸出工作在系統態,不占用任務TCB和優先級,增加彈性緩沖區,減少等待。
在51單片機上硬件仿真uCOS51的說明:
zyware網友2002/11/22來信詢問uCOS51在單片機上的硬件仿真問題,具體情況是“在51上用uCOS51核,以及一些構件,keilc上仿真通過,用wave接硬件仿真程序亂飛,wave仿真以前的程序沒有問題,不知是何緣故”。
由于我的OS程序已經在KEIL軟件仿真和硬件上實際測試過,所以不可能是程序錯。可能的原因只能是硬件仿真軟件設置問題。本人用的是Medwin軟件,在Insight上調試,使用uCOS51編譯測試程序一樣跑飛。即使添加修改后的startup.a51(詳見《在51單片機上固化uCOS51的說明》)也不正常。我發現Medwin似乎沒有編譯startup.a51,因為它把該文件加在了other Files目錄下而不是source Files目錄,于是我猜測只有放在source Files目錄下的文件才被編譯。由觀察知,以.c和.asm做后綴的文件均被放在此目錄下且被編譯。于是我立即將startup.a51改成startup.asm并加入項目編譯,結果測試正常。不必擔心startup改名造成沖突,KEIL在鏈接目標文件時會自動處理重名段,本目錄的文件優先級高(我是這么理解的,具體原理不清楚,這只是根據實踐得到的結論,希望了解此處理過程的朋友能告之,不勝感激。)。
具體做法如下:
1。按《在51單片機上固化uCOS51的說明》一文修改startup.a51,并將其更名為startup.asm。
2。將startup.asm、yy1.c、os_cpu_c.c、ucos_ii.c、os_cpu_a.asm五個文件加入項目編譯。
3。運行
在51單片機上固化uCOS51的說明:
近來,收到多位網友來信詢問uCOS51在51單片機上的固化問題,歸納其焦點就是:為什么OS在KeilC51上模擬可以正常運行,但把它燒錄在CPU上卻不能工作?理論上,程序在軟件仿真通過測試后,將其燒錄在硬件上,硬件調試應該一次成功。許多網友也有這個經驗,可為什么在調試uCOS51時失效了呢?難道操作系統調試很特殊嗎?
其實問題出在重入函數的引入。原來KEILC51軟件仿真在不修改startup.a51文件的情況下,缺剩使用64K外部RAM,它把0000H-FFFFH全部仿真為可讀寫的RAM,而用戶的硬件系統可能沒有用到那么大的RAM空間,比如只用了8K/16K/32K等,或者用戶把一些地址空間映射給了別的設備,比如8019AS等。在沒有調用OSTaskCreate前,定義為reentrant的函數將用FFE0H做仿真堆棧棧頂指針,而此處在用戶的系統里不是RAM,造成程序跑飛。比如在我的用戶板上,將FE00H-FFFFH空間的一部分分配給8019AS使用,如果把demo程序編譯后直接燒到51上,將不能運行。解決辦法是根據系統RAM配置,修改startup.a51文件,并將其加入項目編譯,如下所示:
XBPSTACK EQU 1 ; set to 1 if large reentrant is used.
XBPSTACKTOP EQU 07FFFH+1; set top of stack to highest location+1.
按此修改后,在有32K外部RAM的系統上可以正常運行。用戶可根據自己XRAM的實際配置情況修改startup.a51相關參數,并將其添加到項目里編譯。不必理會KEIL/C51/LIB目錄下的同名文件,此處的startup.a51優先級高,KEIL將按此處該文件的配置編譯項目。
這也解釋了有些網友問到的,“為什么加入reentrant關鍵字,在軟件仿真時正確,燒在芯片上就死機,去掉reentrant后兩者都正常”的問題。由于大多數人很少使用重入函數,往往不了解這個細節,特此提請大家注意。
關于uCOS51不能正常工作的原因還可能是因為串口波特率和OS_TICKS_PER_SEC及TH0、TL0設置不正確引起的。demo程序默認使用22.1184MHz晶體,19200波特率,切換頻率為50Hz。為此,1。在SERIAL.C中設置“TL1=0xFD;TH1=0xFD;”使波特率為19200;2。在OS_CPU_C.C和OS_CPU_A.ASM中設置“TH0=0x70;TL0=0x00;”使時鐘節拍tick=50次/秒;3。在OS_CFG.H中設置OS_TICKS_PER_SEC為50Hz。用戶應根據實際情況,相應地修改這些參數,否則運行不正確。
定時器初值設置:
定時器0用于時鐘節拍發生器
/
} wt[MaxLenWordTable];
} WORDTABLE;
取詞
bit GetWord(unsigned char *ComBuf,WORDTABLE *WordTable)
{
int i=0;
int j=0;
int k=-1;
int StrFlag=0;
int SentenceEndFlag=0;
char ch;
WordTable->Num=0;
WordTable->LeftCurveNum=0;
WordTable->RightCurveNum=0;
ch=ComBuf[0];
while(!SentenceEndFlag&&i if((ch>=’0’&&ch<=’9’)||(ch>=’a’&&ch<=’z’)||(ch>=’A’&&ch<=’Z’)||(ch==’.’)){
if(StrFlag==0){
StrFlag=1;k=k+1;j=0;
if(k>=MaxLenWordTable) return 0;
WordTable->wt[k].Str[j]=ch;
WordTable->Num=k+1;
}
else{
j=j+1;
if(j>=MaxLenWord) return 0;
WordTable->wt[k].Str[j]=ch;
}
}
else if(ch==’ ’||ch==’,’||ch==’(’||ch==’)’||ch==’
主站蜘蛛池模板:
定陶县|
正镶白旗|
建德市|
古浪县|
花莲市|
乐东|
安庆市|
双辽市|
麻栗坡县|
星座|
灵寿县|
阿巴嘎旗|
闽侯县|
肥西县|
和硕县|
修文县|
瑞丽市|
顺义区|
南川市|
闸北区|
美姑县|
中阳县|
惠安县|
泾阳县|
双江|
迭部县|
西贡区|
静乐县|
浏阳市|
旺苍县|
鲁山县|
洛隆县|
阳江市|
达拉特旗|
乌拉特前旗|
三门县|
温泉县|
辛集市|
龙口市|
上蔡县|
成都市|