您的位置:首页 > 其它

ucosii学习 事件标志组

2014-07-17 10:56 281 查看
FLAG-事件标志组管理,在UCOSII里我个人觉相对比较复杂,首先我们要有个大致的概念,就是FLAG事件组能用来干什么。以下只摘自一位网友博客上的,写的很好,转一下:gliethttp.cublog.cn

(一)描述://对于flag--"事件组"的使用,可以用一个简单的例子做说明:

// 比如,我现在用迅雷下载一部10集的连续剧,我打算10集全部下载完成之后,

//才开始正式看,现在3~10集因为种子原因,先早下完了,现在第1集下到了82%,

//第2集下到了97%,因为我的计划是10集全部下完才开始看,而第1集和第2集

//由于网络,种子等等各种原因,迟迟不能下载完成,进而导致我的计划被悬停,不能进行,

//已下载的8集,也因为前2集没能下完,而白白等待---这就等同于flag事件组,

//1~10集,每一集都是一个事件,因为我内定,10个事件全部完成之后,才进入下一事件--"观看"

//所以及早完成自己事件的第3~10集,将主动把自己通过flag事件组函数OSFlagPost()登记到事件组上,

//他们不关心,其他友邻事件完成否,只专注自己的事件是否完成,自己的事件一旦完成

//就登记到事件组上,最后3~10集,都把自己登记上去了,只剩下第1集和第2集,

//一旦某天的某个时刻,第2集下完了,那么第2集也把自己登记到事件组上,这样整个事件距离完成

//还剩下一个事件,就是第1集是否下载完成,只要第1集下载完成,那么我内定的"观看"计划

//开始启动,过了3分钟,由于网速提高,竟以300k的速度开始下载第1集,1分钟之后,

//第1集也下载完成了,第1集立即调用OSFlagPost事件组函数,将自己登记到事件组上,

//ok,OSFlagPost()检测到所有事件已经完成,OSFlagPost()将是"我"自动进入下一事件---"观看"

// 还有一点就是关于flag事件组和Sem,Mbox,Queue的区别之处,flag事件组不使用事件控制矩阵来

//管理被阻塞在事件上的task进程,flag事件组使用pgrp的双向链表来挂接起所有task,

//在OSFlagPost()中将遍历这个链表,查找符合当前flag事件的task,将该task从双向链表中摘下

//然后放入就绪控制矩阵中,之所以这样,是因为flag事件组不像Sem,Mbox,Queue那样具有二值性,

//即Sem,Mbox,Queue,要么有,要么没有,flag事件组,还要进一步判断,有的话,是什么程度的有。

通过以上介绍,对于FLAG事件组有了大致的了解,但要具体了解其实现过程,最好还是去看看其原代码,原代码就不在这贴出来了。UCOSII使用等待事件标志组的任务列表是一个双向链表,使用了3个数据结构:OS-FLAG-GRP,任务控制块TCB,OS-FLAG-NODE。

特别是对OS-FLAG-NODE我觉得要注意一下,当一个任务开始等待某些事件标志时,就建立一个OS-FLAG-NODE数据结构。当这些等待事件标志发生后,这个数据被删除。具体分析看邵贝贝老师的书,写的比较详细。对于原代码自己在有些细节上还是有些没有弄清楚,但基本用用还是可以的,下面就来举几个自己实现过的例子吧。

(二)举例。以下举的例子,是自己从网上找来的,再经过一定的修改,在STM32上跑的。

1).简单的OSFLAGPEND()与OSFLAGPOST()函数的应用

显示用,LCD1602和两个LED灯(条件有限啊)

TASK1():建一个OSFlagPend() 若等待事件标志没有发生 该函数挂起该任务若事件标志发生,则LCD显不一个值,LED灯每隔一秒闪一次。

TASK2():OSFlagPost()向任务一发送一个信号量

TASK3():OSFlagPost()向任务一发送一个信号量,具体看代码

static void Task1(void *pdata)

{

INT8U error;

INT8U i=0;

pdata = pdata;

while(1)

{

//若等待事件标志没有发生 该函数挂起该任务

OSFlagPend(

Sem_F, //请求信号量集

(OS_FLAGS)3, //请求第0位和第1位信号

OS_FLAG_WAIT_SET_ALL,//且都置为1时为有效 否则任务挂在这里

0, //无限等待 直到收到为止

&error);

//OSFLAGPEND收到有效信号后 在LCD显示字符串 闪两个LED 可以跟据自己代码而定

write_com(0x80+10);

while(table1[i] != '\0')

{

write_dat(table1[i]);

i++;

}

GPIO_ResetBits(GPIOD, GPIO_Pin_15);

GPIO_ResetBits(GPIOD, GPIO_Pin_13);

OSTimeDlyHMSM(0,0,1,0); //任务挂起1秒 否则优先级低的任务就没机会执行了

GPIO_SetBits(GPIOD, GPIO_Pin_15);

GPIO_SetBits(GPIOD, GPIO_Pin_13);

OSTimeDlyHMSM(0,0,1,0); //让两个LED每秒闪一次

}

}

static void Task2(void *pdata)

{

INT8U error;

pdata = pdata;

while(1)

{

OSFlagPost( //发送信号量集

Sem_F,

(OS_FLAGS)2, //给第1位发信号

OS_FLAG_SET, //信号量置1

&error

);

OSTimeDlyHMSM(0, 0, 1, 0); //等待1秒

}

}

static void Task3(void *pdata)

