这段时间在折腾树莓派 Pico,越玩越觉得这个小板子被严重低估了。几十块钱的东西,片上集成了 ADC、PWM、UART 一整套外设,配合 MicroPython 几行代码就能跑起来,特别适合做传感器采集、小电机控制、灯光交互这类入门到进阶的实战项目。今天这篇就围绕一个完整的例子展开:用模拟电位器作为输入源,通过 Pico 的 ADC 通道采集电压,再把采集结果映射成 PWM 信号去控制 LED 亮度,最后用 SerialPlot 在电脑上实时画出波形。整个过程涵盖硬件接线、电路计算、固件编写、串口通信、上位机可视化这几个环节,属于典型的"从硬件到代码"全链路实操。不管你手里是 Pico 还是 Pico W,跟着做一遍,对 ADC 采集、PWM 输出、串口协议这些基础外设的理解绝对会上一个台阶。
1. 项目整体设计与硬件接线
1.1 为什么选 Pico 做这个项目
先聊下选型。做 ADC 采集的板子很多,Arduino、ESP32、STM32 都行,我选 Pico 的核心原因有三个。第一,RP2040 这颗芯片的 ADC 是 12 位分辨率,也就是 0~4095 的量化等级,配合 3.3V 参考电压,理论分辨率能到 0.8mV 左右,比 Arduino 经典的 10 位 ADC 精细得多,采集电位器这种缓变信号绰绰有余。第二,Pico 用 MicroPython 开发非常顺手,ADC 和 PWM 的操作封装得很简洁,不用像 STM32 那样折腾寄存器配置,很适合快速验证想法。第三,Pico 的 PWM 外设支持 16 位计数器,控制 LED 亮度可以做到非常细腻的 65536 级渐变,视觉效果很顺滑。
当然,Pico 的 ADC 也有局限。它只有 4 个可用的外部输入通道,对应 GP26、GP27、GP28、GP29 这四个引脚,而且没有独立的参考电压引脚,VREF 直接接在 3.3V 电源上。这意味着电源质量会直接影响采样精度,后面我会专门讲这个问题怎么处理。整体来说,对于入门到中阶的采集类、交互类项目,Pico 的性价比和上手速度都是第一梯队的。
1.2 硬件清单与接线实操
这个项目的硬件非常精简,都是手边常备的元件:
| 元件 | 型号/参数 | 数量 | 作用 |
|---|---|---|---|
| 主控板 | Raspberry Pi Pico / Pico W | 1 | 核心控制与采集 |
| 电位器 | 10kΩ 旋转电位器 | 1 | 产生模拟电压信号 |
| LED | 普通 5mm 红色或绿色 LED | 1 | PWM 亮度调节对象 |
| 电阻 | 220Ω 或 330Ω | 1 | LED 限流保护 |
| 面包板 | 400 孔或 800 孔 | 1 | 电路搭建 |
| 杜邦线 | 公对公/公对母 | 若干 | 连接线路 |
| 数据线 | Micro USB,注意要支持数据传输 | 1 | 供电 + 烧录 + 串口通信 |
接线方式如下:
- 电位器有三个引脚,两侧的固定端分别接 3V3 和 GND,中间的活动端(也叫滑臂,wiper)接到 GP26。这样旋转电位器时,GP26 上就能得到 0~3.3V 之间的连续电压。
- LED 的阳极(长脚)通过 220Ω 限流电阻接 GP15,阴极(短脚)直接接 GND。选择 GP15 是因为它在 Pico 上对应的 PWM 通道是 5A,使用灵活,后续即使改代码也不影响其他引脚。
- 所有共地线要接牢靠。特别是 USB 供电的 GND 和面包板的 GND 必须是同一根线,否则串口通信和 ADC 参考地都会出问题。
这里我特别强调一下:电位器用的是"分压"接法,不是"限流"接法。旋转电位器的本质是一个可变电阻,把 3.3V 通过电阻分压后输出一个中间电压。10kΩ 的阻值对 Pico 的 ADC 输入来说是合适的,既能保证低功耗,又不会因为源阻抗过高导致采样值漂移。
1.3 硬件搭建的三个注意事项
第一,LED 限流电阻的计算。红色 LED 的典型正向压降约 1.8~2.0V,工作电流取 10mA 时,限流电阻 R = (3.3 - 2.0) / 0.01 = 130Ω,取标准值 220Ω 完全够用,既能保证亮度可观,又不会过量损耗。如果用的是白色或蓝色 LED,正向压降约 3.0~3.3V,在 3.3V 供电下压差太小,亮度上不去是正常的,建议直接换红色或绿色 LED。
第二,电位器的引脚顺序。不同类型的电位器引脚定义不一定一样,我用的这种常见旋转电位器,中间脚就是活动端。如果不确定,用万用表量一下两侧引脚之间的阻值,再用滑臂对地量,旋转旋钮时阻值变化的那一组就是活动端。这个千万别搞混,接反了虽然不会烧板子,但采集值会完全反着走。
第三,接触不良的问题。面包板上插杜邦线的时候,要确保引脚完全插到底。我见过不少初学者采集值突然跳变到满量程,排查了半天发现是电位器中间脚在面包板上虚接。实在不行,把电位器的三个脚直接焊上短引线再插面包板,能大幅减少接触噪声。
2. 固件代码:从 ADC 采集到 PWM 控制
2.1 MicroPython 环境准备与开发选型
Pico 有两种主流的开发方式:C SDK 和 MicroPython。我做这个项目选的是 MicroPython,理由很直接:代码量少,调试迭代快,而且 ADC、PWM、UART 这些外设都有现成的高层封装,非常适合把注意力放在逻辑而不是寄存器上。
开发环境我建议用 Thonny,它对 Pico 的支持很成熟,安装固件、编辑代码、查看串口输出、管理文件系统都是图形化操作。如果你更习惯命令行,也可以用 mpremote 或 VS Code 配合 MicroPico 插件,选自己顺手的就行。
首次使用需要给 Pico 烧录 MicroPython 固件。按住 Pico 上的 BOOTSEL 键,用数据线连接电脑,会出现一个名为 RPI-RP2 的 U 盘,把下载好的.uf2固件文件拖进去,板子会自动重启进入 MicroPython 模式。这一步完成后,在 Thonny 右下角选择"MicroPython (Raspberry Pi Pico)"解释器,就能看到交互式 REPL 提示符了。
2.2 ADC 采集代码逐行解析
下面是核心采集代码,我写的比较啰嗦,主要是方便讲解,实际精简版后面也会给出:
from machine import ADC, Pin, PWM import time # 初始化 ADC,GP26 对应 ADC 通道 0 adc = ADC(Pin(26)) # 初始化 PWM,GP15 对应 PWM 通道 5A led = PWM(Pin(15)) led.freq(1000) # 设置 PWM 频率为 1kHz while True: # 读取 ADC 原始值,范围 0~65535(MicroPython 内部做了 12 位到 16 位对齐) raw_value = adc.read_u16() # 直接将 16 位 ADC 值映射到 16 位 PWM 占空比 led.duty_u16(raw_value) # 通过串口打印数值,供 SerialPlot 采集 print(raw_value) # 控制采样周期,约 100Hz time.sleep(0.01)这里有个非常容易踩的坑,我必须单独拎出来讲:adc.read_u16()返回的值范围是 0~65535,而不是 0~4095。RP2040 的硬件 ADC 是 12 位的,MicroPython 在底层帮你做了左移对齐,把 12 位值左移 4 位变成 16 位。这样做的好处是方便和 PWM 的 16 位占空比直接对接。如果你需要真正的 12 位原始值,可以自己右移四位:raw_value = adc.read_u16() >> 4。
led.duty_u16(raw_value)这一行是把 ADC 采集到的数值直接作为 PWM 占空比。因为两边都是 16 位范围,所以不需要做任何换算。当电位器旋到最小时,ADC 值接近 0,PWM 占空比接近 0%,LED 熄灭;旋到最大时,ADC 值接近 65535,LED 全亮。中间过程则是连续渐变的亮度,这就是整个系统的核心逻辑:用模拟输入控制模拟输出。
2.3 PWM 频率选择与亮度控制细节
PWM 频率这里我用了 1kHz,但很多教程会用 5kHz 或者 10kHz。区别在哪里?LED 的亮度稳定性和人眼的感知。频率太低(比如 100Hz 以下),LED 会明显闪烁,因为人眼能感知到亮灭周期。频率太高(比如 20kHz 以上),虽然不闪了,但可能会产生可闻的线圈啸叫,而且对 MOS 管驱动这类场景有额外损耗。1kHz 对人眼已经完全无感,对 LED 驱动来说是甜点区,所以我默认用这个值。
另外注意一个细节:led.freq(1000)设置的是 PWM 的开关频率,而不是占空比刷新频率。占空比是通过duty_u16动态更新的。当 ADC 采集值持续变化时,PWM 的占空比也在跟着变,LED 的亮度就会实时响应电位器的旋转角度。这个响应链路的延迟主要来自 ADC 采样周期和打印频率,整体在几十毫秒级别,人眼看上去是实时联动的。
2.4 从 12 位到 16 位:MicroPython 的数据对齐逻辑
前面提到过read_u16()返回的是 16 位右对齐值,再展开说说底层原理。RP2040 的 ADC 硬件寄存器是 12 位的,MicroPython 在编写 ADC 类时,为了统一不同开发板的 API,把原始 12 位结果左移 4 位,补成 16 位返回。这样才能保证read_u16()的返回值在 0~65535 之间线性分布。
处理时需要明白的一个换算关系:
| API | 值范围 | 对应电压范围 | 典型中点值 |
|---|---|---|---|
| read_u16() | 0 ~ 65535 | 0 ~ 3.3V | 32768 ≈ 1.65V |
| read_u16() >> 4 | 0 ~ 4095 | 0 ~ 3.3V | 2048 ≈ 1.65V |
如果你用duty_u16(raw_value)控制 PWM,直接使用read_u16()的结果最方便。如果你拿到的是 12 位原始值,记得先左移 4 位再传给duty_u16(),或者用duty_u16(raw_value << 4)。这个坑不搞清楚,LED 亮度变化会只能亮一半,让人怀疑硬件是不是坏了。
2.5 给代码加上采样滤波,让波形更干净
直接读取 ADC 值有可能会看到轻微抖动。抖动来源包括电源纹波、电位器内部碳膜噪声、电磁干扰等。如果直接把抖动的数据用于 LED 控制,肉眼还能接受;但用于 SerialPlot 波形显示会很难看,毛刺很明显。这时可以加一个简单的中值滤波或滑动平均滤波。
滑动平均的思路是维护一个固定长度的缓冲区,每次采样把最新值加入、最老值移除,然后取平均值。代码实现:
from collections import deque adc = ADC(Pin(26)) buf = deque([], 10) # 长度 10 的滑动窗口 while True: buf.append(adc.read_u16()) avg = sum(buf) // len(buf) print(avg) time.sleep(0.01)实测下来,滑动窗口取 10~20 的效果最好,既能明显平滑毛刺,又不会让信号响应变得太迟钝。注意deque的 maxlen 在 MicroPython 里支持得比较完整,可以直接用。如果你想缩短代码,用列表加pop(0)也能达到类似效果,但内存操作略多,不推荐在频繁采样场景用。
3. SerialPlot 串口波形采集实战
3.1 SerialPlot 是什么、能做什么
硬件和代码都通了以后,下一步就是让数据"看得见"。Pico 支持 USB 虚拟串口,MicroPython 的print()输出会通过 USB 以串口数据的形式发给电脑。SerialPlot 是一款免费的串口波形绘图工具,专门用来实时显示串口传过来的数值,省去了自己用 Python + matplotlib 画实时图的麻烦。
SerialPlot 有几个核心优势:支持多通道数据同时显示,支持 ASCII 和二进制两种协议,支持自定义采样时间轴,支持数据导出 CSV。这些功能对调试传感器、观察控制系统的动态响应非常有用。比如你写了一个 PID 温控程序,想同时看设定值、实际温度、PWM 输出三条曲线,SerialPlot 开三个通道就够了,波形图直接摆在一起对比,调试效率翻倍。
3.2 数据输出格式与协议对接
要让 SerialPlot 正确解析 Pico 发来的数据,需要约定好串口协议。最省事的方式是使用 ASCII 格式:一行发送一个数值,多个通道用逗号或空格分隔。以本项目为例,单通道输出,所以只需要:
print(raw_value)SerialPlot 端打开串口,选择波特率 115200,接收格式选 ASCII,然后点开始按钮,它就会自动解析每一行的数值并绘制波形。如果不开 SerailPlot 也可以先打开 Thonny 或任意串口调试助手确认数据格式,确保每一行是一个可解析的数字。
如果你要发多个通道的数据,比如同时发 ADC 值和 PWM 值:
print("{}, {}".format(adc_value, pwm_value))SerialPlot 会自动识别多个逗号分隔的数字为多个通道,并分别用不同颜色的曲线绘制。这个功能在做闭环控制调试时特别有用。注意一点:不要用中文逗号,必须是英文逗号,否则解析会失败。
3.3 波特率、采样频率与数据吞吐量的匹配
很多人忽略一个问题:采集频率高,但打印频率跟不上,或者串口带宽不够,会导致波形显示卡顿或丢数。算一笔账:
- MicroPython 里
time.sleep(0.01)约 100Hz 采样率,每秒 100 个数值; - 单个 16 位数值转成字符串大约占用 5~7 个字符,加上换行符,100 个数值也就 700 字节/秒;
- 115200 波特率理论传输速率约 11520 字节/秒,完全绰绰有余。
所以这个配置下,串口带宽不是瓶颈,瓶颈反而是 MicroPython 的解释执行速度。如果你把采样率提到 1kHz 以上,print()本身可能就会成为耗时大户,因为每个print都要做字符串转换和 I/O。这时候更好的方案是:把数据打包成二进制格式发送,或者降低采样率只传关键特征值。对电位器这种缓变信号,100Hz 的采样率已经非常充足了。
3.4 SerialPlot 界面操作与调参技巧
SerialPlot 的操作逻辑很直观,几个核心选项:
- 串口选择:选择 "COMx (USB Serial Port)" 对应的 Pico 端口;
- 波特率:必须和代码里的
uart.init(115200)或 MicroPython 默认 REPL 匹配,通常用 115200; - 协议类型:选 ASCII;
- 绘图范围:可以手动设置 Y 轴上下限,比如固定到 0~65535,避免自动缩放导致曲线上下乱跳;
- 采样点数显示:调节时间窗口,看 5 秒还是 30 秒的波形。
实操时有个小技巧:先别急着开自动缩放,手动把 Y 轴设成 0~65535,这样波形才能完整显示在整个窗口里,波动幅度一目了然。等代码稳定运行后再打开"自动缩放",可以让微小变化也能看出来。
3.5 完整联动测试:旋转电位器,观察波形与亮度变化
到这里,整个系统已经打通。把代码上传到 Pico 后,复位运行,你会看到 LED 随电位器旋转发生亮度变化。打开 SerialPlot,连接串口后,旋转电位器,屏幕上会画出随角度变化的波形曲线。逆时针到底时,曲线贴近 0;顺时针到底时,曲线冲到 65535 附近;中间旋转时,曲线平滑上升或下降。
这看似简单的测试,实际上验证了完整的信号链路:模拟电压产生 -> ADC 量化 -> 数字映射 -> PWM 输出 -> LED 亮度变化 -> 串口回传 -> 上位机可视化。任何一环出了问题,都能通过这个闭环快速定位。波形如果跳变剧烈,问题大概率在硬件接触质量;波形如果纹丝不动,先检查电位器是否接对了引脚或代码是否在运行;LED 不亮但波形正常,问题出在 PWM 映射或 LED 电路。
4. 常见问题与排查技巧实录
4.1 电压读数不准或漂移厉害
这是玩 Pico ADC 最常遇到的问题。最典型的症状是:电位器固定在一个位置,SerialPlot 上的波形不是一条直线,而是上下抖动,或者数值整体偏大偏小。
先解决抖动。抖动通常来自电源纹波。Pico 的 ADC 参考电压 VREF 直接取自 3.3V,而 3.3V 又来自板载 LDO 从 USB 电源转换而来。USB 电源本身就不是纯净的,如果电脑同时接了很多外设,纹波会更明显。缓解方法很简单:在 3.3V 和 GND 之间并联一个 10~100µF 的电解电容,如果能再并一个 0.1µF 的陶瓷电容更好,高低频纹波一起滤。注意电容要尽量靠近 Pico 的供电引脚。
再解决不准。如果旋转到最底时读到的不是 0 而是几十,旋转到最顶时不是 65535 而是 65000 左右,这不一定是坏了,可能是电位器量程两端存在死区,或者 ADC 的线性度在边界处略有偏差。更精确的做法是拿到原始值后做两点校准:记录实际最小值min_val和最大值max_val,然后写一个映射函数:
def calibrate(value, in_min=100, in_max=65000, out_min=0, out_max=65535): result = (value - in_min) * (out_max - out_min) // (in_max - in_min) + out_min return max(out_min, min(out_max, result))这个"三点校准"或"多点校准"思路在模拟量采集里非常重要。电位器的碳膜本身就有一定非线性,加上 ADC 输入端有采样电容,首采值会有微小的建立误差。做一次标定能显著提升一致性。
4.2 SerialPlot 连不上串口或波形不更新
这个问题九成是端口选择错误或代码还在交互式 REPL 模式。Pico 的 MicroPython 固件默认把 USB 虚拟串口用作 REPL 交互口,如果 Thonny 仍占着这个端口,SerialPlot 就抢不到。解决方案是:先彻底关闭 Thonny,或者先执行 Ctrl+D 软复位让 Pico 脱离 REPL 等待状态,再打开 SerialPlot 连接。
另一个坑是运行的不是实际文件。如果你在 Thonny 的 REPL 窗口里逐行执行代码,关闭 Thonny 后程序就停了。正确做法是把完整代码保存为main.py上传到 Pico 的文件系统,并且让板子重新上电自动运行。SerialPlot 连接时如果还是收不到数据,先在 Thonny 里确认print()有输出,再切换工具排查。
4.3 LED 亮度变化不平滑或完全不亮
LED 亮度变化不平滑,通常是因为 PWM 占空比映射时数据位宽不匹配。如果你把read_u16()的 16 位值直接塞给一个 8 位 PWM API,或者反过来把 8 位值塞给 16 位 PWM,都会导致亮度跳跃。Pico 的 MicroPythonduty_u16接受 0~65535,duty_ns是按纳秒为单位,两种方式混用会乱。新手最容易搞混的是duty()和duty_u16(),duty()是旧版 API,值域 0~1023,新代码强烈建议统一用duty_u16()。
LED 完全不亮的第一步排查:用万用表量 GP15 对地电压。如果旋转电位器时这个电压在 0~3.3V 之间变化,说明 PWM 输出正常,问题在 LED 电路;如果电压固定在 0V 或 3.3V,说明代码或映射有问题。另外记得检查 LED 两个脚的方向,插反了也完全不亮。
4.4 电源与布线的干扰陷阱
项目简单,但干扰问题不能忽视。面包板的寄生电容不是闹着玩的,当电位器引线比较长、且在电机或继电器附近时,ADC 采集值会出现规律性毛刺。硬件上把信号线尽量缩短,远离大电流走线;软件上加上滑动平均滤波。这两招组合使用,能把大多数噪声问题压下去。如果要求更高,可以在 ADC 引脚和 GND 之间并联一个 0.1µF 电容,构成一个简单的低通滤波器,截止频率约 1.6kHz,对几百赫兹的电源噪声有不错的抑制效果。
4.5 常见问题速查表
| 现象 | 可能原因 | 排查方向 |
|---|---|---|
| SerialPlot 无数据 | 端口被 Thonny 占用 | 关闭 Thonny,重新连接 |
| 曲线全是 65535 | GP26 悬空或接触不良 | 检查电位器中间脚接线 |
| 波形毛刺严重 | 电源纹波、面包板虚接 | 加去耦电容、滤波代码 |
| LED 不亮且波形正常 | LED 方向错、限流电阻过大 | 检查 LED 极性、换 220Ω |
| LED 亮度只有 0 和 1 两档 | PWM 位宽映射错误 | 统一用 duty_u16() |
| 旋转时数值突然跳变 | 电位器碳膜磨损或虚接 | 清洁引脚、更换电位器 |
| 采样值整体偏低 | 参考电压偏低 | 测量 3V3 实际电压,做校准 |
5. 扩展方向:从单通道采集到更多实战应用
5.1 多通道 ADC 采集与数据融合
做完单通道采集后,很多人第一个想法是:能不能同时采多个模拟量?Pico 的 ADC 支持多通道切换,但注意所有通道共用一个 ADC 硬件,切换后需要一点建立时间才能读到稳定值。MicroPython 里可以创建多个 ADC 对象:
adc0 = ADC(Pin(26)) adc1 = ADC(Pin(27)) adc2 = ADC(Pin(28)) while True: v0 = adc0.read_u16() time.sleep_ms(1) # 给采样电容一点建立时间 v1 = adc1.read_u16() time.sleep_ms(1) v2 = adc2.read_u16() print("{}, {}, {}".format(v0, v1, v2)) time.sleep(0.01)这时候 SerialPlot 的三条曲线可以同时显示三个通道的变化,比如三个电位器分别控制 RGB 三色 LED 的亮度,或者三个光敏电阻感知环境光强度,直接做成一个多通道数据采集仪。我之前做的一个小项目就是把两个电位器和一个按钮结合,做了一个简易的"双通道调色灯",效果很棒。
5.2 扩展传感器:光敏电阻与土壤湿度
ADC 的典型应用场景远不止电位器。光敏电阻和电位器一样是可变电阻,接法也一样:一端接 3.3V,另一端接一个固定电阻到 GND,中间节点接 ADC。固定电阻的阻值要根据光敏电阻的亮暗阻值范围选择,一般取 10kΩ 左右,能让分压点落在 ADC 量程中段,灵敏度最高。
土壤湿度传感器模块的输出也是模拟电压,接法完全一样,可以直接替换电位器,通过 ADC 采集土壤湿度值,再通过 PWM 控制一个小水泵或浇水指示 LED。这种从"手动旋钮"到"环境变量"的迁移,代码几乎不用改,只需要重新标定测量范围。ADC 采集的通用性就在这里,传感器变了,数据链路不变,核心竞争力是理解信号调理和数据映射的方法。
5.3 与舵机控制结合:从 LED 亮度到角度输出
标题热词里提到了舵机控制,这确实是把 PWM 玩溜之后的自然进阶。舵机本质上也是一个 PWM 控制对象,只是它看的是脉冲宽度而不是占空比。典型舵机需要 50Hz 的 PWM 频率,脉冲宽度 0.5ms~2.5ms 对应 0°~180°。Pico 的 MicroPython 通过duty_ns()可以直接设置纳秒级脉冲宽度,结合 ADC 采集值做映射,就能实现"电位器旋钮控制舵机角度"。
核心代码只需要把 LED 那部分替换成舵机初始化:
servo = PWM(Pin(15)) servo.freq(50) # 50Hz 舵机信号 # 将 ADC 值 0~65535 映射到脉冲宽度 0.5ms~2.5ms pulse_min = 500_000 # 0.5ms 对应 0° pulse_max = 2_500_000 # 2.5ms 对应 180° pulse = int(pulse_min + (raw_value / 65535) * (pulse_max - pulse_min)) servo.duty_ns(pulse)这一步做通以后,ADC -> PWM 这套链路就从"控制灯光"升级成了"控制运动",很多小型机械臂、云台、小车转向的项目基础就有了。前面 LED 阶段积累的滤波、校准、串口调参经验,在舵机调试里依然完全适用。
5.4 与 PC 上位机联动的闭环控制
最后再提供一个高阶思路:让 Pico 不仅往 PC 发数据,还接收 PC 下发的指令,构成一个双向闭环。比如在 SerialPlot 之外,用 Python 脚本读取串口数据并绘制 matplotlib 实时曲线,同时根据曲线特征自动调整 Pico 端的参数。这需要 Pico 端解析串口指令,MicroPython 里用sys.stdin.readline()或者uart.readline()都能实现。
更进一步,可以把 Pico 采集的数据接入 Node-RED 或 Grafana,做成一个完整的 IoT 数据链路。不过这些都是后话了。先把今天的单通道采集、PWM 控制、串口可视化这个闭环吃透,你就已经掌握了嵌入式开发中最核心的"感知 -> 处理 -> 输出 -> 反馈"能力,后面换任何传感器、任何执行器都是同一套方法论。
最后分享一点个人体会:这个项目做完,最大的收获不是会接一个电位器或控制一个 LED,而是建立了一种"信号链"的思维方式。任何一个 ADC 采集项目,都要从源头信号、调理电路、采样量化、数据处理、输出执行、可视化反馈这六个环节逐一审视,任何一环出问题,整个链路都白搭。调试的时候不要同时改多个变量,一次只动一个环节,配合 SerialPlot 的波形,能很快定位病根。这也是我玩了这么多年嵌入式,觉得最值得养成的习惯。