CH04-03-操作系统原理与实践-μCOS-III任务通信_第1页
CH04-03-操作系统原理与实践-μCOS-III任务通信_第2页
CH04-03-操作系统原理与实践-μCOS-III任务通信_第3页
CH04-03-操作系统原理与实践-μCOS-III任务通信_第4页
CH04-03-操作系统原理与实践-μCOS-III任务通信_第5页
已阅读5页,还剩82页未读 继续免费阅读

下载本文档

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

文档简介

《嵌入式系统设计原理》嵌入式操作系统原理与实践μC/OS-III通信机制主讲人:赖树明东莞理工学院05事件标志组01通信机制概述02信号量03互斥信号量04消息队列01通信机制概述通信机制概述01使用实时操作系统,相对于裸机程序的一个优势是可以把复杂的功能划分到不同的任务中,但这些任务的数据以及逻辑功能是存在密切的联系的,因此需要有一种机制可以实现任务之间的通信,以便于使用这些独立的任务可以按我们的设想协调运行起来,uc/OS-III操作系统提供了丰富的任务间通信的机制,可以让我们根据使用场景,选择不同的通信方式。概述A信号量B互斥信号量C消息队列D事件标志组E任务信号量F任务消息队列概述02信号量信号量介绍API函数接口任务同步示例任务互斥示例信号量02信号量(Semaphore)是一种实现任务间通信的机制,用于任务之间同步或临界资源的互斥访问。通俗理解:信号量是一个正值变量,表示资源的可申请数,当任务申请信号量时,变量值减1,任务使用完成后,再释放信号量,效果是变量值

加1;信号量为0时,申请信号号的任务则无法获得信号,表现为退出或者等待挂起,直到有其他任务释放了信号量,这些等待的任务会按照优先级来获取信号量,继续执行其代码。概念信号量介绍信号量可以细分为两种类型:二进制信号量和计数信号量。二进制信号量只能取两个值:0或者1,在开发中使用最多的还是是二值信号量这种情形;计数信号量允许的值介于0~255/65535/4294967295之间,具体取决于信号量机制是使用8位、16位,还是32位数据类型实现的。对于μC/OS-III,信号量的最大值由数据类型OS_SEM_CTR(见os_type.h)决定,可以根据需要更改其他类似。特征信号量的使用流程有三个必需的步骤,分别是创建信号量、申请信号量、发送信号量,其他可选操作有删除信号量、中止等待信号量、修改信号值等。使用流程表4.1信号量函数序号函数名功能描述备注1OSSemCreate()创建一个信号量必须调用2OSSemDel()删除一个信号量必须调用3OSSemPend()等待信号量必须调用4OSSemPendAbort()中止对信号量的等待可选调用5OSSemPost()发送或发出信号量信号可选调用6OSSemSet()强制信号量计数为所需值可选调用信号量02API函数接口μC/OS-III系统使用OS_SEM结构来表示一个信号量,创建信号量,只需要调用系统提供的OSSemCreate函数,根据需要传递必须参数即可创建信号量原型:voidOSSemCreate(OS_SEM*p_sem,CPU_CHAR*p_name,OS_SEM_CTRcnt,RTOS_ERR*p_err)功能:创建信号量参数:p_sem:指向要初始化的信号量的指针,一般传递OS_SEM类型变量地址。p_name:指向要分配给信号量名称的指针。cnt:信号量的初始值,如果用于共享资源,则应初始化为可用资源数;如用于表示事件的发生,则应初始化为0。p_err:指向存放错误代码变量的地址,函数调用可能产生的错误码如下:OS_ERR_NONE:无错误,指示信号量创建成功;OS_ERR_CREATE_ISR:在中断服务程序中调用了此函数;OS_ERR_ILLEGAL_CREATE_RUN_TIME:在调用OSSafetyCriticalStart()后尝试创建信号量。OS_ERR_NAME:如果'p_name'是一个NULL指针OS_ERR_OBJ_CREATED:如果信号量已经创建,即p_sem前面已经被创建过了。OS_ERR_OBJ_PTR_NULL:如果'p_sem'是一个NULL指针OS_ERR_OBJ_TYPE:p_sem类型不是OS_SEM*;信号量02API函数接口示例代码OS_SEMmy_sem_test;

//定义信号量全局变量,用于任务同步。//创建一个信号量OSSemCreate((OS_SEM*)&my_sem_test,(CPU_CHAR*)"MySemTest",(OS_SEM_CTR)0,(OS_ERR*)&err);//创建失败,让程序进入死循环,这样在开发阶段方便发现问题。if(err!=OS_ERR_NONE){ while(1){;}}信号量02API函数接口调用OSSemPend函数申请指定的信号量,如果所申请的信号量其信号值大于0,则马上获得信号量,同时把信号值减去1,继续执行后面的代码。如果当前信号值是0,则表示当前信号量不可用,此时任务可以选择继续往下运行或者进入挂起状态等待信号量变成正数。申请信号量函数原型:OS_SEM_CTROSSemPend(OS_SEM*p_sem,OS_TICKtimeout,

OS_OPTopt,CPU_TS*p_ts,OS_ERR*p_err)函数功能:申请信号量,申请成功任务继续往下运行,信号量不可用时,任务挂起或者返回错误码后继续往下运行(具体哪一种情况由调用函数时给opt参数传递的值决定)。函数参数:p_sem:是指向信号量的指针,一般传递OS_SEM类型变量地址;

