📄 functionforucos.txt
字号:
#endif
#if (OS_Q_EN && (OS_MAX_QS >= 2)) || OS_MBOX_EN
void *OSTCBMsg; //
#endif
INT16U OSTCBDly; //任务延时节拍
INT8U OSTCBStat; //任务状态,当它等于OS_STAT_READY时,任务进入就绪态
INT8U OSTCBPrio; //任务优先级
INT8U OSTCBX; //例如任务的优先级为25, 则OSTCBX=25%8=1
INT8U OSTCBY; //OSTCBY=25%8=3
INT8U OSTCBBitX; //
INT8U OSTCBBitY; //对应的位数
#if OS_TASK_DEL_EN
BOOLEAN OSTCBDelReq; //
#endif
} OS_TCB;
第六章:OS的核心运转过程 (1)空闲任务的建立及运行过程
μC/OS /sherlock_lai 发表于2008-01-16, 21:56
OS是如何运转起来的?(一般情况下,不考虑系统的一些配置被修改).
1.系统变量常量配置
在程序的最开头,先定义你需要的任务的堆栈大小,优先级,此时CPU已经将系统的一些变量,常量储存于实际物理地址了(例如任务堆栈)
#define .....
...
2.运行到OSInit();
首先是一个主函数,CPU运行第一个函数OSInit();在这里,CPU初始化一些系统变量的值,例如一些任务记数器等.
然后创建两个基本的任务:空闲任务(CPU闲置时就连续运行此任务),统计任务(统计空闲任务,以便计算CPU的使用率).
void Main(void)
{
OSInit();
OSTaskCreate(Main_Task, (void *)0, (OS_STK *)&Main_Stack[TASK_STACK_SIZE-1], Main_PRIO);
OSStart();
}
2.0 初始化一个任务控制快单向链表, .OSTCBNext=下一个, OSTCBFreeList为第一个,如果这个链表被取出一个,则OSTCBFreeList指向空链表里的第一个
2.1 建立空闲任务的过程
2.11 OSTaskStkInit 根据任务函数入口,传递的参数,栈顶,选择项,4个参数决定物理栈顶指针,这个栈里还有其他参数,比如PC值,这些参数都保存在这个物理栈里,在任务开始运行时,将这些值取出,而当任务因为某种原因中断了,则保存这些值,这样下次就能从中断的那个状态再次运行这个任务.
初始化堆栈,从栈顶依次压入,任务函数入口,各个寄存器,这样恢复任务的时候先恢复各个寄存器,最后才将任务函数入口压入PC,从而运行任务
2.12 OSTCBInit 初始化一个任务控制块
将各个参数填入这个控制块,并且生成一个双向任务链表,每次初始化的任务控制块将会添加入链表的左端,这个双向链表在时钟节拍中会用到.
最后在就绪表里将此任务对应的位置一,表示这个任务已经就绪了.
2.13 如果以上都成功了,则任务记数器+1. 接下来会检查OSRunning,如果它为0,说明系统还未运行,这样空闲任务就建立了,等待OSStart 函数开始运行系统,如果OSRunning 为1,说明系统已经运行,这样就执行一次任务调度,任务调度的作用是,如果刚刚建立的任务优先级最高,就运行这个任务. 以下分析任务调度函数
2.131 OSSched 如果任务调度的条件成立,则将就绪表里优先级最高的任务赋予OSPrioHighRdy(等待运行里优先级最高的任务)(这个地方有个疑问:在这个地方运行完后,OSPrioHighRdy和OSPrioRdy会变成什么) 如果这个最高优先级的任务还没运行,那么任务切换计数器+1,执行OS_TASK_SW 任务切换函数. 接下来解析任务切换函数功能
2.1311 OS_TASK_SW 保存处理器的值,将当前任务的堆栈指针保存到当前任务的OS_TCB, (第一次运行OS,这个TCB是否为空闲任务的TCB?) , OSTCBCur , OSPrioCur 赋予最高优先级任务,堆栈指针赋予最高优先级任务指针,将堆栈中的内容恢复,执行中断返回指令,这样就运行了最高优先级任务了,这里就是空闲任务. (疑问:第一次的堆栈有何作用?还是本来就没用?)
2.2 建立完空闲任务,这时空闲任务就运行了,这里又有个疑问,此时系统的最高优先级任务为空闲任务,那么当空闲任务挂起进行任务调度时,那么会运行的不会又是空闲任务? 还是没有跳会到主程序流程? 可能空闲任务里保存了两块堆栈,主流程堆栈和自身的堆栈. 这里不是很理解...不管先,继续往下走,假设系统跳回到任务建立那,接下来也就是要建立统计任务了.
第六章:OS的核心运转过程 (2)始终节拍函数,统计任务
μC/OS /sherlock_lai 发表于2008-01-06, 22:19
OSTickISR 时钟节拍函数
时钟一到,保存寄存器值,当前的堆栈指针SP赋予OSTCBCur.OSTCBStkPtr,调用OSTimeTick()
OSTimeTick 节拍服务函数
将每个TCB中的.OSTCBDly-1,如果OSTCBDly为0了,任务原先也没有被挂起,就将任务置为就绪态,如果原先被挂起,就先将OSTCBDly为1. 这里有条语句ptcb=OSTCBList ,OSTCBList是?
接着恢复处理器寄存器的值,返回原先的状态.
前面提出的疑问这里突然也得到解决, 因为第一次建立空闲函数,此时OSRUNNING还是为0,所以还不会出现程序在空闲任务处循环的情况,因为空闲任务根本不会运行,这里程序会继续往下走,建立统计任务
OSTaskStat
第七章:函数解析:OSTimeTick
μC/OS /sherlock_lai 发表于2008-01-08, 20:31
void OSTimeTick (void) //这个节拍服务函数是在OSTickISR函数中调用的,目的是在时钟节拍到来时,检查每个任务的任务控制块中的.OSTCBDly-1后是否为0,如果是,那么表明这个任务刚才是挂起的状态,此时应改变为就绪态
{
OS_TCB *ptcb;
OSTimeTickHook(); //
ptcb = OSTCBList; //时钟节拍到来时,将控制块双向链表的第一个控制块取出(并不是节拍之前运行的任务)
while (ptcb->OSTCBPrio != OS_IDLE_PRIO) { //空闲任务处于控制块双向链表的最后一个,如果取出的控制块为空闲任务的控制块,那么已经取到最后一个了,就结束
// OS_ENTER_CRITICAL();
if (ptcb->OSTCBDly != 0) { //
if (--ptcb->OSTCBDly == 0) { //
if (!(ptcb->OSTCBStat & OS_STAT_SUSPEND)) { //检查任务是否处于强制挂起状态,如果是,那再挂起一个时钟节拍,否则就将它就绪
OSRdyGrp |= ptcb->OSTCBBitY;
OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX;
} else { //
ptcb->OSTCBDly = 1; //
} //
}
}
ptcb = ptcb->OSTCBNext; //下一个任务控制块
// OS_EXIT_CRITICAL();
}
// OS_ENTER_CRITICAL(); //
OSTime++; //节拍计数器+1
// OS_EXIT_CRITICAL();
}
第八章:函数解析:OSTaskStat
μC/OS /sherlock_lai 发表于2008-01-08, 21:01
void OSTaskStat (void *pdata) //统计任务的目的是计算CPU利用率。CPU空闲时会不停的运行空闲任务,只要得到空闲任务运行次数的全局变量,就能统计出CPU利用率
{
INT32U run;
INT8S usage;
pdata = pdata; //
while (OSStatRdy == FALSE) { //等待统计任务就绪
OSTimeDly(2 * OS_TICKS_PER_SEC); //
}
for (;;) {
OS_ENTER_CRITICAL();
OSIdleCtrRun = OSIdleCtr; //获得统计任务运行次数(1秒内)
run = OSIdleCtr;
OSIdleCtr = 0L; //清零,必要的,为下一秒的统计做准备
OS_EXIT_CRITICAL();
if (OSIdleCtrMax > 0L) {
usage = (INT8S)(100L - 100L * run / OSIdleCtrMax); //计算百分比
if (usage > 100) {
OSCPUUsage = 100;
} else if (usage < 0) {
OSCPUUsage = 0;
} else {
OSCPUUsage = usage;
}
} else {
OSCPUUsage = 0;
}
OSTaskStatHook(); //
OSTimeDly(OS_TICKS_PER_SEC); //挂起一秒,统计任务一秒运行一次 }
}
第九章:函数解析:OSStart
μC/OS /sherlock_lai 发表于2008-01-08, 22:07
void OSStart (void) //执行这个函数OS就开始运行了
{
INT8U y;
INT8U x;
if (OSRunning == FALSE)
{
y = OSUnMapTbl[OSRdyGrp]; //取出优先级最高的任务
x = OSUnMapTbl[OSRdyTbl[y]];
OSPrioHighRdy = (INT8U)((y << 3) + x);
OSPrioCur = OSPrioHighRdy;
OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy]; //
OSTCBCur = OSTCBHighRdy;
OSStartHighRdy(); //OSRunning=TRUE,得到要将要运行的最高优先级任务的指针,恢复任务栈内的寄存器和PC值,开始执行任务。疑问:AMR7移植的OS里,这个函数中为什么不包含OSRunning=TRUE这条指令????
}
}
执行这个函数后,空闲任务开始不停运行,到了第一个时钟节拍,CPU首先将任务双向链表里的任务的延迟变量都-1,然后将延迟变量为0的任务就绪,接着看处于就绪态的优先级最高的任务是否运行,如果没有,就运行这个任务。然后系统到了第二个时钟节拍,第三个.....一直做这样的循环,这样就保证优先级最高的任务得以第一时间运行。
一般情况下,UCOS的核心运转过程就是这样一个循环。
第十章:函数解析:OSTickISR,OSIntExit,OSIntCtxSw
μC/OS /sherlock_lai 发表于2008-01-08, 22:43
这个函数涉及寄存器的操作。以下是示意性代码(引用邵贝贝一书)。
保存处理器寄存器;
调用OSIntEnter()或者直接给OSIntNesting加1;
if(OSIntNesting==1)
{
OSTCBCur->OSTCBStkPtr=Stack Pointer;
}
给产生中断的设备清中断;
重新允许中断(可选);
OSTimeTick(); //给延时到的任务就绪态
OSIntExit(); //这个函数与OSSched 差不多,都是将最高级任务置为就绪态
恢复处理器寄存器;
执行中断返回指令;
}
void OSIntExit (void)
{
// OS_ENTER_CRITICAL();
if ((--OSIntNesting | OSLockNesting) == 0) //
OSIntExitY = OSUnMapTbl[OSRdyGrp];
OSPrioHighRdy = (INT8U)((OSIntExitY << 3) + OSUnMapTbl[OSRdyTbl[OSIntExitY]]);
if (OSPrioHighRdy != OSPrioCur)
OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy]; //在这里得到新的OSTCBHighRdy参数
OSCtxSwCtr++;
OSIntCtxSw(); //这个函数与OSCtxSw差不多
}
}
// OS_EXIT_CRITICAL();
}
void OSIntCtxSw(void)
{
OSTCBCur->OSTCBStkPtr=堆栈指针;
OSTCBCur=OSTCBHighRdy;
OSPrioCur=OSPrioHighRdy; //恢复OSTCBCur参数
堆栈指针=OSTCBHighRdy->OSTCBStkPtr;
将任务堆栈中的内容恢复;
执行中断返回指令;
}
第十一章:关于OSRunning的问题
μC/OS /sherlock_lai 发表于2008-01-09, 12:41
UCOS的源代码中要求在OSStartHighRdy中加入OSRunning=TRUE的代码,但是我用的那个ARM7移植的OSSTartHighRdy并没有这段代码,这会带来什么问题?
首先,OSRunning这个变量在创建任务函数OSTaskCreate中有用到,如果OSRunning为TRUE,则执行OSSched,OSSched的功能就是将就绪表中优先级任务最高的任务取出,然后进行任务切换。也就是说,如果在OSStartHighRdy中没有OSRunning=TRUE的代码,在创建任务的时候,如果创建的任务的优先级为最高,那么它并不能得到马上运行。但实际上这也没多大关系,因为当时钟节拍到来的时候,时钟节拍依次调用了OSTimeTick(节拍服务函数)和OSIntExit。后者与OSSched是极其相似的,它们的作用也一样。这样,在节拍到来时,还是会进行任务切换,只不过刚创建的优先级最高的任务相当于延时了一个节拍运行而已。
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -