找回密碼
 立即注冊(cè)

QQ登錄

只需一步,快速開(kāi)始

搜索
查看: 18418|回復(fù): 3
收起左側(cè)

uCOS_51之移植心得(持續(xù)更新)

[復(fù)制鏈接]
ID:70976 發(fā)表于 2014-12-25 13:25 | 顯示全部樓層 |閱讀模式
【uCOS_51的移植概述】
uCOS_51是uCOS-II v2.52在MCS-51系列單片機(jī)上的移植實(shí)例,采用大模式,須外部擴(kuò)展64KB的SRAM,內(nèi)核的移植簡(jiǎn)單地歸納為如下幾條:
(1)聲明11個(gè)數(shù)據(jù)類(lèi)型(OS_CPU.H);
(2)用#define聲明4個(gè)宏(OS_CPU.H);
(3)用C語(yǔ)言編寫(xiě)10個(gè)簡(jiǎn)單的函數(shù)(OS_CPU_C.C);
(4)編寫(xiě)4個(gè)匯編語(yǔ)言函數(shù)(OS_CPU_A.ASM)。
上述為一般移植過(guò)程中所要進(jìn)行的工作,除此之外,我還增設(shè)了其它措施,以便于應(yīng)用。

【uCOS_51的技術(shù)支持】
uCOS_51由本人休閑在家編寫(xiě),由于時(shí)間精力和能力的有限,難免有所疏乎,歡迎有志人士一起學(xué)習(xí)探討。
作者:華兄
郵箱:xxxxxxxx@qq.com

“uCOS_51”是我給工程起的名稱(chēng),“望文生義”,我還習(xí)慣Version的簡(jiǎn)寫(xiě)使用小寫(xiě)字母,uCOS-II v2.52,而不是uCOS-II V2.52,哈哈,習(xí)慣了就好。我原本想去移植從官網(wǎng)下的最新版本uCOS-II內(nèi)核《Micrium-uCOS-II-V290》,我只移植作簡(jiǎn)單地測(cè)試,并不打算試用其它的功能,我又熱忠uCOS-II v2.52這個(gè)版本,對(duì)其內(nèi)核源碼非常了解,于是打消了移植最新版本內(nèi)核的念頭,感興趣的朋友可以去試試。其實(shí)這篇文章早就醞釀要寫(xiě)了,由于沒(méi)有工作的壓力,也就沒(méi)有學(xué)習(xí)的動(dòng)力,三天打魚(yú)兩天曬網(wǎng),想到什么,寫(xiě)什么,沒(méi)有什么邏輯性,不要見(jiàn)怪,哈哈!

我不想談移植工作和具體的實(shí)現(xiàn),這些隨大流的東西,源碼是最好的老師,里面有詳盡的注釋。我就隨寫(xiě)自己的想法吧。

每個(gè)接觸過(guò)uCOS_51的朋友,心里面總會(huì)有一些想法:它究竟是如何運(yùn)作的?OS內(nèi)核究竟有多奇妙?小小51單片機(jī)也能跑操作系統(tǒng) ……?

想必不少朋友聽(tīng)說(shuō)過(guò)或者使用過(guò)RTX-51實(shí)時(shí)系統(tǒng),這是我使用過(guò)的最小操作系統(tǒng)內(nèi)核,Keil自帶的,內(nèi)核源碼完全使用匯編語(yǔ)言編寫(xiě)。談起OS內(nèi)核,核心任務(wù)之一就是調(diào)度:為任務(wù)分配資源和時(shí)間,決定任務(wù)運(yùn)行的次序,從而使系統(tǒng)滿(mǎn)足特定的性能要求。這些說(shuō)起來(lái)過(guò)于籠統(tǒng),一句話(huà)就是不停地切換CPU寄存器——保存寄存器:入棧;彈出寄存器:出棧;保存寄存器:入棧;恢復(fù)寄存器:出棧。講來(lái)講去,都跟棧有關(guān)系,那就談?wù)剈COS_51的任務(wù)棧和硬件堆棧。

