當前位置:首頁 > 嵌入式 > 嵌入式軟件
[導讀]建立一個屬于自己的AVR的RTOS

自從03年以來,對單片機的RTOS的學習和應用的熱潮可謂一浪高過一浪.03年,在離開校園前的,非典的那幾個月,在華師的后門那里買了本邵貝貝的《UCOSII》,通讀了幾次,沒有實驗器材,也不了了之。

    在21IC上,大家都可以看到楊屹寫的關于UCOSII在51上的移植,于是掀起了51上的RTOS的熱潮。

    再后來,陳明計先生推出的small rots,展示了一個用在51上的微內(nèi)核,足以在52上進行任務調(diào)度。

    前段時間,在ouravr上面開有專門關于AVR的Rtos的專欄,并且不少的兄弟把自己的作品拿出來,著實開了不少眼界。這時,我重新回顧了使用單片機的經(jīng)歷,覺得很有必要,從根本上對單片機的RTOS的知識進行整理,于是,我開始了編寫一個用在AVR單片機的RTOS。
   
    當時,我所有的知識和資源有: 

    Proteus6.7        可以用來模擬仿真avr系列的單片機
    WinAVR v2.0.5.48  基于GCC AVR的編譯環(huán)境,好處在于可以在C語言中插入asm的語句
    mega8  1K的ram有8K的rom,是開發(fā)8位的RTOS的一個理想的器件,并且我對它也比較熟悉。
    
    寫UCOS的Jean J.Labrosse在他的書上有這樣一句話,“漸漸地,我自然會想到,寫個實時內(nèi)核直有那么難嗎?不就是不斷地保存,恢復CPU的那些寄存器嘛。” 

    好了,當這一切準備好后,我們就可以開始我們的Rtos for mega8的實驗之旅了。
   
    本文列出的例子,全部完整可用。只需要一個文件就可以編譯了。我相信,只要適當可用,最簡單的就是最好的,這樣可以排除一些不必要的干擾,讓大家專注到每一個過程的學習。

第一篇:函數(shù)的運行

    在一般的單片機系統(tǒng)中,是以前后臺的方式(大循環(huán)+中斷)來處理數(shù)據(jù)和作出反應的。
    例子如下:
   
    makefile的設定:運行WinAvr中的Mfile,設定如下
    MCU Type: mega8
    Optimization level: s
    Debug format :AVR-COFF
    C/C++ source file: 選譯要編譯的C文件

#include <avr/io.h>
void fun1(void)
{
  unsigned char i=0;
  while(1)
  {
    PORTB=i++;
    PORTC=0x01<<(i%8);
  }
}

int main(void)
{
  fun1();
}

    首先,提出一個問題:如果要調(diào)用一個函數(shù),真是只能以上面的方式進行嗎?
    相信學習過C語言的各位會回答,No!我們還有一種方式,就是“用函數(shù)指針變量調(diào)用函數(shù)”,如果大家都和我一樣,當初的教科書是譚浩強先生的《C程序設計》的話,請找回書的第9.5節(jié)。
    
    例子:用函數(shù)指針變量調(diào)用函數(shù)


#include <avr/io.h>
void fun1(void)
{
  unsigned char i=0;
  while(1)
  {
    PORTB=i++;
    PORTC=0x01<<(i%8);
  }
}
void (*pfun)();  //指向函數(shù)的指針

int main(void)
{

  pfun=fun1;    //
  (*pfun)();    //運行指針所指向的函數(shù)
}
    
     第二種,是“把指向函數(shù)的指針變量作函數(shù)參數(shù)”
     
#include <avr/io.h>
void fun1(void)
{
  unsigned char i=0;
  while(1)
  {
    PORTB=i++;
    PORTC=0x01<<(i%8);
  }
}

void RunFun(void (*pfun)())  //獲得了要傳遞的函數(shù)的地址
{
  (*pfun)();                 //在RunFun中,運行指針所指向的函數(shù)
}

int main(void)
{
   RunFun(fun1);            //將函數(shù)的指針作為變量傳遞
    
}

    看到上面的兩種方式,很多人可能會說,“這的確不錯”,但是這樣與我們想要的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

    在調(diào)用void RunFun(void (*pfun)())的時候,的確可以把fun1的地址通過r24和r25傳遞給RunFun()。但是,RTOS如何才能有效地利用函數(shù)的地址呢?

第二篇: 人工堆棧

在單片機的指令集中,一類指令是專門與堆棧和PC指針打道的,它們是
    rcall   相對調(diào)用子程序指令
    icall   間接調(diào)用子程序指令
    ret     子程序返回指令
    reti    中斷返回指令   

    對于ret和reti,它們都可以將堆棧棧頂?shù)膬蓚€字節(jié)被彈出來送入程序計數(shù)器PC中,一般用來從子程序或中斷中退出。其中reti還可以在退出中斷時,重開全局中斷使能。
    有了這個基礎,就可以建立我們的人工堆棧了。
    例:
#include <avr/io.h>
void fun1(void)
{
  unsigned char i=0;
  while(1)
  {
    PORTB=i++;
    PORTC=0x01<<(i%8);
  }
}

unsigned char Stack[100]; //建立一個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 nt");    //返回并開中斷,開始運行fun1()

}

