01_STM32_Freertos_基础篇
本文最后更新于 2025-06-10,学习久了要注意休息哟
第一章 FreeRTOS 介绍
1.1 裸机开发与操作系统介绍
1.1.1 裸机开发
裸机开发(Bare-metal)指的是不依赖任何操作系统,直接编写程序运行在硬件上的一种开发方式。STM32 中我们常用的 main()
函数 + 初始化 + 无限循环结构,就是裸机开发的典型代表。
1、特点
- 程序结构单一,所有功能写在
main()
的大循环中。 - 中断服务函数承担“前台”角色,主循环作为“后台”执行。
- 延时
delay
会造成 CPU 空等,浪费资源。 - 所有功能堆叠在一个任务流中,结构臃肿,扩展困难。
2、举例
玩游戏和回复消息不能同时进行,必须先完成一项才能做另一项 —— 这是裸机的本质:“轮流执行”
1.1.2 操作系统
操作系统(Operating System, OS)是一个系统控制程序,它作为应用程序和硬件之间的桥梁,主要负责资源的协调与调度。
一个完整的计算系统包含:
- 硬件层:CPU、内存、I/O 外设等设备,提供基础的运行资源
- 操作系统层:负责资源分配、中断响应、任务调度,是连接硬件与应用的中间层
- 应用层:由用户编写的程序,通过操作系统提供的接口访问硬件,实现各种功能
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、特点
- 实时性强:系统必须在设定的时间范围内响应事件
- 抢占式调度:高优先级任务能立即打断低优先级任务
- 任务分离:每个功能作为独立任务运行,便于维护与扩展
- 小巧高效:代码体积小,占用资源低,适合单片机
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 程序的运行一般包括以下几个关键步骤:
- 硬件初始化
- 初始化时钟、串口、外设等
- 任务创建
- 使用
xTaskCreate()
创建多个任务,每个任务都有自己的入口函数 - 启动调度器
- 调用
vTaskStartScheduler()
启动任务调度器,系统正式进入多任务运行状态 - 任务调度与运行
- 调度器根据优先级、就绪状态、延时等因素动态切换任务
- 空闲任务
- 当没有任何任务可运行时,系统会自动运行空闲任务(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 多任务的实际运行
- 上半图(感知):多个任务似乎同时运行,没有中断,也没有阻塞
- 下半图(实际):每个时间片只运行一个任务,调度器在任务之间快速切换
总结:只要任务切换速度足够快,用户就难以察觉切换,从而认为“多个任务在同时运行”。
使用多任务操作系统,可以显著提升软件架构的清晰度与可维护性:
- 分而治之:复杂应用可拆分为多个小任务,代码更清晰、功能更独立
- 简化控制逻辑:由 RTOS 自动调度,减少人为排队、等待、判断等代码
- 提高效率与响应:抢占式调度可优先执行重要任务,响应更快
- 便于协作开发与调试:每个任务模块独立,利于多人分工与模块测试
- 可复用性强:标准化任务结构便于代码移植和功能模块复用
多任务不是同时执行,而是“看起来像”同时执行。调度器负责在多个任务之间快速切换,实现高效的并发模拟。
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 钩子函数
- 感谢你赐予我前进的力量