每個(gè)任務(wù)都會(huì)有自己的任務(wù)棧用于保存現(xiàn)場(chǎng)——CPU寄存器和其它信息,uCOS_51中通過(guò)OSTaskStkInit函數(shù)(源碼見(jiàn)工程,建議下載uCOS_51修訂版)來(lái)初始化所有的任務(wù)棧,棧底保存堆棧長(zhǎng)度,然后就是任務(wù)代碼起始地址,接下來(lái)CPU寄存器,最后仿真堆棧指針。接下來(lái)談?wù)動(dòng)布褩,硬件堆棧在OS_CPU_A.ASM中通過(guò)保留一定數(shù)量的存儲(chǔ)單元獲得,它只關(guān)心存儲(chǔ)單元數(shù)量,堆棧起始地址OSStack由Keil指定。所謂硬件堆棧,就是專(zhuān)門(mén)給占用CPU的任務(wù)使用,如何使用的呢?就是把任務(wù)棧內(nèi)容彈出到硬件堆棧,然后用去恢復(fù)現(xiàn)場(chǎng),還有保存運(yùn)行過(guò)程中的中間數(shù)據(jù)。發(fā)生任務(wù)切換保存現(xiàn)場(chǎng)時(shí),也是按初始化任務(wù)棧的次序,從硬件堆棧中彈出任務(wù)運(yùn)行信息包括其它信息(如任務(wù)調(diào)用子函數(shù)產(chǎn)生的斷點(diǎn)信息)、任務(wù)代碼地址,Oh,no!此時(shí)叫斷點(diǎn),還有CPU寄存器。其實(shí)是由OS_CPU_A.ASM中的出、入棧次序決定了OSTaskStkInit函數(shù)中的出、入棧次序。你會(huì)發(fā)現(xiàn)沒(méi)有從硬件堆棧中彈出堆棧長(zhǎng)度以及仿真堆棧指針!堆棧長(zhǎng)度在OS_CPU_A.ASM中是通過(guò)硬件堆棧棧頂SP減去OSStkStart(OSStkStart=OSStack-1)得來(lái)的,怎么不會(huì)是初始值15呢?當(dāng)然不是啦,誰(shuí)知道程序在哪里由于突然發(fā)生調(diào)度而中斷運(yùn)行,任務(wù)運(yùn)行過(guò)程中調(diào)用子函數(shù)就會(huì)有出入棧行為,還有其它因素影響堆棧長(zhǎng)度。在uCOS-II v2.52內(nèi)核中,中斷嵌套不允許發(fā)生任務(wù)的調(diào)度,只有當(dāng)一層中斷嵌套時(shí)才能進(jìn)行中斷級(jí)的任務(wù)切換,如若此時(shí)發(fā)生任務(wù)的切換,會(huì)調(diào)整硬件堆棧棧項(xiàng)SP,SP=SP-4,去掉在調(diào)用OSIntExit()、OSIntCtxSw()過(guò)程中壓入堆棧的多余內(nèi)容(就是兩個(gè)斷點(diǎn))。接下來(lái)就是仿真堆棧指針,它是賦給了?C_XBP這個(gè)變量,保存現(xiàn)場(chǎng)時(shí)也是從這里獲取,我們來(lái)專(zhuān)門(mén)談?wù)?C_XBP這個(gè)仿真堆棧指針。

