CH04-03-操作系统原理与实践-μCOS-III任务通信.pptx
嵌入式系统设计原理嵌入式操作系统原理与实践主讲人:赖树明东莞理工学院05事件标志组01通信机制概述02信号量03互斥信号量04消息队列0101通信机制概述通信机制概述01使用实时操作系统,相对于裸机程序的一个优势是可以把复杂的功能划分到不同的任务中,但这些任务的数据以及逻辑功能是存在密切的联系的,因此需要有一种机制可以实现任务之间的通信,以便于使用这些独立的任务可以按我们的设想协调运行起来,uc/OS-III操作系统提供了丰富的任务间通信的机制,可以让我们根据使用场景,选择不同的通信方式。概述A信号量B互斥信号量C消息队列D事件标志组E任务信号量F任务消息队列概述0202信号量信号量介绍API函数接口任务同步示例任务互斥示例信号量02信号量(Semaphore)是一种实现任务间通信的机制,用于任务之间同步或临界资源的互斥访问。通俗理解:信号量是一个正值变量,表示资源的可申请数,当任务申请信号量时,变量值减1,任务使用完成后,再释放信号量,效果是变量值加1;信号量为0时,申请信号号的任务则无法获得信号,表现为退出或者等待挂起,直到有其他任务释放了信号量,这些等待的任务会按照优先级来获取信号量,继续执行其代码。概念信号量介绍n信号量可以细分为两种类型:二进制信号量和计数信号量。n二进制信号量只能取两个值:0或者1,在开发中使用最多的还是是二值信号量这种情形;n计数信号量允许的值介于0255/65535/4294967295之间,具体取决于信号量机制是使用8位、16位,还是32位数据类型实现的。n对于C/OS-III,信号量的最大值由数据类型OS_SEM_CTR(见os_type.h)决定,可以根据需要更改其他类似。特征信号量的使用流程有三个必需的步骤,分别是创建信号量、申请信号量、发送信号量,其他可选操作有删除信号量、中止等待信号量、修改信号值等。使用流程表表4.1 信号量函数信号量函数序号函数名功能描述备注1OSSemCreate()创建一个信号量必须调用2OSSemDel()删除一个信号量必须调用3OSSemPend()等待信号量必须调用4OSSemPendAbort()中止对信号量的等待可选调用5OSSemPost()发送或发出信号量信号可选调用6OSSemSet()强制信号量计数为所需值可选调用信号量02API函数接口C/OS-III系统使用OS_SEM结构来表示一个信号量,创建信号量,只需要调用系统提供的OSSemCreate函数,根据需要传递必须参数即可创建信号量原型:void OSSemCreate(OS_SEM *p_sem,CPU_CHAR *p_name,OS_SEM_CTR cnt,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_SEM my_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_ERR err;/存放函数调用错误码 /没有获得信号量会挂起任务 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_SEM key_sem;/定义一个信号量,用于任务同步int main(void)OS_ERR err;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_SEM key_sem;/定义一个信号量,用于任务同步int main(void)OS_ERR err;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的任务函数void task1_task(void*p_arg)u8 key;OS_ERR err;while(1)key=KEY_Scan(0);/扫描按键if(key=KEY1_PRES)/如果按下开发板按键1printf(Task1:发送一个信号量rn);/输出发送信号量提示OSSemPost(&key_sem,OS_OPT_POST_1,&err);/发送信号量printf(当前信号值:%urn,key_sem.Ctr);OSTimeDlyHMSM(0,0,0,10,OS_OPT_TIME_PERIODIC,&err);/延时10ms 信号量02任务同步示例任务1函数 void task2_task(void*p_arg)u8 num;OS_ERR err;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_SEM share_mem_sem;/定义一个信号量,用于保护共享资访问。int main(void)OS_ERR err;CPU_SR_ALLOC();.OSInit(&err);/初始化C/OS-III /创建一个信号量share_mem_sem OSSemCreate(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任务互斥示例主函数 void task1_task(void*p_arg)OS_ERR err;uint32_t cnt=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次写入数据:%srn,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的任务函数void task1_task(void*p_arg)u8 key;OS_ERR err;while(1)key=KEY_Scan(0);/扫描按键if(key=KEY1_PRES)/如果按下开发板按键1printf(Task1:发送一个信号量rn);/输出发送信号量提示OSSemPost(&key_sem,OS_OPT_POST_1,&err);/发送信号量printf(当前信号值:%urn,key_sem.Ctr);OSTimeDlyHMSM(0,0,0,10,OS_OPT_TIME_PERIODIC,&err);/延时10ms 信号量02任务互斥示例任务2函数 void task2_task(void*p_arg)OS_ERR err;char read_buf50=0;/用于存放临时数据 uint32_t cnt=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次读取数据:%srn,cnt+,read_buf);OSSemPost(&share_mem_sem,OS_OPT_POST_1,&err);/释放信号量 OSTimeDlyHMSM(0,0,0,500,OS_OPT_TIME_PERIODIC,&err);信号量02任务互斥示例0303互斥信号量互斥信号量介绍API函数接口任务互斥示例-优先级反转,是指某个共享资源被较低优先级的任务所持有,较高优先级的任务任务申请该共享资源无法获得,导致较高优先级任务反而被更低优先级的任务推迟被调度执行的现象。互斥信号量03互斥信号量介绍优先级反转概念C/OS-III支持一种特殊类型的二值互斥信号量,称为互斥信号量(也称为互斥体),相对于前面学习过普通二值互斥信号量,它解决了优先级反转的问题。接下来的先来学习什么是什么级转换,才可以更好理解互斥信号量的作用。互斥信号量概念右图展示一个优先级反转的示例:系统中有高(TaskH),中(TaskM),低(TaskL)三个级别优先级任务,某一时刻低TaskL任务持有信号量S,此时TaskH就绪抢占TaskL,然后申请信号量S,但是此时信号量被 Task L 持有,因此Task H挂起,放弃CPU。然后Task L恢复运行,运行过程中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函数接口函数原型:void OSMutexCreate(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函数接口申请互斥信号量函数原型:void OSMutexPend(OS_MUTEX *p_mutex,OS_TICK timeout,OS_OPT opt,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_ERR err;/存放函数调用错误码/没有获得互斥信号量会挂起任务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_ERR err;/存放函数调用错误码OSMutexPost(&my_mutex_test,OS_OPT_POST_NONE,&err);/发送互斥信号量/判断互斥信号量是否成功发送,发送一般都不会出错,以下判断代码也可以不写if(err!=OS_ERR_NONE)/在以下编写没有成功发送互斥信号量时的处理代码,一般情况不用写。/互斥信号量03API函数接口n本示例是使用互斥信号量实现对共享资源的互斥访问。n任务1和任务2都需要访问一个共享资源,即一块数据缓冲区,程序中可表示为一个数组,其中任务1对这块数组进行写操作,任务2负责读取出数据缓冲区任务1写入的数据。n为了保证任务2每次都可以完整读取任务1的一次完成写操作,保证数据不会混乱,则任务1和任务2在访问这块共享的内存(数组)时都需要申请同一个互斥信号量,如果互斥信号量被其中一个任务持有了,则需要等待对方访问完共享资源,然后互斥信号量才可以访问共享资源,从而保证了共享资源的互斥访问。功能说明互斥信号量03任务互斥示例示例框图互斥信号量03任务互斥示例主函数 OS_MUTEX share_mem_mutex;/定义一个互斥信号量,用于保护共享资访问 int main(void)OS_ERR err;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任务互斥示例主函数 void task1_task(void*p_arg)OS_ERR err;uint32_t cnt=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次写入数据:%srn,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函数void task1_task(void*p_arg)OS_ERR err;uint32_t cnt=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次写入数据:%srn,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函数void task2_task(void*p_arg)OS_ERR err;char read_buf50=0;/用于存放临时数据 uint32_t cnt=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次读取数据:%srn,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任务互斥示例0404消息队列量消息队列介绍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函数,根据需要传递必需的参数即可。创建消息队列函数原型:void OSQCreate(OS_Q*p_q,CPU_CHAR*p_name,OS_MSG_QTY max_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_q OSQCreate(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_TICK timeout,OS_OPT opt,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_ERR err;/存放函数调用的错误码 CPU_TS ts;/保存时间戳 uint32_t key_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_LIFO、OS_OPT_POST_FIFO+OS_OPT_POST_ALL、OS_OPT_POST_LIFO+OS_OPT_POST_ALL、OS_OPT_POST_FIFO