09. µCOS-Ⅲ的事件标志组

一、µC/OS-Ⅲ的事件标志简介

1.1、事件标志简介

  事件标志 是一个用于指示事件是否发生的比特位,因为一个事件是否发生只有两种情况,分别为事件发生和事件未发生,因此只需一个比特位就能够表示事件是否发生,µC/OS-Ⅲ 用 1 表示事件发生,用 0 表示事件未发生。

  事件标志组是多个事件的集合,事件标志组的结构体定义在文件 os.h 中,具体的代码如下所示:

struct  os_flag_grp 
{
#if (OS_OBJ_TYPE_REQ > 0u)                                  // 此宏用于使能检查内核对象的类型
    // 对象类型,在信号量初始化时,信号量的类型会被初始化为OS_OBJ_TYPE_FLAG
    OS_OBJ_TYPE          Type;
#endif
#if (OS_CFG_DBG_EN > 0u)                                    // 此宏用于使能代码调试功能
    // 如果使能了代码调试功能,那么就会为每一个信号量赋一个名称,方便调试
    CPU_CHAR            *NamePtr;
#endif
    // 挂起等待任务链表,当任务获取的事件还未发生时,当任务获取的事件还未发生时,等待事件发生
    OS_PEND_LIST         PendList;
#if (OS_CFG_DBG_EN > 0u)                                    // 此宏用于使能代码调试功能
    // 一些与代码调试相关的成员变量
    OS_FLAG_GRP         *DbgPrevPtr;
    OS_FLAG_GRP         *DbgNextPtr;
    CPU_CHAR            *DbgNamePtr;
#endif
    // 事件表示的集合,对于32位MCU,这一成员变量是一个32位的无符号数,一比特位表示一个事件标志,因此一个事件标志组最多容纳32个事件标志
    OS_FLAGS             Flags;

// 以下的成员变量用于第三方的调试工具
#if (OS_CFG_TS_EN > 0u)
    CPU_TS               TS;
#endif
#if (defined(OS_CFG_TRACE_EN) && (OS_CFG_TRACE_EN > 0u))
    CPU_ADDR             FlagID;
#endif
};

  从上面的代码中可以看到,事件标志组的结构体中包含了一个数据类型为 OS_FLAGS 的成员变量 Flags,对于 32 位的 MCU 而言,数据类型 OS_FLAGS 实际上就是 32 位的无符号整型,因此成员变量 Flags 就包含了 32 比特,而 Flags 的每一个比特就能够用来存储一个事件标志。当成员变量 Flags 的某一比特位被置一时,就说明这一比特位对应的事件发生了。

它的每一个位表示一个事件(最多可以表示 32 个事件标志)。

每一位事件的含义,由用户自己决定,这些位的值为 1:表示事件发生了;值为 0:表示事件未发生。

任意任务或中断都可以写这些位,但读这些位只能由任务。

可以等待某一位成立,或者等待多位同时成立。

1.2、事件标志操作简介

  事件标志组 由多个事件标志组成,因此任务就能够很方便地通过多个事件的发生进行任务间的同步。通过事件标志,任务还能够通过多个事件之间的逻辑关系(与、或关系)进行任务间的同步,如下图所示:

事件标志操作示意图

可以等待某一位成立,或者等待多位同时成立。

任意任务或中断都可以写这些位,但读这些位只能由任务。

支持读取阻塞。

二、事件标志相关API函数

2.1、创建一个事件标志组

void  OSFlagCreate (OS_FLAG_GRP  *p_grp,        // 指向事件标志组结构体的指针
                    CPU_CHAR     *p_name,       // 指向作为事件标志组名的 ASCII 字符串的指针
                    OS_FLAGS      flags,        // 事件标志组的初始值
                    OS_ERR       *p_err);       // 指向接收错误代码变量的指针

  函数 OSFlagCreate()的错误代码描述,如下所示:

OS_ERR_NONE                         // 事件标志组创建成功
OS_ERR_CREATE_ISR                   // 在中断中非法调用该函数
OS_ERR_ILLEGAL_CREATE_RUN_TIME      // 在系统运行过程中非法创建内核对象
OS_ERR_OBJ_PTR_NULL                 // 指向事件标志组结构体的指针为空

2.2、删除一个事件标志组

