第8章 任务优先级修改
本章节主要为大家讲解RTX任务优先级设置的注意事项,任务优先级的分配方案及其相关的一个例子,内容相对比较简单。
本章教程配套的例子含Cortex-M3内核的STM32F103和Cortex-M4内核的STM32F407。
8.1 任务优先级设置注意事项
8.2 任务优先级分配方案
8.3 任务优先级设置
8.4 实验例程说明
8.5 总结
8.1 任务优先级设置注意事项
RTX操作系统任务优先级的设置要注意以下几个问题:
1. 设置任务的优先级时,数值越小优先级越低。
2. 最低任务优先级是0,此优先级被空闲任务使用,任何其它任务都不可以使用。
3. 用户可以设置的优先级范围是1-254,由于RTX支持时间片调度,所以也是支持用户任务设置为相同的优先级。
4. 优先级255被保留,用于最重要的任务。
8.2 任务优先级分配方案
对于初学者,有时候会纠结任务优先级设置为多少合适,因为任务优先级设置多少是没有标准的。对于这个问题,RTX有一个推荐的设置标准,任务优先级设置推荐方式如下图8.1所示:
图8.1 任务优先级分配方案
IRQ任务:IRQ任务是指通过中断服务程序进行触发的任务,此类任务应该设置为所有任务里面优先级最高的。
高优先级后台任务:比如按键检测,触摸检测,USB消息处理,串口消息处理等,都可以归为这一类任务。
低优先级的时间片调度任务:比如emWin的界面显示,LED数码管的显示等不需要实时执行的都可以归为这一类任务。实际应用中用户不必拘泥于将这些任务都设置为优先级1的同优先级任务,可以设置多个优先级,只需注意这类任务不需要高实时性。
空闲任务:空闲任务是系统任务。
特别注意:IRQ任务和高优先级任务必须设置为阻塞式(调用消息等待或者延迟等函数即可),只有这样高优先级任务才会释放CPU的使用权,从低优先级任务才有机会得到执行。
这里的优先级分配方案是RTX操作系统推荐的一种方式,实际项目也可以不采用这种方法。调试出适合项目需求的才是最好的。
8.3 任务优先级设置
除了创建任务时可以设置任务优先级,也可以通过下面两个函数修改任务优先级:
os_tsk_prio
os_tsk_prio_self
关于这两个函数的讲解及其使用方法可以看教程第3章3.3小节里面说的参考资料rlarm.chm文件
这里重点的说一下函数os_tsk_prio。
函数原型:
- OS_RESULT os_tsk_prio (
- OS_TID task_id, /* 任务ID */
- U8 new_prio ); /* 新的任务优先级 (1-254) */
函数描述:
函数os_tsk_prio用于修改任务的优先级。
(1)第1个参数填任务的ID。如果ID参数是0,那么设置就是当前任务的优先级。
(2)第2个参数修改任务的优先级,如果new_prio的数值比当前执行任务的优先级大,将触发一次任务切换,切换到任务ID为task_id的任务中。如果new_pro的数值比当前执行任务的优先级小,当前任务会继续执行。
(3)如果任务优先级修改成功,函数返回OS_R_OK,其余所有情况返回OS_R_NOK,比如所写的任务ID不存在或者任务还没有启动。
使用这个函数要注意以下几个问题
1. 参数new_prio的范围是1-254。
2. 被修改任务的新优先级会一直保持有效直到用户再次修改。
3. 优先级0用于空闲任务,如果用户将这个参数设置为0的话,RTX系统会将其更改为1。优先级255被保留用于最重要的任务。
4. 对于RTX操作系统来说,优先级参数中数值越小优先级越低,也就是说空闲任务的优先级是最低的,因为它的优先级数值是0
使用举例:
- /*
- **********************************************************************************************************
- 变量
- **********************************************************************************************************
- */
- static uint64_t AppTaskUserIFStk[512/8]; /* 任务栈 */
-
- /* 任务句柄 */
- OS_TID HandleTaskUserIF = NULL;
- /*
- *********************************************************************************************************
- * 函 数 名: AppTaskChangePrio
- * 功能说明: 修改任务优先级
- * 形 参: 无
- * 返 回 值: 无
- *********************************************************************************************************
- */
- static void AppTaskDelete (void)
- {
- HandleTaskUserIF = os_tsk_create_user(AppTaskUserIF, /* 任务函数 */
- 1, /* 任务优先级 */
- &AppTaskUserIFStk, /* 任务栈 */
- sizeof(AppTaskUserIFStk)); /* 任务栈大小,单位字节数 */
-
- if(os_tsk_prio(HandleTaskLED, 3) == OS_R_OK)
- {
- printf("任务AppTaskLED优先级修改成功\r\n");
- }
- else
- {
- printf("任务AppTaskLED优先级修改失败\r\n");
- }
-
- }
8.4 实验例程说明
8.4.1 STM32F103开发板实验
配套例子:
V4-403_RTX实验_任务优先级修改
实验目的:
1. 学习RTX的任务优先级修改。
实验内容:
1. K1按键按下,串口打印。
2. K2按键按下,将任务AppTaskLED的优先级由2修改为3。
3. k3按键按下,将任务AppTaskLED的优先级由3修改为2。
4. 各个任务实现的功能如下:
AppTaskUserIF任务 :按键消息处理。
AppTaskLED任务 :LED闪烁。
AppTaskMsgPro任务 :消息处理,这里是用作LED闪烁。
AppTaskStart任务 :启动任务,也是最高优先级任务,这里实现按键扫描。
RTX配置:
RTX配置向导详情如下:
Task Configuration
(1)Number of concurrent running tasks
允许创建4个任务,实际创建了如下四个任务
AppTaskUserIF任务 :按键消息处理。
AppTaskLED任务 :LED闪烁。
AppTaskMsgPro任务 :消息处理,这里是用作LED闪烁。
AppTaskStart任务 :启动任务,也是最高优先级任务,这里实现按键扫描。
(2)Number of tasks with user-provided stack
创建的4个任务都是采用自定义堆栈方式。
RTX任务调试信息:
程序设计:
任务栈大小分配:
staticuint64_t AppTaskUserIFStk[512/8]; /* 任务栈 */
staticuint64_t AppTaskLEDStk[256/8]; /* 任务栈 */
staticuint64_t AppTaskMsgProStk[512/8]; /* 任务栈 */
staticuint64_t AppTaskStartStk[512/8]; /* 任务栈 */
将任务栈定义成uint64_t类型可以保证任务栈是8字节对齐的,8字节对齐的含义就是数组的首地址对8求余等于0。如果不做8字节对齐的话,部分C语言库函数,浮点运算和uint64_t类型数据运算会出问题。
系统栈大小分配:
RTX初始化:
- /*
- *********************************************************************************************************
- * 函 数 名: main
- * 功能说明: 标准c程序入口。
- * 形 参: 无
- * 返 回 值: 无
- *********************************************************************************************************
- */
- int main (void)
- {
- /* 初始化外设 */
- bsp_Init();
-
- /* 创建启动任务 */
- os_sys_init_user (AppTaskStart, /* 任务函数 */
- 4, /* 任务优先级 */
- &AppTaskStartStk, /* 任务栈 */
- sizeof(AppTaskStartStk)); /* 任务栈大小,单位字节数 */
- while(1);
- }
RTX任务创建:
- /*
- *********************************************************************************************************
- * 函 数 名: AppTaskCreate
- * 功能说明: 创建应用任务
- * 形 参: 无
- * 返 回 值: 无
- *********************************************************************************************************
- */
- static void AppTaskCreate (void)
- {
- HandleTaskUserIF = os_tsk_create_user(AppTaskUserIF, /* 任务函数 */
- 1, /* 任务优先级 */
- &AppTaskUserIFStk, /* 任务栈 */
- sizeof(AppTaskUserIFStk)); /* 任务栈大小,单位字节数 */
-
- HandleTaskLED = os_tsk_create_user(AppTaskLED, /* 任务函数 */
- 2, /* 任务优先级 */
- &AppTaskLEDStk, /* 任务栈 */
- sizeof(AppTaskLEDStk)); /* 任务栈大小,单位字节数 */
-
- HandleTaskMsgPro = os_tsk_create_user(AppTaskMsgPro, /* 任务函数 */
- 3, /* 任务优先级 */
- &AppTaskMsgProStk, /* 任务栈 */
- sizeof(AppTaskMsgProStk)); /* 任务栈大小,单位字节数 */
- }
四个RTX任务的实现:
- /*
- *********************************************************************************************************
- * 函 数 名: AppTaskUserIF
- * 功能说明: 按键消息处理
- * 形 参: 无
- * 返 回 值: 无
- * 优 先 级: 1 (数值越小优先级越低,这个跟uCOS相反)
- *********************************************************************************************************
- */
- __task void AppTaskUserIF(void)
- {
- uint8_t ucKeyCode;
-
- while(1)
- {
- ucKeyCode = bsp_GetKey();
-
- if (ucKeyCode != KEY_NONE)
- {
- switch (ucKeyCode)
- {
- /* K1键按下,打印调试说明 */
- case KEY_DOWN_K1:
- printf("K1键按下,使用MDK中自带的RTX调试组件,请务必使用MDK4.74版本进行调试\r\n");
- break;
-
- /* K2键按下,修改任务AppTaskLED优先级 */
- case KEY_DOWN_K2:
- printf("K2键按下,将任务AppTaskLED的优先级由2修改为3\r\n");
- if(os_tsk_prio(HandleTaskLED, 3) == OS_R_OK)
- {
- printf("任务AppTaskLED优先级修改成功\r\n");
- }
- else
- {
- printf("任务AppTaskLED优先级修改失败\r\n");
- }
- break;
-
- /* K3键按下,修改任务AppTaskLED优先级 */
- case KEY_DOWN_K3:
- printf("K3键按下,将任务AppTaskLED的优先级由3修改为2\r\n");
- if(os_tsk_prio(HandleTaskLED, 2) == OS_R_OK)
- {
- printf("任务AppTaskLED优先级修改成功\r\n");
- }
- else
- {
- printf("任务AppTaskLED优先级修改失败\r\n");
- }
- break;
-
-
- /* 其他的键值不处理 */
- default:
- break;
- }
- }
-
- os_dly_wait(20);
- }
- }
-
- /*
- *********************************************************************************************************
- * 函 数 名: AppTaskLED
- * 功能说明: LED闪烁
- * 形 参: 无
- * 返 回 值: 无
- * 优 先 级: 2
- *********************************************************************************************************
- */
- __task void AppTaskLED(void)
- {
- while(1)
- {
- bsp_LedToggle(2);
- bsp_LedToggle(3);
- os_dly_wait(200);
- }
- }
-
- /*
- *********************************************************************************************************
- * 函 数 名: AppTaskMsgPro
- * 功能说明: 信息处理,这里是用作LED闪烁
- * 形 参: 无
- * 返 回 值: 无
- * 优 先 级: 3
- *********************************************************************************************************
- */
- __task void AppTaskMsgPro(void)
- {
- while(1)
- {
- bsp_LedToggle(1);
- bsp_LedToggle(4);
- os_dly_wait(300);
- }
- }
-
- /*
- *********************************************************************************************************
- * 函 数 名: AppTaskStart
- * 功能说明: 启动任务,也就是最高优先级任务。
- * 形 参: 无
- * 返 回 值: 无
- * 优 先 级: 4
- *********************************************************************************************************
- */
- __task void AppTaskStart(void)
- {
- AppTaskCreate();
-
- while(1)
- {
- /* 按键扫描 */
- bsp_KeyScan();
- os_dly_wait(10);
- }
- }
8.4.2 STM32F407开发板实验
配套例子:
V5-403_RTX实验_任务优先级修改
实验目的:
1. 学习RTX的任务优先级修改。
实验内容:
1. K1按键按下,串口打印。
2. K2按键按下,将任务AppTaskLED的优先级由2修改为3。
3. k3按键按下,将任务AppTaskLED的优先级由3修改为2。
4. 各个任务实现的功能如下:
AppTaskUserIF任务 :按键消息处理。
AppTaskLED任务 :LED闪烁。
AppTaskMsgPro任务 :消息处理,这里是用作LED闪烁。
AppTaskStart任务 :启动任务,也是最高优先级任务,这里实现按键扫描。
RTX配置:
RTX配置向导详情如下:
Task Configuration
(1)Number of concurrent running tasks
允许创建4个任务,实际创建了如下四个任务
AppTaskUserIF任务 :按键消息处理。
AppTaskLED任务 :LED闪烁。
AppTaskMsgPro任务 :消息处理,这里是用作LED闪烁。
AppTaskStart任务 :启动任务,也是最高优先级任务,这里实现按键扫描。
(2)Number of tasks with user-provided stack
创建的4个任务都是采用自定义堆栈方式。
RTX任务调试信息:
程序设计:
任务栈大小分配:
staticuint64_t AppTaskUserIFStk[512/8]; /* 任务栈 */
staticuint64_t AppTaskLEDStk[256/8]; /* 任务栈 */
staticuint64_t AppTaskMsgProStk[512/8]; /* 任务栈 */
staticuint64_t AppTaskStartStk[512/8]; /* 任务栈 */
将任务栈定义成uint64_t类型可以保证任务栈是8字节对齐的,8字节对齐的含义就是数组的首地址对8求余等于0。如果不做8字节对齐的话,部分C语言库函数,浮点运算和uint64_t类型数据运算会出问题。
系统栈大小分配:
RTX初始化:
- /*
- *********************************************************************************************************
- * 函 数 名: main
- * 功能说明: 标准c程序入口。
- * 形 参: 无
- * 返 回 值: 无
- *********************************************************************************************************
- */
- int main (void)
- {
- /* 初始化外设 */
- bsp_Init();
-
- /* 创建启动任务 */
- os_sys_init_user (AppTaskStart, /* 任务函数 */
- 4, /* 任务优先级 */
- &AppTaskStartStk, /* 任务栈 */
- sizeof(AppTaskStartStk)); /* 任务栈大小,单位字节数 */
- while(1);
- }
RTX任务创建:
- /*
- *********************************************************************************************************
- * 函 数 名: AppTaskCreate
- * 功能说明: 创建应用任务
- * 形 参: 无
- * 返 回 值: 无
- *********************************************************************************************************
- */
- static void AppTaskCreate (void)
- {
- HandleTaskUserIF = os_tsk_create_user(AppTaskUserIF, /* 任务函数 */
- 1, /* 任务优先级 */
- &AppTaskUserIFStk, /* 任务栈 */
- sizeof(AppTaskUserIFStk)); /* 任务栈大小,单位字节数 */
-
- HandleTaskLED = os_tsk_create_user(AppTaskLED, /* 任务函数 */
- 2, /* 任务优先级 */
- &AppTaskLEDStk, /* 任务栈 */
- sizeof(AppTaskLEDStk)); /* 任务栈大小,单位字节数 */
-
- HandleTaskMsgPro = os_tsk_create_user(AppTaskMsgPro, /* 任务函数 */
- 3, /* 任务优先级 */
- &AppTaskMsgProStk, /* 任务栈 */
- sizeof(AppTaskMsgProStk)); /* 任务栈大小,单位字节数 */
- }
四个RTX任务的实现:
- /*
- *********************************************************************************************************
- * 函 数 名: AppTaskUserIF
- * 功能说明: 按键消息处理
- * 形 参: 无
- * 返 回 值: 无
- * 优 先 级: 1 (数值越小优先级越低,这个跟uCOS相反)
- *********************************************************************************************************
- */
- __task void AppTaskUserIF(void)
- {
- uint8_t ucKeyCode;
-
- while(1)
- {
- ucKeyCode = bsp_GetKey();
-
- if (ucKeyCode != KEY_NONE)
- {
- switch (ucKeyCode)
- {
- /* K1键按下,打印调试说明 */
- case KEY_DOWN_K1:
- printf("K1键按下,使用MDK中自带的RTX调试组件,请务必使用MDK4.74版本进行调试\r\n");
- break;
-
- /* K2键按下,修改任务AppTaskLED优先级 */
- case KEY_DOWN_K2:
- printf("K2键按下,将任务AppTaskLED的优先级由2修改为3\r\n");
- if(os_tsk_prio(HandleTaskLED, 3) == OS_R_OK)
- {
- printf("任务AppTaskLED优先级修改成功\r\n");
- }
- else
- {
- printf("任务AppTaskLED优先级修改失败\r\n");
- }
- break;
-
- /* K3键按下,修改任务AppTaskLED优先级 */
- case KEY_DOWN_K3:
- printf("K3键按下,将任务AppTaskLED的优先级由3修改为2\r\n");
- if(os_tsk_prio(HandleTaskLED, 2) == OS_R_OK)
- {
- printf("任务AppTaskLED优先级修改成功\r\n");
- }
- else
- {
- printf("任务AppTaskLED优先级修改失败\r\n");
- }
- break;
-
-
- /* 其他的键值不处理 */
- default:
- break;
- }
- }
-
- os_dly_wait(20);
- }
- }
-
- /*
- *********************************************************************************************************
- * 函 数 名: AppTaskLED
- * 功能说明: LED闪烁
- * 形 参: 无
- * 返 回 值: 无
- * 优 先 级: 2
- *********************************************************************************************************
- */
- __task void AppTaskLED(void)
- {
- while(1)
- {
- bsp_LedToggle(2);
- bsp_LedToggle(3);
- os_dly_wait(200);
- }
- }
-
- /*
- *********************************************************************************************************
- * 函 数 名: AppTaskMsgPro
- * 功能说明: 信息处理,这里是用作LED闪烁
- * 形 参: 无
- * 返 回 值: 无
- * 优 先 级: 3
- *********************************************************************************************************
- */
- __task void AppTaskMsgPro(void)
- {
- while(1)
- {
- bsp_LedToggle(1);
- bsp_LedToggle(4);
- os_dly_wait(300);
- }
- }
-
- /*
- *********************************************************************************************************
- * 函 数 名: AppTaskStart
- * 功能说明: 启动任务,也就是最高优先级任务。
- * 形 参: 无
- * 返 回 值: 无
- * 优 先 级: 4
- *********************************************************************************************************
- */
- __task void AppTaskStart(void)
- {
- AppTaskCreate();
-
- while(1)
- {
- /* 按键扫描 */
- bsp_KeyScan();
- os_dly_wait(10);
- }
- }
8.5 总结
本章节内容相对比较容易,重点是学习任务优先级分配方案,随着后面的学习,初学者需要慢慢积累这方面的经验。