UCOS

您所在的位置:网站首页 ucosii的任务调度方式 UCOS

UCOS

2023-12-17 19:48| 来源: 网络整理| 查看: 265

UCOS—II 一、实时操作系统的概念 1.1 操作系统

​ 操作系统是一种系统软件,他在计算机硬件与计算机应用程序之间,通过提供程序接口,屏蔽了计算机硬件工作的一些细节,从提高了应用程序的开发效率。

​ 应用在嵌入式系统中的操作系统称为嵌入式操作系统

1.2 前后台系统

​ 不复杂的小系统一般设计成前后台系统(也称超循环系统)。应用程序是一个无限的循环,循环中调用相应的函数完成相应的操作,这部分可以看成后台系统。中断服务程序处理异步事件,这部分可以看成前台系统。后台也可以叫任务级,前台也可以叫中断级。时间相关很强的关键操作一定是靠中断服务来保证的,因为中断服务提供的信息一定要等到后台程序走到该处理信息这一步时才能得到处理,因此这种系统在处理信息的及时性上,比实际可以做到的要差。这个指标称作任务的相应时间,最坏情况下的任务级相应时间取决于整个循环的执行时间。因为循环的执行时间不是常数,程序经过某一特定的部分的准确时间也是不能确定的。

​ 很多基于微处理器的产品处采用前后台系统设计,如微波炉、玩具、电话机等。

图片1(前后台系统)

1.3 实时操作系统

​ 如果操作系统能使计算机系统及时响应外部事件的请求,并能及时控制所有实时设备与实时系统协调运行,且能在一个规定的时间内完成对时间的处理,那么这种操作系统就是一个实时操作系统。

实时系统的两个基本要求:

实时系统的计算必须产生正确的结果,称为逻辑或功能正确实时系统的计算必须在预定的时间内完成,称为时间正确

按时间正确的程度来分,实时操作系统又分为硬实时操作系统和软实时操作系统两种。

**硬实时操作系统:**要求系统必须在极严格的时间内完成实时任务

**软实时操作系统:**系统完成实时任务的截止时间要求不是十分严格

实时操作系统需要满足的条件:

实时操作系统必须是多任务系统任务的切换时间应与系统中的任务数无关中断延时时间可预知并尽可能短

UCOS—II是一个嵌入式多任务试试操作系统。

二、任务的相关概念

​ 多任务操作系统在设计较为复杂的应用程序时,通常把大型任务分解成多个小任务,然后再计算机中通过运行这些小任务,最终达到完成大任务的目的。这种方法可以使任务并发的运行多个任务,从而提高处理器的利用率,加快程序的执行速度。

​ UCOS-II就是一个能对这些小任务的运行进行管理和调度的多任务系统。

