树莓派Pico ADC采样与温度采集:machine.ADC、NTC与定时器中断避坑指南
2026/9/7 17:42:09 网站建设 项目流程

树莓派 Pico 做 ADC 采样,听起来不算难,但真到了要把一路模拟信号稳定读回来、换算成温度、再按固定周期采集的时候,不少人是会被机器上的 monkey 拳打懵的:读到的数值到底是 0 到 1023 还是 0 到 65535?为什么温度算出来是负的?为什么在中断里加了一行 print,整个板子就像死机了一样?这篇文章就把 machine.ADC 从 API 层面到定时温度采集的完整链路拆开来讲,包括 NTC 分压电路怎么搭、B 值公式怎么算、Timer 中断里哪些事千万不能做。内容适合刚入手 MicroPython 的新手,也适合写过一些代码但是被“中断回调”折磨过的朋友。

1. Pico 的 ADC 到底能干什么

1.1 先搞明白 RP2040 的 ADC 通道

树莓派 Pico 用的 RP2040 芯片内部集成了一个 12 位 SAR ADC,这是逐次逼近型结构,也就是把模拟电压逐步和内部 DAC 的比较电压做比较,一步步逼近真实值。看到“12 位”别急着高兴,MicroPython 的read_u16()返回的并不是原始的 12 位值,而是把 12 位结果等比映射到了 16 位范围,返回0~65535。这一点要是没搞清楚,后面做电压换算很容易自乱阵脚。

RP2040 的 ADC 通道分布其实不多,外部可用的只有 4 路,在板子上分别对应:

  • ADC0:GPIO26
  • ADC1:GPIO27
  • ADC2:GPIO28
  • ADC3:GPIO29

再加上一个内部通道,专门用来读芯片温度,在 MicroPython 里用ADC(4)就能访问。除了这 5 个通道之外,没有其他模拟输入口了。

这里有个很多人会忽略的点:GPIO29 虽然也能做 ADC 输入,但很多 Pico 型号的板子上,GPIO29 已经被板载的 VSYS 检测分压电路占用。换句话说,你如果用 GPIO29 去接外部传感器,读到的电压会和电池/供电电压叠加,数值完全不是你想的那样。如果你不是非常清楚自己手里板子的原理图,外部模拟输入老老实实用 GPIO26/27/28,别去碰 GPIO29。

1.2 硬件连接时的三个关键细节

ADC 的硬件连接看起来简单,就是传感器输出脚接 GPIO,但实操中踩坑的人特别多。

第一,RP2040 的 ADC 输入范围是 0 到 3.3V。如果你把 5V 输出直接接到 GPIO26,轻则读数一直顶到最大值,重则烧掉引脚。所以任何模拟信号进 ADC 之前,先确认电平范围。

第二,ADC 引脚是高阻抗输入,但是输入阻抗不是无限大。你要是拿一个内阻特别高的传感器直接接上去,采样结果会明显偏低,因为 ADC 内部的采样电容充电需要时间。这个问题在接 NTC 热敏电阻分压电路的时候要特别留意,分压网络的等效阻抗最好不要超过 100K 级别,否则读数会不太稳定。

第三,模拟地和数字地尽量保持干净。Pico 本身没有专门的 AGND 引脚,但你在面包板上搭电路时,最好把传感器地线、分压电阻地线集中到同一个点,再回到 Pico 的 GND。不要和舵机、电机这些大电流设备的回流地混在一起,否则 ADC 数值会跟着电机转动一起跳,后面怎么滤波都很痛苦。

这些点听起来很简单,但实际调试的时候,我见过太多人把问题定位到代码上,最后发现其实是参考电压不稳或者共地没做好。

2. machine.ADC API 精讲:从构造到读取的完整链路

2.1 创建 ADC 对象:三种常见写法

MicroPython 在不同平台上对 ADC 构造函数的支持略有差异。在树莓派 Pico 上,你可以用下面几种方式创建对象:

from machine import ADC, Pin # 方式 1:传 Pin 对象 adc = ADC(Pin(26)) # 方式 2:直接传引脚编号 adc = ADC(26) # 方式 3:读取内部温度通道 internal_temp_adc = ADC(4)

三种方式在 Pico 上都是可用的,ADC(26)ADC(Pin(26))效果一样。我自己的习惯是写ADC(Pin(26)),因为这样可以同时拿到引脚对象做其他配置,代码可读性也好一点。

