引言
前面介绍了一般SPI的基本原理及软件模拟SPI实现了W25Q32闪存存储器的读写操作,本次继续介绍STM32内部自带的SPI外设的相关内容以及常用寄存器的配置内容。
一、SPI外设基本介绍
笔者使用的是STM32F103ZET6这款型号的STM32单片机,对于STM32 的 SPI 外设,其可用作通讯的主机和从机,支持最高的 SCK 时钟频率为fpclk/2 (STM32F103 型号的芯片默认fpclk1为36MHz,fpclk2为72MHz。),完全支持 SPI 协议的4 种模式 ,数据帧长度可设置为8 位或 16 位 ,可设置数据 MSB 先行或 LSB 先行。它还支持双线全双工、单线双向以及单线模式。当然,一般用的比较多还是双线全双工模式。
STM32F103系列提供了3个SPI外设 ,其中SPI1挂在APB2总线,SPI2/3挂在APB1总线。即一款STM32上使用不同种SPI外设,可能最高频率也会有所差距。
二、SPI外设功能框图
接下来,简单介绍一下STM32中SPI外设的功能框图,如下图所示,大家可直接在手册中相应章节找到。

相信大家学完前面的SP基本知识和实战练习后,看见这张图应该并不觉得很陌生了,因为其中的核心原理在前面介绍SPI基本原理时其实用过类似的简化结构图描述过。本次,我们专门来介绍一下STM32的SPI外设功能框图,同样依据前面思路,从边缘引脚开始,逐层深入介绍。
先看左边引脚部分,一共就4个引脚,可想而知就是SPI设备具备的时钟线SCK、输入输出数据线MOSI、MISO、片选信号线NSS。
最上一个是MOSI ,即主机输出 / 从机输入 引脚,作为单向数据通路:当STM32作为SPI主机时,数据经此引脚向外 传输;若作为从机,则通过该引脚接收外部主机的指令与数据。
第二个是MISO ,与 MOSI 构成双向链路的另一通路 :主机用它接收从机的数据,从机则通过它向主机回传信息。
第三个是 SCK 引脚,作为同步时钟输出引脚,SPI的数据位遵循该引脚产生的电平跳变完成传输.SCK引脚输出的时钟信号来自内部的 "波特率发生器",可类比串口通信波特率产生器件,即用于设置通信速率的,也就是负责生成稳定的时钟频率。
第四个是NSS 引脚,即片选信号引脚。SPI总线可挂载多设备,此引脚的电平状态(通常低电平有效)决定了当前与主机通信的目标设备,未被选中的设备会处于静默状态;其控制方式灵活,既可以由硬件电路自动管理(比如通信时自动拉低选中设备,也就是直接使用主机的GPIO引脚接线控制),也能通过软件配置手动控制选通逻辑。
从引脚向内部看,首先进入的是数据寄存器对应的双缓冲区结构, 即一个数据寄存器对应俩缓冲区。SPI 的数据传输围绕两个缓冲区展开,一个是发送缓冲区,执行写操作时,数据会被存入这个缓冲区;另一个是接收缓冲区,读操作则是从这个缓冲区中获取已接收的内容。
缓冲区之后,是实现串并行转换的移位寄存器,它是数据格式转换的核心单元。发送流程中,写入发送缓冲区的数据会被传递到移位寄存器,随后移位寄存器将并行数据逐位拆分,通过 MOSI 引脚串行发送出去;接收流程则呈现逆向逻辑:外部设备经 MISO 引脚传入的串行数据,会被移位寄存器按时钟节拍逐位接收、重组为并行数据,待一帧数据接收完成后,数据被转存至接收缓冲区,供系统读取。

此单元中还设有LSBFIRST 控制位,可定义数据帧格式(高位先行,还是低位先行)。

进一步深入内部,是 SPI 的控制核心部分,由波特率发生器、主控制电路及配置寄存器共同构成。
波特率发生器作为 SCK 时钟的信号来源,当 STM32 作为主机时,它由SPI_CR1寄存器中 BR [2:0] 位的配置,生成分频后的时钟信号,一般不会将时钟频率配置最高即总线时钟的一半,不然过于极限;若 STM32 为从机,时钟则由外部主机提供,此发生器暂不工作。