​ UCOS-II的任务由三个部分所组成:任务程序代码(函数)、任务堆栈和任务控制块。其中,任务控制块就是关联了任务代码的程序控制块,它记录了任务的各个属性;任务堆栈则用来保存任务的工作环境;任务程序代码就是任务的执行部分。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bbAEtnok-1631626214977)(https://i.loli.net/2021/08/04/GHsovt5CqTWgfJl.png)]

根据任务是否具有自己的私有运行空间,可以把任务分成”线程“或”进程“。具体来说,具有私有空间的任务叫做进程,没有私有空间的任务叫做线程。

从任务的组成上来看,UCOS-II没有给任务定义私有空间,因此UCOS-II中所有的任务都属于线程。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xfTyPRJX-1631626214978)(https://i.loli.net/2021/08/04/w49AecBNh5rWFJy.png)]

UCOS-II的任务由两种:用户任务和系统任务,由应用程序设计者编写的任务,叫做用户任务;由系统提供的任务叫做系统任务;系统任务是胃应用程序提供某种服务或为系统本身服务的服务的,在UCOS-II中,最多可包含64个任务(包括用户任务和系统任务)。

64个任务中,保留了4个最高优先级和4个最低优先级任务供UCOS-II自己使用,所以用户可以使用的只有56个任务。任务的优先级越高,反映的优先级的值则越低。

​ 一个任务,也称作一个线程,是一个简单的程序,该程序可以认为CPU完全只属于该程序自己。试试应用程序的设计过程,包括如何吧问题分割成多个任务,每个任务都是整个应用的某一部分,每个任务都被赋予一定的优先级,有自己的一套CPU寄存器和直接的栈空间。

图片2(任务概念)

​ UCOS-II是按照系统中只有一个CPU来设计的,在这种系统中,一个时刻只会有一个任务占用CPU处在运行状态,而其他任务只能处在其他状态。

任务的状态说明休眠态任务只是以代码的形式驻留在程序空间(ROM或RAM),还没有交给操作系统管理,当并不被多任务内核所调度就绪态意味着任务具备了运行的充分条件,可以运行了,但是由于任务的优先级比正在运行的任务的优先级低,因此暂时还不能运行运行态指任务掌握了CPU的控制权,正在运行中,任何时刻只能有一个任务处于运行状态。挂起态也叫做等待事件态,指任务在等待某一时间的发生(例如等待某外设的I/O操作,等待某共享资源由暂不能使用变成能使用状态,等待定时脉冲的到来或等待超时信号的到来以结束目前的等待,等待)。中断态一个正在运行的任务一旦响应中断申请就会在中断运行而去执行中断服务程序,这时候任务的状态叫做中断服务态。

​ 在系统任务的管理下,一个任务可以在5个不同对的状态自己发生转换。其转换关系如下图所示。

图片3(任务的5种状态)

三、任务调度的相关概念 用户任务代码的结构

任务的执行代码通常是一个无线循环的结构,并且在这个循环中可以响应中断,这种结构也叫做超循环结构

void Task1(void *pdata) { while(1) { 可以被中断的用户代码; OS_ENTHER_CRITICAL(); //进入临界端(关中断) 不可以被中断的用户代码; OS_EXIT_CRITICAL(); //退出临界段(开中断) 可以被中断的用户代码; } }

一个UCOS-II任务的代码就是一个C语言函数,为了可以传递各种不同类型的数据甚至是函数,所以UCOS-II把任务的参数定义成一个void类型的指针。

用户应用程序的结构

​ 用户的任务就是一个C语言函数,当时这个函数不是由主函数main()调用的函数,在系统中他与主函数main()处于平等的地位,他们何时被运行以及何时被终止都是由操作系统来调度的。

​ main()是一个应用程序的主函数,是程序运行的入口点,所以虽然它不调用任务,但是负责任务的创建并将他们交给系统,至于何时运行,则与主函数无关。

void main() { ...... OSInit(); //初始化UCOS-II ...... OSTaskCreate(Task1,......); //创建用户任务1 OSTaskCreate(Task2,......); //创建用户任务2 ...... OSStart(); //启动UCOS-II ...... } void Task1(void *pdata) //定义用户任务1 { while(1) { ...... } } void Task2(void *pdata) //定义用户任务2 { while(1) { ...... } }

OSTaskCreate()是UCOS-II提供的用来创建任务的函数;

OSStart()启动UCOS-III的函数。系统被启动后,任务就由操作系统来管理和调度了。

系统任务

UCOS-II预定义了两个系统任务:空闲任务和统计任务。其中,空闲任务是每个应用程序必须使用的,而统计任务是应用程序可以根据实际需要来选择使用的。

空闲任务

在系统运行的过程中,极有可能会在某个时间内无用户任务可运行而处于所谓的空闲状态。为了使CPU在没有用户任务可执行时有事可做,UCOS-II提供了一个叫做空闲任务OSTaskIdle()的系统任务。其代码如下

void OStaskIdle(void *pdate) { # if OS_CRITICAL_METHOD == 3 OS_CPU_SR cpu_sr; #endif padta = pdata; //防止某些编译器报错 while(1) { OS_ENTER_CRITICAL(); //关闭中断 OSdleCtr++; //计数 OS_EXIT_CRITICAL(); //开放中断 } }

空闲任务几乎不做什么东西,只是对系统定义的一个空闲任务次数运行次数计数器OSdleCtr进行加1操作。UCOS-II规定,一个用户应用程序必须使用这个空闲任务,而且这个任务不能通过程序来删除。

​ 至于代码中的“Pdata = pdata;"是为了防止编译器报错而使用的一个程序设计技巧,因为空闲任务没有用参数pdata,而某些C编译器会对这种情况报错(说定义了参数却没有使用),有了这行代码,编译器就不会报错了。

统计任务

​ UCOS-II提供的另一个系统任务就是统计任务()。该任务每秒计算一次CPU在单位时间内被使用的时间,并把计算结果以百分比的形式存放在变量OSCOUsage中,以便其他应用程序来了解CPU的利用率。

​ 系统是否使用统计任务,用户可以根据应用程序的实际需求来进行选择。如果用户应用程序决定要使用统计任务,则必须把定义在系统头文件OS_CFG.H中的系统配置常数OS_TASH_STAT_EN设置为1,并且在程序中要调用函数OSStatInit()对统计任务进行初始化。

任务的优先权级优先级别

​ UCOS-II中创建追到64个任务(0-63)。数字越小,优先级别越高。

​ UCOS-II在系统配置文件OS_CFG.H中定义了一个用来表示最低优先级别的参数OS_LOWEST_PRIO,如果用户为其赋了值,那么就以为着系统可用的优先级有OS_LOWEST_PRIO+1个(0到OS_LOWEST_PRIO)。

​ 另外,系统会把最低优先级别OS_LOWEST_PRIO自动赋值给统计任务,如果应用程序使用了统计任务,则系统还会把优先级别OS_LOWEST_PRIO-1自动赋值给统计任务。

任务堆栈

任务堆栈是任务的重要的组成部分

堆栈就是在存储器中按“后进先出(LIFO)”的原则组织的连续存储空间。为了满足任务切换和响应中断时保存CPU寄存器中的内容及任务调用其他函数时的需要,每个任务都应该配有自己的堆栈。所有UCOS-II任务的任务控制块中都含有一个指向该任务堆栈的指针。

四、中断的相关概念

​ 任务在运行过程中,应内部或外部异步事件的请求中止当前任务,而去处理异步事件所要求的过程叫做中断。应中断请求而运行的程序叫做中断服务子程序(ISR),中断服务子程序的入口地址叫做中断向量。

4.1.1 UCOS-II的中断过程

​ UCOS-II系统响应中断的过程是:系统接收到中断请求后,如果这时CPU出于中断允许状态,系统就会中止正在运行的当前任务,而按照中断向量的指向转而去运行中断服务子程序;当终端服务子程序的运行结束后,系统将会根据返回情况返回到被中止的任务继续运行,或者转向运行另一个具有更高优先级别的就绪任务。

​ 对于可剥夺型UCOS-II内核,中断服务子程序运行结束之后,系统将会根据情况进行一次任务调度去运行优先级别最高的就绪任务,而并不一定要接着运行被中断的任务。

​ UCOS-II系统允许中断嵌套,即高优先级别的中断源的中断请求可以中断低优先级别的中断服务程序的运行。为了记录中断嵌套的层数,UCOS-II定义了一个全局变量OSIntNesting。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HJOIlmwK-1631626214981)(https://i.loli.net/2021/08/04/bHtewxyfhqs6EWN.png)]

​ 编写UCOS-II的中断服务程序时,要用到两个重要的函数:OSIntEnter()和OSIntExit()。

函数OSIntEnter()的作用就是把全局变量OS_IntNesting加1,从而用它来记录中断嵌套的层数。

函数OSIntEnter()的调用通常发生在中断服务程序保护了被中断任务的断点数据之后运行用户中断服务代码之前,所以称之为进入中断服务函数。

另一个在中断服务程序中要调用的函数叫做退出中断服务函数OSIntExit()。调用该函数中断嵌套层数计数器减1.

函数OSIntExit()的流程图为:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ogsioMAL-1631626214983)(https://i.loli.net/2021/08/04/hks7AJVtR2jquZE.png)]

​ 从图中可以看出,函数在中断嵌套层数计数器为0、调度器未被锁定且从任务就绪表中查找到的最高级的就绪任务不是被中断的任务的条件下将要进行任务切换,否则就返回被中断的服务子程序。

​ 中断服务子程序流程:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4sX0iFCa-1631626214984)(https://i.loli.net/2021/08/04/iqBUjX9Vr4ZDGKI.png)]

UCOS-II中,通常用一个任务来表示异步事件的处理,而在中断服务程序中通过向任务发消息的方法去激活这个任务

OSIntCtxSw():中断级任务切换函数

4.1.3应用程序中的临界段

在UCOS-II中,不希望被中断的代码段叫做临界段。从代码上来看,处在关中断和开中断之间的代码段就是临界段。

在UCOS-II中,不要在临界段中调用UCOS-II提供的功能函数,以免系统崩溃。

OS_ENTER_CRITICAL():封装了相关关中断指令的宏

OS_EXIT_CRITICAL():封装了相关开中断指令的宏

4.2 时钟

​ 任何操作系统都要提供一个周期性的信号源,以供系统处理诸如延时、超时等与时间有关的事件,这个周期性的信号源叫做时钟。

​ UCOS-II用硬件定时器产生一个周期为毫秒(ms)级的周期性中断来实现系统时钟。最小的时钟单位就是两次中断之间相隔的时间,这个最小时钟单位叫做时钟拍(Time Tick)。

​ 硬件定时器以时钟节拍为周期定时地产生中断,该中断的中断服务程序叫做OSTickISR(),中断服务程序通过调用函数OSTimeTick()来完成系统系统在每个时钟节拍时需要做的工作。

​ UCOS-II每次响应定时中断时调用OSTimeTick()做了两件事情:一是给计数器OSTime加1;二是遍历任务控制块链表中的所有任务控制块,把各个任务控制块中用来存放任务延时时限的OSTCBDly变量减1,并使该项为0,同时又不使被挂起的任务进入就绪状态。

​ 简单地说,函数OSTimeTick()的任务就是在每个时钟节拍了解任务的延时状态,了解每个任务的延时状态,使其中一家到了延时时间的非挂起任务进入就绪状态。

4.3 时间管理 4.3.1任务的延时

​ 因为嵌入式系统的任务是一个无限循环,并且UCOS-II是一个抢占式内核。UCOS-II规定,出空闲任务之外的所有任务必须在合适的位置调用系统提供的函数OSTimeDly(),使当前任务的运行延时(暂停)一段时间并进行一次任务调度,让出CPU的使用权。

OSTimeDly(INT16U ticks)中的函数参数ticks是以时钟节拍数为单位的延时时间

OSTimeDlyHMSM()shi 是UCOS-II提供的另一个可以用时、分、秒、毫秒为参数的任务的延时函数。该函数与函数OSTimeDly()一样也要引发一次调度。

​ 调用了函数OSTime()或OSTimeDlyHMSM()的任务,当规定的延时时间期满,或有其他任务调用函数OSTimeDlyResume()取消了延时,他立即会进入就绪状态。

4.3.2 取消任务的延时

延时的任务可通过在其他任务中调用函数OSTimeDlyResume()取消延时而进入就绪状态。如果任务比正在运行的任务优先级别高,则立即引发一次任务调度。

OSTimeDlyResume(INT8U prio)中的prio为被取消延时任务的优先级别。、

4.3.2 获取和设置系统时间

​ 系统定义了一个INT32U类型的全局变量OSTime来记录系统发生的时钟节拍数。OSTime在应用程序调用OSStart()时被初始化为0,以后每发生一个时钟节拍OSTime的值就被加1。

​ 在应用程序中调用函数OSTimeGet()可获取OSTime的值。

​ 在应用程序中调用函数OSTimeSet(),可设置OSTime的值。函数 OSTimeSet( INT32U ticks)中的参数ticks为OSTime的设定值(节拍数)。

+++

+++

+++

OS_TICKS_PER_SEC :一秒有多少个系统TICK(时钟节拍)

如(#define OS_TICKS_PER_SEC 100)则一秒有100个TICK

reload:系统频率的8分频 (MHz)

1MHz=1000 000Hz

Hz:每秒钟变化几次 例如2Hz则是每秒变化2次

(reload*1000 000)/OS_TICKS_PER_SEC:每个时间节拍需要变化多少次

OSTaskCreate();

第一个参数一个指针,也就是用户代码的首地址

第二个参数是指向数据初始化的指针

第三个参数是指向任务堆栈栈顶的指针

第四个参数是任务的优先级

void OSSemPend ( OS_EVNNT *pevent, INT16U timeout, int8u *err );

pevent 是指向信号量的指针。该指针的值在建立该信号量时可以得到。

err 是指向包含错误码的变量的指针,返回的错误码可能为下述几种:

​ * OS_NO_ERR :信号量不为零。 ​ * OS_TIMEOUT :信号量没有在指定数目的时钟周期内被设置。 ​ * OS_ERR_PEND_ISR :从中断调用该函数。虽然规定了不允许从中断调用该函数,但μC/OS-Ⅱ仍然包含了检测这种情况的功能。 ​ * OS_ERR_EVENT_TYPE :pevent 不是指向信号量的指针。

INT8U OSTaskResume (INT8U prio)

任务控制块:用来记录任务的堆栈、任务的当前状态、任务的优先级、等一些与任务管理有关属性的表叫做任务控制快

五、进程通信 5.1 任务的同步和事件 5.1.1 任务间的同步

为了防止任务之间起冲突,各个任务之间必须建立一些制约关系

制约关系有:直接制约关系、间接制约关系

直接制约关系源于任务之间的合作

间接制约关系源于对资源的共享

任务之间的制约性的合作运行机制叫做任务间的同步

5.1.2 事件

​ UCOS-II使用信号量、邮箱(消息邮箱)和消息队列这些中间环节来实现任务之间的通信,这些中间环节被统一称作“事件”。

5.1.2.1 信号量

每当有任务申请信号量时,如果信号量计数器OSEventCnt的值大于0,则把OSEventCnt减1并使任务继续进行;如果OSEventCnt的值为0,则会将任务列入任务等待列表,使任务处于等待状态。如果有正在使用信号量的任务释放了该信号量,则会在任务等待表中找出优先级别最高的等待任务,并在使它就绪后调用调度器引发一次调度;如果任务等待表中已经没有等待任务,则信号量计数器就只是简单的加1。

信号量的操作

/*定义信号量*/ OS_EVENT *sem; int main(void) { ...... sem=OSSemCreate(0); //创建一个信号量 } //任务1 void taks1(void *pdata) { while(1) { ...... OSSemPost(sem); //发送信号量 } } //任务2 void taks2(void *pdata) { while(1) { OSSemPend(sem,0,&err); //请求信号量 } }

创建:OSSemCreate(cnt):cnt为信号量计数器初值

请求:OSSempend(pevent,timeout,err):pevent为信号量的指针,timeout为等待时限,err为错误信息

发送:OSSemPost(pevent)

5.1.2.2 互斥型信号量

​ 互斥型信号量是一个二值信号,他可以使任务以独占方式使用共享资源。

​ 两个任务在使用互斥信号量进行通信,可使这两个信号量无冲突的访问一个共享资源,当任务1发现信号量标志位为1时,它一方面把信号量的标志值由“1”改为“0”,另一方面进行共享资源的访问。如果任务2在任务一已经获得信号之后来请求信号量,由于任务2获得的标志值为0,所以任务2只能等待二不能访问共享资源。直到任务1使用完共享资源后,由任务1向信号量发信号使信号量标志的值由“0”再变为“1”时,任务2才能访问共享资源。

​ 在使用互斥信号量时,在访问完共享资源后,一定要把信号量标志的值由“1”变为“0”。

任务优先级的反转现象:在可剥夺型内核中,当任务以独占方式使用共享资源时,会出现低优先级任务先于高优先级任务而被运行的现象,这就是所谓的任务优先级反转。

一般来说,在实时操作系统中不允许出现这种现象,因为它破坏了任务执行的预期顺序,可能会导致严重的后果,因此应该杜绝任务优先级反转现象的发生。

任务优先级的反转的原因:之所以出现任务优先级反转现象,是因为一个优先级别较低的任务在获得了信号量使用共享资源期间,被具有较高优先级别的任务所打断而不能示范信号量,从而使正在等待这一信号量的更高级别的任务因得不到信号量而被迫处于等待状态,在这个等待期间,就让优先级别低于它而高于占据 信号量的任务先运行了

**任务优先级的反转的解决办法之一:**是获得信号量任务的优先级别在使用共享资源期间展示提升到所有任务最高优先级别的一个级别上,从而使该任务不被其他任务所打断,从而尽快的使用完共享资源并释放信号量,然后再释放信号量之后,再恢复该任务原来的优先级别。

互斥信号量的操作:

OS_EVENT *UartSend; //定义一个互斥型信号量 int main(void) { ...... UartSend = OSMutexCreate(0, &err);//创建互斥信号量 } //任务1 void taks1(void *pdata) { while(1) { ...... OSMutexPend(UartSend, 0, &err); //请求互斥信号量 ...... OSMutexPost(UartSend); //释放互斥信号量 } } //任务2 void taks2(void *pdata) { while(1) { ...... OSMutexPend(UartSend, 0, &err); //请求互斥信号量 ...... OSMutexPost(UartSend); //释放互斥信号量 } }

创建:OSMutexCreate( 优先级别,错误信息)

请求:OSMutexPend(互斥型信号量指针,等待时限,错误信息)

释放:OSMutexPost(互斥型信号量指针)

获取互斥信号量当前状态:OSMutexQuery(互斥型信号量指针,存放互斥型信号量状态的结构)

删除:OSMutexDel(互斥型信号量指针,删除方式选项,错误信息)

5.1.2.3 消息邮箱

多任务操作系统中,任务与任务之间可以通过一个数据的方式来进行传递,这种数据叫做“消息“。可以在内存中创建一个存储空间作为该数据的缓冲区,该缓冲区叫做消息缓冲区,在任务间传递数据(消息)的一个最简单的方法就是传递消息缓冲区的指针。

用来传递消息缓冲区指针的数据结构叫做消息邮箱

消息邮箱的操作

OS_EVENT *Str_Box; //定义一个邮箱 int main(void) { ...... Str_Box = OSMboxCreate ((void*)0);//创建消息邮箱 } //任务1 void taks1(void *pdata) { U8 num=0; while(1) { ...... OSMboxPost(Str_Box,&EXTI_num); //发送一个消息到邮箱中 } } //任务2 void taks2(void *pdata) { INT8U err; //错误信息 u8 *result; //存放接收到的消息邮箱指针 u8 result_num; //存放接收到的消息邮箱的数据 result = OSMboxPend(Str_Box,0,&err);//请求消息邮箱 while(1) { result_num = *result; ...... } }

创建:OSMboxCreate(msg) :msg为消息的指针,函数的返回值为消息邮箱的指针;

发送:OSMboxPost(pevent, msg):pevent为消息邮箱指针,msg为消息指针;

以广播形式发送:OSMboxPostOpt(pevent, msg,opt):opt为广播选项,该值为OS_POST_OPT_BROADCAST时向所有等待任务广播;若为OS_POST_OPT_NONE,则只把消息向任务级别最高的等待任务发送。

阻塞请求:OSMboxPend(pevent,timeout,*err):timeout为等待时限

*err为错误信息。作用是查看邮箱指针OSEventPtr是否为NULL,若不是NULL则把邮箱中的消息指针返回给调用函数的任务,当参数err为OS_NO_ERR时,表示任务获取消息成功;若邮箱指针OSEventPtr,则任务进入等待状态,并引发一次任务调度

非阻塞请求:OSMboxAccept(pevent):该函数与OSMboxPend()的区别在于,调用函数时候,若请求失败,则不等待而继续运行。如果邮箱中有消息,就把邮箱清空,而邮箱中原来指向消息的指针被返回给OSMboxAccept()的调用函数。

查询状态:OSMboxQuery(pevent,*pdata):pdata为存放邮箱信息的结构

删除:OSMboxDel(*pevent, opt, *err):opt为删除选项

5.1.2.4 消息队列

​ 消息队列可在任务之间传递多条消息,消息队列由三部分组成:事件控制块、消息队列和消息

​ 消息队列相当于一个共用一个任务等待列表的消息邮箱数据,也可以理解成是一个消息的阵列

消息队列的操作

/*定义一个队列*/ OS_EVENT *str_Q; #define Messages_Size 128 void *MsgGrp[Messages_Size]; //定义消息指针数 void main(void) { ...... str_Q = OSQCreate(&MsgGrp[0],Messages_Size);//创建消息队列 } //任务1 void taks1(void *pdata) { u8 *result; while(1) { OSQPost (str_Q, &result_num); //向消息队列中发送消息 } } //任务2 void taks2(void *pdata) { uint8_t err; u8 Num=0; u8 *ss; //存放接收到的消息队列指针 while(1) { ss = OSQPend(str_Q,0,&err); //从消息队列中取消息 Num = *ss; } }

创建:OSQCreate(start,size):start为指针数组地址,size为数组长度

请求:OSQPend(pevent,timeout,err)

发送:OSQPost(pevent,msg)

广播发送:OSQPostOpt(pevent,msg,opt)

清空:OSQFlush(pevent)

删除:OSQDel(pevent)

查询:OSQQuery(pevent,pdata)

六、内存管理

UCOS-II改进了ANSI C用来分配和释放内存的函数malloc()和free(),使他们可以对大小固定的内存进行操作,从而使函数malloc()和free()的执行时间成为可确定的。

6.1 内存控制块

​ 为了便于内存的管理,在µC/OS-II中使用内存控制块(memory control blocks)的数据结构来跟踪每一个内存分区,系统中的每个内存分区都有它自己的内存控制块。

内存块:UCOS-II以内存为单位向应用程序提供动态内存。内存块的大小可由用户指定

内存分区:大小相等的内存块可以组成一个内存分区。内存分区是系统对内存进行管理的基本单位

OS_MEM *CommTxBuffer; //定义内存分区指针

INT8U CommTxPart[50][64]; //定义分区和内存块

创建动态内存分区:OS_MEM *OSMemCreate(void *addr,INIT32U nblks,INIT32U blksize,INIT8U *err)

addr为内存分区的起始地址;nblks为分区中内存块的数目;blksize为每个内存块的字节数;err为错误信息

程序清单 L7.1 内存控制块的数据结构typedef struct {void *OSMemAddr;void *OSMemFreeList;INT32U OSMemBlkSize;INT32U OSMemNBlks;INT32U OSMemNFree;} OS_MEM;

OSMemAddr是指向内存分区起始地址的指针。它在建立内存分区[见7.1节,建立一个内存分区,OSMemCreate()]时被初始化,在此之后就不能更改了。

OSMemFreeList是指向下一个空闲内存控制块或者下一个空闲的内存块的指针,具体含义要根据该内存分区是否已经建立来决定[见7.1节]。

OSMemBlkSize是内存分区中内存块的大小,是用户建立该内存分区时指定的[见7.1节]。

OSMemNBlks是内存分区中总的内存块数量,也是用户建立该内存分区时指定的[见7.1节]。

OSMemNFree是内存分区中当前可以得空闲内存块数量。

如果要在µC/OS-II中使用内存管理,需要在OS_CFG.H文件中将开关量OS_MEM_EN设置为1。这样µC/OS-II 在启动时就会对内存管理器进行初始化[由OSInit()调用OSMemInit()实现]。该初始化主要建立一个图 F7.3所示的内存控制块链表,其中的常数OS_MAX_MEM_PART(见文件OS_CFG.H)定义了最大的内存分区数,该常数值至少应为2。



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3