树莓派 Pico ADC 探秘:从寄存器到 FIFO、DMA 与过采样
2026/9/9 5:39:56 网站建设 项目流程

刚拿到树莓派 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 位逐次逼近型(SAR,Successive 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.3V

RP2040 的 ADC 输入范围是 0 到 3.3V,对应 12 位数字量 0 到 4095。这里有一个非常关键的安全红线:输入电压绝对不能超过 3.3V 太多,也不能低于 0V。超过的话,轻则读数被钳位到 4095,重则通过芯片内部的 ESD 保护二极管灌入电流,长期下来会损伤引脚,甚至永久损坏 RP2040。

我之前见过有人直接拿一个可调电源衰减到 3.5V 去测 ADC,想着就差 0.2V 应该没事,结果引脚附近芯片表面微微发烫,后续读值就开始漂。保护电路不是这么用的。稳妥做法是外接分压电阻或者运放,把被测电压严格限制在 0~3.3V 范围内。如果被测信号可能超过 3.3V,至少先串一个 1kΩ 到 10kΩ 的电阻,让外部异常电流被限制在安全范围内。

3. 寄存器工作模式详解:从 CS 到 FIFO 再到 DMA

3.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 的 FIFO,FIFO 控制状态寄存器叫 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 的位都置 1,FIFO 里就会交替出现两个通道的采样结果,软件在读取时需要知道哪个数据来自哪个通道。这里有个推荐做法:在设计时就固定好轮询通道顺序,最好自己在 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" // 直接操作寄存器读取 ADC0(GPIO26)的单次采样结果 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 ADC

Pico 的内置 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 不算顶级,但把它的脾气摸透,足够支撑你做出一堆靠谱的小项目和原型了。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询