对于嵌入式开发来说,SPI 协议(Serial Peripheral Interface,串行外设接口) 是最常用的同步串行通信协议之一,核心用于单片机(如 STM32)与外设(传感器、存储芯片、显示屏等)的高速数据传输,特点是简单、高速、全双工,非常适合嵌入式场景的实践应用。
一、先搞懂:SPI 协议到底是干什么的?
可以把 SPI 理解为「单片机与外设之间的高速 "专线"」:
五、SPI 的优缺点(嵌入式场景选择依据)
优点:
缺点:
六、嵌入式实践:STM32 的 SPI 编程核心步骤(C 语言)
以 STM32F103 为例,用标准库函数实现 SPI 通信的核心步骤(简化版,聚焦实操):
七、常见应用场景
总结
SPI 协议的核心是「同步时钟 + 全双工传输」,记住 3 个关键点:
- 场景:比如 STM32 要读取温湿度传感器数据、控制 OLED 显示屏显示、往 Flash 芯片存数据,都可以用 SPI。
- 核心优势:
- 1、同步通信:有专门的时钟线(CLK)同步数据,传输速度比 I2C 快(通常可达几 MHz~ 几十 MHz);
- 2、全双工:通信时双方可以同时发数据和收数据(比如 STM32 给外设发指令的同时,外设给 STM32 回数据);
- 3、简单可靠:信号线少、协议逻辑简单,不易出错,适合近距离通信(板内或板间短距离)。
二、SPI 的核心组成:4 根信号线
-
SPI 是「主从架构」(一个主机 + 多个从机),主机通常是单片机(如 STM32),从机是外设(传感器、Flash 等),核心信号线共 4 根(部分场景可省略 1 根):
信号线名称 作用(通俗解释) 主机 / 从机方向 SCK(Serial Clock) 时钟线:主机发时钟信号,同步数据传输(相当于 "节拍器") 主机→从机 MOSI(Master Out Slave In) 主机输出 / 从机输入:主机给从机发数据的线 主机→从机 MISO(Master In Slave Out) 主机输入 / 从机输出:从机给主机回数据的线 从机→主机 SS(Slave Select) 从机选择线:主机通过拉低某从机的 SS 线,选中该从机(多从机时必须有) 主机→从机 注意:
- 单从机时,SS 线可直接拉低(不用主机控制),但多从机时必须给每个从机分配独立的 SS 线(比如 STM32 的 PA0、PA1 分别接两个从机的 SS);
- 部分文档会把 SS 叫做 CS(Chip Select),意思完全一样。
三、SPI 的通信过程:像 "同步传球" 一样简单
核心逻辑:时钟线(SCK)控制节奏,MOSI 和 MISO 线同时传数据,步骤如下(以 STM32 为主机、传感器为从机为例):
-
主机选中从机:拉低该从机的 SS 线(告诉 "目标外设":准备和你通信了);
-
主机发时钟信号:通过 SCK 线输出固定频率的时钟脉冲(比如 1MHz);
-
同步传输数据:
- 主机在 SCK 的 "上升沿" 或 "下降沿"(可配置),通过 MOSI 线发送 1 位数据;
- 同时,从机在同一个时钟沿,通过 MISO 线给主机回 1 位数据;
- 时钟脉冲持续 8 次(默认 1 字节 = 8 位),就完成 1 个字节的全双工传输;
-
通信结束:主机拉高 SS 线,释放从机。
-
举个实际例子:STM32 通过 SPI 读取传感器数据
- 主机(STM32)通过 MOSI 发 "读取数据" 指令(比如 0x03);
- 同时,从机(传感器)通过 MISO 回数据(比如温湿度值的二进制);
- 时钟走 8 次,指令发完,数据也收完,一次通信完成。
四、SPI 的关键配置参数
在 STM32 等单片机编程中,需要配置 4 个核心参数(不同外设可能有不同要求,需看 datasheet):
-
时钟极性(CPOL) :SCK 线空闲时的电平
- CPOL=0:空闲时 SCK 为低电平;
- CPOL=1:空闲时 SCK 为高电平。
-
时钟相位(CPHA) :数据采样的时钟沿
- CPHA=0:在 SCK 的 "第一个跳变沿"(上升沿或下降沿,取决于 CPOL)采样数据;
- CPHA=1:在 SCK 的 "第二个跳变沿" 采样数据。
- 组合后有 4 种模式(最常用 Mode 0:CPOL=0,CPHA=0;Mode 3:CPOL=1,CPHA=1)。
-
数据位长度:默认 8 位(1 字节),可配置为 16 位(比如传输 16 位的传感器数据)。
-
时钟频率:主机决定,不能超过从机的最大支持频率(比如传感器最大支持 10MHz,主机就设≤10MHz,常用 1MHz、4MHz)。
-
速度快:比 I2C(通常 100KHz~400KHz)快得多,适合传输大量数据(比如 OLED 屏显示、Flash 存储);
-
协议简单:无起始 / 停止位、无应答机制,编程逻辑简单;
-
全双工:可同时收发数据,效率高。
-
信号线多:比 I2C(仅 2 根线)多 2 根,不适合引脚紧张的场景;
-
无硬件应答:通信错误需要软件处理(比如校验和);
-
仅支持主从架构:不能多主机通信(I2C 支持)。
-
GPIO 初始化:配置 SCK、MOSI、MISO 为复用推挽输出,SS 为通用推挽输出;
c
运行
GPIO_InitTypeDef GPIO_InitStruct; // SCK(PA5)、MOSI(PA7)、MISO(PA6) 复用推挽 GPIO_InitStruct.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7; GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP; // 复用推挽 GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStruct); // SS(PA0) 通用推挽 GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0; GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_Init(GPIOA, &GPIO_InitStruct); GPIO_SetBits(GPIOA, GPIO_Pin_0); // 初始拉高SS线 -
SPI 初始化:配置模式、时钟、数据位等参数;
SPI_InitTypeDef SPI_InitStruct; SPI_InitStruct.SPI_Mode = SPI_Mode_Master; // 主机模式 SPI_InitStruct.SPI_CPOL = SPI_CPOL_Low; // CPOL=0 SPI_InitStruct.SPI_CPHA = SPI_CPHA_1Edge; // CPHA=0(Mode 0) SPI_InitStruct.SPI_NSS = SPI_NSS_Soft; // 软件控制SS线(也可硬件控制) SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_16; // 时钟分频(APB2=72MHz→72/16=4.5MHz) SPI_InitStruct.SPI_DataSize = SPI_DataSize_8b; // 8位数据 SPI_InitStruct.SPI_FirstBit = SPI_FirstBit_MSB; // 高位先传 SPI_Init(SPI1, &SPI_InitStruct); SPI_Cmd(SPI1, ENABLE); // 使能SPI1 -
SPI 收发数据函数(核心:发送 1 字节的同时接收 1 字节);
// SPI发送并接收1字节数据 uint8_t SPI_ReadWriteByte(SPI_TypeDef* SPIx, uint8_t txData) { while (SPI_I2S_GetFlagStatus(SPIx, SPI_I2S_FLAG_TXE) == RESET); // 等待发送缓冲区空 SPI_I2S_SendData(SPIx, txData); // 发送1字节 while (SPI_I2S_GetFlagStatus(SPIx, SPI_I2S_FLAG_RXNE) == RESET); // 等待接收缓冲区非空 return SPI_I2S_ReceiveData(SPIx); // 返回接收的1字节 } -
实际通信示例:读取从机数据;
uint8_t SPI_ReadSensorData(void) { uint8_t rxData; GPIO_ResetBits(GPIOA, GPIO_Pin_0); // 拉低SS,选中从机 SPI_ReadWriteByte(SPI1, 0x03); // 发送"读取指令"0x03(同时接收的字节可忽略) rxData = SPI_ReadWriteByte(SPI1, 0x00); // 发送空数据(占位),接收传感器数据 GPIO_SetBits(GPIOA, GPIO_Pin_0); // 拉高SS,释放从机 return rxData; } -
存储芯片:Flash(如 W25Q64)、EEPROM;
-
传感器:温湿度传感器(如 DHT22 的 SPI 版本)、加速度传感器(如 ADXL345);
-
显示设备:OLED 屏(如 1.3 寸 SPI OLED)、LCD 屏;
-
其他:ADC(模数转换器)、DAC(数模转换器)、无线模块(如 NRF24L01)。
-
4 根信号线:SCK(时钟)、MOSI(主机发)、MISO(主机收)、SS(选从机);
-
通信逻辑:选中从机→时钟同步→收发数据→释放从机;
-
编程关键:配置 SPI 模式(CPOL/CPHA)、时钟频率、SS 线控制。