// 返回值:被终止挂起等待事件标志任务的数量
OS_OBJ_QTY  OSFlagDel (OS_FLAG_GRP  *p_grp,         // 指向事件标志组结构体的指针
                       OS_OPT        opt,           // 函数操作选项
                       OS_ERR       *p_err);        // 指向接收错误代码变量的指针

  函数 OSFlagDel() 的错误代码描述,如下所示:

OS_ERR_NONE                     // 事件标志组删除成功
OS_ERR_DEL_ISR                  // 在中断中非法调用该函数
OS_ERR_ILLEGAL_DEL_RUN_TIME     // 在系统运行过程中非法删除内核对象
OS_ERR_OBJ_PTR_NULL             // 指向事件标志组结构体的指针为空
OS_ERR_OBJ_TYPE                 // 待被删除内核对象的类型不是事件标志组
OS_ERR_OPT_INVALID              // 无效的任务操作选项
OS_ERR_OS_NOT_RUNING            // µC/OS-Ⅲ 内核还未运行
OS_ERR_TASK_WAITING             // 有任务挂起等待待被删除的事件标志组

2.3、等待事件标志组中的事件

// 返回值:任务实际等待到的事件标志
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);         // 指向接收错误代码变量的指针

  函数 OSFlagPend() 的函数操作选项描述,如下所示:

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            // 标志组不满足条件时不挂起任务

  函数 OSFlagPend() 的错误代码描述,如下所示:

OS_ERR_NONE                         // 没有错误
OS_ERR_OBJ_DEL                      // 指定的事件标志组已经被删除
OS_ERR_OBJ_PTR_NULL                 // 指向事件标志组结构体的指针为空
OS_ERR_OBJ_TYPE                     // 操作的内核对象的类型不是事件标志组
OS_ERR_OPT_INVALID                  // 无效的函数操作选项
OS_ERR_OS_NOT_RUNING                // μC/OS-Ⅲ 内核还未运行
OS_ERR_PEND_ABORT                   // 任务挂起等待事件标志组时被终止(还未超时)
OS_ERR_PEND_ISR                     // 在中断中非法调用该函数
OS_ERR_PEND_WOULD_BLOCK             // 等待事件失败,并且不挂起任务等待事件
OS_ERR_SCHED_LOCKED                 // 任务调度器被锁定
OS_ERR_STATUS_INVALID               // 无效的任务挂起结果
OS_ERR_TIMEOUT                      // 任务挂起等待事件标志组超时

2.4、获取任务等待到的事件

// 返回值:任务获取到的事件标志
OS_FLAGS  OSFlagPendGetFlagsRdy (OS_ERR  *p_err);       // p_err 指向接收错误代码变量的指针

  函数 OSFlagPendGetFlagsRdy() 的错误代码描述,如下所示:

OS_ERR_NONE             // 获取任务获取到的事件标志成功
OS_ERR_OS_NOT_RUNING    // µC/OS-Ⅲ 内核还未运行
OS_ERR_PEND_ISR         // 在中断中非法调用该函数

2.5、设置事件标志组中的事件

// 返回值:事件标志组更新后的事件标志值
OS_FLAGS  OSFlagPost (OS_FLAG_GRP  *p_grp,          // 指向事件标志组结构体的指针
                      OS_FLAGS      flags,          // 等待的事件标志
                      OS_OPT        opt,            // 函数操作选项
                      OS_ERR       *p_err);         // 指向接收错误代码变量的指针

  函数 OSFlagPost() 的函数操作选项描述,如下所示:

OS_OPT_POST_FLAG_SET
OS_OPT_POST_FLAG_CLR

  函数 OSFlagPost() 的错误代码描述,如下所示:

OS_ERR_NONE                 // 设置事件标志成功
OS_ERR_OBJ_PTR_NULL         // 指向事件标志组结构体的指针为空
OS_ERR_OBJ_TYPE             // 操作的内核对象的类型不是事件标志组
OS_ERR_OPT_INVALID          // 无效的函数操作选项
OS_ERR_OS_NOT_RUNING        // μC/OS-Ⅲ 内核还未运行

2.6、终止挂起等待事件标志组中的事件