主控制电路则是 SPI 的逻辑调度组件,它解析 SPI_CR1 寄存器中的配置位,例如 MSTR 位定义主机 / 从机模式、CPOL/CPHA 位定义时钟的极性与相位、SSM 位定义 NSS 引脚的硬件 / 软件控制方式、LSB FIRST即数据低位先行控制位、SPE位即SPI的使能开启位,并将这些配置转化为实际的通信行为,同时协调各模块的时序配合。

之所以片选引脚控制分为硬件和软件控制两种方式,可能是全面考虑。
所谓硬件 控即直接从机片选信号引脚通过连线接到主机上,主机利用GPIO口输出高低电平进行芯片选择,显然这一般更常用于从机,毕竟更方便供主机控制;
所谓软件 则是直接由软件置位完成片选信号的控制,这样无需接线,更适合主机自身需要控制片选线的场景,当然此时片选可能多置输出,比如主机片选线作为总线挂载其他设备的片选,主机一键拉低自身片选进而同时给多设备进行数据交换,这也是SPI外设的一种广播发送模式。
最后还有一部分即SPI_SR状态寄存器部分,用于管理SPI通信的一些状态。SR状态寄存器中的BSY 位标识 SPI 是否处于忙状态、TXE 位指示发送缓冲区是否为空、RXNE 位提示接收缓冲区是否不为空即是否准备好进行读取,这些都是进行数据交换时相对重要的内容。

除此之外,若出现接收溢出(OVR 位)、CRC 校验错误(CRCERR 位)等异常,寄存器也会实时设置状态位,便于系统及时处理。其中,CRC 校验模块可通过 SPI_CR1 寄存器的 CRC_EN 位使能,它会自动为发送数据生成校验值,并在接收端核对数据完整性;而 BIDI_MODE 位则支持将 MOSI 与 MISO 合并为单引脚的双向通信模式,以适配不同的硬件简化场景,这些了解即可。
三、SP I外设的片选控制
STM32 与 Flash 进行SPI通信时的片选控制及 NSS 引脚使用主要有三种情况:
1、Flash 片选的控制方式
Flash 的 CS 引脚固定连接到 STM32 的某一通用 GPIO 引脚(如 PC13),无论采用软件模拟 SPI 还是 硬件 SPI,均通过该 GPIO 引脚的高低电平切换来控制 Flash 片选,与 STM32 SPI 模块自身的 NSS 引脚无关。
2、主设备模式下 NSS 引脚的处理
当 STM32 作为 SPI 主设备时,NSS 引脚不用于 Flash 片选,有两种常用处理方式:
- 硬件方式:将 NSS 引脚直接接 3.3V 高电平,使其保持非选中状态,确保 STM32 稳定工作在主设备模式。
- 软件方式:配置 SPI_CR1 寄存器,置位 SSM(软件从设备管理) 和 SSI(内部从设备选择) 位,在软件层面拉高 NSS 信号,无需额外硬件接线。
3、NSS 引脚的特殊应用场景
可通过设置 SPI_CR2 寄存器的 SSOE 位,将 NSS 引脚配置为输出模式,在主模式下实现对多个从设备的片选信号广播,适用于 SPI 广播工作模式。


因此,如上图。在笔者开发板上,实际主机STM32的片选引脚是没有进行硬件接线的,因此选择软件控制,并拉高即可,然后从机芯片W25Q32的片选引脚仍是由主机32的GPIO引脚PC13进行控制,属于硬件控制方式。
四、常用寄存器介绍
从前面框图来看,STM32 SPI外设的配置主要围绕几个核心寄存器展开:SPI_CR1(控制寄存器1)、SPI_CR2(控制寄存器2)、SPI_SR(状态寄存器)和SPI_DR(数据寄存器)。

4.1 SPI_CR1寄存器
其中,SPI_CR1寄存器承担了大部分基本工作模式的配置:

-
MSTR (Master Selection):必须置1,将STM32配置为主设备模式,以控制Flash。

