ESP32 ADC引脚深度解析:从硬件原理到软件优化的物联网数据采集实战
1. 项目概述ESP32 ADC引脚深度解析在嵌入式开发领域ESP32以其强大的双核处理能力和丰富的无线连接功能成为了物联网项目的明星芯片。然而很多开发者尤其是从Arduino Uno这类简单MCU转型过来的朋友在初次接触ESP32的模拟功能时往往会感到困惑——它的ADC模数转换器引脚怎么用精度如何为什么读出来的数值跳得厉害今天我就结合自己多年在物联网设备数据采集上的实战经验来一次彻底的ESP32 ADC引脚“拆机”式讲解。这不仅仅是引脚定义的罗列更是从硬件原理到软件优化从基础读取到高精度应用的全方位剖析。无论你是想用ESP32做个环境传感器监测温湿度还是想采集电池电压做电量管理理解ADC引脚的正确用法都是绕不开的关键一步。2. ESP32 ADC硬件架构与引脚映射2.1 ADC模块核心架构解析ESP32内部集成了两个独立的ADC模块ADC1和ADC2。这不是简单的复制而是有明确分工的。ADC1和ADC2均为12位逐次逼近型SARADC理论上可以提供0到4095的原始读数。但它们的“命运”截然不同。ADC1的通道相对独立而ADC2的通道则与ESP32的Wi-Fi射频模块共享部分模拟电路。这个设计直接导致了一个关键限制当Wi-Fi功能启动时ADC2无法使用。这是因为Wi-Fi工作时会产生高频噪声严重干扰ADC2的采样精度。因此在项目规划初期就必须明确如果需要同时使用Wi-Fi和ADC那么所有模拟输入都必须规划在ADC1的可用引脚上。2.2 具体引脚功能定义与选型指南不同封装的ESP32如ESP32-D0WDQ6其引脚数可能不同但ADC引脚的命名规则是统一的。我们以常见的38-pin ESP32开发板为例ADC1 (8个通道):GPIO32- ADC1_CH4GPIO33- ADC1_CH5GPIO34- ADC1_CH6GPIO35- ADC1_CH7GPIO36- ADC1_CH0 (VP)GPIO37- ADC1_CH1 (VN)注意GPIO37和GPIO38通常不引出到开发板引脚GPIO38- ADC1_CH2GPIO39- ADC1_CH3 (VN)ADC2 (10个通道):GPIO4- ADC2_CH0GPIO0- ADC2_CH1注意GPIO0常为Boot模式选择引脚GPIO2- ADC2_CH2注意GPIO2常接板载LEDGPIO15- ADC2_CH3GPIO13- ADC2_CH4GPIO12- ADC2_CH5注意上电时的电平会影响Flash电压GPIO14- ADC2_CH6GPIO27- ADC2_CH7GPIO25- ADC2_CH8GPIO26- ADC2_CH9重要提示GPIO34、GPIO35、GPIO36、GPIO39仅能作为输入引脚它们内部没有上拉或下拉电阻。这意味着你不能将它们配置为输出模式去驱动LED或继电器。它们生来就是为“聆听”输入模拟信号而存在的。引脚选型实战心得优先使用ADC1的引脚特别是GPIO32、GPIO33、GPIO34、GPIO35。它们是最“清净”、最可靠的ADC输入引脚。谨慎使用ADC2引脚除非你的项目确定不需要Wi-Fi或者可以在采样时短暂关闭Wi-Fi否则应避免使用。我曾在一个需要Wi-Fi定时上报数据的传感器项目中误用了GPIO2作为ADC输入结果采样值完全不可信排查了很久才发现是Wi-Fi的干扰。避开多功能引脚像GPIO0、GPIO2、GPIO12、GPIO15等除了ADC功能外还关联着启动、Flash、LED等关键电路。在这些引脚上接模拟传感器可能会在上电、下载程序时引发意外行为增加调试复杂度。3. ADC性能深潜精度、衰减与噪声应对3.1 理想与现实被“封印”的12位精度ESP32的ADC标称12位但如果你直接用analogRead()读取会发现数值跳动很大有效位数远达不到12位。这不是代码问题而是硬件特性。ESP32的ADC参考电压Vref并非来自一个精准的内部基准源而是直接取自电源电压VDD。通常VDD是3.3V但USB供电、LDO稳压芯片的波动都会导致Vref变化从而影响ADC读数的绝对精度。更关键的是ESP32的ADC输入电压范围默认是0-1V在衰减为0dB时。对于常见的3.3V传感器输出直接接入会烧毁ADC引脚。因此ESP32引入了“衰减器Attenuation”的概念。3.2 衰减器配置与量程选择衰减器本质是一个分压网络用于将更高的输入电压降低到ADC可安全测量的范围内。在Arduino ESP32核心库或ESP-IDF中可以为每个ADC通道配置衰减。// Arduino ESP32 环境中设置衰减 #include driver/adc.h void setup() { adc1_config_width(ADC_WIDTH_BIT_12); // 设置ADC1为12位宽度 adc1_config_channel_atten(ADC1_CHANNEL_6, ADC_ATTEN_DB_11); // 为GPIO34 (ADC1_CH6) 配置11dB衰减 } void loop() { int rawValue adc1_get_raw(ADC1_CHANNEL_6); // ... 后续处理 }常见的衰减设置与量程对应关系如下衰减枚举值近似衰减倍数推荐输入电压范围说明ADC_ATTEN_DB_01x0 ~ 1.1V默认设置量程最小噪声相对小。ADC_ATTEN_DB_2_5~1.33x0 ~ 1.5V较少使用。ADC_ATTEN_DB_62x0 ~ 2.2V适用于很多输出为0-2V的传感器。ADC_ATTEN_DB_11~3.55x0 ~ 3.3V最常用匹配3.3V系统可测量0-3.3V信号。配置经验如果你的传感器输出在0-3.3V如电位器分压、某些模拟温度传感器务必设置为ADC_ATTEN_DB_11。设置衰减后analogRead()返回的原始值0-4095对应的满量程电压就是上表中的“推荐输入电压范围”最大值。例如在11dB衰减下读数4095对应约3.3V。3.3 噪声来源与软件滤波实战即使配置正确ADC读数依然会有跳动。噪声主要来自电源噪声尤其是使用开关电源或USB供电时。数字电路噪声CPU、Wi-Fi/蓝牙射频的快速开关电流。引线引入的噪声长导线像天线一样接收环境干扰。软件滤波是必选项而非可选项。以下是我最常用的几种方法1. 移动平均滤波最简单有效。连续采样N次求平均值。#define ADC_SAMPLE_NUM 64 // 采样次数可取32, 64, 128等2的幂次方便于计算 int readADC_Smoothed(adc1_channel_t channel) { long sum 0; for (int i 0; i ADC_SAMPLE_NUM; i) { sum adc1_get_raw(channel); } return (int)(sum / ADC_SAMPLE_NUM); }2. 中位值平均滤波防脉冲干扰平均滤波法结合中值滤波和平均滤波的优点能有效滤除偶然的突发性脉冲干扰。步骤连续采样N个数据去掉一个最大值和一个最小值然后计算剩余N-2个数据的算术平均值。优点既能抑制随机噪声又能防止突发性脉冲干扰。3. 卡尔曼滤波适用于需要动态跟踪信号变化的场景。虽然算法复杂但在ESP32上运行绰绰有余。网上有大量轻量级的单变量卡尔曼滤波器Arduino库可供使用。实测技巧对于缓慢变化的信号如温度、光照在loop()中每次循环做一次移动平均即可。对于变化较快的信号可以在一个紧循环内快速完成多次采样。采样时短暂关闭Wi-Fi如果使用ADC2则必须关闭使用ADC1也建议关闭能显著提升精度可以在采样前后调用WiFi.mode(WIFI_OFF)和WiFi.mode(WIFI_STA)但要注意网络重连的耗时。4. 从原始值到实际电压校准与换算4.1 线性度问题与分段校准ESP32的ADC非线性问题在高低电压两端尤为明显。这意味着即使你知道满量程电压是3.3V直接用电压 (原始值 / 4095) * 3.3公式计算得到的电压误差也可能很大几十到上百毫伏。更准确的方法是分段校准或使用查找表。最实用的方法是测量两个已知精确电压点对应的ADC原始值然后进行两点线性拟合。实操步骤准备一个可调精密电源或两个已知的、稳定的参考电压点例如使用一个精准的1.25V基准源和3.00V稳压输出。将电压分别接到ADC引脚在11dB衰减下读取稳定的原始值raw1和raw2。使用以下公式推导出换算系数电压 (原始值 - raw1) * (V2 - V1) / (raw2 - raw1) V1将V1,V2,raw1,raw2作为常量存入代码。4.2 利用eFuse进行出厂校准进阶部分ESP32芯片在出厂时会将每个ADC在特定衰减下的测量误差以差分形式写入eFuse存储区。ESP-IDF提供了API来读取这些校准值并自动补偿。在Arduino环境中可以通过包含esp_adc_cal.h库来使用这个高级功能。#include esp_adc_cal.h esp_adc_cal_characteristics_t adc_chars; // 检查eFuse中是否有校准值并初始化特征结构体 esp_adc_cal_characterize(ADC_UNIT_1, ADC_ATTEN_DB_11, ADC_WIDTH_BIT_12, 1100, adc_chars); // 1100mV是默认Vref uint32_t readADC_Calibrated(adc1_channel_t channel) { uint32_t raw adc1_get_raw(channel); // 将原始值转换为校准后的电压值单位毫伏 uint32_t voltage_mV esp_adc_cal_raw_to_voltage(raw, adc_chars); return voltage_mV; }使用这个函数得到的电压值精度远高于原始计算。强烈建议在要求精度的项目中使用此方法。5. 外围电路设计与实战注意事项5.1 输入保护与RC滤波电路直接将传感器输出接到ESP32的ADC引脚是危险的。一个稳健的设计必须包含保护电路。推荐电路如下传感器输出 —— [1kΩ电阻] ——||—— [ADC引脚] | | [100nF陶瓷电容] | | GND GND1kΩ电阻限流电阻防止意外高压或引脚配置错误时产生大电流。100nF电容对地滤波电容构成一个低通滤波器截止频率约1.6kHz能有效滤除高频噪声。电容应尽可能靠近ESP32的ADC引脚放置。TVS二极管如果测量环境存在静电或浪涌风险如从室外引线应在ADC引脚和GND之间并联一个3.6V左右的TVS二极管如SMAJ3.3A进行钳位保护。5.2 降低噪声的硬件布局技巧电源去耦在ESP32的VDD引脚附近放置一个10uF的电解电容和一个100nF的陶瓷电容这是稳定ADC参考电压的基石。模拟地与数字地虽然ESP32没有独立的模拟地引脚但在PCB布局时应将ADC输入回路的地线尽量走线宽而短并单点连接到主电源滤波电容的地端形成“星型接地”避免数字电流在模拟地路径上产生压降。远离噪声源ADC输入走线应远离晶振、高频数字信号线如SDIO、SPI CLK以及电源电感。5.3 典型应用场景接线示例场景一电位器控制电位器10kΩ中间引脚 —— [1kΩ电阻] —— GPIO34 电位器另外两脚分别接 3.3V 和 GND。 GPIO34对GND接100nF电容。场景二光敏电阻与固定电阻分压3.3V —— [光敏电阻] —— ADC节点 —— [10kΩ固定电阻] —— GND ADC节点 —— [1kΩ电阻] —— GPIO35 GPIO35对GND接100nF电容。注意分压电阻的阻值会影响响应速度和功耗需要根据具体传感器特性调整。6. 常见问题排查与调试实录即使按照最佳实践设计调试ADC时仍会遇到各种问题。下面是我总结的“故障树”和解决方案。现象可能原因排查步骤与解决方案读数始终为01. 引脚配置错误如配置为输出模式。2. 输入电压确实为0V。3. 使用了仅输入的GPIO34,35,36,39但试图在代码中设置上拉。1. 检查代码确保引脚已初始化为输入模式Arduino中pinMode(pin, INPUT)。2. 用万用表测量引脚实际电压。3. 对于仅输入引脚移除任何pinMode(pin, INPUT_PULLUP)的尝试。读数始终接近4095满量程1. 输入电压超过量程如未配置衰减输入1.1V。2. 引脚浮空未接任何信号。3. 内部上拉被意外启用仅对非仅输入引脚。1. 确认ADC衰减配置是否正确应为ADC_ATTEN_DB_11测3.3V。2. 确保信号源已正确连接且共地。3. 检查代码是否错误地使能了内部上拉电阻。读数跳动剧烈噪声大1. 未使用滤波电容。2. 电源噪声大。3. Wi-Fi正在工作特别是影响ADC2。4. 传感器信号本身不稳定。1. 在ADC引脚就近添加100nF对地电容。2. 使用线性稳压电源如LM1117代替开关电源测试。3. 在ADC采样期间短暂关闭Wi-Fi或换用ADC1引脚。4. 增加软件滤波如移动平均的采样点数。读数非线性低/高电压端误差大ADC固有的非线性特性。1. 接受其精度限制仅用于阈值判断。2. 实施两点校准法。3. 使用esp_adc_cal库的校准功能。不同引脚读数存在固定偏差不同ADC通道的偏移误差不一致。对每个使用的通道进行独立的校准并存储各自的校准参数。ADC读数导致Wi-Fi断开或系统不稳定可能在使用ADC2时采样操作与Wi-Fi底层驱动冲突。绝对避免在开启Wi-Fi后使用ADC2。将所有模拟输入迁移到ADC1的引脚上。调试心法万用表是你的第一双眼睛任何时候对读数有疑问先用万用表测量ADC引脚上的实际电压与代码换算的电压对比。这是区分硬件问题还是软件问题的关键。简化系统如果问题复杂创建一个最简化的测试程序只做ADC读取和串口打印排除其他代码如网络服务、复杂算法的干扰。观察电源波形如果条件允许用示波器观察ADC引脚和3.3V电源的波形可以直观看到噪声情况。7. 超越基础高精度采集与外部ADC当你需要高于ESP32内置ADC的精度如16位或更稳定的性能时就需要引入外部ADC芯片。这是工业级数据采集的常见做法。为何需要外部ADC更高的分辨率16位、24位ADC可将测量范围细分得更多能分辨更微小的电压变化。更好的线性度和积分非线性INL/DNL专用ADC芯片性能远优于微控制器内置ADC。更低的噪声独立的模拟电源和基准源。差分输入可以测量两个输入端的电压差有效抑制共模噪声适合测量桥式传感器如电子秤的称重传感器。常见选型与连接ADS111516位4通道I2C接口带可编程增益放大器PGA。这是最受欢迎的选择之一价格适中性能优异Arduino库成熟。// 使用Adafruit_ADS1X15库的示例片段 #include Adafruit_ADS1X15.h Adafruit_ADS1115 ads; // 使用ADS1115 void setup() { ads.begin(); // 默认I2C地址 0x48 ads.setGain(GAIN_ONE); // 设置增益GAIN_ONE对应±4.096V量程 } void loop() { int16_t adcValue ads.readADC_SingleEnded(0); // 读取A0引脚 float voltage ads.computeVolts(adcValue); // 转换为电压 }MCP342118位单通道I2C接口性价比高。ADS125624位8通道SPI接口超高精度用于专业测量仪器。使用外部ADC后ESP32的ADC引脚角色转变它们可以解放出来用于数字功能或者作为备份。整个系统的模拟信号调理滤波、放大和供电稳定性要求会转移到外部ADC模块上ESP32只需通过I2C/SPI读取稳定的数字结果即可极大地提升了系统的整体测量性能。通过从硬件原理到软件优化从内部模块到外部扩展的层层拆解相信你已经对ESP32的ADC引脚有了立体而深入的理解。ADC是连接物理世界与数字世界的桥梁它的稳定性直接决定了物联网设备感知环境的可靠性。多动手实测多观察波形善用滤波与校准你就能让ESP32的ADC发挥出它应有的价值为你的项目奠定坚实的数据基础。

