STM32—SPI通信外设

news2025/1/21 3:00:20

1.SPI外设简介

  • STM32内部集成了硬件SPI收发电路,可以由硬件自动执行时钟生成、数据收发等功能,减轻CPU的负担
  • 可配置8位/16位数据帧、高位先行/低位先行
  • 时钟频率:fpclk/(2,4,8,16,32,64,128,256)
  • 支持多主机模型、主或从操作
  • 可精简为半双工/单工通信
  • 支持DMA
  • 兼容I2S协议
  • STM32F103C8T6 硬件SPI资源:SPI1(APB2外设)、SPI2(APB1外设)

2.SPI框图

我们可以大致把它分成两部分,左上角就是数据寄存器和移位寄存器打配合的过程,这个和串口、12C那里的设计思路都是异曲同工的,主要是为了实现连续的数据流,一个个数据前仆后继的一个效果,然后右下角就是一些控制逻辑了,寄存器的哪些位,控制哪些部分,会产生哪些效果,这个可以通过手册的寄存器描述来得知

首先左上角,核心部分,就是这个移位寄存器,右边的数据低位,一位一位地,从MOSI移出去,然后MISO的数据,一位一位地,移入到左边的数据高位,显然移位寄存器应该是一个右移的状态,所以目前图上表示的是低位先行的配置,对应右下角有一个LSBFIRST控制位,这一位可以控制是低位先行还是高位先行,LSBFIRST给1低位先行,LSBFIRST给0高位先行

然后继续看左边这一块,这里画了个方框,里面把MOSI和MISO做了个交叉,这一块主要是用来进行主从模式引脚变换的,我们这个SPI外设,可以做主机,也可以做从机,做主机时,这个交叉就不用,MOSI,为MO,主机输出,MISO,为MI,主机输入,这是主机的情况,如果我们STM32作为从机的话,MOSl,为Sl,从机输入,这时它就要走交又的这一路输入到移位寄存器

接下来,上下两个缓冲区,就还是我们熟悉的设计,这两个缓冲区,实际上就是数据寄存器DR下面发送缓冲区,就是发送数据寄存器TDR,上面接收缓冲区,就是接收数据寄存器RDR,和串回那里一样,TDR和RDR占用同一个地址,统一叫作DR,写入DR时,数据从这里,写入到TDR,读取DR时,数据从这里,从RDR读出,数据寄存器和移位寄存器打配合,可以实现连续的数据流,具体流程就是比如我们需要连续发送一批数据,第一个数据,写入到TDR,当移位寄存器没有数据移位时,TDR的数据会立刻转入移位寄存器,开始移位,这个转入时刻,会置状态寄存器的TXE为1,表示发送寄存器空,,当我们检查TXE置1后,紧跟着,下一个数据,就可以提前写入到TDR里候着了,旦上一个数据发完,下一个数据就可以立刻跟进,实现不间断的连续传输,然后移位寄存器这里,一旦有数据过来了,它就会自动产生时钟,将数据移出去,在移出的过程中,MISO的数据也会移入,一旦数据移出完成,数据移入是不是也完成了,这时,移入的数据,就会整体地,从移位寄存器转入到接收缓冲区RDR,这个时刻,会置状态寄存器的RXNE为1,表示接收寄存器非空,当我们检查RXNE置1后,就要尽快把数据从RDR读出来,在下一个数据到来之前,读出RDR,就可以实现连续接收,否则,如果下一个数据已经收到了,上一个数据还没从RDR读出来,那RDR的数据就会被覆盖,就不能实现连续的数据流了,这就是移位寄存器配合数据寄存器实现连续数据流的过程

简而言之,就是发送数据先写入TDR,再转到移位寄存器发送,发送的同时,接收数据,接收到的数据,转到RDR,我们再从RDR读取数据,数据寄存器和移位寄存器配合,可以实现无延迟的数据传输,这就是这一块的设计思路,是不是和之前串口、12C的都差不多的意思啊,当然,这三者也是有一些区别的,比如这里SPI是全双工,发送和接收同步进行,所以它的数据寄存器,发送和接收是分离的,而移位寄存器发送和接收可以共用 

