void_c 发表于 2009-6-15 22:02:44

ucos2移植尝试:“剔除”空闲任务,由主函数代替

ucos2移植尝试:“剔除”空闲任务,并不是真的剔除,
空闲任务控制块仍然存在,同样参与调度,只是任务空闲时,并不是进入空闲任务,而是直接切换到主函数。
没有了空闲任务,也就不用给空闲任务分配堆栈了。只支持OS_TASK_CREATE_EXT_EN为0的情况。

ucos不熟,只是移植尝试,不保证正确。
没有在硬件平台测试。

移植平台:atmega16+iaravr,atmega16+avrgcc,cc1110+iar8051

#include "config.h"

#define TASK1_STK_SIZE 150
#define TASK2_STK_SIZE 150

OS_STK Task1Stk;
OS_STK Task2Stk;

void Task1(void *pdata)
{
volatile float f1=(int)pdata;
while(1)
    {         
       PORTD^=0x80;
       f1+=0.1;      //浮点数测试
       OSTimeDly(2);
    }
}

void Task2(void *pdata)
{
volatile float f2=(int)pdata;
while(1)
    {   
         PORTD^=0x40;
         f2+=0.1;      //浮点数测试
         OSTimeDly(3);
    }
}

int main(void)
{

   DDRD=_BV(7)|_BV(6);
   PORTD=_BV(7)|_BV(6);
         
   TCNT2 = 0;
   TCCR2=_BV(WGM21) | T2_CLK_DIV_128;
   OCR2=OCR2_INIT;
   TIFR|=_BV(OCF2);
   TIMSK=_BV(OCIE2);

    OSInit();

    OSTaskCreate(Task1,(void *)1,&Task1Stk,1);
    OSTaskCreate(Task2,(void *)2,&Task2Stk,2);
   
    OSStart();

    volatilefloat f3=0;
    sei();         //主函数(相当于空闲任务)开中断
       
   while(1)
   {
      CRITICAL()   //临界段段
      {
          OSIdleCtr++;
      }
      
      OSTaskIdleHook();   
   
      f3+=0.1;      
   }
   
   
}

ISR(TIMER2_COMP_vect)
{
    OSIntEnter();                                       
    OSTimeTick();                                                            
    OSIntExit();                     
}


点击此处下载 ourdev_453558.rar(文件大小:300K) (原文件名:ucos.7z.rar)

ralfak 发表于 2009-6-15 22:47:43

ucos2移植尝试:“剔除”空闲任务,并不是真的剔除,
空闲任务控制块仍然存在,同样参与调度,只是任务空闲时,并不是进入空闲任务,而是直接切换到主函数。
没有了空闲任务,也就不用给空闲任务分配堆栈了。只支持OS_TASK_CREATE_EXT_EN为0的情况。

你怎么返回到主任务,考虑过这个问题没有
每个任务要有自己的任务控制块以及优先级
调用 OSStart();之后就永远不反回了,你这么干只会跑飞了,跑飞的原因是就绪表里面没有任务,而其他任务处于阻塞中。
程序没有去处了
延时任务阻塞后会调用OS_Sched
此时如果没有任务就绪 OSRdyGrp=0
y             = OSUnMapTbl;          /* Get pointer to HPT ready to run            */
      OSPrioHighRdy = (INT8U)((y << 3) + OSUnMapTbl]);
计算出最高优先级为0

如果你0优先级有任务的话,那还好说,比如是延时任务,接着延时的位置继续运行,但实际上延时的次数根本就没到
你又跑到那个延时任务去了。
如果0优先级没有任务的话,对不起,飞了
OSTCBHighRdy = OSTCBPrioTbl;
这个任务块根本就没初始化

voidOS_Sched (void)
{

    INT8U      y;


    OS_ENTER_CRITICAL();
    if ((OSIntNesting == 0) && (OSLockNesting == 0)) { /* Sched. only if all ISRs done & not locked    */
      y             = OSUnMapTbl;          /* Get pointer to HPT ready to run            */
      OSPrioHighRdy = (INT8U)((y << 3) + OSUnMapTbl]);
      if (OSPrioHighRdy != OSPrioCur) {            /* No Ctx Sw if current task is highest rdy   */
            OSTCBHighRdy = OSTCBPrioTbl;
            OSCtxSwCtr++;                              /* Increment context switch counter             */
            OS_TASK_SW();                              /* Perform a context switch                     */
      }
    }
    OS_EXIT_CRITICAL();
}

void_c 发表于 2009-6-15 23:07:00

【1楼】 ralfak
调用 OSStart();之后就永远不反回了,你这么干只会跑飞了,跑飞的原因是就绪表里面没有任务,而其他任务处于阻塞中。
-----------------------------------------------------------------------------------------------------------------

为什么调用 OSStart();之后就永远不反回了。
原因是调用OSStart()的时候,没有保护本身断点信息,就切换任务里了,任务之间无论怎么切换,都不可能切回来了。

我的做法是,OSStart()第一次切换任务时,把断点信息记录到Idle Task的TCB里,当再次切换到Idle Task时,自动就切换回断点了。
任务切换对OSStart()来说,就好像只是发生了一次中断。任务切换回来断点在函数OSStart()里,继续执行从OSStart()返回,进入后面的while(1)循环。