-
BR (Baud Rate Control):用于设置SPI通信的波特率分频因子。例如,对于连接到APB2总线的SPI1(72MHz时钟),选择四分频(BR位设置为001)可得到18MHz的SPI时钟,满足Flash通常的频率要求,同时一般考虑从设备传输速率限制,也不会将通信频率设置到极限,一般十几MHz的频率就够了,后续HAL库实现时就会有所体会。

-
CPOL (Clock Polarity) 和 CPHA (Clock Phase):共同决定SPI的时钟极性和相位,定义了数据采样和时钟跳变沿的关系。通常选择模式0(CPOL=0, CPHA=0),即空闲时时钟为低电平,在第一个时钟跳变沿(上升沿)采样数据。

-
DFF (Data Frame Format):选择数据帧格式,可配置为8位(置0)或16位(置1)。通常使用8位数据帧,即每次传输一个字节。

-
LSBFIRST (LSB First):控制数据传输是高位先行(默认,置0)还是低位先行(置1)。

-
SSM (Software Slave Management) 和 SSI (Internal Slave Select):如前所述,SSM置1启用软件NSS管理,SSI置1使NSS信号在内部拉高,确保主设备模式。


-
SPE (SPI Enable) :所有配置完成后,置1以使能SPI外设模块。

4.2 SPI_CR2寄存器
SPI_CR2寄存器主要用于配置中断使能和DMA功能:

-
TXIE, RXNEIE, ERRIE:分别用于使能发送缓冲区空、接收缓冲区非空和错误中断。
-
TXDMAEN, RXDMAEN:用于使能发送和接收的DMA通道,以实现更高效的数据传输。
-
SSOE (SS Output Enable):用于在主模式下使能NSS引脚作为输出,实现广播模式。
由于本次学习不会使用,故这里不过多赘述,大家需要使用时,可自行在手册中查阅。
4.3 SPI_DR寄存器
SPI_DR (Data Register) 寄存器用于数据的读写操作。对应框图中的发送接收缓冲区,向DR写入数据会触发数据发送,从DR读取数据则获取接收到的数据。

4.4 SPI_SR寄存器
SPI_SR (Status Register) 寄存器包含了各种状态标志:
-
TXE (Transmit Buffer Empty):发送缓冲区为空标志,置1表示可以写入新数据,由硬件自动置位,读取DR时一般可清除该位。

-
RXNE (Receive Buffer Not Empty):接收缓冲区非空标志,置1表示有新数据待读取由硬件自动置位,读取DR时一般可清除该位。

-
BSY (Busy Flag):忙碌标志,置1表示SPI正在进行通信。

-
OVR (Overrun Flag):溢出标志,表示接收缓冲区溢出,基础学习不会使用。

-
MODF (Mode Fault) :模式错误标志。基础学习不会使用。

-
CRCERR (CRC Error Flag) :CRC校验错误标志。基础学习不会使用。

该寄存器主要使用的是忙标志、接收非空标志以及发送为空标志,根据前面软件模拟SPI实战经验,这三个标志将用于读写操作循环等待过程。
4.5 相关高级配置简述
除了基本的SPI通信功能,STM32的SPI模块还支持一些高级特性:

-
循环冗余校验 (CRC):SPI模块内置了CRC校验功能,通过配置CRCPR(CRC多项式寄存器)、TXCRCR(发送CRC寄存器)和RXCRCR(接收CRC寄存器)以及CR1寄存器中的CRCEN和CRCNEXT位,可以启用和管理CRC校验。这在需要数据完整性验证的场合非常有用,例如文件传输。
-
I2S(Inter-IC Sound)协议支持:STM32的SPI接口可以配置为支持I2S协议,这是一种用于传输数字音频数据的串行总线协议。相关的寄存器如I2SCFGR和I2SPR用于配置I2S的工作模式,例如音频采样率、数据长度、通道选择等。这使得SPI模块在多媒体应用中具有更广泛的用途。
-
单线双向(半双工)模式:通过配置CR1寄存器中的BIDIMODE和BIDIOE位,可以将SPI模块从默认的双线双向(全双工)模式切换到单线双向(半双工)模式。在半双工模式下,SPI只使用一根数据线进行收发,但不能同时进行。BIDIOE位用于控制数据线的方向(输出或输入)。
-
只接收模式:通过设置CR1寄存器中的RXONLY位,可以将SPI模块配置为只接收模式,即只接收数据而不发送数据,这在某些特定应用中可以简化通信逻辑。这些高级功能虽然在日常的Flash读写中不常用,但它们展示了STM32 SPI模块的强大灵活性和广泛适用性,能够满足各种复杂的通信需求。
4.6 主机配置一般流程
根据手册描述可知,当SPI设备作为主机时,配置流程如下:

其实,手册描述已经极为清楚,所以这里再精简总结一下这个配置步骤,实际上就是后续初始化硬件SPI核心部分的步骤:设置主模式-时钟波特率配置-时钟极性和相位-数据帧格式(宽度、传输顺序)-片选配置-启动SPI。大家根据整个SPI外设框图从外到内来看的话,也基本能够得出这个步骤,然后配置对应的寄存器位就完事了,这里就不在赘述。
4.7 读写操作寄存器总结
SPI外设进行数据收发时属于交换数据的过程,因此根据前面经验,通常会封装一个"交换字节数据"的函数(或者根据DFF配置,交换16位即2字节数据)。这里根据前面实战经验及这里寄存器配置方式总结一下:
(1)发送数据流程:
判断发送缓冲区是否为空:通过查询SPI_SR寄存器中的TXE位。当TXE为1时,表示发送缓冲区为空,可以写入新数据。
写入数据:将待发送的字节数据写入SPI_DR寄存器。一旦数据写入,SPI硬件会自动开始将数据移位发送出去。
(2)接收数据流程:
等待接收缓冲区非空:通过查询SPI_SR寄存器中的RXNE位。当RXNE为1时,表示接收缓冲区已接收到一个完整的数据帧(例如一个字节),可以读取。
读取数据:从SPI_DR寄存器中读取接收到的字节数据。读取操作会自动清零RXNE标志位。
在一个典型的交换字节函数中,发送和接收是同步进行的。发送一个字节的同时,SPI模块也会接收一个字节。因此,在发送数据后,需要等待接收缓冲区非空,然后读取接收到的数据。这种操作模式确保了数据的双向同步交换。例如,在Flash读操作中,发送读命令后,需要不断发送"哑数据"(dummy byte)即没有意义随意发送的数据,同时接收Flash返回的数据。参考代码如下:
cpp
uint8_t SPI_SwapByte(uint8_t byte_sended)
{
/* 1. 等待发送缓冲区为空 */
while (!(SPI1->SR & SPI_SR_TXE))
{}
/* 2. 把数据写入到数据寄存器 */
SPI1->DR = byte_sended;
/* 3. 等待接收缓冲区非空 */
while (!(SPI1->SR & SPI_SR_RXNE))
{}
/* 4. 返回读到的字节数据 */
return SPI1->DR;
}
注:考虑到手册中关于SPI外设的寄存器配置较为简单,且笔者认为该部分手册描述也较为清楚,因此没有做过多描述,大家若仍存在基础内容和配置的疑问,可在评论区讨论和自行查阅资料解决。
五、小结
本文介绍了STM32 SPI外设的基本原理与配置方法。主要内容包括:
1)SPI外设功能框图解析,详细说明MOSI、MISO、SCK和NSS引脚的功能及内部数据缓冲区、移位寄存器等核心模块;
2)片选控制的三种实现方式,重点分析主设备模式下NSS引脚的硬件/软件配置方法;
3)关键寄存器(CR1、CR2、SR、DR)的功能说明及典型配置流程;
4)数据交换的实现机制,给出了基于寄存器操作的字节交换函数示例。
以上便是本次文章的所有内容,欢迎各位朋友在评论区讨论,本人也是一名初学小白,愿大家共同努力,一起进步吧!
鉴于笔者能力有限,难免出现一些纰漏和不足,望大家在评论区批评指正,谢谢!