左上角是比较重要的,它是SPI通信的核心部分,体现了发送和接收的执行流程

右下角部分首先是波特率发生器,这个主要就是用来产生SCK时钟的,它的内部,主要就是一个分频器,输入时钟是PCLK,72M或36M,经过分频器之后,输出到SCK引脚,当然这里生成的时钟肯定是和移位寄存器同步的了,每产生一个周期的时钟,移入移出一个bit,

然后右边,CR1寄存器的三个位BR0、BR1、BR2,用来控制分频系数,

接着后面这些通信电路和各种寄存器都是一些黑盒子电路,比如,LSBFIRST,刚才说过,决定高位先行还是低位先行,SPE(SPl Enable),是SPI使能,就是SPlCmd函数配置的位,BR(Baud Rate)配置波特率,就是SCK时钟频率,MSTR(Master),配置主从模式,1是主模式,0是从模式,我们一般用主模式,CPOL和CPHA,这个之前讲过,用来选择SPI的4种模式,然后,这里SR状态寄存器,最后两个,TXE发送寄存器空,RXNE接收寄存器非,这两个比较重要,我们发送接收数据的时候,需要关注这两位,之后CR2寄存器,就是一些使能位了,比如中断使能,DMA使能等

那最后,这里还有个NSS引l脚,SS就是从机选择,低电平有效,所以这里前面加了个N,这个NSS,和我们想象的从机选择可能不太一样,我们想象的应该是,用来指定某个从机,对吧,但是根据手册里的描述,我也研究了一下,这里的NSS设计,可能更偏向于实现这里说的,多主机模型,总的来说,这个NSS我们并不会用到,SS引脚,我们直接使用一个GPIO模拟就行,因为SS引脚很简单,就置一个高低电平就行了,而且多从机的情况下,SS还会有多个,这里硬件的NSS也完成不了我们想要的功能,那这个NSS是如何实现多主机切换的功能呢?(不重要)

假如这里有3个STM32设备,我们需要把这3个设备的NSS全都连接在一起,首先,这个NSS,可以配置为输出或者输入,当配置为输出时,可以输出电平告诉别的设备,我现在要变为主机,你们其他设备都给我变成从机,不要过来捣乱,当配置为输入时可以接收别设备的信号,当有设备是主机,拉低NSS后我就无论如何也变不成主机了,内部电路的设计,当这里这个SSOE=1时,NSS作为输出引脚,并在当前设备变为主设备时,给NSS输出低电平,这个输出的低电平,就是告诉其他设备,我现在是主机了,当主机结束后,SSOE要清0,NSS变为输入,这时,输入信号就会跑到右边这里,这有个数据选择器,SSM位决定选择哪一路,当选择上面一路时,是硬件NSS模式,也就是说,这时外部如果输入了低电平,那当前的设备就进入不了主模式了,因为NSS低电平,肯定是外部已经有设备进入了主模式,它已经提前告诉我它是主模式了,我就不能再跟它抢了,当数据选择器选择下面一路时,是软件管理NSS输入,NSS是1还是0,由这一位SSl来决定,

3.SPI基本结构

这里,移位寄存器我画的是左移,高位移出去,通过GPIO,到MOSI,从MOSI输出,显然这是SPI的主机,对吧,之后移入的数据,从MISO进来,通过GPIO,到移位寄存器的低位,这样循环8次,就能实现主机和从机交换一个字节,然后TDR和RDR的配合,可以实现连续的数据流,另外,TDR数据,整体转入移位寄存器的时刻,置TXE标志位,移位寄存器数据,整体转入RDR的时刻,置RXNE标志位

剩下的部分波特率发生器,产生时钟,输出到SCK引脚,数据控制器呢,就看成是一个管理员,它控制着所有电路的运行,最后,开关控制,就是SPlCmd,初始化之后,给个ENABLE,使能整个外设,另外,这里我并没有画SS,从机选择引脚,这个引脚,我们还是使用普通的GPIO口来模拟即可,在一主多从的模型下,GPIO模拟的SS是最佳选择