// 返回值:被终止挂起任务的数量
OS_OBJ_QTY  OSFlagPendAbort (OS_FLAG_GRP  *p_grp,           // 指向事件标志组结构体的指针
                             OS_OPT        opt,             // 函数操作选项
                             OS_ERR       *p_err);          // 指向接收错误代码变量的指针

  函数 OSFlagPendAbort() 的错误代码描述,如下所示:

OS_ERR_NONE                 // 终止任务挂起等待事件标志组成功
OS_ERR_OBJ_PTR_NULL         // 指向事件标志组结构体的指针为空
OS_ERR_OBJ_TYPE             // 操作的内核对象的类型不是事件标志组
OS_ERR_OBJ_TYPE             // 无效的函数操作选项
OS_ERR_OS_NOT_RUNING        // μC/OS-Ⅲ 内核还未运行
OS_ERR_PEND_ABORT_ISR       // 在中断中非法调用该函数
OS_ERR_PEND_ABORT_NONE      // 没有任务挂起等待事件标志组

三、实验例程

  main() 函数内容如下:

int main(void)
{
    HAL_Init();
    System_Clock_Init(8, 336, 2, 7);
    Delay_Init(168);

    UART_Init(&g_usart1_handle, USART1, 115200);

    Key_Init();
  
    UC_OS3_Demo();
  
    return 0;
}

  µC/OS-Ⅲ 例程入口函数:

/**
 * @brief µC/OS-Ⅲ例程入口函数
 * 
 */
void UC_OS3_Demo(void)
{
    OS_ERR error = {0};

    OSInit(&error);                                                             // 初始化µC/OS-Ⅲ

    // 创建开始任务
    OSTaskCreate((OS_TCB *   )  &start_task_tcb,                                // 任务控制块
                (CPU_CHAR *  )  "start_task",                                   // 任务名
                (OS_TASK_PTR )  Start_Task,                                     // 任务函数
                (void *      )  0,                                              // 任务参数
                (OS_PRIO     )  START_TASK_PRIORITY,                            // 任务优先级
                (CPU_STK *   )  start_task_stack,                               // 任务堆栈
                (CPU_STK_SIZE)  START_TASK_STACK_SIZE / 10,                     // 任务栈的使用警戒线
                (CPU_STK_SIZE)  START_TASK_STACK_SIZE,                          // 任务栈大小
                (OS_MSG_QTY  )  0,                                              // 消息队列长度
                (OS_TICK     )  0,                                              // 时间片长度
                (void *      )  0,                                              // 扩展内存
                (OS_OPT      )  (OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR),    // 任务选项
                (OS_ERR *    )  &error);                                        // 错误码

    OSStart(&error);                                                            // 开始任务调度
}

  START_TASK 任务配置:

OS_FLAG_GRP flag;                                                               // 事件标志组

/**
 * START_TASK 任务配置
 * 包括:任务优先级 任务栈大小 任务控制块 任务栈 任务函数
 */
#define START_TASK_PRIORITY     1
#define START_TASK_STACK_SIZE   256

OS_TCB  start_task_tcb;
CPU_STK start_task_stack[START_TASK_STACK_SIZE];

void Start_Task(void *p_arg);

/**
 * @brief 开始任务的任务函数
 * 
 * @param p_arg 任务参数
 */
