可实现STM32间稳定可靠的SPI通信。实际项目需根据具体需求调整缓冲区大小、传输模式和错误处理策略。建议先通过逻辑分析仪验证基础时序,再逐步优化性能参数。
一、硬件连接规范
1. 标准4线SPI连接
主机(STM32F103C8T6) 从机(STM32F103ZET6)
-------------------------------
SCK → SCK(PA5→PA5)
MOSI → MOSI(PA7→PA7)
MISO ← MISO(PA6←PA6)
NSS → NSS(PA4→PA4)
GND → GND
3.3V → 3.3V
2. 关键参数匹配
| 参数 | 主机配置 | 从机配置 |
|---|---|---|
| SPI模式 | 主模式(Master) | 从模式(Slave) |
| 时钟极性 | CPOL=1(空闲高电平) | CPOL=1 |
| 时钟相位 | CPHA=1(边沿采样) | CPHA=1 |
| 数据位宽 | 8位 | 8位 |
| 片选方式 | 软件控制(NSS=Soft) | 硬件控制(NSS=Hard) |
二、软件实现方案
1. HAL库配置流程
// 主机初始化(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; // 8位数据hspi1.Init.CLKPolarity = SPI_POLARITY_HIGH; // CPOL=1hspi1.Init.CLKPhase = SPI_PHASE_1EDGE; // CPHA=1hspi1.Init.NSS = SPI_NSS_SOFT; // 软件片选hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2; // 36MHz/2=18MHzhspi1.Init.FirstBit = SPI_FIRSTBIT_MSB; // 高位优先HAL_SPI_Init(&hspi1);
}// 从机初始化
void MX_SPI2_Init(void) {hspi2.Instance = SPI2;hspi2.Init.Mode = SPI_MODE_SLAVE; // 从模式hspi2.Init.Direction = SPI_DIRECTION_2LINES; hspi2.Init.DataSize = SPI_DATASIZE_8BIT;hspi2.Init.CLKPolarity = SPI_POLARITY_HIGH;hspi2.Init.CLKPhase = SPI_PHASE_1EDGE;hspi2.Init.NSS = SPI_NSS_HARD; // 硬件片选hspi2.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2;hspi2.Init.FirstBit = SPI_FIRSTBIT_MSB;HAL_SPI_Init(&hspi2);
}
2. 数据传输模式对比
| 模式 | 主机代码示例 | 从机代码示例 | 适用场景 |
|---|---|---|---|
| 阻塞模式 | HAL_SPI_Transmit(&hspi1, data, 1, 1000); |
HAL_SPI_Receive(&hspi2, buffer, 1, 1000); |
简单单次通信 |
| 中断模式 | HAL_SPI_Transmit_IT(&hspi1, data, 1); |
HAL_SPI_Receive_IT(&hspi2, buffer, 1); |
实时响应需求 |
| DMA模式 | HAL_SPI_Transmit_DMA(&hspi1, data, 100); |
HAL_SPI_Receive_DMA(&hspi2, buffer, 100); |
大数据量传输 |
三、关键算法实现
1. CRC校验(增强可靠性)
// CRC16-CCITT计算
uint16_t SPI_CRC16(uint8_t *data, uint16_t len) {uint16_t crc = 0xFFFF;for(uint16_t i=0; i<len; i++) {crc ^= (uint16_t)data[i] << 8;for(uint8_t j=0; j<8; j++) {if(crc & 0x8000) crc = (crc << 1) ^ 0x1021;else crc <<= 1;}}return crc;
}// 发送带CRC的数据
void Send_With_CRC(uint8_t *data, uint16_t len) {uint16_t crc = SPI_CRC16(data, len);HAL_SPI_Transmit(&hspi1, data, len, 1000);HAL_SPI_Transmit(&hspi1, (uint8_t*)&crc, 2, 1000);
}
2. 双缓冲DMA配置
// 主机DMA配置(STM32F103)
void DMA_SPI_Config() {DMA_InitTypeDef DMA_InitStruct = {0};// 发送通道配置DMA_InitStruct.DMA_Channel = DMA_Channel_3; // SPI1_TX对应DMA1_CH3DMA_InitStruct.DMA_DIR = DMA_DIR_MemoryToPeripheral;DMA_InitStruct.DMA_BufferSize = 1024;DMA_InitStruct.DMA_PeripheralInc = DMA_PINC_DISABLE;DMA_InitStruct.DMA_MemoryInc = DMA_MINC_ENABLE;HAL_DMA_Init(&DMA_InitStruct);// 接收通道配置DMA_InitStruct.DMA_Channel = DMA_Channel_2; // SPI1_RX对应DMA1_CH2DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralToMemory;HAL_DMA_Init(&DMA_InitStruct);
}// 启动DMA传输
HAL_DMA_Start_IT(htim1.Instance, (uint32_t)data_tx, (uint32_t)data_rx, 1024);
HAL_SPI_Transmit_DMA(&hspi1, data_tx, 1024);
HAL_SPI_Receive_DMA(&hspi1, data_rx, 1024);
四、扩展应用场景
-
传感器网络
- 主机轮询多个从机(通过GPIO模拟多片选)
void Select_Slave(uint8_t slave_id) {switch(slave_id) {case 0: HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET); break;case 1: HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET); break;} } -
固件升级
- 通过SPI传输Bootloader数据
void SPI_Firmware_Update(uint8_t *fw_data, uint32_t size) {while(size--) {HAL_SPI_Transmit(&hspi1, fw_data++, 1, 1000);HAL_SPI_Receive(&hspi1, ack_buf, 1, 1000); // 等待ACK} } -
高速数据采集
- 使用DMA+双缓冲实现连续采样
#define BUFFER_SIZE 2048 uint16_t adc_buffer[2][BUFFER_SIZE];void MX_DMA_Init() {HAL_DMA_Start_IT(&hdma_spi_rx, (uint32_t)adc_buffer[0], (uint32_t)adc_buffer[1], BUFFER_SIZE); }
参考代码 STM32 SPI双机通信 www.youwenfan.com/contentcnm/72372.html
五、调试工具推荐
- 逻辑分析仪 Saleae/DSView捕获SPI时序 关键参数设置:时钟频率≥2倍SPI速率
- 示波器探头 使用差分探头测量SCK/MOSI/MISO信号 探头带宽≥500MHz
- 协议分析工具 Saleae Logic 2支持SPI协议解析 自动识别主从设备交互过程
六、注意事项
-
硬件设计要点 使用屏蔽双绞线(如Cat5e网线) 总线长度≤1米(>1米需加信号中继) 电源滤波:0.1μF+10μF并联
-
软件健壮性设计
- 添加超时机制防止死锁
#define SPI_TIMEOUT 1000 HAL_StatusTypeDef status = HAL_SPI_Transmit(&hspi1, data, len, SPI_TIMEOUT); if(status != HAL_OK) {Error_Handler(); // 进入错误处理流程 }- 实现软件复位功能
void SPI_Reset() {HAL_SPI_DeInit(&hspi1);MX_SPI1_Init(); // 重新初始化 }