timeout:超时时间(以时钟节拍为单位)。当opt参数传递为OS_OPT_PEND_BLOCKING,timeout值为大于0时,表示信号量任务挂起的最长等待时间,如超过在该参数指定的时长,信号量还不可用,则任务恢复运行,并且将p_err指向的错误码变量值设置为OS_ERR_TIMEOUT。当值为0时,表示任务将永久挂起,直到等待的信号量变成可用,然后获得信号量恢复运行。

opt:OS_OPT_PEND_BLOCKING:任务会阻塞;OS_OPT_PEND_NON_BLOCKING:任务不会阻塞.

p_ts:用于存放信号量释放/挂起中止/信号量删除时的时间戳。如不需要时间戳,传NULL。

p_err:指向存放错误代码变量的地址,函数调用可能产生的错误码如下:

OS_ERR_NONE:指示成功获得信号量;.......

OS_ERR_PEND_ISR:指示从ISR调用此函数,结果将导致挂起;

OS_ERR_TIMEOUT:指示在指定的超时时间内未收到信号量。函数返回值:信号量计数器的当前值,值为0表示当前信号量不可用。一般是通过p_err指向的错误码来判断申请结果。信号量02API函数接口示例代码函数示例:假设当前信号量my_sem_test已经创建好了。OS_ERRerr;//存放函数调用错误码//没有获得信号量会挂起任务OSSemPend(&my_sem_test,0,OS_OPT_PEND_BLOCKING,0,&err);//请求信号量//判断申请信号量是否成功if(err!=OS_ERR_NONE){//在以下编写没有正确获得信号量但是函数返回时的处理代码……//根据实际情况编写出错处理代码}

信号量02API函数接口本示例是使用信号量实现两个任务同步,演示通过信号量实现任务间通信的方法。任务1负责检测按键1是否按下,按下了则发送信号量;任务2负责申请信号量,等待任务1发送信号量后,往下执行代码,控制开发板上的LED。实验的效果是每按下按键1一次,开发板上的LED就会反转一次。发送信号量信号量02任务同步示例主函数OS_SEMkey_sem;//定义一个信号量,用于任务同步intmain(void){OS_ERRerr;CPU_SR_ALLOC();......OSInit(&err);//初始化μC/OS-III//创建一个信号量OSSemCreate((OS_SEM*)&key_sem,(CPU_CHAR*)"key_sem",(OS_SEM_CTR)0,//信号值初始值设置为0(OS_ERR*)&err);//创建失败,让程序进入死循环,这样在开发阶段方便发现问题。if(err!=OS_ERR_NONE){while(1){;}}OS_CRITICAL_ENTER();//进入临界区//创建开始任务.......//调用OSTaskCreate创建启动任务OS_CRITICAL_EXIT();//退出临界区OSStart(&err);//开启μC/OS-III}信号量02任务同步示例主函数OS_SEMkey_sem;//定义一个信号量,用于任务同步intmain(void){OS_ERRerr;CPU_SR_ALLOC();......OSInit(&err);//初始化μC/OS-III//创建一个信号量OSSemCreate((OS_SEM*)&key_sem,(CPU_CHAR*)"key_sem",(OS_SEM_CTR)0,//信号值初始值设置为0(OS_ERR*)&err);//创建失败,让程序进入死循环,这样在开发阶段方便发现问题。if(err!=OS_ERR_NONE){while(1){;}}OS_CRITICAL_ENTER();//进入临界区//创建开始任务.......//调用OSTaskCreate创建启动任务OS_CRITICAL_EXIT();//退出临界区OSStart(&err);//开启μC/OS-III}信号量02任务同步示例任务1函数//任务1的任务函数voidtask1_task(void*p_arg){

u8key;

OS_ERRerr;

while(1)

