1. 项目概述
SPI(Serial Peripheral Interface)作为一种高速全双工同步串行通信协议,在嵌入式系统中扮演着重要角色。以STM32F103C6T6这款经典的Cortex-M3内核MCU为例,其内置的硬件SPI控制器能够极大简化外设通信设计。本文将深入解析如何在该芯片上完成SPI接口的完整配置流程,从寄存器级操作到HAL库应用,涵盖主从模式设置、时钟极性与相位调整、DMA传输实现等核心环节。
对于嵌入式开发者而言,掌握SPI配置不仅意味着能驱动常见的外设模块(如Flash存储器、显示屏、传感器等),更是理解时序协议和硬件交互原理的重要实践。STM32CubeMX工具虽然能自动生成初始化代码,但知其然更要知其所以然——本文将同步揭示图形化配置背后的寄存器操作本质。
2. 硬件基础与原理分析
2.1 STM32F103C6T6的SPI资源特性
该型号配备两个SPI接口(SPI1和SPI2),主要差异在于时钟频率上限和所在总线位置:
- SPI1挂载APB2总线(最高72MHz)
- SPI2挂载APB1总线(最高36MHz)
每个接口支持以下关键特性:
- 全双工/半双工同步传输
- 8/16位数据帧格式
- 主从模式可配置
- 硬件CRC校验
- 支持DMA传输
注意:实际可用时钟频率需考虑外设模块的工作范围。例如W25Q系列Flash芯片通常支持最高104MHz时钟,但STM32F103的SPI2在36MHz限制下需权衡传输速率需求。
2.2 SPI协议核心参数解析
配置前必须明确的四个关键参数:
-
时钟极性(CPOL):
- 0:SCK空闲时为低电平
- 1:SCK空闲时为高电平
-
时钟相位(CPHA):
- 0:数据在第一个时钟边沿采样
- 1:数据在第二个时钟边沿采样
-
数据帧格式:
- 8位或16位数据长度
- MSB/LSB先传选择
-
NSS信号管理:
- 硬件模式(自动片选)
- 软件模式(手动控制GPIO)
这些参数的组合直接影响设备间的时序匹配。例如,SD卡的SPI模式通常采用CPOL=0/CPHA=0,而某些传感器可能需要CPOL=1/CPHA=1。
3. CubeMX可视化配置详解
3.1 工程创建与基本设置
- 打开STM32CubeMX,新建工程选择STM32F103C6T6
- 在"Pinout & Configuration"视图启用SPI接口
- 配置模式为"Full-Duplex Master"(全双工主机)
- 设置硬件NSS信号为"Disable"(推荐使用软件控制)
关键参数配置界面示例:
| 参数项 | 推荐值 | 说明 |
|---|---|---|
| Clock Division | /16 | 72MHz/16=4.5MHz |
| CPOL | Low | 空闲低电平 |
| CPHA | 1 Edge | 第一时钟边沿采样 |
| Data Size | 8 bits | 标准字节传输 |
| First Bit | MSB | 高位优先 |
3.2 引脚分配与复用功能
SPI1默认引脚映射:
- PA5 -> SPI1_SCK
- PA6 -> SPI1_MISO
- PA7 -> SPI1_MOSI
实操技巧:若需重映射引脚,需同时开启AFIO时钟并调用
__HAL_AFIO_REMAP_SPI1_ENABLE()。
3.3 生成代码前的关键检查
- 确认NVIC中断已启用(如需中断模式)
- DMA设置界面配置好TX/RX通道(如需DMA传输)
- 在Project Manager中勾选"Generate peripheral initialization as a pair of .c/.h files"
4. 底层寄存器级配置解析
4.1 关键寄存器组详解
即使使用HAL库,了解寄存器操作仍对调试有重要意义:
-
CR1(控制寄存器1):
- Bit 6 SPE:SPI使能
- Bit 2 MSTR:主从模式选择
- Bits 3-5 BR[2:0]:波特率分频
-
CR2(控制寄存器2):
- Bit 0 RXDMAEN:接收DMA使能
- Bit 1 TXDMAEN:发送DMA使能
-
SR(状态寄存器):
- Bit 1 TXE:发送缓冲区空标志
- Bit 0 RXNE:接收缓冲区非空
4.2 手动初始化代码示例
c复制void SPI1_Manual_Init(void) {
// 1. 使能时钟
RCC->APB2ENR |= RCC_APB2ENR_SPI1EN;
// 2. 配置CR1
SPI1->CR1 = SPI_CR1_MSTR | // 主机模式
SPI_CR1_BR_1 | // 分频系数/8
SPI_CR1_SSM | // 软件NSS管理
SPI_CR1_SSI; // 内部NSS信号
// 3. 使能SPI
SPI1->CR1 |= SPI_CR1_SPE;
}
5. HAL库驱动开发实战
5.1 典型传输流程
阻塞式传输示例:
c复制HAL_StatusTypeDef SPI_TransmitReceive(uint8_t *txData, uint8_t *rxData, uint16_t size) {
HAL_SPI_TransmitReceive(&hspi1, txData, rxData, size, HAL_MAX_DELAY);
// 检查传输状态
if(hspi1.State != HAL_SPI_STATE_READY) {
return HAL_ERROR;
}
return HAL_OK;
}
中断模式注意事项:
- 实现
HAL_SPI_TxRxCpltCallback()回调函数 - 在NVIC中设置合适的中断优先级
- 传输前调用
HAL_SPI_TransmitReceive_IT()
5.2 DMA优化技巧
- 内存缓冲区需4字节对齐(
__attribute__((aligned(4)))) - 双缓冲策略示例:
c复制uint8_t dmaBuffer1[256], dmaBuffer2[256];
void Start_DualBuffer_Transfer(void) {
HAL_SPI_TransmitReceive_DMA(&hspi1, dmaBuffer1, dmaBuffer2, 256);
}
// 在XferCpltCallback中切换缓冲区
6. 典型问题排查指南
6.1 常见故障现象与解决方案
| 现象 | 可能原因 | 排查步骤 |
|---|---|---|
| 无时钟输出 | GPIO配置错误 | 检查SCK引脚复用功能 |
| 能发送但接收全0 | NSS信号未拉低 | 手动控制片选GPIO |
| 数据错位 | CPOL/CPHA不匹配 | 用逻辑分析仪捕获时序 |
| DMA传输卡死 | 缓冲区未对齐 | 添加__ALIGNED(4)修饰符 |
6.2 逻辑分析仪调试要点
- 连接SCK、MOSI、MISO三线
- 设置采样率至少为SPI时钟的4倍
- 解码时注意:
- 起始条件:NSS下降沿
- 数据有效性:根据CPHA选择采样边沿
实测案例:某次调试中发现ADXL345传感器无响应,最终发现是CPHA设置错误。传感器要求CPHA=1而配置为0,导致数据采样相位错位。
7. 性能优化进阶技巧
7.1 时钟分频与吞吐量计算
理论最大传输速率公式:
code复制Throughput = fPCLK / (2 * SPI_BaudRatePrescaler)
例如APB2时钟72MHz,分频系数16时:
code复制72MHz / (2*16) = 2.25Mbps
7.2 软件优化策略
- 减少函数调用开销:
c复制// 直接寄存器访问替代HAL库
while(!(SPI1->SR & SPI_SR_TXE));
SPI1->DR = data;
- 使用内存屏障确保操作顺序:
c复制__DSB(); // 数据同步屏障
- 临界区保护:
c复制__disable_irq();
// SPI关键操作
__enable_irq();
8. 外设驱动实例:W25Q128 Flash
8.1 初始化序列
c复制void W25Q_Init(void) {
// 1. 发送Release Power-down指令
uint8_t cmd = 0xAB;
HAL_SPI_Transmit(&hspi1, &cmd, 1, 100);
// 2. 读取JEDEC ID
uint8_t idCmd[4] = {0x9F};
uint8_t idData[3];
HAL_SPI_TransmitReceive(&hspi1, idCmd, idData, 4, 100);
}
8.2 页编程优化
c复制void W25Q_PageProgram(uint32_t addr, uint8_t *data) {
uint8_t cmd[4] = {
0x02, // Page Program指令
(addr >> 16) & 0xFF,
(addr >> 8) & 0xFF,
addr & 0xFF
};
HAL_GPIO_WritePin(FLASH_CS_GPIO_Port, 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_Port, FLASH_CS_Pin, GPIO_PIN_SET);
}
9. 实测波形分析
通过Saleae Logic捕获的SPI通信波形示例:
- SCK频率:实测4.48MHz(配置值4.5MHz)
- 传输内容:0x55AA的16位数据
- 建立时间(Setup Time):22ns(满足典型器件要求)
调试心得:发现STM32的SPI时钟存在约5%的偏差,在高速传输时需预留足够时序余量。对于要求严格的器件,建议实测确认时序参数。
