实验三、消息队列
本节我们主要介绍的是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",(INT8U *)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); } } 由于写入消息队列的速度快于读出队列的速度,因此到实验做了一段时间后会出现消息队列满的现象,等待消息被读出后,才能继续往队列里面写数据,通过串口打印的现象如下:
|