前言
在嵌入式开发中,SPI(Serial Peripheral Interface)作为一种高速全双工的同步通信协议,被广泛应用于FLASH存储器、显示屏、传感器等外设的通信。本文将结合STM32F1系列芯片,分享如何通过标准外设库(Standard Peripheral Library)实现SPI通信的开发过程。
一、SPI协议基础
1.1 通信特点
-
四线制:SCK(时钟)、MOSI(主出从入)、MISO(主入从出)、NSS(片选)
-
全双工同步传输:主机控制时钟,主从设备同时收发数据
-
通信模式:由CPOL(时钟极性)和CPHA(时钟相位)组合的4种模式
1.2 关键参数
参数 | 说明 |
---|---|
时钟频率 | 可达几十MHz(取决于外设) |
数据帧格式 | 8位或16位数据帧 |
传输顺序 | MSB First或LSB First |
二、STM32的SPI外设
2.1 硬件特性
-
支持主/从模式切换
-
3个独立SPI控制器(SPI1/SPI2/SPI3)
-
8/16位数据帧可编程
-
硬件CRC校验(可选)
2.2 时钟配置
SPI时钟源来自APB总线:
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE); // SPI1在APB2总线
三、标准库开发步骤
3.1 初始化配置
void SPI1_Init(void)
{
SPI_InitTypeDef SPI_InitStructure;
/* 引脚配置(以SPI1为例) */
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
// SCK:PA5, MOSI:PA7
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_7;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; // 复用推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* SPI参数配置 */
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
SPI_InitStructure.SPI_Mode = SPI_Mode_Master; // 主模式
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; // 8位数据
SPI_InitStructure.SPI_CPOL = SPI_CPOL_High; // 模式3
SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; // 软件控制NSS
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256;
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; // 高位先行
SPI_Init(SPI1, &SPI_InitStructure);
SPI_Cmd(SPI1, ENABLE); // 使能SPI外设
}
3.2 数据收发函数
uint8_t SPI1_ReadWriteByte(uint8_t TxData)
{
while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET); // 等待发送缓冲区空
SPI_I2S_SendData(SPI1, TxData);
while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET); // 等待接收完成
return SPI_I2S_ReceiveData(SPI1);
}
四、关键问题与调试技巧
4.1 常见问题排查
-
无数据收发
-
检查SPI使能位是否开启
-
确认NSS片选信号有效
-
验证时钟极性/相位是否匹配从设备
-
-
数据错位
-
检查MSB/LSB配置
-
确认数据帧长度一致
-
4.2 调试建议
-
逻辑分析仪:抓取SCK、MOSI、MISO波形
-
示波器:验证时钟频率和信号质量
-
分步调试:通过断点检查SPI状态寄存器
五、应用实例——读写FLASH芯片
以W25Q128为例:
// 读取设备ID
uint16_t W25Q_ReadID(void)
{
uint16_t Temp = 0;
W25Q_CS_LOW(); // 拉低片选
SPI1_ReadWriteByte(0x90); // 发送读ID命令
SPI1_ReadWriteByte(0x00);
SPI1_ReadWriteByte(0x00);
SPI1_ReadWriteByte(0x00);
Temp |= SPI1_ReadWriteByte(0xFF) << 8;
Temp |= SPI1_ReadWriteByte(0xFF);
W25Q_CS_HIGH();
return Temp;
}
六、总结
通过标准库开发SPI通信需要重点关注:
-
正确的GPIO模式配置(复用功能)
-
SPI工作模式与从设备匹配
-
合理的时钟分频设置
-
严格的时序控制(特别是片选信号)
建议结合STM32参考手册的SPI章节(Chapter 23)和具体外设的数据手册进行开发。后续可继续探索DMA传输、中断模式等高级应用。