
树莓派 Pico 这块小板子很多人买回来第一个项目就是点灯但点完灯之后呢多半会碰上一个绕不开的需求跟电脑通信、跟传感器通信、跟另一块开发板通信。这时候串口通信就是最直接、最朴素的选择。Pico 的串口通信加上 MicroPython 的封装上手速度比 STM32 那套寄存器操作舒服太多但真到实际调试的时候又有一堆文档里不会写明白的坑等着你。这篇文章打算从硬件特性、MicroPython 编程、调试工具链三个维度把 Pico 串口通信彻底讲透最后再把我实际调试中踩过的典型问题整理成排查清单。不管你是刚拿到 Pico 想用串口给舵机发指令还是想基于 Pico 做一个采集传感器数据的节点或者只是想搞明白宿主机怎么通过串口跟虚拟机里的 Linux 通信这篇文章应该都能给你省下不少时间。1. Pico 上的串口资源不是所有引脚都能随便接很多人拿到 Pico 第一反应是看引脚图上密密麻麻的编号然后就开始纠结该往哪根针上焊线。其实 Pico 的串口资源非常清晰先搞清楚硬件层有什么后面写代码才不会瞎。1.1 RP2040 芯片内置了两路硬件 UARTPico 用的 RP2040 芯片内部集成两路硬件 UART分别叫 UART0 和 UART1。这里说的硬件 UART意味着收发数据、帧格式解析、错误检测这些事都由芯片内部的专用电路处理不需要 CPU 一条一条指令去模拟时序。对比一下用 GPIO 模拟串口的方案硬件 UART 在波特率准确性和 CPU 占用率上有压倒性优势。每一路 UART 在 MicroPython 里对应一个 UART 对象你可以设置波特率、数据位、停止位和校验位。常用配置是 8 数据位、1 停止位、无校验简称 8N1。这个格式跟绝大多数串口设备、USB 转串口工具默认配置一致。Pico 的这两路 UART 可以同时工作互不干扰。如果你项目里既要连调试串口又要连外部传感器模块两路正好各司其职。我自己常用的分配方案是 UART0 接调试口UART1 接实际业务设备这样调试信息和业务数据完全隔离排查问题时一眼就能分清消息来源。1.2 引脚分配与默认复用关系Pico 的引脚具备复用功能同一个引脚可能既可以是 GPIO也可以是 UART、I2C、SPI 或 PWM 功能通过内部选择开关决定当前用哪种功能。具体到串口UART0 的 TX 默认在 GP0RX 默认在 GP1UART1 的 TX 默认在 GP4RX 默认在 GP5注意MicroPython 的 UART 构造函数里如果没有指定 tx 和 rx 参数就会使用上述默认引脚。如果你在初始化时显式指定了其他引脚比如UART(0, txPin(12), rxPin(13))那也是可以的。RP2040 的引脚复用比较灵活多数 GPIO 都能映射到 UART 功能但前提是确认该引脚没有被其他外设占用。我之前犯过一个低级错误UART0 的 RX 用了 GP1结果这个引脚同时还被 ADC 功能占用代码逻辑上没问题但读到的数据偶尔会出错。后面把引脚分开才解决。所以规划项目时最好先把引脚占用表画出来一个引脚不要安排两个功能。1.3 电平标准与外部设备对接原则Pico 的 GPIO 电平是 3.3VUART 的 TX 和 RX 引脚同样是 3.3V 逻辑电平。如果直接接 5V 的 TTL 串口设备从 5V 设备输出的高电平可能超过 Pico 引脚的耐压值长期使用有烧毁风险反过来Pico 输出的 3.3V 高电平某些 5V 设备可能无法正确识别为高电平导致通信不稳定。最稳妥的方案是所有串口对接前先确认两端的电平标准。如果外部设备是 5V TTL建议加电平转换模块或者用分压电阻把 RX 线路的电平降到 3.3V 范围。常见的 USB 转 TTL 模块通常都有 3.3V/5V 跳线接 Pico 时务必拨到 3.3V 档位。还有一个容易忽略的点两个设备之间串口通信需要共地。也就是 Pico 的 GND 要跟对方设备的 GND 接在一起否则信号电平没有一个统一参考点数据乱码甚至完全没反应都是正常的。共地这件事很多新手栽过跟头我见过最典型的现象是发送正常、接收乱码查了半天发现就是 GND 没接。2. MicroPython 串口编程从裸收发到协议解析硬件资源搞清楚之后写代码就顺畅多了。MicroPython 的machine.UART模块把底层寄存器操作封装得很干净核心调用就几个方法但要用好它们还是有一些细节值得细看。2.1 UART 对象初始化与参数选择初始化一个 UART 最简单的写法from machine import Pin, UART uart0 UART(0, baudrate115200, txPin(0), rxPin(1), bits8, parityNone, stop1)这里参数说明UART(0)表示使用 UART0也可以传UART(1)使用 UART1。baudrate是波特率常见值有 9600、57600、115200。Pico 的 UART 波特率理论上最高可以到几 Mbps但我实测下来115200 是稳定性和速度最均衡的选择。9600 太慢大一点的日志传输会卡460800 以上对线材和连接方式敏感杜邦线稍微长一点就容易出错。tx和rx指定引脚不填就默认用 GP0 和 GP1。bits8是 8 个数据位parityNone表示无校验stop1指 1 个停止位。这个组合对应前面说的 8N1。初始化完成后可以用uart.any()查询接收缓冲区里有多少字节数据。这个函数在轮询场景下非常有用可以避免阻塞在读取操作上。2.2 数据收发的基础操作发送数据用write()接收数据用read()或readline()。看一个简单回环示例from machine import Pin, UART import time uart0 UART(0, baudrate115200, txPin(0), rxPin(1)) while True: if uart0.any(): data uart0.read(uart0.any()) uart0.write(data)这段代码做的事每次检查是否收到数据收到多少就原样返回多少。相当于一个最简单的串口回环服务用来验证接线和基本通信是否正常。read(uart0.any())里的参数表示最多读多少个字节这里直接读缓冲区里的全部内容。readline()是按行读取默认遇到换行符\n返回。它适合处理以换行为结尾的文本帧。但要注意readline()会一直阻塞等待直到收到换行符或超时。如果你不确定对方设备会不会发换行符最好先配合any()使用有数据再调readline()。发送数据时有个小技巧write()返回实际发送的字节数。如果返回值和传入数据的长度不一致说明发送缓冲区可能不够需要稍等重试。正常情况不用管但在大数据量发送场景下值得留意。2.3 接收中断与缓冲管理轮询any()的方式逻辑简单但如果 Pico 同时在处理其他任务轮询间隔稍长就可能漏掉数据。这时候用 UART 中断更合适。MicroPython 里可以为 UART 注册中断回调from machine import Pin, UART uart1 UART(1, baudrate115200, txPin(4), rxPin(5)) received_data [] def uart_handler(uart): if uart.any(): data uart.read(uart.any()) received_data.append(data) uart1.irq(triggerUART.RX_ANY, handleruart_handler)UART.RX_ANY表示只要有数据到达就触发中断。回调函数里读取数据并存入全局列表主程序可以随时处理这些数据。这个模式在处理不定长帧的时候很顺手。中断回调函数里尽量不要做耗时操作比如不要在里面直接写文件或者长时间阻塞。它应该只负责把数据搬出来放进缓冲区后续的主循环再处理。虽然 MicroPython 的 UART 内部有缓冲但缓冲溢出后新数据会被丢弃回调处理太快的话缓冲区就容易爆。2.4 一个可落地的串口指令控制舵机示例串口通信最大的价值不在于收发几个字节而是能按协议解析数据、驱动外设。这里我用一个实际项目说明上位机通过串口发送指令控制舵机转动。假设协议格式是三个字节帧头0xAA、舵机角度0~180、校验和帧头与角度之和的低 8 位。三个字节合法才执行否则丢弃。from machine import Pin, UART, PWM import time # 舵机接在 GP15PWM 频率 50Hz servo PWM(Pin(15)) servo.freq(50) def set_servo_angle(angle): duty int(1638 (8192 - 1638) * angle / 180) servo.duty_u16(duty) uart UART(0, baudrate115200, txPin(0), rxPin(1)) buffer bytearray() while True: if uart.any(): buffer.extend(uart.read(uart.any())) while len(buffer) 3: if buffer[0] 0xAA: checksum (buffer[0] buffer[1]) 0xFF if buffer[2] checksum: set_servo_angle(buffer[1]) buffer buffer[3:] else: buffer buffer[1:] else: buffer buffer[1:]这个协议虽然简单但已经包含了帧同步和校验的思路。实际项目里如果需要在一条串口链路上传输多种指令可以把帧头换成指令类型帧尾加上校验和规格化成一个完整的数据帧。协议设计原则是接收方必须能在任何位置进入同步状态即使收到半个帧也能自动恢复不能因为一次错误数据就卡死整个通信流程。这里特别提一下舵机 PWM 的占空比换算。50Hz 对应周期 20ms标准舵机在 0.5ms 高电平对应 0 度2.5ms 高电平对应 180 度。RP2040 的 PWM 是 16 位计数duty_u16范围是 0 到 6553565535 对应全高。所以占空比计算的本质是把脉宽时长转换成 16 位计数值。0.5ms 在 20ms 周期里占比 2.5%对应 16382.5ms 占比 12.5%对应 8192。中间线性插值就是上面代码里的公式。这个公式我实际调过线性关系在普通舵机上完全够用。3. 调试工具选型与真实联调场景写代码只是串口通信的一半另一半是调试。串口通信的调试工具五花八门从几十块的 USB 转 TTL 模块到几千块的逻辑分析仪各有用途。工具选得对排查问题的时间能缩短一倍以上。3.1 必备硬件USB 转 TTL 与逻辑分析仪USB 转 TTL 模块是串口调试的入门必备工具。它把电脑的 USB 接口虚拟成一个串口通过模块上的 TX/RX 引脚与目标设备连接。市面上常见的芯片有 CH340、CP2102、FT232 等实际使用中 CH340 性价比最高几块钱就能买到Windows 和 Linux 下都有成熟驱动CP2102 稳定性更好一点FT232 价格最高一般工业应用才需要。选 USB 转 TTL 模块有几点要注意必须确认模块上有 3.3V/5V 电平切换跳线接 Pico 时拨到 3.3V。最好选带 TX/RX 指示灯的产品收发数据时 LED 会闪烁排查到底有没有数据在线上走这种问题时非常直观。接线时必须交叉连接模块的 TX 接 Pico 的 RX模块的 RX 接 Pico 的 TX同时共地。这个交叉逻辑我反复强调因为正接反接是串口调试最常见的错误。逻辑分析仪则是抓串口协议细节的利器。当你需要确认波特率是否正确、数据位是否完整、某条指令是否真的发出去时逻辑分析仪能直接显示波形软件还能自动解码 UART 协议把波形转成十六进制数据。入门级的 8 通道逻辑分析仪几十块钱就能用软件一般推荐 PulseViewsigrok 配套解码效果很好。逻辑分析仪有一个 USB 转 TTL 无法替代的优势它不参与通信只是旁路监听所以不会影响双方设备的电平状态。遇到一方说自己在发数据、另一方死活收不到的争执逻辑分析仪挂在线上看波形真相立刻见分晓。3.2 软件工具链串口助手、minicom 与 pySerial硬件接好之后电脑端的软件工具也很关键。Windows 下我习惯用串口助手类软件这类工具很多核心功能就是打开串口、配置波特率、发送和接收数据。PuTTY 也能干这个事但用它看 HEX 不如专门的串口助手方便。Linux 环境下minicom是老牌工具命令行操作功能稳定。启动命令是minicom -D /dev/ttyUSB0 -b 115200-D指定设备文件-b指定波特率。minicom默认的显示模式是字符模式适合看文本数据看二进制数据稍微费劲一点。另一个轻量替代是screenscreen /dev/ttyUSB0 115200screen更轻CtrlA 然后按 K 就能退出会话。如果只是快速看一眼串口输出cat /dev/ttyUSB0也不是不行但至少要先把波特率设置对。Linux 下配置串口参数可以用sttystty -F /dev/ttyUSB0 115200 cs8 -cstopb -parenb这行命令把串口设置为 115200 波特率、8 数据位、1 停止位、无校验也就是前面说的 8N1。cs8表示字符大小 8 位-cstopb表示 1 个停止位去掉前缀表示 2 个停止位-parenb表示无校验。如果要在脚本里跟 Pico 通信pyserial是 Python 生态里的标准库。一个简单示例import serial import time ser serial.Serial(/dev/ttyUSB0, 115200, timeout1) ser.write(b\xAA\x5A\x04) time.sleep(0.1) response ser.read(1024) print(response)pyserial比串口助手灵活的地方在于可以写自动化测试脚本批量发送指令、统计响应时间、校验数据完整性。我在做 Pico 指令协议测试时都是用pyserial写脚本一次性验证几十组边界值效率比手点高太多。3.3 三个真实联调场景复盘场景一宿主机 Windows 通过串口与虚拟机中 Linux 通信。这在开发调试中很常见目标是在 Linux 环境里运行程序访问 Windows 宿主机上的物理串口。VMware 和 VirtualBox 都支持把宿主机串口直接映射给虚拟机。具体操作不复杂在虚拟机设置中添加串行端口选择使用物理串口再指定宿主机上的 COM 口。映射成功后Linux 里就能看到/dev/ttyS0这样的设备节点然后按普通串口操作。这里的坑在于宿主机上如果已经打开了该 COM 口的软件虚拟机会争抢设备导致打不开必须确保物理串口没被占用。场景二Pico 与 PC 之间用 USB 转 TTL 做双向通信。PC 上用串口助手发文本指令Pico 接收后解析执行。这个场景里最容易踩的坑是 USB 转 TTL 模块的 TX 和 RX 接反。接反的现象是 Pico 收不到任何数据串口助手倒是能收到奇怪的乱码甚至是自己发出的数据。排查时先看模块上的 TX/RX 指示灯发送数据时只有 TX 灯闪说明接线有问题。场景三Pico 通过 UART 与 ESP32 设备通信。这类跨芯片通信要注意波特率双方必须一致而且两边都要用 3.3V 电平。ESP32 和 Pico 都是 3.3V可以直连但 GND 必须相连。我自己调过一块 Pico 和三块 ESP32 组网通信Pico 做中心节点三块 ESP32 分别上报传感器数据用 115200 波特率跑一小时没丢包。4. 常见问题排查与避坑经验串口调试最磨人的不是写代码而是排查各种稀奇古怪的现象。这里我把实际调试中遇到的高频问题和排查方法整理成一套可执行的思路按顺序排查绝大多数问题都能在几分钟内定位。4.1 串口完全无反应的排查顺序先给一个可以直接照做的排查顺序确认接线TX 接对方 RX、RX 接对方 TX、GND 必须共地。这三根线任何一根有问题通信都起不来。用万用表通断档量一下线路是否导通排除杜邦线内部断线或接触不良。确认双方参数一致波特率、数据位、停止位、校验位四个参数必须完全一致。常用的是 115200-8-N-1两边都要设置正确。确认设备枚举正常在 Windows 设备管理器或 Linuxls /dev/tty*里能看到设备节点确认驱动已安装。CH340 在 Windows 下偶尔需要手动装驱动插上没反应时先看设备管理器里有没有未知设备。做回环测试把 USB 转 TTL 模块的 TX 和 RX 直接短接然后在调试软件里发送数据。如果模块本身正常收到的应该就是自己发出去的内容。这一步能验证整个工具链是否正常。我用这套顺序排除过很多问题其中接线正常但没反应的案例里最后发现七成是驱动问题、两成是波特率不一致、剩下的才是硬件问题。所以一上来不要急着换硬件先按这个顺序过一遍。4.2 乱码与数据错位的常见原因乱码是最常见的串口问题而且原因很多最容易误判。波特率不一致是乱码的第一大来源。波特率指每秒传输的比特数两边的采样时刻必须对齐才能恢复出正确的数据。误差超过容许范围接收方就会把 1 看成 0、把 0 看成 1。实际排查时如果收到的数据是连续的干扰符号第一反应就应该是波特率不匹配。检查方法是计算一下实际波特率和配置波特率的误差比如用逻辑分析仪抓一个字符的位宽算出实际波特率跟配置值对比。第二个原因是电平不匹配。前面说过 Pico 是 3.3V 电平如果接到 5V 设备或者 USB 转 TTL 模块拨到了 5V 档数据线高电平范围不对接收方无法正确识别表现也是乱码或者完全不可靠。遇到乱码第一个动作不是改代码而是确认电平档位。第三个原因是文本编码问题。如果 Pico 发送的是字节数据而调试助手按 ASCII 文本显示非 ASCII 字节就会显示成乱码。这个不是通信问题换一下显示模式为 HEX 就能确认。比如发送0xFF 0x01按 HEX 模式显示就是FF 01按文本模式可能显示成两个奇怪字符。第四个是接地问题。前面提过串口通信必须共地。如果地线没接好信号参考点漂移通信质量随机性极大有时候正常有时候乱码。这个问题的隐蔽性在于它可能不是 100% 出问题而是偶尔出错排查起来像玄学实际上就是地线没接牢或接触不良。4.3 引脚复用、电源与驱动的隐藏坑有些问题藏得比较深不是一次排查就能发现的这里单独列出来。引脚复用冲突是我吃过亏的地方。Pico 某些引脚同时承担多种功能比如 GP0 默认的 UART0 TX跟 ADC 通道不冲突但 GP1 既是 UART0 RX也是 I2C0 SDA 或 ADC1。如果你初始化 I2C 时用到了 GP1串口接收功能就会异常。解决方法是规划引脚时画一张完整的复用表确认每个引脚的当前用途是唯一的。电源问题也会间接影响串口通信。Pico 如果供电不足运行时 UART 收发会产生毛刺或丢数据尤其是同时驱动舵机、WiFi 模块这类高功耗外设时。舵机启动瞬间电流很大如果 Pico 用的是 USB 口供电电压跌落严重时不仅舵机抖动串口也可能发错数据。建议给 Pico 用带独立供电的电源舵机的外接电源要与 Pico 电源分开只共地不共电。这个细节在控制舵机的项目中特别重要很多人的舵机乱跳、串口数据偶尔出错最后查到的都是电源问题。驱动问题集中在 USB 转 TTL 模块上。CH340 芯片在 Windows 10/11 下一般能自动识别但某些精简版系统缺少驱动需要手动安装。还有一个常见坑Windows 更新驱动后COM 口号变了旧软件还盯着原来的 COM 号自然收不到数据。排查时先打开设备管理器确认当前 COM 号。这里再分享一个小工具使用技巧。用mpremote远程连接 Pico 时它的mpremote connect /dev/ttyACM0方式实际上走的是 USB 虚拟串口而不是 Pico 的 UART0/UART1。这个 USB 虚拟串口在 MicroPython 环境下默认是 REPL不是普通 UART。所以你要是通过 USB 线连接 Pico然后试图初始化UART(0)跟电脑通信电脑上是看不到任何数据的因为UART(0)的数据根本没有走 USB 线。这是新手最容易困惑的问题。想要 UART0 与电脑通信必须用 USB 转 TTL 模块接 GP0/GP1而不能直接用 USB 数据线。我个人在实际调试里还有一个习惯无论代码多赶串口通信程序一定会加上异常处理。MicroPython 的 UART 操作偶尔会因为缓冲区溢出或外设忙抛出异常不加try/except的话整个运行循环都可能被中断。加上异常处理至少能保证程序不会因为一次串口错误就崩溃。通信协议层面帧头、帧尾、校验加全这是所有稳定通信系统的底线。Pico 的串口通信说白了就是三件事硬件接对、参数配对、工具用对。硬件上记住共地和交叉连接参数上记住 8N1 和双方一致工具上备一个 USB 转 TTL 和一个逻辑分析仪基本就能覆盖 90% 的场景。剩下的就是多调多测踩过坑之后自然会形成肌肉记忆。