?C_XBP初始讓人覺(jué)得異常神秘,我也是,移植時(shí)差些被它迷糊了。查閱資料發(fā)現(xiàn),這個(gè)仿真堆棧非常特殊,它是向下生長(zhǎng),而普通堆棧是向上生長(zhǎng),所以我們要把仿真堆棧的高地址值賦給?C_XBP。在STARTUP.A51用戶(hù)上電初始化程序中,我把?C_XBP設(shè)在了外部RAM的最高地址FFFFH+1處,這里較少被占用。哈哈,其實(shí)這些都是門(mén)面工作,uCOS_51中每個(gè)任務(wù)都會(huì)有自己的仿真堆棧指針?C_XBP,OSTaskStkInit函數(shù)中,它是設(shè)在任務(wù)棧項(xiàng)端,也就是任務(wù)棧棧底ptos+MAX_STK_SIZE地址處,那么,ptos+MAX_STK_SIZE除去任務(wù)運(yùn)行正常使用,其余就用作仿真堆棧。哈哈,你不必?fù)?dān)心,仿真堆棧我也不熟,uCOS_51不使用它。

我們?cè)僬勑┦裁春媚兀嫦氤鋈プ咦吡,外邊下著雨,算了,辛苦一?…,先指正uCOS_51修訂版中的三個(gè)錯(cuò)誤吧:(一)在OS_CPU_A.ASM中,?PR?_?SerialISR?OS_CPU_A    SEGMENT CODE,串口中斷服務(wù)子程序不可重入,應(yīng)該是?PR?SerialISR?OS_CPU_A         SEGMENT CODE;(二)在serial.c中,InitSerial函數(shù)里注釋有誤,SCON=0x50;PCON=0x00; // 模式2,SM2=0,SMOD=0,應(yīng)該是模式1,這也是張義和老師書(shū)上的錯(cuò)誤;(三)在uCOS_II.H中,OS_TaskIdle函數(shù)聲明以及OS_TaskStat函數(shù)聲明的參數(shù)少寫(xiě)了一個(gè)'d'變成ppata,應(yīng)該是ppdata;(四)這個(gè)錯(cuò)誤來(lái)自?xún)?nèi)核源碼,在OS_TASK.C中,OSTaskDel函數(shù)定義self這個(gè)布爾變量卻沒(méi)有使用,編譯器發(fā)出警告,該定義已在修訂版中去除,這里只是引起朋友的關(guān)注。

我再談?wù)劀y(cè)試uCOS_51過(guò)程中發(fā)現(xiàn)的一個(gè)非常有意思的問(wèn)題,就是工作狀態(tài)指示燈閃爍異常,起初我設(shè)置1秒閃爍一次,而實(shí)際發(fā)現(xiàn)幾乎要2秒才會(huì)閃爍。系統(tǒng)只有兩個(gè)任務(wù)——空閑任務(wù)和工作狀態(tài)指示任務(wù),負(fù)荷不重,折騰了我老半天,最后我添加另外一個(gè)低優(yōu)先級(jí)任務(wù),高于空閑任務(wù),里面置死循環(huán),完完全全的空任務(wù),使得空閑任務(wù)無(wú)法得到執(zhí)行,這下發(fā)現(xiàn)指示燈正常閃爍起來(lái)了,然后盤(pán)根究底,發(fā)現(xiàn)問(wèn)題出在空閑計(jì)數(shù)器OSIdleCtr上,"OSIdleCtr" 變量務(wù)必設(shè)置為 "idata" 存儲(chǔ)類(lèi)型,否則任務(wù)運(yùn)行節(jié)拍變慢。這就是答案,我特意注釋在main.c中。
由上面的問(wèn)題引發(fā)我們思考,為什么設(shè)置idata存儲(chǔ)類(lèi)型就正常了呢?由于uCOS_51采用大模式,全部變量默認(rèn)為xdata存儲(chǔ)類(lèi)型,存儲(chǔ)在外部RAM中,訪問(wèn)需要花費(fèi)更多的CPU時(shí)間,而空閑任務(wù)是經(jīng)常被調(diào)度的任務(wù),自然空閑計(jì)數(shù)器OSIdleCtr也就成了經(jīng)常被訪問(wèn)的變量。訪問(wèn)頻率如此之高,如若設(shè)在外部RAM中,也就要耗費(fèi)更多的CPU時(shí)間,增加了系統(tǒng)延時(shí),閃爍也就變得慢了。因此需要把它放在內(nèi)部RAM中,其它同理。

