uCOS事件标志组管理OS_Flag.C
事件标志组是用来干什么的呢?在一个网友的博客上看到一个描述,用一个很形象的例子说明了这个事件组的用途。
现在要用迅雷下载一部10集的连续剧,10集全部下载完成之后才开始正式看。现在3~10集因为种子原因,先下载完成了。而第1集下载到83%,第2集下载到97%。因为计划是10集全部下完才开始看,而第1集和第2集由于网络和种子等各种原因,迟迟不能下载完成,进而导致计划被悬停,不能进行。已下载的8集也因为前2集没能下完而白白等待,不能观看。这就等同于Flag事件组。
1~10集,每一集都是一个事件,内定10个事件全部完成之后,才进入下一个事件——“观看”。所以,及早完成自己事件的第3~10集,将主动把自己通过flag事件组函数OSFlagPost()登记到事件组上,它们不关心其它友邻事件是否完成,只专注自己的事件是否完成,自己的事件一旦完成,就登记到事件组上。最后3~10集都把自己登记上去了,只剩下第1集和第2集,一旦某天的某个时刻,第2集下载完成了,第2集也把自己登记到事件组上去,这样整个事件距离完成就剩下一个事件,就是第1集是否下载完成。只要第1集下载完成,内定的“观看”计划就可以开始启动。
过了2钟,由于网速提高,竟以400K的速度开始下载第1集,很快第1集也载完成了。第1集立即调用OSFlagPost()事件函数将自己登记到事件组上。OSFlagPost()检测到所有事件已经完成,OSFlagPost()将自动进入下一事件——“观看”。
Flag事件组与Sem、Mbox、Queue的区别之处,事件组不使用事件控制矩阵来管理被阻塞在事件上的task进程,Flag事件组使用pgrp的双向链表来挂起所有task,在OSFlagPost()中遍历这个链表,查找符合当前事件的task,将该task从双向链表中摘下,然后放入就绪控制矩阵中。之所以这样,是因为事件组不像Sem、Mbox、Queue那样具有二值性,即Sem、Mbox、Queue要么有要么没有,Flag事件组还要进一步判断,有的话,是什么程序的有。
通过以上介绍,对Flag事件的用途有了大致的了解,在详细讲述之前,先上个简单的例子,加深一下对事件组使用的理解。
简单的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秒
}
}