本文最后更新于 2025-06-10,学习久了要注意休息哟

第一章 FreeRTOS 介绍

1.1 裸机开发与操作系统介绍

1.1.1 裸机开发

裸机开发(Bare-metal)指的是不依赖任何操作系统,直接编写程序运行在硬件上的一种开发方式。STM32 中我们常用的 main() 函数 + 初始化 + 无限循环结构,就是裸机开发的典型代表。

1、特点

  • 程序结构单一,所有功能写在 main() 的大循环中。
  • 中断服务函数承担“前台”角色,主循环作为“后台”执行。
  • 延时 delay 会造成 CPU 空等,浪费资源。
  • 所有功能堆叠在一个任务流中,结构臃肿,扩展困难。

2、举例

玩游戏和回复消息不能同时进行,必须先完成一项才能做另一项 —— 这是裸机的本质:“轮流执行”

1.1.2 操作系统

操作系统(Operating System, OS)是一个系统控制程序,它作为应用程序和硬件之间的桥梁,主要负责资源的协调与调度。

一个完整的计算系统包含:

  1. 硬件层:CPU、内存、I/O 外设等设备,提供基础的运行资源
  2. 操作系统层:负责资源分配、中断响应、任务调度,是连接硬件与应用的中间层
  3. 应用层:由用户编写的程序,通过操作系统提供的接口访问硬件,实现各种功能

1、特点

  • 操作系统支持 多任务并发执行,每个任务独立运行,由调度器控制切换
  • 提供 任务管理、内存管理、文件系统、驱动支持等功能
  • 避免了资源冲突,提升了系统的可扩展性和稳定性
  • 支持多种调度算法(如抢占式、时间片轮转)实现高效资源利用

2、举例

玩游戏的同时还能及时收到微信消息、后台下载文件 —— 这些就是操作系统带来的 “多任务调度与并发能力”

操作系统就像一个“管理者”,它将 CPU 资源公平地分配给多个任务,每个任务只关注自己的逻辑,系统整体运行更加高效与可维护。

1.1.3 区别对比

| 对比维度 | 裸机开发 | 操作系统开发(如 RTOS) |
|———-|———-|—————————|
| 编程方式 | 顺序流程 + 中断响应 | 多任务并发调度 |
| 延时机制 | delay 空等 | 自动释放 CPU 给其他任务 |
| 多任务支持 | 无,需要人为管理 | 原生支持多个任务 |
| 实时性保障 | 靠开发者手动实现 | 有抢占式调度机制 |
| CPU 利用率 | 低,容易浪费 | 高,资源合理分配 |
| 系统结构 | 所有功能都写在循环里 | 功能拆分为多个任务,独立调度 |
| 适用范围 | 简单项目、资源极少 | 实时控制、多任务复杂应用 |

1.2 实时操作系统与通用操作系统

操作系统从应用领域与特性上可以大致分为两类:

  • 通用操作系统(General Purpose OS)
  • 实时操作系统(Real-Time OS,简称 RTOS)

1.2.1 通用操作系统

通用操作系统(如 Windows、Linux、macOS)面向的是桌面、服务器、办公等通用计算场景,它们的特点是功能强大、资源管理能力丰富,但不强调时间上的“确定性”

1、特点

  • 提供完整的图形界面、多用户支持、丰富的驱动程序
  • 重视系统的吞吐量与用户体验
  • 任务调度采用时间片轮转优先级+公平调度
  • 当系统负载高时,某些任务响应可能延迟(不适合硬实时场景)

2、场景

  • 台式电脑、笔记本电脑
  • 网站服务器
  • 办公软件运行环境(Word、Photoshop 等)

1.2.2 实时操作系统

实时操作系统强调的是任务在规定时间内完成,即任务具有明确的时间约束。RTOS 常用于工业控制、智能硬件、汽车电子等对响应时间有严格要求的系统中。

1、特点

  1. 实时性强:系统必须在设定的时间范围内响应事件
  2. 抢占式调度:高优先级任务能立即打断低优先级任务
  3. 任务分离:每个功能作为独立任务运行,便于维护与扩展
  4. 小巧高效:代码体积小,占用资源低,适合单片机

