您好,欢迎来到华佗小知识。
搜索
您的当前位置:首页uCOS-II

uCOS-II

来源:华佗小知识
实验一、任务创建与删除

1、uC/OS-II介绍

对于操作系统的学习,创建任务和删除任务是最为基础的工作,uC/OS-II以源代码的形式发布,是开源软件, 但并不意味着它是免费软件。可以将其用于教学和私下研究;但是如果将其用于商业用途,那么必须通过Micrium获得商用许可。

uC/OS-II属于抢占式内核,最多可以支持个任务,分别对应优先级0~63,每个任务只能对应唯一的优先级,其中0为最高优先级。63为最低级,系统保留了4个最高优先级的任务和4个最低优先级的任务,所有用户可以使用的任务数有56个。 uC/OS-II提供了任务管理的各种函数调用,包括创建任务,删除任务,改变任务的优先级,任务挂起和恢复等。

系统初始化时会自动产生两个任务:一个是空闲任务,它的优先级最低,该任务仅给一个整型变量做累加运算;另一个是系统任务,它的优先级为次低,该任务负责统计当前cpu的利用率。

μC/OS-II可管理多达63个应用任务,并可以提供如下服务,本章将针对以下服务分别以例程的方式来介绍 1)信号量 2)互斥信号量 3)事件标识 4)消息邮箱 5)消息队列 6)任务管理

7)固定大小内存块管理 8)时间管理

2、任务创建与删除

想让uC/OS-II管理用户的任务,用户必须要先建立任务,在开始多任务调度(即调用OSStart())前,用户必须建立至少一个任务。uC/OS-II提供了两个函数来创建任务:OSTaskCreate()或OSTaskCreateExt()。可以使用其中任意一个即可,其函数原型如下:

INT8U OSTaskCreate (void (*task)(void *pd), void *pdata, OS_STK *ptos, INT8U prio)

INT8U OSTaskCreateExt (void(*task)(void *pd),void *pdata,SD_STK *ptos,INT8U prio,INT16U id,OS_STK *pbos,INT32U stk_size,void *pext,INT16U opt) task:任务代码指针

pdata:任务的参数指针

ptos:任务的堆栈的栈顶指针 prio:任务优先级

id:任务特殊的标识符(uC/OS-II中还未使用) pbos:任务的堆栈栈底的指针(用于堆栈检验) stk_size:堆栈成员数目的容量(宽度为4字节) pext:指向用户附加的数据域的指针

opt:是否允许堆栈检验,是否将堆栈清零,任务是否要进行浮点操作等等

删除任务,是说任务将返回并处于休眠状态,任务的代码不再被uC/OS-II调用,而不是删除任务代码。删除任务主要是把任务控制块从OSTCBList链表中移到OSTCBFreeList。uC/OS-II提供了两个函数来删除任务:OSTaskDel()或OSTaskDelReq()。 INT8U OSTaskDel (INT8U prio) //删除任务

INT8U RequestorTask (INT8U prio) //请求删除其他任务 prio:需要删除任务的优先级

3、实验分析

本次实验创建两个任务,任务一每隔两秒秒打印一次“AppTask1”,任务二每隔1s打印一次“AppTask2”,打印6次后打印“删除任务2”并删除任务。其源代码如下: int main (void) {

SysTick_Configuration(); //系统定时器初始化 USART_Configuration(); //串口初始化 LED_Configuration();

OSInit(); //usos ii初始化 AppTaskCreate();//创建任务 OSStart(); //开始任务调度

}