4.主模式全双工连续传输

如何来产生具体的时序呢?什么时候写DR,什么时候读DR呢

这个图演示的是借助缓冲区,数据前价后继,实现连续数据流的过程,但是,这个流程,稍微比较复架,也不太方便封装,所以,在实际过程中,如果对性能没有极致的追求,我们更倾向使用下面这个非连续传输的示意图,这个非连续传输使用起来更加简单,实际用的话,只需要4行代码就能完成任务了,非连续传输的好处就是,容易封装,好理解,好用,但是会损失一丢丢性能,连续传输呢,传输更快,但是操作起来相对复杂

首先,第一行是SCK时钟线,这里,CPOL=1,CPHA=1,示例使用的是SPI模式3,所以SCK默认是高电平,然后在第一个下降沿,MOSI和MISO移出数据,之后,上升沿移入数据,依次这样来进行,那下面,第二行是MOS|和MISO输出的波形,跟随SCK时钟变换,数据位依次出现,这里从前到后,依次出现的是b0、b1,一直到b7,所以这里示例演示的是低位先行的模式,实际SPI高位先行用的多一些

之后第三行是TXE,发送寄存器空标志位,下面继续看,是发送缓冲器,括号,写入SPI_DR,实际上,就是这里的TDR,然后BSY,BUSY,是由硬件自动设置和清除的,当有数据传输时,BUSY置1,那上面这部分,演示的就是输出的流程和现象,然后下面,是输入的流程和现象

第一个是MISO/MOSl的输入数据,之后是,RXNE,接收数据寄存器非空标志位,最后是接收缓冲器,读出SPI_DR,显然就是这里的RDR了

我们来从左到右依次分析,首先,SS置低电平,开始时序,这个没画,但是是必须得有的,在刚开始时,TXE为1,表示TDR空,可以写入数据开始传输,然后,下面指示的第一步就是,软件写入0xF1至SPI_DR,0xF1,就是要发送的第一个数据,之后可以看到,写入之后,TDR变为0xF1,同时,TXE变为0,表示TDR已经有数据了,那此时,TDR是等候区,移位寄存器才是真正的发送区,那此时,TDR是等候区,移位寄存器才是真正的发送区,所以在等候区TDR里的F1,就会立刻转入移位寄存器,开始发送,转入瞬间,置TXE标志为1,表示发送寄存器空,然后,移位寄存器有数据了,波形就自动开始生成

数据转入移位寄存器之后,数据F1的波形就开始产生了,在移位产生F1波形的同时,等候区TDR是空的,为了移位完成时,下一个数据能不间断地跟随,这里,我们就要提早把下一个数据写入到TDR里等着了,所以下面指示第二步的操作是写入F1之后,软件等待TXE=1,在这个位置,一旦TDR空了,我们就写入F2至SPI DR,写入之后,可以看到,TDR的内容,就变成F2了,也就是把下一个数据放到TDR里候着,之后的发送流程也是同理,P1数据波形产生完毕后,F2转入移位寄存器开始发送,这时,TXE=1,我们尽快把下一个数据F3放入到TDR等着,这就是这里的操作软件等待TXE=1,然后写入F3至DR,写入之后TDR变为F3,最后在这里,如果我们只想发送3个类据,F3转入移位寄存器之后,TXE=1,我们就不需要继续写入了,TXE之后-直是1,注意,在最后一个TXE=1之后还需要继续等待一段时间F3的波形才能完整发送,等波形全部完整发送之后,BUSY标志由硬件清除,这才表示,波形发送完成了,那这些,就是发送的流程

