先给自己打个广告,本人的微信公众号正式上线了,搜索:张笑生的地盘,主要关注嵌入式软件开发,股票基金定投,足球等等,希望大家多多关注,有问题可以直接留言给我,一定尽心尽力回答大家的问题,二维码如下:
前面我们介绍了如何在K60平台上移植第一个ucos系统,我们知道创建一个task并启动它,需要三个函数支持,分别是OSInit、OSTaskCreate以及OSStart,今天我们先来看看OSInit和OSStart分别作了什么。
void OSInit (void)
{
#if OS_TASK_CREATE_EXT_EN > 0u
#if defined(OS_TLS_TBL_SIZE) && (OS_TLS_TBL_SIZE > 0u)
INT8U err;
#endif
#endif
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 > 0u) && (OS_MAX_FLAGS > 0u)
OS_FlagInit(); /* Initialize the event flag structures */
#endif
#if (OS_MEM_EN > 0u) && (OS_MAX_MEM_PART > 0u)
OS_MemInit(); /* Initialize the memory manager */
#endif
#if (OS_Q_EN > 0u) && (OS_MAX_QS > 0u)
OS_QInit(); /* Initialize the message queue structures */
#endif
#if OS_TASK_CREATE_EXT_EN > 0u
#if defined(OS_TLS_TBL_SIZE) && (OS_TLS_TBL_SIZE > 0u)
OS_TLS_Init(&err); /* Initialize TLS, before creating tasks */
if (err != OS_ERR_NONE) {
return;
}
#endif
#endif
OS_InitTaskIdle(); /* Create the Idle Task */
#if OS_TASK_STAT_EN > 0u
OS_InitTaskStat(); /* Create the Statistic Task */
#endif
#if OS_TMR_EN > 0u
OSTmr_Init(); /* Initialize the Timer Manager */
#endif
OSInitHookEnd(); /* Call port specific init. code */
#if OS_DEBUG_EN > 0u
OSDebugInit();
#endif
}
我们根据宏开关,简写如下:
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 > 0u) && (OS_MAX_FLAGS > 0u)
OS_FlagInit(); /* Initialize the event flag structures */
#endif
#if (OS_MEM_EN > 0u) && (OS_MAX_MEM_PART > 0u)
OS_MemInit(); /* Initialize the memory manager */
#endif
#if (OS_Q_EN > 0u) && (OS_MAX_QS > 0u)
OS_QInit(); /* Initialize the message queue structures */
#endif
OS_InitTaskIdle(); /* Create the Idle Task */
OSInitHookEnd(); /* Call port specific init. code */
}
一行一行分析,首先是OSInitHookBegin,如下,这个函数的主要作用就是清除异常堆栈信息,并没有其他功能。
void OSInitHookBegin (void)
{
INT32U size;
OS_STK *pstk;
/* Clear exception stack for stack checking. */
pstk = &OS_CPU_ExceptStk[0];
size = OS_CPU_EXCEPT_STK_SIZE;
while (size > 0u) {
size--;
*pstk++ = (OS_STK)0;
}
/* Align the ISR stack to 8-bytes */
OS_CPU_ExceptStkBase = (OS_STK *)&OS_CPU_ExceptStk[OS_CPU_EXCEPT_STK_SIZE];
OS_CPU_ExceptStkBase = (OS_STK *)((OS_STK)(OS_CPU_ExceptStkBase) & 0xFFFFFFF8);
#if OS_TMR_EN > 0u
OSTmrCtr = 0u;
#endif
}
再来看OS_InitMisc,如下,这个函数的作用就是对任务管理控制的全局变量初始化
static void OS_InitMisc (void)
{
#if OS_TIME_GET_SET_EN > 0u
OSTime = 0uL; /* Clear the 32-bit system clock */
#endif
OSIntNesting = 0u; /* Clear the interrupt nesting counter */
OSLockNesting = 0u; /* Clear the scheduling lock counter */
OSTaskCtr = 0u; /* Clear the number of tasks */
OSRunning = OS_FALSE; /* Indicate that multitasking not started */
OSCtxSwCtr = 0u; /* Clear the context switch counter */
OSIdleCtr = 0uL; /* Clear the 32-bit idle counter */
#if OS_TASK_STAT_EN > 0u
OSIdleCtrRun = 0uL;
OSIdleCtrMax = 0uL;
OSStatRdy = OS_FALSE; /* Statistic task is not ready */
#endif
#ifdef OS_SAFETY_CRITICAL_IEC61508
OSSafetyCriticalStartFlag = OS_FALSE; /* Still allow creation of objects */
#endif
#if OS_TASK_REG_TBL_SIZE > 0u
OSTaskRegNextAvailID = 0u; /* Initialize the task register ID */
#endif
}
接着看OS_InitRdyList,如下,主要是对任务状态变量初始化,我们知道ucos中是通过任务组就绪变量OSRdyGrp和任务就绪数组OSRdyTbl来获取任务当前所处状态的(运行态,挂起态等等),初始化时,因为还没有创建任务,这里先清零
static void OS_InitRdyList (void)
{
INT8U i;
OSRdyGrp = 0u; /* Clear the ready list */
for (i = 0u; i < OS_RDY_TBL_SIZE; i++) {
OSRdyTbl[i] = 0u;
}
OSPrioCur = 0u;
OSPrioHighRdy = 0u;
OSTCBHighRdy = (OS_TCB *)0;
OSTCBCur = (OS_TCB *)0;
}
接着看OS_InitTCBList,如下,这个函数主要是对任务链表做初始化,其实就是为了先占据部分RAM内存空间,在我们真正使用的时候,使用这些RAM空间。我们知道在ucos中是通过任务链表来管理任务的
static void OS_InitTCBList (void)
{
INT8U ix;
INT8U ix_next;
OS_TCB *ptcb1;
OS_TCB *ptcb2;
OS_MemClr((INT8U *)&OSTCBTbl[0], sizeof(OSTCBTbl)); /* Clear all the TCBs */
OS_MemClr((INT8U *)&OSTCBPrioTbl[0], sizeof(OSTCBPrioTbl)); /* Clear the priority table */
for (ix = 0u; ix < (OS_MAX_TASKS + OS_N_SYS_TASKS - 1u); ix++) { /* Init. list of free TCBs */
ix_next = ix + 1u;
ptcb1 = &OSTCBTbl[ix];
ptcb2 = &OSTCBTbl[ix_next];
ptcb1->OSTCBNext = ptcb2;
#if OS_TASK_NAME_EN > 0u
ptcb1->OSTCBTaskName = (INT8U *)(void *)"?"; /* Unknown name */
#endif
}
ptcb1 = &OSTCBTbl[ix];
ptcb1->OSTCBNext = (OS_TCB *)0; /* Last OS_TCB */
#if OS_TASK_NAME_EN > 0u
ptcb1->OSTCBTaskName = (INT8U *)(void *)"?"; /* Unknown name */
#endif
OSTCBList = (OS_TCB *)0; /* TCB lists initializations */
OSTCBFreeList = &OSTCBTbl[0];
}
再来看OS_InitEventList,如下,主要是对系统的事件机制(邮箱、队列、信号量等等)初始化,也是先占据RAM内存空间,以便后续使用。
static void OS_InitEventList (void)
{
#if (OS_EVENT_EN) && (OS_MAX_EVENTS > 0u)
#if (OS_MAX_EVENTS > 1u)
INT16U ix;
INT16U ix_next;
OS_EVENT *pevent1;
OS_EVENT *pevent2;
OS_MemClr((INT8U *)&OSEventTbl[0], sizeof(OSEventTbl)); /* Clear the event table */
for (ix = 0u; ix < (OS_MAX_EVENTS - 1u); ix++) { /* Init. list of free EVENT control blocks */
ix_next = ix + 1u;
pevent1 = &OSEventTbl[ix];
pevent2 = &OSEventTbl[ix_next];
pevent1->OSEventType = OS_EVENT_TYPE_UNUSED;
pevent1->OSEventPtr = pevent2;
#if OS_EVENT_NAME_EN > 0u
pevent1->OSEventName = (INT8U *)(void *)"?"; /* Unknown name */
#endif
}
pevent1 = &OSEventTbl[ix];
pevent1->OSEventType = OS_EVENT_TYPE_UNUSED;
pevent1->OSEventPtr = (OS_EVENT *)0;
#if OS_EVENT_NAME_EN > 0u
pevent1->OSEventName = (INT8U *)(void *)"?"; /* Unknown name */
#endif
OSEventFreeList = &OSEventTbl[0];
#else
OSEventFreeList = &OSEventTbl[0]; /* Only have ONE event control block */
OSEventFreeList->OSEventType = OS_EVENT_TYPE_UNUSED;
OSEventFreeList->OSEventPtr = (OS_EVENT *)0;
#if OS_EVENT_NAME_EN > 0u
OSEventFreeList->OSEventName = (INT8U *)"?"; /* Unknown name */
#endif
#endif
#endif
}
OS_FlagInit、OS_MemInit、OS_QInit是分别对消息机制、内存管理功能进行初始化,主要也是占据内存空间,预留分配名额
再看OS_InitTaskIdle,如下,主要是创建了一个空闲任务,我们知道,在ucos中,系统肯定会有某个时刻处在没有任务运行的状态,这个时候cpu不能停着,它会执行这个空闲任务。
static void OS_InitTaskIdle (void)
{
#if OS_TASK_NAME_EN > 0u
INT8U err;
#endif
#if OS_TASK_CREATE_EXT_EN > 0u
#if OS_STK_GROWTH == 1u
(void)OSTaskCreateExt(OS_TaskIdle,
(void *)0, /* No arguments passed to OS_TaskIdle() */
&OSTaskIdleStk[OS_TASK_IDLE_STK_SIZE - 1u],/* Set Top-Of-Stack */
OS_TASK_IDLE_PRIO, /* Lowest priority level */
OS_TASK_IDLE_ID,
&OSTaskIdleStk[0], /* Set Bottom-Of-Stack */
OS_TASK_IDLE_STK_SIZE,
(void *)0, /* No TCB extension */
OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR);/* Enable stack checking + clear stack */
#else
(void)OSTaskCreateExt(OS_TaskIdle,
(void *)0, /* No arguments passed to OS_TaskIdle() */
&OSTaskIdleStk[0], /* Set Top-Of-Stack */
OS_TASK_IDLE_PRIO, /* Lowest priority level */
OS_TASK_IDLE_ID,
&OSTaskIdleStk[OS_TASK_IDLE_STK_SIZE - 1u],/* Set Bottom-Of-Stack */
OS_TASK_IDLE_STK_SIZE,
(void *)0, /* No TCB extension */
OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR);/* Enable stack checking + clear stack */
#endif
#else
#if OS_STK_GROWTH == 1u
(void)OSTaskCreate(OS_TaskIdle,
(void *)0,
&OSTaskIdleStk[OS_TASK_IDLE_STK_SIZE - 1u],
OS_TASK_IDLE_PRIO);
#else
(void)OSTaskCreate(OS_TaskIdle,
(void *)0,
&OSTaskIdleStk[0],
OS_TASK_IDLE_PRIO);
#endif
#endif
#if OS_TASK_NAME_EN > 0u
OSTaskNameSet(OS_TASK_IDLE_PRIO, (INT8U *)(void *)"uC/OS-II Idle", &err);
#endif
}
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;
OSStartHighRdy(); /* Execute target specific code to start task */
}
}
我们分析代码,发现,在这里先做了一次任务调度查找,找到当前优先级最高的任务(OS_SchedNew);然后将最高优先级任务指针赋值给系统,系统开始运行最高优先级任务(OSStartHighRdy)
这里预留一个小问题:为什么首先需要判断OS状态(if (OSRunning == OS_FALSE)),理论上来说,之前初始化时候设置了OSRunning = OS_FALSE,执行到这里的时候,应该还是OS_FALSE,那为什么需要这一句判断呢?希望了解的朋友们在留言区讨论,感谢大家的赐教,先谢过了!
我们来看一下汇编函数OSStartHighRdy,如下,先设置PendSV异常优先级,然后触发这个异常,从而引起任务调度(任务调度我们在别的地方已经分析过了,这里就不在分析了)
OSStartHighRdy
LDR R0, =NVIC_SYSPRI14 ; Set the PendSV exception priority
LDR R1, =NVIC_PENDSV_PRI
STRB R1, [R0]
MOVS R0, #0 ; Set the PSP to 0 for initial context switch call
MSR PSP, R0
LDR R0, =OS_CPU_ExceptStkBase ; Initialize the MSP to the OS_CPU_ExceptStkBase
LDR R1, [R0]
MSR MSP, R1
LDR R0, =OSRunning ; OSRunning = TRUE
MOVS R1, #1
STRB R1, [R0]
LDR R0, =NVIC_INT_CTRL ; Trigger the PendSV exception (causes context switch)
LDR R1, =NVIC_PENDSVSET
STR R1, [R0]
CPSIE I