相关新闻

JsonPath 核心语法与实战:JSON 数据精准查询与提取指南

JsonPath 核心语法与实战:JSON 数据精准查询与提取指南

1. 从“找东西”说起:为什么需要JsonPath?你有没有过这样的经历?面对一个从API接口返回的、嵌套了七八层的JSON数据,你只想拿到最里面某个数组的第三个元素的id字段。于是你开始写一长串的data.result.items[2].id,小心…

2026/9/26 7:41:31 阅读更多 →
Win/Mac 通用部署思路,OpenClaw 跨平台搭建教程

Win/Mac 通用部署思路,OpenClaw 跨平台搭建教程

核心亮点:提供全程可视化的图形操作界面,自动补齐全套运行依赖,数据独立存储于本地设备,兼容多款主流大模型,并采用轻量化的 45.7MB 整合压缩包。 教程适配:OpenClaw | 适配 Windows 10/11 与 macOS 双系统…

2026/9/28 15:04:01 阅读更多 →
Cron表达式详解:从核心语法到时区陷阱与实战场景

Cron表达式详解:从核心语法到时区陷阱与实战场景

1. 从一次线上故障说起:被误解的“每天凌晨执行”去年,我们团队的一个核心数据同步服务出了个不大不小的故障。这个服务负责在每天凌晨将前一天的订单数据同步到分析库,用的是最经典的0 0 * * *这个 cron 表达式。开发同学拍着胸脯说&#xf…