然后继续看一下下面接收的流程,SPI是全双工,发送的同时,还有接收,所以可以看到,在第一个字节发送完成后,第一个字节的接收也完成了,接收到的数据1,是A1,这时,移位寄存器的数据,整体转入RDR,RDR随后存储的就是A1,转入的同时,RXNE标志位也置1,表示收到数据了,转入的同时,我们的操作是下面这里写的RXNE标志位也置1,表示收到数据了,然后从SPIDR,也就是RDR,读出数据A1,这就是第一个接收到的数据,接收之后,软件清除RXNE标志位,然后,当下一个数据2收到之后,RXNE重新置1,我们监测到RXNE=1时,就继续读出RDR,这是第二个数据A2,最后,在最后一个字节时序完全产生之后数据3才能收到,数据3才能收到,所以数据3,直到这里才能读出来,然后注意,一个字节波形收到后移位寄存器的数据自动转入RDR,会覆盖原有的数据,所以,我们读取RDR要及时,比如A1这个数据,收到之后,最迟,你也要A2前面把它读走,否则,下一个数据A2,覆盖A1就不能实现连续数据流的接收了,这就是整个发送和接收的流程,这个连续数据流,对软件的配合要求较高,在每个标志位产生后,你的数据都要及时处理,配合的好,时钟可以连续不间断地产生,每个字节之间,没有任何空隙,传输效率是最高的,如果你对传输效率有非常高的要求,那需要好好研究这个连续数据流传输

但是,我们入门的话,可以先不用这个,因为这个操作比较复杂,而且数据的位置交叉比较多,比如我们发送数据1,按理说,交换字节,发送了,我们就想看一下接收的是什么,对吧,但是这里,接收的数据1,直到这里,才能收到,而在这之前,我们就要把发送的数据2写入到TDR了,所以它的流程并不是我们想象的,发送数据1、接收数据1、发送数据2、接收数据2这样依次交换,而是发送数据1、发送数据2、之后接收数据1然后再,发送数据3、接收数据2、发送数据4、接收数据3,这个交换的流程是交错的,对我们程序设计,不太友好,总之,如果你对效率要求很高,就研究一下这个

5.非连续传输

非连续传输,对于程序设计非常友好

这个非连续传输,和连续传输有什么区别呢,首先,这个配置还是SPI模式3,SCK默认高电平,我们想发送数据时,如果检测到TXE=1了,TDR为空,就软件写入0xF1至SPI_DR,这时,TDR的值变为F1,TXE变为0,目前移位寄存器也是空,所以这个F1会立刻转入移位寄存器开始发送,波形产生,并且TXE置回1,表示你可以把下一个数据放在TDR里候着了,但是,现在区别就来了,在连续传输的这里,一旦TXE=1了,我们就会把下一个数据写到TDR里候着,这样是为了连续传输,数据衔接更紧密,但是这样的话,流程就比较混乱,程序写起来比较复杂,所以,在非连续传输这里,TXE=1了,我们不着急把下一个数据写进去,而是一直等待,等第一个字节时序结束,在这个位置,时序结束了,是不是意味着接收第一个字节也完成了,这时接收的RXNE会置1,我们等待RXNE置1后,先把第一个接收到的数据读出来,之后,再写入下一个字节数据,也就是这里的软件等待TXE=1,但是较晚写入0xF2至SPI_DR,较晚写入TDR后,数据2开始发送,我们还是不急着写数据3,等到了这里,先把接收的数据2收着再继续写入数据3,数据3时序结束后,最后,再接收数据3置换回来的数据,按照这个流程我们的整体步骤就是,第1步,等待TXE为1,第2步,写入发送的数据至TDR,第3步,等待RXNE为1,第4步,读取RDR接收的数据,之后交换第二个字节,重复这4步,就是发送一个,等到接收一个之后,再发送下一个,那这样,我们就可以把这4步封装到一个函数,调用一次,交换一个字节,这样程序逻辑是不是就非常简单了,和之前软件SPI的流程基本上是一样的,我们只需要稍作修改,就可以把软件SPI改成硬件SPI