{ key=KEY_Scan(0);

//扫描按键 if(key==KEY1_PRES)

//如果按下开发板按键1 { printf("Task1:发送一个信号量\r\n");

//输出发送信号量提示 OSSemPost(&key_sem,OS_OPT_POST_1,&err);

//发送信号量 printf("当前信号值:%u\r\n",key_sem.Ctr); } OSTimeDlyHMSM(0,0,0,10,OS_OPT_TIME_PERIODIC,&err);//延时10ms

}}信号量02任务同步示例任务1函数voidtask2_task(void*p_arg){u8num;OS_ERRerr;while(1){//没有获得信号量会挂起任务OSSemPend(&key_sem,0,OS_OPT_PEND_BLOCKING,0,&err);//请求信号量//判断申请信号量是否成功if(err!=OS_ERR_NONE){//在以下编写没有正确获得信号量但是函数返回时的处理代码//......}LED1_Toglge();//翻转LED1状态}}信号量02任务同步示例本示例是使用信号量实现对共享资源的互斥访问,演示通过信号量实现任务间通信的另一种使用场景。任务1和任务2都需要访问一个共享资源,即一块数据缓冲区,程序中可表示为一个数组,其中任务1对这块数组进行写操作,任务2负责读取出任务1写入的数据。为了保证任务2每次都可以完整读取任务1的一次完成写操作数据,保证数据不会混乱,则任务1和任务2在访问这块共享的内存(数组)时都需要申请同一个信号量,如果信号量被其中一个持有了,则需要等待对方访问完共享资源,然后发送信号量,才可以访问共享资源,从而保证了共享资源的互斥访问。功能说明信号量02任务互斥示例示例框图信号量02任务互斥示例主函数OS_SEMshare_mem_sem;//定义一个信号量,用于保护共享资访问。intmain(void){OS_ERRerr;CPU_SR_ALLOC();......OSInit(&err);//初始化μC/OS-III//创建一个信号量share_mem_semOSSemCreate((OS_SEM*)&share_mem_sem,(CPU_CHAR*)"share_mem_sem",(OS_SEM_CTR)1,//注意本示例初始值不能为0(OS_ERR*)&err);//创建失败,让程序进入死循环,这样在开发阶段方便发现问题。if(err!=OS_ERR_NONE){while(1){;}}OS_CRITICAL_ENTER();//进入临界区……//调用OSTaskCreate创建启动任务。OS_CRITICAL_EXIT();//退出临界区OSStart(&err);//开启μC/OS-III}信号量02任务互斥示例主函数voidtask1_task(void*p_arg){ OS_ERRerr; uint32_tcnt=1;//记录写入数据的次数 while(1) { OSSemPend(&share_mem_sem,0,OS_OPT_PEND_BLOCKING,0,&err);//请求信号量 //判断申请信号量是否成功 if(err!=OS_ERR_NONE){ //在以下编写没有正确获得信号量但是函数返回时的处理代码 } //以下开始访问共享资源,往array_buf数组中写入数据 sprintf(array_buf,"cnt:%05d",cnt);//开始访问共享资源 printf("第%d次写入数据:%s\r\n",cnt,array_buf);//输出写入内容提示 cnt++;//写入次数增加 OSSemPost(&share_mem_sem,OS_OPT_POST_1,&err);//释放信号量 OSTimeDlyHMSM(0,0,1,0,OS_OPT_TIME_PERIODIC,&err);//延时1s }}信号量02任务互斥示例任务1函数//任务1的任务函数voidtask1_task(void*p_arg){

u8key;

OS_ERRerr;

while(1)

{ key=KEY_Scan(0);

//扫描按键 if(key==KEY1_PRES)

//如果按下开发板按键1 { printf("Task1:发送一个信号量\r\n");

//输出发送信号量提示 OSSemPost(&key_sem,OS_OPT_POST_1,&err);

//发送信号量 printf("当前信号值:%u\r\n",key_sem.Ctr); } OSTimeDlyHMSM(0,0,0,10,OS_OPT_TIME_PERIODIC,&err);//延时10ms

}}信号量02任务互斥示例任务2函数voidtask2_task(void*p_arg){OS_ERRerr;charread_buf[50]={0};//用于存放临时数据uint32_tcnt=1;

//记录读取数据的次数while(1){

//没有获得信号量会挂起任务OSSemPend(&share_mem_sem,0,OS_OPT_PEND_BLOCKING,0,&err);if(err!=OS_ERR_NONE){//在以下编写没有正确获得信号量但是函数返回时的处理代码....}//以下开始访问共享资源,把array_buf数组中的字符串复制到read_buf中strcpy(read_buf,array_buf);//开始访问共享资源//输出读取到的内容提示,写入次数增加printf("第%d次读取数据:%s\r\n",cnt++,read_buf);OSSemPost(&share_mem_sem,OS_OPT_POST_1,&err);//释放信号量OSTimeDlyHMSM(0,0,0,500,OS_OPT_TIME_PERIODIC,&err);}}信号量02任务互斥示例03互斥信号量互斥信号量介绍API函数接口任务互斥示例----------优先级反转,是指某个共享资源被较低优先级的任务所持有,较高优先级的任务任务申请该共享资源无法获得,导致较高优先级任务反而被更低优先级的任务推迟被调度执行的现象。互斥信号量03互斥信号量介绍优先级反转概念μC/OS-III支持一种特殊类型的二值互斥信号量,称为互斥信号量(也称为互斥体),相对于前面学习过普通二值互斥信号量,它解决了优先级反转的问题。接下来的先来学习什么是什么级转换,才可以更好理解互斥信号量的作用。互斥信号量概念右图展示一个优先级反转的示例:系统中有高(TaskH),中(TaskM),低(TaskL)三个级别优先级任务,某一时刻低TaskL任务持有信号量S,此时TaskH就绪抢占TaskL,然后申请信号量S,但是此时信号量被TaskL持有,因此TaskH挂起,放弃CPU。然后TaskL恢复运行,运行过程中TaskM就绪抢占TaskL,TaskM没有申请信号量S,因此它可以长时间持有CPU,这样就导致了TaskH反而被比它低的任务TaskM推迟调度,因为只有TaskM释放CPU,TaskL才可以继续运行,从而释放信号量,Task才有机会运行,这现象就是优先级反转。互斥信号量03互斥信号量介绍优先级反转示例互斥信号量的使用流程很简单:创建互斥信号量、申请互斥信号量、释放互斥信号量三个必需的步骤,其他可选操作有删除互斥信号量、中止等待互斥信号量。互斥信号量接口互斥信号量03API函数接口

互斥信号量函数序号函数名功能描述备注1OSMutexCreate()创建一个互斥信号量必须调用2OSMutexDel()删除一个互斥信号量必须调用3OSMutexPend()等待互斥信号量必须调用4OSMutexPendAbort()中止对互斥信号量的等待可选调用5OSMutexPost()释放互斥信号量可选调用μC/OS-III系统使用OS_MUTEX结构来表示一个互斥信号量,要创建互斥信号量,我们只需要调用系统提供的OSMutexCreateAPI函数,根据需要传递必须参数即可创建互斥信号量互斥信号量03API函数接口函数原型:voidOSMutexCreate(OS_MUTEX*p_mutex,CPU_CHAR*p_name,OS_ERR*p_err)函数功能:创建互斥信号量函数参数:p_mutex:指向要初始化的互斥信号量的指针,一般是传递OS_MUTEX类型的变量地址。

p_name:指向要分配给互斥信号量的名称的指针

p_err:指向存放错误代码变量的指针,函数调用可能产生的错误码如下:

OS_ERR_NONE:指示互斥信号量创建成功;

OS_ERR_CREATE_ISR:指示在中断服务程序中调用了此函数;

OS_ERR_NAME:指令'p_name'是一个NULL指针;

OS_ERR_OBJ_CREATED:指示p_mutex已经创建过了;

OS_ERR_OBJ_PTR_NULL:指示'p_mutex'是一个NULL指针;示例代码函数示例:OS_MUTEXmy_mutex_test;//定义互斥信号量全局变量,用于共享资源保护。OSMutexCreate((OS_MUTEX*)&my_mutex_test,(CPU_CHAR*)"my_mutex_test",(OS_ERR*)&err);//创建失败,让程序进入死循环,这样在开发阶段方便发现问题。if(err!=OS_ERR_NONE){ while(1){;}}示例说明:①使用互斥信号量时,互斥信号量变量需要定义为全局变量,否则其他任务不能使用;②不要在中断服务程序中去创建互斥信号量;备注:函数提供的错误码比较多,写代码时一般判断错误码值是否等于OS_ERR_NONE,如果不等于该值表示函数调用出错了,如果需要分析具体哪一种错误,再使用单步调试的方法来观察函数返回的错误码。互斥信号量03API函数接口调用OSMutexPend函数申请指定的互斥信号量,如果持有互斥信号量的任务优先级比当前申请互斥信号量的任务优先级低,则会把持有互斥信号量的任务的优先级临时提升到和当前申请互斥信号量的任务优先级一样。然后,当前任务进入挂起状态,把CPU归还给原来持有互斥信号量的任务,让持有互斥信号量的任务继续运行。当持有该互斥信号量的任务释放掉互斥信号量后,其优先级重新恢复到原来的优先级。互斥信号量03API函数接口申请互斥信号量函数原型:voidOSMutexPend(OS_MUTEX*p_mutex,OS_TICKtimeout,OS_OPTopt,CPU_TS*p_ts,OS_ERR*p_err)函数功能:申请指定的互斥信号量,函数形参:p_sem:指向互斥信号量的指针;timeout:超时时间(以时钟节拍为单位),用法和作用和申请信号量函数时一样。opt:用法和作用和申请信号量函数时一样。。p_ts:用于保存互斥信号量发布或挂起中止或互斥信号量删除时的时间戳。不需要时间戳,可传NULL。p_err:指向存放错误代码变量的指针,函数调用可能产生的错误码如下:

OS_ERR_NONE:指示成功获得互斥信号量

OS_ERR_PEND_ISR:指示在中断服务程序中调用了此函数,结果将导致挂起。

OS_ERR_TIMEOUT:指示在指定的超时时间内未能成功获得互斥信号量

.......示例代码函数示例:假设当前互斥信号量my_mutex_test已经创建好。OS_ERRerr;//存放函数调用错误码//没有获得互斥信号量会挂起任务OSMutexPend(&my_mutex_test,0,OS_OPT_PEND_BLOCKING,0,&err);//请求互斥信号量//判断互斥信号量是否成功获得if(err!=OS_ERR_NONE){ //在以下编写没有正确获得互斥信号量但是函数返回时的处理代码 ......//根据实际情况编写出错处理代码}互斥信号量03API函数接口调用OSMutexPost函数可以释放互斥信号量,如果有等待该互斥信号量的任务就绪,并比当前任务有更高的优先级,则执行任务调度,CPU执行切换到新任务执行。否则,原任务在释放互斥信号量之后继续执行后面代码。函数原型:voidOSMutexPost(OS_MUTEX*p_mutex,OS_OPTopt,OS_ERR*p_err)函数功能:释放互斥信号量,如果有等待此互斥信号量的高优先级任务,则马上发生任务切换,否则继续运行当前任务后面的代码。函数形参:p_sem:是指向互斥信号量的指针

opt:确定执行的POST类型

OS_OPT_POST_NONE:表示未选择特殊选项

OS_OPT_POST_NO_SCHED:表示如果你不希望在释放互斥信号量后开始调度程序p_err:指向存放错误代码变量的指针。函数调用可能产生的错误码如下:

OS_ERR_NONE:指示调用成功释放互斥信号量;

OS_ERR_MUTEX_NESTING:指示互斥锁拥有者嵌套了它对互斥锁的使用;

OS_ERR_MUTEX_NOT_OWNER:指示释放互斥信号量的任务不是互斥锁所有者;

OS_ERR_OBJ_PTR_NULL:指示'p_mutex'是一个NULL指针;

OS_ERR_OBJ_TYPE:指示'p_mutex'没有指向OS_MUTEX类型变量;

OS_ERR_POST_ISR:指示在中断服务程序中释放互斥信号量。互斥信号量03API函数接口释放互斥信号量示例代码函数示例:假设当前互斥信号量my_mutex_test已经创建好了。OS_ERRerr;//存放函数调用错误码OSMutexPost(&my_mutex_test,OS_OPT_POST_NONE,&err);//发送互斥信号量//判断互斥信号量是否成功发送,发送一般都不会出错,以下判断代码也可以不写if(err!=OS_ERR_NONE){ //在以下编写没有成功发送互斥信号量时的处理代码,一般情况不用写。 ……//}互斥信号量03API函数接口本示例是使用互斥信号量实现对共享资源的互斥访问。任务1和任务2都需要访问一个共享资源,即一块数据缓冲区,程序中可表示为一个数组,其中任务1对这块数组进行写操作,任务2负责读取出数据缓冲区任务1写入的数据。为了保证任务2每次都可以完整读取任务1的一次完成写操作,保证数据不会混乱,则任务1和任务2在访问这块共享的内存(数组)时都需要申请同一个互斥信号量,如果互斥信号量被其中一个任务持有了,则需要等待对方访问完共享资源,然后互斥信号量才可以访问共享资源,从而保证了共享资源的互斥访问。功能说明互斥信号量03任务互斥示例示例框图互斥信号量03任务互斥示例主函数OS_MUTEXshare_mem_mutex;//定义一个互斥信号量,用于保护共享资访问intmain(void){OS_ERRerr;CPU_SR_ALLOC();......OSInit(&err);//初始化μC/OS-III//创建一个互斥信号量OSMutexCreate((OS_MUTEX*)&share_mem_mutex,(CPU_CHAR*)"share_mem_mutex",(OS_ERR*)&err);//创建失败,让程序进入死循环,这样在开发阶段方便发现问题if(err!=OS_ERR_NONE){while(1){;}}OS_CRITICAL_ENTER();//进入临界区//调用OSTaskCreate创建启动任务,和前面讲解的代码相同,此时省略......OS_CRITICAL_EXIT();//退出临界区OSStart(&err);//开启μC/OS-III}互斥信号量03任务互斥示例主函数voidtask1_task(void*p_arg){ OS_ERRerr; uint32_tcnt=1;//记录写入数据的次数 while(1) { OSSemPend(&share_mem_sem,0,OS_OPT_PEND_BLOCKING,0,&err);//请求信号量 //判断申请信号量是否成功 if(err!=OS_ERR_NONE){ //在以下编写没有正确获得信号量但是函数返回时的处理代码 } //以下开始访问共享资源,往array_buf数组中写入数据 sprintf(array_buf,"cnt:%05d",cnt);//开始访问共享资源 printf("第%d次写入数据:%s\r\n",cnt,array_buf);//输出写入内容提示 cnt++;//写入次数增加 OSSemPost(&share_mem_sem,OS_OPT_POST_1,&err);//释放信号量 OSTimeDlyHMSM(0,0,1,0,OS_OPT_TIME_PERIODIC,&err);//延时1s }}互斥信号量03任务互斥示例任务1函数voidtask1_task(void*p_arg){OS_ERRerr;uint32_tcnt=1;//记录写入数据的次数while(1){

//请求互斥信号量OSMutexPend(&share_mem_mutex,0,OS_OPT_PEND_BLOCKING,0,&err);

if(err!=OS_ERR_NONE){//在以下编写没有正确获得互斥信号量但是函数返回时的处理代码//……} //以下开始访问共享资源,往array_buf数组中写入数据sprintf(array_buf,"cnt:%05d",cnt);//开始访问共享资源printf("第%d次写入数据:%s\r\n",cnt++,array_buf);//输出写入内容提示,写入次数增加OSMutexPost(&share_mem_mutex,OS_OPT_POST_NONE,&err);//释放互斥信号量OSTimeDlyHMSM(0,0,1,0,OS_OPT_TIME_PERIODIC,&err);//延时1s}}互斥信号量03任务互斥示例任务2函数voidtask2_task(void*p_arg){OS_ERRerr;charread_buf[50]={0};//用于存放临时数据uint32_tcnt=1;

//记录读取数据的次数while(1){//请求互斥信号量,没有获得互斥信号量会挂起任务OSMutexPend(&share_mem_mutex,0,OS_OPT_PEND_BLOCKING,0,&err);if(err!=OS_ERR_NONE){//判断申请互斥信号量是否成功//在以下编写没有正确获得互斥信号量但是函数返回时的处理代码}//以下开始访问共享资源,把array_buf数组中的字符串复制到read_buf中strcpy(read_buf,array_buf);//开始访问共享资源printf("第%d次读取数据:%s\r\n",cnt++,read_buf);//输出读取到的内容提示OSMutexPost(&share_mem_mutex,OS_OPT_POST_NONE,&err);//释放互斥信号量OSTimeDlyHMSM(0,0,0,500,OS_OPT_TIME_PERIODIC,&err);//延时0.5s}}互斥信号量03任务互斥示例04消息队列量消息队列介绍API函数接口任务同步示例μC/OS-III提供的信号量和互斥信号量,它们可以实现任务间的通信和中断与任务之间的通信,但是它们还是有缺陷的,它们不支持在任务间传递用户自定义的数据,仅仅是由系统报告了一个事件的发生而已。在实现编程中,很多场景需要在任务间或中断与任务间传输自定义的数据。比如,有键盘扫描任务和动作执行任务,键盘扫描任务负责扫描用户按下了哪个按键,然后把按键码发送给动作执行任务。动作执行任务接收键盘扫描任务发来的按键码,根据不同的按键码执行不同的控制动作。要实现这样的功能,就需要使用到μC/OS-III提供的消息队列功能了。消息队列04消息队列介绍消除队列特征消息队列也是任务间通信的一种机制,可以看成一个容器,可存放多条消息。一条消息由指向具体数据的指针、存放指向数据大小的变量和指示消息发送时间的时间戳组成。数据的指针可以指向任何用户自定义的数据区,甚至可以指向一个函数。消息队列概念消息队列04消息队列介绍消息内容在消息发出后,在被任务接收前,必须保持静态,中途不能改变它的值,因为数据是通过发送其内存地址而不是通过值发送的。换句话说,发送的数据不会被复制,或者,传递指向全局变量、全局数据结构、全局数组或函数等的指针。μC/OS-III中消息队列是用户创建的内核对象,只有内存资源足够大,数量没有限制。下图展示了对消息队列进行的操作有数据流程。认识消息队列消息队列04消息队列介绍消息队列默认是使用先进先出管道(FIFO)的方式,即先进入队列的消息也是先被取出来。在μC/OS-III中,还可以按后进先出顺序(LIFO)发布消息。当任务或中断服务程序必须向任务发送“紧急”消息时,LIFO机制很有用。往任务中发送消息是通过调用OSQPost()函数来实现,至于使用FIFO,还是LIFO方式,则由传递给OSQPost()函数的参数来决定。认识消息队列消息队列使用OSQPend()函数来接收消息,接收消息的任务旁边的小沙漏表示任务可以指定一个超时等待时间,如果任务指定的时间内没有收到消息,则会超时唤醒任务,并且返回错误码指示当前任务是因为接收消息超时而被唤醒的,不是正确接收到消息被唤醒。如果指定超时时间为0,任务就会永远等待下去,直到接收到消息为止。消息队列04消息队列介绍消息队列还包含一个等待消息发送到消息队列的任务列表。多个任务可以在一个消息队列上等待,如右图所示。当消息发送到消息队列时,等待消息队列的最高优先级任务接收该消息,还可以向所有在消息队列上等待的任务广播发送一条消息,从广播中接收到消息的任务,只要它们的优先级高于发送消息的任务,或者如果任务是被中断打断,消息是在中断服务程序中发送,μC/OS-III都会发生任务调度,μC/OS-III将运行这些处于就绪状态优先级最高的任务。认识消息队列消息队列的使用流程很简单:创建消息邮箱、读取消息、发送消息三个必需的步骤,其他可选操作有删除等待消息、中止等待消息。消息队列接口消息队列04API函数接口消息队列函数序号函数名功能描述备注1OSQCreate()创建消息队列必须调用2OSQPend()等待接收消息必须调用3OSQPost()发送消息到消息队列中必须调用4OSQDel()删除消息队列可选调用5OSQFlush()清空消息队列可选调用6OSQPendAbort()取消等待消除队列可选调用μC/OS-III系统使用OS_Q结构来表示一个消息队列,创建消息队列,只需要调用系统提供的OSQCreateAPI函数,根据需要传递必需的参数即可。创建消息队列函数原型:voidOSQCreate(OS_Q*p_q,CPU_CHAR*p_name,OS_MSG_QTYmax_qty,OS_ERR*p_err)函数功能:创建消息队列函数形参:p_q:指向消息队列的指针,传递OS_Q类型变量的地址;p_name:一个字符串指针,用于给消息队列命名;max_qty:表示消息队列的大小(必须非零),即消息队列中可以容纳消息的数量;p_err:一个指向存放错误码变量的指针,该变量将包含此函数返回的错误代码,可取错误码有:OS_ERR_NONE:成功创建消息队列OS_ERR_CREATE_ISR:指示不能从ISR创建OS_ERR_NAME:指示'p_name'是一个NULL指针OS_ERR_Q_SIZE:指示max_qty指定的大小为0。.......消息队列04API函数接口示例代码函数示例:创建消息队列key_msg_qOSQCreate((OS_Q*)&key_msg_q,//消息队列(CPU_CHAR*)"key_msg_q",

//消息队列名称(OS_MSG_QTY)10,

//消息队列长度,这里设置为10。(OS_ERR*)&err);

//错误码//创建失败,让程序进入死循环,这样在开发阶段方便发现问题。if(err!=OS_ERR_NONE){while(1){;}}消息队列04API函数接口当一个任务要获得其他任务或中断程序发来的消息,然后执行某些操作时,通过用OSQPend函数接收消息,如果当前没有消息可接收,任务会进入挂起状态或直接返回(具体行为和调用时传递的参数有关),并且在参数中携带返回错误码.从队列获取消息函数原型:void*OSQPend(OS_Q*p_q,OS_TICKtimeout,OS_OPTopt,OS_MSG_SIZE*p_msg_size,CPU_TS*p_ts,OS_ERR*p_err)函数功能:等待接收消息,如果成功在消息队列中接收到消息。函数形参:p_q:是指向消息队列的指针,传递OS_Q类型变量的地址;timeout:超时时间(以时钟节拍为单位),和信号量函数中的timeout参数相同。opt:指示在接收消息时,和信号量函数中的opt参数相同。p_ts:存放消息接收、挂起中止或消息队列删除时的时间戳。不需要时间戳时,传递NULL;p_err该变量将包含此函数返回的错误代码。函数调用可能产生的错误码如下:OS_ERR_NONE:指示成功接收到消息。OS_ERR_PEND_ISR:指示在中断服务程序中调用此函数OS_ERR_TIMEOUT:指示在指定的时间内未收到消息导致超时返回....函数返回值:非NULL:指向接收到的消息指针

消息队列04API函数接口示例代码函数示例:假设当前消息队列key_msg_q已经创建好了OS_ERRerr;//存放函数调用的错误码CPU_TSts;//保存时间戳uint32_tkey_code;//保存消息传递来的按键码//假设OSQPost发送来的消息是uint32_t类型按键码,因此接收到后需要转换为原来数据类型。key_code=(uint32_t)OSQPend((OS_Q*)&key_msg_q,

(OS_TICK)0,

OS_OPT_PEND_BLOCKING,

(OS_MSG_SIZE*)&key_code,

(CPU_TS*)&ts,(OS_ERR*)&err);消息队列04API函数接口调用OSQPost函数可以发送一条消息到消息队列中,如果有在此消息队列上等待接收消息,并比当前任务有更高优先级的任务,则执行任务调度,CPU执行切换到新任务中执行。函数原型:voidOSQPost(OS_Q*p_q,void*p_void,OS_MSG_SIZEmsg_size,OS_OPTopt,OS_ERR*p_err)函数功能:向消息队列中发送一条消息函数形参:p_q:是指向消息队列的指针,传递OS_Q类型变量的地址;opt:确定执行的POST类型,可取值有:

OS_OPT_POST_ALL:表示POST到队列中等待的所有任务。该选项可以添加到OS_OPT_POST_FIFO或OS_OPT_POST_LIFO上。

OS_OPT_POST_FIFOPOST:表示消息到队列末尾(FIFO)并唤醒单个等待任务。

OS_OPT_POST_LIFOPOST:表示消息到队列前面的LIFO)并唤醒单个等待任务。

OS_OPT_POST_NO_SCHED:表示发送消息到消息队列上后,不调用调度器。

可能出现的选项组合:

OS_OPT_POST_FIFO、OS_OPT_POST_LIFO、OS_OPT_POST_FIFO+OS_OPT_POST_ALL、

OS_OPT_POST_LIFO+OS_OPT_POST_ALL、

OS_OPT_POST_FIFO+OS_OPT_POST_NO_SCHED

、OS_OPT_POST_LIFO+OS_OPT_POST_NO_SCHED、

OS_OPT_POST_FIFO+OS_OPT_POST_ALL+OS_OPT_POST_NO_SCHED、OS_OPT_POST_LIFO+OS_OPT_POST_ALL+OS_OPT_POST_NO_SCHEDp_err:指向存放错误代码的变量的指针,可能产生的错误码如下:OS_ERR_NONE:指示成功发送消息到消息队列上;........发送消息到队列消息队列04API函数接口示例代码函数示例:假设当前消息队列key_msg_q已经创建好OS_ERRerr;uint32_tkey=0x01;//注意类型是uint32_t,后面接收到需要转换回来//发送key的值到消息队列中OSQPost((OS_Q*)&key_msg_q,

(void*)key,

//注意:把按键码强制转换为Void

(OS_MSG_SIZE)sizeof(key),

//消息内存占用的字节数量

(OS_OPT)OS_OPT_POST_FIFO,

//使用先进先出的方式发送消息

(OS_ERR*)&err);

