初始化和启动都在main函数里实现。
int main(void)
{
system_init(); //各种硬件外设功能的初始化
OSInit(); //系统初始化
...
.../*在这里创建任务*/
OSStart(); //系统启动
}
1、初始化
为了完成自身的工作,μC/OS-II定义了大量的全局数据结构,要让系统跑起来,需要先初始化各种需要的全局变量。
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 */
#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
}
- OS首先Initialize了任务控制块、事件控制表,还有一些条件编译的初始化(也就是说这些可以不要,改变一下宏定义就好了,这就是μC/OS-II的剪裁性,具体宏定义全都在os_cfg.h文件里。)
- 初始化之后,创建了一个空闲任务,这个必须要有,而且是最低优先级的,这样如果没有任务运行可以让系统有事可干(一直待机)。
2、创建任务
使用OSTaskCreate 创建任务,函数原型为
INT8U OSTaskCreate (void (*task)(void *p_arg),void *p_arg,OS_STK *ptos,INT8U prio)
创建任务代码示例:
int main(void)
{
...
OSInit();
...
OSTaskCreate(start_task,......);//创建起始任务
OSStart();
}
void start_task(void *pdata)
{
... ...//在这里启动系统时钟
OSStatInit(); //初始化统计任务
OS_ENTER_CRITICAL(); //进入临界区
... ...//创建任务
OSTaskCreate(main_task, ... , ... , ...);//创建任务
OS_EXIT_CRITICAL(); //退出临界区
}
这里只讲创建任务函数和进/退出临界区函数
OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL():
概念扫盲的时候知道,任何系统都是由中断驱动工作的,对于一些重要的操作,操作系统不希望被打断,于是会先关中断,完成操作后再把中断打开,进入临界区函数和退出实际上就是关开总中断。OSTaskCreate(main_task, … , … , …)函数原型
INT8U OSTaskCreate (
void (*task)(void *p_arg), //指向任务的指针
void *p_arg,//传递给任务的参数
OS_STK *ptos,//指向任务堆栈栈顶的指针
INT8U prio//任务的优先级
)
3、μC/OS-II的启动
μC/OS-II启动前,是需要先创建至少一个任务的,这个任务在初始化函数里就帮你创建好了,就是空闲函数。不过空闲函数属于系统级任务,是不能在里面添加用户代码的,只进行简单的计数功能,如果执行了OSStart(),而前面并没有创建用户任务,那么这系统除了待机并没啥用了(叫别人干活首先要让别人知道自己要干什么活吧,不然就空闲呗),所以一般创建一个任务,然后用这个任务再创建其它的任务(当然也可以把所有任务一股脑全在这里创建,只不过不好看而已),创建完把这个任务挂起(回收资源)就OK了。
上面有个错误,空闲函数是可以添加用户代码的,不过需要通过os提供的钩子函数,在钩子函数内进行用户操作,并且需要修改相应的宏使能钩子函数。(参考正点原子的UCOS开发手册发现)
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()函数源码如下
static void OS_SchedNew (void)
{
#if OS_LOWEST_PRIO <= 63u /* See if we support up to 64 tasks */
INT8U y;
y = OSUnMapTbl[OSRdyGrp];
OSPrioHighRdy = (INT8U)((y << 3u) + OSUnMapTbl[OSRdyTbl[y]]);//找到最高优先级任务
#else /* We support up to 256 tasks */
INT8U y;
OS_PRIO *ptbl;
if ((OSRdyGrp & 0xFFu) != 0u) {
y = OSUnMapTbl[OSRdyGrp & 0xFFu];
} else {
y = OSUnMapTbl[(OS_PRIO)(OSRdyGrp >> 8u) & 0xFFu] + 8u;
}
ptbl = &OSRdyTbl[y];
if ((*ptbl & 0xFFu) != 0u) {
OSPrioHighRdy = (INT8U)((y << 4u) + OSUnMapTbl[(*ptbl & 0xFFu)]);
} else {
OSPrioHighRdy = (INT8U)((y << 4u) + OSUnMapTbl[(OS_PRIO)(*ptbl >> 8u) & 0xFFu] + 8u);
}
#endif
}
OSStart ()主要任务就是,找到最高优先级的用户任务,标志相应任务控制块,然后执行。
关于如何找到最高优先级任务,在任务调度表一章,OS_SchedNew是对任务就绪表进行的操作,得到就绪任务表里优先级最高的任务。
关于可以支持256个任务,百度了一下,原来是版本升级了。。。
总结一下:
μC/OS-II的初始化和启动涉及的知识有
1、第4章,任务的基本概念,任务的状态、优先级、控制块及其链表结构、任务堆栈,临界区的概念。
2、第5章,任务的管理,包括任务的创建、挂起,并没有涉及任务优先级别的修改、删除、恢复及查询功能。
3、第6章,任务的调度,这里并没有详细描写,只用了一个OS_SchedNew ()函数得到就绪表中的最高优先级,OS_Sched()有在创建任务的时候使用,不过这里没描述。
3、第7章,就是这里的内容了,囊括了书前面写大部分的内容,这本书写的真好,不愧是畅销书