3、数字接口基础:I2C、SPI、UART 协议详解

各位同学,大家好。今天我们来聊聊压力传感器里最常用的三种数字接口:I2C、SPI 和 UART。说实话,我当年刚入行时,总觉得这些协议就是些时序图,背下来就行。后来踩了不少坑才明白——理解协议背后的设计思想,比死记硬背重要得多

这三种接口各有各的脾气。I2C 省引脚,适合板内短距离通信;SPI 速度快,适合大数据量传输;UART 最简单,但需要双方约定好参数。咱们一个一个来拆解。

数字接口基础(第3章) I2C 接口 • 地址:7位/10位 • 读写时序:START/STOP • 时钟拉伸:SCL 拉低 • 应答机制:ACK/NACK • 速率:100k/400k/1M • 开漏输出 + 上拉电阻 SPI 接口 • 4线:SCLK/MOSI/MISO/CS • 4种模式:CPOL+CPHA • 片选管理:低电平有效 • 全双工通信 • 无应答机制 • 速率可达数十MHz UART 接口 • 2线:TX/RX • 波特率:9600/115200等 • 数据位:5/6/7/8 • 校验位:无/奇/偶 • 停止位:1/1.5/2 • 异步通信,需约定参数

3.1 I2C 接口协议详解

I2C 总线,说白了就是两根线干大事。一根时钟线 SCL,一根数据线 SDA。我最早接触 I2C 是在一个温湿度传感器项目上,当时觉得两根线就能挂一堆设备,真神奇。

3.1.1 地址机制

每个 I2C 设备都有一个唯一地址。标准模式是 7 位地址,所以理论上一条总线上最多挂 127 个设备。不过实际中,我建议你别超过 10 个——总线电容会变大,信号质量下降。

7位地址 vs 10位地址
7位地址:0x00 ~ 0x7F(常用)
10位地址:0x000 ~ 0x3FF(扩展模式,需特殊时序)

我记得有一次调试一个压力传感器,怎么都读不到数据。折腾了半天,发现是地址搞错了——传感器默认地址是 0x76,我写成了 0x77。嗯,这种低级错误,谁还没犯过呢?

3.1.2 读写时序

I2C 的读写时序其实很优雅。主机发起 START 条件(SCL 高时 SDA 下降沿),然后发送 7 位地址 + 1 位读写位。从机收到后拉低 SDA 表示应答(ACK)。

// 伪代码:I2C 写一个字节到从机地址 0x76
START();
send_byte(0x76 << 1 | 0);  // 地址 + 写位
wait_ACK();
send_byte(0x01);            // 寄存器地址
wait_ACK();
send_byte(0xAB);            // 要写的数据
wait_ACK();
STOP();

读操作稍微复杂一点。通常需要先写寄存器地址,然后重新发起 START 条件(这叫重复起始条件),再发地址+读位。为什么要这样?因为从机需要知道你要读哪个寄存器。

3.1.3 时钟拉伸

时钟拉伸是 I2C 里一个很有意思的特性。从机可以拉低 SCL 线,告诉主机「我还没准备好,你等等」。主机检测到 SCL 被拉低后,就会等待。

注意: 不是所有 MCU 的 I2C 外设都支持时钟拉伸!我曾在 STM32F103 上遇到过这个问题——从机拉伸时钟时,硬件 I2C 直接卡死了。后来改用软件模拟 I2C 才解决。

我个人习惯是:如果传感器支持时钟拉伸,尽量用软件 I2C 实现。虽然慢一点,但兼容性好。硬件 I2C 虽然快,遇到拉伸就容易出幺蛾子。

3.2 SPI 接口协议详解

SPI 和 I2C 完全是两种性格。I2C 像公交车,大家共用线路;SPI 像专车,主从一对一通信。SPI 需要 4 根线:SCLK(时钟)、MOSI(主出从入)、MISO(主入从出)、CS(片选)。

3.2.1 模式选择

SPI 有 4 种模式,由时钟极性 CPOL 和时钟相位 CPHA 决定。说白了就是:时钟空闲时是高还是低?数据在时钟上升沿还是下降沿采样?

模式CPOLCPHA说明
模式 000空闲低,上升沿采样
模式 101空闲低,下降沿采样
模式 210空闲高,下降沿采样
模式 311空闲高,上升沿采样

我最常用的是模式 0 和模式 3。为什么?因为大部分传感器默认就是这两种。你想想看,如果传感器手册没写清楚模式,你就得用逻辑分析仪去抓波形,自己判断。

小技巧: 不确定 SPI 模式时,先试试模式 0。如果通信不正常,再试模式 3。这两个覆盖了 80% 以上的传感器。

3.2.2 片选管理

片选信号 CS 是 SPI 的灵魂。它低电平有效,意味着你要把 CS 拉低,然后开始通信,结束后再拉高。

这里有个坑:片选信号必须在整个数据帧传输过程中保持低电平。我见过有人每发一个字节就拉高一次 CS,结果传感器根本不认。为什么?因为很多传感器把 CS 的上升沿当作数据帧结束的标志。

3.2.3 数据帧格式

SPI 的数据帧格式很灵活。常见的是 8 位一帧,但也支持 16 位甚至 32 位。我个人习惯用 8 位,因为大多数 MCU 的 SPI 外设默认就是 8 位模式。

// SPI 读取压力传感器数据(模式0,8位)
CS_LOW();
spi_transfer(0xAA);  // 发送读命令
uint8_t high = spi_transfer(0x00);  // 读高字节
uint8_t low  = spi_transfer(0x00);  // 读低字节
CS_HIGH();
uint16_t value = (high << 8) | low;

注意看,SPI 是全双工的——你发数据的同时也在收数据。所以读操作时,主机需要发送 dummy 字节(通常是 0x00 或 0xFF)来产生时钟。

3.3 UART 接口协议详解

UART 是最古老的数字接口之一,但至今仍在广泛使用。它只需要两根线:TX(发送)和 RX(接收)。没有时钟线,所以通信双方必须事先约定好参数。

3.3.1 波特率

波特率就是每秒传输的符号数。常见的有 9600、19200、38400、115200 等。波特率越高,传输越快,但误码率也会上升。

我曾经在一个项目中,把波特率设成了 115200,结果在长距离传输时数据全是乱码。后来降到 9600,问题就解决了。所以我的建议是:能低就别高,除非你确认线路质量很好。

3.3.2 数据位

数据位可以是 5、6、7 或 8 位。现在基本都用 8 位,因为一个字节正好 8 位。7 位数据位主要用于传输 ASCII 字符,现在已经很少见了。

3.3.3 校验位

校验位用于简单的错误检测。有三种选择:无校验、奇校验、偶校验。

  • 无校验: 最简单,没有校验位。适合短距离、低噪声环境。
  • 奇校验: 数据位 + 校验位中 1 的个数为奇数。
  • 偶校验: 数据位 + 校验位中 1 的个数为偶数。
UART 数据帧格式(8N1 为例)
起始位(1) + 数据位(8) + 校验位(0) + 停止位(1) = 10 位
所以波特率 9600 时,实际传输速率是 960 字节/秒

你可能会问:校验位到底有没有用?说实话,在短距离通信中,校验位的作用有限。但如果你要通过长线传输,或者环境干扰大,校验位还是能帮你发现一些错误的。

3.4 三种接口的对比与选择

好了,三种接口都讲完了。最后做个简单对比,方便你选型时参考。

特性I2CSPIUART
引脚数2(SCL+SDA)4(最少3)2(TX+RX)
速度100k~1M可达数十MHz通常 9600~115200
通信方式半双工全双工全双工
多设备支持(地址寻址)支持(片选)不支持(点对点)
距离短(<1m)短(<1m)中(可达数米)
复杂度中等最低

我个人选型的习惯是:

  • 如果传感器数量多、引脚紧张 → 选 I2C
  • 如果要求高速、大数据量 → 选 SPI
  • 如果只是简单传几个字节、距离稍远 → 选 UART

当然,实际项目中还要考虑 MCU 的外设资源、传感器的接口支持等因素。没有绝对的好坏,只有合不合适的方案。

最后说一句: 无论用哪种接口,逻辑分析仪都是你的好帮手。我调试接口问题时,第一件事就是抓波形。波形对了,问题就解决了一半。

公众号:蓝海资料掘金营,微信deep3321