????自從03年以來,對單片機的RTOS的學習和應用的熱潮可謂一浪高過一浪.03年,在離開校園前的,非典的那幾個月,在華師的后門那里買了本邵貝貝的《UCOSII》,通讀了幾次,沒有實驗器材,也不了了之。
????在21IC上,大家都可以看到楊屹寫的關于UCOSII在51上的移植,于是掀起了51上的RTOS的熱潮。
????再后來,陳明計先生推出的small?rots,展示了一個用在51上的微內核,足以在52上進行任務調度。
????前段時間,在ouravr上面開有專門關于AVR的Rtos的專欄,并且不少的兄弟把自己的作品拿出來,著實開了不少眼界。這時,我重新回顧了使用單片機的經歷,覺得很有必要,從根本上對單片機的RTOS的知識進行整理,于是,我開始了編寫一個用在AVR單片機的RTOS。
???
????當時,我所有的知識和資源有:?
????Proteus6.7????????可以用來模擬仿真avr系列的單片機
????WinAVR?v2.0.5.48??基于GCC?AVR的編譯環境,好處在于可以在C語言中插入asm的語句
????mega8??1K的ram有8K的rom,是開發8位的RTOS的一個理想的器件,并且我對它也比較熟悉。
????
????寫UCOS的Jean?J.Labrosse在他的書上有這樣一句話,“漸漸地,我自然會想到,寫個實時內核直有那么難嗎?不就是不斷地保存,恢復CPU的那些寄存器嘛。”?
????好了,當這一切準備好后,我們就可以開始我們的Rtos?for?mega8的實驗之旅了。
???
????本文列出的例子,全部完整可用。只需要一個文件就可以編譯了。我相信,只要適當可用,最簡單的就是最好的,這樣可以排除一些不必要的干擾,讓大家專注到每一個過程的學習。
第一篇:函數的運行
????在一般的單片機系統中,是以前后臺的方式(大循環+中斷)來處理數據和作出反應的。
????例子如下:
???
????makefile的設定:運行WinAvr中的Mfile,設定如下
????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();
}
????首先,提出一個問題:如果要調用一個函數,真是只能以上面的方式進行嗎?
????相信學習過C語言的各位會回答,No!我們還有一種方式,就是“用函數指針變量調用函數”,如果大家都和我一樣,當初的教科書是譚浩強先生的《C程序設計》的話,請找回書的第9.5節。
????
????例子:用函數指針變量調用函數
#include?
void?fun1(void)
{
??unsigned?char?i=0;
??while(1)
??{
????PORTB=i++;
????PORTC=0x01<<(i%8);
??}
}
void?(*pfun)();??//指向函數的指針
int?main(void)
{
??pfun=fun1;????//
??(*pfun)();????//運行指針所指向的函數
}
????
?????第二種,是“把指向函數的指針變量作函數參數”
?????
#include?
void?fun1(void)
{
??unsigned?char?i=0;
??while(1)
??{
????PORTB=i++;
????PORTC=0x01<<(i%8);
??}
}
void?RunFun(void?(*pfun)())??//獲得了要傳遞的函數的地址
{
??(*pfun)();?????????????????//在RunFun中,運行指針所指向的函數
}
int?main(void)
{
???RunFun(fun1);????????????//將函數的指針作為變量傳遞
????
}
????看到上面的兩種方式,很多人可能會說,“這的確不錯”,但是這樣與我們想要的RTOS,有什么關系呢?各位請細心向下看。
????以下是GCC對上面的代碼的編譯的情況:
????
????對main()中的RunFun(fun1);?的編譯如下
??ldi?r24,lo8(pm(fun1))
??ldi?r25,hi8(pm(fun1))
??rcall?RunFun
????
對void?RunFun(void?(*pfun)())的編譯如下
????????????????/*void?RunFun(void?(*pfun)())*/
???????????????/*(*pfun)();*/
.LM6:
??movw?r30,r24
??icall
??ret
????在調用void?RunFun(void?(*pfun)())的時候,的確可以把fun1的地址通過r24和r25傳遞給RunFun()。但是,RTOS如何才能有效地利用函數的地址呢?
第二篇:?人工堆棧
在單片機的指令集中,一類指令是專門與堆棧和PC指針打道的,它們是
????rcall???相對調用子程序指令
????icall???間接調用子程序指令
????ret?????子程序返回指令
????reti????中斷返回指令???
????對于ret和reti,它們都可以將堆棧棧頂的兩個字節被彈出來送入程序計數器PC中,一般用來從子程序或中斷中退出。其中reti還可以在退出中斷時,重開全局中斷使能。
????有了這個基礎,就可以建立我們的人工堆棧了。
????例:
#include?
void?fun1(void)
{
??unsigned?char?i=0;
??while(1)
??{
????PORTB=i++;
????PORTC=0x01<<(i%8);
??}
}
unsigned?char?Stack[100];?//建立一個100字節的人工堆棧
void?RunFunInNewStack(void?(*pfun)(),unsigned?char?*pStack)
{
??*pStack--=(unsigned?int)pfun>>8;????//將函數的地址高位壓入堆棧,
??*pStack--=(unsigned?int)pfun;????????//將函數的地址低位壓入堆棧,
??SP=pStack;????????????????????????????//將堆棧指針指向人工堆棧的棧頂
??__asm__?__volatile__("RET?nt");????//返回并開中斷,開始運行fun1()
}
int?main(void)
{
???RunFunInNewStack(fun1,&Stack[99]);
}
?????RunFunInNewStack(),將指向函數的指針的值保存到一個unsigned??char的數組Stack中,作為人工堆棧。并且將棧頂的數值傳遞組堆棧指針SP,因此當用"ret"返回時,從SP中恢復到PC中的值,就變為了指向fun1()的地址,開始運行fun1().
????上面例子中在RunFunInNewStack()的最后一句嵌入了匯編代碼?"ret",實際上是可以去除的。因為在RunFunInNewStack()返回時,編譯器已經會加上"ret"。我特意寫出來,是為了讓大家看到用"ret"作為返回后運行fun1()的過程。
第三篇:GCC中對寄存器的分配與使用
在很多用于AVR的RTOS中,都會有任務調度時,插入以下的語句:
????
????入棧:
????__asm__?__volatile__("PUSH?R0??nt");
????__asm__?__volatile__("PUSH?R1??nt");
????......
????__asm__?__volatile__("PUSH?R31?nt");
????出棧
????__asm__?__volatile__("POP??R31?nt");
????......
????__asm__?__volatile__("POP??R1??nt");
????__asm__?__volatile__("POP??R0??nt");
????通常大家都會認為,在任務調度開始時,當然要將所有的通用寄存器都保存,并且還應該保存程序狀態寄存器SREG。然后再根據相反的次序,將新任務的寄存器的內容恢復。
????
????但是,事實真的是這樣嗎?如果大家看過陳明計先生寫的small?rots51,就會發現,它所保存的通用寄存器不過是4組通用寄存器中的1組。
????
????在Win?AVR中的幫助文件?avr-libc?Manual中的Related?Pages中的Frequently?Asked?Questions,其實有一個問題是"What?registers?are?used?by?the?C?compiler?"??回答了編譯器所需要占用的寄存器。一般情況下,編譯器會先用到以下寄存器
????1?Call-used?registers?(r18-r27,?r30-r31):?調用函數時作為參數傳遞,也就是用得最多的寄存器。
????2?Call-saved?registers?(r2-r17,?r28-r29):?調用函數時作為結果傳遞,當中的r28和r29可能會被作為指向堆棧上的變量的指針。
????
????3?Fixed?registers?(r0,?r1):?固定作用。r0用于存放臨時數據,r1用于存放0。
????
????
????還有另一個問題是"How?to?permanently?bind?a?variable?to?a?register?",是將變量綁定到通用寄存器的方法。而且我發現,如果將某個寄存器定義為變量,編譯器就會不將該寄存器分配作其它用途。這對RTOS是很重要的。
????在"Inline?Asm"中的"C?Names?Used?in?Assembler?Code"明確表示,如果將太多的通用寄存器定義為變量,剛在編譯的過程中,被定義的變量依然可能被編譯器占用。
????大家可以比較以下兩個例子,看看編譯器產生的代碼:(在*.lst文件中)
第一個例子:沒有定義通用寄存器為變量
#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
第二個例子:定義通用寄存器為變量
#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
????當然,在上面兩個例子中,有部份代碼被編譯器優化了。?
????
????通過反復測試,發現編譯器一般使用如下寄存器:
????第1類寄存器,第2類寄存器的r28,r29,第3類寄存器
????如在中斷函數中有調用基它函數,剛會在進入中斷后,固定地將第1類寄存器和第3類寄存器入棧,在退出中斷又將它們出棧。
?
第四篇:只有延時服務的協作式的內核
???Cooperative?Multitasking
???前后臺系統,協作式內核系統,與占先式內核系統,有什么不同呢?
???記得在21IC上看過這樣的比喻,“你(小工)在用廁所,經理在外面排第一,老板在外面排第二。如果是前后臺,不管是誰,都必須按排隊的次序使用廁所;如果是協作式,那么可以等你用完廁所,老板就要比經理先進入;如果是占先式,只要有更高級的人在外面等,那么廁所里無論是誰,都要第一時間讓出來,讓最高級別的人先用。”
#include?
#include?
#include?
unsigned?char?Stack[200];
register?unsigned?char?OSRdyTbl??????????asm("r2");????//任務運行就緒表
register?unsigned?char?OSTaskRunningPrio?asm("r3");????//正在運行的任務
#define?OS_TASKS?3????????????????????//設定運行任務的數量
struct?TaskCtrBlock???????????//任務控制塊
{
??unsigned?int?OSTaskStackTop;??//保存任務的堆棧頂
??unsigned?int?OSWaitTick;??????//任務延時時鐘
}?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");
//建立任務
void?OSTaskCreate(void?(*Task)(void),unsigned?char?*Stack,unsigned?char?TaskID)
{
??unsigned?char?i;
??*Stack--=(unsigned?int)Task>>8;????//將任務的地址高位壓入堆棧,
??*Stack--=(unsigned?int)Task;?????????//將任務的地址低位壓入堆棧,
????
??*Stack--=0x00;?????????????????????//R1?__zero_reg__????????????
??*Stack--=0x00;?????????????????????//R0?__tmp_reg__
??*Stack--=0x80;????????????????????????????????????????//SREG?在任務中,開啟全局中斷????????
??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;????//將人工堆棧的棧頂,保存到堆棧的數組中
????OSRdyTbl|=0x01<
//開始任務調度,從最低優先級的任務的開始
void?OSStartTask()????????
{
??OSTaskRunningPrio=OS_TASKS;
??SP=TCB[OS_TASKS].OSTaskStackTop+17;
??__asm__?__volatile__(????"reti"???????"nt"??);?
}
//進行任務調度
void?OSSched(void)
{?
???//??根據中斷時保存寄存器的次序入棧,模擬一次中斷后,入棧的情況??
??__asm__?__volatile__("PUSH?__zero_reg__?????????nt");??//R1
??__asm__?__volatile__("PUSH?__tmp_reg__??????????nt");??//R0?
??__asm__?__volatile__("IN???__tmp_reg__,__SREG__?nt");??//保存狀態寄存器SREG
??__asm__?__volatile__("PUSH?__tmp_reg__??????????nt");
??__asm__?__volatile__("CLR??__zero_reg__?????????nt");??//R0重新清零
??__asm__?__volatile__("PUSH?R18??????????????????nt");
??__asm__?__volatile__("PUSH?R19??????????????????nt");
??__asm__?__volatile__("PUSH?R20??????????????????nt");
??__asm__?__volatile__("PUSH?R21??????????????????nt");
??__asm__?__volatile__("PUSH?R22??????????????????nt");
??__asm__?__volatile__("PUSH?R23??????????????????nt");
??__asm__?__volatile__("PUSH?R24??????????????????nt");
??__asm__?__volatile__("PUSH?R25??????????????????nt");
??__asm__?__volatile__("PUSH?R26??????????????????nt");
??__asm__?__volatile__("PUSH?R27??????????????????nt");
??__asm__?__volatile__("PUSH?R30??????????????????nt");????
??__asm__?__volatile__("PUSH?R31??????????????????nt");
??__asm__?__volatile__("PUSH?R28??????????????????nt");??//R28與R29用于建立在堆棧上的指針
??__asm__?__volatile__("PUSH?R29??????????????????nt");??//入棧完成
????
??TCB[OSTaskRunningPrio].OSTaskStackTop=SP;???????????//將正在運行的任務的堆棧底保存
????
??
??unsigned?char?OSNextTaskID;?????????????????????????????//在現有堆棧上開設新的空間?
??for?(OSNextTaskID?=?0;??????????????????????????????????//進行任務調度
????OSNextTaskID?????OSNextTaskID++);
????OSTaskRunningPrio?=?OSNextTaskID?;
??cli();??//保護堆棧轉換
??SP=TCB[OSTaskRunningPrio].OSTaskStackTop;
??sei();
????
????//根據中斷時的出棧次序????
??__asm__?__volatile__("POP??R29??????????????????nt");????
??__asm__?__volatile__("POP??R28??????????????????nt");????????
??__asm__?__volatile__("POP??R31??????????????????nt");????
??__asm__?__volatile__("POP??R30??????????????????nt");????
??__asm__?__volatile__("POP??R27??????????????????nt");????
??__asm__?__volatile__("POP??R26??????????????????nt");????
??__asm__?__volatile__("POP??R25??????????????????nt");????
??__asm__?__volatile__("POP??R24??????????????????nt");????
??__asm__?__volatile__("POP??R23??????????????????nt");????
??__asm__?__volatile__("POP??R22??????????????????nt");????
??__asm__?__volatile__("POP??R21??????????????????nt");????
??__asm__?__volatile__("POP??R20??????????????????nt");????
??__asm__?__volatile__("POP??R19??????????????????nt");
??__asm__?__volatile__("POP??R18??????????????????nt");
??__asm__?__volatile__("POP??__tmp_reg__??????????nt");??????//SERG?出棧并恢復
??__asm__?__volatile__("OUT??__SREG__,__tmp_reg__?nt");??????//
??__asm__?__volatile__("POP??__tmp_reg__??????????nt");??????//R0?出棧
??__asm__?__volatile__("POP??__zero_reg__?????????nt");??????//R1?出棧
??//中斷時出棧完成
}
void?OSTimeDly(unsigned?int?ticks)
{
??if(ticks)?????????????????????????????//當延時有效
??{
????OSRdyTbl?&=?~(0x01<
????OSSched();??????????????????????????//從新調度
??}
}
void?TCN0Init(void)????//?計時器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)?????//當任務時鐘到時,必須是由定時器減時的才行
??????{??
????????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();??????//反復進行調度
??}
}
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();
}
???在上面的例子中,一切變得很簡單,三個正在運行的主任務,都通過延時服務,主動放棄對CPU的控制權。
???在時間中斷中,對各個任務的的延時進行計時,如果某個任務的延時結束,將任務重新在就緒表中置位。
???最低級的系統任務TaskScheduler(),在三個主任務在放棄對CPU的控制權后開始不斷地進行調度。如果某個任務在就緒表中置位,通過調度,進入最高級別的任務中繼續運行。
第五篇:?完善的協作式的內核??
??現在為上面的協作式內核添加一些OS中所必須的服務:
???1??掛起和重新運行任務
???2??信號量(在必要時候,可以擴展成郵箱和信息隊列)
???3??延時
???
#include?
#include?
#include?
unsigned?char?Stack[400];
register?unsigned?char?OSRdyTbl??????????asm("r2");????//任務運行就緒表
register?unsigned?char?OSTaskRunningPrio?asm("r3");????//正在運行的任務
#define?OS_TASKS?3????????????????????//設定運行任務的數量
struct?TaskCtrBlock
{
??unsigned?int?OSTaskStackTop;??//保存任務的堆棧頂
??unsigned?int?OSWaitTick;??????//任務延時時鐘
}?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");
//建立任務
void?OSTaskCreate(void?(*Task)(void),unsigned?char?*Stack,unsigned?char?TaskID)
{
??unsigned?char?i;?????????????????????
??*Stack--=(unsigned?int)Task>>8;????//將任務的地址高位壓入堆棧,
??*Stack--=(unsigned?int)Task;?????????//將任務的地址低位壓入堆棧,
????
??*Stack--=0x00;?????????????????????//R1?__zero_reg__????????????
??*Stack--=0x00;?????????????????????//R0?__tmp_reg__
??*Stack--=0x80;????????????????????????????????????????
//SREG?在任務中,開啟全局中斷????????
??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;????//將人工堆棧的棧頂,保存到堆棧的數組中
??OSRdyTbl|=0x01<
//開始任務調度,從最低優先級的任務的開始
void?OSStartTask()????????
{
??OSTaskRunningPrio=OS_TASKS;
??SP=TCB[OS_TASKS].OSTaskStackTop+17;
??__asm__?__volatile__(????"reti"???????"nt"??);?
}
//進行任務調度
void?OSSched(void)
{?
???//??根據中斷時保存寄存器的次序入棧,模擬一次中斷后,入棧的情況??
??__asm__?__volatile__("PUSH?__zero_reg__?????????nt");??//R1
??__asm__?__volatile__("PUSH?__tmp_reg__??????????nt");??//R0?
??__asm__?__volatile__("IN???__tmp_reg__,__SREG__?nt");??//保存狀態寄存器SREG
??__asm__?__volatile__("PUSH?__tmp_reg__??????????nt");
??__asm__?__volatile__("CLR??__zero_reg__?????????nt");??//R0重新清零
??__asm__?__volatile__("PUSH?R18??????????????????nt");
??__asm__?__volatile__("PUSH?R19??????????????????nt");
??__asm__?__volatile__("PUSH?R20??????????????????nt");
??__asm__?__volatile__("PUSH?R21??????????????????nt");
??__asm__?__volatile__("PUSH?R22??????????????????nt");
??__asm__?__volatile__("PUSH?R23??????????????????nt");
??__asm__?__volatile__("PUSH?R24??????????????????nt");
??__asm__?__volatile__("PUSH?R25??????????????????nt");
??__asm__?__volatile__("PUSH?R26??????????????????nt");
??__asm__?__volatile__("PUSH?R27??????????????????nt");
??__asm__?__volatile__("PUSH?R30??????????????????nt");????
??__asm__?__volatile__("PUSH?R31??????????????????nt");
??__asm__?__volatile__("PUSH?R28??????????????????nt");??//R28與R29用于建立在堆棧上的指針
??__asm__?__volatile__("PUSH?R29??????????????????nt");??//入棧完成
????
??TCB[OSTaskRunningPrio].OSTaskStackTop=SP;???????????//將正在運行的任務的堆棧底保存
??unsigned?char?OSNextTaskID;?????????????????????????????//在現有堆棧上開設新的空間?
??for?(OSNextTaskID?=?0;??????????????????????????????????//進行任務調度
????OSNextTaskID?????OSNextTaskID++);
????OSTaskRunningPrio?=?OSNextTaskID?;
??cli();??//保護堆棧轉換
??SP=TCB[OSTaskRunningPrio].OSTaskStackTop;
??sei();
????
????//根據中斷時的出棧次序????
??__asm__?__volatile__("POP??R29??????????????????nt");????
??__asm__?__volatile__("POP??R28??????????????????nt");????????
??__asm__?__volatile__("POP??R31??????????????????nt");????
??__asm__?__volatile__("POP??R30??????????????????nt");????
??__asm__?__volatile__("POP??R27??????????????????nt");????
??__asm__?__volatile__("POP??R26??????????????????nt");????
??__asm__?__volatile__("POP??R25??????????????????nt");????
??__asm__?__volatile__("POP??R24??????????????????nt");????
??__asm__?__volatile__("POP??R23??????????????????nt");????
??__asm__?__volatile__("POP??R22??????????????????nt");????
??__asm__?__volatile__("POP??R21??????????????????nt");????
??__asm__?__volatile__("POP??R20??????????????????nt");????
??__asm__?__volatile__("POP??R19??????????????????nt");
??__asm__?__volatile__("POP??R18??????????????????nt");
??__asm__?__volatile__("POP??__tmp_reg__??????????nt");??????//SERG?出棧并恢復
??__asm__?__volatile__("OUT??__SREG__,__tmp_reg__?nt");??????//
??__asm__?__volatile__("POP??__tmp_reg__??????????nt");??????//R0?出棧
??__asm__?__volatile__("POP??__zero_reg__?????????nt");??????//R1?出棧
??//中斷時出棧完成
}
////////////////////////////////////////////任務處理
//掛起任務
void?OSTaskSuspend(unsigned?char?prio)?
{
??TCB[prio].OSWaitTick=0;
??OSRdyTbl?&=?~(0x01<
????OSSched();???????????????//從新調度
}
//恢復任務?可以讓被OSTaskSuspend或?OSTimeDly暫停的任務恢復
void?OSTaskResume(unsigned?char?prio)
{
??OSRdyTbl?|=?0x01<
??if(OSTaskRunningPrio>prio)???//當要當前任務的優先級低于重置位的任務的優先級
????OSSched();???????????????//從新調度??????????????//從新調度
}
//?任務延時
void?OSTimeDly(unsigned?int?ticks)
{
??if(ticks)?????????????????????????????//當延時有效
??{
????OSRdyTbl?&=?~(0x01<
????OSSched();??????????????????????????//從新調度
??}
}
//信號量
struct?SemBlk
{
??unsigned?char?OSEventType;?????//型號?0,信號量獨占型;1信號量共享型?
??unsigned?char?OSEventState;????//狀態?0,不可用;1,可用
??unsigned?char?OSTaskPendTbl;???//等待信號量的任務列表
}?Sem[10];
//初始化信號量
void?OSSemCreat(unsigned?char?Index,unsigned?char?Type)
{
??Sem[Index].OSEventType=Type;??//型號?0,信號量獨占型;1信號量共享型?
??Sem[Index].OSTaskPendTbl=0;
??Sem[Index].OSEventState=0;
}
//任務等待信號量,掛起
unsigned?char?OSTaskSemPend(unsigned?char?Index,unsigned?int?Timeout)
{
??//unsigned?char?i=0;
??if(Sem[Index].OSEventState)???????????????//信號量有效
??{?
????if(Sem[Index].OSEventType==0)??????????//如果為獨占型
????Sem[Index].OSEventState?=?0x00;???????//信號量被獨占,不可用
??}
??else
??{?????????????????????????????????????????//加入信號的任務等待表
????Sem[Index].OSTaskPendTbl?|=?0x01<
????OSSched();???//從新調度
????if(TCB[OSTaskRunningPrio].OSWaitTick==0)?return?0;????
??}
??return?1;
}
//發送一個信號量,可以從任務或中斷發送
void?OSSemPost(unsigned?char?Index)
{
if(Sem[Index].OSEventType)????????????????//當要求的信號量是共享型
??{
????Sem[Index].OSEventState=0x01;???????????//使信號量有效
????OSRdyTbl?|=Sem?[Index].OSTaskPendTbl;???//使在等待該信號的所有任務就緒
????Sem[Index].OSTaskPendTbl=0;?????????????//清空所有等待該信號的等待任務
??}??
??else???????????????????????????????????????//當要求的信號量為獨占型
??{??????
????unsigned?char?i;
????for?(i?=?0;?i?????if(i?????{
??????Sem[Index].OSTaskPendTbl?&=?~(0x01<??????OSRdyTbl?|=?0x01<????}
????else
????{
??????Sem[Index].OSEventState?=1;????????//使信號量有效
????}
??}
}
//從任務發送一個信號量,并進行調度
void?OSTaskSemPost(unsigned?char?Index)?
{
??OSSemPost(Index);
??OSSched();???
}
//清除一個信號量,只對共享型的有用。
//對于獨占型的信號量,在任務占用后,就交得不可以用了。?
void?OSSemClean(unsigned?char?Index)
{
??Sem[Index].OSEventState?=0;??????????//要求的信號量無效
}
void?TCN0Init(void)????//?計時器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)?????//當任務時鐘到時,必須是由定時器減時的才行
??????{??
????????OSRdyTbl?|=?(0x01<??????}
????}
??}
??TCNT0=100;
}
void?Task0()
{
??unsigned?int?j=0;
??while(1)
??{????????????
????PORTB=j++;
????OSTaskSuspend(1);????//掛起任務1?
????OSTaskSemPost(0);
????OSTimeDly(50);
????OSTaskResume(1);?????//恢復任務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();??????//反復進行調度
??}
}
int?main(void)
{????
??TCN0Init();
??OSRdyTbl=0;
??OSSemCreat(0,1);??//將信號量設為共享型
??OSTaskCreate(Task0,&Stack[99],0);
??OSTaskCreate(Task1,&Stack[199],1);
??OSTaskCreate(Task2,&Stack[299],2);
??OSTaskCreate(TaskScheduler,&Stack[399],OS_TASKS);
??OSStartTask();
}
第六篇:時間片輪番調度法的內核
???Round-Robin?Sheduling
????時間片輪調法是非常有趣的。本篇中的例子,建立了3個任務,任務沒有優先級,在時間中斷的調度下,每個任務都輪流運行相同的時間。如果在內核中沒有加入其它服務,感覺上就好像是有三個大循環在同時運行。
????本例只是提供了一個用時間中斷進行調度的內核,大家可以根據自己的需要,添加相應的服務。
????要注意到:?
????1,由于在時間中斷內調用了任務切換函數,因為在進入中斷時,已經將一系列的寄存器入棧。
????2,在中斷內進行調度,是直接通過"RJMP?Int_OSSched"進入任務切換和調度的,這是GCC?AVR的一個特點,為用C編寫內核提供了極大的方便。
????3,在閱讀代碼的同時,請對照閱讀編譯器產生的?*.lst文件,會對你理解例子有很大的幫助。
???
#include?
#include?
#include?
unsigned?char?Stack[400];
register?unsigned?char?OSRdyTbl??????????asm("r2");????//任務運行就緒表
register?unsigned?char?OSTaskRunningPrio?asm("r3");????//正在運行的任務
#define?OS_TASKS?3????????????????????//設定運行任務的數量
struct?TaskCtrBlock
{
??unsigned?int?OSTaskStackTop;??//保存任務的堆棧頂
??unsigned?int?OSWaitTick;??????//任務延時時鐘
}?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");
//建立任務
void?OSTaskCreate(void?(*Task)(void),unsigned?char?*Stack,unsigned?char?TaskID)
{
??unsigned?char?i;?????????????????????
??*Stack--=(unsigned?int)Task>>8;????//將任務的地址高位壓入堆棧,
??*Stack--=(unsigned?int)Task;?????????//將任務的地址低位壓入堆棧,
????
??*Stack--=0x00;?????????????????????//R1?__zero_reg__????????????
??*Stack--=0x00;?????????????????????//R0?__tmp_reg__
??*Stack--=0x80;????????????????????????????????????????
//SREG?在任務中,開啟全局中斷????????
??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;????//將人工堆棧的棧頂,保存到堆棧的數組中
??OSRdyTbl|=0x01<
//開始任務調度,從最低優先級的任務的開始
void?OSStartTask()????????
{
??OSTaskRunningPrio=OS_TASKS;
??SP=TCB[OS_TASKS].OSTaskStackTop+17;
??__asm__?__volatile__(????"reti"???????"nt"??);?
}
//進行任務調度
void?OSSched(void)
{?
???//??根據中斷時保存寄存器的次序入棧,模擬一次中斷后,入棧的情況??
??__asm__?__volatile__("PUSH?__zero_reg__?????????nt");??//R1
??__asm__?__volatile__("PUSH?__tmp_reg__??????????nt");??//R0?
??__asm__?__volatile__("IN???__tmp_reg__,__SREG__?nt");??//保存狀態寄存器SREG
??__asm__?__volatile__("PUSH?__tmp_reg__??????????nt");
??__asm__?__volatile__("CLR??__zero_reg__?????????nt");??//R0重新清零
??__asm__?__volatile__("PUSH?R18??????????????????nt");
??__asm__?__volatile__("PUSH?R19??????????????????nt");
??__asm__?__volatile__("PUSH?R20??????????????????nt");
??__asm__?__volatile__("PUSH?R21??????????????????nt");
??__asm__?__volatile__("PUSH?R22??????????????????nt");
??__asm__?__volatile__("PUSH?R23??????????????????nt");
??__asm__?__volatile__("PUSH?R24??????????????????nt");
??__asm__?__volatile__("PUSH?R25??????????????????nt");
??__asm__?__volatile__("PUSH?R26??????????????????nt");
??__asm__?__volatile__("PUSH?R27??????????????????nt");
??__asm__?__volatile__("PUSH?R30??????????????????nt");????
??__asm__?__volatile__("PUSH?R31??????????????????nt");
????
??__asm__?__volatile__("Int_OSSched:??????????????nt");??//當中斷要求調度,直接進入這里
??__asm__?__volatile__("PUSH?R28??????????????????nt");??//R28與R29用于建立在堆棧上的指針
??__asm__?__volatile__("PUSH?R29??????????????????nt");??//入棧完成
????
??TCB[OSTaskRunningPrio].OSTaskStackTop=SP;???????????//將正在運行的任務的堆棧底保存
??if(++OSTaskRunningPrio>=OS_TASKS)?//輪流運行各個任務,沒有優先級
??????OSTaskRunningPrio=0;
??//cli();??//保護堆棧轉換
??SP=TCB[OSTaskRunningPrio].OSTaskStackTop;
??//sei();
????
????//根據中斷時的出棧次序????
??__asm__?__volatile__("POP??R29??????????????????nt");????
??__asm__?__volatile__("POP??R28??????????????????nt");????????
??__asm__?__volatile__("POP??R31??????????????????nt");????
??__asm__?__volatile__("POP??R30??????????????????nt");????
??__asm__?__volatile__("POP??R27??????????????????nt");????
??__asm__?__volatile__("POP??R26??????????????????nt");????
??__asm__?__volatile__("POP??R25??????????????????nt");????
??__asm__?__volatile__("POP??R24??????????????????nt");????
??__asm__?__volatile__("POP??R23??????????????????nt");????
??__asm__?__volatile__("POP??R22??????????????????nt");????
??__asm__?__volatile__("POP??R21??????????????????nt");????
??__asm__?__volatile__("POP??R20??????????????????nt");????
??__asm__?__volatile__("POP??R19??????????????????nt");
??__asm__?__volatile__("POP??R18??????????????????nt");
??__asm__?__volatile__("POP??__tmp_reg__??????????nt");??????//SERG?出棧并恢復
??__asm__?__volatile__("OUT??__SREG__,__tmp_reg__?nt");??????//
??__asm__?__volatile__("POP??__tmp_reg__??????????nt");??????//R0?出棧
??__asm__?__volatile__("POP??__zero_reg__?????????nt");??????//R1?出棧
??__asm__?__volatile__("RETI??????????????????????nt");?????//返回并開中斷
??//中斷時出棧完成
}
void?IntSwitch(void)
{????
??__asm__?__volatile__("POP??R31??????????????????nt");??//去除因調用子程序而入棧的PC
??__asm__?__volatile__("POP??R31??????????????????nt");
??__asm__?__volatile__("RJMP?Int_OSSched??????????nt");??//重新調度
}
void?TCN0Init(void)????//?計時器0
{
??TCCR0?=?0;
??TCCR0?|=?(1<
}
SIGNAL(SIG_OVERFLOW0)
{
??TCNT0=100;
??IntSwitch();????????//任務調度
}
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();??????//反復進行調度
??}
}
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();
}
?第七篇:占先式內核(只帶延時服務)
Preemptive?Multitasking
????當大家理解時間片輪番調度法的任務調度方式后,占先式的內核的原理,已經伸手可及了。
????先想想,占先式內核是在什么地方實現任務調度的呢?對了,它在可以在任務中進行調度,這個在協作式的內核中已經做到了;同時,它也可以在中斷結束后進行調度,這個問題,已經在時間片輪番調度法中已經做到了。
????
????由于中斷是可以嵌套的,只有當各層嵌套中要求調度,并且中斷嵌套返回到最初進入的中斷的那一層時,才能進行任務調度。
??
#include?
#include?
#include?
unsigned?char?Stack[400];
register?unsigned?char?OSRdyTbl??????????asm("r2");????//任務運行就緒表
register?unsigned?char?OSTaskRunningPrio?asm("r3");????//正在運行的任務
register?unsigned?char?IntNum????????????asm("r4");??//中斷嵌套計數器
//只有當中斷嵌套數為0,并且有中斷要求時,才能在退出中斷時,進行任務調度
register?unsigned?char?OSCoreState???????asm("r16");?//?系統核心標志位?,R16?編譯器沒有使用
//只有大于R15的寄存器才能直接賦值?例LDI?R16,0x01
//0x01?正在任務?切換??0x02?有中斷要求切換
#define?OS_TASKS?3????????????????????//設定運行任務的數量
struct?TaskCtrBlock
{
??unsigned?int?OSTaskStackTop;??//保存任務的堆棧頂
??unsigned?int?OSWaitTick;??????//任務延時時鐘
}?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");
//建立任務
void?OSTaskCreate(void?(*Task)(void),unsigned?char?*Stack,unsigned?char?TaskID)
{
??unsigned?char?i;?????????????????????
??*Stack--=(unsigned?int)Task>>8;????//將任務的地址高位壓入堆棧,
??*Stack--=(unsigned?int)Task;?????????//將任務的地址低位壓入堆棧,
????
??*Stack--=0x00;?????????????????????//R1?__zero_reg__????????????
??*Stack--=0x00;?????????????????????//R0?__tmp_reg__
??*Stack--=0x80;????????????????????????????????????????
//SREG?在任務中,開啟全局中斷????????
??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;????//將人工堆棧的棧頂,保存到堆棧的數組中
??OSRdyTbl|=0x01<
//開始任務調度,從最低優先級的任務的開始
void?OSStartTask()????????
{
??OSTaskRunningPrio=OS_TASKS;
??SP=TCB[OS_TASKS].OSTaskStackTop+17;
??__asm__?__volatile__(????"reti"???????"nt"??);?
}
//進行任務調度
void?OSSched(void)
{?
??__asm__?__volatile__("LDI??R16,0x01?????????????nt");??
??//清除中斷要求任務切換的標志位,設置正在任務切換標志位
??__asm__?__volatile__("SEI???????????????????????nt");??????
??//開中斷,因為如果因中斷在任務調度中進行,要重新進行調度時,已經關中斷
??//根據中斷時保存寄存器的次序入棧,模擬一次中斷后,入棧的情況??
??__asm__?__volatile__("PUSH?__zero_reg__?????????nt");??//R1
??__asm__?__volatile__("PUSH?__tmp_reg__??????????nt");??//R0?
??__asm__?__volatile__("IN???__tmp_reg__,__SREG__?nt");??//保存狀態寄存器SREG
??__asm__?__volatile__("PUSH?__tmp_reg__??????????nt");
??__asm__?__volatile__("CLR??__zero_reg__?????????nt");??//R0重新清零
??__asm__?__volatile__("PUSH?R18??????????????????nt");
??__asm__?__volatile__("PUSH?R19??????????????????nt");
??__asm__?__volatile__("PUSH?R20??????????????????nt");
??__asm__?__volatile__("PUSH?R21??????????????????nt");
??__asm__?__volatile__("PUSH?R22??????????????????nt");
??__asm__?__volatile__("PUSH?R23??????????????????nt");
??__asm__?__volatile__("PUSH?R24??????????????????nt");
??__asm__?__volatile__("PUSH?R25??????????????????nt");
??__asm__?__volatile__("PUSH?R26??????????????????nt");
??__asm__?__volatile__("PUSH?R27??????????????????nt");
??__asm__?__volatile__("PUSH?R30??????????????????nt");????
??__asm__?__volatile__("PUSH?R31??????????????????nt");
????
??__asm__?__volatile__("Int_OSSched:??????????????nt");??//當中斷要求調度,直接進入這里
??__asm__?__volatile__("SEI???????????????????????nt");?
//開中斷,因為如果因中斷在任務調度中進行,已經關中斷?
??__asm__?__volatile__("PUSH?R28??????????????????nt");??//R28與R29用于建立在堆棧上的指針
??__asm__?__volatile__("PUSH?R29??????????????????nt");??//入棧完成
????
??TCB[OSTaskRunningPrio].OSTaskStackTop=SP;???????????//將正在運行的任務的堆棧底保存
??unsigned?char?OSNextTaskPrio;????????????????????????????//在現有堆棧上開設新的空間?
??for?(OSNextTaskPrio?=?0;?????????????????????????????????//進行任務調度
????OSNextTaskPrio?????OSNextTaskPrio++);
????OSTaskRunningPrio?=?OSNextTaskPrio?;
??cli();??//保護堆棧轉換
??SP=TCB[OSTaskRunningPrio].OSTaskStackTop;
??sei();
????
??//根據中斷時的出棧次序????
??__asm__?__volatile__("POP??R29??????????????????nt");????
??__asm__?__volatile__("POP??R28??????????????????nt");????????
??__asm__?__volatile__("POP??R31??????????????????nt");????
??__asm__?__volatile__("POP??R30??????????????????nt");????
??__asm__?__volatile__("POP??R27??????????????????nt");????
??__asm__?__volatile__("POP??R26??????????????????nt");????
??__asm__?__volatile__("POP??R25??????????????????nt");????
??__asm__?__volatile__("POP??R24??????????????????nt");????
??__asm__?__volatile__("POP??R23??????????????????nt");????
??__asm__?__volatile__("POP??R22??????????????????nt");????
??__asm__?__volatile__("POP??R21??????????????????nt");????
??__asm__?__volatile__("POP??R20??????????????????nt");????
??__asm__?__volatile__("POP??R19??????????????????nt");
??__asm__?__volatile__("POP??R18??????????????????nt");
??__asm__?__volatile__("POP??__tmp_reg__??????????nt");??????//SERG?出棧并恢復
??__asm__?__volatile__("OUT??__SREG__,__tmp_reg__?nt");??????//
??__asm__?__volatile__("POP??__tmp_reg__??????????nt");??????//R0?出棧
??__asm__?__volatile__("POP??__zero_reg__?????????nt");??????//R1?出棧
??//中斷時出棧完成
??__asm__?__volatile__("CLI???????????????????????nt");??//關中斷????
??__asm__?__volatile__("SBRC?R16,1????????????????nt");??//SBRC當寄存器位為0剛跳過下一條指令
??//檢查是在調度時,是否有中斷要求任務調度?0x02是中斷要求調度的標志位
??__asm__?__volatile__("RJMP?OSSched??????????????nt");??//重新調度
??__asm__?__volatile__("LDI??R16,0x00?????????????nt");??
??//清除中斷要求任務切換的標志位,清除正在任務切換標志位
??__asm__?__volatile__("RETI??????????????????????nt");?????//返回并開中斷
}
//從中斷退出并進行調度
void?IntSwitch(void)
{????
??//當中斷無嵌套,并且沒有在切換任務的過程中,直接進行任務切換
??if(OSCoreState?==?0x02?&&?IntNum==0)?
??{
????//進入中斷時,已經保存了SREG和R0,R1,R18~R27,R30,R31
????__asm__?__volatile__("POP??R31??????????????????nt");??//去除因調用子程序而入棧的PC
????__asm__?__volatile__("POP??R31??????????????????nt");
????__asm__?__volatile__("LDI??R16,0x01?????????????nt");??
????//清除中斷要求任務切換的標志位,設置正在任務切換標志位
????__asm__?__volatile__("RJMP?Int_OSSched??????????nt");??//重新調度
??}
}
//?任務延時
void?OSTimeDly(unsigned?int?ticks)
{
??if(ticks)?????????????????????????????//當延時有效
??{
????OSRdyTbl?&=?~(0x01<
????OSSched();??????????????????????????//從新調度
??}
}
void?TCN0Init(void)????//?計時器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)?????????//當任務時鐘到時,必須是由定時器減時的才行
??????{??
????????OSRdyTbl?|=?(0x01<????????OSCoreState|=0x02;??????????????//要求任務切換的標志位
??????}
????}
??}
??TCNT0=100;
??cli();
??IntNum--;???????????????//中斷嵌套-1
??IntSwitch();?????????//進行任務調度
}
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();??????//反復進行調度
??}
}
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();
}
第八篇:占先式內核(完善的服務)
如果將前面所提到的占先式內核和協作式內核組合在一起,很容易就可以得到一個功能較為完善的占先式內核,它的功能有:
????1,掛起和恢復任務
????2,任務延時
????3,信號量(包括共享型和獨占型)
????另外,在本例中,在各個任務中加入了從串口發送任務狀態的功能。
????
#include?
#include?
#include?
unsigned?char?Stack[400];
register?unsigned?char?OSRdyTbl??????????asm("r2");????//任務運行就緒表
register?unsigned?char?OSTaskRunningPrio?asm("r3");????//正在運行的任務
register?unsigned?char?IntNum????????????asm("r4");?????//中斷嵌套計數器
//只有當中斷嵌套數為0,并且有中斷要求時,才能在退出中斷時,進行任務調度
register?unsigned?char?OSCoreState???????asm("r16");?//?系統核心標志位?,R16?編譯器沒有使用
//只有大于R15的寄存器才能直接賦值?例LDI?R16,0x01
//0x01?正在任務?切換??0x02?有中斷要求切換
#define?OS_TASKS?3????????????????????//設定運行任務的數量
struct?TaskCtrBlock
{
??unsigned?int?OSTaskStackTop;??//保存任務的堆棧頂
??unsigned?int?OSWaitTick;??????//任務延時時鐘
}?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");
//建立任務
void?OSTaskCreate(void?(*Task)(void),unsigned?char?*Stack,unsigned?char?TaskID)
{
??unsigned?char?i;?????????????????????
??*Stack--=(unsigned?int)Task>>8;????//將任務的地址高位壓入堆棧,
??*Stack--=(unsigned?int)Task;?????????//將任務的地址低位壓入堆棧,
????
??*Stack--=0x00;?????????????????????//R1?__zero_reg__????????????
??*Stack--=0x00;?????????????????????//R0?__tmp_reg__
??*Stack--=0x80;????????????????????????????????????????
//SREG?在任務中,開啟全局中斷????????
??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;????//將人工堆棧的棧頂,保存到堆棧的數組中
??OSRdyTbl|=0x01<
//開始任務調度,從最低優先級的任務的開始
void?OSStartTask()????????
{
??OSTaskRunningPrio=OS_TASKS;
??SP=TCB[OS_TASKS].OSTaskStackTop+17;
??__asm__?__volatile__(????"reti"???????"nt"??);?
}
//進行任務調度
void?OSSched(void)
{?
??__asm__?__volatile__("LDI??R16,0x01?????????????nt");??
??//清除中斷要求任務切換的標志位,設置正在任務切換標志位
??__asm__?__volatile__("SEI???????????????????????nt");??????
??//開中斷,因為如果因中斷在任務調度中進行,要重新進行調度時,已經關中斷
???//??根據中斷時保存寄存器的次序入棧,模擬一次中斷后,入棧的情況??
??__asm__?__volatile__("PUSH?__zero_reg__?????????nt");??//R1
??__asm__?__volatile__("PUSH?__tmp_reg__??????????nt");??//R0?
??__asm__?__volatile__("IN???__tmp_reg__,__SREG__?nt");??//保存狀態寄存器SREG
??__asm__?__volatile__("PUSH?__tmp_reg__??????????nt");
??__asm__?__volatile__("CLR??__zero_reg__?????????nt");??//R0重新清零
??__asm__?__volatile__("PUSH?R18??????????????????nt");
??__asm__?__volatile__("PUSH?R19??????????????????nt");
??__asm__?__volatile__("PUSH?R20??????????????????nt");
??__asm__?__volatile__("PUSH?R21??????????????????nt");
??__asm__?__volatile__("PUSH?R22??????????????????nt");
??__asm__?__volatile__("PUSH?R23??????????????????nt");
??__asm__?__volatile__("PUSH?R24??????????????????nt");
??__asm__?__volatile__("PUSH?R25??????????????????nt");
??__asm__?__volatile__("PUSH?R26??????????????????nt");
??__asm__?__volatile__("PUSH?R27??????????????????nt");
??__asm__?__volatile__("PUSH?R30??????????????????nt");????
??__asm__?__volatile__("PUSH?R31??????????????????nt");
????
??__asm__?__volatile__("Int_OSSched:??????????????nt");??//當中斷要求調度,直接進入這里
??__asm__?__volatile__("SEI???????????????????????nt");?
????//開中斷,因為如果因中斷在任務調度中進行,已經關中斷?
??__asm__?__volatile__("PUSH?R28??????????????????nt");??//R28與R29用于建立在堆棧上的指針
??__asm__?__volatile__("PUSH?R29??????????????????nt");??//入棧完成
????
??TCB[OSTaskRunningPrio].OSTaskStackTop=SP;???????????//將正在運行的任務的堆棧底保存
??unsigned?char?OSNextTaskPrio;????????????????????????????//在現有堆棧上開設新的空間?
??for?(OSNextTaskPrio?=?0;?????????????????????????????????//進行任務調度
????OSNextTaskPrio?????OSNextTaskPrio++);
????OSTaskRunningPrio?=?OSNextTaskPrio?;
??cli();??//保護堆棧轉換
??SP=TCB[OSTaskRunningPrio].OSTaskStackTop;
??sei();
????
??//根據中斷時的出棧次序????
??__asm__?__volatile__("POP??R29??????????????????nt");????
??__asm__?__volatile__("POP??R28??????????????????nt");????????
??__asm__?__volatile__("POP??R31??????????????????nt");????
??__asm__?__volatile__("POP??R30??????????????????nt");????
??__asm__?__volatile__("POP??R27??????????????????nt");????
??__asm__?__volatile__("POP??R26??????????????????nt");????
??__asm__?__volatile__("POP??R25??????????????????nt");????
??__asm__?__volatile__("POP??R24??????????????????nt");????
??__asm__?__volatile__("POP??R23??????????????????nt");????
??__asm__?__volatile__("POP??R22??????????????????nt");????
??__asm__?__volatile__("POP??R21??????????????????nt");????
??__asm__?__volatile__("POP??R20??????????????????nt");????
??__asm__?__volatile__("POP??R19??????????????????nt");
??__asm__?__volatile__("POP??R18??????????????????nt");
??__asm__?__volatile__("POP??__tmp_reg__??????????nt");??????//SERG?出棧并恢復
??__asm__?__volatile__("OUT??__SREG__,__tmp_reg__?nt");??????//
??__asm__?__volatile__("POP??__tmp_reg__??????????nt");??????//R0?出棧
??__asm__?__volatile__("POP??__zero_reg__?????????nt");??????//R1?出棧
??//中斷時出棧完成
??__asm__?__volatile__("CLI???????????????????????nt");??//關中斷????
??__asm__?__volatile__("SBRC?R16,1????????????????nt");??//SBRC當寄存器位為0剛跳過下一條指令
??//檢查是在調度時,是否有中斷要求任務調度?0x02是中斷要求調度的標志位
??__asm__?__volatile__("RJMP?OSSched??????????????nt");??//重新調度
??__asm__?__volatile__("LDI??R16,0x00?????????????nt");??
??//清除中斷要求任務切換的標志位,清除正在任務切換標志位
??__asm__?__volatile__("RETI??????????????????????nt");?????//返回并開中斷
}
//從中斷退出并進行調度
void?IntSwitch(void)
{????
??//當中斷無嵌套,并且沒有在切換任務的過程中,直接進行任務切換
??if(OSCoreState?==?0x02?&&?IntNum==0)?
??{
????//進入中斷時,已經保存了SREG和R0,R1,R18~R27,R30,R31
????__asm__?__volatile__("POP??R31??????????????????nt");??//去除因調用子程序而入棧的PC
????__asm__?__volatile__("POP??R31??????????????????nt");
????__asm__?__volatile__("LDI??R16,0x01?????????????nt");??
????//清除中斷要求任務切換的標志位,設置正在任務切換標志位
????__asm__?__volatile__("RJMP?Int_OSSched??????????nt");??//重新調度
??}
}
////////////////////////////////////////////任務處理
//掛起任務
void?OSTaskSuspend(unsigned?char?prio)?
{
??TCB[prio].OSWaitTick=0;
??OSRdyTbl?&=?~(0x01<
????OSSched();???????????????//從新調度
}
//恢復任務?可以讓被OSTaskSuspend或?OSTimeDly暫停的任務恢復
void?OSTaskResume(unsigned?char?prio)
{
??OSRdyTbl?|=?0x01<
??if(OSTaskRunningPrio>prio)???//當要當前任務的優先級低于重置位的任務的優先級
????OSSched();???????????????//從新調度??????????????//從新調度
}
//?任務延時
void?OSTimeDly(unsigned?int?ticks)
{
??if(ticks)?????????????????????????????//當延時有效
??{
????OSRdyTbl?&=?~(0x01<
????OSSched();??????????????????????????//從新調度
??}
}
//信號量
struct?SemBlk
{
??unsigned?char?OSEventType;?????//型號?0,信號量獨占型;1信號量共享型?
??unsigned?char?OSEventState;????//狀態?0,不可用;1,可用
??unsigned?char?OSTaskPendTbl;???//等待信號量的任務列表
}?Sem[10];
//初始化信號量
void?OSSemCreat(unsigned?char?Index,unsigned?char?Type)
{
??Sem[Index].OSEventType=Type;??//型號?0,信號量獨占型;1信號量共享型?
??Sem[Index].OSTaskPendTbl=0;
??Sem[Index].OSEventState=0;
}
//任務等待信號量,掛起
//當Timeout==0xffff時,為無限延時
unsigned?char?OSTaskSemPend(unsigned?char?Index,unsigned?int?Timeout)
{
??//unsigned?char?i=0;
??if(Sem[Index].OSEventState)??????????????????????//信號量有效
??{?
????if(Sem[Index].OSEventType==0)??????????????????//如果為獨占型
????Sem[Index].OSEventState?=?0x00;????????????????//信號量被獨占,不可用
??}
??else
??{????????????????????????????????????????????????//加入信號的任務等待表
????Sem[Index].OSTaskPendTbl?|=?0x01<
????OSRdyTbl?&=?~(0x01<
????if(TCB[OSTaskRunningPrio].OSWaitTick==0?)?????//超時,未能拿到資源
??????????return?0;????????
??}
??return?1;
}
//發送一個信號量,可以從任務或中斷發送
void?OSSemPost(unsigned?char?Index)
{
if(Sem[Index].OSEventType)????????????????//當要求的信號量是共享型
??{
????Sem[Index].OSEventState=0x01;???????????//使信號量有效
????OSRdyTbl?|=Sem?[Index].OSTaskPendTbl;???//使在等待該信號的所有任務就緒
????Sem[Index].OSTaskPendTbl=0;?????????????//清空所有等待該信號的等待任務
??}??
??else???????????????????????????????????????//當要求的信號量為獨占型
??{??????
????unsigned?char?i;
????for?(i?=?0;?i?????if(i?????{
??????Sem[Index].OSTaskPendTbl?&=?~(0x01<??????OSRdyTbl?|=?0x01<????}
????else
????{
??????Sem[Index].OSEventState?=1;????????//使信號量有效
????}
??}
}
//從任務發送一個信號量,并進行調度
void?OSTaskSemPost(unsigned?char?Index)?
{
??OSSemPost(Index);
??OSSched();???
}
//清除一個信號量,只對共享型的有用。
//對于獨占型的信號量,在任務占用后,就交得不可以用了。?
void?OSSemClean(unsigned?char?Index)
{
??Sem[Index].OSEventState?=0;??????????//要求的信號量無效
}
void?TCN0Init(void)????//?計時器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)?????????//當任務時鐘到時,必須是由定時器減時的才行
??????{??
????????OSRdyTbl?|=?(0x01<????????OSCoreState|=0x02;?????????//要求任務切換的標志位
??????}
????}
??}
??TCNT0=100;
??cli();
??IntNum--;???????????????//中斷嵌套-1
??IntSwitch();?????????//進行任務調度
}
unsigned?char?__attribute__?((progmem))?proStrA[]="Task???????????????????????";
unsigned?char?strA[20];
SIGNAL(SIG_UART_RECV)????????//串口接收中斷
{
??strA[0]=UDR;
}
/////////////////////////////////////串口發送
unsigned?char?*pstr_UART_Send;
unsigned?int??nUART_Sending=0;
void?UART_Send(unsigned?char?*Res,unsigned?int?Len)????//發送字符串數組
{
??if(Len>0)
??{
????pstr_UART_Send=Res;????//發送字串的指針
????nUART_Sending=Len;????//發送字串的長度
????UCSRB=0xB8;????????????????????//發送中斷使能
??}
}
//SIGNAL?在中斷期間,其它中斷禁止
SIGNAL(SIG_UART_DATA)???????//串口發送數據中斷
{
??IntNum++;?????//中斷嵌套+1,不充許中斷
??if(nUART_Sending)????????????????????//如果未發完
??{
????UDR=*pstr_UART_Send;????????//發送字節
????pstr_UART_Send++;????????????????//發送字串的指針加1
????nUART_Sending--;????????????????//等待發送的字串數減1
??}
??if(nUART_Sending==0)????????????//當已經發送完
??{????
????OSSemPost(0);
????OSCoreState|=0x02;??????//要求任務切換的標志位
????UCSRB=0x98;????
??}
??cli();????????????????????????//關發送中斷
??IntNum--;????
??IntSwitch();?//進行任務調度
}
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();
}
結束語?
????本文中的例子,基本上用WinAVR和?Proteus調試仿真成功,一定可能存在某些方面的缺陷,因為工作上時間的壓力,就沒有進一步查找。
- RTOS(118666)
相關推薦
RTOS中的線程、進程和協程詳解
![](https://skin.elecfans.com/images/2021-soft/eye.png)
![](https://file1.elecfans.com/web2/M00/AD/33/wKgaomVMYlyAHIQWAAAc0qas8rU840.png)
3天開發一個RTOS挑戰
AVR單片機的C語言開發環境建立
RTOS在中國
RTOS的ABC討論(轉)
avr proteus仿真第一課:安裝設置avr studio和winavr
DIY一款屬于自己的智能家居
DIY制作屬于自己的數字示波器
STM32 RTOS介紹
[推薦]·零基礎新手建站教程系列:如何一步一步的搭建屬于自己的獨立網站
[討論]如何一步一步的搭建屬于自己的獨立網站
【先楫HPM5361EVK開發板試用體驗】-06-如何自己建立一個工程
【福利】手把手教你 從“0”開始去獨立寫一個RTOS
【設計技巧】rtos的核心原理簡析
為什么需要RTOS?
介紹基于AD來繪制一款屬于自己的DAPLink在線仿真+脫機燒錄器
使用Vitis HLS創建屬于自己的IP相關資料分享
關于自己建立PCB庫的問題,有點兒急。。。。
內部時鐘和RTOS系統
剛剛打開一個AVR工程,出問題,編譯也沒有辦法進行
剛開始接觸AVR 自己DIY AVR ISP下載 成功
剛開始接觸AVR 自己DIY AVR ISP下載 成功
發一個自制的RTOS,適合學習和極簡開發
在RTOS框架下使用ROSSerial
如何建立一個屬于自己的AVR的RTOS
如何去實現一種基于ARM9的多任務搶占式調度器設計
如何在RTOS中使用看門狗
如何學習RTOS
如何用ICCV7 For AVR建立單片機程序工程
如何編寫屬于自己的算法呢
如何規劃建立屬于自己的電子工作室
嵌入式系列:RTOS專題資料合集
帶有Arduion IDE和RTOS的ESP8266可能嗎?
開發中用到的RTOS和TSOS有什么區別
能否提供一個關于如何使用RTOS配置TouchGFX的示例
請大神給一個AVR燒寫器的原理圖
讀什么,才能找到屬于自己的生命的意義?
轉:構建屬于自己的AVR操作系統
如何建立一個屬于自己的AVR的RTOS
![](https://skin.elecfans.com/images/2021-soft/load.png)
編寫屬于自己的PCB設計規則檢查器
![](https://skin.elecfans.com/images/2021-soft/eye.png)
AVR單片機的RTOS AVRX應用的資料介紹
![](https://skin.elecfans.com/images/2021-soft/load.png)
如何才能建立一個屬于自己AVR的RTOS
![](https://skin.elecfans.com/images/2021-soft/load.png)
如何建立一個屬于自己的AVR的RTOS詳細教程說明
![](https://skin.elecfans.com/images/2021-soft/load.png)
如何搭建一個屬于自己的簡易電動滑板?
![](https://skin.elecfans.com/images/2021-soft/eye.png)
![](https://file.elecfans.com/web1/M00/EF/EE/pIYBAGCjKdyAE-hcAAAkC3C4ljM317.png)
51操作系統學習筆記(三):建立一個屬于自己的AVR的RTOS(1)
![](https://skin.elecfans.com/images/2021-soft/load.png)
51操作系統學習筆記(四):建立一個屬于自己的AVR的RTOS(2)
![](https://skin.elecfans.com/images/2021-soft/load.png)
如何快速打造屬于自己的工業物聯網云平臺
![](https://skin.elecfans.com/images/2021-soft/eye.png)
![](https://file1.elecfans.com/web2/M00/BF/56/wKgaomWyG7-AbGkwAA0BEPucs4s524.png)
評論