树莓派Pico ADC实战:从原理到ISR定时采集与校准

发布时间:2026/9/8 20:33:26
树莓派Pico ADC实战:从原理到ISR定时采集与校准 别的不说单说树莓派 Pico 上这颗 ADC真的是让人又爱又恨。爱的是它便宜大碗RP2040 给了你 4 个外部模拟输入通道加 1 个内部温度通道MicroPython 里machine.ADC几行代码就能读数恨的是如果你只照着教程抄了个read_u16()然后直接上项目十有八九会被读数飘、零点偏、定时采集卡死这些问题折腾到怀疑人生。这篇文章就把 Pico 的 ADC 从硬件原理到软件 API从单纯读电压到定时采温度再到最容易被忽略的 ISR 中断采集坑完整过一遍。所有东西都是我在实际项目里踩过之后整理出来的代码可以直接复制跑但更重要的是搞清楚每一步背后的为什么。适合刚拿到 Pico 想玩模拟采集的新手也适合已经写了几天 ADC 代码但总感觉哪里不对劲的进阶玩家。1. 先把 Pico 这颗 12 位 SAR ADC 的底细摸清楚1.1 采样原理与 12 位分辨率到底意味着什么Pico 用的是逐次逼近型 ADC也就是 SAR ADC。你可以把它想象成一个天平秤砣的过程内部有一个比较器一个 DAC先从最高位开始猜把猜测的电压和输入电压比较根据结果决定这一位是 1 还是 0然后继续猜下一位。猜 12 次就得到了一个 12 位的二进制结果。这个过程很快但对输入信号有一些要求后面会讲。12 位分辨率是什么概念输入量程是 0 到 3.3V那么最小的量化台阶就是LSB 3.3V / 4096 ≈ 0.8058mV也就是说ADC 读到的每个数字变化 1对应电压变化约 0.8mV。如果你觉得这个精度不够其实不是 Pico 的问题而是 SAR ADC 的物理极限这个价位这个位数已经算良心了。在 MicroPython 里你常用的read_u16()返回的是 0 到 65535 的 16 位无符号整数注意这并不代表 ADC 真的有 16 位精度。它只是把 12 位的结果左移了 4 位或者做了类似缩放让你在软件层更好处理。真正有效的低 12 位信息你换算电压的时候还是要除以 4096 而不是 65535。1.2 输入阻抗和采样保持电容很多人忽略的大坑SAR ADC 在工作前需要先对内部的采样保持电容充电充电时间取决于信号源的内阻。如果信号源内阻太高电容充不满采出来的值就会偏低而且会不稳定。Pico 官方文档其实提了一句ADC 输入没有内部缓冲放大器所以对信号源阻抗比较敏感。实测下来如果你直接用一根杜邦线把电位器或者传感器输出接到 ADC 引脚通常没问题因为电位器的阻值一般在 10kΩ 级别。但如果你接的是一个高阻输出的传感器比如某些 pH 电极、压电薄膜或者前面串了一个非常大的分压电阻读数就会明显飘甚至完全不准。我的建议是如果信号源阻抗超过 10kΩ最好在进入 ADC 之前加一个运放电压跟随器用一句大白话说就是加一个缓冲让 ADC 看到的是一个低阻的、稳定的信号源。如果你不想加运放那就尽量把传感器输出端的电阻压到 10kΩ 以下。1.3 通道映射哪些引脚真的能用Pico 上 RP2040 芯片一共有 5 个 ADC 通道但在板子上并不是都能随便用通道GPIO对应 Pico 引脚注意事项ADC0GPIO2631 脚完全可用ADC1GPIO2732 脚完全可用ADC2GPIO2834 脚完全可用ADC3GPIO2935 脚Pico 板上被 VSYS 分压电路占用一般不作为普通 ADC 使用ADC4内部温度传感器无接的是芯片内部温度传感器也就是说在标准 Pico 板子上真正方便拿来做的外部模拟输入就 GPIO26、GPIO27、GPIO28 这三个。GPIO29 在 Pico 上其实是用来检测 USB 供电电压VSYS的出厂时板上已经接了分压电阻你要想拿它当普通 ADC 还得先搞清楚板上走线不建议新手碰。2. machine.ADC 的 API 细节做到心里有数2.1 创建 ADC 对象传 ID 还是传 Pin 对象MicroPython 的machine.ADC构造函数有两种写法效果一样from machine import ADC, Pin # 方式一传 ADC 通道号0~3 表示外部通道4 表示内部温度传感器 adc0 ADC(0) # 方式二传 Pin 对象更直观后续复用引脚对象方便 adc_pin Pin(26, Pin.IN) adc1 ADC(adc_pin) # 方式三直接传引脚编号MicroPython 也能识别 adc2 ADC(26)我个人习惯用方式二因为有时候我还要提前把引脚配置成输入模式或者需要知道这个 ADC 对应哪个物理引脚。三种方式跑出来的结果完全一样看你自己习惯。2.2 read_u16、read_uv、read_u32到底该用哪个machine.ADC对象主要有这么几个读方法read_u16()返回 0~65535 的整数内部把 12 位 ADC 结果缩放到 16 位范围。这是兼容性最好的方法大部分教程都用它。read_uv()返回微伏单位的电压值MicroPython 1.17 之后支持。省去你自己换算的步骤但返回值是整数单位是 µV。read_u32()返回 0~0xFFFFFFFF 的整数是把 u16 的结果再放大得到的一般用不上。如果你要算电压最直接的做法是# 从 read_u16 换算电压 voltage adc.read_u16() * 3.3 / 65535 # 或者直接用 read_uv除以 1000000 换算成伏特 voltage_v adc.read_uv() / 1000000这两种方式计算的电压值理论上是一样的区别在于read_uv()内部可能考虑了 ADC 基准电压的实际值在某些 MicroPython 版本里会更准确一点。不过实测差异可以忽略项目里选一种就行建议统一用read_u16()因为相关资料最多排查问题方便。2.3 别再用 adc.read()这是个隐藏的坑如果你在搜索引擎找 Pico ADC 的教程会看到一些很老的帖子让你用adc.read()然后跟你说返回的是 0~1023。那个是早期 MicroPython 版本为 Pico 适配时的历史遗留现在的固件里read()这个方法的行为已经和read_u16()一样了但再老一点的固件可能会有兼容问题。我踩过一次在一台很久没升级固件的 Pico 上跑老代码用了adc.read()当 10 位读数结果所有数据都不对。排查了半天才发现是固件版本和 API 行为差异。解决办法很简单别用read()统一改成read_u16()然后把你的换算公式从除以 1024改成除以 65535。2.4 一次完整的电压读取代码长这样from machine import ADC import time # 用 GPIO26 做 ADC 输入 adc ADC(26) while True: raw adc.read_u16() voltage raw * 3.3 / 65535 print(raw:, raw, voltage:, voltage, V) time.sleep(0.5)这段代码跑起来很简单但真正做项目你会发现一个问题打印出来的数据一直在小幅抖动。比如你接一个稳定的 1.5V 电压读出来的值可能在 1.49 到 1.51 之间跳。这不是 Pico 坏了这是 ADC 的正常噪声。后面专门讲滤波。3. 定时温度采集实战NTC 分压电阻 定时器3.1 为什么用 NTC 热敏电阻而不是 DS18B20平时做温度采集很多人首选 DS18B20因为它走的是 1-Wire 数字协议不需要 ADC读出来的直接就是温度值方便得很。但如果你是想玩 ADC或者需要一个低成本、响应快、电路简单的方案NTC 热敏电阻是更好的选择。NTC 的阻值会随温度变化而且变化非常明显25°C 时 10kΩ0°C 时可能到 30kΩ50°C 时可能降到 4kΩ 左右。你只要把它的阻值变化转换成电压变化再用 ADC 去读这个电压就能反推温度。整个过程是完全模拟的非常适合用来练手 ADC 采集、滤波、标定这一整套流程。另外 NTC 的响应速度通常比 DS18B20 这种封装在金属管里的数字传感器快贴在发热源上更能及时反映温度变化做温控项目时优势明显。3.2 硬件连接与分压电路计算我用的电路是经典的电阻分压3.3V - NTC(10k25°C) - ADC引脚 - 固定电阻(10k) - GND注意NTC 在上、固定电阻在下。这样接法下温度升高时 NTC 阻值下降分压点电压升高ADC 读到的值增大趋势是正的看起来比较舒服。你要是反过来接也行只是后面换算时要记得温度越高电压越低。固定电阻选多少理论上选和 NTC 在目标温度中点阻值接近的电阻能让分压曲线在这个温度附近最线性。目标工作范围如果是 0~100°C中点大概在 50°CNTC 在 50°C 时阻值大概 3.6kΩ 到 4kΩ10k 3950 型号所以选一个 3.3kΩ 到 10kΩ 的固定电阻都有。我项目里直接用了 10kΩ因为手边最常见而且测量范围从 -20°C 到 100°C 都能覆盖只是中段线性度略差后用查表法完全可以弥补。分压电路的计算公式Vout 3.3 * R_fixed / (R_ntc R_fixed)那么通过 ADC 读到的电压反推 NTC 阻值R_ntc R_fixed * (3.3 / Vout - 1)注意这里的 Vout 是 ADC 引脚处的电压也就是 ADC 实测电压。把这个公式写进代码里后面每读到一个电压就能算出当前 NTC 阻值。3.3 温度换算B 值公式和查表法算出 NTC 阻值后怎么得到温度两种常用方法。第一种B 值公式法。这是基于 NTC 的指数特性得到的理论公式1/T 1/T0 (1/B) * ln(Rt / R0)其中T 是目标温度单位开尔文T0 是 298.15K也就是 25°CR0 是 25°C 下的 NTC 阻值比如 10000ΩB 是 NTC 的 B 值你买传感器时会拿到常见的是 3435 或 3950Rt 是当前实测阻值MicroPython 里的代码import math R0 10000 B 3950 T0 298.15 def read_temperature(adc_value): voltage adc_value * 3.3 / 65535 if voltage 0: voltage 0.0001 # 防止除零 rt 10000 * (3.3 / voltage - 1) temp_k 1 / (1 / T0 (1 / B) * math.log(rt / R0)) temp_c temp_k - 273.15 return temp_c, rt, voltage第二种查表法。先做一个温度到 ADC 原始值的对应表然后实时读取时在表里做线性插值。这样做的好处是你可以在标定时直接把实际传感器的真实温度-ADC值对应关系存进去绕开 B 值模型带来的误差精度更高代价是你需要先有一组标定数据。我实际项目里是这么干的用 B 值公式生成的 0 到 100°C 步进 1°C 的 ADC 值数组注意这里存的是 u16 原始值不是电阻值。每次读到一个 ADC 值就在数组里二分查找落在哪两个温度之间然后线性插值。# 假设 lut[] 是 0~100°C 对应的 ADC 原始值数组需要预生成 def lookup_temperature(raw): if raw lut[0]: return 0.0 # 温度越高NTC 阻值越低分压电压越高raw 越大 for i in range(1, len(lut)): if raw lut[i]: ratio (raw - lut[i-1]) / (lut[i] - lut[i-1]) return (i - 1) ratio return 100.0查表法比 B 值公式在两端更准确因为 B 值公式在极端温度下偏移会比较大现在网络上的温控项目很多都用这种查表加插值的方式。3.4 用 Timer 做定时采集别再用 time.sleep 硬撑一开始我做定时采集用的是time.sleep(2)然后读取一次最简单但有一个明显的缺点读取的时刻不精确如果你有其他耗时操作sleep 之后还要等定时就不准了。更好的方式是使用 MicroPython 的machine.Timer。Pico 上能用 Timer 对象做定时回调周期可以精确到毫秒级from machine import Timer adc ADC(26) latest_raw 0 def adc_timer_callback(timer): global latest_raw latest_raw adc.read_u16() timer Timer() timer.init(period2000, modeTimer.PERIODIC, callbackadc_timer_callback) # 主循环不用管采集等 timer 自动更新 while True: if latest_raw 0: temp, rt, v convert(latest_raw) print(temp:, temp) time.sleep(1)这里有个关键细节Timer.PERIODIC模式会让回调函数每 2 秒执行一次回调里只做 ADC 读取和更新全局变量温度换算放在主循环中。这正好引出了这篇文章的重头戏——ISR 避坑。3.5 采样滤波三种实用方案对比ADC 裸读出来的数据抖动是正常的尤其在 NTC 这种传感器上因为 NTC 自身热噪声和 ADC 量化噪声会叠加。不加滤波直接用控温会跳得没法看。比较实用的滤波方案就三种第一种滑动平均滤波。维持一个固定长度的队列每来一个新数据就把它加进去同时把队首的旧数据移出去取平均。class MovingAverage: def __init__(self, size10): self.size size self.buf [] def add(self, value): self.buf.append(value) if len(self.buf) self.size: self.buf.pop(0) return sum(self.buf) / len(self.buf)第二种中值滤波。连续采 5 次排序后取中间值。对尖峰噪声特别有效但会引入一定延迟。第三种一阶低通滤波。相当于软件 RC 滤波代码最简单alpha 0.3 filtered 0.0 def lowpass(new_value): global filtered filtered filtered alpha * (new_value - filtered) return filteredalpha 越大响应越快、滤波越弱越小越平滑但延迟也越大。温度采集通常低频信号alpha 取 0.2 到 0.4 比较合适这个参数每个项目都不同需要根据自己的采样周期去调。我的经验是温度采集用滑动平均就够队列长度设 8 到 16 之间温度显示就已经很稳定了。如果还是要抖先去检查你分压电路的电阻噪声而不是一味加大滤波深度。4. ISR 避坑指南在定时器中断里读 ADC 的正确姿势4.1 MicroPython 的中断回调到底是个什么环境MicroPython 的中断回调比如定时器、外部中断触发时执行的那个函数运行时不是在普通的主程序循环里而是在中断上下文里。这意味着你在回调函数里做的事情越少越好整个回调的执行时间越短越好。原因有两条第一中断回调执行期间主循环是被暂停的你在回调里写 100 行的数据处理逻辑主程序就卡在这个中断里面 100 行的时间第二MicroPython 在中断回调里不能安全地执行某些操作比如动态分配内存list.append()超过预分配容量时就会分配新内存、抛出复杂的异常、调用带浮点运算的库函数这些都可能导致运行时错误甚至系统崩溃。最典型的报错是Traceback (most recent call last): MemoryError: memory allocation failed, allocating ... bytes有些 MicroPython 固件版本在 ISR 里发生内存分配错误后回调会直接被丢弃看起来程序还在跑但数据就是不变了非常坑。4.2 坑一在 ISR 里做浮点运算和温度换算刚开始我把 NTC 的温度换算直接写在 Timer 回调里结果定时器回调执行时间明显变长系统响应变慢而且长时间运行后偶尔会卡死。后来一查math.log()这个函数在 MicroPython 里是软浮点实现的运算量很大在中断上下文里做太奢侈。正确做法是回调里只做最简单的一件事比如只读read_u16()并存入一个全局变量至于电压换算、电阻换算、温度换算、滤波全部放到主循环里去处理。这样定时器中断的开销只有几微秒对系统的影响微乎其微。4.3 坑二在 ISR 里用 time.sleep 或者 print 调试调试期间我犯过一个经典错误在回调里加print()看数据结果程序跑几秒就崩了。原因很简单print()在 MicroPython 中会调用复杂的输出模块可能会触发内存分配而且输出到串口还是阻塞式的中断回调里阻塞等待串口发送完成如果串口没连电脑等待时间更长整个系统卡得像死机一样。如果一定要调试中断里的数据就用一个标志位加一个全局变量在主循环里打印flag_data_ready False latest_raw 0 def timer_cb(t): global latest_raw, flag_data_ready latest_raw adc.read_u16() flag_data_ready True while True: if flag_data_ready: flag_data_ready False print(new data:, latest_raw)4.4 坑三在 ISR 里用 ticks_ms 判断时间结果逻辑错乱有人会在回调里用utime.ticks_ms()记录上一次执行的时间用来判断两次回调之间的时间间隔。理论上可行但在 MicroPython 的定时器中断上下文里ticks_ms()的返回值有时是不可靠的因为它依赖系统节拍而系统节拍本身可能被中断处理影响。如果你需要精确测量时间更稳妥的方式是用一个自增计数器counter 0 def timer_cb(t): global counter counter 1主循环里把 counter 的差值乘上定时器周期就能算出经过的现实时间。这个方法没有依赖任何时钟函数在中断上下文里绝对安全。4.5 正确的 ISR ADC 采集模式我推荐这么搭说了这么多坑给一个实测稳定跑了几周的完整框架from machine import Timer, ADC import time # 采集参数 SAMPLE_PERIOD_MS 1000 # 全局变量 adc ADC(26) latest_raw 0 sample_counter 0 data_ready False # 定时器回调只读 ADC只做极简操作 def timer_cb(timer): global latest_raw, sample_counter, data_ready latest_raw adc.read_u16() sample_counter 1 data_ready True # 初始化定时器 timer Timer() timer.init(periodSAMPLE_PERIOD_MS, modeTimer.PERIODIC, callbacktimer_cb) # 主循环负责所有计算和输出 while True: if data_ready: data_ready False voltage latest_raw * 3.3 / 65535 rt 10000 * (3.3 / voltage - 1) temp_c 1 / (1/298.15 (1/3950) * math.log(rt / 10000)) - 273.15 print(sample_counter, temp_c)这个模式里中断函数只做两个操作一次硬件寄存器读取、一次全局变量赋值。没有内存分配、没有浮点、没有输出理论上是绝对安全的。主循环里做所有重活两件事各司其职互不干扰。4.6 定时器周期不是越短越好要考虑 ADC 采样时间有人想做高频率采样把定时器周期设成 1ms结果发现数据全是乱的。原因很简单RP2040 的 ADC 在转换之间需要一定的稳定时间而且分压电路的电容效应也需要时间重新建立电压。实际测试下来Pico 的 ADC 单次转换本身很快微秒级但如果你前面的信号源阻抗比较大比如上万欧的 NTC 分压电路ADC 内部的采样保持电容需要充电时间频率太高时电容还没充满就读下一次数据自然不准。对温度采集这种慢变信号定时器周期设在 500ms 到 2s 之间都合理没必要追求高速。如果你真的需要高频采样那就要重新设计前端电路并仔细阅读 RP2040 数据手册里 ADC 采样时间的参数。5. 常见问题速查与校准心得5.1 读数、稳定性、温度换算问题排查表现象可能原因解决方案读数一直跳幅度大于 0.05V信号源阻抗太高加运放跟随器或降低分压电阻阻值读数整体偏高/偏低ADC 偏移误差或基准电压误差做两点或三点校准用软件修正温度换算明显不对NTC B 值符号或公式用错确认 B 值检查 R0、T0 参数温度读数在某个点突然跳变查表法插值边界处理有误检查 lut 数组越界设置边界约束定时器回调里 print 后程序死机ISR 内做内存分配/耗时操作回调只更新全局变量主循环输出每次上电第一次读数异常ADC 引脚未稳定上电后延时 100ms先读几次丢弃5.2 校准方法从零开始的单点、两点、三点校准Pico 的 ADC 出厂并不是特别准偏移和增益误差是存在的。如果你只是测个大概温度忽略它如果你要拿来做产品或者精确测量就要校准。单点校准最简单适用于只关心相对变化的场景。比如你知道当前环境是 25°C你读到一个 ADC 原始值把这个值定义为 25°C 的基准点以后所有温度都是相对这个点算的偏差。问题是一旦温度范围拉大增益误差会暴露出来。两点校准是更实用的方式也是我做温度采集项目时用的方案。拿一个稳压电源或者精密电压源分别给 ADC 输入两个已知电压比如 1.0V 和 2.0V分别记录 ADC 读到的原始值 raw1 和 raw2然后线性校正电压真实值 (raw - raw1) * (2.0 - 1.0) / (raw2 - raw1) 1.0这个公式本质上是在做一次线性映射补偿 offset 和 gain 两项误差。只要你选的两个校准点覆盖了你实际工作的电压范围精度就会好很多。三点校准则是为了应对 ADC 实际响应不完全线性的情况尤其在量程两端。做法是用三个已知电压标出三段折线每段独立做线性映射。有人说三点校准够用其实如果 ADC 的非线性很差三点也不够但对 Pico 这颗 ADC 来说三点校准已经能发挥出它的极限精度了。5.3 我的校准实测记录我用一个直流稳压电源给 Pico 的 GPIO26 输入已知电压做了完整的校准测试实际输入电压校准前读数(换算电压)两点校准后读数0.500V0.482V0.501V1.000V0.973V1.001V2.000V1.958V2.002V3.000V2.951V3.000V可以看到校准前最大偏差大约 18mV这个偏差在 12 位 ADC 上已经不算小了。校准之后在整个量程范围内偏差基本控制在 2mV 以内。如果你后面要做 NTC 温度采集这个电压误差换算成温度大约只有 0.1°C 甚至更低完全可以接受。5.4 上电稳定期不处理前几次采样有一个细节很多人注意不到Pico 上电后瞬间ADC 引脚电压还没稳定尤其是在你用了大电容滤波的电路上分压点电压要慢慢爬升。如果上电后立刻开始采集前几次读数通常是偏低的温度会显示一个不太真实的上升过程。我的做法是定义一个初始化阶段比如开机后前 500ms 内采样但不记录只用来等电路稳定。实测这个设计对温度控制项目很有帮助可以避免控温逻辑在开机瞬间产生误动作。6. 一次完整的 NTC 温度采集工程代码整合前面所有知识点给一份可以直接跑的完整代码。这个 demo 做了这么几件事定时器每 1 秒触发一次中断中断里只读 ADC 原始值主循环做滤波、换算、校准和打印。from machine import Timer, ADC import time import math # ---------- 硬件参数 ---------- ADC_PIN 26 R_FIXED 10000 # 分压固定电阻 10kΩ R0 10000 # NTC 25°C 阻值 10kΩ B_VALUE 3950 # NTC B 值 T0 298.15 # 25°C 开尔文温度 # ---------- 采样参数 ---------- SAMPLE_INTERVAL_MS 1000 # ---------- 校准参数按 5.2 节方法标定 ---------- OFFSET_RAW 0.0 GAIN_RAW 1.0 # ---------- 全局变量 ---------- adc ADC(ADC_PIN) latest_raw 0 sample_counter 0 data_ready False # 滑动平均队列 filter_size 8 filter_buf [] # ---------- 定时器回调极简操作 ---------- def adc_timer_cb(timer): global latest_raw, sample_counter, data_ready v adc.read_u16() v v * GAIN_RAW OFFSET_RAW latest_raw int(v) sample_counter 1 data_ready True # ---------- 滤波 ---------- def moving_average(value): global filter_buf filter_buf.append(value) if len(filter_buf) filter_size: filter_buf.pop(0) return sum(filter_buf) / len(filter_buf) # ---------- 温度换算 ---------- def raw_to_temp(raw): voltage raw * 3.3 / 65535 if voltage 0: voltage 0.0001 rt R_FIXED * (3.3 / voltage - 1) if rt 0: rt 0.1 temp_k 1 / (1 / T0 (1 / B_VALUE) * math.log(rt / R0)) return temp_k - 273.15 # ---------- 主流程 ---------- timer Timer() timer.init(periodSAMPLE_INTERVAL_MS, modeTimer.PERIODIC, callbackadc_timer_cb) # 上电稳定期等待 500ms time.sleep(0.5) while True: if data_ready: data_ready False raw_filtered moving_average(latest_raw) temp_c raw_to_temp(raw_filtered) print(count:, sample_counter, raw:, latest_raw, temp:, round(temp_c, 2), °C) time.sleep(0.1)这段代码包含了我前面讲到的所有关键点ISR 极简操作、主循环重活、滑动平均滤波、NTC 公式换算、上电稳定期。你把ADC_PIN、R_FIXED、B_VALUE改成自己电路的实际参数接好线就能直接跑。我在实际调这个项目时最初没加滤波温度在小数点后一位来回跳看着很闹心。加了 8 点滑动平均之后同样环境下温度稳定在 26.4°C几乎没有肉眼可见的波动了。如果你觉得响应太慢把filter_size调小到 4 试试找到一个平衡点就行。7. 从 Pico 往外延展ADC 项目的通用经验Pico 这颗 ADC 玩明白了你去碰 STM32 的 ADC、ESP32 的 ADC很多经验是通用的。比如 SAR ADC 的输入阻抗问题几乎在每个单片机上都会遇到比如中断回调里不能做浮点和内存分配在 STM32 的 HAL 库中断里同样是铁律比如 ADC 校准的线性映射思想换什么芯片都一样。如果你后面想升级到外部独立 ADC 芯片比如 HX711、ADS1232 这种高精度 24 位 ADC它们自身的数字接口和校准方式又是另一套玩法但模拟前端设计、滤波策略、抗干扰布局这些底层逻辑你在 Pico 上练过一遍之后再上手会快很多。还有一点值得说的就是 C 语言里常见的 ADC 滤波很多做法比如中值滤波、滑动平均、IIR 低通在 MicroPython 里你能写出来换成 C 实现思路也差不多。区别只是 C 里用数组和指针Python 里用列表逻辑完全一致。我个人在实际操作中的体会是树莓派 Pico 这颗 ADC 虽然参数上比不过那些专门的数据采集芯片但作为入门和学习平台它最大的价值恰恰在于处处有坑、坑坑能学。你把输入阻抗搞明白了把 ISR 的限制摸透了把校准的流程跑通了这些东西在之后任何单片机的 ADC 项目里都能直接用上。所以别嫌它精度不够先把基本功练扎实比什么都强。