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 的通信流程:

  1. 主机发送起始条件(SCL 高时,SDA 从高变低)
  2. 主机发送 7 位设备地址 + 1 位读写位
  3. 从机应答(拉低 SDA)
  4. 传输数据(每字节后跟应答位)
  5. 主机发送停止条件(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 知识体系总览

下面这张图,把四种接口的核心要素串起来了。你写驱动前,先对着这张图过一遍,心里就有底了。

四种硬件接口知识体系总览 硬件接口知识体系总览 嵌入式硬件接口 GPIO UART I2C SPI 关键特性 • 推挽/开漏输出 • 上拉/下拉电阻 • 驱动能力有限 关键特性 • 异步,无时钟线 • 波特率需匹配 • 全双工通信 关键特性 • 开漏 + 上拉电阻 • 地址区分设备 • 半双工通信 关键特性 • 全双工,高速 • 片选区分设备 • 4 种模式 驱动适配 = 软件逻辑 × 硬件时序

1.7 选型建议

项目里到底用哪个接口?我一般这么判断:

  • 控制单个外设(LED、按键、继电器)→ GPIO,简单粗暴。
  • 调试输出、低速数据(日志、GPS、蓝牙)→ UART,线少,方便。
  • 挂多个低速传感器(温湿度、加速度计、EEPROM)→ I2C,两根线搞定一堆设备。
  • 高速数据吞吐(ADC 采样、LCD 刷新、Flash 读写)→ SPI,全双工,速率高。

最后说一句:接口选型不只是看速率。还要考虑引脚数量、功耗、抗干扰能力、驱动复杂度。我见过有人为了省一根线,硬用 I2C 传音频数据,结果速率不够,音质一塌糊涂。选型时多想想,别给自己挖坑。