int main(void)
{
   RunFunInNewStack(fun1,&Stack[99]);
}
     RunFunInNewStack(),將指向函數(shù)的指針的值保存到一個unsigned  char的數(shù)組Stack中,作為人工堆棧。并且將棧頂?shù)臄?shù)值傳遞組堆棧指針SP,因此當用"ret"返回時,從SP中恢復到PC中的值,就變?yōu)榱酥赶騠un1()的地址,開始運行fun1().

    上面例子中在RunFunInNewStack()的最后一句嵌入了匯編代碼 "ret",實際上是可以去除的。因為在RunFunInNewStack()返回時,編譯器已經(jīng)會加上"ret"。我特意寫出來,是為了讓大家看到用"ret"作為返回后運行fun1()的過程。

第三篇:GCC中對寄存器的分配與使用

在很多用于AVR的RTOS中,都會有任務調(diào)度時,插入以下的語句:
    
    入棧:
    __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");

    通常大家都會認為,在任務調(diào)度開始時,當然要將所有的通用寄存器都保存,并且還應該保存程序狀態(tài)寄存器SREG。然后再根據(jù)相反的次序,將新任務的寄存器的內(nèi)容恢復。
    
    但是,事實真的是這樣嗎?如果大家看過陳明計先生寫的small rots51,就會發(fā)現(xiàn),它所保存的通用寄存器不過是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): 調(diào)用函數(shù)時作為參數(shù)傳遞,也就是用得最多的寄存器。

    2 Call-saved registers (r2-r17, r28-r29): 調(diào)用函數(shù)時作為結果傳遞,當中的r28和r29可能會被作為指向堆棧上的變量的指針。
    
    3 Fixed registers (r0, r1): 固定作用。r0用于存放臨時數(shù)據(jù),r1用于存放0。
    
    
    還有另一個問題是"How to permanently bind a variable to a register?",是將變量綁定到通用寄存器的方法。而且我發(fā)現(xiàn),如果將某個寄存器定義為變量,編譯器就會不將該寄存器分配作其它用途。這對RTOS是很重要的。

    在"Inline Asm"中的"C Names Used in Assembler Code"明確表示,如果將太多的通用寄存器定義為變量,剛在編譯的過程中,被定義的變量依然可能被編譯器占用。

    大家可以比較以下兩個例子,看看編譯器產(chǎn)生的代碼:(在*.lst文件中)

第一個例子:沒有定義通用寄存器為變量

#include <avr/io.h>

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 <avr/io.h>

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

    當然,在上面兩個例子中,有部份代碼被編譯器優(yōu)化了。 
    
    通過反復測試,發(fā)現(xiàn)編譯器一般使用如下寄存器:
    第1類寄存器,第2類寄存器的r28,r29,第3類寄存器

    如在中斷函數(shù)中有調(diào)用基它函數(shù),剛會在進入中斷后,固定地將第1類寄存器和第3類寄存器入棧,在退出中斷又將它們出棧。

 

第四篇:只有延時服務的協(xié)作式的內(nèi)核

   Cooperative Multitasking

   前后臺系統(tǒng),協(xié)作式內(nèi)核系統(tǒng),與占先式內(nèi)核系統(tǒng),有什么不同呢?

   記得在21IC上看過這樣的比喻,“你(小工)在用廁所,經(jīng)理在外面排第一,老板在外面排第二。如果是前后臺,不管是誰,都必須按排隊的次序使用廁所;如果是協(xié)作式,那么可以等你用完廁所,老板就要比經(jīng)理先進入;如果是占先式,只要有更高級的人在外面等,那么廁所里無論是誰,都要第一時間讓出來,讓最高級別的人先用。”


#include <avr/io.h>
#include <avr/Interrupt.h>
#include <avr/signal.h>
unsigned char Stack[200];

register unsigned char OSRdyTbl          asm("r2");    //任務運行就緒表
register unsigned char OSTaskRunningPrio asm("r3");    //正在運行的任務

#define OS_TASKS 3                    //設定運行任務的數(shù)量
struct TaskCtrBlock           //任務控制塊
{
  unsigned int OSTaskStackTop;  //保存任務的堆棧頂 [!--empirenews.page--]
  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;    //將人工堆棧的棧頂,保存到堆棧的數(shù)組中
    OSRdyTbl|=0x01<<TaskID;      //任務就緒表已經(jīng)準備好
}

//開始任務調(diào)度,從最低優(yōu)先級的任務的開始
void OSStartTask()        
{
  OSTaskRunningPrio=OS_TASKS;
  SP=TCB[OS_TASKS].OSTaskStackTop+17;
  __asm__ __volatile__(    "reti"       "nt"  ); 
}

//進行任務調(diào)度
void OSSched(void)

   //  根據(jù)中斷時保存寄存器的次序入棧,模擬一次中斷后,入棧的情況  
  __asm__ __volatile__("PUSH __zero_reg__         nt");  //R1
  __asm__ __volatile__("PUSH __tmp_reg__          nt");  //R0 
  __asm__ __volatile__("IN   __tmp_reg__,__SREG__ nt");  //保存狀態(tài)寄存器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;                             //在現(xiàn)有堆棧上開設新的空間 
  for (OSNextTaskID = 0;                                  //進行任務調(diào)度
    OSNextTaskID < OS_TASKS && !(OSRdyTbl & (0x01<<OSNextTaskID)); 
    OSNextTaskID++);
    OSTaskRunningPrio = OSNextTaskID ;

  cli();  //保護堆棧轉(zhuǎn)換
  SP=TCB[OSTaskRunningPrio].OSTaskStackTop;
  sei();
    
    //根據(jù)中斷時的出棧次序    
  __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<<OSTaskRunningPrio);         
    TCB[OSTaskRunningPrio].OSWaitTick=ticks;
    OSSched();                          //從新調(diào)度
  }
}


