uCOS-II的移植及使用_第1页
uCOS-II的移植及使用_第2页
uCOS-II的移植及使用_第3页
uCOS-II的移植及使用_第4页
uCOS-II的移植及使用_第5页
已阅读5页,还剩145页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

uCOS-II的移植及使用报告人:日期:2016.1.181425uC/OS-II的任务3uC/OS-II的移植uC/OS-II同步与通信uC/OS-II概述uC/OS-III简介目录uC/OS-II概述C/OS——MicroControllerOS,微控制器操作系统

美国人JeanLabrosse1992年完成应用面覆盖了诸多领域,如照相机、医疗器械、音响设备、发动机控制、高速公路电话系统、自动提款机等1998年出C/OS-II,目前的版本C/OS-IIV2.912000年,得到美国航空管理局(FAA)的认证,可以用于飞行器中网站uC/OS-II概述-性能特点源代码公开可移植(Portable)大部分代码用ANSIC写,与处理器无关,移植时不需修改少量与微处理器硬件相关的部分用C与汇编编写,移植时需修改:OS_CPU.H//与硬件相关,移植时需修改OS_CPU_A.ASM//集中了所有与处理器相关的汇编语言代码OS_CPU.C//集中了所有与处理器相关的汇编语言代码uC/OS-II概述-性能特点可裁剪(Scalable)可以只使用

C/OS-II中应用程序需要的那些系统服务。也就是说某产品可以只使用很少几个

C/OS-II调用,而另一个产品则使用了几乎所有

C/OS-II的功能,这样可以减少产品中的

C/OS-II所需的存储器空间(RAM和ROM)。可剪裁性通过条件编译实现。uC/OS-II概述-性能特点可剥夺性(Preemptive)与可确定性内核可剥夺、函数调用或系统服务的执行时间具有可确定性,是硬实时操作系统。支持多任务C/OS-II可以管理255个任务任务栈每个任务有自己单独的栈,

C/OS-II允许每个任务有不同的栈空间,以便压低应用程序对RAM的需求。uC/OS-II概述-性能特点系统服务C/OS-II提供很多系统服务,例如邮箱、消息队列、信号量、块大小固定的内存的申请与释放、时间相关函数等。中断管理中断可以使正在执行的任务暂时挂起,如果优先级更高的任务被该中断唤醒,则高优先级的任务在中断嵌套全部退出后立即执行,中断嵌套层数可达255层。uC/OS-II概述--文件结构体系结构1425uC/OS-II的任务3uC/OS-II的移植uC/OS-II同步与通信uC/OS-II概述uC/OS-III简介目录uC/OS-II的任务2.1任务组成部分2.2任务的结构2.3任务存储结构2.4任务状态2.5任务优先级2.6任务就绪表及任务调度2.7任务操作2.8uC/OS-II的初始化一个完整的任务应该有如下三部分:任务代码(程序)任务的私有堆栈(用以保护运行环境)任务控制块(提供私有堆栈也是虚拟处理器的位置)2.1uC/OS-II的任务—组成前一个任务控制块的指针后一个任务控制块的指针指向任务的指针指向任务堆栈的指针任务的优先级别……voidmytask(void*pdata){……for(;;){……}}

任务的代码

任务控制块……

任务堆栈

任务图2-4任务的组成从应用程序设计的角度来看,任务就是一个线程。就是一个用来解决用户问题的C语言函数和与之相关联的一些数据结构而构成的一个实体从任务存储结构来看,由三部分构成:任务程序代码、任务堆栈和任务控制块。任务控制块用来保存任务属性,任务堆栈用来保存任务工作环境,任务程序代码是任务的执行部分2.1uC/OS-II的任务—组成uCOS中的任务是一个线程,其代码通常是一个无限循环结构/超循环结构,看起来像其它C函数一样。2.1.1uC/OS-II的任务—代码结构voidMyTask(void*pdata) { for(;;) {

可以被中断的任务代码;

OS_ENTER_CRITICAL(); //进入临阶段(关中断) 不可以被中断的任务代码;

OS_EXIT_CRITICAL(); //退出临阶段(开中断)

可以被中断的任务代码;

}}根据嵌入式系统任务的工作特点,任务的执行代码通常是一个无限循环结构,并且在这个循环中可以响应中断,这种结构也叫超循环结构。为了有效的对中断进行控制,在任务的代码里可使用UC/OS-II定义的宏OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL()来控制何时响应中断,何时屏蔽中断。在运行这两个宏之间的代码时是不会响应中断的,这种受保护的代码段叫临界段。一旦任务建立,一个任务控制块OS_TCB就被赋值。任务控制块是一个数据结构,保存该任务的相关参数,包括任务堆栈指针,任务的当前状态,任务的优先级等。任务CPU使用权被剥夺时,TCB保存该时刻任务状态;任务重新得到CPU控制权时,TCB确保任务从当时被中断的那一点丝毫不差地继续执行。OS_TCB全部驻留在RAM中。任务控制块就相当于是一个任务的身份证,没有任务控制块的任务是不能被系统承认和管理的。

