序言
前面,我们了解到可以使用信号量来完成任务同步,这里我们再讲解一下另一种任务同步的方法:事件标志组。
再本篇文章中,我会首先介绍一下什么是是事件标志组,它是一种什么样的实现思想,然后我会介绍相关的API,最后会有一个小实验。
什么是事件标志组
有时候一个任务可能需要和多个事件同步,这个时候就需要使用事件标志组。事件标志组也是一个内核对象。事件标志组与任务之间有两种同步机制:“或”同步和“与”同步,当任何一个事件发生,任务都被同步的同步机制是“或同步”;需要所有的事件都发生任务才会被同步的机制是“与”同步,这两种同步机制如下图所示:
- UCOSIII中的事件标志组是OS_FLAG_GRP,在os.h文件中有定义,事件标志组中也包含了一串任务,这些任务都在等待着事件标志组中的某些标志位被置1或者清零,在使用前,必须创建事件标志组。
- 任务和ISR都可以发布事件标志,但是,只有任务可以创建事件标志、删除事件标志组以及取消其它任务对事件标志组的等待。
- 任务可以通过调用函数OSFlagPend()等待事件标志组中的任意个事件标志,调用函数OSFlagPend()的时候可以设置一个超时时间,如果过了超时时间请求的事件还没有被发布,那么任务就会进入就绪态。
- 我们可以设置同步机制为“或”同步还是“与”同步。
相关函数
函数 | 描述 |
---|---|
OSFlagCreate | 创建事件标志组 |
OSFlagDel() | 删除事件标志组 |
OSFlagPend() | 等待事件标志组 |
OSFlagPendAbort() | 取消等待事件标志组 |
OSFlagPendGetFlagRdy() | 获取使任务就绪的事件标志 |
OSFlagPost() | 向事件标志组发布标志 |
创建事件标志组
OSFlagCreate
void OSFlagCreate (OS_FLAG_GRP *p_grp,
CPU_CHAR *p_name,
OS_FLAGS flags,
OS_ERR *p_err)
- p_grp:指向事件标志组的指针
- p_name:事件标志组的名字
- flags:定义事件标志组的初始值
- p_err:用来保存调用此函数后返回的错误码
OSFlagPend
OS_FLAGS OSFlagPend (OS_FLAG_GRP *p_grp,
OS_FLAGS flags,
OS_TICK timeout,
OS_OPT opt,
CPU_TS *p_ts,
OS_ERR *p_err)
- p_grp:指向事件标志组
- flags:bit序列,任务需要等待事件标志组的哪个位就把这个序列对应的位置1,根据设置这个序列可以是8bit、16bit或者32bit。
- timeout:指向等待事件标志组的超时时间,如果在指定的超时时间内所等待的一个或多个事件没有发生,那么任务恢复运行。如果此值设置为0,则任务就将一直等待下去,直到一个或多个事件发生。
- opt:选项,具体选项含义,可以自己看看源码注释
- p_ts:指向一个时间戳,记录了发送、终止和删除事件标志组的时刻,如果为这个指针赋值NULL,则此函数的调用者将不会收到时间戳。
- p_err:用来保存调用此函数后的错误码
OSFlagPost
OS_FLAGS OSFlagPost (OS_FLAG_GRP *p_grp,
OS_FLAGS flags,
OS_OPT opt,
OS_ERR *p_err)
-
p_grp:指向事件标志组
-
flags:决定哪些位清零
-
opt:具体选项含义可以看源码注释
-
p_err:保存调用此函数后返回的错误码
事件标志组实验
实验概述
任务目标:设计一个程序,只有按下KEY0和KEY1时任务flagsprocess_task任务才能执行。
任务设计:分析上面的任务目标,我们可以使用事件标志组来实现,按下KEY0和KEY1作为两个不同的事件,只有这两个事件同时发生了才能执行任务flagprocess_task。
实验代码
首先定义一个事件标志组
事件标志组//
#define KEY0_FLAG 0x01
#define KEY1_FLAG 0x02
#define KEYFLAGS_VALUE 0X00
OS_FLAG_GRP EventFlags; //定义一个事件标志组
然后创建一个事件标志组
//创建一个事件标志组
OSFlagCreate((OS_FLAG_GRP*)&EventFlags, //指向事件标志组
(CPU_CHAR* )"Event Flags", //名字
(OS_FLAGS )KEYFLAGS_VALUE, //事件标志组初始值
(OS_ERR* )&err); //错误码
接着创建两个任务
//创建主任务
OSTaskCreate((OS_TCB* )&Main_TaskTCB,
(CPU_CHAR* )"Main task",
(OS_TASK_PTR )main_task,
(void* )0,
(OS_PRIO )MAIN_TASK_PRIO,
(CPU_STK* )&MAIN_TASK_STK[0],
(CPU_STK_SIZE)MAIN_STK_SIZE/10,
(CPU_STK_SIZE)MAIN_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);
//创建MSGDIS任务
OSTaskCreate((OS_TCB* )&Flagsprocess_TaskTCB,
(CPU_CHAR* )"Flagsprocess task",
(OS_TASK_PTR )flagsprocess_task,
(void* )0,
(OS_PRIO )FLAGSPROCESS_TASK_PRIO,
(CPU_STK* )&FLAGSPROCESS_TASK_STK[0],
(CPU_STK_SIZE)FLAGSPROCESS_STK_SIZE/10,
(CPU_STK_SIZE)FLAGSPROCESS_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);
下面实现来个任务
//主任务的任务函数
void main_task(void *p_arg)
{
u8 key,num;
OS_FLAGS flags_num;
OS_ERR err;
while(1)
{
key = KEY_Scan(0); //扫描按键
if(key == KEY0_PRES)
{
//向事件标志组EventFlags发送标志
flags_num=OSFlagPost((OS_FLAG_GRP*)&EventFlags,
(OS_FLAGS )KEY0_FLAG,
(OS_OPT )OS_OPT_POST_FLAG_SET,
(OS_ERR* )&err);
LCD_ShowxNum(174,110,flags_num,1,16,0);
printf("事件标志组EventFlags的值:%d\r\n",flags_num);
}
else if(key == KEY1_PRES)
{
//向事件标志组EventFlags发送标志
flags_num=OSFlagPost((OS_FLAG_GRP*)&EventFlags,
(OS_FLAGS )KEY1_FLAG,
(OS_OPT )OS_OPT_POST_FLAG_SET,
(OS_ERR* )&err);
LCD_ShowxNum(174,110,flags_num,1,16,0);
printf("事件标志组EventFlags的值:%d\r\n",flags_num);
}
num++;
if(num==50)
{
num=0;
LED0 = ~LED0;
}
OSTimeDlyHMSM(0,0,0,10,OS_OPT_TIME_PERIODIC,&err); //延时10ms
}
}
//事件标志组处理任务
void flagsprocess_task(void *p_arg)
{
u8 num;
OS_ERR err;
while(1)
{
//等待事件标志组
OSFlagPend((OS_FLAG_GRP*)&EventFlags,
(OS_FLAGS )KEY0_FLAG+KEY1_FLAG,
(OS_TICK )0,
(OS_OPT )OS_OPT_PEND_FLAG_SET_ALL+OS_OPT_PEND_FLAG_CONSUME,
(CPU_TS* )0,
(OS_ERR* )&err);
num++;
LED1 = ~LED1;
LCD_Fill(6,131,233,313,lcd_discolor[num%14]);
printf("事件标志组EventFlags的值:%d\r\n",EventFlags.Flags);
LCD_ShowxNum(174,110,EventFlags.Flags,1,16,0);
}
}
具体的实验现象,自己抄一下代码,烧录一下历程,分析一下就知道了