STM32 SPI 通信详解:主从模式、时序与 Flash 读写实战
👁 3 阅读 · 2026-08-14 · STM32 SPI
SPI 是嵌入式系统中常用的高速同步串行接口。本文将深入解析 SPI 协议的主从模式与四种时序配置,结合 STM32 HAL 库给出完整配置流程,并以 W25Q64 Flash 为例演示如何通过 SPI 进行设备 ID 读取、数据写入与读取,帮助开发者快速掌握 STM32 SPI 通信的实战技能。
## 一、SPI 协议基础与主从模式
SPI(Serial Peripheral Interface)由 Motorola 提出,是一种高速全双工同步串行总线。它通常使用 4 根信号线:
- **SCK**:时钟线,由主设备产生,决定通信速率与同步节奏。
- **MOSI**:主输出从输入(Master Out Slave In)。
- **MISO**:主输入从输出(Master In Slave Out)。
- **CS**:片选信号,低电平有效,由主设备控制选择从设备。
SPI 采用**主从模式**:一个主设备可以挂多个从设备,但同一时刻只能有一个从设备被选中(通过 CS 引脚)。主设备始终控制 SCK 和 CS,完成数据的发送与接收。
**全双工通信**意味着主设备发送数据的同时也能接收数据。SPI 没有定义应用层协议,因此读写具体外设(如 Flash、传感器)时必须遵循外设的数据手册时序。
## 二、SPI 时序:极性(CPOL)与相位(CPHA)
SPI 的四种工作模式由**时钟极性 CPOL** 和**时钟相位 CPHA** 决定:
- **CPOL**:决定 SCK 空闲时的电平。CPOL=0 时空闲为低电平,CPOL=1 时空闲为高电平。
- **CPHA**:决定数据采样边沿。CPHA=0 时在第一个时钟边沿采样(数据更新在边沿前),CPHA=1 时在第二个边沿采样。
两种参数组合成 Mode 0~Mode 3。
| CPOL | CPHA | 模式 | 采样边沿 | 常见应用 |
|------|------|------|----------|----------|
| 0 | 0 | Mode 0 | 上升沿 | W25Q64 等 Flash |
| 0 | 1 | Mode 1 | 下降沿 | - |
| 1 | 0 | Mode 2 | 下降沿 | - |
| 1 | 1 | Mode 3 | 上升沿 | 部分传感器 |
> 注意:主从设备的 CPOL/CPHA 必须一致,否则通信数据会错位。
## 三、STM32 SPI 配置步骤
以 STM32F1 系列 + HAL 库为例,配置 SPI 的典型步骤:
1. **使能 GPIO 和 SPI 时钟**。
2. **配置 GPIO 复用功能**,将 SCK、MOSI、MISO 映射到 SPI 引脚,CS 通常用普通 GPIO 控制。
3. **配置 SPI 句柄**:设置波特率、时钟极性/相位、数据格式等。
4. **调用 HAL_SPI_Init() 初始化**。
5. **编写读写函数**,使用 HAL_SPI_Transmit/Receive 进行数据传输。
使用 STM32CubeMX 可快速完成时钟和引脚配置,但底层原理仍需理解。以下代码展示 HAL 库初始化:
```c
SPI_HandleTypeDef hspi1;
void SPI_Init(void)
{
hspi1.Instance = SPI1;
hspi1.Init.Mode = SPI_MODE_MASTER; // 主模式
hspi1.Init.Direction = SPI_DIRECTION_2LINES; // 全双工
hspi1.Init.DataSize = SPI_DATASIZE_8BIT; // 8 位数据
hspi1.Init.CLKPolarity = SPI_POLARITY_LOW; // CPOL=0
hspi1.Init.CLKPhase = SPI_PHASE_1EDGE; // CPHA=0,即 Mode 0
hspi1.Init.NSS = SPI_NSS_SOFT; // 软件管理片选
hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_16; // 分频
hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB; // 高位先行
HAL_SPI_Init(&hspi1);
}
```
CS 引脚控制建议使用普通 GPIO,例如:
```c
#define FLASH_CS_LOW() HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET)
#define FLASH_CS_HIGH() HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET)
```
## 四、读写 Flash 实战(以 W25Q64 为例)
W25Q64 是一款常见的 SPI NOR Flash,容量 64Mbit(8MB),支持标准 SPI 指令。对 Flash 的操作通常先发送指令地址,再读写数据。常用指令:
- 读 ID:`0x90`(读取制造商/设备 ID)
- 写使能:`0x06`
- 读数据:`0x03`
- 页编程:`0x02`(一页最多 256 字节)
- 擦除扇区:`0x20`(一次擦除 4KB)
下面给出基于 HAL 库的完整示例代码。
```c
/* 发送一个字节同时接收一个字节 */
uint8_t SPI_ExchangeByte(uint8_t txData)
{
uint8_t rxData = 0;
HAL_SPI_TransmitReceive(&hspi1, &txData, &rxData, 1, HAL_MAX_DELAY);
return rxData;
}
/* 读取 Flash ID */
uint16_t Flash_ReadID(void)
{
uint8_t cmd[4] = {0x90, 0x00, 0x00, 0x00};
uint8_t id[2] = {0};
FLASH_CS_LOW();
HAL_SPI_Transmit(&hspi1, cmd, 4, HAL_MAX_DELAY);
HAL_SPI_Receive(&hspi1, id, 2, HAL_MAX_DELAY);
FLASH_CS_HIGH();
return (id[0] << 8) | id[1]; // W25Q64 返回 0xEF17
}
/* 写使能 */
void Flash_WriteEnable(void)
{
uint8_t cmd = 0x06;
FLASH_CS_LOW();
HAL_SPI_Transmit(&hspi1, &cmd, 1, HAL_MAX_DELAY);
FLASH_CS_HIGH();
}
/* 读取指定地址数据 */
void Flash_ReadData(uint32_t addr, uint8_t *buf, uint32_t len)
{
uint8_t cmd[4] = {0x03, (addr >> 16) & 0xFF, (addr >> 8) & 0xFF, addr & 0xFF};
FLASH_CS_LOW();
HAL_SPI_Transmit(&hspi1, cmd, 4, HAL_MAX_DELAY);
HAL_SPI_Receive(&hspi1, buf, len, HAL_MAX_DELAY);
FLASH_CS_HIGH();
}
/* 页编程(写一页) */
void Flash_PageProgram(uint32_t addr, uint8_t *data, uint16_t len)
{
uint8_t cmd[4] = {0x02, (addr >> 16) & 0xFF, (addr >> 8) & 0xFF, addr & 0xFF};
Flash_WriteEnable();
FLASH_CS_LOW();
HAL_SPI_Transmit(&hspi1, cmd, 4, HAL_MAX_DELAY);
HAL_SPI_Transmit(&hspi1, data, len, HAL_MAX_DELAY);
FLASH_CS_HIGH();
// 可等待 Flash 内部完成(读取状态寄存器)
}
```
使用时注意:Flash 写前必须先擦除,且页编程不能跨页。实际应用中还需添加状态寄存器的轮询等待,以确保操作完成。
## 五、注意事项
- **时钟极性和相位必须匹配**:错误的 CPOL/CPHA 会导致数据错位,调试时可用逻辑分析仪观察。
- **片选时序**:多数 Flash 要求 CS 在指令发送期间保持低电平,完成后拉高,不能随意翻转。
- **波特率匹配**:SPI 频率不要超过外设上限(W25Q64 最快可达 150MHz,但 STM32 时钟要配置合理)。
- **DMA 传输**:大数据读写建议使用 HAL_SPI_TransmitReceive_DMA 提高效率,注意 DMA 与 CPU 的同步。
- **引脚复用**:使用 STM32CubeMX 时,确保 GPIO 复用为 SPI 功能,否则数据发不出去。
- **Flash 扇区擦除**:W25Q64 需先擦后写,且擦除等待时间较长,务必加延时或状态轮询。
掌握 SPI 主从模式与时序后,无论是连接 Flash、SD 卡还是其他传感器,都能得心应手。建议在实际项目中根据硬件手册调整 CPOL/CPHA,并用示波器验证时序波形。