2、示例

  • 工业自动化控制(如 PLC 系统)
  • 医疗设备控制系统(如心电监测仪)
  • 航空航天(如姿态控制系统)
  • 智能硬件(如智能音箱、扫地机器人)
  • 汽车电子(如自动刹车系统、气囊控制器)

1.3 FreeRTOS 实时操作系统

FreeRTOS 是嵌入式领域中应用最广泛的轻量级实时操作系统之一,由 Richard Barry 创建,现由亚马逊(Amazon)维护。它被广泛应用于 ARM Cortex-M、AVR、RISC-V 等嵌入式平台上。

1.3.1 FreeRTOS 系统简介

FreeRTOS(Free Real-Time Operating System)是一个开源的、可移植的、裁剪灵活的嵌入式实时操作系统内核,遵循 MIT 开源协议,免费商用。

  • 官网地址:https://www.freertos.org
  • 开源协议:MIT License(商业可用,限制少)
  • 支持平台:ARM Cortex-M、RISC-V、AVR、MSP430、x86 等多种架构
  • 典型内核大小:约 10KB(功能裁剪后)

1.3.2 FreeRTOS 系统特点

FreeRTOS 作为一个轻量级 RTOS,具备以下核心优势:

| 特性 | 描述 |
| ——- | —————————— |
| 小巧高效 | 代码精简,适合资源受限的单片机环境,内核约 10KB |
| 支持多任务 | 每个任务独立运行,可设定优先级,互不干扰 |
| 抢占式调度 | 高优先级任务可以抢占低优先级任务,保障实时性 |
| 丰富的同步机制 | 支持信号量、互斥锁、消息队列、事件组、软件定时器等 |
| 移植性强 | 提供标准接口 + 多平台支持,便于移植到任意 MCU 架构上 |
| 社区活跃 | 亚马逊维护,有大量开源项目和技术文档 |

1.3.3 FreeRTOS 应用场景

FreeRTOS 适合用于所有需要多任务调度与实时控制的嵌入式场景,例如:

  • 工业自动化控制:多任务并发控制生产线流程、设备调度等
  • 智能家居设备:语音识别 + 网络通信 + 控制模块同时运行
  • 车载系统:如仪表盘、自动灯光系统、辅助驾驶控制
  • 医疗设备:心电监护仪、生命体征采集等需高可靠性系统
  • 物联网终端:LoRa、NB-IoT、Wi-Fi 终端设备的数据采集与上传

第二章 FreeRTOS 基础

2.1 运行机制

FreeRTOS 的运行机制与传统裸机开发最大区别在于:程序不是从 main() 一直跑到底,而是由“任务调度器”管理多个任务的执行顺序。

2.3.1 基本执行流程

FreeRTOS 程序的运行一般包括以下几个关键步骤:

  1. 硬件初始化
  2. 初始化时钟、串口、外设等
  3. 任务创建
  4. 使用 xTaskCreate() 创建多个任务,每个任务都有自己的入口函数
  5. 启动调度器
  6. 调用 vTaskStartScheduler() 启动任务调度器,系统正式进入多任务运行状态
  7. 任务调度与运行
  8. 调度器根据优先级、就绪状态、延时等因素动态切换任务
  9. 空闲任务
  10. 当没有任何任务可运行时,系统会自动运行空闲任务(Idle Task)

2.3.2 操作系统与裸机的差异

| 项目 | 裸机程序 | FreeRTOS 程序 |
| —– | —————– | ——————– |
| 程序入口 | main() | main() 中创建任务 + 启动调度器 |
| 执行方式 | 顺序执行,死循环 | 多个任务同时存在,调度器切换执行 |
| 多任务支持 | 无 | 有 |
| 延时处理 | delay 造成阻塞,浪费 CPU | vTaskDelay 不会阻塞 CPU |
| 资源管理 | 手动管理,代码混乱 | 分模块开发,逻辑清晰 |

2.2.3 运行中的关键概念

  • 任务切换(Context Switch)
    调度器根据任务状态、优先级等条件自动切换任务运行,保存/恢复 CPU 上下文信息

  • 任务状态转换
    每个任务可能处于:就绪(Ready)、运行(Running)、阻塞(Blocked)、挂起(Suspended)等状态之间切换

  • 中断与调度的关系
    中断发生时可以打断当前任务,进入 ISR(中断服务函数),中断后可能触发调度器切换任务

