目录
5.1.2.1记录就绪优先级函数portRECORD_READY_PRIORITY具体操作如下:
5.1.2.2清除就绪优先级函数portRESET_READY_PRIORITY具体操作如下:
5.1.2.3寻找最高就绪优先级函数portGET_HIGHEST_PRIORITY具体操作如下:
5.1.2.4选择当前最高优先级就绪任务taskSELECT_HIGHEST_PRIORITY_TASK
5.2.1.1prvInitialiseNewTask函数的新实现:
5.4修改vTaskDelay阻塞延时函数(task.c中)
5.5修改vTaskSwitchContext切换任务函数(task.c中)
5.6修改xTaskIncrementTick函数(task.c中)
5.实现多优先级
5.1查找最高优先级的就绪任务函数(task.c中定义)
这里首先说明一点,configUSE_PORT_OPTIMISED_TASK_SELECTION 宏控制是选择“通用方法”还是“优化方法”的宏定义,1选择“优化方法”,反之“通用方法”;
5.1.1通用查找方法(没有针对特定处理器优化)
通用查找方法有以下两种方法:
//以下代码在“task.h”中定义
#define tskIDLE_PRIORITY ( ( UBaseType_t ) 0U )
//以下代码在“task.C”中实现
static volatile UBaseType_t uxTopReadyPriority = tskIDLE_PRIORITY;
/* 查找最高优先级的就绪任务:通用方法 */
#if ( configUSE_PORT_OPTIMISED_TASK_SELECTION == 0 )
/* uxTopReadyPriority 存的是就绪任务的最高优先级 */
#define taskRECORD_READY_PRIORITY( uxPriority ) \
{ \
if( ( uxPriority ) > uxTopReadyPriority ) \
{ \
uxTopReadyPriority = ( uxPriority ); \
} \
} /* taskRECORD_READY_PRIORITY */
/*-----------------------------------------------------------*/
#define taskSELECT_HIGHEST_PRIORITY_TASK() \
{ \
UBaseType_t uxTopPriority = uxTopReadyPriority; \
/* 寻找包含就绪任务的最高优先级的队列 */ \
while( listLIST_IS_EMPTY( &( pxReadyTasksLists[ uxTopPriority ] ) ) ) \
{ \
--uxTopPriority; \
} \
/* 获取优先级最高的就绪任务的TCB,然后更新到pxCurrentTCB */ \
listGET_OWNER_OF_NEXT_ENTRY( pxCurrentTCB, &( pxReadyTasksLists[ uxTopPriority ] ) ); \
/* 更新uxTopReadyPriority */ \
uxTopReadyPriority = uxTopPriority; \
} /* taskSELECT_HIGHEST_PRIORITY_TASK */
首先说明一下,uxTopReadyPriority表示的是当前创建的所有任务中优先级最高的那个任务的有限级,它的初始值由宏tskIDLE_PRIORITY所决定,默认为0(0是最低优先级);
第一个查找当前任务最大优先级函数taskRECORD_READY_PRIORITY,它具体做了以下操作:
1、当前传入形参uxPriority是否>当前的uxTopReadyPriority任务最高优先级,若大于,则更新最大任务优先级,让uxTopReadyPriority始终=现有任务最大任务优先级;(创建任务时调用此函数,将创建任务的优先级别传入,更新uxTopReadyPriority为任务最大优先级)
第二个更新当前可运行最大优先级任务函数taskSELECT_HIGHEST_PRIORITY_TASK,它具体做了以下操作:
1、通过while循环,从最高优先级的任务TCB开始向下迭代查找那个挂载了任务的最大优先级的根节点,找到这个节点后即退出while循环;
2、通过listGET_OWNER_OF_NEXT_ENTRY宏小函数,这个函数在list.h中实现,其实现为:
#define listGET_OWNER_OF_NEXT_ENTRY( pxTCB, pxList ) \
{ \
List_t * const pxConstList = ( pxList ); \
/* 节点索引指向链表第一个节点调整节点索引指针,指向下一个节点,
如果当前链表有N个节点,当第N次调用该函数时,pxInedex则指向第N个节点 */\
( pxConstList )->pxIndex = ( pxConstList )->pxIndex->pxNext; \
/* 当前链表为空 */ \
if( ( void * ) ( pxConstList )->pxIndex == ( void * ) &( ( pxConstList )->xListEnd ) ) \
{ \
( pxConstList )->pxIndex = ( pxConstList )->pxIndex->pxNext; \
} \
/* 获取节点的OWNER,即TCB */ \
( pxTCB ) = ( pxConstList )->pxIndex->pvOwner; \
}
可以看到利用这个函数可以让pxcurrent当前任务指针,指向当前挂载有任务且优先级最高的根节点上;(总的来说,这个函数主要在做的就是让当前任务指针,指向挂载有任务并且优先级最高的任务控制块)
5.1.2针对处理器优化方法
若此时运行操作系统的处理器是以cortex为内核的处理器,则在其指令集中可以使用一个前导0指令CLZ(它可以计算一个变量从高位开始数第一次出现1时,计算这个1前面有多少个0),具体如下例所示:
上图中uxTopReadyPriority变量的第25,24位及第0位为1其余位置默认为0,且每个bit位都对应相应的优先号(所以这里优先级 0、优先级 24 和优先级 25 这三个任务就绪),其中优先级为 25的任务优先级最高,这里利用前导0指令就能快速计算出当前优先级最高并且已就绪的最高优先级任务的优先号:( 31UL - ( uint32_t ) __clz( ( uxReadyPriorities ) ) ) = ( 31UL - ( uint32_t )
这里以上面的原理为基础,引出两个优化过的记录和清除以及找出已就绪最高优先的函数portRECORD_READY_PRIORITY和portRESET_READY_PRIORITY以及portGET_HIGHEST_PRIORITY:
//portmacro.h中定义
#if configUSE_PORT_OPTIMISED_TASK_SELECTION == 1
/* 检测优先级配置 */
#if( configMAX_PRIORITIES > 32 )
#error configUSE_PORT_OPTIMISED_TASK_SELECTION can only be set to 1 when configMAX_PRIORITIES is less than or equal to 32. It is very rare that a system requires more than 10 to 15 difference priorities as tasks that share a priority will time slice.
#endif
/* 根据优先级设置/清除优先级位图中相应的位 */
#define portRECORD_READY_PRIORITY( uxPriority, uxReadyPriorities ) ( uxReadyPriorities ) |= ( 1UL << ( uxPriority ) )
#define portRESET_READY_PRIORITY( uxPriority, uxReadyPriorities ) ( uxReadyPriorities ) &= ~( 1UL << ( uxPriority ) )
/*-----------------------------------------------------------*/
#define portGET_HIGHEST_PRIORITY( uxTopPriority, uxReadyPriorities ) uxTopPriority = ( 31UL - ( uint32_t ) __clz( ( uxReadyPriorities ) ) )
#endif /* taskRECORD_READY_PRIORITY */
可以看到“configUSE_PORT_OPTIMISED_TASK_SELECTION ”设置为1,在“优化模式”(也就是cortex内核下)下,且任务优先级必须设置在32位以内才能够执行,否则返回错误信息:
5.1.2.1记录就绪优先级函数portRECORD_READY_PRIORITY具体操作如下:
1.第一个参数uxPriority是优先级,第二个参数uxReadyPriorities是存储全部优先级状态的变量;
2.将uxReadyPriorities相应bit位置1;(至此记录了当前任务优先级)
5.1.2.2清除就绪优先级函数portRESET_READY_PRIORITY具体操作如下:
1.第一个参数uxPriority是优先级,第二个参数uxReadyPriorities是存储全部优先级状态的变量;
2.将uxReadyPriorities相应bit位置0;(至此清除了当前任务优先级)
5.1.2.3寻找最高就绪优先级函数portGET_HIGHEST_PRIORITY具体操作如下:
1.第一个参数uxTopPriority是记录当前最高优先级的,第二个参数uxReadyPriorities记录了所有优先级任务的状态;
2.通过CLZ指令uxTopPriority =31UL - ( uint32_t ) __clz( ( uxReadyPriorities ),uxTopPriority),uxTopPriority现在得到了高就绪任务的优先级;(至此计算出了当前就绪任务的最高优先级)
5.1.2.4选择当前最高优先级就绪任务taskSELECT_HIGHEST_PRIORITY_TASK
此时通过上面的几个基础的函数,就可以组合成寻找优先级最高的就绪任务的函数了,这个函数在后面会用到:
//task.c中实现
#else /* configUSE_PORT_OPTIMISED_TASK_SELECTION */
#define taskRECORD_READY_PRIORITY( uxPriority ) portRECORD_READY_PRIORITY(uxPriority, uxTopReadyPriority )
/*-----------------------------------------------------------*/
#define taskSELECT_HIGHEST_PRIORITY_TASK() \
{ \
UBaseType_t uxTopPriority;
/* 寻找最高优先级 */ \
portGET_HIGHEST_PRIORITY( uxTopPriority, uxTopReadyPriority ); \
/* 获取优先级最高的就绪任务的TCB,然后更新到pxCurrentTCB */ \
listGET_OWNER_OF_NEXT_ENTRY( pxCurrentTCB, &( pxReadyTasksLists[ uxTopPriority ] ) ); \
} /* taskSELECT_HIGHEST_PRIORITY_TASK() */
在task.c中记录当前优先级函数portRECORD_READY_PRIORITY被替换成了taskRECORD_READY_PRIORITY,且新增了“选择当前最高优先级就绪任务”,这里着重介绍“选择当前最高优先级就绪任务”taskSELECT_HIGHEST_PRIORITY_TASK:
1、创建临时变量uxTopPriority;
2、通过portGET_HIGHEST_PRIORITY函数,将当前就绪的最高优先级任务的优先级号,赋予记录到临时变量uxTopPriority中;
3、pxcurrentTCB当前任务控制块指针->当前最高优先级的任务控制块;(若这个函数在调度器中调度,则当前任务会被切换到pxcurrentTCB所指向的任务)
5.2修改创建任务函数
各个任务都有一个任务任务控制块,前面几节由于没有使用优先级的概念所以在任务控制块的成员中没有优先级成员,现在加入这个成员"uxPriority":
typedef struct tskTaskControlBlock
{
volatile StackType_t *pxTopOfStack; /* 栈顶 */
ListItem_t xStateListItem; /* 任务节点 */
StackType_t *pxStack; /* 任务栈起始地址 */
char pcTaskName[ configMAX_TASK_NAME_LEN ];/* 任务名称,字符串形式 */
TickType_t xTicksToDelay;
UBaseType_t uxPriority;
} tskTCB;
typedef tskTCB TCB_t;
5.2.1修改静态创建任务函数(task.c中)
在第二节中,在静态创建任务时并没有考虑到各个任务都有其优先级,现在加入优先级的概念,所以这里在创建任务时,要将优先级考虑进去:
TaskHandle_t xTaskCreateStatic( TaskFunction_t pxTaskCode, /* 任务入口 */
const char * const pcName, /* 任务名称,字符串形式 */
const uint32_t ulStackDepth, /* 任务栈大小,单位为字 */
void * const pvParameters, /* 任务形参 */
UBaseType_t uxPriority, /* 任务优先级,数值越大,优先级越高 */
StackType_t * const puxStackBuffer, /* 任务栈起始地址 */
TCB_t * const pxTaskBuffer ) /* 任务控制块 */
{
TCB_t *pxNewTCB;
TaskHandle_t xReturn;
if( ( pxTaskBuffer != NULL ) && ( puxStackBuffer != NULL ) )
{
pxNewTCB = ( TCB_t * ) pxTaskBuffer;
pxNewTCB->pxStack = ( StackType_t * ) puxStackBuffer;
/* 创建新的任务 */
prvInitialiseNewTask( pxTaskCode, pcName, ulStackDepth, pvParameters,uxPriority, &xReturn, pxNewTCB);
/* 将任务添加到就绪列表 */
prvAddNewTaskToReadyList( pxNewTCB );
}
else
{
xReturn = NULL;
}
return xReturn;
}
可以看相比于第二节,只是在它之上在prvInitialiseNewTask中添加了“优先级项”,在创建完新任务之后,新写了一个实现,直接将任务添加到就绪列表,这里先介绍修改后的prvInitialiseNewTask函数,再介绍添加任务到就绪列表的新实现,从而来确定静态创建任务函数到底做了什么。
5.2.1.1prvInitialiseNewTask函数的新实现:
static void prvInitialiseNewTask( TaskFunction_t pxTaskCode, /* 任务入口 */
const char * const pcName, /* 任务名称,字符串形式 */
const uint32_t ulStackDepth, /* 任务栈大小,单位为字 */
void * const pvParameters, /* 任务形参 */
UBaseType_t uxPriority, /* 任务优先级,数值越大,优先级越高 */
TaskHandle_t * const pxCreatedTask, /* 任务句柄 */
TCB_t *pxNewTCB ) /* 任务控制块 */
{
StackType_t *pxTopOfStack;
UBaseType_t x;
/* 获取栈顶地址 */
pxTopOfStack = pxNewTCB->pxStack + ( ulStackDepth - ( uint32_t ) 1 );
//pxTopOfStack = ( StackType_t * ) ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack ) & ( ~( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) ) );
/* 向下做8字节对齐 */
pxTopOfStack = ( StackType_t * ) ( ( ( uint32_t ) pxTopOfStack ) & ( ~( ( uint32_t ) 0x0007 ) ) );
/* 将任务的名字存储在TCB中 */
for( x = ( UBaseType_t ) 0; x < ( UBaseType_t ) configMAX_TASK_NAME_LEN; x++ )
{
pxNewTCB->pcTaskName[ x ] = pcName[ x ];
if( pcName[ x ] == 0x00 )
{
break;
}
}
/* 任务名字的长度不能超过configMAX_TASK_NAME_LEN */
pxNewTCB->pcTaskName[ configMAX_TASK_NAME_LEN - 1 ] = '\0';
/* 初始化TCB中的xStateListItem节点 */
vListInitialiseItem( &( pxNewTCB->xStateListItem ) );
/* 设置xStateListItem节点的拥有者 */
listSET_LIST_ITEM_OWNER( &( pxNewTCB->xStateListItem ), pxNewTCB );
/* 初始化优先级 */
if( uxPriority >= ( UBaseType_t ) configMAX_PRIORITIES )
{
uxPriority = ( UBaseType_t ) configMAX_PRIORITIES - ( UBaseType_t ) 1U;
}
pxNewTCB->uxPriority = uxPriority;
/* 初始化任务栈 */
pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxTaskCode, pvParameters );
/* 让任务句柄指向任务控制块 */
if( ( void * ) pxCreatedTask != NULL )
{
*pxCreatedTask = ( TaskHandle_t ) pxNewTCB;
}
}
prvInitialiseNewTask创建新任务函数它具体做了如下操作(其中有些语句原理性的东西再第二节已经讲过这里不再详诉):
1、首先将pxTopOfStack指针指向当前任务栈栈顶,而后根据当前“所处指令集”,向下做8字节对齐;
2、将“名字”传到当前TCB中,并赋予结束符;
3、初始化当前任务TCB的节点项(也就是将里面的节点项xStateListItem,内的指向当前所属链表指针pvContainer置空);
4、设置当前任务节点的内核对象为当前任务的TCB控制块;
5、设置优先级,首先将优先级限制在当前允许的最大优先级32内(由于现在优先级是存在32位变量中的),再将限制过后的优先级传入当前任务控制块新增优先级项中;
6、初始化任务栈,也就是手动压栈到当前任务栈,此时初始化任务栈函数返回的是手动压栈后,pxtopofstack栈顶指针指向的栈空间地址;
7、若任务句柄不为空,则将任务句柄指向当前任务控制块(之后在静态创建任务函数中返回出去时,若任务句柄不为空则表示任务创建成功);
5.2.1.2添加任务添加到就绪列表函数
现在说明完了创建新任务函数prvInitialiseNewTask的具体操作,那么在静态创建任务中下一步要执行的就是将任务添加到就绪列表函数prvAddNewTaskToReadyList具体如下所示:
static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB )
{
/* 进入临界段 */
taskENTER_CRITICAL();
{
/* 全局任务计时器加一操作 */
uxCurrentNumberOfTasks++;
/* 如果pxCurrentTCB为空,则将pxCurrentTCB指向新创建的任务 */
if( pxCurrentTCB == NULL )
{
pxCurrentTCB = pxNewTCB;
/* 如果是第一次创建任务,则需要初始化任务相关的列表 */
if( uxCurrentNumberOfTasks == ( UBaseType_t ) 1 )
{
/* 初始化任务相关的列表 */
prvInitialiseTaskLists();
}
}
else /* 如果pxCurrentTCB不为空,则根据任务的优先级将pxCurrentTCB指向最高优先级任务的TCB */
{
if( pxCurrentTCB->uxPriority <= pxNewTCB->uxPriority )
{
pxCurrentTCB = pxNewTCB;
}
}
uxTaskNumber++;
/* 将任务添加到就绪列表 */
prvAddTaskToReadyList( pxNewTCB );
}
/* 退出临界段 */
taskEXIT_CRITICAL();
}
任务添加到就绪列表函数prvAddNewTaskToReadyList具体做了以下操作:
1、进入临界段防止其它中断打断此操作;(可以看到,将任务添加到就绪列表这个操作还是比较重要的)
2、全局正在运行任务计数变量(代表当前正在运行的任务数量,当一个任务被挂起(例如等待某个事件或信号)时,uxCurrentNumberOfTasks会减少;当一个任务被恢复执行时,这个值会增加)+1,此时是在创建任务,任务没有被挂起所以这个变量+1;
3、如果现在还没有创建过任务(因为只有还没有调用创建任务函数时,pxCurrentTCB才会为空),则将pxcurrentTCB当前任务控制块指针指向现在创建的任务(也就是哪个任务第一次创建,那么pxcurrentTCB就首先指向谁),若现在是第一次创建任务,则还没有初始化过就绪列表,此时必须初始化一边就绪列表(就是将就绪列表内的各个根节点的next与previous指针指向自己);
4、如果现在已经创建过任务了(pxCurrentTCB != NULL),则pxCurrentTCB 指向当前已创建任务中优先级最高的那个任务;
5、全局记录创建任务个数变量uxTaskNumber+1(创建一个任务,这个变量就会+1);
6、将任务添加到就绪列表其具体实现如下所示:
//task.c中实现
/* 将任务添加到就绪列表 */
#define prvAddTaskToReadyList( pxTCB ) \
taskRECORD_READY_PRIORITY( ( pxTCB )->uxPriority ); \
vListInsertEnd( &( pxReadyTasksLists[ ( pxTCB )->uxPriority ] ), &( ( pxTCB )->xStateListItem ) ); \
可以看到就是将当前创建任务的TCB任务控制块传入,之后让全局优先级记录变量uxReadyPriorities记录当前优先级的任务已就绪(就是将那个优先号上的bit位置1),最后将任务插入到其所属优先级根节点的末尾;
7、已添加任务到就绪列表,退出临界段保护;
执行完前面的操作后,就以静态方式成功的创建了一个任务,并将其挂载到了其所属优先级的就绪列表;
5.3修改任务调度函数(task.c中)
由于现在要为系统添加了优先级概念,所以创建空闲任务时也要为其添加优先级,而空闲任务是在调用任务调度函数时创建的,故现在要修改任务调度函数中创建空闲任务的部分,具体如下所示:
void vTaskStartScheduler( void )
{
/*======================================创建空闲任务start==============================================*/
TCB_t *pxIdleTaskTCBBuffer = NULL;
StackType_t *pxIdleTaskStackBuffer = NULL;
uint32_t ulIdleTaskStackSize;
/* 获取空闲任务的内存:任务栈和任务TCB */
vApplicationGetIdleTaskMemory( &pxIdleTaskTCBBuffer,
&pxIdleTaskStackBuffer,
&ulIdleTaskStackSize );
xIdleTaskHandle = xTaskCreateStatic( (TaskFunction_t)prvIdleTask, /* 任务入口 */
(char *)"IDLE", /* 任务名称,字符串形式 */
(uint32_t)ulIdleTaskStackSize , /* 任务栈大小,单位为字 */
(void *) NULL, /* 任务形参 */
(UBaseType_t) tskIDLE_PRIORITY, /* 任务优先级,数值越大,优先级越高 */
(StackType_t *)pxIdleTaskStackBuffer, /* 任务栈起始地址 */
(TCB_t *)pxIdleTaskTCBBuffer ); /* 任务控制块 */
/*======================================创建空闲任务end================================================*/
/* 手动指定第一个运行的任务 */
//pxCurrentTCB = &Task1TCB;
/* 启动调度器 */
if( xPortStartScheduler() != pdFALSE )
{
/* 调度器启动成功,则不会返回,即不会来到这里 */
}
}
可以看到空闲任务现在是以静态创建带优先级任务的方式创建的;
5.4修改vTaskDelay阻塞延时函数(task.c中)
和前面实现阻塞延时的方式一样,但由于此时引入了记录各个优先级任务就绪的变量uxReadyPriorities,所以此时应该置调用阻塞延时的那个任务在调用阻塞延时函数后应该处于阻塞态,此时要将此任务所处的优先级在uxReadyPriorities变量的对应bit上置0,表示此优先级的任务未就绪,处于阻塞态;
void vTaskDelay( const TickType_t xTicksToDelay )
{
TCB_t *pxTCB = NULL;
/* 获取当前任务的TCB */
pxTCB = pxCurrentTCB;
/* 设置延时时间 */
pxTCB->xTicksToDelay = xTicksToDelay;
/* 将任务从就绪列表移除 */
//uxListRemove( &( pxTCB->xStateListItem ) );
taskRESET_READY_PRIORITY( pxTCB->uxPriority );
/* 任务切换 */
taskYIELD();
}
这里就添加了一个语句taskRESET_READY_PRIORITY( pxTCB->uxPriority );(前面介绍过,这个函数主要做的就是将当前传入优先级的bit位置0)
5.5修改vTaskSwitchContext切换任务函数(task.c中)
这个函数主要干的就是在pendsv中断中,找到下次要切换到的任务,现在引入了优先级概念,所以不能像前面几节一样手动切换。
/* 任务切换,即寻找优先级最高的就绪任务 */
void vTaskSwitchContext( void )
{
/* 获取优先级最高的就绪任务的TCB,然后更新到pxCurrentTCB */
taskSELECT_HIGHEST_PRIORITY_TASK();
}
可以看到这里直接使用前面所介绍过的taskSELECT_HIGHEST_PRIORITY_TASK函数,找到当前优先级最高的任务,并让当前任务控制块指针pxcurrentTCB指向它;(这样当程序退出pendsv中断时,会自动跳转到现在pxcurrentTCB指向的那个任务)
5.6修改xTaskIncrementTick函数(task.c中)
这个函数会在systick中断中被调用,其作用是更新当前说有任务的阻塞时间,但在加入优先级概念后,一个任务现在有了两个状态“就绪态”和“阻塞态”,当阻塞计时结束,就将现有任务切换为"就绪态",具体如下所示:
void xTaskIncrementTick( void )
{
TCB_t *pxTCB = NULL;
BaseType_t i = 0;
const TickType_t xConstTickCount = xTickCount + 1;
xTickCount = xConstTickCount;
/* 扫描就绪列表中所有线程的remaining_tick,如果不为0,则减1 */
for(i=0; i<configMAX_PRIORITIES; i++)
{
pxTCB = ( TCB_t * ) listGET_OWNER_OF_HEAD_ENTRY( ( &pxReadyTasksLists[i] ) );
if(pxTCB->xTicksToDelay > 0)
{
pxTCB->xTicksToDelay --;
/* 延时时间到,将任务就绪 */
if( pxTCB->xTicksToDelay ==0 )
{
taskRECORD_READY_PRIORITY( pxTCB->uxPriority );
}
}
}
/* 任务切换 */
portYIELD();
}
可以看到增加了一个判断:当计时完毕后,就将当前任务置就绪态;
5.7仿真
( 程序链接链接:https://pan.baidu.com/s/1IkEAQ0wAVUsGhbll3aj6lQ?pwd=1234
提取码:1234)
1、首先以带优先级的方法创建两个任务:
这个步骤中在任务1被创建后pxcurrent指针指向的是任务1所在的TCB任务控制块,在创建任务2时,由于任务2的优先级大于任务1所以当前pxcurrent指针应该指向的是任务2(这是由创建新任务函数中的 “prvAddNewTaskToReadyList函数” 决定的)
2、启动调度器,按上面的配置调度器启动后,应该进入任务2,这与在仿真中看到的结果相同,仿真如下:(第一次进入的任务是任务2)
且当前记录所有优先级任务就绪的变量当前是:(也就是任务1和任务2和空闲任务都都处于就绪态,前三位为1)
3、任务2进行到第一个阻塞延时函数,此时它应该将任务2所处优先级的那个bit位置0,从仿真中我们可以看到现在所有优先级任务就绪的变量变为了3:(也就是第3位变为了0)
4、至此在阻塞延时函数中会调用任务切换函数taskYIELD,之后会进入PendSV中断中,在这个中断中会调用vTaskSwitchContext任务切换函数来确定下一次执行任务;
5、在vTaskSwitchContext函数中,会选择当前优先级最高的就绪任务,按理来说只要任务2现在计时没完毕(任务2没被置“就绪态”),执行的就会是任务1,现在在来看仿真:(退出pendsv中断后执行的是任务1)
现在任务1会执行到阻塞函数那儿,之后的运行过程就和上面的运行过程一样了,这里不再做详细叙述了,让程序一直运行最后查看波形图会看到任务1和2的标志位是这样的:
详细看看每个脉冲宽度就是20ms:(看d的位子,这里没有将分隔线放准)
本人为初学菜鸟,文章如有错误地方,感谢指正!!
参考:野火freertos内核实现与应用