好了,我要出去了,哈哈 …

今天先談?wù)?define REENTRANT  reentrant這個(gè)宏,也就是Keil里reentrant這個(gè)關(guān)鍵字。原則上我們不需要修改任何與處理器無(wú)關(guān)的代碼,由于Keil編繹器的特殊性,這些代碼仍需作改動(dòng)。Keil缺省情況下編譯出來(lái)的代碼不可重入,考慮多任務(wù)并發(fā)執(zhí)行,系統(tǒng)要求可重入代碼,因此,需要在每個(gè)C函數(shù)及其聲明后加上關(guān)鍵字reentrant。代碼的可重入是指可以被多個(gè)任務(wù)并發(fā)使用,而數(shù)據(jù)不會(huì)遭到破壞;不可重入是指不能由多個(gè)任務(wù)所共享,除非能確保互斥訪問(wèn)。想要了解更多,還請(qǐng)朋友自發(fā)去查閱資料,我就不再贅述!

下面說(shuō)說(shuō)uCOS-II v2.52里pdata這個(gè)參數(shù),pdata是Keil里保留的關(guān)鍵字,代表分頁(yè)式存儲(chǔ)類(lèi)型。所以避免沖突,給內(nèi)核源碼所有pdata改名為ppdata,這是追隨楊大俠的做法。

在uCOS-II v2.52里,有BOOLEAN這個(gè)數(shù)據(jù)類(lèi)型,它是布爾型,取值0或1。雖然你很想用bit這個(gè)數(shù)據(jù)類(lèi)型去typedef,但強(qiáng)烈建議最好不要這么去做,因?yàn)閎it無(wú)法在結(jié)構(gòu)體里使用,所以把BOOLEAN定義成uchar類(lèi)型。

... ...

貌似很久沒(méi)有更新過(guò)了 。。。

修正兩個(gè)錯(cuò)誤吧,OS_EXT  DF_IDATA  OS_TCB           *OSTCBCur; 以及 OS_EXT  DF_IDATA  OS_TCB           *OSTCBHighRdy; 改為 OS_EXT    OS_TCB           *DF_IDATA OSTCBCur; 和 OS_EXT    OS_TCB           *DF_IDATA OSTCBHighRdy;,原因很簡(jiǎn)單,OSTCBCur和OSTCBHighRdy存放于idata中,所指向的對(duì)象才存放在xdata。

討論一下OSTCBCur和OSTCBHighRdy這兩個(gè)指針,根據(jù)Keil C51用戶(hù)手冊(cè),這兩個(gè)指針?lè)謩e占用三個(gè)字節(jié)的地址空間,+0表示指針?biāo)赶驅(qū)ο蟮臄?shù)據(jù)類(lèi)型,+1表示高8位數(shù)據(jù),+2表示低8位數(shù)據(jù),+1數(shù)據(jù)和+2數(shù)據(jù)共同組成對(duì)象的16位地址。

這里講一下uCOS_51中的模塊化編程,uCOS_II.C中,#define  OS_GLOBALS這個(gè)宏,使得uCOS_II.H中的普通變量、結(jié)構(gòu)體變量等對(duì)于內(nèi)核來(lái)說(shuō)是全局變量(見(jiàn)uCOS_II.H之#define  OS_EXT);內(nèi)核以外,由于沒(méi)有這個(gè)宏,BSP、應(yīng)用層將視為extern類(lèi)型(見(jiàn)uCOS_II.H之#define  OS_EXT  extern)。#define  OS_MASTER_FILE防止內(nèi)核重復(fù)包含INCLUDES.H這個(gè)頭文件。uCOS_II.C包含頭文件和內(nèi)核文件,編譯預(yù)處理階段,就會(huì)加載這些頭文件和內(nèi)核文件信息,編譯時(shí),一塊編譯成uCOS_II.O目標(biāo)文件。