那非连续传输,缺点就是,在这个位置没有及时把下一个数据写入TDR候着,所以,等到第一个字节时序完成后,第二个字节还没有送过来,那这个数据传输,就会在这里等着,所以这里时钟和数据的时序,在字节与字节之间,会产生间隙,拖慢了整体数据传输的速度,这个间隙,在SCK频率低的时候,影响不大,但是在SCK频率非常高时,间隙拖后腿的现象,就比较严重了

6.软件/硬件波形对比

上面软件下面硬件

首先它们的数据变换趋势,肯定是一样的,采样得到的数据,也是一样的,区别就是,硬件波形数据线的变化是紧贴SCK边沿的,而软件波形,数据线的变化在边沿后有一些延迟,实际上我们还可以发现,12C所描述的SCL低电平期间数据变化,高电平期间数据采样和SPI描述的SCK下降沿数据移出,上升沿数据移入,最终波形的表现形式,都是一样的,无论是下降沿变化,还是低电平期间变化它们都是一个意思,下降沿和低电平期间,都可以作为数据变化的时刻,只是硬件波形,一般会紧贴边沿,软件波形,一般只能在电平期间,当然无论是哪种方式,最终都不会影响数据传输,不过软件波形,如果能贴近边沿,我们还是贴远边沿为好,否则,如果你等太久,比较靠近下一个边沿了,数据也容易出错

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2218030.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【GESP】C++一级练习BCQM3049,细胞分裂

GESP一级知识点整形int和for循环练习。 题目题解详见:【GESP】C一级练习BCQM3049,细胞分裂 | OneCoder 【GESP】C一级练习BCQM3049,细胞分裂 | OneCoderGESP一级知识点整形int和for循环练习。https://www.coderli.com/gesp-1-bcqm3049/ C …

微服务--Ribbon负载均衡器

Nacos 本身里面就内置了Rabbion, 所以 不需要额外添加 添加LoadBalanced注解: Rabbion 内置的有好几种 负载均衡器 可以根据业务去选择,我们一般不会额外配置 都是默认的轮询,因为我们是基于docker发布的 大家的资源都是平等的 若…

Vue.js + Element UI 实现多方式登录功能(账号/手机号验证码登录)

引言 在现代Web应用中,提供多种登录方式已成为一种标准做法,这不仅能提升用户体验,还能满足不同用户的需求。本文将详细介绍如何使用Vue.js框架结合Element UI组件库,实现一个包含账号登录和手机号验证码登录两种方式的登录页面。…

Leetcode 单词规律

即判断给定的模式字符串(pattern)和单词字符串(s)是否遵循相同的对应规则。具体来说,就是要判断 pattern 中的字符与 s 中的单词是否存在一一对应的关系,即双射(bijection)。 算法思…

ant design vue TimePicker时间选择器不点击确认也可以设置值