这里有个细节:ADC(4)是 RP2040 特有的内部温度传感器通道编号,在其他板子上数字含义可能完全不同。比如 ESP32 上ADC(4)又是一个外部引脚。所以一旦你的代码要跨平台复用,通道编号这块一定要单独抽出来做配置项。

2.2 read_u16 与底层 12 位精度的关系

我在开头说过,RP2040 的 ADC 是 12 位的,但 MicroPython 最常用的读取方法read_u16()返回的是 16 位范围。

raw = adc.read_u16() voltage = raw * 3.3 / 65535

把 12 位值映射到 16 位,其实就是左移 4 位,相当于raw_16bit = raw_12bit << 4。所以硬件上 1 LSB 对应电压是3.3 / 4096 ≈ 0.806 mV,而 MicroPython 返回的 1 LSB 对应的是3.3 / 65535 ≈ 50.3 uV。也就是说,read_u16()只是在数值上“显得更精细”,真正能达到的物理精度仍然是 12 位。

这个说法听起来有点绕,但实际用起来很简单:你只需要记住,MicroPython 返回 0~65535 之间的整数,用raw / 65535 * 3.3就能换算电压。不需要自己去做位运算。

如果你参考 STM32 的习惯,总觉得应该像 HAL 库那样从某个寄存器里取 12 位原始值,反而容易绕晕。MicroPython 已经把这件事封装好了,你要做的只是理解归一化比例。

2.3 采样周期、建立时间与读数稳定性的关系

“ADC 采样周期”是芯片手册里常见的一个概念。RP2040 的 ADC 时钟来自芯片内部,单个采样转换时间大概在 2 微秒级别。很多人想当然觉得,我只要不停read_u16(),就能以很高频率连续采集。实际上 MicroPython 层面每次调用read_u16()都有解释器开销,真正能跑的采样率比很多人想象的低得多。

另外有个容易被忽略的指标叫“建立时间”。ADC 在采集外部信号之前,内部采样电容要先充电到和输入电压一致。如果信号源的内阻很大,电容充电就慢,转换结果还没稳定下来就被采样了,读到的值自然会偏低。解决方法是:在 ADC 引脚和信号源之间并联一个小电容,比如 100nF,给采样电容一个“蓄水池”,这样读数会稳很多。这也是为什么很多工业传感器的输出端口都会加一级 RC 滤波,不是没有道理的。

3. 温度采集实战:从内部温度传感器到 NTC 热敏电阻

3.1 先用内部温度传感器跑通整条链路

在接任何外部电路之前,我建议你先用 RP2040 自带的内部温度传感器跑一遍代码,确认开发板、固件、ADC 调用都没问题。这是最快的“冒烟测试”。

from machine import ADC import time adc = ADC(4) # 内部温度传感器通道 while True: raw = adc.read_u16() voltage = raw * 3.3 / 65535 # RP2040 数据手册给出的内部传感器换算公式 temp_c = 27.0 - (voltage - 0.706) / 0.001721 print("raw:", raw, "voltage: %.3f V" % voltage, "temp: %.2f C" % temp_c) time.sleep(1)

跑一下,如果温度数值在 25~35 摄氏度之间,说明 ADC 读取链路是通的。内部温度传感器测的是芯片温度,比环境温度一般会偏高几度,尤其是板子运行时间长了以后,这个值会明显高于室温。所以它适合验证链路,不适合做高精度室温监测。

我在第一次跑这段代码的时候,打印出来的温度显示 28 度左右,看起来一切正常。后来用手按住芯片,温度值果然开始上升,说明内部链路完全没问题。这个时候再接外部传感器,就可以把“传感器接线问题”和“代码问题”分开定位。

3.2 NTC 分压电路搭建:两种接法选哪种

外部测温最常用的方案是 NTC 热敏电阻,也就是负温度系数电阻:温度升高,阻值降低。NTC 不能直接接到 ADC,因为 ADC 读的是电压,不是电阻。所以需要用一个固定电阻和 NTC 组成分压电路。

推荐接法如下:

3.3V —— 10K 固定电阻 —— ADC采样点 —— NTC —— GND

也就是固定电阻接在 3.3V 和采样点之间,NTC 接在采样点和 GND 之间。这种接法下,温度越高,NTC 阻值越小,采样点电压越低,ADC 读到的值越小。

此时候的电压关系是:

V_adc = 3.3 * R_ntc / (R_fixed + R_ntc)

反推 NTC 阻值:

R_ntc = R_fixed * V_adc / (3.3 - V_adc)