2026/10/2 7:23:45 阅读更多 →

最新新闻

二叉树:从递归思维到实战应用,一篇讲透

二叉树:从递归思维到实战应用,一篇讲透

二叉树(Binary Tree)是树形结构中最基础、最重要的一种。每个节点最多有两个子节点,分别称为 左孩子 和 右孩子。二叉树不仅是数据结构课程的核心内容,也是很多高级结构(如堆、红黑树、B 树、线段树)的基础…

2026/10/4 16:36:53 阅读更多 →
一文讲透|2026年专业AI论文网站接入TaoToken的配置与验证

一文讲透|2026年专业AI论文网站接入TaoToken的配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 16:36:53 阅读更多 →
Fast Note Sync Service 离线同步实战:断网编辑自动合并,笔记不再冲突覆盖

Fast Note Sync Service 离线同步实战:断网编辑自动合并,笔记不再冲突覆盖

Fast Note Sync Service 离线同步实战:断网编辑自动合并,笔记不再冲突覆盖 【免费下载链接】fast-note-sync-service High-performance, low-latency note synchronization, online management, and remote REST API service platform. 项目地址: http…

2026/10/4 16:36:53 阅读更多 →
基于原始套接字的Tracert程序设计:ICMP协议与VC6.0实战

基于原始套接字的Tracert程序设计:ICMP协议与VC6.0实战