2.1.2uC/OS-II的任务—任务控制块(TCB)任务控制块的结构typedefstructos_tcb{OS_STK*OSTCBStkPtr;//指向当前任务堆栈栈顶的指针。每个任务的堆栈容量可以是任意的。#ifOS_TASK_CREATE_EXT_ENvoid*OSTCBExtPtr;//指向任务控制块扩展的指针,扩展功能:任务执行时间、任务切换次数、任务堆栈使用情况等。OS_STK*OSTCBStkBottom;//指向当前任务堆栈栈底的指针。INT32UOSTCBStkSize;//任务堆栈的长度INT16UOSTCBOpt;//创建任务时的选择项INT16UOSTCBId;//用于存储任务的识别码。保留。#endifstructos_tcb*OSTCBNext;//指向后一个任务控制块的指针structos_tcb*OSTCBPrev;//指向前一个任务控制块的指针#if(OS_Q_EN&&(OS_MAX_QS>=2))||OS_MBOX_EN||OS_SEM_ENOS_EVENT*OSTCBEventPtr;//指向事件控制块的指针#endif#if(OS_Q_EN&&(OS_MAX_QS>=2))||OS_MBOX_ENvoid*OSTCBMsg;//指向传递给任务消息的指针#endifINT16UOSTCBDly;//任务等待的时限INT8UOSTCBStat;//任务的当前状态标志

INT8UOSTCBPrio;//任务的优先级别INT8UOSTCBX;//用于快速访问就绪表的数据INT8UOSTCBY;//用于快速访问就绪表的数据INT8UOSTCBBitX;//用于快速访问就绪表的数据INT8UOSTCBBitY;//用于快速访问就绪表的数据#ifOS_TASK_DEL_EN

BOOLEANOSTCBDelReq;//请求删除任务时用到的标志

#endif}OS_TCB;任务控制块结构的主要成员typedefstructos_tcb{OS_STK*OSTCBStkPtr;//指向任务堆栈栈顶的指针

……

structos_tcb*OSTCBNext;//指向后一个任务控制块的指针

structos_tcb*OSTCBPrev;//指向前一个任务控制块的指针

……INT16U OSTCBDly; //任务等待的时限(节拍数)

INT8U OSTCBStat; //任务的当前状态标志

INT8U OSTCBPrio; //任务的优先级别

……}

OS_TCB;任务控制块的初始化当用户程序调用函数OSTaskCreate()创建一个任务时,这个函数会调用系统函数OSTCBInit()来为任务控制块进行初始化。这个函数首先为被创建任务从空任务控制块链表获取一个任务控制块,然后利用任务的属性对任务控制块各个成员进行赋值,最后再把这个任务控制块链入到任务控制块链表的头部。初始化任务控制块函数OSTCBInit()的原型如下:INT8UOSTCBInit(){ INT8U Prio, //任务的优先级别,保存在OSTCBPrio中

OS_STK *ptos, //任务堆栈栈顶指针,保存在OSTCBStkPtr中

OS_STK*pbos, //任务堆栈栈底指针,保存在OSTCBStkBottom中

INT16U id, //任务的标示符,保存在OSTCBId中

INT16U stk_size, //任务堆栈的长度,保存在OSTCBStkSize中

void *pext, //任务控制块的扩展指针,保存在OSTCBExtPtr中

INT16U opt //任务的控制块的选择项,保存在OSTCBOpt中};所谓堆栈,就是在存储器中按数据“后进先出(LIFO)”的原则组织的连续存储空间。为了满足任务切换和响应中断时保存CPU寄存器中的内容及存储任务私有数据的需要,每个任务都应该配有自己的堆栈。任务堆栈是任务的重要组成部分。2.1.3uC/OS-II的任务—任务堆栈任务堆栈的创建为方便定义任务堆栈,在文件OS_CPU.H中专门定义了一个数据类型OS_STK:TypedefunsignedintOS_STK;

//该类型长度为16位这样,在应用程序中定义任务堆栈的栈区就非常简单,即定义一个OS_STK类型的一个数组即可。例如:#defineTASK_STK_SIZE512//定义堆栈长度(1024字节)OS_STKTaskStk[TASK_STK_SIZE];//定义一个数组来作为任务堆栈

创建任务函数OSTaskCreate()原型如下:

INT8UOSTaskCreate( void(*task)(void*pd), //指向任务的指针

void*pdata, //传递给任务的参数

OS_STK*ptos, //任务堆栈栈顶的指针

INT8Uprio); //指定任务优先级别的参数

当调用函数OSTaskCreate()来创建一个任务时,把数组的指针传递给函数OSTaskCreate()中的堆栈栈顶参数ptos,就可以把该数组与任务关联起来而成为该任务的任务堆栈。任务堆栈的创建堆栈的增长方向是随系统所使用的处理器不同而不同。因此在使用函数OSTaskCreate()创建任务时,一定要注意所使用的处理器对堆栈增长方向的支持是向上的还是向下的。低地址高地址堆栈增长的方向向下堆栈增长的方向向上图3-7堆栈的不同增长方向上一练习时假设使用了支持堆栈向下增长方式的处理器设置参数ptos。如果使用的处理器支持堆栈的增长方向向上,则应该为: OSTaskCreate(MyTask,&ttt,&MyTaskStk[0],20);为了提高应用程序的移植性,在编写代码时可兼顾两种情况,利用OS_CFG.H文件中的常数OS_STK_GROWTH作为选择开关,使用户可通过定义该常数的值来选择相应代码段,以适应不同的堆栈增长方向的需要。试改写上一练习代码。任务堆栈的初始化

uC/OS-II在创建任务函数OSTaskCreat()中通过调用任务堆栈初始化函数OSTaskStkInit()来完成任务堆栈初始化工作的,其原型如下:

OS_STK*OSTaskStkInit(

void(*task)(void*pd),

void*pdata,

OS_STK*ptos,

INIT16Uopt

);

由于各处理器的寄存器及对堆栈的操作方式不同,因此该函数需要用户在进行uC/OS-II的移植时,按所使用的处理器由用户来编写。当CPU启动运行一个任务时,CPU的各寄存器总是需要预置一些初始数据,例如指向任务的指针、任务堆栈指针、程序状态字等。#defineOS_TASK_OPT_STK_CHK0x0001/*Enablestackcheckingforthetask*/#defineOS_TASK_OPT_STK_CLR0x0002/*Clearthestackwhenthetaskiscreate*/#defineOS_TASK_OPT_SAVE_FP0x0004/*Savethecontentsofanyfloating-pointregisters*/最方便的方法就是让CPU从这个任务的任务堆栈里获得这些数据!任务堆栈voidmytask(){…..

for(;;){…..}}任务代码任务控制块指向任务堆栈的指针…..指向前一个任务控制块的指针指向后一个任务控制块的指针……..任务的优先级别……任务2.2uC/OS-II的任务--存储结构指向任务代码任务控制块任务代码任务堆栈任务1……任务控制块链表任务控制块任务代码任务堆栈任务2任务控制块任务代码任务堆栈任务n2.2uC/OS-II的任务--存储结构uC/OS-II的任务有5种状态睡眠态(DORMANT):任务驻留在程序空间,还没有交给uCOS管理,即还没有配备任务控制块,还没有被创建。就绪态(READY):

任务一旦建立,就进入就绪态准备运行,“万事具备,只欠CPU”。运行态(RUNNING):正在使用CPU的状态称运行态。等待态(WAITING):等待某事件发生的状态.中断服务态(ISR):正在运行的任务被中断时就进入了中断服务态(ISR)。2.3uC/OS-II的任务—工作状态正在运行的任务,需要等待一段时间或需要等待一个事件发生再运行时,该任务就会把CPU的使用权让给别的任务而使任务进入等待状态。任务在没有被配备任务控制块或被剥夺了任务控制块时的状态叫做任务的睡眠状态

系统为任务配备了任务控制块且在任务就绪表中进行了就绪登记,这时任务的状态叫做就绪状态。

处于就绪状态的任务如果经调度器判断获得了CPU的使用权,则任务就进入运行状态

一个正在运行的任务一旦响应中断申请就会中止运行而去执行中断服务程序,这时任务的状态叫做中断服务状态

COSII(V2.91版)

支持255个任务,每个任务有一个特定的优先级。任务的优先级别用数字表示,0表示的任务的优先级最高,数字越大表示的优先级越低。通过常数OS_LOWEST_PRIO(在OS_CFG.H中)定义系统的最低优先级别,同时限定系统能容纳的最多任务数量。OS_LOWEST_PRIO给空闲任务,OS_LOWEST_PRIO-1给统计任务。2.4uC/OS-II的任务—优先级

多任务操作系统的核心:任务调度。调度定义:就是通过一个算法在多个任务中确定那个任务来运行。做这项工作的就是调度器。任务调度的思想:总是让优先级最高的就绪任务处于运行状态。

uC/OS-I进行任务调度的依据:任务就续表。为了能使系统清楚地知道系统中哪些任务已经就绪,那些还没有就绪,UC/OS-II在RAM中设立了一个记录表,系统中的每个任务都在这个表占据一个位置,并用这个位置的状态(1或者0)来表示任务是否处于就绪状态。这个表就叫做任务就绪表。2.5uC/OS-II的任务—任务就绪表及调度每个就绪的任务都放入就绪表中,就绪表有两个变量:OSRdyGrp、OSRdyTbl[]OSRdyGrp1207654300XXXYYY任务优先级2017654310891514131211181617232221201926242531302928273432333938373635424041474645444350484955545352515856576362616059[0][1][2][3][4][5][6][7]XY优先级最低任务优先级最高任务任务优先级号(idletask)任务就绪表的结构图2-10就绪表的结构由于每个任务的就绪状态只占一位,因此OSRdyTbl[]数组的一个元素可表达8个任务的就绪状态。即每一个数组元素描述了8个任务的就绪状态,于是这8个任务就可以看成一个任务组。数据类型为INT8U的变量OSRdyGrp,该变量的每一个位都对应OSRdyTbl[]的一个任务组(即数组的一个元素)。任务就绪表的结构如何根据任务的优先级别查找任务在就绪表的位置呢?例:已知某一个已经就绪的任务优先级别为prio=30,试判断应该在就绪表的哪一位置上置1。分析:由于优先级别是一个单字节的数字,而且最大值不会超过63,即二进制00111111,因此,可以把优先级别看成是一个6位的二进制数,这样就可以用高3位(D5、D4、D3)来指明变量OSRdyGrp的具体数据位,并用来确定就绪表数组元素的下标;用低3位(D2、D1、D0)来指明该数组元素的具体数据位。答:30的二进制形式为00011110,其低6位为011110,于是可知应该在OSRdyTbl[3]的D6位上置1,同时要把变量OSRdyGrp的D3位置1。对任务就绪表的操作在程序中,可用类似于下面的代码把优先级为prio的任务置为就绪态。OSRdyGrp|=OSMapTbl[prio>>3];OSRdyTbl[prio>>3]|=OSMapTbl[prio&0x07];其中,OSMapTbl[]是uC/OS-II为加快运算速度定义的一个数组,各元素为: OSMapTbl[0]=00000001B OSMapTbl[1]=00000010B OSMapTbl[2]=00000100B OSMapTbl[3]=00001000B OSMapTbl[4]=00010000B OSMapTbl[5]=00100000B OSMapTbl[6]=01000000B OSMapTbl[7]=10000000B如果要使一个优先级为prio的任务脱离就绪态,则可使用如下代码:If((OSRdyTbl[prio>>3]&=OSMapTbl[prio&0x07])==0)OSRdyGrp&=OSMapTbl[prio>>3];将任务就绪表OSRdyTbl[prio>>3]相应元素的相应位清零,而且当OSRdyTbl[prio>>3]中的所有位都为零时,即全组任务中没有一个进入就绪态时,OSRdyGrp的相应位才为零。对任务就绪表的操作(续)例:欲使优先级为12的任务进入就绪态,设置就绪表。答:优先级为12,即00001100B。OSRdyGrp|=OSMapTbl[prio>>3]=OSMapTbl[1]=00000010;OSRdyTbl[1]|=OSMapTbl[prio&0x07]=OSMapTbl[4]=00010000;例:欲使优先级为12的任务脱离就绪态,设置就绪表。答:优先级为12,即00001100B。OSMapTbl[prio&0x07]=OSMapTbl[4]=11101111OSMapTbl[prio>>3]=OSMapTbl[1]=11111101由于OSRdyTbl[prio>>3]&=OSMapTbl[prio&0x07]=0

则OSRdyGrp=OSRdyGrp&OSMapTbl[prio>>3]=0从任务就绪表中获取优先级别最高的就绪任务可用如下代码:采用查表法确定高优先级任务Y=OSUnMapTbl[OSRdyGrp]; //获得优先级别的D5、D4、D3位X=OSUnMapTbl[OSRdyTbl[Y]];//获得优先级别的D2、D1、D0位Prio=(Y<<3)+X; //获得就绪任务的优先级别其中,OSUnMapTbl[]同样是uC/OS-II为加快运算速度定义的一个数组,共有256个元素:INT8UconstOSUnMapTbl[]={0,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,6,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,7,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,6,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0};举例:如OSRdyGrp的值为01101000B,即104,则查得OSUnMapTbl[OSRdyGrp]的值是3。如OSRdyTbl[3]的值是11100100B,即228,则查OSUnMapTbl[OSRdyTbl[3]]的值是2。则进入就绪态的最高任务优先级。

Prio=3*8+2=26

UC/OS-II经常使用类似于就绪表的形式来记录任务的某种状态,因此一定要熟悉这种表的结构以及对这种表的基本操作。任务切换:在多任务系统中,令CPU中止当前正在运行的任务而去运行另一个任务的工作。任务调度:按某种规则进行任务切换的工作。任务调度由任务调度器完成,调度器主要工作:

1.在任务就绪表中查找具有最高优先级别的就绪任务。

2.实现任务切换。uC/OS-II有两种调度器:

任务级调度器(由OSSched()实现)、中断级调度器(由OSIntExt()实现)。任务切换的两个步骤:

1.获得待运行任务的TCB指针。

2.进行断点数据的切换。任务的调度任务切换过程获得待运行任务的任务控制块恢复待运行任务的运行环境处理器的PC=任务堆栈中的断点地址处理器的SP=任务块中保存的SP如何获得待运行任务的任务控制块?根据就绪表获得待运行任务的任务控制块指针其实,调度器在进行调度时,在这个位置还要进行一下判断:究竟是待运行任务是否为当前任务,如果是,则不切换;如果不是才切换,而且还要保存被中止任务的运行环境。任务代码任务堆栈内存处理器PCSP处理器通过两个指针寄存器(PC和SP)来与任务代码和任务堆栈建立联系并运行它寄存器组程序运行环境运行环境包括了两部分:处理器中的运行环境和内存中的运行环境任务运行时与处理器之间的关系任务代码任务堆栈内存处理器PCSP多任务时的问题任务代码任务堆栈内存任务代码任务堆栈内存?当有多个任务时,处理器中的运行环境应该怎么办?寄存器组程序运行环境程序

虚拟处理器PCSP

虚拟处理器PCSP

虚拟处理器PCSP

虚拟处理器PCSP调度器多任务时任务与处理器之间关系的处理程序处理器PCSP在内存中为每个任务创建一个虚拟的处理器(处理器部分的运行环境由操作系统的调度器按某种规则来进行这两个复制工作复制当需要运行某个任务时就把该任务的虚拟处理器复制到实际处理器中复制当需要中止当前任务时,则把任务对应的虚拟处理器复制到内存复制再把另一个需要运行的任务的虚拟处理器复制到实际处理器中寄存器组寄存器组也就是说,任务的切换是任务运行环境的切换任务切换宏OS_TASK_SW()

任务切换就是中止正在运行的任务(当前任务),转而去运行另外一个任务的操作,当然这个任务应该是就绪任务中优先级别最高的那个任务。需要由宏OS_TASK_SW()

来引发一次中断或者一次调用来使OSCtxSw()执行任务切换工作,任务切换OSCtxSw()是一个中断服务程序。任务的调度(续)1.获得待运行任务的TCB指针voidOSSched(void){INT8Uy;OS_ENTER_CRITICAL();if((OSLockNesting|OSIntNesting)==0)//未被上锁且不是中断服务程序调用

{

y=OSUnMapTbl[OSRdyGrp]; OSPrioHighRdy=(INT8U)((y<<3)+OSUnMapTbl[OSRdyTbl[y]]);//得到最高优先级任务

if(OSPrioHighRdy!=OSPrioCur)//不是当前运行任务

{

OSTCBHighRdy=OSTCBPrioTbl[OSPrioHighRdy];//得到待运行任务控制块指针OSCtxSwCtr++;//统计任务切换次数的计数器加1OS_TASK_SW();//实施任务切换

}}OS_EXIT_CRITICAL();}由于操作系统是通过任务的任务控制块TCB来管理任务的,因此调度器真正实施任务切换之前的主要工作是要获得待运行任务的任务控制块指针和当前任务的任务控制块指针。任务的调度(续)2.任务切换宏OS_TASK_SW()SPR1R2……RnPCPSWR1R2……RnPCPSWR1R2……RnPCPSWCPU的各寄存器被中止任务TCB待运行任务TCBOSTCBStkPtrOSTCBStkPtr被中止任务堆栈待运行任务堆栈图2-11调度器进行任务切换时的动作断点:任务被中止运行时的位置。断点数据:当时存放在CPU的各寄存器中的数据。“无缝”接续:当任务恢复运行时,必须在断点处以断点数据作为初始数据接着运行。正确地恢复断点数据的关键:CPU的堆栈指针SP是否有正确的指向。在系统中存在多个任务时,如果在恢复断点数据使用另一个任务的任务堆栈指针(存放在TCB的OSTCBStkPtr中)来改变CPU的堆栈指针SP,那么CPU运行的就不是刚才被中止运行的任务,而使另一个任务了,也就是实现任务切换了。为了了解调度器是如何进行任务切换的,探讨一下一个被中止运行的任务,将来又要“无缝”地恢复运行应该满足的条件2.7任务的操作—创建uC/OS-II是通过任务控制块来管理任务的。

uC/OS-II有两个用来创建任务的函数:TaskCreate()、OSTaskCreateExt()OSTaskCreate()向下兼容,OSTaskCreateExt()是前一函数的扩展,提供了一些附加功能。任务可以在多任务调度开始前建立,也可以在其他任务执行过程中建立。任务不能由中断服务程序建立。创建任务的一般方法

可在调用函数OSStart()启动任务调度之前来创建。在任务中来创建。

uC/OS-II的规定:在OSStart()启动任务调度之前必须创建至少一个任务。这样,在OSStart()之前先创建一个任务,并赋予它最高的优先级,从而使它成为起始任务,然后在这个起始任务中,在创建其它各任务。如果要使用系统提供的统计任务,则统计任务的初始化函数必须在这个起始任务中来调用。

uC/OS-II不允许在中断服务程序中创建任务!创建任务的一般方法/*****************************主函数**********************************/voidmain(void){

…… OSInit(); //对uC/OS-II初始化

…… OSTaskCreate(TaskStart,……); //创建起始任务

OSStart(); //开始多任务调度}/*****************************起始任务*********************************/voidTaskStart(void*pdata){

…… //在这个位置安装并启动uC/OS-II时钟

OSStatInit(); //初始化统计任务

…… //在这个位置创建其它任务

For(;;) {

起始任务的代码

}}

所谓挂起一个任务,就是停止这个任务的运行。在uC/OS-II中,用户任务可通过调用系统提供的函数OSTaskSuspend()来挂起自身或者除空闲任务之外的其他任务。用函数OSTaskSuspend()挂起的任务,只能在其它任务中通过调用恢复函数OSTaskResume()使其恢复为就绪状态。等待状态就绪状态运行状态OS_TASK_SW()OSTaskResume()OSTaskSuspend()

任务的CPU使用权被剥夺图任务的挂起和恢复2.7任务的操作—挂起与恢复

删除任务,就是把该任务处于休眠状态。并不是说任务的代码真的被删除了,只是任务的代码不再被操作系统调用。通过调用OSTaskDel()就可以完成删除任务自身或除了空闲任务之外的其他任务。

函数OSTaskDel()的原型如下:

#ifOS_TASK_DEL_ENINT8UOSTaskDel( INT8Uprio //要删除任务的优先级

);

如果任务删除自己,则应在调用函数时令函数的参数prio为OS_PRIO_SELF。

删除任务具体做法是,把被删除任务的任务控制块从任务控制块链表中删除,并归还给空任务控制块链表,然后在任务就绪表中把该任务的就绪状态位置0,于是该任务就不能再被调度器调用了。2.7任务的操作—删除请求删除任务函数OSTaskDelReq()的原型如下:

INT8UOSTaskDelReq( INT8Uprio //要删除任务的优先级

);

如果任务请求删除自己,则应在调用函数时令函数的参数prio为OS_PRIO_SELF。

进入Prio是空闲任务?

返回OS_TASK_NOT_EXIST

返回OS_TASK_IDLE_PRIO返回被删除任务TCB的成员OSTCBDelReq的值NOYESYESYES

返回OS_NO_ERRNOPrio是合法优先级?

返回OS_PRIO_INVALIDNOPrio=OS_PRIO_SELF?Prio任务的TCB不存在?NOYES图2-15函数OSTaskDelReq()流程图提出删除请求任务的执行路径被删除任务的执行路径有时任务会占用一些动态分配的资源,这时如果有其他任务把这个任务删除了,那么被删除任务所占用的一些资源就会因为没有被释放而丢失,这是任何系统都无法接受的。具体办法是:提出删除任务请求的任务只负责删除任务请求,而删除工作则由被删除任务自己来完成。这样,被删除任务就可以根据自身的具体情况来决定何时删除自身,同时也有机会在删除自身之前把占用的资源释放掉。2.7任务的操作—请求删除任务在用户建立任务的时候会分配给任务一个优先级。在程序运行期间,用户可以通过调用OSTaskChangePrio()来改变任务的优先级。换句话说,就是µC/OS-Ⅱ允许用户动态的改变任务的优先级。函数OSTaskChangePrio()的原型如下:

INT8UOSTaskChangePrio( INT8Uoldprio, //任务现在的优先级

INT8Unewprio//要修改的优先级

);

2.7任务的操作—改变任务优先级INT8UOSTaskQuery( INT8Uprio, //待查询任务的优先级

OS_TCB*pdata //存储任务信息的结构

);函数OSTaskQuery()的原型如下:

有时,在应用程序运行中需要了解一个任务的指针、堆栈等信息,这时可通过调用函数OSTaskQuery()来获取选定的任务的信息。若调用函数OSTaskQuery()查询成功,则返回OS_NO_ERR,并把查询得到的任务信息存放在结构OS_TCB类型的变量中。2.7任务的操作—查询任务信息系统首先调用初始化函数OSIint()。OSIint()初始化μC/OS-Ⅱ所有的变量和数据结构。OSInit()建立空闲任务idletask,这个任务总是处于就绪态的。空闲任务OSTaskIdle()的优先级总是设成最低,即OS_LOWEST_PRIO。如果统计任务允许OS_TASK_STAT_EN和任务建立扩展允许都设为1,则OSInit()还得建立统计任务OSTaskStat()并且让其进入就绪态。OSTaskStat的优先级总是设为OS_LOWEST_PRIO-1。

初始化时主要是创建包括空任务控制块链表在内的5个空数据缓冲区,及按任务优先级存放任务控制块指针的数组OSTCBPrioTbl[OS_LOWEST_PRIO+1]。2.8uC/OS-II的初始化uC/OS-II初始化后的数据结构000000000000000000000000000000000000000000000000000000001100000000000001图2-16uC/OS-II初始化后的数据结构任务就绪表

OSRdyGrp任务就绪表

OSRdyTbl[]●●[OS_LOWEST_PRIO-1][OS_LOWEST_PRIO]OSTCBPrioTbl[][0][1][2]

空任务控制块

空任务控制块

空任务控制块……OSTCBStkBottom……OSTCBStkPtr…………OSTCBStkBottom……OSTCBStkPtr……统计任务的控制块空闲任务的控制块OSTCBFreeList

任务控制块链表OSTCBList

统计任务堆栈

空闲任务堆栈

其余4个链表1425uC/OS-II的任务3uC/OS-II的移植uC/OS-II同步与通信uC/OS-II概述uC/OS-III简介目录uC/OS-II的移植3.1基于Cortex-M3内核的移植3.2基于ARM7内核的移植μC/OS-II移植应用程序(用户代码)μC/OS-II(与处理器无关代码)OS_CORE.cOS_FLAG.c...μC/OS-II配置(与应用无关)OS_CFG.HINCLUDES.H

μC/OS-II移植(与处理器相关代码)CPU定时器硬件软件——μC/OS-II硬件软件体系结构用于产生系统时钟移植时需要编写的代码54User目录Main.CMain.HIncludes.HOS_Cfg.HµC/OS-II

Source目录OS_CORE.COS_FLAG.COS_MBOX.COS_MEM.COS_MUTEX.COS_Q.COS_SEM.COS_TASK.COS_TIME.COS_TMR.CuCOS_II.HµC/OS-II

Ports目录CortexM3

OS_CPU_C.COS_CPU_A.ASMOS_CPU.HBSP(板级支持包)Startup.STarget.CTarget.H

ARMCortex-M3

目标板与处理器无关操作系统内核代码与处理器有关需要移植代码开发板相关代码3.1基于ARMCortex-M3的移植55内核文件移植的部分用户代码配置代码主任务56ARMCortex-M3的寄存器模型在移植之前,先简述ARMCortex-M3寄存器模型。ARMCortex-M3总共有20个寄存器,每个都是32位宽度。R0-R12通用寄存器,可存储数据也可存放指针R13用于存放堆栈指针。实际上有两个堆栈指针SP_process(进程堆栈)和

SP_main(主堆栈),但任何时候只有一个是可见的。在本移植中,

SP_process用于任务代码(即线程模式),SP_main用于异常代码(即处理模式)。R14连接寄存器LR。在执行分支链接指令(BL)或带交换的分支链接指令(BLX)时,存储来自PC的返回地址;也用作异常的返回。R15程序计数寄存器PC。用于指示当前正被执行的指令。根据不同的指令,每执行一条,PC增加2或增加4。5758状态中断59控制寄存器603.1.1移植条件移植µC/OS-II到处理器上必须满足以下条件:(1)处理器的C编译器能产生可重入代码µC/OS是多任务内核,函数可能会被多个任务调用,代码的重入性是保证完成多任务的基础。可重入代码指的是可被多个体任务同时调用,而不会破坏数据的一段代码,或者说代码具有在执行过程中打断后再次被调用的能力。举例说明:Swap1函数代码:Inttemp;voidswap1(int*x,int*y){temp=*x;*x=*y;*y=temp;}举例说明:Swap2函数代码:voidswap2(int*x,int*y){inttemp;temp=*x;*x=*y;*y=temp;}可重入不可重入编译器还得支持,MDK开发环境,可生成可重入代码61(2)用C语言可打开和关闭中断ARM处理器核包含一个CPSR寄存器,该寄存器包括一个全局的中断禁止位,控制它便可打开和关闭中断。PRIMASK(3)处理器支持中断并且能产生定时中断µC/OS-II通过处理器产生的定时器中断来实现多任务之间的调度。ARMCortex-M3的处理器都支持中断并能产生定时器中断,专门有一个SysTick定时器来实现。(4)处理器支持能够容纳一定量数据的硬件堆栈(通常需要几十KByte字节)

比如AT98C51处理器,内部只有128字节的RAM,要运行,需外扩RAM。CM3的芯片,内部可多达128KByte的容量,因此可直接使用。(5)处理器有将堆栈指针和其他CPU寄存器读出和存储到堆栈(或内存)的指令