void Start_Task(void *p_arg)
{
    OS_ERR error = {0};
    CPU_INT32U cnts = 0;

    CPU_Init();                                                                 // 初始化CPU库

    CPU_SR_ALLOC();                                                             // 临界区保护

    cnts = HAL_RCC_GetSysClockFreq() / OS_CFG_TICK_RATE_HZ;
    OS_CPU_SysTickInit(cnts);                                                   // 根据配置的节拍频率配置SysTick中断及优先级

    OSFlagCreate(&flag, "flag", 0, &error);                                     // 创建一个事件标志组

    CPU_CRITICAL_ENTER();                                                       // 进入临界区

    // 创建任务1
    OSTaskCreate((OS_TCB *   )  &task1_tcb,                                     // 任务控制块
                (CPU_CHAR *  )  "task1",                                        // 任务名
                (OS_TASK_PTR )  Task1,                                          // 任务函数
                (void *      )  0,                                              // 任务参数
                (OS_PRIO     )  TASK1_PRIORITY,                                 // 任务优先级
                (CPU_STK *   )  task1_stack,                                    // 任务堆栈
                (CPU_STK_SIZE)  TASK1_STACK_SIZE / 10,                          // 任务栈的使用警戒线
                (CPU_STK_SIZE)  TASK1_STACK_SIZE,                               // 任务栈大小
                (OS_MSG_QTY  )  0,                                              // 消息队列长度
                (OS_TICK     )  0,                                              // 时间片长度,设置为0,则默认时间片长度
                (void *      )  0,                                              // 扩展内存
                (OS_OPT      )  (OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR),    // 任务选项
                (OS_ERR *    )  &error);                                        // 错误码

    // 创建任务2
    OSTaskCreate((OS_TCB *   )  &task2_tcb,                                     // 任务控制块
                (CPU_CHAR *  )  "task2",                                        // 任务名
                (OS_TASK_PTR )  Task2,                                          // 任务函数
                (void *      )  0,                                              // 任务参数
                (OS_PRIO     )  TASK2_PRIORITY,                                 // 任务优先级
                (CPU_STK *   )  task2_stack,                                    // 任务堆栈
                (CPU_STK_SIZE)  TASK2_STACK_SIZE / 10,                          // 任务栈的使用警戒线
                (CPU_STK_SIZE)  TASK2_STACK_SIZE,                               // 任务栈大小
                (OS_MSG_QTY  )  0,                                              // 消息队列长度
                (OS_TICK     )  0,                                              // 时间片长度
                (void *      )  0,                                              // 扩展内存
                (OS_OPT      )  (OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR),    // 任务选项
                (OS_ERR *    )  &error);                                        // 错误码

    CPU_CRITICAL_EXIT();                                                        // 退出临界区

    OSTaskDel(NULL, &error);                                                    // 删除任务
}

  TASK1 任务配置:

#define FLAG_BIT1           (1 << 1)
#define FLAG_BIT2           (1 << 2)
#define FLAG_BIT3           (1 << 3)

/**
 * TASK1 任务配置
 * 包括:任务优先级 任务栈大小 任务控制块 任务栈 任务函数
 */
#define TASK1_PRIORITY          2
#define TASK1_STACK_SIZE        256

OS_TCB  task1_tcb;
CPU_STK task1_stack[TASK1_STACK_SIZE];

void Task1(void *p_arg);

/**
 * @brief 任务1的任务函数
 * 
 * @param p_arg 任务参数
 */
void Task1(void *p_arg)
{
    OS_ERR error = {0};

    while (1)
    {
        switch (Key_Scan(0))
        {
        case KEY1_PRESS:
            OSFlagPost(&flag, FLAG_BIT1, OS_OPT_POST_FLAG_SET, &error);
            printf("事件1置位\r\n");
            break;
  
        case KEY2_PRESS:
            OSFlagPost(&flag, FLAG_BIT2, OS_OPT_POST_FLAG_SET, &error);
            printf("事件2置位\r\n");
            break;

        case KEY3_PRESS:
            OSFlagPost(&flag, FLAG_BIT3, OS_OPT_POST_FLAG_SET, &error);
            printf("事件3置位\r\n");
            break;

        default:
            break;
        }

        OSTimeDly(10, OS_OPT_TIME_DLY, &error);
    }
}

  TASK2 任务配置:

/**
 * TASK2 任务配置
 * 包括:任务优先级 任务栈大小 任务控制块 任务栈 任务函数
 */
#define TASK2_PRIORITY          2
#define TASK2_STACK_SIZE        256

OS_TCB  task2_tcb;
CPU_STK task2_stack[TASK2_STACK_SIZE];

void Task2(void *p_arg);

/**
 * @brief 任务2的任务函数
 * 
 * @param p_arg 任务参数
 */
void Task2(void *p_arg)
{
    OS_ERR error = {0};
  
    while (1)
    {
        OSFlagPend(&flag, FLAG_BIT1 | FLAG_BIT2 | FLAG_BIT3, 0, OS_OPT_PEND_FLAG_SET_ALL | OS_OPT_PEND_FLAG_CONSUME, 0, &error);
        printf("等到到指定事件成立\r\n");
    }
}
posted @ 2024-02-22 20:01  星光映梦  阅读(203)  评论(0)    收藏  举报