void TCN0Init(void)    // 計時器0
{
  TCCR0 = 0;
  TCCR0 |= (1<<CS02);  // 256預分頻
  TIMSK |= (1<<TOIE0); // T0溢出中斷允許                  
  TCNT0 = 100;         // 置計數(shù)起始值
    
}


SIGNAL(SIG_OVERFLOW0)
{
  unsigned char i;
  for(i=0;i<OS_TASKS;i++)       //任務時鐘
  {
    if(TCB[i].OSWaitTick) 
    {
      TCB[i].OSWaitTick--;
      if(TCB[i].OSWaitTick==0)     //當任務時鐘到時,必須是由定時器減時的才行
      {  
        OSRdyTbl |= (0x01<<i);     //使任務在就緒表中置位   
      }
    }
  }
  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();      //反復進行調(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();
}

   在上面的例子中,一切變得很簡單,三個正在運行的主任務,都通過延時服務,主動放棄對CPU的控制權。
   在時間中斷中,對各個任務的的延時進行計時,如果某個任務的延時結束,將任務重新在就緒表中置位。
   最低級的系統(tǒng)任務TaskScheduler(),在三個主任務在放棄對CPU的控制權后開始不斷地進行調(diào)度。如果某個任務在就緒表中置位,通過調(diào)度,進入最高級別的任務中繼續(xù)運行。

第五篇: 完善的協(xié)作式的內(nèi)核  

  現(xiàn)在為上面的協(xié)作式內(nèi)核添加一些OS中所必須的服務:
   1  掛起和重新運行任務
   2  信號量(在必要時候,可以擴展成郵箱和信息隊列)
   3  延時

   
#include <avr/io.h>
#include <avr/Interrupt.h>
#include <avr/signal.h>
unsigned char Stack[400];

register unsigned char OSRdyTbl          asm("r2");    //任務運行就緒表
register unsigned char OSTaskRunningPrio asm("r3");    //正在運行的任務

#define OS_TASKS 3                    //設定運行任務的數(shù)量
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;    //將人工堆棧的棧頂,保存到堆棧的數(shù)組中
  OSRdyTbl|=0x01<<TaskID;      //任務就緒表已經(jīng)準備好
}

//開始任務調(diào)度,從最低優(yōu)先級的任務的開始
void OSStartTask()        
{
  OSTaskRunningPrio=OS_TASKS;
  SP=TCB[OS_TASKS].OSTaskStackTop+17;
  __asm__ __volatile__(    "reti"       "nt"  ); 
}

//進行任務調(diào)度
void OSSched(void)

   //  根據(jù)中斷時保存寄存器的次序入棧,模擬一次中斷后,入棧的情況  
  __asm__ __volatile__("PUSH __zero_reg__         nt");  //R1
  __asm__ __volatile__("PUSH __tmp_reg__          nt");  //R0 
  __asm__ __volatile__("IN   __tmp_reg__,__SREG__ nt");  //保存狀態(tài)寄存器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用于建立在堆棧上的指針 [!--empirenews.page--]
  __asm__ __volatile__("PUSH R29                  nt");  //入棧完成
    
  TCB[OSTaskRunningPrio].OSTaskStackTop=SP;           //將正在運行的任務的堆棧底保存

  unsigned char OSNextTaskID;                             //在現(xiàn)有堆棧上開設新的空間 
  for (OSNextTaskID = 0;                                  //進行任務調(diào)度
    OSNextTaskID < OS_TASKS && !(OSRdyTbl & (0x01<<OSNextTaskID)); 
    OSNextTaskID++);
    OSTaskRunningPrio = OSNextTaskID ;

  cli();  //保護堆棧轉(zhuǎn)換
  SP=TCB[OSTaskRunningPrio].OSTaskStackTop;
  sei();
    
    //根據(jù)中斷時的出棧次序    
  __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<<prio); //從任務就緒表上去除標志位
  if(OSTaskRunningPrio==prio)  //當要掛起的任務為當前任務
    OSSched();               //從新調(diào)度
}

//恢復任務 可以讓被OSTaskSuspend或 OSTimeDly暫停的任務恢復
void OSTaskResume(unsigned char prio)
{
  OSRdyTbl |= 0x01<<prio;    //從任務就緒表上重置標志位
    TCB[prio].OSWaitTick=0;        //將時間計時設為0,到時
  if(OSTaskRunningPrio>prio)   //當要當前任務的優(yōu)先級低于重置位的任務的優(yōu)先級
    OSSched();               //從新調(diào)度              //從新調(diào)度
}

// 任務延時
void OSTimeDly(unsigned int ticks)
{
  if(ticks)                             //當延時有效
  {
    OSRdyTbl &= ~(0x01<<OSTaskRunningPrio);         
    TCB[OSTaskRunningPrio].OSWaitTick=ticks;
    OSSched();                          //從新調(diào)度
  }
}


