1. SPI协议概述

1.1 什么是SPI?

SPI(Serial Peripheral Interface)是由摩托罗拉公司于1980年代提出的同步串行通信协议,主要用于短距离高速芯片间通信。作为四线制全双工通信协议,它以简单的硬件实现和高效的传输速率著称,广泛应用于存储器、传感器、显示模块等嵌入式设备中。

典型应用场景

  • 微控制器与Flash存储器通信(如W25Q128)
  • 触摸屏控制器数据传输
  • 数字信号处理器与ADC/DAC模块连接
  • TFT液晶屏驱动控制
  • 1.2 核心特性对比

    特性 SPI I2C UART
    通信方式 全双工 半双工 全双工
    拓扑结构 点对点/主从 多主多从 点对点
    最大速率 100Mbps+ 3.4Mbps 115200bps
    信号线数量 4 2 2
    寻址方式 硬件片选 软件地址 无地址

    https://i3.wp.com/img-blog.csdnimg.cn/20210720172318888.png


    2. SPI物理层详解

    2.1 四线制信号定义

    1. MOSI (Master Output Slave Input)
    2. 主设备数据输出线
    3. 传输方向固定:主→从
    4. 典型应用:发送控制指令或写入数据
    5. MISO (Master Input Slave Output)
    6. 从设备数据输出线
    7. 传输方向固定:从→主
    8. 注意:同一时刻只能有一个从设备驱动该线路
    9. SCLK (Serial Clock)
    10. 主设备产生的同步时钟
    11. 频率范围:通常1MHz-50MHz(具体取决于器件)
    12. 时钟相位和极性可配置(详见第4章)
    13. SS/CS (Slave Select)
    14. 低电平有效的片选信号
    15. 每个从设备独立拥有CS线
    16. 多从机系统需要多个GPIO控制

    2.2 硬件连接注意事项

  • 总线长度限制:一般不超过30cm
  • 上拉电阻:根据器件需求添加(通常4.7kΩ-10kΩ)
  • 信号完整性:高速传输时需考虑阻抗匹配
  • 多从机连接方式:
  • 独立片选(推荐):每个从机单独CS线
  • 菊花链:数据级联传输(需器件支持)

  • 3. SPI协议层深度解析

    3.1 通信时序模型

    c

    Copy

    // 典型SPI数据传输伪代码
    void SPI_Transfer(uint8_t *txData, uint8_t *rxData, int length)
    {
        CS_LOW(); // 起始信号
        
        for(int i=0; i<length; i++){
            // 时钟边沿触发数据交换
            for(int bit=7; bit>=0; bit--){
                MOSI = (txData[i] >> bit) & 0x01;
                SCLK_TOGGLE();
                rxData[i] |= (MISO << bit);
                SCLK_TOGGLE();
            }
        }
        
        CS_HIGH(); // 停止信号
    }
    

    3.2 关键时序参数

    参数 描述 典型值
    t_SCLK 时钟周期 20ns@50MHz
    t_SU 数据建立时间 5ns
    t_HOLD 数据保持时间 3ns
    t_CS2CLK CS有效到第一个时钟边沿的延迟 10ns

    https://i3.wp.com/img-blog.csdnimg.cn/20210720172536907.png


    4. SPI工作模式详解

    4.1 CPOL与CPHA组合模式

    c

    Copy

    typedef enum {
        SPI_MODE0 = 0,  // CPOL=0, CPHA=0
        SPI_MODE1,      // CPOL=0, CPHA=1
        SPI_MODE2,      // CPOL=1, CPHA=0
        SPI_MODE3       // CPOL=1, CPHA=1
    } SPI_Mode;
    
    4.1.1 模式0(最常用)
  • 时钟空闲状态:低电平
  • 数据采样时刻:上升沿
  • 应用案例:大多数SPI Flash存储器
  • 4.1.2 模式3
  • 时钟空闲状态:高电平
  • 数据采样时刻:下降沿
  • 应用案例:某些型号的SD卡
  • 4.2 模式选择实践建议

    1. 严格参照器件手册确定工作模式
    2. 使用逻辑分析仪验证实际波形
    3. 当通信异常时首先检查模式设置
    4. 注意不同厂家对模式的命名差异

    5. STM32的SPI外设配置

    5.1 初始化代码示例

    c

    Copy

    void SPI1_Init(void)
    {
        GPIO_InitTypeDef GPIO_InitStruct = {0};
        SPI_HandleTypeDef hspi1 = {0};
    
        // 时钟使能
        __HAL_RCC_SPI1_CLK_ENABLE();
        __HAL_RCC_GPIOA_CLK_ENABLE();
    
        // MOSI(PA7), MISO(PA6), SCLK(PA5)
        GPIO_InitStruct.Pin = GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7;
        GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
        GPIO_InitStruct.Pull = GPIO_NOPULL;
        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
        GPIO_InitStruct.Alternate = GPIO_AF5_SPI1;
        HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
    
        // SPI参数配置
        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;
        hspi1.Init.NSS = SPI_NSS_SOFT;
        hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_4;
        hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
        HAL_SPI_Init(&hspi1);
    }
    

    5.2 关键配置参数解析

    1. BaudRatePrescaler
    2. 计算公式:SCLK = APB Clock / (2 * prescaler)
    3. 常用值:2分频(高速模式)、8分频(常规模式)
    4. DataSize
    5. 支持8/16位数据帧
    6. 注意对齐16位访问(使用__packed关键字)
    7. NSS管理
    8. 硬件模式:自动片选(需配置NSS引脚)
    9. 软件模式:手动控制GPIO(推荐)

    6. SPI与I2C的对比选择

    6.1 协议特性对比

    对比维度 SPI I2C
    传输速率 高速(50MHz+) 中速(400kHz-1MHz)
    引脚资源 4线+N*CS 2线
    寻址方式 硬件片选 7/10位地址
    拓扑复杂度 多CS线导致布线复杂 总线式易于扩展
    功耗 较高(持续时钟) 较低(时钟拉伸)

    6.2 选型建议

    选择SPI当

  • 需要高速数据传输
  • 系统对实时性要求高
  • 通信距离短(<30cm)
  • 主设备引脚资源充足
  • 选择I2C当

  • 设备数量多且布线空间有限
  • 通信速率要求不高
  • 需要热插拔支持
  • 低功耗设计场景

  • 7. SPI实战应用案例

    7.1 OLED显示屏驱动

    c

    Copy

    // SSD1306写命令函数
    void OLED_WriteCmd(uint8_t cmd)
    {
        OLED_CS_LOW();
        OLED_DC_CMD();  // 命令模式
        HAL_SPI_Transmit(&hspi1, &cmd, 1, 100);
        OLED_CS_HIGH();
    }
    
    // 初始化序列示例
    const uint8_t init_seq[] = {
        0xAE, // 关闭显示
        0xD5, 0x80, // 设置时钟分频
        0xA8, 0x3F, // 设置多路复用率
        // ...其他初始化命令
    };
    
    void OLED_Init(void)
    {
        for(int i=0; i<sizeof(init_seq); i++){
            OLED_WriteCmd(init_seq[i]);
        }
    }
    

    7.2 FLASH存储器读写

    c

    Copy

    #define W25Q_CMD_READ  0x03
    #define W25Q_CMD_WRITE 0x02
    
    void W25Q_ReadData(uint32_t addr, uint8_t *buffer, uint16_t len)
    {
        uint8_t cmd[4] = {
            W25Q_CMD_READ,
            (addr >> 16) & 0xFF,
            (addr >> 8) & 0xFF,
            addr & 0xFF
        };
        
        W25Q_CS_LOW();
        HAL_SPI_Transmit(&hspi1, cmd, 4, 100);
        HAL_SPI_Receive(&hspi1, buffer, len, 1000);
        W25Q_CS_HIGH();
    }
    

    8. 常见问题排查指南

    8.1 通信失败常见原因

    1. 模式配置错误(占故障的60%以上)
    2. 检查CPOL/CPHA设置
    3. 使用逻辑分析仪捕获实际波形
    4. 时序问题
    5. 确保CS信号有效时间足够
    6. 调整时钟分频系数
    7. 硬件连接故障
    8. 检查线路短路/断路
    9. 确认电压电平匹配

    8.2 调试技巧

    1. 使用示波器测量关键信号:
    2. SCLK频率是否符合预期
    3. MOSI/MISO数据是否对齐时钟边沿
    4. CS信号的有效时间
    5. 分步验证:
    6. 先测试单字节传输
    7. 验证主从设备各自独立工作
    8. 添加软件延时辅助调试

    9. 未来发展趋势

    1. 增强型SPI协议
    2. QSPI(四线制,带宽翻倍)
    3. OSPI(Octal SPI,八线制)
    4. HyperBus(混合协议)
    5. 自动化配置技术
    6. 基于AI的自动模式识别
    7. 动态时钟调整技术
    8. 安全增强
    9. 硬件加密引擎集成
    10. 时序随机化防窃听

    10. 总结与建议

    通过本文的系统讲解,我们深入剖析了SPI协议的各个技术细节。在实际项目开发中,建议:

    1. 建立标准化的SPI驱动框架
    2. 编写完善的错误检测和恢复机制
    3. 使用版本控制管理设备配置参数
    4. 定期进行总线信号完整性测试

    随着物联网设备的爆发式增长,SPI作为经典的高速通信协议,仍将在嵌入式领域发挥重要作用。掌握其核心原理并积累实战经验,是嵌入式工程师的必备技能。

    作者:四代目 水门

    物联沃分享整理
    物联沃-IOTWORD物联网 » STM32 SPI通信协议详解

    发表回复