UCOSII概念及内核Word格式文档下载.docx
《UCOSII概念及内核Word格式文档下载.docx》由会员分享,可在线阅读,更多相关《UCOSII概念及内核Word格式文档下载.docx(12页珍藏版)》请在冰豆网上搜索。
法简化了任务间的信息交换,但是必须保证每个任务在处理共享数据时的
排它性,以避免竞争和数据的破坏。
与共享资源打交道时,使之满足互斥
条件最一般的方法有:
关中断(μC/OS-?
在处理内部变量和数据结构时就是使用的这种手
段)
使用测试并置位指令
禁止做任务切换
利用信号量
6.信号量:
信号量用于:
控制共享资源的使用权(满足互斥条件)
标志某事件的发生
使两个任务的行为同步
对信号量只能实施三种操作:
初始化(INITIALIZE),也可称作建立(CREATE);
等信号(WAIT)也可称作挂起(PEND);
给信号(SIGNAL)或发信号(POST)。
这是我自己写的验证信号量的部分代码:
voidtask_one(void*pdata);
voidtask_two(void*pdata);
OS_EVENT*tasktwo;
#include"
includes.h"
GUI_Init();
VCInit();
//初始化一些变量
OSInit();
timeSetEvent(OS_TICKS_PER_SEC,0,OSTickISR,0,TIME_PERIODIC);
//
产生节拍
tasktwo=OSSemCreate(0);
OSTaskCreate(task_two,0,&
TaskStk[7][TASK_STK_SIZE-1],1);
OSTaskCreate(task_one,0,&
TaskStk[8][TASK_STK_SIZE-1],2);
OSStart();
7.使用实时内核的优缺点:
实时内核也称为实时操作系统或RTOS。
它的使用
使得实时应用程序的设计和扩展变得容易,不需要大的改动就可以增加新
的功能。
通过将应用程序分割成若干独立的任务,RTOS使得应用程序的设
计过程大为减化。
使用可剥夺性内核时,所有时间要求苛刻的事件都得到
了尽可能快捷、有效的处理。
通过有效的服务,如信号量、邮箱、队列、
延时、超时等,RTOS使得资源得到更好的利用。
如果应用项目对额外的需
求可以承受,应该考虑使用实时内核。
这些额外的需求是:
内核的价格,
额外的ROM/RAM开销,2到4百分点的CPU额外负荷。
内核结构
1.任务:
一个任务通常是一个无限的循环。
看起来像其它C的函数一样,有
函数返回类型,有形式参数变量,但是任务是绝不会返回的。
故返回参数
必须定义成void。
当任务完成以后任务代码并非真的删除了,μC/OS-?
只
是简单地不再理会这个任务了。
列出一任务:
voidYourTask(void*pdata)
(1)
{
for(;
;
){
(2)
/*用户代码*/
调用uC/OS-II的某种系统服务:
OSMboxPend();
OSQPend();
OSSemPend();
OSTaskDel(OS_PRIO_SELF);
OSTaskSuspend(OS_PRIO_SELF);
OSTimeDly();
OSTimeDlyHMSM();
}
形式参数变量
(1)是由用户代码在第一次执行的时候带入的。
该变量的类型是一个指向void的指针。
这是为了允许用户应用程序传递任何类型的数据给任务。
μC/OS-?
可以管理多达64个任务,但目前版本的μC/OS-?
有两个任务已经被系统占用了。
作者保留了优先级为0、1、2、3、OS_LOWEST_PRIO-3、OS_LOWEST_PRI0-2,OS_LOWEST_PRI0-1以及OS_LOWEST_PRI0这8个任务以被将来使用。
为了使μC/OS-?
能管理用户任务,用户必须在建立一个任务的时候,将任务的起始地址与其它参数一起传给下面两个函数中的一个:
OSTastCreat或OSTaskCreatExt()。
2.任务控制块:
一旦任务建立了,任务控制块OS_TCBs将被赋值。
任务控制
块是一个数据结构,当任务的CPU使用权被剥夺时,μC/OS-?
用它来保存
该任务的状态。
当任务重新得到CPU使用权时,任务控制块能确保任务从
当时被中断的那一点丝毫不差地继续执行。
OS_TCBs全部驻留在RAM中。
µ
C/OS-II任务控制块清单:
typedefstructos_tcb{
OS_STK*OSTCBStkPtr;
//指向当前任务栈顶的指针。
每个任务的栈的
容量可以是//任意的。
#ifOS_TASK_CREATE_EXT_EN
void*OSTCBExtPtr;
//指向用户定义的任务控制块扩展
OS_STK*OSTCBStkBottom;
//是指向任务栈底的指针
INT32UOSTCBStkSize;
//存有栈中可容纳的指针元数目
INT16UOSTCBOpt;
//把“选择项”传给OSTaskCreateExt()
INT16UOSTCBId;
//存储任务的识别码,留给将来扩展用
#endif
//用于任务控制块OS_TCBs的双重链接,该链表在时钟节
structos_tcb*OSTCBNext;
//拍函数OSTimeTick()中使用,用于刷新各个任务的任
structos_tcb*OSTCBPrev;
//务延迟变量.
#if(OS_Q_EN&
&
(OS_MAX_QS>
=2))||OS_MBOX_EN||OS_SEM_EN
OS_EVENT*OSTCBEventPtr;
//指向事件控制块的指针#endif
=2))||OS_MBOX_EN
void*OSTCBMsg;
//是指向传给任务的消息的指针#endif
INT16UOSTCBDly;
INT8UOSTCBStat;
INT8UOSTCBPrio;
INT8UOSTCBX;
INT8UOSTCBY;
INT8UOSTCBBitX;
INT8UOSTCBBitY;
#ifOS_TASK_DEL_EN
BOOLEANOSTCBDelReq;
}OS_TCB;
目前,一个用于空闲任务,另一个用于任务统计(如果OS_TASK_STAT_EN是设为1的)。
在μC/OS-?
初始化的时候,所有任务控制块OS_TCBs被链接成单向空任务链表。
当任务一旦建立,空任务控制块指针OSTCBFreeList指向的任务控制块便赋给了该任务,然后OSTCBFreeList的值调整为指向下链表中下一个空的任务控制块。
一旦任务被删除,任务控制块就还给空任务链表。
3.就绪表(ReadyList):
每个任务被赋予不同的优先级等级,从0级到最
低优先级OS_LOWEST_PR1O。
每个任务的就绪态标志都放入就绪表中的,就
绪表中有两个变量OSRedyGrp和OSRdyTbl[]。
在OSRdyGrp中,任务按优先
级分组,8个任务为一组。
OSRdyGrp中的每一位表示8组任务中每一组中
是否有进入就绪态的任务。
任务进入就绪态时,就绪表OSRdyTbl[]中的相
应元素的相应位也置位。
以下程序清单中的代码用于将任务放入就绪表。
Prio是任务的优先级OSRdyGrp|=OSMapTbl[prio>
>
3];
OSRdyTbl[prio>
3]|=OSMapTbl[prio&
0x07];
如果一个任务被删除了,则用程序清单中的代码做求反处理
if((OSRdyTbl[prio>
3]&
=~OSMapTbl[prio&
0x07])==0)
OSRdyGrp&
=~OSMapTbl[prio>
找出进入就绪态的优先级最高的任务
y=OSUnMapTbl[OSRdyGrp];
x=OSUnMapTbl[OSRdyTbl[y]];
prio=(y<
<
3)+x;
4.任务调度(TaskScheduling):
总是运行进入就绪态任务中优
先级最高的那一个。
确定哪个任务优先级最高,下面该哪个任务运行了的
工作是由调度器(Scheduler)完成的。
任务级的调度是由函数OSSched()
完成的。
中断级的调度是由另一个函数OSIntExt()完成的,OSSched()的所
有代码都属临界段代码。
5.系统初始化OSIint():
OSInit()建立空闲任务idletask,这个任务总是
处于就绪态的。
OSInit()还得建立统计任务OSTaskStat()并且让其进入就
绪态。
以上两个任务的任务控制块(OS_TCBs)是用双向链表链接在一起的。
还初始化了4个空数据结构缓冲区。
6.任务启动OSStart():
多任务的启动是用户通过调用OSStart()实现的。
然而,启动μC/OS-?
之前,用户至少要建立一个应用任务。
当调用OSStart()
时,OSStart()从任务就绪表中找出那个用户建立的优先级最高任务的任务
控制块,然后,OSStart()调用高优先级就绪任务启动函数
任务管理
1.建立任务:
建立任务:
OSTaskCreate()或OSTaskCreateExt()。
OSTaskCreate()与µ
C/OS是向下兼容的,OSTaskCreateExt()是
OSTaskCreate()的扩展版本,提供了一些附加的功能。
任务可以在多任务
调度开始前建立,也可以在其它任务的执行过程中被建立。
在开始多任务
调度(即调用OSStart())前,用户必须建立至少一个任务。
任务不能由中断
服务程序(ISR)来建立。
OSTaskCreate()需要四个参数:
task是任务代码的指针,pdata是当任务开始执行时传递给任务的参数的指针,ptos是分配给任务的堆栈的栈顶指针,prio是分配给任务的优先级。
OSTaskCreate()清单
INT8UOSTaskCreate(void(*task)(void*pd),void*pdata,OS_STK*ptos,
INT8Uprio)
void*psp;
INT8Uerr;
if(prio>
OS_LOWEST_PRIO){//检测分配给任务的优先级是否有效
return(OS_PRIO_INVALID);
OS_ENTER_CRITICAL();
//关中断
if(OSTCBPrioTbl[prio]==(OS_TCB*)0){//确保在规定的优先级上还没有建立任务
OSTCBPrioTbl[prio]=(OS_TCB*)1;
//放置一个非空指针保留该优先级
OS_EXIT_CRITICAL();
//开中断
psp=(void*)OSTaskStkInit(task,pdata,ptos,0);
//建立任务的堆栈
err=OSTCBInit(prio,psp,(void*)0,0,0,(void*)0,0);
//从空闲的OS_TCB池中获得并初始化一个OS_TCB
if(err==OS_NO_ERR){
OSTaskCtr++;
//于保存产生的任务数目
OSTaskCreateHook(OSTCBPrioTbl[prio]);
//户自己定义的函数,用来扩展OSTaskCreate()的功能
if(OSRunning){//如果OSTaskCreate()函数是在某个任务的执行过程中被调用
OSSched();
//调用调度函数
}else{
OSTCBPrioTbl[prio]=(OS_TCB*)0;
//放弃该任务的优先级
return(err);
return(OS_PRIO_EXIST);
用OSTaskCreateExt()函数来建立任务会更加灵活,但会增加一些额外的开销。
其内部结构与OSTaskCreate()大体相同。
2.任务堆栈:
每个任务都有自己的堆栈空间。
堆栈必须声明为OS_STK类型,
并且由连续的内存空间组成。
用户可以静态分配堆栈空间(在编译的时候分
配)也可以动态地分配堆栈空间(在运行的时候分配)。
µ
C/OS-?
支持的处理
器的堆栈既可以从上(高地址)往下(低地址)长也可以从下往上长。
用户在
调用OSTaskCreate()或OSTaskCreateExt()的时候必须知道堆栈是怎样长
的,因为用户必须得把堆栈的栈顶传递给以上两个函数,当OS_CPU.H文件
中的OS_STK_GROWTH置为0时,用户需要将堆栈的最低内存地址传递给任
务创建函数,当OS_CPU.H文件中的OS_STK_GROWTH置为1时,用户需要将
堆栈的最高内存地址传递给任务创建函数。
任务所需的堆栈的容量是由应
用程序指定的。
用户在指定堆栈大小的时候必须考虑用户的任务所调用的
所有函数的嵌套情况,任务所调用的所有函数会分配的局部变量的数目,
以及所有可能的中断服务例程嵌套的堆栈需求。
另外,用户的堆栈必须能
储存所有的CPU寄存器。
3.堆栈检验OSTaskStkChk():
为了使用µ
的堆栈检验功能,用户必须要做以下几件事情:
在OS_CFG.H文件中设OS_TASK_CREATE_EXT为1。
用OSTaskCreateExt()建立任务,并给予任务比实际需要更多的内存空间。
在OSTaskCreateExt()中,将参数opt设置为OS_TASK_OPT_STK_CHK+
OS_TASK_OPT_STK_
CLR。
注意如果用户的程序启动代码清除了所有的RAM,并且从未删除过已
建立了的任务,那么用户就不必设置选项OS_TASK_OPT_STK_CLR了。
这样
就会减少OSTaskCreateExt()的执行时间。
将用户想检验的任务的优先级作为OSTaskStkChk()的参数并调用之。
OSTaskStkChk()顺着堆栈的栈底开始计算空闲的堆栈空间大小。
具体实现方法是统计储存值为0的连续堆栈入口的数目,直到发现储存值不为0的堆栈入口。
用户应该使自己的应用程序运行足够长的时间,并且经历最坏的堆栈使用情况,这样才能得到正确的数。
4.删除任务OSTaskDel():
删除任务,是说任务将返回并处于休眠状态,并不
是说任务的代码被删除了,只是任务的代码不再被µ
调用。
过调用
OSTaskDel()就可以完成删除任务的功能。
删除任务程序清单
INT8UOSTaskDel(INT8Uprio)
OS_TCB*ptcb;
OS_EVENT*pevent;
if(prio==OS_IDLE_PRIO){//确保用户所要删除的任务并非是空闲任务
return(OS_TASK_DEL_IDLE);
=OS_LOWEST_PRIO&
prio!
=OS_PRIO_SELF){
//用户可以删除statistic任务
if(OSIntNesting>
0){//确保用户不是在ISR例程中去试图删除一个任务
return(OS_TASK_DEL_ISR);
if(prio==OS_PRIO_SELF){//任务可以通过指定OS_PRIO_SELF参数来删除自己
Prio=OSTCBCur->
OSTCBPrio;
if((ptcb=OSTCBPrioTbl[prio])!
=(OS_TCB*)0){//保证被删除的任务确实存在
if((OSRdyTbl[ptcb->
OSTCBY]&
=~ptcb->
OSTCBBitX)==0){
OSTCBBitY;
//任务处于就绪表中,它会直接被移除
if((pevent=ptcb->
OSTCBEventPtr)!
=(OS_EVENT*)0){
if((pevent->
OSEventTbl[ptcb->
OSTCBBitX)==0){//如果任务处于邮箱、消息队列或信号量的等待表中,它就从自己所处的表中被移除
pevent->
OSEventGrp&
Ptcb->
OSTCBDly=0;
//将任务的时钟延迟数清零,以确保自己重新允许中断的时候,ISR例程不会使该任务就绪
OSTCBStat=OS_STAT_RDY;
//阻止其它任务或ISR例程让该任务重新开始执行
OSLockNesting++;
//阻止任务调度程序在删除过程中切换到其它的任务中去
//重新允许中断以减少中断的响应时间
OSDummy();
//确保在再次禁止中断之前至少执行了一个调用指令和一个返回指令
OSLockNesting--;
//锁定嵌套计数器减一以重新允许任务调度
OSTaskDelHook(ptcb);
//调用用户自定义的OSTaskDelHook()函数
OSTaskCtr--;
//将指向被删除的任务的OS_TCB的指针指向NULL
if(ptcb->
OSTCBPrev==(OS_TCB*)0){//将任务从OS_TCB双向链表中移除
ptcb->
OSTCBNext->
OSTCBPrev=(OS_TCB*)0;
OSTCBList=ptcb->
OSTCBNext;
OSTCBPrev->
OSTCBNext=ptcb->
OSTCBPrev=ptcb->
OSTCBPrev;
OSTCBNext=OSTCBFreeList;
//OS_TCB返回到空闲OS_TCB表中,并允许其它任务的建立
OSTCBFreeList=ptcb;
//中断服务子程序是否曾使更高优先级的任务处于就绪状态
return(OS_NO_ERR);
return(OS_TASK_DEL_ERR);
5.请求删除任务,OSTaskDelReq():
有时候,如果任务A拥有内存缓冲区或
信号量之类的资源,而任务B想删除该任务,这些资源就可能由于没被释
放而丢失。
在这种情况下,用户可以想法子让拥有这些资源的任务在使用
完资源后,先释放资源,再删除自己。
用户可以通过OSTaskDelReq()函数
来完成该功能。
6.改变任务的优先级OSTaskChangePrio():
为了改变调用本函数的任务的优
先级,用户可以指定该任务当前的优先级或OS_PRIO_SELF,
OSTaskChangePrio()会决定该任务的优先级。
用户还必须指定任务的新(即
想要的)优先级。
因为µ
不允许多个任务具有相同的优先级,所以
OSTaskChangePrio()需要检验新优先级是否是合法的
7.挂起任务OSTaskSuspend():
挂起任务可通过调用OSTaskSuspend()函数
来完成。
被挂起的任务只能通过调用OSTaskResume()函数来恢复。
OSTaskSuspend()要确保用户的应用程序不是在挂起空闲任务
确认用户指定优先级是有效的,记住最大的有效的优先级数是
OS_LOWEST_PRIO
检验用户是否通过指定OS_PRIO_SELF来挂起调用本函数的任务本身
用户也可以通过指定优先级来挂起调用本函数的任务
检验要挂起的任务是否存在
在任务的OS_TCB中设置OS_STAT_SUSPEND标志,以表明任务正在被挂
起
若被挂起的任务是调用本函数的任务本身,调用任务调度程序
任务管理2
1.时钟节拍:
(其它内核也一样)要求用户提供定时中断来实现延时与超时控制等功能。
五个与时钟节拍有关的系统服务:
OSTimeDly()
OSTimeDlyHMSM()
OSTimeDlyResume()
OSTimeGet()
OSTimeSet()
2.任务延时函数OSTimeDly():
调用该函数会使µ
进行一次任务调度,并且执行下一个优先级最高的就绪态任务。
其实现过程如下:
如果用户指定0值,则表明用户不想延时任务,函数会立即返回到调
用者