//信號量
struct SemBlk
{
  unsigned char OSEventType;     //型號 0,信號量獨占型;1信號量共享型 
  unsigned char OSEventState;    //狀態(tài) 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<<OSTaskRunningPrio; 
    OSRdyTbl &= ~(0x01<<OSTaskRunningPrio);  //從任務就緒表中去除    
    TCB[OSTaskRunningPrio].OSWaitTick=Timeout;    //如延時為0,剛無限等待
    OSSched();   //從新調(diào)度
    if(TCB[OSTaskRunningPrio].OSWaitTick==0) return 0;    
  }
  return 1;
}



//發(fā)送一個信號量,可以從任務或中斷發(fā)送
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 < OS_TASKS && !(Sem[Index].OSTaskPendTbl & (0x01<<i));  i++);
    if(i < OS_TASKS)                       //如果有任務需要
    {
      Sem[Index].OSTaskPendTbl &= ~(0x01<<i); //從等待表中去除
      OSRdyTbl |= 0x01<<i;                     //任務就緒
    }
    else
    {
      Sem[Index].OSEventState =1;        //使信號量有效
    }
  }
}

//從任務發(fā)送一個信號量,并進行調(diào)度
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<<CS02);  // 256預分頻
  TIMSK |= (1<<TOIE0); // T0溢出中斷允許                  
  TCNT0 = 100;         // 置計數(shù)起始值
    
}


SIGNAL(SIG_OVERFLOW0)
{
  unsigned char i;
  for(i=0;i<OS_TASKS;i++)       //任務時鐘
  {
    if(TCB[i].OSWaitTick) 
    {
      TCB[i].OSWaitTick--;
      if(TCB[i].OSWaitTick==0)     //當任務時鐘到時,必須是由定時器減時的才行
      {  
        OSRdyTbl |= (0x01<<i);     //使任務在就緒表中置位   
      }
    }
  }
  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();      //反復進行調(diào)度
  }
}


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();
}
第六篇:時間片輪番調(diào)度法的內(nèi)核

   Round-Robin Sheduling

    時間片輪調(diào)法是非常有趣的。本篇中的例子,建立了3個任務,任務沒有優(yōu)先級,在時間中斷的調(diào)度下,每個任務都輪流運行相同的時間。如果在內(nèi)核中沒有加入其它服務,感覺上就好像是有三個大循環(huán)在同時運行。

    本例只是提供了一個用時間中斷進行調(diào)度的內(nèi)核,大家可以根據(jù)自己的需要,添加相應的服務。
    要注意到: 
    1,由于在時間中斷內(nèi)調(diào)用了任務切換函數(shù),因為在進入中斷時,已經(jīng)將一系列的寄存器入棧。
    2,在中斷內(nèi)進行調(diào)度,是直接通過"RJMP Int_OSSched"進入任務切換和調(diào)度的,這是GCC AVR的一個特點,為用C編寫內(nèi)核提供了極大的方便。
    3,在閱讀代碼的同時,請對照閱讀編譯器產(chǎn)生的 *.lst文件,會對你理解例子有很大的幫助。
   
#include <avr/io.h>
#include <avr/Interrupt.h>
#include <avr/signal.h>
unsigned char Stack[400];

register unsigned char OSRdyTbl          asm("r2");    //任務運行就緒表
register unsigned char OSTaskRunningPrio asm("r3");    //正在運行的任務

#define OS_TASKS 3                    //設定運行任務的數(shù)量
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;    //將人工堆棧的棧頂,保存到堆棧的數(shù)組中
  OSRdyTbl|=0x01<<TaskID;      //任務就緒表已經(jīng)準備好
}

//開始任務調(diào)度,從最低優(yōu)先級的任務的開始
void OSStartTask()        
{
  OSTaskRunningPrio=OS_TASKS;
  SP=TCB[OS_TASKS].OSTaskStackTop+17;
  __asm__ __volatile__(    "reti"       "nt"  ); 
}

//進行任務調(diào)度
void OSSched(void)

   //  根據(jù)中斷時保存寄存器的次序入棧,模擬一次中斷后,入棧的情況  
  __asm__ __volatile__("PUSH __zero_reg__         nt");  //R1 [!--empirenews.page--]  __asm__ __volatile__("PUSH __tmp_reg__          nt");  //R0 
  __asm__ __volatile__("IN   __tmp_reg__,__SREG__ nt");  //保存狀態(tài)寄存器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");  //當中斷要求調(diào)度,直接進入這里
  __asm__ __volatile__("PUSH R28                  nt");  //R28與R29用于建立在堆棧上的指針
  __asm__ __volatile__("PUSH R29                  nt");  //入棧完成
    
  TCB[OSTaskRunningPrio].OSTaskStackTop=SP;           //將正在運行的任務的堆棧底保存

  if(++OSTaskRunningPrio>=OS_TASKS) //輪流運行各個任務,沒有優(yōu)先級
      OSTaskRunningPrio=0;

  //cli();  //保護堆棧轉(zhuǎn)換
  SP=TCB[OSTaskRunningPrio].OSTaskStackTop;
  //sei();
    
    //根據(jù)中斷時的出棧次序    
  __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");  //去除因調(diào)用子程序而入棧的PC
  __asm__ __volatile__("POP  R31                  nt");
  __asm__ __volatile__("RJMP Int_OSSched          nt");  //重新調(diào)度
}




