搜索
bottom↓
回复: 37

只有延时服务的协作式的内核(DIY实时操作系统中)总结

[复制链接]

出0入0汤圆

发表于 2007-12-3 00:52:49 | 显示全部楼层 |阅读模式
原帖子是在21IC.COM,舍友后来传我看"DIY实时操作系统中",觉得很好理解,循序渐进.
昨天刚认真开始看的,花了一天半的时间看得感觉还可以,不过只是看了只有延时服务的协作式的内核那部分,后面的暂时没看.后来自己就写了个总结,花了我三个钟头,太崩溃了,希望大家有空帮我指正指正.
一般我都会写一些东西,放在我的blog上:http://blog.csdn.net/zhiyu520/
先把原来的人家写的贴出来:

/*
   第四篇:只有延时服务的协作式的内核  

                    Cooperative Multitasking
   前后台系统,协作式内核系统,与占先式内核系统,有什么不同呢?
   记得在21IC上看过这样的比喻,“你(小工)在用厕所,经理在外面排第一,
   老板在外面排第二。如果是前后台,不管是谁,都必须按排队的次序使用
   厕所;如果是协作式,那么可以等你用完厕所,老板就要比经理先进入;
   如果是占先式,只要有更高级的人在外面等,那么厕所里无论是谁,都要
   第一时间让出来,让最高级别的人先用。”
*/

#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                //设定运行任务的数量
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 tempR17 asm("r17");



//建立任务(每个任务在其本身的人工堆栈里压了19个字节)
void OSTaskCreate(void (*Task)(void),unsigned char *Stack,unsigned char TaskID)
{
  unsigned char i;  
  *Stack--=(unsigned int)Task;                //将任务的地址低位压入堆栈
  *Stack--=(unsigned int)Task>>8;        //将任务的地址高位压入堆栈
     
  *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;   
  //将人工堆栈的栈顶,保存到TCB结构体数组中
  OSRdyTbl|=0x01<<TaskID;                  //任务就绪表相应位置1,表明处于就绪态
}

//开始任务调度,从最低优先级的任务TaskScheduler开始
void OSStartTask()         
{
  OSTaskRunningPrio=OS_TASKS;                //首先运行TaskScheduler任务
  SP=TCB[OS_TASKS].OSTaskStackTop+17;        //等号右边得到任务TaskScheduler的入口地址
  __asm__ __volatile__("reti" "\t");       //ret和reti,它们都可以将堆栈栈顶SP的两个字节弹出来送入程序计数器PC中
}

