STM32WB55-->任务调度器--> (1)调度器管理源码解析
STM32WB55任务调度器管理【初始化、运行】
【源代码附在最后,其中如有涉及到的其他函数实现过程,会在后面发布专门的篇章进行解析】
STM32WB55任务调度器,是通过定义不同的uint32类型变量,协同进行任务管理。
(1)这32bit,1个bit代表1个任务,因此可将每个变量当作列表去理解(这些列表并不是真正意义上完整的列表,只是当作列表在使用)
【后续源码讲解过程,这些变量也将按照列表的说法展开】
(2)该任务调度器具备优先级,每个优先级下最多有32个任务
第一层优先级是优先级任务列表,第二层是同优先级任务列表中不同任务的ID(即所占bit位置,位置越高,优先级越高)
(3)同优先级下的任务按照任务注册时给的ID号顺序执行,ID号越高代表该任务在同级优先级任务列表下的执行优先级越高
注:建议使用枚举类型参数给分配ID号,避免任务注册时被覆盖
一、初始化
/**
* @brief This function initializes the sequencer resources.
*
* @note It shall not be called from an ISR.
*
*/
void UTIL_SEQ_Init( void );
1.相关参数初始化
(1)任务激活状态【任务激活列表】初始化为0(当某个任务激活时,根据ID号将对应的bit置1)
eg.TasdSet初始状态为0,当激活一个ID号为1的任务时,TaskSet被修改为1
(2)任务运行状态【运行态任务列表】初始化为~0U,即32bit全部初始化为1【对应bit为1说明该任务为运行态,为0说明该任务被挂起】
(3)任务阻塞状态【阻塞态任务列表】初始化为~0U,即32bit全部初始化为1【对应bit为1说明该任务未阻塞,为0说明该任务被阻塞】(此处的阻塞并不一定是真的阻塞,当不想某个任务被调度器安排执行时也可将对应bit置1,调度器会忽略该任务)
(4)事件等待完成标志【事件等待完成列表】初始化为0(当某个任务获取到等待的事件时,该任务对应的bit位置1)【等待结束】
(5)事件等待标志【事件等待列表】初始化位0(当某个任务等待事件时,该任务对应的bit位置1)【等待过程】
(6)当前执行的任务ID初始化为0

2.将后期存储具体任务的函数指针数组TaskCb所有元素初始化为0
![]()
该函数指针数组原型声明如下

3.将当前所有的优先级任务列表初始化
(1)每个优先级任务列表包含的所有任务激活状态初始化为0,即未激活(TaskPrio数组下标数字越小,该任务列表所代表的优先级越高)
(2)每个优先级任务列表的所有任务运行状态初始化为0(0表示对应任务已运行,1表示对应任务未运行)

4.该函数接口未实现,无实际意义,可忽略
![]()
二、运行
/**
* @brief This function requests the sequencer to execute all pending tasks using round robin mechanism.
* When no task are pending, it calls UTIL_SEQ_Idle();
* This function should be called in a while loop in the application
*
* @param Mask_bm list of task (bit mapping) that is be kept in the sequencer list.
*
* @note It shall not be called from an ISR.
* @note The construction of the task must take into account the fact that there is no counting / protection
* on the activation of the task. Thus, when the task is running, it must perform all the operations
* in progress programmed before its call or manage a reprogramming of the task.
*
*/
void UTIL_SEQ_Run( UTIL_SEQ_bm_t Mask_bm );
1.备份阻塞任务列表信息,将传递的最新阻塞态任务列表Mask_bm和当前阻塞态任务列表SuperMask按位与,获取到最新需要执行的任务列表【具体某个任务是否执行,还取决于该任务的其它状态标记】