#include "..\ucos_51\ucos-ii\inc\includes.h",這個(gè)東東,起初也許你會(huì)覺(jué)得它比較冗長(zhǎng),沒(méi)有辦法,#include "includes.h"編譯器報(bào)錯(cuò)找不到文件,只能使用笨方法為編譯器指定文件路徑。

扯一下就緒表吧,uCOS-II v2.52支持64個(gè)優(yōu)先級(jí),OSRdyGrp、OSRdyTbl[]、OSUnMapTbl[]這幾個(gè)變量功不可沒(méi)。我們可以通過(guò)void  OS_Sched (void);這個(gè)函數(shù)(見(jiàn)OS_CORE.C)來(lái)研究uCOS-II v2.52查找當(dāng)前就緒的最高優(yōu)先級(jí)算法。特別談一下OSUnMapTbl[]這個(gè)東西,它是由256個(gè)元素組成的數(shù)組,作用:查找一個(gè)8位二進(jìn)數(shù)中,最低位為1的是哪個(gè)位。如0x80,最低位為1的是第7位;0x00和0xff,最低位為1的是第0位。我們來(lái)看看uCOS-II v2.52查找當(dāng)前就緒的最高優(yōu)先級(jí)算法,下面代碼來(lái)自void  OS_Sched (void);(見(jiàn)OS_CORE.C)。

y             = OSUnMapTbl[OSRdyGrp];          /* Get pointer to HPT ready to run              */
OSPrioHighRdy = (INT8U)((y << 3) + OSUnMapTbl[OSRdyTbl[y]]);

OSUnMapTbl[OSRdyGrp]獲得OSRdyGrp中最低位為1的位置y,即最高優(yōu)先級(jí)所在就緒表OSRdyTbl[]的分組,一共8組(0~7)。OSRdyTbl[y]可以獲得最高優(yōu)先級(jí)所在就緒表OSRdyTbl[]的分組信息,一串8位二進(jìn)制數(shù)。OSUnMapTbl[OSRdyTbl[y]]可以獲得這串二制數(shù)中最低位為1的位,假設(shè)為x。在uC/OS中,數(shù)值越小,優(yōu)先級(jí)越大,y和x都是OSRdyGrp以及OSRdyTbl[y]位最低的,uCOS-II v2.52中優(yōu)先級(jí)計(jì)算公式:Prio=y<<3+x。y和x都是最低最小,則Prio最小,優(yōu)先級(jí)就越大。

uCOS-II v2.52只支持64個(gè)優(yōu)先級(jí),我記得應(yīng)該在uCOS-II v2.80以后,就開(kāi)始支持256個(gè)優(yōu)先級(jí)了。我們拿uCOS-II v2.83舉例說(shuō)明,它沒(méi)有改變OSUnMapTbl[]這個(gè)數(shù)組,但改變了OSRdyGrp和OSRdyTbl[],有8位和16位兩種定義,也改變了OSPrioHighRdy的長(zhǎng)度,由8位變?yōu)?6位長(zhǎng)度。我們來(lái)看看uCOS-II v2.83查找當(dāng)前就緒的最高優(yōu)先級(jí)算法。

下面代碼來(lái)自u(píng)COS_II.H。

#if OS_LOWEST_PRIO <= 63 // 優(yōu)先級(jí)數(shù)少于64時(shí)
OS_EXT  INT8U             OSRdyGrp;                        /* Ready list group                         */
OS_EXT  INT8U             OSRdyTbl[OS_RDY_TBL_SIZE];       /* Table of tasks which are ready to run    */
#else // 優(yōu)先級(jí)數(shù)多于63時(shí),注意變量數(shù)據(jù)長(zhǎng)度的變化
OS_EXT  INT16U            OSRdyGrp;                        /* Ready list group                         */
OS_EXT  INT16U            OSRdyTbl[OS_RDY_TBL_SIZE];       /* Table of tasks which are ready to run    */
#endif

