STM32F407ZGT6-UCOSIII笔记10:消息传递--消息队列
UCOS III 中消息可以通过消息队列进行与几个任务的交流、任务间通信
本文学习与程序编写基于 正点原子的 STM32F1 UCOS开发手册
文章提供测试代码讲解、完整工程下载、测试效果图
我这个实验设计的不太好,但消息队列代码运行没啥问题...将就看看
目录
消息队列:
创建消息队列:
等待消息队列:
向消息队列发送消息:
实验目标解释:
定义消息队列与软件定时器:
创建消息队列:
目前各个文件任务:
#include "main.h"
#include "ComTask.h"
#include "MessageTask.h"
#include "CalculateTask.h"
测试效果截图:
测试工程下载:
提示:
消息队列:
消息一般包含:指向数据的指针,表明数据长度的变量和记录消息发布时刻的时间戳,指
针指向的可以是一块数据区或者甚至是一个函数,消息的内容必须一直保持可见性,因为发布数据采用的是引用传递是指针传递而不是值传递,也就说,发布的数据本身不产生数据拷贝。
创建消息队列:
等待消息队列:
向消息队列发送消息:
实验目标解释:
ComTask计数自己运行次数,
将自己运行次数通过消息队列 Message_Msg 发送给MessageTask,
同时检测消息队列DATA_Msg总大小,剩余大小,
消息队列 DATA_Msg 总大小,剩余大小 ,
DATA_Msg 剩余大小会因为 软件定时器 的发送操作而 改变
Message_Msg获取消息队列的消息,根据消息的情况点亮LED并打印
定义消息队列与软件定时器:
在 #include "Public.h" 文件中定义:
#include "Public.h" OS_TMR OS_Timer1_Periodic; //OS系统 软件定时器 1 周期模式 OS_Q Message_Msg; //定义一共消息队列,用于comtask消息传递,模拟消息邮箱 OS_Q DATA_Msg; //定义一个消息队列,用于发数据
#ifndef __Public_H #define __Public_H #include "headfile.h" extern OS_TMR OS_Timer1_Periodic; //OS系统 软件定时器 1 周期模式 #define Message_Msg_NUM 1 // comtask消息队列数量 #define DATA_Msg_NUM 4 // 发数据消息队列数量 extern OS_Q Message_Msg; //定义一共消息队列,用于comtask消息传递,模拟消息邮箱 extern OS_Q DATA_Msg; //定义一个消息队列,用于发数据
创建消息队列:
在 #include "main.h" 文件中的 void start_task(void *p_arg) 函数内进行创建:
代码如下:
//创建消息队列 Message_Msg OSQCreate( (OS_Q *) &Message_Msg,//消息队列 Message_Msg (CPU_CHAR * )"Message_Msg", //名称 (OS_MSG_QTY ) Message_Msg_NUM, //消息队列长度 (OS_ERR *) &err ); //创建消息队列 DATA_Msg OSQCreate( (OS_Q *) &DATA_Msg,//消息队列 DATA_Msg (CPU_CHAR * )"DATA_Msg", //名称 (OS_MSG_QTY ) DATA_Msg_NUM, //消息队列长度 (OS_ERR *) &err );
目前各个文件任务:
#include "main.h"
创建开始任务,初始化消息队列,初始化各个任务,初始化软件定时器
#include "main.h" void start_task(void *p_arg);//开始任务函数 int main(void) { OS_ERR err; CPU_SR_ALLOC(); Init_ALL(); OSInit(&err); //初始化UCOSIII OS_CRITICAL_ENTER();//进入临界区 //创建开始任务 OSTaskCreate((OS_TCB * )&StartTaskTCB, //任务控制块 (CPU_CHAR * )"start task", //任务名字 (OS_TASK_PTR )start_task, //任务函数 (void * )0, //传递给任务函数的参数 (OS_PRIO )START_TASK_PRIO, //任务优先级 (CPU_STK * )&START_TASK_STK[0], //任务堆栈基地址 (CPU_STK_SIZE)START_STK_SIZE/10, //任务堆栈深度限位 (CPU_STK_SIZE)START_STK_SIZE, //任务堆栈大小 (OS_MSG_QTY )0, //任务内部消息队列能够接收的最大消息数目,为0时禁止接收消息 (OS_TICK )0, //当使能时间片轮转时的时间片长度,为0时为默认长度, (void * )0, //用户补充的存储区 (OS_OPT )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR, //任务选项 (OS_ERR * )&err); //存放该函数错误时的返回值 OS_CRITICAL_EXIT(); //退出临界区 OSStart(&err); //开启UCOSIII while(1); } //开始任务函数 void start_task(void *p_arg) { OS_ERR err; CPU_SR_ALLOC(); p_arg = p_arg; CPU_Init(); #if OS_CFG_STAT_TASK_EN > 0u OSStatTaskCPUUsageInit(&err); //统计任务 #endif #ifdef CPU_CFG_INT_DIS_MEAS_EN //如果使能了测量中断关闭时间 CPU_IntDisMeasMaxCurReset(); #endif #if OS_CFG_SCHED_ROUND_ROBIN_EN //当使用时间片轮转的时候 //使能时间片轮转调度功能,时间片长度为1个系统时钟节拍,既1*5=5ms OSSchedRoundRobinCfg(DEF_ENABLED,1,&err); #endif //初始化 OS_Timer1_Periodic 软件定时器 OSTmrCreate((OS_TMR *) &OS_Timer1_Periodic , //OS系统 软件定时器 1 周期模式 (CPU_CHAR *) "OS_Timer1_Periodic", //定时器名称 (OS_TICK ) 100, // 启动延时 为100*10 ms (OS_TICK ) 20, // 周期为 20*10 ms (OS_OPT ) OS_OPT_TMR_PERIODIC, //周期定时模式 (OS_TMR_CALLBACK_PTR ) OS_Timer1_Periodic_callback,//回调函数 (void * ) 0,//参数为0 (OS_ERR *) &err ); OS_CRITICAL_ENTER(); //进入临界区 //创建消息队列 Message_Msg OSQCreate( (OS_Q *) &Message_Msg,//消息队列 Message_Msg (CPU_CHAR * )"Message_Msg", //名称 (OS_MSG_QTY ) Message_Msg_NUM, //消息队列长度 (OS_ERR *) &err ); //创建消息队列 DATA_Msg OSQCreate( (OS_Q *) &DATA_Msg,//消息队列 DATA_Msg (CPU_CHAR * )"DATA_Msg", //名称 (OS_MSG_QTY ) DATA_Msg_NUM, //消息队列长度 (OS_ERR *) &err ); //创建ComTask任务 OSTaskCreate((OS_TCB * )&COMTASKTaskTCB, (CPU_CHAR * )"com task", (OS_TASK_PTR )comTask, (void * )0, (OS_PRIO )COMTASK_TASK_PRIO, (CPU_STK * )&COMTASK_TASK_STK[0], (CPU_STK_SIZE)COMTASK_STK_SIZE/10, (CPU_STK_SIZE)COMTASK_STK_SIZE, (OS_MSG_QTY )0, (OS_TICK )0, //之前为0 //2个时间片 2*5ms (void * )0, (OS_OPT )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR, (OS_ERR * )&err); //创建MessageTask任务 OSTaskCreate((OS_TCB * )&MessageTaskTaskTCB, (CPU_CHAR * )"Message task", (OS_TASK_PTR )MessageTask, (void * )0, (OS_PRIO )MessageTask_TASK_PRIO, (CPU_STK * )&MessageTask_TASK_STK[0], (CPU_STK_SIZE)MessageTask_STK_SIZE/10, (CPU_STK_SIZE)MessageTask_STK_SIZE, (OS_MSG_QTY )0, (OS_TICK )0, //之前为0 //2个时间片 2*5ms (void * )0, (OS_OPT )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR, (OS_ERR * )&err); //创建CalculateTask任务 OSTaskCreate((OS_TCB * )&CalculateTaskTaskTCB, (CPU_CHAR * )"Calculate task", (OS_TASK_PTR )CalculateTask, (void * )0, (OS_PRIO )CalculateTask_TASK_PRIO, (CPU_STK * )&CalculateTask_TASK_STK[0], (CPU_STK_SIZE)CalculateTask_STK_SIZE/10, (CPU_STK_SIZE)CalculateTask_STK_SIZE, (OS_MSG_QTY )0, (OS_TICK )0, (void * )0, (OS_OPT )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR, (OS_ERR * )&err); //创建 Shared_Str共享全局资源 的 信号量 OSSemCreate((OS_SEM *) &SEM_Shared_Str, //指向信号量 (CPU_CHAR *) "SEM_Shared_Str", //信号量名称 ( OS_SEM_CTR ) 1, //信号量值为1 (OS_ERR *) &err ); //创建 Shared_Str共享全局资源 的 互斥信号量 OSMutexCreate((OS_MUTEX *) &MUTEX_Shared_Str, //指向互斥信号量 (CPU_CHAR *) "MUTEX_Shared_Str", //互斥信号量名称 (OS_ERR *) &err ); OS_TaskSuspend((OS_TCB*)&StartTaskTCB,&err); //挂起开始任务 OS_CRITICAL_EXIT(); //进入临界区 }
#include "ComTask.h"
#include "ComTask.h" /* ComTask 打印系统节拍频率 删除CalculateTask ComTask 计数打印自己运行次数 发送 Message_Msg 消息 打印 消息队列 DATA_Msg 总大小 计算 打印 DATA_Msg 剩余大小 延时50ms */ void comTask(void * p_arg) { OS_ERR err; int i=0,OSTime_tickRate; char msgq_remain_size;//消息队列剩余大小 //char ComTask_str[]="ComTask_write"; p_arg = p_arg; OSTime_tickRate=OSCfg_TickRate_Hz; //获取系统节拍频率,(该宏定义在 OS_CFG_APP.H) UsartPrintf(USART1, "OSCfg_TickRate_Hz = %d Hz \r\n",OSTime_tickRate); //打印系统节拍频率 OSTaskDel((OS_TCB*)&CalculateTaskTaskTCB,&err); //删除CalculateTask UsartPrintf(USART1, "ComTask delete CalculateTask !\r\n"); //打印删除 CalculateTask 提示 while (DEF_TRUE) { i++; UsartPrintf(USART1, "ComTask Print %d\r\n",i); //打印 ComTask 运行次数 //发送Message_Msg 消息 OSQPost((OS_Q *) &Message_Msg, (void *) i, (OS_MSG_SIZE ) 1, (OS_OPT ) OS_OPT_POST_FIFO, (OS_ERR *) &err ); msgq_remain_size =DATA_Msg.MsgQ.NbrEntriesSize-DATA_Msg.MsgQ.NbrEntries; //计算消息队列 DATA_Msg 剩余大小 UsartPrintf(USART1, "DATA_Msg Total Size %d \r\n",DATA_Msg.MsgQ.NbrEntriesSize); //打印 消息队列 DATA_Msg 总大小 UsartPrintf(USART1, "DATA_Msg Remain Size %d \r\n",msgq_remain_size); //打印 消息队列 DATA_Msg 剩余大小 OSTimeDlyHMSM(0,0,0,50,OS_OPT_TIME_HMSM_STRICT,&err); //延时50ms } }
#include "MessageTask.h"
#include "MessageTask.h" /* MessageTask 计数打印一次运行次数 请求 Message_Msg 消息 根据 消息情况 点灯与打印灯颜色 延时1000 ms */ void MessageTask (void * p_arg) { OS_ERR err; int i=0; char *KEY; OS_MSG_SIZE size; //char MessageTask_str[]="MessageTask_write"; p_arg = p_arg; while (DEF_TRUE) { i++; UsartPrintf(USART1, "MessageTask Print %d\r\n",i); //打印 MessageTask 运行次数 //请求 Message_Msg KEY=OSQPend((OS_Q *) &Message_Msg, (OS_TICK ) 0, (OS_OPT ) OS_OPT_PEND_BLOCKING, (OS_MSG_SIZE *)&size, (CPU_TS *) 0, (OS_ERR *) &err ); if(KEY[0] % 2==0) { LED_Blue_L;LED_GREEN_H; OSTmrStart(&OS_Timer1_Periodic ,&err); //启动软件 定时器 OS_Timer1_Periodic UsartPrintf(USART1, "MessageTask Print BLUE\r\n",i); //打印 灯 } else if(KEY[0] % 2 !=0) { LED_Blue_H;LED_GREEN_L; UsartPrintf(USART1, "MessageTask Print GREEN\r\n",i); //打印 灯 } OSTimeDlyHMSM(0,0,1,0,OS_OPT_TIME_HMSM_STRICT,&err); //延时1000ms } }
#include "CalculateTask.h"
本文此任务开头就被删除,不做解释
#include "Public.h"
软件定时器任务:打印运行提示信息,发送消息 DATA_Msg 以改变DATA_Msg 的剩余大小
(因为这个消息没有消费者 及时请求 它 !)
// OS_Timer1_Periodic 定时器回调函数 // 打印提示信息 void OS_Timer1_Periodic_callback(void *p_tmr,void *p_arg) { OS_ERR err; char *pbuf; static int Timer1_count=0; Timer1_count++; //打印中断次数 UsartPrintf(USART1, "OS_Timer1_Periodic Print %d\r\n",Timer1_count); //发送DATA_Msg 消息 OSQPost((OS_Q *) &DATA_Msg, (void *) pbuf, (OS_MSG_SIZE ) 10, (OS_OPT ) OS_OPT_POST_FIFO, (OS_ERR *) &err ); }
测试效果截图:
我这个实验设计的不太好,但消息队列代码运行没啥问题...将就看看
刚上电,定时器 OS_Timer1_Periodic 没有启动,所以DATA_Msg的 总大小与剩余大小都是4
随后执行到启动定时器时:
因为定时器发送的 DATA_Msg 消息并没有消费者及时来请求
所以它的队列空间就会一直减少:
MessageTask根据情况亮灯:
我们还发现,即使Comtask发送消息频次很快,但MessageTask还是1000ms(MessageTask的延时周期)响应一次
测试工程下载:
工程包含一份程序设计框架说明,不明白可以看看:
https://download.csdn.net/download/qq_64257614/90154608
提示:
如果 软件定时器 因为变量语句太多被卡主了,就在这个文件更改它的栈大小,改大些: