2. 硬件接口基础:SPI、I2C、QSPI 总线协议详解

做 NVM 存储模块,说白了就是跟 Flash 芯片打交道。而 Flash 芯片怎么跟主控沟通?靠的就是 SPI、I2C、QSPI 这些总线协议。我见过不少工程师,代码写得飞起,但连时序图都读不利索,结果板子调不通,拿着示波器干瞪眼。

这一章,咱们就把这三个协议掰开揉碎。不讲虚的,直接上时序图和实战波形。

2.1 SPI 总线:最经典的存储接口

SPI(Serial Peripheral Interface)是 NVM 模块用得最多的接口。它简单、高速、全双工,非常适合 Flash 这种需要批量数据传输的场景。

2.1.1 四线制与信号定义

SPI 用四根线:

  • SCLK(串行时钟)—— 由主控产生,决定传输速率
  • MOSI(主出从入)—— 主控发数据给从设备
  • MISO(主入从出)—— 从设备发数据给主控
  • CS(片选)—— 低电平有效,选中目标设备

注意,SPI 是全双工的。什么意思?主控发一个字节的同时,从设备也在发一个字节。你想想看,这比 I2C 那种半双工效率高多了。

我的习惯:画原理图时,SPI 信号线尽量等长,尤其是 SCLK 和 MOSI/MISO。虽然低速下影响不大,但到了 50MHz 以上,信号反射会让你头疼。

2.1.2 四种模式(CPOL & CPHA)

SPI 有四种工作模式,由时钟极性(CPOL)和时钟相位(CPHA)决定。很多新手在这里栽跟头。

模式 CPOL CPHA 时钟空闲电平 数据采样边沿
模式 0 0 0 低电平 上升沿
模式 1 0 1 低电平 下降沿
模式 2 1 0 高电平 下降沿
模式 3 1 1 高电平 上升沿

大多数 SPI Flash 默认用模式 0。但别想当然,一定要看数据手册。我曾经被一颗国产 Flash 坑过,手册上写支持模式 0,实际跑起来必须用模式 3 才能稳定通信。查了两天才发现是芯片内部逻辑有 bug。

2.1.3 时序图实战分析

咱们来看一个典型的 SPI Flash 读数据时序。主控先发指令(0x03),再发 3 字节地址,然后从设备连续返回数据。

// SPI Flash 读数据伪代码
CS = 0;                    // 拉低片选
spi_write(0x03);           // 发送读指令
spi_write(addr >> 16);     // 发送地址高字节
spi_write(addr >> 8);      // 发送地址中字节
spi_write(addr);           // 发送地址低字节
data = spi_read();         // 读取第一个字节
data = spi_read();         // 读取第二个字节
// ...
CS = 1;                    // 拉高片选

这里有个关键点:地址发送完后,主控需要继续提供时钟,从设备才会在 MISO 上返回数据。很多初学者以为发完地址就完事了,结果读回来的全是 0xFF。

避坑指南:SPI 读操作时,主控每提供一个时钟周期,从设备就返回一个 bit。所以读 N 个字节,主控必须额外产生 N×8 个时钟。别指望从设备自己会推数据。

2.2 I2C 总线:两线制的优雅与局限

I2C 只有两根线:SCL(时钟)和 SDA(数据)。它用地址来区分设备,一根总线上可以挂几十个芯片。但代价是什么?速度慢,而且协议复杂。

2.2.1 信号与协议基础

I2C 的几个关键概念:

  • 起始条件:SCL 高电平时,SDA 从高变低
  • 停止条件:SCL 高电平时,SDA 从低变高
  • 应答(ACK):每发送 8 位数据后,接收方拉低 SDA 表示收到
  • 非应答(NACK):接收方不拉低 SDA,表示结束或错误

I2C 的地址通常是 7 位,加上读写位组成 8 位。比如一个 EEPROM 的地址是 0x50,读操作就是 0xA1,写操作是 0xA0。

2.2.2 标准速率与模式

模式 最大速率 适用场景
标准模式 100 kbps 低速传感器、小容量 EEPROM
快速模式 400 kbps 大多数 I2C 设备
快速模式+ 1 Mbps 高速 EEPROM、小容量 NVM
高速模式 3.4 Mbps 特殊应用,但兼容性差

说实话,I2C 在 NVM 存储领域用得越来越少。为什么?因为速度太慢。一个 256 字节的页写入,SPI 几微秒就搞定了,I2C 要磨蹭几十微秒。但 I2C 有个不可替代的优势:引脚少。如果你做的是小尺寸产品,比如智能戒指、医疗贴片,I2C 可能是唯一选择。

2.2.3 时序图实战分析

来看一个 I2C EEPROM 的页写入时序:

// I2C EEPROM 页写入伪代码
i2c_start();                // 发送起始条件
i2c_write(0xA0);            // 发送设备地址 + 写位
i2c_wait_ack();             // 等待应答
i2c_write(addr_high);       // 发送地址高字节
i2c_wait_ack();
i2c_write(addr_low);        // 发送地址低字节
i2c_wait_ack();
for (i = 0; i < 16; i++) {  // 发送 16 字节数据
    i2c_write(data[i]);
    i2c_wait_ack();
}
i2c_stop();                 // 发送停止条件

注意,I2C 的应答机制是强制性的。如果从设备没拉低 SDA 表示 ACK,主控必须认为通信失败。我遇到过一种情况:上拉电阻选太大,SDA 信号上升沿太慢,从设备明明发了 ACK,主控却读不到。后来把 10kΩ 换成 4.7kΩ,问题解决。

关键指标:I2C 总线的上拉电阻值取决于总线电容和速率。100 kHz 时用 4.7kΩ~10kΩ,400 kHz 时用 2.2kΩ~4.7kΩ。别偷懒用默认值,算一下再选。

2.3 QSPI 总线:四线并行的性能飞跃

QSPI(Quad SPI)是 SPI 的升级版。它把 MOSI 和 MISO 扩展成四根数据线(IO0~IO3),一个时钟周期能传 4 个 bit。说白了,就是 SPI 的带宽翻了 4 倍。

2.3.1 与标准 SPI 的对比

特性 标准 SPI QSPI
数据线数量 2(MOSI + MISO) 4(IO0~IO3)
每时钟传输 bit 1 4
典型速率 50~100 Mbps 200~400 Mbps
引脚数 4 6(加了两根数据线)
协议复杂度 低 中

QSPI 的厉害之处在于,它可以在单线模式和四线模式之间切换。指令阶段用单线,数据阶段切到四线。这样既兼容 SPI,又能在传输大数据时飙速度。

2.3.2 操作模式详解

QSPI 有三种操作模式:

  • 标准模式:跟普通 SPI 一样,用 MOSI/MISO
  • 双线模式(Dual SPI):用两根数据线,每时钟传 2 bit
  • 四线模式(Quad SPI):用四根数据线,每时钟传 4 bit

实际应用中,读 Flash 时最常用四线模式。比如 W25Q64 这个经典芯片,用四线模式读数据,速度能到 80MB/s 以上。你想想看,这比标准 SPI 快了将近 4 倍。

2.3.3 时序图实战分析

QSPI 的时序比 SPI 复杂一些。以四线快速读(指令 0xEB)为例:

// QSPI 四线快速读时序
CS = 0;
// 阶段 1:发送指令(单线模式)
qspi_write_byte(0xEB);     // 四线快速读指令
// 阶段 2:发送 3 字节地址(单线模式)
qspi_write_byte(addr_high);
qspi_write_byte(addr_mid);
qspi_write_byte(addr_low);
// 阶段 3:发送 1 字节 dummy(单线模式)
qspi_write_byte(0x00);     // 等待内部读取完成
// 阶段 4:切换到四线模式读取数据
qspi_set_quad_mode();      // 将 IO0~IO3 设为双向
for (i = 0; i < 256; i++) {
    data[i] = qspi_read_quad(); // 每时钟读 4 bit
}
CS = 1;

注意那个 dummy 字节。为什么需要它?因为 Flash 内部需要时间把数据从存储阵列搬到缓存。这个等待时间因芯片而异,有的需要 4 个时钟,有的需要 8 个。一定要看数据手册。

我曾经踩过的坑:QSPI 的四根数据线在四线模式下是双向的。如果主控和 Flash 的驱动能力不匹配,或者 PCB 走线不等长,很容易出现数据错位。我建议在四根数据线上各串一个 22Ω 的电阻,能有效抑制过冲和振铃。

2.4 三种协议的选型对比

做项目时怎么选?我一般按这个思路来:

需求场景 推荐协议 理由
引脚受限、容量小 I2C 只需 2 根线,适合小封装
通用存储、中等容量 SPI 简单可靠,生态成熟
高速存储、大容量 QSPI 带宽高,适合代码执行
极端高速、XIP 执行 QSPI + DDR 模式 双倍数据速率,性能极致

我个人习惯是:能用 QSPI 就不用 SPI,能用 SPI 就不用 I2C。除非引脚实在不够,否则别在 I2C 上浪费性能。

2.5 本章知识体系

下面这张图总结了三种协议的核心关系,帮你快速建立知识框架:

SPI、I2C、QSPI 总线协议知识体系 NVM 存储总线协议知识体系 SPI 总线 I2C 总线 QSPI 总线 核心特性 • 全双工通信 • 4 线制(SCLK/MOSI/MISO/CS) • 4 种模式(CPOL/CPHA) • 无地址,靠片选区分 核心特性 • 半双工通信 • 2 线制(SCL/SDA) • 7/10 位地址寻址 • 强制应答机制 核心特性 • 全双工通信 • 6 线制(含 4 根数据线) • 单线/双线/四线模式切换 • 支持 XIP 执行 选型对比速查 指标 SPI I2C QSPI 引脚数 4 2 6 典型速率 50~100 Mbps 0.1~3.4 Mbps 200~400 Mbps 适用场景 通用存储 小容量/低功耗 高速/代码执行

这张图把三种协议的核心特性、选型对比都列出来了。你可以把它当成一个快速参考,做项目时拿出来对照一下,基本不会选错。

2.6 实战波形解读技巧

最后,分享几个看波形的小技巧。这些是我这些年用示波器抓出来的经验:

  1. 先看时钟:SCLK 或 SCL 的频率对不对?占空比是不是 50%?如果时钟歪了,后面都不用看。
  2. 再看片选:CS 有没有在正确的时间拉低和拉高?很多通信失败是因为 CS 毛刺或者没拉低。
  3. 检查建立/保持时间:数据信号在时钟边沿前后有没有稳定?特别是高速 SPI 和 QSPI,这个很容易出问题。
  4. 确认应答:I2C 一定要看到 ACK 位。如果看到 NACK,要么地址错了,要么设备没准备好。
  5. 对比数据:发出去的数据和抓到的数据逐 bit 对比。有时候波形看着对,但数据就是错的,可能是位序搞反了。
我的习惯:调试新板子时,先用 1 MHz 低速跑通通信,再逐步提高频率。这样能快速排除硬件连接问题,避免一上来就被高速信号完整性搞晕。