UCOSII概念及内核.docx
《UCOSII概念及内核.docx》由会员分享,可在线阅读,更多相关《UCOSII概念及内核.docx(12页珍藏版)》请在冰豆网上搜索。
UCOSII概念及内核
UCOS-II概念及内核
实时系统概念
1.有两种类型的实时系统:
软实时系统和硬实时系统。
在软实时系统中系统
的宗旨是使各个任务运行得越快越好,并不要求限定某一任务必须在多长
时间内完成。
在硬实时系统中,各任务不仅要执行无误而且要做到准时。
2.前后台系统:
应用程序是一个无限的循环,循环中调用相应的函数完成相
应的操作,这部分可以看成后台行为(background)。
中断服务程序处理异
步事件,这部分可以看成前台行为(foreground)。
后台也可以叫做任务
级。
前台也叫中断级。
3.可重入型函数:
可重入型函数可以被一个以上的任务调用,而不必担心数
据的破坏。
可重入型函数任何时候都可以被中断,一段时间以后又可以运
行,而相应数据不会丢失。
可重入型函数或者只使用局部变量,即变量保
存在CPU寄存器中或堆栈中。
如果使用全局变量,则要对全局变量予以保
护。
以下技术之一即可使不可重入函数具有可重入性:
把变量定义为局部变量
调用函数之前关中断,调动后再开中断
用信号量禁止该函数在使用过程中被再次调用
4.优先级反转:
应用程序执行过程中,任务的优先级是可变的,则称之为动
态优先级。
实时内核应当避免出现优先级反转问题。
为防止发生优先级反
转,内核能自动变换任务的优先级,这叫做优先级继承(Priority
inheritance)
5.互斥条件:
实现任务间通讯最简便到办法是使用共享数据结构。
特别是当所
有到任务都在一个单一地址空间下,能使用全程变量、指针、缓冲区、链
表、循环缓冲区等,使用共享数据结构通讯就更为容易。
虽然共享数据区
法简化了任务间的信息交换,但是必须保证每个任务在处理共享数据时的
排它性,以避免竞争和数据的破坏。
与共享资源打交道时,使之满足互斥
条件最一般的方法有:
关中断(μ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
#if(OS_Q_EN&&(OS_MAX_QS>=2))||OS_MBOX_EN
void*OSTCBMsg;//是指向传给任务的消息的指针#endif
INT16UOSTCBDly;
INT8UOSTCBStat;
INT8UOSTCBPrio;
INT8UOSTCBX;
INT8UOSTCBY;
INT8UOSTCBBitX;
INT8UOSTCBBitY;
#ifOS_TASK_DEL_EN
BOOLEANOSTCBDelReq;
#endif
}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>>3];
找出进入就绪态的优先级最高的任务
y=OSUnMapTbl[OSRdyGrp];
x=OSUnMapTbl[OSRdyTbl[y]];
prio=(y<<3)+x;
4.任务调度(TaskScheduling):
μC/OS-?
总是运行进入就绪态任务中优
先级最高的那一个。
确定哪个任务优先级最高,下面该哪个任务运行了的
工作是由调度器(Scheduler)完成的。
任务级的调度是由函数OSSched()
完成的。
中断级的调度是由另一个函数OSIntExt()完成的,OSSched()的所
有代码都属临界段代码。
5.系统初始化OSIint():
OSInit()建立空闲任务idletask,这个任务总是
处于就绪态的。
OSInit()还得建立统计任务OSTaskStat()并且让其进入就
绪态。
以上两个任务的任务控制块(OS_TCBs)是用双向链表链接在一起的。
μC/OS-?
还初始化了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){
OS_ENTER_CRITICAL();
OSTaskCtr++;//于保存产生的任务数目
OSTaskCreateHook(OSTCBPrioTbl[prio]);
//户自己定义的函数,用来扩展OSTaskCreate()的功能
OS_EXIT_CRITICAL();
if(OSRunning){//如果OSTaskCreate()函数是在某个任务的执行过程中被调用
OSSched();//调用调度函数
}
}else{
OS_ENTER_CRITICAL();
OSTCBPrioTbl[prio]=(OS_TCB*)0;//放弃该任务的优先级
OS_EXIT_CRITICAL();
}
return(err);
}else{
OS_EXIT_CRITICAL();
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():
为了使用µC/OS-?
的堆栈检验功能,用户必须要做以下几件事情:
在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():
删除任务,是说任务将返回并处于休眠状态,并不
是说任务的代码被删除了,只是任务的代码不再被µC/OS-?
调用。
过调用
OSTaskDel()就可以完成删除任务的功能。
删除任务程序清单
INT8UOSTaskDel(INT8Uprio)
{
OS_TCB*ptcb;
OS_EVENT*pevent;
if(prio==OS_IDLE_PRIO){//确保用户所要删除的任务并非是空闲任务
return(OS_TASK_DEL_IDLE);
}
if(prio>=OS_LOWEST_PRIO&&prio!
=OS_PRIO_SELF){
//用户可以删除statistic任务
return(OS_PRIO_INVALID);
}
OS_ENTER_CRITICAL();
if(OSIntNesting>0){//确保用户不是在ISR例程中去试图删除一个任务
OS_EXIT_CRITICAL();
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){
OSRdyGrp&=~ptcb->OSTCBBitY;//任务处于就绪表中,它会直接被移除
}
if((pevent=ptcb->OSTCBEventPtr)!
=(OS_EVENT*)0){
if((pevent->OSEventTbl[ptcb->OSTCBY]&=~ptcb->OSTCBBitX)==0){//如果任务处于邮箱、消息队列或信号量的等待表中,它就从自己所处的表中被移除
pevent->OSEventGrp&=~ptcb->OSTCBBitY;
}
}
Ptcb->OSTCBDly=0;
//将任务的时钟延迟数清零,以确保自己重新允许中断的时候,ISR例程不会使该任务就绪
Ptcb->OSTCBStat=OS_STAT_RDY;//阻止其它任务或ISR例程让该任务重新开始执行
OSLockNesting++;//阻止任务调度程序在删除过程中切换到其它的任务中去
OS_EXIT_CRITICAL();//重新允许中断以减少中断的响应时间
OSDummy();//确保在再次禁止中断之前至少执行了一个调用指令和一个返回指令
OS_ENTER_CRITICAL();
OSLockNesting--;//锁定嵌套计数器减一以重新允许任务调度
OSTaskDelHook(ptcb);//调用用户自定义的OSTaskDelHook()函数
OSTaskCtr--;
OSTCBPrioTbl[prio]=(OS_TCB*)0;//将指向被删除的任务的OS_TCB的指针指向NULL
if(ptcb->OSTCBPrev==(OS_TCB*)0){//将任务从OS_TCB双向链表中移除
ptcb->OSTCBNext->OSTCBPrev=(OS_TCB*)0;
OSTCBList=ptcb->OSTCBNext;
}else{
ptcb->OSTCBPrev->OSTCBNext=ptcb->OSTCBNext;
ptcb->OSTCBNext->OSTCBPrev=ptcb->OSTCBPrev;
}
ptcb->OSTCBNext=OSTCBFreeList;
//OS_TCB返回到空闲OS_TCB表中,并允许其它任务的建立
OSTCBFreeList=ptcb;
OS_EXIT_CRITICAL();
OSSched();//中断服务子程序是否曾使更高优先级的任务处于就绪状态
return(OS_NO_ERR);
}else{
OS_EXIT_CRITICAL();
return(OS_TASK_DEL_ERR);
}
}
5.请求删除任务,OSTaskDelReq():
有时候,如果任务A拥有内存缓冲区或
信号量之类的资源,而任务B想删除该任务,这些资源就可能由于没被释
放而丢失。
在这种情况下,用户可以想法子让拥有这些资源的任务在使用
完资源后,先释放资源,再删除自己。
用户可以通过OSTaskDelReq()函数
来完成该功能。
6.改变任务的优先级OSTaskChangePrio():
为了改变调用本函数的任务的优
先级,用户可以指定该任务当前的优先级或OS_PRIO_SELF,
OSTaskChangePrio()会决定该任务的优先级。
用户还必须指定任务的新(即
想要的)优先级。
因为µC/OS-?
不允许多个任务具有相同的优先级,所以
OSTaskChangePrio()需要检验新优先级是否是合法的
7.挂起任务OSTaskSuspend():
挂起任务可通过调用OSTaskSuspend()函数
来完成。
被挂起的任务只能通过调用OSTaskResume()函数来恢复。
OSTaskSuspend()要确保用户的应用程序不是在挂起空闲任务
确认用户指定优先级是有效的,记住最大的有效的优先级数是
OS_LOWEST_PRIO
检验用户是否通过指定OS_PRIO_SELF来挂起调用本函数的任务本身
用户也可以通过指定优先级来挂起调用本函数的任务
检验要挂起的任务是否存在
在任务的OS_TCB中设置OS_STAT_SUSPEND标志,以表明任务正在被挂
起
若被挂起的任务是调用本函数的任务本身,调用任务调度程序
任务管理2
1.时钟节拍:
µC/OS-?
(其它内核也一样)要求用户提供定时中断来实现延时与超时控制等功能。
五个与时钟节拍有关的系统服务:
OSTimeDly()
OSTimeDlyHMSM()
OSTimeDlyResume()
OSTimeGet()
OSTimeSet()
2.任务延时函数OSTimeDly():
调用该函数会使µC/OS-?
进行一次任务调度,并且执行下一个优先级最高的就绪态任务。
其实现过程如下:
如果用户指定0值,则表明用户不想延时任务,函数会立即返回到调
用者