有时一个任务需要与多个事件同步,这就要用到事件标志组
本文学习与程序编写基于 正点原子的 STM32F1 UCOS开发手册
文章提供测试代码讲解、完整工程下载、测试效果图
目录
事件标志组:
定义与初始化事件标志组:
#include "Public.h"定义事件标志组
#include"main.h"创建事件标志组
实验目标解释:
目前各个文件任务:
#include "main.h"
#include "Public.h"
#include "ComTask.h"
#include "MessageTask.h"
#include "CalculateTask.h"
测试效果截图:
测试工程下载:
事件标志组:
定义与初始化事件标志组:
#include "Public.h"定义事件标志组
OS_FLAG_GRP EventFlags; //定义一个事件标志组
顺带有一些宏定义:
#define LED_GREEN_FLAG 0x01 //LED绿灯标志 #define EXTI15_10_IRQ_FLAG 0x02 //按键中断时间标志 #define EventFlags_Value 0x00 //事件标志初始值 extern OS_FLAG_GRP EventFlags; //定义一个事件标志组
#include"main.h"创建事件标志组
在void start_task(void *p_arg) //开始任务函数 创建事件标志组:
//创建一个事件标志组 OSFlagCreate((OS_FLAG_GRP *) &EventFlags, //指向事件标志组 (CPU_CHAR * )"EventFlags", //名称 (OS_FLAGS ) EventFlags_Value,//事件标志组初始值 (OS_ERR *) &err );
实验目标解释:
设计程序:
MessageTask 循环亮灯 打印自己运行次数,亮绿灯就发送绿灯 事件标志组
ComTask 循环检测外部中断标志,检测到就发送对应 事件标志,并打印此时的值
只有当MessageTask亮灯为绿色,且外部中断按键按下时,才运行CalculateTask
CalculateTask 开始打印自己运行次数
目前各个文件任务:
#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 // ); //创建一个事件标志组 OSFlagCreate((OS_FLAG_GRP *) &EventFlags, //指向事件标志组 (CPU_CHAR * )"EventFlags", //名称 (OS_FLAGS ) EventFlags_Value,//事件标志组初始值 (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 )TASK_Q_NUM, (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 "Public.h"
外部中断的中断服务函数,用于置标志位,提示事件发生:
//外部中断0服务程序 void EXTI15_10_IRQHandler(void) { //OS_Timer1_Periodic_flag=1; // 定时器状态 标志取1 EXTI15_10_IRQ_flag=!EXTI15_10_IRQ_flag; //外部中断标志取反 EXTI_ClearITPendingBit(EXTI_Line15); //清除LINE0上的中断标志位 }
#include "ComTask.h"
#include "ComTask.h" /* ComTask 打印系统节拍频率 循环中: ComTask 计数 自己运行次数 根据 外部中断 控制的 标志位 发送 外部中断 事件标志 同时打印发送完后 EventFlags 事件标志组 的值 随后 置0 外部中断 事件标志,准备下次触发 延时50ms */ void comTask(void * p_arg) { OS_ERR err; int i=0,OSTime_tickRate; p_arg = p_arg; OSTime_tickRate=OSCfg_TickRate_Hz; //获取系统节拍频率,(该宏定义在 OS_CFG_APP.H) UsartPrintf(USART1, "OSCfg_TickRate_Hz = %d Hz \r\n",OSTime_tickRate); //打印系统节拍频率 UsartPrintf(USART1, "ComTask Print %d\r\n",i); //打印 ComTask 运行次数 while (DEF_TRUE) { i++; // EXTI15_10_IRQ_flag 置0 用于限制comTask 检测标志位动作只有一次 if(EXTI15_10_IRQ_flag==1) { flag_num=OSFlagPost((OS_FLAG_GRP *)&EventFlags, (OS_FLAGS ) EXTI15_10_IRQ_FLAG, (OS_OPT ) OS_OPT_POST_FLAG_SET, (OS_ERR *) &err ); UsartPrintf(USART1, "EventFlags Value %d\r\n",flag_num); //打印 EventFlags 事件标志组的值 EXTI15_10_IRQ_flag=0; } OSTimeDlyHMSM(0,0,0,50,OS_OPT_TIME_HMSM_STRICT,&err); //延时50ms } }
#include "MessageTask.h"
#include "MessageTask.h" /* MessageTask 计数打印一次运行次数 每隔 2s 切换一次灯状态 当切换到绿灯时发送绿灯的事件标志 并打印此时事件标志组的值 */ void MessageTask (void * p_arg) { OS_ERR err; int i=0; // OS_MSG_SIZE size; // char *p; // CPU_SR_ALLOC(); p_arg = p_arg; while (DEF_TRUE) { i++; UsartPrintf(USART1, "MessageTask Print %d\r\n",i); //打印 MessageTask 运行次数 LED_RED_L;LED_GREEN_H;LED_Blue_H; //单独亮红灯 OSTimeDlyHMSM(0,0,2,0,OS_OPT_TIME_HMSM_STRICT,&err); //延时2s UsartPrintf(USART1, "LED RED\r\n"); //打印 红灯 LED_Blue_L;LED_RED_H;LED_GREEN_H; //单独亮蓝灯 OSTimeDlyHMSM(0,0,2,0,OS_OPT_TIME_HMSM_STRICT,&err); //延时2s UsartPrintf(USART1, "LED BLUE\r\n"); //打印 蓝灯 LED_GREEN_L;LED_RED_H;LED_Blue_H; //单独亮绿灯 OSTimeDlyHMSM(0,0,2,0,OS_OPT_TIME_HMSM_STRICT,&err); //延时2s UsartPrintf(USART1, "LED GREEN\r\n"); //打印 绿灯 flag_num=OSFlagPost((OS_FLAG_GRP *)&EventFlags, (OS_FLAGS ) LED_GREEN_FLAG, (OS_OPT ) OS_OPT_POST_FLAG_SET, (OS_ERR *) &err ); UsartPrintf(USART1, "EventFlags Value %d\r\n",flag_num); //打印 EventFlags 事件标志组的值 } }
#include "CalculateTask.h"
如果测试有问题,可尝试将 CalculateTask 的延时降低
#include "CalculateTask.h" /* CalculateTask 等待事件标志 打印自己运行次数 然后等待1000ms */ void CalculateTask(void *p_arg) { OS_ERR err; int i=0; //int times; p_arg = p_arg; while (DEF_TRUE) { OSFlagPend((OS_FLAG_GRP *) &EventFlags, (OS_FLAGS ) LED_GREEN_FLAG+EXTI15_10_IRQ_FLAG,//表示俩个事件都要发生(无需同时) (OS_TICK )0,//无限制等待 事件标志组满足条件 (OS_OPT) OS_OPT_PEND_FLAG_SET_ALL+OS_OPT_PEND_FLAG_CONSUME, //等待事件标志,满足条件后全部清零 (CPU_TS *) 0, //时间戳 (OS_ERR *) &err ); i++; UsartPrintf(USART1, "CalculateTask Print %d\r\n",i); OSTimeDlyHMSM(0,0,1,0,OS_OPT_TIME_HMSM_STRICT,&err); //延时1000ms } }
测试效果截图:
这里需要注意的只有事件发生先后,事件标志组的值不一样
先发生绿灯事件,事件标志组的值是1
先发生按键中断事件,事件标志组的值是2
但肯定要满足俩事件相加,也就是事件标志组的值为3时,才会触发CalculateTask运行一次
CalculateTask运行一次后又会将 事件标志组 全部清零
代码逻辑问题修改:
顺便说一下下载工程的小逻辑问题需要修改一下:
之前没有注意到灯语打印的逻辑不对版,发现是将打印状态放在了延时语句之后
这会导致蓝灯时打印RED,换到延时之前打印就行了,
测试工程下载:
工程包含一份程序设计框架说明,不明白可以看看:
https://download.csdn.net/download/qq_64257614/90158960