ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

树莓派 Pico ADC 探秘:从寄存器到 FIFO、DMA 与过采样

树莓派 Pico ADC 探秘:从寄存器到 FIFO、DMA 与过采样 刚拿到树莓派 Pico 那会儿多数人第一次用 ADC 的场景都是这样接一个电位器掏出 MicroPython 写两行ADC(26).read_u16()屏幕上滚出一个 0 到 65535 的数开心半天。等真上了项目、接了传感器、要把数据往显示屏或者上位机送的时候才发现Pico 的 ADC 远比想象中“有脾气”。我后来用寄存器方式把 RP2040 的 ADC 全部重读了一遍从单次采样到 FIFO、DMA、过采样一路捋下来很多之前模模糊糊的坑才算真正想通。这篇文章就把 Pico 这颗 RP2040 的 ADC 从硬件架构、寄存器工作模式到实际应用避坑完整拆一遍。适合正准备用 Pico 做数据采集、电池监测、传感器接入的开发者也适合那些已经用 SDK 里adc_read()封装读得很顺手、但想知道底层到底发生了什么的同学。读完你不仅能写出不依赖函数的寄存器级操作还能少走几条我踩过的弯路。1. 先看硬件RP2040 的 ADC 到底能测什么1.1 板子上能用到的输入通道RP2040 内部实际有 5 路 ADC 输入被叫做 ADC0 到 ADC4其中前面 4 路通过 GPIO 引出到芯片外部第 5 路接的是芯片内部温度传感器。这个布局在树莓派 Pico 开发板上体现得很直接我先把对应关系整理成一张表。ADC 通道对应引脚Pico 板载标注典型用途注意事项ADC0GPIO26ADC0电位器、传感器模拟输出、分压采样板子左边第一根模拟脚ADC1GPIO27ADC1同上第二根模拟脚ADC2GPIO28ADC2同上第三根模拟脚ADC3GPIO29VSYS / ADC3Pico 板上接 VSYS 分压网络外接信号前务必确认分压网络影响ADC4无外部引脚内部温度传感器监控芯片温度精度一般需要校准很多新手第一次看 Pico 引脚图都会忽略 GPIO29因为它的标注是 VSYS / ADC3。Pico 开发板出厂时GPIO29 已经被板上电阻分压网络接到了 VSYS也就是 USB 供电电压的检测点。如果你想用 GPIO29 去采外部信号得先理解这个引脚现在连着的分压电阻会干扰你的测量通常做法是直接放弃它或者把板子背面的电阻网络断开以后再外接。从实际可用角度来说Pico 默认能随插随用的模拟输入通道通常就 ADC0、ADC1、ADC2 这三路。做两三路以内的模拟采集完全够用再多的话就要考虑外扩 ADC 或者用外部多路复用器了。1.2 内部温度传感器和它的偏差那个 ADC4 温度传感器很多人以为能直接当温度计用实际上没那么美好。RP2040 数据手册上给了典型参数在 27°C 左右传感器输出电压约在 0.706V 附近温度系数大约为 -1.8mV/°C注意是负系数温度越高电压越低。换算公式可以写成[ T(\°C) \approx 27 - \frac{V_{measured} - 0.706}{0.0018} ]问题在于这个 0.706V 和 -1.8mV/°C 都是“典型值”每一颗芯片之间都有个体差异而且芯片自发热会让传感器读数偏高。我实测过同一批板子同样室温下有的换算出来 28°C有的能到 33°C。所以这个传感器适合做温升判断、过热保护这类不需要精确绝对温度的场合不适合当精密温度计。真要测环境温度老老实实外接 NTC 或者数字温度传感器。2. 底层原理12 位 SAR ADC 和 48MHz 时钟2.1 逐步逼近型 ADC 的工作方式RP2040 内置的 ADC 是 12 位逐次逼近型SARSuccessive Approximation Register不是那种用于音频或者高精度测量的 Sigma-Delta 类型。SAR 的工作原理可以用天平称重来理解你先放一个一半满量程的砝码看看比输入电压高还是低高了就换小一半的砝码低了就继续加小一半的砝码如此反复比较。12 位分辨率就意味着要比较 12 次每一次比较其实就是硬件里的一个时钟周期。这个过程天然就有一个“采样保持”动作ADC 开始转换前内部采样电容先连接到输入引脚把电压存到电容上然后断开输入再逐位比较。所以信号源要给这个采样电容充电如果信号源阻抗太高电容还没充满就开始转换读出来的值就会偏低。这是后面很多坑的根源。RP2040 用的是 48MHz 的 ADC 时钟一次完整的 SAR 转换需要 96 个 ADC 时钟周期。算一下就知道48MHz 除以 96 得到的最大采样率正好是 500kS/s也就是每秒 50 万次采样。这个吞吐量在 MCU 内置 ADC 里属于中规中矩的水平采音频、采电机电流、做简单振动分析都够用但如果你想采宽带信号或者做高精度称重那它就不合适了。2.2 分频器 DIV 是怎么影响采样率的RP2040 的 ADC 模块里有一个 DIV 寄存器作用是把 48MHz 基准时钟继续分频。实际 ADC 时钟频率越低完成一次转换的时间就越长采样率就越低。但降低时钟频率也有好处采样电容的充电时间更宽裕对高阻抗信号源更友好。数据手册里给的是最大采样率 500kS/s实际项目中我很少直接把采样率跑满。原因有几个一是跑满时对信号源阻抗要求很苛刻二是后续数据处理往往跟不上三是不需要那么高的采样率时降频能降低功耗和干扰。Pico SDK 里的adc_set_clkdiv函数本质就是写 DIV 寄存器明白这层关系以后你就知道什么时候该调分频、调多少了。一个我常用的估算方法实际采样率 48MHz / (DIV 分频系数 × 96)。例如 DIV 设置为 10采样率约为 48M / (10 × 96) 50kS/s。如果信号本身只有几百赫兹这个采样率足够还能留出大量 CPU 时间处理其他任务。2.3 模拟输入限制不能超过 3.3VRP2040 的 ADC 输入范围是 0 到 3.3V对应 12 位数字量 0 到 4095。这里有一个非常关键的安全红线输入电压绝对不能超过 3.3V 太多也不能低于 0V。超过的话轻则读数被钳位到 4095重则通过芯片内部的 ESD 保护二极管灌入电流长期下来会损伤引脚甚至永久损坏 RP2040。我之前见过有人直接拿一个可调电源衰减到 3.5V 去测 ADC想着就差 0.2V 应该没事结果引脚附近芯片表面微微发烫后续读值就开始漂。保护电路不是这么用的。稳妥做法是外接分压电阻或者运放把被测电压严格限制在 03.3V 范围内。如果被测信号可能超过 3.3V至少先串一个 1kΩ 到 10kΩ 的电阻让外部异常电流被限制在安全范围内。3. 寄存器工作模式详解从 CS 到 FIFO 再到 DMA3.1 CS 寄存器一切操作的入口RP2040 的 ADC 外设基地址是 0x4004C000核心寄存器包括 CS、RESULT、FCS、FIFO、DIV 和一组中断寄存器。其中 CS 是控制与状态寄存器也是最常用的一个。硬件工程师看寄存器总是从控制位入手我习惯把它拆成几个关键字段来看。CS 寄存器里最基础的有 EN使能、START_ONCE单次启动、START_MANY连续启动、READY空闲/忙状态、ERROR错误标志、AINSEL通道选择和 RROBIN轮询扫描通道选择。整个工作流程可以非常简单地描述先使能 ADC选好通道然后通过 START_ONCE 触发一次转换软件轮询 READY 位或者等中断最后从 RESULT 寄存器读出 12 位结果。读 RESULT 的时候有一点要注意这个寄存器只有低 12 位是有效数据高位的噪声位和保留位最好用掩码清掉。实际代码里就是adc_hw-result 0xFFF这样无论后续寄存器内容如何变化拿到的都是干净的 12 位数值。这个小细节在调试时能省很多眼力。3.2 单次转换与连续转换的差异单次转换是 ADC 最常用的模式。操作序列是选择通道 → 置 START_ONCE → 等待 READY → 读 RESULT。这个模式省电、逻辑清晰适合低频采样场景比如每秒读一次电池电压。SDK 里的adc_read()封装就是按这个流程来的我在文章后面会给出寄存器写法。如果需要连续不断采样比如做波形采集就要用 START_MANY。这个模式下 ADC 会自动连续转换不需要每次去触发。但连续转换产生的结果必须有地方放如果不开 FIFO新的结果会不断覆盖 RESULT 寄存器软件读取时机不对就可能漏数据。所以连续模式我强烈建议配合 FIFO 和 DMA 一起用否则 CPU 忙死还采不完整。刚开始用寄存器操作时最容易犯的错是清了 READY 状态之后就着急启动下一次转换导致转换重叠。其实 READY 只是一个状态位不是中断确认位读 RESULT 也不会清掉它。正确做法是检查 READY确定转换完成再读结果再决定是否启动下一次转换。3.3 FIFO 模式把等待变成批量处理RP2040 的 ADC 内部带了一个深度为 4 的 FIFOFIFO 控制状态寄存器叫 FCS。FIFO 的作用是把多个转换结果先存起来软件可以批量读取也可以等 FIFO 数据量达到阈值后再一次性处理。这在连续采样场景下能显著降低 CPU 开销。FCS 寄存器里需要关注的字段包括 FIFO 使能位、溢出状态、空/满状态以及阈值设置。启用 FIFO 之后建议先清空一次 FIFO避免里面残留上一轮采样数据然后再启动转换。如果 FIFO 满了而新的转换结果又产生RP2040 会丢弃新结果并置起错误状态位。这个错误标志是粘性位需要软件手动清除不处理的话后续采样结果很可能不可信。FIFO 还有一个实用价值是可以配合 DMA 做数据搬运。把 FIFO 的 DMA 请求打开后硬件会自动把 FIFO 里的结果搬到内存数组根本不需要 CPU 介入。我以前采一段完整的电机启动电流波形开了 FIFO 加 DMA 以后CPU 占用率从 80% 降到几乎可以忽略数据完整性还更高。对于需要边采样边处理其他逻辑的项目这一步非常关键。3.4 RROBIN 轮询扫描多通道自动切换ADC 想采多个通道时最笨的办法是每读一次就切换一次 AINSEL再触发一次转换。RP2040 提供了 RROBIN 字段可以选哪些通道参与硬件轮询ADC 会自动依次采样这些通道结果不断进入 FIFO。这个模式对多通道数据采集非常友好。RROBIN 的位宽对应 ADC0 到 ADC3每一位代表一个通道是否参与轮询。置位之后硬件会从低序号通道开始依次转换循环往复。举例来说把 ADC0 和 ADC1 的位都置 1FIFO 里就会交替出现两个通道的采样结果软件在读取时需要知道哪个数据来自哪个通道。这里有个推荐做法在设计时就固定好轮询通道顺序最好自己在 FIFO 数据里加上通道标识或者用固定的偶数/奇数值位置来区分。轮询模式的一个陷阱在于轮询多个通道后等效到每个通道的采样率是下降的。原本单通道 500kS/s如果轮询 4 个通道每个通道平均只能拿到 125kS/s。所以分配通道时要想清楚不是所有通道都需要高速采样那些变化慢的信号就不该占用宝贵的采样时间。3.5 硬件过采样拿采样率换有效位数单独说一个我很看重的功能RP2040 的 ADC 支持硬件过采样和平均。FCS 寄存器里可以配置对多个采样结果进行累加并右移相当于硬件自动做了多次采样平均。原理并不神秘ADC 转换结果里混入了随机噪声多次采样平均后随机噪声会被部分抵消有效位数能提高一些。用过采样的时候要注意它消耗的是采样率。每提高 2 位有效位数理论上需要 4 倍的采样点数。比如你原来有效位数大概 10 位想过采样到 12 位效果那每个输出值背后可能要采 16 次原始样本。对于直流或低频信号这招非常好用比如测电池电压、测精密参考电压出来的数值会稳很多。但如果测的是本来就在快速变化的信号过采样会引入时间上的平滑反而丢失快速变化细节。4. 实战不靠封装函数直接把寄存器玩明白4.1 一个最简的寄存器级单次采样程序前面讲了这么多寄存器概念这里放一个完整的寄存器级示例。我用的是 Pico C SDK但并不调用adc_read()高层封装而是直接操作adc_hw结构体。#include pico/stdlib.h #include hardware/adc.h #include hardware/structs/adc.h // 直接操作寄存器读取 ADC0GPIO26的单次采样结果 uint16_t reg_adc_single_read(void) { // 先让SDK把ADC时钟和外设基础状态初始化好 // 实际会设置EN使能、清理分频等 adc_init(); adc_gpio_init(26); // 选择通道0清空AINSEL字段然后写入0 adc_hw-cs (adc_hw-cs ~ADC_CS_AINSEL_BITS) | (0u ADC_CS_AINSEL_LSB); // 触发一次转换 adc_hw-cs | ADC_CS_START_ONCE_BITS; // 等待转换完成轮询READY位 while (!(adc_hw-cs ADC_CS_READY_BITS)) { tight_loop_contents(); } // 读结果只取低12位有效数据 return (uint16_t)(adc_hw-result 0xFFF); } int main(void) { stdio_init_all(); while (true) { uint16_t raw reg_adc_single_read(); printf(ADC raw: %u, voltage: %.3f V\n, raw, raw * 3.3f / 4095.0f); sleep_ms(500); } }这段代码基本就是 SDK 里adc_read()的底层逻辑。注意两点一是adc_init()已经把使能位和分频设置好了所以不用自己再去配 CS 的 EN二是掩码 0xFFF一定要保留否则高位噪声可能混进来。如果你对某个特定 GPIO 的复用功能不熟悉adc_gpio_init的作用就是把它切换到 ADC 功能少了这步寄存器配置得再对也读不出值。4.2 从 ADC 到舵机控制一套完整的信号链热搜词里出现了“树莓派 pico 控制舵机”很多人以为这跟 ADC 没关系其实舵机闭环控制里 ADC 经常用来读反馈电位器或者直接用 ADC 读取外部旋钮设定目标角度。SG90 这类模拟舵机本身只吃 PWM 信号Pico 的 PWM 模块很容易生成 50Hz、脉宽 0.5ms 到 2.5ms 的控制信号。下面这个示例把 ADC 和 PWM 串成了一条完整的信号链旋转电位器ADC 读回 12 位数值映射成舵机目标脉宽Pico 输出对应 PWM 波形驱动舵机转动。#include pico/stdlib.h #include hardware/adc.h #include hardware/pwm.h #define SERVO_PIN 15 #define PWM_WRAP 19999 // 20ms 周期对应 50Hz int main(void) { stdio_init_all(); // 配置 PWM gpio_set_function(SERVO_PIN, GPIO_FUNC_PWM); uint slice pwm_gpio_to_slice_num(SERVO_PIN); pwm_set_clkdiv(slice, 125.0f); // 125MHz / 125 1MHz保证计数单位为 1us pwm_set_wrap(slice, PWM_WRAP); pwm_set_enabled(slice, true); // 配置 ADC读电位器 adc_init(); adc_gpio_init(26); adc_select_input(0); while (true) { uint16_t raw adc_read(); // 0~4095 映射到 0.5ms~2.5ms 脉宽也就是 500~2500 (us 计数) uint32_t duty 500 (uint32_t)raw * 2000 / 4095; pwm_set_chan_level(slice, PWM_CHAN_A, duty); sleep_ms(10); } }这里 PWM 的时钟分频设置是关键125MHz 的系统时钟除以 125 得到 1MHz意味着 PWM 计数器每增加 1 就是 1µs那么 wrap 设为 19999 正好是 20ms 周期duty 设置为 500 到 2500 正好对应 0.5ms 到 2.5ms 脉宽。电位器从最小转到最大舵机就能跟着从 0° 摆到 180°。整个过程里 ADC 只是“感知”部分但如果没有准确的采样值舵机角度就会飘这正好说明底层采样精度对上层控制的重要性。4.3 FIFO 与 DMA 的协作思路如果你要连续采集一段模拟波形用单次转换模式一个点一个点地读CPU 会被完全占住。正确的做法是配置 ADC 连续转换模式打开 FIFO再让 DMA 把 FIFO 里的数据搬到内存数组。思路是先用 DMA 的通道配置函数设置好源地址为 FIFO 寄存器地址、目的地址为内存数组地址不回绕传输长度就是需要采集的点数。然后启动 ADC 的连续转换此时 DMA 会自动响应 FIFO 的数据请求直到数组填满。整个过程 CPU 只需要在 DMA 传输完成中断里做一次收尾处理。我自己在调试一个音频频谱显示项目时就用这个方案ADC 以 200kS/s 采样DMA 收集 1024 个点后触发中断CPU 再对数据做 FFT。如果不用 DMA这个项目在 Pico 上根本无法同时完成采样和运算。对于采样率要求高、数据处理量大的项目ADC FIFO DMA 几乎是必备组合。5. 比代码更重要的常见坑与排查实录5.1 参考电压和模拟地的坑Pico 的 ADC 参考电压来自 ADC_VREF 引脚板子上默认经过一个低通滤波器接到了 3.3V。但 3.3V 不是精密参考它由板载稳压器产生精度有限、温度特性一般。如果你的项目对电压绝对值要求高比如要做电池电量百分比显示想准确到 0.01V那默认的 3.3V 参考很可能不够用。解决思路有两种。一是校准用一个精密万用表实测参考电压用实测值替代代码里的 3.3V 参与计算。比如实测参考电压是 3.28V那电压计算公式就改成raw * 3.28 / 4095精度立刻上一个台阶。二是外接精密基准源Pico 板上有 ADC_VREF 跳线可以断开默认连接把外部基准芯片的输出直接接到 ADC_VREF 引脚。这个方法适合真正需要高精度测量的场合。模拟地 AGND 和数字地 DGND 的布局也很重要。Pico 开发板把 AGND 引脚安排在 ADC 引脚旁边就是为了让模拟信号回路尽量短。如果你的传感器离板子很远一定用双绞线或者屏蔽线并且让信号地线直接回 AGND不要绕到数字地上去否则数字开关噪声会耦合进模拟信号采样值会莫名跳动。5.2 信号源阻抗的坑为什么读数偏低前面说过 SAR ADC 采样前要给内部电容充电。如果信号源是一个高阻分压器比如两个 100kΩ 电阻把 3.3V 分压成 1.65V然后直接连到 ADC 输入你会发现读数可能偏低而且不是固定偏差而是随温度、随前一次采样值变化。这就是采样电容还没充满就开始转换导致的。解决办法有几个降低分压电阻阻值比如把 100kΩ 换成 10kΩ让源阻抗降下来或者在 ADC 引脚对地并联一个 0.1µF 电容让外部电容提供瞬间充电电流最稳的方案是在信号源和 ADC 之间加一个运放跟随器利用运放的低输出阻抗驱动 ADC。实际项目里如果被测信号变化不快并联一个小电容往往是最省事的方案但要注意它会和源电阻组成低通滤波器把高频信号滤掉。5.3 温度传感器与软件校准如果你想用内部温度传感器做粗略测温至少要做一次单点校准。方法很简单用红外测温枪或者精密温度计测到芯片表面温度同时读取 ADC 温度传感器的 raw 值反推出实际的 27°C 基准电压把这个实测值替换到公式里。这样做之后误差可以从 ±5°C 缩小到 ±2°C 左右。如果想更精确可以做两点校准。比如把板子放进恒温箱或者利用冰水和开水的环境温度差异记录两个温度点对应的传感器电压然后线性插值。但说实话RP2040 内部温度传感器本身的线性度和一致性有限校准收益也有限。我在实际项目中一般只拿它做“芯片是不是过热了”的判断阈值设成 80°C精度要求根本不用太高。5.4 ADC 值漂移、跳变与滤波即使硬件上把所有坑都避开了ADC 采样值依然会有随机噪声只是幅度大小的问题。很多新手看到读数在小数点后几位跳动就慌了其实正常SAR ADC 的最后一位或者两位本来就有噪声。处理办法是软件滤波。我常用的组合是“限幅滤波 滑动平均”。先判断当前值跟上次值的差是否超过合理范围超过就认为是一次离群尖峰丢弃然后用一个 8 到 16 点的滑动窗口做平均。下面是一个简单的滑动平均实现适合直流和慢变信号#define FILTER_N 8 static uint16_t buf[FILTER_N]; static uint8_t idx; static uint32_t sum; uint16_t adc_smooth(uint16_t raw) { sum - buf[idx]; sum raw; buf[idx] raw; idx (idx 1) % FILTER_N; return (uint16_t)(sum / FILTER_N); }这个滤波函数几乎没有延迟响应也够快比中值滤波更适合连续采样场景。如果信号变化本身很慢比如电池电压可以把窗口拉大到 32 点甚至 64 点。注意窗口太大会让输出曲线变“钝”对于需要实时响应的控制回路要权衡滤波强度和响应速度。5.5 多通道切换时的串扰问题多通道轮询模式有个不太容易发现的坑通道切换后采样保持电容里残留的是上一个通道的电压需要一点时间才能充分切换到新通道。如果上一个通道是高电压、下一个通道是低电压切换瞬间读到的新通道数值会偏高产生串扰。RP2040 的硬件轮询本身会有一定切换时间但如果你对串扰敏感比如同时采两路信号一路是 3V 一路是 0.1V最好的办法是切换通道后丢弃前 1 到 2 次采样结果从第三次开始算有效数据。或者干脆用两个独立的采集周期去采不同通道牺牲一点速度换取干净的数据。这个技巧在工业采集里很常见做多路传感器接入时一定要记住。6. 从 Pico 内置 ADC 到外部高精度 ADC 的延伸6.1 VSYS 监测与电池占比计算Pico 的 GPIO29 被板上分压网络接到 VSYS典型场景是用来监测 USB 供电电压尤其是在电池供电项目里算剩余电量很常用。原理是电池电压通过分压降到 ADC 可测范围然后根据分压比换算回真实电压。需要注意的是 Pico 板上 GP29 的分压网络是固定的不同版本的板子分压电阻可能有差异最好用万用表实测一下 GP29 引脚的电压和 VSYS 电压的比例然后把这个比例写进代码。换算公式是[ V_{battery} \frac{raw}{4095} \times V_{ref} \times ratio ]比如实测分压比是 2 比 1那真实电压就是 ADC 读数电压的两倍。做电池监测时要特别注意参考电压的校准3.3V 差 0.05V在 12 位 ADC 上就是 60 多个 LSB 的偏差电池电量百分比能差出好几个点。6.2 什么时候该外接 Sigma-Delta ADCPico 的内置 ADC 是 SAR 型速度有余、精度不足。如果你要接称重传感器、应变片、热电偶这类微弱信号或者要做到 16 位、24 位分辨率内置 ADC 基本没法胜任。商用方案通常用 HX711、ADS1232、MCP3561 这类 Sigma-Delta ADC它们牺牲速度用很高的过采样率换取极高的有效位数。搜索热词里有人提到“24bit 危-螖 adc”其实就是这类器件。HX711 做电子秤非常经典内置可编程增益放大器直接接电阻应变桥通信协议简单。ADS1232 和 MCP3561 的接口更灵活精度更高适合工业称重和精密测量。外接 ADC 和 Pico 的配合并不复杂Pico 的 SPI 和 GPIO 资源足够驱动这些芯片。6.3 外部 ADC 三点校准的思路不管内置还是外部 ADC校准思路是通用的。两点校准能修正增益误差和偏置误差但遇到非线性比较明显的 ADC 或传感器两点校准还不够这时候用三点校准。具体做法给输入端加三个已知的标准电压记为 V0、V1、V2分别读出原始值 raw0、raw1、raw2。先计算每段的增益和偏置。比如第一段修正公式是[ V V_0 \frac{raw - raw_0}{raw_1 - raw_0} \times (V_1 - V_0) ]这样的话输入电压落在哪个区间就用哪个公式。三点校准比两点校准多覆盖一个中间电压点能够把非线性误差控制得更好。实际操作中标准电压可以用高精度可调电源产生也可以用精密基准芯片外加高精度分压电阻产生。校准之后把参数直接烧进 Pico 的 flash 里每次上电加载就能把 ADC 的绝对误差从几个 LSB 压到接近一个 LSB。写在最后的实操心得做了这么多年嵌入式我始终觉得寄存器操作能力是分水岭。Pico SDK 把 ADC 封装得很好用但如果你不理解背后 CS、FCS、DIV 这些寄存器在干什么遇到采样值异常、FIFO 溢出、多通道串扰这类问题时就会手足无措。反过来当你能在脑子清晰浮现“采样电容充电 → 逐步逼近比较 → 结果进 FIFO → DMA 搬到内存”这条链路时绝大多数 ADC 问题都能很快定位到具体环节。我个人还有个习惯每次新接触一块板子不管 SDK 封装多方便都会先研究一下数据手册里外设寄存器的结构再用寄存器写一遍最小功能 demo。这个“用笨办法走一遍底层”的过程往往比看十篇教程都管用。ADC 尤其如此因为它太常用了也太容易想当然。Pico 的 ADC 不算顶级但把它的脾气摸透足够支撑你做出一堆靠谱的小项目和原型了。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进