void TCN0Init(void)    // 計時器0
{
  TCCR0 = 0;
  TCCR0 |= (1<<CS02);  // 256預分頻
  TIMSK |= (1<<TOIE0); // T0溢出中斷允許                  
  TCNT0 = 100;         // 置計數(shù)起始值    
}


SIGNAL(SIG_OVERFLOW0)
{
  TCNT0=100;
  IntSwitch();        //任務調(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();      //反復進行調(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)核(只帶延時服務)

Preemptive Multitasking
    當大家理解時間片輪番調(diào)度法的任務調(diào)度方式后,占先式的內(nèi)核的原理,已經(jīng)伸手可及了。
    先想想,占先式內(nèi)核是在什么地方實現(xiàn)任務調(diào)度的呢?對了,它在可以在任務中進行調(diào)度,這個在協(xié)作式的內(nèi)核中已經(jīng)做到了;同時,它也可以在中斷結束后進行調(diào)度,這個問題,已經(jīng)在時間片輪番調(diào)度法中已經(jīng)做到了。
    
    由于中斷是可以嵌套的,只有當各層嵌套中要求調(diào)度,并且中斷嵌套返回到最初進入的中斷的那一層時,才能進行任務調(diào)度。
  
#include <avr/io.h>
#include <avr/Interrupt.h>
#include <avr/signal.h>
unsigned char Stack[400];

register unsigned char OSRdyTbl          asm("r2");    //任務運行就緒表
register unsigned char OSTaskRunningPrio asm("r3");    //正在運行的任務
register unsigned char IntNum            asm("r4");  //中斷嵌套計數(shù)器
//只有當中斷嵌套數(shù)為0,并且有中斷要求時,才能在退出中斷時,進行任務調(diào)度
register unsigned char OSCoreState       asm("r16"); // 系統(tǒng)核心標志位 ,R16 編譯器沒有使用
//只有大于R15的寄存器才能直接賦值 例LDI R16,0x01
//0x01 正在任務 切換  0x02 有中斷要求切換

#define OS_TASKS 3                    //設定運行任務的數(shù)量
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;    //將人工堆棧的棧頂,保存到堆棧的數(shù)組中
  OSRdyTbl|=0x01<<TaskID;      //任務就緒表已經(jīng)準備好
}

//開始任務調(diào)度,從最低優(yōu)先級的任務的開始
void OSStartTask()        
{
  OSTaskRunningPrio=OS_TASKS;
  SP=TCB[OS_TASKS].OSTaskStackTop+17;
  __asm__ __volatile__(    "reti"       "nt"  ); 
}

//進行任務調(diào)度
void OSSched(void)


  __asm__ __volatile__("LDI  R16,0x01             nt");  
  //清除中斷要求任務切換的標志位,設置正在任務切換標志位
  __asm__ __volatile__("SEI                       nt");      
  //開中斷,因為如果因中斷在任務調(diào)度中進行,要重新進行調(diào)度時,已經(jīng)關中斷
  //根據(jù)中斷時保存寄存器的次序入棧,模擬一次中斷后,入棧的情況  
  __asm__ __volatile__("PUSH __zero_reg__         nt");  //R1
  __asm__ __volatile__("PUSH __tmp_reg__          nt");  //R0 
  __asm__ __volatile__("IN   __tmp_reg__,__SREG__ nt");  //保存狀態(tài)寄存器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");  //當中斷要求調(diào)度,直接進入這里
  __asm__ __volatile__("SEI                       nt"); 
//開中斷,因為如果因中斷在任務調(diào)度中進行,已經(jīng)關中斷 
  __asm__ __volatile__("PUSH R28                  nt");  //R28與R29用于建立在堆棧上的指針
  __asm__ __volatile__("PUSH R29                  nt");  //入棧完成
    
  TCB[OSTaskRunningPrio].OSTaskStackTop=SP;           //將正在運行的任務的堆棧底保存

  unsigned char OSNextTaskPrio;                            //在現(xiàn)有堆棧上開設新的空間 
  for (OSNextTaskPrio = 0;                                 //進行任務調(diào)度
    OSNextTaskPrio < OS_TASKS && !(OSRdyTbl & (0x01<<OSNextTaskPrio)); 
    OSNextTaskPrio++);
    OSTaskRunningPrio = OSNextTaskPrio ;

  cli();  //保護堆棧轉(zhuǎn)換
  SP=TCB[OSTaskRunningPrio].OSTaskStackTop;
  sei();
    
  //根據(jù)中斷時的出棧次序    
  __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剛跳過下一條指令
  //檢查是在調(diào)度時,是否有中斷要求任務調(diào)度 0x02是中斷要求調(diào)度的標志位
  __asm__ __volatile__("RJMP OSSched              nt");  //重新調(diào)度
  __asm__ __volatile__("LDI  R16,0x00             nt");  
  //清除中斷要求任務切換的標志位,清除正在任務切換標志位
  __asm__ __volatile__("RETI                      nt");     //返回并開中斷
}


