STM32硬件SPI通信原理与实战应用
📅 2026/7/21 3:03:09
👁️ 阅读次数
📝 编程学习
1. STM32硬件SPI通信基础解析
SPI(Serial Peripheral Interface)是嵌入式领域最常用的同步串行通信协议之一,特别适合短距离高速数据传输。与I2C相比,SPI采用全双工通信模式,理论传输速率可达几十MHz(具体取决于芯片型号),且协议开销更小。STM32全系列芯片都内置了硬件SPI控制器,极大减轻了开发者的时序控制负担。
在硬件连接上,SPI总线包含四根基础信号线:
- SCK(Serial Clock):时钟信号,由主设备产生
- MOSI(Master Out Slave In):主设备输出、从设备输入
- MISO(Master In Slave Out):主设备输入、从设备输出
- NSS(Slave Select):从设备选择(低电平有效)
以STM32F103C8T6为例,其SPI1外设的默认引脚映射为:
- PA4 - NSS
- PA5 - SCK
- PA6 - MISO
- PA7 - MOSI
注意:NSS引脚在硬件SPI模式下可以配置为硬件管理或软件管理。在多从机系统中,建议使用软件控制GPIO来模拟片选信号,以获得更灵活的时序控制。
2. 硬件SPI主从通信系统搭建
2.1 硬件连接方案
在双STM32主从通信系统中,连接方式如下:
- 主控STM32的MOSI接从机STM32的MOSI
- 主控STM32的MISO接从机STM32的MISO
- 主控STM32的SCK接从机STM32的SCK
- 主控STM32的任意GPIO(如PB0)作为从机片选信号
特别要注意的是,SPI从设备需要在SCK上升沿或下降沿采样数据(由CPHA决定),因此主从设备的时钟极性(CPOL)和时钟相位(CPHA)必须严格匹配。常见的模式有:
- 模式0:CPOL=0,CPHA=0(SCK空闲低电平,第一个边沿采样)
- 模式3:CPOL=1,CPHA=1(SCK空闲高电平,第二个边沿采样)
2.2 CubeMX配置详解
使用STM32CubeMX进行初始化配置时,主从设备需要分别设置:
主设备配置:
- 在"Connectivity"选项卡中启用SPI1
- 模式选择"Full-Duplex Master"
- 硬件NSS选择"Disable"(使用软件控制)
- 参数设置:
- Prescaler:根据系统时钟和所需速率选择分频系数
- CPOL/CPHA:根据从设备要求选择
- First Bit:通常选择MSB First
- CRC Calculation:禁用
从设备配置:
- 启用SPI1
- 模式选择"Full-Duplex Slave"
- 硬件NSS选择"Hardware NSS Input"
- 其他参数必须与主设备完全一致
3. HAL库SPI通信代码实现
3.1 主设备发送流程
主设备的典型发送代码如下:
// 初始化代码(CubeMX生成) void MX_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; // ...其他参数 HAL_SPI_Init(&hspi1); } // 发送函数 uint8_t SPI_TransmitReceive(uint8_t *pTxData, uint8_t *pRxData, uint16_t Size) { HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET); // 拉低片选 HAL_StatusTypeDef status = HAL_SPI_TransmitReceive(&hspi1, pTxData, pRxData, Size, 100); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET); // 释放片选 return status; }3.2 从设备接收处理
从设备需要持续监听SPI总线:
// 从设备初始化 void MX_SPI1_Init(void) { hspi1.Instance = SPI1; hspi1.Init.Mode = SPI_MODE_SLAVE; // ...其他参数与主设备匹配 HAL_SPI_Init(&hspi1); } // 接收中断回调函数 void HAL_SPI_RxCpltCallback(SPI_HandleTypeDef *hspi) { if(hspi->Instance == SPI1) { // 处理接收到的数据 processSPIData(rxBuffer); // 重新启动接收 HAL_SPI_Receive_IT(&hspi1, rxBuffer, BUFFER_SIZE); } }4. 实战调试与性能优化
4.1 常见问题排查
无通信信号:
- 检查SPI外设时钟是否使能(__HAL_RCC_SPI1_CLK_ENABLE())
- 确认GPIO模式配置正确(SPI引脚应设为AF_PP模式)
- 使用逻辑分析仪抓取SCK信号,确认时钟频率符合预期
数据错位或丢失:
- 确保主从设备的CPOL/CPHA设置完全一致
- 检查硬件连接,特别是MISO/MOSI是否交叉连接
- 在片选切换后增加适当延时(至少1us)
从设备无响应:
- 验证从设备的供电电压和复位电路
- 检查NSS信号是否有效拉低
- 确认从设备的SPI初始化在主机开始通信前完成
4.2 DMA优化方案
对于高速数据传输,建议启用DMA以减少CPU开销:
// 主设备DMA发送配置 void SPI_DMA_Init(void) { __HAL_RCC_DMA2_CLK_ENABLE(); hdma_spi1_tx.Instance = DMA2_Stream3; hdma_spi1_tx.Init.Channel = DMA_CHANNEL_3; hdma_spi1_tx.Init.Direction = DMA_MEMORY_TO_PERIPH; // ...其他DMA参数 HAL_DMA_Init(&hdma_spi1_tx); __HAL_LINKDMA(&hspi1, hdmatx, hdma_spi1_tx); HAL_SPI_Transmit_DMA(&hspi1, txBuffer, BUFFER_SIZE); }实测数据显示,在STM32F407上使用DMA传输可将SPI吞吐量提升3-5倍,同时降低CPU占用率至10%以下。
5. 进阶应用:多从机系统设计
当需要控制多个SPI从设备时,可采用以下两种方案:
5.1 独立片选方案
每个从设备使用独立的片选线:
- 优点:硬件简单,通信时序完全独立
- 缺点:占用较多GPIO资源
- 典型应用:同时控制SPI Flash和显示屏等不同外设
5.2 菊花链方案
将多个相同从设备串联:
- 优点:节省GPIO资源,适合同类型设备
- 缺点:所有设备共享同一时钟,通信效率较低
- 实现要点:
- 所有设备的MISO接下一设备的MOSI
- 只有最后一个设备的MISO返回主机
- 数据需要包含设备地址信息
// 菊花链通信示例 void DaisyChain_Transmit(uint8_t devAddr, uint8_t *data) { uint8_t packet[3] = {devAddr, data[0], data[1]}; HAL_SPI_Transmit(&hspi1, packet, 3, 100); }在实际项目中,我曾遇到一个典型问题:当主设备快速切换片选时,从设备可能无法及时响应。解决方案是在片选切换后增加5-10us的延时,或者通过读取从设备的状态寄存器确认其准备就绪。
编程学习
技术分享
实战经验