µC/OS-II进行任务调度时,会把当前任务的CPU寄存器存到此任务的堆栈中,然后,再从另一个任务的堆栈中恢复原来的工作寄存器,继续运行另一个任务。所以,寄存器的入栈和出栈是µC/OS-II多任务调度的基础。运行TCP、UDP需要的内存会更大,通常要100K左右62

所谓移植,就是使一个实时操作系统能够在某个微处理器平台上或微控制器平台上运行。由µC/OS-II的文件系统可知,在移植过程中,用户需要关注的就是与处理器相关的代码。这部分包括一个头文件OS_CPU.H、一个汇编文件OS_CPU_A.ASM和一个C代码文件OS_CPU_C.C。OS_CPU.HOS_CPU_C.COS_CPU_A.ASM#define设置一个常量的值声明10个数据类型用#define声明三个宏用C语言编写六个简单的函数编写四个汇编语言函数移植实际中,写一个就行3.1.2移植步骤631、INCLUDES.HINCLUDES.H是一个头文件,它在所有.C文件的第一行被包含。#include"includes.h"INCLUDES.H使得用户项目中的每个.C文件不用分别去考虑它实际上需要哪些头文件。使用INCLUDES.H的唯一缺点是它可能会包含一些实际不相关的头文件。这意味着每个文件的编译时间可能会增加。但由于它增强了代码的可移植性,所以我们还是决定使用这一方法。用户可以通过编辑INCLUDES.H来增加自己的头文件,但是用户的头文件必须添加在头文件列表的最后。2、基本配置和定义OS_CPU.H(1)用#define设置一个常量的值#ifdefOS_CPU_GLOBALS#defineOS_CPU_EXT#else#defineOS_CPU_EXTextern#endif64(2)定义与编译器相关的数据类型

为了保证可移植性,程序中没有直接使用C语言中的short、int和long等数据类型的定义,因为它们与处理器类型有关,隐含着不可移植性。程序中自己定义了一套数据类型,如INT16U表示16位无符号整型。对于ARM这样的32位内核,INT16U是unsignedshort型;如果是16位处理器,则是unsingedint型。typedefunsignedcharBOOLEAN;/*Boolean布尔变量*/typedefunsignedcharINT8U;/*无符号8位实体*/typedefsignedcharINT8S;/*有符号8位实体*/typedefunsignedshortINT16U;/*无符号16位实体*/typedefsignedshortINT16S;/*有符号16位实体*/typedefunsignedintINT32U;/*无符号32位实体*/typedefsignedintINT32S;/*有符号32位实体*/typedeffloatFP32/*单精度浮点数*/typedefdoubleFP64;/*双精度浮点数*/typedefunsignedintOS_STK;/*堆栈是32位宽度*/typedefunsignedintOS_CPU_SR;/*申明状态寄存器是32位*/µC/OS-II内核的代码需要与处理器位有关65(3)定义临界段(允许和禁止中断宏)

与所有实时内核一样,µC/OS-II需要先禁止中断,再访问代码的临界区,并且在访问完毕后,重新允计中断。这就是µC/OS-II能够保护临界段代码免受多任务或中断服务例程ISR的破坏。中断禁止时间是商业实时内核公司提供的重要指标之一,因为它将影响到用户的系统对实时事件的响应能力。虽然µC/OS-II尽量使中断禁止时间达到最短,但是µC/OS-II的中断禁止时间还主要依赖于处理器结构和编译器产生的代码的质量。通常每个处理器都会提供一定的指令来禁止/允许中断,因此用户的C编译器必须由一定的机制来直接从C中执行这些操作。OS_ENTER_CRITICAL()OS_EXIT_CRITICAL()µC/OS-II定义了两个宏来禁止和允许中断:#defineOS_CRITICAL_METHOD3#defineOS_ENTER_CRITICAL(){cpu_sr=OS_CPU_SR_Save();}#defineOS_EXIT_CRITICAL(){OS_CPU_SR_Restore(cpu_sr);}µC/OS-II定义了三种方法关闭和打开中断(OS_CRITICAL_METHED=1,2,3),通常情况下,我们都是选用的方法3。66OS_CPU_SR_SaveMRSR0,PRIMASKCPSIDIBXLROS_CPU_SR_RestoreMSRPRIMASK,R0BXLR关中断开中断(4)定义栈的增长方向µC/OS-II使用结构常量OS_STK_GROWTH来指定堆栈的增长方式:置OS_STK_GROWTH为0,表示堆栈从下往上增长;置OS_STK_GROWTH为1,表示堆栈从上往下增长。Cortex-M3支持从上往下增长的方式。因此,我们在移植时,需将OS_STK_GROWTH=1,如果是51系列单片机,则OS_STK_GROWTH=0。#defineOS_STK_GROWTH1

67(5)定义OS_TASK_SW()宏,任务级上下文切换

任务级上下文切换(即任务切换)调用宏定义OS_TASK_SW()。因为上下文切换跟处理器有密切关系,OS_TASK_SW()实质上是调用汇编函数OSCtxSW(),它在OS_CPU_A.ASM文件中定义。#defineOS_TASK_SW()OSCtxSw()OSCtxSwPUSH{R4,R5}LDRR4,=NVIC_INT_CTRL;触发软件中断