(1)任务在执行过程中,如果已经执行过的任务就绪,但当前优先级列表任务还未执行结束,那么已经就绪的任务不会执行,直至本轮任务全部执行结束,才会重头开始执行【不存在抢占式调度】。
(2)此处的按位与,在官方例程的框架中主要作用体现在任务调度器运行的嵌套,当任务运行过程中有一个任务需要等待事件时,任务调度器就会停在该位置死等,直到事件获取为止。同时开启新一轮的任务调度器:
1)继续执行后续任务,直至事件获取结束,结束当前新一轮调度器,回到上一次开启的调度器。
2)如果在新一轮调度器执行完成后,事件还未获取,那么此时的调度器依然会执行,但是屏蔽掉了等待事件的任务ID,即在阻塞列表中进行了标记,在该调度器执行过程中所有该ID位置所标识的任务均不会执行,这是当前调度器的一个缺陷。
2.根据任务激活列表(TaskSet)、运行态任务列表(TaskMask)、阻塞态任务列表(SuperMask),三者按位与,结合是否有事件需要等待(以及等待的状态),判断是否有任务需要运行

2.1有任务需要运行【有可执行的任务,且无等待事件(或等待事件还未完成)】
(1)清空计数值count,进一步确定当前需要执行的最高优先级任务列表,并记录该优先级任务列表位置count

(2)获取到当前需要执行的任务列表
![]()
(3)如果当前的优先级任务列表中任务均未执行,则将当前优先级任务列表所有任务的运行状态标记为未运行

(4)获取当前要执行的最新任务列表中,优先级最高的任务ID,即最大ID
![]()
注:最大ID获取方式为从高位向低位判断有多少个bit为0,即可得到当前任务列表中最高优先级任务ID,SEQ_BitPosition函数实现如下

(5)将当前优先级任务列表中准备执行的任务状态修改为已运行

(6)代码在临界段内,将准备执行的任务从激活任务列表TaskSet和所有优先级任务列表TaskPrio[counter].priority移除

注:TaskSet在对应任务执行完成后,相对应的bit就会置0,标识该任务未激活,此处对于整个调度器框架而言具有非常的意义:
1)当多个优先级任务列表同一个任务激活时【既一个任务在不同的优先级列表中激活】,只能执行最高优先级列表的任务,其余该位置任务被从相关任务列表移除
2)理论上每个优先级任务列表都能最多有32个任务,但后面篇章讲到任务注册时,就会发现该调度器框架真正能注册的任务数量就是32个,其余列表最终都是为了这个注册任务的函数指针数组所服务
(7)执行该任务所关联的函数
![]()
(8)修改相关任务列表(临时列表),并从2开始继续执行,直至无任务需要执行

3.当前运行任务ID置为默认,下面的函数体没有实现,可忽略

4.低功耗状态进入临界段,修改相关临时参数,无任务需要执行且无等待事件(或等待事件还未获取)则进入低功耗模式
【该程序框架低功耗进入临界段依然是普通方式进入临界段,进入低功耗模式函数未真正实现,可忽略】

