ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

【嵌入式STM32】SPI总结

2026/8/14 7:13:37 拓冰建站 浏览量
【嵌入式STM32】SPI总结

1.SPI介绍

SPI 协议是由摩托罗拉公司提出的通讯协议(Serial Peripheral Interface),即串行外围设
备接口,是一种高速全双工的通信总线。它被广泛地使用在 ADC、LCD 等设备与 MCU 间,
要求通讯速率较高的场合。

1.1特点

  • 全双工通信
    主设备和从设备能够同时发送和接收数据,提高数据传输效率。
  • 同步串行通信
    通过时钟信号同步数据传输,确保数据的准确接收。
  • 主从架构
    一个主设备控制时钟和片选信号,多个从设备通过片选线选择和通信。SPI总线设计为单主机,多主机情况下需要额外硬件管理。通过多条片选线或外部解码器管理多个从设备。每个从设备通常需要独立片选线,增加I/O资源消耗。
  • 高速传输能力
    通常支持几MHz甚至几十MHz的时钟频率,适合高速数据交换。主机最大传输速率可达fPCLK/2。
  • 简单的硬件接口
    仅需4条主要信号线(SCLK、MOSI、MISO、SS),硬件设计简单。
  • 缺少标准错误检测机制
    SPI本身不提供CRC或校验功能,错误检测需上层协议实现。
  • 无仲裁机制
    主设备完全控制总线,不能自动处理总线冲突。
  • 不适合长距离通信
    由于没有差分信号和驱动能力,适合板内或短距离通信。

应用场景

  • 传感器数据采集
  • 闪存、EEPROM读写
  • LCD显示屏驱动
  • 音频DAC/ADC通信
  • 其他高速外设接口

1.2 物理连接

  • SS( Slave Select):从设备选择信号线,常称为片选信号线,也称为 NSS、CS。当主机要选择从设备时,把该从设备的 NSS 信号线设置为低电平,该从设备即被选中,即片选有效,接着主机开始与被选中的从设备进行 SPI 通讯。所以SPI 通讯以 NSS 线置低电平为开始信号,以 NSS 线被拉高作为结束信号。
  • SCK (Serial Clock):时钟信号线,用于通讯数据同步。它由通讯主机产生,决定了通讯的速率,不同的设备支持的最高时钟频率不一样,如 STM32 的 SPI 时钟频率最大为fpclk/2,两个设备之间通讯时,通讯速率受限于低速设备。主机SCK频率必须 ≤ 从设备最大支持频率。
  • MOSI(Master Output, Slave Input):主设备输出/从设备输入引脚。主机的数据从这条信号线输出,从机由这条信号线读入主机发送的数据,即这条线上数据的方向为主机到从机。传输数据时,MSB和LSB先行都是可以设置的。
  • MISO(Master Input,,Slave Output):主设备输入/从设备输出引脚。主机从这条信号线读入数据,从机的数据由这条信号线输出到主机,即在这条线上数据的方向为从机到主机。传输数据时,MSB和LSB先行都是可以设置的。

1.3 协议时序

四种模式:

下图中是模式1,空闲时SCK低电平,采样是偶边沿采样(上升和下降各算一个边沿)。

NSS、SCK、MOSI 信号都由主机控制产生,而 MISO 的信号由从机产生,主机通过该信号线读取从机的数据。MOSI 与 MISO 的信号只在 NSS 为低电平的时候才有效,在 SCK 的每个时钟周期 MOSI 和 MISO 传输一位数据。

  • 通讯的起始和停止信号
    标号①处,NSS 信号线由高变低,是 SPI 通讯的起始信号。NSS 是每个从机各自独占的信号线,当从机在自己的 NSS 线检测到起始信号后,就知道自己被主机选中了,开始准备与主机通讯。在图中的标号⑥处,NSS 信号由低变高,是 SPI 通讯的停止信号,表示本次通讯结束,从机的选中状态被取消。

  • 数据有效性、
    如图②-⑤,MOSI 及 MISO 的数据在 SCK 的上升沿期间变化输出,在 SCK 的下降沿被采样。即在 SCK 的下降沿时刻,MOSI 及 MISO 的数据有效,高电平时表示数据“1”,为低电平时表示数据“0”。SPI 每次数据传输可以 8 位或 16 位为单位,每次传输的单位数不受限制。