//进行任务调度
void OSSched(void)
{  
   //  根据中断时保存寄存器的次序入栈(压栈不是在人工堆栈里面,而是在avr芯片本来分配的堆栈里),模拟一次中断后,入栈的情况   
  __asm__ __volatile__("PUSH __zero_reg__\t");       //R1
  __asm__ __volatile__("PUSH __tmp_reg__\t");        //R0  
  __asm__ __volatile__("IN   __tmp_reg__,__SREG__ \t");  //保存状态寄存器SREG
  __asm__ __volatile__("PUSH __tmp_reg__\t");
  __asm__ __volatile__("CLR  __zero_reg__\t");       //R0重新清零
  __asm__ __volatile__("PUSH R18\t");
  __asm__ __volatile__("PUSH R19\t");
  __asm__ __volatile__("PUSH R20\t");
  __asm__ __volatile__("PUSH R21\t");
  __asm__ __volatile__("PUSH R22\t");
  __asm__ __volatile__("PUSH R23\t");
  __asm__ __volatile__("PUSH R24\t");
  __asm__ __volatile__("PUSH R25\t");
  __asm__ __volatile__("PUSH R26\t");
  __asm__ __volatile__("PUSH R27\t");
  __asm__ __volatile__("PUSH R30\t");     
  __asm__ __volatile__("PUSH R31\t");
  __asm__ __volatile__("PUSH R28\t");  //R28与R29用于建立在堆栈上的指针
  __asm__ __volatile__("PUSH R29\t");  //入栈完成
     
  TCB[OSTaskRunningPrio].OSTaskStackTop=SP;                    //将正在运行的任务的堆栈顶保存

  unsigned char OSNextTaskID;                                    //在现有堆栈上开设新的空间  
  for (OSNextTaskID = 0;                                    //进行任务调度
    OSNextTaskID < OS_TASKS && !(OSRdyTbl & (0x01<<OSNextTaskID));  //有已就绪任务就退出循环
    OSNextTaskID++);                                            //注意分号位置,这里循环体只是那个For语句
    OSTaskRunningPrio = OSNextTaskID ;                            //得到一个新任务,调度到就执行态

  cli();  //关中断,保护堆栈转换
  SP=TCB[OSTaskRunningPrio].OSTaskStackTop;                    //OSSched函数一执行完,就可以运行这个新任务
  sei();
     
    //根据中断时的出栈次序     
  __asm__ __volatile__("POP  R29\t");     
  __asm__ __volatile__("POP  R28\t");         
  __asm__ __volatile__("POP  R31\t");     
  __asm__ __volatile__("POP  R30\t");     
  __asm__ __volatile__("POP  R27\t");     
  __asm__ __volatile__("POP  R26\t");     
  __asm__ __volatile__("POP  R25\t");     
  __asm__ __volatile__("POP  R24\t");     
  __asm__ __volatile__("POP  R23\t");     
  __asm__ __volatile__("POP  R22 \t");     
  __asm__ __volatile__("POP  R21\t");     
  __asm__ __volatile__("POP  R20\t");     
  __asm__ __volatile__("POP  R19\t");
  __asm__ __volatile__("POP  R18\t");
  __asm__ __volatile__("POP  __tmp_reg__\t");           //SERG 出栈并恢复
  __asm__ __volatile__("OUT  __SREG__,__tmp_reg__\t");
  __asm__ __volatile__("POP  __tmp_reg__\t");           //R0 出栈
  __asm__ __volatile__("POP  __zero_reg__\t");           //R1 出栈
  //中断时出栈完成
}

//使正在运行的任务自动放弃CPU,但不是进入就绪态
void OSTimeDly(unsigned int ticks)
{
  if(ticks)                                //当延时有效
  {
    OSRdyTbl &= ~(0x01<<OSTaskRunningPrio);        //任务运行就绪表相应位清0      
    TCB[OSTaskRunningPrio].OSWaitTick=ticks;//设置任务延时时钟
    OSSched();                                //从新调度
  }
}


void TCN0Init(void)    // 计时器0
{
  TCCR0 = 0;
  TCCR0 |= (1<<CS02);  // 256预分频
  TIMSK |= (1<<TOIE0); // T0溢出中断允许                  
  TCNT0 = 100;         // 置计数起始值 (递增,往255计数)   
}


