本篇介绍的是OSStart()函数和OSStatInit()函数和OSTimeTick()函数。
OSStart()多任务开始函数:
/*$PAGE*/
/*
*********************************************************************************************************
* START MULTITASKING
* 启动多个任务
* Description: This function is used to start the multitasking process which lets uC/OS-II manages the
* task that you have created. Before you can call OSStart(), you MUST have called OSInit()
* and you MUST have created at least one task.
* 该功能用来开始多任务进程,使UC/OS-II管理你所创建的任务。在调用OSStart()之前必须先调用OSInit()并且必须创建至少一个任务
* Arguments : none
*
* Returns : none
*
* Note : OSStartHighRdy() MUST:
* a) Call OSTaskSwHook() then,调用OSStartHighRdy()之后就调用OSTaskSwHook()函数
* b) Set OSRunning to OS_TRUE.将OSRunning设置为真,指出多任务已经开始
* c) Load the context of the task pointed to by OSTCBHighRdy.加载高优先级就绪任务启动函数中指出的任务的上下文
* d_ Execute the task.执行任务
*********************************************************************************************************
*/
void OSStart(void) /*启动多个任务*/
{
if (OSRunning == OS_FALSE) { /*此时没有多任务*/
OS_SchedNew(); /* Find highest priority's task priority number 找到最高优先级的任务优先级号 */
OSPrioCur = OSPrioHighRdy; /*将最高优先级任务的优先级号作为当前要执行任务的优先级号*/
OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy]; /* Point to highest priority task ready to run 指向将要运行的最高优先级任务 */
OSTCBCur = OSTCBHighRdy; /*将高优先级任务的TCB作为当前TCB*/
OSStartHighRdy(); /* Execute target specific code to start task执行代码开始任务 */
}
}
将上述代码总结为如图所示流程:
OSStatInit()统计任务初始化函数:
/*$PAGE*/
/*
*********************************************************************************************************
* STATISTICS INITIALIZATION
* 统计任务初始化
* Description: This function is called by your application to establish CPU usage by first determining
* how high a 32-bit counter would count to in 1 second if no other tasks were to execute
* during that time. CPU usage is then determined by a low priority task which keeps track
* of this 32-bit counter every second but this time, with other tasks running. CPU usage is
* determined by:
* 统计初始化函数OSStatInit()决定在没有其它应用任务运行时,空闲计数器(OSIdleCtr)的计数有多快。
这个任务每秒执行一次,以确定所有应用程序中的任务消耗了多少CPU时间。当用户的应用程序代码加入以后,
运行空闲任务的CPU时间就少了,OSIdleCtr就不会像原来什么任务都不运行时有那么多计数。要知道,
OSIdleCtr的最大计数值是OSStatInit()在初始化时保存在计数器最大值OSIdleCtrMax中的。
* OSIdleCtr
* CPU Usage (%) = 100 * (1 - ------------)
* OSIdleCtrMax
CPU使用率(百分比形式)=100*(1-空闲计数值/设定最大空闲计数值)
*
* Arguments : none
*
* Returns : none
*********************************************************************************************************
*/
#if OS_TASK_STAT_EN > 0u
void OSStatInit(void) /*统计任务初始化*/
{
#if OS_CRITICAL_METHOD == 3u /* Allocate storage for CPU status register 中断函数被设定为模式3 */
OS_CPU_SR cpu_sr = 0u;
#endif
OSTimeDly(2u); /* Synchronize with clock tick调用延迟函数OSTimeDly()将自身延时2个时钟节拍以停止自身的运行,
这是为了使OSStatInit()与时钟节拍同步 */
OS_ENTER_CRITICAL(); /*关闭中断(进入中断)*/
OSIdleCtr = 0uL; /* Clear idle counter 执行OSStartInit()时,空闲计数器OSIdleCtr被清零*/
OS_EXIT_CRITICAL(); /*打开中断*/
OSTimeDly(OS_TICKS_PER_SEC / 10u); /* Determine MAX. idle counter value for 1/10 second确定最大空闲计数值为1或10s */
OS_ENTER_CRITICAL(); /*关闭中断*/
OSIdleCtrMax = OSIdleCtr; /* Store maximum idle counter count in 1/10 second 存储最大空闲计数值 */
OSStatRdy = OS_TRUE; /*将统计任务就绪标志OSStatRdy设为"真",以此来允许两个时钟节拍以后OSTaskStat()开始计算CPU的利用率*/
OS_EXIT_CRITICAL(); /*打开中断*/
}
#endif
OSTimeTick()时钟节拍函数:
/*$PAGE*/
/*2018/2/3~2/7
*********************************************************************************************************
* PROCESS SYSTEM TICK
* 时钟节拍函数
* Description: This function is used to signal to uC/OS-II the occurrence of a 'system tick' (also known
* as a 'clock tick'). This function should be called by the ticker ISR but, can also be
* called by a high priority task.
*该功能是用来给uc/os发送时钟信号。该功能称为时钟中断,也叫做高优先级任务
* Arguments : none
*
* Returns : none
*********************************************************************************************************
*/
void OSTimeTick(void) /*时钟节拍函数*/
{
OS_TCB *ptcb; /*指向TCB列表的的指针*/
#if OS_TICK_STEP_EN > 0u
BOOLEAN step; /*设置一个布尔型变量*/
#endif
#if OS_CRITICAL_METHOD == 3u /* Allocate storage for CPU status register中断函数被设定为模式3 */
OS_CPU_SR cpu_sr = 0u;
#endif
#if OS_TIME_TICK_HOOK_EN > 0u /*可以生成时钟钩子函数OS_TIME_TICK_HOOK()*/
OSTimeTickHook(); /* Call user definable hook 调用用户自定义钩子函数 */
#endif
#if OS_TIME_GET_SET_EN > 0u /*可以生成OS_TIME_GET_SET()函数*/
OS_ENTER_CRITICAL(); /* Update the 32-bit tick counter 更新32位节拍数,关中断 */
OSTime++; /*/累加从开机以来的时间,用的是一个无符号32位变量*/
OS_EXIT_CRITICAL(); /*开中断*/
#endif
if (OSRunning == OS_TRUE) {
#if OS_TICK_STEP_EN > 0u
switch (OSTickStepState) { /* Determine whether we need to process a tick步进的状态:决定我们是否需要处理节拍 */
case OS_TICK_STEP_DIS: /* Yes, stepping is disabled 步进被禁用 */
step = OS_TRUE;
break;
case OS_TICK_STEP_WAIT: /* No, waiting for uC/OS-View to set ...步进等待状态:
等待uC/OS-View将步进状态设置成进一步*/
step = OS_FALSE;
break;
case OS_TICK_STEP_ONCE: /* Yes, process tick once and wait for next ...步进一次等待下一步命令 */
step = OS_TRUE; /*... step command from uC/OS-View步进命令来自于uc/os-view*/
OSTickStepState = OS_TICK_STEP_WAIT; /*将步进状态设置为步进等待状态*/
break;
default: /* Invalid case, correct situatio无效的情况或者正确的情况下*/
step = OS_TRUE;
OSTickStepState = OS_TICK_STEP_DIS; /*将步进状态设置为禁止状态*/
break;
}
if (step == OS_FALSE) { /* Return if waiting for step command如果正在等待步进命令,返回*/
return;
}
#endif
ptcb = OSTCBList; /* Point at first TCB in TCB list 指向TCB列表的第一个*/
while (ptcb->OSTCBPrio != OS_TASK_IDLE_PRIO) { /* Go through all TCBs in TCB list 浏览TCB列表的所有TCB*/
OS_ENTER_CRITICAL(); /*进入中断*/
if (ptcb->OSTCBDly != 0u) { /* No, Delayed or waiting for event with TO等待任务时的最多节拍数不为0*/
ptcb->OSTCBDly--; /* Decrement nbr of ticks to end of delay时间延迟项减1*/
if (ptcb->OSTCBDly == 0u) { /* Check for timeout减1后检查是否到时间了。时间延迟项等于0,就转为就绪态(外部条件准备就绪)*/
if ((ptcb->OSTCBStat & OS_STAT_PEND_ANY) != OS_STAT_RDY) { /*(检查内部条件是否准备就绪)
如果TCB状态和挂起状态进行位与运算之后不等于准备好的状态,简单来说就是任务没有准备好*/
ptcb->OSTCBStat &= (INT8U)~(INT8U)OS_STAT_PEND_ANY; /* Yes, Clear status flag清除状态标志*/
ptcb->OSTCBStatPend = OS_STAT_PEND_TO; /* Indicate PEND timeout将TCB挂起状态设置为超时状态*/
}
else { /*如果是已准备好的状态(内部条件准备就绪)*/
ptcb->OSTCBStatPend = OS_STAT_PEND_OK;/*就将TCB挂起状态设置为挂起结束(正常结束)状态*/
}
if ((ptcb->OSTCBStat & OS_STAT_SUSPEND) == OS_STAT_RDY) { /* Is task suspended? 查看任务是否为挂起状态*/
OSRdyGrp |= ptcb->OSTCBBitY; /* No, Make ready如果不是(即已为就绪态),在就绪表中找到具体的任务控制块*/
OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX;
}
}
}
ptcb = ptcb->OSTCBNext; /* Point at next TCB in TCB list指向TCB列表的下一个 */
OS_EXIT_CRITICAL(); /*退出中断(开中断)*/
}
}
}
同时介绍一下相关的时间管理方面的内容:http://blog.csdn.net/adam_embedded/article/details/46323491
1.1 时钟节拍
UCOSii通过时钟节拍OSTimeTick()来定期进行任务调度,一般来说这个频率是10-100HZ,频率越高,系统的开销也就越大。
1.2 任务延时函数
OSTimeDly()
任务可以调用OSTimeDly()来对自身延时一段时间。延时时,任务被挂起。任务被延时的时间必须是时钟节拍的倍数。与延时有关的变量在Tcb结构体中。
Tcb.OSTCBDly表示任务自己延时挂起的时间。
这样,当任务调用OSTimeDly()来进行延时时,该函数会修改修改OSTCBDly的值,把要延时的次数写入该变量,最后进行任务调度即可。
每次时钟节拍发生的时候,OSTCBDly的值都会被减去一,当该值为0的时候,内核就会把它放入就绪队列。
NOTE: 当调用OSTimeDly(1)只延时一个时钟节拍的时候,由于任务可能运行在一个时钟节拍的中后期,此时经过不到半个时钟节拍的时间,OSTCBDly的值就会被修改。因此,如果用户的应用程序至少得延时一个节拍,必须要调用 OSTimeDly(2),指定延时两个节拍。(即指定延时1,延时可能不足1,但是指定延时为2,至少可以保证延时一个节拍)。
来看一下OSTimeDly()的结构和原型:
结构:
void OSTimeDly(OS_TICK dly,//指定延时的长度,单位为时间节拍
OS_OPT opt,//延时模式
OS_ERR *p_err//错误信息
)
- 如果延时时间dly>0,则会发生任务调度;0表示不延时
OSTimeDly()
函数延时模式有四种:OS_OPT_TIME_DLY;OS_OPT_TIME_TIMEOUT;OS_OPT_TIME_PERIODIC;OS_OPT_TIME_MATCH;
void OSTimeDly (INT16U ticks)
{
if (ticks > 0) {
OS_ENTER_CRITICAL();
if ((OSRdyTbl[OSTCBCur->OSTCBY] &= ~OSTCBCur->OSTCBBitX) == 0) {
OSRdyGrp &= ~OSTCBCur->OSTCBBitY;
}
OSTCBCur->OSTCBDly = ticks;
OS_EXIT_CRITICAL();
OSSched();
}
}
可以看到该函数将延时次数传递给了OSTCBDly。
OSTimeDly()有一些缺点:一是只能延时65535次时钟节拍,二是不能换算成时间。
1.3 结束任务延时函数
OSTimeDlyResume()函数可以被用来强制某一任务结束延时,它的主要作用是支持任务之间的通讯和同步。
OSTimeDlyResume()要做的就是将OSTCBDly清0,然后进行任务调度。
1.4 系统时间
每次发生时钟节拍时,UCOS都会将一个32位的计数器加1。
OSTimeGet()和 OSTimeSet()用来获取或者设置这个计数器的值。