如果你把 NTC 和固定电阻的位置对调,公式会变成R_ntc = R_fixed * (3.3 - V_adc) / V_adc。两种接法都能用,但推荐前面这种,因为当温度升高时 ADC 数值单调下降,比较容易直觉判断接线是否正确。

固定电阻的取值一般和 NTC 在常温下的阻值相当。比如常见的 10K NTC,就配 10K 固定电阻。这样在 25 摄氏度附近,分压点电压大概是 1.65V,正好处在 ADC 量程中间,测量灵敏度最高。如果配 1K 电阻,分辨率会集中在高温段,低温段反应很迟钝。

3.3 温度换算公式:B 值公式和 Steinhart-Hart 方程

拿到 NTC 阻值只是第一步,下一步要算温度。NTC 的阻值和温度之间的关系不是线性的,最常用的是 B 值公式:

1/T = 1/T0 + (1/B) * ln(R / R0)

其中:

  • T 是目标温度,单位开尔文(K)
  • T0 是标定温度,通常取 298.15K,也就是 25 摄氏度
  • R0 是 NTC 在 T0 温度下的标称阻值,常见是 10K
  • B 是 NTC 的 B 值,常见是 3380、3435、3950 等,买 NTC 的时候商家都会标

换算成摄氏度:

import math def ntc_temp_from_resistance(r_ntc, r0=10000.0, b_value=3950.0, t0=298.15): temp_k = 1.0 / (1.0 / t0 + (1.0 / b_value) * math.log(r_ntc / r0)) return temp_k - 273.15

B 值公式在 -20 到 100 摄氏度的范围内,精度大概在 ±0.5 摄氏度,对大多数 DIY 场景完全够用。想要更高精度,可以用 Steinhart-Hart 方程,需要三个标定系数,一般商家不会给,需要自己做标定。对于普通室温监测,B 值公式足够了。

网上有些代码把公式里的 R0 搞错,或者把 T0 直接用 25 代入而不是 298.15,算出来的温度经常是负数或者上百度。这里一定要记住:公式里的温度用的是开尔文,不是摄氏度。

3.4 完整温度采集代码:从电压到温度的完整链路

下面这段代码可以直接扔到 Pico 上跑,接好上述分压电路后,把传感器放在 GPIO26 上。

from machine import ADC, Pin import time import math # 硬件配置 adc = ADC(Pin(26)) # 分压电路参数 R_FIXED = 10000.0 # 固定电阻,10K # NTC 参数 R0 = 10000.0 # 25 度时的标称阻值 B_VALUE = 3950.0 # 热敏电阻 B 值 T0 = 298.15 # 298.15K = 25°C def read_ntc_resistance(): raw = adc.read_u16() if raw == 0: return None voltage = raw / 65535.0 * 3.3 if voltage >= 3.3: return None # NTC 接地侧时:V_adc = 3.3 * R_ntc / (R_fixed + R_ntc) r_ntc = R_FIXED * voltage / (3.3 - voltage) return r_ntc def resistance_to_temp(r_ntc): temp_k = 1.0 / (1.0 / T0 + (1.0 / B_VALUE) * math.log(r_ntc / R0)) return temp_k - 273.15 while True: r_ntc = read_ntc_resistance() if r_ntc is None: print("ADC 读数异常") else: temp_c = resistance_to_temp(r_ntc) print("R_ntc = %.1f ohm, T = %.2f C" % (r_ntc, temp_c)) time.sleep(2)

这段代码结构分成采集、换算、打印三个部分,后续做滤波、定时采集时都往这三个环节里加逻辑就行。我习惯把 NTC 参数都放在顶部,因为不同批次的 NTC,B 值和 R0 会有差异,做成配置项方便换传感器。

4. 定时温度采集的三种实现方式

4.1 简单粗暴的 while + sleep 方案

如果你只是想在电脑上实时看温度曲线,最简单的办法就是主循环里读一次,睡一秒。

while True: temp_c = read_temperature() print("%.2f" % temp_c) time.sleep(1)

睡眠 1 秒,实际采集周期大约就是 1 秒加一点点执行时间。如果代码里有打印、计算,实际周期会稍微长于 1 秒,但对室温监测来说,抖动几十毫秒完全无所谓。

这种方式的缺点是:主循环被sleep卡住之后,没法同时干别的事情。比如你要按键控制采集开始/停止,或者要驱动 OLED 显示,循环结构就会变得混乱。而且睡眠期间如果其他设备触发了什么事件,响应会有延迟。

