|
a0a.1 32b0c
本帖最后由 creep 于 2017-4-16 14:03 编辑
在上个贴了里面我们测试了信号量和软件定时器,信号量的作用类似一个标志位一样,能在中断和任务之间传输一种状态 ,但是并不能传递更多的信息内容和信息长度大小这些,下面我们要测试的消息队列就是用来传递具体的消息内容在中断和任务以及任务之间。 有些情况下任务或者ISR与另一个任务间进行通信,这种信息交换有2种方法:全局变量或者发送消息。如果使用全局变量,任务或者ISR就必须确保它独占该变量。如果防止被ISR嵌套就只能用来关中断来进行保护。如果是任务间共享该变量,那么可以通过关中断、锁调度器、信号量、mutex来保护变量。任务与ISR通信只能通过全局变量,如果全局变量被ISR改变,任务将不会知道全局变量被改变,除非任务检测该变量或者ISR标记任务告知该变量被改变。发送消息可以很好的解决这个问题。
消息中一般包含一个指向数据的指针和数据的大小等信息,这里的指针可以指向数据区或者一个函数。消息的发送方和接收方都应知道消息的具体含义 ,这样就可以在接收到消息时进行正常的处理。因为消息发送的是指针并不是直接拷贝的消息内容,所以接收消息方要在接收到消息后自己去访问具体的消息内容。
一个比较重要的问题是因为消息发放的是地址给接收方,所以在消息发送后接收方访问之前该消息必须不能被修改,否则可能会导致消息的传递出错。简要概括如下:
1)在发送消息时会发送消息的地址和消息的地址长度。 初始化消息时会设置消息个数,如果发送的消息没有被取走的话达到了
消息存储的最大个数时会导致消息的丢失。
2)发送消息之后消息被取走之前,消息所在的内存不应该被修改,否则可能会导致取到消息出错,所以正确的做法是定一个内存空间用于保存每条发送的消息。
一个比较形象的消息传递图如下:
1、内存管理
UCOSIII提供的动态内存管理方案是将存储区分成区和块,一个存储区有几个固定大小的块组成。如下图:
这个存储区可以直接定义或者使用malloc()申请。使用的时候从这个存储区中取出空闲的块,使用之后释放。
为了测试消息队列,我们使用UCOSIII的内存管理定一个长度为100字节,个数为12的内存空间用来存储消息。
- /*直接定义的方法*/
-
- //定义存储变量
- OS_MEM MyPartition;
- //直接定义存储区
- CPU_INT08U MyPartitionStorage[12][100];
-
- /*使用malloc的方式*/
-
- //定义存储变量
- OS_MEM *MyPartitionPtr;
- //使用malloc定义存储区
- MyPartitionPtr = (OS_MEM *)malloc(sizeof(OS_MEM));
复制代码 定义之后需要创建一个内存管理对象
- //定义一个内存管理
- OSMemCreate((OS_MEM *) &MyPartition,
- (CPU_CHAR *) "mymemory",
- (void *) &MyPartitionStorage[0][0],
- (OS_MEM_QTY) MY_MEM_NUM,
- (OS_MEM_SIZE) MY_MEMBLOCK_SIZE,
- (OS_ERR *) &err);
复制代码 使用的时候先申请后使用
- //定义要申请的内存变量
- CPU_INT08U *MyDataBlkPtr;
- //申请内存
- MyDataBlkPtr = OSMemGet((OS_MEM *) &MyPartition, (OS_ERR *) &err);
复制代码 使用之后释放即可
- //释放内存p
- OSMemPut((OS_MEM *) &MyPartition, (void *) p, (OS_ERR *) &err);
复制代码
从上面的操作过程可以看到UCOSIII的内存管理其实就是定义一个二维数组进行使用,只是使用UCOSIII的内存管理可以很好的判断内存是否有空闲以及申请和释放更加安全简单一些。
2、消息队列
消息队列的测试是在按键扫描函数中Post消息给另外一个任务,消息中包含当前OS的TICKS,接收到消息的任务会取出消息输出到串口同时也会打印剩余的消息个数。注意测试中我们定义的消息个数最大为12个。
使用之前定义和创建消息队列
- /定义消息队列
- OS_Q Key_Q;
- //创建消息队列
- OSQCreate((OS_Q *) &Key_Q,
- (CPU_CHAR *) "my_q",
- (OS_MSG_QTY) 12,
- (OS_ERR * )&err);
复制代码 任务中pend消息队列,OSQPend的返回值就是消息队列传递的指针,返回的size就是这条消息的长度。
为了测试消息的传递和消息的个数,我们的post任务5秒执行一次。
- void task1_task(void *p_arg)
- {
- OS_ERR err;
- void *p;
- OS_MSG_SIZE size;
- uint8_t buff[20];
- CPU_SR_ALLOC();
- while (DEF_ON)
- {
- p = OSQPend((OS_Q *) &Key_Q,
- (OS_TICK) 0,
- (OS_OPT)OS_OPT_PEND_BLOCKING ,
- (OS_MSG_SIZE *) &size,
- (CPU_TS *) 0,
- (OS_ERR *) &err);
- OS_CRITICAL_ENTER();
- sprintf((char*)buff,"msg num:%d\r\n",Key_Q.MsgQ.NbrEntries);
- LPUart_SendData((uint8_t*)buff, strlen((const char*)buff));
- LPUart_SendData((uint8_t*)p, size);
- //释放内存
- OSMemPut((OS_MEM *) &MyPartition, (void *) p, (OS_ERR *) &err);
- OS_CRITICAL_EXIT();
- //延时5秒
- OSTimeDlyHMSM(0, 0, 5, 0, OS_OPT_TMR_PERIODIC, &err);
- }
- }
复制代码
按键的检测函数中Post消息,注意如果消息个数超过12个导致内存没有被及时释放会出现消息丢失,此时会打印信息到串口
- //按键定时器回调函数
- void KeyTmrCallback(void *p_tmr, void *p_arg)
- {
- //定义USER键按下的时间计数,每次为10ms
- static uint8_t KeyUser_PressCnt = 0;
- static uint8_t KeyUser_Release = ENABLE;
- CPU_INT08U *MyDataBlkPtr;
- OS_ERR err;
- if(KEY_USER)
- {
- //按键按下
- KeyUser_PressCnt++;
- if((KeyUser_PressCnt > 10) && (KeyUser_Release == ENABLE))
- {
- //申请内存
- MyDataBlkPtr = OSMemGet((OS_MEM *) &MyPartition, (OS_ERR *) &err);
- if(MyDataBlkPtr != NULL)
- {
- sprintf((char*)MyDataBlkPtr,"Current Ticks:%d",OSTickCtr);
- //psot消息队列
- OSQPost((OS_Q *)&Key_Q , (void *) MyDataBlkPtr, (OS_MSG_SIZE) strlen((const char*)MyDataBlkPtr), (OS_OPT) OS_OPT_POST_FIFO, (OS_ERR *) &err);
- }
- else
- {
- //申请内存失败
- printf("OSMemGet Failed\r\n");
- }
- KeyUser_PressCnt = 0;
- KeyUser_Release = DISABLE;
- }
- }
- else
- {
- //按键松开
- KeyUser_PressCnt = 0;
- KeyUser_Release = ENABLE;
- }
- }
复制代码 测试中我们按下按键可以看到输出如下,如果5秒内按键次数少于12个时消息的传输不会出错
如果在5秒内快速按键超过12个,串口输出会提示申请内存出错,消息队列中只会保存12个有效的消息,多余的消息将会丢失。
要避免这种问题要么申请大的内存空间要么加快消息的处理(此处是为了演示效果故意5秒处理一次消息)
消息队列的功能远不止上面测试的那些,除此之外还有很多比较强大的功能,但是基本的使用熟悉后再进阶就比较简单了,更多的内容可以参考之前帖子分享的官方文档内容。
测试代码:
UCOSIII-Q.rar
(1.49 MB, 下载次数: 161)
|
评分
-
查看全部评分
|