<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Rtos on Klin's Notebook 🍉</title><link>https://klinlike.github.io/tags/rtos/</link><description>Recent content in Rtos on Klin's Notebook 🍉</description><generator>Hugo -- gohugo.io</generator><language>zh-CN</language><copyright>Klin</copyright><lastBuildDate>Tue, 02 Jun 2026 19:55:41 +0800</lastBuildDate><atom:link href="https://klinlike.github.io/tags/rtos/index.xml" rel="self" type="application/rss+xml"/><item><title>理解RTOS任务切换：ARM寄存器、栈帧与上下文</title><link>https://klinlike.github.io/posts/2026-06-02-arm-stack-task-switch/</link><pubDate>Tue, 02 Jun 2026 19:55:41 +0800</pubDate><guid>https://klinlike.github.io/posts/2026-06-02-arm-stack-task-switch/</guid><description>&lt;h2 id="arm是risc架构"&gt;ARM是RISC架构
&lt;/h2&gt;&lt;p&gt;ARM属于精简指令集计算机（RISC），其核心理念是：&lt;strong&gt;内存只做读写，运算全部在CPU内部完成&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;这与CISC（如x86）不同——x86的一条指令可以直接对内存中的值进行运算（如内存值+5），而ARM必须先把数据从内存加载到寄存器，运算后再写回内存。&lt;/p&gt;
&lt;h2 id="三个关键寄存器"&gt;三个关键寄存器
&lt;/h2&gt;&lt;p&gt;R13、R14、R15是三个特殊寄存器，理解它们对掌握RTOS任务切换至关重要：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;寄存器&lt;/th&gt;
&lt;th&gt;别名&lt;/th&gt;
&lt;th&gt;作用&lt;/th&gt;
&lt;th&gt;RTOS意义&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;R13&lt;/td&gt;
&lt;td&gt;SP（Stack Pointer）&lt;/td&gt;
&lt;td&gt;指向当前栈顶地址&lt;/td&gt;
&lt;td&gt;任务切换时必须保存/恢复&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;R14&lt;/td&gt;
&lt;td&gt;LR（Link Register）&lt;/td&gt;
&lt;td&gt;保存函数返回地址&lt;/td&gt;
&lt;td&gt;函数调用链追踪&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;R15&lt;/td&gt;
&lt;td&gt;PC（Program Counter）&lt;/td&gt;
&lt;td&gt;存储下一条指令地址&lt;/td&gt;
&lt;td&gt;修改PC即跳转，任务切换的核心&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="核心汇编指令"&gt;核心汇编指令
&lt;/h2&gt;&lt;h3 id="数据搬运指令"&gt;数据搬运指令
&lt;/h3&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;指令&lt;/th&gt;
&lt;th&gt;作用&lt;/th&gt;
&lt;th&gt;示例&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;LDR&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;从内存读取4字节到寄存器&lt;/td&gt;
&lt;td&gt;&lt;code&gt;LDR R0, [R1]&lt;/code&gt; → 读R1地址处的值到R0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;LDRB&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;从内存读取1字节&lt;/td&gt;
&lt;td&gt;&lt;code&gt;LDRB R0, [R1]&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;LDRH&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;从内存读取2字节&lt;/td&gt;
&lt;td&gt;&lt;code&gt;LDRH R0, [R1]&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;STR&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;将寄存器值写入内存4字节&lt;/td&gt;
&lt;td&gt;&lt;code&gt;STR R0, [R1]&lt;/code&gt; → R0的值写入R1地址&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;STRB&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;写入1字节&lt;/td&gt;
&lt;td&gt;&lt;code&gt;STRB R0, [R1]&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;STRH&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;写入2字节&lt;/td&gt;
&lt;td&gt;&lt;code&gt;STRH R0, [R1]&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id="寻址方式"&gt;寻址方式
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;"&gt;&lt;tr&gt;&lt;td style="vertical-align:top;padding:0;margin:0;border:0;"&gt;
&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;1
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;2
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%"&gt;
&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-asm" data-lang="asm"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;LDR&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;R0&lt;/span&gt;, [&lt;span style="color:#66d9ef"&gt;R1&lt;/span&gt;] &lt;span style="color:#75715e"&gt;; 直接寻址：读取R1地址处的值
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;LDR&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;R0&lt;/span&gt;, [&lt;span style="color:#66d9ef"&gt;R1&lt;/span&gt;, &lt;span style="color:#75715e"&gt;#4] ; 带偏移：读取R1+4地址处的值
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;LDR&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;R0&lt;/span&gt;, [&lt;span style="color:#66d9ef"&gt;R1&lt;/span&gt;, &lt;span style="color:#66d9ef"&gt;R2&lt;/span&gt;] &lt;span style="color:#75715e"&gt;; 寄存器偏移：读取R1+R2地址处的值
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;以&lt;code&gt;LDR R0, [R1, R2]&lt;/code&gt;为例，假设R1 = &lt;code&gt;0x20000000&lt;/code&gt;，R2 = &lt;code&gt;0x04&lt;/code&gt;，则实际访问地址 = &lt;code&gt;0x20000004&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;偏移量的单位永远是字节&lt;/strong&gt;，指令后缀决定读写大小：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;LDR R0, [R1, #4]&lt;/code&gt;：偏移4字节，从目标地址读取&lt;strong&gt;4字节&lt;/strong&gt;数据&lt;/li&gt;
&lt;li&gt;&lt;code&gt;LDRB R0, [R1, #4]&lt;/code&gt;：偏移同样是4字节，但只读取&lt;strong&gt;1字节&lt;/strong&gt;数据&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="运算指令"&gt;运算指令
&lt;/h3&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;指令&lt;/th&gt;
&lt;th&gt;作用&lt;/th&gt;
&lt;th&gt;示例&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ADD&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;加法&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ADD R0, R0, R1&lt;/code&gt; → R0 = R0 + R1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SUB&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;减法&lt;/td&gt;
&lt;td&gt;&lt;code&gt;SUB R0, R0, #1&lt;/code&gt; → R0 = R0 - 1&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id="比较与跳转指令"&gt;比较与跳转指令
&lt;/h3&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;指令&lt;/th&gt;
&lt;th&gt;作用&lt;/th&gt;
&lt;th&gt;示例&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;CMP&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;比较两个值，更新状态寄存器&lt;/td&gt;
&lt;td&gt;&lt;code&gt;CMP R0, R1&lt;/code&gt; → 不改变R0和R1的值&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;B&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;无条件跳转&lt;/td&gt;
&lt;td&gt;&lt;code&gt;B label&lt;/code&gt; → 跳转到label处&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;BL&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;带链接跳转（函数调用）&lt;/td&gt;
&lt;td&gt;&lt;code&gt;BL func&lt;/code&gt; → 保存返回地址到LR，跳转到func&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;重点理解&lt;code&gt;BL&lt;/code&gt;指令&lt;/strong&gt;：假设执行&lt;code&gt;BL func&lt;/code&gt;时PC = &lt;code&gt;0x100&lt;/code&gt;，执行后：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;LR = &lt;code&gt;0x104&lt;/code&gt;（下一条指令的地址，即返回地址）&lt;/li&gt;
&lt;li&gt;PC = func的入口地址&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;效果：跳转到目标函数，同时保存了返回地址。&lt;/p&gt;
&lt;h2 id="嵌套调用如何保护lr"&gt;嵌套调用如何保护LR
&lt;/h2&gt;&lt;p&gt;如果函数A调用函数B，&lt;code&gt;BL&lt;/code&gt;会将A的返回地址覆盖到LR中。那A原来的返回地址怎么办？&lt;/p&gt;
&lt;p&gt;答案是&lt;strong&gt;用栈保存&lt;/strong&gt;：函数入口处先&lt;code&gt;PUSH {LR}&lt;/code&gt;将当前LR压栈，函数返回时&lt;code&gt;POP {PC}&lt;/code&gt;从栈中恢复返回地址并直接跳转回去。嵌套多层调用时，每层的返回地址都保存在各自的栈帧中，逐层恢复即可。&lt;/p&gt;
&lt;h2 id="栈帧"&gt;栈帧
&lt;/h2&gt;&lt;p&gt;栈帧是函数调用时在栈上分配的一块空间，函数返回时自动回收。每个栈帧包含：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;返回地址（LR）&lt;/li&gt;
&lt;li&gt;保存的寄存器值&lt;/li&gt;
&lt;li&gt;局部变量（编译器可能优化到寄存器中，除非寄存器不够用或使用了&lt;code&gt;volatile&lt;/code&gt;）&lt;/li&gt;
&lt;li&gt;超过4个的函数参数&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="栈帧的生命周期"&gt;栈帧的生命周期
&lt;/h3&gt;&lt;pre tabindex="0"&gt;&lt;code&gt; 栈空间
┌─────────────┐
调用 funcA 前 │ 空闲 │ ← SP在这里
└─────────────┘
↓ PUSH
调用 funcA 时 ┌─────────────┐
│ funcA栈帧 │ ← SP向下移动
└─────────────┘
↓ POP
funcA 返回后 ┌─────────────┐
│ 空闲 │ ← SP回到原位，栈帧&amp;#34;消失&amp;#34;
└─────────────┘
（内存数据还在，但标记为可复用）
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="嵌套调用时的栈帧变化"&gt;嵌套调用时的栈帧变化
&lt;/h3&gt;&lt;pre tabindex="0"&gt;&lt;code&gt;栈空间（从高地址向低地址增长）：
调用 a_func 时：
┌────────────────────┐ 高地址
│ main 的栈帧 │
├────────────────────┤
│ a_func 栈帧 │ ← SP（包含局部变量、保存的LR等）
└────────────────────┘ 低地址
a_func 中调用 b_func 时：
┌────────────────────┐ 高地址
│ main 的栈帧 │
├────────────────────┤
│ a_func 栈帧 │ （仍然占用，等待恢复）
├────────────────────┤
│ b_func 栈帧 │ ← SP 继续向下移动
└────────────────────┘ 低地址
b_func 返回后：
┌────────────────────┐ 高地址
│ main 的栈帧 │
├────────────────────┤
│ a_func 栈帧 │ ← SP 回到这里
├────────────────────┤
│ （空闲，可复用） │
└────────────────────┘ 低地址
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="与rtos任务切换的关系"&gt;与RTOS任务切换的关系
&lt;/h2&gt;&lt;p&gt;理解了上述概念，就能理解RTOS任务切换的本质：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;SP&lt;/strong&gt;：每个任务拥有独立的栈和SP，切换任务时保存当前SP、恢复目标任务的SP&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;LR&lt;/strong&gt;：函数调用链保存在栈中，恢复SP后调用链自然恢复&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;PC&lt;/strong&gt;：任务切换的核心就是修改PC，让CPU跳转到目标任务的代码继续执行&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;PUSH/POP&lt;/strong&gt;：任务切换时用栈保存/恢复整个任务现场（所有寄存器）&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="任务栈大小估算"&gt;任务栈大小估算
&lt;/h2&gt;&lt;p&gt;估算任务栈大小需要考虑两部分开销：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;基础开销&lt;/strong&gt;：任务切换时需要保存所有寄存器，约&lt;strong&gt;64字节&lt;/strong&gt;（16个寄存器 × 4字节）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;调用深度开销&lt;/strong&gt;：每层函数调用消耗一个栈帧（LR + 保存的寄存器 + 局部变量），调用深度越深，栈消耗越大&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;例如，调用深度为5层时：总栈大小 ≈ 64 + 5 × 平均栈帧大小。&lt;/p&gt;</description></item><item><title>初识RTOS：任务创建与内存分配</title><link>https://klinlike.github.io/posts/2026-06-02-rtos-intro-cmsis/</link><pubDate>Tue, 02 Jun 2026 19:53:41 +0800</pubDate><guid>https://klinlike.github.io/posts/2026-06-02-rtos-intro-cmsis/</guid><description>&lt;h2 id="背景为什么需要rtos"&gt;背景：为什么需要RTOS
&lt;/h2&gt;&lt;p&gt;在裸机编程中，只有一个主循环。如果想同时做两件事，就只能在一个循环里交替执行，代码耦合度高，扩展困难。&lt;/p&gt;
&lt;p&gt;RTOS（实时操作系统）将CPU时间切成小片段，轮流分配给不同任务。宏观上看，多个任务&amp;quot;同时&amp;quot;执行；微观上看，它们仍然是交替执行的。&lt;/p&gt;
&lt;h2 id="cmsis-os统一的rtos接口层"&gt;CMSIS-OS：统一的RTOS接口层
&lt;/h2&gt;&lt;p&gt;不同的RTOS有不同的API：FreeRTOS用&lt;code&gt;xTaskCreate&lt;/code&gt;，RT-Thread用&lt;code&gt;rt_thread_create&lt;/code&gt;。如果代码直接使用某个RTOS的原生API，迁移到另一个RTOS时就需要改动所有调用，维护成本很高。&lt;/p&gt;
&lt;p&gt;为此，ARM提供了&lt;strong&gt;CMSIS-OS&lt;/strong&gt;——一套中间抽象层，对上提供统一接口，对下适配不同RTOS：&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;┌─────────────────────────────────┐
│ 你的应用程序代码 │
│ osThreadNew() 统一接口 │
├─────────────────────────────────┤
│ CMSIS-OS 封装层 │
│ 自动适配底层 RTOS API │
├─────────────────────────────────┤
│ FreeRTOS │ RT-Thread │ uC/OS │
│ xTaskCreate│rt_thread_ │OSTask_ │
│ │ create │ Create │
└─────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;使用CMSIS-OS创建任务的代码如下：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;"&gt;&lt;tr&gt;&lt;td style="vertical-align:top;padding:0;margin:0;border:0;"&gt;
&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;1
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%"&gt;
&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-c" data-lang="c"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;osThreadId_t&lt;/span&gt; taskHandle;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;taskHandle &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;osThreadNew&lt;/span&gt;(TaskA, NULL, &lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt;task_attributes);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;CMSIS-OS通过条件编译来决定底层调用哪个RTOS的API，例如：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;"&gt;&lt;tr&gt;&lt;td style="vertical-align:top;padding:0;margin:0;border:0;"&gt;
&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%"&gt;
&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-c" data-lang="c"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;#define USE_FREERTOS 1
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;在CubeMX中创建工程时，可以在&lt;strong&gt;Middleware&lt;/strong&gt;配置中选择FreeRTOS，CubeMX会自动完成CMSIS-OS的适配工作。&lt;/p&gt;
&lt;h2 id="动态分配与静态分配"&gt;动态分配与静态分配
&lt;/h2&gt;&lt;p&gt;动态分配和静态分配指的是：创建任务时，任务所需的内存（&lt;strong&gt;栈空间&lt;/strong&gt;和&lt;strong&gt;任务控制块TCB&lt;/strong&gt;）从哪里来。&lt;/p&gt;
&lt;h3 id="动态分配"&gt;动态分配
&lt;/h3&gt;&lt;p&gt;系统在运行时从**堆（Heap）**中自动分配内存。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;优点&lt;/strong&gt;：使用简单，不需要预先计算内存大小，适合原型开发&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;缺点&lt;/strong&gt;：堆内存不足时创建可能失败，存在堆碎片化风险，稳定性稍差&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="静态分配"&gt;静态分配
&lt;/h3&gt;&lt;p&gt;用户在代码中预先定义好内存空间，创建任务时传入这些预分配的内存地址。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;优点&lt;/strong&gt;：内存来源确定，不依赖堆，绝对可靠&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;缺点&lt;/strong&gt;：需要预先计算内存大小，代码更复杂&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;静态分配更适合对确定性要求高的场景，如医疗设备、航空航天系统。&lt;/p&gt;</description></item></channel></rss>