LDRR5,=NVIC_PENDSVSETSTRR5,[R4]POP{R4,R5}BXLR原型如下:打开MDK查看原始代码NVIC_INT_CTRLEQU0xE000ED04NVIC_PENDSVSETEQU0x10000000当执行完这段代后,自运的产生PendSV中断,也即14号异常,自动跳到14号异常服务程序执行。在本移植中则会直接去执行:OSPendSV部分内容68中断控制及状态寄存器ICSR0xE000_ED04设置1将挂起中断693、移植汇编语言编写的4个与处理器相关的函数OS_CPU_A.ASM(1)OSStartHighRdy():运行优先级最高的就绪任务OSStartHighRdy()OSCtxSw()OSIntCtxSw()OSTickISR()OSStartHighRdy()函数是在OSStart()多任务启动之后,负责从最高优先级任务的TCB控制中获得该任务的堆栈指针SP,并通过SP依次将CPU现场恢复。这时系统就将控制权交给用户创建的任务进程,直到该任务被阻塞或都被其他更高优先级的任务抢占CPU。该函数仅仅在多任务启动时被执行一次,用来启动最高优先级的任务执行。移植该函数的原因是,它涉及将处理器寄存器保存到堆栈的操作。70OSStartHighRdyLDRR4,=NVIC_SYSPRI2;setthePendSVexception;priority设置PendSV优先级

LDRR5,=NVIC_PENDSV_PRI STRR5,[R4]

MOVR4,#0;setthePSPto0forinitial;contextswitchcall使PSP等于0MSRPSP,R4

LDRR4,=OSRunning;OSRunning=TRUEMOVR5,#1STRBR5,[R4]LDRR4,=NVIC_INT_CTRL;triggerthePendSVexception;触发软件中断

LDRR5,=NVIC_PENDSVSETSTRR5,[R4]CPSIEI;enableinterruptsatprocessor;level使能所有优先级的中断OSStartHangBOSStartHang71(2)OSCtxSw():任务优先级切换函数

该函数由OS_TASK_SW()宏调用,OS_TASK_SW()由OSSched()函数调用,OSSched()函数负责任务之间的调度。OSCtxSw()函数的工作是,先将当前任务的CPU现场保存到该任务的堆栈中,然后获得最高优先级任务的堆栈指针,并从该堆栈中恢复此任务的CPU现场,使之继续执行,该函数就完了一次任切换。OSCtxSwPUSH{R4,R5}LDRR4,=NVIC_INT_CTRL;触发软件中断

LDRR5,=NVIC_PENDSVSETSTRR5,[R4]POP{R4,R5}BXLR产生PendSV异常PendSV并没有马上执行,因为OS_TASK_SW()(实际是OSCtxSw())被调用前中断是关闭的。PednSV只能在中断使能后才能执行。OS_TASK_SW()总是被OS_Sched()调用(见OS_CORE.C文件)72(3)OSInitCtxSw():中断级的任务切换函数

该函数由OSIntExit()调用。由于中断可能会使更高优先级的任务进入就绪态,因此,为了让更高优先级的任务能立即运行,在中断服务子程序的最后,OSInitExit()函数会调用OSInitCtxSw()做任务切换。这样做的目的主要是能够尽快地让高优先级的任务得到响应,保证系统的实时性能。OSInitCtxSw()与OSCtxSw()都是用于任务切换函数,其区别在于,在OSIntCtxSw()中无需再保存CPU寄存器,因为在调用OSIntCtxSw()之前已发生了中断,OSIntCtxSw()已将默认的CPU寄存器保存到被中断的任务堆栈中。OSIntCtxSwPUSH{R4,R5}LDRR4,=NVIC_INT_CTRL;触发软件中断

LDRR5,=NVIC_PENDSVSETSTRR5,[R4]POP{R4,R5}BXLRNOPOSCtxSw()OSIntCtxSw()这两个函最终都会触发PendSV异常73OSPendSV()函数OSPendSV()是PendSV(可挂起中断服务)的中断处理函数,它负责µC/OS-II的全部上下文切换。这是ARMCortex-M3提倡的上下文切换方法。使用这程方法的好处理当发生任何的异常时,Cortex-m3自动保存CPU的一半通用寄存器到预先指定的堆栈中,并且在退出异常前按顺序恢复寄存器。OSPendSV()只需保存剩下的R4-R11寄存器并且调整好堆栈指针。这种方法速度快,充分体现了ARMCortex-M3的优势,而且无论是任务还是异常均可触发此函数切换上下文。注意使用前应在Startup.S中申明。7475ARMCortex-M3任务切换示意图76(4)OSTickISR():时钟节拍中断服务函数

时钟节拍是特定的周期性中断,是由硬件定时器产生的。时钟节拍式中断使得内核可将任务延时若干个整数时钟节拍,以及当任务等待事件发生时,提供等待超时的依据。时钟节拍频率越高,系统的额外开销越大。中断间的时间间隔取决于不同的应用。

OSTickISR()首先将CPU寄存器的值保存在被中断任务的堆栈中,之后调用OSIntEnter()。随后,OSTickISR()调用OSTimeTick,检查所有处于延时等待状态的任务,判断是否有延时结束就绪的任务。OSTickISR()最后调用OSIntExit()。如果在中断中(或其他嵌套的中断)有更高优先级的任务就绪,并且当前中断为中断嵌套的最后一层,那么OSIntExit()将进行任务调度。774、移植C语言编写的6个与操作系统相关的函数OS_CPU_C.COS_CPU_C.C文件中包含6个和CPU相关的函数,这6个函数为:

这些函数中,唯一必须移植的是任务堆栈初始化函数OSTaskStkInit()。这个函数在任务创建时被调用,负责初始化任务的堆栈结构并返回新堆栈的指针stk。堆栈初始化工作结束后,返回新的堆栈栈顶指针。OSTaskStkInit()OSTaskDelHook()OSTaskSwHook()OSTaskStartHook()OSTimeHook()783.2基于ARM7的移植任务模式的取舍ARM7处理器核具有用户、系统、管理、中止、未定义、中断和快中断七种

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论