注:在使用大多数功能时,FreeRTOS都要将对应的宏置为1,具体的需要查看FreeRTOS官方文档。
任务堆栈相关寄存器如下,
启动第一个任务
FreeRTOS中启动第一个任务的流程总结如下,
- 启动任务调度器vTaskStartScheduler()
- 在该函数中会创建空闲任务prvIdleTask和软件定时器任务xTimerCreateTimerTask(可选)
- 关闭中断,主要是为了防止其他中断打断第一个任务的启动
- 初始化一些全局变量
- 初始化任务运行时间统计功能(这个功能详解可以看后续文章)
- 调用函数启动任务调度器xPortStartScheduler()
xPortStartScheduler()的流程如下,
- 配置PendSV和SysTick的中断优先级位最低优先级
- 调用函数vPortSetupTimerInterrupt()配置SysTick
- 调用函数使能FPU(Cortex_M3内核没有)
- 调用prvStartFirstTask()函数启动第一个任务,该函数的流程为:首先复位MSP指针(后面会介绍)的初始值并使能中断,然后触发SVC中断。在SVC中断中,首先获取任务优先级最高的任务控制块pxCurrentTCB并将该任务的寄存器值出栈到CPU寄存器中,然后设置PSP指针(后面会讲),最后返回r14寄存器执行第一个任务。
MSP和PSP指针介绍
MSP(主堆栈指针):由OS内核,异常服务例程及需要特权访问的应用程序使用,在FreeRTOS中用于中断以内使用。
PSP(进程堆栈指针):用于常规的应用程序代码,在FreeRTOS中用于中断以外使用。
SVC中断服务函数介绍
任务切换
任务切换的所有流程都是在PendSV中断中进行,触发PendSV中断有两个方式,一是滴答定时器中断触发,二是API函数portYIELD()触发。FreeRTOS中其他API函数的PendSV中断触发本质都是使用portYIELD(),该函数内部就是将ICSR寄存器的bit28进行置1的操作(详细看Cortex_M3的手册)。
任务切换流程如下,
建议在学习的朋友对照源码一起学习,效果更加。
时间片调度
时间片调度针对的是同等优先级的任务,让它们可以轮流的享有相同的CPU时间,这个时间可以通过设置滴答定时器的频率来设置。在FreeRTOS中,一个时间片等于SysTick中断周期。
备注:
- 同等优先级任务轮流执行,时间片流转
- 一个时间片大小,取决于滴答定时器的中断频率
- 注意没有用完的时间片不会再使用,下次任务依然按照一个时间片执行