不同的CPOL


2.工程实践

2.1 外设介绍

在大容量产品和互联型产品上,SPI接口可以配置为支持SPI协议或者支持2S音频协议。SPI接口默认工作在SPI方式,可以通过软件把功能从SPI模式切换到I2S模式。注意在小容量和中容量产品上,不支持I2S音频协议。

  • 通讯引脚
    SCK引脚:SPI1/SPI2/SPI3芯片手册中告知,最大速率18Mbps。

NSS引脚:有两种NSS模式,软件和硬件。软件模式即可以用指定的GPIO来进行从设备片选。一般采用这种模式。
MISO/MOSI引脚:数据收发引脚,通过一个数据寄存器(DR)进行收发控制。

主模式收发流程如下: (1) 控制 NSS 信号线,产生起始信号; (2) 把要发送的数据写入到“数据寄存器 DR”中,该数据会被存储到发送缓冲区; (3) 通讯开始,SCK 时钟开始运行。MOSI
把发送缓冲区中的数据一位一位地传输出去;MISO 则把数据一位一位地存储进接收缓冲区中; (4) 当发送完一帧数据的时候,“状态寄存器
SR”中的“TXE 标志位”会被置 1,表示传输完一帧,发送缓冲区已空;类似地,当接收完一帧数据的时候,“RXNE标志位”会被置
1,表示传输完一帧,接收缓冲区非空; (5) 等待到“TXE 标志位”为 1
时,若还要继续发送数据,则再次往“数据寄存器DR”写入数据即可;等待到“RXNE 标志位”为 1
时,通过读取“数据寄存器DR”可以获取接收缓冲区中的内容。

2.2 MX配置说明

  • Mode: Full-Duplex Master
    Full-Duplex 表示SPI支持同时发送和接收数据。Master 表示该设备作为主机控制时钟和片选信号。

  • Hardware NSS Signal: Disable
    是否启用硬件自动管理NSS(片选)信号,Disable 表示由软件手动控制NSS引脚,通常用户自行控制片选信号。

  • Frame Format: Motorola
    SPI数据帧格式,STM32支持两种帧格式:
    Motorola 标准SPI协议格式,最常用。
    TI 表示Texas Instruments的特定帧格式,较少用。、

  • Data Size: 8 Bits
    每次传输的数据位宽,这里表示每个数据单元是8位(1字节)。
    SPI也支持16位或其他位宽。

  • First Bit: MSB First
    传输数据时,先发送最高有效位(MSB)还是最低有效位(LSB)。

  • Prescaler (for Baud Rate):4
    SPI时钟分频系数,用于控制SPI时钟频率。因为SPI1挂接在APB2总线上,需要除以4来保证不大于18Mbits/s

  • CPOL 时钟极性:
    SPI时钟空闲状态的电平。
    Low(0):空闲时SCK为低电平。
    High(1):空闲时SCK为高电平。

  • CPHA:
    SPI时钟采样数据的时刻。
    1 Edge (First Edge):数据在第一个时钟边沿采样。
    2 Edge (Second Edge):数据在第二个时钟边沿采样。
    CPOL和CPHA组合决定SPI模式(Mode0~Mode3)。

生成代码后,spi.c中多出的关键代码:

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;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();}

GPIO对应的代码(略)。

2.3 和FLASH通信

2.3.1 W25Q64 FLASH芯片