//從中斷退出并進行調(diào)度
void IntSwitch(void)
{    
  //當中斷無嵌套,并且沒有在切換任務的過程中,直接進行任務切換
  if(OSCoreState == 0x02 && IntNum==0) 
  {
    //進入中斷時,已經(jīng)保存了SREG和R0,R1,R18~R27,R30,R31
    __asm__ __volatile__("POP  R31                  nt");  //去除因調(diào)用子程序而入棧的PC
    __asm__ __volatile__("POP  R31                  nt");
    __asm__ __volatile__("LDI  R16,0x01             nt");  
    //清除中斷要求任務切換的標志位,設置正在任務切換標志位
    __asm__ __volatile__("RJMP Int_OSSched          nt");  //重新調(diào)度
  }
}

// 任務延時
void OSTimeDly(unsigned int ticks)
{
  if(ticks)                             //當延時有效
  {
    OSRdyTbl &= ~(0x01<<OSTaskRunningPrio);         
    TCB[OSTaskRunningPrio].OSWaitTick=ticks;
    OSSched();                          //從新調(diào)度
  }
}



void TCN0Init(void)    // 計時器0
{
  TCCR0 = 0;
  TCCR0 |= (1<<CS02);  // 256預分頻
  TIMSK |= (1<<TOIE0); // T0溢出中斷允許                  
  TCNT0 = 100;         // 置計數(shù)起始值
    
}

SIGNAL(SIG_OVERFLOW0)
{
  IntNum++;     //中斷嵌套+1
  sei();  //在中斷中,重開中斷
    
  unsigned char i,j=0;
  for(i=0;i<OS_TASKS;i++)        //任務時鐘
  {
    if(TCB[i].OSWaitTick) 
    {
      TCB[i].OSWaitTick--;
      if(TCB[i].OSWaitTick==0)         //當任務時鐘到時,必須是由定時器減時的才行
      {  
        OSRdyTbl |= (0x01<<i);         //使任務可以重新運行
        OSCoreState|=0x02;              //要求任務切換的標志位
      }
    }
  }
  TCNT0=100;
  cli();
  IntNum--;               //中斷嵌套-1
  IntSwitch();         //進行任務調(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();      //反復進行調(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)核(完善的服務)

如果將前面所提到的占先式內(nèi)核和協(xié)作式內(nèi)核組合在一起,很容易就可以得到一個功能較為完善的占先式內(nèi)核,它的功能有:
    1,掛起和恢復任務
    2,任務延時
    3,信號量(包括共享型和獨占型)
    另外,在本例中,在各個任務中加入了從串口發(fā)送任務狀態(tài)的功能。
    

#include <avr/io.h>
#include <avr/Interrupt.h>
#include <avr/signal.h>
unsigned char Stack[400];

register unsigned char OSRdyTbl          asm("r2");    //任務運行就緒表
register unsigned char OSTaskRunningPrio asm("r3");    //正在運行的任務
register unsigned char IntNum            asm("r4");     //中斷嵌套計數(shù)器
//只有當中斷嵌套數(shù)為0,并且有中斷要求時,才能在退出中斷時,進行任務調(diào)度
register unsigned char OSCoreState       asm("r16"); // 系統(tǒng)核心標志位 ,R16 編譯器沒有使用
//只有大于R15的寄存器才能直接賦值 例LDI R16,0x01
//0x01 正在任務 切換  0x02 有中斷要求切換

#define OS_TASKS 3                    //設定運行任務的數(shù)量
struct TaskCtrBlock
{
  unsigned int OSTaskStackTop;  //保存任務的堆棧頂
  unsigned int OSWaitTick;      //任務延時時鐘
} TCB[OS_TASKS+1];

//防止被編譯器占用 [!--empirenews.page--]
//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;    //將人工堆棧的棧頂,保存到堆棧的數(shù)組中
  OSRdyTbl|=0x01<<TaskID;      //任務就緒表已經(jīng)準備好
}

//開始任務調(diào)度,從最低優(yōu)先級的任務的開始
void OSStartTask()        
{
  OSTaskRunningPrio=OS_TASKS;
  SP=TCB[OS_TASKS].OSTaskStackTop+17;
  __asm__ __volatile__(    "reti"       "nt"  ); 
}

//進行任務調(diào)度
void OSSched(void)


  __asm__ __volatile__("LDI  R16,0x01             nt");  
  //清除中斷要求任務切換的標志位,設置正在任務切換標志位
  __asm__ __volatile__("SEI                       nt");      
  //開中斷,因為如果因中斷在任務調(diào)度中進行,要重新進行調(diào)度時,已經(jīng)關中斷
   //  根據(jù)中斷時保存寄存器的次序入棧,模擬一次中斷后,入棧的情況  
  __asm__ __volatile__("PUSH __zero_reg__         nt");  //R1
  __asm__ __volatile__("PUSH __tmp_reg__          nt");  //R0 
  __asm__ __volatile__("IN   __tmp_reg__,__SREG__ nt");  //保存狀態(tài)寄存器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");  //當中斷要求調(diào)度,直接進入這里
  __asm__ __volatile__("SEI                       nt"); 
    //開中斷,因為如果因中斷在任務調(diào)度中進行,已經(jīng)關中斷 
  __asm__ __volatile__("PUSH R28                  nt");  //R28與R29用于建立在堆棧上的指針
  __asm__ __volatile__("PUSH R29                  nt");  //入棧完成
    
  TCB[OSTaskRunningPrio].OSTaskStackTop=SP;           //將正在運行的任務的堆棧底保存

  unsigned char OSNextTaskPrio;                            //在現(xiàn)有堆棧上開設新的空間 
  for (OSNextTaskPrio = 0;                                 //進行任務調(diào)度
    OSNextTaskPrio < OS_TASKS && !(OSRdyTbl & (0x01<<OSNextTaskPrio)); 
    OSNextTaskPrio++);
    OSTaskRunningPrio = OSNextTaskPrio ;

  cli();  //保護堆棧轉(zhuǎn)換
  SP=TCB[OSTaskRunningPrio].OSTaskStackTop;
  sei();
    
  //根據(jù)中斷時的出棧次序    
  __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剛跳過下一條指令
  //檢查是在調(diào)度時,是否有中斷要求任務調(diào)度 0x02是中斷要求調(diào)度的標志位
  __asm__ __volatile__("RJMP OSSched              nt");  //重新調(diào)度
  __asm__ __volatile__("LDI  R16,0x00             nt");  
  //清除中斷要求任務切換的標志位,清除正在任務切換標志位
  __asm__ __volatile__("RETI                      nt");     //返回并開中斷
}


//從中斷退出并進行調(diào)度
void IntSwitch(void)
{    
  //當中斷無嵌套,并且沒有在切換任務的過程中,直接進行任務切換
  if(OSCoreState == 0x02 && IntNum==0) 
  {
    //進入中斷時,已經(jīng)保存了SREG和R0,R1,R18~R27,R30,R31
    __asm__ __volatile__("POP  R31                  nt");  //去除因調(diào)用子程序而入棧的PC
    __asm__ __volatile__("POP  R31                  nt");
    __asm__ __volatile__("LDI  R16,0x01             nt");  
    //清除中斷要求任務切換的標志位,設置正在任務切換標志位
    __asm__ __volatile__("RJMP Int_OSSched          nt");  //重新調(diào)度
  }
}
////////////////////////////////////////////任務處理
//掛起任務
void OSTaskSuspend(unsigned char prio) 
{
  TCB[prio].OSWaitTick=0;
  OSRdyTbl &= ~(0x01<<prio); //從任務就緒表上去除標志位
  if(OSTaskRunningPrio==prio)  //當要掛起的任務為當前任務
    OSSched();               //從新調(diào)度
}

//恢復任務 可以讓被OSTaskSuspend或 OSTimeDly暫停的任務恢復
void OSTaskResume(unsigned char prio)
{
  OSRdyTbl |= 0x01<<prio;    //從任務就緒表上重置標志位
    TCB[prio].OSWaitTick=0;        //將時間計時設為0,到時
  if(OSTaskRunningPrio>prio)   //當要當前任務的優(yōu)先級低于重置位的任務的優(yōu)先級
    OSSched();               //從新調(diào)度              //從新調(diào)度
}

// 任務延時
void OSTimeDly(unsigned int ticks)
{
  if(ticks)                             //當延時有效
  {
    OSRdyTbl &= ~(0x01<<OSTaskRunningPrio);         
    TCB[OSTaskRunningPrio].OSWaitTick=ticks;
    OSSched();                          //從新調(diào)度
  }
}


//信號量
struct SemBlk
{
  unsigned char OSEventType;     //型號 0,信號量獨占型;1信號量共享型 
  unsigned char OSEventState;    //狀態(tài) 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<<OSTaskRunningPrio; 
    TCB[OSTaskRunningPrio].OSWaitTick=Timeout;    //如延時為0,剛無限等待
    OSRdyTbl &= ~(0x01<<OSTaskRunningPrio);       //從任務就緒表中去除    
    OSSched();   //從新調(diào)度
    if(TCB[OSTaskRunningPrio].OSWaitTick==0 )     //超時,未能拿到資源
          return 0;        
  }
  return 1;
}



//發(fā)送一個信號量,可以從任務或中斷發(fā)送
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 < OS_TASKS && !(Sem[Index].OSTaskPendTbl & (0x01<<i));  i++);
    if(i < OS_TASKS)                       //如果有任務需要
    {
      Sem[Index].OSTaskPendTbl &= ~(0x01<<i); //從等待表中去除
      OSRdyTbl |= 0x01<<i;                     //任務就緒
    }
    else
    {
      Sem[Index].OSEventState =1;        //使信號量有效
    }
  }
}

//從任務發(fā)送一個信號量,并進行調(diào)度
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<<CS02);  // 256預分頻 [!--empirenews.page--]
  TIMSK |= (1<<TOIE0); // T0溢出中斷允許                  
  TCNT0 = 100;         // 置計數(shù)起始值
    
}

SIGNAL(SIG_OVERFLOW0)
{
  IntNum++;     //中斷嵌套+1
  sei();  //在中斷中,重開中斷
    
  unsigned char i;
  for(i=0;i<OS_TASKS;i++)        //任務時鐘
  {
    if(TCB[i].OSWaitTick && TCB[i].OSWaitTick!=0xffff) 
    {
      TCB[i].OSWaitTick--;
      if(TCB[i].OSWaitTick==0)         //當任務時鐘到時,必須是由定時器減時的才行
      {  
        OSRdyTbl |= (0x01<<i);         //使任務可以重新運行
        OSCoreState|=0x02;         //要求任務切換的標志位
      }
    }
  }
  TCNT0=100;
  cli();
  IntNum--;               //中斷嵌套-1
  IntSwitch();         //進行任務調(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ā)送字符串數(shù)組
{
  if(Len>0)
  {
    pstr_UART_Send=Res;    //發(fā)送字串的指針
    nUART_Sending=Len;    //發(fā)送字串的長度
    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ā)送的字串數(shù)減1
  }
  if(nUART_Sending==0)            //當已經(jīng)發(fā)送完
  {    
    OSSemPost(0);
    OSCoreState|=0x02;      //要求任務切換的標志位
    UCSRB=0x98;    
  }
  cli();                        //關發(fā)送中斷
  IntNum--;    
  IntSwitch(); //進行任務調(diào)度
}


