2、硬件接口入门:GPIO、I2C、SPI、UART通信协议原理与区别,树莓派/ESP32引脚图解读
好,咱们今天聊点硬核的。很多朋友刚开始接触嵌入式,看到板子上密密麻麻的引脚就头大。我当年也一样,拿着树莓派和ESP32的引脚图,愣是看了半天没敢下手。
其实没那么玄乎。你想想看,传感器和主控芯片之间怎么说话?靠的就是这些硬件接口。今天我就把GPIO、I2C、SPI、UART这四种最常用的协议,掰开了揉碎了讲清楚。
2.1 GPIO:最基础的“开关”
GPIO,全称是General Purpose Input Output,通用输入输出口。说白了,它就是芯片上的一个引脚,你可以控制它输出高电平(3.3V或5V)或低电平(0V),也可以读取它当前的电平状态。
我在项目中遇到过最典型的场景:用GPIO控制一个LED灯。代码里写pin.write(1),灯就亮了;写pin.write(0),灯就灭了。就这么简单。
核心要点:
- 输出模式:控制引脚输出高/低电平
- 输入模式:读取引脚当前电平(0或1)
- 上拉/下拉:防止引脚悬空时电平不稳定
我的小技巧:GPIO驱动能力有限,一般只能输出几毫安电流。想驱动大功率设备(比如电机),必须加驱动芯片或三极管。我曾经直接拿GPIO推一个继电器,结果芯片烧了……嗯,学费交得有点贵。
2.2 UART:最古老的“串口”
UART,Universal Asynchronous Receiver/Transmitter,通用异步收发器。这是最古老的通信方式之一,但至今仍在大量使用。
它只需要两根线:TX(发送)和RX(接收)。注意,设备A的TX要接设备B的RX,交叉连接。我刚开始做的时候,经常把TX和TX接一起,结果啥反应没有,还以为是代码写错了。
UART的通信参数有四个:波特率、数据位、停止位、校验位。最常见的是9600波特率、8数据位、1停止位、无校验,简写为9600-8-N-1。
避坑指南:我曾经在调试一个GPS模块时,怎么都收不到数据。折腾了两小时,最后发现是波特率设错了。模块默认是115200,我代码里写的是9600。所以,拿到新模块第一件事:看数据手册,确认默认波特率。
2.3 I2C:多设备“总线”
I2C,Inter-Integrated Circuit,集成电路间总线。它最大的特点是:只用两根线(SDA数据线、SCL时钟线),就能挂载多个设备。
每个I2C设备都有一个唯一的地址,比如7位地址范围是0x00到0x7F。主设备通过地址来寻址,想跟谁说话就叫谁。
我特别喜欢I2C的一点:它支持多主设备。不过实际项目中,我基本只用单主设备模式,省心。
I2C通信流程:
- 主设备发送起始信号
- 主设备发送从设备地址+读写位
- 从设备应答(ACK)
- 数据传输(每字节后跟一个ACK)
- 主设备发送停止信号
我的经验:I2C总线上需要上拉电阻,一般4.7kΩ或10kΩ。如果距离较长或设备较多,可以适当减小上拉电阻值。我有个项目挂了8个传感器,上拉电阻从10k换到4.7k才稳定。
2.4 SPI:高速“全双工”
SPI,Serial Peripheral Interface,串行外设接口。它比I2C快得多,而且支持全双工通信(可以同时收发数据)。
SPI需要四根线:
- SCLK:时钟线,由主设备产生
- MOSI:主设备输出,从设备输入
- MISO:主设备输入,从设备输出
- SS/CS:片选线,每个从设备一根
你想想看,SPI的片选线是独立的,所以每增加一个设备,就要多占用一个GPIO。这也是SPI的缺点——设备多了,引脚不够用。
注意:SPI有四种模式(CPOL和CPHA的组合),不同设备可能要求不同模式。我踩过这个坑:一个LCD屏幕死活不显示,后来发现是SPI模式设错了。数据手册上写的是Mode 0,我代码里配的是Mode 3。
2.5 四种协议对比
| 特性 | GPIO | UART | I2C | SPI |
|---|---|---|---|---|
| 引脚数 | 1根 | 2根 | 2根 | 4根+ |
| 通信方式 | 并行 | 异步串行 | 同步串行 | 同步串行 |
| 速度 | 取决于软件 | 慢(最高约1Mbps) | 中(最高约3.4Mbps) | 快(最高几十Mbps) |
| 设备数量 | 1对1 | 1对1 | 多设备(地址区分) | 多设备(片选区分) |
| 典型应用 | 按键、LED | GPS模块、蓝牙 | 温湿度传感器、OLED | SD卡、LCD屏 |
2.6 树莓派和ESP32引脚图解读
好了,理论讲完了,咱们看看实物。树莓派和ESP32的引脚图,我建议你打印出来贴在桌上,调试时随时看。
树莓派(以Pi 4 Model B为例)
树莓派的40针GPIO排针,布局很规整。我个人习惯把板子USB口朝下,这样左上角就是3.3V,右上角是5V。
- 物理引脚1和17:3.3V
- 物理引脚2和4:5V
- 物理引脚6、9、14、20、25、30、34、39:GND
- I2C:物理引脚3(SDA)、5(SCL)
- SPI:物理引脚19(MOSI)、21(MISO)、23(SCLK)、24(CE0)、26(CE1)
- UART:物理引脚8(TX)、10(RX)
注意:树莓派的GPIO编号有两种:物理引脚编号(1-40)和BCM编号(GPIO0-GPIO27)。编程时用BCM编号,但接线时看物理引脚。我刚开始经常搞混,后来养成习惯:代码里写BCM编号,接线时看物理引脚图。
ESP32(以ESP32-DevKitC为例)
ESP32的引脚比树莓派更灵活,很多引脚可以复用。但也正因为灵活,容易踩坑。
- ADC:GPIO32-GPIO39(注意GPIO34-39只能输入,不能输出)
- DAC:GPIO25、GPIO26
- I2C:默认GPIO21(SDA)、GPIO22(SCL),但可以任意指定
- SPI:默认VSPI(GPIO23 MOSI、GPIO19 MISO、GPIO18 SCLK、GPIO5 CS)
- UART:UART0(GPIO1 TX、GPIO3 RX),UART1(GPIO10 TX、GPIO9 RX),UART2(GPIO17 TX、GPIO16 RX)
ESP32特别提醒:
- GPIO6-GPIO11连接了Flash芯片,不要乱用
- GPIO0在启动时拉低会进入下载模式
- GPIO2在启动时拉高才能正常启动
我曾经在GPIO0上接了一个按键,结果每次上电都进入下载模式,折腾了半天才发现问题。
2.7 实战建议:如何选择通信协议?
好了,四种协议都讲完了。你可能会问:那我该用哪个?
我的建议很简单:
- 控制单个设备(LED、继电器):用GPIO
- 简单数据传输(GPS、蓝牙):用UART
- 挂载多个低速传感器(温湿度、气压):用I2C
- 高速数据传输(摄像头、SD卡、LCD):用SPI
嗯,其实没有绝对的好坏。我在一个项目里同时用了I2C和SPI:I2C挂温湿度传感器和气压计,SPI接SD卡存数据。各取所长,才是王道。
下一章,咱们就动手写代码,让树莓派和ESP32真正跑起来。到时候你会觉得,这些协议其实没那么神秘。