UCOS操作系统——事件标志组(十四)
UCOS操作系统文章目录UCOS操作系统前言一、事件标志组二、相关函数1.创建事件标志组2. 等待事件标志组3.向事件标志组发布标志三、事件标志组实验前言前面我们提到过可以使用信号量来完成任务同步,这里我们再说一下另外一种任务同步的方法,就是事件标志组,事件标志组用来解决一个任务和多个事件之间的同步一、事件标志组有时候一个任务可能需要和多个事件同步,这个时候就需要使用事件标志组。事件标志组与任务之
UCOS操作系统
前言
前面我们提到过可以使用信号量来完成任务同步,这里我们再说一下另外一种任务同步的方法,就是事件标志组,事件标志组用来解决一个任务和多个事件之间的同步
一、事件标志组
有时候一个任务可能需要和多个事件同步,这个时候就需要使用事件标志组。事件标志组与任务之间有两种同步机制:“或”同步和“与”同步,当任何一个事件发生,任务都被同步的同步机制是“或”同步;需要所有的事件都发生任务才会被同步的同步机制是“与”同步,这两种同步机制如图所示
1、任务和 ISR(中断服务程序)都可以发布事件标志,但是,只有任务可以创建、删除事件标志组以及取消其他任务对事件标志组的等待。
2、任务可以通过调用函数 OSFlagPend()等待事件标志组中的任意个事件标志,调用函数OSFlagPend()的时候可以设置一个超时时间,如果过了超时时间请求的事件还没有被发布,那么任务就会重新进入就绪态。
3、我们可以设置同步机制为“或”同步还是“与”同步
二、相关函数
1.创建事件标志组
在使用事件标志组之前,需要调用函数 OSFlagCreate()创建一个事件标志组,OSFlagCreate()函数原型如下
void OSFlagCreate ( OS_FLAG_GRP *p_grp,
CPU_CHAR *p_name,
OS_FLAGS flags,
OS_ERR *p_err)
p_grp: 指向事件标志组,事件标志组的存储空间需要应用程序进行实际分配,我们可以按照下面的例子来定义一个事件标志组。
OS_FLAG_GRP EventFlag;
p_name: 事件标志组的名字。
flags: 定义事件标志组的初始值。32位bit,一个bit代表一个事件标志组
p_err: 用来保存调用此函数后返回的错误码。
2. 等待事件标志组
等待一个事件标志组需要调用函数 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 或者 32 比他。比如任务需要等待时间标志组的 bit0和 bit1 时(无论是等待置位还是清零),flag 是的值就为 0X03。(你要请求的位数是二进制1,其他是0,然后转16进制)
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: 用来保存调用此函数后返回的错误码
3.向事件标志组发布标志
调用函数 OSFlagPost()可以对事件标志组进行置位或清零,函数原型如下
OS_FLAGS OSFlagPost ( OS_FLAG_GRP *p_grp,
OS_FLAGS flags,
OS_OPT opt,
OS_ERR *p_err)
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: 保存调用此函数后返回的错误码。
三、事件标志组实验
#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "lcd.h"
#include "key.h"
#include "malloc.h"
#include "sram.h"
#include "beep.h"
#include "includes.h"
//ÈÎÎñÓÅÏȼ¶
#define START_TASK_PRIO 3
//ÈÎÎñ¶ÑÕ»´óС
#define START_STK_SIZE 128
//ÈÎÎñ¿ØÖÆ¿é
OS_TCB StartTaskTCB;
//ÈÎÎñ¶ÑÕ»
CPU_STK START_TASK_STK[START_STK_SIZE];
//ÈÎÎñº¯Êý
void start_task(void *p_arg);
//ÈÎÎñÓÅÏȼ¶
#define MAIN_TASK_PRIO 4
//ÈÎÎñ¶ÑÕ»´óС
#define MAIN_STK_SIZE 128
//ÈÎÎñ¿ØÖÆ¿é
OS_TCB Main_TaskTCB;
//ÈÎÎñ¶ÑÕ»
CPU_STK MAIN_TASK_STK[MAIN_STK_SIZE];
void main_task(void *p_arg);
//ÈÎÎñÓÅÏȼ¶
#define FLAGSPROCESS_TASK_PRIO 5
//ÈÎÎñ¶ÑÕ»´óС
#define FLAGSPROCESS_STK_SIZE 128
//ÈÎÎñ¿ØÖÆ¿é
OS_TCB Flagsprocess_TaskTCB;
//ÈÎÎñ¶ÑÕ»
CPU_STK FLAGSPROCESS_TASK_STK[FLAGSPROCESS_STK_SIZE];
//ÈÎÎñº¯Êý
void flagsprocess_task(void *p_arg);
//LCDË¢ÆÁʱʹÓõÄÑÕÉ«
int lcd_discolor[14]={ WHITE, BLACK, BLUE, BRED,
GRED, GBLUE, RED, MAGENTA,
GREEN, CYAN, YELLOW,BROWN,
BRRED, GRAY };
////////////////////////ʼþ±êÖ¾×é//////////////////////////////
#define KEY0_FLAG 0x01
#define KEY1_FLAG 0x02
#define KEYFLAGS_VALUE 0X00
OS_FLAG_GRP EventFlags; //¶¨ÒåÒ»¸öʼþ±êÖ¾×é
//¼ÓÔØÖ÷½çÃæ
void ucos_load_main_ui(void)
{
POINT_COLOR = RED;
LCD_ShowString(30,10,200,16,16,"ALIENTEK STM32F1");
LCD_ShowString(30,30,200,16,16,"UCOSIII Examp 12-1");
LCD_ShowString(30,50,200,16,16,"Event Flags");
LCD_ShowString(30,70,200,16,16,"ATOM@ALIENTEK");
LCD_ShowString(30,90,200,16,16,"2015/3/19");
POINT_COLOR = BLACK;
LCD_DrawRectangle(5,130,234,314); //»¾ØÐÎ
POINT_COLOR = BLUE;
LCD_ShowString(30,110,220,16,16,"Event Flags Value:0");
}
//Ö÷º¯Êý
int main(void)
{
OS_ERR err;
CPU_SR_ALLOC();
delay_init(); //ʱÖÓ³õʼ»¯
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//ÖжϷÖ×éÅäÖÃ
uart_init(115200); //´®¿Ú³õʼ»¯
LED_Init(); //LED³õʼ»¯
LCD_Init(); //LCD³õʼ»¯
KEY_Init(); //°´¼ü³õʼ»¯
BEEP_Init(); //³õʼ»¯·äÃùÆ÷
my_mem_init(SRAMIN);//³õʼ»¯ÄÚ²¿RAM
ucos_load_main_ui();//¼ÓÔØÖ÷UI
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
}
//¿ªÊ¼ÈÎÎñº¯Êý
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_CRITICAL_ENTER(); //½øÈëÁÙ½çÇø
//´´½¨Ò»¸öʼþ±êÖ¾×é
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);
OS_CRITICAL_EXIT(); //Í˳öÁÙ½çÇø
OSTaskDel((OS_TCB*)0,&err); //ɾ³ýstart_taskÈÎÎñ×ÔÉí
}
//Ö÷ÈÎÎñµÄÈÎÎñº¯Êý
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);
}
}
main_task()函数为主任务的任务函数,函数里面主要是获取按键值,如果按下 KEY0 的话就调用 OSFlagPost()向事件标志组 EventFlags 发布标志 KEY0_FLAG,如果按下 KEY1 的话就向事件标志组 EventFlags 发布 KEY1_FLAG。 在 main_task()函数中每调用一次 OSFlagPost()就 在 LCD 上显示事件标志组 EventFlags 的当前值并且通过串口输出这个值,我们可以通过这个值的变化来观察事件标志组各个事件产生的过程。
flagsprocess_task()函数为事件标志组处理任务的任务函数,在这个函数中我们一直等待事
件标志组 Eventflags 中相应的事件发生,当等待的事件发生的话就刷新 LCD 下方方框的背景颜
色,并且控制 LED1 反转。
此时是没有现象的,当我们按下 KEY0 的时候
此时事件标志组 EventFlags 的值为 1,因为我们按下 KEY0 的话EventFlags 的 bit0 就会置 1,但是此时 KEY1 还没有按下因此下方方框内的背景还是为白色。此时我们再按下 KEY1 键
可以看出此时 EventFlags 的值为 0,这是因为我们按下 KEY1 后,任务flagsprocess_task 等待的事件都发生了就会刷新一次下方方框内背景,并且 LED1 会反转。我们在调用函数 OSFlagPend()的时候设定了参数 opt 为 OS_OPT_PEND_FLAG_SET_ALL 和OS_OPT_PEND_FLAG_CONSUME,因此会清除相应的标志的,因此此时的事件标志组的值就为 0 了。
注意!其实当我们按下 KEY1 的一瞬间事件标志组 EventFlags 的值应该为 3 的,但是很快会被任务 flagsprocess_task 刷新掉显示为 0,这个过程非常快,在 LCD 上是看不出来的,不过串口是可以看出来的
更多推荐
所有评论(0)