void UARTInit()    //初始化串口
{
#define fosc 8000000 //晶振8  MHZ UBRRL=(fosc/16/(baud+1))%256;
#define baud 9600     //波特率
  OSCCAL=0x97;          //串口波特率校正值,從編程器中讀出
  //UCSRB=(1<<RXEN)|(1<<TXEN);//允許發(fā)送和接收
  UCSRB=0x98;
  //UCSRB=0x08;
  UBRRL=(fosc/16/(baud+1))%256;
  UBRRH=(fosc/16/(baud+1))/256;
  UCSRC=(1<<URSEL)|(1<<UCSZ1)|(1<<UCSZ0);//8位數(shù)據(jù)+1位STOP位
  UCSRB=0xB8;
  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<i;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調(diào)試仿真成功,一定可能存在某些方面的缺陷,因為工作上時間的壓力,就沒有進一步查找。

本站聲明: 本文章由作者或相關機構授權發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點,本站亦不保證或承諾內(nèi)容真實性等。需要轉(zhuǎn)載請聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權益,請及時聯(lián)系本站刪除。
換一批
延伸閱讀

9月2日消息,不造車的華為或?qū)⒋呱龈蟮莫毥谦F公司,隨著阿維塔和賽力斯的入局,華為引望愈發(fā)顯得引人矚目。

關鍵字: 阿維塔 塞力斯 華為

加利福尼亞州圣克拉拉縣2024年8月30日 /美通社/ -- 數(shù)字化轉(zhuǎn)型技術解決方案公司Trianz今天宣布,該公司與Amazon Web Services (AWS)簽訂了...

關鍵字: AWS AN BSP 數(shù)字化

倫敦2024年8月29日 /美通社/ -- 英國汽車技術公司SODA.Auto推出其旗艦產(chǎn)品SODA V,這是全球首款涵蓋汽車工程師從創(chuàng)意到認證的所有需求的工具,可用于創(chuàng)建軟件定義汽車。 SODA V工具的開發(fā)耗時1.5...

關鍵字: 汽車 人工智能 智能驅(qū)動 BSP

北京2024年8月28日 /美通社/ -- 越來越多用戶希望企業(yè)業(yè)務能7×24不間斷運行,同時企業(yè)卻面臨越來越多業(yè)務中斷的風險,如企業(yè)系統(tǒng)復雜性的增加,頻繁的功能更新和發(fā)布等。如何確保業(yè)務連續(xù)性,提升韌性,成...

關鍵字: 亞馬遜 解密 控制平面 BSP

8月30日消息,據(jù)媒體報道,騰訊和網(wǎng)易近期正在縮減他們對日本游戲市場的投資。

關鍵字: 騰訊 編碼器 CPU

8月28日消息,今天上午,2024中國國際大數(shù)據(jù)產(chǎn)業(yè)博覽會開幕式在貴陽舉行,華為董事、質(zhì)量流程IT總裁陶景文發(fā)表了演講。

關鍵字: 華為 12nm EDA 半導體

8月28日消息,在2024中國國際大數(shù)據(jù)產(chǎn)業(yè)博覽會上,華為常務董事、華為云CEO張平安發(fā)表演講稱,數(shù)字世界的話語權最終是由生態(tài)的繁榮決定的。

關鍵字: 華為 12nm 手機 衛(wèi)星通信

要點: 有效應對環(huán)境變化,經(jīng)營業(yè)績穩(wěn)中有升 落實提質(zhì)增效舉措,毛利潤率延續(xù)升勢 戰(zhàn)略布局成效顯著,戰(zhàn)新業(yè)務引領增長 以科技創(chuàng)新為引領,提升企業(yè)核心競爭力 堅持高質(zhì)量發(fā)展策略,塑強核心競爭優(yōu)勢...

關鍵字: 通信 BSP 電信運營商 數(shù)字經(jīng)濟

北京2024年8月27日 /美通社/ -- 8月21日,由中央廣播電視總臺與中國電影電視技術學會聯(lián)合牽頭組建的NVI技術創(chuàng)新聯(lián)盟在BIRTV2024超高清全產(chǎn)業(yè)鏈發(fā)展研討會上宣布正式成立。 活動現(xiàn)場 NVI技術創(chuàng)新聯(lián)...

關鍵字: VI 傳輸協(xié)議 音頻 BSP

北京2024年8月27日 /美通社/ -- 在8月23日舉辦的2024年長三角生態(tài)綠色一體化發(fā)展示范區(qū)聯(lián)合招商會上,軟通動力信息技術(集團)股份有限公司(以下簡稱"軟通動力")與長三角投資(上海)有限...

關鍵字: BSP 信息技術
關閉
關閉