uC/OS-II中的事件标志组(或称信号量集)

2015-08-04, 星期二, 00:00

uC/OS-II 中的事件标志组相当于一群互斥型信号量,通过这些信号量占用情况的逻辑函数求值确定任务的执行与挂起。事件标志组在一些中文参考资料中又称信号量集,以下采用前一种名称。本文使用的是 uC/OS-II V2.91 的源码。

OS_FLAG_GRP

与事件 OS_EVENT 不同的是,uC/OS 使用另一种数据结构 OS_FLAG_GRP 来表示事件标志组。

typedef struct os_flag_grp {
    INT8U      OSFlagType;      //表示数据类型的标识,一定为 OS_EVENT_TYPE_FLAG
    void      *OSFlagWaitList;  //指向等待任务列表
    OS_FLAGS   OSFlagFlags;     //8, 16 or 32 bit flags
#if OS_FLAG_NAME_EN > 0u
    INT8U      *OSFlagName;
#endif
} OS_FLAG_GRP;

结构体中的 OSFlagFlags 成员可以是一个 8 位、16 位、32 位的变量,变量的每个位代表一个事件标志(又称信号量,对应于信号量集),变量的大小根据 os_cfg.h 中的 OS_FLAGS_NBITS 条件编译而得

#if OS_FLAGS_NBITS == 8u
typedef  INT8U    OS_FLAGS;
#endif
#if OS_FLAGS_NBITS == 16u
typedef  INT16U   OS_FLAGS;
#endif
#if OS_FLAGS_NBITS == 32u
typedef  INT32U   OS_FLAGS;
#endif

OSFlagWaitList 指向等待任务列表的首地址,等待任务列表是一个双向链表,节点名为 OS_FLAG_NODE ,每个节点对应一个等待任务。

typedef struct os_flag_node {
    void      *OSFlagNodeNext;    //指向等待链表中的后一个节点
    void      *OSFlagNodePrev;    //指向等待链表中的前一个节点
    void      *OSFlagNodeTCB;     //指向本节点任务的任务控制块 TCB
    void      *OSFlagNodeFlagGrp; //反向指向事件标志组
    OS_FLAGS   OSFlagNodeFlags;   //获取事件标志的掩码(对应位为 1 的时候,信号对当前任务有影响)
    INT8U      OSFlagNodeWaitType;//逻辑函数(下文提及)
} OS_FLAG_NODE;

对等待任务列表的操作 OS_FlagBlock()OS_FlagInit()OS_FlagTaskRdy()OS_FlagUnlink() 作为底层函数被事件标志组函数封装。

OSFlagCreate()

使用事件标志组之前需要调用 OSFlagCreate()函数从空标志组链表中取出一个元素。

OS_FLAG_GRP *OSFlagCreate(OS_FLAGS flags, INT8U *perr)

flags 原样赋值给 OSFlagFlags,由于后者是无符号整型数,以防万一传递参数时可用强制类型转换。这里是要给所有事件标志初始化,赋值 0 即可。

perr 照旧是存储输出信息的地方。

OS_ERR_CREATE_ISR           //无法在中断服务函数中创建事件标志组
OS_ERR_FLAG_GRP_DEPLETED    //空的事件标志组节点已经用完了

这两种情况都会返回空指针,OS_ERR_NONE 表示创建成功,返回事件标志组指针。

OSFlagDel()

删除事件标志组的方式与其他事件相同。

OS_FLAG_GRP *OSFlagDel(OS_FLAG_GRP *pgrp, INT8U opt, INT8U *perr)

perropt 行为一致,删除成功后返回空指针

OSFlagPend() & OSFlagAccept()

任务请求一个事件标志组事件的某些标志位,在触发或超时后继续执行。

OS_FLAGS OSFlagPend(OS_FLAG_GRP *pgrp,
                    OS_FLAGS     flags,
                    INT8U        wait_type,
                    INT32U timeout, INT8U *perr)

flags 是即将写入等待任务列表节点的掩码 OSFlagNodeFlagswait_type 是触发条件设置,写入等待任务列表节点的 OSFlagNodeWaitType

#define  OS_FLAG_WAIT_CLR_ALL  0u   //等待所有有效位清零
#define  OS_FLAG_WAIT_CLR_AND  0u
#define  OS_FLAG_WAIT_CLR_ANY  1u   //等待任意有效位清零
#define  OS_FLAG_WAIT_CLR_OR   1u
#define  OS_FLAG_WAIT_SET_ALL  2u   //等待任意有效位置位
#define  OS_FLAG_WAIT_SET_AND  2u
#define  OS_FLAG_WAIT_SET_ANY  3u   //等待所有有效位置位
#define  OS_FLAG_WAIT_SET_OR   3u

需要不挂起请求的,使用 OSFlagAccept(),用法基本相同

OS_FLAGS  OSFlagAccept (OS_FLAG_GRP  *pgrp,
                        OS_FLAGS      flags,
                        INT8U         wait_type,
                        INT8U        *perr)

需要注意的是,如果对应的事件标志没有发生,函数返回值为 0;如果对应事件标志发生,函数返回事件标志组结构中的 OSFlagFlags 成员,即当前事件标志组的所有事件标志。因此在判断时还需参考 perr 的值。

OSFlagPost()

用于发送事件标志。

OS_FLAGS OSFlagPost(OS_FLAG_GRP *pgrp,
                    OS_FLAGS     flags,
                    INT8U        opt,
                    INT8U       *perr)

flags 仍是用于掩码,判断哪些位被操作;opt 则描述对有效位的操作。

switch (opt) {
    case OS_FLAG_CLR:       //将有效位清零
         pgrp->OSFlagFlags &= (OS_FLAGS)~flags;break;
    case OS_FLAG_SET:       //将有效位置位
         pgrp->OSFlagFlags |=  flags;break;
    default:                //除此之外的其他值将返回出错信息
         OS_EXIT_CRITICAL();
         *perr = OS_ERR_FLAG_INVALID_OPT;
         return ((OS_FLAGS)0);
}

OSFlagQuery()

OSFlagQuery() 可以直接返回事件标志组结构中的 OSFlagFlags 成员。

OS_FLAGS OSFlagQuery(OS_FLAG_GRP *pgrp, INT8U *perr) 与其他 OS_EVENT 不同的是,访问事件标志组不会对内部的事件标志造成影响。一旦有任务就绪,等待任务列表里只会移除对应任务的节点。