一、SPI协议概述
SPI由Motorola于1980年代推出,采用全双工同步通信模式,具有以下核心特征:
- 高速传输:可达100MHz时钟频率
- 主从架构:支持一主多从拓扑
- 硬件连接:4线制基础配置(SCK/MOSI/MISO/SS)
关于具体的SPI,I2C,UART的特点与比较在之前的博客中都有介绍,这里不再过多阐述。
二、工作原理
2.1 物理层接口
信号线 | 全称 | 方向 | 作用描述 |
SCK | Serial Clock | 主→从 | 同步时钟信号 |
MOSI | Master Out Slave In | 主→从 | 主机数据输出 |
MISO | Master In Slave Out | 从→主 | 从机数据输出 |
SS/CS | Slave Select | 主→从 | 从机片选信号(低电平有效) |
根据英文很容易记住每个信号线的作用
典型连接拓扑:
主机 从机1 从机2
SCK --------- SCK --------- SCK
MOSI --------- SDI --------- SDI
MISO --------- SDO --------- SDO
SS1 ---------- CS |
SS2 ------------------------ CS
2.2 数据传输机制
1. 时钟同步:主机生成SCK信号,每个时钟周期传输1bit数据
2. 双工传输:MOSI与MISO同时工作,实现双向通信
3. 数据移位:采用移位寄存器实现串并转换
- 主机寄存器:8位数据通过MOSI移出
- 从机寄存器:同时通过MISO移入数据
2.3 时钟模式配置
SPI通信通过CPOL(时钟极性)与CPHA(时钟相位)组合形成四种工作模式,它们共同决定了 SPI 通信的时序,确保主设备和从设备能够正确地同步和交换数据。
首先了解时钟极性和时钟相位两个概念:
时钟极性 (CPOL - Clock Polarity)
定义: CPOL 决定了 SPI 时钟线 (SCK) 在空闲状态时的电平。 换句话说,当没有数据传输时,SCK 应该保持什么电平。
CPOL = 0: SCK 在空闲时保持低电平。
CPOL = 1: SCK 在空闲时保持高电平。
时钟相位 (CPHA - Clock Phase)
定义: CPHA 决定了在 SPI 时钟周期的哪个边沿 (上升沿或下降沿) 采样 (读取) 数据,以及在哪个边沿改变 (输出) 数据。
CPHA = 0:
- 在 SCK 的第一个边沿 (取决于 CPOL,如果是上升沿或下降沿) 采样 MISO (主设备输入,从设备输出) 和 MOSI (主设备输出,从设备输入) 上的数据。
- 在 SCK 的第二个边沿 改变 MOSI 和 MISO 上的数据。 这意味着数据在时钟边沿发生变化,并在下一个时钟边沿被采样。
CPHA = 1:
- 在 SCK 的第二个边沿 采样 MISO 和 MOSI 上的数据。
- 在 SCK 的第一个边沿 改变 MOSI 和 MISO 上的数据。 这意味着数据在时钟边沿发生变化,并在同一个时钟边沿被采样。
由此我们可以做出这样的总结:
- CPOL 决定空闲状态: SCK 空闲时是高电平还是低电平。
- CPHA 决定采样和改变的时机: 决定在哪个时钟边沿采样数据,在哪个时钟边沿改变数据。
CPOL(时钟极性)与CPHA(时钟相位)组合形成的四种工作模式
三、协议进阶特性
1. 独立片选法:
每个从机独占SS线类似并联结构(硬件资源消耗大)
2. 菊花链拓扑:
多个从机串联,数据级联传输
3. 性能优化策略
- 时钟分频:根据设备特性调整SCK频率
- DMA传输:减少CPU占用率
- 双缓冲机制:提升吞吐量
四、SPI协议优缺点分析
优势:
- 传输速率显著高于I2C(可达100Mbps)
- 时钟配置更灵活
- 全双工通信效率高
局限:
- 短距离通信
- 多从机需要较多IO资源(至少四根)
- 没有设备地址,多设备时需要额外的片选线
五、应用场景与开发
1. 存储器访问:NOR Flash、SD卡(SPI模式)
2. 显示驱动:OLED、TFT屏幕
3. 传感器读取:IMU(MPU6050)、环境传感器
4. 无线模块:nRF24L01+、ESP8266
以下是STM32cubeMX的部分代码,仅供参考
static void MX_SPI1_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为数据线的第一个变化沿
hspi1.Init.NSS = SPI_NSS_SOFT; //软件控制NSS
hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2;//2分频,32M/2=16MHz
hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB; //最高位先发送
hspi1.Init.TIMode = SPI_TIMODE_DISABLE; //TIMODE模式关闭
hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;//CRC关闭
hspi1.Init.CRCPolynomial = 10; //默认值,无效
if (HAL_SPI_Init(&hspi1) != HAL_OK) //初始化
{
_Error_Handler(__FILE__, __LINE__);
}
}
//发送数据
HAL_StatusTypeDef
HAL_SPI_Transmit(SPI_HandleTypeDef *hspi,
uint8_t *pData,
uint16_t Size,
uint32_t Timeout);
//接收数据
HAL_StatusTypeDef
HAL_SPI_Receive(SPI_HandleTypeDef *hspi,
uint8_t *pData,
uint16_t Size,
uint32_t Timeout);
六、SPI的常见问题
时钟模式不匹配: 主从设备的CPOL和CPHA设置不一致会导致通信失败。
片选错误: 没有正确选择从设备会导致数据传输错误。
传输速率过高: 传输速率超过从设备的最大支持速率会导致数据丢失。
引脚连接错误: MOSI和MISO引脚连接错误会导致数据传输方向错误。
B站有众多课程涉及到SPI通信讲解,大家可以自行学习,本文仅作为学习总结,欢迎大家交流学习,共同进步。