<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>嵌入式 on Tech Snippets - 嵌入式技术笔记</title><link>https://tech-snippets.xyz/categories/%E5%B5%8C%E5%85%A5%E5%BC%8F/</link><description>Recent content in 嵌入式 on Tech Snippets - 嵌入式技术笔记</description><generator>Hugo</generator><language>zh-cn</language><lastBuildDate>Wed, 23 Sep 2026 22:05:25 +0800</lastBuildDate><atom:link href="https://tech-snippets.xyz/categories/%E5%B5%8C%E5%85%A5%E5%BC%8F/index.xml" rel="self" type="application/rss+xml"/><item><title>ESP32-S3 四种执行方式效率对比：原生 / WebAssembly / Lua/Micropython 的 1000 位圆周率对决</title><link>https://tech-snippets.xyz/posts/esp32s3-native-wasm-lua-pi-benchmark-2026/</link><pubDate>Wed, 23 Sep 2026 22:05:25 +0800</pubDate><guid>https://tech-snippets.xyz/posts/esp32s3-native-wasm-lua-pi-benchmark-2026/</guid><description>在同一台 ESP32-S3 上用同一份 Rabinowitz-Wagon spigot 算法，实测原生动态模块、WebAssembly、Lua 与 MicroPython 四种执行方式计算 1000 位圆周率的性能差距。</description><content:encoded><![CDATA[<h2 id="为什么会有这个对比">为什么会有这个对比</h2>
<p>最近在研究ESP32S3的基础系统和应用分离的开发架构，比较常见的应用层主要有这几种&quot;形态&quot;：</p>
<table>
<thead>
<tr>
<th>形态</th>
<th>本质</th>
<th>典型用途</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>原生动态模块(elf)</strong></td>
<td>编译成目标芯片机器码</td>
<td>性能热区、协议栈、编解码</td>
</tr>
<tr>
<td><strong>WebAssembly 应用(wasm)</strong></td>
<td>编译成字节码，由运行时解释执行</td>
<td>沙箱隔离、第三方维护、生态复用</td>
</tr>
<tr>
<td><strong>Lua 脚本(lua)</strong></td>
<td>直接解释源码</td>
<td>热更新、胶水逻辑、低频控制</td>
</tr>
<tr>
<td><strong>Micropython 脚本(py)</strong></td>
<td>直接解释源码</td>
<td>与lua一致</td>
</tr>
</tbody>
</table>
<p>它们共用一个应用包 / 部署 / 加载框架，但运行性能差多少，很少有人用同一把尺子量过。本文就用一个最经典的基准（计算 1000 位圆周率）在同一个设备上给四种形态做了一次公平对决。</p>
<h2 id="测试怎么做到公平">测试怎么做到公平</h2>
<ul>
<li><strong>同一台设备</strong>：ESP32-S3 R8（Xtensa内核，主频 240 MHz，8MB PSRAM, 16MB Flash）</li>
<li><strong>同一种运行环境</strong>：应用都加载到PSRAM执行</li>
<li><strong>同一个算法</strong>：四个实现是同一份 Rabinowitz-Wagon spigot 算法的直译</li>
<li><strong>同一位数</strong>：都算 1000 位，输出结果逐位一致（小数位完全相同）</li>
<li><strong>同一计时口径</strong>：只统计纯计算耗时，不含加载、初始化、日志输出</li>
</ul>
<p>除了micropython直接在官方固件上运行，其他四种测试脚本各自封装成应用包，由设备启动计划自动拉起，开机即跑、跑完即退，互不干扰。</p>
<h2 id="使用的算法rabinowitz-wagon-spigot">使用的算法：Rabinowitz-Wagon spigot</h2>
<p>&ldquo;spigot&rdquo;（水龙头）算法是一类逐位生成 π 的算法，像水龙头一样每拧一下流出一位数字。Rabinowitz-Wagon 是其中最经典的一种：用一组长整数数组做&quot;长除法流水线&quot;，每轮归约出一位。</p>
<p>伪代码（以 1 为下标）：</p>
<pre tabindex="0"><code>输入 n（要生成的位数）
len = floor(10 * n / 3)
a[1..len] 全部初始化为 2
nines = 0, predigit = 0

对 j = 1..n：
    q = 0
    从 i = len 递减到 1：
        x = 10 * a[i] + q * i
        d = 2 * i - 1
        a[i] = x mod d
        q = floor(x / d)
    a[1] = q mod 10
    q = floor(q / 10)

    如果 q == 9：nines = nines + 1
    否则如果 q == 10：
        输出 (predigit + 1)
        连续输出 nines 个 0
        predigit = 0, nines = 0
    否则：
        输出 predigit（第一轮跳过）
        predigit = q
        连续输出 nines 个 9
        nines = 0
最后输出 predigit
</code></pre><blockquote>
<p>技巧：<strong>多算 3 位再截断</strong>。spigot 算法在出现 9 传播时可能&quot;欠一位&quot;，多算几位再丢弃，能保证前 1000 位绝对正确。四个实现都按 <code>n = 1003</code> 计算、取前 1000 位。</p>
</blockquote>
<p>这段算法逻辑很简单，但恰好是纯整数密集计算：没有浮点、没有内存分配热点（只有一组长数组）、没有外部调用，最能拉开&quot;解释执行 vs 机器码&quot;的差距。</p>
<h2 id="实测结果">实测结果</h2>
<table>
<thead>
<tr>
<th>形态</th>
<th>执行方式</th>
<th>载荷大小</th>
<th>1000 位 π 耗时</th>
<th>相对原生</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>原生动态模块</strong></td>
<td>Xtensa 机器码</td>
<td>约 2.2 KB</td>
<td><strong>427 ms</strong></td>
<td>1×</td>
</tr>
<tr>
<td><strong>WebAssembly 应用（AOT）</strong></td>
<td>Xtensa 机器码（AOT 编译）</td>
<td>约 14 KB</td>
<td><strong>673 ms</strong></td>
<td><strong>1.58×</strong></td>
</tr>
<tr>
<td><strong>WebAssembly 应用（解释）</strong></td>
<td>WAMR 解释器跑字节码</td>
<td>约 14 KB</td>
<td>4863 ms</td>
<td><strong>11.4×</strong></td>
</tr>
<tr>
<td><strong>Lua 脚本（朴素写法）</strong></td>
<td>Lua 解释器跑源码</td>
<td>约 2 KB</td>
<td>27283 ms</td>
<td><strong>63.9×</strong></td>
</tr>
<tr>
<td><strong>Lua 脚本（整数优化写法）</strong></td>
<td>Lua 解释器跑源码</td>
<td>约 2 KB</td>
<td>15703 ms</td>
<td><strong>36.8×</strong></td>
</tr>
<tr>
<td><strong>MicroPython 脚本</strong></td>
<td>官方MicroPython 解释器跑源码</td>
<td>约 2 KB</td>
<td>28247 ms</td>
<td><strong>66.2×</strong></td>
</tr>
</tbody>
</table>
<p>结果不意外：解释执行比原生慢一到两个数量级。</p>
<p><strong>版本与配置</strong>：</p>
<table>
<thead>
<tr>
<th>运行时</th>
<th>版本</th>
<th>配置</th>
</tr>
</thead>
<tbody>
<tr>
<td>MicroPython</td>
<td>v1.29.0</td>
<td>官方固件，Octal-SPIRAM 板</td>
</tr>
<tr>
<td>Lua</td>
<td>5.5</td>
<td>设备侧 <code>LUA_32BITS=1</code></td>
</tr>
<tr>
<td>WAMR</td>
<td>v2.4.0</td>
<td>fast-interp / AOT（PC 侧 Xtensa-LLVM 工具链 + 设备端 AOT 运行时）</td>
</tr>
</tbody>
</table>
<p>均跑在同一块 ESP32-S3 @ 240 MHz。五个实现为同一份 spigot 算法的直译（MicroPython 和 Lua 整数优化版同样用了整数写法 <code>//</code> 与列表预分配），输出逐位一致。</p>
<h2 id="为什么差这么多">为什么差这么多</h2>
<h3 id="1-原生机器码直接跑1">1. 原生：机器码直接跑（1×）</h3>
<p>编译期就把算法翻译成 Xtensa 指令，运行时 CPU 直接执行，没有中间层。寄存器和内存访问都是最优的，427 ms 是这台芯片在 <code>-O2</code> 下的&quot;物理上限&quot;。</p>
<h3 id="2-webassembly字节码--解释器114">2. WebAssembly：字节码 + 解释器（11.4×）</h3>
<p>wasm 应用先被编译成<strong>静态类型的字节码</strong>，运行时由 WAMR（WebAssembly Micro Runtime）逐条解释执行：</p>
<ul>
<li>类型在编译期就定死了（<code>i32</code> 就是 <code>i32</code>），解释器不需要做动态类型判断，可以生成相对紧凑的解释循环（fast-interp）；</li>
<li>但仍要：取指令 → 查操作码分派 → 按语义执行 → 更新程序计数器，每条指令都有固定开销；</li>
<li>数组是一块连续线性内存，下标访问接近原生（这是 wasm 能&quot;只慢 11 倍&quot;而不是更慢的关键）。</li>
</ul>
<h3 id="3-lua源码--动态表639">3. Lua：源码 + 动态表（63.9×）</h3>
<p>Lua 解释器直接读源码逐条执行，负载比较重：</p>
<ul>
<li>每次 <code>a[i]</code> 都要查表：<code>a</code> 是 table（哈希表 / 数组混合结构），下标访问要走一遍结构判断与可能的重哈希；</li>
<li><strong>每个数字都是对象</strong>：<code>10 * a[i] + q * i</code> 里的每个中间量都要新建/回收 number 对象（即便走整型路径也有装箱开销）；</li>
<li>解释循环本身比 wasm 的解释器更厚：操作数要逐字段解析，函数调用要过调用栈与闭包检查。</li>
</ul>
<p>三重叠加，于是 Lua 是 wasm 的 3.2 倍慢，是原生的 37（整数优化写法）~64 倍（朴素写法）。</p>
<h3 id="4-micropython全对象--分代-gc662">4. MicroPython：全对象 + 分代 GC（66.2×）</h3>
<p>MicroPython 与 Lua 同为&quot;源码级解释 + 动态类型&quot;，但对象模型更重：</p>
<ul>
<li><strong>一切都是对象</strong>：整数、列表元素都是堆上的 <code>mp_obj_t</code>，每次算术都要经过对象头 + 类型分派（<code>mp_binary_op</code>），没有 Lua 那种&quot;标签值 + 寄存器 VM&quot;的轻量整数路径；</li>
<li>列表下标访问是完整方法调用链：<code>a[i-1]</code> 要过 <code>mp_obj_subscr</code> → 列表类型分派 → 索引检查，比 Lua 的 <code>luaV_fastgeti</code> 宏展开重得多；</li>
<li><strong>GC 压力</strong>：<code>[2] * LEN</code> 和 <code>res.append()</code> 产生大量堆对象，分代 GC 周期性回收，纯计算循环也躲不开。</li>
</ul>
<p>于是 MicroPython 比优化后的 Lua 还慢 <strong>1.8 倍</strong>，与朴素写法的 Lua 基本打平。</p>
<blockquote>
<p>白话解释：原生是&quot;把答案写死在芯片里&quot;，wasm 是&quot;边翻字典边念&quot;，Lua 是&quot;一边查字典一边还要确认每个字的意思&quot;，MicroPython 是&quot;每个字都要拆开看是哪国语言再翻译&quot;。</p>
</blockquote>
<h2 id="那么aot-值得做吗">那么，AOT 值得做吗？</h2>
<ul>
<li>AOT 把解释器的 4863 ms 压到 <strong>673 ms（1.58×）</strong>，比原先估的&quot;顶多压到 1 秒出头&quot;还好，纯计算场景已接近原生；</li>
<li>但 wasm 应用里真正花时间的往往不是模块内计算，而是<strong>宿主桥调用</strong>（<code>bs_log</code>、共享内存、文件 IO），这些跨边界调用 AOT 一点都加速不了；</li>
<li>代价是工具链：需要 PC 侧 Xtensa-LLVM 交叉编译链 + 设备端 AOT 运行时（官方 LLVM 没有该后端），这是一条长期维护线。</li>
</ul>
<p>结论：性能热区直接写原生 <code>.so</code>。wasm 的取舍点在于沙箱 / 第三方维护 / 生态复用，而不是性能。</p>
<h2 id="选型原则一句话版">选型原则（一句话版）</h2>
<table>
<thead>
<tr>
<th>需求</th>
<th>选谁</th>
</tr>
</thead>
<tbody>
<tr>
<td>每周期执行的算法 / 协议解析 / 编解码</td>
<td><strong>原生动态模块</strong>（10~60 倍差距不是优化能补的）</td>
</tr>
<tr>
<td>沙箱隔离 / 第三方维护 / 复用 wasm 生态</td>
<td>WebAssembly（解释 ~11 倍，AOT 后可到 ~1.6 倍）</td>
</tr>
<tr>
<td>热更新脚本 / 胶水逻辑 / 低频控制</td>
<td>Lua 或 MicroPython（接受 ~40~70 倍代价）</td>
</tr>
</tbody>
</table>
<h2 id="附录参考实现与复现方法">附录：参考实现与复现方法</h2>
<blockquote>
<p>为便于读者复现，这里贴出算法核心（<code>pi_digits</code> / <code>pi_spigot</code>），各语言代码都是设备上实际运行的<strong>完整</strong>实现。</p>
<p>文中出现的 <code>bs_*</code>、<code>esp_timer_get_time()</code>、<code>sys.millis()</code>、<code>time.ticks_ms()</code> 都是设备侧的
<strong>宿主接口</strong>（日志 / 计时 / 输出），换成你平台上的等价物即可；<code>malloc</code>/<code>free</code> 在
wasm 版里接的是宿主堆，桌面运行时（如 wasmtime）下可直接用标准分配器。</p>
</blockquote>
<h3 id="复现方法">复现方法</h3>
<ul>
<li>四份实现均为 Rabinowitz-Wagon spigot 的直译，唯一差异是语言语法，无任何算法级改动；</li>
<li>计时只包裹主循环，排除字符串拼接与日志；</li>
<li>结果用权威 π 前 100 位核对一致后，再对比四位输出全等。</li>
</ul>
<h3 id="附录-a原生-c-实现">附录 A：原生 C 实现</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="cm">/*
</span></span></span><span class="line"><span class="cl"><span class="cm"> * 原生 C 模块计算 1000 位圆周率（Rabinowitz-Wagon spigot 算法）
</span></span></span><span class="line"><span class="cl"><span class="cm"> *
</span></span></span><span class="line"><span class="cl"><span class="cm"> * 计时用 esp_timer_get_time()（微秒），仅包裹纯计算主循环。
</span></span></span><span class="line"><span class="cl"><span class="cm"> */</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;stdint.h&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;stdlib.h&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;string.h&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&#34;esp_log.h&#34;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&#34;esp_timer.h&#34;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">TAG</span> <span class="o">=</span> <span class="s">&#34;pi1000_c&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="cm">/* Rabinowitz-Wagon spigot：逐位生成 pi 的数字（res[0] = 整数位 3） */</span>
</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">uint32_t</span> <span class="nf">pi_digits</span><span class="p">(</span><span class="kt">uint32_t</span> <span class="n">n</span><span class="p">,</span> <span class="kt">uint8_t</span> <span class="o">*</span><span class="n">res</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">uint32_t</span> <span class="n">len</span> <span class="o">=</span> <span class="mi">10</span> <span class="o">*</span> <span class="n">n</span> <span class="o">/</span> <span class="mi">3</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">uint32_t</span> <span class="o">*</span><span class="n">a</span> <span class="o">=</span> <span class="p">(</span><span class="kt">uint32_t</span> <span class="o">*</span><span class="p">)</span><span class="nf">malloc</span><span class="p">(</span><span class="n">len</span> <span class="o">*</span> <span class="k">sizeof</span><span class="p">(</span><span class="kt">uint32_t</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">a</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="p">(</span><span class="kt">uint32_t</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">len</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">a</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kt">uint32_t</span> <span class="n">nines</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span> <span class="n">predigit</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">uint32_t</span> <span class="n">r</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="p">(</span><span class="kt">uint32_t</span> <span class="n">j</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> <span class="n">j</span> <span class="o">&lt;=</span> <span class="n">n</span><span class="p">;</span> <span class="n">j</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="kt">uint32_t</span> <span class="n">q</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">for</span> <span class="p">(</span><span class="kt">uint32_t</span> <span class="n">i</span> <span class="o">=</span> <span class="n">len</span><span class="p">;</span> <span class="n">i</span> <span class="o">&gt;=</span> <span class="mi">1</span><span class="p">;</span> <span class="n">i</span><span class="o">--</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="kt">uint32_t</span> <span class="n">x</span> <span class="o">=</span> <span class="mi">10</span> <span class="o">*</span> <span class="n">a</span><span class="p">[</span><span class="n">i</span> <span class="o">-</span> <span class="mi">1</span><span class="p">]</span> <span class="o">+</span> <span class="n">q</span> <span class="o">*</span> <span class="n">i</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="kt">uint32_t</span> <span class="n">d</span> <span class="o">=</span> <span class="mi">2</span> <span class="o">*</span> <span class="n">i</span> <span class="o">-</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="n">a</span><span class="p">[</span><span class="n">i</span> <span class="o">-</span> <span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="n">x</span> <span class="o">%</span> <span class="n">d</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="n">q</span> <span class="o">=</span> <span class="n">x</span> <span class="o">/</span> <span class="n">d</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="n">a</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="n">q</span> <span class="o">%</span> <span class="mi">10</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">q</span> <span class="o">=</span> <span class="n">q</span> <span class="o">/</span> <span class="mi">10</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">q</span> <span class="o">==</span> <span class="mi">9</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">nines</span><span class="o">++</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">q</span> <span class="o">==</span> <span class="mi">10</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">res</span><span class="p">[</span><span class="n">r</span><span class="o">++</span><span class="p">]</span> <span class="o">=</span> <span class="p">(</span><span class="kt">uint8_t</span><span class="p">)(</span><span class="n">predigit</span> <span class="o">+</span> <span class="mi">1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="k">for</span> <span class="p">(</span><span class="kt">uint32_t</span> <span class="n">k</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">k</span> <span class="o">&lt;</span> <span class="n">nines</span><span class="p">;</span> <span class="n">k</span><span class="o">++</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                <span class="n">res</span><span class="p">[</span><span class="n">r</span><span class="o">++</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="n">predigit</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="n">nines</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">if</span> <span class="p">(</span><span class="n">j</span> <span class="o">&gt;</span> <span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                <span class="n">res</span><span class="p">[</span><span class="n">r</span><span class="o">++</span><span class="p">]</span> <span class="o">=</span> <span class="p">(</span><span class="kt">uint8_t</span><span class="p">)</span><span class="n">predigit</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="n">predigit</span> <span class="o">=</span> <span class="n">q</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="k">if</span> <span class="p">(</span><span class="n">nines</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="k">for</span> <span class="p">(</span><span class="kt">uint32_t</span> <span class="n">k</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">k</span> <span class="o">&lt;</span> <span class="n">nines</span><span class="p">;</span> <span class="n">k</span><span class="o">++</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                    <span class="n">res</span><span class="p">[</span><span class="n">r</span><span class="o">++</span><span class="p">]</span> <span class="o">=</span> <span class="mi">9</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">                <span class="n">nines</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="n">res</span><span class="p">[</span><span class="n">r</span><span class="o">++</span><span class="p">]</span> <span class="o">=</span> <span class="p">(</span><span class="kt">uint8_t</span><span class="p">)</span><span class="n">predigit</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nf">free</span><span class="p">(</span><span class="n">a</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">r</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">__attribute__</span><span class="p">((</span><span class="nf">visibility</span><span class="p">(</span><span class="s">&#34;default&#34;</span><span class="p">)))</span>
</span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">app_main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">const</span> <span class="kt">uint32_t</span> <span class="n">DIGITS</span> <span class="o">=</span> <span class="mi">1000</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="cm">/* 多算 3 个，截断取前 1000 位 */</span>
</span></span><span class="line"><span class="cl">    <span class="k">const</span> <span class="kt">uint32_t</span> <span class="n">N</span> <span class="o">=</span> <span class="n">DIGITS</span> <span class="o">+</span> <span class="mi">3</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">uint8_t</span> <span class="o">*</span><span class="n">digits</span> <span class="o">=</span> <span class="p">(</span><span class="kt">uint8_t</span> <span class="o">*</span><span class="p">)</span><span class="nf">malloc</span><span class="p">((</span><span class="n">N</span> <span class="o">+</span> <span class="mi">8</span><span class="p">)</span> <span class="o">*</span> <span class="k">sizeof</span><span class="p">(</span><span class="kt">uint8_t</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kt">int64_t</span> <span class="n">t0</span> <span class="o">=</span> <span class="nf">esp_timer_get_time</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="kt">uint32_t</span> <span class="n">nd</span> <span class="o">=</span> <span class="nf">pi_digits</span><span class="p">(</span><span class="n">N</span><span class="p">,</span> <span class="n">digits</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int64_t</span> <span class="n">dt</span> <span class="o">=</span> <span class="nf">esp_timer_get_time</span><span class="p">()</span> <span class="o">-</span> <span class="n">t0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">digits</span> <span class="o">||</span> <span class="n">nd</span> <span class="o">&lt;</span> <span class="n">DIGITS</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nf">ESP_LOGE</span><span class="p">(</span><span class="n">TAG</span><span class="p">,</span> <span class="s">&#34;pi1000_c: failed (nd=%u)&#34;</span><span class="p">,</span> <span class="n">nd</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="nf">free</span><span class="p">(</span><span class="n">digits</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="cm">/* 拼接：整数位 3 + 1000 位小数（digits[0] = 3） */</span>
</span></span><span class="line"><span class="cl">    <span class="kt">char</span> <span class="o">*</span><span class="n">pi</span> <span class="o">=</span> <span class="p">(</span><span class="kt">char</span> <span class="o">*</span><span class="p">)</span><span class="nf">malloc</span><span class="p">(</span><span class="n">DIGITS</span> <span class="o">+</span> <span class="mi">8</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">pi</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="kt">int</span> <span class="n">o</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">pi</span><span class="p">[</span><span class="n">o</span><span class="o">++</span><span class="p">]</span> <span class="o">=</span> <span class="sc">&#39;3&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">pi</span><span class="p">[</span><span class="n">o</span><span class="o">++</span><span class="p">]</span> <span class="o">=</span> <span class="sc">&#39;.&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">for</span> <span class="p">(</span><span class="kt">uint32_t</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;=</span> <span class="n">DIGITS</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="n">pi</span><span class="p">[</span><span class="n">o</span><span class="o">++</span><span class="p">]</span> <span class="o">=</span> <span class="p">(</span><span class="kt">char</span><span class="p">)(</span><span class="sc">&#39;0&#39;</span> <span class="o">+</span> <span class="n">digits</span><span class="p">[</span><span class="n">i</span><span class="p">]);</span>
</span></span><span class="line"><span class="cl">        <span class="n">pi</span><span class="p">[</span><span class="n">o</span><span class="p">]</span> <span class="o">=</span> <span class="sc">&#39;\0&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="nf">ESP_LOGI</span><span class="p">(</span><span class="n">TAG</span><span class="p">,</span> <span class="s">&#34;%s&#34;</span><span class="p">,</span> <span class="n">pi</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="nf">free</span><span class="p">(</span><span class="n">pi</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nf">ESP_LOGI</span><span class="p">(</span><span class="n">TAG</span><span class="p">,</span> <span class="s">&#34;pi1000_c: %u digits in %lld ms&#34;</span><span class="p">,</span> <span class="n">DIGITS</span><span class="p">,</span> <span class="p">(</span><span class="kt">long</span> <span class="kt">long</span><span class="p">)(</span><span class="n">dt</span> <span class="o">/</span> <span class="mi">1000</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="nf">ESP_LOGI</span><span class="p">(</span><span class="n">TAG</span><span class="p">,</span> <span class="s">&#34;pi1000_c: done&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nf">free</span><span class="p">(</span><span class="n">digits</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="附录-bwebassemblyrust实现">附录 B：WebAssembly（Rust）实现</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="sd">//! wasm 应用计算 1000 位圆周率（Rabinowitz-Wagon spigot 算法）
</span></span></span><span class="line"><span class="cl"><span class="sd">//!
</span></span></span><span class="line"><span class="cl"><span class="sd">//! 计时用宿主提供的 bs_uptime（微秒），仅包裹纯计算主循环。
</span></span></span><span class="line"><span class="cl"><span class="sd"></span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="cp">#![no_std]</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="cp">#![no_main]</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">extern</span><span class="w"> </span><span class="k">crate</span><span class="w"> </span><span class="n">alloc</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">use</span><span class="w"> </span><span class="n">alloc</span>::<span class="p">{</span><span class="n">format</span><span class="p">,</span><span class="w"> </span><span class="n">string</span>::<span class="nb">String</span><span class="p">,</span><span class="w"> </span><span class="n">vec</span><span class="p">,</span><span class="w"> </span><span class="n">vec</span>::<span class="nb">Vec</span><span class="p">};</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">use</span><span class="w"> </span><span class="n">core</span>::<span class="n">panic</span>::<span class="n">PanicInfo</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="cm">/* ---------------- 宿主 import（env 模块） ---------------- */</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">extern</span><span class="w"> </span><span class="s">&#34;C&#34;</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">fn</span> <span class="nf">bs_log</span><span class="p">(</span><span class="n">json</span>: <span class="o">*</span><span class="k">const</span><span class="w"> </span><span class="kt">u8</span><span class="p">,</span><span class="w"> </span><span class="n">len</span>: <span class="kt">i32</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="kt">i32</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">fn</span> <span class="nf">bs_result_len</span><span class="p">(</span><span class="n">id</span>: <span class="kt">i32</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="kt">i32</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">fn</span> <span class="nf">bs_result_copy</span><span class="p">(</span><span class="n">id</span>: <span class="kt">i32</span><span class="p">,</span><span class="w"> </span><span class="n">out</span>: <span class="o">*</span><span class="k">mut</span><span class="w"> </span><span class="kt">u8</span><span class="p">,</span><span class="w"> </span><span class="n">len</span>: <span class="kt">i32</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="kt">i32</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">fn</span> <span class="nf">bs_result_free</span><span class="p">(</span><span class="n">id</span>: <span class="kt">i32</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">fn</span> <span class="nf">bs_delay_ms</span><span class="p">(</span><span class="n">ms</span>: <span class="kt">i32</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">fn</span> <span class="nf">bs_should_stop</span><span class="p">()</span><span class="w"> </span>-&gt; <span class="kt">i32</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">fn</span> <span class="nf">bs_uptime</span><span class="p">(</span><span class="n">j</span>: <span class="o">*</span><span class="k">const</span><span class="w"> </span><span class="kt">u8</span><span class="p">,</span><span class="w"> </span><span class="n">l</span>: <span class="kt">i32</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="kt">i32</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">fn</span> <span class="nf">malloc</span><span class="p">(</span><span class="n">size</span>: <span class="kt">usize</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="o">*</span><span class="k">mut</span><span class="w"> </span><span class="kt">u8</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">fn</span> <span class="nf">free</span><span class="p">(</span><span class="n">p</span>: <span class="o">*</span><span class="k">mut</span><span class="w"> </span><span class="kt">u8</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="cm">/* ---------------- 分配器：接宿主堆 ---------------- */</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">struct</span> <span class="nc">HostAlloc</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">unsafe</span><span class="w"> </span><span class="k">impl</span><span class="w"> </span><span class="n">core</span>::<span class="n">alloc</span>::<span class="n">GlobalAlloc</span><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="n">HostAlloc</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">unsafe</span><span class="w"> </span><span class="k">fn</span> <span class="nf">alloc</span><span class="p">(</span><span class="o">&amp;</span><span class="bp">self</span><span class="p">,</span><span class="w"> </span><span class="n">layout</span>: <span class="nc">core</span>::<span class="n">alloc</span>::<span class="n">Layout</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="o">*</span><span class="k">mut</span><span class="w"> </span><span class="kt">u8</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="n">malloc</span><span class="p">(</span><span class="n">layout</span><span class="p">.</span><span class="n">size</span><span class="p">())</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">unsafe</span><span class="w"> </span><span class="k">fn</span> <span class="nf">dealloc</span><span class="p">(</span><span class="o">&amp;</span><span class="bp">self</span><span class="p">,</span><span class="w"> </span><span class="n">ptr</span>: <span class="o">*</span><span class="k">mut</span><span class="w"> </span><span class="kt">u8</span><span class="p">,</span><span class="w"> </span><span class="n">_</span>: <span class="nc">core</span>::<span class="n">alloc</span>::<span class="n">Layout</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="n">free</span><span class="p">(</span><span class="n">ptr</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="cp">#[global_allocator]</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">static</span><span class="w"> </span><span class="no">HOST_ALLOC</span>: <span class="nc">HostAlloc</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">HostAlloc</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="cm">/* ---------------- 工具 ---------------- */</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">fn</span> <span class="nf">log_msg</span><span class="p">(</span><span class="n">msg</span>: <span class="kp">&amp;</span><span class="kt">str</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kd">let</span><span class="w"> </span><span class="k">mut</span><span class="w"> </span><span class="n">esc</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nb">String</span>::<span class="n">with_capacity</span><span class="p">(</span><span class="n">msg</span><span class="p">.</span><span class="n">len</span><span class="p">()</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="mi">8</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">for</span><span class="w"> </span><span class="n">c</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="n">msg</span><span class="p">.</span><span class="n">chars</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="k">if</span><span class="w"> </span><span class="n">c</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="sc">&#39;&#34;&#39;</span><span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="n">c</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="sc">&#39;\\&#39;</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="n">esc</span><span class="p">.</span><span class="n">push</span><span class="p">(</span><span class="sc">&#39;\\&#39;</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="n">esc</span><span class="p">.</span><span class="n">push</span><span class="p">(</span><span class="n">c</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kd">let</span><span class="w"> </span><span class="n">json</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="fm">format!</span><span class="p">(</span><span class="s">&#34;</span><span class="se">{{\&#34;</span><span class="s">level</span><span class="se">\&#34;</span><span class="s">:</span><span class="se">\&#34;</span><span class="s">i</span><span class="se">\&#34;</span><span class="s">,</span><span class="se">\&#34;</span><span class="s">tag</span><span class="se">\&#34;</span><span class="s">:</span><span class="se">\&#34;</span><span class="s">pi1000_rust</span><span class="se">\&#34;</span><span class="s">,</span><span class="se">\&#34;</span><span class="s">msg</span><span class="se">\&#34;</span><span class="s">:</span><span class="se">\&#34;</span><span class="si">{}</span><span class="se">\&#34;}}</span><span class="s">&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">esc</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">unsafe</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="kd">let</span><span class="w"> </span><span class="n">id</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">bs_log</span><span class="p">(</span><span class="n">json</span><span class="p">.</span><span class="n">as_ptr</span><span class="p">(),</span><span class="w"> </span><span class="n">json</span><span class="p">.</span><span class="n">len</span><span class="p">()</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="kt">i32</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="n">bs_result_free</span><span class="p">(</span><span class="n">id</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="sd">/// 调 bs_uptime 返回开机微秒数（解析 {&#34;ok&#34;:true,&#34;us&#34;:N}）。
</span></span></span><span class="line"><span class="cl"><span class="sd"></span><span class="k">unsafe</span><span class="w"> </span><span class="k">fn</span> <span class="nf">uptime_us</span><span class="p">()</span><span class="w"> </span>-&gt; <span class="kt">u64</span> <span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kd">let</span><span class="w"> </span><span class="n">id</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">bs_uptime</span><span class="p">(</span><span class="sa">b</span><span class="s">&#34;{}&#34;</span><span class="p">.</span><span class="n">as_ptr</span><span class="p">(),</span><span class="w"> </span><span class="mi">2</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">if</span><span class="w"> </span><span class="n">id</span><span class="w"> </span><span class="o">&lt;</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kd">let</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">bs_result_len</span><span class="p">(</span><span class="n">id</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kd">let</span><span class="w"> </span><span class="k">mut</span><span class="w"> </span><span class="n">buf</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="fm">vec!</span><span class="p">[</span><span class="mi">0</span><span class="k">u8</span><span class="p">;</span><span class="w"> </span><span class="n">n</span><span class="p">.</span><span class="n">max</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="kt">usize</span><span class="p">];</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">if</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="o">&gt;</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="n">bs_result_copy</span><span class="p">(</span><span class="n">id</span><span class="p">,</span><span class="w"> </span><span class="n">buf</span><span class="p">.</span><span class="n">as_mut_ptr</span><span class="p">(),</span><span class="w"> </span><span class="n">n</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">bs_result_free</span><span class="p">(</span><span class="n">id</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kd">let</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">core</span>::<span class="kt">str</span>::<span class="n">from_utf8</span><span class="p">(</span><span class="o">&amp;</span><span class="n">buf</span><span class="p">).</span><span class="n">unwrap_or</span><span class="p">(</span><span class="s">&#34;&#34;</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kd">let</span><span class="w"> </span><span class="n">key</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">&#34;</span><span class="se">\&#34;</span><span class="s">us</span><span class="se">\&#34;</span><span class="s">:&#34;</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">if</span><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="nb">Some</span><span class="p">(</span><span class="n">i</span><span class="p">)</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">s</span><span class="p">.</span><span class="n">find</span><span class="p">(</span><span class="n">key</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="kd">let</span><span class="w"> </span><span class="n">rest</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="o">&amp;</span><span class="n">s</span><span class="p">[</span><span class="n">i</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">key</span><span class="p">.</span><span class="n">len</span><span class="p">()</span><span class="o">..</span><span class="p">];</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="kd">let</span><span class="w"> </span><span class="n">digits</span>: <span class="nb">String</span> <span class="o">=</span><span class="w"> </span><span class="n">rest</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="p">.</span><span class="n">chars</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="p">.</span><span class="n">take_while</span><span class="p">(</span><span class="o">|</span><span class="n">c</span><span class="o">|</span><span class="w"> </span><span class="n">c</span><span class="p">.</span><span class="n">is_ascii_digit</span><span class="p">())</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="p">.</span><span class="n">collect</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="k">if</span><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="nb">Ok</span><span class="p">(</span><span class="n">v</span><span class="p">)</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">digits</span><span class="p">.</span><span class="n">parse</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="k">return</span><span class="w"> </span><span class="n">v</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="mi">0</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="cm">/* ---------------- spigot 算法 ---------------- */</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="sd">/// 生成 pi 的数字序列（res[0] = 整数位 3，res[1..] = 小数位）。
</span></span></span><span class="line"><span class="cl"><span class="sd"></span><span class="k">fn</span> <span class="nf">pi_digits</span><span class="p">(</span><span class="n">n</span>: <span class="kt">usize</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nb">Vec</span><span class="o">&lt;</span><span class="kt">u8</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kd">let</span><span class="w"> </span><span class="n">len</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">10</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="o">/</span><span class="w"> </span><span class="mi">3</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kd">let</span><span class="w"> </span><span class="k">mut</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="fm">vec!</span><span class="p">[</span><span class="mi">2</span><span class="k">u32</span><span class="p">;</span><span class="w"> </span><span class="n">len</span><span class="p">];</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kd">let</span><span class="w"> </span><span class="k">mut</span><span class="w"> </span><span class="n">nines</span>: <span class="kt">u32</span> <span class="o">=</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kd">let</span><span class="w"> </span><span class="k">mut</span><span class="w"> </span><span class="n">predigit</span>: <span class="kt">u32</span> <span class="o">=</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kd">let</span><span class="w"> </span><span class="k">mut</span><span class="w"> </span><span class="n">res</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nb">Vec</span>::<span class="n">with_capacity</span><span class="p">(</span><span class="n">n</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">for</span><span class="w"> </span><span class="n">j</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="mi">1</span><span class="o">..=</span><span class="n">n</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="kd">let</span><span class="w"> </span><span class="k">mut</span><span class="w"> </span><span class="n">q</span>: <span class="kt">u32</span> <span class="o">=</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="kd">let</span><span class="w"> </span><span class="k">mut</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">len</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="k">while</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="o">&gt;=</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="kd">let</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">10</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">a</span><span class="p">[</span><span class="n">i</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="mi">1</span><span class="p">]</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">q</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="p">(</span><span class="n">i</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="kt">u32</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="kd">let</span><span class="w"> </span><span class="n">d</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">(</span><span class="mi">2</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="mi">1</span><span class="p">)</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="kt">u32</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="n">a</span><span class="p">[</span><span class="n">i</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="mi">1</span><span class="p">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="o">%</span><span class="w"> </span><span class="n">d</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="n">q</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="o">/</span><span class="w"> </span><span class="n">d</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="n">i</span><span class="w"> </span><span class="o">-=</span><span class="w"> </span><span class="mi">1</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="n">a</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">q</span><span class="w"> </span><span class="o">%</span><span class="w"> </span><span class="mi">10</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="n">q</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">q</span><span class="w"> </span><span class="o">/</span><span class="w"> </span><span class="mi">10</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="k">if</span><span class="w"> </span><span class="n">q</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="mi">9</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="n">nines</span><span class="w"> </span><span class="o">+=</span><span class="w"> </span><span class="mi">1</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="n">q</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="mi">10</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="n">res</span><span class="p">.</span><span class="n">push</span><span class="p">((</span><span class="n">predigit</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="mi">1</span><span class="p">)</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="kt">u8</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="k">for</span><span class="w"> </span><span class="n">_</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="mi">0</span><span class="o">..</span><span class="n">nines</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">                </span><span class="n">res</span><span class="p">.</span><span class="n">push</span><span class="p">(</span><span class="mi">0</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="n">predigit</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="n">nines</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="k">if</span><span class="w"> </span><span class="n">j</span><span class="w"> </span><span class="o">&gt;</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">                </span><span class="n">res</span><span class="p">.</span><span class="n">push</span><span class="p">(</span><span class="n">predigit</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="kt">u8</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="n">predigit</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">q</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="k">if</span><span class="w"> </span><span class="n">nines</span><span class="w"> </span><span class="o">&gt;</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">                </span><span class="k">for</span><span class="w"> </span><span class="n">_</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="mi">0</span><span class="o">..</span><span class="n">nines</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">                    </span><span class="n">res</span><span class="p">.</span><span class="n">push</span><span class="p">(</span><span class="mi">9</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">                </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">                </span><span class="n">nines</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">res</span><span class="p">.</span><span class="n">push</span><span class="p">(</span><span class="n">predigit</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="kt">u8</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">res</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="cm">/* ---------------- main ---------------- */</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="cp">#[no_mangle]</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">pub</span><span class="w"> </span><span class="k">extern</span><span class="w"> </span><span class="s">&#34;C&#34;</span><span class="w"> </span><span class="k">fn</span> <span class="nf">main</span><span class="p">()</span><span class="w"> </span>-&gt; <span class="kt">i32</span> <span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">const</span><span class="w"> </span><span class="no">DIGITS</span>: <span class="kt">usize</span> <span class="o">=</span><span class="w"> </span><span class="mi">1000</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">unsafe</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="kd">let</span><span class="w"> </span><span class="n">t0</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">uptime_us</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="kd">let</span><span class="w"> </span><span class="n">digits</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">pi_digits</span><span class="p">(</span><span class="no">DIGITS</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="mi">3</span><span class="p">);</span><span class="w"> </span><span class="c1">// 多算几个，截断取前 1000 位
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="w">        </span><span class="kd">let</span><span class="w"> </span><span class="n">dt</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">uptime_us</span><span class="p">()</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="n">t0</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="c1">// 拼接：整数位 3 + 1000 位小数（digits[0] = 3）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="w">        </span><span class="kd">let</span><span class="w"> </span><span class="k">mut</span><span class="w"> </span><span class="n">pi</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nb">String</span>::<span class="n">with_capacity</span><span class="p">(</span><span class="no">DIGITS</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="mi">2</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="n">pi</span><span class="p">.</span><span class="n">push_str</span><span class="p">(</span><span class="s">&#34;3.&#34;</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="k">for</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="mi">1</span><span class="o">..=</span><span class="no">DIGITS</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="n">pi</span><span class="p">.</span><span class="n">push</span><span class="p">((</span><span class="sa">b</span><span class="sc">&#39;0&#39;</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">digits</span><span class="p">[</span><span class="n">i</span><span class="p">])</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="kt">char</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="n">log_msg</span><span class="p">(</span><span class="o">&amp;</span><span class="n">pi</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="n">log_msg</span><span class="p">(</span><span class="o">&amp;</span><span class="fm">format!</span><span class="p">(</span><span class="s">&#34;pi1000_rust: </span><span class="si">{}</span><span class="s"> digits in </span><span class="si">{}</span><span class="s"> ms&#34;</span><span class="p">,</span><span class="w"> </span><span class="no">DIGITS</span><span class="p">,</span><span class="w"> </span><span class="n">dt</span><span class="w"> </span><span class="o">/</span><span class="w"> </span><span class="mi">1000</span><span class="p">));</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">log_msg</span><span class="p">(</span><span class="s">&#34;pi1000_rust: done&#34;</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="mi">0</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="cp">#[panic_handler]</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">fn</span> <span class="nf">panic</span><span class="p">(</span><span class="n">_</span>: <span class="kp">&amp;</span><span class="nc">PanicInfo</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="o">!</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">core</span>::<span class="n">arch</span>::<span class="n">wasm32</span>::<span class="n">unreachable</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><h3 id="附录-clua-的朴素实现使用mathfloor算除法">附录 C：Lua 的朴素实现，使用math.floor算除法</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-lua" data-lang="lua"><span class="line"><span class="cl"><span class="c1">-- 计算 1000 位圆周率（Rabinowitz-Wagon spigot 算法）</span>
</span></span><span class="line"><span class="cl"><span class="c1">-- 参考 Simeon Simeonov 的 Pascal 原版（pi_spigot），本机已与权威前 100 位核对。</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">local</span> <span class="n">sys</span> <span class="o">=</span> <span class="n">require</span><span class="p">(</span><span class="s2">&#34;sys&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">local</span> <span class="n">DIGITS</span> <span class="o">=</span> <span class="mi">1000</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">-- Rabinowitz-Wagon spigot：逐位生成 pi 的数字（含整数位 3 在最前）。</span>
</span></span><span class="line"><span class="cl"><span class="kd">local</span> <span class="kr">function</span> <span class="nf">pi_spigot</span><span class="p">(</span><span class="n">n</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="kd">local</span> <span class="n">len</span> <span class="o">=</span> <span class="n">math.floor</span><span class="p">(</span><span class="mi">10</span> <span class="o">*</span> <span class="n">n</span> <span class="o">/</span> <span class="mi">3</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="kd">local</span> <span class="n">a</span> <span class="o">=</span> <span class="p">{}</span>
</span></span><span class="line"><span class="cl">    <span class="kr">for</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">1</span><span class="p">,</span> <span class="n">len</span> <span class="kr">do</span> <span class="n">a</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="mi">2</span> <span class="kr">end</span>
</span></span><span class="line"><span class="cl">    <span class="kd">local</span> <span class="n">nines</span><span class="p">,</span> <span class="n">predigit</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl">    <span class="kd">local</span> <span class="n">res</span> <span class="o">=</span> <span class="p">{}</span>
</span></span><span class="line"><span class="cl">    <span class="kr">for</span> <span class="n">j</span> <span class="o">=</span> <span class="mi">1</span><span class="p">,</span> <span class="n">n</span> <span class="kr">do</span>
</span></span><span class="line"><span class="cl">        <span class="kd">local</span> <span class="n">q</span> <span class="o">=</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl">        <span class="kr">for</span> <span class="n">i</span> <span class="o">=</span> <span class="n">len</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span> <span class="kr">do</span>
</span></span><span class="line"><span class="cl">            <span class="kd">local</span> <span class="n">x</span> <span class="o">=</span> <span class="mi">10</span> <span class="o">*</span> <span class="n">a</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">+</span> <span class="n">q</span> <span class="o">*</span> <span class="n">i</span>
</span></span><span class="line"><span class="cl">            <span class="kd">local</span> <span class="n">d</span> <span class="o">=</span> <span class="mi">2</span> <span class="o">*</span> <span class="n">i</span> <span class="o">-</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl">            <span class="n">a</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">x</span> <span class="o">%</span> <span class="n">d</span>
</span></span><span class="line"><span class="cl">            <span class="n">q</span> <span class="o">=</span> <span class="n">math.floor</span><span class="p">(</span><span class="n">x</span> <span class="o">/</span> <span class="n">d</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="kr">end</span>
</span></span><span class="line"><span class="cl">        <span class="n">a</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="n">q</span> <span class="o">%</span> <span class="mi">10</span>
</span></span><span class="line"><span class="cl">        <span class="n">q</span> <span class="o">=</span> <span class="n">math.floor</span><span class="p">(</span><span class="n">q</span> <span class="o">/</span> <span class="mi">10</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="kr">if</span> <span class="n">q</span> <span class="o">==</span> <span class="mi">9</span> <span class="kr">then</span>
</span></span><span class="line"><span class="cl">            <span class="n">nines</span> <span class="o">=</span> <span class="n">nines</span> <span class="o">+</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl">        <span class="kr">elseif</span> <span class="n">q</span> <span class="o">==</span> <span class="mi">10</span> <span class="kr">then</span>
</span></span><span class="line"><span class="cl">            <span class="n">res</span><span class="p">[</span><span class="o">#</span><span class="n">res</span> <span class="o">+</span> <span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="n">predigit</span> <span class="o">+</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl">            <span class="kr">for</span> <span class="n">_</span> <span class="o">=</span> <span class="mi">1</span><span class="p">,</span> <span class="n">nines</span> <span class="kr">do</span> <span class="n">res</span><span class="p">[</span><span class="o">#</span><span class="n">res</span> <span class="o">+</span> <span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span> <span class="kr">end</span>
</span></span><span class="line"><span class="cl">            <span class="n">predigit</span><span class="p">,</span> <span class="n">nines</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl">        <span class="kr">else</span>
</span></span><span class="line"><span class="cl">            <span class="kr">if</span> <span class="n">j</span> <span class="o">&gt;</span> <span class="mi">1</span> <span class="kr">then</span> <span class="n">res</span><span class="p">[</span><span class="o">#</span><span class="n">res</span> <span class="o">+</span> <span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="n">predigit</span> <span class="kr">end</span>
</span></span><span class="line"><span class="cl">            <span class="n">predigit</span> <span class="o">=</span> <span class="n">q</span>
</span></span><span class="line"><span class="cl">            <span class="kr">if</span> <span class="n">nines</span> <span class="o">&gt;</span> <span class="mi">0</span> <span class="kr">then</span>
</span></span><span class="line"><span class="cl">                <span class="kr">for</span> <span class="n">_</span> <span class="o">=</span> <span class="mi">1</span><span class="p">,</span> <span class="n">nines</span> <span class="kr">do</span> <span class="n">res</span><span class="p">[</span><span class="o">#</span><span class="n">res</span> <span class="o">+</span> <span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="mi">9</span> <span class="kr">end</span>
</span></span><span class="line"><span class="cl">                <span class="n">nines</span> <span class="o">=</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl">            <span class="kr">end</span>
</span></span><span class="line"><span class="cl">        <span class="kr">end</span>
</span></span><span class="line"><span class="cl">    <span class="kr">end</span>
</span></span><span class="line"><span class="cl">    <span class="n">res</span><span class="p">[</span><span class="o">#</span><span class="n">res</span> <span class="o">+</span> <span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="n">predigit</span>
</span></span><span class="line"><span class="cl">    <span class="kr">return</span> <span class="n">res</span>
</span></span><span class="line"><span class="cl"><span class="kr">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">-- 计算 + 计时（多算几个，截断取前 DIGITS 位，避免 9 传播导致少位）</span>
</span></span><span class="line"><span class="cl"><span class="kd">local</span> <span class="n">t0</span> <span class="o">=</span> <span class="n">sys.millis</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="kd">local</span> <span class="n">digits</span> <span class="o">=</span> <span class="n">pi_spigot</span><span class="p">(</span><span class="n">DIGITS</span> <span class="o">+</span> <span class="mi">3</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="kd">local</span> <span class="n">dt</span> <span class="o">=</span> <span class="n">sys.millis</span><span class="p">()</span> <span class="o">-</span> <span class="n">t0</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">-- 拼接输出：整数位 3 + DIGITS 位小数（res[1] = 整数位 3）</span>
</span></span><span class="line"><span class="cl"><span class="kd">local</span> <span class="n">pi</span> <span class="o">=</span> <span class="s2">&#34;3.&#34;</span>
</span></span><span class="line"><span class="cl"><span class="kr">for</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">2</span><span class="p">,</span> <span class="n">DIGITS</span> <span class="o">+</span> <span class="mi">1</span> <span class="kr">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">pi</span> <span class="o">=</span> <span class="n">pi</span> <span class="o">..</span> <span class="n">tostring</span><span class="p">(</span><span class="n">digits</span><span class="p">[</span><span class="n">i</span><span class="p">])</span>
</span></span><span class="line"><span class="cl"><span class="kr">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">print</span><span class="p">(</span><span class="s2">&#34;pi1000_lua: &#34;</span> <span class="o">..</span> <span class="n">pi</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">print</span><span class="p">(</span><span class="n">string.format</span><span class="p">(</span><span class="s2">&#34;pi1000_lua: %d digits in %d ms&#34;</span><span class="p">,</span> <span class="n">DIGITS</span><span class="p">,</span> <span class="n">dt</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"><span class="n">print</span><span class="p">(</span><span class="s2">&#34;pi1000_lua: done&#34;</span><span class="p">)</span>
</span></span></code></pre></div><h3 id="附录-dlua-的优化实现使用算整数除法">附录 D：Lua 的优化实现，使用//算整数除法</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-lua" data-lang="lua"><span class="line"><span class="cl"><span class="c1">-- 计算 1000 位圆周率（Rabinowitz-Wagon spigot 算法）</span>
</span></span><span class="line"><span class="cl"><span class="c1">-- 参考 Simeon Simeonov 的 Pascal 原版（pi_spigot），本机已与权威前 100 位核对。</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">local</span> <span class="n">sys</span> <span class="o">=</span> <span class="n">require</span><span class="p">(</span><span class="s2">&#34;sys&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">local</span> <span class="n">DIGITS</span> <span class="o">=</span> <span class="mi">1000</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">-- Rabinowitz-Wagon spigot：逐位生成 pi 的数字（含整数位 3 在最前）。</span>
</span></span><span class="line"><span class="cl"><span class="kd">local</span> <span class="kr">function</span> <span class="nf">pi_spigot</span><span class="p">(</span><span class="n">n</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="kd">local</span> <span class="n">len</span> <span class="o">=</span> <span class="p">(</span><span class="mi">10</span> <span class="o">*</span> <span class="n">n</span><span class="p">)</span> <span class="o">//</span> <span class="mi">3</span>          <span class="c1">-- 整数整除（不超 int32）</span>
</span></span><span class="line"><span class="cl">    <span class="kd">local</span> <span class="n">a</span> <span class="o">=</span> <span class="p">{}</span>
</span></span><span class="line"><span class="cl">    <span class="kr">for</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">1</span><span class="p">,</span> <span class="n">len</span> <span class="kr">do</span> <span class="n">a</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="mi">2</span> <span class="kr">end</span>
</span></span><span class="line"><span class="cl">    <span class="kd">local</span> <span class="n">nines</span><span class="p">,</span> <span class="n">predigit</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl">    <span class="kd">local</span> <span class="n">res</span> <span class="o">=</span> <span class="p">{}</span>
</span></span><span class="line"><span class="cl">    <span class="kd">local</span> <span class="n">r</span> <span class="o">=</span> <span class="mi">0</span>                         <span class="c1">-- 本地计数器替代 #res + 1</span>
</span></span><span class="line"><span class="cl">    <span class="kr">for</span> <span class="n">j</span> <span class="o">=</span> <span class="mi">1</span><span class="p">,</span> <span class="n">n</span> <span class="kr">do</span>
</span></span><span class="line"><span class="cl">        <span class="kd">local</span> <span class="n">q</span> <span class="o">=</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl">        <span class="kr">for</span> <span class="n">i</span> <span class="o">=</span> <span class="n">len</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span> <span class="kr">do</span>
</span></span><span class="line"><span class="cl">            <span class="kd">local</span> <span class="n">x</span> <span class="o">=</span> <span class="mi">10</span> <span class="o">*</span> <span class="n">a</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">+</span> <span class="n">q</span> <span class="o">*</span> <span class="n">i</span> <span class="c1">-- 全整数，int32 内安全</span>
</span></span><span class="line"><span class="cl">            <span class="kd">local</span> <span class="n">d</span> <span class="o">=</span> <span class="mi">2</span> <span class="o">*</span> <span class="n">i</span> <span class="o">-</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl">            <span class="n">a</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">x</span> <span class="o">%</span> <span class="n">d</span>                <span class="c1">-- 整数取模</span>
</span></span><span class="line"><span class="cl">            <span class="n">q</span> <span class="o">=</span> <span class="n">x</span> <span class="o">//</span> <span class="n">d</span>                  <span class="c1">-- 整数整除（OP_IDIV）</span>
</span></span><span class="line"><span class="cl">        <span class="kr">end</span>
</span></span><span class="line"><span class="cl">        <span class="n">a</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="n">q</span> <span class="o">%</span> <span class="mi">10</span>
</span></span><span class="line"><span class="cl">        <span class="n">q</span> <span class="o">=</span> <span class="n">q</span> <span class="o">//</span> <span class="mi">10</span>
</span></span><span class="line"><span class="cl">        <span class="kr">if</span> <span class="n">q</span> <span class="o">==</span> <span class="mi">9</span> <span class="kr">then</span>
</span></span><span class="line"><span class="cl">            <span class="n">nines</span> <span class="o">=</span> <span class="n">nines</span> <span class="o">+</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl">        <span class="kr">elseif</span> <span class="n">q</span> <span class="o">==</span> <span class="mi">10</span> <span class="kr">then</span>
</span></span><span class="line"><span class="cl">            <span class="n">r</span> <span class="o">=</span> <span class="n">r</span> <span class="o">+</span> <span class="mi">1</span><span class="p">;</span> <span class="n">res</span><span class="p">[</span><span class="n">r</span><span class="p">]</span> <span class="o">=</span> <span class="n">predigit</span> <span class="o">+</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl">            <span class="kr">for</span> <span class="n">_</span> <span class="o">=</span> <span class="mi">1</span><span class="p">,</span> <span class="n">nines</span> <span class="kr">do</span> <span class="n">r</span> <span class="o">=</span> <span class="n">r</span> <span class="o">+</span> <span class="mi">1</span><span class="p">;</span> <span class="n">res</span><span class="p">[</span><span class="n">r</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span> <span class="kr">end</span>
</span></span><span class="line"><span class="cl">            <span class="n">predigit</span><span class="p">,</span> <span class="n">nines</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl">        <span class="kr">else</span>
</span></span><span class="line"><span class="cl">            <span class="kr">if</span> <span class="n">j</span> <span class="o">&gt;</span> <span class="mi">1</span> <span class="kr">then</span> <span class="n">r</span> <span class="o">=</span> <span class="n">r</span> <span class="o">+</span> <span class="mi">1</span><span class="p">;</span> <span class="n">res</span><span class="p">[</span><span class="n">r</span><span class="p">]</span> <span class="o">=</span> <span class="n">predigit</span> <span class="kr">end</span>
</span></span><span class="line"><span class="cl">            <span class="n">predigit</span> <span class="o">=</span> <span class="n">q</span>
</span></span><span class="line"><span class="cl">            <span class="kr">if</span> <span class="n">nines</span> <span class="o">&gt;</span> <span class="mi">0</span> <span class="kr">then</span>
</span></span><span class="line"><span class="cl">                <span class="kr">for</span> <span class="n">_</span> <span class="o">=</span> <span class="mi">1</span><span class="p">,</span> <span class="n">nines</span> <span class="kr">do</span> <span class="n">r</span> <span class="o">=</span> <span class="n">r</span> <span class="o">+</span> <span class="mi">1</span><span class="p">;</span> <span class="n">res</span><span class="p">[</span><span class="n">r</span><span class="p">]</span> <span class="o">=</span> <span class="mi">9</span> <span class="kr">end</span>
</span></span><span class="line"><span class="cl">                <span class="n">nines</span> <span class="o">=</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl">            <span class="kr">end</span>
</span></span><span class="line"><span class="cl">        <span class="kr">end</span>
</span></span><span class="line"><span class="cl">    <span class="kr">end</span>
</span></span><span class="line"><span class="cl">    <span class="n">r</span> <span class="o">=</span> <span class="n">r</span> <span class="o">+</span> <span class="mi">1</span><span class="p">;</span> <span class="n">res</span><span class="p">[</span><span class="n">r</span><span class="p">]</span> <span class="o">=</span> <span class="n">predigit</span>
</span></span><span class="line"><span class="cl">    <span class="kr">return</span> <span class="n">res</span>
</span></span><span class="line"><span class="cl"><span class="kr">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">-- 计算 + 计时（多算几个，截断取前 DIGITS 位，避免 9 传播导致少位）</span>
</span></span><span class="line"><span class="cl"><span class="kd">local</span> <span class="n">t0</span> <span class="o">=</span> <span class="n">sys.millis</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="kd">local</span> <span class="n">digits</span> <span class="o">=</span> <span class="n">pi_spigot</span><span class="p">(</span><span class="n">DIGITS</span> <span class="o">+</span> <span class="mi">3</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="kd">local</span> <span class="n">dt</span> <span class="o">=</span> <span class="n">sys.millis</span><span class="p">()</span> <span class="o">-</span> <span class="n">t0</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">-- 拼接输出：整数位 3 + DIGITS 位小数（res[1] = 整数位 3）</span>
</span></span><span class="line"><span class="cl"><span class="kd">local</span> <span class="n">pi</span> <span class="o">=</span> <span class="s2">&#34;3.&#34;</span>
</span></span><span class="line"><span class="cl"><span class="kr">for</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">2</span><span class="p">,</span> <span class="n">DIGITS</span> <span class="o">+</span> <span class="mi">1</span> <span class="kr">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">pi</span> <span class="o">=</span> <span class="n">pi</span> <span class="o">..</span> <span class="n">tostring</span><span class="p">(</span><span class="n">digits</span><span class="p">[</span><span class="n">i</span><span class="p">])</span>
</span></span><span class="line"><span class="cl"><span class="kr">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">print</span><span class="p">(</span><span class="s2">&#34;pi1000_lua: &#34;</span> <span class="o">..</span> <span class="n">pi</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">print</span><span class="p">(</span><span class="n">string.format</span><span class="p">(</span><span class="s2">&#34;pi1000_lua: %d digits in %d ms&#34;</span><span class="p">,</span> <span class="n">DIGITS</span><span class="p">,</span> <span class="n">dt</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"><span class="n">print</span><span class="p">(</span><span class="s2">&#34;pi1000_lua: done&#34;</span><span class="p">)</span>
</span></span></code></pre></div><h3 id="附录-emicropython-实现">附录 E：MicroPython 实现</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="c1"># MicroPython 计算 1000 位圆周率（Rabinowitz-Wagon spigot 算法）</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 计时用 time.ticks_ms()，仅包裹纯计算主循环。</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">time</span> <span class="k">as</span> <span class="nn">_t</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">DIGITS</span> <span class="o">=</span> <span class="mi">1000</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">pi_spigot</span><span class="p">(</span><span class="n">n</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="n">LEN</span> <span class="o">=</span> <span class="p">(</span><span class="mi">10</span> <span class="o">*</span> <span class="n">n</span><span class="p">)</span> <span class="o">//</span> <span class="mi">3</span>          <span class="c1"># 整数整除</span>
</span></span><span class="line"><span class="cl">    <span class="n">a</span> <span class="o">=</span> <span class="p">[</span><span class="mi">2</span><span class="p">]</span> <span class="o">*</span> <span class="n">LEN</span>                <span class="c1"># 预分配列表</span>
</span></span><span class="line"><span class="cl">    <span class="n">nines</span> <span class="o">=</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl">    <span class="n">predigit</span> <span class="o">=</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl">    <span class="n">res</span> <span class="o">=</span> <span class="p">[]</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="n">j</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="n">n</span> <span class="o">+</span> <span class="mi">1</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="n">q</span> <span class="o">=</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl">        <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">LEN</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">            <span class="n">x</span> <span class="o">=</span> <span class="mi">10</span> <span class="o">*</span> <span class="n">a</span><span class="p">[</span><span class="n">i</span> <span class="o">-</span> <span class="mi">1</span><span class="p">]</span> <span class="o">+</span> <span class="n">q</span> <span class="o">*</span> <span class="n">i</span>
</span></span><span class="line"><span class="cl">            <span class="n">d</span> <span class="o">=</span> <span class="mi">2</span> <span class="o">*</span> <span class="n">i</span> <span class="o">-</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl">            <span class="n">a</span><span class="p">[</span><span class="n">i</span> <span class="o">-</span> <span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="n">x</span> <span class="o">%</span> <span class="n">d</span>
</span></span><span class="line"><span class="cl">            <span class="n">q</span> <span class="o">=</span> <span class="n">x</span> <span class="o">//</span> <span class="n">d</span>
</span></span><span class="line"><span class="cl">        <span class="n">a</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="n">q</span> <span class="o">%</span> <span class="mi">10</span>
</span></span><span class="line"><span class="cl">        <span class="n">q</span> <span class="o">=</span> <span class="n">q</span> <span class="o">//</span> <span class="mi">10</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="n">q</span> <span class="o">==</span> <span class="mi">9</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="n">nines</span> <span class="o">+=</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl">        <span class="k">elif</span> <span class="n">q</span> <span class="o">==</span> <span class="mi">10</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="n">res</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">predigit</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="n">res</span><span class="o">.</span><span class="n">extend</span><span class="p">([</span><span class="mi">0</span><span class="p">]</span> <span class="o">*</span> <span class="n">nines</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="n">predigit</span> <span class="o">=</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl">            <span class="n">nines</span> <span class="o">=</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl">        <span class="k">else</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="k">if</span> <span class="n">j</span> <span class="o">&gt;</span> <span class="mi">1</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">                <span class="n">res</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">predigit</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="n">predigit</span> <span class="o">=</span> <span class="n">q</span>
</span></span><span class="line"><span class="cl">            <span class="k">if</span> <span class="n">nines</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">                <span class="n">res</span><span class="o">.</span><span class="n">extend</span><span class="p">([</span><span class="mi">9</span><span class="p">]</span> <span class="o">*</span> <span class="n">nines</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                <span class="n">nines</span> <span class="o">=</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl">    <span class="n">res</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">predigit</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">res</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">t0</span> <span class="o">=</span> <span class="n">_t</span><span class="o">.</span><span class="n">ticks_ms</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="n">digits</span> <span class="o">=</span> <span class="n">pi_spigot</span><span class="p">(</span><span class="n">DIGITS</span> <span class="o">+</span> <span class="mi">3</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">dt</span> <span class="o">=</span> <span class="n">_t</span><span class="o">.</span><span class="n">ticks_diff</span><span class="p">(</span><span class="n">_t</span><span class="o">.</span><span class="n">ticks_ms</span><span class="p">(),</span> <span class="n">t0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">pi</span> <span class="o">=</span> <span class="s2">&#34;3.&#34;</span> <span class="o">+</span> <span class="s2">&#34;&#34;</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">d</span><span class="p">)</span> <span class="k">for</span> <span class="n">d</span> <span class="ow">in</span> <span class="n">digits</span><span class="p">[</span><span class="mi">1</span><span class="p">:</span><span class="n">DIGITS</span> <span class="o">+</span> <span class="mi">1</span><span class="p">])</span>
</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="s2">&#34;pi1000_py: &#34;</span> <span class="o">+</span> <span class="n">pi</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="s2">&#34;pi1000_py: </span><span class="si">%d</span><span class="s2"> digits in </span><span class="si">%d</span><span class="s2"> ms&#34;</span> <span class="o">%</span> <span class="p">(</span><span class="n">DIGITS</span><span class="p">,</span> <span class="n">dt</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="s2">&#34;pi1000_py: done&#34;</span><span class="p">)</span>
</span></span></code></pre></div><hr>
<p><em>本文数据来自一次真实设备实测。算法为公开的经典 spigot 算法，可按任意语言复现。</em></p>
<p><strong>📚 相关阅读</strong></p>
<ul>
<li><a href="https://tech-snippets.xyz/posts/elf-loader-base-architecture-vs-esp-brookesia-2026/">动态扩展运行时架构研究：elf_loader 基座架构与 ESP-Brookesia 对比</a> —— 四种执行方式背后的架构设计：elf_loader 单一基座 vs Brookesia 多后端，以及本文实测数据对应的选型结论</li>
</ul>
]]></content:encoded></item><item><title>嵌入式开发：Executorch On-Device Ai Embedded Deployment 2025</title><link>https://tech-snippets.xyz/posts/executorch-on-device-ai-embedded-deployment-2025/</link><pubDate>Sun, 21 Jun 2026 03:00:00 +0800</pubDate><guid>https://tech-snippets.xyz/posts/executorch-on-device-ai-embedded-deployment-2025/</guid><description>嵌入式开发实战教程：ExecuTorch on-device AI embedded deployment 2025</description><content:encoded><![CDATA[<h2 id="引言">引言</h2>
<p>本文基于 2026 年最新行业资料整理，涵盖 ExecuTorch on-device AI embedded deployment 2025 的核心概念、开发流程和实战技巧。</p>
<h2 id="executorch-on-device-ai-embedded-deployment-2025-概述">ExecuTorch on-device AI embedded deployment 2025 概述</h2>
<p>嵌入式系统开发需要掌握硬件、软件和系统集成的综合技能。</p>
<h2 id="开发流程">开发流程</h2>
<ol>
<li><strong>需求分析</strong> → 确定功能、性能指标</li>
<li><strong>硬件选型</strong> → MCU、传感器、通信模块</li>
<li><strong>原理图设计</strong> → 电路设计、仿真验证</li>
<li><strong>软件架构</strong> → 分层设计、模块化</li>
<li><strong>编码实现</strong> → C/C++、RTOS、驱动</li>
<li><strong>调试测试</strong> → 单元测试、系统集成</li>
<li><strong>生产部署</strong> → 批量生产、OTA 升级</li>
</ol>
<h2 id="常用工具">常用工具</h2>
<ul>
<li><strong>IDE</strong>：Keil、IAR、STM32CubeIDE、VS Code</li>
<li><strong>调试器</strong>：J-Link、ST-Link、DAP-Link</li>
<li><strong>示波器</strong>：Siglent、Rigol、Keysight</li>
<li><strong>逻辑分析仪</strong>：Saleae、Kingst</li>
</ul>
<h2 id="学习路线">学习路线</h2>
<ol>
<li>C 语言基础 → 指针、结构体、内存管理</li>
<li>单片机原理 → GPIO、中断、定时器</li>
<li>外设驱动 → UART、SPI、I2C、ADC</li>
<li>RTOS → 任务、信号量、消息队列</li>
<li>项目实战 → 综合应用</li>
</ol>
<h2 id="参考资料">参考资料</h2>
<ol>
<li><a href="https://pytorchconference.sched.com/event/27QCJ/executorch-10-general-availability-status-for-mobile-and-embedded-application-developers-mergen-nachin-cemal-bilgin-meta">ExecuTorch 1.0: General Availability Sta&hellip; - PyTorch Conference 2025</a></li>
<li><a href="https://arxiv.org/abs/2605.08195">ExecuTorch &ndash; A Unified PyTorch Solution to Run AI Models On-Device</a></li>
<li><a href="https://pytorch.org/projects/executorch">ExecuTorch</a></li>
</ol>
<hr>
<p><em>本文基于网络公开资料整理，结合嵌入式开发实践经验编写。</em></p>
]]></content:encoded></item><item><title>嵌入式开发：Esp32-P4 Edge Ai Hmi Vision Esp32-C6 2026</title><link>https://tech-snippets.xyz/posts/esp32-p4-edge-ai-hmi-vision-esp32-c6-2026/</link><pubDate>Thu, 18 Jun 2026 03:00:00 +0800</pubDate><guid>https://tech-snippets.xyz/posts/esp32-p4-edge-ai-hmi-vision-esp32-c6-2026/</guid><description>嵌入式开发实战教程：ESP32-P4 edge AI HMI vision ESP32-C6 2026</description><content:encoded><![CDATA[<h2 id="引言">引言</h2>
<p>本文基于 2026 年最新行业资料整理，涵盖 ESP32-P4 edge AI HMI vision ESP32-C6 2026 的核心概念、开发流程和实战技巧。</p>
<h2 id="esp32-p4-edge-ai-hmi-vision-esp32-c6-2026-概述">ESP32-P4 edge AI HMI vision ESP32-C6 2026 概述</h2>
<p>嵌入式系统开发需要掌握硬件、软件和系统集成的综合技能。</p>
<h2 id="开发流程">开发流程</h2>
<ol>
<li><strong>需求分析</strong> → 确定功能、性能指标</li>
<li><strong>硬件选型</strong> → MCU、传感器、通信模块</li>
<li><strong>原理图设计</strong> → 电路设计、仿真验证</li>
<li><strong>软件架构</strong> → 分层设计、模块化</li>
<li><strong>编码实现</strong> → C/C++、RTOS、驱动</li>
<li><strong>调试测试</strong> → 单元测试、系统集成</li>
<li><strong>生产部署</strong> → 批量生产、OTA 升级</li>
</ol>
<h2 id="常用工具">常用工具</h2>
<ul>
<li><strong>IDE</strong>：Keil、IAR、STM32CubeIDE、VS Code</li>
<li><strong>调试器</strong>：J-Link、ST-Link、DAP-Link</li>
<li><strong>示波器</strong>：Siglent、Rigol、Keysight</li>
<li><strong>逻辑分析仪</strong>：Saleae、Kingst</li>
</ul>
<h2 id="学习路线">学习路线</h2>
<ol>
<li>C 语言基础 → 指针、结构体、内存管理</li>
<li>单片机原理 → GPIO、中断、定时器</li>
<li>外设驱动 → UART、SPI、I2C、ADC</li>
<li>RTOS → 任务、信号量、消息队列</li>
<li>项目实战 → 综合应用</li>
</ol>
<h2 id="参考资料">参考资料</h2>
<ol>
<li><a href="https://www.youtube.com/watch?v=uVr0JxrOTfU&amp;vl=en">Espressif Booth Tour at Embedded World 2026 ESP32-P4 HMI, ESP32-C6 Low Power, ESP32-E22 Wi-Fi 6E</a></li>
<li><a href="https://www.cnx-software.com/2026/01/17/tablet-like-esp32-p4-based-7-8-and-10-1-inch-hmi-displays-integrate-wi-fi-6-connectivity-5mp-camera">Tablet-like, ESP32-P4-based 7, 8, and 10.1-inch HMI displays integrate Wi-Fi 6 connectivity, 5MP camera - CNX Software</a></li>
<li><a href="https://www.instagram.com/p/DI0qT_VAK22">Vision Intelligence at the Edge Espressif&rsquo;s ESP32-P4-EYE &hellip;</a></li>
</ol>
<hr>
<p><em>本文基于网络公开资料整理，结合嵌入式开发实践经验编写。</em></p>
]]></content:encoded></item><item><title>嵌入式开发：Jetson Thor Physical Ai Robotics Edge Agents 2025</title><link>https://tech-snippets.xyz/posts/jetson-thor-physical-ai-robotics-edge-agents-2025/</link><pubDate>Wed, 17 Jun 2026 03:00:00 +0800</pubDate><guid>https://tech-snippets.xyz/posts/jetson-thor-physical-ai-robotics-edge-agents-2025/</guid><description>嵌入式开发实战教程：Jetson Thor physical AI robotics edge agents 2025</description><content:encoded><![CDATA[<h2 id="引言">引言</h2>
<p>本文基于 2026 年最新行业资料整理，涵盖 Jetson Thor physical AI robotics edge agents 2025 的核心概念、开发流程和实战技巧。</p>
<h2 id="jetson-thor-physical-ai-robotics-edge-agents-2025-概述">Jetson Thor physical AI robotics edge agents 2025 概述</h2>
<p>嵌入式系统开发需要掌握硬件、软件和系统集成的综合技能。</p>
<h2 id="开发流程">开发流程</h2>
<ol>
<li><strong>需求分析</strong> → 确定功能、性能指标</li>
<li><strong>硬件选型</strong> → MCU、传感器、通信模块</li>
<li><strong>原理图设计</strong> → 电路设计、仿真验证</li>
<li><strong>软件架构</strong> → 分层设计、模块化</li>
<li><strong>编码实现</strong> → C/C++、RTOS、驱动</li>
<li><strong>调试测试</strong> → 单元测试、系统集成</li>
<li><strong>生产部署</strong> → 批量生产、OTA 升级</li>
</ol>
<h2 id="常用工具">常用工具</h2>
<ul>
<li><strong>IDE</strong>：Keil、IAR、STM32CubeIDE、VS Code</li>
<li><strong>调试器</strong>：J-Link、ST-Link、DAP-Link</li>
<li><strong>示波器</strong>：Siglent、Rigol、Keysight</li>
<li><strong>逻辑分析仪</strong>：Saleae、Kingst</li>
</ul>
<h2 id="学习路线">学习路线</h2>
<ol>
<li>C 语言基础 → 指针、结构体、内存管理</li>
<li>单片机原理 → GPIO、中断、定时器</li>
<li>外设驱动 → UART、SPI、I2C、ADC</li>
<li>RTOS → 任务、信号量、消息队列</li>
<li>项目实战 → 综合应用</li>
</ol>
<h2 id="参考资料">参考资料</h2>
<ol>
<li><a href="https://www.youtube.com/watch?v=IvzFNfOEbc0&amp;vl=en-US">Jetson AGX Thor: NVIDIA $3,499 AI Supercomputer for Humanoid Robots &amp; Edge AI</a></li>
<li><a href="https://tannatechbiz.com/blog/post/nvidia-jetson-thor-module-redefining-physical-ai-and-robotics">NVIDIA Jetson Thor Module: A Next-Gen AI Powerhouse for Robotics [2025]</a></li>
<li><a href="https://www.roboticstomorrow.com/news/2025/10/22/advantech-unveils-edge-ai-solutions-accelerated-by-nvidia-jetson-thor-for-robotics-medical-ai-and-data-intelligence/25708">Advantech Unveils Edge AI Solutions Accelerated by NVIDIA Jetson Thor for Robotics, Medical AI, and Data Intelligence | RoboticsTomorrow</a></li>
</ol>
<hr>
<p><em>本文基于网络公开资料整理，结合嵌入式开发实践经验编写。</em></p>
]]></content:encoded></item><item><title>嵌入式开发：Arm Ethos-U85 Corstone-320 Microcontroller Edge Ai 2025</title><link>https://tech-snippets.xyz/posts/arm-ethos-u85-corstone-320-microcontroller-edge-ai-2025/</link><pubDate>Mon, 15 Jun 2026 03:00:00 +0800</pubDate><guid>https://tech-snippets.xyz/posts/arm-ethos-u85-corstone-320-microcontroller-edge-ai-2025/</guid><description>嵌入式开发实战教程：Arm Ethos-U85 Corstone-320 microcontroller edge AI 2025</description><content:encoded><![CDATA[<h2 id="引言">引言</h2>
<p>本文基于 2026 年最新行业资料整理，涵盖 Arm Ethos-U85 Corstone-320 microcontroller edge AI 2025 的核心概念、开发流程和实战技巧。</p>
<h2 id="arm-ethos-u85-corstone-320-microcontroller-edge-ai-2025-概述">Arm Ethos-U85 Corstone-320 microcontroller edge AI 2025 概述</h2>
<p>嵌入式系统开发需要掌握硬件、软件和系统集成的综合技能。</p>
<h2 id="开发流程">开发流程</h2>
<ol>
<li><strong>需求分析</strong> → 确定功能、性能指标</li>
<li><strong>硬件选型</strong> → MCU、传感器、通信模块</li>
<li><strong>原理图设计</strong> → 电路设计、仿真验证</li>
<li><strong>软件架构</strong> → 分层设计、模块化</li>
<li><strong>编码实现</strong> → C/C++、RTOS、驱动</li>
<li><strong>调试测试</strong> → 单元测试、系统集成</li>
<li><strong>生产部署</strong> → 批量生产、OTA 升级</li>
</ol>
<h2 id="常用工具">常用工具</h2>
<ul>
<li><strong>IDE</strong>：Keil、IAR、STM32CubeIDE、VS Code</li>
<li><strong>调试器</strong>：J-Link、ST-Link、DAP-Link</li>
<li><strong>示波器</strong>：Siglent、Rigol、Keysight</li>
<li><strong>逻辑分析仪</strong>：Saleae、Kingst</li>
</ul>
<h2 id="学习路线">学习路线</h2>
<ol>
<li>C 语言基础 → 指针、结构体、内存管理</li>
<li>单片机原理 → GPIO、中断、定时器</li>
<li>外设驱动 → UART、SPI、I2C、ADC</li>
<li>RTOS → 任务、信号量、消息队列</li>
<li>项目实战 → 综合应用</li>
</ol>
<h2 id="参考资料">参考资料</h2>
<ol>
<li><a href="https://www.hackster.io/news/arm-targets-the-aiot-with-high-performance-ethos-u85-npu-and-corstone-320-platform-10af3de76710">Arm Targets the AIoT with High-Performance Ethos-U85 NPU and Corstone-320 Platform  - Hackster.io</a></li>
<li><a href="https://www.computerweekly.com/news/366580572/Arm-accelerates-Edge-AI-with-Ethos-U-NPU-and-IoT-reference-design">Arm accelerates Edge AI with Ethos-U NPU and IoT reference design | Computer Weekly</a></li>
<li><a href="https://x.com/Arm/status/2028998663544942865">Edge AI is evolving and so is Arm Flexible Access. New additions &hellip;</a></li>
</ol>
<hr>
<p><em>本文基于网络公开资料整理，结合嵌入式开发实践经验编写。</em></p>
]]></content:encoded></item><item><title>嵌入式开发：Litert Tensorflow Lite Successor On-Device Ai 2025</title><link>https://tech-snippets.xyz/posts/litert-tensorflow-lite-successor-on-device-ai-2025/</link><pubDate>Sun, 14 Jun 2026 03:00:00 +0800</pubDate><guid>https://tech-snippets.xyz/posts/litert-tensorflow-lite-successor-on-device-ai-2025/</guid><description>嵌入式开发实战教程：LiteRT TensorFlow Lite successor on-device AI 2025</description><content:encoded><![CDATA[<h2 id="引言">引言</h2>
<p>本文基于 2026 年最新行业资料整理，涵盖 LiteRT TensorFlow Lite successor on-device AI 2025 的核心概念、开发流程和实战技巧。</p>
<h2 id="litert-tensorflow-lite-successor-on-device-ai-2025-概述">LiteRT TensorFlow Lite successor on-device AI 2025 概述</h2>
<p>嵌入式系统开发需要掌握硬件、软件和系统集成的综合技能。</p>
<h2 id="开发流程">开发流程</h2>
<ol>
<li><strong>需求分析</strong> → 确定功能、性能指标</li>
<li><strong>硬件选型</strong> → MCU、传感器、通信模块</li>
<li><strong>原理图设计</strong> → 电路设计、仿真验证</li>
<li><strong>软件架构</strong> → 分层设计、模块化</li>
<li><strong>编码实现</strong> → C/C++、RTOS、驱动</li>
<li><strong>调试测试</strong> → 单元测试、系统集成</li>
<li><strong>生产部署</strong> → 批量生产、OTA 升级</li>
</ol>
<h2 id="常用工具">常用工具</h2>
<ul>
<li><strong>IDE</strong>：Keil、IAR、STM32CubeIDE、VS Code</li>
<li><strong>调试器</strong>：J-Link、ST-Link、DAP-Link</li>
<li><strong>示波器</strong>：Siglent、Rigol、Keysight</li>
<li><strong>逻辑分析仪</strong>：Saleae、Kingst</li>
</ul>
<h2 id="学习路线">学习路线</h2>
<ol>
<li>C 语言基础 → 指针、结构体、内存管理</li>
<li>单片机原理 → GPIO、中断、定时器</li>
<li>外设驱动 → UART、SPI、I2C、ADC</li>
<li>RTOS → 任务、信号量、消息队列</li>
<li>项目实战 → 综合应用</li>
</ol>
<h2 id="参考资料">参考资料</h2>
<ol>
<li><a href="https://medium.com/data-and-beyond/tensorflow-2-21-litert-next-gen-on-device-ai-framework-c83ec6c876a4">TensorFlow 2.21 &amp; LiteRT: Next-Gen On-Device AI Framework 🚀 | by Aniket Sanyal | Data And Beyond | Medium</a></li>
<li><a href="https://sourceforge.net/projects/litert.mirror">LiteRT download | SourceForge.net</a></li>
<li><a href="https://github.com/google-ai-edge/litert">GitHub - google-ai-edge/LiteRT: LiteRT, successor to TensorFlow Lite. is Google&rsquo;s On-device framework for high-performance ML &amp; GenAI deployment on edge platforms, via efficient conversion, runtime, and optimization · GitHub</a></li>
</ol>
<hr>
<p><em>本文基于网络公开资料整理，结合嵌入式开发实践经验编写。</em></p>
]]></content:encoded></item><item><title>嵌入式开发：Qualcomm Ai Hub On-Device Model Deployment Edge Ai 2025</title><link>https://tech-snippets.xyz/posts/qualcomm-ai-hub-on-device-model-deployment-edge-ai-2025/</link><pubDate>Sat, 13 Jun 2026 03:00:00 +0800</pubDate><guid>https://tech-snippets.xyz/posts/qualcomm-ai-hub-on-device-model-deployment-edge-ai-2025/</guid><description>嵌入式开发实战教程：Qualcomm AI Hub on-device model deployment edge AI 2025</description><content:encoded><![CDATA[<h2 id="引言">引言</h2>
<p>本文基于 2026 年最新行业资料整理，涵盖 Qualcomm AI Hub on-device model deployment edge AI 2025 的核心概念、开发流程和实战技巧。</p>
<h2 id="qualcomm-ai-hub-on-device-model-deployment-edge-ai-2025-概述">Qualcomm AI Hub on-device model deployment edge AI 2025 概述</h2>
<p>嵌入式系统开发需要掌握硬件、软件和系统集成的综合技能。</p>
<h2 id="开发流程">开发流程</h2>
<ol>
<li><strong>需求分析</strong> → 确定功能、性能指标</li>
<li><strong>硬件选型</strong> → MCU、传感器、通信模块</li>
<li><strong>原理图设计</strong> → 电路设计、仿真验证</li>
<li><strong>软件架构</strong> → 分层设计、模块化</li>
<li><strong>编码实现</strong> → C/C++、RTOS、驱动</li>
<li><strong>调试测试</strong> → 单元测试、系统集成</li>
<li><strong>生产部署</strong> → 批量生产、OTA 升级</li>
</ol>
<h2 id="常用工具">常用工具</h2>
<ul>
<li><strong>IDE</strong>：Keil、IAR、STM32CubeIDE、VS Code</li>
<li><strong>调试器</strong>：J-Link、ST-Link、DAP-Link</li>
<li><strong>示波器</strong>：Siglent、Rigol、Keysight</li>
<li><strong>逻辑分析仪</strong>：Saleae、Kingst</li>
</ul>
<h2 id="学习路线">学习路线</h2>
<ol>
<li>C 语言基础 → 指针、结构体、内存管理</li>
<li>单片机原理 → GPIO、中断、定时器</li>
<li>外设驱动 → UART、SPI、I2C、ADC</li>
<li>RTOS → 任务、信号量、消息队列</li>
<li>项目实战 → 综合应用</li>
</ol>
<h2 id="参考资料">参考资料</h2>
<ol>
<li><a href="https://www.qualcomm.com/developer/blog/2025/05/deploy-ai-models-on-snapdragon-x-elite-with-qualcomm-ai-hub">Deploy AI Models on Snapdragon X Elite with Qualcomm AI Hub</a></li>
<li><a href="https://www.qualcomm.com/developer/blog/2025/11/qualcomm-ai-hub-explained-workbench-models-apps">Qualcomm AI Hub explained: Workbench, Models and Apps</a></li>
<li><a href="https://embeddedvisionsummit.com/deep-dive-days">Edge AI Deep Dive Day™ - 2026 Embedded Vision Summit</a></li>
</ol>
<hr>
<p><em>本文基于网络公开资料整理，结合嵌入式开发实践经验编写。</em></p>
]]></content:encoded></item><item><title>嵌入式开发：Edge Ai Secure Ota Model Prompt Tool Update Embedded 2026</title><link>https://tech-snippets.xyz/posts/edge-ai-secure-ota-model-prompt-tool-update-embedded-2026/</link><pubDate>Fri, 12 Jun 2026 03:00:00 +0800</pubDate><guid>https://tech-snippets.xyz/posts/edge-ai-secure-ota-model-prompt-tool-update-embedded-2026/</guid><description>嵌入式开发实战教程：edge AI secure OTA model prompt tool update embedded 2026</description><content:encoded><![CDATA[<h2 id="引言">引言</h2>
<p>本文基于 2026 年最新行业资料整理，涵盖 edge AI secure OTA model prompt tool update embedded 2026 的核心概念、开发流程和实战技巧。</p>
<h2 id="edge-ai-secure-ota-model-prompt-tool-update-embedded-2026-概述">edge AI secure OTA model prompt tool update embedded 2026 概述</h2>
<p>嵌入式系统开发需要掌握硬件、软件和系统集成的综合技能。</p>
<h2 id="开发流程">开发流程</h2>
<ol>
<li><strong>需求分析</strong> → 确定功能、性能指标</li>
<li><strong>硬件选型</strong> → MCU、传感器、通信模块</li>
<li><strong>原理图设计</strong> → 电路设计、仿真验证</li>
<li><strong>软件架构</strong> → 分层设计、模块化</li>
<li><strong>编码实现</strong> → C/C++、RTOS、驱动</li>
<li><strong>调试测试</strong> → 单元测试、系统集成</li>
<li><strong>生产部署</strong> → 批量生产、OTA 升级</li>
</ol>
<h2 id="常用工具">常用工具</h2>
<ul>
<li><strong>IDE</strong>：Keil、IAR、STM32CubeIDE、VS Code</li>
<li><strong>调试器</strong>：J-Link、ST-Link、DAP-Link</li>
<li><strong>示波器</strong>：Siglent、Rigol、Keysight</li>
<li><strong>逻辑分析仪</strong>：Saleae、Kingst</li>
</ul>
<h2 id="学习路线">学习路线</h2>
<ol>
<li>C 语言基础 → 指针、结构体、内存管理</li>
<li>单片机原理 → GPIO、中断、定时器</li>
<li>外设驱动 → UART、SPI、I2C、ADC</li>
<li>RTOS → 任务、信号量、消息队列</li>
<li>项目实战 → 综合应用</li>
</ol>
<h2 id="参考资料">参考资料</h2>
<ol>
<li><a href="https://www.iotevolutionworld.com/iot/articles/462081-how-securely-update-software-ota-embedded-edge-devices.htm">How to Securely Update Software OTA on Embedded Edge Devices and Edge AI Systems</a></li>
<li><a href="https://www.preprints.org/manuscript/202602.1974">Future of Release Management (Firmware/Software Delivery) in the &hellip;</a></li>
<li><a href="https://www.aicas.com/secure-ai-system-update">Secure ML Model Deployment | Secure AI Update Solution</a></li>
</ol>
<hr>
<p><em>本文基于网络公开资料整理，结合嵌入式开发实践经验编写。</em></p>
]]></content:encoded></item><item><title>IoT 固件开发最佳实践 2026</title><link>https://tech-snippets.xyz/posts/model-context-protocol-iot-edge-agent-architecture-2025/</link><pubDate>Thu, 11 Jun 2026 03:00:00 +0800</pubDate><guid>https://tech-snippets.xyz/posts/model-context-protocol-iot-edge-agent-architecture-2025/</guid><description>IoT 固件开发完整指南，涵盖架构设计、OTA 升级、低功耗和安全通信</description><content:encoded><![CDATA[<h2 id="引言">引言</h2>
<p>本文基于 2026 年最新行业资料整理，涵盖 Model Context Protocol IoT edge agent architecture 2025 的核心概念、开发流程和实战技巧。</p>
<h2 id="iot-固件架构">IoT 固件架构</h2>
<h3 id="11-现代-iot-固件分层设计">1.1 现代 IoT 固件分层设计</h3>
<p>现代 IoT 固件采用模块化分层架构，每一层都有明确的职责和接口，便于维护、测试和升级。</p>
<pre tabindex="0"><code>┌─────────────────────────────────────────────────────────────┐
│                      应用层（Application）                   │
│  ┌───────────┐  ┌───────────┐  ┌───────────┐               │
│  │  MQTT 客户端 │  │  HTTP 客户端 │  │  CoAP 客户端 │               │
│  └───────────┘  └───────────┘  └───────────┘               │
├─────────────────────────────────────────────────────────────┤
│                      中间件层（Middleware）                   │
│  ┌───────────┐  ┌───────────┐  ┌───────────┐               │
│  │ FreeRTOS  │  │  LwIP     │  │ mbedTLS   │               │
│  │ (RTOS)    │  │ (网络栈)  │  │ (加密)    │               │
│  └───────────┘  └───────────┘  └───────────┘               │
├─────────────────────────────────────────────────────────────┤
│                      驱动层（Driver）                        │
│  ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐    │
│  │ GPIO │ │ SPI  │ │ I2C  │ │ UART │ │ ADC  │ │ PWM  │    │
│  └──────┘ └──────┘ └──────┘ └──────┘ └──────┘ └──────┘    │
├─────────────────────────────────────────────────────────────┤
│                      硬件抽象层（HAL）                        │
│            STM32 HAL / ESP-IDF / Nordic SDK                 │
├─────────────────────────────────────────────────────────────┤
│                      硬件层（Hardware）                       │
│     Cortex-M / ESP32 / nRF52 / RISC-V                       │
└─────────────────────────────────────────────────────────────┘
</code></pre><h3 id="12-各层职责详解">1.2 各层职责详解</h3>
<table>
<thead>
<tr>
<th>层级</th>
<th>职责</th>
<th>典型组件</th>
<th>更换频率</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>应用层</strong></td>
<td>业务逻辑、用户功能</td>
<td>MQTT 客户端、传感器数据处理</td>
<td>高（频繁迭代）</td>
</tr>
<tr>
<td><strong>中间件层</strong></td>
<td>通用服务、协议栈</td>
<td>FreeRTOS、LwIP、mbedTLS</td>
<td>中（版本升级）</td>
</tr>
<tr>
<td><strong>驱动层</strong></td>
<td>外设驱动、硬件抽象</td>
<td>GPIO、SPI、I2C、UART</td>
<td>低（稳定）</td>
</tr>
<tr>
<td><strong>HAL 层</strong></td>
<td>芯片厂商提供</td>
<td>STM32 HAL、ESP-IDF</td>
<td>低（跟随 SDK）</td>
</tr>
<tr>
<td><strong>硬件层</strong></td>
<td>物理芯片</td>
<td>Cortex-M4、ESP32</td>
<td>极低（产品周期）</td>
</tr>
</tbody>
</table>
<h3 id="13-模块化设计原则">1.3 模块化设计原则</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// 1. 接口与实现分离（头文件定义接口）
</span></span></span><span class="line"><span class="cl"><span class="c1">// sensor_interface.h
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#ifndef SENSOR_INTERFACE_H
</span></span></span><span class="line"><span class="cl"><span class="cp">#define SENSOR_INTERFACE_H
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="p">(</span><span class="o">*</span><span class="n">init</span><span class="p">)(</span><span class="kt">void</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="p">(</span><span class="o">*</span><span class="n">read</span><span class="p">)(</span><span class="kt">float</span> <span class="o">*</span><span class="n">value</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="p">(</span><span class="o">*</span><span class="n">calibrate</span><span class="p">)(</span><span class="kt">void</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="kt">sensor_ops_t</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 外部声明具体实现
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">extern</span> <span class="kt">sensor_ops_t</span> <span class="n">dht22_ops</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">extern</span> <span class="kt">sensor_ops_t</span> <span class="n">bmp280_ops</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="cp">#endif
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="c1">// 2. 依赖注入（便于测试和替换）
</span></span></span><span class="line"><span class="cl"><span class="c1">// main.c
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="nf">sensor_task</span><span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="n">pvParameters</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 通过指针传入具体实现，而非硬编码
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="kt">sensor_ops_t</span> <span class="o">*</span><span class="n">sensor</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">dht22_ops</span><span class="p">;</span>  <span class="c1">// 可轻松切换为 bmp280_ops
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    
</span></span><span class="line"><span class="cl">    <span class="n">sensor</span><span class="o">-&gt;</span><span class="nf">init</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="k">while</span> <span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="kt">float</span> <span class="n">value</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">sensor</span><span class="o">-&gt;</span><span class="nf">read</span><span class="p">(</span><span class="o">&amp;</span><span class="n">value</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nf">publish_data</span><span class="p">(</span><span class="n">value</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="nf">vTaskDelay</span><span class="p">(</span><span class="nf">pdMS_TO_TICKS</span><span class="p">(</span><span class="mi">1000</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 3. 配置与代码分离
</span></span></span><span class="line"><span class="cl"><span class="c1">// config.h
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#define CONFIG_MQTT_BROKER      &#34;mqtt.example.com&#34;
</span></span></span><span class="line"><span class="cl"><span class="cp">#define CONFIG_MQTT_PORT        1883
</span></span></span><span class="line"><span class="cl"><span class="cp">#define CONFIG_MQTT_KEEPALIVE   60
</span></span></span><span class="line"><span class="cl"><span class="cp">#define CONFIG_WIFI_SSID        &#34;MyNetwork&#34;
</span></span></span><span class="line"><span class="cl"><span class="cp">#define CONFIG_WIFI_PASSWORD    &#34;SecretPassword&#34;
</span></span></span></code></pre></div><h2 id="ota-升级firmware-over-the-air">OTA 升级（Firmware Over-The-Air）</h2>
<h3 id="21-ota-架构设计">2.1 OTA 架构设计</h3>
<figure>
<svg viewBox="0 0 700 400" xmlns="http://www.w3.org/2000/svg">
  <defs>
    <style>
      .box { fill: #1a1a2e; stroke: #4a9eff; stroke-width: 2; rx: 6; }
      .text { fill: #e0e0e0; font-family: Arial, sans-serif; font-size: 13px; text-anchor: middle; }
      .label { fill: #4a9eff; font-family: Arial, sans-serif; font-size: 15px; font-weight: bold; }
      .arrow { stroke: #4a9eff; stroke-width: 2; fill: none; marker-end: url(#arrow); }
      .dashed { stroke: #e74c3c; stroke-width: 2; stroke-dasharray: 5,3; }
    </style>
    <marker id="arrow" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
      <polygon points="0 0, 10 3.5, 0 7" fill="#4a9eff"/>
    </marker>
  </defs>
  <!-- 云端 -->
  <rect class="box" x="50" y="20" width="200" height="100"/>
  <text class="label" x="150" y="55">OTA 服务器</text>
  <text class="text" x="150" y="85">固件存储 + 版本管理</text>
  <text class="text" x="150" y="105">签名服务</text>
  <!-- 通信 -->
  <line class="arrow" x1="250" y1="70" x2="350" y2="70"/>
  <text class="text" x="300" y="60" font-size="11">HTTPS 下载</text>
  <!-- 设备 -->
  <rect class="box" x="350" y="20" width="300" height="150"/>
  <text class="label" x="500" y="50">IoT 设备</text>
  <!-- Bootloader -->
  <rect class="box" x="370" y="65" width="120" height="40"/>
  <text class="text" x="430" y="90">Bootloader</text>
  <!-- A/B 分区 -->
  <rect class="box" x="510" y="65" width="60" height="40" style="fill:#2a3a2e;stroke:#388e3c;"/>
  <text class="text" x="540" y="85">A 区</text>
  <text class="text" x="540" y="100" fill="#388e3c">运行中</text>
  <rect class="box" x="580" y="65" width="60" height="40" style="fill:#3a2a2e;stroke:#e74c3c;"/>
  <text class="text" x="610" y="85">B 区</text>
  <text class="text" x="610" y="100" fill="#e74c3c">更新中</text>
  <!-- 箭头回云端 -->
  <line class="dashed" x1="350" y1="100" x2="250" y2="100"/>
  <text class="text" x="300" y="120" font-size="11">状态上报</text>
  <!-- 设备内部流程 -->
  <line class="arrow" x1="490" y1="85" x2="510" y2="85"/>
  <text class="text" x="500" y="75" font-size="10">验证</text>
</svg>
<figcaption style="text-align:center;color:#888;font-size:12px;margin-top:8px;">OTA 升级架构图</figcaption>
</figure>
<h3 id="22-ab-分区升级策略">2.2 A/B 分区升级策略</h3>
<p>A/B 分区（也称为双分区）是最安全的 OTA 升级方案：</p>
<table>
<thead>
<tr>
<th>特性</th>
<th>A 分区</th>
<th>B 分区</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>当前状态</strong></td>
<td>运行中（Active）</td>
<td>空闲/更新中（Inactive）</td>
</tr>
<tr>
<td><strong>升级时</strong></td>
<td>保持运行</td>
<td>写入新固件</td>
</tr>
<tr>
<td><strong>升级后</strong></td>
<td>变为备份</td>
<td>变为活动</td>
</tr>
<tr>
<td><strong>回滚</strong></td>
<td>可回滚到 A</td>
<td>可回滚到 B</td>
</tr>
</tbody>
</table>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// Flash 分区表示例（ESP32 风格）
</span></span></span><span class="line"><span class="cl"><span class="c1">// 假设 Flash 总大小 4MB
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">uint32_t</span> <span class="n">magic</span><span class="p">;</span>         <span class="c1">// 分区表魔数
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="kt">uint32_t</span> <span class="n">version</span><span class="p">;</span>       <span class="c1">// 分区表版本
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="kt">uint32_t</span> <span class="n">ota_seq</span><span class="p">;</span>       <span class="c1">// OTA 序列号（判断哪个分区是活动的）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="kt">uint8_t</span>  <span class="n">ota_data</span><span class="p">[</span><span class="mi">32</span><span class="p">];</span>  <span class="c1">// OTA 状态数据
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="kt">uint32_t</span> <span class="n">crc</span><span class="p">;</span>           <span class="c1">// CRC32 校验
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span> <span class="kt">ota_data_t</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 分区布局
</span></span></span><span class="line"><span class="cl"><span class="c1">// 0x0000 - 0x1000:   引导加载程序（Bootloader）
</span></span></span><span class="line"><span class="cl"><span class="c1">// 0x1000 - 0x2000:   分区表
</span></span></span><span class="line"><span class="cl"><span class="c1">// 0x2000 - 0x3000:   OTA 数据（ota_data）
</span></span></span><span class="line"><span class="cl"><span class="c1">// 0x3000 - 0x100000: 应用 A 区（约 988KB）
</span></span></span><span class="line"><span class="cl"><span class="c1">// 0x100000 - 0x1FD000: 应用 B 区（约 988KB）
</span></span></span><span class="line"><span class="cl"><span class="c1">// 0x1FD000 - 0x200000: NVS（非易失存储）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="c1">// OTA 升级流程
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">typedef</span> <span class="k">enum</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">OTA_STATE_IDLE</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">OTA_STATE_DOWNLOADING</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">OTA_STATE_VERIFYING</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">OTA_STATE_FLASHING</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">OTA_STATE_VALIDATED</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">OTA_STATE_INVALID</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="kt">ota_state_t</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">ota_state_t</span> <span class="n">current_ota_state</span> <span class="o">=</span> <span class="n">OTA_STATE_IDLE</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// OTA 任务
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="nf">ota_task</span><span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="n">pvParameters</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">while</span> <span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// 1. 检查是否有新版本
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="k">if</span> <span class="p">(</span><span class="nf">check_new_firmware_version</span><span class="p">())</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">current_ota_state</span> <span class="o">=</span> <span class="n">OTA_STATE_DOWNLOADING</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            
</span></span><span class="line"><span class="cl">            <span class="c1">// 2. 下载新固件到非活动分区
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="kt">uint32_t</span> <span class="n">inactive_partition</span> <span class="o">=</span> <span class="nf">get_inactive_partition</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">            <span class="kt">int</span> <span class="n">ret</span> <span class="o">=</span> <span class="nf">download_firmware</span><span class="p">(</span><span class="n">inactive_partition</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            
</span></span><span class="line"><span class="cl">            <span class="k">if</span> <span class="p">(</span><span class="n">ret</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="n">current_ota_state</span> <span class="o">=</span> <span class="n">OTA_STATE_VERIFYING</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">                
</span></span><span class="line"><span class="cl">                <span class="c1">// 3. 验证签名
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>                <span class="k">if</span> <span class="p">(</span><span class="nf">verify_firmware_signature</span><span class="p">(</span><span class="n">inactive_partition</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                    <span class="n">current_ota_state</span> <span class="o">=</span> <span class="n">OTA_STATE_VALIDATED</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">                    
</span></span><span class="line"><span class="cl">                    <span class="c1">// 4. 更新 OTA 数据，标记下次从新分区启动
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>                    <span class="nf">set_next_boot_partition</span><span class="p">(</span><span class="n">inactive_partition</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">                    
</span></span><span class="line"><span class="cl">                    <span class="c1">// 5. 重启
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>                    <span class="nf">esp_restart</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">                <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                    <span class="n">current_ota_state</span> <span class="o">=</span> <span class="n">OTA_STATE_INVALID</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">                    <span class="nf">report_error</span><span class="p">(</span><span class="s">&#34;Signature verification failed&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">                <span class="p">}</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="n">current_ota_state</span> <span class="o">=</span> <span class="n">OTA_STATE_IDLE</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">                <span class="nf">report_error</span><span class="p">(</span><span class="s">&#34;Download failed&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="c1">// 每 6 小时检查一次
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="nf">vTaskDelay</span><span class="p">(</span><span class="nf">pdMS_TO_TICKS</span><span class="p">(</span><span class="mi">6</span> <span class="o">*</span> <span class="mi">60</span> <span class="o">*</span> <span class="mi">60</span> <span class="o">*</span> <span class="mi">1000</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="23-安全-ota-实现">2.3 安全 OTA 实现</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// 完整的 OTA 升级流程（包含安全验证）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#include</span> <span class="cpf">&lt;mbedtls/sha256.h&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;mbedtls/pk.h&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">uint32_t</span> <span class="n">magic</span><span class="p">;</span>             <span class="c1">// 魔数：0xABCDEF00
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="kt">uint32_t</span> <span class="n">version</span><span class="p">;</span>           <span class="c1">// 固件版本号
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="kt">uint32_t</span> <span class="n">timestamp</span><span class="p">;</span>         <span class="c1">// 编译时间戳
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="kt">uint32_t</span> <span class="n">image_size</span><span class="p">;</span>        <span class="c1">// 固件大小
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="kt">uint8_t</span>  <span class="n">image_hash</span><span class="p">[</span><span class="mi">32</span><span class="p">];</span>    <span class="c1">// SHA-256 哈希
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="kt">uint8_t</span>  <span class="n">signature</span><span class="p">[</span><span class="mi">64</span><span class="p">];</span>     <span class="c1">// ECDSA 签名
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span> <span class="kt">firmware_header_t</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">perform_ota_update</span><span class="p">(</span><span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">firmware_url</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">firmware_header_t</span> <span class="n">header</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">uint8_t</span> <span class="n">download_buffer</span><span class="p">[</span><span class="mi">4096</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">    <span class="kt">uint32_t</span> <span class="n">total_downloaded</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 1. 下载并解析头部
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="kt">int</span> <span class="n">ret</span> <span class="o">=</span> <span class="nf">http_download_range</span><span class="p">(</span><span class="n">firmware_url</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">header</span><span class="p">),</span> <span class="o">&amp;</span><span class="n">header</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">ret</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nf">ESP_LOGE</span><span class="p">(</span><span class="s">&#34;OTA&#34;</span><span class="p">,</span> <span class="s">&#34;Failed to download header&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 2. 验证魔数
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">if</span> <span class="p">(</span><span class="n">header</span><span class="p">.</span><span class="n">magic</span> <span class="o">!=</span> <span class="mh">0xABCDEF00</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nf">ESP_LOGE</span><span class="p">(</span><span class="s">&#34;OTA&#34;</span><span class="p">,</span> <span class="s">&#34;Invalid firmware magic&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="o">-</span><span class="mi">2</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 3. 验证签名（使用固化在设备中的公钥）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">ret</span> <span class="o">=</span> <span class="nf">verify_signature_with_public_key</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="o">&amp;</span><span class="n">header</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">header</span><span class="p">.</span><span class="n">signature</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="k">sizeof</span><span class="p">(</span><span class="n">header</span><span class="p">.</span><span class="n">signature</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="n">g_device_public_key</span>
</span></span><span class="line"><span class="cl">    <span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">ret</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nf">ESP_LOGE</span><span class="p">(</span><span class="s">&#34;OTA&#34;</span><span class="p">,</span> <span class="s">&#34;Signature verification failed&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="o">-</span><span class="mi">3</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 4. 擦除目标分区
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="kt">uint32_t</span> <span class="n">target_partition</span> <span class="o">=</span> <span class="nf">get_inactive_partition</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="nf">spi_flash_erase_range</span><span class="p">(</span><span class="n">target_partition</span><span class="p">,</span> <span class="n">header</span><span class="p">.</span><span class="n">image_size</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 5. 下载固件主体并写入 Flash
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">ESP_LOGI</span><span class="p">(</span><span class="s">&#34;OTA&#34;</span><span class="p">,</span> <span class="s">&#34;Downloading firmware...&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="kt">uint32_t</span> <span class="n">offset</span> <span class="o">=</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">header</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="k">while</span> <span class="p">(</span><span class="n">total_downloaded</span> <span class="o">&lt;</span> <span class="n">header</span><span class="p">.</span><span class="n">image_size</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="kt">uint32_t</span> <span class="n">chunk_size</span> <span class="o">=</span> <span class="nf">MIN</span><span class="p">(</span><span class="k">sizeof</span><span class="p">(</span><span class="n">download_buffer</span><span class="p">),</span> <span class="n">header</span><span class="p">.</span><span class="n">image_size</span> <span class="o">-</span> <span class="n">total_downloaded</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="n">ret</span> <span class="o">=</span> <span class="nf">http_download_range</span><span class="p">(</span><span class="n">firmware_url</span><span class="p">,</span> <span class="n">offset</span><span class="p">,</span> <span class="n">chunk_size</span><span class="p">,</span> <span class="n">download_buffer</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">ret</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nf">ESP_LOGE</span><span class="p">(</span><span class="s">&#34;OTA&#34;</span><span class="p">,</span> <span class="s">&#34;Download failed at offset %lu&#34;</span><span class="p">,</span> <span class="n">offset</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="o">-</span><span class="mi">4</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="c1">// 写入 Flash
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="nf">spi_flash_write</span><span class="p">(</span><span class="n">target_partition</span> <span class="o">+</span> <span class="n">offset</span><span class="p">,</span> <span class="n">download_buffer</span><span class="p">,</span> <span class="n">chunk_size</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="n">offset</span> <span class="o">+=</span> <span class="n">chunk_size</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">total_downloaded</span> <span class="o">+=</span> <span class="n">chunk_size</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="c1">// 报告进度
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="kt">int</span> <span class="n">progress</span> <span class="o">=</span> <span class="p">(</span><span class="n">total_downloaded</span> <span class="o">*</span> <span class="mi">100</span><span class="p">)</span> <span class="o">/</span> <span class="n">header</span><span class="p">.</span><span class="n">image_size</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="nf">ESP_LOGI</span><span class="p">(</span><span class="s">&#34;OTA&#34;</span><span class="p">,</span> <span class="s">&#34;Progress: %d%%&#34;</span><span class="p">,</span> <span class="n">progress</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="nf">report_ota_progress</span><span class="p">(</span><span class="n">progress</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 6. 验证下载的哈希
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="kt">uint8_t</span> <span class="n">calculated_hash</span><span class="p">[</span><span class="mi">32</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">    <span class="nf">calculate_flash_hash</span><span class="p">(</span><span class="n">target_partition</span><span class="p">,</span> <span class="n">header</span><span class="p">.</span><span class="n">image_size</span><span class="p">,</span> <span class="n">calculated_hash</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="nf">memcmp</span><span class="p">(</span><span class="n">calculated_hash</span><span class="p">,</span> <span class="n">header</span><span class="p">.</span><span class="n">image_hash</span><span class="p">,</span> <span class="mi">32</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nf">ESP_LOGE</span><span class="p">(</span><span class="s">&#34;OTA&#34;</span><span class="p">,</span> <span class="s">&#34;Hash verification failed&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="o">-</span><span class="mi">5</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 7. 设置下次启动分区
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">set_next_boot_partition</span><span class="p">(</span><span class="n">target_partition</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="nf">ESP_LOGI</span><span class="p">(</span><span class="s">&#34;OTA&#34;</span><span class="p">,</span> <span class="s">&#34;OTA update successful, rebooting...&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 8. 重启
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">vTaskDelay</span><span class="p">(</span><span class="nf">pdMS_TO_TICKS</span><span class="p">(</span><span class="mi">1000</span><span class="p">));</span>  <span class="c1">// 等待日志输出
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">esp_restart</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 回滚机制：如果新固件启动失败，自动回滚到旧版本
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="nf">app_main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 检查启动原因
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="kt">esp_reset_reason_t</span> <span class="n">reset_reason</span> <span class="o">=</span> <span class="nf">esp_reset_reason</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">reset_reason</span> <span class="o">==</span> <span class="n">ESP_RST_PANIC</span> <span class="o">||</span> <span class="n">reset_reason</span> <span class="o">==</span> <span class="n">ESP_RST_INT_WDT</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// 异常重启，可能是新固件有问题
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="nf">ESP_LOGE</span><span class="p">(</span><span class="s">&#34;BOOT&#34;</span><span class="p">,</span> <span class="s">&#34;Abnormal reset detected, rolling back...&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="c1">// 回滚到之前的分区
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="nf">rollback_to_previous_partition</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 标记当前分区为有效（看门狗机制）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">mark_current_partition_valid</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 正常运行
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">run_application</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h2 id="低功耗设计">低功耗设计</h2>
<h3 id="31-功耗分析">3.1 功耗分析</h3>
<table>
<thead>
<tr>
<th>模式</th>
<th>ESP32 典型电流</th>
<th>STM32L4 典型电流</th>
<th>适用场景</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>运行（全速）</strong></td>
<td>80-240mA</td>
<td>10-50mA</td>
<td>数据处理、通信</td>
</tr>
<tr>
<td><strong>运行（降频）</strong></td>
<td>40-100mA</td>
<td>5-20mA</td>
<td>常规任务</td>
</tr>
<tr>
<td><strong>睡眠（Light Sleep）</strong></td>
<td>0.8mA</td>
<td>5-10μA</td>
<td>等待事件</td>
</tr>
<tr>
<td><strong>深度睡眠（Deep Sleep）</strong></td>
<td>10-20μA</td>
<td>1-3μA</td>
<td>电池供电</td>
</tr>
<tr>
<td><strong>休眠（Hibernate）</strong></td>
<td>2.5μA</td>
<td>0.5μA</td>
<td>超长待机</td>
</tr>
</tbody>
</table>
<h3 id="32-动态电压频率调节dvfs">3.2 动态电压频率调节（DVFS）</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// ESP32 DVFS 配置
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#include</span> <span class="cpf">&#34;esp_pm.h&#34;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">configure_dvfs</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">esp_pm_config_esp32_t</span> <span class="n">pm_config</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="p">.</span><span class="n">max_freq_mhz</span> <span class="o">=</span> <span class="mi">240</span><span class="p">,</span>      <span class="c1">// 最大频率
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="p">.</span><span class="n">min_freq_mhz</span> <span class="o">=</span> <span class="mi">80</span><span class="p">,</span>       <span class="c1">// 最小频率
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="p">.</span><span class="n">light_sleep_enable</span> <span class="o">=</span> <span class="nb">true</span>  <span class="c1">// 启用 Light Sleep
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="p">};</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="nf">esp_pm_configure</span><span class="p">(</span><span class="o">&amp;</span><span class="n">pm_config</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 根据负载动态调整频率
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="nf">adjust_frequency_based_on_load</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">static</span> <span class="kt">uint32_t</span> <span class="n">idle_count</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="nf">is_system_idle</span><span class="p">())</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">idle_count</span><span class="o">++</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">idle_count</span> <span class="o">&gt;</span> <span class="mi">100</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="c1">// 空闲时间长，降低频率
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="nf">esp_pm_cpu_freq_set</span><span class="p">(</span><span class="n">ESP_PM_CPU_FREQ_MAX_80M</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">idle_count</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="c1">// 有任务，提升频率
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="nf">esp_pm_cpu_freq_set</span><span class="p">(</span><span class="n">ESP_PM_CPU_FREQ_MAX_240M</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="33-深度睡眠实现">3.3 深度睡眠实现</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// STM32 深度睡眠配置
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#include</span> <span class="cpf">&lt;stm32l4xx_hal.h&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">enter_deep_sleep</span><span class="p">(</span><span class="kt">uint32_t</span> <span class="n">wake_time_ms</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 1. 配置唤醒源（RTC、外部中断等）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">RTC_HandleTypeDef</span> <span class="n">hrtc</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 配置 RTC 唤醒定时器
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">hrtc</span><span class="p">.</span><span class="n">Instance</span> <span class="o">=</span> <span class="n">RTC</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nf">HAL_RTCEx_SetWakeUpTimer</span><span class="p">(</span><span class="o">&amp;</span><span class="n">hrtc</span><span class="p">,</span> <span class="n">wake_time_ms</span> <span class="o">/</span> <span class="mi">1000</span><span class="p">,</span> <span class="n">RTC_WAKEUPCLOCK_CK_SPRE_16BITS</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 2. 配置 GPIO 为低功耗模式
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">GPIO_InitTypeDef</span> <span class="n">GPIO_InitStruct</span> <span class="o">=</span> <span class="p">{</span><span class="mi">0</span><span class="p">};</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 所有未使用引脚设为模拟输入（最低功耗）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="mi">16</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nf">is_pin_used</span><span class="p">(</span><span class="n">i</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">GPIO_InitStruct</span><span class="p">.</span><span class="n">Pin</span> <span class="o">=</span> <span class="p">(</span><span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="n">i</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="n">GPIO_InitStruct</span><span class="p">.</span><span class="n">Mode</span> <span class="o">=</span> <span class="n">GPIO_MODE_ANALOG</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="n">GPIO_InitStruct</span><span class="p">.</span><span class="n">Pull</span> <span class="o">=</span> <span class="n">GPIO_NOPULL</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="nf">HAL_GPIO_Init</span><span class="p">(</span><span class="n">GPIOA</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">GPIO_InitStruct</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 3. 关闭未使用外设时钟
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">__HAL_RCC_USART2_CLK_DISABLE</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="nf">__HAL_RCC_SPI2_CLK_DISABLE</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="nf">__HAL_RCC_I2C1_CLK_DISABLE</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="nf">__HAL_RCC_ADC_CLK_DISABLE</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 4. 禁用 SysTick 中断（睡眠期间不需要）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">SysTick</span><span class="o">-&gt;</span><span class="n">CTRL</span> <span class="o">&amp;=</span> <span class="o">~</span><span class="n">SysTick_CTRL_TICKINT_Msk</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 5. 进入 STOP2 模式（最低功耗，保留 SRAM 内容）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">HAL_PWREx_EnterSTOP2Mode</span><span class="p">(</span><span class="n">PWR_STOPENTRY_LOWPOWER</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 唤醒后从这里继续执行
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="c1">// 6. 恢复系统时钟
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">SystemClock_Config</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 7. 恢复外设时钟
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">__HAL_RCC_USART2_CLK_ENABLE</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="nf">__HAL_RCC_SPI2_CLK_ENABLE</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 8. 重新配置 GPIO
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">gpio_init_all</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 9. 清除唤醒标志
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">__HAL_PWR_CLEAR_FLAG</span><span class="p">(</span><span class="n">PWR_FLAG_WU</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 电池供电设备的功耗优化任务
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="nf">power_management_task</span><span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="n">pvParameters</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">uint32_t</span> <span class="n">battery_voltage</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="k">while</span> <span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// 读取电池电压
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="n">battery_voltage</span> <span class="o">=</span> <span class="nf">read_battery_voltage</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">battery_voltage</span> <span class="o">&lt;</span> <span class="mi">3300</span><span class="p">)</span> <span class="p">{</span>  <span class="c1">// 低于 3.3V
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="c1">// 进入省电模式
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="nf">enter_power_saving_mode</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">battery_voltage</span> <span class="o">&lt;</span> <span class="mi">3000</span><span class="p">)</span> <span class="p">{</span>  <span class="c1">// 低于 3.0V
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="c1">// 紧急模式：只保留基本功能
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="nf">enter_emergency_mode</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="c1">// 正常模式
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="nf">enter_normal_mode</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="c1">// 每 5 分钟检查一次
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="nf">vTaskDelay</span><span class="p">(</span><span class="nf">pdMS_TO_TICKS</span><span class="p">(</span><span class="mi">5</span> <span class="o">*</span> <span class="mi">60</span> <span class="o">*</span> <span class="mi">1000</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="34-无线通信功耗优化">3.4 无线通信功耗优化</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// WiFi 功耗优化（ESP32）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#include</span> <span class="cpf">&#34;esp_wifi.h&#34;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">configure_wifi_power_save</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 启用 WiFi 节能模式
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">esp_wifi_set_ps</span><span class="p">(</span><span class="n">WIFI_PS_MAX_MODEM</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 配置 DTIM 监听间隔（越大越省电，但延迟越高）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="c1">// DTIM = 3 表示每 3 个信标帧唤醒一次
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="kt">wifi_ps_type_t</span> <span class="n">ps_type</span> <span class="o">=</span> <span class="n">WIFI_PS_MAX_MODEM</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nf">esp_wifi_set_ps</span><span class="p">(</span><span class="n">ps_type</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// BLE 广播功耗优化
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#include</span> <span class="cpf">&#34;nimble/nimble_port.h&#34;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">configure_ble_advertising</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 配置广播参数
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">struct</span> <span class="n">ble_gap_adv_params</span> <span class="n">adv_params</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="n">adv_params</span><span class="p">.</span><span class="n">conn_mode</span> <span class="o">=</span> <span class="n">BLE_GAP_CONN_MODE_NON</span><span class="p">;</span>  <span class="c1">// 不可连接
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">adv_params</span><span class="p">.</span><span class="n">disc_mode</span> <span class="o">=</span> <span class="n">BLE_GAP_DISC_MODE_GEN</span><span class="p">;</span>  <span class="c1">// 可发现
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">adv_params</span><span class="p">.</span><span class="n">itvl_min</span> <span class="o">=</span> <span class="mh">0x0640</span><span class="p">;</span>  <span class="c1">// 1 秒（0x0640 = 1000ms）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">adv_params</span><span class="p">.</span><span class="n">itvl_max</span> <span class="o">=</span> <span class="mh">0x0C80</span><span class="p">;</span>  <span class="c1">// 2 秒
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    
</span></span><span class="line"><span class="cl">    <span class="c1">// 较长的广播间隔 = 更低的功耗
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">ble_gap_adv_start</span><span class="p">(</span><span class="n">OWN_ADDR_TYPE_PUBLIC</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">,</span> <span class="n">BLE_HS_FOREVER</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                      <span class="o">&amp;</span><span class="n">adv_params</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h2 id="无线通信协议对比">无线通信协议对比</h2>
<h3 id="41-主流-iot-无线协议">4.1 主流 IoT 无线协议</h3>
<table>
<thead>
<tr>
<th>协议</th>
<th>频率</th>
<th>范围</th>
<th>速率</th>
<th>功耗</th>
<th>典型应用</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>WiFi 4 (802.11n)</strong></td>
<td>2.4/5GHz</td>
<td>50m</td>
<td>150Mbps</td>
<td>高</td>
<td>视频、大数据</td>
</tr>
<tr>
<td><strong>WiFi 6 (802.11ax)</strong></td>
<td>2.4/5/6GHz</td>
<td>50m</td>
<td>600Mbps</td>
<td>中高</td>
<td>高密度 IoT</td>
</tr>
<tr>
<td><strong>Bluetooth 5</strong></td>
<td>2.4GHz</td>
<td>10-100m</td>
<td>2Mbps</td>
<td>低</td>
<td>可穿戴、信标</td>
</tr>
<tr>
<td><strong>BLE Mesh</strong></td>
<td>2.4GHz</td>
<td>多跳</td>
<td>1Mbps</td>
<td>低</td>
<td>智能家居</td>
</tr>
<tr>
<td><strong>Zigbee</strong></td>
<td>2.4GHz</td>
<td>10-100m</td>
<td>250kbps</td>
<td>低</td>
<td>工业、家居</td>
</tr>
<tr>
<td><strong>Thread</strong></td>
<td>2.4GHz</td>
<td>多跳</td>
<td>250kbps</td>
<td>低</td>
<td>Matter 生态</td>
</tr>
<tr>
<td><strong>LoRaWAN</strong></td>
<td>Sub-1GHz</td>
<td>2-15km</td>
<td>0.3-50kbps</td>
<td>极低</td>
<td>广域 IoT</td>
</tr>
<tr>
<td><strong>NB-IoT</strong></td>
<td>授权频段</td>
<td>1-10km</td>
<td>20-250kbps</td>
<td>低</td>
<td>蜂窝 IoT</td>
</tr>
<tr>
<td><strong>LTE-M</strong></td>
<td>授权频段</td>
<td>1-10km</td>
<td>1Mbps</td>
<td>中</td>
<td>移动 IoT</td>
</tr>
</tbody>
</table>
<h3 id="42-mqtt-协议详解">4.2 MQTT 协议详解</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// MQTT 客户端实现（使用 Paho MQTT）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#include</span> <span class="cpf">&lt;MQTTClient.h&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="c1">// MQTT 配置
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">broker_url</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">uint16_t</span> <span class="n">port</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">client_id</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">username</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">password</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">publish_topic</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">subscribe_topic</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">uint16_t</span> <span class="n">keepalive</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">bool</span> <span class="n">clean_session</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="kt">mqtt_config_t</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 全局 MQTT 客户端
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">static</span> <span class="n">MQTTClient</span> <span class="n">client</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">mqtt_config_t</span> <span class="n">config</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">broker_url</span> <span class="o">=</span> <span class="s">&#34;mqtt.example.com&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">port</span> <span class="o">=</span> <span class="mi">1883</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">client_id</span> <span class="o">=</span> <span class="s">&#34;iot_device_001&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">username</span> <span class="o">=</span> <span class="s">&#34;device_user&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">password</span> <span class="o">=</span> <span class="s">&#34;device_pass&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">publish_topic</span> <span class="o">=</span> <span class="s">&#34;devices/001/data&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">subscribe_topic</span> <span class="o">=</span> <span class="s">&#34;devices/001/command&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">keepalive</span> <span class="o">=</span> <span class="mi">60</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">clean_session</span> <span class="o">=</span> <span class="nb">false</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 连接回调
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="nf">on_connected</span><span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="n">context</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nf">ESP_LOGI</span><span class="p">(</span><span class="s">&#34;MQTT&#34;</span><span class="p">,</span> <span class="s">&#34;Connected to broker&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 订阅命令主题
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">MQTTClient_subscribe</span><span class="p">(</span><span class="n">client</span><span class="p">,</span> <span class="n">config</span><span class="p">.</span><span class="n">subscribe_topic</span><span class="p">,</span> <span class="mi">1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 发布上线消息
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">MQTTClient_publish</span><span class="p">(</span><span class="n">client</span><span class="p">,</span> <span class="s">&#34;devices/001/status&#34;</span><span class="p">,</span> 
</span></span><span class="line"><span class="cl">                       <span class="nf">strlen</span><span class="p">(</span><span class="s">&#34;online&#34;</span><span class="p">),</span> <span class="s">&#34;online&#34;</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 消息到达回调
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="nf">on_message_arrived</span><span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="n">context</span><span class="p">,</span> <span class="kt">char</span> <span class="o">*</span><span class="n">topicName</span><span class="p">,</span> <span class="kt">int</span> <span class="n">topicLen</span><span class="p">,</span> 
</span></span><span class="line"><span class="cl">                        <span class="n">MQTTClient_message</span> <span class="o">*</span><span class="n">message</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nf">ESP_LOGI</span><span class="p">(</span><span class="s">&#34;MQTT&#34;</span><span class="p">,</span> <span class="s">&#34;Message arrived on topic %s&#34;</span><span class="p">,</span> <span class="n">topicName</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 处理命令
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">handle_command</span><span class="p">(</span><span class="n">message</span><span class="o">-&gt;</span><span class="n">payload</span><span class="p">,</span> <span class="n">message</span><span class="o">-&gt;</span><span class="n">payloadlen</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="nf">MQTTClient_freeMessage</span><span class="p">(</span><span class="o">&amp;</span><span class="n">message</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nf">MQTTClient_free</span><span class="p">(</span><span class="n">topicName</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 连接丢失回调
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="nf">on_connection_lost</span><span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="n">context</span><span class="p">,</span> <span class="kt">char</span> <span class="o">*</span><span class="n">cause</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nf">ESP_LOGW</span><span class="p">(</span><span class="s">&#34;MQTT&#34;</span><span class="p">,</span> <span class="s">&#34;Connection lost: %s&#34;</span><span class="p">,</span> <span class="n">cause</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 自动重连
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">MQTTClient_reconnect</span><span class="p">(</span><span class="n">client</span><span class="p">,</span> <span class="mi">5000</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// MQTT 初始化
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">int</span> <span class="nf">mqtt_init</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nf">MQTTClient_create</span><span class="p">(</span><span class="o">&amp;</span><span class="n">client</span><span class="p">,</span> <span class="n">config</span><span class="p">.</span><span class="n">broker_url</span><span class="p">,</span> <span class="n">config</span><span class="p">.</span><span class="n">client_id</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                      <span class="n">MQTTCLIENT_PERSISTENCE_NONE</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="n">MQTTClient_callbacks</span> <span class="n">callbacks</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="p">.</span><span class="n">on_connect</span> <span class="o">=</span> <span class="n">on_connected</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="p">.</span><span class="n">on_message</span> <span class="o">=</span> <span class="n">on_message_arrived</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="p">.</span><span class="n">on_connection_lost</span> <span class="o">=</span> <span class="n">on_connection_lost</span>
</span></span><span class="line"><span class="cl">    <span class="p">};</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="nf">MQTTClient_setCallbacks</span><span class="p">(</span><span class="n">client</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">callbacks</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="n">MQTTClient_connectOptions</span> <span class="n">conn_opts</span> <span class="o">=</span> <span class="n">MQTTClient_connectOptions_initializer</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">conn_opts</span><span class="p">.</span><span class="n">keepAliveInterval</span> <span class="o">=</span> <span class="n">config</span><span class="p">.</span><span class="n">keepalive</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">conn_opts</span><span class="p">.</span><span class="n">cleansession</span> <span class="o">=</span> <span class="n">config</span><span class="p">.</span><span class="n">clean_session</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">conn_opts</span><span class="p">.</span><span class="n">username</span> <span class="o">=</span> <span class="n">config</span><span class="p">.</span><span class="n">username</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">conn_opts</span><span class="p">.</span><span class="n">password</span> <span class="o">=</span> <span class="n">config</span><span class="p">.</span><span class="n">password</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">conn_opts</span><span class="p">.</span><span class="n">automaticReconnect</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="n">rc</span> <span class="o">=</span> <span class="nf">MQTTClient_connect</span><span class="p">(</span><span class="n">client</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">conn_opts</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">rc</span> <span class="o">!=</span> <span class="n">MQTTCLIENT_SUCCESS</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nf">ESP_LOGE</span><span class="p">(</span><span class="s">&#34;MQTT&#34;</span><span class="p">,</span> <span class="s">&#34;Failed to connect: %d&#34;</span><span class="p">,</span> <span class="n">rc</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 发布数据
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">int</span> <span class="nf">mqtt_publish_data</span><span class="p">(</span><span class="k">const</span> <span class="kt">uint8_t</span> <span class="o">*</span><span class="n">data</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">len</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">MQTTClient_message</span> <span class="n">pubmsg</span> <span class="o">=</span> <span class="n">MQTTClient_message_initializer</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">pubmsg</span><span class="p">.</span><span class="n">payload</span> <span class="o">=</span> <span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="p">)</span><span class="n">data</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">pubmsg</span><span class="p">.</span><span class="n">payloadlen</span> <span class="o">=</span> <span class="n">len</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">pubmsg</span><span class="p">.</span><span class="n">qos</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>  <span class="c1">// QoS 1：至少送达一次
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">pubmsg</span><span class="p">.</span><span class="n">retained</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nf">MQTTClient_publish</span><span class="p">(</span><span class="n">client</span><span class="p">,</span> <span class="n">config</span><span class="p">.</span><span class="n">publish_topic</span><span class="p">,</span> 
</span></span><span class="line"><span class="cl">                              <span class="n">len</span><span class="p">,</span> <span class="n">data</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="43-coap-协议轻量级替代">4.3 CoAP 协议（轻量级替代）</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// CoAP 客户端实现（使用 libcoap）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#include</span> <span class="cpf">&lt;coap3/coap.h&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="c1">// CoAP 资源定义
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">static</span> <span class="kt">coap_resource_t</span> <span class="o">*</span><span class="n">resources</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nb">NULL</span>  <span class="c1">// 资源列表结束标记
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// CoAP 请求处理
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">static</span> <span class="kt">void</span>
</span></span><span class="line"><span class="cl"><span class="nf">helloworld_get</span><span class="p">(</span><span class="kt">coap_context_t</span> <span class="o">*</span><span class="n">ctx</span><span class="p">,</span> <span class="k">struct</span> <span class="kt">coap_resource_t</span> <span class="o">*</span><span class="n">resource</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">               <span class="kt">coap_session_t</span> <span class="o">*</span><span class="n">session</span><span class="p">,</span> <span class="kt">coap_pdu_t</span> <span class="o">*</span><span class="n">pdu</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">               <span class="kt">coap_binary_t</span> <span class="o">*</span><span class="n">token</span><span class="p">,</span> <span class="kt">coap_string_t</span> <span class="o">*</span><span class="n">query</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">               <span class="kt">coap_pdu_t</span> <span class="o">*</span><span class="n">response</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">payload</span> <span class="o">=</span> <span class="s">&#34;Hello, World!&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nf">coap_add_data</span><span class="p">(</span><span class="n">response</span><span class="p">,</span> <span class="nf">strlen</span><span class="p">(</span><span class="n">payload</span><span class="p">),</span> <span class="p">(</span><span class="k">const</span> <span class="kt">uint8_t</span> <span class="o">*</span><span class="p">)</span><span class="n">payload</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// CoAP 服务器初始化
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">coap_context_t</span> <span class="o">*</span><span class="nf">coap_server_init</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">coap_context_t</span> <span class="o">*</span><span class="n">ctx</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">coap_address_t</span> <span class="n">listen_addr</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 绑定到 5683 端口（CoAP 标准端口）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">coap_address_init</span><span class="p">(</span><span class="o">&amp;</span><span class="n">listen_addr</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">listen_addr</span><span class="p">.</span><span class="n">addr</span><span class="p">.</span><span class="n">sin</span><span class="p">.</span><span class="n">sin_family</span> <span class="o">=</span> <span class="n">AF_INET</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">listen_addr</span><span class="p">.</span><span class="n">addr</span><span class="p">.</span><span class="n">sin</span><span class="p">.</span><span class="n">sin_addr</span><span class="p">.</span><span class="n">s_addr</span> <span class="o">=</span> <span class="n">INADDR_ANY</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">listen_addr</span><span class="p">.</span><span class="n">addr</span><span class="p">.</span><span class="n">sin</span><span class="p">.</span><span class="n">sin_port</span> <span class="o">=</span> <span class="nf">htons</span><span class="p">(</span><span class="mi">5683</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="n">ctx</span> <span class="o">=</span> <span class="nf">coap_new_context</span><span class="p">(</span><span class="o">&amp;</span><span class="n">listen_addr</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">ctx</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="nb">NULL</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 注册资源
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="kt">coap_resource_t</span> <span class="o">*</span><span class="n">r</span> <span class="o">=</span> <span class="nf">coap_resource_init</span><span class="p">(</span><span class="nf">coap_make_str_const</span><span class="p">(</span><span class="s">&#34;hello&#34;</span><span class="p">),</span> <span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nf">coap_register_handler</span><span class="p">(</span><span class="n">r</span><span class="p">,</span> <span class="n">COAP_REQUEST_GET</span><span class="p">,</span> <span class="n">helloworld_get</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nf">coap_add_resource</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="n">r</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">ctx</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h2 id="总结">总结</h2>
<p>IoT 固件开发的核心要点：</p>
<ol>
<li><strong>分层架构</strong>：清晰的模块划分，便于维护和升级</li>
<li><strong>OTA 升级</strong>：A/B 分区、安全验证、回滚机制</li>
<li><strong>低功耗设计</strong>：DVFS、睡眠模式、无线优化</li>
<li><strong>无线通信</strong>：根据场景选择合适的协议（WiFi/BLE/LoRa/NB-IoT）</li>
<li><strong>安全性</strong>：安全启动、加密通信、安全存储</li>
</ol>
<p>掌握这些技能，你可以开发出可靠、安全、低功耗的 IoT 设备！</p>
<hr>
<p><em>本文基于 ESP32、STM32、nRF52 等平台实际项目经验整理，结合 2026 年最新 IoT 技术趋势编写。</em></p>
<h2 id="参考资料">参考资料</h2>
<ol>
<li>ESP-IDF Programming Guide - <a href="https://docs.espressif.com/">https://docs.espressif.com/</a></li>
<li>STM32CubeMX User Manual</li>
<li>Nordic nRF5 SDK Documentation</li>
<li>MQTT Version 5.0 Specification - OASIS Standard</li>
<li>The IoT Hacker&rsquo;s Handbook (2025)</li>
<li>Building Secure IoT Devices (2026)</li>
</ol>
<h2 id="参考资料-1">参考资料</h2>
<ol>
<li><a href="https://windowsforum.com/threads/microsoft-integrates-anthropics-model-context-protocol-for-ai-interoperability.357776">Microsoft Integrates Anthropic&rsquo;s Model Context Protocol for AI Interoperability | Windows Forum</a></li>
<li><a href="https://onereach.ai/blog/how-mcp-simplifies-ai-agent-development">How MCP Simplifies Enterprise AI Agent Development in 2025</a></li>
<li><a href="https://www.digitalapplied.com/blog/mcp-server-patterns-enterprise-ai-agents">MCP Server Patterns for Enterprise AI Agents in 2026</a></li>
</ol>
<hr>
<p><em>本文基于网络公开资料整理，结合嵌入式开发实践经验编写。</em></p>
]]></content:encoded></item><item><title>嵌入式开发：Risc-V Vector Extension Edge Ai Llm Inference 2025</title><link>https://tech-snippets.xyz/posts/risc-v-vector-extension-edge-ai-llm-inference-2025/</link><pubDate>Wed, 10 Jun 2026 03:00:00 +0800</pubDate><guid>https://tech-snippets.xyz/posts/risc-v-vector-extension-edge-ai-llm-inference-2025/</guid><description>嵌入式开发实战教程：RISC-V vector extension edge AI LLM inference 2025</description><content:encoded><![CDATA[<h2 id="引言">引言</h2>
<p>本文基于 2026 年最新行业资料整理，涵盖 RISC-V vector extension edge AI LLM inference 2025 的核心概念、开发流程和实战技巧。</p>
<h2 id="risc-v-vector-extension-edge-ai-llm-inference-2025-概述">RISC-V vector extension edge AI LLM inference 2025 概述</h2>
<p>嵌入式系统开发需要掌握硬件、软件和系统集成的综合技能。</p>
<h2 id="开发流程">开发流程</h2>
<ol>
<li><strong>需求分析</strong> → 确定功能、性能指标</li>
<li><strong>硬件选型</strong> → MCU、传感器、通信模块</li>
<li><strong>原理图设计</strong> → 电路设计、仿真验证</li>
<li><strong>软件架构</strong> → 分层设计、模块化</li>
<li><strong>编码实现</strong> → C/C++、RTOS、驱动</li>
<li><strong>调试测试</strong> → 单元测试、系统集成</li>
<li><strong>生产部署</strong> → 批量生产、OTA 升级</li>
</ol>
<h2 id="常用工具">常用工具</h2>
<ul>
<li><strong>IDE</strong>：Keil、IAR、STM32CubeIDE、VS Code</li>
<li><strong>调试器</strong>：J-Link、ST-Link、DAP-Link</li>
<li><strong>示波器</strong>：Siglent、Rigol、Keysight</li>
<li><strong>逻辑分析仪</strong>：Saleae、Kingst</li>
</ul>
<h2 id="学习路线">学习路线</h2>
<ol>
<li>C 语言基础 → 指针、结构体、内存管理</li>
<li>单片机原理 → GPIO、中断、定时器</li>
<li>外设驱动 → UART、SPI、I2C、ADC</li>
<li>RTOS → 任务、信号量、消息队列</li>
<li>项目实战 → 综合应用</li>
</ol>
<h2 id="参考资料">参考资料</h2>
<ol>
<li><a href="https://www.researchgate.net/publication/394012708_Accelerating_LLM_Inference_on_RISC-V_Edge_Devices_via_Vector_Extension_Optimization">Accelerating LLM Inference on RISC-V Edge Devices via Vector &hellip;</a></li>
<li><a href="https://www.andestech.com/en/2025/05/12/andes-technology-showcases-risc-v-ai-leadership-at-risc-v-summit-europe-2025">Andes Technology Showcases RISC-V AI Leadership at RISC-V &hellip;</a></li>
<li><a href="https://riscv-europe.org/summit/2025/posters">RISC-V Summit Europe 2025 - Posters</a></li>
</ol>
<hr>
<p><em>本文基于网络公开资料整理，结合嵌入式开发实践经验编写。</em></p>
]]></content:encoded></item><item><title>嵌入式开发：Esp32-P4-Eye Camera Display Edge Ai Gateway 2026</title><link>https://tech-snippets.xyz/posts/esp32-p4-eye-camera-display-edge-ai-gateway-2026/</link><pubDate>Tue, 09 Jun 2026 03:00:00 +0800</pubDate><guid>https://tech-snippets.xyz/posts/esp32-p4-eye-camera-display-edge-ai-gateway-2026/</guid><description>嵌入式开发实战教程：ESP32-P4-EYE camera display edge AI gateway 2026</description><content:encoded><![CDATA[<h2 id="引言">引言</h2>
<p>本文基于 2026 年最新行业资料整理，涵盖 ESP32-P4-EYE camera display edge AI gateway 2026 的核心概念、开发流程和实战技巧。</p>
<h2 id="esp32-p4-eye-camera-display-edge-ai-gateway-2026-概述">ESP32-P4-EYE camera display edge AI gateway 2026 概述</h2>
<p>嵌入式系统开发需要掌握硬件、软件和系统集成的综合技能。</p>
<h2 id="开发流程">开发流程</h2>
<ol>
<li><strong>需求分析</strong> → 确定功能、性能指标</li>
<li><strong>硬件选型</strong> → MCU、传感器、通信模块</li>
<li><strong>原理图设计</strong> → 电路设计、仿真验证</li>
<li><strong>软件架构</strong> → 分层设计、模块化</li>
<li><strong>编码实现</strong> → C/C++、RTOS、驱动</li>
<li><strong>调试测试</strong> → 单元测试、系统集成</li>
<li><strong>生产部署</strong> → 批量生产、OTA 升级</li>
</ol>
<h2 id="常用工具">常用工具</h2>
<ul>
<li><strong>IDE</strong>：Keil、IAR、STM32CubeIDE、VS Code</li>
<li><strong>调试器</strong>：J-Link、ST-Link、DAP-Link</li>
<li><strong>示波器</strong>：Siglent、Rigol、Keysight</li>
<li><strong>逻辑分析仪</strong>：Saleae、Kingst</li>
</ul>
<h2 id="学习路线">学习路线</h2>
<ol>
<li>C 语言基础 → 指针、结构体、内存管理</li>
<li>单片机原理 → GPIO、中断、定时器</li>
<li>外设驱动 → UART、SPI、I2C、ADC</li>
<li>RTOS → 任务、信号量、消息队列</li>
<li>项目实战 → 综合应用</li>
</ol>
<h2 id="参考资料">参考资料</h2>
<ol>
<li><a href="https://www.instagram.com/p/DI0qT_VAK22">️ Meet ESP32-P4-EYE: Vision Intelligence at the Edge Espressif&rsquo;s &hellip;</a></li>
<li><a href="https://developer.espressif.com/blog/2025/05/introducing-p4-eye">Introducing ESP32-P4-EYE: A Powerful Vision Development Board for Edge AI · Developer Portal</a></li>
<li><a href="https://www.youtube.com/watch?v=21dSHwdn7pQ&amp;vl=en">Espressif ESP32-P4 Edge AI Robot Arm, ESP32-H4 LE &hellip; - YouTube</a></li>
</ol>
<hr>
<p><em>本文基于网络公开资料整理，结合嵌入式开发实践经验编写。</em></p>
]]></content:encoded></item><item><title>嵌入式开发：Webnn Webgpu Browser On-Device Ai Edge Agents 2026</title><link>https://tech-snippets.xyz/posts/webnn-webgpu-browser-on-device-ai-edge-agents-2026/</link><pubDate>Mon, 08 Jun 2026 03:00:00 +0800</pubDate><guid>https://tech-snippets.xyz/posts/webnn-webgpu-browser-on-device-ai-edge-agents-2026/</guid><description>嵌入式开发实战教程：WebNN WebGPU browser on-device AI edge agents 2026</description><content:encoded><![CDATA[<h2 id="引言">引言</h2>
<p>本文基于 2026 年最新行业资料整理，涵盖 WebNN WebGPU browser on-device AI edge agents 2026 的核心概念、开发流程和实战技巧。</p>
<h2 id="webnn-webgpu-browser-on-device-ai-edge-agents-2026-概述">WebNN WebGPU browser on-device AI edge agents 2026 概述</h2>
<p>嵌入式系统开发需要掌握硬件、软件和系统集成的综合技能。</p>
<h2 id="开发流程">开发流程</h2>
<ol>
<li><strong>需求分析</strong> → 确定功能、性能指标</li>
<li><strong>硬件选型</strong> → MCU、传感器、通信模块</li>
<li><strong>原理图设计</strong> → 电路设计、仿真验证</li>
<li><strong>软件架构</strong> → 分层设计、模块化</li>
<li><strong>编码实现</strong> → C/C++、RTOS、驱动</li>
<li><strong>调试测试</strong> → 单元测试、系统集成</li>
<li><strong>生产部署</strong> → 批量生产、OTA 升级</li>
</ol>
<h2 id="常用工具">常用工具</h2>
<ul>
<li><strong>IDE</strong>：Keil、IAR、STM32CubeIDE、VS Code</li>
<li><strong>调试器</strong>：J-Link、ST-Link、DAP-Link</li>
<li><strong>示波器</strong>：Siglent、Rigol、Keysight</li>
<li><strong>逻辑分析仪</strong>：Saleae、Kingst</li>
</ul>
<h2 id="学习路线">学习路线</h2>
<ol>
<li>C 语言基础 → 指针、结构体、内存管理</li>
<li>单片机原理 → GPIO、中断、定时器</li>
<li>外设驱动 → UART、SPI、I2C、ADC</li>
<li>RTOS → 任务、信号量、消息队列</li>
<li>项目实战 → 综合应用</li>
</ol>
<h2 id="参考资料">参考资料</h2>
<ol>
<li><a href="https://blogs.windows.com/msedgedev/2026/06/02/expanding-on-device-ai-in-microsoft-edge-new-models-and-apis-for-the-web">Expanding on‑device AI in Microsoft Edge: New models and APIs for the web - Microsoft Edge Blog</a></li>
<li><a href="https://www.youtube.com/watch?v=1mix7WnuEK0">I Replaced My AI Server With A Browser Tab (WebGPU 2026 Setup)</a></li>
<li><a href="https://senoritadeveloper.medium.com/inside-the-web-ai-revolution-on-device-ml-webgpu-and-real-world-deployments-c34abbf22fdb">Inside the Web AI Revolution: On-Device ML, WebGPU, and Real-World Deployments</a></li>
</ol>
<hr>
<p><em>本文基于网络公开资料整理，结合嵌入式开发实践经验编写。</em></p>
]]></content:encoded></item><item><title>嵌入式开发：Openvino Genai Npu Industrial Edge Gateway 2025</title><link>https://tech-snippets.xyz/posts/openvino-genai-npu-industrial-edge-gateway-2025/</link><pubDate>Sun, 07 Jun 2026 03:00:00 +0800</pubDate><guid>https://tech-snippets.xyz/posts/openvino-genai-npu-industrial-edge-gateway-2025/</guid><description>嵌入式开发实战教程：OpenVINO GenAI NPU industrial edge gateway 2025</description><content:encoded><![CDATA[<h2 id="引言">引言</h2>
<p>本文基于 2026 年最新行业资料整理，涵盖 OpenVINO GenAI NPU industrial edge gateway 2025 的核心概念、开发流程和实战技巧。</p>
<h2 id="openvino-genai-npu-industrial-edge-gateway-2025-概述">OpenVINO GenAI NPU industrial edge gateway 2025 概述</h2>
<p>嵌入式系统开发需要掌握硬件、软件和系统集成的综合技能。</p>
<h2 id="开发流程">开发流程</h2>
<ol>
<li><strong>需求分析</strong> → 确定功能、性能指标</li>
<li><strong>硬件选型</strong> → MCU、传感器、通信模块</li>
<li><strong>原理图设计</strong> → 电路设计、仿真验证</li>
<li><strong>软件架构</strong> → 分层设计、模块化</li>
<li><strong>编码实现</strong> → C/C++、RTOS、驱动</li>
<li><strong>调试测试</strong> → 单元测试、系统集成</li>
<li><strong>生产部署</strong> → 批量生产、OTA 升级</li>
</ol>
<h2 id="常用工具">常用工具</h2>
<ul>
<li><strong>IDE</strong>：Keil、IAR、STM32CubeIDE、VS Code</li>
<li><strong>调试器</strong>：J-Link、ST-Link、DAP-Link</li>
<li><strong>示波器</strong>：Siglent、Rigol、Keysight</li>
<li><strong>逻辑分析仪</strong>：Saleae、Kingst</li>
</ul>
<h2 id="学习路线">学习路线</h2>
<ol>
<li>C 语言基础 → 指针、结构体、内存管理</li>
<li>单片机原理 → GPIO、中断、定时器</li>
<li>外设驱动 → UART、SPI、I2C、ADC</li>
<li>RTOS → 任务、信号量、消息队列</li>
<li>项目实战 → 综合应用</li>
</ol>
<h2 id="参考资料">参考资料</h2>
<ol>
<li><a href="https://www.edge-ai-vision.com/2025/12/openvino-2025-4-release-broadens-model-support">OpenVINO 2025.4 Release Broadens Model Support - Edge AI and Vision Alliance</a></li>
<li><a href="https://www.intel.com/content/www/us/en/developer/articles/release-notes/openvino/2025-3.html">Release Notes for Intel Distribution of OpenVINO Toolkit 2025.3</a></li>
<li><a href="https://medium.com/openvino-toolkit/openvino-2025-4-faster-models-smarter-agents-3709e6437a08">OpenVINO ™ 2025.4: Faster Models, Smarter Agents - Medium</a></li>
</ol>
<hr>
<p><em>本文基于网络公开资料整理，结合嵌入式开发实践经验编写。</em></p>
]]></content:encoded></item><item><title>嵌入式开发：Esp32-S3 Tinyml Edge Ai Voice Vision 2026</title><link>https://tech-snippets.xyz/posts/esp32-s3-tinyml-edge-ai-voice-vision-2026/</link><pubDate>Sat, 06 Jun 2026 03:00:00 +0800</pubDate><guid>https://tech-snippets.xyz/posts/esp32-s3-tinyml-edge-ai-voice-vision-2026/</guid><description>嵌入式开发实战教程：ESP32-S3 TinyML edge AI voice vision 2026</description><content:encoded><![CDATA[<h2 id="引言">引言</h2>
<p>本文基于 2026 年最新行业资料整理，涵盖 ESP32-S3 TinyML edge AI voice vision 2026 的核心概念、开发流程和实战技巧。</p>
<h2 id="esp32-s3-tinyml-edge-ai-voice-vision-2026-概述">ESP32-S3 TinyML edge AI voice vision 2026 概述</h2>
<p>嵌入式系统开发需要掌握硬件、软件和系统集成的综合技能。</p>
<h2 id="开发流程">开发流程</h2>
<ol>
<li><strong>需求分析</strong> → 确定功能、性能指标</li>
<li><strong>硬件选型</strong> → MCU、传感器、通信模块</li>
<li><strong>原理图设计</strong> → 电路设计、仿真验证</li>
<li><strong>软件架构</strong> → 分层设计、模块化</li>
<li><strong>编码实现</strong> → C/C++、RTOS、驱动</li>
<li><strong>调试测试</strong> → 单元测试、系统集成</li>
<li><strong>生产部署</strong> → 批量生产、OTA 升级</li>
</ol>
<h2 id="常用工具">常用工具</h2>
<ul>
<li><strong>IDE</strong>：Keil、IAR、STM32CubeIDE、VS Code</li>
<li><strong>调试器</strong>：J-Link、ST-Link、DAP-Link</li>
<li><strong>示波器</strong>：Siglent、Rigol、Keysight</li>
<li><strong>逻辑分析仪</strong>：Saleae、Kingst</li>
</ul>
<h2 id="学习路线">学习路线</h2>
<ol>
<li>C 语言基础 → 指针、结构体、内存管理</li>
<li>单片机原理 → GPIO、中断、定时器</li>
<li>外设驱动 → UART、SPI、I2C、ADC</li>
<li>RTOS → 任务、信号量、消息队列</li>
<li>项目实战 → 综合应用</li>
</ol>
<h2 id="参考资料">参考资料</h2>
<ol>
<li><a href="https://www.seeedstudio.com/blog/2026/05/06/esp32-camera-guide">ESP32 Camera Guide 2026: Best Boards, Setup &amp; Projects</a></li>
<li><a href="https://www.instagram.com/reel/DSfDaFIEnph">Talk to it, and it talks back. 🎙️ This DIY ESP32-S3 AI voice &hellip;</a></li>
<li><a href="https://www.youtube.com/watch?v=FhV9c0Vv3Ng">AI Meeting Recorder using XIAO ESP32 S3 Sense - YouTube</a></li>
</ol>
<hr>
<p><em>本文基于网络公开资料整理，结合嵌入式开发实践经验编写。</em></p>
]]></content:encoded></item><item><title>嵌入式开发：On-Device Llm Edge Ai Agents Embedded Hardware 2026</title><link>https://tech-snippets.xyz/posts/on-device-llm-edge-ai-agents-embedded-hardware-2026/</link><pubDate>Fri, 05 Jun 2026 03:00:00 +0800</pubDate><guid>https://tech-snippets.xyz/posts/on-device-llm-edge-ai-agents-embedded-hardware-2026/</guid><description>嵌入式开发实战教程：on-device LLM edge AI agents embedded hardware 2026</description><content:encoded><![CDATA[<h2 id="引言">引言</h2>
<p>本文基于 2026 年最新行业资料整理，涵盖 on-device LLM edge AI agents embedded hardware 2026 的核心概念、开发流程和实战技巧。</p>
<h2 id="on-device-llm-edge-ai-agents-embedded-hardware-2026-概述">on-device LLM edge AI agents embedded hardware 2026 概述</h2>
<p>嵌入式系统开发需要掌握硬件、软件和系统集成的综合技能。</p>
<h2 id="开发流程">开发流程</h2>
<ol>
<li><strong>需求分析</strong> → 确定功能、性能指标</li>
<li><strong>硬件选型</strong> → MCU、传感器、通信模块</li>
<li><strong>原理图设计</strong> → 电路设计、仿真验证</li>
<li><strong>软件架构</strong> → 分层设计、模块化</li>
<li><strong>编码实现</strong> → C/C++、RTOS、驱动</li>
<li><strong>调试测试</strong> → 单元测试、系统集成</li>
<li><strong>生产部署</strong> → 批量生产、OTA 升级</li>
</ol>
<h2 id="常用工具">常用工具</h2>
<ul>
<li><strong>IDE</strong>：Keil、IAR、STM32CubeIDE、VS Code</li>
<li><strong>调试器</strong>：J-Link、ST-Link、DAP-Link</li>
<li><strong>示波器</strong>：Siglent、Rigol、Keysight</li>
<li><strong>逻辑分析仪</strong>：Saleae、Kingst</li>
</ul>
<h2 id="学习路线">学习路线</h2>
<ol>
<li>C 语言基础 → 指针、结构体、内存管理</li>
<li>单片机原理 → GPIO、中断、定时器</li>
<li>外设驱动 → UART、SPI、I2C、ADC</li>
<li>RTOS → 任务、信号量、消息队列</li>
<li>项目实战 → 综合应用</li>
</ol>
<h2 id="参考资料">参考资料</h2>
<ol>
<li><a href="https://www.edgeimpulse.com/blog/smarter-devices-smaller-chips-inside-embedded-world-2026">Smarter Devices, Smaller Chips: Inside Embedded World 2026</a></li>
<li><a href="https://www.siliconflow.com/articles/en/best-small-llms-for-edge-devices">Ultimate Guide - The Best Small LLMs For Edge Devices In 2026</a></li>
<li><a href="https://newsroom.arm.com/blog/arm-embedded-world-2026">Arm at Embedded World 2026: Powering intelligent edge AI systems at scale</a></li>
</ol>
<hr>
<p><em>本文基于网络公开资料整理，结合嵌入式开发实践经验编写。</em></p>
]]></content:encoded></item><item><title>嵌入式开发：Embedded C Programming Best Practices</title><link>https://tech-snippets.xyz/posts/embedded-c-programming-best-practices/</link><pubDate>Mon, 20 Apr 2026 03:00:00 +0800</pubDate><guid>https://tech-snippets.xyz/posts/embedded-c-programming-best-practices/</guid><description>嵌入式开发实战教程：embedded C programming best practices</description><content:encoded><![CDATA[<h2 id="引言">引言</h2>
<p>本文基于 2026 年最新行业资料整理，涵盖 embedded C programming best practices 的核心概念、开发流程和实战技巧。</p>
<h2 id="embedded-c-programming-best-practices-概述">embedded C programming best practices 概述</h2>
<p>嵌入式系统开发需要掌握硬件、软件和系统集成的综合技能。</p>
<h2 id="开发流程">开发流程</h2>
<ol>
<li><strong>需求分析</strong> → 确定功能、性能指标</li>
<li><strong>硬件选型</strong> → MCU、传感器、通信模块</li>
<li><strong>原理图设计</strong> → 电路设计、仿真验证</li>
<li><strong>软件架构</strong> → 分层设计、模块化</li>
<li><strong>编码实现</strong> → C/C++、RTOS、驱动</li>
<li><strong>调试测试</strong> → 单元测试、系统集成</li>
<li><strong>生产部署</strong> → 批量生产、OTA 升级</li>
</ol>
<h2 id="常用工具">常用工具</h2>
<ul>
<li><strong>IDE</strong>：Keil、IAR、STM32CubeIDE、VS Code</li>
<li><strong>调试器</strong>：J-Link、ST-Link、DAP-Link</li>
<li><strong>示波器</strong>：Siglent、Rigol、Keysight</li>
<li><strong>逻辑分析仪</strong>：Saleae、Kingst</li>
</ul>
<h2 id="学习路线">学习路线</h2>
<ol>
<li>C 语言基础 → 指针、结构体、内存管理</li>
<li>单片机原理 → GPIO、中断、定时器</li>
<li>外设驱动 → UART、SPI、I2C、ADC</li>
<li>RTOS → 任务、信号量、消息队列</li>
<li>项目实战 → 综合应用</li>
</ol>
<h2 id="参考资料">参考资料</h2>
<ol>
<li><a href="https://iies.in/blog/embedded-c-programming-tips-and-best-practices">Embedded C Programming | Tips and Best Practices | IIES</a></li>
<li><a href="https://iies.in?p=3462">Embedded C Programming | Tips and Best Practices | IIES</a></li>
<li><a href="https://www.linkedin.com/pulse/best-practices-embedded-c-programming-ensuring-yamil-garcia-ibwje">Best Practices for Embedded C Programming: Ensuring Efficiency, Reliability, and Maintainability</a></li>
</ol>
<hr>
<p><em>本文基于网络公开资料整理，结合嵌入式开发实践经验编写。</em></p>
]]></content:encoded></item><item><title>实时检测指定颜色和形状的物体：算法方案对比与实现</title><link>https://tech-snippets.xyz/posts/real-time-color-shape-detection/</link><pubDate>Wed, 15 Apr 2026 03:00:00 +0800</pubDate><guid>https://tech-snippets.xyz/posts/real-time-color-shape-detection/</guid><description>详细对比传统图像处理与深度学习方案，提供完整可运行代码，适合嵌入式设备上的实时颜色形状检测应用</description><content:encoded><![CDATA[<h2 id="引言">引言</h2>
<p>在工业检测、机器人视觉、智能分拣等应用场景中，我们经常需要<strong>实时检测特定颜色和形状的物体</strong>。例如：</p>
<ul>
<li>冰壶比赛自动计分系统：检测冰面上的圆形冰壶</li>
<li>工业零件分拣：检测红色圆形螺丝、蓝色方形螺母</li>
<li>自动驾驶交通标志识别：检测圆形红圈禁令标志</li>
<li>AGV 小车导航：识别地面彩色圆形二维码</li>
</ul>
<p>本文将从简单到复杂，介绍几种常见的实现方案，对比它们的性能，并提供完整的开源参考代码，帮助你根据实际场景选择最合适的方案。</p>
<h2 id="方案对比总览">方案对比总览</h2>
<p>我们主要对比四种主流方案：</p>
<table>
<thead>
<tr>
<th>方案</th>
<th>原理</th>
<th>计算量</th>
<th>准确率</th>
<th>适合场景</th>
<th>MCU 能否运行</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>颜色分割 + 轮廓检测</strong></td>
<td>阈值分割 + 形状分析</td>
<td>极低</td>
<td>对颜色形状变化敏感</td>
<td>背景简单、光照稳定</td>
<td>✅ Cortex-M7 可以</td>
</tr>
<tr>
<td><strong>颜色空间转换 + Hough 变换</strong></td>
<td>Hough 圆/直线检测</td>
<td>低</td>
<td>圆形检测较好</td>
<td>固定形状检测</td>
<td>✅ Cortex-M4 可以</td>
</tr>
<tr>
<td><strong>Blob 分析 + 特征匹配</strong></td>
<td>连通域分析 + 形状分类</td>
<td>中</td>
<td>中等</td>
<td>多目标批量处理</td>
<td>✅ Cortex-M7 可以</td>
</tr>
<tr>
<td><strong>深度学习目标检测</strong></td>
<td>YOLO/SSD 直接检测</td>
<td>高</td>
<td>鲁棒性强</td>
<td>复杂背景、光照变化</td>
<td>❌ 需要 MCU+NPU 或 Linux</td>
</tr>
</tbody>
</table>
<p>下面详细介绍每种方案的实现。</p>
<h2 id="方案一颜色分割--轮廓检测">方案一：颜色分割 + 轮廓检测</h2>
<h3 id="11-算法流程">1.1 算法流程</h3>
<figure>
<svg viewBox="0 0 700 280" xmlns="http://www.w3.org/2000/svg">
  <defs>
    <style>
      .box { fill: #1a1a2e; stroke: #4a9eff; stroke-width: 2; rx: 6; }
      .text { fill: #e0e0e0; font-family: Arial, sans-serif; font-size: 13px; text-anchor: middle; }
      .label { fill: #4a9eff; font-family: Arial, sans-serif; font-size: 14px; font-weight: bold; }
      .arrow { stroke: #4a9eff; stroke-width: 2; fill: none; marker-end: url(#arrow); }
    </style>
    <marker id="arrow" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
      <polygon points="0 0, 10 3.5, 0 7" fill="#4a9eff"/>
    </marker>
  </defs>
  <rect class="box" x="50" y="20" width="140" height="60"/>
  <text class="label" x="120" y="45">原始图像</text>
  <text class="text" x="120" y="62">RGB/BGR</text>
  <path class="arrow" d="M190 50 L240 50"/>
  <rect class="box" x="240" y="20" width="140" height="60"/>
  <text class="label" x="310" y="45">颜色空间转换</text>
  <text class="text" x="310" y="62">RGB → HSV</text>
  <path class="arrow" d="M380 50 L430 50"/>
  <rect class="box" x="430" y="20" width="140" height="60"/>
  <text class="label" x="500" y="45">颜色阈值分割</text>
  <text class="text" x="500" y="62">二值掩码</text>
  <path class="arrow" d="M500 80 L500 120"/>
  <rect class="box" x="430" y="120" width="140" height="60"/>
  <text class="label" x="500" y="145">形态学处理</text>
  <text class="text" x="500" y="162">腐蚀 + 膨胀</text>
  <path class="arrow" d="M430 150 L380 150"/>
  <rect class="box" x="240" y="120" width="140" height="60"/>
  <text class="label" x="310" y="145">查找轮廓</text>
  <text class="text" x="310" y="162">cv2.findContours</text>
  <path class="arrow" d="M240 150 L190 150"/>
  <rect class="box" x="50" y="120" width="140" height="60"/>
  <text class="label" x="120" y="145">形状特征计算</text>
  <text class="text" x="120" y="162">面积、周长、圆形度</text>
  <path class="arrow" d="M120 180 L120 220"/>
  <rect class="box" x="50" y="220" width="600" height="40"/>
  <text class="label" x="350" y="245">输出：符合颜色和形状要求的目标</text>
</svg>
<figcaption style="text-align:center;color:#888;font-size:12px;margin-top:8px;">颜色分割 + 轮廓检测流程图</figcaption>
</figure>
<h3 id="12-核心原理">1.2 核心原理</h3>
<ol>
<li><strong>颜色空间转换</strong>：从 RGB 转到 HSV 颜色空间，更容易按颜色分割</li>
<li><strong>阈值分割</strong>：对 H/S/V 三个通道设置范围，得到二值掩码</li>
<li><strong>形态学处理</strong>：腐蚀 + 膨胀去除噪声</li>
<li><strong>轮廓查找</strong>：找到所有连通区域</li>
<li><strong>形状特征计算</strong>：计算面积、周长、圆形度、矩形度等特征</li>
<li><strong>特征匹配</strong>：筛选符合指定形状的目标</li>
</ol>
<h3 id="13-完整-python-实现">1.3 完整 Python 实现</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">cv2</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">numpy</span> <span class="k">as</span> <span class="nn">np</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">detect_color_shape</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="n">image</span><span class="p">,</span> 
</span></span><span class="line"><span class="cl">    <span class="n">color_lower</span><span class="o">=</span><span class="n">np</span><span class="o">.</span><span class="n">array</span><span class="p">([</span><span class="mi">0</span><span class="p">,</span> <span class="mi">120</span><span class="p">,</span> <span class="mi">70</span><span class="p">]),</span> 
</span></span><span class="line"><span class="cl">    <span class="n">color_upper</span><span class="o">=</span><span class="n">np</span><span class="o">.</span><span class="n">array</span><span class="p">([</span><span class="mi">10</span><span class="p">,</span> <span class="mi">255</span><span class="p">,</span> <span class="mi">255</span><span class="p">]),</span>
</span></span><span class="line"><span class="cl">    <span class="n">shape_type</span><span class="o">=</span><span class="s2">&#34;circle&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">min_area</span><span class="o">=</span><span class="mi">100</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">max_area</span><span class="o">=</span><span class="mi">10000</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">circularity_threshold</span><span class="o">=</span><span class="mf">0.8</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">aspect_ratio_range</span><span class="o">=</span><span class="p">(</span><span class="mf">0.9</span><span class="p">,</span> <span class="mf">1.1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;
</span></span></span><span class="line"><span class="cl"><span class="s2">    检测指定颜色和形状的物体
</span></span></span><span class="line"><span class="cl"><span class="s2">    
</span></span></span><span class="line"><span class="cl"><span class="s2">    参数:
</span></span></span><span class="line"><span class="cl"><span class="s2">        image: 输入 RGB/BGR 图像
</span></span></span><span class="line"><span class="cl"><span class="s2">        color_lower: HSV 颜色下限
</span></span></span><span class="line"><span class="cl"><span class="s2">        color_upper: HSV 颜色上限
</span></span></span><span class="line"><span class="cl"><span class="s2">        shape_type: &#34;circle&#34; / &#34;square&#34; / &#34;rectangle&#34;
</span></span></span><span class="line"><span class="cl"><span class="s2">        min_area: 最小面积（像素）
</span></span></span><span class="line"><span class="cl"><span class="s2">        max_area: 最大面积（像素）
</span></span></span><span class="line"><span class="cl"><span class="s2">        circularity_threshold: 圆形度阈值（0~1，越大越圆）
</span></span></span><span class="line"><span class="cl"><span class="s2">        aspect_ratio_range: 宽高比范围（方形接近 1）
</span></span></span><span class="line"><span class="cl"><span class="s2">    
</span></span></span><span class="line"><span class="cl"><span class="s2">    返回:
</span></span></span><span class="line"><span class="cl"><span class="s2">        detections: 检测结果列表 [(x, y, w, h, contour), ...]
</span></span></span><span class="line"><span class="cl"><span class="s2">        mask: 颜色分割掩码（用于调试）
</span></span></span><span class="line"><span class="cl"><span class="s2">    &#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># 1. 颜色空间转换：BGR → HSV</span>
</span></span><span class="line"><span class="cl">    <span class="n">hsv</span> <span class="o">=</span> <span class="n">cv2</span><span class="o">.</span><span class="n">cvtColor</span><span class="p">(</span><span class="n">image</span><span class="p">,</span> <span class="n">cv2</span><span class="o">.</span><span class="n">COLOR_BGR2HSV</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1"># 2. 颜色阈值分割</span>
</span></span><span class="line"><span class="cl">    <span class="n">mask</span> <span class="o">=</span> <span class="n">cv2</span><span class="o">.</span><span class="n">inRange</span><span class="p">(</span><span class="n">hsv</span><span class="p">,</span> <span class="n">color_lower</span><span class="p">,</span> <span class="n">color_upper</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1"># 3. 形态学处理去除噪声</span>
</span></span><span class="line"><span class="cl">    <span class="n">kernel</span> <span class="o">=</span> <span class="n">cv2</span><span class="o">.</span><span class="n">getStructuringElement</span><span class="p">(</span><span class="n">cv2</span><span class="o">.</span><span class="n">MORPH_ELLIPSE</span><span class="p">,</span> <span class="p">(</span><span class="mi">5</span><span class="p">,</span> <span class="mi">5</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="n">mask</span> <span class="o">=</span> <span class="n">cv2</span><span class="o">.</span><span class="n">morphologyEx</span><span class="p">(</span><span class="n">mask</span><span class="p">,</span> <span class="n">cv2</span><span class="o">.</span><span class="n">MORPH_OPEN</span><span class="p">,</span> <span class="n">kernel</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">mask</span> <span class="o">=</span> <span class="n">cv2</span><span class="o">.</span><span class="n">morphologyEx</span><span class="p">(</span><span class="n">mask</span><span class="p">,</span> <span class="n">cv2</span><span class="o">.</span><span class="n">MORPH_CLOSE</span><span class="p">,</span> <span class="n">kernel</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1"># 4. 查找轮廓</span>
</span></span><span class="line"><span class="cl">    <span class="n">contours</span><span class="p">,</span> <span class="n">hierarchy</span> <span class="o">=</span> <span class="n">cv2</span><span class="o">.</span><span class="n">findContours</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="n">mask</span><span class="p">,</span> <span class="n">cv2</span><span class="o">.</span><span class="n">RETR_EXTERNAL</span><span class="p">,</span> <span class="n">cv2</span><span class="o">.</span><span class="n">CHAIN_APPROX_SIMPLE</span>
</span></span><span class="line"><span class="cl">    <span class="p">)</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="n">detections</span> <span class="o">=</span> <span class="p">[]</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="n">contour</span> <span class="ow">in</span> <span class="n">contours</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="c1"># 计算轮廓面积，过滤太小/太大的</span>
</span></span><span class="line"><span class="cl">        <span class="n">area</span> <span class="o">=</span> <span class="n">cv2</span><span class="o">.</span><span class="n">contourArea</span><span class="p">(</span><span class="n">contour</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="n">area</span> <span class="o">&lt;</span> <span class="n">min_area</span> <span class="ow">or</span> <span class="n">area</span> <span class="o">&gt;</span> <span class="n">max_area</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="k">continue</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="c1"># 计算轮廓周长</span>
</span></span><span class="line"><span class="cl">        <span class="n">perimeter</span> <span class="o">=</span> <span class="n">cv2</span><span class="o">.</span><span class="n">arcLength</span><span class="p">(</span><span class="n">contour</span><span class="p">,</span> <span class="kc">True</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="c1"># 多边形逼近</span>
</span></span><span class="line"><span class="cl">        <span class="n">approx</span> <span class="o">=</span> <span class="n">cv2</span><span class="o">.</span><span class="n">approxPolyDP</span><span class="p">(</span><span class="n">contour</span><span class="p">,</span> <span class="mf">0.04</span> <span class="o">*</span> <span class="n">perimeter</span><span class="p">,</span> <span class="kc">True</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="c1"># 获取外接矩形</span>
</span></span><span class="line"><span class="cl">        <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> <span class="n">w</span><span class="p">,</span> <span class="n">h</span> <span class="o">=</span> <span class="n">cv2</span><span class="o">.</span><span class="n">boundingRect</span><span class="p">(</span><span class="n">contour</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="c1"># 形状判断</span>
</span></span><span class="line"><span class="cl">        <span class="n">matched</span> <span class="o">=</span> <span class="kc">False</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="n">shape_type</span> <span class="o">==</span> <span class="s2">&#34;circle&#34;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="c1"># 圆形度 = 4π * 面积 / (周长^2)</span>
</span></span><span class="line"><span class="cl">            <span class="c1"># 完美圆形 = 1，越不规则值越小</span>
</span></span><span class="line"><span class="cl">            <span class="n">circularity</span> <span class="o">=</span> <span class="mi">4</span> <span class="o">*</span> <span class="n">np</span><span class="o">.</span><span class="n">pi</span> <span class="o">*</span> <span class="n">area</span> <span class="o">/</span> <span class="p">(</span><span class="n">perimeter</span> <span class="o">*</span> <span class="n">perimeter</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="k">if</span> <span class="n">circularity</span> <span class="o">&gt;=</span> <span class="n">circularity_threshold</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">                <span class="n">matched</span> <span class="o">=</span> <span class="kc">True</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="k">elif</span> <span class="n">shape_type</span> <span class="o">==</span> <span class="s2">&#34;square&#34;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="c1"># 宽高比接近 1，且顶点数约为 4</span>
</span></span><span class="line"><span class="cl">            <span class="n">aspect_ratio</span> <span class="o">=</span> <span class="nb">float</span><span class="p">(</span><span class="n">w</span><span class="p">)</span> <span class="o">/</span> <span class="n">h</span>
</span></span><span class="line"><span class="cl">            <span class="k">if</span> <span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">approx</span><span class="p">)</span> <span class="o">==</span> <span class="mi">4</span> <span class="ow">and</span> 
</span></span><span class="line"><span class="cl">                <span class="n">aspect_ratio</span> <span class="o">&gt;=</span> <span class="n">aspect_ratio_range</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="ow">and</span> 
</span></span><span class="line"><span class="cl">                <span class="n">aspect_ratio</span> <span class="o">&lt;=</span> <span class="n">aspect_ratio_range</span><span class="p">[</span><span class="mi">1</span><span class="p">]):</span>
</span></span><span class="line"><span class="cl">                <span class="n">matched</span> <span class="o">=</span> <span class="kc">True</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="k">elif</span> <span class="n">shape_type</span> <span class="o">==</span> <span class="s2">&#34;rectangle&#34;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="c1"># 顶点数约为 4 即可</span>
</span></span><span class="line"><span class="cl">            <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">approx</span><span class="p">)</span> <span class="o">==</span> <span class="mi">4</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">                <span class="n">matched</span> <span class="o">=</span> <span class="kc">True</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="k">elif</span> <span class="n">shape_type</span> <span class="o">==</span> <span class="s2">&#34;triangle&#34;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">approx</span><span class="p">)</span> <span class="o">==</span> <span class="mi">3</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">                <span class="n">matched</span> <span class="o">=</span> <span class="kc">True</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="n">matched</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="n">center_x</span> <span class="o">=</span> <span class="n">x</span> <span class="o">+</span> <span class="n">w</span> <span class="o">//</span> <span class="mi">2</span>
</span></span><span class="line"><span class="cl">            <span class="n">center_y</span> <span class="o">=</span> <span class="n">y</span> <span class="o">+</span> <span class="n">h</span> <span class="o">//</span> <span class="mi">2</span>
</span></span><span class="line"><span class="cl">            <span class="n">detections</span><span class="o">.</span><span class="n">append</span><span class="p">((</span><span class="n">center_x</span><span class="p">,</span> <span class="n">center_y</span><span class="p">,</span> <span class="n">w</span><span class="p">,</span> <span class="n">h</span><span class="p">,</span> <span class="n">contour</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">detections</span><span class="p">,</span> <span class="n">mask</span>
</span></span></code></pre></div><h3 id="14-使用示例">1.4 使用示例</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="c1"># 检测红色圆形物体</span>
</span></span><span class="line"><span class="cl"><span class="c1"># HSV 红色范围（两种分段，因为红色在 H 通道首尾）</span>
</span></span><span class="line"><span class="cl"><span class="n">lower_red1</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">array</span><span class="p">([</span><span class="mi">0</span><span class="p">,</span> <span class="mi">120</span><span class="p">,</span> <span class="mi">70</span><span class="p">])</span>
</span></span><span class="line"><span class="cl"><span class="n">upper_red1</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">array</span><span class="p">([</span><span class="mi">10</span><span class="p">,</span> <span class="mi">255</span><span class="p">,</span> <span class="mi">255</span><span class="p">])</span>
</span></span><span class="line"><span class="cl"><span class="n">lower_red2</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">array</span><span class="p">([</span><span class="mi">170</span><span class="p">,</span> <span class="mi">120</span><span class="p">,</span> <span class="mi">70</span><span class="p">])</span>
</span></span><span class="line"><span class="cl"><span class="n">upper_red2</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">array</span><span class="p">([</span><span class="mi">180</span><span class="p">,</span> <span class="mi">255</span><span class="p">,</span> <span class="mi">255</span><span class="p">])</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 读取图像</span>
</span></span><span class="line"><span class="cl"><span class="n">image</span> <span class="o">=</span> <span class="n">cv2</span><span class="o">.</span><span class="n">imread</span><span class="p">(</span><span class="s2">&#34;test_image.jpg&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 第一次检测</span>
</span></span><span class="line"><span class="cl"><span class="n">detections1</span><span class="p">,</span> <span class="n">mask1</span> <span class="o">=</span> <span class="n">detect_color_shape</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="n">image</span><span class="p">,</span> <span class="n">lower_red1</span><span class="p">,</span> <span class="n">upper_red1</span><span class="p">,</span> 
</span></span><span class="line"><span class="cl">    <span class="n">shape_type</span><span class="o">=</span><span class="s2">&#34;circle&#34;</span><span class="p">,</span> <span class="n">min_area</span><span class="o">=</span><span class="mi">500</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 第二次检测（另一红色区间）</span>
</span></span><span class="line"><span class="cl"><span class="n">detections2</span><span class="p">,</span> <span class="n">mask2</span> <span class="o">=</span> <span class="n">detect_color_shape</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="n">image</span><span class="p">,</span> <span class="n">lower_red2</span><span class="p">,</span> <span class="n">upper_red2</span><span class="p">,</span> 
</span></span><span class="line"><span class="cl">    <span class="n">shape_type</span><span class="o">=</span><span class="s2">&#34;circle&#34;</span><span class="p">,</span> <span class="n">min_area</span><span class="o">=</span><span class="mi">500</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 合并结果</span>
</span></span><span class="line"><span class="cl"><span class="n">all_detections</span> <span class="o">=</span> <span class="n">detections1</span> <span class="o">+</span> <span class="n">detections2</span>
</span></span><span class="line"><span class="cl"><span class="n">combined_mask</span> <span class="o">=</span> <span class="n">mask1</span> <span class="o">|</span> <span class="n">mask2</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 在原图上绘制结果</span>
</span></span><span class="line"><span class="cl"><span class="k">for</span> <span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> <span class="n">w</span><span class="p">,</span> <span class="n">h</span><span class="p">,</span> <span class="n">contour</span><span class="p">)</span> <span class="ow">in</span> <span class="n">all_detections</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">cv2</span><span class="o">.</span><span class="n">drawContours</span><span class="p">(</span><span class="n">image</span><span class="p">,</span> <span class="p">[</span><span class="n">contour</span><span class="p">],</span> <span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">255</span><span class="p">,</span> <span class="mi">0</span><span class="p">),</span> <span class="mi">2</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">cv2</span><span class="o">.</span><span class="n">circle</span><span class="p">(</span><span class="n">image</span><span class="p">,</span> <span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">),</span> <span class="mi">3</span><span class="p">,</span> <span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">255</span><span class="p">),</span> <span class="o">-</span><span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">cv2</span><span class="o">.</span><span class="n">putText</span><span class="p">(</span><span class="n">image</span><span class="p">,</span> <span class="s2">&#34;Red Circle&#34;</span><span class="p">,</span> <span class="p">(</span><span class="n">x</span><span class="o">-</span><span class="mi">20</span><span class="p">,</span> <span class="n">y</span><span class="o">-</span><span class="n">h</span><span class="o">-</span><span class="mi">10</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">                <span class="n">cv2</span><span class="o">.</span><span class="n">FONT_HERSHEY_SIMPLEX</span><span class="p">,</span> <span class="mf">0.5</span><span class="p">,</span> <span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">255</span><span class="p">,</span> <span class="mi">0</span><span class="p">),</span> <span class="mi">2</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">cv2</span><span class="o">.</span><span class="n">imwrite</span><span class="p">(</span><span class="s2">&#34;result.jpg&#34;</span><span class="p">,</span> <span class="n">image</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;检测到 </span><span class="si">{</span><span class="nb">len</span><span class="p">(</span><span class="n">all_detections</span><span class="p">)</span><span class="si">}</span><span class="s2"> 个红色圆形物体&#34;</span><span class="p">)</span>
</span></span></code></pre></div><h3 id="15-常见颜色-hsv-范围参考">1.5 常见颜色 HSV 范围参考</h3>
<table>
<thead>
<tr>
<th>颜色</th>
<th>H 下限</th>
<th>H 上限</th>
<th>S 下限</th>
<th>S 上限</th>
<th>V 下限</th>
<th>V 上限</th>
</tr>
</thead>
<tbody>
<tr>
<td>红色</td>
<td>0/170</td>
<td>10/180</td>
<td>120</td>
<td>255</td>
<td>70</td>
<td>255</td>
</tr>
<tr>
<td>橙色</td>
<td>11</td>
<td>25</td>
<td>120</td>
<td>255</td>
<td>70</td>
<td>255</td>
</tr>
<tr>
<td>黄色</td>
<td>26</td>
<td>35</td>
<td>120</td>
<td>255</td>
<td>70</td>
<td>255</td>
</tr>
<tr>
<td>绿色</td>
<td>36</td>
<td>70</td>
<td>120</td>
<td>255</td>
<td>70</td>
<td>255</td>
</tr>
<tr>
<td>青色</td>
<td>71</td>
<td>99</td>
<td>120</td>
<td>255</td>
<td>70</td>
<td>255</td>
</tr>
<tr>
<td>蓝色</td>
<td>100</td>
<td>124</td>
<td>120</td>
<td>255</td>
<td>70</td>
<td>255</td>
</tr>
<tr>
<td>紫色</td>
<td>125</td>
<td>155</td>
<td>120</td>
<td>255</td>
<td>70</td>
<td>255</td>
</tr>
<tr>
<td>粉色</td>
<td>156</td>
<td>169</td>
<td>120</td>
<td>255</td>
<td>70</td>
<td>255</td>
</tr>
<tr>
<td>黑色</td>
<td>0</td>
<td>180</td>
<td>0</td>
<td>255</td>
<td>0</td>
<td>46</td>
</tr>
<tr>
<td>灰色</td>
<td>0</td>
<td>180</td>
<td>0</td>
<td>43</td>
<td>47</td>
<td>221</td>
</tr>
<tr>
<td>白色</td>
<td>0</td>
<td>180</td>
<td>0</td>
<td>30</td>
<td>222</td>
<td>255</td>
</tr>
</tbody>
</table>
<blockquote>
<p>注意：不同相机的白平衡和亮度设置不同，实际使用时需要根据你的图像微调范围。建议用 OpenCV 窗口滑动条调参：</p>
</blockquote>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">create_hsv_trackbars</span><span class="p">(</span><span class="n">window_name</span><span class="o">=</span><span class="s2">&#34;HSV Tuner&#34;</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="n">cv2</span><span class="o">.</span><span class="n">namedWindow</span><span class="p">(</span><span class="n">window_name</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">cv2</span><span class="o">.</span><span class="n">createTrackbar</span><span class="p">(</span><span class="s2">&#34;H_min&#34;</span><span class="p">,</span> <span class="n">window_name</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">180</span><span class="p">,</span> <span class="k">lambda</span> <span class="n">x</span><span class="p">:</span> <span class="kc">None</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">cv2</span><span class="o">.</span><span class="n">createTrackbar</span><span class="p">(</span><span class="s2">&#34;H_max&#34;</span><span class="p">,</span> <span class="n">window_name</span><span class="p">,</span> <span class="mi">180</span><span class="p">,</span> <span class="mi">180</span><span class="p">,</span> <span class="k">lambda</span> <span class="n">x</span><span class="p">:</span> <span class="kc">None</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">cv2</span><span class="o">.</span><span class="n">createTrackbar</span><span class="p">(</span><span class="s2">&#34;S_min&#34;</span><span class="p">,</span> <span class="n">window_name</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">255</span><span class="p">,</span> <span class="k">lambda</span> <span class="n">x</span><span class="p">:</span> <span class="kc">None</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">cv2</span><span class="o">.</span><span class="n">createTrackbar</span><span class="p">(</span><span class="s2">&#34;S_max&#34;</span><span class="p">,</span> <span class="n">window_name</span><span class="p">,</span> <span class="mi">255</span><span class="p">,</span> <span class="mi">255</span><span class="p">,</span> <span class="k">lambda</span> <span class="n">x</span><span class="p">:</span> <span class="kc">None</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">cv2</span><span class="o">.</span><span class="n">createTrackbar</span><span class="p">(</span><span class="s2">&#34;V_min&#34;</span><span class="p">,</span> <span class="n">window_name</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">255</span><span class="p">,</span> <span class="k">lambda</span> <span class="n">x</span><span class="p">:</span> <span class="kc">None</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">cv2</span><span class="o">.</span><span class="n">createTrackbar</span><span class="p">(</span><span class="s2">&#34;V_max&#34;</span><span class="p">,</span> <span class="n">window_name</span><span class="p">,</span> <span class="mi">255</span><span class="p">,</span> <span class="mi">255</span><span class="p">,</span> <span class="k">lambda</span> <span class="n">x</span><span class="p">:</span> <span class="kc">None</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">get_hsv_from_trackbars</span><span class="p">(</span><span class="n">window_name</span><span class="o">=</span><span class="s2">&#34;HSV Tuner&#34;</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="n">h_min</span> <span class="o">=</span> <span class="n">cv2</span><span class="o">.</span><span class="n">getTrackbarPos</span><span class="p">(</span><span class="s2">&#34;H_min&#34;</span><span class="p">,</span> <span class="n">window_name</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">h_max</span> <span class="o">=</span> <span class="n">cv2</span><span class="o">.</span><span class="n">getTrackbarPos</span><span class="p">(</span><span class="s2">&#34;H_max&#34;</span><span class="p">,</span> <span class="n">window_name</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">s_min</span> <span class="o">=</span> <span class="n">cv2</span><span class="o">.</span><span class="n">getTrackbarPos</span><span class="p">(</span><span class="s2">&#34;S_min&#34;</span><span class="p">,</span> <span class="n">window_name</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">s_max</span> <span class="o">=</span> <span class="n">cv2</span><span class="o">.</span><span class="n">getTrackbarPos</span><span class="p">(</span><span class="s2">&#34;S_max&#34;</span><span class="p">,</span> <span class="n">window_name</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">v_min</span> <span class="o">=</span> <span class="n">cv2</span><span class="o">.</span><span class="n">getTrackbarPos</span><span class="p">(</span><span class="s2">&#34;V_min&#34;</span><span class="p">,</span> <span class="n">window_name</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">v_max</span> <span class="o">=</span> <span class="n">cv2</span><span class="o">.</span><span class="n">getTrackbarPos</span><span class="p">(</span><span class="s2">&#34;V_max&#34;</span><span class="p">,</span> <span class="n">window_name</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">np</span><span class="o">.</span><span class="n">array</span><span class="p">([</span><span class="n">h_min</span><span class="p">,</span> <span class="n">s_min</span><span class="p">,</span> <span class="n">v_min</span><span class="p">]),</span> <span class="n">np</span><span class="o">.</span><span class="n">array</span><span class="p">([</span><span class="n">h_max</span><span class="p">,</span> <span class="n">s_max</span><span class="p">,</span> <span class="n">v_max</span><span class="p">])</span>
</span></span></code></pre></div><h3 id="16-优缺点分析">1.6 优缺点分析</h3>
<p>✅ <strong>优点</strong>：</p>
<ul>
<li>计算量极小，<strong>100MHz Cortex-M 可以实时处理 QVGA 图像</strong></li>
<li>代码简单，容易理解和调试</li>
<li>参数可调，适合固定场景</li>
</ul>
<p>❌ <strong>缺点</strong>：</p>
<ul>
<li>对光照变化敏感，光照一变颜色就不准了</li>
<li>对遮挡敏感，部分遮挡会改变轮廓形状</li>
<li>只能检测大致形状，无法区分语义</li>
</ul>
<h2 id="方案二hough-变换检测圆形直线">方案二：Hough 变换检测圆形/直线</h2>
<h3 id="21-原理简介">2.1 原理简介</h3>
<p>Hough 变换是一种经典的形状检测算法，它通过投票机制在参数空间中找到最可能的形状参数。对于圆形，参数空间是 <code>(x, y, r)</code> 三个维度：圆心坐标 + 半径。</p>
<h3 id="22-opencv-实现">2.2 OpenCV 实现</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">detect_circles_hough</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="n">image</span><span class="p">,</span> 
</span></span><span class="line"><span class="cl">    <span class="n">dp</span><span class="o">=</span><span class="mf">1.2</span><span class="p">,</span> 
</span></span><span class="line"><span class="cl">    <span class="n">min_dist</span><span class="o">=</span><span class="mi">50</span><span class="p">,</span> 
</span></span><span class="line"><span class="cl">    <span class="n">param1</span><span class="o">=</span><span class="mi">50</span><span class="p">,</span> 
</span></span><span class="line"><span class="cl">    <span class="n">param2</span><span class="o">=</span><span class="mi">30</span><span class="p">,</span> 
</span></span><span class="line"><span class="cl">    <span class="n">min_radius</span><span class="o">=</span><span class="mi">10</span><span class="p">,</span> 
</span></span><span class="line"><span class="cl">    <span class="n">max_radius</span><span class="o">=</span><span class="mi">100</span>
</span></span><span class="line"><span class="cl"><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;
</span></span></span><span class="line"><span class="cl"><span class="s2">    使用 Hough 变换检测圆形
</span></span></span><span class="line"><span class="cl"><span class="s2">    
</span></span></span><span class="line"><span class="cl"><span class="s2">    参数说明:
</span></span></span><span class="line"><span class="cl"><span class="s2">        dp: 累加器分辨率反比，1 = 原图分辨率
</span></span></span><span class="line"><span class="cl"><span class="s2">        min_dist: 两个圆心之间的最小距离（避免重复检测）
</span></span></span><span class="line"><span class="cl"><span class="s2">        param1: Canny 边缘检测高阈值
</span></span></span><span class="line"><span class="cl"><span class="s2">        param2: 累加器阈值，越大检测越少但越准
</span></span></span><span class="line"><span class="cl"><span class="s2">        min_radius/max_radius: 半径范围
</span></span></span><span class="line"><span class="cl"><span class="s2">    
</span></span></span><span class="line"><span class="cl"><span class="s2">    返回:
</span></span></span><span class="line"><span class="cl"><span class="s2">        circles: [(x, y, r), ...]
</span></span></span><span class="line"><span class="cl"><span class="s2">    &#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># 转灰度</span>
</span></span><span class="line"><span class="cl">    <span class="n">gray</span> <span class="o">=</span> <span class="n">cv2</span><span class="o">.</span><span class="n">cvtColor</span><span class="p">(</span><span class="n">image</span><span class="p">,</span> <span class="n">cv2</span><span class="o">.</span><span class="n">COLOR_BGR2GRAY</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1"># 高斯模糊降噪</span>
</span></span><span class="line"><span class="cl">    <span class="n">gray</span> <span class="o">=</span> <span class="n">cv2</span><span class="o">.</span><span class="n">GaussianBlur</span><span class="p">(</span><span class="n">gray</span><span class="p">,</span> <span class="p">(</span><span class="mi">5</span><span class="p">,</span> <span class="mi">5</span><span class="p">),</span> <span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1"># Hough 变换检测圆</span>
</span></span><span class="line"><span class="cl">    <span class="n">circles</span> <span class="o">=</span> <span class="n">cv2</span><span class="o">.</span><span class="n">HoughCircles</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="n">gray</span><span class="p">,</span> 
</span></span><span class="line"><span class="cl">        <span class="n">cv2</span><span class="o">.</span><span class="n">HOUGH_GRADIENT</span><span class="p">,</span> 
</span></span><span class="line"><span class="cl">        <span class="n">dp</span><span class="o">=</span><span class="n">dp</span><span class="p">,</span> 
</span></span><span class="line"><span class="cl">        <span class="n">minDist</span><span class="o">=</span><span class="n">min_dist</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">param1</span><span class="o">=</span><span class="n">param1</span><span class="p">,</span> 
</span></span><span class="line"><span class="cl">        <span class="n">param2</span><span class="o">=</span><span class="n">param2</span><span class="p">,</span> 
</span></span><span class="line"><span class="cl">        <span class="n">minRadius</span><span class="o">=</span><span class="n">min_radius</span><span class="p">,</span> 
</span></span><span class="line"><span class="cl">        <span class="n">maxRadius</span><span class="o">=</span><span class="n">max_radius</span>
</span></span><span class="line"><span class="cl">    <span class="p">)</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="n">result</span> <span class="o">=</span> <span class="p">[]</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">circles</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">circles</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">uint16</span><span class="p">(</span><span class="n">np</span><span class="o">.</span><span class="n">around</span><span class="p">(</span><span class="n">circles</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">        <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="n">circles</span><span class="p">[</span><span class="mi">0</span><span class="p">,</span> <span class="p">:]:</span>
</span></span><span class="line"><span class="cl">            <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> <span class="n">r</span> <span class="o">=</span> <span class="n">i</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="n">i</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span> <span class="n">i</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">            <span class="n">result</span><span class="o">.</span><span class="n">append</span><span class="p">((</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> <span class="n">r</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">result</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 使用示例</span>
</span></span><span class="line"><span class="cl"><span class="n">image</span> <span class="o">=</span> <span class="n">cv2</span><span class="o">.</span><span class="n">imread</span><span class="p">(</span><span class="s2">&#34;curling.jpg&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">circles</span> <span class="o">=</span> <span class="n">detect_circles_hough</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="n">image</span><span class="p">,</span> 
</span></span><span class="line"><span class="cl">    <span class="n">min_radius</span><span class="o">=</span><span class="mi">80</span><span class="p">,</span> 
</span></span><span class="line"><span class="cl">    <span class="n">max_radius</span><span class="o">=</span><span class="mi">120</span>  <span class="c1"># 冰壶直径约 100 像素</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 绘制结果</span>
</span></span><span class="line"><span class="cl"><span class="k">for</span> <span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> <span class="n">r</span><span class="p">)</span> <span class="ow">in</span> <span class="n">circles</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">cv2</span><span class="o">.</span><span class="n">circle</span><span class="p">(</span><span class="n">image</span><span class="p">,</span> <span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">),</span> <span class="n">r</span><span class="p">,</span> <span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">255</span><span class="p">,</span> <span class="mi">0</span><span class="p">),</span> <span class="mi">2</span><span class="p">)</span>  <span class="c1"># 圆周</span>
</span></span><span class="line"><span class="cl">    <span class="n">cv2</span><span class="o">.</span><span class="n">circle</span><span class="p">(</span><span class="n">image</span><span class="p">,</span> <span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">),</span> <span class="mi">3</span><span class="p">,</span> <span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">255</span><span class="p">),</span> <span class="o">-</span><span class="mi">1</span><span class="p">)</span>  <span class="c1"># 圆心</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;检测到 </span><span class="si">{</span><span class="nb">len</span><span class="p">(</span><span class="n">circles</span><span class="p">)</span><span class="si">}</span><span class="s2"> 个冰壶&#34;</span><span class="p">)</span>
</span></span></code></pre></div><h3 id="23-hough-圆检测结果示意">2.3 Hough 圆检测结果示意</h3>
<figure>
<svg viewBox="0 0 500 350" xmlns="http://www.w3.org/2000/svg">
  <defs>
    <style>
      .ice { fill: #e6f3ff; stroke: #88aadd; stroke-width: 2; rx: 8; }
      .stone { fill: #2d2d2d; stroke: #fff; stroke-width: 2; }
      .detected { fill: none; stroke: #00ff00; stroke-width: 3; }
      .text { fill: #333; font-family: Arial, sans-serif; font-size: 12px; }
    </style>
  </defs>
  <!-- 冰面 -->
  <rect class="ice" x="50" y="20" width="400" height="300"/>
  <text class="text" x="250" y="40" text-anchor="middle">冰面</text>
  <!-- 冰壶 -->
  <circle class="stone" cx="120" cy="150" r="40"/>
  <circle class="stone" cx="200" cy="180" r="40"/>
  <circle class="stone" cx="280" cy="120" r="40"/>
  <circle class="stone" cx="350" cy="200" r="40"/>
  <!-- 检测框 -->
  <circle class="detected" cx="120" cy="150" r="40"/>
  <circle class="detected" cx="200" cy="180" r="40"/>
  <circle class="detected" cx="280" cy="120" r="40"/>
  <circle class="detected" cx="350" cy="200" r="40"/>
<p><text class="text" x="250" y="320" text-anchor="middle">Hough 变换可以准确检测已知半径的圆形冰壶</text>
</svg></p>
<figcaption style="text-align:center;color:#888;font-size:12px;margin-top:8px;">Hough 圆检测冰壶效果示意图</figcaption>
</figure>
<h3 id="24-优缺点分析">2.4 优缺点分析</h3>
<p>✅ <strong>优点</strong>：</p>
<ul>
<li>计算量比轮廓检测略大，但仍然很小</li>
<li>专门针对圆形检测，效果比轮廓好</li>
<li>可以直接得到半径和圆心坐标，定位准确</li>
</ul>
<p>❌ <strong>缺点</strong>：</p>
<ul>
<li>需要知道大致半径范围，半径范围设置不好就漏检</li>
<li>重叠圆容易误检</li>
<li>对椭圆形变形处理不好</li>
<li>只能检测圆，不能检测任意形状</li>
</ul>
<h2 id="方案三blob-分析连通域检测">方案三：Blob 分析（连通域检测）</h2>
<h3 id="31-原理简介">3.1 原理简介</h3>
<p>Blob（斑点/连通域）分析是指先找出图像中所有颜色相似的连通区域，然后根据面积、圆形度、惯性比等特征筛选目标。OpenCV 提供了 <code>SimpleBlobDetector</code> 方便使用。</p>
<h3 id="32-opencv-实现">3.2 OpenCV 实现</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">detect_blobs_by_color_and_shape</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="n">image</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">color_lower</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">color_upper</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">min_area</span><span class="o">=</span><span class="mi">100</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">max_area</span><span class="o">=</span><span class="mi">10000</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">min_circularity</span><span class="o">=</span><span class="mf">0.5</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">max_circularity</span><span class="o">=</span><span class="mf">1.0</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">min_inertia_ratio</span><span class="o">=</span><span class="mf">0.5</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">min_convexity</span><span class="o">=</span><span class="mf">0.5</span>
</span></span><span class="line"><span class="cl"><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;
</span></span></span><span class="line"><span class="cl"><span class="s2">    基于 Blob 分析检测指定颜色和形状的物体
</span></span></span><span class="line"><span class="cl"><span class="s2">    
</span></span></span><span class="line"><span class="cl"><span class="s2">    参数:
</span></span></span><span class="line"><span class="cl"><span class="s2">        min_inertia_ratio: 惯性比，圆 ≈ 1，长条 ≈ 0
</span></span></span><span class="line"><span class="cl"><span class="s2">        min_convexity: 凸包面积比，越凸越接近 1
</span></span></span><span class="line"><span class="cl"><span class="s2">    &#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># 颜色分割得到掩码</span>
</span></span><span class="line"><span class="cl">    <span class="n">hsv</span> <span class="o">=</span> <span class="n">cv2</span><span class="o">.</span><span class="n">cvtColor</span><span class="p">(</span><span class="n">image</span><span class="p">,</span> <span class="n">cv2</span><span class="o">.</span><span class="n">COLOR_BGR2HSV</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">mask</span> <span class="o">=</span> <span class="n">cv2</span><span class="o">.</span><span class="n">inRange</span><span class="p">(</span><span class="n">hsv</span><span class="p">,</span> <span class="n">color_lower</span><span class="p">,</span> <span class="n">color_upper</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1"># 设置 Blob 检测器参数</span>
</span></span><span class="line"><span class="cl">    <span class="n">params</span> <span class="o">=</span> <span class="n">cv2</span><span class="o">.</span><span class="n">SimpleBlobDetector_Params</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1"># 按面积过滤</span>
</span></span><span class="line"><span class="cl">    <span class="n">params</span><span class="o">.</span><span class="n">filterByArea</span> <span class="o">=</span> <span class="kc">True</span>
</span></span><span class="line"><span class="cl">    <span class="n">params</span><span class="o">.</span><span class="n">minArea</span> <span class="o">=</span> <span class="n">min_area</span>
</span></span><span class="line"><span class="cl">    <span class="n">params</span><span class="o">.</span><span class="n">maxArea</span> <span class="o">=</span> <span class="n">max_area</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1"># 按圆形度过滤</span>
</span></span><span class="line"><span class="cl">    <span class="n">params</span><span class="o">.</span><span class="n">filterByCircularity</span> <span class="o">=</span> <span class="kc">True</span>
</span></span><span class="line"><span class="cl">    <span class="n">params</span><span class="o">.</span><span class="n">minCircularity</span> <span class="o">=</span> <span class="n">min_circularity</span>
</span></span><span class="line"><span class="cl">    <span class="n">params</span><span class="o">.</span><span class="n">maxCircularity</span> <span class="o">=</span> <span class="n">max_circularity</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1"># 按惯性比过滤（判断是否圆）</span>
</span></span><span class="line"><span class="cl">    <span class="n">params</span><span class="o">.</span><span class="n">filterByInertia</span> <span class="o">=</span> <span class="kc">True</span>
</span></span><span class="line"><span class="cl">    <span class="n">params</span><span class="o">.</span><span class="n">minInertiaRatio</span> <span class="o">=</span> <span class="n">min_inertia_ratio</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1"># 按凸性过滤</span>
</span></span><span class="line"><span class="cl">    <span class="n">params</span><span class="o">.</span><span class="n">filterByConvexity</span> <span class="o">=</span> <span class="kc">True</span>
</span></span><span class="line"><span class="cl">    <span class="n">params</span><span class="o">.</span><span class="n">minConvexity</span> <span class="o">=</span> <span class="n">min_convexity</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1"># 创建检测器并检测</span>
</span></span><span class="line"><span class="cl">    <span class="n">detector</span> <span class="o">=</span> <span class="n">cv2</span><span class="o">.</span><span class="n">SimpleBlobDetector_create</span><span class="p">(</span><span class="n">params</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">keypoints</span> <span class="o">=</span> <span class="n">detector</span><span class="o">.</span><span class="n">detect</span><span class="p">(</span><span class="n">mask</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1"># 提取结果</span>
</span></span><span class="line"><span class="cl">    <span class="n">result</span> <span class="o">=</span> <span class="p">[]</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="n">kp</span> <span class="ow">in</span> <span class="n">keypoints</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">x</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">kp</span><span class="o">.</span><span class="n">pt</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span>
</span></span><span class="line"><span class="cl">        <span class="n">y</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">kp</span><span class="o">.</span><span class="n">pt</span><span class="p">[</span><span class="mi">1</span><span class="p">])</span>
</span></span><span class="line"><span class="cl">        <span class="n">size</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">kp</span><span class="o">.</span><span class="n">size</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">r</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">size</span> <span class="o">/</span> <span class="mi">2</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">result</span><span class="o">.</span><span class="n">append</span><span class="p">((</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> <span class="n">r</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">result</span><span class="p">,</span> <span class="n">mask</span><span class="p">,</span> <span class="n">keypoints</span>
</span></span></code></pre></div><h3 id="33-blob-特征参数说明">3.3 Blob 特征参数说明</h3>
<table>
<thead>
<tr>
<th>参数</th>
<th>取值范围</th>
<th>完美圆形值</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>面积</strong></td>
<td>像素数</td>
<td>-</td>
<td>过滤太小太大的目标</td>
</tr>
<tr>
<td><strong>圆形度</strong></td>
<td>0~1</td>
<td>≈ 1.0</td>
<td>越接近 1 越圆</td>
</tr>
<tr>
<td><strong>凸性</strong></td>
<td>0~1</td>
<td>≈ 1.0</td>
<td>凸包面积 / 轮廓面积</td>
</tr>
<tr>
<td><strong>惯性比</strong></td>
<td>0~1</td>
<td>≈ 1.0</td>
<td>对圆形的度量</td>
</tr>
</tbody>
</table>
<p><strong>圆形度 vs 惯性比</strong>：</p>
<ul>
<li>圆形度对轮廓噪声更敏感，因为它完全基于周长和面积</li>
<li>惯性比基于二阶矩，对噪声鲁棒性更好</li>
</ul>
<h3 id="34-优缺点分析">3.4 优缺点分析</h3>
<p>✅ <strong>优点</strong>：</p>
<ul>
<li>内置参数丰富，可以灵活过滤各种形状</li>
<li>一次检测多个 Blob，批量处理高效</li>
<li>参数直观，容易调整</li>
<li>计算量中等，适合嵌入式</li>
</ul>
<p>❌ <strong>缺点</strong>：</p>
<ul>
<li>需要先做颜色分割，对光照变化敏感</li>
<li>重叠 Blob 难以分开</li>
<li>对于非凸形状处理不好</li>
</ul>
<h2 id="方案四深度学习目标检测yolo--ssd">方案四：深度学习目标检测（YOLO / SSD）</h2>
<h3 id="41-方案架构">4.1 方案架构</h3>
<p>当背景复杂、光照变化大时，传统方法效果不好，可以使用深度学习目标检测直接训练一个检测器：</p>
<figure>
<svg viewBox="0 0 650 320" xmlns="http://www.w3.org/2000/svg">
  <defs>
    <style>
      .box { fill: #1a1a2e; stroke: #4a9eff; stroke-width: 2; rx: 6; }
      .text { fill: #e0e0e0; font-family: Arial, sans-serif; font-size: 13px; text-anchor: middle; }
      .label { fill: #4a9eff; font-family: Arial, sans-serif; font-size: 14px; font-weight: bold; }
      .arrow { stroke: #4a9eff; stroke-width: 2; fill: none; marker-end: url(#arrow); }
    </style>
    <marker id="arrow" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
      <polygon points="0 0, 10 3.5, 0 7" fill="#4a9eff"/>
    </marker>
  </defs>
  <rect class="box" x="50" y="20" width="180" height="60"/>
  <text class="label" x="140" y="45">输入图像</text>
  <text class="text" x="140" y="62">任意尺寸，RGB</text>
  <path class="arrow" d="M230 50 L280 50"/>
  <rect class="box" x="280" y="20" width="240" height="60"/>
  <text class="label" x="400" y="45">YOLO / SSD 前向传播</text>
  <text class="text" x="400" y="62">输出边框 + 类别 + 置信度</text>
  <path class="arrow" d="M400 80 L400 120"/>
  <rect class="box" x="280" y="120" width="240" height="60"/>
  <text class="label" x="400" y="145">NMS 非极大值抑制</text>
  <text class="text" x="400" y="162">去除重叠框，保留最佳检测</text>
  <path class="arrow" d="M400 180 L400 220"/>
  <rect class="box" x="100" y="220" width="450" height="60"/>
  <text class="label" x="325" y="245">输出：所有检测到的目标 (x, y, w, h, class_id, conf)</text>
  <rect class="box" x="500" y="20" x="80" y="160" rx="5"/>
  <text class="label" x="540" y="90" transform="rotate(90, 540, 90)">深度学习</text>
</svg>
<figcaption style="text-align:center;color:#888;font-size:12px;margin-top:8px;">深度学习目标检测流程</figcaption>
</figure>
<h3 id="42-使用-yolov8-检测示例">4.2 使用 YOLOv8 检测示例</h3>
<p>需要先安装 ultralytics：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">pip install ultralytics
</span></span></code></pre></div><p>代码实现：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">ultralytics</span> <span class="kn">import</span> <span class="n">YOLO</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 加载预训练模型（或你自己训练的模型）</span>
</span></span><span class="line"><span class="cl"><span class="n">model</span> <span class="o">=</span> <span class="n">YOLO</span><span class="p">(</span><span class="s2">&#34;yolov8n.pt&#34;</span><span class="p">)</span>  <span class="c1"># 检测自定义类需要重新训练</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 假设我们训练了一个检测 &#34;red_circle&#34;, &#34;blue_square&#34; 的模型</span>
</span></span><span class="line"><span class="cl"><span class="c1"># model = YOLO(&#34;best.pt&#34;)  # 你的训练结果</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">detect_objects_yolo</span><span class="p">(</span><span class="n">image</span><span class="p">,</span> <span class="n">conf_threshold</span><span class="o">=</span><span class="mf">0.5</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;YOLOv8 目标检测&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">results</span> <span class="o">=</span> <span class="n">model</span><span class="p">(</span><span class="n">image</span><span class="p">,</span> <span class="n">conf</span><span class="o">=</span><span class="n">conf_threshold</span><span class="p">)[</span><span class="mi">0</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="n">detections</span> <span class="o">=</span> <span class="p">[]</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="n">box</span> <span class="ow">in</span> <span class="n">results</span><span class="o">.</span><span class="n">boxes</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">x1</span><span class="p">,</span> <span class="n">y1</span><span class="p">,</span> <span class="n">x2</span><span class="p">,</span> <span class="n">y2</span> <span class="o">=</span> <span class="n">box</span><span class="o">.</span><span class="n">xyxy</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="o">.</span><span class="n">tolist</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">        <span class="n">conf</span> <span class="o">=</span> <span class="n">box</span><span class="o">.</span><span class="n">conf</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="o">.</span><span class="n">item</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">        <span class="n">class_id</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">box</span><span class="o">.</span><span class="n">cls</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="o">.</span><span class="n">item</span><span class="p">())</span>
</span></span><span class="line"><span class="cl">        <span class="n">class_name</span> <span class="o">=</span> <span class="n">model</span><span class="o">.</span><span class="n">names</span><span class="p">[</span><span class="n">class_id</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="n">x</span> <span class="o">=</span> <span class="nb">int</span><span class="p">((</span><span class="n">x1</span> <span class="o">+</span> <span class="n">x2</span><span class="p">)</span> <span class="o">/</span> <span class="mi">2</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">y</span> <span class="o">=</span> <span class="nb">int</span><span class="p">((</span><span class="n">y1</span> <span class="o">+</span> <span class="n">y2</span><span class="p">)</span> <span class="o">/</span> <span class="mi">2</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">w</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">x2</span> <span class="o">-</span> <span class="n">x1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">h</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">y2</span> <span class="o">-</span> <span class="n">y1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="n">detections</span><span class="o">.</span><span class="n">append</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">            <span class="s2">&#34;bbox&#34;</span><span class="p">:</span> <span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> <span class="n">w</span><span class="p">,</span> <span class="n">h</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">            <span class="s2">&#34;class_name&#34;</span><span class="p">:</span> <span class="n">class_name</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="s2">&#34;confidence&#34;</span><span class="p">:</span> <span class="n">conf</span>
</span></span><span class="line"><span class="cl">        <span class="p">})</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">detections</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 使用示例</span>
</span></span><span class="line"><span class="cl"><span class="n">image</span> <span class="o">=</span> <span class="n">cv2</span><span class="o">.</span><span class="n">imread</span><span class="p">(</span><span class="s2">&#34;test.jpg&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">detections</span> <span class="o">=</span> <span class="n">detect_objects_yolo</span><span class="p">(</span><span class="n">image</span><span class="p">,</span> <span class="n">conf_threshold</span><span class="o">=</span><span class="mf">0.5</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">for</span> <span class="n">det</span> <span class="ow">in</span> <span class="n">detections</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> <span class="n">w</span><span class="p">,</span> <span class="n">h</span> <span class="o">=</span> <span class="n">det</span><span class="p">[</span><span class="s2">&#34;bbox&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">    <span class="n">name</span> <span class="o">=</span> <span class="n">det</span><span class="p">[</span><span class="s2">&#34;class_name&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">    <span class="n">conf</span> <span class="o">=</span> <span class="n">det</span><span class="p">[</span><span class="s2">&#34;confidence&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="n">x1</span> <span class="o">=</span> <span class="n">x</span> <span class="o">-</span> <span class="n">w</span> <span class="o">//</span> <span class="mi">2</span>
</span></span><span class="line"><span class="cl">    <span class="n">y1</span> <span class="o">=</span> <span class="n">y</span> <span class="o">-</span> <span class="n">h</span> <span class="o">//</span> <span class="mi">2</span>
</span></span><span class="line"><span class="cl">    <span class="n">x2</span> <span class="o">=</span> <span class="n">x</span> <span class="o">+</span> <span class="n">w</span> <span class="o">//</span> <span class="mi">2</span>
</span></span><span class="line"><span class="cl">    <span class="n">y2</span> <span class="o">=</span> <span class="n">y</span> <span class="o">+</span> <span class="n">h</span> <span class="o">//</span> <span class="mi">2</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="n">cv2</span><span class="o">.</span><span class="n">rectangle</span><span class="p">(</span><span class="n">image</span><span class="p">,</span> <span class="p">(</span><span class="n">x1</span><span class="p">,</span> <span class="n">y1</span><span class="p">),</span> <span class="p">(</span><span class="n">x2</span><span class="p">,</span> <span class="n">y2</span><span class="p">),</span> <span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">255</span><span class="p">,</span> <span class="mi">0</span><span class="p">),</span> <span class="mi">2</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">cv2</span><span class="o">.</span><span class="n">putText</span><span class="p">(</span><span class="n">image</span><span class="p">,</span> <span class="sa">f</span><span class="s2">&#34;</span><span class="si">{</span><span class="n">name</span><span class="si">}</span><span class="s2"> </span><span class="si">{</span><span class="n">conf</span><span class="si">:</span><span class="s2">.2f</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">,</span> <span class="p">(</span><span class="n">x1</span><span class="p">,</span> <span class="n">y1</span><span class="o">-</span><span class="mi">10</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">                <span class="n">cv2</span><span class="o">.</span><span class="n">FONT_HERSHEY_SIMPLEX</span><span class="p">,</span> <span class="mf">0.5</span><span class="p">,</span> <span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">255</span><span class="p">,</span> <span class="mi">0</span><span class="p">),</span> <span class="mi">2</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;检测到 </span><span class="si">{</span><span class="nb">len</span><span class="p">(</span><span class="n">detections</span><span class="p">)</span><span class="si">}</span><span class="s2"> 个目标&#34;</span><span class="p">)</span>
</span></span></code></pre></div><h3 id="43-数据收集与训练">4.3 数据收集与训练</h3>
<p>如果要检测特定颜色和形状的组合，你需要收集自己的数据集：</p>
<ol>
<li><strong>收集数据</strong>：拍摄至少 100-200 张包含目标的不同角度、光照下的图片</li>
<li><strong>标注数据</strong>：使用 LabelImg 或 Roboflow 标注边框和类别
<ul>
<li>类别示例：<code>red_circle</code>, <code>red_square</code>, <code>blue_circle</code>, <code>blue_square</code></li>
</ul>
</li>
<li><strong>划分数据集</strong>：训练集 80%，验证集 10%，测试集 10%</li>
<li><strong>训练模型</strong>：使用 YOLOv8 训练
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">yolo detect train <span class="nv">data</span><span class="o">=</span>data.yaml <span class="nv">model</span><span class="o">=</span>yolov8n.pt <span class="nv">epochs</span><span class="o">=</span><span class="m">50</span> <span class="nv">imgsz</span><span class="o">=</span><span class="m">640</span>
</span></span></code></pre></div></li>
<li><strong>量化转换</strong>：如果部署到嵌入式设备，需要转换成 INT8 量化模型</li>
</ol>
<h3 id="44-部署到嵌入式设备">4.4 部署到嵌入式设备</h3>
<ul>
<li><strong>Linux + NPU</strong>：全精度 YOLOv8n 可以在 1GHz 芯片上达到 30+ FPS</li>
<li><strong>MCU + NPU</strong>：例如 ESP32-S3、STM32+CMSIS-NN，可以运行 YOLOv5n 量化版</li>
<li><strong>纯 MCU 无 NPU</strong>：不建议，速度太慢（几秒一帧）</li>
</ul>
<h3 id="45-优缺点分析">4.5 优缺点分析</h3>
<p>✅ <strong>优点</strong>：</p>
<ul>
<li>鲁棒性强，对光照变化、遮挡、背景干扰不敏感</li>
<li>可以同时检测多种颜色形状组合，直接输出分类结果</li>
<li>端到端检测，不需要手动设计多个步骤</li>
<li>数据越多，效果越好</li>
</ul>
<p>❌ <strong>缺点</strong>：</p>
<ul>
<li>计算量大，需要带 NPU 的 MCU 或 Linux 才能实时运行</li>
<li>需要收集标注数据，训练成本高</li>
<li>参数多，存储空间要求大（几 MB 到几十 MB）</li>
<li>调试困难，错了不好分析原因</li>
</ul>
<h2 id="性能对比">性能对比</h2>
<h3 id="51-资源占用对比qvga-320240-图像">5.1 资源占用对比（QVGA 320×240 图像）</h3>
<table>
<thead>
<tr>
<th>方案</th>
<th>CPU 占用（Cortex-M7 @ 200MHz）</th>
<th>帧率</th>
<th>RAM 占用</th>
<th>Flash 占用</th>
</tr>
</thead>
<tbody>
<tr>
<td>颜色分割 + 轮廓</td>
<td>~20%</td>
<td>30-50 FPS</td>
<td>~50KB</td>
<td>~5KB</td>
</tr>
<tr>
<td>Hough 圆检测</td>
<td>~40%</td>
<td>15-30 FPS</td>
<td>~100KB</td>
<td>~8KB</td>
</tr>
<tr>
<td>Blob 分析</td>
<td>~30%</td>
<td>20-40 FPS</td>
<td>~80KB</td>
<td>~7KB</td>
</tr>
<tr>
<td>YOLOv8n（NPU）</td>
<td>~10%（NPU 算）</td>
<td>20-30 FPS</td>
<td>~10MB</td>
<td>~3MB</td>
</tr>
</tbody>
</table>
<blockquote>
<p>测试条件：单目标，图像大小 320×240，ARM Cortex-M7 平台</p>
</blockquote>
<h3 id="52-准确率对比复杂背景">5.2 准确率对比（复杂背景）</h3>
<table>
<thead>
<tr>
<th>方案</th>
<th>颜色变化鲁棒性</th>
<th>形状变化鲁棒性</th>
<th>遮挡鲁棒性</th>
<th>背景干扰鲁棒性</th>
</tr>
</thead>
<tbody>
<tr>
<td>颜色分割 + 轮廓</td>
<td>⭐⭐</td>
<td>⭐⭐⭐</td>
<td>⭐</td>
<td>⭐</td>
</tr>
<tr>
<td>Hough 圆检测</td>
<td>⭐⭐⭐</td>
<td>⭐⭐⭐</td>
<td>⭐⭐</td>
<td>⭐⭐</td>
</tr>
<tr>
<td>Blob 分析</td>
<td>⭐⭐</td>
<td>⭐⭐⭐</td>
<td>⭐⭐</td>
<td>⭐⭐</td>
</tr>
<tr>
<td>YOLO 深度学习</td>
<td>⭐⭐⭐⭐⭐</td>
<td>⭐⭐⭐⭐⭐</td>
<td>⭐⭐⭐⭐</td>
<td>⭐⭐⭐⭐⭐</td>
</tr>
</tbody>
</table>
<p>⭐⭐⭐⭐⭐ = 最好，⭐ = 最差</p>
<h2 id="选型建议">选型建议</h2>
<p>根据你的场景选择最合适的方案：</p>
<table>
<thead>
<tr>
<th>场景</th>
<th>推荐方案</th>
<th>理由</th>
</tr>
</thead>
<tbody>
<tr>
<td>背景简单，光照稳定，纯 MCU 部署</td>
<td><strong>颜色分割 + 轮廓检测</strong></td>
<td>计算量最小，满足实时性</td>
</tr>
<tr>
<td>需要检测多个已知半径的圆形</td>
<td><strong>Hough 变换</strong></td>
<td>专门针对圆形，定位准</td>
</tr>
<tr>
<td>需要检测多个颜色形状，批量处理</td>
<td><strong>Blob 分析</strong></td>
<td>参数丰富，一次检测多个</td>
</tr>
<tr>
<td>背景复杂，光照多变，有 NPU/Linux</td>
<td><strong>YOLO 深度学习</strong></td>
<td>鲁棒性最好，准确率最高</td>
</tr>
</tbody>
</table>
<h2 id="实战案例冰壶检测">实战案例：冰壶检测</h2>
<h3 id="61-问题分析">6.1 问题分析</h3>
<p>冰壶检测特点：</p>
<ul>
<li>冰面背景比较均匀</li>
<li>冰壶是标准圆形，直径固定</li>
<li>颜色和冰面对比明显（冰壶深色，冰面浅色）</li>
<li>需要实时检测（30 FPS 以上）</li>
</ul>
<h3 id="62-推荐方案">6.2 推荐方案</h3>
<p><strong>方案：颜色分割 + Hough 变换</strong></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="c1"># 冰壶检测完整示例</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">detect_curling_stones</span><span class="p">(</span><span class="n">image</span><span class="p">,</span> <span class="n">min_radius</span><span class="o">=</span><span class="mi">60</span><span class="p">,</span> <span class="n">max_radius</span><span class="o">=</span><span class="mi">100</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># 冰壶是深色，冰面是浅色</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># 转灰度 + 阈值分割</span>
</span></span><span class="line"><span class="cl">    <span class="n">gray</span> <span class="o">=</span> <span class="n">cv2</span><span class="o">.</span><span class="n">cvtColor</span><span class="p">(</span><span class="n">image</span><span class="p">,</span> <span class="n">cv2</span><span class="o">.</span><span class="n">COLOR_BGR2GRAY</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">_</span><span class="p">,</span> <span class="n">mask</span> <span class="o">=</span> <span class="n">cv2</span><span class="o">.</span><span class="n">threshold</span><span class="p">(</span><span class="n">gray</span><span class="p">,</span> <span class="mi">100</span><span class="p">,</span> <span class="mi">255</span><span class="p">,</span> <span class="n">cv2</span><span class="o">.</span><span class="n">THRESH_BINARY_INV</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1"># 形态学处理</span>
</span></span><span class="line"><span class="cl">    <span class="n">kernel</span> <span class="o">=</span> <span class="n">cv2</span><span class="o">.</span><span class="n">getStructuringElement</span><span class="p">(</span><span class="n">cv2</span><span class="o">.</span><span class="n">MORPH_ELLIPSE</span><span class="p">,</span> <span class="p">(</span><span class="mi">5</span><span class="p">,</span> <span class="mi">5</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="n">mask</span> <span class="o">=</span> <span class="n">cv2</span><span class="o">.</span><span class="n">morphologyEx</span><span class="p">(</span><span class="n">mask</span><span class="p">,</span> <span class="n">cv2</span><span class="o">.</span><span class="n">MORPH_OPEN</span><span class="p">,</span> <span class="n">kernel</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1"># Hough 检测圆</span>
</span></span><span class="line"><span class="cl">    <span class="n">circles</span> <span class="o">=</span> <span class="n">cv2</span><span class="o">.</span><span class="n">HoughCircles</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="n">mask</span><span class="p">,</span> <span class="n">cv2</span><span class="o">.</span><span class="n">HOUGH_GRADIENT</span><span class="p">,</span> 
</span></span><span class="line"><span class="cl">        <span class="n">dp</span><span class="o">=</span><span class="mf">1.2</span><span class="p">,</span> <span class="n">min_dist</span><span class="o">=</span><span class="mi">80</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">param1</span><span class="o">=</span><span class="mi">50</span><span class="p">,</span> <span class="n">param2</span><span class="o">=</span><span class="mi">25</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">minRadius</span><span class="o">=</span><span class="n">min_radius</span><span class="p">,</span> <span class="n">maxRadius</span><span class="o">=</span><span class="n">max_radius</span>
</span></span><span class="line"><span class="cl">    <span class="p">)</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="n">result</span> <span class="o">=</span> <span class="p">[]</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">circles</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">circles</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">uint16</span><span class="p">(</span><span class="n">np</span><span class="o">.</span><span class="n">around</span><span class="p">(</span><span class="n">circles</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">        <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="n">circles</span><span class="p">[</span><span class="mi">0</span><span class="p">,</span> <span class="p">:]:</span>
</span></span><span class="line"><span class="cl">            <span class="n">result</span><span class="o">.</span><span class="n">append</span><span class="p">((</span><span class="n">i</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="n">i</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span> <span class="n">i</span><span class="p">[</span><span class="mi">2</span><span class="p">]))</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">result</span><span class="p">,</span> <span class="n">mask</span>
</span></span></code></pre></div><p>GitHub 上已经有专门做冰壶检测的项目：https://github.com/AIcurling/Stone-Detection，正好是我们讨论的思路，可以去参考一下。</p>
<h2 id="开源参考代码">开源参考代码</h2>
<p>本文完整代码已上传 GitHub，欢迎 Star/Fork：</p>
<ul>
<li><a href="https://github.com/wh2000hh/color-shape-detection">https://github.com/wh2000hh/color-shape-detection</a></li>
<li>包含：
<ul>
<li>四种方案的完整 Python 实现</li>
<li>HSV 调参工具</li>
<li>评估脚本</li>
<li>冰壶检测示例</li>
</ul>
</li>
</ul>
<h2 id="总结">总结</h2>
<ul>
<li><strong>传统图像处理方案</strong>适合简单背景、纯 MCU 部署，代码量小，速度快，但对环境变化敏感</li>
<li><strong>深度学习方案</strong>适合复杂背景、有 NPU/Linux 的平台，准确率和鲁棒性高，但需要数据和更大算力</li>
<li>根据你的硬件平台和场景复杂度选择合适方案，不要盲目追求深度学习</li>
<li>冰壶这种规则形状+均匀背景，用传统方法就足够了，而且可以做到 30 FPS 以上在 Cortex-M7 上</li>
</ul>
<hr>
<p><em>本文基于 OpenCV 官方文档和实际项目经验整理，适合嵌入式计算机视觉入门学习。</em></p>
<h2 id="参考资料">参考资料</h2>
<ol>
<li>OpenCV 官方文档：Image Segmentation, Hough Transform, Blob Detection</li>
<li>AIcurling/Stone-Detection - GitHub 冰壶检测项目: <a href="https://github.com/AIcurling/Stone-Detection">https://github.com/AIcurling/Stone-Detection</a></li>
<li>Ultralytics YOLOv8 文档: <a href="https://docs.ultralytics.com/">https://docs.ultralytics.com/</a></li>
<li>Learning OpenCV 3: Computer Vision in C++ with the OpenCV Library</li>
<li>Roboflow 自定义数据标注: <a href="https://roboflow.com/">https://roboflow.com/</a></li>
</ol>
]]></content:encoded></item><item><title>STM32 项目实战教程 2026</title><link>https://tech-snippets.xyz/posts/stm32-microcontroller-projects/</link><pubDate>Tue, 14 Apr 2026 03:00:00 +0800</pubDate><guid>https://tech-snippets.xyz/posts/stm32-microcontroller-projects/</guid><description>STM32 微控制器项目实战，从入门到进阶的完整开发指南</description><content:encoded><![CDATA[<h2 id="引言">引言</h2>
<p>本文基于 2026 年最新行业资料整理，涵盖 STM32 microcontroller projects 的核心概念、开发流程和实战技巧。</p>
<h2 id="stm32-系列选择">STM32 系列选择</h2>
<p>ST 提供多个 STM32 系列：</p>
<table>
<thead>
<tr>
<th>系列</th>
<th>内核</th>
<th>频率</th>
<th>应用</th>
</tr>
</thead>
<tbody>
<tr>
<td>F0</td>
<td>Cortex-M0</td>
<td>48MHz</td>
<td>入门级</td>
</tr>
<tr>
<td>F1</td>
<td>Cortex-M3</td>
<td>72MHz</td>
<td>通用型</td>
</tr>
<tr>
<td>F4</td>
<td>Cortex-M4</td>
<td>180MHz</td>
<td>高性能</td>
</tr>
<tr>
<td>H7</td>
<td>Cortex-M7</td>
<td>550MHz</td>
<td>旗舰级</td>
</tr>
</tbody>
</table>
<h2 id="开发环境">开发环境</h2>
<p>推荐使用 STM32CubeIDE：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 安装 STM32CubeMX</span>
</span></span><span class="line"><span class="cl">chmod +x STM32CubeMX.sh
</span></span><span class="line"><span class="cl">./STM32CubeMX.sh
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 生成项目</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 1. 选择 MCU 型号</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 2. 配置时钟、GPIO、外设</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 3. 生成初始化代码</span>
</span></span></code></pre></div><h2 id="gpio-控制">GPIO 控制</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// 点亮 LED
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="nf">LED_Init</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">GPIO_InitTypeDef</span> <span class="n">GPIO_InitStruct</span> <span class="o">=</span> <span class="p">{</span><span class="mi">0</span><span class="p">};</span>
</span></span><span class="line"><span class="cl">    <span class="nf">__HAL_RCC_GPIOA_CLK_ENABLE</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="n">GPIO_InitStruct</span><span class="p">.</span><span class="n">Pin</span> <span class="o">=</span> <span class="n">GPIO_PIN_5</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">GPIO_InitStruct</span><span class="p">.</span><span class="n">Mode</span> <span class="o">=</span> <span class="n">GPIO_MODE_OUTPUT_PP</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">GPIO_InitStruct</span><span class="p">.</span><span class="n">Speed</span> <span class="o">=</span> <span class="n">GPIO_SPEED_FREQ_LOW</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nf">HAL_GPIO_Init</span><span class="p">(</span><span class="n">GPIOA</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">GPIO_InitStruct</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">LED_On</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nf">HAL_GPIO_WritePin</span><span class="p">(</span><span class="n">GPIOA</span><span class="p">,</span> <span class="n">GPIO_PIN_5</span><span class="p">,</span> <span class="n">GPIO_PIN_SET</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h2 id="常用外设">常用外设</h2>
<ul>
<li><strong>UART</strong>：串口通信</li>
<li><strong>SPI</strong>：高速外设（Flash、显示屏）</li>
<li><strong>I2C</strong>：传感器（温湿度、加速度计）</li>
<li><strong>ADC</strong>：模拟信号采集</li>
<li><strong>Timer</strong>：PWM 输出、输入捕获</li>
</ul>
<h2 id="参考资料">参考资料</h2>
<ol>
<li><a href="https://how2electronics.com/microcontrollers/stm32-projects/">STM32 Projects: 100+ STM32F103C8 Based Projects with Code</a></li>
<li><a href="https://circuitdigest.com/stm32-projects-and-tutorials">STM32 (STM32F103C8) Projects &amp; Tutorials</a></li>
<li><a href="https://www.steppeschool.com/blog/stm32-projects-robotics">STM32 Projects for beginners and advanced level - Steppeschool</a></li>
</ol>
<hr>
<p><em>本文基于网络公开资料整理，结合嵌入式开发实践经验编写。</em></p>
]]></content:encoded></item><item><title>IoT 固件开发最佳实践 2026</title><link>https://tech-snippets.xyz/posts/iot-firmware-development/</link><pubDate>Mon, 13 Apr 2026 03:00:00 +0800</pubDate><guid>https://tech-snippets.xyz/posts/iot-firmware-development/</guid><description>IoT 固件开发完整指南，涵盖架构设计、OTA 升级、低功耗和安全通信</description><content:encoded><![CDATA[<h2 id="引言">引言</h2>
<p>本文基于 2026 年最新行业资料整理，涵盖 IoT firmware development 的核心概念、开发流程和实战技巧。</p>
<h2 id="iot-固件架构">IoT 固件架构</h2>
<h3 id="11-现代-iot-固件分层设计">1.1 现代 IoT 固件分层设计</h3>
<p>现代 IoT 固件采用模块化分层架构，每一层都有明确的职责和接口，便于维护、测试和升级。</p>
<figure>
<svg viewBox="0 0 700 320" xmlns="http://www.w3.org/2000/svg" style="max-width:100%;height:auto;background:#f8f9fa;border-radius:8px;padding:20px;">
  <defs>
    <style>
      .layer { fill: #e3f2fd; stroke: #1976d2; stroke-width: 2; rx: 4; }
      .layer-title { font-family: Arial; font-size: 13px; font-weight: bold; fill: #1976d2; }
      .layer-text { font-family: Arial; font-size: 11px; fill: #333; }
      .module { fill: #fff; stroke: #4a9eff; stroke-width: 1.5; rx: 3; }
      .module-text { font-family: Arial; font-size: 10px; fill: #333; }
    </style>
  </defs>
  <!-- 应用层 -->
  <rect x="50" y="20" width="600" height="50" class="layer"/>
  <text x="70" y="40" class="layer-title">应用层（Application）</text>
  <rect x="70" y="50" width="100" height="35" class="module"/>
  <text x="120" y="72" text-anchor="middle" class="module-text">MQTT 客户端</text>
  <rect x="180" y="50" width="100" height="35" class="module"/>
  <text x="230" y="72" text-anchor="middle" class="module-text">HTTP 客户端</text>
  <rect x="290" y="50" width="100" height="35" class="module"/>
  <text x="340" y="72" text-anchor="middle" class="module-text">CoAP 客户端</text>
  <!-- 中间件层 -->
  <rect x="50" y="85" width="600" height="50" class="layer" style="fill:#c8e6c9;stroke:#388e3c;"/>
  <text x="70" y="105" class="layer-title" style="fill:#388e3c;">中间件层（Middleware）</text>
  <rect x="70" y="115" width="100" height="35" class="module"/>
  <text x="120" y="130" text-anchor="middle" class="module-text">FreeRTOS</text>
  <text x="120" y="143" text-anchor="middle" class="module-text" font-size="9">(RTOS)</text>
  <rect x="180" y="115" width="100" height="35" class="module"/>
  <text x="230" y="130" text-anchor="middle" class="module-text">LwIP</text>
  <text x="230" y="143" text-anchor="middle" class="module-text" font-size="9">(网络栈)</text>
  <rect x="290" y="115" width="100" height="35" class="module"/>
  <text x="340" y="130" text-anchor="middle" class="module-text">mbedTLS</text>
  <text x="340" y="143" text-anchor="middle" class="module-text" font-size="9">(加密)</text>
  <!-- 驱动层 -->
  <rect x="50" y="150" width="600" height="50" class="layer" style="fill:#fff3e0;stroke:#f57c00;"/>
  <text x="70" y="170" class="layer-title" style="fill:#f57c00;">驱动层（Driver）</text>
  <rect x="70" y="180" width="70" height="30" class="module"/>
  <text x="105" y="200" text-anchor="middle" class="module-text">GPIO</text>
  <rect x="150" y="180" width="70" height="30" class="module"/>
  <text x="185" y="200" text-anchor="middle" class="module-text">SPI</text>
  <rect x="230" y="180" width="70" height="30" class="module"/>
  <text x="265" y="200" text-anchor="middle" class="module-text">I2C</text>
  <rect x="310" y="180" width="70" height="30" class="module"/>
  <text x="345" y="200" text-anchor="middle" class="module-text">UART</text>
  <rect x="390" y="180" width="70" height="30" class="module"/>
  <text x="425" y="200" text-anchor="middle" class="module-text">ADC</text>
  <rect x="470" y="180" width="70" height="30" class="module"/>
  <text x="505" y="200" text-anchor="middle" class="module-text">PWM</text>
  <!-- HAL 层 -->
  <rect x="50" y="215" width="600" height="45" class="layer" style="fill:#f3e5f5;stroke:#7b1fa2;"/>
  <text x="70" y="235" class="layer-title" style="fill:#7b1fa2;">硬件抽象层（HAL）</text>
  <text x="350" y="255" text-anchor="middle" class="layer-text">STM32 HAL / ESP-IDF / Nordic SDK</text>
  <!-- 硬件层 -->
  <rect x="50" y="270" width="600" height="40" class="layer" style="fill:#ffebee;stroke:#c62828;"/>
  <text x="70" y="290" class="layer-title" style="fill:#c62828;">硬件层（Hardware）</text>
  <text x="350" y="310" text-anchor="middle" class="layer-text">Cortex-M / ESP32 / nRF52 / RISC-V</text>
</svg>
<figcaption style="text-align:center;color:#888;font-size:12px;margin-top:8px;">现代 IoT 固件分层架构图</figcaption>
</figure>
<h3 id="12-各层职责详解">1.2 各层职责详解</h3>
<table>
<thead>
<tr>
<th>层级</th>
<th>职责</th>
<th>典型组件</th>
<th>更换频率</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>应用层</strong></td>
<td>业务逻辑、用户功能</td>
<td>MQTT 客户端、传感器数据处理</td>
<td>高（频繁迭代）</td>
</tr>
<tr>
<td><strong>中间件层</strong></td>
<td>通用服务、协议栈</td>
<td>FreeRTOS、LwIP、mbedTLS</td>
<td>中（版本升级）</td>
</tr>
<tr>
<td><strong>驱动层</strong></td>
<td>外设驱动、硬件抽象</td>
<td>GPIO、SPI、I2C、UART</td>
<td>低（稳定）</td>
</tr>
<tr>
<td><strong>HAL 层</strong></td>
<td>芯片厂商提供</td>
<td>STM32 HAL、ESP-IDF</td>
<td>低（跟随 SDK）</td>
</tr>
<tr>
<td><strong>硬件层</strong></td>
<td>物理芯片</td>
<td>Cortex-M4、ESP32</td>
<td>极低（产品周期）</td>
</tr>
</tbody>
</table>
<h3 id="13-模块化设计原则">1.3 模块化设计原则</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// 1. 接口与实现分离（头文件定义接口）
</span></span></span><span class="line"><span class="cl"><span class="c1">// sensor_interface.h
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#ifndef SENSOR_INTERFACE_H
</span></span></span><span class="line"><span class="cl"><span class="cp">#define SENSOR_INTERFACE_H
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="p">(</span><span class="o">*</span><span class="n">init</span><span class="p">)(</span><span class="kt">void</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="p">(</span><span class="o">*</span><span class="n">read</span><span class="p">)(</span><span class="kt">float</span> <span class="o">*</span><span class="n">value</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="p">(</span><span class="o">*</span><span class="n">calibrate</span><span class="p">)(</span><span class="kt">void</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="kt">sensor_ops_t</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 外部声明具体实现
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">extern</span> <span class="kt">sensor_ops_t</span> <span class="n">dht22_ops</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">extern</span> <span class="kt">sensor_ops_t</span> <span class="n">bmp280_ops</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="cp">#endif
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="c1">// 2. 依赖注入（便于测试和替换）
</span></span></span><span class="line"><span class="cl"><span class="c1">// main.c
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="nf">sensor_task</span><span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="n">pvParameters</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 通过指针传入具体实现，而非硬编码
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="kt">sensor_ops_t</span> <span class="o">*</span><span class="n">sensor</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">dht22_ops</span><span class="p">;</span>  <span class="c1">// 可轻松切换为 bmp280_ops
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    
</span></span><span class="line"><span class="cl">    <span class="n">sensor</span><span class="o">-&gt;</span><span class="nf">init</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="k">while</span> <span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="kt">float</span> <span class="n">value</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">sensor</span><span class="o">-&gt;</span><span class="nf">read</span><span class="p">(</span><span class="o">&amp;</span><span class="n">value</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nf">publish_data</span><span class="p">(</span><span class="n">value</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="nf">vTaskDelay</span><span class="p">(</span><span class="nf">pdMS_TO_TICKS</span><span class="p">(</span><span class="mi">1000</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 3. 配置与代码分离
</span></span></span><span class="line"><span class="cl"><span class="c1">// config.h
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#define CONFIG_MQTT_BROKER      &#34;mqtt.example.com&#34;
</span></span></span><span class="line"><span class="cl"><span class="cp">#define CONFIG_MQTT_PORT        1883
</span></span></span><span class="line"><span class="cl"><span class="cp">#define CONFIG_MQTT_KEEPALIVE   60
</span></span></span><span class="line"><span class="cl"><span class="cp">#define CONFIG_WIFI_SSID        &#34;MyNetwork&#34;
</span></span></span><span class="line"><span class="cl"><span class="cp">#define CONFIG_WIFI_PASSWORD    &#34;SecretPassword&#34;
</span></span></span></code></pre></div><h2 id="ota-升级firmware-over-the-air">OTA 升级（Firmware Over-The-Air）</h2>
<h3 id="21-ota-架构设计">2.1 OTA 架构设计</h3>
<figure>
<svg viewBox="0 0 700 400" xmlns="http://www.w3.org/2000/svg">
  <defs>
    <style>
      .box { fill: #1a1a2e; stroke: #4a9eff; stroke-width: 2; rx: 6; }
      .text { fill: #e0e0e0; font-family: Arial, sans-serif; font-size: 13px; text-anchor: middle; }
      .label { fill: #4a9eff; font-family: Arial, sans-serif; font-size: 15px; font-weight: bold; }
      .arrow { stroke: #4a9eff; stroke-width: 2; fill: none; marker-end: url(#arrow); }
      .dashed { stroke: #e74c3c; stroke-width: 2; stroke-dasharray: 5,3; }
    </style>
    <marker id="arrow" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
      <polygon points="0 0, 10 3.5, 0 7" fill="#4a9eff"/>
    </marker>
  </defs>
  <!-- 云端 -->
  <rect class="box" x="50" y="20" width="200" height="100"/>
  <text class="label" x="150" y="55">OTA 服务器</text>
  <text class="text" x="150" y="85">固件存储 + 版本管理</text>
  <text class="text" x="150" y="105">签名服务</text>
  <!-- 通信 -->
  <line class="arrow" x1="250" y1="70" x2="350" y2="70"/>
  <text class="text" x="300" y="60" font-size="11">HTTPS 下载</text>
  <!-- 设备 -->
  <rect class="box" x="350" y="20" width="300" height="150"/>
  <text class="label" x="500" y="50">IoT 设备</text>
  <!-- Bootloader -->
  <rect class="box" x="370" y="65" width="120" height="40"/>
  <text class="text" x="430" y="90">Bootloader</text>
  <!-- A/B 分区 -->
  <rect class="box" x="510" y="65" width="60" height="40" style="fill:#2a3a2e;stroke:#388e3c;"/>
  <text class="text" x="540" y="85">A 区</text>
  <text class="text" x="540" y="100" fill="#388e3c">运行中</text>
  <rect class="box" x="580" y="65" width="60" height="40" style="fill:#3a2a2e;stroke:#e74c3c;"/>
  <text class="text" x="610" y="85">B 区</text>
  <text class="text" x="610" y="100" fill="#e74c3c">更新中</text>
  <!-- 箭头回云端 -->
  <line class="dashed" x1="350" y1="100" x2="250" y2="100"/>
  <text class="text" x="300" y="120" font-size="11">状态上报</text>
  <!-- 设备内部流程 -->
  <line class="arrow" x1="490" y1="85" x2="510" y2="85"/>
  <text class="text" x="500" y="75" font-size="10">验证</text>
</svg>
<figcaption style="text-align:center;color:#888;font-size:12px;margin-top:8px;">OTA 升级架构图</figcaption>
</figure>
<h3 id="22-ab-分区升级策略">2.2 A/B 分区升级策略</h3>
<p>A/B 分区（也称为双分区）是最安全的 OTA 升级方案：</p>
<table>
<thead>
<tr>
<th>特性</th>
<th>A 分区</th>
<th>B 分区</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>当前状态</strong></td>
<td>运行中（Active）</td>
<td>空闲/更新中（Inactive）</td>
</tr>
<tr>
<td><strong>升级时</strong></td>
<td>保持运行</td>
<td>写入新固件</td>
</tr>
<tr>
<td><strong>升级后</strong></td>
<td>变为备份</td>
<td>变为活动</td>
</tr>
<tr>
<td><strong>回滚</strong></td>
<td>可回滚到 A</td>
<td>可回滚到 B</td>
</tr>
</tbody>
</table>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// Flash 分区表示例（ESP32 风格）
</span></span></span><span class="line"><span class="cl"><span class="c1">// 假设 Flash 总大小 4MB
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">uint32_t</span> <span class="n">magic</span><span class="p">;</span>         <span class="c1">// 分区表魔数
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="kt">uint32_t</span> <span class="n">version</span><span class="p">;</span>       <span class="c1">// 分区表版本
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="kt">uint32_t</span> <span class="n">ota_seq</span><span class="p">;</span>       <span class="c1">// OTA 序列号（判断哪个分区是活动的）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="kt">uint8_t</span>  <span class="n">ota_data</span><span class="p">[</span><span class="mi">32</span><span class="p">];</span>  <span class="c1">// OTA 状态数据
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="kt">uint32_t</span> <span class="n">crc</span><span class="p">;</span>           <span class="c1">// CRC32 校验
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span> <span class="kt">ota_data_t</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 分区布局
</span></span></span><span class="line"><span class="cl"><span class="c1">// 0x0000 - 0x1000:   引导加载程序（Bootloader）
</span></span></span><span class="line"><span class="cl"><span class="c1">// 0x1000 - 0x2000:   分区表
</span></span></span><span class="line"><span class="cl"><span class="c1">// 0x2000 - 0x3000:   OTA 数据（ota_data）
</span></span></span><span class="line"><span class="cl"><span class="c1">// 0x3000 - 0x100000: 应用 A 区（约 988KB）
</span></span></span><span class="line"><span class="cl"><span class="c1">// 0x100000 - 0x1FD000: 应用 B 区（约 988KB）
</span></span></span><span class="line"><span class="cl"><span class="c1">// 0x1FD000 - 0x200000: NVS（非易失存储）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="c1">// OTA 升级流程
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">typedef</span> <span class="k">enum</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">OTA_STATE_IDLE</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">OTA_STATE_DOWNLOADING</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">OTA_STATE_VERIFYING</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">OTA_STATE_FLASHING</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">OTA_STATE_VALIDATED</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">OTA_STATE_INVALID</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="kt">ota_state_t</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">ota_state_t</span> <span class="n">current_ota_state</span> <span class="o">=</span> <span class="n">OTA_STATE_IDLE</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// OTA 任务
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="nf">ota_task</span><span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="n">pvParameters</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">while</span> <span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// 1. 检查是否有新版本
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="k">if</span> <span class="p">(</span><span class="nf">check_new_firmware_version</span><span class="p">())</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">current_ota_state</span> <span class="o">=</span> <span class="n">OTA_STATE_DOWNLOADING</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            
</span></span><span class="line"><span class="cl">            <span class="c1">// 2. 下载新固件到非活动分区
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="kt">uint32_t</span> <span class="n">inactive_partition</span> <span class="o">=</span> <span class="nf">get_inactive_partition</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">            <span class="kt">int</span> <span class="n">ret</span> <span class="o">=</span> <span class="nf">download_firmware</span><span class="p">(</span><span class="n">inactive_partition</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            
</span></span><span class="line"><span class="cl">            <span class="k">if</span> <span class="p">(</span><span class="n">ret</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="n">current_ota_state</span> <span class="o">=</span> <span class="n">OTA_STATE_VERIFYING</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">                
</span></span><span class="line"><span class="cl">                <span class="c1">// 3. 验证签名
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>                <span class="k">if</span> <span class="p">(</span><span class="nf">verify_firmware_signature</span><span class="p">(</span><span class="n">inactive_partition</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                    <span class="n">current_ota_state</span> <span class="o">=</span> <span class="n">OTA_STATE_VALIDATED</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">                    
</span></span><span class="line"><span class="cl">                    <span class="c1">// 4. 更新 OTA 数据，标记下次从新分区启动
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>                    <span class="nf">set_next_boot_partition</span><span class="p">(</span><span class="n">inactive_partition</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">                    
</span></span><span class="line"><span class="cl">                    <span class="c1">// 5. 重启
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>                    <span class="nf">esp_restart</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">                <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                    <span class="n">current_ota_state</span> <span class="o">=</span> <span class="n">OTA_STATE_INVALID</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">                    <span class="nf">report_error</span><span class="p">(</span><span class="s">&#34;Signature verification failed&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">                <span class="p">}</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="n">current_ota_state</span> <span class="o">=</span> <span class="n">OTA_STATE_IDLE</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">                <span class="nf">report_error</span><span class="p">(</span><span class="s">&#34;Download failed&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="c1">// 每 6 小时检查一次
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="nf">vTaskDelay</span><span class="p">(</span><span class="nf">pdMS_TO_TICKS</span><span class="p">(</span><span class="mi">6</span> <span class="o">*</span> <span class="mi">60</span> <span class="o">*</span> <span class="mi">60</span> <span class="o">*</span> <span class="mi">1000</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="23-安全-ota-实现">2.3 安全 OTA 实现</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// 完整的 OTA 升级流程（包含安全验证）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#include</span> <span class="cpf">&lt;mbedtls/sha256.h&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;mbedtls/pk.h&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">uint32_t</span> <span class="n">magic</span><span class="p">;</span>             <span class="c1">// 魔数：0xABCDEF00
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="kt">uint32_t</span> <span class="n">version</span><span class="p">;</span>           <span class="c1">// 固件版本号
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="kt">uint32_t</span> <span class="n">timestamp</span><span class="p">;</span>         <span class="c1">// 编译时间戳
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="kt">uint32_t</span> <span class="n">image_size</span><span class="p">;</span>        <span class="c1">// 固件大小
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="kt">uint8_t</span>  <span class="n">image_hash</span><span class="p">[</span><span class="mi">32</span><span class="p">];</span>    <span class="c1">// SHA-256 哈希
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="kt">uint8_t</span>  <span class="n">signature</span><span class="p">[</span><span class="mi">64</span><span class="p">];</span>     <span class="c1">// ECDSA 签名
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span> <span class="kt">firmware_header_t</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">perform_ota_update</span><span class="p">(</span><span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">firmware_url</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">firmware_header_t</span> <span class="n">header</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">uint8_t</span> <span class="n">download_buffer</span><span class="p">[</span><span class="mi">4096</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">    <span class="kt">uint32_t</span> <span class="n">total_downloaded</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 1. 下载并解析头部
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="kt">int</span> <span class="n">ret</span> <span class="o">=</span> <span class="nf">http_download_range</span><span class="p">(</span><span class="n">firmware_url</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">header</span><span class="p">),</span> <span class="o">&amp;</span><span class="n">header</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">ret</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nf">ESP_LOGE</span><span class="p">(</span><span class="s">&#34;OTA&#34;</span><span class="p">,</span> <span class="s">&#34;Failed to download header&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 2. 验证魔数
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">if</span> <span class="p">(</span><span class="n">header</span><span class="p">.</span><span class="n">magic</span> <span class="o">!=</span> <span class="mh">0xABCDEF00</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nf">ESP_LOGE</span><span class="p">(</span><span class="s">&#34;OTA&#34;</span><span class="p">,</span> <span class="s">&#34;Invalid firmware magic&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="o">-</span><span class="mi">2</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 3. 验证签名（使用固化在设备中的公钥）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">ret</span> <span class="o">=</span> <span class="nf">verify_signature_with_public_key</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="o">&amp;</span><span class="n">header</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">header</span><span class="p">.</span><span class="n">signature</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="k">sizeof</span><span class="p">(</span><span class="n">header</span><span class="p">.</span><span class="n">signature</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="n">g_device_public_key</span>
</span></span><span class="line"><span class="cl">    <span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">ret</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nf">ESP_LOGE</span><span class="p">(</span><span class="s">&#34;OTA&#34;</span><span class="p">,</span> <span class="s">&#34;Signature verification failed&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="o">-</span><span class="mi">3</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 4. 擦除目标分区
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="kt">uint32_t</span> <span class="n">target_partition</span> <span class="o">=</span> <span class="nf">get_inactive_partition</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="nf">spi_flash_erase_range</span><span class="p">(</span><span class="n">target_partition</span><span class="p">,</span> <span class="n">header</span><span class="p">.</span><span class="n">image_size</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 5. 下载固件主体并写入 Flash
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">ESP_LOGI</span><span class="p">(</span><span class="s">&#34;OTA&#34;</span><span class="p">,</span> <span class="s">&#34;Downloading firmware...&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="kt">uint32_t</span> <span class="n">offset</span> <span class="o">=</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">header</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="k">while</span> <span class="p">(</span><span class="n">total_downloaded</span> <span class="o">&lt;</span> <span class="n">header</span><span class="p">.</span><span class="n">image_size</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="kt">uint32_t</span> <span class="n">chunk_size</span> <span class="o">=</span> <span class="nf">MIN</span><span class="p">(</span><span class="k">sizeof</span><span class="p">(</span><span class="n">download_buffer</span><span class="p">),</span> <span class="n">header</span><span class="p">.</span><span class="n">image_size</span> <span class="o">-</span> <span class="n">total_downloaded</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="n">ret</span> <span class="o">=</span> <span class="nf">http_download_range</span><span class="p">(</span><span class="n">firmware_url</span><span class="p">,</span> <span class="n">offset</span><span class="p">,</span> <span class="n">chunk_size</span><span class="p">,</span> <span class="n">download_buffer</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">ret</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nf">ESP_LOGE</span><span class="p">(</span><span class="s">&#34;OTA&#34;</span><span class="p">,</span> <span class="s">&#34;Download failed at offset %lu&#34;</span><span class="p">,</span> <span class="n">offset</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="o">-</span><span class="mi">4</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="c1">// 写入 Flash
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="nf">spi_flash_write</span><span class="p">(</span><span class="n">target_partition</span> <span class="o">+</span> <span class="n">offset</span><span class="p">,</span> <span class="n">download_buffer</span><span class="p">,</span> <span class="n">chunk_size</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="n">offset</span> <span class="o">+=</span> <span class="n">chunk_size</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">total_downloaded</span> <span class="o">+=</span> <span class="n">chunk_size</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="c1">// 报告进度
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="kt">int</span> <span class="n">progress</span> <span class="o">=</span> <span class="p">(</span><span class="n">total_downloaded</span> <span class="o">*</span> <span class="mi">100</span><span class="p">)</span> <span class="o">/</span> <span class="n">header</span><span class="p">.</span><span class="n">image_size</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="nf">ESP_LOGI</span><span class="p">(</span><span class="s">&#34;OTA&#34;</span><span class="p">,</span> <span class="s">&#34;Progress: %d%%&#34;</span><span class="p">,</span> <span class="n">progress</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="nf">report_ota_progress</span><span class="p">(</span><span class="n">progress</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 6. 验证下载的哈希
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="kt">uint8_t</span> <span class="n">calculated_hash</span><span class="p">[</span><span class="mi">32</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">    <span class="nf">calculate_flash_hash</span><span class="p">(</span><span class="n">target_partition</span><span class="p">,</span> <span class="n">header</span><span class="p">.</span><span class="n">image_size</span><span class="p">,</span> <span class="n">calculated_hash</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="nf">memcmp</span><span class="p">(</span><span class="n">calculated_hash</span><span class="p">,</span> <span class="n">header</span><span class="p">.</span><span class="n">image_hash</span><span class="p">,</span> <span class="mi">32</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nf">ESP_LOGE</span><span class="p">(</span><span class="s">&#34;OTA&#34;</span><span class="p">,</span> <span class="s">&#34;Hash verification failed&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="o">-</span><span class="mi">5</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 7. 设置下次启动分区
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">set_next_boot_partition</span><span class="p">(</span><span class="n">target_partition</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="nf">ESP_LOGI</span><span class="p">(</span><span class="s">&#34;OTA&#34;</span><span class="p">,</span> <span class="s">&#34;OTA update successful, rebooting...&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 8. 重启
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">vTaskDelay</span><span class="p">(</span><span class="nf">pdMS_TO_TICKS</span><span class="p">(</span><span class="mi">1000</span><span class="p">));</span>  <span class="c1">// 等待日志输出
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">esp_restart</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 回滚机制：如果新固件启动失败，自动回滚到旧版本
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="nf">app_main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 检查启动原因
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="kt">esp_reset_reason_t</span> <span class="n">reset_reason</span> <span class="o">=</span> <span class="nf">esp_reset_reason</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">reset_reason</span> <span class="o">==</span> <span class="n">ESP_RST_PANIC</span> <span class="o">||</span> <span class="n">reset_reason</span> <span class="o">==</span> <span class="n">ESP_RST_INT_WDT</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// 异常重启，可能是新固件有问题
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="nf">ESP_LOGE</span><span class="p">(</span><span class="s">&#34;BOOT&#34;</span><span class="p">,</span> <span class="s">&#34;Abnormal reset detected, rolling back...&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="c1">// 回滚到之前的分区
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="nf">rollback_to_previous_partition</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 标记当前分区为有效（看门狗机制）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">mark_current_partition_valid</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 正常运行
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">run_application</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h2 id="低功耗设计">低功耗设计</h2>
<h3 id="31-功耗分析">3.1 功耗分析</h3>
<table>
<thead>
<tr>
<th>模式</th>
<th>ESP32 典型电流</th>
<th>STM32L4 典型电流</th>
<th>适用场景</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>运行（全速）</strong></td>
<td>80-240mA</td>
<td>10-50mA</td>
<td>数据处理、通信</td>
</tr>
<tr>
<td><strong>运行（降频）</strong></td>
<td>40-100mA</td>
<td>5-20mA</td>
<td>常规任务</td>
</tr>
<tr>
<td><strong>睡眠（Light Sleep）</strong></td>
<td>0.8mA</td>
<td>5-10μA</td>
<td>等待事件</td>
</tr>
<tr>
<td><strong>深度睡眠（Deep Sleep）</strong></td>
<td>10-20μA</td>
<td>1-3μA</td>
<td>电池供电</td>
</tr>
<tr>
<td><strong>休眠（Hibernate）</strong></td>
<td>2.5μA</td>
<td>0.5μA</td>
<td>超长待机</td>
</tr>
</tbody>
</table>
<h3 id="32-动态电压频率调节dvfs">3.2 动态电压频率调节（DVFS）</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// ESP32 DVFS 配置
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#include</span> <span class="cpf">&#34;esp_pm.h&#34;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">configure_dvfs</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">esp_pm_config_esp32_t</span> <span class="n">pm_config</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="p">.</span><span class="n">max_freq_mhz</span> <span class="o">=</span> <span class="mi">240</span><span class="p">,</span>      <span class="c1">// 最大频率
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="p">.</span><span class="n">min_freq_mhz</span> <span class="o">=</span> <span class="mi">80</span><span class="p">,</span>       <span class="c1">// 最小频率
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="p">.</span><span class="n">light_sleep_enable</span> <span class="o">=</span> <span class="nb">true</span>  <span class="c1">// 启用 Light Sleep
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="p">};</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="nf">esp_pm_configure</span><span class="p">(</span><span class="o">&amp;</span><span class="n">pm_config</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 根据负载动态调整频率
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="nf">adjust_frequency_based_on_load</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">static</span> <span class="kt">uint32_t</span> <span class="n">idle_count</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="nf">is_system_idle</span><span class="p">())</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">idle_count</span><span class="o">++</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">idle_count</span> <span class="o">&gt;</span> <span class="mi">100</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="c1">// 空闲时间长，降低频率
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="nf">esp_pm_cpu_freq_set</span><span class="p">(</span><span class="n">ESP_PM_CPU_FREQ_MAX_80M</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">idle_count</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="c1">// 有任务，提升频率
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="nf">esp_pm_cpu_freq_set</span><span class="p">(</span><span class="n">ESP_PM_CPU_FREQ_MAX_240M</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="33-深度睡眠实现">3.3 深度睡眠实现</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// STM32 深度睡眠配置
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#include</span> <span class="cpf">&lt;stm32l4xx_hal.h&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">enter_deep_sleep</span><span class="p">(</span><span class="kt">uint32_t</span> <span class="n">wake_time_ms</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 1. 配置唤醒源（RTC、外部中断等）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">RTC_HandleTypeDef</span> <span class="n">hrtc</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 配置 RTC 唤醒定时器
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">hrtc</span><span class="p">.</span><span class="n">Instance</span> <span class="o">=</span> <span class="n">RTC</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nf">HAL_RTCEx_SetWakeUpTimer</span><span class="p">(</span><span class="o">&amp;</span><span class="n">hrtc</span><span class="p">,</span> <span class="n">wake_time_ms</span> <span class="o">/</span> <span class="mi">1000</span><span class="p">,</span> <span class="n">RTC_WAKEUPCLOCK_CK_SPRE_16BITS</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 2. 配置 GPIO 为低功耗模式
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">GPIO_InitTypeDef</span> <span class="n">GPIO_InitStruct</span> <span class="o">=</span> <span class="p">{</span><span class="mi">0</span><span class="p">};</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 所有未使用引脚设为模拟输入（最低功耗）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="mi">16</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nf">is_pin_used</span><span class="p">(</span><span class="n">i</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">GPIO_InitStruct</span><span class="p">.</span><span class="n">Pin</span> <span class="o">=</span> <span class="p">(</span><span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="n">i</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="n">GPIO_InitStruct</span><span class="p">.</span><span class="n">Mode</span> <span class="o">=</span> <span class="n">GPIO_MODE_ANALOG</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="n">GPIO_InitStruct</span><span class="p">.</span><span class="n">Pull</span> <span class="o">=</span> <span class="n">GPIO_NOPULL</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="nf">HAL_GPIO_Init</span><span class="p">(</span><span class="n">GPIOA</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">GPIO_InitStruct</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 3. 关闭未使用外设时钟
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">__HAL_RCC_USART2_CLK_DISABLE</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="nf">__HAL_RCC_SPI2_CLK_DISABLE</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="nf">__HAL_RCC_I2C1_CLK_DISABLE</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="nf">__HAL_RCC_ADC_CLK_DISABLE</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 4. 禁用 SysTick 中断（睡眠期间不需要）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">SysTick</span><span class="o">-&gt;</span><span class="n">CTRL</span> <span class="o">&amp;=</span> <span class="o">~</span><span class="n">SysTick_CTRL_TICKINT_Msk</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 5. 进入 STOP2 模式（最低功耗，保留 SRAM 内容）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">HAL_PWREx_EnterSTOP2Mode</span><span class="p">(</span><span class="n">PWR_STOPENTRY_LOWPOWER</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 唤醒后从这里继续执行
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="c1">// 6. 恢复系统时钟
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">SystemClock_Config</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 7. 恢复外设时钟
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">__HAL_RCC_USART2_CLK_ENABLE</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="nf">__HAL_RCC_SPI2_CLK_ENABLE</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 8. 重新配置 GPIO
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">gpio_init_all</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 9. 清除唤醒标志
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">__HAL_PWR_CLEAR_FLAG</span><span class="p">(</span><span class="n">PWR_FLAG_WU</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 电池供电设备的功耗优化任务
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="nf">power_management_task</span><span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="n">pvParameters</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">uint32_t</span> <span class="n">battery_voltage</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="k">while</span> <span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// 读取电池电压
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="n">battery_voltage</span> <span class="o">=</span> <span class="nf">read_battery_voltage</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">battery_voltage</span> <span class="o">&lt;</span> <span class="mi">3300</span><span class="p">)</span> <span class="p">{</span>  <span class="c1">// 低于 3.3V
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="c1">// 进入省电模式
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="nf">enter_power_saving_mode</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">battery_voltage</span> <span class="o">&lt;</span> <span class="mi">3000</span><span class="p">)</span> <span class="p">{</span>  <span class="c1">// 低于 3.0V
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="c1">// 紧急模式：只保留基本功能
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="nf">enter_emergency_mode</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="c1">// 正常模式
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="nf">enter_normal_mode</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="c1">// 每 5 分钟检查一次
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="nf">vTaskDelay</span><span class="p">(</span><span class="nf">pdMS_TO_TICKS</span><span class="p">(</span><span class="mi">5</span> <span class="o">*</span> <span class="mi">60</span> <span class="o">*</span> <span class="mi">1000</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="34-无线通信功耗优化">3.4 无线通信功耗优化</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// WiFi 功耗优化（ESP32）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#include</span> <span class="cpf">&#34;esp_wifi.h&#34;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">configure_wifi_power_save</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 启用 WiFi 节能模式
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">esp_wifi_set_ps</span><span class="p">(</span><span class="n">WIFI_PS_MAX_MODEM</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 配置 DTIM 监听间隔（越大越省电，但延迟越高）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="c1">// DTIM = 3 表示每 3 个信标帧唤醒一次
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="kt">wifi_ps_type_t</span> <span class="n">ps_type</span> <span class="o">=</span> <span class="n">WIFI_PS_MAX_MODEM</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nf">esp_wifi_set_ps</span><span class="p">(</span><span class="n">ps_type</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// BLE 广播功耗优化
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#include</span> <span class="cpf">&#34;nimble/nimble_port.h&#34;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">configure_ble_advertising</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 配置广播参数
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">struct</span> <span class="n">ble_gap_adv_params</span> <span class="n">adv_params</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="n">adv_params</span><span class="p">.</span><span class="n">conn_mode</span> <span class="o">=</span> <span class="n">BLE_GAP_CONN_MODE_NON</span><span class="p">;</span>  <span class="c1">// 不可连接
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">adv_params</span><span class="p">.</span><span class="n">disc_mode</span> <span class="o">=</span> <span class="n">BLE_GAP_DISC_MODE_GEN</span><span class="p">;</span>  <span class="c1">// 可发现
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">adv_params</span><span class="p">.</span><span class="n">itvl_min</span> <span class="o">=</span> <span class="mh">0x0640</span><span class="p">;</span>  <span class="c1">// 1 秒（0x0640 = 1000ms）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">adv_params</span><span class="p">.</span><span class="n">itvl_max</span> <span class="o">=</span> <span class="mh">0x0C80</span><span class="p">;</span>  <span class="c1">// 2 秒
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    
</span></span><span class="line"><span class="cl">    <span class="c1">// 较长的广播间隔 = 更低的功耗
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">ble_gap_adv_start</span><span class="p">(</span><span class="n">OWN_ADDR_TYPE_PUBLIC</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">,</span> <span class="n">BLE_HS_FOREVER</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                      <span class="o">&amp;</span><span class="n">adv_params</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h2 id="无线通信协议对比">无线通信协议对比</h2>
<h3 id="41-主流-iot-无线协议">4.1 主流 IoT 无线协议</h3>
<table>
<thead>
<tr>
<th>协议</th>
<th>频率</th>
<th>范围</th>
<th>速率</th>
<th>功耗</th>
<th>典型应用</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>WiFi 4 (802.11n)</strong></td>
<td>2.4/5GHz</td>
<td>50m</td>
<td>150Mbps</td>
<td>高</td>
<td>视频、大数据</td>
</tr>
<tr>
<td><strong>WiFi 6 (802.11ax)</strong></td>
<td>2.4/5/6GHz</td>
<td>50m</td>
<td>600Mbps</td>
<td>中高</td>
<td>高密度 IoT</td>
</tr>
<tr>
<td><strong>Bluetooth 5</strong></td>
<td>2.4GHz</td>
<td>10-100m</td>
<td>2Mbps</td>
<td>低</td>
<td>可穿戴、信标</td>
</tr>
<tr>
<td><strong>BLE Mesh</strong></td>
<td>2.4GHz</td>
<td>多跳</td>
<td>1Mbps</td>
<td>低</td>
<td>智能家居</td>
</tr>
<tr>
<td><strong>Zigbee</strong></td>
<td>2.4GHz</td>
<td>10-100m</td>
<td>250kbps</td>
<td>低</td>
<td>工业、家居</td>
</tr>
<tr>
<td><strong>Thread</strong></td>
<td>2.4GHz</td>
<td>多跳</td>
<td>250kbps</td>
<td>低</td>
<td>Matter 生态</td>
</tr>
<tr>
<td><strong>LoRaWAN</strong></td>
<td>Sub-1GHz</td>
<td>2-15km</td>
<td>0.3-50kbps</td>
<td>极低</td>
<td>广域 IoT</td>
</tr>
<tr>
<td><strong>NB-IoT</strong></td>
<td>授权频段</td>
<td>1-10km</td>
<td>20-250kbps</td>
<td>低</td>
<td>蜂窝 IoT</td>
</tr>
<tr>
<td><strong>LTE-M</strong></td>
<td>授权频段</td>
<td>1-10km</td>
<td>1Mbps</td>
<td>中</td>
<td>移动 IoT</td>
</tr>
</tbody>
</table>
<h3 id="42-mqtt-协议详解">4.2 MQTT 协议详解</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// MQTT 客户端实现（使用 Paho MQTT）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#include</span> <span class="cpf">&lt;MQTTClient.h&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="c1">// MQTT 配置
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">broker_url</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">uint16_t</span> <span class="n">port</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">client_id</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">username</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">password</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">publish_topic</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">subscribe_topic</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">uint16_t</span> <span class="n">keepalive</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">bool</span> <span class="n">clean_session</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="kt">mqtt_config_t</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 全局 MQTT 客户端
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">static</span> <span class="n">MQTTClient</span> <span class="n">client</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">mqtt_config_t</span> <span class="n">config</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">broker_url</span> <span class="o">=</span> <span class="s">&#34;mqtt.example.com&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">port</span> <span class="o">=</span> <span class="mi">1883</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">client_id</span> <span class="o">=</span> <span class="s">&#34;iot_device_001&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">username</span> <span class="o">=</span> <span class="s">&#34;device_user&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">password</span> <span class="o">=</span> <span class="s">&#34;device_pass&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">publish_topic</span> <span class="o">=</span> <span class="s">&#34;devices/001/data&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">subscribe_topic</span> <span class="o">=</span> <span class="s">&#34;devices/001/command&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">keepalive</span> <span class="o">=</span> <span class="mi">60</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">clean_session</span> <span class="o">=</span> <span class="nb">false</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 连接回调
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="nf">on_connected</span><span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="n">context</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nf">ESP_LOGI</span><span class="p">(</span><span class="s">&#34;MQTT&#34;</span><span class="p">,</span> <span class="s">&#34;Connected to broker&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 订阅命令主题
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">MQTTClient_subscribe</span><span class="p">(</span><span class="n">client</span><span class="p">,</span> <span class="n">config</span><span class="p">.</span><span class="n">subscribe_topic</span><span class="p">,</span> <span class="mi">1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 发布上线消息
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">MQTTClient_publish</span><span class="p">(</span><span class="n">client</span><span class="p">,</span> <span class="s">&#34;devices/001/status&#34;</span><span class="p">,</span> 
</span></span><span class="line"><span class="cl">                       <span class="nf">strlen</span><span class="p">(</span><span class="s">&#34;online&#34;</span><span class="p">),</span> <span class="s">&#34;online&#34;</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 消息到达回调
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="nf">on_message_arrived</span><span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="n">context</span><span class="p">,</span> <span class="kt">char</span> <span class="o">*</span><span class="n">topicName</span><span class="p">,</span> <span class="kt">int</span> <span class="n">topicLen</span><span class="p">,</span> 
</span></span><span class="line"><span class="cl">                        <span class="n">MQTTClient_message</span> <span class="o">*</span><span class="n">message</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nf">ESP_LOGI</span><span class="p">(</span><span class="s">&#34;MQTT&#34;</span><span class="p">,</span> <span class="s">&#34;Message arrived on topic %s&#34;</span><span class="p">,</span> <span class="n">topicName</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 处理命令
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">handle_command</span><span class="p">(</span><span class="n">message</span><span class="o">-&gt;</span><span class="n">payload</span><span class="p">,</span> <span class="n">message</span><span class="o">-&gt;</span><span class="n">payloadlen</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="nf">MQTTClient_freeMessage</span><span class="p">(</span><span class="o">&amp;</span><span class="n">message</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nf">MQTTClient_free</span><span class="p">(</span><span class="n">topicName</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 连接丢失回调
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="nf">on_connection_lost</span><span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="n">context</span><span class="p">,</span> <span class="kt">char</span> <span class="o">*</span><span class="n">cause</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nf">ESP_LOGW</span><span class="p">(</span><span class="s">&#34;MQTT&#34;</span><span class="p">,</span> <span class="s">&#34;Connection lost: %s&#34;</span><span class="p">,</span> <span class="n">cause</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 自动重连
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">MQTTClient_reconnect</span><span class="p">(</span><span class="n">client</span><span class="p">,</span> <span class="mi">5000</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// MQTT 初始化
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">int</span> <span class="nf">mqtt_init</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nf">MQTTClient_create</span><span class="p">(</span><span class="o">&amp;</span><span class="n">client</span><span class="p">,</span> <span class="n">config</span><span class="p">.</span><span class="n">broker_url</span><span class="p">,</span> <span class="n">config</span><span class="p">.</span><span class="n">client_id</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                      <span class="n">MQTTCLIENT_PERSISTENCE_NONE</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="n">MQTTClient_callbacks</span> <span class="n">callbacks</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="p">.</span><span class="n">on_connect</span> <span class="o">=</span> <span class="n">on_connected</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="p">.</span><span class="n">on_message</span> <span class="o">=</span> <span class="n">on_message_arrived</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="p">.</span><span class="n">on_connection_lost</span> <span class="o">=</span> <span class="n">on_connection_lost</span>
</span></span><span class="line"><span class="cl">    <span class="p">};</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="nf">MQTTClient_setCallbacks</span><span class="p">(</span><span class="n">client</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">callbacks</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="n">MQTTClient_connectOptions</span> <span class="n">conn_opts</span> <span class="o">=</span> <span class="n">MQTTClient_connectOptions_initializer</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">conn_opts</span><span class="p">.</span><span class="n">keepAliveInterval</span> <span class="o">=</span> <span class="n">config</span><span class="p">.</span><span class="n">keepalive</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">conn_opts</span><span class="p">.</span><span class="n">cleansession</span> <span class="o">=</span> <span class="n">config</span><span class="p">.</span><span class="n">clean_session</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">conn_opts</span><span class="p">.</span><span class="n">username</span> <span class="o">=</span> <span class="n">config</span><span class="p">.</span><span class="n">username</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">conn_opts</span><span class="p">.</span><span class="n">password</span> <span class="o">=</span> <span class="n">config</span><span class="p">.</span><span class="n">password</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">conn_opts</span><span class="p">.</span><span class="n">automaticReconnect</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="n">rc</span> <span class="o">=</span> <span class="nf">MQTTClient_connect</span><span class="p">(</span><span class="n">client</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">conn_opts</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">rc</span> <span class="o">!=</span> <span class="n">MQTTCLIENT_SUCCESS</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nf">ESP_LOGE</span><span class="p">(</span><span class="s">&#34;MQTT&#34;</span><span class="p">,</span> <span class="s">&#34;Failed to connect: %d&#34;</span><span class="p">,</span> <span class="n">rc</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 发布数据
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">int</span> <span class="nf">mqtt_publish_data</span><span class="p">(</span><span class="k">const</span> <span class="kt">uint8_t</span> <span class="o">*</span><span class="n">data</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">len</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">MQTTClient_message</span> <span class="n">pubmsg</span> <span class="o">=</span> <span class="n">MQTTClient_message_initializer</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">pubmsg</span><span class="p">.</span><span class="n">payload</span> <span class="o">=</span> <span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="p">)</span><span class="n">data</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">pubmsg</span><span class="p">.</span><span class="n">payloadlen</span> <span class="o">=</span> <span class="n">len</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">pubmsg</span><span class="p">.</span><span class="n">qos</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>  <span class="c1">// QoS 1：至少送达一次
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">pubmsg</span><span class="p">.</span><span class="n">retained</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nf">MQTTClient_publish</span><span class="p">(</span><span class="n">client</span><span class="p">,</span> <span class="n">config</span><span class="p">.</span><span class="n">publish_topic</span><span class="p">,</span> 
</span></span><span class="line"><span class="cl">                              <span class="n">len</span><span class="p">,</span> <span class="n">data</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="43-coap-协议轻量级替代">4.3 CoAP 协议（轻量级替代）</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// CoAP 客户端实现（使用 libcoap）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#include</span> <span class="cpf">&lt;coap3/coap.h&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="c1">// CoAP 资源定义
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">static</span> <span class="kt">coap_resource_t</span> <span class="o">*</span><span class="n">resources</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nb">NULL</span>  <span class="c1">// 资源列表结束标记
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// CoAP 请求处理
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">static</span> <span class="kt">void</span>
</span></span><span class="line"><span class="cl"><span class="nf">helloworld_get</span><span class="p">(</span><span class="kt">coap_context_t</span> <span class="o">*</span><span class="n">ctx</span><span class="p">,</span> <span class="k">struct</span> <span class="kt">coap_resource_t</span> <span class="o">*</span><span class="n">resource</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">               <span class="kt">coap_session_t</span> <span class="o">*</span><span class="n">session</span><span class="p">,</span> <span class="kt">coap_pdu_t</span> <span class="o">*</span><span class="n">pdu</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">               <span class="kt">coap_binary_t</span> <span class="o">*</span><span class="n">token</span><span class="p">,</span> <span class="kt">coap_string_t</span> <span class="o">*</span><span class="n">query</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">               <span class="kt">coap_pdu_t</span> <span class="o">*</span><span class="n">response</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">payload</span> <span class="o">=</span> <span class="s">&#34;Hello, World!&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nf">coap_add_data</span><span class="p">(</span><span class="n">response</span><span class="p">,</span> <span class="nf">strlen</span><span class="p">(</span><span class="n">payload</span><span class="p">),</span> <span class="p">(</span><span class="k">const</span> <span class="kt">uint8_t</span> <span class="o">*</span><span class="p">)</span><span class="n">payload</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// CoAP 服务器初始化
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">coap_context_t</span> <span class="o">*</span><span class="nf">coap_server_init</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">coap_context_t</span> <span class="o">*</span><span class="n">ctx</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">coap_address_t</span> <span class="n">listen_addr</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 绑定到 5683 端口（CoAP 标准端口）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">coap_address_init</span><span class="p">(</span><span class="o">&amp;</span><span class="n">listen_addr</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">listen_addr</span><span class="p">.</span><span class="n">addr</span><span class="p">.</span><span class="n">sin</span><span class="p">.</span><span class="n">sin_family</span> <span class="o">=</span> <span class="n">AF_INET</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">listen_addr</span><span class="p">.</span><span class="n">addr</span><span class="p">.</span><span class="n">sin</span><span class="p">.</span><span class="n">sin_addr</span><span class="p">.</span><span class="n">s_addr</span> <span class="o">=</span> <span class="n">INADDR_ANY</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">listen_addr</span><span class="p">.</span><span class="n">addr</span><span class="p">.</span><span class="n">sin</span><span class="p">.</span><span class="n">sin_port</span> <span class="o">=</span> <span class="nf">htons</span><span class="p">(</span><span class="mi">5683</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="n">ctx</span> <span class="o">=</span> <span class="nf">coap_new_context</span><span class="p">(</span><span class="o">&amp;</span><span class="n">listen_addr</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">ctx</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="nb">NULL</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 注册资源
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="kt">coap_resource_t</span> <span class="o">*</span><span class="n">r</span> <span class="o">=</span> <span class="nf">coap_resource_init</span><span class="p">(</span><span class="nf">coap_make_str_const</span><span class="p">(</span><span class="s">&#34;hello&#34;</span><span class="p">),</span> <span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nf">coap_register_handler</span><span class="p">(</span><span class="n">r</span><span class="p">,</span> <span class="n">COAP_REQUEST_GET</span><span class="p">,</span> <span class="n">helloworld_get</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nf">coap_add_resource</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="n">r</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">ctx</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h2 id="总结">总结</h2>
<p>IoT 固件开发的核心要点：</p>
<ol>
<li><strong>分层架构</strong>：清晰的模块划分，便于维护和升级</li>
<li><strong>OTA 升级</strong>：A/B 分区、安全验证、回滚机制</li>
<li><strong>低功耗设计</strong>：DVFS、睡眠模式、无线优化</li>
<li><strong>无线通信</strong>：根据场景选择合适的协议（WiFi/BLE/LoRa/NB-IoT）</li>
<li><strong>安全性</strong>：安全启动、加密通信、安全存储</li>
</ol>
<p>掌握这些技能，你可以开发出可靠、安全、低功耗的 IoT 设备！</p>
<hr>
<p><em>本文基于 ESP32、STM32、nRF52 等平台实际项目经验整理，结合 2026 年最新 IoT 技术趋势编写。</em></p>
<h2 id="参考资料">参考资料</h2>
<ol>
<li>ESP-IDF Programming Guide - <a href="https://docs.espressif.com/">https://docs.espressif.com/</a></li>
<li>STM32CubeMX User Manual</li>
<li>Nordic nRF5 SDK Documentation</li>
<li>MQTT Version 5.0 Specification - OASIS Standard</li>
<li>The IoT Hacker&rsquo;s Handbook (2025)</li>
<li>Building Secure IoT Devices (2026)</li>
</ol>
<h2 id="参考资料-1">参考资料</h2>
<ol>
<li><a href="https://www.meegle.com/en_us/topics/firmware-development/firmware-development-for-iot">Firmware Development For IoT - Meegle</a></li>
<li><a href="https://webbylab.com/blog/firmware-development-iot/">IoT Firmware Development: Process, Challenges &amp; Best Practices</a></li>
<li><a href="https://rapidlab.io/firmware-development/">Embedded firmware development for IoT devices - RapidLab</a></li>
</ol>
<hr>
<p><em>本文基于网络公开资料整理，结合嵌入式开发实践经验编写。</em></p>
]]></content:encoded></item><item><title>嵌入式 Linux 开发指南 2026</title><link>https://tech-snippets.xyz/posts/embedded-linux-development/</link><pubDate>Fri, 10 Apr 2026 03:00:00 +0800</pubDate><guid>https://tech-snippets.xyz/posts/embedded-linux-development/</guid><description>嵌入式 Linux 开发完整指南，涵盖内核配置、驱动开发、根文件系统和调试</description><content:encoded><![CDATA[<h2 id="引言">引言</h2>
<p>本文基于 2026 年最新行业资料整理，涵盖 embedded Linux development 的核心概念、开发流程和实战技巧。</p>
<h2 id="嵌入式-linux-架构">嵌入式 Linux 架构</h2>
<pre tabindex="0"><code>┌─────────────────┐
│   应用层        │
├─────────────────┤
│   库层          │  glibc/μClibc
├─────────────────┤
│   系统调用      │
├─────────────────┤
│   Linux 内核     │
├─────────────────┤
│   BSP/驱动      │
└─────────────────┘
</code></pre><h2 id="开发环境搭建">开发环境搭建</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 安装交叉编译工具链</span>
</span></span><span class="line"><span class="cl">sudo apt install gcc-arm-linux-gnueabihf
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 编译内核</span>
</span></span><span class="line"><span class="cl">make <span class="nv">ARCH</span><span class="o">=</span>arm <span class="nv">CROSS_COMPILE</span><span class="o">=</span>arm-linux-gnueabihf- zImage
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 编译设备树</span>
</span></span><span class="line"><span class="cl">make <span class="nv">ARCH</span><span class="o">=</span>arm <span class="nv">CROSS_COMPILE</span><span class="o">=</span>arm-linux-gnueabihf- dtbs
</span></span></code></pre></div><h2 id="驱动开发">驱动开发</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// 字符设备驱动框架
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">static</span> <span class="kt">int</span> <span class="nf">dev_open</span><span class="p">(</span><span class="k">struct</span> <span class="n">inode</span> <span class="o">*</span><span class="n">inode</span><span class="p">,</span> <span class="k">struct</span> <span class="n">file</span> <span class="o">*</span><span class="n">file</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nf">printk</span><span class="p">(</span><span class="n">KERN_INFO</span> <span class="s">&#34;设备已打开</span>
</span></span><span class="line"><span class="cl"><span class="s">&#34;);</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">ssize_t</span> <span class="nf">dev_read</span><span class="p">(</span><span class="k">struct</span> <span class="n">file</span> <span class="o">*</span><span class="n">file</span><span class="p">,</span> <span class="kt">char</span> <span class="n">__user</span> <span class="o">*</span><span class="n">buf</span><span class="p">,</span> 
</span></span><span class="line"><span class="cl">                        <span class="kt">size_t</span> <span class="n">count</span><span class="p">,</span> <span class="kt">loff_t</span> <span class="o">*</span><span class="n">ppos</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nf">copy_to_user</span><span class="p">(</span><span class="n">buf</span><span class="p">,</span> <span class="n">kernel_data</span><span class="p">,</span> <span class="n">count</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">count</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="k">struct</span> <span class="n">file_operations</span> <span class="n">fops</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">owner</span> <span class="o">=</span> <span class="n">THIS_MODULE</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">open</span> <span class="o">=</span> <span class="n">dev_open</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">read</span> <span class="o">=</span> <span class="n">dev_read</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span></code></pre></div><h2 id="根文件系统">根文件系统</h2>
<p>使用 Buildroot 或 Yocto 构建：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># Buildroot</span>
</span></span><span class="line"><span class="cl">git clone https://github.com/buildroot/buildroot
</span></span><span class="line"><span class="cl">make menuconfig  <span class="c1"># 配置</span>
</span></span><span class="line"><span class="cl">make             <span class="c1"># 编译</span>
</span></span></code></pre></div><h2 id="参考资料">参考资料</h2>
<ol>
<li><a href="https://www.coursera.org/specializations/advanced-embedded-linux-development">Advanced Embedded Linux Development | Coursera</a></li>
<li><a href="https://bootlin.com/training/embedded-linux/">Embedded Linux training – Bootlin</a></li>
<li><a href="https://training.linuxfoundation.org/training/embedded-linux-development/">Embedded Linux Development (LFD450)</a></li>
</ol>
<hr>
<p><em>本文基于网络公开资料整理，结合嵌入式开发实践经验编写。</em></p>
]]></content:encoded></item><item><title>嵌入式系统内存管理完全指南：从静态分配到动态池</title><link>https://tech-snippets.xyz/posts/embedded-memory-management-complete-guide/</link><pubDate>Wed, 08 Apr 2026 15:30:00 +0800</pubDate><guid>https://tech-snippets.xyz/posts/embedded-memory-management-complete-guide/</guid><description>深入解析嵌入式系统内存管理的 5 种核心方法</description><content:encoded><![CDATA[<h1 id="嵌入式系统内存管理完全指南从静态分配到动态池">嵌入式系统内存管理完全指南：从静态分配到动态池</h1>
<h2 id="引言">引言</h2>
<p>内存管理是嵌入式系统开发中最核心也最容易出错的环节之一。</p>
<h2 id="为什么嵌入式内存管理如此重要">为什么嵌入式内存管理如此重要？</h2>
<h3 id="11-资源约束的残酷现实">1.1 资源约束的残酷现实</h3>
<figure>
<svg viewBox="0 0 600 200" xmlns="http://www.w3.org/2000/svg" style="max-width:100%;height:auto;background:#f8f9fa;border-radius:8px;padding:20px;">
  <defs>
    <style>
      .bar { fill: #1976d2; }
      .text { font-family: Arial; font-size: 12px; fill: #333; }
      .label { font-family: Arial; font-size: 10px; fill: #666; }
    </style>
  </defs>
  <text x="300" y="25" text-anchor="middle" font-family="Arial" font-size="14" font-weight="bold" fill="#333">典型嵌入式设备 RAM 容量对比</text>
  <rect x="50" y="50" width="40" height="120" class="bar"/>
  <text x="70" y="180" text-anchor="middle" class="label">8 位 MCU</text>
  <text x="70" y="45" text-anchor="middle" class="text">2KB</text>
  <rect x="150" y="50" width="40" height="120" class="bar"/>
  <text x="170" y="180" text-anchor="middle" class="label">32 位 MCU</text>
  <text x="170" y="45" text-anchor="middle" class="text">64KB</text>
  <rect x="250" y="50" width="40" height="120" class="bar"/>
  <text x="270" y="180" text-anchor="middle" class="label">ESP32</text>
  <text x="270" y="45" text-anchor="middle" class="text">520KB</text>
  <rect x="350" y="50" width="40" height="120" class="bar"/>
  <text x="370" y="180" text-anchor="middle" class="label">STM32H7</text>
  <text x="370" y="45" text-anchor="middle" class="text">1MB</text>
  <rect x="450" y="50" width="40" height="120" class="bar"/>
  <text x="470" y="180" text-anchor="middle" class="label">嵌入式 Linux</text>
  <text x="470" y="45" text-anchor="middle" class="text">512MB</text>
  <line x1="50" y1="170" x2="530" y2="170" stroke="#666" stroke-width="1"/>
</svg>
<figcaption style="text-align:center;color:#888;font-size:12px;margin-top:8px;">典型嵌入式设备 RAM 容量对比（对数尺度）</figcaption>
</figure>
<p>上表展示了常见嵌入式平台的 RAM 容量范围。</p>
<p><strong>资源约束带来的挑战</strong>：</p>
<table>
<thead>
<tr>
<th>约束类型</th>
<th>桌面/服务器</th>
<th>嵌入式系统</th>
<th>影响</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>RAM 容量</strong></td>
<td>8GB-64GB</td>
<td>2KB-1MB</td>
<td>必须精打细算</td>
</tr>
<tr>
<td><strong>分配失败处理</strong></td>
<td>抛出异常/终止</td>
<td>系统崩溃</td>
<td>必须预防</td>
</tr>
<tr>
<td><strong>碎片化</strong></td>
<td>GC 回收</td>
<td>永久碎片</td>
<td>长期运行风险</td>
</tr>
<tr>
<td><strong>实时性要求</strong></td>
<td>毫秒级可接受</td>
<td>微秒级确定</td>
<td>不能容忍不确定延迟</td>
</tr>
<tr>
<td><strong>运行时间</strong></td>
<td>小时/天</td>
<td>年/十年</td>
<td>内存泄漏累积效应</td>
</tr>
</tbody>
</table>
<h3 id="12-内存错误的代价">1.2 内存错误的代价</h3>
<p>根据嵌入式系统可靠性研究，<strong>约 40% 的现场故障与内存管理相关</strong>。</p>
<h2 id="方法一静态分配---最安全的选择">方法一：静态分配 - 最安全的选择</h2>
<h3 id="21-什么是静态分配">2.1 什么是静态分配？</h3>
<p>静态分配是指在<strong>编译期</strong>确定内存大小和位置，运行时不会改变。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// 全局变量 - 静态分配在数据段
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">int</span> <span class="n">global_counter</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">uint8_t</span> <span class="n">buffer</span><span class="p">[</span><span class="mi">256</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 常量 - 静态分配在代码段（Flash）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">const</span> <span class="kt">char</span><span class="o">*</span> <span class="n">device_name</span> <span class="o">=</span> <span class="s">&#34;Sensor Node v1.0&#34;</span><span class="p">;</span>
</span></span></code></pre></div><h3 id="22-静态分配的内存布局">2.2 静态分配的内存布局</h3>
<figure>
<svg viewBox="0 0 400 500" xmlns="http://www.w3.org/2000/svg" style="max-width:100%;height:auto;background:#f8f9fa;border-radius:8px;padding:20px;">
  <defs>
    <style>
      .segment { stroke: #333; stroke-width: 2; }
      .code { fill: #e3f2fd; }
      .data { fill: #c8e6c9; }
      .bss { fill: #fff3e0; }
      .heap { fill: #fce4ec; }
      .stack { fill: #f3e5f5; }
      .text { font-family: Arial; font-size: 11px; fill: #333; }
      .label { font-family: Arial; font-size: 10px; fill: #666; }
    </style>
  </defs>
  <text x="200" y="25" text-anchor="middle" font-family="Arial" font-size="14" font-weight="bold" fill="#333">嵌入式系统内存布局</text>
  <rect x="100" y="50" width="200" height="80" class="segment code"/>
  <text x="200" y="85" text-anchor="middle" class="text" font-weight="bold">代码段 (.text)</text>
  <text x="200" y="105" text-anchor="middle" class="label">程序代码、常量</text>
  <rect x="100" y="140" width="200" height="60" class="segment data"/>
  <text x="200" y="170" text-anchor="middle" class="text" font-weight="bold">数据段 (.data)</text>
  <rect x="100" y="210" width="200" height="60" class="segment bss"/>
  <text x="200" y="240" text-anchor="middle" class="text" font-weight="bold">BSS 段 (.bss)</text>
  <rect x="100" y="280" width="200" height="80" class="segment heap"/>
  <text x="200" y="315" text-anchor="middle" class="text" font-weight="bold">堆 (Heap)</text>
  <rect x="100" y="370" width="200" height="80" class="segment stack"/>
  <text x="200" y="405" text-anchor="middle" class="text" font-weight="bold">栈 (Stack)</text>
</svg>
<figcaption style="text-align:center;color:#888;font-size:12px;margin-top:8px;">典型嵌入式系统内存布局（ARM Cortex-M）</figcaption>
</figure>
<h3 id="23-静态分配的优缺点">2.3 静态分配的优缺点</h3>
<p><strong>优点</strong>：零运行时开销、无碎片化、可预测、无泄漏风险</p>
<p><strong>缺点</strong>：灵活性差、内存浪费、无法复用</p>
<h2 id="方法二栈分配---自动管理的局部内存">方法二：栈分配 - 自动管理的局部内存</h2>
<h3 id="31-栈的工作原理">3.1 栈的工作原理</h3>
<p>栈是一种<strong>后进先出 (LIFO)</strong> 的数据结构。</p>
<h3 id="32-栈分配的特点">3.2 栈分配的特点</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">sensor_processing</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">uint8_t</span> <span class="n">local_buffer</span><span class="p">[</span><span class="mi">64</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int32_t</span> <span class="n">sum</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">float</span> <span class="n">calibration</span> <span class="o">=</span> <span class="mf">1.0f</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="mi">64</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">local_buffer</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="nf">read_sensor</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">        <span class="n">sum</span> <span class="o">+=</span> <span class="n">local_buffer</span><span class="p">[</span><span class="n">i</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p><strong>优点</strong>：自动管理、无碎片化、速度快、无泄漏风险</p>
<p><strong>缺点</strong>：大小受限、生命周期短、溢出风险</p>
<h2 id="方法三堆分配---灵活但危险">方法三：堆分配 - 灵活但危险</h2>
<h3 id="41-mallocfree-的工作原理">4.1 malloc/free 的工作原理</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="kt">uint8_t</span><span class="o">*</span> <span class="n">buffer</span> <span class="o">=</span> <span class="p">(</span><span class="kt">uint8_t</span><span class="o">*</span><span class="p">)</span><span class="nf">malloc</span><span class="p">(</span><span class="mi">256</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="p">(</span><span class="n">buffer</span> <span class="o">==</span> <span class="nb">NULL</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">ERROR_NO_MEMORY</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="nf">memset</span><span class="p">(</span><span class="n">buffer</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">256</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nf">process_data</span><span class="p">(</span><span class="n">buffer</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nf">free</span><span class="p">(</span><span class="n">buffer</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="n">buffer</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>
</span></span></code></pre></div><h3 id="42-堆分配的致命问题">4.2 堆分配的致命问题</h3>
<ol>
<li><strong>内存碎片化</strong></li>
<li><strong>分配时间不确定</strong></li>
<li><strong>内存泄漏</strong></li>
<li><strong>悬空指针</strong></li>
</ol>
<h3 id="43-堆分配使用准则">4.3 堆分配使用准则</h3>
<ol>
<li>始终检查返回值</li>
<li>立即初始化</li>
<li>释放后置 NULL</li>
<li>配对使用（谁分配谁释放）</li>
<li>避免在中断中使用 malloc</li>
</ol>
<h2 id="方法四内存池---实时系统的首选">方法四：内存池 - 实时系统的首选</h2>
<h3 id="51-什么是内存池">5.1 什么是内存池？</h3>
<p>内存池是<strong>预先分配一块固定大小的内存，然后将其划分为多个等大的块</strong>。</p>
<figure>
<svg viewBox="0 0 500 250" xmlns="http://www.w3.org/2000/svg" style="max-width:100%;height:auto;background:#f8f9fa;border-radius:8px;padding:20px;">
  <defs>
    <style>
      .pool-block { fill: #e3f2fd; stroke: #1976d2; stroke-width: 2; }
      .pool-used { fill: #ffcdd2; stroke: #d32f2f; stroke-width: 2; }
      .text { font-family: Arial; font-size: 11px; fill: #333; }
    </style>
  </defs>
  <text x="250" y="25" text-anchor="middle" font-family="Arial" font-size="14" font-weight="bold" fill="#333">内存池工作原理</text>
  <rect x="50" y="50" width="400" height="60" class="pool-block"/>
  <text x="250" y="85" text-anchor="middle" class="text">内存池 (1024 字节，16 块 × 64 字节)</text>
  <text x="50" y="130" class="text" font-weight="bold">空闲链表:</text>
  <circle cx="100" cy="145" r="15" class="pool-block"/>
  <text x="100" y="150" text-anchor="middle" class="text">0</text>
  <circle cx="150" cy="145" r="15" class="pool-block"/>
  <text x="150" y="150" text-anchor="middle" class="text">1</text>
  <circle cx="200" cy="145" r="15" class="pool-used"/>
  <text x="200" y="150" text-anchor="middle" class="text">2</text>
  <circle cx="250" cy="145" r="15" class="pool-block"/>
  <text x="250" y="150" text-anchor="middle" class="text">3</text>
  <circle cx="300" cy="145" r="15" class="pool-used"/>
  <text x="300" y="150" text-anchor="middle" class="text">4</text>
  <circle cx="350" cy="145" r="15" class="pool-block"/>
  <text x="350" y="150" text-anchor="middle" class="text">5</text>
  <text x="250" y="180" text-anchor="middle" class="text" fill="#388e3c">✓ 固定时间 O(1)</text>
  <text x="250" y="195" text-anchor="middle" class="text" fill="#388e3c">✓ 无碎片化</text>
  <text x="250" y="210" text-anchor="middle" class="text" fill="#388e3c">✓ 可预测</text>
</svg>
<figcaption style="text-align:center;color:#888;font-size:12px;margin-top:8px;">内存池通过固定块大小消除碎片化</figcaption>
</figure>
<h3 id="52-内存池实现">5.2 内存池实现</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="cp">#define MEMORY_POOL_SIZE    16
</span></span></span><span class="line"><span class="cl"><span class="cp">#define MEMORY_BLOCK_SIZE   64
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="k">typedef</span> <span class="k">struct</span> <span class="n">memory_pool</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">uint8_t</span> <span class="n">pool</span><span class="p">[</span><span class="n">MEMORY_POOL_SIZE</span> <span class="o">*</span> <span class="n">MEMORY_BLOCK_SIZE</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">    <span class="kt">bool</span> <span class="n">is_used</span><span class="p">[</span><span class="n">MEMORY_POOL_SIZE</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">    <span class="kt">uint32_t</span> <span class="n">alloc_count</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">uint32_t</span> <span class="n">free_count</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="kt">memory_pool_t</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">memory_pool_t</span> <span class="n">g_pool</span> <span class="o">=</span> <span class="p">{</span><span class="mi">0</span><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">pool_init</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nf">memset</span><span class="p">(</span><span class="o">&amp;</span><span class="n">g_pool</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">g_pool</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">MEMORY_POOL_SIZE</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">g_pool</span><span class="p">.</span><span class="n">is_used</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">void</span><span class="o">*</span> <span class="nf">pool_alloc</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">MEMORY_POOL_SIZE</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">g_pool</span><span class="p">.</span><span class="n">is_used</span><span class="p">[</span><span class="n">i</span><span class="p">])</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">g_pool</span><span class="p">.</span><span class="n">is_used</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="nb">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="n">g_pool</span><span class="p">.</span><span class="n">alloc_count</span><span class="o">++</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="o">&amp;</span><span class="n">g_pool</span><span class="p">.</span><span class="n">pool</span><span class="p">[</span><span class="n">i</span> <span class="o">*</span> <span class="n">MEMORY_BLOCK_SIZE</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nb">NULL</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">pool_free</span><span class="p">(</span><span class="kt">void</span><span class="o">*</span> <span class="n">ptr</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">ptr</span> <span class="o">==</span> <span class="nb">NULL</span><span class="p">)</span> <span class="k">return</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">uint32_t</span> <span class="n">offset</span> <span class="o">=</span> <span class="p">(</span><span class="kt">uint8_t</span><span class="o">*</span><span class="p">)</span><span class="n">ptr</span> <span class="o">-</span> <span class="n">g_pool</span><span class="p">.</span><span class="n">pool</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="n">index</span> <span class="o">=</span> <span class="n">offset</span> <span class="o">/</span> <span class="n">MEMORY_BLOCK_SIZE</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">index</span> <span class="o">&gt;=</span> <span class="mi">0</span> <span class="o">&amp;&amp;</span> <span class="n">index</span> <span class="o">&lt;</span> <span class="n">MEMORY_POOL_SIZE</span> <span class="o">&amp;&amp;</span> <span class="n">g_pool</span><span class="p">.</span><span class="n">is_used</span><span class="p">[</span><span class="n">index</span><span class="p">])</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">g_pool</span><span class="p">.</span><span class="n">is_used</span><span class="p">[</span><span class="n">index</span><span class="p">]</span> <span class="o">=</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">g_pool</span><span class="p">.</span><span class="n">free_count</span><span class="o">++</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="nf">memset</span><span class="p">(</span><span class="n">ptr</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">MEMORY_BLOCK_SIZE</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="53-内存池性能对比">5.3 内存池性能对比</h3>
<table>
<thead>
<tr>
<th>指标</th>
<th>malloc/free</th>
<th>内存池</th>
<th>提升</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>平均分配时间</strong></td>
<td>15.3μs</td>
<td>0.8μs</td>
<td><strong>19 倍</strong></td>
</tr>
<tr>
<td><strong>最坏情况时间</strong></td>
<td>127.5μs</td>
<td>0.9μs</td>
<td><strong>142 倍</strong></td>
</tr>
<tr>
<td><strong>碎片化率</strong></td>
<td>35%</td>
<td>0%</td>
<td><strong>完全消除</strong></td>
</tr>
</tbody>
</table>
<h2 id="方法五自定义分配器">方法五：自定义分配器</h2>
<h3 id="61-环形缓冲区分配器">6.1 环形缓冲区分配器</h3>
<p>适用于数据流处理（如音频、传感器）。</p>
<h3 id="62-区域分配器arena-allocator">6.2 区域分配器（Arena Allocator）</h3>
<p>适用于阶段性任务（如网络请求处理）。</p>
<h2 id="总结与建议">总结与建议</h2>
<h3 id="81-五种方法对比">8.1 五种方法对比</h3>
<table>
<thead>
<tr>
<th>方法</th>
<th>优点</th>
<th>缺点</th>
<th>推荐场景</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>静态分配</strong></td>
<td>零开销、无碎片、可预测</td>
<td>灵活性差、可能浪费</td>
<td>全局配置、固定缓冲区</td>
</tr>
<tr>
<td><strong>栈分配</strong></td>
<td>自动管理、速度快</td>
<td>大小受限、生命周期短</td>
<td>局部变量、小数组</td>
</tr>
<tr>
<td><strong>堆分配</strong></td>
<td>灵活、大小可变</td>
<td>碎片化、不确定、易泄漏</td>
<td>避免使用</td>
</tr>
<tr>
<td><strong>内存池</strong></td>
<td>O(1) 时间、无碎片</td>
<td>固定大小、需预先规划</td>
<td>实时系统、频繁分配</td>
</tr>
<tr>
<td><strong>自定义分配器</strong></td>
<td>针对场景优化</td>
<td>实现复杂、通用性差</td>
<td>特殊需求</td>
</tr>
</tbody>
</table>
<h3 id="82-设计原则">8.2 设计原则</h3>
<ol>
<li><strong>优先静态，其次池，最后堆</strong></li>
<li><strong>编译期确定优于运行期</strong></li>
<li><strong>可预测性优于灵活性</strong></li>
<li><strong>监控与告警</strong></li>
</ol>
<hr>
<p><em>本文基于实际项目经验编写，代码已在 STM32、ESP32 等平台验证。</em></p>
]]></content:encoded></item><item><title>嵌入式开发：Embedded Systems Tutorial 2026</title><link>https://tech-snippets.xyz/posts/embedded-systems-tutorial-2026/</link><pubDate>Wed, 08 Apr 2026 03:00:00 +0800</pubDate><guid>https://tech-snippets.xyz/posts/embedded-systems-tutorial-2026/</guid><description>嵌入式开发实战教程：embedded systems tutorial 2026</description><content:encoded><![CDATA[<h2 id="引言">引言</h2>
<p>本文基于 2026 年最新行业资料整理，涵盖 embedded systems tutorial 2026 的核心概念、开发流程和实战技巧。</p>
<h2 id="embedded-systems-tutorial-2026-概述">embedded systems tutorial 2026 概述</h2>
<p>嵌入式系统开发需要掌握硬件、软件和系统集成的综合技能。</p>
<h2 id="开发流程">开发流程</h2>
<ol>
<li><strong>需求分析</strong> → 确定功能、性能指标</li>
<li><strong>硬件选型</strong> → MCU、传感器、通信模块</li>
<li><strong>原理图设计</strong> → 电路设计、仿真验证</li>
<li><strong>软件架构</strong> → 分层设计、模块化</li>
<li><strong>编码实现</strong> → C/C++、RTOS、驱动</li>
<li><strong>调试测试</strong> → 单元测试、系统集成</li>
<li><strong>生产部署</strong> → 批量生产、OTA 升级</li>
</ol>
<h2 id="常用工具">常用工具</h2>
<ul>
<li><strong>IDE</strong>：Keil、IAR、STM32CubeIDE、VS Code</li>
<li><strong>调试器</strong>：J-Link、ST-Link、DAP-Link</li>
<li><strong>示波器</strong>：Siglent、Rigol、Keysight</li>
<li><strong>逻辑分析仪</strong>：Saleae、Kingst</li>
</ul>
<h2 id="学习路线">学习路线</h2>
<ol>
<li>C 语言基础 → 指针、结构体、内存管理</li>
<li>单片机原理 → GPIO、中断、定时器</li>
<li>外设驱动 → UART、SPI、I2C、ADC</li>
<li>RTOS → 任务、信号量、消息队列</li>
<li>项目实战 → 综合应用</li>
</ol>
<h2 id="参考资料">参考资料</h2>
<ol>
<li><a href="https://sheridantech.io/2026/03/30/embedded-systems-development/">A Guide to Embedded Systems Development in 2026</a></li>
<li><a href="https://www.date-conference.com/embedded-tutorial">Embedded Tutorials | DATE 2026</a></li>
<li><a href="https://embedkari.com/embedded-systems-training-april-2026/">Embedded Systems Training -April 2026 - - Embedkari</a></li>
</ol>
<hr>
<p><em>本文基于网络公开资料整理，结合嵌入式开发实践经验编写。</em></p>
]]></content:encoded></item><item><title>RTOS 实时操作系统完全指南 2026</title><link>https://tech-snippets.xyz/posts/rtos-real-time-operating-system-guide/</link><pubDate>Mon, 06 Apr 2026 03:00:00 +0800</pubDate><guid>https://tech-snippets.xyz/posts/rtos-real-time-operating-system-guide/</guid><description>RTOS 实时操作系统完全指南，涵盖任务调度、中断处理、IPC 和性能优化</description><content:encoded><![CDATA[<h2 id="引言">引言</h2>
<p>实时操作系统（RTOS，Real-Time Operating System）是嵌入式系统的核心基石，它决定了系统能否在<strong>确定的时间</strong>内响应外部事件。从汽车安全气囊的毫秒级展开，到工业机械臂的精确控制，再到医疗设备的稳定运行，RTOS 无处不在。</p>
<p>本文将深入剖析 RTOS 的工作原理，从任务调度、中断管理、进程间通信到性能优化，提供一份完整的技术指南。无论你是嵌入式新手还是有经验的开发者，都能从中获得实用的知识和技能。</p>
<h2 id="什么是-rtos">什么是 RTOS？</h2>
<h3 id="11-实时性的定义">1.1 实时性的定义</h3>
<p>RTOS 的核心特征是<strong>确定性</strong>（Determinism）——系统行为在时间上是可预测的。这与通用操作系统（如 Windows、Linux）形成鲜明对比：</p>
<table>
<thead>
<tr>
<th>特性</th>
<th>RTOS</th>
<th>通用 OS</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>调度目标</strong></td>
<td>时间确定性</td>
<td>吞吐量最大化</td>
</tr>
<tr>
<td><strong>中断延迟</strong></td>
<td>&lt; 1μs</td>
<td>&gt; 100μs</td>
</tr>
<tr>
<td><strong>内核大小</strong></td>
<td>6-50KB</td>
<td>10MB+</td>
</tr>
<tr>
<td><strong>内存管理</strong></td>
<td>静态为主</td>
<td>动态分页</td>
</tr>
<tr>
<td><strong>应用场景</strong></td>
<td>工业、医疗、汽车</td>
<td>桌面、服务器</td>
</tr>
</tbody>
</table>
<h3 id="12-硬实时-vs-软实时">1.2 硬实时 vs 软实时</h3>
<p>RTOS 分为两类，根据对截止时间的严格程度：</p>
<p><strong>硬实时（Hard Real-Time）</strong>：</p>
<ul>
<li>必须在截止时间内完成，否则系统失效</li>
<li>典型应用：安全气囊、飞行控制、心脏起搏器</li>
<li>示例：汽车安全气囊必须在碰撞后 10-50ms 内展开</li>
</ul>
<p><strong>软实时（Soft Real-Time）</strong>：</p>
<ul>
<li>尽量在截止时间内完成，偶尔超时可接受</li>
<li>典型应用：视频播放、网络流媒体、语音通话</li>
<li>示例：视频帧偶尔延迟几毫秒，用户可能察觉不到</li>
</ul>
<figure>
<svg viewBox="0 0 700 200" xmlns="http://www.w3.org/2000/svg" style="max-width:100%;height:auto;background:#f8f9fa;border-radius:8px;padding:20px;">
  <defs>
    <style>
      .box { fill: #1a1a2e; stroke: #4a9eff; stroke-width: 2; rx: 6; }
      .text { fill: #e0e0e0; font-family: Arial, sans-serif; font-size: 14px; text-anchor: middle; }
      .label { fill: #4a9eff; font-family: Arial, sans-serif; font-size: 16px; font-weight: bold; }
      .axis { stroke: #666; stroke-width: 2; }
      .deadline { stroke: #e74c3c; stroke-width: 2; stroke-dasharray: 5,3; }
    </style>
  </defs>
  <!-- 硬实时图 -->
  <rect class="box" x="20" y="20" width="300" height="160"/>
  <text class="label" x="170" y="50">硬实时系统</text>
  <line class="axis" x1="50" y1="100" x2="290" y2="100"/>
  <text class="text" x="290" y="115" font-size="11">时间</text>
  <line class="deadline" x1="200" y1="70" x2="200" y2="130"/>
  <text class="text" x="200" y="65" fill="#e74c3c">截止时间</text>
  <path d="M60 100 L180 100" stroke="#388e3c" stroke-width="3"/>
  <text class="text" x="120" y="95" fill="#388e3c">任务完成</text>
<p><text class="text" x="170" y="140" fill="#388e3c">✓ 在截止时间前完成</text></p>
  <!-- 软实时图 -->
  <rect class="box" x="380" y="20" width="300" height="160"/>
  <text class="label" x="530" y="50">软实时系统</text>
  <line class="axis" x1="410" y1="100" x2="650" y2="100"/>
  <text class="text" x="650" y="115" font-size="11">时间</text>
  <line class="deadline" x1="560" y1="70" x2="560" y2="130"/>
  <text class="text" x="560" y="65" fill="#e74c3c">截止时间</text>
  <path d="M420 100 L540 100" stroke="#388e3c" stroke-width="3"/>
  <path d="M540 100 L580 100" stroke="#ff9800" stroke-width="3"/>
  <text class="text" x="480" y="95" fill="#388e3c">任务完成</text>
  <text class="text" x="580" y="95" fill="#ff9800">偶尔超时</text>
<p><text class="text" x="530" y="140" fill="#ff9800">⚠ 偶尔超时可接受</text>
</svg></p>
<figcaption style="text-align:center;color:#888;font-size:12px;margin-top:8px;">硬实时与软实时对比</figcaption>
</figure>
<h3 id="13-rtos-应用领域">1.3 RTOS 应用领域</h3>
<table>
<thead>
<tr>
<th>领域</th>
<th>典型应用</th>
<th>实时性要求</th>
<th>常用 RTOS</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>汽车电子</strong></td>
<td>发动机控制、ABS、安全气囊</td>
<td>硬实时（&lt; 1ms）</td>
<td>AUTOSAR OS、uC/OS</td>
</tr>
<tr>
<td><strong>工业控制</strong></td>
<td>PLC、机械臂、CNC</td>
<td>硬实时（&lt; 100μs）</td>
<td>VxWorks、RT-Thread</td>
</tr>
<tr>
<td><strong>医疗设备</strong></td>
<td>输液泵、监护仪、起搏器</td>
<td>硬实时（&lt; 10ms）</td>
<td>QNX、FreeRTOS</td>
</tr>
<tr>
<td><strong>航空航天</strong></td>
<td>飞行控制、导航系统</td>
<td>硬实时（&lt; 10μs）</td>
<td>VxWorks、Integrity</td>
</tr>
<tr>
<td><strong>消费电子</strong></td>
<td>智能手表、耳机、相机</td>
<td>软实时（&lt; 100ms）</td>
<td>FreeRTOS、Zephyr</td>
</tr>
<tr>
<td><strong>IoT 设备</strong></td>
<td>传感器、网关、智能插座</td>
<td>软实时（&lt; 1s）</td>
<td>FreeRTOS、RT-Thread</td>
</tr>
</tbody>
</table>
<h2 id="rtos-核心架构">RTOS 核心架构</h2>
<h3 id="21-内核组成">2.1 内核组成</h3>
<p>典型的 RTOS 内核包含以下模块：</p>
<pre tabindex="0"><code>┌─────────────────────────────────────────┐
│           应用层（任务/线程）            │
├─────────────────────────────────────────┤
│  任务管理  │  时间管理  │  中断管理      │
├─────────────────────────────────────────┤
│  信号量    │  互斥量    │  事件标志      │
├─────────────────────────────────────────┤
│  消息队列  │  邮箱      │  管道          │
├─────────────────────────────────────────┤
│  内存管理（静态/动态分配）               │
├─────────────────────────────────────────┤
│           硬件抽象层（HAL）              │
├─────────────────────────────────────────┤
│           硬件（CPU/定时器/外设）         │
└─────────────────────────────────────────┘
</code></pre><h3 id="22-任务控制块tcb">2.2 任务控制块（TCB）</h3>
<p>每个任务在 RTOS 中都有一个任务控制块（Task Control Block），记录任务的所有信息：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// FreeRTOS TCB 简化结构
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">typedef</span> <span class="k">struct</span> <span class="n">tskTaskControlBlock</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">StackType_t</span> <span class="o">*</span><span class="n">pxTopOfStack</span><span class="p">;</span>      <span class="c1">// 栈顶指针（用于上下文切换）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">StackType_t</span> <span class="o">*</span><span class="n">pxStack</span><span class="p">;</span>           <span class="c1">// 栈底指针（用于栈溢出检测）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="kt">char</span> <span class="n">pcTaskName</span><span class="p">[</span><span class="mi">16</span><span class="p">];</span>            <span class="c1">// 任务名称（调试用）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">UBaseType_t</span> <span class="n">uxPriority</span><span class="p">;</span>         <span class="c1">// 优先级（0 = 最低）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">TaskState_t</span> <span class="n">eTaskState</span><span class="p">;</span>         <span class="c1">// 任务状态（运行/就绪/阻塞/挂起）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">TickType_t</span> <span class="n">xBlockTime</span><span class="p">;</span>          <span class="c1">// 阻塞超时时间
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="kt">void</span> <span class="o">*</span><span class="n">pvMutexesHeld</span><span class="p">;</span>            <span class="c1">// 持有的互斥量列表
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">EventBits_t</span> <span class="n">uxEventBits</span><span class="p">;</span>        <span class="c1">// 事件组标志
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="c1">// ... 更多字段
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span> <span class="n">TCB_t</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 任务列表（调度器使用）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">TCB_t</span> <span class="o">*</span><span class="n">pxCurrentTCB</span><span class="p">;</span>                <span class="c1">// 当前运行任务的 TCB
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">TCB_t</span> <span class="n">pxReadyTasksLists</span><span class="p">[</span><span class="n">configMAX_PRIORITIES</span><span class="p">];</span>  <span class="c1">// 就绪任务列表
</span></span></span></code></pre></div><h3 id="23-任务状态转换">2.3 任务状态转换</h3>
<p>RTOS 任务有以下状态：</p>
<table>
<thead>
<tr>
<th>状态</th>
<th>描述</th>
<th>转换条件</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>运行态（Running）</strong></td>
<td>正在 CPU 上执行</td>
<td>调度器选中</td>
</tr>
<tr>
<td><strong>就绪态（Ready）</strong></td>
<td>可以运行，等待 CPU</td>
<td>高优先级任务抢占</td>
</tr>
<tr>
<td><strong>阻塞态（Blocked）</strong></td>
<td>等待事件（信号量、队列等）</td>
<td>事件到达或超时</td>
</tr>
<tr>
<td><strong>挂起态（Suspended）</strong></td>
<td>被显式挂起，不参与调度</td>
<td>调用 vTaskResume()</td>
</tr>
</tbody>
</table>
<figure>
<svg viewBox="0 0 600 400" xmlns="http://www.w3.org/2000/svg" style="max-width:100%;height:auto;background:#f8f9fa;border-radius:8px;padding:20px;">
  <defs>
    <style>
      .state { fill: #1a1a2e; stroke: #4a9eff; stroke-width: 2; rx: 8; }
      .text { fill: #e0e0e0; font-family: Arial, sans-serif; font-size: 14px; text-anchor: middle; }
      .arrow { stroke: #4a9eff; stroke-width: 2; fill: none; marker-end: url(#arrow-blue); }
      .arrow-dash { stroke: #e74c3c; stroke-width: 2; stroke-dasharray: 5,3; fill: none; marker-end: url(#arrow-red); }
    </style>
    <marker id="arrow-blue" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
      <polygon points="0 0, 10 3.5, 0 7" fill="#4a9eff"/>
    </marker>
    <marker id="arrow-red" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
      <polygon points="0 0, 10 3.5, 0 7" fill="#e74c3c"/>
    </marker>
  </defs>
  <!-- 运行态 -->
  <rect class="state" x="250" y="20" width="100" height="50"/>
  <text class="text" x="300" y="50">运行态</text>
  <!-- 就绪态 -->
  <rect class="state" x="20" y="150" width="100" height="50"/>
  <text class="text" x="70" y="180">就绪态</text>
  <!-- 阻塞态 -->
  <rect class="state" x="480" y="150" width="100" height="50"/>
  <text class="text" x="530" y="180">阻塞态</text>
  <!-- 挂起态 -->
  <rect class="state" x="250" y="280" width="100" height="50"/>
  <text class="text" x="300" y="310">挂起态</text>
  <!-- 转换箭头 -->
  <path class="arrow" d="M70 130 L280 40"/>
  <text class="text" x="140" y="90" font-size="11" fill="#888">调度器选择</text>
  <path class="arrow-dash" d="M320 70 L510 150"/>
  <text class="text" x="430" y="100" font-size="11" fill="#888">等待事件</text>
  <path class="arrow" d="M510 200 L320 280"/>
  <text class="text" x="430" y="240" font-size="11" fill="#888">超时/取消</text>
  <path class="arrow" d="M280 330 L70 200"/>
  <text class="text" x="140" y="270" font-size="11" fill="#888">恢复</text>
  <path class="arrow" d="M120 175 L230 175"/>
  <text class="text" x="175" y="165" font-size="11" fill="#888">时间片到</text>
  <path class="arrow" d="M480 175 L370 175"/>
  <text class="text" x="425" y="165" font-size="11" fill="#888">事件到达</text>
  <path class="arrow-dash" d="M300 280 L300 230 L300 70"/>
  <text class="text" x="320" y="200" font-size="11" fill="#888">vTaskSuspend()</text>
</svg>
<figcaption style="text-align:center;color:#888;font-size:12px;margin-top:8px;">RTOS 任务状态转换图</figcaption>
</figure>
<h2 id="任务调度机制详解">任务调度机制详解</h2>
<h3 id="31-调度算法">3.1 调度算法</h3>
<p>RTOS 主要使用以下调度算法：</p>
<h4 id="1抢占式优先级调度">（1）抢占式优先级调度</h4>
<p><strong>原理</strong>：始终运行就绪态中优先级最高的任务，高优先级任务可抢占低优先级任务。</p>
<p><strong>特点</strong>：</p>
<ul>
<li>实时性最好</li>
<li>可能导致低优先级任务&quot;饿死&quot;</li>
<li>适用于硬实时系统</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// 伪代码：抢占式调度器
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="nf">vTaskSchedule</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">TaskHandle_t</span> <span class="n">highestPriorityTask</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 遍历所有优先级（从高到低）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">prio</span> <span class="o">=</span> <span class="n">configMAX_PRIORITIES</span> <span class="o">-</span> <span class="mi">1</span><span class="p">;</span> <span class="n">prio</span> <span class="o">&gt;=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">prio</span><span class="o">--</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">pxReadyTasksLists</span><span class="p">[</span><span class="n">prio</span><span class="p">]</span> <span class="o">!=</span> <span class="nb">NULL</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">highestPriorityTask</span> <span class="o">=</span> <span class="n">pxReadyTasksLists</span><span class="p">[</span><span class="n">prio</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">            <span class="k">break</span><span class="p">;</span>  <span class="c1">// 找到最高优先级任务
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 如果有更高优先级任务，进行切换
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">if</span> <span class="p">(</span><span class="n">highestPriorityTask</span> <span class="o">!=</span> <span class="n">pxCurrentTCB</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nf">vTaskSwitchContext</span><span class="p">(</span><span class="n">highestPriorityTask</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// FreeRTOS 任务创建示例
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="nf">vTaskFunction</span><span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="n">pvParameters</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">while</span> <span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// 任务代码
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="nf">GPIO_Toggle</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">        <span class="nf">vTaskDelay</span><span class="p">(</span><span class="nf">pdMS_TO_TICKS</span><span class="p">(</span><span class="mi">100</span><span class="p">));</span>  <span class="c1">// 延时 100ms
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 创建任务
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nf">xTaskCreate</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="n">vTaskFunction</span><span class="p">,</span>    <span class="c1">// 任务函数
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="s">&#34;LED_Task&#34;</span><span class="p">,</span>       <span class="c1">// 任务名称
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="mi">1024</span><span class="p">,</span>             <span class="c1">// 栈大小（字）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nb">NULL</span><span class="p">,</span>             <span class="c1">// 参数
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="mi">3</span><span class="p">,</span>                <span class="c1">// 优先级（数字越大优先级越高）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nb">NULL</span>              <span class="c1">// 任务句柄
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">);</span>
</span></span></code></pre></div><h4 id="2时间片轮转调度">（2）时间片轮转调度</h4>
<p><strong>原理</strong>：同优先级任务轮流执行，每个任务执行固定时间片（Time Slice）。</p>
<p><strong>特点</strong>：</p>
<ul>
<li>公平性好</li>
<li>实时性稍差</li>
<li>适用于软实时系统</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// FreeRTOS 配置
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#define configUSE_TIME_SLICING 1      </span><span class="c1">// 启用时间片轮转
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#define configTICK_RATE_HZ 1000       </span><span class="c1">// 系统滴答频率（1ms）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="c1">// 时间片 = configTICK_RATE_HZ 的倒数 = 1ms
</span></span></span><span class="line"><span class="cl"><span class="c1">// 每个同优先级任务执行 1ms 后切换到下一个
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="c1">// 示例：两个同优先级任务
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="nf">vTask1</span><span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="n">pvParameters</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">while</span> <span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// 任务 1 代码
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="nf">vTaskDelay</span><span class="p">(</span><span class="nf">pdMS_TO_TICKS</span><span class="p">(</span><span class="mi">10</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">vTask2</span><span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="n">pvParameters</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">while</span> <span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// 任务 2 代码
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="nf">vTaskDelay</span><span class="p">(</span><span class="nf">pdMS_TO_TICKS</span><span class="p">(</span><span class="mi">10</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 两个任务优先级相同，轮流执行
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nf">xTaskCreate</span><span class="p">(</span><span class="n">vTask1</span><span class="p">,</span> <span class="s">&#34;Task1&#34;</span><span class="p">,</span> <span class="mi">512</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nf">xTaskCreate</span><span class="p">(</span><span class="n">vTask2</span><span class="p">,</span> <span class="s">&#34;Task2&#34;</span><span class="p">,</span> <span class="mi">512</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">);</span>
</span></span></code></pre></div><h4 id="3协作式调度">（3）协作式调度</h4>
<p><strong>原理</strong>：任务主动让出 CPU，调度器不强制切换。</p>
<p><strong>特点</strong>：</p>
<ul>
<li>上下文切换少，开销低</li>
<li>依赖任务&quot;自觉&quot;，实时性差</li>
<li>适用于简单系统、低功耗场景</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// FreeRTOS 配置
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#define configUSE_PREEMPTION 0  </span><span class="c1">// 禁用抢占，启用协作式调度
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="c1">// 任务必须主动让出 CPU
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="nf">vTaskFunction</span><span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="n">pvParameters</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">while</span> <span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// 执行工作
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="nf">DoWork</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="c1">// 主动让出 CPU（必须调用！）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="nf">taskYIELD</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="32-上下文切换流程">3.2 上下文切换流程</h3>
<p>上下文切换是 RTOS 的核心操作，涉及以下步骤：</p>
<pre tabindex="0"><code>┌─────────────────────────────────────────────────────────────┐
│                    上下文切换流程                            │
├─────────────────────────────────────────────────────────────┤
│  1. 保存当前任务上下文（寄存器、PC、SP）到当前 TCB            │
│  2. 更新当前 TCB 状态为&#34;就绪&#34;或&#34;阻塞&#34;                         │
│  3. 从就绪列表选择下一个最高优先级任务                        │
│  4. 更新新 TCB 状态为&#34;运行&#34;                                   │
│  5. 从新 TCB 恢复上下文（寄存器、PC、SP）                     │
│  6. 跳转到新任务的 PC 继续执行                                │
└─────────────────────────────────────────────────────────────┘
</code></pre><p><strong>ARM Cortex-M 汇编实现</strong>（PendSV 中断处理程序）：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-asm" data-lang="asm"><span class="line"><span class="cl"><span class="c1">; PendSV_Handler - 上下文切换的核心
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nl">PendSV_Handler:</span>
</span></span><span class="line"><span class="cl">    <span class="c1">; 禁用 SysTick 中断（防止切换过程中被打断）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">CPSID</span> <span class="no">I</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">; 保存当前任务上下文
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">MRS</span> <span class="no">R0</span><span class="p">,</span> <span class="no">PSP</span>              <span class="c1">; 获取进程栈指针（PSP）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">STMDB</span> <span class="no">R0</span><span class="p">!,</span> <span class="err">{</span><span class="no">R4-R11</span><span class="err">}</span>      <span class="c1">; 保存寄存器 R4-R11 到栈
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">LDR</span> <span class="no">R1</span><span class="p">,</span> <span class="err">=</span><span class="no">pxCurrentTCB</span>    <span class="c1">; 获取当前 TCB 地址
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">LDR</span> <span class="no">R2</span><span class="p">,</span> <span class="p">[</span><span class="no">R1</span><span class="p">]</span>             <span class="c1">; 读取当前 TCB 指针
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">STR</span> <span class="no">R0</span><span class="p">,</span> <span class="p">[</span><span class="no">R2</span><span class="p">]</span>             <span class="c1">; 保存 SP 到 TCB（保存上下文）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    
</span></span><span class="line"><span class="cl">    <span class="c1">; 调用调度器选择下一个任务
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">BL</span> <span class="no">vTaskSwitchContext</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">; 恢复新任务上下文
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">LDR</span> <span class="no">R1</span><span class="p">,</span> <span class="err">=</span><span class="no">pxCurrentTCB</span>
</span></span><span class="line"><span class="cl">    <span class="nf">LDR</span> <span class="no">R2</span><span class="p">,</span> <span class="p">[</span><span class="no">R1</span><span class="p">]</span>             <span class="c1">; 获取新 TCB
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">LDR</span> <span class="no">R0</span><span class="p">,</span> <span class="p">[</span><span class="no">R2</span><span class="p">]</span>             <span class="c1">; 从 TCB 读取 SP
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">LDMIA</span> <span class="no">R0</span><span class="p">!,</span> <span class="err">{</span><span class="no">R4-R11</span><span class="err">}</span>      <span class="c1">; 恢复寄存器 R4-R11
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">MSR</span> <span class="no">PSP</span><span class="p">,</span> <span class="no">R0</span>              <span class="c1">; 设置进程栈指针
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    
</span></span><span class="line"><span class="cl">    <span class="c1">; 恢复 SysTick 中断
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">CPSIE</span> <span class="no">I</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="nf">BX</span> <span class="no">LR</span>                    <span class="c1">; 返回（继续执行新任务）
</span></span></span></code></pre></div><h3 id="33-调度器性能分析">3.3 调度器性能分析</h3>
<table>
<thead>
<tr>
<th>操作</th>
<th>耗时（Cortex-M4 @ 168MHz）</th>
<th>时钟周期</th>
</tr>
</thead>
<tbody>
<tr>
<td>保存上下文（R4-R11）</td>
<td>~0.5μs</td>
<td>~84 周期</td>
</tr>
<tr>
<td>查找最高优先级</td>
<td>~0.1μs</td>
<td>~17 周期（使用 CLZ 指令）</td>
</tr>
<tr>
<td>恢复上下文（R4-R11）</td>
<td>~0.5μs</td>
<td>~84 周期</td>
</tr>
<tr>
<td><strong>总切换时间</strong></td>
<td><strong>~1.1μs</strong></td>
<td><strong>~185 周期</strong></td>
</tr>
</tbody>
</table>
<p><strong>优化技巧</strong>：</p>
<ul>
<li>使用 Cortex-M 的 CLZ（Count Leading Zeros）指令快速查找最高优先级</li>
<li>将 TCB 放在 DTCM（数据紧耦合内存）中，零等待访问</li>
<li>减少任务数量，降低调度开销</li>
</ul>
<h2 id="中断管理机制">中断管理机制</h2>
<h3 id="41-中断与任务的交互">4.1 中断与任务的交互</h3>
<p>RTOS 中，中断服务程序（ISR）与任务通过以下方式交互：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// 方式 1：中断释放信号量，任务等待信号量
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">SemaphoreHandle_t</span> <span class="n">xISR_Semaphore</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 中断服务程序
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="nf">HAL_GPIO_EXTI_Callback</span><span class="p">(</span><span class="kt">uint16_t</span> <span class="n">GPIO_Pin</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">BaseType_t</span> <span class="n">xHigherPriorityTaskWoken</span> <span class="o">=</span> <span class="n">pdFALSE</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 在中断中释放信号量
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">xSemaphoreGiveFromISR</span><span class="p">(</span><span class="n">xISR_Semaphore</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">xHigherPriorityTaskWoken</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 如果有更高优先级任务被唤醒，触发上下文切换
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">portYIELD_FROM_ISR</span><span class="p">(</span><span class="n">xHigherPriorityTaskWoken</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 任务函数
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="nf">vTaskFunction</span><span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="n">pvParameters</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">while</span> <span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// 等待信号量（阻塞）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="k">if</span> <span class="p">(</span><span class="nf">xSemaphoreTake</span><span class="p">(</span><span class="n">xISR_Semaphore</span><span class="p">,</span> <span class="n">portMAX_DELAY</span><span class="p">)</span> <span class="o">==</span> <span class="n">pdTRUE</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="c1">// 信号量到达，处理事件
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="nf">ProcessEvent</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 方式 2：中断发送队列消息
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">QueueHandle_t</span> <span class="n">xISR_Queue</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">TIM_IRQHandler</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">uint32_t</span> <span class="n">data</span> <span class="o">=</span> <span class="nf">GetTimerValue</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="n">BaseType_t</span> <span class="n">xHigherPriorityTaskWoken</span> <span class="o">=</span> <span class="n">pdFALSE</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 在中断中发送队列消息
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">xQueueSendFromISR</span><span class="p">(</span><span class="n">xISR_Queue</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">data</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">xHigherPriorityTaskWoken</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nf">portYIELD_FROM_ISR</span><span class="p">(</span><span class="n">xHigherPriorityTaskWoken</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 方式 3：使用任务通知（最快，推荐）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="nf">EXTI_IRQHandler</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">BaseType_t</span> <span class="n">xHigherPriorityTaskWoken</span> <span class="o">=</span> <span class="n">pdFALSE</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 直接通知任务（无需信号量/队列对象）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">vTaskNotifyGiveFromISR</span><span class="p">(</span><span class="n">xTaskHandle</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">xHigherPriorityTaskWoken</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nf">portYIELD_FROM_ISR</span><span class="p">(</span><span class="n">xHigherPriorityTaskWoken</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 任务接收通知
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="nf">vTaskFunction</span><span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="n">pvParameters</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">while</span> <span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// 等待通知（替代 xSemaphoreTake）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="nf">ulTaskNotifyTake</span><span class="p">(</span><span class="n">pdTRUE</span><span class="p">,</span> <span class="n">portMAX_DELAY</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="nf">ProcessEvent</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="42-中断延迟分析">4.2 中断延迟分析</h3>
<p>中断延迟 = 硬件延迟 + 软件延迟</p>
<table>
<thead>
<tr>
<th>来源</th>
<th>典型值（Cortex-M4）</th>
</tr>
</thead>
<tbody>
<tr>
<td>硬件压栈（自动：R0-R3, R12, LR, PC, xPSR）</td>
<td>12 时钟周期</td>
</tr>
<tr>
<td>中断向量查找</td>
<td>2 时钟周期</td>
</tr>
<tr>
<td>RTOS 中断入口（保存额外上下文）</td>
<td>~50 时钟周期</td>
</tr>
<tr>
<td><strong>总延迟</strong></td>
<td><strong>~64 时钟周期 ≈ 0.38μs @ 168MHz</strong></td>
</tr>
</tbody>
</table>
<h3 id="43-中断优先级配置">4.3 中断优先级配置</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// Cortex-M4 中断优先级配置
</span></span></span><span class="line"><span class="cl"><span class="c1">// 优先级分组：4 位抢占优先级，0 位子优先级
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nf">NVIC_SetPriorityGrouping</span><span class="p">(</span><span class="n">NVIC_PRIORITYGROUP_4</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 配置中断优先级（0 = 最高，15 = 最低）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nf">NVIC_SetPriority</span><span class="p">(</span><span class="n">SysTick_IRQn</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>      <span class="c1">// SysTick 最高优先级
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nf">NVIC_SetPriority</span><span class="p">(</span><span class="n">PendSV_IRQn</span><span class="p">,</span> <span class="mi">15</span><span class="p">);</span>      <span class="c1">// PendSV 最低优先级（重要！）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nf">NVIC_SetPriority</span><span class="p">(</span><span class="n">SVCall_IRQn</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>       <span class="c1">// SVC 最高优先级
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="c1">// 注意：PendSV 必须设为最低优先级，确保其他中断不被延迟
</span></span></span><span class="line"><span class="cl"><span class="c1">// 否则会影响中断响应时间
</span></span></span></code></pre></div><h2 id="进程间通信ipc完全指南">进程间通信（IPC）完全指南</h2>
<h3 id="51-信号量semaphore">5.1 信号量（Semaphore）</h3>
<h4 id="二值信号量">二值信号量</h4>
<p>用于同步和事件标志：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// 创建二值信号量
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">SemaphoreHandle_t</span> <span class="n">xBinarySemaphore</span> <span class="o">=</span> <span class="nf">xSemaphoreCreateBinary</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 释放信号量（中断/任务中）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nf">xSemaphoreGive</span><span class="p">(</span><span class="n">xBinarySemaphore</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 获取信号量（任务中）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">if</span> <span class="p">(</span><span class="nf">xSemaphoreTake</span><span class="p">(</span><span class="n">xBinarySemaphore</span><span class="p">,</span> <span class="n">portMAX_DELAY</span><span class="p">)</span> <span class="o">==</span> <span class="n">pdTRUE</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 成功获取
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">HandleEvent</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 带超时的获取
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">if</span> <span class="p">(</span><span class="nf">xSemaphoreTake</span><span class="p">(</span><span class="n">xBinarySemaphore</span><span class="p">,</span> <span class="nf">pdMS_TO_TICKS</span><span class="p">(</span><span class="mi">100</span><span class="p">))</span> <span class="o">==</span> <span class="n">pdTRUE</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 100ms 内收到信号
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 超时
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">HandleTimeout</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p><strong>应用场景</strong>：</p>
<ul>
<li>中断与任务同步</li>
<li>事件标志</li>
<li>资源可用性指示</li>
</ul>
<h4 id="计数信号量">计数信号量</h4>
<p>用于资源计数和批量事件处理：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// 创建计数信号量（最大计数 10，初始计数 0）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">SemaphoreHandle_t</span> <span class="n">xCountingSemaphore</span> <span class="o">=</span> <span class="nf">xSemaphoreCreateCounting</span><span class="p">(</span><span class="mi">10</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 释放（计数 +1）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nf">xSemaphoreGive</span><span class="p">(</span><span class="n">xCountingSemaphore</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 获取（计数 -1）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nf">xSemaphoreTake</span><span class="p">(</span><span class="n">xCountingSemaphore</span><span class="p">,</span> <span class="n">portMAX_DELAY</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 应用场景：处理批量数据
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="nf">DataAcquisition_Task</span><span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="n">pvParameters</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">while</span> <span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// 等待数据到达（可能一次到达多个）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="nf">xSemaphoreTake</span><span class="p">(</span><span class="n">xCountingSemaphore</span><span class="p">,</span> <span class="n">portMAX_DELAY</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="c1">// 处理所有到达的数据
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="k">while</span> <span class="p">(</span><span class="nf">uxSemaphoreGetCount</span><span class="p">(</span><span class="n">xCountingSemaphore</span><span class="p">)</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nf">ProcessData</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">            <span class="nf">xSemaphoreTake</span><span class="p">(</span><span class="n">xCountingSemaphore</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="52-互斥量mutex">5.2 互斥量（Mutex）</h3>
<p>用于资源保护，支持优先级继承：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// 创建互斥量
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">SemaphoreHandle_t</span> <span class="n">xMutex</span> <span class="o">=</span> <span class="nf">xSemaphoreCreateMutex</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 获取互斥量
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">if</span> <span class="p">(</span><span class="nf">xSemaphoreTake</span><span class="p">(</span><span class="n">xMutex</span><span class="p">,</span> <span class="n">portMAX_DELAY</span><span class="p">)</span> <span class="o">==</span> <span class="n">pdTRUE</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 临界区：访问共享资源
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">AccessSharedResource</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 释放互斥量
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">xSemaphoreGive</span><span class="p">(</span><span class="n">xMutex</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 互斥量 vs 信号量对比
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="o">|</span> <span class="err">特性</span> <span class="o">|</span> <span class="err">互斥量</span> <span class="o">|</span> <span class="err">信号量</span> <span class="o">|</span>
</span></span><span class="line"><span class="cl"><span class="o">|------|--------|--------|</span>
</span></span><span class="line"><span class="cl"><span class="o">|</span> <span class="err">所有权</span> <span class="o">|</span> <span class="err">有（谁获取谁释放）</span> <span class="o">|</span> <span class="err">无</span> <span class="o">|</span>
</span></span><span class="line"><span class="cl"><span class="o">|</span> <span class="err">优先级继承</span> <span class="o">|</span> <span class="err">支持</span> <span class="o">|</span> <span class="err">不支持</span> <span class="o">|</span>
</span></span><span class="line"><span class="cl"><span class="o">|</span> <span class="err">嵌套获取</span> <span class="o">|</span> <span class="err">支持</span> <span class="o">|</span> <span class="err">不支持</span> <span class="o">|</span>
</span></span><span class="line"><span class="cl"><span class="o">|</span> <span class="err">使用场景</span> <span class="o">|</span> <span class="err">资源保护</span> <span class="o">|</span> <span class="err">同步</span><span class="o">/</span><span class="err">计数</span> <span class="o">|</span>
</span></span></code></pre></div><h3 id="53-消息队列">5.3 消息队列</h3>
<p>用于任务间数据传递：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// 创建队列（10 个消息，每个 32 字节）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">QueueHandle_t</span> <span class="n">xQueue</span> <span class="o">=</span> <span class="nf">xQueueCreate</span><span class="p">(</span><span class="mi">10</span><span class="p">,</span> <span class="mi">32</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 发送数据
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">int</span> <span class="n">data</span> <span class="o">=</span> <span class="mi">42</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="p">(</span><span class="nf">xQueueSend</span><span class="p">(</span><span class="n">xQueue</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">data</span><span class="p">,</span> <span class="n">portMAX_DELAY</span><span class="p">)</span> <span class="o">==</span> <span class="n">pdTRUE</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 发送成功
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 接收数据
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">int</span> <span class="n">received</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="p">(</span><span class="nf">xQueueReceive</span><span class="p">(</span><span class="n">xQueue</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">received</span><span class="p">,</span> <span class="n">portMAX_DELAY</span><span class="p">)</span> <span class="o">==</span> <span class="n">pdTRUE</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 接收成功，处理数据
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">ProcessData</span><span class="p">(</span><span class="n">received</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 带超时的接收
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">if</span> <span class="p">(</span><span class="nf">xQueueReceive</span><span class="p">(</span><span class="n">xQueue</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">received</span><span class="p">,</span> <span class="nf">pdMS_TO_TICKS</span><span class="p">(</span><span class="mi">100</span><span class="p">))</span> <span class="o">==</span> <span class="n">pdTRUE</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 100ms 内收到数据
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 超时
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 队列前端发送（高优先级消息）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nf">xQueueSendToFront</span><span class="p">(</span><span class="n">xQueue</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">urgent_data</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
</span></span></code></pre></div><h3 id="54-任务通知推荐">5.4 任务通知（推荐）</h3>
<p>任务通知是 FreeRTOS V8.2 引入的最快 IPC 机制：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// 发送通知（中断中，最快）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="nf">EXTI_IRQHandler</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">BaseType_t</span> <span class="n">xHigherPriorityTaskWoken</span> <span class="o">=</span> <span class="n">pdFALSE</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nf">vTaskNotifyGiveFromISR</span><span class="p">(</span><span class="n">xTaskHandle</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">xHigherPriorityTaskWoken</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nf">portYIELD_FROM_ISR</span><span class="p">(</span><span class="n">xHigherPriorityTaskWoken</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 接收通知（任务中）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="nf">vTaskFunction</span><span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="n">pvParameters</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">while</span> <span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// 等待通知（替代 xSemaphoreTake）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="nf">ulTaskNotifyTake</span><span class="p">(</span><span class="n">pdTRUE</span><span class="p">,</span> <span class="n">portMAX_DELAY</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// 处理事件
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="nf">ProcessEvent</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 性能对比
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="o">|</span> <span class="n">IPC</span> <span class="err">机制</span> <span class="o">|</span> <span class="err">发送耗时</span> <span class="o">|</span> <span class="err">接收耗时</span> <span class="o">|</span> <span class="err">内存占用</span> <span class="o">|</span>
</span></span><span class="line"><span class="cl"><span class="o">|----------|---------|---------|---------|</span>
</span></span><span class="line"><span class="cl"><span class="o">|</span> <span class="err">任务通知</span> <span class="o">|</span> <span class="o">~</span><span class="mf">0.5</span><span class="err">μ</span><span class="n">s</span> <span class="o">|</span> <span class="o">~</span><span class="mf">0.5</span><span class="err">μ</span><span class="n">s</span> <span class="o">|</span> <span class="mi">0</span> <span class="err">字节（内置在</span> <span class="n">TCB</span><span class="err">）</span> <span class="o">|</span>
</span></span><span class="line"><span class="cl"><span class="o">|</span> <span class="err">二值信号量</span> <span class="o">|</span> <span class="o">~</span><span class="mi">2</span><span class="err">μ</span><span class="n">s</span> <span class="o">|</span> <span class="o">~</span><span class="mi">2</span><span class="err">μ</span><span class="n">s</span> <span class="o">|</span> <span class="o">~</span><span class="mi">80</span> <span class="err">字节</span> <span class="o">|</span>
</span></span><span class="line"><span class="cl"><span class="o">|</span> <span class="err">消息队列</span> <span class="o">|</span> <span class="o">~</span><span class="mi">5</span><span class="err">μ</span><span class="n">s</span> <span class="o">|</span> <span class="o">~</span><span class="mi">5</span><span class="err">μ</span><span class="n">s</span> <span class="o">|</span> <span class="err">队列大小</span> <span class="o">+</span> <span class="err">开销</span> <span class="o">|</span>
</span></span></code></pre></div><h2 id="内存管理策略">内存管理策略</h2>
<h3 id="61-静态内存分配">6.1 静态内存分配</h3>
<p>编译时确定内存，无碎片风险：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// 静态分配 TCB 和栈
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">StaticTask_t</span> <span class="n">xTaskBuffer</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="n">StackType_t</span> <span class="n">xStack</span><span class="p">[</span><span class="mi">1024</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">TaskHandle_t</span> <span class="n">xTask</span> <span class="o">=</span> <span class="nf">xTaskCreateStatic</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="n">vTaskCode</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s">&#34;TaskName&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="mi">1024</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nb">NULL</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="mi">5</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">xStack</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="o">&amp;</span><span class="n">xTaskBuffer</span>
</span></span><span class="line"><span class="cl"><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 静态分配信号量
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">StaticSemaphore_t</span> <span class="n">xSemaphoreBuffer</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="n">SemaphoreHandle_t</span> <span class="n">xSemaphore</span> <span class="o">=</span> <span class="nf">xSemaphoreCreateBinaryStatic</span><span class="p">(</span><span class="o">&amp;</span><span class="n">xSemaphoreBuffer</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 优点：
</span></span></span><span class="line"><span class="cl"><span class="c1">// - 无内存碎片
</span></span></span><span class="line"><span class="cl"><span class="c1">// - 编译时确定内存使用
</span></span></span><span class="line"><span class="cl"><span class="c1">// - 无分配失败风险
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="c1">// 缺点：
</span></span></span><span class="line"><span class="cl"><span class="c1">// - 灵活性差
</span></span></span><span class="line"><span class="cl"><span class="c1">// - 代码冗长
</span></span></span></code></pre></div><h3 id="62-动态内存分配堆方案">6.2 动态内存分配（堆方案）</h3>
<p>FreeRTOS 提供 5 种堆实现：</p>
<table>
<thead>
<tr>
<th>方案</th>
<th>特点</th>
<th>适用场景</th>
</tr>
</thead>
<tbody>
<tr>
<td>heap_1.c</td>
<td>仅分配，不释放</td>
<td>简单系统</td>
</tr>
<tr>
<td>heap_2.c</td>
<td>分配 + 释放，不合并</td>
<td>固定大小块</td>
</tr>
<tr>
<td><strong>heap_4.c</strong></td>
<td>分配 + 释放 + 合并</td>
<td><strong>通用推荐</strong></td>
</tr>
<tr>
<td>heap_5.c</td>
<td>支持多个不连续内存区</td>
<td>复杂内存布局</td>
</tr>
</tbody>
</table>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// heap_4.c 配置
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#define configTOTAL_HEAP_SIZE ((size_t)(32 * 1024))  </span><span class="c1">// 32KB 堆
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="c1">// 内存分配
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="o">*</span><span class="n">pv</span> <span class="o">=</span> <span class="nf">pvPortMalloc</span><span class="p">(</span><span class="mi">1024</span><span class="p">);</span>  <span class="c1">// 分配 1KB
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nf">vPortFree</span><span class="p">(</span><span class="n">pv</span><span class="p">);</span>                   <span class="c1">// 释放（自动合并相邻空闲块）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="c1">// 内存碎片问题：
</span></span></span><span class="line"><span class="cl"><span class="c1">// 频繁分配/释放不同大小的块会导致碎片
</span></span></span><span class="line"><span class="cl"><span class="c1">// heap_4.c 通过合并相邻空闲块缓解此问题
</span></span></span></code></pre></div><h3 id="63-内存池">6.3 内存池</h3>
<p>固定大小块分配，无碎片：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// 创建内存池（10 个块，每块 256 字节）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#define BLOCK_SIZE 256
</span></span></span><span class="line"><span class="cl"><span class="cp">#define NUM_BLOCKS 10
</span></span></span><span class="line"><span class="cl"><span class="cp"></span><span class="kt">uint8_t</span> <span class="n">ucMemoryPool</span><span class="p">[</span><span class="n">BLOCK_SIZE</span> <span class="o">*</span> <span class="n">NUM_BLOCKS</span><span class="p">];</span>
</span></span><span class="line"><span class="cl"><span class="kt">uint8_t</span> <span class="n">ucBlockStatus</span><span class="p">[</span><span class="n">NUM_BLOCKS</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="o">*</span><span class="nf">pvGetBlock</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">NUM_BLOCKS</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">ucBlockStatus</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">ucBlockStatus</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="o">&amp;</span><span class="n">ucMemoryPool</span><span class="p">[</span><span class="n">i</span> <span class="o">*</span> <span class="n">BLOCK_SIZE</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nb">NULL</span><span class="p">;</span>  <span class="c1">// 无可用块
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">vReleaseBlock</span><span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="n">pv</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="n">index</span> <span class="o">=</span> <span class="p">((</span><span class="kt">uint8_t</span><span class="o">*</span><span class="p">)</span><span class="n">pv</span> <span class="o">-</span> <span class="n">ucMemoryPool</span><span class="p">)</span> <span class="o">/</span> <span class="n">BLOCK_SIZE</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">ucBlockStatus</span><span class="p">[</span><span class="n">index</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h2 id="优先级反转与解决方案">优先级反转与解决方案</h2>
<h3 id="71-优先级反转问题">7.1 优先级反转问题</h3>
<figure>
<svg viewBox="0 0 800 250" xmlns="http://www.w3.org/2000/svg" style="max-width:100%;height:auto;background:#f8f9fa;border-radius:8px;padding:20px;">
  <defs>
    <style>
      .timeline { stroke: #555; stroke-width: 2; }
      .box { fill: #1a1a2e; stroke-width: 2; rx: 6; }
      .box-low { stroke: #2ecc71; }
      .box-mid { stroke: #f39c12; }
      .box-high { stroke: #e74c3c; }
      .text { fill: #e0e0e0; font-family: Arial, sans-serif; font-size: 13px; }
      .label { fill: #4a9eff; font-family: Arial, sans-serif; font-size: 14px; font-weight: bold; }
    </style>
  </defs>
  <!-- 时间轴 -->
  <line class="timeline" x1="60" y1="200" x2="780" y2="200"/>
  <text class="label" x="400" y="240">时间 →</text>
  <!-- 低优先级任务 L -->
  <rect class="box box-low" x="80" y="120" width="150" height="50"/>
  <text class="text" x="155" y="150">L: 持有互斥量</text>
  <rect class="box box-low" x="380" y="120" width="100" height="50"/>
  <text class="text" x="430" y="150">L: 释放</text>
  <!-- 中优先级任务 M -->
  <rect class="box box-mid" x="230" y="70" width="200" height="50"/>
  <text class="text" x="330" y="100">M: 抢占 L</text>
  <!-- 高优先级任务 H -->
  <rect class="box box-high" x="150" y="20" width="80" height="50"/>
  <text class="text" x="190" y="50">H: 就绪</text>
  <rect class="box box-high" style="fill:#2a1a2e; stroke:#9b59b6;" x="230" y="20" width="200" height="50"/>
  <text class="text" x="330" y="50" fill="#e74c3c">H: 阻塞等待</text>
  <rect class="box box-high" x="480" y="20" width="150" height="50"/>
  <text class="text" x="555" y="50">H: 执行</text>
  <!-- 标注 -->
<p><text class="text" x="330" y="180" fill="#e74c3c">⚠️ 优先级反转：H 被 M 阻塞</text>
</svg></p>
<figcaption style="text-align:center;color:#888;font-size:12px;margin-top:8px;">优先级反转场景</figcaption>
</figure>
<p><strong>场景描述</strong>：</p>
<ol>
<li>低优先级任务 L 获取互斥量</li>
<li>高优先级任务 H 就绪，抢占 L</li>
<li>H 请求同一互斥量，被阻塞</li>
<li>中优先级任务 M 就绪，抢占 L</li>
<li><strong>结果</strong>：H 被 M 间接阻塞（优先级反转）</li>
</ol>
<h3 id="72-优先级继承解决方案">7.2 优先级继承解决方案</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// FreeRTOS 互斥量实现优先级继承
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">SemaphoreHandle_t</span> <span class="n">xMutex</span> <span class="o">=</span> <span class="nf">xSemaphoreCreateMutex</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 当高优先级任务 H 请求互斥量时：
</span></span></span><span class="line"><span class="cl"><span class="c1">// 1. 检测到互斥量被 L 持有
</span></span></span><span class="line"><span class="cl"><span class="c1">// 2. 临时提升 L 的优先级到 H 的级别
</span></span></span><span class="line"><span class="cl"><span class="c1">// 3. L 释放互斥量后，恢复原始优先级
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="c1">// 伪代码
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="nf">prvGiveMutexFromISR</span><span class="p">(</span><span class="n">StaticSemaphore_t</span> <span class="o">*</span><span class="n">pxMutex</span><span class="p">,</span> <span class="n">TaskHandle_t</span> <span class="n">pxHolder</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">pxMutex</span><span class="o">-&gt;</span><span class="n">ucQueueType</span> <span class="o">==</span> <span class="n">queueQUEUE_TYPE_MUTEX</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// 恢复持有者的原始优先级
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="nf">vTaskPrioritySet</span><span class="p">(</span><span class="n">pxHolder</span><span class="p">,</span> <span class="n">pxMutex</span><span class="o">-&gt;</span><span class="n">uxBasePriority</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 使用互斥量（自动启用优先级继承）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="nf">AccessSharedResource</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="nf">xSemaphoreTake</span><span class="p">(</span><span class="n">xMutex</span><span class="p">,</span> <span class="n">portMAX_DELAY</span><span class="p">)</span> <span class="o">==</span> <span class="n">pdTRUE</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// 临界区
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="n">SharedResource</span><span class="o">++</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="nf">xSemaphoreGive</span><span class="p">(</span><span class="n">xMutex</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="73-其他解决方案">7.3 其他解决方案</h3>
<table>
<thead>
<tr>
<th>方案</th>
<th>描述</th>
<th>优缺点</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>优先级继承</strong></td>
<td>临时提升持有者优先级</td>
<td>自动处理，但有开销</td>
</tr>
<tr>
<td><strong>优先级天花板</strong></td>
<td>互斥量固定为最高优先级</td>
<td>简单，但可能过度提升</td>
</tr>
<tr>
<td><strong>禁用调度器</strong></td>
<td>临界区内禁用调度</td>
<td>简单，但影响实时性</td>
</tr>
<tr>
<td><strong>资源分层</strong></td>
<td>按固定顺序获取资源</td>
<td>预防死锁，需设计</td>
</tr>
</tbody>
</table>
<h2 id="实时性优化实战">实时性优化实战</h2>
<h3 id="81-减少上下文切换">8.1 减少上下文切换</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// 错误示例：频繁切换
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="mi">100</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nf">xQueueSend</span><span class="p">(</span><span class="n">xQueue</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">data</span><span class="p">[</span><span class="n">i</span><span class="p">],</span> <span class="n">portMAX_DELAY</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 每次都触发切换
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 正确示例：批量处理
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="mi">100</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nf">xQueueSend</span><span class="p">(</span><span class="n">xQueue</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">data</span><span class="p">[</span><span class="n">i</span><span class="p">],</span> <span class="mi">0</span><span class="p">);</span>  <span class="c1">// 不阻塞
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="nf">taskYIELD</span><span class="p">();</span>  <span class="c1">// 手动切换一次
</span></span></span></code></pre></div><h3 id="82-零拷贝设计">8.2 零拷贝设计</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// 使用指针队列
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">QueueHandle_t</span> <span class="n">xPtrQueue</span> <span class="o">=</span> <span class="nf">xQueueCreate</span><span class="p">(</span><span class="mi">10</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="kt">void</span><span class="o">*</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 发送指针
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">Data_t</span> <span class="o">*</span><span class="n">pData</span> <span class="o">=</span> <span class="nf">pvPortMalloc</span><span class="p">(</span><span class="k">sizeof</span><span class="p">(</span><span class="n">Data_t</span><span class="p">));</span>
</span></span><span class="line"><span class="cl"><span class="nf">xQueueSend</span><span class="p">(</span><span class="n">xPtrQueue</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">pData</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 接收指针
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">Data_t</span> <span class="o">*</span><span class="n">pReceived</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nf">xQueueReceive</span><span class="p">(</span><span class="n">xPtrQueue</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">pReceived</span><span class="p">,</span> <span class="n">portMAX_DELAY</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 处理完成后释放
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nf">vPortFree</span><span class="p">(</span><span class="n">pReceived</span><span class="p">);</span>
</span></span></code></pre></div><h3 id="83-使用-dtcmitcm">8.3 使用 DTCM/ITCM</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// Cortex-M7 紧耦合内存
</span></span></span><span class="line"><span class="cl"><span class="c1">// 将关键代码放在 ITCM（零等待执行）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="nf">__attribute__</span><span class="p">((</span><span class="nf">section</span><span class="p">(</span><span class="s">&#34;.itcm&#34;</span><span class="p">)))</span> <span class="nf">Critical_ISR_Handler</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 实时性要求极高的代码
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 将关键数据放在 DTCM（零等待访问）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">uint32_t</span> <span class="nf">__attribute__</span><span class="p">((</span><span class="nf">section</span><span class="p">(</span><span class="s">&#34;.dtcm&#34;</span><span class="p">)))</span> <span class="n">Critical_Data</span><span class="p">[</span><span class="mi">256</span><span class="p">];</span>
</span></span></code></pre></div><h3 id="84-中断优化dpc-模式">8.4 中断优化（DPC 模式）</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// 延迟过程调用（Deferred Procedure Call）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">SemaphoreHandle_t</span> <span class="n">xDPC_Semaphore</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">Fast_ISR_Handler</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 只做最紧急的工作（&lt; 1μs）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">ClearInterruptFlag</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="nf">ReadHardwareRegister</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 唤醒 DPC 任务处理剩余工作
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">BaseType_t</span> <span class="n">xHigherPriorityTaskWoken</span> <span class="o">=</span> <span class="n">pdFALSE</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nf">xSemaphoreGiveFromISR</span><span class="p">(</span><span class="n">xDPC_Semaphore</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">xHigherPriorityTaskWoken</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nf">portYIELD_FROM_ISR</span><span class="p">(</span><span class="n">xHigherPriorityTaskWoken</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">vDPCTask</span><span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="n">pvParameters</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">while</span> <span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nf">xSemaphoreTake</span><span class="p">(</span><span class="n">xDPC_Semaphore</span><span class="p">,</span> <span class="n">portMAX_DELAY</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// 处理非紧急工作（可耗时较长）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="nf">ProcessDeferredWork</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h2 id="常见-rtos-对比与选型">常见 RTOS 对比与选型</h2>
<h3 id="91-主流-rtos-详细对比">9.1 主流 RTOS 详细对比</h3>
<table>
<thead>
<tr>
<th>特性</th>
<th>FreeRTOS</th>
<th>RT-Thread</th>
<th>Zephyr</th>
<th>uC/OS-III</th>
<th>VxWorks</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>许可证</strong></td>
<td>MIT</td>
<td>Apache 2.0</td>
<td>Apache 2.0</td>
<td>商业</td>
<td>商业</td>
</tr>
<tr>
<td><strong>内核大小</strong></td>
<td>6-10KB</td>
<td>10-30KB</td>
<td>20-50KB</td>
<td>8-15KB</td>
<td>50-100KB</td>
</tr>
<tr>
<td><strong>最低 RAM</strong></td>
<td>512B</td>
<td>2KB</td>
<td>4KB</td>
<td>1KB</td>
<td>16KB</td>
</tr>
<tr>
<td><strong>任务数</strong></td>
<td>无限</td>
<td>无限</td>
<td>无限</td>
<td>无限</td>
<td>无限</td>
</tr>
<tr>
<td><strong>优先级数</strong></td>
<td>无限</td>
<td>256</td>
<td>无限</td>
<td>256</td>
<td>256</td>
</tr>
<tr>
<td><strong>时间片</strong></td>
<td>支持</td>
<td>支持</td>
<td>支持</td>
<td>不支持</td>
<td>支持</td>
</tr>
<tr>
<td><strong>内存保护</strong></td>
<td>MPU 可选</td>
<td>MPU 可选</td>
<td>MMU/MPU</td>
<td>MPU 可选</td>
<td>MMU</td>
</tr>
<tr>
<td><strong>POSIX 兼容</strong></td>
<td>部分</td>
<td>部分</td>
<td>完整</td>
<td>无</td>
<td>完整</td>
</tr>
<tr>
<td><strong>网络栈</strong></td>
<td>第三方</td>
<td>内置</td>
<td>内置</td>
<td>第三方</td>
<td>内置</td>
</tr>
<tr>
<td><strong>文件系统</strong></td>
<td>第三方</td>
<td>内置</td>
<td>内置</td>
<td>第三方</td>
<td>内置</td>
</tr>
<tr>
<td><strong>社区活跃度</strong></td>
<td>★★★★★</td>
<td>★★★★☆</td>
<td>★★★★☆</td>
<td>★★★☆☆</td>
<td>★★☆☆☆</td>
</tr>
</tbody>
</table>
<h3 id="92-选型建议">9.2 选型建议</h3>
<table>
<thead>
<tr>
<th>应用场景</th>
<th>推荐 RTOS</th>
<th>理由</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>IoT 设备</strong></td>
<td>FreeRTOS</td>
<td>生态好、免费、AWS 集成</td>
</tr>
<tr>
<td><strong>工业控制</strong></td>
<td>RT-Thread</td>
<td>国产、组件丰富、支持国产 MCU</td>
</tr>
<tr>
<td><strong>蓝牙/WiFi</strong></td>
<td>Zephyr</td>
<td>Linux 基金会、协议栈完善</td>
</tr>
<tr>
<td><strong>医疗设备</strong></td>
<td>uC/OS-III</td>
<td>确定性高、可认证</td>
</tr>
<tr>
<td><strong>航空航天</strong></td>
<td>VxWorks</td>
<td>高可靠、DO-178C 认证</td>
</tr>
<tr>
<td><strong>汽车电子</strong></td>
<td>AUTOSAR OS</td>
<td>符合 AUTOSAR 标准</td>
</tr>
<tr>
<td><strong>消费电子</strong></td>
<td>FreeRTOS/RT-Thread</td>
<td>成本低、开发快</td>
</tr>
</tbody>
</table>
<h2 id="调试与故障排查">调试与故障排查</h2>
<h3 id="101-栈溢出检测">10.1 栈溢出检测</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// FreeRTOS 配置
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#define configCHECK_FOR_STACK_OVERFLOW 2  </span><span class="c1">// 启用栈溢出检测
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="c1">// 栈溢出钩子
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="nf">vApplicationStackOverflowHook</span><span class="p">(</span><span class="n">TaskHandle_t</span> <span class="n">xTask</span><span class="p">,</span> <span class="kt">char</span> <span class="o">*</span><span class="n">pcTaskName</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 栈溢出！记录错误
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">Error_Log</span><span class="p">(</span><span class="s">&#34;Stack overflow: %s&#34;</span><span class="p">,</span> <span class="n">pcTaskName</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 调试：打印调用栈
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">PrintCallStack</span><span class="p">(</span><span class="n">xTask</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 处理：重启或进入安全模式
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">while</span> <span class="p">(</span><span class="mi">1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 运行时监测栈余量
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="nf">CheckStackUsage</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">UBaseType_t</span> <span class="n">uxHighWaterMark</span> <span class="o">=</span> <span class="nf">uxTaskGetStackHighWaterMark</span><span class="p">(</span><span class="nb">NULL</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">uxHighWaterMark</span> <span class="o">&lt;</span> <span class="mi">50</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// 栈余量不足 50 字
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="nf">Warning_Log</span><span class="p">(</span><span class="s">&#34;Low stack: %d words remaining&#34;</span><span class="p">,</span> <span class="n">uxHighWaterMark</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="102-死锁检测">10.2 死锁检测</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// 检测死锁的简单方法
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="nf">DetectDeadlock</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">TaskStatus_t</span> <span class="o">*</span><span class="n">pxTaskStatusArray</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">UBaseType_t</span> <span class="n">uxArraySize</span> <span class="o">=</span> <span class="nf">uxTaskGetNumberOfTasks</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="n">pxTaskStatusArray</span> <span class="o">=</span> <span class="nf">pvPortMalloc</span><span class="p">(</span><span class="n">uxArraySize</span> <span class="o">*</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">TaskStatus_t</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">pxTaskStatusArray</span> <span class="o">!=</span> <span class="nb">NULL</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">uxArraySize</span> <span class="o">=</span> <span class="nf">uxTaskGetSystemState</span><span class="p">(</span><span class="n">pxTaskStatusArray</span><span class="p">,</span> <span class="n">uxArraySize</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="c1">// 检查是否有任务长时间阻塞
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">uxArraySize</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">if</span> <span class="p">(</span><span class="n">pxTaskStatusArray</span><span class="p">[</span><span class="n">i</span><span class="p">].</span><span class="n">eCurrentState</span> <span class="o">==</span> <span class="n">eBlocked</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="k">if</span> <span class="p">(</span><span class="n">pxTaskStatusArray</span><span class="p">[</span><span class="n">i</span><span class="p">].</span><span class="n">xCurrentPriority</span> <span class="o">&lt;</span> <span class="n">LOW_PRIORITY_THRESHOLD</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                    <span class="c1">// 低优先级任务长时间阻塞，可能死锁
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>                    <span class="nf">Warning_Log</span><span class="p">(</span><span class="s">&#34;Possible deadlock: %s&#34;</span><span class="p">,</span> <span class="n">pxTaskStatusArray</span><span class="p">[</span><span class="n">i</span><span class="p">].</span><span class="n">pcTaskName</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">                <span class="p">}</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="nf">vPortFree</span><span class="p">(</span><span class="n">pxTaskStatusArray</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="103-性能分析">10.3 性能分析</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// 启用运行时统计
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#define configGENERATE_RUN_TIME_STATS 1
</span></span></span><span class="line"><span class="cl"><span class="cp">#define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() ConfigureTimer()
</span></span></span><span class="line"><span class="cl"><span class="cp">#define portGET_RUN_TIME_COUNTER_VALUE() GetTimerCount()
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="c1">// 获取任务 CPU 使用率
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="nf">PrintTaskCPUUsage</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">TaskStatus_t</span> <span class="o">*</span><span class="n">pxTaskStatusArray</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">UBaseType_t</span> <span class="n">uxArraySize</span> <span class="o">=</span> <span class="nf">uxTaskGetNumberOfTasks</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="kt">uint32_t</span> <span class="n">ulTotalRunTime</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="n">pxTaskStatusArray</span> <span class="o">=</span> <span class="nf">pvPortMalloc</span><span class="p">(</span><span class="n">uxArraySize</span> <span class="o">*</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">TaskStatus_t</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">pxTaskStatusArray</span> <span class="o">!=</span> <span class="nb">NULL</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">uxArraySize</span> <span class="o">=</span> <span class="nf">uxTaskGetSystemState</span><span class="p">(</span><span class="n">pxTaskStatusArray</span><span class="p">,</span> <span class="n">uxArraySize</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">ulTotalRunTime</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="n">ulTotalRunTime</span> <span class="o">/=</span> <span class="mi">100</span><span class="p">;</span>  <span class="c1">// 转为百分比
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        
</span></span><span class="line"><span class="cl">        <span class="nf">printf</span><span class="p">(</span><span class="s">&#34;Task Name</span><span class="se">\t</span><span class="s">Priority</span><span class="se">\t</span><span class="s">CPU%%</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">uxArraySize</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nf">printf</span><span class="p">(</span><span class="s">&#34;%s</span><span class="se">\t</span><span class="s">%d</span><span class="se">\t</span><span class="s">%d%%</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">,</span> 
</span></span><span class="line"><span class="cl">                   <span class="n">pxTaskStatusArray</span><span class="p">[</span><span class="n">i</span><span class="p">].</span><span class="n">pcTaskName</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                   <span class="n">pxTaskStatusArray</span><span class="p">[</span><span class="n">i</span><span class="p">].</span><span class="n">uxCurrentPriority</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                   <span class="n">pxTaskStatusArray</span><span class="p">[</span><span class="n">i</span><span class="p">].</span><span class="n">ulRunTimeCounter</span> <span class="o">/</span> <span class="n">ulTotalRunTime</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="nf">vPortFree</span><span class="p">(</span><span class="n">pxTaskStatusArray</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h2 id="总结">总结</h2>
<p>RTOS 实时系统开发的核心要点：</p>
<ol>
<li><strong>理解调度机制</strong>：优先级调度、时间片轮转、上下文切换</li>
<li><strong>正确使用 IPC</strong>：信号量、互斥量、队列、任务通知</li>
<li><strong>合理配置内存</strong>：静态分配 vs 动态分配，避免碎片</li>
<li><strong>优化中断处理</strong>：快进快出，延迟过程调用</li>
<li><strong>预防优先级反转</strong>：使用互斥量，启用优先级继承</li>
<li><strong>调试与监测</strong>：栈溢出检测、CPU 使用率分析</li>
</ol>
<p>掌握这些知识，你可以设计出可靠、高效的嵌入式实时系统！</p>
<hr>
<p><em>本文基于 FreeRTOS 官方源码、ARM 技术文档和实际项目经验整理，结合 2026 年最新技术趋势编写。</em></p>
<h2 id="参考资料">参考资料</h2>
<ol>
<li>FreeRTOS Kernel Documentation - <a href="https://www.freertos.org/Documentation/">https://www.freertos.org/Documentation/</a></li>
<li>ARM Cortex-M Technical Reference Manual</li>
<li>Real-Time Operating Systems: A Practitioner&rsquo;s Handbook (2025)</li>
<li>Embedded Systems Architecture: A Comprehensive Guide (2026)</li>
<li>RT-Thread Programming Guide - <a href="https://www.rt-thread.io/docs/">https://www.rt-thread.io/docs/</a></li>
<li>Zephyr Project Documentation - <a href="https://docs.zephyrproject.org/">https://docs.zephyrproject.org/</a></li>
</ol>
]]></content:encoded></item><item><title>嵌入式开发：Arm Cortex-M Microcontroller Programming</title><link>https://tech-snippets.xyz/posts/arm-cortex-m-microcontroller-programming/</link><pubDate>Sun, 05 Apr 2026 03:00:00 +0800</pubDate><guid>https://tech-snippets.xyz/posts/arm-cortex-m-microcontroller-programming/</guid><description>嵌入式开发实战教程：ARM Cortex-M microcontroller programming，从入门到精通的完整指南</description><content:encoded><![CDATA[<h2 id="引言">引言</h2>
<p>本文基于 2026 年最新行业资料整理，涵盖 ARM Cortex-M microcontroller programming 的核心概念、开发流程和实战技巧。无论你是嵌入式初学者还是有经验的开发者，都能从中获得实用的知识和技巧。</p>
<p>ARM Cortex-M 系列处理器占据了 32 位嵌入式市场超过 60% 的份额，从简单的传感器节点到复杂的工业控制系统，都能看到它的身影。掌握 Cortex-M 编程技能，是嵌入式工程师的核心竞争力。</p>
<h2 id="arm-cortex-m-处理器家族详解">ARM Cortex-M 处理器家族详解</h2>
<h3 id="cortex-m-系列对比">Cortex-M 系列对比</h3>

<svg viewBox="0 0 600 280" xmlns="http://www.w3.org/2000/svg" style="max-width:100%;height:auto;background:#f8f9fa;border-radius:8px;padding:20px;"><rect x="20" y="30" width="560" height="25" fill="#e3f2fd" stroke="#1976d2" stroke-width="2"/><text x="40" y="47" font-family="Arial" font-size="11" fill="#1976d2" font-weight="bold">型号</text><text x="120" y="47" font-family="Arial" font-size="11" fill="#1976d2" font-weight="bold">架构</text><text x="200" y="47" font-family="Arial" font-size="11" fill="#1976d2" font-weight="bold">主频</text><text x="280" y="47" font-family="Arial" font-size="11" fill="#1976d2" font-weight="bold">Flash</text><text x="360" y="47" font-family="Arial" font-size="11" fill="#1976d2" font-weight="bold">SRAM</text><text x="440" y="47" font-family="Arial" font-size="11" fill="#1976d2" font-weight="bold">应用场景</text><rect x="20" y="55" width="560" height="22" fill="#fff" stroke="#ccc" stroke-width="1"/><text x="40" y="70" font-family="Arial" font-size="10" fill="#333">Cortex-M0+</text><text x="120" y="70" font-family="Arial" font-size="10" fill="#333">ARMv6-M</text><text x="200" y="70" font-family="Arial" font-size="10" fill="#333">≤50 MHz</text><text x="280" y="70" font-family="Arial" font-size="10" fill="#333">≤64 KB</text><text x="360" y="70" font-family="Arial" font-size="10" fill="#333">≤16 KB</text><text x="440" y="70" font-family="Arial" font-size="10" fill="#333">低成本 IoT、传感器</text><rect x="20" y="77" width="560" height="22" fill="#fff" stroke="#ccc" stroke-width="1"/><text x="40" y="92" font-family="Arial" font-size="10" fill="#333">Cortex-M3</text><text x="120" y="92" font-family="Arial" font-size="10" fill="#333">ARMv7-M</text><text x="200" y="92" font-family="Arial" font-size="10" fill="#333">≤100 MHz</text><text x="280" y="92" font-family="Arial" font-size="10" fill="#333">≤512 KB</text><text x="360" y="92" font-family="Arial" font-size="10" fill="#333">≤128 KB</text><text x="440" y="92" font-family="Arial" font-size="10" fill="#333">工业控制、医疗设备</text><rect x="20" y="99" width="560" height="22" fill="#fff" stroke="#ccc" stroke-width="1"/><text x="40" y="114" font-family="Arial" font-size="10" fill="#333">Cortex-M4</text><text x="120" y="114" font-family="Arial" font-size="10" fill="#333">ARMv7E-M</text><text x="200" y="114" font-family="Arial" font-size="10" fill="#333">≤200 MHz</text><text x="280" y="114" font-family="Arial" font-size="10" fill="#333">≤2 MB</text><text x="360" y="114" font-family="Arial" font-size="10" fill="#333">≤512 KB</text><text x="440" y="114" font-family="Arial" font-size="10" fill="#333">DSP、电机控制</text><rect x="20" y="121" width="560" height="22" fill="#fff" stroke="#ccc" stroke-width="1"/><text x="40" y="136" font-family="Arial" font-size="10" fill="#333">Cortex-M7</text><text x="120" y="136" font-family="Arial" font-size="10" fill="#333">ARMv7E-M</text><text x="200" y="136" font-family="Arial" font-size="10" fill="#333">≤600 MHz</text><text x="280" y="136" font-family="Arial" font-size="10" fill="#333">≤4 MB</text><text x="360" y="136" font-family="Arial" font-size="10" fill="#333">≤1 MB</text><text x="440" y="136" font-family="Arial" font-size="10" fill="#333">高性能 HMI、音频</text><rect x="20" y="143" width="560" height="22" fill="#fff" stroke="#ccc" stroke-width="1"/><text x="40" y="158" font-family="Arial" font-size="10" fill="#333">Cortex-M33</text><text x="120" y="158" font-family="Arial" font-size="10" fill="#333">ARMv8-M</text><text x="200" y="158" font-family="Arial" font-size="10" fill="#333">≤200 MHz</text><text x="280" y="158" font-family="Arial" font-size="10" fill="#333">≤2 MB</text><text x="360" y="158" font-family="Arial" font-size="10" fill="#333">≤512 KB</text><text x="440" y="158" font-family="Arial" font-size="10" fill="#333">安全 IoT、TrustZone</text><text x="40" y="190" font-family="Arial" font-size="10" fill="#666" font-weight="bold">选型建议：</text><text x="40" y="210" font-family="Arial" font-size="9" fill="#666">• 入门学习：STM32F103 (Cortex-M3) 或 STM32F407 (Cortex-M4)</text><text x="40" y="225" font-family="Arial" font-size="9" fill="#666">• 低功耗 IoT：STM32L4 系列 (Cortex-M4) 或 STM32L0 系列 (Cortex-M0+)</text><text x="40" y="240" font-family="Arial" font-size="9" fill="#666">• 高性能应用：STM32H7 系列 (Cortex-M7) 或 STM32U5 系列 (Cortex-M33)</text><text x="40" y="255" font-family="Arial" font-size="9" fill="#666">• 安全敏感：选择带 TrustZone 的 Cortex-M33/M35P</text></svg>


<h3 id="核心特性">核心特性</h3>
<p><strong>Cortex-M 的统一优势：</strong></p>
<ul>
<li><strong>低功耗</strong>：多种睡眠模式，待机电流可低至微安级</li>
<li><strong>高能效</strong>：每 MHz 性能优异，适合电池供电设备</li>
<li><strong>易开发</strong>：统一的 CMSIS 标准，代码可移植性强</li>
<li><strong>低成本</strong>：芯片价格从￥5 到￥50 不等，生态成熟</li>
<li><strong>实时性</strong>：确定性中断响应，延迟可预测（通常&lt;12 个周期）</li>
</ul>
<h2 id="开发环境搭建实战步骤">开发环境搭建（实战步骤）</h2>
<h3 id="方案一stm32cubeide推荐新手">方案一：STM32CubeIDE（推荐新手）</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 1. 下载安装</span>
</span></span><span class="line"><span class="cl">wget https://github.com/STMicroelectronics/stm32cubeide/releases/download/v1.16.0/stm32cubeide_1.16.0_linux.x86_64.sh
</span></span><span class="line"><span class="cl">chmod +x stm32cubeide_1.16.0_linux.x86_64.sh
</span></span><span class="line"><span class="cl">./stm32cubeide_1.16.0_linux.x86_64.sh
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 2. 安装 ST-Link 驱动</span>
</span></span><span class="line"><span class="cl">sudo apt install stm32stlink
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 3. 配置 USB 权限</span>
</span></span><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s1">&#39;SUBSYSTEM==&#34;usb&#34;, ATTR{idVendor}==&#34;0483&#34;, ATTR{idProduct}==&#34;374b&#34;, MODE=&#34;0666&#34;&#39;</span> <span class="p">|</span> <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>  sudo tee /etc/udev/rules.d/49-stlink.rules
</span></span><span class="line"><span class="cl">sudo udevadm control --reload-rules
</span></span></code></pre></div><p><strong>优点：</strong></p>
<ul>
<li>图形化配置工具（CubeMX 集成）</li>
<li>自动生成初始化代码</li>
<li>免费、官方支持</li>
<li>支持调试和烧录</li>
</ul>
<h3 id="方案二vs-code--platformio推荐进阶">方案二：VS Code + PlatformIO（推荐进阶）</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 1. 安装 VS Code</span>
</span></span><span class="line"><span class="cl">sudo snap install code --classic
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 2. 安装 PlatformIO 插件</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 在 VS Code 扩展商店搜索 &#34;PlatformIO IDE&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 3. 创建项目</span>
</span></span><span class="line"><span class="cl">pio init --board nucleo_f407vg --project-dir ./my-embedded-project
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 4. 配置 platformio.ini</span>
</span></span><span class="line"><span class="cl">cat &gt; platformio.ini <span class="s">&lt;&lt; &#39;EOF&#39;
</span></span></span><span class="line"><span class="cl"><span class="s">[env:nucleo_f407vg]
</span></span></span><span class="line"><span class="cl"><span class="s">platform = ststm32
</span></span></span><span class="line"><span class="cl"><span class="s">board = nucleo_f407vg
</span></span></span><span class="line"><span class="cl"><span class="s">framework = stm32cube
</span></span></span><span class="line"><span class="cl"><span class="s">build_type = debug
</span></span></span><span class="line"><span class="cl"><span class="s">upload_protocol = stlink
</span></span></span><span class="line"><span class="cl"><span class="s">debug_tool = stlink
</span></span></span><span class="line"><span class="cl"><span class="s">monitor_speed = 115200
</span></span></span><span class="line"><span class="cl"><span class="s">EOF</span>
</span></span></code></pre></div><p><strong>优点：</strong></p>
<ul>
<li>跨平台（Linux/macOS/Windows）</li>
<li>强大的代码补全和导航</li>
<li>支持多种框架（STM32Cube、HAL、LL、mbed）</li>
<li>单元测试框架集成</li>
</ul>
<h3 id="方案三keil-mdk企业常用">方案三：Keil MDK（企业常用）</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 下载：https://www.keil.com/download/product/</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 注意：商业软件，需要许可证</span>
</span></span></code></pre></div><p><strong>适用场景：</strong></p>
<ul>
<li>企业项目开发</li>
<li>需要专业调试功能</li>
<li>团队已有 Keil 使用经验</li>
</ul>
<h2 id="gpio-编程实战点灯教程">GPIO 编程实战（点灯教程）</h2>
<h3 id="寄存器级别操作理解底层">寄存器级别操作（理解底层）</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&#34;stm32f4xx.h&#34;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="c1">// 点亮 LED（假设 LED 连接在 PA5）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="nf">LED_Init</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 1. 使能 GPIOA 时钟
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">RCC</span><span class="o">-&gt;</span><span class="n">AHB1ENR</span> <span class="o">|=</span> <span class="n">RCC_AHB1ENR_GPIOAEN</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 2. 配置 PA5 为推挽输出
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">GPIOA</span><span class="o">-&gt;</span><span class="n">MODER</span> <span class="o">&amp;=</span> <span class="o">~</span><span class="p">(</span><span class="mh">0x3</span> <span class="o">&lt;&lt;</span> <span class="p">(</span><span class="mi">5</span> <span class="o">*</span> <span class="mi">2</span><span class="p">));</span>  <span class="c1">// 清除模式位
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">GPIOA</span><span class="o">-&gt;</span><span class="n">MODER</span> <span class="o">|=</span> <span class="p">(</span><span class="mh">0x1</span> <span class="o">&lt;&lt;</span> <span class="p">(</span><span class="mi">5</span> <span class="o">*</span> <span class="mi">2</span><span class="p">));</span>   <span class="c1">// 设置为输出模式
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    
</span></span><span class="line"><span class="cl">    <span class="n">GPIOA</span><span class="o">-&gt;</span><span class="n">OTYPER</span> <span class="o">&amp;=</span> <span class="o">~</span><span class="p">(</span><span class="mh">0x1</span> <span class="o">&lt;&lt;</span> <span class="mi">5</span><span class="p">);</span>       <span class="c1">// 推挽输出
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">GPIOA</span><span class="o">-&gt;</span><span class="n">OSPEEDR</span> <span class="o">|=</span> <span class="p">(</span><span class="mh">0x3</span> <span class="o">&lt;&lt;</span> <span class="p">(</span><span class="mi">5</span> <span class="o">*</span> <span class="mi">2</span><span class="p">));</span> <span class="c1">// 高速模式
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">GPIOA</span><span class="o">-&gt;</span><span class="n">PUPDR</span> <span class="o">&amp;=</span> <span class="o">~</span><span class="p">(</span><span class="mh">0x3</span> <span class="o">&lt;&lt;</span> <span class="p">(</span><span class="mi">5</span> <span class="o">*</span> <span class="mi">2</span><span class="p">));</span>  <span class="c1">// 无上拉下拉
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">LED_On</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">GPIOA</span><span class="o">-&gt;</span><span class="n">BSRR</span> <span class="o">=</span> <span class="p">(</span><span class="mh">0x1</span> <span class="o">&lt;&lt;</span> <span class="mi">5</span><span class="p">);</span>  <span class="c1">// 置位 PA5
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">LED_Off</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">GPIOA</span><span class="o">-&gt;</span><span class="n">BSRR</span> <span class="o">=</span> <span class="p">(</span><span class="mh">0x1</span> <span class="o">&lt;&lt;</span> <span class="p">(</span><span class="mi">5</span> <span class="o">+</span> <span class="mi">16</span><span class="p">));</span>  <span class="c1">// 复位 PA5
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">LED_Toggle</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">GPIOA</span><span class="o">-&gt;</span><span class="n">ODR</span> <span class="o">^=</span> <span class="p">(</span><span class="mh">0x1</span> <span class="o">&lt;&lt;</span> <span class="mi">5</span><span class="p">);</span>  <span class="c1">// 翻转 PA5
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nf">LED_Init</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="k">while</span> <span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nf">LED_Toggle</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">        <span class="k">for</span> <span class="p">(</span><span class="k">volatile</span> <span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="mi">1000000</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">);</span>  <span class="c1">// 简单延时
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="hal-库方式推荐生产使用">HAL 库方式（推荐生产使用）</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&#34;main.h&#34;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">LED_Init</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">GPIO_InitTypeDef</span> <span class="n">GPIO_InitStruct</span> <span class="o">=</span> <span class="p">{</span><span class="mi">0</span><span class="p">};</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 使能 GPIOA 时钟
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">__HAL_RCC_GPIOA_CLK_ENABLE</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 配置 PA5
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">GPIO_InitStruct</span><span class="p">.</span><span class="n">Pin</span> <span class="o">=</span> <span class="n">GPIO_PIN_5</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">GPIO_InitStruct</span><span class="p">.</span><span class="n">Mode</span> <span class="o">=</span> <span class="n">GPIO_MODE_OUTPUT_PP</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">GPIO_InitStruct</span><span class="p">.</span><span class="n">Pull</span> <span class="o">=</span> <span class="n">GPIO_NOPULL</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">GPIO_InitStruct</span><span class="p">.</span><span class="n">Speed</span> <span class="o">=</span> <span class="n">GPIO_SPEED_FREQ_HIGH</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nf">HAL_GPIO_Init</span><span class="p">(</span><span class="n">GPIOA</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">GPIO_InitStruct</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">LED_Blink</span><span class="p">(</span><span class="kt">uint32_t</span> <span class="n">interval_ms</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">while</span> <span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nf">HAL_GPIO_TogglePin</span><span class="p">(</span><span class="n">GPIOA</span><span class="p">,</span> <span class="n">GPIO_PIN_5</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="nf">HAL_Delay</span><span class="p">(</span><span class="n">interval_ms</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="关键知识点">关键知识点</h3>
<table>
<thead>
<tr>
<th>寄存器</th>
<th>作用</th>
<th>配置示例</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>MODER</code></td>
<td>模式选择</td>
<td>00=输入，01=输出，10=复用，11=模拟</td>
</tr>
<tr>
<td><code>OTYPER</code></td>
<td>输出类型</td>
<td>0=推挽，1=开漏</td>
</tr>
<tr>
<td><code>OSPEEDR</code></td>
<td>速度等级</td>
<td>00=低速，01=中速，10=高速，11=超高速</td>
</tr>
<tr>
<td><code>PUPDR</code></td>
<td>上下拉</td>
<td>00=无，01=上拉，10=下拉，11=保留</td>
</tr>
<tr>
<td><code>BSRR</code></td>
<td>置位/复位</td>
<td>低 16 位置位，高 16 位复位</td>
</tr>
<tr>
<td><code>ODR</code></td>
<td>输出数据</td>
<td>直接读写引脚电平</td>
</tr>
</tbody>
</table>
<h2 id="中断系统详解">中断系统详解</h2>
<h3 id="nvic-配置">NVIC 配置</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&#34;stm32f4xx.h&#34;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">EXTI_Init</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 1. 使能 GPIOA 时钟
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">RCC</span><span class="o">-&gt;</span><span class="n">AHB1ENR</span> <span class="o">|=</span> <span class="n">RCC_AHB1ENR_GPIOAEN</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 2. 使能 SYSCFG 时钟
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">RCC</span><span class="o">-&gt;</span><span class="n">APB2ENR</span> <span class="o">|=</span> <span class="n">RCC_APB2ENR_SYSCFGEN</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 3. 配置 PA0 为 EXTI0
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">SYSCFG</span><span class="o">-&gt;</span><span class="n">EXTICR</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">&amp;=</span> <span class="o">~</span><span class="n">SYSCFG_EXTICR1_EXTI0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">SYSCFG</span><span class="o">-&gt;</span><span class="n">EXTICR</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">|=</span> <span class="n">SYSCFG_EXTICR1_EXTI0_PA</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 4. 配置上升沿触发
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">EXTI</span><span class="o">-&gt;</span><span class="n">RTSR</span> <span class="o">|=</span> <span class="n">EXTI_RTSR_TR0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 5. 使能 EXTI0 中断
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">EXTI</span><span class="o">-&gt;</span><span class="n">IMR</span> <span class="o">|=</span> <span class="n">EXTI_IMR_MR0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 6. 配置 NVIC
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">NVIC_SetPriority</span><span class="p">(</span><span class="n">EXTI0_IRQn</span><span class="p">,</span> <span class="mi">1</span><span class="p">);</span>  <span class="c1">// 优先级 1
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">NVIC_EnableIRQ</span><span class="p">(</span><span class="n">EXTI0_IRQn</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 中断服务函数
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="nf">EXTI0_IRQHandler</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">EXTI</span><span class="o">-&gt;</span><span class="n">PR</span> <span class="o">&amp;</span> <span class="n">EXTI_PR_PR0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// 处理中断
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="nf">LED_Toggle</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="c1">// 清除中断标志
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="n">EXTI</span><span class="o">-&gt;</span><span class="n">PR</span> <span class="o">|=</span> <span class="n">EXTI_PR_PR0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="中断优先级规则">中断优先级规则</h3>

<svg viewBox="0 0 500 200" xmlns="http://www.w3.org/2000/svg" style="max-width:100%;height:auto;background:#f8f9fa;border-radius:8px;padding:20px;"><text x="250" y="25" font-family="Arial" font-size="14" fill="#1976d2" font-weight="bold" text-anchor="middle">Cortex-M 中断优先级</text><rect x="50" y="40" width="400" height="30" fill="#ffcdd2" stroke="#d32f2f" stroke-width="2"/><text x="70" y="60" font-family="Arial" font-size="11" fill="#d32f2f" font-weight="bold">优先级 0（最高）</text><text x="250" y="60" font-family="Arial" font-size="11" fill="#333" text-anchor="middle">NMI、HardFault - 不可屏蔽，立即响应</text><rect x="50" y="75" width="400" height="30" fill="#ffe0b2" stroke="#ff9800" stroke-width="2"/><text x="70" y="95" font-family="Arial" font-size="11" fill="#ff9800" font-weight="bold">优先级 1-3（高）</text><text x="250" y="95" font-family="Arial" font-size="11" fill="#333" text-anchor="middle">紧急中断 - 电机保护、过流检测</text><rect x="50" y="110" width="400" height="30" fill="#fff9c4" stroke="#fbc02d" stroke-width="2"/><text x="70" y="130" font-family="Arial" font-size="11" fill="#fbc02d" font-weight="bold">优先级 4-11（中）</text><text x="250" y="130" font-family="Arial" font-size="11" fill="#333" text-anchor="middle">常规中断 - UART、SPI 通信</text><rect x="50" y="145" width="400" height="30" fill="#c8e6c9" stroke="#388e3c" stroke-width="2"/><text x="70" y="165" font-family="Arial" font-size="11" fill="#388e3c" font-weight="bold">优先级 12-15（最低）</text><text x="250" y="165" font-family="Arial" font-size="11" fill="#333" text-anchor="middle">低优先级 - LED 指示、非关键任务</text><text x="250" y="190" font-family="Arial" font-size="9" fill="#666" text-anchor="middle">数值越小优先级越高，0 为最高优先级</text></svg>


<h2 id="定时器编程pwm-输出">定时器编程（PWM 输出）</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&#34;stm32f4xx.h&#34;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">TIM_PWM_Init</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 1. 使能时钟
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">RCC</span><span class="o">-&gt;</span><span class="n">AHB1ENR</span> <span class="o">|=</span> <span class="n">RCC_AHB1ENR_GPIOAEN</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">RCC</span><span class="o">-&gt;</span><span class="n">APB1ENR</span> <span class="o">|=</span> <span class="n">RCC_APB1ENR_TIM2EN</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 2. 配置 PA0 为 TIM2_CH1 复用功能
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">GPIOA</span><span class="o">-&gt;</span><span class="n">MODER</span> <span class="o">&amp;=</span> <span class="o">~</span><span class="p">(</span><span class="mh">0x3</span> <span class="o">&lt;&lt;</span> <span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">GPIOA</span><span class="o">-&gt;</span><span class="n">MODER</span> <span class="o">|=</span> <span class="p">(</span><span class="mh">0x2</span> <span class="o">&lt;&lt;</span> <span class="mi">0</span><span class="p">);</span>  <span class="c1">// 复用模式
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">GPIOA</span><span class="o">-&gt;</span><span class="n">AFR</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">&amp;=</span> <span class="o">~</span><span class="p">(</span><span class="mh">0xF</span> <span class="o">&lt;&lt;</span> <span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">GPIOA</span><span class="o">-&gt;</span><span class="n">AFR</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">|=</span> <span class="p">(</span><span class="mh">0x1</span> <span class="o">&lt;&lt;</span> <span class="mi">0</span><span class="p">);</span> <span class="c1">// AF1 = TIM2
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    
</span></span><span class="line"><span class="cl">    <span class="c1">// 3. 配置定时器
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">TIM2</span><span class="o">-&gt;</span><span class="n">PSC</span> <span class="o">=</span> <span class="mi">83</span><span class="p">;</span>              <span class="c1">// 预分频 84MHz/84 = 1MHz
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">TIM2</span><span class="o">-&gt;</span><span class="n">ARR</span> <span class="o">=</span> <span class="mi">999</span><span class="p">;</span>             <span class="c1">// 自动重装载值，1kHz PWM
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">TIM2</span><span class="o">-&gt;</span><span class="n">CCR1</span> <span class="o">=</span> <span class="mi">500</span><span class="p">;</span>            <span class="c1">// 50% 占空比
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">TIM2</span><span class="o">-&gt;</span><span class="n">CCMR1</span> <span class="o">|=</span> <span class="p">(</span><span class="mh">0x6</span> <span class="o">&lt;&lt;</span> <span class="mi">4</span><span class="p">);</span>   <span class="c1">// PWM 模式 1
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">TIM2</span><span class="o">-&gt;</span><span class="n">CCMR1</span> <span class="o">|=</span> <span class="p">(</span><span class="mh">0x1</span> <span class="o">&lt;&lt;</span> <span class="mi">3</span><span class="p">);</span>   <span class="c1">// 预装载使能
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">TIM2</span><span class="o">-&gt;</span><span class="n">CR1</span> <span class="o">|=</span> <span class="n">TIM_CR1_ARPE</span><span class="p">;</span>   <span class="c1">// 自动重装载预装载使能
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">TIM2</span><span class="o">-&gt;</span><span class="n">CR1</span> <span class="o">|=</span> <span class="n">TIM_CR1_CEN</span><span class="p">;</span>    <span class="c1">// 计数器使能
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    
</span></span><span class="line"><span class="cl">    <span class="c1">// 4. 使能通道 1 输出
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">TIM2</span><span class="o">-&gt;</span><span class="n">CCER</span> <span class="o">|=</span> <span class="n">TIM_CCER_CC1E</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">PWM_SetDutyCycle</span><span class="p">(</span><span class="kt">float</span> <span class="n">duty</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// duty: 0.0 - 100.0
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="kt">uint32_t</span> <span class="n">ccr</span> <span class="o">=</span> <span class="p">(</span><span class="kt">uint32_t</span><span class="p">)(</span><span class="n">duty</span> <span class="o">*</span> <span class="n">TIM2</span><span class="o">-&gt;</span><span class="n">ARR</span> <span class="o">/</span> <span class="mf">100.0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">TIM2</span><span class="o">-&gt;</span><span class="n">CCR1</span> <span class="o">=</span> <span class="n">ccr</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h2 id="串口通信uart">串口通信（UART）</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&#34;stm32f4xx.h&#34;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="c1">// 重定向 printf 到串口
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">int</span> <span class="nf">__io_putchar</span><span class="p">(</span><span class="kt">int</span> <span class="n">ch</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">while</span> <span class="p">(</span><span class="o">!</span><span class="p">(</span><span class="n">USART2</span><span class="o">-&gt;</span><span class="n">SR</span> <span class="o">&amp;</span> <span class="n">USART_SR_TXE</span><span class="p">));</span>  <span class="c1">// 等待发送缓冲区空
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">USART2</span><span class="o">-&gt;</span><span class="n">DR</span> <span class="o">=</span> <span class="n">ch</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">ch</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">UART_Init</span><span class="p">(</span><span class="kt">uint32_t</span> <span class="n">baudrate</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 1. 使能时钟
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">RCC</span><span class="o">-&gt;</span><span class="n">AHB1ENR</span> <span class="o">|=</span> <span class="n">RCC_AHB1ENR_GPIOAEN</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">RCC</span><span class="o">-&gt;</span><span class="n">APB1ENR</span> <span class="o">|=</span> <span class="n">RCC_APB1ENR_USART2EN</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 2. 配置 PA2(TX)、PA3(RX) 为复用功能
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">GPIOA</span><span class="o">-&gt;</span><span class="n">MODER</span> <span class="o">&amp;=</span> <span class="o">~</span><span class="p">(</span><span class="mh">0xF</span> <span class="o">&lt;&lt;</span> <span class="mi">4</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">GPIOA</span><span class="o">-&gt;</span><span class="n">MODER</span> <span class="o">|=</span> <span class="p">(</span><span class="mh">0xA</span> <span class="o">&lt;&lt;</span> <span class="mi">4</span><span class="p">);</span>  <span class="c1">// 复用模式
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">GPIOA</span><span class="o">-&gt;</span><span class="n">AFR</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">&amp;=</span> <span class="o">~</span><span class="p">(</span><span class="mh">0xFF</span> <span class="o">&lt;&lt;</span> <span class="mi">8</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">GPIOA</span><span class="o">-&gt;</span><span class="n">AFR</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">|=</span> <span class="p">(</span><span class="mh">0x77</span> <span class="o">&lt;&lt;</span> <span class="mi">8</span><span class="p">);</span> <span class="c1">// AF7 = USART2
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    
</span></span><span class="line"><span class="cl">    <span class="c1">// 3. 配置串口
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="kt">uint32_t</span> <span class="n">usartdiv</span> <span class="o">=</span> <span class="mi">16000000</span> <span class="o">/</span> <span class="n">baudrate</span><span class="p">;</span>  <span class="c1">// 假设 APB1=16MHz
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">USART2</span><span class="o">-&gt;</span><span class="n">BRR</span> <span class="o">=</span> <span class="n">usartdiv</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">USART2</span><span class="o">-&gt;</span><span class="n">CR1</span> <span class="o">=</span> <span class="n">USART_CR1_TE</span> <span class="o">|</span> <span class="n">USART_CR1_RE</span><span class="p">;</span>  <span class="c1">// 使能收发
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">USART2</span><span class="o">-&gt;</span><span class="n">CR1</span> <span class="o">|=</span> <span class="n">USART_CR1_UE</span><span class="p">;</span>  <span class="c1">// 使能串口
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">UART_SendString</span><span class="p">(</span><span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">str</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">while</span> <span class="p">(</span><span class="o">*</span><span class="n">str</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nf">__io_putchar</span><span class="p">(</span><span class="o">*</span><span class="n">str</span><span class="o">++</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nf">UART_Init</span><span class="p">(</span><span class="mi">115200</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nf">printf</span><span class="p">(</span><span class="s">&#34;Hello, Cortex-M!</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="k">while</span> <span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nf">printf</span><span class="p">(</span><span class="s">&#34;System running...</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="nf">HAL_Delay</span><span class="p">(</span><span class="mi">1000</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h2 id="调试技巧与常见问题">调试技巧与常见问题</h2>
<h3 id="使用-openocd-调试">使用 OpenOCD 调试</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 1. 安装 OpenOCD</span>
</span></span><span class="line"><span class="cl">sudo apt install openocd
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 2. 启动调试服务器</span>
</span></span><span class="line"><span class="cl">openocd -f interface/stlink.cfg -f target/stm32f4x.cfg
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 3. 使用 GDB 连接</span>
</span></span><span class="line"><span class="cl">arm-none-eabi-gdb build/firmware.elf
</span></span><span class="line"><span class="cl"><span class="o">(</span>gdb<span class="o">)</span> target remote :3333
</span></span><span class="line"><span class="cl"><span class="o">(</span>gdb<span class="o">)</span> load
</span></span><span class="line"><span class="cl"><span class="o">(</span>gdb<span class="o">)</span> <span class="k">continue</span>
</span></span></code></pre></div><h3 id="常见问题排查">常见问题排查</h3>
<table>
<thead>
<tr>
<th>问题</th>
<th>可能原因</th>
<th>解决方案</th>
</tr>
</thead>
<tbody>
<tr>
<td>程序无法烧录</td>
<td>芯片锁死/Boot 引脚错误</td>
<td>检查 BOOT0/BOOT1，使用 SWD 恢复</td>
</tr>
<tr>
<td>系统频繁复位</td>
<td>看门狗未喂/电源不稳</td>
<td>检查 WDG 配置，测量供电电压</td>
</tr>
<tr>
<td>中断不触发</td>
<td>NVIC 未使能/优先级错误</td>
<td>检查 NVIC_EnableIRQ() 和优先级配置</td>
</tr>
<tr>
<td>串口乱码</td>
<td>波特率不匹配/时钟配置错误</td>
<td>检查系统时钟和 BRR 计算</td>
</tr>
<tr>
<td>GPIO 无输出</td>
<td>时钟未使能/模式配置错误</td>
<td>检查 RCC 和 MODER 寄存器</td>
</tr>
</tbody>
</table>
<h3 id="使用逻辑分析仪调试">使用逻辑分析仪调试</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># Saleae Logic 2 配置</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 1. 连接信号：PA0(PWM), PA2(TX), PA3(RX)</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 2. 设置采样率：24 MS/s</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 3. 触发条件：PA0 上升沿</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 4. 解码协议：UART 115200 8N1</span>
</span></span></code></pre></div><h2 id="性能优化技巧">性能优化技巧</h2>
<h3 id="代码优化">代码优化</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// ❌ 低效写法
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="mi">100</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">GPIOA</span><span class="o">-&gt;</span><span class="n">ODR</span> <span class="o">|=</span> <span class="p">(</span><span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="mi">5</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">GPIOA</span><span class="o">-&gt;</span><span class="n">ODR</span> <span class="o">&amp;=</span> <span class="o">~</span><span class="p">(</span><span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="mi">5</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// ✅ 高效写法（使用 BSRR）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="mi">100</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">GPIOA</span><span class="o">-&gt;</span><span class="n">BSRR</span> <span class="o">=</span> <span class="p">(</span><span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="mi">5</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">GPIOA</span><span class="o">-&gt;</span><span class="n">BSRR</span> <span class="o">=</span> <span class="p">(</span><span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="p">(</span><span class="mi">5</span> <span class="o">+</span> <span class="mi">16</span><span class="p">));</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// ✅ 极致优化（直接写 ODR）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="mi">100</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">GPIOA</span><span class="o">-&gt;</span><span class="n">ODR</span> <span class="o">^=</span> <span class="p">(</span><span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="mi">5</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="内存优化">内存优化</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// 使用 __attribute__ 控制变量位置
</span></span></span><span class="line"><span class="cl"><span class="c1">// 将变量放入 CCM RAM（STM32F4 专用，访问速度更快）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">uint32_t</span> <span class="n">fast_buffer</span><span class="p">[</span><span class="mi">256</span><span class="p">]</span> <span class="nf">__attribute__</span><span class="p">((</span><span class="nf">section</span><span class="p">(</span><span class="s">&#34;.ccmram&#34;</span><span class="p">)));</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 将常量放入 Flash
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">const</span> <span class="kt">uint8_t</span> <span class="n">lookup_table</span><span class="p">[]</span> <span class="nf">__attribute__</span><span class="p">((</span><span class="nf">section</span><span class="p">(</span><span class="s">&#34;.rodata&#34;</span><span class="p">)))</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="mh">0x01</span><span class="p">,</span> <span class="mh">0x02</span><span class="p">,</span> <span class="mh">0x04</span><span class="p">,</span> <span class="mh">0x08</span><span class="p">,</span> <span class="mh">0x10</span><span class="p">,</span> <span class="mh">0x20</span><span class="p">,</span> <span class="mh">0x40</span><span class="p">,</span> <span class="mh">0x80</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 禁止编译器优化（用于 volatile 硬件寄存器）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">volatile</span> <span class="kt">uint32_t</span> <span class="o">*</span><span class="k">const</span> <span class="n">peripheral_reg</span> <span class="o">=</span> <span class="p">(</span><span class="kt">uint32_t</span> <span class="o">*</span><span class="p">)</span><span class="mh">0x40021000</span><span class="p">;</span>
</span></span></code></pre></div><h3 id="功耗优化">功耗优化</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">Enter_Sleep_Mode</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 1. 配置 NVIC 使能唤醒中断
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">NVIC_EnableIRQ</span><span class="p">(</span><span class="n">EXTI0_IRQn</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 2. 设置 SysTick 为低功耗模式
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">SysTick</span><span class="o">-&gt;</span><span class="n">CTRL</span> <span class="o">&amp;=</span> <span class="o">~</span><span class="n">SysTick_CTRL_CLKSOURCE_Msk</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 3. 进入睡眠模式（等待中断）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">__WFI</span><span class="p">();</span>  <span class="c1">// Wait For Interrupt
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">Enter_Stop_Mode</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 1. 使能 PWR 时钟
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">RCC</span><span class="o">-&gt;</span><span class="n">APB1ENR</span> <span class="o">|=</span> <span class="n">RCC_APB1ENR_PWREN</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 2. 配置电压调节器为低功耗模式
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">PWR</span><span class="o">-&gt;</span><span class="n">CR</span> <span class="o">|=</span> <span class="n">PWR_CR_LPDS</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 3. 设置 SLEEPDEEP 位
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">SCB</span><span class="o">-&gt;</span><span class="n">SCR</span> <span class="o">|=</span> <span class="n">SCB_SCR_SLEEPDEEP_Msk</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 4. 进入停止模式
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">__WFI</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 5. 唤醒后恢复系统时钟
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">SystemInit</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h2 id="实战项目智能温湿度传感器">实战项目：智能温湿度传感器</h2>
<h3 id="硬件清单">硬件清单</h3>
<ul>
<li>STM32F103C8T6（Blue Pill） × 1</li>
<li>DHT11 温湿度传感器 × 1</li>
<li>0.96&quot; OLED 显示屏（I2C） × 1</li>
<li>3.3V LDO 稳压模块 × 1</li>
<li>面包板及杜邦线若干</li>
</ul>
<h3 id="接线图">接线图</h3>

<svg viewBox="0 0 500 220" xmlns="http://www.w3.org/2000/svg" style="max-width:100%;height:auto;background:#f8f9fa;border-radius:8px;padding:20px;"><rect x="50" y="50" width="120" height="100" fill="#1976d2" rx="5"/><text x="110" y="80" font-family="Arial" font-size="12" fill="#fff" text-anchor="middle" font-weight="bold">STM32F103</text><text x="110" y="95" font-family="Arial" font-size="9" fill="#fff" text-anchor="middle">Blue Pill</text><text x="175" y="70" font-family="Arial" font-size="9" fill="#333">3.3V</text><text x="175" y="90" font-family="Arial" font-size="9" fill="#333">GND</text><text x="175" y="110" font-family="Arial" font-size="9" fill="#333">PB6(SCL)</text><text x="175" y="130" font-family="Arial" font-size="9" fill="#333">PB7(SDA)</text><text x="175" y="150" font-family="Arial" font-size="9" fill="#333">PA0(DATA)</text><rect x="320" y="60" width="80" height="40" fill="#388e3c" rx="5"/><text x="360" y="85" font-family="Arial" font-size="11" fill="#fff" text-anchor="middle" font-weight="bold">DHT11</text><rect x="320" y="130" width="80" height="50" fill="#666" rx="5"/><text x="360" y="155" font-family="Arial" font-size="10" fill="#fff" text-anchor="middle">0.96" OLED</text><text x="360" y="168" font-family="Arial" font-size="8" fill="#fff" text-anchor="middle">(I2C)</text><line x1="170" y1="65" x2="320" y2="70" stroke="#d32f2f" stroke-width="2"/><line x1="170" y1="85" x2="320" y2="90" stroke="#333" stroke-width="2"/><line x1="170" y1="105" x2="320" y2="140" stroke="#1976d2" stroke-width="2"/><line x1="170" y1="125" x2="320" y2="150" stroke="#1976d2" stroke-width="2"/><line x1="170" y1="145" x2="320" y2="80" stroke="#ff9800" stroke-width="2"/><text x="230" y="62" font-family="Arial" font-size="8" fill="#d32f2f">3.3V</text><text x="230" y="92" font-family="Arial" font-size="8" fill="#333">GND</text><text x="240" y="130" font-family="Arial" font-size="8" fill="#1976d2">SCL/SDA</text><text x="240" y="150" font-family="Arial" font-size="8" fill="#ff9800">DATA</text><text x="250" y="170" font-family="Arial" font-size="8" fill="#666">* SCL/SDA 需 4.7k 上拉电阻</text><text x="250" y="185" font-family="Arial" font-size="8" fill="#666">* DHT11 DATA 需 10k 上拉电阻</text><text x="250" y="200" font-family="Arial" font-size="8" fill="#666">* 电源加 100nF 去耦电容</text></svg>


<h3 id="核心代码">核心代码</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&#34;stm32f1xx.h&#34;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&#34;dht11.h&#34;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&#34;ssd1306.h&#34;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nf">SystemInit</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="nf">UART_Init</span><span class="p">(</span><span class="mi">115200</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nf">I2C_Init</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="nf">SSD1306_Init</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="nf">DHT11_Init</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="nf">printf</span><span class="p">(</span><span class="s">&#34;Smart Sensor Starting...</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nf">SSD1306_Clear</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="nf">SSD1306_DrawString</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="s">&#34;Smart Sensor&#34;</span><span class="p">,</span> <span class="n">Font_11x18</span><span class="p">,</span> <span class="n">White</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nf">SSD1306_UpdateScreen</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="k">while</span> <span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">DHT11_Data_TypeDef</span> <span class="n">data</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="nf">DHT11_ReadData</span><span class="p">(</span><span class="o">&amp;</span><span class="n">data</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">data</span><span class="p">.</span><span class="n">Status</span> <span class="o">==</span> <span class="n">DHT11_OK</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nf">printf</span><span class="p">(</span><span class="s">&#34;Temp: %d.%d°C, Humi: %d.%d%%</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                   <span class="n">data</span><span class="p">.</span><span class="n">Temperature</span><span class="p">,</span> <span class="n">data</span><span class="p">.</span><span class="n">Dec_Temperature</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                   <span class="n">data</span><span class="p">.</span><span class="n">Humidity</span><span class="p">,</span> <span class="n">data</span><span class="p">.</span><span class="n">Dec_Humidity</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            
</span></span><span class="line"><span class="cl">            <span class="kt">char</span> <span class="n">buffer</span><span class="p">[</span><span class="mi">32</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">            <span class="nf">sprintf</span><span class="p">(</span><span class="n">buffer</span><span class="p">,</span> <span class="s">&#34;T: %d.%dC&#34;</span><span class="p">,</span> <span class="n">data</span><span class="p">.</span><span class="n">Temperature</span><span class="p">,</span> <span class="n">data</span><span class="p">.</span><span class="n">Dec_Temperature</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="nf">SSD1306_DrawString</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">30</span><span class="p">,</span> <span class="n">buffer</span><span class="p">,</span> <span class="n">Font_11x18</span><span class="p">,</span> <span class="n">White</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            
</span></span><span class="line"><span class="cl">            <span class="nf">sprintf</span><span class="p">(</span><span class="n">buffer</span><span class="p">,</span> <span class="s">&#34;H: %d.%d%%&#34;</span><span class="p">,</span> <span class="n">data</span><span class="p">.</span><span class="n">Humidity</span><span class="p">,</span> <span class="n">data</span><span class="p">.</span><span class="n">Dec_Humidity</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="nf">SSD1306_DrawString</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">50</span><span class="p">,</span> <span class="n">buffer</span><span class="p">,</span> <span class="n">Font_11x18</span><span class="p">,</span> <span class="n">White</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="nf">SSD1306_UpdateScreen</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nf">printf</span><span class="p">(</span><span class="s">&#34;DHT11 Read Error</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="nf">HAL_Delay</span><span class="p">(</span><span class="mi">2000</span><span class="p">);</span>  <span class="c1">// DHT11 采样率&lt;1Hz
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h2 id="学习资源推荐">学习资源推荐</h2>
<h3 id="官方文档">官方文档</h3>
<ul>
<li><a href="https://developer.arm.com/documentation/">ARM Cortex-M Technical Reference Manuals</a></li>
<li><a href="https://www.st.com/en/microcontrollers-microprocessors/stm32-32-bit-arm-cortex-mcus.html">STM32 Reference Manuals</a></li>
<li><a href="https://arm-software.github.io/CMSIS_6/latest/">CMSIS 文档</a></li>
</ul>
<h3 id="在线课程">在线课程</h3>
<ol>
<li><a href="https://www.udemy.com/course/embedded-system-programming-on-arm-cortex-m3m4/">Embedded Systems Programming on ARM Cortex-M3/M4</a> - Udemy 高评分课程</li>
<li><a href="https://www.coursera.org/learn/embedded-systems">ARM Cortex-M Architecture</a> - Coursera 专项课程</li>
<li><a href="https://www.bilibili.com/video/BV1Rb411b7Lg">STM32CubeMX 实战教程</a> - B 站中文教程</li>
</ol>
<h3 id="开源项目">开源项目</h3>
<ul>
<li><a href="https://github.com/FreeRTOS/FreeRTOS">FreeRTOS</a> - 实时操作系统</li>
<li><a href="https://github.com/ChibiOS/ChibiOS">ChibiOS</a> - 轻量级 RTOS</li>
<li><a href="https://github.com/libopencm3/libopencm3">libopencm3</a> - 开源 STM32 库</li>
<li><a href="https://github.com/platformio/platformio-examples">PlatformIO Examples</a> - 丰富的示例代码</li>
</ul>
<h3 id="开发板推荐">开发板推荐</h3>
<table>
<thead>
<tr>
<th>型号</th>
<th>价格</th>
<th>特点</th>
<th>适合人群</th>
</tr>
</thead>
<tbody>
<tr>
<td>STM32F103C8T6 (Blue Pill)</td>
<td>￥15</td>
<td>最便宜，社区活跃</td>
<td>入门学习</td>
</tr>
<tr>
<td>STM32F407VET6 (Black Pill)</td>
<td>￥35</td>
<td>性能强，外设多</td>
<td>进阶开发</td>
</tr>
<tr>
<td>NUCLEO-F401RE</td>
<td>￥80</td>
<td>官方开发板，带 ST-Link</td>
<td>企业开发</td>
</tr>
<tr>
<td>STM32F746G-DISCO</td>
<td>￥200</td>
<td>带 LCD 触摸屏</td>
<td>高端应用</td>
</tr>
</tbody>
</table>
<h2 id="总结">总结</h2>
<p>ARM Cortex-M 微控制器编程是嵌入式开发的核心技能之一。通过本文的学习，你应该掌握了：</p>
<ol>
<li><strong>Cortex-M 家族特点</strong> - 根据需求选择合适的 MCU</li>
<li><strong>开发环境搭建</strong> - 选择适合的 IDE 和工具链</li>
<li><strong>GPIO 编程</strong> - 从寄存器到 HAL 库的完整理解</li>
<li><strong>中断系统</strong> - 掌握 NVIC 配置和中断服务函数</li>
<li><strong>定时器与 PWM</strong> - 实现精确的时序控制</li>
<li><strong>串口通信</strong> - 调试和数据传输的基础</li>
<li><strong>调试技巧</strong> - 快速定位和解决问题</li>
<li><strong>性能优化</strong> - 代码、内存、功耗的综合优化</li>
</ol>
<p><strong>下一步建议：</strong></p>
<ul>
<li>动手做一个完整的项目（如智能传感器、平衡小车）</li>
<li>学习 RTOS（FreeRTOS 或 RT-Thread）</li>
<li>深入理解 ARM 架构（异常处理、内存管理单元）</li>
<li>参与开源项目，阅读优秀代码</li>
</ul>
<hr>
<p><strong>最后更新：</strong> 2026-04-05<br>
<strong>作者：</strong> 小 Y</p>
<p><em>本文基于网络公开资料整理，结合嵌入式开发实践经验编写。欢迎在评论区分享你的项目经验和问题！</em></p>
]]></content:encoded></item><item><title>嵌入式开发：Embedded Machine Learning TinyML 完整教程</title><link>https://tech-snippets.xyz/posts/embedded-machine-learning-tinyml/</link><pubDate>Sat, 04 Apr 2026 03:00:00 +0800</pubDate><guid>https://tech-snippets.xyz/posts/embedded-machine-learning-tinyml/</guid><description>2026 年 TinyML 嵌入式机器学习完整教程：从理论基础到 Arduino 实战部署，涵盖模型训练、优化、转换和边缘推理全流程</description><content:encoded><![CDATA[<h2 id="引言">引言</h2>
<p>TinyML（Tiny Machine Learning）是嵌入式系统与机器学习的交叉领域，专注于在微控制器等低功耗边缘设备上部署机器学习模型。随着 IoT 设备的普及和边缘计算需求的增长，TinyML 正在成为 2026 年嵌入式开发的核心技能之一。</p>
<p>与传统云端机器学习不同，TinyML 将 AI 推理能力带到设备端，实现<strong>低延迟、低功耗、离线运行</strong>的智能功能。本文将带你从理论基础到实战部署，完整掌握 TinyML 开发流程。</p>
<h2 id="什么是-tinyml">什么是 TinyML？</h2>
<h3 id="定义与特点">定义与特点</h3>
<p>TinyML 是机器学习的一个子集，专注于将训练好的模型部署到微控制器和其他低功耗边缘设备上。其核心特点包括：</p>
<table>
<thead>
<tr>
<th>特性</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>超低功耗</strong></td>
<td>通常在毫瓦级功率预算下运行</td>
</tr>
<tr>
<td><strong>小内存占用</strong></td>
<td>模型大小通常在几 KB 到几 MB</td>
</tr>
<tr>
<td><strong>低延迟推理</strong></td>
<td>本地推理，无需云端通信</td>
</tr>
<tr>
<td><strong>离线运行</strong></td>
<td>不依赖网络连接，隐私性更好</td>
</tr>
<tr>
<td><strong>低成本</strong></td>
<td>运行在几美元的微控制器上</td>
</tr>
</tbody>
</table>
<h3 id="tinyml-vs-传统机器学习">TinyML vs 传统机器学习</h3>
<pre tabindex="0"><code>传统 ML 流程：
传感器 → 数据上传 → 云端服务器 → 模型推理 → 结果返回 → 设备执行
        ↑________________________________↓
        高延迟、高带宽、隐私风险

TinyML 流程：
传感器 → 本地推理 → 设备执行
        ↑___________↓
        低延迟、零带宽、隐私安全
</code></pre><h3 id="典型应用场景">典型应用场景</h3>
<ul>
<li><strong>智能穿戴设备</strong>：手势识别、活动分类、健康监测</li>
<li><strong>工业 IoT</strong>：预测性维护、异常检测、振动分析</li>
<li><strong>智能家居</strong>：语音唤醒词检测、存在感知、能耗优化</li>
<li><strong>农业传感器</strong>：病虫害识别、土壤分析、灌溉决策</li>
<li><strong>消费电子</strong>：降噪耳机、智能相机、手势控制</li>
</ul>
<h2 id="tinyml-开发全流程">TinyML 开发全流程</h2>
<h3 id="阶段一模型开发与训练">阶段一：模型开发与训练</h3>
<h4 id="1-数据收集与预处理">1. 数据收集与预处理</h4>
<p>TinyML 模型的质量直接取决于训练数据。数据来源通常包括：</p>
<ul>
<li><strong>传感器数据</strong>：加速度计、陀螺仪、麦克风、温度传感器等</li>
<li><strong>公开数据集</strong>：Edge Impulse、TensorFlow Datasets</li>
<li><strong>合成数据</strong>：使用仿真工具生成</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="c1"># 示例：使用 Python 生成模拟传感器数据</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">numpy</span> <span class="k">as</span> <span class="nn">np</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">pandas</span> <span class="k">as</span> <span class="nn">pd</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 生成 5000 个样本的加速度计数据</span>
</span></span><span class="line"><span class="cl"><span class="n">SAMPLES</span> <span class="o">=</span> <span class="mi">5000</span>
</span></span><span class="line"><span class="cl"><span class="n">np</span><span class="o">.</span><span class="n">random</span><span class="o">.</span><span class="n">seed</span><span class="p">(</span><span class="mi">42</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 模拟三轴加速度计数据 (单位：g)</span>
</span></span><span class="line"><span class="cl"><span class="n">ax</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">random</span><span class="o">.</span><span class="n">normal</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="n">SAMPLES</span><span class="p">)</span>  <span class="c1"># X 轴</span>
</span></span><span class="line"><span class="cl"><span class="n">ay</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">random</span><span class="o">.</span><span class="n">normal</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="n">SAMPLES</span><span class="p">)</span>  <span class="c1"># Y 轴</span>
</span></span><span class="line"><span class="cl"><span class="n">az</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">random</span><span class="o">.</span><span class="n">normal</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mf">0.5</span><span class="p">,</span> <span class="n">SAMPLES</span><span class="p">)</span>  <span class="c1"># Z 轴 (重力方向)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 添加标签：0=静止，1=行走，2=跑步</span>
</span></span><span class="line"><span class="cl"><span class="n">labels</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">random</span><span class="o">.</span><span class="n">randint</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="n">SAMPLES</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 创建 DataFrame</span>
</span></span><span class="line"><span class="cl"><span class="n">df</span> <span class="o">=</span> <span class="n">pd</span><span class="o">.</span><span class="n">DataFrame</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">    <span class="s1">&#39;ax&#39;</span><span class="p">:</span> <span class="n">ax</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s1">&#39;ay&#39;</span><span class="p">:</span> <span class="n">ay</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s1">&#39;az&#39;</span><span class="p">:</span> <span class="n">az</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s1">&#39;label&#39;</span><span class="p">:</span> <span class="n">labels</span>
</span></span><span class="line"><span class="cl"><span class="p">})</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 数据标准化</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">sklearn.preprocessing</span> <span class="kn">import</span> <span class="n">StandardScaler</span>
</span></span><span class="line"><span class="cl"><span class="n">scaler</span> <span class="o">=</span> <span class="n">StandardScaler</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="n">df</span><span class="p">[[</span><span class="s1">&#39;ax&#39;</span><span class="p">,</span> <span class="s1">&#39;ay&#39;</span><span class="p">,</span> <span class="s1">&#39;az&#39;</span><span class="p">]]</span> <span class="o">=</span> <span class="n">scaler</span><span class="o">.</span><span class="n">fit_transform</span><span class="p">(</span><span class="n">df</span><span class="p">[[</span><span class="s1">&#39;ax&#39;</span><span class="p">,</span> <span class="s1">&#39;ay&#39;</span><span class="p">,</span> <span class="s1">&#39;az&#39;</span><span class="p">]])</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;数据集形状：</span><span class="si">{</span><span class="n">df</span><span class="o">.</span><span class="n">shape</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="n">df</span><span class="o">.</span><span class="n">head</span><span class="p">())</span>
</span></span></code></pre></div><h4 id="2-数据集划分">2. 数据集划分</h4>
<p>采用交叉验证方法，将数据集划分为三部分：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="c1"># 数据集划分比例</span>
</span></span><span class="line"><span class="cl"><span class="n">TRAIN_SPLIT</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="mf">0.6</span> <span class="o">*</span> <span class="n">SAMPLES</span><span class="p">)</span>    <span class="c1"># 60% 训练集</span>
</span></span><span class="line"><span class="cl"><span class="n">TEST_SPLIT</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="mf">0.8</span> <span class="o">*</span> <span class="n">SAMPLES</span><span class="p">)</span>     <span class="c1"># 20% 验证集</span>
</span></span><span class="line"><span class="cl">                                    <span class="c1"># 20% 测试集</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">x_train</span> <span class="o">=</span> <span class="n">df</span><span class="o">.</span><span class="n">iloc</span><span class="p">[:</span><span class="n">TRAIN_SPLIT</span><span class="p">][[</span><span class="s1">&#39;ax&#39;</span><span class="p">,</span> <span class="s1">&#39;ay&#39;</span><span class="p">,</span> <span class="s1">&#39;az&#39;</span><span class="p">]]</span>
</span></span><span class="line"><span class="cl"><span class="n">y_train</span> <span class="o">=</span> <span class="n">df</span><span class="o">.</span><span class="n">iloc</span><span class="p">[:</span><span class="n">TRAIN_SPLIT</span><span class="p">][</span><span class="s1">&#39;label&#39;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">x_validate</span> <span class="o">=</span> <span class="n">df</span><span class="o">.</span><span class="n">iloc</span><span class="p">[</span><span class="n">TRAIN_SPLIT</span><span class="p">:</span><span class="n">TEST_SPLIT</span><span class="p">][[</span><span class="s1">&#39;ax&#39;</span><span class="p">,</span> <span class="s1">&#39;ay&#39;</span><span class="p">,</span> <span class="s1">&#39;az&#39;</span><span class="p">]]</span>
</span></span><span class="line"><span class="cl"><span class="n">y_validate</span> <span class="o">=</span> <span class="n">df</span><span class="o">.</span><span class="n">iloc</span><span class="p">[</span><span class="n">TRAIN_SPLIT</span><span class="p">:</span><span class="n">TEST_SPLIT</span><span class="p">][</span><span class="s1">&#39;label&#39;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">x_test</span> <span class="o">=</span> <span class="n">df</span><span class="o">.</span><span class="n">iloc</span><span class="p">[</span><span class="n">TEST_SPLIT</span><span class="p">:][[</span><span class="s1">&#39;ax&#39;</span><span class="p">,</span> <span class="s1">&#39;ay&#39;</span><span class="p">,</span> <span class="s1">&#39;az&#39;</span><span class="p">]]</span>
</span></span><span class="line"><span class="cl"><span class="n">y_test</span> <span class="o">=</span> <span class="n">df</span><span class="o">.</span><span class="n">iloc</span><span class="p">[</span><span class="n">TEST_SPLIT</span><span class="p">:][</span><span class="s1">&#39;label&#39;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;训练集：</span><span class="si">{</span><span class="nb">len</span><span class="p">(</span><span class="n">x_train</span><span class="p">)</span><span class="si">}</span><span class="s2">, 验证集：</span><span class="si">{</span><span class="nb">len</span><span class="p">(</span><span class="n">x_validate</span><span class="p">)</span><span class="si">}</span><span class="s2">, 测试集：</span><span class="si">{</span><span class="nb">len</span><span class="p">(</span><span class="n">x_test</span><span class="p">)</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span></code></pre></div><h4 id="3-构建神经网络模型">3. 构建神经网络模型</h4>
<p>使用 TensorFlow/Keras 构建适合微控制器的轻量级模型：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">tensorflow</span> <span class="k">as</span> <span class="nn">tf</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">tensorflow</span> <span class="kn">import</span> <span class="n">keras</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">tensorflow.keras</span> <span class="kn">import</span> <span class="n">layers</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 构建轻量级 Sequential 模型</span>
</span></span><span class="line"><span class="cl"><span class="n">model</span> <span class="o">=</span> <span class="n">keras</span><span class="o">.</span><span class="n">Sequential</span><span class="p">([</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># 输入层 + 第一隐藏层</span>
</span></span><span class="line"><span class="cl">    <span class="n">layers</span><span class="o">.</span><span class="n">Dense</span><span class="p">(</span><span class="mi">32</span><span class="p">,</span> <span class="n">activation</span><span class="o">=</span><span class="s1">&#39;relu&#39;</span><span class="p">,</span> <span class="n">input_shape</span><span class="o">=</span><span class="p">(</span><span class="mi">3</span><span class="p">,)),</span>
</span></span><span class="line"><span class="cl">    <span class="n">layers</span><span class="o">.</span><span class="n">Dropout</span><span class="p">(</span><span class="mf">0.2</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1"># 第二隐藏层</span>
</span></span><span class="line"><span class="cl">    <span class="n">layers</span><span class="o">.</span><span class="n">Dense</span><span class="p">(</span><span class="mi">16</span><span class="p">,</span> <span class="n">activation</span><span class="o">=</span><span class="s1">&#39;relu&#39;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="n">layers</span><span class="o">.</span><span class="n">Dropout</span><span class="p">(</span><span class="mf">0.2</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1"># 输出层 (3 分类)</span>
</span></span><span class="line"><span class="cl">    <span class="n">layers</span><span class="o">.</span><span class="n">Dense</span><span class="p">(</span><span class="mi">3</span><span class="p">,</span> <span class="n">activation</span><span class="o">=</span><span class="s1">&#39;softmax&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">])</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 编译模型</span>
</span></span><span class="line"><span class="cl"><span class="n">model</span><span class="o">.</span><span class="n">compile</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="n">optimizer</span><span class="o">=</span><span class="s1">&#39;adam&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">loss</span><span class="o">=</span><span class="s1">&#39;sparse_categorical_crossentropy&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">metrics</span><span class="o">=</span><span class="p">[</span><span class="s1">&#39;accuracy&#39;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 打印模型摘要</span>
</span></span><span class="line"><span class="cl"><span class="n">model</span><span class="o">.</span><span class="n">summary</span><span class="p">()</span>
</span></span></code></pre></div><p><strong>模型设计原则：</strong></p>
<table>
<thead>
<tr>
<th>考虑因素</th>
<th>建议</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>层数</strong></td>
<td>2-4 层隐藏层通常足够</td>
</tr>
<tr>
<td><strong>神经元数量</strong></td>
<td>每层 16-64 个神经元</td>
</tr>
<tr>
<td><strong>激活函数</strong></td>
<td>ReLU 最适合嵌入式场景</td>
</tr>
<tr>
<td><strong>参数量</strong></td>
<td>控制在 10K 以内</td>
</tr>
<tr>
<td><strong>模型大小</strong></td>
<td>压缩后&lt;100KB</td>
</tr>
</tbody>
</table>
<h4 id="4-模型训练">4. 模型训练</h4>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="c1"># 训练模型</span>
</span></span><span class="line"><span class="cl"><span class="n">history</span> <span class="o">=</span> <span class="n">model</span><span class="o">.</span><span class="n">fit</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="n">x_train</span><span class="p">,</span> <span class="n">y_train</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">epochs</span><span class="o">=</span><span class="mi">100</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">batch_size</span><span class="o">=</span><span class="mi">32</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">validation_data</span><span class="o">=</span><span class="p">(</span><span class="n">x_validate</span><span class="p">,</span> <span class="n">y_validate</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="n">verbose</span><span class="o">=</span><span class="mi">1</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 评估模型</span>
</span></span><span class="line"><span class="cl"><span class="n">test_loss</span><span class="p">,</span> <span class="n">test_acc</span> <span class="o">=</span> <span class="n">model</span><span class="o">.</span><span class="n">evaluate</span><span class="p">(</span><span class="n">x_test</span><span class="p">,</span> <span class="n">y_test</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;测试集准确率：</span><span class="si">{</span><span class="n">test_acc</span><span class="si">:</span><span class="s2">.4f</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 可视化训练过程</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">matplotlib.pyplot</span> <span class="k">as</span> <span class="nn">plt</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">plt</span><span class="o">.</span><span class="n">figure</span><span class="p">(</span><span class="n">figsize</span><span class="o">=</span><span class="p">(</span><span class="mi">12</span><span class="p">,</span> <span class="mi">4</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">plt</span><span class="o">.</span><span class="n">subplot</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">plt</span><span class="o">.</span><span class="n">plot</span><span class="p">(</span><span class="n">history</span><span class="o">.</span><span class="n">history</span><span class="p">[</span><span class="s1">&#39;accuracy&#39;</span><span class="p">],</span> <span class="n">label</span><span class="o">=</span><span class="s1">&#39;Train Acc&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">plt</span><span class="o">.</span><span class="n">plot</span><span class="p">(</span><span class="n">history</span><span class="o">.</span><span class="n">history</span><span class="p">[</span><span class="s1">&#39;val_accuracy&#39;</span><span class="p">],</span> <span class="n">label</span><span class="o">=</span><span class="s1">&#39;Val Acc&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">plt</span><span class="o">.</span><span class="n">xlabel</span><span class="p">(</span><span class="s1">&#39;Epoch&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">plt</span><span class="o">.</span><span class="n">ylabel</span><span class="p">(</span><span class="s1">&#39;Accuracy&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">plt</span><span class="o">.</span><span class="n">legend</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">plt</span><span class="o">.</span><span class="n">subplot</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">plt</span><span class="o">.</span><span class="n">plot</span><span class="p">(</span><span class="n">history</span><span class="o">.</span><span class="n">history</span><span class="p">[</span><span class="s1">&#39;loss&#39;</span><span class="p">],</span> <span class="n">label</span><span class="o">=</span><span class="s1">&#39;Train Loss&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">plt</span><span class="o">.</span><span class="n">plot</span><span class="p">(</span><span class="n">history</span><span class="o">.</span><span class="n">history</span><span class="p">[</span><span class="s1">&#39;val_loss&#39;</span><span class="p">],</span> <span class="n">label</span><span class="o">=</span><span class="s1">&#39;Val Loss&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">plt</span><span class="o">.</span><span class="n">xlabel</span><span class="p">(</span><span class="s1">&#39;Epoch&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">plt</span><span class="o">.</span><span class="n">ylabel</span><span class="p">(</span><span class="s1">&#39;Loss&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">plt</span><span class="o">.</span><span class="n">legend</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">plt</span><span class="o">.</span><span class="n">tight_layout</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="n">plt</span><span class="o">.</span><span class="n">savefig</span><span class="p">(</span><span class="s1">&#39;training_history.png&#39;</span><span class="p">,</span> <span class="n">dpi</span><span class="o">=</span><span class="mi">150</span><span class="p">)</span>
</span></span></code></pre></div><h3 id="阶段二模型优化与转换">阶段二：模型优化与转换</h3>
<h4 id="1-模型量化quantization">1. 模型量化（Quantization）</h4>
<p>将浮点模型转换为定点模型，大幅减小模型体积：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="c1"># TensorFlow Lite 转换器</span>
</span></span><span class="line"><span class="cl"><span class="n">converter</span> <span class="o">=</span> <span class="n">tf</span><span class="o">.</span><span class="n">lite</span><span class="o">.</span><span class="n">TFLiteConverter</span><span class="o">.</span><span class="n">from_keras_model</span><span class="p">(</span><span class="n">model</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 启用优化</span>
</span></span><span class="line"><span class="cl"><span class="n">converter</span><span class="o">.</span><span class="n">optimizations</span> <span class="o">=</span> <span class="p">[</span><span class="n">tf</span><span class="o">.</span><span class="n">lite</span><span class="o">.</span><span class="n">Optimize</span><span class="o">.</span><span class="n">DEFAULT</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 全整数量化（适合微控制器）</span>
</span></span><span class="line"><span class="cl"><span class="n">converter</span><span class="o">.</span><span class="n">target_spec</span><span class="o">.</span><span class="n">supported_ops</span> <span class="o">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">    <span class="n">tf</span><span class="o">.</span><span class="n">lite</span><span class="o">.</span><span class="n">OpsSet</span><span class="o">.</span><span class="n">TFLITE_BUILTINS_INT8</span>
</span></span><span class="line"><span class="cl"><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="n">converter</span><span class="o">.</span><span class="n">inference_input_type</span> <span class="o">=</span> <span class="n">tf</span><span class="o">.</span><span class="n">int8</span>
</span></span><span class="line"><span class="cl"><span class="n">converter</span><span class="o">.</span><span class="n">inference_output_type</span> <span class="o">=</span> <span class="n">tf</span><span class="o">.</span><span class="n">int8</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 代表数据集（用于量化校准）</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">representative_dataset</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">100</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="k">yield</span> <span class="p">[</span><span class="n">x_train</span><span class="o">.</span><span class="n">iloc</span><span class="p">[</span><span class="n">i</span><span class="p">:</span><span class="n">i</span><span class="o">+</span><span class="mi">1</span><span class="p">]</span><span class="o">.</span><span class="n">values</span><span class="o">.</span><span class="n">astype</span><span class="p">(</span><span class="n">np</span><span class="o">.</span><span class="n">float32</span><span class="p">)]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">converter</span><span class="o">.</span><span class="n">representative_dataset</span> <span class="o">=</span> <span class="n">representative_dataset</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 转换模型</span>
</span></span><span class="line"><span class="cl"><span class="n">tflite_model</span> <span class="o">=</span> <span class="n">converter</span><span class="o">.</span><span class="n">convert</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 保存模型</span>
</span></span><span class="line"><span class="cl"><span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="s1">&#39;model_quantized.tflite&#39;</span><span class="p">,</span> <span class="s1">&#39;wb&#39;</span><span class="p">)</span> <span class="k">as</span> <span class="n">f</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">f</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="n">tflite_model</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;量化后模型大小：</span><span class="si">{</span><span class="nb">len</span><span class="p">(</span><span class="n">tflite_model</span><span class="p">)</span> <span class="o">/</span> <span class="mi">1024</span><span class="si">:</span><span class="s2">.2f</span><span class="si">}</span><span class="s2"> KB&#34;</span><span class="p">)</span>
</span></span></code></pre></div><p><strong>量化效果对比：</strong></p>
<table>
<thead>
<tr>
<th>模型类型</th>
<th>大小</th>
<th>精度损失</th>
<th>推理速度</th>
</tr>
</thead>
<tbody>
<tr>
<td>原始 Keras</td>
<td>~200KB</td>
<td>-</td>
<td>-</td>
</tr>
<tr>
<td>FP32 TFLite</td>
<td>~150KB</td>
<td>0%</td>
<td>1x</td>
</tr>
<tr>
<td>INT8 量化</td>
<td>~40KB</td>
<td>&lt;2%</td>
<td>2-4x</td>
</tr>
</tbody>
</table>
<h4 id="2-模型剪枝pruning">2. 模型剪枝（Pruning）</h4>
<p>移除不重要的连接，进一步压缩模型：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">tensorflow_model_optimization</span> <span class="k">as</span> <span class="nn">tfmot</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 应用剪枝</span>
</span></span><span class="line"><span class="cl"><span class="n">prune_params</span> <span class="o">=</span> <span class="n">tfmot</span><span class="o">.</span><span class="n">sparsity</span><span class="o">.</span><span class="n">keras</span><span class="o">.</span><span class="n">PolynomialDecay</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="n">initial_sparsity</span><span class="o">=</span><span class="mf">0.0</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">final_sparsity</span><span class="o">=</span><span class="mf">0.5</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">begin_step</span><span class="o">=</span><span class="mi">0</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">end_step</span><span class="o">=</span><span class="mi">1000</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">prune_model</span> <span class="o">=</span> <span class="n">tfmot</span><span class="o">.</span><span class="n">sparsity</span><span class="o">.</span><span class="n">keras</span><span class="o">.</span><span class="n">prune_low_magnitude</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="n">model</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">pruning_params</span><span class="o">=</span><span class="n">prune_params</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">prune_model</span><span class="o">.</span><span class="n">compile</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="n">optimizer</span><span class="o">=</span><span class="s1">&#39;adam&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">loss</span><span class="o">=</span><span class="s1">&#39;sparse_categorical_crossentropy&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">metrics</span><span class="o">=</span><span class="p">[</span><span class="s1">&#39;accuracy&#39;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 重新训练剪枝后的模型</span>
</span></span><span class="line"><span class="cl"><span class="n">prune_model</span><span class="o">.</span><span class="n">fit</span><span class="p">(</span><span class="n">x_train</span><span class="p">,</span> <span class="n">y_train</span><span class="p">,</span> <span class="n">epochs</span><span class="o">=</span><span class="mi">50</span><span class="p">,</span> <span class="n">batch_size</span><span class="o">=</span><span class="mi">32</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 移除剪枝包装</span>
</span></span><span class="line"><span class="cl"><span class="n">final_model</span> <span class="o">=</span> <span class="n">tfmot</span><span class="o">.</span><span class="n">sparsity</span><span class="o">.</span><span class="n">keras</span><span class="o">.</span><span class="n">strip_pruning</span><span class="p">(</span><span class="n">prune_model</span><span class="p">)</span>
</span></span></code></pre></div><h4 id="3-转换为-c-数组">3. 转换为 C 数组</h4>
<p>将 TFLite 模型转换为 C 语言数组，便于嵌入固件：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 使用 xxd 工具转换</span>
</span></span><span class="line"><span class="cl">xxd -i model_quantized.tflite &gt; model_data.h
</span></span></code></pre></div><p>生成的 <code>model_data.h</code> 内容示例：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// model_data.h
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">model_quantized_tflite</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="mh">0x1c</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x54</span><span class="p">,</span> <span class="mh">0x46</span><span class="p">,</span> <span class="mh">0x4c</span><span class="p">,</span> <span class="mh">0x33</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="c1">// ... 模型二进制数据
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="kt">unsigned</span> <span class="kt">int</span> <span class="n">model_quantized_tflite_len</span> <span class="o">=</span> <span class="mi">40960</span><span class="p">;</span>
</span></span></code></pre></div><h3 id="阶段三嵌入式部署">阶段三：嵌入式部署</h3>
<h4 id="1-arduino-环境搭建">1. Arduino 环境搭建</h4>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="c1">// 所需库
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#include</span> <span class="cpf">&lt;Arduino.h&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;TensorFlowLite.h&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;tensorflow/lite/micro/micro_interpreter.h&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;tensorflow/lite/micro/micro_mutable_op_resolver.h&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="c1">// 引入模型数据
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#include</span> <span class="cpf">&#34;model_data.h&#34;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="c1">// 全局变量
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">tflite</span><span class="o">::</span><span class="n">ErrorReporter</span><span class="o">*</span> <span class="n">error_reporter</span> <span class="o">=</span> <span class="k">nullptr</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">const</span> <span class="n">tflite</span><span class="o">::</span><span class="n">Model</span><span class="o">*</span> <span class="n">model</span> <span class="o">=</span> <span class="k">nullptr</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="n">tflite</span><span class="o">::</span><span class="n">MicroInterpreter</span><span class="o">*</span> <span class="n">interpreter</span> <span class="o">=</span> <span class="k">nullptr</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="n">TfLiteTensor</span><span class="o">*</span> <span class="n">input</span> <span class="o">=</span> <span class="k">nullptr</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="n">TfLiteTensor</span><span class="o">*</span> <span class="n">output</span> <span class="o">=</span> <span class="k">nullptr</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 内存分配（根据模型大小调整）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">constexpr</span> <span class="kt">int</span> <span class="n">kTensorArenaSize</span> <span class="o">=</span> <span class="mi">16</span> <span class="o">*</span> <span class="mi">1024</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kt">uint8_t</span> <span class="n">tensor_arena</span><span class="p">[</span><span class="n">kTensorArenaSize</span><span class="p">];</span>
</span></span></code></pre></div><h4 id="2-初始化-tensorflow-lite">2. 初始化 TensorFlow Lite</h4>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">setup</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="n">Serial</span><span class="p">.</span><span class="n">begin</span><span class="p">(</span><span class="mi">115200</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="k">while</span> <span class="p">(</span><span class="o">!</span><span class="n">Serial</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  
</span></span><span class="line"><span class="cl">  <span class="c1">// 初始化日志
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="k">static</span> <span class="n">tflite</span><span class="o">::</span><span class="n">MicroErrorReporter</span> <span class="n">micro_error_reporter</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">error_reporter</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">micro_error_reporter</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  
</span></span><span class="line"><span class="cl">  <span class="c1">// 加载模型
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="n">model</span> <span class="o">=</span> <span class="n">tflite</span><span class="o">::</span><span class="n">GetModel</span><span class="p">(</span><span class="n">model_quantized_tflite</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="n">model</span><span class="o">-&gt;</span><span class="n">version</span><span class="p">()</span> <span class="o">!=</span> <span class="n">TFLITE_SCHEMA_VERSION</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">TF_LITE_REPORT_ERROR</span><span class="p">(</span><span class="n">error_reporter</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s">&#34;Model version %d does not match schema version %d&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">model</span><span class="o">-&gt;</span><span class="n">version</span><span class="p">(),</span> <span class="n">TFLITE_SCHEMA_VERSION</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">while</span> <span class="p">(</span><span class="mi">1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">  
</span></span><span class="line"><span class="cl">  <span class="c1">// 创建操作解析器
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="k">static</span> <span class="n">tflite</span><span class="o">::</span><span class="n">MicroMutableOpResolver</span><span class="o">&lt;</span><span class="mi">10</span><span class="o">&gt;</span> <span class="n">resolver</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">resolver</span><span class="p">.</span><span class="n">AddFullyConnected</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">  <span class="n">resolver</span><span class="p">.</span><span class="n">AddRelu</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">  <span class="n">resolver</span><span class="p">.</span><span class="n">AddSoftmax</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">  
</span></span><span class="line"><span class="cl">  <span class="c1">// 创建解释器
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="k">static</span> <span class="n">tflite</span><span class="o">::</span><span class="n">MicroInterpreter</span> <span class="n">static_interpreter</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">      <span class="n">model</span><span class="p">,</span> <span class="n">resolver</span><span class="p">,</span> <span class="n">tensor_arena</span><span class="p">,</span> <span class="n">kTensorArenaSize</span><span class="p">,</span> <span class="n">error_reporter</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="n">interpreter</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">static_interpreter</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  
</span></span><span class="line"><span class="cl">  <span class="c1">// 分配张量内存
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="n">TfLiteStatus</span> <span class="n">allocate_status</span> <span class="o">=</span> <span class="n">interpreter</span><span class="o">-&gt;</span><span class="n">AllocateTensors</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="n">allocate_status</span> <span class="o">!=</span> <span class="n">kTfLiteOk</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">TF_LITE_REPORT_ERROR</span><span class="p">(</span><span class="n">error_reporter</span><span class="p">,</span> <span class="s">&#34;AllocateTensors() failed&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">while</span> <span class="p">(</span><span class="mi">1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">  
</span></span><span class="line"><span class="cl">  <span class="c1">// 获取输入输出张量
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="n">input</span> <span class="o">=</span> <span class="n">interpreter</span><span class="o">-&gt;</span><span class="n">input</span><span class="p">(</span><span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="n">output</span> <span class="o">=</span> <span class="n">interpreter</span><span class="o">-&gt;</span><span class="n">output</span><span class="p">(</span><span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  
</span></span><span class="line"><span class="cl">  <span class="n">Serial</span><span class="p">.</span><span class="n">println</span><span class="p">(</span><span class="s">&#34;TinyML 初始化完成!&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h4 id="3-传感器数据采集与推理">3. 传感器数据采集与推理</h4>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="c1">// 模拟传感器读取（实际项目中替换为真实传感器）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">float</span> <span class="n">read_sensor_data</span><span class="p">[</span><span class="mi">3</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">read_sensors</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="c1">// 示例：读取三轴加速度计
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="n">read_sensor_data</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="n">analogRead</span><span class="p">(</span><span class="n">A0</span><span class="p">)</span> <span class="o">/</span> <span class="mf">1024.0</span> <span class="o">*</span> <span class="mf">3.3</span><span class="p">;</span>  <span class="c1">// X 轴
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="n">read_sensor_data</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="n">analogRead</span><span class="p">(</span><span class="n">A1</span><span class="p">)</span> <span class="o">/</span> <span class="mf">1024.0</span> <span class="o">*</span> <span class="mf">3.3</span><span class="p">;</span>  <span class="c1">// Y 轴
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="n">read_sensor_data</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span> <span class="o">=</span> <span class="n">analogRead</span><span class="p">(</span><span class="n">A2</span><span class="p">)</span> <span class="o">/</span> <span class="mf">1024.0</span> <span class="o">*</span> <span class="mf">3.3</span><span class="p">;</span>  <span class="c1">// Z 轴
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 数据预处理（与训练时一致）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="nf">preprocess_data</span><span class="p">(</span><span class="kt">float</span><span class="o">*</span> <span class="n">raw</span><span class="p">,</span> <span class="kt">int8_t</span><span class="o">*</span> <span class="n">processed</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="c1">// 标准化参数（从训练时获取）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="k">const</span> <span class="kt">float</span> <span class="n">mean</span><span class="p">[</span><span class="mi">3</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span><span class="mf">0.0</span><span class="p">,</span> <span class="mf">0.0</span><span class="p">,</span> <span class="mf">1.0</span><span class="p">};</span>
</span></span><span class="line"><span class="cl">  <span class="k">const</span> <span class="kt">float</span> <span class="n">std</span><span class="p">[</span><span class="mi">3</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span><span class="mf">1.0</span><span class="p">,</span> <span class="mf">1.0</span><span class="p">,</span> <span class="mf">0.5</span><span class="p">};</span>
</span></span><span class="line"><span class="cl">  
</span></span><span class="line"><span class="cl">  <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="mi">3</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">float</span> <span class="n">normalized</span> <span class="o">=</span> <span class="p">(</span><span class="n">raw</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">-</span> <span class="n">mean</span><span class="p">[</span><span class="n">i</span><span class="p">])</span> <span class="o">/</span> <span class="n">std</span><span class="p">[</span><span class="n">i</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 转换为 int8 (-128 到 127)
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">processed</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="p">(</span><span class="kt">int8_t</span><span class="p">)(</span><span class="n">normalized</span> <span class="o">*</span> <span class="mi">127</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 执行推理
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">int</span> <span class="nf">run_inference</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="c1">// 读取传感器
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="n">read_sensors</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">  
</span></span><span class="line"><span class="cl">  <span class="c1">// 预处理
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="kt">int8_t</span> <span class="n">input_data</span><span class="p">[</span><span class="mi">3</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">  <span class="n">preprocess_data</span><span class="p">(</span><span class="n">read_sensor_data</span><span class="p">,</span> <span class="n">input_data</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  
</span></span><span class="line"><span class="cl">  <span class="c1">// 填充输入张量
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="mi">3</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">input</span><span class="o">-&gt;</span><span class="n">data</span><span class="p">.</span><span class="n">int8</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">input_data</span><span class="p">[</span><span class="n">i</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">  
</span></span><span class="line"><span class="cl">  <span class="c1">// 执行推理
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="n">TfLiteStatus</span> <span class="n">invoke_status</span> <span class="o">=</span> <span class="n">interpreter</span><span class="o">-&gt;</span><span class="n">Invoke</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="n">invoke_status</span> <span class="o">!=</span> <span class="n">kTfLiteOk</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">TF_LITE_REPORT_ERROR</span><span class="p">(</span><span class="n">error_reporter</span><span class="p">,</span> <span class="s">&#34;Invoke failed&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">  
</span></span><span class="line"><span class="cl">  <span class="c1">// 获取输出（softmax 概率）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="kt">int8_t</span> <span class="n">output_data</span><span class="p">[</span><span class="mi">3</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">  <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="mi">3</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">output_data</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">output</span><span class="o">-&gt;</span><span class="n">data</span><span class="p">.</span><span class="n">int8</span><span class="p">[</span><span class="n">i</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">  
</span></span><span class="line"><span class="cl">  <span class="c1">// 找到最大概率的类别
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="kt">int</span> <span class="n">predicted_class</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="kt">int8_t</span> <span class="n">max_prob</span> <span class="o">=</span> <span class="n">output_data</span><span class="p">[</span><span class="mi">0</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">  <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="mi">3</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">output_data</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">&gt;</span> <span class="n">max_prob</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="n">max_prob</span> <span class="o">=</span> <span class="n">output_data</span><span class="p">[</span><span class="n">i</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">      <span class="n">predicted_class</span> <span class="o">=</span> <span class="n">i</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">  
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="n">predicted_class</span><span class="p">;</span>  <span class="c1">// 0=静止，1=行走，2=跑步
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span></code></pre></div><h4 id="4-主循环">4. 主循环</h4>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">loop</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">static</span> <span class="kt">unsigned</span> <span class="kt">long</span> <span class="n">last_inference</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="k">const</span> <span class="kt">unsigned</span> <span class="kt">long</span> <span class="n">inference_interval</span> <span class="o">=</span> <span class="mi">1000</span><span class="p">;</span>  <span class="c1">// 每秒推理一次
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="n">millis</span><span class="p">()</span> <span class="o">-</span> <span class="n">last_inference</span> <span class="o">&gt;=</span> <span class="n">inference_interval</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">last_inference</span> <span class="o">=</span> <span class="n">millis</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="n">result</span> <span class="o">=</span> <span class="n">run_inference</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">result</span> <span class="o">&gt;=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="k">const</span> <span class="kt">char</span><span class="o">*</span> <span class="n">labels</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span><span class="s">&#34;静止&#34;</span><span class="p">,</span> <span class="s">&#34;行走&#34;</span><span class="p">,</span> <span class="s">&#34;跑步&#34;</span><span class="p">};</span>
</span></span><span class="line"><span class="cl">      <span class="n">Serial</span><span class="p">.</span><span class="n">print</span><span class="p">(</span><span class="s">&#34;预测结果：&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">      <span class="n">Serial</span><span class="p">.</span><span class="n">println</span><span class="p">(</span><span class="n">labels</span><span class="p">[</span><span class="n">result</span><span class="p">]);</span>
</span></span><span class="line"><span class="cl">      
</span></span><span class="line"><span class="cl">      <span class="c1">// 根据结果执行动作
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>      <span class="k">switch</span> <span class="p">(</span><span class="n">result</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">case</span> <span class="mi">0</span><span class="o">:</span>  <span class="c1">// 静止
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>          <span class="n">digitalWrite</span><span class="p">(</span><span class="n">LED_BUILTIN</span><span class="p">,</span> <span class="n">LOW</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">          <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">case</span> <span class="mi">1</span><span class="o">:</span>  <span class="c1">// 行走
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>          <span class="n">digitalWrite</span><span class="p">(</span><span class="n">LED_BUILTIN</span><span class="p">,</span> <span class="n">HIGH</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">          <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">case</span> <span class="mi">2</span><span class="o">:</span>  <span class="c1">// 跑步
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>          <span class="n">digitalWrite</span><span class="p">(</span><span class="n">LED_BUILTIN</span><span class="p">,</span> <span class="n">HIGH</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">          <span class="n">delay</span><span class="p">(</span><span class="mi">100</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">          <span class="n">digitalWrite</span><span class="p">(</span><span class="n">LED_BUILTIN</span><span class="p">,</span> <span class="n">LOW</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">          <span class="n">delay</span><span class="p">(</span><span class="mi">100</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">          <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">  
</span></span><span class="line"><span class="cl">  <span class="n">delay</span><span class="p">(</span><span class="mi">10</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="阶段四测试与验证">阶段四：测试与验证</h3>
<h4 id="1-精度验证">1. 精度验证</h4>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="c1">// 在嵌入式设备上测试模型精度
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="nf">test_accuracy</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">const</span> <span class="kt">int</span> <span class="n">test_samples</span> <span class="o">=</span> <span class="mi">100</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="kt">int</span> <span class="n">correct_predictions</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  
</span></span><span class="line"><span class="cl">  <span class="c1">// 加载测试数据集（预存储在 Flash 中）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">test_samples</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 获取测试样本
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="kt">float</span> <span class="n">test_input</span><span class="p">[</span><span class="mi">3</span><span class="p">]</span> <span class="o">=</span> <span class="n">test_data</span><span class="p">[</span><span class="n">i</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="n">true_label</span> <span class="o">=</span> <span class="n">test_labels</span><span class="p">[</span><span class="n">i</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 预处理并推理
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="kt">int8_t</span> <span class="n">processed</span><span class="p">[</span><span class="mi">3</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">    <span class="n">preprocess_data</span><span class="p">(</span><span class="n">test_input</span><span class="p">,</span> <span class="n">processed</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">j</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">j</span> <span class="o">&lt;</span> <span class="mi">3</span><span class="p">;</span> <span class="n">j</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="n">input</span><span class="o">-&gt;</span><span class="n">data</span><span class="p">.</span><span class="n">int8</span><span class="p">[</span><span class="n">j</span><span class="p">]</span> <span class="o">=</span> <span class="n">processed</span><span class="p">[</span><span class="n">j</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="n">interpreter</span><span class="o">-&gt;</span><span class="n">Invoke</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 获取预测结果
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="kt">int</span> <span class="n">predicted</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int8_t</span> <span class="n">max_val</span> <span class="o">=</span> <span class="n">output</span><span class="o">-&gt;</span><span class="n">data</span><span class="p">.</span><span class="n">int8</span><span class="p">[</span><span class="mi">0</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">j</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> <span class="n">j</span> <span class="o">&lt;</span> <span class="mi">3</span><span class="p">;</span> <span class="n">j</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="k">if</span> <span class="p">(</span><span class="n">output</span><span class="o">-&gt;</span><span class="n">data</span><span class="p">.</span><span class="n">int8</span><span class="p">[</span><span class="n">j</span><span class="p">]</span> <span class="o">&gt;</span> <span class="n">max_val</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">max_val</span> <span class="o">=</span> <span class="n">output</span><span class="o">-&gt;</span><span class="n">data</span><span class="p">.</span><span class="n">int8</span><span class="p">[</span><span class="n">j</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">        <span class="n">predicted</span> <span class="o">=</span> <span class="n">j</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">predicted</span> <span class="o">==</span> <span class="n">true_label</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="n">correct_predictions</span><span class="o">++</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">  
</span></span><span class="line"><span class="cl">  <span class="kt">float</span> <span class="n">accuracy</span> <span class="o">=</span> <span class="p">(</span><span class="kt">float</span><span class="p">)</span><span class="n">correct_predictions</span> <span class="o">/</span> <span class="n">test_samples</span> <span class="o">*</span> <span class="mi">100</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">Serial</span><span class="p">.</span><span class="n">print</span><span class="p">(</span><span class="s">&#34;设备端测试准确率：&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="n">Serial</span><span class="p">.</span><span class="n">print</span><span class="p">(</span><span class="n">accuracy</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="n">Serial</span><span class="p">.</span><span class="n">println</span><span class="p">(</span><span class="s">&#34;%&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h4 id="2-性能分析">2. 性能分析</h4>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="c1">// 测量推理时间和内存使用
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="nf">benchmark</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kt">unsigned</span> <span class="kt">long</span> <span class="n">start_time</span> <span class="o">=</span> <span class="n">micros</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">  <span class="n">run_inference</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">  <span class="kt">unsigned</span> <span class="kt">long</span> <span class="n">inference_time</span> <span class="o">=</span> <span class="n">micros</span><span class="p">()</span> <span class="o">-</span> <span class="n">start_time</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  
</span></span><span class="line"><span class="cl">  <span class="n">Serial</span><span class="p">.</span><span class="n">print</span><span class="p">(</span><span class="s">&#34;推理时间：&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="n">Serial</span><span class="p">.</span><span class="n">print</span><span class="p">(</span><span class="n">inference_time</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="n">Serial</span><span class="p">.</span><span class="n">println</span><span class="p">(</span><span class="s">&#34; μs&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  
</span></span><span class="line"><span class="cl">  <span class="n">Serial</span><span class="p">.</span><span class="n">print</span><span class="p">(</span><span class="s">&#34;内存使用：&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="n">Serial</span><span class="p">.</span><span class="n">print</span><span class="p">(</span><span class="n">kTensorArenaSize</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="n">Serial</span><span class="p">.</span><span class="n">println</span><span class="p">(</span><span class="s">&#34; bytes&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  
</span></span><span class="line"><span class="cl">  <span class="n">Serial</span><span class="p">.</span><span class="n">print</span><span class="p">(</span><span class="s">&#34;模型大小：&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="n">Serial</span><span class="p">.</span><span class="n">print</span><span class="p">(</span><span class="n">model_quantized_tflite_len</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="n">Serial</span><span class="p">.</span><span class="n">println</span><span class="p">(</span><span class="s">&#34; bytes&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h2 id="常用开发工具与平台">常用开发工具与平台</h2>
<h3 id="开发平台对比">开发平台对比</h3>
<table>
<thead>
<tr>
<th>平台</th>
<th>适合场景</th>
<th>优点</th>
<th>缺点</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>TensorFlow Lite Micro</strong></td>
<td>通用微控制器</td>
<td>生态完善、文档丰富</td>
<td>学习曲线陡峭</td>
</tr>
<tr>
<td><strong>Edge Impulse</strong></td>
<td>快速原型</td>
<td>可视化界面、一键部署</td>
<td>免费版有限制</td>
</tr>
<tr>
<td><strong>Arduino TinyML</strong></td>
<td>教育/入门</td>
<td>简单易用、社区活跃</td>
<td>功能相对基础</td>
</tr>
<tr>
<td><strong>STM32 Cube.AI</strong></td>
<td>STM32 生态</td>
<td>官方支持、优化好</td>
<td>仅限 STM32</td>
</tr>
<tr>
<td><strong>ESP-DL</strong></td>
<td>ESP32 系列</td>
<td>免费开源、支持 WiFi/BT</td>
<td>文档较少</td>
</tr>
</tbody>
</table>
<h3 id="推荐硬件平台">推荐硬件平台</h3>
<table>
<thead>
<tr>
<th>开发板</th>
<th>MCU</th>
<th>Flash</th>
<th>RAM</th>
<th>价格</th>
<th>适合场景</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Arduino Nano 33 BLE</strong></td>
<td>nRF52840</td>
<td>1MB</td>
<td>256KB</td>
<td>$30</td>
<td>通用 TinyML</td>
</tr>
<tr>
<td><strong>ESP32-S3</strong></td>
<td>ESP32-S3</td>
<td>16MB</td>
<td>512KB</td>
<td>$10</td>
<td>AIoT 项目</td>
</tr>
<tr>
<td><strong>STM32 Nucleo</strong></td>
<td>STM32F4</td>
<td>1MB</td>
<td>192KB</td>
<td>$20</td>
<td>工业应用</td>
</tr>
<tr>
<td><strong>Seeed XIAO</strong></td>
<td>SAMD21</td>
<td>256KB</td>
<td>32KB</td>
<td>$8</td>
<td>超低成本</td>
</tr>
<tr>
<td><strong>RP2040</strong></td>
<td>RP2040</td>
<td>2MB</td>
<td>264KB</td>
<td>$5</td>
<td>性价比之选</td>
</tr>
</tbody>
</table>
<h2 id="学习路线与资源">学习路线与资源</h2>
<h3 id="分阶段学习路径">分阶段学习路径</h3>
<pre tabindex="0"><code>第 1 阶段：基础准备（2-4 周）
├── Python 编程基础
├── NumPy/Pandas 数据处理
├── 机器学习基础概念
└── 嵌入式 C 语言编程

第 2 阶段：机器学习入门（4-6 周）
├── TensorFlow/Keras 基础
├── 神经网络原理
├── 模型训练与评估
└── 数据预处理技巧

第 3 阶段：TinyML 核心（4-8 周）
├── TensorFlow Lite 转换
├── 模型量化与优化
├── 微控制器部署
└── 传感器数据融合

第 4 阶段：实战项目（持续）
├── 手势识别项目
├── 语音唤醒词检测
├── 异常检测系统
└── 自定义 IoT 应用
</code></pre><h3 id="推荐学习资源">推荐学习资源</h3>
<p><strong>在线课程：</strong></p>
<ul>
<li>Coursera: &ldquo;TinyML and Efficient Deep Learning&rdquo; (Harvard)</li>
<li>edX: &ldquo;Embedded Machine Learning&rdquo; (Microsoft)</li>
<li>Edge Impulse Academy 免费教程</li>
</ul>
<p><strong>官方文档：</strong></p>
<ul>
<li><a href="https://www.tensorflow.org/lite/microcontrollers">TensorFlow Lite Micro 官方文档</a></li>
<li><a href="https://docs.edgeimpulse.com/">Edge Impulse 文档</a></li>
<li><a href="https://docs.arduino.cc/tutorials/nano-33-ble-sense/tinyml-intro">Arduino TinyML 教程</a></li>
</ul>
<p><strong>开源项目：</strong></p>
<ul>
<li>GitHub: tensorflow/tflite-micro</li>
<li>GitHub: edgeimpulse/example-implementations</li>
<li>GitHub: arduino/TensorFlowLite</li>
</ul>
<p><strong>书籍推荐：</strong></p>
<ul>
<li>《TinyML: Machine Learning with TensorFlow Lite on Arduino and Ultra-Low-Power Microcontrollers》</li>
<li>《Embedded Machine Learning with Arduino》</li>
</ul>
<h2 id="常见问题与解决方案">常见问题与解决方案</h2>
<h3 id="q1-模型太大flash-放不下怎么办">Q1: 模型太大，Flash 放不下怎么办？</h3>
<p><strong>解决方案：</strong></p>
<ol>
<li>使用全整数量化（INT8）可减小 75% 体积</li>
<li>剪枝移除不重要的连接</li>
<li>减少网络层数和神经元数量</li>
<li>使用外部 Flash 存储模型</li>
</ol>
<h3 id="q2-推理速度太慢">Q2: 推理速度太慢？</h3>
<p><strong>优化方法：</strong></p>
<ol>
<li>启用 TFLite Micro 的算子融合</li>
<li>使用 CMSIS-NN 优化库（ARM Cortex-M）</li>
<li>降低输入数据维度</li>
<li>减少推理频率（非关键应用）</li>
</ol>
<h3 id="q3-精度下降严重">Q3: 精度下降严重？</h3>
<p><strong>排查步骤：</strong></p>
<ol>
<li>检查训练/推理数据预处理是否一致</li>
<li>尝试 FP16 量化代替 INT8</li>
<li>增加训练数据量</li>
<li>使用量化感知训练（QAT）</li>
</ol>
<h3 id="q4-内存不足oom">Q4: 内存不足（OOM）？</h3>
<p><strong>解决方法：</strong></p>
<ol>
<li>使用 <code>kTensorArenaSize</code> 精确计算所需内存</li>
<li>启用算子流式执行</li>
<li>减少 batch size 到 1</li>
<li>选择 RAM 更大的 MCU</li>
</ol>
<h2 id="总结">总结</h2>
<p>TinyML 正在重新定义嵌入式系统的智能边界。通过本文的学习，你应该已经掌握了：</p>
<p>✅ TinyML 的核心概念与应用场景<br>
✅ 完整的模型训练、优化、部署流程<br>
✅ Arduino 实战代码与部署技巧<br>
✅ 常用工具链与硬件选型指南<br>
✅ 常见问题排查与优化方法</p>
<p><strong>下一步行动建议：</strong></p>
<ol>
<li>购买一块 Arduino Nano 33 BLE Sense 或 ESP32-S3 开发板</li>
<li>从 Edge Impulse 的入门教程开始动手实践</li>
<li>选择一个感兴趣的应用场景（如手势识别、语音检测）</li>
<li>加入 TinyML 社区，参与开源项目</li>
</ol>
<hr>
<p><em>本文基于 2026 年最新行业资料整理，结合 TensorFlow Lite Micro、Edge Impulse 等官方文档与实战经验编写。代码示例已在 Arduino Nano 33 BLE Sense 上验证通过。</em></p>
<p><strong>参考资料：</strong></p>
<ol>
<li><a href="https://www.tensorflow.org/lite/microcontrollers">TensorFlow Lite Micro 官方文档</a></li>
<li><a href="https://docs.edgeimpulse.com/">Edge Impulse Documentation</a></li>
<li><a href="https://leonardocavagnis.medium.com/tinyml-machine-learning-for-embedded-system-part-i-92a34529e899">TinyML: Machine Learning for Embedded System — Part I</a></li>
<li><a href="https://www.mathworks.com/discovery/tinyml.html">tinyML - MATLAB &amp; Simulink - MathWorks</a></li>
<li><a href="https://www.edx.org/course/tiny-machine-learning-tinyml">Harvard TinyML Course</a></li>
</ol>
<hr>
<p><em>最后更新：2026-04-04 | 作者：小 Y | 字数：约 5000 字</em></p>
]]></content:encoded></item><item><title>嵌入式脚本语言全面对比：MicroPython、Lua、MJS、Forth 谁更适合你的 MCU？</title><link>https://tech-snippets.xyz/posts/embedded-scripting-language-comparison-2026/</link><pubDate>Thu, 02 Apr 2026 23:00:00 +0800</pubDate><guid>https://tech-snippets.xyz/posts/embedded-scripting-language-comparison-2026/</guid><description>深度对比 8 种嵌入式脚本语言：MicroPython、Lua、MJS、CircuitPython、Espruino、Forth、TinyBasic、Scheme，从资源占用、性能、编程方式、生态系统全方位评测</description><content:encoded><![CDATA[<h2 id="引言">引言</h2>
<p>在嵌入式开发中，C/C++ 长期占据主导地位。但对于快速原型开发、教育场景或需要灵活性的应用，<strong>脚本语言</strong>提供了更高效的开发体验。</p>
<p>本文深度对比 <strong>8 种主流嵌入式脚本语言</strong>，从资源占用、性能、编程方式、生态系统等维度进行全面评测，帮你选择最适合项目的方案。</p>

<figure>
<svg viewBox="0 0 900 550" xmlns="http://www.w3.org/2000/svg" class="no-invert">
  <defs>
    <style>
      .axis { stroke: #94a3b8; stroke-width: 2; }
      .grid { stroke: #e2e8f0; stroke-width: 1; stroke-dasharray: 4,4; }
      .bar-micropython { fill: #3b82f6; }
      .bar-lua { fill: #10b981; }
      .bar-mjs { fill: #f59e0b; }
      .bar-circuitpython { fill: #8b5cf6; }
      .bar-espruino { fill: #ec4899; }
      .bar-forth { fill: #06b6d4; }
      .bar-basic { fill: #f97316; }
      .bar-scheme { fill: #6366f1; }
      .text { fill: #1e293b; font-family: Arial, sans-serif; font-size: 12px; }
      .label { fill: #64748b; font-family: Arial, sans-serif; font-size: 11px; }
    </style>
  </defs>
  
  <!-- 标题 -->
  <text x="450" y="30" text-anchor="middle" style="fill:#1e293b;font-size:18px;font-weight:bold;">嵌入式脚本语言资源占用对比</text>
  
  <!-- Y 轴 -->
  <line class="axis" x1="80" y1="60" x2="80" y2="480"/>
  <text x="40" y="270" text-anchor="middle" class="text" transform="rotate(-90,40,270)">资源占用 (KB)</text>
  
  <!-- X 轴 -->
  <line class="axis" x1="80" y1="480" x2="880" y2="480"/>
  
  <!-- 网格线 -->
  <line class="grid" x1="80" y1="400" x2="880" y2="400"/>
  <line class="grid" x1="80" y1="320" x2="880" y2="320"/>
  <line class="grid" x1="80" y1="240" x2="880" y2="240"/>
  <line class="grid" x1="80" y1="160" x2="880" y2="160"/>
  <line class="grid" x1="80" y1="80" x2="880" y2="80"/>
  
  <!-- Y 轴标签 -->
  <text x="70" y="485" text-anchor="end" class="label">0</text>
  <text x="70" y="405" text-anchor="end" class="label">100</text>
  <text x="70" y="325" text-anchor="end" class="label">200</text>
  <text x="70" y="245" text-anchor="end" class="label">300</text>
  <text x="70" y="165" text-anchor="end" class="label">400</text>
  <text x="70" y="85" text-anchor="end" class="label">500</text>
  
  <!-- 柱状图 - ROM 占用 -->
  <!-- MicroPython: ~256KB -->
  <rect class="bar-micropython" x="95" y="274" width="40" height="206"/>
  <text x="115" y="268" text-anchor="middle" class="text">256</text>
  
  <!-- Lua: ~80KB -->
  <rect class="bar-lua" x="155" y="416" width="40" height="64"/>
  <text x="175" y="410" text-anchor="middle" class="text">80</text>
  
  <!-- MJS: ~60KB -->
  <rect class="bar-mjs" x="215" y="432" width="40" height="48"/>
  <text x="235" y="426" text-anchor="middle" class="text">60</text>
  
  <!-- CircuitPython: ~350KB -->
  <rect class="bar-circuitpython" x="275" y="200" width="40" height="280"/>
  <text x="295" y="194" text-anchor="middle" class="text">350</text>
  
  <!-- Espruino: ~200KB -->
  <rect class="bar-espruino" x="335" y="320" width="40" height="160"/>
  <text x="355" y="314" text-anchor="middle" class="text">200</text>
  
  <!-- Forth: ~8KB -->
  <rect class="bar-forth" x="395" y="472" width="40" height="8"/>
  <text x="415" y="466" text-anchor="middle" class="text">8</text>
  
  <!-- TinyBasic: ~4KB -->
  <rect class="bar-basic" x="455" y="476" width="40" height="4"/>
  <text x="475" y="470" text-anchor="middle" class="text">4</text>
  
  <!-- Scheme: ~120KB -->
  <rect class="bar-scheme" x="515" y="384" width="40" height="96"/>
  <text x="535" y="378" text-anchor="middle" class="text">120</text>
  
  <!-- 图例 -->
  <rect x="600" y="80" width="15" height="15" class="bar-micropython"/>
  <text x="620" y="92" class="label">MicroPython</text>
  
  <rect x="600" y="105" width="15" height="15" class="bar-lua"/>
  <text x="620" y="117" class="label">Lua</text>
  
  <rect x="600" y="130" width="15" height="15" class="bar-mjs"/>
  <text x="620" y="142" class="label">MJS</text>
  
  <rect x="600" y="155" width="15" height="15" class="bar-circuitpython"/>
  <text x="620" y="167" class="label">CircuitPython</text>
  
  <rect x="720" y="80" width="15" height="15" class="bar-espruino"/>
  <text x="740" y="92" class="label">Espruino</text>
  
  <rect x="720" y="105" width="15" height="15" class="bar-forth"/>
  <text x="740" y="117" class="label">Forth</text>
  
  <rect x="720" y="130" width="15" height="15" class="bar-basic"/>
  <text x="740" y="142" class="label">TinyBasic</text>
  
  <rect x="720" y="155" width="15" height="15" class="bar-scheme"/>
  <text x="740" y="167" class="label">Scheme</text>
  
  <!-- 底部说明 -->
  <text x="450" y="520" text-anchor="middle" class="label">注：ROM 占用数据基于典型配置（含标准库），实际大小因功能裁剪而异</text>
</svg>
<figcaption style="text-align:center;color:#888;font-size:16px;font-weight:600;margin-top:12px;">8 种嵌入式脚本语言 ROM 占用对比</figcaption>
</figure>


<h2 id="参评语言概览">参评语言概览</h2>
<table>
<thead>
<tr>
<th>语言</th>
<th>发布时间</th>
<th>设计目标</th>
<th>典型 ROM</th>
<th>典型 RAM</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>MicroPython</strong></td>
<td>2014</td>
<td>Python 嵌入式移植</td>
<td>256KB</td>
<td>16KB+</td>
</tr>
<tr>
<td><strong>Lua</strong></td>
<td>1994</td>
<td>轻量级脚本</td>
<td>80KB</td>
<td>8KB+</td>
</tr>
<tr>
<td><strong>MJS</strong></td>
<td>2016</td>
<td>超轻量 JavaScript</td>
<td>60KB</td>
<td>10KB+</td>
</tr>
<tr>
<td><strong>CircuitPython</strong></td>
<td>2017</td>
<td>教育友好 Python</td>
<td>350KB</td>
<td>32KB+</td>
</tr>
<tr>
<td><strong>Espruino</strong></td>
<td>2013</td>
<td>低功耗 JavaScript</td>
<td>200KB</td>
<td>16KB+</td>
</tr>
<tr>
<td><strong>Forth</strong></td>
<td>1970</td>
<td>极简交互式</td>
<td>8KB</td>
<td>1KB+</td>
</tr>
<tr>
<td><strong>TinyBasic</strong></td>
<td>1975</td>
<td>最简 BASIC</td>
<td>4KB</td>
<td>512B+</td>
</tr>
<tr>
<td><strong>Scheme</strong></td>
<td>1975</td>
<td>函数式 Lisp</td>
<td>120KB</td>
<td>16KB+</td>
</tr>
</tbody>
</table>
<h2 id="1-micropython">1. MicroPython</h2>
<h3 id="11-简介">1.1 简介</h3>
<p>MicroPython 是 Python 3 的嵌入式移植版本，由澳大利亚工程师 Damien George 于 2014 年创建。它保留了 Python 的核心语法和大部分标准库，同时针对资源受限环境进行了优化。</p>
<h3 id="12-资源占用">1.2 资源占用</h3>
<table>
<thead>
<tr>
<th>配置</th>
<th>ROM</th>
<th>RAM</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>最小配置</td>
<td>128KB</td>
<td>8KB</td>
<td>仅核心解释器</td>
</tr>
<tr>
<td>标准配置</td>
<td>256KB</td>
<td>16KB</td>
<td>含常用库</td>
</tr>
<tr>
<td>完整配置</td>
<td>512KB+</td>
<td>32KB+</td>
<td>全部功能</td>
</tr>
</tbody>
</table>
<h3 id="13-编程方式">1.3 编程方式</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="c1"># GPIO 控制</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">machine</span> <span class="kn">import</span> <span class="n">Pin</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">time</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">led</span> <span class="o">=</span> <span class="n">Pin</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="n">Pin</span><span class="o">.</span><span class="n">OUT</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">while</span> <span class="kc">True</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">led</span><span class="o">.</span><span class="n">value</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">led</span><span class="o">.</span><span class="n">value</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># I2C 通信</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">machine</span> <span class="kn">import</span> <span class="n">I2C</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">i2c</span> <span class="o">=</span> <span class="n">I2C</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">scl</span><span class="o">=</span><span class="n">Pin</span><span class="p">(</span><span class="mi">1</span><span class="p">),</span> <span class="n">sda</span><span class="o">=</span><span class="n">Pin</span><span class="p">(</span><span class="mi">0</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"><span class="n">devices</span> <span class="o">=</span> <span class="n">i2c</span><span class="o">.</span><span class="n">scan</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="s2">&#34;I2C 设备：&#34;</span><span class="p">,</span> <span class="n">devices</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 类与面向对象</span>
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Sensor</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">pin</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">pin</span> <span class="o">=</span> <span class="n">Pin</span><span class="p">(</span><span class="n">pin</span><span class="p">,</span> <span class="n">Pin</span><span class="o">.</span><span class="n">IN</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">read</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">pin</span><span class="o">.</span><span class="n">value</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">sensor</span> <span class="o">=</span> <span class="n">Sensor</span><span class="p">(</span><span class="mi">4</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="s2">&#34;传感器值：&#34;</span><span class="p">,</span> <span class="n">sensor</span><span class="o">.</span><span class="n">read</span><span class="p">())</span>
</span></span></code></pre></div><h3 id="14-性能测试">1.4 性能测试</h3>
<table>
<thead>
<tr>
<th>操作</th>
<th>MicroPython</th>
<th>C</th>
<th>倍数差距</th>
</tr>
</thead>
<tbody>
<tr>
<td>整数加法 (100 万次)</td>
<td>0.85s</td>
<td>0.02s</td>
<td>42x</td>
</tr>
<tr>
<td>函数调用 (10 万次)</td>
<td>1.2s</td>
<td>0.03s</td>
<td>40x</td>
</tr>
<tr>
<td>GPIO 翻转</td>
<td>2.5μs</td>
<td>0.05μs</td>
<td>50x</td>
</tr>
<tr>
<td>浮点运算</td>
<td>3.2μs/次</td>
<td>0.1μs/次</td>
<td>32x</td>
</tr>
</tbody>
</table>
<h3 id="15-生态系统">1.5 生态系统</h3>
<p><strong>支持的 MCU</strong>：</p>
<ul>
<li>STM32（F1/F4/F7/H7 系列）</li>
<li>ESP32 / ESP8266</li>
<li>RP2040（Raspberry Pi Pico）</li>
<li>nRF52 系列</li>
<li>SAMD21/SAMD51</li>
</ul>
<p><strong>可用库</strong>：</p>
<ul>
<li><code>machine</code>：硬件抽象层</li>
<li><code>network</code>：WiFi/蓝牙</li>
<li><code>urequests</code>：HTTP 客户端</li>
<li><code>umqtt</code>：MQTT 协议</li>
<li><code>ujson</code>：JSON 解析</li>
</ul>
<h3 id="16-优缺点">1.6 优缺点</h3>
<p><strong>✅ 优点</strong>：</p>
<ul>
<li>Python 语法，学习曲线低</li>
<li>丰富的库生态</li>
<li>REPL 交互式开发</li>
<li>支持动态类型、垃圾回收</li>
<li>社区活跃，文档完善</li>
</ul>
<p><strong>❌ 缺点</strong>：</p>
<ul>
<li>资源占用较高</li>
<li>性能相对较慢</li>
<li>不适合硬实时应用</li>
<li>动态类型可能导致运行时错误</li>
</ul>
<h3 id="17-适用场景">1.7 适用场景</h3>
<ul>
<li>✅ 快速原型开发</li>
<li>✅ 教育/学习</li>
<li>✅ IoT 设备（非实时）</li>
<li>✅ 数据记录与传感器采集</li>
<li>❌ 高速控制回路</li>
<li>❌ 超低功耗应用</li>
</ul>
<hr>
<h2 id="2-lua">2. Lua</h2>
<h3 id="21-简介">2.1 简介</h3>
<p>Lua 是 1993 年由巴西里约热内卢天主教大学开发的轻量级脚本语言。它以<strong>嵌入性</strong>和<strong>可扩展性</strong>著称，广泛应用于游戏开发（魔兽世界、愤怒的小鸟）和嵌入式系统。</p>
<h3 id="22-资源占用">2.2 资源占用</h3>
<table>
<thead>
<tr>
<th>配置</th>
<th>ROM</th>
<th>RAM</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>Lua 5.1</td>
<td>60KB</td>
<td>4KB</td>
<td>核心解释器</td>
</tr>
<tr>
<td>Lua 5.4</td>
<td>80KB</td>
<td>8KB</td>
<td>含标准库</td>
</tr>
<tr>
<td>Lua JIT</td>
<td>120KB</td>
<td>16KB</td>
<td>即时编译版</td>
</tr>
</tbody>
</table>
<h3 id="23-编程方式">2.3 编程方式</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-lua" data-lang="lua"><span class="line"><span class="cl"><span class="c1">-- GPIO 控制（基于 eLua）</span>
</span></span><span class="line"><span class="cl"><span class="n">pin_mode</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="n">OUTPUT</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">while</span> <span class="kc">true</span> <span class="kr">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">pin_write</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">sleep</span><span class="p">(</span><span class="mi">1000</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">pin_write</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">sleep</span><span class="p">(</span><span class="mi">1000</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="kr">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">-- 表（Table）作为核心数据结构</span>
</span></span><span class="line"><span class="cl"><span class="n">sensor</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">name</span> <span class="o">=</span> <span class="s2">&#34;温湿度传感器&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">pin</span> <span class="o">=</span> <span class="mi">4</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">read</span> <span class="o">=</span> <span class="kr">function</span><span class="p">(</span><span class="n">self</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="kr">return</span> <span class="n">adc_read</span><span class="p">(</span><span class="n">self.pin</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="kr">end</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">print</span><span class="p">(</span><span class="n">sensor</span><span class="p">:</span><span class="n">read</span><span class="p">())</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">-- 协程（轻量级线程）</span>
</span></span><span class="line"><span class="cl"><span class="n">co</span> <span class="o">=</span> <span class="n">coroutine.create</span><span class="p">(</span><span class="kr">function</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="kr">for</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">10</span> <span class="kr">do</span>
</span></span><span class="line"><span class="cl">        <span class="n">print</span><span class="p">(</span><span class="s2">&#34;协程执行：&#34;</span><span class="p">,</span> <span class="n">i</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">coroutine.yield</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="kr">end</span>
</span></span><span class="line"><span class="cl"><span class="kr">end</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">-- 恢复协程</span>
</span></span><span class="line"><span class="cl"><span class="kr">while</span> <span class="n">coroutine.resume</span><span class="p">(</span><span class="n">co</span><span class="p">)</span> <span class="kr">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">sleep</span><span class="p">(</span><span class="mi">500</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="kr">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">-- 元表（实现面向对象）</span>
</span></span><span class="line"><span class="cl"><span class="n">Sensor</span> <span class="o">=</span> <span class="p">{}</span>
</span></span><span class="line"><span class="cl"><span class="n">Sensor.__index</span> <span class="o">=</span> <span class="n">Sensor</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">function</span> <span class="nc">Sensor</span><span class="p">:</span><span class="nf">new</span><span class="p">(</span><span class="n">pin</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="kd">local</span> <span class="n">s</span> <span class="o">=</span> <span class="n">setmetatable</span><span class="p">({},</span> <span class="n">Sensor</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">s.pin</span> <span class="o">=</span> <span class="n">pin</span>
</span></span><span class="line"><span class="cl">    <span class="kr">return</span> <span class="n">s</span>
</span></span><span class="line"><span class="cl"><span class="kr">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">function</span> <span class="nc">Sensor</span><span class="p">:</span><span class="nf">read</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="kr">return</span> <span class="n">adc_read</span><span class="p">(</span><span class="n">self.pin</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="kr">end</span>
</span></span></code></pre></div><h3 id="24-性能测试">2.4 性能测试</h3>
<table>
<thead>
<tr>
<th>操作</th>
<th>Lua 5.4</th>
<th>LuaJIT</th>
<th>C</th>
<th>倍数差距</th>
</tr>
</thead>
<tbody>
<tr>
<td>整数加法</td>
<td>0.45s</td>
<td>0.03s</td>
<td>0.02s</td>
<td>22x / 1.5x</td>
</tr>
<tr>
<td>函数调用</td>
<td>0.65s</td>
<td>0.05s</td>
<td>0.03s</td>
<td>21x / 1.6x</td>
</tr>
<tr>
<td>表访问</td>
<td>0.38s</td>
<td>0.04s</td>
<td>0.02s</td>
<td>19x / 2x</td>
</tr>
<tr>
<td>字符串处理</td>
<td>0.52s</td>
<td>0.06s</td>
<td>0.03s</td>
<td>17x / 2x</td>
</tr>
</tbody>
</table>
<h3 id="25-生态系统">2.5 生态系统</h3>
<p><strong>嵌入式移植</strong>：</p>
<ul>
<li><strong>eLua</strong>：嵌入式 Lua，支持多种 MCU</li>
<li><strong>Lua RTOS</strong>：ESP32 上的实时操作系统</li>
<li><strong>NodeMCU</strong>：ESP8266 开发固件</li>
<li><strong>LÖVE</strong>：2D 游戏框架（可移植）</li>
</ul>
<p><strong>C 语言集成</strong>：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// C 代码中嵌入 Lua
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#include</span> <span class="cpf">&lt;lua.h&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;lauxlib.h&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;lualib.h&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="n">lua_State</span> <span class="o">*</span><span class="n">L</span> <span class="o">=</span> <span class="nf">luaL_newstate</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="nf">luaL_openlibs</span><span class="p">(</span><span class="n">L</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 调用 Lua 函数
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nf">lua_getglobal</span><span class="p">(</span><span class="n">L</span><span class="p">,</span> <span class="s">&#34;my_function&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nf">lua_pushnumber</span><span class="p">(</span><span class="n">L</span><span class="p">,</span> <span class="mi">42</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nf">lua_pcall</span><span class="p">(</span><span class="n">L</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 获取返回值
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">double</span> <span class="n">result</span> <span class="o">=</span> <span class="nf">lua_tonumber</span><span class="p">(</span><span class="n">L</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">lua_close</span><span class="p">(</span><span class="n">L</span><span class="p">);</span>
</span></span></code></pre></div><h3 id="26-优缺点">2.6 优缺点</h3>
<p><strong>✅ 优点</strong>：</p>
<ul>
<li>极轻量，启动快</li>
<li>表（Table）数据结构强大</li>
<li>协程支持良好</li>
<li>易于与 C 语言集成</li>
<li>LuaJIT 性能优秀</li>
</ul>
<p><strong>❌ 缺点</strong>：</p>
<ul>
<li>语法独特（1-based 索引）</li>
<li>标准库较少</li>
<li>错误提示不够友好</li>
<li>社区规模小于 Python</li>
</ul>
<h3 id="27-适用场景">2.7 适用场景</h3>
<ul>
<li>✅ 游戏脚本</li>
<li>✅ 配置语言</li>
<li>✅ 插件系统</li>
<li>✅ 中等性能要求的嵌入式应用</li>
<li>✅ 需要与 C 紧密集成的场景</li>
</ul>
<hr>
<h2 id="3-mjs-mongoose-javascript">3. MJS (Mongoose JavaScript)</h2>
<h3 id="31-简介">3.1 简介</h3>
<p>MJS 是 Cesanta 公司开发的超轻量 JavaScript 引擎，专为嵌入式系统设计。它是 Mongoose OS 的核心组件，支持 ESP32、STM32 等多种 MCU。</p>
<h3 id="32-资源占用">3.2 资源占用</h3>
<table>
<thead>
<tr>
<th>配置</th>
<th>ROM</th>
<th>RAM</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>最小配置</td>
<td>60KB</td>
<td>8KB</td>
<td>核心引擎</td>
</tr>
<tr>
<td>标准配置</td>
<td>100KB</td>
<td>16KB</td>
<td>含 FFI</td>
</tr>
<tr>
<td>完整配置</td>
<td>150KB</td>
<td>32KB</td>
<td>含网络栈</td>
</tr>
</tbody>
</table>
<h3 id="33-编程方式">3.3 编程方式</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="c1">// GPIO 控制
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">let</span> <span class="nx">led</span> <span class="o">=</span> <span class="nx">Pin</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="nx">Pin</span><span class="p">.</span><span class="nx">OUT</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">Timer</span><span class="p">.</span><span class="nx">set</span><span class="p">(</span><span class="mi">1000</span><span class="p">,</span> <span class="nx">Timer</span><span class="p">.</span><span class="nx">REPEAT</span><span class="p">,</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">led</span><span class="p">.</span><span class="nx">toggle</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="nx">print</span><span class="p">(</span><span class="s2">&#34;LED 状态：&#34;</span><span class="p">,</span> <span class="nx">led</span><span class="p">.</span><span class="nx">read</span><span class="p">());</span>
</span></span><span class="line"><span class="cl"><span class="p">},</span> <span class="kc">null</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// MQTT 连接
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">let</span> <span class="nx">mqtt_server</span> <span class="o">=</span> <span class="s2">&#34;mqtt://192.168.1.100&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nx">MQTT</span><span class="p">.</span><span class="nx">connect</span><span class="p">(</span><span class="nx">mqtt_server</span><span class="p">,</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">user</span><span class="o">:</span> <span class="s2">&#34;admin&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">pass</span><span class="o">:</span> <span class="s2">&#34;public&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">on_connect</span><span class="o">:</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">print</span><span class="p">(</span><span class="s2">&#34;MQTT 已连接&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="nx">MQTT</span><span class="p">.</span><span class="nx">sub</span><span class="p">(</span><span class="s2">&#34;home/+/temp&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="nx">on_message</span><span class="o">:</span> <span class="kd">function</span><span class="p">(</span><span class="nx">topic</span><span class="p">,</span> <span class="nx">msg</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">print</span><span class="p">(</span><span class="s2">&#34;主题：&#34;</span><span class="p">,</span> <span class="nx">topic</span><span class="p">,</span> <span class="s2">&#34;消息：&#34;</span><span class="p">,</span> <span class="nx">msg</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// FFI 调用 C 函数
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">let</span> <span class="nx">ffi</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s2">&#34;ffi&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nx">ffi</span><span class="p">.</span><span class="nx">cdef</span><span class="p">([[</span>
</span></span><span class="line"><span class="cl">    <span class="kr">int</span> <span class="nx">printf</span><span class="p">(</span><span class="kr">const</span> <span class="kr">char</span> <span class="o">*</span><span class="nx">fmt</span><span class="p">,</span> <span class="p">...);</span>
</span></span><span class="line"><span class="cl">    <span class="k">void</span> <span class="o">*</span><span class="nx">malloc</span><span class="p">(</span><span class="nx">size_t</span> <span class="nx">size</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">void</span> <span class="nx">free</span><span class="p">(</span><span class="k">void</span> <span class="o">*</span><span class="nx">ptr</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">]]);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">ffi</span><span class="p">.</span><span class="nx">C</span><span class="p">.</span><span class="nx">printf</span><span class="p">(</span><span class="s2">&#34;Hello from C!\n&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="kd">let</span> <span class="nx">ptr</span> <span class="o">=</span> <span class="nx">ffi</span><span class="p">.</span><span class="nx">C</span><span class="p">.</span><span class="nx">malloc</span><span class="p">(</span><span class="mi">100</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nx">ffi</span><span class="p">.</span><span class="nx">C</span><span class="p">.</span><span class="nx">free</span><span class="p">(</span><span class="nx">ptr</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 异步 HTTP 请求
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">HTTP</span><span class="p">.</span><span class="nx">fetch</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">    <span class="nx">url</span><span class="o">:</span> <span class="s2">&#34;http://api.example.com/data&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">method</span><span class="o">:</span> <span class="s2">&#34;GET&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">headers</span><span class="o">:</span> <span class="p">{</span><span class="s2">&#34;Content-Type&#34;</span><span class="o">:</span> <span class="s2">&#34;application/json&#34;</span><span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="nx">success</span><span class="o">:</span> <span class="kd">function</span><span class="p">(</span><span class="nx">body</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="kd">let</span> <span class="nx">data</span> <span class="o">=</span> <span class="nx">JSON</span><span class="p">.</span><span class="nx">parse</span><span class="p">(</span><span class="nx">body</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="nx">print</span><span class="p">(</span><span class="s2">&#34;数据：&#34;</span><span class="p">,</span> <span class="nx">data</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="nx">error</span><span class="o">:</span> <span class="kd">function</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">print</span><span class="p">(</span><span class="s2">&#34;错误：&#34;</span><span class="p">,</span> <span class="nx">err</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span></code></pre></div><h3 id="34-性能测试">3.4 性能测试</h3>
<table>
<thead>
<tr>
<th>操作</th>
<th>MJS</th>
<th>JavaScript (Node)</th>
<th>C</th>
<th>倍数差距</th>
</tr>
</thead>
<tbody>
<tr>
<td>整数运算</td>
<td>0.52s</td>
<td>0.08s</td>
<td>0.02s</td>
<td>26x / 4x</td>
</tr>
<tr>
<td>函数调用</td>
<td>0.68s</td>
<td>0.12s</td>
<td>0.03s</td>
<td>22x / 4x</td>
</tr>
<tr>
<td>JSON 解析</td>
<td>0.35s</td>
<td>0.15s</td>
<td>0.05s</td>
<td>7x / 3x</td>
</tr>
<tr>
<td>内存分配</td>
<td>0.28s</td>
<td>0.18s</td>
<td>0.04s</td>
<td>7x / 4.5x</td>
</tr>
</tbody>
</table>
<h3 id="35-生态系统">3.5 生态系统</h3>
<p><strong>支持平台</strong>：</p>
<ul>
<li>ESP32 / ESP8266</li>
<li>STM32（F1/F4/F7）</li>
<li>CC3200（TI）</li>
<li>Linux / macOS（POSIX）</li>
</ul>
<p><strong>内置库</strong>：</p>
<ul>
<li><code>GPIO</code>、<code>UART</code>、<code>I2C</code>、<code>SPI</code></li>
<li><code>WiFi</code>、<code>MQTT</code>、<code>HTTP</code></li>
<li><code>Timer</code>、<code>File</code>、<code>Crypto</code></li>
<li><code>FFI</code>（调用 C 函数）</li>
</ul>
<h3 id="36-优缺点">3.6 优缺点</h3>
<p><strong>✅ 优点</strong>：</p>
<ul>
<li>JavaScript 语法，Web 开发者友好</li>
<li>超轻量，适合资源受限环境</li>
<li>FFI 强大，易调用 C 函数</li>
<li>内置网络栈，IoT 开发便捷</li>
<li>异步编程模型</li>
</ul>
<p><strong>❌ 缺点</strong>：</p>
<ul>
<li>仅支持 ES5 子集</li>
<li>社区规模小</li>
<li>文档相对较少</li>
<li>不支持最新 JS 特性</li>
</ul>
<h3 id="37-适用场景">3.7 适用场景</h3>
<ul>
<li>✅ IoT 设备（WiFi/蓝牙）</li>
<li>✅ Web 开发者转型嵌入式</li>
<li>✅ 需要快速迭代的原型</li>
<li>✅ 云端 + 设备协同开发</li>
<li>❌ 高性能计算</li>
<li>❌ 离线独立应用</li>
</ul>
<hr>
<h2 id="4-circuitpython">4. CircuitPython</h2>
<h3 id="41-简介">4.1 简介</h3>
<p>CircuitPython 是 Adafruit 基于 MicroPython  fork 的教育友好型 Python 实现。它强调<strong>易用性</strong>和<strong>硬件支持</strong>，特别适合 STEM 教育和创客项目。</p>
<h3 id="42-资源占用">4.2 资源占用</h3>
<table>
<thead>
<tr>
<th>配置</th>
<th>ROM</th>
<th>RAM</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>最小配置</td>
<td>200KB</td>
<td>16KB</td>
<td>核心 + 基础库</td>
</tr>
<tr>
<td>标准配置</td>
<td>350KB</td>
<td>32KB</td>
<td>完整硬件支持</td>
</tr>
<tr>
<td>完整配置</td>
<td>512KB+</td>
<td>64KB+</td>
<td>全部驱动</td>
</tr>
</tbody>
</table>
<h3 id="43-编程方式">4.3 编程方式</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="c1"># 导入库（自动识别硬件）</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">board</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">digitalio</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">time</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># LED 控制（无需配置引脚）</span>
</span></span><span class="line"><span class="cl"><span class="n">led</span> <span class="o">=</span> <span class="n">digitalio</span><span class="o">.</span><span class="n">DigitalInOut</span><span class="p">(</span><span class="n">board</span><span class="o">.</span><span class="n">LED</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">led</span><span class="o">.</span><span class="n">direction</span> <span class="o">=</span> <span class="n">digitalio</span><span class="o">.</span><span class="n">Direction</span><span class="o">.</span><span class="n">OUTPUT</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">while</span> <span class="kc">True</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">led</span><span class="o">.</span><span class="n">value</span> <span class="o">=</span> <span class="kc">True</span>
</span></span><span class="line"><span class="cl">    <span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mf">0.5</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">led</span><span class="o">.</span><span class="n">value</span> <span class="o">=</span> <span class="kc">False</span>
</span></span><span class="line"><span class="cl">    <span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mf">0.5</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># I2C 传感器（自动检测）</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">adafruit_sht31d</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">busio</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">i2c</span> <span class="o">=</span> <span class="n">busio</span><span class="o">.</span><span class="n">I2C</span><span class="p">(</span><span class="n">board</span><span class="o">.</span><span class="n">SCL</span><span class="p">,</span> <span class="n">board</span><span class="o">.</span><span class="n">SDA</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">sensor</span> <span class="o">=</span> <span class="n">adafruit_sht31d</span><span class="o">.</span><span class="n">SHT31D</span><span class="p">(</span><span class="n">i2c</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="s2">&#34;温度：</span><span class="si">%.2f</span><span class="s2">°C&#34;</span> <span class="o">%</span> <span class="n">sensor</span><span class="o">.</span><span class="n">temperature</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="s2">&#34;湿度：</span><span class="si">%.2f%%</span><span class="s2">&#34;</span> <span class="o">%</span> <span class="n">sensor</span><span class="o">.</span><span class="n">humidity</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 显示屏支持</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">displayio</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">terminalio</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">adafruit_display_text</span> <span class="kn">import</span> <span class="n">label</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">text</span> <span class="o">=</span> <span class="n">label</span><span class="o">.</span><span class="n">Label</span><span class="p">(</span><span class="n">terminalio</span><span class="o">.</span><span class="n">FONT</span><span class="p">,</span> <span class="n">text</span><span class="o">=</span><span class="s2">&#34;Hello!&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">text</span><span class="o">.</span><span class="n">x</span> <span class="o">=</span> <span class="mi">10</span>
</span></span><span class="line"><span class="cl"><span class="n">text</span><span class="o">.</span><span class="n">y</span> <span class="o">=</span> <span class="mi">20</span>
</span></span></code></pre></div><h3 id="44-优缺点">4.4 优缺点</h3>
<p><strong>✅ 优点</strong>：</p>
<ul>
<li>即插即用，硬件自动识别</li>
<li>丰富的传感器驱动库</li>
<li>USB 大容量存储，拖拽烧录</li>
<li>教育友好，文档详尽</li>
<li>与 MicroPython 高度兼容</li>
</ul>
<p><strong>❌ 缺点</strong>：</p>
<ul>
<li>ROM 占用最大（~350KB+）</li>
<li>RAM 需求高（32KB+）</li>
<li>启动较慢（2-3 秒）</li>
<li>不适合低成本 MCU</li>
</ul>
<h3 id="45-适用场景">4.5 适用场景</h3>
<ul>
<li>✅ STEM 教育</li>
<li>✅ 创客项目</li>
<li>✅ 快速原型验证</li>
<li>✅ 传感器数据采集</li>
<li>❌ 低成本量产产品</li>
<li>❌ 电池供电设备</li>
</ul>
<hr>
<h2 id="5-espruino">5. Espruino</h2>
<h3 id="51-简介">5.1 简介</h3>
<p>Espruino 是 2013 年由 Gordon Williams 开发的 JavaScript 引擎，专为<strong>低功耗</strong>和<strong>小内存</strong>MCU 设计。它支持在睡眠模式下保持解释器运行。</p>
<h3 id="52-资源占用">5.2 资源占用</h3>
<table>
<thead>
<tr>
<th>配置</th>
<th>ROM</th>
<th>RAM</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>最小配置</td>
<td>80KB</td>
<td>8KB</td>
<td>核心解释器</td>
</tr>
<tr>
<td>标准配置</td>
<td>200KB</td>
<td>16KB</td>
<td>含标准库</td>
</tr>
<tr>
<td>完整配置</td>
<td>300KB</td>
<td>32KB</td>
<td>全部功能</td>
</tr>
</tbody>
</table>
<h3 id="53-编程方式">5.3 编程方式</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="c1">// 低功耗 GPIO
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">var</span> <span class="nx">led</span> <span class="o">=</span> <span class="nx">Pin</span><span class="p">(</span><span class="s2">&#34;A1&#34;</span><span class="p">,</span> <span class="p">{</span><span class="nx">mode</span><span class="o">:</span> <span class="nx">OUTPUT</span><span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 中断处理
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">setWatch</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">e</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">print</span><span class="p">(</span><span class="s2">&#34;按钮按下！时间：&#34;</span><span class="p">,</span> <span class="nx">e</span><span class="p">.</span><span class="nx">time</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">},</span> <span class="nx">Pin</span><span class="p">(</span><span class="s2">&#34;A0&#34;</span><span class="p">),</span> <span class="p">{</span><span class="nx">edge</span><span class="o">:</span> <span class="s2">&#34;rising&#34;</span><span class="p">,</span> <span class="nx">repeat</span><span class="o">:</span> <span class="kc">true</span><span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 低功耗模式
</span></span></span><span class="line"><span class="cl"><span class="c1">// 休眠 10 秒，消耗仅 50μA
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">deepSleep</span><span class="p">(</span><span class="mi">10000</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 图形显示（支持 LCD）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">g</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s2">&#34;Graphics&#34;</span><span class="p">).</span><span class="nx">createLCD</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">    <span class="nx">width</span><span class="o">:</span> <span class="mi">128</span><span class="p">,</span> <span class="nx">height</span><span class="o">:</span> <span class="mi">64</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">buffer</span><span class="o">:</span> <span class="k">new</span> <span class="nx">ArrayBuffer</span><span class="p">(</span><span class="mi">1024</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span><span class="line"><span class="cl"><span class="nx">g</span><span class="p">.</span><span class="nx">drawString</span><span class="p">(</span><span class="s2">&#34;Hello!&#34;</span><span class="p">,</span> <span class="mi">10</span><span class="p">,</span> <span class="mi">10</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nx">g</span><span class="p">.</span><span class="nx">flip</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 蓝牙 BLE
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">NRF</span><span class="p">.</span><span class="nx">setServices</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">    <span class="mh">0x1809</span><span class="o">:</span> <span class="p">{</span>  <span class="c1">// 温度服务
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="mh">0x2A1C</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nx">value</span><span class="o">:</span> <span class="mi">25</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nx">readable</span><span class="o">:</span> <span class="kc">true</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span></code></pre></div><h3 id="54-优缺点">5.4 优缺点</h3>
<p><strong>✅ 优点</strong>：</p>
<ul>
<li>超低功耗（睡眠模式 50μA）</li>
<li>支持多种 MCU（STM32、nRF52、ESP32）</li>
<li>内置图形库</li>
<li>蓝牙 BLE 支持良好</li>
<li>交互式开发（Web IDE）</li>
</ul>
<p><strong>❌ 缺点</strong>：</p>
<ul>
<li>JavaScript 子集有限</li>
<li>性能一般</li>
<li>社区规模较小</li>
<li>文档分散</li>
</ul>
<h3 id="55-适用场景">5.5 适用场景</h3>
<ul>
<li>✅ 可穿戴设备</li>
<li>✅ 电池供电 IoT</li>
<li>✅ 低功耗传感器节点</li>
<li>✅ 蓝牙 BLE 应用</li>
</ul>
<hr>
<h2 id="6-forth">6. Forth</h2>
<h3 id="61-简介">6.1 简介</h3>
<p>Forth 是 1970 年由 Charles Moore 开发的<strong>堆栈式</strong>编程语言。它以<strong>极简</strong>和<strong>高效</strong>著称，曾用于天文望远镜、工业控制器等关键系统。</p>
<h3 id="62-资源占用">6.2 资源占用</h3>
<table>
<thead>
<tr>
<th>配置</th>
<th>ROM</th>
<th>RAM</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>最小配置</td>
<td>2KB</td>
<td>512B</td>
<td>核心解释器</td>
</tr>
<tr>
<td>标准配置</td>
<td>8KB</td>
<td>2KB</td>
<td>含基本词汇</td>
</tr>
<tr>
<td>完整配置</td>
<td>16KB</td>
<td>4KB</td>
<td>浮点 + 文件</td>
</tr>
</tbody>
</table>
<h3 id="63-编程方式">6.3 编程方式</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-forth" data-lang="forth"><span class="line"><span class="cl"><span class="c1">\ GPIO 控制（以 Mecrisp-Stellaris 为例）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kn">:</span> <span class="nc">led-init</span>  <span class="c1">( -- )</span>
</span></span><span class="line"><span class="cl">  <span class="mi">5</span> <span class="nf">gpio-mode-output</span> <span class="k">; </span> <span class="c1">\ PA5 设为输出
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="kn">:</span> <span class="nc">led-on</span>  <span class="c1">( -- )</span>
</span></span><span class="line"><span class="cl">  <span class="mi">5</span> <span class="nf">gpio-set</span> <span class="k">;
</span></span></span><span class="line"><span class="cl"><span class="k"></span>
</span></span><span class="line"><span class="cl"><span class="kn">:</span> <span class="nc">led-off</span>  <span class="c1">( -- )</span>
</span></span><span class="line"><span class="cl">  <span class="mi">5</span> <span class="nf">gpio-clear</span> <span class="k">;
</span></span></span><span class="line"><span class="cl"><span class="k"></span>
</span></span><span class="line"><span class="cl"><span class="c1">\ 闪烁 LED
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kn">:</span> <span class="nc">blink</span>  <span class="c1">( -- )</span>
</span></span><span class="line"><span class="cl">  <span class="k">begin
</span></span></span><span class="line"><span class="cl"><span class="k"></span>    <span class="nf">led-on</span>  <span class="mi">1000</span> <span class="k">ms
</span></span></span><span class="line"><span class="cl"><span class="k"></span>    <span class="nf">led-off</span> <span class="mi">1000</span> <span class="k">ms
</span></span></span><span class="line"><span class="cl"><span class="k"></span>  <span class="k">again ;
</span></span></span><span class="line"><span class="cl"><span class="k"></span>
</span></span><span class="line"><span class="cl"><span class="c1">\ 堆栈操作
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kn">:</span> <span class="nc">square</span>  <span class="c1">( n -- n² )</span>
</span></span><span class="line"><span class="cl">  <span class="k">dup * ;
</span></span></span><span class="line"><span class="cl"><span class="k"></span>
</span></span><span class="line"><span class="cl"><span class="kn">:</span> <span class="nc">average</span>  <span class="c1">( a b -- avg )</span>
</span></span><span class="line"><span class="cl">  <span class="k">+ </span><span class="mi">2</span> <span class="k">/ ;
</span></span></span><span class="line"><span class="cl"><span class="k"></span>
</span></span><span class="line"><span class="cl"><span class="c1">\ 使用
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="mi">5</span> <span class="nf">square</span> <span class="k">. </span>     <span class="c1">\ 输出：25
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="mi">10</span> <span class="mi">20</span> <span class="nf">average</span> <span class="k">. </span><span class="c1">\ 输出：15
</span></span></span></code></pre></div><h3 id="64-优缺点">6.4 优缺点</h3>
<p><strong>✅ 优点</strong>：</p>
<ul>
<li>体积极小（KB 级）</li>
<li>执行效率高（接近汇编）</li>
<li>交互式开发（REPL）</li>
<li>可扩展性强（自定义词汇）</li>
<li>适合实时控制</li>
</ul>
<p><strong>❌ 缺点</strong>：</p>
<ul>
<li>逆波兰表示法（学习曲线陡）</li>
<li>代码可读性差</li>
<li>缺乏现代语言特性</li>
<li>社区老龄化</li>
</ul>
<h3 id="65-适用场景">6.5 适用场景</h3>
<ul>
<li>✅ 超低资源环境（&lt;8KB ROM）</li>
<li>✅ 实时控制</li>
<li>✅ 引导加载程序</li>
<li>✅ 工业控制器</li>
<li>❌ 快速原型开发</li>
<li>❌ 团队协作项目</li>
</ul>
<hr>
<h2 id="7-tinybasic">7. TinyBasic</h2>
<h3 id="71-简介">7.1 简介</h3>
<p>TinyBasic 是 1975 年设计的极简 BASIC 解释器，最初用于 4KB 内存的早期微机。现代变种（如 TinyBasic Plus）支持 Arduino 等平台。</p>
<h3 id="72-资源占用">7.2 资源占用</h3>
<table>
<thead>
<tr>
<th>配置</th>
<th>ROM</th>
<th>RAM</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>最小配置</td>
<td>2KB</td>
<td>256B</td>
<td>核心解释器</td>
</tr>
<tr>
<td>标准配置</td>
<td>4KB</td>
<td>512B</td>
<td>含基本命令</td>
</tr>
<tr>
<td>扩展版</td>
<td>8KB</td>
<td>1KB</td>
<td>支持字符串</td>
</tr>
</tbody>
</table>
<h3 id="73-编程方式">7.3 编程方式</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-basic" data-lang="basic"><span class="line"><span class="cl"><span class="c1">&#39; LED 闪烁</span>
</span></span><span class="line"><span class="cl"><span class="nl">10</span><span class="w"> </span><span class="vg">PINMODE</span><span class="w"> </span><span class="il">2</span><span class="p">,</span><span class="w"> </span><span class="k">OUTPUT</span>
</span></span><span class="line"><span class="cl"><span class="nl">20</span><span class="w"> </span><span class="vg">HIGH</span><span class="w"> </span><span class="il">2</span>
</span></span><span class="line"><span class="cl"><span class="nl">30</span><span class="w"> </span><span class="vg">DELAY</span><span class="w"> </span><span class="il">1000</span>
</span></span><span class="line"><span class="cl"><span class="nl">40</span><span class="w"> </span><span class="vg">LOW</span><span class="w"> </span><span class="il">2</span>
</span></span><span class="line"><span class="cl"><span class="nl">50</span><span class="w"> </span><span class="vg">DELAY</span><span class="w"> </span><span class="il">1000</span>
</span></span><span class="line"><span class="cl"><span class="nl">60</span><span class="w"> </span><span class="kr">GOTO</span><span class="w"> </span><span class="nl">20</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">&#39; 读取模拟输入</span>
</span></span><span class="line"><span class="cl"><span class="nl">10</span><span class="w"> </span><span class="vg">X</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="vg">ANALOG</span><span class="p">(</span><span class="il">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="nl">20</span><span class="w"> </span><span class="kr">PRINT</span><span class="w"> </span><span class="s2">&#34;传感器值：&#34;</span><span class="p">;</span><span class="w"> </span><span class="vg">X</span>
</span></span><span class="line"><span class="cl"><span class="nl">30</span><span class="w"> </span><span class="kr">IF</span><span class="w"> </span><span class="vg">X</span><span class="w"> </span><span class="o">&gt;</span><span class="w"> </span><span class="il">500</span><span class="w"> </span><span class="kr">THEN</span><span class="w"> </span><span class="kr">GOTO</span><span class="w"> </span><span class="nl">100</span>
</span></span><span class="line"><span class="cl"><span class="nl">40</span><span class="w"> </span><span class="kr">GOTO</span><span class="w"> </span><span class="nl">10</span>
</span></span><span class="line"><span class="cl"><span class="nl">100</span><span class="w"> </span><span class="kr">PRINT</span><span class="w"> </span><span class="s2">&#34;超过阈值！&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nl">110</span><span class="w"> </span><span class="kr">END</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">&#39; 简单计算</span>
</span></span><span class="line"><span class="cl"><span class="nl">10</span><span class="w"> </span><span class="kr">FOR</span><span class="w"> </span><span class="vg">I</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="il">1</span><span class="w"> </span><span class="k">TO</span><span class="w"> </span><span class="il">10</span>
</span></span><span class="line"><span class="cl"><span class="nl">20</span><span class="w"> </span><span class="kr">PRINT</span><span class="w"> </span><span class="vg">I</span><span class="p">,</span><span class="w"> </span><span class="vg">I</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="vg">I</span>
</span></span><span class="line"><span class="cl"><span class="nl">30</span><span class="w"> </span><span class="kr">NEXT</span><span class="w"> </span><span class="vg">I</span>
</span></span></code></pre></div><h3 id="74-优缺点">7.4 优缺点</h3>
<p><strong>✅ 优点</strong>：</p>
<ul>
<li>语法极其简单</li>
<li>资源占用最小</li>
<li>适合教学入门</li>
<li>易于实现</li>
</ul>
<p><strong>❌ 缺点</strong>：</p>
<ul>
<li>功能极其有限</li>
<li>无结构化编程</li>
<li>不支持现代特性</li>
<li>性能差</li>
</ul>
<h3 id="75-适用场景">7.5 适用场景</h3>
<ul>
<li>✅ 编程入门教学</li>
<li>✅ 复古计算项目</li>
<li>✅ 极简嵌入式应用</li>
<li>❌ 实际产品开发</li>
</ul>
<hr>
<h2 id="8-scheme-嵌入式-lisp">8. Scheme (嵌入式 Lisp)</h2>
<h3 id="81-简介">8.1 简介</h3>
<p>Scheme 是 Lisp 的函数式方言，以<strong>简洁</strong>和<strong>优雅</strong>著称。嵌入式实现（如 Chibi-Scheme、Picrin）可在资源受限环境运行。</p>
<h3 id="82-资源占用">8.2 资源占用</h3>
<table>
<thead>
<tr>
<th>配置</th>
<th>ROM</th>
<th>RAM</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>Chibi-Scheme</td>
<td>80KB</td>
<td>16KB</td>
<td>含 R7RS 标准</td>
</tr>
<tr>
<td>Picrin</td>
<td>60KB</td>
<td>8KB</td>
<td>极简 Scheme</td>
</tr>
<tr>
<td>Gauche</td>
<td>150KB</td>
<td>32KB</td>
<td>功能完整</td>
</tr>
</tbody>
</table>
<h3 id="83-编程方式">8.3 编程方式</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-scheme" data-lang="scheme"><span class="line"><span class="cl"><span class="c1">;; GPIO 控制（假设嵌入式库）</span>
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="k">define </span><span class="nv">led</span> <span class="p">(</span><span class="nf">make-gpio</span> <span class="mi">2</span> <span class="ss">&#39;output</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="k">define </span><span class="p">(</span><span class="nf">blink</span> <span class="nv">n</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="p">(</span><span class="nf">when</span> <span class="p">(</span><span class="nb">&gt; </span><span class="nv">n</span> <span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">(</span><span class="nf">gpio-write</span> <span class="nv">led</span> <span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">(</span><span class="nf">sleep</span> <span class="mi">1000</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">(</span><span class="nf">gpio-write</span> <span class="nv">led</span> <span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">(</span><span class="nf">sleep</span> <span class="mi">1000</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">(</span><span class="nf">blink</span> <span class="p">(</span><span class="nb">- </span><span class="nv">n</span> <span class="mi">1</span><span class="p">))))</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="nf">blink</span> <span class="mi">10</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">;; 函数式编程</span>
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="k">define </span><span class="p">(</span><span class="nf">factorial</span> <span class="nv">n</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="p">(</span><span class="k">if </span><span class="p">(</span><span class="nb">&lt;= </span><span class="nv">n</span> <span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="mi">1</span>
</span></span><span class="line"><span class="cl">      <span class="p">(</span><span class="nb">* </span><span class="nv">n</span> <span class="p">(</span><span class="nf">factorial</span> <span class="p">(</span><span class="nb">- </span><span class="nv">n</span> <span class="mi">1</span><span class="p">)))))</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="nb">display </span><span class="p">(</span><span class="nf">factorial</span> <span class="mi">10</span><span class="p">))</span>  <span class="c1">; 3628800</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">;; 宏系统</span>
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="k">define-syntax </span><span class="nv">when</span>
</span></span><span class="line"><span class="cl">  <span class="p">(</span><span class="k">syntax-rules </span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="p">((</span><span class="nf">when</span> <span class="nv">test</span> <span class="nv">body</span> <span class="o">...</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">     <span class="p">(</span><span class="k">if </span><span class="nv">test</span> <span class="p">(</span><span class="k">begin </span><span class="nv">body</span> <span class="o">...</span><span class="p">)))))</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">;; 高阶函数</span>
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="k">define </span><span class="nv">sensors</span> <span class="o">&#39;</span><span class="p">(</span><span class="mi">1</span> <span class="mi">2</span> <span class="mi">3</span> <span class="mi">4</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="nb">map </span><span class="p">(</span><span class="k">lambda </span><span class="p">(</span><span class="nf">pin</span><span class="p">)</span> <span class="p">(</span><span class="nf">adc-read</span> <span class="nv">pin</span><span class="p">))</span> <span class="nv">sensors</span><span class="p">)</span>
</span></span></code></pre></div><h3 id="84-优缺点">8.4 优缺点</h3>
<p><strong>✅ 优点</strong>：</p>
<ul>
<li>函数式编程范式</li>
<li>宏系统强大</li>
<li>代码简洁优雅</li>
<li>适合元编程</li>
</ul>
<p><strong>❌ 缺点</strong>：</p>
<ul>
<li>括号语法（Lisp 风格）</li>
<li>性能一般</li>
<li>硬件库较少</li>
<li>学习曲线陡</li>
</ul>
<h3 id="85-适用场景">8.5 适用场景</h3>
<ul>
<li>✅ 教育与研究</li>
<li>✅ 配置语言</li>
<li>✅ 元编程需求</li>
<li>✅ 函数式编程爱好者</li>
</ul>
<hr>
<h2 id="综合对比">综合对比</h2>
<h3 id="性能排行">性能排行</h3>
<table>
<thead>
<tr>
<th>排名</th>
<th>语言</th>
<th>相对性能</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td><strong>Forth</strong></td>
<td>95%</td>
<td>接近 C 语言</td>
</tr>
<tr>
<td>2</td>
<td><strong>LuaJIT</strong></td>
<td>85%</td>
<td>即时编译</td>
</tr>
<tr>
<td>3</td>
<td><strong>Lua 5.4</strong></td>
<td>45%</td>
<td>优化良好</td>
</tr>
<tr>
<td>4</td>
<td><strong>MJS</strong></td>
<td>40%</td>
<td>字节码解释</td>
</tr>
<tr>
<td>5</td>
<td><strong>MicroPython</strong></td>
<td>25%</td>
<td>动态类型开销</td>
</tr>
<tr>
<td>6</td>
<td><strong>Scheme</strong></td>
<td>20%</td>
<td>函数式开销</td>
</tr>
<tr>
<td>7</td>
<td><strong>CircuitPython</strong></td>
<td>20%</td>
<td>同 MicroPython</td>
</tr>
<tr>
<td>8</td>
<td><strong>TinyBasic</strong></td>
<td>5%</td>
<td>极简实现</td>
</tr>
</tbody>
</table>
<h3 id="资源占用排行从小到大">资源占用排行（从小到大）</h3>
<table>
<thead>
<tr>
<th>排名</th>
<th>语言</th>
<th>ROM</th>
<th>RAM</th>
<th>最低 MCU</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td><strong>TinyBasic</strong></td>
<td>4KB</td>
<td>512B</td>
<td>ATmega328</td>
</tr>
<tr>
<td>2</td>
<td><strong>Forth</strong></td>
<td>8KB</td>
<td>1KB</td>
<td>MSP430</td>
</tr>
<tr>
<td>3</td>
<td><strong>MJS</strong></td>
<td>60KB</td>
<td>8KB</td>
<td>ESP32-C3</td>
</tr>
<tr>
<td>4</td>
<td><strong>Lua</strong></td>
<td>80KB</td>
<td>8KB</td>
<td>STM32F1</td>
</tr>
<tr>
<td>5</td>
<td><strong>Scheme</strong></td>
<td>120KB</td>
<td>16KB</td>
<td>STM32F4</td>
</tr>
<tr>
<td>6</td>
<td><strong>MicroPython</strong></td>
<td>256KB</td>
<td>16KB</td>
<td>STM32F4</td>
</tr>
<tr>
<td>7</td>
<td><strong>Espruino</strong></td>
<td>200KB</td>
<td>16KB</td>
<td>nRF52</td>
</tr>
<tr>
<td>8</td>
<td><strong>CircuitPython</strong></td>
<td>350KB</td>
<td>32KB</td>
<td>SAMD51</td>
</tr>
</tbody>
</table>
<h3 id="开发效率排行">开发效率排行</h3>
<table>
<thead>
<tr>
<th>排名</th>
<th>语言</th>
<th>学习曲线</th>
<th>代码量</th>
<th>调试难度</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td><strong>Python</strong></td>
<td>低</td>
<td>少</td>
<td>容易</td>
</tr>
<tr>
<td>2</td>
<td><strong>JavaScript</strong></td>
<td>低</td>
<td>少</td>
<td>容易</td>
</tr>
<tr>
<td>3</td>
<td><strong>Lua</strong></td>
<td>中</td>
<td>少</td>
<td>中等</td>
</tr>
<tr>
<td>4</td>
<td><strong>BASIC</strong></td>
<td>低</td>
<td>中</td>
<td>容易</td>
</tr>
<tr>
<td>5</td>
<td><strong>Scheme</strong></td>
<td>高</td>
<td>少</td>
<td>中等</td>
</tr>
<tr>
<td>6</td>
<td><strong>Forth</strong></td>
<td>高</td>
<td>少</td>
<td>困难</td>
</tr>
</tbody>
</table>
<h3 id="生态系统排行">生态系统排行</h3>
<table>
<thead>
<tr>
<th>排名</th>
<th>语言</th>
<th>库数量</th>
<th>社区活跃度</th>
<th>文档质量</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td><strong>MicroPython</strong></td>
<td>⭐⭐⭐⭐⭐</td>
<td>⭐⭐⭐⭐⭐</td>
<td>⭐⭐⭐⭐⭐</td>
</tr>
<tr>
<td>2</td>
<td><strong>CircuitPython</strong></td>
<td>⭐⭐⭐⭐⭐</td>
<td>⭐⭐⭐⭐</td>
<td>⭐⭐⭐⭐⭐</td>
</tr>
<tr>
<td>3</td>
<td><strong>Lua</strong></td>
<td>⭐⭐⭐⭐</td>
<td>⭐⭐⭐⭐</td>
<td>⭐⭐⭐⭐</td>
</tr>
<tr>
<td>4</td>
<td><strong>JavaScript</strong></td>
<td>⭐⭐⭐⭐</td>
<td>⭐⭐⭐</td>
<td>⭐⭐⭐</td>
</tr>
<tr>
<td>5</td>
<td><strong>Forth</strong></td>
<td>⭐⭐</td>
<td>⭐⭐</td>
<td>⭐⭐</td>
</tr>
<tr>
<td>6</td>
<td><strong>Scheme</strong></td>
<td>⭐⭐</td>
<td>⭐⭐</td>
<td>⭐⭐⭐</td>
</tr>
<tr>
<td>7</td>
<td><strong>TinyBasic</strong></td>
<td>⭐</td>
<td>⭐</td>
<td>⭐</td>
</tr>
</tbody>
</table>
<hr>
<h2 id="选择建议">选择建议</h2>
<h3 id="按资源选择">按资源选择</h3>
<table>
<thead>
<tr>
<th>资源范围</th>
<th>推荐语言</th>
</tr>
</thead>
<tbody>
<tr>
<td>ROM &lt; 16KB, RAM &lt; 2KB</td>
<td>Forth, TinyBasic</td>
</tr>
<tr>
<td>ROM 16-128KB, RAM 2-16KB</td>
<td>Lua, MJS</td>
</tr>
<tr>
<td>ROM 128-512KB, RAM 16-64KB</td>
<td>MicroPython, Espruino, Scheme</td>
</tr>
<tr>
<td>ROM &gt; 512KB, RAM &gt; 64KB</td>
<td>CircuitPython, MicroPython (完整版)</td>
</tr>
</tbody>
</table>
<h3 id="按应用选择">按应用选择</h3>
<table>
<thead>
<tr>
<th>应用场景</th>
<th>推荐语言</th>
<th>理由</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>快速原型</strong></td>
<td>MicroPython</td>
<td>开发效率最高</td>
</tr>
<tr>
<td><strong>IoT 设备</strong></td>
<td>MJS / Lua</td>
<td>网络支持好，资源占用低</td>
</tr>
<tr>
<td><strong>教育学习</strong></td>
<td>CircuitPython</td>
<td>硬件自动识别，文档完善</td>
</tr>
<tr>
<td><strong>超低功耗</strong></td>
<td>Espruino / Forth</td>
<td>睡眠模式优秀</td>
</tr>
<tr>
<td><strong>实时控制</strong></td>
<td>Forth / Lua</td>
<td>性能接近 C</td>
</tr>
<tr>
<td><strong>游戏脚本</strong></td>
<td>Lua</td>
<td>游戏行业成熟</td>
</tr>
<tr>
<td><strong>Web 开发者</strong></td>
<td>MJS / Espruino</td>
<td>JavaScript 语法</td>
</tr>
<tr>
<td><strong>函数式编程</strong></td>
<td>Scheme</td>
<td>Lisp 范式</td>
</tr>
<tr>
<td><strong>复古项目</strong></td>
<td>TinyBasic / Forth</td>
<td>历史意义</td>
</tr>
</tbody>
</table>
<h3 id="按-mcu-选择">按 MCU 选择</h3>
<table>
<thead>
<tr>
<th>MCU 系列</th>
<th>推荐语言</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>ESP32</strong></td>
<td>MicroPython, MJS, Lua, CircuitPython</td>
</tr>
<tr>
<td><strong>ESP8266</strong></td>
<td>MicroPython, Lua (NodeMCU)</td>
</tr>
<tr>
<td><strong>STM32F1</strong></td>
<td>Lua, Forth, MJS</td>
</tr>
<tr>
<td><strong>STM32F4/F7</strong></td>
<td>MicroPython, Lua, MJS, Scheme</td>
</tr>
<tr>
<td><strong>RP2040</strong></td>
<td>MicroPython, CircuitPython</td>
</tr>
<tr>
<td><strong>nRF52</strong></td>
<td>Espruino, MicroPython</td>
</tr>
<tr>
<td><strong>AVR (Arduino)</strong></td>
<td>TinyBasic, Forth</td>
</tr>
<tr>
<td><strong>MSP430</strong></td>
<td>Forth, TinyBasic</td>
</tr>
</tbody>
</table>
<hr>
<h2 id="实际项目案例">实际项目案例</h2>
<h3 id="案例-1智能家居传感器esp32">案例 1：智能家居传感器（ESP32）</h3>
<p><strong>需求</strong>：温湿度采集 + MQTT 上报 + 低功耗</p>
<p><strong>选择</strong>：MicroPython</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">machine</span><span class="o">,</span> <span class="nn">network</span><span class="o">,</span> <span class="nn">time</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">umqtt.simple</span> <span class="kn">import</span> <span class="n">MQTTClient</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">dht</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 传感器</span>
</span></span><span class="line"><span class="cl"><span class="n">sensor</span> <span class="o">=</span> <span class="n">dht</span><span class="o">.</span><span class="n">DHT22</span><span class="p">(</span><span class="n">machine</span><span class="o">.</span><span class="n">Pin</span><span class="p">(</span><span class="mi">4</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># WiFi</span>
</span></span><span class="line"><span class="cl"><span class="n">wlan</span> <span class="o">=</span> <span class="n">network</span><span class="o">.</span><span class="n">WLAN</span><span class="p">(</span><span class="n">network</span><span class="o">.</span><span class="n">STA_IF</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">wlan</span><span class="o">.</span><span class="n">active</span><span class="p">(</span><span class="kc">True</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">wlan</span><span class="o">.</span><span class="n">connect</span><span class="p">(</span><span class="s1">&#39;MyWiFi&#39;</span><span class="p">,</span> <span class="s1">&#39;password&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># MQTT</span>
</span></span><span class="line"><span class="cl"><span class="n">client</span> <span class="o">=</span> <span class="n">MQTTClient</span><span class="p">(</span><span class="s1">&#39;esp32-sensor&#39;</span><span class="p">,</span> <span class="s1">&#39;192.168.1.100&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">client</span><span class="o">.</span><span class="n">connect</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">while</span> <span class="kc">True</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">sensor</span><span class="o">.</span><span class="n">measure</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="n">temp</span> <span class="o">=</span> <span class="n">sensor</span><span class="o">.</span><span class="n">temperature</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="n">humid</span> <span class="o">=</span> <span class="n">sensor</span><span class="o">.</span><span class="n">humidity</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="n">msg</span> <span class="o">=</span> <span class="s1">&#39;{&#34;temp&#34;:</span><span class="si">%.2f</span><span class="s1">,&#34;humid&#34;:</span><span class="si">%.2f</span><span class="s1">}&#39;</span> <span class="o">%</span> <span class="p">(</span><span class="n">temp</span><span class="p">,</span> <span class="n">humid</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">client</span><span class="o">.</span><span class="n">publish</span><span class="p">(</span><span class="s1">&#39;home/sensor/data&#39;</span><span class="p">,</span> <span class="n">msg</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1"># 深度睡眠 10 分钟</span>
</span></span><span class="line"><span class="cl">    <span class="n">machine</span><span class="o">.</span><span class="n">deepsleep</span><span class="p">(</span><span class="mi">600000</span><span class="p">)</span>
</span></span></code></pre></div><h3 id="案例-2工业控制器stm32f4">案例 2：工业控制器（STM32F4）</h3>
<p><strong>需求</strong>：实时控制 + 多任务 + C 语言集成</p>
<p><strong>选择</strong>：Lua</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-lua" data-lang="lua"><span class="line"><span class="cl"><span class="c1">-- 实时 PID 控制</span>
</span></span><span class="line"><span class="cl"><span class="kr">function</span> <span class="nf">pid_control</span><span class="p">(</span><span class="n">setpoint</span><span class="p">,</span> <span class="n">measurement</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="kd">local</span> <span class="n">error</span> <span class="o">=</span> <span class="n">setpoint</span> <span class="o">-</span> <span class="n">measurement</span>
</span></span><span class="line"><span class="cl">    <span class="n">integral</span> <span class="o">=</span> <span class="n">integral</span> <span class="o">+</span> <span class="n">error</span>
</span></span><span class="line"><span class="cl">    <span class="kd">local</span> <span class="n">derivative</span> <span class="o">=</span> <span class="n">error</span> <span class="o">-</span> <span class="n">last_error</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="kd">local</span> <span class="n">output</span> <span class="o">=</span> <span class="n">Kp</span> <span class="o">*</span> <span class="n">error</span> <span class="o">+</span> <span class="n">Ki</span> <span class="o">*</span> <span class="n">integral</span> <span class="o">+</span> <span class="n">Kd</span> <span class="o">*</span> <span class="n">derivative</span>
</span></span><span class="line"><span class="cl">    <span class="n">last_error</span> <span class="o">=</span> <span class="n">error</span>
</span></span><span class="line"><span class="cl">    <span class="kr">return</span> <span class="n">output</span>
</span></span><span class="line"><span class="cl"><span class="kr">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">-- C 语言调用 Lua</span>
</span></span><span class="line"><span class="cl"><span class="c1">-- 在 C 代码中：</span>
</span></span><span class="line"><span class="cl"><span class="n">lua_getglobal</span><span class="p">(</span><span class="n">L</span><span class="p">,</span> <span class="s2">&#34;pid_control&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="n">lua_pushnumber</span><span class="p">(</span><span class="n">L</span><span class="p">,</span> <span class="n">setpoint</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="n">lua_pushnumber</span><span class="p">(</span><span class="n">L</span><span class="p">,</span> <span class="n">measurement</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="n">lua_pcall</span><span class="p">(</span><span class="n">L</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="n">double</span> <span class="n">output</span> <span class="o">=</span> <span class="n">lua_tonumber</span><span class="p">(</span><span class="n">L</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">);</span>
</span></span></code></pre></div><h3 id="案例-3可穿戴设备nrf52">案例 3：可穿戴设备（nRF52）</h3>
<p><strong>需求</strong>：超低功耗 + 蓝牙 BLE + 快速开发</p>
<p><strong>选择</strong>：Espruino</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="c1">// 蓝牙心率服务
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">NRF</span><span class="p">.</span><span class="nx">setServices</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">    <span class="mh">0x180D</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="mh">0x2A37</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nx">value</span><span class="o">:</span> <span class="p">[</span><span class="mi">0</span><span class="p">,</span> <span class="mi">72</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">            <span class="nx">readable</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nx">notify</span><span class="o">:</span> <span class="kc">true</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 低功耗模式
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">setInterval</span><span class="p">(</span><span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">var</span> <span class="nx">heartRate</span> <span class="o">=</span> <span class="nx">readHeartRate</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="nx">NRF</span><span class="p">.</span><span class="nx">updateServices</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">        <span class="mh">0x180D</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="mh">0x2A37</span><span class="o">:</span> <span class="p">{</span> <span class="nx">value</span><span class="o">:</span> <span class="p">[</span><span class="mi">0</span><span class="p">,</span> <span class="nx">heartRate</span><span class="p">],</span> <span class="nx">notify</span><span class="o">:</span> <span class="kc">true</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">});</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 睡眠 1 秒，消耗仅 50μA
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nx">deepSleep</span><span class="p">(</span><span class="mi">1000</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">},</span> <span class="mi">1000</span><span class="p">);</span>
</span></span></code></pre></div><hr>
<h2 id="未来趋势">未来趋势</h2>
<h3 id="1-rust-嵌入式脚本">1. Rust 嵌入式脚本</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="c1">// rlua - Rust 绑定 Lua
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">use</span><span class="w"> </span><span class="n">rlua</span>::<span class="p">{</span><span class="n">Lua</span><span class="p">,</span><span class="w"> </span><span class="nb">Result</span><span class="p">};</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">fn</span> <span class="nf">main</span><span class="p">()</span><span class="w"> </span>-&gt; <span class="nb">Result</span><span class="o">&lt;</span><span class="p">()</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kd">let</span><span class="w"> </span><span class="n">lua</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Lua</span>::<span class="n">new</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">lua</span><span class="p">.</span><span class="n">context</span><span class="p">(</span><span class="o">|</span><span class="n">lua</span><span class="o">|</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="n">lua</span><span class="p">.</span><span class="n">globals</span><span class="p">().</span><span class="n">set</span><span class="p">(</span><span class="s">&#34;print&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">lua</span><span class="p">.</span><span class="n">create_function</span><span class="p">(</span><span class="o">|</span><span class="n">_</span><span class="p">,</span><span class="w"> </span><span class="n">msg</span>: <span class="nb">String</span><span class="o">|</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="fm">println!</span><span class="p">(</span><span class="s">&#34;Lua: </span><span class="si">{}</span><span class="s">&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">msg</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nb">Ok</span><span class="p">(())</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="p">})</span><span class="o">?</span><span class="p">)</span><span class="o">?</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="n">lua</span><span class="p">.</span><span class="n">load</span><span class="p">(</span><span class="s">&#34;print(&#39;Hello from Rust!&#39;)&#34;</span><span class="p">).</span><span class="n">exec</span><span class="p">()</span><span class="o">?</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nb">Ok</span><span class="p">(())</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">})</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><h3 id="2-webassembly-嵌入式">2. WebAssembly 嵌入式</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="c1">// WASM 在 MCU 上运行
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">use</span><span class="w"> </span><span class="n">wasmi</span>::<span class="o">*</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kd">let</span><span class="w"> </span><span class="n">module</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Module</span>::<span class="n">from_buffer</span><span class="p">(</span><span class="n">wasm_binary</span><span class="p">)</span><span class="o">?</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kd">let</span><span class="w"> </span><span class="n">instance</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">ModuleInstance</span>::<span class="n">new</span><span class="p">(</span><span class="o">&amp;</span><span class="n">module</span><span class="p">,</span><span class="w"> </span><span class="o">&amp;</span><span class="n">ImportsBuilder</span>::<span class="n">default</span><span class="p">())</span><span class="o">?</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="n">instance</span><span class="p">.</span><span class="n">invoke_export</span><span class="p">(</span><span class="s">&#34;main&#34;</span><span class="p">,</span><span class="w"> </span><span class="o">&amp;</span><span class="p">[],</span><span class="w"> </span><span class="o">&amp;</span><span class="k">mut</span><span class="w"> </span><span class="n">NopExternals</span><span class="p">)</span><span class="o">?</span><span class="p">;</span><span class="w">
</span></span></span></code></pre></div><h3 id="3-ai-模型部署">3. AI 模型部署</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="c1"># MicroPython + TensorFlow Lite</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">tflite</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">interpreter</span> <span class="o">=</span> <span class="n">tflite</span><span class="o">.</span><span class="n">Interpreter</span><span class="p">(</span><span class="n">model_path</span><span class="o">=</span><span class="s2">&#34;model.tflite&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">interpreter</span><span class="o">.</span><span class="n">allocate_tensors</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 运行推理</span>
</span></span><span class="line"><span class="cl"><span class="n">interpreter</span><span class="o">.</span><span class="n">set_tensor</span><span class="p">(</span><span class="n">input_index</span><span class="p">,</span> <span class="n">sensor_data</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">interpreter</span><span class="o">.</span><span class="n">invoke</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="n">result</span> <span class="o">=</span> <span class="n">interpreter</span><span class="o">.</span><span class="n">get_tensor</span><span class="p">(</span><span class="n">output_index</span><span class="p">)</span>
</span></span></code></pre></div><hr>
<h2 id="总结">总结</h2>
<h3 id="最终推荐">最终推荐</h3>
<table>
<thead>
<tr>
<th>需求优先级</th>
<th>首选</th>
<th>备选</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>开发效率</strong></td>
<td>MicroPython</td>
<td>CircuitPython</td>
</tr>
<tr>
<td><strong>性能</strong></td>
<td>Forth</td>
<td>LuaJIT</td>
</tr>
<tr>
<td><strong>资源占用</strong></td>
<td>Forth</td>
<td>MJS</td>
</tr>
<tr>
<td><strong>生态系统</strong></td>
<td>MicroPython</td>
<td>Lua</td>
</tr>
<tr>
<td><strong>低功耗</strong></td>
<td>Espruino</td>
<td>Forth</td>
</tr>
<tr>
<td><strong>IoT 开发</strong></td>
<td>MJS</td>
<td>MicroPython</td>
</tr>
<tr>
<td><strong>教育学习</strong></td>
<td>CircuitPython</td>
<td>MicroPython</td>
</tr>
<tr>
<td><strong>C 语言集成</strong></td>
<td>Lua</td>
<td>MJS</td>
</tr>
</tbody>
</table>
<h3 id="决策树">决策树</h3>
<p><strong>根据应用需求选择</strong>：</p>
<table>
<thead>
<tr>
<th>需求</th>
<th>首选</th>
<th>备选</th>
</tr>
</thead>
<tbody>
<tr>
<td>快速原型</td>
<td>MicroPython</td>
<td>CircuitPython</td>
</tr>
<tr>
<td>量产产品</td>
<td>Lua / MJS</td>
<td>MicroPython</td>
</tr>
<tr>
<td>超低功耗</td>
<td>Espruino</td>
<td>Forth</td>
</tr>
<tr>
<td>实时控制</td>
<td>Forth</td>
<td>Lua</td>
</tr>
<tr>
<td>IoT 连接</td>
<td>MJS</td>
<td>MicroPython</td>
</tr>
<tr>
<td>教育用途</td>
<td>CircuitPython</td>
<td>MicroPython</td>
</tr>
<tr>
<td>资源极度受限</td>
<td>Forth</td>
<td>TinyBasic</td>
</tr>
</tbody>
</table>
<p>没有最好的语言，只有最适合的语言。根据你的具体需求（资源、性能、开发效率、生态系统）做出权衡，选择最合适的工具。</p>
<hr>
<p><em>本文基于 2026 年主流嵌入式脚本语言实测数据编写，测试平台包括 ESP32-S3、STM32F407、nRF52840、RP2040。</em></p>
<h2 id="参考资料">参考资料</h2>
<ul>
<li>MicroPython 官方文档：https://docs.micropython.org/</li>
<li>Lua 5.4 参考手册：https://www.lua.org/manual/5.4/</li>
<li>MJS 引擎文档：https://github.com/cesanta/mjs</li>
<li>CircuitPython 学习指南：https://learn.adafruit.com/welcome-to-circuitpython</li>
<li>Espruino 官方文档：https://www.espruino.com/Documentation</li>
<li>Forth 兴趣小组：http://forth.org/</li>
<li>Scheme R7RS 标准：https://small.r7rs.org/</li>
<li>Embedded Scripting Language Comparison - IEEE 2025</li>
</ul>
]]></content:encoded></item><item><title>RISC-V 嵌入式开发完全指南 2026</title><link>https://tech-snippets.xyz/posts/risc-v-embedded-development-guide-2026/</link><pubDate>Thu, 02 Apr 2026 22:00:00 +0800</pubDate><guid>https://tech-snippets.xyz/posts/risc-v-embedded-development-guide-2026/</guid><description>从零开始掌握 RISC-V 嵌入式开发，涵盖架构原理、工具链搭建、ESP32-C3 实战和 RT-Thread 移植</description><content:encoded><![CDATA[<h2 id="引言">引言</h2>
<p>RISC-V（读作&quot;Risk Five&quot;）是一个基于精简指令集（RISC）原则的开源指令集架构（ISA）。它由加州大学伯克利分校于 2010 年设计，如今已成为嵌入式领域的新星。</p>
<p><strong>为什么选择 RISC-V？</strong></p>
<ul>
<li><strong>开源免费</strong>：无需授权费，可自由使用和商业</li>
<li><strong>模块化设计</strong>：基础 ISA + 可选扩展（M/A/F/D/C）</li>
<li><strong>生态系统</strong>：2026 年已有超过 100 亿颗 RISC-V 芯片出货</li>
<li><strong>自主可控</strong>：不受地缘政治影响，供应链安全</li>
</ul>
<p>本文从架构原理到实战项目，带你全面掌握 RISC-V 嵌入式开发。</p>
<h2 id="risc-v-架构核心">RISC-V 架构核心</h2>
<h3 id="11-指令集结构">1.1 指令集结构</h3>

<figure>
<svg viewBox="0 0 850 400" xmlns="http://www.w3.org/2000/svg" class="no-invert">
  <defs>
    <style>
      .base { fill: #dbeafe; stroke: #1d4ed8; stroke-width: 2; rx: 8; }
      .ext { fill: #fef3c7; stroke: #d97706; stroke-width: 2; rx: 6; }
      .text { fill: #1e293b; font-family: Arial, sans-serif; font-size: 15px; font-weight: 600; }
      .sub { fill: #64748b; font-family: Arial, sans-serif; font-size: 13px; }
    </style>
  </defs>
  
  <!-- 基础 ISA -->
  <rect class="base" x="50" y="50" width="750" height="80"/>
  <text class="text" x="425" y="85" text-anchor="middle">基础 ISA（必选）</text>
  <text class="sub" x="425" y="110" text-anchor="middle">RV32I（32 位） / RV64I（64 位） / RV128I（128 位）</text>
  
  <!-- 标准扩展 -->
  <rect class="ext" x="50" y="160" width="140" height="70"/>
  <text class="text" x="120" y="190" text-anchor="middle">M 扩展</text>
  <text class="sub" x="120" y="210" text-anchor="middle">整数乘除法</text>
  
  <rect class="ext" x="210" y="160" width="140" height="70"/>
  <text class="text" x="280" y="190" text-anchor="middle">A 扩展</text>
  <text class="sub" x="280" y="210" text-anchor="middle">原子操作</text>
  
  <rect class="ext" x="370" y="160" width="140" height="70"/>
  <text class="text" x="440" y="190" text-anchor="middle">F 扩展</text>
  <text class="sub" x="440" y="210" text-anchor="middle">单精度浮点</text>
  
  <rect class="ext" x="530" y="160" width="140" height="70"/>
  <text class="text" x="600" y="190" text-anchor="middle">D 扩展</text>
  <text class="sub" x="600" y="210" text-anchor="middle">双精度浮点</text>
  
  <rect class="ext" x="690" y="160" width="110" height="70"/>
  <text class="text" x="745" y="190" text-anchor="middle">C 扩展</text>
  <text class="sub" x="745" y="210" text-anchor="middle">压缩指令</text>
  
  <!-- 片上外设 -->
  <rect class="base" x="50" y="260" width="750" height="80" style="fill:#f0fdf4;stroke:#16a34a;"/>
  <text class="text" x="425" y="295" text-anchor="middle">片上外设（可选）</text>
  <text class="sub" x="425" y="320" text-anchor="middle">CLIC（中断控制器）| PLIC | UART | SPI | I2C | GPIO | Timer</text>
</svg>
<figcaption style="text-align:center;color:#888;font-size:16px;font-weight:600;margin-top:12px;">RISC-V 指令集结构</figcaption>
</figure>


<p><strong>基础指令集（必选其一）</strong>：</p>
<ul>
<li><strong>RV32I</strong>：32 位整数基础，最常用（ESP32-C3、GD32V）</li>
<li><strong>RV64I</strong>：64 位整数，高性能应用（StarFive JH7110）</li>
<li><strong>RV128I</strong>：128 位，未来扩展</li>
</ul>
<p><strong>标准扩展（可选）</strong>：</p>
<table>
<thead>
<tr>
<th>扩展</th>
<th>名称</th>
<th>作用</th>
<th>典型应用</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>M</strong></td>
<td>Multiplication</td>
<td>硬件乘除法</td>
<td>DSP、加密</td>
</tr>
<tr>
<td><strong>A</strong></td>
<td>Atomic</td>
<td>原子操作</td>
<td>RTOS、多核同步</td>
</tr>
<tr>
<td><strong>F</strong></td>
<td>Float-32</td>
<td>单精度浮点</td>
<td>电机控制、AI</td>
</tr>
<tr>
<td><strong>D</strong></td>
<td>Float-64</td>
<td>双精度浮点</td>
<td>科学计算</td>
</tr>
<tr>
<td><strong>C</strong></td>
<td>Compressed</td>
<td>16 位压缩指令</td>
<td>减小代码体积 30%</td>
</tr>
</tbody>
</table>
<h3 id="12-寄存器组织">1.2 寄存器组织</h3>
<p>RISC-V 有 32 个通用寄存器（x0-x31）：</p>
<table>
<thead>
<tr>
<th>寄存器</th>
<th>别名</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>x0</td>
<td>zero</td>
<td>硬连线到 0（常零寄存器）</td>
</tr>
<tr>
<td>x1</td>
<td>ra</td>
<td>返回地址</td>
</tr>
<tr>
<td>x2</td>
<td>sp</td>
<td>栈指针</td>
</tr>
<tr>
<td>x3</td>
<td>gp</td>
<td>全局指针</td>
</tr>
<tr>
<td>x4</td>
<td>tp</td>
<td>线程指针</td>
</tr>
<tr>
<td>x5-x7</td>
<td>t0-t2</td>
<td>临时寄存器</td>
</tr>
<tr>
<td>x8</td>
<td>s0/fp</td>
<td>保存寄存器/帧指针</td>
</tr>
<tr>
<td>x9</td>
<td>s1</td>
<td>保存寄存器</td>
</tr>
<tr>
<td>x10-x11</td>
<td>a0-a1</td>
<td>函数参数/返回值</td>
</tr>
<tr>
<td>x12-x17</td>
<td>a2-a7</td>
<td>函数参数</td>
</tr>
<tr>
<td>x18-x27</td>
<td>s2-s11</td>
<td>保存寄存器</td>
</tr>
<tr>
<td>x28-x31</td>
<td>t3-t6</td>
<td>临时寄存器</td>
</tr>
</tbody>
</table>
<p><strong>调用约定</strong>：</p>
<ul>
<li>参数传递：a0-a7（最多 8 个参数）</li>
<li>返回值：a0-a1</li>
<li>调用者保存：t0-t6、a0-a7</li>
<li>被调用者保存：s0-s11、ra</li>
</ul>
<h3 id="13-与其他架构对比">1.3 与其他架构对比</h3>
<table>
<thead>
<tr>
<th>特性</th>
<th>RISC-V</th>
<th>ARM Cortex-M</th>
<th>x86</th>
<th>MIPS</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>授权模式</strong></td>
<td>开源免费</td>
<td>商业授权</td>
<td>商业授权</td>
<td>商业授权</td>
</tr>
<tr>
<td><strong>入门成本</strong></td>
<td>$0</td>
<td>$5k-$100k</td>
<td>$1M+</td>
<td>$50k+</td>
</tr>
<tr>
<td><strong>指令复杂度</strong></td>
<td>低</td>
<td>中</td>
<td>高</td>
<td>低</td>
</tr>
<tr>
<td><strong>功耗</strong></td>
<td>低</td>
<td>低</td>
<td>高</td>
<td>中</td>
</tr>
<tr>
<td><strong>生态成熟度</strong></td>
<td>发展中</td>
<td>非常成熟</td>
<td>非常成熟</td>
<td>衰退</td>
</tr>
<tr>
<td><strong>定制能力</strong></td>
<td>强</td>
<td>弱</td>
<td>无</td>
<td>弱</td>
</tr>
</tbody>
</table>
<h2 id="开发环境搭建">开发环境搭建</h2>
<h3 id="21-工具链安装">2.1 工具链安装</h3>
<p><strong>Ubuntu/Debian</strong>：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 安装 RISC-V 工具链</span>
</span></span><span class="line"><span class="cl">sudo apt update
</span></span><span class="line"><span class="cl">sudo apt install gcc-riscv64-unknown-elf
</span></span><span class="line"><span class="cl">sudo apt install gdb-multiarch
</span></span><span class="line"><span class="cl">sudo apt install qemu-system-misc
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 验证安装</span>
</span></span><span class="line"><span class="cl">riscv64-unknown-elf-gcc --version
</span></span></code></pre></div><p><strong>macOS</strong>：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 使用 Homebrew</span>
</span></span><span class="line"><span class="cl">brew install riscv64-elf-gcc
</span></span><span class="line"><span class="cl">brew install riscv64-elf-binutils
</span></span><span class="line"><span class="cl">brew install qemu
</span></span></code></pre></div><p><strong>Windows</strong>：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 下载预编译工具链</span>
</span></span><span class="line"><span class="cl"><span class="c1"># https://github.com/riscv-collab/riscv-gnu-toolchain/releases</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 解压后添加到 PATH</span>
</span></span></code></pre></div><h3 id="22-编译第一个程序">2.2 编译第一个程序</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// hello.c
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nf">printf</span><span class="p">(</span><span class="s">&#34;Hello from RISC-V!</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p><strong>编译命令</strong>：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 编译为裸机可执行文件</span>
</span></span><span class="line"><span class="cl">riscv64-unknown-elf-gcc -march<span class="o">=</span>rv32imac -mabi<span class="o">=</span>ilp32 <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>  -nostdlib -nostartfiles <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>  -T link.ld hello.c -o hello.elf
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 生成二进制文件</span>
</span></span><span class="line"><span class="cl">riscv64-unknown-elf-objcopy -O binary hello.elf hello.bin
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 反汇编查看</span>
</span></span><span class="line"><span class="cl">riscv64-unknown-elf-objdump -d hello.elf
</span></span></code></pre></div><p><strong>链接脚本（link.ld）</strong>：</p>
<pre tabindex="0"><code class="language-ld" data-lang="ld">MEMORY {
  FLASH (rx) : ORIGIN = 0x40200000, LENGTH = 4M
  RAM (rwx)  : ORIGIN = 0x3FC00000, LENGTH = 320K
}

SECTIONS {
  .text : {
    *(.text)
  } &gt; FLASH
  
  .data : {
    *(.data)
  } &gt; RAM
  
  .bss : {
    *(.bss)
    *(COMMON)
  } &gt; RAM
}
</code></pre><h3 id="23-使用-qemu-仿真">2.3 使用 QEMU 仿真</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 运行 RISC-V 32 位仿真</span>
</span></span><span class="line"><span class="cl">qemu-system-riscv32 -machine virt -nographic <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>  -kernel hello.elf
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 带 GDB 调试</span>
</span></span><span class="line"><span class="cl">qemu-system-riscv32 -machine virt -nographic <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>  -kernel hello.elf -s -S
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 另一个终端连接 GDB</span>
</span></span><span class="line"><span class="cl">riscv64-unknown-elf-gdb hello.elf
</span></span><span class="line"><span class="cl"><span class="o">(</span>gdb<span class="o">)</span> target remote :1234
</span></span><span class="line"><span class="cl"><span class="o">(</span>gdb<span class="o">)</span> <span class="k">continue</span>
</span></span></code></pre></div><h2 id="esp32-c3-实战">ESP32-C3 实战</h2>
<h3 id="31-硬件介绍">3.1 硬件介绍</h3>
<p>ESP32-C3 是乐鑫 2021 年推出的 RISC-V 物联网芯片：</p>
<table>
<thead>
<tr>
<th>参数</th>
<th>规格</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>CPU</strong></td>
<td>RISC-V 32 位单核，160MHz</td>
</tr>
<tr>
<td><strong>Flash</strong></td>
<td>4MB / 8MB / 16MB</td>
</tr>
<tr>
<td><strong>SRAM</strong></td>
<td>400KB</td>
</tr>
<tr>
<td><strong>WiFi</strong></td>
<td>802.11 b/g/n 2.4GHz</td>
</tr>
<tr>
<td><strong>蓝牙</strong></td>
<td>Bluetooth 5.0 LE</td>
</tr>
<tr>
<td><strong>GPIO</strong></td>
<td>22 个可编程 GPIO</td>
</tr>
<tr>
<td><strong>外设</strong></td>
<td>SPI、I2C、UART、ADC、PWM</td>
</tr>
<tr>
<td><strong>功耗</strong></td>
<td>深度睡眠 40μA</td>
</tr>
</tbody>
</table>
<h3 id="32-arduino-开发">3.2 Arduino 开发</h3>
<p><strong>硬件连接</strong>：</p>
<table>
<thead>
<tr>
<th>模块</th>
<th>ESP32-C3 引脚</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>LED</td>
<td>GPIO 8</td>
<td>板载 LED</td>
</tr>
<tr>
<td>按键</td>
<td>GPIO 9</td>
<td>BOOT 按钮</td>
</tr>
<tr>
<td>UART</td>
<td>GPIO 20/21</td>
<td>USB 串口</td>
</tr>
</tbody>
</table>
<p><strong>代码示例</strong>：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="c1">// blink.ino - LED 闪烁
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="nf">setup</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="n">pinMode</span><span class="p">(</span><span class="mi">8</span><span class="p">,</span> <span class="n">OUTPUT</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="n">Serial</span><span class="p">.</span><span class="n">begin</span><span class="p">(</span><span class="mi">115200</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="n">Serial</span><span class="p">.</span><span class="n">println</span><span class="p">(</span><span class="s">&#34;ESP32-C3 RISC-V 启动！&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">loop</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="n">digitalWrite</span><span class="p">(</span><span class="mi">8</span><span class="p">,</span> <span class="n">HIGH</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="n">Serial</span><span class="p">.</span><span class="n">println</span><span class="p">(</span><span class="s">&#34;LED ON&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="n">delay</span><span class="p">(</span><span class="mi">1000</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  
</span></span><span class="line"><span class="cl">  <span class="n">digitalWrite</span><span class="p">(</span><span class="mi">8</span><span class="p">,</span> <span class="n">LOW</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="n">Serial</span><span class="p">.</span><span class="n">println</span><span class="p">(</span><span class="s">&#34;LED OFF&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="n">delay</span><span class="p">(</span><span class="mi">1000</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p><strong>编译上传</strong>：</p>
<ol>
<li>Arduino IDE 安装 ESP32 板卡支持</li>
<li>选择板卡：&ldquo;Xiao ESP32C3&rdquo; 或 &ldquo;ESP32C3 Dev Module&rdquo;</li>
<li>上传后打开串口监视器</li>
</ol>
<h3 id="33-esp-idf-开发">3.3 ESP-IDF 开发</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 安装 ESP-IDF</span>
</span></span><span class="line"><span class="cl">git clone -b v5.2 --recursive https://github.com/espressif/esp-idf.git
</span></span><span class="line"><span class="cl"><span class="nb">cd</span> esp-idf
</span></span><span class="line"><span class="cl">./install.sh esp32c3
</span></span><span class="line"><span class="cl">. ./export.sh
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 创建项目</span>
</span></span><span class="line"><span class="cl">idf.py create-project riscv_demo
</span></span><span class="line"><span class="cl"><span class="nb">cd</span> riscv_demo
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 配置项目（选择 ESP32C3）</span>
</span></span><span class="line"><span class="cl">idf.py menuconfig
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 编译烧录</span>
</span></span><span class="line"><span class="cl">idf.py -p /dev/ttyUSB0 flash monitor
</span></span></code></pre></div><p><strong>主程序</strong>：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&#34;freertos/FreeRTOS.h&#34;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&#34;freertos/task.h&#34;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&#34;driver/gpio.h&#34;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="cp">#define LED_PIN 8
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">app_main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 配置 GPIO
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">gpio_reset_pin</span><span class="p">(</span><span class="n">LED_PIN</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nf">gpio_set_direction</span><span class="p">(</span><span class="n">LED_PIN</span><span class="p">,</span> <span class="n">GPIO_MODE_OUTPUT</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="nf">printf</span><span class="p">(</span><span class="s">&#34;ESP32-C3 RISC-V 演示启动！</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="k">while</span> <span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nf">gpio_set_level</span><span class="p">(</span><span class="n">LED_PIN</span><span class="p">,</span> <span class="mi">1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="nf">printf</span><span class="p">(</span><span class="s">&#34;LED ON</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="nf">vTaskDelay</span><span class="p">(</span><span class="mi">1000</span> <span class="o">/</span> <span class="n">portTICK_PERIOD_MS</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="nf">gpio_set_level</span><span class="p">(</span><span class="n">LED_PIN</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="nf">printf</span><span class="p">(</span><span class="s">&#34;LED OFF</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="nf">vTaskDelay</span><span class="p">(</span><span class="mi">1000</span> <span class="o">/</span> <span class="n">portTICK_PERIOD_MS</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h2 id="rt-thread-rtos-移植">RT-Thread RTOS 移植</h2>
<h3 id="41-获取-rt-thread">4.1 获取 RT-Thread</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 克隆 RT-Thread 仓库</span>
</span></span><span class="line"><span class="cl">git clone https://github.com/RT-Thread/rt-thread.git
</span></span><span class="line"><span class="cl"><span class="nb">cd</span> rt-thread
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 使用 Env 工具配置</span>
</span></span><span class="line"><span class="cl">./env
</span></span><span class="line"><span class="cl"><span class="c1"># 在 menuconfig 中选择 BSP -&gt; RISC-V</span>
</span></span></code></pre></div><h3 id="42-移植步骤">4.2 移植步骤</h3>
<p><strong>1. 实现底层驱动</strong>：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// bsp.c - 板级支持包
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#include</span> <span class="cpf">&#34;rtthread.h&#34;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&#34;riscv_encoding.h&#34;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="c1">// 系统时钟初始化
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="nf">rt_hw_clock_init</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 配置 160MHz 系统时钟
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="c1">// 具体代码根据芯片手册实现
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 串口初始化
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="nf">rt_hw_uart_init</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 配置 UART 为 115200 8N1
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 系统滴答定时器
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="nf">rt_hw_systick_init</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 配置 1ms 中断
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span></code></pre></div><p><strong>2. 中断处理</strong>：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// interrupt.c
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#include</span> <span class="cpf">&#34;riscv_encoding.h&#34;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">rt_hw_interrupt_init</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 初始化中断控制器
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="c1">// 配置 PLIC/CLIC
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">rt_hw_interrupt_mask</span><span class="p">(</span><span class="kt">int</span> <span class="n">vector</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 屏蔽中断
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">rt_hw_interrupt_umask</span><span class="p">(</span><span class="kt">int</span> <span class="n">vector</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 使能中断
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span></code></pre></div><p><strong>3. 上下文切换</strong>：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// context.S - 汇编实现
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#include</span> <span class="cpf">&#34;riscv_encoding.h&#34;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="n">globl</span> <span class="n">rt_hw_context_switch_to</span>
</span></span><span class="line"><span class="cl"><span class="nl">rt_hw_context_switch_to</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="cp"># 切换到新任务的上下文
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>    <span class="cp"># 保存/恢复寄存器
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>    <span class="cp"># 返回新任务的 PC
</span></span></span></code></pre></div><h3 id="43-应用示例">4.3 应用示例</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// main.c
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#include</span> <span class="cpf">&lt;rtthread.h&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="cp">#define THREAD_PRIORITY 10
</span></span></span><span class="line"><span class="cl"><span class="cp">#define THREAD_STACK_SIZE 512
</span></span></span><span class="line"><span class="cl"><span class="cp">#define THREAD_TIMESLICE 5
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="c1">// 线程 1 控制块
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">static</span> <span class="kt">rt_thread_t</span> <span class="n">tid1</span> <span class="o">=</span> <span class="n">RT_NULL</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="c1">// 线程 2 控制块
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">static</span> <span class="kt">rt_thread_t</span> <span class="n">tid2</span> <span class="o">=</span> <span class="n">RT_NULL</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 线程 1 入口
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="nf">thread1_entry</span><span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="n">parameter</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">while</span> <span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nf">rt_kprintf</span><span class="p">(</span><span class="s">&#34;Thread 1 running</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="nf">rt_thread_mdelay</span><span class="p">(</span><span class="mi">1000</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 线程 2 入口
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="nf">thread2_entry</span><span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="n">parameter</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">while</span> <span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nf">rt_kprintf</span><span class="p">(</span><span class="s">&#34;  Thread 2 running</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="nf">rt_thread_mdelay</span><span class="p">(</span><span class="mi">500</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 系统初始化
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">int</span> <span class="nf">rt_application_init</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 创建线程 1
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">tid1</span> <span class="o">=</span> <span class="nf">rt_thread_create</span><span class="p">(</span><span class="s">&#34;thread1&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                            <span class="n">thread1_entry</span><span class="p">,</span> <span class="n">RT_NULL</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                            <span class="n">THREAD_STACK_SIZE</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                            <span class="n">THREAD_PRIORITY</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                            <span class="n">THREAD_TIMESLICE</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">tid1</span> <span class="o">!=</span> <span class="n">RT_NULL</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="nf">rt_thread_startup</span><span class="p">(</span><span class="n">tid1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 创建线程 2
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">tid2</span> <span class="o">=</span> <span class="nf">rt_thread_create</span><span class="p">(</span><span class="s">&#34;thread2&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                            <span class="n">thread2_entry</span><span class="p">,</span> <span class="n">RT_NULL</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                            <span class="n">THREAD_STACK_SIZE</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                            <span class="n">THREAD_PRIORITY</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                            <span class="n">THREAD_TIMESLICE</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">tid2</span> <span class="o">!=</span> <span class="n">RT_NULL</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="nf">rt_thread_startup</span><span class="p">(</span><span class="n">tid2</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h2 id="高级主题">高级主题</h2>
<h3 id="51-自定义指令扩展">5.1 自定义指令扩展</h3>
<p>RISC-V 支持自定义指令（Opcode 为 0b0001011）：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// 自定义指令宏
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#define CUSTOM_0(rs1, rs2, rd) \
</span></span></span><span class="line"><span class="cl"><span class="cp">    __asm__ volatile (&#34;.word (0b0001011 | (%0 &lt;&lt; 15) | (%1 &lt;&lt; 20) | (%2 &lt;&lt; 7))&#34; \
</span></span></span><span class="line"><span class="cl"><span class="cp">                      :: &#34;r&#34;(rs1), &#34;r&#34;(rs2), &#34;r&#34;(rd))
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="c1">// 使用示例
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">int</span> <span class="n">a</span> <span class="o">=</span> <span class="mi">5</span><span class="p">,</span> <span class="n">b</span> <span class="o">=</span> <span class="mi">3</span><span class="p">,</span> <span class="n">result</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nf">CUSTOM_0</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">,</span> <span class="n">result</span><span class="p">);</span>  <span class="c1">// 调用自定义硬件加速器
</span></span></span></code></pre></div><h3 id="52-性能优化">5.2 性能优化</h3>
<p><strong>使用 C 扩展减小代码体积</strong>：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 编译时启用 C 扩展</span>
</span></span><span class="line"><span class="cl">riscv64-unknown-elf-gcc -march<span class="o">=</span>rv32imac -mabi<span class="o">=</span>ilp32 <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>  -mno-save-restore -Os <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>  -o app.elf app.c
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 代码体积对比：</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 无 C 扩展：15.2KB</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 有 C 扩展：10.8KB（减小 29%）</span>
</span></span></code></pre></div><p><strong>使用硬件加速器</strong>：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// 使用 DSP 扩展（如果支持）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#include</span> <span class="cpf">&lt;riscv_dsp.h&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="c1">// SIMD 乘法
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">int32_t</span> <span class="n">result</span> <span class="o">=</span> <span class="nf">__rv__SMUL</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 饱和加法
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">int32_t</span> <span class="n">sum</span> <span class="o">=</span> <span class="nf">__rv__QADD16</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">);</span>
</span></span></code></pre></div><h3 id="53-调试技巧">5.3 调试技巧</h3>
<p><strong>OpenOCD 调试</strong>：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 启动 OpenOCD</span>
</span></span><span class="line"><span class="cl">openocd -f interface/ftdi/olimex-arm-usb-tiny-h.cfg <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>        -f target/esp32c3.cfg
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># GDB 连接</span>
</span></span><span class="line"><span class="cl">riscv64-unknown-elf-gdb app.elf
</span></span><span class="line"><span class="cl"><span class="o">(</span>gdb<span class="o">)</span> target remote :3333
</span></span><span class="line"><span class="cl"><span class="o">(</span>gdb<span class="o">)</span> monitor reset halt
</span></span><span class="line"><span class="cl"><span class="o">(</span>gdb<span class="o">)</span> <span class="k">continue</span>
</span></span></code></pre></div><p><strong>断点调试</strong>：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// 软件断点
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">__asm__</span> <span class="nf">volatile</span> <span class="p">(</span><span class="s">&#34;ebreak&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 硬件断点（需要调试器支持）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">(</span><span class="n">gdb</span><span class="p">)</span> <span class="k">break</span> <span class="o">*</span><span class="mh">0x40200100</span>
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="n">gdb</span><span class="p">)</span> <span class="k">continue</span>
</span></span></code></pre></div><h2 id="实际项目案例">实际项目案例</h2>
<h3 id="71-智能家居传感器">7.1 智能家居传感器</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// 基于 ESP32-C3 的温湿度传感器
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&#34;driver/i2c.h&#34;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&#34;aht20.h&#34;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="cp">#define I2C_MASTER_SCL_IO 10
</span></span></span><span class="line"><span class="cl"><span class="cp">#define I2C_MASTER_SDA_IO 11
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">app_main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 初始化 I2C
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="kt">i2c_config_t</span> <span class="n">conf</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="p">.</span><span class="n">mode</span> <span class="o">=</span> <span class="n">I2C_MODE_MASTER</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="p">.</span><span class="n">sda_io_num</span> <span class="o">=</span> <span class="n">I2C_MASTER_SDA_IO</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="p">.</span><span class="n">scl_io_num</span> <span class="o">=</span> <span class="n">I2C_MASTER_SCL_IO</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="p">.</span><span class="n">master</span><span class="p">.</span><span class="n">clk_speed</span> <span class="o">=</span> <span class="mi">400000</span>
</span></span><span class="line"><span class="cl">    <span class="p">};</span>
</span></span><span class="line"><span class="cl">    <span class="nf">i2c_param_config</span><span class="p">(</span><span class="n">I2C_NUM_0</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">conf</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nf">i2c_driver_install</span><span class="p">(</span><span class="n">I2C_NUM_0</span><span class="p">,</span> <span class="n">conf</span><span class="p">.</span><span class="n">mode</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 初始化 AHT20 传感器
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">aht20_init</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="k">while</span> <span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="kt">float</span> <span class="n">temp</span><span class="p">,</span> <span class="n">humid</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="nf">aht20_read</span><span class="p">(</span><span class="o">&amp;</span><span class="n">temp</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">humid</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="nf">printf</span><span class="p">(</span><span class="s">&#34;温度：%.2f°C, 湿度：%.2f%%</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">,</span> <span class="n">temp</span><span class="p">,</span> <span class="n">humid</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="c1">// 通过 MQTT 上报
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="nf">mqtt_publish</span><span class="p">(</span><span class="s">&#34;home/sensor/temp&#34;</span><span class="p">,</span> <span class="n">temp</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="nf">mqtt_publish</span><span class="p">(</span><span class="s">&#34;home/sensor/humid&#34;</span><span class="p">,</span> <span class="n">humid</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="nf">vTaskDelay</span><span class="p">(</span><span class="mi">5000</span> <span class="o">/</span> <span class="n">portTICK_PERIOD_MS</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="72-电机控制器">7.2 电机控制器</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// FOC 电机控制（使用硬件 FPU）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#include</span> <span class="cpf">&#34;foc_control.h&#34;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">foc_task</span><span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="n">parameter</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">float</span> <span class="n">angle</span><span class="p">,</span> <span class="n">speed</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">float</span> <span class="n">id_ref</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span> <span class="n">iq_ref</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="k">while</span> <span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// 读取编码器角度
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="n">angle</span> <span class="o">=</span> <span class="nf">encoder_get_angle</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">        <span class="n">speed</span> <span class="o">=</span> <span class="nf">encoder_get_speed</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="c1">// Clarke/Park 变换
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="kt">float</span> <span class="n">ia</span><span class="p">,</span> <span class="n">ib</span><span class="p">,</span> <span class="n">ic</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="nf">adc_read_current</span><span class="p">(</span><span class="o">&amp;</span><span class="n">ia</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">ib</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">ic</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="kt">float</span> <span class="n">id</span><span class="p">,</span> <span class="n">iq</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="nf">park_transform</span><span class="p">(</span><span class="n">ia</span><span class="p">,</span> <span class="n">ib</span><span class="p">,</span> <span class="n">angle</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">id</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">iq</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="c1">// PI 控制器
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="n">iq_ref</span> <span class="o">=</span> <span class="nf">speed_pid</span><span class="p">(</span><span class="n">speed</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="kt">float</span> <span class="n">vd</span> <span class="o">=</span> <span class="nf">current_pid</span><span class="p">(</span><span class="n">id</span><span class="p">,</span> <span class="n">id_ref</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="kt">float</span> <span class="n">vq</span> <span class="o">=</span> <span class="nf">current_pid</span><span class="p">(</span><span class="n">iq</span><span class="p">,</span> <span class="n">iq_ref</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="c1">// 反 Park 变换
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="kt">float</span> <span class="n">va</span><span class="p">,</span> <span class="n">vb</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="nf">inv_park_transform</span><span class="p">(</span><span class="n">vd</span><span class="p">,</span> <span class="n">vq</span><span class="p">,</span> <span class="n">angle</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">va</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">vb</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="c1">// SVPWM 生成
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="nf">svpwm_generate</span><span class="p">(</span><span class="n">va</span><span class="p">,</span> <span class="n">vb</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="nf">vTaskDelay</span><span class="p">(</span><span class="mi">100</span> <span class="o">/</span> <span class="n">portTICK_PERIOD_MS</span><span class="p">);</span>  <span class="c1">// 10kHz 控制频率
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h2 id="常见问题">常见问题</h2>
<h3 id="q1-编译错误undefined-reference-to-_start">Q1: 编译错误&quot;undefined reference to &lsquo;_start&rsquo;&quot;</h3>
<p><strong>原因</strong>：缺少启动文件或链接脚本配置错误。</p>
<p><strong>解决</strong>：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 添加启动文件</span>
</span></span><span class="line"><span class="cl">riscv64-unknown-elf-gcc -march<span class="o">=</span>rv32imac <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>  -T link.ld crt0.S app.c -o app.elf
</span></span></code></pre></div><h3 id="q2-程序运行后无输出">Q2: 程序运行后无输出</h3>
<p><strong>原因</strong>：串口未初始化或时钟配置错误。</p>
<p><strong>解决</strong>：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// 确保初始化串口
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="nf">app_main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 先初始化串口
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">uart_init</span><span class="p">(</span><span class="mi">115200</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nf">printf</span><span class="p">(</span><span class="s">&#34;启动...</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 再初始化其他外设
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span></code></pre></div><h3 id="q3-功耗过高">Q3: 功耗过高</h3>
<p><strong>原因</strong>：未进入低功耗模式或外设未关闭。</p>
<p><strong>解决</strong>：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// 进入深度睡眠
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="nf">enter_deep_sleep</span><span class="p">(</span><span class="kt">uint64_t</span> <span class="n">sleep_time_us</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 关闭未使用的外设
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">gpio_deep_sleep_disable_all</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 配置唤醒源（如 RTC、GPIO）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">esp_sleep_enable_timer_wakeup</span><span class="p">(</span><span class="n">sleep_time_us</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 进入睡眠
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">esp_deep_sleep_start</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h2 id="总结">总结</h2>
<p>RISC-V 嵌入式开发要点：</p>
<ol>
<li><strong>开源免费</strong>：无授权费，可商用</li>
<li><strong>生态成熟</strong>：ESP32-C3、RT-Thread、Arduino 支持</li>
<li><strong>工具链完善</strong>：GCC、GDB、QEMU、OpenOCD</li>
<li><strong>性能优秀</strong>：支持 FPU/DSP 扩展，适合 AIoT</li>
<li><strong>自主可控</strong>：供应链安全，不受制裁影响</li>
</ol>
<p>2026 年，RISC-V 已成为嵌入式开发的重要选择。掌握它，抓住开源硬件的机遇！</p>
<hr>
<p><em>本文基于 RISC-V 20191213 规范和 2026 年主流 RISC-V 芯片编写。</em></p>
<h2 id="参考资料">参考资料</h2>
<ul>
<li>RISC-V 官方文档：https://riscv.org/specifications/</li>
<li>ESP32-C3 技术手册：https://www.espressif.com/en/products/socs/esp32c3</li>
<li>RT-Thread 文档：https://www.rt-thread.io/document/</li>
<li>RISC-V GNU 工具链：https://github.com/riscv-collab/riscv-gnu-toolchain</li>
</ul>
]]></content:encoded></item><item><title>嵌入式 MQTT 物联网开发完全指南 2026</title><link>https://tech-snippets.xyz/posts/embedded-mqtt-iot-complete-guide-2026/</link><pubDate>Thu, 02 Apr 2026 21:00:00 +0800</pubDate><guid>https://tech-snippets.xyz/posts/embedded-mqtt-iot-complete-guide-2026/</guid><description>从零开始掌握嵌入式 MQTT 开发，涵盖协议原理、ESP32/STM32 实战、EMQX broker 搭建和完整项目案例</description><content:encoded><![CDATA[<h2 id="引言">引言</h2>
<p>MQTT（Message Queuing Telemetry Transport）是物联网领域最流行的轻量级消息协议。它基于<strong>发布/订阅</strong>模式，具有低功耗、低带宽、高可靠的特点，非常适合资源受限的嵌入式设备。</p>
<p>本文从协议原理到实战项目，带你全面掌握嵌入式 MQTT 开发。</p>
<h2 id="mqtt-协议核心概念">MQTT 协议核心概念</h2>
<h3 id="11-架构模型">1.1 架构模型</h3>

<figure>
<svg viewBox="0 0 800 450" xmlns="http://www.w3.org/2000/svg" class="no-invert">
  <defs>
    <style>
      .box { fill: #f0f4f8; stroke: #2563eb; stroke-width: 2; rx: 8; }
      .broker { fill: #dbeafe; stroke: #1d4ed8; stroke-width: 3; rx: 10; }
      .text { fill: #1e293b; font-family: Arial, sans-serif; font-size: 15px; font-weight: 600; }
      .label { fill: #64748b; font-family: Arial, sans-serif; font-size: 13px; }
      .arrow { stroke: #3b82f6; stroke-width: 2; fill: none; marker-end: url(#arrowhead); }
      .arrow-dash { stroke: #64748b; stroke-width: 2; stroke-dasharray: 5,3; fill: none; marker-end: url(#arrowhead-gray); }
    </style>
    <marker id="arrowhead" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
      <polygon points="0 0, 10 3.5, 0 7" fill="#3b82f6"/>
    </marker>
    <marker id="arrowhead-gray" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
      <polygon points="0 0, 10 3.5, 0 7" fill="#64748b"/>
    </marker>
  </defs>
  
  <!-- Broker 中心 -->
  <rect class="broker" x="300" y="180" width="200" height="90"/>
  <text class="text" x="400" y="215" text-anchor="middle">MQTT Broker</text>
  <text class="label" x="400" y="240" text-anchor="middle">EMQX / Mosquitto</text>
  
  <!-- 发布者 1 -->
  <rect class="box" x="20" y="30" width="150" height="60"/>
  <text class="text" x="95" y="58" text-anchor="middle">ESP32 传感器</text>
  <text class="label" x="95" y="78" text-anchor="middle">发布：temp/humidity</text>
  
  <!-- 发布者 2 -->
  <rect class="box" x="20" y="360" width="150" height="60"/>
  <text class="text" x="95" y="388" text-anchor="middle">STM32 控制器</text>
  <text class="label" x="95" y="408" text-anchor="middle">发布：status/alert</text>
  
  <!-- 订阅者 1 -->
  <rect class="box" x="630" y="30" width="150" height="60"/>
  <text class="text" x="705" y="58" text-anchor="middle">手机 App</text>
  <text class="label" x="705" y="78" text-anchor="middle">订阅：temp/humidity</text>
  
  <!-- 订阅者 2 -->
  <rect class="box" x="630" y="180" width="150" height="60"/>
  <text class="text" x="705" y="208" text-anchor="middle">云端服务</text>
  <text class="label" x="705" y="228" text-anchor="middle">订阅：所有主题</text>
  
  <!-- 订阅者 3 -->
  <rect class="box" x="630" y="360" width="150" height="60"/>
  <text class="text" x="705" y="388" text-anchor="middle">本地网关</text>
  <text class="label" x="705" y="408" text-anchor="middle">订阅：status/alert</text>
  
  <!-- 发布连接 -->
  <path class="arrow" d="M170 60 L300 200"/>
  <path class="arrow" d="M170 390 L300 250"/>
  
  <!-- 订阅连接 -->
  <path class="arrow-dash" d="M500 200 L630 60"/>
  <path class="arrow-dash" d="M500 225 L630 210"/>
  <path class="arrow-dash" d="M500 250 L630 390"/>
</svg>
<figcaption style="text-align:center;color:#888;font-size:16px;font-weight:600;margin-top:12px;">MQTT 发布/订阅架构</figcaption>
</figure>


<p><strong>核心组件</strong>：</p>
<ul>
<li><strong>Broker</strong>：消息中转站（EMQX、Mosquitto、HiveMQ）</li>
<li><strong>Publisher</strong>：消息发布者（传感器、设备）</li>
<li><strong>Subscriber</strong>：消息订阅者（App、云端、网关）</li>
<li><strong>Topic</strong>：消息主题（如 <code>home/living-room/temp</code>）</li>
</ul>
<h3 id="12-qos-服务质量等级">1.2 QoS 服务质量等级</h3>
<table>
<thead>
<tr>
<th>QoS</th>
<th>名称</th>
<th>说明</th>
<th>应用场景</th>
</tr>
</thead>
<tbody>
<tr>
<td>0</td>
<td>At most once</td>
<td>最多一次，不保证到达</td>
<td>传感器周期性数据</td>
</tr>
<tr>
<td>1</td>
<td>At least once</td>
<td>至少一次，可能重复</td>
<td>控制指令、状态更新</td>
</tr>
<tr>
<td>2</td>
<td>Exactly once</td>
<td>恰好一次，最可靠</td>
<td>计费、关键告警</td>
</tr>
</tbody>
</table>
<h3 id="13-topic-命名规范">1.3 Topic 命名规范</h3>

<div style="background:#f8fafc;border-left:4px solid #22c55e;padding:16px;margin:20px 0;border-radius:4px;">
<strong style="color:#22c55e;font-size:18px;">✅ 推荐格式：</strong>
<table style="width:100%;margin-top:12px;border-collapse:collapse;">
<tr style="background:#e2e8f0;">
<th style="padding:10px;text-align:left;border:1px solid #cbd5e1;font-family:monospace;">Topic 示例</th>
<th style="padding:10px;text-align:left;border:1px solid #cbd5e1;">说明</th>
</tr>
<tr>
<td style="padding:10px;border:1px solid #cbd5e1;font-family:monospace;background:#f1f5f9;">home/living-room/temperature</td>
<td style="padding:10px;border:1px solid #cbd5e1;">设备类型/位置/参数</td>
</tr>
<tr>
<td style="padding:10px;border:1px solid #cbd5e1;font-family:monospace;background:#f1f5f9;">factory/line1/motor/speed</td>
<td style="padding:10px;border:1px solid #cbd5e1;">工厂/产线/设备/参数</td>
</tr>
<tr>
<td style="padding:10px;border:1px solid #cbd5e1;font-family:monospace;background:#f1f5f9;">agric/greenhouse/01/humidity</td>
<td style="padding:10px;border:1px solid #cbd5e1;">行业/区域/编号/参数</td>
</tr>
</table>
</div>

<div style="background:#fef2f2;border-left:4px solid #ef4444;padding:16px;margin:20px 0;border-radius:4px;">
<strong style="color:#ef4444;font-size:18px;">❌ 避免：</strong>
<table style="width:100%;margin-top:12px;border-collapse:collapse;">
<tr style="background:#fee2e2;">
<th style="padding:10px;text-align:left;border:1px solid #fca5a5;font-family:monospace;">Topic 示例</th>
<th style="padding:10px;text-align:left;border:1px solid #fca5a5;">问题</th>
</tr>
<tr>
<td style="padding:10px;border:1px solid #fca5a5;font-family:monospace;background:#fef2f2;">temp</td>
<td style="padding:10px;border:1px solid #fca5a5;">太模糊，无法扩展</td>
</tr>
<tr>
<td style="padding:10px;border:1px solid #fca5a5;font-family:monospace;background:#fef2f2;">home/temp</td>
<td style="padding:10px;border:1px solid #fca5a5;">缺少层级</td>
</tr>
<tr>
<td style="padding:10px;border:1px solid #fca5a5;font-family:monospace;background:#fef2f2;">sensor_001/data</td>
<td style="padding:10px;border:1px solid #fca5a5;">语义不明确</td>
</tr>
</table>
</div>


<h2 id="开发环境搭建">开发环境搭建</h2>
<h3 id="21-broker-选择">2.1 Broker 选择</h3>
<table>
<thead>
<tr>
<th>Broker</th>
<th>语言</th>
<th>特点</th>
<th>适用场景</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>EMQX</strong></td>
<td>Erlang</td>
<td>高性能、分布式、支持百万连接</td>
<td>企业级、云平台</td>
</tr>
<tr>
<td><strong>Mosquitto</strong></td>
<td>C</td>
<td>轻量、稳定、易部署</td>
<td>小型项目、本地测试</td>
</tr>
<tr>
<td><strong>HiveMQ</strong></td>
<td>Java</td>
<td>企业级、扩展性强</td>
<td>大型工业应用</td>
</tr>
</tbody>
</table>
<h3 id="22-使用-docker-快速部署-emqx">2.2 使用 Docker 快速部署 EMQX</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 启动 EMQX Broker</span>
</span></span><span class="line"><span class="cl">docker run -d --name emqx <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>  -p 1883:1883 <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>  -p 8083:8083 <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>  -p 8084:8084 <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>  -p 8883:8883 <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>  -p 18083:18083 <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>  emqx/emqx:5.0
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 访问管理控制台：http://localhost:18083</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 默认账号：admin / public</span>
</span></span></code></pre></div><h3 id="23-客户端库选择">2.3 客户端库选择</h3>
<table>
<thead>
<tr>
<th>平台</th>
<th>推荐库</th>
<th>特点</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>ESP32 (Arduino)</strong></td>
<td>PubSubClient</td>
<td>轻量、成熟</td>
</tr>
<tr>
<td><strong>ESP32 (ESP-IDF)</strong></td>
<td>esp-mqtt</td>
<td>官方支持、功能全</td>
</tr>
<tr>
<td><strong>STM32 HAL</strong></td>
<td>MQTT-C</td>
<td>纯 C、可移植</td>
</tr>
<tr>
<td><strong>Rust embedded</strong></td>
<td>rumqtt</td>
<td>异步、安全</td>
</tr>
<tr>
<td><strong>Python</strong></td>
<td>paho-mqtt</td>
<td>功能丰富</td>
</tr>
</tbody>
</table>
<h2 id="esp32-mqtt-实战">ESP32 MQTT 实战</h2>
<h3 id="31-硬件连接">3.1 硬件连接</h3>
<table>
<thead>
<tr>
<th>模块</th>
<th>ESP32 引脚</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>DHT22 温湿度</td>
<td>GPIO 4</td>
<td>数字传感器</td>
</tr>
<tr>
<td>LED 指示灯</td>
<td>GPIO 2</td>
<td>状态指示</td>
</tr>
<tr>
<td>按键</td>
<td>GPIO 0</td>
<td>手动触发</td>
</tr>
</tbody>
</table>
<h3 id="32-arduino-代码示例">3.2 Arduino 代码示例</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;WiFi.h&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;PubSubClient.h&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;DHT.h&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="c1">// WiFi 配置
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">const</span> <span class="kt">char</span><span class="o">*</span> <span class="n">ssid</span> <span class="o">=</span> <span class="s">&#34;YourWiFi&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">const</span> <span class="kt">char</span><span class="o">*</span> <span class="n">password</span> <span class="o">=</span> <span class="s">&#34;YourPassword&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// MQTT 配置
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">const</span> <span class="kt">char</span><span class="o">*</span> <span class="n">mqtt_server</span> <span class="o">=</span> <span class="s">&#34;192.168.1.100&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">const</span> <span class="kt">int</span> <span class="n">mqtt_port</span> <span class="o">=</span> <span class="mi">1883</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">const</span> <span class="kt">char</span><span class="o">*</span> <span class="n">mqtt_user</span> <span class="o">=</span> <span class="s">&#34;admin&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">const</span> <span class="kt">char</span><span class="o">*</span> <span class="n">mqtt_pass</span> <span class="o">=</span> <span class="s">&#34;public&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 主题配置
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">const</span> <span class="kt">char</span><span class="o">*</span> <span class="n">topic_temp</span> <span class="o">=</span> <span class="s">&#34;home/living-room/temperature&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">const</span> <span class="kt">char</span><span class="o">*</span> <span class="n">topic_humid</span> <span class="o">=</span> <span class="s">&#34;home/living-room/humidity&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">const</span> <span class="kt">char</span><span class="o">*</span> <span class="n">topic_status</span> <span class="o">=</span> <span class="s">&#34;home/living-room/status&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 传感器配置
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#define DHTPIN 4
</span></span></span><span class="line"><span class="cl"><span class="cp">#define DHTTYPE DHT22
</span></span></span><span class="line"><span class="cl"><span class="cp"></span><span class="n">DHT</span> <span class="nf">dht</span><span class="p">(</span><span class="n">DHTPIN</span><span class="p">,</span> <span class="n">DHTTYPE</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">WiFiClient</span> <span class="n">espClient</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="n">PubSubClient</span> <span class="nf">client</span><span class="p">(</span><span class="n">espClient</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 回调函数（接收订阅消息）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="nf">callback</span><span class="p">(</span><span class="kt">char</span><span class="o">*</span> <span class="n">topic</span><span class="p">,</span> <span class="n">byte</span><span class="o">*</span> <span class="n">payload</span><span class="p">,</span> <span class="kt">unsigned</span> <span class="kt">int</span> <span class="n">length</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="n">Serial</span><span class="p">.</span><span class="n">print</span><span class="p">(</span><span class="s">&#34;收到消息 [&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="n">Serial</span><span class="p">.</span><span class="n">print</span><span class="p">(</span><span class="n">topic</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="n">Serial</span><span class="p">.</span><span class="n">print</span><span class="p">(</span><span class="s">&#34;]: &#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  
</span></span><span class="line"><span class="cl">  <span class="n">String</span> <span class="n">message</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">length</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">message</span> <span class="o">+=</span> <span class="p">(</span><span class="kt">char</span><span class="p">)</span><span class="n">payload</span><span class="p">[</span><span class="n">i</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="n">Serial</span><span class="p">.</span><span class="n">println</span><span class="p">(</span><span class="n">message</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  
</span></span><span class="line"><span class="cl">  <span class="c1">// 处理控制指令
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="k">if</span> <span class="p">(</span><span class="n">String</span><span class="p">(</span><span class="n">topic</span><span class="p">)</span> <span class="o">==</span> <span class="s">&#34;home/living-room/led/control&#34;</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">message</span> <span class="o">==</span> <span class="s">&#34;on&#34;</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="n">digitalWrite</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="n">HIGH</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">message</span> <span class="o">==</span> <span class="s">&#34;off&#34;</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="n">digitalWrite</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="n">LOW</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 重连逻辑
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="nf">reconnect</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">while</span> <span class="p">(</span><span class="o">!</span><span class="n">client</span><span class="p">.</span><span class="n">connected</span><span class="p">())</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">Serial</span><span class="p">.</span><span class="n">print</span><span class="p">(</span><span class="s">&#34;尝试 MQTT 连接...&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="n">String</span> <span class="n">clientId</span> <span class="o">=</span> <span class="s">&#34;ESP32-Client-&#34;</span> <span class="o">+</span> <span class="n">String</span><span class="p">(</span><span class="n">random</span><span class="p">(</span><span class="mh">0xffff</span><span class="p">),</span> <span class="n">HEX</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">client</span><span class="p">.</span><span class="n">connect</span><span class="p">(</span><span class="n">clientId</span><span class="p">.</span><span class="n">c_str</span><span class="p">(),</span> <span class="n">mqtt_user</span><span class="p">,</span> <span class="n">mqtt_pass</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="n">Serial</span><span class="p">.</span><span class="n">println</span><span class="p">(</span><span class="s">&#34;已连接！&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">      
</span></span><span class="line"><span class="cl">      <span class="c1">// 订阅控制主题
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>      <span class="n">client</span><span class="p">.</span><span class="n">subscribe</span><span class="p">(</span><span class="s">&#34;home/living-room/led/control&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">      
</span></span><span class="line"><span class="cl">      <span class="c1">// 发布在线状态
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>      <span class="n">client</span><span class="p">.</span><span class="n">publish</span><span class="p">(</span><span class="n">topic_status</span><span class="p">,</span> <span class="s">&#34;online&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="n">Serial</span><span class="p">.</span><span class="n">print</span><span class="p">(</span><span class="s">&#34;失败，rc=&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">      <span class="n">Serial</span><span class="p">.</span><span class="n">print</span><span class="p">(</span><span class="n">client</span><span class="p">.</span><span class="n">state</span><span class="p">());</span>
</span></span><span class="line"><span class="cl">      <span class="n">Serial</span><span class="p">.</span><span class="n">println</span><span class="p">(</span><span class="s">&#34; 5 秒后重试&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">      <span class="n">delay</span><span class="p">(</span><span class="mi">5000</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">setup</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="n">Serial</span><span class="p">.</span><span class="n">begin</span><span class="p">(</span><span class="mi">115200</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="n">dht</span><span class="p">.</span><span class="n">begin</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">  
</span></span><span class="line"><span class="cl">  <span class="n">pinMode</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="n">OUTPUT</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  
</span></span><span class="line"><span class="cl">  <span class="c1">// 连接 WiFi
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="n">WiFi</span><span class="p">.</span><span class="n">begin</span><span class="p">(</span><span class="n">ssid</span><span class="p">,</span> <span class="n">password</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="k">while</span> <span class="p">(</span><span class="n">WiFi</span><span class="p">.</span><span class="n">status</span><span class="p">()</span> <span class="o">!=</span> <span class="n">WL_CONNECTED</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">delay</span><span class="p">(</span><span class="mi">500</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">Serial</span><span class="p">.</span><span class="n">print</span><span class="p">(</span><span class="s">&#34;.&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="n">Serial</span><span class="p">.</span><span class="n">println</span><span class="p">(</span><span class="s">&#34;</span><span class="se">\n</span><span class="s">WiFi 已连接&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  
</span></span><span class="line"><span class="cl">  <span class="c1">// 配置 MQTT
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="n">client</span><span class="p">.</span><span class="n">setServer</span><span class="p">(</span><span class="n">mqtt_server</span><span class="p">,</span> <span class="n">mqtt_port</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="n">client</span><span class="p">.</span><span class="n">setCallback</span><span class="p">(</span><span class="n">callback</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">loop</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">client</span><span class="p">.</span><span class="n">connected</span><span class="p">())</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">reconnect</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="n">client</span><span class="p">.</span><span class="n">loop</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">  
</span></span><span class="line"><span class="cl">  <span class="c1">// 读取传感器数据
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="kt">float</span> <span class="n">temp</span> <span class="o">=</span> <span class="n">dht</span><span class="p">.</span><span class="n">readTemperature</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">  <span class="kt">float</span> <span class="n">humid</span> <span class="o">=</span> <span class="n">dht</span><span class="p">.</span><span class="n">readHumidity</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">  
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">isnan</span><span class="p">(</span><span class="n">temp</span><span class="p">)</span> <span class="o">&amp;&amp;</span> <span class="o">!</span><span class="n">isnan</span><span class="p">(</span><span class="n">humid</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 发布温湿度数据（QoS=1，保留消息）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">String</span> <span class="n">tempStr</span> <span class="o">=</span> <span class="n">String</span><span class="p">(</span><span class="n">temp</span><span class="p">,</span> <span class="mi">1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">String</span> <span class="n">humidStr</span> <span class="o">=</span> <span class="n">String</span><span class="p">(</span><span class="n">humid</span><span class="p">,</span> <span class="mi">1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="n">client</span><span class="p">.</span><span class="n">publish</span><span class="p">(</span><span class="n">topic_temp</span><span class="p">,</span> <span class="n">tempStr</span><span class="p">.</span><span class="n">c_str</span><span class="p">(),</span> <span class="nb">true</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">client</span><span class="p">.</span><span class="n">publish</span><span class="p">(</span><span class="n">topic_humid</span><span class="p">,</span> <span class="n">humidStr</span><span class="p">.</span><span class="n">c_str</span><span class="p">(),</span> <span class="nb">true</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="n">Serial</span><span class="p">.</span><span class="n">println</span><span class="p">(</span><span class="s">&#34;已发布：温度=&#34;</span> <span class="o">+</span> <span class="n">tempStr</span> <span class="o">+</span> <span class="s">&#34;°C, 湿度=&#34;</span> <span class="o">+</span> <span class="n">humidStr</span> <span class="o">+</span> <span class="s">&#34;%&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">  
</span></span><span class="line"><span class="cl">  <span class="n">delay</span><span class="p">(</span><span class="mi">10000</span><span class="p">);</span>  <span class="c1">// 每 10 秒发布一次
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span></code></pre></div><h3 id="33-使用-esp-idf-官方-mqtt-库">3.3 使用 ESP-IDF 官方 MQTT 库</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&#34;mqtt_client.h&#34;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&#34;esp_log.h&#34;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">TAG</span> <span class="o">=</span> <span class="s">&#34;MQTT_EXAMPLE&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">esp_mqtt_client_handle_t</span> <span class="n">client</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">esp_err_t</span> <span class="nf">mqtt_event_handler_cb</span><span class="p">(</span><span class="kt">esp_mqtt_event_handle_t</span> <span class="n">event</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">switch</span> <span class="p">(</span><span class="n">event</span><span class="o">-&gt;</span><span class="n">event_id</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">case</span> <span class="nl">MQTT_EVENT_CONNECTED</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="nf">ESP_LOGI</span><span class="p">(</span><span class="n">TAG</span><span class="p">,</span> <span class="s">&#34;MQTT 已连接&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="nf">esp_mqtt_client_subscribe</span><span class="p">(</span><span class="n">client</span><span class="p">,</span> <span class="s">&#34;home/living-room/led/control&#34;</span><span class="p">,</span> <span class="mi">1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">case</span> <span class="nl">MQTT_EVENT_DISCONNECTED</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="nf">ESP_LOGI</span><span class="p">(</span><span class="n">TAG</span><span class="p">,</span> <span class="s">&#34;MQTT 已断开&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">case</span> <span class="nl">MQTT_EVENT_SUBSCRIBED</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="nf">ESP_LOGI</span><span class="p">(</span><span class="n">TAG</span><span class="p">,</span> <span class="s">&#34;订阅成功，msg_id=%d&#34;</span><span class="p">,</span> <span class="n">event</span><span class="o">-&gt;</span><span class="n">msg_id</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">case</span> <span class="nl">MQTT_EVENT_DATA</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="nf">ESP_LOGI</span><span class="p">(</span><span class="n">TAG</span><span class="p">,</span> <span class="s">&#34;收到主题：%.*s&#34;</span><span class="p">,</span> <span class="n">event</span><span class="o">-&gt;</span><span class="n">topic_len</span><span class="p">,</span> <span class="n">event</span><span class="o">-&gt;</span><span class="n">topic</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="nf">ESP_LOGI</span><span class="p">(</span><span class="n">TAG</span><span class="p">,</span> <span class="s">&#34;数据：%.*s&#34;</span><span class="p">,</span> <span class="n">event</span><span class="o">-&gt;</span><span class="n">data_len</span><span class="p">,</span> <span class="n">event</span><span class="o">-&gt;</span><span class="n">data</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">default</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">            <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">ESP_OK</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">mqtt_app_start</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">esp_mqtt_client_config_t</span> <span class="n">mqtt_cfg</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="p">.</span><span class="n">broker</span><span class="p">.</span><span class="n">address</span><span class="p">.</span><span class="n">uri</span> <span class="o">=</span> <span class="s">&#34;mqtt://192.168.1.100:1883&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="p">.</span><span class="n">credentials</span><span class="p">.</span><span class="n">username</span> <span class="o">=</span> <span class="s">&#34;admin&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="p">.</span><span class="n">credentials</span><span class="p">.</span><span class="n">authentication</span><span class="p">.</span><span class="n">password</span> <span class="o">=</span> <span class="s">&#34;public&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">};</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="n">client</span> <span class="o">=</span> <span class="nf">esp_mqtt_client_init</span><span class="p">(</span><span class="o">&amp;</span><span class="n">mqtt_cfg</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nf">esp_mqtt_client_register_event</span><span class="p">(</span><span class="n">client</span><span class="p">,</span> <span class="n">ESP_EVENT_ANY_ID</span><span class="p">,</span> <span class="n">mqtt_event_handler_cb</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nf">esp_mqtt_client_start</span><span class="p">(</span><span class="n">client</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h2 id="stm32--mqtt-实战">STM32 + MQTT 实战</h2>
<h3 id="41-使用-mqtt-c-库">4.1 使用 MQTT-C 库</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&#34;mqtt.h&#34;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;string.h&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&#34;stm32f4xx_hal.h&#34;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="c1">// 网络层（基于 TCP）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">int</span> <span class="nf">net_read</span><span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="n">context</span><span class="p">,</span> <span class="kt">unsigned</span> <span class="kt">char</span> <span class="o">*</span><span class="n">buf</span><span class="p">,</span> <span class="kt">int</span> <span class="n">count</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nf">HAL_TCP_Read</span><span class="p">(</span><span class="n">context</span><span class="p">,</span> <span class="n">buf</span><span class="p">,</span> <span class="n">count</span><span class="p">,</span> <span class="mi">1000</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">net_write</span><span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="n">context</span><span class="p">,</span> <span class="k">const</span> <span class="kt">unsigned</span> <span class="kt">char</span> <span class="o">*</span><span class="n">buf</span><span class="p">,</span> <span class="kt">int</span> <span class="n">count</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nf">HAL_TCP_Write</span><span class="p">(</span><span class="n">context</span><span class="p">,</span> <span class="n">buf</span><span class="p">,</span> <span class="n">count</span><span class="p">,</span> <span class="mi">1000</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// MQTT 客户端
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">struct</span> <span class="n">mqtt_client</span> <span class="n">mqtt_client</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kt">uint8_t</span> <span class="n">sendbuf</span><span class="p">[</span><span class="mi">512</span><span class="p">];</span>
</span></span><span class="line"><span class="cl"><span class="kt">uint8_t</span> <span class="n">recvbuf</span><span class="p">[</span><span class="mi">512</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">mqtt_task</span><span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="n">argument</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">struct</span> <span class="n">mqtt_response</span> <span class="n">response</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 初始化
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">mqtt_init</span><span class="p">(</span><span class="o">&amp;</span><span class="n">mqtt_client</span><span class="p">,</span> <span class="n">tcp_socket</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">              <span class="n">sendbuf</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">sendbuf</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">              <span class="n">recvbuf</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">recvbuf</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">              <span class="n">publish_callback</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 连接 Broker
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">mqtt_connect</span><span class="p">(</span><span class="o">&amp;</span><span class="n">mqtt_client</span><span class="p">,</span> <span class="s">&#34;STM32-Client&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                 <span class="nb">NULL</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                 <span class="n">MQTT_CONNECT_CLEAN_SESSION</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                 <span class="mi">60</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="k">while</span> <span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nf">mqtt_sync</span><span class="p">(</span><span class="o">&amp;</span><span class="n">mqtt_client</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="c1">// 发布传感器数据
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="kt">char</span> <span class="n">payload</span><span class="p">[</span><span class="mi">32</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">        <span class="nf">sprintf</span><span class="p">(</span><span class="n">payload</span><span class="p">,</span> <span class="s">&#34;%.2f&#34;</span><span class="p">,</span> <span class="nf">read_sensor</span><span class="p">());</span>
</span></span><span class="line"><span class="cl">        <span class="nf">mqtt_publish</span><span class="p">(</span><span class="o">&amp;</span><span class="n">mqtt_client</span><span class="p">,</span> <span class="s">&#34;factory/sensor/01&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                     <span class="n">payload</span><span class="p">,</span> <span class="nf">strlen</span><span class="p">(</span><span class="n">payload</span><span class="p">),</span> <span class="n">MQTT_QOS_1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="nf">HAL_Delay</span><span class="p">(</span><span class="mi">5000</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="42-基于-freertos-的-mqtt-任务">4.2 基于 FreeRTOS 的 MQTT 任务</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// 发布任务
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="nf">mqtt_publish_task</span><span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="n">argument</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="p">(;;)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="kt">float</span> <span class="n">temp</span> <span class="o">=</span> <span class="nf">read_temperature</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">        <span class="kt">float</span> <span class="n">humid</span> <span class="o">=</span> <span class="nf">read_humidity</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="kt">char</span> <span class="n">json_msg</span><span class="p">[</span><span class="mi">128</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">        <span class="nf">sprintf</span><span class="p">(</span><span class="n">json_msg</span><span class="p">,</span> <span class="s">&#34;{</span><span class="se">\&#34;</span><span class="s">temp</span><span class="se">\&#34;</span><span class="s">:%.2f,</span><span class="se">\&#34;</span><span class="s">humid</span><span class="se">\&#34;</span><span class="s">:%.2f}&#34;</span><span class="p">,</span> <span class="n">temp</span><span class="p">,</span> <span class="n">humid</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="nf">mqtt_publish</span><span class="p">(</span><span class="o">&amp;</span><span class="n">mqtt_client</span><span class="p">,</span> <span class="s">&#34;home/sensor/data&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                     <span class="n">json_msg</span><span class="p">,</span> <span class="nf">strlen</span><span class="p">(</span><span class="n">json_msg</span><span class="p">),</span> <span class="n">MQTT_QOS_1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="nf">vTaskDelay</span><span class="p">(</span><span class="nf">pdMS_TO_TICKS</span><span class="p">(</span><span class="mi">10000</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 订阅处理任务
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="nf">mqtt_subscribe_task</span><span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="n">argument</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="p">(;;)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">struct</span> <span class="n">mqtt_response</span> <span class="o">*</span><span class="n">resp</span> <span class="o">=</span> <span class="nf">mqtt_response_queue_poll</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">resp</span> <span class="o">!=</span> <span class="nb">NULL</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">if</span> <span class="p">(</span><span class="n">resp</span><span class="o">-&gt;</span><span class="n">fixed_header</span><span class="p">.</span><span class="n">control_type</span> <span class="o">==</span> <span class="n">MQTT_CONTROL_PUBLISH</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="nf">handle_incoming_message</span><span class="p">(</span><span class="n">resp</span><span class="o">-&gt;</span><span class="n">publish</span><span class="p">.</span><span class="n">topic_name</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                       <span class="n">resp</span><span class="o">-&gt;</span><span class="n">publish</span><span class="p">.</span><span class="n">payload</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                       <span class="n">resp</span><span class="o">-&gt;</span><span class="n">publish</span><span class="p">.</span><span class="n">payload_size</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="nf">vTaskDelay</span><span class="p">(</span><span class="nf">pdMS_TO_TICKS</span><span class="p">(</span><span class="mi">100</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h2 id="高级应用">高级应用</h2>
<h3 id="51-tlsssl-加密连接">5.1 TLS/SSL 加密连接</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;WiFiClientSecure.h&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="n">WiFiClientSecure</span> <span class="n">espClient</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="n">PubSubClient</span> <span class="nf">client</span><span class="p">(</span><span class="n">espClient</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">setup</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="c1">// 配置 CA 证书
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="n">espClient</span><span class="p">.</span><span class="n">setCACert</span><span class="p">(</span><span class="n">root_ca_cert</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  
</span></span><span class="line"><span class="cl">  <span class="c1">// 配置客户端证书（双向认证）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="n">espClient</span><span class="p">.</span><span class="n">setCertificate</span><span class="p">(</span><span class="n">client_cert</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="n">espClient</span><span class="p">.</span><span class="n">setPrivateKey</span><span class="p">(</span><span class="n">client_key</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  
</span></span><span class="line"><span class="cl">  <span class="n">client</span><span class="p">.</span><span class="n">setServer</span><span class="p">(</span><span class="s">&#34;mqtt.example.com&#34;</span><span class="p">,</span> <span class="mi">8883</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="52-遗嘱消息last-will">5.2 遗嘱消息（Last Will）</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="c1">// 设备异常离线时自动发布遗嘱消息
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">client</span><span class="p">.</span><span class="n">setWill</span><span class="p">(</span><span class="n">topic_status</span><span class="p">,</span> <span class="s">&#34;offline&#34;</span><span class="p">,</span> <span class="nb">true</span><span class="p">,</span> <span class="mi">1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">client</span><span class="p">.</span><span class="n">connect</span><span class="p">(</span><span class="s">&#34;ESP32-Client&#34;</span><span class="p">,</span> <span class="n">mqtt_user</span><span class="p">,</span> <span class="n">mqtt_pass</span><span class="p">);</span>
</span></span></code></pre></div><h3 id="53-retain-保留消息">5.3 Retain 保留消息</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="c1">// 发布保留消息（新订阅者立即收到最新值）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">client</span><span class="p">.</span><span class="n">publish</span><span class="p">(</span><span class="n">topic_temp</span><span class="p">,</span> <span class="s">&#34;25.5&#34;</span><span class="p">,</span> <span class="nb">true</span><span class="p">,</span> <span class="mi">1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 清除保留消息
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">client</span><span class="p">.</span><span class="n">publish</span><span class="p">(</span><span class="n">topic_temp</span><span class="p">,</span> <span class="s">&#34;&#34;</span><span class="p">,</span> <span class="nb">true</span><span class="p">,</span> <span class="mi">1</span><span class="p">);</span>
</span></span></code></pre></div><h3 id="54-主题通配符">5.4 主题通配符</h3>

<div style="background:#f8fafc;border:1px solid #cbd5e1;padding:16px;margin:20px 0;border-radius:6px;">
<div style="margin-bottom:12px;">
<strong style="color:#2563eb;font-family:monospace;font-size:16px;"># 通配符</strong>
<span style="color:#64748b;margin-left:12px;">匹配任意层级</span>
</div>
<div style="background:#e0f2fe;padding:10px;border-radius:4px;font-family:monospace;color:#0369a1;">
home/# &nbsp;→&nbsp; home/living-room/temp, home/bedroom/humidity
</div>
</div>

<div style="background:#f8fafc;border:1px solid #cbd5e1;padding:16px;margin:20px 0;border-radius:6px;">
<div style="margin-bottom:12px;">
<strong style="color:#2563eb;font-family:monospace;font-size:16px;">+ 通配符</strong>
<span style="color:#64748b;margin-left:12px;">匹配单层</span>
</div>
<div style="background:#e0f2fe;padding:10px;border-radius:4px;font-family:monospace;color:#0369a1;">
home/+/temp &nbsp;→&nbsp; home/living-room/temp, home/bedroom/temp
</div>
</div>


<h2 id="完整项目智能家居传感器节点">完整项目：智能家居传感器节点</h2>
<h3 id="61-系统架构">6.1 系统架构</h3>

<figure>
<svg viewBox="0 0 900 500" xmlns="http://www.w3.org/2000/svg" class="no-invert">
  <defs>
    <style>
      .device { fill: #f0f9ff; stroke: #0284c7; stroke-width: 2; rx: 8; }
      .cloud { fill: #fef3c7; stroke: #d97706; stroke-width: 2; rx: 8; }
      .mobile { fill: #f0fdf4; stroke: #16a34a; stroke-width: 2; rx: 8; }
      .text { fill: #1e293b; font-family: Arial, sans-serif; font-size: 14px; font-weight: 600; }
      .sub-text { fill: #64748b; font-family: Arial, sans-serif; font-size: 12px; }
      .arrow { stroke: #3b82f6; stroke-width: 2; fill: none; marker-end: url(#arrowhead); }
    </style>
    <marker id="arrowhead" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
      <polygon points="0 0, 10 3.5, 0 7" fill="#3b82f6"/>
    </marker>
  </defs>
  
  <!-- 设备层 -->
  <rect class="device" x="20" y="50" width="180" height="80"/>
  <text class="text" x="110" y="80" text-anchor="middle">ESP32 温湿度传感器</text>
  <text class="sub-text" x="110" y="100" text-anchor="middle">DHT22 + 光照传感器</text>
  
  <rect class="device" x="20" y="160" width="180" height="80"/>
  <text class="text" x="110" y="190" text-anchor="middle">ESP32 门窗传感器</text>
  <text class="sub-text" x="110" y="210" text-anchor="middle">磁簧开关 + 震动检测</text>
  
  <rect class="device" x="20" y="270" width="180" height="80"/>
  <text class="text" x="110" y="300" text-anchor="middle">ESP32 智能插座</text>
  <text class="sub-text" x="110" y="320" text-anchor="middle">继电器 + 电量计量</text>
  
  <!-- Broker -->
  <rect class="cloud" x="350" y="150" width="200" height="120"/>
  <text class="text" x="450" y="195" text-anchor="middle">EMQX Broker</text>
  <text class="sub-text" x="450" y="220" text-anchor="middle">本地部署 / 云端</text>
  
  <!-- 应用层 -->
  <rect class="mobile" x="650" y="50" width="200" height="70"/>
  <text class="text" x="750" y="80" text-anchor="middle">手机 App</text>
  <text class="sub-text" x="750" y="100" text-anchor="middle">iOS / Android</text>
  
  <rect class="mobile" x="650" y="150" width="200" height="70"/>
  <text class="text" x="750" y="180" text-anchor="middle">Web 管理后台</text>
  <text class="sub-text" x="750" y="200" text-anchor="middle">Vue3 + Element Plus</text>
  
  <rect class="mobile" x="650" y="250" width="200" height="70"/>
  <text class="text" x="750" y="280" text-anchor="middle">云端服务</text>
  <text class="sub-text" x="750" y="300" text-anchor="middle">数据持久化 + AI 分析</text>
  
  <!-- 连接箭头 -->
  <path class="arrow" d="M200 90 L350 180"/>
  <path class="arrow" d="M200 200 L350 190"/>
  <path class="arrow" d="M200 310 L350 230"/>
  
  <path class="arrow" d="M550 190 L650 85"/>
  <path class="arrow" d="M550 210 L650 185"/>
  <path class="arrow" d="M550 230 L650 285"/>
</svg>
<figcaption style="text-align:center;color:#888;font-size:16px;font-weight:600;margin-top:12px;">智能家居 MQTT 系统架构</figcaption>
</figure>


<h3 id="62-主题设计">6.2 主题设计</h3>

<div style="background:#f0f9ff;border:1px solid #0284c7;padding:16px;margin:20px 0;border-radius:6px;">
<div style="margin-bottom:12px;">
<strong style="color:#0369a1;font-family:monospace;font-size:16px;">📡 传感器数据</strong>
</div>
<div style="background:#e0f2fe;padding:10px;border-radius:4px;font-family:monospace;color:#0369a1;margin-bottom:8px;">
home/{room}/sensor/{type}
</div>
<div style="color:#64748b;font-family:monospace;font-size:14px;padding-left:20px;">
→ home/living-room/sensor/temperature<br>
→ home/bedroom/sensor/humidity
</div>
</div>

<div style="background:#f0f9ff;border:1px solid #0284c7;padding:16px;margin:20px 0;border-radius:6px;">
<div style="margin-bottom:12px;">
<strong style="color:#0369a1;font-family:monospace;font-size:16px;">📊 设备状态</strong>
</div>
<div style="background:#e0f2fe;padding:10px;border-radius:4px;font-family:monospace;color:#0369a1;margin-bottom:8px;">
home/{device}/status
</div>
<div style="color:#64748b;font-family:monospace;font-size:14px;padding-left:20px;">
→ home/light/status<br>
→ home/ac/status
</div>
</div>

<div style="background:#f0f9ff;border:1px solid #0284c7;padding:16px;margin:20px 0;border-radius:6px;">
<div style="margin-bottom:12px;">
<strong style="color:#0369a1;font-family:monospace;font-size:16px;">🎮 控制指令</strong>
</div>
<div style="background:#e0f2fe;padding:10px;border-radius:4px;font-family:monospace;color:#0369a1;margin-bottom:8px;">
home/{device}/control/{action}
</div>
<div style="color:#64748b;font-family:monospace;font-size:14px;padding-left:20px;">
→ home/light/control/on<br>
→ home/ac/control/set_temp
</div>
</div>


<h3 id="63-数据格式json">6.3 数据格式（JSON）</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="c1">// 传感器上报
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;device_id&#34;</span><span class="p">:</span> <span class="s2">&#34;esp32-living-01&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;timestamp&#34;</span><span class="p">:</span> <span class="mi">1712012400</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;data&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;temperature&#34;</span><span class="p">:</span> <span class="mf">25.5</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;humidity&#34;</span><span class="p">:</span> <span class="mf">60.2</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;light&#34;</span><span class="p">:</span> <span class="mi">850</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 控制指令
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;command&#34;</span><span class="p">:</span> <span class="s2">&#34;set_temperature&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;value&#34;</span><span class="p">:</span> <span class="mi">26</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;mode&#34;</span><span class="p">:</span> <span class="s2">&#34;cool&#34;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="64-node-red-可视化">6.4 Node-RED 可视化</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 安装 Node-RED</span>
</span></span><span class="line"><span class="cl">docker run -d --name nodered <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>  -p 1880:1880 <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>  -v node_red_data:/data <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>  nodered/node-red
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 安装 MQTT 节点</span>
</span></span><span class="line"><span class="cl">npm install node-red-contrib-mqtt
</span></span></code></pre></div><h2 id="性能优化">性能优化</h2>
<h3 id="71-降低功耗">7.1 降低功耗</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="c1">// ESP32 深度睡眠 + MQTT
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#include</span> <span class="cpf">&lt;esp_deep_sleep.h&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">loop</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="c1">// 读取并发布数据
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="n">publish_sensor_data</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">  
</span></span><span class="line"><span class="cl">  <span class="c1">// 进入深度睡眠（10 分钟）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="n">esp_deep_sleep</span><span class="p">(</span><span class="mi">10</span> <span class="o">*</span> <span class="mi">60</span> <span class="o">*</span> <span class="mi">1000000</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="72-减少流量">7.2 减少流量</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="c1">// 变化时发布（而非定时）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">float</span> <span class="n">last_temp</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kt">float</span> <span class="n">current_temp</span> <span class="o">=</span> <span class="n">read_temperature</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="p">(</span><span class="n">abs</span><span class="p">(</span><span class="n">current_temp</span> <span class="o">-</span> <span class="n">last_temp</span><span class="p">)</span> <span class="o">&gt;</span> <span class="mf">0.5</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="n">client</span><span class="p">.</span><span class="n">publish</span><span class="p">(</span><span class="n">topic_temp</span><span class="p">,</span> <span class="n">String</span><span class="p">(</span><span class="n">current_temp</span><span class="p">).</span><span class="n">c_str</span><span class="p">());</span>
</span></span><span class="line"><span class="cl">  <span class="n">last_temp</span> <span class="o">=</span> <span class="n">current_temp</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="73-批量发布">7.3 批量发布</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="c1">// 多条消息打包
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">String</span> <span class="n">batch_msg</span> <span class="o">=</span> <span class="s">&#34;{</span><span class="se">\&#34;</span><span class="s">sensors</span><span class="se">\&#34;</span><span class="s">:[&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">sensor_count</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="n">batch_msg</span> <span class="o">+=</span> <span class="n">sensors</span><span class="p">[</span><span class="n">i</span><span class="p">].</span><span class="n">toJson</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="n">i</span> <span class="o">&lt;</span> <span class="n">sensor_count</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span> <span class="n">batch_msg</span> <span class="o">+=</span> <span class="s">&#34;,&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="n">batch_msg</span> <span class="o">+=</span> <span class="s">&#34;]}&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">client</span><span class="p">.</span><span class="n">publish</span><span class="p">(</span><span class="s">&#34;factory/batch&#34;</span><span class="p">,</span> <span class="n">batch_msg</span><span class="p">.</span><span class="n">c_str</span><span class="p">());</span>
</span></span></code></pre></div><h2 id="常见问题">常见问题</h2>
<h3 id="q1-连接频繁断开">Q1: 连接频繁断开</h3>
<p><strong>原因</strong>：Keep Alive 时间过短或网络不稳定。</p>
<p><strong>解决</strong>：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="n">client</span><span class="p">.</span><span class="n">setKeepAlive</span><span class="p">(</span><span class="mi">120</span><span class="p">);</span>  <span class="c1">// 延长到 120 秒
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">client</span><span class="p">.</span><span class="n">setSocketTimeout</span><span class="p">(</span><span class="mi">5000</span><span class="p">);</span>
</span></span></code></pre></div><h3 id="q2-消息丢失">Q2: 消息丢失</h3>
<p><strong>原因</strong>：QoS 等级过低或 Broker 配置问题。</p>
<p><strong>解决</strong>：</p>
<ul>
<li>关键数据使用 QoS 1 或 QoS 2</li>
<li>启用 Broker 消息持久化</li>
<li>客户端实现本地消息队列</li>
</ul>
<h3 id="q3-内存不足">Q3: 内存不足</h3>
<p><strong>原因</strong>：缓冲区过大或连接数过多。</p>
<p><strong>解决</strong>：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="c1">// 减小缓冲区
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">PubSubClient</span> <span class="nf">client</span><span class="p">(</span><span class="n">espClient</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="n">client</span><span class="p">.</span><span class="n">setBufferSize</span><span class="p">(</span><span class="mi">256</span><span class="p">);</span>  <span class="c1">// 默认 128
</span></span></span></code></pre></div><h2 id="总结">总结</h2>
<p>嵌入式 MQTT 开发要点：</p>
<ol>
<li><strong>选择合适 Broker</strong>：EMQX（企业级）、Mosquitto（轻量）</li>
<li><strong>理解 QoS 等级</strong>：根据场景选择 0/1/2</li>
<li><strong>规范 Topic 命名</strong>：层级清晰、语义明确</li>
<li><strong>实现重连机制</strong>：确保连接可靠性</li>
<li><strong>优化功耗流量</strong>：深度睡眠、变化发布</li>
</ol>
<p>掌握 MQTT，让你的嵌入式设备轻松接入物联网！</p>
<hr>
<p><em>本文基于 MQTT 3.1.1/5.0 协议和 2026 年主流嵌入式平台编写。</em></p>
<h2 id="参考资料">参考资料</h2>
<ul>
<li>MQTT 官方文档：https://mqtt.org/documentation</li>
<li>EMQX 文档：https://docs.emqx.com/</li>
<li>PubSubClient 库：https://pubsubclient.knolleary.net/</li>
<li>ESP-IDF MQTT：https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/protocols/mqtt.html</li>
</ul>
]]></content:encoded></item><item><title>嵌入式 JavaScript 引擎 MJS 完全指南 2026</title><link>https://tech-snippets.xyz/posts/embedded-javascript-mjs-complete-guide-2026/</link><pubDate>Thu, 02 Apr 2026 20:00:00 +0800</pubDate><guid>https://tech-snippets.xyz/posts/embedded-javascript-mjs-complete-guide-2026/</guid><description>深入解析 Cesanta MJS 嵌入式 JavaScript 引擎，从架构原理到 ESP32/STM32 实战，让你的 MCU 跑起 JavaScript 代码</description><content:encoded><![CDATA[<h2 id="引言">引言</h2>
<p>JavaScript 通常运行在浏览器或 Node.js 环境中，但在资源受限的嵌入式设备上运行 JavaScript 也是可能的。<strong>MJS（Mongoose JavaScript）</strong> 是 Cesanta 公司开发的超轻量级 JavaScript 引擎，专为嵌入式系统设计。</p>
<ul>
<li><strong>代码大小</strong>：仅 60-100KB ROM，10-30KB RAM</li>
<li><strong>性能</strong>：基于字节码解释器，执行效率高</li>
<li><strong>特性</strong>：支持 ES5 核心语法、异步回调、硬件访问</li>
</ul>
<p>本文从架构原理到实战项目，带你全面掌握嵌入式 JavaScript 开发。</p>
<h2 id="mjs-架构解析">MJS 架构解析</h2>
<h3 id="11-整体架构">1.1 整体架构</h3>

<figure>
<svg viewBox="0 0 850 520" xmlns="http://www.w3.org/2000/svg" class="no-invert">
  <defs>
    <style>
      .layer { fill: #f0f9ff; stroke: #0284c7; stroke-width: 2; rx: 8; }
      .core { fill: #dbeafe; stroke: #1d4ed8; stroke-width: 2; rx: 6; }
      .module { fill: #fef3c7; stroke: #d97706; stroke-width: 2; rx: 6; }
      .hw { fill: #f0fdf4; stroke: #16a34a; stroke-width: 2; rx: 6; }
      .text { fill: #1e293b; font-family: Arial, sans-serif; font-size: 14px; font-weight: 600; }
      .sub { fill: #64748b; font-family: Arial, sans-serif; font-size: 12px; }
      .arrow { stroke: #3b82f6; stroke-width: 2; fill: none; marker-end: url(#arrowhead); }
    </style>
    <marker id="arrowhead" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
      <polygon points="0 0, 10 3.5, 0 7" fill="#3b82f6"/>
    </marker>
  </defs>
  
  <!-- JavaScript 应用层 -->
  <rect class="layer" x="50" y="30" width="750" height="70"/>
  <text class="text" x="425" y="60" text-anchor="middle">JavaScript 应用代码</text>
  <text class="sub" x="425" y="82" text-anchor="middle">GPIO、I2C、SPI、网络、定时器</text>
  
  <!-- MJS 核心层 -->
  <rect class="core" x="50" y="130" width="750" height="100"/>
  <text class="text" x="425" y="160" text-anchor="middle">MJS 核心引擎</text>
  <rect class="core" x="80" y="175" width="130" height="40" style="fill:#bfdbfe;"/>
  <text class="sub" x="145" y="200" text-anchor="middle">词法分析器</text>
  <rect class="core" x="230" y="175" width="130" height="40" style="fill:#bfdbfe;"/>
  <text class="sub" x="295" y="200" text-anchor="middle">语法分析器</text>
  <rect class="core" x="380" y="175" width="130" height="40" style="fill:#bfdbfe;"/>
  <text class="sub" x="445" y="200" text-anchor="middle">字节码生成器</text>
  <rect class="core" x="530" y="175" width="130" height="40" style="fill:#bfdbfe;"/>
  <text class="sub" x="595" y="200" text-anchor="middle">虚拟机解释器</text>
  <rect class="core" x="680" y="175" width="90" height="40" style="fill:#bfdbfe;"/>
  <text class="sub" x="725" y="200" text-anchor="middle">垃圾回收</text>
  
  <!-- FFI 接口层 -->
  <rect class="module" x="50" y="260" width="750" height="80"/>
  <text class="text" x="425" y="290" text-anchor="middle">FFI（Foreign Function Interface）</text>
  <text class="sub" x="425" y="312" text-anchor="middle">C 函数绑定、硬件抽象层、系统调用</text>
  
  <!-- 硬件抽象层 -->
  <rect class="hw" x="50" y="370" width="220" height="70"/>
  <text class="text" x="160" y="400" text-anchor="middle">ESP32 HAL</text>
  <text class="sub" x="160" y="422" text-anchor="middle">GPIO、UART、I2C</text>
  
  <rect class="hw" x="315" y="370" width="220" height="70"/>
  <text class="text" x="425" y="400" text-anchor="middle">STM32 HAL</text>
  <text class="sub" x="425" y="422" text-anchor="middle">GPIO、SPI、ADC</text>
  
  <rect class="hw" x="580" y="370" width="220" height="70"/>
  <text class="text" x="690" y="400" text-anchor="middle">POSIX 层</text>
  <text class="sub" x="690" y="422" text-anchor="middle">Linux、macOS</text>
  
  <!-- 连接箭头 -->
  <line x1="425" y1="100" x2="425" y2="130" stroke="#3b82f6" stroke-width="2"/>
  <line x1="425" y1="230" x2="425" y2="260" stroke="#3b82f6" stroke-width="2"/>
  <line x1="425" y1="340" x2="425" y2="370" stroke="#3b82f6" stroke-width="2"/>
</svg>
<figcaption style="text-align:center;color:#888;font-size:16px;font-weight:600;margin-top:12px;">MJS 引擎架构分层</figcaption>
</figure>


<p><strong>核心组件</strong>：</p>
<ul>
<li><strong>词法/语法分析器</strong>：解析 JavaScript 源代码</li>
<li><strong>字节码生成器</strong>：编译为紧凑的字节码格式</li>
<li><strong>虚拟机解释器</strong>：执行字节码，管理堆栈</li>
<li><strong>垃圾回收器</strong>：自动内存管理（标记 - 清除算法）</li>
<li><strong>FFI 接口</strong>：调用 C 函数，访问硬件资源</li>
</ul>
<h3 id="12-内存模型">1.2 内存模型</h3>

<figure>
<svg viewBox="0 0 800 280" xmlns="http://www.w3.org/2000/svg" class="no-invert">
  <defs>
    <style>
      .heap { fill: #fef3c7; stroke: #d97706; stroke-width: 2; rx: 6; }
      .obj { fill: #fde68a; stroke: #b45309; stroke-width: 1.5; rx: 4; }
      .text { fill: #1e293b; font-family: Arial, sans-serif; font-size: 13px; font-weight: 600; }
      .label { fill: #64748b; font-family: Arial, sans-serif; font-size: 12px; }
      .ptr { stroke: #dc2626; stroke-width: 2; marker-end: url(#arrow-red); }
    </style>
    <marker id="arrow-red" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
      <polygon points="0 0, 10 3.5, 0 7" fill="#dc2626"/>
    </marker>
  </defs>
  
  <!-- 堆内存 -->
  <rect class="heap" x="50" y="50" width="700" height="180"/>
  <text class="text" x="400" y="75" text-anchor="middle">MJS Heap（堆内存）</text>
  
  <!-- 对象 1 -->
  <rect class="obj" x="80" y="100" width="120" height="50"/>
  <text class="text" x="140" y="120" text-anchor="middle">Object A</text>
  <text class="label" x="140" y="138" text-anchor="middle">{x: 1, y: 2}</text>
  
  <!-- 对象 2 -->
  <rect class="obj" x="250" y="100" width="120" height="50"/>
  <text class="text" x="310" y="120" text-anchor="middle">Array B</text>
  <text class="label" x="310" y="138" text-anchor="middle">[1, 2, 3]</text>
  
  <!-- 对象 3 -->
  <rect class="obj" x="420" y="100" width="120" height="50"/>
  <text class="text" x="480" y="120" text-anchor="middle">Function C</text>
  <text class="label" x="480" y="138" text-anchor="middle">function(){}</text>
  
  <!-- 对象 4（垃圾） -->
  <rect class="obj" x="590" y="100" width="120" height="50" style="fill:#fcd34d;opacity:0.5;"/>
  <text class="text" x="650" y="120" text-anchor="middle" style="opacity:0.6;">Object D</text>
  <text class="label" x="650" y="138" text-anchor="middle" style="opacity:0.6;">unreachable</text>
  
  <!-- 根引用 -->
  <circle cx="150" cy="200" r="8" fill="#dc2626"/>
  <text class="label" x="150" y="225" text-anchor="middle">Root Set</text>
  <path class="ptr" d="M150 200 L150 170 L150 155"/>
  
  <!-- 引用链 -->
  <path class="ptr" d="M200 130 L240 130" style="stroke:#64748b;stroke-width:1.5;"/>
  <path class="ptr" d="M380 130 L410 130" style="stroke:#64748b;stroke-width:1.5;"/>
  
  <!-- GC 标记 -->
  <text class="label" x="700" y="220" text-anchor="middle" fill="#dc2626;">⚠️ 不可达对象</text>
  <text class="label" x="700" y="240" text-anchor="middle" fill="#16a34a;">✓ 存活对象</text>
</svg>
<figcaption style="text-align:center;color:#888;font-size:16px;font-weight:600;margin-top:12px;">MJS 垃圾回收机制</figcaption>
</figure>


<p><strong>内存管理特点</strong>：</p>
<ul>
<li><strong>堆大小可配置</strong>：默认 10-50KB，根据 MCU RAM 调整</li>
<li><strong>标记 - 清除算法</strong>：从根节点遍历，标记可达对象</li>
<li><strong>自动回收</strong>：当堆内存不足时触发 GC</li>
<li><strong>手动触发</strong>：<code>mjs_gc()</code> 强制垃圾回收</li>
</ul>
<h3 id="13-与其他引擎对比">1.3 与其他引擎对比</h3>
<table>
<thead>
<tr>
<th>特性</th>
<th>MJS</th>
<th>JerryScript</th>
<th>Duktape</th>
<th>Espruino</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>ROM 占用</strong></td>
<td>60KB</td>
<td>80KB</td>
<td>100KB</td>
<td>150KB</td>
</tr>
<tr>
<td><strong>RAM 占用</strong></td>
<td>10KB</td>
<td>15KB</td>
<td>20KB</td>
<td>30KB</td>
</tr>
<tr>
<td><strong>ES 支持</strong></td>
<td>ES5</td>
<td>ES5.1</td>
<td>ES5</td>
<td>ES5+</td>
</tr>
<tr>
<td><strong>FFI 支持</strong></td>
<td>✅</td>
<td>✅</td>
<td>✅</td>
<td>✅</td>
</tr>
<tr>
<td><strong>异步支持</strong></td>
<td>回调</td>
<td>Promise</td>
<td>无</td>
<td>事件驱动</td>
</tr>
<tr>
<td><strong>商业许可</strong></td>
<td>双授权</td>
<td>Apache 2.0</td>
<td>MIT</td>
<td>GPL</td>
</tr>
</tbody>
</table>
<h2 id="环境搭建">环境搭建</h2>
<h3 id="21-获取源码">2.1 获取源码</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 克隆 MJS 仓库</span>
</span></span><span class="line"><span class="cl">git clone https://github.com/cesanta/mjs.git
</span></span><span class="line"><span class="cl"><span class="nb">cd</span> mjs
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 切换到稳定版本</span>
</span></span><span class="line"><span class="cl">git checkout 1.20.0  <span class="c1"># 2026 年最新版</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 目录结构</span>
</span></span><span class="line"><span class="cl">.
</span></span><span class="line"><span class="cl">├── mjs.c              <span class="c1"># 核心引擎实现</span>
</span></span><span class="line"><span class="cl">├── mjs.h              <span class="c1"># 公共 API 头文件</span>
</span></span><span class="line"><span class="cl">├── ffi.c              <span class="c1"># FFI 接口实现</span>
</span></span><span class="line"><span class="cl">├── gc.c               <span class="c1"># 垃圾回收器</span>
</span></span><span class="line"><span class="cl">├── examples/          <span class="c1"># 示例代码</span>
</span></span><span class="line"><span class="cl">└── ports/             <span class="c1"># 平台移植层</span>
</span></span><span class="line"><span class="cl">    ├── esp32/
</span></span><span class="line"><span class="cl">    ├── stm32/
</span></span><span class="line"><span class="cl">    └── posix/
</span></span></code></pre></div><h3 id="22-编译配置">2.2 编译配置</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// mjs_config.h - 内存配置
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#ifndef MJS_CONFIG_INCLUDED
</span></span></span><span class="line"><span class="cl"><span class="cp">#define MJS_CONFIG_INCLUDED
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="c1">// 堆大小（字节）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#define MJS_HEAP_SIZE (32 * 1024)  </span><span class="c1">// 32KB
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="c1">// 最大字符串长度
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#define MJS_MAX_STRING_LEN 256
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="c1">// 最大嵌套深度
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#define MJS_MAX_CALL_DEPTH 32
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="c1">// 启用调试输出
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#define MJS_ENABLE_DEBUG 1
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="c1">// 启用 FFI
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#define MJS_ENABLE_FFI 1
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="cp">#endif
</span></span></span></code></pre></div><h3 id="23-最小可运行示例posix">2.3 最小可运行示例（POSIX）</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&#34;mjs.h&#34;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;stdlib.h&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 创建 MJS 实例
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">struct</span> <span class="n">mjs</span> <span class="o">*</span><span class="n">mjs</span> <span class="o">=</span> <span class="nf">mjs_create</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 执行 JavaScript 代码
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="kt">mjs_err_t</span> <span class="n">err</span> <span class="o">=</span> <span class="nf">mjs_exec</span><span class="p">(</span><span class="n">mjs</span><span class="p">,</span> <span class="s">&#34;print(&#39;Hello from MJS!&#39;);&#34;</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">err</span> <span class="o">!=</span> <span class="n">MJS_OK</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nf">printf</span><span class="p">(</span><span class="s">&#34;错误：%s</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">,</span> <span class="nf">mjs_strerror</span><span class="p">(</span><span class="n">mjs</span><span class="p">,</span> <span class="n">err</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 执行数学运算
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="kt">mjs_val_t</span> <span class="n">result</span> <span class="o">=</span> <span class="nf">mjs_exec</span><span class="p">(</span><span class="n">mjs</span><span class="p">,</span> <span class="s">&#34;2 + 3 * 4&#34;</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="kt">double</span> <span class="n">num</span> <span class="o">=</span> <span class="nf">mjs_get_double</span><span class="p">(</span><span class="n">mjs</span><span class="p">,</span> <span class="n">result</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nf">printf</span><span class="p">(</span><span class="s">&#34;结果：%.0f</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">,</span> <span class="n">num</span><span class="p">);</span>  <span class="c1">// 输出：14
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    
</span></span><span class="line"><span class="cl">    <span class="c1">// 清理资源
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">mjs_destroy</span><span class="p">(</span><span class="n">mjs</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 编译命令
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">gcc</span> <span class="o">-</span><span class="n">o</span> <span class="n">mjs_demo</span> <span class="n">mjs</span><span class="p">.</span><span class="n">c</span> <span class="n">main</span><span class="p">.</span><span class="n">c</span> <span class="o">-</span><span class="n">lm</span> <span class="o">-</span><span class="n">ldl</span>
</span></span></code></pre></div><h2 id="esp32-实战">ESP32 实战</h2>
<h3 id="31-硬件连接">3.1 硬件连接</h3>
<table>
<thead>
<tr>
<th>模块</th>
<th>ESP32 引脚</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>LED</td>
<td>GPIO 2</td>
<td>板载 LED</td>
</tr>
<tr>
<td>按键</td>
<td>GPIO 0</td>
<td>启动按钮</td>
</tr>
<tr>
<td>UART</td>
<td>GPIO 1/3</td>
<td>串口调试</td>
</tr>
</tbody>
</table>
<h3 id="32-arduino-集成">3.2 Arduino 集成</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;mjs.h&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&#34;mjs_port_esp32.h&#34;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="c1">// 全局 MJS 实例
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">struct</span> <span class="nc">mjs</span> <span class="o">*</span><span class="n">g_mjs</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// FFI 函数：控制 GPIO
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">mjs_val_t</span> <span class="nf">ffi_gpio_write</span><span class="p">(</span><span class="k">struct</span> <span class="nc">mjs</span> <span class="o">*</span><span class="n">mjs</span><span class="p">,</span> <span class="n">mjs_val_t</span> <span class="n">args</span><span class="p">[],</span> <span class="kt">int</span> <span class="n">nargs</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">nargs</span> <span class="o">!=</span> <span class="mi">2</span><span class="p">)</span> <span class="k">return</span> <span class="n">mjs_mk_undefined</span><span class="p">(</span><span class="n">mjs</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="n">pin</span> <span class="o">=</span> <span class="n">mjs_get_int</span><span class="p">(</span><span class="n">mjs</span><span class="p">,</span> <span class="n">args</span><span class="p">[</span><span class="mi">0</span><span class="p">]);</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="n">value</span> <span class="o">=</span> <span class="n">mjs_get_int</span><span class="p">(</span><span class="n">mjs</span><span class="p">,</span> <span class="n">args</span><span class="p">[</span><span class="mi">1</span><span class="p">]);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="n">pinMode</span><span class="p">(</span><span class="n">pin</span><span class="p">,</span> <span class="n">OUTPUT</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">digitalWrite</span><span class="p">(</span><span class="n">pin</span><span class="p">,</span> <span class="n">value</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">mjs_mk_undefined</span><span class="p">(</span><span class="n">mjs</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// FFI 函数：延时
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">mjs_val_t</span> <span class="nf">ffi_delay</span><span class="p">(</span><span class="k">struct</span> <span class="nc">mjs</span> <span class="o">*</span><span class="n">mjs</span><span class="p">,</span> <span class="n">mjs_val_t</span> <span class="n">args</span><span class="p">[],</span> <span class="kt">int</span> <span class="n">nargs</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">nargs</span> <span class="o">!=</span> <span class="mi">1</span><span class="p">)</span> <span class="k">return</span> <span class="n">mjs_mk_undefined</span><span class="p">(</span><span class="n">mjs</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="n">ms</span> <span class="o">=</span> <span class="n">mjs_get_int</span><span class="p">(</span><span class="n">mjs</span><span class="p">,</span> <span class="n">args</span><span class="p">[</span><span class="mi">0</span><span class="p">]);</span>
</span></span><span class="line"><span class="cl">    <span class="n">delay</span><span class="p">(</span><span class="n">ms</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">mjs_mk_undefined</span><span class="p">(</span><span class="n">mjs</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// FFI 函数：读取模拟量
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">mjs_val_t</span> <span class="nf">ffi_analog_read</span><span class="p">(</span><span class="k">struct</span> <span class="nc">mjs</span> <span class="o">*</span><span class="n">mjs</span><span class="p">,</span> <span class="n">mjs_val_t</span> <span class="n">args</span><span class="p">[],</span> <span class="kt">int</span> <span class="n">nargs</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">nargs</span> <span class="o">!=</span> <span class="mi">1</span><span class="p">)</span> <span class="k">return</span> <span class="n">mjs_mk_undefined</span><span class="p">(</span><span class="n">mjs</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="n">pin</span> <span class="o">=</span> <span class="n">mjs_get_int</span><span class="p">(</span><span class="n">mjs</span><span class="p">,</span> <span class="n">args</span><span class="p">[</span><span class="mi">0</span><span class="p">]);</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="n">value</span> <span class="o">=</span> <span class="n">analogRead</span><span class="p">(</span><span class="n">pin</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">mjs_mk_number</span><span class="p">(</span><span class="n">mjs</span><span class="p">,</span> <span class="n">value</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 注册 FFI 函数
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="nf">register_ffi</span><span class="p">(</span><span class="k">struct</span> <span class="nc">mjs</span> <span class="o">*</span><span class="n">mjs</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">mjs_set</span><span class="p">(</span><span class="n">mjs</span><span class="p">,</span> <span class="n">mjs_get_global</span><span class="p">(</span><span class="n">mjs</span><span class="p">),</span> <span class="s">&#34;gpioWrite&#34;</span><span class="p">,</span> 
</span></span><span class="line"><span class="cl">            <span class="n">MJS_MK_FN</span><span class="p">(</span><span class="n">ffi_gpio_write</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="n">mjs_set</span><span class="p">(</span><span class="n">mjs</span><span class="p">,</span> <span class="n">mjs_get_global</span><span class="p">(</span><span class="n">mjs</span><span class="p">),</span> <span class="s">&#34;delay&#34;</span><span class="p">,</span> 
</span></span><span class="line"><span class="cl">            <span class="n">MJS_MK_FN</span><span class="p">(</span><span class="n">ffi_delay</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="n">mjs_set</span><span class="p">(</span><span class="n">mjs</span><span class="p">,</span> <span class="n">mjs_get_global</span><span class="p">(</span><span class="n">mjs</span><span class="p">),</span> <span class="s">&#34;analogRead&#34;</span><span class="p">,</span> 
</span></span><span class="line"><span class="cl">            <span class="n">MJS_MK_FN</span><span class="p">(</span><span class="n">ffi_analog_read</span><span class="p">));</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">setup</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">Serial</span><span class="p">.</span><span class="n">begin</span><span class="p">(</span><span class="mi">115200</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 初始化 MJS
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">g_mjs</span> <span class="o">=</span> <span class="n">mjs_create</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="n">register_ffi</span><span class="p">(</span><span class="n">g_mjs</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 执行初始化脚本
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">init_script</span> <span class="o">=</span> <span class="n">R</span><span class="s">&#34;(</span>
</span></span><span class="line"><span class="cl">        <span class="n">print</span><span class="p">(</span><span class="err">&#39;</span><span class="n">MJS</span> <span class="err">已初始化&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="n">print</span><span class="p">(</span><span class="err">&#39;</span><span class="n">ESP32</span> <span class="err">芯片：&#39;</span> <span class="o">+</span> <span class="n">chipModel</span><span class="p">());</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="c1">// LED 闪烁测试
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="n">gpioWrite</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="mi">1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="n">delay</span><span class="p">(</span><span class="mi">500</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="n">gpioWrite</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">)</span><span class="s">&#34;;</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="n">mjs_err_t</span> <span class="n">err</span> <span class="o">=</span> <span class="n">mjs_exec</span><span class="p">(</span><span class="n">g_mjs</span><span class="p">,</span> <span class="n">init_script</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">err</span> <span class="o">!=</span> <span class="n">MJS_OK</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">Serial</span><span class="p">.</span><span class="n">printf</span><span class="p">(</span><span class="s">&#34;初始化失败：%s</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">,</span> <span class="n">mjs_strerror</span><span class="p">(</span><span class="n">g_mjs</span><span class="p">,</span> <span class="n">err</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">loop</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 从串口读取 JavaScript 代码并执行
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">if</span> <span class="p">(</span><span class="n">Serial</span><span class="p">.</span><span class="n">available</span><span class="p">())</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">String</span> <span class="n">code</span> <span class="o">=</span> <span class="n">Serial</span><span class="p">.</span><span class="n">readStringUntil</span><span class="p">(</span><span class="sc">&#39;\n&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="n">mjs_err_t</span> <span class="n">err</span> <span class="o">=</span> <span class="n">mjs_exec</span><span class="p">(</span><span class="n">g_mjs</span><span class="p">,</span> <span class="n">code</span><span class="p">.</span><span class="n">c_str</span><span class="p">(),</span> <span class="nb">NULL</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">err</span> <span class="o">!=</span> <span class="n">MJS_OK</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">Serial</span><span class="p">.</span><span class="n">printf</span><span class="p">(</span><span class="s">&#34;执行错误：%s</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">,</span> <span class="n">mjs_strerror</span><span class="p">(</span><span class="n">g_mjs</span><span class="p">,</span> <span class="n">err</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="n">delay</span><span class="p">(</span><span class="mi">100</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="33-javascript-应用示例">3.3 JavaScript 应用示例</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="c1">// blink.js - LED 闪烁
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">var</span> <span class="nx">ledPin</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">state</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">setInterval</span><span class="p">(</span><span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">state</span> <span class="o">=</span> <span class="mi">1</span> <span class="o">-</span> <span class="nx">state</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nx">gpioWrite</span><span class="p">(</span><span class="nx">ledPin</span><span class="p">,</span> <span class="nx">state</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nx">print</span><span class="p">(</span><span class="s1">&#39;LED 状态：&#39;</span> <span class="o">+</span> <span class="nx">state</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">},</span> <span class="mi">1000</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// sensor.js - 传感器读取
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">var</span> <span class="nx">sensorPin</span> <span class="o">=</span> <span class="mi">34</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">threshold</span> <span class="o">=</span> <span class="mi">2000</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">readSensor</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">var</span> <span class="nx">value</span> <span class="o">=</span> <span class="nx">analogRead</span><span class="p">(</span><span class="nx">sensorPin</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nx">print</span><span class="p">(</span><span class="s1">&#39;传感器值：&#39;</span> <span class="o">+</span> <span class="nx">value</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="nx">value</span> <span class="o">&gt;</span> <span class="nx">threshold</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">print</span><span class="p">(</span><span class="s1">&#39;⚠️ 超过阈值！&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="nx">gpioWrite</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="mi">1</span><span class="p">);</span>  <span class="c1">// 打开 LED 告警
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">gpioWrite</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 每 500ms 读取一次
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">setInterval</span><span class="p">(</span><span class="nx">readSensor</span><span class="p">,</span> <span class="mi">500</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// wifi.js - 网络连接（需要 ESP32 WiFi 库）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">var</span> <span class="nx">ssid</span> <span class="o">=</span> <span class="s1">&#39;MyWiFi&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">password</span> <span class="o">=</span> <span class="s1">&#39;secret123&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">WiFi</span><span class="p">.</span><span class="nx">begin</span><span class="p">(</span><span class="nx">ssid</span><span class="p">,</span> <span class="nx">password</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nx">print</span><span class="p">(</span><span class="s1">&#39;正在连接 WiFi...&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">while</span> <span class="p">(</span><span class="nx">WiFi</span><span class="p">.</span><span class="nx">status</span><span class="p">()</span> <span class="o">!==</span> <span class="nx">WL_CONNECTED</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">delay</span><span class="p">(</span><span class="mi">500</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nx">print</span><span class="p">(</span><span class="s1">&#39;.&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">print</span><span class="p">(</span><span class="s1">&#39;已连接！IP: &#39;</span> <span class="o">+</span> <span class="nx">WiFi</span><span class="p">.</span><span class="nx">localIP</span><span class="p">());</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 发送 HTTP 请求
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">var</span> <span class="nx">http</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">HTTPClient</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="nx">http</span><span class="p">.</span><span class="nx">begin</span><span class="p">(</span><span class="s1">&#39;http://api.example.com/data&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">code</span> <span class="o">=</span> <span class="nx">http</span><span class="p">.</span><span class="nx">GET</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="nx">print</span><span class="p">(</span><span class="s1">&#39;HTTP 状态码：&#39;</span> <span class="o">+</span> <span class="nx">code</span><span class="p">);</span>
</span></span></code></pre></div><h2 id="stm32-实战">STM32 实战</h2>
<h3 id="41-基于-hal-的移植">4.1 基于 HAL 的移植</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// mjs_port_stm32.c
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#include</span> <span class="cpf">&#34;mjs.h&#34;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&#34;stm32f4xx_hal.h&#34;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;stdlib.h&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="c1">// 系统时钟获取
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">mjs_val_t</span> <span class="nf">ffi_sys_clock</span><span class="p">(</span><span class="k">struct</span> <span class="n">mjs</span> <span class="o">*</span><span class="n">mjs</span><span class="p">,</span> <span class="kt">mjs_val_t</span> <span class="n">args</span><span class="p">[],</span> <span class="kt">int</span> <span class="n">nargs</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">uint32_t</span> <span class="n">clock</span> <span class="o">=</span> <span class="nf">HAL_RCC_GetHCLKFreq</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nf">mjs_mk_number</span><span class="p">(</span><span class="n">mjs</span><span class="p">,</span> <span class="n">clock</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// GPIO 初始化
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">mjs_val_t</span> <span class="nf">ffi_gpio_init</span><span class="p">(</span><span class="k">struct</span> <span class="n">mjs</span> <span class="o">*</span><span class="n">mjs</span><span class="p">,</span> <span class="kt">mjs_val_t</span> <span class="n">args</span><span class="p">[],</span> <span class="kt">int</span> <span class="n">nargs</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">nargs</span> <span class="o">!=</span> <span class="mi">2</span><span class="p">)</span> <span class="k">return</span> <span class="nf">mjs_mk_undefined</span><span class="p">(</span><span class="n">mjs</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="n">GPIO_TypeDef</span> <span class="o">*</span><span class="n">port</span> <span class="o">=</span> <span class="p">(</span><span class="n">GPIO_TypeDef</span> <span class="o">*</span><span class="p">)</span><span class="nf">mjs_get_ptr</span><span class="p">(</span><span class="n">mjs</span><span class="p">,</span> <span class="n">args</span><span class="p">[</span><span class="mi">0</span><span class="p">]);</span>
</span></span><span class="line"><span class="cl">    <span class="kt">uint16_t</span> <span class="n">pin</span> <span class="o">=</span> <span class="nf">mjs_get_int</span><span class="p">(</span><span class="n">mjs</span><span class="p">,</span> <span class="n">args</span><span class="p">[</span><span class="mi">1</span><span class="p">]);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="n">GPIO_InitTypeDef</span> <span class="n">GPIO_InitStruct</span> <span class="o">=</span> <span class="p">{</span><span class="mi">0</span><span class="p">};</span>
</span></span><span class="line"><span class="cl">    <span class="n">GPIO_InitStruct</span><span class="p">.</span><span class="n">Pin</span> <span class="o">=</span> <span class="n">pin</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">GPIO_InitStruct</span><span class="p">.</span><span class="n">Mode</span> <span class="o">=</span> <span class="n">GPIO_MODE_OUTPUT_PP</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">GPIO_InitStruct</span><span class="p">.</span><span class="n">Pull</span> <span class="o">=</span> <span class="n">GPIO_NOPULL</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">GPIO_InitStruct</span><span class="p">.</span><span class="n">Speed</span> <span class="o">=</span> <span class="n">GPIO_SPEED_FREQ_LOW</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nf">HAL_GPIO_Init</span><span class="p">(</span><span class="n">port</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">GPIO_InitStruct</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nf">mjs_mk_undefined</span><span class="p">(</span><span class="n">mjs</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// GPIO 写入
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">mjs_val_t</span> <span class="nf">ffi_gpio_write</span><span class="p">(</span><span class="k">struct</span> <span class="n">mjs</span> <span class="o">*</span><span class="n">mjs</span><span class="p">,</span> <span class="kt">mjs_val_t</span> <span class="n">args</span><span class="p">[],</span> <span class="kt">int</span> <span class="n">nargs</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">nargs</span> <span class="o">!=</span> <span class="mi">3</span><span class="p">)</span> <span class="k">return</span> <span class="nf">mjs_mk_undefined</span><span class="p">(</span><span class="n">mjs</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="n">GPIO_TypeDef</span> <span class="o">*</span><span class="n">port</span> <span class="o">=</span> <span class="p">(</span><span class="n">GPIO_TypeDef</span> <span class="o">*</span><span class="p">)</span><span class="nf">mjs_get_ptr</span><span class="p">(</span><span class="n">mjs</span><span class="p">,</span> <span class="n">args</span><span class="p">[</span><span class="mi">0</span><span class="p">]);</span>
</span></span><span class="line"><span class="cl">    <span class="kt">uint16_t</span> <span class="n">pin</span> <span class="o">=</span> <span class="nf">mjs_get_int</span><span class="p">(</span><span class="n">mjs</span><span class="p">,</span> <span class="n">args</span><span class="p">[</span><span class="mi">1</span><span class="p">]);</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="n">value</span> <span class="o">=</span> <span class="nf">mjs_get_int</span><span class="p">(</span><span class="n">mjs</span><span class="p">,</span> <span class="n">args</span><span class="p">[</span><span class="mi">2</span><span class="p">]);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="nf">HAL_GPIO_WritePin</span><span class="p">(</span><span class="n">port</span><span class="p">,</span> <span class="n">pin</span><span class="p">,</span> <span class="n">value</span> <span class="o">?</span> <span class="nl">GPIO_PIN_SET</span> <span class="p">:</span> <span class="n">GPIO_PIN_RESET</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nf">mjs_mk_undefined</span><span class="p">(</span><span class="n">mjs</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// ADC 读取
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">mjs_val_t</span> <span class="nf">ffi_adc_read</span><span class="p">(</span><span class="k">struct</span> <span class="n">mjs</span> <span class="o">*</span><span class="n">mjs</span><span class="p">,</span> <span class="kt">mjs_val_t</span> <span class="n">args</span><span class="p">[],</span> <span class="kt">int</span> <span class="n">nargs</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">nargs</span> <span class="o">!=</span> <span class="mi">1</span><span class="p">)</span> <span class="k">return</span> <span class="nf">mjs_mk_undefined</span><span class="p">(</span><span class="n">mjs</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="n">ADC_HandleTypeDef</span> <span class="o">*</span><span class="n">hadc</span> <span class="o">=</span> <span class="p">(</span><span class="n">ADC_HandleTypeDef</span> <span class="o">*</span><span class="p">)</span><span class="nf">mjs_get_ptr</span><span class="p">(</span><span class="n">mjs</span><span class="p">,</span> <span class="n">args</span><span class="p">[</span><span class="mi">0</span><span class="p">]);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="nf">HAL_ADC_Start</span><span class="p">(</span><span class="n">hadc</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nf">HAL_ADC_PollForConversion</span><span class="p">(</span><span class="n">hadc</span><span class="p">,</span> <span class="mi">10</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="kt">uint32_t</span> <span class="n">value</span> <span class="o">=</span> <span class="nf">HAL_ADC_GetValue</span><span class="p">(</span><span class="n">hadc</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nf">HAL_ADC_Stop</span><span class="p">(</span><span class="n">hadc</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nf">mjs_mk_number</span><span class="p">(</span><span class="n">mjs</span><span class="p">,</span> <span class="n">value</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 注册所有 FFI 函数
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="nf">mjs_init_stm32</span><span class="p">(</span><span class="k">struct</span> <span class="n">mjs</span> <span class="o">*</span><span class="n">mjs</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 系统函数
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">mjs_set</span><span class="p">(</span><span class="n">mjs</span><span class="p">,</span> <span class="nf">mjs_get_global</span><span class="p">(</span><span class="n">mjs</span><span class="p">),</span> <span class="s">&#34;sysClock&#34;</span><span class="p">,</span> 
</span></span><span class="line"><span class="cl">            <span class="nf">MJS_MK_FN</span><span class="p">(</span><span class="n">ffi_sys_clock</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// GPIO 函数
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">mjs_set</span><span class="p">(</span><span class="n">mjs</span><span class="p">,</span> <span class="nf">mjs_get_global</span><span class="p">(</span><span class="n">mjs</span><span class="p">),</span> <span class="s">&#34;gpioInit&#34;</span><span class="p">,</span> 
</span></span><span class="line"><span class="cl">            <span class="nf">MJS_MK_FN</span><span class="p">(</span><span class="n">ffi_gpio_init</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="nf">mjs_set</span><span class="p">(</span><span class="n">mjs</span><span class="p">,</span> <span class="nf">mjs_get_global</span><span class="p">(</span><span class="n">mjs</span><span class="p">),</span> <span class="s">&#34;gpioWrite&#34;</span><span class="p">,</span> 
</span></span><span class="line"><span class="cl">            <span class="nf">MJS_MK_FN</span><span class="p">(</span><span class="n">ffi_gpio_write</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// ADC 函数
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">mjs_set</span><span class="p">(</span><span class="n">mjs</span><span class="p">,</span> <span class="nf">mjs_get_global</span><span class="p">(</span><span class="n">mjs</span><span class="p">),</span> <span class="s">&#34;adcRead&#34;</span><span class="p">,</span> 
</span></span><span class="line"><span class="cl">            <span class="nf">MJS_MK_FN</span><span class="p">(</span><span class="n">ffi_adc_read</span><span class="p">));</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="42-主程序">4.2 主程序</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// main.c
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#include</span> <span class="cpf">&#34;mjs.h&#34;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&#34;stm32f4xx_hal.h&#34;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="c1">// MJS 堆内存（32KB）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">static</span> <span class="kt">uint8_t</span> <span class="n">mjs_heap</span><span class="p">[</span><span class="mi">32</span> <span class="o">*</span> <span class="mi">1024</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// JavaScript 脚本（存储在 Flash 中）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">const</span> <span class="kt">char</span> <span class="n">js_script</span><span class="p">[]</span> <span class="o">=</span> <span class="n">R</span><span class="s">&#34;(</span>
</span></span><span class="line"><span class="cl">    <span class="nf">print</span><span class="p">(</span><span class="err">&#39;</span><span class="n">STM32</span> <span class="n">MJS</span> <span class="err">演示&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nf">print</span><span class="p">(</span><span class="err">&#39;系统时钟：&#39;</span> <span class="o">+</span> <span class="nf">sysClock</span><span class="p">()</span> <span class="o">+</span> <span class="err">&#39;</span> <span class="n">Hz</span><span class="err">&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 初始化 GPIO
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">var</span> <span class="n">GPIOA</span> <span class="o">=</span> <span class="mh">0x40020000</span><span class="p">;</span>  <span class="c1">// GPIOA 基地址
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">var</span> <span class="n">PIN5</span> <span class="o">=</span> <span class="mh">0x0020</span><span class="p">;</span>       <span class="c1">// PA5
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    
</span></span><span class="line"><span class="cl">    <span class="nf">gpioInit</span><span class="p">(</span><span class="n">GPIOA</span><span class="p">,</span> <span class="n">PIN5</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// LED 闪烁
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">for</span> <span class="p">(</span><span class="n">var</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="mi">10</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nf">gpioWrite</span><span class="p">(</span><span class="n">GPIOA</span><span class="p">,</span> <span class="n">PIN5</span><span class="p">,</span> <span class="mi">1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="nf">delay</span><span class="p">(</span><span class="mi">500</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="nf">gpioWrite</span><span class="p">(</span><span class="n">GPIOA</span><span class="p">,</span> <span class="n">PIN5</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="nf">delay</span><span class="p">(</span><span class="mi">500</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="nf">print</span><span class="p">(</span><span class="err">&#39;演示完成！&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span><span class="s">&#34;;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nf">HAL_Init</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="nf">SystemClock_Config</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 初始化 MJS
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">struct</span> <span class="n">mjs</span> <span class="o">*</span><span class="n">mjs</span> <span class="o">=</span> <span class="nf">mjs_create</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="nf">mjs_init_stm32</span><span class="p">(</span><span class="n">mjs</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 执行脚本
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="kt">mjs_err_t</span> <span class="n">err</span> <span class="o">=</span> <span class="nf">mjs_exec</span><span class="p">(</span><span class="n">mjs</span><span class="p">,</span> <span class="n">js_script</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">err</span> <span class="o">!=</span> <span class="n">MJS_OK</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nf">printf</span><span class="p">(</span><span class="s">&#34;错误：%s</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">,</span> <span class="nf">mjs_strerror</span><span class="p">(</span><span class="n">mjs</span><span class="p">,</span> <span class="n">err</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 进入空闲循环
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">while</span> <span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nf">__WFI</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h2 id="高级特性">高级特性</h2>
<h3 id="51-异步编程">5.1 异步编程</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="c1">// 回调函数风格
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">function</span> <span class="nx">readSensor</span><span class="p">(</span><span class="nx">callback</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">var</span> <span class="nx">value</span> <span class="o">=</span> <span class="nx">analogRead</span><span class="p">(</span><span class="mi">34</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nx">callback</span><span class="p">(</span><span class="nx">value</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">readSensor</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">val</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">print</span><span class="p">(</span><span class="s1">&#39;传感器值：&#39;</span> <span class="o">+</span> <span class="nx">val</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 使用 setTimeout 实现非阻塞
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">var</span> <span class="nx">counter</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">tick</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">counter</span><span class="o">++</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nx">print</span><span class="p">(</span><span class="s1">&#39;计数器：&#39;</span> <span class="o">+</span> <span class="nx">counter</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nx">setTimeout</span><span class="p">(</span><span class="nx">tick</span><span class="p">,</span> <span class="mi">1000</span><span class="p">);</span>  <span class="c1">// 1 秒后再次调用
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">tick</span><span class="p">();</span>  <span class="c1">// 启动
</span></span></span></code></pre></div><h3 id="52-模块系统">5.2 模块系统</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="c1">// math.js - 数学模块
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">var</span> <span class="nx">MathLib</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">PI</span><span class="o">:</span> <span class="mf">3.14159</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="nx">add</span><span class="o">:</span> <span class="kd">function</span><span class="p">(</span><span class="nx">a</span><span class="p">,</span> <span class="nx">b</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="nx">a</span> <span class="o">+</span> <span class="nx">b</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="nx">multiply</span><span class="o">:</span> <span class="kd">function</span><span class="p">(</span><span class="nx">a</span><span class="p">,</span> <span class="nx">b</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="nx">a</span> <span class="o">*</span> <span class="nx">b</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="nx">circleArea</span><span class="o">:</span> <span class="kd">function</span><span class="p">(</span><span class="nx">r</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">PI</span> <span class="o">*</span> <span class="nx">r</span> <span class="o">*</span> <span class="nx">r</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// main.js - 使用模块
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">load</span><span class="p">(</span><span class="s1">&#39;math.js&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">result</span> <span class="o">=</span> <span class="nx">MathLib</span><span class="p">.</span><span class="nx">add</span><span class="p">(</span><span class="mi">5</span><span class="p">,</span> <span class="mi">3</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nx">print</span><span class="p">(</span><span class="s1">&#39;5 + 3 = &#39;</span> <span class="o">+</span> <span class="nx">result</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">area</span> <span class="o">=</span> <span class="nx">MathLib</span><span class="p">.</span><span class="nx">circleArea</span><span class="p">(</span><span class="mi">10</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nx">print</span><span class="p">(</span><span class="s1">&#39;圆面积：&#39;</span> <span class="o">+</span> <span class="nx">area</span><span class="p">);</span>
</span></span></code></pre></div><h3 id="53-错误处理">5.3 错误处理</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="c1">// try-catch 风格（MJS 模拟实现）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">function</span> <span class="nx">safeExec</span><span class="p">(</span><span class="nx">fn</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">try</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">fn</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="nx">e</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">print</span><span class="p">(</span><span class="s1">&#39;错误：&#39;</span> <span class="o">+</span> <span class="nx">e</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 实际 MJS 中使用错误回调
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">function</span> <span class="nx">divide</span><span class="p">(</span><span class="nx">a</span><span class="p">,</span> <span class="nx">b</span><span class="p">,</span> <span class="nx">errorCallback</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="nx">b</span> <span class="o">===</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">errorCallback</span><span class="p">(</span><span class="s1">&#39;除零错误&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="kc">undefined</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nx">a</span> <span class="o">/</span> <span class="nx">b</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">divide</span><span class="p">(</span><span class="mi">10</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">print</span><span class="p">(</span><span class="s1">&#39;捕获错误：&#39;</span> <span class="o">+</span> <span class="nx">err</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span></code></pre></div><h3 id="54-与-c-代码互操作">5.4 与 C 代码互操作</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// C 侧：定义结构体
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="n">x</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="n">y</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="n">Point</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// JavaScript 侧：访问结构体
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">var</span> <span class="n">Point</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nl">create</span><span class="p">:</span> <span class="nf">ffi</span><span class="p">(</span><span class="nf">function</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">Point</span> <span class="o">*</span><span class="n">p</span> <span class="o">=</span> <span class="nf">malloc</span><span class="p">(</span><span class="k">sizeof</span><span class="p">(</span><span class="n">Point</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">        <span class="n">p</span><span class="o">-&gt;</span><span class="n">x</span> <span class="o">=</span> <span class="n">x</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">p</span><span class="o">-&gt;</span><span class="n">y</span> <span class="o">=</span> <span class="n">y</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">p</span><span class="p">;</span>  <span class="c1">// 返回指针
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="p">}),</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="nl">getX</span><span class="p">:</span> <span class="nf">ffi</span><span class="p">(</span><span class="nf">function</span><span class="p">(</span><span class="n">ptr</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="p">((</span><span class="n">Point</span> <span class="o">*</span><span class="p">)</span><span class="n">ptr</span><span class="p">)</span><span class="o">-&gt;</span><span class="n">x</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}),</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="nl">setX</span><span class="p">:</span> <span class="nf">ffi</span><span class="p">(</span><span class="nf">function</span><span class="p">(</span><span class="n">ptr</span><span class="p">,</span> <span class="n">val</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="p">((</span><span class="n">Point</span> <span class="o">*</span><span class="p">)</span><span class="n">ptr</span><span class="p">)</span><span class="o">-&gt;</span><span class="n">x</span> <span class="o">=</span> <span class="n">val</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">})</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 使用
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">var</span> <span class="n">p</span> <span class="o">=</span> <span class="n">Point</span><span class="p">.</span><span class="nf">create</span><span class="p">(</span><span class="mi">10</span><span class="p">,</span> <span class="mi">20</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nf">print</span><span class="p">(</span><span class="err">&#39;</span><span class="n">X</span> <span class="o">=</span> <span class="err">&#39;</span> <span class="o">+</span> <span class="n">Point</span><span class="p">.</span><span class="nf">getX</span><span class="p">(</span><span class="n">p</span><span class="p">));</span>
</span></span><span class="line"><span class="cl"><span class="n">Point</span><span class="p">.</span><span class="nf">setX</span><span class="p">(</span><span class="n">p</span><span class="p">,</span> <span class="mi">30</span><span class="p">);</span>
</span></span></code></pre></div><h2 id="性能优化">性能优化</h2>
<h3 id="61-内存优化">6.1 内存优化</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// 减小堆大小（如果 RAM 紧张）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#define MJS_HEAP_SIZE (16 * 1024)  </span><span class="c1">// 16KB
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="c1">// 手动触发 GC
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="nf">periodic_gc</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">static</span> <span class="kt">uint32_t</span> <span class="n">counter</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="o">++</span><span class="n">counter</span> <span class="o">%</span> <span class="mi">100</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nf">mjs_gc</span><span class="p">(</span><span class="n">g_mjs</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="nf">print</span><span class="p">(</span><span class="err">&#39;已执行垃圾回收&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="62-代码优化">6.2 代码优化</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="c1">// ❌ 避免：频繁创建对象
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">function</span> <span class="nx">bad</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="p">(</span><span class="kd">var</span> <span class="nx">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="o">&lt;</span> <span class="mi">100</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="kd">var</span> <span class="nx">obj</span> <span class="o">=</span> <span class="p">{</span><span class="nx">x</span><span class="o">:</span> <span class="nx">i</span><span class="p">,</span> <span class="nx">y</span><span class="o">:</span> <span class="nx">i</span> <span class="o">*</span> <span class="mi">2</span><span class="p">};</span>  <span class="c1">// 每次循环创建新对象
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// ✅ 推荐：复用对象
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">function</span> <span class="nx">good</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">var</span> <span class="nx">obj</span> <span class="o">=</span> <span class="p">{</span><span class="nx">x</span><span class="o">:</span> <span class="mi">0</span><span class="p">,</span> <span class="nx">y</span><span class="o">:</span> <span class="mi">0</span><span class="p">};</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="p">(</span><span class="kd">var</span> <span class="nx">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="o">&lt;</span> <span class="mi">100</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">obj</span><span class="p">.</span><span class="nx">x</span> <span class="o">=</span> <span class="nx">i</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="nx">obj</span><span class="p">.</span><span class="nx">y</span> <span class="o">=</span> <span class="nx">i</span> <span class="o">*</span> <span class="mi">2</span><span class="p">;</span>  <span class="c1">// 复用同一对象
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// ❌ 避免：长字符串拼接
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">var</span> <span class="nx">s</span> <span class="o">=</span> <span class="s1">&#39;&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">for</span> <span class="p">(</span><span class="kd">var</span> <span class="nx">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="o">&lt;</span> <span class="mi">100</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">s</span> <span class="o">+=</span> <span class="nx">i</span><span class="p">.</span><span class="nx">toString</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// ✅ 推荐：使用数组
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">var</span> <span class="nx">arr</span> <span class="o">=</span> <span class="p">[];</span>
</span></span><span class="line"><span class="cl"><span class="k">for</span> <span class="p">(</span><span class="kd">var</span> <span class="nx">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="o">&lt;</span> <span class="mi">100</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">arr</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="nx">i</span><span class="p">.</span><span class="nx">toString</span><span class="p">());</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">s</span> <span class="o">=</span> <span class="nx">arr</span><span class="p">.</span><span class="nx">join</span><span class="p">(</span><span class="s1">&#39;&#39;</span><span class="p">);</span>
</span></span></code></pre></div><h3 id="63-预编译字节码">6.3 预编译字节码</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 将 JavaScript 编译为字节码（减小体积）</span>
</span></span><span class="line"><span class="cl">./mjs_compile.js app.js &gt; app.mbc
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 加载字节码</span>
</span></span><span class="line"><span class="cl">mjs_exec_file<span class="o">(</span>mjs, <span class="s2">&#34;app.mbc&#34;</span>, NULL<span class="o">)</span><span class="p">;</span>
</span></span></code></pre></div><h2 id="实际项目案例">实际项目案例</h2>
<h3 id="71-智能家居控制器">7.1 智能家居控制器</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="c1">// smart_home.js
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">var</span> <span class="nx">devices</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">light</span><span class="o">:</span> <span class="p">{</span><span class="nx">pin</span><span class="o">:</span> <span class="mi">2</span><span class="p">,</span> <span class="nx">state</span><span class="o">:</span> <span class="kc">false</span><span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="nx">fan</span><span class="o">:</span> <span class="p">{</span><span class="nx">pin</span><span class="o">:</span> <span class="mi">4</span><span class="p">,</span> <span class="nx">state</span><span class="o">:</span> <span class="kc">false</span><span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="nx">ac</span><span class="o">:</span> <span class="p">{</span><span class="nx">pin</span><span class="o">:</span> <span class="mi">5</span><span class="p">,</span> <span class="nx">state</span><span class="o">:</span> <span class="kc">false</span><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">toggle</span><span class="p">(</span><span class="nx">device</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">var</span> <span class="nx">dev</span> <span class="o">=</span> <span class="nx">devices</span><span class="p">[</span><span class="nx">device</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">dev</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">print</span><span class="p">(</span><span class="s1">&#39;未知设备：&#39;</span> <span class="o">+</span> <span class="nx">device</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="nx">dev</span><span class="p">.</span><span class="nx">state</span> <span class="o">=</span> <span class="o">!</span><span class="nx">dev</span><span class="p">.</span><span class="nx">state</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nx">gpioWrite</span><span class="p">(</span><span class="nx">dev</span><span class="p">.</span><span class="nx">pin</span><span class="p">,</span> <span class="nx">dev</span><span class="p">.</span><span class="nx">state</span> <span class="o">?</span> <span class="mi">1</span> <span class="o">:</span> <span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nx">print</span><span class="p">(</span><span class="nx">device</span> <span class="o">+</span> <span class="s1">&#39; 已&#39;</span> <span class="o">+</span> <span class="p">(</span><span class="nx">dev</span><span class="p">.</span><span class="nx">state</span> <span class="o">?</span> <span class="s1">&#39;开启&#39;</span> <span class="o">:</span> <span class="s1">&#39;关闭&#39;</span><span class="p">));</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// MQTT 集成（通过 FFI）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">mqtt</span><span class="p">.</span><span class="nx">subscribe</span><span class="p">(</span><span class="s1">&#39;home/+/control&#39;</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">topic</span><span class="p">,</span> <span class="nx">msg</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">var</span> <span class="nx">parts</span> <span class="o">=</span> <span class="nx">topic</span><span class="p">.</span><span class="nx">split</span><span class="p">(</span><span class="s1">&#39;/&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="kd">var</span> <span class="nx">room</span> <span class="o">=</span> <span class="nx">parts</span><span class="p">[</span><span class="mi">1</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">    <span class="kd">var</span> <span class="nx">action</span> <span class="o">=</span> <span class="nx">msg</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="nx">toggle</span><span class="p">(</span><span class="nx">room</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 定时任务
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">setInterval</span><span class="p">(</span><span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 每 5 分钟上报状态
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="kd">var</span> <span class="nx">status</span> <span class="o">=</span> <span class="nx">JSON</span><span class="p">.</span><span class="nx">stringify</span><span class="p">(</span><span class="nx">devices</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nx">mqtt</span><span class="p">.</span><span class="nx">publish</span><span class="p">(</span><span class="s1">&#39;home/status&#39;</span><span class="p">,</span> <span class="nx">status</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">},</span> <span class="mi">300000</span><span class="p">);</span>
</span></span></code></pre></div><h3 id="72-数据记录器">7.2 数据记录器</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="c1">// data_logger.js
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">var</span> <span class="nx">fs</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;fs&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">logFile</span> <span class="o">=</span> <span class="s1">&#39;/sdcard/sensor_log.csv&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 初始化 CSV 文件
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">fs</span><span class="p">.</span><span class="nx">exists</span><span class="p">(</span><span class="nx">logFile</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">fs</span><span class="p">.</span><span class="nx">write</span><span class="p">(</span><span class="nx">logFile</span><span class="p">,</span> <span class="s1">&#39;timestamp,temperature,humidity\n&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">logData</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">var</span> <span class="nx">temp</span> <span class="o">=</span> <span class="nx">readTemperature</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="kd">var</span> <span class="nx">humid</span> <span class="o">=</span> <span class="nx">readHumidity</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="kd">var</span> <span class="nx">ts</span> <span class="o">=</span> <span class="nb">Date</span><span class="p">.</span><span class="nx">now</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="kd">var</span> <span class="nx">line</span> <span class="o">=</span> <span class="nx">ts</span> <span class="o">+</span> <span class="s1">&#39;,&#39;</span> <span class="o">+</span> <span class="nx">temp</span> <span class="o">+</span> <span class="s1">&#39;,&#39;</span> <span class="o">+</span> <span class="nx">humid</span> <span class="o">+</span> <span class="s1">&#39;\n&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nx">fs</span><span class="p">.</span><span class="nx">append</span><span class="p">(</span><span class="nx">logFile</span><span class="p">,</span> <span class="nx">line</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="nx">print</span><span class="p">(</span><span class="s1">&#39;已记录：&#39;</span> <span class="o">+</span> <span class="nx">temp</span> <span class="o">+</span> <span class="s1">&#39;°C, &#39;</span> <span class="o">+</span> <span class="nx">humid</span> <span class="o">+</span> <span class="s1">&#39;%&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 每分钟记录一次
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">setInterval</span><span class="p">(</span><span class="nx">logData</span><span class="p">,</span> <span class="mi">60000</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 命令处理
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">onCommand</span><span class="p">(</span><span class="s1">&#39;export&#39;</span><span class="p">,</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">var</span> <span class="nx">data</span> <span class="o">=</span> <span class="nx">fs</span><span class="p">.</span><span class="nx">read</span><span class="p">(</span><span class="nx">logFile</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nx">mqtt</span><span class="p">.</span><span class="nx">publish</span><span class="p">(</span><span class="s1">&#39;sensor/export&#39;</span><span class="p">,</span> <span class="nx">data</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span></code></pre></div><h2 id="常见问题">常见问题</h2>
<h3 id="q1-内存不足错误">Q1: 内存不足错误</h3>
<p><strong>原因</strong>：堆大小不足或内存泄漏。</p>
<p><strong>解决</strong>：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// 增大堆
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#define MJS_HEAP_SIZE (64 * 1024)
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="c1">// 检查内存使用
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nf">print</span><span class="p">(</span><span class="err">&#39;堆使用：&#39;</span> <span class="o">+</span> <span class="nf">mjs_get_heap_used</span><span class="p">()</span> <span class="o">+</span> <span class="err">&#39;</span> <span class="err">字节&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 手动 GC
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nf">mjs_gc</span><span class="p">(</span><span class="n">mjs</span><span class="p">);</span>
</span></span></code></pre></div><h3 id="q2-ffi-调用崩溃">Q2: FFI 调用崩溃</h3>
<p><strong>原因</strong>：指针类型不匹配或内存访问越界。</p>
<p><strong>解决</strong>：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="c1">// 确保 C 侧正确注册类型
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">mjs_set</span><span class="p">(</span><span class="nx">mjs</span><span class="p">,</span> <span class="nx">global</span><span class="p">,</span> <span class="s2">&#34;myFunc&#34;</span><span class="p">,</span> <span class="nx">MJS_MK_FN</span><span class="p">(</span><span class="nx">ffi_my_func</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// JavaScript 侧检查参数
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">if</span> <span class="p">(</span><span class="k">typeof</span> <span class="nx">arg</span> <span class="o">!==</span> <span class="s1">&#39;number&#39;</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">print</span><span class="p">(</span><span class="s1">&#39;参数类型错误&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="q3-执行速度慢">Q3: 执行速度慢</h3>
<p><strong>原因</strong>：频繁 GC 或复杂计算。</p>
<p><strong>解决</strong>：</p>
<ul>
<li>减少对象创建频率</li>
<li>将计算密集型任务移到 C 侧</li>
<li>使用字节码预编译</li>
</ul>
<h2 id="总结">总结</h2>
<p>MJS 嵌入式 JavaScript 引擎要点：</p>
<ol>
<li><strong>超轻量级</strong>：60KB ROM，10KB RAM，适合 MCU</li>
<li><strong>FFI 强大</strong>：轻松调用 C 函数，访问硬件</li>
<li><strong>开发高效</strong>：JavaScript 语法，快速迭代</li>
<li><strong>跨平台</strong>：ESP32、STM32、Linux 均支持</li>
<li><strong>社区活跃</strong>：Cesanta 维护，文档完善</li>
</ol>
<p>使用 MJS，让嵌入式开发像 Web 开发一样高效！</p>
<hr>
<p><em>本文基于 MJS 1.20.0 和 2026 年嵌入式 JavaScript 生态编写。</em></p>
<h2 id="参考资料">参考资料</h2>
<ul>
<li>MJS 官方仓库：https://github.com/cesanta/mjs</li>
<li>Mongoose OS 文档：https://mongoose-os.com/docs/</li>
<li>Cesanta 官网：https://cesanta.com/</li>
<li>JavaScript 嵌入式应用案例：https://esp32.com/viewtopic.php?t=12345</li>
</ul>
]]></content:encoded></item><item><title>嵌入式 Rust 编程完全指南 2026</title><link>https://tech-snippets.xyz/posts/embedded-rust-programming-guide-2026/</link><pubDate>Thu, 02 Apr 2026 19:00:00 +0800</pubDate><guid>https://tech-snippets.xyz/posts/embedded-rust-programming-guide-2026/</guid><description>使用 Rust 编写安全高效的嵌入式代码，从零开始掌握 embedded Rust 开发</description><content:encoded><![CDATA[<h2 id="引言">引言</h2>
<p>嵌入式开发长期被 C/C++ 主导，但内存安全问题频发。Rust 凭借<strong>零成本抽象</strong>和<strong>编译期内存安全</strong>，正在成为嵌入式开发的新选择。STM32、ESP32、nRF 等主流 MCU 都已支持 Rust。</p>
<p>本文从零开始，带你掌握嵌入式 Rust 开发的核心技能。</p>
<h2 id="为什么选择嵌入式-rust">为什么选择嵌入式 Rust？</h2>
<h3 id="11-内存安全-without-gc">1.1 内存安全 without GC</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="c1">// C 代码：可能的空指针解引用
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">int</span><span class="w"> </span><span class="o">*</span><span class="n">ptr</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">get_sensor_data</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="n">int</span><span class="w"> </span><span class="n">value</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="o">*</span><span class="n">ptr</span><span class="p">;</span><span class="w">  </span><span class="c1">// ❌ 如果 ptr 为空，崩溃
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="c1">// Rust 代码：编译期检查
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">let</span><span class="w"> </span><span class="n">data</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">get_sensor_data</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kd">let</span><span class="w"> </span><span class="n">value</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="o">*</span><span class="n">data</span><span class="p">;</span><span class="w">  </span><span class="c1">// ✅ Option 类型强制处理 None 情况
</span></span></span></code></pre></div><h3 id="12-所有权系统防止数据竞争">1.2 所有权系统防止数据竞争</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="c1">// 多任务访问共享资源
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">static</span><span class="w"> </span><span class="k">mut</span><span class="w"> </span><span class="no">SENSOR_DATA</span>: <span class="kt">u32</span> <span class="o">=</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span><span class="w">  </span><span class="c1">// ❌ C 的全局变量，不安全
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="c1">// Rust 使用 Mutex 保护共享数据
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">static</span><span class="w"> </span><span class="no">SENSOR_DATA</span>: <span class="nc">Mutex</span><span class="o">&lt;</span><span class="kt">u32</span><span class="o">&gt;</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Mutex</span>::<span class="n">new</span><span class="p">(</span><span class="mi">0</span><span class="p">);</span><span class="w">  </span><span class="c1">// ✅ 编译期保证线程安全
</span></span></span></code></pre></div><h3 id="13-零成本抽象">1.3 零成本抽象</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="c1">//  trait 在编译期展开，无运行时开销
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">trait</span><span class="w"> </span><span class="n">Sensor</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">fn</span> <span class="nf">read</span><span class="p">(</span><span class="o">&amp;</span><span class="bp">self</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="kt">u16</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">struct</span> <span class="nc">TemperatureSensor</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="cm">/* ... */</span><span class="w"> </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">impl</span><span class="w"> </span><span class="n">Sensor</span><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="n">TemperatureSensor</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">fn</span> <span class="nf">read</span><span class="p">(</span><span class="o">&amp;</span><span class="bp">self</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="kt">u16</span> <span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="c1">// 直接编译为高效机器码
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><h2 id="环境搭建">环境搭建</h2>
<h3 id="21-安装-rust-工具链">2.1 安装 Rust 工具链</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 安装 rustup</span>
</span></span><span class="line"><span class="cl">curl --proto <span class="s1">&#39;=https&#39;</span> --tlsv1.2 -sSf https://sh.rustup.rs <span class="p">|</span> sh
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 添加嵌入式目标</span>
</span></span><span class="line"><span class="cl">rustup target add thumbv7em-none-eabihf  <span class="c1"># Cortex-M4/M7</span>
</span></span><span class="line"><span class="cl">rustup target add thumbv6m-none-eabi     <span class="c1"># Cortex-M0/M0+</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 安装必要工具</span>
</span></span><span class="line"><span class="cl">cargo install cargo-binutils
</span></span><span class="line"><span class="cl">rustup component add llvm-tools-preview
</span></span></code></pre></div><h3 id="22-项目模板">2.2 项目模板</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 使用 cargo-generate 创建项目</span>
</span></span><span class="line"><span class="cl">cargo install cargo-generate
</span></span><span class="line"><span class="cl">cargo generate --git https://github.com/rust-embedded/cortex-m-quickstart
</span></span></code></pre></div><h3 id="项目结构">项目结构</h3>

<figure>
<svg viewBox="0 0 700 380" xmlns="http://www.w3.org/2000/svg" class="no-invert">
  <defs>
    <style>
      .folder { fill: #f0f4f8; stroke: #2563eb; stroke-width: 2; rx: 6; }
      .file { fill: #ffffff; stroke: #3b82f6; stroke-width: 2; rx: 4; }
      .text { fill: #1e293b; font-family: 'Courier New', monospace; font-size: 16px; font-weight: 600; }
      .label { fill: #64748b; font-family: Arial, sans-serif; font-size: 14px; }
      .line { stroke: #94a3b8; stroke-width: 2; fill: none; }
      .root-text { fill: #2563eb; font-family: 'Courier New', monospace; font-size: 16px; font-weight: bold; }
    </style>
  </defs>
  <text class="root-text" x="20" y="40">my-embedded-app/</text>
  <line class="line" x1="30" y1="50" x2="30" y2="90"/>
  <line class="line" x1="30" y1="90" x2="50" y2="90"/>
  <rect class="file" x="60" y="70" width="180" height="35"/>
  <text class="text" x="70" y="93" font-size="15">Cargo.toml</text>
  <text class="label" x="260" y="93"># 依赖配置</text>
  <line class="line" x1="30" y1="90" x2="30" y2="130"/>
  <line class="line" x1="30" y1="130" x2="50" y2="130"/>
  <rect class="file" x="60" y="110" width="140" height="35"/>
  <text class="text" x="70" y="133" font-size="15">build.rs</text>
  <text class="label" x="260" y="133"># 构建脚本</text>
  <line class="line" x1="30" y1="130" x2="30" y2="170"/>
  <line class="line" x1="30" y1="170" x2="50" y2="170"/>
  <rect class="file" x="60" y="150" width="140" height="35"/>
  <text class="text" x="70" y="173" font-size="15">memory.x</text>
  <text class="label" x="260" y="173"># 内存布局</text>
  <line class="line" x1="30" y1="170" x2="30" y2="210"/>
  <line class="line" x1="30" y1="210" x2="50" y2="210"/>
  <rect class="folder" x="60" y="190" width="100" height="35"/>
  <text class="text" x="70" y="213" font-size="15">src/</text>
  <line class="line" x1="110" y1="210" x2="110" y2="250"/>
  <line class="line" x1="110" y1="250" x2="130" y2="250"/>
  <rect class="file" x="140" y="230" width="120" height="35"/>
  <text class="text" x="150" y="253" font-size="15">main.rs</text>
  <text class="label" x="340" y="253"># 入口</text>
  <line class="line" x1="110" y1="250" x2="110" y2="290"/>
  <line class="line" x1="110" y1="290" x2="130" y2="290"/>
  <rect class="file" x="140" y="270" width="200" height="35"/>
  <text class="text" x="150" y="293" font-size="15">peripherals.rs</text>
  <text class="label" x="340" y="293"># 外设驱动</text>
</svg>
<figcaption style="text-align:center;color:#888;font-size:16px;font-weight:600;margin-top:12px;">嵌入式 Rust 项目结构</figcaption>
</figure>


<h3 id="23-cargotoml-配置">2.3 Cargo.toml 配置</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="cl"><span class="p">[</span><span class="nx">package</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="nx">name</span> <span class="p">=</span> <span class="s2">&#34;my-embedded-app&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nx">version</span> <span class="p">=</span> <span class="s2">&#34;0.1.0&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nx">edition</span> <span class="p">=</span> <span class="s2">&#34;2021&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">[</span><span class="nx">dependencies</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="nx">cortex-m</span> <span class="p">=</span> <span class="s2">&#34;0.7&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nx">cortex-m-rt</span> <span class="p">=</span> <span class="s2">&#34;0.7&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nx">panic-halt</span> <span class="p">=</span> <span class="s2">&#34;0.2&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nx">embedded-hal</span> <span class="p">=</span> <span class="s2">&#34;0.2&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c"># 芯片特定 PAC</span>
</span></span><span class="line"><span class="cl"><span class="nx">stm32f4xx-hal</span> <span class="p">=</span> <span class="p">{</span> <span class="nx">version</span> <span class="p">=</span> <span class="s2">&#34;0.21&#34;</span><span class="p">,</span> <span class="nx">features</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;rt&#34;</span><span class="p">,</span> <span class="s2">&#34;stm32f407&#34;</span><span class="p">]</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">[</span><span class="nx">profile</span><span class="p">.</span><span class="nx">release</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="nx">lto</span> <span class="p">=</span> <span class="kc">true</span>
</span></span><span class="line"><span class="cl"><span class="nx">debug</span> <span class="p">=</span> <span class="kc">true</span>
</span></span></code></pre></div><h2 id="核心概念">核心概念</h2>
<h3 id="31-外设访问">3.1 外设访问</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="cp">#![no_std]</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="cp">#![no_main]</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">use</span><span class="w"> </span><span class="n">panic_halt</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="n">_</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">use</span><span class="w"> </span><span class="n">cortex_m_rt</span>::<span class="n">entry</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">use</span><span class="w"> </span><span class="n">stm32f4xx_hal</span>::<span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">pac</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">prelude</span>::<span class="o">*</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">};</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="cp">#[entry]</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">fn</span> <span class="nf">main</span><span class="p">()</span><span class="w"> </span>-&gt; <span class="o">!</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c1">// 获取外设寄存器
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="w">    </span><span class="kd">let</span><span class="w"> </span><span class="n">dp</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">pac</span>::<span class="n">Peripherals</span>::<span class="n">take</span><span class="p">().</span><span class="n">unwrap</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kd">let</span><span class="w"> </span><span class="n">cp</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">cortex_m</span>::<span class="n">Peripherals</span>::<span class="n">take</span><span class="p">().</span><span class="n">unwrap</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c1">// 配置时钟
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="w">    </span><span class="kd">let</span><span class="w"> </span><span class="n">rcc</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">dp</span><span class="p">.</span><span class="no">RCC</span><span class="p">.</span><span class="n">constrain</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kd">let</span><span class="w"> </span><span class="n">clocks</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">rcc</span><span class="p">.</span><span class="n">cfgr</span><span class="p">.</span><span class="n">sysclk</span><span class="p">(</span><span class="mf">168.</span><span class="n">MHz</span><span class="p">()).</span><span class="n">freeze</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c1">// 配置 GPIO
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="w">    </span><span class="kd">let</span><span class="w"> </span><span class="n">gpioa</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">dp</span><span class="p">.</span><span class="no">GPIOA</span><span class="p">.</span><span class="n">split</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kd">let</span><span class="w"> </span><span class="k">mut</span><span class="w"> </span><span class="n">led</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">gpioa</span><span class="p">.</span><span class="n">pa5</span><span class="p">.</span><span class="n">into_push_pull_output</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c1">// 闪烁 LED
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="w">    </span><span class="k">loop</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="n">led</span><span class="p">.</span><span class="n">set_high</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="n">cortex_m</span>::<span class="n">asm</span>::<span class="n">delay</span><span class="p">(</span><span class="mi">8_000_000</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="n">led</span><span class="p">.</span><span class="n">set_low</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="n">cortex_m</span>::<span class="n">asm</span>::<span class="n">delay</span><span class="p">(</span><span class="mi">8_000_000</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><h3 id="32-中断处理">3.2 中断处理</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="k">use</span><span class="w"> </span><span class="n">cortex_m_rt</span>::<span class="n">exception</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">use</span><span class="w"> </span><span class="n">stm32f4xx_hal</span>::<span class="n">pac</span>::<span class="n">interrupt</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="c1">// 系统异常
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#[exception]</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">fn</span> <span class="nf">HardFault</span><span class="p">(</span><span class="n">ef</span>: <span class="kp">&amp;</span><span class="nc">cortex_m_rt</span>::<span class="n">ExceptionFrame</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="o">!</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c1">// 记录错误信息
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="w">    </span><span class="k">loop</span><span class="w"> </span><span class="p">{}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="c1">// 外部中断
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#[interrupt]</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">fn</span> <span class="nf">EXTI0</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c1">// 清除中断标志
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="w">    </span><span class="kd">let</span><span class="w"> </span><span class="n">dp</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">unsafe</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">pac</span>::<span class="n">Peripherals</span>::<span class="n">steal</span><span class="p">()</span><span class="w"> </span><span class="p">};</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">dp</span><span class="p">.</span><span class="no">EXTI</span><span class="p">.</span><span class="n">pr</span><span class="p">.</span><span class="n">write</span><span class="p">(</span><span class="o">|</span><span class="n">w</span><span class="o">|</span><span class="w"> </span><span class="n">w</span><span class="p">.</span><span class="n">pr0</span><span class="p">().</span><span class="n">set_bit</span><span class="p">());</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c1">// 处理中断逻辑
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><h3 id="33-使用-embedded-hal-trait">3.3 使用 embedded-hal trait</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="k">use</span><span class="w"> </span><span class="n">embedded_hal</span>::<span class="n">digital</span>::<span class="n">v2</span>::<span class="n">OutputPin</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">use</span><span class="w"> </span><span class="n">embedded_hal</span>::<span class="n">blocking</span>::<span class="n">delay</span>::<span class="n">DelayMs</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="c1">// 编写通用驱动
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">pub</span><span class="w"> </span><span class="k">struct</span> <span class="nc">LedDriver</span><span class="o">&lt;</span><span class="no">PIN</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">pin</span>: <span class="nc">PIN</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">impl</span><span class="o">&lt;</span><span class="no">PIN</span><span class="o">&gt;</span><span class="w"> </span><span class="n">LedDriver</span><span class="o">&lt;</span><span class="no">PIN</span><span class="o">&gt;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">where</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="no">PIN</span>: <span class="nc">OutputPin</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">blink</span><span class="o">&lt;</span><span class="n">D</span><span class="o">&gt;</span><span class="p">(</span><span class="o">&amp;</span><span class="k">mut</span><span class="w"> </span><span class="bp">self</span><span class="p">,</span><span class="w"> </span><span class="n">delay</span>: <span class="kp">&amp;</span><span class="nc">mut</span><span class="w"> </span><span class="n">D</span><span class="p">,</span><span class="w"> </span><span class="n">times</span>: <span class="kt">u8</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">where</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="n">D</span>: <span class="nc">DelayMs</span><span class="o">&lt;</span><span class="kt">u8</span><span class="o">&gt;</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="k">for</span><span class="w"> </span><span class="n">_</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="mi">0</span><span class="o">..</span><span class="n">times</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="bp">self</span><span class="p">.</span><span class="n">pin</span><span class="p">.</span><span class="n">set_high</span><span class="p">().</span><span class="n">unwrap</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="n">delay</span><span class="p">.</span><span class="n">delay_ms</span><span class="p">(</span><span class="mi">500</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="bp">self</span><span class="p">.</span><span class="n">pin</span><span class="p">.</span><span class="n">set_low</span><span class="p">().</span><span class="n">unwrap</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="n">delay</span><span class="p">.</span><span class="n">delay_ms</span><span class="p">(</span><span class="mi">500</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><h2 id="实战项目温湿度传感器">实战项目：温湿度传感器</h2>
<h3 id="41-硬件连接">4.1 硬件连接</h3>
<table>
<thead>
<tr>
<th>引脚</th>
<th>STM32F4</th>
<th>AHT20 传感器</th>
</tr>
</thead>
<tbody>
<tr>
<td>SCL</td>
<td>PB6</td>
<td>SCL</td>
</tr>
<tr>
<td>SDA</td>
<td>PB7</td>
<td>SDA</td>
</tr>
<tr>
<td>VCC</td>
<td>3.3V</td>
<td>VDD</td>
</tr>
<tr>
<td>GND</td>
<td>GND</td>
<td>GND</td>
</tr>
</tbody>
</table>
<h3 id="42-i2c-驱动代码">4.2 I2C 驱动代码</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="k">use</span><span class="w"> </span><span class="n">stm32f4xx_hal</span>::<span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">i2c</span>::<span class="p">{</span><span class="n">I2c</span><span class="p">,</span><span class="w"> </span><span class="n">DutyCycle</span><span class="p">,</span><span class="w"> </span><span class="n">Mode</span><span class="p">},</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">pac</span>::<span class="no">I2C1</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">};</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">pub</span><span class="w"> </span><span class="k">struct</span> <span class="nc">AHT20</span><span class="o">&lt;</span><span class="no">I2C</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">i2c</span>: <span class="nc">I2C</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">impl</span><span class="o">&lt;</span><span class="no">I2C</span><span class="o">&gt;</span><span class="w"> </span><span class="no">AHT20</span><span class="o">&lt;</span><span class="no">I2C</span><span class="o">&gt;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">where</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="no">I2C</span>: <span class="nc">embedded_hal</span>::<span class="n">blocking</span>::<span class="n">i2c</span>::<span class="n">WriteRead</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">       </span><span class="o">+</span><span class="w"> </span><span class="n">embedded_hal</span>::<span class="n">blocking</span>::<span class="n">i2c</span>::<span class="n">Write</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">new</span><span class="p">(</span><span class="n">i2c</span>: <span class="nc">I2C</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nc">Self</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="bp">Self</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">i2c</span><span class="w"> </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">measure</span><span class="p">(</span><span class="o">&amp;</span><span class="k">mut</span><span class="w"> </span><span class="bp">self</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nb">Result</span><span class="o">&lt;</span><span class="p">(</span><span class="kt">f32</span><span class="p">,</span><span class="w"> </span><span class="kt">f32</span><span class="p">),</span><span class="w"> </span><span class="p">()</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="c1">// 启动测量
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="w">        </span><span class="bp">self</span><span class="p">.</span><span class="n">i2c</span><span class="p">.</span><span class="n">write</span><span class="p">(</span><span class="mh">0x38</span><span class="p">,</span><span class="w"> </span><span class="o">&amp;</span><span class="p">[</span><span class="mh">0xAC</span><span class="p">,</span><span class="w"> </span><span class="mh">0x33</span><span class="p">,</span><span class="w"> </span><span class="mh">0x00</span><span class="p">]).</span><span class="n">map_err</span><span class="p">(</span><span class="o">|</span><span class="n">_</span><span class="o">|</span><span class="w"> </span><span class="p">())</span><span class="o">?</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="c1">// 等待转换完成
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="w">        </span><span class="n">cortex_m</span>::<span class="n">asm</span>::<span class="n">delay</span><span class="p">(</span><span class="mi">80_000</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="c1">// 读取数据
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="w">        </span><span class="kd">let</span><span class="w"> </span><span class="k">mut</span><span class="w"> </span><span class="n">buf</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[</span><span class="mi">0</span><span class="k">u8</span><span class="p">;</span><span class="w"> </span><span class="mi">6</span><span class="p">];</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="bp">self</span><span class="p">.</span><span class="n">i2c</span><span class="p">.</span><span class="n">write_read</span><span class="p">(</span><span class="mh">0x38</span><span class="p">,</span><span class="w"> </span><span class="o">&amp;</span><span class="p">[</span><span class="mh">0x71</span><span class="p">],</span><span class="w"> </span><span class="o">&amp;</span><span class="k">mut</span><span class="w"> </span><span class="n">buf</span><span class="p">).</span><span class="n">map_err</span><span class="p">(</span><span class="o">|</span><span class="n">_</span><span class="o">|</span><span class="w"> </span><span class="p">())</span><span class="o">?</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="c1">// 解析温湿度
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="w">        </span><span class="kd">let</span><span class="w"> </span><span class="n">humidity</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">((</span><span class="n">buf</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="kt">u32</span><span class="p">)</span><span class="w"> </span><span class="o">&lt;&lt;</span><span class="w"> </span><span class="mi">12</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="p">(</span><span class="n">buf</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="kt">u32</span><span class="p">)</span><span class="w"> </span><span class="o">&lt;&lt;</span><span class="w"> </span><span class="mi">4</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="p">(</span><span class="n">buf</span><span class="p">[</span><span class="mi">3</span><span class="p">]</span><span class="w"> </span><span class="o">&gt;&gt;</span><span class="w"> </span><span class="mi">4</span><span class="p">)</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="kt">u32</span><span class="p">)</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="kt">f32</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="kd">let</span><span class="w"> </span><span class="n">temperature</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">(((</span><span class="n">buf</span><span class="p">[</span><span class="mi">3</span><span class="p">]</span><span class="w"> </span><span class="o">&amp;</span><span class="w"> </span><span class="mh">0x0F</span><span class="p">)</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="kt">u32</span><span class="p">)</span><span class="w"> </span><span class="o">&lt;&lt;</span><span class="w"> </span><span class="mi">16</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="p">(</span><span class="n">buf</span><span class="p">[</span><span class="mi">4</span><span class="p">]</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="kt">u32</span><span class="p">)</span><span class="w"> </span><span class="o">&lt;&lt;</span><span class="w"> </span><span class="mi">8</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">buf</span><span class="p">[</span><span class="mi">5</span><span class="p">]</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="kt">u32</span><span class="p">)</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="kt">f32</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nb">Ok</span><span class="p">((</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="n">humidity</span><span class="w"> </span><span class="o">/</span><span class="w"> </span><span class="mf">1048576.0</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="mf">100.0</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="n">temperature</span><span class="w"> </span><span class="o">/</span><span class="w"> </span><span class="mf">1048576.0</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="mf">200.0</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="mf">50.0</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="p">))</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><h3 id="43-主程序">4.3 主程序</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="cp">#[entry]</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">fn</span> <span class="nf">main</span><span class="p">()</span><span class="w"> </span>-&gt; <span class="o">!</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kd">let</span><span class="w"> </span><span class="n">dp</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">pac</span>::<span class="n">Peripherals</span>::<span class="n">take</span><span class="p">().</span><span class="n">unwrap</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c1">// 配置时钟
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="w">    </span><span class="kd">let</span><span class="w"> </span><span class="n">rcc</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">dp</span><span class="p">.</span><span class="no">RCC</span><span class="p">.</span><span class="n">constrain</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kd">let</span><span class="w"> </span><span class="n">clocks</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">rcc</span><span class="p">.</span><span class="n">cfgr</span><span class="p">.</span><span class="n">sysclk</span><span class="p">(</span><span class="mf">168.</span><span class="n">MHz</span><span class="p">()).</span><span class="n">freeze</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c1">// 配置 I2C
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="w">    </span><span class="kd">let</span><span class="w"> </span><span class="n">gpiob</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">dp</span><span class="p">.</span><span class="no">GPIOB</span><span class="p">.</span><span class="n">split</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kd">let</span><span class="w"> </span><span class="n">scl</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">gpiob</span><span class="p">.</span><span class="n">pb6</span><span class="p">.</span><span class="n">into_alternate</span>::<span class="o">&lt;</span><span class="mi">4</span><span class="o">&gt;</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kd">let</span><span class="w"> </span><span class="n">sda</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">gpiob</span><span class="p">.</span><span class="n">pb7</span><span class="p">.</span><span class="n">into_alternate</span>::<span class="o">&lt;</span><span class="mi">4</span><span class="o">&gt;</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kd">let</span><span class="w"> </span><span class="n">i2c</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">I2c</span>::<span class="n">new</span><span class="p">(</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="n">dp</span><span class="p">.</span><span class="no">I2C1</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="p">(</span><span class="n">scl</span><span class="p">,</span><span class="w"> </span><span class="n">sda</span><span class="p">),</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="n">Mode</span>::<span class="n">Fast</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="n">frequency</span>: <span class="mf">400.</span><span class="n">kHz</span><span class="p">(),</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="n">duty_cycle</span>: <span class="nc">DutyCycle</span>::<span class="n">Ratio2to1</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="p">},</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="n">clocks</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c1">// 创建传感器
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="w">    </span><span class="kd">let</span><span class="w"> </span><span class="k">mut</span><span class="w"> </span><span class="n">sensor</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="no">AHT20</span>::<span class="n">new</span><span class="p">(</span><span class="n">i2c</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">loop</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="k">match</span><span class="w"> </span><span class="n">sensor</span><span class="p">.</span><span class="n">measure</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nb">Ok</span><span class="p">((</span><span class="n">humidity</span><span class="p">,</span><span class="w"> </span><span class="n">temperature</span><span class="p">))</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">                </span><span class="c1">// 通过串口打印
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="w">                </span><span class="fm">println!</span><span class="p">(</span><span class="s">&#34;温度：</span><span class="si">{:.2}</span><span class="s">°C, 湿度：</span><span class="si">{:.2}</span><span class="s">%&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">temperature</span><span class="p">,</span><span class="w"> </span><span class="n">humidity</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nb">Err</span><span class="p">(</span><span class="n">_</span><span class="p">)</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">                </span><span class="fm">println!</span><span class="p">(</span><span class="s">&#34;传感器读取失败&#34;</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="n">cortex_m</span>::<span class="n">asm</span>::<span class="n">delay</span><span class="p">(</span><span class="mi">80_000_000</span><span class="p">);</span><span class="w">  </span><span class="c1">// 1 秒
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><h2 id="调试技巧">调试技巧</h2>
<h3 id="51-使用-defmt-日志">5.1 使用 defmt 日志</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="cl"><span class="c"># Cargo.toml</span>
</span></span><span class="line"><span class="cl"><span class="p">[</span><span class="nx">dependencies</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="nx">defmt</span> <span class="p">=</span> <span class="s2">&#34;0.3&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nx">defmt-rtt</span> <span class="p">=</span> <span class="s2">&#34;0.4&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">[</span><span class="nx">profile</span><span class="p">.</span><span class="nx">dev</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="nx">debug</span> <span class="p">=</span> <span class="mi">2</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">[</span><span class="nx">profile</span><span class="p">.</span><span class="nx">release</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="nx">debug</span> <span class="p">=</span> <span class="mi">2</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="cp">#[defmt::global_logger]</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">struct</span> <span class="nc">Logger</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">unsafe</span><span class="w"> </span><span class="k">impl</span><span class="w"> </span><span class="n">defmt</span>::<span class="n">Logger</span><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="n">Logger</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">fn</span> <span class="nf">acquire</span><span class="p">()</span><span class="w"> </span><span class="p">{}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">unsafe</span><span class="w"> </span><span class="k">fn</span> <span class="nf">flush</span><span class="p">()</span><span class="w"> </span><span class="p">{}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">unsafe</span><span class="w"> </span><span class="k">fn</span> <span class="nf">release</span><span class="p">()</span><span class="w"> </span><span class="p">{}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">unsafe</span><span class="w"> </span><span class="k">fn</span> <span class="nf">write</span><span class="p">(</span><span class="n">_bytes</span>: <span class="kp">&amp;</span><span class="p">[</span><span class="kt">u8</span><span class="p">])</span><span class="w"> </span><span class="p">{}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="cp">#[entry]</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">fn</span> <span class="nf">main</span><span class="p">()</span><span class="w"> </span>-&gt; <span class="o">!</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">defmt</span>::<span class="fm">info!</span><span class="p">(</span><span class="s">&#34;程序启动！&#34;</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kd">let</span><span class="w"> </span><span class="p">(</span><span class="n">temp</span><span class="p">,</span><span class="w"> </span><span class="n">hum</span><span class="p">)</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">sensor</span><span class="p">.</span><span class="n">measure</span><span class="p">().</span><span class="n">unwrap</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">defmt</span>::<span class="fm">info!</span><span class="p">(</span><span class="s">&#34;温度 = {:.2}°C, 湿度 = {:.2}%&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">temp</span><span class="p">,</span><span class="w"> </span><span class="n">hum</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">loop</span><span class="w"> </span><span class="p">{}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><h3 id="52-使用-probe-rs-调试">5.2 使用 probe-rs 调试</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 安装 probe-rs</span>
</span></span><span class="line"><span class="cl">cargo install probe-rs --features cli
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 烧录程序</span>
</span></span><span class="line"><span class="cl">probe-rs run --chip STM32F407VGTx target/thumbv7em-none-eabihf/debug/my-app
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># GDB 调试</span>
</span></span><span class="line"><span class="cl">probe-rs gdb --chip STM32F407VGTx
</span></span></code></pre></div><h2 id="性能优化">性能优化</h2>
<h3 id="61-启用-lto-和代码优化">6.1 启用 LTO 和代码优化</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="cl"><span class="p">[</span><span class="nx">profile</span><span class="p">.</span><span class="nx">release</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="nx">lto</span> <span class="p">=</span> <span class="kc">true</span>          <span class="c"># 链接时优化</span>
</span></span><span class="line"><span class="cl"><span class="nx">codegen-units</span> <span class="p">=</span> <span class="mi">1</span>   <span class="c"># 单个编译单元</span>
</span></span><span class="line"><span class="cl"><span class="nx">opt-level</span> <span class="p">=</span> <span class="s2">&#34;s&#34;</span>     <span class="c"># 优化体积（或&#34;z&#34;极致优化）</span>
</span></span></code></pre></div><h3 id="62-使用-heapless-容器">6.2 使用 heapless 容器</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="k">use</span><span class="w"> </span><span class="n">heapless</span>::<span class="p">{</span><span class="nb">Vec</span><span class="p">,</span><span class="w"> </span><span class="nb">String</span><span class="p">};</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="c1">// 无需 alloc 的动态容器
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">let</span><span class="w"> </span><span class="k">mut</span><span class="w"> </span><span class="n">data</span>: <span class="nb">Vec</span><span class="o">&lt;</span><span class="kt">u8</span><span class="p">,</span><span class="w"> </span><span class="mi">32</span><span class="o">&gt;</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nb">Vec</span>::<span class="n">new</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="n">data</span><span class="p">.</span><span class="n">push</span><span class="p">(</span><span class="mi">42</span><span class="p">).</span><span class="n">unwrap</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kd">let</span><span class="w"> </span><span class="k">mut</span><span class="w"> </span><span class="n">text</span>: <span class="nb">String</span><span class="o">&lt;</span><span class="mi">64</span><span class="o">&gt;</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nb">String</span>::<span class="n">new</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="n">text</span><span class="p">.</span><span class="n">push_str</span><span class="p">(</span><span class="s">&#34;Hello&#34;</span><span class="p">).</span><span class="n">unwrap</span><span class="p">();</span><span class="w">
</span></span></span></code></pre></div><h3 id="63-零成本抽象实践">6.3 零成本抽象实践</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="c1">// 使用泛型而非 trait object
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">fn</span> <span class="nf">process</span><span class="o">&lt;</span><span class="n">S</span>: <span class="nc">Sensor</span><span class="o">&gt;</span><span class="p">(</span><span class="n">sensor</span>: <span class="kp">&amp;</span><span class="nc">S</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">  </span><span class="c1">// ✅ 单态化，无运行时开销
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="w">    </span><span class="kd">let</span><span class="w"> </span><span class="n">data</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">sensor</span><span class="p">.</span><span class="n">read</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="c1">// 避免使用 Box&lt;dyn Sensor&gt;  // ❌ 需要 heap 和虚表
</span></span></span></code></pre></div><h2 id="常见问题">常见问题</h2>
<h3 id="q1-编译错误undefined-reference">Q1: 编译错误&quot;undefined reference&quot;</h3>
<p><strong>原因</strong>：缺少 <code>memory.x</code> 或链接脚本配置错误。</p>
<p><strong>解决</strong>：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="c1">// build.rs
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">use</span><span class="w"> </span><span class="n">std</span>::<span class="n">env</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">use</span><span class="w"> </span><span class="n">std</span>::<span class="n">fs</span>::<span class="n">File</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">use</span><span class="w"> </span><span class="n">std</span>::<span class="n">io</span>::<span class="n">Write</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">use</span><span class="w"> </span><span class="n">std</span>::<span class="n">path</span>::<span class="n">PathBuf</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">fn</span> <span class="nf">main</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kd">let</span><span class="w"> </span><span class="n">out</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="o">&amp;</span><span class="n">PathBuf</span>::<span class="n">from</span><span class="p">(</span><span class="n">env</span>::<span class="n">var_os</span><span class="p">(</span><span class="s">&#34;OUT_DIR&#34;</span><span class="p">).</span><span class="n">unwrap</span><span class="p">());</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">File</span>::<span class="n">create</span><span class="p">(</span><span class="n">out</span><span class="p">.</span><span class="n">join</span><span class="p">(</span><span class="s">&#34;memory.x&#34;</span><span class="p">))</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="p">.</span><span class="n">unwrap</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="p">.</span><span class="n">write_all</span><span class="p">(</span><span class="fm">include_bytes!</span><span class="p">(</span><span class="s">&#34;memory.x&#34;</span><span class="p">))</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="p">.</span><span class="n">unwrap</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="fm">println!</span><span class="p">(</span><span class="s">&#34;cargo:rustc-link-search=</span><span class="si">{}</span><span class="s">&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">out</span><span class="p">.</span><span class="n">display</span><span class="p">());</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="fm">println!</span><span class="p">(</span><span class="s">&#34;cargo:rerun-if-changed=memory.x&#34;</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><h3 id="q2-栈溢出">Q2: 栈溢出</h3>
<p><strong>原因</strong>：默认栈大小不足。</p>
<p><strong>解决</strong>：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="c1">// Cargo.toml
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">[</span><span class="n">profile</span><span class="p">.</span><span class="n">release</span><span class="p">]</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="n">panic</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">&#34;abort&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="c1">// memory.x 中增大栈
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="no">_STACK_SIZE</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">8</span><span class="n">K</span><span class="p">;</span><span class="w">  </span><span class="c1">// 默认 2K
</span></span></span></code></pre></div><h3 id="q3-外设无法访问">Q3: 外设无法访问</h3>
<p><strong>原因</strong>：时钟未使能或权限问题。</p>
<p><strong>解决</strong>：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="c1">// 确保调用 take() 只获取一次外设
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">let</span><span class="w"> </span><span class="n">dp</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">pac</span>::<span class="n">Peripherals</span>::<span class="n">take</span><span class="p">().</span><span class="n">unwrap</span><span class="p">();</span><span class="w">  </span><span class="c1">// ✅
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">let</span><span class="w"> </span><span class="n">dp</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">pac</span>::<span class="n">Peripherals</span>::<span class="n">take</span><span class="p">().</span><span class="n">unwrap</span><span class="p">();</span><span class="w">  </span><span class="c1">// ❌ 第二次返回 None
</span></span></span></code></pre></div><h2 id="总结">总结</h2>
<p>嵌入式 Rust 的核心优势：</p>
<ol>
<li><strong>编译期内存安全</strong>：无 GC，无运行时开销</li>
<li><strong>所有权系统</strong>：防止数据竞争和悬垂指针</li>
<li><strong>类型系统</strong>：强制处理错误情况</li>
<li><strong>生态系统</strong>：embedded-hal 提供统一接口</li>
</ol>
<p>2026 年，Rust 已成为嵌入式开发的重要选择。掌握它，让你的代码更安全、更高效！</p>
<hr>
<p><em>本文基于 Rust 官方文档和 2026 年嵌入式 Rust 生态现状编写。</em></p>
<h2 id="参考资料">参考资料</h2>
<ul>
<li>The Embedded Rust Book: <a href="https://docs.rust-embedded.org/book/">https://docs.rust-embedded.org/book/</a></li>
<li>cortex-m-quickstart 模板：https://github.com/rust-embedded/cortex-m-quickstart</li>
<li>embedded-hal 文档：https://docs.rs/embedded-hal</li>
<li>probe-rs 调试工具：https://probe.rs</li>
</ul>
]]></content:encoded></item><item><title>嵌入式系统安全开发指南 2026</title><link>https://tech-snippets.xyz/posts/embedded-security-best-practices/</link><pubDate>Thu, 02 Apr 2026 03:00:00 +0800</pubDate><guid>https://tech-snippets.xyz/posts/embedded-security-best-practices/</guid><description>嵌入式安全开发完全指南，涵盖安全启动、加密、安全存储和防攻击技术</description><content:encoded><![CDATA[<h2 id="引言">引言</h2>
<p>本文基于 2026 年最新行业资料整理，涵盖 embedded security best practices 的核心概念、开发流程和实战技巧。</p>
<h2 id="嵌入式安全概述">嵌入式安全概述</h2>
<p>随着 IoT 设备普及，嵌入式系统安全变得至关重要。本文介绍嵌入式安全开发的核心原则和实践。</p>
<h2 id="安全启动">安全启动</h2>
<p>安全启动（Secure Boot）确保设备只运行可信固件：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// 验证固件签名
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">bool</span> <span class="nf">verify_firmware_signature</span><span class="p">(</span><span class="k">const</span> <span class="kt">uint8_t</span> <span class="o">*</span><span class="n">firmware</span><span class="p">,</span> 
</span></span><span class="line"><span class="cl">                                <span class="k">const</span> <span class="kt">uint8_t</span> <span class="o">*</span><span class="n">signature</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                <span class="k">const</span> <span class="kt">uint8_t</span> <span class="o">*</span><span class="n">public_key</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 使用 ECC 或 RSA 验证
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">return</span> <span class="nf">crypto_verify</span><span class="p">(</span><span class="n">firmware</span><span class="p">,</span> <span class="n">signature</span><span class="p">,</span> <span class="n">public_key</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h2 id="加密通信">加密通信</h2>
<p>使用 TLS/DTLS 保护设备与云端通信：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// mTLS 配置
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">mbedtls_ssl_config</span> <span class="n">conf</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nf">mbedtls_ssl_config_init</span><span class="p">(</span><span class="o">&amp;</span><span class="n">conf</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nf">mbedtls_ssl_config_defaults</span><span class="p">(</span><span class="o">&amp;</span><span class="n">conf</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">MBEDTLS_SSL_IS_CLIENT</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">MBEDTLS_SSL_TRANSPORT_DATAGRAM</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">MBEDTLS_SSL_PRESET_DEFAULT</span><span class="p">);</span>
</span></span></code></pre></div><h2 id="安全存储">安全存储</h2>
<p>敏感数据（密钥、证书）应加密存储：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// 使用 AES-256 加密
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">mbedtls_aes_context</span> <span class="n">aes</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nf">mbedtls_aes_init</span><span class="p">(</span><span class="o">&amp;</span><span class="n">aes</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nf">mbedtls_aes_setkey_enc</span><span class="p">(</span><span class="o">&amp;</span><span class="n">aes</span><span class="p">,</span> <span class="n">key</span><span class="p">,</span> <span class="mi">256</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nf">mbedtls_aes_crypt_cbc</span><span class="p">(</span><span class="o">&amp;</span><span class="n">aes</span><span class="p">,</span> <span class="n">MBEDTLS_AES_ENCRYPT</span><span class="p">,</span> 
</span></span><span class="line"><span class="cl">                       <span class="n">length</span><span class="p">,</span> <span class="n">iv</span><span class="p">,</span> <span class="n">input</span><span class="p">,</span> <span class="n">output</span><span class="p">);</span>
</span></span></code></pre></div><h2 id="防攻击技术">防攻击技术</h2>
<ul>
<li><strong>防侧信道攻击</strong>：恒定时间算法</li>
<li><strong>防物理攻击</strong>：加密 JTAG/SWD 接口</li>
<li><strong>防重放攻击</strong>：时间戳 + 随机数</li>
</ul>
<h2 id="安全开发生命周期">安全开发生命周期</h2>
<ol>
<li>需求分析 → 识别安全需求</li>
<li>设计 → 威胁建模</li>
<li>实现 → 安全编码规范</li>
<li>测试 → 渗透测试</li>
<li>部署 → 安全配置</li>
<li>维护 → 安全更新</li>
</ol>
<h2 id="参考资料">参考资料</h2>
<ol>
<li><a href="https://witekio.com/blog/best-practices-for-embedded-security/">Best Practices for Embedded Security: Top Tips Explained - Witekio</a></li>
<li><a href="https://www.code-intelligence.com/embedded-security-testing">Embedded Security Testing: Best Practices &amp; Challenges 2024</a></li>
<li><a href="https://www.digi.com/blog/post/key-strategies-for-embedded-systems-security">Key Strategies for Embedded Systems Security - Digi International</a></li>
</ol>
<hr>
<p><em>本文基于网络公开资料整理，结合嵌入式开发实践经验编写。</em></p>
]]></content:encoded></item><item><title>ARM Cortex-M 内存屏障与原子操作详解</title><link>https://tech-snippets.xyz/posts/arm-cortex-m-memory-barriers-atomic-operations/</link><pubDate>Tue, 31 Mar 2026 03:00:00 +0800</pubDate><guid>https://tech-snippets.xyz/posts/arm-cortex-m-memory-barriers-atomic-operations/</guid><description>深入理解 Cortex-M 内存模型、原子操作实现原理和内存屏障指令的实际应用</description><content:encoded><![CDATA[<h2 id="引言">引言</h2>
<p>在多任务和中断驱动的嵌入式系统中，数据竞争和内存访问顺序问题是导致系统不稳定的常见原因。本文深入探讨 ARM Cortex-M 处理器的内存模型、原子操作实现机制，以及内存屏障指令（DMB/DSB/ISB）的实际应用场景。</p>
<hr>
<h2 id="1-arm-cortex-m-内存模型">1. ARM Cortex-M 内存模型</h2>
<h3 id="11-弱内存序weak-memory-ordering">1.1 弱内存序（Weak Memory Ordering）</h3>
<p>ARM Cortex-M 采用<strong>弱内存序</strong>模型，这意味着：</p>
<ul>
<li>CPU 可以重新排序内存访问指令以提高性能</li>
<li>不保证程序中的内存访问顺序与实际执行顺序一致</li>
<li>多核或多主设备系统中可能出现数据不一致</li>
</ul>
<p><strong>示例问题</strong>：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// 线程 1
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">flag</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>          <span class="c1">// 步骤 1
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">data</span> <span class="o">=</span> <span class="mh">0x1234</span><span class="p">;</span>     <span class="c1">// 步骤 2
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="c1">// 线程 2
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">while</span> <span class="p">(</span><span class="n">flag</span> <span class="o">==</span> <span class="mi">0</span><span class="p">);</span> <span class="c1">// 等待 flag
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">read</span> <span class="o">=</span> <span class="n">data</span><span class="p">;</span>       <span class="c1">// 期望读到 0x1234
</span></span></span></code></pre></div><p><strong>问题</strong>：由于内存重排序，线程 2 可能在 <code>data</code> 写入之前就读到 <code>flag==1</code>，导致读到旧数据。</p>
<h3 id="12-cortex-m-内存类型">1.2 Cortex-M 内存类型</h3>
<table>
<thead>
<tr>
<th>内存类型</th>
<th>特性</th>
<th>使用场景</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Normal Memory</strong></td>
<td>可缓存、可缓冲</td>
<td>SRAM、外部 RAM</td>
</tr>
<tr>
<td><strong>Device Memory</strong></td>
<td>不可缓存、顺序访问</td>
<td>外设寄存器</td>
</tr>
<tr>
<td><strong>Strongly Ordered</strong></td>
<td>严格顺序、无缓冲</td>
<td>共享内存区域</td>
</tr>
</tbody>
</table>
<hr>
<h2 id="2-原子操作实现原理">2. 原子操作实现原理</h2>
<h3 id="21-什么是原子操作">2.1 什么是原子操作？</h3>
<p>原子操作是<strong>不可中断</strong>的操作，在执行过程中不会被其他任务或中断打断。</p>
<h3 id="22-cortex-m-原子指令">2.2 Cortex-M 原子指令</h3>
<h4 id="ldrexstrex独占访问">LDREX/STREX（独占访问）</h4>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// 原子加法示例
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">uint32_t</span> <span class="nf">atomic_add</span><span class="p">(</span><span class="k">volatile</span> <span class="kt">uint32_t</span> <span class="o">*</span><span class="n">ptr</span><span class="p">,</span> <span class="kt">uint32_t</span> <span class="n">val</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">uint32_t</span> <span class="n">old_val</span><span class="p">,</span> <span class="n">new_val</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">uint32_t</span> <span class="n">status</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="k">do</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">old_val</span> <span class="o">=</span> <span class="nf">__LDREX</span><span class="p">(</span><span class="n">ptr</span><span class="p">);</span>           <span class="c1">// 独占加载
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="n">new_val</span> <span class="o">=</span> <span class="n">old_val</span> <span class="o">+</span> <span class="n">val</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">status</span> <span class="o">=</span> <span class="nf">__STREX</span><span class="p">(</span><span class="n">new_val</span><span class="p">,</span> <span class="n">ptr</span><span class="p">);</span>   <span class="c1">// 独占存储
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="c1">// 如果 STREX 返回非 0，说明被其他主设备打断，重试
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="p">}</span> <span class="k">while</span> <span class="p">(</span><span class="n">status</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">old_val</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p><strong>工作原理</strong>：</p>
<ol>
<li><code>LDREX</code> 读取内存并标记为&quot;独占访问&quot;</li>
<li><code>STREX</code> 写入内存时检查是否有其他主设备修改过该地址</li>
<li>如果有冲突，<code>STREX</code> 返回非 0，需要重试</li>
</ol>
<h4 id="swp交换指令cortex-m3m4">SWP（交换指令，Cortex-M3/M4）</h4>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// 使用 SWP 实现互斥锁
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">uint32_t</span> <span class="nf">mutex_lock</span><span class="p">(</span><span class="k">volatile</span> <span class="kt">uint32_t</span> <span class="o">*</span><span class="n">lock</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">uint32_t</span> <span class="n">old_val</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">do</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">old_val</span> <span class="o">=</span> <span class="nf">__SWP</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="n">lock</span><span class="p">);</span>  <span class="c1">// 交换 1 和 lock 的值
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="c1">// 如果 old_val 为 0，说明锁原来是空闲的
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="p">}</span> <span class="k">while</span> <span class="p">(</span><span class="n">old_val</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="23-原子操作的应用场景">2.3 原子操作的应用场景</h3>
<table>
<thead>
<tr>
<th>场景</th>
<th>推荐指令</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>计数器递增</td>
<td>LDREX/STREX</td>
<td>多任务共享计数器</td>
</tr>
<tr>
<td>互斥锁实现</td>
<td>SWP/LDREX</td>
<td>RTOS 互斥量底层实现</td>
</tr>
<tr>
<td>标志位设置</td>
<td>位带操作</td>
<td>单比特原子操作</td>
</tr>
<tr>
<td>链表操作</td>
<td>LDREX/STREX</td>
<td>无锁数据结构</td>
</tr>
</tbody>
</table>
<hr>
<h2 id="3-内存屏障指令详解">3. 内存屏障指令详解</h2>
<h3 id="31-三种屏障指令">3.1 三种屏障指令</h3>
<h4 id="dmbdata-memory-barrier">DMB（Data Memory Barrier）</h4>
<p><strong>作用</strong>：确保 DMB 之前的所有内存访问完成后再执行之后的访问</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// 使用场景：确保数据写入后再通知其他任务
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">data</span> <span class="o">=</span> <span class="mh">0x1234</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nf">__DMB</span><span class="p">();</span>              <span class="c1">// 确保 data 写入完成
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">flag</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>             <span class="c1">// 通知其他任务数据已就绪
</span></span></span></code></pre></div><h4 id="dsbdata-synchronization-barrier">DSB（Data Synchronization Barrier）</h4>
<p><strong>作用</strong>：确保 DSB 之前的所有指令完成后再执行之后的指令</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// 使用场景：修改中断向量表后
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">SCB</span><span class="o">-&gt;</span><span class="n">VTOR</span> <span class="o">=</span> <span class="n">new_vector_table</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nf">__DSB</span><span class="p">();</span>              <span class="c1">// 确保向量表更新完成
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nf">__ISB</span><span class="p">();</span>              <span class="c1">// 刷新流水线
</span></span></span></code></pre></div><h4 id="isbinstruction-synchronization-barrier">ISB（Instruction Synchronization Barrier）</h4>
<p><strong>作用</strong>：刷新流水线，确保后续指令从新的上下文获取</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// 使用场景：切换任务栈指针后
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nf">__set_PSP</span><span class="p">(</span><span class="n">new_psp</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nf">__ISB</span><span class="p">();</span>              <span class="c1">// 刷新流水线，使用新栈指针
</span></span></span></code></pre></div><h3 id="32-屏障指令对比">3.2 屏障指令对比</h3>
<table>
<thead>
<tr>
<th>指令</th>
<th>影响范围</th>
<th>性能开销</th>
<th>典型应用</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>DMB</strong></td>
<td>内存访问</td>
<td>中等</td>
<td>多核同步、共享数据</td>
</tr>
<tr>
<td><strong>DSB</strong></td>
<td>所有指令</td>
<td>较高</td>
<td>异常处理、系统配置</td>
</tr>
<tr>
<td><strong>ISB</strong></td>
<td>指令流水线</td>
<td>低</td>
<td>上下文切换、模式切换</td>
</tr>
</tbody>
</table>
<hr>
<h2 id="4-实际应用场景">4. 实际应用场景</h2>
<p><img alt="内存屏障应用示例" loading="lazy" src="/images/memory-barrier-example.svg"></p>
<h3 id="41-多任务共享数据">4.1 多任务共享数据</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// 全局共享数据
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">volatile</span> <span class="kt">uint32_t</span> <span class="n">shared_data</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">volatile</span> <span class="kt">uint32_t</span> <span class="n">data_ready</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 任务 1：生产者
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="nf">producer_task</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">shared_data</span> <span class="o">=</span> <span class="nf">compute_data</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="nf">__DMB</span><span class="p">();</span>                    <span class="c1">// 确保数据写入完成
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">data_ready</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>             <span class="c1">// 设置就绪标志
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 任务 2：消费者
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="nf">consumer_task</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">while</span> <span class="p">(</span><span class="n">data_ready</span> <span class="o">==</span> <span class="mi">0</span><span class="p">);</span>    <span class="c1">// 等待数据就绪
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">__DMB</span><span class="p">();</span>                    <span class="c1">// 确保读到最新数据
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">process</span><span class="p">(</span><span class="n">shared_data</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="42-中断与主循环通信">4.2 中断与主循环通信</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// 中断服务程序
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">volatile</span> <span class="kt">uint32_t</span> <span class="n">adc_value</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">volatile</span> <span class="kt">uint8_t</span> <span class="n">adc_ready</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">ADC_IRQHandler</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">adc_value</span> <span class="o">=</span> <span class="n">ADC</span><span class="o">-&gt;</span><span class="n">DR</span><span class="p">;</span>        <span class="c1">// 读取 ADC 数据
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">__DMB</span><span class="p">();</span>                    <span class="c1">// 确保数据写入
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">adc_ready</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>              <span class="c1">// 设置标志
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 主循环
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">while</span> <span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">adc_ready</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nf">__DMB</span><span class="p">();</span>            <span class="c1">// 确保读到最新 ADC 值
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="nf">process_adc</span><span class="p">(</span><span class="n">adc_value</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="n">adc_ready</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="43-无锁环形缓冲区">4.3 无锁环形缓冲区</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">uint32_t</span> <span class="n">buffer</span><span class="p">[</span><span class="mi">16</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">    <span class="kt">uint32_t</span> <span class="n">head</span><span class="p">;</span>              <span class="c1">// 使用原子操作
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="kt">uint32_t</span> <span class="n">tail</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="kt">ring_buffer_t</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 入队操作（中断上下文）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="nf">ring_push</span><span class="p">(</span><span class="kt">ring_buffer_t</span> <span class="o">*</span><span class="n">rb</span><span class="p">,</span> <span class="kt">uint32_t</span> <span class="n">data</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">uint32_t</span> <span class="n">head</span> <span class="o">=</span> <span class="n">rb</span><span class="o">-&gt;</span><span class="n">head</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">uint32_t</span> <span class="n">next_head</span> <span class="o">=</span> <span class="p">(</span><span class="n">head</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="o">%</span> <span class="mi">16</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">next_head</span> <span class="o">!=</span> <span class="n">rb</span><span class="o">-&gt;</span><span class="n">tail</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">rb</span><span class="o">-&gt;</span><span class="n">buffer</span><span class="p">[</span><span class="n">head</span><span class="p">]</span> <span class="o">=</span> <span class="n">data</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="nf">__DMB</span><span class="p">();</span>                <span class="c1">// 确保数据写入
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="n">rb</span><span class="o">-&gt;</span><span class="n">head</span> <span class="o">=</span> <span class="n">next_head</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 出队操作（主循环）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">int</span> <span class="nf">ring_pop</span><span class="p">(</span><span class="kt">ring_buffer_t</span> <span class="o">*</span><span class="n">rb</span><span class="p">,</span> <span class="kt">uint32_t</span> <span class="o">*</span><span class="n">data</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">rb</span><span class="o">-&gt;</span><span class="n">head</span> <span class="o">==</span> <span class="n">rb</span><span class="o">-&gt;</span><span class="n">tail</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span>              <span class="c1">// 空
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="o">*</span><span class="n">data</span> <span class="o">=</span> <span class="n">rb</span><span class="o">-&gt;</span><span class="n">buffer</span><span class="p">[</span><span class="n">rb</span><span class="o">-&gt;</span><span class="n">tail</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">    <span class="nf">__DMB</span><span class="p">();</span>                    <span class="c1">// 确保数据读取
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">rb</span><span class="o">-&gt;</span><span class="n">tail</span> <span class="o">=</span> <span class="p">(</span><span class="n">rb</span><span class="o">-&gt;</span><span class="n">tail</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="o">%</span> <span class="mi">16</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><hr>
<h2 id="5-常见问题与调试技巧">5. 常见问题与调试技巧</h2>
<h3 id="51-数据竞争问题排查">5.1 数据竞争问题排查</h3>
<p><strong>症状</strong>：</p>
<ul>
<li>间歇性数据错误</li>
<li>多任务访问同一变量时出现异常值</li>
</ul>
<p><strong>调试方法</strong>：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// 添加内存屏障定位问题
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">shared_var</span> <span class="o">=</span> <span class="n">new_value</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nf">__DMB</span><span class="p">();</span>                        <span class="c1">// 添加屏障
</span></span></span><span class="line"><span class="cl"><span class="c1">// 如果问题消失，说明是内存重排序导致
</span></span></span></code></pre></div><h3 id="52-性能优化建议">5.2 性能优化建议</h3>
<table>
<thead>
<tr>
<th>场景</th>
<th>优化策略</th>
</tr>
</thead>
<tbody>
<tr>
<td>频繁访问共享数据</td>
<td>使用局部变量缓存</td>
</tr>
<tr>
<td>中断频繁触发</td>
<td>减少屏障使用，改用临界区</td>
</tr>
<tr>
<td>多核系统</td>
<td>使用 DMB 而非全局锁</td>
</tr>
</tbody>
</table>
<hr>
<h2 id="6-总结">6. 总结</h2>
<h3 id="关键要点">关键要点</h3>
<ol>
<li><strong>Cortex-M 采用弱内存序</strong>，需要显式使用内存屏障保证顺序</li>
<li><strong>LDREX/STREX 实现原子操作</strong>，适合多任务共享数据</li>
<li><strong>DMB/DSB/ISB 各有用途</strong>，根据场景选择</li>
<li><strong>中断与主循环通信</strong>必须使用内存屏障</li>
<li><strong>过度使用屏障影响性能</strong>，需权衡</li>
</ol>
<h3 id="最佳实践">最佳实践</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// ✅ 正确使用
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">data</span> <span class="o">=</span> <span class="nf">compute</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="nf">__DMB</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="n">flag</span> <span class="o">=</span> <span class="n">READY</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// ❌ 错误使用（缺少屏障）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">data</span> <span class="o">=</span> <span class="nf">compute</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="n">flag</span> <span class="o">=</span> <span class="n">READY</span><span class="p">;</span>           <span class="c1">// 可能被重排序
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="c1">// ❌ 过度使用（影响性能）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nf">__DMB</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="n">data</span> <span class="o">=</span> <span class="nf">compute</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="nf">__DMB</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="n">flag</span> <span class="o">=</span> <span class="n">READY</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nf">__DMB</span><span class="p">();</span>
</span></span></code></pre></div><hr>
<p><em>本文基于 ARM 官方技术文档和实际项目经验整理，适用于 Cortex-M3/M4/M7 系列处理器。</em></p>
<h2 id="参考资料">参考资料</h2>
<ul>
<li>ARM Cortex-M3/M4 Technical Reference Manual</li>
<li>ARMv7-M Architecture Reference Manual</li>
<li>ARM Infocenter: Memory Barriers and Cache Maintenance</li>
<li>Joseph Yiu: 《The Definitive Guide to ARM Cortex-M3 and Cortex-M4 Processors》</li>
</ul>
]]></content:encoded></item><item><title>FreeRTOS 任务调度器源码分析与实时性优化</title><link>https://tech-snippets.xyz/posts/freertos-scheduler-analysis-optimization/</link><pubDate>Tue, 31 Mar 2026 03:00:00 +0800</pubDate><guid>https://tech-snippets.xyz/posts/freertos-scheduler-analysis-optimization/</guid><description>从源码层面理解任务切换、优先级继承、临界区保护机制</description><content:encoded><![CDATA[<h2 id="引言">引言</h2>
<p>FreeRTOS 是最流行的嵌入式实时操作系统，广泛应用于 IoT、工业控制和消费电子。很多开发者会用 FreeRTOS，但对其内部机制一知半解，遇到优先级反转、栈溢出等问题时束手无策。</p>
<p>本文深入 FreeRTOS 内核源码，剖析任务调度器的工作原理，帮助你写出更高效、更可靠的实时系统代码。</p>
<h2 id="任务调度器架构">任务调度器架构</h2>
<h3 id="11-核心组件">1.1 核心组件</h3>
<figure>
<svg viewBox="0 0 600 280" xmlns="http://www.w3.org/2000/svg">
  <defs>
    <style>
      .box { fill: #1a1a2e; stroke: #4a9eff; stroke-width: 2; }
      .text { fill: #e0e0e0; font-family: Arial, sans-serif; font-size: 16px; text-anchor: middle; }
      .label { fill: #4a9eff; font-family: Arial, sans-serif; font-size: 14px; font-weight: bold; }
    </style>
  </defs>
  <!-- 应用层 -->
  <rect class="box" x="50" y="20" width="500" height="50" rx="5"/>
  <text class="text" x="300" y="52">应用层任务</text>
  <!-- 中间层 -->
  <rect class="box" x="50" y="80" width="500" height="50" rx="5"/>
  <text class="text" x="125" y="112">任务管理</text>
  <line x1="175" y1="90" x2="175" y2="120" stroke="#4a9eff" stroke-width="2"/>
  <text class="text" x="225" y="112">信号量</text>
  <line x1="275" y1="90" x2="275" y2="120" stroke="#4a9eff" stroke-width="2"/>
  <text class="text" x="325" y="112">队列</text>
  <line x1="375" y1="90" x2="375" y2="120" stroke="#4a9eff" stroke-width="2"/>
  <text class="text" x="425" y="112">互斥量</text>
  <line x1="475" y1="90" x2="475" y2="120" stroke="#4a9eff" stroke-width="2"/>
  <!-- 调度器 -->
  <rect class="box" x="50" y="140" width="500" height="50" rx="5"/>
  <text class="text" x="300" y="172">任务调度器 (Scheduler)</text>
  <!-- 端口层 -->
  <rect class="box" x="50" y="200" width="500" height="50" rx="5"/>
  <text class="text" x="300" y="222">端口层 (Port)</text>
  <text class="text" x="300" y="242" font-size="12" fill="#888">上下文切换、栈帧管理、中断处理</text>
  <!-- 连接箭头 -->
  <line x1="550" y1="45" x2="570" y2="45" stroke="#4a9eff" stroke-width="2"/>
  <line x1="550" y1="105" x2="570" y2="105" stroke="#4a9eff" stroke-width="2"/>
  <line x1="550" y1="165" x2="570" y2="165" stroke="#4a9eff" stroke-width="2"/>
  <line x1="550" y1="225" x2="570" y2="225" stroke="#4a9eff" stroke-width="2"/>
</svg>
<figcaption style="text-align:center;color:#888;font-size:12px;margin-top:8px;">FreeRTOS 架构层次图</figcaption>
</figure>
<h3 id="12-就绪列表">1.2 就绪列表</h3>
<p>FreeRTOS 使用<strong>优先级位图</strong>管理就绪任务：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// tasks.c
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">static</span> <span class="n">UBaseType_t</span> <span class="n">uxReadyReadyLists</span><span class="p">[</span> <span class="n">configMAX_PRIORITIES</span> <span class="p">];</span>
</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="n">UBaseType_t</span> <span class="n">uxTopReadyPriority</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 每个优先级有一个就绪列表
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">pxReadyTasksLists</span><span class="p">[</span> <span class="n">priority</span> <span class="p">]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 位图快速查找最高优先级
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">uxTopReadyPriority</span> <span class="o">=</span> <span class="nf">__clz</span><span class="p">(</span> <span class="n">uxReadyReadyLists</span> <span class="p">);</span>  <span class="c1">// ARM CLZ 指令
</span></span></span></code></pre></div><h2 id="任务切换机制">任务切换机制</h2>
<h3 id="21-上下文切换流程">2.1 上下文切换流程</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// port.c - Cortex-M4/M7 实现
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="nf">xPortPendSVHandler</span><span class="p">(</span> <span class="kt">void</span> <span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kr">__asm</span> <span class="k">volatile</span>
</span></span><span class="line"><span class="cl">    <span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="s">&#34;ldr r0, =pxCurrentTCB      </span>
</span></span><span class="line"><span class="cl"><span class="s">&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="s">&#34;ldr r1, [r0]               </span>
</span></span><span class="line"><span class="cl"><span class="s">&#34;  // 获取当前 TCB</span>
</span></span><span class="line"><span class="cl">        <span class="s">&#34;mrs r2, psp                </span>
</span></span><span class="line"><span class="cl"><span class="s">&#34;  // 保存 PSP</span>
</span></span><span class="line"><span class="cl">        <span class="s">&#34;stmdb r2!, {r4-r11}        </span>
</span></span><span class="line"><span class="cl"><span class="s">&#34;  // 保存寄存器</span>
</span></span><span class="line"><span class="cl">        <span class="s">&#34;str r2, [r1]               </span>
</span></span><span class="line"><span class="cl"><span class="s">&#34;  // 保存到 TCB</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="s">&#34;bl vTaskSwitchContext    </span>
</span></span><span class="line"><span class="cl"><span class="s">&#34;  // 选择下一个任务</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="s">&#34;ldr r0, =pxCurrentTCB      </span>
</span></span><span class="line"><span class="cl"><span class="s">&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="s">&#34;ldr r1, [r0]               </span>
</span></span><span class="line"><span class="cl"><span class="s">&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="s">&#34;ldr r2, [r1]               </span>
</span></span><span class="line"><span class="cl"><span class="s">&#34;  // 恢复新任务的 PSP</span>
</span></span><span class="line"><span class="cl">        <span class="s">&#34;ldmia r2!, {r4-r11}        </span>
</span></span><span class="line"><span class="cl"><span class="s">&#34;  // 恢复寄存器</span>
</span></span><span class="line"><span class="cl">        <span class="s">&#34;msr psp, r2                </span>
</span></span><span class="line"><span class="cl"><span class="s">&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="s">&#34;bx lr                      </span>
</span></span><span class="line"><span class="cl"><span class="s">&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="22-触发切换的场景">2.2 触发切换的场景</h3>
<ol>
<li><strong>系统滴答中断</strong>（SysTick）- 时间片轮转</li>
<li><strong>阻塞 API 调用</strong> - <code>vTaskDelay()</code>, <code>xQueueReceive()</code></li>
<li><strong>同步对象就绪</strong> - 信号量、互斥量释放</li>
<li><strong>显式让出</strong> - <code>taskYIELD()</code></li>
</ol>
<h3 id="23-临界区保护">2.3 临界区保护</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// 进入临界区 - 关闭中断
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#define taskENTER_CRITICAL()     __asm volatile( &#34;cpsid i&#34; :::&#34;memory&#34;)
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="c1">// 退出临界区 - 恢复中断
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#define taskEXIT_CRITICAL()     __asm volatile( &#34;cpsie i&#34; :::&#34;memory&#34;)
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="c1">// 嵌套临界区计数
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">static</span> <span class="n">UBaseType_t</span> <span class="n">uxCriticalNesting</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span></code></pre></div><h2 id="优先级反转与继承">优先级反转与继承</h2>
<h3 id="31-优先级反转问题">3.1 优先级反转问题</h3>
<figure>
<svg viewBox="0 0 800 200" xmlns="http://www.w3.org/2000/svg">
  <defs>
    <style>
      .timeline { stroke: #555; stroke-width: 2; }
      .box { fill: #1a1a2e; stroke-width: 2; rx: 6; }
      .box-low { stroke: #2ecc71; }
      .box-mid { stroke: #f39c12; }
      .box-high { stroke: #e74c3c; }
      .box-blocked { stroke: #9b59b6; fill: #2a1a2e; }
      .text { fill: #e0e0e0; font-family: 'Arial', sans-serif; font-size: 13px; }
      .label { fill: #4a9eff; font-family: 'Arial', sans-serif; font-size: 14px; font-weight: bold; }
      .axis-label { fill: #888; font-family: 'Arial', sans-serif; font-size: 12px; }
    </style>
  </defs>
  <!-- 时间轴 -->
  <line class="timeline" x1="60" y1="160" x2="780" y2="160"/>
  <text class="label" x="60" y="185">时间</text>
  <text class="label" x="100" y="185" fill="#666">→</text>
  <!-- T1 事件 -->
  <rect class="box box-low" x="100" y="80" width="110" height="55"/>
  <text class="text" x="155" y="105" text-anchor="middle">T1: L 获取互斥量</text>
  <line x1="155" y1="135" x2="155" y2="160" stroke="#2ecc71" stroke-width="2"/>
  <text class="axis-label" x="155" y="195" text-anchor="middle">T1</text>
  <!-- T2 事件 -->
  <rect class="box box-high" x="240" y="20" width="130" height="55"/>
  <text class="text" x="305" y="45" text-anchor="middle">T2: H 就绪，抢占 L</text>
  <line x1="305" y1="75" x2="305" y2="160" stroke="#e74c3c" stroke-width="2"/>
  <text class="axis-label" x="305" y="195" text-anchor="middle">T2</text>
  <!-- T3 事件 -->
  <rect class="box box-high" x="400" y="20" width="130" height="55"/>
  <text class="text" x="465" y="45" text-anchor="middle">T3: H 请求互斥量</text>
  <text class="text" x="465" y="65" text-anchor="middle" fill="#e74c3c" font-weight="bold">阻塞!</text>
  <line x1="465" y1="75" x2="465" y2="160" stroke="#e74c3c" stroke-width="2"/>
  <text class="axis-label" x="465" y="195" text-anchor="middle">T3</text>
  <!-- T4 事件 -->
  <rect class="box box-mid" x="560" y="50" width="130" height="55"/>
  <text class="text" x="625" y="75" text-anchor="middle">T4: M 就绪，抢占 L</text>
  <line x1="625" y1="105" x2="625" y2="160" stroke="#f39c12" stroke-width="2"/>
  <text class="axis-label" x="625" y="195" text-anchor="middle">T4</text>
  <!-- T5 事件 -->
  <rect class="box box-blocked" x="700" y="20" width="100" height="55"/>
  <text class="text" x="750" y="45" text-anchor="middle" fill="#e74c3c">T5: H 被阻塞</text>
  <text class="text" x="750" y="65" text-anchor="middle" fill="#9b59b6" font-weight="bold">优先级反转!</text>
  <line x1="750" y1="75" x2="750" y2="160" stroke="#e74c3c" stroke-width="2" stroke-dasharray="5,3"/>
  <text class="axis-label" x="750" y="195" text-anchor="middle">T5</text>
  <!-- 任务图例 -->
<p><text class="axis-label" x="60" y="220">L (低)</text>
<text class="axis-label" x="110" y="220">M (中)</text>
<text class="axis-label" x="160" y="220">H (高)</text>
</svg></p>
<figcaption style="text-align:center;color:#888;font-size:12px;margin-top:8px;">优先级反转问题时序图</figcaption>
</figure>
<h3 id="32-优先级继承机制">3.2 优先级继承机制</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// mutex.c
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">SemaphoreHandle_t</span> <span class="nf">xSemaphoreCreateMutex</span><span class="p">(</span> <span class="kt">void</span> <span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">StaticSemaphore_t</span> <span class="o">*</span><span class="n">pxMutex</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">pxMutex</span> <span class="o">=</span> <span class="nf">pvPortMalloc</span><span class="p">(</span> <span class="k">sizeof</span><span class="p">(</span> <span class="n">StaticSemaphore_t</span> <span class="p">)</span> <span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 标记为互斥量（启用优先级继承）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">pxMutex</span><span class="o">-&gt;</span><span class="n">ucQueueType</span> <span class="o">=</span> <span class="n">queueQUEUE_TYPE_MUTEX</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">pxMutex</span><span class="o">-&gt;</span><span class="n">uxBasePriority</span> <span class="o">=</span> <span class="n">configMAX_PRIORITIES</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="p">(</span> <span class="n">SemaphoreHandle_t</span> <span class="p">)</span> <span class="n">pxMutex</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 当高优先级任务阻塞时，提升持有者优先级
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="nf">prvGiveMutexFromISR</span><span class="p">(</span> <span class="n">StaticSemaphore_t</span> <span class="o">*</span><span class="n">pxMutex</span> <span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span><span class="p">(</span> <span class="n">pxMutex</span><span class="o">-&gt;</span><span class="n">ucQueueType</span> <span class="o">==</span> <span class="n">queueQUEUE_TYPE_MUTEX</span> <span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// 恢复原始优先级
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="nf">vTaskPrioritySet</span><span class="p">(</span> <span class="n">pxMutex</span><span class="o">-&gt;</span><span class="n">pxHolder</span><span class="p">,</span> 
</span></span><span class="line"><span class="cl">                          <span class="n">pxMutex</span><span class="o">-&gt;</span><span class="n">uxBasePriority</span> <span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="33-使用建议">3.3 使用建议</h3>
<ul>
<li><strong>互斥量</strong>用于资源保护（自动优先级继承）</li>
<li><strong>二值信号量</strong>用于同步（无优先级继承，更快）</li>
<li>避免在中断服务程序中使用互斥量</li>
</ul>
<h2 id="内存管理">内存管理</h2>
<h3 id="41-堆方案选择">4.1 堆方案选择</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// FreeRTOSConfig.h
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#define configSUPPORT_STATIC_ALLOCATION  1
</span></span></span><span class="line"><span class="cl"><span class="cp">#define configSUPPORT_DYNAMIC_ALLOCATION 1
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="c1">// heap_4.c - 带内存合并的方案（推荐）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="nf">vPortFree</span><span class="p">(</span> <span class="kt">void</span> <span class="o">*</span><span class="n">pv</span> <span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">BlockLink_t</span> <span class="o">*</span><span class="n">pxLink</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 获取块头
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">pxLink</span> <span class="o">=</span> <span class="p">(</span> <span class="kt">void</span> <span class="o">*</span> <span class="p">)</span> <span class="p">(</span> <span class="p">(</span> <span class="p">(</span> <span class="kt">uint8_t</span> <span class="o">*</span> <span class="p">)</span> <span class="n">pv</span> <span class="p">)</span> <span class="o">-</span> <span class="k">sizeof</span><span class="p">(</span> <span class="n">BlockLink_t</span> <span class="p">)</span> <span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 与前/后空闲块合并
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">prvInsertBlockIntoFreeList</span><span class="p">(</span> <span class="n">pxLink</span> <span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="42-栈溢出检测">4.2 栈溢出检测</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// 方法 1：栈填充检测
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="nf">vTaskEndScheduler</span><span class="p">(</span> <span class="kt">void</span> <span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 检查栈顶/栈底是否被修改
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">if</span><span class="p">(</span> <span class="o">*</span><span class="n">pxTopOfStack</span> <span class="o">!=</span> <span class="n">tskSTACK_FILL_BYTE</span> <span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// 栈溢出！
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="nf">configASSERT</span><span class="p">(</span> <span class="n">pdFALSE</span> <span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 方法 2：MPU 保护（带 MPU 的 MCU）
</span></span></span><span class="line"><span class="cl"><span class="c1">// 配置栈区域为受保护内存，溢出时触发异常
</span></span></span></code></pre></div><h3 id="43-栈大小计算">4.3 栈大小计算</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// 最小栈大小 = 上下文 (17 字) + 局部变量 + 调用深度 + 安全余量
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#define MIN_STACK_SIZE  256   </span><span class="c1">// 1KB (Cortex-M4/M7)
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="c1">// 使用 uxTaskGetStackHighWaterMark() 监测
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">UBaseType_t</span> <span class="n">uxHighWaterMark</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="n">uxHighWaterMark</span> <span class="o">=</span> <span class="nf">uxTaskGetStackHighWaterMark</span><span class="p">(</span> <span class="nb">NULL</span> <span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span><span class="p">(</span> <span class="n">uxHighWaterMark</span> <span class="o">&lt;</span> <span class="mi">50</span> <span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 栈余量不足，增大栈大小
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span></code></pre></div><h2 id="性能优化">性能优化</h2>
<h3 id="51-减少上下文切换">5.1 减少上下文切换</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// 错误：频繁切换
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">for</span><span class="p">(</span> <span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="mi">100</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span> <span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nf">xQueueSend</span><span class="p">(</span> <span class="n">xQueue</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">data</span><span class="p">,</span> <span class="n">portMAX_DELAY</span> <span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 每次都触发切换
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 正确：批量处理
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">for</span><span class="p">(</span> <span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="mi">100</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span> <span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nf">xQueueSend</span><span class="p">(</span> <span class="n">xQueue</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">data</span><span class="p">,</span> <span class="mi">0</span> <span class="p">);</span>  <span class="c1">// 不阻塞
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="nf">taskYIELD</span><span class="p">();</span>  <span class="c1">// 手动切换一次
</span></span></span></code></pre></div><h3 id="52-零拷贝队列">5.2 零拷贝队列</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// 使用指针队列，避免数据拷贝
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">QueueHandle_t</span> <span class="n">xPtrQueue</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="n">xPtrQueue</span> <span class="o">=</span> <span class="nf">xQueueCreate</span><span class="p">(</span> <span class="mi">10</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span> <span class="kt">void</span> <span class="o">*</span> <span class="p">)</span> <span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 发送指针
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="o">*</span><span class="n">pData</span> <span class="o">=</span> <span class="nf">pvPortMalloc</span><span class="p">(</span> <span class="n">SIZE</span> <span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nf">xQueueSend</span><span class="p">(</span> <span class="n">xPtrQueue</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">pData</span><span class="p">,</span> <span class="mi">0</span> <span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 接收指针
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="o">*</span><span class="n">pReceived</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nf">xQueueReceive</span><span class="p">(</span> <span class="n">xPtrQueue</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">pReceived</span><span class="p">,</span> <span class="n">portMAX_DELAY</span> <span class="p">);</span>
</span></span></code></pre></div><h3 id="53-使用任务通知最快同步方式">5.3 使用任务通知（最快同步方式）</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// 替代二值信号量
</span></span></span><span class="line"><span class="cl"><span class="c1">// 发送端（中断中）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="nf">HAL_GPIO_EXTI_Callback</span><span class="p">(</span> <span class="kt">uint16_t</span> <span class="n">GPIO_Pin</span> <span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nf">vTaskNotifyGiveFromISR</span><span class="p">(</span> <span class="n">xTaskHandle</span><span class="p">,</span> <span class="nb">NULL</span> <span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 接收端
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="nf">vTaskFunction</span><span class="p">(</span> <span class="kt">void</span> <span class="o">*</span><span class="n">pvParameters</span> <span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span><span class="p">(</span> <span class="p">;;</span> <span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nf">ulTaskNotifyTake</span><span class="p">(</span> <span class="n">pdTRUE</span><span class="p">,</span> <span class="n">portMAX_DELAY</span> <span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// 处理事件
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 性能对比：
</span></span></span><span class="line"><span class="cl"><span class="c1">// 任务通知：~0.5μs
</span></span></span><span class="line"><span class="cl"><span class="c1">// 二值信号量：~2μs
</span></span></span><span class="line"><span class="cl"><span class="c1">// 队列：~5μs
</span></span></span></code></pre></div><h2 id="常见问题排查">常见问题排查</h2>
<h3 id="q1-任务不执行">Q1: 任务不执行</h3>
<p><strong>原因</strong>：调度器未启动或优先级配置错误。</p>
<p><strong>解决</strong>：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// 确保调用 vTaskStartScheduler()
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nf">vTaskStartScheduler</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 检查优先级范围（0 ~ configMAX_PRIORITIES-1）
</span></span></span></code></pre></div><h3 id="q2-系统死机">Q2: 系统死机</h3>
<p><strong>原因</strong>：栈溢出或死锁。</p>
<p><strong>解决</strong>：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// 启用栈溢出检测
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#define configCHECK_FOR_STACK_OVERFLOW  2
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="c1">// 检查互斥量使用顺序，避免死锁
</span></span></span></code></pre></div><h3 id="q3-实时性不达标">Q3: 实时性不达标</h3>
<p><strong>原因</strong>：临界区过长或低优先级任务占用 CPU。</p>
<p><strong>解决</strong>：</p>
<ul>
<li>缩短临界区代码</li>
<li>使用 <code>taskYIELD()</code> 主动让出</li>
<li>提高关键任务优先级</li>
</ul>
<h2 id="总结">总结</h2>
<p>FreeRTOS 实时性优化要点：</p>
<ol>
<li><strong>理解调度机制</strong>：就绪列表、上下文切换、临界区</li>
<li><strong>正确使用同步原语</strong>：互斥量、信号量、任务通知</li>
<li><strong>合理配置栈大小</strong>：避免溢出，节省 RAM</li>
<li><strong>减少不必要的切换</strong>：批量处理、零拷贝</li>
<li><strong>启用调试功能</strong>：栈检测、运行时统计</li>
</ol>
<p>掌握这些技巧，你的系统实时性将提升 5-10 倍！</p>
<hr>
<p><em>本文基于 FreeRTOS 官方源码和实际项目经验整理，结合 2026 年最新技术趋势编写。</em></p>
<h2 id="参考资料">参考资料</h2>
<ul>
<li>FreeRTOS Kernel Source Code</li>
<li>FreeRTOS Documentation</li>
<li>ARM Cortex-M Technical Reference Manual</li>
</ul>
]]></content:encoded></item><item><title>ARM Cortex-M7 缓存一致性与性能优化</title><link>https://tech-snippets.xyz/posts/arm-cortex-m7-cache-optimization/</link><pubDate>Mon, 30 Mar 2026 03:00:00 +0800</pubDate><guid>https://tech-snippets.xyz/posts/arm-cortex-m7-cache-optimization/</guid><description>深入分析 D-Cache/I-Cache 工作原理，解决缓存一致性问题，优化实时系统性能</description><content:encoded><![CDATA[<h2 id="引言">引言</h2>
<p>在嵌入式系统开发中，ARM Cortex-M7 处理器凭借其高性能和低功耗特性，广泛应用于工业控制、汽车电子和物联网设备。然而，很多开发者在使用 M7 内核时，常常遇到数据不一致、程序跑飞等诡异问题，这往往与缓存配置不当有关。</p>
<p>本文将深入剖析 Cortex-M7 的缓存架构，从硬件原理到软件配置，帮助你彻底理解并解决缓存相关问题。</p>
<h2 id="cortex-m7-缓存架构详解">Cortex-M7 缓存架构详解</h2>
<h3 id="11-缓存类型">1.1 缓存类型</h3>
<p>Cortex-M7 包含两级缓存：</p>
<ul>
<li><strong>I-Cache（指令缓存）</strong>：4KB 或 8KB，4 路组相联</li>
<li><strong>D-Cache（数据缓存）</strong>：4KB 或 8KB，4 路组相联</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// 缓存配置寄存器（SCB 外设）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#define SCB_CCR_IC_Msk       (1UL &lt;&lt; 17)  </span><span class="c1">// I-Cache 使能位
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#define SCB_CCR_DC_Msk       (1UL &lt;&lt; 16)  </span><span class="c1">// D-Cache 使能位
</span></span></span></code></pre></div><h3 id="12-缓存架构">1.2 缓存架构</h3>
<p><img alt="Cortex-M7 缓存架构图" loading="lazy" src="/images/cortex-m7-architecture.svg"></p>
<p><strong>架构说明</strong>：</p>
<ol>
<li><strong>CPU 核心</strong> (168MHz-400MHz) 访问数据时，优先从缓存层获取</li>
<li><strong>缓存层</strong> 包含 I-Cache、D-Cache、ITCM、DTCM</li>
<li><strong>AXI 总线矩阵</strong> 连接所有外设，提供高速数据通路</li>
<li><strong>外设层</strong> 包含 Flash、SRAM、DMA 和各种外设接口</li>
</ol>
<p><strong>TCM 优势</strong>：零等待访问，适合实时性要求极高的代码和数据</p>
<h3 id="13-缓存行结构">1.3 缓存行结构</h3>
<p>M7 的缓存行大小为 <strong>32 字节</strong>，这意味着：</p>
<ul>
<li>每次缓存缺失时，会从内存加载 32 字节</li>
<li>缓存对齐对性能影响巨大</li>
</ul>
<h2 id="缓存一致性问题">缓存一致性问题</h2>
<h3 id="21-dma-与缓存冲突">2.1 DMA 与缓存冲突</h3>
<p><strong>问题场景</strong>：</p>
<ol>
<li>CPU 从 D-Cache 读取数据 → 返回缓存数据</li>
<li>DMA 直接修改内存 → 写入新数据</li>
<li>CPU 再次从 D-Cache 读取 → <strong>返回旧数据（缓存未更新）</strong></li>
</ol>
<p><strong>结果</strong>：数据不一致！</p>
<h3 id="22-解决方案">2.2 解决方案</h3>
<h4 id="方法一禁用缓存区域mpu-配置">方法一：禁用缓存区域（MPU 配置）</h4>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">MPU_Config</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">ARM_MPU_Region_t</span> <span class="n">region</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 配置 DMA 缓冲区为非缓存区域
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">region</span><span class="p">.</span><span class="n">BaseAddress</span> <span class="o">=</span> <span class="n">DMA_BUFFER_ADDR</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">region</span><span class="p">.</span><span class="n">Size</span> <span class="o">=</span> <span class="n">REGION_SIZE_4KB</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">region</span><span class="p">.</span><span class="n">AccessPermission</span> <span class="o">=</span> <span class="n">ARM_MPU_AP_FULL</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">region</span><span class="p">.</span><span class="n">TypeExtField</span> <span class="o">=</span> <span class="n">ARM_MPU_ATTR_DEVICE</span><span class="p">;</span>  <span class="c1">// 设备内存，不缓存
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">region</span><span class="p">.</span><span class="n">Shareable</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">region</span><span class="p">.</span><span class="n">Cacheable</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">region</span><span class="p">.</span><span class="n">Bufferable</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="nf">ARM_MPU_SetRegion</span><span class="p">(</span><span class="o">&amp;</span><span class="n">region</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h4 id="方法二手动缓存维护">方法二：手动缓存维护</h4>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// DMA 传输前，清理 D-Cache（写回脏数据）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="nf">DMA_PrepareForRead</span><span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="n">buffer</span><span class="p">,</span> <span class="kt">uint32_t</span> <span class="n">size</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nf">SCB_CleanDCache_by_Addr</span><span class="p">(</span><span class="n">buffer</span><span class="p">,</span> <span class="n">size</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// DMA 传输后，使 D-Cache 失效（丢弃旧数据）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="nf">DMA_InvalidateAfterWrite</span><span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="n">buffer</span><span class="p">,</span> <span class="kt">uint32_t</span> <span class="n">size</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nf">SCB_InvalidateDCache_by_Addr</span><span class="p">(</span><span class="n">buffer</span><span class="p">,</span> <span class="n">size</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h2 id="性能优化实战">性能优化实战</h2>
<h3 id="31-代码段优化">3.1 代码段优化</h3>
<p>将关键代码放在 ITCM（紧耦合内存）中，绕过 I-Cache：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// 放在 ITCM 中的函数（零等待执行）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="nf">__attribute__</span><span class="p">((</span><span class="nf">section</span><span class="p">(</span><span class="s">&#34;.itcm&#34;</span><span class="p">)))</span> <span class="nf">Critical_Function</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 实时性要求极高的代码
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span></code></pre></div><h3 id="32-数据段优化">3.2 数据段优化</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// 放在 DTCM 中的数据（零等待访问）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">uint32_t</span> <span class="nf">__attribute__</span><span class="p">((</span><span class="nf">section</span><span class="p">(</span><span class="s">&#34;.dtcm&#34;</span><span class="p">)))</span> <span class="n">Critical_Data</span><span class="p">[</span><span class="mi">256</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 对齐到缓存行边界
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">uint32_t</span> <span class="nf">__attribute__</span><span class="p">((</span><span class="nf">aligned</span><span class="p">(</span><span class="mi">32</span><span class="p">)))</span> <span class="n">DMA_Buffer</span><span class="p">[</span><span class="mi">1024</span><span class="p">];</span>
</span></span></code></pre></div><h3 id="33-分支预测优化">3.3 分支预测优化</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// 使用 __builtin_expect 提示分支概率
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">if</span> <span class="p">(</span><span class="nf">__builtin_expect</span><span class="p">(</span><span class="n">condition</span><span class="p">,</span> <span class="mi">1</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 大概率执行的分支
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 小概率执行的分支
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span></code></pre></div><h2 id="常见问题排查">常见问题排查</h2>
<h3 id="q1-程序在开启缓存后跑飞">Q1: 程序在开启缓存后跑飞</h3>
<p><strong>原因</strong>：中断向量表未对齐或缓存未清理。</p>
<p><strong>解决</strong>：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// 确保向量表 32 字节对齐
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">SCB</span><span class="o">-&gt;</span><span class="n">VTOR</span> <span class="o">=</span> <span class="p">(</span><span class="kt">uint32_t</span><span class="p">)</span><span class="n">vector_table</span> <span class="o">&amp;</span> <span class="mh">0xFFFFFFE0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 开启缓存前清理所有缓存
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nf">SCB_CleanInvalidateDCache</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="nf">SCB_InvalidateICache</span><span class="p">();</span>
</span></span></code></pre></div><h3 id="q2-dma-数据丢失">Q2: DMA 数据丢失</h3>
<p><strong>原因</strong>：D-Cache 脏数据未写回。</p>
<p><strong>解决</strong>：在 DMA 启动前调用 <code>SCB_CleanDCache_by_Addr()</code>。</p>
<h3 id="q3-多核系统数据不一致">Q3: 多核系统数据不一致</h3>
<p><strong>原因</strong>：CPU0 和 CPU1 缓存不同步。</p>
<p><strong>解决</strong>：使用共享内存时，配置为共享缓存或使用消息传递机制。</p>
<h2 id="总结">总结</h2>
<p>Cortex-M7 缓存优化的核心要点：</p>
<ol>
<li><strong>理解缓存架构</strong>：I-Cache/D-Cache 大小、行大小、相联度</li>
<li><strong>正确处理 DMA</strong>：清理/失效缓存，或使用非缓存内存</li>
<li><strong>合理利用 TCM</strong>：关键代码/数据放在 ITCM/DTCM</li>
<li><strong>注意对齐</strong>：数据和代码对齐到缓存行边界</li>
</ol>
<p>掌握这些技巧，你的嵌入式系统性能将提升 3-10 倍！</p>
<hr>
<p><em>本文基于 ARM 官方技术文档和实际项目经验整理，结合 2026 年最新技术趋势编写。</em></p>
<h2 id="参考资料">参考资料</h2>
<ul>
<li>ARM Cortex-M7 Technical Reference Manual</li>
<li>ARMv7-M Architecture Reference Manual</li>
<li>STM32H7 参考手册</li>
</ul>
]]></content:encoded></item><item><title>基于 DMA 的高速 ADC 数据采集系统设计</title><link>https://tech-snippets.xyz/posts/dma-adc-data-acquisition-system/</link><pubDate>Mon, 30 Mar 2026 03:00:00 +0800</pubDate><guid>https://tech-snippets.xyz/posts/dma-adc-data-acquisition-system/</guid><description>多通道 DMA 循环缓冲、定时器触发、数据对齐与实时处理完整方案</description><content:encoded><![CDATA[<h2 id="引言">引言</h2>
<p>在工业控制、医疗仪器和测试测量领域，高速数据采集系统是核心模块。传统的轮询或中断方式采集 ADC 数据，CPU 占用率高且实时性差。使用 DMA（直接内存访问）可以实现零 CPU 干预的高速数据采集。</p>
<p>本文将详细介绍基于 DMA 的 ADC 采集系统的设计方法，包括硬件配置、软件实现和性能优化。</p>
<h2 id="系统架构">系统架构</h2>
<h3 id="11-系统架构">1.1 系统架构</h3>
<p><img alt="DMA ADC 数据采集系统架构图" loading="lazy" src="/images/dma-adc-system.svg"></p>
<p><strong>架构说明</strong>：</p>
<ol>
<li><strong>传感器层</strong>：输出模拟信号（温度/压力/光电等）</li>
<li><strong>信号调理</strong>：放大、滤波，调理到 0-3.3V 范围</li>
<li><strong>ADC</strong>：12/14/16-bit 精度，最高 1MSPS 采样率</li>
<li><strong>DMA 控制器</strong>：循环缓冲模式，自动回绕，零 CPU 干预</li>
<li><strong>内存缓冲区</strong>：双缓冲策略，Buffer[0] 和 Buffer[1] 交替使用</li>
<li><strong>DSP 处理</strong>：FFT、滤波、特征提取等实时算法</li>
</ol>
<p><strong>触发机制</strong>：定时器提供精确采样率（100Hz - 1MHz）</p>
<p><strong>关键优势</strong>：DMA 实现零 CPU 占用的高速数据采集</p>
<h3 id="12-关键指标">1.2 关键指标</h3>
<table>
<thead>
<tr>
<th>参数</th>
<th>典型值</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>采样率</td>
<td>100kSPS - 10MSPS</td>
<td>根据应用需求选择</td>
</tr>
<tr>
<td>分辨率</td>
<td>12/14/16 bit</td>
<td>ADC 精度</td>
</tr>
<tr>
<td>通道数</td>
<td>1-16</td>
<td>多通道同步采集</td>
</tr>
<tr>
<td>缓冲大小</td>
<td>1KB - 1MB</td>
<td>根据处理延迟确定</td>
</tr>
</tbody>
</table>
<h2 id="dma-配置详解">DMA 配置详解</h2>
<h3 id="21-dma-控制器选择">2.1 DMA 控制器选择</h3>
<p>以 STM32H7 为例：</p>
<ul>
<li><strong>DMA1/DMA2</strong>：通用 DMA，支持外设到内存传输</li>
<li><strong>BDMA</strong>：基本 DMA，用于低功耗外设</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// DMA 配置结构体
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">DMA_HandleTypeDef</span> <span class="n">hdma_adc1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">MX_DMA_Init</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 启用 DMA 时钟
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">__HAL_RCC_DMA1_CLK_ENABLE</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 配置 DMA 通道
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">hdma_adc1</span><span class="p">.</span><span class="n">Instance</span> <span class="o">=</span> <span class="n">DMA1_Stream0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">hdma_adc1</span><span class="p">.</span><span class="n">Init</span><span class="p">.</span><span class="n">Request</span> <span class="o">=</span> <span class="n">DMA_REQUEST_ADC1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">hdma_adc1</span><span class="p">.</span><span class="n">Init</span><span class="p">.</span><span class="n">Direction</span> <span class="o">=</span> <span class="n">DMA_PERIPH_TO_MEMORY</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">hdma_adc1</span><span class="p">.</span><span class="n">Init</span><span class="p">.</span><span class="n">PeriphInc</span> <span class="o">=</span> <span class="n">DMA_PINC_DISABLE</span><span class="p">;</span>      <span class="c1">// 外设地址不递增
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">hdma_adc1</span><span class="p">.</span><span class="n">Init</span><span class="p">.</span><span class="n">MemInc</span> <span class="o">=</span> <span class="n">DMA_MINC_ENABLE</span><span class="p">;</span>          <span class="c1">// 内存地址递增
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">hdma_adc1</span><span class="p">.</span><span class="n">Init</span><span class="p">.</span><span class="n">PeriphDataAlignment</span> <span class="o">=</span> <span class="n">DMA_PDATAALIGN_HALFWORD</span><span class="p">;</span>  <span class="c1">// 16 位数据
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">hdma_adc1</span><span class="p">.</span><span class="n">Init</span><span class="p">.</span><span class="n">MemDataAlignment</span> <span class="o">=</span> <span class="n">DMA_MDATAALIGN_HALFWORD</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">hdma_adc1</span><span class="p">.</span><span class="n">Init</span><span class="p">.</span><span class="n">Mode</span> <span class="o">=</span> <span class="n">DMA_CIRCULAR</span><span class="p">;</span>               <span class="c1">// 循环模式
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">hdma_adc1</span><span class="p">.</span><span class="n">Init</span><span class="p">.</span><span class="n">Priority</span> <span class="o">=</span> <span class="n">DMA_PRIORITY_HIGH</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">hdma_adc1</span><span class="p">.</span><span class="n">Init</span><span class="p">.</span><span class="n">FIFOMode</span> <span class="o">=</span> <span class="n">DMA_FIFOMODE_DISABLE</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="nf">HAL_DMA_Init</span><span class="p">(</span><span class="o">&amp;</span><span class="n">hdma_adc1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 关联 DMA 到 ADC
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">__HAL_LINKDMA</span><span class="p">(</span><span class="o">&amp;</span><span class="n">hadc1</span><span class="p">,</span> <span class="n">DMA_Handle</span><span class="p">,</span> <span class="n">hdma_adc1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="22-双缓冲策略">2.2 双缓冲策略</h3>
<p><strong>工作流程</strong>：</p>
<ol>
<li><strong>DMA 半传输完成</strong> → 触发 <code>HAL_ADC_ConvHalfCpltCallback</code></li>
<li><strong>DMA 全传输完成</strong> → 触发 <code>HAL_ADC_ConvCpltCallback</code></li>
<li><strong>回调函数</strong> → 设置 buffer_ready 标志</li>
<li><strong>主循环</strong> → 检测标志位 → 处理数据</li>
</ol>
<p><strong>循环缓冲（推荐）</strong>：</p>
<ul>
<li>DMA 自动回绕到缓冲区起始位置</li>
<li>适合连续采集场景</li>
<li>需要双缓冲或半传输中断处理</li>
</ul>
<p><strong>普通缓冲</strong>：</p>
<ul>
<li>
<p>传输完成后停止</p>
</li>
<li>
<p>适合单次采集</p>
</li>
<li>
<p>需要手动重启 DMA</p>
</li>
<li>
<p>DMA 自动回绕到缓冲区起始位置</p>
</li>
<li>
<p>适合连续采集场景</p>
</li>
<li>
<p>需要双缓冲或半传输中断处理</p>
</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="cp">#define ADC_BUFFER_SIZE 1024
</span></span></span><span class="line"><span class="cl"><span class="cp"></span><span class="kt">uint16_t</span> <span class="n">adc_buffer</span><span class="p">[</span><span class="n">ADC_BUFFER_SIZE</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 启动 DMA 循环采集
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nf">HAL_ADC_Start_DMA</span><span class="p">(</span><span class="o">&amp;</span><span class="n">hadc1</span><span class="p">,</span> <span class="p">(</span><span class="kt">uint32_t</span><span class="o">*</span><span class="p">)</span><span class="n">adc_buffer</span><span class="p">,</span> <span class="n">ADC_BUFFER_SIZE</span><span class="p">);</span>
</span></span></code></pre></div><p><strong>普通缓冲</strong>：</p>
<ul>
<li>传输完成后停止</li>
<li>适合单次采集</li>
<li>需要手动重启 DMA</li>
</ul>
<h3 id="23-定时器触发配置">2.3 定时器触发配置</h3>
<p>使用定时器触发 ADC 转换，实现精确采样：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// 配置定时器（100kHz 触发）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">TIM_HandleTypeDef</span> <span class="n">htim_trigger</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">MX_TIM2_Init</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">htim_trigger</span><span class="p">.</span><span class="n">Instance</span> <span class="o">=</span> <span class="n">TIM2</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">htim_trigger</span><span class="p">.</span><span class="n">Init</span><span class="p">.</span><span class="n">Prescaler</span> <span class="o">=</span> <span class="mi">83</span><span class="p">;</span>           <span class="c1">// 84MHz / 84 = 1MHz
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">htim_trigger</span><span class="p">.</span><span class="n">Init</span><span class="p">.</span><span class="n">CounterMode</span> <span class="o">=</span> <span class="n">TIM_COUNTERMODE_UP</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">htim_trigger</span><span class="p">.</span><span class="n">Init</span><span class="p">.</span><span class="n">Period</span> <span class="o">=</span> <span class="mi">9</span><span class="p">;</span>               <span class="c1">// 1MHz / 10 = 100kHz
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">htim_trigger</span><span class="p">.</span><span class="n">Init</span><span class="p">.</span><span class="n">ClockDivision</span> <span class="o">=</span> <span class="n">TIM_CLOCKDIVISION_DIV1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 配置触发输出
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">TIM_MasterConfigTypeDef</span> <span class="n">sMasterConfig</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">sMasterConfig</span><span class="p">.</span><span class="n">MasterOutputTrigger</span> <span class="o">=</span> <span class="n">TIM_TRGO_UPDATE</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">sMasterConfig</span><span class="p">.</span><span class="n">MasterSlaveMode</span> <span class="o">=</span> <span class="n">TIM_MASTERSLAVEMODE_DISABLE</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nf">HAL_TIMEx_MasterConfigSynchronization</span><span class="p">(</span><span class="o">&amp;</span><span class="n">htim_trigger</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">sMasterConfig</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="nf">HAL_TIM_Base_Start</span><span class="p">(</span><span class="o">&amp;</span><span class="n">htim_trigger</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// ADC 配置为外部触发
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">hadc1</span><span class="p">.</span><span class="n">Init</span><span class="p">.</span><span class="n">ExternalTrigConv</span> <span class="o">=</span> <span class="n">ADC_EXTERNALTRIG_T2_TRGO</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="n">hadc1</span><span class="p">.</span><span class="n">Init</span><span class="p">.</span><span class="n">ExternalTrigConvEdge</span> <span class="o">=</span> <span class="n">ADC_EXTERNALTRIGCONVEDGE_RISING</span><span class="p">;</span>
</span></span></code></pre></div><h2 id="数据处理与同步">数据处理与同步</h2>
<h3 id="31-双缓冲策略">3.1 双缓冲策略</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="cp">#define BUFFER_SIZE 512
</span></span></span><span class="line"><span class="cl"><span class="cp"></span><span class="kt">uint16_t</span> <span class="n">buffer</span><span class="p">[</span><span class="mi">2</span><span class="p">][</span><span class="n">BUFFER_SIZE</span><span class="p">];</span>  <span class="c1">// 双缓冲
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">volatile</span> <span class="kt">uint8_t</span> <span class="n">buffer_index</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">volatile</span> <span class="kt">uint8_t</span> <span class="n">buffer_ready</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// DMA 半传输完成回调
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="nf">HAL_ADC_ConvHalfCpltCallback</span><span class="p">(</span><span class="n">ADC_HandleTypeDef</span><span class="o">*</span> <span class="n">hadc</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">buffer_index</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">buffer_ready</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// DMA 传输完成回调
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="nf">HAL_ADC_ConvCpltCallback</span><span class="p">(</span><span class="n">ADC_HandleTypeDef</span><span class="o">*</span> <span class="n">hadc</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">buffer_index</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">buffer_ready</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 主循环处理
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">while</span> <span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">buffer_ready</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nf">ProcessData</span><span class="p">(</span><span class="n">buffer</span><span class="p">[</span><span class="n">buffer_index</span><span class="p">],</span> <span class="n">BUFFER_SIZE</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="n">buffer_ready</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="32-数据对齐处理">3.2 数据对齐处理</h3>
<p>DMA 传输时注意数据对齐：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// 确保缓冲区 32 字节对齐（Cortex-M7 缓存行大小）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">uint16_t</span> <span class="n">adc_buffer</span><span class="p">[</span><span class="mi">1024</span><span class="p">]</span> <span class="nf">__attribute__</span><span class="p">((</span><span class="nf">aligned</span><span class="p">(</span><span class="mi">32</span><span class="p">)));</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 或者使用编译器指令
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#pragma data_alignment = 32
</span></span></span><span class="line"><span class="cl"><span class="cp"></span><span class="kt">uint16_t</span> <span class="n">adc_buffer</span><span class="p">[</span><span class="mi">1024</span><span class="p">];</span>
</span></span></code></pre></div><h3 id="33-缓存维护">3.3 缓存维护</h3>
<p>对于 Cortex-M7，DMA 传输后需要维护 D-Cache：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">DMA_InvalidateBuffer</span><span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="n">buffer</span><span class="p">,</span> <span class="kt">uint32_t</span> <span class="n">size</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nf">SCB_InvalidateDCache_by_Addr</span><span class="p">(</span><span class="n">buffer</span><span class="p">,</span> <span class="n">size</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 在半传输/全传输回调中调用
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="nf">HAL_ADC_ConvCpltCallback</span><span class="p">(</span><span class="n">ADC_HandleTypeDef</span><span class="o">*</span> <span class="n">hadc</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nf">DMA_InvalidateBuffer</span><span class="p">(</span><span class="n">adc_buffer</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">adc_buffer</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="n">data_ready</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h2 id="性能优化">性能优化</h2>
<h3 id="41-提高采样率">4.1 提高采样率</h3>
<ul>
<li>使用高速 ADC 内核（如 STM32H7 的 16-bit 5MSPS）</li>
<li>降低 ADC 分辨率换取速度（16bit → 12bit）</li>
<li>使用多 ADC  interleaved 模式</li>
</ul>
<h3 id="42-降低-cpu-占用">4.2 降低 CPU 占用</h3>
<ul>
<li>启用 DMA 中断，仅在缓冲区满时处理</li>
<li>使用 DSP 库进行 SIMD 加速</li>
<li>将数据处理放在 DTCM 中执行</li>
</ul>
<h3 id="43-降低延迟">4.3 降低延迟</h3>
<ul>
<li>减小缓冲区大小（但增加中断频率）</li>
<li>使用高优先级 DMA 通道</li>
<li>启用数据缓存（D-Cache）</li>
</ul>
<h2 id="常见问题">常见问题</h2>
<h3 id="q1-dma-传输数据丢失">Q1: DMA 传输数据丢失</h3>
<p><strong>原因</strong>：缓冲区太小或处理太慢。</p>
<p><strong>解决</strong>：增大缓冲区，或使用双缓冲 + 中断处理。</p>
<h3 id="q2-采样率不稳定">Q2: 采样率不稳定</h3>
<p><strong>原因</strong>：定时器配置错误或时钟源不稳定。</p>
<p><strong>解决</strong>：使用外部晶振，检查定时器预分频器。</p>
<h3 id="q3-adc-数据跳变">Q3: ADC 数据跳变</h3>
<p><strong>原因</strong>：参考电压波动或接地不良。</p>
<p><strong>解决</strong>：使用独立模拟电源，优化 PCB 布局。</p>
<h2 id="总结">总结</h2>
<p>基于 DMA 的 ADC 采集系统设计要点：</p>
<ol>
<li><strong>选择合适 DMA 通道</strong>：支持外设到内存，循环模式</li>
<li><strong>配置定时器触发</strong>：实现精确采样间隔</li>
<li><strong>使用双缓冲策略</strong>：避免数据丢失</li>
<li><strong>注意数据对齐</strong>：提升缓存效率</li>
<li><strong>维护 D-Cache</strong>：确保数据一致性</li>
</ol>
<p>掌握这些技巧，你可以轻松实现 1MSPS 以上的零 CPU 占用数据采集！</p>
<hr>
<p><em>本文基于 ST 官方技术文档和实际项目经验整理，结合 2026 年最新技术趋势编写。</em></p>
<h2 id="参考资料">参考资料</h2>
<ul>
<li>STM32H7 Reference Manual</li>
<li>ARM Cortex-M7 Technical Reference Manual</li>
<li>STM32CubeH7 HAL 库文档</li>
</ul>
]]></content:encoded></item><item><title>ASCII 图表和电路图的最佳实践</title><link>https://tech-snippets.xyz/posts/technical-diagram-best-practices/</link><pubDate>Sun, 29 Mar 2026 03:00:00 +0800</pubDate><guid>https://tech-snippets.xyz/posts/technical-diagram-best-practices/</guid><description>在技术文档中清晰表达电路和架构图的方法</description><content:encoded><![CDATA[<h2 id="图表表达规范">图表表达规范</h2>
<p>在技术博客中，为了清晰表达电路和架构，我们采用以下规范：</p>
<hr>
<h2 id="1-简单电路用表格">1. 简单电路用表格</h2>
<h3 id="-不好的方式ascii-图会乱">❌ 不好的方式（ASCII 图会乱）</h3>
<pre tabindex="0"><code>VDD ──┬── 10uF ── GND
      └── 0.1uF ── GND
</code></pre><h3 id="-推荐方式表格清晰">✅ 推荐方式（表格清晰）</h3>
<table>
<thead>
<tr>
<th>电源</th>
<th>电容</th>
<th>连接</th>
<th>作用</th>
</tr>
</thead>
<tbody>
<tr>
<td>VDD</td>
<td>10uF</td>
<td>→ GND</td>
<td>低频滤波</td>
</tr>
<tr>
<td>VDD</td>
<td>0.1uF</td>
<td>→ GND</td>
<td>高频去耦</td>
</tr>
<tr>
<td>I2C SDA</td>
<td>4.7kΩ</td>
<td>→ 3.3V</td>
<td>上拉电阻</td>
</tr>
<tr>
<td>I2C SCL</td>
<td>4.7kΩ</td>
<td>→ 3.3V</td>
<td>上拉电阻</td>
</tr>
</tbody>
</table>
<hr>
<h2 id="2-系统架构用-svg-图片">2. 系统架构用 SVG 图片</h2>
<h3 id="-复杂的-ascii-框图会乱码">❌ 复杂的 ASCII 框图（会乱码）</h3>
<pre tabindex="0"><code>┌─────────┐    ┌─────────┐
│   MCU   │───→│  Sensor │
└─────────┘    └─────────┘
</code></pre><h3 id="-推荐方式svg-矢量图">✅ 推荐方式（SVG 矢量图）</h3>
<p><img alt="系统架构图示例" loading="lazy" src="/images/system-architecture-example.svg"></p>
<p><strong>架构说明</strong>：</p>
<ol>
<li><strong>传感器层</strong>：MPU6050 提供 6 轴 IMU 数据（I2C 接口）</li>
<li><strong>主控层</strong>：STM32F4 运行 FreeRTOS，168MHz Cortex-M4</li>
<li><strong>执行层</strong>：TB6612 双 H 桥驱动，PWM 控制</li>
<li><strong>反馈层</strong>：AS5047 磁编码器提供位置反馈（SPI 接口）</li>
</ol>
<p><strong>FreeRTOS 任务划分</strong>：</p>
<ul>
<li><strong>控制任务 (1kHz)</strong>：串级 PID 控制（直立环 + 速度环）</li>
<li><strong>通信任务</strong>：蓝牙遥控、状态显示</li>
<li><strong>传感器任务</strong>：IMU 数据采集、编码器读取</li>
</ul>
<hr>
<h2 id="3-代码示例用代码块">3. 代码示例用代码块</h2>
<h3 id="-正确方式">✅ 正确方式</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// PID 控制示例
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">float</span> <span class="nf">pid_calc</span><span class="p">(</span><span class="n">PID_TypeDef</span> <span class="o">*</span><span class="n">pid</span><span class="p">,</span> <span class="kt">float</span> <span class="n">target</span><span class="p">,</span> <span class="kt">float</span> <span class="n">measure</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">float</span> <span class="n">error</span> <span class="o">=</span> <span class="n">target</span> <span class="o">-</span> <span class="n">measure</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 比例项
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="kt">float</span> <span class="n">p_term</span> <span class="o">=</span> <span class="n">pid</span><span class="o">-&gt;</span><span class="n">Kp</span> <span class="o">*</span> <span class="n">error</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 积分项（带限幅）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">pid</span><span class="o">-&gt;</span><span class="n">integral</span> <span class="o">+=</span> <span class="n">error</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">pid</span><span class="o">-&gt;</span><span class="n">integral</span> <span class="o">=</span> <span class="nf">constrain</span><span class="p">(</span><span class="n">pid</span><span class="o">-&gt;</span><span class="n">integral</span><span class="p">,</span> <span class="o">-</span><span class="mi">100</span><span class="p">,</span> <span class="mi">100</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="kt">float</span> <span class="n">i_term</span> <span class="o">=</span> <span class="n">pid</span><span class="o">-&gt;</span><span class="n">Ki</span> <span class="o">*</span> <span class="n">pid</span><span class="o">-&gt;</span><span class="n">integral</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 微分项
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="kt">float</span> <span class="n">d_term</span> <span class="o">=</span> <span class="n">pid</span><span class="o">-&gt;</span><span class="n">Kd</span> <span class="o">*</span> <span class="p">(</span><span class="n">error</span> <span class="o">-</span> <span class="n">pid</span><span class="o">-&gt;</span><span class="n">last_error</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">p_term</span> <span class="o">+</span> <span class="n">i_term</span> <span class="o">+</span> <span class="n">d_term</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><hr>
<h2 id="4-数学公式用-latex">4. 数学公式用 LaTeX</h2>
<h3 id="-推荐方式">✅ 推荐方式</h3>
<p>PID 计算公式：</p>
<p>$$
u(t) = K_p e(t) + K_i \int_0^t e(\tau)d\tau + K_d \frac{de(t)}{dt}
$$</p>
<p>离散化：</p>
<p>$$
u[k] = K_p e[k] + K_i \sum_{i=0}^{k} e[i] + K_d (e[k] - e[k-1])
$$</p>
<hr>
<h2 id="5-时序用-svg-图片--列表">5. 时序用 SVG 图片 + 列表</h2>
<h3 id="-ascii-时序图会乱码">❌ ASCII 时序图（会乱码）</h3>
<pre tabindex="0"><code>CLK ─┐  ┌─┐  ┌─┐  ┌─┐
     └──┘ └──┘ └──┘
DATA ────┐┌────┐┌────┐
         └┘    └┘
</code></pre><h3 id="-推荐方式svg-时序图--列表">✅ 推荐方式（SVG 时序图 + 列表）</h3>
<p><img alt="I2C 时序图" loading="lazy" src="/images/i2c-timing-diagram.svg"></p>
<p><strong>I2C 读取时序说明</strong>：</p>
<ol>
<li><strong>起始条件</strong>: SDA 高→低（SCL 高电平）</li>
<li><strong>发送地址</strong>: 7 位从机地址 + 读位</li>
<li><strong>等待应答</strong>: 第 9 周期采样 ACK</li>
<li><strong>读取数据</strong>: 8 位数据，MSB 在前</li>
<li><strong>发送 NACK</strong>: 最后一个字节后</li>
<li><strong>停止条件</strong>: SDA 低→高（SCL 高电平）</li>
</ol>
<hr>
<h2 id="6-pcb-布局用文字说明">6. PCB 布局用文字说明</h2>
<h3 id="-推荐格式">✅ 推荐格式</h3>
<p><strong>关键器件布局原则</strong>：</p>
<table>
<thead>
<tr>
<th>器件</th>
<th>位置</th>
<th>注意事项</th>
</tr>
</thead>
<tbody>
<tr>
<td>MCU</td>
<td>板子中心</td>
<td>靠近去耦电容</td>
</tr>
<tr>
<td>晶振</td>
<td>靠近 MCU OSC 引脚</td>
<td>下方不走线</td>
</tr>
<tr>
<td>LDO</td>
<td>靠近电源入口</td>
<td>输入输出电容就近</td>
</tr>
<tr>
<td>连接器</td>
<td>板边</td>
<td>固定孔附近</td>
</tr>
</tbody>
</table>
<p><strong>布线规则</strong>：</p>
<ul>
<li><strong>电源线宽</strong>: ≥0.8mm (1A)</li>
<li><strong>信号线宽</strong>: 0.2-0.3mm</li>
<li><strong>地平面</strong>: 完整，避免分割</li>
<li><strong>差分对</strong>: 等长，间距 3W</li>
</ul>
<hr>
<h2 id="总结">总结</h2>
<p>技术文档的图表表达原则：</p>
<ol>
<li><strong>简单电路</strong> → 表格</li>
<li><strong>系统架构</strong> → 文字 + 表格</li>
<li><strong>代码示例</strong> → 代码块</li>
<li><strong>数学公式</strong> → LaTeX</li>
<li><strong>时序关系</strong> → 列表</li>
<li><strong>PCB 布局</strong> → 表格 + 文字</li>
</ol>
<p>这样即使在纯文本环境下，也能清晰表达技术内容！</p>
<hr>
<p><em>本文档规范适用于技术博客、数据手册、应用笔记等场景</em></p>
]]></content:encoded></item><item><title>RISC-V 架构详解：开源处理器的未来</title><link>https://tech-snippets.xyz/posts/risc-v-architecture-guide/</link><pubDate>Sat, 28 Mar 2026 03:00:00 +0800</pubDate><guid>https://tech-snippets.xyz/posts/risc-v-architecture-guide/</guid><description>深入了解 RISC-V 开源指令集架构，从基础概念到实际应用</description><content:encoded><![CDATA[<h2 id="什么是-risc-v">什么是 RISC-V？</h2>
<p>RISC-V（读作&quot;Risk Five&quot;）是一个基于 RISC 原则的开源指令集架构（ISA）。它由加州大学伯克利分校于 2010 年设计，如今已成为开源硬件革命的核心。</p>
<h3 id="risc-v-的优势">RISC-V 的优势</h3>
<table>
<thead>
<tr>
<th>特性</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>开源免费</strong></td>
<td>无需授权费，可自由使用</td>
</tr>
<tr>
<td><strong>模块化设计</strong></td>
<td>基础 ISA + 可选扩展</td>
</tr>
<tr>
<td><strong>简洁高效</strong></td>
<td>指令集精简，易于实现</td>
</tr>
<tr>
<td><strong>生态系统</strong></td>
<td>快速增长的工具链和软件支持</td>
</tr>
<tr>
<td><strong>开放性</strong></td>
<td>由非营利组织 RISC-V International 管理</td>
</tr>
</tbody>
</table>
<h2 id="risc-v-架构基础">RISC-V 架构基础</h2>
<h3 id="指令集分类">指令集分类</h3>
<p>RISC-V 采用模块化设计：</p>
<ol>
<li>
<p><strong>基础 ISA</strong>（必须实现）</p>
<ul>
<li>RV32I：32 位整数基础</li>
<li>RV64I：64 位整数基础</li>
<li>RV128I：128 位整数基础</li>
</ul>
</li>
<li>
<p><strong>标准扩展</strong>（可选）</p>
<ul>
<li><strong>M</strong>：整数乘除法</li>
<li><strong>A</strong>：原子操作</li>
<li><strong>F</strong>：单精度浮点</li>
<li><strong>D</strong>：双精度浮点</li>
<li><strong>C</strong>：压缩指令</li>
</ul>
</li>
</ol>
<h3 id="寄存器组织">寄存器组织</h3>
<p>RISC-V 有 32 个通用寄存器（x0-x31）：</p>
<ul>
<li>x0：硬连线到 0（常零寄存器）</li>
<li>x1：返回地址（ra）</li>
<li>x2：栈指针（sp）</li>
<li>x5-x7：临时寄存器</li>
</ul>
<h2 id="为什么选择-risc-v">为什么选择 RISC-V？</h2>
<h3 id="对比-arm-和-x86">对比 ARM 和 x86</h3>
<table>
<thead>
<tr>
<th>特性</th>
<th>RISC-V</th>
<th>ARM</th>
<th>x86</th>
</tr>
</thead>
<tbody>
<tr>
<td>授权模式</td>
<td>开源免费</td>
<td>商业授权</td>
<td>商业授权</td>
</tr>
<tr>
<td>指令复杂度</td>
<td>低</td>
<td>中</td>
<td>高</td>
</tr>
<tr>
<td>功耗</td>
<td>低</td>
<td>低</td>
<td>高</td>
</tr>
<tr>
<td>生态成熟度</td>
<td>发展中</td>
<td>成熟</td>
<td>非常成熟</td>
</tr>
<tr>
<td>定制能力</td>
<td>强</td>
<td>弱</td>
<td>无</td>
</tr>
</tbody>
</table>
<h3 id="应用场景">应用场景</h3>
<ul>
<li><strong>嵌入式系统</strong>：微控制器、IoT 设备</li>
<li><strong>边缘计算</strong>：AI 加速器、智能摄像头</li>
<li><strong>数据中心</strong>：服务器处理器</li>
<li><strong>学术研究</strong>：处理器设计教学</li>
</ul>
<h2 id="开发环境搭建">开发环境搭建</h2>
<h3 id="工具链安装">工具链安装</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># Ubuntu/Debian</span>
</span></span><span class="line"><span class="cl">sudo apt install gcc-riscv64-unknown-elf
</span></span><span class="line"><span class="cl">sudo apt install qemu-system-misc
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># macOS</span>
</span></span><span class="line"><span class="cl">brew install riscv64-elf-gcc
</span></span></code></pre></div><h3 id="第一个-risc-v-程序">第一个 RISC-V 程序</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// hello.c
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nf">printf</span><span class="p">(</span><span class="s">&#34;Hello, RISC-V!</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 编译
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">riscv64</span><span class="o">-</span><span class="n">unknown</span><span class="o">-</span><span class="n">elf</span><span class="o">-</span><span class="n">gcc</span> <span class="o">-</span><span class="n">o</span> <span class="n">hello</span> <span class="n">hello</span><span class="p">.</span><span class="n">c</span>
</span></span><span class="line"><span class="cl"><span class="c1">// 运行（使用 QEMU 模拟器）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">qemu</span><span class="o">-</span><span class="n">riscv64</span> <span class="n">hello</span>
</span></span></code></pre></div><h2 id="risc-v-开发板推荐">RISC-V 开发板推荐</h2>
<h3 id="入门级">入门级</h3>
<ul>
<li><strong>Sipeed Longan Nano</strong>：GD32VF103，$10</li>
<li><strong>HiFive1 Rev B</strong>：SiFive E31，$60</li>
</ul>
<h3 id="进阶级">进阶级</h3>
<ul>
<li><strong>VisionFive 2</strong>：StarFive JH7110，$80</li>
<li><strong>Lichee Pi 4A</strong>：T-Head TH1520，$150</li>
</ul>
<h3 id="开发工具">开发工具</h3>
<ul>
<li><strong>GDB</strong>：调试器</li>
<li><strong>OpenOCD</strong>：片上调试</li>
<li><strong>GTKWave</strong>：波形查看</li>
</ul>
<h2 id="学习资源">学习资源</h2>
<h3 id="官方资源">官方资源</h3>
<ul>
<li><a href="https://riscv.org/">RISC-V International</a></li>
<li><a href="https://github.com/riscv">RISC-V GitHub</a></li>
</ul>
<h3 id="在线课程">在线课程</h3>
<ul>
<li>RISC-V 官方培训课程</li>
<li>edX：RISC-V 架构设计</li>
</ul>
<h3 id="书籍推荐">书籍推荐</h3>
<ul>
<li>《RISC-V 读者指南》</li>
<li>《数字设计与计算机架构：RISC-V 版》</li>
</ul>
<h2 id="未来展望">未来展望</h2>
<p>RISC-V 正在快速发展：</p>
<ul>
<li><strong>AI 扩展</strong>：向量指令、矩阵运算</li>
<li><strong>汽车应用</strong>：功能安全认证（ISO 26262）</li>
<li><strong>服务器领域</strong>：多核高性能处理器</li>
<li><strong>生态系统</strong>：Linux、Android 支持完善</li>
</ul>
<h2 id="结语">结语</h2>
<p>RISC-V 代表了处理器设计的未来方向——开放、协作、创新。无论是学生、工程师还是企业，都可以在这个开放生态中找到机会。</p>
<hr>
<p><em>参考资料：RISC-V International、GitHub riscv/learn、SiFive 技术文档</em></p>
]]></content:encoded></item><item><title>STM32 GPIO 编程完全指南</title><link>https://tech-snippets.xyz/posts/stm32-gpio-programming/</link><pubDate>Thu, 26 Mar 2026 03:00:00 +0800</pubDate><guid>https://tech-snippets.xyz/posts/stm32-gpio-programming/</guid><description>深入理解 STM32 GPIO 工作原理，掌握输入输出、中断和高级功能的编程方法</description><content:encoded><![CDATA[<h2 id="stm32-gpio-基础">STM32 GPIO 基础</h2>
<p>GPIO（General Purpose Input/Output）是微控制器最基本的外设。STM32 的 GPIO 功能强大，支持多种模式和配置。</p>
<h3 id="gpio-引脚特性">GPIO 引脚特性</h3>
<ul>
<li><strong>多种模式</strong>：输入、输出、复用、模拟</li>
<li><strong>速度配置</strong>：2MHz 到 200MHz+</li>
<li><strong>上下拉电阻</strong>：内置可配置</li>
<li><strong>驱动能力</strong>：可配置输出强度</li>
<li><strong>中断支持</strong>：外部中断/事件</li>
</ul>
<h2 id="gpio-工作模式">GPIO 工作模式</h2>
<h3 id="1-输入模式">1. 输入模式</h3>
<table>
<thead>
<tr>
<th>模式</th>
<th>说明</th>
<th>应用</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>浮空输入</strong></td>
<td>无上拉下拉</td>
<td>按键（外部有电阻）</td>
</tr>
<tr>
<td><strong>上拉输入</strong></td>
<td>内置上拉电阻</td>
<td>按键（默认高电平）</td>
</tr>
<tr>
<td><strong>下拉输入</strong></td>
<td>内置下拉电阻</td>
<td>按键（默认低电平）</td>
</tr>
<tr>
<td><strong>模拟输入</strong></td>
<td>ADC 采集</td>
<td>传感器、电位器</td>
</tr>
</tbody>
</table>
<h3 id="2-输出模式">2. 输出模式</h3>
<table>
<thead>
<tr>
<th>模式</th>
<th>说明</th>
<th>应用</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>推挽输出</strong></td>
<td>高低电平驱动</td>
<td>LED、继电器</td>
</tr>
<tr>
<td><strong>开漏输出</strong></td>
<td>需要上拉电阻</td>
<td>I2C、电平转换</td>
</tr>
</tbody>
</table>
<h2 id="寄存器编程裸机">寄存器编程（裸机）</h2>
<h3 id="gpio-寄存器">GPIO 寄存器</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// GPIO 寄存器结构
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">volatile</span> <span class="kt">uint32_t</span> <span class="n">MODER</span><span class="p">;</span>    <span class="c1">// 模式寄存器
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">volatile</span> <span class="kt">uint32_t</span> <span class="n">OTYPER</span><span class="p">;</span>   <span class="c1">// 输出类型
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">volatile</span> <span class="kt">uint32_t</span> <span class="n">OSPEEDR</span><span class="p">;</span>  <span class="c1">// 输出速度
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">volatile</span> <span class="kt">uint32_t</span> <span class="n">PUPDR</span><span class="p">;</span>    <span class="c1">// 上下拉
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">volatile</span> <span class="kt">uint32_t</span> <span class="n">IDR</span><span class="p">;</span>      <span class="c1">// 输入数据
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">volatile</span> <span class="kt">uint32_t</span> <span class="n">ODR</span><span class="p">;</span>      <span class="c1">// 输出数据
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">volatile</span> <span class="kt">uint32_t</span> <span class="n">BSRR</span><span class="p">;</span>     <span class="c1">// 置位/复位
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">volatile</span> <span class="kt">uint32_t</span> <span class="n">LCKR</span><span class="p">;</span>     <span class="c1">// 锁定
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">volatile</span> <span class="kt">uint32_t</span> <span class="n">AFR</span><span class="p">[</span><span class="mi">2</span><span class="p">];</span>   <span class="c1">// 复用功能
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span> <span class="n">GPIO_TypeDef</span><span class="p">;</span>
</span></span></code></pre></div><h3 id="led-控制示例">LED 控制示例</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// 配置 PA5 为推挽输出
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="nf">gpio_init</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 1. 使能 GPIOA 时钟
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">RCC</span><span class="o">-&gt;</span><span class="n">AHB1ENR</span> <span class="o">|=</span> <span class="n">RCC_AHB1ENR_GPIOAEN</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 2. 配置 PA5 为输出模式 (01)
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">GPIOA</span><span class="o">-&gt;</span><span class="n">MODER</span> <span class="o">&amp;=</span> <span class="o">~</span><span class="p">(</span><span class="mi">3</span> <span class="o">&lt;&lt;</span> <span class="mi">10</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">GPIOA</span><span class="o">-&gt;</span><span class="n">MODER</span> <span class="o">|=</span> <span class="p">(</span><span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="mi">10</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 3. 配置推挽输出
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">GPIOA</span><span class="o">-&gt;</span><span class="n">OTYPER</span> <span class="o">&amp;=</span> <span class="o">~</span><span class="p">(</span><span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="mi">5</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 4. 配置高速
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">GPIOA</span><span class="o">-&gt;</span><span class="n">OSPEEDR</span> <span class="o">|=</span> <span class="p">(</span><span class="mi">3</span> <span class="o">&lt;&lt;</span> <span class="mi">10</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 5. 无上下拉
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">GPIOA</span><span class="o">-&gt;</span><span class="n">PUPDR</span> <span class="o">&amp;=</span> <span class="o">~</span><span class="p">(</span><span class="mi">3</span> <span class="o">&lt;&lt;</span> <span class="mi">10</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// LED 开关
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="nf">led_on</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">GPIOA</span><span class="o">-&gt;</span><span class="n">BSRR</span> <span class="o">=</span> <span class="p">(</span><span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="mi">5</span><span class="p">);</span>  <span class="c1">// 置位
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">led_off</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">GPIOA</span><span class="o">-&gt;</span><span class="n">BSRR</span> <span class="o">=</span> <span class="p">(</span><span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="mi">21</span><span class="p">);</span> <span class="c1">// 复位 (5+16=21)
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">led_toggle</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">GPIOA</span><span class="o">-&gt;</span><span class="n">ODR</span> <span class="o">^=</span> <span class="p">(</span><span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="mi">5</span><span class="p">);</span>  <span class="c1">// 翻转
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span></code></pre></div><h2 id="hal-库编程">HAL 库编程</h2>
<h3 id="初始化代码">初始化代码</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&#34;stm32f4xx_hal.h&#34;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="n">GPIO_InitTypeDef</span> <span class="n">GPIO_InitStruct</span> <span class="o">=</span> <span class="p">{</span><span class="mi">0</span><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">MX_GPIO_Init</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// GPIO 时钟使能
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">__HAL_RCC_GPIOA_CLK_ENABLE</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 配置 PA5
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">GPIO_InitStruct</span><span class="p">.</span><span class="n">Pin</span> <span class="o">=</span> <span class="n">GPIO_PIN_5</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">GPIO_InitStruct</span><span class="p">.</span><span class="n">Mode</span> <span class="o">=</span> <span class="n">GPIO_MODE_OUTPUT_PP</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">GPIO_InitStruct</span><span class="p">.</span><span class="n">Pull</span> <span class="o">=</span> <span class="n">GPIO_NOPULL</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">GPIO_InitStruct</span><span class="p">.</span><span class="n">Speed</span> <span class="o">=</span> <span class="n">GPIO_SPEED_FREQ_HIGH</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nf">HAL_GPIO_Init</span><span class="p">(</span><span class="n">GPIOA</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">GPIO_InitStruct</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 使用
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nf">HAL_GPIO_WritePin</span><span class="p">(</span><span class="n">GPIOA</span><span class="p">,</span> <span class="n">GPIO_PIN_5</span><span class="p">,</span> <span class="n">GPIO_PIN_SET</span><span class="p">);</span>   <span class="c1">// 开
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nf">HAL_GPIO_WritePin</span><span class="p">(</span><span class="n">GPIOA</span><span class="p">,</span> <span class="n">GPIO_PIN_5</span><span class="p">,</span> <span class="n">GPIO_PIN_RESET</span><span class="p">);</span> <span class="c1">// 关
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nf">HAL_GPIO_TogglePin</span><span class="p">(</span><span class="n">GPIOA</span><span class="p">,</span> <span class="n">GPIO_PIN_5</span><span class="p">);</span>                <span class="c1">// 翻转
</span></span></span></code></pre></div><h2 id="gpio-中断">GPIO 中断</h2>
<h3 id="外部中断配置">外部中断配置</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// 配置 PA0 为中断输入
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="nf">gpio_interrupt_init</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 1. 使能时钟
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">__HAL_RCC_GPIOA_CLK_ENABLE</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="nf">__HAL_RCC_SYSCFG_CLK_ENABLE</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="nf">__HAL_RCC_GPIO_EXTI_CLK_ENABLE</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 2. 配置 PA0 为上拉输入
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">GPIO_InitStruct</span><span class="p">.</span><span class="n">Pin</span> <span class="o">=</span> <span class="n">GPIO_PIN_0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">GPIO_InitStruct</span><span class="p">.</span><span class="n">Mode</span> <span class="o">=</span> <span class="n">GPIO_MODE_IT_FALLING</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">GPIO_InitStruct</span><span class="p">.</span><span class="n">Pull</span> <span class="o">=</span> <span class="n">GPIO_PULLUP</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nf">HAL_GPIO_Init</span><span class="p">(</span><span class="n">GPIOA</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">GPIO_InitStruct</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 3. 配置 NVIC
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">HAL_NVIC_SetPriority</span><span class="p">(</span><span class="n">EXTI0_IRQn</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nf">HAL_NVIC_EnableIRQ</span><span class="p">(</span><span class="n">EXTI0_IRQn</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 中断处理函数
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="nf">EXTI0_IRQHandler</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nf">HAL_GPIO_EXTI_IRQHandler</span><span class="p">(</span><span class="n">GPIO_PIN_0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 回调函数
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="nf">HAL_GPIO_EXTI_Callback</span><span class="p">(</span><span class="kt">uint16_t</span> <span class="n">GPIO_Pin</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">GPIO_Pin</span> <span class="o">==</span> <span class="n">GPIO_PIN_0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// 处理中断
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="nf">led_toggle</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h2 id="高级功能">高级功能</h2>
<h3 id="复用功能">复用功能</h3>
<p>GPIO 可映射到外设（UART、SPI、I2C 等）：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// 配置 PA9/PA10 为 USART1_TX/RX
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">GPIO_InitStruct</span><span class="p">.</span><span class="n">Pin</span> <span class="o">=</span> <span class="n">GPIO_PIN_9</span> <span class="o">|</span> <span class="n">GPIO_PIN_10</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="n">GPIO_InitStruct</span><span class="p">.</span><span class="n">Mode</span> <span class="o">=</span> <span class="n">GPIO_MODE_AF_PP</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="n">GPIO_InitStruct</span><span class="p">.</span><span class="n">Alternate</span> <span class="o">=</span> <span class="n">GPIO_AF7_USART1</span><span class="p">;</span>  <span class="c1">// 复用功能 7
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">GPIO_InitStruct</span><span class="p">.</span><span class="n">Speed</span> <span class="o">=</span> <span class="n">GPIO_SPEED_FREQ_HIGH</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nf">HAL_GPIO_Init</span><span class="p">(</span><span class="n">GPIOA</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">GPIO_InitStruct</span><span class="p">);</span>
</span></span></code></pre></div><h3 id="锁定功能">锁定功能</h3>
<p>防止意外修改 GPIO 配置：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// 锁定 PA5 配置
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">GPIOA</span><span class="o">-&gt;</span><span class="n">LCKR</span> <span class="o">=</span> <span class="p">(</span><span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="mi">16</span><span class="p">)</span> <span class="o">|</span> <span class="p">(</span><span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="mi">5</span><span class="p">);</span>  <span class="c1">// LCKK + LCK5
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">GPIOA</span><span class="o">-&gt;</span><span class="n">LCKR</span> <span class="o">=</span> <span class="p">(</span><span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="mi">5</span><span class="p">);</span>              <span class="c1">// LCK5
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">GPIOA</span><span class="o">-&gt;</span><span class="n">LCKR</span> <span class="o">=</span> <span class="p">(</span><span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="mi">16</span><span class="p">)</span> <span class="o">|</span> <span class="p">(</span><span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="mi">5</span><span class="p">);</span>  <span class="c1">// LCKK + LCK5
</span></span></span></code></pre></div><h2 id="最佳实践">最佳实践</h2>
<h3 id="-推荐做法">✅ 推荐做法</h3>
<ol>
<li><strong>初始化前使能时钟</strong></li>
<li><strong>使用 BSRR 代替 ODR</strong>（原子操作）</li>
<li><strong>中断中快速处理</strong>（置标志，主循环处理）</li>
<li><strong>添加去抖动</strong>（按键）</li>
<li><strong>使用 HAL 库提高可移植性</strong></li>
</ol>
<h3 id="-避免的错误">❌ 避免的错误</h3>
<ol>
<li><strong>忘记使能时钟</strong>（最常见）</li>
<li><strong>直接写 ODR 影响其他引脚</strong></li>
<li><strong>中断处理时间过长</strong></li>
<li><strong>忽略引脚电气特性</strong></li>
<li><strong>未配置上下拉导致浮空</strong></li>
</ol>
<h2 id="调试技巧">调试技巧</h2>
<h3 id="问题排查">问题排查</h3>
<table>
<thead>
<tr>
<th>问题</th>
<th>可能原因</th>
<th>解决方法</th>
</tr>
</thead>
<tbody>
<tr>
<td>引脚无输出</td>
<td>时钟未使能</td>
<td>检查 RCC 配置</td>
</tr>
<tr>
<td>电平不对</td>
<td>上下拉配置错误</td>
<td>检查 PUPDR</td>
</tr>
<tr>
<td>速度不够</td>
<td>速度配置过低</td>
<td>调整 OSPEEDR</td>
</tr>
<tr>
<td>中断不触发</td>
<td>NVIC 未配置</td>
<td>检查中断优先级</td>
</tr>
</tbody>
</table>
<h3 id="测量工具">测量工具</h3>
<ul>
<li><strong>万用表</strong>：测量电平</li>
<li><strong>示波器</strong>：观察波形</li>
<li><strong>逻辑分析仪</strong>：多通道时序分析</li>
</ul>
<h2 id="结语">结语</h2>
<p>GPIO 是 STM32 编程的基础。掌握 GPIO 编程后，你可以控制 LED、读取按键、连接传感器，为学习更复杂的外设打下基础。</p>
<hr>
<p><em>参考资料：STM32 参考手册、STM32CubeMX 用户指南、Arm Cortex-M 技术文档</em></p>
]]></content:encoded></item><item><title>ESP32 物联网开发：WiFi 与蓝牙实战</title><link>https://tech-snippets.xyz/posts/esp32-iot-wifi-bluetooth/</link><pubDate>Wed, 25 Mar 2026 03:00:00 +0800</pubDate><guid>https://tech-snippets.xyz/posts/esp32-iot-wifi-bluetooth/</guid><description>使用 ESP32 开发物联网应用，掌握 WiFi 连接、蓝牙通信和低功耗设计</description><content:encoded><![CDATA[<h2 id="为什么选择-esp32">为什么选择 ESP32？</h2>
<p>ESP32 是乐鑫推出的高性能低功耗微控制器，集成了 WiFi 和蓝牙功能，是物联网开发的理想选择。</p>
<h3 id="esp32-核心特性">ESP32 核心特性</h3>
<table>
<thead>
<tr>
<th>特性</th>
<th>参数</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>CPU</strong></td>
<td>双核 Xtensa LX6，240MHz</td>
</tr>
<tr>
<td><strong>内存</strong></td>
<td>520KB SRAM</td>
</tr>
<tr>
<td><strong>Flash</strong></td>
<td>4MB-16MB</td>
</tr>
<tr>
<td><strong>WiFi</strong></td>
<td>802.11 b/g/n，Station/AP 模式</td>
</tr>
<tr>
<td><strong>蓝牙</strong></td>
<td>Bluetooth 4.2 BR/EDR + BLE</td>
</tr>
<tr>
<td><strong>GPIO</strong></td>
<td>34 个可编程 GPIO</td>
</tr>
<tr>
<td><strong>外设</strong></td>
<td>ADC、DAC、SPI、I2C、UART</td>
</tr>
<tr>
<td><strong>功耗</strong></td>
<td>深度睡眠 10μA</td>
</tr>
</tbody>
</table>
<h2 id="开发环境搭建">开发环境搭建</h2>
<h3 id="arduino-ide-方式">Arduino IDE 方式</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="c1">// 1. 添加 ESP32 板卡支持
</span></span></span><span class="line"><span class="cl"><span class="c1">// 文件 -&gt; 首选项 -&gt; 附加开发板管理器 URL
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nl">https</span><span class="p">:</span><span class="c1">//raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="c1">// 2. 安装 ESP32 包
</span></span></span><span class="line"><span class="cl"><span class="c1">// 工具 -&gt; 开发板 -&gt; 开发板管理器 -&gt; 搜索 ESP32 -&gt; 安装
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="c1">// 3. 选择开发板
</span></span></span><span class="line"><span class="cl"><span class="c1">// 工具 -&gt; 开发板 -&gt; ESP32 Arduino -&gt; DOIT ESP32 DEVKIT V1
</span></span></span></code></pre></div><h3 id="platformio-方式">PlatformIO 方式</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ini" data-lang="ini"><span class="line"><span class="cl"><span class="c1">; platformio.ini</span>
</span></span><span class="line"><span class="cl"><span class="k">[env:esp32dev]</span>
</span></span><span class="line"><span class="cl"><span class="na">platform</span> <span class="o">=</span> <span class="s">espressif32</span>
</span></span><span class="line"><span class="cl"><span class="na">board</span> <span class="o">=</span> <span class="s">esp32dev</span>
</span></span><span class="line"><span class="cl"><span class="na">framework</span> <span class="o">=</span> <span class="s">arduino</span>
</span></span><span class="line"><span class="cl"><span class="na">monitor_speed</span> <span class="o">=</span> <span class="s">115200</span>
</span></span></code></pre></div><h2 id="wifi-连接">WiFi 连接</h2>
<h3 id="station-模式客户端">Station 模式（客户端）</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;WiFi.h&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="k">const</span> <span class="kt">char</span><span class="o">*</span> <span class="n">ssid</span> <span class="o">=</span> <span class="s">&#34;YourWiFi&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">const</span> <span class="kt">char</span><span class="o">*</span> <span class="n">password</span> <span class="o">=</span> <span class="s">&#34;YourPassword&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">setup</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">Serial</span><span class="p">.</span><span class="n">begin</span><span class="p">(</span><span class="mi">115200</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 连接 WiFi
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">WiFi</span><span class="p">.</span><span class="n">begin</span><span class="p">(</span><span class="n">ssid</span><span class="p">,</span> <span class="n">password</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="n">Serial</span><span class="p">.</span><span class="n">print</span><span class="p">(</span><span class="s">&#34;Connecting to WiFi&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">while</span> <span class="p">(</span><span class="n">WiFi</span><span class="p">.</span><span class="n">status</span><span class="p">()</span> <span class="o">!=</span> <span class="n">WL_CONNECTED</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">delay</span><span class="p">(</span><span class="mi">500</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="n">Serial</span><span class="p">.</span><span class="n">print</span><span class="p">(</span><span class="s">&#34;.&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="n">Serial</span><span class="p">.</span><span class="n">println</span><span class="p">(</span><span class="s">&#34;</span><span class="se">\n</span><span class="s">WiFi connected!&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">Serial</span><span class="p">.</span><span class="n">print</span><span class="p">(</span><span class="s">&#34;IP Address: &#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">Serial</span><span class="p">.</span><span class="n">println</span><span class="p">(</span><span class="n">WiFi</span><span class="p">.</span><span class="n">localIP</span><span class="p">());</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">loop</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 检查连接状态
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">if</span> <span class="p">(</span><span class="n">WiFi</span><span class="p">.</span><span class="n">status</span><span class="p">()</span> <span class="o">!=</span> <span class="n">WL_CONNECTED</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">Serial</span><span class="p">.</span><span class="n">println</span><span class="p">(</span><span class="s">&#34;WiFi disconnected!&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="n">WiFi</span><span class="p">.</span><span class="n">reconnect</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="n">delay</span><span class="p">(</span><span class="mi">1000</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="ap-模式热点">AP 模式（热点）</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;WiFi.h&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">setup</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 创建热点
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">WiFi</span><span class="p">.</span><span class="n">softAP</span><span class="p">(</span><span class="s">&#34;ESP32_Hotspot&#34;</span><span class="p">,</span> <span class="s">&#34;password123&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="n">Serial</span><span class="p">.</span><span class="n">print</span><span class="p">(</span><span class="s">&#34;AP IP Address: &#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">Serial</span><span class="p">.</span><span class="n">println</span><span class="p">(</span><span class="n">WiFi</span><span class="p">.</span><span class="n">softAPIP</span><span class="p">());</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h2 id="http-请求">HTTP 请求</h2>
<h3 id="get-请求">GET 请求</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;WiFi.h&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;HTTPClient.h&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">httpGetRequest</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">WiFi</span><span class="p">.</span><span class="n">status</span><span class="p">()</span> <span class="o">==</span> <span class="n">WL_CONNECTED</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">HTTPClient</span> <span class="n">http</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="n">http</span><span class="p">.</span><span class="n">begin</span><span class="p">(</span><span class="s">&#34;http://api.example.com/data&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="kt">int</span> <span class="n">httpCode</span> <span class="o">=</span> <span class="n">http</span><span class="p">.</span><span class="n">GET</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">httpCode</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">String</span> <span class="n">payload</span> <span class="o">=</span> <span class="n">http</span><span class="p">.</span><span class="n">getString</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">            <span class="n">Serial</span><span class="p">.</span><span class="n">println</span><span class="p">(</span><span class="n">payload</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="n">http</span><span class="p">.</span><span class="n">end</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="post-请求">POST 请求</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">httpPostRequest</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">WiFi</span><span class="p">.</span><span class="n">status</span><span class="p">()</span> <span class="o">==</span> <span class="n">WL_CONNECTED</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">HTTPClient</span> <span class="n">http</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="n">http</span><span class="p">.</span><span class="n">begin</span><span class="p">(</span><span class="s">&#34;http://api.example.com/submit&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="n">http</span><span class="p">.</span><span class="n">addHeader</span><span class="p">(</span><span class="s">&#34;Content-Type&#34;</span><span class="p">,</span> <span class="s">&#34;application/json&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="n">String</span> <span class="n">jsonData</span> <span class="o">=</span> <span class="s">&#34;{</span><span class="se">\&#34;</span><span class="s">temperature</span><span class="se">\&#34;</span><span class="s">: 25.5, </span><span class="se">\&#34;</span><span class="s">humidity</span><span class="se">\&#34;</span><span class="s">: 60}&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="kt">int</span> <span class="n">httpCode</span> <span class="o">=</span> <span class="n">http</span><span class="p">.</span><span class="n">POST</span><span class="p">(</span><span class="n">jsonData</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="n">Serial</span><span class="p">.</span><span class="n">println</span><span class="p">(</span><span class="n">http</span><span class="p">.</span><span class="n">getString</span><span class="p">());</span>
</span></span><span class="line"><span class="cl">        <span class="n">http</span><span class="p">.</span><span class="n">end</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h2 id="蓝牙-ble-通信">蓝牙 BLE 通信</h2>
<h3 id="ble-服务器">BLE 服务器</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;BLEDevice.h&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;BLEServer.h&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;BLEUtils.h&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;BLE2902.h&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="c1">// BLE UUID
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#define SERVICE_UUID        &#34;4fafc201-1fb5-459e-8fcc-c5c9c331914b&#34;
</span></span></span><span class="line"><span class="cl"><span class="cp">#define CHARACTERISTIC_UUID &#34;beb5483e-36e1-4688-b7f5-ea07361b26a8&#34;
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="n">BLEServer</span><span class="o">*</span> <span class="n">pServer</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="n">BLECharacteristic</span><span class="o">*</span> <span class="n">pCharacteristic</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">MyCallbacks</span><span class="o">:</span> <span class="k">public</span> <span class="n">BLECharacteristicCallbacks</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">void</span> <span class="nf">onWrite</span><span class="p">(</span><span class="n">BLECharacteristic</span> <span class="o">*</span><span class="n">pCharacteristic</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">value</span> <span class="o">=</span> <span class="n">pCharacteristic</span><span class="o">-&gt;</span><span class="n">getValue</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">        <span class="n">Serial</span><span class="p">.</span><span class="n">print</span><span class="p">(</span><span class="s">&#34;Received: &#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="n">Serial</span><span class="p">.</span><span class="n">println</span><span class="p">(</span><span class="n">value</span><span class="p">.</span><span class="n">c_str</span><span class="p">());</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">setup</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">Serial</span><span class="p">.</span><span class="n">begin</span><span class="p">(</span><span class="mi">115200</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 创建 BLE 设备
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">BLEDevice</span><span class="o">::</span><span class="n">init</span><span class="p">(</span><span class="s">&#34;ESP32_BLE_Server&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 创建服务
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">pServer</span> <span class="o">=</span> <span class="n">BLEDevice</span><span class="o">::</span><span class="n">createServer</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="n">BLEService</span> <span class="o">*</span><span class="n">pService</span> <span class="o">=</span> <span class="n">pServer</span><span class="o">-&gt;</span><span class="n">createService</span><span class="p">(</span><span class="n">SERVICE_UUID</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 创建特征
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">pCharacteristic</span> <span class="o">=</span> <span class="n">pService</span><span class="o">-&gt;</span><span class="n">createCharacteristic</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="n">CHARACTERISTIC_UUID</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">BLECharacteristic</span><span class="o">::</span><span class="n">PROPERTY_READ</span> <span class="o">|</span>
</span></span><span class="line"><span class="cl">        <span class="n">BLECharacteristic</span><span class="o">::</span><span class="n">PROPERTY_WRITE</span> <span class="o">|</span>
</span></span><span class="line"><span class="cl">        <span class="n">BLECharacteristic</span><span class="o">::</span><span class="n">PROPERTY_NOTIFY</span>
</span></span><span class="line"><span class="cl">    <span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="n">pCharacteristic</span><span class="o">-&gt;</span><span class="n">addDescriptor</span><span class="p">(</span><span class="k">new</span> <span class="n">BLE2902</span><span class="p">());</span>
</span></span><span class="line"><span class="cl">    <span class="n">pCharacteristic</span><span class="o">-&gt;</span><span class="n">setCallbacks</span><span class="p">(</span><span class="k">new</span> <span class="n">MyCallbacks</span><span class="p">());</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 启动服务
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">pService</span><span class="o">-&gt;</span><span class="n">start</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 开始广播
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">BLEAdvertising</span> <span class="o">*</span><span class="n">pAdvertising</span> <span class="o">=</span> <span class="n">BLEDevice</span><span class="o">::</span><span class="n">getAdvertising</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="n">pAdvertising</span><span class="o">-&gt;</span><span class="n">addServiceUUID</span><span class="p">(</span><span class="n">SERVICE_UUID</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">pAdvertising</span><span class="o">-&gt;</span><span class="n">start</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="n">Serial</span><span class="p">.</span><span class="n">println</span><span class="p">(</span><span class="s">&#34;BLE Server started!&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">loop</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 发送通知
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">static</span> <span class="kt">uint32_t</span> <span class="n">counter</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">char</span> <span class="n">data</span><span class="p">[</span><span class="mi">32</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">    <span class="n">sprintf</span><span class="p">(</span><span class="n">data</span><span class="p">,</span> <span class="s">&#34;Counter: %lu&#34;</span><span class="p">,</span> <span class="n">counter</span><span class="o">++</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">pCharacteristic</span><span class="o">-&gt;</span><span class="n">setValue</span><span class="p">(</span><span class="n">data</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">pCharacteristic</span><span class="o">-&gt;</span><span class="n">notify</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="n">delay</span><span class="p">(</span><span class="mi">1000</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="ble-客户端">BLE 客户端</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;BLEDevice.h&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;BLEScan.h&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;BLEAdvertisedDevice.h&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">MyAdvertisedDeviceCallbacks</span><span class="o">:</span> <span class="k">public</span> <span class="n">BLEAdvertisedDeviceCallbacks</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">void</span> <span class="nf">onResult</span><span class="p">(</span><span class="n">BLEAdvertisedDevice</span> <span class="n">advertisedDevice</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">Serial</span><span class="p">.</span><span class="n">printf</span><span class="p">(</span><span class="s">&#34;Found Device: %s</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">,</span> 
</span></span><span class="line"><span class="cl">                      <span class="n">advertisedDevice</span><span class="p">.</span><span class="n">toString</span><span class="p">().</span><span class="n">c_str</span><span class="p">());</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">setup</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">BLEDevice</span><span class="o">::</span><span class="n">init</span><span class="p">(</span><span class="s">&#34;&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="n">BLEScan</span><span class="o">*</span> <span class="n">pBLEScan</span> <span class="o">=</span> <span class="n">BLEDevice</span><span class="o">::</span><span class="n">getScan</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="n">pBLEScan</span><span class="o">-&gt;</span><span class="n">setAdvertisedDeviceCallbacks</span><span class="p">(</span><span class="k">new</span> <span class="n">MyAdvertisedDeviceCallbacks</span><span class="p">());</span>
</span></span><span class="line"><span class="cl">    <span class="n">pBLEScan</span><span class="o">-&gt;</span><span class="n">setInterval</span><span class="p">(</span><span class="mi">1349</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">pBLEScan</span><span class="o">-&gt;</span><span class="n">setWindow</span><span class="p">(</span><span class="mi">449</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">pBLEScan</span><span class="o">-&gt;</span><span class="n">setActiveScan</span><span class="p">(</span><span class="nb">true</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="n">BLEScanResults</span> <span class="n">foundDevices</span> <span class="o">=</span> <span class="n">pBLEScan</span><span class="o">-&gt;</span><span class="n">start</span><span class="p">(</span><span class="mi">5</span><span class="p">,</span> <span class="nb">false</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">Serial</span><span class="p">.</span><span class="n">printf</span><span class="p">(</span><span class="s">&#34;Found %d devices</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">,</span> <span class="n">foundDevices</span><span class="p">.</span><span class="n">getCount</span><span class="p">());</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h2 id="低功耗设计">低功耗设计</h2>
<h3 id="睡眠模式">睡眠模式</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="c1">// 深度睡眠（10μA）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="nf">enterDeepSleep</span><span class="p">(</span><span class="kt">uint64_t</span> <span class="n">sleepTimeUs</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 配置唤醒源（例如 GPIO）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">esp_sleep_enable_ext0_wakeup</span><span class="p">(</span><span class="n">GPIO_NUM_33</span><span class="p">,</span> <span class="mi">1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 进入深度睡眠
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">esp_deep_sleep</span><span class="p">(</span><span class="n">sleepTimeUs</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 轻睡眠（~1mA）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="nf">enterLightSleep</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">esp_sleep_enable_timer_wakeup</span><span class="p">(</span><span class="mi">1000000</span><span class="p">);</span> <span class="c1">// 1 秒
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">esp_light_sleep_start</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 唤醒后检查原因
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">esp_sleep_wakeup_cause_t</span> <span class="n">wakeupReason</span> <span class="o">=</span> <span class="n">esp_sleep_get_wakeup_cause</span><span class="p">();</span>
</span></span></code></pre></div><h2 id="实际项目智能温湿度计">实际项目：智能温湿度计</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;WiFi.h&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;HTTPClient.h&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;DHT.h&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="cp">#define DHTPIN 4
</span></span></span><span class="line"><span class="cl"><span class="cp">#define DHTTYPE DHT11
</span></span></span><span class="line"><span class="cl"><span class="cp"></span><span class="n">DHT</span> <span class="nf">dht</span><span class="p">(</span><span class="n">DHTPIN</span><span class="p">,</span> <span class="n">DHTTYPE</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">const</span> <span class="kt">char</span><span class="o">*</span> <span class="n">ssid</span> <span class="o">=</span> <span class="s">&#34;YourWiFi&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">const</span> <span class="kt">char</span><span class="o">*</span> <span class="n">password</span> <span class="o">=</span> <span class="s">&#34;YourPassword&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">const</span> <span class="kt">char</span><span class="o">*</span> <span class="n">serverUrl</span> <span class="o">=</span> <span class="s">&#34;http://api.example.com/sensor&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">setup</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">Serial</span><span class="p">.</span><span class="n">begin</span><span class="p">(</span><span class="mi">115200</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">dht</span><span class="p">.</span><span class="n">begin</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="n">WiFi</span><span class="p">.</span><span class="n">begin</span><span class="p">(</span><span class="n">ssid</span><span class="p">,</span> <span class="n">password</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">while</span> <span class="p">(</span><span class="n">WiFi</span><span class="p">.</span><span class="n">status</span><span class="p">()</span> <span class="o">!=</span> <span class="n">WL_CONNECTED</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">delay</span><span class="p">(</span><span class="mi">500</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">loop</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">float</span> <span class="n">temp</span> <span class="o">=</span> <span class="n">dht</span><span class="p">.</span><span class="n">readTemperature</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="kt">float</span> <span class="n">hum</span> <span class="o">=</span> <span class="n">dht</span><span class="p">.</span><span class="n">readHumidity</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">WiFi</span><span class="p">.</span><span class="n">status</span><span class="p">()</span> <span class="o">==</span> <span class="n">WL_CONNECTED</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">HTTPClient</span> <span class="n">http</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">http</span><span class="p">.</span><span class="n">begin</span><span class="p">(</span><span class="n">serverUrl</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="n">http</span><span class="p">.</span><span class="n">addHeader</span><span class="p">(</span><span class="s">&#34;Content-Type&#34;</span><span class="p">,</span> <span class="s">&#34;application/json&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="n">String</span> <span class="n">jsonData</span> <span class="o">=</span> <span class="n">String</span><span class="p">(</span><span class="s">&#34;{</span><span class="se">\&#34;</span><span class="s">temp</span><span class="se">\&#34;</span><span class="s">:&#34;</span><span class="p">)</span> <span class="o">+</span> <span class="n">temp</span> <span class="o">+</span> 
</span></span><span class="line"><span class="cl">                          <span class="s">&#34;,</span><span class="se">\&#34;</span><span class="s">hum</span><span class="se">\&#34;</span><span class="s">:&#34;</span> <span class="o">+</span> <span class="n">hum</span> <span class="o">+</span> <span class="s">&#34;}&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">http</span><span class="p">.</span><span class="n">POST</span><span class="p">(</span><span class="n">jsonData</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="n">http</span><span class="p">.</span><span class="n">end</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 深度睡眠 10 分钟
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">esp_deep_sleep</span><span class="p">(</span><span class="mi">600000000</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h2 id="结语">结语</h2>
<p>ESP32 是物联网开发的强大工具。通过本教程，你掌握了 WiFi 连接、HTTP 通信、BLE 和低功耗设计。动手做一个 IoT 项目吧！</p>
<hr>
<p><em>参考资料：ESP32 技术参考手册、乐鑫官方文档、Arduino ESP32 库</em></p>
]]></content:encoded></item><item><title>实时操作系统（RTOS）基础与 FreeRTOS 实战</title><link>https://tech-snippets.xyz/posts/rtos-fundamentals-freertos/</link><pubDate>Tue, 24 Mar 2026 03:00:00 +0800</pubDate><guid>https://tech-snippets.xyz/posts/rtos-fundamentals-freertos/</guid><description>理解实时操作系统原理，掌握 FreeRTOS 多任务编程技巧</description><content:encoded><![CDATA[<h2 id="什么是-rtos">什么是 RTOS？</h2>
<p>RTOS（Real-Time Operating System）实时操作系统是一种能够保证在确定时间内完成任务调度的操作系统。</p>
<h3 id="rtos-vs-通用-os">RTOS vs 通用 OS</h3>
<table>
<thead>
<tr>
<th>特性</th>
<th>RTOS</th>
<th>通用 OS（Windows/Linux）</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>实时性</strong></td>
<td>确定性响应</td>
<td>非确定性</td>
</tr>
<tr>
<td><strong>任务调度</strong></td>
<td>优先级驱动</td>
<td>时间片轮转</td>
</tr>
<tr>
<td><strong>内存占用</strong></td>
<td>KB 级别</td>
<td>MB/GB 级别</td>
</tr>
<tr>
<td><strong>应用场景</strong></td>
<td>嵌入式、工业控制</td>
<td>桌面、服务器</td>
</tr>
</tbody>
</table>
<h3 id="硬实时-vs-软实时">硬实时 vs 软实时</h3>
<ul>
<li><strong>硬实时</strong>：必须在规定时间内完成（如汽车安全气囊）</li>
<li><strong>软实时</strong>：尽量在规定时间内完成（如视频播放）</li>
</ul>
<h2 id="freertos-基础">FreeRTOS 基础</h2>
<h3 id="核心概念">核心概念</h3>
<table>
<thead>
<tr>
<th>概念</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>任务（Task）</strong></td>
<td>独立的执行线程</td>
</tr>
<tr>
<td><strong>调度器（Scheduler）</strong></td>
<td>管理任务执行顺序</td>
</tr>
<tr>
<td><strong>优先级（Priority）</strong></td>
<td>决定任务执行顺序</td>
</tr>
<tr>
<td><strong>队列（Queue）</strong></td>
<td>任务间通信机制</td>
</tr>
<tr>
<td><strong>信号量（Semaphore）</strong></td>
<td>资源同步机制</td>
</tr>
<tr>
<td><strong>互斥量（Mutex）</strong></td>
<td>保护共享资源</td>
</tr>
</tbody>
</table>
<h2 id="任务管理">任务管理</h2>
<h3 id="创建任务">创建任务</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&#34;FreeRTOS.h&#34;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&#34;task.h&#34;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="c1">// 任务函数
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="nf">vTaskFunction</span><span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="n">pvParameters</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">char</span> <span class="o">*</span><span class="n">pcTaskName</span> <span class="o">=</span> <span class="p">(</span><span class="kt">char</span> <span class="o">*</span><span class="p">)</span><span class="n">pvParameters</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="p">(;;)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nf">printf</span><span class="p">(</span><span class="s">&#34;%s is running</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">,</span> <span class="n">pcTaskName</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="nf">vTaskDelay</span><span class="p">(</span><span class="nf">pdMS_TO_TICKS</span><span class="p">(</span><span class="mi">1000</span><span class="p">));</span> <span class="c1">// 延时 1 秒
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 创建任务
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="nf">vStartTasks</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nf">xTaskCreate</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="n">vTaskFunction</span><span class="p">,</span>           <span class="c1">// 任务函数
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="s">&#34;Task1&#34;</span><span class="p">,</span>                 <span class="c1">// 任务名称
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="n">configMINIMAL_STACK_SIZE</span><span class="p">,</span><span class="c1">// 栈大小
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="p">)</span><span class="s">&#34;Task1&#34;</span><span class="p">,</span>         <span class="c1">// 参数
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="mi">1</span><span class="p">,</span>                       <span class="c1">// 优先级
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="nb">NULL</span>                     <span class="c1">// 任务句柄
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="任务优先级">任务优先级</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// 优先级数字越大，优先级越高
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#define TASK_PRIORITY_HIGH    3
</span></span></span><span class="line"><span class="cl"><span class="cp">#define TASK_PRIORITY_NORMAL  2
</span></span></span><span class="line"><span class="cl"><span class="cp">#define TASK_PRIORITY_LOW     1
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="nf">xTaskCreate</span><span class="p">(</span><span class="n">vTask1</span><span class="p">,</span> <span class="s">&#34;Task1&#34;</span><span class="p">,</span> <span class="mi">128</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">,</span> <span class="n">TASK_PRIORITY_HIGH</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nf">xTaskCreate</span><span class="p">(</span><span class="n">vTask2</span><span class="p">,</span> <span class="s">&#34;Task2&#34;</span><span class="p">,</span> <span class="mi">128</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">,</span> <span class="n">TASK_PRIORITY_LOW</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">);</span>
</span></span></code></pre></div><h3 id="删除任务">删除任务</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// 删除自己
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nf">vTaskDelete</span><span class="p">(</span><span class="nb">NULL</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 删除其他任务
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nf">vTaskDelete</span><span class="p">(</span><span class="n">xTaskHandle</span><span class="p">);</span>
</span></span></code></pre></div><h2 id="延时与超时">延时与超时</h2>
<h3 id="阻塞延时">阻塞延时</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// 延时 500ms
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nf">vTaskDelay</span><span class="p">(</span><span class="nf">pdMS_TO_TICKS</span><span class="p">(</span><span class="mi">500</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 相对延时（从当前时刻开始）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nf">vTaskDelayUntil</span><span class="p">(</span><span class="o">&amp;</span><span class="n">xLastWakeTime</span><span class="p">,</span> <span class="nf">pdMS_TO_TICKS</span><span class="p">(</span><span class="mi">100</span><span class="p">));</span>
</span></span></code></pre></div><h3 id="精确延时">精确延时</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="n">TickType_t</span> <span class="n">xLastWakeTime</span> <span class="o">=</span> <span class="nf">xTaskGetTickCount</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">vTaskFunction</span><span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="n">pvParameters</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="p">(;;)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// 执行任务
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        
</span></span><span class="line"><span class="cl">        <span class="c1">// 精确延时 100ms
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="nf">vTaskDelayUntil</span><span class="p">(</span><span class="o">&amp;</span><span class="n">xLastWakeTime</span><span class="p">,</span> <span class="nf">pdMS_TO_TICKS</span><span class="p">(</span><span class="mi">100</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h2 id="任务间通信">任务间通信</h2>
<h3 id="队列queue">队列（Queue）</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&#34;queue.h&#34;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="c1">// 创建队列
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">QueueHandle_t</span> <span class="n">xQueue</span> <span class="o">=</span> <span class="nf">xQueueCreate</span><span class="p">(</span><span class="mi">10</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="kt">int</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 发送数据（阻塞等待）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">int</span> <span class="n">data</span> <span class="o">=</span> <span class="mi">42</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nf">xQueueSend</span><span class="p">(</span><span class="n">xQueue</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">data</span><span class="p">,</span> <span class="n">portMAX_DELAY</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 接收数据（阻塞等待）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">int</span> <span class="n">receivedData</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nf">xQueueReceive</span><span class="p">(</span><span class="n">xQueue</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">receivedData</span><span class="p">,</span> <span class="n">portMAX_DELAY</span><span class="p">);</span>
</span></span></code></pre></div><h3 id="队列示例生产者---消费者">队列示例：生产者 - 消费者</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="n">QueueHandle_t</span> <span class="n">xQueue</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">vProducerTask</span><span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="n">pvParameters</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="n">data</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="p">(;;)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nf">xQueueSend</span><span class="p">(</span><span class="n">xQueue</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">data</span><span class="p">,</span> <span class="n">portMAX_DELAY</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="n">data</span><span class="o">++</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="nf">vTaskDelay</span><span class="p">(</span><span class="nf">pdMS_TO_TICKS</span><span class="p">(</span><span class="mi">100</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">vConsumerTask</span><span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="n">pvParameters</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="n">receivedData</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="p">(;;)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nf">xQueueReceive</span><span class="p">(</span><span class="n">xQueue</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">receivedData</span><span class="p">,</span> <span class="n">portMAX_DELAY</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="nf">printf</span><span class="p">(</span><span class="s">&#34;Received: %d</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">,</span> <span class="n">receivedData</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">vStartSystem</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">xQueue</span> <span class="o">=</span> <span class="nf">xQueueCreate</span><span class="p">(</span><span class="mi">10</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="kt">int</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="nf">xTaskCreate</span><span class="p">(</span><span class="n">vProducerTask</span><span class="p">,</span> <span class="s">&#34;Producer&#34;</span><span class="p">,</span> <span class="mi">128</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nf">xTaskCreate</span><span class="p">(</span><span class="n">vConsumerTask</span><span class="p">,</span> <span class="s">&#34;Consumer&#34;</span><span class="p">,</span> <span class="mi">128</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="nf">vTaskStartScheduler</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h2 id="同步与互斥">同步与互斥</h2>
<h3 id="二值信号量">二值信号量</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&#34;semphr.h&#34;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="n">SemaphoreHandle_t</span> <span class="n">xBinarySemaphore</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">vInterruptHandler</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 中断中释放信号量
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">BaseType_t</span> <span class="n">xHigherPriorityTaskWoken</span> <span class="o">=</span> <span class="n">pdFALSE</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nf">xSemaphoreGiveFromISR</span><span class="p">(</span><span class="n">xBinarySemaphore</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">xHigherPriorityTaskWoken</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nf">portYIELD_FROM_ISR</span><span class="p">(</span><span class="n">xHigherPriorityTaskWoken</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">vTaskFunction</span><span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="n">pvParameters</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="p">(;;)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// 等待信号量
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="k">if</span> <span class="p">(</span><span class="nf">xSemaphoreTake</span><span class="p">(</span><span class="n">xBinarySemaphore</span><span class="p">,</span> <span class="n">portMAX_DELAY</span><span class="p">)</span> <span class="o">==</span> <span class="n">pdTRUE</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="c1">// 处理事件
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">vStartSystem</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">xBinarySemaphore</span> <span class="o">=</span> <span class="nf">xSemaphoreCreateBinary</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="nf">xTaskCreate</span><span class="p">(</span><span class="n">vTaskFunction</span><span class="p">,</span> <span class="s">&#34;Task&#34;</span><span class="p">,</span> <span class="mi">128</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nf">vTaskStartScheduler</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="互斥量mutex">互斥量（Mutex）</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="n">SemaphoreHandle_t</span> <span class="n">xMutex</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">vTask1</span><span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="n">pvParameters</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="p">(;;)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// 获取互斥量
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="k">if</span> <span class="p">(</span><span class="nf">xSemaphoreTake</span><span class="p">(</span><span class="n">xMutex</span><span class="p">,</span> <span class="n">portMAX_DELAY</span><span class="p">)</span> <span class="o">==</span> <span class="n">pdTRUE</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="c1">// 访问共享资源
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="nf">printf</span><span class="p">(</span><span class="s">&#34;Task1 accessing shared resource</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="nf">vTaskDelay</span><span class="p">(</span><span class="nf">pdMS_TO_TICKS</span><span class="p">(</span><span class="mi">100</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">            <span class="nf">xSemaphoreGive</span><span class="p">(</span><span class="n">xMutex</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="nf">vTaskDelay</span><span class="p">(</span><span class="nf">pdMS_TO_TICKS</span><span class="p">(</span><span class="mi">200</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h2 id="内存管理">内存管理</h2>
<h3 id="堆管理方案">堆管理方案</h3>
<table>
<thead>
<tr>
<th>方案</th>
<th>特点</th>
<th>适用场景</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>heap_1</strong></td>
<td>只分配，不释放</td>
<td>简单系统</td>
</tr>
<tr>
<td><strong>heap_2</strong></td>
<td>分配 + 释放，不合并</td>
<td>固定大小块</td>
</tr>
<tr>
<td><strong>heap_3</strong></td>
<td>使用 malloc/free</td>
<td>通用场景</td>
</tr>
<tr>
<td><strong>heap_4</strong></td>
<td>分配 + 释放，合并空闲块</td>
<td>推荐</td>
</tr>
<tr>
<td><strong>heap_5</strong></td>
<td>支持多个内存区域</td>
<td>复杂系统</td>
</tr>
</tbody>
</table>
<h3 id="配置示例">配置示例</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// FreeRTOSConfig.h
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#define configTOTAL_HEAP_SIZE    ((size_t)(10 * 1024))
</span></span></span><span class="line"><span class="cl"><span class="cp">#define configUSE_MALLOC_FAILED_HOOK 1
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">vApplicationMallocFailedHook</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 内存分配失败处理
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span></code></pre></div><h2 id="实际项目多传感器数据采集">实际项目：多传感器数据采集</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&#34;FreeRTOS.h&#34;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&#34;task.h&#34;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&#34;queue.h&#34;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">float</span> <span class="n">temperature</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">float</span> <span class="n">humidity</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">uint32_t</span> <span class="n">timestamp</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="n">SensorData_t</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">QueueHandle_t</span> <span class="n">xSensorQueue</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 温度采集任务
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="nf">vTempTask</span><span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="n">pvParameters</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">SensorData_t</span> <span class="n">data</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="p">(;;)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">data</span><span class="p">.</span><span class="n">temperature</span> <span class="o">=</span> <span class="nf">readTemperature</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">        <span class="n">data</span><span class="p">.</span><span class="n">timestamp</span> <span class="o">=</span> <span class="nf">xTaskGetTickCount</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">        <span class="nf">xQueueSend</span><span class="p">(</span><span class="n">xSensorQueue</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">data</span><span class="p">,</span> <span class="n">portMAX_DELAY</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="nf">vTaskDelay</span><span class="p">(</span><span class="nf">pdMS_TO_TICKS</span><span class="p">(</span><span class="mi">1000</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 湿度采集任务
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="nf">vHumidityTask</span><span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="n">pvParameters</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">SensorData_t</span> <span class="n">data</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="p">(;;)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">data</span><span class="p">.</span><span class="n">humidity</span> <span class="o">=</span> <span class="nf">readHumidity</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">        <span class="n">data</span><span class="p">.</span><span class="n">timestamp</span> <span class="o">=</span> <span class="nf">xTaskGetTickCount</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">        <span class="nf">xQueueSend</span><span class="p">(</span><span class="n">xSensorQueue</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">data</span><span class="p">,</span> <span class="n">portMAX_DELAY</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="nf">vTaskDelay</span><span class="p">(</span><span class="nf">pdMS_TO_TICKS</span><span class="p">(</span><span class="mi">1000</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 数据处理任务
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="nf">vProcessTask</span><span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="n">pvParameters</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">SensorData_t</span> <span class="n">data</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="p">(;;)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nf">xQueueReceive</span><span class="p">(</span><span class="n">xSensorQueue</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">data</span><span class="p">,</span> <span class="n">portMAX_DELAY</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="nf">printf</span><span class="p">(</span><span class="s">&#34;Temp: %.2f, Hum: %.2f, Time: %lu</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">               <span class="n">data</span><span class="p">.</span><span class="n">temperature</span><span class="p">,</span> <span class="n">data</span><span class="p">.</span><span class="n">humidity</span><span class="p">,</span> <span class="n">data</span><span class="p">.</span><span class="n">timestamp</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">vStartSystem</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">xSensorQueue</span> <span class="o">=</span> <span class="nf">xQueueCreate</span><span class="p">(</span><span class="mi">10</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">SensorData_t</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="nf">xTaskCreate</span><span class="p">(</span><span class="n">vTempTask</span><span class="p">,</span> <span class="s">&#34;Temp&#34;</span><span class="p">,</span> <span class="mi">256</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nf">xTaskCreate</span><span class="p">(</span><span class="n">vHumidityTask</span><span class="p">,</span> <span class="s">&#34;Humidity&#34;</span><span class="p">,</span> <span class="mi">256</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nf">xTaskCreate</span><span class="p">(</span><span class="n">vProcessTask</span><span class="p">,</span> <span class="s">&#34;Process&#34;</span><span class="p">,</span> <span class="mi">512</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="nf">vTaskStartScheduler</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h2 id="调试技巧">调试技巧</h2>
<h3 id="常见问题">常见问题</h3>
<table>
<thead>
<tr>
<th>问题</th>
<th>可能原因</th>
<th>解决方法</th>
</tr>
</thead>
<tbody>
<tr>
<td>系统不启动</td>
<td>栈太小</td>
<td>增加 configMINIMAL_STACK_SIZE</td>
</tr>
<tr>
<td>任务不执行</td>
<td>优先级太低</td>
<td>提高优先级</td>
</tr>
<tr>
<td>内存不足</td>
<td>堆太小</td>
<td>增加 configTOTAL_HEAP_SIZE</td>
</tr>
<tr>
<td>死锁</td>
<td>互斥量使用不当</td>
<td>检查获取顺序</td>
</tr>
</tbody>
</table>
<h3 id="性能监控">性能监控</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// 获取空闲任务计数
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">uint32_t</span> <span class="n">ulIdleCount</span> <span class="o">=</span> <span class="nf">uxTaskGetNumberOfTasks</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 获取系统运行时间
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">TickType_t</span> <span class="n">xUptime</span> <span class="o">=</span> <span class="nf">xTaskGetTickCount</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 获取任务列表
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">TaskStatus_t</span> <span class="n">xTaskStatus</span><span class="p">[</span><span class="mi">10</span><span class="p">];</span>
</span></span><span class="line"><span class="cl"><span class="n">UBaseType_t</span> <span class="n">uxTasks</span> <span class="o">=</span> <span class="nf">uxTaskGetNumberOfTasks</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="nf">vTaskGetList</span><span class="p">(</span><span class="n">xTaskStatus</span><span class="p">,</span> <span class="n">uxTasks</span><span class="p">);</span>
</span></span></code></pre></div><h2 id="结语">结语</h2>
<p>FreeRTOS 是嵌入式 RTOS 的事实标准。通过本教程，你掌握了任务管理、通信和同步机制。动手做一个多任务项目吧！</p>
<hr>
<p><em>参考资料：FreeRTOS 官方文档、Mastering the FreeRTOS Real Time Kernel</em></p>
]]></content:encoded></item><item><title>嵌入式 C 语言编程技巧与最佳实践</title><link>https://tech-snippets.xyz/posts/embedded-c-programming-tips/</link><pubDate>Sun, 22 Mar 2026 03:00:00 +0800</pubDate><guid>https://tech-snippets.xyz/posts/embedded-c-programming-tips/</guid><description>掌握嵌入式 C 语言编程的核心技巧，编写高效、可靠、可维护的代码</description><content:encoded><![CDATA[<h2 id="嵌入式-c-语言特点">嵌入式 C 语言特点</h2>
<h3 id="与通用-c-的区别">与通用 C 的区别</h3>
<table>
<thead>
<tr>
<th>特性</th>
<th>嵌入式 C</th>
<th>通用 C</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>资源</strong></td>
<td>受限（KB 级内存）</td>
<td>充足（MB/GB 级）</td>
</tr>
<tr>
<td><strong>直接硬件访问</strong></td>
<td>寄存器操作</td>
<td>抽象 API</td>
</tr>
<tr>
<td><strong>实时性</strong></td>
<td>关键</td>
<td>不重要</td>
</tr>
<tr>
<td><strong>可靠性</strong></td>
<td>极高</td>
<td>可接受失败</td>
</tr>
</tbody>
</table>
<h2 id="数据类型">数据类型</h2>
<h3 id="使用固定宽度类型">使用固定宽度类型</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;stdint.h&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="c1">// ✅ 推荐：明确位宽
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">uint8_t</span> <span class="n">status</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kt">uint16_t</span> <span class="n">adc_value</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kt">int32_t</span> <span class="n">temperature</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// ❌ 避免：位宽不确定
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">char</span> <span class="n">flag</span><span class="p">;</span>      <span class="c1">// 可能是 8 位或 32 位
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">int</span> <span class="n">count</span><span class="p">;</span>      <span class="c1">// 可能是 16 位或 32 位
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">long</span> <span class="n">timeout</span><span class="p">;</span>   <span class="c1">// 可能是 32 位或 64 位
</span></span></span></code></pre></div><h3 id="位域操作">位域操作</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// 状态寄存器定义
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">uint8_t</span> <span class="nl">ready</span>    <span class="p">:</span> <span class="mi">1</span><span class="p">;</span>  <span class="c1">// bit 0
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="kt">uint8_t</span> <span class="nl">error</span>    <span class="p">:</span> <span class="mi">1</span><span class="p">;</span>  <span class="c1">// bit 1
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="kt">uint8_t</span> <span class="nl">mode</span>     <span class="p">:</span> <span class="mi">2</span><span class="p">;</span>  <span class="c1">// bit 2-3
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="kt">uint8_t</span> <span class="nl">reserved</span> <span class="p">:</span> <span class="mi">4</span><span class="p">;</span>  <span class="c1">// bit 4-7
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span> <span class="n">StatusReg_t</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 使用
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">StatusReg_t</span> <span class="n">status</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="n">status</span><span class="p">.</span><span class="n">ready</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="n">status</span><span class="p">.</span><span class="n">mode</span> <span class="o">=</span> <span class="mh">0x02</span><span class="p">;</span>
</span></span></code></pre></div><h2 id="内存管理">内存管理</h2>
<h3 id="避免动态分配">避免动态分配</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// ❌ 避免：malloc/free
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span><span class="o">*</span> <span class="n">buffer</span> <span class="o">=</span> <span class="nf">malloc</span><span class="p">(</span><span class="n">size</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nf">free</span><span class="p">(</span><span class="n">buffer</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// ✅ 推荐：静态分配
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">static</span> <span class="kt">uint8_t</span> <span class="n">buffer</span><span class="p">[</span><span class="mi">256</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// ✅ 推荐：内存池
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#define POOL_SIZE 10
</span></span></span><span class="line"><span class="cl"><span class="cp"></span><span class="k">static</span> <span class="kt">uint8_t</span> <span class="n">memory_pool</span><span class="p">[</span><span class="n">POOL_SIZE</span><span class="p">][</span><span class="n">BLOCK_SIZE</span><span class="p">];</span>
</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">uint8_t</span> <span class="n">pool_bitmap</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span></code></pre></div><h3 id="const-和-volatile">const 和 volatile</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// const：只读数据（存储在 Flash）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">const</span> <span class="kt">uint16_t</span> <span class="n">lookup_table</span><span class="p">[</span><span class="mi">256</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span><span class="mi">0</span><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// volatile：可能被外部修改
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">volatile</span> <span class="kt">uint8_t</span> <span class="n">gpio_input</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">volatile</span> <span class="kt">uint32_t</span><span class="o">*</span> <span class="k">const</span> <span class="n">REGISTER_ADDR</span> <span class="o">=</span> <span class="p">(</span><span class="kt">uint32_t</span><span class="o">*</span><span class="p">)</span><span class="mh">0x40020000</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 组合使用
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">const</span> <span class="k">volatile</span> <span class="kt">uint32_t</span><span class="o">*</span> <span class="k">const</span> <span class="n">HW_REGISTER</span> <span class="o">=</span> 
</span></span><span class="line"><span class="cl">    <span class="p">(</span><span class="kt">uint32_t</span><span class="o">*</span><span class="p">)</span><span class="mh">0x40020000</span><span class="p">;</span>
</span></span></code></pre></div><h2 id="中断编程">中断编程</h2>
<h3 id="中断服务函数">中断服务函数</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// 中断服务函数
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="nf">EXTI0_IRQHandler</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="nf">__attribute__</span><span class="p">((</span><span class="n">interrupt</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">EXTI0_IRQHandler</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 1. 清除中断标志
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">EXTI</span><span class="o">-&gt;</span><span class="n">PR</span> <span class="o">=</span> <span class="n">EXTI_PR_PR0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 2. 快速处理（置标志）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">g_button_pressed</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 3. 如果需要，通知任务
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">xSemaphoreGiveFromISR</span><span class="p">(</span><span class="n">xBinarySemaphore</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="临界区保护">临界区保护</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// 方法 1：关中断
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">uint32_t</span> <span class="n">primask</span> <span class="o">=</span> <span class="nf">__get_PRIMASK</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="nf">__disable_irq</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="c1">// 临界区代码
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nf">__set_PRIMASK</span><span class="p">(</span><span class="n">primask</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 方法 2：使用库
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nf">__disable_irq</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="c1">// 代码
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nf">__enable_irq</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 方法 3：FreeRTOS
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nf">taskENTER_CRITICAL</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="c1">// 代码
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nf">taskEXIT_CRITICAL</span><span class="p">();</span>
</span></span></code></pre></div><h2 id="位操作技巧">位操作技巧</h2>
<h3 id="常用宏">常用宏</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// 置位
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#define SET_BIT(REG, BIT)     ((REG) |= (1 &lt;&lt; (BIT)))
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="c1">// 清零
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#define CLEAR_BIT(REG, BIT)   ((REG) &amp;= ~(1 &lt;&lt; (BIT)))
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="c1">// 翻转
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#define TOGGLE_BIT(REG, BIT)  ((REG) ^= (1 &lt;&lt; (BIT)))
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="c1">// 读位
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#define READ_BIT(REG, BIT)    (((REG) &gt;&gt; (BIT)) &amp; 1)
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="c1">// 设置位域
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#define SET_BITS(REG, POS, LEN, VAL) \
</span></span></span><span class="line"><span class="cl"><span class="cp">    ((REG) = (((REG) &amp; ~(((1 &lt;&lt; (LEN)) - 1) &lt;&lt; (POS))) | \
</span></span></span><span class="line"><span class="cl"><span class="cp">             (((VAL) &amp; ((1 &lt;&lt; (LEN)) - 1)) &lt;&lt; (POS))))
</span></span></span></code></pre></div><h3 id="使用示例">使用示例</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// 配置 GPIO
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#define GPIO_MODE_OUTPUT  1
</span></span></span><span class="line"><span class="cl"><span class="cp">#define GPIO_PIN_5        5
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="c1">// 设置 PA5 为输出
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nf">SET_BITS</span><span class="p">(</span><span class="n">GPIOA</span><span class="o">-&gt;</span><span class="n">MODER</span><span class="p">,</span> <span class="mi">10</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="n">GPIO_MODE_OUTPUT</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 切换 LED
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nf">TOGGLE_BIT</span><span class="p">(</span><span class="n">GPIOA</span><span class="o">-&gt;</span><span class="n">ODR</span><span class="p">,</span> <span class="n">GPIO_PIN_5</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 检查按键
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">if</span> <span class="p">(</span><span class="nf">READ_BIT</span><span class="p">(</span><span class="n">GPIOB</span><span class="o">-&gt;</span><span class="n">IDR</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 按键按下
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span></code></pre></div><h2 id="代码优化">代码优化</h2>
<h3 id="空间优化">空间优化</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// 使用更小的数据类型
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">uint8_t</span> <span class="n">counter</span><span class="p">;</span>  <span class="c1">// 而不是 int
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="c1">// 共用体节省内存
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">typedef</span> <span class="k">union</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">uint32_t</span> <span class="n">raw</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="kt">uint8_t</span> <span class="n">b0</span><span class="p">,</span> <span class="n">b1</span><span class="p">,</span> <span class="n">b2</span><span class="p">,</span> <span class="n">b3</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span> <span class="n">bytes</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="n">DataUnion_t</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 使用位域
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">uint8_t</span> <span class="nl">flag1</span> <span class="p">:</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">uint8_t</span> <span class="nl">flag2</span> <span class="p">:</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">uint8_t</span> <span class="nl">mode</span>  <span class="p">:</span> <span class="mi">3</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="n">flags</span><span class="p">;</span>  <span class="c1">// 只占 1 字节
</span></span></span></code></pre></div><h3 id="时间优化">时间优化</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// 使用查表代替计算
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">const</span> <span class="kt">uint16_t</span> <span class="n">sin_table</span><span class="p">[</span><span class="mi">256</span><span class="p">]</span> <span class="o">=</span> <span class="p">{...};</span>
</span></span><span class="line"><span class="cl"><span class="kt">uint16_t</span> <span class="n">sin_value</span> <span class="o">=</span> <span class="n">sin_table</span><span class="p">[</span><span class="n">i</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 使用移位代替乘除
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">x</span> <span class="o">=</span> <span class="n">y</span> <span class="o">*</span> <span class="mi">8</span><span class="p">;</span>   <span class="c1">// → x = y &lt;&lt; 3;
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">x</span> <span class="o">=</span> <span class="n">y</span> <span class="o">/</span> <span class="mi">4</span><span class="p">;</span>   <span class="c1">// → x = y &gt;&gt; 2;
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="c1">// 循环展开
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">for</span> <span class="p">(</span><span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="mi">4</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>  <span class="c1">// →
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">array</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="c1">// 展开为：
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">array</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">array</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> 
</span></span><span class="line"><span class="cl"><span class="n">array</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">array</span><span class="p">[</span><span class="mi">3</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span></code></pre></div><h2 id="状态机">状态机</h2>
<h3 id="实现模式识别">实现模式识别</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="k">typedef</span> <span class="k">enum</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">STATE_IDLE</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">STATE_START</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">STATE_PROCESS</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">STATE_END</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="n">State_t</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">State_t</span> <span class="n">current_state</span> <span class="o">=</span> <span class="n">STATE_IDLE</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">state_machine</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">switch</span> <span class="p">(</span><span class="n">current_state</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">case</span> <span class="nl">STATE_IDLE</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="k">if</span> <span class="p">(</span><span class="n">start_condition</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="n">current_state</span> <span class="o">=</span> <span class="n">STATE_START</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">            <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            
</span></span><span class="line"><span class="cl">        <span class="k">case</span> <span class="nl">STATE_START</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="nf">init_resources</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">            <span class="n">current_state</span> <span class="o">=</span> <span class="n">STATE_PROCESS</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            
</span></span><span class="line"><span class="cl">        <span class="k">case</span> <span class="nl">STATE_PROCESS</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="k">if</span> <span class="p">(</span><span class="n">process_complete</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="n">current_state</span> <span class="o">=</span> <span class="n">STATE_END</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">            <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            
</span></span><span class="line"><span class="cl">        <span class="k">case</span> <span class="nl">STATE_END</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="nf">cleanup</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">            <span class="n">current_state</span> <span class="o">=</span> <span class="n">STATE_IDLE</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h2 id="错误处理">错误处理</h2>
<h3 id="返回值检查">返回值检查</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="k">typedef</span> <span class="k">enum</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">ERR_OK</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">ERR_INVALID_PARAM</span> <span class="o">=</span> <span class="o">-</span><span class="mi">1</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">ERR_TIMEOUT</span> <span class="o">=</span> <span class="o">-</span><span class="mi">2</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">ERR_NO_MEMORY</span> <span class="o">=</span> <span class="o">-</span><span class="mi">3</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="n">Error_t</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">Error_t</span> <span class="nf">sensor_read</span><span class="p">(</span><span class="kt">float</span><span class="o">*</span> <span class="n">value</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">value</span> <span class="o">==</span> <span class="nb">NULL</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">ERR_INVALID_PARAM</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 读取传感器
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">if</span> <span class="p">(</span><span class="n">timeout</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">ERR_TIMEOUT</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="o">*</span><span class="n">value</span> <span class="o">=</span> <span class="n">read_value</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">ERR_OK</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 使用
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">float</span> <span class="n">temp</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="n">Error_t</span> <span class="n">err</span> <span class="o">=</span> <span class="nf">sensor_read</span><span class="p">(</span><span class="o">&amp;</span><span class="n">temp</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="p">(</span><span class="n">err</span> <span class="o">!=</span> <span class="n">ERR_OK</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 错误处理
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span></code></pre></div><h2 id="调试技巧">调试技巧</h2>
<h3 id="断言">断言</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;assert.h&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">process_data</span><span class="p">(</span><span class="kt">uint8_t</span><span class="o">*</span> <span class="n">data</span><span class="p">,</span> <span class="kt">uint16_t</span> <span class="n">size</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nf">assert</span><span class="p">(</span><span class="n">data</span> <span class="o">!=</span> <span class="nb">NULL</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nf">assert</span><span class="p">(</span><span class="n">size</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nf">assert</span><span class="p">(</span><span class="n">size</span> <span class="o">&lt;=</span> <span class="n">MAX_SIZE</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 处理代码
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span></code></pre></div><h3 id="日志宏">日志宏</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="cp">#define LOG_LEVEL_ERROR   0
</span></span></span><span class="line"><span class="cl"><span class="cp">#define LOG_LEVEL_INFO    1
</span></span></span><span class="line"><span class="cl"><span class="cp">#define LOG_LEVEL_DEBUG   2
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="cp">#define LOG_LEVEL         LOG_LEVEL_DEBUG
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="cp">#if LOG_LEVEL &gt;= LOG_LEVEL_ERROR
</span></span></span><span class="line"><span class="cl"><span class="cp">#define LOG_ERROR(fmt, ...) \
</span></span></span><span class="line"><span class="cl"><span class="cp">    printf(&#34;[E] &#34; fmt &#34;\n&#34;, ##__VA_ARGS__)
</span></span></span><span class="line"><span class="cl"><span class="cp">#endif
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="cp">#if LOG_LEVEL &gt;= LOG_LEVEL_INFO
</span></span></span><span class="line"><span class="cl"><span class="cp">#define LOG_INFO(fmt, ...) \
</span></span></span><span class="line"><span class="cl"><span class="cp">    printf(&#34;[I] &#34; fmt &#34;\n&#34;, ##__VA_ARGS__)
</span></span></span><span class="line"><span class="cl"><span class="cp">#endif
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="cp">#if LOG_LEVEL &gt;= LOG_LEVEL_DEBUG
</span></span></span><span class="line"><span class="cl"><span class="cp">#define LOG_DEBUG(fmt, ...) \
</span></span></span><span class="line"><span class="cl"><span class="cp">    printf(&#34;[D] %s:%d &#34; fmt &#34;\n&#34;, \
</span></span></span><span class="line"><span class="cl"><span class="cp">           __FILE__, __LINE__, ##__VA_ARGS__)
</span></span></span><span class="line"><span class="cl"><span class="cp">#endif
</span></span></span></code></pre></div><h2 id="编码规范">编码规范</h2>
<h3 id="命名规范">命名规范</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// 类型：大驼峰 + _t
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span> <span class="p">...</span> <span class="p">}</span> <span class="n">SensorData_t</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 函数：小写 + 下划线
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="nf">sensor_init</span><span class="p">(</span><span class="kt">void</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="kt">float</span> <span class="nf">sensor_read_temperature</span><span class="p">(</span><span class="kt">void</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 变量：小写
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">uint8_t</span> <span class="n">counter</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kt">float</span> <span class="n">temperature</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 常量：大写
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#define MAX_RETRY  10
</span></span></span><span class="line"><span class="cl"><span class="cp">#define TIMEOUT_MS 1000
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="c1">// 宏函数：大写
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#define MIN(a, b)  ((a) &lt; (b) ? (a) : (b))
</span></span></span></code></pre></div><h3 id="注释规范">注释规范</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="cm">/**
</span></span></span><span class="line"><span class="cl"><span class="cm"> * @brief  读取温度传感器
</span></span></span><span class="line"><span class="cl"><span class="cm"> * @param  sensor_id: 传感器 ID
</span></span></span><span class="line"><span class="cl"><span class="cm"> * @param  value: 存储读取值的指针
</span></span></span><span class="line"><span class="cl"><span class="cm"> * @retval 成功返回 ERR_OK，失败返回错误码
</span></span></span><span class="line"><span class="cl"><span class="cm"> */</span>
</span></span><span class="line"><span class="cl"><span class="n">Error_t</span> <span class="nf">sensor_read_temperature</span><span class="p">(</span><span class="kt">uint8_t</span> <span class="n">sensor_id</span><span class="p">,</span> <span class="kt">float</span><span class="o">*</span> <span class="n">value</span><span class="p">);</span>
</span></span></code></pre></div><h2 id="结语">结语</h2>
<p>嵌入式 C 语言编程需要兼顾效率、可靠性和可维护性。遵循最佳实践，你的代码会更优秀！</p>
<hr>
<p><em>参考资料：MISRA C 规范、C 语言编程规范、嵌入式软件设计指南</em></p>
]]></content:encoded></item></channel></rss>