FreeRTOS——支持多优先级

在FreeRTOS中,数字优先级越小,逻辑优先级也越小。

一、如何支持多优先级

就绪列表 pxReadyTasksLists[ configMAX_PRIORITIES ]是一个数组,数组里面存储的是就绪任务的TCB(准确来说是TCB里面的xStateListItem节点),数组的下表对应任务的优先级,优先级越低对应数组的下标越小。空闲任务的优先级最低,对应的是下标为0的链表。下图展示了就绪列表中有两个就续任务,优先级分别是1和2,其中空闲任务没有被画出来,空闲任务自系统启动就会一直就绪,因为系统至少要保证有一个任务可以运行。
在这里插入图片描述
任务在创建时,会根据任务的优先级将任务插入到就绪列表中的不同位置。相同优先级的任务插入到就绪列表中的同一条链表中。
pxCurrentTCB是一个全局的TCB指针,用于指向优先级最高的就续任务的TCB,即当前正在运行的TCB。那么我们想要让任务支持多优先级,即只要解决在任务切换的时候,让pxCurrentTCB指向最高优先级的就续任务的TCB就可以了。关于如何找到最高优先级的就续任务的TCB。FreeRTOS提供了两套方法,一是通用的,另一个是根据特定的处理器优化过的。

二、查找最高优先级的就绪任务代码

/* 查找最高优先级的就绪任务:通用方法 */
#if ( configUSE_PORT_OPTIMISED_TASK_SELECTION == 0 ) (1)
  /* uxTopReadyPriority 存的是就绪任务的最高优先级 */
  #define taskRECORD_READY_PRIORITY( uxPriority )\ (2)
 {\
	 if( ( uxPriority ) > uxTopReadyPriority )\
	 {\
	 	uxTopReadyPriority = ( uxPriority );\
	 }\
 } /* taskRECORD_READY_PRIORITY */

/*-----------------------------------------------------------*/
 #define taskSELECT_HIGHEST_PRIORITY_TASK()\ (3)
 {\
	 UBaseType_t uxTopPriority = uxTopReadyPriority;\ (3)-/* 寻找包含就绪任务的最高优先级的队列 */\ (3)-while( listLIST_IS_EMPTY( &( pxReadyTasksLists[ uxTopPriority ] ) ) )\
	 {\
		 --uxTopPriority;\
	 }\
	/* 获取优先级最高的就绪任务的 TCB,然后更新到 pxCurrentTCB */\
	 listGET_OWNER_OF_NEXT_ENTRY(pxCurrentTCB, &(pxReadyTasksLists[ uxTopPriority ]));\ (3)-/* 更新 uxTopReadyPriority */\
	 uxTopReadyPriority = uxTopPriority;\ (3)-} /* taskSELECT_HIGHEST_PRIORITY_TASK */

/*-----------------------------------------------------------*/
/* 这两个宏定义只有在选择优化方法时才用,这里定义为空 */
#define taskRESET_READY_PRIORITY( uxPriority )
#define portRESET_READY_PRIORITY( uxPriority, uxTopReadyPriority )

 /* 查找最高优先级的就绪任务:根据处理器架构优化后的方法 */
#else /* configUSE_PORT_OPTIMISED_TASK_SELECTION */ (4)

 #define taskRECORD_READY_PRIORITY( uxPriority ) \ (5)
 portRECORD_READY_PRIORITY( uxPriority, uxTopReadyPriority )

/*-----------------------------------------------------------*/

 #define taskSELECT_HIGHEST_PRIORITY_TASK()\ (7)
 {\
	 UBaseType_t uxTopPriority;\
	 /* 寻找最高优先级 */\
	 portGET_HIGHEST_PRIORITY( uxTopPriority, uxTopReadyPriority );\ (7)-/* 获取优先级最高的就绪任务的 TCB,然后更新到 pxCurrentTCB */\
	 listGET_OWNER_OF_NEXT_ENTRY( pxCurrentTCB, &( pxReadyTasksLists[ uxTopPriority ] ) );\ (7)-} /* taskSELECT_HIGHEST_PRIORITY_TASK() */

