论坛原始地址(持续更新):http://www.armbbs.cn/forum.php?mod=viewthread&tid=99514
第12章 ThreadX任务优先级修改及其分配方案
本章节主要为大家讲解ThreadX任务优先级设置的注意事项、任务优先级的分配方案及其相关的一个例子,内容相对比较简单。
12.1 任务优先级说明
12.2 任务优先级分配方案
12.3 中断优先级和任务优先级的区别
12.4 任务优先级修改函数tx_thread_priority_change
12.5 任务优先级获取函数tx_thread_info_get
12.6 实验例程说明
12.7 总结
12.1 任务优先级说明
下面对ThreadX优先级相关的几个重要知识点进行下说明,这些知识点在以后的使用中务必要掌握牢固。
1、 ThreadX中任务的最大优先级数值是通过tx_port.h文件中的TX_MAX_PRIORITIES进行配置的,用户实际可以使用的优先级范围是0到configMAX_PRIORITIES – 1。比如我们配置此宏定义为32,那么用户可以使用的优先级号是0到31,不包含32,对于这一点,初学者要特别的注意。并且TX_MAX_PRIORITIES的宏定义设置的数值必须是32的整数倍:
#define TX_MAX_PRIORITIES 32
2、 用户配置任务的优先级数值越小,那么此任务的优先级越低(0是最高优先级任务),ThreadX没有空闲任务,如果大家创建空闲任务,需要将其设置为最低优先级。
3、 建议用户配置宏定义TX_MAX_PRIORITIES的最大值不要超过32,即用户任务可以使用的优先级范围是0到31。
- 因为对于CM内核的移植文件,有专用的汇编指令CLZ(Count Leading Zeros),通过这些指令可以加速算法执行速度。
- 比通用方式高效。
- ThreadX查找最高优先级任务是通过定义的32bit数组,
ULONG _tx_thread_preempted_maps[TX_MAX_PRIORITIES/32];
如果TX_MAX_PRIORITIES设置为32,那么仅需一个32bit变量就可以方便记录32不同优先级任务,程序运行时查找最高优先级任务也方便。
4、 ThreadX中处于运行状态的任务永远是当前能够运行的最高优先级任务。下一章节讲解调度器,大家会对这个知识点有一个全面的认识。
12.2 任务优先级分配方案
对于初学者,有时候会纠结任务优先级设置为多少合适,因为任务优先级设置多少是没有标准的。对于这个问题,我们这里为大家推荐一个标准,任务优先级设置推荐方式如下图所示:
- IRQ任务:IRQ任务是指通过中断服务程序进行触发的任务,此类任务应该设置为所有任务里面优先级最高的。
- 高优先级后台任务:比如按键检测,触摸检测,USB消息处理,串口消息处理等,都可以归为这一类任务。
- 低优先级的时间片调度任务:比如GUI的界面显示,LED数码管的显示等不需要实时执行的都可以归为这一类任务。实际应用中用户不必拘泥于将这些任务都设置为优先级1的同优先级任务,可以设置多个优先级,只需注意这类任务不需要高实时性。
- 空闲任务:空闲任务是系统任务。
- 特别注意:IRQ任务和高优先级任务必须设置为阻塞式(调用消息等待或者延迟等函数即可),只有这样,高优先级任务才会释放CPU的使用权,,从而低优先级任务才有机会得到执行。
这里的优先级分配方案是我们推荐的一种方式,实际项目也可以不采用这种方法。调试出适合项目需求的才是最好的。
12.3 中断优先级和任务优先级区别
部分初学者也容易在这两个概念上面出现问题。简单的说,这两个之间没有任何关系,不管中断的优先级是多少,中断的优先级永远高于任何任务的优先级,即任务在执行的过程中,中断来了就开始执行中断服务程序。
另外对于STM32来说,中断优先级的数值越小,优先级越高。同样,ThreadX的任务优先级也是任务优先级数值越小,任务优先级越高。
12.4 任务优先级修改函数tx_thread_priority_change
函数原型:
UINT tx_thread_priority_change( TX_THREAD *thread_ptr, UINT new_priority, UINT *old_priority);
函数描述:
函数tx_thread_priority_change用于实现ThreadX任务优先级的修改。
- 第1个参数thread_ptr是任务句柄,用于区分不同的任务。
- 第2个参数new_priority是新任务优先级(0 至 (TX_MAX_PRIORITIES-1))。0表示最高优先级。
- 第3个参数old_priority是任务之前的优先级。
注意事项:
- 允许在任务和定时器组里面调用。
- 指定任务的抢占阈值自动设置为新的优先级。如果需要新的阈值,则必须在此调用之后使用函数tx_thread_preemption_change。
- 第二个参数数值不可大于等于tx_port.h文件中的宏定义:#define TX_MAX_PRIORITIES 配置的数值。
使用举例:
TX_THREAD AppTaskUserIFTCB; UINT OldPriority; /*优先级将其设置为6 */ tx_thread_priority_change(&AppTaskUserIFTCB, 6, &OldPriority);
12.5 任务优先级获取函数tx_thread_info_get
函数原型:
UINT tx_thread_info_get( TX_THREAD *thread_ptr, CHAR **name, UINT *state, ULONG *run_count, UINT *priority, UINT *preemption_threshold, ULONG *time_slice, TX_THREAD **next_thread, TX_THREAD **suspended_thread);
函数描述:
函数tx_thread_info_get不仅仅可用于获取ThreadX任务优先级。还可以获取其它相关信息。
1、 第1个参数thread_ptr是任务句柄,用于区分不同的任务。
2、 第2个参数name用于获取任务名。
3、 第3个参数state用于获取任务状态,可能的值如下所示。
- TX_READY (0x00)
- TX_COMPLETED (0x01)
- TX_TERMINATED (0x02)
- TX_SUSPENDED (0x03)
- TX_SLEEP (0x04)
- TX_QUEUE_SUSP (0x05)
- TX_SEMAPHORE_SUSP (0x06)
- TX_EVENT_FLAG (0x07)
- TX_BLOCK_MEMORY (0x08)
- TX_BYTE_MEMORY (0x09)
- TX_MUTEX_SUSP (0x0D)
4、 第4个参数run_count用于获取任务运行计数。
5、 第5个参数priority用于获取任务优先级。
6、 第6个参数preemption_threshold用于获取抢占阀值。
7、 第7个参数time_slice用于获取时间片。
8、 第8个参数next_thread指向下一个创建的任务句柄。
9、 第9个参数suspended_thread指向挂起列表中下一个任务句柄。
10、 返回值
- TX_SUCCESS:(0X00) 成功检索任务信息。
- TX_THREAD_ERROR:(0x0E) 任务控制指针无效。
注意事项:
- 可以在初始化阶段,任务,定时器组和中断服务程序里面调用。
使用举例:
TX_THREAD AppTaskUserIFTCB; CHAR *name; UINT state; ULONG run_count; UINT priority; UINT preemption_threshold; UINT time_slice; TX_THREAD *next_thread; TX_THREAD *suspended_thread; UINT status; status = tx_thread_info_get(&AppTaskUserIFTCB, &name, &state, &run_count, &priority, &preemption_threshold, &time_slice, &next_thread, &suspended_thread);
12.6 实验例程
配套例子:
V7-3007_ThreadX Task Priority Change
实验目的:
- 学习ThreadX的开关中断使用BasePri寄存器方案,这样使用ThreadX可以仅开关受ThreadX管理的中断,从而可以让高优先级中断实现零中断延迟。
实验内容:
1、共创建了如下几个任务,通过按下按键K1可以通过串口或者RTT打印任务堆栈使用情况
===================================================
OS CPU Usage = 1.94%
===================================================
Prio StackSize CurStack MaxStack Taskname
2 4092 383 391 App Task Start
3 4092 543 659 App Msp Pro
4 4092 391 391 App Task UserIF
5 4092 543 659 App Task COM
30 1020 519 519 App Task STAT
31 1020 143 71 App Task IDLE
0 1020 391 391 System Timer Thread
串口软件可以使用SecureCRT或者H7-TOOL RTT查看打印信息。
App Task Start任务 :启动任务,这里用作BSP驱动包处理。
App Task MspPro任务 :消息处理,这里未使用。
App Task UserIF任务 :按键消息处理。
App Task COM任务 :这里用作LED闪烁。
App Task STAT任务 :统计任务
App Task IDLE任务 :空闲任务
System Timer Thread任务:系统定时器任务
2、 (1) 凡是用到printf函数的全部通过函数App_Printf实现。
(2) App_Printf函数做了信号量的互斥操作,解决资源共享问题。
3、默认上电是通过串口打印信息,如果使用RTT打印信息
(1) MDK AC5,MDK AC6或IAR通过使能bsp.h文件中的宏定义为1即可
#define Enable_RTTViewer 1
(2) Embedded Studio继续使用此宏定义为0, 因为Embedded Studio仅制作了调试状态RTT方式查看。
实验操作:
- K1按键按下打印任务执行情况,按下K2和K3设置优先级后,可以按K1查看任务最新优先级是否设置成功。
- K2按键按下,设置任务App Task UserIF的优先级为6。
- K3按键按下,设置任务App Task UserIF的优先级为4。
串口打印信息方式(AC5,AC6和IAR):
波特率 115200,数据位 8,奇偶校验位无,停止位 1
RTT打印信息方式(AC5,AC6和IAR):
Embedded Studio仅支持调试状态RTT打印:
由于Embedded Studio不支持中文,所以中文部分显示乱码,不用管。
程序执行框图:
12.7 总结
本章节内容相对比较容易,重点是学习任务优先级分配方案,随着后面的学习,初学者需要慢慢积累这方面的经验。