1.任务类型划分
µC/OS-II系统任务类型可以大致分为三种,空闲任务,统计任务,应用任务(用户开发对应功能模块需要创建的任务)
2.任务类型介绍
a.首先说一下空闲任务和统计任务:
µC/OS-II要用户在使用任何服务之前先调用OSInit() 。它会建立两个任务:空闲任务和统计任务,前者在没有其它任务处于就绪态时运行;后者计算CPU的利用率。
OSInit() 接口定义如下:
/*
*********************************************************************************************************
* INITIALIZATION
*
* Description: This function is used to initialize the internals of uC/OS-II and MUST be called prior to
* creating any uC/OS-II object and, prior to calling OSStart().
*
* Arguments : none
*
* Returns : none
*********************************************************************************************************
*/
void OSInit (void)
{
OSInitHookBegin(); /* Call port specific initialization code */
OS_InitMisc(); /* Initialize miscellaneous variables */
OS_InitRdyList(); /* Initialize the Ready List */
OS_InitTCBList(); /* Initialize the free list of OS_TCBs */
OS_InitEventList(); /* Initialize the free list of OS_EVENTs */
#if (OS_FLAG_EN > 0) && (OS_MAX_FLAGS > 0)
OS_FlagInit(); /* Initialize the event flag structures */
#endif
#if (OS_MEM_EN > 0) && (OS_MAX_MEM_PART > 0)
OS_MemInit(); /* Initialize the memory manager */
#endif
#if (OS_Q_EN > 0) && (OS_MAX_QS > 0)
OS_QInit(); /* Initialize the message queue structures */
#endif
OS_InitTaskIdle(); /* Create the Idle Task */
#if OS_TASK_STAT_EN > 0
OS_InitTaskStat(); /* Create the Statistic Task */
#endif
#if OS_TMR_EN > 0
OSTmr_Init(); /* Initialize the Timer Manager */
#endif
OSInitHookEnd(); /* Call port specific init. code */
#if OS_DEBUG_EN > 0
OSDebugInit();
#endif
}
前面有说到空闲任务什么时候会执行,下面说一下统计任务的作用。
作用1:CPU使用率统计
实际上,如果用户要计算CPU的利用率时,也需要先 建立一个任务。µCOS-II的统计任务要求在整个一秒钟内没有任何其它任务运行。如果用户在启动多任务之前要建立其它任务,必须保证用户的任务代码监控全局变量OSStatRdy和延时程序 [即调用 OSTimeDly()]的执行,直到这个变量变成TRUE。这表明µC/OS-II的CPU利用率统计函数已经采集到了数据。
统计任务的代码如下:
/*
*********************************************************************************************************
* STATISTICS TASK
*
* Description: This task is internal to uC/OS-II and is used to compute some statistics about the
* multitasking environment. Specifically, OS_TaskStat() computes the CPU usage.
* CPU usage is determined by:
*
* OSIdleCtr
* OSCPUUsage = 100 * (1 - ------------) (units are in %)
* OSIdleCtrMax
*
* Arguments : parg this pointer is not used at this time.
*
* Returns : none
*
* Notes : 1) This task runs at a priority level higher than the idle task. In fact, it runs at the
* next higher priority, OS_TASK_IDLE_PRIO-1.
* 2) You can disable this task by setting the configuration #define OS_TASK_STAT_EN to 0.
* 3) You MUST have at least a delay of 2/10 seconds to allow for the system to establish the
* maximum value for the idle counter.
*********************************************************************************************************
*/
#if OS_TASK_STAT_EN > 0
void OS_TaskStat (void *p_arg)
{
#if OS_CRITICAL_METHOD == 3 /* Allocate storage for CPU status register */
OS_CPU_SR cpu_sr = 0;
#endif
(void)p_arg; /* Prevent compiler warning for not using 'p_arg' */
while (OSStatRdy == OS_FALSE) {
OSTimeDly(2 * OS_TICKS_PER_SEC / 10); /* Wait until statistic task is ready */
}
OSIdleCtrMax /= 100L;
if (OSIdleCtrMax == 0L) {
OSCPUUsage = 0;
(void)OSTaskSuspend(OS_PRIO_SELF);
}
for (;;) {
OS_ENTER_CRITICAL();
OSIdleCtrRun = OSIdleCtr; /* Obtain the of the idle counter for the past second */
OSIdleCtr = 0L; /* Reset the idle counter for the next second */
OS_EXIT_CRITICAL();
OSCPUUsage = (INT8U)(100L - OSIdleCtrRun / OSIdleCtrMax);
OSTaskStatHook(); /* Invoke user definable hook */
#if (OS_TASK_STAT_STK_CHK_EN > 0) && (OS_TASK_CREATE_EXT_EN > 0)
OS_TaskStatStkChk(); /* Check the stacks for each task */
#endif
OSTimeDly(OS_TICKS_PER_SEC / 10); /* Accumulate OSIdleCtr for the next 1/10 second */
}
}
#endif
OSCPUUsage为统计获取到的CPU使用率。
作用2:堆栈检查
OS_TaskStatStkChk()统计各个任务的stack的使用情况,实现代码如下:
/*
*********************************************************************************************************
* CHECK ALL TASK STACKS
*
* Description: This function is called by OS_TaskStat() to check the stacks of each active task.
*
* Arguments : none
*
* Returns : none
*********************************************************************************************************
*/
#if (OS_TASK_STAT_STK_CHK_EN > 0) && (OS_TASK_CREATE_EXT_EN > 0)
void OS_TaskStatStkChk (void)
{
OS_TCB *ptcb;
OS_STK_DATA stk_data;
INT8U err;
INT8U prio;
for (prio = 0; prio <= OS_TASK_IDLE_PRIO; prio++) {
err = OSTaskStkChk(prio, &stk_data);
if (err == OS_ERR_NONE) {
ptcb = OSTCBPrioTbl[prio];
if (ptcb != (OS_TCB *)0) { /* Make sure task 'ptcb' is ... */
if (ptcb != OS_TCB_RESERVED) { /* ... still valid. */
#if OS_TASK_PROFILE_EN > 0
#if OS_STK_GROWTH == 1
ptcb->OSTCBStkBase = ptcb->OSTCBStkBottom + ptcb->OSTCBStkSize;
#else
ptcb->OSTCBStkBase = ptcb->OSTCBStkBottom - ptcb->OSTCBStkSize;
#endif
ptcb->OSTCBStkUsed = stk_data.OSUsed; /* Store the number of bytes used */
#endif
}
}
}
}
}
#endif
每一个任务优先级对应唯一的一个任务,通过优先级遍历每一个任务,统计各个任务的stack栈的使用情况。
ptcb->OSTCBStkUsed为对应任务stack的使用情况。
堆栈检测需要注意点:
(1). 当用户不知道应该给任务分配多少堆栈空间时,堆栈检查功能是很有用的。在这个例子里,先分配足够的堆栈空间给任务,然后用堆栈检查操作看看任务到底需要多少堆栈空间。显然,任务要运行足够长时间,并要考虑各种情况才能得到正确数据。
(2). 决定的堆栈大小还要考虑系统今后的扩展,一般多分配10%,25%或者更多。如果系统对稳定性要求高,则应该多一倍以上。
(3). uCOS-II的堆栈检查功能要求任务建立时堆栈清零。OSTaskCreateExt()可以执行此项操作(设置选项OS_TASK_OPT_STK_CHK和OS_TASK_OPT_STK_CLR打开此项操作。
堆栈检测的原理:
uCOS-II从栈底向栈顶搜索非0元素(参看图F 1.1),同时用一个计数器记录0元素的个数。见下图:
|
b.应用任务的创建:
在开始多任务之前,笔者建立了一个叫做TaskStart()的任务,在启动多任务OSStart()之前用户至少要先建立一个任务,这一点非常重要。
不这样做用户的应用程序将会崩溃。
OSTaskCreate(Task_start,(void *)0,
&startup_task_stk[STARTUP_TASK_STK_SIZE-1], STARTUP_TASK_PRIO);
用户添加各个功能模块的任务,可以在Task_start任务里去创建,如下图:
void Task_start(void *p_arg)
{
OS_CPU_SR cpu_sr;
(void)p_arg; // 'p_arg' 并没有用到,防止编译器提示警告
OS_ENTER_CRITICAL();
OSTaskCreate(TaskLed0, (void * )0, (OS_STK *)&TASK_LED0_STK[LED_STK_SIZE-1], LED0_TASK_Prio);
OSTaskCreate(TaskKey, (void * )0, (OS_STK *)&TASK_KEY_STK[LED_STK_SIZE-1], KEY_TASK_Prio);
OSTaskSuspend(STARTUP_TASK_PRIO); //suspend but not delete
OS_EXIT_CRITICAL();
}