下面代碼來(lái)自static  void  OS_SchedNew (void);(見(jiàn)OS_CORE.C),此函數(shù)會(huì)被OS_Sched ()調(diào)用,用于查找當(dāng)前就緒的最高優(yōu)先級(jí)。

static  void  OS_SchedNew (void)
{
#if OS_LOWEST_PRIO <= 63                         /* See if we support up to 64 tasks                   */
     INT8U   y;


     y             = OSUnMapTbl[OSRdyGrp];
     OSPrioHighRdy = (INT8U)((y << 3) + OSUnMapTbl[OSRdyTbl[y]]);
#else                                            /* We support up to 256 tasks                         */
// ---------------------------------------- 上半部分與uCOS-II v2.52一樣 ----------------------------------------
     INT8U   y;
     INT16U *ptbl;


     if ((OSRdyGrp & 0xFF) != 0) { // OSRdyGrp低8位不為0,最高優(yōu)先級(jí)信息必定分布在OSRdyGrp低8位
         y = OSUnMapTbl[OSRdyGrp & 0xFF]; // 查找OSRdyGrp低8位中最低位為1的位置y
     } else { // OSRdyGrp低8位為0,最高優(yōu)先級(jí)信息必定分布在OSRdyGrp高8位
         y = OSUnMapTbl[(OSRdyGrp >> 8) & 0xFF] + 8; // OSRdyGrp右移8位,查找OSRdyGrp低8位中最低位為1的位置,再加8才是最高優(yōu)先級(jí)信息分布在OSRdyGrp中的實(shí)際位置y,這是高明之處
     }
     ptbl = &OSRdyTbl[y]; // 注意,OSRdyTbl[y]長(zhǎng)度為16位
     if ((*ptbl & 0xFF) != 0) { // OSRdyTbl[y]低8位不為0,最高優(yōu)先級(jí)信息必定分布在OSRdyTbl[y]低8位
         OSPrioHighRdy = (INT8U)((y << 4) + OSUnMapTbl[(*ptbl & 0xFF)]); // OSUnMapTbl[(*ptbl & 0xFF)])類(lèi)似計(jì)算y,不過(guò),這里計(jì)算優(yōu)先級(jí)的方法有所不同而已
     } else { // OSRdyTbl[y]低8位為0,最高優(yōu)先級(jí)信息必定分布在OSRdyTbl[y]高8位
         OSPrioHighRdy = (INT8U)((y << 4) + OSUnMapTbl[(*ptbl >> 8) & 0xFF] + 8); // OSUnMapTbl[(*ptbl >> 8) & 0xFF] + 8類(lèi)似計(jì)算y
     }
#endif
}

前半截優(yōu)先級(jí)數(shù)少于64時(shí),與uCOS-II v2.52一樣,咱們看后半截,我就直接在代碼里面注釋說(shuō)明。

回復(fù)

使用道具 舉報(bào)

ID:96534 發(fā)表于 2016-5-15 21:22 | 顯示全部樓層
?C_XBP 仿真堆棧是什么東西啊,我查了好多資料都沒(méi)查到
回復(fù)

使用道具 舉報(bào)

ID:275111 發(fā)表于 2018-8-23 12:02 | 顯示全部樓層
贊一個(gè),要是說(shuō)明再完整些就更好了。
回復(fù)

使用道具 舉報(bào)

ID:87594 發(fā)表于 2018-9-4 13:42 | 顯示全部樓層
不錯(cuò)
回復(fù)

使用道具 舉報(bào)

本版積分規(guī)則

手機(jī)版|小黑屋|51黑電子論壇 |51黑電子論壇6群 QQ 管理員QQ:125739409;技術(shù)交流QQ群281945664

Powered by 單片機(jī)教程網(wǎng)

快速回復(fù) 返回頂部 返回列表