STM32学习笔记:SPI接口使用
SPI协议核心解析与STM32应用指南 SPI通信采用四线制架构(SCK/MOSI/MISO/NSS),支持4种时钟模式(Mode0-3),不同外设需匹配特定模式。
·
一、SPI协议核心机制解析
1. 四线制通信架构
- SCK:时钟信号(主机控制)
- MOSI:主机输出从机输入
- MISO:主机输入从机输出
- NSS:片选信号(低电平有效)
2. 时钟极性/相位组合
| 模式 | CPOL | CPHA | 数据采样时刻 |
|---|---|---|---|
| Mode0 | 0 | 0 | SCK上升沿 |
| Mode1 | 0 | 1 | SCK下降沿 |
| Mode2 | 1 | 0 | SCK下降沿 |
| Mode3 | 1 | 1 | SCK上升沿 |
行业惯例:SD卡常用Mode0,FLASH常用Mode3
二、STM32硬件SPI深度配置
1. CubeMX关键配置项
// 典型SPI1配置(APB2=72MHz)
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 (Mode0)
hspi1.Init.NSS = SPI_NSS_SOFT; // 软件控制NSS
hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_4; // 18MHz
hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB; // 高位先行
2. 波特率精密计算
SPI_Speed=APB_ClockPrescaler SPI\_Speed = \frac{APB\_Clock}{Prescaler} SPI_Speed=PrescalerAPB_Clock
// 时钟分频系数表
| 预分频值 | 实际分频比 |
|------------------|-----------|
| SPI_BAUDRATEPRESCALER_2 | 2 |
| SPI_BAUDRATEPRESCALER_4 | 4 |
| SPI_BAUDRATEPRESCALER_8 | 8 |
| ... | ... |
| SPI_BAUDRATEPRESCALER_256 | 256 |
三、软件模拟SPI(GPIO模拟)
1. 模式0实现核心代码
void Soft_SPI_Write(uint8_t data) {
for(int i=0; i<8; i++) {
// 下降沿准备数据
MOSI_LOW();
if(data & 0x80) MOSI_HIGH();
// 上升沿采样
SCK_HIGH();
data <<= 1;
// 下降沿切换
SCK_LOW();
}
}
2. 硬件SPI vs 软件SPI性能对比
| 指标 | 硬件SPI (72MHz) | 软件SPI (2MHz) |
|---|---|---|
| 传输速度 | 72 Mbps | <2 Mbps |
| CPU占用率 | 0% (DMA模式) | >90% |
| 信号完整性 | 高 | 易受中断干扰 |
| 多从机支持 | 硬件NSS管理 | 需手动控制CS |
四、SPI外设驱动实战
1. W25Q128 FLASH芯片驱动
// 读Flash ID(厂商+容量)
uint8_t cmd[4] = {0x9F, 0x00, 0x00, 0x00};
uint8_t id[2];
HAL_SPI_TransmitReceive(&hspi1, cmd, id, 4, 100);
// id[1]=0x40表示128Mbit
// 页编程(启用写使能)
void Flash_WritePage(uint32_t addr, uint8_t* data) {
uint8_t cmd[4] = {0x06}; // WREN
HAL_SPI_Transmit(&hspi1, cmd, 1, 100);
cmd[0] = 0x02; // Page Program
cmd[1] = (addr>>16)&0xFF;
cmd[2] = (addr>>8)&0xFF;
cmd[3] = addr&0xFF;
HAL_GPIO_WritePin(FLASH_CS_GPIO, FLASH_CS_PIN, GPIO_PIN_RESET);
HAL_SPI_Transmit(&hspi1, cmd, 4, 100);
HAL_SPI_Transmit(&hspi1, data, 256, 100);
HAL_GPIO_WritePin(FLASH_CS_GPIO, FLASH_CS_PIN, GPIO_PIN_SET);
}
2. ILI9341 LCD屏驱动优化
// 使用16位色快速填充
#define LCD_CMD 0
#define LCD_DATA 1
void LCD_Fill(uint16_t color, uint32_t len) {
uint8_t cmd = 0x2C; // Memory Write
LCD_Send(LCD_CMD, &cmd, 1);
// DMA传输优化(速度提升5倍)
uint16_t *buf = malloc(len*2);
for(int i=0; i<len; i++) buf[i] = color;
HAL_SPI_Transmit_DMA(&hspi1, (uint8_t*)buf, len*2);
}
五、高级应用与性能优化
1. DMA链式传输(H7系列)
// 创建链表实现自动传输
void SPI_DMA_Chain_Config(void) {
// 1. 初始化链表
HAL_DMAEx_List_Init(&handle, LIST_SIZE);
// 2. 构建传输节点
HAL_DMAEx_List_BuildNode(&tx_config, &node1);
HAL_DMAEx_List_BuildNode(&rx_config, &node2);
// 3. 链接节点
HAL_DMAEx_List_InsertNode_Head(&handle, &node1);
HAL_DMAEx_List_InsertNode_Tail(&handle, &node2);
// 4. 启动链表传输
HAL_DMAEx_List_Start_IT(&handle);
}
2. 多从机硬件管理方案
// 使用I/O扩展器管理片选
void SPI_SelectDevice(uint8_t dev_id) {
uint8_t mask = 1 << dev_id;
uint8_t cmd[2] = {0x40, mask}; // TCA9538写命令
HAL_I2C_Master_Transmit(&hi2c1, 0x70, cmd, 2, 100);
}
3. 信号完整性设计要点
六、调试技巧与故障排除
1. 逻辑分析仪诊断SPI四要素
- 片选有效性:操作期间CS保持低电平
- 时钟模式匹配:CPOL/CPHA是否符合设备要求
- 数据对齐:MSB/LSB顺序是否正确
- 建立/保持时间:数据在时钟边沿的稳定性
2. 常见故障解决方案
| 故障现象 | 可能原因 | 解决方案 |
|---|---|---|
| 数据全为0xFF | MISO未连接 | 检查硬件连接 |
| 偶发数据错误 | 时序裕量不足 | 降低波特率或增加时钟延时 |
| DMA传输卡死 | 内存未对齐访问 | 使用__attribute__((aligned(4))) |
| 多从机相互干扰 | CS信号串扰 | 增加1KΩ下拉电阻 |
3. 示波器实测波形分析
正常波形:
SCK _┌┐_┌┐_┌┐_┌┐_
MOSI __▁▁▃▃▅▅▇▇__ // 清晰电平跳变
故障波形:
SCK _┌──┐_┌──┐_ // 时钟畸变
MOSI __▁~~▃▃~~~~__ // 信号振铃
七、跨系列兼容性设计
1. F1/F4/H7 SPI差异对比
| 特性 | STM32F1 | STM32F4 | STM32H7 |
|---|---|---|---|
| 最大时钟 | 18 MHz | 45 MHz | 133 MHz |
| 数据帧格式 | 8/16位 | 4-16位可编程 | 4-32位可编程 |
| DMA触发方式 | 单次触发 | FIFO+突发传输 | MDMA矩阵传输 |
| CRC计算 | 仅基本CRC | 可配置多项式 | 硬件CRC32 |
2. 通用驱动封装技巧
// 抽象SPI操作接口
typedef struct {
void (*Init)(void);
uint8_t (*TransmitReceive)(uint8_t data);
void (*CS_Control)(uint8_t state);
} SPI_Driver;
// 硬件SPI实现
SPI_Driver HW_SPI = {
.Init = SPI1_Init,
.TransmitReceive = HAL_SPI_TransmitReceive,
.CS_Control = HW_CS_Control
};
// 软件SPI实现
SPI_Driver SW_SPI = {
.Init = Soft_SPI_Init,
.TransmitReceive = Soft_SPI_Transceive,
.CS_Control = GPIO_CS_Control
};
更多推荐



所有评论(0)