4.2 用 machine.Timer 做真正的定时触发

如果希望采集周期不受主循环影响,推荐用machine.Timer。Pico 的 MicroPython 固件支持周期定时器,回调函数会周期性地被触发。

from machine import Timer, ADC, Pin import time adc = ADC(Pin(26)) # 全局标志位和原始值缓存 sample_ready = False sample_raw = 0 def on_timer(timer): global sample_ready, sample_raw sample_raw = adc.read_u16() sample_ready = True timer = Timer() timer.init(period=1000, mode=Timer.PERIODIC, callback=on_timer) while True: if sample_ready: sample_ready = False voltage = sample_raw / 65535.0 * 3.3 # 这里再做温度换算和打印,不要在 ISR 里做 r_ntc = R_FIXED * voltage / (3.3 - voltage) temp_c = resistance_to_temp(r_ntc) print("T = %.2f C" % temp_c) # 主循环可以穿插别的任务 time.sleep_ms(10)

timer.init(period=1000)里的单位是毫秒,所以 1000 就是每秒触发一次。Timer.PERIODIC表示周期模式,还有一个Timer.ONE_SHOT模式,只触发一次,一般用于延时启动。

这套结构的核心思想是:中断回调只负责置标志位,所有耗时操作放到主循环里处理。为什么这么做?下一节详细讲。

4.3 定时方案取舍:什么场景选哪种

三种方式的选择其实不复杂:

  • 只是单个任务,周期也不严格,用while + sleep,代码最简单
  • 要同时处理按键、显示、通信等多个任务,用Timer定时采样,主循环处理其他逻辑
  • 对时间精度要求很高,比如毫秒级采样,就要考虑用time模块的高精度计时,或者直接用 C 写

还有一个小技巧:如果定时周期比较长,比如 5 秒一次,可以把 Pico 在采集间隙进入lightsleep模式,降低功耗。但注意,MicroPython 的lightsleep可能会影响 Timer 的行为,不同固件版本表现不一样,建议实际测试后再用。

5. ISR 避坑指南:中断回调里千万不要干这些事

5.1 MicroPython 中断回调的特殊性

machine.Timer的回调函数,本质上运行在中断上下文里。这和主线程里的普通 Python 函数有一个巨大差别:普通函数可以调用任何功能,但如果中断回调里发生了错误,MicroPython 有时候不会直接抛出你能看懂的异常,而是会让系统进入一种奇怪的不稳定状态。

具体来说,在 Pico 的 MicroPython 固件中,中断回调期间:

  • 分配内存是危险的,因为内存分配器不是完全可重入的,可能导致堆损坏
  • 解释器对 Python 字节码的处理在中断上下文里有限制
  • 耗时操作会拖慢主线程,甚至造成看门狗超时复位

这些限制听起来很抽象,但落到代码上就是几个非常明确的“不能做”。

5.2 ISR 里不能做的三件事

第一,不要在中断回调里调用print。串口输出本身很慢,一个字符可能就要微秒级,一长串字符串打印出去,几十毫秒就没了。更严重的是,print里面涉及到字符串格式化和 IO 操作,这些在中断上下文里非常容易出问题。很多人的 Pico 表现为“上电几秒后程序卡死”,十有八九就是 ISR 里 print 惹的祸。

第二,不要在中断回调里做浮点运算。MicroPython 在 Pico 上做浮点运算依赖软浮点库,计算math.logmath.pow这种函数需要大量栈空间和 CPU 时间,而且内部可能会分配临时对象。在 Pico 上,最安全的 ISR 里只做整数赋值和标志位切换。

第三,不要在中断回调里动态分配内存。list.append()、字符串拼接、创建新对象,这些操作在正常 Python 代码里毫不起眼,但在中断上下文里可能破坏堆一致性。尤其是长时间运行的程序,偶尔会突然崩溃,很难排查。

我一开始写 Timer 回调,直接在里面读 ADC、算温度、打印,看起来跑得挺好。结果运行十几分钟后,板子莫名卡死,必须拔电重启。后来把打印和浮点运算全部移出回调,问题再也没出现过。

5.3 用“标志位 + 主循环”模式安全传递数据

安全的模式是:ISR 里只更新一个全局变量和标志位,主循环检测到标志后就地处理。

sample_ready = False sample_raw = 0 def on_timer(timer): global sample_ready, sample_raw sample_raw = adc.read_u16() # 只读整数,不做运算 sample_ready = True

