资源说明:FreeRTOS 任务切换流程说明
FreeRTOS 作为一个实时操作系统,任务管理是其核心,而任务管理的核心就是如何进行任务切换。本文将详细介绍 FreeRTOS 任务切换流程,包括任务基础知识、任务优先级、任务控制块、任务堆栈、任务切换流程等。
一、任务基础知识
1. 多任务系统与裸机的区别:裸机是直接 main 函数内一个大的 while 循环(后台程序)完成所有的处理,可以使用中断(前台程序),前后台各个任务排队轮流等候执行,相当于所有的程序优先级相同。多任务系统支持优先级设置,高优先级可以打断低优先级任务的运行取得 cpu 使用权,保证紧急任务的运行。
2. 任务状态:任务可以处于以下状态:
- 就绪态:可以执行的任务
- 运行态:当前正在执行的任务
- 等待态:等待某个事件的任务
-挂起态:暂停执行的任务
3. 任务优先级:FreeRTOS 支持优先级设置,高优先级可以打断低优先级任务的运行取得 cpu 使用权,保证紧急任务的运行。优先级从 0 到 configMAX_PRIORITIES-1,0 代表空闲任务的优先级最低。
四. 任务实现:使用函数 xTaskCreate() 或 xTaskCreateStatic() 创建任务,任务函数的模板为:void task(void *par) { for(;;) { --任务应用程序-- vTaskDelay(); } vTaskDelete(NULL);}。
五. 任务控制块:每个任务都有一些属性需要存储,FreeRTOS 把属性集中在一起用一个结构体:tskTCB(老版本)/TCB_t(任务控制块),创建任务时就会自动给每个任务块分配任务控制块。
六. 任务堆栈:任务调度器在进行任务切换时,就会将当前任务的现场(cpu 寄存器值等)保存在任务的任务堆栈中,任务下次运行就会用 stack 保存的值来恢复现场。创建任务时可以选择动态或静态创建任务堆栈,均需要指定任务堆栈大小。
二、任务切换
任务切换是在 PendSV 异常服务函数里完成的。PendSV 异常可以通过 ICSR 的 bit28 置 1 引起。上下文切换的场合包括:
1. 执行系统调用:直接调用可以引起任务切换的相关的 API 函数,如任务切换函数 taskYIELD() 以及一些可以调用 taskYIELD() 的 API 函数。
2. Systick 中断:SysTick_Handler() -> xPortSysTickHandler()。
PendSV 中断服务函数的源码为:
```
__asm void xPortPendSVHandler( void )
{
extern uxCriticalNesting;
extern pxCurrentTCB; /* 指向当前激活的任务 */
extern vTaskSwitchContext;
PRESERVE8
mrs r0, psp /* PSP 内容存入 R0 */
isb /* 指令同步隔离,清流水线 */
ldr r3, =pxCurrentTCB /* 当前激活的任务 TCB 指针存入 R2 */
ldr r2, [r3]
stmdb r0!, {r4-r11} /* 保存剩余的寄存器,异常处理程序执行前,硬件自动将xPSR、PC、LR、R12、R0-R3 入栈 */
str r0, [r2] /* 将新的栈顶保存到任务 TCB 的第一个成员中 */
stmdbsp!, {r3, r14} /* 将 R3 和 R14 临 时 压 入 堆 栈 , 因 为 即 将 调 用 函 数vTaskSwitchContext,调用函数时,返回地址自动保存到 R14 中,所以一旦调用发生,R14 的值会被覆盖,因此需要入栈保护; R3 保存的当前激活的任务 TCB 指针(pxCurrentTCB)地址,函数调用后会用到,因此也要入栈保护*/
mov r0, #configMAX_SYSCALL_INTERRUPT_PRIORITY /* 进入临界区 */
msr basepri, r0
dsb /* 数据和指令同步隔离 */
isb
bl vTaskSwitchContext /* 调用函数,寻找新的任务运行,通过使变量 pxCurrentTCB指向新的任务来实现任务切换 */
mov r0, #0 /* 退出临界区*/
msr basepri, r0
ldmia sp!, {r3, r14}
}
```
该函数首先保存当前任务的寄存器值和栈顶,然后调用 vTaskSwitchContext 函数寻找新的任务运行,并将新的栈顶保存到任务 TCB 的第一个成员中。
本源码包内暂不包含可直接显示的源代码文件,请下载源码包。