参考教程:【正点原子】手把手教你学UCOS-III实时操作系统_哔哩哔哩_bilibili
一、事件标志组简介
1、概述
(1)事件标志位是一个“位”,用来表示事件是否发生。
(2)事件标志组是一组事件标志位的集合,可以简单的理解事件标志组,是一个整数。
(3)事件标志组的特点:
①每一个位与一个事件相关联,高8位除外,高8位用作存储事件标志组的控制信息。(下图所示的是32 位长度的事件标志组)
②每一位事件的含义,以及高电平和低电平分别代表什么,由用户自己决定。
③任意任务或中断都可以写这些位,但读这些位只能由任务来读。
④可以等待某一位成立,或者等待多位同时成立。
⑤支持读取阻塞。
2、事件标志组相关API函数介绍
(1)事件标志组相关API函数概览:
函数 | 描述 |
OSFlagCreate | 创建一个事件标志组 |
OSFlagDel | 删除一个事件标志组 |
OSFlagPend | 等待事件标志组中的事件 |
OSFlagPendAbort | 终止挂起等待事件标志组中的事件 |
OSFlagPendGetFlagRdy | 获取任务等待到的事件 |
OSFlagPost | 设置事件标志组中的事件 |
(2)OSFlagCreate函数:
void OSFlagCreate
(
OS_FLAG_GRP* p_grp,
CPU_CHAR* p_name,
OS_FLAGS flags,
OS_ERR* p_err
)
形参 | 描述 |
p_grp | 指向事件标志组结构体的指针 |
p_name | 指向作为事件标志组名的 ASCII 字符串的指针 |
flags | 事件标志组的初始值 |
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 | 设置事件指定位清0或置1 |
opt | 函数操作选项:OS_OPT_POST_FLAG_SET 或 OS_OPT_POST_FLAG_CLR |
p_err | 指向接收错误代码变量的指针 |
(4)OSFlagPend函数:
OS_FLAGS OSFlagPend //返回任务实际等待到的事件标志,如无任何标志准备就绪则为0
(
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 | 等待的事件标志 |
timeout | 任务挂起等待事件标志的最大允许时间 |
opt | 函数操作选项 |
p_ts | 指向接收等待到事件时的时间戳的变量的指针 |
p_err | 指向接收错误代码变量的指针 |
其中可选的函数操作选项如下所示:
opt | 描述 |
OS_OPT_PEND_FLAG_CLR_ALL | 等待“flags”中的所有指定位被清0 |
OS_OPT_PEND_FLAG_CLR_ANY | 等待“flags”中的任意指定位被清0 |
OS_OPT_PEND_FLAG_SET_ALL | 等待“flags”中的所有指定位被置1 |
OS_OPT_PEND_FLAG_SET_ANY | 等待“flags”中的任意指定位被置1 |
调用上面四个选项的时候还可以搭配下面三个选项 | |
OS_OPT_PEND_FLAG_CONSUME | 当等待到指定位后,清0对应位 |
OS_OPT_PEND_BLOCKING | 标志组不满足条件时挂起任务 |
OS_OPT_PEND_NON_BLOCKING | 标志组不满足条件时不挂起任务 |
二、事件标志组实验
1、原理图与实验目标
(1)原理图(按键未画出,接法与任务信号量实验相同):
(2)①设计3个任务——start_task、task1、task2:
[1]start_task:用于创建其它三个任务,并创建事件标志组。
[2]task1:读取按键按下键值,根据不同键值将事件标志组相应事件位置1,模拟事件发生(按下某个按键,对应的标志位置1)。
[3]task2:同时等待事件标志组中的多个事件位,当这些事件位都置1的话就执行相应的处理(串口打印信息),同时清除标志位。
②预期实验现象:
[1]程序下载到板子上后,暂时没有任何现象。
[2]按下相关按键,串口会输出相应的信息。
2、实验步骤
(1)将“任务队列实验”的工程文件夹复制一份,在拷贝版中进行实验。
(2)更改UCOS_experiment.c文件的内容,如下所示。
#include "stm32f10x.h" // Device header
#include "os.h"
#include "cpu.h"
#include "Key.h"
#include "Serial.h"
#include <stdio.h>
/* START_TASK 任务 配置
* 包括: 任务优先级 任务栈大小 任务控制块 任务栈 任务函数
*/
#define START_TASK_PRIO 1
#define START_TASK_STACK_SIZE 256
CPU_STK start_task_stack[START_TASK_STACK_SIZE];
OS_TCB start_task_tcb;
void start_task(void);
/* TASK1 任务 配置
* 包括: 任务优先级 任务栈大小 任务控制块 任务栈 任务函数
*/
#define TASK1_PRIO 4
#define TASK1_STACK_SIZE 256
CPU_STK task1_stack[TASK1_STACK_SIZE];
OS_TCB task1_tcb;
void task1(void);
/* TASK2 任务 配置
* 包括: 任务优先级 任务栈大小 任务控制块 任务栈 任务函数
*/
#define TASK2_PRIO 3
#define TASK2_STACK_SIZE 256
CPU_STK task2_stack[TASK2_STACK_SIZE];
OS_TCB task2_tcb;
void task2(void);
OS_FLAG_GRP flag;
#define FLAG_BIT0 (1 << 0) //flag事件标志组bit0位置1时使用
#define FLAG_BIT1 (1 << 1) //flag事件标志组bit1位置1时使用
void UCOS_Test(void)
{
OS_ERR err;
OSInit(&err); //初始化μC/OS-III
//创建Start Task
OSTaskCreate (&start_task_tcb,
"start_task",
(OS_TASK_PTR)start_task,
NULL,
START_TASK_PRIO,
start_task_stack,
START_TASK_STACK_SIZE / 10,
START_TASK_STACK_SIZE,
0,
0,
0,
(OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR),
&err);
/* 开始任务调度 */
OSStart(&err);
}
void start_task(void)
{
OS_ERR err; //接收错误代码使用,对错误代码进行分情况处理可增强程序鲁棒性
CPU_Init(); //初始化CPU库
CPU_SR_ALLOC();
OSFlagCreate (&flag, "flag", 0, &err); //创建一个事件标志组
//滴答定时器重装载值 = 系统主频 / 滴答定时器中断频率(滴答定时器是递减计数)
CPU_INT32U cnts = SystemCoreClock / OS_CFG_TICK_RATE_HZ;
OS_CPU_SysTickInit(cnts); //配置Systick中断及优先级
CPU_CRITICAL_ENTER(); //进入临界区(关中断)
//创建task1
OSTaskCreate (&task1_tcb,
"task1",
(OS_TASK_PTR)task1,
0,
TASK1_PRIO,
task1_stack,
TASK1_STACK_SIZE / 10,
TASK1_STACK_SIZE,
0,
0,
0,
(OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR),
&err);
//创建task2
OSTaskCreate (&task2_tcb,
"task2",
(OS_TASK_PTR)task2,
0,
TASK2_PRIO,
task2_stack,
TASK2_STACK_SIZE / 10,
TASK2_STACK_SIZE,
0,
0,
0,
(OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR),
&err);
CPU_CRITICAL_EXIT(); //退出临界区(开中断)
OSTaskDel(NULL, &err); //删除任务自身
}
void task1(void)
{
OS_ERR err;
uint8_t key = 0;
while(1)
{
key = Key_GetNum();
if(key == 1)
{
Serial_Printf("KEY1按下,bit0置1!!\r\n");
OSFlagPost(&flag, FLAG_BIT0, OS_OPT_POST_FLAG_SET, &err);
}
else if(key == 2)
{
Serial_Printf("KEY2按下,bit1置1!!\r\n");
OSFlagPost(&flag, FLAG_BIT1, OS_OPT_POST_FLAG_SET, &err);
}
OSTimeDly(10,OS_OPT_TIME_DLY,&err);
}
}
void task2(void)
{
OS_ERR err;
while(1)
{
OSFlagPend(&flag, //事件标志组地址
FLAG_BIT0 | FLAG_BIT1, //等待事件标志组的bit0和bit1位均置1
0,
OS_OPT_PEND_FLAG_SET_ALL | OS_OPT_PEND_FLAG_CONSUME | OS_OPT_PEND_BLOCKING,
//等待“flags”中的所有指定位被置1
//等待到事件标志位后,清除事件标志组的bit0和bit1位
//标志组不满足条件时挂起任务
NULL,
&err);
Serial_Printf("等待到指定事件成立!!!\r\n");
}
}
(3)程序完善好后点击“编译”,然后将程序下载到开发板上,打开串口助手分析信息。