SPI协议基础:从一根线到四根线的进化
大家好,我是你们这门课的老朋友。今天咱们聊聊SPI——这个在嵌入式世界里出镜率极高的串行接口。说实话,我做了十几年嵌入式开发,SPI是我用得最多的协议之一。它不像I2C那么“啰嗦”,也不像UART那么“随意”,SPI给我的感觉就是——干脆、利落、速度快。
你可能会问:为什么需要SPI?想想看,如果你要驱动一个高速ADC,或者刷新一块OLED屏幕,UART那点速度根本不够看。I2C虽然能挂多个设备,但总线仲裁和应答机制拖慢了节奏。这时候,SPI就站出来了。
核心认知:SPI是一种全双工、同步、主从式的串行通信协议。它用四根线解决了“谁说话、什么时候说话、说什么、说多快”这四个通信核心问题。
四线制详解:SCLK、MOSI、MISO、CS
SPI的四根线,每一根都有明确的职责。我刚开始学的时候,总觉得这四根线名字绕口,后来发现——记住它们的英文全称,就再也不会搞混了。
| 信号线 | 全称 | 方向 | 作用 |
|---|---|---|---|
| SCLK | Serial Clock | 主→从 | 时钟信号,由主机产生,决定通信速率 |
| MOSI | Master Out Slave In | 主→从 | 主机发送数据给从机 |
| MISO | Master In Slave Out | 从→主 | 从机发送数据给主机 |
| CS | Chip Select | 主→从 | 片选信号,低电平有效,选中要通信的从机 |
这里有个细节我特别想强调:CS线是SPI多设备通信的关键。我在一个项目里遇到过这样的问题——两个从机共用一组SCLK/MOSI/MISO,但CS线没处理好,结果一个设备在发送数据时,另一个设备也在“偷听”,导致总线冲突。嗯,从那以后,我每次画原理图都会再三确认CS的拉高拉低时序。
个人经验:我习惯在CS线上加一个10kΩ上拉电阻,确保系统上电时从机处于非选中状态。这个习惯帮我避免了好几次“莫名其妙”的通信失败。
SPI的四种工作模式:CPOL和CPHA
SPI的四种模式,说白了就是时钟极性(CPOL)和时钟相位(CPHA)的排列组合。很多初学者在这里栽跟头,我当年也是。
先理解两个概念:
- CPOL:决定SCLK空闲时的电平。CPOL=0,空闲低电平;CPOL=1,空闲高电平。
- CPHA:决定数据在哪个时钟沿采样。CPHA=0,第一个跳变沿采样;CPHA=1,第二个跳变沿采样。
组合起来就是四种模式:
| 模式 | CPOL | CPHA | 空闲时钟电平 | 数据采样沿 |
|---|---|---|---|---|
| 模式0 | 0 | 0 | 低电平 | 上升沿采样 |
| 模式1 | 0 | 1 | 低电平 | 下降沿采样 |
| 模式2 | 1 | 0 | 高电平 | 下降沿采样 |
| 模式3 | 1 | 1 | 高电平 | 上升沿采样 |
你可能会问:这么多模式,到底用哪个?我个人的建议是:先看从机数据手册。大多数SPI外设默认支持模式0或模式3。我做过一个项目,用STM32驱动一个MEMS加速度计,数据手册上写的是“模式3”,我一开始没注意,用了模式0,结果读出来的数据全是乱的。查了两天才发现是模式不匹配。
避坑指南:我曾经在一个量产项目中,因为主控和从机的SPI模式配置不一致,导致部分批次的产品通信不稳定。后来我在驱动初始化代码里加了一段自动检测逻辑——先发一个已知字节,根据回读数据判断当前模式是否正确。这个技巧后来成了我SPI驱动开发的标配。
SPI与I2C/UART的对比
很多初学者会问:这三种协议到底怎么选?我做个表格,一目了然。
| 特性 | SPI | I2C | UART |
|---|---|---|---|
| 线数 | 4线(全双工) | 2线(半双工) | 2线(全双工) |
| 速度 | 最高可达几十MHz | 标准100kHz/400kHz,高速3.4MHz | 通常115200bps~几Mbps |
| 通信方式 | 同步(有时钟线) | 同步(有时钟线) | 异步(无时钟线) |
| 多设备支持 | 每设备一根CS线 | 通过地址区分,最多127个 | 点对点,多设备需额外协议 |
| 硬件复杂度 | 中等 | 低(开漏输出需上拉) | 低 |
| 典型应用 | ADC/DAC、显示屏、SD卡、传感器 | EEPROM、温湿度传感器、RTC | GPS模块、蓝牙模块、调试串口 |
说说我的选择逻辑:
- 要速度?选SPI。 比如驱动一个320×240的LCD,刷新率60fps,用SPI轻松搞定,I2C就吃力了。
- 要省引脚?选I2C。 一个项目里挂了5个传感器,I2C只需要两根线,SPI得6根线起步。
- 要简单?选UART。 和PC通信、调试输出,UART最方便,随便一个串口助手就能看数据。
我的经验法则:如果设备需要高速、连续的数据传输(比如音频、视频、传感器数据流),SPI是首选。如果设备只是偶尔读写几个字节的配置信息(比如EEPROM、RTC),I2C更合适。UART则适合设备间简单、低速的通信,尤其是和PC交互。
SPI通信的核心流程
说了这么多理论,咱们看看SPI通信到底是怎么跑的。我画了一张流程图,帮你理清思路。
这个流程看起来简单,但有几个关键点要注意:
- CS的拉高拉低时机:必须在SCLK稳定之后才能拉低CS,传输完成后也要等最后一个数据位采样完毕再拉高。我见过有人把CS和SCLK的时序搞反,结果数据错位。
- 数据是同时收发的:SPI是全双工,主机发一个字节的同时,从机也在发一个字节。如果你只关心接收数据,也得发送一个“虚拟字节”(通常是0x00或0xFF)。
- 时钟频率不是越高越好:从机有最大时钟限制,PCB走线也有信号完整性约束。我一般先看数据手册,再留20%的余量。
调试技巧:用逻辑分析仪抓SPI波形时,先抓CS和SCLK的时序关系,确认模式配置正确。然后再看MOSI/MISO的数据是否对齐。我习惯在发送第一个字节时发0x55(二进制01010101),这样从波形上能直观看到每一位的变化。
SPI的变体与扩展
标准的四线制SPI已经很强大了,但实际项目中还有一些变体:
- 三线制SPI:把MOSI和MISO合并成一根双向数据线。省了一根线,但牺牲了全双工能力。我一般在引脚紧张的MCU上才用。
- 双线制SPI:只有SCLK和双向数据线,常用于一些简单的传感器。
- 多从机SPI:每个从机独立CS线,或者用菊花链方式串联。菊花链我用的不多,因为一旦中间某个设备出问题,整条链都受影响。
- QSPI/OSPI:四线/八线SPI,数据线从1根扩展到4根或8根,速度翻倍。我在驱动高速NOR Flash时用过QSPI,确实快。
嗯,SPI协议的基础就聊到这里。记住一句话:SPI的核心是“主控节奏,从机响应”。后面的章节我们会深入具体的驱动开发,到时候你会看到这些基础知识是怎么落地的。