STM32 SPI通信详解:从原理到实战代码
一、SPI通信基础
1.1 什么是SPI?
SPI(Serial Peripheral Interface)是由Motorola提出的全双工同步串行通信协议,广泛用于微控制器与外围设备(Flash、传感器、显示屏等)的高速数据传输。
核心特性:
全双工通信(同时收发)
最高时钟频率可达数十MHz
主从架构(一主多从)
硬件连接简单(4线制)
1.2 SPI物理接口
信号线 | 全称 | 作用 |
---|---|---|
SCK | Serial Clock | 同步时钟(由主机产生) |
MOSI | Master Out Slave In | 主机发送,从机接收 |
MISO | Master In Slave Out | 主机接收,从机发送 |
NSS | Slave Select | 从机片选(低电平有效) |
二、SPI工作原理及时序
2.1 四种工作模式
由CPOL(时钟极性)和CPHA(时钟相位)组合决定:
模式 | CPOL | CPHA | 特性 |
---|---|---|---|
0 | 0 | 0 | 时钟空闲低电平,第一个边沿采样 |
1 | 0 | 1 | 时钟空闲低电平,第二个边沿采样 |
2 | 1 | 0 | 时钟空闲高电平,第一个边沿采样 |
3 | 1 | 1 | 时钟空闲高电平,第二个边沿采样 |
关键点:
CPOL=0:SCK空闲时为低电平
CPOL=1:SCK空闲时为高电平
CPHA=0:在SCK第一个边沿(奇数边沿)采样数据
CPHA=1:在SCK第二个边沿(偶数边沿)采样数据
2.2 典型时序分析(模式0)
以主机发送0xAA(10101010)为例:
-
NSS拉低:选中从机
-
SCK产生脉冲:CPOL=0,空闲低电平
-
数据采样:
-
MOSI在SCK上升沿改变数据
-
从机在SCK下降沿采样数据
-
数据传输:高位(MSB)先发
三、STM32 SPI外设配置
3.1 硬件设计
以STM32F103C8T6与W25Q64 Flash芯片通信为例:
SCK -> PA5
MOSI -> PA7
MISO -> PA6
NSS -> PA4(软件控制)
3.2 CubeMX配置步骤
-
启用SPI1(模式选择为全双工主模式)
-
配置参数:
-
时钟分频(PCLK2为72MHz时,分频256得281.25kHz)
-
数据宽度:8位
-
模式选择:Mode 0(CPOL=0, CPHA=0)
-
NSS选择:软件模式
-
生成代码
3.3 关键代码解析
初始化代码
SPI_HandleTypeDef hspi1;
void SPI1_Init(void)
{
hspi1.Instance = SPI1;
hspi1.Init.Mode = SPI_MODE_MASTER;
hspi1.Init.Direction = SPI_DIRECTION_2LINES;
hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;
hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;
hspi1.Init.NSS = SPI_NSS_SOFT;
hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_256;
hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
HAL_SPI_Init(&hspi1);
}
数据收发函数
// 发送单字节并接收返回
uint8_t SPI_TransmitReceive(uint8_t data)
{
uint8_t rx_data;
HAL_SPI_TransmitReceive(&hspi1, &data, &rx_data, 1, 100);
return rx_data;
}
// 读取Flash芯片ID(以W25Q64为例)
uint16_t W25Q_ReadID(void)
{
uint8_t cmd = 0x9F; // JEDEC ID命令
uint8_t id[3];
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET); // 拉低NSS
HAL_SPI_Transmit(&hspi1, &cmd, 1, 100);
HAL_SPI_Receive(&hspi1, id, 3, 100);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET); // 释放NSS
return (id[1] << 8) | id[2]; // 返回厂商ID和设备ID
}
四、调试技巧与常见问题
4.1 示波器诊断
检测SCK波形:确认频率和极性是否符合预期
观察MOSI/MISO:验证数据发送是否正确
检查NSS信号:确保片选信号正常拉低
4.2 常见问题排查
-
无数据通信:
-
检查硬件连接是否短路/断路
-
验证SPI模式(主/从)设置是否正确
-
确认NSS信号是否有效
-
数据错位:
-
检查MSB/LSB设置
-
确认时钟相位设置是否匹配从机要求
-
通信速率问题:
-
降低波特率测试
-
检查电源稳定性
五、进阶应用:DMA+SPI
5.1 使用DMA提升效率
// 启用DMA传输
void SPI_DMA_Transmit(uint8_t *pData, uint16_t Size)
{
HAL_SPI_Transmit_DMA(&hspi1, pData, Size);
while (hspi1.State != HAL_SPI_STATE_READY); // 等待传输完成
}
// 接收数据
void SPI_DMA_Receive(uint8_t *pData, uint16_t Size)
{
HAL_SPI_Receive_DMA(&hspi1, pData, Size);
while (hspi1.State != HAL_SPI_STATE_READY);
}
六、总结
本文详细解析了SPI协议的工作原理,并通过STM32实战代码演示了通信流程。实际开发中需注意:
-
严格匹配主从设备的SPI模式
-
合理选择通信速率(兼顾稳定性和速度)
-
复杂场景建议使用DMA减轻CPU负担
创作不易,如果本文对您有帮助,请点赞收藏支持!
关于SPI的更多问题,欢迎在评论区留言讨论
作者:DOMINICHZL