UCOSIII操作系统
UCOSIII其他内容导航不迷路
UCOSIII操作系统-简介
【UCOSIII操作系统】任务篇(1)创建任务
【UCOSIII操作系统】任务篇(2)相关API函数
【UCOSIII操作系统】系统初始化篇(1)系统初始化
【UCOSIII操作系统】系统初始化篇(2)CPU,SysTick,内存初始化
【UCOSIII操作系统】硬件初始化篇(1)硬件初始化以及开始运行系统
【UCOSIII操作系统】消息队列篇(1)消息队列
【UCOSIII操作系统】消息队列篇(2)任务消息队列
【UCOSIII操作系统】信号量与互斥量篇(1)信号量
【UCOSIII操作系统】信号量与互斥量篇(2)互斥量
【UCOSIII操作系统】信号量与互斥量篇(3)任务信号量
【UCOSIII操作系统】中断管理篇
【UCOSIII操作系统】临界段篇
【UCOSIII操作系统】软件定时器篇
【UCOSIII操作系统】内存管理篇
已完结
说在前面:
这个内容不适合0基础的人,因为这里只讲了应用层面的东西,并没有深入内核讲解,所以要从零开始学UCOSIII的朋友,可以先去学完入门内容,再来观看这个笔记加深印象。
这篇文章是个人学习整理,如有错误请指正
UCOSIII操作系统——事件篇
- 前面我们讲过可以使用信号量来完成任务同步,这里我们再讲解一-下另外一种任务同步的
方法,就是事件标志组,事件标志组用来解决一个任务和多个事件之间的同步。 - 事件是一种实现任务间通信的机制,主要用于实现多任务间的同步,但事件通信只能是事件类型的通信,无数据传输。与信号量不同的是,它可以实现一对多,多对多的同步。即一个任务可以等待多个事件的发生:可以是任意一个事件发生时唤醒任务进行事件处理;也可以是几个事件都发生后才唤醒任务进行事件处理。同样,也可以是多个任务同步多个事件。
- 相当于标志位(可以这么理解吧)
- 事件不与任务相关联,事件相互独立,一个32位的变量就是事件的集合,用于标识该任务发生的事件类型,其中每一位表示一种事件类型 (0表示该事件类型未发生、1表示该事件类型已经发生),一共32种事件。
- 32个事件,一个32位的变量,每一位表示一个事件
事件相关的API函数
函数 | 描述 |
---|---|
OSFlagCreate() | 创建事件标志组(常用) |
OSFlagDel() | 删除事件标志组 |
OSFlagPend() | 等待事件标志组(常用) |
OSFlagPendAbort() | 取消等待事件标志组 |
OSFlagPost() | 向事件标志组发布标志(常用) |
OSFlagPendGetFlagsRdy() | 获取使任务就绪的事件标志 |
创建事件标志组->OSFlagCreate()
- 函数原型
void OSFlagCreate (OS_FLAG_GRP *p_grp, //事件标志组指针
CPU_CHAR *p_name, //命名事件标志组
OS_FLAGS flags, //标志初始值
OS_ERR *p_err) //返回错误类型
事件标志组指针: 事件标志组的存储空间需要应用程序进行实际分配,我们可以
按照下面的例子来定义一个事件标志组。
OS_FLAG_GRP flag_grp; //声明事件标志组
标志初始值赋0就好
- 创建实例
/* 创建事件标志组 flag_grp */
OSFlagCreate ((OS_FLAG_GRP *)&flag_grp, //指向事件标志组的指针
(CPU_CHAR *)"FLAG For Test", //事件标志组的名字
(OS_FLAGS )0, //事件标志组的初始值
(OS_ERR *)&err); //返回错误类型
向事件标志组发布标志->OSFlagPost()
OSFlagPost()用于设置事件组中指定的位。使用该函数接口时,通过参数指定的事件标志来设置事件的标志位,然后遍历等待在事件对象上的事件等待列表,判断是否有任务的事件激活要求与当前事件对象标志值匹配,如果有,则唤醒该任务。简单来说,就是设置我们自己定义的事件标志位为1, 并且看看有没有任务在等待这个事件,有的话就唤醒它。
- 函数原型
OS_FLAGS OSFlagPost (OS_FLAG_GRP *p_grp, //事件标志组指针
OS_FLAGS flags, //选定要操作的标志位
OS_OPT opt, //选项
OS_ERR *p_err) //返回错误类型
flag 赋值有多种方法(这里是确定哪一位要操作,并不是置位复位)
//这是一种方法
#define KEY0_FLAG 0x01
#define KEY1_FLAG 0x02
//这是另一种方法
#define KEY1_EVENT (0x01 << 0)//设置事件掩码的位0
#define KEY2_EVENT (0x01 << 1)//设置事件掩码的位1
opt选项 (通过这个选项来确定标志位是置位还是复位)
决定对标志位的操作,有两种选项。
OS_OPT_POST_FLAG_SET 对标志位进行置位操作
OS_OPT_POST_FLAG_CLR 对标志位进行清零操作
- 应用实例
while (DEF_TRUE) { //任务体
if( Key_ReadStatus ( macKEY1_GPIO_PORT, macKEY1_GPIO_PIN, 1 ) == 1 ) //如果KEY1被按下
{ //点亮LED1
printf("KEY1被按下\n");
OSFlagPost ((OS_FLAG_GRP *)&flag_grp, //将标志组的BIT0置1
(OS_FLAGS )KEY1_EVENT,
(OS_OPT )OS_OPT_POST_FLAG_SET,
(OS_ERR *)&err);
}
if( Key_ReadStatus ( macKEY2_GPIO_PORT, macKEY2_GPIO_PIN, 1 ) == 1 ) //如果KEY2被按下
{ //点亮LED2
printf("KEY2被按下\n");
OSFlagPost ((OS_FLAG_GRP *)&flag_grp, //将标志组的BIT1置1
(OS_FLAGS )KEY2_EVENT,
(OS_OPT )OS_OPT_POST_FLAG_SET,
(OS_ERR *)&err);
}
OSTimeDlyHMSM ( 0, 0, 0, 20, OS_OPT_TIME_DLY, & err ); //每20ms扫描一次
}
等待事件标志组->OSFlagPend()
既然标记了事件的发生,那么我们怎么知道他到底有没有发生,这也是需要一个函数来获取事件是否已经发生,uCOS提供了一个等待指定事件的函数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) //返回错误类型
选定要操作的标志位: 这个和发布事件对应的标志位一样就行
等待期限: 指定等待事件标志组的超时时间(时钟节拍数),如果在指定的超时时间内所等待的一个或多个事件没有发生,那么任务恢复运行。如果此值设置为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。
消耗掉该事件标志位,即清零
- 应用实例
/*
*********************************************************************************************************
* PEND TASK
*********************************************************************************************************
*/
static void AppTaskPend ( void * p_arg )
{
OS_ERR err;
OS_FLAGS flags_rdy;
(void)p_arg;
while (DEF_TRUE) { //任务体
//等待标志组的的BIT0和BIT1均被置1
flags_rdy = OSFlagPend ((OS_FLAG_GRP *)&flag_grp,
(OS_FLAGS )( KEY1_EVENT | KEY2_EVENT ),
(OS_TICK )0,
(OS_OPT )OS_OPT_PEND_FLAG_SET_ALL |
OS_OPT_PEND_BLOCKING |
OS_OPT_PEND_FLAG_CONSUME,
(CPU_TS *)0,
(OS_ERR *)&err);
if((flags_rdy & (KEY1_EVENT|KEY2_EVENT)) == (KEY1_EVENT|KEY2_EVENT))
{
/* 如果接收完成并且正确 */
printf ( "KEY1与KEY2都按下\n");
macLED1_TOGGLE(); //LED1 反转
}
}
}
删除事件标志组->OSFlagDel()
想要使用删除事件函数则必须将OS_CFG_FLAG_DEL_EN 宏定义配置为1
- 函数原型
#if OS_CFG_FLAG_DEL_EN > 0u //如果使能了 OSFlagDel() 函数
OS_OBJ_QTY OSFlagDel (OS_FLAG_GRP *p_grp, //事件指针
OS_OPT opt, //选项
OS_ERR *p_err) //返回错误类型
opt选项:
OS_OPT_DEL_NO_PEND //如果只在没任务等待时进行删除
OS_OPT_DEL_ALWAYS //如果必须删除事件