FreeRTOS的学习(十二)——任务调度器的开启

本文深入解析FreeRTOS的任务调度启动过程,包括vTaskStartScheduler函数的内部步骤,如创建空闲任务、开启调度器、管理中断、初始化硬件以及利用SVC中断启动第一个任务。同时,介绍了空闲任务在系统中的角色,如释放被删除任务的资源和运行用户定义的空闲任务钩子函数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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内部步骤主要如下:

  1. 创建空闲任务,由于FreeRTOS一直是有任务在运行的,所以在没有用户要求的任务时,会调用名为“IDLE”的空闲任务,空闲任务并不是啥都不干,其存在也会进行如下操作:
    1.1. 判断系统是否有任务删除,如果有的话就在空闲任务中释放被删除任务的任务堆栈和任务控制块的内存。这里值得注意的是,只有某个任务要调用函数 vTaskDelete()删除自身时,需要在空闲任务中释放掉,如果删除的是别的任务那么相应的内存就会被直接释放掉,不需要在空闲任务中释放。因此在上述特殊情况下必须给予空闲任务运行的时间。
    1.2.运行用户设置的空闲任务钩子函数
    1.3.判断是否开启低功耗 tickless 模式,如果开启的话还需要做相应的处理
  2. 如果使能了软件定时器,创建软件定时器任务,其功能代码如下:
#if ( configUSE_TIMERS == 1 )
    {
        if( xReturn == pdPASS )
        {
            xReturn = xTimerCreateTimerTask();
        }
        else
        {
            mtCOVERAGE_TEST_MARKER();
        }
    }
  1. 关中断。为了保证软件定时器在xPortStartScheduler调用之前和调用期间开始计数,也就是第一个任务开始运行前,是不能计数的。
portDISABLE_INTERRUPTS();
  1. 初始化一些静态全局变量:
xNextTaskUnblockTime = portMAX_DELAY;       			//因为还不知道下面有什么任务要从阻塞出来,所以xNextTaskUnblockTime设置为最大值
xSchedulerRunning = pdTRUE;                 			//判断调度器是否在运行
xTickCount = ( TickType_t ) configINITIAL_TICK_COUNT;	//初始化为configINITIAL_TICK_COUNT(0)
  1. 调用函数 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
}
### FreeRTOS任务调度工作机制 #### 任务调度概述 FreeRTOS中的任务调度由操作系统核心完成,主要职责是从就绪列表中挑选最高优先级的任务来执行[^3]。 #### 数据结构与准备阶段 为了支持任务的创建、管理和调度,FreeRTOS定义了几种重要的数据结构用于表示任务及其状态。这些数据结构共同维护了一个全局的就绪链表,它是任务调度的基础之一[^2]。 #### 调度算法 默认情况下,FreeRTOS采用的是固定优先级抢占式的调度策略。这意味着每个任务都被赋予一个固定的优先级,在运行过程中除非发生特定情况(如资源锁定导致的优先级继承),否则此优先级保持不变。当有更高优先级的任务变为可执行状态时,当前正在运行较低优先级的任务会被立即打断并挂起等待重新获得CPU使用权;对于相同优先级下的多个任务,则采取轮转法分配处理时间[^4]。 #### 启动流程 在系统初始化期间,FreeRTOS会先建立好必要的环境,包括但不限于: - 创建至少一个空闲任务作为最低优先级的存在; - 配置Systick定时以便提供周期性的节拍信号触发上下文切换检查; - 设置处理模式下使用的主堆栈指针(MSP),并通过加载向量表基址完成异常响应路径设定; - 开启中断允许标志位以及使能各类硬件中断源; 最后通过发起一次SVCall软中断请求进入特权态服务例程从而正式开启多任务并发执行模型——即从所有处于就绪态的任务集合里选取最合适的那个开始真正意义上的“工作”。 ```cpp // 示例代码展示如何配置SysTick定时以驱动tick计数 void vConfigureTimerForRunTimeStats(void){ SysTick->LOAD = configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ; SysTick->VAL = (0UL); SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | SysTick_CTRL_TICKINT_Msk | SysTick_CTRL_ENABLE_Msk; } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

LEODWL

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值