//接收函数的错误码消息队列04API函数接口本示例是使用消息队列实现任务间通信的方法。任务1负责检测按键1~按键4是否按下,检测到有键按下了,则把按键码当成消息内容发送到消息队列上;任务2负责接收消息,并且根据消息内容执行不同的代码。实验的效果是开发板上KEY1~KEY4,每按下一个按键,开发板上的对应的LED1~LED4状态就会翻转一次。功能说明消息队列04任务间传递数据示例示例框图消息队列04任务间传递数据示例主函数OS_Qkey_msg_q;//定义一个消息队列变量intmain(void){OS_ERRerr;CPU_SR_ALLOC();......OSInit(&err);//初始化μC/OS-III//创建消息队列key_msg_qOSQCreate((OS_Q*)&key_msg_q,//消息队列(CPU_CHAR*)"key_msg_q",

//消息队列名称(OS_MSG_QTY)10,

//消息队列长度,这里设置为10(OS_ERR*)&err);

//错误码if(err!=OS_ERR_NONE){//创建失败,让程序进入死循环,这样在开发阶段方便发现问题while(1){;}}OS_CRITICAL_ENTER();

//进入临界区//调用OSTaskCreate创建启动任务,和前面讲解的代码相同,此时省略......OS_CRITICAL_EXIT();