注意这里我仍然在 ISR 里调用了adc.read_u16()。这个是允许的,因为read_u16()在底层是同步读取硬件寄存器,不分配大内存,执行时间很快。但如果你还是觉得不放心,可以只在 ISR 里置标志,主循环里读 ADC,不过那样采样的时间点会受主循环调度影响,不是严格定时。

“主循环里处理”也有讲究。主循环要快速响应标志位,不能在一个耗时很长的操作里卡住。例如主循环里如果写了time.sleep(5),那标志位就算置位了,也得等 5 秒才能被处理。这种场景下,主循环里建议用time.sleep_ms(10)或更短的时间片。

5.4 为什么我建议把温度换算放到主循环

温度换算本身也不复杂,为什么非要挪到主循环?因为一次math.log调用在 Pico 上可能要花费几十微秒,看起来不多,但如果在中断里反复调用,会明显压缩主循环的可执行时间。更重要的是,math模块内部实现可能会用到临时内存分配,在中断上下文里这是不可控风险。

考虑到采集温度本身是低频任务,每秒一次、每秒十次已经很多了,把换算放到主循环里完全不影响实时性。何必为了省那几十微秒,给自己埋一个随机崩溃的雷呢?

6. 数据滤波与稳定性优化:ADC 噪声其实很常见

6.1 噪声来源:电源纹波、电磁干扰和采样抖动

即使你严格按照 3.3V 供电、共地做好,ADC 读数也未必稳定。我见过最典型的现象是:同一个电压源,连续读取 100 次,最后 20 个 LSB 一直在上下跳。表面看是“噪声太大”,实际上来源可能有三个。

第一个是电源纹波。Pico 的 3.3V 由板载 LDO 提供,纹波本来不大,但如果你用面包板供电,或者用了质量很差的 USB 线,纹波会被 ADC 直接采进去。解决办法是在 3.3V 引脚和 GND 之间并联一个大电容,比如 10uF 和 100nF 配合。

第二个是电磁干扰。NTC 传感器的引线如果太长,相当于一根天线,会把周围环境的电磁噪声耦合进来。解决方法是把引线绞合起来,或者用屏蔽线。ADC 采样点对地并联一个 100nF 电容,也能显著抑制高频噪声。

第三个是采样抖动。ADC 每次采样的时间点不完全一致,如果信号本身带有微弱的 50Hz 工频干扰,采样点不同会导致读到的值不同。用滑动平均滤波可以很好地抹平这种抖动。

6.2 滑动平均滤波和中值滤波的代码实现

最简单的滤波是滑动平均,取最近 N 次采样的算术平均值。

history = [] def read_filtered(raw_adc, window=10): history.append(raw_adc) if len(history) > window: history.pop(0) return sum(history) // len(history)

窗口大小 N 不是越大越好。窗口太大,响应会变慢,比如你要测一个快速变化的温度信号,10 秒均值可能完全看不到变化。NTC 测温本来就慢,室温变化更是缓慢,所以窗口取 5~10 就够。

中值滤波对“偶发毛刺”效果更好。做法很简单:连续读 N 次,排序,取中间值。

