事件标志组的原理和创建

事件标志组主要是用于中断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中我们同样也只需要取出等待队列并调度即可