/*-----------------------------------------------------------*/
#if 0
 #define taskRESET_READY_PRIORITY( uxPriority )\ (注意)
 {\
	 if(listCURRENT_LIST_LENGTH(&(pxReadyTasksLists[( uxPriority)]))==(UBaseType_t)0)\
	 {\
	 	portRESET_READY_PRIORITY( ( uxPriority ), ( uxTopReadyPriority ) );\
	 }\
 }
#else
 #define taskRESET_READY_PRIORITY( uxPriority )\ (6)
 {\
 	portRESET_READY_PRIORITY((uxPriority ), (uxTopReadyPriority));\
 }
#endif

#endif /* configUSE_PORT_OPTIMISED_TASK_SELECTION */

(1):查 找 最 高 优 先 级 的 就 绪 任 务 有 两 种 方 法 , 具 体 由
configUSE_PORT_OPTIMISED_TASK_SELECTION 这个宏控制,定义0选择通用方法,定义1选择根据处理器优化的方法。

1、通用方法

(2):taskRECORD_READY_PRIORITY()用于更新uxTopReadyPriority的值。 uxTopReadyPriority 是一个在 task.c 中定义的静态变量, 用于表示创建的任务的最高优先级, 默认初始化为 0,即空闲任务的优先级。
uxTopReadyPriority 定义:

#define tskIDLE_PRIORITY ( ( UBaseType_t ) 0U )
/* 定义 uxTopReadyPriority,在 task.c 中定义 */
static volatile UBaseType_t uxTopReadyPriority = tskIDLE_PRIORITY;

(3):taskSELECT_HIGHEST_PRIORITY_TASK()用于寻找优先级最高的就绪任务, 实质就是更新 uxTopReadyPriority和pxCurrentTCB 的值。
(3)-①:将 uxTopReadyPriority 的值暂存到局部变量 uxTopPriority, 接下来需要用到
(3)-②:从最高优先级对应的就绪列表数组下表开始寻找当前链表下是否有任务存在,如果没有,则uxTopPriority 减一操作,继续寻找下一个优先级对应的链表中是否有任务存在,如果有则跳出while循环,表示找到了最高优先级的就绪任务。之所以采用从最高优先级往下搜索,是因为任务的优先级与就绪列表的下标是一一对应的,优先级越高,对应的就绪列表数组的下表就越大。
(3)-③:获取优先级最高的就绪任务的 TCB,然后更新到pxCurrentTCB。
(3)-④:更新 uxTopPriority 的值到 uxTopReadyPriority。

2、优化方法

(4):优化的方法得益于Contex-M内核有一个计算前导零的指令CLZ,所谓前导零就是计算一个变量(Cortex-M 内核单片机的变量为 32 位)从高位开始出现1的位的前面的零的个数,比如:一个32位的变量uxTopReadyPriority,其位0、24、25均置1,其余位为0,具体见下图。那么使用前导零指令_CLZ(uxTopReadyPriority)可以很快计算出uxTopReadyPriority的前导零的个数为6.
在这里插入图片描述
如果uxTopReadyPriority的每个位号对应的是任务的优先级,任务就绪时,则将对应的位置1,反之则清0。那么上图表示优先级0、优先级24、优先级25这三个任务就绪,其中优先级为25的任务优先级最高。利用前导零指令可以很快计算出就续任务中的最高优先级:( 31UL - ( uint32_t ) __clz( ( uxReadyPriorities ) ) ) = ( 31UL - ( uint32_t )6 ) = 25。
(5):taskRECORD_READY_PRIORITY()用于根据传进来的形参(通常形参就是任务先级) 将变量 uxTopReadyPriority 的某个位置 1。与通用方法中用来表示创建的任务的最高优先级不一样,它在优化方法中担任的是一个优先级位图表的角色,即该变量的每个位对应任务的优先级,如果任务就绪,则将对应的位置 1,反之清零。根据这个原理,只需要计算出 uxTopReadyPriority 的前导零个数就算找到了就绪任务的最高优先级。与通用方法中用来表示创建的任务的最高优先级不一样,它在优化方法中担任的是一个优先级位图表的角色,即该变量的每个位对应任务的优先级,如果任务就绪,则将对应的位置 1,反之清零。根据这个原理,只需要计算出 uxTopReadyPriority 的前导零个数就算找到了就绪任务的最高优先级。

