教程: 建立一個(gè)屬于自己的AVR的RTOS
序
自從03年以來(lái),對(duì)單片機(jī)的RTOS的學(xué)習(xí)和應(yīng)用的熱潮可謂一浪高過一浪.03年,在離開校園前的,非典的那幾個(gè)月,在華師的后門那里買了本邵貝貝的《UCOSII》,通讀了幾次,沒有實(shí)驗(yàn)器材,也不了了之。
在21IC上,大家都可以看到楊屹寫的關(guān)于UCOSII在51上的移植,于是掀起了51上的RTOS的熱潮。
再后來(lái),陳明計(jì)先生推出的small rots,展示了一個(gè)用在51上的微內(nèi)核,足以在52上進(jìn)行任務(wù)調(diào)度。
前段時(shí)間,在ouravr上面開有專門關(guān)于AVR的Rtos的專欄,并且不少的兄弟把自己的作品拿出來(lái),著實(shí)開了不少眼界。這時(shí),我重新回顧了使用單片機(jī)的經(jīng)歷,覺得很有必要,從根本上對(duì)單片機(jī)的RTOS的知識(shí)進(jìn)行整理,于是,我開始了編寫一個(gè)用在AVR單片機(jī)的RTOS。
當(dāng)時(shí),我所有的知識(shí)和資源有:
Proteus6.7 可以用來(lái)模擬仿真avr系列的單片機(jī)
WinAVR v2.0.5.48 基于GCC AVR的編譯環(huán)境,好處在于可以在C語(yǔ)言中插入asm的語(yǔ)句
mega8 1K的ram有8K的rom,是開發(fā)8位的RTOS的一個(gè)理想的器件,并且我對(duì)它也比較熟悉。
寫UCOS的Jean J.Labrosse在他的書上有這樣一句話,“漸漸地,我自然會(huì)想到,寫個(gè)實(shí)時(shí)內(nèi)核直有那么難嗎?不就是不斷地保存,恢復(fù)CPU的那些寄存器嘛?!?nbsp;
好了,當(dāng)這一切準(zhǔn)備好后,我們就可以開始我們的Rtos for mega8的實(shí)驗(yàn)之旅了。
本文列出的例子,全部完整可用。只需要一個(gè)文件就可以編譯了。我相信,只要適當(dāng)可用,最簡(jiǎn)單的就是最好的,這樣可以排除一些不必要的干擾,讓大家專注到每一個(gè)過程的學(xué)習(xí)。
第一篇:函數(shù)的運(yùn)行
在一般的單片機(jī)系統(tǒng)中,是以前后臺(tái)的方式(大循環(huán)+中斷)來(lái)處理數(shù)據(jù)和作出反應(yīng)的。
例子如下:
makefile的設(shè)定:運(yùn)行WinAvr中的Mfile,設(shè)定如下
MCU Type: mega8
Optimization level: s
Debug format :AVR-COFF
C/C++ source file: 選譯要編譯的C文件
#include
void fun1(void)
{
unsigned char i=0;
while(1)
{
PORTB=i++;
PORTC=0x01<<(i%8);
}
}
int main(void)
{
fun1();
}
首先,提出一個(gè)問題:如果要調(diào)用一個(gè)函數(shù),真是只能以上面的方式進(jìn)行嗎?
相信學(xué)習(xí)過C語(yǔ)言的各位會(huì)回答,No!我們還有一種方式,就是“用函數(shù)指針變量調(diào)用函數(shù)”,如果大家都和我一樣,當(dāng)初的教科書是譚浩強(qiáng)先生的《C程序設(shè)計(jì)》的話,請(qǐng)找回書的第9.5節(jié)。
例子:用函數(shù)指針變量調(diào)用函數(shù)
#include
void fun1(void)
{
unsigned char i=0;
while(1)
{
PORTB=i++;
PORTC=0x01<<(i%8);
}
}
void (*pfun)(); //指向函數(shù)的指針
int main(void)
{
pfun=fun1; //
(*pfun)(); //運(yùn)行指針?biāo)赶虻暮瘮?shù)
}
第二種,是“把指向函數(shù)的指針變量作函數(shù)參數(shù)”
#include
void fun1(void)
{
unsigned char i=0;
while(1)
{
PORTB=i++;
PORTC=0x01<<(i%8);
}
}
void RunFun(void (*pfun)()) //獲得了要傳遞的函數(shù)的地址
{
(*pfun)(); //在RunFun中,運(yùn)行指針?biāo)赶虻暮瘮?shù)
}
int main(void)
{
RunFun(fun1); //將函數(shù)的指針作為變量傳遞
}
看到上面的兩種方式,很多人可能會(huì)說(shuō),“這的確不錯(cuò)”,但是這樣與我們想要的RTOS,有什么關(guān)系呢?各位請(qǐng)細(xì)心向下看。
以下是GCC對(duì)上面的代碼的編譯的情況:
對(duì)main()中的RunFun(fun1); 的編譯如下
ldi r24,lo8(pm(fun1))
ldi r25,hi8(pm(fun1))
rcall RunFun
對(duì)void RunFun(void (*pfun)())的編譯如下
/*void RunFun(void (*pfun)())*/
/*(*pfun)();*/
.LM6:
movw r30,r24
icall
ret
在調(diào)用void RunFun(void (*pfun)())的時(shí)候,的確可以把fun1的地址通過r24和r25傳遞給RunFun()。但是,RTOS如何才能有效地利用函數(shù)的地址呢?
第二篇: 人工堆棧
在單片機(jī)的指令集中,一類指令是專門與堆棧和PC指針打道的,它們是
rcall 相對(duì)調(diào)用子程序指令
icall 間接調(diào)用子程序指令
ret 子程序返回指令
reti 中斷返回指令
對(duì)于ret和reti,它們都可以將堆棧棧頂?shù)膬蓚€(gè)字節(jié)被彈出來(lái)送入程序計(jì)數(shù)器PC中,一般用來(lái)從子程序或中斷中退出。其中reti還可以在退出中斷時(shí),重開全局中斷使能。
有了這個(gè)基礎(chǔ),就可以建立我們的人工堆棧了。
例:
#include
void fun1(void)
{
unsigned char i=0;
while(1)
{
PORTB=i++;
PORTC=0x01<<(i%8);
}
}
unsigned char Stack[100]; //建立一個(gè)100字節(jié)的人工堆棧
void RunFunInNewStack(void (*pfun)(),unsigned char *pStack)
{
*pStack--=(unsigned int)pfun>>8; //將函數(shù)的地址高位壓入堆棧,
*pStack--=(unsigned int)pfun; //將函數(shù)的地址低位壓入堆棧,
SP=pStack; //將堆棧指針指向人工堆棧的棧頂
__asm__ __volatile__("RET
"); //返回并開中斷,開始運(yùn)行fun1()
}
int main(void)
{
RunFunInNewStack(fun1,&Stack[99]);
}
RunFunInNewStack(),將指向函數(shù)的指針的值保存到一個(gè)unsigned char的數(shù)組Stack中,作為人工堆棧。并且將棧頂?shù)臄?shù)值傳遞組堆棧指針SP,因此當(dāng)用"ret"返回時(shí),從SP中恢復(fù)到PC中的值,就變?yōu)榱酥赶騠un1()的地址,開始運(yùn)行fun1().
上面例子中在RunFunInNewStack()的最后一句嵌入了匯編代碼 "ret",實(shí)際上是可以去除的。因?yàn)樵赗unFunInNewStack()返回時(shí),編譯器已經(jīng)會(huì)加上"ret"。我特意寫出來(lái),是為了讓大家看到用"ret"作為返回后運(yùn)行fun1()的過程。
第三篇:GCC中對(duì)寄存器的分配與使用
在很多用于AVR的RTOS中,都會(huì)有任務(wù)調(diào)度時(shí),插入以下的語(yǔ)句:
入棧:
__asm__ __volatile__("PUSH R0
");
__asm__ __volatile__("PUSH R1
");
......
__asm__ __volatile__("PUSH R31
");
出棧
__asm__ __volatile__("POP R31
");
......
__asm__ __volatile__("POP R1
");
__asm__ __volatile__("POP R0
");
通常大家都會(huì)認(rèn)為,在任務(wù)調(diào)度開始時(shí),當(dāng)然要將所有的通用寄存器都保存,并且還應(yīng)該保存程序狀態(tài)寄存器SREG。然后再根據(jù)相反的次序,將新任務(wù)的寄存器的內(nèi)容恢復(fù)。
但是,事實(shí)真的是這樣嗎?如果大家看過陳明計(jì)先生寫的small rots51,就會(huì)發(fā)現(xiàn),它所保存的通用寄存器不過是4組通用寄存器中的1組。
在Win AVR中的幫助文件 avr-libc Manual中的Related Pages中的Frequently Asked Questions,其實(shí)有一個(gè)問題是"What registers are used by the C compiler?" 回答了編譯器所需要占用的寄存器。一般情況下,編譯器會(huì)先用到以下寄存器
1 Call-used registers (r18-r27, r30-r31): 調(diào)用函數(shù)時(shí)作為參數(shù)傳遞,也就是用得最多的寄存器。
2 Call-saved registers (r2-r17, r28-r29): 調(diào)用函數(shù)時(shí)作為結(jié)果傳遞,當(dāng)中的r28和r29可能會(huì)被作為指向堆棧上的變量的指針。
3 Fixed registers (r0, r1): 固定作用。r0用于存放臨時(shí)數(shù)據(jù),r1用于存放0。
還有另一個(gè)問題是"How to permanently bind a variable to a register?",是將變量綁定到通用寄存器的方法。而且我發(fā)現(xiàn),如果將某個(gè)寄存器定義為變量,編譯器就會(huì)不將該寄存器分配作其它用途。這對(duì)RTOS是很重要的。
在"Inline Asm"中的"C Names Used in Assembler Code"明確表示,如果將太多的通用寄存器定義為變量,剛在編譯的過程中,被定義的變量依然可能被編譯器占用。
大家可以比較以下兩個(gè)例子,看看編譯器產(chǎn)生的代碼:(在*.lst文件中)
第一個(gè)例子:沒有定義通用寄存器為變量
#include
unsigned char add(unsigned char b,unsigned char c,unsigned char d)
{
return b+c*d;
}
int main(void)
{
unsigned char a=0;
while(1)
{
a++;
PORTB=add(a,a,a);
}
}
在本例中,"add(a,a,a);"被編譯如下:
mov r20,r28
mov r22,r28
mov r24,r28
rcall add
第二個(gè)例子:定義通用寄存器為變量
#include
unsigned char add(unsigned char b,unsigned char c,unsigned char d)
{
return b+c*d;
}
register unsigned char a asm("r20"); //將r20定義為 變量a
int main(void)
{
while(1)
{
a++;
PORTB=add(a,a,a);
}
}
在本例中,"add(a,a,a);"被編譯如下:
mov r22,r20
mov r24,r20
rcall add
當(dāng)然,在上面兩個(gè)例子中,有部份代碼被編譯器優(yōu)化了。
通過反復(fù)測(cè)試,發(fā)現(xiàn)編譯器一般使用如下寄存器:
第1類寄存器,第2類寄存器的r28,r29,第3類寄存器
如在中斷函數(shù)中有調(diào)用基它函數(shù),剛會(huì)在進(jìn)入中斷后,固定地將第1類寄存器和第3類寄存器入棧,在退出中斷又將它們出棧。
第四篇:只有延時(shí)服務(wù)的協(xié)作式的內(nèi)核
Cooperative Multitasking
前后臺(tái)系統(tǒng),協(xié)作式內(nèi)核系統(tǒng),與占先式內(nèi)核系統(tǒng),有什么不同呢?
記得在21IC上看過這樣的比喻,“你(小工)在用廁所,經(jīng)理在外面排第一,老板在外面排第二。如果是前后臺(tái),不管是誰(shuí),都必須按排隊(duì)的次序使用廁所;如果是協(xié)作式,那么可以等你用完廁所,老板就要比經(jīng)理先進(jìn)入;如果是占先式,只要有更高級(jí)的人在外面等,那么廁所里無(wú)論是誰(shuí),都要第一時(shí)間讓出來(lái),讓最高級(jí)別的人先用?!?
#include
#include
#include
unsigned char Stack[200];
register unsigned char OSRdyTbl asm("r2"); //任務(wù)運(yùn)行就緒表
register unsigned char OSTaskRunningPrio asm("r3"); //正在運(yùn)行的任務(wù)
#define OS_TASKS 3 //設(shè)定運(yùn)行任務(wù)的數(shù)量
struct TaskCtrBlock //任務(wù)控制塊
{
unsigned int OSTaskStackTop; //保存任務(wù)的堆棧頂
unsigned int OSWaitTick; //任務(wù)延時(shí)時(shí)鐘
} TCB[OS_TASKS+1];
//防止被編譯器占用
register unsigned char tempR4 asm("r4");
register unsigned char tempR5 asm("r5");
register unsigned char tempR6 asm("r6");
register unsigned char tempR7 asm("r7");
register unsigned char tempR8 asm("r8");
register unsigned char tempR9 asm("r9");
register unsigned char tempR10 asm("r10");
register unsigned char tempR11 asm("r11");
register unsigned char tempR12 asm("r12");
register unsigned char tempR13 asm("r13");
register unsigned char tempR14 asm("r14");
register unsigned char tempR15 asm("r15");
register unsigned char tempR16 asm("r16");
register unsigned char tempR16 asm("r17");
//建立任務(wù)
void OSTaskCreate(void (*Task)(void),unsigned char *Stack,unsigned char TaskID)
{
unsigned char i;
*Stack--=(unsigned int)Task>>8; //將任務(wù)的地址高位壓入堆棧,
*Stack--=(unsigned int)Task; //將任務(wù)的地址低位壓入堆棧,
*Stack--=0x00; //R1 __zero_reg__
*Stack--=0x00; //R0 __tmp_reg__
*Stack--=0x80; //SREG 在任務(wù)中,開啟全局中斷
for(i=0;i<14;i++) //在 avr-libc 中的 FAQ中的 What registers are used by the C compiler?
*Stack--=i; //描述了寄存器的作用
TCB[TaskID].OSTaskStackTop=(unsigned int)Stack; //將人工堆棧的棧頂,保存到堆棧的數(shù)組中
OSRdyTbl|=0x01<
//開始任務(wù)調(diào)度,從最低優(yōu)先級(jí)的任務(wù)的開始
void OSStartTask()
{
OSTaskRunningPrio=OS_TASKS;
SP=TCB[OS_TASKS].OSTaskStackTop+17;
__asm__ __volatile__( "reti" "
" );
}
//進(jìn)行任務(wù)調(diào)度
void OSSched(void)
{
// 根據(jù)中斷時(shí)保存寄存器的次序入棧,模擬一次中斷后,入棧的情況
__asm__ __volatile__("PUSH __zero_reg__
"); //R1
__asm__ __volatile__("PUSH __tmp_reg__
"); //R0
__asm__ __volatile__("IN __tmp_reg__,__SREG__
"); //保存狀態(tài)寄存器SREG
__asm__ __volatile__("PUSH __tmp_reg__
");
__asm__ __volatile__("CLR __zero_reg__
"); //R0重新清零
__asm__ __volatile__("PUSH R18
");
__asm__ __volatile__("PUSH R19
");
__asm__ __volatile__("PUSH R20
");
__asm__ __volatile__("PUSH R21
");
__asm__ __volatile__("PUSH R22
");
__asm__ __volatile__("PUSH R23
");
__asm__ __volatile__("PUSH R24
");
__asm__ __volatile__("PUSH R25
");
__asm__ __volatile__("PUSH R26
");
__asm__ __volatile__("PUSH R27
");
__asm__ __volatile__("PUSH R30
");
__asm__ __volatile__("PUSH R31
");
__asm__ __volatile__("PUSH R28
"); //R28與R29用于建立在堆棧上的指針
__asm__ __volatile__("PUSH R29
"); //入棧完成
TCB[OSTaskRunningPrio].OSTaskStackTop=SP; //將正在運(yùn)行的任務(wù)的堆棧底保存
unsigned char OSNextTaskID; //在現(xiàn)有堆棧上開設(shè)新的空間
for (OSNextTaskID = 0; //進(jìn)行任務(wù)調(diào)度
OSNextTaskID < OS_TASKS && !(OSRdyTbl & (0x01<
OSTaskRunningPrio = OSNextTaskID ;
cli(); //保護(hù)堆棧轉(zhuǎn)換
SP=TCB[OSTaskRunningPrio].OSTaskStackTop;
sei();
//根據(jù)中斷時(shí)的出棧次序
__asm__ __volatile__("POP R29
");
__asm__ __volatile__("POP R28
");
__asm__ __volatile__("POP R31
");
__asm__ __volatile__("POP R30
");
__asm__ __volatile__("POP R27
");
__asm__ __volatile__("POP R26
");
__asm__ __volatile__("POP R25
");
__asm__ __volatile__("POP R24
");
__asm__ __volatile__("POP R23
");
__asm__ __volatile__("POP R22
");
__asm__ __volatile__("POP R21
");
__asm__ __volatile__("POP R20
");
__asm__ __volatile__("POP R19
");
__asm__ __volatile__("POP R18
");
__asm__ __volatile__("POP __tmp_reg__
"); //SERG 出棧并恢復(fù)
__asm__ __volatile__("OUT __SREG__,__tmp_reg__
"); //
__asm__ __volatile__("POP __tmp_reg__
"); //R0 出棧
__asm__ __volatile__("POP __zero_reg__
"); //R1 出棧
//中斷時(shí)出棧完成
}
void OSTimeDly(unsigned int ticks)
{
if(ticks) //當(dāng)延時(shí)有效
{
OSRdyTbl &= ~(0x01<
OSSched(); //從新調(diào)度
}
}
void TCN0Init(void) // 計(jì)時(shí)器0
{
TCCR0 = 0;
TCCR0 |= (1<
}
SIGNAL(SIG_OVERFLOW0)
{
unsigned char i;
for(i=0;i
if(TCB[i].OSWaitTick)
{
TCB[i].OSWaitTick--;
if(TCB[i].OSWaitTick==0) //當(dāng)任務(wù)時(shí)鐘到時(shí),必須是由定時(shí)器減時(shí)的才行
{
OSRdyTbl |= (0x01< }
}
}
TCNT0=100;
}
void Task0()
{
unsigned int j=0;
while(1)
{
PORTB=j++;
OSTimeDly(2);
}
}
void Task1()
{
unsigned int j=0;
while(1)
{
PORTC=j++;
OSTimeDly(4);
}
}
void Task2()
{
unsigned int j=0;
while(1)
{
PORTD=j++;
OSTimeDly(8);
}
}
void TaskScheduler()
{
while(1)
{
OSSched(); //反復(fù)進(jìn)行調(diào)度
}
}
int main(void)
{
TCN0Init();
OSRdyTbl=0;
OSTaskRunningPrio=0;
OSTaskCreate(Task0,&Stack[49],0);
OSTaskCreate(Task1,&Stack[99],1);
OSTaskCreate(Task2,&Stack[149],2);
OSTaskCreate(TaskScheduler,&Stack[199],OS_TASKS);
OSStartTask();
}
在上面的例子中,一切變得很簡(jiǎn)單,三個(gè)正在運(yùn)行的主任務(wù),都通過延時(shí)服務(wù),主動(dòng)放棄對(duì)CPU的控制權(quán)。
在時(shí)間中斷中,對(duì)各個(gè)任務(wù)的的延時(shí)進(jìn)行計(jì)時(shí),如果某個(gè)任務(wù)的延時(shí)結(jié)束,將任務(wù)重新在就緒表中置位。
最低級(jí)的系統(tǒng)任務(wù)TaskScheduler(),在三個(gè)主任務(wù)在放棄對(duì)CPU的控制權(quán)后開始不斷地進(jìn)行調(diào)度。如果某個(gè)任務(wù)在就緒表中置位,通過調(diào)度,進(jìn)入最高級(jí)別的任務(wù)中繼續(xù)運(yùn)行。
第五篇: 完善的協(xié)作式的內(nèi)核
現(xiàn)在為上面的協(xié)作式內(nèi)核添加一些OS中所必須的服務(wù):
1 掛起和重新運(yùn)行任務(wù)
2 信號(hào)量(在必要時(shí)候,可以擴(kuò)展成郵箱和信息隊(duì)列)
3 延時(shí)
#include
#include
#include
unsigned char Stack[400];
register unsigned char OSRdyTbl asm("r2"); //任務(wù)運(yùn)行就緒表
register unsigned char OSTaskRunningPrio asm("r3"); //正在運(yùn)行的任務(wù)
#define OS_TASKS 3 //設(shè)定運(yùn)行任務(wù)的數(shù)量
struct TaskCtrBlock
{
unsigned int OSTaskStackTop; //保存任務(wù)的堆棧頂
unsigned int OSWaitTick; //任務(wù)延時(shí)時(shí)鐘
} TCB[OS_TASKS+1];
//防止被編譯器占用
register unsigned char tempR4 asm("r4");
register unsigned char tempR5 asm("r5");
register unsigned char tempR6 asm("r6");
register unsigned char tempR7 asm("r7");
register unsigned char tempR8 asm("r8");
register unsigned char tempR9 asm("r9");
register unsigned char tempR10 asm("r10");
register unsigned char tempR11 asm("r11");
register unsigned char tempR12 asm("r12");
register unsigned char tempR13 asm("r13");
register unsigned char tempR14 asm("r14");
register unsigned char tempR15 asm("r15");
register unsigned char tempR16 asm("r16");
register unsigned char tempR16 asm("r17");
//建立任務(wù)
void OSTaskCreate(void (*Task)(void),unsigned char *Stack,unsigned char TaskID)
{
unsigned char i;
*Stack--=(unsigned int)Task>>8; //將任務(wù)的地址高位壓入堆棧,
*Stack--=(unsigned int)Task; //將任務(wù)的地址低位壓入堆棧,
*Stack--=0x00; //R1 __zero_reg__
*Stack--=0x00; //R0 __tmp_reg__
*Stack--=0x80;
//SREG 在任務(wù)中,開啟全局中斷
for(i=0;i<14;i++) //在 avr-libc 中的 FAQ中的 What registers are used by the C compiler?
*Stack--=i; //描述了寄存器的作用
TCB[TaskID].OSTaskStackTop=(unsigned int)Stack; //將人工堆棧的棧頂,保存到堆棧的數(shù)組中
OSRdyTbl|=0x01<
//開始任務(wù)調(diào)度,從最低優(yōu)先級(jí)的任務(wù)的開始
void OSStartTask()
{
OSTaskRunningPrio=OS_TASKS;
SP=TCB[OS_TASKS].OSTaskStackTop+17;
__asm__ __volatile__( "reti" "
" );
}
//進(jìn)行任務(wù)調(diào)度
void OSSched(void)
{
// 根據(jù)中斷時(shí)保存寄存器的次序入棧,模擬一次中斷后,入棧的情況
__asm__ __volatile__("PUSH __zero_reg__
"); //R1
__asm__ __volatile__("PUSH __tmp_reg__
"); //R0
__asm__ __volatile__("IN __tmp_reg__,__SREG__
"); //保存狀態(tài)寄存器SREG
__asm__ __volatile__("PUSH __tmp_reg__
");
__asm__ __volatile__("CLR __zero_reg__
"); //R0重新清零
__asm__ __volatile__("PUSH R18
");
__asm__ __volatile__("PUSH R19
");
__asm__ __volatile__("PUSH R20
");
__asm__ __volatile__("PUSH R21
");
__asm__ __volatile__("PUSH R22
");
__asm__ __volatile__("PUSH R23
");
__asm__ __volatile__("PUSH R24
");
__asm__ __volatile__("PUSH R25
");
__asm__ __volatile__("PUSH R26
");
__asm__ __volatile__("PUSH R27
");
__asm__ __volatile__("PUSH R30
");
__asm__ __volatile__("PUSH R31
");
__asm__ __volatile__("PUSH R28
"); //R28與R29用于建立在堆棧上的指針
__asm__ __volatile__("PUSH R29
"); //入棧完成
TCB[OSTaskRunningPrio].OSTaskStackTop=SP; //將正在運(yùn)行的任務(wù)的堆棧底保存
unsigned char OSNextTaskID; //在現(xiàn)有堆棧上開設(shè)新的空間
for (OSNextTaskID = 0; //進(jìn)行任務(wù)調(diào)度
OSNextTaskID < OS_TASKS && !(OSRdyTbl & (0x01<
OSTaskRunningPrio = OSNextTaskID ;
cli(); //保護(hù)堆棧轉(zhuǎn)換
SP=TCB[OSTaskRunningPrio].OSTaskStackTop;
sei();
//根據(jù)中斷時(shí)的出棧次序
__asm__ __volatile__("POP R29
");
__asm__ __volatile__("POP R28
");
__asm__ __volatile__("POP R31
");
__asm__ __volatile__("POP R30
");
__asm__ __volatile__("POP R27
");
__asm__ __volatile__("POP R26
");
__asm__ __volatile__("POP R25
");
__asm__ __volatile__("POP R24
");
__asm__ __volatile__("POP R23
");
__asm__ __volatile__("POP R22
");
__asm__ __volatile__("POP R21
");
__asm__ __volatile__("POP R20
");
__asm__ __volatile__("POP R19
");
__asm__ __volatile__("POP R18
");
__asm__ __volatile__("POP __tmp_reg__
"); //SERG 出棧并恢復(fù)
__asm__ __volatile__("OUT __SREG__,__tmp_reg__
"); //
__asm__ __volatile__("POP __tmp_reg__
"); //R0 出棧
__asm__ __volatile__("POP __zero_reg__
"); //R1 出棧
//中斷時(shí)出棧完成
}
////////////////////////////////////////////任務(wù)處理
//掛起任務(wù)
void OSTaskSuspend(unsigned char prio)
{
TCB[prio].OSWaitTick=0;
OSRdyTbl &= ~(0x01<
OSSched(); //從新調(diào)度
}
//恢復(fù)任務(wù) 可以讓被OSTaskSuspend或 OSTimeDly暫停的任務(wù)恢復(fù)
void OSTaskResume(unsigned char prio)
{
OSRdyTbl |= 0x01<
if(OSTaskRunningPrio>prio) //當(dāng)要當(dāng)前任務(wù)的優(yōu)先級(jí)低于重置位的任務(wù)的優(yōu)先級(jí)
OSSched(); //從新調(diào)度 //從新調(diào)度
}
// 任務(wù)延時(shí)
void OSTimeDly(unsigned int ticks)
{
if(ticks) //當(dāng)延時(shí)有效
{
OSRdyTbl &= ~(0x01<
OSSched(); //從新調(diào)度
}
}
//信號(hào)量
struct SemBlk
{
unsigned char OSEventType; //型號(hào) 0,信號(hào)量獨(dú)占型;1信號(hào)量共享型
unsigned char OSEventState; //狀態(tài) 0,不可用;1,可用
unsigned char OSTaskPendTbl; //等待信號(hào)量的任務(wù)列表
} Sem[10];
//初始化信號(hào)量
void OSSemCreat(unsigned char Index,unsigned char Type)
{
Sem[Index].OSEventType=Type; //型號(hào) 0,信號(hào)量獨(dú)占型;1信號(hào)量共享型
Sem[Index].OSTaskPendTbl=0;
Sem[Index].OSEventState=0;
}
//任務(wù)等待信號(hào)量,掛起
unsigned char OSTaskSemPend(unsigned char Index,unsigned int Timeout)
{
//unsigned char i=0;
if(Sem[Index].OSEventState) //信號(hào)量有效
{
if(Sem[Index].OSEventType==0) //如果為獨(dú)占型
Sem[Index].OSEventState = 0x00; //信號(hào)量被獨(dú)占,不可用
}
else
{ //加入信號(hào)的任務(wù)等待表
Sem[Index].OSTaskPendTbl |= 0x01<
OSSched(); //從新調(diào)度
if(TCB[OSTaskRunningPrio].OSWaitTick==0) return 0;
}
return 1;
}
//發(fā)送一個(gè)信號(hào)量,可以從任務(wù)或中斷發(fā)送
void OSSemPost(unsigned char Index)
{
if(Sem[Index].OSEventType) //當(dāng)要求的信號(hào)量是共享型
{
Sem[Index].OSEventState=0x01; //使信號(hào)量有效
OSRdyTbl |=Sem [Index].OSTaskPendTbl; //使在等待該信號(hào)的所有任務(wù)就緒
Sem[Index].OSTaskPendTbl=0; //清空所有等待該信號(hào)的等待任務(wù)
}
else //當(dāng)要求的信號(hào)量為獨(dú)占型
{
unsigned char i;
for (i = 0; i < OS_TASKS && !(Sem[Index].OSTaskPendTbl & (0x01< if(i < OS_TASKS) //如果有任務(wù)需要
{
Sem[Index].OSTaskPendTbl &= ~(0x01< OSRdyTbl |= 0x01< }
else
{
Sem[Index].OSEventState =1; //使信號(hào)量有效
}
}
}
//從任務(wù)發(fā)送一個(gè)信號(hào)量,并進(jìn)行調(diào)度
void OSTaskSemPost(unsigned char Index)
{
OSSemPost(Index);
OSSched();
}
//清除一個(gè)信號(hào)量,只對(duì)共享型的有用。
//對(duì)于獨(dú)占型的信號(hào)量,在任務(wù)占用后,就交得不可以用了。
void OSSemClean(unsigned char Index)
{
Sem[Index].OSEventState =0; //要求的信號(hào)量無(wú)效
}
void TCN0Init(void) // 計(jì)時(shí)器0
{
TCCR0 = 0;
TCCR0 |= (1<
}
SIGNAL(SIG_OVERFLOW0)
{
unsigned char i;
for(i=0;i
if(TCB[i].OSWaitTick)
{
TCB[i].OSWaitTick--;
if(TCB[i].OSWaitTick==0) //當(dāng)任務(wù)時(shí)鐘到時(shí),必須是由定時(shí)器減時(shí)的才行
{
OSRdyTbl |= (0x01< }
}
}
TCNT0=100;
}
void Task0()
{
unsigned int j=0;
while(1)
{
PORTB=j++;
OSTaskSuspend(1); //掛起任務(wù)1
OSTaskSemPost(0);
OSTimeDly(50);
OSTaskResume(1); //恢復(fù)任務(wù)1
OSSemClean(0);
OSTimeDly(50);
}
}
void Task1()
{
unsigned int j=0;
while(1)
{
PORTC=j++;
OSTimeDly(5);
}
}
void Task2()
{
unsigned int j=0;
while(1)
{
OSTaskSemPend(0,10);
PORTD=j++;
OSTimeDly(5);
}
}
void TaskScheduler()
{
while(1)
{
OSSched(); //反復(fù)進(jìn)行調(diào)度
}
}
int main(void)
{
TCN0Init();
OSRdyTbl=0;
OSSemCreat(0,1); //將信號(hào)量設(shè)為共享型
OSTaskCreate(Task0,&Stack[99],0);
OSTaskCreate(Task1,&Stack[199],1);
OSTaskCreate(Task2,&Stack[299],2);
OSTaskCreate(TaskScheduler,&Stack[399],OS_TASKS);
OSStartTask();
}
第六篇:時(shí)間片輪番調(diào)度法的內(nèi)核
Round-Robin Sheduling
時(shí)間片輪調(diào)法是非常有趣的。本篇中的例子,建立了3個(gè)任務(wù),任務(wù)沒有優(yōu)先級(jí),在時(shí)間中斷的調(diào)度下,每個(gè)任務(wù)都輪流運(yùn)行相同的時(shí)間。如果在內(nèi)核中沒有加入其它服務(wù),感覺上就好像是有三個(gè)大循環(huán)在同時(shí)運(yùn)行。
本例只是提供了一個(gè)用時(shí)間中斷進(jìn)行調(diào)度的內(nèi)核,大家可以根據(jù)自己的需要,添加相應(yīng)的服務(wù)。
要注意到:
1,由于在時(shí)間中斷內(nèi)調(diào)用了任務(wù)切換函數(shù),因?yàn)樵谶M(jìn)入中斷時(shí),已經(jīng)將一系列的寄存器入棧。
2,在中斷內(nèi)進(jìn)行調(diào)度,是直接通過"RJMP Int_OSSched"進(jìn)入任務(wù)切換和調(diào)度的,這是GCC AVR的一個(gè)特點(diǎn),為用C編寫內(nèi)核提供了極大的方便。
3,在閱讀代碼的同時(shí),請(qǐng)對(duì)照閱讀編譯器產(chǎn)生的 *.lst文件,會(huì)對(duì)你理解例子有很大的幫助。
#include
#include
#include
unsigned char Stack[400];
register unsigned char OSRdyTbl asm("r2"); //任務(wù)運(yùn)行就緒表
register unsigned char OSTaskRunningPrio asm("r3"); //正在運(yùn)行的任務(wù)
#define OS_TASKS 3 //設(shè)定運(yùn)行任務(wù)的數(shù)量
struct TaskCtrBlock
{
unsigned int OSTaskStackTop; //保存任務(wù)的堆棧頂
unsigned int OSWaitTick; //任務(wù)延時(shí)時(shí)鐘
} TCB[OS_TASKS+1];
//防止被編譯器占用
register unsigned char tempR4 asm("r4");
register unsigned char tempR5 asm("r5");
register unsigned char tempR6 asm("r6");
register unsigned char tempR7 asm("r7");
register unsigned char tempR8 asm("r8");
register unsigned char tempR9 asm("r9");
register unsigned char tempR10 asm("r10");
register unsigned char tempR11 asm("r11");
register unsigned char tempR12 asm("r12");
register unsigned char tempR13 asm("r13");
register unsigned char tempR14 asm("r14");
register unsigned char tempR15 asm("r15");
register unsigned char tempR16 asm("r16");
register unsigned char tempR16 asm("r17");
//建立任務(wù)
void OSTaskCreate(void (*Task)(void),unsigned char *Stack,unsigned char TaskID)
{
unsigned char i;
*Stack--=(unsigned int)Task>>8; //將任務(wù)的地址高位壓入堆棧,
*Stack--=(unsigned int)Task; //將任務(wù)的地址低位壓入堆棧,
*Stack--=0x00; //R1 __zero_reg__
*Stack--=0x00; //R0 __tmp_reg__
*Stack--=0x80;
//SREG 在任務(wù)中,開啟全局中斷
for(i=0;i<14;i++) //在 avr-libc 中的 FAQ中的 What registers are used by the C compiler?
*Stack--=i; //描述了寄存器的作用
TCB[TaskID].OSTaskStackTop=(unsigned int)Stack; //將人工堆棧的棧頂,保存到堆棧的數(shù)組中
OSRdyTbl|=0x01<
//開始任務(wù)調(diào)度,從最低優(yōu)先級(jí)的任務(wù)的開始
void OSStartTask()
{
OSTaskRunningPrio=OS_TASKS;
SP=TCB[OS_TASKS].OSTaskStackTop+17;
__asm__ __volatile__( "reti" "
" );
}
//進(jìn)行任務(wù)調(diào)度
void OSSched(void)
{
// 根據(jù)中斷時(shí)保存寄存器的次序入棧,模擬一次中斷后,入棧的情況
__asm__ __volatile__("PUSH __zero_reg__
"); //R1
__asm__ __volatile__("PUSH __tmp_reg__
"); //R0
__asm__ __volatile__("IN __tmp_reg__,__SREG__
"); //保存狀態(tài)寄存器SREG
__asm__ __volatile__("PUSH __tmp_reg__
");
__asm__ __volatile__("CLR __zero_reg__
"); //R0重新清零
__asm__ __volatile__("PUSH R18
");
__asm__ __volatile__("PUSH R19
");
__asm__ __volatile__("PUSH R20
");
__asm__ __volatile__("PUSH R21
");
__asm__ __volatile__("PUSH R22
");
__asm__ __volatile__("PUSH R23
");
__asm__ __volatile__("PUSH R24
");
__asm__ __volatile__("PUSH R25
");
__asm__ __volatile__("PUSH R26
");
__asm__ __volatile__("PUSH R27
");
__asm__ __volatile__("PUSH R30
");
__asm__ __volatile__("PUSH R31
");
__asm__ __volatile__("Int_OSSched:
"); //當(dāng)中斷要求調(diào)度,直接進(jìn)入這里
__asm__ __volatile__("PUSH R28
"); //R28與R29用于建立在堆棧上的指針
__asm__ __volatile__("PUSH R29
"); //入棧完成
TCB[OSTaskRunningPrio].OSTaskStackTop=SP; //將正在運(yùn)行的任務(wù)的堆棧底保存
if(++OSTaskRunningPrio>=OS_TASKS) //輪流運(yùn)行各個(gè)任務(wù),沒有優(yōu)先級(jí)
OSTaskRunningPrio=0;
//cli(); //保護(hù)堆棧轉(zhuǎn)換
SP=TCB[OSTaskRunningPrio].OSTaskStackTop;
//sei();
//根據(jù)中斷時(shí)的出棧次序
__asm__ __volatile__("POP R29
");
__asm__ __volatile__("POP R28
");
__asm__ __volatile__("POP R31
");
__asm__ __volatile__("POP R30
");
__asm__ __volatile__("POP R27
");
__asm__ __volatile__("POP R26
");
__asm__ __volatile__("POP R25
");
__asm__ __volatile__("POP R24
");
__asm__ __volatile__("POP R23
");
__asm__ __volatile__("POP R22
");
__asm__ __volatile__("POP R21
");
__asm__ __volatile__("POP R20
");
__asm__ __volatile__("POP R19
");
__asm__ __volatile__("POP R18
");
__asm__ __volatile__("POP __tmp_reg__
"); //SERG 出棧并恢復(fù)
__asm__ __volatile__("OUT __SREG__,__tmp_reg__
"); //
__asm__ __volatile__("POP __tmp_reg__
"); //R0 出棧
__asm__ __volatile__("POP __zero_reg__
"); //R1 出棧
__asm__ __volatile__("RETI
"); //返回并開中斷
//中斷時(shí)出棧完成
}
void IntSwitch(void)
{
__asm__ __volatile__("POP R31
"); //去除因調(diào)用子程序而入棧的PC
__asm__ __volatile__("POP R31
");
__asm__ __volatile__("RJMP Int_OSSched
"); //重新調(diào)度
}
void TCN0Init(void) // 計(jì)時(shí)器0
{
TCCR0 = 0;
TCCR0 |= (1<
}
SIGNAL(SIG_OVERFLOW0)
{
TCNT0=100;
IntSwitch(); //任務(wù)調(diào)度
}
void Task0()
{
unsigned int j=0;
while(1)
{
PORTB=j++;
//OSTimeDly(50);
}
}
void Task1()
{
unsigned int j=0;
while(1)
{
PORTC=j++;
//OSTimeDly(5);
}
}
void Task2()
{
unsigned int j=0;
while(1)
{
PORTD=j++;
//OSTimeDly(5);
}
}
void TaskScheduler()
{
while(1)
{
OSSched(); //反復(fù)進(jìn)行調(diào)度
}
}
int main(void)
{
TCN0Init();
OSRdyTbl=0;
OSTaskCreate(Task0,&Stack[99],0);
OSTaskCreate(Task1,&Stack[199],1);
OSTaskCreate(Task2,&Stack[299],2);
OSTaskCreate(TaskScheduler,&Stack[399],OS_TASKS);
OSStartTask();
}
第七篇:占先式內(nèi)核(只帶延時(shí)服務(wù))
Preemptive Multitasking
當(dāng)大家理解時(shí)間片輪番調(diào)度法的任務(wù)調(diào)度方式后,占先式的內(nèi)核的原理,已經(jīng)伸手可及了。
先想想,占先式內(nèi)核是在什么地方實(shí)現(xiàn)任務(wù)調(diào)度的呢?對(duì)了,它在可以在任務(wù)中進(jìn)行調(diào)度,這個(gè)在協(xié)作式的內(nèi)核中已經(jīng)做到了;同時(shí),它也可以在中斷結(jié)束后進(jìn)行調(diào)度,這個(gè)問題,已經(jīng)在時(shí)間片輪番調(diào)度法中已經(jīng)做到了。
由于中斷是可以嵌套的,只有當(dāng)各層嵌套中要求調(diào)度,并且中斷嵌套返回到最初進(jìn)入的中斷的那一層時(shí),才能進(jìn)行任務(wù)調(diào)度。
#include
#include
#include
unsigned char Stack[400];
register unsigned char OSRdyTbl asm("r2"); //任務(wù)運(yùn)行就緒表
register unsigned char OSTaskRunningPrio asm("r3"); //正在運(yùn)行的任務(wù)
register unsigned char IntNum asm("r4"); //中斷嵌套計(jì)數(shù)器
//只有當(dāng)中斷嵌套數(shù)為0,并且有中斷要求時(shí),才能在退出中斷時(shí),進(jìn)行任務(wù)調(diào)度
register unsigned char OSCoreState asm("r16"); // 系統(tǒng)核心標(biāo)志位 ,R16 編譯器沒有使用
//只有大于R15的寄存器才能直接賦值 例LDI R16,0x01
//0x01 正在任務(wù) 切換 0x02 有中斷要求切換
#define OS_TASKS 3 //設(shè)定運(yùn)行任務(wù)的數(shù)量
struct TaskCtrBlock
{
unsigned int OSTaskStackTop; //保存任務(wù)的堆棧頂
unsigned int OSWaitTick; //任務(wù)延時(shí)時(shí)鐘
} TCB[OS_TASKS+1];
//防止被編譯器占用
//register unsigned char tempR4 asm("r4");
register unsigned char tempR5 asm("r5");
register unsigned char tempR6 asm("r6");
register unsigned char tempR7 asm("r7");
register unsigned char tempR8 asm("r8");
register unsigned char tempR9 asm("r9");
register unsigned char tempR10 asm("r10");
register unsigned char tempR11 asm("r11");
register unsigned char tempR12 asm("r12");
register unsigned char tempR13 asm("r13");
register unsigned char tempR14 asm("r14");
register unsigned char tempR15 asm("r15");
//register unsigned char tempR16 asm("r16");
register unsigned char tempR16 asm("r17");
//建立任務(wù)
void OSTaskCreate(void (*Task)(void),unsigned char *Stack,unsigned char TaskID)
{
unsigned char i;
*Stack--=(unsigned int)Task>>8; //將任務(wù)的地址高位壓入堆棧,
*Stack--=(unsigned int)Task; //將任務(wù)的地址低位壓入堆棧,
*Stack--=0x00; //R1 __zero_reg__
*Stack--=0x00; //R0 __tmp_reg__
*Stack--=0x80;
//SREG 在任務(wù)中,開啟全局中斷
for(i=0;i<14;i++) //在 avr-libc 中的 FAQ中的 What registers are used by the C compiler?
*Stack--=i; //描述了寄存器的作用
TCB[TaskID].OSTaskStackTop=(unsigned int)Stack; //將人工堆棧的棧頂,保存到堆棧的數(shù)組中
OSRdyTbl|=0x01<
//開始任務(wù)調(diào)度,從最低優(yōu)先級(jí)的任務(wù)的開始
void OSStartTask()
{
OSTaskRunningPrio=OS_TASKS;
SP=TCB[OS_TASKS].OSTaskStackTop+17;
__asm__ __volatile__( "reti" "
" );
}
//進(jìn)行任務(wù)調(diào)度
void OSSched(void)
{
__asm__ __volatile__("LDI R16,0x01
");
//清除中斷要求任務(wù)切換的標(biāo)志位,設(shè)置正在任務(wù)切換標(biāo)志位
__asm__ __volatile__("SEI
");
//開中斷,因?yàn)槿绻蛑袛嘣谌蝿?wù)調(diào)度中進(jìn)行,要重新進(jìn)行調(diào)度時(shí),已經(jīng)關(guān)中斷
//根據(jù)中斷時(shí)保存寄存器的次序入棧,模擬一次中斷后,入棧的情況
__asm__ __volatile__("PUSH __zero_reg__
"); //R1
__asm__ __volatile__("PUSH __tmp_reg__
"); //R0
__asm__ __volatile__("IN __tmp_reg__,__SREG__
"); //保存狀態(tài)寄存器SREG
__asm__ __volatile__("PUSH __tmp_reg__
");
__asm__ __volatile__("CLR __zero_reg__
"); //R0重新清零
__asm__ __volatile__("PUSH R18
");
__asm__ __volatile__("PUSH R19
");
__asm__ __volatile__("PUSH R20
");
__asm__ __volatile__("PUSH R21
");
__asm__ __volatile__("PUSH R22
");
__asm__ __volatile__("PUSH R23
");
__asm__ __volatile__("PUSH R24
");
__asm__ __volatile__("PUSH R25
");
__asm__ __volatile__("PUSH R26
");
__asm__ __volatile__("PUSH R27
");
__asm__ __volatile__("PUSH R30
");
__asm__ __volatile__("PUSH R31
");
__asm__ __volatile__("Int_OSSched:
"); //當(dāng)中斷要求調(diào)度,直接進(jìn)入這里
__asm__ __volatile__("SEI
");
//開中斷,因?yàn)槿绻蛑袛嘣谌蝿?wù)調(diào)度中進(jìn)行,已經(jīng)關(guān)中斷
__asm__ __volatile__("PUSH R28
"); //R28與R29用于建立在堆棧上的指針
__asm__ __volatile__("PUSH R29
"); //入棧完成
TCB[OSTaskRunningPrio].OSTaskStackTop=SP; //將正在運(yùn)行的任務(wù)的堆棧底保存
unsigned char OSNextTaskPrio; //在現(xiàn)有堆棧上開設(shè)新的空間
for (OSNextTaskPrio = 0; //進(jìn)行任務(wù)調(diào)度
OSNextTaskPrio < OS_TASKS && !(OSRdyTbl & (0x01<
OSTaskRunningPrio = OSNextTaskPrio ;
cli(); //保護(hù)堆棧轉(zhuǎn)換
SP=TCB[OSTaskRunningPrio].OSTaskStackTop;
sei();
//根據(jù)中斷時(shí)的出棧次序
__asm__ __volatile__("POP R29
");
__asm__ __volatile__("POP R28
");
__asm__ __volatile__("POP R31
");
__asm__ __volatile__("POP R30
");
__asm__ __volatile__("POP R27
");
__asm__ __volatile__("POP R26
");
__asm__ __volatile__("POP R25
");
__asm__ __volatile__("POP R24
");
__asm__ __volatile__("POP R23
");
__asm__ __volatile__("POP R22
");
__asm__ __volatile__("POP R21
");
__asm__ __volatile__("POP R20
");
__asm__ __volatile__("POP R19
");
__asm__ __volatile__("POP R18
");
__asm__ __volatile__("POP __tmp_reg__
"); //SERG 出棧并恢復(fù)
__asm__ __volatile__("OUT __SREG__,__tmp_reg__
"); //
__asm__ __volatile__("POP __tmp_reg__
"); //R0 出棧
__asm__ __volatile__("POP __zero_reg__
"); //R1 出棧
//中斷時(shí)出棧完成
__asm__ __volatile__("CLI
"); //關(guān)中斷
__asm__ __volatile__("SBRC R16,1
"); //SBRC當(dāng)寄存器位為0剛跳過下一條指令
//檢查是在調(diào)度時(shí),是否有中斷要求任務(wù)調(diào)度 0x02是中斷要求調(diào)度的標(biāo)志位
__asm__ __volatile__("RJMP OSSched
"); //重新調(diào)度
__asm__ __volatile__("LDI R16,0x00
");
//清除中斷要求任務(wù)切換的標(biāo)志位,清除正在任務(wù)切換標(biāo)志位
__asm__ __volatile__("RETI
"); //返回并開中斷
}
//從中斷退出并進(jìn)行調(diào)度
void IntSwitch(void)
{
//當(dāng)中斷無(wú)嵌套,并且沒有在切換任務(wù)的過程中,直接進(jìn)行任務(wù)切換
if(OSCoreState == 0x02 && IntNum==0)
{
//進(jìn)入中斷時(shí),已經(jīng)保存了SREG和R0,R1,R18~R27,R30,R31
__asm__ __volatile__("POP R31
"); //去除因調(diào)用子程序而入棧的PC
__asm__ __volatile__("POP R31
");
__asm__ __volatile__("LDI R16,0x01
");
//清除中斷要求任務(wù)切換的標(biāo)志位,設(shè)置正在任務(wù)切換標(biāo)志位
__asm__ __volatile__("RJMP Int_OSSched
"); //重新調(diào)度
}
}
// 任務(wù)延時(shí)
void OSTimeDly(unsigned int ticks)
{
if(ticks) //當(dāng)延時(shí)有效
{
OSRdyTbl &= ~(0x01<
OSSched(); //從新調(diào)度
}
}
void TCN0Init(void) // 計(jì)時(shí)器0
{
TCCR0 = 0;
TCCR0 |= (1<
}
SIGNAL(SIG_OVERFLOW0)
{
IntNum++; //中斷嵌套+1
sei(); //在中斷中,重開中斷
unsigned char i,j=0;
for(i=0;i
if(TCB[i].OSWaitTick)
{
TCB[i].OSWaitTick--;
if(TCB[i].OSWaitTick==0) //當(dāng)任務(wù)時(shí)鐘到時(shí),必須是由定時(shí)器減時(shí)的才行
{
OSRdyTbl |= (0x01< OSCoreState|=0x02; //要求任務(wù)切換的標(biāo)志位
}
}
}
TCNT0=100;
cli();
IntNum--; //中斷嵌套-1
IntSwitch(); //進(jìn)行任務(wù)調(diào)度
}
void Task0()
{
unsigned int j=0;
while(1)
{
PORTB=j++;
OSTimeDly(50);
}
}
void Task1()
{
unsigned int j=0;
while(1)
{
PORTC=j++;
OSTimeDly(20);
}
}
void Task2()
{
unsigned int j=0;
while(1)
{
PORTD=j++;
OSTimeDly(5);
}
}
void TaskScheduler()
{
OSSched();
while(1)
{
//OSSched(); //反復(fù)進(jìn)行調(diào)度
}
}
int main(void)
{
TCN0Init();
OSRdyTbl=0;
IntNum=0;
OSTaskCreate(Task0,&Stack[99],0);
OSTaskCreate(Task1,&Stack[199],1);
OSTaskCreate(Task2,&Stack[299],2);
OSTaskCreate(TaskScheduler,&Stack[399],OS_TASKS);
OSStartTask();
}
第八篇:占先式內(nèi)核(完善的服務(wù))
如果將前面所提到的占先式內(nèi)核和協(xié)作式內(nèi)核組合在一起,很容易就可以得到一個(gè)功能較為完善的占先式內(nèi)核,它的功能有:
1,掛起和恢復(fù)任務(wù)
2,任務(wù)延時(shí)
3,信號(hào)量(包括共享型和獨(dú)占型)
另外,在本例中,在各個(gè)任務(wù)中加入了從串口發(fā)送任務(wù)狀態(tài)的功能。
#include
#include
#include
unsigned char Stack[400];
register unsigned char OSRdyTbl asm("r2"); //任務(wù)運(yùn)行就緒表
register unsigned char OSTaskRunningPrio asm("r3"); //正在運(yùn)行的任務(wù)
register unsigned char IntNum asm("r4"); //中斷嵌套計(jì)數(shù)器
//只有當(dāng)中斷嵌套數(shù)為0,并且有中斷要求時(shí),才能在退出中斷時(shí),進(jìn)行任務(wù)調(diào)度
register unsigned char OSCoreState asm("r16"); // 系統(tǒng)核心標(biāo)志位 ,R16 編譯器沒有使用
//只有大于R15的寄存器才能直接賦值 例LDI R16,0x01
//0x01 正在任務(wù) 切換 0x02 有中斷要求切換
#define OS_TASKS 3 //設(shè)定運(yùn)行任務(wù)的數(shù)量
struct TaskCtrBlock
{
unsigned int OSTaskStackTop; //保存任務(wù)的堆棧頂
unsigned int OSWaitTick; //任務(wù)延時(shí)時(shí)鐘
} TCB[OS_TASKS+1];
//防止被編譯器占用
//register unsigned char tempR4 asm("r4");
register unsigned char tempR5 asm("r5");
register unsigned char tempR6 asm("r6");
register unsigned char tempR7 asm("r7");
register unsigned char tempR8 asm("r8");
register unsigned char tempR9 asm("r9");
register unsigned char tempR10 asm("r10");
register unsigned char tempR11 asm("r11");
register unsigned char tempR12 asm("r12");
register unsigned char tempR13 asm("r13");
register unsigned char tempR14 asm("r14");
register unsigned char tempR15 asm("r15");
//register unsigned char tempR16 asm("r16");
register unsigned char tempR16 asm("r17");
//建立任務(wù)
void OSTaskCreate(void (*Task)(void),unsigned char *Stack,unsigned char TaskID)
{
unsigned char i;
*Stack--=(unsigned int)Task>>8; //將任務(wù)的地址高位壓入堆棧,
*Stack--=(unsigned int)Task; //將任務(wù)的地址低位壓入堆棧,
*Stack--=0x00; //R1 __zero_reg__
*Stack--=0x00; //R0 __tmp_reg__
*Stack--=0x80;
//SREG 在任務(wù)中,開啟全局中斷
for(i=0;i<14;i++) //在 avr-libc 中的 FAQ中的 What registers are used by the C compiler?
*Stack--=i; //描述了寄存器的作用
TCB[TaskID].OSTaskStackTop=(unsigned int)Stack; //將人工堆棧的棧頂,保存到堆棧的數(shù)組中
OSRdyTbl|=0x01<
//開始任務(wù)調(diào)度,從最低優(yōu)先級(jí)的任務(wù)的開始
void OSStartTask()
{
OSTaskRunningPrio=OS_TASKS;
SP=TCB[OS_TASKS].OSTaskStackTop+17;
__asm__ __volatile__( "reti" "
" );
}
//進(jìn)行任務(wù)調(diào)度
void OSSched(void)
{
__asm__ __volatile__("LDI R16,0x01
");
//清除中斷要求任務(wù)切換的標(biāo)志位,設(shè)置正在任務(wù)切換標(biāo)志位
__asm__ __volatile__("SEI
");
//開中斷,因?yàn)槿绻蛑袛嘣谌蝿?wù)調(diào)度中進(jìn)行,要重新進(jìn)行調(diào)度時(shí),已經(jīng)關(guān)中斷
// 根據(jù)中斷時(shí)保存寄存器的次序入棧,模擬一次中斷后,入棧的情況
__asm__ __volatile__("PUSH __zero_reg__
"); //R1
__asm__ __volatile__("PUSH __tmp_reg__
"); //R0
__asm__ __volatile__("IN __tmp_reg__,__SREG__
"); //保存狀態(tài)寄存器SREG
__asm__ __volatile__("PUSH __tmp_reg__
");
__asm__ __volatile__("CLR __zero_reg__
"); //R0重新清零
__asm__ __volatile__("PUSH R18
");
__asm__ __volatile__("PUSH R19
");
__asm__ __volatile__("PUSH R20
");
__asm__ __volatile__("PUSH R21
");
__asm__ __volatile__("PUSH R22
");
__asm__ __volatile__("PUSH R23
");
__asm__ __volatile__("PUSH R24
");
__asm__ __volatile__("PUSH R25
");
__asm__ __volatile__("PUSH R26
");
__asm__ __volatile__("PUSH R27
");
__asm__ __volatile__("PUSH R30
");
__asm__ __volatile__("PUSH R31
");
__asm__ __volatile__("Int_OSSched:
"); //當(dāng)中斷要求調(diào)度,直接進(jìn)入這里
__asm__ __volatile__("SEI
");
//開中斷,因?yàn)槿绻蛑袛嘣谌蝿?wù)調(diào)度中進(jìn)行,已經(jīng)關(guān)中斷
__asm__ __volatile__("PUSH R28
"); //R28與R29用于建立在堆棧上的指針
__asm__ __volatile__("PUSH R29
"); //入棧完成
TCB[OSTaskRunningPrio].OSTaskStackTop=SP; //將正在運(yùn)行的任務(wù)的堆棧底保存
unsigned char OSNextTaskPrio; //在現(xiàn)有堆棧上開設(shè)新的空間
for (OSNextTaskPrio = 0; //進(jìn)行任務(wù)調(diào)度
OSNextTaskPrio < OS_TASKS && !(OSRdyTbl & (0x01<
OSTaskRunningPrio = OSNextTaskPrio ;
cli(); //保護(hù)堆棧轉(zhuǎn)換
SP=TCB[OSTaskRunningPrio].OSTaskStackTop;
sei();
//根據(jù)中斷時(shí)的出棧次序
__asm__ __volatile__("POP R29
");
__asm__ __volatile__("POP R28
");
__asm__ __volatile__("POP R31
");
__asm__ __volatile__("POP R30
");
__asm__ __volatile__("POP R27
");
__asm__ __volatile__("POP R26
");
__asm__ __volatile__("POP R25
");
__asm__ __volatile__("POP R24
");
__asm__ __volatile__("POP R23
");
__asm__ __volatile__("POP R22
");
__asm__ __volatile__("POP R21
");
__asm__ __volatile__("POP R20
");
__asm__ __volatile__("POP R19
");
__asm__ __volatile__("POP R18
");
__asm__ __volatile__("POP __tmp_reg__
"); //SERG 出棧并恢復(fù)
__asm__ __volatile__("OUT __SREG__,__tmp_reg__
"); //
__asm__ __volatile__("POP __tmp_reg__
"); //R0 出棧
__asm__ __volatile__("POP __zero_reg__
"); //R1 出棧
//中斷時(shí)出棧完成
__asm__ __volatile__("CLI
"); //關(guān)中斷
__asm__ __volatile__("SBRC R16,1
"); //SBRC當(dāng)寄存器位為0剛跳過下一條指令
//檢查是在調(diào)度時(shí),是否有中斷要求任務(wù)調(diào)度 0x02是中斷要求調(diào)度的標(biāo)志位
__asm__ __volatile__("RJMP OSSched
"); //重新調(diào)度
__asm__ __volatile__("LDI R16,0x00
");
//清除中斷要求任務(wù)切換的標(biāo)志位,清除正在任務(wù)切換標(biāo)志位
__asm__ __volatile__("RETI
"); //返回并開中斷
}
//從中斷退出并進(jìn)行調(diào)度
void IntSwitch(void)
{
//當(dāng)中斷無(wú)嵌套,并且沒有在切換任務(wù)的過程中,直接進(jìn)行任務(wù)切換
if(OSCoreState == 0x02 && IntNum==0)
{
//進(jìn)入中斷時(shí),已經(jīng)保存了SREG和R0,R1,R18~R27,R30,R31
__asm__ __volatile__("POP R31
"); //去除因調(diào)用子程序而入棧的PC
__asm__ __volatile__("POP R31
");
__asm__ __volatile__("LDI R16,0x01
");
//清除中斷要求任務(wù)切換的標(biāo)志位,設(shè)置正在任務(wù)切換標(biāo)志位
__asm__ __volatile__("RJMP Int_OSSched
"); //重新調(diào)度
}
}
////////////////////////////////////////////任務(wù)處理
//掛起任務(wù)
void OSTaskSuspend(unsigned char prio)
{
TCB[prio].OSWaitTick=0;
OSRdyTbl &= ~(0x01<
OSSched(); //從新調(diào)度
}
//恢復(fù)任務(wù) 可以讓被OSTaskSuspend或 OSTimeDly暫停的任務(wù)恢復(fù)
void OSTaskResume(unsigned char prio)
{
OSRdyTbl |= 0x01<
if(OSTaskRunningPrio>prio) //當(dāng)要當(dāng)前任務(wù)的優(yōu)先級(jí)低于重置位的任務(wù)的優(yōu)先級(jí)
OSSched(); //從新調(diào)度 //從新調(diào)度
}
// 任務(wù)延時(shí)
void OSTimeDly(unsigned int ticks)
{
if(ticks) //當(dāng)延時(shí)有效
{
OSRdyTbl &= ~(0x01<
OSSched(); //從新調(diào)度
}
}
//信號(hào)量
struct SemBlk
{
unsigned char OSEventType; //型號(hào) 0,信號(hào)量獨(dú)占型;1信號(hào)量共享型
unsigned char OSEventState; //狀態(tài) 0,不可用;1,可用
unsigned char OSTaskPendTbl; //等待信號(hào)量的任務(wù)列表
} Sem[10];
//初始化信號(hào)量
void OSSemCreat(unsigned char Index,unsigned char Type)
{
Sem[Index].OSEventType=Type; //型號(hào) 0,信號(hào)量獨(dú)占型;1信號(hào)量共享型
Sem[Index].OSTaskPendTbl=0;
Sem[Index].OSEventState=0;
}
//任務(wù)等待信號(hào)量,掛起
//當(dāng)Timeout==0xffff時(shí),為無(wú)限延時(shí)
unsigned char OSTaskSemPend(unsigned char Index,unsigned int Timeout)
{
//unsigned char i=0;
if(Sem[Index].OSEventState) //信號(hào)量有效
{
if(Sem[Index].OSEventType==0) //如果為獨(dú)占型
Sem[Index].OSEventState = 0x00; //信號(hào)量被獨(dú)占,不可用
}
else
{ //加入信號(hào)的任務(wù)等待表
Sem[Index].OSTaskPendTbl |= 0x01<
OSRdyTbl &= ~(0x01<
if(TCB[OSTaskRunningPrio].OSWaitTick==0 ) //超時(shí),未能拿到資源
return 0;
}
return 1;
}
//發(fā)送一個(gè)信號(hào)量,可以從任務(wù)或中斷發(fā)送
void OSSemPost(unsigned char Index)
{
if(Sem[Index].OSEventType) //當(dāng)要求的信號(hào)量是共享型
{
Sem[Index].OSEventState=0x01; //使信號(hào)量有效
OSRdyTbl |=Sem [Index].OSTaskPendTbl; //使在等待該信號(hào)的所有任務(wù)就緒
Sem[Index].OSTaskPendTbl=0; //清空所有等待該信號(hào)的等待任務(wù)
}
else //當(dāng)要求的信號(hào)量為獨(dú)占型
{
unsigned char i;
for (i = 0; i < OS_TASKS && !(Sem[Index].OSTaskPendTbl & (0x01< if(i < OS_TASKS) //如果有任務(wù)需要
{
Sem[Index].OSTaskPendTbl &= ~(0x01< OSRdyTbl |= 0x01< }
else
{
Sem[Index].OSEventState =1; //使信號(hào)量有效
}
}
}
//從任務(wù)發(fā)送一個(gè)信號(hào)量,并進(jìn)行調(diào)度
void OSTaskSemPost(unsigned char Index)
{
OSSemPost(Index);
OSSched();
}
//清除一個(gè)信號(hào)量,只對(duì)共享型的有用。
//對(duì)于獨(dú)占型的信號(hào)量,在任務(wù)占用后,就交得不可以用了。
void OSSemClean(unsigned char Index)
{
Sem[Index].OSEventState =0; //要求的信號(hào)量無(wú)效
}
void TCN0Init(void) // 計(jì)時(shí)器0
{
TCCR0 = 0;
TCCR0 |= (1<
}
SIGNAL(SIG_OVERFLOW0)
{
IntNum++; //中斷嵌套+1
sei(); //在中斷中,重開中斷
unsigned char i;
for(i=0;i
if(TCB[i].OSWaitTick && TCB[i].OSWaitTick!=0xffff)
{
TCB[i].OSWaitTick--;
if(TCB[i].OSWaitTick==0) //當(dāng)任務(wù)時(shí)鐘到時(shí),必須是由定時(shí)器減時(shí)的才行
{
OSRdyTbl |= (0x01< OSCoreState|=0x02; //要求任務(wù)切換的標(biāo)志位
}
}
}
TCNT0=100;
cli();
IntNum--; //中斷嵌套-1
IntSwitch(); //進(jìn)行任務(wù)調(diào)度
}
unsigned char __attribute__ ((progmem)) proStrA[]="Task ";
unsigned char strA[20];
SIGNAL(SIG_UART_RECV) //串口接收中斷
{
strA[0]=UDR;
}
/////////////////////////////////////串口發(fā)送
unsigned char *pstr_UART_Send;
unsigned int nUART_Sending=0;
void UART_Send(unsigned char *Res,unsigned int Len) //發(fā)送字符串?dāng)?shù)組
{
if(Len>0)
{
pstr_UART_Send=Res; //發(fā)送字串的指針
nUART_Sending=Len; //發(fā)送字串的長(zhǎng)度
UCSRB=0xB8; //發(fā)送中斷使能
}
}
//SIGNAL 在中斷期間,其它中斷禁止
SIGNAL(SIG_UART_DATA) //串口發(fā)送數(shù)據(jù)中斷
{
IntNum++; //中斷嵌套+1,不充許中斷
if(nUART_Sending) //如果未發(fā)完
{
UDR=*pstr_UART_Send; //發(fā)送字節(jié)
pstr_UART_Send++; //發(fā)送字串的指針加1
nUART_Sending--; //等待發(fā)送的字串?dāng)?shù)減1
}
if(nUART_Sending==0) //當(dāng)已經(jīng)發(fā)送完
{
OSSemPost(0);
OSCoreState|=0x02; //要求任務(wù)切換的標(biāo)志位
UCSRB=0x98;
}
cli(); //關(guān)發(fā)送中斷
IntNum--;
IntSwitch(); //進(jìn)行任務(wù)調(diào)度
}
void UARTInit() //初始化串口
{
#define fosc 8000000 //晶振8 MHZ UBRRL=(fosc/16/(baud+1))%256;
#define baud 9600 //波特率
OSCCAL=0x97; //串口波特率校正值,從編程器中讀出
//UCSRB=(1<
//UCSRB=0x08;
UBRRL=(fosc/16/(baud+1))%256;
UBRRH=(fosc/16/(baud+1))/256;
UCSRC=(1<
UDR=0;
}
//打印unsigned int 到字符串中 00000
void strPUT_uInt(unsigned char *Des,unsigned int i)
{
unsigned char j;
Des=Des+4;
for(j=0;j<5;j++)
{
*Des=i%10+’0’;
i=i/10;
Des--;
}
}
void strPUT_Star(unsigned char *Des,unsigned char i)
{
unsigned char j;
for(j=0;j {
*Des++=’*’;
}
*Des++=13;
}
unsigned int strPUT_TaskState(unsigned char *Des,
unsigned char TaskID,
unsigned char Num)
{
//unsigned int i=0;
*(Des+4)=’0’+TaskID;
strPUT_uInt(Des+6,Num);
strPUT_Star(Des+12,TaskID);
return 12+TaskID+1;
}
void Task0()
{
unsigned int j=0;
while(1)
{
PORTB=j++;
if(OSTaskSemPend(0,0xffff))
{
unsigned int m;
m=strPUT_TaskState(strA,OSTaskRunningPrio,j);
UART_Send(strA,m);
}
OSTimeDly(200);
}
}
void Task1()
{
unsigned int j=0;
while(1)
{
PORTC=j++;
if(OSTaskSemPend(0,0xffff))
{
unsigned int m;
m=strPUT_TaskState(strA,OSTaskRunningPrio,j);
UART_Send(strA,m);
}
OSTimeDly(100);
}
}
void Task2()
{
unsigned int j=0;
while(1)
{
if(OSTaskSemPend(0,0xffff))
{
unsigned int m;
m=strPUT_TaskState(strA,OSTaskRunningPrio,j);
UART_Send(strA,m);
}
PORTD=j++;
OSTimeDly(50);
}
}
void TaskScheduler()
{
OSSched();
while(1)
{
}
}
int main(void)
{
strlcpy_P(strA,proStrA,20);
UARTInit();
TCN0Init();
OSRdyTbl=0;
IntNum=0;
OSTaskCreate(Task0,&Stack[99],0);
OSTaskCreate(Task1,&Stack[199],1);
OSTaskCreate(Task2,&Stack[299],2);
OSTaskCreate(TaskScheduler,&Stack[399],OS_TASKS);
OSStartTask();
}
結(jié)束語(yǔ)
本文中的例子,基本上用WinAVR和 Proteus調(diào)試仿真成功,一定可能存在某些方面的缺陷,因?yàn)楣ぷ魃蠒r(shí)間的壓力,就沒有進(jìn)一步查找。
但我相信,大家通過學(xué)習(xí),會(huì)一步步了解一個(gè)內(nèi)核的具體實(shí)現(xiàn)形式,慢慢完善,并且最終寫出一個(gè)屬于自己的內(nèi)核。
當(dāng)掌握一定的基本知識(shí)后,再回頭看看 UCOSII和small rots51等,可能會(huì)有更深的體會(huì),對(duì)進(jìn)一步了解嵌入式系統(tǒng)和操作系統(tǒng),條理會(huì)更加明析。希望本文能幫助大家做到這一點(diǎn)。
希望大家能夠提出自己寶貴的意見,我會(huì)進(jìn)行階段性的總結(jié),并盡可能地不斷改進(jìn)。
牛頓曾說(shuō)過,“我能夠看得更遠(yuǎn),是因?yàn)檎驹诰奕说募绨蛏?。?
希望大家都能出一份力,推動(dòng)我們的嵌入式的事業(yè)的進(jìn)一步發(fā)展。
2006年1月14日 希望同行多多交流。
作者 黃健昌 Alex
QQ :43679769
mobil: 13719033059
e-mail: magenta-hjc@tom.com-