树莓派串口全解析:UART、SPI、I2C与GPIO配置实战指南
1. 为什么值得花时间把树莓派的串口摸清楚很多人拿到树莓派的第一件事是点亮 LED、跑个 Python 脚本再进阶一点就是接个摄像头做图像识别。但真正让树莓派从玩具变成工具的往往是那些看起来不起眼的低速总线——UART、SPI、I2C。这三个东西加上 GPIO构成了树莓派和外部世界对话的底层通道。你手里的传感器、屏幕、存储芯片、单片机、舵机驱动板几乎都是通过这几根线跟树莓派打交道的。我见过太多人卡在同一个地方买了一个模块照着教程接线代码跑起来没反应然后开始怀疑人生。问题十有八九出在串口配置上——要么是树莓派的 UART 被系统占用没释放要么是 SPI 没在配置里打开要么是 I2C 地址没扫到。这些坑其实都不难但如果你对树莓派各个串口的身份和脾气没有系统性的认识就会反复踩。这篇内容就是把我这些年用树莓派做项目时关于串口部分的经验整理出来。从 UART、SPI、I2C 到 GPIO 的基础概念到树莓派 4B 和 5 上的具体配置方法再到实际接线和调试时容易忽略的细节都会覆盖到。不管你是刚拿到树莓派的新手还是已经用过一段时间但总觉得串口这块知其然不知其所以然的开发者应该都能从中找到有用的东西。特别是那些正在做树莓派项目、需要接各种外设模块的朋友这篇内容可以当作一个串口速查手册来用。2. 树莓派上到底有哪些串口各自是什么角色2.1 UART、SPI、I2C 三兄弟的分工逻辑树莓派的 40 针 GPIO 排针上并不是所有引脚都只能做数字输入输出。其中有相当一部分引脚有第二职业它们可以被配置成 UART、SPI 或 I2C 的通信线。理解这三者的区别是选型的第一步。UART 是最古老也最直观的串口。它只需要两根线TX发送和 RX接收再加上地线就能工作。数据一位一位地传没有时钟线靠双方约定的波特率来同步。树莓派上默认的 UART 是/dev/serial0它背后其实对应着硬件 UART 或者迷你 UART具体是哪个取决于你的树莓派型号和配置。UART 的优点是简单、可靠、距离可以拉得比较远几米没问题缺点是速度相对慢而且一般只能点对点通信没法像 I2C 那样一条总线上挂一堆设备。SPI 是速度担当。它用四根线SCLK时钟、MOSI主出从入、MISO主入从出、CS片选。因为是全双工、有时钟同步SPI 的速率可以轻松跑到几十 MHz。树莓派 4B 的 SPI 时钟最高可以配置到 125 MHz实际稳定值要看外设这在低速总线里算是相当快了。SPI 的代价是引脚多而且每增加一个从设备就要多一根片选线。它适合那些需要高速数据传输的场景比如驱动 TFT 屏幕、读取高速 ADC、跟 Flash 芯片通信。I2C 是省引脚担当。它只用两根线SDA数据和 SCL时钟就能在一条总线上挂最多 127 个设备7 位地址。每个设备有自己唯一的地址主设备通过地址来寻址。I2C 的速度比 SPI 慢不少标准模式 100 kHz快速模式 400 kHz高速模式 3.4 MHz但胜在引脚少、扩展方便。它适合接那些数据量不大但数量多的传感器比如温湿度、气压、加速度计、EEPROM 等。注意树莓派的 I2C 引脚是开漏输出必须外接上拉电阻才能正常工作。树莓派板载已经有 1.8kΩ 的上拉电阻所以一般直接接模块就行。但如果你挂了很多设备或者走线很长可能需要额外并联更小的上拉电阻。上拉电阻太小会导致功耗增加和信号边沿变差太大则上升沿太慢导致通信失败这个后面会详细说。2.2 树莓派 4B 与 5 的串口资源差异树莓派 4B 和树莓派 5 在串口资源上有一些值得注意的变化。树莓派 4B 的 40 针 GPIO 上默认提供了一路 UARTGPIO14/15、两路 SPISPI0 在 GPIO9/10/11 和 GPIO7/8SPI1 在 GPIO16/17/18/19/20/21、以及一路 I2CI2C1 在 GPIO2/3。此外还有 I2C0 但默认被系统占用一般不建议动。树莓派 5 的 GPIO 排针布局跟 4B 保持兼容但内部的总线控制器换了芯片RP1 芯片负责 GPIO 和外设。树莓派 5 上 SPI 和 I2C 的可用实例更多性能也更好。不过对于大多数项目来说默认的那几路已经够用了。需要注意的是树莓派 5 的 UART 配置方式跟 4B 略有不同/boot/firmware/config.txt的路径和参数有些变化后面会具体讲。还有一个容易被忽略的点树莓派上其实有两路 UART。一路是硬件 UARTPL011性能好、波特率稳定另一路是迷你 UART它其实是 GPU 的一部分性能稍差波特率容易受系统负载影响。默认情况下蓝牙会占用硬件 UART而 GPIO 排针上引出的/dev/serial0会指向迷你 UART。如果你要做对波特率精度要求高的串口通信比如跟某些对时序敏感的模块通信就需要把硬件 UART 分配给 GPIO把蓝牙切到迷你 UART 上。这个交换在树莓派 4B 上通过dtoverlaydisable-bt和dtoverlayminiuart-bt来实现。2.3 GPIO 的复用功能与引脚映射关系树莓派的 GPIO 引脚不是一根筋的每个引脚都有多种功能通过引脚复用pin muxing来切换。比如 GPIO14 默认是 UART 的 TX但你也可以把它配置成普通的数字输出或者 SPI 的某个信号。这种灵活性带来了便利也带来了困惑——为什么我明明接的是 SPI 的线代码里却读不到数据很可能是因为这个引脚被别的功能占用了。树莓派的引脚映射可以通过pinout命令或者gpio readall来查看。pinout是树莓派官方系统自带的工具输出一张彩色的引脚图非常直观。gpio readall是 WiringPi 工具集里的命令会列出每个引脚的 BCM 编号、物理编号、当前模式和电平状态。我建议在接线之前先跑一下这两个命令确认你要用的引脚当前是什么状态。另外树莓派的 GPIO 编号有三种物理编号就是排针上的第几针、BCM 编号芯片内部的编号也是代码里最常用的、WiringPi 编号WiringPi 库自己的编号。这三种编号经常让人搞混。我的习惯是统一用 BCM 编号因为 Python 的 RPi.GPIO 和 gpiozero 库默认都用 BCM。物理编号只在接线的时候用对照引脚图看就行。3. 动手之前树莓派串口配置的完整流程3.1 系统层面的串口开关与配置修改拿到一张新的系统卡烧录完系统之后第一件事不是急着写代码而是把串口该开的开、该关的关。树莓派官方的 Raspberry Pi OS 里raspi-config工具可以完成大部分配置。运行sudo raspi-config进入 Interface Options里面可以分别启用 SPI、I2C、Serial Port。这里有一个非常关键的选项当你在 Serial Port 里选择启用时系统会问你 Would you like a login shell to be accessible over serial?这个一定要选No。如果你选了 Yes系统会把串口分配给控制台登录你的代码就没法用这个串口了。然后它会问 Would you like the serial port hardware to be enabled?这个选Yes。这样配置之后/dev/serial0就可以给你的程序用了。如果你用的是 Ubuntu 22.04 或者树莓派 5 上的新系统raspi-config可能不完全一样或者需要手动改配置文件。在树莓派 4B 的 Raspberry Pi OS 上配置文件在/boot/config.txt在树莓派 5 或者 Ubuntu 上路径可能是/boot/firmware/config.txt。你需要确保里面有这几行enable_uart1 dtparamspion dtparami2c_armon改完之后重启生效。重启后可以用ls /dev/serial*和ls /dev/spi*、ls /dev/i2c*来确认设备节点是否存在。3.2 验证串口是否真正可用配置改完不代表就能用了还得验证。对于 UART最直接的验证方法是把 TX 和 RX 短接也就是回环测试然后发数据看能不能收到。你可以用 Python 的pyserial库写个几行的小脚本import serial ser serial.Serial(/dev/serial0, 9600, timeout1) ser.write(bhello) print(ser.read(5)) ser.close()如果 TX 和 RX 短接后能打印出bhello说明 UART 硬件和驱动都正常。如果读出来是空的那就要检查是不是串口被占用了或者波特率不对。对于 I2C验证方法是sudo i2cdetect -y 1。这个命令会扫描 I2C-1 总线上的所有地址把有设备响应的地址显示出来。如果你接了模块但扫描不到先检查接线再检查模块是否上电最后检查上拉电阻。对于 SPI验证稍微麻烦一点可以用lsmod | grep spi看驱动是否加载然后用spidev库写个简单的读写测试。提示在树莓派 5 上I2C 和 SPI 的设备节点编号可能跟 4B 不同。比如 I2C 可能是/dev/i2c-1也可能是/dev/i2c-13具体取决于你启用了哪一路。用ls /dev/i2c*确认一下再用。3.3 串口权限与用户组设置默认情况下/dev/serial0、/dev/spidev0.0、/dev/i2c-1这些设备节点的权限是 root 或者 dialout、spi、i2c 用户组。如果你用普通用户跑程序会遇到 Permission denied。解决办法是把当前用户加到对应的组里sudo usermod -a -G dialout,gpio,spi,i2c $USER然后注销重新登录或者重启。这样就不用每次都用sudo跑 Python 脚本了。用sudo跑脚本虽然能解决权限问题但会带来另一个坑用sudo跑的时候Python 环境变量和用户目录下的库路径可能不一样导致import失败。所以还是老老实实配用户组比较稳妥。4. 三大串口在实际项目中的接线与调试要点4.1 UART 接线交叉连接与电平匹配UART 接线最简单的原则就是TX 接 RXRX 接 TXGND 接 GND。很多人第一次接的时候会直连 TX 到 TX然后纳闷为什么收不到数据。记住发送端要对着接收端所以是交叉的。电平方面树莓派的 GPIO 是 3.3V 电平绝对不能直接接 5V 的串口设备。如果你要跟 Arduino Uno 或者某些 5V 的单片机通信必须加电平转换模块或者至少加个分压电阻。我见过有人直接把 5V 的 TX 接到树莓派的 RX 上结果把 GPIO 烧了。虽然树莓派有一定的保护但长期这么干迟早出问题。如果你是用 USB 转串口模块比如 CH340、CP2102、FT232跟树莓派通信那模块本身输出的是 USB 电平经过芯片转换成 3.3V 或 5V 的 TTL 电平。买的时候要确认模块支持 3.3V很多便宜的 CH340 模块只有 5V 输出需要改跳线或者自己加电平转换。调试的时候我习惯在电脑上开一个串口调试助手Windows 下用 XCOM 或者 SSCOMLinux 下用 minicom 或者 picocom树莓派这边用 Python 脚本收发。这样能直观地看到数据流排查问题很方便。如果电脑上没有物理串口就用 USB 转串口模块装好驱动之后会在设备管理器里多出一个 COM 口。4.2 SPI 接线片选、时钟极性与相位SPI 的接线比 UART 多两根线但逻辑也不复杂。标准四线 SPI 是 SCLK、MOSI、MISO、CS。树莓派的 SPI0 默认引脚是信号物理引脚BCM 编号SCLK2311MOSI1910MISO219CS0248CS1267接线的时候SCLK 接从设备的时钟MOSI 接从设备的输入从设备的数据输入MISO 接从设备的输出CS 接从设备的片选。注意有些模块上标的是 SDI、SDO、SCK、SS对应关系是 SDIMOSI、SDOMISO、SCKSCLK、SSCS。SPI 有两个容易踩坑的参数时钟极性CPOL和时钟相位CPHA。这两个参数组合成四种模式Mode 0~3。如果模式不对数据会错位或者完全读不到。大多数模块用 Mode 0CPOL0CPHA0但有些 ADC 或者 Flash 芯片用 Mode 3。具体用哪个模式一定要查模块的数据手册不能凭感觉试。还有一个坑是片选方式。树莓派的硬件 SPI 控制器自带片选信号但有些模块需要软件控制片选比如在传输前后手动拉低拉高某个 GPIO。树莓派的spidev库支持硬件片选但如果你要用软件片选就需要把 CS 引脚配置成普通 GPIO在代码里手动控制。这两种方式在时序上有区别软件片选更灵活但速度慢一些。4.3 I2C 接线上拉电阻与地址冲突I2C 的接线最简单SDA 接 SDASCL 接 SCLVCC 和 GND 接好就行。但 I2C 的坑往往不在接线上而在上拉电阻和地址上。前面说过I2C 是开漏输出必须靠上拉电阻把总线拉高。树莓派板载的 1.8kΩ 上拉电阻对于大多数场景是够的。但如果你挂了很多设备或者走线很长比如超过 30cm总线电容会增加上升沿变慢可能导致通信失败。这时候可以尝试减小上拉电阻比如并联一个 1kΩ 的电阻让总上拉降到 1kΩ 左右。但也不能太小太小会导致低电平时电流过大有些设备可能拉不低。地址冲突是另一个常见问题。很多传感器模块的 I2C 地址是固定的比如 BMP280 是 0x76 或 0x77MPU6050 是 0x68。如果你要挂两个同型号的传感器就得想办法改地址。有些模块提供了地址选择引脚比如 AD0拉高或拉低可以切换地址。如果没有那就只能用 I2C 多路复用器比如 TCA9548A来扩展总线。用i2cdetect -y 1扫描的时候如果看到某个地址显示为UU说明这个地址被内核驱动占用了你的用户程序没法直接访问。这种情况通常是因为系统自动加载了某个驱动你需要用dtoverlay或者黑名单的方式禁用那个驱动。5. 常见问题排查与实战避坑经验5.1 串口设备找不到或权限报错这是最常见的问题没有之一。表现是ls /dev/serial0不存在或者 Python 脚本报Permission denied。排查思路是这样的第一步确认配置有没有生效。检查/boot/config.txt或/boot/firmware/config.txt里enable_uart1有没有写对有没有被注释掉。改完之后有没有重启。第二步确认串口有没有被控制台占用。运行cat /proc/cmdline如果看到consoleserial0或者consolettyAMA0说明串口被内核当控制台用了。这时候需要改cmdline.txt把consoleserial0,115200删掉。第三步确认权限。用ls -l /dev/serial0看属主和权限。如果是root dialout而你的用户在dialout组里那就没问题。如果不在用groups命令确认一下然后usermod加组。第四步如果以上都没问题但 Python 还是报错检查一下是不是用了sudo跑脚本导致环境不对。或者检查pyserial有没有装对版本。5.2 I2C 扫描不到设备或通信不稳定I2C 扫描不到设备原因可能有很多。我一般按这个顺序排查先看接线。SDA 和 SCL 有没有接反VCC 有没有接对有些模块标的是 VIN 而不是 VCC电压范围可能不一样。GND 有没有共地I2C 必须共地不共地什么都收不到。再看上拉电阻。如果模块自带 10kΩ 上拉树莓派板载 1.8kΩ并联之后大约是 1.5kΩ一般没问题。但如果模块的上拉是 100kΩ那并联之后还是接近 1.8kΩ也没问题。问题出在模块没有上拉而树莓派的上拉又因为走线太长而失效。这时候可以在 SDA 和 SCL 上各加一个 4.7kΩ 的上拉电阻到 3.3V。然后看地址。用i2cdetect -y 1扫描如果扫不到试试降低 I2C 速率。在/boot/config.txt里加dtparami2c_arm_baudrate50000把速率降到 50kHz有时候能解决长走线或者劣质模块的问题。最后看电源。有些 I2C 模块功耗比较大树莓派的 3.3V 引脚供电能力有限总共大概 50mA 到 100mA取决于型号。如果模块需要更大电流得单独供电但记得共地。5.3 SPI 通信失败或数据错位SPI 通信失败最常见的原因是模式不对。我遇到过好几次模块手册上写的是 Mode 0但实际用 Mode 0 读出来全是 0xFF换成 Mode 3 就正常了。所以如果数据不对先把四种模式都试一遍。另一个原因是片选没控制好。如果你用的是硬件片选确认spidev打开的设备节点是/dev/spidev0.0还是/dev/spidev0.1对应 CS0 和 CS1。如果用软件片选确认 GPIO 有没有正确拉低和拉高时序对不对。还有一个隐蔽的坑是 SPI 时钟太快。树莓派 4B 的 SPI 默认时钟是 125MHz 分频出来的实际速率可能比你设置的高。如果模块支持的最高时钟是 10MHz你设了 20MHz就可能出现数据错位。这时候把时钟降下来试试比如设成 1MHz 或者 500kHz。5.4 常见问题速查表问题现象可能原因排查方法解决方案/dev/serial0不存在UART 未启用或被控制台占用检查 config.txt 和 cmdline.txt启用 UART删除 console 参数Python 报 Permission denied用户不在 dialout 组groups查看当前组usermod -a -G dialout $USERI2C 扫描不到设备接线错误或上拉不足检查 SDA/SCL/VCC/GND加 4.7kΩ 上拉降低速率I2C 地址显示 UU内核驱动占用lsmod查看驱动禁用对应驱动或换地址SPI 读出来全是 0xFF模式不对或片选错误尝试四种模式改用正确模式检查 CSSPI 数据错位时钟太快降低 SPI 时钟设成 1MHz 或更低UART 收到乱码波特率不匹配确认双方波特率统一波特率检查时钟源串口回环测试失败TX/RX 未短接或串口被占检查短接和占用短接 TX/RX释放串口提示这张表里的问题我几乎都遇到过特别是 I2C 上拉和 SPI 模式这两个新手很容易卡住。建议把这张表存下来下次遇到问题直接对照排查。6. 从认识到熟练一些个人体会和进阶方向串口这个东西看文档觉得简单实际动手才知道细节多。我刚开始用树莓派的时候觉得 UART 不就是两根线吗结果被控制台占用坑了一整天。后来做项目多了慢慢总结出一个习惯每次接新模块之前先跑一遍pinout和i2cdetect确认引脚状态和总线设备然后再动手接线。这个习惯帮我省了很多返工的时间。另外树莓派 5 出来之后GPIO 的性能和灵活性都有提升但配置方式跟 4B 有些差异。如果你是从 4B 迁移到 5建议重新过一遍串口配置不要直接照搬 4B 的 config.txt。特别是 SPI 和 I2C 的设备节点编号树莓派 5 上可能会变。如果你已经掌握了基本的 UART、SPI、I2C 用法下一步可以试试这几个方向一是用 SPI 驱动 TFT 屏幕这个对时序要求高能帮你深入理解 SPI 的极性和相位二是用 I2C 挂多个传感器练习地址管理和多路复用三是把树莓派作为 SPI 从设备跟 STM32 之类的单片机通信这个场景在项目里很常见但配置起来比主设备模式麻烦一些。还有一个有意思的方向是用树莓派的 UART 跟串口屏通信串口屏的协议通常比较简单适合练手。最后分享一个小技巧如果你在调试 I2C 的时候不确定上拉电阻够不够可以用示波器看 SDA 和 SCL 的上升沿。如果上升沿明显变缓超过 1us说明上拉不够或者总线电容太大。没有示波器的话用逻辑分析仪也行几十块钱的就能用。这个工具在调试 SPI 和 I2C 的时候特别有用能看到实际的时序波形比猜来猜去强多了。