• <li id="00i08"><input id="00i08"></input></li>
  • <sup id="00i08"><tbody id="00i08"></tbody></sup>
    <abbr id="00i08"></abbr>
  • 新聞中心

    EEPW首頁 > 嵌入式系統 > 設計應用 > uC/OS-II在51單片機上的移植2

    uC/OS-II在51單片機上的移植2

    作者: 時間:2016-11-22 來源:網絡 收藏
    文件名 : YY.C

    #i nclude

    本文引用地址:http://www.czjhyjcfj.com/article/201611/319610.htm

    #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==’

    主站蜘蛛池模板: 定陶县| 正镶白旗| 建德市| 古浪县| 花莲市| 乐东| 安庆市| 双辽市| 麻栗坡县| 星座| 灵寿县| 阿巴嘎旗| 闽侯县| 肥西县| 和硕县| 修文县| 瑞丽市| 顺义区| 南川市| 闸北区| 美姑县| 中阳县| 惠安县| 泾阳县| 双江| 迭部县| 西贡区| 静乐县| 浏阳市| 旺苍县| 鲁山县| 洛隆县| 阳江市| 达拉特旗| 乌拉特前旗| 三门县| 温泉县| 辛集市| 龙口市| 上蔡县| 成都市|