文章目录 前言一、背景二、操作步骤1.复现前的准备工作(1)vue版本和ant design vue 版本(2)任意ant design vue TimePicker的demo 2.解决问题(1)使用change时间(无效)(2&…

【学习】word保存图片

word中有想保存的照片 直接右键另存为的话,文件总是不清晰,截屏的话,好像也欠妥。 怎么办? 可以另存为 网页 .html 可以得到: 原图就放到了文件夹里面

在线白板:为远程课堂注入活力的协作工具

在线白板作为一种协作平台,极大地丰富了远程教学的互动性和创造性。在即时白板的帮助下,教师能够与学生共同在虚拟空间中创作和交流,实现知识的共享与思维的碰撞。 https://js.design/?sourcecsdn&planjh1018 首先,在线白板…

Spring Security 基础配置详解(附Demo)

目录 前言1. 基本知识2. Demo3. 实战 前言 基本的Java知识推荐阅读: java框架 零基础从入门到精通的学习路线 附开源项目面经等(超全)【Java项目】实战CRUD的功能整理(持续更新) 1. 基本知识 HttpSecurity 是 Spri…

【计算机网络 - 基础问题】每日 3 题(四十五)

✍个人博客:https://blog.csdn.net/Newin2020?typeblog 📣专栏地址:http://t.csdnimg.cn/fYaBd 📚专栏简介:在这个专栏中,我将会分享 C 面试中常见的面试题给大家~ ❤️如果有收获的话,欢迎点赞…

基于Spring Cloud的电商系统设计与实现——用户与商品模块的研究(上)

操作系统:Windows Java开发包:JDK1.8 项目管理工具:Maven3.6.0 项目开发工具:IntelliJIDEA 数据库:MySQL Spring Cloud版本:Finchley.SR2 Spring Boot版本:2.0.6.RELEASE 目录 用户模块—user-…

机器学习-RBF

径向基函数内核 – 机器学习 内核在将数据转换为更高维空间方面发挥着重要作用,使算法能够学习复杂的模式和关系。在众多的内核函数中,径向基函数(RBF)内核作为一种多功能且强大的工具脱颖而出。在本文中,我们深入探讨了RBF内核的复杂性,探讨了它的数学公式、直观理解、…

【分布式知识】MapReduce详细介绍

文章目录 MapReduce概述1. MapReduce编程模型Map阶段Reduce阶段 2. Shuffle和Sort阶段3. MapReduce作业的执行流程4. MapReduce的优化和特性5. MapReduce的配置和调优 MapReduce局限性相关文献 MapReduce概述 MapReduce是一个分布式计算框架,它允许用户编写可以在大…

使用串口中断接收时遇到的小问题(单字节接收,固定多字节接收,不定长字节接收)

单字节接收 在cubemx初始化串口1,打开串口中断 在usart。c文件中修改中断回调函数 //串口接收回调函数 void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { if( huart &huart1)//判断中断源 { if(g_ucUsart1ReceiveData 0x01) { …

Java使用原生HttpURLConnection实现发送HTTP请求

1、HttpURLConnection 类的介绍 HttpURLConnection 是 Java 提供的原生标准的用于发送 HTTP 请求和接收 HTTP 响应的一个类,它位于 java.net 包下,并继承了 URLConnection 类。 HttpURLconnection 是基于 HTTP 协议的,支持 get,…

Flink有状态计算

前言 状态是什么?状态就是数据,准确点说,状态是指 Flink 作业计算时依赖的历史数据或中间数据。如果一个 Flink 作业计算依赖状态,那它就是有状态计算的作业,反之就是无状态计算的作业。 举个例子,服务端…

【高阶数据结构】揭开红黑树‘恶魔’的面具:深度解析底层逻辑

高阶数据结构相关知识点可以通过点击以下链接进行学习一起加油!二叉搜索树AVL树 大家好,我是店小二,欢迎来到本篇内容!今天我们将一起探索红黑树的工作原理及部分功能实现。红黑树的概念相对抽象,但只要我们一步步深入…

单链表算法题(二)(超详细版)

前言 : 通过算法题 , 学习解决问题的思路 , 再面对类似的算法题时 , 能快速定位解决方案 一 . 链表的回文结构 链表的回文结构 : 链表的回文结构_牛客题霸_牛客网 思路一 : 创建新链表 , 对原链表进行反转,结果存储在…

计算机毕业设计Python深度学习房价预测 房源可视化 房源爬虫 二手房可视化 二手房爬虫 递归决策树模型 机器学习 深度学习 大数据毕业设计

温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 房地产是促进我国经济持续增…

Google play开发者账号被封,申诉就有机会,别不信

在谷歌上架,开发者账号被封对很多开发者来说已经是家常便饭了,虽说一直都有在流传申诉没有用。别灰心啊,申诉就有机会,不少开发者都申诉成功了。 尤其是用一个少一个、价值好几个w的老号,不申诉就认栽实在是太亏了&…

Bootstrap 弹出框(Popover)插件

弹出框(Popover)与工具提示(Tooltip)类似,提供了一个扩展的视图。如需激活弹出框,用户只需把鼠标悬停在元素上即可。弹出框的内容完全可使用 Bootstrap 数据 API(Bootstrap Data API&#xff09…