当前位置: 首页 > news >正文

STM32单片机学习(28) —— STM32的SPI外设

文章目录概述SPI通信的移位机制以bit为单位SPI外设框图第一部分数据通路SPI通信的数据帧格式SPI外设移位机制以字节为单位第二部分主机时钟生成器SPI通信时钟频率与传输速率第三部分主从控制与NSS管理电路硬件NSS软件NSS硬件NSS和软件NSS如何选择总结概述在上一章节 《SPI 相关概念》 中我们已经从通信协议本身出发系统讲解了 SPI 通信在时序结构上的灵活可配置性。包括比特序的选择。时钟极性CPOL也就是通信空闲状态的选择。时钟相位CPHA也就是采用时机的选择。四种时钟模式选择。至此我们已经大体了解了SPI通信的基本规则。结合我们学习I2C通信的经验距离我们实现SPI通信还有一个很重要的问题需要解决我们实现软件SPI还是实现硬件SPI呢事实上和 I2C 通信类似SPI 通信同样可以采用两种实现方式软件 SPI通过普通 GPIO 引脚由软件控制引脚电平变化手动模拟 SPI 的时序过程。硬件 SPI由单片机内部集成的 SPI 外设模块在硬件层面自动完成时序控制和数据移位。在实现I2C时两种方式我们都实现过了。对于I2C而言它们的差异在于软件I2C实现更灵活简单但通信效率低时序控制不稳定。硬件I2C实现通信效率更高时序控制稳定。但相对配置多更繁琐引脚选择也不灵活。由于I2C本身就不是高速通信协议快速模式时钟频率不过400kHz不以性能著称主打性价比所以软I2C是应用更广泛的实现。SPI以性能著称通信速率快是其最核心的优势采用软件SPI则会放弃这最大优势而且软件SPI实现起来会很繁琐。所以在实际工程应用中硬件SPI是决定的主流。在后续的SPI章节内容中我们所讲的SPI实现都是基于STM32的SPI外设实现的也就是硬件SPI后续内容不再单独强调这一点。在本章节内容中我们会讲解单片机SPI外设的核心工作原理并且还会带着大家完成SPL库相关代码的编写。我们就分两个大部分来讲解这一章节SPI通信的移位机制。STM32的SPI外设框图。主要讲解SPI框图来看一下STM32的SPI外设是如何工作的。SPI通信的移位机制以bit为单位早在SPI通信讲解的开头我们就提出关于SPI通信的重要特性SPI 通信本质上是在时钟信号驱动下主从双方移位寄存器进行同步移位操作从而完成串行数据的交换。SPI通信的过程是数据交换的过程那么SPI通信究竟如何完成这个数据交换的过程呢在单主单从的前提下《参考手册》的SPI章节为我们提供了一个简易的、移位机制示意图。如下所示我们可以把图中无关的内容去掉然后在移位寄存器中增添数据一个简化的SPI移位机制示意图。如下所示解释一下这张图主机控制SCK时钟信号线主机掌握时序SPI是严格的主从通信模型。主机每移位发送1个bit数据也同时移位接收1个bit数据。从机每移位接收1个bit数据也同时移位发送1个bit数据。在这张示意图中主机发送1个字节数据给从机从机接收这1个字节的过程也会发送1个字节给主机。总之SPI通信的过程实际上就是主从双方移位寄存器移位进而实现数据交换的过程。上面这张图双方完成移位的过程以及最终结果如下所示现在你应当可以理解什么叫做**“数据交换”**了吧当然这个图中还涉及一个小问题如果按照图中的移位寄存器向左移位的方向逐bit收发数据这是什么比特序呢当然是MSB First。如果移位的方向反过来则又变成了另一种比特序——LSB First。如此关于SPI的移位机制我们就搞懂了。所以 SPI 通信的本质是主从双方各自拥有一个移位寄存器。在时钟信号的驱动下每来一个时钟沿双方同时移出一位、移入一位SPI通信本质上是一次“数据交换”而不是单向发送。这种设计把通信简化为一个最纯粹的动作——在时钟驱动下的、互相持续移位。只要主机产生时钟数据就必须流动。每一个时钟周期都必须完成一次 bit 交换既不会浪费时钟资源也无需额外的方向控制逻辑。因此SPI 的核心思想不是“发送”或“接收”而是“同步移位交换”。SPI通信的高速就是这种结构高效、简单所带来的自然结果。下面我们要进一步来看一下单片机SPI外设是如何工作的。我们来研究一下SPI外设框图。SPI外设框图《参考手册》的SPI章节给出了单片机SPI的外设框图如下我们学STM32已经有一段时间了再加上我们前面对SPI相关知识的讲解相信已经能够看懂这张图。我们应对这种框图最好的办法还是按照功能进行切割分区然后每个分区单独进行理解。其中寄存器部分不进行单独讲解在其它功能部分中用到了我们就会提。这个框图我把它分解成三个功能区域来进行讲解。第一部分数据通路首先我们先来看一下这个图左上角部分也就是SPI外设实现移位通信过程。把图进行一下裁剪得到如下图内容释说明一下这个图STM32的SPI外设其移位寄存器的位数是可以配置的。实际上是通过配置SPI_CR1寄存器的DFFData frame format位来实现的。有两种选择可以配置成8位也可以配置成16位。也就是说基于SPI外设实现通信时可以一次性交换1个字节或2个字节数据。大多数情况下程序员处理数据都是以字节为单位的所以8位移位寄存器是最常见的选择如果选择8位移位寄存器长度那么SPI通信一次性移位、收发数据的基本长度就是1个字节。配置SPI外设时可以通过配置**“SPI_CR1寄存器的LSBFIRST位”**来确定SPI通信的比特序。图中的“发送缓冲区“其实就是我们之前讲通信时的发送数据寄存器它是一个真实存在于SPI外设当中的寄存器。图中的“接收缓冲区“其实就是我们之前讲通信时的接收数据寄存器它是一个真实存在于SPI外设当中的寄存器。图中展示的核心机制就是上一小节的中的以bit为单位的“数据移位”机制这里不再赘述。SPI通信的数据帧格式数据帧格式是我们学习串口和I2C通信时最最核心的概念没有之一。下面两张图展示了串口通信和I2C通信的数据帧格式很显然串口通信的数据帧格式最固定固定长度且每1个bit位做什么也都是固定的。I2C通信的数据帧长度虽然不固定但也仍然具有起始位、停止位、寻址字节等这些相对比较固定的格式那么关于SPISPI通信的数据帧格式到底是什么样的呢有多长啊起始位停止位有没有为了回答这个问题我们不妨先思考下面的问题就在上一章节《SPI相关概念》中我们看过下面的时序图当时我们还得到了这样的一个结论SPI通信是非常灵活的在NSS拉低有效期间主从双方在1个时钟周期内交换1bit数据有多少个时钟周期就能交换多少个bit数据。SPI通信不受固定的数据帧格式限制一次通信可以交换不固定数量个bit数据而且也不需要是必须是8整数倍个bit数据。在上一章节中我们讲过这是SPI的一个优势数据帧的格式不受限比较灵活。但是到了这一章节我们发现SPI外设的移位寄存器是8位或16位的这也就意味着使用单片机的SPI外设进行数据处理交换时以字节为基本单元或以半字2个字节为基本单元。这样灵活性不就丧失了吗这是什么情况之所以会产生这样的疑问主要的原因是混淆了SPI协议和SPI外设硬件实现上的差异从SPI通信协议上来讲NSS拉低通信时间内产生多少个时钟周期就能够收发多少bit数据。从协议上确实非常灵活没有任何限制。但到了具体硬件实现时移位寄存器总得确定一个长度那么8位1个字节16位2个字节就是最常见的选择了。也就是说SPI协议层面对于数据帧长度的限制是没有的。但到了单片机SPI外设硬件实现时处理数据的基本单元是1个字节或2个字节。所以有些人会认为配置移位寄存器的长度就是配置数据帧格式的长度这也有一定道理。把移位寄存器的长度叫做“1帧”这在很多官方文档、官方库代码中都能够见到。上面聊完了SPI数据帧的长度那么其它格式限制有吗答完全没有。为什么一直没讲就是因为没有限制。至此关于SPI通信的数据帧格式我们做以下总结从SPI通信协议层面讲SPI没有数据帧格式的概念只要符合通信的时序可以随意进行通信收发交换数据。从SPI硬件外设实现层面讲移位寄存器的长度经常被设置为8位1个字节所以很多人会认定SPI通信的“1帧”是一个字节。具体到通信的从机设备不同从机设备在通信时有不同的指令、数据以及行为约束但这本身和SPI协议没有关系。举一个通俗的例子就是全球任何国家都是有货币的你花钱的时候可以花1元、8元、2.5元、10.8元……但具体到这个国家的货币体系印纸币时总不能所有金额都印一种纸币所以就选择一些常用金额来印刷纸币。不同的国家有不同货币美国总不能花人民币这就是从机不同通信方式行为有些许区别但不是根本区别因为大家都使用货币。这是通信的软件协议和硬件实现上的差异性。扩展/思考其实讲完SPI数据帧的问题SPI为什么快就可以总结第一个核心因素了SPI通信中收发的数据几乎100%都是有效数据没有起始和停止位没有应答更没有什么寻址等乱七八糟的内容。SPI通信本身就是交换数据通信一直都是全双工的也没有切换通信方向的消耗。SPI外设移位机制以字节为单位第一次讲移位机制时我们是以bit为单位的。站在整个SPI外设的角度处理数据是以整个移位寄存器为基本单位的可以一次性处理1个字节也可以一次性处理2个字节。一次性处理1个字节是最常见的选择接下来以字节为单位看一下SPI外设的移位机制是如何运行的。这一次分析我们要带上SR寄存器中的标志位TXE和RXNE。这两个标志位大家都很熟悉了这里不再赘述。SPI外设收发处理一个字节数据的过程是这样的主机判断和等待TXE标志位置位也就是等待发送数据寄存器为空。在 TXE 1 的前提下主机将 1 个字节写入发送数据寄存器TDR该1个字节的数据进入主机移位寄存器被逐bit移除发送。与此同时从机也会逐bit发送数据主机的移位寄存器也会同步接收这些bit数据。等待主机接收数据寄存器非空当主机接收数据寄存器非空时意味着主机已经将1个字节数据完全发给从机。意味着从机已经将1个字节数据完全发给主机。主机读接收数据寄存器。如此一轮操作下来主机就完成了发送1个字节数据同时也收到1个字节数据。第二部分主机时钟生成器SPI外设框图的第二核心区域是SPI外设主机时钟生成器如下图所示SCK 引脚输出时钟信号的时钟频率是由 APB 外设总线时钟频率经过“波特率发生器”分频后得到的。解释一下这句话。第一APB外设总线的时钟频率是多少这个概念早在串口通信讲波特率时就已经详细讲过了APB外设总线的时钟频率在默认情况下APB2外设总线的时钟频率是72MHz。APB1外设总线的时钟频率是36MHz。参考下列STM32系统结构框图我们所使用的STM32F103C8T6单片机一共有两个SPI外设即SPI1和SPI2。其中SPI1挂载在APB2外设总线上SPI2挂载在APB1外设总线上。综上所述如果你使用SPI1外设那么默认情况下通信的时钟频率是分频72MHz得到的。如果你使用SPI2外设那么默认情况下通信的时钟频率是分频36MHz得到的。第二波特率发生器是什么意思SPI还有波特率的概念首先SPI一定没有波特率这个概念。在串口通信中由于是异步通信没有时钟信号统一时序所以才需要通信双方约定波特率来确定每bit数据的时长。但SPI是同步通信由时钟信号严格控制时序完全没必要使用波特率。那这里的波特率发生器是什么呢这里实际上只是一个名称的沿用就像马路上没有马波特率发生器也不是生成波特率的。波特率发生器更贴近其功能的叫法应该是“时钟生成器”它通过对APB外设总线频率进行分频最终得到SPI通信的时钟频率。第三波特率生成器的分频系数如何设定呢图上已经展示的非常清楚了SPI外设的CR1寄存器的三位BR0、BR1、BR2用于确定分频系数。具体来说如下图所示从1/2分频到1/4分频最高还可以做到1/256分频。举一个例子使用SPI1外设实现SPI通信时波特率生成器采用1/4分频那么此时通信中的时钟频率是72 / 4 18MHz那么通信的时钟频率有什么用呢SPI通信时钟频率与传输速率I2C和SPI都是同步通信通信的时序由时钟信号完全控制通信的速率则由时钟线的时钟频率决定。对于SPI通信来说假设SPI时钟频率是18MHz这意味着每秒钟有18 000 000个时钟周期。这意味着在连续传输的情况下理论上每秒钟能传输有效数据18 000 000 bit。所以数据的传输速率是18 000 000 / 8 / 1000 / 1000 2.25 MB/s这是18MHz的SPI通信在协议层面上的理论最大传输速率。在实际工程中由于各种软硬件因素影响18MHz时钟频率下SPI通信通常也能稳定在2MB/s左右的数据传输速率。这对于串行通信而言已经是一个非常快的传输速率了。相比较而言串口通信比较常见的通信速率是115200波特率折算下来通信速率大约是11KB/sI2C通信的快速模式时钟频率是400kHz连续数据传输折算下来通信速率大约是44KB/s可以看到在中低速通信场景中SPI 的数据传输能力相比 UART 和 I2C 具有数量级上的优势。扩展/了解那这是为什么呢为什么SPI通信的速度优势这么明显呢首先对于串口通信这种异步通信双方没有统一时钟信号来确定时序这种设计先天就不可能是高速通信。串口通信只能靠双方约定波特率后依靠起始位对齐时间然后再逐bit的进行过采样判决结果。某种程度上来说串口通信的采样过程是一种“盲人摸象”式的采样本身出错的风险就是存在的。如果波特率设置过大那么采样难度就会显著提升采样的准确度就很难以保证了那样通信就会完全失败。而对于I2C来说它的硬件电路设计天然就是有短板的。信号线电平的上拉依赖于上拉电阻这个过程注定是很慢的。所以I2C通信一旦将时钟频率设置过高就很容易出现上拉过慢时序异常的问题。最后回到SPI通信它在通信时是完全的一对一有确定时钟信号统一时序电平上拉下拉也都是直接完成的。所以SPI通信的时钟频率可以设置得很高通信速率非常快。如果用通俗的语言来描述那这三种通信串口就是写信读信而且还要靠掐表来完成注定不可能快。I2C就像普通的马路从设计上来说就不支持让你飙车。SPI则像高速公路而且还是一对一的专线高速公路设计的目的就是跑得快。第三部分主从控制与NSS管理电路在前面的内容中我们已经分别讲清楚了 SPI 外设中的数据通路以及主机模式下的时钟生成电路。到这里为止SPI 通信中“数据怎么走”“时钟从哪来”这两件最核心的事情其实已经解决了。接下来这部分电路我们统一称之为SPI 主从控制与 NSS 管理电路。主要解决的都是一个核心问题此SPI外设在通信中究竟做主机还是做从机。当然我们早就已经确定了STM32作为通信中的主机所以我们需要把此SPI外设设置为主机模式。这部分电路内容稍多一些简化后如下图所示首先在 STM32 的 SPI 外设中由 SPI_CR1 寄存器中的 MSTR 位决定其主从身份当 MSTR 被配置为 1 时SPI 外设工作在主机模式。当 MSTR 为 0 时SPI 外设工作在从机模式。这张图中我还把上面讲过的**“时钟发生器”**电路放进来了这是因为主机模式下 SPI 会启用内部的波特率发生器产生 SCK 时钟对外输出时钟信号。从机模式下 SPI 则完全依赖外部 SCK 引脚输入的时钟信号这时候“波特率发生器”就不工作了。然后我们重点来讲一下NSS引脚及其相关电路。首先在前面的内容中我们几乎没有刻意提及单片机的 NSS 引脚。原因很简单NSS引脚是从机片选使用的STM32做主机看起来用不到NSS引脚。是这样的吗当然不是这是很明显的误解。SPI通信是一种完全没有总线仲裁、严格一主机、主机不切换的同步总线通信协议这固然带来了通信的简洁性和便利性。但也不可避免带来了一个问题如果总线上确实出现了多台设备竞争主机怎么办从SPI通信协议上来说不用考虑这个问题你不符合协议要求通信失败就是了。但从硬件设计和工程实用性的角度出发这样不管不顾就不合理了。至少需要有一种方式能够检测并暴露这种非法状态从而提醒使用者及时发现并解决问题。STM32的SPI外设其NSS引脚以及相关电路就是被设计用于确认当前SPI外设作为主机其身份是否合法。也就是说从SPI软件协议上来说不关心什么主机从机冲突问题但具体到硬件实现时从实用性出发必须防范这种问题。那么具体怎么做呢要想理解NSS相关电路首先需要理解右上角的梯形电路符号这是一个双路选择器如果SSM标志位是0则表示选择上面的0也就是把NSS引脚的输入电平送入SPI主控制电路。这种方式叫做“硬件NSS”。如果SSM标志位是1则表示选择下面的1也就是把SSI标志位确定的内部电平信号送入SPI主控制电路。这种方式叫做“软件NSS”。下面展开讲解NSS的两种模式——硬件NSS和软件NSS。硬件NSS在CR1寄存器SSM标志位设置为0的情况下此时单片机的 NSS 引脚输入的电平会被直接送入 SPI 主控制电路用于裁决判断当前STM32的主机身份是否合法。通俗的讲此时SPI外设会这么来进行判断如果我被配置为主机MSTR 1那么 NSS 引脚的输入电平必须持续保持为高电平。而如果发现NSS引脚的输入电平变成了低电平则说明总线上“有其它设备试图做主机选我做从机”如果出现NSS引脚输入低电平就表示出现了主机竞争冲突。此时SPI外设会自动置位SR状态寄存器中的MODF模式错误位也就是说NSS设置与实际情况不符合。最终SPI外设会自动清除 MSTR 位强制退出主机模式。需要注意的是使用硬件NSS必须选择引脚定义表中的固定NSS引脚不能选择随意引脚。比如引脚号引脚名称类型I/O 口电平主功能默认复用功能重定义功能14PA4I/O/PA4SPI1_NSS/USART2_CK/ADC12_IN4/软件NSS在CR1寄存器SSM标志位设置为1的情况下此时SSI标志位的值决定主控制电路的输入电平如果SSI标志位设置为0复位那么主控制电路的输入电平就固定是低电平。如果SSI标志位设置为1置位那么主控制电路的输入电平就固定是高电平。所以在MSTR 1单片机SPI外设被设置为主机时只需要设置SSM 1SSI 1就是告诉SPI外设我一直都是主机你别操心我的主机身份了我是神圣且合法的主机。当然在MSTR 1单片机SPI外设被设置为主机且SSM 1选择软件NSS的前提下如果SSI 0则依然表示模式错误。硬件NSS和软件NSS如何选择现在我们的需求是STM32始终做主机不切换。所以如果选择使用硬件NSS会有以下麻烦需要将固定的NSS引脚接入3.3V表示NSS引脚始终输入高电平从而固定STM32主机身份。但如果选择软件NSS则只需要设置三个标志位就行了设置MSTR 1SSM 1SSI 1从编程角度来说只需要一个函数调用即可解决无需任何额外操作和接线。很显然在我们当前的需求中直接用软件NSS是最合适的。总结总得来说在STM32做主机时建议大家选择软件NSS直接固定自身主机身份。如果需要STM32做从机则可以选择硬件NSS。如下图所示
http://www.gsyq.cn/news/1386930.html