5.阻塞态任务列表恢复当前调度器运行前的状态
![]()
至此,任务调度器的初始化和运行相关源码解析结束。
【如有理解有误地方,欢迎指正,勿喷】
【内容对你有帮助的话,点个关注吧~ 你的支持是我更新的动力!】
/* 调度器初始化 */
void UTIL_SEQ_Init( void )
{
TaskSet = UTIL_SEQ_NO_BIT_SET;
TaskMask = UTIL_SEQ_ALL_BIT_SET;
SuperMask = UTIL_SEQ_ALL_BIT_SET;
EvtSet = UTIL_SEQ_NO_BIT_SET;
EvtWaited = UTIL_SEQ_NO_BIT_SET;
CurrentTaskIdx = 0U;
(void)UTIL_SEQ_MEMSET8((uint8_t *)TaskCb, 0, sizeof(TaskCb));
for(uint32_t index = 0; index < UTIL_SEQ_CONF_PRIO_NBR; index++)
{
TaskPrio[index].priority = 0;
TaskPrio[index].round_robin = 0;
}
UTIL_SEQ_INIT_CRITICAL_SECTION( );
}
/* 调度器运行 */
void UTIL_SEQ_Run( UTIL_SEQ_bm_t Mask_bm )
{
uint32_t counter;
UTIL_SEQ_bm_t current_task_set;
UTIL_SEQ_bm_t super_mask_backup;
UTIL_SEQ_bm_t local_taskset;
UTIL_SEQ_bm_t local_evtset;
UTIL_SEQ_bm_t local_taskmask;
UTIL_SEQ_bm_t local_evtwaited;
/*
* When this function is nested, the mask to be applied cannot be larger than the first call
* The mask is always getting smaller and smaller
* A copy is made of the mask set by UTIL_SEQ_Run() in case it is called again in the task
*/
super_mask_backup = SuperMask;
SuperMask &= Mask_bm;
/*
* There are two independent mask to check:
* TaskMask that comes from UTIL_SEQ_PauseTask() / UTIL_SEQ_ResumeTask
* SuperMask that comes from UTIL_SEQ_Run
* If the waited event is there, exit from UTIL_SEQ_Run() to return to the
* waiting task
*/
local_taskset = TaskSet;
local_evtset = EvtSet;
local_taskmask = TaskMask;
local_evtwaited = EvtWaited;
while(((local_taskset & local_taskmask & SuperMask) != 0U) && ((local_evtset & local_evtwaited)==0U))
{
counter = 0U;
/*
* When a flag is set, the associated bit is set in TaskPrio[counter].priority mask depending
* on the priority parameter given from UTIL_SEQ_SetTask()
* The while loop is looking for a flag set from the highest priority maskr to the lower
*/
while((TaskPrio[counter].priority & local_taskmask & SuperMask)== 0U)
{
counter++;
}
current_task_set = TaskPrio[counter].priority & local_taskmask & SuperMask;
/*
* The round_robin register is a mask of allowed flags to be evaluated.
* The concept is to make sure that on each round on UTIL_SEQ_Run(), if two same flags are always set,
* the sequencer does not run always only the first one.
* When a task has been executed, The flag is removed from the round_robin mask.
* If on the next UTIL_SEQ_RUN(), the two same flags are set again, the round_robin mask will mask out the first flag
* so that the second one can be executed.
* Note that the first flag is not removed from the list of pending task but just masked by the round_robin mask
*
* In the check below, the round_robin mask is reinitialize in case all pending tasks haven been executed at least once
*/
if ((TaskPrio[counter].round_robin & current_task_set) == 0U)
{
TaskPrio[counter].round_robin = UTIL_SEQ_ALL_BIT_SET;
}
/*
* Read the flag index of the task to be executed
* Once the index is read, the associated task will be executed even though a higher priority stack is requested
* before task execution.
*/
CurrentTaskIdx = (SEQ_BitPosition(current_task_set & TaskPrio[counter].round_robin));
/*
* remove from the roun_robin mask the task that has been selected to be executed
*/
TaskPrio[counter].round_robin &= ~(1U << CurrentTaskIdx);
UTIL_SEQ_ENTER_CRITICAL_SECTION( );
/* remove from the list or pending task the one that has been selected to be executed */
TaskSet &= ~(1U << CurrentTaskIdx);
/* remove from all priority mask the task that has been selected to be executed */
for (counter = UTIL_SEQ_CONF_PRIO_NBR; counter != 0U; counter--)
{
TaskPrio[counter - 1U].priority &= ~(1U << CurrentTaskIdx);
}
UTIL_SEQ_EXIT_CRITICAL_SECTION( );
/* Execute the task */
TaskCb[CurrentTaskIdx]( );
/* 每执行一个任务喂狗一次 */
//HAL_IWDG_Refresh(&hiwdg);
local_taskset = TaskSet;
local_evtset = EvtSet;
local_taskmask = TaskMask;
local_evtwaited = EvtWaited;
}
/* the set of CurrentTaskIdx to no task running allows to call WaitEvt in the Pre/Post ilde context */
CurrentTaskIdx = UTIL_SEQ_NOTASKRUNNING;
UTIL_SEQ_PreIdle( );
UTIL_SEQ_ENTER_CRITICAL_SECTION_IDLE( );
local_taskset = TaskSet;
local_evtset = EvtSet;
local_taskmask = TaskMask;
if ((local_taskset & local_taskmask & SuperMask) == 0U)
{
if ((local_evtset & EvtWaited)== 0U)
{
UTIL_SEQ_Idle( );
}
}
UTIL_SEQ_EXIT_CRITICAL_SECTION_IDLE( );
UTIL_SEQ_PostIdle( );
/* restore the mask from UTIL_SEQ_Run() */
SuperMask = super_mask_backup;
return;
}

更多推荐



所有评论(0)