uC/OS II学习《一》—uC/OS II内核完全解析之系统初始化
最近一直在学习 uC/OS II嵌入式操作系统,经过几天的摸索,终于将他移植到
了 STC51单片机上,虽然 51上跑操作系统确实有点吃力,没什么实际应用意义,
但还是蛮兴奋的。现将本人对内核的理解和心得记录下来。
从哪里开始呢?从操作系统的概念说起?那肯定不现实,我也没有那么多的时
间来写。就从 main()函数开始说起,一层层的将内核剥开。
Main()函数如下:
void main(void)
{
OSInit(); //系统初始化
InitTimer0(); // Timer0初始化,Timer0作为系统时钟节拍源
OSTaskCreate(job1, (void *)0, (void *)&TaskStk[1][0],6); /*建立任务 1 */
OSTaskCreate(job2, (void *)0, (void *)&TaskStk[2][0],7); /*建立任务 2 */
OSStart(); /* 开始多任务调度 */
}
看起来很简单,先来分析一下:
OSInit(); //系统初始化,这是个大头,接下来就讲他。
InitTimer0(); // Timer0初始化,Timer0作为系统时钟节拍源,时钟节拍,不难理
解,操作系统离不开它,这个简单,不忙讲。
OSTaskCreate(job1, (void *)0, (void *)&TaskStk[1][0],6); /*建立任务 1 */
OSTaskCreate(job2, (void *)0, (void *)&TaskStk[2][0],7); /*建立任务 2 */
这是建立的两个任务,和内核关系不大,也简单,你先当他不存在。
OSStart(); /* 开始多任务调度 */。这是内核级的,很重要,不过也简单,慢慢来,
以后讲。
下面我们就从 OSInit()系统初始化,开始解析,系统初始化,主要的任务就是
一.将系统定义的全局变量该清零的清零,该赋值的赋值。
二.初始化任务控制块(TCB),建立空闲 TCB
三.创建空闲任务
就这三个初始化的任务,我们一条一条说:
一.将系统定义的全局变量该清零的清零,该赋值的赋值。看 OSInit()函数的时
候,你会看到很多的条件编译,我们只看必不可少的:
OSTime = 0L; //系统时间清零(32位的)
OSIntNesting = 0; //中断嵌套层数清零
OSLockNesting = 0; //任务锁定层数清零
OSTaskCtr = 0; //任务计数器清零
OSRunning = FALSE; //任务运行标志置零
OSIdleCtr = 0L; //空闲任务计数器清零
OSCtxSwCtr = 0; //任务切换次数清零
OSRdyGrp = 0; //任务就绪组清零
for (i = 0; i < OS_RDY_TBL_SIZE; i++) //任务就绪表清零
{
OSRdyTbl[i] = 0;
}
OSPrioCur = 0; //当前任务优先级清零
OSPrioHighRdy = 0; //最高优先级清零
二.初始化任务控制块(TCB),建立空闲 TCB:
OSTCBHighRdy = (OS_TCB *)0; //初始化最高优先级任务控制块
OSTCBCur = (OS_TCB *)0; //初始化当前任务控制块
OSTCBList = (OS_TCB *)0; //初始化任务控制块链表
for (i = 0; i < (OS_LOWEST_PRIO + 1); i++)
{
OSTCBPrioTbl[i] = (OS_TCB *)0; //初始化任务控制块优先级列
表
}
for (i = 0; i < (OS_MAX_TASKS + OS_N_SYS_TASKS - 1); i++)
{
OSTCBTbl[i].OSTCBNext = &OSTCBTbl[i + 1]; //建立 TCB单向链
表
}
OSTCBTbl[OS_MAX_TASKS + OS_N_SYS_TASKS - 1].OSTCBNext = (OS_TCB
*)0;
//链表末端
指向 0
OSTCBFreeList = &OSTCBTbl[0]; //链表头指向 OSTCBTbl[0]
三.创建空闲任务,51的堆栈是向上增长的模式所以 OS_STK_GROWTH ==0,
建立空闲任务应该调用:
OSTaskCreate(OSTaskIdle, (void *)0, &OSTaskIdleStk[0], OS_IDLE_PRIO);//
建立空闲任务
OK,OSInit()系统初始化函数就这些要做的,看起来也很简单。在初始化函
数中,还有好多其他繁琐的定义如:事件控制块的初始化,内存管理的初始化,
建立统计任务等,那些都不是必须的,可要可不要,鉴于 51 RAM太小,我们就
不要那些东西了,裁减掉。
系统初始化中前两项都是一些变量的赋值,清零,很好理解。第三项,创建
空闲任务是怎么创建的呢?我们下次再把它展开
uC/OS II学习《二》—uC/OS II内核完全解析之空闲任务的建立
上次说到空闲任务的建立:
OSTaskCreate(OSTaskIdle, (void *)0, &OSTaskIdleStk[0], OS_IDLE_PRIO);//建立
空闲任务
空闲任务的建立是调用 OS_TASK.C中的 OSTaskCreate 任务创建函数完成
的,OSTaskCreate函数接收 4个变量:task是任务代码的指针,pdata是当任务
开始执行时传递给任务的参数的指针,ptos是分配给任务的堆栈的栈顶指针,
prio是分配给任务的优先级。
INT8U OSTaskCreate (void (*task)(void *pd), void *ppdata, OS_STK *ptos,
INT8U prio)
{
void *psp;
INT8U err;
if (prio > OS_LOWEST_PRIO)// 判断分配给任务的优先级是否有效。任务的
优先级//必须在 0到 64之间
{
return (OS_PRIO_INVALID); //优先级无效,返回 42
}
OS_ENTER_CRITICAL(); //关中断,EA=0;
if (OSTCBPrioTbl[prio] == (OS_TCB *)0) //确保在规定的优先级上还没有建立任
务,如//果此优先级 prio上有任务建立则 OSTCBPrioTbl[prio] =
(OS_TCB *)1
{
OSTCBPrioTbl[prio] = (OS_TCB *)1; //占用该优先级,其他任务就不能再应用
//该优先级
OS_EXIT_CRITICAL(); //开中断 EA=1
psp = (void *)OSTaskStkInit(task, ppdata, ptos, 0);//堆栈初始化,返回栈顶指针
err = OSTCBInit(prio, psp, (void *)0, 0, 0, (void *)0, 0); //初始化任务控制块
if (err == OS_NO_ERR) //如果初始化成功
{
OS_ENTER_CRITICAL(); //关中断 EA=0
OSTaskCtr++; //任务计数器加一
OS_EXIT_CRITICAL(); //开中断 EA=1
if (OSRunning) //如果任务运行标志为 1
{
OSSched(); //调用任务调用任务调度函数
}
}
else {
OS_ENTER_CRITICAL(); //关中断 EA=0
OSTCBPrioTbl[prio] = (OS_TCB *)0; //初始化不成功,讲此优先级
//还给优先级表,让其他任务建立时可用
OS_EXIT_CRITICAL(); //开中断 EA=1
}
return (err); //返回错误
}
else {
OS_EXIT_CRITICAL();//开中断 EA=1
return (OS_PRIO_EXIST); //返回此任务优先级已经存在
}
}
到这空闲任务就算建立起来了,从上面的函数来看,空闲任务的建立主要做的
事情就是:
psp = (void *)OSTaskStkInit(task, ppdata, ptos, 0);//堆栈初始化,返回栈顶指针
err = OSTCBInit(prio, psp, (void *)0, 0, 0, (void *)0, 0); //初始化任务控制块
其余的语句都是为了做这两件事情服务的。
下面我们在来看堆栈初始化函数 OSTaskStkInit(),堆栈的初始化是要自己根据
处处理器的特点编写的,51单片机在任务切换和中断来临时需要入栈的寄存器
有 12个:R0-R7,ACC,B,PSW,DPTR(DPH,DPL)共 13个字节,在加上
任务的地址(2字节),共 15字节。
从上面看出 OSTaskStkInit()函数接收 4个参数:
task 指向任务代码的指针
pdata 当任务第一次执行时将要传入任务的用户数据结构指
针
ptos 栈顶指针。ptos指针被默认为用户堆栈入口指针。
OS_STK_GROWTH被置 1,那么,ptos指向用户堆
栈的最高有效地址。同样地,如果
OS_STK_GROWTH清 0,ptos将指向用户堆栈的最
低有效地址。
opt 指定可以改变 OSTaskStkInit()行为的选项。
void *OSTaskStkInit (void (*task)(void *pd), void *ppdata, void *ptos, INT16U opt)
{
OS_STK *stk; // typedef unsigned char OS_STK栈单元宽度为 8比特
ppdata = ppdata;
opt = opt; //opt没被用到,保留此语句防
止告警产生
stk = (OS_STK *)ptos; //用户堆栈最低有效地址
*stk++ = 15; //用户堆栈长度
*stk++ = (INT16U)task & 0xFF; //任务地址低 8位
*stk++ = (INT16U)task >> 8; //任务地址高 8位
*stk++ = 0x00; //PSW
*stk++ = 0x0A; //ACC
*stk++ = 0x0B; //B
*stk++ = 0x00; //DPL
*stk++ = 0x00; //DPH
*stk++ = 0x00; //R0
*stk++ = 0x01; //R1
*stk++ = 0x02; //R2
*stk++ = 0x03; //R3
*stk++ = 0x04; //R4
*stk++ = 0x05; //R5
*stk++ = 0x06; //R6
*stk++ = 0x07; //R7
return ((void *)ptos); //返回用户堆栈最低有效地址
}
上面这四个参数是由 OSTaskCreate(OSTaskIdle, (void *)0,
&OSTaskIdleStk[0], OS_IDLE_PRIO);任务创建函数传递进来的。初始化完任务堆
栈后,函数返回用户堆栈的栈顶指针,即最低地址。将这个地址传递给任务控制
块初始化函数 OSTCBInit()。
即:err = OSTCBInit(prio, psp, (void *)0, 0, 0, (void *)0, 0); //初始化任务控制块
该上班了,有空再说任务控制块的初始化
uC/OS II学习《三》—uC/OS II内核完全解析之任务控制块的初始化
好长时间没更新我的博客了,接着前两节的写,上次写到任务控制块的初始
化。任务控制块是一个数据结构,当任务的 CPU使用权被剥夺时,μC/OS-Ⅱ用
它来保存该任务的状态。当任务重新得到 CPU使用权时,任务控制块能确保任务
从当时被中断的那一点丝毫不差地继续执行。OS_TCBs全部驻留在 RAM中。
先看任务控制块的组织结构:
typedef struct os_tcb
{
OS_STK *OSTCBStkPtr; //指向当前任务栈顶的指针
#if OS_TASK_CREATE_EXT_EN //以下参数只有在任务控制块扩展标
//志 使能 OS_TASK_CREATE_EXT_EN才有用
void *OSTCBExtPtr;
OS_STK *OSTCBStkBottom;
INT32U OSTCBStkSize;
INT16U OSTCBOpt;
INT16U OSTCBId;
#endif
//下面两个变量用于任务控制块 OS_TCBs的双重链接,该链表在时钟节拍函数 OSTimeTick()中使用,用于
刷新各
//个任务的任务延迟变量.OSTCBDly,每个任务的任务控制块 OS_TCB在任务建立的时候被链接到链表
//中,在任务删除的时候从链表中被删除。双重连接的链表使得任一成员都能被快速插入或删除。
struct os_tcb *OSTCBNext;
struct os_tcb *OSTCBPrev;
#if (OS_Q_EN && (OS_MAX_QS >= 2)) || OS_MBOX_EN || OS_SEM_EN
OS_EVENT *OSTCBEventPtr;// OSTCBEventPtr是指向事件控制块的指针
#endif
#if (OS_Q_EN && (OS_MAX_QS >= 2)) || OS_MBOX_EN
void *OSTCBMsg;// OSTCBMsg 是指向传给任务的消息的指针。
#endif
INT16U OSTCBDly;//当需要把任务延时若干时钟节拍时要用到这个变量
INT8U OSTCBStat;// 任务的状态字
INT8U OSTCBPrio;// 任务优先级
// .OSTCBX, .OSTCBY, .OSTCBBitX和 .OSTCBBitY用于加速任务进入
//就绪态的过程或进入等待事件发生状态的过程(避免在运行中去计算这些
//值)。
INT8U OSTCBX;
INT8U OSTCBY;
INT8U OSTCBBitX;
INT8U OSTCBBitY;
#if OS_TASK_DEL_EN
BOOLEAN OSTCBDelReq;// .OSTCBDelReq 是一个布尔量,用于表示
//该任务是否需要删除
#endif
} OS_TCB;
下面来看新建任务的 TCB初始化,先说该函数的七个参数:
INT8U prio, ……………..任务优先级
OS_STK *ptos, ……………..任务堆栈栈顶指针
OS_STK *pbos, ……………..任务堆栈栈低指针
INT16U id, ……………..任务 ID号(以后扩展用)
INT16U stk_size, ……………..任务任务堆栈长度
void *pext, ……………..任务扩展指针
INT16U opt,…………….可选项
其中必不可少的是前两个参数,其他注释在程序里。
INT8U OSTCBInit (INT8U prio, OS_STK *ptos, OS_STK *pbos, INT16U id,
INT16U stk_size, void *pext, INT16U opt)
{
OS_TCB *ptcb; //定义 TCB变量
OS_ENTER_CRITICAL(); //关中断
ptcb = OSTCBFreeList; //从空闲 TCB列表中取出一个 TCB(OSTCBFreeList
//指向的那个)
if (ptcb != (OS_TCB *)0) //检查上一步取到的 TCB存不存在
{
OSTCBFreeList = ptcb->OSTCBNext;//让 OSTCBFreeList指向下一个空
//闲 TCB
OS_EXIT_CRITICAL();//关中断
ptcb->OSTCBStkPtr = ptos; //将传递进来的七个参数放入 TCB中
ptcb->OSTCBPrio = (INT8U)prio;
ptcb->OSTCBStat = OS_STAT_RDY;
ptcb->OSTCBDly = 0;
#if OS_TASK_CREATE_EXT_EN //其中有五个参数用于任务扩展
ptcb->OSTCBExtPtr = pext;
ptcb->OSTCBStkSize = stk_size;
ptcb->OSTCBStkBottom = pbos;
ptcb->OSTCBOpt = opt;
ptcb->OSTCBId = id;
#else // 如果 OS_TASK_CREATE_EXT_EN=0,执行下面为了防止编译器警告
pext = pext;
stk_size = stk_size;
pbos = pbos;
opt = opt;
id = id;
#endif
#if OS_TASK_DEL_EN //如果任务删除功能使能,对相应变量赋值,此语句可视
//为为无关代码
ptcb->OSTCBDelReq = OS_NO_ERR;
#endif
//下面四个变量是用于加速任务进入就绪态的过程或进入等待事件发生状态的
//过程(避免在运行中去计算这些值)。这些值是在任务建立时算好的,或者是
在//改变任务优先级时算出的
ptcb->OSTCBY = prio >> 3;
ptcb->OSTCBBitY = OSMapTbl[ptcb->OSTCBY];
ptcb->OSTCBX = prio & 0x07;
ptcb->OSTCBBitX = OSMapTbl[ptcb->OSTCBX];
//下面这几句也不是必须的,涉及到任务间的通讯,先不管
#if OS_MBOX_EN || (OS_Q_EN && (OS_MAX_QS >= 2)) || OS_SEM_EN
ptcb->OSTCBEventPtr = (OS_EVENT *)0;
#endif
#if OS_MBOX_EN || (OS_Q_EN && (OS_MAX_QS >= 2))
ptcb->OSTCBMsg = (void *)0;
#endif
OS_ENTER_CRITICAL(); //关中断
OSTCBPrioTbl[prio] = ptcb; //将新建的 TCB放到 TCB优先级表中
//下面语句是将新建的任务 TCB放到已建立的任务 TCB链表中
ptcb->OSTCBNext = OSTCBList;
ptcb->OSTCBPrev = (OS_TCB *)0;
if (OSTCBList != (OS_TCB *)0) {
OSTCBList->OSTCBPrev = ptcb;
}
OSTCBList = ptcb;
//下面两句是将就绪表中相应位置位,表示该新建任务进入就绪状态
OSRdyGrp |= ptcb->OSTCBBitY;
OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX;
OS_EXIT_CRITICAL();
return (OS_NO_ERR);
} else {
OS_EXIT_CRITICAL();
return (OS_NO_MORE_TCB);
}
}
这样加上上面一篇文章,uC/OS II的空闲任务算是建立起来,总结一下任务
的建立主要做两件事情:一是任务堆栈的初始化,二是任务控制块(TCB)的初
始化。
这样《一》《二》《三》加起来就把 uC/OS II的系统初始化完成了,下面就要
说说系统的心率——时钟节拍了……下章再写
本文档为【uC-OS_II学习】,请使用软件OFFICE或WPS软件打开。作品中的文字与图均可以修改和编辑,
图片更改请在作品中右键图片并更换,文字修改请直接点击文字进行修改,也可以新增和删除文档中的内容。
该文档来自用户分享,如有侵权行为请发邮件ishare@vip.sina.com联系网站客服,我们会及时删除。
[版权声明] 本站所有资料为用户分享产生,若发现您的权利被侵害,请联系客服邮件isharekefu@iask.cn,我们尽快处理。
本作品所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用。
网站提供的党政主题相关内容(国旗、国徽、党徽..)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。