2. 硬件接口基础:GPIO、UART、I2C、SPI的电气特性与协议基础

做嵌入式控制算法调试,说白了就是跟芯片的“手脚”打交道。GPIO、UART、I2C、SPI 这四个接口,我几乎每天都在用。它们就像工程师的“五官”——看状态、听数据、摸传感器、说指令,全靠它们。

很多新手一上来就写代码,结果板子不工作,查半天发现是电平没对上,或者时序差了几个微秒。嗯,这里咱们先把底层的电气特性和协议基础捋清楚。

2.1 GPIO:最基础的“开关”与“眼睛”

GPIO(通用输入输出)是嵌入式系统里最简单的接口,但也是最容易出坑的地方。我个人习惯,拿到一块新板子,第一件事就是点个灯——确认 GPIO 能正常拉高拉低。

2.1.1 电气特性

  • 输出模式:推挽输出(Push-Pull)和开漏输出(Open-Drain)。推挽输出能主动拉高拉低,驱动能力强;开漏输出只能拉低,高电平要靠外部上拉电阻。
  • 输入模式:浮空输入、上拉输入、下拉输入。浮空输入电平不确定,我建议除非有外部驱动,否则别用。
  • 电压阈值:TTL 电平(5V)和 CMOS 电平(3.3V/1.8V)的阈值不同。比如 3.3V 的 CMOS,高电平阈值是 0.7×VDD ≈ 2.31V,低电平是 0.3×VDD ≈ 0.99V。
我曾经踩过的坑: 用 5V 的单片机直接驱动 3.3V 的传感器,结果把传感器烧了。后来我养成了习惯——先看数据手册的“绝对最大额定值”,再决定要不要加电平转换。

2.1.2 常见应用场景

场景模式注意事项
LED 控制推挽输出限流电阻不能省
按键检测上拉输入软件去抖,硬件加电容更好
I2C 总线开漏输出必须配上拉电阻
中断触发输入+中断边沿触发还是电平触发?看信号质量

2.2 UART:最经典的“串口对话”

UART(通用异步收发器)是我调试时最依赖的接口。没有它,很多问题根本不知道从哪里下手。你想想看,一个 printf 就能把变量值打出来,多方便。

2.2.1 电气特性

  • 电平标准:TTL 电平(0~3.3V/5V)和 RS-232 电平(-12V~+12V)。两者不能直连,需要 MAX232 之类的芯片转换。
  • 波特率:常见的有 9600、115200、921600。波特率误差超过 ±2% 就会丢数据。我遇到过因为晶振不准导致通信失败的案例,后来改用内部 RC 振荡器时都会先测实际频率。
  • 数据格式:起始位(1位)+ 数据位(5~8位)+ 校验位(可选)+ 停止位(1~2位)。
调试技巧: 用逻辑分析仪抓 UART 波形时,先看起始位(从高拉低)是不是干净。如果边沿有毛刺,大概率是电源噪声或者地线没接好。

2.2.2 协议基础

UART 是异步通信,没有时钟线。收发双方靠约定的波特率来同步。发送时,数据从 LSB(最低有效位)开始,依次发送。接收端在起始位下降沿后,按波特率采样。

// 典型的 UART 初始化代码(STM32 HAL 库)
UART_HandleTypeDef huart2;
huart2.Instance = USART2;
huart2.Init.BaudRate = 115200;
huart2.Init.WordLength = UART_WORDLENGTH_8B;
huart2.Init.StopBits = UART_STOPBITS_1;
huart2.Init.Parity = UART_PARITY_NONE;
HAL_UART_Init(&huart2);

2.3 I2C:多设备共享的“两线总线”

I2C(Inter-Integrated Circuit)只有两根线:SCL(时钟)和 SDA(数据)。它支持多主多从,但实际项目中我很少用多主,太容易冲突了。

