﻿<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>程序员日志</title>
	<atom:link href="https://e673.com/feed/" rel="self" type="application/rss+xml" />
	<link>https://e673.com</link>
	<description></description>
	<lastBuildDate>Wed, 06 May 2026 13:35:06 +0000</lastBuildDate>
	<language>zh-Hans</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9.4</generator>
	<item>
		<title>git 推送时提示：send-pack: unexpected disconnect while reading sideband packet fatal: the remote end hung up unexpec</title>
		<link>https://e673.com/git-push-send-pack-unexpected-disconnect-while-reading-sideband-packet/</link>
		
		<dc:creator><![CDATA[ahe]]></dc:creator>
		<pubDate>Wed, 06 May 2026 13:35:06 +0000</pubDate>
				<category><![CDATA[应用笔记]]></category>
		<category><![CDATA[经验分享]]></category>
		<category><![CDATA[版本控制]]></category>
		<guid isPermaLink="false">https://e673.com/?p=3106</guid>

					<description><![CDATA[搜索各种办法无效，我的场景： 提&#46;&#46;&#46;]]></description>
										<content:encoded><![CDATA[<p>搜索各种办法无效，我的场景：</p>
<p>提交了一个48M大小的更改，无法push</p>
<p>使用<br />
git config &#8211;global http.postBuffer 524288000</p>
<p>设置超时时间后再push，问题解决。</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>ESP32 4G拨号实战：ML307A和ML307R的PPP指令差异详解（附完整代码）</title>
		<link>https://e673.com/esp32-4g-ppp-dial-ml307a-ml307r/</link>
		
		<dc:creator><![CDATA[ahe]]></dc:creator>
		<pubDate>Thu, 26 Mar 2026 04:36:47 +0000</pubDate>
				<category><![CDATA[应用笔记]]></category>
		<category><![CDATA[嵌入程序]]></category>
		<guid isPermaLink="false">https://e673.com/?p=3102</guid>

					<description><![CDATA[最近在做一个基于ESP32S3的&#46;&#46;&#46;]]></description>
										<content:encoded><![CDATA[<p>最近在做一个基于ESP32S3的户外数据采集项目，需要用到4G Cat.1模组进行网络通信。手头正好有移远通信的ML307A和ML307R两款模组，本以为都是同一系列，AT指令应该大同小异，结果在PPP拨号这个环节上却结结实实踩了个坑。ML307R用ATD*99#一切正常，换到ML307A上就死活连不上，串口只返回ERROR。折腾了大半天，翻遍了手册和论坛，才发现问题就出在那个看似不起眼的拨号指令上——ML307A需要显式指定PDP上下文ID。这个细微的差异，对于嵌入式物联网 开发者来说，可能就是项目进度卡上一天的关键所在。</p>
<p>这篇文章，我就从一个实际开发者的角度，把这两款模组在ESP32S3上进行PPP拨号时的指令差异、背后的原理、完整的配置流程以及如何编写健壮的兼容性代码，给大家掰开揉碎了讲清楚。无论你是正在选型纠结用哪款模组，还是已经遇到了连接问题，希望这里的实战经验能帮你快速绕过这些“坑”。</p>
<p>1. 理解PPP拨号与AT指令基础<br />
在深入ML307A和ML307R的差异之前，我们有必要先统一一下基础认知。PPP，即点对点协议，是物联网设备通过蜂窝模组接入互联网的经典方式。它本质上是在设备的MCU（如ESP32S3）和蜂窝模组之间建立一条透明的数据通道 ，模组负责无线通信，MCU则通过串口使用PPP协议栈来处理IP数据包。</p>
<p>整个过程始于一系列AT指令的交互。AT指令是调制解调器的“语言”，我们通过UART发送特定的文本命令来控制模组。一个典型的PPP连接建立流程，可以概括为以下几个核心阶段：</p>
<p>模组初始化与网络注册：确保模组上电、SIM卡就绪并成功注册到运营商网络。<br />
PDP上下文定义：告诉模组使用哪个接入点名称（APN）来建立数据连接。你可以把它理解为给本次数据会话办理“入网手续”。<br />
PDP上下文激活：正式向网络发起请求，分配IP地址等资源。<br />
发起PPP拨号：启动模组内部的PPP协议栈，并将串口切换到PPP数据模式。<br />
MCU侧PPP协议栈处理：ESP32S3上的LwIP PPP协议栈开始与模组协商，最终建立IP层连接。<br />
注意：很多新手容易混淆“PDP上下文激活”和“PPP拨号”。前者是蜂窝网络层面的数据承载建立，后者是在这个承载之上建立点对点协议链路。两者必须按顺序成功完成。</p>
<p>对于ESP32开发者，乐鑫提供了esp-modem组件来简化这部分工作。它封装了AT指令交互和PPP协议栈的对接，但我们仍然需要准确配置底层的拨号指令，这正是本文要解决的核心问题。</p>
<p>2. ML307A与ML307R的拨号指令关键差异剖析<br />
为什么ATD*99#在ML307R上好用，到了ML307A上就不行？这需要我们从模组的固件行为和协议规范层面来理解。</p>
<p>*99#是一个通用的蜂窝数据呼叫号码，用于请求建立PPP连接。而ATD是发起呼叫的指令。关键在于，在发起呼叫时，模组需要知道使用哪一个之前定义好的PDP上下文。PDP上下文可以有多个（ID通常为1-3），用于不同的网络连接（比如主用和备用APN）。</p>
<p>ML307R的处理方式更为“宽松”或“智能”。当你发送ATD*99#时，如果当前只有一个PDP上下文（通常是ID为1的上下文）被定义和激活，模组会默认使用它。这种设计简化了操作，对开发者友好。</p>
<p>ML307A则要求“显式指定”。它的固件在处理ATD*99#时，不会自动关联已激活的上下文，因此会因参数不全而失败。必须使用ATD*99***X#的格式，其中X就是你想要使用的PDP上下文ID（通常是1）。***作为分隔符，其后紧跟的数字明确告知模组：“请使用第X号PDP上下文发起本次PPP呼叫”。</p>
<p>这个差异看似微小，但影响巨大。我们可以通过一个简单的表格来对比两款模组在这个环节上的不同：</p>
<p>特性 ML307R ML307A 说明与影响<br />
默认拨号指令 ATD*99# ATD*99***1# ML307A必须携带上下文ID参数。<br />
固件行为 自动关联首个激活的上下文。 必须显式指定上下文ID。 ML307A的行为更严格遵循某些AT命令规范。<br />
开发者适配 简单，通用指令兼容性好。 需特别注意指令格式，否则连接失败。 在代码中需做模组类型判断或指令适配。<br />
错误现象 指令正确则返回CONNECT。 使用ATD*99#会直接返回ERROR。 这是最直接的排查线索。<br />
提示：除了拨号指令，两款模组在绝大多数其他AT指令（如网络查询、信号强度获取、短信功能等）上都是兼容的。差异主要集中在这条特定的拨号命令上。</p>
<p>那么，ATD*99***1#中的“1”是否可以更改？当然可以，前提是你定义并激活了其他ID的PDP上下文。例如，如果你为备用APN定义了上下文ID=2，那么拨号时就可以使用ATD*99***2#。但在绝大多数单数据通道的应用中，我们只使用上下文1。</p>
<p>3. 完整的PPP连接配置流程与AT指令序列<br />
理解了核心差异，我们来看一个完整的、可操作的配置流程。这里以ML307A为例，给出每一步的AT指令和预期响应。对于ML307R，你只需要修改最后一步的拨号指令即可。</p>
<p>3.1 硬件连接与基础检查<br />
首先，确保你的ESP32S3与ML307模组正确连接。至少需要连接电源、地线、主串口（用于AT指令和PPP数据）的TX和RX。建议额外连接PWRKEY引脚以便软件控制开机。</p>
<p>上电后，先通过串口工具 手动发送AT指令，确认通信正常，应返回OK。</p><pre class="crayon-plain-tag"># 基础AT指令测试
AT
# 预期响应: OK</pre><p>接着，检查SIM卡状态和网络注册情况。</p><pre class="crayon-plain-tag"># 查询SIM卡是否就绪
AT+CPIN?
# 预期响应: +CPIN: READY

# 查询网络注册状态
AT+CREG?
# 预期响应: +CREG: 0,1 (其中第二个参数为1表示已注册到本地网)</pre><p>bash<br />
3.2 定义并激活PDP上下文<br />
这是建立数据通道的基础。你需要将&lt;your_apn&gt;替换为你所使用的运营商APN，例如中国移动的cmnet。</p><pre class="crayon-plain-tag"># 1. 定义PDP上下文（上下文ID=1，协议为IP，APN为cmnet）
AT+CGDCONT=1,"IP","cmnet"
# 预期响应: OK

# 2. 激活上面定义的PDP上下文
AT+CGACT=1,1
# 预期响应: OK
# 第一个参数1表示“激活”，第二个参数1表示上下文ID</pre><p>bash<br />
执行AT+CGACT=1,1后，模组会向蜂窝网络发起数据承载请求。你可以通过AT+CGACT?来查询激活状态。</p>
<p>3.3 发起PPP拨号（关键步骤）<br />
对于ML307A，使用显式指定上下文的指令：</p><pre class="crayon-plain-tag">ATD*99***1#
# 成功则返回: CONNECT
# 此后串口将进入PPP数据模式，不再响应AT指令。</pre><p>bash<br />
对于ML307R，使用通用指令：</p><pre class="crayon-plain-tag">ATD*99#
# 成功则返回: CONNECT</pre><p>bash<br />
一旦收到CONNECT，模组的串口就会切换模式。此时，ESP32S3端的PPP协议栈应该开始工作，与模组进行LCP、IPCP等协议协商，最终为你分配一个IP地址。</p>
<p>3.4 断开连接<br />
当需要断开网络时，对于PPP连接，通常不是发送AT指令（因为串口已在数据模式），而是由MCU侧的PPP协议栈发起断开流程。协议断开后，模组会自动退出数据模式。你也可以通过给模组发送+++（需要在前后有至少1秒的静默时间）返回到命令模式，再发送ATH来挂断呼叫。</p>
<p>4. 在ESP32S3项目中实现兼容性代码<br />
在实际项目中，我们不可能每次都在串口工具里手动输入指令。我们需要将上述流程固化为ESP32S3上的代码，并且要优雅地处理ML307A和ML307R的差异。</p>
<p>乐鑫的esp-modem组件是我们的好帮手。它提供了esp_modem_dce抽象层来处理与各种模组的通信。下面，我将展示如何配置和使用它，并实现拨号指令的兼容。</p>
<p>4.1 项目配置与组件引入<br />
首先，在你的项目idf.py menuconfig中，需要配置PPP支持：</p>
<p>Component config -&gt; LWIP -&gt; Enable PPP networking (NEW)<br />
Component config -&gt; LWIP -&gt; PPP support -&gt; Enable PPPoS client support (NEW)<br />
然后，在CMakeLists.txt中添加esp_modem组件依赖：</p>
<p>set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/components/esp_modem)<br />
# 或者如果你将esp_modem作为组件放在项目内部<br />
list(APPEND EXTRA_COMPONENT_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/components)<br />
cmake<br />
4.2 定义模组配置与拨号指令适配<br />
我们可以创建一个头文件，如modem_config.h，来定义不同模组的配置参数和拨号指令。</p><pre class="crayon-plain-tag">// modem_config.h
#pragma once

#include "esp_modem_config.h"

// 定义模组类型枚举
typedef enum {
MODEM_TYPE_ML307R,
MODEM_TYPE_ML307A
} modem_type_t;

// 根据模组类型获取拨号指令
static inline const char* get_ppp_dial_command(modem_type_t type) {
return (type == MODEM_TYPE_ML307A) ? "ATD*99***1#" : "ATD*99#";
}

// 通用的ML307系列配置（波特率、流控等）
#define ML307_COMMON_CONFIG() { \
.command_terminator = "\r", \
.pdp_context = { \
.apn = "cmnet", // 你的APN \
.user = "", \
.password = "", \
}, \
.timeout_ms = 10000, \
}</pre><p>c</p>
<p>4.3 主程序中的初始化与拨号流程<br />
在主程序源文件中，我们实现完整的逻辑。关键点在于：在调用esp_modem_dce_start_ppp()之前，如果我们检测到是ML307A，需要手动设置拨号指令。</p><pre class="crayon-plain-tag">// main.c
#include "esp_log.h"
#include "esp_modem.h"
#include "modem_config.h"

static const char *TAG = "PPP_EXAMPLE";

// 假设我们通过某种方式（如GPIO检测、配置菜单）确定了模组类型
modem_type_t current_modem_type = MODEM_TYPE_ML307A; // 或 MODEM_TYPE_ML307R

void app_main(void) {
esp_err_t err = ESP_OK;

// 1. 配置DCE（数据电路终端设备，即模组）
esp_modem_dce_config_t dce_config = ML307_COMMON_CONFIG();
esp_modem_dce_t *dce = NULL;

// 2. 创建DCE实例，假设使用UART1，引脚为GPIO4(TX)、GPIO5(RX)
err = esp_modem_dce_create(&amp;dce_config, UART_NUM_1, 4, 5, &amp;dce);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to create DCE! err=0x%x", err);
return;
}

// 3. 【关键步骤】针对ML307A，覆盖默认拨号指令
if (current_modem_type == MODEM_TYPE_ML307A) {
const char* dial_cmd = get_ppp_dial_command(current_modem_type);
ESP_LOGI(TAG, "Using modem-specific dial command: %s", dial_cmd);
// 通过generic command接口预置拨号指令
err = esp_modem_dte_generic_command(dce, dial_cmd, NULL, 0);
// 注意：这里只是设置指令，并非立即拨号。真正的拨号由start_ppp触发。
if (err != ESP_OK) {
ESP_LOGW(TAG, "Pre-set dial command may not be supported, PPP start will use default.");
}
}

// 4. 启动PPP会话
// esp_modem_dce_start_ppp()内部会处理PDP上下文定义、激活和拨号全过程。
// 对于ML307R，它使用默认的ATD*99#。
// 对于ML307A，如果我们上一步预置成功，它会使用我们设置的指令。
err = esp_modem_dce_start_ppp(dce);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to start PPP session! err=0x%x", err);
// 这里可以加入重试或错误处理逻辑
esp_modem_dce_delete(dce);
return;
}

ESP_LOGI(TAG, "PPP session started successfully!");

// 5. 此时，网络已连接。你可以使用socket API进行通信。
// ...

// 6. 应用运行... 当需要断开时：
// esp_modem_dce_stop_ppp(dce);
// esp_modem_dce_delete(dce);
}</pre><p>c</p>
<p>4.4 更健壮的自动检测与适配方案<br />
上面的代码假设我们已经知道模组类型。一个更完善的方案是在运行时自动检测。这可以通过发送AT指令查询模组型号来实现。</p><pre class="crayon-plain-tag">// 在初始化阶段添加模组类型检测函数
modem_type_t detect_modem_type(esp_modem_dce_t *dce) {
char resp[128] = {0};
esp_err_t err = esp_modem_dte_generic_command(dce, "ATI\r", resp, sizeof(resp)-1, pdMS_TO_TICKS(2000));
if (err == ESP_OK) {
ESP_LOGI(TAG, "Modem info: %s", resp);
// 解析响应，判断是ML307A还是ML307R
if (strstr(resp, "ML307A") != NULL) {
return MODEM_TYPE_ML307A;
} else if (strstr(resp, "ML307R") != NULL) {
return MODEM_TYPE_ML307R;
}
}
// 如果检测失败，可以返回一个默认类型，或根据其他特征判断
ESP_LOGW(TAG, "Modem detection failed, using default (ML307R).");
return MODEM_TYPE_ML307R; // 假设ML307R更常见
}

// 然后在app_main中调用
current_modem_type = detect_modem_type(dce);</pre><p>c</p>
<p>通过这种设计，你的代码就能智能地适配不同型号的ML307模组，大大提高项目的可维护性和硬件的兼容性。在实际部署中，这种自动检测机制能避免因生产批次或物料替换带来的固件升级问题。<br />
————————————————<br />
版权声明：本文为CSDN博主「sony5」的原创文章，遵循CC 4.0 BY-SA版权协议，转载请附上原文出处链接及本声明。<br />
原文链接：https://blog.csdn.net/sony5/article/details/152875556</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>C 和 C++ 对 enum 处理的差异，以及强制转换在GCC/ARMCC 下的实际表现</title>
		<link>https://e673.com/c-cpp-enum-dealing-in-deep/</link>
		
		<dc:creator><![CDATA[ahe]]></dc:creator>
		<pubDate>Sat, 14 Mar 2026 08:35:23 +0000</pubDate>
				<category><![CDATA[经验分享]]></category>
		<category><![CDATA[C/C++]]></category>
		<category><![CDATA[嵌入程序]]></category>
		<category><![CDATA[硬件]]></category>
		<guid isPermaLink="false">https://e673.com/?p=3099</guid>

					<description><![CDATA[1. C 里的 enum，本质上&#46;&#46;&#46;]]></description>
										<content:encoded><![CDATA[<h2 data-section-id="mtorkv" data-start="150" data-end="183">1. C 里的 enum，本质上更像“带名字的整数常量集合”</h2>
<p data-start="185" data-end="193">在 C 语言里：</p>
<div class="relative w-full mt-4 mb-1">
<div class="">
<div class="relative">
<div class="h-full min-h-0 min-w-0">
<div class="h-full min-h-0 min-w-0">
<div class="border border-token-border-light border-radius-3xl corner-superellipse/1.1 rounded-3xl">
<div class="h-full w-full border-radius-3xl bg-token-bg-elevated-secondary corner-superellipse/1.1 overflow-clip rounded-3xl lxnfua_clipPathFallback">
<div class="pointer-events-none absolute inset-x-4 top-12 bottom-4">
<div class="pointer-events-none sticky z-40 shrink-0 z-1!">
<div class="sticky bg-token-border-light"></div>
</div>
</div>
<div class="">
<div class="relative z-0 flex max-w-full">
<div id="code-block-viewer" class="q9tKkq_viewer cm-editor z-10 light:cm-light dark:cm-light flex h-full w-full flex-col items-stretch ͼ5 ͼj" dir="ltr">
<div class="cm-scroller">
<div class="cm-content q9tKkq_readonly"><span class="ͼ8">typedef</span> <span class="ͼ8">enum</span><br />
{<br />
<span class="ͼe">MODE_A</span> <span class="ͼ8">=</span> <span class="ͼb">0</span>,<br />
<span class="ͼe">MODE_B</span> <span class="ͼ8">=</span> <span class="ͼb">1</span>,<br />
<span class="ͼe">MODE_C</span> <span class="ͼ8">=</span> <span class="ͼb">2</span><br />
} <span class="ͼe">Mode</span>;</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="">
<div class=""></div>
</div>
</div>
</div>
</div>
<p data-start="275" data-end="305">这并不表示 <code data-start="281" data-end="287">Mode</code> 类型的变量只能取 <code data-start="297" data-end="304">0/1/2</code>。</p>
<p data-start="307" data-end="316">它更准确的含义是：</p>
<ul data-start="318" data-end="400">
<li data-section-id="1e1zpwg" data-start="318" data-end="336">
<p data-start="320" data-end="336">定义了一个枚举类型 <code data-start="330" data-end="336">Mode</code></p>
</li>
<li data-section-id="1sraup8" data-start="337" data-end="371">
<p data-start="339" data-end="371">定义了几个命名常量 <code data-start="349" data-end="371">MODE_A/MODE_B/MODE_C</code></p>
</li>
<li data-section-id="137n67y" data-start="372" data-end="400">
<p data-start="374" data-end="400"><code data-start="374" data-end="380">Mode</code> 类型的对象通常仍然按整数方式存储和运算</p>
</li>
</ul>
<p data-start="402" data-end="405">所以：</p>
<div class="relative w-full mt-4 mb-1">
<div class="">
<div class="relative">
<div class="h-full min-h-0 min-w-0">
<div class="h-full min-h-0 min-w-0">
<div class="border border-token-border-light border-radius-3xl corner-superellipse/1.1 rounded-3xl">
<div class="h-full w-full border-radius-3xl bg-token-bg-elevated-secondary corner-superellipse/1.1 overflow-clip rounded-3xl lxnfua_clipPathFallback">
<div class="pointer-events-none absolute inset-x-4 top-12 bottom-4">
<div class="pointer-events-none sticky z-40 shrink-0 z-1!">
<div class="sticky bg-token-border-light"></div>
</div>
</div>
<div class="">
<div class="relative z-0 flex max-w-full">
<div id="code-block-viewer" class="q9tKkq_viewer cm-editor z-10 light:cm-light dark:cm-light flex h-full w-full flex-col items-stretch ͼ5 ͼj" dir="ltr">
<div class="cm-scroller">
<div class="cm-content q9tKkq_readonly"><span class="ͼe">Mode</span> <span class="ͼe">m</span> <span class="ͼ8">=</span> (<span class="ͼe">Mode</span>)<span class="ͼb">123</span>;</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="">
<div class=""></div>
</div>
</div>
</div>
</div>
<p data-start="437" data-end="466">在 <strong data-start="439" data-end="444">C</strong> 里通常是允许的，<code data-start="453" data-end="456">m</code> 的值就是 123。</p>
<p data-start="468" data-end="496">也就是说，<strong data-start="473" data-end="495">C 的 enum 不是运行时受限集合</strong>。</p>
<hr data-start="498" data-end="501" />
<h2 data-section-id="1nar478" data-start="503" data-end="522">2. C 里为什么容易出 bug</h2>
<p data-start="524" data-end="555">因为代码看起来像“只有几个合法状态”，但实际上变量能装更多值。</p>
<p data-start="557" data-end="560">例如：</p>
<div class="relative w-full mt-4 mb-1">
<div class="">
<div class="relative">
<div class="h-full min-h-0 min-w-0">
<div class="h-full min-h-0 min-w-0">
<div class="border border-token-border-light border-radius-3xl corner-superellipse/1.1 rounded-3xl">
<div class="h-full w-full border-radius-3xl bg-token-bg-elevated-secondary corner-superellipse/1.1 overflow-clip rounded-3xl lxnfua_clipPathFallback">
<div class="pointer-events-none absolute inset-x-4 top-12 bottom-4">
<div class="pointer-events-none sticky z-40 shrink-0 z-1!">
<div class="sticky bg-token-border-light"></div>
</div>
</div>
<div class="">
<div class="relative z-0 flex max-w-full">
<div id="code-block-viewer" class="q9tKkq_viewer cm-editor z-10 light:cm-light dark:cm-light flex h-full w-full flex-col items-stretch ͼ5 ͼj" dir="ltr">
<div class="cm-scroller">
<div class="cm-content q9tKkq_readonly"><span class="ͼ8">typedef</span> <span class="ͼ8">enum</span><br />
{<br />
<span class="ͼe">SYS_IDLE</span> <span class="ͼ8">=</span> <span class="ͼb">0</span>,<br />
<span class="ͼe">SYS_RUN</span> <span class="ͼ8">=</span> <span class="ͼb">1</span>,<br />
<span class="ͼe">SYS_ERR</span> <span class="ͼ8">=</span> <span class="ͼb">2</span><br />
} <span class="ͼe">SysState</span>;</p>
<p><span class="ͼe">void</span> <span class="ͼe">process</span>(<span class="ͼe">SysState</span> <span class="ͼe">s</span>)<br />
{<br />
<span class="ͼ8">switch</span> (<span class="ͼe">s</span>)<br />
{<br />
<span class="ͼ8">case</span> <span class="ͼe">SYS_IDLE</span>:<br />
<span class="ͼ6">// &#8230;</span><br />
<span class="ͼ8">break</span>;<br />
<span class="ͼ8">case</span> <span class="ͼe">SYS_RUN</span>:<br />
<span class="ͼ6">// &#8230;</span><br />
<span class="ͼ8">break</span>;<br />
<span class="ͼ8">case</span> <span class="ͼe">SYS_ERR</span>:<br />
<span class="ͼ6">// &#8230;</span><br />
<span class="ͼ8">break</span>;<br />
}<br />
}</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="">
<div class=""></div>
</div>
</div>
</div>
</div>
<p data-start="854" data-end="861">如果有人传入：</p>
<div class="relative w-full mt-4 mb-1">
<div class="">
<div class="relative">
<div class="h-full min-h-0 min-w-0">
<div class="h-full min-h-0 min-w-0">
<div class="border border-token-border-light border-radius-3xl corner-superellipse/1.1 rounded-3xl">
<div class="h-full w-full border-radius-3xl bg-token-bg-elevated-secondary corner-superellipse/1.1 overflow-clip rounded-3xl lxnfua_clipPathFallback">
<div class="pointer-events-none absolute inset-x-4 top-12 bottom-4">
<div class="pointer-events-none sticky z-40 shrink-0 z-1!">
<div class="sticky bg-token-border-light"></div>
</div>
</div>
<div class="">
<div class="relative z-0 flex max-w-full">
<div id="code-block-viewer" class="q9tKkq_viewer cm-editor z-10 light:cm-light dark:cm-light flex h-full w-full flex-col items-stretch ͼ5 ͼj" dir="ltr">
<div class="cm-scroller">
<div class="cm-content q9tKkq_readonly"><span class="ͼe">process</span>((<span class="ͼe">SysState</span>)<span class="ͼb">99</span>);</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="">
<div class=""></div>
</div>
</div>
</div>
</div>
<p data-start="896" data-end="899">那么：</p>
<ul data-start="901" data-end="943">
<li data-section-id="1s6j7pf" data-start="901" data-end="907">
<p data-start="903" data-end="907">不会报错</p>
</li>
<li data-section-id="1s6cfo5" data-start="908" data-end="914">
<p data-start="910" data-end="914">不会崩溃</p>
</li>
<li data-section-id="1i937dr" data-start="915" data-end="931">
<p data-start="917" data-end="931"><code data-start="917" data-end="925">switch</code> 没有匹配项</p>
</li>
<li data-section-id="1n1ezmd" data-start="932" data-end="943">
<p data-start="934" data-end="943">函数可能什么都不做</p>
</li>
</ul>
<p data-start="945" data-end="965">这类 bug 在嵌入式里很常见，尤其是：</p>
<ul data-start="967" data-end="1020">
<li data-section-id="188n2ww" data-start="967" data-end="978">
<p data-start="969" data-end="978">Flash 读配置</p>
</li>
<li data-section-id="1k62tik" data-start="979" data-end="995">
<p data-start="981" data-end="995">UART/CAN 收协议字段</p>
</li>
<li data-section-id="1dqyehc" data-start="996" data-end="1009">
<p data-start="998" data-end="1009">EEPROM 恢复状态</p>
</li>
<li data-section-id="1yztdul" data-start="1010" data-end="1020">
<p data-start="1012" data-end="1020">上位机下发模式值</p>
</li>
</ul>
<hr data-start="1022" data-end="1025" />
<h2 data-section-id="zj774z" data-start="1027" data-end="1044">3. C 标准层面再严谨一点</h2>
<p data-start="1046" data-end="1076">C 标准并没有要求“枚举变量必须只能持有枚举器中列出的值”。</p>
<p data-start="1078" data-end="1091">但是它会涉及一个更细的点：</p>
<h3 data-section-id="13j7baf" data-start="1093" data-end="1112">枚举类型有一个底层整数表示范围</h3>
<p data-start="1114" data-end="1160">如果你强转进去的整数值 <strong data-start="1126" data-end="1143">落在该枚举类型可表示范围内</strong>，通常就只是一个“未命名枚举值”。</p>
<p data-start="1162" data-end="1199">如果 <strong data-start="1165" data-end="1180">超出该实现能表示的范围</strong>，结果就可能变成实现定义，甚至不可靠。</p>
<p data-start="1201" data-end="1219">不过在绝大多数 STM32 开发里：</p>
<ul data-start="1221" data-end="1271">
<li data-section-id="1vd7u6b" data-start="1221" data-end="1242">
<p data-start="1223" data-end="1242"><code data-start="1223" data-end="1229">enum</code> 往往按 <code data-start="1234" data-end="1239">int</code> 处理</p>
</li>
<li data-section-id="10vkwoq" data-start="1243" data-end="1271">
<p data-start="1245" data-end="1271"><code data-start="1245" data-end="1259">0、1、2、99、255</code> 这种小值一般都能存进去</p>
</li>
</ul>
<p data-start="1273" data-end="1286">所以日常看到的现象通常是：</p>
<blockquote data-start="1288" data-end="1311">
<p data-start="1290" data-end="1311">“可以存，能比较，能打印，但逻辑上非法。”</p>
</blockquote>
<hr data-start="1313" data-end="1316" />
<h2 data-section-id="oszekz" data-start="1318" data-end="1337">4. C++ 里的普通 enum</h2>
<p data-start="1339" data-end="1362">C++ 里的传统 <code data-start="1348" data-end="1354">enum</code> 和 C 很像：</p>
<div class="relative w-full mt-4 mb-1">
<div class="">
<div class="relative">
<div class="h-full min-h-0 min-w-0">
<div class="h-full min-h-0 min-w-0">
<div class="border border-token-border-light border-radius-3xl corner-superellipse/1.1 rounded-3xl">
<div class="h-full w-full border-radius-3xl bg-token-bg-elevated-secondary corner-superellipse/1.1 overflow-clip rounded-3xl lxnfua_clipPathFallback">
<div class="pointer-events-none absolute inset-x-4 top-12 bottom-4">
<div class="pointer-events-none sticky z-40 shrink-0 z-1!">
<div class="sticky bg-token-border-light"></div>
</div>
</div>
<div class="">
<div class="relative z-0 flex max-w-full">
<div id="code-block-viewer" class="q9tKkq_viewer cm-editor z-10 light:cm-light dark:cm-light flex h-full w-full flex-col items-stretch ͼ5 ͼj" dir="ltr">
<div class="cm-scroller">
<div class="cm-content q9tKkq_readonly"><span class="ͼ8">enum</span> <span class="ͼe">Color</span><br />
{<br />
<span class="ͼe">RED</span> = <span class="ͼb">0</span>,<br />
<span class="ͼe">GREEN</span> = <span class="ͼb">1</span>,<br />
<span class="ͼe">BLUE</span> = <span class="ͼb">2</span><br />
};</p>
<p><span class="ͼe">Color</span> <span class="ͼe">c</span> = (<span class="ͼe">Color</span>)<span class="ͼb">5</span>;</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="">
<div class=""></div>
</div>
</div>
</div>
</div>
<p data-start="1454" data-end="1462">这通常也能成立。</p>
<p data-start="1464" data-end="1501">区别主要在于类型检查会稍微严格一些，但只要你显式强转，编译器一般还是接受。</p>
<p data-start="1503" data-end="1531">所以 <strong data-start="1506" data-end="1530">C++ 的传统 enum 也并不天然安全</strong>。</p>
<hr data-start="1533" data-end="1536" />
<h2 data-section-id="1plruxi" data-start="1538" data-end="1570">5. C++11 的 enum class 才更“强类型”</h2>
<p data-start="1572" data-end="1575">例如：</p>
<div class="relative w-full mt-4 mb-1">
<div class="">
<div class="relative">
<div class="h-full min-h-0 min-w-0">
<div class="h-full min-h-0 min-w-0">
<div class="border border-token-border-light border-radius-3xl corner-superellipse/1.1 rounded-3xl">
<div class="h-full w-full border-radius-3xl bg-token-bg-elevated-secondary corner-superellipse/1.1 overflow-clip rounded-3xl lxnfua_clipPathFallback">
<div class="pointer-events-none absolute inset-x-4 top-12 bottom-4">
<div class="pointer-events-none sticky z-40 shrink-0 z-1!">
<div class="sticky bg-token-border-light"></div>
</div>
</div>
<div class="">
<div class="relative z-0 flex max-w-full">
<div id="code-block-viewer" class="q9tKkq_viewer cm-editor z-10 light:cm-light dark:cm-light flex h-full w-full flex-col items-stretch ͼ5 ͼj" dir="ltr">
<div class="cm-scroller">
<div class="cm-content q9tKkq_readonly"><span class="ͼ8">enum</span> <span class="ͼ8">class</span> <span class="ͼe">Color</span> : <span class="ͼe">uint8_t</span><br />
{<br />
<span class="ͼe">Red</span> = <span class="ͼb">0</span>,<br />
<span class="ͼe">Green</span> = <span class="ͼb">1</span>,<br />
<span class="ͼe">Blue</span> = <span class="ͼb">2</span><br />
};</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="">
<div class=""></div>
</div>
</div>
</div>
</div>
<p data-start="1662" data-end="1669">它有几个特点：</p>
<h3 data-section-id="14jd84m" data-start="1671" data-end="1685">不能隐式转成 int</h3>
<div class="relative w-full mt-4 mb-1">
<div class="">
<div class="relative">
<div class="h-full min-h-0 min-w-0">
<div class="h-full min-h-0 min-w-0">
<div class="border border-token-border-light border-radius-3xl corner-superellipse/1.1 rounded-3xl">
<div class="h-full w-full border-radius-3xl bg-token-bg-elevated-secondary corner-superellipse/1.1 overflow-clip rounded-3xl lxnfua_clipPathFallback">
<div class="pointer-events-none absolute inset-x-4 top-12 bottom-4">
<div class="pointer-events-none sticky z-40 shrink-0 z-1!">
<div class="sticky bg-token-border-light"></div>
</div>
</div>
<div class="">
<div class="relative z-0 flex max-w-full">
<div id="code-block-viewer" class="q9tKkq_viewer cm-editor z-10 light:cm-light dark:cm-light flex h-full w-full flex-col items-stretch ͼ5 ͼj" dir="ltr">
<div class="cm-scroller">
<div class="cm-content q9tKkq_readonly"><span class="ͼe">Color</span> <span class="ͼe">c</span> = Color::<span class="ͼe">Red</span>;<br />
<span class="ͼ6">// int x = c; // 编译错误</span><br />
<span class="ͼe">int</span> <span class="ͼe">x</span> = (<span class="ͼe">int</span>)<span class="ͼe">c</span>; <span class="ͼ6">// 需要显式转换</span></div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="">
<div class=""></div>
</div>
</div>
</div>
</div>
<h3 data-section-id="1mc84ji" data-start="1770" data-end="1788">不会把枚举项污染外层命名空间</h3>
<div class="relative w-full mt-4 mb-1">
<div class="">
<div class="relative">
<div class="h-full min-h-0 min-w-0">
<div class="h-full min-h-0 min-w-0">
<div class="border border-token-border-light border-radius-3xl corner-superellipse/1.1 rounded-3xl">
<div class="h-full w-full border-radius-3xl bg-token-bg-elevated-secondary corner-superellipse/1.1 overflow-clip rounded-3xl lxnfua_clipPathFallback">
<div class="pointer-events-none absolute inset-x-4 top-12 bottom-4">
<div class="pointer-events-none sticky z-40 shrink-0 z-1!">
<div class="sticky bg-token-border-light"></div>
</div>
</div>
<div class="">
<div class="relative z-0 flex max-w-full">
<div id="code-block-viewer" class="q9tKkq_viewer cm-editor z-10 light:cm-light dark:cm-light flex h-full w-full flex-col items-stretch ͼ5 ͼj" dir="ltr">
<div class="cm-scroller">
<div class="cm-content q9tKkq_readonly"><span class="ͼe">Color</span> <span class="ͼe">c</span> = Color::<span class="ͼe">Red</span>;</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="">
<div class=""></div>
</div>
</div>
</div>
</div>
<p data-start="1823" data-end="1836">而不是直接写 <code data-start="1830" data-end="1835">RED</code>。</p>
<h3 data-section-id="1yae7tz" data-start="1838" data-end="1845">但注意</h3>
<p data-start="1846" data-end="1872">即使是 <code data-start="1850" data-end="1862">enum class</code>，你仍然可以这样写：</p>
<div class="relative w-full mt-4 mb-1">
<div class="">
<div class="relative">
<div class="h-full min-h-0 min-w-0">
<div class="h-full min-h-0 min-w-0">
<div class="border border-token-border-light border-radius-3xl corner-superellipse/1.1 rounded-3xl">
<div class="h-full w-full border-radius-3xl bg-token-bg-elevated-secondary corner-superellipse/1.1 overflow-clip rounded-3xl lxnfua_clipPathFallback">
<div class="pointer-events-none absolute inset-x-4 top-12 bottom-4">
<div class="pointer-events-none sticky z-40 shrink-0 z-1!">
<div class="sticky bg-token-border-light"></div>
</div>
</div>
<div class="">
<div class="relative z-0 flex max-w-full">
<div id="code-block-viewer" class="q9tKkq_viewer cm-editor z-10 light:cm-light dark:cm-light flex h-full w-full flex-col items-stretch ͼ5 ͼj" dir="ltr">
<div class="cm-scroller">
<div class="cm-content q9tKkq_readonly"><span class="ͼe">Color</span> <span class="ͼe">c</span> = (<span class="ͼe">Color</span>)<span class="ͼb">5</span>;</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="">
<div class=""></div>
</div>
</div>
</div>
</div>
<p data-start="1906" data-end="1909">或者：</p>
<div class="relative w-full mt-4 mb-1">
<div class="">
<div class="relative">
<div class="h-full min-h-0 min-w-0">
<div class="h-full min-h-0 min-w-0">
<div class="border border-token-border-light border-radius-3xl corner-superellipse/1.1 rounded-3xl">
<div class="h-full w-full border-radius-3xl bg-token-bg-elevated-secondary corner-superellipse/1.1 overflow-clip rounded-3xl lxnfua_clipPathFallback">
<div class="pointer-events-none absolute inset-x-4 top-12 bottom-4">
<div class="pointer-events-none sticky z-40 shrink-0 z-1!">
<div class="sticky bg-token-border-light"></div>
</div>
</div>
<div class="">
<div class="relative z-0 flex max-w-full">
<div id="code-block-viewer" class="q9tKkq_viewer cm-editor z-10 light:cm-light dark:cm-light flex h-full w-full flex-col items-stretch ͼ5 ͼj" dir="ltr">
<div class="cm-scroller">
<div class="cm-content q9tKkq_readonly"><span class="ͼe">Color</span> <span class="ͼe">c</span> = <span class="ͼe">static_cast</span><span class="ͼg">&lt;</span><span class="ͼe">Color</span><span class="ͼg">&gt;</span>(<span class="ͼb">5</span>);</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="">
<div class=""></div>
</div>
</div>
</div>
</div>
<p data-start="1956" data-end="1978">这仍然可能得到一个“不对应任何枚举项”的值。</p>
<p data-start="1980" data-end="1983">所以：</p>
<blockquote data-start="1985" data-end="2031">
<p data-start="1987" data-end="2031"><code data-start="1987" data-end="1999">enum class</code> 改善的是类型安全和命名安全，<strong data-start="2014" data-end="2030">不是自动帮你做合法性检查</strong>。</p>
</blockquote>
<p data-start="2033" data-end="2041">这一点非常重要。</p>
<hr data-start="2043" data-end="2046" />
<h2 data-section-id="xw8lg9" data-start="2048" data-end="2074">6. C 和 C++ 的核心差异，最实用的理解</h2>
<p data-start="2076" data-end="2083">你可以这么记：</p>
<h3 data-section-id="1qmxbci" data-start="2085" data-end="2097">C 的 enum</h3>
<ul data-start="2098" data-end="2130">
<li data-section-id="c4wlak" data-start="2098" data-end="2105">
<p data-start="2100" data-end="2105">更接近整数</p>
</li>
<li data-section-id="oshx1s" data-start="2106" data-end="2113">
<p data-start="2108" data-end="2113">类型约束弱</p>
</li>
<li data-section-id="160vhib" data-start="2114" data-end="2130">
<p data-start="2116" data-end="2130">很容易出现“非法但可存”的值</p>
</li>
</ul>
<h3 data-section-id="jl4fpp" data-start="2132" data-end="2148">C++ 的传统 enum</h3>
<ul data-start="2149" data-end="2185">
<li data-section-id="4jt2lt" data-start="2149" data-end="2159">
<p data-start="2151" data-end="2159">比 C 稍强一点</p>
</li>
<li data-section-id="2l1x0h" data-start="2160" data-end="2171">
<p data-start="2162" data-end="2171">但本质仍然接近整数</p>
</li>
<li data-section-id="t4pozr" data-start="2172" data-end="2185">
<p data-start="2174" data-end="2185">强转后同样可能有非法值</p>
</li>
</ul>
<h3 data-section-id="1cl0ikc" data-start="2187" data-end="2207">C++ 的 enum class</h3>
<ul data-start="2208" data-end="2244">
<li data-section-id="h44x4f" data-start="2208" data-end="2215">
<p data-start="2210" data-end="2215">强类型得多</p>
</li>
<li data-section-id="1iydwin" data-start="2216" data-end="2225">
<p data-start="2218" data-end="2225">不会乱隐式转换</p>
</li>
<li data-section-id="sdhgs" data-start="2226" data-end="2244">
<p data-start="2228" data-end="2244">但显式强转后仍可能得到非法枚举值</p>
</li>
</ul>
<hr data-start="2246" data-end="2249" />
<h2 data-section-id="4dvmv9" data-start="2251" data-end="2285">7. GCC / ARMClang / Keil 下的实际表现</h2>
<p data-start="2287" data-end="2315">在 STM32 常见环境里，通常会遇到这些编译器/前端：</p>
<ul data-start="2317" data-end="2390">
<li data-section-id="1o4h1r" data-start="2317" data-end="2322">
<p data-start="2319" data-end="2322">GCC</p>
</li>
<li data-section-id="1uq7vk7" data-start="2323" data-end="2344">
<p data-start="2325" data-end="2344">ARMClang / armclang</p>
</li>
<li data-section-id="ke9q8t" data-start="2345" data-end="2384">
<p data-start="2347" data-end="2384">Keil MDK（现在主要也是 ARMClang，老项目可能 ARMCC）</p>
</li>
<li data-section-id="1o48ia" data-start="2385" data-end="2390">
<p data-start="2387" data-end="2390">IAR</p>
</li>
</ul>
<p data-start="2392" data-end="2419">它们在大多数默认配置下，对 enum 的行为都很类似：</p>
<h3 data-section-id="1no63w" data-start="2421" data-end="2441">现象一：通常按 int 宽度处理</h3>
<p data-start="2442" data-end="2445">例如：</p>
<div class="relative w-full mt-4 mb-1">
<div class="">
<div class="relative">
<div class="h-full min-h-0 min-w-0">
<div class="h-full min-h-0 min-w-0">
<div class="border border-token-border-light border-radius-3xl corner-superellipse/1.1 rounded-3xl">
<div class="h-full w-full border-radius-3xl bg-token-bg-elevated-secondary corner-superellipse/1.1 overflow-clip rounded-3xl lxnfua_clipPathFallback">
<div class="pointer-events-none absolute inset-x-4 top-12 bottom-4">
<div class="pointer-events-none sticky z-40 shrink-0 z-1!">
<div class="sticky bg-token-border-light"></div>
</div>
</div>
<div class="">
<div class="relative z-0 flex max-w-full">
<div id="code-block-viewer" class="q9tKkq_viewer cm-editor z-10 light:cm-light dark:cm-light flex h-full w-full flex-col items-stretch ͼ5 ͼj" dir="ltr">
<div class="cm-scroller">
<div class="cm-content q9tKkq_readonly"><span class="ͼ8">typedef</span> <span class="ͼ8">enum</span><br />
{<br />
<span class="ͼe">A</span> <span class="ͼ8">=</span> <span class="ͼb">0</span>,<br />
<span class="ͼe">B</span> <span class="ͼ8">=</span> <span class="ͼb">1</span>,<br />
<span class="ͼe">C</span> <span class="ͼ8">=</span> <span class="ͼb">2</span><br />
} <span class="ͼe">MyEnum</span>;</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="">
<div class=""></div>
</div>
</div>
</div>
</div>
<p data-start="2514" data-end="2544">很多情况下 <code data-start="2520" data-end="2536">sizeof(MyEnum)</code> 会是 <code data-start="2540" data-end="2543">4</code>。</p>
<p data-start="2546" data-end="2572">但这不是永远绝对的，因为编译器可以选合适的整数类型。</p>
<hr data-start="2574" data-end="2577" />
<h3 data-section-id="d2p61" data-start="2579" data-end="2598">现象二：强转非法值通常不会报错</h3>
<div class="relative w-full mt-4 mb-1">
<div class="">
<div class="relative">
<div class="h-full min-h-0 min-w-0">
<div class="h-full min-h-0 min-w-0">
<div class="border border-token-border-light border-radius-3xl corner-superellipse/1.1 rounded-3xl">
<div class="h-full w-full border-radius-3xl bg-token-bg-elevated-secondary corner-superellipse/1.1 overflow-clip rounded-3xl lxnfua_clipPathFallback">
<div class="pointer-events-none absolute inset-x-4 top-12 bottom-4">
<div class="pointer-events-none sticky z-40 shrink-0 z-1!">
<div class="sticky bg-token-border-light"></div>
</div>
</div>
<div class="">
<div class="relative z-0 flex max-w-full">
<div id="code-block-viewer" class="q9tKkq_viewer cm-editor z-10 light:cm-light dark:cm-light flex h-full w-full flex-col items-stretch ͼ5 ͼj" dir="ltr">
<div class="cm-scroller">
<div class="cm-content q9tKkq_readonly"><span class="ͼe">MyEnum</span> <span class="ͼe">e</span> <span class="ͼ8">=</span> (<span class="ͼe">MyEnum</span>)<span class="ͼb">100</span>;</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="">
<div class=""></div>
</div>
</div>
</div>
</div>
<p data-start="2633" data-end="2642">一般照样编译通过。</p>
<hr data-start="2644" data-end="2647" />
<h3 data-section-id="ce9bzk" data-start="2649" data-end="2670">现象三：switch 优化可能埋坑</h3>
<p data-start="2671" data-end="2677">看这个例子：</p>
<div class="relative w-full mt-4 mb-1">
<div class="">
<div class="relative">
<div class="h-full min-h-0 min-w-0">
<div class="h-full min-h-0 min-w-0">
<div class="border border-token-border-light border-radius-3xl corner-superellipse/1.1 rounded-3xl">
<div class="h-full w-full border-radius-3xl bg-token-bg-elevated-secondary corner-superellipse/1.1 overflow-clip rounded-3xl lxnfua_clipPathFallback">
<div class="pointer-events-none absolute inset-x-4 top-12 bottom-4">
<div class="pointer-events-none sticky z-40 shrink-0 z-1!">
<div class="sticky bg-token-border-light"></div>
</div>
</div>
<div class="">
<div class="relative z-0 flex max-w-full">
<div id="code-block-viewer" class="q9tKkq_viewer cm-editor z-10 light:cm-light dark:cm-light flex h-full w-full flex-col items-stretch ͼ5 ͼj" dir="ltr">
<div class="cm-scroller">
<div class="cm-content q9tKkq_readonly"><span class="ͼe">int</span> <span class="ͼe">f</span>(<span class="ͼe">MyEnum</span> <span class="ͼe">e</span>)<br />
{<br />
<span class="ͼ8">switch</span> (<span class="ͼe">e</span>)<br />
{<br />
<span class="ͼ8">case</span> <span class="ͼe">A</span>: <span class="ͼ8">return</span> <span class="ͼb">1</span>;<br />
<span class="ͼ8">case</span> <span class="ͼe">B</span>: <span class="ͼ8">return</span> <span class="ͼb">2</span>;<br />
<span class="ͼ8">case</span> <span class="ͼe">C</span>: <span class="ͼ8">return</span> <span class="ͼb">3</span>;<br />
}<br />
<span class="ͼ8">return</span> <span class="ͼb">0</span>;<br />
}</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="">
<div class=""></div>
</div>
</div>
</div>
</div>
<p data-start="2816" data-end="2901">如果传进来 <code data-start="2822" data-end="2827">100</code>，大多数时候会返回 0。<br data-start="2839" data-end="2842" />但编译器在高优化下，可能基于一些假设做优化，尤其当代码写法让它认为“这个分支不可能出现”时，行为可能变得和你预期不同。</p>
<p data-start="2903" data-end="2942">在 C 里这种问题通常不算特别夸张，但在 C++ 配合更激进优化时更值得警惕。</p>
<hr data-start="2944" data-end="2947" />
<h2 data-section-id="h90nd6" data-start="2949" data-end="2980">8. <code data-start="2955" data-end="2970">-fshort-enums</code> 这类选项要特别小心</h2>
<p data-start="2982" data-end="2993">GCC 有个常见选项：</p>
<div class="relative w-full mt-4 mb-1">
<div class="">
<div class="relative">
<div class="h-full min-h-0 min-w-0">
<div class="h-full min-h-0 min-w-0">
<div class="border border-token-border-light border-radius-3xl corner-superellipse/1.1 rounded-3xl">
<div class="h-full w-full border-radius-3xl bg-token-bg-elevated-secondary corner-superellipse/1.1 overflow-clip rounded-3xl lxnfua_clipPathFallback">
<div class="pointer-events-none absolute inset-x-4 top-12 bottom-4">
<div class="pointer-events-none sticky z-40 shrink-0 z-1!">
<div class="sticky bg-token-border-light"></div>
</div>
</div>
<div class="">
<div class="relative z-0 flex max-w-full">
<div id="code-block-viewer" class="q9tKkq_viewer cm-editor z-10 light:cm-light dark:cm-light flex h-full w-full flex-col items-stretch ͼ5 ͼj" dir="ltr">
<div class="cm-scroller">
<div class="cm-content q9tKkq_readonly"><span class="ͼ8">&#8211;</span><span class="ͼe">fshort</span><span class="ͼ8">&#8211;</span><span class="ͼe">enums</span></div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="">
<div class=""></div>
</div>
</div>
</div>
</div>
<p data-start="3019" data-end="3042">它会让编译器尽量用更小的整数类型存 enum。</p>
<p data-start="3044" data-end="3065">例如原本是 4 字节，可能变成 1 字节。</p>
<p data-start="3067" data-end="3099">这在嵌入式里可能是为了省 RAM/Flash，但会带来几个风险：</p>
<h3 data-section-id="8yhiyr" data-start="3101" data-end="3117">风险 1：结构体布局变化</h3>
<div class="relative w-full mt-4 mb-1">
<div class="">
<div class="relative">
<div class="h-full min-h-0 min-w-0">
<div class="h-full min-h-0 min-w-0">
<div class="border border-token-border-light border-radius-3xl corner-superellipse/1.1 rounded-3xl">
<div class="h-full w-full border-radius-3xl bg-token-bg-elevated-secondary corner-superellipse/1.1 overflow-clip rounded-3xl lxnfua_clipPathFallback">
<div class="pointer-events-none absolute inset-x-4 top-12 bottom-4">
<div class="pointer-events-none sticky z-40 shrink-0 z-1!">
<div class="sticky bg-token-border-light"></div>
</div>
</div>
<div class="">
<div class="relative z-0 flex max-w-full">
<div id="code-block-viewer" class="q9tKkq_viewer cm-editor z-10 light:cm-light dark:cm-light flex h-full w-full flex-col items-stretch ͼ5 ͼj" dir="ltr">
<div class="cm-scroller">
<div class="cm-content q9tKkq_readonly"><span class="ͼ8">typedef</span> <span class="ͼ8">struct</span><br />
{<br />
<span class="ͼe">uint8_t</span> <span class="ͼe">a</span>;<br />
<span class="ͼe">MyEnum</span> <span class="ͼe">e</span>;<br />
<span class="ͼe">uint8_t</span> <span class="ͼe">b</span>;<br />
} <span class="ͼe">Data_t</span>;</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="">
<div class=""></div>
</div>
</div>
</div>
</div>
<p data-start="3200" data-end="3241">有无 <code data-start="3203" data-end="3218">-fshort-enums</code>，<code data-start="3219" data-end="3235">sizeof(Data_t)</code> 可能不同。</p>
<p data-start="3243" data-end="3248">这会影响：</p>
<ul data-start="3250" data-end="3295">
<li data-section-id="1g26fm" data-start="3250" data-end="3256">
<p data-start="3252" data-end="3256">通信协议</p>
</li>
<li data-section-id="x2bi7" data-start="3257" data-end="3269">
<p data-start="3259" data-end="3269">Flash 存储结构</p>
</li>
<li data-section-id="ebw9t8" data-start="3270" data-end="3281">
<p data-start="3272" data-end="3281">EEPROM 映射</p>
</li>
<li data-section-id="snf7js" data-start="3282" data-end="3295">
<p data-start="3284" data-end="3295">与上位机二进制数据对接</p>
</li>
</ul>
<hr data-start="3297" data-end="3300" />
<h3 data-section-id="iaaiac" data-start="3302" data-end="3321">风险 2：超范围值截断风险更大</h3>
<p data-start="3322" data-end="3377">如果 enum 被压成 <code data-start="3334" data-end="3343">uint8_t</code> 或 <code data-start="3346" data-end="3354">int8_t</code> 风格的底层存储，那你强转的超大值可能被截断。</p>
<p data-start="3379" data-end="3382">例如：</p>
<div class="relative w-full mt-4 mb-1">
<div class="">
<div class="relative">
<div class="h-full min-h-0 min-w-0">
<div class="h-full min-h-0 min-w-0">
<div class="border border-token-border-light border-radius-3xl corner-superellipse/1.1 rounded-3xl">
<div class="h-full w-full border-radius-3xl bg-token-bg-elevated-secondary corner-superellipse/1.1 overflow-clip rounded-3xl lxnfua_clipPathFallback">
<div class="pointer-events-none absolute inset-x-4 top-12 bottom-4">
<div class="pointer-events-none sticky z-40 shrink-0 z-1!">
<div class="sticky bg-token-border-light"></div>
</div>
</div>
<div class="">
<div class="relative z-0 flex max-w-full">
<div id="code-block-viewer" class="q9tKkq_viewer cm-editor z-10 light:cm-light dark:cm-light flex h-full w-full flex-col items-stretch ͼ5 ͼj" dir="ltr">
<div class="cm-scroller">
<div class="cm-content q9tKkq_readonly"><span class="ͼe">MyEnum</span> <span class="ͼe">e</span> <span class="ͼ8">=</span> (<span class="ͼe">MyEnum</span>)<span class="ͼb">300</span>;</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="">
<div class=""></div>
</div>
</div>
</div>
</div>
<p data-start="3418" data-end="3435">结果可能不再是你直觉里的 300。</p>
<hr data-start="3437" data-end="3440" />
<h3 data-section-id="1g13qzp" data-start="3442" data-end="3462">风险 3：跨模块 ABI 不一致</h3>
<p data-start="3463" data-end="3513">一个模块开了 <code data-start="3470" data-end="3485">-fshort-enums</code>，另一个没开，接口里又传 enum，可能出很隐蔽的问题。</p>
<p data-start="3515" data-end="3561">所以嵌入式项目里，<strong data-start="3524" data-end="3560">枚举如果用于对外接口、通信协议、存储格式，最好不要依赖其底层大小</strong>。</p>
<hr data-start="3563" data-end="3566" />
<h2 data-section-id="1ypu0xf" data-start="3568" data-end="3587">9. 在嵌入式里最危险的几个场景</h2>
<h3 data-section-id="7s42qt" data-start="3589" data-end="3613">场景 A：直接把协议字段强转成 enum</h3>
<div class="relative w-full mt-4 mb-1">
<div class="">
<div class="relative">
<div class="h-full min-h-0 min-w-0">
<div class="h-full min-h-0 min-w-0">
<div class="border border-token-border-light border-radius-3xl corner-superellipse/1.1 rounded-3xl">
<div class="h-full w-full border-radius-3xl bg-token-bg-elevated-secondary corner-superellipse/1.1 overflow-clip rounded-3xl lxnfua_clipPathFallback">
<div class="pointer-events-none absolute inset-x-4 top-12 bottom-4">
<div class="pointer-events-none sticky z-40 shrink-0 z-1!">
<div class="sticky bg-token-border-light"></div>
</div>
</div>
<div class="">
<div class="relative z-0 flex max-w-full">
<div id="code-block-viewer" class="q9tKkq_viewer cm-editor z-10 light:cm-light dark:cm-light flex h-full w-full flex-col items-stretch ͼ5 ͼj" dir="ltr">
<div class="cm-scroller">
<div class="cm-content q9tKkq_readonly"><span class="ͼe">cmd</span>.<span class="ͼe">mode</span> <span class="ͼ8">=</span> (<span class="ͼe">WorkMode</span>)<span class="ͼe">rxBuf</span>[<span class="ͼb">3</span>];</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="">
<div class=""></div>
</div>
</div>
</div>
</div>
<p data-start="3655" data-end="3684">如果协议异常、数据损坏、上位机发错值，后续状态机可能乱跑。</p>
<p data-start="3686" data-end="3690">更稳妥：</p>
<div class="relative w-full mt-4 mb-1">
<div class="">
<div class="relative">
<div class="h-full min-h-0 min-w-0">
<div class="h-full min-h-0 min-w-0">
<div class="border border-token-border-light border-radius-3xl corner-superellipse/1.1 rounded-3xl">
<div class="h-full w-full border-radius-3xl bg-token-bg-elevated-secondary corner-superellipse/1.1 overflow-clip rounded-3xl lxnfua_clipPathFallback">
<div class="pointer-events-none absolute inset-x-4 top-12 bottom-4">
<div class="pointer-events-none sticky z-40 shrink-0 z-1!">
<div class="sticky bg-token-border-light"></div>
</div>
</div>
<div class="">
<div class="relative z-0 flex max-w-full">
<div id="code-block-viewer" class="q9tKkq_viewer cm-editor z-10 light:cm-light dark:cm-light flex h-full w-full flex-col items-stretch ͼ5 ͼj" dir="ltr">
<div class="cm-scroller">
<div class="cm-content q9tKkq_readonly"><span class="ͼe">uint8_t</span> <span class="ͼe">raw</span> <span class="ͼ8">=</span> <span class="ͼe">rxBuf</span>[<span class="ͼb">3</span>];<br />
<span class="ͼ8">if</span> (<span class="ͼe">raw</span> <span class="ͼ8">&lt;=</span> <span class="ͼe">WORK_MODE_MAX</span>)<br />
{<br />
<span class="ͼe">cmd</span>.<span class="ͼe">mode</span> <span class="ͼ8">=</span> (<span class="ͼe">WorkMode</span>)<span class="ͼe">raw</span>;<br />
}<br />
<span class="ͼ8">else</span><br />
{<br />
<span class="ͼe">cmd</span>.<span class="ͼe">mode</span> <span class="ͼ8">=</span> <span class="ͼe">WORK_MODE_INVALID</span>;<br />
}</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="">
<div class=""></div>
</div>
</div>
</div>
</div>
<hr data-start="3829" data-end="3832" />
<h3 data-section-id="djvwn" data-start="3834" data-end="3870">场景 B：把 Flash/EEPROM 的脏数据恢复成 enum</h3>
<p data-start="3871" data-end="3886">掉电后存储区可能不是你预期值。</p>
<div class="relative w-full mt-4 mb-1">
<div class="">
<div class="relative">
<div class="h-full min-h-0 min-w-0">
<div class="h-full min-h-0 min-w-0">
<div class="border border-token-border-light border-radius-3xl corner-superellipse/1.1 rounded-3xl">
<div class="h-full w-full border-radius-3xl bg-token-bg-elevated-secondary corner-superellipse/1.1 overflow-clip rounded-3xl lxnfua_clipPathFallback">
<div class="pointer-events-none absolute inset-x-4 top-12 bottom-4">
<div class="pointer-events-none sticky z-40 shrink-0 z-1!">
<div class="sticky bg-token-border-light"></div>
</div>
</div>
<div class="">
<div class="relative z-0 flex max-w-full">
<div id="code-block-viewer" class="q9tKkq_viewer cm-editor z-10 light:cm-light dark:cm-light flex h-full w-full flex-col items-stretch ͼ5 ͼj" dir="ltr">
<div class="cm-scroller">
<div class="cm-content q9tKkq_readonly"><span class="ͼe">cfg</span>.<span class="ͼe">mode</span> <span class="ͼ8">=</span> (<span class="ͼe">WorkMode</span>)<span class="ͼe">flash_data</span>.<span class="ͼe">mode</span>;</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="">
<div class=""></div>
</div>
</div>
</div>
</div>
<p data-start="3936" data-end="3942">最好加校验。</p>
<hr data-start="3944" data-end="3947" />
<h3 data-section-id="1mm7lrv" data-start="3949" data-end="3975">场景 C：switch 没有 default</h3>
<div class="relative w-full mt-4 mb-1">
<div class="">
<div class="relative">
<div class="h-full min-h-0 min-w-0">
<div class="h-full min-h-0 min-w-0">
<div class="border border-token-border-light border-radius-3xl corner-superellipse/1.1 rounded-3xl">
<div class="h-full w-full border-radius-3xl bg-token-bg-elevated-secondary corner-superellipse/1.1 overflow-clip rounded-3xl lxnfua_clipPathFallback">
<div class="pointer-events-none absolute inset-x-4 top-12 bottom-4">
<div class="pointer-events-none sticky z-40 shrink-0 z-1!">
<div class="sticky bg-token-border-light"></div>
</div>
</div>
<div class="">
<div class="relative z-0 flex max-w-full">
<div id="code-block-viewer" class="q9tKkq_viewer cm-editor z-10 light:cm-light dark:cm-light flex h-full w-full flex-col items-stretch ͼ5 ͼj" dir="ltr">
<div class="cm-scroller">
<div class="cm-content q9tKkq_readonly"><span class="ͼ8">switch</span> (<span class="ͼe">state</span>)<br />
{<br />
<span class="ͼ8">case</span> <span class="ͼe">S0</span>: &#8230;<br />
<span class="ͼ8">case</span> <span class="ͼe">S1</span>: &#8230;<br />
<span class="ͼ8">case</span> <span class="ͼe">S2</span>: &#8230;<br />
}</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="">
<div class=""></div>
</div>
</div>
</div>
</div>
<p data-start="4044" data-end="4061">如果状态非法，什么都不做，很难查。</p>
<p data-start="4063" data-end="4070">嵌入式里推荐：</p>
<div class="relative w-full mt-4 mb-1">
<div class="">
<div class="relative">
<div class="h-full min-h-0 min-w-0">
<div class="h-full min-h-0 min-w-0">
<div class="border border-token-border-light border-radius-3xl corner-superellipse/1.1 rounded-3xl">
<div class="h-full w-full border-radius-3xl bg-token-bg-elevated-secondary corner-superellipse/1.1 overflow-clip rounded-3xl lxnfua_clipPathFallback">
<div class="pointer-events-none absolute inset-x-4 top-12 bottom-4">
<div class="pointer-events-none sticky z-40 shrink-0 z-1!">
<div class="sticky bg-token-border-light"></div>
</div>
</div>
<div class="">
<div class="relative z-0 flex max-w-full">
<div id="code-block-viewer" class="q9tKkq_viewer cm-editor z-10 light:cm-light dark:cm-light flex h-full w-full flex-col items-stretch ͼ5 ͼj" dir="ltr">
<div class="cm-scroller">
<div class="cm-content q9tKkq_readonly"><span class="ͼ8">switch</span> (<span class="ͼe">state</span>)<br />
{<br />
<span class="ͼ8">case</span> <span class="ͼe">S0</span>: &#8230;<br />
<span class="ͼ8">break</span>;<br />
<span class="ͼ8">case</span> <span class="ͼe">S1</span>: &#8230;<br />
<span class="ͼ8">break</span>;<br />
<span class="ͼ8">case</span> <span class="ͼe">S2</span>: &#8230;<br />
<span class="ͼ8">break</span>;<br />
<span class="ͼ8">default</span>:<br />
<span class="ͼ6">// 错误处理、回退默认状态、计数告警</span><br />
<span class="ͼ8">break</span>;<br />
}</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="">
<div class=""></div>
</div>
</div>
</div>
</div>
<hr data-start="4217" data-end="4220" />
<h2 data-section-id="1jdj3qn" data-start="4222" data-end="4241">10. 编译器告警能帮到什么程度</h2>
<p data-start="4243" data-end="4253">你可以打开一些告警：</p>
<h3 data-section-id="1slfy5k" data-start="4255" data-end="4276">GCC / Clang 常见有用项</h3>
<ul data-start="4277" data-end="4346">
<li data-section-id="djn8dv" data-start="4277" data-end="4286">
<p data-start="4279" data-end="4286"><code data-start="4279" data-end="4286">-Wall</code></p>
</li>
<li data-section-id="1e326yw" data-start="4287" data-end="4298">
<p data-start="4289" data-end="4298"><code data-start="4289" data-end="4298">-Wextra</code></p>
</li>
<li data-section-id="4p8668" data-start="4299" data-end="4311">
<p data-start="4301" data-end="4311"><code data-start="4301" data-end="4311">-Wswitch</code></p>
</li>
<li data-section-id="6286v2" data-start="4312" data-end="4329">
<p data-start="4314" data-end="4329"><code data-start="4314" data-end="4329">-Wswitch-enum</code></p>
</li>
<li data-section-id="1d6hka" data-start="4330" data-end="4346">
<p data-start="4332" data-end="4346"><code data-start="4332" data-end="4346">-Wconversion</code></p>
</li>
</ul>
<p data-start="4348" data-end="4351">其中：</p>
<ul data-start="4353" data-end="4424">
<li data-section-id="uum1o0" data-start="4353" data-end="4399">
<p data-start="4355" data-end="4399"><code data-start="4355" data-end="4370">-Wswitch-enum</code> 能提示 <code data-start="4375" data-end="4389">switch(enum)</code> 是否漏掉某些枚举项</p>
</li>
<li data-section-id="1dhovem" data-start="4400" data-end="4424">
<p data-start="4402" data-end="4424">但它<strong data-start="4404" data-end="4424">不能阻止非法整数强转成 enum</strong></p>
</li>
</ul>
<p data-start="4426" data-end="4454">所以编译器只能帮一部分，<strong data-start="4438" data-end="4453">值合法性还是得你自己查</strong>。</p>
<hr data-start="4456" data-end="4459" />
<h2 data-section-id="16ip3oa" data-start="4461" data-end="4476">11. 最推荐的安全写法</h2>
<h3 data-section-id="1yuwm3v" data-start="4478" data-end="4498">方法一：定义 INVALID 项</h3>
<p data-start="4499" data-end="4510">这是嵌入式里最实用的。</p>
<div class="relative w-full mt-4 mb-1">
<div class="">
<div class="relative">
<div class="h-full min-h-0 min-w-0">
<div class="h-full min-h-0 min-w-0">
<div class="border border-token-border-light border-radius-3xl corner-superellipse/1.1 rounded-3xl">
<div class="h-full w-full border-radius-3xl bg-token-bg-elevated-secondary corner-superellipse/1.1 overflow-clip rounded-3xl lxnfua_clipPathFallback">
<div class="pointer-events-none absolute inset-x-4 top-12 bottom-4">
<div class="pointer-events-none sticky z-40 shrink-0 z-1!">
<div class="sticky bg-token-border-light"></div>
</div>
</div>
<div class="">
<div class="relative z-0 flex max-w-full">
<div id="code-block-viewer" class="q9tKkq_viewer cm-editor z-10 light:cm-light dark:cm-light flex h-full w-full flex-col items-stretch ͼ5 ͼj" dir="ltr">
<div class="cm-scroller">
<div class="cm-content q9tKkq_readonly"><span class="ͼ8">typedef</span> <span class="ͼ8">enum</span><br />
{<br />
<span class="ͼe">MODE_IDLE</span> <span class="ͼ8">=</span> <span class="ͼb">0</span>,<br />
<span class="ͼe">MODE_RUN</span> <span class="ͼ8">=</span> <span class="ͼb">1</span>,<br />
<span class="ͼe">MODE_STOP</span> <span class="ͼ8">=</span> <span class="ͼb">2</span>,<br />
<span class="ͼe">MODE_INVALID</span> <span class="ͼ8">=</span> <span class="ͼb">255</span><br />
} <span class="ͼe">Mode</span>;</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="">
<div class=""></div>
</div>
</div>
</div>
</div>
<p data-start="4625" data-end="4628">然后：</p>
<div class="relative w-full mt-4 mb-1">
<div class="">
<div class="relative">
<div class="h-full min-h-0 min-w-0">
<div class="h-full min-h-0 min-w-0">
<div class="border border-token-border-light border-radius-3xl corner-superellipse/1.1 rounded-3xl">
<div class="h-full w-full border-radius-3xl bg-token-bg-elevated-secondary corner-superellipse/1.1 overflow-clip rounded-3xl lxnfua_clipPathFallback">
<div class="pointer-events-none absolute inset-x-4 top-12 bottom-4">
<div class="pointer-events-none sticky z-40 shrink-0 z-1!">
<div class="sticky bg-token-border-light"></div>
</div>
</div>
<div class="">
<div class="relative z-0 flex max-w-full">
<div id="code-block-viewer" class="q9tKkq_viewer cm-editor z-10 light:cm-light dark:cm-light flex h-full w-full flex-col items-stretch ͼ5 ͼj" dir="ltr">
<div class="cm-scroller">
<div class="cm-content q9tKkq_readonly"><span class="ͼ8">static</span> <span class="ͼ8">inline</span> <span class="ͼe">Mode</span> <span class="ͼe">Mode_FromInt</span>(<span class="ͼe">int</span> <span class="ͼe">x</span>)<br />
{<br />
<span class="ͼ8">switch</span> (<span class="ͼe">x</span>)<br />
{<br />
<span class="ͼ8">case</span> <span class="ͼe">MODE_IDLE</span>: <span class="ͼ8">return</span> <span class="ͼe">MODE_IDLE</span>;<br />
<span class="ͼ8">case</span> <span class="ͼe">MODE_RUN</span>: <span class="ͼ8">return</span> <span class="ͼe">MODE_RUN</span>;<br />
<span class="ͼ8">case</span> <span class="ͼe">MODE_STOP</span>: <span class="ͼ8">return</span> <span class="ͼe">MODE_STOP</span>;<br />
<span class="ͼ8">default</span>: <span class="ͼ8">return</span> <span class="ͼe">MODE_INVALID</span>;<br />
}<br />
}</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="">
<div class=""></div>
</div>
</div>
</div>
</div>
<hr data-start="4864" data-end="4867" />
<h3 data-section-id="14qqw7b" data-start="4869" data-end="4890">方法二：配套 IsValid 函数</h3>
<div class="relative w-full mt-4 mb-1">
<div class="">
<div class="relative">
<div class="h-full min-h-0 min-w-0">
<div class="h-full min-h-0 min-w-0">
<div class="border border-token-border-light border-radius-3xl corner-superellipse/1.1 rounded-3xl">
<div class="h-full w-full border-radius-3xl bg-token-bg-elevated-secondary corner-superellipse/1.1 overflow-clip rounded-3xl lxnfua_clipPathFallback">
<div class="pointer-events-none absolute inset-x-4 top-12 bottom-4">
<div class="pointer-events-none sticky z-40 shrink-0 z-1!">
<div class="sticky bg-token-border-light"></div>
</div>
</div>
<div class="">
<div class="relative z-0 flex max-w-full">
<div id="code-block-viewer" class="q9tKkq_viewer cm-editor z-10 light:cm-light dark:cm-light flex h-full w-full flex-col items-stretch ͼ5 ͼj" dir="ltr">
<div class="cm-scroller">
<div class="cm-content q9tKkq_readonly"><span class="ͼ8">static</span> <span class="ͼ8">inline</span> <span class="ͼe">int</span> <span class="ͼe">Mode_IsValid</span>(<span class="ͼe">int</span> <span class="ͼe">x</span>)<br />
{<br />
<span class="ͼ8">return</span> (<span class="ͼe">x</span> <span class="ͼ8">==</span> <span class="ͼe">MODE_IDLE</span>) <span class="ͼ8">||</span><br />
(<span class="ͼe">x</span> <span class="ͼ8">==</span> <span class="ͼe">MODE_RUN</span>) <span class="ͼ8">||</span><br />
(<span class="ͼe">x</span> <span class="ͼ8">==</span> <span class="ͼe">MODE_STOP</span>);<br />
}</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="">
<div class=""></div>
</div>
</div>
</div>
</div>
<p data-start="5034" data-end="5038">使用时：</p>
<div class="relative w-full mt-4 mb-1">
<div class="">
<div class="relative">
<div class="h-full min-h-0 min-w-0">
<div class="h-full min-h-0 min-w-0">
<div class="border border-token-border-light border-radius-3xl corner-superellipse/1.1 rounded-3xl">
<div class="h-full w-full border-radius-3xl bg-token-bg-elevated-secondary corner-superellipse/1.1 overflow-clip rounded-3xl lxnfua_clipPathFallback">
<div class="pointer-events-none absolute inset-x-4 top-12 bottom-4">
<div class="pointer-events-none sticky z-40 shrink-0 z-1!">
<div class="sticky bg-token-border-light"></div>
</div>
</div>
<div class="">
<div class="relative z-0 flex max-w-full">
<div id="code-block-viewer" class="q9tKkq_viewer cm-editor z-10 light:cm-light dark:cm-light flex h-full w-full flex-col items-stretch ͼ5 ͼj" dir="ltr">
<div class="cm-scroller">
<div class="cm-content q9tKkq_readonly"><span class="ͼe">int</span> <span class="ͼe">raw</span> <span class="ͼ8">=</span> <span class="ͼe">get_mode</span>();<br />
<span class="ͼ8">if</span> (<span class="ͼe">Mode_IsValid</span>(<span class="ͼe">raw</span>))<br />
{<br />
<span class="ͼe">mode</span> <span class="ͼ8">=</span> (<span class="ͼe">Mode</span>)<span class="ͼe">raw</span>;<br />
}<br />
<span class="ͼ8">else</span><br />
{<br />
<span class="ͼ6">// 错误处理</span><br />
}</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="">
<div class=""></div>
</div>
</div>
</div>
</div>
<hr data-start="5142" data-end="5145" />
<h3 data-section-id="32jyxx" data-start="5147" data-end="5165">方法三：连续枚举可做范围判断</h3>
<p data-start="5166" data-end="5177">如果你的枚举是连续的：</p>
<div class="relative w-full mt-4 mb-1">
<div class="">
<div class="relative">
<div class="h-full min-h-0 min-w-0">
<div class="h-full min-h-0 min-w-0">
<div class="border border-token-border-light border-radius-3xl corner-superellipse/1.1 rounded-3xl">
<div class="h-full w-full border-radius-3xl bg-token-bg-elevated-secondary corner-superellipse/1.1 overflow-clip rounded-3xl lxnfua_clipPathFallback">
<div class="pointer-events-none absolute inset-x-4 top-12 bottom-4">
<div class="pointer-events-none sticky z-40 shrink-0 z-1!">
<div class="sticky bg-token-border-light"></div>
</div>
</div>
<div class="">
<div class="relative z-0 flex max-w-full">
<div id="code-block-viewer" class="q9tKkq_viewer cm-editor z-10 light:cm-light dark:cm-light flex h-full w-full flex-col items-stretch ͼ5 ͼj" dir="ltr">
<div class="cm-scroller">
<div class="cm-content q9tKkq_readonly"><span class="ͼ8">typedef</span> <span class="ͼ8">enum</span><br />
{<br />
<span class="ͼe">MODE_IDLE</span> <span class="ͼ8">=</span> <span class="ͼb">0</span>,<br />
<span class="ͼe">MODE_RUN</span>,<br />
<span class="ͼe">MODE_STOP</span>,<br />
<span class="ͼe">MODE_COUNT</span><br />
} <span class="ͼe">Mode</span>;</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="">
<div class=""></div>
</div>
</div>
</div>
</div>
<p data-start="5275" data-end="5280">那么可以：</p>
<div class="relative w-full mt-4 mb-1">
<div class="">
<div class="relative">
<div class="h-full min-h-0 min-w-0">
<div class="h-full min-h-0 min-w-0">
<div class="border border-token-border-light border-radius-3xl corner-superellipse/1.1 rounded-3xl">
<div class="h-full w-full border-radius-3xl bg-token-bg-elevated-secondary corner-superellipse/1.1 overflow-clip rounded-3xl lxnfua_clipPathFallback">
<div class="pointer-events-none absolute inset-x-4 top-12 bottom-4">
<div class="pointer-events-none sticky z-40 shrink-0 z-1!">
<div class="sticky bg-token-border-light"></div>
</div>
</div>
<div class="">
<div class="relative z-0 flex max-w-full">
<div id="code-block-viewer" class="q9tKkq_viewer cm-editor z-10 light:cm-light dark:cm-light flex h-full w-full flex-col items-stretch ͼ5 ͼj" dir="ltr">
<div class="cm-scroller">
<div class="cm-content q9tKkq_readonly"><span class="ͼ8">if</span> ((<span class="ͼe">unsigned</span>)<span class="ͼe">raw</span> <span class="ͼ8">&lt;</span> <span class="ͼe">MODE_COUNT</span>)<br />
{<br />
<span class="ͼe">mode</span> <span class="ͼ8">=</span> (<span class="ͼe">Mode</span>)<span class="ͼe">raw</span>;<br />
}<br />
<span class="ͼ8">else</span><br />
{<br />
<span class="ͼ6">// invalid</span><br />
}</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="">
<div class=""></div>
</div>
</div>
</div>
</div>
<p data-start="5374" data-end="5391">这个写法很适合 STM32 项目。</p>
<p data-start="5393" data-end="5398">但前提是：</p>
<ul data-start="5400" data-end="5431">
<li data-section-id="ou9jz1" data-start="5400" data-end="5409">
<p data-start="5402" data-end="5409">枚举值必须连续</p>
</li>
<li data-section-id="1wzk480" data-start="5410" data-end="5420">
<p data-start="5412" data-end="5420">从 0 开始最好</p>
</li>
<li data-section-id="tek03v" data-start="5421" data-end="5431">
<p data-start="5423" data-end="5431">中间不能插空洞值</p>
</li>
</ul>
<hr data-start="5433" data-end="5436" />
<h2 data-section-id="16sshl0" data-start="5438" data-end="5462">12. 一个 STM32 项目里的实战建议</h2>
<p data-start="5464" data-end="5494">如果是状态机、工作模式、菜单页面这类枚举，我建议你这样设计：</p>
<div class="relative w-full mt-4 mb-1">
<div class="">
<div class="relative">
<div class="h-full min-h-0 min-w-0">
<div class="h-full min-h-0 min-w-0">
<div class="border border-token-border-light border-radius-3xl corner-superellipse/1.1 rounded-3xl">
<div class="h-full w-full border-radius-3xl bg-token-bg-elevated-secondary corner-superellipse/1.1 overflow-clip rounded-3xl lxnfua_clipPathFallback">
<div class="pointer-events-none absolute inset-x-4 top-12 bottom-4">
<div class="pointer-events-none sticky z-40 shrink-0 z-1!">
<div class="sticky bg-token-border-light"></div>
</div>
</div>
<div class="">
<div class="relative z-0 flex max-w-full">
<div id="code-block-viewer" class="q9tKkq_viewer cm-editor z-10 light:cm-light dark:cm-light flex h-full w-full flex-col items-stretch ͼ5 ͼj" dir="ltr">
<div class="cm-scroller">
<div class="cm-content q9tKkq_readonly"><span class="ͼ8">typedef</span> <span class="ͼ8">enum</span><br />
{<br />
<span class="ͼe">PAGE_HOME</span> <span class="ͼ8">=</span> <span class="ͼb">0</span>,<br />
<span class="ͼe">PAGE_MENU</span>,<br />
<span class="ͼe">PAGE_SETTING</span>,<br />
<span class="ͼe">PAGE_COUNT</span>,<br />
<span class="ͼe">PAGE_INVALID</span> <span class="ͼ8">=</span> <span class="ͼb">0xFF</span><br />
} <span class="ͼe">Page_t</span>;</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="">
<div class=""></div>
</div>
</div>
</div>
</div>
<p data-start="5623" data-end="5632">然后统一入口转换：</p>
<div class="relative w-full mt-4 mb-1">
<div class="">
<div class="relative">
<div class="h-full min-h-0 min-w-0">
<div class="h-full min-h-0 min-w-0">
<div class="border border-token-border-light border-radius-3xl corner-superellipse/1.1 rounded-3xl">
<div class="h-full w-full border-radius-3xl bg-token-bg-elevated-secondary corner-superellipse/1.1 overflow-clip rounded-3xl lxnfua_clipPathFallback">
<div class="pointer-events-none absolute inset-x-4 top-12 bottom-4">
<div class="pointer-events-none sticky z-40 shrink-0 z-1!">
<div class="sticky bg-token-border-light"></div>
</div>
</div>
<div class="">
<div class="relative z-0 flex max-w-full">
<div id="code-block-viewer" class="q9tKkq_viewer cm-editor z-10 light:cm-light dark:cm-light flex h-full w-full flex-col items-stretch ͼ5 ͼj" dir="ltr">
<div class="cm-scroller">
<div class="cm-content q9tKkq_readonly"><span class="ͼ8">static</span> <span class="ͼ8">inline</span> <span class="ͼe">Page_t</span> <span class="ͼe">Page_Parse</span>(<span class="ͼe">uint8_t</span> <span class="ͼe">raw</span>)<br />
{<br />
<span class="ͼ8">return</span> (<span class="ͼe">raw</span> <span class="ͼ8">&lt;</span> <span class="ͼe">PAGE_COUNT</span>) <span class="ͼ8">?</span> (<span class="ͼe">Page_t</span>)<span class="ͼe">raw</span> : <span class="ͼe">PAGE_INVALID</span>;<br />
}</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="">
<div class=""></div>
</div>
</div>
</div>
</div>
<p data-start="5753" data-end="5766">这样后续代码几乎不会踩坑。</p>
<hr data-start="5768" data-end="5771" />
<h2 data-section-id="1sfur0p" data-start="5773" data-end="5786">13. 总结成一句话</h2>
<h3 data-section-id="1xjqg81" data-start="5788" data-end="5797">在 C 里</h3>
<p data-start="5798" data-end="5834"><code data-start="5798" data-end="5804">enum</code> 不是“只能取这几个值”的严格集合，而更像“有名字的整数”。</p>
<h3 data-section-id="ictjnl" data-start="5836" data-end="5847">在 C++ 里</h3>
<p data-start="5848" data-end="5898"><code data-start="5848" data-end="5860">enum class</code> 更安全，但也只是防止乱隐式转换，<strong data-start="5877" data-end="5897">不负责检查你强转进去的值是否合法</strong>。</p>
<h3 data-section-id="1d8jqn1" data-start="5900" data-end="5934">在 GCC / ARMClang / Keil 嵌入式环境里</h3>
<p data-start="5935" data-end="5979">非法整数强转成枚举，通常不会立刻出错；真正的风险在于后续逻辑 silently 出问题。</p>
<hr data-start="5981" data-end="5984" />
<h2 data-section-id="1sufs81" data-start="5986" data-end="6006">14. 你可以直接记住这条工程规则</h2>
<p data-start="6008" data-end="6054"><strong data-start="6008" data-end="6054">凡是从外部输入、存储恢复、通信报文、寄存器解析得到的整数，转 enum 前都先校验。</strong></p>
<p data-start="6056" data-end="6062">这是最稳的。</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>STM32+CubeMX 配置ADC配合DMA多通道多次采样并自动取平均</title>
		<link>https://e673.com/stm32cubemx-adc-with-dma-multiple-channel-average-oversample/</link>
		
		<dc:creator><![CDATA[ahe]]></dc:creator>
		<pubDate>Sun, 01 Mar 2026 08:10:34 +0000</pubDate>
				<category><![CDATA[应用笔记]]></category>
		<category><![CDATA[经验分享]]></category>
		<category><![CDATA[ARM]]></category>
		<category><![CDATA[嵌入程序]]></category>
		<guid isPermaLink="false">https://e673.com/?p=3096</guid>

					<description><![CDATA[一、推荐参数 先用这一组，比较稳&#46;&#46;&#46;]]></description>
										<content:encoded><![CDATA[<h2 data-start="284" data-end="305">一、推荐参数</h2>
<p data-start="307" data-end="317">先用这一组，比较稳：</p>
<ul data-start="319" data-end="374">
<li data-start="319" data-end="348">
<p data-start="321" data-end="348"><strong data-start="321" data-end="348">Oversampling Ratio = 8x</strong></p>
</li>
<li data-start="349" data-end="374">
<p data-start="351" data-end="374"><strong data-start="351" data-end="374">Right Bit Shift = 3</strong></p>
</li>
</ul>
<p data-start="376" data-end="381">这等价于：</p>
<ul data-start="383" data-end="438">
<li data-start="383" data-end="396">
<p data-start="385" data-end="396">每个通道内部采 8 次</p>
</li>
<li data-start="397" data-end="415">
<p data-start="399" data-end="415">累加后右移 3 位（即除以 8）</p>
</li>
<li data-start="416" data-end="438">
<p data-start="418" data-end="438">输出结果仍保持接近 <strong data-start="428" data-end="438">12 位量程</strong></p>
</li>
</ul>
<p data-start="440" data-end="545">也就是说，<strong data-start="445" data-end="463">你拿到的就是“8 次平均值”</strong>。STM32L4 的硬件过采样支持把输出当作 averaging function 来用。</p>
<p data-start="547" data-end="561">如果你更想抑制噪声，可改成：</p>
<ul data-start="563" data-end="582">
<li data-start="563" data-end="582">
<p data-start="565" data-end="582"><strong data-start="565" data-end="582">16x + Shift 4</strong></p>
</li>
</ul>
<p data-start="584" data-end="597">但总转换时间会进一步增加。</p>
<hr data-start="599" data-end="602" />
<h2 data-start="604" data-end="620">二、CubeMX 配置</h2>
<p data-start="622" data-end="671">下面按 <strong data-start="626" data-end="667">“软件触发一次，采完 6 个通道（每个都做过采样），DMA 搬 6 个值”</strong> 来配。</p>
<h3 data-start="673" data-end="688">1）ADC1 基本参数</h3>
<p data-start="689" data-end="723">在 <strong data-start="691" data-end="720">ADC1 &gt; Parameter Settings</strong> 里：</p>
<ul data-start="725" data-end="1113">
<li data-start="725" data-end="758">
<p data-start="727" data-end="758"><strong data-start="727" data-end="751">Scan Conversion Mode</strong>：Enable</p>
</li>
<li data-start="759" data-end="784">
<p data-start="761" data-end="784"><strong data-start="761" data-end="782">Nbr Of Conversion</strong>：6</p>
</li>
<li data-start="785" data-end="820">
<p data-start="787" data-end="820">把你的 6 个通道分别设成 <strong data-start="801" data-end="820">Rank 1 ~ Rank 6</strong></p>
</li>
<li data-start="821" data-end="845">
<p data-start="823" data-end="845"><strong data-start="823" data-end="837">Resolution</strong>：12 bits</p>
</li>
<li data-start="846" data-end="872">
<p data-start="848" data-end="872"><strong data-start="848" data-end="866">Data Alignment</strong>：Right</p>
</li>
<li data-start="873" data-end="917">
<p data-start="875" data-end="917"><strong data-start="875" data-end="905">Continuous Conversion Mode</strong>：<strong data-start="906" data-end="917">Disable</strong></p>
</li>
<li data-start="918" data-end="961">
<p data-start="920" data-end="961"><strong data-start="920" data-end="953">Discontinuous Conversion Mode</strong>：Disable</p>
</li>
<li data-start="962" data-end="1017">
<p data-start="964" data-end="1017"><strong data-start="964" data-end="1002">External Trigger Conversion Source</strong>：Software Start</p>
</li>
<li data-start="1018" data-end="1059">
<p data-start="1020" data-end="1059"><strong data-start="1020" data-end="1047">DMA Continuous Requests</strong>：<strong data-start="1048" data-end="1059">Disable</strong></p>
</li>
<li data-start="1060" data-end="1113">
<p data-start="1062" data-end="1113"><strong data-start="1062" data-end="1093">End Of Conversion Selection</strong>：<strong data-start="1094" data-end="1113">End of Sequence</strong></p>
</li>
</ul>
<p data-start="1115" data-end="1118">说明：</p>
<ul data-start="1119" data-end="1355">
<li data-start="1119" data-end="1156">
<p data-start="1121" data-end="1156">你要的是“触发一次，拿一组”，所以 <strong data-start="1139" data-end="1156">Continuous 要关</strong></p>
</li>
<li data-start="1157" data-end="1191">
<p data-start="1159" data-end="1191">DMA 用 <strong data-start="1165" data-end="1175">Normal</strong> 模式，一次搬完 6 个结果就停</p>
</li>
<li data-start="1192" data-end="1355">
<p data-start="1194" data-end="1355">EOC 选 <strong data-start="1200" data-end="1219">End of Sequence</strong>，这样一整组 6 通道完成后再作为一次完整结果处理，更省心。STM32L4 的 ADC 支持 single-shot 或 continuous 的单通道/扫描模式，规则组数据也可通过 DMA 获取。</p>
</li>
</ul>
<hr data-start="1357" data-end="1360" />
<h3 data-start="1362" data-end="1387">2）每个通道的 Sampling Time</h3>
<p data-start="1388" data-end="1421">在 6 个通道各自的 Rank 设置里，给一个相对稳妥的采样时间：</p>
<ul data-start="1423" data-end="1510">
<li data-start="1423" data-end="1467">
<p data-start="1425" data-end="1467">若信号源阻抗不高：<strong data-start="1434" data-end="1449">24.5 cycles</strong> 或 <strong data-start="1452" data-end="1467">47.5 cycles</strong></p>
</li>
<li data-start="1468" data-end="1510">
<p data-start="1470" data-end="1510">若是分压电阻较大 / 传感器阻抗高：建议 <strong data-start="1491" data-end="1506">92.5 cycles</strong> 或更长</p>
</li>
</ul>
<p data-start="1512" data-end="1585">STM32L4 允许每个通道单独设置采样时间；高阻信号需要更长采样时间。</p>
<hr data-start="1587" data-end="1590" />
<h3 data-start="1592" data-end="1613">3）打开 Oversampling</h3>
<p data-start="1614" data-end="1693">在 ADC1 的参数页里，找到 <strong data-start="1630" data-end="1669">Oversampling / Regular Oversampling</strong>（不同 CubeMX 版本位置略有不同），设置：</p>
<ul data-start="1695" data-end="1931">
<li data-start="1695" data-end="1725">
<p data-start="1697" data-end="1725"><strong data-start="1697" data-end="1718">Oversampling Mode</strong>：Enable</p>
</li>
<li data-start="1726" data-end="1757">
<p data-start="1728" data-end="1757"><strong data-start="1728" data-end="1750">Oversampling Ratio</strong>：<strong data-start="1751" data-end="1757">8x</strong></p>
</li>
<li data-start="1758" data-end="1790">
<p data-start="1760" data-end="1790"><strong data-start="1760" data-end="1779">Right Bit Shift</strong>：<strong data-start="1780" data-end="1790">3 bits</strong></p>
</li>
<li data-start="1791" data-end="1931">
<p data-start="1793" data-end="1853"><strong data-start="1793" data-end="1852">Triggered Oversampling Mode / Regular Oversampling Mode</strong>：</p>
<ul data-start="1856" data-end="1931">
<li data-start="1856" data-end="1895">
<p data-start="1858" data-end="1895">若有这个选项，建议先用 <strong data-start="1870" data-end="1895">Continued mode（连续过采样）</strong></p>
</li>
<li data-start="1898" data-end="1931">
<p data-start="1900" data-end="1931">也就是一次规则转换内，把 8 次子采样连续完成，再给出一个结果</p>
</li>
</ul>
</li>
</ul>
<p data-start="1933" data-end="1936">说明：</p>
<ul data-start="1937" data-end="2054">
<li data-start="1937" data-end="2017">
<p data-start="1939" data-end="2017">L4 的硬件过采样支持 <strong data-start="1951" data-end="1977">2/4/8/16/32/64/128/256</strong> 倍。</p>
</li>
<li data-start="2018" data-end="2054">
<p data-start="2020" data-end="2054"><strong data-start="2020" data-end="2036">8x + shift 3</strong> 是最像“硬件平均 8 次”的配置。</p>
</li>
</ul>
<hr data-start="2056" data-end="2059" />
<h3 data-start="2061" data-end="2073">4）DMA 配置</h3>
<p data-start="2074" data-end="2112">在 <strong data-start="2076" data-end="2099">ADC1 &gt; DMA Settings</strong> 添加一个 DMA 通道：</p>
<ul data-start="2114" data-end="2341">
<li data-start="2114" data-end="2150">
<p data-start="2116" data-end="2150"><strong data-start="2116" data-end="2129">Direction</strong>：Peripheral to Memory</p>
</li>
<li data-start="2151" data-end="2172">
<p data-start="2153" data-end="2172"><strong data-start="2153" data-end="2161">Mode</strong>：<strong data-start="2162" data-end="2172">Normal</strong></p>
</li>
<li data-start="2173" data-end="2207">
<p data-start="2175" data-end="2207"><strong data-start="2175" data-end="2199">Peripheral Increment</strong>：Disable</p>
</li>
<li data-start="2208" data-end="2237">
<p data-start="2210" data-end="2237"><strong data-start="2210" data-end="2230">Memory Increment</strong>：Enable</p>
</li>
<li data-start="2238" data-end="2275">
<p data-start="2240" data-end="2275"><strong data-start="2240" data-end="2265">Peripheral Data Width</strong>：Half Word</p>
</li>
<li data-start="2276" data-end="2309">
<p data-start="2278" data-end="2309"><strong data-start="2278" data-end="2299">Memory Data Width</strong>：Half Word</p>
</li>
<li data-start="2310" data-end="2341">
<p data-start="2312" data-end="2341"><strong data-start="2312" data-end="2324">Priority</strong>：Low / Medium 都可以</p>
</li>
</ul>
<p data-start="2343" data-end="2459">因为 ADC 数据寄存器是 16 位，DMA 用 <strong data-start="2368" data-end="2381">Half Word</strong> 最合适。STM32 的 ADC 结果寄存器是 16 位，数据可由 DMA 取走。</p>
<hr data-start="2461" data-end="2464" />
<h2 data-start="2466" data-end="2495">三、CubeMX 生成代码关键检查</h2>
<p data-start="2497" data-end="2525">不同 HAL 版本字段名略有差异，但核心会类似下面这样。</p>
<h3 data-start="2527" data-end="2557"><code data-start="2531" data-end="2547">MX_ADC1_Init()</code> 里关键部分（示例）</h3>
<div class="relative w-full my-4">
<div class="">
<div class="relative">
<div class="h-full min-h-0 min-w-0">
<div class="h-full min-h-0 min-w-0">
<div class="border border-token-border-light border-radius-3xl corner-superellipse/1.1 rounded-3xl">
<div class="h-full w-full border-radius-3xl bg-token-bg-elevated-secondary corner-superellipse/1.1 overflow-clip rounded-3xl lxnfua_clipPathFallback">
<div class="pointer-events-none absolute inset-x-4 top-12 bottom-4">
<div class="pointer-events-none sticky z-40 shrink-0 z-1!">
<div class="sticky bg-token-border-light"></div>
</div>
</div>
<div class="">
<div class="relative z-0 flex max-w-full">
<div id="code-block-viewer" class="q9tKkq_viewer cm-editor z-10 light:cm-light dark:cm-light flex h-full w-full flex-col items-stretch ͼ5 ͼj" dir="ltr">
<div class="cm-scroller">
<div class="cm-content q9tKkq_readonly"><span class="ͼe">hadc1</span>.<span class="ͼe">Instance</span> <span class="ͼ8">=</span> <span class="ͼe">ADC1</span>;<br />
<span class="ͼe">hadc1</span>.<span class="ͼe">Init</span>.<span class="ͼe">ClockPrescaler</span> <span class="ͼ8">=</span> <span class="ͼe">ADC_CLOCK_ASYNC_DIV2</span>; <span class="ͼ6">// 或按你的需求</span><br />
<span class="ͼe">hadc1</span>.<span class="ͼe">Init</span>.<span class="ͼe">Resolution</span> <span class="ͼ8">=</span> <span class="ͼe">ADC_RESOLUTION_12B</span>;<br />
<span class="ͼe">hadc1</span>.<span class="ͼe">Init</span>.<span class="ͼe">DataAlign</span> <span class="ͼ8">=</span> <span class="ͼe">ADC_DATAALIGN_RIGHT</span>;<br />
<span class="ͼe">hadc1</span>.<span class="ͼe">Init</span>.<span class="ͼe">ScanConvMode</span> <span class="ͼ8">=</span> <span class="ͼe">ADC_SCAN_ENABLE</span>;<br />
<span class="ͼe">hadc1</span>.<span class="ͼe">Init</span>.<span class="ͼe">EOCSelection</span> <span class="ͼ8">=</span> <span class="ͼe">ADC_EOC_SEQ_CONV</span>;<br />
<span class="ͼe">hadc1</span>.<span class="ͼe">Init</span>.<span class="ͼe">LowPowerAutoWait</span> <span class="ͼ8">=</span> <span class="ͼe">DISABLE</span>;<br />
<span class="ͼe">hadc1</span>.<span class="ͼe">Init</span>.<span class="ͼe">ContinuousConvMode</span> <span class="ͼ8">=</span> <span class="ͼe">DISABLE</span>;<br />
<span class="ͼe">hadc1</span>.<span class="ͼe">Init</span>.<span class="ͼe">NbrOfConversion</span> <span class="ͼ8">=</span> <span class="ͼb">6</span>;<br />
<span class="ͼe">hadc1</span>.<span class="ͼe">Init</span>.<span class="ͼe">DiscontinuousConvMode</span> <span class="ͼ8">=</span> <span class="ͼe">DISABLE</span>;<br />
<span class="ͼe">hadc1</span>.<span class="ͼe">Init</span>.<span class="ͼe">ExternalTrigConv</span> <span class="ͼ8">=</span> <span class="ͼe">ADC_SOFTWARE_START</span>;<br />
<span class="ͼe">hadc1</span>.<span class="ͼe">Init</span>.<span class="ͼe">ExternalTrigConvEdge</span> <span class="ͼ8">=</span> <span class="ͼe">ADC_EXTERNALTRIGCONVEDGE_NONE</span>;<br />
<span class="ͼe">hadc1</span>.<span class="ͼe">Init</span>.<span class="ͼe">DMAContinuousRequests</span> <span class="ͼ8">=</span> <span class="ͼe">DISABLE</span>;<br />
<span class="ͼe">hadc1</span>.<span class="ͼe">Init</span>.<span class="ͼe">Overrun</span> <span class="ͼ8">=</span> <span class="ͼe">ADC_OVR_DATA_OVERWRITTEN</span>;<br />
<span class="ͼe">hadc1</span>.<span class="ͼe">Init</span>.<span class="ͼe">OversamplingMode</span> <span class="ͼ8">=</span> <span class="ͼe">ENABLE</span>;</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="">
<div class=""></div>
</div>
</div>
</div>
</div>
<p data-start="3240" data-end="3278">然后还有 Oversampling 结构体（字段名按你当前 HAL 为准）：</p>
<div class="relative w-full my-4">
<div class="">
<div class="relative">
<div class="h-full min-h-0 min-w-0">
<div class="h-full min-h-0 min-w-0">
<div class="border border-token-border-light border-radius-3xl corner-superellipse/1.1 rounded-3xl">
<div class="h-full w-full border-radius-3xl bg-token-bg-elevated-secondary corner-superellipse/1.1 overflow-clip rounded-3xl lxnfua_clipPathFallback">
<div class="pointer-events-none absolute inset-x-4 top-12 bottom-4">
<div class="pointer-events-none sticky z-40 shrink-0 z-1!">
<div class="sticky bg-token-border-light"></div>
</div>
</div>
<div class="">
<div class="relative z-0 flex max-w-full">
<div id="code-block-viewer" class="q9tKkq_viewer cm-editor z-10 light:cm-light dark:cm-light flex h-full w-full flex-col items-stretch ͼ5 ͼj" dir="ltr">
<div class="cm-scroller">
<div class="cm-content q9tKkq_readonly"><span class="ͼe">hadc1</span>.<span class="ͼe">Init</span>.<span class="ͼe">Oversampling</span>.<span class="ͼe">Ratio</span> <span class="ͼ8">=</span> <span class="ͼe">ADC_OVERSAMPLING_RATIO_8</span>;<br />
<span class="ͼe">hadc1</span>.<span class="ͼe">Init</span>.<span class="ͼe">Oversampling</span>.<span class="ͼe">RightBitShift</span> <span class="ͼ8">=</span> <span class="ͼe">ADC_RIGHTBITSHIFT_3</span>;<br />
<span class="ͼe">hadc1</span>.<span class="ͼe">Init</span>.<span class="ͼe">Oversampling</span>.<span class="ͼe">TriggeredMode</span> <span class="ͼ8">=</span> <span class="ͼe">ADC_TRIGGEREDMODE_SINGLE_TRIGGER</span>;<br />
<span class="ͼe">hadc1</span>.<span class="ͼe">Init</span>.<span class="ͼe">Oversampling</span>.<span class="ͼe">OversamplingStopReset</span> <span class="ͼ8">=</span> <span class="ͼe">ADC_REGOVERSAMPLING_CONTINUED_MODE</span>;</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="">
<div class=""></div>
</div>
</div>
</div>
</div>
<blockquote data-start="3580" data-end="3682">
<p data-start="3582" data-end="3682">这里最后两个枚举名在不同 HAL 版本中可能略有差异；<strong data-start="3609" data-end="3630">CubeMX 生成什么就以什么为准</strong>。<br data-start="3631" data-end="3634" />你只需要保证逻辑上是：<strong data-start="3647" data-end="3681">一次启动后，每个规则转换会连续完成 8 次子采样并输出平均值</strong>。</p>
</blockquote>
<hr data-start="3684" data-end="3687" />
<h2 data-start="3689" data-end="3712">四、6 通道配置示例（Rank 1~6）</h2>
<p data-start="3714" data-end="3731">这个你前面已经配好了，只确认保留：</p>
<div class="relative w-full my-4">
<div class="">
<div class="relative">
<div class="h-full min-h-0 min-w-0">
<div class="h-full min-h-0 min-w-0">
<div class="border border-token-border-light border-radius-3xl corner-superellipse/1.1 rounded-3xl">
<div class="h-full w-full border-radius-3xl bg-token-bg-elevated-secondary corner-superellipse/1.1 overflow-clip rounded-3xl lxnfua_clipPathFallback">
<div class="pointer-events-none absolute inset-x-4 top-12 bottom-4">
<div class="pointer-events-none sticky z-40 shrink-0 z-1!">
<div class="sticky bg-token-border-light"></div>
</div>
</div>
<div class="">
<div class="relative z-0 flex max-w-full">
<div id="code-block-viewer" class="q9tKkq_viewer cm-editor z-10 light:cm-light dark:cm-light flex h-full w-full flex-col items-stretch ͼ5 ͼj" dir="ltr">
<div class="cm-scroller">
<div class="cm-content q9tKkq_readonly"><span class="ͼe">ADC_ChannelConfTypeDef</span> <span class="ͼe">sConfig</span> <span class="ͼ8">=</span> {<span class="ͼb">0</span>};</p>
<p><span class="ͼe">sConfig</span>.<span class="ͼe">SamplingTime</span> <span class="ͼ8">=</span> <span class="ͼe">ADC_SAMPLETIME_47CYCLES_5</span>;<br />
<span class="ͼe">sConfig</span>.<span class="ͼe">SingleDiff</span> <span class="ͼ8">=</span> <span class="ͼe">ADC_SINGLE_ENDED</span>;<br />
<span class="ͼe">sConfig</span>.<span class="ͼe">OffsetNumber</span> <span class="ͼ8">=</span> <span class="ͼe">ADC_OFFSET_NONE</span>;<br />
<span class="ͼe">sConfig</span>.<span class="ͼe">Offset</span> <span class="ͼ8">=</span> <span class="ͼb">0</span>;</p>
<p><span class="ͼ6">/* Rank 1 */</span><br />
<span class="ͼe">sConfig</span>.<span class="ͼe">Channel</span> <span class="ͼ8">=</span> <span class="ͼe">ADC_CHANNEL_1</span>;<br />
<span class="ͼe">sConfig</span>.<span class="ͼe">Rank</span> <span class="ͼ8">=</span> <span class="ͼe">ADC_REGULAR_RANK_1</span>;<br />
<span class="ͼe">HAL_ADC_ConfigChannel</span>(<span class="ͼ8">&amp;</span><span class="ͼe">hadc1</span>, <span class="ͼ8">&amp;</span><span class="ͼe">sConfig</span>);</p>
<p><span class="ͼ6">/* Rank 2 */</span><br />
<span class="ͼe">sConfig</span>.<span class="ͼe">Channel</span> <span class="ͼ8">=</span> <span class="ͼe">ADC_CHANNEL_2</span>;<br />
<span class="ͼe">sConfig</span>.<span class="ͼe">Rank</span> <span class="ͼ8">=</span> <span class="ͼe">ADC_REGULAR_RANK_2</span>;<br />
<span class="ͼe">HAL_ADC_ConfigChannel</span>(<span class="ͼ8">&amp;</span><span class="ͼe">hadc1</span>, <span class="ͼ8">&amp;</span><span class="ͼe">sConfig</span>);</p>
<p><span class="ͼ6">/* &#8230; 一直到 Rank 6 */</span></div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="">
<div class=""></div>
</div>
</div>
</div>
</div>
<p data-start="4211" data-end="4299">STM32L4 的 sequencer 可按任意顺序转换多达 16 个通道；你这里只用 6 个没问题。</p>
<hr data-start="4301" data-end="4304" />
<h2 data-start="4306" data-end="4339">五、应用代码模板（一次触发，DMA 收 6 个“已平均值”）</h2>
<h3 data-start="4341" data-end="4351">1）全局变量</h3>
<div class="relative w-full my-4">
<div class="">
<div class="relative">
<div class="h-full min-h-0 min-w-0">
<div class="h-full min-h-0 min-w-0">
<div class="border border-token-border-light border-radius-3xl corner-superellipse/1.1 rounded-3xl">
<div class="h-full w-full border-radius-3xl bg-token-bg-elevated-secondary corner-superellipse/1.1 overflow-clip rounded-3xl lxnfua_clipPathFallback">
<div class="pointer-events-none absolute inset-x-4 top-12 bottom-4">
<div class="pointer-events-none sticky z-40 shrink-0 z-1!">
<div class="sticky bg-token-border-light"></div>
</div>
</div>
<div class="">
<div class="relative z-0 flex max-w-full">
<div id="code-block-viewer" class="q9tKkq_viewer cm-editor z-10 light:cm-light dark:cm-light flex h-full w-full flex-col items-stretch ͼ5 ͼj" dir="ltr">
<div class="cm-scroller">
<div class="cm-content q9tKkq_readonly"><span class="ͼ7">#include &#8220;main.h&#8221;</span></p>
<p><span class="ͼ8">extern</span> <span class="ͼe">ADC_HandleTypeDef</span> <span class="ͼe">hadc1</span>;</p>
<p><span class="ͼ7">#define ADC_CH_NUM 6</span></p>
<p><span class="ͼe">uint16_t</span> <span class="ͼe">adc_avg_buf</span>[<span class="ͼe">ADC_CH_NUM</span>];<br />
<span class="ͼ8">volatile</span> <span class="ͼe">uint8_t</span> <span class="ͼe">adc_done</span> <span class="ͼ8">=</span> <span class="ͼb">0</span>;</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="">
<div class=""></div>
</div>
</div>
</div>
</div>
<hr data-start="4514" data-end="4517" />
<h3 data-start="4519" data-end="4537">2）初始化时先做一次校准</h3>
<p data-start="4538" data-end="4634">STM32L4 官方建议在应用中做 ADC 校准；当参考电压变化超过 10%（如复位、某些低功耗恢复）时建议重新校准。</p>
<div class="relative w-full my-4">
<div class="">
<div class="relative">
<div class="h-full min-h-0 min-w-0">
<div class="h-full min-h-0 min-w-0">
<div class="border border-token-border-light border-radius-3xl corner-superellipse/1.1 rounded-3xl">
<div class="h-full w-full border-radius-3xl bg-token-bg-elevated-secondary corner-superellipse/1.1 overflow-clip rounded-3xl lxnfua_clipPathFallback">
<div class="pointer-events-none absolute inset-x-4 top-12 bottom-4">
<div class="pointer-events-none sticky z-40 shrink-0 z-1!">
<div class="sticky bg-token-border-light"></div>
</div>
</div>
<div class="">
<div class="relative z-0 flex max-w-full">
<div id="code-block-viewer" class="q9tKkq_viewer cm-editor z-10 light:cm-light dark:cm-light flex h-full w-full flex-col items-stretch ͼ5 ͼj" dir="ltr">
<div class="cm-scroller">
<div class="cm-content q9tKkq_readonly"><span class="ͼe">void</span> <span class="ͼe">ADC1_AppInit</span>(<span class="ͼe">void</span>)<br />
{<br />
<span class="ͼe">HAL_ADCEx_Calibration_Start</span>(<span class="ͼ8">&amp;</span><span class="ͼe">hadc1</span>, <span class="ͼe">ADC_SINGLE_ENDED</span>);<br />
}</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="">
<div class=""></div>
</div>
</div>
</div>
</div>
<hr data-start="4745" data-end="4748" />
<h3 data-start="4750" data-end="4762">3）启动一次采样</h3>
<div class="relative w-full my-4">
<div class="">
<div class="relative">
<div class="h-full min-h-0 min-w-0">
<div class="h-full min-h-0 min-w-0">
<div class="border border-token-border-light border-radius-3xl corner-superellipse/1.1 rounded-3xl">
<div class="h-full w-full border-radius-3xl bg-token-bg-elevated-secondary corner-superellipse/1.1 overflow-clip rounded-3xl lxnfua_clipPathFallback">
<div class="pointer-events-none absolute inset-x-4 top-12 bottom-4">
<div class="pointer-events-none sticky z-40 shrink-0 z-1!">
<div class="sticky bg-token-border-light"></div>
</div>
</div>
<div class="">
<div class="relative z-0 flex max-w-full">
<div id="code-block-viewer" class="q9tKkq_viewer cm-editor z-10 light:cm-light dark:cm-light flex h-full w-full flex-col items-stretch ͼ5 ͼj" dir="ltr">
<div class="cm-scroller">
<div class="cm-content q9tKkq_readonly"><span class="ͼe">HAL_StatusTypeDef</span> <span class="ͼe">ADC1_ScanOnce_Oversampled_Start</span>(<span class="ͼe">void</span>)<br />
{<br />
<span class="ͼe">adc_done</span> <span class="ͼ8">=</span> <span class="ͼb">0</span>;<br />
<span class="ͼ8">return</span> <span class="ͼe">HAL_ADC_Start_DMA</span>(<span class="ͼ8">&amp;</span><span class="ͼe">hadc1</span>, (<span class="ͼe">uint32_t</span> <span class="ͼe">*</span>)<span class="ͼe">adc_avg_buf</span>, <span class="ͼe">ADC_CH_NUM</span>);<br />
}</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="">
<div class=""></div>
</div>
</div>
</div>
</div>
<p data-start="4938" data-end="4975">这里的 <code data-start="4942" data-end="4961">adc_avg_buf[0..5]</code> 不是“原始单次值”，而是：</p>
<ul data-start="4977" data-end="5051">
<li data-start="4977" data-end="4999">
<p data-start="4979" data-end="4999">Rank1 通道：内部 8 次平均后的值</p>
</li>
<li data-start="5000" data-end="5022">
<p data-start="5002" data-end="5022">Rank2 通道：内部 8 次平均后的值</p>
</li>
<li data-start="5023" data-end="5028">
<p data-start="5025" data-end="5028">&#8230;</p>
</li>
<li data-start="5029" data-end="5051">
<p data-start="5031" data-end="5051">Rank6 通道：内部 8 次平均后的值</p>
</li>
</ul>
<hr data-start="5053" data-end="5056" />
<h3 data-start="5058" data-end="5072">4）DMA 完成回调</h3>
<div class="relative w-full my-4">
<div class="">
<div class="relative">
<div class="h-full min-h-0 min-w-0">
<div class="h-full min-h-0 min-w-0">
<div class="border border-token-border-light border-radius-3xl corner-superellipse/1.1 rounded-3xl">
<div class="h-full w-full border-radius-3xl bg-token-bg-elevated-secondary corner-superellipse/1.1 overflow-clip rounded-3xl lxnfua_clipPathFallback">
<div class="pointer-events-none absolute inset-x-4 top-12 bottom-4">
<div class="pointer-events-none sticky z-40 shrink-0 z-1!">
<div class="sticky bg-token-border-light"></div>
</div>
</div>
<div class="">
<div class="relative z-0 flex max-w-full">
<div id="code-block-viewer" class="q9tKkq_viewer cm-editor z-10 light:cm-light dark:cm-light flex h-full w-full flex-col items-stretch ͼ5 ͼj" dir="ltr">
<div class="cm-scroller">
<div class="cm-content q9tKkq_readonly"><span class="ͼe">void</span> <span class="ͼe">HAL_ADC_ConvCpltCallback</span>(<span class="ͼe">ADC_HandleTypeDef</span> <span class="ͼ8">*</span><span class="ͼe">hadc</span>)<br />
{<br />
<span class="ͼ8">if</span> (<span class="ͼe">hadc</span><span class="ͼ8">-&gt;</span><span class="ͼe">Instance</span> <span class="ͼ8">==</span> <span class="ͼe">ADC1</span>)<br />
{<br />
<span class="ͼe">adc_done</span> <span class="ͼ8">=</span> <span class="ͼb">1</span>;<br />
<span class="ͼe">HAL_ADC_Stop_DMA</span>(<span class="ͼ8">&amp;</span><span class="ͼe">hadc1</span>);<br />
}<br />
}</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="">
<div class=""></div>
</div>
</div>
</div>
</div>
<hr data-start="5254" data-end="5257" />
<h3 data-start="5259" data-end="5269">5）使用方法</h3>
<div class="relative w-full my-4">
<div class="">
<div class="relative">
<div class="h-full min-h-0 min-w-0">
<div class="h-full min-h-0 min-w-0">
<div class="border border-token-border-light border-radius-3xl corner-superellipse/1.1 rounded-3xl">
<div class="h-full w-full border-radius-3xl bg-token-bg-elevated-secondary corner-superellipse/1.1 overflow-clip rounded-3xl lxnfua_clipPathFallback">
<div class="pointer-events-none absolute inset-x-4 top-12 bottom-4">
<div class="pointer-events-none sticky z-40 shrink-0 z-1!">
<div class="sticky bg-token-border-light"></div>
</div>
</div>
<div class="">
<div class="relative z-0 flex max-w-full">
<div id="code-block-viewer" class="q9tKkq_viewer cm-editor z-10 light:cm-light dark:cm-light flex h-full w-full flex-col items-stretch ͼ5 ͼj" dir="ltr">
<div class="cm-scroller">
<div class="cm-content q9tKkq_readonly"><span class="ͼ8">if</span> (<span class="ͼe">ADC1_ScanOnce_Oversampled_Start</span>() <span class="ͼ8">==</span> <span class="ͼe">HAL_OK</span>)<br />
{<br />
<span class="ͼ6">// 等待 adc_done 或在主循环里异步判断</span><br />
}</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="">
<div class=""></div>
</div>
</div>
</div>
</div>
<p data-start="5375" data-end="5380">主循环里：</p>
<div class="relative w-full my-4">
<div class="">
<div class="relative">
<div class="h-full min-h-0 min-w-0">
<div class="h-full min-h-0 min-w-0">
<div class="border border-token-border-light border-radius-3xl corner-superellipse/1.1 rounded-3xl">
<div class="h-full w-full border-radius-3xl bg-token-bg-elevated-secondary corner-superellipse/1.1 overflow-clip rounded-3xl lxnfua_clipPathFallback">
<div class="pointer-events-none absolute inset-x-4 top-12 bottom-4">
<div class="pointer-events-none sticky z-40 shrink-0 z-1!">
<div class="sticky bg-token-border-light"></div>
</div>
</div>
<div class="">
<div class="relative z-0 flex max-w-full">
<div id="code-block-viewer" class="q9tKkq_viewer cm-editor z-10 light:cm-light dark:cm-light flex h-full w-full flex-col items-stretch ͼ5 ͼj" dir="ltr">
<div class="cm-scroller">
<div class="cm-content q9tKkq_readonly"><span class="ͼ8">if</span> (<span class="ͼe">adc_done</span>)<br />
{<br />
<span class="ͼe">adc_done</span> <span class="ͼ8">=</span> <span class="ͼb">0</span>;</p>
<p><span class="ͼ6">// adc_avg_buf[0] -&gt; Rank1 通道的平均值</span><br />
<span class="ͼ6">// adc_avg_buf[1] -&gt; Rank2</span><br />
<span class="ͼ6">// &#8230;</span><br />
<span class="ͼ6">// adc_avg_buf[5] -&gt; Rank6</span><br />
}</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="">
<div class=""></div>
</div>
</div>
</div>
</div>
<hr data-start="5552" data-end="5555" />
<h2 data-start="5557" data-end="5576">六、这时“单次采样时间”会怎么变</h2>
<p data-start="5578" data-end="5589">这是你最容易忽略的点。</p>
<p data-start="5591" data-end="5609">如果你原来单通道一次转换耗时近似是：</p>
<p><span class="katex-display"><span class="katex"><span class="katex-mathml">Tone=Tsample+TconvertT_{one} = T_{sample} + T_{convert}</span><span class="katex-html" aria-hidden="true"><span class="base"><span class="mord"><span class="mord mathnormal">T</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist"><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight">o</span><span class="mord mathnormal mtight">n</span><span class="mord mathnormal mtight">e</span></span></span></span><span class="vlist-s">​</span></span></span></span></span><span class="mrel">=</span></span><span class="base"><span class="mord"><span class="mord mathnormal">T</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist"><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight">s</span><span class="mord mathnormal mtight">am</span><span class="mord mathnormal mtight">pl</span><span class="mord mathnormal mtight">e</span></span></span></span><span class="vlist-s">​</span></span></span></span></span><span class="mbin">+</span></span><span class="base"><span class="mord"><span class="mord mathnormal">T</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist"><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight">co</span><span class="mord mathnormal mtight">n</span><span class="mord mathnormal mtight">v</span><span class="mord mathnormal mtight">er</span><span class="mord mathnormal mtight">t</span></span></span></span><span class="vlist-s">​</span></span></span></span></span></span></span></span></span></p>
<p data-start="5653" data-end="5693">那打开 <strong data-start="5657" data-end="5676">8x oversampling</strong> 后，<strong data-start="5679" data-end="5687">每个通道</strong>大致会变成：</p>
<p><span class="katex-display"><span class="katex"><span class="katex-mathml">Tone_os≈8×(Tsample+Tconvert)T_{one\_os} \approx 8 \times (T_{sample} + T_{convert})</span><span class="katex-html" aria-hidden="true"><span class="base"><span class="mord"><span class="mord mathnormal">T</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist"><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight">o</span><span class="mord mathnormal mtight">n</span><span class="mord mathnormal mtight">e</span>_<span class="mord mathnormal mtight">os</span></span></span></span><span class="vlist-s">​</span></span></span></span></span><span class="mrel">≈</span></span><span class="base"><span class="mord">8</span><span class="mbin">×</span></span><span class="base"><span class="mopen">(</span><span class="mord"><span class="mord mathnormal">T</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist"><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight">s</span><span class="mord mathnormal mtight">am</span><span class="mord mathnormal mtight">pl</span><span class="mord mathnormal mtight">e</span></span></span></span><span class="vlist-s">​</span></span></span></span></span><span class="mbin">+</span></span><span class="base"><span class="mord"><span class="mord mathnormal">T</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist"><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight">co</span><span class="mord mathnormal mtight">n</span><span class="mord mathnormal mtight">v</span><span class="mord mathnormal mtight">er</span><span class="mord mathnormal mtight">t</span></span></span></span><span class="vlist-s">​</span></span></span></span></span><span class="mclose">)</span></span></span></span></span></p>
<p data-start="5758" data-end="5857">所以 <strong data-start="5761" data-end="5772">整组 6 通道</strong> 的总时间也大约乘以 8。STM32L4 的硬件过采样就是先做多次采样并累加，再输出给 DMA。</p>
<p data-start="5859" data-end="5866">举例（粗略）：</p>
<ul data-start="5867" data-end="5909">
<li data-start="5867" data-end="5885">
<p data-start="5869" data-end="5885">若原来 6 通道一轮约 20µs</p>
</li>
<li data-start="5886" data-end="5909">
<p data-start="5888" data-end="5909">开 8x 后，可能接近 <strong data-start="5900" data-end="5909">160µs</strong></p>
</li>
</ul>
<p data-start="5911" data-end="5947">所以如果你有“50µs 内完成”的硬限制，8x 可能就太慢了；那就改成：</p>
<ul data-start="5948" data-end="5977">
<li data-start="5948" data-end="5961">
<p data-start="5950" data-end="5961">2x + shift1</p>
</li>
<li data-start="5962" data-end="5977">
<p data-start="5964" data-end="5977">或 4x + shift2</p>
</li>
</ul>
<hr data-start="5979" data-end="5982" />
<h2 data-start="5984" data-end="5994">七、最常见的坑</h2>
<h3 data-start="5996" data-end="6030">1）开了 Oversampling，却还按“原采样速度”估算</h3>
<p data-start="6031" data-end="6089">这是最常见误判。过采样会显著拉长转换时间。</p>
<h3 data-start="6091" data-end="6112">2）DMA 长度还写成 <code data-start="6107" data-end="6112">6*N</code></h3>
<p data-start="6113" data-end="6168">模板 B 下，DMA 长度就是 <strong data-start="6129" data-end="6134">6</strong>，不是 <code data-start="6138" data-end="6143">6×8</code>。<br data-start="6144" data-end="6147" />因为多次采样在 ADC 内部已经处理完了。</p>
<h3 data-start="6170" data-end="6182">3）采样时间太短</h3>
<p data-start="6183" data-end="6290">如果前端源阻抗偏大（比如几十 kΩ 分压），哪怕开了过采样，<strong data-start="6213" data-end="6227">采样电容本身还没充稳</strong>，结果照样漂。<br data-start="6234" data-end="6237" />这时应优先增加 <strong data-start="6245" data-end="6262">Sampling Time</strong>，而不是盲目加大 Oversampling Ratio。</p>
<h3 data-start="6292" data-end="6312">4）误以为分辨率一定“真实提高”</h3>
<p data-start="6313" data-end="6438">过采样确实能改善随机噪声、实现平均/基本滤波，L4 在一定条件下可把 12 位输出扩展到 16 位表现；但这依赖噪声特性和输入信号条件，不是“无条件白捡 4 位有效精度”。</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>电容NP0、C0G、X7R、X5R、Y5V、Z5U的区别</title>
		<link>https://e673.com/capacitornp0-c0g-x7r-x5r-y5v-z5u-compare/</link>
		
		<dc:creator><![CDATA[ahe]]></dc:creator>
		<pubDate>Sat, 28 Feb 2026 04:56:18 +0000</pubDate>
				<category><![CDATA[应用笔记]]></category>
		<category><![CDATA[经验分享]]></category>
		<category><![CDATA[电路]]></category>
		<category><![CDATA[硬件]]></category>
		<guid isPermaLink="false">https://e673.com/?p=3087</guid>

					<description><![CDATA[主要是介质材料不同。不同介质种类&#46;&#46;&#46;]]></description>
										<content:encoded><![CDATA[<div class="css-1ac3ifk">
<div class="css-12o297s">
<div class="css-1ld0bim"><picture><img fetchpriority="high" decoding="async" class="alignnone size-full wp-image-3088" src="https://e673.com/wp-content/uploads/2026/02/8561390c5c274cc6c8f0a8a6e5615d39.jpg" width="576" height="334" alt="NP0、C0G、X7R、X5R、Y5V、Z5U的区别" srcset="https://e673.com/wp-content/uploads/2026/02/8561390c5c274cc6c8f0a8a6e5615d39.jpg 576w, https://e673.com/wp-content/uploads/2026/02/8561390c5c274cc6c8f0a8a6e5615d39-500x290.jpg 500w" sizes="(max-width: 576px) 100vw, 576px" /></picture></div>
</div>
</div>
<article class="Post-Main Post-NormalMain" tabindex="-1">
<header class="Post-Header">
<p data-first-child="" data-pid="1fhL8KMc">主要是介质材料不同。不同介质种类由于它的主要<a class="RichContent-EntityWord css-b7erz1" href="https://zhida.zhihu.com/search?content_id=189120689&amp;content_type=Article&amp;match_order=1&amp;q=%E6%9E%81%E5%8C%96%E7%B1%BB%E5%9E%8B&amp;zd_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ6aGlkYV9zZXJ2ZXIiLCJleHAiOjE3NzI0MjcwNTQsInEiOiLmnoHljJbnsbvlnosiLCJ6aGlkYV9zb3VyY2UiOiJlbnRpdHkiLCJjb250ZW50X2lkIjoxODkxMjA2ODksImNvbnRlbnRfdHlwZSI6IkFydGljbGUiLCJtYXRjaF9vcmRlciI6MSwiemRfdG9rZW4iOm51bGx9.Vlhr6H0l-ukFPtZ-RGm1Oiw253pTIwMjmR-_Bmgs6PE&amp;zhida_source=entity" target="_blank" rel="noopener" data-za-not-track-link="true" data-paste-text="true">极化类型</a>不一样，其对电场变化的响应速度和极化率亦不一样。 在相同的体积下的容量就不同，随之带来的<u><a class=" wrap external" href="https://link.zhihu.com/?target=http%3A//www.elecfans.com/tags/%25E7%2594%25B5%25E5%25AE%25B9%25E5%2599%25A8/" target="_blank" rel="nofollow noopener noreferrer" data-za-detail-view-id="1043">电容器</a></u>的介质损耗、容量稳定性等也就不同。介质材料划按容量的温度稳定性可以分为两类，即<a class="RichContent-EntityWord css-b7erz1" href="https://zhida.zhihu.com/search?content_id=189120689&amp;content_type=Article&amp;match_order=1&amp;q=%E2%85%A0%E7%B1%BB%E9%99%B6%E7%93%B7&amp;zd_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ6aGlkYV9zZXJ2ZXIiLCJleHAiOjE3NzI0MjcwNTQsInEiOiLihaDnsbvpmbbnk7ciLCJ6aGlkYV9zb3VyY2UiOiJlbnRpdHkiLCJjb250ZW50X2lkIjoxODkxMjA2ODksImNvbnRlbnRfdHlwZSI6IkFydGljbGUiLCJtYXRjaF9vcmRlciI6MSwiemRfdG9rZW4iOm51bGx9.P3QA2e63OWKMyGr6clpkp-I0vYZfH17i9e_52jOO6VE&amp;zhida_source=entity" target="_blank" rel="noopener" data-za-not-track-link="true" data-paste-text="true">Ⅰ类陶瓷</a><u><a class=" wrap external" href="https://link.zhihu.com/?target=http%3A//www.elecfans.com/tags/%25E7%2594%25B5%25E5%25AE%25B9/" target="_blank" rel="nofollow noopener noreferrer" data-za-detail-view-id="1043">电容</a></u>器和<a class="RichContent-EntityWord css-b7erz1" href="https://zhida.zhihu.com/search?content_id=189120689&amp;content_type=Article&amp;match_order=1&amp;q=%E2%85%A1%E7%B1%BB%E9%99%B6%E7%93%B7&amp;zd_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ6aGlkYV9zZXJ2ZXIiLCJleHAiOjE3NzI0MjcwNTQsInEiOiLihaHnsbvpmbbnk7ciLCJ6aGlkYV9zb3VyY2UiOiJlbnRpdHkiLCJjb250ZW50X2lkIjoxODkxMjA2ODksImNvbnRlbnRfdHlwZSI6IkFydGljbGUiLCJtYXRjaF9vcmRlciI6MSwiemRfdG9rZW4iOm51bGx9.I3DTpjYa2OE3Mjd9eIIpU9jEHgHOX4Zf_Jy0abU3sFY&amp;zhida_source=entity" target="_blank" rel="noopener" data-za-not-track-link="true" data-paste-text="true">Ⅱ类陶瓷</a>电容器， NPO属于Ⅰ类陶瓷，而其他的<a class="RichContent-EntityWord css-b7erz1" href="https://zhida.zhihu.com/search?content_id=189120689&amp;content_type=Article&amp;match_order=1&amp;q=X7R&amp;zd_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ6aGlkYV9zZXJ2ZXIiLCJleHAiOjE3NzI0MjcwNTQsInEiOiJYN1IiLCJ6aGlkYV9zb3VyY2UiOiJlbnRpdHkiLCJjb250ZW50X2lkIjoxODkxMjA2ODksImNvbnRlbnRfdHlwZSI6IkFydGljbGUiLCJtYXRjaF9vcmRlciI6MSwiemRfdG9rZW4iOm51bGx9.7TSKLvwzPrYdUeEx7zwL8lDnV8hbvcAseW6ZXYx2wwk&amp;zhida_source=entity" target="_blank" rel="noopener" data-za-not-track-link="true" data-paste-text="true">X7R</a>、<a class="RichContent-EntityWord css-b7erz1" href="https://zhida.zhihu.com/search?content_id=189120689&amp;content_type=Article&amp;match_order=1&amp;q=X5R&amp;zd_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ6aGlkYV9zZXJ2ZXIiLCJleHAiOjE3NzI0MjcwNTQsInEiOiJYNVIiLCJ6aGlkYV9zb3VyY2UiOiJlbnRpdHkiLCJjb250ZW50X2lkIjoxODkxMjA2ODksImNvbnRlbnRfdHlwZSI6IkFydGljbGUiLCJtYXRjaF9vcmRlciI6MSwiemRfdG9rZW4iOm51bGx9.Po28k12T087BN1MrQVazawfHT3zBJJJseJGOkc_MaNw&amp;zhida_source=entity" target="_blank" rel="noopener" data-za-not-track-link="true" data-paste-text="true">X5R</a>、<a class="RichContent-EntityWord css-b7erz1" href="https://zhida.zhihu.com/search?content_id=189120689&amp;content_type=Article&amp;match_order=1&amp;q=Y5V&amp;zd_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ6aGlkYV9zZXJ2ZXIiLCJleHAiOjE3NzI0MjcwNTQsInEiOiJZNVYiLCJ6aGlkYV9zb3VyY2UiOiJlbnRpdHkiLCJjb250ZW50X2lkIjoxODkxMjA2ODksImNvbnRlbnRfdHlwZSI6IkFydGljbGUiLCJtYXRjaF9vcmRlciI6MSwiemRfdG9rZW4iOm51bGx9.fuQxwIDUn0OGN_7Z-PGjzKL0L1Io1oCmImWo-SlvoVQ&amp;zhida_source=entity" target="_blank" rel="noopener" data-za-not-track-link="true" data-paste-text="true">Y5V</a>、<a class="RichContent-EntityWord css-b7erz1" href="https://zhida.zhihu.com/search?content_id=189120689&amp;content_type=Article&amp;match_order=1&amp;q=Z5U&amp;zd_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ6aGlkYV9zZXJ2ZXIiLCJleHAiOjE3NzI0MjcwNTQsInEiOiJaNVUiLCJ6aGlkYV9zb3VyY2UiOiJlbnRpdHkiLCJjb250ZW50X2lkIjoxODkxMjA2ODksImNvbnRlbnRfdHlwZSI6IkFydGljbGUiLCJtYXRjaF9vcmRlciI6MSwiemRfdG9rZW4iOm51bGx9.hOFSIbt_qFdrJyF-yybJ1Ggf_6fNjevXhfnSGdb65pk&amp;zhida_source=entity" target="_blank" rel="noopener" data-za-not-track-link="true" data-paste-text="true">Z5U</a>等都属于Ⅱ类陶瓷。</p>
<figure data-size="normal">
<div class="RichText-ConditionalImagePortal"><img decoding="async" class="alignnone size-full wp-image-3089" src="https://e673.com/wp-content/uploads/2026/02/v2-d41e5cbc8825d7c747c7e2b00463ddc9_1440w.jpg" width="506" height="125" srcset="https://e673.com/wp-content/uploads/2026/02/v2-d41e5cbc8825d7c747c7e2b00463ddc9_1440w.jpg 506w, https://e673.com/wp-content/uploads/2026/02/v2-d41e5cbc8825d7c747c7e2b00463ddc9_1440w-500x124.jpg 500w" sizes="(max-width: 506px) 100vw, 506px" /></div>
</figure>
<h2 id="h_454453669_0" data-into-catalog-status=""><b>什么是Ⅰ类陶瓷，有什么特点？</b></h2>
<p data-pid="5p1Xt4hy">
Ⅰ类陶瓷电容器（ClassⅠce<u><a class=" wrap external" href="https://link.zhihu.com/?target=http%3A//www.elecfans.com/tags/ram/" target="_blank" rel="nofollow noopener noreferrer" data-za-detail-view-id="1043">ram</a></u>ic capacitor），过去称高频陶瓷电容器（High-frequency ceramic capacitor），介质采用非铁电（顺电）配方，以<u><a class=" wrap external" href="https://link.zhihu.com/?target=http%3A//www.elecfans.com/tags/ti/" target="_blank" rel="nofollow noopener noreferrer" data-za-detail-view-id="1043">Ti</a></u>O2为主要成分（介电常数小于150），因此具有最稳定的性能；或者通过添加少量其他（铁电体）<u><a class=" wrap external" href="https://link.zhihu.com/?target=http%3A//www.hqpcb.com/zhuoluye8" target="_blank" rel="nofollow noopener noreferrer" data-za-detail-view-id="1043">氧化</a></u>物，如CaTiO3 或SrTiO3，构成“扩展型”温度补偿陶瓷，则可表现出近似线性的温度系数，介电常数增加至500。这两种介质损耗小、绝缘电阻高、温度特性好。特别适用于<u><a class=" wrap external" href="https://link.zhihu.com/?target=http%3A//www.elecfans.com/tags/%25E6%258C%25AF%25E8%258D%25A1%25E5%2599%25A8/" target="_blank" rel="nofollow noopener noreferrer" data-za-detail-view-id="1043">振荡器</a></u>、谐振回路、高频<u><a class=" wrap external" href="https://link.zhihu.com/?target=http%3A//www.hqpcb.com/zhuoluye8" target="_blank" rel="nofollow noopener noreferrer" data-za-detail-view-id="1043">电路</a></u>中的<u><a class=" wrap external" href="https://link.zhihu.com/?target=http%3A//www.elecfans.com/tags/%25E8%2580%25A6%25E5%2590%2588/" target="_blank" rel="nofollow noopener noreferrer" data-za-detail-view-id="1043">耦合</a></u>电容，以及其他要求损耗小和电容量稳定的电路，或用于温度补偿。</p>
<h2 id="h_454453669_1" data-into-catalog-status=""><b>Ⅰ类陶瓷的温度特性怎么表示</b></h2>
<p data-pid="2FL06UGT">Ⅰ类陶瓷的温度容量特性（TCC）非常小，单位往往在ppm/℃，容量较基准值的变化往往远小于1皮法。美国<u><a class=" wrap external" href="https://link.zhihu.com/?target=http%3A//www.elecfans.com/soft/special/" target="_blank" rel="nofollow noopener noreferrer" data-za-detail-view-id="1043">电子</a></u>工业协会（EIA）标准采用“字母+数字+字母” 这种代码形式来表示Ⅰ类陶瓷温度系数。比如常见的<a class="RichContent-EntityWord css-b7erz1" href="https://zhida.zhihu.com/search?content_id=189120689&amp;content_type=Article&amp;match_order=1&amp;q=C0G&amp;zd_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ6aGlkYV9zZXJ2ZXIiLCJleHAiOjE3NzI0MjcwNTQsInEiOiJDMEciLCJ6aGlkYV9zb3VyY2UiOiJlbnRpdHkiLCJjb250ZW50X2lkIjoxODkxMjA2ODksImNvbnRlbnRfdHlwZSI6IkFydGljbGUiLCJtYXRjaF9vcmRlciI6MSwiemRfdG9rZW4iOm51bGx9.tVzzblgXak3tQN3EZXUG3R5OnzcZc8TsQGfun5vPWUA&amp;zhida_source=entity" target="_blank" rel="noopener" data-za-not-track-link="true" data-paste-text="true">C0G</a>。</p>
<p data-pid="dD-FEgWo">C0G代表的温度系数究竟是多少？</p>
<p data-pid="1CxoCiNc">C 表示电容温度系数的有效数字为 0 ppm/℃</p>
<p data-pid="2GHcvFmg">0 表示有效数字的倍乘因数为 -1（即10的0次方）</p>
<p data-pid="KZfmnRAQ">G 表示随温度变化的容差为 ±30ppm</p>
<figure data-size="normal">
<div class="RichText-ConditionalImagePortal"><img decoding="async" class="alignnone size-full wp-image-3090" src="https://e673.com/wp-content/uploads/2026/02/v2-84482e952d41e481e36b6397d3a6cc63_1440w.jpg" width="607" height="337" srcset="https://e673.com/wp-content/uploads/2026/02/v2-84482e952d41e481e36b6397d3a6cc63_1440w.jpg 607w, https://e673.com/wp-content/uploads/2026/02/v2-84482e952d41e481e36b6397d3a6cc63_1440w-500x278.jpg 500w" sizes="(max-width: 607px) 100vw, 607px" /></div>
</figure>
<p data-pid="9gMIycL8">计算下来，C0G电容最终的TCC为：0×（-1）ppm/℃±30ppm/℃。而相应的其他Ⅰ类陶瓷的温度系数，例如U2J电容，计算下来则为：-750 ppm/℃±120 ppm/℃。</p>
<figure data-size="normal">
<div class="RichText-ConditionalImagePortal"><img loading="lazy" decoding="async" class="alignnone size-full wp-image-3091" src="https://e673.com/wp-content/uploads/2026/02/v2-a873a3920e3aff9326c0e4203dbb8209_1440w.jpg" width="588" height="338" srcset="https://e673.com/wp-content/uploads/2026/02/v2-a873a3920e3aff9326c0e4203dbb8209_1440w.jpg 588w, https://e673.com/wp-content/uploads/2026/02/v2-a873a3920e3aff9326c0e4203dbb8209_1440w-500x287.jpg 500w" sizes="(max-width: 588px) 100vw, 588px" /></div>
</figure>
<h2 id="h_454453669_2" data-into-catalog-status=""><b>NPO和C0G是同一种电容吗？</b></h2>
<p data-pid="lNScXpas">NPO是美国军用标准（<u><a class=" wrap external" href="https://link.zhihu.com/?target=http%3A//www.hqpcb.com/zhuoluye11/%3Ftid%3D26%26plan%3Dfashaoyou" target="_blank" rel="nofollow noopener noreferrer" data-za-detail-view-id="1043">MI</a></u>L）中的说法，其实应该是NP0（零），但一般大家习惯写成NPO（欧）。这是Negative-Positive-Zero的简写，用来表示的温度特性。说明NPO的电容温度特性很好，不随正负温度变化而出现容值漂移。</p>
<p data-pid="NCqnpYgD">从前面我们已经知道，C0G是I类陶瓷中温度稳定性最好的一种，温度特性近似为0，满足“负-正-零”的含义。所以C0G其实和NPO是一样的，只不过是两个标准的两种表示方法（当然，容值更小、精度略差一点的C0K、C0J等也是NPO电容）。类似的，U2J对应于MIL标准中的组别代码为N750。</p>
<p data-pid="bs3A31g8">NPO 电容器随封装形式不同其电容量和介质损耗随频率变化的特性也不同，大封装尺寸的要比小封装尺寸的频率特性好。</p>
<figure data-size="normal">
<div class="RichText-ConditionalImagePortal"><img loading="lazy" decoding="async" class="alignnone size-full wp-image-3092" src="https://e673.com/wp-content/uploads/2026/02/v2-75de4cc45374fec3c8a2a5dd12f1c74e_1440w.jpg" width="619" height="487" srcset="https://e673.com/wp-content/uploads/2026/02/v2-75de4cc45374fec3c8a2a5dd12f1c74e_1440w.jpg 619w, https://e673.com/wp-content/uploads/2026/02/v2-75de4cc45374fec3c8a2a5dd12f1c74e_1440w-500x393.jpg 500w" sizes="(max-width: 619px) 100vw, 619px" /></div>
</figure>
<h2 id="h_454453669_3" data-into-catalog-status=""><b>什么是Ⅱ类陶瓷，有什么特点？</b></h2>
<p data-pid="KPxFuq5l">Ⅱ类陶瓷电容器（Class Ⅱ ceramic capacitor）过去称为为低频陶瓷电容器（Low frequency ceramic capacitor），指用铁电陶瓷作介质的电容器，因此也称铁电陶瓷电容器。这类电容器的比电容大，电容量随温度呈非线性变化，损耗较大，常在电子设备中用于旁路、耦合或用于其它对损耗和电容量稳定性要求不高的电路中。其中Ⅱ类陶瓷电容器又分为稳定级和可用级。X5R、X7R属于Ⅱ类陶瓷的稳定级，而Y5V和Z5U属于可用级。</p>
<p data-pid="Qty64DSq">X5R、X7R、Y5V、Z5U之间的区别是什么？</p>
<p data-pid="UWHCzS1b">区别主要还在于温度范围和容值随温度的变化特性上。下表提示了这些代号的含义。</p>
<figure data-size="normal">
<div class="RichText-ConditionalImagePortal"><img loading="lazy" decoding="async" class="alignnone size-full wp-image-3093" src="https://e673.com/wp-content/uploads/2026/02/v2-a8317644e70edf931af359824a82ef79_1440w.jpg" width="576" height="326" srcset="https://e673.com/wp-content/uploads/2026/02/v2-a8317644e70edf931af359824a82ef79_1440w.jpg 576w, https://e673.com/wp-content/uploads/2026/02/v2-a8317644e70edf931af359824a82ef79_1440w-500x283.jpg 500w, https://e673.com/wp-content/uploads/2026/02/v2-a8317644e70edf931af359824a82ef79_1440w-520x293.jpg 520w" sizes="(max-width: 576px) 100vw, 576px" /></div>
</figure>
<p data-pid="aSX6N7vR">以X7R为例。</p>
<p data-pid="hsqNYynX">X 代表电容最低可工作在 -55℃</p>
<p data-pid="U-LPv4cj">7 代表电容最高可工作在 +125℃</p>
<p data-pid="9XiySm3H">R 代表容值随温度的变化为 ±15%</p>
<p data-pid="u8shGNgf">同样的，Y5V正常工作温度范围在-30℃～+85℃, 对应的电容容量变化为+22～82%；而Z5U 正常工作温度范围在+10℃～+85℃，对应的电容容量变化为+22～-56%。</p>
<figure data-size="normal">
<div class="RichText-ConditionalImagePortal"><img loading="lazy" decoding="async" class="alignnone size-full wp-image-3094" src="https://e673.com/wp-content/uploads/2026/02/v2-9dab5bd9e27f4826c7dd4ee623a43492_1440w.jpg" width="576" height="334" srcset="https://e673.com/wp-content/uploads/2026/02/v2-9dab5bd9e27f4826c7dd4ee623a43492_1440w.jpg 576w, https://e673.com/wp-content/uploads/2026/02/v2-9dab5bd9e27f4826c7dd4ee623a43492_1440w-500x290.jpg 500w" sizes="(max-width: 576px) 100vw, 576px" /></div>
</figure>
</header>
</article>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>取消新款MacBook的掀盖自动启动功能以及恢复开机音效</title>
		<link>https://e673.com/to-disable-the-lid-auto-power-on-and-restart-chime-on-new-macbooks/</link>
		
		<dc:creator><![CDATA[ahe]]></dc:creator>
		<pubDate>Wed, 25 Feb 2026 12:22:59 +0000</pubDate>
				<category><![CDATA[经验分享]]></category>
		<category><![CDATA[OS X]]></category>
		<guid isPermaLink="false">https://e673.com/?p=3085</guid>

					<description><![CDATA[去年 2016 新款 MacBo&#46;&#46;&#46;]]></description>
										<content:encoded><![CDATA[<p>去年 2016 新款 MacBook Pro 起，預設打開筆電上蓋或連接電源供應器時，電腦就會自動開機而且開機時沒有啟動聲，本文教你如何將這些東西調整回原本的樣子。</p>
<p>以下指令都是透過「工具程式」→「終端機」來達成。</p>
<p>取消自動開機：</p><pre class="crayon-plain-tag">sudo nvram AutoBoot=%00</pre><p>重新啟用自動開機：</p><pre class="crayon-plain-tag">sudo nvram AutoBoot=%03</pre><p>開啟開機音效：</p><pre class="crayon-plain-tag">sudo nvram BootAudio=%01</pre><p>取消開機音效：</p><pre class="crayon-plain-tag">sudo nvram BootAudio=%00</pre><p>輸入指令後若提示你輸入密碼，就打吧！</p>
<p>以上，提供大家參考。</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>龙芯架构软件体系中的旧世界与新世界</title>
		<link>https://e673.com/the-new-world-old-world-in-loongarch/</link>
		
		<dc:creator><![CDATA[ahe]]></dc:creator>
		<pubDate>Fri, 19 Dec 2025 05:57:44 +0000</pubDate>
				<category><![CDATA[信创环境]]></category>
		<category><![CDATA[国产系统]]></category>
		<guid isPermaLink="false">https://e673.com/?p=3077</guid>

					<description><![CDATA[我需要关心这问题吗？ 简单来讲，&#46;&#46;&#46;]]></description>
										<content:encoded><![CDATA[<div class="theme-admonition theme-admonition-info admonition_xJq3 alert alert--info">
<div class="admonitionHeading_Gvgb">我需要关心这问题吗？</div>
<div class="admonitionContent_BuS1">
<p>简单来讲，如果您不自行编译安装软件，或许就不必关心。 当然，随意浏览下这篇文章，以后见到这俩说法不至于一头雾水，或者其他同学遇到问题您可以给 tā 们指路，也是不错的。</p>
<ul>
<li>如果您目前在龙架构电脑上使用 Loongnix、麒麟或者 UOS 这些系统，几个月或一两年之后，一定会有一次全系统升级。
<ul>
<li>如果您不升级，那么本身外界如何变化也与您无关。</li>
<li>如果您升级，那么升级之后您应该也感受不到使用上的差别，这其实就是「移民新世界」了。</li>
</ul>
</li>
<li>如果您目前在龙架构电脑上使用 Arch、Gentoo 等等这些系统，那么您已经是新世界住民了，这一切也与您无关。</li>
</ul>
<p>会被坑到的情况目前来看只有：</p>
<ul>
<li>您使用 Loongnix、麒麟或者 UOS 这些系统，但自行编译了一些要用的软件。 在未来那次全系统升级之后，您自行编译的软件应该不再能工作，需要重新编译或从系统包管理器安装。</li>
<li>您是为龙架构适配或开发软件的开发者用户。 会来到这个页面，基本是已经被坑到了，那就往下读吧！</li>
</ul>
</div>
</div>
<p>截至目前（2024 年初），LoongArch 有两套不兼容的软件体系，习惯上大家把它们叫作「旧世界」和「新世界」。 龙芯中科的材料中也有「ABI1.0」、「ABI2.0」的提法（目前所见的表述均未在 ABI 与数字之间加空格）。</p>
<p><strong>旧世界</strong>是指最早在龙芯中科内部适配的、随着 LoongArch 公开一并发布的那个 LoongArch 软件生态。 <strong>新世界</strong>是指龙芯中科与社区同仁一道，以典型开源社区协作模式打造的，完全开源的 LoongArch 软件生态。</p>
<p>两个世界的产生是龙芯中科对 LoongArch 采取了秘密开发、突然全盘推出的商业策略， 由于未能预见到这一版工作有些地方不得不做不兼容修改，而使客户和自身不得不面对的无奈后果。 按照目前的趋势和一些公开消息，未来旧世界将逐渐消亡。 从龙芯 3A6000 一代产品起，相关产品的出厂配套固件都已达到兼容新、旧世界的状态， 但就 2024 年 2 月初的现状而言，可能发行版方面（Loongnix 及其他商业发行版）仍需更长时间才能完成迁移， 因此未能赶上 3A6000 的正式发布。</p>
<p>在讨论龙芯话题时，新旧世界的说法仅仅被用来区分两个不兼容的 LoongArch 生态。 MIPS 型号的龙芯既不是新世界也不是旧世界。 一般只会说「MIPS 时代的龙芯」（the MIPS-era Loongson）怎么怎么样。</p>
<p>「旧世界」、「新世界」的名词形式英译即为「the old world」、「the new world」。 作形容词时一般以连字符连接前后部分即「old-world」、「new-world」。 如果在一段话中频繁使用，有时也会用「OW」、「NW」的缩写形式。</p>
<h2 id="我在哪个世界" class="anchor anchorWithStickyNavbar_LWe7">我在哪个世界？</h2>
<p>如果符合以下任一条件，你就在用<strong>旧世界</strong>：</p>
<ul>
<li>系统是麒麟、Loongnix、UOS 其中之一</li>
<li>内核版本以 4.19 开头</li>
<li>有 WPS 用但没有安装过 <code>libLoL</code> 等旧世界兼容方案</li>
</ul>
<p>如果一条都没中，你就在用<strong>新世界</strong>。</p>
<p>这个判断方法是基于 2024 年 2 月的已知信息设计的。 后续如果没及时更新，可能会不准。</p>
<h2 id="手头这软件是哪个世界的" class="anchor anchorWithStickyNavbar_LWe7">手头这软件是哪个世界的？</h2>
<p>可以使用 <code>file</code> 工具方便地检查一个二进制程序属于哪个世界。 假设你想检查 <code>someprogram</code> 这个文件，就执行 <code>file someprogram</code>，如果输出的行含有这些字样：</p>
<div class="codeBlockContainer_Ckt0 theme-code-block">
<div class="codeBlockContent_biex">
<pre class="crayon-plain-tag">&lt;span class=&quot;token-line&quot;&gt;&lt;span class=&quot;token plain&quot;&gt;interpreter /lib64/ld.so.1, for GNU/Linux 4.15.0&lt;/span&gt;
&lt;/span&gt;</pre></p>
<div class="buttonGroup__atx"></div>
</div>
</div>
<p>就表明这是一个旧世界程序。</p>
<p>相应地，如果输出的行含有这些字样：</p>
<div class="codeBlockContainer_Ckt0 theme-code-block">
<div class="codeBlockContent_biex">
<pre class="crayon-plain-tag">&lt;span class=&quot;token-line&quot;&gt;&lt;span class=&quot;token plain&quot;&gt;interpreter /lib64/ld-linux-loongarch-lp64d.so.1, for GNU/Linux 5.19.0&lt;/span&gt;
&lt;/span&gt;</pre></p>
<div class="buttonGroup__atx"></div>
</div>
</div>
<p>就表明这是一个新世界程序。</p>
<p>以上的判断都适用于系统 libc 为 glibc 且动态链接的程序。如果程序是静态链接的，便没有 interpreter 信息； 如果程序是 Go 语言的或者使用了 musl 作为 C 库，那么文件里就没有对应到 <code>for GNU/Linux</code> 这部分信息的标记。 这种时候试着运行一下就可以了，「异世界」的程序几乎没有可能正常启动。</p>
<p>当然，如果你得到的输出类似下边几行之一：</p>
<div class="codeBlockContainer_Ckt0 theme-code-block">
<div class="codeBlockContent_biex">
<pre class="crayon-plain-tag">&lt;span class=&quot;token-line&quot;&gt;&lt;span class=&quot;token plain&quot;&gt;someprogram: Python script, Unicode text, UTF-8 text executable&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;token-line&quot;&gt;&lt;span class=&quot;token plain&quot;&gt;someprogram: Bourne-Again shell script, ASCII text executable, with escape sequences&lt;/span&gt;
&lt;/span&gt;</pre></p>
<div class="buttonGroup__atx"></div>
</div>
</div>
<p>这种情况代表你检查的程序是个脚本。 一般脚本语言的程序都无所谓新旧世界的，但它仍然有可能依赖一些二进制组件才能正常工作： 因此最靠谱的方法还是试着跑一下！</p>
<h2 id="为啥叫新旧世界" class="anchor anchorWithStickyNavbar_LWe7">为啥叫「新旧世界」？</h2>
<p>在汉语圈其他领域已有「新旧世界」的说法了，例如「旧/新世界猴」、「旧/新世界葡萄酒」： 这里的「世界」其实就是「大陆」。 LoongArch 的新世界也比旧世界出现得晚，并且也存在做事方式上的不兼容，相互之间也有一定的沟通交流， 因此这样的类比在一定程度上还比较贴切。</p>
<p>在技术领域，「新旧世界」的说法也有先例。怀旧计算（retro-computing）群体对 Macintosh 电脑的不同型号就使用 Old World、New World 来区分。 他们的「旧世界」是指系统硬件包含一块 ROM 芯片，其中搭载了 Macintosh Toolbox 这个老固件。 「新世界」硬件则没有这块芯片。 使用 Old/New World ROM 的 Macintosh 型号就被叫作 Old/New World Macs。</p>
<p>另一方面，使用 Linux 源码发行版如 Gentoo 的用户群体， 口语上也有「重做世界」（rebuild world）之类的表达。 此处的「世界」是系统上所有包的统称， 这层意义上 world 的含义类似 userland：统称在用户态运行的所有程序。 对 Gentoo 用户而言，还恰好表示 <code>@world</code> 集合。 而 LoongArch 的新旧世界区分，很大程度上正是由于内核态提供的系统调用界面有细微差别， 导致了用户态的程序相互间不兼容。</p>
<h2 id="俩世界区别在哪" class="anchor anchorWithStickyNavbar_LWe7">俩世界区别在哪？</h2>
<p><strong>源码开放程度不一样</strong>。 新世界都是开源代码，而旧世界的部分底层代码由于知识产权等原因始终没有开放，尽管其中也有一部分后来放出了。 比方说旧世界的 binutils、gcc 在最初发布之后过了几个月有了完整源码，Linux 源码直到 2023 年才有， 但 GSGPU 的 shader 编译器源码就始终没有。 放出的源码基本也比较少有完整的 Git 提交历史，因此不便基于它二次修改或者将其移植到上游新版本。</p>
<p><strong>可用的发行版不一样</strong>。 由于外界拿不到旧世界的完整源码，旧世界发行版只有几个商业公司能做。 社区制作的发行版都属于新世界。</p>
<p>目前已知的旧世界发行版（移植）有：（按英文名字母顺序排序）</p>
<ul>
<li>麒麟 (Kylin)</li>
<li>Loongnix</li>
<li>UOS</li>
</ul>
<p>目前已知的新世界发行版（移植）有：（按英文名字母顺序排序）</p>
<ul>
<li><a class="link--overseas" href="https://www.altlinux.org/Ports/loongarch64" target="_blank" rel="noopener noreferrer">ALT Linux</a></li>
<li><a class="link--overseas" href="https://aosc.io/zh-cn" target="_blank" rel="noopener noreferrer">AOSC OS</a></li>
<li><a class="link--overseas" href="https://github.com/sunhaiyong1978/CLFS-for-LoongArch" target="_blank" rel="noopener noreferrer">CLFS 手册与成品</a></li>
<li><a class="link--overseas" href="https://wiki.debian.org/Ports/loong64" target="_blank" rel="noopener noreferrer">Debian</a></li>
<li><a class="link--overseas" href="https://github.com/fedora-remix-loongarch/releases-info" target="_blank" rel="noopener noreferrer">Fedora LoongArch Remix</a></li>
<li><a class="link--overseas" href="https://wiki.gentoo.org/wiki/Project:LoongArch" target="_blank" rel="noopener noreferrer">Gentoo</a></li>
<li><a class="link--overseas" href="https://github.com/loongarchlinux" target="_blank" rel="noopener noreferrer">Loong Arch Linux</a></li>
<li><a class="link--overseas" href="https://github.com/shipujin/slackware-loongarch64" target="_blank" rel="noopener noreferrer">Slackware</a></li>
<li><a class="link--overseas" href="https://github.com/sunhaiyong1978/Yongbao" target="_blank" rel="noopener noreferrer">Yongbao</a></li>
</ul>
<div class="theme-admonition theme-admonition-warning admonition_xJq3 alert alert--warning">
<div class="admonitionHeading_Gvgb">注意</div>
<div class="admonitionContent_BuS1">
<p>有些新世界发行版是由龙芯员工制作，因此相比其他纯社区工作， 可能会额外集成一些：</p>
<ul>
<li>尚未正式合入上游的代码（如内核的二进制翻译支持）</li>
<li>授权不清晰的内容（如 LATX 没有许可协议，默认为所有权利保留，无法被龙芯中科以外的主体集成、分发等）</li>
<li>尚未开源的内容（如 libffi、LibreOffice、Chromium 等软件，在 CLFS、Loong Arch Linux 很早就有包了，比相应的开源补丁最早一版还早。它们的移植都更晚甚至仍未完全进入上游。最极端的例子是 2021 年初 LoongArch 工具链、内核源码、QEMU 移植都还没开源，硬件也很难买到，CLFS 就已经出来了。）</li>
</ul>
<p>不过随着时间推移，更多内容会进入上游或变成熟， 这种情况以后也会越来越少直至消失。</p>
</div>
</div>
<p><strong>软件版本不一样</strong>。 旧世界的基础组件版本主要跟随当初移植时基于的 Debian 或 RHEL 大版本。 因为商业公司不一定有优先级（或者能力）去关心跟进新版本的事情， 所以旧世界的基础组件版本几乎不会有大的更新。 视具体用户场景和开发、部署习惯而定，有时候这是个好事，有时候很糟心。</p>
<p>以下是一些常见软件、开发工具在两个世界的版本对比：</p>
<table>
<thead>
<tr>
<th>软件</th>
<th>旧世界版本</th>
<th>新世界版本</th>
</tr>
</thead>
<tbody>
<tr>
<td>Linux</td>
<td>4.19</td>
<td>≥ 5.19，常见 ≥ 6.1</td>
</tr>
<tr>
<td>binutils</td>
<td>2.31</td>
<td>≥ 2.38，常见 ≥ 2.40</td>
</tr>
<tr>
<td>gcc</td>
<td>8.3</td>
<td>≥ 12.1，常见 ≥ 13.1</td>
</tr>
<tr>
<td>glibc</td>
<td>2.28</td>
<td>≥ 2.36</td>
</tr>
<tr>
<td>LLVM</td>
<td>8</td>
<td>≥ 16</td>
</tr>
<tr>
<td>Node.js</td>
<td>14.16.1</td>
<td>≥ 18</td>
</tr>
<tr>
<td>Go</td>
<td>1.15、1.18、1.19</td>
<td>≥ 1.19</td>
</tr>
<tr>
<td>Rust</td>
<td>1.41、1.58</td>
<td>≥ 1.71</td>
</tr>
</tbody>
</table>
<h2 id="怎么兼容两个世界" class="anchor anchorWithStickyNavbar_LWe7">怎么兼容两个世界？</h2>
<p>鉴于新旧世界的差异可谓不大不小，想要实现完美的兼容性十分困难：一个方案无法同时具备以下的全部优势，而必须作出取舍。</p>
<ul>
<li>所需磁盘空间尽可能少，</li>
<li>性能开销尽可能低，</li>
<li>对宿主系统的侵入式修改尽可能少，</li>
<li>（尤指极端场景下）尽量保证正确性：不使原世界本应成功的操作在兼容下失败，也不使原世界本应失败的操作在兼容下成功。</li>
</ul>
<p>目前，源自 AOSC 社区的 <a class="link--overseas" href="https://liblol.aosc.io/" target="_blank" rel="noopener noreferrer"><code>libLoL</code></a> 是完成度最高的解决方案，已为许多新世界发行版所集成。 龙芯方面也表态过会开发兼容方案，但截至 2024 年 1 月，未有任何此方面的公开消息。</p>
<h2 id="常见的坑" class="anchor anchorWithStickyNavbar_LWe7">常见的坑</h2>
<h3 id="执行一个程序报没有那个文件或目录咋回事" class="anchor anchorWithStickyNavbar_LWe7">执行一个程序，报「没有那个文件或目录」，咋回事？</h3>
<p>如果您执行一个程序却被告知这个程序不存在，类似这样：</p>
<div class="language-sh-session codeBlockContainer_Ckt0 theme-code-block">
<div class="codeBlockContent_biex">
<pre class="crayon-plain-tag">&lt;span class=&quot;token-line&quot;&gt;&lt;span class=&quot;token command shell-symbol important&quot;&gt;$&lt;/span&gt; &lt;span class=&quot;token command bash language-bash&quot;&gt;./foo&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;token-line&quot;&gt;&lt;span class=&quot;token output&quot;&gt;zsh: no such file or directory: ./foo&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;token-line&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;token-line&quot;&gt;&lt;span class=&quot;token command shell-symbol important&quot;&gt;$&lt;/span&gt; &lt;span class=&quot;token command bash language-bash&quot;&gt;./foo&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;token-line&quot;&gt;&lt;span class=&quot;token output&quot;&gt;zsh: 没有那个文件或目录: ./foo&lt;/span&gt;
&lt;/span&gt;</pre></p>
<div class="buttonGroup__atx"></div>
</div>
</div>
<p>如果这个文件确实存在，那么大概率是您在尝试执行异世界的程序。 不存在的文件并非程序本身，而是所谓「ELF 解释器」， 即上文教你判断一个程序是新世界还是旧世界那里提到的 ELF interpreter。 请换对应您系统的程序版本，或要求软件提供者增加适配。</p>
<h3 id="我给龙架构交叉编译的-go-程序运行报段错误咋回事" class="anchor anchorWithStickyNavbar_LWe7">我给龙架构交叉编译的 Go 程序，运行报段错误，咋回事？</h3>
<p>可能是由于未使用正确的 Go 工具链，导致不经意间构建了与您预期 ABI 相异的二进制。</p>
<ul>
<li>为旧世界发行版构建，必须搭配使用龙芯提供的 Go 工具链与 goproxy 源（所谓「龙芯源」；见下）。</li>
<li>为新世界发行版构建，必须搭配使用上游版本的 Go 工具链，不可使用「龙芯源」。</li>
</ul>
<p>具体而言，Go 程序在异世界运行时，初始化过程中必须的一次 <code>rt_sigprocmask</code> 系统调用会由于它使用的 <code>NSIG</code> 常量定义与当前运行内核不同而失败， 此时 Go 会故意访问一个非法地址直接崩溃： 此时从程序视角看，必然成功的系统调用居然失败，这说明内核服务已经不再可靠，继续下去没有意义。</p>
<h3 id="龙芯提供了很多镜像源龙芯源我能用吗" class="anchor anchorWithStickyNavbar_LWe7">龙芯提供了很多镜像源（「龙芯源」），我能用吗？</h3>
<p>龙芯确实提供了很多「龙芯源」。<strong>旧世界开发者必须使用（系统可能已默认加载了相应的配置修改），新世界开发者不可使用。</strong></p>
<p>出于 SEO 以及行侠仗义（天下开发者是一家）的精神，此处破例提供相应的旧世界文档。</p>
<table>
<thead>
<tr>
<th>种类</th>
<th>典型地址</th>
<th>备注</th>
</tr>
</thead>
<tbody>
<tr>
<td>Go</td>
<td><code>http://goproxy.loongnix.cn:3000</code></td>
<td><a href="https://docs.loongnix.cn/golang/goproxy.html" target="_blank" rel="noopener noreferrer">文档</a></td>
</tr>
<tr>
<td>PyPI</td>
<td><code>https://pypi.loongnix.cn/loongson/pypi</code></td>
<td><a href="https://docs.loongnix.cn/python/python.html" target="_blank" rel="noopener noreferrer">文档</a></td>
</tr>
<tr>
<td>npm</td>
<td><code>https://registry.loongnix.cn:4873</code></td>
<td><a href="http://docs.loongnix.cn/nodejs/doc/list/03.%E9%BE%99%E8%8A%AFnpm%E7%9A%84%E5%AE%89%E8%A3%85%E5%92%8C%E4%BB%93%E5%BA%93%E9%85%8D%E7%BD%AE%E4%BD%BF%E7%94%A8.html" target="_blank" rel="noopener noreferrer">文档</a></td>
</tr>
<tr>
<td>NuGet</td>
<td><code>https://nuget.loongnix.cn</code></td>
<td><a href="https://docs.loongnix.cn/dotnet/support/list/01.%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98-FAQ.html" target="_blank" rel="noopener noreferrer">文档</a></td>
</tr>
<tr>
<td>Rust<br />
(crates.io)</td>
<td><code>https://crates.loongnix.cn</code></td>
<td><a href="https://docs.loongnix.cn/rust/" target="_blank" rel="noopener noreferrer">文档</a></td>
</tr>
<tr>
<td>Harbor<br />
(容器镜像)</td>
<td><code>https://cr.loongnix.cn</code></td>
<td><a class="link--overseas" href="https://loongson-cloud-community.github.io/Loongson-Cloud-Community" target="_blank" rel="noopener noreferrer">文档</a></td>
</tr>
</tbody>
</table>
<p>由于旧世界 ABI、API 并未上游，也不会被上游，那些需要关心系统底层 ABI、API 细节的包， 其正式版本——也就是从上游或常规镜像站下到的版本——在旧世界不可能正常工作：要么没适配过龙架构，要么适配的是新世界。 因此为了方便为旧世界适配软件，龙芯搭建了这些源：那些会受影响的包和相应版本，在这些源里被针对旧世界改过了。</p>
<p>这就是为何新世界开发者不可贪图方便使用它们：从这些软件源中下载的有些包，对新世界而言反而是坏的，更何况完整性校验不会通过——龙芯提供魔改代码的行为与「中间人攻击」没有外观上的差别。 换个角度看，这也是旧世界开发者必须开启它们，并且关闭相应的完整性校验的原因。</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Nuget 生成本地包源v3 feed的自动批处理脚本</title>
		<link>https://e673.com/nuget-create-local-v3-feed-batch-script/</link>
		
		<dc:creator><![CDATA[ahe]]></dc:creator>
		<pubDate>Fri, 05 Dec 2025 00:29:44 +0000</pubDate>
				<category><![CDATA[应用笔记]]></category>
		<category><![CDATA[.Net]]></category>
		<guid isPermaLink="false">https://e673.com/?p=3074</guid>

					<description><![CDATA[[crayon-6a173a5f&#46;&#46;&#46;]]></description>
										<content:encoded><![CDATA[<p></p><pre class="crayon-plain-tag">@echo off
setlocal enabledelayedexpansion

:: ========================================
:: 配置区 —— 请按需修改
:: ========================================
set RAW_ROOT=F:\third_ext\NAPS2
set LOCAL_FEED=F:\third_ext\LocalFeed
set NUGET_EXE=F:\Tools\nuget.exe
:: ========================================


echo.
echo ======= 自动 NuGet Feed 更新（安全版） =======
echo RAW_ROOT = %RAW_ROOT%
echo LOCAL_FEED = %LOCAL_FEED%
echo NUGET_EXE = %NUGET_EXE%
echo ================================================
echo.

:: ===== 检查路径是否存在 =====
if not exist "%RAW_ROOT%" (
echo [错误] RAW_ROOT 路径不存在：
echo %RAW_ROOT%
pause
exit /b 1
)

if not exist "%NUGET_EXE%" (
echo [错误] 未找到 nuget.exe：
echo %NUGET_EXE%
pause
exit /b 1
)

:: ===== 创建本地 Feed 目录 =====
if not exist "%LOCAL_FEED%" (
echo [Info] LOCAL_FEED 不存在，正在创建...
mkdir "%LOCAL_FEED%"
)

:: ===== 初始化 feed =====
if not exist "%LOCAL_FEED%\index.json" (
echo [Info] 正在初始化 NuGet V3 feed...
"%NUGET_EXE%" init "%RAW_ROOT%" "%LOCAL_FEED%"
echo 初始化完成！
)

echo.
echo [Info] 开始扫描子目录...
echo.

:: ===== 递归扫描所有 .nupkg（必须加引号） =====
for /R "%RAW_ROOT%" %%f in (*.nupkg) do (
echo [Add] %%f

:: 强制使用引号，避免路径非法
"%NUGET_EXE%" add "%%f" -Source "%LOCAL_FEED%"
echo.
)

echo ================================================
echo 所有包已成功导入！
echo 本地源： "%LOCAL_FEED%"
echo ================================================
echo.

pause
exit /b 0</pre><p>&nbsp;</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>.Net Core web项目“检测到URL存在 http host 头攻击漏洞”的修复及验证</title>
		<link>https://e673.com/net-core-web-fix-http-host-header-attack-vulnerabilities/</link>
		
		<dc:creator><![CDATA[ahe]]></dc:creator>
		<pubDate>Wed, 26 Nov 2025 00:34:02 +0000</pubDate>
				<category><![CDATA[应用笔记]]></category>
		<category><![CDATA[.Net]]></category>
		<category><![CDATA[运维]]></category>
		<guid isPermaLink="false">https://e673.com/?p=3066</guid>

					<description><![CDATA[基于.Net 6.0的SSO认证&#46;&#46;&#46;]]></description>
										<content:encoded><![CDATA[<p>基于.Net 6.0的SSO认证小应用，交付后进行web安全扫描，提示“检测到URL存在 http host 头攻击漏洞”，验证过程：</p>
<p>Chrome浏览器安装ModHeader插件，启用插件后，在ModHeader中添加Host  abcdexxx.com，此时刷新目标网址，如果能正常显示或试图跳转至abcdexxx.com ，则证明漏洞存在。修复后应提示400 host invalid，无法正常访问。</p>
<p>ModHeader插件下载<a href="https://e673.com/wp-content/uploads/2025/11/modheader_idgpnmonknjnojddfkpgkljpfnnfcklj.zip">modheader_idgpnmonknjnojddfkpgkljpfnnfcklj</a></p>
<p>以下是修复过程：</p>
<h1 data-start="116" data-end="143"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Host Header Attack 是什么？</h1>
<p data-start="144" data-end="177">攻击者通过伪造 <code data-start="152" data-end="159">Host:</code> 请求头，使应用产生错误行为，例如：</p>
<ul data-start="178" data-end="235">
<li data-start="178" data-end="198">
<p data-start="180" data-end="198">伪造生成带恶意域名的回调 URL</p>
</li>
<li data-start="199" data-end="220">
<p data-start="201" data-end="220">绕过某些基于 Host 判断的逻辑</p>
</li>
<li data-start="221" data-end="235">
<p data-start="223" data-end="235">触发开放重定向等漏洞</p>
</li>
</ul>
<p data-start="237" data-end="287">IIS 默认不会严格验证 Host，所以 ASP.NET Core 应用必须自己限制可信 Host。</p>
<hr data-start="289" data-end="292" />
<h1 data-start="294" data-end="351"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f680.png" alt="🚀" class="wp-smiley" style="height: 1em; max-height: 1em;" /> <strong data-start="299" data-end="351">ASP.NET Core 官方推荐做法：使用 Host Filtering Middleware</strong></h1>
<p data-start="353" data-end="433">ASP.NET Core 自带 <code data-start="369" data-end="405">Microsoft.AspNetCore.HostFiltering</code> 中间件，可以 <strong data-start="413" data-end="432">限制允许访问的 Host 列表</strong>。</p>
<h2 data-start="435" data-end="469">1）在 appsettings.json 添加允许的 Host</h2>
<div class="contain-inline-size rounded-2xl relative bg-token-sidebar-surface-primary">
<div class="sticky top-9">
<div class="absolute end-0 bottom-0 flex h-9 items-center pe-2">
<div class="bg-token-bg-elevated-secondary text-token-text-secondary flex items-center gap-4 rounded-sm px-2 font-sans text-xs"></div>
</div>
</div>
<div class="overflow-y-auto p-4" dir="ltr"><code class="whitespace-pre! language-json"><span class="hljs-punctuation">{</span><br />
<span class="hljs-attr">"AllowedHosts"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"www.yourdomain.com;yourdomain.com;localhost"</span><br />
<span class="hljs-punctuation">}</span><br />
</code></div>
</div>
<p data-start="552" data-end="583"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/26a0.png" alt="⚠" class="wp-smiley" style="height: 1em; max-height: 1em;" /> <strong data-start="555" data-end="575">必须列出所有可能访问的 Host</strong><br data-start="575" data-end="578" />例如：</p>
<ul data-start="584" data-end="672">
<li data-start="584" data-end="594">
<p data-start="586" data-end="594">不带 www</p>
</li>
<li data-start="595" data-end="604">
<p data-start="597" data-end="604">带 www</p>
</li>
<li data-start="605" data-end="620">
<p data-start="607" data-end="620">内网 IP（如有需要）</p>
</li>
<li data-start="621" data-end="672">
<p data-start="623" data-end="672">HTTPS 端口如使用 5001 这种自定义端口要标明：<code data-start="651" data-end="672">yourdomain.com:5001</code></p>
</li>
</ul>
<hr data-start="674" data-end="677" />
<h2 data-start="679" data-end="727">2）在 Program.cs / Startup.cs 启用 Host Filtering</h2>
<h3 data-start="729" data-end="752">.NET 6+（Program.cs）</h3>
<div class="contain-inline-size rounded-2xl relative bg-token-sidebar-surface-primary">
<div class="sticky top-9">
<div class="absolute end-0 bottom-0 flex h-9 items-center pe-2">
<div class="bg-token-bg-elevated-secondary text-token-text-secondary flex items-center gap-4 rounded-sm px-2 font-sans text-xs"></div>
</div>
</div>
<div class="overflow-y-auto p-4" dir="ltr">
<p><code class="whitespace-pre! language-csharp"><span class="hljs-keyword">var</span> builder = WebApplication.CreateBuilder(args);</code></p>
<p>builder.Services.AddControllers();</p>
<p><span class="hljs-comment">// 启用 Host Filtering</span><br />
builder.Services.AddHostFiltering(options =&gt;<br />
{<br />
<span class="hljs-comment">// 自动从 AllowedHosts 加载</span><br />
});</p>
<p><span class="hljs-keyword">var</span> app = builder.Build();</p>
<p>app.UseHostFiltering();</p>
<p>app.UseRouting();<br />
app.UseAuthorization();<br />
app.MapControllers();<br />
app.Run();</p>
</div>
</div>
<h3 data-start="1084" data-end="1102">若使用 Startup.cs</h3>
<div class="contain-inline-size rounded-2xl relative bg-token-sidebar-surface-primary">
<div class="sticky top-9">
<div class="absolute end-0 bottom-0 flex h-9 items-center pe-2">
<div class="bg-token-bg-elevated-secondary text-token-text-secondary flex items-center gap-4 rounded-sm px-2 font-sans text-xs"></div>
</div>
</div>
<div class="overflow-y-auto p-4" dir="ltr"><code class="whitespace-pre! language-csharp"><code class="whitespace-pre! language-csharp"><span class="hljs-function"><span class="hljs-keyword">public</span></span> <span class="hljs-keyword">void</span> <span class="hljs-title">ConfigureServices</span>(<span class="hljs-params">IServiceCollection services</span>)<br />
{<br />
services.AddControllers();<br />
services.AddHostFiltering();<br />
}</code></code><span class="hljs-function"><span class="hljs-keyword">public</span></span> <span class="hljs-keyword">void</span> <span class="hljs-title">Configure</span>(<span class="hljs-params">IApplicationBuilder app, IWebHostEnvironment env</span>)<br />
{<br />
app.UseHostFiltering();<br />
app.UseRouting();<br />
app.UseAuthorization();<br />
app.UseEndpoints(endpoints =&gt;<br />
{<br />
endpoints.MapControllers();<br />
});<br />
}</p>
</div>
</div>
<hr data-start="1485" data-end="1488" />
<h1 data-start="1490" data-end="1502"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/270b.png" alt="✋" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 中间件会做什么？</h1>
<p data-start="1503" data-end="1535">当请求头中 <code data-start="1509" data-end="1516">Host:</code> 不在 AllowedHosts 内：</p>
<ul data-start="1536" data-end="1613">
<li data-start="1536" data-end="1573">
<p data-start="1538" data-end="1573">ASP.NET Core 返回 <strong data-start="1554" data-end="1573">400 Bad Request</strong></p>
</li>
<li data-start="1574" data-end="1613">
<p data-start="1576" data-end="1613">阻止进一步处理请求<br data-start="1585" data-end="1588" />→ 有效修补 Host Header Attack</p>
</li>
</ul>
<hr data-start="1615" data-end="1618" />
<h1 data-start="1620" data-end="1637"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f512.png" alt="🔒" class="wp-smiley" style="height: 1em; max-height: 1em;" /> IIS 额外加固（可选）</h1>
<h2 data-start="1639" data-end="1667">1）限制操作系统级别的 Host 绑定（强烈推荐）</h2>
<p data-start="1668" data-end="1680">在 IIS 网站绑定中：</p>
<ul data-start="1681" data-end="1743">
<li data-start="1681" data-end="1721">
<p data-start="1683" data-end="1721">只填写正确的 Host Name（如 <a class="decorated-link cursor-pointer" target="_new" rel="noopener" data-start="1702" data-end="1721">www.yourdomain.com）</a></p>
</li>
<li data-start="1722" data-end="1743">
<p data-start="1724" data-end="1743">不要使用 <code data-start="1729" data-end="1734">*:*</code> 或空的 Host</p>
</li>
</ul>
<p data-start="1745" data-end="1748">例如：</p>
<div class="_tableContainer_1rjym_1">
<div class="group _tableWrapper_1rjym_13 flex w-fit flex-col-reverse" tabindex="-1">
<table class="w-fit min-w-(--thread-content-width)" data-start="1750" data-end="1875">
<thead data-start="1750" data-end="1777">
<tr data-start="1750" data-end="1777">
<th data-start="1750" data-end="1757" data-col-size="sm">Type</th>
<th data-start="1757" data-end="1769" data-col-size="sm">Host Name</th>
<th data-start="1769" data-end="1777" data-col-size="sm">Port</th>
</tr>
</thead>
<tbody data-start="1806" data-end="1875">
<tr data-start="1806" data-end="1842">
<td data-start="1806" data-end="1814" data-col-size="sm">https</td>
<td data-col-size="sm" data-start="1814" data-end="1835"><a class="decorated-link cursor-pointer" target="_new" rel="noopener" data-start="1816" data-end="1834">www.yourdomain.com</a></td>
<td data-col-size="sm" data-start="1835" data-end="1842">443</td>
</tr>
<tr data-start="1843" data-end="1875">
<td data-start="1843" data-end="1851" data-col-size="sm">https</td>
<td data-col-size="sm" data-start="1851" data-end="1868">yourdomain.com</td>
<td data-col-size="sm" data-start="1868" data-end="1875">443</td>
</tr>
</tbody>
</table>
</div>
</div>
<p data-start="1877" data-end="1897">这样 IIS 只接受你允许的 Host。</p>
<hr data-start="1899" data-end="1902" />
<h1 data-start="1904" data-end="1945"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f6e1.png" alt="🛡" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 额外防护：阻止 X-Forwarded-Host 注入（若使用反向代理）</h1>
<p data-start="1947" data-end="2023">如果你在前面使用 <strong data-start="1956" data-end="1990">Nginx / Apache / Load Balancer</strong><br data-start="1990" data-end="1993" />请开启 Host Forwarding Strict 模式：</p>
<div class="contain-inline-size rounded-2xl relative bg-token-sidebar-surface-primary">
<div class="sticky top-9">
<div class="absolute end-0 bottom-0 flex h-9 items-center pe-2">
<div class="bg-token-bg-elevated-secondary text-token-text-secondary flex items-center gap-4 rounded-sm px-2 font-sans text-xs"></div>
</div>
</div>
<div class="overflow-y-auto p-4" dir="ltr"><code class="whitespace-pre! language-csharp">app.UseForwardedHeaders(<span class="hljs-keyword">new</span> ForwardedHeadersOptions<br />
{<br />
ForwardedHeaders = ForwardedHeaders.XForwardedHost,<br />
AllowedHosts = { <span class="hljs-string">"yourdomain.com"</span>, <span class="hljs-string">"www.yourdomain.com"</span> }<br />
});<br />
</code></div>
</div>
<hr data-start="2216" data-end="2219" />
<h1 data-start="2221" data-end="2233"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2714.png" alt="✔" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 完整修补步骤总结</h1>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>在麒麟KylinOS、统信UOS等Linux系统安装.Net SDK</title>
		<link>https://e673.com/install-dotnet-sdk-on-kylinos-uos-linux/</link>
		
		<dc:creator><![CDATA[ahe]]></dc:creator>
		<pubDate>Tue, 25 Nov 2025 12:32:22 +0000</pubDate>
				<category><![CDATA[信创环境]]></category>
		<category><![CDATA[.Net]]></category>
		<category><![CDATA[国产系统]]></category>
		<guid isPermaLink="false">https://e673.com/?p=3061</guid>

					<description><![CDATA[本文演示如何使用安装脚本或通过提&#46;&#46;&#46;]]></description>
										<content:encoded><![CDATA[<p>本文演示如何使用安装脚本或通过提取二进制文件，在 Linux 上安装 .NET SDK 或 .NET 运行时。 有关支持内置包管理器的发行版列表，请参阅<a href="https://learn.microsoft.com/zh-cn/dotnet/core/install/linux" data-linktype="relative-path">在 Linux 上安装 .NET</a>。</p>
<p>如果要开发 .NET 应用，请安装 SDK（包括运行时）。 或者，如果只需运行应用程序，请安装运行时。 如果要安装该运行时，建议安装 ASP.NET Core 运行时，因为它同时包括 .NET 和 ASP.NET Core 运行时。</p>
<p>使用 <code>dotnet --list-sdks</code> 和 <code>dotnet --list-runtimes</code> 命令查看安装的版本。 有关详细信息，请参阅<a href="https://learn.microsoft.com/zh-cn/dotnet/core/install/how-to-detect-installed-versions" data-linktype="relative-path">如何检查是否已安装 .NET</a>。</p>
<div class="heading-wrapper" data-heading-level="h2">
<h2 id="net-releases" class="heading-anchor">.NET 版本</h2>
</div>
<p>有两种类型的受支持版本：长期支持（LTS）和标准期限支持（STS）。 所有版本的质量都是一样的。 唯一的区别是支持的时间长短。 LTS 版本获得三年的免费支持和修补程序。 STS 版本可以获得两年的免费支持和修补程序。 有关详细信息，请参阅 <a href="https://dotnet.microsoft.com/platform/support/policy/dotnet-core" data-linktype="external">.NET 支持策略</a>。</p>
<p>下表列出了每个版本的 .NET（和 .NET Core）的支持状态：</p>
<div class="buttons buttons-right margin-bottom-none margin-top-sm"></div>
<div class="inner-focus">
<table class="table table-sm margin-top-none" aria-label=".NET 版本">
<thead>
<tr>
<th><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2714.png" alt="✔" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 受支持</th>
<th><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/274c.png" alt="❌" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 支持不足</th>
</tr>
</thead>
<tbody>
<tr>
<td>10 （LTS）</td>
<td>7</td>
</tr>
<tr>
<td>9 （STS）</td>
<td>6</td>
</tr>
<tr>
<td>8 （LTS）</td>
<td>5</td>
</tr>
<tr>
<td></td>
<td>3.1</td>
</tr>
<tr>
<td></td>
<td>3.0</td>
</tr>
<tr>
<td></td>
<td>2.2</td>
</tr>
<tr>
<td></td>
<td>2.1</td>
</tr>
<tr>
<td></td>
<td>2.0</td>
</tr>
<tr>
<td></td>
<td>1.1</td>
</tr>
<tr>
<td></td>
<td>1.0</td>
</tr>
</tbody>
</table>
</div>
<div class="heading-wrapper" data-heading-level="h2">
<h2 id="dependencies" class="heading-anchor">依赖项</h2>
</div>
<p>安装 .NET 时，可能不会安装某些特定的依赖项，特别是在 <a href="https://learn.microsoft.com/zh-cn/dotnet/core/install/linux-scripted-manual#manual-install" data-linktype="self-bookmark">手动安装</a>时。 以下列表详细介绍了Microsoft支持的 Linux 分发版，并具有可能需要安装的依赖项。 更多信息，请查看发行版页面：</p>
<ul>
<li><a href="https://learn.microsoft.com/zh-cn/dotnet/core/install/linux-alpine#dependencies" data-linktype="relative-path">高山</a></li>
<li><a href="https://learn.microsoft.com/zh-cn/dotnet/core/install/linux-debian#dependencies" data-linktype="relative-path">Debian</a></li>
<li><a href="https://learn.microsoft.com/zh-cn/dotnet/core/install/linux-fedora#dependencies" data-linktype="relative-path">Fedora</a></li>
<li><a href="https://learn.microsoft.com/zh-cn/dotnet/core/install/linux-rhel#dependencies" data-linktype="relative-path">RHEL 和 CentOS Stream</a></li>
<li><a href="https://learn.microsoft.com/zh-cn/dotnet/core/install/linux-sles#dependencies" data-linktype="relative-path">SLES</a></li>
<li><a href="https://learn.microsoft.com/zh-cn/dotnet/core/install/linux-ubuntu-decision#dependencies" data-linktype="relative-path">Ubuntu</a></li>
</ul>
<p>有关依赖项的一般信息，请参阅<a href="https://github.com/dotnet/core/blob/main/Documentation/self-contained-linux-apps.md" data-linktype="external">独立式 Linux 应用</a>。</p>
<div class="heading-wrapper" data-heading-level="h3">
<h3 id="rpm-dependencies" class="heading-anchor">RPM 依赖项</h3>
</div>
<p>如果以前未列出分发版且基于 RPM，则可能需要以下依赖项：</p>
<ul>
<li>glibc</li>
<li>libgcc</li>
<li>CA证书</li>
<li>openssl-libs</li>
<li>libstdc++</li>
<li>libicu</li>
<li>tzdata</li>
<li>krb5-libs</li>
</ul>
<div class="heading-wrapper" data-heading-level="h3">
<h3 id="deb-dependencies" class="heading-anchor">DEB 依赖项</h3>
</div>
<p>如果以前未列出分发版且基于 debian，则可能需要以下依赖项：</p>
<ul>
<li>libc6</li>
<li>libgcc1</li>
<li>libgssapi-krb5-2</li>
<li>libicu70</li>
<li>libssl3</li>
<li>libstdc++6</li>
<li>zlib1g</li>
</ul>
<div class="heading-wrapper" data-heading-level="h2">
<h2 id="scripted-install" class="heading-anchor">脚本安装</h2>
</div>
<p><a href="https://learn.microsoft.com/zh-cn/dotnet/core/tools/dotnet-install-script" data-linktype="relative-path">dotnet-install 脚本</a>用于 <strong>SDK</strong> 和<strong>运行时</strong>的自动化和非管理员安装。 可通过 <a href="https://dot.net/v1/dotnet-install.sh" data-linktype="external">https://dot.net/v1/dotnet-install.sh</a> 下载脚本。如果以这种方式安装 .NET，必须安装 Linux 发行版所需的依赖项。 使用<a href="https://learn.microsoft.com/zh-cn/dotnet/core/install/linux" data-linktype="relative-path">在 Linux 上安装 .NET</a>一文中的链接获得特定 Linux 分发版。</p>
<div class="alert is-primary">
<p class="alert-title"> 重要</p>
<p>需要 Bash 才能运行该脚本。</p>
</div>
<p>可通过 <code>wget</code> 下载脚本：</p>
<div id="code-try-0" class="code-block-header margin-top-sm" data-code-block-header="" data-bi-name="code-header"><span class="code-block-header-language">Bash</span><button class="button button-sm action inner-focus display-none-print" type="button" data-bi-name="copy" data-code-header-copy-button=""></button></div>
<p></p><pre class="crayon-plain-tag">wget https://dot.net/v1/dotnet-install.sh -O dotnet-install.sh</pre><p>或者，使用 <code>curl</code>：</p>
<div id="code-try-1" class="code-block-header margin-top-sm" data-code-block-header="" data-bi-name="code-header"><span class="code-block-header-language">Bash</span><button class="button button-sm action inner-focus display-none-print" type="button" data-bi-name="copy" data-code-header-copy-button=""></button></div>
<p></p><pre class="crayon-plain-tag">curl -L https://dot.net/v1/dotnet-install.sh -o dotnet-install.sh</pre><p>运行此脚本之前，请确保授予此脚本作为可执行文件运行的权限：</p>
<div id="code-try-2" class="code-block-header margin-top-sm" data-code-block-header="" data-bi-name="code-header"><span class="code-block-header-language">Bash</span><button class="button button-sm action inner-focus display-none-print" type="button" data-bi-name="copy" data-code-header-copy-button=""></button></div>
<p></p><pre class="crayon-plain-tag">chmod +x ./dotnet-install.sh</pre><p>该脚本默认安装最新的 <a href="https://dotnet.microsoft.com/platform/support/policy/dotnet-core" data-linktype="external">长期支持 （LTS）</a> SDK 版本，即 .NET 10。 若要安装最新版本（可能不是 (LTS) 版本），请使用 <code>--version latest</code> 参数。</p>
<div id="code-try-3" class="code-block-header margin-top-sm" data-code-block-header="" data-bi-name="code-header"><span class="code-block-header-language">Bash</span><button class="button button-sm action inner-focus display-none-print" type="button" data-bi-name="copy" data-code-header-copy-button=""></button></div>
<p></p><pre class="crayon-plain-tag">./dotnet-install.sh --version latest</pre><p>若要安装 .NET 运行时而非 SDK，请使用 <code>--runtime</code> 参数。</p>
<div id="code-try-4" class="code-block-header margin-top-sm" data-code-block-header="" data-bi-name="code-header"><span class="code-block-header-language">Bash</span><button class="button button-sm action inner-focus display-none-print" type="button" data-bi-name="copy" data-code-header-copy-button=""></button></div>
<p></p><pre class="crayon-plain-tag">./dotnet-install.sh --version latest --runtime aspnetcore</pre><p>可以通过 <code>--channel</code> 参数更改特定主要版本来指示特定版本。 以下命令安装 .NET 9.0 SDK。</p>
<div id="code-try-5" class="code-block-header margin-top-sm" data-code-block-header="" data-bi-name="code-header"><span class="code-block-header-language">Bash</span><button class="button button-sm action inner-focus display-none-print" type="button" data-bi-name="copy" data-code-header-copy-button=""></button></div>
<p></p><pre class="crayon-plain-tag">./dotnet-install.sh --channel 9.0</pre><p>有关详细信息，请参阅 <a href="https://learn.microsoft.com/zh-cn/dotnet/core/tools/dotnet-install-script" data-linktype="relative-path">dotnet-install 脚本参考</a>。</p>
<p>若要在命令行上启用 .NET，请参阅<a href="https://learn.microsoft.com/zh-cn/dotnet/core/install/linux-scripted-manual#set-environment-variables-system-wide" data-linktype="self-bookmark">设置系统范围的环境变量</a>。</p>
<p>若要了解如何使用 .NET CLI，请参阅 <a href="https://learn.microsoft.com/zh-cn/dotnet/core/tools/" data-linktype="relative-path">.NET CLI 概述</a>。</p>
<div class="heading-wrapper" data-heading-level="h2">
<h2 id="manual-install" class="heading-anchor">手动安装</h2>
</div>
<p>除了使用包管理器，还可以下载并手动安装 SDK 和运行时。 手动安装通常作为持续集成测试的一部分执行，或在不支持的 Linux 发行版上执行。 对于开发人员或用户，使用包管理器会更好。</p>
<p>从以下站点之一下载 SDK 或运行时的<strong>二进制</strong>版本。 .NET SDK 包括相应的运行时：</p>
<ul>
<li><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2714.png" alt="✔" class="wp-smiley" style="height: 1em; max-height: 1em;" /> <a href="https://dotnet.microsoft.com/download/dotnet/10.0" data-linktype="external">.NET 10 下载</a></li>
<li><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2714.png" alt="✔" class="wp-smiley" style="height: 1em; max-height: 1em;" /> <a href="https://dotnet.microsoft.com/download/dotnet/9.0" data-linktype="external">.NET 9 下载</a></li>
<li><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2714.png" alt="✔" class="wp-smiley" style="height: 1em; max-height: 1em;" /> <a href="https://dotnet.microsoft.com/download/dotnet/8.0" data-linktype="external">.NET 8 下载</a></li>
<li><a href="https://dotnet.microsoft.com/download/dotnet" data-linktype="external">所有 .NET Core 下载项</a></li>
</ul>
<p>提取已下载的文件并使用 <code>export</code> 命令将 <code>DOTNET_ROOT</code> 设置为提取文件夹的位置，然后确保 .NET 位于 PATH 中。 导出 <code>DOTNET_ROOT</code> 会使 .NET CLI 命令在终端中可用。 有关 .NET 环境变量的详细信息，请参阅 <a href="https://learn.microsoft.com/zh-cn/dotnet/core/tools/dotnet-environment-variables#net-sdk-and-cli-environment-variables" data-linktype="relative-path">.NET SDK 和 CLI 环境变量</a>。</p>
<p>可以将不同版本的 .NET 提取到同一文件夹，这些版本并存。</p>
<div class="heading-wrapper" data-heading-level="h3">
<h3 id="example" class="heading-anchor">示例</h3>
</div>
<p>以下命令使用 Bash 将环境变量 <code>DOTNET_ROOT</code> 设置为当前工作目录，后跟 <code>.dotnet</code>。 如果该目录不存在，则会创建它。 <code>DOTNET_FILE</code> 环境变量是要安装的 .NET 二进制版本的文件名。 此文件会提取到 <code>DOTNET_ROOT</code> 目录中。 <code>DOTNET_ROOT</code> 目录及其 <code>tools</code> 子目录都添加到 <code>PATH</code> 环境变量中。</p>
<div class="alert is-primary">
<p class="alert-title"> 重要</p>
<p>如果运行这些命令，请记住将 <code>DOTNET_FILE</code> 值更改为下载的 .NET 二进制文件的名称。</p>
</div>
<div id="code-try-6" class="code-block-header margin-top-sm" data-code-block-header="" data-bi-name="code-header"><span class="code-block-header-language">Bash</span><button class="button button-sm action inner-focus display-none-print" type="button" data-bi-name="copy" data-code-header-copy-button=""></button></div>
<p></p><pre class="crayon-plain-tag">DOTNET_FILE=dotnet-sdk-9.0.306-linux-x64.tar.gz
&lt;span class=&quot;hljs-built_in&quot;&gt;export&lt;/span&gt; DOTNET_ROOT=$(&lt;span class=&quot;hljs-built_in&quot;&gt;pwd&lt;/span&gt;)/.dotnet

&lt;span class=&quot;hljs-built_in&quot;&gt;mkdir&lt;/span&gt; -p &lt;span class=&quot;hljs-string&quot;&gt;&quot;&lt;span class=&quot;hljs-variable&quot;&gt;$DOTNET_ROOT&lt;/span&gt;&quot;&lt;/span&gt; &amp;amp;&amp;amp; tar zxf &lt;span class=&quot;hljs-string&quot;&gt;&quot;&lt;span class=&quot;hljs-variable&quot;&gt;$DOTNET_FILE&lt;/span&gt;&quot;&lt;/span&gt; -C &lt;span class=&quot;hljs-string&quot;&gt;&quot;&lt;span class=&quot;hljs-variable&quot;&gt;$DOTNET_ROOT&lt;/span&gt;&quot;&lt;/span&gt;

&lt;span class=&quot;hljs-built_in&quot;&gt;export&lt;/span&gt; PATH=&lt;span class=&quot;hljs-variable&quot;&gt;$PATH&lt;/span&gt;:&lt;span class=&quot;hljs-variable&quot;&gt;$DOTNET_ROOT&lt;/span&gt;:&lt;span class=&quot;hljs-variable&quot;&gt;$DOTNET_ROOT&lt;/span&gt;/tools</pre><p>可以在同一文件夹中安装多个版本的 .NET。</p>
<p>还可以将 .NET 安装到由 <code>HOME</code> 变量或 <code>~</code> 路径标识的主目录：</p>
<div id="code-try-7" class="code-block-header margin-top-sm" data-code-block-header="" data-bi-name="code-header"><span class="code-block-header-language">Bash</span><button class="button button-sm action inner-focus display-none-print" type="button" data-bi-name="copy" data-code-header-copy-button=""></button></div>
<p></p><pre class="crayon-plain-tag">&lt;span class=&quot;hljs-built_in&quot;&gt;export&lt;/span&gt; DOTNET_ROOT=&lt;span class=&quot;hljs-variable&quot;&gt;$HOME&lt;/span&gt;/.dotnet</pre><p>若要了解如何使用 .NET CLI，请参阅 <a href="https://learn.microsoft.com/zh-cn/dotnet/core/tools/" data-linktype="relative-path">.NET CLI 概述</a>。</p>
<div class="heading-wrapper" data-heading-level="h2">
<h2 id="verify-downloaded-binaries" class="heading-anchor">验证下载的二进制文件</h2>
</div>
<p>下载安装程序或二进制版本后，请对其进行验证，以确保文件未被更改或损坏。 可以验证计算机上的校验和，然后将其与下载网站上报告的内容进行比较。</p>
<p>从官方下载页下载文件时，文件的校验和会显示在文本框中。 选择“<strong>复制</strong>”按钮将校验和值复制到剪贴板。</p>
<p><span class="mx-imgBorder"><img loading="lazy" decoding="async" class="alignnone size-full wp-image-3062" src="https://e673.com/wp-content/uploads/2025/11/checksum.png" width="900" height="381" alt="具有校验和的 .NET 下载页" srcset="https://e673.com/wp-content/uploads/2025/11/checksum.png 900w, https://e673.com/wp-content/uploads/2025/11/checksum-500x212.png 500w, https://e673.com/wp-content/uploads/2025/11/checksum-768x325.png 768w, https://e673.com/wp-content/uploads/2025/11/checksum-670x284.png 670w" sizes="(max-width: 900px) 100vw, 900px" /></span></p>
<p>使用 <code>sha512sum</code> 命令打印已下载的文件的校验和。 例如，以下命令显示 <em>dotnet-sdk-9.0.306-linux-x64.tar.gz</em> 文件的校验和：</p>
<div id="code-try-8" class="code-block-header margin-top-sm" data-code-block-header="" data-bi-name="code-header"><span class="code-block-header-language">Bash</span><button class="button button-sm action inner-focus display-none-print" type="button" data-bi-name="copy" data-code-header-copy-button=""></button></div>
<p></p><pre class="crayon-plain-tag">$ sha512sum dotnet-sdk-9.0.306-linux-x64.tar.gz
bbb6bdc3c8048e7cc189759b406257839e7d4bd6b8b1ba4bcdaeea8f92340e6855231043dd73f902130ca5357af72b810bb51a4da4d1315a2927ff85f831f1d5  dotnet-sdk-9.0.306-linux-x64.tar.gz</pre><p>将校验和与下载站点提供的值进行比较。</p>
<div class="heading-wrapper" data-heading-level="h3">
<h3 id="use-a-checksum-file-to-validate" class="heading-anchor">使用校验和文件进行验证</h3>
</div>
<p>.NET 发行说明包含校验和文件的链接，可用于验证下载的文件。 以下步骤介绍了如何下载校验和文件并验证 .NET 安装二进制文件：</p>
<ol>
<li>GitHub <a href="https://github.com/dotnet/core/tree/main/release-notes/9.0#releases" data-linktype="external">https://github.com/dotnet/core/tree/main/release-notes/9.0#releases</a> 上 .NET 9 的发行说明页包含名为 <strong>Releases</strong> 的部分。 该部分的表格链接到每个 .NET 9 版本的下载和校验和文件。 下图显示了 .NET 8 发布表作为参考：
<p><span class="mx-imgBorder"><img loading="lazy" decoding="async" class="alignnone size-full wp-image-3063" src="https://e673.com/wp-content/uploads/2025/11/release-notes-root.png" width="258" height="278" alt=".NET 的 github 发行说明版本表" /></span></li>
<li>选择下载的 .NET 版本的链接。
<p>上一部分使用了 .NET SDK 9.0.306，该版本位于 .NET 9.0.10 版本中。</li>
<li>在发布页面中，可以看到 .NET 运行时和 .NET SDK 版本，以及校验和文件的链接。 下图显示了 .NET 8 发布表作为参考：
<p><span class="mx-imgBorder"><img loading="lazy" decoding="async" class="alignnone size-full wp-image-3064" src="https://e673.com/wp-content/uploads/2025/11/release-notes-version.png" width="1055" height="487" alt="具有 .NET 校验和的下载表" srcset="https://e673.com/wp-content/uploads/2025/11/release-notes-version.png 1055w, https://e673.com/wp-content/uploads/2025/11/release-notes-version-500x231.png 500w, https://e673.com/wp-content/uploads/2025/11/release-notes-version-1024x473.png 1024w, https://e673.com/wp-content/uploads/2025/11/release-notes-version-768x355.png 768w, https://e673.com/wp-content/uploads/2025/11/release-notes-version-670x309.png 670w" sizes="(max-width: 1055px) 100vw, 1055px" /></span></li>
<li>右键单击 <strong>校验和</strong> 链接并将其复制到剪贴板。</li>
<li>打开终端。</li>
<li>使用 <code>curl -O {link}</code> 下载校验和文件。
<p>用复制的链接替换以下命令中的链接。</p>
<div id="code-try-9" class="code-block-header margin-top-sm" data-code-block-header="" data-bi-name="code-header"><span class="code-block-header-language">Bash</span><button class="button button-sm action inner-focus display-none-print" type="button" data-bi-name="copy" data-code-header-copy-button=""></button></div>
<p></p><pre class="crayon-plain-tag">curl -O https://builds.dotnet.microsoft.com/dotnet/checksums/9.0.10-sha.txt</pre><p>
</li>
<li>将校验和文件和 .NET 版本文件下载到同一目录后，请使用 <code>sha512sum -c {file} --ignore-missing</code> 命令来验证下载的文件。
<p>如果验证通过，会看到打印了 <strong>OK</strong> 状态的文件：</p>
<div id="code-try-10" class="code-block-header margin-top-sm" data-code-block-header="" data-bi-name="code-header"><span class="code-block-header-language">Bash</span><button class="button button-sm action inner-focus display-none-print" type="button" data-bi-name="copy" data-code-header-copy-button=""></button></div>
<p></p><pre class="crayon-plain-tag">$ sha512sum -c 9.0.10-sha.txt --ignore-missing
dotnet-sdk-9.0.306-linux-x64.tar.gz: OK</pre><p>如果看到文件标记为<strong>失败</strong>，则下载的文件无效，不应使用。</p>
<div id="code-try-11" class="code-block-header margin-top-sm" data-code-block-header="" data-bi-name="code-header"><span class="code-block-header-language">Bash</span><button class="button button-sm action inner-focus display-none-print" type="button" data-bi-name="copy" data-code-header-copy-button=""></button></div>
<p></p><pre class="crayon-plain-tag">$ sha512sum -c 9.0.10-sha.txt --ignore-missing
dotnet-sdk-9.0.306-linux-x64.tar.gz: FAILED
sha512sum: WARNING: 1 computed checksum did NOT match
sha512sum: 9.0.10-sha.txt: no file was verified</pre><p>
</li>
</ol>
<div class="heading-wrapper" data-heading-level="h2">
<h2 id="set-environment-variables-system-wide" class="heading-anchor">设置系统范围的环境变量</h2>
</div>
<p>如果使用了以前的安装脚本，则设置的变量仅适用于当前的终端会话。 将其添加到 shell 配置文件。 Linux 提供了许多不同的 shell，每个都有不同的配置文件。 例如：</p>
<ul>
<li><strong>Bash Shell</strong>: <em>~/.bash_profile</em> 或 <em>~/.bashrc</em></li>
<li><strong>Korn Shell</strong>：~/.kshrc 或 .profile</li>
<li><strong>Z Shell</strong>：~/.zshrc 或 .zprofile</li>
</ul>
<p>在 shell 配置文件中设置以下两个环境变量：</p>
<ul>
<li><code>DOTNET_ROOT</code>
<p>此变量设置为 .NET 安装到的文件夹，如 <code>$HOME/.dotnet</code>：</p>
<div id="code-try-12" class="code-block-header margin-top-sm" data-code-block-header="" data-bi-name="code-header"><span class="code-block-header-language">Bash</span><button class="button button-sm action inner-focus display-none-print" type="button" data-bi-name="copy" data-code-header-copy-button=""></button></div>
<p></p><pre class="crayon-plain-tag">&lt;span class=&quot;hljs-built_in&quot;&gt;export&lt;/span&gt; DOTNET_ROOT=&lt;span class=&quot;hljs-variable&quot;&gt;$HOME&lt;/span&gt;/.dotnet</pre><p>
</li>
<li><code>PATH</code>
<p>此变量应同时包含 <code>DOTNET_ROOT</code> 文件夹和 <code>DOTNET_ROOT/tools</code> 文件夹：</p>
<div id="code-try-13" class="code-block-header margin-top-sm" data-code-block-header="" data-bi-name="code-header"><span class="code-block-header-language">Bash</span><button class="button button-sm action inner-focus display-none-print" type="button" data-bi-name="copy" data-code-header-copy-button=""></button></div>
<p><pre class="crayon-plain-tag">&lt;span class=&quot;hljs-built_in&quot;&gt;export&lt;/span&gt; PATH=&lt;span class=&quot;hljs-variable&quot;&gt;$PATH&lt;/span&gt;:&lt;span class=&quot;hljs-variable&quot;&gt;$DOTNET_ROOT&lt;/span&gt;:&lt;span class=&quot;hljs-variable&quot;&gt;$DOTNET_ROOT&lt;/span&gt;/tools</pre>
</li>
</ul>
<div class="heading-wrapper" data-heading-level="h2">
<h2 id="next-steps" class="heading-anchor">后续步骤</h2>
</div>
]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>