{

INT8U error;

pdata = pdata;

while(1)

{

//在执行此函数时 发生任务切换 去执行TASK1 在OSFLAGPOST中发生任务切换

OSFlagPost( //发送信号量集Sem_F,(OS_FLAGS)1, //给第1位发信号,OS_FLAG_SET, //信号量置1,&error);

OSTimeDlyHMSM(0, 0, 1, 0); //等待1秒

}

}

MAIN函数大致如下:

void main(void)

{

#if (OS_TASK_NAME_SIZE > 14) && (OS_TASK_STAT_EN > 0)

INT8U err;

#endif

//目标板初始化,

Target_Init();

lcd_init();

OSInit();

//设置空闲任务名称

#if OS_TASK_NAME_SIZE > 14

OSTaskNameSet(OS_TASK_IDLE_PRIO, "uC/OS-II Idle", &err);

#endif

//设置统计任务名称

#if (OS_TASK_NAME_SIZE > 14) && (OS_TASK_STAT_EN > 0)

OSTaskNameSet(OS_TASK_STAT_PRIO, "uC/OS-II Stat", &err);

#endif

Sem_F = OSFlagCreate(0,&error);

//用任务建立任务

OSTaskCreateExt(APP_TaskStart, //void (*task)(void *pd) 任务首地址

(void *)0, //void *pdata 数据指针

&APP_TaskStartStk[APP_TASK_START_STK_SIZE - 1], //OS_STK *ptos 指向任务堆栈栈顶的指针

(INT8U)APP_TASK_START_PRIO, //INT8U prio 任务优先级

(INT16U)APP_TASK_START_ID, //INT16U id 任务的ID号

&APP_TaskStartStk[0], //OS_STK *pbos 指向任务堆栈栈底的指针

(INT32U)APP_TASK_START_STK_SIZE, //INT32U stk_size 堆栈容量

(void *)0, //void *pnext 数据指针

OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR); //INT16U opt 设定OSTaskCreateExt的选项

OSStart();

}

上述例子中,TASK1()OSFLGAPEND()需要第0,1位都置位时才有效,任务刚进来时不满足即被挂起。等待着OSFLGAPOST()把相应位置1。TASK2,TASK3分别把第0,1位置位。即等到执行完TASK3时,TASK1()有效。

2.)若TASK1()如下

static void Task1(void *pdata)

{

INT8U error;

INT8U i=0;

pdata = pdata;

while(1)

{

//若等待事件标志没有发生 该函数并不挂起该任务

OSFlagAccept( //请求信号量集 Sem_F, (OS_FLAGS)3, //请求第0位和第1位信号 OS_FLAG_WAIT_SET_ALL, //第0位和第1位信号都为1为有效&error);

//OSFLAGPEND收到有效信号后 在LCD显示字符串 闪两个LED 可以跟据己代码而定

write_com(0x80+10);

while(table1[i] != '\0')

{

write_dat(table1[i]);

i++;

}

GPIO_ResetBits(GPIOD, GPIO_Pin_15);

GPIO_ResetBits(GPIOD, GPIO_Pin_13);

OSTimeDlyHMSM(0,0,1,0); //任务挂起1秒 否则优先级低的任务就没机会执行了

GPIO_SetBits(GPIOD, GPIO_Pin_15);

GPIO_SetBits(GPIOD, GPIO_Pin_13);

OSTimeDlyHMSM(0,0,1,0); //让两个LED每秒闪一次

}

}

即为无等待获取,当信号量不满足,任务也不挂起,即TASK2(),TASK3()不给OSFLAGCCEPT()发送信号,TASK1()仍然执行,在本例中,LCD,LED显示正常。

3.)我们还可以用OSFLGAQUERY()来查询事件标志组的状态。跟据状态来执行自己所期望的代码,用起来很方便且很好。

static void Task1(void *pdata)

{

INT8U error;

INT8U i=0,j=0,k=0,Flags;

pdata = pdata;

while(1)

{

Flags=OSFlagQuery( //查询事件标志组的状态 Sem_F, &error );

switch(Flags)

{

case 1:

write_com(0x80+10);

while(table1[i] != '\0')

{

write_dat(table1[i]);

i++;

}

break;

case 2:

write_com(0x80+0x40);

while(table2[j] != '\0')

{

write_dat(table2[j]);

j++;

}

break;

case 3:

write_com(0x80+0x40+

;

while(table3[k] != '\0')

{

write_dat(table3[k]);

k++;

}

break;

}

OSTimeDlyHMSM(0, 0, 1, 0); //等待2秒





(三)总结,对于FLAG事件组大致就这些吧。自己也正在学习中,可能有很多地方还不对。最后再讲一点体会吧。关键是理解两个函数,OSFLAGPEND(),OSFLAGPOST()。具体它们是在干什么的:

OSFLAGPEND(): 任务等待事件标志组中的事件标志,可以是多个事件标志的不同组合方式。可以等待任意指定事件标志位置位或清0,也可以是全部指定事件标志位置位或清0。如果任务等待的事件标志位条件尚不满足,则任务会被挂起,直到指定的事件标志组合发生或指定的等待时间超时。

OSFLAGPOST(): 给出设定的事件标志位。指定的事件标志位可以设定为置位或清除。若OSFlagPost()设置的事件标志位正好满足某个等待使劲标志组的任务,则OSFlagPost()将该任务设为就绪。注意: 必须先创建事件标志组,然后使用; 这个函数的运行时间决定于等待事件标志组的任务的数目; 关闭中断的时间也取决于等待事件标志组的任务的数目。

(四)参考资料:

来自各位网友博客

《嵌入式实时操作系统第2版》
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: