ucosii学习 事件标志组

来源:互联网 发布:终结者2审判日 知乎 编辑:程序博客网 时间:2024/06/05 07:19

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+ 8) ;
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版》

0 0
原创粉丝点击