1. SPI协议基础:从四线制到百万级传输
第一次接触SPI是在2013年做智能家居网关项目时,当时需要同时控制多个传感器模块。面对I2C地址冲突的困扰,老工程师甩给我一句"用SPI吧,一根片选线解决一个设备"。这个看似简单的四线制协议(SCK、MOSI、MISO、SS),后来成了我嵌入式开发生涯中使用最频繁的通讯方式。
SPI全称Serial Peripheral Interface,本质是移位寄存器的硬件实现。想象两个面对面的水桶(主从设备),通过两根水管(MOSI和MISO)连接。每次时钟脉冲(SCK)就像同时按压两个活塞,主设备的水(数据)流向从设备的同时,从设备的水也反向流动。这种全双工特性让SPI在AD采样、Flash读写等场景优势明显,我实测STM32H7系列的SPI3接口能达到108MHz时钟频率,传输8MB文件仅需0.6秒。
硬件连接有个经典陷阱:所有设备的SCK、MOSI、MISO必须并联,但片选线必须独立。曾有个血泪教训——把加速度计的SS线误接到光照传感器,导致两个设备同时响应造成数据错乱。正确的接法应该是:
主机GPIO1 → 设备A SS 主机GPIO2 → 设备B SS 主机SCK → 所有设备SCK 主机MOSI → 所有设备MOSI 主机MISO ← 所有设备MISO2. SPI的四种模式与时序玄机
很多新手会困惑:为什么我的SPI设备偶尔能通信,偶尔又失败?这通常与模式配置有关。SPI有四种工作模式,本质是**时钟极性(CPOL)和时钟相位(CPHA)**的组合:
| 模式 | CPOL | CPHA | 时钟空闲状态 | 数据采样时刻 |
|---|---|---|---|---|
| 0 | 0 | 0 | 低电平 | 第一个边沿(上升沿) |
| 1 | 0 | 1 | 低电平 | 第二个边沿(下降沿) |
| 2 | 1 | 0 | 高电平 | 第一个边沿(下降沿) |
| 3 | 1 | 1 | 高电平 | 第二个边沿(上升沿) |
以常见的NOR Flash为例,多数采用模式0或模式3。有次调试Winbond W25Q128时,发现读取的ID总是0xFF,最终发现是CPHA配置错误。示波器是最直接的调试工具:抓取SCK和MOSI/MISO波形时,要特别注意:
- SS下降沿标志传输开始
- 数据在有效边沿保持稳定
- SS上升沿前完成最后一位传输
用Verilog实现SPI主机时,状态机设计是关键。建议将时钟边沿检测和数据移位分成两个always块,比如:
// 时钟边沿检测 always @(posedge clk) begin prev_sck <= sck; rising_edge <= (sck==1 && prev_sck==0); falling_edge <= (sck==0 && prev_sck==1); end // 数据移位(模式0为例) always @(posedge clk) begin if(rising_edge) begin mosi <= tx_data[31]; tx_data <= {tx_data[30:0], 1'b0}; end if(falling_edge) begin rx_data <= {rx_data[30:0], miso}; end end3. QSPI:四车道的高速公路
当项目需要频繁读写大容量Flash时,传统SPI的瓶颈就出现了。记得在开发智能摄像头固件时,用标准SPI读取1MB图像数据需要近100ms,改用QSPI后直接缩短到22ms。QSPI的精髓在于将单条数据线扩展为四条并行通道(IO0-IO3),就像单车道变四车道:
- 标准SPI:1时钟周期传输1bit
- Dual SPI:1时钟周期传输2bit
- Quad SPI:1时钟周期传输4bit
但要注意信号完整性的挑战。在PCB布局时,四条数据线必须等长走线,我通常控制在±50ps的时序偏差内。某次六层板设计中,因IO2线比其它长3mm,导致在80MHz时钟下出现误码,后来通过调整走线层解决了问题。
QSPI有三大工作模式:
- 间接模式:通过寄存器操作(适合初始化配置)
- 状态轮询模式:检查状态寄存器位(适合擦除/编程等待)
- 内存映射模式:直接映射到CPU地址空间(最快速)
以MX25L25635E Flash为例,切换到QSPI模式需要发送35h命令:
// 发送命令序列 uint8_t cmd[2] = {0x35, 0x00}; HAL_QSPI_Command(&hqspi, QSPI_INSTRUCTION_1_LINE, cmd, 2); // 启用四线模式 MODIFY_REG(QUADSPI->CR, QUADSPI_CR_FMODE, QSPI_FMODE_INDIRECT_WRITE);4. 实战:SRAM的SPI与QSPI性能对决
去年设计高速数据采集系统时,需要对VTI7064 SRAM进行性能对比测试。这个案例很典型——同一器件支持两种协议,实测数据如下:
| 指标 | SPI模式(1线) | QSPI模式(4线) |
|---|---|---|
| 写入1KB数据 | 820μs | 210μs |
| 读取1KB数据 | 780μs | 195μs |
| 信号线数量 | 4根 | 6根 |
| 功耗(@50MHz) | 12mA | 18mA |
QSPI的地址周期需要特别注意。常规SPI发送24位地址要3个字节,而QSPI可以压缩:
// SPI地址发送 0x03 // 读命令 0x00 0x30 0x00 // 24位地址 // QSPI地址发送(仅需2周期) 0xEB // 四线读命令 0x003000 // 压缩后的地址在STM32CubeIDE中配置QSPI外设时,这几个参数最易出错:
- Sample shifting:对于长走线建议设为1/2周期
- Flash size:必须与实际芯片容量一致
- Chip select high time:VTI7064要求至少4个时钟周期
遇到CRC校验失败时,先检查DLL(延迟锁相环)配置。有次在-40℃低温测试时出现随机错误,最终发现是未启用动态相位补偿:
// 启用自动校准 QUADSPI->DCR |= QUADSPI_DCR_CKMODE; QUADSPI->CR |= QUADSPI_CR_EN;经过多个项目验证,我的协议选型经验是:低速设备用SPI(<10MHz),大数据量必选QSPI。但要注意,QSPI的布线复杂度更高,在空间受限的双面板上,有时宁愿牺牲速度也要保证可靠性。