简介:这份Tracert程序设计报告面向计算机网络课程学习者与网络编程初学者,围绕原始套接字编程与ICMP协议展开,帮助读者理解路由跟踪的底层原理并完成课程设计或实验报告撰写。报告内容涵盖设计目的与要求、系统详细设计、程序流程与主要函数说…

2026/10/4 16:36:53 阅读更多 →
MCU晶振电路设计实战:选型、匹配电容计算、PCB布局与Debug排查全指南

MCU晶振电路设计实战:选型、匹配电容计算、PCB布局与Debug排查全指南

画PCB时最怕什么?我排第二的答案,是晶振电路不起振。排第一的是板子拿回来不工作,结果发现是晶振问题。这个看似只有两个电容加一个晶体的电路,几乎每个硬件工程师都会画,但真正出问题时,Debug起来却能把人…

2026/10/4 16:36:53 阅读更多 →
软件测试面试高频考点与备考思路:从理论基础到项目实战

软件测试面试高频考点与备考思路:从理论基础到项目实战

1. 应届生软件测试面试,到底在考什么每年秋招春招,总有不少应届生被软件测试面试题打得措手不及。有人觉得测试岗门槛低,随便背背题就能过,结果一开口就露馅——面试官随便抛出一个“给你一个登录框,你怎么设计测试用例…

2026/10/4 16:35:52 阅读更多 →

日新闻

KT148A语音芯片外挂8002D功放的工程实践指南

KT148A语音芯片外挂8002D功放的工程实践指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 1:00:58 阅读更多 →
LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 1:00:58 阅读更多 →
ARM架构深度解析:从RISC设计理念到交叉编译实战

ARM架构深度解析:从RISC设计理念到交叉编译实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 1:00:58 阅读更多 →

周新闻

KT148A语音芯片外挂8002D功放的工程实践指南

KT148A语音芯片外挂8002D功放的工程实践指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 1:00:58 阅读更多 →
LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 1:00:58 阅读更多 →
ARM架构深度解析:从RISC设计理念到交叉编译实战

ARM架构深度解析:从RISC设计理念到交叉编译实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 1:00:58 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 11:40:45 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 9:43:54 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/3 9:42:36 阅读更多 →