事件标志组
事件标志组的原理和创建


事件标志组主要是用于中断ISR和任务之间传递多个标志,当然这些标志可以有很多操作,例如基础的与或非,然后触发相应的任务
typedef struct _tFlagGroup
{
// 事件控制块
tEvent event;
// 当前事件标志
uint32_t flags;
}tFlagGroup;
void tFlagGroupInit (tFlagGroup * flagGroup, uint32_t flags)
{
tEventInit(&flagGroup->event, tEventTypeFlagGroup);
flagGroup->flags = flags;
}
新增事件控制块结构体,如上图,里面有一个事件控制块和事件标志—32位的
初始化也只初始化这两个部分,一个是事件标志,一个是事件控制块
事件标志组的等待和通知




对于等待来说,任务去比较事件标志,即任务是因,如果匹配就清除继续执行,否则就插入等待队列
对于通知来说,由于事件标志我们可能会对它进行操作,所以会有通知操作,逻辑就是遍历等待队列,找出符合的任务,取出开启调度
#define TFLAGGROUP_CLEAR (0x0 << 0)
#define TFLAGGROUP_SET (0x1 << 0)
#define TFLAGGROUP_ANY (0x0 << 1)
#define TFLAGGROUP_ALL (0x1 << 1)
#define TFLAGGROUP_SET_ALL (TFLAGGROUP_SET | TFLAGGROUP_ALL)
#define TFLAGGROUP_SET_ANY (TFLAGGROUP_SET | TFLAGGROUP_ANY)
#define TFLAGGROUP_CLEAR_ALL (TFLAGGROUP_CLEAR | TFLAGGROUP_ALL)
#define TFLAGGROUP_CLEAR_ANY (TFLAGGROUP_CLEAR | TFLAGGROUP_ANY)
static uint32_t tFlagGroupCheckAndConsume (tFlagGroup * flagGroup, uint32_t type, uint32_t * flags)
{
uint32_t srcFlags = *flags;
uint32_t isSet = type & TFLAGGROUP_SET;
uint32_t isAll = type & TFLAGGROUP_ALL;
uint32_t isConsume = type & TFLAGGROUP_CONSUME;
// 有哪些类型的标志位出现
// flagGroup->flags & flags:计算出哪些位为1
// ~flagGroup->flags & flags:计算出哪位为0
uint32_t calcFlag = isSet ? (flagGroup->flags & srcFlags) : (~flagGroup->flags & srcFlags);
// 所有标志位出现, 或者做任意标志位出现,满足条件
if (((isAll != 0) && (calcFlag == srcFlags)) || ((isAll == 0) && (calcFlag != 0)))
{
// 是否消耗掉标志位
if (isConsume)
{
if (isSet)
{
// 清除为1的标志位,变成0
flagGroup->flags &= ~srcFlags;
}
else
{
// 清除为0的标志位,变成1
flagGroup->flags |= srcFlags;
}
}
*flags = calcFlag;
return tErrorNoError;
}
*flags = calcFlag;
return tErrorResourceUnavaliable;
}
在宏定义中,我们需要注意的是CLEAR和SET,ANY和ALL是一组的,所以不必在意CLEAR和ANY的值是一样的
tFlagGroupCheckAndConsume函数是一个中间过渡函数,主要是用于检查并消耗掉事件标志;大致逻辑是先是对标志的判断,例如是否消耗,是任意触发还是全部触发等,然后对其进行相应的操作,在操作的时候我们需要理解isSet,它会对我们的事件标志作出更改,也就是置0或置1,最后返回值,匹配返回tErrorNoError,否则返回tErrorResourceUnavaliable
uint32_t tFlagGroupWait (tFlagGroup * flagGroup, uint32_t waitType, uint32_t requestFlag,
uint32_t * resultFlag, uint32_t waitTicks)
{
uint32_t result;
uint32_t flags = requestFlag;
uint32_t status = tTaskEnterCritical();
result = tFlagGroupCheckAndConsume(flagGroup, waitType, &flags);
if (result != tErrorNoError)
{
// 如果事件标志不满足条件,则插入到等待队列中
currentTask->waitFlagsType = waitType;
currentTask->eventFlags = requestFlag;
tEventWait(&flagGroup->event, currentTask, (void *)0, tEventTypeFlagGroup, waitTicks);
tTaskExitCritical(status);
// 再执行一次事件调度,以便于切换到其它任务
tTaskSched();
*resultFlag = currentTask->eventFlags;
result = currentTask->waitEventResult;
}
else
{
*resultFlag = flags;
tTaskExitCritical(status);
}
return result;
}
uint32_t tFlagGroupNoWaitGet (tFlagGroup * flagGroup, uint32_t waitType, uint32_t requestFlag, uint32_t * resultFlag)
{
uint32_t flags = requestFlag;
uint32_t status = tTaskEnterCritical();
uint32_t result = tFlagGroupCheckAndConsume(flagGroup, waitType, &flags);
tTaskExitCritical(status);
*resultFlag = flags;
return status;
}
void tFlagGroupNotify (tFlagGroup * flagGroup, uint8_t isSet, uint32_t flags)
{
tList *waitList;
tNode * node;
tNode * nextNode;
uint8_t sched = 0;
uint32_t status = tTaskEnterCritical();
if (isSet) {
flagGroup->flags |= flags; // 置1事件
} else {
flagGroup->flags &= ~flags; // 清0事件
}
// 遍历所有的等待任务, 获取满足条件的任务,加入到待移除列表中
waitList = &flagGroup->event.waitList;
for (node = waitList->headNode.nextNode; node != &(waitList->headNode); node = nextNode) {
uint32_t result;
tTask *task = tNodeParent(node, tTask, linkNode);
uint32_t flags = task->eventFlags;
nextNode = node->nextNode;
// 检查标志
result = tFlagGroupCheckAndConsume(flagGroup, task->waitFlagsType, &flags);
if (result == tErrorNoError) {
// 唤醒任务
task->eventFlags = flags;
tEventWakeUpTask(&flagGroup->event, task, (void *)0, tErrorNoError);
sched = 1;
}
}
// 如果有任务就绪,则执行一次调度
if (sched)
{
tTaskSched();
}
tTaskExitCritical(status);
}
tFlagGroupWait中,首先是用tFlagGroupCheckAndConsume进行了判断,如果符合,那么就给requestFlag附上处理后的值—因为根据需求可能会作出清除处理,如果不符合,那么插入等待队列中,开启调度
tFlagGroupNoWaitGet是一个查询函数,也可以理解为不阻塞的requestFlag
tFlagGroupNotify中,首先是对我们的事件标志作出修改,也就是上文提到的置0或置1,然后就是遍历,找到相应的任务,取出并开启调度
这里需要优化的是遍历这部分,试试能不能找出更快的算法
事件标志组的删除和查询
typedef struct _tFlagGroupInfo
{
// 当前的事件标志
uint32_t flags;
// 当前等待的任务计数
uint32_t taskCount;
}tFlagGroupInfo;
void tFlagGroupGetInfo (tFlagGroup * flagGroup, tFlagGroupInfo * info)
{
uint32_t status = tTaskEnterCritical();
// 拷贝需要的信息
info->flags = flagGroup->flags;
info->taskCount = tEventWaitCount(&flagGroup->event);
tTaskExitCritical(status);
}
uint32_t tFlagGroupDestroy (tFlagGroup * flagGroup)
{
uint32_t status = tTaskEnterCritical();
// 清空事件控制块中的任务
uint32_t count = tEventRemoveAll(&flagGroup->event, (void *)0, tErrorDel);
tTaskExitCritical(status);
// 清空过程中可能有任务就绪,执行一次调度
if (count > 0)
{
tTaskSched();
}
return count;
}
新增一个tFlagGroupInfo查询状态的结构体,我们需要的信息就只有事件标志和等待任务数量
在tFlagGroupDestroy中我们同样也只需要取出等待队列并调度即可