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) 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();
} 【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();//任务切换,当任务再次切换回来,从这里继续向后执行
}
}
} 那你的意思还是需要IDLE的堆栈,既然都要堆栈了,那不跟有一个IDLE任务一样么。
你单独在main函数中做死循环,还要单独写一段初始化主函数堆栈的代码,要将主函数的要跳转执行的PC压入堆栈,至于其他的都不用关心了。实际效果与IDLE没任何差别。相反还添加了第一次压主函数进堆栈的任务。
还得在代码上标记一个lable吧,不知道管不管用 “那你的意思还是需要IDLE的堆栈,既然都要堆栈了,那不跟有一个IDLE任务一样么。”
----------------------------------------------------------------------------
如果有单独的IDLE TASK,就必须给IDLE TASK分配堆栈。
IDLE TASK的堆栈和主函数的堆栈是两个不同的堆栈。
如果去掉IDLE TASK,由主函数代替,那么只需要主函数的堆栈就行了。
“你单独在main函数中做死循环,还要单独写一段初始化主函数堆栈的代码,要将主函数的要跳转执行的PC压入堆栈,至于其他的都不用关心了。实际效果与IDLE没任何差别。相反还添加了第一次压主函数进堆栈的任务。”
---------------------------------------------------------------------------------
其实主主函数断点压入堆栈,是任务切换自动完成的。
主函数本身就是任务,我称主函数任务。
当主函数任务已经在运行,再初始化主函数任务上下文和上下文指针没有意义。
当主函数任务切换到其他任务,OS会自动保护主函数任务的断点上下文并记录上断点上下文指针,
当再次切换到主函数任务,从这个断点上下文指针恢复断点上下文。 ---------------------------------------------------------------------------------
其实主主函数断点压入堆栈,是任务切换自动完成的。
可以这样理解。
一般的OS,是由主函数先完成一个和多个任务的初始上下文堆栈,然后跳到某个任务开始运行。
任务在运行之前,必须初始化上下文堆栈。
现在假设已经在某个任务中运行了,那么这个任务的上下文堆栈还需要初始化吗?
不需要。任务切换的时候自动维护上下文堆栈。
当把主函数当作IDLE TASK运行时,
___________________________________________________________________________
前后台系统是不需要初始化
但是UC/OS你必须初始化
因为任务调度器确定任务运行的位置,
你都没有告诉调度器我这儿还运行着一个任务。
调度器又怎么能找得到你这个任务。 讨论这样的话题没有任何意义,其实没有IDLE TASK只有一个意义,就是让系统待机,省电,如果不是朝着这个方向进行的话,干也白干。
主函数系统自身分配个10几个字节的堆栈足够了,完成各种子程序的跳转的保护操作,函数基本不用保护,直接采用naked属性即可,我看3个字节就够了(128K以上flash需要3字节PC指针)
超过3个字节都是浪费。 直接采用naked属性即可,我看3个字节就够了(128K以上flash需要3字节PC指针) ,超过3个字节都是浪费。
---------------------------------------------------------------------------
主函数难道不需要初始化OS,初始化OS难道不需要函数调用,返回?3字节楼上说笑了。
其实没有IDLE TASK只有一个意义,就是让系统待机,省电,如果不是朝着这个方向进行的话,干也白干。
------------------------------------------------------------------------------------------------
楼上这话太绝对了。
记得FreeRTOS的协程调度就是放在空闲任务的。 前后台系统是不需要初始化
但是UC/OS你必须初始化
因为任务调度器确定任务运行的位置,
你都没有告诉调度器我这儿还运行着一个任务。
调度器又怎么能找得到你这个任务。
-----------------------------------------------------------
当任务还没有运行时(一次都没有),必须初始化任务入口(上下文和上下文指针),使任务第一次运行从这里开始。
但是当任务已经在运行,还需要初始化始化任务入口吗?
不用。当任务切换出去的时候,OS会自动保护任务的断点上下文,并记录到上下文指针。
当任务再次切换回来,从断点上下文指针恢复断点上下文。
这里,任务初始化和任务入口初始化时两个不同的概念。
任务当然要初始化,
但是主函数任务初始化不需要初始化任务入口。因为主函数任务已经在运行。
页:
[1]