FreeRTOS学习(三十六)——FreeRTOS移植与调度原理

上一节通过系统架构综合项目,把任务、队列以及任务优先级真正组合到了一个完整程序中。

到这里,我们已经会“使用”FreeRTOS了。

但是还有一个很重要的问题:

FreeRTOS为什么能让多个任务轮流运行?

STM32F407明明只有一个Cortex-M4内核,却可以表现得像:

Task1在运行

Task2也在运行

Task3也在运行

实际上CPU同一时刻只能执行一个任务。FreeRTOS只是通过快速保存和恢复任务运行现场,让CPU不断在多个任务之间切换。

本节学习目标:初步理解FreeRTOS在STM32F407上的移植结构,以及SysTick、PendSV和SVC在任务调度中的作用。不深入分析汇编源码,先建立整体认识。


一、什么叫FreeRTOS移植

FreeRTOS可以运行在:

Cortex-M0

Cortex-M3

Cortex-M4

Cortex-M7

RISC-V
……

但是不同CPU:

寄存器不同

中断机制不同

栈结构不同

任务切换方法不同

所以FreeRTOS不可能只靠完全相同的一份底层代码运行在所有处理器上。

因此FreeRTOS把:

通用功能

和:

处理器相关功能

进行了分离。

例如:

tasks.c
queue.c
list.c

主要属于FreeRTOS通用内核代码。

而STM32F407使用Cortex-M4,因此还需要对应Cortex-M4的:

port.c

portmacro.h

这部分就是:

FreeRTOS移植层。

二、port.c有什么作用

前面学习BaseType_t时,我们已经接触过:

portmacro.h

STM32F407对应的移植层会处理很多和Cortex-M4直接相关的内容。

例如:

任务栈初始化

中断优先级处理

临界区

启动第一个任务

任务上下文切换

所以可以简单理解:

FreeRTOS内核
      ↓
   port.c
      ↓
Cortex-M4硬件

FreeRTOS内核负责决定:

下一个应该运行哪个任务

而移植层负责解决:

在Cortex-M4上到底怎样完成切换。

三、什么是任务切换

假设现在有:

Task1

Task2

CPU当前正在运行Task1:

CPU
 ↓
Task1

Task1运行到:

vTaskDelay(
    pdMS_TO_TICKS(100)
);

Task1需要阻塞。

FreeRTOS于是选择Task2:

Task1
 ↓
保存运行现场
 ↓
选择Task2
 ↓
恢复Task2运行现场
 ↓
Task2继续运行

这里所谓的:

运行现场

可以简单理解为任务当前的:

CPU寄存器

栈指针

程序执行位置

等信息。

这个过程就叫:

上下文切换

也就是Context Switch。

四、为什么任务都有自己的栈

前面学习任务栈时,我们知道每个任务都有自己的栈。

现在就能进一步理解原因了。

假设:

Task1 Stack

Task2 Stack

Task1被切换出去时:

Task1当前运行状态
        ↓
保存到Task1自己的栈

以后重新运行Task1:

Task1 Stack
        ↓
恢复CPU状态
        ↓
Task1继续执行

所以任务切换并不是:

Task1重新从函数开头运行。

而是:

从之前停止的位置继续运行。

这就是为什么:

void Task1(void *pvParameters)
{
    while(1)
    {
        LED1_Toggle();

        vTaskDelay(
            pdMS_TO_TICKS(500)
        );
    }
}

每次Task1重新获得CPU以后,可以继续正常执行。

五、SysTick负责什么

STM32F407的Cortex-M4内核提供:

SysTick

FreeRTOS通常可以利用它产生系统节拍。

假设:

#define configTICK_RATE_HZ 1000

那么:

每1ms产生一次Tick

系统Tick到来以后,FreeRTOS需要处理:

任务延时时间

阻塞时间

软件定时器相关时间

是否有任务需要解除阻塞

例如Task1:

vTaskDelay(
    pdMS_TO_TICKS(100)
);

可以简单理解成:

Task1阻塞
    ↓
Tick不断计时
    ↓
100ms到达
    ↓
Task1重新进入Ready

所以SysTick主要解决:

FreeRTOS的时间节拍问题。

六、PendSV负责什么

真正进行任务上下文切换时,Cortex-M系列中一个非常重要的异常就是:

PendSV

可以简单记成:

SysTick
负责时间

PendSV
负责任务切换

例如:

SysTick到来
    ↓
发现高优先级Task2已经到时间
    ↓
需要进行任务切换
    ↓
触发PendSV
    ↓
保存Task1现场
    ↓
恢复Task2现场
    ↓
Task2运行

FreeRTOS通常会把PendSV设置成很低的中断优先级。

这样做的目的之一就是:

尽量等其他需要及时处理的中断结束

↓

再进行任务上下文切换。

七、SVC又是干什么的

除了:

SysTick

PendSV

还经常会看到:

SVC

也就是:

Supervisor Call

在Cortex-M的FreeRTOS移植中,它与启动调度过程有关。

例如程序执行:

vTaskStartScheduler();

以后,FreeRTOS最终需要:

启动第一个任务。

可以简单理解成:

vTaskStartScheduler()
        ↓
配置调度相关硬件
        ↓
SVC参与启动第一个任务
        ↓
恢复任务现场
        ↓
第一个任务开始运行

初学阶段不用深入SVC汇编代码,只需要先记住:

SVC

和FreeRTOS启动第一个任务有关。

八、三个异常怎么记

学习到这里最容易混淆的就是:

SysTick

PendSV

SVC

可以先这样记:

SysTick
   ↓
提供系统时间节拍
PendSV
   ↓
完成任务上下文切换
SVC
   ↓
参与启动第一个任务

整个过程可以粗略理解为:

vTaskStartScheduler()
        ↓
SVC
        ↓
第一个任务运行
        ↓
SysTick周期到来
        ↓
FreeRTOS进行时间管理
        ↓
发现需要切换任务
        ↓
PendSV
        ↓
保存当前任务
        ↓
恢复下一个任务

掌握这条主线就足够了。

九、为什么我们平时感觉不到任务切换

假设:

Task1运行

↓

切换Task2

↓

切换Task3

↓

再次Task1

这个过程非常快。

所以从人的观察角度来看:

LED1在闪

LED2也在闪

串口还在发送数据

看起来好像:

所有任务同时运行。

实际上对于STM32F407这种单核MCU:

同一时刻

只有一个任务真正占用CPU。

FreeRTOS只是不断进行:

保存现场

↓

切换任务

↓

恢复现场

从而实现我们看到的多任务运行效果。

十、总结

这一节初步学习了FreeRTOS移植与任务调度原理

FreeRTOS能够运行在STM32F407上,除了通用内核:

tasks.c

queue.c

list.c

还需要与Cortex-M4相关的:

port.c

portmacro.h

任务切换的核心过程:

当前任务运行
        ↓
保存任务现场
        ↓
选择下一个任务
        ↓
恢复任务现场
        ↓
新任务运行

而STM32F407上的三个重要异常可以先记成:

SysTick
    ↓
系统时间节拍

PendSV
    ↓
任务上下文切换

SVC
    ↓
参与启动第一个任务

因此FreeRTOS所谓的:

多任务同时运行

对于单核STM32来说,本质上其实是:

CPU在多个任务之间快速切换。

到这里,我们不仅会使用:

xTaskCreate();

vTaskDelay();

xQueueSend();

xSemaphoreTake();

也开始知道这些API背后最终是怎样依靠Cortex-M4硬件完成任务调度的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值