在使用UCOSIII之前我们必须先初始化UCOSIII,函数OSInit()用来完成UCOSIII的初始化,而且OSInit()必须先于其他UCOSIII函数调用,包括OSStart()。
一般UCOSIII的main函数遵循以下的格式编写:
int main(void)
{
OS_ERR err;
……
//其他函数,一般为外设初始化函数
……
OSInit(&err);
……
//其他函数,一般为创建任务函数
……
OSStart(&err);
}
也就是说:
需要注意:我们在调用OSStart()开启UCOSIII之前一定要至少创建一个任务,其实我们在调用OSInit()函数初始化UCOSIII的时候已经创建了一个空闲任务和时钟节拍任务。
接下来,我们看一下UCOSIII的初始化函数OSInit()函数:
void OSInit (OS_ERR *p_err)
{
CPU_STK *p_stk;
CPU_STK_SIZE size;
#ifdef OS_SAFETY_CRITICAL
if (p_err == (OS_ERR *)0) {
OS_SAFETY_CRITICAL_EXCEPTION();
return;
}
#endif
OSInitHook(); /* 钩子函数 */
OSIntNestingCtr = (OS_NESTING_CTR)0; /* Clear the interrupt nesting counter */
OSRunning = OS_STATE_OS_STOPPED; /* UCOSIII正在运行的标志 */
OSSchedLockNestingCtr = (OS_NESTING_CTR)0; /* Clear the scheduling lock counter */
OSTCBCurPtr = (OS_TCB *)0; /* 指向正在运行任务控制块的指针 */
OSTCBHighRdyPtr = (OS_TCB *)0; //指向最高优先级就绪任务控制块的指针
OSPrioCur = (OS_PRIO)0; /* 正在运行任务的优先级 */
OSPrioHighRdy = (OS_PRIO)0; //最高优先级别的就绪任务的优先级
OSPrioSaved = (OS_PRIO)0;
#if OS_CFG_SCHED_LOCK_TIME_MEAS_EN > 0u
OSSchedLockTimeBegin = (CPU_TS)0;
OSSchedLockTimeMax = (CPU_TS)0;
OSSchedLockTimeMaxCur = (CPU_TS)0;
#endif
#ifdef OS_SAFETY_CRITICAL_IEC61508
OSSafetyCriticalStartFlag = DEF_FALSE;
#endif
#if OS_CFG_SCHED_ROUND_ROBIN_EN > 0u
OSSchedRoundRobinEn = DEF_FALSE;
OSSchedRoundRobinDfltTimeQuanta = OSCfg_TickRate_Hz / 10u;
#endif
if (OSCfg_ISRStkSize > (CPU_STK_SIZE)0) {
p_stk = OSCfg_ISRStkBasePtr; /* Clear exception stack for stack checking. */
if (p_stk != (CPU_STK *)0) {
size = OSCfg_ISRStkSize;
while (size > (CPU_STK_SIZE)0) {
size--;
*p_stk = (CPU_STK)0;
p_stk++;
}
}
}
#if OS_CFG_APP_HOOKS_EN > 0u
OS_AppTaskCreateHookPtr = (OS_APP_HOOK_TCB )0; /* Clear application hook pointers */
OS_AppTaskDelHookPtr = (OS_APP_HOOK_TCB )0;
OS_AppTaskReturnHookPtr = (OS_APP_HOOK_TCB )0;
OS_AppIdleTaskHookPtr = (OS_APP_HOOK_VOID)0;
OS_AppStatTaskHookPtr = (OS_APP_HOOK_VOID)0;
OS_AppTaskSwHookPtr = (OS_APP_HOOK_VOID)0;
OS_AppTimeTickHookPtr = (OS_APP_HOOK_VOID)0;
#endif
#if OS_CFG_TASK_REG_TBL_SIZE > 0u
OSTaskRegNextAvailID = (OS_REG_ID)0;
#endif
OS_PrioInit(); /* 优先级初始化 */
OS_RdyListInit(); /* 任务就绪列表初始化 */
#if OS_CFG_FLAG_EN > 0u /* Initialize the Event Flag module */
OS_FlagInit(p_err);
if (*p_err != OS_ERR_NONE) {
return;
}
#endif
#if OS_CFG_MEM_EN > 0u /* Initialize the Memory Manager module */
OS_MemInit(p_err);
if (*p_err != OS_ERR_NONE) {
return;
}
#endif
#if (OS_MSG_EN) > 0u /* Initialize the free list of OS_MSGs */
OS_MsgPoolInit(p_err);
if (*p_err != OS_ERR_NONE) {
return;
}
#endif
#if OS_CFG_MUTEX_EN > 0u /* Initialize the Mutex Manager module */
OS_MutexInit(p_err);
if (*p_err != OS_ERR_NONE) {
return;
}
#endif
#if OS_CFG_Q_EN > 0u
OS_QInit(p_err); /* Initialize the Message Queue Manager module */
if (*p_err != OS_ERR_NONE) {
return;
}
#endif
#if OS_CFG_SEM_EN > 0u /* Initialize the Semaphore Manager module */
OS_SemInit(p_err);
if (*p_err != OS_ERR_NONE) {
return;
}
#endif
#if defined(OS_CFG_TLS_TBL_SIZE) && (OS_CFG_TLS_TBL_SIZE > 0u)
OS_TLS_Init(p_err); /* Initialize Task Local Storage, before creating tasks */
if (*p_err != OS_ERR_NONE) {
return;
}
#endif
OS_TaskInit(p_err); /* Initialize the task manager */
if (*p_err != OS_ERR_NONE) {
return;
}
#if OS_CFG_ISR_POST_DEFERRED_EN > 0u
OS_IntQTaskInit(p_err); /* Initialize the Interrupt Queue Handler Task */
if (*p_err != OS_ERR_NONE) {
return;
}
#endif
OS_IdleTaskInit(p_err); /* 空闲任务初始化 */
if (*p_err != OS_ERR_NONE) {
return;
}
OS_TickTaskInit(p_err); /* 时钟节拍任务初始化 */
if (*p_err != OS_ERR_NONE) {
return;
}
#if OS_CFG_STAT_TASK_EN > 0u /* Initialize the Statistic Task */
OS_StatTaskInit(p_err);
if (*p_err != OS_ERR_NONE) {
return;
}
#endif
#if OS_CFG_TMR_EN > 0u /* Initialize the Timer Manager module */
OS_TmrInit(p_err);
if (*p_err != OS_ERR_NONE) {
return;
}
#endif
#if OS_CFG_DBG_EN > 0u
OS_Dbg_Init();
#endif
OSCfg_Init();
}
可以看出,函数OSInit()主要负责建立任务控制链表、就绪任务表等一些数据结构,并对系统使用的全局变量进行初始化。
我们之前讲5个系统任务的时候说到,空闲任务、时钟节拍任务是必须创建的任务;而统计任务、定时任务、中断服务管理任务则是可选任务。怎么保证这一点呢?
在这里就得到解答了。在UCOSIII的系统初始化函数OSInit()中,前两个任务OS_IdleTaskInit()和OS_TickTaskInit()都是一定要运行的,而后三个任务OS_StatTaskInit()、OS_TmrInit()和OS_IntQTaskInit()都采用条件编译的方式。只有在OS_CFG.h文件中配置开启后,才会运行这些任务。
既然是任务,那么就一定有优先级。那么这5个系统任务的优先级有事怎么分布的呢?
UCOSIII中以下优先级用户程序不能使用,因为这些优先级分配给了UCOSIII的5个系统内部任务:
使用函数OSStart()来启动UCOSIII,函数如下:
void OSStart (OS_ERR *p_err)
{
#ifdef OS_SAFETY_CRITICAL
if (p_err == (OS_ERR *)0) {
OS_SAFETY_CRITICAL_EXCEPTION();
return;
}
#endif
if (OSRunning == OS_STATE_OS_STOPPED) {
OSPrioHighRdy = OS_PrioGetHighest(); /* 寻找有就绪任务的最高优先级 */
OSPrioCur = OSPrioHighRdy;
OSTCBHighRdyPtr = OSRdyList[OSPrioHighRdy].HeadPtr; //寻找该优先级的任务控制块
OSTCBCurPtr = OSTCBHighRdyPtr;
OSRunning = OS_STATE_OS_RUNNING;
OSStartHighRdy(); /* Execute target specific code to start task */
*p_err = OS_ERR_FATAL_RETURN; /* OSStart() is not supposed to return */
} else {
*p_err = OS_ERR_OS_RUNNING; /* OS is already running */
}
}
在函数OSStart()中,用到了表示内核是否处于运行状态的变量OSRunning。若该变量为FALSE,则意味着内核处于未运行状态;若该变量为TRUE,则意味着内核处于运行状态。
由于在调用函数OSInit()进行初始化之后,已经将该值初始化为FALSE,所以在OSStart()函数中首先对OSRunning进行判断。如果该值为假,则查找最高优先级的就绪任务开始运行,并将OSRunning赋值为TRUE;否则,意味着内核已经处于运行状态了,函数OSInit()就什么工作也不做地返回。