FreeRTOS的学习系列文章目录
FreeRTOS的学习(一)——STM32上的移植问题
FreeRTOS的学习(二)——任务优先级问题
FreeRTOS的学习(三)——中断机制
FreeRTOS的学习(四)——列表
FreeRTOS的学习(五)——系统延时
FreeRTOS的学习(六)——系统时钟
FreeRTOS的学习(七)——1.队列概念
FreeRTOS的学习(七)——2.队列入队源码分析
FreeRTOS的学习(七)——3.队列出队源码分析
FreeRTOS的学习(八)——1.二值信号量
FreeRTOS的学习(八)——2.计数型信号量
FreeRTOS的学习(八)——3.优先级翻转问题
FreeRTOS的学习(八)——4.互斥信号量
FreeRTOS的学习(九)——软件定时器
FreeRTOS的学习(十)——事件标志组
FreeRTOS的学习(十一)——任务通知
前言
本文将分析阐述FreeRTOS的任务是如何开始调度。关于任务调度的内容由于涉及了很多底层的东西,所以相对晦涩难懂,尤其是很多的汇编语言以及地址,寄存器等等,记忆起来非常混乱。目前打算优先梳理逻辑过程,暂时忽视细节内容。
1 调度器开启
任务调度器在start_task任务创建后就会开启,也就是调用vTaskStartScheduler函数,其功能就是开启任务调度器。
vTaskStartScheduler内部步骤主要如下:
- 创建空闲任务,由于FreeRTOS一直是有任务在运行的,所以在没有用户要求的任务时,会调用名为“IDLE”的空闲任务,空闲任务并不是啥都不干,其存在也会进行如下操作:
1.1. 判断系统是否有任务删除,如果有的话就在空闲任务中释放被删除任务的任务堆栈和任务控制块的内存。这里值得注意的是,只有某个任务要调用函数 vTaskDelete()删除自身时,需要在空闲任务中释放掉,如果删除的是别的任务那么相应的内存就会被直接释放掉,不需要在空闲任务中释放。因此在上述特殊情况下必须给予空闲任务运行的时间。
1.2.运行用户设置的空闲任务钩子函数。
1.3.判断是否开启低功耗 tickless 模式,如果开启的话还需要做相应的处理 - 如果使能了软件定时器,创建软件定时器任务,其功能代码如下:
#if ( configUSE_TIMERS == 1 )
{
if( xReturn == pdPASS )
{
xReturn = xTimerCreateTimerTask();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
- 关中断。为了保证软件定时器在xPortStartScheduler调用之前和调用期间开始计数,也就是第一个任务开始运行前,是不能计数的。
portDISABLE_INTERRUPTS();
- 初始化一些静态全局变量:
xNextTaskUnblockTime = portMAX_DELAY; //因为还不知道下面有什么任务要从阻塞出来,所以xNextTaskUnblockTime设置为最大值
xSchedulerRunning = pdTRUE; //判断调度器是否在运行
xTickCount = ( TickType_t ) configINITIAL_TICK_COUNT; //初始化为configINITIAL_TICK_COUNT(0)
- 调用函数 xPortStartScheduler来初始化跟调度器启动有关的硬件,比如滴答定时器、FPU单元(M4才有)和 PendSV中断等等。
5.1. 设置PendSV和Systick中断优先级
5.2. 初始化滴答定时器,中断周期,中断使能,systcik使能
5.3. 如果MCUu有 FPU的话,开启FPU
5.4. 如果使用FPU的话,开启惰性压栈
5.5. prvStartFirstTask开启第一个任务
5.6. 在prvStartFirstTask函数中最终通过SVC 0指令引起SVC中断,SVC 也叫做请求管理调用。在 FreeRTOS 中仅仅使用 SVC异常来启动第一个任务,后面的程序中就再也用不到 SVC了。
SVC 中断服务函数应该为 SVC_Handler
#define vPortSVCHandler SVC_Handler
函数具体内容如下:
//SVC中断函数,开启中断并恢复现场
__asm void vPortSVCHandler( void )
{
/* *INDENT-OFF* */
PRESERVE8
ldr r3, = pxCurrentTCB //R3=pxCurrentTCB的地址
ldr r1, [ r3 ] //取 R3 所保存的地址处的值赋给 R1
ldr r0, [ r1 ] //取 R1 所保存的地址处的值赋给 R0
ldmia r0 !, { r4 - r11 } //出栈 ,R4~R11 和R14
msr psp, r0 //进程栈指针 PSP 设置为任务的堆栈
isb //指令同步屏障
mov r0, # 0 //R0=0
msr basepri, r0 //寄存器 basepri=0,开启中断
orr r14, # 0xd
bx r14
}