2.2 系统时基

在 FreeRTOS 中,所有任务的延时、调度、超时管理等“时间相关行为”都依赖于一个统一的时间基准 —— 系统节拍(System Tick)

2.2.1 系统Tick

系统 Tick 是由硬件定时器(如 SysTick)周期性产生的一个 时间中断信号,FreeRTOS 借此实现时间推进和任务切换。

每发生一次 Tick 中断,FreeRTOS 就会:

  • 更新内部时间计数器(Tick Count)
  • 判断是否有延时任务需要唤醒
  • 决定是否需要切换任务

2.2.2 节拍配置

系统 Tick 的频率通过配置宏 configTICK_RATE_HZ 设置,单位为 Hz:

#define configTICK_RATE_HZ 1000  // 表示 1 秒发生 1000 次 Tick 中断

| 值 | 每个 Tick 间隔 | 说明 |
| —- | ———- | ———- |
| 1000 | 1 ms | 精度高,常用 |
| 500 | 2 ms | 精度中,节省资源 |
| 100 | 10 ms | 精度低,适合简单项目 |

节拍频率越高,系统中断越频繁,时间精度更高,但 CPU 占用也更高。

2.2.3 时钟来源

STM32 通常使用 SysTick 定时器 作为系统 Tick 源。

初始化方式如下:

SysTick_Config(SystemCoreClock / configTICK_RATE_HZ);
  • SystemCoreClock:系统主频(如 168MHz)

  • configTICK_RATE_HZ:设定 Tick 频率(如 1000)

该配置表示每隔 1ms 触发一次中断。

2.2.4 任务延时

所有基于时间的任务操作,都依赖系统 Tick 计数器:

vTaskDelay(100);  // 当前任务延时 100 个 Tick
  • 系统每次 Tick 递减延时计数

  • 到期后,任务被置为就绪状态

  • 下一次调度器判断是否切换任务

如果 configTICK_RATE_HZ = 1000,表示每 1ms 一个 Tick
当你调用 vTaskDelay(500),即让当前任务延时 500ms
在这段时间内,调度器将运行其他任务,直到当前任务“醒来”重新加入调度

2.3 多任务系统

FreeRTOS 的核心能力就是支持多任务(Multitasking)运行。每个任务(Task)本质上就是一个具有独立执行入口、独立堆栈空间的函数,它们由调度器轮流占用 CPU 时间,从而实现“伪并行”运行。

2.3.1 多任务概念

任务是一个可以独立运行的程序片段,每个任务具有:

  • 独立的函数入口
  • 自己的运行堆栈
  • 独立的运行状态与优先级

任务可以随时被挂起、中断、就绪、运行,这种状态的流转由 调度器 控制。

2.3.2 创建任务的基本方式

在 FreeRTOS 中,使用 xTaskCreate() 函数创建任务:

xTaskCreate(
    TaskFunction,     // 任务函数入口
    "TaskName",       // 任务名称(调试用)
    StackSize,        // 堆栈大小(单位:字)
    pvParameters,     // 传入任务的参数
    Priority,         // 任务优先级(数值越大优先级越高)
    &TaskHandle       // 任务句柄(可选)
);

任务创建后,系统不会立即执行,而是等待调用 vTaskStartScheduler() 启动调度器后,由调度器安排运行。

2.3.3 多任务的实际运行

  • 上半图(感知):多个任务似乎同时运行,没有中断,也没有阻塞
  • 下半图(实际):每个时间片只运行一个任务,调度器在任务之间快速切换

总结:只要任务切换速度足够快,用户就难以察觉切换,从而认为“多个任务在同时运行”。

使用多任务操作系统,可以显著提升软件架构的清晰度与可维护性:

  1. 分而治之:复杂应用可拆分为多个小任务,代码更清晰、功能更独立
  2. 简化控制逻辑:由 RTOS 自动调度,减少人为排队、等待、判断等代码
  3. 提高效率与响应:抢占式调度可优先执行重要任务,响应更快
  4. 便于协作开发与调试:每个任务模块独立,利于多人分工与模块测试
  5. 可复用性强:标准化任务结构便于代码移植和功能模块复用

