STM32的SPI双机通信实现

简介: STM32的SPI双机通信实现

STM32的SPI双机通信实现

可实现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=1
    hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;    // CPHA=1
    hspi1.Init.NSS = SPI_NSS_SOFT;            // 软件片选
    hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2; // 36MHz/2=18MHz
    hspi1.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_CH3
    DMA_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_CH2
    DMA_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);

四、扩展应用场景

  1. 传感器网络

    • 主机轮询多个从机(通过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;
        }
    }
    
  2. 固件升级

    • 通过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
        }
    }
    
  3. 高速数据采集

    • 使用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/contentalf/72372.html

五、调试工具推荐

  1. 逻辑分析仪 Saleae/DSView捕获SPI时序 关键参数设置:时钟频率≥2倍SPI速率
  2. 示波器探头 使用差分探头测量SCK/MOSI/MISO信号 探头带宽≥500MHz
  3. 协议分析工具 Saleae Logic 2支持SPI协议解析 自动识别主从设备交互过程

六、注意事项

  1. 硬件设计要点 使用屏蔽双绞线(如Cat5e网线) 总线长度≤1米(>1米需加信号中继) 电源滤波:0.1μF+10μF并联

  2. 软件健壮性设计

    • 添加超时机制防止死锁
    #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();  // 重新初始化
    }
    
相关文章
|
开发者
【经典案例】使用HAL库配置STM32F407的SPI外设
在嵌入式系统开发中,STM32F407是一款广泛应用的微控制器,而SPI(Serial Peripheral Interface)是一种常用的通信接口。本文将详细介绍如何使用STM32的硬件抽象层(HAL)库配置STM32F407的SPI外设,并提供完整的代码示例。
2029 1
|
存储 缓存 芯片
STM32标准库SPI通信协议与W25Q64-2
STM32标准库SPI通信协议与W25Q64
|
传感器 编解码 API
【STM32开发入门】温湿度监测系统实战:SPI LCD显示、HAL库应用、GPIO配置、UART中断接收、ADC采集与串口通信全解析
SPI(Serial Peripheral Interface)是一种同步串行通信接口,常用于微控制器与外围设备间的数据传输。SPI LCD是指使用SPI接口与微控制器通信的液晶显示屏。这类LCD通常具有较少的引脚(通常4个:MISO、MOSI、SCK和SS),因此在引脚资源有限的系统中非常有用。通过SPI协议,微控制器可以向LCD发送命令和数据,控制显示内容和模式。
595 0
|
存储 芯片 内存技术
STM32F0单片机快速入门十 用 SPI HAL 库读写W25Q128
STM32F0单片机快速入门十 用 SPI HAL 库读写W25Q128
STM32 OLED显示屏--SPI通信知识汇总
在此功能中,采用的是四线SPI,我们在开发过程中,应该去寻扎数据手册里面的通信时序图,才能使得单片机利用四线SPI和OLED进行通信的功能操作。
STM32 OLED显示屏--SPI通信知识汇总
|
芯片 开发者
16 玩转STM32之SPI通信
16 玩转STM32之SPI通信
|
存储 芯片 数据格式
STM32第十章-SPI通讯应用
&emsp;&emsp;上一章我们讲到了IIC通讯,这一章来说一说SPI通信,同样的很多模块也用到了SPI通信,比如0.96寸的OLED模块。玩过单片机的小伙伴都知道OLED有4针的也有7针的,4针的就是IIC通信,7针的就是SPI通信。
415 0
STM32第十章-SPI通讯应用
|
芯片
STM32通过io模拟SPI与ADC124S021通信
STM32通过io模拟SPI与ADC124S021通信