因为OSStart()会保护断点信息到Idle Task的TCB里,前面Idle Task初始化不必分配任何有效堆栈,后面保护断点信息到IDLE TASK的TCB的时候,会把前面的初始值覆盖掉。

voidOSStart (void)
{
    INT8U y;
    INT8U x;


    if (OSRunning == FALSE) {
      y             = OSUnMapTbl;      /* Find highest priority's task priority number   */
      x             = OSUnMapTbl];
      OSPrioHighRdy = (INT8U)((y << 3) + x);
         
      OSTCBHighRdy= OSTCBPrioTbl; /* Point to highest priority task ready to run    */
      
      OSTCBCur      = OSTCBPrioTbl;//当前任务是空闲任务,即主函数
      
        CRITICAL()   //临界段
        {
             OSRunning=1;
             OSCtxSw();//任务切换,当任务再次切换回来,从这里继续向后执行
        }
    }
}

ralfak 发表于 2009-6-16 17:41:27

那你的意思还是需要IDLE的堆栈,既然都要堆栈了,那不跟有一个IDLE任务一样么。
你单独在main函数中做死循环,还要单独写一段初始化主函数堆栈的代码,要将主函数的要跳转执行的PC压入堆栈,至于其他的都不用关心了。实际效果与IDLE没任何差别。相反还添加了第一次压主函数进堆栈的任务。
还得在代码上标记一个lable吧,不知道管不管用

void_c 发表于 2009-6-16 18:17:39

“那你的意思还是需要IDLE的堆栈,既然都要堆栈了,那不跟有一个IDLE任务一样么。”
----------------------------------------------------------------------------
如果有单独的IDLE TASK,就必须给IDLE TASK分配堆栈。
IDLE TASK的堆栈和主函数的堆栈是两个不同的堆栈。

如果去掉IDLE TASK,由主函数代替,那么只需要主函数的堆栈就行了。



“你单独在main函数中做死循环,还要单独写一段初始化主函数堆栈的代码,要将主函数的要跳转执行的PC压入堆栈,至于其他的都不用关心了。实际效果与IDLE没任何差别。相反还添加了第一次压主函数进堆栈的任务。”
---------------------------------------------------------------------------------
其实主主函数断点压入堆栈,是任务切换自动完成的。

主函数本身就是任务,我称主函数任务。
当主函数任务已经在运行,再初始化主函数任务上下文和上下文指针没有意义。
当主函数任务切换到其他任务,OS会自动保护主函数任务的断点上下文并记录上断点上下文指针,
当再次切换到主函数任务,从这个断点上下文指针恢复断点上下文。

ralfak 发表于 2009-6-16 18:49:36

---------------------------------------------------------------------------------
其实主主函数断点压入堆栈,是任务切换自动完成的。

可以这样理解。
一般的OS,是由主函数先完成一个和多个任务的初始上下文堆栈,然后跳到某个任务开始运行。
任务在运行之前,必须初始化上下文堆栈。

现在假设已经在某个任务中运行了,那么这个任务的上下文堆栈还需要初始化吗?
不需要。任务切换的时候自动维护上下文堆栈。

当把主函数当作IDLE TASK运行时,

___________________________________________________________________________
前后台系统是不需要初始化
但是UC/OS你必须初始化
因为任务调度器确定任务运行的位置,
你都没有告诉调度器我这儿还运行着一个任务。
调度器又怎么能找得到你这个任务。

ralfak 发表于 2009-6-16 18:54:30

讨论这样的话题没有任何意义,其实没有IDLE TASK只有一个意义,就是让系统待机,省电,如果不是朝着这个方向进行的话,干也白干。
主函数系统自身分配个10几个字节的堆栈足够了,完成各种子程序的跳转的保护操作,函数基本不用保护,直接采用naked属性即可,我看3个字节就够了(128K以上flash需要3字节PC指针)
超过3个字节都是浪费。

void_c 发表于 2009-6-16 19:52:58

直接采用naked属性即可,我看3个字节就够了(128K以上flash需要3字节PC指针) ,超过3个字节都是浪费。
---------------------------------------------------------------------------
主函数难道不需要初始化OS,初始化OS难道不需要函数调用,返回?3字节楼上说笑了。


其实没有IDLE TASK只有一个意义,就是让系统待机,省电,如果不是朝着这个方向进行的话,干也白干。
------------------------------------------------------------------------------------------------
楼上这话太绝对了。
记得FreeRTOS的协程调度就是放在空闲任务的。

void_c 发表于 2009-6-16 20:02:16

前后台系统是不需要初始化
但是UC/OS你必须初始化
因为任务调度器确定任务运行的位置,
你都没有告诉调度器我这儿还运行着一个任务。
调度器又怎么能找得到你这个任务。

-----------------------------------------------------------
当任务还没有运行时(一次都没有),必须初始化任务入口(上下文和上下文指针),使任务第一次运行从这里开始。
但是当任务已经在运行,还需要初始化始化任务入口吗?
不用。当任务切换出去的时候,OS会自动保护任务的断点上下文,并记录到上下文指针。
当任务再次切换回来,从断点上下文指针恢复断点上下文。

这里,任务初始化和任务入口初始化时两个不同的概念。
任务当然要初始化,
但是主函数任务初始化不需要初始化任务入口。因为主函数任务已经在运行。
页: [1]
查看完整版本: ucos2移植尝试:“剔除”空闲任务,由主函数代替