相关文章:

  • DeepSeek代码质量评估实战手册:7步完成从混沌到可度量的质变跃迁
  • STM32单片机学习(27) —— SPI相关概念
  • 从安防监控到在线视频:聊聊Chrome对H265‘又爱又恨’的硬解策略与我们的日常影响
  • sudo高频指令【20260525】001篇
  • Envoy KillRequest 过滤器功能实现分析
  • 别再问OpenCV能干啥了!用Python+OpenCV 4.x,5分钟搞定你的第一个图像处理小程序
  • 别再只调API了!用Python+OpenCV实战拆解RGB到YCbCr灰度转换的每一步(附避坑指南)
  • 告别Kafka+Flink拼装:用DolphinDB重构IoT数据分析平台
  • AMD锐龙笔记本也能跑macOS?实测4800H+VMware 16安装macOS 10.14保姆级避坑指南
  • 3分钟快速上手:如何在浏览器中免费将HTML转换为Word文档
  • 你的模型结果总飘忽不定?可能是异常值在捣鬼:实战对比缩尾、截尾与RobustScaler
  • ARMv8虚拟化核心:HCRX_EL2寄存器架构与配置详解
  • ARM调试寄存器架构与内存映射访问机制详解
  • 别再让SSD越用越慢了!手把手教你检查并开启Windows/Linux/macOS的Trim功能
  • ARM CoreSight ETE调试寄存器详解与应用实践
  • 【Claude微服务架构设计黄金法则】:20年架构师亲授5大反模式避坑指南
  • 告别玄学修蓝屏:用Windows事件查看器和可靠性监视器精准诊断‘PAGE_FAULT’错误
  • SPT-AKI Profile Editor终极指南:完全掌控你的离线塔科夫存档修改
  • Unity项目里用EnhancedScroller v2.15.6做排行榜,5分钟搞定数据绑定和滚动优化
  • UE5 C++委托避坑指南:从‘崩溃’到‘优雅’,聊聊动态多播与蓝图通信的那些事儿
  • 告别瞬移眩晕!在UE5里给你的VR项目加上平滑的圆盘移动(蓝图详解)
  • CVPR 2023反无人机数据集实战:用ModelScope上的开源模型快速上手目标检测
  • 什么是吱吱OC|2026
  • 2026年05月排污泵优选:这些供货商值得一看,户外泵房/光伏太阳能供水设备/潜水排污泵,排污泵制造企业哪家好 - 品牌推荐师
  • 2026年Reddit养号指南:养号四个阶段实操
  • 保姆级教程:在CentOS 7上用达梦8搭建DCA练习环境(附ulimit、VNC、ODBC全配置)
  • 当有限元遇上游戏引擎:用Unity重现Abaqus应力云图的完整流程
  • 基于肠道菌群与机器学习的帕金森病早期诊断模型BDPM详解
  • 告别卡顿!用Potree+WebGL在浏览器里流畅查看超大规模点云(附Octree原理详解)
  • 如何用ComfyUI-SUPIR实现专业级图像超分辨率:完整实战指南