前言 其實(shí)關(guān)于uCOS-II的51單片機(jī)移植教程和例子網(wǎng)上已經(jīng)有很多了,但是大部分都是基于Proteus仿真外擴(kuò)內(nèi)存的,下載之后也不能直接在硬件上使用,也沒(méi)有具體的移植教程。這對(duì)于一個(gè)想學(xué)習(xí)操作系統(tǒng)而又無(wú)從下手小白的來(lái)說(shuō)簡(jiǎn)直就是噩夢(mèng)。 由于51內(nèi)核的特殊性和keil編譯器原因(51的系統(tǒng)堆棧指針和Keil編譯后仿真堆棧指針增長(zhǎng)的方向是相反的)帶來(lái)移植的困難。網(wǎng)上的例程處理堆棧的方式有兩種(至于不太懂的同學(xué)可以移步看看這篇帖子) https://blog.csdn.net/s111sw/article/details/6012720,一種是用小模式Small,另外一種是大模式Large。Small模式是把系統(tǒng)堆棧數(shù)據(jù)和仿真堆棧數(shù)據(jù)一起復(fù)制到用戶(hù)堆棧XDATA區(qū),這種方式編譯代碼小但是任務(wù)切換速度慢。而Large模式在編譯的時(shí)候Keil默認(rèn)是把仿真堆棧數(shù)據(jù)設(shè)置在XDATA區(qū),所以在任務(wù)切換的時(shí)候只需要把系統(tǒng)堆棧和仿真堆棧的當(dāng)前地址保存到用戶(hù)堆棧就行,這樣的方式編譯代碼大但任務(wù)切換速度快(現(xiàn)在都是用這種方式,STC12C5A60S2在22.1184MHz晶振下任務(wù)切換時(shí)間37us)?墒,前面已經(jīng)說(shuō)了51的系統(tǒng)堆棧指針是向上增長(zhǎng)的,而Keil編譯的仿真指針是向下增長(zhǎng)的,這就導(dǎo)致了一個(gè) 問(wèn)題---uCOS操作系統(tǒng)的堆棧檢測(cè)函數(shù)OSTaskStkChk沒(méi)辦法使用。上面鏈接帖子里面用的方法是修改uCOS的內(nèi)核函數(shù)實(shí)現(xiàn)堆棧檢測(cè)功能的。正是因?yàn)槲也幌胄薷膬?nèi)核函數(shù)的原因所以才有了我現(xiàn)在移植的uCOS的版本。
1.png (41.08 KB, 下載次數(shù): 123)
下載附件
2020-2-12 13:07 上傳
圖1
雖然51的系統(tǒng)堆棧指針只能向上增長(zhǎng),但是在代碼里面我們可以人為的把里面的數(shù)據(jù)按照自己的意愿存放到用戶(hù)堆棧里。下面是我移植的堆棧結(jié)構(gòu),把系統(tǒng)堆棧增長(zhǎng)方向和仿真堆棧統(tǒng)一起來(lái)就可以實(shí)現(xiàn)堆棧連續(xù)存放數(shù)據(jù)了。
2.png (46.89 KB, 下載次數(shù): 110)
下載附件
2020-2-12 13:08 上傳
圖2 開(kāi)始移植 準(zhǔn)備工具 1、 電腦一臺(tái)(廢話!) 2、 Keil4 3、 下載配套的代碼一份,我移植的是比較經(jīng)典的版本uCOS-II 2.52
下載的代碼已經(jīng)是移植好STC12C5A60S2的例程 功能就是兩個(gè)任務(wù)用堆棧檢測(cè)函數(shù)OSTaskStkChk檢測(cè)當(dāng)前自己堆棧使用情況,然后串口發(fā)出 波特率115200
下面開(kāi)始講解怎么把uCOS移植到不同型號(hào)51單片機(jī) 打開(kāi)工程\51_uCOS-IIV2.52\Project\ uCOS.uvproj
第1步:把keil配置為大模式,就是讓Keil把默認(rèn)變量定義到XDATA,下圖3
3.png (93.9 KB, 下載次數(shù): 115)
下載附件
2020-2-12 13:08 上傳
圖3
第2步:打開(kāi)STARTUP .A51啟動(dòng)文件修改一些啟動(dòng)參數(shù)來(lái)對(duì)應(yīng)單片機(jī)資源 根據(jù)單片機(jī)內(nèi)部資源作調(diào)整,因?yàn)镾TC12C5A60S2有1280字節(jié)的SRAM,包括內(nèi)部0-0xFF 256字節(jié)和外部0-0x3FF 1024字節(jié) 圖4把IDATALEN改成100H(內(nèi)部256),XDATALEN改成0x03FF(外部?jī)?nèi)存根據(jù)不同型號(hào)芯片的大小配置,最大是0xFFFF),這樣單片機(jī)初始化的時(shí)候就會(huì)把相應(yīng)的RAM清0。
4.png (54.97 KB, 下載次數(shù): 106)
下載附件
2020-2-12 13:08 上傳
圖4 接著把圖5的XBPSTACK設(shè)置為1使能圖6的仿真堆棧初始化代碼,XBPSTACKTOP設(shè)置成 0x03FF,就是單片機(jī)外部RAM的末尾地址。
5.png (5.76 KB, 下載次數(shù): 111)
下載附件
2020-2-12 13:08 上傳
圖5
6.png (2.38 KB, 下載次數(shù): 127)
下載附件
2020-2-12 13:08 上傳
圖6
這樣就配置好啟動(dòng)文件了。 第3 步:配置uCOS文件 uCOS系統(tǒng)需要一個(gè)時(shí)鐘節(jié)拍,節(jié)拍頻率10Hz-1000Hz。我用的是T0定時(shí)器每10ms中斷一次。打開(kāi)OS_CPU_C.C文件找到void InitHardware(void) T0定時(shí)器初始化函數(shù),配置成想要的中斷時(shí)間,然后修改中斷函數(shù)void OSTickISR(void) interrupt 1里面TL0和TL1的初值,如果是有 自動(dòng)重裝功能的定時(shí)器就可以注釋掉這兩句,最后配置OS_CFG.H文件里最下面的宏定義#define OS_TICKS_PER_SEC,這個(gè)就是1秒鐘的節(jié)拍數(shù),例如10ms中斷一次就是100,20ms中斷一次就是50。 #define OS_TICKS_PER_SEC100 /* Set the number of ticks in one second */ ----------------------------------移植完畢---------------------------------------
堆棧結(jié)構(gòu)解釋
7.png (50.56 KB, 下載次數(shù): 114)
下載附件
2020-2-12 13:09 上傳
圖7 任務(wù)創(chuàng)建之后堆棧指針一直指向用戶(hù)棧頂,圖7左是堆棧初始化之后里面的數(shù)據(jù)結(jié)構(gòu),用戶(hù)堆棧的最高3個(gè)字節(jié)一直固定保存“系統(tǒng)堆棧長(zhǎng)度”和“?C_XBP(仿真堆棧指針)”因?yàn)槿蝿?wù)初始化的時(shí)候仿真堆棧還沒(méi)有使用,所以?C_XBP指向的堆棧下一個(gè)地址就是空閑堆棧,緊跟著就是系統(tǒng)堆棧數(shù)據(jù)。 在啟動(dòng)任務(wù)調(diào)度置后仿真堆棧被使用之后變成圖7右的結(jié)構(gòu),任務(wù)切換時(shí)從?C_XBP指向的下一個(gè)地址開(kāi)始保存系統(tǒng)堆棧數(shù)據(jù),保存的數(shù)據(jù)長(zhǎng)度由“系統(tǒng)堆棧長(zhǎng)度” 決定 ,這樣就實(shí)現(xiàn)了堆棧向下連續(xù)增長(zhǎng)而不需要修改uCOS的堆棧檢測(cè)函數(shù)。
//堆棧初始化函數(shù)
OS_STK *OSTaskStkInit(void (*task)(void *pd) reentrant, void *p_arg, OS_STK *ptos, INT16U opt)reentrant { OS_STK *stk;
p_arg =p_arg; opt = opt; //opt沒(méi)被用到,保留此語(yǔ)句防止告警產(chǎn)生 stk =ptos; //用戶(hù)堆棧最低有效地址 *stk-- =15; //系統(tǒng)堆棧長(zhǎng)度 *stk-- =(INT16U)(ptos-3) >> 8; //?C_XBP 仿真堆棧指針高8位 *stk-- = (INT16U)(ptos-3) &0xFF; //?C_XBP 仿真堆棧指針低8位 最高3個(gè)字節(jié)一直被占 //用所以減3 *stk-- =0x07; //R7 *stk-- =0x06; //R6 *stk-- =0x05; //R5 *stk-- =0x04; //R4 *stk-- =0x01; //R3 *stk-- =0x02; //R2 *stk-- =0x01; //R1 *stk-- =0x00; //R0 *stk-- =0x00; //PSW *stk-- =0x00; //DPL *stk-- =0x00; //DPH *stk-- =0x0B; //B *stk-- =0x0A; //ACC *stk-- =(INT16U)task >> 8; //任務(wù)地址高8位 *stk-- =(INT16U)task & 0xFF; //任務(wù)地址低8位
stk = ptos;//堆棧指針一直指向棧頂
return stk; }
;***************************************************************************************** ;* uC/OS-II ;* 實(shí)時(shí)內(nèi)核 ;* ;* (c) Copyright1992-1998, Jean J. Labrosse, Plantation, FL ;* 版權(quán)所有 ;* ;* MCU-51 專(zhuān)用代碼 ;* KEIL C51大模式編譯 ;* ;* 文件名 : OS_CPU_A.ASM ;* 作者 : Jean J. Labrosse ;*****************************************************************************************
;聲明:本代碼僅供學(xué)習(xí)研究uCOS-II使用,如用作其他用途出現(xiàn)問(wèn)題本人概不負(fù)責(zé)。
;偽指令詳細(xì)用法請(qǐng)查A51.PDF文件 ;程序結(jié)構(gòu)詳見(jiàn)《uC/OS-II》193-198頁(yè)
;不用此語(yǔ)句!。 $CASE ;標(biāo)號(hào)和變量名區(qū)分大小寫(xiě)
$NOMOD51 EA BIT 0A8H.7 SP DATA 081H B DATA 0F0H ACC DATA 0E0H DPH DATA 083H DPL DATA 082H PSW DATA 0D0H TR0 BIT 088H.4 TH0 DATA 08CH TL0 DATA 08AH
NAMEOS_CPU_A ;模塊名
;定義重定位段 ?PR?OSStartHighRdy?OS_CPU_A SEGMENT CODE ?PR?OSCtxSw?OS_CPU_A SEGMENT CODE ?PR?OSIntCtxSw?OS_CPU_A SEGMENT CODE
;?PR?OSTickISR?OS_CPU_A SEGMENT CODE ;?PR?_?serial?OS_CPU_A SEGMENT CODE
;聲明引用全局變量和外部子程序 EXTRNDATA (?C_XBP) ;仿真堆棧指針用于重入局部變量保存,為V2.51能被C使用定義在本模塊中
EXTRNIDATA (OSTCBCur) EXTRNIDATA (OSTCBHighRdy) EXTRNIDATA (OSRunning) EXTRNIDATA (OSPrioCur) EXTRN IDATA (OSPrioHighRdy) EXTRNDATA (EA_Nesting)
; EXTRNCODE (OSTaskSwHook) EXTRNCODE (OSIntEnter) EXTRNCODE (OSIntExit) EXTRNCODE (OSTimeTick)
;對(duì)外聲明4個(gè)不可重入函數(shù) PUBLICOSStartHighRdy PUBLICOSCtxSw PUBLICOSIntCtxSw
; PUBLICOSTickISR ; PUBLICSerialISR
;分配堆?臻g,?STACK和STARTUP.A51中同名,編譯器會(huì)將兩個(gè)?STACK段合并,堆棧大小在STARTUP.A51中定義 ?STACK SEGMENT IDATA RSEG ?STACK ;------------------------------------------------------------------------------- PUSHALL MACRO ;定義壓棧出棧宏 PUSH ACC PUSH B PUSH DPH PUSH DPL PUSH PSW MOV PSW,#0x00 PUSH0x00 ;R0-R7入棧 PUSH 0x01 PUSH 0x02 PUSH 0x03 PUSH 0x04 PUSH 0x05 PUSH 0x06 PUSH 0x07 ENDM
POPALL MACRO POP 0x07 ;R0-R7出棧 POP 0x06 POP 0x05 POP 0x04 POP 0x03 POP 0x02 POP 0x01 POP 0x00 POP PSW POP DPL POP DPH POP B POP ACC ENDM ;------------------------------------------------------------------------- ; 啟動(dòng)任務(wù)------切換堆棧指針,恢復(fù)堆棧數(shù)據(jù) ;------------------------------------------------------------------------- RSEG?PR?OSStartHighRdy?OS_CPU_A OSStartHighRdy: USING0 ; ; LCALL OSTaskSwHook CtxSw: ;OSTCBCur ===> DPTR 獲得當(dāng)前TCB指針,詳見(jiàn)C51.PDF第178頁(yè) MOV R0,#LOW (OSTCBCur) ;獲得OSTCBCur指針低地址, INC R0 ;指針占3字節(jié)。+0類(lèi)型+1高8位數(shù)據(jù)+2低8位數(shù)據(jù) MOV DPH,@R0 ;全局變量OSTCBCur在IDATA中 INC R0 MOV DPL,@R0
;OSTCBCur->OSTCBStkPtr ===> DPTR 獲得用戶(hù)堆棧指針 INC DPTR ;指針占3字節(jié)。+0類(lèi)型+1高8位數(shù)據(jù)+2低8位數(shù)據(jù) MOVXA,@DPTR ;取用戶(hù)棧頂指針OSTCBStkPtr MOV R0,A INC DPTR MOVXA,@DPTR
ADD A,#0FEH ;DPTR-2指向保存?C_XBP低8位的地址 MOV DPL,A MOV A,R0 ADDC A,#0FFH MOV DPH,A
;恢復(fù)仿真堆棧指針 MOVX A,@DPTR MOV ?C_XBP+1,A ;?C_XBP仿真堆棧指針低8位 INC DPTR MOVX A,@DPTR MOV ?C_XBP,A ;?C_XBP仿真堆棧指針高8位
INC DPTR MOVXA,@DPTR ;恢復(fù)系統(tǒng)堆棧長(zhǎng)度 MOV R5,A ; ;因?yàn)镈PTR沒(méi)有自減1指令,所以只能計(jì)算出系統(tǒng)堆棧的末尾地址然后用INC DPTR復(fù)制數(shù)據(jù)提高效率 CLR C ;系統(tǒng)堆棧末尾地址 = 當(dāng)前仿真堆棧地址-系統(tǒng)堆棧長(zhǎng)度 MOV A,?C_XBP+1 SUBB A,R5 MOV DPL,A MOV A,?C_XBP SUBB A,#0 MOV DPH,A
MOV R0,#?STACK-1 ;獲取堆棧起址
RestoreStk: ;恢復(fù)現(xiàn)場(chǎng)堆棧內(nèi)容 INC DPTR INC R0 MOVXA,@DPTR MOV @R0,A DJNZR5,RestoreStk
MOV SP,R0 ;恢復(fù)堆棧指針SP
;OSRunning=TRUE MOV R0,#LOW (OSRunning) MOV @R0,#01
POPALL
MOVEA_Nesting,#0 ;切換任務(wù)清零EA嵌套 SETBEA ;開(kāi)中斷 RETI ;------------------------------------------------------------------------- ; 任務(wù)級(jí)切換函數(shù) ;------------------------------------------------------------------------- RSEG?PR?OSCtxSw?OS_CPU_A OSCtxSw: PUSHALL;任務(wù)堆棧進(jìn)棧 IntCtxSw: ;OSTCBCur ===> DPTR 獲得當(dāng)前TCB指針,詳見(jiàn)C51.PDF第178頁(yè) MOV R0,#LOW (OSTCBCur) ;獲得OSTCBCur指針低地址,指針占3字節(jié)。+0類(lèi)型+1高8位數(shù)據(jù)+2低8位數(shù)據(jù) INC R0 MOV DPH,@R0 ;全局變量OSTCBCur在IDATA中 INC R0 MOV DPL,@R0
;OSTCBCur->OSTCBStkPtr ===> DPTR 獲得用戶(hù)堆棧指針 INC DPTR ;指針占3字節(jié)。+0類(lèi)型+1高8位數(shù)據(jù)+2低8位數(shù)據(jù) MOVXA,@DPTR ;取用戶(hù)棧頂指針OSTCBStkPtr MOV R0,A INC DPTR MOVXA,@DPTR
ADD A,#0FEH ;DPTR-2指向保存?C_XBP低8位的地址 MOV DPL,A MOV A,R0 ADDC A,#0FFH MOV DPH,A
;保存仿真堆棧指針?C_XBP MOV A,?C_XBP+1 ;?C_XBP 仿真堆棧指針低8位 MOVX @DPTR,A INC DPTR MOV A,?C_XBP ;?C_XBP 仿真堆棧指針高8位 MOVX @DPTR,A
MOV A,SP ;計(jì)算系統(tǒng)堆棧長(zhǎng)度 CLR C SUBBA,#?STACK-1 MOV R5,A ;保存系統(tǒng)堆棧長(zhǎng)度 INC DPTR MOVX @DPTR,A ;因?yàn)镈PTR沒(méi)有自減1指令,所以只能計(jì)算出系統(tǒng)堆棧的末尾地址然后用INC DPTR復(fù)制數(shù)據(jù)提高效率 CLR C ;系統(tǒng)堆棧末尾地址 = 當(dāng)前仿真堆棧地址-系統(tǒng)堆棧長(zhǎng)度 MOV A,?C_XBP+1 SUBB A,R5 MOV DPL,A MOV A,?C_XBP SUBB A,#0 MOV DPH,A
MOV R0,#?STACK-1 ;獲取堆棧起址 SaveStk: INC DPTR ;保存系統(tǒng)堆棧到任務(wù)堆棧 INC R0 MOV A,@R0 MOVX @DPTR,A DJNZR5,SaveStk
; LCALL OSTaskSwHook ;調(diào)用用戶(hù)程序
;獲取新任務(wù)TCB指針 MOV R0,#OSTCBCur ;OSTCBCur= OSTCBHighRdy MOV R1,#OSTCBHighRdy MOV A,@R1 MOV @R0,A ;指針類(lèi)型 INC R0 INC R1 MOV A,@R1 MOV @R0,A ;指針高8位 INC R0 INC R1 MOV A,@R1 MOV @R0,A ;指針低8位
;獲取新任務(wù)優(yōu)先級(jí) MOV R0,#OSPrioCur ;OSPrioCur =OSPrioHighRdy MOV R1,#OSPrioHighRdy MOV A,@R1 MOV @R0,A
LJMP CtxSw ;------------------------------------------------------------------------- ; 中斷級(jí)任務(wù)切換 ;------------------------------------------------------------------------- RSEG?PR?OSIntCtxSw?OS_CPU_A OSIntCtxSw: ;調(diào)整SP指針去掉在調(diào)用OSIntExit(),OSIntCtxSw()過(guò)程中壓入堆棧的多余內(nèi)容 ;SP=SP-4 MOV A,SP CLR C SUBBA,#4 MOV SP,A
LJMPIntCtxSw
END
|