目录
事件标志组:
事件标志组API函数:
创建事件标志组:
等待事件标志组:
向事件标志组发送标志:
事件标志组实验:
事件标志组:
有时候一个任务可能需要和多个事件同步这个时候就需要使用事件标志组。时间标志组与任务之间有两种同步机制:“或”同步和“与”同步,当一个事件发生,任务都被同步的同步机制是“或”同步,需要所有的事件都发生任务才会被同步的机制是“与”同步。如下图所示:
下边是正点原子对他的解释:
为什么要使用事件标志组???直接搞个全局变量不就行了吗???真的可以吗
事件标志组是实现多任务同步的有效机制之一。在裸机编程的时候使用全局变量确实方便,但是在加上RTOS后就是另一种情况了。
- 使用事件标志组可以让RTOS内核有效的管理任务,而全局变量是无法做到的,任务的超时等机制需要用户自己去实现。(麻烦至极)
- 使用全局变量就要防止多任务的访问冲突,而使用事件标志组则处理好了这个问题,用户无需担心。
- 使用事件标志组可以有效的解决中断服务程序和任务之间的同步问题。
事件标志组API函数:
创建事件标志组:
在使用事件标志组之前,需要调用OSFlagCreate()创建一个事件标志组,而在创建事件标志组之前需要先定一个事件标志组(eg: OS_FLAG_GRP EventFlag;)。
OSFlagCreate()函数原型:
p_grp:指向事件标志组,
p_name:事件标志组的名字。
flags:定义事件标志组的初始值。
p_err:用来保存调用此函数后返回的错误码
等待事件标志组:
等待一个事件标志组需要调用函数OSFlagPend()函数,函数原型如下:
OSFlagPend()函数允许将事件标志组里事件标志的“与或”组合状态设置成任务的等待条件。
任务等待的条件可以使标志组里任意一个标志置位或者清零,也可以是所有事件标志都置位或者清零。如果任务等待的事件标志组不满足设置的条件,那么该任务被置位挂起,直到等待的事件标志组满足条件,指定的超时时间到,事件标志被删除或者被另一个任务终止了该任务的挂起状态。
参数:
p_grp : 指向时间标志组,
flags : bit序列,任务需要等待事件标志组的哪个位就是把这个序列对应的位置1.根据设置这个序列可以是8bit,16bit,32bit比如任务需要等待事件标志组的bit0或者bit1(无论是等待置位还是清零)flag的值就是0x03。
timeout:指定等待事件标志组的超时时间(时钟节拍数),如果在指定的超时时间内所等待的一个或多个事件没有发生,那么任务恢复运行。如果此值设置为 0,则任务就将一直等待下去,直到一个或多个事件发生。
opt:决定任务等待的条件是所有标志置位、所有标志清零、任意一个标志置位还是任 意一个标志清零,具体定义如下:
OS_OPT_PEND_FLAG_CLR_ALL 等待事件标志组所有的位清零
OS_OPT_PEND_FLAG_CLR_ANY 等待事件标志组中任意一个标志清零
OS_OPT_PEND_FLAG_SET_ALL 等待事件标志组中所有的位置位
OS_OPT_PEND_FLAG_SET_ANY 等待事件标志组中任意一个标志置位
上边调用四个选项的时候还可以搭配下边三个选项。
OS_OPT_PEND_FLAG_CONSUME 用来设置是否保留该事件的标志状态
OS_OPT_PEND_NON_BLOCKING 标志组不满足条件时,不挂起任务
OS_OPT_PEND_BLOCKING 标志组不满足条件时挂起任务
这里应该注意选项OS_OPT_PEND_FLAG_CONSUME 的使用方法,如果我们希望任务等待事件标志组的任意一个标志置位,并在满 足条件后将对应的标志清零,那么就可以搭配OS_OPT_PEND_FLAG_CONSUME。
p_ts:指向一个时间戳,记录了发送、终止和删除事件标志组的时刻,如果为这个指针赋值 NULL,则函数的调用者将不会收到时间戳。
p_err:用来保存调用此函数后返回的错误码。
向事件标志组发送标志:
调用函数OSFlagPost()可以对事件标志组经行置位或者清零,函数原型如下:
一般情况下,需要经行置位或者清零的标志由一个掩码确定(参数:flag)。OSFlagPost()修改完事件标志以后,将检查并使那些等待条件已经满足的任务进入就绪状态,该函数可以对已经置位或者清零的标志进行重复置位和清零操作。
参数:
p_grp:指向事件标志组。
flags:决定对哪些位清零和置位,当 opt 参数为 OS_OPT_POST_FLAG_SET 的时,参flags 中置位的位就会在事件标志组中对应的位也将被置位。当 opt OS_OPT_POST_FLAG_CLR 的时候参数 flags 中置位的位在事件标志组中对应的位将被清零。
opt:决定对标志位的操作,有两种选项。
OS_OPT_POST_FLAG_SET对标志位进行置位操作
OS_OPT_POST_FLAG_CLR对标志位进行清零操作p_err: 保存调用此函数后返回的错误码。
事件标志组实验:
设计一个程序,只有按下KEY0和KEY1(不需要同时按下)时任务任务C才可以执行。
实验源码:
#include "led.h" #include "delay.h" #include "sys.h" #include "usart.h" #include "includes.h" #include "os_app_hooks.h" #include "key.h" //UCOSIII中以下优先级用户程序不能使用,ALIENTEK //将这些优先级分配给了UCOSIII的5个系统内部任务 //优先级0:中断服务服务管理任务 OS_IntQTask() //优先级1:时钟节拍任务 OS_TickTask() //优先级2:定时任务 OS_TmrTask() //优先级OS_CFG_PRIO_MAX-2:统计任务 OS_StatTask() //优先级OS_CFG_PRIO_MAX-1:空闲任务 OS_IdleTask() //创建任务A //定义任务优先级 #define TASK_A_PRIO 3 //定义任务控制块 OS_TCB TASK_A_TCB; //定义任务堆栈大小 #define TASK_A_STK_SIZE 128 //定义任务堆栈 CPU_STK TASK_A_STK[TASK_A_STK_SIZE]; //定义任务函数 void TASK_A(void *arg); //创建任务B //定义任务优先级 #define TASK_B_PRIO 4 //定义任务控制块 OS_TCB TASK_B_TCB; //定义任务堆栈大小 #define TASK_B_STK_SIZE 128 //定义任务堆栈 CPU_STK TASK_B_STK[TASK_B_STK_SIZE]; //定义任务函数 void TASK_B(void *arg); //创建任务C //定义任务优先级 #define TASK_C_PRIO 5 //定义任务控制块 OS_TCB TASK_C_TCB; //定义任务堆栈大小 #define TASK_C_STK_SIZE 128 //定义任务堆栈 CPU_STK TASK_C_STK[TASK_C_STK_SIZE]; //定义任务函数 void TASK_C(void *arg); //共享资源 u8 share_res[256]; //定义事件标志 #define KEY0_FLAG 0x01//0000 0001 #define KEY1_FLAG 0x02//0000 0010 //定义事件标志组 OS_FLAG_GRP EventFlag; int main(void) { OS_ERR err1;//错误码变量 CPU_SR_ALLOC();//定义临界区需要的变量 //硬件初始化 delay_init(); //延时初始化 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //中断分组配置 uart_init(115200); //串口波特率设置 LED_Init(); KEY_Init(); OSInit(&err1);//初始化UCOSIII OS_CRITICAL_ENTER();//进入临界区代码 //创建开始任务A OSTaskCreate((OS_TCB * )&TASK_A_TCB, //任务控制块 (CPU_CHAR * )"main TASKA", //任务名字 (OS_TASK_PTR )TASK_A, //任务函数 (void * )0, //传递给任务函数的参数 (OS_PRIO )TASK_A_PRIO, //任务优先级 (CPU_STK * )&TASK_A_STK[0], //任务堆栈基地址 (CPU_STK_SIZE)TASK_A_STK_SIZE/10, //任务堆栈深度限位 (CPU_STK_SIZE)TASK_A_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 * )&err1); //存放该函数错误时的返回值 OS_CRITICAL_EXIT();//退出临界区代码 OSStart(&err1);//开启UCOSIII while(1); } //开始任务函数 void TASK_A(void *arg) { OS_ERR err2_3;//错误码变量 CPU_SR_ALLOC();//定义临界区需要的变量 arg = arg; CPU_Init(); #if OS_CFG_STAT_TASK_EN > 0u OSStatTaskCPUUsageInit(&err2_3); //统计任务 #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,&err2_3); #endif //#if OS_CFG_APP_HOOKS_EN //使用钩子函数 // App_OS_SetAllHooks(); //#endif #if OS_CFG_SCHED_ROUND_ROBIN_EN //使用时间片轮转调度功能,时间片长度为一个时钟节拍即1*5=5ms OSSchedRoundRobinCfg(DEF_ENABLED,1,&err2_3); #endif OS_CRITICAL_ENTER();//进入临界区代码 //创建事件标志组 OSFlagCreate((OS_FLAG_GRP *)&EventFlag, (CPU_CHAR *)"EventFlag", (OS_FLAGS )0, (OS_ERR *)&err2_3 ); //创建开始任务B OSTaskCreate((OS_TCB * )&TASK_B_TCB, //任务控制块 (CPU_CHAR * )"main TASKB", //任务名字 (OS_TASK_PTR )TASK_B, //任务函数 (void * )0, //传递给任务函数的参数 (OS_PRIO )TASK_B_PRIO, //任务优先级 (CPU_STK * )&TASK_B_STK[0], //任务堆栈基地址 (CPU_STK_SIZE)TASK_B_STK_SIZE/10, //任务堆栈深度限位 (CPU_STK_SIZE)TASK_B_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 * )&err2_3); //存放该函数错误时的返回值 //创建开始任务C OSTaskCreate((OS_TCB * )&TASK_C_TCB, //任务控制块 (CPU_CHAR * )"main TASKC", //任务名字 (OS_TASK_PTR )TASK_C, //任务函数 (void * )0, //传递给任务函数的参数 (OS_PRIO )TASK_C_PRIO, //任务优先级 (CPU_STK * )&TASK_C_STK[0], //任务堆栈基地址 (CPU_STK_SIZE)TASK_C_STK_SIZE/10, //任务堆栈深度限位 (CPU_STK_SIZE)TASK_C_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 * )&err2_3); //存放该函数错误时的返回值 OS_CRITICAL_EXIT();//退出临界区代码 //任务一执行完函数之后删掉自身 OSTaskDel((OS_TCB *)0,&err2_3); } //任务B任务函数 高优先级 void TASK_B(void *arg) { // u8 key; OS_ERR err_B; u8 key; OS_FLAGS num_flag; while(1) { key = KEY_Scan(0); //扫描按键 if(key==KEY0_PRES) { printf("KEY0:向事件标志组发送标志!\r\n"); num_flag=OSFlagPost(&EventFlag,KEY0_FLAG,OS_OPT_POST_FLAG_SET,&err_B);//向事件标志组发送标志 printf("事件标记组的值为%d\r\n",num_flag); LED0 = ~LED0; } else if(key==KEY1_PRES) { printf("KEY1:向事件标志组发送标志!\r\n"); num_flag=OSFlagPost(&EventFlag,KEY1_FLAG,OS_OPT_POST_FLAG_SET,&err_B);//向事件标志组发送标志 printf("任务B:事件标记组的值为%d\r\n",num_flag); LED1 = ~LED1; } OSTimeDlyHMSM(0,0,0,10,OS_OPT_TIME_PERIODIC,&err_B); //延时10ms } } //任务C任务函数 中优先级 void TASK_C(void *arg) { OS_ERR err_C; while(1) { //等待事件标志组 OSFlagPend (&EventFlag,KEY0_FLAG+KEY1_FLAG,0,OS_OPT_PEND_FLAG_SET_ALL,0,&err_C); printf("任务C等到事件\r\n"); printf("任务C:事件标记组的值为%d\r\n",EventFlag.Flags); OSTimeDlyHMSM(0,0,1,0,OS_OPT_TIME_PERIODIC,&err_C); //延时1s } }
实验现象:
文章参考:正点原子《STM32F1 UCOS开发手册_V2.0》