1.消息队列的作用
对比信号量和互斥信号量。信号量和互斥信号量是用来完成任务间同步或共享资源加锁的。也就是说这两种都是一种flag标志位作用。表示一种事件的发生,并不能传递数据。那比如说我想让任务A给任务B发送4个字节的数据,怎么办?所以引入了消息队列的概念。
2.消息队列的API 文件os_q.c
2.1创建消息队列
void OSQCreate (OS_Q *p_q, CPU_CHAR *p_name, OS_MSG_QTY max_qty, OS_ERR *p_err)
第一个参数是一个消息队列的结构体变量,需要先定义在:
OS_Q DATA_Msg; //定义一个消息队列,用于发送数据
第二个参数是一个名字
第三个参数是是需要传递的数据的长度,字节为单位
第四个参数是保存返回值的变量
2.2删除消息队列
OS_OBJ_QTY OSQDel (OS_Q *p_q,
OS_OPT opt,
OS_ERR *p_err)
2.3发送消息
void OSQPost (OS_Q *p_q, void *p_void, OS_MSG_SIZE msg_size, OS_OPT opt, OS_ERR *p_err)
第一个参数是一个消息结构体变量
第二个参数是传递的数据的第一个字节的地址(如果是数组则传数组名,如果是malloc分配的内存需要传对应的指针即可)
第三个参数是传递数据的大小,字节为单位
第四个参数是
OS OPT POST ALL
OS OPT POST FIFO
OS OPT POST LIFO
0S OPT POST NO SCHED
2.4请求消息
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)
第一个参数是消息结构体变量
第二个参数是超时时间
第三个参数是设置阻塞还是不阻塞
第四个参数是传递的数据的大小(这个参数是一个输出型参数,是请求到数据后自动计算有多少字节的数据,我们需要读它)
第五个参数是时间戳
第六个参数是记录错误返回值的
使用过程:
1 任务A发送消息队列,包含数据多少字节
2 任务B接收消息队列,自动计算消息队列有多少字节
3.任务之间使用消息队列传递数据
现在创建2个任务A和B,任务A发送数据,任务B接收数据。先使用消息队列发送字符串在发送4个字节的int数据。记录下遇到的问题。
3.1传递字符串
省略基础代码,只保留和消息队列相关的。
定义数据大小和消息队列(全局)
#define MyMsgSizeInByte 4 //发送数据的消息队列的大小 OS_Q MyMsg; //定义一个消息队列,用于发送数据
创建消息队列(在第一个任务中,)
//创建消息队列MyMsg OSQCreate ((OS_Q* )&MyMsg, (CPU_CHAR* )"MyMsg", (OS_MSG_QTY )MyMsgSizeInByte, (OS_ERR* )&err);
任务A:发送数据
//任务A的任务函数 void taskA(void *p_arg) { OS_ERR err; u8 *p; u8 *pbuf;
char * TaskAData = "TaskADatatoPost"; while(1) { printf("任务1 "); OSQPost(&MyMsg,TaskAData,sizeof(TaskAData),OS_OPT_POST_FIFO + OS_OPT_POST_ALL,&err);
printf("任务1 post 数据:%s ",TaskAData);
OSTimeDlyHMSM(0,0,1,0,OS_OPT_TIME_PERIODIC,&err); //延时10ms } }
任务B:请求数据
//任务B任务函数 void taskB(void *p_arg) { u8 num; u8 datas[4]= {0}; OS_MSG_SIZE size = 10; OS_ERR err; u8 *TaskBData; while(1) { //printf("任务2 "); TaskBData = OSQPend(&MyMsg,0,OS_OPT_PEND_BLOCKING,&size,0,&err); printf("任务2 pend 数据:%s ",TaskBData); printf("size大小%d ",size); OSTimeDlyHMSM(0,0,1,0,OS_OPT_TIME_PERIODIC,&err); //延时1s } }
结果
发送字符串很顺利。
3.2传递多个int类型数据
在发送4个字节int数据时遇到了个问题,只能接收第一个字节的数据。
定义了1个全局数组用来发送数据
int datatopost[4] = {15,16,17,18}; //存放任务A发
任务A:发送数据
//任务A的任务函数 void taskA(void *p_arg) { u8 key,num; OS_ERR err; u8 *p; u8 *pbuf; while(1) { printf("任务1 "); OSQPost(&MyMsg,datatopost,4,OS_OPT_POST_FIFO + OS_OPT_POST_ALL,&err); printf("任务1 post 数据:%d%d%d%d ",datatopost[0],datatopost[1],datatopost[2],datatopost[3]); OSTimeDlyHMSM(0,0,1,0,OS_OPT_TIME_PERIODIC,&err); //延时1s } }
任务B:接收数据
//任务B任务函数 void taskB(void *p_arg) { u8 num; u8 datas[4]= {0}; OS_MSG_SIZE size = 10; OS_ERR err; u8 *TaskBData;//接收数据的指针 while(1) { //printf("任务2 "); TaskBData = OSQPend(&MyMsg,0,OS_OPT_PEND_BLOCKING,&size,0,&err); printf("任务2 pend 数据:%d%d%d%d ",TaskBData[0],TaskBData[1],TaskBData[2],TaskBData[3]); printf("size大小%d ",size); OSTimeDlyHMSM(0,0,1,0,OS_OPT_TIME_PERIODIC,&err); //延时1s } }
结果:
我在任务B中定义了一个u8指针用来接收这4个字节的数据。但是从运行结果中看到任务B确实接收到了4个字节,但是只能接收到第一个字节的数据。经过查找资料,最后发现要使用malloc给消息队列分配内存才行,改进代码后,任务A发送的数据不再使用全局变量,在任务A中使用malloc函数分配内存,经过测试只要发送的时候使用malloc,任务B接收的时候直接定义一个指针还是使用malloc分配内存,都可以接收到。关于详细的介绍在最后面的博客地址中有介绍。下面使用的mymalloc是原子封装的ucos系统下的malloc函数。
任务 A
//任务A的任务函数 void taskA(void *p_arg) { u8 key,num; OS_ERR err; u8 *p; u8 *pbuf; pbuf = mymalloc(SRAMIN,10); //申请10个字节 pbuf[0]=15;pbuf[1]=16;pbuf[2]=17;pbuf[3]=18; while(1) { printf("任务1 "); OSQPost(&MyMsg,pbuf,4,OS_OPT_POST_FIFO + OS_OPT_POST_ALL,&err); printf("任务1 post 数据:%d%d%d%d ",pbuf[0],pbuf[1],pbuf[2],pbuf[3]); OSTimeDlyHMSM(0,0,1,0,OS_OPT_TIME_PERIODIC,&err); //延时1s } }
任务B
//任务B任务函数 void taskB(void *p_arg) { u8 num; u8 datas[4]= {0}; OS_MSG_SIZE size = 10; OS_ERR err; u8 *TaskBData; //TaskBData = mymalloc(SRAMIN,10); //申请10个字节 无论这里是否分配内存都可以接到4个字节完整的数据 while(1) { //printf("任务2 "); TaskBData = OSQPend(&MyMsg,0,OS_OPT_PEND_BLOCKING,&size,0,&err); printf("任务2 pend 数据:%d%d%d%d ",TaskBData[0],TaskBData[1],TaskBData[2],TaskBData[3]); printf("size大小%d ",size); OSTimeDlyHMSM(0,0,1,0,OS_OPT_TIME_PERIODIC,&err); //延时1s } }
结果:
参考资料:
1.消息队列接收数据覆盖问题https://blog.csdn.net/u012252959/article/details/53115024
2.原子的mymalloc