一、STM32F407 SPI 控制器简介
STM32F407 包含 3个SPI控制器(SPI1/2/3),支持:
全双工/半双工通信
主/从模式切换
时钟频率高达 42 MHz(SPI1)
硬件 CRC 校验(可选)
通过DMA可实现高效数据流传输,适用于高速、大容量数据场景(如显示屏刷新、高速ADC采集)。
二、HAL库配置SPI+DMA的关键步骤
- 初始化SPI外设
c
SPI_HandleTypeDef hspi1;
hspi1.Instance = SPI1;
hspi1.Init.Mode = SPI_MODE_MASTER; // 主模式
hspi1.Init.Direction = SPI_DIRECTION_2LINES; // 全双工
hspi1.Init.DataSize = SPI_DATASIZE_8BIT; // 数据长度(8/16位)
hspi1.Init.CLKPolarity = SPI_POLARITY_LOW; // 时钟极性
hspi1.Init.CLKPhase = SPI_PHASE_1EDGE; // 时钟相位
hspi1.Init.NSS = SPI_NSS_SOFT; // 软件控制片选
hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_8; // 42 MHz / 8 = 5.25 MHz
hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB; // 高位先行
hspi1.Init.TIMode = SPI_TIMODE_DISABLE; // TI模式禁用
hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE; // CRC校验关闭
HAL_SPI_Init(&hspi1);
- 配置DMA传输
c
// 发送DMA配置(以SPI1_TX为例,使用DMA1 Stream3)
DMA_HandleTypeDef hdma_spi1_tx;
hdma_spi1_tx.Instance = DMA1_Stream3;
hdma_spi1_tx.Init.Channel = DMA_CHANNEL_3; // SPI1_TX对应通道3
hdma_spi1_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
hdma_spi1_tx.Init.PeriphInc = DMA_PINC_DISABLE; // 外设地址固定(SPI数据寄存器)
hdma_spi1_tx.Init.MemInc = DMA_MINC_ENABLE; // 内存地址递增
hdma_spi1_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE; // 对齐方式(8位)
hdma_spi1_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
hdma_spi1_tx.Init.Mode = DMA_NORMAL; // 单次传输模式
hdma_spi1_tx.Init.Priority = DMA_PRIORITY_HIGH;
HAL_DMA_Init(&hdma_spi1_tx);
__HAL_LINKDMA(&hspi1, hdmatx, hdma_spi1_tx); // 绑定DMA到SPI句柄
// 接收DMA配置(方向为PERIPH_TO_MEMORY,流和通道需参考手册)
DMA_HandleTypeDef hdma_spi1_rx;
hdma_spi1_rx.Instance = DMA1_Stream0; // SPI1_RX对应DMA1 Stream0
hdma_spi1_rx.Init.Channel = DMA_CHANNEL_3; // 通道3
HAL_DMA_Init(&hdma_spi1_rx);
__HAL_LINKDMA(&hspi1, hdmarx, hdma_spi1_rx);
- 启动DMA传输
全双工模式(同时收发)
c
uint8_t tx_buffer[] = {0xAA, 0xBB, 0xCC};
uint8_t rx_buffer[3];
// 启动全双工传输(需同时启用TX和RX DMA)
HAL_SPI_TransmitReceive_DMA(&hspi1, tx_buffer, rx_buffer, sizeof(tx_buffer));
单工模式(仅发送或接收)
c
// 仅发送
HAL_SPI_Transmit_DMA(&hspi1, tx_buffer, sizeof(tx_buffer));
// 仅接收(需发送虚拟数据以产生时钟)
HAL_SPI_Receive_DMA(&hspi1, rx_buffer, sizeof(rx_buffer));
- 中断与回调函数
c
// DMA传输完成回调
void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef *hspi) {
// 全双工传输完成处理
}
void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi) {
// 发送完成处理
}
void HAL_SPI_RxCpltCallback(SPI_HandleTypeDef *hspi) {
// 接收完成处理
}
// 错误回调
void HAL_SPI_ErrorCallback(SPI_HandleTypeDef *hspi) {
uint32_t error = HAL_SPI_GetError(hspi);
// 处理错误(如DMA溢出、模式错误等)
}
三、优缺点分析
优点:
极致吞吐量
DMA支持连续传输,在高速SPI(如42 MHz)下可实现 >30 Mbps 的稳定速率,适合图像传输、高速数据采集。
解放CPU资源
CPU无需干预数据搬运,可处理其他任务(如协议解析、用户交互)或进入低功耗模式。
低延迟保障
DMA自动处理数据流,避免因中断服务程序(ISR)导致的时序抖动,适用于实时控制系统。
缺点:
硬件配置复杂
DMA流与通道必须严格匹配SPI外设(如SPI1_TX → DMA1 Stream3,SPI1_RX → DMA1 Stream0)。
需处理内存对齐(如16位数据需2字节对齐)和缓冲区溢出风险。
调试难度高
DMA传输错误(如外设未就绪)可能引发数据丢失,需逻辑分析仪抓取SPI时序。
全双工模式下,TX和RX DMA需严格同步,否则导致数据错位。
硬件限制
STM32F407的DMA流数量有限,多外设共用时需合理分配优先级。
SPI从模式下DMA支持较弱,需依赖中断辅助。
潜在的竞争条件
多任务环境下,若未正确管理缓冲区,可能导致数据覆盖(如DMA未完成时修改缓冲区)。
四、关键注意事项
DMA流与通道映射
SPI1_TX → DMA1 Stream3/Channel3
SPI1_RX → DMA1 Stream0/Channel3
SPI2/3的DMA映射需查阅手册,避免与其他外设(如UART、ADC)冲突。
内存对齐与缓冲区
使用 __ALIGNED(4) 确保缓冲区4字节对齐(防止DMA访问越界)。
循环DMA模式需配合双缓冲区策略(如Ping-Pong Buffer)实现无缝传输。
SPI片选(NSS)管理
软件片选(NSS_SOFT)时,需手动控制GPIO电平:
c
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET); // 使能片选
HAL_SPI_Transmit_DMA(...);
// 在传输完成回调中关闭片选
错误恢复机制
在 HAL_SPI_ErrorCallback 中复位SPI和DMA:
c
HAL_SPI_DeInit(&hspi1);
HAL_SPI_Init(&hspi1);
HAL_DMA_DeInit(&hdma_spi1_tx);
HAL_DMA_Init(&hdma_spi1_tx);
五、适用场景
高速数据流传输:如TFT显示屏刷新、SD卡读写、摄像头数据采集。
实时信号处理:如音频编解码(I2S)、高速ADC/DAC通信。
多外设并行操作:CPU同时处理多个任务(如网络通信+传感器读取)。
六、常见问题与解决方案
DMA传输未启动
检查DMA流是否被其他外设占用(如ADC、UART)。
确认SPI时钟使能(__HAL_RCC_SPI1_CLK_ENABLE())。
数据错位(MSB/LSB颠倒)
检查 SPI_Init.FirstBit 配置(MSB或LSB先行)。
确保发送和接收端的数据格式一致。
SPI时钟无输出
验证SPI模式(主模式需配置GPIO为复用功能)。
检查时钟分频系数(BaudRatePrescaler)是否过小(如分频值=2时可能超出GPIO速度限制)。
全双工数据错乱
确保TX和RX DMA的缓冲区长度一致。
在传输前手动清除SPI DR寄存器:
c
__HAL_SPI_CLEAR_OVRFLAG(&hspi1); // 清除溢出标志
通过合理配置,SPI+DMA方案可显著提升系统性能,但其硬件依赖性强,建议在资源充足且对吞吐量要求苛刻的场景中使用。
2194

被折叠的 条评论
为什么被折叠?



