文章目录
- 1、概述
- 2、什么是SPI?
-
- [2.1 SPI的特点是什么?](#2.1 SPI的特点是什么?)
- 3、SPI的历史发展
-
- [3.1 SPI诞生](#3.1 SPI诞生)
- [3.2 为什么是SPI?](#3.2 为什么是SPI?)
- [3.3 SPI的一路升级](#3.3 SPI的一路升级)
-
- [3.3.1 标准 SPI (Standard SPI)](#3.3.1 标准 SPI (Standard SPI))
- [3.3.2 Dual SPI & Quad SPI (QSPI)](#3.3.2 Dual SPI & Quad SPI (QSPI))
- [3.3.3 Octal SPI (OSPI) / xSPI](#3.3.3 Octal SPI (OSPI) / xSPI)
- [3.3.4 eSPI (Enhanced SPI)](#3.3.4 eSPI (Enhanced SPI))
- 4、协议架构及通信原理
-
- [4.1 SPI功能层级划分(非官方,但实用便于理解)](#4.1 SPI功能层级划分(非官方,但实用便于理解))
-
- [4.1.1 物理层 :四线制结构](#4.1.1 物理层 :四线制结构)
- [4.1.2 传输层 / 链路控制层](#4.1.2 传输层 / 链路控制层)
- [4.1.3 数据链路层 / 协议层](#4.1.3 数据链路层 / 协议层)
- [4.1.4. 应用层](#4.1.4. 应用层)
- [4.1.5 SPI的通俗结构解密:](#4.1.5 SPI的通俗结构解密:)
- [4.2 SPI通信规则](#4.2 SPI通信规则)
-
- [4.2.1 数据传输机制--交换的"魔术":](#4.2.1 数据传输机制--交换的“魔术”:)
- [4.2.2 关键的"约定"--时钟极性与相位 (CPOL & CPHA)](#4.2.2 关键的“约定”--时钟极性与相位 (CPOL & CPHA))
- [4.2.3 数据传输顺序:](#4.2.3 数据传输顺序:)
- [4.3 具体时序图解析](#4.3 具体时序图解析)
- 5、驱动代码示例
-
- [5.1 抽象层定义 (spi_interface.h)](#5.1 抽象层定义 (spi_interface.h))
- [5.2 设备驱动实现示例 (sensor_driver.c)](#5.2 设备驱动实现示例 (sensor_driver.c))
- [5.3 适配层 (port_stm32.c)](#5.3 适配层 (port_stm32.c))
- 6、总结展望
- 7、激励自己学习的话
- 8、相关参考
1、概述
在嵌入式系统设计的世界里,各种芯片之间的高效沟通是系统流畅运行的基石。作为一名嵌入式工程师,深知选择合适的通信协议至关重要。而SPI (Serial Peripheral Interface,串行外围接口)正是这样一位"高速传声筒",它以其卓越的性能和简洁的特性,成为短距离、高效率芯片间通信的优选方案。

2、什么是SPI?
想象一下,你(主设备)是小组长,要和几个同学(从设备)交换秘密纸条。SPI就是这个小组的交流规则:
- 同步 (Synchronous): 所有的交流都步调一致。你喊"1、2、3!",大家就同时传,节奏统一。这意味着主设备会生成一个共享的时钟信号(SCLK),所有连接的设备都遵循这个时钟节拍,确保数据在精确的时间点传输和接收,避免了异步通信中常见的波特率匹配问题。
- 串行 (Serial): 信息一位一位地传送。一次只传一个字,排队发送。与并行通信相比,这显著减少了所需的物理连接线数量。
- 全双工 (Full-Duplex): 可以同时发送和接收。你给我纸条的同时,我也可以给你纸条,信息交换不耽误。这是SPI的一个显著优势,它极大地提高了通信的吞吐量和效率。
- 主从架构 (Master-Slave Architecture): 你是唯一的发令者,指定和谁交换,什么时候开始/结束。主设备(通常是微控制器)发起并控制整个通信过程,它生成时钟信号并选择特定的从设备进行通信。从设备(如传感器、显示器、存储芯片)则响应主设备的指令。

官方定义:SPI是由摩托罗拉提出的一种同步、全双工、串行、主从式的短距离高速通信总线标准,采用主设备控制时钟和四线制(SCLK, MOSI, MISO, SS)实现设备间数据交换。
2.1 SPI的特点是什么?
SPI之所以如此受欢迎,正是因为它具备多项"超能力":
- 速度快得像闪电: SPI以其高速数据传输能力而闻名,通常可以达到每秒数兆位(Mbps),在普通 MCU 中,标准 SPI 时钟通常为 10--50 MHz;在 QSPI/OSPI 等扩展模式下,结合多数据线与 DDR,可实现等效百 MHz 级吞吐。实现芯片间秒级反应,远超I²C或UART等协议。
- 线路简单: 典型的SPI通信只需要四根线,不搞复杂排场。这简化了硬件设计,降低了布线复杂度和成本。
- 效率超高: 全双工通信允许主从设备在同一时钟周期内同时发送和接收数据,效率倍增,非常适合需要快速数据采集或实时控制的应用。
- 连续数据传输: SPI支持连续的数据流传输,不需要像基于数据包的协议那样引入额外的开销或复杂的握手机制,简化了数据处理。
由于其高吞吐量和硬件实现简单的特性,SPI 已成为嵌入式系统中的事实标准(De facto Standard),广泛应用于微控制器(MCU)与外设(如传感器、Flash 存储、ADC/DAC、显示屏)之间的高速短距离通信。与 I2C 相比,SPI 没有复杂的寻址机制和确认位(ACK),更适合对实时性要求高的数据流传输。
3、SPI的历史发展
每一个伟大的技术背后,都有其特定的历史背景和需求驱动。SPI的诞生,正是为了解决上世纪80年代嵌入式系统发展中遇到的芯片间通信瓶颈。
3.1 SPI诞生
大约在20世纪80年代中期,一个叫摩托罗拉(Motorola)的公司,为了让自家68000系列微控制器(就像是芯片的大脑)能更高效地和各种"小配件"(如EEPROM、传感器、数据转换器)交流,发明了SPI。摩托罗拉M68HC11微控制器(1984年推出)是早期集成SPI硬件支持的芯片典范。
3.2 为什么是SPI?
那时候,芯片间的沟通方式要么慢得像蜗牛,比如早期的并行总线需要大量引脚,或者串行协议如UART速度受限;要么需要一大堆线路(像一堆电话线),增加了硬件复杂度和成本。SPI的出现,就像给芯片们找到了一条"高速专线",用四根线就解决了高速、同步对话的难题。它的全双工特性在当时也是一个重要的创新,允许数据双向并行流动,显著提升了通信效率。
3.3 SPI的一路升级
SPI凭借其简单高效的特点,迅速在电子界"C位出道"。它无需复杂的地址寻址,也没有协议开销,使得其在实现上更加轻量。随着对带宽需求的爆炸式增长(特别是 Flash 存储器和 FPGA 配置),从最初的"单车道"四线标准,发展出"双车道"DSPI、"四车道"QSPI,甚至"八车道"OSPI等超级加速版,简直是通信协议界的"进化狂魔",目的只有一个------让数据传输更快!
3.3.1 标准 SPI (Standard SPI)
带宽:x1 (单线数据)
应用:传感器、低速 ADC、传统 EEPROM。
3.3.2 Dual SPI & Quad SPI (QSPI)
为了在不增加时钟频率的情况下提高吞吐量,业界引入了并行数据线概念。
Quad SPI (QSPI):使用 4 条数据线 (IO0 - IO3) 进行半双工传输。
改进点:在传输地址和数据阶段,4 条线同时工作,理论速度是标准 SPI 的 4 倍。
现状:目前是外置 NOR Flash 的主流接口,STM32、ESP32 等芯片均标配 QSPI 接口。
3.3.3 Octal SPI (OSPI) / xSPI
带宽:x8 (8 条数据线)
DDR (Double Data Rate):在时钟的上升沿和下降沿都传输数据。
性能:8 线 + DDR 技术使得 OSPI 的吞吐量可达标准 SPI 的 16 倍,常用于大容量 PSRAM 和高性能 Flash。
3.3.4 eSPI (Enhanced SPI)
由 Intel 提出,旨在取代老旧的 LPC (Low Pin Count) 总线。
核心变革:eSPI 不仅传输数据,还通过"虚拟线 (Virtual Wires)"通道传输边带信号(如中断、复位、GPIO状态),大幅减少了主板上的布线数量。
架构:支持 1.8V 电平,频率高达 66MHz,包含四个通道:外设通道、虚拟线通道、带外通道 (OOB)、Flash 通道。
4、协议架构及通信原理
SPI的核心特点。与TCP/IP、USB或CAN等复杂协议不同,SPI本身没有一个标准化的、正式的分层架构(如OSI七层模型)。SPI的设计哲学是极简主义和高度灵活性 ,将大部分复杂性留给了硬件实现和软件驱动开发者。我们可以将其理解为一种主要在物理层和链路层交界处运行的"硬协议"。
尽管如此,为了更好地理解和设计SPI系统,我们可以从功能上将其划分为以下几个层级,但这是一种分析性/概念性的划分,而非协议标准的一部分:
不同协议设计的简单对比:
| 协议/总线 | 分层架构 | 特点 |
|---|---|---|
| SPI | 无标准分层 ,可视为 "物理层 + 极简链路控制" | 灵活、高效、简单。协议开销几乎为零,但所有高层功能(寻址、命令、数据含义)需外接定义。 |
| I²C | 有较清晰的链路层(7/10位地址、开始/停止条件、ACK位) | 内置寻址和确认机制,总线管理更复杂。 |
| UART | 有简单的链路层(定义起始位、停止位、奇偶校验位) | 异步点对点,帧结构简单。 |
| USB/CAN | 有完整、严格的分层协议栈(物理层、数据链路层、协议层等) | 复杂、功能强大(支持热插拔、错误恢复、多主机仲裁等),协议开销大。 |
4.1 SPI功能层级划分(非官方,但实用便于理解)
便于理解类比记忆:

4.1.1 物理层 :四线制结构
这是SPI最明确的一层,定义了电气和物理连接。
- 信号线:明确规定了SCLK、MOSI、MISO、SS这四条基础信号线的电气特性(电压、驱动能力)和物理连接方式。
- 时序参数:严格定义了时钟频率、建立时间、保持时间、上升/下降时间等。这些参数决定了SPI的最高工作速度。
- 物理接口:包括引脚定义、PCB布线规则(如长度匹配、阻抗控制)等。
SPI 通常被称为"四线串口",其核心信号线如下:
| 信号名称 | 别名 (常见) | 方向 (主 -> 从) | 描述 |
|---|---|---|---|
| SCLK | SCK, CLK | 输出 | 时钟信号:由主机产生,决定通信速率。 |
| MOSI | SDI, DO | 输出 | 主机输出/从机输入:数据从主机发送到从机。 |
| MISO | SDO, DI | 输入 | 主机输入/从机输出:数据从从机返回主机。 |
| CS/SS | nCS, CS# | 输出 | 片选信号:通常为低电平有效。拉低时激活特定的从机。 |
4.1.2 传输层 / 链路控制层
这一层是SPI的核心,但它非常简单,几乎完全由硬件状态机实现。
- 帧定义:一个最基本的"帧"通常就是一个8位或16位的移位寄存器传输过程。从SS下降沿开始,到SS上升沿结束。
- 时钟模式:定义了CPOL和CPHA,即数据与时钟的相位关系。
- 寻址机制:没有复杂的地址帧。寻址通过唯一的片选信号(SS) 在物理层实现。拉低某个SS线,即选中对应的从设备。
- 流控:无硬件流控。主设备完全控制时钟节奏,从设备必须跟得上。
- 错误检测:无CRC校验、无应答(ACK/NACK)机制。数据传输的可靠性依赖稳定的电源、时钟和短距离布线。
4.1.3 数据链路层 / 协议层
这是SPI缺失的一环,也是其灵活性的来源。 SPI标准在此处戛然而止,由应用开发者或从设备制造商来定义。
- 命令-响应结构:绝大多数SPI设备(如传感器、Flash芯片)都会定义一套自己的寄存器映射和命令集。例如,主设备先发送一个8位的"读寄存器"命令(0x03),再发送一个地址,然后开始接收数据。这套规则是设备特定的,不属于SPI总线本身。
- 数据含义:总线上传输的原始比特流的具体含义(是配置、是温度数据还是像素值)完全由应用层解释。
- 多字节传输:对于超过寄存器长度的传输(如读写Flash的一个扇区),需要定义如何分帧、如何维持SS有效等规则,这些通常也在设备数据手册中说明。
4.1.4. 应用层
这是最终使用数据的软件或固件。它负责:
- 调用底层驱动,发起符合设备特定命令集的SPI传输。
- 解释接收到的原始字节数据,并将其转换为有意义的物理量(如摄氏度、压力值)。
- 处理高级逻辑,如文件系统(对于SPI Flash)、图形显示(对于SPI屏幕)等。
4.1.5 SPI的通俗结构解密:
从工程架构的角度看,SPI的简洁而强大的结构设计是其成功的关键。它将复杂的通信拆解成几个清晰的功能块,通过四根核心信号线,构建了一个高效的"班长与同学"通信模式。
"班长与同学"模式:
SPI通信的核心在于其严格的"主从架构"。
- 主设备 (Master): 永远的"班长",掌控一切。它通常是微控制器(Microcontroller Unit, MCU),负责启动和终止通信,产生唯一的时钟信号(SCLK),并决定和哪个从设备对话。一个SPI总线上只能有一个主设备。
- 从设备 (Slave): 听话的"同学",只有被班长选中后才能响应。从设备可以是各种外设,如传感器、存储器、显示屏等。当其片选线(SS/CS)被主设备激活时,从设备才开始参与数据交换。
四根"线"的秘密 (核心信号线,就像四条不同的高速公路):
为了实现全双工的同步串行通信,SPI协议通常使用以下四根逻辑信号线:
- SCLK (Serial Clock - 序列时钟线): 这是"指挥棒"!主设备挥舞它,通过连续的高低电平交替,为所有设备提供一个统一的节奏,确保数据传输的同步性。时钟频率由主设备控制,可以达到很高的速度(例如,高达100 MHz或更高)。
- MOSI (Master Out Slave In - 主发从收线): 这是"班长的喇叭"!主设备通过这条线向从设备喊话(发送数据)。数据位从主设备串行输出,并被选定的从设备串行输入。
- MISO (Master In Slave Out - 主收从发线): 这是"同学的回音"!从设备通过这条线向主设备回应(发送数据)。数据位从选定的从设备串行输出,并被主设备串行输入。为了避免多个从设备同时向主设备发送数据造成冲突,未被选中的从设备会将其MISO引脚置于高阻态。
- SS/CS (Slave Select / Chip Select - 从机选择线/片选线): 这是"点名器"!主设备用这条线"点名"要和哪个从设备交流。SS/CS通常是低电平有效的信号,意味着当某条SS/CS线被拉低时,对应的从设备就被激活。当有多达四个从设备时,主设备可以使用独立的CS线来选择它们。没点到的(电平拉高)就安静待机。
多个从设备怎么排队?
SPI支持多从设备连接,主要有两种配置方式:
-
独立点名(Independent Slave Selects): 这是最常见的配置方式。就像每个同学都有个专属名字牌,班长想和谁说话就点谁的名字。主设备为每个从设备提供一根独立的SS/CS线。当主设备需要与某个从设备通信时,它会将该从设备的SS/CS线拉低,同时保持其他从设备的SS/CS线为高电平。所有从设备共享SCLK、MOSI和MISO线。这种方式灵活,但随着从设备数量增加,主设备所需的引脚数量也会增加。

-
"击鼓传花"(菊花链 Daisy-Chain Configuration): 所有的从设备手拉手排成一队,班长把数据给第一个,第一个传给第二个,直到最后一个再传回班长。在这种模式下,多个从设备串联连接。主设备的MOSI连接到第一个从设备的输入,第一个从设备的输出连接到第二个从设备的输入,依此类推,直到最后一个从设备的输出连接回主设备的MISO。所有从设备共享一根SCLK线和一根SS/CS线(这根SS/CS线通常用于激活整个链)。这种方式节省了主设备的引脚数量,但数据传输的时延会随着链中从设备数量的增加而增加,因为数据需要依次通过每个从设备。

4.2 SPI通信规则
4.2.1 数据传输机制--交换的"魔术":
SPI 的通信本质是移位寄存器(Shift Register)的交换 。这两个移位寄存器以环形方式连接在一起,一个寄存器的输出到另一个寄存器的输入,反之亦然。主设备控制共同的时钟信号,确保每个寄存器在另一个寄存器移出一个比特时,正好移入一个比特。

简单理解如下:
- "你传纸条,我抄答案": SPI最迷人的地方在于其全双工操作。在每个时钟节拍(SCLK的一次跳动)中,主设备会通过MOSI线发一个数据比特给从设备,同时,选定的从设备也会通过MISO线发一个数据比特给主设备。这种同步、双向的数据流使得通信效率极高,双方数据交换互不影响!
- 内部"传送带": 芯片内部都有"移位寄存器"(Shift Registers),这就像一条传送带。数据比特一个接一个地被移入(从MOSI线)或移出(到MISO线)。主从设备的移位寄存器长度通常相同(例如8位或16位),在一个数据帧内同步完成移位和采样。
4.2.2 关键的"约定"--时钟极性与相位 (CPOL & CPHA)
为了确保主从设备能够正确地"理解"对方的数据,它们在通信前必须约定好"暗号"------即时钟极性(CPOL)和时钟相位(CPHA)。这两个参数共同定义了四种SPI通信模式(Mode 0、1、2、3)。主从设备必须采用相同的模式才能成功通信。
- CPOL (Clock Polarity - 时钟极性): 规定了"休息时钟是站着(高电平)还是坐着(低电平)"。
- CPOL = 0: SCLK在空闲时为低电平(Idle low)。
- CPOL = 1: SCLK在空闲时为高电平(Idle high)。
- CPHA (Clock Phase - 时钟相位): 规定了"数据是在时钟跳动的'开始'瞬间读,还是在'结束'瞬间读"。
- CPHA = 0: 数据在时钟的第一个有效边沿(leading edge)被采样,并在第二个有效边沿(trailing edge)改变。
- CPHA = 1: 数据在时钟的第二个有效边沿(trailing edge)被采样,并在第一个有效边沿(leading edge)改变。
CPOL 和 CPHA 的组合产生了四种不同的 SPI 模式 :
| 模式 | CPOL (时钟极性) | CPHA (时钟相位) | 空闲时时钟电平 | 数据采样边沿 | 数据移位边沿 |
|---|---|---|---|---|---|
| Mode 0 | 0 | 0 | 低电平 | 第一个时钟边沿(上升沿) | 第二个时钟边沿(下降沿) |
| Mode 1 | 0 | 1 | 低电平 | 第二个时钟边沿(下降沿) | 第一个时钟边沿(上升沿) |
| Mode 2 | 1 | 0 | 高电平 | 第一个时钟边沿(下降沿) | 第二个时钟边沿(上升沿) |
| Mode 3 | 1 | 1 | 高电平 | 第二个时钟边沿(上升沿) | 第一个时钟边沿(下降沿) |
4.2.3 数据传输顺序:
虽然不是所有设备都严格遵循,但大部分SPI设备都遵循"高位在前"(MSB first - Most Significant Bit first)的规则,就像我们写数字从左到右一样,即先发送字节的最高位。然而,也有少数设备支持或默认"低位在前"(LSB first)的传输顺序。因此,在集成新设备时,务必查阅其数据手册,确认数据传输顺序,以免出现"鸡同鸭讲"的局面。
4.3 具体时序图解析
一句话总结者张图:"CS下降沿开始对话,SCK节拍控制节奏,MOSI和MISO同时双向传输,CS上升沿结束对话。"

通信阶段分解
-
阶段①:通信开始
CS(片选)线从高电平变为低电平(下降沿)
这相当于"敲门"告诉从设备:"注意,我要和你说话了!"
注意:CS低电平期间,整个SPI通信过程持续进行
-
阶段②:数据传输
SCK(时钟)开始有规律的跳动(方波)
每个时钟周期传输1位数据(1bit)
同步传输:
MOSI:主设备在SCK的某个边沿发送数据
MISO:从设备在SCK的某个边沿返回数据
同时进行!这就是"全双工"的特点
-
阶段③:通信结束
CS线从低电平恢复为高电平(上升沿)
表示:"我说完了,对话结束"
从设备知道传输完成,可以处理接收到的数据
5、驱动代码示例
为了实现硬件无关性(Hardware Abstraction),企业级代码通常使用结构体 + 函数指针的方式来封装 SPI 驱动。以下是一个标准的 C 语言驱动模板:
5.1 抽象层定义 (spi_interface.h)
这是驱动的核心接口,不依赖具体硬件(STM32, NXP, Linux 均可通用)。
c
#include <stdint.h>
#include <stdbool.h>
// 定义标准 SPI 错误码
typedef enum {
SPI_OK = 0,
SPI_ERROR_TIMEOUT,
SPI_ERROR_BUSY
} spi_status_t;
// 定义 SPI 配置结构体
typedef struct {
uint32_t speed_hz;
uint8_t cpol; // 0 or 1
uint8_t cpha; // 0 or 1
bool msb_first;
} spi_config_t;
// 定义 SPI 驱动接口 (V-Table 思想)
typedef struct {
// 初始化 SPI 硬件
spi_status_t (*init)(spi_config_t *config);
// 控制片选引脚 (true=拉低选中, false=拉高释放)
void (*cs_control)(bool select);
// 核心传输函数:全双工交换数据
// tx_data: 发送缓冲区 (可为 NULL, 发送 0xFF)
// rx_data: 接收缓冲区 (可为 NULL, 丢弃数据)
// len: 数据长度
spi_status_t (*transfer)(const uint8_t *tx_data, uint8_t *rx_data, uint16_t len);
} spi_driver_t;
5.2 设备驱动实现示例 (sensor_driver.c)
上层业务逻辑只需调用 spi_driver_t 中的函数,无需关心底层寄存器。
c
// 假设这是某个温度传感器的驱动
typedef struct {
spi_driver_t *spi; // 持有底层 SPI 接口的指针
} temp_sensor_t;
// 读取传感器寄存器
uint8_t sensor_read_reg(temp_sensor_t *dev, uint8_t reg_addr) {
uint8_t tx_buf[2];
uint8_t rx_buf[2];
// 准备数据:第一字节是地址,第二字节是空字节(用于产生时钟)
tx_buf[0] = reg_addr | 0x80; // 假设最高位 1 代表读
tx_buf[1] = 0x00; // Dummy byte
// 1. 选中芯片
dev->spi->cs_control(true);
// 2. 交换数据
dev->spi->transfer(tx_buf, rx_buf, 2);
// 3. 释放芯片
dev->spi->cs_control(false);
// 返回第二个字节(有效数据)
return rx_buf[1];
}
5.3 适配层 (port_stm32.c)
最后,将具体的硬件操作"绑定"到接口上。
c
// 具体的硬件发送函数 (STM32 HAL 风格)
static spi_status_t stm32_spi_transfer(const uint8_t *tx, uint8_t *rx, uint16_t len) {
// 调用厂商 HAL 库
if (HAL_SPI_TransmitReceive(&hspi1, (uint8_t*)tx, rx, len, 1000) != HAL_OK) {
return SPI_ERROR_TIMEOUT;
}
return SPI_OK;
}
static void stm32_cs_control(bool select) {
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, select ? GPIO_PIN_RESET : GPIO_PIN_SET);
}
// 构造驱动实例
spi_driver_t my_spi_driver = {
.init = stm32_spi_init,
.cs_control = stm32_cs_control,
.transfer = stm32_spi_transfer
};
6、总结展望
SPI 协议以其极简的架构统治了嵌入式外设通信四十年。虽然缺乏统一的标准(如无 ACK 机制、无统一寄存器定义)是其劣势,但在追求极致速度和低延迟的场景下,SPI 依然是首选。
SPI的低延迟和高效率特性使其在物联网(IoT)、人工智能(AI)、边缘计算(Edge Computing)以及机器人技术等新兴领域中扮演关键角色。它将继续成为传感器数据采集、无线模块通信、AI加速器与主控芯片交互,以及机器人实时控制的"基石"通信协议。例如,在边缘AI设备中,传感器收集的数据需要通过高速SPI快速传输到本地AI处理器进行实时推理。SPI的持续演进将确保它能够无缝融入这些创新场景,推动智能世界的构建。
7、激励自己学习的话
真正拉开工程师差距的,不是用过多少接口,而是是否理解每一次时钟跳变背后的原理。