
1. STM32硬件SPI通信基础解析SPISerial Peripheral Interface是嵌入式领域最常用的同步串行通信协议之一特别适合短距离高速数据传输。与I2C相比SPI采用全双工通信模式理论传输速率可达几十MHz具体取决于芯片型号且协议开销更小。STM32全系列芯片都内置了硬件SPI控制器极大减轻了开发者的时序控制负担。在硬件连接上SPI总线包含四根基础信号线SCKSerial Clock时钟信号由主设备产生MOSIMaster Out Slave In主设备输出、从设备输入MISOMaster In Slave Out主设备输入、从设备输出NSSSlave Select从设备选择低电平有效以STM32F103C8T6为例其SPI1外设的默认引脚映射为PA4 - NSSPA5 - SCKPA6 - MISOPA7 - 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必须严格匹配。常见的模式有模式0CPOL0CPHA0SCK空闲低电平第一个边沿采样模式3CPOL1CPHA1SCK空闲高电平第二个边沿采样2.2 CubeMX配置详解使用STM32CubeMX进行初始化配置时主从设备需要分别设置主设备配置在Connectivity选项卡中启用SPI1模式选择Full-Duplex Master硬件NSS选择Disable使用软件控制参数设置Prescaler根据系统时钟和所需速率选择分频系数CPOL/CPHA根据从设备要求选择First Bit通常选择MSB FirstCRC 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的延时或者通过读取从设备的状态寄存器确认其准备就绪。