#define portRECORD_READY_PRIORITY( uxPriority, uxReadyPriorities )    ( uxReadyPriorities ) |= ( 1UL << ( uxPriority ) )

#define portRESET_READY_PRIORITY( uxPriority, uxReadyPriorities )    ( uxReadyPriorities ) &= ~( 1UL << ( uxPriority ) )

(6):taskRESET_READY_PRIORITY()用于根据传进来的形参(通常形参就是任务的优先级) 将变量 uxTopReadyPriority 的某个位清零。
(注意):实际上根据优先级调用 taskRESET_READY_PRIORITY()函数复位uxTopReadyPriority时, 要先确保就绪列表中对应优先级下的链表没有任务才行。但是我们当前实现的阻塞延时方案还是通过扫描就绪列表中的TCB的延时变量xTicksToDelay来实现的,还没有单独实现延时列表,所以任务非就绪时暂时不能将任务优先级变量从就绪列表中删除,而是仅仅通过将uxTopReadyPriority中对应的位清零。
(7):taskSELECT_HIGHEST_PRIORITY_TASK()用于寻找优先级最高的就绪任务, 实质就是更新 uxTopReadyPriority和pxCurrentTCB 的值。
(7)-①:根据 uxTopReadyPriority 的值, 找到最高优先级, 然后更新到
uxTopPriority 这个局部变量中。

#define portGET_HIGHEST_PRIORITY( uxTopPriority, uxReadyPriorities )    uxTopPriority = ( 31UL - ( uint32_t ) __clz( ( uxReadyPriorities ) ) )

(7)-②:根据 uxTopPriority 的值, 从就绪列表中找到就绪的最高优先级的任务的 TCB,然后将 TCB 更新到 pxCurrentTCB。

参考:[野火®]《FreeRTOS 内核实现与应用开发实战—基于STM32》

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
FreeRTOS中任务的优先级通过配置文件FreeRTOSConfig.h中的configMAX_PRIORITIES宏定义进行设置。用户可以将可用的优先级范围从0到configMAX_PRIORITIES - 1进行配置。建议将configMAX_PRIORITIES的最大值设置为不超过32。空闲任务的优先级为0,而任务的优先级数值越小,优先级越低。 通常情况下,任务的优先级可以根据以下几个方案进行分配: 1. 中断任务:这些任务是通过中断服务程序触发的,应该设置为所有任务中优先级最高的。 2. 高优先级后台任务:例如按键检测、触摸检测、USB消息处理、串口消息处理等,这类任务可以归为高优先级后台任务。 3. 低优先级的时间片调度任务:例如emWin的界面显示、LED数码管的显示等不需要实时执行的任务,可以归为低优先级的时间片调度任务。 4. 空闲任务:空闲任务是系统任务。 需要注意的是,中断的优先级永远高于任何任务的优先级,即任务在执行的过程中,中断来了就开始执行中断服务程序。因此,中断优先级的数值越小,优先级越高;任务优先级数值越小,优先级越低。 关于为什么设置最大优先级为32个的疑惑,实际上,虽然一般十几个优先级就足够使用了,但是最大优先级的设置还是有一些限制条件的。具体限制条件可能与系统硬件或软件的实现有关,但是没有提供具体的信息。 在FreeRTOS中,任务的调度器会根据优先级来选择就绪任务进行执行。调度器会从优先级最高的任务开始查找就绪任务,如果找到了就会跳出循环,将找到的任务分配给TCB任务控制块去执行。然后,将找到的优先级传递给uxTopReadyPriority变量。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [FreeRTOS任务优先级](https://blog.csdn.net/m0_55744970/article/details/125951720)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [FreeRTOS的学习(二)——任务优先级问题](https://blog.csdn.net/qq_39397153/article/details/123666584)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值