系统讲解 STM32 的 SPI 实验,用STM32F103ZET6 和 HAL 库,内容将围绕:

  • SPI 原理简介

  • HAL SPI 的关键函数及寄存器解释

  • 实验流程 & 代码讲解

  • 典型应用场景

  • 错误排查方法


  • 🧠 一、SPI 原理:我如何用自己的话教会你?

    什么是 SPI?

    SPI(Serial Peripheral Interface,串行外设接口)是一种全双工主从结构同步传输的串行通信协议。

    简单说,SPI 就像一根高速的传送带,主机发数据、从机同步收,同时从机也能发数据回来。


    SPI 通信线:

    引脚 名称 方向(以主机为参考) 说明
    SCK 串行时钟线 主机输出 → 从机输入 提供数据同步时钟
    MOSI 主出从入线 主机输出 → 从机输入 主机发送数据
    MISO 主入从出线 从机输出 → 主机输入 从机发送数据
    NSS 片选信号(可选) 主机输出 → 从机输入 选中从设备,低电平有效

    SPI 特点:

  • 主机产生时钟,主机控制通信节奏

  • 数据边沿对齐(CPOL/CPHA 控制)

  • 通信速度快(MHz级别)

  • 适用于短距离高速通信


  • 🧪 二、实验内容描述

    我们现在做一个 SPI主机 实验,STM32作为主机,通过 SPI1 发送一串数据给从设备(可以是另一个 MCU,或 SPI Flash 等)。


    🔧 三、硬件连接示意

    以 SPI1 为例:

    信号 管脚(STM32F103ZET6)
    SCK PA5
    MISO PA6
    MOSI PA7
    NSS PA4(手动GPIO控制)

    📚 四、HAL库函数讲解 + 寄存器关系

    1. HAL_SPI_Init()

    函数作用:初始化 SPI 结构体并配置 SPI 寄存器

    HAL_SPI_Init(&hspi1);
    
    参数 含义
    hspi1.Instance = SPI1 指定使用 SPI1
    hspi1.Init.Mode = SPI_MODE_MASTER 主机模式
    hspi1.Init.Direction = SPI_DIRECTION_2LINES 双线全双工
    hspi1.Init.DataSize = SPI_DATASIZE_8BIT 每次传 8 bit
    hspi1.Init.CLKPolarity = SPI_POLARITY_LOW 空闲低电平
    hspi1.Init.CLKPhase = SPI_PHASE_1EDGE 第一个边沿采样
    hspi1.Init.NSS = SPI_NSS_SOFT NSS 手动控制
    hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_16 分频控制速度
    hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB 高位先发
    hspi1.Init.TIMode = SPI_TIMODE_DISABLE 禁用 TI 模式
    hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE 不用 CRC

    2. HAL_SPI_Transmit()

    发送数据(阻塞)

    HAL_SPI_Transmit(&hspi1, data, len, timeout);
    
  • data:要发的数据数组

  • len:数据长度

  • timeout:超时时间(单位:ms)


  • 3. HAL_SPI_Receive()

    接收数据(阻塞)

    HAL_SPI_Receive(&hspi1, recv_buf, len, timeout);
    

    4. HAL_SPI_TransmitReceive()

    同时收发(全双工通信)

    HAL_SPI_TransmitReceive(&hspi1, tx_data, rx_data, len, timeout);
    

    🧪 五、SPI主机发送实验 – 完整代码

    1. SPI 初始化(CubeMX 生成或手写)

    SPI_HandleTypeDef hspi1;
    
    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;
      hspi1.Init.NSS = SPI_NSS_SOFT;  // NSS由软件控制
      hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_16;
      hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
      hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
      hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
      hspi1.Init.CRCPolynomial = 10;
      if (HAL_SPI_Init(&hspi1) != HAL_OK)
      {
        Error_Handler();
      }
    }
    

    2. 主函数中测试:

    uint8_t tx_data[] = {0xAA, 0xBB, 0xCC};
    
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET); // NSS = 0,开始通信
    
    HAL_SPI_Transmit(&hspi1, tx_data, sizeof(tx_data), 1000); // 发送数据
    
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET);   // NSS = 1,结束通信
    

    📍 六、SPI寄存器背后做了什么?

    寄存器 功能解释
    CR1 SPI模式设置(主从、CPOL、CPHA、数据大小、使能等)
    SR 状态寄存器,判断 TXE(发送缓冲区空)/ RXNE(接收缓冲区非空)
    DR 数据寄存器,写入=发出,读取=接收
    CR2 中断、DMA 等扩展控制

    🎯 七、典型应用

  • 驱动外部 SPI Flash(W25Qxx)

  • 与 OLED 屏通信

  • 驱动 AD/DA 芯片

  • 通信模块(如 NRF24L01)


  • 🧰 八、常见问题排查

    问题 原因
    无法发送 NSS没有拉低、SPI没初始化成功
    收到全0或全1 没接收数据、MISO引脚浮空
    SPI总线卡死 CPOL/CPHA 配置不一致
    收发数据错位 SPI模式配置不匹配

    ✅ 总结

  • SPI 是一种主从同步全双工协议

  • STM32 使用 SPI 的关键函数是 HAL_SPI_Init()Transmit()Receive()

  • SPI 的行为受寄存器 CR1 和 SR 控制

  • 用 NSS 控制设备选通(GPIO 控制)

  • 实验中 STM32 作为主机发数据给从机

  • 作者:不如出家吧

    物联沃分享整理
    物联沃-IOTWORD物联网 » STM32 SPI 实验全面解析

    发表回复