static void AppTaskCreate(void) {

INT8U err;

OSTaskCreateExt(AppTask1,(void*)0,(OS_STK )&AppTask1Stk[APP_TASK1_STK_SIZE-1],APP_TASK1_PRIO,APP_TASK1_PRIO,(OS_STK )&AppTask1Stk[0],APP_TASK1_STK_SIZE,(void )0,OS_TASK_OPT_STK_CHK|OS_TASK_OPT_STK_CLR); //创建任务1

OSTaskNameSet(APP_TASK1_PRIO, \"AppTask1\&err);

OSTaskCreateExt(AppTask2,(void*)0,(OS_STK )&AppTask2Stk[APP_TASK2_STK_SIZE-1],APP_TASK2_PRIO,APP_TASK2_PRIO,(OS_STK )&AppTask2Stk[0],APP_TASK2_STK_SIZE,(void*)0,OS_TASK_OPT_STK_CHK|OS_TASK_OPT_STK_CLR); //创建任务2

OSTaskNameSet(APP_TASK2_PRIO, \"AppTask2\&err); }

//任务1

static void AppTask1 (void *p_arg) {

while(1) {

printf(\"\\n\\rAppTask1\\r\\n\"); LED1(0);

OSTimeDlyHMSM(0,0,1,0); LED1(1);

OSTimeDlyHMSM(0,0,1,0); }

}

//任务2

static void AppTask2 (void *p_arg) {

INT8U i;

for(i=0;i<6;i++) {

printf(\"\\n\\rAppTask2 \\r\\n\");

OSTimeDlyHMSM(0,0,0,1); }

printf(\"\\n\\r删除任务2\\r\\n\");

OSTaskDel(APP_TASK2_PRIO); //删除任务2 }

如下图是串口打印的数据:

实验二、任务调度

本节我们主要介绍的是uC/OS-II任务调度。 1、uC/OS-II任务调度原理

在uC/OS-II创建的任务中,每个任务都是一个无限循环的函数,实现任务的切换需要操作系统完成。用户任务创建后,调用OSStart()开始进行任务调度。任务调度始终会运行就绪列表中优先级最高的任务。

如下图是任务状态的转换表图,正在运行的任务通过调用OS..Pend和延时函数进入等待状态,同时将其从就绪列表中删除,并加入到等待列表中,此时在就绪列表中查找优先级最高的任务设置为当前任务并运行。正在运行的任务通过调用OS..Post或OS..Resume函数使得正在等待挂起的任务进入就绪态,若有任务处于延时等待中,在心跳时钟是延时值减一,当减为零时延时等待任务充等待状态切换到就绪态,同时将其从等待列表中删除,并加入到就绪列表中。如果此任务优先级高于正在运行的任务优先级,则进行任务切换。

下面是系统定时器中断源代码

void SysTick_Handler(void) {

OS_CPU_SR cpu_sr;

OS_ENTER_CRITICAL(); //保存全局中断标志,关总中断 OSIntNesting++;

OS_EXIT_CRITICAL(); //恢复全局中断标志

OSTimeTick(); //延时计数值减一,判断计数时间到后进行任务切换 OSIntExit(); //在os_core.c文件里定义,如果有更高优先级的任务 //就绪了,则执行一次任务切换 }

2、实验分析

本次实验创建两个任务,任务一循环从串口打印“AppTask1”后延时2s,LED1闪烁一次,任务二循环从串口打印“AppTask2”后延时1s,LED2闪烁一次。其源代码如下: int main (void) {

SysTick_Configuration(); //系统定时器初始化 USART_Configuration(); //串口初始化 LED_Configuration(); OSInit(); //usos ii初始化 AppTaskCreate();//创建任务 OSStart(); //开始任务调度 }

static void AppTaskCreate(void) {

INT8U err;

OSTaskCreateExt(AppTask1,(void*)0,(OS_STK )&AppTask1Stk[APP_TASK1_STK_SIZE-1],APP_TASK1_PRIO,APP_TASK1_PRIO,(OS_STK )&AppTask1Stk[0],APP_TASK1_STK_SIZE,(void )0,OS_TASK_OPT_STK_CHK|OS_TASK_OPT_STK_CLR); //创建任务1 OSTaskNameSet(APP_TASK1_PRIO, \"AppTask1\&err);

OSTaskCreateExt(AppTask2,(void*)0,(OS_STK )&AppTask2Stk[APP_TASK2_STK_SIZE-1],APP_TASK2_PRIO,APP_TASK2_PRIO,(OS_STK )&AppTask2Stk[0],APP_TASK2_STK_SIZE,(void*)0,OS_TASK_OPT_STK_CHK|OS_TASK_OPT_STK_CLR); //创建任务2 OSTaskNameSet(APP_TASK2_PRIO, \"AppTask2\&err); }

//任务1

static void AppTask1 (void *p_arg) {

while(1) {

printf(\"\\n\\rAppTask1\\r\\n\"); LED1(0);

OSTimeDlyHMSM(0,0,1,0); LED1(1);

OSTimeDlyHMSM(0,0,1,0); } }

//任务2

static void AppTask2 (void *p_arg) {

while(1) {

printf(\"\\n\\rAppTask2 \\r\\n\"); LED2(0);

OSTimeDlyHMSM(0,0,0,500); LED2(1);

OSTimeDlyHMSM(0,0,0,500); } }

如下图是串口打印的数据,通过修改OSTimeDlyHMSM()的延时值,其中该延时函数的四个参数分别代表时分秒毫秒,可以看到打印速度的变化及两个字符串打印出来的比例。

实验三、消息队列

本节我们主要介绍的是uC/OS-II的通信方式--消息队列 1、队列介绍

为了使用uC/OS-II的消息队列,需要将OS_CFG.H中的OS_Q_EN及其下面的函数使能宏置位,并设置OS_MAX_QS为应用程序中最多可以设置的消息队列个数。消息队列使用的是先入先出的方式进行异步通信,即对先来的事件先处理,uC/OS-II也可以使用OSQPostFront将消息加入到队列头来实现后入先出。下图是任务、中断服务子程序和消息队列之间的关系。

2、队列操作

uC/OS-II提供了一系列函数对队列进行创建、接收和发送等。 1)创建队列OSQCreate()

OS_EVENT *OSQCreate(void **start, INT16U size)

start 二级指针,类型为void 数组的起始地址

size 队列长度

返回值 NULL表示没有空闲的事件控制块 OS_EVENT类型的指针指向生成的队列

2)将数据发送到队列尾OSQPost()与头OSQPostFront()

INT8U OSQPost (OS_EVENT *pevent, void *msg) INT8U OSQPostFront (OS_EVENT *pevent, void*msg) pevent 创建队列时返回的指针 msg 发送数据的指针。 返回值 有三个可能的返回值:

1.OS_NO_ERR 数据被成功发送到队列中。 2.OS_ERR_EVENT_TYPE 事件类型错误 3.OS_Q_FULL 队列满

3)从消息队列中获取一个消息OSQPend()等待和OSQAccept()立即返回 void *OSQPend (OS_EVENT *pevent, INT16U timeout, INT8U *err) void *OSQAccept (OS_EVENT *pevent)

pevent 创建队列时返回的指针 timeout 阻塞超时时间 err 错误类型指针 返回值 有两个可能的返回值: 1.NULL 没有获取到数据 2.数据指针

4)清空消息队列OSQFlush()

INT8U OSQFlush (OS_EVENT *pevent)

pevent 创建队列时返回的指针 返回值 有两个可能的返回值:

1.OS_NO_ERR 清空成功 2.OS_ERR_EVENT_TYPE 事件类型错误 5)查询队列的当前状态OSQQuery()

INT8U OSQQuery (OS_EVENT *pevent, OS_Q_DATA *pdata) pevent 创建队列时返回的指针 pdata 消息队列的信息 返回值 有两个可能的返回值:

1.OS_NO_ERR 清空成功 2.OS_ERR_EVENT_TYPE 事件类型错误 3、实验分析

实验创建两个任务,并创建一个长度为10的队列,一个任务用于每500ms向队列发送字符串,另一个任务用于每隔1s等待接收字符串。接收到字符串后将其打印出来,由于发送间隔时间比接收间隔时间短,因此实验到后来会出现队列满的现象。其源代码如下: OS_EVENT *CommQ;

void *CommMsg[OS_MAX_QS]; //OS_MAX_QS(在os_cfg.h里定义)个指针的数组 int main (void) {

INT8U err;

SysTick_Configuration(); //系统定时器初始化

USART_Configuration(); //串口初始化

LED_Configuration();

OSInit(); //usos ii初始化

CommQ= OSQCreate(&CommMsg[0], 10); //建立消息队列 长度为10 OSQFlush(CommQ);

AppTaskCreate();//创建任务 OSStart(); //开始任务调度

}

static void AppTaskCreate(void) {

INT8U err;

OSTaskCreateExt(AppTask1,(void*)0,(OS_STK )&AppTask1Stk[APP_TASK1_STK_SIZE-1],APP_TASK1_PRIO,APP_TASK1_PRIO,(OS_STK)&AppTask1Stk[0],APP_TASK1_STK_SIZE,(void )0,OS_TASK_OPT_STK_CHK|OS_TASK_OPT_STK_CLR); //创建任务1

OSTaskNameSet(APP_TASK1_PRIO, \"AppTask1\&err);

OSTaskCreateExt(AppTask2,(void*)0,(OS_STK )&AppTask2Stk[APP_TASK2_STK_SIZE-1],APP_TASK2_PRIO,APP_TASK2_PRIO,(OS_STK )&AppTask2Stk[0],APP_TASK2_STK_SIZE,(void*)0,OS_TASK_OPT_STK_CHK|OS_TASK_OPT_STK_CLR); //创建任务2

OSTaskNameSet(APP_TASK2_PRIO, \"AppTask2\&err); }

//任务1

static void AppTask1 (void *p_arg) {

INT8U err;

void *msg; while(1) {

msg= OSQPend(CommQ, 100, &err);

if (err == OS_NO_ERR){//读取成功,打印消息

printf(\"\\n\\r读取队列成功:%s\\r\\n\*)msg); } else{

printf(\"\\n\\r读取失败\\r\\n\"); //读取失败 }

OSTimeDlyHMSM(0,0,0,500); LED1(1);

OSTimeDlyHMSM(0,0,0,500); LED1(0); } }

INT8U *CommRxBuf=\"bbs.openmcu.com\"; //任务2

static void AppTask2 (void *p_arg)

{

INT8U err; while(1) {

err= OSQPost(CommQ, (void *)&CommRxBuf[0]); if (err == OS_NO_ERR){

printf(\"\\n\\r消息加入队列中 \\r\\n\"); //将消息放入消息队列

} else{

printf(\"\\n\\r队列已满 \\r\\n\"); //消息队列已满

}

OSTimeDlyHMSM(0,0,0,500); } }

由于写入消息队列的速度快于读出队列的速度,因此到实验做了一段时间后会出现消息队列满的现象,等待消息被读出后,才能继续往队列里面写数据,通过串口打印的现象如下:

实验四、信号量

本节我们主要介绍的是uC/OS-II的信号量 1、信号量介绍

信号量的本质是一种数据操作锁,它本身不具有数据交换的功能,而是通过控制其他的通信资源(文件,外部设备)来实现进程间通信,它本身只是一种外部资源的标识。信号量在此过程中负责数据操作的互斥、同步等功能。当请求一个使用信号量来表示的资源时,进程需要先读取信号量的值来判断资源是否可用。如果大于0则资源可以请求,等于0则无资源可用,进程会进入睡眠状态直至资源可用。

µC/OS-II中的信号量由两部分组成:一个是信号量的计数值,它是一个16位的无符号整数(0到65,535之间);另一个是由等待该信号量的任务组成的等待任务表。用户要在 OS_CFG.H中将 OS_SEM_EN 开关量常数置成 1,这样µC/OS-II才能支持信号量。在使用一个信号量之前, 首先要建立该信号量, 也即调用 OSSemCreate()函数,对信号量的初始计数值赋值。该初始值为0到 65,535之间的一个数。如果信号量是用来表示一个或者多个事件的发生, 那么该信号量的初始值应设为0。如果信号量是用于对共享资源的访问,那么该信号量的初始值应设为1(例如,把它当作二值信号量使用)。最后,如果该信号量是用来表示允许任务访问n 个相同的资源,那么该初始值显然应该是n,并把该信号量作为一个可计数的信号量使用。如下图是任务、中断服务子程序和信号量之间的关系。

2、信号量操作

µC/OS-II提供了一系列函数对信号量进行创建、获取和给出等。 1)创建信号量OSSemCreate()

OS_EVENT *OSSemCreate (INT16U cnt)

cnt 信号量的初始值 返回值 NULL创建失败 否则返回事件控制块指针

2)信号量获取OSSemPend( )等待和OSSemAccept()立即返回

void OSSemPend (OS_EVENT *pevent, INT16U timeout, INT8U *err) INT16U OSSemAccept (OS_EVENT *pevent)

pevent 创建队列时返回的指针 Timeout 阻塞超时时间。 err 错误类型 返回值 信号量计数值减一 3)发出信号量OSSemPost()

INT8U OSSemPost (OS_EVENT *pevent)

pevent 创建队列时返回的指针 返回值 有两个可能的返回值: 1.OS_NO_ERR 清空成功 2.OS_ERR_EVENT_TYPE 事件类型错误 3.OS_SEM_OVF 溢出 3)查询一个信号量的当前状态OSSemQuery()

INT8U OSSemQuery (OS_EVENT *pevent,OS_SEM_DATA *pdata) pevent 创建队列时返回的指针

pdata 用于记录信号量信息的数据结构 返回值 有三个可能的返回值: 1.OS_NO_ERR 清空成功 2.OS_ERR_EVENT_TYPE 事件类型错误 3.OS_SEM_OVF 溢出 3、实验分析

实验创建两个任务,任务二每隔500ms发送发送两次信号量,并打印发送状态,任务一则一直等待接收信号,并接下来进行两次无等待接收信号,并打印两次接收的状态。其实验代码如下: int main (void)

{

INT8U err;

SysTick_Configuration(); //系统定时器初始化 USART_Configuration(); //串口初始化 LED_Configuration();

OSInit(); //usos ii初始化

DispSem= OSSemCreate(1); //建立显示设备的信号量 AppTaskCreate();//创建任务 OSStart(); //开始任务调度

}

static void AppTaskCreate(void) {

INT8U err;

OSTaskCreateExt(AppTask1,(void*)0,(OS_STK )&AppTask1Stk[APP_TASK1_STK_SIZE-1],APP_TASK1_PRIO,APP_TASK1_PRIO,(OS_STK)&AppTask1Stk[0],APP_TASK1_STK_SIZE,(void )0,OS_TASK_OPT_STK_CHK|OS_TASK_OPT_STK_CLR); //创建任务1

OSTaskNameSet(APP_TASK1_PRIO, \"AppTask1\&err);

OSTaskCreateExt(AppTask2,(void*)0,(OS_STK )&AppTask2Stk[APP_TASK2_STK_SIZE-1],APP_TASK2_PRIO,APP_TASK2_PRIO,(OS_STK )&AppTask2Stk[0],APP_TASK2_STK_SIZE,(void*)0,OS_TASK_OPT_STK_CHK|OS_TASK_OPT_STK_CLR); //创建任务2

OSTaskNameSet(APP_TASK2_PRIO, \"AppTask2\&err); }

//任务1

static void AppTask1 (void *p_arg) {

INT8U err; while(1) {

OSSemPend(DispSem, 0, &err);

//P操作等待信号量置起才继续运行,最大为65535个时钟节拍,0为无限等待

printf(\"\\n\\rOSSemPend成功\\r\\n\");

err= OSSemAccept(DispSem); //P操作,立即返回,若成功err大于0 if (err> 0){

printf(\"\\n\\rOSSemAccept成功\\r\\n\"); //P操作成功 }else {

printf(\"\\n\\rOSSemAccept失败\\r\\n\"); //P操作失败 }

err= OSSemAccept(DispSem); //P操作,立即返回,若成功err大于0 if (err> 0){

printf(\"\\n\\rOSSemAccept成功\\r\\n\"); //P操作成功

}else {

printf(\"\\n\\rOSSemAccept失败\\r\\n\"); //P操作失败 } } }

//任务2

static void AppTask2 (void *p_arg) {

INT8U err; while(1) {

err= OSSemPost(DispSem); //V操作 err= OSSemPost(DispSem); //V操作 if (err == OS_NO_ERR){

printf(\"\\n\\r信号量置起\\r\\n\");//V操作成功 }else{

printf(\"\\n\\r信号量溢出\\r\\n\");//V操作失败

}

OSTimeDlyHMSM(0,0,0,500); } }

任务2每500ms发送两次V操作(发送信号),操作成功打印“信号量置起”,任务1循环查询一次等待获取信号量,后两次立即返回获取信号量,由于每次循环任务2进行两次V操作,而任务1进行一次等待获取信号量后,又立即获取两次(时间间隔很短),所以第一个立即获取成功,第二个立即获取失败。打印输出“OSSemPend成功”“OSSemAccept成功”和“OSSemAccept失败”,从串口打印如下:

实验五、互斥锁

本节我们主要介绍的是uC/OS-II的互斥锁 1、互斥锁

互斥锁即互斥型信号量,是一种特殊的信号量,他除了具有普通信号量的机制外,还有一些其他的特性。互斥信号量可以在应用程序中解决优先级反转问题。当高优先级的任务需要使用某个共享资源,而该资源已被一个低优先级的任务占用时,就会发生优先级反转。为了解决优先级反转,内核可以将低优先级任务的优先级提升到高于那个高优先级的任务,知道低优先级的任务使用完占用的共享资源。

如下图是任务与互斥锁之间的关系流程图,互斥锁智能提供任务使用。

2、互斥锁操作

µC/OS-II提供了一系列函数对互斥锁进行创建、上锁与解锁等。 1)创建互斥锁OSMutexCreate();

OS_EVENT *OSMutexCreate (INT8U prio, INT8U *perr) prio 优先级 perr 错误类型 返回值 NULL创建失败

否则返回事件控制块指针

2)上锁OSMutexPend( )等待和OSMutexAccept()立即返回

void OSMutexPend (OS_EVENT *pevent, INT16U timeout, INT8U *perr) BOOLEAN OSMutexAccept (OS_EVENT *pevent, INT8U *perr) pevent 创建互斥锁时返回的指针 timeout 阻塞超时时间。 err 错误类型 返回值 OS_TRUE上锁成功 OS_FALSE上锁失败 3)解锁OSMutexPost()

INT8U OSMutexPost (OS_EVENT *pevent)

pevent 创建队列时返回的指针 返回值 有六个可能的返回值:

1.OS_ERR_POST_ISR 不能从中断程序中发送 2.OS_ERR_PEVENT_NULL 不存在

3.OS_ERR_EVENT_TYPE 事件类型错误

4.OS_ERR_NOT_MUTEX_OWNER 不是本任务上锁

5.OS_ERR_PIP_LOWER 互斥锁的优先级低于当前任务的优先级 6.OS_ERR_NONE 解锁成功 4)获取互斥锁当前状态OSMutexQuery()

INT8U OSMutexQuery (OS_EVENT *pevent, OS_MUTEX_DATA *p_mutex_data) pevent 创建队列时返回的指针 pdata 用于记录互斥锁的数据结构 返回值 有五个可能的返回值:

1.OS_ERR_QUERY_ISR 不能从中断程序中发送 2.OS_ERR_PEVENT_NULL 不存在

3.OS_ERR_PDATA_NULL 记录数据结构不存在 4.OS_ERR_EVENT_TYPE 事件类型错误 5.OS_ERR_NONE 获取成功 3、实验分析

实验创建两个任务,两个任务依次进行上锁,上锁后打印出字符串,然后解锁。其实验代码如下:

OS_EVENT *ResourceMutex; int main (void) {

INT8U err;

SysTick_Configuration(); //系统定时器初始化 USART_Configuration(); //串口初始化

LED_Configuration();

OSInit(); //usos ii初始化

ResourceMutex=OSMutexCreate(9, &err); //创建互斥锁优先级为9 AppTaskCreate();//创建任务 OSStart(); //开始任务调度 }

static void AppTaskCreate(void) {

INT8U err;

OSTaskCreateExt(AppTask1,(void*)0,(OS_STK )&AppTask1Stk[APP_TASK1_STK_SIZE-1],APP_TASK1_PRIO,APP_TASK1_PRIO,(OS_STK)&AppTask1Stk[0],APP_TASK1_STK_SIZE,(void )0,OS_TASK_OPT_STK_CHK|OS_TASK_OPT_STK_CLR); //创建任务1

OSTaskNameSet(APP_TASK1_PRIO, \"AppTask1\&err);

OSTaskCreateExt(AppTask2,(void*)0,(OS_STK )&AppTask2Stk[APP_TASK2_STK_SIZE-1],APP_TASK2_PRIO,APP_TASK2_PRIO,(OS_STK )&AppTask2Stk[0],APP_TASK2_STK_SIZE,(void*)0,OS_TASK_OPT_STK_CHK|OS_TASK_OPT_STK_CLR); //创建任务2

OSTaskNameSet(APP_TASK2_PRIO, \"AppTask2\&err); }

//任务1

static void AppTask1 (void *p_arg) {

INT8U err; while(1) {

OSMutexPend(ResourceMutex, 0, &err); //获取资源,无限等待 LED1(1);

printf(\"\\n\\r任务1获取资源\\r\\n\");

OSTimeDlyHMSM(0,0,1,0);

printf(\"\\n\\r任务1释放资源\\r\\n\"); OSMutexPost(ResourceMutex);

//释放资源时进行任务调度 所以把输出写前面 LED1(0); } }

//任务2

static void AppTask2 (void *p_arg) {

INT8U err; while(1) {

OSMutexPend(ResourceMutex, 0, &err); // 获取资源,无限等待 printf(\"\\n\\r任务2获取资源 \\r\\n\"); LED2(1);

OSTimeDlyHMSM(0,0,0,500); printf(\"\\n\\r任务2释放资源\\r\\n\");

//释放资源时进行任务调度 所以把输出写前面 OSMutexPost(ResourceMutex); LED2(0);

} }

程序中每个任务获得互斥锁后打印字符串,并延时1s钟后解锁,这样下个任务才能进行上锁,完成自己的任务。其程序从串口打印如下

实验六、邮箱

本节我们主要介绍的是uC/OS-II的邮箱 1、邮箱介绍

µC/OS-II中的邮箱也称为消息邮箱, 是用一个指针型变量, 通过内核服务, 一个任务或一个中断服务程序可以把一则消息(即一个指针)放到邮箱里去。同样,一个或多个任务可以通过内核服务接收这则消息。发送消息的任务和接收消息的任务约定,该指针指向的内容就是那则消息。消息邮箱与消息队列类似,消息邮箱相当于消息队列的一个成员,消息队列实际上是邮箱阵列。如下图是任务、中断服务子程序和邮箱之间的关系。

2、邮箱操作

µC/OS-II提供了一系列函数对邮箱进行创建、获取和给出等。 1)创建邮箱OSMboxCreate()

OS_EVENT *OSMboxCreate (void *msg)

msg 邮箱的初始指针为0则无初值 返回值 NULL创建失败 否则返回事件控制块指针

2)信号量获取OSSemPend( )等待和OSMboxAccept()立即返回 void *OSMboxPend (OS_EVENT *pevent,INT16Utimeout, INT8U*err) void *OSMboxAccept (OS_EVENT *pevent)

pevent 创建邮箱时返回的指针 Timeout 阻塞超时时间。 err 错误类型 返回值 NULL获取失败 邮件指针

3)发出邮箱OSMboxPost()

INT8U OSMboxPost (OS_EVENT *pevent,void *msg) pevent 创建邮箱时返回的指针 msg 要发送的邮箱指针 返回值 有两个可能的返回值: 1.OS_NO_ERR 清空成功 2.OS_ERR_EVENT_TYPE 事件类型错误 3.OS_MBOX_FULL 邮箱满 3)查询一个信号量的当前状态OSMboxQuery()

INT8U OSMboxQuery (OS_EVENT*pevent, OS_MBOX_DATA *pdata) pevent 创建邮箱时返回的指针

pdata 用于记录邮箱信息的数据结构 返回值 有两个可能的返回值: 1.OS_NO_ERR 清空成功 2.OS_ERR_EVENT_TYPE 事件类型错误 3、实验分析

实验创建两个任务,任务二每隔500ms发送发送一次邮箱,并打印发送状态,任务一则一直阻塞等待接收邮箱,并打印接受到的邮箱。其实验代码如下: int main (void) {

INT8U err;

SysTick_Configuration(); //系统定时器初始化 USART_Configuration(); //串口初始化 LED_Configuration();

OSInit(); //usos ii初始化

CommMbox = OSMboxCreate((void *)0); //建立消息邮箱 AppTaskCreate();//创建任务

OSStart(); //开始任务调度 }

static void AppTaskCreate(void) {

INT8U err;

OSTaskCreateExt(AppTask1,(void*)0,(OS_STK )&AppTask1Stk[APP_TASK1_STK_SIZE-1],APP_TASK1_PRIO,APP_TASK1_PRIO,(OS_STK)&AppTask1Stk[0],APP_TASK1_STK_SIZE,(void )0,OS_TASK_OPT_STK_CHK|OS_TASK_OPT_STK_CLR); //创建任务1

OSTaskNameSet(APP_TASK1_PRIO, \"AppTask1\&err);

OSTaskCreateExt(AppTask2,(void*)0,(OS_STK )&AppTask2Stk[APP_TASK2_STK_SIZE-1],APP_TASK2_PRIO,APP_TASK2_PRIO,(OS_STK )&AppTask2Stk[0],APP_TASK2_STK_SIZE,(void*)0,OS_TASK_OPT_STK_CHK|OS_TASK_OPT_STK_CLR); //创建任务2

OSTaskNameSet(APP_TASK2_PRIO, \"AppTask2\&err); }

//邮箱接收任务

static void AppTask1 (void *p_arg) {

INT8U err; void *msg; while(1) {

msg=OSMboxPend(CommMbox,600,&err);//等待接收邮箱最大时间600ms if (err == OS_NO_ERR){

printf(\"接收到消息%s\\r\\n\*)msg);//消息正确的接受 } else{

printf(\"没有接收到消息\\r\\n\");//在指定时间内没有接受到消息 } } }

//邮箱发送任务

static void AppTask2 (void *p_arg) {

INT8U CommRxBuf[100]=\"bbs.openmcu.com\"; INT8U err; while(1) {

err= OSMboxPost(CommMbox, (void *)CommRxBuf); if (err == OS_NO_ERR){

printf(\"发送成功\\r\\n\");//发送成功 } else{

printf(\"发送失败\\r\\n\");//发送失败 }

OSTimeDlyHMSM(0,0,0,500); }

}

本程序为ucos的另一种通信方式邮箱,工程创建两个任务,一个每隔500ms发送一个邮箱,一个等待接收邮箱最大等待时间600ms,任务二发送邮箱并打印“发送成功”,任务一接收到邮箱后打印“接收到消息bbs.openmcu.com”。

实验七、事件标志组

本节我们主要介绍的是uC/OS-II的事件标志组 1、事件标志组介绍

µC/OS-II中当某任务要与多个事件同步时,要使用事件标志。若任务需要与任何事件之一发生同步,可称为型同步(即逻辑或关系)。任务也可以与若干事件都发生了同步,称之为关联型(逻辑与关系)。型及关联型同步如图所示。

2、事件标志组操作

µC/OS-II提供了一系列函数对邮箱进行创建、获取和给出等。 1)创建事件标志组OSFlagCreate ()

OS_FLAG_GRP *OSFlagCreate (OS_FLAGS flags, INT8U *perr) flags 事件标志组初始化值 Perr 错误标志 返回值 标志组指针 2)等待标志置位获取OSFlagPend ()

OS_FLAGS OSFlagPend (OS_FLAG_GRP *pgrp, OS_FLAGS flags, INT8U wait_type, INT16U timeout, INT8U *perr)

pgrp 标志组指针 flags 等待标志位

wait_type 设置操作类型‘与’‘或’

Timeout 等待事件 Perr 错误类型 3)发出邮箱OSFlagPost()

OS_FLAGS OSFlagPost (OS_FLAG_GRP *pgrp, OS_FLAGS flags, INT8U opt, INT8U *perr)

pgrp 标志组指针 flags 等待标志位

opt 置位还是清零 Perr 错误类型 3、实验分析

实验创建三个任务,任务一等待事件标志组bit0和bit1置位,任务二置位标志组bit1位,任务三置位标志组bit0位。其实验代码如下: int main (void) {

SysTick_Configuration(); //系统定时器初始化 USART_Configuration(); //串口初始化 LED_Configuration();

OSInit(); //usos ii初始化

Sem_F=OSFlagCreate(0,&err);

OSFlagPost(Sem_F,(OS_FLAGS)0,OS_FLAG_CLR,&err); AppTaskCreate();//创建任务

OSStart(); //开始任务调度

}

static void AppTaskCreate(void) {

INT8U err;

OSTaskCreateExt(AppTask1,(void*)0,(OS_STK )&AppTask1Stk[APP_TASK1_STK_SIZE-1],APP_TASK1_PRIO,APP_TASK1_PRIO,(OS_STK)&AppTask1Stk[0],APP_TASK1_STK_SIZE,(void )0,OS_TASK_OPT_STK_CHK|OS_TASK_OPT_STK_CLR); //创建任务1

OSTaskNameSet(APP_TASK1_PRIO, \"AppTask1\&err);

OSTaskCreateExt(AppTask2,(void*)0,(OS_STK )&AppTask2Stk[APP_TASK2_STK_SIZE-1],APP_TASK2_PRIO,APP_TASK2_PRIO,(OS_STK )&AppTask2Stk[0],APP_TASK2_STK_SIZE,(void*)0,OS_TASK_OPT_STK_CHK|OS_TASK_OPT_STK_CLR); //创建任务2

OSTaskNameSet(APP_TASK2_PRIO, \"AppTask2\&err);

OSTaskCreateExt(AppTask3,(void*)0,(OS_STK )&AppTask3Stk[APP_TASK3_STK_SIZE-1],APP_TASK3_PRIO,APP_TASK3_PRIO,(OS_STK )&AppTask3Stk[0],APP_TASK3_STK_SIZE,(void*)0,OS_TASK_OPT_STK_CHK|OS_TASK_OPT_STK_CLR); //创建任务3

OSTaskNameSet(APP_TASK2_PRIO, \"AppTask3\&err); }

//任务1

static void AppTask1 (void *p_arg)

{

while(1) {

OSFlagPend(Sem_F,(OS_FLAGS)3,OS_FLAG_WAIT_SET_ALL+OS_FLAG_CONSUME, 0,&err); //等待bit0 bit1置位,全部置位后清除

printf(\"\\n\\r等待的标志位置位,清除\\r\\n\"); LED1(0);

OSTimeDlyHMSM(0,0,1,0); LED1(1);

OSTimeDlyHMSM(0,0,1,0); } }

//任务2

static void AppTask2 (void *p_arg) {

while(1) {

OSFlagPost(Sem_F,(OS_FLAGS)2,OS_FLAG_SET,&err); //置位bit1 printf(\"\\n\\r标志位1位置高\\r\\n\"); OSTimeDlyHMSM(0,0,1,500); } }

//任务3

static void AppTask3 (void *p_arg) {

while(1) {

OSFlagPost(Sem_F,(OS_FLAGS)1,OS_FLAG_SET,&err); //置位bit0 printf(\"\\n\\r标志位0位置高 \\r\\n\"); OSTimeDlyHMSM(0,0,1,500); }

}

本程序为ucos的另一种通信方式邮箱,程序创建三个任务,任务一等待事件标志组bit0和bit1置位,任务二置位标志组bit1位,任务三置位标志组bit0位。任务一等待标志位置位后清除标志位并打印信息。其串口打印如下

因篇幅问题不能全部显示,请点此查看更多更全内容

Copyright © 2019- huatuo0.cn 版权所有 湘ICP备2023017654号-2

违法及侵权请联系:TEL:199 18 7713 E-MAIL:2724546146@qq.com

本站由北京市万商天勤律师事务所王兴未律师提供法律服务