多任务不是同时执行,而是“看起来像”同时执行。调度器负责在多个任务之间快速切换,实现高效的并发模拟。

2.4 任务调度

FreeRTOS 内核最核心的功能之一就是任务调度 —— 决定在某个时刻,哪个任务可以占用 CPU 执行。

任务调度器会根据任务的状态和优先级来进行切换,使得系统能在多个任务之间进行高效调度,从而模拟“并发”运行。

2.4.1 任务调度概述

FreeRTOS 支持以下三种调度方式:

| 调度方式 | 说明 |
| —– | ———————————– |
| 抢占式调度 | 高优先级任务可以随时打断低优先级任务执行 |
| 时间片调度 | 同优先级任务轮流执行,每个任务分配一个时间片 |
| 协程式调度 | 不推荐使用,由用户手动切换任务,FreeRTOS 官方已不再维护该方式 |

当前版本推荐使用 抢占式调度 + 时间片轮转调度,默认在 FreeRTOS 中均已支持。

2.4.2 抢占式调度

抢占式调度是指:高优先级任务随时可以打断低优先级任务的运行,实现“谁重要谁先执行”的调度策略。

  • 优先级越高的任务拥有更高的执行权

  • 一旦有更高优先级的任务变为就绪态,调度器立即中断当前任务并切换

  • 被抢占的任务会自动保存上下文,稍后恢复继续运行

  • Task1(优先级 1)先运行,随后被 Task2(优先级 2)抢占
  • Task2 被 Task3(优先级 3)进一步抢占
  • Task3 运行过程中,被 NVIC 硬件中断打断(中断优先级 > 任务)
  • 中断处理完毕后恢复 Task3 执行
  • Task3 执行结束或阻塞后,调度器切换回 Task2 继续执行

2.4.3 时间片调度

时间片调度是指:当多个任务拥有相同优先级时,调度器按顺序分配 CPU 时间给它们轮流运行。

每个任务获得的 CPU 时间称为“一个时间片”,它的长度由节拍定时器决定(通常是一个 Tick)。

  • 所有同级任务轮流运行,时间公平

  • 每个时间片到达时,调度器切换到下一个任务

  • 时间片浪费不会补发,即:如果某任务提前阻塞,下一个任务会直接开始,不再等待补齐时间

  • Task1 开始运行一个时间片
  • Task2 在运行过程中发生“阻塞”(如等待信号量、延时等)→ 主动让出 CPU
  • 任务轮转到 Task3 执行
  • Task3 执行期间出现 NVIC 硬件中断 → 中断打断 Task3
  • 中断服务完成后,系统恢复 Task3 继续执行(如果未阻塞)
  • 时间片结束后系统自动切换到 Task1,然后再到 Task2

2.4.3 优先级管理

在 FreeRTOS 中,每个任务都拥有一个“优先级”属性,范围通常为 0 ~ configMAX_PRIORITIES - 1,值越大,优先级越高。

此外,硬件中断是可以随时打断系统任务。

2.5 任务状态

FreeRTOS中任务共存在4种状态:

1、运行态:正在执行的任务,该任务就处于运行态,注意在STM32中,同一时间仅一个任务处于运行态
2、就绪态:如果该任务已经能够被执行,但当前还未被执行,那么该任务处于就绪态
3、阻塞态:如果一个任务因延时或等待外部事件发生,那么这个任务就处于阻塞态
4、挂起态:类似暂停,调用函数 vTaskSuspend() 进入挂起态,需要调用解挂函数vTaskResume()
才可以进入就绪态

2.5.1 状态切换

2.5.2 状态列表

这四种状态中,除了运行态,其他三种任务状态的任务都有其对应的任务状态列表

就绪列表:pxReadyTasksLists[x],其中x代表任务优先级数值
阻塞列表:pxDelayedTaskList
挂起列表:xSuspendedTaskList

在32位的硬件中,会保存一个32位的变量,代表0-31的优先级。当某个位,置一时,代表所对应的优先级就绪列表有任务存在。

结构如下图:

2.6 上下文切换

2.7 空闲任务

2.8 钩子函数