2.3.1 电气特性

  • 开漏输出:SCL 和 SDA 都是开漏结构,必须外接上拉电阻(通常 4.7kΩ~10kΩ)。
  • 速率模式:标准模式(100kHz)、快速模式(400kHz)、高速模式(3.4MHz)。总线电容会影响速率,线太长或设备太多时,要降低频率。
  • 地址冲突:每个设备有 7 位或 10 位地址。我遇到过两个传感器地址一样,结果总线一直报错。后来我用了地址转换芯片才解决。

2.3.2 协议基础

I2C 的通信流程很清晰:起始条件(SCL 高时 SDA 下降沿)→ 发送从机地址 + 读写位 → 从机应答(ACK)→ 数据字节(每字节后跟 ACK)→ 停止条件(SCL 高时 SDA 上升沿)。

关键点: 如果从机不应答(NACK),常见原因有:地址写错了、从机忙、或者总线被拉死。我一般先用示波器看 SDA 和 SCL 波形,确认有没有正常的起始和停止条件。

2.4 SPI:高速全双工的“四线通信”

SPI(串行外设接口)比 I2C 快得多,而且支持全双工。它需要四根线:SCLK(时钟)、MOSI(主出从入)、MISO(主入从出)、CS(片选)。

2.4.1 电气特性

  • 推挽输出:SCLK、MOSI、CS 都是推挽结构,驱动能力强,速率可以跑到几十 MHz。
  • 电平匹配:主设备和从设备的电平必须一致。我试过 3.3V 的主机接 5V 的从机,结果 MISO 引脚被烧了。后来加了电平转换芯片。
  • 时钟极性(CPOL)和相位(CPHA):有四种模式(Mode 0~3)。主从必须匹配,否则数据会错位。

2.4.2 协议基础

SPI 的通信由主机控制。主机拉低 CS 选中从机,然后产生时钟信号。每个时钟周期,主机通过 MOSI 发送一位,同时从机通过 MISO 返回一位。传输完成后,主机拉高 CS。

SPI 模式CPOLCPHA数据采样边沿
Mode 000上升沿采样
Mode 101下降沿采样
Mode 210下降沿采样
Mode 311上升沿采样
注意: SPI 没有应答机制。如果从机没反应,主机照样发数据,但 MISO 上读到的是垃圾数据。我习惯在通信前先发一个“读 ID”指令,确认从机在线。

2.5 四种接口对比与选型建议

在实际项目中,选哪个接口不是拍脑袋决定的。我一般会从这几个维度考虑:

  • 速度需求:SPI > UART > I2C > GPIO(模拟)。
  • 引脚数量:GPIO(1 根)< UART(2 根)< I2C(2 根)< SPI(4 根)。
  • 设备数量:I2C(多设备,地址区分)> SPI(多设备,片选区分)> UART(点对点)。
  • 抗干扰能力:差分信号(如 RS-485)> 单端信号(如 UART TTL)。
我的经验: 如果只是调试打印,UART 最方便。如果接多个传感器,I2C 省引脚。如果传大量数据(比如摄像头),SPI 是首选。GPIO 嘛,用来做简单的控制或状态指示。

2.6 知识体系总览

下面这张图把四种接口的核心要素串起来了。你可以把它当作调试时的“速查地图”。

嵌入式硬件接口知识体系 GPIO 推挽 / 开漏 / 上拉 UART 异步 / 波特率 / 电平 I2C 开漏 / 地址 / ACK SPI 全双工 / 片选 / 模式 电气特性对比 推挽 / 开漏 TTL / RS-232 开漏 + 上拉 推挽 速率:低速 速率:中速 速率:中速 速率:高速 引脚:1~N 引脚:2 引脚:2 引脚:4 调试建议 先看波形 → 再查电平 → 最后对协议 逻辑分析仪 + 示波器是标配 图:四种硬件接口的核心特性与调试要点

好了,这一章的内容就到这里。记住,硬件接口是嵌入式系统的“骨架”,协议是“血液”。只有把这两样搞清楚了,后面的控制算法调试才能有的放矢。