Pārlūkot izejas kodu

lint: 重构 FreeRTOS raw 笔记(11 篇旧 → 16 篇新)

- 旧 11 篇笔记替换为 16 篇,按课程 17 章 + 22 个代码项目映射
- 每篇:frontmatter + 生活类比 + 概念 + 实验代码(逐行注释) + API 速查表 + FAQ
- 图片 48→13 张精选(删除 IDE/Keil 截图)
- 代码从 22 个配套 Keil 项目 freertos_demo.c 提取
- 更新 wiki/source-freertos.md, wiki/index.md, wiki/log.md
- 设计规范: docs/superpowers/specs/2026-07-17-*-ingest-design.md
- 实现计划: docs/superpowers/plans/2026-07-17-*-ingest.md
OpenCode 9 stundas atpakaļ
vecāks
revīzija
eca3dd020c
41 mainītis faili ar 4990 papildinājumiem un 1906 dzēšanām
  1. 49 42
      X-Knowledge-Base/raw/Joplin/嵌入式+Linux/FreeRTOS学习笔记/01-实时操作系统基础.md
  2. 108 48
      X-Knowledge-Base/raw/Joplin/嵌入式+Linux/FreeRTOS学习笔记/02-任务调度与状态管理.md
  3. 115 206
      X-Knowledge-Base/raw/Joplin/嵌入式+Linux/FreeRTOS学习笔记/03-FreeRTOS移植指南.md
  4. 0 66
      X-Knowledge-Base/raw/Joplin/嵌入式+Linux/FreeRTOS学习笔记/04-数据类型与命名规范.md
  5. 209 0
      X-Knowledge-Base/raw/Joplin/嵌入式+Linux/FreeRTOS学习笔记/04-数据类型命名与配置详解.md
  6. 374 0
      X-Knowledge-Base/raw/Joplin/嵌入式+Linux/FreeRTOS学习笔记/05-任务创建与删除.md
  7. 0 309
      X-Knowledge-Base/raw/Joplin/嵌入式+Linux/FreeRTOS学习笔记/05-任务创建删除与挂起恢复.md
  8. 0 219
      X-Knowledge-Base/raw/Joplin/嵌入式+Linux/FreeRTOS学习笔记/06-中断管理与时间管理.md
  9. 232 0
      X-Knowledge-Base/raw/Joplin/嵌入式+Linux/FreeRTOS学习笔记/06-任务挂起与恢复.md
  10. 199 0
      X-Knowledge-Base/raw/Joplin/嵌入式+Linux/FreeRTOS学习笔记/07-中断管理与临界段.md
  11. 0 197
      X-Knowledge-Base/raw/Joplin/嵌入式+Linux/FreeRTOS学习笔记/07-消息队列与队列集.md
  12. 0 255
      X-Knowledge-Base/raw/Joplin/嵌入式+Linux/FreeRTOS学习笔记/08-信号量与互斥信号量.md
  13. 280 0
      X-Knowledge-Base/raw/Joplin/嵌入式+Linux/FreeRTOS学习笔记/08-时间片调度与延时管理.md
  14. 0 198
      X-Knowledge-Base/raw/Joplin/嵌入式+Linux/FreeRTOS学习笔记/09-事件标志组与任务通知.md
  15. 342 0
      X-Knowledge-Base/raw/Joplin/嵌入式+Linux/FreeRTOS学习笔记/09-任务状态查询与运行时统计.md
  16. 234 0
      X-Knowledge-Base/raw/Joplin/嵌入式+Linux/FreeRTOS学习笔记/10-消息队列.md
  17. 0 202
      X-Knowledge-Base/raw/Joplin/嵌入式+Linux/FreeRTOS学习笔记/10-软件定时器与Tickless低功耗.md
  18. 359 0
      X-Knowledge-Base/raw/Joplin/嵌入式+Linux/FreeRTOS学习笔记/11-信号量机制.md
  19. 0 139
      X-Knowledge-Base/raw/Joplin/嵌入式+Linux/FreeRTOS学习笔记/11-内存管理.md
  20. 205 0
      X-Knowledge-Base/raw/Joplin/嵌入式+Linux/FreeRTOS学习笔记/12-队列集.md
  21. 189 0
      X-Knowledge-Base/raw/Joplin/嵌入式+Linux/FreeRTOS学习笔记/13-事件标志组.md
  22. 224 0
      X-Knowledge-Base/raw/Joplin/嵌入式+Linux/FreeRTOS学习笔记/14-任务通知.md
  23. 359 0
      X-Knowledge-Base/raw/Joplin/嵌入式+Linux/FreeRTOS学习笔记/15-软件定时器与Tickless低功耗.md
  24. 175 0
      X-Knowledge-Base/raw/Joplin/嵌入式+Linux/FreeRTOS学习笔记/16-内存管理.md
  25. BIN
      X-Knowledge-Base/raw/Joplin/嵌入式+Linux/FreeRTOS学习笔记/assets/basepri_interrupt_management.png
  26. BIN
      X-Knowledge-Base/raw/Joplin/嵌入式+Linux/FreeRTOS学习笔记/assets/delay_relative_vs_absolute.png
  27. BIN
      X-Knowledge-Base/raw/Joplin/嵌入式+Linux/FreeRTOS学习笔记/assets/event_group_bits.png
  28. BIN
      X-Knowledge-Base/raw/Joplin/嵌入式+Linux/FreeRTOS学习笔记/assets/heap2_best_fit.png
  29. 0 0
      X-Knowledge-Base/raw/Joplin/嵌入式+Linux/FreeRTOS学习笔记/assets/heap_comparison.png
  30. 0 0
      X-Knowledge-Base/raw/Joplin/嵌入式+Linux/FreeRTOS学习笔记/assets/multitasking_concurrent_vs_actual.png
  31. BIN
      X-Knowledge-Base/raw/Joplin/嵌入式+Linux/FreeRTOS学习笔记/assets/mutex_priority_inheritance.png
  32. BIN
      X-Knowledge-Base/raw/Joplin/嵌入式+Linux/FreeRTOS学习笔记/assets/priority_inversion.png
  33. BIN
      X-Knowledge-Base/raw/Joplin/嵌入式+Linux/FreeRTOS学习笔记/assets/queue_structure.png
  34. BIN
      X-Knowledge-Base/raw/Joplin/嵌入式+Linux/FreeRTOS学习笔记/assets/stm32_low_power_modes.png
  35. 0 0
      X-Knowledge-Base/raw/Joplin/嵌入式+Linux/FreeRTOS学习笔记/assets/timer_state_diagram.png
  36. 14 12
      X-Knowledge-Base/raw/Joplin/嵌入式+Linux/STM32学习笔记/03-GPIO输出:LED控制.md
  37. 10 1
      X-Knowledge-Base/wiki/index.md
  38. 23 0
      X-Knowledge-Base/wiki/log.md
  39. 15 12
      X-Knowledge-Base/wiki/source-freertos.md
  40. 822 0
      docs/superpowers/plans/2026-07-17-freertos-ingest.md
  41. 453 0
      docs/superpowers/specs/2026-07-17-freertos-silicon-valley-ingest-design.md

+ 49 - 42
X-Knowledge-Base/raw/Joplin/嵌入式+Linux/FreeRTOS学习笔记/01-实时操作系统基础.md

@@ -1,68 +1,75 @@
 ---
 tags: [source-summary]
 type: source
-source: "尚硅谷嵌入式技术之FreeRTOS实时操作系统 V1.0.3"
+source: "尚硅谷嵌入式技术之FreeRTOS实时操作系统 V1.0.3 — 操作系统介绍 + FreeRTOS简介"
 author: "尚硅谷研究院"
-date: 2026-07-12
-url: ""
-created: 2026-07-12
+date: 2026-07-17
+created: 2026-07-17
 ---
 
 # 实时操作系统基础
 
-> 来源:尚硅谷 FreeRTOS 教程第 1 章。覆盖:裸机 vs OS、通用 OS vs RTOS、FreeRTOS 简介与发展历史
+> **用生活理解**:裸机开发像一个人做整桌菜——切菜、炒菜、看火全包,手忙脚乱;RTOS 像一个厨房团队,主厨只管炒菜,配菜工负责备料,每个人专注自己的任务,由店长(调度器)统一协调
 
-## 裸机开发 vs 操作系统
+## 操作系统介绍
 
-**裸机开发**:没有 OS 支持,直接在硬件上开发,开发者亲自管理 CPU、内存和 I/O 资源。前期学习的 STM32 单片机代码都属于裸机开发。
+### Basic concept
 
-**操作系统**:作为硬件与应用之间的桥梁,协调分配计算和内存资源给不同应用程序使用
+**裸机开发**指在没有操作系统支持的情况下直接操作硬件,开发者需要亲自管理 CPU、内存和 I/O 资源。STM32 单片机的基础开发都属于裸机开发
 
-一个计算机系统分为三部分:硬件(Hardware)、操作系统(Operating System)、应用程序(Application Programs)。
+一个计算机系统分为三层:
+- **硬件**:芯片、存储、I/O 设备,提供基础计算资源
+- **操作系统**:介于硬件与应用之间的控制程序,负责资源分配与调度
+- **应用程序**:通过 OS 提供的 API 获取资源完成任务
 
-## 通用操作系统 vs 实时操作系统
+### Bare metal vs OS development
 
-| 类型 | 代表 | 特点 |
-|------|------|------|
-| 通用 OS | Linux、Windows、macOS | 功能强大,但不保证每个程序实时响应 |
-| 实时 OS(RTOS) | FreeRTOS、uC/OS、RT-Thread、QNX | 保证任务在给定时间(Deadline)内完成 |
+裸机开发的问题在于,所有逻辑都写在 main 函数的 while(1) 大循环中。当功能增多时,代码会变得难以维护——你要手动计算每个任务的执行时序,确保 LED 闪烁的同时还能响应按键、处理传感器数据。一旦需求变化,你可能需要重写整个循环结构。
 
-**实时(Real Time)** 的含义:任务必须在给定的时间内完成。强大的计算能力并不能保证实时性——例如安全气囊需要在 30ms 内打开,如果因任务调度问题导致响应超时,将导致严重事故
+操作系统则将应用拆分为多个独立任务,每个任务只管自己的逻辑。操作系统负责"什么时候该运行哪个任务",开发者不用再操心时序问题。
 
-RTOS 能保障:
-1. 对中断和内部异常的处理
-2. 对安全相关的事件的处理
-3. 任务调度机制等
+### General OS vs RTOS
+
+**通用操作系统**(Linux、Windows、macOS)优先保障系统流畅运行,公平调度所有程序,但无法保证某个任务在确定时间内完成。它们追求的是"平均响应好",而不是"最坏情况有保证"。
+
+**实时操作系统(RTOS)** 的核心是"任务必须在给定的截止时间(Deadline)内完成"。
+
+> **安全气囊案例**:汽车碰撞后,安全气囊必须在 **30ms** 内完全打开。即使车载 ECU 有强大的 CPU 和充足的内存,如果系统在执行其他复杂计算任务时耽搁了气囊的响应,就会造成严重事故。
+>
+> 实时性不是靠堆砌硬件性能来实现的,而是靠**确定性的调度机制**。RTOS 确保关键任务在任何情况下都能在规定时间内响应。
 
 ## FreeRTOS 简介
 
-FreeRTOS 是所有实时操作系统中最受欢迎的一款。
+### Basic concept
 
-### 发展历史
-- 2003 年:由美国的 Richard Barry 发布
-- 2017 年:被亚马逊收购,改名为 AWS FreeRTOS
+FreeRTOS 是市场占有率最高的嵌入式实时操作系统,由美国的 **Richard Barry** 于 **2003 年**发布。2017 年被亚马逊收购,更名为 **AWS FreeRTOS**。
 
-### 核心优势
-- **开源免费**:MIT 许可证(注意:开源≠免费,有些开源协议只允许个人学习不允许商用,FreeRTOS MIT 两者都允许)
-- **轻量级**:适用于资源受限的嵌入式系统
-- **广泛应用**:工业自动化、医疗设备、消费电子、汽车电子
-- **多平台支持**:可移植到不同硬件平台
-- **丰富的功能**:多任务调度、任务通信、同步
+采用 MIT 开源许可,每 170 秒就有一次下载。适用于微控制器和小型微处理器,突出**可靠性和易用性**。
 
-![多任务系统:感知到的并发 vs 实际的多任务执行](assets/multitasking_perceived_vs_actual.png)
+### Working principle
 
-*单核处理器通过任务间快速切换制造"同时运行"的假象。上图:感知到的并发(所有任务同时运行),下图:实际执行(时间上串行)。*
+FreeRTOS 作为一个小型 RTOS 内核,提供以下核心功能:
 
-> **并发 vs 并行**:就像三个坑位的卫生间——3人×3坑 = **并行**(同时上),6人×3坑 = **并发**(轮流上)。单核 RTOS 只能做到并发,做不到并行。
->
-> **炒菜类比**:一位厨师(单核)炒三道菜。裸机编程 = 做完一道再做下一道。RTOS = 同时准备所有食材,然后轮流翻炒——这就是"快速切换"的并发效果。
+- **任务调度**:通过调度器管理多个任务,支持优先级抢占和时间片轮转
+- **任务通信与同步**:队列、信号量、事件组等机制确保任务间数据安全传递
+- **内存管理**:提供 heap_1 ~ heap_5 五种动态内存分配方案
+- **定时器与中断处理**:软件定时器和可靠的中断响应
+- **高可移植性**:支持多种芯片架构,移植只需替换 portable 层文件
+
+## Core API Reference Table
+
+FreeRTOS 基础 API 分类概览(本章仅作了解,后续章节详细展开):
+
+| 类别 | 主要函数 | 用途 |
+|------|---------|------|
+| 任务创建 | `xTaskCreate()` | 动态创建任务 |
+| 任务删除 | `vTaskDelete()` | 删除指定任务 |
+| 任务延时 | `vTaskDelay()` | 相对延时 |
+| 任务挂起 | `vTaskSuspend()` | 挂起任务 |
+| 任务恢复 | `vTaskResume()` | 恢复任务 |
 
-### 关键特点
-- 任务调度:支持不同优先级的任务有序执行
-- 任务通信和同步:队列、信号量等机制
-- 内存管理:适用于嵌入式环境
-- 定时器和中断处理
-- 庞大的用户社区
-- 可移植性:轻松移植到不同硬件平台
+## Common Issues & Pitfalls
 
-> 后续章节:[[02-任务调度与状态管理]]、[[03-FreeRTOS移植指南]]、[[04-数据类型与命名规范]]
+- **不要把 RTOS 当成万能的**:RTOS 解决的是任务调度问题,不解决算法效率问题。一个低效的算法在任何系统上都是低效的。
+- **裸机思维转换**:从裸机转向 RTOS 时,不要试图在任务中手动控制时序——让调度器帮你做,用 `vTaskDelay` 代替 `HAL_Delay`。
+- **优先级不是越高越好**:优先级设计不当会导致低优先级任务"饿死"(无法获得 CPU 时间)。

+ 108 - 48
X-Knowledge-Base/raw/Joplin/嵌入式+Linux/FreeRTOS学习笔记/02-任务调度与状态管理.md

@@ -1,89 +1,149 @@
 ---
 tags: [source-summary]
 type: source
-source: "尚硅谷嵌入式技术之FreeRTOS实时操作系统 V1.0.3 — 第2章"
+source: "尚硅谷嵌入式技术之FreeRTOS实时操作系统 V1.0.3 — FreeRTOS基础知识"
 author: "尚硅谷研究院"
-date: 2026-07-12
-url: ""
-created: 2026-07-12
+date: 2026-07-17
+created: 2026-07-17
 ---
 
 # 任务调度与状态管理
 
-> 来源:尚硅谷 FreeRTOS 教程第 2 章。覆盖:多任务处理、任务调度策略、任务状态、滴答定时器、上下文切换、空闲任务
+> **用生活理解**:CPU 像一个只能同时接待一位顾客的收银员。多任务系统让收银员在多个顾客之间快速切换,让每个顾客都觉得自己被"同时"服务着。调度器就是排号系统——谁优先级高谁先结账,同优先级按顺序来
 
 ## 多任务处理
 
-内核是操作系统的核心组件。多任务操作系统可以将复杂应用程序分割成一组更小、更易管理的任务。即使单核处理器一次只能执行一项任务,通过任务间快速切换也能制造并发执行的假象。
+### Basic concept
+
+**内核**是操作系统的核心组件。多任务操作系统通过任务之间的快速切换,在单核处理器上制造出"并发执行"的假象。
+
+实际上,单核 CPU 在同一时刻只能运行一个任务。任务切换的速度非常快(毫秒级),让人感觉多个任务在同时运行。
+
+![多任务逻辑并发 vs 实际执行](./assets/multitasking_concurrent_vs_actual.png)
+
+多任务系统的好处:
+- 将复杂应用拆分为一组更小、更易管理的任务
+- 便于团队分工和代码复用
+- 时序和排序细节从应用代码中剥离,由 OS 负责
 
 ## 任务调度
 
-调度器决定在任何特定时间应执行哪个任务。FreeRTOS 默认使用 **固定优先级的抢占式调度策略**,对**同等优先级的任务执行时间片轮询**:
+### Working principle
+
+**调度器**是内核中负责决定"下一时刻运行哪个任务"的组件。**调度策略**是决定算法。
+
+FreeRTOS 默认使用**固定优先级的抢占式调度策略**,对同等优先级任务执行**时间片轮询**。
+
+### Scheduling modes
+
+| 模式 | 说明 | 适用场景 |
+|------|------|---------|
+| **抢占式调度** | 高优先级任务随时可以抢占正在执行的低优先级任务 | 需要确保高优先级任务实时响应的场景 |
+| **时间片轮询** | 同优先级任务轮流执行固定长度的时间片 | 多任务需要公平分配 CPU 的场景 |
 
-- **抢占式调度**:更高优先级的任务可随时抢占正在执行的低优先级任务
-- **时间片轮询**:同优先级任务轮流执行一个时间片(即 SysTick 中断周期)
+![抢占式调度](./assets/scheduling_preemption.png)
 
-![抢占式调度:高优先级任务抢占低优先级](assets/scheduling_preemption.png)
-![时间片轮转:同优先级任务轮流执行](assets/scheduling_timeslice.png)
+高优先级任务在等待资源(延时、信号量等)时会主动让出 CPU,调度器选择其他就绪的高优先级任务执行,不会让低优先级任务"饿死"。
 
-高优先级任务等待资源(延时或等待信号量等)而无法执行时,调度器会选择执行其他就绪的高优先级任务。
+![时间片轮询调度](./assets/scheduling_timeslice.png)
+
+### Cooperative scheduling
+
+除了抢占式调度,FreeRTOS 也支持协程式调度(configUSE_PREEMPTION = 0 时启用)。在协程式调度下,任务不会被强制打断,只有当任务主动调用 `taskYIELD()` 或阻塞等待时,调度器才会切换任务。协程式调度实时性较弱,一般不推荐使用。
 
 ## 任务状态
 
-FreeRTOS 中共 4 种任务状态:
+### Four task states
 
-| 状态 | 说明 |
-|------|------|
-| **运行态**(Running) | 任务实际执行中,单核下仅一个任务处于此状态 |
-| **就绪态**(Ready) | 任务可执行但尚未运行,因同/更高优先级任务正在运行 |
-| **阻塞态**(Blocked) | 任务等待延时或外部事件(**主动**行为) |
-| **挂起态**(Suspended) | 调用 `vTaskSuspend()` 进入,需 `vTaskResume()` 恢复(**被动**行为——任务被"踢出局") |
+FreeRTOS 中任务共存在 4 种状态:
 
-> **阻塞 vs 挂起的本质区别**:阻塞是任务**主动**说"我等会儿再跑"(延时/等资源),调度器还认得它;挂起是任务被**被动**标记为"出局",调度器完全忽略它,直到被明确恢复。就像员工自己去茶水间接水(阻塞)vs 被领导叫去面壁(挂起)。
->
-> **核心规则**:**只有就绪态可转为运行态**。其他所有状态必须先进入就绪态。运行态不维护自己的链表——"运行是最终目标,不需要排队"。
+| 状态 | 含义 | 进入方式 | 退出方式 |
+|------|------|---------|---------|
+| **运行态**(Running) | 任务正在执行 | 调度器选中 | 被抢占、阻塞、挂起 |
+| **就绪态**(Ready) | 任务可执行但未运行 | 创建、解除阻塞、解挂 | 被调度器选中 |
+| **阻塞态**(Blocked) | 等待延时或外部事件 | vTaskDelay、等待队列/信号量等 | 延时到、事件发生 |
+| **挂起态**(Suspended) | 类似暂停 | vTaskSuspend() | vTaskResume() 解挂 |
 
-![FreeRTOS 任务状态转换图:运行态 ↔ 就绪态 ↔ 阻塞态 ↔ 挂起态](assets/task_state_diagram.png)
+### State transitions
 
-每种状态对应一个任务状态列表:
-- **就绪列表**:`pxReadyTasksLists[x]`(x 为优先级数值)
-- **阻塞列表**:`pxDelayedTaskList`
-- **挂起列表**:`xSuspendedTaskList`
+只有就绪态可以转变为运行态。其他状态的任务想要运行,必须先转变为就绪态。
 
-![就绪列表位图:每个 bit 代表一个优先级是否有任务就绪](assets/ready_list_bitmap.png)
-![同优先级多任务链在同一就绪列表上](assets/ready_list_same_priority.png)
+![任务状态转换图](./assets/task_state_diagram.png)
 
-> **实现细节**:就绪列表本质是一个**链表数组**——每个优先级对应一个链表。一个 32 位位图变量记录哪些优先级有就绪任务(bit n = 1 表示优先级 n 有任务就绪)。调度器从最高 bit 向下搜索,找到第一个置 1 的 bit 即可。**运行态不维护链表**——"运行是最终目标,不需要排队"。
+## 滴答计数器
 
-调度器总是在所有就绪列表中选择最高优先级的任务执行。
+### Working principle
 
-> **时间片提前结束**:如果任务在时间片用完前就主动让出 CPU(调用 `vTaskDelay()` 或等信号量),切换**立即发生**,不会浪费剩余的时间片。就像员工提前干完活,不会非要等下班才走
+FreeRTOS 实时内核通过**滴答计数变量**(Tick Count)测量时间。SysTick 定时器中断以固定频率递增滴答数,每次中断称为一个 **RTOS 滴答**
 
-## FreeRTOS 滴答(Tick)
+每次滴答中断时,内核检查是否有任务需要解除阻塞或唤醒。如果需要,就在 ISR 中进行任务切换。
 
-FreeRTOS 通过滴答计数变量测量时间。定时器中断(RTOS 滴答中断)以严格时间精度增加滴答数。每次滴答增加时,内核检查是否需要解除阻塞或唤醒任务
+`configTICK_RATE_HZ` 配置项定义每秒的滴答数(通常设为 1000,即 1ms 一次滴答)
 
 ## 上下文切换
 
-**上下文**包括处理器寄存器、堆栈等任务执行资源。任务被挂起时保存上下文到任务堆栈,恢复时从堆栈还原到处理器寄存器。
+### Basic concept
+
+**上下文**是任务执行时使用的所有处理器资源(寄存器、堆栈等)的集合。
+
+**上下文切换**就是:暂停当前任务时保存其上下文到栈中,切换到下一个任务时从栈中恢复其上下文。这样任务恢复执行时,"以为自己从未被打断过"。
+
+![上下文切换——保存](./assets/context_switch_save.png)
+![上下文切换——恢复](./assets/context_switch_restore.png)
+
+### Three triggers for context switch
 
-![上下文切换 — 保存 TaskA 上下文到任务堆栈](assets/context_switch_save.png)
-![上下文切换 — 从任务堆栈恢复 TaskB 上下文](assets/context_switch_restore.png)
+1. **RTOS 滴答中断**:处理就绪列表,判断是否抢占或时间片轮转
+2. **任务执行完毕**:任务函数返回时自动切换
+3. **主动让出**:调用 `taskYIELD()` 主动触发
 
-切换时机:
-1. **RTOS 滴答中断**:处理就绪列表,判断是否切换(抢占/时间片)
-2. **任务执行完毕**:主动调用任务切换函数
+### PendSV mechanism
 
-上下文切换在 PendSV ISR 中处理。PendSV 被设置为最低中断优先级,避免影响其他 ISR。通过将 ICSR 寄存器的 bit28(PendSV 挂起位)置 1 来触发 PendSV 中断。
+实际的上下文切换在 **PendSV** 中断服务函数中完成。PendSV 被设置为**最低中断优先级**,这样上下文切换不会影响其他关键 ISR。
+
+触发方式:将 ICSR 寄存器的 bit28(PendSV 挂起位)置 1。
 
 ## 空闲任务
 
-调度器启动时自动创建空闲任务(最低优先级),确保始终有任务可运行。空闲任务负责释放被删除任务的内存。
+### Basic concept
+
+调度器启动时**自动创建空闲任务**,以最低优先级创建,确保至少有一个任务可运行。
+
+空闲任务的三个作用:
+
+| 作用 | 说明 |
+|------|------|
+| **释放被删任务的内存** | 任务调用 vTaskDelete 后,其资源在空闲任务中统一回收 |
+| **CPU 空闲计数** | 统计 CPU 空闲率,辅助性能分析 |
+| **低功耗模式** | 配合 tickless 模式,在空闲时进入低功耗状态 |
+
+## 就绪列表
+
+### Ready list structure
+
+就绪列表是 `pxReadyTasksLists[x]` 数组,其中 x 代表任务优先级(0 ~ configMAX_PRIORITIES-1)。
+
+在 32 位硬件上,一个 32 位的位图变量标记哪些优先级对应的就绪列表中有任务:
+
+![就绪列表位图](./assets/ready_list_bitmap.png)
+
+多个相同优先级的任务连接在同一个就绪列表上:
+
+![同优先级就绪列表](./assets/ready_list_same_priority.png)
+
+调度器始终在所有就绪列表中选择**最高优先级**的任务来执行。
+
+## Core API Reference Table
+
+| Function | Parameters | Return | Description |
+|----------|-----------|--------|-------------|
+| `vTaskStartScheduler()` | void | void | 启动任务调度器 |
+| `vTaskSuspend()` | TaskHandle_t | void | 挂起指定任务 |
+| `vTaskResume()` | TaskHandle_t | void | 恢复被挂起的任务 |
+| `taskYIELD()` | void | void | 主动请求任务切换 |
 
-> **桶的类比**:空闲任务就像没人用坑位时在坑里放一个桶占位——有人来了把桶拿走。空闲任务就是那个"桶"。
->
-> **异步删除**:`vTaskDelete()` 只是打了一个"删除标记",内存空间要到空闲任务运行时才真正清理。就像有人说"回头再打扫",但真正打扫要等没人用时才进行。
->
-> **空闲任务的三个作用**:① 确保始终有任务可运行 ② 清理已删除任务的内存 ③ 低功耗模式的入口。
+## Common Issues & Pitfalls
 
-> 后续章节:[[05-任务创建删除与挂起恢复]]、[[06-中断管理与时间管理]]
+- **避免死循环占用 CPU**:任务循环中必须包含阻塞操作(vTaskDelay、等待队列等),否则同优先级或低优先级任务无法运行。
+- **优先级反转**:高优先级任务等待低优先级任务持有的资源时,可通过互斥信号量的优先级继承机制缓解。
+- **不要中断中调用阻塞 API**:ISR 中调用会阻塞的 FreeRTOS API 可能导致系统崩溃,应使用带 `FromISR` 后缀的函数。

+ 115 - 206
X-Knowledge-Base/raw/Joplin/嵌入式+Linux/FreeRTOS学习笔记/03-FreeRTOS移植指南.md

@@ -1,267 +1,176 @@
 ---
 tags: [source-summary]
 type: source
-source: "尚硅谷嵌入式技术之FreeRTOS实时操作系统 V1.0.3 — 第3章"
+source: "尚硅谷嵌入式技术之FreeRTOS实时操作系统 V1.0.3 — FreeRTOS移植"
 author: "尚硅谷研究院"
-date: 2026-07-12
-url: ""
-created: 2026-07-12
+date: 2026-07-17
+created: 2026-07-17
 ---
 
 # FreeRTOS 移植指南
 
-> 来源:尚硅谷 FreeRTOS 教程第 3 章。覆盖:源码结构、HAL 库移植步骤、寄存器移植步骤、FreeRTOSConfig.h 配置详解
+> **用生活理解**:移植 FreeRTOS 就像给一台电脑装操作系统——你得知道安装包(源码结构)里有什么,按照步骤装驱动(移植文件),再配置系统设置(FreeRTOSConfig.h),最后开机运行。我们的芯片是 Cortex-M3,选对应驱动即可
 
-## VSCode + GCC + CMake 移植步骤(STM32F103 实战)
+## FreeRTOS 源码结构
 
-### 目录结构
+### Basic concept
+
+FreeRTOS 源码包顶层结构:
+
+| 目录 | 说明 |
+|------|------|
+| `FreeRTOS/` | 内核源码——移植的核心 |
+| `FreeRTOS-Plus/` | 第三方组件(TCP、FAT 等) |
+| `Tools/` | 工具 |
+| `Quick_Start_Guide/` | 快速入门指南 |
+
+### Source/ 目录详解
+
+`FreeRTOS/Source/` 是最重要的文件夹:
 
 ```
-CMake_HAL_Key/
-├── Core/             CubeMX 生成的 HAL 代码
-├── Drivers/          STM32 HAL 驱动
-├── Hardware/         用户硬件驱动(LED、Key)
-├── Middle/
-│   └── FreeRTOS/     FreeRTOS 源码(手工复制)
-│       ├── croutine.c
-│       ├── event_groups.c
-│       ├── list.c
-│       ├── queue.c
-│       ├── stream_buffer.c
-│       ├── tasks.c
-│       ├── timers.c
-│       ├── include/
-│       │   ├── FreeRTOS.h
-│       │   ├── FreeRTOSConfig.h    ← 从 Demo 复制并修改
-│       │   ├── task.h
-│       │   └── ...
-│       └── portable/
-│           ├── GCC/
-│           │   └── ARM_CM3/        ← STM32F103 = Cortex-M3
-│           │       ├── port.c
-│           │       └── portmacro.h
-│           └── MemMang/
-│               └── heap_4.c        ← 推荐
-└── cmake/            CMake 工具链配置
+Source/
+├── include/              # 通用头文件(与编译器/处理器无关)
+├── portable/             # 移植文件(与编译器/架构相关)
+│   ├── Keil/             # → 指向 RVDS
+│   ├── RVDS/             # 各处理器核的接口文件
+│   │   └── ARM_CM3/      # Cortex-M3 移植(port.c + portmacro.h)
+│   └── MemMang/          # 内存管理(heap_1 ~ heap_5)
+├── croutine.c            # 协程(可选)
+├── event_groups.c        # 事件组(可选)
+├── list.c                # 列表(必选)
+├── queue.c               # 队列(可选,但信号量/互斥量依赖它)
+├── stream_buffer.c       # 流式缓冲区(可选)
+├── tasks.c               # 任务管理(必选)
+└── timers.c              # 软件定时器(可选)
 ```
 
-### 步骤一:复制 FreeRTOS 源码到 Middle/
+- **必选** .c 文件:`tasks.c`、`list.c`(基础依赖)
+- **portable**:`RVDS/ARM_CM3/port.c`(Cortex-M3 移植接口)+ `portmacro.h`
+- **MemMang**:选一个 heap 文件,初学者推荐 **heap_4.c**
 
-从官方源码包取以下文件:
+### Portable 层
 
-| 来源 | 目标 |
-|------|------|
-| `Source/*.c`(7 个核心 .c) | `Middle/FreeRTOS/` |
-| `Source/include/*.h` | `Middle/FreeRTOS/include/` |
-| `Source/portable/GCC/ARM_CM3/port.c` | `Middle/FreeRTOS/portable/GCC/ARM_CM3/` |
-| `Source/portable/GCC/ARM_CM3/portmacro.h` | `Middle/FreeRTOS/portable/GCC/ARM_CM3/` |
-| `Source/portable/MemMang/heap_4.c` | `Middle/FreeRTOS/portable/MemMang/` |
-| `Demo/CORTEX_STM32F103_GCC_Rowley/FreeRTOSConfig.h` | `Middle/FreeRTOS/include/` |
+portable 目录是连接 FreeRTOS 与硬件的桥梁,包含两部分:
 
-### 步骤二:修改 FreeRTOSConfig.h
+| 组件 | 文件 | 说明 |
+|------|------|------|
+| 移植接口 | `port.c` | 上下文切换汇编代码(已由官方编写好) |
+| 移植头文件 | `portmacro.h` | 数据类型定义和宏定义 |
 
-在 FreeRTOSConfig.h 末尾添加三个宏,用来将 FreeRTOS port.c 中的中断处理函数名重定义为向量表名称:
+对于 STM32F103(Cortex-M3 内核),只需使用 `RVDS/ARM_CM3/` 下的这两个文件即可。官方已经把底层移植代码写好了,我们只需要"拿起即用"。
 
-```c
-/* 必须的三个宏 */
-#define xPortPendSVHandler PendSV_Handler
-#define vPortSVCHandler SVC_Handler
-#define INCLUDE_xTaskGetSchedulerState 1
-```
+### MemMang 方案对比
 
-原理:FreeRTOS port.c 定义了 `xPortPendSVHandler()` 和 `vPortSVCHandler()`,而 STM32 的启动文件向量表中写的是 `PendSV_Handler` 和 `SVC_Handler`。通过宏替换,预处理器将 port.c 中的函数名直接替换为向量表名称,无需额外修改启动文件。
+| 算法 | 特点 | 适用场景 |
+|------|------|---------|
+| heap_1 | 只分配不释放,无碎片 | 确定不需要删除任务的场景 |
+| heap_2 | 最佳匹配,可释放,会碎片 | 不推荐,已被 heap_4 替代 |
+| heap_3 | 简单包装 malloc/free,线程安全 | 编译器的 malloc 可用时 |
+| **heap_4** | 首次匹配,合并相邻空闲块 | **初学者首选,通用场景** |
+| heap_5 | heap_4 + 跨非连续内存区域 | 多块物理内存的场景 |
 
-> ❗ 此方法依赖宏定义,方向是:`FreeRTOS原名 → 向量表名`,不可写反。
+## HAL 库移植步骤
 
-CubeMX 生成的 GCC 启动文件是标准 ST 名称(`SVC_Handler`、`PendSV_Handler`),不能被 CubeMX 重新生成时覆盖,所以不能改;因此 GCC 方向相反——用宏重命名 FreeRTOS 的函数来匹配启动文件。
+### Step 1: 目录添加源码文件
 
-`configCHECK_FOR_STACK_OVERFLOW` 默认来自 `CORTEX_STM32F103_GCC_Rowley` Demo,值为 2(启用栈溢出检测)。但该检测需要用户提供 `vApplicationStackOverflowHook` 函数,否则链接报错。
+1. 在工程根目录新建 `FreeRTOS/` 文件夹,其下创建 `portable/` 和 `source/`
+2. 复制 `FreeRTOS/Source/` 下的 **7 个 .c 文件**(tasks.c, queue.c, timers.c, event_groups.c, croutine.c, list.c, stream_buffer.c)到 `source/`
+3. 复制 `portable/` 下的 `Keil/`、`RVDS/`、`MemMang/` 到 `portable/`
+4. `MemMang/` 只保留 `heap_4.c`
+5. `RVDS/` 只保留 `ARM_CM3/`
+6. 复制 `include/` 到 `FreeRTOS/` 下
+7. 从 `Demo/CORTEX_STM32F103_Keil/` 复制 `FreeRTOSConfig.h` 到 `Core/Inc/`
 
-两种解决方式(二选一):
+### Step 2: 工程添加源文件
 
-**方式 A**(本实战采用):关闭栈溢出检测
-```c
-#define configCHECK_FOR_STACK_OVERFLOW 0
-```
+1. 在 Keil 中创建两个 Group:
+   - `FreeRTOS/Source` —— 添加 source/ 下的 7 个 .c 文件
+   - `FreeRTOS/Portable` —— 添加 `port.c` 和 `heap_4.c`
+2. 添加头文件路径:`FreeRTOS/include/` 和 `FreeRTOS/portable/RVDS/ARM_CM3/`
+3. `FreeRTOSConfig.h` 在 `Core/Inc/`(该路径通常已有)
 
-**方式 B**:保留检测,在任意 .c 添加 hook 函数
-```c
-void vApplicationStackOverflowHook(TaskHandle_t xTask, char *pcTaskName) {
-    for (;;) {}
-}
-```
+### Step 3: 修改 FreeRTOSConfig.h
 
-> Keil Demo(`CORTEX_STM32F103_Keil`)的 FreeRTOSConfig.h 未定义此项,默认 0,所以 Keil 教程无需处理此问题。
+在 `FreeRTOSConfig.h` 中添加:
 
-### 步骤三:修改 stm32f1xx_it.c
+```c
+#define xPortPendSVHandler  PendSV_Handler
+#define vPortSVCHandler     SVC_Handler
+#define INCLUDE_xTaskGetSchedulerState   1
+```
 
-添加 FreeRTOS 头文件:
+### Step 4: 修改 stm32f1xx_it.c
 
+1. 添加头文件:
 ```c
-/* USER CODE BEGIN Includes */
 #include "FreeRTOS.h"
 #include "task.h"
-/* USER CODE END Includes */
 ```
 
-注释掉与 FreeRTOS 冲突的空中断处理函数(由 FreeRTOS port.c 通过宏提供):
-
+2. 注释掉默认的空中断:
 ```c
-// void SVC_Handler(void) { ... }
-// void PendSV_Handler(void) { ... }
+// void SVC_Handler(void) { }
+// void PendSV_Handler(void) { }
 ```
 
-修改 SysTick_Handler,让 HAL 和 FreeRTOS 共用 SysTick:
-
+3. 添加 SysTick 中断服务函数:
 ```c
+/* USER CODE BEGIN PV */
 extern void xPortSysTickHandler(void);
-void SysTick_Handler(void) {
-    if (xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED) {
+/* USER CODE END PV */
+
+void SysTick_Handler(void)
+{
+    /* USER CODE BEGIN SysTick_IRQn 0 */
+    /* USER CODE END SysTick_IRQn 0 */
+
+    /* USER CODE BEGIN SysTick_IRQn 1 */
+    if (xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED)
+    {
         xPortSysTickHandler();
     }
+    /* USER CODE END SysTick_IRQn 1 */
 }
 ```
 
-> 注意:HAL 默认也依赖 SysTick 做时基。如果 FreeRTOS 抢占 SysTick 后 HAL 的 `HAL_Delay()` 会失效,建议在 CubeMX 中将 HAL 时基换为其他定时器(如 TIM7)。
-
-### 步骤四:修改 CMakeLists.txt
-
-将 FreeRTOS 源码和头文件路径加入构建:
-
-```cmake
-target_sources(${CMAKE_PROJECT_NAME} PRIVATE
-    Hardware/LED.c
-    Hardware/Key.c
-    Middle/FreeRTOS/croutine.c
-    Middle/FreeRTOS/event_groups.c
-    Middle/FreeRTOS/list.c
-    Middle/FreeRTOS/queue.c
-    Middle/FreeRTOS/stream_buffer.c
-    Middle/FreeRTOS/tasks.c
-    Middle/FreeRTOS/timers.c
-    Middle/FreeRTOS/portable/GCC/ARM_CM3/port.c
-    Middle/FreeRTOS/portable/MemMang/heap_4.c
-)
-
-target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE
-    Hardware
-    Middle/FreeRTOS/include
-    Middle/FreeRTOS/portable/GCC/ARM_CM3
-)
-```
-
-### 步骤五:构建验证
-
-```bash
-cmake -S . -B build/Debug -G Ninja -DCMAKE_BUILD_TYPE=Debug
-cmake --build build/Debug
-```
-
-预期输出 `Project.elf`,无链接错误。
-
-## 源码结构
-
-```
-FreeRTOS/
-├── Demo/           演示例程(多芯片架构)
-├── License/        许可
-├── Source/         核心源码
-│   ├── include/    头文件(通用)
-│   ├── portable/   移植文件(编译器相关)
-│   │   ├── Keil/  → 指向 RVDS
-│   │   ├── RVDS/  不同内核芯片移植文件
-│   │   │   └── ARM_CM3/   port.c + portmacro.h
-│   │   └── MemMang/  heap_1~5.c
-│   ├── tasks.c     任务管理
-│   ├── queue.c     队列
-│   ├── timers.c    软件定时器
-│   ├── event_groups.c
-│   ├── stream_buffer.c
-│   └── list.c      列表
-└── Test/           测试代码
-```
-
-**port.c**:上下文切换等核心汇编代码,由 FreeRTOS 官方为各内核编写。
-**portmacro.h**:数据类型和宏定义。
+### Step 5: 解决 HAL + SysTick 冲突
 
-**MemMang** 五个 heap 文件必须使用一个(初学推荐 heap_4.c)
+HAL 库默认使用 SysTick 作为时基。FreeRTOS 也依赖 SysTick 作为系统滴答。
 
-## HAL 库移植步骤
+**解决方案**:在 STM32CubeMX 的 SYS 配置中,将 Timebase Source 改为其他定时器(如 TIM6),并将中断优先级设为较高(如 1)。
 
-### 目录添加源码
-```
-Project/FreeRTOS/
-├── portable/   (Keil + RVDS + MemMang,保留 ARM_CM3 + heap_4.c)
-└── source/     (7 个 .c 文件)
-Core/Inc/FreeRTOSConfig.h  (从 Demo 目录拷贝)
-```
+## 寄存器版移植步骤
 
-### 工程添加文件
-1. 新建 Group `FreeRTOS/Source` 和 `FreeRTOS/Portable`
-2. Source 组添加 7 个 .c 文件
-3. Portable 组添加 `port.c` + `heap_4.c`
-4. 添加头文件路径:`include/` 和 `RVDS/ARM_CM3/`
+与 HAL 库移植的前两步相同(目录添加 + 工程添加源文件)。
 
-### 系统配置
-```c
-#define xPortPendSVHandler    PendSV_Handler
-#define vPortSVCHandler       SVC_Handler
-#define INCLUDE_xTaskGetSchedulerState   1
-```
+不同之处在于系统配置修改:
 
-### 修改 stm32f1xx_it.c
+1. 同样在 `FreeRTOSConfig.h` 添加三个配置
+2. 在 `main.c` 中添加 SysTick_Handler:
 ```c
-#include "FreeRTOS.h"
-#include "task.h"
-
-// 注释掉 SVC_Handler() 和 PendSV_Handler()
-
 extern void xPortSysTickHandler(void);
-void SysTick_Handler(void) {
-    if (xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED) {
+
+void SysTick_Handler(void)
+{
+    if (xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED)
+    {
         xPortSysTickHandler();
     }
 }
 ```
 
-注意:HAL 和 FreeRTOS 都默认依赖 SysTick,建议在 CubeMX 中将 HAL 时基换成其他定时器。
+## Core API Reference Table
 
-## 寄存器移植步骤
+| Function | Parameters | Return | Description |
+|----------|-----------|--------|-------------|
+| `vTaskStartScheduler()` | void | void | 启动调度器 |
+| `xTaskGetSchedulerState()` | void | BaseType_t | 获取调度器状态(NOT_STARTED / RUNNING / SUSPENDED) |
+| `xPortSysTickHandler()` | void | void | 滴答中断处理函数(在 SysTick_Handler 中调用) |
 
-与 HAL 移植类似,但不需要 HAL 相关配置。main.c 中直接添加 SysTick_Handler:
-```c
-extern void xPortSysTickHandler(void);
-void SysTick_Handler(void) {
-    if (xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED) {
-        xPortSysTickHandler();
-    }
-}
-```
+## Common Issues & Pitfalls
 
-## FreeRTOSConfig.h 配置
-
-配置项分三类:
-1. **INCLUDE_** 开头:函数使能(1=可用,0=禁用)
-2. **config** 开头:功能配置(基本、内存、钩子、中断)
-3. **其他**:PendSV/SVC 宏定义
-
-### 关键配置项
-| 配置 | 值 | 说明 |
-|------|-----|------|
-| `configUSE_PREEMPTION` | 1 | 抢占式调度 |
-| `configCPU_CLOCK_HZ` | SystemCoreClock | CPU 主频 |
-| `configTICK_RATE_HZ` | 1000 | 系统节拍频率 |
-| `configMAX_PRIORITIES` | 32 | 最大优先级数 |
-| `configMINIMAL_STACK_SIZE` | 128 | 空闲任务栈大小(Word) |
-| `configTOTAL_HEAP_SIZE` | 10*1024 | 堆大小(Byte) |
-| `configUSE_MUTEXES` | 1 | 使能互斥信号量 |
-| `configUSE_TIMERS` | 1 | 使能软件定时器 |
-| `configUSE_TICKLESS_IDLE` | 0 | 低功耗模式 |
-| `configMAX_SYSCALL_INTERRUPT_PRIORITY` | 5<<4 | FreeRTOS 管理的最高中断优先级 |
-
-## 配套代码
-
-移植完成的 HAL 库基础工程模板:
-- `code/00_HAL_Key/` — HAL 库按键检测模板(FreeRTOS 移植基础工程)
-
-> 后续章节:[[04-数据类型与命名规范]]、[[05-任务创建删除与挂起恢复]]
+- **PendSV_Handler 被注释掉了吗?**:FreeRTOS 接管了 PendSV_Handler,必须在 `stm32f1xx_it.c` 中注释掉原有的空函数,否则链接时冲突。
+- **SysTick 冲突卡死**:HAL 与 FreeRTOS 都默认使用 SysTick。启动调度器后,HAL 的 HAL_Delay 会失效甚至卡死。记得将 HAL 时基换到其他定时器。
+- **变量报 undefined**:如果编译报变量未定义,检查 `FreeRTOSConfig.h` 开头是否包含了系统头文件(如 `stm32f1xx_hal.h`)并定义了 `SystemCoreClock`。
+- **内存不足**:`configTOTAL_HEAP_SIZE` 默认可能太大。STM32F103 系列通常设为 10\*1024(10KB)即可。

+ 0 - 66
X-Knowledge-Base/raw/Joplin/嵌入式+Linux/FreeRTOS学习笔记/04-数据类型与命名规范.md

@@ -1,66 +0,0 @@
----
-tags: [source-summary]
-type: source
-source: "尚硅谷嵌入式技术之FreeRTOS实时操作系统 V1.0.3 — 第4章"
-author: "尚硅谷研究院"
-date: 2026-07-12
-url: ""
-created: 2026-07-12
----
-
-# 数据类型与命名规范
-
-> 来源:尚硅谷 FreeRTOS 教程第 4 章。覆盖:FreeRTOS 数据类型、编码规范。
-
-## 数据类型
-
-针对每个移植定义四种类型:
-
-### TickType_t
-系统节拍计数器类型。由 `configUSE_16_BIT_TICKS` 决定:
-- 非零 → 无符号 16 位
-- 零 → 无符号 32 位
-
-记录了系统过去的节拍次数。
-
-### BaseType_t
-架构中最有效、最自然的类型。有符号。
-- 32 位架构 → 32 位
-- 16 位架构 → 16 位
-
-### UBaseType_t
-无符号的 BaseType_t。
-
-### StackType_t
-架构用于存储堆栈项目的类型。供 FreeRTOS 内部使用。
-- 16 位架构 → 16 位
-- 32 位架构 → 32 位
-
-## 命名规范
-
-### 变量前缀
-| 前缀 | 类型 | 示例 |
-|------|------|------|
-| `ul` | uint32_t | `ulValue` |
-| `us` | uint16_t | `usCounter` |
-| `uc` | uint8_t | `ucByte` |
-| `x` | 非 stdint 类型(BaseType_t, TickType_t, size_t) | `xItem` |
-| `ux` | 无符号非 stdint 类型(UBaseType_t) | `uxPriority` |
-| `e` | 枚举 | `eState` |
-| `p` | 指针(附加前缀如 `pus`=uint16_t*) | `pusBuffer` |
-| `c` | 无符号 char(仅 ASCII 字符) | `cChar` |
-| `pc` | char*(指向 ASCII 字符串) | `pcName` |
-
-### 函数前缀
-- `prv`:文件作用域静态(私有)函数
-- API 函数以**返回类型为前缀**(`v`=void),+ 文件名 + 功能名
-
-示例:`vTaskDelay` → 返回 void,定义在 task.c,功能是延时。
-
-### 宏
-- 以定义宏的文件为前缀(小写)
-- 除前缀外全部大写,下划线分隔单词
-
-示例:`configUSE_PREEMPTION`(定义在 FreeRTOSConfig.h)
-
-> 后续章节:[[05-任务创建删除与挂起恢复]]

+ 209 - 0
X-Knowledge-Base/raw/Joplin/嵌入式+Linux/FreeRTOS学习笔记/04-数据类型命名与配置详解.md

@@ -0,0 +1,209 @@
+---
+tags: [source-summary]
+type: source
+source: "尚硅谷嵌入式技术之FreeRTOS实时操作系统 V1.0.3 — 数据类型 + 命名规范 + FreeRTOSConfig.h"
+author: "尚硅谷研究院"
+date: 2026-07-17
+created: 2026-07-17
+---
+
+# 数据类型、命名规范与配置详解
+
+> **用生活理解**:数据类型是 FreeRTOS 定义的标准"容器"规格,命名规范是像快递单号一样——通过前缀就知道包裹类型。FreeRTOSConfig.h 就像系统的 BIOS 设置界面,你可以开关功能、分配资源大小。
+
+## FreeRTOS 数据类型
+
+### Basic concept
+
+FreeRTOS 针对每个移植平台定义了四种核心数据类型,保证代码在不同架构间可移植。
+
+| 类型 | 说明 | 默认定义(Cortex-M3) | 相关配置 |
+|------|------|----------------------|---------|
+| `TickType_t` | 滴答计数值类型,记录系统节拍次数 | `uint32_t` | `configUSE_16_BIT_TICKS`——设为非零时变为 `uint16_t` |
+| `BaseType_t` | 架构最自然类型,有符号 | `uint32_t`(Cortex-M3 32位) | 架构自动决定 |
+| `UBaseType_t` | `BaseType_t` 的无符号版本 | `uint32_t` | 架构自动决定 |
+| `StackType_t` | 堆栈存储单元类型 | `uint32_t`(Cortex-M3 32位) | 架构自动决定,供 FreeRTOS 内部使用 |
+
+### 各类型详解
+
+**TickType_t**:系统滴答中断每次发生时递增的计数器类型。如果定义了 `configUSE_16_BIT_TICKS`,则为 16 位无符号整数(最大计数值 65535),否则为 32 位无符号整数。对于长时间运行的系统,建议保持默认的 32 位,避免溢出。
+
+**BaseType_t**:定义为架构中最有效率的类型。在 32 位架构上是 32 位,16 位架构上是 16 位。FreeRTOS 的很多 API 返回值使用此类型。
+
+**StackType_t**:堆栈的基本单位,在 32 位架构上是 32 位。所有任务栈的分配和管理都以此类型为单位。
+
+## FreeRTOS 命名规范
+
+### Basic concept
+
+FreeRTOS 采用**匈牙利命名法**与**驼峰命名法**的结合。通过变量或函数的前缀就可以推测出返回类型和所属模块。
+
+### Variable prefixes
+
+| 前缀 | 含义 | 示例 |
+|------|------|------|
+| `c` | `char`(仅 ASCII 字符) | `cError` |
+| `s` | `int16_t`(short) | `sValue` |
+| `l` | `int32_t`(long) | `lCount` |
+| `x` | `BaseType_t`、`TickType_t` 或结构体 | `xQueueCreate` 返回值 |
+| `ux` | `UBaseType_t` | `uxTaskGetNumberOfTasks()` |
+| `ul` | `uint32_t` | `ulCounter` |
+| `us` | `uint16_t` | `usADCValue` |
+| `uc` | `uint8_t`(`unsigned char`) | `ucParameter` |
+| `e` | 枚举 | `eTaskState` |
+| `p` | 指针(追加在其他前缀前) | `pucBuffer`(`uint8_t*`) |
+| `pc` | `char*`(指向 ASCII 字符串) | `pcTaskName` |
+
+### Function prefixes
+
+函数命名格式:`{返回类型前缀}{文件名}{功能}`
+
+| 前缀 | 返回类型 | 示例 |
+|------|---------|------|
+| `v` | `void` | `vTaskDelay` |
+| `x` | `BaseType_t` 或结构体 | `xQueueCreate` |
+| `ux` | `UBaseType_t` | `uxTaskGetNumberOfTasks` |
+| `pv` | `void*` | `pvPortMalloc` |
+| `prv` | `void`(私有/static 函数) | `prvIdleTask` |
+
+例如 `vTaskDelay`:`v` → 返回 void,`Task` → 定义在 `tasks.c`,`Delay` → 功能是延时。
+
+### Macro naming
+
+| 规则 | 说明 | 示例 |
+|------|------|------|
+| 以小写文件名为前缀 | 对应定义所在文件 | `configUSE_PREEMPTION`(FreeRTOSConfig.h) |
+| 大写 + 下划线 | 可读性 | `pdTRUE`、`pdPASS`、`portMAX_DELAY` |
+| `pd` 前缀 | Protocol Define(协议定义) | `pdTRUE`、`pdFALSE`、`pdPASS`、`pdFAIL` |
+
+### Handle suffix
+
+| 后缀 | 含义 | 示例 |
+|------|------|------|
+| `_t` | typedef 类型 | `TickType_t`、`TaskHandle_t` |
+| `_handler` | 任务或中断句柄 | `task1_handler` |
+
+## FreeRTOSConfig.h 配置详解
+
+### Basic concept
+
+`FreeRTOSConfig.h` 是 FreeRTOS 的工程配置文件。它是一个纯头文件,在编译阶段确定内核行为。用户通过修改宏定义来裁剪内核功能,减少 ROM/RAM 占用。
+
+配置项分为三类:
+- **`INCLUDE_` 开头**:API 函数使能,1=可用,0=禁用
+- **`config` 开头**:功能配置,包括基础、内存、钩子、中断等
+- **其他**:PendSV/SVC 宏定义、断言等
+
+### 基础配置
+
+| 宏 | 课程值 | 默认值 | 说明 |
+|---|--------|-------|------|
+| `configUSE_PREEMPTION` | 1 | 无默认 | 1=抢占式调度,0=协程式调度 |
+| `configUSE_PORT_OPTIMISED_TASK_SELECTION` | 1 | 0 | 1=硬件计算下一个任务,0=软件算法 |
+| `configUSE_TICKLESS_IDLE` | 0 | 0 | 1=使能 tickless 低功耗模式 |
+| `configCPU_CLOCK_HZ` | `SystemCoreClock` | 无默认 | CPU 主频(Hz) |
+| `configTICK_RATE_HZ` | 1000 | 无默认 | 系统节拍频率(Hz),即每秒滴答次数 |
+| `configMAX_PRIORITIES` | 32 | 无默认 | 最大优先级数(0 ~ configMAX_PRIORITIES-1) |
+| `configMINIMAL_STACK_SIZE` | 128 | 无默认 | 空闲任务栈大小(单位:Word) |
+| `configMAX_TASK_NAME_LEN` | 16 | 16 | 任务名最大字符数 |
+| `configUSE_16_BIT_TICKS` | 0 | 无默认 | 1=滴答计数器为 16 位,0=32 位 |
+| `configIDLE_SHOULD_YIELD` | 1 | 1 | 同优先级任务能否抢占空闲任务 |
+| `configUSE_TASK_NOTIFICATIONS` | 1 | 1 | 使能任务通知 |
+| `configTASK_NOTIFICATION_ARRAY_ENTRIES` | 1 | 1 | 任务通知数组大小 |
+| `configUSE_MUTEXES` | 1 | 0 | 使能互斥信号量 |
+| `configUSE_RECURSIVE_MUTEXES` | 1 | 0 | 使能递归互斥信号量 |
+| `configUSE_COUNTING_SEMAPHORES` | 1 | 0 | 使能计数信号量 |
+| `configQUEUE_REGISTRY_SIZE` | 8 | 0 | 可注册的队列/信号量个数 |
+| `configUSE_QUEUE_SETS` | 1 | 0 | 使能队列集 |
+| `configUSE_TIME_SLICING` | 1 | 1 | 使能时间片调度 |
+
+### 内存配置
+
+| 宏 | 课程值 | 默认值 | 说明 |
+|---|--------|-------|------|
+| `configSUPPORT_STATIC_ALLOCATION` | 0 | 0 | 支持静态内存分配 |
+| `configSUPPORT_DYNAMIC_ALLOCATION` | 1 | 1 | 支持动态内存分配 |
+| `configTOTAL_HEAP_SIZE` | 10\*1024 | 无默认 | FreeRTOS 堆总大小(Byte) |
+| `configAPPLICATION_ALLOCATED_HEAP` | 0 | 0 | 用户手动分配堆空间 |
+
+### 钩子函数配置
+
+| 宏 | 课程值 | 默认值 | 说明 |
+|---|--------|-------|------|
+| `configUSE_IDLE_HOOK` | 0 | 无默认 | 空闲任务钩子 |
+| `configUSE_TICK_HOOK` | 0 | 无默认 | 滴答中断钩子 |
+| `configCHECK_FOR_STACK_OVERFLOW` | 0 | 0 | 栈溢出检测(1=方法1,2=方法2) |
+| `configUSE_MALLOC_FAILED_HOOK` | 0 | 0 | 内存分配失败钩子 |
+
+### 软件定时器配置
+
+| 宏 | 课程值 | 默认值 | 说明 |
+|---|--------|-------|------|
+| `configUSE_TIMERS` | 1 | 0 | 使能软件定时器 |
+| `configTIMER_TASK_PRIORITY` | `configMAX_PRIORITIES - 1` | 无默认 | 定时器服务任务优先级 |
+| `configTIMER_QUEUE_LENGTH` | 5 | 无默认 | 定时器命令队列长度 |
+| `configTIMER_TASK_STACK_DEPTH` | `configMINIMAL_STACK_SIZE * 2` | 无默认 | 定时器服务任务栈大小 |
+
+### 中断配置
+
+| 宏 | 课程值 | 说明 |
+|---|--------|------|
+| `configPRIO_BITS` | `__NVIC_PRIO_BITS` | 优先级位数(STM32F103 为 4) |
+| `configLIBRARY_LOWEST_INTERRUPT_PRIORITY` | 15 | 中断最低优先级 |
+| `configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY` | 5 | FreeRTOS 可管理的最高中断优先级 |
+| `configKERNEL_INTERRUPT_PRIORITY` | 15 << (8-4) = 240 | 内核自身中断优先级 |
+| `configMAX_SYSCALL_INTERRUPT_PRIORITY` | 5 << (8-4) = 80 | 可安全调用 API 的最高中断优先级 |
+
+### INCLUDE_ 函数使能配置
+
+| 宏 | 课程值 | 说明 |
+|---|--------|------|
+| `INCLUDE_vTaskPrioritySet` | 1 | 设置任务优先级 |
+| `INCLUDE_uxTaskPriorityGet` | 1 | 获取任务优先级 |
+| `INCLUDE_vTaskDelete` | 1 | 删除任务 |
+| `INCLUDE_vTaskSuspend` | 1 | 挂起任务 |
+| `INCLUDE_xResumeFromISR` | 1 | 中断中恢复任务 |
+| `INCLUDE_vTaskDelayUntil` | 1 | 任务绝对延时 |
+| `INCLUDE_vTaskDelay` | 1 | 任务延时 |
+| `INCLUDE_xTaskGetSchedulerState` | 1 | 获取调度器状态 |
+| `INCLUDE_xTaskGetCurrentTaskHandle` | 1 | 获取当前任务句柄 |
+| `INCLUDE_uxTaskGetStackHighWaterMark` | 1 | 获取堆栈历史最低水位 |
+| `INCLUDE_xTaskGetIdleTaskHandle` | 1 | 获取空闲任务句柄 |
+| `INCLUDE_eTaskGetState` | 1 | 获取任务状态 |
+| `INCLUDE_xTaskGetHandle` | 1 | 通过名称获取任务句柄 |
+
+### 中断服务函数重映射
+
+```c
+#define xPortPendSVHandler  PendSV_Handler
+#define vPortSVCHandler     SVC_Handler
+```
+
+这两个宏将 FreeRTOS 的 PendSV/SVC 中断处理函数重映射为标准的中断入口名,必须定义。
+
+### 断言配置
+
+```c
+#define vAssertCalled(char, int) printf("Error: %s, %d\r\n", char, int)
+#define configASSERT(x) if ((x) == 0) vAssertCalled(__FILE__, __LINE__)
+```
+
+调试阶段建议开启 `configASSERT`,在参数错误时能快速定位问题。发布版本可关闭以减小代码体积。
+
+## Core API Reference Table
+
+| API 类型 | 使能宏 | 功能 |
+|----------|--------|------|
+| `vTaskDelay` | `INCLUDE_vTaskDelay` | 任务相对延时 |
+| `vTaskDelayUntil` | `INCLUDE_vTaskDelayUntil` | 任务绝对延时 |
+| `vTaskDelete` | `INCLUDE_vTaskDelete` | 删除任务 |
+| `vTaskSuspend` | `INCLUDE_vTaskSuspend` | 挂起任务 |
+| `vTaskResume` | `INCLUDE_vTaskSuspend` | 恢复任务 |
+| `eTaskGetState` | `INCLUDE_eTaskGetState` | 获取任务状态 |
+
+## Common Issues & Pitfalls
+
+- **`INCLUDE_` 宏未定义导致链接错误**:调用某个 API 函数但对应的 `INCLUDE_` 宏为 0 或未定义时,编译可通过但链接会报 undefined reference。先确认 FreeRTOSConfig.h 中的使能配置。
+- **`configASSERT` 在发布版中应关闭**:断言会增加代码体积和运行开销,调试完功能后记得改为 0 或注释掉。
+- **`configTOTAL_HEAP_SIZE` 设太大**:STM32F103C8T6 只有 20KB RAM,设为 10KB 左右比较安全。设太大会导致 `pvPortMalloc` 分配失败。
+- **`configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY` 设置错误**:如果某个中断的优先级比这个值低(数值更大),该 ISR 中不能调用 FreeRTOS API。设得太高可能导致临界区无法正确嵌套。

+ 374 - 0
X-Knowledge-Base/raw/Joplin/嵌入式+Linux/FreeRTOS学习笔记/05-任务创建与删除.md

@@ -0,0 +1,374 @@
+---
+tags: [source-summary]
+type: source
+source: "尚硅谷嵌入式技术之FreeRTOS实时操作系统 V1.0.3 — 第4章 任务创建和删除"
+author: "尚硅谷研究院"
+date: 2026-07-17
+created: 2026-07-17
+---
+
+# 任务创建与删除
+
+> **用生活理解**:公司招人(创建任务)需要分配工位(栈)和建立档案(TCB);人员可以按编制内(静态)或外聘(动态)两种方式入职;裁员(删除任务)时需清理工位和档案。
+
+## Concept
+
+### Basic concept
+
+FreeRTOS 中,一个**任务(Task)**包含三要素:
+- **任务函数**:用户编写的无限循环函数
+- **堆栈(Stack)**:保存任务上下文(寄存器、局部变量)
+- **TCB(Task Control Block)**:任务控制块,记录任务状态、优先级等信息
+
+### Working principle
+
+FreeRTOS 创建任务时:
+1. 分配栈空间和 TCB 内存
+2. 将任务加入就绪链表
+3. 调度器根据优先级和时间片选择运行的任务
+
+**动态创建**(`xTaskCreate`)由内核自动分配栈和 TCB 内存:
+- 优点:使用简单,无需预先规划内存
+- 缺点:依赖 heap 管理,需要确保堆空间充足
+
+**静态创建**(`xTaskCreateStatic`)由用户预先分配栈和 TCB 内存:
+- 优点:不依赖堆分配,适合安全关键系统
+- 缺点:需要提前计算任务数量并分配内存
+
+删除任务时使用 `vTaskDelete()`,任务被删除后不会再次运行,任务占用的资源会被释放。
+
+### Core API
+
+#### 动态创建:xTaskCreate
+
+| Function | Params | Description |
+|----------|--------|-------------|
+| `xTaskCreate` | `pvTaskCode`, `pcName`, `usStackDepth`, `pvParameters`, `uxPriority`, `pxCreatedTask` | 动态创建一个新任务 |
+| | `pvTaskCode` | 任务函数指针 |
+| | `pcName` | 任务名称(用于调试) |
+| | `usStackDepth` | 栈深度(单位 word,STM32 中 1 word = 4 字节) |
+| | `pvParameters` | 传递给任务函数的参数 |
+| | `uxPriority` | 任务优先级(0~configMAX_PRIORITIES-1) |
+| | `pxCreatedTask` | 返回任务句柄 |
+| **返回** | `pdPASS` | 创建成功 |
+| | `errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY` | 创建失败(内存不足) |
+
+#### 静态创建:xTaskCreateStatic
+
+| Function | Params | Description |
+|----------|--------|-------------|
+| `xTaskCreateStatic` | `pvTaskCode`, `pcName`, `ulStackDepth`, `pvParameters`, `uxPriority`, `puxStackBuffer`, `pxTaskBuffer` | 静态创建一个新任务 |
+| | `puxStackBuffer` | 用户预先分配的栈空间 |
+| | `pxTaskBuffer` | 用户预先分配的 TCB 空间 |
+| **返回** | 非 NULL | 任务句柄 |
+| | NULL | 创建失败(用户未提供内存) |
+
+#### 删除任务:vTaskDelete
+
+| Function | Params | Description |
+|----------|--------|-------------|
+| `vTaskDelete` | `xTask` | 删除指定任务 |
+| | `xTask` | 待删除任务句柄;传 NULL 表示删除自身 |
+| **注意** | 传 NULL 时会在函数末尾触发一次任务切换 | |
+
+#### 空闲任务与定时器任务内存钩子(静态分配)
+
+当 `configSUPPORT_STATIC_ALLOCATION = 1` 时,必须提供以下两个钩子函数:
+
+| Function | Description |
+|----------|-------------|
+| `vApplicationGetIdleTaskMemory` | 提供空闲任务的 TCB 和栈缓冲区 |
+| `vApplicationGetTimerTaskMemory` | 提供定时器任务的 TCB 和栈缓冲区 |
+
+## Experiment
+
+### Requirements
+
+- P01:动态创建 3 个任务,task1 翻转 LED1,task2 翻转 LED2,task3 检测按键删除 task1
+- P02:使用静态创建实现同样功能,并提供空闲任务和定时器任务的内存
+
+### Code Implementation
+
+#### P01:动态创建
+
+**File: 01_任务创建与删除_CubeMX/Core/Src/freertos_demo.c**
+
+```c
+#include "freertos_demo.h"
+#include "FreeRTOS.h"
+#include "task.h"
+#include "LED.h"
+#include "Key.h"
+
+/* 起始任务配置 */
+#define START_TASK_STACK 128
+#define START_TASK_PRIORITY 1
+TaskHandle_t start_task_handle;
+void start_task(void *pvParameters);
+
+/* 任务1配置 */
+#define TASK1_STACK 128
+#define TASK1_PRIORITY 2
+TaskHandle_t task1_handle;
+void task1(void *pvParameters);
+
+/* 任务2配置 */
+#define TASK2_STACK 128
+#define TASK2_PRIORITY 3
+TaskHandle_t task2_handle;
+void task2(void *pvParameters);
+
+/* 任务3配置 */
+#define TASK3_STACK 128
+#define TASK3_PRIORITY 4
+TaskHandle_t task3_handle;
+void task3(void *pvParameters);
+
+/**
+ * @description: 启动FreeRTOS
+ */
+void freertos_start(void)
+{
+    /* 1. 创建起始任务(所有任务在起始任务中统一创建) */
+    xTaskCreate((TaskFunction_t)start_task,               // 任务函数指针
+                (char *)"start_task",                     // 任务名称(用于调试标识)
+                (configSTACK_DEPTH_TYPE)START_TASK_STACK, // 栈空间大小 128 word × 4 = 512 字节
+                (void *)NULL,                             // 传递给任务的参数
+                (UBaseType_t)START_TASK_PRIORITY,         // 任务优先级
+                (TaskHandle_t *)&start_task_handle);      // 返回任务句柄
+
+    /* 2. 启动调度器:将CPU控制权交给FreeRTOS */
+    vTaskStartScheduler();
+}
+
+/**
+ * @description: 起始任务函数,负责创建其他任务后自我删除
+ */
+void start_task(void *pvParameters)
+{
+    /* 进入临界区:防止创建任务过程中被中断干扰 */
+    taskENTER_CRITICAL();
+
+    /* 创建3个实际工作任务 */
+    xTaskCreate((TaskFunction_t)task1,
+                (char *)"task1",
+                (configSTACK_DEPTH_TYPE)TASK1_STACK,
+                (void *)NULL,
+                (UBaseType_t)TASK1_PRIORITY,
+                (TaskHandle_t *)&task1_handle);
+    xTaskCreate((TaskFunction_t)task2,
+                (char *)"task2",
+                (configSTACK_DEPTH_TYPE)TASK2_STACK,
+                (void *)NULL,
+                (UBaseType_t)TASK2_PRIORITY,
+                (TaskHandle_t *)&task2_handle);
+    xTaskCreate((TaskFunction_t)task3,
+                (char *)"task3",
+                (configSTACK_DEPTH_TYPE)TASK3_STACK,
+                (void *)NULL,
+                (UBaseType_t)TASK3_PRIORITY,
+                (TaskHandle_t *)&task3_handle);
+
+    /* 删除起始任务自身:传NULL代表删除当前运行的任务 */
+    vTaskDelete(NULL);
+
+    /* 退出临界区 */
+    taskEXIT_CRITICAL();
+}
+
+/**
+ * @description: 任务1——翻转LED1,周期500ms
+ */
+void task1(void *pvParameters)
+{
+    while (1)
+    {
+        printf("task1正在运行...\r\n");
+        LED_Toggle(LED1_Pin);          // 翻转LED1
+        vTaskDelay(500);               // 阻塞延时500ms,让出CPU
+    }
+}
+
+/**
+ * @description: 任务2——翻转LED2,周期500ms
+ */
+void task2(void *pvParameters)
+{
+    while (1)
+    {
+        printf("task2正在运行...\r\n");
+        LED_Toggle(LED2_Pin);          // 翻转LED2
+        vTaskDelay(500);               // 阻塞延时500ms,让出CPU
+    }
+}
+
+/**
+ * @description: 任务3——检测按键,按下KEY1则删除task1
+ */
+void task3(void *pvParameters)
+{
+    uint8_t key = 0;
+    while (1)
+    {
+        printf("task3正在运行...\r\n");
+        key = Key_Detect();
+        if (key == KEY1_PRESS)
+        {
+            /* 确保task1句柄有效 */
+            if (task1_handle != NULL)
+            {
+                /* KEY1按下则删除task1 */
+                printf("正在删除Task1....\r\n");
+                vTaskDelete(task1_handle);       // 删除指定任务
+                task1_handle = NULL;             // 句柄置空防止误用
+            }
+        }
+        vTaskDelay(500);
+        // HAL_Delay(500);   // 注意:HAL_Delay是忙等待,会占用CPU
+    }
+}
+```
+
+#### P02:静态创建
+
+**File: 02_任务创建与删除_静态创建/Core/Src/freertos_demo.c**
+
+```c
+#include "freertos_demo.h"
+#include "FreeRTOS.h"
+#include "task.h"
+#include "LED.h"
+#include "Key.h"
+
+/* 任务栈和TCB变量全部由用户预先定义 */
+#define START_TASK_STACK 128
+StackType_t start_task_stack[START_TASK_STACK];  // 用户分配的栈空间
+StaticTask_t start_task_tcb;                      // 用户分配的TCB空间
+TaskHandle_t start_task_handle;
+
+#define TASK1_STACK 128
+StackType_t task1_stack[TASK1_STACK];
+StaticTask_t task1_tcb;
+TaskHandle_t task1_handle;
+
+#define TASK2_STACK 128
+StackType_t task2_stack[TASK2_STACK];
+StaticTask_t task2_tcb;
+TaskHandle_t task2_handle;
+
+#define TASK3_STACK 128
+StackType_t task3_stack[TASK3_STACK];
+StaticTask_t task3_tcb;
+TaskHandle_t task3_handle;
+
+/* ============= 系统任务内存分配(静态分配时必须提供)=============== */
+StackType_t idle_task_stack[configMINIMAL_STACK_SIZE];
+StaticTask_t idle_task_tcb;
+
+StackType_t timer_task_stack[configTIMER_TASK_STACK_DEPTH];
+StaticTask_t timer_task_tcb;
+
+/* 提供空闲任务的栈和TCB */
+void vApplicationGetIdleTaskMemory(StaticTask_t **ppxIdleTaskTCBBuffer,
+                                   StackType_t **ppxIdleTaskStackBuffer,
+                                   uint32_t *pulIdleTaskStackSize)
+{
+    *ppxIdleTaskTCBBuffer = &idle_task_tcb;
+    *ppxIdleTaskStackBuffer = idle_task_stack;
+    *pulIdleTaskStackSize = configMINIMAL_STACK_SIZE;
+}
+
+/* 提供定时器任务的栈和TCB */
+void vApplicationGetTimerTaskMemory(StaticTask_t **ppxTimerTaskTCBBuffer,
+                                    StackType_t **ppxTimerTaskStackBuffer,
+                                    uint32_t *pulTimerTaskStackSize)
+{
+    *ppxTimerTaskTCBBuffer = &timer_task_tcb;
+    *ppxTimerTaskStackBuffer = timer_task_stack;
+    *pulTimerTaskStackSize = configTIMER_TASK_STACK_DEPTH;
+}
+
+void freertos_start(void)
+{
+    /* 静态创建起始任务:需要用户提供栈和TCB缓冲区 */
+    start_task_handle = xTaskCreateStatic(
+        (TaskFunction_t)start_task,
+        (char *)"start_task",
+        (uint32_t)START_TASK_STACK,
+        (void *)NULL,
+        (UBaseType_t)START_TASK_PRIORITY,
+        (StackType_t *)start_task_stack,   // 用户预分配的栈
+        (StaticTask_t *)&start_task_tcb    // 用户预分配的TCB
+    );
+
+    vTaskStartScheduler();
+}
+
+void start_task(void *pvParameters)
+{
+    taskENTER_CRITICAL();
+
+    /* 静态创建3个工作任务 */
+    task1_handle = xTaskCreateStatic(
+        (TaskFunction_t)task1, (char *)"task1",
+        (configSTACK_DEPTH_TYPE)TASK1_STACK, (void *)NULL,
+        (UBaseType_t)TASK1_PRIORITY,
+        (StackType_t *)task1_stack, (StaticTask_t *)&task1_tcb
+    );
+    task2_handle = xTaskCreateStatic(
+        (TaskFunction_t)task2, (char *)"task2",
+        (configSTACK_DEPTH_TYPE)TASK2_STACK, (void *)NULL,
+        (UBaseType_t)TASK2_PRIORITY,
+        (StackType_t *)task2_stack, (StaticTask_t *)&task2_tcb
+    );
+    task3_handle = xTaskCreateStatic(
+        (TaskFunction_t)task3, (char *)"task3",
+        (configSTACK_DEPTH_TYPE)TASK3_STACK, (void *)NULL,
+        (UBaseType_t)TASK3_PRIORITY,
+        (StackType_t *)task3_stack, (StaticTask_t *)&task3_tcb
+    );
+
+    vTaskDelete(NULL);
+    taskEXIT_CRITICAL();
+}
+/* task1/task2/task3 与动态创建版本相同 */
+```
+
+### Key Config
+
+**P01 FreeRTOSConfig.h**(默认配置,无需特殊设置):
+
+```c
+// 默认配置已启用 vTaskDelete
+#define INCLUDE_vTaskDelete  1
+```
+
+**P02 FreeRTOSConfig.h**(静态分配额外配置):
+
+```c
+#define configSUPPORT_STATIC_ALLOCATION  1    // 启用静态分配
+#define configUSE_TIMERS                 1    // 启用软件定时器(产生定时器任务)
+#define configTIMER_TASK_PRIORITY        4    // 定时器任务优先级
+#define configTIMER_QUEUE_LENGTH         5    // 定时器命令队列长度
+#define configTIMER_TASK_STACK_DEPTH     256  // 定时器任务栈大小
+```
+
+## Core API Reference Table
+
+| Function | Parameters | Return | Description |
+|----------|-----------|--------|-------------|
+| `xTaskCreate` | pvTaskCode, pcName, usStackDepth, pvParameters, uxPriority, pxCreatedTask | BaseType_t | 动态创建任务 |
+| `xTaskCreateStatic` | pvTaskCode, pcName, ulStackDepth, pvParameters, uxPriority, puxStackBuffer, pxTaskBuffer | TaskHandle_t | 静态创建任务 |
+| `vTaskDelete` | xTaskToDelete | void | 删除任务(NULL=自身) |
+| `vTaskStartScheduler` | 无 | void | 启动调度器 |
+| `vApplicationGetIdleTaskMemory` | ppxIdleTaskTCBBuffer, ppxIdleTaskStackBuffer, pulIdleTaskStackSize | void | 提供空闲任务内存(静态分配时) |
+| `vApplicationGetTimerTaskMemory` | ppxTimerTaskTCBBuffer, ppxTimerTaskStackBuffer, pulTimerTaskStackSize | void | 提供定时器任务内存(静态分配时) |
+
+## Common Issues & Pitfalls
+
+1. **栈溢出**:栈大小设置不当会导致任务异常。调试时可设置 `configCHECK_FOR_STACK_OVERFLOW` 为 1 或 2,并实现 `vApplicationStackOverflowHook()`。
+2. **删除后句柄未置空**:删除任务后其句柄仍指向原 TCB 地址,后续误用会导致崩溃。应在删除后立即将句柄置 `NULL`。
+3. **动态创建失败**:堆空间不足时 `xTaskCreate` 返回 `errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY`。增大 `configTOTAL_HEAP_SIZE` 或改用静态创建。
+4. **静态创建必须提供系统任务内存**:设置 `configSUPPORT_STATIC_ALLOCATION = 1` 后,必须实现 `vApplicationGetIdleTaskMemory`,若启用定时器还必须实现 `vApplicationGetTimerTaskMemory`。
+5. **vTaskDelete(NULL) 后需要切换**:删除自身时,函数末尾会自动触发一次任务切换,因此无需手动调用 `taskYIELD()`。
+6. **临界区内不要使用阻塞操作**:`taskENTER_CRITICAL()` / `taskEXIT_CRITICAL()` 之间不应调用 `vTaskDelay()` 等可能导致阻塞的 API。

+ 0 - 309
X-Knowledge-Base/raw/Joplin/嵌入式+Linux/FreeRTOS学习笔记/05-任务创建删除与挂起恢复.md

@@ -1,309 +0,0 @@
----
-tags: [source-summary]
-type: source
-source: "尚硅谷嵌入式技术之FreeRTOS实时操作系统 V1.0.3 — 第5~6章 + 配套代码01/02/03/06"
-author: "尚硅谷研究院"
-date: 2026-07-12
-url: ""
-created: 2026-07-12
----
-
-# 任务创建、删除、挂起与恢复
-
-> **用生活理解**:可以把 RTOS 想象成一个"公司",每个任务就是一个"员工"。创建任务就是招人,删除就是开除,挂起就是让员工休假(随时能叫回来),调度器就是 CEO —— 决定谁在什么时候干活。
-
-## API 速查
-
-| 函数 | 作用 | 通俗理解 |
-|------|------|---------|
-| `xTaskCreate()` | 动态创建任务 | 公司招人,HR 帮忙找工位(内存) |
-| `xTaskCreateStatic()` | 静态创建任务 | 公司招人,但工位你自己提前准备好 |
-| `vTaskDelete()` | 删除任务 | 开除员工,工位回收 |
-| `vTaskSuspend()` | 挂起任务 | 让员工休假,保留工位 |
-| `vTaskResume()` | 恢复任务 | 休假员工叫回来上班 |
-| `vTaskSuspendAll()` | 暂停调度器 | CEO 说"都别动,听我讲两句" |
-| `xTaskResumeAll()` | 恢复调度器 | CEO 讲完了,大家继续工作 |
-
----
-
-## 示例 1:动态创建和删除任务(项目 01)
-
-### 情景
-有 4 个任务:
-- **start_task**(主管):负责招人(创建 task1/2/3),招完就辞职(删除自己)
-- **task1**(员工1):每 500ms 闪一次 LED1
-- **task2**(员工2):每 500ms 闪一次 LED2
-- **task3**(员工3):按 KEY1 就开除 task1
-
-### 核心代码
-
-```c
-#include "FreeRTOS.h"
-#include "task.h"
-
-/* ========== 任务配置:相当于每个员工的工牌编号 ========== */
-#define START_TASK_STACK 128      // 主管的办公桌大小(128个Word=512字节)
-#define START_TASK_PRIORITY 1     // 优先级1(数字越小优先级越低)
-TaskHandle_t start_task_handle;   // 主管的工牌(句柄)
-
-#define TASK1_STACK 128
-#define TASK1_PRIORITY 2          // 比主管高一点
-TaskHandle_t task1_handle;
-
-#define TASK2_STACK 128
-#define TASK2_PRIORITY 3
-TaskHandle_t task2_handle;
-
-#define TASK3_STACK 128
-#define TASK3_PRIORITY 4          // 最高优先级
-TaskHandle_t task3_handle;
-
-/* ========== 启动 FreeRTOS ========== */
-void freertos_start(void)
-{
-    // 第一步:创建主管任务(start_task)
-    xTaskCreate(
-        (TaskFunction_t)start_task,          // 任务函数 = 员工要干的活
-        (char *)"start_task",                // 任务名字(调试用)
-        (configSTACK_DEPTH_TYPE)START_TASK_STACK, // 栈大小(办公桌尺寸)
-        (void *)NULL,                        // 传给任务的参数
-        (UBaseType_t)START_TASK_PRIORITY,    // 优先级
-        (TaskHandle_t *)&start_task_handle); // 工牌(句柄)
-
-    // 第二步:启动调度器 = CEO 开始上班
-    vTaskStartScheduler();
-    // 注意:vTaskStartScheduler 不会返回!
-    // CEO 一旦开始工作,就永远不停止
-}
-
-/* ========== 主管任务 ========== */
-void start_task(void *pvParameters)
-{
-    taskENTER_CRITICAL();  // 进入临界区 = "关门开大会,谁都不许打扰"
-
-    // 主管负责创建三个员工
-    xTaskCreate((TaskFunction_t)task1, (char *)"task1",
-                (configSTACK_DEPTH_TYPE)TASK1_STACK, NULL,
-                (UBaseType_t)TASK1_PRIORITY, (TaskHandle_t *)&task1_handle);
-    xTaskCreate((TaskFunction_t)task2, (char *)"task2",
-                (configSTACK_DEPTH_TYPE)TASK2_STACK, NULL,
-                (UBaseType_t)TASK2_PRIORITY, (TaskHandle_t *)&task2_handle);
-    xTaskCreate((TaskFunction_t)task3, (char *)"task3",
-                (configSTACK_DEPTH_TYPE)TASK3_STACK, NULL,
-                (UBaseType_t)TASK3_PRIORITY, (TaskHandle_t *)&task3_handle);
-
-    // 主管的使命完成,辞职(删除自己)
-    vTaskDelete(NULL);  // NULL = 删除调用者自己
-
-    taskEXIT_CRITICAL();  // 退出临界区
-}
-
-/* ========== task1:每500ms闪一次LED1 ========== */
-void task1(void *pvParameters)
-{
-    while (1)  // 所有任务基本都是死循环(不停干活)
-    {
-        LED_Toggle(LED1_Pin);  // 翻转LED1
-        vTaskDelay(500);       // 延时500ms = "睡500ms,让出CPU"
-    }
-}
-
-/* ========== task2:每500ms闪一次LED2 ========== */
-void task2(void *pvParameters)
-{
-    while (1)
-    {
-        LED_Toggle(LED2_Pin);
-        vTaskDelay(500);
-    }
-}
-
-/* ========== task3:按KEY1删除task1 ========== */
-void task3(void *pvParameters)
-{
-    uint8_t key = 0;
-    while (1)
-    {
-        key = Key_Detect();       // 读按键
-        if (key == KEY1_PRESS)
-        {
-            if (task1_handle != NULL)  // 防止重复删除
-            {
-                vTaskDelete(task1_handle);  // 删除task1
-                task1_handle = NULL;        // 标记已删除
-            }
-        }
-        vTaskDelay(500);
-    }
-}
-```
-
-### 关键点理解
-
-**vTaskDelay vs HAL_Delay**:
-- `vTaskDelay(500)`:告诉调度器"我睡500ms,这期间让别人干活"——**主动让出CPU**
-- `HAL_Delay(500)`:死等500ms,期间CPU空转——**浪费CPU**
-- 在 RTOS 中**永远用 `vTaskDelay`** 而不是 `HAL_Delay`!
-
-**vTaskDelete(NULL)**:传入 NULL 表示删除自己。任务执行完后如果不删除自己,调度器会一直给它分配时间片。
-
----
-
-## 示例 2:静态创建任务(项目 02)
-
-### 动态 vs 静态的区别
-| 方式 | 通俗理解 | 内存谁管 |
-|------|---------|---------|
-| 动态创建 | 你只管招人,工位(内存)让操作系统去准备 | FreeRTOS 从堆里自动分配 |
-| 静态创建 | 你招人,还得自己给他准备好工位 | 用户提前声明数组和结构体 |
-
-### 静态创建的特殊要求
-
-```c
-// ======== 必须提前准备的"硬件资源" ========
-StackType_t task1_stack[TASK1_STACK];  // 手动准备办公桌(栈空间)
-StaticTask_t task1_tcb;                // 手动准备工牌(TCB结构体)
-
-// ======== 空闲任务也需要手动准备! ========
-StackType_t idle_task_stack[configMINIMAL_STACK_SIZE];
-StaticTask_t idle_task_tcb;
-
-// FreeRTOS 会通过这个接口找你要空闲任务的资源
-void vApplicationGetIdleTaskMemory(
-    StaticTask_t **ppxIdleTaskTCBBuffer,
-    StackType_t **ppxIdleTaskStackBuffer,
-    uint32_t *pulIdleTaskStackSize)
-{
-    *ppxIdleTaskTCBBuffer = &idle_task_tcb;       // "空闲任务的工牌在这儿"
-    *ppxIdleTaskStackBuffer = idle_task_stack;     // "空闲任务的办公桌在这儿"
-    *pulIdleTaskStackSize = configMINIMAL_STACK_SIZE;
-}
-
-// ======== 静态创建任务 ========
-void freertos_start(void)
-{
-    start_task_handle = xTaskCreateStatic(
-        (TaskFunction_t)start_task,
-        (char *)"start_task",
-        (uint32_t)START_TASK_STACK,
-        (void *)NULL,
-        (UBaseType_t)START_TASK_PRIORITY,
-        (StackType_t *)start_task_stack,     // ⚠️ 把准备好的栈传进去
-        (StaticTask_t *)&start_task_tcb);    // ⚠️ 把准备好的TCB传进去
-    vTaskStartScheduler();
-}
-```
-
-### 为什么要静态创建?
-- **资源受限的系统**:内存很小,不允许动态分配
-- **安全关键系统**:不允许 malloc 失败的情况
-- **确定性的需要**:静态分配在编译时就确定了内存布局
-
----
-
-## 示例 3:任务挂起与恢复(项目 03)
-
-### 情景
-4 个按键分别对应不同操作:
-- **KEY1** → 挂起 task1(让 task1 休假)
-- **KEY2** → 恢复 task1(叫 task1 回来上班)
-- **KEY3** → 挂起调度器(CEO 说全部暂停!)
-- **KEY4** → 恢复调度器(CEO 说继续干活!)
-
-### 核心代码
-
-```c
-void task3(void *pvParameters)
-{
-    uint8_t key = 0;
-    while (1)
-    {
-        key = Key_Detect();
-        if (key == KEY1_PRESS)
-        {
-            vTaskSuspend(task1_handle);  // 让 task1 休假
-        }
-        else if (key == KEY2_PRESS)
-        {
-            vTaskResume(task1_handle);   // 叫 task1 回来
-        }
-        else if (key == KEY3_PRESS)
-        {
-            vTaskSuspendAll();           // CEO:全部暂停!
-        }
-        else if (key == KEY4_PRESS)
-        {
-            xTaskResumeAll();            // CEO:继续工作!
-        }
-        vTaskDelay(500);
-    }
-}
-```
-
-### 挂起 vs 阻塞 vs 删除
-| 状态 | 怎么进去 | 怎么出来 | 还占内存吗 |
-|------|---------|---------|-----------|
-| **挂起态** | `vTaskSuspend()` | `vTaskResume()` | 占着,随时恢复 |
-| **阻塞态** | `vTaskDelay()` 或等队列/信号量 | 时间到或资源可用 | 占着 |
-| **删除态** | `vTaskDelete()` | 回不来了 | 回收了 |
-
-> **打个比方**:
-> - 挂起 = 员工长期休假(工位保留,**被动**——领导让休的)
-> - 阻塞 = 员工去茶水间接水(一会儿就回来,**主动**行为)
-> - 删除 = 员工被开除了(工位回收,回不来了)
-
-### vTaskSuspendAll 的特殊性
-`vTaskSuspendAll()` 只是暂停任务切换,**但中断不受影响**。这就像 CEO 说"都别动",但快递员(中断)来了照样可以送快递。而 `taskENTER_CRITICAL()` 是直接把公司大门锁了,连快递都进不来。
-
----
-
-## 示例 4:任务状态查询(项目 06)
-
-### 关键 API 理解
-
-```c
-// 获取任务优先级 = 查这个员工的职级
-UBaseType_t prio = uxTaskPriorityGet(task1_handle);
-
-// 设置任务优先级 = 给员工升职
-vTaskPrioritySet(task1_handle, 4);
-
-// 获取系统中任务总数 = 统计公司有多少员工
-UBaseType_t count = uxTaskGetNumberOfTasks();
-
-// 获取所有任务状态 = 打印花名册
-TaskStatus_t status[10];
-uxTaskGetSystemState(status, count, NULL);
-for (i = 0; i < count; i++) {
-    printf("任务名:%s 编号:%d 状态:%d 优先级:%d\r\n",
-           status[i].pcTaskName,
-           status[i].xTaskNumber,
-           status[i].eCurrentState,    // 0=运行 1=就绪 2=阻塞 3=挂起
-           status[i].uxCurrentPriority);
-}
-
-// 获取栈历史剩余最小值 = 检查办公桌还剩多少空间
-// 用来判断任务栈是否设得太大(浪费)或太小(会溢出)
-UBaseType_t remain = uxTaskGetStackHighWaterMark(task2_handle);
-// 如果 remain ≈ 0,说明栈快爆了,得加大 STACK 值
-
-// 以表格形式打印所有任务状态
-char info[500];
-vTaskList(info);
-printf("%s\r\n", info);
-// 输出示例:
-// Task Name   State  Priority  Stack  Num
-// task1       X      2         45     1
-// task2       B      3         120    2
-// IDLE        R      0         80     3
-// 状态: X=运行  B=阻塞  R=就绪  S=挂起  D=删除
-```
-
-### 配套代码
-- `code/01_动态创建任务和删除/`
-- `code/02_静态创建任务和删除/`
-- `code/03_任务挂起与恢复/`
-- `code/06_任务状态查询/`
-- `code/07_任务时间统计/`
-
-> 后续章节:[[06-中断管理与时间管理]]

+ 0 - 219
X-Knowledge-Base/raw/Joplin/嵌入式+Linux/FreeRTOS学习笔记/06-中断管理与时间管理.md

@@ -1,219 +0,0 @@
----
-tags: [source-summary]
-type: source
-source: "尚硅谷嵌入式技术之FreeRTOS实时操作系统 V1.0.3 — 第7、9章 + 配套代码04/05/08"
-author: "尚硅谷研究院"
-date: 2026-07-12
-url: ""
-created: 2026-07-12
----
-
-# 中断管理与时间管理
-
-> **用生活理解**:中断就像你在写代码时,突然有人打电话找你。如果你接电话(响应中断),当前写代码的工作就被"打断"了。FreeRTOS 的中断管理就是决定:谁的电话必须接(高优先级中断),谁的电话可以稍等(低优先级中断被屏蔽)。
-
-## BASEPRI 中断管理
-
-STM32 的中断优先级 0~15(0 最高,15 最低)。FreeRTOS 用 **BASEPRI 寄存器** 设置一个"门槛":
-
-```
-BASEPRI = 5(实际存的是0x50)
-    ↓
-中断优先级 0~4  → 正常响应(这些是紧急的)
-中断优先级 5~15 → 全部屏蔽(这些可以等)
-```
-
-![BASEPRI 中断管理示意图:阈值 5 屏蔽优先级 5~15 的中断](assets/basepri_interrupt_management.png)
-
-就像公司前台:优先级 0~4 是老板的电话,必须立刻接通;优先级 5~15 是推销电话,先不接。
-
-## 示例 1:开关中断实验(项目 04)
-
-### 情景
-- 两个定时器:TIM2(优先级=4,在门槛之上)和 TIM3(优先级=6,在门槛之下)
-- task1:按 KEY1 关中断,按 KEY2 开中断
-- 观察:关中断后 TIM3 停止打印(被屏蔽了),但 TIM2 仍在工作
-
-### 核心代码
-
-```c
-void task1(void *pvParameters)
-{
-    uint8_t key = 0;
-    while (1)
-    {
-        key = Key_Detect();
-        if (key == KEY1_PRESS)
-        {
-            // 关中断:BASEPRI = 5<<4 = 0x50
-            // 屏蔽优先级 5~15 的中断
-            portDISABLE_INTERRUPTS();
-            printf(">>>>关了中断,TIM3(优先级6)被屏蔽了\r\n");
-        }
-        else if (key == KEY2_PRESS)
-        {
-            // 开中断:BASEPRI = 0
-            // 所有中断都能响应
-            portENABLE_INTERRUPTS();
-            printf(">>>>开了中断,TIM3又能工作了\r\n");
-        }
-        // 注意:不能用 vTaskDelay!因为 vTaskDelay 底层也会开关中断
-        // 改用 HAL_Delay(前提是 HAL 时基用别的定时器)
-        HAL_Delay(500);
-    }
-}
-```
-
-### 中断配置要点
-
-```c
-// FreeRTOSConfig.h 中的关键配置
-// 内核自身使用的中断优先级(设为最低,避免屏蔽其他中断)
-#define configKERNEL_INTERRUPT_PRIORITY        (15 << 4)
-
-// FreeRTOS 管理的最高中断优先级(门槛值)
-// 优先级小于 5 的中断不受 FreeRTOS 管理
-#define configMAX_SYSCALL_INTERRUPT_PRIORITY   (5 << 4)
-```
-
----
-
-## 示例 2:时间片调度(项目 05)
-
-### 核心概念
-
-时间片 = 每个任务能连续运行的最长时间(一个 SysTick 周期)。
-同优先级任务轮流干活,就像"轮班制"——每人干一个时间片,时间到了换下一个人。
-
-**配置方法**:
-```c
-// FreeRTOSConfig.h
-#define configUSE_TIME_SLICING    1   // 开启时间片轮转
-#define configUSE_PREEMPTION      1   // 开启抢占式调度
-#define configTICK_RATE_HZ       20   // 每秒20个tick = 每个时间片50ms
-```
-
-### 演示代码
-
-```c
-// task1 和 task2 优先级相同(都是2),会轮流执行
-#define TASK1_PRIORITY 2
-#define TASK2_PRIORITY 2  // ⚠️ 相同优先级!
-
-void task1(void *pvParameters)
-{
-    uint16_t count = 0;
-    while (1)
-    {
-        taskENTER_CRITICAL();  // 防止 printf 被中途打断
-        printf("task1第[%d]次运行\r\n", ++count);
-        HAL_Delay(10);  // 模拟干活(不用vTaskDelay,因为想看到切换)
-        taskEXIT_CRITICAL();
-    }
-}
-
-void task2(void *pvParameters)
-{
-    uint16_t count = 0;
-    while (1)
-    {
-        taskENTER_CRITICAL();
-        printf("task2第[%d]次运行\r\n", ++count);
-        HAL_Delay(10);
-        taskEXIT_CRITICAL();
-    }
-}
-```
-
-> **为什么这里用 HAL_Delay 而不是 vTaskDelay?**
-> 因为 vTaskDelay 会让任务进入阻塞态主动让出 CPU,我们想观察的是"时间片到了被迫让出"的效果。
-
----
-
-## 示例 3:相对延时 vs 绝对延时(项目 08)
-
-### 理解两者的区别
-
-**`vTaskDelay(500)` — 相对延时:**
-```
-    任务开始执行 ─→ vTaskDelay(500) ─→ 睡500ms ─→ 醒来继续
-    就像说"从现在开始算,我睡500ms"
-    如果你干活花了 10ms,下次醒来是 510ms 后
-```
-
-**`xTaskDelayUntil()` — 绝对延时:**
-```
-    任务开始执行 ─→ vTaskDelayUntil(500) ─→ 睡到固定时间点
-    就像说"不管我这次干了多久,下次准时在整秒时刻醒来"
-    周期是固定的!
-```
-
-![相对延时 vs 绝对延时:相对延时累积误差,绝对延时周期固定](assets/delay_relative_vs_absolute.png)
-
-用 LED 波形观察最直观:
-- **相对延时**:LED 亮的时长 = 固定延时 + 干活时间(累计误差)
-- **绝对延时**:LED 亮的时长 = 严格固定(适合做采样、数据采集)
-
-### 演示代码
-
-```c
-void task1(void *pvParameters)
-{
-    while (1)
-    {
-        LED_Toggle(LED1_Pin);  // 翻转LED1
-        HAL_Delay(20);          // 模拟干了20ms的活
-        vTaskDelay(500);        // 再睡500ms
-        // 实际周期 = 20ms + 500ms = 520ms(误差累积)
-    }
-}
-
-void task2(void *pvParameters)
-{
-    TickType_t last_wake = xTaskGetTickCount();  // 记录第一次醒来的时间
-    while (1)
-    {
-        LED_Toggle(LED2_Pin);
-        HAL_Delay(20);          // 模拟干了20ms的活
-        vTaskDelayUntil(&last_wake, 500);  // 睡到"上次醒来时间 + 500ms"
-        // 实际周期 = 固定500ms(干活时间不计入)
-        // 即使这次多干了一会儿,下次也会准时
-    }
-}
-```
-
-| | vTaskDelay | xTaskDelayUntil |
-|---|---|---|
-| 周期 | 不固定(随干活时间漂移) | **固定** |
-| 适用场景 | 普通延时(LED闪烁) | 定时采样、PWM控制、通信时序 |
-| 误差 | 累积误差 | 只有单次抖动,不累积 |
-
-### 配套代码
-- `code/04_中断管理/`
-- `code/05_时间片调度/`
-- `code/08_时间延时函数/`
-
----
-
-## 补充:PendSV 与 SysTick 中断设计哲学
-
-### PendSV(上下文切换中断)
-
-**为什么选择 PendSV?**
-- PendSV 是一个系统级异常,可以**通过写寄存器手动触发**(ICSR bit28)
-- 可以**在其他 ISR 中设置**——Tick ISR 发现需要切换时,只需"挂起 PendSV"然后继续执行,等 Tick ISR 退出后再真正切换
-- 被设为**最低中断优先级**,避免任务切换干扰 UART、定时器、SPI 等外设中断
-
-**Tick ISR 的完整工作流程**:
-1. tick 计数加 1
-2. 遍历阻塞列表,找出到期任务并移至就绪列表
-3. 检查就绪列表是否需要切换(更高优先级就绪 / 同优先级时间片用完)
-4. 如果需要切换,挂起 PendSV(等 Tick ISR 退出后再切,不抢占当前中断)
-
-**主动切换**:任务提前结束时(如调用 `vTaskDelay()`),直接主动调用任务切换函数,无需等 tick 中断——这就是"时间片提前结束不浪费"的实现机制。
-
-### SysTick 的两个作用
-1. 为时间片轮转提供基准时钟
-2. 为阻塞列表中的任务提供超时计时
-
-> 后续章节:[[07-消息队列与队列集]]

+ 232 - 0
X-Knowledge-Base/raw/Joplin/嵌入式+Linux/FreeRTOS学习笔记/06-任务挂起与恢复.md

@@ -0,0 +1,232 @@
+---
+tags: [source-summary]
+type: source
+source: "尚硅谷嵌入式技术之FreeRTOS实时操作系统 V1.0.3 — 第5章 任务挂起与恢复"
+author: "尚硅谷研究院"
+date: 2026-07-17
+created: 2026-07-17
+---
+
+# 任务挂起与恢复
+
+> **用生活理解**:挂起任务就像把员工暂时调离岗位(保留工位和编制),不需要时让他歇着;恢复就像把他叫回来上班。而挂起调度器则像是整个公司暂停运转,所有员工原地待命。
+
+## Concept
+
+### Basic concept
+
+**挂起(Suspend)** 将任务置于挂起态(Suspended State):
+- 任务暂停运行,调度器不再分配 CPU 时间给该任务
+- 任务在挂起态不会就绪,需要通过 `vTaskResume()` 手动恢复
+- 与阻塞态不同,挂起不存在超时机制,只能由外部显式恢复
+
+### Working principle
+
+FreeRTOS 提供两套挂起/恢复机制:
+
+**1. 单任务挂起/恢复**
+
+| API | 描述 |
+|-----|------|
+| `vTaskSuspend(TaskHandle_t xTaskToSuspend)` | 挂起指定任务 |
+| `vTaskResume(TaskHandle_t xTaskToResume)` | 恢复指定任务 |
+| `xTaskResumeFromISR(TaskHandle_t xTaskToResume)` | 从中断中恢复任务(中断安全版本) |
+
+**2. 调度器级挂起/恢复**
+
+| API | 描述 |
+|-----|------|
+| `vTaskSuspendAll()` | 挂起整个调度器,所有任务停止切换 |
+| `xTaskResumeAll()` | 恢复调度器,返回 `pdTRUE` 或 `pdFALSE` |
+
+### 三种状态对比
+
+| 操作 | 挂起 | 阻塞 | 删除 |
+|------|------|------|------|
+| 任务存在 | ✅ | ✅ | ❌ |
+| 可自动恢复 | ❌ | ✅(超时后) | ❌ |
+| 资源释放 | ❌ | ❌ | ✅ |
+| 恢复方式 | 手动 resume | 超时/事件触发 | 需重新创建 |
+
+## Experiment
+
+### Requirements
+
+- 创建 3 个任务,task1/task2 各自翻转到 LED,task3 检测按键:
+  - KEY1 → `vTaskSuspend(task1)` 挂起 task1
+  - KEY2 → `vTaskResume(task1)` 恢复 task1
+  - KEY3 → `vTaskSuspendAll()` 挂起调度器
+  - KEY4 → `xTaskResumeAll()` 恢复调度器
+  - 每次按键后调用 `vTaskList()` 打印所有任务状态
+
+### Code Implementation
+
+**File: 03_任务挂起与恢复/Core/Src/freertos_demo.c**
+
+```c
+#include "freertos_demo.h"
+#include "FreeRTOS.h"
+#include "task.h"
+#include "LED.h"
+#include "Key.h"
+
+/* 起始任务配置 */
+#define START_TASK_STACK 128
+#define START_TASK_PRIORITY 1
+TaskHandle_t start_task_handle;
+void start_task(void *pvParameters);
+
+/* 任务1配置 */
+#define TASK1_STACK 128
+#define TASK1_PRIORITY 2
+TaskHandle_t task1_handle;
+void task1(void *pvParameters);
+
+/* 任务2配置 */
+#define TASK2_STACK 128
+#define TASK2_PRIORITY 3
+TaskHandle_t task2_handle;
+void task2(void *pvParameters);
+
+/* 任务3配置 */
+#define TASK3_STACK 128
+#define TASK3_PRIORITY 4
+TaskHandle_t task3_handle;
+void task3(void *pvParameters);
+
+void freertos_start(void)
+{
+    /* 动态创建起始任务 */
+    xTaskCreate((TaskFunction_t)start_task,
+                (char *)"start_task",
+                (configSTACK_DEPTH_TYPE)START_TASK_STACK,
+                (void *)NULL,
+                (UBaseType_t)START_TASK_PRIORITY,
+                (TaskHandle_t *)&start_task_handle);
+
+    /* 启动调度器 */
+    vTaskStartScheduler();
+}
+
+void start_task(void *pvParameters)
+{
+    /* 进入临界区,防止创建过程被打断 */
+    taskENTER_CRITICAL();
+
+    /* 创建3个任务 */
+    xTaskCreate((TaskFunction_t)task1, (char *)"task1",
+                (configSTACK_DEPTH_TYPE)TASK1_STACK, (void *)NULL,
+                (UBaseType_t)TASK1_PRIORITY, (TaskHandle_t *)&task1_handle);
+    xTaskCreate((TaskFunction_t)task2, (char *)"task2",
+                (configSTACK_DEPTH_TYPE)TASK2_STACK, (void *)NULL,
+                (UBaseType_t)TASK2_PRIORITY, (TaskHandle_t *)&task2_handle);
+    xTaskCreate((TaskFunction_t)task3, (char *)"task3",
+                (configSTACK_DEPTH_TYPE)TASK3_STACK, (void *)NULL,
+                (UBaseType_t)TASK3_PRIORITY, (TaskHandle_t *)&task3_handle);
+
+    /* 删除起始任务自身 */
+    vTaskDelete(NULL);
+
+    taskEXIT_CRITICAL();
+}
+
+void task1(void *pvParameters)
+{
+    while (1)
+    {
+        printf("task1正在运行...\r\n");
+        LED_Toggle(LED1_Pin);
+        vTaskDelay(500);     // 阻塞延时500ms,让出CPU
+    }
+}
+
+void task2(void *pvParameters)
+{
+    while (1)
+    {
+        printf("task2正在运行...\r\n");
+        LED_Toggle(LED2_Pin);
+        vTaskDelay(500);
+    }
+}
+
+char task_info[500];  // 用于保存vTaskList输出的任务信息字符串
+
+/**
+ * @description: 任务3——按键控制挂起/恢复,并打印任务状态
+ *               KEY1=挂起task1  KEY2=恢复task1
+ *               KEY3=挂起调度器  KEY4=恢复调度器
+ */
+void task3(void *pvParameters)
+{
+    uint8_t key = 0;
+    while (1)
+    {
+        printf("task3正在运行...\r\n");
+        key = Key_Detect();
+        if (key == KEY1_PRESS)
+        {
+            /* KEY1按下:挂起task1——task1将停止运行 */
+            printf(">>>>挂起task1....\r\n");
+            vTaskSuspend(task1_handle);
+        }
+        else if (key == KEY2_PRESS)
+        {
+            /* KEY2按下:恢复task1——task1重新进入就绪态 */
+            printf(">>>>恢复task1....\r\n");
+            vTaskResume(task1_handle);
+        }
+        else if (key == KEY3_PRESS)
+        {
+            /* KEY3按下:挂起调度器——所有任务停止切换 */
+            printf("<<<<挂起调度器....\r\n");
+            vTaskSuspendAll();
+        }
+        else if (key == KEY4_PRESS)
+        {
+            /* KEY4按下:恢复调度器——任务切换恢复 */
+            printf("<<<<恢复调度器....\r\n");
+            xTaskResumeAll();
+        }
+
+        /* 打印当前所有任务的状态信息 */
+        vTaskList(task_info);
+        printf("%s\r\n", task_info);
+
+        vTaskDelay(500);
+    }
+}
+```
+
+### Key Config
+
+**FreeRTOSConfig.h**(启用任务状态跟踪功能):
+
+```c
+// 任务挂起/恢复API必须包含
+#define INCLUDE_vTaskSuspend         1
+#define INCLUDE_xResumeFromISR      1
+
+// 任务列表查询支持
+#define configUSE_TRACE_FACILITY               1
+#define configUSE_STATS_FORMATTING_FUNCTIONS   1
+```
+
+## Core API Reference Table
+
+| Function | Parameters | Return | Description |
+|----------|-----------|--------|-------------|
+| `vTaskSuspend` | xTaskToSuspend | void | 挂起指定任务,使其进入挂起态 |
+| `vTaskResume` | xTaskToResume | void | 恢复指定任务,使任务就绪 |
+| `xTaskResumeFromISR` | xTaskToResume | BaseType_t | 从中断中恢复任务,返回 pdTRUE 表示需要任务切换 |
+| `vTaskSuspendAll` | 无 | void | 挂起调度器,禁止任务切换 |
+| `xTaskResumeAll` | 无 | BaseType_t | 恢复调度器,返回 pdTRUE 表示有挂起的切换 |
+| `vTaskList` | pcWriteBuffer | void | 将任务状态写入字符串缓冲区 |
+
+## Common Issues & Pitfalls
+
+1. **挂起调度器时不得调用 FreeRTOS API**:`vTaskSuspendAll()` 之后调用 `vTaskDelay()`、`xQueueSend()` 等会导致断言失败。只在临界代码段中使用。
+2. **挂起任务与调度器挂起的区别**:`vTaskSuspend()` 只针对单个任务,其余任务正常运行;`vTaskSuspendAll()` 影响所有任务,常用于保护短代码段。
+3. **恢复已挂起的任务无效**:多次调用 `vTaskSuspend()` 挂起同一个任务,只需一次 `vTaskResume()` 即可恢复。
+4. **vTaskList() 需要缓冲区足够大**:每个任务约占用 40 字节,根据任务数量计算缓冲区大小。
+5. **从中断中恢复任务**:必须在中断中使用 `xTaskResumeFromISR()` 而非 `vTaskResume()`,并根据返回值判断是否需要 `portYIELD_FROM_ISR()`。

+ 199 - 0
X-Knowledge-Base/raw/Joplin/嵌入式+Linux/FreeRTOS学习笔记/07-中断管理与临界段.md

@@ -0,0 +1,199 @@
+---
+tags: [source-summary]
+type: source
+source: "尚硅谷嵌入式技术之FreeRTOS实时操作系统 V1.0.3 — 第6章 中断管理"
+author: "尚硅谷研究院"
+date: 2026-07-17
+created: 2026-07-17
+---
+
+# 中断管理与临界段
+
+> **用生活理解**:中断就像领导突然敲门安排紧急任务——你得先把手头工作保存好(保存上下文),处理完紧急任务再回来继续。临界区则像"开会时手机静音"——某些关键时刻不允许被打断。
+
+## Concept
+
+### Basic concept
+
+FreeRTOS 需要管理中断以确保系统的实时性和确定性。其核心思路是:
+
+- **临界段(Critical Section)**:完全关闭中断,任何中断都不能打断当前代码
+- **BASEPRI 优先级屏蔽**:只屏蔽优先级低于阈值的中断,高优先级中断仍可响应
+- **中断安全 API(FromISR 后缀)**:可在中断服务函数中安全调用的 API 版本
+
+### Working principle
+
+FreeRTOS 提供两个层次的中断控制:
+
+**1. 临界段保护(完整中断屏蔽)**
+
+| API | 描述 |
+|-----|------|
+| `taskENTER_CRITICAL()` | 进入临界区,关闭所有中断(有嵌套计数) |
+| `taskEXIT_CRITICAL()` | 退出临界区,恢复之前的中断状态 |
+| `taskENTER_CRITICAL_FROM_ISR()` | 从中断中进入临界区 |
+| `taskEXIT_CRITICAL_FROM_ISR()` | 从中断中退出临界区 |
+
+任务级临界区内部维护一个嵌套计数器(uxCriticalNesting):
+- 第一次进入时关闭中断
+- 嵌套进入时仅增加计数
+- 每次退出递减计数,减到 0 时恢复中断
+
+**2. 中断开关(直接操作)**
+
+| API | 描述 |
+|-----|------|
+| `portDISABLE_INTERRUPTS()` | 直接关闭所有中断 |
+| `portENABLE_INTERRUPTS()` | 直接打开所有中断 |
+| 注意:无嵌套保护,不推荐用于临界段 |
+
+**3. BASEPRI 优先级屏蔽**
+
+FreeRTOS 使用 Cortex-M3 的 BASEPRI 寄存器实现优先级屏蔽:
+
+```
+configKERNEL_INTERRUPT_PRIORITY     = 15 << 4   // 内核中断优先级(最低)
+configMAX_SYSCALL_INTERRUPT_PRIORITY = 5 << 4    // API可调用的最高中断优先级
+```
+
+- 中断优先级 >= 5(数值 <= 5<<4)→ 可调用 FreeRTOS API
+- 中断优先级 < 5(数值 > 5<<4)→ 不允许调用 FreeRTOS API
+
+**4. 延后中断处理(Deferred Interrupt Processing)**
+
+在 ISR 中只做最少的处理(记录事件、发送信号量/消息到队列),将耗时逻辑推迟到任务中执行。这是 RTOS 中推荐的中断处理模式。
+
+## Experiment
+
+### Requirements
+
+- task1 检测按键:
+  - KEY1 → `portDISABLE_INTERRUPTS()` 关闭中断(LED1 停止响应)
+  - KEY2 → `portENABLE_INTERRUPTS()` 打开中断(LED1 恢复)
+- 对比 `vTaskDelay()` 和 `HAL_Delay()` 的行为差异
+
+### Code Implementation
+
+**File: 04_中断管理/Core/Src/freertos_demo.c**
+
+```c
+#include "freertos_demo.h"
+#include "FreeRTOS.h"
+#include "task.h"
+#include "LED.h"
+#include "Key.h"
+
+/* 起始任务配置 */
+#define START_TASK_STACK 128
+#define START_TASK_PRIORITY 1
+TaskHandle_t start_task_handle;
+void start_task(void *pvParameters);
+
+/* 任务1配置 */
+#define TASK1_STACK 128
+#define TASK1_PRIORITY 2
+TaskHandle_t task1_handle;
+void task1(void *pvParameters);
+
+void freertos_start(void)
+{
+    /* 动态创建起始任务 */
+    xTaskCreate((TaskFunction_t)start_task,
+                (char *)"start_task",
+                (configSTACK_DEPTH_TYPE)START_TASK_STACK,
+                (void *)NULL,
+                (UBaseType_t)START_TASK_PRIORITY,
+                (TaskHandle_t *)&start_task_handle);
+
+    /* 启动调度器 */
+    vTaskStartScheduler();
+}
+
+void start_task(void *pvParameters)
+{
+    /* 进入临界区,防止创建过程被打断 */
+    taskENTER_CRITICAL();
+
+    /* 创建1个任务 */
+    xTaskCreate((TaskFunction_t)task1, (char *)"task1",
+                (configSTACK_DEPTH_TYPE)TASK1_STACK, (void *)NULL,
+                (UBaseType_t)TASK1_PRIORITY, (TaskHandle_t *)&task1_handle);
+
+    /* 删除起始任务自身 */
+    vTaskDelete(NULL);
+
+    taskEXIT_CRITICAL();
+}
+
+/**
+ * @description: 任务1——按键控制中断开关
+ *               KEY1 → portDISABLE_INTERRUPTS() 关闭中断
+ *               KEY2 → portENABLE_INTERRUPTS()  打开中断
+ *               使用HAL_Delay模拟工作负载(非vTaskDelay)
+ */
+void task1(void *pvParameters)
+{
+    uint8_t key = 0;
+    while (1)
+    {
+        key = Key_Detect();
+        if (key == KEY1_PRESS)
+        {
+            /* KEY1按下:关闭所有中断——系统将无法响应任何中断 */
+            printf(">>>>关闭中断.....\r\n");
+            portDISABLE_INTERRUPTS();
+        }
+        else if (key == KEY2_PRESS)
+        {
+            /* KEY2按下:打开所有中断 */
+            printf(">>>>打开中断.....\r\n");
+            portENABLE_INTERRUPTS();
+        }
+
+        /* 注意:这里使用HAL_Delay而非vTaskDelay
+           HAL_Delay依赖SysTick中断,如果中断被关闭,HAL_Delay将永远卡死!
+           vTaskDelay不依赖中断,在中断关闭时仍能正常工作 */
+        HAL_Delay(500);
+    }
+}
+```
+
+### Key Config
+
+**FreeRTOSConfig.h**(中断优先级配置):
+
+```c
+/* Cortex-M3 中断优先级配置 */
+/* 内核中断优先级:最低优先级 15<<4 = 240(数值最大,优先级最低) */
+#define configKERNEL_INTERRUPT_PRIORITY       (15 << 4)
+
+/* 系统调用中断优先级:优先级 5 以上的中断可以调用 FreeRTOS API
+   优先级为 0-4 的中断被屏蔽,不能调用 FreeRTOS API
+   数值越小优先级越高:0 > 5 > 15                */
+#define configMAX_SYSCALL_INTERRUPT_PRIORITY  (5 << 4)
+
+/* 别名配置 */
+#define configMAX_API_CALL_INTERRUPT_PRIORITY configMAX_SYSCALL_INTERRUPT_PRIORITY
+```
+
+## Core API Reference Table
+
+| Function | Parameters | Return | Description |
+|----------|-----------|--------|-------------|
+| `taskENTER_CRITICAL()` | 无 | void | 进入临界区(支持嵌套) |
+| `taskEXIT_CRITICAL()` | 无 | void | 退出临界区(嵌套计数减一) |
+| `taskENTER_CRITICAL_FROM_ISR()` | 无 | uint32_t | 从中断进入临界区,返回中断屏蔽状态 |
+| `taskEXIT_CRITICAL_FROM_ISR(ux)` | uxSavedMaskStatus | void | 从中断退出临界区,恢复中断状态 |
+| `portDISABLE_INTERRUPTS()` | 无 | void | 直接关闭所有中断 |
+| `portENABLE_INTERRUPTS()` | 无 | void | 直接打开所有中断 |
+| `portDISABLE_INTERRUPTS_FROM_ISR()` | 无 | UBaseType_t | ISR中关闭中断 |
+| `portENABLE_INTERRUPTS_FROM_ISR(ux)` | uxSavedStatus | void | ISR中恢复中断 |
+
+## Common Issues & Pitfalls
+
+1. **HAL_Delay 在中断关闭后卡死**:`HAL_Delay()` 依赖 SysTick 中断更新 uwTick 变量。如果通过 `portDISABLE_INTERRUPTS()` 关闭了中断,SysTick 无法触发,`HAL_Delay` 将永远死等。
+2. **临界区内不要执行耗时操作**:临界区内所有中断被屏蔽,包括 SysTick。长时间停留在临界区内会破坏系统的实时性。
+3. **嵌套临界区必须配对**:每个 `taskENTER_CRITICAL()` 必须有对应的 `taskEXIT_CRITICAL()`,否则中断永久关闭。
+4. **portDISABLE_INTERRUPTS 无嵌套保护**:与 `taskENTER_CRITICAL()` 不同,`portDISABLE_INTERRUPTS()` 不维护嵌套计数,在嵌套使用时可能会导致提前恢复中断。
+5. **BASEPRI 与 NVIC 优先级的关系**:STM32 使用 4 位优先级(0~15),在 FreeRTOS 中需左移 4 位变为 CM3 的 8 位格式。例如优先级 5 → `5 << 4 = 0x50`。
+6. **ISR 中必须使用 FromISR 版本**:在中断服务函数中调用队列、信号量等 API 时,必须使用带 `FromISR` 后缀的版本,否则可能导致内核数据结构损坏。

+ 0 - 197
X-Knowledge-Base/raw/Joplin/嵌入式+Linux/FreeRTOS学习笔记/07-消息队列与队列集.md

@@ -1,197 +0,0 @@
----
-tags: [source-summary]
-type: source
-source: "尚硅谷嵌入式技术之FreeRTOS实时操作系统 V1.0.3 — 第10、14章 + 配套代码09/14"
-author: "尚硅谷研究院"
-date: 2026-07-12
-url: ""
-created: 2026-07-12
----
-
-# 消息队列与队列集
-
-> **用生活理解**:队列就像公司内部的"传话器"——task1 把消息放进队列,task2 从队列取出消息。就像两个人用一个信箱通信:一个人往里塞信,一个人取信看。**队列集**就像你把多个信箱挂在一起,派一个人守着,哪个信箱有信来了他就取。
-
-## 队列基础
-
-**队列 = 先进先出(FIFO)的消息缓冲区**,线程安全。
-
-### 创建队列
-```c
-QueueHandle_t queue1;
-// 参数1:队列长度(能存几个消息)
-// 参数2:每个消息的大小(单位字节)
-queue1 = xQueueCreate(2, sizeof(uint8_t));
-// 创建一个能存2个消息的队列,每个消息1字节
-// 就像做了一个只能放2封信的信箱,每封信只能写1个字
-```
-
-### 发送消息(入队)
-```c
-uint8_t key = KEY1_PRESS;
-xQueueSend(queue1, &key, portMAX_DELAY);
-// 第3个参数 portMAX_DELAY = "队列满了就死等"
-// 也可以设 0 = "满了就放弃" 或者 100 = "等100个tick"
-```
-
-> **阻塞 / 非阻塞**:就像排队吃饭——队列满了你可以等(设超时),也可以直接走人(超时=0 不等待)。接收同理:队列空了可以等数据来,也可以直接放弃。
-
-### 接收消息(出队)
-```c
-uint8_t receive;
-xQueueReceive(queue1, &receive, portMAX_DELAY);
-// 同样 portMAX_DELAY = "队列空了就死等"
-```
-
-> **Receive vs Peek**:`xQueueReceive()` = **剪切**(从队列中删除数据),`xQueuePeek()` = **复制**(数据保留在队列中,只看不取)。就像看信箱——Receive 是取走信,Peek 是只看看不拿。
->
-> **Send to front**:`xQueueSendToFront()` = **插队**(把消息放到队首),`xQueueSendToBack()` = 正常排队(默认)。
-
-### 小数据 vs 大数据
-
-```c
-// ======== 小数据:直接传值(比如按键值) ========
-uint8_t key = KEY1_PRESS;
-xQueueSend(queue1, &key, portMAX_DELAY);
-// 把key的值复制一份放进队列,原始key怎么变都不影响
-
-// ======== 大数据:传地址(比如字符串) ========
-char *big_data = "这是一段很长的数据...";
-xQueueSend(big_queue, &big_data, portMAX_DELAY);
-// 不复制整个字符串,只复制地址(4字节),省内存!
-// 接收方通过地址直接访问字符串
-```
-
-> **为什么要传地址?** 如果数据有 1KB,每次复制一份就太浪费内存了。传地址就像把文件路径告诉对方,而不是把整个文件复印一份送过去。
-
----
-
-## 示例 1:消息队列实验(项目 09)
-
-### 情景
-- **task1**:按键扫描,KEY1/KEY2 → 键值入队;KEY3 → 大数据地址入队
-- **task2**:从队列接收小数据(键值)
-- **task3**:从队列接收大数据地址,访问数据
-
-### 核心代码
-
-```c
-#include "queue.h"  // 队列相关API的头文件
-
-QueueHandle_t queue1;     // 小数据队列
-QueueHandle_t big_queue;  // 大数据队列
-
-void freertos_start(void)
-{
-    // 创建小数据队列:长度2,每项1字节
-    queue1 = xQueueCreate(2, sizeof(uint8_t));
-    // 创建大数据队列:长度1,每项4字节(指针大小)
-    big_queue = xQueueCreate(1, sizeof(char *));
-
-    xTaskCreate(/* ...start_task... */);
-    vTaskStartScheduler();
-}
-
-/* ========== task1:发送消息 ========== */
-void task1(void *pvParameters)
-{
-    char *big_data = "这里是需要传递的大数据内容...";
-    uint8_t key = 0;
-    while (1)
-    {
-        key = Key_Detect();
-        if (key == KEY1_PRESS || key == KEY2_PRESS)
-        {
-            // 发送键值到小数据队列
-            xQueueSend(queue1, &key, portMAX_DELAY);
-        }
-        else if (key == KEY3_PRESS)
-        {
-            // 发送大数据地址
-            xQueueSend(big_queue, &big_data, portMAX_DELAY);
-        }
-        vTaskDelay(500);
-    }
-}
-
-/* ========== task2:接收小数据 ========== */
-void task2(void *pvParameters)
-{
-    uint8_t receive;
-    while (1)
-    {
-        xQueueReceive(queue1, &receive, portMAX_DELAY);
-        printf("收到键值: %d\r\n", receive);
-    }
-}
-
-/* ========== task3:接收大数据 ========== */
-void task3(void *pvParameters)
-{
-    char *receive_buff;
-    while (1)
-    {
-        xQueueReceive(big_queue, &receive_buff, portMAX_DELAY);
-        printf("收到大数据: %s\r\n", receive_buff);
-    }
-}
-```
-
----
-
-## 示例 2:队列集(项目 14)
-
-### 为什么需要队列集?
-
-假设你管理两个信箱(队列)和一个通知铃(信号量)。不用队列集时,你必须轮询检查每个信箱——"1号有信吗?2号有信吗?铃响了吗?"很麻烦。
-
-> **痛点导向**:当需要从多个队列读取数据时,如果先阻塞在队列 A 的读取上,但队列 B 先到了数据,就会卡住——永远等不到 B 的数据。队列集让任务一次性阻塞在所有队列上,哪个先到就处理哪个。
-
-**队列集 = 统一管理的"消息中心"**。你把队列和信号量都注册进去,然后只需要等一个函数返回,它就告诉你"是哪个有消息了"。
-
-### 核心代码
-
-```c
-// ======== 创建队列集并注册 ========
-QueueSetHandle_t queue_set_handle;
-
-// 创建一个能容纳2个成员的队列集
-queue_set_handle = xQueueCreateSet(2);
-
-// 把队列和信号量注册到队列集
-xQueueAddToSet(queue1, queue_set_handle);    // 注册队列
-xQueueAddToSet(sem_handle, queue_set_handle); // 注册信号量
-
-// ======== 接收方:哪个有消息就处理哪个 ========
-void task2(void *pvParameters)
-{
-    QueueSetMemberHandle_t member_handle;
-
-    while (1)
-    {
-        // 阻塞等待:队列集中任何一个成员有消息
-        member_handle = xQueueSelectFromSet(
-            queue_set_handle, portMAX_DELAY);
-
-        if (member_handle == queue1)
-        {
-            // 是队列有消息了
-            uint8_t val;
-            xQueueReceive(queue1, &val, 0);
-            printf("队列收到: %d\r\n", val);
-        }
-        else if (member_handle == sem_handle)
-        {
-            // 是信号量被释放了
-            xSemaphoreTake(sem_handle, 0);
-            printf("信号量收到!\r\n");
-        }
-    }
-}
-```
-
-### 配套代码
-- `code/09_消息队列/`
-- `code/14_队列集/`
-
-> 后续章节:[[08-信号量与互斥信号量]]

+ 0 - 255
X-Knowledge-Base/raw/Joplin/嵌入式+Linux/FreeRTOS学习笔记/08-信号量与互斥信号量.md

@@ -1,255 +0,0 @@
----
-tags: [source-summary]
-type: source
-source: "尚硅谷嵌入式技术之FreeRTOS实时操作系统 V1.0.3 — 第11~12章 + 配套代码10/11/12/13"
-author: "尚硅谷研究院"
-date: 2026-07-12
-url: ""
-created: 2026-07-12
----
-
-# 信号量与互斥信号量
-
-> **用生活理解**:信号量就像动车上的卫生间——
-> - **二值信号量** = 卫生间指示灯:有人(0)/没人(1)。你想用卫生间,先看灯(获取信号量),绿灯就进去,红灯就等着
-> - **计数信号量** = 停车场余位显示屏:显示剩余车位数。每进一辆车减1,每出一辆车加1
-> - **互斥信号量** = 带"优先级继承"的卫生间(优化版):如果你(高优先级)想上厕所但被同事(低优先级)占着,系统会让那个同事赶紧上完让给你
-
-## 信号量分类
-
-| 类型 | 创建函数 | 计数值 | 典型用途 | 可递归 |
-|------|---------|--------|---------|--------|
-| 二值信号量 | `xSemaphoreCreateBinary()` | 0 或 1 | 同步(任务间/中断与任务间) | 否 |
-| 计数信号量 | `xSemaphoreCreateCounting(max,init)` | 0~max | 资源管理、事件计数 | 否 |
-| 互斥信号量 | `xSemaphoreCreateMutex()` | 0 或 1 | 互斥访问(带优先级继承) | 是 |
-
-**通用操作**:`xSemaphoreGive()` = 释放(给),`xSemaphoreTake()` = 获取(拿)。是所有信号量都通用的!
-
----
-
-## 示例 1:二值信号量(项目10)
-
-### 情景
-- **task1**:按 KEY1 → 释放信号量(Give)
-- **task2**:获取信号量(Take),成功就打印
-
-就像:task1 负责按铃,task2 听到铃响就开始干活。
-
-### 核心代码
-
-```c
-#include "semphr.h"  // 信号量头文件
-
-SemaphoreHandle_t sem_handle;  // 信号量句柄
-
-void freertos_start(void)
-{
-    // 参数1: 队列长度(二值信号量=1)
-    // 参数2: 每个消息的大小(二值信号量=0)
-    sem_handle = xSemaphoreCreateBinary();
-
-    xTaskCreate(/* task1 */);
-    xTaskCreate(/* task2 */);
-    vTaskStartScheduler();
-}
-
-void task1(void *pvParameters)
-{
-    uint8_t key = 0;
-    while (1)
-    {
-        key = Key_Detect();
-        if (key == KEY1_PRESS)
-        {
-            // 释放信号量(把卫生间灯从"有人"变"没人")
-            xSemaphoreGive(sem_handle);
-            printf("信号量已释放\r\n");
-        }
-        vTaskDelay(500);
-    }
-}
-
-void task2(void *pvParameters)
-{
-    while (1)
-    {
-        // 获取信号量(等卫生间变空)
-        // portMAX_DELAY = 死等,直到有信号量可用
-        xSemaphoreTake(sem_handle, portMAX_DELAY);
-        printf("获取信号量成功,开始干活\r\n");
-    }
-}
-```
-
-### 二值信号量的本质
-
-> 二值信号量本质上就是一个**长度=1、项目大小=0 的特殊队列**。你不关心"消息内容"是什么,只关心"有没有消息"。
->
-> **源码实现**:信号量底层就是用队列实现的——`xSemaphoreCreateBinary()` 内部调用了 `xQueueCreate(1, 0)`。就像火车卫生间的指示灯——红灯=有人(已获取),绿灯=空闲(已释放)。队列什么都能干,信号量能干的队列都能干,但队列能干的信号量不一定能干。
-
----
-
-## 示例 2:计数信号量(项目11)
-
-### 情景
-- **task1**:按 KEY1 → 释放计数信号量(计数值+1,相当于进一辆车)
-- **task2**:每秒获取一次(计数值-1,相当于出一辆车)
-
-### 核心代码
-
-```c
-SemaphoreHandle_t count_sem;
-
-void freertos_start(void)
-{
-    // 创建一个计数信号量:最大100,初始0
-    count_sem = xSemaphoreCreateCounting(100, 0);
-    // 最大计数=100:停车场最多停100辆车
-    // 初始计数=0:停车场一开始是空的
-
-    xTaskCreate(/* task1 */);
-    xTaskCreate(/* task2 */);
-    vTaskStartScheduler();
-}
-
-void task1(void *pvParameters)
-{
-    uint8_t key = 0;
-    while (1)
-    {
-        key = Key_Detect();
-        if (key == KEY1_PRESS)
-        {
-            xSemaphoreGive(count_sem);  // 计数值+1
-        }
-        vTaskDelay(500);
-    }
-}
-
-void task2(void *pvParameters)
-{
-    while (1)
-    {
-        xSemaphoreTake(count_sem, portMAX_DELAY);  // 计数值-1
-        // 获取后查看当前计数值
-        UBaseType_t count = uxSemaphoreGetCount(count_sem);
-        printf("当前计数值: %d\r\n", count);
-        vTaskDelay(1000);
-    }
-}
-```
-
-### 计数信号量的两种用法
-
-| 用途 | 创建参数 | 行为 |
-|------|---------|------|
-| **事件计数** | `CreateCounting(max, 0)` | Give=事件发生(+1),Take=处理事件(-1) |
-| **资源管理** | `CreateCounting(max, max)` | Take=占用资源(-1),Give=释放资源(+1) |
-
----
-
-## 示例 3:优先级翻转(项目12)
-
-### 什么是优先级翻转?
-
-> **用生活理解**:你在机场排队安检(你就是高优先级任务A)。你前面有个老人(低优先级任务B)正在安检,你只能等着。这时来了个 VIP(中优先级任务C),工作人员让 VIP 插队到前面先安检。结果 VIP 安检完了,老人才能继续。老人完了才轮到你。**问题**:一个"插队的"反而让你(最高优先级)等了最久——这就是优先级翻转。
-
-![优先级翻转示意图:中优先级任务插队导致高优先级任务等待最久](assets/priority_inversion.png)
-
-### 代码演示
-
-```c
-// 三个任务优先级:Task1(2) < Task2(3) < Task3(4)
-// 共享一个二值信号量
-
-void task1(void *pvParameters)  // 低优先级 Task1(2)
-{
-    while (1)
-    {
-        xSemaphoreTake(sem_handle, portMAX_DELAY);  // 拿到信号量
-        printf("低优先级Task1正在执行...\r\n");
-        HAL_Delay(3000);  // 占用信号量3秒!
-        xSemaphoreGive(sem_handle);
-        vTaskDelay(1000);
-    }
-}
-
-void task2(void *pvParameters)  // 中优先级 Task2(3)
-{
-    while (1)
-    {
-        printf("中优先级Task2正在执行...\r\n");
-        HAL_Delay(1500);            // 纯粹的计算任务
-        vTaskDelay(1000);
-    }
-}
-
-void task3(void *pvParameters)  // 高优先级 Task3(4)
-{
-    while (1)
-    {
-        xSemaphoreTake(sem_handle, portMAX_DELAY);  // 等信号量
-        printf("高优先级Task3正在执行...\r\n");
-        HAL_Delay(1000);
-        xSemaphoreGive(sem_handle);
-        vTaskDelay(1000);
-    }
-}
-```
-
-> **优先级翻转的本质**:"倒反天罡"——低优先级任务反而阻塞了高优先级任务。中优先级任务"山中无老虎,猴子称霸王"——不受限制地运行,持续抢占持有锁的低优先级任务,导致高优先级等待时间无限延长。这是信号量的典型问题,不是所有同步机制都有。
-
-**执行流程**(问题所在):
-```
-时间 →
-Task1(低) 拿信号量 ──── 干了3秒 ──── 释放
-Task3(高)              等着...  等着...  拿到!
-Task2(中)          打断了Task1  执行完
-                     ^ 问题就在这里!
-                     中优先级插队了,高优先级反而等最久
-```
-
----
-
-## 示例 4:互斥信号量解决优先级翻转(项目13)
-
-### 解决方法
-
-把 `xSemaphoreCreateBinary()` 换成 `xSemaphoreCreateMutex()`,其他代码完全一样!
-
-```c
-// freertos_start 中只需改这一行
-// sem_handle = xSemaphoreCreateBinary();   // 二值信号量:会有优先级翻转
-sem_handle = xSemaphoreCreateMutex();        // 互斥信号量:自动处理优先级翻转
-```
-
-![互斥信号量优先级继承:低优先级任务被临时提升,防止中优先级插队](assets/mutex_priority_inheritance.png)
-
-### 互斥信号量做了什么?
-
-系统检测到"高优先级任务在等低优先级任务释放信号量"时,**临时把低优先级任务的优先级提升到和高优先级一样高**。这样中优先级任务就不能插队了,低优先级任务赶紧执行完释放信号量,高优先级任务就能拿到。用完就丢——资源释放后优先级再降回去。
-
-> **创建时自动释放**:创建互斥信号量时会**自动执行一次 Give 操作**,确保初始状态为"可用"。
-
-```
-互斥信号量下:
-Task1(低) 拿信号量 ──── 干了3秒 ──── 释放
-              ↑优先级被临时提升到4   ↑恢复原来优先级
-Task3(高)   等着...                   拿到!
-Task2(中)           想插队但发现Task1优先级比自己高了,插不了
-```
-
-### 互斥信号量的限制
-
-| 限制 | 原因 |
-|------|------|
-| ❌ 不能在中断中使用 | 优先级继承需要在任务上下文中运行 |
-| ✅ 可以递归获取 | 同一个任务可以多次 Take,但要Give同样次数 |
-
-### 配套代码
-- `code/10_二值信号量/`
-- `code/11_计数型信号量/`
-- `code/12_优先级翻转/`
-- `code/13_互斥信号量/`
-
-> 后续章节:[[09-事件标志组与任务通知]]

+ 280 - 0
X-Knowledge-Base/raw/Joplin/嵌入式+Linux/FreeRTOS学习笔记/08-时间片调度与延时管理.md

@@ -0,0 +1,280 @@
+---
+tags: [source-summary]
+type: source
+source: "尚硅谷嵌入式技术之FreeRTOS实时操作系统 V1.0.3 — 第7章 时间片调度 + 第9章 时间管理/延时函数"
+author: "尚硅谷研究院"
+date: 2026-07-17
+created: 2026-07-17
+---
+
+# 时间片调度与延时管理
+
+> **用生活理解**:时间片调度就像自助餐厅的取餐规则——同等级的人轮流取菜,每人吃一口就轮到下一位(Round-Robin)。vTaskDelay 相当于"我过一会儿再来排队",vTaskDelayUntil 则是"以后每周二下午三点我来,雷打不动"。
+
+## Concept
+
+### Time slice scheduling
+
+**时间片调度**指多个相同优先级的任务轮流获得 CPU 使用权,每个任务执行一个时间片(一个 Tick)后被切换到下一个同优先级任务。
+
+关键配置:
+- `configUSE_TIME_SLICING`:启用/禁用时间片调度(默认开启)
+- `configUSE_PREEMPTION`:启用抢占式调度(必须开启,否则时间片不生效)
+- `configTICK_RATE_HZ`:Tick 频率,决定时间片长度
+
+时间片 = 1 个 Tick = 1 / configTICK_RATE_HZ 秒
+
+### Working principle
+
+**抢占式调度 + 时间片调度**:
+
+```
+高优先级任务 → 总是先运行(抢占)
+同优先级任务 → 轮流运行(时间片轮转)
+低优先级任务 → 高/同优先级任务阻塞时才运行
+```
+
+- `configTICK_RATE_HZ = 20` → 每个 Tick 50ms → 时间片 50ms
+- `configTICK_RATE_HZ = 1000` → 每个 Tick 1ms → 时间片 1ms
+
+### 延时函数对比
+
+| 函数 | 类型 | 描述 | 漂移 |
+|------|------|------|------|
+| `vTaskDelay(Tick)` | 相对延时 | 从调用开始等待指定 Tick 数 | ❌ 会漂移 |
+| `vTaskDelayUntil(pxPrevWakeTime, Tick)` | 绝对延时 | 以固定周期执行,不受执行时间影响 | ✅ 固定周期 |
+| `HAL_Delay(ms)` | 忙等待 | 占用 CPU 循环等待 | 阻塞式查询 |
+
+**vTaskDelay 的漂移问题**:
+
+```
+第1周期:任务执行 20ms + 延时 500ms = 间隔 520ms
+第2周期:任务执行 35ms + 延时 500ms = 间隔 535ms
+→ 实际周期越跑越慢(漂移累积)
+```
+
+**vTaskDelayUntil 的固定周期**:
+```
+第1周期:记录唤醒时间 t1,延时到 t1+500
+第2周期:记录唤醒时间 t2,延时到 t2+500
+→ 无论任务执行多久,周期始终为 500ms
+```
+
+## Experiment
+
+### Requirements
+
+**P05(时间片调度)**:
+- 2 个同优先级任务(优先级均为 2),各自进入临界区打印计数后退出
+- 使用 `HAL_Delay(10)` 模拟工作量
+- 观察两个任务交替执行(时间片轮转)
+
+**P08(延时对比)**:
+- task1:使用 `vTaskDelay(500)` + `HAL_Delay(20)` 模拟工作 → 演示漂移
+- task2:使用 `vTaskDelayUntil(&pxPreviousWakeTime, 500)` + `HAL_Delay(20)` → 演示固定周期
+
+### Code Implementation
+
+#### P05:时间片调度
+
+**File: 05_时间片调度/Core/Src/freertos_demo.c**
+
+```c
+#include "freertos_demo.h"
+#include "FreeRTOS.h"
+#include "task.h"
+#include "LED.h"
+
+#define START_TASK_STACK 128
+#define START_TASK_PRIORITY 1
+TaskHandle_t start_task_handle;
+void start_task(void *pvParameters);
+
+/* 两个任务使用相同优先级(均为2)→ 时间片轮转 */
+#define TASK1_STACK 128
+#define TASK1_PRIORITY 2
+TaskHandle_t task1_handle;
+void task1(void *pvParameters);
+
+#define TASK2_STACK 128
+#define TASK2_PRIORITY 2
+TaskHandle_t task2_handle;
+void task2(void *pvParameters);
+
+void freertos_start(void)
+{
+    xTaskCreate((TaskFunction_t)start_task,
+                (char *)"start_task",
+                (configSTACK_DEPTH_TYPE)START_TASK_STACK,
+                (void *)NULL,
+                (UBaseType_t)START_TASK_PRIORITY,
+                (TaskHandle_t *)&start_task_handle);
+
+    vTaskStartScheduler();
+}
+
+void start_task(void *pvParameters)
+{
+    taskENTER_CRITICAL();
+
+    xTaskCreate((TaskFunction_t)task1, (char *)"task1",
+                (configSTACK_DEPTH_TYPE)TASK1_STACK, (void *)NULL,
+                (UBaseType_t)TASK1_PRIORITY, (TaskHandle_t *)&task1_handle);
+    xTaskCreate((TaskFunction_t)task2, (char *)"task2",
+                (configSTACK_DEPTH_TYPE)TASK2_STACK, (void *)NULL,
+                (UBaseType_t)TASK2_PRIORITY, (TaskHandle_t *)&task2_handle);
+
+    vTaskDelete(NULL);
+    taskEXIT_CRITICAL();
+}
+
+/**
+ * @description: task1——进入临界区打印计数,模拟工作
+ * 注意:临界区内不触发任务切换,所以每次打印会连续输出多次后切换到task2
+ */
+void task1(void *pvParameters)
+{
+    uint16_t task1_count = 0;
+    while (1)
+    {
+        taskENTER_CRITICAL();                // 进入临界区:防止切换
+        printf("task1正在运行[%d]次...\r\n", ++task1_count);
+        // vTaskDelay(500);                  // 使用vTaskDelay会触发调度,观察不到时间片
+        HAL_Delay(10);                       // 忙等待模拟工作负载,不切换任务
+        taskEXIT_CRITICAL();                 // 退出临界区:允许切换
+    }
+}
+
+/**
+ * @description: task2——与task1同优先级,时间片轮转
+ */
+void task2(void *pvParameters)
+{
+    uint16_t task2_count = 0;
+    while (1)
+    {
+        taskENTER_CRITICAL();
+        printf("task2正在运行[%d]次...\r\n", ++task2_count);
+        HAL_Delay(10);
+        taskEXIT_CRITICAL();
+    }
+}
+
+/* 观察现象:
+   当configTICK_RATE_HZ=20时,时间片=50ms
+   HAL_Delay(10)执行时间<50ms,
+   所以每次进入临界区可以在一个时间片内打印多次
+   退出临界区后下次调度切换到另一个任务 */
+```
+
+#### P08:延时函数对比
+
+**File: 08_延时函数/Core/Src/freertos_demo.c**
+
+```c
+#include "freertos_demo.h"
+#include "FreeRTOS.h"
+#include "task.h"
+#include "LED.h"
+
+#define START_TASK_STACK 128
+#define START_TASK_PRIORITY 1
+TaskHandle_t start_task_handle;
+void start_task(void *pvParameters);
+
+#define TASK1_STACK 128
+#define TASK1_PRIORITY 2
+TaskHandle_t task1_handle;
+void task1(void *pvParameters);
+
+#define TASK2_STACK 128
+#define TASK2_PRIORITY 3
+TaskHandle_t task2_handle;
+void task2(void *pvParameters);
+
+void freertos_start(void)
+{
+    xTaskCreate((TaskFunction_t)start_task,
+                (char *)"start_task",
+                (configSTACK_DEPTH_TYPE)START_TASK_STACK,
+                (void *)NULL,
+                (UBaseType_t)START_TASK_PRIORITY,
+                (TaskHandle_t *)&start_task_handle);
+
+    vTaskStartScheduler();
+}
+
+void start_task(void *pvParameters)
+{
+    taskENTER_CRITICAL();
+
+    xTaskCreate((TaskFunction_t)task1, (char *)"task1",
+                (configSTACK_DEPTH_TYPE)TASK1_STACK, (void *)NULL,
+                (UBaseType_t)TASK1_PRIORITY, (TaskHandle_t *)&task1_handle);
+    xTaskCreate((TaskFunction_t)task2, (char *)"task2",
+                (configSTACK_DEPTH_TYPE)TASK2_STACK, (void *)NULL,
+                (UBaseType_t)TASK2_PRIORITY, (TaskHandle_t *)&task2_handle);
+
+    vTaskDelete(NULL);
+    taskEXIT_CRITICAL();
+}
+
+/**
+ * @description: task1——使用vTaskDelay相对延时 + HAL_Delay模拟工作
+ *               现象:LED翻转间隔会逐渐漂移(越来越慢)
+ */
+void task1(void *pvParameters)
+{
+    while (1)
+    {
+        LED_Toggle(LED1_Pin);              // 翻转LED1
+        HAL_Delay(20);                      // 模拟工作任务耗时20ms
+        vTaskDelay(500);                   // 相对延时500ms(不包括HAL_Delay耗时)
+    }                                      // 实际周期 = 20ms + 500ms = 520ms
+}
+
+/**
+ * @description: task2——使用vTaskDelayUntil绝对延时 + HAL_Delay模拟工作
+ *               现象:LED翻转周期严格固定为500ms
+ */
+void task2(void *pvParameters)
+{
+    TickType_t pxPreviousWakeTime = xTaskGetTickCount();  // 获取当前Tick计数值作为基准
+    while (1)
+    {
+        LED_Toggle(LED2_Pin);              // 翻转LED2
+        HAL_Delay(20);                      // 模拟工作任务耗时20ms
+        vTaskDelayUntil(&pxPreviousWakeTime, 500);  // 绝对延时:等待到 pxPrevWakeTime + 500
+    }                                      // 实际周期始终为500ms,不受HAL_Delay影响
+}
+```
+
+### Key Config
+
+**P05 FreeRTOSConfig.h**(降低 Tick 频率便于观察时间片):
+
+```c
+#define configTICK_RATE_HZ         20    // Tick频率20Hz,时间片=50ms
+#define configUSE_TIME_SLICING     1     // 启用时间片调度(默认即开启)
+#define configUSE_PREEMPTION       1     // 启用抢占式调度(必须)
+```
+
+## Core API Reference Table
+
+| Function | Parameters | Return | Description |
+|----------|-----------|--------|-------------|
+| `vTaskDelay` | xTicksToDelay | void | 相对延时,从调用时开始计时 |
+| `vTaskDelayUntil` | pxPreviousWakeTime, xTimeIncrement | BaseType_t | 绝对延时,固定周期 |
+| `xTaskGetTickCount` | 无 | TickType_t | 获取当前 Tick 计数值 |
+| `configTICK_RATE_HZ` | 宏定义 | — | 配置每秒 Tick 数 |
+| `configUSE_TIME_SLICING` | 宏定义 | — | 启用/禁用的时间片调度 |
+| `HAL_Delay` | Delay | void | HAL 库忙等待延时(阻塞 CPU) |
+
+## Common Issues & Pitfalls
+
+1. **vTaskDelay 的漂移累积**:如果任务内部的执行时间不固定,`vTaskDelay` 会导致周期逐渐漂移。对周期有严格要求的场景必须使用 `vTaskDelayUntil`。
+2. **vTaskDelayUntil 的初次使用**:`vTaskDelayUntil` 要求 `pxPreviousWakeTime` 初始化为 `xTaskGetTickCount()`,且在每次调用后会被 API 自动更新。
+3. **时间片调度与临界区的相互作用**:在临界区中不会发生任务切换,因此如果在临界区内耗时超过一个时间片,仍不会切换到同优先级任务。
+4. **HAL_Delay 在 FreeRTOS 中的问题**:`HAL_Delay` 是忙等待不释放 CPU,在 RTOS 中应优先使用 `vTaskDelay`。如果定时器中断优先级配置不当,`HAL_Delay` 可能无法正常工作。
+5. **configTICK_RATE_HZ 不能过高**:Tick 频率过高(如 10000Hz)意味着每个 Tick 0.1ms,频繁的上下文切换会消耗大量 CPU 时间,降低有效运算能力。嵌入式系统常用 100~1000Hz。
+6. **时间片观测技巧**:在临界区中打印计数可以观察到时间片行为——同一任务在一次时间片内可能执行多次循环。

+ 0 - 198
X-Knowledge-Base/raw/Joplin/嵌入式+Linux/FreeRTOS学习笔记/09-事件标志组与任务通知.md

@@ -1,198 +0,0 @@
----
-tags: [source-summary]
-type: source
-source: "尚硅谷嵌入式技术之FreeRTOS实时操作系统 V1.0.3 — 第15~16章 + 配套代码15/16/17/18"
-author: "尚硅谷研究院"
-date: 2026-07-12
-url: ""
-created: 2026-07-12
----
-
-# 事件标志组与任务通知
-
-> **用生活理解**:
-> - **事件标志组**:就像你妈让你出门前检查"带钥匙了吗?带手机了吗?带钱包了吗?"——每个事件是一个 checkbox,全部勾上了才能出门
-> - **任务通知**:就像同事直接在你桌上贴便条——不用经过前台、不用信箱,直接通知你。效率最高
-
----
-
-## 事件标志组
-
-### 核心概念
-
-![事件标志组位结构示意图](assets/event_group_bits.png)
-
-事件标志组是一个整数(24位或8位),每个 bit 代表一个独立事件:
-
-> **位宽计算**:实际可用位数 = tick 类型的位数 - 8 位预留。由 `configUSE_16_BIT_TICKS` 决定——置 1 → 8 位事件标志(16-8=8),置 0 → 24 位(32-8=24)。事件标志组占用 tick 类型的高 8 位。
-
-```
-Bit:  23 ... 2  1  0
-     +---------+--+--+
-     | 其他事件 |B |A | ← A事件=bit0, B事件=bit1
-     +---------+--+--+
-```
-
-**关键特点**(对比信号量):
-| 特性 | 事件标志组 | 信号量 |
-|------|-----------|--------|
-| 等待条件 | 可等**多个位同时置1** | 只能等一个"资源" |
-| 位操作 | 每个位独立,可分别设置 | 计数器整体加减 |
-| 灵活性 | 高(任意组合) | 低(单一条件) |
-
-### 示例:事件标志组(项目15)
-
-#### 情景
-- **task1**:按 KEY1 → 置位 bit0(事件A发生),按 KEY2 → 置位 bit1(事件B发生)
-- **task2**:等待 bit0 和 bit1 **同时**置位,然后处理
-
-#### 核心代码
-
-```c
-#include "event_groups.h"
-
-EventGroupHandle_t event_group;
-
-#define EVENTBIT_0  (1 << 0)  // 事件A = 第0位
-#define EVENTBIT_1  (1 << 1)  // 事件B = 第1位
-
-void freertos_start(void)
-{
-    event_group = xEventGroupCreate();  // 创建事件标志组
-    xTaskCreate(/* task1 */);
-    xTaskCreate(/* task2 */);
-    vTaskStartScheduler();
-}
-
-void task1(void *pvParameters)
-{
-    uint8_t key = 0;
-    while (1)
-    {
-        key = Key_Detect();
-        if (key == KEY1_PRESS)
-        {
-            xEventGroupSetBits(event_group, EVENTBIT_0);  // "我带了钥匙!"
-        }
-        else if (key == KEY2_PRESS)
-        {
-            xEventGroupSetBits(event_group, EVENTBIT_1);  // "我带了手机!"
-        }
-        vTaskDelay(500);
-    }
-}
-
-void task2(void *pvParameters)
-{
-    EventBits_t bits;
-    while (1)
-    {
-        bits = xEventGroupWaitBits(
-            event_group,              // 事件标志组句柄
-            EVENTBIT_0 | EVENTBIT_1,  // 等bit0和bit1同时成立
-            pdTRUE,                   // 等到之后自动清除这两个位
-            pdTRUE,                   // pdTRUE = 所有位都置1才算
-            portMAX_DELAY);          // 死等
-        printf("钥匙和手机都带齐了,可以出门了!\r\n");
-    }
-}
-```
-
-> **`xEventGroupWaitBits` 第4个参数**:
-> - `pdTRUE`(AND模式):所有指定的位都置1才唤醒
-> - `pdFALSE`(OR模式):任意一个位置1就唤醒
-
----
-
-## 任务通知
-
-### 为什么任务通知更快?
-
-```
-传统方式(信号量):                    任务通知方式:
-发送方 → 信号量对象 → 接收方             发送方 → 接收方
-        中间多了一层                       直达!
-```
-
-任务通知**不需要创建中间对象**。每个任务内部都有 32 位通知值,其他任务可以直接修改它。
-
-> **信箱类比**:每个任务的 TCB 中有一个"邮箱",其他任务直接把信放进去——不需要队列、信号量等中间对象。发送方不能阻塞(只能覆盖或放弃),只有接收方可以阻塞。
->
-> **发送非阻塞**:发送方总是非阻塞的——要么覆盖旧值,要么放弃。这跟队列不同——队列满了发送方可以等,通知满了就只能覆盖或丢弃。
-
-### 三种典型用法
-
-项目16/17/18 分别演示了三种用法:
-
-#### 用法1:模拟二值信号量(项目16)
-
-```c
-// 发送方(Task1)
-xTaskNotifyGive(task2_handle);  // 直接给 task2 发通知
-
-// 接收方(Task2)
-ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
-// pdTRUE = 收到通知后把通知值清零(就像二值信号量)
-```
-
-#### 用法2:模拟消息邮箱(项目17)
-
-```c
-// 发送方(Task1)
-uint8_t key = KEY1_PRESS;
-xTaskNotify(task2_handle, key, eSetValueWithOverwrite);
-// 第3个参数 eSetValueWithOverwrite = "直接覆盖旧值"
-
-// 接收方(Task2)
-uint32_t notify_val;
-xTaskNotifyWait(0, 0xFFFFFFFF, &notify_val, portMAX_DELAY);
-switch (notify_val) {
-    case KEY1_PRESS: LED_Toggle(LED1_Pin); break;
-    case KEY2_PRESS: LED_Toggle(LED2_Pin); break;
-}
-```
-
-#### 用法3:模拟事件标志组(项目18)
-
-```c
-// 发送方
-xTaskNotify(task2_handle, EVENTBIT_0, eSetBits);  // 设置某一位
-xTaskNotify(task2_handle, EVENTBIT_1, eSetBits);
-
-// 接收方
-uint32_t val;
-xTaskNotifyWait(0, 0xFFFFFFFF, &val, portMAX_DELAY);
-if (val & EVENTBIT_0) { /* 事件A发生了 */ }
-if (val & EVENTBIT_1) { /* 事件B发生了 */ }
-```
-
-### 任务通知 API 速查
-
-| 函数 | 用途 | 对应传统机制 |
-|------|------|------------|
-| `xTaskNotifyGive()` / `ulTaskNotifyTake()` | 发/收通知(无数据) | 二值信号量 |
-| `xTaskNotify(handle, val, eAction)` / `xTaskNotifyWait()` | 发/收通知(带数据) | 消息邮箱/队列 |
-| `xTaskNotify(handle, bits, eSetBits)` + `xTaskNotifyWait()` | 设置位 + 等待 | 事件标志组 |
-
-**`eAction` 参数选项**:
-| 选项 | 含义 |
-|------|------|
-| `eSetValueWithOverwrite` | 直接覆盖通知值(像邮箱) |
-| `eSetBits` | 按位或操作(像事件组) |
-| `eIncrement` | 通知值+1(像计数信号量) |
-| `eNoAction` | 只发通知不改值 |
-
-### 注意事项
-- ⚠️ 任务通知**不能在 ISR 中接收**(没有 FromISR 版本的接收函数)——因为 ISR 没有自己的 TCB
-- ✅ 发送可以在 ISR 中:`xTaskNotifyFromISR()` / `vTaskNotifyGiveFromISR()`
-- 通知默认启用,禁用时设置 `configUSE_TASK_NOTIFICATIONS = 0`
-
-> **v10.4 新增通知数组**:旧版本每个任务只有一个 32 位通知值。新版本支持**通知数组**(`configTASK_NOTIFICATION_ARRAY_ENTRIES`),可接收来自多个不同任务的独立通知。发送时通过 `xTaskNotifyIndexed()` 指定数组索引。
-
-### 配套代码
-- `code/15_事件标志组/`
-- `code/16_任务通知模拟信号量/`
-- `code/17_任务通知模拟消息队列/`
-- `code/18_任务通知模拟事件标志组/`
-
-> 后续章节:[[10-软件定时器与Tickless低功耗]]

+ 342 - 0
X-Knowledge-Base/raw/Joplin/嵌入式+Linux/FreeRTOS学习笔记/09-任务状态查询与运行时统计.md

@@ -0,0 +1,342 @@
+---
+tags: [source-summary]
+type: source
+source: "尚硅谷嵌入式技术之FreeRTOS实时操作系统 V1.0.3 — 第8章 任务相关API函数"
+author: "尚硅谷研究院"
+date: 2026-07-17
+created: 2026-07-17
+---
+
+# 任务状态查询与运行时统计
+
+> **用生活理解**:任务状态查询就像给公司做一次全面盘点——有多少员工(任务数)、每个员工的职级(优先级)、当前在干嘛(运行/就绪/阻塞)、堆栈压力(水位线)一目了然。运行时统计则像月底看考勤——每个员工干了多少活,一目了然。
+
+## Concept
+
+### Basic concept
+
+FreeRTOS 提供一套丰富的 API 用于查询任务运行时信息,包括:
+
+- **优先级查询与设置**:获取/修改任务的优先级
+- **任务数量统计**:系统中当前存在的总任务数
+- **任务详细信息**:获取任务名称、编号、状态、优先级等
+- **栈空间使用**:查看栈的"水位线"(剩余空间最小值)
+- **任务状态**:查询任务当前处于运行/就绪/阻塞/挂起/删除态
+- **任务列表**:以可读文本格式输出所有任务信息
+- **运行时统计**:需要额外硬件定时器,统计各任务占用 CPU 的百分比
+
+### Working principle
+
+**运行时统计(Run Time Stats)** 需要额外的硬件定时器作为时钟源:
+
+```
+配置步骤:
+1. 启用 configGENERATE_RUN_TIME_STATS = 1
+2. 配置一个硬件定时器(如 TIM6/TIM7)产生高频计时
+3. 实现 portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() 初始化定时器
+4. 实现 portGET_RUN_TIME_COUNTER_VALUE() 返回定时器计数值
+```
+
+统计结果以表格形式输出:
+
+```
+Task           Abs Time    % Time
+--------------------------------
+task1          12345       45.2
+task2          9876        36.1
+IDLE           5120        18.7
+```
+
+## Experiment
+
+### Requirements
+
+**P06(状态查询 API)**:
+- task2 一次性调用所有状态查询 API 并打印结果:
+  - `uxTaskPriorityGet()` / `vTaskPrioritySet()`
+  - `uxTaskGetNumberOfTasks()`
+  - `uxTaskGetSystemState()` / `vTaskGetInfo()`
+  - `xTaskGetCurrentTaskHandle()` / `xTaskGetHandle()`
+  - `uxTaskGetStackHighWaterMark()`
+  - `eTaskGetState()`
+  - `vTaskList()`
+
+**P07(运行时统计)**:
+- task2 每隔 500ms 打印 `vTaskGetRunTimeStats()` 结果
+- 需要 TIM6 提供高频计时
+
+### Code Implementation
+
+#### P06:任务状态查询
+
+**File: 06_任务状态查询/Core/Src/freertos_demo.c**
+
+```c
+#include "freertos_demo.h"
+#include "FreeRTOS.h"
+#include "task.h"
+#include "LED.h"
+#include "Key.h"
+
+#define START_TASK_STACK 128
+#define START_TASK_PRIORITY 1
+TaskHandle_t start_task_handle;
+void start_task(void *pvParameters);
+
+#define TASK1_STACK 128
+#define TASK1_PRIORITY 2
+TaskHandle_t task1_handle;
+void task1(void *pvParameters);
+
+/* task2栈空间更大,因为要调用多个API和打印 */
+#define TASK2_STACK 256
+#define TASK2_PRIORITY 3
+TaskHandle_t task2_handle;
+void task2(void *pvParameters);
+
+void freertos_start(void)
+{
+    xTaskCreate((TaskFunction_t)start_task,
+                (char *)"start_task",
+                (configSTACK_DEPTH_TYPE)START_TASK_STACK,
+                (void *)NULL,
+                (UBaseType_t)START_TASK_PRIORITY,
+                (TaskHandle_t *)&start_task_handle);
+
+    vTaskStartScheduler();
+}
+
+void start_task(void *pvParameters)
+{
+    taskENTER_CRITICAL();
+
+    xTaskCreate((TaskFunction_t)task1, (char *)"task1",
+                (configSTACK_DEPTH_TYPE)TASK1_STACK, (void *)NULL,
+                (UBaseType_t)TASK1_PRIORITY, (TaskHandle_t *)&task1_handle);
+    xTaskCreate((TaskFunction_t)task2, (char *)"task2",
+                (configSTACK_DEPTH_TYPE)TASK2_STACK, (void *)NULL,
+                (UBaseType_t)TASK2_PRIORITY, (TaskHandle_t *)&task2_handle);
+
+    vTaskDelete(NULL);
+    taskEXIT_CRITICAL();
+}
+
+void task1(void *pvParameters)
+{
+    while (1)
+    {
+        printf("task1正在运行...\r\n");
+        LED_Toggle(LED1_Pin);
+        vTaskDelay(500);
+    }
+}
+
+char task_info[500];
+
+/**
+ * @description: task2——演示所有任务状态查询API的使用
+ */
+void task2(void *pvParameters)
+{
+    UBaseType_t task_priority = 0;
+    UBaseType_t task_num = 0;
+    TaskStatus_t task_status[10] = {0};          // 最多10个任务的状态信息
+    TaskStatus_t task_state;
+    TaskHandle_t task_handle = NULL;
+    UBaseType_t task_stack_remain_min = 0;
+    eTaskState e_task_state = 0;
+
+    /* 1. 获取task1的优先级 */
+    task_priority = uxTaskPriorityGet(task1_handle);
+    printf("task1当前优先级=%d\r\n", task_priority);
+
+    /* 2. 设置task1的优先级为4 */
+    vTaskPrioritySet(task1_handle, 4);
+    task_priority = uxTaskPriorityGet(task1_handle);
+    printf("task1修改后优先级=%d\r\n", task_priority);
+
+    /* 3. 获取系统中任务总数 */
+    task_num = uxTaskGetNumberOfTasks();
+    printf("当前系统中任务数量=%d\r\n", task_num);
+
+    /* 4. 获取所有任务状态信息 */
+    uxTaskGetSystemState(task_status, task_num, NULL);
+    printf("任务名\t\t任务号\t\t状态\t\t当前优先级\t\t基础优先级\r\n");
+    for (uint8_t i = 0; i < task_num; i++)
+    {
+        printf("%s\t\t%d\t\t%d\t\t%d\t\t%d\r\n",
+               task_status[i].pcTaskName,
+               task_status[i].xTaskNumber,
+               task_status[i].eCurrentState,
+               task_status[i].uxCurrentPriority,
+               task_status[i].uxBasePriority);
+    }
+
+    /* 5. 获取任务详细信息 */
+    vTaskGetInfo(
+        task1_handle,      // 要查询的任务句柄
+        &task_state,       // 用于接收任务信息的结构体
+        pdTRUE,            // 是否获取栈状态
+        eInvalid);         // 任务状态(入参被忽略)
+    printf("任务名称: %s\r\n", task_state.pcTaskName);
+    printf("任务编号: %d\r\n", task_state.xTaskNumber);
+    printf("任务状态: %d\r\n", task_state.eCurrentState);
+    printf("当前优先级: %d\r\n", task_state.uxCurrentPriority);
+
+    /* 6. 获取当前正在运行的任务句柄 */
+    task_handle = xTaskGetCurrentTaskHandle();
+    printf("当前运行任务句柄=%p, task2句柄=%p\r\n", task_handle, task2_handle);
+
+    /* 7. 通过任务名获取句柄 */
+    task_handle = xTaskGetHandle("task1");
+    printf("通过名称获取task1句柄=%p, task1原始句柄=%p\r\n", task_handle, task1_handle);
+
+    /* 8. 获取栈空间水位线(栈剩余的最小值→反映栈峰值使用量) */
+    task_stack_remain_min = uxTaskGetStackHighWaterMark(task2_handle);
+    printf("task2栈剩余空间最小值=%d\r\n", task_stack_remain_min);
+
+    /* 9. 获取任务状态(运行/就绪/阻塞/挂起/删除) */
+    e_task_state = eTaskGetState(task1_handle);
+    printf("task1当前状态=%d\r\n", e_task_state);
+
+    /* 10. 打印可读格式的任务列表 */
+    vTaskList(task_info);
+    printf("%s\r\n", task_info);
+
+    while (1)
+    {
+        vTaskDelay(500);
+    }
+}
+```
+
+#### P07:运行时统计
+
+**File: 07_任务运行时间统计/Core/Src/freertos_demo.c**
+
+```c
+#include "freertos_demo.h"
+#include "FreeRTOS.h"
+#include "task.h"
+#include "LED.h"
+
+#define START_TASK_STACK 128
+#define START_TASK_PRIORITY 1
+TaskHandle_t start_task_handle;
+void start_task(void *pvParameters);
+
+#define TASK1_STACK 128
+#define TASK1_PRIORITY 2
+TaskHandle_t task1_handle;
+void task1(void *pvParameters);
+
+#define TASK2_STACK 128
+#define TASK2_PRIORITY 3
+TaskHandle_t task2_handle;
+void task2(void *pvParameters);
+
+void freertos_start(void)
+{
+    xTaskCreate((TaskFunction_t)start_task,
+                (char *)"start_task",
+                (configSTACK_DEPTH_TYPE)START_TASK_STACK,
+                (void *)NULL,
+                (UBaseType_t)START_TASK_PRIORITY,
+                (TaskHandle_t *)&start_task_handle);
+
+    vTaskStartScheduler();
+}
+
+void start_task(void *pvParameters)
+{
+    taskENTER_CRITICAL();
+
+    xTaskCreate((TaskFunction_t)task1, (char *)"task1",
+                (configSTACK_DEPTH_TYPE)TASK1_STACK, (void *)NULL,
+                (UBaseType_t)TASK1_PRIORITY, (TaskHandle_t *)&task1_handle);
+    xTaskCreate((TaskFunction_t)task2, (char *)"task2",
+                (configSTACK_DEPTH_TYPE)TASK2_STACK, (void *)NULL,
+                (UBaseType_t)TASK2_PRIORITY, (TaskHandle_t *)&task2_handle);
+
+    vTaskDelete(NULL);
+    taskEXIT_CRITICAL();
+}
+
+void task1(void *pvParameters)
+{
+    while (1)
+    {
+        printf("task1正在运行...\r\n");
+        LED_Toggle(LED1_Pin);
+        vTaskDelay(500);
+    }
+}
+
+char task_time_info[500];
+
+/**
+ * @description: task2——每隔500ms打印各任务运行时间统计
+ */
+void task2(void *pvParameters)
+{
+    while (1)
+    {
+        printf("task2正在运行...\r\n");
+        /* 获取各任务的运行时间统计表(需要硬件定时器支持) */
+        vTaskGetRunTimeStats(task_time_info);
+        printf("%s\r\n", task_time_info);
+        vTaskDelay(500);
+    }
+}
+```
+
+### Key Config
+
+**P06 FreeRTOSConfig.h**(启用查询功能):
+
+```c
+#define configUSE_TRACE_FACILITY             1     // 启用任务跟踪功能
+#define configUSE_STATS_FORMATTING_FUNCTIONS 1     // 启用格式化输出函数
+#define INCLUDE_xTaskGetHandle              1     // 启用通过名称获取句柄
+#define INCLUDE_uxTaskGetStackHighWaterMark 1     // 启用栈水位线查询
+```
+
+**P07 FreeRTOSConfig.h**(启用运行时统计 + 硬件定时器配置):
+
+```c
+#define configGENERATE_RUN_TIME_STATS    1     // 生成运行时统计
+
+#if configGENERATE_RUN_TIME_STATS
+extern volatile unsigned long ulHighFrequencyTimerTicks;  // 高频定时器计数值
+#define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() ( ulHighFrequencyTimerTicks = 0UL )  // 初始化定时器
+#define portGET_RUN_TIME_COUNTER_VALUE()    ulHighFrequencyTimerTicks  // 读取当前计数值
+#endif
+
+#define configUSE_TRACE_FACILITY               1
+#define configUSE_STATS_FORMATTING_FUNCTIONS   1
+```
+
+## Core API Reference Table
+
+| Function | Parameters | Return | Description |
+|----------|-----------|--------|-------------|
+| `uxTaskPriorityGet` | xTask | UBaseType_t | 获取任务优先级 |
+| `vTaskPrioritySet` | xTask, uxNewPriority | void | 设置任务优先级 |
+| `uxTaskGetNumberOfTasks` | 无 | UBaseType_t | 获取系统中任务总数 |
+| `uxTaskGetSystemState` | pxTaskStatusArray, uxArraySize, pulTotalRunTime | UBaseType_t | 获取所有任务的状态 |
+| `vTaskGetInfo` | xTask, pxTaskStatus, xGetFreeStackSpace, eState | void | 获取任务详细信息 |
+| `xTaskGetCurrentTaskHandle` | 无 | TaskHandle_t | 获取当前运行任务的句柄 |
+| `xTaskGetHandle` | pcNameToQuery | TaskHandle_t | 通过任务名获取句柄 |
+| `uxTaskGetStackHighWaterMark` | xTask | UBaseType_t | 获取栈剩余空间下限(以 word 计) |
+| `eTaskGetState` | xTask | eTaskState | 获取任务状态(running/ready/blocked/suspended/deleted) |
+| `vTaskList` | pcWriteBuffer | void | 将任务列表格式化输出为可读字符串 |
+| `vTaskGetRunTimeStats` | pcWriteBuffer | void | 获取任务运行时统计信息 |
+
+## Common Issues & Pitfalls
+
+1. **configUSE_TRACE_FACILITY 必须开启**:`vTaskList()`、`uxTaskGetSystemState()`、`eTaskGetState()` 等函数都需要此宏为 1。
+2. **configUSE_STATS_FORMATTING_FUNCTIONS 与 vTaskList**:`vTaskList()` 需要同时开启 `configUSE_TRACE_FACILITY` 和 `configUSE_STATS_FORMATTING_FUNCTIONS`。
+3. **运行时统计需要硬件定时器**:`vTaskGetRunTimeStats()` 依赖一个比 SysTick 更高分辨率的硬件定时器。如果不配置该定时器,统计结果无意义。使用 TIM6/TIM7 等基本定时器即可。
+4. **栈水位线(High Water Mark)的含义**:返回的是任务运行以来栈剩余空间的最小值,用这个值可以判断栈配置是否合理。如果返回值接近 0,说明栈空间紧张,需要增大栈大小。
+5. **缓冲区大小**:`vTaskList()` 和 `vTaskGetRunTimeStats()` 输出的文本内容随任务数量变化,确保提供的缓冲区足够大(每个任务约 40~60 字节)。
+6. **vTaskPrioritySet 修改优先级后的调度**:当提高一个低优先级任务的优先级时,如果新优先级高于当前运行任务的优先级,会立即触发一次任务切换。

+ 234 - 0
X-Knowledge-Base/raw/Joplin/嵌入式+Linux/FreeRTOS学习笔记/10-消息队列.md

@@ -0,0 +1,234 @@
+---
+tags: [source-summary]
+type: source
+source: "尚硅谷嵌入式技术之FreeRTOS实时操作系统 V1.0.3 — 第10章 消息队列"
+author: "尚硅谷研究院"
+date: 2026-07-17
+created: 2026-07-17
+---
+
+# 消息队列
+
+> **用生活理解**:消息队列就像快递柜。寄件人(发送任务)把包裹放进柜子,收件人(接收任务)从柜子里取走。柜子有固定数量的格子(队列长度),格子满了寄件人就得排队等空位,格子空了收件人就得等新包裹。每个格子只放固定尺寸的物品(数据拷贝),而不是放一把钥匙让你自己去取(传指针)。
+
+## Concept
+
+### Basic concept
+
+队列(Queue)是 FreeRTOS 中任务间通信的主要形式,用于在**任务之间**以及**中断和任务之间**发送消息。
+
+- **FIFO 顺序**:先发送的消息会被先接收
+- **线程安全**:队列操作是原子的,确保多任务环境中的数据完整性
+- **阻塞操作**:队列满时发送方可阻塞等待,队列空时接收方可阻塞等待
+- **传值而非传指针**:数据被拷贝到队列中(对于大数据可传指针优化)
+
+### Working principle
+
+队列内部结构包含:
+- **环形缓冲区**:存储实际数据
+- **发送等待列表**:队列满时,等待发送的任务进入阻塞
+- **接收等待列表**:队列空时,等待接收的任务进入阻塞
+
+当队列有空间时,最高优先级的等待发送任务将解除阻塞;当队列有数据时,最高优先级的等待接收任务将解除阻塞。
+
+### Core API
+
+| Function | Params | Description |
+|----------|--------|-------------|
+| `xQueueCreate()` | `uxQueueLength`, `uxItemSize` | 动态创建队列 |
+| `xQueueSend()` | `xQueue`, `pvItemToQueue`, `xTicksToWait` | 尾部写入消息 |
+| `xQueueSendToBack()` | 同上 | 同 xQueueSend() |
+| `xQueueSendToFront()` | 同上 | 头部写入消息 |
+| `xQueueReceive()` | `xQueue`, `pvBuffer`, `xTicksToWait` | 头部读取并删除消息 |
+| `xQueuePeek()` | 同上 | 头部读取消息(不删除) |
+| `xQueueOverwrite()` | `xQueue`, `pvItemToQueue` | 覆写(仅队列长度=1时可用) |
+
+## Experiment
+
+### Requirements
+
+学习使用 FreeRTOS 队列相关函数,包括创建队列、入队和出队操作:
+
+- **task1**:按键 KEY1/KEY2 按下时,将键值发送到 `queue1`(小数据队列);按键 KEY3 按下时,将大数据的地址发送到 `big_queue`(指针队列)
+- **task2**:从 `queue1` 读取消息并打印键值
+- **task3**:从 `big_queue` 读取大数据地址,通过地址访问数据
+
+### Code Implementation
+
+**File: P09/09_消息队列/Core/Src/freertos_demo.c**
+
+```c
+#include "queue.h"                          /* 队列操作头文件 */
+#include "FreeRTOS.h"
+#include "task.h"
+
+/* 任务配置 */
+#define START_TASK_PRIORITY     1
+#define START_TASK_STACK_DEPTH  128
+TaskHandle_t start_task_handler;
+void Start_Task(void *pvParameters);
+
+#define TASK1_PRIORITY          2
+#define TASK1_STACK_DEPTH       128
+TaskHandle_t task1_handler;
+void Task1(void *pvParameters);
+
+#define TASK2_PRIORITY          3
+#define TASK2_STACK_DEPTH       128
+TaskHandle_t task2_handler;
+void Task2(void *pvParameters);
+
+#define TASK3_PRIORITY          4
+#define TASK3_STACK_DEPTH       128
+TaskHandle_t task3_handler;
+void Task3(void *pvParameters);
+
+QueueHandle_t queue1;           /* 小数据队列句柄 */
+QueueHandle_t big_queue;        /* 大数据队列句柄 */
+char buff[100] = {"大大大fdahjk324hjkhfjksdahjk#$@!@#jfaskdfhjka"};
+
+/* FreeRTOS入口函数 */
+void FreeRTOS_Start(void)
+{
+    /* 创建queue1队列:长度2,每个元素1字节 */
+    queue1 = xQueueCreate(2, sizeof(uint8_t));
+    if (queue1 != NULL) {
+        printf("queue1队列创建成功\r\n");
+    } else {
+        printf("queue1队列创建失败\r\n");
+    }
+
+    /* 创建big_queue队列:长度1,每个元素为指针(char*) */
+    big_queue = xQueueCreate(1, sizeof(char *));
+    if (big_queue != NULL) {
+        printf("big_queue队列创建成功\r\n");
+    } else {
+        printf("big_queue队列创建失败\r\n");
+    }
+
+    xTaskCreate((TaskFunction_t)Start_Task,
+                (char *)"Start_Task",
+                (configSTACK_DEPTH_TYPE)START_TASK_STACK_DEPTH,
+                (void *)NULL,
+                (UBaseType_t)START_TASK_PRIORITY,
+                (TaskHandle_t *)&start_task_handler);
+    vTaskStartScheduler();
+}
+
+/* 启动任务:创建三个工作任务 */
+void Start_Task(void *pvParameters)
+{
+    taskENTER_CRITICAL();       /* 进入临界区 */
+
+    xTaskCreate((TaskFunction_t)Task1,
+                (char *)"Task1",
+                (configSTACK_DEPTH_TYPE)TASK1_STACK_DEPTH,
+                (void *)NULL,
+                (UBaseType_t)TASK1_PRIORITY,
+                (TaskHandle_t *)&task1_handler);
+
+    xTaskCreate((TaskFunction_t)Task2,
+                (char *)"Task2",
+                (configSTACK_DEPTH_TYPE)TASK2_STACK_DEPTH,
+                (void *)NULL,
+                (UBaseType_t)TASK2_PRIORITY,
+                (TaskHandle_t *)&task2_handler);
+
+    xTaskCreate((TaskFunction_t)Task3,
+                (char *)"Task3",
+                (configSTACK_DEPTH_TYPE)TASK3_STACK_DEPTH,
+                (void *)NULL,
+                (UBaseType_t)TASK3_PRIORITY,
+                (TaskHandle_t *)&task3_handler);
+
+    vTaskDelete(NULL);          /* 删除启动任务自身 */
+    taskEXIT_CRITICAL();        /* 退出临界区 */
+}
+
+/* task1:扫描按键,入队 */
+void Task1(void *pvParameters)
+{
+    uint8_t key = 0;
+    char *buf;
+    BaseType_t err = 0;
+    buf = &buff[0];             /* 获取大数据缓冲区地址 */
+
+    while (1) {
+        key = Key_Detect();
+        if (key == KEY1_PRESS || key == KEY2_PRESS) {
+            /* 发送键值到queue1队列,永久等待直到成功 */
+            err = xQueueSend(queue1, &key, portMAX_DELAY);
+            if (err != pdTRUE) {
+                printf("queue1队列发送失败\r\n");
+            }
+        } else if (key == KEY3_PRESS) {
+            /* 发送大数据地址到big_queue队列 */
+            err = xQueueSend(big_queue, &buf, portMAX_DELAY);
+            if (err != pdTRUE) {
+                printf("big_queue队列发送失败\r\n");
+            }
+        }
+        vTaskDelay(10);
+    }
+}
+
+/* task2:从queue1出队(小数据) */
+void Task2(void *pvParameters)
+{
+    uint8_t key = 0;
+    BaseType_t err = 0;
+
+    while (1) {
+        /* 从queue1读取键值,永久等待 */
+        err = xQueueReceive(queue1, &key, portMAX_DELAY);
+        if (err != pdTRUE) {
+            printf("queue1队列读取失败\r\n");
+        } else {
+            printf("queue1读取队列成功,数据:%d\r\n", key);
+        }
+    }
+}
+
+/* task3:从big_queue出队(大数据) */
+void Task3(void *pvParameters)
+{
+    char *buf;
+    BaseType_t err = 0;
+
+    while (1) {
+        /* 从big_queue读取指针,永久等待 */
+        err = xQueueReceive(big_queue, &buf, portMAX_DELAY);
+        if (err != pdTRUE) {
+            printf("big_queue队列读取失败\r\n");
+        } else {
+            printf("数据:%s\r\n", buf);    /* 通过指针访问大数据 */
+        }
+    }
+}
+```
+
+### Key Config
+
+```c
+/* FreeRTOSConfig.h */
+#define configQUEUE_REGISTRY_SIZE   8   /* 可注册的队列/信号量个数 */
+```
+
+## Core API Reference Table
+
+| Function | Parameters | Return | Description |
+|----------|-----------|--------|-------------|
+| `QueueHandle_t xQueueCreate(uxQueueLength, uxItemSize)` | 队列长度, 每项字节数 | NULL=失败, 非NULL=句柄 | 动态创建队列 |
+| `BaseType_t xQueueSend(xQueue, pvItemToQueue, xTicksToWait)` | 队列句柄, 数据指针, 超时ticks | pdPASS/errQUEUE_FULL | 尾部写入 |
+| `BaseType_t xQueueSendToFront(xQueue, pvItemToQueue, xTicksToWait)` | 同上 | 同上 | 头部写入 |
+| `BaseType_t xQueueReceive(xQueue, pvBuffer, xTicksToWait)` | 队列句柄, 接收缓冲区指针, 超时ticks | pdPASS/errQUEUE_EMPTY | 读取并删除 |
+| `BaseType_t xQueuePeek(xQueue, pvBuffer, xTicksToWait)` | 同上 | 同上 | 读取不删除 |
+| `BaseType_t xQueueOverwrite(xQueue, pvItemToQueue)` | 队列句柄, 数据指针 | pdPASS | 覆写(长度须为1) |
+| `UBaseType_t uxQueueMessagesWaiting(xQueue)` | 队列句柄 | 当前消息数 | 查询队列中消息数量 |
+
+## Common Issues & Pitfalls
+
+1. **队列项大小与发送不匹配**:`xQueueCreate` 的 `uxItemSize` 必须与发送数据大小一致。传结构体时特别注意大小。
+2. **队列满导致死等**:发送时若 `xTicksToWait = portMAX_DELAY` 且队列始终无空位,任务将永久阻塞。建议设计时确保接收方及时取走数据。
+3. **大数据拷贝开销**:队列默认传值(拷贝数据)。大数据建议传指针(如 `xQueueCreate(1, sizeof(char*))`),但需确保指针指向的内存生命周期有效。
+4. **中断中发送**:在 ISR 中使用 `xQueueSendFromISR()` 而非 `xQueueSend()`,且末尾调用 `portYIELD_FROM_ISR()` 触发切换。

+ 0 - 202
X-Knowledge-Base/raw/Joplin/嵌入式+Linux/FreeRTOS学习笔记/10-软件定时器与Tickless低功耗.md

@@ -1,202 +0,0 @@
----
-tags: [source-summary]
-type: source
-source: "尚硅谷嵌入式技术之FreeRTOS实时操作系统 V1.0.3 — 第17~18章 + 配套代码19/20"
-author: "尚硅谷研究院"
-date: 2026-07-12
-url: ""
-created: 2026-07-12
----
-
-# 软件定时器与 Tickless 低功耗
-
----
-
-![STM32 低功耗模式与 Tickless 模式示意图](assets/stm32_low_power_modes.png)
-
-## 软件定时器
-
-> **用生活理解**:软件定时器就像你手机上的"闹钟"——
-> - **一次性定时器**:设一个闹钟,响了就没了(比如"30分钟后关火")
-> - **周期性定时器**:设一个重复闹钟,每天这个点都响(比如"每天早上7点起床")
-
-> **精度限制**:软件定时器的精度由 tick 周期决定(如 1ms 节拍 → 最大精度 1ms),无法达到微秒级——这是与硬件定时器的本质区别。
->
-> **由专用任务管理**:软件定时器由调度器启动时自动创建的**定时器服务任务**(`prvTimerTask`)负责管理。所有定时器命令(启动/停止/复位)通过**命令队列**发送给该任务。回调函数在该任务上下文中执行,因此**不能调用任何阻塞 API**,否则会卡住整个定时器服务。
-
-### 硬件 vs 软件定时器
-
-| 特性 | 硬件定时器 | 软件定时器 |
-|------|-----------|-----------|
-| 本质 | 芯片内部真实计数器 | 用RTOS tick模拟的"假定时器" |
-| 精度 | 微秒级 | 受任务调度影响(毫秒级) |
-| 数量 | 有限(STM32F1有几个TIM) | 无限(只要内存够) |
-| 回调 | 在中断中执行 | 在"定时器服务任务"中执行 |
-
-### 示例:软件定时器(项目19)
-
-#### 情景
-- 创建两个定时器:timer1(一次性,500ms)和 timer2(周期性,1000ms)
-- 按 KEY1 → 启动两个定时器
-- 按 KEY2 → 停止两个定时器
-
-#### 核心代码
-
-```c
-#include "timers.h"
-
-TimerHandle_t timer1_handle;  // 一次性
-TimerHandle_t timer2_handle;  // 周期性
-
-void freertos_start(void)
-{
-    // 创建一次性定时器:500ms后执行一次
-    timer1_handle = xTimerCreate(
-        "timer1",          // 名字(调试用)
-        500,               // 周期 = 500个tick
-        pdFALSE,           // pdFALSE = 一次性,pdTRUE = 周期性
-        (void *)1,         // 定时器ID(可用于区分多个定时器)
-        timer1_callback);  // 超时回调函数
-
-    // 创建周期性定时器:每1000ms执行一次
-    timer2_handle = xTimerCreate(
-        "timer2", 1000, pdTRUE, (void *)2, timer2_callback);
-
-    xTaskCreate(/* task1 */);
-    vTaskStartScheduler();
-}
-
-/* ========== 控制任务 ========== */
-void task1(void *pvParameters)
-{
-    uint8_t key = 0;
-    while (1)
-    {
-        key = Key_Detect();
-        if (key == KEY1_PRESS)
-        {
-            xTimerStart(timer1_handle, portMAX_DELAY);  // 启动一次性
-            xTimerStart(timer2_handle, portMAX_DELAY);  // 启动周期性
-        }
-        else if (key == KEY2_PRESS)
-        {
-            xTimerStop(timer1_handle, portMAX_DELAY);   // 停止
-            xTimerStop(timer2_handle, portMAX_DELAY);
-        }
-        vTaskDelay(500);
-    }
-}
-
-/* ========== 回调函数 ========== */
-void timer1_callback(TimerHandle_t xTimer)
-{
-    static uint16_t count = 0;
-    printf("一次性定时器触发了第%d次\r\n", ++count);
-    // 一次性定时器:只会打印一次
-}
-
-void timer2_callback(TimerHandle_t xTimer)
-{
-    static uint16_t count = 0;
-    printf("周期性定时器触发了第%d次\r\n", ++count);
-    // 周期性定时器:一直打印下去
-}
-```
-
-### 定时器 API 速查
-
-| 函数 | 作用 | 类比 |
-|------|------|------|
-| `xTimerCreate()` | 创建定时器 | 买一个闹钟 |
-| `xTimerStart()` | 启动 | 拧上发条开始计时 |
-| `xTimerStop()` | 停止 | 按掉闹钟 |
-| `xTimerReset()` | 复位(重新开始计时) | 把闹钟重设为当前时间+N |
-| `xTimerChangePeriod()` | 修改周期 | 把闹钟改成不同的时间响 |
-
-### ⚠️ 定时器回调的约束
-
-定时器回调函数在**定时器服务任务**中执行,**不能调用阻塞 API**:
-```c
-// ❌ 错误:在回调中调用阻塞函数
-void timer_callback(TimerHandle_t xTimer)
-{
-    vTaskDelay(100);         // 禁止!会阻塞整个定时器服务任务
-    xQueueReceive(queue, &val, portMAX_DELAY);  // 禁止!
-    xSemaphoreTake(sem, portMAX_DELAY);         // 禁止!
-}
-
-// ✅ 正确做法:在回调里只做快速处理
-void timer_callback(TimerHandle_t xTimer)
-{
-    printf("超时了!\r\n");        // 可以:printf通常很快
-    LED_Toggle(LED1_Pin);         // 可以:GPIO操作极快
-    xQueueSend(queue, &val, 0);   // 可以:超时=0不阻塞
-}
-```
-
----
-
-## Tickless 低功耗模式
-
-> **用生活理解**:普通模式下,系统像一个人"每秒醒来看一次有没有事做"。Tickless 模式下,没事做就直接睡大觉,直到有事了才被叫醒。省电!
-
-### 工作原理
-
-```
-普通模式:  醒→看→没事→睡→醒→看→没事→睡→醒→看→有事→干→醒→看...
-                ↑                ↑                ↑
-             1秒醒一次       1秒醒一次        1秒醒一次(浪费电)
-
-Tickless:   醒→看→没事→睡大觉→有事→醒了→干活
-                ↑                          ↑
-             睡很久                     有人叫醒(省电!)
-```
-
-> **核心机制**:当所有任务都阻塞时,系统可以**预测空闲任务的持续时长**(从最近的阻塞任务超时时间计算),然后在此时间段内**禁用 SysTick 中断**,醒来时**一次性补偿**漏掉的 tick 计数。
->
-> **睡觉被手机吵醒类比**:"好不容易有半个小时要睡一会儿,结果你的手机一分钟响一次"——这就是不禁用 SysTick 的感受。Tickless 模式相当于睡前把手机调成静音,睡醒了再看所有未接来电。
->
-> **最小空闲时长配置**:可设置进入低功耗所需的最短空闲时间——如果空闲时间太短,"进入退出的开销得不偿失",就不进低功耗模式。
-
-### 配置方法
-
-```c
-// FreeRTOSConfig.h
-#define configUSE_TICKLESS_IDLE    1   // 开启低功耗
-
-// 定义"睡前处理"和"醒后处理"
-#define configPRE_SLEEP_PROCESSING(x)     PRE_SLEEP_PROCESSING()
-#define configPOST_SLEEP_PROCESSING(x)    POST_SLEEP_PROCESSING()
-
-// 睡前:关掉所有外设时钟以省电
-void PRE_SLEEP_PROCESSING(void)
-{
-    __HAL_RCC_GPIOA_CLK_DISABLE();
-    __HAL_RCC_GPIOB_CLK_DISABLE();
-    __HAL_RCC_GPIOC_CLK_DISABLE();
-    // ... 关掉当前不需要的外设
-}
-
-// 醒后:重新打开外设时钟
-void POST_SLEEP_PROCESSING(void)
-{
-    __HAL_RCC_GPIOA_CLK_ENABLE();
-    __HAL_RCC_GPIOB_CLK_ENABLE();
-    __HAL_RCC_GPIOC_CLK_ENABLE();
-    // ... 重新打开外设
-}
-```
-
-### STM32 低功耗模式对比
-
-| 模式 | 功耗 | 唤醒方式 | 醒来速度 | Tickless用哪个? |
-|------|------|---------|---------|----------------|
-| 睡眠(Sleep) | 中 | 任何中断 | 最快 | ✅ 默认用这个 |
-| 停机(Stop) | 低 | EXTI/RTC/USB | 中等 | ⚠️ 需额外配置 |
-| 待机(Standby) | 极低 | 复位/WKUP/RTC | 最慢 | ❌ 用不了 |
-
-### 配套代码
-- `code/19_软件定时器/`
-- `code/20_低功耗模式/`
-
-> 后续章节:[[11-内存管理]]

+ 359 - 0
X-Knowledge-Base/raw/Joplin/嵌入式+Linux/FreeRTOS学习笔记/11-信号量机制.md

@@ -0,0 +1,359 @@
+---
+tags: [source-summary]
+type: source
+source: "尚硅谷嵌入式技术之FreeRTOS实时操作系统 V1.0.3 — 第11章 信号量"
+author: "尚硅谷研究院"
+date: 2026-07-17
+created: 2026-07-17
+---
+
+# 信号量机制
+
+> **用生活理解**:信号量就像动车上的卫生间指示灯。二值信号量 = 一个卫生间,有人/无人两种状态(0/1);计数信号量 = 一排卫生间,有几个空闲就给几个"可用"标志;互斥信号量 = 带"插队保护"的卫生间——如果一个大佬(高优先级任务)在等,正占用卫生间的人会临时获得"VIP卡"(优先级提升),赶紧用完让位。
+
+## Concept
+
+### Basic concept
+
+信号量(Semaphore)是 FreeRTOS 中用于任务间同步和资源管理的机制,底层基于队列实现。
+
+- **二值信号量**:只能取 0 或 1,本质是长度为 1 的队列。用于事件通知、同步
+- **计数信号量**:可取 0~N,本质是长度为 N 的队列。用于资源计数
+- **互斥信号量**:特殊的二值信号量,具有**优先级继承**特性,解决优先级翻转问题
+
+### Working principle
+
+信号量操作只有两个:**Give(释放)**和 **Take(获取)**。
+- Take 成功减 1,Give 成功加 1
+- 计数为 0 时 Take 操作可阻塞等待
+- 阻塞时高优先级任务优先解除阻塞
+
+**优先级翻转**(Priority Inversion):
+- 低优先级任务持有信号量 → 高优先级任务等待信号量 → 中优先级任务抢占低优先级任务
+- 结果:高优先级任务被中优先级任务间接阻塞,优先级"倒反天罡"
+
+**优先级继承**(Mutex 解决翻转):
+- 低优先级任务持有互斥信号量时,若有高优先级任务在等待,低优先级任务被**临时提升**到高优先级任务的优先级
+- 释放后恢复原优先级,中优先级任务无法再抢占
+
+### Core API
+
+| Function | Params | Description |
+|----------|--------|-------------|
+| `xSemaphoreCreateBinary()` | 无 | 动态创建二值信号量 |
+| `xSemaphoreCreateCounting()` | `uxMaxCount`, `uxInitialCount` | 动态创建计数信号量 |
+| `xSemaphoreCreateMutex()` | 无 | 动态创建互斥信号量 |
+| `xSemaphoreGive()` | `xSemaphore` | 释放信号量 |
+| `xSemaphoreTake()` | `xSemaphore`, `xTicksToWait` | 获取信号量 |
+| `uxSemaphoreGetCount()` | `xSemaphore` | 获取当前计数值 |
+
+### Comparison Table
+
+| Feature | Binary | Counting | Mutex |
+|---------|--------|----------|-------|
+| Initial Value | 0 | User specified | 1 |
+| Max Value | 1 | User specified | 1 |
+| Priority Inheritance | No | No | Yes |
+| Recursive | No | No | Configurable |
+| Use Case | Event notification | Resource counting | Mutual exclusion |
+
+## Experiment
+
+### Experiment 1: 二值信号量 (P10)
+
+**Requirements**: KEY1 释放信号量 → task2 获取信号量并打印。
+
+**File: P10/10_二值信号量/Core/Src/freertos_demo.c**
+
+```c
+#include "semphr.h"                         /* 信号量操作头文件 */
+
+QueueHandle_t semphore_handle;              /* 二值信号量句柄 */
+
+void FreeRTOS_Start(void)
+{
+    /* 动态创建二值信号量,初始值为0 */
+    semphore_handle = xSemaphoreCreateBinary();
+    if (semphore_handle != NULL) {
+        printf("二值信号量创建成功\r\n");
+    }
+
+    xTaskCreate((TaskFunction_t)Start_Task,
+                (char *)"Start_Task",
+                (configSTACK_DEPTH_TYPE)START_TASK_STACK_DEPTH,
+                (void *)NULL,
+                (UBaseType_t)START_TASK_PRIORITY,
+                (TaskHandle_t *)&start_task_handler);
+    vTaskStartScheduler();
+}
+
+/* task1:KEY1按下时释放信号量 */
+void Task1(void *pvParameters)
+{
+    uint8_t key = 0;
+    BaseType_t err;
+
+    while (1) {
+        key = Key_Detect();
+        if (key == KEY1_PRESS) {
+            if (semphore_handle != NULL) {
+                /* 释放(Give)二值信号量,计数值变为1 */
+                err = xSemaphoreGive(semphore_handle);
+                if (err == pdPASS) {
+                    printf("信号量释放成功\r\n");
+                } else
+                    printf("信号量释放失败\r\n");
+            }
+        }
+        vTaskDelay(10);
+    }
+}
+
+/* task2:获取信号量,永久等待 */
+void Task2(void *pvParameters)
+{
+    uint32_t i = 0;
+    BaseType_t err;
+
+    while (1) {
+        /* 获取(Take)信号量,无信号时永久阻塞 */
+        err = xSemaphoreTake(semphore_handle, portMAX_DELAY);
+        if (err == pdTRUE) {
+            printf("获取信号量成功\r\n");
+        } else {
+            printf("已超时%d\r\n", ++i);
+        }
+    }
+}
+```
+
+### Experiment 2: 计数型信号量 (P11)
+
+**Requirements**: KEY1 释放计数信号量,task2 每秒获取一次并打印计数值。
+
+**File: P11/11_计数型信号量/Core/Src/freertos_demo.c**
+
+```c
+#include "semphr.h"
+
+QueueHandle_t count_semphore_handle;        /* 计数型信号量句柄 */
+
+void FreeRTOS_Start(void)
+{
+    /* 创建计数信号量:最大100,初始0 */
+    count_semphore_handle = xSemaphoreCreateCounting(100, 0);
+    if (count_semphore_handle != NULL) {
+        printf("计数型信号量创建成功\r\n");
+    }
+
+    xTaskCreate((TaskFunction_t)Start_Task,
+                (char *)"Start_Task",
+                (configSTACK_DEPTH_TYPE)START_TASK_STACK_DEPTH,
+                (void *)NULL,
+                (UBaseType_t)START_TASK_PRIORITY,
+                (TaskHandle_t *)&start_task_handler);
+    vTaskStartScheduler();
+}
+
+/* task1:KEY1按下释放一次计数信号量 */
+void Task1(void *pvParameters)
+{
+    uint8_t key = 0;
+
+    while (1) {
+        key = Key_Detect();
+        if (key == KEY1_PRESS) {
+            if (count_semphore_handle != NULL) {
+                xSemaphoreGive(count_semphore_handle);
+            }
+        }
+        vTaskDelay(10);
+    }
+}
+
+/* task2:每秒获取一次计数信号量,打印剩余计数值 */
+void Task2(void *pvParameters)
+{
+    BaseType_t err = 0;
+
+    while (1) {
+        err = xSemaphoreTake(count_semphore_handle, portMAX_DELAY);
+        if (err == pdTRUE) {
+            /* uxSemaphoreGetCount() 获取当前计数值 */
+            printf("信号量的计数值=%d\r\n",
+                   (int)uxSemaphoreGetCount(count_semphore_handle));
+        }
+        vTaskDelay(1000);
+    }
+}
+```
+
+### Experiment 3: 优先级翻转 (P12)
+
+**Requirements**: 模拟优先级翻转场景,观察使用二值信号量时的"倒反天罡"现象。
+
+**File: P12/12_优先级翻转/Core/Src/freertos_demo.c**
+
+```c
+#include "semphr.h"
+
+QueueHandle_t semphore_handle;
+/* Task1 (prio 2): 低优先级 — 先获取信号量,然后HAL_Delay(3000)模拟长时间占用 */
+/* Task2 (prio 3): 中优先级 — 只打印,证明它能抢占低优先级 */
+/* Task3 (prio 4): 高优先级 — 等待信号量,但在低优先级释放前被中优先级"插队" */
+
+void FreeRTOS_Start(void)
+{
+    /* 创建二值信号量,并主动释放一次使其初始值为1 */
+    semphore_handle = xSemaphoreCreateBinary();
+    if (semphore_handle != NULL) {
+        printf("二值信号量创建成功\r\n");
+    }
+    xSemaphoreGive(semphore_handle);        /* 初始释放一次 */
+
+    xTaskCreate((TaskFunction_t)Start_Task,
+                (char *)"Start_Task",
+                (configSTACK_DEPTH_TYPE)START_TASK_STACK_DEPTH,
+                (void *)NULL,
+                (UBaseType_t)START_TASK_PRIORITY,
+                (TaskHandle_t *)&start_task_handler);
+    vTaskStartScheduler();
+}
+
+/* 低优先级任务 Task1 (prio=2):获取信号量后占用3秒 */
+void Task1(void *pvParameters)
+{
+    while (1) {
+        printf("低优先级Task1获取信号量\r\n");
+        xSemaphoreTake(semphore_handle, portMAX_DELAY);  /* 获取信号量 */
+        printf("低优先级Task1正在运行\r\n");
+        HAL_Delay(3000);                                /* 模拟长时间占用资源 */
+        printf("低优先级Task1释放信号量\r\n");
+        xSemaphoreGive(semphore_handle);                 /* 释放信号量 */
+        vTaskDelay(1000);
+    }
+}
+
+/* 中优先级任务 Task2 (prio=3):纯打印,抢占低优先级 */
+void Task2(void *pvParameters)
+{
+    while (1) {
+        printf("中优先级的Task2正在执行\r\n");
+        HAL_Delay(1500);
+        printf("Task2 执行完成一次.....\r\n");
+        vTaskDelay(1000);
+    }
+}
+
+/* 高优先级任务 Task3 (prio=4):等待信号量,被中优先级"插队"阻塞 */
+void Task3(void *pvParameters)
+{
+    while (1) {
+        printf("高优先级Task3获取信号量\r\n");
+        xSemaphoreTake(semphore_handle, portMAX_DELAY);  /* 等待信号量 */
+        printf("高优先级Task3正在运行\r\n");
+        HAL_Delay(1000);
+        printf("高优先级Task3释放信号量\r\n");
+        xSemaphoreGive(semphore_handle);                 /* 释放信号量 */
+        vTaskDelay(1000);
+    }
+}
+```
+
+### Experiment 4: 互斥信号量 (P13)
+
+**Requirements**: 与 P12 相同实验场景,但使用互斥信号量 `xSemaphoreCreateMutex()` 观察优先级继承解决翻转。
+
+**File: P13/13_互斥信号量/Core/Src/freertos_demo.c**
+
+```c
+#include "semphr.h"
+
+QueueHandle_t mutex_semphore_handle;        /* 互斥信号量句柄 */
+
+void FreeRTOS_Start(void)
+{
+    /* 创建互斥信号量(已自动释放一次,初始值为1) */
+    mutex_semphore_handle = xSemaphoreCreateMutex();
+    if (mutex_semphore_handle != NULL) {
+        printf("互斥信号量创建成功\r\n");
+    }
+
+    xTaskCreate((TaskFunction_t)Start_Task,
+                (char *)"Start_Task",
+                (configSTACK_DEPTH_TYPE)START_TASK_STACK_DEPTH,
+                (void *)NULL,
+                (UBaseType_t)START_TASK_PRIORITY,
+                (TaskHandle_t *)&start_task_handler);
+    vTaskStartScheduler();
+}
+
+/* 低优先级任务:使用互斥信号量,优先级被临时提升 */
+void Task1(void *pvParameters)
+{
+    while (1) {
+        printf("低优先级Task1获取信号量\r\n");
+        xSemaphoreTake(mutex_semphore_handle, portMAX_DELAY);
+        printf("低优先级Task1正在运行\r\n");
+        HAL_Delay(3000);        /* 此时若高优先级等待,Task1优先级被提升到与Task3相同 */
+        printf("低优先级Task1释放信号量\r\n");
+        xSemaphoreGive(mutex_semphore_handle);
+        vTaskDelay(1000);
+    }
+}
+
+/* 中优先级任务:由于低优先级已被提升,无法抢占 */
+void Task2(void *pvParameters)
+{
+    while (1) {
+        printf("中优先级的Task2正在执行\r\n");
+        HAL_Delay(1500);
+        printf("Task2 for循环完毕.....\r\n");
+        vTaskDelay(1000);
+    }
+}
+
+/* 高优先级任务:等待互斥信号量 */
+void Task3(void *pvParameters)
+{
+    while (1) {
+        printf("高优先级Task3获取信号量\r\n");
+        xSemaphoreTake(mutex_semphore_handle, portMAX_DELAY);
+        printf("高优先级Task3正在运行\r\n");
+        HAL_Delay(1000);
+        printf("高优先级Task3释放信号量\r\n");
+        xSemaphoreGive(mutex_semphore_handle);
+        vTaskDelay(1000);
+    }
+}
+```
+
+### Key Config
+
+```c
+/* FreeRTOSConfig.h */
+#define configUSE_COUNTING_SEMAPHORES   1   /* 使能计数信号量 */
+#define configUSE_MUTEXES               1   /* 使能互斥信号量 */
+#define configUSE_RECURSIVE_MUTEXES     1   /* 使能递归互斥信号量 */
+```
+
+## Core API Reference Table
+
+| Function | Parameters | Return | Description |
+|----------|-----------|--------|-------------|
+| `SemaphoreHandle_t xSemaphoreCreateBinary()` | 无 | NULL=失败 | 创建二值信号量(初始0) |
+| `SemaphoreHandle_t xSemaphoreCreateCounting(uxMaxCount, uxInitialCount)` | 最大计数值, 初始值 | NULL=失败 | 创建计数信号量 |
+| `SemaphoreHandle_t xSemaphoreCreateMutex()` | 无 | NULL=失败 | 创建互斥信号量(初始1,含优先级继承) |
+| `BaseType_t xSemaphoreGive(xSemaphore)` | 信号量句柄 | pdPASS/pdFALSE | 释放信号量(+1) |
+| `BaseType_t xSemaphoreTake(xSemaphore, xTicksToWait)` | 信号量句柄, 超时 | pdPASS/pdFALSE | 获取信号量(-1) |
+| `BaseType_t xSemaphoreGiveFromISR(xSemaphore, pxHigherPriorityTaskWoken)` | 同上+唤醒标记 | pdPASS/pdFALSE | 中断中释放 |
+| `UBaseType_t uxSemaphoreGetCount(xSemaphore)` | 信号量句柄 | 当前计数值 | 查询当前计数 |
+| `BaseType_t xSemaphoreCreateRecursiveMutex()` | 无 | 句柄/NULL | 创建递归互斥信号量 |
+
+## Common Issues & Pitfalls
+
+1. **二值信号量初始值为 0**:创建后需先 Give 一次才能被 Take。常见错误是忘记初始 Give 导致第一个 Take 永远阻塞。
+2. **互斥信号量不能在中断中使用**:因为优先级继承机制依赖任务上下文。ISR 中使用 `xSemaphoreGiveFromISR()` 仅对二值/计数信号量有效。
+3. **优先级翻转的隐蔽性**:低→中→高三优先级任务存在时才有可能出现。调试时可通过 `vTaskList()` 观察任务状态。
+4. **互斥信号量与二值信号量的选择**:纯同步用二值信号量,保护共享资源用互斥信号量。
+5. **递归互斥信号量**:同一个任务可多次 Take 同一个互斥信号量(嵌套使用),适用于递归函数中保护临界区。

+ 0 - 139
X-Knowledge-Base/raw/Joplin/嵌入式+Linux/FreeRTOS学习笔记/11-内存管理.md

@@ -1,139 +0,0 @@
----
-tags: [source-summary]
-type: source
-source: "尚硅谷嵌入式技术之FreeRTOS实时操作系统 V1.0.3 — 第19章 + 配套代码21"
-author: "尚硅谷研究院"
-date: 2026-07-12
-url: ""
-created: 2026-07-12
----
-
-# 内存管理
-
-> **用生活理解**:内存管理就像图书馆的座位管理——
-> - **heap_1**:图书馆只进不出,每个人来了就固定坐一个位置,永远不走(最简单)
-> - **heap_2**:可以走了,但走之后留下的空位不合并——时间长了满是小缝隙,大桌子放不下
-> - **heap_4**:有人走了,把旁边空位合并成大空间(推荐!)
-> - **heap_5**:图书馆有多个独立区域,可以跨区域管理座位
-
-## 为什么 FreeRTOS 不用标准 malloc?
-
-在嵌入式系统中,标准 C 库的 `malloc()`/`free()` 有这些问题:
-1. **不一定可用**——很多 MCU 开发环境没带完整 C 库
-2. **占用代码空间**——printf、malloc 全家桶很大
-3. **非线程安全**——两个任务同时 malloc 可能崩溃
-4. **非确定性**——malloc 执行时间不确定(RTOS 要求确定!)
-
-## 五种 heap 算法对比
-
-| 算法 | 策略 | 合并空闲块 | 允许释放 | 适用场景 | 推荐? |
-|------|------|-----------|---------|---------|:----:|
-| heap_1 | 简单数组分配 | 不适用 | ❌ 不能释放 | 只创建不删除的项目 | |
-| heap_2 | 最佳适应(Best-fit) | ❌ | ✅ | 任务栈大小固定 | ❌ 已过时 |
-| heap_3 | C 库 malloc/free | 由 C 库决定 | ✅ | 标准库可用时 | |
-| **heap_4** | **首次适应(First-fit)** | **✅** | ✅ | **通用场景** | **⭐ 推荐** |
-| heap_5 | 跨区域 First-fit | ✅(跨区域) | ✅ | 内存地址不连续 | |
-
-![heap_1 内存分配示意图:简单数组,只分配不释放](assets/heap1.png)
-
-### heap_1:最简单(不能释放)
-
-> **"蹲坑不冲水"**:只申请不释放,就像上厕所不冲水——简单粗暴,但只适用于项目启动时一次性创建所有对象的场景。
-
-```
-初始化: [         整个堆空间          ]
-分配后: [ Task1 ][ Task2 ][ Task3 ][ 剩余 ]
-```
-
-一次分配、永不释放。适用于项目启动时创建所有任务/队列,运行中不删除任何东西。
-
-![heap_2 最佳适应算法:不合并碎片,时间长产生大量小碎片](assets/heap2_best_fit.png)
-
-### heap_2:最佳适应(过时)
-
-> **"伤过的心已经变成玻璃碎片"**:最佳适应算法(Best-fit)从满足要求的块中选择最接近请求大小的。问题是不合并相邻空闲块——时间长满是小碎片,大块分配失败。
-
-```
-释放后: [ Task1 ][ 空闲 ][ Task3 ][ 空闲 ]
-         ↑不合并          ↑不合并
-        变成小碎片        变成小碎片
-分配16字节:选"最适合"的块(不一定是最大的)
-问题:碎片越来越多,最终大块分配失败
-```
-
-![heap_4 第一适应算法:合并相邻空闲块减少碎片](assets/heap4_first_fit.png)
-
-### heap_3:C 库包装
-
-> 标准 C 库 `malloc()`/`free()` 的简单线程安全包装。如果开发环境自带 C 库就可以用,但和裸机 malloc 一样有非确定性问题。
-
-### heap_4:第一适应 + 合并(推荐)
-
-> **首次适应算法**(First-fit):按地址顺序找到第一个足够大的块就用,不追求最优匹配(更快)。**自动合并相邻空闲块**——有人走了,把旁边空位合并成大空间。
-
-```
-释放后: [ Task1 ][ 空闲 ][ Task3 ][ 空闲 ]
-         ↓自动合并相邻空闲块
-         [ Task1 ][   大空闲空间   ]
-分配时:选"第一个足够大的"块
-```
-
-**最多碎片控制**:相邻空闲块自动合并,减少碎片。
-
-### heap_5:跨区域 heap_4
-
-> **"杀鸡焉用牛刀"**:能合并不连续的内存区域,但开销大。除非你的内存物理上就不连续(比如外扩 SARM 和片内 SRAM 要统一管理),否则用 heap_4 就够了。
-
-```
-内存布局: [ 区域A 128KB ]    [ 区域B 64KB ]    [ 区域C 32KB ]
-           ↑ 不连续但heap_5能统一管理
-```
-
-## 示例:内存管理(项目21)
-
-### 核心代码
-
-```c
-void task1(void *pvParameters)
-{
-    uint8_t key = 0;
-    void *buff = NULL;
-
-    while (1)
-    {
-        key = Key_Detect();
-
-        if (key == KEY1_PRESS)
-        {
-            // 申请20字节内存
-            buff = pvPortMalloc(20);
-            if (buff != NULL) {
-                printf("申请20字节成功!剩余堆=%d\r\n",
-                       xPortGetFreeHeapSize());
-            }
-        }
-        else if (key == KEY2_PRESS)
-        {
-            // 释放内存
-            vPortFree(buff);
-            printf("释放成功!剩余堆=%d\r\n",
-                   xPortGetFreeHeapSize());
-        }
-
-        vTaskDelay(500);
-    }
-}
-```
-
-### 内存 API
-
-| 函数 | 作用 | 类比 |
-|------|------|------|
-| `pvPortMalloc(size)` | 申请 size 字节 | 跟图书馆说"我要坐这儿" |
-| `vPortFree(ptr)` | 释放之前申请的内存 | 离开座位 |
-| `xPortGetFreeHeapSize()` | 查还剩多少堆空间 | 看看图书馆还有多少空位 |
-
-### 配套代码
-- `code/21_内存管理/`
-
-> 相关:[[03-FreeRTOS移植指南]](移植时需选择 heap_4.c)

+ 205 - 0
X-Knowledge-Base/raw/Joplin/嵌入式+Linux/FreeRTOS学习笔记/12-队列集.md

@@ -0,0 +1,205 @@
+---
+tags: [source-summary]
+type: source
+source: "尚硅谷嵌入式技术之FreeRTOS实时操作系统 V1.0.3 — 第12章 队列集"
+author: "尚硅谷研究院"
+date: 2026-07-17
+created: 2026-07-17
+---
+
+# 队列集
+
+> **用生活理解**:队列集就像医院的叫号系统。你去挂号(注册队列),然后在候诊区等待。不管是哪个科室(队列/信号量)叫号,你都能听到(统一通知)。不用每个科室门口都去排队看。
+
+## Concept
+
+### Basic concept
+
+队列集(Queue Set)是 FreeRTOS 中用于管理多个队列/信号量的数据结构。它解决了"一个任务需要等待多个队列或信号量"的问题。
+
+- **集中管理**:将多个队列、信号量注册到同一个队列集中
+- **统一等待**:任务调用 `xQueueSelectFromSet()` 阻塞等待任一注册成员有数据
+- **单出接口**:哪个成员有数据,返回该成员的句柄;任务再对该成员执行读操作
+
+### Working principle
+
+1. 创建队列集(指定可容纳的成员数量)
+2. 创建队列、信号量等成员
+3. 调用 `xQueueAddToSet()` 注册成员
+4. 任务调用 `xQueueSelectFromSet()` 阻塞等待
+5. 有成员数据到达 → 返回该成员句柄 → 任务对该成员执行读/取操作
+
+**限制**:
+- 队列集中的成员不能独立删除(必须先移出集再删除)
+- 队列集的大小在创建时固定
+
+### Core API
+
+| Function | Params | Description |
+|----------|--------|-------------|
+| `xQueueCreateSet()` | `uxEventQueueLength` | 创建队列集 |
+| `xQueueAddToSet()` | `xQueueOrSemaphore`, `xQueueSet` | 注册成员到队列集 |
+| `xQueueRemoveFromSet()` | `xQueueOrSemaphore`, `xQueueSet` | 从队列集中移除成员 |
+| `xQueueSelectFromSet()` | `xQueueSet`, `xTicksToWait` | 等待队列集中任一成员有数据 |
+| `xQueueSelectFromSetFromISR()` | `xQueueSet` | 中断中查询就绪成员 |
+
+## Experiment
+
+### Requirements
+
+学习使用 FreeRTOS 队列集:
+
+- **task1**:KEY1 → 往队列写入数据;KEY2 → 释放二值信号量
+- **task2**:从队列集中等待,根据返回的成员句柄执行对应操作
+
+### Code Implementation
+
+**File: P14/14_队列集/Core/Src/freertos_demo.c**
+
+```c
+#include "queue.h"                          /* 队列操作头文件 */
+#include "semphr.h"                         /* 信号量操作头文件 */
+
+/* 任务配置 */
+#define START_TASK_PRIORITY     1
+#define START_TASK_STACK_DEPTH  128
+TaskHandle_t start_task_handler;
+void Start_Task(void *pvParameters);
+
+#define TASK1_PRIORITY          2
+#define TASK1_STACK_DEPTH       128
+TaskHandle_t task1_handler;
+void Task1(void *pvParameters);
+
+#define TASK2_PRIORITY          3
+#define TASK2_STACK_DEPTH       128
+TaskHandle_t task2_handler;
+void Task2(void *pvParameters);
+
+QueueSetHandle_t queueset_handle;           /* 队列集句柄 */
+QueueHandle_t queue_handle;                 /* 普通队列句柄 */
+QueueHandle_t semphr_handle;                /* 二值信号量句柄 */
+
+/* FreeRTOS入口函数 */
+void FreeRTOS_Start(void)
+{
+    xTaskCreate((TaskFunction_t)Start_Task,
+                (char *)"Start_Task",
+                (configSTACK_DEPTH_TYPE)START_TASK_STACK_DEPTH,
+                (void *)NULL,
+                (UBaseType_t)START_TASK_PRIORITY,
+                (TaskHandle_t *)&start_task_handler);
+    vTaskStartScheduler();
+}
+
+/* 启动任务:创建队列集、队列、信号量并注册 */
+void Start_Task(void *pvParameters)
+{
+    taskENTER_CRITICAL();
+
+    /* 创建队列集,可容纳2个成员 */
+    queueset_handle = xQueueCreateSet(2);
+    if (queueset_handle != NULL) {
+        printf("队列集创建成功\r\n");
+    }
+
+    /* 创建普通队列:长度1,元素1字节 */
+    queue_handle = xQueueCreate(1, sizeof(uint8_t));
+
+    /* 创建二值信号量 */
+    semphr_handle = xSemaphoreCreateBinary();
+
+    /* 将队列和信号量注册到队列集 */
+    xQueueAddToSet(queue_handle, queueset_handle);
+    xQueueAddToSet(semphr_handle, queueset_handle);
+
+    xTaskCreate((TaskFunction_t)Task1,
+                (char *)"Task1",
+                (configSTACK_DEPTH_TYPE)TASK1_STACK_DEPTH,
+                (void *)NULL,
+                (UBaseType_t)TASK1_PRIORITY,
+                (TaskHandle_t *)&task1_handler);
+
+    xTaskCreate((TaskFunction_t)Task2,
+                (char *)"Task2",
+                (configSTACK_DEPTH_TYPE)TASK2_STACK_DEPTH,
+                (void *)NULL,
+                (UBaseType_t)TASK2_PRIORITY,
+                (TaskHandle_t *)&task2_handler);
+
+    vTaskDelete(NULL);
+    taskEXIT_CRITICAL();
+}
+
+/* task1:KEY1发队列,KEY2释放信号量 */
+void Task1(void *pvParameters)
+{
+    uint8_t key = 0;
+    BaseType_t err = 0;
+
+    while (1) {
+        key = Key_Detect();
+        if (key == KEY1_PRESS) {
+            /* 往队列写入数据 */
+            err = xQueueSend(queue_handle, &key, portMAX_DELAY);
+            if (err == pdPASS) {
+                printf("往队列queue_handle写入数据成功\r\n");
+            }
+        } else if (key == KEY2_PRESS) {
+            /* 释放二值信号量 */
+            err = xSemaphoreGive(semphr_handle);
+            if (err == pdPASS) {
+                printf("释放信号量成功\r\n");
+            }
+        }
+        vTaskDelay(10);
+    }
+}
+
+/* task2:从队列集中等待任一成员就绪 */
+void Task2(void *pvParameters)
+{
+    QueueSetMemberHandle_t member_handle;
+    uint8_t key;
+
+    while (1) {
+        /* 阻塞等待队列集中任一成员有数据,永久等待 */
+        member_handle = xQueueSelectFromSet(queueset_handle, portMAX_DELAY);
+
+        if (member_handle == queue_handle) {
+            /* 是队列成员就绪 */
+            xQueueReceive(member_handle, &key, portMAX_DELAY);
+            printf("获取到的队列数据=%d\r\n", key);
+        } else if (member_handle == semphr_handle) {
+            /* 是信号量成员就绪 */
+            xSemaphoreTake(member_handle, portMAX_DELAY);
+            printf("获取信号量成功\r\n");
+        }
+    }
+}
+```
+
+### Key Config
+
+```c
+/* FreeRTOSConfig.h */
+#define configUSE_QUEUE_SETS    1   /* 使能队列集功能 */
+```
+
+## Core API Reference Table
+
+| Function | Parameters | Return | Description |
+|----------|-----------|--------|-------------|
+| `QueueSetHandle_t xQueueCreateSet(uxEventQueueLength)` | 事件队列长度 | NULL=失败 | 创建队列集 |
+| `BaseType_t xQueueAddToSet(xQueueOrSemaphore, xQueueSet)` | 成员句柄, 队列集句柄 | pdPASS/pdFAIL | 注册成员 |
+| `BaseType_t xQueueRemoveFromSet(xQueueOrSemaphore, xQueueSet)` | 同上 | pdPASS/pdFAIL | 移除成员 |
+| `QueueSetMemberHandle_t xQueueSelectFromSet(xQueueSet, xTicksToWait)` | 队列集句柄, 超时 | NULL=超时 | 等待就绪成员 |
+| `QueueSetMemberHandle_t xQueueSelectFromSetFromISR(xQueueSet)` | 队列集句柄 | NULL=无就绪 | ISR中查询就绪成员 |
+
+## Common Issues & Pitfalls
+
+1. **队列集大小需足够**:`xQueueCreateSet(N)` 的 N 必须 ≥ 注册的成员总数,否则注册可能失败。
+2. **已注册成员不可独立删除**:必须先 `xQueueRemoveFromSet()` 再删除,否则行为未定义。
+3. **队列集不可嵌套**:不能将一个队列集注册到另一个队列集中。
+4. **信号量在队列集中的使用**:二值/计数信号量均可注册到队列集,但互斥信号量不可以。
+5. **Select 后必须立即操作**:`xQueueSelectFromSet()` 返回后,该成员已从等待列表中移除,若不及时读取可能导致其他任务抢先读取。

+ 189 - 0
X-Knowledge-Base/raw/Joplin/嵌入式+Linux/FreeRTOS学习笔记/13-事件标志组.md

@@ -0,0 +1,189 @@
+---
+tags: [source-summary]
+type: source
+source: "尚硅谷嵌入式技术之FreeRTOS实时操作系统 V1.0.3 — 第13章 事件标志组"
+author: "尚硅谷研究院"
+date: 2026-07-17
+created: 2026-07-17
+---
+
+# 事件标志组
+
+> **用生活理解**:事件标志组就像任务清单上的多个"待办事项"打勾。你可以等"所有事项都完成"(AND逻辑)才行动,也可以等"任意一个完成"(OR逻辑)就行动。打勾后还可以选择"自动擦掉"(自动清除)或"留着标记"(手动清除)。
+
+## Concept
+
+### Basic concept
+
+事件标志组(Event Group)是 FreeRTOS 中用于任务间事件通知和同步的轻量级机制。每个标志位代表一个特定的状态或事件。
+
+- **24 个可用位**:高 8 位保留给内核(32 位系统中)
+- **AND 模式**:等待多个位**全部**置 1 才解除阻塞
+- **OR 模式**:等待多个位**任意一个**置 1 即解除阻塞
+- **自动清除**:等待成功后自动清零标志位
+- **手动清除**:等待成功后标志位保持不变,需手动 `xEventGroupClearBits()`
+
+### Working principle
+
+事件标志组中的所有事件位存储在 `EventBits_t` 类型的单个无符号整数变量中。位 0 存储事件 0,位 1 存储事件 1,以此类推。
+
+![](assets/event_group_bits.png)
+
+事件标志组 vs 信号量:
+
+| 事件标志组 | 信号量 |
+|-----------|--------|
+| 事件通知和同步 | 资源控制和同步 |
+| 每个位代表不同事件 | 计数器表示可用资源数量 |
+| 任务可等待多个位同时/任一个发生 | 任务等待计数值 > 0 |
+
+### Core API
+
+| Function | Params | Description |
+|----------|--------|-------------|
+| `xEventGroupCreate()` | 无 | 动态创建事件标志组 |
+| `xEventGroupSetBits()` | `xEventGroup`, `uxBitsToSet` | 设置事件标志位 |
+| `xEventGroupClearBits()` | `xEventGroup`, `uxBitsToClear` | 清零事件标志位 |
+| `xEventGroupWaitBits()` | `xEventGroup`, `uxBitsToWaitFor`, `xClearOnExit`, `xWaitForAllBits`, `xTicksToWait` | 等待事件标志位 |
+| `xEventGroupSync()` | `xEventGroup`, `uxBitsToSet`, `uxBitsToWaitFor`, `xTicksToWait` | 设置并等待(原子操作) |
+| `xEventGroupGetBits()` | `xEventGroup` | 获取当前事件标志位值 |
+
+## Experiment
+
+### Requirements
+
+学习使用 FreeRTOS 事件标志组:
+
+- **task1**:KEY1 → 置位 EVENTBIT_0;KEY2 → 置位 EVENTBIT_1
+- **task2**:等待 EVENTBIT_0 和 EVENTBIT_1 **都**置位后打印,自动清除
+
+### Code Implementation
+
+**File: P15/15_事件标志组/Core/Src/freertos_demo.c**
+
+```c
+#include "event_groups.h"                   /* 事件标志组头文件 */
+
+/* 任务配置 */
+#define START_TASK_PRIORITY     1
+#define START_TASK_STACK_DEPTH  128
+TaskHandle_t start_task_handler;
+void Start_Task(void *pvParameters);
+
+#define TASK1_PRIORITY          2
+#define TASK1_STACK_DEPTH       128
+TaskHandle_t task1_handler;
+void Task1(void *pvParameters);
+
+#define TASK2_PRIORITY          3
+#define TASK2_STACK_DEPTH       128
+TaskHandle_t task2_handler;
+void Task2(void *pvParameters);
+
+EventGroupHandle_t  eventgroup_handle;      /* 事件标志组句柄 */
+
+#define EVENTBIT_0  (1 << 0)                /* 定义事件位0 */
+#define EVENTBIT_1  (1 << 1)                /* 定义事件位1 */
+
+void FreeRTOS_Start(void)
+{
+    xTaskCreate((TaskFunction_t)Start_Task,
+                (char *)"Start_Task",
+                (configSTACK_DEPTH_TYPE)START_TASK_STACK_DEPTH,
+                (void *)NULL,
+                (UBaseType_t)START_TASK_PRIORITY,
+                (TaskHandle_t *)&start_task_handler);
+    vTaskStartScheduler();
+}
+
+/* 启动任务:创建事件标志组和工作任务 */
+void Start_Task(void *pvParameters)
+{
+    taskENTER_CRITICAL();
+
+    /* 创建事件标志组 */
+    eventgroup_handle = xEventGroupCreate();
+    if (eventgroup_handle != NULL) {
+        printf("事件标志组创建成功\r\n");
+    }
+
+    xTaskCreate((TaskFunction_t)Task1,
+                (char *)"Task1",
+                (configSTACK_DEPTH_TYPE)TASK1_STACK_DEPTH,
+                (void *)NULL,
+                (UBaseType_t)TASK1_PRIORITY,
+                (TaskHandle_t *)&task1_handler);
+
+    xTaskCreate((TaskFunction_t)Task2,
+                (char *)"Task2",
+                (configSTACK_DEPTH_TYPE)TASK2_STACK_DEPTH,
+                (void *)NULL,
+                (UBaseType_t)TASK2_PRIORITY,
+                (TaskHandle_t *)&task2_handler);
+
+    vTaskDelete(NULL);
+    taskEXIT_CRITICAL();
+}
+
+/* task1:KEY1 -> EVENTBIT_0,KEY2 -> EVENTBIT_1 */
+void Task1(void *pvParameters)
+{
+    uint8_t key = 0;
+
+    while (1) {
+        key = Key_Detect();
+        if (key == KEY1_PRESS) {
+            /* 将事件标志组的bit0位置1 */
+            xEventGroupSetBits(eventgroup_handle, EVENTBIT_0);
+        } else if (key == KEY2_PRESS) {
+            /* 将事件标志组的bit1位置1 */
+            xEventGroupSetBits(eventgroup_handle, EVENTBIT_1);
+        }
+        vTaskDelay(10);
+    }
+}
+
+/* task2:等待bit0和bit1都置位 */
+void Task2(void *pvParameters)
+{
+    EventBits_t event_bit = 0;
+
+    while (1) {
+        /* 等待事件标志组的bit0和bit1位都置1,成功后自动清除,永久等待 */
+        event_bit = xEventGroupWaitBits(
+                        eventgroup_handle,          /* 事件标志组句柄 */
+                        EVENTBIT_0 | EVENTBIT_1,    /* 等待bit0和bit1 */
+                        pdTRUE,                     /* 成功等待后自动清除标志位 */
+                        pdTRUE,                     /* 等待所有位都置1(AND) */
+                        portMAX_DELAY);             /* 永久等待 */
+
+        printf("等待到的事件标志位值=%#x\r\n", event_bit);
+    }
+}
+```
+
+### Key Config
+
+```c
+/* FreeRTOSConfig.h — 无额外配置,事件标志组默认使能 */
+```
+
+## Core API Reference Table
+
+| Function | Parameters | Return | Description |
+|----------|-----------|--------|-------------|
+| `EventGroupHandle_t xEventGroupCreate()` | 无 | NULL=失败 | 动态创建事件标志组 |
+| `EventBits_t xEventGroupSetBits(xEventGroup, uxBitsToSet)` | 句柄, 待置位掩码 | 设置后的值 | 设置事件位 |
+| `EventBits_t xEventGroupClearBits(xEventGroup, uxBitsToClear)` | 句柄, 待清除掩码 | 清除后的值 | 清除事件位 |
+| `EventBits_t xEventGroupWaitBits(xEventGroup, uxBitsToWaitFor, xClearOnExit, xWaitForAllBits, xTicksToWait)` | 句柄, 等待掩码, 退出时清除?, 等待所有?, 超时 | 等待结果 | 阻塞等待事件位 |
+| `EventBits_t xEventGroupSync(xEventGroup, uxBitsToSet, uxBitsToWaitFor, xTicksToWait)` | 句柄, 设置位, 等待位, 超时 | 同步结果 | 设置并等待(原子) |
+| `EventBits_t xEventGroupGetBits(xEventGroup)` | 句柄 | 当前值 | 获取当前所有位值 |
+| `BaseType_t xEventGroupSetBitsFromISR(xEventGroup, uxBitsToSet, pxHigherPriorityTaskWoken)` | 句柄, 置位掩码, 唤醒标记 | pdPASS/pdFAIL | ISR中设置位 |
+
+## Common Issues & Pitfalls
+
+1. **只有 24 个可用位**:32 位系统中高 8 位被内核占用。`EVENTBIT_23` 是最大可用位。
+2. **AND 与 OR 的区别**:`xWaitForAllBits = pdTRUE` 是 AND(等待所有),`pdFALSE` 是 OR(等待任一)。初学时容易混淆。
+3. **自动清除 vs 手动清除**:`xClearOnExit = pdTRUE` 表示等待成功后自动清除已匹配的位。如果多个任务等待同一位,建议手动清除以免误清。
+4. **ISR 中设置位**:使用 `xEventGroupSetBitsFromISR()` → 不要直接在 ISR 中调用 `xEventGroupSetBits()`。
+5. **事件标志组创建后初始值为 0**:所有位默认清零,首次 WaitBits 会立即返回或阻塞取决于位是否已置位。

+ 224 - 0
X-Knowledge-Base/raw/Joplin/嵌入式+Linux/FreeRTOS学习笔记/14-任务通知.md

@@ -0,0 +1,224 @@
+---
+tags: [source-summary]
+type: source
+source: "尚硅谷嵌入式技术之FreeRTOS实时操作系统 V1.0.3 — 第14章 任务通知"
+author: "尚硅谷研究院"
+date: 2026-07-17
+created: 2026-07-17
+---
+
+# 任务通知
+
+> **用生活理解**:任务通知就像同事直接走到你工位前告诉你一件事,不需要通过公司广播(队列)、公告栏(信号量)或旗语系统(事件标志组)。直接、高效,但因为是一对一,没法同时通知所有人。
+
+## Concept
+
+### Basic concept
+
+任务通知(Task Notification)是 FreeRTOS V8.2.0 引入的轻量级任务间通信机制。每个 TCB 中内置了一个 **32 位通知值**和 **8 位通知状态**,无需创建额外的内核对象。
+
+- **无需中间对象**:发送任务直接向目标任务发通知
+- **约 45% 更快**:比队列/信号量省去了创建和操作中间对象的时间
+- **一对一**:每个任务只有一个通知值,不能一对多广播
+
+### Working principle
+
+任务通知支持 3 种使用模式:
+
+| 模式 | 发送函数 | 接收函数 | 行为 |
+|------|---------|---------|------|
+| **信号量模式** | `xTaskNotifyGive()` | `ulTaskNotifyTake()` | 通知值递增/递减,模拟二值/计数信号量 |
+| **消息邮箱模式** | `xTaskNotify(handle, value, eSetValueWithOverwrite)` | `xTaskNotifyWait()` | 直接覆盖通知值,模拟消息邮箱 |
+| **事件标志组模式** | `xTaskNotify(handle, bits, eSetBits)` | `xTaskNotifyWait()` | 按位 OR,模拟事件标志组 |
+
+### Core API
+
+| Function | Params | Description |
+|----------|--------|-------------|
+| `xTaskNotifyGive()` | `xTaskToNotify` | 发送通知(信号量模式),通知值 +1 |
+| `ulTaskNotifyTake()` | `xClearCountOnExit`, `xTicksToWait` | 接收通知(信号量模式),返回通知值 |
+| `xTaskNotify()` | `xTaskToNotify`, `ulValue`, `eAction` | 发送通知(带值),eAction 控制写/OR/覆写行为 |
+| `xTaskNotifyWait()` | `ulBitsToClearOnEntry`, `ulBitsToClearOnExit`, `pulNotificationValue`, `xTicksToWait` | 接收通知(带值),可获取通知值和清除指定位 |
+| `xTaskNotifyFromISR()` | 同上+ISR 版本 | 中断中发送通知 |
+| `vTaskNotifyGiveFromISR()` | `xTaskToNotify`, `pxHigherPriorityTaskWoken` | 中断中发送(信号量模式) |
+
+## Experiment
+
+### Experiment 1: 任务通知模拟信号量 (P16)
+
+**Requirements**: KEY1 → `xTaskNotifyGive(task2)`,task2 通过 `ulTaskNotifyTake()` 接收。
+
+**File: P16/16_任务通知模拟信号量/Core/Src/freertos_demo.c**
+
+```c
+/* task1:KEY1按下,发送任务通知(信号量模式) */
+void Task1(void *pvParameters)
+{
+    uint8_t key = 0;
+
+    while (1) {
+        key = Key_Detect();
+        if (key == KEY1_PRESS) {
+            printf("任务通知模拟二值信号量释放\r\n");
+            /* 向task2发送任务通知,通知值+1 */
+            xTaskNotifyGive(task2_handler);
+        }
+        vTaskDelay(10);
+    }
+}
+
+/* task2:接收任务通知(信号量模式) */
+void Task2(void *pvParameters)
+{
+    uint32_t rev = 0;
+
+    while (1) {
+        /* 等待任务通知,pdTRUE表示退出时清零通知值 */
+        rev = ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
+        if (rev != 0) {
+            printf("接收任务通知成功,模拟获取二值信号量\r\n");
+        }
+    }
+}
+```
+
+### Experiment 2: 任务通知模拟消息邮箱 (P17)
+
+**Requirements**: KEY1/KEY2 → `xTaskNotify(task2, key, eSetValueWithOverwrite)`,task2 通过 `xTaskNotifyWait()` 接收并处理。
+
+**File: P17/17_任务通知模拟消息邮箱/Core/Src/freertos_demo.c**
+
+```c
+/* task1:扫描按键,通过任务通知发送键值 */
+void Task1(void *pvParameters)
+{
+    uint8_t key = 0;
+
+    while (1) {
+        key = Key_Detect();
+        if ((key != 0) && (task2_handler != NULL)) {
+            printf("任务通知模拟消息邮箱发送,发送的键值为:%d\r\n", key);
+            /* 直接覆写task2的通知值(消息邮箱模式) */
+            xTaskNotify(task2_handler, key, eSetValueWithOverwrite);
+        }
+        vTaskDelay(10);
+    }
+}
+
+/* task2:接收通知值,根据键值做不同动作 */
+void Task2(void *pvParameters)
+{
+    uint32_t notify_val = 0;
+
+    while (1) {
+        /* 等待任务通知,参数:入口不清零,出口全清零 */
+        xTaskNotifyWait(0, 0xFFFFFFFF, &notify_val, portMAX_DELAY);
+
+        switch (notify_val) {
+            case KEY1_PRESS: {
+                printf("接收到的通知值为:%d\r\n", notify_val);
+                LED_Toggle(LED1_Pin);
+                break;
+            }
+            case KEY2_PRESS: {
+                printf("接收到的通知值为:%d\r\n", notify_val);
+                LED_Toggle(LED2_Pin);
+                break;
+            }
+            default:
+                break;
+        }
+    }
+}
+```
+
+### Experiment 3: 任务通知模拟事件标志组 (P18)
+
+**Requirements**: KEY1 → 置 EVENTBIT_0,KEY2 → 置 EVENTBIT_1,task2 等待两个位都置位。
+
+**File: P18/18_任务通知模拟事件标志组/Core/Src/freertos_demo.c**
+
+```c
+#define EVENTBIT_0  (1 << 0)
+#define EVENTBIT_1  (1 << 1)
+
+/* task1:KEY1 -> 设置EVENTBIT_0,KEY2 -> 设置EVENTBIT_1 */
+void Task1(void *pvParameters)
+{
+    uint8_t key = 0;
+
+    while (1) {
+        key = Key_Detect();
+        if (key == KEY1_PRESS) {
+            printf("将bit0位置1\r\n");
+            /* 使用eSetBits模式,按位OR设置通知值 */
+            xTaskNotify(task2_handler, EVENTBIT_0, eSetBits);
+        } else if (key == KEY2_PRESS) {
+            printf("将bit1位置1\r\n");
+            xTaskNotify(task2_handler, EVENTBIT_1, eSetBits);
+        }
+        vTaskDelay(10);
+    }
+}
+
+/* task2:等待bit0和bit1都置位 */
+void Task2(void *pvParameters)
+{
+    uint32_t notify_val = 0, event_bit = 0;
+
+    while (1) {
+        /* 等待任务通知 */
+        xTaskNotifyWait(0, 0xFFFFFFFF, &notify_val, portMAX_DELAY);
+
+        if (notify_val & EVENTBIT_0) {
+            event_bit |= EVENTBIT_0;
+        }
+        if (notify_val & EVENTBIT_1) {
+            event_bit |= EVENTBIT_1;
+        }
+
+        if (event_bit == (EVENTBIT_0 | EVENTBIT_1)) {
+            printf("任务通知模拟事件标志组接收成功\r\n");
+            event_bit = 0;                      /* 手动清除 */
+        }
+    }
+}
+```
+
+### Key Config
+
+```c
+/* FreeRTOSConfig.h */
+#define configUSE_TASK_NOTIFICATIONS        1   /* 使能任务通知(默认已使能) */
+#define configTASK_NOTIFICATION_ARRAY_ENTRIES 1 /* 通知数组大小,默认1 */
+```
+
+## Core API Reference Table
+
+| Function | Parameters | Return | Description |
+|----------|-----------|--------|-------------|
+| `BaseType_t xTaskNotifyGive(xTaskToNotify)` | 目标任务句柄 | pdPASS/pdFAIL | 发送通知(信号量模式,通知值+1) |
+| `uint32_t ulTaskNotifyTake(xClearCountOnExit, xTicksToWait)` | 退出时清零?, 超时 | 通知值 | 接收通知(信号量模式,阻塞) |
+| `BaseType_t xTaskNotify(xTaskToNotify, ulValue, eAction)` | 目标任务, 值, 动作(覆写/OR/增量等) | pdPASS/pdFAIL | 发送通知(带值) |
+| `BaseType_t xTaskNotifyWait(ulBitsToClearOnEntry, ulBitsToClearOnExit, pulNotificationValue, xTicksToWait)` | 入口清除, 出口清除, 值指针, 超时 | pdPASS/pdFAIL | 接收通知(阻塞等待) |
+| `BaseType_t xTaskNotifyFromISR(xTaskToNotify, ulValue, eAction, pxHigherPriorityTaskWoken)` | 同 xTaskNotify+唤醒标记 | pdPASS/pdFAIL | ISR中发送通知 |
+| `void vTaskNotifyGiveFromISR(xTaskToNotify, pxHigherPriorityTaskWoken)` | 目标任务, 唤醒标记 | 无 | ISR中信号量模式发送 |
+| `BaseType_t xTaskNotifyAndQuery(xTaskToNotify, ulValue, eAction, pulPreviousValue)` | 同 xTaskNotify+原值 | pdPASS/pdFAIL | 发送并获取原通知值 |
+
+**eAction 枚举值**:
+- `eNoAction`:仅发送通知(不修改值)
+- `eSetBits`:与现有值按位 OR
+- `eIncrement`:通知值 +1
+- `eSetValueWithOverwrite`:无条件覆写
+- `eSetValueWithoutOverwrite`:仅当接收方未读取时覆写(否则返回 pdFAIL)
+
+## Common Issues & Pitfalls
+
+1. **任务通知只能一对一**:一个任务只有一个通知值。不能像事件标志组那样多个任务等待同一个事件。
+2. **ISR 不可接收通知**:没有 `xTaskNotifyWaitFromISR()` 函数。接收任务通知只能在任务上下文中进行。
+3. **通知值溢出不提示**:`xTaskNotifyGive()` 使 32 位通知值递增,溢出后归零且无错误提示。
+4. **信号量模式的 xClearCountOnExit**:
+   - `pdTRUE`:读取后清零(适合二值信号量)
+   - `pdFALSE`:读取后减 1(适合计数信号量),`ulTaskNotifyTake()` 返回减 1 前的值
+5. **消息邮箱模式的数据丢失**:使用 `eSetValueWithOverwrite` 时,如果发送者在接收者读取前多次发送,只有最后一次的值会被保留。
+6. **性能优势的来源**:任务通知不经过队列内核对象,直接在 TCB 中操作,省去了队列的入队/出队开销和等待列表管理。

+ 359 - 0
X-Knowledge-Base/raw/Joplin/嵌入式+Linux/FreeRTOS学习笔记/15-软件定时器与Tickless低功耗.md

@@ -0,0 +1,359 @@
+---
+tags: [source-summary]
+type: source
+source: "尚硅谷嵌入式技术之FreeRTOS实时操作系统 V1.0.3 — 软件定时器 + Tickless低功耗模式"
+author: "尚硅谷研究院"
+date: 2026-07-17
+created: 2026-07-17
+---
+
+# 软件定时器与 Tickless 低功耗
+
+> **用生活理解**:软件定时器像手机闹钟——设定时间到了就响铃(回调函数),可以是一次性的(单次提醒)或每天重复(周期提醒)。Tickless 低功耗像你晚上睡觉时关掉所有闹钟,第二天早上再一次性补上所有错过的报时——省电又不误事。
+
+## 软件定时器
+
+### Basic concept
+
+FreeRTOS 软件定时器是一种基于 Tick 计数的轻量级时间管理工具,完全由软件实现,不依赖硬件定时器资源。
+
+软件定时器与硬件定时器的主要区别:
+
+| 软件定时器 | 硬件定时器 |
+|-----------|-----------|
+| 依赖 RTOS 调度器进行计时 | 由芯片硬件提供,独立于 CPU |
+| 精度受任务调度影响 | 精度高、分辨率高 |
+| 不占用额外硬件资源 | 占用硬件定时器外设 |
+
+定时器回调函数在**定时器服务任务**(Timer Service Task)的上下文中执行,因此回调函数中**不能调用会导致阻塞的 API 函数**(如 vTaskDelay、xQueueReceive 等)。
+
+### Working principle
+
+软件定时器完全基于系统 Tick 中断计数实现。所有对定时器的操作(启动、停止、复位等)都通过**定时器命令队列**发送消息给定时器服务任务,由该任务统一处理。
+
+**定时器服务任务**:当 `configUSE_TIMERS` 设置为 1 时,启动调度器会自动创建此任务。它负责:
+- 判断定时器是否超时
+- 调用超时定时器的回调函数
+- 处理定时器命令队列中的消息
+
+**定时器状态机**:
+
+```
+未创建 (Uncreated) → 已创建 (Created) → 运行中 (Running)
+                          ↑                    │
+                          └── 停止 ────────────┘
+```
+
+- **Dormant(休眠)**:定时器已创建但未启动
+- **Running(运行)**:定时器已启动,正在倒计时
+- 单次定时器到期后自动回到 Dormant;周期定时器到期后自动重启
+
+### 单次定时器 vs 周期定时器
+
+| 类型 | AutoReload | 行为 |
+|------|-----------|------|
+| 单次 (One-shot) | pdFALSE | 到期执行一次回调后停止 |
+| 周期 (Periodic) | pdTRUE | 每到期一次执行一次回调,循环往复 |
+
+### Core API
+
+| 函数 | 描述 |
+|------|------|
+| `xTimerCreate()` | 动态创建软件定时器 |
+| `xTimerStart()` | 启动定时器(发命令到命令队列) |
+| `xTimerStop()` | 停止定时器 |
+| `xTimerReset()` | 复位定时器(重新开始计时) |
+| `xTimerChangePeriod()` | 更改定时周期 |
+| `xTimerStartFromISR()` | 在中断中启动定时器 |
+| `xTimerStopFromISR()` | 在中断中停止定时器 |
+| `xTimerResetFromISR()` | 在中断中复位定时器 |
+
+所有 API 均通过命令队列发送消息给定时器服务任务,因此有**阻塞时间**参数(如 `portMAX_DELAY`)。
+
+---
+
+## Tickless 低功耗模式
+
+### Basic concept
+
+Tickless 模式是 FreeRTOS 的一种特殊运行模式,用于最小化系统时钟中断频率以降低功耗。在 Tickless 模式下,当所有任务都处于阻塞或挂起状态时,空闲任务会让 MCU 进入低功耗模式(通常是睡眠模式),并暂停 SysTick 中断,直到有事件需要处理才唤醒。
+
+### Working principle
+
+```
+所有任务阻塞 → 空闲任务运行
+                   ↓
+         configPRE_SLEEP_PROCESSING(x)   ← 关闭外设时钟等
+                   ↓
+          MCU 进入睡眠模式 (WFI)
+          SysTick 暂停
+                   ↓
+         中断/事件唤醒
+                   ↓
+         configPOST_SLEEP_PROCESSING(x)  ← 恢复外设时钟
+                   ↓
+         补上错过的 Tick 计数
+```
+
+Tickless 的关键机制:退出低功耗后,FreeRTOS 会自动修正 Tick 计数值,确保系统时间不因休眠而丢失。
+
+### STM32 低功耗模式
+
+| 模式 | 说明 | 唤醒方式 |
+|------|------|---------|
+| 睡眠 (Sleep) | 仅 CPU 停止,外设全工作 | 任何中断/事件 |
+| 停机 (Stop) | SRAM/寄存器保持,1.8V 部分断电 | EXTI、RTC、USB 唤醒 |
+| 待机 (Standby) | 最低功耗,SRAM 内容丢失 | NRST、IWDG、WKUP、RTC |
+
+Tickless 默认使用**睡眠模式**(通过 `__WFI` 指令),任何中断均可唤醒。
+
+### 配置项
+
+| 宏 | 说明 |
+|----|------|
+| `configUSE_TICKLESS_IDLE` | 使能 Tickless 模式(默认 0) |
+| `configEXPECTED_IDLE_TIME_BEFORE_SLEEP` | 进入低功耗的最短空闲时间(默认 2 ticks) |
+| `configPRE_SLEEP_PROCESSING(x)` | 进入低功耗前执行的操作(关闭外设时钟) |
+| `configPOST_SLEEP_PROCESSING(x)` | 退出低功耗后执行的操作(恢复外设时钟) |
+
+## Experiment
+
+### Experiment 1: 软件定时器
+
+#### Requirements
+
+- 创建单次定时器 timer1(500 ticks)和周期定时器 timer2(1000 ticks)
+- KEY1 → 启动两个定时器,KEY2 → 停止两个定时器
+- 观察回调函数的执行次数
+
+#### Code Implementation
+
+**File: 19_软件定时器/Core/Src/freertos_demo.c**
+
+```c
+#include "freertos_demo.h"
+#include "FreeRTOS.h"
+#include "task.h"
+#include "timers.h"          // 软件定时器头文件
+#include "LED.h"
+#include "Key.h"
+
+#define START_TASK_STACK 128
+#define START_TASK_PRIORITY 1
+TaskHandle_t start_task_handle;
+void start_task(void *pvParameters);
+
+#define TASK1_STACK 128
+#define TASK1_PRIORITY 2
+TaskHandle_t task1_handle;
+void task1(void *pvParameters);
+
+// 定时器回调函数声明
+void timer1_callback(TimerHandle_t xTimer);    // 单次
+void timer2_callback(TimerHandle_t xTimer);    // 周期
+
+TimerHandle_t timer1_handle;
+TimerHandle_t timer2_handle;
+
+void freertos_start(void)
+{
+    // 创建单次定时器:500 ticks,不会自动重载
+    timer1_handle = xTimerCreate(
+        "timer1",            // 定时器名称
+        (TickType_t)500,     // 定时周期(Tick 数)
+        pdFALSE,             // 单次模式(不自动重载)
+        (void *)1,           // 定时器 ID
+        timer1_callback);    // 回调函数
+
+    // 创建周期定时器:1000 ticks,自动重载
+    timer2_handle = xTimerCreate(
+        "timer2",            // 定时器名称
+        (TickType_t)1000,    // 定时周期(Tick 数)
+        pdTRUE,              // 周期模式(自动重载)
+        (void *)2,           // 定时器 ID
+        timer2_callback);    // 回调函数
+
+    xTaskCreate((TaskFunction_t)start_task,
+                (char *)"start_task",
+                (configSTACK_DEPTH_TYPE)START_TASK_STACK,
+                (void *)NULL,
+                (UBaseType_t)START_TASK_PRIORITY,
+                (TaskHandle_t *)&start_task_handle);
+
+    vTaskStartScheduler();
+}
+
+void start_task(void *pvParameters)
+{
+    taskENTER_CRITICAL();
+
+    xTaskCreate((TaskFunction_t)task1,
+                (char *)"task1",
+                (configSTACK_DEPTH_TYPE)TASK1_STACK,
+                (void *)NULL,
+                (UBaseType_t)TASK1_PRIORITY,
+                (TaskHandle_t *)&task1_handle);
+
+    vTaskDelete(NULL);       // 启动任务自我删除
+    taskEXIT_CRITICAL();
+}
+
+void task1(void *pvParameters)
+{
+    uint8_t key = 0;
+    BaseType_t res = 0;
+    while (1)
+    {
+        key = Key_Detect();
+        if (key == KEY1_PRESS)
+        {
+            // KEY1 按下 → 启动两个定时器
+            res = xTimerStart(timer1_handle, portMAX_DELAY);
+            if (res != pdFAIL)
+                printf("timer1 单次定时器启动成功\r\n");
+
+            res = xTimerStart(timer2_handle, portMAX_DELAY);
+            if (res != pdFAIL)
+                printf("timer2 周期定时器启动成功\r\n");
+        }
+        else if (key == KEY2_PRESS)
+        {
+            // KEY2 按下 → 停止两个定时器
+            res = xTimerStop(timer1_handle, portMAX_DELAY);
+            if (res != pdFAIL)
+                printf("timer1 单次定时器停止成功\r\n");
+
+            res = xTimerStop(timer2_handle, portMAX_DELAY);
+            if (res != pdFAIL)
+                printf("timer2 周期定时器停止成功\r\n");
+        }
+        vTaskDelay(500);
+    }
+}
+
+void timer1_callback(TimerHandle_t xTimer)
+{
+    static uint16_t timer1_count = 0;
+    printf("timer1 定时回调=%d 次..\r\n", ++timer1_count);
+}
+
+void timer2_callback(TimerHandle_t xTimer)
+{
+    static uint16_t timer2_count = 0;
+    printf("timer2 定时回调=%d 次..\r\n", ++timer2_count);
+}
+```
+
+#### Key Config
+
+```c
+// FreeRTOSConfig.h 软件定时器相关配置
+#define configUSE_TIMERS              1           // 使能软件定时器
+#define configTIMER_TASK_PRIORITY     4           // 定时器服务任务优先级(configMAX_PRIORITIES - 1)
+#define configTIMER_QUEUE_LENGTH      5           // 定时器命令队列长度
+#define configTIMER_TASK_STACK_DEPTH  256         // 定时器服务任务栈大小(128 * 2)
+```
+
+### Experiment 2: Tickless 低功耗
+
+#### Requirements
+
+- 基于二值信号量实验(task1 按键释放信号量 → task2 获取信号量)
+- 使能 Tickless 模式,编写睡眠前/后处理函数
+- 对比功耗变化
+
+#### Code Implementation
+
+**File: 20_Tickless低功耗/Core/Src/freertos_demo.c**
+
+```c
+#include "freertos_demo.h"
+#include "FreeRTOS.h"
+#include "task.h"
+#include "semphr.h"          // 信号量头文件
+#include "LED.h"
+#include "Key.h"
+
+QueueHandle_t sem_handle;    // 二值信号量句柄
+
+// 进入低功耗前关闭所有 GPIO 时钟
+void PRE_SLEEP_PROCESSING()
+{
+    __HAL_RCC_GPIOA_CLK_DISABLE();
+    __HAL_RCC_GPIOB_CLK_DISABLE();
+    __HAL_RCC_GPIOC_CLK_DISABLE();
+    __HAL_RCC_GPIOD_CLK_DISABLE();
+    __HAL_RCC_GPIOE_CLK_DISABLE();
+    __HAL_RCC_GPIOF_CLK_DISABLE();
+    __HAL_RCC_GPIOG_CLK_DISABLE();
+}
+
+// 退出低功耗后恢复所有 GPIO 时钟
+void POST_SLEEP_PROCESSING()
+{
+    __HAL_RCC_GPIOA_CLK_ENABLE();
+    __HAL_RCC_GPIOB_CLK_ENABLE();
+    __HAL_RCC_GPIOC_CLK_ENABLE();
+    __HAL_RCC_GPIOD_CLK_ENABLE();
+    __HAL_RCC_GPIOE_CLK_ENABLE();
+    __HAL_RCC_GPIOF_CLK_ENABLE();
+    __HAL_RCC_GPIOG_CLK_ENABLE();
+}
+
+/* start_task / task1 / task2 创建与信号量实验代码... */
+// (核心逻辑:task1 KEY1 → xSemaphoreGive,task2 xSemaphoreTake)
+```
+
+完整任务代码与前文信号量实验相同,增加了 `configUSE_TICKLESS_IDLE` 使能和睡眠前/后处理函数。
+
+#### Key Config
+
+```c
+// FreeRTOSConfig.h Tickless 相关配置
+#define configUSE_TICKLESS_IDLE                         1   // 使能 Tickless 低功耗模式
+#include "freertos_demo.h"
+#define configPRE_SLEEP_PROCESSING(x)         PRE_SLEEP_PROCESSING()
+#define configPOST_SLEEP_PROCESSING(x)        POST_SLEEP_PROCESSING()
+```
+
+`configEXPECTED_IDLE_TIME_BEFORE_SLEEP` 默认值 2,表示空闲时间超过 2 个 Tick 才进入低功耗。
+
+## Core API Reference Table
+
+### 软件定时器 API
+
+| API | 参数 | 说明 |
+|-----|------|------|
+| `xTimerCreate(name, period, autoReload, id, callback)` | name=定时器名, period=Tick数, autoReload=pdTRUE/pdFALSE, id=用户ID, callback=回调函数 | 创建软件定时器,返回句柄 |
+| `xTimerStart(xTimer, blockTime)` | xTimer=句柄, blockTime=等待命令队列的阻塞时间 | 启动定时器 |
+| `xTimerStop(xTimer, blockTime)` | 同上 | 停止定时器 |
+| `xTimerReset(xTimer, blockTime)` | 同上 | 复位定时器(从头计时) |
+| `xTimerChangePeriod(xTimer, newPeriod, blockTime)` | newPeriod=新周期 | 更改定时周期 |
+
+### Tickless 配置 API
+
+| 配置宏 | 说明 |
+|--------|------|
+| `configUSE_TICKLESS_IDLE` | 使能 Tickless(0=关, 1=开) |
+| `configEXPECTED_IDLE_TIME_BEFORE_SLEEP` | 进入休眠前最小空闲 Tick 数 |
+| `configPRE_SLEEP_PROCESSING(x)` | 休眠前调用的用户函数 |
+| `configPOST_SLEEP_PROCESSING(x)` | 唤醒后调用的用户函数 |
+
+## 常见问题与避坑
+
+1. **定时器回调中不要调用阻塞 API**:回调函数在定时器服务任务上下文中运行,调用 `vTaskDelay()`、`xQueueReceive()` 等阻塞函数会导致服务任务卡死,影响所有定时器。
+
+2. **`xTimerStart` 不是立即生效**:所有定时器 API 都通过命令队列异步执行。返回值只表示命令是否成功放入队列,定时器实际状态变化有延迟。
+
+3. **`configTIMER_TASK_PRIORITY` 要合理设置**:如果此优先级过低,定时器回调可能被其他任务长时间抢占,导致定时精度下降。
+
+4. **Tickless 下调试困难**:进入低功耗后调试器可能断开连接。建议先关闭 Tickless 完成功能调试,最后再使能低功耗。
+
+5. **`configPRE_SLEEP_PROCESSING` 不能阻塞**:此函数在空闲任务上下文中被调用,阻塞会影响系统进入低功耗。
+
+6. **`configTOTAL_HEAP_SIZE` 要足够**:Tickless 模式会增加少量内存开销(用于记录休眠时间),堆大小不足可能导致创建任务失败。
+
+7. **Tickless 下中断频率过低可能有副作用**:长时间休眠后,多个中断可能同时触发,需确保中断处理函数能处理累积事件。
+
+![STM32 低功耗模式](./assets/stm32_low_power_modes.png)
+![定时器状态图](./assets/timer_state_diagram.png)

+ 175 - 0
X-Knowledge-Base/raw/Joplin/嵌入式+Linux/FreeRTOS学习笔记/16-内存管理.md

@@ -0,0 +1,175 @@
+---
+tags: [source-summary]
+type: source
+source: "尚硅谷嵌入式技术之FreeRTOS实时操作系统 V1.0.3 — 内存管理"
+author: "尚硅谷研究院"
+date: 2026-07-17
+created: 2026-07-17
+---
+
+# FreeRTOS 内存管理
+
+> **用生活理解**:动态内存像租房子——heap_1 是只租不退的宿舍(简单粗暴),heap_2 是随意退租但不打扫的房间(会碎片),heap_4 是退租时帮你合并相邻空房的管家(推荐),heap_5 是能跨多个不同地段管理房源的大中介。
+
+## Concept
+
+### Basic concept
+
+FreeRTOS 在创建任务、队列、信号量等内核对象时,支持**动态创建**和**静态创建**两种方式。动态方式更灵活(调用 `pvPortMalloc` 自动从堆中分配),静态方式则需要用户预先分配内存。
+
+标准 C 库的 `malloc()` / `free()` 在嵌入式系统中存在以下问题:
+- 并非所有嵌入式平台都可用
+- 占用宝贵的代码空间
+- 线程不安全
+- 非确定性(执行时间不确定)
+
+为此 FreeRTOS 将内存分配 API 放在可移植层(`MemMang/` 目录),提供了 5 种 heap 算法。
+
+### 5 种 Heap 算法对比
+
+| 算法 | 策略 | 释放 | 碎片 | 适用场景 |
+|------|------|------|------|---------|
+| **heap_1** | 简单数组分配 | ❌ 不支持 | 无碎片 | 从不删除任务/队列的简单应用 |
+| **heap_2** | 最佳适应(Best-fit)链表 | ✅ | ❌ 会碎片 | 动态创建/删除但大小固定的场景 |
+| **heap_3** | 包装标准 malloc/free | ✅ | 取决于 C 库 | 需要线程安全的 malloc |
+| **heap_4** | 首次适应(First-fit)+合并相邻块 | ✅ | 碎片少 | **默认推荐** |
+| **heap_5** | 同 heap_4 | ✅ | 碎片少 | 多个非连续 RAM 区域 |
+
+### Working principle
+
+**heap_1**:最简单的实现。一个大数组作为堆空间,每次分配从空闲位置递增指针。不允许释放,因此不会产生碎片。适用于系统启动时一次性创建所有内核对象、永不删除的场景。
+
+**heap_2**:使用**最佳适应**(Best-fit)算法——在所有空闲块中选择与请求大小最接近的块进行分配。支持释放,但**不会合并相邻空闲块**。当频繁分配和释放不同大小的内存时,会产生严重碎片。适用于创建/删除任务大小始终相同的场景。
+
+**heap_3**:简单包装 C 库的 `malloc()` / `free()`,通过挂起调度器实现线程安全。内存分配行为完全取决于 C 库实现。
+
+**heap_4**:使用**首次适应**(First-fit)算法——在空闲块链表中选择第一个足够大的块进行分配。与 heap_2 的关键区别:释放时会**合并相邻空闲块**,显著减少碎片。是 FreeRTOS 官方推荐的默认算法。
+
+**heap_5**:算法与 heap_4 完全相同,区别在于支持**跨多个非连续 RAM 区域**的堆。适用于内存地址不连续的复杂硬件场景(如外扩 SRAM + 内部 SRAM 并存)。
+
+### Core API
+
+| API | 描述 |
+|-----|------|
+| `void *pvPortMalloc(size_t xWantedSize)` | 申请内存,返回指针 |
+| `void vPortFree(void *pv)` | 释放内存 |
+| `size_t xPortGetFreeHeapSize(void)` | 获取当前空闲堆大小 |
+
+## Experiment
+
+### Requirements
+
+- task1 按键扫描:KEY1 → `pvPortMalloc(20)` 申请 20 字节,KEY2 → `vPortFree()` 释放
+- 每次操作后打印剩余堆大小 `xPortGetFreeHeapSize()`
+
+### Code Implementation
+
+**File: 21_内存管理/Core/Src/freertos_demo.c**
+
+```c
+#include "freertos_demo.h"
+#include "FreeRTOS.h"
+#include "task.h"
+#include "LED.h"
+#include "Key.h"
+
+#define START_TASK_STACK 128
+#define START_TASK_PRIORITY 1
+TaskHandle_t start_task_handle;
+void start_task(void *pvParameters);
+
+#define TASK1_STACK 128
+#define TASK1_PRIORITY 2
+TaskHandle_t task1_handle;
+void task1(void *pvParameters);
+
+void freertos_start(void)
+{
+    xTaskCreate((TaskFunction_t)start_task,
+                (char *)"start_task",
+                (configSTACK_DEPTH_TYPE)START_TASK_STACK,
+                (void *)NULL,
+                (UBaseType_t)START_TASK_PRIORITY,
+                (TaskHandle_t *)&start_task_handle);
+
+    vTaskStartScheduler();
+}
+
+void start_task(void *pvParameters)
+{
+    taskENTER_CRITICAL();
+
+    xTaskCreate((TaskFunction_t)task1,
+                (char *)"task1",
+                (configSTACK_DEPTH_TYPE)TASK1_STACK,
+                (void *)NULL,
+                (UBaseType_t)TASK1_PRIORITY,
+                (TaskHandle_t *)&task1_handle);
+
+    vTaskDelete(NULL);       // 启动任务自我删除
+    taskEXIT_CRITICAL();
+}
+
+void task1(void *pvParameters)
+{
+    uint8_t key = 0;
+    void *buff = 0;
+    while (1)
+    {
+        key = Key_Detect();
+        if (key == KEY1_PRESS)
+        {
+            // KEY1 按下 → 申请 20 字节内存
+            buff = pvPortMalloc(20);
+            printf("申请 20 字节内存,当前剩余堆大小=%d\r\n",
+                   xPortGetFreeHeapSize());
+        }
+        else if (key == KEY2_PRESS)
+        {
+            // KEY2 按下 → 释放内存
+            vPortFree(buff);
+            printf("释放内存,当前剩余堆大小=%d\r\n",
+                   xPortGetFreeHeapSize());
+        }
+        vTaskDelay(500);
+    }
+}
+```
+
+### Key Config
+
+```c
+// FreeRTOSConfig.h 内存相关配置
+#define configTOTAL_HEAP_SIZE    ((size_t)(10 * 1024))   // 总堆大小 10KB
+```
+
+注意:实际使用 heap_4 时 `configTOTAL_HEAP_SIZE` 决定了 `ucHeap[]` 数组大小,所有动态创建的任务、队列、信号量等对象都从这片空间分配。
+
+## Core API Reference Table
+
+| API | 参数 | 返回值 | 说明 |
+|-----|------|--------|------|
+| `pvPortMalloc(xWantedSize)` | xWantedSize=请求字节数 | 成功返回指针,失败返回 NULL | 从 FreeRTOS 堆中分配内存 |
+| `vPortFree(pv)` | pv=要释放的内存指针 | 无 | 释放内存回堆 |
+| `xPortGetFreeHeapSize()` | 无 | size_t 空闲字节数 | 获取当前堆空闲大小 |
+| `xPortGetMinimumEverFreeHeapSize()` | 无 | size_t 历史最小空闲 | 追踪最小剩余堆(需 `configUSE_TRACE_FACILITY=1`) |
+
+> **注意**:`xPortGetFreeHeapSize()` 只在 heap_1/2/4/5 中有效(heap_3 包装 C 库 malloc,无法统计 FreeRTOS 堆)。
+
+## 常见问题与避坑
+
+1. **默认使用 heap_4**:移植 FreeRTOS 时必须从 `MemMang/` 中选择一个 heap 文件加入工程(通常选 `heap_4.c`),否则链接报错。
+
+2. **`configTOTAL_HEAP_SIZE` 不够会导致创建任务失败**:任务栈、TCB 等全部从堆中分配。堆太小 → `pvPortMalloc` 返回 NULL → `xTaskCreate` 返回 `errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY`。
+
+3. **`pvPortMalloc` 返回 NULL 要处理**:申请失败不会崩溃,但后续使用 NULL 指针会导致 HardFault。务必检查返回值。
+
+4. **释放野指针会导致 HardFault**:`vPortFree` 必须传入 `pvPortMalloc` 返回的有效指针。释放栈上变量或已释放的指针会造成不可预知的错误。
+
+5. **不能在中断中调用 `pvPortMalloc` / `vPortFree`**:这些函数不是中断安全的。在中断中需要分配内存时,使用中断安全版的队列/信号量函数替代。
+
+6. **heap_4 的内存碎片虽然少但仍有**:长期频繁分配/释放不同大小的内存仍会导致外部碎片。定期用 `xPortGetMinimumEverFreeHeapSize()` 监控堆健康状态。
+
+7. **heap_5 需要额外初始化**:使用前必须调用 `vPortDefineHeapRegions()` 指定每个 RAM 区域的起始地址和大小。
+
+![Heap 算法对比](./assets/heap_comparison.png)

BIN
X-Knowledge-Base/raw/Joplin/嵌入式+Linux/FreeRTOS学习笔记/assets/basepri_interrupt_management.png


BIN
X-Knowledge-Base/raw/Joplin/嵌入式+Linux/FreeRTOS学习笔记/assets/delay_relative_vs_absolute.png


BIN
X-Knowledge-Base/raw/Joplin/嵌入式+Linux/FreeRTOS学习笔记/assets/event_group_bits.png


BIN
X-Knowledge-Base/raw/Joplin/嵌入式+Linux/FreeRTOS学习笔记/assets/heap2_best_fit.png


+ 0 - 0
X-Knowledge-Base/raw/Joplin/嵌入式+Linux/FreeRTOS学习笔记/assets/heap4_first_fit.png → X-Knowledge-Base/raw/Joplin/嵌入式+Linux/FreeRTOS学习笔记/assets/heap_comparison.png


+ 0 - 0
X-Knowledge-Base/raw/Joplin/嵌入式+Linux/FreeRTOS学习笔记/assets/multitasking_perceived_vs_actual.png → X-Knowledge-Base/raw/Joplin/嵌入式+Linux/FreeRTOS学习笔记/assets/multitasking_concurrent_vs_actual.png


BIN
X-Knowledge-Base/raw/Joplin/嵌入式+Linux/FreeRTOS学习笔记/assets/mutex_priority_inheritance.png


BIN
X-Knowledge-Base/raw/Joplin/嵌入式+Linux/FreeRTOS学习笔记/assets/priority_inversion.png


BIN
X-Knowledge-Base/raw/Joplin/嵌入式+Linux/FreeRTOS学习笔记/assets/queue_structure.png


BIN
X-Knowledge-Base/raw/Joplin/嵌入式+Linux/FreeRTOS学习笔记/assets/stm32_low_power_modes.png


+ 0 - 0
X-Knowledge-Base/raw/Joplin/嵌入式+Linux/FreeRTOS学习笔记/assets/heap1.png → X-Knowledge-Base/raw/Joplin/嵌入式+Linux/FreeRTOS学习笔记/assets/timer_state_diagram.png


+ 14 - 12
X-Knowledge-Base/raw/Joplin/嵌入式+Linux/STM32学习笔记/03-GPIO输出:LED控制.md

@@ -6,7 +6,6 @@ author: "尚硅谷研究院"
 date: 2026-07-15
 created: 2026-07-15
 ---
-
 # GPIO输出:LED控制
 
 > **用生活理解**:LED 控制就像你手里有 3 个开关(GPIO 引脚)分别控制 3 盏灯。你手动打开灯(IO=0),等一会儿,再关上(IO=1),再打开下一盏,就形成了"流水灯"效果。模块化封装就像给每个灯配了一个遥控器(LED_On/LED_Off 函数),不用再直接去掰开关了。
@@ -18,12 +17,14 @@ created: 2026-07-15
 ![LED电路原理图](assets/led_电路原理图.png)
 
 开发板上有 4 个 LED:
+
 - **LED1** → PA0(低电平点亮)
 - **LED2** → PA1(低电平点亮)
 - **LED3** → PA8(低电平点亮)
 - **LED4** → 电源指示灯(上电即亮,不可控)
 
 **电路原理**:
+
 ```
 VCC(3.3V) → 限流电阻(1KΩ) → LED 正极 → LED 负极 → GPIO引脚
 
@@ -32,6 +33,7 @@ VCC(3.3V) → 限流电阻(1KΩ) → LED 正极 → LED 负极 → GPIO引脚
 ```
 
 **限流电阻计算**:
+
 - LED 工作电流约 5~10mA
 - LED 正向压降约 1.8~2.0V
 - `R = (VCC - V_LED) / I_LED = (3.3 - 2.0) / 0.01 = 130Ω`(实际使用 1KΩ,延长 LED 寿命)
@@ -435,21 +437,21 @@ void LED_OffAll(LED leds[], uint8_t size)
 
 ### 寄存器版 (基于 stm32f10x.h 宏)
 
-| 操作 | 代码 | 说明 |
-|------|------|------|
-| LED初始化 | `LED_Init()` | 开启时钟 + 配置推挽输出 + 初始熄灭 |
-| 点亮LED | `LED_On(LED1)` | `GPIOA->ODR &= ~led_bit` |
-| 熄灭LED | `LED_Off(LED1)` | `GPIOA->ODR |= led_bit` |
-| 翻转LED | `LED_Toggle(LED1)` | 读取IDR判断当前状态,取反 |
-| 延时 | `Delay_ms(500)` | 基于SysTick查询方式 |
+| 操作      | 代码               | 说明                               |
+| --------- | ------------------ | ---------------------------------- |
+| LED初始化 | `LED_Init()`       | 开启时钟 + 配置推挽输出 + 初始熄灭 |
+| 点亮LED   | `LED_On(LED1)`     | `GPIOA->ODR &= ~led_bit`           |
+| 熄灭LED   | `LED_Off(LED1)`    | `GPIOA->ODR                        |
+| 翻转LED   | `LED_Toggle(LED1)` | 读取IDR判断当前状态,取反          |
+| 延时      | `Delay_ms(500)`    | 基于SysTick查询方式                |
 
 ### HAL库版
 
-| HAL函数 | 说明 |
-|---------|------|
+| HAL函数                                | 说明                          |
+| -------------------------------------- | ----------------------------- |
 | `HAL_GPIO_WritePin(GPIOA, pin, state)` | 写引脚电平 (RESET=低, SET=高) |
-| `HAL_GPIO_TogglePin(GPIOA, pin)` | 翻转引脚电平 |
-| `HAL_Delay(ms)` | 毫秒延时(基于SysTick中断) |
+| `HAL_GPIO_TogglePin(GPIOA, pin)`       | 翻转引脚电平                  |
+| `HAL_Delay(ms)`                        | 毫秒延时(基于SysTick中断)   |
 
 ---
 

+ 10 - 1
X-Knowledge-Base/wiki/index.md

@@ -10,11 +10,20 @@ created: 2026-07-12
 
 > 来源:[[source-freertos|尚硅谷 FreeRTOS 教程]] + B 站 FreeRTOS 视频转录
 
-原始笔记(含代码 + 通俗解释):
+16 篇原始笔记(含 22 个配套项目的核心代码 + 逐行注释):
 `raw/Joplin/嵌入式+Linux/FreeRTOS学习笔记/`
 
 已从 B 站视频教程的 55 个视频中提取字幕/语音讲解内容,丰富笔记中的类比和细节。
 
+笔记覆盖内容:
+| 层级 | 笔记编号 | 主题 |
+|------|---------|------|
+| **概念基础** | 01~02 | 实时操作系统基础 / 任务调度与状态管理 |
+| **移植配置** | 03~04 | 移植指南 / 数据类型命名与配置详解 |
+| **任务管理** | 05~09 | 任务创建删除 / 挂起恢复 / 中断管理 / 时间片与延时 / 状态查询统计 |
+| **IPC 机制** | 10~14 | 消息队列 / 信号量 / 队列集 / 事件标志组 / 任务通知 |
+| **高级特性** | 15~16 | 软件定时器与 Tickless / 内存管理 |
+
 Wiki 页面:
 - [[freertos-intro|FreeRTOS 概念]] — OS vs RTOS,数据类型,命名规范
 - [[freertos-task-scheduling|任务调度与状态]] — 调度策略,任务状态,上下文切换

+ 23 - 0
X-Knowledge-Base/wiki/log.md

@@ -138,3 +138,26 @@ type: log
     - WiFi 笔记中 TCP 服务器功能实际属于项目 11(非项目 10)
 
     更新 `wiki/index.md`、`wiki/log.md`
+
+## 2026-07-17
+
+- `lint`: 重构 FreeRTOS raw 笔记(11 篇旧 → 16 篇新)
+
+    原始资料重写:
+    - `raw/Joplin/嵌入式+Linux/FreeRTOS学习笔记/01~16` 共 16 篇笔记
+    - `raw/Joplin/嵌入式+Linux/FreeRTOS学习笔记/assets/`(48→13 张精选图片)
+    - 代码来源:22 个配套 Keil 项目(03_代码/),核心代码段逐行注释
+
+    笔记结构(按课程章节映射):
+    - 01~02:概念基础(OS vs RTOS / 调度与状态管理)
+    - 03~04:移植与配置(源码结构 / 数据类型与 FreeRTOSConfig.h)
+    - 05~09:任务管理(创建删除 / 挂起恢复 / 中断管理 / 时间片与延时 / 状态查询统计)
+    - 10~14:IPC 机制(消息队列 / 信号量 / 队列集 / 事件标志组 / 任务通知)
+    - 15~16:高级特性(软件定时器+Tickless / 内存管理)
+
+    每篇标准结构:frontmatter + 生活类比 + 概念介绍 + API 速查表 + 实验代码 + 常见问题与避坑
+
+    设计规范:`docs/superpowers/specs/2026-07-17-freertos-silicon-valley-ingest-design.md`
+    实现计划:`docs/superpowers/plans/2026-07-17-freertos-ingest.md`
+
+    更新 `wiki/source-freertos.md`、`wiki/log.md`

+ 15 - 12
X-Knowledge-Base/wiki/source-freertos.md

@@ -3,16 +3,14 @@ tags: [source-summary]
 type: source
 source: "尚硅谷嵌入式技术之FreeRTOS实时操作系统 V1.0.3"
 author: "尚硅谷研究院"
-date: 2026-07-12
+date: 2026-07-17
 url: ""
 created: 2026-07-12
 ---
 
 # 尚硅谷 FreeRTOS 教程 — 资料索引
 
-11 篇原始笔记位于 `raw/Joplin/嵌入式+Linux/FreeRTOS学习笔记/`,含完整代码和通俗解释。
-
-> 2026-07-13 补充:从 B 站 FreeRTOS 教程视频("freertos零基础教程,FreeRTOS项目实战")转录 55 个视频的字幕/语音内容,融入笔记作为补充讲解视角。
+16 篇原始笔记位于 `raw/Joplin/嵌入式+Linux/FreeRTOS学习笔记/`,含从 22 个配套 Keil 项目提取的核心代码 + 逐行注释。
 
 ## 笔记列表
 
@@ -21,14 +19,19 @@ created: 2026-07-12
 | 01 | [[raw/Joplin/嵌入式+Linux/FreeRTOS学习笔记/01-实时操作系统基础\|实时操作系统基础]] | — |
 | 02 | [[raw/Joplin/嵌入式+Linux/FreeRTOS学习笔记/02-任务调度与状态管理\|任务调度与状态管理]] | — |
 | 03 | [[raw/Joplin/嵌入式+Linux/FreeRTOS学习笔记/03-FreeRTOS移植指南\|FreeRTOS移植指南]] | 00_HAL_Key |
-| 04 | [[raw/Joplin/嵌入式+Linux/FreeRTOS学习笔记/04-数据类型与命名规范\|数据类型与命名规范]] | — |
-| 05 | [[raw/Joplin/嵌入式+Linux/FreeRTOS学习笔记/05-任务创建删除与挂起恢复\|任务创建删除与挂起恢复]] | 01~03, 06, 07 |
-| 06 | [[raw/Joplin/嵌入式+Linux/FreeRTOS学习笔记/06-中断管理与时间管理\|中断管理与时间管理]] | 04, 05, 08 |
-| 07 | [[raw/Joplin/嵌入式+Linux/FreeRTOS学习笔记/07-消息队列与队列集\|消息队列与队列集]] | 09, 14 |
-| 08 | [[raw/Joplin/嵌入式+Linux/FreeRTOS学习笔记/08-信号量与互斥信号量\|信号量与互斥信号量]] | 10~13 |
-| 09 | [[raw/Joplin/嵌入式+Linux/FreeRTOS学习笔记/09-事件标志组与任务通知\|事件标志组与任务通知]] | 09, 14 |
-| 10 | [[raw/Joplin/嵌入式+Linux/FreeRTOS学习笔记/10-软件定时器与Tickless低功耗\|软件定时器与Tickless低功耗]] | 19, 20 |
-| 11 | [[raw/Joplin/嵌入式+Linux/FreeRTOS学习笔记/11-内存管理\|内存管理]] | 21 |
+| 04 | [[raw/Joplin/嵌入式+Linux/FreeRTOS学习笔记/04-数据类型命名与配置详解\|数据类型命名与配置详解]] | — |
+| 05 | [[raw/Joplin/嵌入式+Linux/FreeRTOS学习笔记/05-任务创建与删除\|任务创建与删除]] | 01, 02 |
+| 06 | [[raw/Joplin/嵌入式+Linux/FreeRTOS学习笔记/06-任务挂起与恢复\|任务挂起与恢复]] | 03 |
+| 07 | [[raw/Joplin/嵌入式+Linux/FreeRTOS学习笔记/07-中断管理与临界段\|中断管理与临界段]] | 04 |
+| 08 | [[raw/Joplin/嵌入式+Linux/FreeRTOS学习笔记/08-时间片调度与延时管理\|时间片调度与延时管理]] | 05, 08 |
+| 09 | [[raw/Joplin/嵌入式+Linux/FreeRTOS学习笔记/09-任务状态查询与运行时统计\|任务状态查询与运行时统计]] | 06, 07 |
+| 10 | [[raw/Joplin/嵌入式+Linux/FreeRTOS学习笔记/10-消息队列\|消息队列]] | 09 |
+| 11 | [[raw/Joplin/嵌入式+Linux/FreeRTOS学习笔记/11-信号量机制\|信号量机制]] | 10, 11, 12, 13 |
+| 12 | [[raw/Joplin/嵌入式+Linux/FreeRTOS学习笔记/12-队列集\|队列集]] | 14 |
+| 13 | [[raw/Joplin/嵌入式+Linux/FreeRTOS学习笔记/13-事件标志组\|事件标志组]] | 15 |
+| 14 | [[raw/Joplin/嵌入式+Linux/FreeRTOS学习笔记/14-任务通知\|任务通知]] | 16, 17, 18 |
+| 15 | [[raw/Joplin/嵌入式+Linux/FreeRTOS学习笔记/15-软件定时器与Tickless低功耗\|软件定时器与Tickless低功耗]] | 19, 20 |
+| 16 | [[raw/Joplin/嵌入式+Linux/FreeRTOS学习笔记/16-内存管理\|内存管理]] | 21 |
 
 ## Wiki 页面
 

+ 822 - 0
docs/superpowers/plans/2026-07-17-freertos-ingest.md

@@ -0,0 +1,822 @@
+# 尚硅谷 FreeRTOS 笔记重构实现计划
+
+> **目标**:将尚硅谷 FreeRTOS 教程 V1.0.3(17 章笔记 + 22 个代码项目 + 48 张图片)转换为 16 篇结构化 Markdown 笔记
+> **目标目录**:`X-Knowledge-Base/raw/Joplin/嵌入式+Linux/FreeRTOS学习笔记/`
+> **设计规范**:`docs/superpowers/specs/2026-07-17-freertos-silicon-valley-ingest-design.md`
+> **工作流参考**:`docs/如何将他人文档整理为LLM-Wiki笔记.md`
+
+## 前置准备
+
+### 需要删除的旧文件
+
+- `X-Knowledge-Base/raw/Joplin/嵌入式+Linux/FreeRTOS学习笔记/assets/` 下的旧图片(全部删除后重建)
+
+### 源文件路径
+
+| 内容 | 路径 |
+|------|------|
+| 完整笔记 | `F:\Baidu\尚硅谷\尚硅谷嵌入式技术之FreeRTOS实时操作系统\01_笔记\尚硅谷嵌入式技术之FreeRTOS实时操作系统1.0.3.md` |
+| 原始图片 | `F:\Baidu\尚硅谷\尚硅谷嵌入式技术之FreeRTOS实时操作系统\01_笔记\images\media\`(48 张 PNG) |
+| 代码项目 | `F:\Baidu\尚硅谷\尚硅谷嵌入式技术之FreeRTOS实时操作系统\03_代码\代码\`(解压到临时目录) |
+| 原理图 | `F:\Baidu\尚硅谷\尚硅谷嵌入式技术之FreeRTOS实时操作系统\02_资料\02_按键案例\SCH_ZET6开发板_1_2024-07-01.pdf` |
+
+### 提取代码项目到临时目录
+
+```powershell
+Expand-Archive -LiteralPath "F:\Baidu\尚硅谷\尚硅谷嵌入式技术之FreeRTOS实时操作系统\03_代码\代码.zip" -DestinationPath "C:\Users\Administrator\AppData\Local\Temp\opencode\freertos_code" -Force
+```
+
+---
+
+## 每篇笔记通用操作步骤
+
+每个笔记的撰写流程如下:
+
+1. **读取源笔记对应章节**:从 `尚硅谷嵌入式技术之FreeRTOS实时操作系统1.0.3.md` 读取相关章节内容
+2. **读取配套代码项目**:读取对应项目的 `Core/Src/freertos_demo.c` 和 `Core/Inc/FreeRTOSConfig.h`
+3. **筛选图片**:从 `images/media/` 中找出与该笔记相关的图片,筛选保留有用的,重命名后复制到 `assets/`
+4. **撰写笔记**:按照设计文档中的模板撰写 Markdown 文件
+5. **写入目标路径**:保存到 `X-Knowledge-Base/raw/Joplin/嵌入式+Linux/FreeRTOS学习笔记/`
+
+**图片视觉验证命令模板**(用于验证图片内容):
+
+```powershell
+# 调用视觉 API 验证图片内容
+function Test-Img {
+  param($path, $question)
+  $b64 = [Convert]::ToBase64String([IO.File]::ReadAllBytes($path))
+  $body = @{ model = "qwen3.5-9b-uncensored-nothink"
+    messages = @(@{ role = "user"; content = @(
+      @{ type = "text"; text = $question }
+      @{ type = "image_url"; image_url = @{ url = "data:image/png;base64,$b64" } }
+    )})
+    max_tokens = 60 } | ConvertTo-Json -Depth 10
+  $r = Invoke-RestMethod -Uri "https://api.1808366.xyz/v1/chat/completions" ...
+  return $r.choices[0].message.content
+}
+```
+
+---
+
+## 文件产出清单
+
+### 目标:`X-Knowledge-Base/raw/Joplin/嵌入式+Linux/FreeRTOS学习笔记/`
+
+| 编号 | 文件名 | 来源章节 | 代码项目 |
+|------|--------|---------|---------|
+| 01 | `01-实时操作系统基础.md` | 第 1 章 | — |
+| 02 | `02-任务调度与状态管理.md` | 第 2 章 | — |
+| 03 | `03-FreeRTOS移植指南.md` | 第 3 章(移植 + 源码结构) | 00_HAL_Key |
+| 04 | `04-数据类型命名与配置详解.md` | 第 3 章(数据类型 + 命名 + 配置) | — |
+| 05 | `05-任务创建与删除.md` | 第 4 章 | 01, 02 |
+| 06 | `06-任务挂起与恢复.md` | 第 5 章 | 03 |
+| 07 | `07-中断管理与临界段.md` | 第 6 章 | 04 |
+| 08 | `08-时间片调度与延时管理.md` | 第 7 章 + 第 9 章 | 05, 08 |
+| 09 | `09-任务状态查询与运行时统计.md` | 第 8 章 | 06, 07 |
+| 10 | `10-消息队列.md` | 第 10 章 | 09 |
+| 11 | `11-信号量机制.md` | 第 11 章 | 10, 11, 12, 13 |
+| 12 | `12-队列集.md` | 第 12 章 | 14 |
+| 13 | `13-事件标志组.md` | 第 13 章 | 15 |
+| 14 | `14-任务通知.md` | 第 14 章 | 16, 17, 18 |
+| 15 | `15-软件定时器与Tickless低功耗.md` | 第 15 章 + 第 16 章 | 19, 20 |
+| 16 | `16-内存管理.md` | 第 17 章 | 21 |
+
+### 附加产出
+- `X-Knowledge-Base/raw/Joplin/嵌入式+Linux/FreeRTOS学习笔记/assets/` — 精选图片(15~25 张)
+- `wiki/source-freertos.md` — 重写资料摘要页
+- `wiki/index.md` — 更新 FreeRTOS 章节
+- `wiki/log.md` — 追加变更日志
+
+---
+
+## 任务 1:准备图片
+
+### 1.1 复制全部图片到临时目录
+
+```powershell
+$srcImages = "F:\Baidu\尚硅谷\尚硅谷嵌入式技术之FreeRTOS实时操作系统\01_笔记\images\media"
+$tmp = "C:\Users\Administrator\AppData\Local\Temp\opencode\freertos_images"
+New-Item -ItemType Directory -Path $tmp -Force
+Copy-Item "$srcImages\*.png" $tmp
+```
+
+### 1.2 识别并筛选图片
+
+使用视觉 API 验证每张图片内容。48 张图片的预期内容分类:
+
+| 图片编号 | 预期内容 | 保留? | 建议文件名 |
+|---------|---------|--------|-----------|
+| image1 | 感知并发 vs 实际多任务图 | 保留 | multitasking_concurrent_vs_actual.png |
+| image2 | 抢占式调度示意图 | 保留 | scheduling_preemption.png |
+| image3 | 时间片轮转调度图 | 保留 | scheduling_timeslice.png |
+| image4 | 任务状态转换图(运行/就绪/阻塞/挂起) | 保留 | task_state_diagram.png |
+| image5 | 就绪列表位图 | 保留 | ready_list_bitmap.png |
+| image6 | 同优先级就绪列表 | 保留 | ready_list_same_priority.png |
+| image7 | 上下文切换步骤图(保存上下文) | 保留 | context_switch_save.png |
+| image8 | 上下文切换步骤图(恢复上下文) | 保留 | context_switch_restore.png |
+| image9 | FreeRTOS 官网下载页面截图 | 删除 | — |
+| image10 | GitHub 下载页面截图 | 删除 | — |
+| image11 | RVDS 文件夹结构截图 | 删除(可文字描述) | — |
+| image12 | heap 文件列表截图 | 删除(可文字描述) | — |
+| image13~image27 | Keil 工程添加文件步骤截图 | 全部删除(IDE 操作截图) | — |
+| image28~image29 | 寄存器版 Keil 添加截图 | 全部删除(IDE 操作截图) | — |
+| image30~image43 | 配置截图/CubeMX 截图 | 全部删除(IDE 操作截图) | — |
+| image44 | 队列结构图 | 保留 | queue_structure.png |
+| image45 | 事件标志组位结构图 | 保留 | event_group_bits.png |
+| image46 | 定时器状态切换图 | 保留 | timer_state_diagram.png |
+| image47 | STM32 低功耗模式图 | 保留 | stm32_low_power_modes.png |
+| image48 | 三种 heap 算法结构对比 | 保留 | heap_comparison.png |
+
+筛选后预计保留约 **14 张**图片,其余 34 张删除。
+
+### 1.3 重命名并复制到 assets
+
+```powershell
+$target = "X-Knowledge-Base/raw/Joplin/嵌入式+Linux/FreeRTOS学习笔记/assets"
+New-Item -ItemType Directory -Path $target -Force
+
+# 保留的图片按映射命名复制
+Copy-Item "$tmp\image1.png" "$target\multitasking_concurrent_vs_actual.png"
+Copy-Item "$tmp\image2.png" "$target\scheduling_preemption.png"
+Copy-Item "$tmp\image3.png" "$target\scheduling_timeslice.png"
+Copy-Item "$tmp\image4.png" "$target\task_state_diagram.png"
+Copy-Item "$tmp\image5.png" "$target\ready_list_bitmap.png"
+Copy-Item "$tmp\image6.png" "$target\ready_list_same_priority.png"
+Copy-Item "$tmp\image7.png" "$target\context_switch_save.png"
+Copy-Item "$tmp\image8.png" "$target\context_switch_restore.png"
+Copy-Item "$tmp\image44.png" "$target\queue_structure.png"
+Copy-Item "$tmp\image45.png" "$target\event_group_bits.png"
+Copy-Item "$tmp\image46.png" "$target\timer_state_diagram.png"
+Copy-Item "$tmp\image47.png" "$target\stm32_low_power_modes.png"
+Copy-Item "$tmp\image48.png" "$target\heap_comparison.png"
+```
+
+---
+
+## 任务 2:笔记 01 — 实时操作系统基础.md
+
+**来源章节**:源笔记第 1 章(操作系统介绍 = 什么是裸机开发/什么是操作系统/通用OS/RTOS/FreeRTOS简介/发展历史/优势/特点)
+
+**代码项目**:无
+
+**图片**:无
+
+### 撰写要点
+
+- 解释裸机开发的局限性:所有代码在 while(1) 循环中顺序执行,实时性无法保证
+- 对比通用 OS(Linux/Windows)与 RTOS(FreeRTOS/uCOS)的核心区别
+- FreeRTOS 的七个特点:任务调度、任务通信、内存管理、定时器、社区、可移植性
+- 用安全气囊 30ms 响应时间的例子说明"强大计算能力 ≠ 实时性"
+- FreeRTOS 发展历史:2003 Richard Barry → 2017 AWS 收购
+- 开源免费 ≠ 免费用于商业产品
+
+### 模板参考
+
+```markdown
+---
+tags: [source-summary]
+type: source
+source: "尚硅谷嵌入式技术之FreeRTOS实时操作系统 V1.0.3 — 第1章:操作系统介绍 + FreeRTOS简介"
+author: "尚硅谷研究院"
+date: 2026-07-17
+created: 2026-07-17
+---
+
+# 实时操作系统基础
+
+> **用生活理解**:如果把单片机比作一个厨师——裸机开发就像厨师必须自己记住每道菜的所有步骤,亲力亲为;RTOS 就像是给厨师配了一个帮厨团队——帮厨(操作系统)负责管理炒菜顺序、准备材料、分配时间,厨师(应用程序)只需专注于做菜本身。
+
+## 什么是裸机开发
+
+### 基本概念
+...
+```
+
+### 速查表内容
+
+无 API 速查表。本笔记以概念为主。
+
+---
+
+## 任务 3:笔记 02 — 任务调度与状态管理.md
+
+**来源章节**:源笔记第 2 章(多任务处理/任务调度/任务状态/滴答/上下文切换/空闲任务)
+
+**代码项目**:无(概念篇)
+
+**图片**:image1~image8
+
+### 撰写要点
+
+- 多任务处理:单核处理器的"并发假象"——任务间快速切换
+- 调度策略三要素:抢占式(高优先级抢占低)、时间片轮询(同优先级轮流)、协程式(任务主动让出)
+- 四任务状态详解:运行、就绪、阻塞、挂起,以及转换关系
+  - 阻塞态 vs 挂起态的本质区别:阻塞态等待事件自动恢复,挂起态需手动恢复
+- 滴答计数器:系统时基,SysTick 定时器产生
+- 上下文切换流程:
+  1. TaskA 上下文保存到堆栈(R0-R12, SP, LR, PC, xPSR)
+  2. 选择下一个任务
+  3. TaskB 上下文从堆栈恢复到寄存器
+  4. PendSV 异常处理(最低优先级,避免影响其他 ISR)
+- 触发上下文切换的三种情况:Tick 中断、任务执行完毕、任务主动挂起
+- 空闲任务:自动创建(最低优先级),三个作用(释放已删除任务内存、提供 CPU 空闲计数、进入低功耗模式)
+- 就绪列表:`pxReadyTasksLists[x]` 位图索引,32 位变量代表 0-31 优先级
+
+### 图片引用
+
+```markdown
+![感知并发 vs 实际多任务](assets/multitasking_concurrent_vs_actual.png)
+![抢占式调度](assets/scheduling_preemption.png)
+![时间片轮转](assets/scheduling_timeslice.png)
+![任务状态转换图](assets/task_state_diagram.png)
+![就绪列表位图](assets/ready_list_bitmap.png)
+![同优先级就绪列表](assets/ready_list_same_priority.png)
+![上下文切换 — 保存](assets/context_switch_save.png)
+![上下文切换 — 恢复](assets/context_switch_restore.png)
+```
+
+---
+
+## 任务 4:笔记 03 — FreeRTOS移植指南.md
+
+**来源章节**:源笔记第 3 章(源码结构 + HAL 库移植 + 寄存器移植)
+
+**代码项目**:`00_HAL_Key`(无 FreeRTOS 的纯 HAL 基础工程)
+
+### 撰写要点
+
+- 源码结构总览:FreeRTOS 内核(Source 7 个 .c + include + portable)
+- Source 目录 7 个核心文件:tasks.c、queue.c、timers.c、event_groups.c、croutine.c、list.c、stream_buffer.c
+- Portable 目录:不同编译器+内核的移植层,ARM_CM3 = port.c + portmacro.h
+- MemMang 5 种 heap:各有适用场景,初学用 heap_4
+- HAL 库移植步骤(逐条说明):
+  1. 新建 FreeRTOS 文件夹
+  2. 拷贝 7 个 .c 到 source
+  3. 拷贝 portable 中 Keil/RVDS/MemMang
+  4. 拷贝 include 头文件
+  5. 拷贝 FreeRTOSConfig.h 并修改
+  6. Keil 添加两个 Group(Source + Portable),添加 .c 文件
+  7. 添加头文件路径
+  8. 修改 FreeRTOSConfig.h(xPortPendSVHandler、vPortSVCHandler、INCLUDE_xTaskGetSchedulerState)
+  9. 修改 stm32f1xx_it.c:注释 SVC_Handler 和 PendSV_Handler,添加 SysTick_Handler 回调
+  10. 解决 HAL+SysTick 冲突问题(SYS 选择其他时钟源,中断优先级设为 1)
+- 寄存器移植:步骤类似,在 main.c 添加 SysTick_Handler
+- 配置文件说明:INCLUDE_ 前缀(函数使能)、config 前缀(功能配置)、其他
+
+### 图片引用
+
+使用文字描述代替 IDE 操作截图。可引用一张源码结构示意图(如有),但大多数 IDE 截图删除。
+
+### 速查表
+
+移植检查清单:
+
+| 步骤 | 检查项 | 是否完成 |
+|------|--------|---------|
+| 1 | FreeRTOS/Source 包含 7 个 .c | □ |
+| 2 | FreeRTOS/portable/RVDS/ARM_CM3 有 port.c + portmacro.h | □ |
+| 3 | MemMang 包含 heap_4.c | □ |
+| 4 | FreeRTOS/include 全部头文件 | □ |
+| 5 | FreeRTOSConfig.h 已修改 | □ |
+| 6 | stm32f1xx_it.c 中 SVC/PendSV 已注释 | □ |
+| 7 | SysTick_Handler 已添加 xPortSysTickHandler 调用 | □ |
+| 8 | HAL 定时器源已切换(避免与 FreeRTOS 冲突) | □ |
+
+---
+
+## 任务 5:笔记 04 — 数据类型命名与配置详解.md
+
+**来源章节**:源笔记第 3 章后半(数据类型/命名规范/FreeRTOSConfig.h 配置说明)
+
+**代码项目**:无
+
+### 撰写要点
+
+- 数据类型表:
+
+| 类型 | 描述 | 默认定义 | 配置决定 |
+|------|------|---------|---------|
+| TickType_t | 滴答计数值 | uint32_t | configUSE_16_BIT_TICKS |
+| BaseType_t | 最自然类型 | uint32_t(Cortex-M3) | 架构相关 |
+| StackType_t | 堆栈单位 | uint32_t | 架构相关 |
+
+- 命名前缀表:
+
+| 前缀 | 含义 | 示例 |
+|------|------|------|
+| v | 返回 void | vTaskDelete |
+| x | 返回 BaseType_t 或非 void 结构体 | xQueueCreate |
+| ux | 返回 UBaseType_t | uxTaskGetNumberOfTasks |
+| pv | 返回 void* | pvPortMalloc |
+| h | 句柄类型后缀 | TaskHandle_t |
+| pd | 协议定义(projdefs.h) | pdTRUE, pdPASS |
+
+- FreeRTOSConfig.h 全配置项参考(分组列出所有项,标注默认值和课程常用值)
+
+---
+
+## 任务 6:笔记 05 — 任务创建与删除.md
+
+**来源章节**:源笔记第 4 章(任务创建和删除)
+
+**代码项目**:01(动态创建删除)、02(静态创建删除)
+
+### 撰写要点
+
+- 任务 = 无限循环函数 + 堆栈 + TCB
+- 动态创建 vs 静态创建:`xTaskCreate` 自动分配 vs `xTaskCreateStatic` 预先分配
+- 完整代码展示 01 项目的 `freertos_demo.c`(作为通用模板参考)
+- 静态创建需要 `vApplicationGetIdleTaskMemory` 和 `vApplicationGetTimerTaskMemory` 钩子
+
+### 代码提取(项目 01 — 动态创建删除)
+
+从 `03_代码/01_动态创建任务和删除/Core/Src/freertos_demo.c` 提取:
+
+- `freertos_start()` 函数(创建 start_task + vTaskStartScheduler)
+- `start_task()` 函数(taskENTER_CRITICAL → 创建 3 个任务 → vTaskDelete(NULL) → taskEXIT_CRITICAL)
+- `task1()` LED 切换 + `task2()` LED 切换 + `task3()` 按键检测删除 task1
+
+### 代码提取(项目 02 — 静态创建删除)
+
+从 `03_代码/02_静态创建任务和删除/Core/Src/freertos_demo.c` 提取:
+
+- 使用 `xTaskCreateStatic` 的差异点
+- `vApplicationGetIdleTaskMemory` 和 `vApplicationGetTimerTaskMemory` 钩子实现
+
+### 关键配置
+
+```c
+// 静态分配需要开启
+#define configSUPPORT_STATIC_ALLOCATION  1
+
+// 软件定时器任务配置(静态创建需要)
+#define configUSE_TIMERS                 1
+#define configTIMER_TASK_PRIORITY        4
+#define configTIMER_QUEUE_LENGTH         5
+#define configTIMER_TASK_STACK_DEPTH     256
+```
+
+---
+
+## 任务 7:笔记 06 — 任务挂起与恢复.md
+
+**来源章节**:源笔记第 5 章(任务挂起与恢复)
+
+**代码项目**:03(任务挂起与恢复)
+
+### 撰写要点
+
+- 挂起态:任务暂停,不参与调度,需要手动恢复
+- 单任务控制:`vTaskSuspend()` / `vTaskResume()`
+- 调度器控制:`vTaskSuspendAll()` / `xTaskResumeAll()`
+- 挂起 vs 阻塞 vs 删除深度对比
+
+### 代码提取(项目 03)
+
+提取 `03_代码/03_任务挂起与恢复/Core/Src/freertos_demo.c`:
+
+- KEY1 = vTaskSuspend(task1)
+- KEY2 = vTaskResume(task1)
+- KEY3 = vTaskSuspendAll()
+- KEY4 = xTaskResumeAll()
+- vTaskList() 打印当前任务状态
+
+---
+
+## 任务 8:笔记 07 — 中断管理与临界段.md
+
+**来源章节**:源笔记第 6 章(中断管理)
+
+**代码项目**:04(中断管理)
+
+### 撰写要点
+
+- FreeRTOS 中断管理策略:关中断(临界区)/ BASEPRI(屏蔽低于阈值的中断)
+- `taskENTER_CRITICAL()` / `taskEXIT_CRITICAL()` — 嵌套计数器
+- `portDISABLE_INTERRUPTS()` / `portENABLE_INTERRUPTS()`
+- 中断安全 API 后缀:`FromISR` 版本(如 `xQueueSendFromISR`)
+- `configMAX_SYSCALL_INTERRUPT_PRIORITY`:FreeRTOS 可管理的中断优先级阈值
+- 延迟中断处理(Deferred Interrupt Processing):ISR 中发信号通知任务处理
+
+### 代码提取(项目 04)
+
+提取 `03_代码/04_中断管理/Core/Src/freertos_demo.c`:
+
+- KEY1 = portDISABLE_INTERRUPTS()
+- KEY2 = portENABLE_INTERRUPTS()
+
+### 关键配置
+
+```c
+#define configKERNEL_INTERRUPT_PRIORITY        (15 << 4)
+#define configMAX_SYSCALL_INTERRUPT_PRIORITY   (5 << 4)
+```
+
+---
+
+## 任务 9:笔记 08 — 时间片调度与延时管理.md
+
+**来源章节**:源笔记第 7 章(时间片调度)+ 第 9 章(时间管理/延时函数)
+
+**代码项目**:05(时间片调度)、08(延时函数对比)
+
+### 撰写要点
+
+- 时间片调度:同优先级任务轮流执行一个 Tick 时间
+- `configUSE_TIME_SLICING` 控制开关
+- `vTaskDelay()` — 相对延时(从调用开始算,可能漂移)
+- `vTaskDelayUntil()` — 绝对延时(从上次唤醒算,固定周期)
+- `HAL_Delay()` vs `vTaskDelay()` 区别:前者忙等(不释放 CPU),后者阻塞(让出 CPU)
+
+### 代码提取(项目 05)
+
+提取 `03_代码/05_时间片调度/Core/Src/freertos_demo.c`:
+
+- 2 个同优先级任务进入临界区 → 打印计数 → HAL_Delay(10) → 退出临界区
+- 演示时间片轮转效果
+
+### 代码提取(项目 08)
+
+提取 `03_代码/08_时间延时函数/Core/Src/freertos_demo.c`:
+
+- task1 使用 vTaskDelay(500) 切换 LED1(漂移)
+- task2 使用 vTaskDelayUntil(&pxPreviousWakeTime, 500) 切换 LED2(固定周期)
+- 两者都额外使用 HAL_Delay(20) 模拟工作负载,观察漂移差异
+
+### 关键配置
+
+```c
+// 项目 05 将滴答频率降低到 50ms 方便观察时间片
+#define configTICK_RATE_HZ         20
+#define configUSE_TIME_SLICING     1
+```
+
+---
+
+## 任务 10:笔记 09 — 任务状态查询与运行时统计.md
+
+**来源章节**:源笔记第 8 章(任务相关 API 函数)
+
+**代码项目**:06(任务状态查询)、07(运行时统计)
+
+### 撰写要点
+
+- 任务查询 API 全家桶:
+  - 优先级:`uxTaskPriorityGet()` / `vTaskPrioritySet()`
+  - 数量:`uxTaskGetNumberOfTasks()`
+  - 系统状态:`uxTaskGetSystemState()` / `vTaskGetInfo()`
+  - 句柄:`xTaskGetCurrentTaskHandle()` / `xTaskGetHandle()`
+  - 栈余量:`uxTaskGetStackHighWaterMark()`
+  - 状态:`eTaskGetState()`
+  - 列表:`vTaskList()`
+- 运行时统计需要额外硬件定时器提供高精度时钟源
+
+### 代码提取(项目 06)
+
+提取 `03_代码/06_任务状态查询/Core/Src/freertos_demo.c`:
+
+- task2 中一次性调用所有查询 API 并打印结果
+
+### 代码提取(项目 07)
+
+提取 `03_代码/07_任务时间统计/Core/Src/freertos_demo.c`:
+
+- task2 周期打印 `vTaskGetRunTimeStats()`
+
+### 关键配置
+
+```c
+#define configUSE_TRACE_FACILITY             1
+#define configUSE_STATS_FORMATTING_FUNCTIONS 1
+#define configGENERATE_RUN_TIME_STATS        1
+```
+
+---
+
+## 任务 11:笔记 10 — 消息队列.md
+
+**来源章节**:源笔记第 10 章(消息队列)
+
+**代码项目**:09(消息队列)
+
+### 撰写要点
+
+- 队列本质:内核对象,任务间传递数据的 FIFO 缓冲区
+- 写时拷贝(Copy by Value)而非引用传递
+- 队列内部结构:环形缓冲区 + 发送等待列表 + 接收等待列表
+- 阻塞机制:队列入队/出队时可选阻塞时间
+- `xQueueCreate()` 参数说明
+
+### 代码提取(项目 09)
+
+提取 `03_代码/09_消息队列/Core/Src/freertos_demo.c`:
+
+- `queue1 = xQueueCreate(2, sizeof(uint8_t))` — 按键值队列
+- `big_queue = xQueueCreate(1, sizeof(char*))` — 大字符串指针队列
+- task1:KEY1/KEY2 → xQueueSend(queue1, &key, portMAX_DELAY)
+- task1:KEY3 → xQueueSend(big_queue, &big_data, portMAX_DELAY)
+- task2:xQueueReceive(queue1, &receive, portMAX_DELAY)
+- task3:xQueueReceive(big_queue, &receive_buff, portMAX_DELAY)
+
+### 关键配置
+
+```c
+#define configQUEUE_REGISTRY_SIZE   8
+```
+
+---
+
+## 任务 12:笔记 11 — 信号量机制.md
+
+**来源章节**:源笔记第 11 章(信号量 — 二值/计数/优先级翻转/互斥)
+
+**代码项目**:10(二值信号量)、11(计数信号量)、12(优先级反转)、13(互斥信号量)
+
+### 撰写要点
+
+- 信号量本质:基于队列实现,队列长度为 1(二值)或 N(计数)
+- 二值信号量:vSemaphoreCreateBinary → xSemaphoreGive → xSemaphoreTake
+- 计数信号量:xSemaphoreCreateCounting(max, init) → 每次 Give 计数值 +1
+- 优先级反转:低优先级任务持有信号量 → 高优先级等待 → 中优先级抢断 → 高优先级被阻塞
+  - 类比"倒反天罡":低级任务反而"压"着高级任务
+- 互斥信号量:内置优先级继承机制,解决优先级反转
+  - 当高优先级等待时,低优先级临时提升到高优先级(继承),完成后再降回原优先级
+- 区别对比表:
+
+| 特性 | 二值信号量 | 计数信号量 | 互斥量 |
+|------|-----------|-----------|--------|
+| 初始值 | 0 | 用户指定 | 1 |
+| 最大值 | 1 | 用户指定 | 1 |
+| 优先级继承 | 无 | 无 | 有 |
+| 递归获取 | 否 | 否 | 可配置 |
+| 适用场景 | 事件通知 | 资源计数 | 互斥访问 |
+
+### 代码提取(项目 10 — 二值信号量)
+
+提取 `03_代码/10_二值信号量/Core/Src/freertos_demo.c`:
+
+- task1:KEY1 → xSemaphoreGive
+- task2:xSemaphoreTake(sem_handle, portMAX_DELAY) 阻塞等待
+
+### 代码提取(项目 12 — 优先级反转)
+
+提取 `03_代码/12_优先级翻转/Core/Src/freertos_demo.c`:
+
+- task1(prio 2):持有信号量 → HAL_Delay(3000)
+- task2(prio 3):中等优先级持续运行
+- task3(prio 4):尝试获取信号量 → 被 task1 持有,等待
+
+### 代码提取(项目 13 — 互斥信号量)
+
+提取 `03_代码/13_互斥信号量/Core/Src/freertos_demo.c`:
+
+- 与项目 12 相同结构,但使用 `xSemaphoreCreateMutex()` 替代 `vSemaphoreCreateBinary()`
+
+### 关键配置
+
+```c
+#define configUSE_COUNTING_SEMAPHORES   1
+#define configUSE_MUTEXES               1
+#define configUSE_RECURSIVE_MUTEXES     1
+```
+
+---
+
+## 任务 13:笔记 12 — 队列集.md
+
+**来源章节**:源笔记第 12 章(队列集)
+
+**代码项目**:14(队列集)
+
+### 撰写要点
+
+- 队列集解决"同时等待多个队列/信号量"的问题
+- 工作原理:将队列/信号量注册到集合 → 集合中任一成员有数据即通知
+- 限制:队列集中的队列/信号量不能被独立删除
+
+### 代码提取(项目 14)
+
+提取 `03_代码/14_队列集/Core/Src/freertos_demo.c`:
+
+- `xQueueCreateSet(queue_set_size)` — 集合大小 = 成员项数之和
+- `xQueueAddToSet(queue1, queue_set_handle)` — 注册队列
+- `xQueueAddToSet(sem_handle, queue_set_handle)` — 注册信号量
+- `xQueueSelectFromSet(queue_set_handle, portMAX_DELAY)` — 等待任一成员
+- KEY1 → 加入队列,KEY2 → 给出信号量
+
+### 关键配置
+
+```c
+#define configUSE_QUEUE_SETS   1
+```
+
+---
+
+## 任务 14:笔记 13 — 事件标志组.md
+
+**来源章节**:源笔记第 13 章(事件标志组)
+
+**代码项目**:15(事件标志组)
+
+### 撰写要点
+
+- 事件标志组 = 24 位可用的位集合(RTOS 保留高 8 位)
+- `xEventGroupWaitBits()` 中的等待逻辑:AND(全部满足)vs OR(任一满足)
+- 清除模式:退出时自动清除(pdTRUE)或手动清除(pdFALSE)
+
+### 代码提取(项目 15)
+
+提取 `03_代码/15_事件标志组/Core/Src/freertos_demo.c`:
+
+- `event_group_handle = xEventGroupCreate()`
+- task1:KEY1 → `xEventGroupSetBits(event_group_handle, EVENTBIT_0)`
+- task1:KEY2 → `xEventGroupSetBits(event_group_handle, EVENTBIT_1)`
+- task2:`xEventGroupWaitBits(handle, BIT0|BIT1, pdTRUE, pdTRUE, portMAX_DELAY)` — 等两个位都置1
+
+### 图片引用
+
+```markdown
+![事件标志组位结构](assets/event_group_bits.png)
+```
+
+---
+
+## 任务 15:笔记 14 — 任务通知.md
+
+**来源章节**:源笔记第 14 章(任务通知)
+
+**代码项目**:16(通知模拟信号量)、17(通知模拟消息队列)、18(通知模拟事件标志组)
+
+### 撰写要点
+
+- 任务通知:每个 TCB 内置 32 位通知值 + 8 位通知状态
+- 三种模式:
+  1. 模拟信号量:`xTaskNotifyGive()` / `ulTaskNotifyTake()` — 计数值累加/递减
+  2. 模拟消息队列:`xTaskNotify(handle, value, eSetValueWithOverwrite)` — 覆写通知值
+  3. 模拟事件标志组:`xTaskNotify(handle, bits, eSetBits)` — 按位或置位
+- 优势:比独立 IPC 快 ~45%(无额外内核对象创建)
+- 限制:只能一对一(一个任务通知另一个),不能一对多
+
+### 代码提取(项目 16 — 模拟信号量)
+
+提取 `03_代码/16_任务通知模拟信号量/Core/Src/freertos_demo.c`:
+
+- task1:KEY1 → `xTaskNotifyGive(task2_handle)`
+- task2:`ulTaskNotifyTake(pdFALSE, portMAX_DELAY)` — 计数值递减
+
+### 代码提取(项目 17 — 模拟消息队列)
+
+提取 `03_代码/17_任务通知模拟消息队列/Core/Src/freertos_demo.c`:
+
+- task1:KEY1/KEY2 → `xTaskNotify(task2_handle, key_value, eSetValueWithOverwrite)`
+- task2:`xTaskNotifyWait(ulBitsToClearOnEntry, ulBitsToClearOnExit, &notification_value, portMAX_DELAY)`
+
+### 代码提取(项目 18 — 模拟事件标志组)
+
+提取 `03_代码/18_任务通知模拟事件标志组/Core/Src/freertos_demo.c`:
+
+- task1:KEY1 → `xTaskNotify(task2_handle, EVENTBIT_0, eSetBits)`
+- task1:KEY2 → `xTaskNotify(task2_handle, EVENTBIT_1, eSetBits)`
+- task2:`xTaskNotifyWait(0, ULONG_MAX, &notify_val, portMAX_DELAY)` 并检查位
+
+---
+
+## 任务 16:笔记 15 — 软件定时器与Tickless低功耗.md
+
+**来源章节**:源笔记第 15 章(软件定时器)+ 第 16 章(Tickless 低功耗模式)
+
+**代码项目**:19(软件定时器)、20(Tickless 低功耗)
+
+### 软件定时器部分
+
+- 软件定时器基于 Tick 计数(非硬件定时器)
+- 状态机:休眠 → 运行 → 暂停
+- 一次性(AutoReload = pdFALSE)vs 周期性(AutoReload = pdTRUE)
+- 定时器命令队列:所有 API 通过队列向定时器服务任务发送命令
+- 定时器服务任务:一个标准 FreeRTOS 任务,处理所有定时器回调
+
+### Tickless 部分
+
+- Tickless 模式:空闲任务进入低功耗模式,暂停 SysTick 中断以省电
+- `configUSE_TICKLESS_IDLE`:开启/关闭
+- `configPRE_SLEEP_PROCESSING(x)` 和 `configPOST_SLEEP_PROCESSING(x)` — 用户在休眠前后执行的自定义操作
+- 最小空闲时间:`configEXPECTED_IDLE_TIME_BEFORE_SLEEP`(太短则不休眠)
+
+### 代码提取(项目 19 — 软件定时器)
+
+提取 `03_代码/19_软件定时器/Core/Src/freertos_demo.c`:
+
+- `timer1 = xTimerCreate("timer1", 500, pdFALSE, NULL, timer1_callback)` — 一次性
+- `timer2 = xTimerCreate("timer2", 1000, pdTRUE, NULL, timer2_callback)` — 周期性
+- task1:KEY1 → `xTimerStart()`,KEY2 → `xTimerStop()`
+
+### 代码提取(项目 20 — Tickless)
+
+提取 `03_代码/20_低功耗模式/Core/Src/freertos_demo.c`:
+
+- 信号量驱动:task1 KEY1 → xSemaphoreGive → task2 接收
+- `configPRE_SLEEP_PROCESSING(x)` → 休眠前关 GPIO 时钟
+- `configPOST_SLEEP_PROCESSING(x)` → 唤醒后开 GPIO 时钟
+
+### 关键配置
+
+```c
+// 软件定时器
+#define configUSE_TIMERS              1
+#define configTIMER_TASK_PRIORITY     4
+#define configTIMER_QUEUE_LENGTH      5
+#define configTIMER_TASK_STACK_DEPTH  256
+
+// Tickless
+#define configUSE_TICKLESS_IDLE       1
+```
+
+### 图片引用
+
+```markdown
+![定时器状态切换图](assets/timer_state_diagram.png)
+![STM32 低功耗模式](assets/stm32_low_power_modes.png)
+```
+
+---
+
+## 任务 17:笔记 16 — 内存管理.md
+
+**来源章节**:源笔记第 17 章(内存管理 — heap_1~5)
+
+**代码项目**:21(内存管理)
+
+### 撰写要点
+
+- FreeRTOS 内存分配发生在:任务创建(堆栈+TCB)、队列创建、信号量创建、定时器创建
+- 5 种 heap 算法对比:
+
+| 算法 | 分配策略 | 释放 | 碎片 | 适用场景 |
+|------|---------|------|------|---------|
+| heap_1 | 简单数组分配 | 不支持释放 | 无 | 不删除任务/队列 |
+| heap_2 | 最佳匹配链表 | 支持释放 | 有 | 随意分配释放 |
+| heap_3 | 包装 malloc/free | 标准 C 库 | 取决于 C 库 | 线程安全需要 |
+| heap_4 | 首序匹配 + 合并 | 合并相邻空闲块 | 优干 heap_2 | 默认推荐 |
+| heap_5 | 同 heap_4 | 跨非连续 RAM 区域 | 同 heap_4 | 多 RAM 区域 |
+
+- `pvPortMalloc()` / `vPortFree()` / `xPortGetFreeHeapSize()`
+
+### 代码提取(项目 21)
+
+提取 `03_代码/21_内存管理/Core/Src/freertos_demo.c`:
+
+- task1:KEY1 → `buff = pvPortMalloc(20)` → 打印 `xPortGetFreeHeapSize()`
+- task1:KEY2 → `vPortFree(buff)` → 打印堆大小
+
+### 图片引用
+
+```markdown
+![三种 heap 算法结构对比](assets/heap_comparison.png)
+```
+
+---
+
+## 任务 18:更新 Wiki
+
+### 18.1 重写 `wiki/source-freertos.md`
+
+- 笔记列表改为 16 篇新笔记映射
+- 代码项目映射表更新
+
+### 18.2 更新 `wiki/index.md`
+
+- FreeRTOS 章节保持不变(wikilink 指向 wiki 页不受影响)
+- 确认原始笔记路径条目已更新到最新目录结构
+
+### 18.3 追加 `wiki/log.md`
+
+```markdown
+## 2026-07-17
+
+- `lint`: 重构 FreeRTOS raw 笔记(16 篇)
+    - 旧 11 篇笔记 → 新 16 篇笔记,按课程章节一对一/合并映射
+    - 代码来源:22 个正式 Keil 项目
+    - 图片精选:48 张 → 约 14 张(删除 IDE/Keil 配置截图)
+    - 每篇标准结构:frontmatter + 生活类比 + 概念 + 实验代码 + API 速查表 + 避坑
+    - 遵循设计规范:`docs/superpowers/specs/2026-07-17-freertos-silicon-valley-ingest-design.md`
+    
+    新笔记列表:
+    - 01 ~ 16 覆盖:实时操作系统基础/任务调度与状态管理/移植指南/数据类型命名与配置/任务创建删除/挂起恢复/中断管理临界段/时间片调度延时管理/任务状态查询运行时统计/消息队列/信号量机制/队列集/事件标志组/任务通知/软件定时器与Tickless低功耗/内存管理
+
+    更新 `wiki/source-freertos.md`、`wiki/index.md`、`wiki/log.md`
+```
+
+---
+
+## 最终验证清单
+
+- [ ] 16 篇笔记全部写入目标目录
+- [ ] 每篇笔记 frontmatter 完整
+- [ ] 每篇笔记有 `> **用生活理解**:` 类比
+- [ ] 代码路径引用指向真实项目文件
+- [ ] API 调用行有中文注释
+- [ ] 图片路径引用(assets/xxx.png)有效
+- [ ] 无冗余 IDE 操作截图保留
+- [ ] `wiki/source-freertos.md` 已更新
+- [ ] `wiki/index.md` 已更新
+- [ ] `wiki/log.md` 已追加

+ 453 - 0
docs/superpowers/specs/2026-07-17-freertos-silicon-valley-ingest-design.md

@@ -0,0 +1,453 @@
+# 设计文档:尚硅谷 FreeRTOS 笔记重构计划
+
+**日期**:2026-07-17
+**来源**:尚硅谷嵌入式技术之FreeRTOS实时操作系统 V1.0.3
+**目标目录**:`raw/Joplin/嵌入式+Linux/FreeRTOS学习笔记/`
+**目标芯片**:STM32F103ZET6(Cortex-M3)
+**参考设计**:`docs/superpowers/specs/2026-07-15-stm32-silicon-valley-ingest-design.md`
+**工作流参考**:`docs/如何将他人文档整理为LLM-Wiki笔记.md`
+
+---
+
+## 1. 笔记分篇(共 16 篇)
+
+### 总览
+
+| #  | 文件名 | 对应课程章节 | 配套代码项目 |
+| -- | ------ | ----------- | ----------- |
+| 01 | `01-实时操作系统基础.md` | 第 1 章(操作系统介绍 + FreeRTOS 简介) | — |
+| 02 | `02-任务调度与状态管理.md` | 第 2 章(多任务/调度策略/任务状态/滴答/上下文切换/空闲任务) | — |
+| 03 | `03-FreeRTOS移植指南.md` | 第 3 章(源码结构 + HAL 移植 + 寄存器移植 + 配置文件说明) | 00_HAL_Key |
+| 04 | `04-数据类型命名与配置详解.md` | 第 3 章后半(数据类型/命名规范/FreeRTOSConfig.h 全项) | — |
+| 05 | `05-任务创建与删除.md` | 第 4 章(动态创建 + 静态创建 + 删除) | 01, 02 |
+| 06 | `06-任务挂起与恢复.md` | 第 5 章(单任务挂起/恢复 + 调度器挂起/恢复) | 03 |
+| 07 | `07-中断管理与临界段.md` | 第 6 章(临界区/BASEPRI/中断安全 API/延迟中断处理) | 04 |
+| 08 | `08-时间片调度与延时管理.md` | 第 7 章 + 第 9 章(时间片/vTaskDelay/vTaskDelayUntil) | 05, 08 |
+| 09 | `09-任务状态查询与运行时统计.md` | 第 8 章(状态查询 API/vTaskList/vTaskGetRunTimeStats) | 06, 07 |
+| 10 | `10-消息队列.md` | 第 10 章(队列创建/发送/接收/多队列) | 09 |
+| 11 | `11-信号量机制.md` | 第 11 章(二值信号量/计数型/优先级翻转/互斥信号量) | 10, 11, 12, 13 |
+| 12 | `12-队列集.md` | 第 12 章(队列集创建/添加/选择读取) | 14 |
+| 13 | `13-事件标志组.md` | 第 13 章(事件组创建/置位/等待) | 15 |
+| 14 | `14-任务通知.md` | 第 14 章(通知模拟信号量/消息队列/事件标志组三种模式) | 16, 17, 18 |
+| 15 | `15-软件定时器与Tickless低功耗.md` | 第 15 章 + 第 16 章(定时器/一次性/周期/Tickless 模式) | 19, 20 |
+| 16 | `16-内存管理.md` | 第 17 章(heap_1~5 算法/pvPortMalloc/vPortFree/xPortGetFreeHeapSize) | 21 |
+
+### 分篇依据
+
+- 每篇对应课程中一个独立概念群,确保每篇可独立阅读
+- 代码项目按功能归属分配到对应笔记
+- 以"介绍 → 工作原理 → API → 实验 → 速查表 → 避坑"结构组织
+
+---
+
+## 2. 每篇笔记模板
+
+统一的 Markdown 模板,参考 STM32 笔记格式:
+
+```markdown
+---
+tags: [source-summary]
+type: source
+source: "尚硅谷嵌入式技术之FreeRTOS实时操作系统 V1.0.3 — {对应课程章节}"
+author: "尚硅谷研究院"
+date: 2026-07-17
+created: 2026-07-17
+---
+
+# {主题名称}
+
+> **用生活理解**:{一句话类比,用生活中的例子解释}
+
+## {概念}介绍
+
+### 基本概念
+{是什么、有什么用、解决什么问题}
+
+### 工作原理
+{框图说明 + 文字描述}
+> 参考:FreeRTOS 官方文档 §X.X
+
+### 核心 API
+
+| 函数 | 参数 | 功能简述 |
+| ---- | ---- | -------- |
+| xxx  | ...  | ...      |
+
+## {实验/案例}
+
+### 需求描述
+{实验要实现的功能}
+
+### 软件设计
+
+**文件:{代码项目相对路径}/Core/Src/freertos_demo.c**
+
+```c
+// 核心代码段,直接从项目复制,保留完整函数
+// 每行 API 调用标注通俗解释
+```
+
+### 关键配置
+
+```c
+// FreeRTOSConfig.h 中本实验特有的配置项
+```
+
+## 核心函数速查表
+
+| 函数 | 参数 | 返回值 | 功能简述 |
+| ---- | ---- | ------ | -------- |
+| xxx  | ...  | ...    | ...      |
+
+## 常见问题与避坑
+```
+
+### 硬性要求
+
+1. **Frontmatter**:必须包含 `tags: [source-summary]`, `type: source`, `source`, `author`, `date`, `created`
+2. **通俗类比**:每篇开头必须有一句 `> **用生活理解**:`,用生活场景类比技术概念
+3. **代码注释**:代码中每行 API 调用必须有中文注释说明其作用;保留 `// 按位或: 保持其他位不变` 这类位操作注释(如有)
+4. **文件标注**:每段代码前标注来源文件路径(相对于 FreeRTOS 学习笔记根目录或外部源码目录)
+5. **API 速查表**:每篇结尾汇总本笔记涉及的 API 函数
+6. **常见问题**:每篇结尾必须有"常见问题与避坑"章节
+
+---
+
+## 3. 代码策略
+
+### 3.1 代码来源
+
+所有代码示例直接来源于`F:\Baidu\尚硅谷\尚硅谷嵌入式技术之FreeRTOS实时操作系统\03_代码\`下的 22 个 Keil 项目(编号 00~21)。
+
+### 3.2 提取规则
+
+- 核心演示代码从 `Core/Src/freertos_demo.c` 提取
+- 保留完整函数体,不打断逻辑
+- 删除重复的模板代码(`start_task` 骨架在不同项目中几乎一致,在 05 笔记中完整展示一次即可,后续笔记中简写)
+- 删除 HAL 初始化骨架(`main.c` 中的 `HAL_Init()`, `SystemClock_Config()`, `MX_GPIO_Init()` 等,与 FreeRTOS 无关)
+- `FreeRTOSConfig.h` 中与本实验**特殊相关**的配置项(非通用基线)必须在笔记中说明
+
+### 3.3 通用模板代码简化
+
+所有项目共有的 `start_task` → `taskENTER_CRITICAL()` → 创建工作任务的模式,在 05 笔记完整展示后,从 06 起可简写为:
+
+```c
+// 创建工作任务的代码与 05 笔记相同,此处省略
+// 完整模板见 05-任务创建与删除.md
+```
+
+### 3.4 代码路径引用格式
+
+```
+**代码来源**:`03_代码/09_消息队列/Core/Src/freertos_demo.c`
+```
+
+---
+
+## 4. 图片策略
+
+### 4.1 源图片
+
+原始笔记 `F:\Baidu\尚硅谷\尚硅谷嵌入式技术之FreeRTOS实时操作系统\01_笔记\images\media\` 包含 48 张 PNG 图片(`image1.png` ~ `image48.png`)。
+
+### 4.2 筛选标准
+
+| 保留 | 删除 |
+|------|------|
+| 调度策略对比图 | IDE 安装/配置截图 |
+| 任务状态转换图 | Keil 工程添加文件步骤截图 |
+| 就绪列表位图 | CubeMX 配置截图 |
+| 上下文切换流程 | 下载链接/官网页面截图 |
+| 队列结构图 | 文件目录结构截图(源码结构等可文字描述) |
+| 信号量机制图 | 选项框勾选截图 |
+| 优先级反转时序 | 数值/文字设置截图 |
+| 事件组位结构图 | 代码编辑界面截图(代码已在正文中) |
+| heap 算法对比 | 与 FreeRTOS 功能无关的配图 |
+| 定时器状态机 | |
+
+### 4.3 重命名规则
+
+- 全部小写英文蛇形命名:`{主题}_{内容}.png`
+- 示例:`task_state_diagram.png`, `scheduling_preemption_vs_cooperative.png`
+- 映射表在实现计划中详细列出
+
+### 4.4 存放位置
+
+精选后的图片存放在 `raw/Joplin/嵌入式+Linux/FreeRTOS学习笔记/assets/`,笔记内用相对路径引用:
+
+```markdown
+![任务状态转换图](assets/task_state_diagram.png)
+```
+
+---
+
+## 5. 每篇笔记内容大纲
+
+### 01-实时操作系统基础.md
+
+| 章节 | 内容概要 |
+|------|---------|
+| 基本概念 | 裸机开发的局限性、OS vs 裸机、通用 OS vs RTOS |
+| 工作原理 | 计算机系统三层模型(硬件/OS/应用) |
+| FreeRTOS简介 | 发展历史(2003→2017 AWS收购)、特点、优势 |
+| 核心API | — |
+| 速查表 | — |
+| 常见问题 | 开源≠免费?RTOS 与 Linux 的区别 |
+
+### 02-任务调度与状态管理.md
+
+| 章节 | 内容概要 |
+|------|---------|
+| 基本概念 | 任务、内核、多任务处理 |
+| 工作原理 | 调度策略(抢占式/时间片/协程式)、4 种任务状态(运行/就绪/阻塞/挂起)、就绪列表位图、滴答计数器、上下文切换流程 |
+| 空闲任务 | 自动创建、作用(释放内存/计数/低功耗) |
+| 核心API | — |
+| 速查表 | — |
+| 常见问题 | 阻塞 vs 挂起本质区别、时间片提前结束条件 |
+
+### 03-FreeRTOS移植指南.md
+
+| 章节 | 内容概要 |
+|------|---------|
+| 源码结构 | FreeRTOS/FreeRTOS-Plus/Tools,Source 目录 7 个 .c + include + portable |
+| HAL库移植 | 目录添加源码、Keil 工程添加 Group、FreeRTOSConfig.h 修改(xPortPendSVHandler/vPortSVCHandler)、stm32f1xx_it.c 修改(SVC/PendSV 注释、SysTick 回调) |
+| 寄存器移植 | 同上 + main.c 添加 SysTick_Handler |
+| 配置文件说明 | 三类配置项:INCLUDE_、config开头、其他 |
+| 移植检查清单 | 关键步骤核对表 |
+| 常见问题 | HAL+SysTick 冲突、卡死排查 |
+
+### 04-数据类型命名与配置详解.md
+
+| 章节 | 内容概要 |
+|------|---------|
+| 数据类型 | TickType_t/BaseType_t/StackType_t 等定义与场景 |
+| 命名规范 | 前缀规则(v/x/ux/pv/h等)、函数命名、宏命名、数据类型命名 |
+| FreeRTOSConfig.h | 全部配置项分组说明(基础/内存/钩子/时间片/中断/函数使能)带推荐值 |
+| 速查表 | 数据类型、命名前缀对照表 |
+| 常见问题 | config 值设置不当的后果 |
+
+### 05-任务创建与删除.md
+
+| 章节 | 内容概要 |
+|------|---------|
+| 基本概念 | 任务控制块 TCB、任务堆栈、任务句柄 |
+| 工作原理 | xTaskCreate 动态分配 vs xTaskCreateStatic 静态分配 |
+| 核心API | xTaskCreate/xTaskCreateStatic/vTaskDelete |
+| 实验1 | 动态创建 3 个任务,task3 按键删除 task1(项目 01) |
+| 实验2 | 静态创建 3 个任务(项目 02),涉及 vApplicationGetIdleTaskMemory/TimerTaskMemory |
+| 关键配置 | configSUPPORT_STATIC_ALLOCATION=1 |
+| 速查表 | 创建/删除 API |
+| 常见问题 | 删除后句柄置 NULL 的重要性 |
+
+### 06-任务挂起与恢复.md
+
+| 章节 | 内容概要 |
+|------|---------|
+| 基本概念 | 挂起态本质(暂停,不参与调度) |
+| 核心API | vTaskSuspend/vTaskResume/vTaskSuspendAll/xTaskResumeAll/vTaskList |
+| 实验 | 4 个按键控制:挂起/恢复单任务、挂起/恢复调度器(项目 03) |
+| 速查表 | 挂起/恢复 API |
+| 常见问题 | 挂起 vs 阻塞 vs 删除深度对比 |
+
+### 07-中断管理与临界段.md
+
+| 章节 | 内容概要 |
+|------|---------|
+| 基本概念 | Cortex-M 中断优先级、BASEPRI 寄存器 |
+| 工作原理 | 临界区(taskENTER_CRITICAL/EXIT_CRITICAL)、中断安全 API(FromISR 后缀)、延迟中断处理(PendSV) |
+| 核心API | taskENTER_CRITICAL/taskEXIT_CRITICAL/portDISABLE_INTERRUPTS/portENABLE_INTERRUPTS |
+| 实验 | KEY1 禁用中断、KEY2 启用中断(项目 04) |
+| 关键配置 | configMAX_SYSCALL_INTERRUPT_PRIORITY |
+| 速查表 | 中断管理 API |
+| 常见问题 | 临界区内不要调用阻塞 API |
+
+### 08-时间片调度与延时管理.md
+
+| 章节 | 内容概要 |
+|------|---------|
+| 基本概念 | 时间片轮转调度、相对延时 vs 绝对延时 |
+| 工作原理 | 同等优先级任务轮流执行一个时间片、vTaskDelay 漂移问题、vTaskDelayUntil 固定周期 |
+| 核心API | vTaskDelay/vTaskDelayUntil/xTaskGetTickCount |
+| 实验1 | 同优先级 2 个任务时间片轮转(项目 05),临界区保护临界段 |
+| 实验2 | vTaskDelay vs vTaskDelayUntil 对比(项目 08) |
+| 关键配置 | configUSE_TIME_SLICING、configTICK_RATE_HZ |
+| 速查表 | 时间管理 API |
+| 常见问题 | vTaskDelayUntil 必须使用 while(1) 开头获取 tick |
+
+### 09-任务状态查询与运行时统计.md
+
+| 章节 | 内容概要 |
+|------|---------|
+| 基本概念 | 任务运行时信息、堆栈余量 |
+| 核心API | uxTaskPriorityGet/vTaskPrioritySet/uxTaskGetNumberOfTasks/uxTaskGetSystemState/vTaskGetInfo/xTaskGetCurrentTaskHandle/xTaskGetHandle/uxTaskGetStackHighWaterMark/eTaskGetState/vTaskList/vTaskGetRunTimeStats |
+| 实验1 | 完整状态查询 API 套件一次性演示(项目 06) |
+| 实验2 | 运行时统计打印(项目 07),需高精度定时器 |
+| 关键配置 | configUSE_TRACE_FACILITY、configUSE_STATS_FORMATTING_FUNCTIONS、configGENERATE_RUN_TIME_STATS |
+| 速查表 | 查询/统计 API |
+| 常见问题 | 运行时间统计需要额外硬件定时器 |
+
+### 10-消息队列.md
+
+| 章节 | 内容概要 |
+|------|---------|
+| 基本概念 | 队列是什么(任务间数据传递的 FIFO 缓冲区)、队列长度/项大小 |
+| 工作原理 | 队列内部结构(环形缓冲区/等待列表)、写时拷贝 |
+| 核心API | xQueueCreate/xQueueSend/xQueueReceive |
+| 实验 | 2 个队列:queue1 传按键值、big_queue 传指针(项目 09) |
+| 关键配置 | configQUEUE_REGISTRY_SIZE |
+| 速查表 | 队列 API |
+| 常见问题 | portMAX_DELAY 等待、队列满时的阻塞、消息队列与全局变量的选择 |
+
+### 11-信号量机制.md
+
+| 章节 | 内容概要 |
+|------|---------|
+| 基本概念 | 信号量是"钥匙"还是"计数器"?二值/计数/互斥的区别 |
+| 工作原理 | 基于队列实现、计数信号量累加、互斥信号量的优先级继承机制 |
+| 核心API | vSemaphoreCreateBinary/xSemaphoreCreateCounting/xSemaphoreCreateMutex/xSemaphoreGive/xSemaphoreTake/uxSemaphoreGetCount |
+| 实验1 | 二值信号量:task1 Give → task2 Take(项目 10) |
+| 实验2 | 计数信号量:KEY1 累加计数(项目 11) |
+| 实验3 | 优先级反转:低优先级任务持有信号量被高优先级等待(项目 12) |
+| 实验4 | 互斥信号量:优先级继承解决反转(项目 13) |
+| 关键配置 | configUSE_COUNTING_SEMAPHORES、configUSE_MUTEXES、configUSE_RECURSIVE_MUTEXES |
+| 速查表 | 信号量 API |
+| 常见问题 | 二值信号量与互斥量的选择、优先级反转的触发条件与解决方案 |
+
+### 12-队列集.md
+
+| 章节 | 内容概要 |
+|------|---------|
+| 基本概念 | 队列集用于同时等待队列和信号量 |
+| 工作原理 | 队列集内部机制:成员注册、事件通知 |
+| 核心API | xQueueCreateSet/xQueueAddToSet/xQueueSelectFromSet |
+| 实验 | 队列集包含一个队列和一个二值信号量(项目 14) |
+| 关键配置 | configUSE_QUEUE_SETS=1 |
+| 速查表 | 队列集 API |
+| 常见问题 | 队列集中不要单独删除成员 |
+
+### 13-事件标志组.md
+
+| 章节 | 内容概要 |
+|------|---------|
+| 基本概念 | 事件标志组是位的集合,任务等待特定位组合 |
+| 工作原理 | 24 位可用(RTOS 保留高 8 位)、等待逻辑(AND/OR) |
+| 核心API | xEventGroupCreate/xEventGroupSetBits/xEventGroupWaitBits |
+| 实验 | KEY1 置位 BIT0、KEY2 置位 BIT1、task2 等待两个位全部置位(项目 15) |
+| 速查表 | 事件标志组 API |
+| 常见问题 | 等待完成后自动清除位(pdTRUE)的使用 |
+
+### 14-任务通知.md
+
+| 章节 | 内容概要 |
+|------|---------|
+| 基本概念 | 任务通知是轻量级 IPC,每个 TCB 内置 32 位通知值 |
+| 工作原理 | 通知值可作信号量(计数递减)/消息队列(覆写值)/事件组(置位),比独立 IPC 快 45% |
+| 核心API | xTaskNotifyGive/ulTaskNotifyTake/xTaskNotify/xTaskNotifyWait |
+| 实验1 | 通知模拟信号量(项目 16) |
+| 实验2 | 通知模拟消息队列(项目 17)使用 eSetValueWithOverwrite |
+| 实验3 | 通知模拟事件标志组(项目 18)使用 eSetBits |
+| 速查表 | 任务通知 API |
+| 常见问题 | 通知不能用于 ISR 到多任务的分发、v10.4 后通知数组支持 |
+
+### 15-软件定时器与Tickless低功耗.md
+
+| 章节 | 内容概要 |
+|------|---------|
+| 基本概念 | 软件定时器基于 Tick 计数、不同于硬件定时器 |
+| 工作原理 | 定时器状态(休眠/运行/暂停)、一次性 vs 周期型、定时器命令队列、定时器服务任务 |
+| Tickless | 低功耗模式原理、configUSE_TICKLESS_IDLE、PRE_SLEEP_PROCESSING/POST_SLEEP_PROCESSING |
+| 核心API | xTimerCreate/xTimerStart/xTimerStop(软件定时器)、configUSE_TICKLESS_IDLE(低功耗) |
+| 实验1 | 一次性定时器 + 周期型定时器,按键启动/停止(项目 19) |
+| 实验2 | Tickless 模式:休眠时关闭 GPIO 时钟(项目 20) |
+| 关键配置 | configUSE_TIMERS、configTIMER_TASK_PRIORITY、configTIMER_QUEUE_LENGTH、configTIMER_TASK_STACK_DEPTH、configUSE_TICKLESS_IDLE |
+| 速查表 | 定时器 API + Tickless 配置项 |
+| 常见问题 | 定时器回调中不要调用阻塞 API、Tickless 模式的最小空闲时间配置 |
+
+### 16-内存管理.md
+
+| 章节 | 内容概要 |
+|------|---------|
+| 基本概念 | FreeRTOS 内存管理需求、5 种 heap 算法的设计哲学 |
+| 工作原理 | | 算法 | 适用场景 | 优点 | 缺点 |
+| |----|---------|------|------|
+| | heap_1 | 不删除任务或队列 | 简单、确定性强 | 不能释放 |
+| | heap_2 | 随意分配释放 | 支持释放 | 碎片化 |
+| | heap_3 | 需要线程安全 malloc | 包装 C 库函数 | 依赖编译器 |
+| | heap_4 | 默认推荐 | 合并相邻空闲块 | 大块释放后碎片 |
+| | heap_5 | 不连续 RAM 堆 | 跨内存区域分配 | 实现复杂 |
+| 核心API | pvPortMalloc/vPortFree/xPortGetFreeHeapSize |
+| 实验 | KEY1 分配 20 字节、KEY2 释放(项目 21) |
+| 关键配置 | configTOTAL_HEAP_SIZE、configSUPPORT_DYNAMIC_ALLOCATION |
+| 速查表 | 内存管理 API |
+| 常见问题 | heap 大小不足时的处理策略、碎片化监控 |
+
+---
+
+## 6. 目标目录结构
+
+```
+raw/Joplin/嵌入式+Linux/FreeRTOS学习笔记/
+├── assets/                           # 精选图片(预计 15~25 张)
+│   ├── task_state_diagram.png
+│   ├── scheduling_preemption.png
+│   ├── scheduling_timeslice.png
+│   ├── ready_list_bitmap.png
+│   ├── ready_list_same_priority.png
+│   ├── context_switch.png
+│   ├── queue_structure.png
+│   ├── priority_inversion.png
+│   ├── priority_inheritance.png
+│   ├── event_group_bits.png
+│   ├── timer_states.png
+│   ├── heap_comparison.png
+│   └── ...(根据实际图片内容命名)
+├── 01-实时操作系统基础.md
+├── 02-任务调度与状态管理.md
+├── 03-FreeRTOS移植指南.md
+├── 04-数据类型命名与配置详解.md
+├── 05-任务创建与删除.md
+├── 06-任务挂起与恢复.md
+├── 07-中断管理与临界段.md
+├── 08-时间片调度与延时管理.md
+├── 09-任务状态查询与运行时统计.md
+├── 10-消息队列.md
+├── 11-信号量机制.md
+├── 12-队列集.md
+├── 13-事件标志组.md
+├── 14-任务通知.md
+├── 15-软件定时器与Tickless低功耗.md
+└── 16-内存管理.md
+```
+
+---
+
+## 7. Wiki 更新
+
+重构完成后必须执行的维基更新:
+
+### 7.1 更新 `wiki/source-freertos.md`
+
+- 笔记列表改为 16 篇新笔记
+- 更新代码项目映射表
+
+### 7.2 更新 `wiki/index.md`
+
+- FreeRTOS 章节保持与已有 Wiki 页面的 `[[wikilink]]` 连接
+- 更新原始笔记路径映射
+
+### 7.3 追加 `wiki/log.md`
+
+- 记录本次重构操作
+- 注明 11 篇旧笔记被 16 篇新笔记替代
+
+---
+
+## 8. 硬性约束
+
+1. **不修改 `raw/` 以外的文件**(Wiki 除外:`source-freertos.md`, `index.md`, `log.md`)
+2. **所有代码必须来自真实项目文件**,不凭空编写代码
+3. **每行 API 代码必须标注通俗解释注释**
+4. **每篇必须有通俗类比**(`> **用生活理解**:`)
+5. **图片必须经过筛选**,不盲目全部复制
+6. **16 篇一次完成**,不分批次
+7. **旧 11 篇笔记覆盖前先确认备份**(实际已不存在,直接新建)