SIGNAL(SIG_OVERFLOW0)
{
  unsigned char i;
  for(i=0;i<OS_TASKS;i++)                        //任务时钟
  {
    if(TCB.OSWaitTick)  
    {
      TCB.OSWaitTick--;
      if(TCB.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);
  }
}

// 任务调度任务(相当于Task3),一般优先级最低
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的控制权后开始不断地进行调度。如果
   某个任务在就绪表中置位,通过调度,进入最高级别的任务中继续运行。
*/

下面就我自己的总结,花了很大力气啊,写点以后希望对一些新手有用,声明,本人也是新手,刚学AVR,想做点小项目.

总结如下:

按函数执行顺序的详细流程文字说明:
主函数main()
        OSTaskCreate()
        函数建立了4个任务,分别是task0,task1,task2和TaskScheduler,在这个函数中,我们做了三件主要的事情
        1、我们先把每个任务的函数地址分别压到它相对应的人工堆栈里面
        2、压完了任务函数地址,我们就把人工堆栈的栈顶指针赋给TCB结构体数组里面的OSTaskStackTop中,
           保存住(其实就是为了后面的寻找相应任务的函数地址)
        3、把每个任务都在任务就绪表的相应位置1,表示任务可以被调用得到CPU的能力之一。但其实还要靠任务的优先级,有
           点需要注意的是:任务就绪表中相应位越低,优先级越高,比如,bit0就比bit1低,bit0的那个任
           务优先级就高。优先级体现在任务调度函数OSSched()里面的任务调度for循环语句中。应该仔细体会。
        OSStartTask()
                准备开始执行任务,因为函数里面“OSTaskRunningPrio=OS_TASKS;”这里(只是这里)根本就没需要考虑优先级的情况,
                所以第一个准备执行(还没开始)的任务是TaskScheduler。
                第二句SP=TCB[OS_TASKS].OSTaskStackTop+17;等号右边得到任务TaskScheduler的入口地址,接着赋给SP。
                给它干吗,当然是有用了,你还记得“2人工堆栈”里面有一句话“ 对于ret和reti,它们都可以将堆栈栈顶的
                两个字节弹出来送入程序计数器PC中,一般用来从子程序或中断中退出。”,显然,那就是等接着的第三句去完
                成他的使命了。呵呵,第三句"reti"一完事,你的avrCPU就可以去程序计数器PC里面取地址,而这个地址呢,
                就正是任务TaskScheduler的函数地址,哈哈,这才是正式开始执行第一个任务TaskScheduler。

现在,你看,我们已经不用再看主函数里面的函数了,因为以后也不会涉及到。以后的任务就是靠任务调度了。我们接着来看吧

刚才不是任务TaskScheduler是执行态嘛,这个任务又去反复不断调用OSSched函数,OSSched是个关键哦,这个函数在
这个“4只有延时服务的协作式的内核.”里面应该是最主要的,操作系统要靠调度,调度就是你妈,吃饭的时候,给
你吃饭你就可以吃,不给你吃你就那里先撑着,还给"TMD"按孩子从大到小(优先级)来分,不过不会相信你妈有那么偏心。
OK,不说废话,我们接着讲。

调度任务里面的调度函数OSSched()
        OSSched()
                一大堆PUSH指令
                这里的一大堆PUSH汇编指令是为了保存一些通用积存器寄存器,也就是保护现场,不懂的,先得看看操作系统里面的书。
                中断就是这样处理的,和这个差不多。刚才我们不是运行了第一个任务TaskScheduler吗,这里保存就是任务TaskScheduler
                的现场,保存了大堆的寄存器。需要注意的是,这里PUSH的压可不是象建立任务OSTaskCreate()里面的把寄存器压到
                每人任务相应的人工堆栈里面,而是压到avr芯片里面专门定义的一片堆栈里面。
                接着TCB[OSTaskRunningPrio].OSTaskStackTop=SP;呵呵,你看了这里的SP是区别于人工堆栈里面的栈顶指针,一旦
                这里的SP赋给了TCB[OSTaskRunningPrio].OSTaskStackTop,那么相应的,此任务的OSTaskStackTop原本指向人工堆
                栈的栈顶指针就再也找不到,看OSTaskCreate()仔细对照一下吧。现在呢,我们OSTaskStackTop保存是正在运行的任务
                的栈顶指针。其实保存SP也应该算是保存现场的一部分工作。
        
                接着
                unsigned char OSNextTaskID;                                //在现有堆栈上开设新的空间  
                for (OSNextTaskID = 0;                                //进行任务调度
                                 OSNextTaskID < OS_TASKS && !(OSRdyTbl & (0x01<<OSNextTaskID));  //有已就绪任务就退出循环
                                 OSNextTaskID++);                        //注意分号位置,这里循环体只是那个For语句
                OSTaskRunningPrio = OSNextTaskID ;                        //得到一个新任务(有可能是原来的任务),调度到就执行态
                注释也都很清楚了,关键有一点就是“得到一个新任务(有可能是原来的任务)”这句。这个要等我后面就会提到了,这
                里暂时不会。
                for里面的循环查找任务就绪表里面优先级最高的任务,都是从bit0向高位开始查找的。很显然,在这之前,我们从来
                没有运行过task0,task1,task2,况且,他们在建立任务(见OSTaskCreate())的时候的就绪位都还是置1,具体
                OSRdyTbl = 0000_1111。在检查了bit0位后就跳出了循环,所以,我们得到了将可执行的任务task0。相应的,
                我们把此时的TCB[O].OSTaskStackTop赋给SP,OSTaskStackTop是等于指向人工堆栈里面的那个栈顶指针。哈哈,你
                有点明白了吗,我们为什么在OSTaskCreate里面压那么多次的栈,其实有有用的,只可惜,它就只用一次,一夜情,用
                完就走,哈哈。SP=TCB[OSTaskRunningPrio].OSTaskStackTop后接下来我们就开始大规模的弹栈了,这次(也只有这次,
                好好再想想,再想想),我们弹出的是人工堆栈里面的那些东西。哦,还是弹17次哦,呵呵,看看OSTaskCreate(),是
                不是也是压17次,想明白了吧。你看,弹了栈后,OSSched函数准备退出,那肯定也执行了"ret",呵呵,这不,SP不是
                给了程序计数器PC,所以,我们就可以从TaskScheduler跳到task0了
                提醒一下,弹栈的时候,我们只会弹一次人工堆栈里面的东西,也只有当这个任务(这个任务可能是task0,task1,task2,
                之一,但不会是TaskScheduler,看for循环里OSNextTaskID < OS_TASKS=3)第一次准备被执行的时候(搞清楚点吧,多看下)
                也就都弹出去了,以后也不会有了,所以,task0,task1,task2的人工堆栈最后只剩下任务入口地址在那里,它没有被弹出,
                但其实它也没什么用了,一旦运行过它本身的任务后,任务函数入口地址就无意义了(为什么无意义,注意一下任务函数
                里面的那个while(1)无限循环)。用一次后就无意义,不是个套吗,一次性,哈哈。

                说到这里,感觉写得真多,不知道能不能让你明白。呵呵,接着

                我们因为上面的调度,task0就开始执行了,我们看
                void Task0()
                {
                        unsigned int j=0;
                        while(1)
                        {            
                                PORTB=j++;
                                OSTimeDly(2);
                        }
                }
                如果我们把OSTimeDly(2);注释掉,那这个任务就永远在那里运行了,也不会被调度,因为OSTimeDly()函数里面有个OSSched()。
                没有它,就不会让其他任务得到运行的机会。
                OSTimeDly()
                OSTimeDly()函数会让任务自动放弃CPU,并且任务就绪表OSRdyTbl相应为清0,任务就暂时没有执行权利了.OSRdyTbl = 00001110
                哦,只是暂时哦,什么时候有呢?那要看定时器/计数器的工作,也就是看定时器的那个中断服务程序。
                SIGNAL(SIG_OVERFLOW0)
                当每个任务时钟到时,使各自任务在就绪表中置位,这样任务就重新得到了运行的机会条件(还需要考虑优先级)之一了。

                刚才不是说到任务Task0通过OSTimeDly()-->OSSched()有进行了一次调度。
                这次调度有点象Task0任务被调度到一样。有一点不同的是,OSRdyTbl = 00001110,根据优先级,知道任务Task1会被调度到,
                调度的过程,比如说压栈,弹栈什么的同第一次调用Task0一样。
                依次类推,会接着执行到Task2,直到执行完........

                现在,你仔细想过没有,上面的过程

                TaskScheduler-->OSSched()-->Task0--->OSSched()-->Task1--->OSSched()-->Task2
                有可能不是这样吗?
                答案是有可能,不过是有可能,编写这个操作系统的人肯定考虑到这种状况了,所以没出现那个问题。
                问题出在哪?就出现在OSTimeDly(unsigned int ticks)函数的那个任务时钟ticks的设定上,期间,可能会出现很多情况。
                就用我们这个操作系统的的例子说一下吧
                有三个任务(不包括调度任务)执行,任务就绪表OSRdyTbl = 00001111
                Task0任务比较大,4.5秒 才能执行完这个任务,void Task0{ ...OSTimeDly(2秒);}
                Task1任务比较大,5.0秒 才能执行完这个任务,void Task1{ ...OSTimeDly(4秒);}
                Task2任务比较大,0.5秒 就能执行完这个任务,void Task2{ ...OSTimeDly(8秒);}
                现在,我执行了Task0,花掉了4.5秒,期间,我也让它OSTimeDly(2秒),OSRdyTbl = 00001110。当然,调用这个函数肯定不能是
                这样的,这里只是打个比方。就让它延时两秒。Task0执行完了,根据优先级,Task1将会被调用,但执行了Task1,要执行5.0秒,
                在执行Task1的5秒期间,Task0延时的2秒早就完了,它会让任务就绪表中Task0的相应位置1,重新得到执行可能。也正是它,瞧,
                Task1执行完后,又去搜索任务就绪表,而此刻OSRdyTbl = 00001101,我们就又能执行Task0了,任务Task2没有执行的份!!
                接着,执行Task0需要4.5秒,但,Task1延时的那个4秒早就过了,它在任务Task0没执行完就已经得到就绪机会了....周而复始。
                任务Task2不管大小,永远也轮不到它去执行,死在那里看人家吃饭了。
                所以,这个ticks的设置在这个"只有延时服务的协作式的内核"是有点讲究的。

                调度任务里面的调度函数OSSched() 我曾经说过:
                OSTaskRunningPrio = OSNextTaskID ;                //得到一个新任务(有可能是原来的任务),调度到就执行态
                注释也都很清楚了,关键有一点就是“得到一个新任务(有可能是原来的任务)”这句。这个要等我后面就会提到了,这
                里暂时不会。
                你想到了答案没有?
                比如说
                Task0任务比较大,0.5秒 才能执行完这个任务,void Task0{ ...OSTimeDly(2秒);}
                Task1任务比较大,0.5秒 才能执行完这个任务,void Task1{ ...OSTimeDly(10秒);}
                Task2任务比较大,0.5秒 就能执行完这个任务,void Task2{ ...OSTimeDly(20秒);}
                Task0有一段是将在那里一直执行几次,当然,期间有有点延时的。

                其实,还有其他的问题,大家按这种思路,也不会很难,就可能在有一点上不开窍而已,多看几次,如果你会用仿真,那就更好
                了,知道具体程序怎么走。我没用过,只是凭思路的。希望对大家有点用处

阿莫论坛20周年了!感谢大家的支持与爱护!!

月入3000的是反美的。收入3万是亲美的。收入30万是移民美国的。收入300万是取得绿卡后回国,教唆那些3000来反美的!

出0入12汤圆

发表于 2007-12-3 01:47:24 | 显示全部楼层
先占一栋楼,以后慢慢看.
头像被屏蔽

出0入0汤圆

发表于 2007-12-3 01:50:33 | 显示全部楼层
谢谢共享! COOL !

出0入0汤圆

发表于 2007-12-3 08:32:15 | 显示全部楼层
感觉:是协作式就不必要建立中断,中途退出,这样内存消耗比较大啊。另外应考虑增加同优先级的轮换调度。最近也在考虑OS,想实现协作式。希望和大家多交流。我用AVR的感觉,即使Tick是1mS,我的大部分任务都可以跑一圈,可能是我以前做的那些项目还不够大吧,呵呵。

出0入0汤圆

发表于 2007-12-3 08:37:08 | 显示全部楼层
好东西,顶一个再仔细学习。

出0入0汤圆

发表于 2007-12-3 08:39:46 | 显示全部楼层
基于优先级协作式调度,usmartx不二选择。

出0入0汤圆

发表于 2007-12-3 08:44:47 | 显示全部楼层
收藏了

出0入0汤圆

发表于 2007-12-3 08:51:22 | 显示全部楼层
喜欢写在前面的  比方

出0入0汤圆

发表于 2007-12-3 13:02:14 | 显示全部楼层
usmartx的缺点(不是关于抢占任务方面):在Tick中进行Tick服务、内核调度关中断等。会降低中断的相应时间。我改进了一个操作系统这样实现:Tick中断中对Tick进行计数。然后设置一个事件标志,等任务返回后检查事件标志然后回调Tick服务函数(其它的中断也采用这种方式,避免中断中进行内核调用。),延时状态的任务直接减去Tick计数数目,也可以实现不丢失Tick。因为即使Tick能使某个任务就绪但是它也不能立即抢占执行,所以还不如等当前任务返回后一道处理。另外,我把就绪队列设置成FIFO,从链尾加任务,调度时遍历链表找到最高优先级的第一个任务,从而变相地实现了同优先级的轮换。如果每次添加任务都进行链表遍历把TCB插入到合适的位置,还不如一次遍历找到最优先任务。
对usmartx没有细致的研究(懒得看别人的代码),大家有什么意见可以交流一下。

出0入0汤圆

发表于 2007-12-3 13:51:48 | 显示全部楼层
楼上所说的 :
在Tick中进行Tick服务、内核调度关中断等.
Tick可以看作是操作系统临界段,临界段影响了中断的实时性。

操作系统提高中断实时性采用非操作系统管理中断。(程明计small rtos一书中称为软非屏蔽中断,这里我称硬件级中断,与系统级中断对应)


我把中断分成两个级别,硬件级中断和系统级中断,对应临界段分为硬件级临界段和系统级临界段。
系统级临界段关闭所有受操作系统管理的中断。
硬件级临界段关闭总中断。
硬件级中断可以几乎不受系统级临界段影响,以提高实时性,但不能调用操作系统内核函数。

只要尽量降低硬件级临界段的时间了,就可以得到很高的硬件级中断实时性。
一般情况下可以做到硬件级临界段最长不超过20个时钟周期,甚至低于8个时钟周期。
8M时钟下,硬件级临界段约为1-2us左右,几乎可认为硬件级中断完全不受临界断影响。

对于usmartx,把Tick当作系统级临界段处理。
在处理tick时,关闭系统级中断,不关闭硬件级中断。

出0入0汤圆

发表于 2007-12-3 14:46:32 | 显示全部楼层
好东西,顶了再学。

出0入0汤圆

发表于 2007-12-3 22:33:11 | 显示全部楼层
讨论一下:
协作式:其实挺简单,将抢占式的中断(包括时拍中断)中任务切换功能全部屏蔽,在任务中或者调用OSTimeDly(x)/OSSemPend(x,x,x)...甚至主动去调用OS_Sched(),学习"自我放弃"便实现目的了.
抢占式:最大的问题在avr单片机上的表现就是RAM资源瓶颈,造成了综合性能下降.速度上并不是主要问题,我将UCOS移植到M16上OS_Sched()函数7.3728m最大切换时间也仅仅24uS(快速保存20uS,MAXTASKS = 8).

硬实时中断,大概只有用在掉电保护方面了.单片机的项目如果工艺分析透了,就上面我移植的用OS做的一个项目中断几乎最长(最坏)响应也是<30uS.对于响应20Khz的输入信号没有影响.
其实绕来绕去,归根结底就是AVR的片内RAM太少, [[ "只要尽量降低硬件级临界段的时间了,就可以得到很高的硬件级中断实时性"]],如果RAM充裕,就不怕压爆了.对于再入性,AVR的几个常用编译器都有先天优势.

出0入0汤圆

发表于 2007-12-3 23:22:58 | 显示全部楼层
先下载,慢慢看。

出0入0汤圆

 楼主| 发表于 2007-12-4 13:02:43 | 显示全部楼层
这几天都在看人家写的UC/OS简化版操作系统,这样学习,不至于太乱,
昨天去了个就书店,买了两本书,一本是<<嵌入式实时操作系统UC/OS-ii原理与应用>>.另外一本是<<基于嵌入式实时操作系统的程序设计技术>>.因为本人电子信息类这些更接近底层的专业,avr方面几乎全部自学.不过在操作系统的学习上应该还是蛮快的.

关于这个帖子,只是看了人家写的一个小教程,原本阿莫也在ouravr上发过,具体的地址:
http://www.ouravr.com/bbs/bbs_content.jsp?bbs_sn=574348&bbs_page_no=1&sub_kind_id=2065&bbs_id=1000
后来就想着应该写点总结什么的.

其他操作系统我也没看过,
因为要用到,所以就专心看这个,能解决我的问题就先看.
从简单入手,那学起来还是蛮快的
再加上最近要学习设计模式,很想把它引入我们的单片机编程上,设计模式在项目开发上真是一个必备的东西,也许你会说那个东西更多的涉及到上位机什么的.呵呵,到了ARM,你就会发现那个可能挺有用的.

数据结构方面,牛书<<算法导论>>的以前也看了一些,不过很多很好的算法在这个资源受限的AVR芯片上跑得有点吃力.不过还好,已经满足了

做点小项目,在操作系统上跑一下,就会促使我们学习更多的东西.会得到蛮大的提高的.

出0入0汤圆

发表于 2007-12-4 13:10:44 | 显示全部楼层
占先式好啊,实时性好,真正感觉到OS的优越与强大。
协作式,我更喜欢裸奔。

出0入0汤圆

发表于 2007-12-15 10:58:07 | 显示全部楼层
占先式,,片子的其它硬件资源如何利用?如串口,SPI接口等等.

出0入0汤圆

发表于 2007-12-15 11:01:05 | 显示全部楼层
我之前也有用占先式做过些项目,小案子还是可以,一旦遇到大案子,就没法用了,也找不出个原因来,烦!
特别是有浮点数运算的场合,如果简单的就按照例子上的,控制几个灯闪几下,还是可以.

出0入0汤圆

发表于 2008-4-10 15:14:34 | 显示全部楼层
学习中

出0入0汤圆

发表于 2009-3-2 14:46:20 | 显示全部楼层
那void Task0{ ...OSTimeDly(2秒)中的延时究竟多少呢?怎么计算出啊?

出0入0汤圆

发表于 2009-4-24 00:17:46 | 显示全部楼层
mark

出0入0汤圆

发表于 2009-5-20 17:54:47 | 显示全部楼层
m

出0入0汤圆

发表于 2009-6-1 15:17:01 | 显示全部楼层
哈哈,刚好我最近也弄这方面的。
问个问题。
原文:“
编译器会先用到以下寄存器
    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。


问题:入栈有必要把r0,r1,r18-r27,r28-r31都入栈吗? 这些寄存器不用堆栈也可以吧?
“(r18-r27, r30-r31): 调用函数时作为参数传递”,OSSched都没有形参。
“r28和r29可能会被作为指向堆栈上的变量的指针。”,不是有sp吗?

出0入0汤圆

发表于 2009-6-1 15:26:04 | 显示全部楼层
上下文切换不保存所有寄存器,典型有chibios 。
个人最喜chibios上下文切换方法。

出0入0汤圆

发表于 2009-6-14 17:21:34 | 显示全部楼层
好贴~顶~

出0入0汤圆

发表于 2009-12-11 15:38:38 | 显示全部楼层
挖个坟,不好意思

出0入0汤圆

发表于 2010-1-9 19:56:36 | 显示全部楼层
学习一下~~

出0入0汤圆

发表于 2010-3-5 16:50:16 | 显示全部楼层
怎么不行呀,端口运行了根本不变

出0入0汤圆

发表于 2010-3-5 22:37:47 | 显示全部楼层
void OSTimeDly(unsigned int ticks)
{
  if(ticks) //当延时有效
  {
    OSRdyTbl &= ~(0x01<<OSTaskRunningPrio); //任务运行就绪表相应位清0      
    TCB[OSTaskRunningPrio].OSWaitTick=ticks;//设置任务延时时钟
    OSSched(); //从新调度
  }
}

执行这个函数的时候会出错,执行后OSTaskRunningPrio变为0xff造成对战数据错误
用的avr gcc20090213

出0入0汤圆

发表于 2010-3-10 10:08:11 | 显示全部楼层
强烈关注~

出0入0汤圆

发表于 2010-4-15 15:54:09 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-7-16 18:39:42 | 显示全部楼层
虚心的学习!

出0入0汤圆

发表于 2010-7-23 08:09:22 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-8-17 21:35:11 | 显示全部楼层
试了第四篇,出现以下提示后就过不去了,不知道什么原因
C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp/cclViXog.s: Assembler messages:
C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp/cclViXog.s:169: Error: garbage at end of line
C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp/cclViXog.s:809: Error: garbage at end of line

出0入0汤圆

发表于 2010-11-4 14:12:53 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-11-7 20:15:03 | 显示全部楼层
MARK

出0入0汤圆

发表于 2011-4-14 14:19:21 | 显示全部楼层
MARK

出0入0汤圆

发表于 2011-4-29 14:05:33 | 显示全部楼层
mark

出0入0汤圆

发表于 2011-4-30 16:36:45 | 显示全部楼层
mark
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-4-24 06:20

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表