1. 硬件接口基础:GPIO、UART、I2C、SPI 协议原理与电气特性对比
做嵌入式驱动开发,说白了就是跟芯片的脚打交道。你写的每一行代码,最终都要落到这些物理引脚上。我刚开始带团队时,发现很多新人上来就调驱动,连接口的电气特性都没搞清楚,结果板子一跑就出各种诡异问题——波形不对、通信超时、甚至烧引脚。
所以这第一章,咱们先把四个最常用的硬件接口掰开揉碎讲清楚。GPIO、UART、I2C、SPI,你八成都用过,但真的吃透了吗?
核心观点:驱动适配的本质,是让软件逻辑匹配硬件时序。不懂接口的电气特性,写出来的驱动就是空中楼阁。
1.1 GPIO — 最基础,也最容易翻车
GPIO(General Purpose Input/Output),通用输入输出口。每个嵌入式工程师的入门第一课。但说实话,越简单的东西越容易出幺蛾子。
GPIO 的核心就三个状态:高电平、低电平、高阻。但实际项目中,你还要考虑:
- 推挽输出 vs 开漏输出:推挽能主动拉高拉低,开漏只能拉低,高电平靠外部上拉。我遇到过有人用开漏去驱动 LED,结果灯死活不亮——忘了加上拉电阻。
- 上拉/下拉电阻:芯片内部通常有可配置的弱上拉/下拉(几十 kΩ),但外部干扰强时,你得自己加更小的电阻。
- 驱动能力:一般 GPIO 只能输出几 mA 到 20mA。直接驱动继电器?等着烧引脚吧。
我的习惯:做 GPIO 驱动时,我会在初始化函数里显式配置引脚模式、速度、上下拉,绝不依赖默认值。不同芯片的默认状态可能不一样,依赖默认值就是埋雷。
1.2 UART — 异步通信的老大哥
UART(Universal Asynchronous Receiver/Transmitter),通用异步收发器。两根线:TX 发,RX 收。没有时钟线,靠双方约定好的波特率来同步。
UART 的帧结构很简单:
- 起始位(1 bit,低电平)
- 数据位(5~9 bit,常用 8 bit)
- 校验位(可选,奇/偶校验)
- 停止位(1 或 2 bit,高电平)
波特率是 UART 的灵魂。两边必须一致,误差一般不能超过 2%~3%。我调试过一个设备,波特率设成 115200,但晶振精度不够,实际跑出来 114800,偶尔丢字节。查了两天才发现是时钟源的问题。
避坑指南:我曾经在量产项目里遇到 UART 通信偶尔乱码,最后发现是地线没接好。UART 是单端信号,共模干扰全靠地线扛。TX/RX 走线太长时,记得加 TVS 管保护。
1.3 I2C — 两线制,多设备,靠地址
I2C(Inter-Integrated Circuit),两根线:SCL(时钟)和 SDA(数据)。都是开漏输出,需要外部上拉电阻。一个总线上可以挂多个设备,每个设备有唯一的 7 位或 10 位地址。
I2C 的通信流程:
- 主机发送起始条件(SCL 高时,SDA 从高变低)
- 主机发送 7 位设备地址 + 1 位读写位
- 从机应答(拉低 SDA)
- 传输数据(每字节后跟应答位)
- 主机发送停止条件(SCL 高时,SDA 从低变高)
I2C 的速度模式:标准模式 100kHz,快速模式 400kHz,高速模式 3.4MHz。但实际能跑多快,取决于总线电容和上拉电阻。总线越长、设备越多,上拉电阻就得越小。
我的经验:I2C 调试时,示波器看 SCL 和 SDA 的上升沿。如果上升沿太缓(像正弦波),说明上拉电阻太大或总线电容太大。我一般从 4.7kΩ 开始试,400kHz 下通常够用。
1.4 SPI — 全双工,速度快,但线多
SPI(Serial Peripheral Interface),串行外设接口。四根线:SCLK(时钟)、MOSI(主出从入)、MISO(主入从出)、CS(片选,低电平有效)。
SPI 是同步通信,主机产生时钟,从机被动接收。全双工——每个时钟周期,主机和从机同时发送和接收一个 bit。
SPI 有四种模式,由时钟极性(CPOL)和时钟相位(CPHA)决定:
| 模式 | CPOL | CPHA | 数据采样边沿 |
|---|---|---|---|
| 模式 0 | 0 | 0 | 上升沿采样 |
| 模式 1 | 0 | 1 | 下降沿采样 |
| 模式 2 | 1 | 0 | 下降沿采样 |
| 模式 3 | 1 | 1 | 上升沿采样 |
模式不匹配,数据全是错的。我见过有人 SPI 驱动调了一天,最后发现从机手册写的是模式 0,但实际要模式 3——芯片勘误表里才提到。
注意:SPI 没有标准的应答机制。主机发完数据,从机有没有收到,主机不知道。所以很多 SPI 设备会在协议层加 CRC 或 ACK 字节。写驱动时别忘了处理超时和重试。
1.5 四种接口的电气特性对比
下面这张表,我建议你收藏。选型时拿出来看一眼,能省很多事。
| 特性 | GPIO | UART | I2C | SPI |
|---|---|---|---|---|
| 信号线数 | 1 根/引脚 | 2 根(TX/RX) | 2 根(SCL/SDA) | 4 根(SCLK/MOSI/MISO/CS) |
| 通信方式 | 并行(位操作) | 异步串行 | 同步串行 | 同步串行 |
| 传输方向 | 单向(可配置) | 全双工 | 半双工 | 全双工 |
| 最大速率 | 取决于 GPIO 翻转速度 | 典型 115200 bps ~ 几 Mbps | 100kHz / 400kHz / 3.4MHz | 几十 MHz |
| 传输距离 | 板级(cm) | 可达数米(RS232 电平) | 板级(<1m) | 板级(<1m) |
| 多设备支持 | 每个引脚一个设备 | 点对点(多设备需协议) | 多设备(地址区分) | 多设备(片选区分) |
| 硬件复杂度 | 极低 | 低 | 中(需上拉电阻) | 中 |
| 典型应用 | LED、按键、中断 | 调试串口、GPS、蓝牙 | 传感器、EEPROM、RTC | ADC、DAC、LCD、Flash |
1.6 知识体系总览
下面这张图,把四种接口的核心要素串起来了。你写驱动前,先对着这张图过一遍,心里就有底了。
1.7 选型建议
项目里到底用哪个接口?我一般这么判断:
- 控制单个外设(LED、按键、继电器)→ GPIO,简单粗暴。
- 调试输出、低速数据(日志、GPS、蓝牙)→ UART,线少,方便。
- 挂多个低速传感器(温湿度、加速度计、EEPROM)→ I2C,两根线搞定一堆设备。
- 高速数据吞吐(ADC 采样、LCD 刷新、Flash 读写)→ SPI,全双工,速率高。
最后说一句:接口选型不只是看速率。还要考虑引脚数量、功耗、抗干扰能力、驱动复杂度。我见过有人为了省一根线,硬用 I2C 传音频数据,结果速率不够,音质一塌糊涂。选型时多想想,别给自己挖坑。