STM32 SPI通信详解:从原理到实战代码

一、SPI通信基础

1.1 什么是SPI?

SPI(Serial Peripheral Interface)是由Motorola提出的全双工同步串行通信协议,广泛用于微控制器与外围设备(Flash、传感器、显示屏等)的高速数据传输。

核心特性:

  • 全双工通信(同时收发)

  • 最高时钟频率可达数十MHz

  • 主从架构(一主多从)

  • 硬件连接简单(4线制)

  • 1.2 SPI物理接口

    信号线 全称 作用
    SCK Serial Clock 同步时钟(由主机产生)
    MOSI Master Out Slave In 主机发送,从机接收
    MISO Master In Slave Out 主机接收,从机发送
    NSS Slave Select 从机片选(低电平有效)

    二、SPI工作原理及时序

    2.1 四种工作模式

    由CPOL(时钟极性)CPHA(时钟相位)组合决定:

    模式 CPOL CPHA 特性
    0 0 0 时钟空闲低电平,第一个边沿采样
    1 0 1 时钟空闲低电平,第二个边沿采样
    2 1 0 时钟空闲高电平,第一个边沿采样
    3 1 1 时钟空闲高电平,第二个边沿采样

    关键点:

  • CPOL=0:SCK空闲时为低电平

  • CPOL=1:SCK空闲时为高电平

  • CPHA=0:在SCK第一个边沿(奇数边沿)采样数据

  • CPHA=1:在SCK第二个边沿(偶数边沿)采样数据

  • 2.2 典型时序分析(模式0)

    以主机发送0xAA(10101010)为例:

    1. NSS拉低:选中从机

    2. SCK产生脉冲:CPOL=0,空闲低电平

    3. 数据采样

    4. MOSI在SCK上升沿改变数据

    5. 从机在SCK下降沿采样数据

    6. 数据传输:高位(MSB)先发


    三、STM32 SPI外设配置

    3.1 硬件设计

    以STM32F103C8T6与W25Q64 Flash芯片通信为例:

  • SCK -> PA5

  • MOSI -> PA7

  • MISO -> PA6

  • NSS -> PA4(软件控制)

  • 3.2 CubeMX配置步骤

    1. 启用SPI1(模式选择为全双工主模式)

    2. 配置参数:

    3. 时钟分频(PCLK2为72MHz时,分频256得281.25kHz)

    4. 数据宽度:8位

    5. 模式选择:Mode 0(CPOL=0, CPHA=0)

    6. NSS选择:软件模式

    7. 生成代码

    3.3 关键代码解析

    初始化代码
    SPI_HandleTypeDef hspi1;
    
    void 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;
        hspi1.Init.NSS = SPI_NSS_SOFT;
        hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_256;
        hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
        HAL_SPI_Init(&hspi1);
    }
    数据收发函数
    // 发送单字节并接收返回
    uint8_t SPI_TransmitReceive(uint8_t data)
    {
        uint8_t rx_data;
        HAL_SPI_TransmitReceive(&hspi1, &data, &rx_data, 1, 100);
        return rx_data;
    }
    
    // 读取Flash芯片ID(以W25Q64为例)
    uint16_t W25Q_ReadID(void)
    {
        uint8_t cmd = 0x9F; // JEDEC ID命令
        uint8_t id[3];
        
        HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET); // 拉低NSS
        HAL_SPI_Transmit(&hspi1, &cmd, 1, 100);
        HAL_SPI_Receive(&hspi1, id, 3, 100);
        HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET); // 释放NSS
        
        return (id[1] << 8) | id[2]; // 返回厂商ID和设备ID
    }

    四、调试技巧与常见问题

    4.1 示波器诊断

  • 检测SCK波形:确认频率和极性是否符合预期

  • 观察MOSI/MISO:验证数据发送是否正确

  • 检查NSS信号:确保片选信号正常拉低

  • 4.2 常见问题排查

    1. 无数据通信

    2. 检查硬件连接是否短路/断路

    3. 验证SPI模式(主/从)设置是否正确

    4. 确认NSS信号是否有效

    5. 数据错位

    6. 检查MSB/LSB设置

    7. 确认时钟相位设置是否匹配从机要求

    8. 通信速率问题

    9. 降低波特率测试

    10. 检查电源稳定性


    五、进阶应用:DMA+SPI

    5.1 使用DMA提升效率

    // 启用DMA传输
    void SPI_DMA_Transmit(uint8_t *pData, uint16_t Size)
    {
        HAL_SPI_Transmit_DMA(&hspi1, pData, Size);
        while (hspi1.State != HAL_SPI_STATE_READY); // 等待传输完成
    }
    
    // 接收数据
    void SPI_DMA_Receive(uint8_t *pData, uint16_t Size)
    {
        HAL_SPI_Receive_DMA(&hspi1, pData, Size);
        while (hspi1.State != HAL_SPI_STATE_READY);
    }

    六、总结

    本文详细解析了SPI协议的工作原理,并通过STM32实战代码演示了通信流程。实际开发中需注意:

    1. 严格匹配主从设备的SPI模式

    2. 合理选择通信速率(兼顾稳定性和速度)

    3. 复杂场景建议使用DMA减轻CPU负担


    创作不易,如果本文对您有帮助,请点赞收藏支持!
    关于SPI的更多问题,欢迎在评论区留言讨论

    作者:DOMINICHZL

    物联沃分享整理
    物联沃-IOTWORD物联网 » STM32 SPI通信详解:从原理到实战代码

    发表回复