//退出临界区OSStart(&err);

//开启μC/OS-III}消息队列04任务间传递数据示例主函数voidtask1_task(void*p_arg){uint32_tkey;OS_ERRerr;while(1){key=KEY_Scan(0);//扫描按键if(key){//如果检测到有键按下//发送按键码OSQPost((OS_Q*)&key_msg_q,(void*)key,//注意:把按键码强制转换为void*(OS_MSG_SIZE)sizeof(key),//消息内存占用的字节数量(OS_OPT)OS_OPT_POST_FIFO,//使用先进先出的方式发送消息(OS_ERR*)&err);//接收函数的错误码OSTimeDlyHMSM(0,0,0,50,OS_OPT_TIME_PERIODIC,&err);//延时50ms}}}消息队列04任务间传递数据示例任务1函数voidtask1_task(void*p_arg){OS_ERRerr;uint32_tcnt=1;//记录写入数据的次数while(1){

//请求互斥信号量OSMutexPend(&share_mem_mutex,0,OS_OPT_PEND_BLOCKING,0,&err);

if(err!=OS_ERR_NONE){//在以下编写没有正确获得互斥信号量但是函数返回时的处理代码//……} //以下开始访问共享资源,往array_buf数组中写入数据sprintf(array_buf,"cnt:%05d",cnt);//开始访问共享资源printf("第%d次写入数据:%s\r\n",cnt++,array_buf);//输出写入内容提示,写入次数增加OSMutexPost(&share_mem_mutex,OS_OPT_POST_NONE,&err);//释放互斥信号量OSTimeDlyHMSM(0,0,1,0,OS_OPT_TIME_PERIODIC,&err);//延时1s}}消息队列04任务间传递数据示例voidtask2_task(void*p_arg){OS_ERRerr;CPU_TSts;//保存时间戳uint32_tkey_code;//保存消息传递来的按键码while(1){//没有接收到消息会挂起任务//OSQPend发送来的消息是uint32_t类型按键码,因此接收到后还原原来的数据类型。key_code=(uint32_t)OSQPend(

(OS_Q*)&key_msg_q,

(OS_TICK)0,

(OS_OPT)OS_OPT_PEND_BLOCKING,

(OS_MSG_SIZE*)&key_code,

(CPU_TS*)&ts,

(OS_ERR*)&err);if(err!=OS_ERR_NONE){//没有正确获得消息但是函数返回时的处理代码}消息队列04任务间传递数据示例//根据接收到的按键码执行不同的操作:这里分别改变LED1~LED4状态switch(key_code){caseKEY1_PRES://按键1按下LED1_Toglge();//翻转LED1状态break;caseKEY2_PRES://按键2按下LED2_Toglge();//翻转LED1状态break;caseKEY3_PRES://按键3按下LED3_Toglge();//翻转LED3状态break;caseKEY4_PRES://按键4按下LED4_Toglge();//翻转LED4状态break;}OSTimeDlyHMSM(0,0,0,500,OS_OPT_TIME_PERIODIC,&err);//延时0.5s}}任务2函数总结与信号量和互斥信号量不同,它们并不能携带用户自定义的数据给其他任务,消息队列设计可以用于任务间或中断与任务间传递数据,消息队列可以容纳多条消息,可以用作事件的缓冲区,暂时存储当前没有及时处理的事件,不会因为系统当前工作负载过重,无法及时处理导致的事件丢失。使用消息队列时,不能在中断程序中调用OSQPend,以阻塞从消息队列中等待接收消息。往消息队列发送消息支持FIFO和FILO方式,对于后面发生的要紧急处理的消息,可以通过指定FILO方式来实现接收者优先处理的效果。消息队列04小结05事件标志组事件标志组介绍API函数接口任务同步示例事件标志组05事件标志组介绍事件标志组是μC/OS-III系统提供的一种用于任务间通信的同步机制,它和信号量类似,都可以实现任务等待某个事件的发生再继续往下运行。但事件标志组在任务同步方面比信号量功能更强大,使用更灵活。一个事件标志组同步等待多个事件发生或者等待多个事件中任何一个事件的发生。从专业术语角度描述,即多个事件的“逻辑与”和多个事件的“逻辑或”。“逻辑与”:等待所有事件都发生时,条件才成立,任务恢复运行;“逻辑或”:等待所有的事件的中任意一个事件发生,条件成立,任务恢复运行;事件标志组概念051.μC/OS-III“事件标志组”使用OS_FLAG_GRP类型来表示,也是一个内核对象,由一系列位(8位/16位/32位,由源码中的OS_FLAGS定义的数据类型决定)组成,可以简单理解为一个二进制位表示一个等待的事件,即图中间的多个小方格。创建事件标志组通过OSFlagCreate()函数创建,创建成功后就可以在任务/中断服务程序中使用它了;2.任务或中断服务程序可通过OSFlagPost()函数发布事件标志。注意:只有任务可以创建、删除和取消其他任务在事件标志组上挂起状态,不能在中断服务程序中执行这几种操作;认识事件标志组事件标志组事件标志组介绍053.调用OSFlagPend()函数,任务可以等待(挂起)事件标志组(所有位的子集)中任意数量的位(一个二进制位就是一个事件)发生期望的事件(逻辑与、逻辑或)。和前面学习过的信号量、消息队列的挂起一样,在调用OSFlagPend()可指定最长的等待超时值,如在指定的时间量(以时钟节拍为单位)内未发生期望的事件,则挂起的任务将恢复并返回超时错误码。认识事件标志组事件标志组事件标志组介绍054.调用OSFlagPend()函数时可以通过参数决定等待的方式是采用逻辑与还是逻辑或、等待目标事件位是清零还是置位。这样就出现了四种等级组合:①等待所有位集合逻辑“与”置位事件;

②等待所有位集合逻辑“与”清零事件;③等待所有位集合逻辑“或”置位事件;④等待所有位集合逻辑“或”清零事件认识事件标志组事件标志组事件标志组介绍事件标志组的使用流程很简单:创建事件标志组、等待事件标志、发送事件标志三个必需的步骤,其他可选操作有删除事件标志组、中止等待事件标志。事件标志组接口事件标志组05API函数接口事件标志组函数序号函数名功能描述备注1OSFlagCreate()创建事件标志组必须调用2OSFlagPend()等待事件标志发布必须调用3OSFlagPost()发布事件标志到事件标志组必须调用4OSFlagPendAbort()取消等待事件标志组可选调用5OSFlagDel()删除事件标志组可选调用6OSFl

温馨提示

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

评论

0/150

提交评论