def median_filter(adc_obj, count=5): samples = [] for _ in range(count): samples.append(adc_obj.read_u16()) time.sleep_ms(2) samples.sort() return samples[count // 2]

中值滤波的缺点是会消耗更多时间,而且每次读取之间的间隔如果太短,采样值之间相关性高,滤波效果会打折。我一般习惯在 ADC 读取之间加 2~5 毫秒的间隔。

6.3 校准:两点校准和三点校准

如果你对绝对温度精度有要求,比如要做一个温控器,那么 B 值公式算出来的温度还不够。因为 NTC 的标称阻值有误差,B 值也有误差,而且分压固定电阻本身也有 1% 甚至 5% 的精度。

最简单的校准方式是两点校准:把传感器放到冰水和沸水里,分别记录 ADC 读数对应的电阻值,然后反推实际的 B 值和 R0。两点校准能修正大部分误差。

更精细一点,用三点校准配合 Steinhart-Hart 方程,可以逼近 NTC 在整个量程内的非线性特性。三点校准需要三个温度点的阻值数据,然后解一组三元方程,求出 A、B、C 三个系数。

对大多数 DIY 场景,两点校准已经够用。做法是:

  1. 把传感器插入冰水混合物中,等读数稳定,记录实际温度为 0 度对应的 ADC 阻值
  2. 把传感器放入沸水中,记录 100 度对应的阻值
  3. 用这两组数据反推误差修正系数

实测下来,我手上的某款 10K NTC 标称 B 值 3950,实际两点校准后的等效 B 值变成了 3985 左右,差距不算大,但如果你要求的精度是 ±0.2 度,这点差距就很关键了。

7. 常见问题排查速查表

7.1 读到的值一直显示 65535 或 4095

read_u16()返回 65535,或者如果你用某些库读原始值显示 4095,说明 ADC 引脚电压已经顶到了 3.3V 以上,或者引脚悬空。先检查传感器接线:分压电路是不是接反了,NTC 是不是已经被短路到 3.3V,或者 GPIO 引脚有没有和其他输出引脚冲突。

还有一个可能就是引脚配置错误,你把 ADC 和 PWM 功能重复配置了。MicroPython 里一个引脚通常只能配置为一种外设功能,如果前面代码里用过machine.PWM(pin),后面又创建 ADC,有些固件会报错,有些固件会静默失败。解决方案是重启解释器,或者重新初始化引脚。

7.2 ADC 数值在正常情况下跳变剧烈

如果接线确认没问题,读数还是跳来跳去,优先怀疑电源问题。把 Pico 插到电脑 USB 口上,试试换一根数据线,或者在 3.3V 和 GND 之间并联电容。

如果板子旁边正好有电机、继电器、舵机在工作,很大概率是电磁干扰。断开大电流设备,看数值是否稳定。PWM 控制舵机时,舵机电源如果和 Pico 共用一个供电轨,ADC 数值会随着舵机转动产生周期性波动。解决办法是把舵机电源单独供电,Pico 只共享 GND,不共享电源。

7.3 温度值明显偏离实际温度

先检查 NTC 型号。如果你的 NTC 标称阻值不是 10K,而是 100K,但代码里 R0 仍然写的 10000,那温度算出来肯定不对。再看看 B 值,不同厂家同尺寸的 NTC B 值可能从 3380 到 4300 都有。

另一个容易被忽略的问题是分压电阻的选型。如果你手边找不到 10K 电阻,顺手拿了个 100K 电阻顶替,分压点的输出阻抗会大幅上升,ADC 采样时电压会被拉低,导致阻值换算偏高。

还有一个问题:Pico 的 3.3V 并不是严格的 3.300V。你可以用万用表实测一下 3.3V 引脚对 GND 的电压,如果实测是 3.28V,但代码里用的是 3.3,误差会直接反映到温度计算中。测一遍,把实际电压代入代码,结果会准很多。

7.4 程序运行一会儿后死机,尤其是加了 Timer 之后

这几乎可以肯定是 ISR 里做了不该做的事。回去检查一下 Timer 回调里是不是有printmath.log、字符串处理、list.append。把这些全部移出回调,只保留全局变量赋值。

还有一个可能:全局变量在 ISR 和主循环之间同时访问,虽然 MicroPython 有 GIL,不会出现真正的数据竞争,但如果你在主循环里修改了 ISR 正在使用的变量类型,仍然会出问题。建议 ISR 里只用整数类型的标志位和原始值,主循环里读取后立刻复制出来,不要长期引用同一个变量。

7.5 ADC 通道读到的电压和万用表测量值不一致

万用表测的是开路电压,ADC 采样时内部会有采样电流,如果你信号源内阻高,读数就比万用表低。这种情况在分压电阻总和超过 100K 时特别明显。解决办法是降低分压电阻阻值,或者在采样点对地并联一个几微法的电容,给 ADC 内部的采样电容提供足够的电荷。

8. 写在最后的个人体会

做树莓派 Pico 的 ADC 采样,前前后后踩过的坑不少。最让我印象深刻的是 Timer 中断问题,那个“程序运行十几分钟后随机卡死”的 bug,我整整排查了两天。后来把 ISR 里的打印全部换成标志位,代码立即稳定了。从那以后,我给自己定了一个规矩:凡是进入中断回调的函数,里面只能写赋值、比较这种最简单的操作,其他一律放到主循环处理。

Pico 的 ADC 功能不复杂,但真要做得稳定,还是得静下心来把硬件基础打好。你现在如果刚开始接触machine.ADC,建议先跑通内部温度传感器的示例,再一步步搭建外部电路。等你能稳定读到温度数据、并且连续运行一周不出问题之后,再考虑接入 OLED 显示、数据上传云平台这些花活。基础扎实了,后面加什么都顺。

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

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

立即咨询