芯片为NOR FLASH,容量为64Mbit(8MB),其中WP 引脚可控制写保护功能,当该引脚为低电平时,禁止写入数据。我们直接接电源,不使用写保护功能。HOLD 引脚可用于暂停通讯,该引脚为低电平时,通讯暂停,数据输出引脚输出高阻抗状态,时钟和数据输入引脚无效。我们直接接电源,不使用通讯暂停功能。
同时它支持 SPI 模式 0 及模式 3,支持双线全双工,使用MSB 先行模式,支持最高通讯时钟为 104MHz,数据帧长度为 8 位。

2.3.2 片选GPIO配置


这里配置默认输出高电平,推挽输出,同时打上标签。

2.3.3 FLASH指令


2.3.4 阻塞式读写

//SPI片选voidSPI_FLASH_NSS(uint8_tnss){if(nss==0){HAL_GPIO_WritePin(FLASH_NSS_GPIO_Port,FLASH_NSS_Pin,GPIO_PIN_RESET);}else{HAL_GPIO_WritePin(FLASH_NSS_GPIO_Port,FLASH_NSS_Pin,GPIO_PIN_SET);}}//SPI收发1个字节 - 阻塞方式uint8_tSPI_FLASH_SendReadByte(uint8_tTxbyte){uint8_tRxByte=0;//数据收发HAL_SPI_TransmitReceive(&hspi1,&Txbyte,&RxByte,1,1000);returnRxByte;}

因为是全双工通信,要收也得发。

2.3.5 读取FLASH 的ID信息

来看看手册中给的读写时序图:

首先发送0x9F,然后再读写三次,获得ID信息。

代码如下:

#defineDummy_Byte0xFF//SPI-FLASH 读取FLASHIDvoidSPI_FLASH_READ_FLASHID(){//进行片选SPI_FLASH_NSS(0);//发送 JEDEC 指令SPI_FLASH_SendReadByte(0x9F);//读生产厂商uint8_tM=SPI_FLASH_SendReadByte(Dummy_Byte);//读存储器类型uint8_tT=SPI_FLASH_SendReadByte(Dummy_Byte);//读容量uint8_tC=SPI_FLASH_SendReadByte(Dummy_Byte);//片选结束SPI_FLASH_NSS(1);}

最终读取的和下面W25Q64能匹配上。

因为只是测试SPI,读写FLASH这里不再罗列,现学现卖即可。

2.3 收发Dummy数据的作用

我们看到,FLASH W25Q64的读写其实在协议层是半双工的,先发,再收,然后收的时候发送的是dummy数据,发dummy的作用:

维持时钟同步!
主设备需继续发送 dummy 数据(通常为 0x00 或固定值),目的是持续生成 SCK 时钟,让从设备有机会在 MISO 线上输出有效数据;
dummy 数据对主设备无意义,仅用于 “驱动时钟”,从设备也不会处理这些数据(协议层已约定 “命令后的数据为有效输出”)。

2.4 SPI DMA读写

SPI阻塞式读写,需反复读取 SPI 状态寄存器(SR),注意是每bit都读,经过实际测试,11M频率下,HAL库读写16bit耗时5us左右,光轮询和检查参数的耗时就有3个us多。想要提速,要么用DMA,要么用LL库。
DMA需要区分单次写还是轮询,具体参考串口的DMA控制。
DMA启动的性能大约在1-2us,还是比阻塞快很多的。

2.5 ADC采集电压

待补充。
高频率采集,如果多个SPI对应多个ADC,如何提升采集的性能呢?
如果用DMA,因为频率太高,那么如果想减少DMA中断次数,可以只启用一个SPI的DMA,比如先启动A,再启动B,B的半满中断时,记录A的DMA当前位置(因为先A后B有时间间隔),然后B满中断时,取出A和B的数据,此时是对齐的,以后也对齐了。当前必须是高速率,比如64k,如果1ms一次这种,需考虑B的满中断时,A可能那次的还没开始。