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

STM32单片机入门学习——第36节: [11-1] SPI通信协议

写这个文章是用来学习的,记录一下我的学习过程。希望我能一直坚持下去,我只是一个小白,只是想好好学习,我知道这会很难,但我还是想去做!

本文写于:2025.04.14

STM32开发板学习——第36节: [11-1] SPI通信协议

  • 前言
  • 开发板说明
  • 引用
  • 解答和科普
  • 一、SPI通信硬件
  • 二、SPI通信软件
  • 问题
  • 总结

前言

   本次笔记是用来记录我的学习过程,同时把我需要的困难和思考记下来,有助于我的学习,同时也作为一种习惯,可以督促我学习,是一个激励自己的过程,让我们开始32单片机的学习之路。
   欢迎大家给我提意见,能给我的嵌入式之旅提供方向和路线,现在作为小白,我就先学习32单片机了,就跟着B站上的江协科技开始学习了.
   在这里会记录下江协科技32单片机开发板的配套视频教程所作的实验和学习笔记内容,因为我之前有一个开发板,我大概率会用我的板子模仿着来做.让我们一起加油!
   另外为了增强我的学习效果:每次笔记把我不知道或者问题在后面提出来,再下一篇开头作为解答!

开发板说明

   本人采用的是慧净的开发板,因为这个板子是我N年前就买的板子,索性就拿来用了。另外我也购买了江科大的学习套间。
   原理图如下
1、开发板原理图
在这里插入图片描述
2、STM32F103C6和51对比
在这里插入图片描述
3、STM32F103C6核心板
在这里插入图片描述

视频中的都用这个开发板来实现,如果有资源就利用起来。另外也计划实现江协科技的套件。

下图是实物图
在这里插入图片描述

引用

【STM32入门教程-2023版 细致讲解 中文字幕】
还参考了下图中的书籍:
STM32库开发实战指南:基于STM32F103(第2版)
在这里插入图片描述
数据手册
在这里插入图片描述

解答和科普

一、SPI通信硬件

SPI和I2C协议差不多,两个协议的设计目的都一样,都是实现主控芯片和各种外挂芯片之间的数据交流,有了数据交流的能力,主控芯片就可以挂载并操纵各式各样的外部芯片,来实现一个功能更加强大的控制系统。
学习SPI协议的软硬件规定,先用软件模拟的SPI,实现读写这个W25Q64 Flash存储器,之后,再学习STM32中的SPI外设,再用硬件SPI,实现同样的功能。
W25Q64 Flash存储器,内部可以存储8M字节的数据,并且掉电不丢失。
在这里插入图片描述
SPI通用数据总线,也都是用于主控和外挂芯片之间的通信,应用领域非常相似。
可以发现,I2C,无论是硬件电路,还是软件时序,设计都是相对比较复杂的,硬件上,我们要配置为开漏外加上拉的模式,软件上,有很多功能和要求,比如一根通信线兼顾数据收发,应答位的收发,寻址机制的设计等等,最终通过这么多的设计,使得I2C通信的性价比非常高,I2C可以在消耗最低硬件资源的情况下,实现最多的功能。在硬件上,无论挂载多少设备,都只需要两根通信线,在软件上,数据双向通信、应答位,都可以实现。I2C就属于精打细算,思维灵活这类型的人,既要实现硬件上最少的通信线,又要实现软件上最多的功能。由于I2C开漏外加上拉电阻的电路结构,使得通信线高电平的驱动能力比较弱,这就会导致,通信线由低电平变到高电平的时候,这个上升沿耗时比较长,这会限制I2C的最大通信速度,所以I2C的标准模式,只有100Khz的时钟频率,I2C的快速模式,也只有400Khz,虽然I2C协议之后又通过改进电路的方式,设计出了高速模式,可以达到3.4Mhz,但是高速模式目前普及程度不是很高,所以我们认为I2C的时钟速度最多是400 Khz,这个速度相比较SPI而言,还是慢了很多的。

首先SPI传输更快,SPI协议并没有严格规定最大传输速度,这个最大传输速度取决于芯片厂商的设计需求,比如W25Q64 Flash存储器,手册里写的SPI时钟频率,最大可达80Mhz,这比STM32F1主频还要高,其次SPI的设计比较简单粗暴,实现的功能没有I2C那么多,所以学习起来,SPI还是比I2C简单很多的。最后SPI的硬件开销比较大,通信线的个数比较多。并且通信过程中,经常会有资源浪费的现象,SPI属于富家子弟、有钱任性这类型的人,SPI说,我不在乎我花了多少钱,我只在乎我的任务有没有最简单,最快速的完成,这就是SPI的风格。

四根通信线:SCK(Serial Clock)、MOSI(Master Output Slave Input)、MISO(Master Input Slave Output)、SS(Slave Select)。
同步,全双工:首先,既然是同步时序,肯定就得有时钟线了,所以这个SCK引脚,就是用来提供时钟信号的,数据位的输出和输入,都是在SCK的上升沿或下降沿进行的,这样数据位的收发时刻就可以明确的确定,并且,同步时序,时钟快点慢点,或者中途暂停一会,都是没问题的,这就是同步时序的好处,那对照I2C总线,这个SCK,就相当于I2C的SCL,两者作用相同,之后,SPI是全双工的协议,全双工是什么意识,就是数据发送和数据接收单独各占一条线,发送用发送的线路,接收用接收的线路,两者互不影响,所以这里MOSI和MISO,就是分别用于发送和接收的两条线路,MOSI是主机输出从机输入,如过是主机接在这条线上,那就是MO,主机输出,如果是从机接在这条线上,就是SI,从机输入;意思就是一条通信线,如果主机接在上面配置为输出,那从机肯定得配置为输入,才能接受主机的数据对吧,主机和从机不能同时配置为输入或输出,要不然就没法通信了,所以这条MOSI,就是主机向从机发送数据的线路;同理,MISO就是主机从从机接收数据的线路,这就是全双工通信的两根通信线,这两根通信线,加在一起,就相当于I2C总线的SDA,当然I2C是一根线兼具发送和接收,是半双工,这里SPI是一根发送,一根接收,是全双工,全双工的好处就是简单高效,输出线就一直输出,输入线就一直输入,数据流的方向不会改变,也不用担心发送和接收没协调好冲突了,但是坏处就是多了一根线,会有通信资源的浪费,这就是全双工。
支持总线挂载多设备(一主多从),SPI从功能上,没有I2C强大,I2C实现一主多从的方式是,在起始条件后,主机必须先发送一个字节进行寻址,用来指定我要跟那个从机进行通信,所以I2C这里,要涉及分配地址和寻址的问题,但是SPI表示,你这太麻烦了,再开辟一条通信线,专门用来指定我要跟那个从机进行通信,所以这条专门用来指定从机的通信线,就是这里的SS,从机选择线,指定地址就是用SS。SPI没有应答机制的设计,发送数据就发送,接收数据就接收,至于对面是不是存在,SPI是不管的。CLK就是SCK,显然这个接在STM32上,应该是从机,这里的DI数据输入,就是从机的数据输入SI,对应需要接在主机的MO上,所以这里的DI就是MOSI,DO就是MISO了。CS就是SS,从机选择。
2.4G无线通信模块,芯片型号是NRF24L01,这个芯片就是SPI通信协议,那就需要SPI,来读写这个芯片,SD卡,官方的通信协议是SDIO,但是也是支持SPI协议的。可以利用SPI,对这个SD卡进行读写操作。
左边这个电路,是SPI主机,主导整个SPI总线,主机,一般都是控制器来作,比如STM32,下面这里,SPI从机123,就是挂载在主机上的从设备了,左边SPI主机实际上引出了6根通信线,因为有3个从机,所以SS线需要3根,再加SCK、MOSI、MISO,就是6根通信线,当然SPI所有通信线都是单端信号,它们的高低电平都是相对GND的电压差,所以单端信号,所有的设备还需要共地,这里GND的线没画出来,但是是必须要接的,然后从机没有单独供电的话,主机还需要再额外引出电源正极VCC,给从机供电。
首先,SCK,时钟线,时钟线完全由主机掌控,所以对于主机来说,时钟线为输出,对于所有从机来说,时钟线都为输入, 这样主机的同步时钟,就能送到各个从机了;MOSI,主机输出从机输入,数据传输方向是,主机通过MOSI输出,所有从机通过MOSI输入;MISO,主机输入从机输出,数据传输方向是,三个从机通过MISO输出,主机通过MISO输入;
从机选择;为了确定通信的目标,主机另外引出多条SS控制线,分别接到各从机的SS引脚,主机的SS线都是输出,从机的SS线都是输入,SS线低电平有效,主机想指定谁,就把对应的SS输出线置低电平就行了。主机想和1通信了,就把SS1线输出低电平,这样从机1就知道,主机在找我。然后主机在数据引脚进行的传输,就只有从机1响应,其他从机的SS线都是高电平,所以它们都会保持沉默,当主机和从机1通信完成后,就会把SS1置回高电平,这样从机1就知道了,主机结束了和我的通信,之后主机需要和从机2和从机3通信时,也是同理。只选中一个从机。
在这里插入图片描述
输出引脚配置为推挽输出,输入引脚配置为浮空或上拉输入。
推挽输出,高低电平均有很强的驱动能力,这将使得SPI引脚信号的下降沿,非常迅速,上升沿也非常迅速,得益于推挽输出的驱动能力。SPI信号变化的快,那自然他就能达到更高的传输速度,一般SPI信号都能轻松达到Mhz的速度级别。然后这里并不是I2C不想使用更快的推挽输出,而是I2C要实现半双工,经常要切换输入输出,另外I2C又要实现多主机的时钟同步和总线仲裁,这些功能,都不允许I2C使用推挽输出,要不然一不小心,就电源短路了,所以I2C选择了更多的功能,自然要放弃更强的性能了。对于SPI来说,首先SPI不支持多主机,然后SPI又是全双工,SPI的输出引脚始终是输出,输入引脚始终是输入,基本不会出现冲突,所以SPI可以大胆地使用推挽输出,不过,SPI还是有一个冲突点的,就是图上的MISO引脚,主机一个是输入,但是三个从机全都是输出,如果三个主机都始终是推挽输出,势必会导致冲突,所以在SPI协议,有一条规定,就是当从机SS引脚为高电平,也就是从机未被选中时,它的MISO引脚,必须切换为高阻态,高阻态就相当于引脚断开,不输出任何电平,这样就可以防止,一条线有多个输出,而导致的电平冲突的问题了。都是从机进行的,我们主机的程序中,并不需要关注这个问题。

在这里插入图片描述
这个移位示意图是SPI硬件电路设计的核心,左边是SPI主机,里面有一个8位的移位寄存器,右边是SPI从机,里面也有一个8位的移位寄存器,这里移位寄存器有一个时钟输入端,因为SPI一般都是高位先行的,所以每来一个时钟,移位寄存器都会向左进行移位,从机种的移位寄存器也是同理,然后,移位寄存器的时钟源,是由主机提供的,这里叫做波特率发生器,它产生的时钟驱动主机的移位寄存器进行移位,同时,这个时钟也通过SCK引脚进行输出,接到从机的移位寄存器里,之后,上面移位寄存器的接法是,主机移位寄存器左边移出去的数据,通过MOSI引脚,输入到从机移位寄存器的右边,从机移位寄存器左边移出去的数据,通过MISO引脚,输入到主机移位寄存器的右边,这样组成一个圈。

在这里插入图片描述

演示一下电路如何工作,首先,规定波特率发生器时钟的上升沿,所有移位寄存器向左移动一位,移出去的位放到引脚上,波特率发生器时钟的下降沿,引脚上的位,采样输入到移位寄存器的最低位,接下来假设有个数据10101010要发送给从机,同时从机有关数据01010101要发送到主机,那我们就可以驱动时钟,先产生一个上升沿,这时所有的位,就会像这样,往左移动一次,那从从高位移出去的数据,就会放到通信线上,数据放在通信线上,实际上是放到了输出数据寄存器,可以看到此时MOSI的数据是1,所以MOSI的电平就是高电平,MISO是0,所以是低电平。这就是第一个时钟上升沿的结果。就是把主机和从机中,移位寄存器的最高位,分别放在MOSI和MISO的通信线上,这就是数据的输出。
在这里插入图片描述
之后,时钟继续运行,上升沿之后,下一个边沿就是下降沿,下降沿时,主机和从机内,都会进行数据采样输入,也就是MOSI的1,会采样输入到从机这里的最低位,MISO的0,会采样输入到主机这里的最低位,这就是一个时钟结束后的结果。同理下一个时钟沿来临。
在这里插入图片描述
最终8个时钟之后,这里原来主机里的1010 1010,跑到从机里了,原来从机里的01010101跑到主机里了,这就实现了,主机和从机一个字节的数据交换,实际上,SPI的运行过程就是这样,SPI的数据收发,都是基于字节交换,这个基本单元来进行的。当主机需要发送一个字节,并且同时需要接受一个字节时,就可以执行一下自己交换的时序,这样,主机要发送的数据,跑到从机,主机要从从机接收的数据跑到主机,这就完成了发送的同时接收的目的。

在这里插入图片描述
如果只想发送,不想接收怎么办,其实很简单,仍然调佣交换字节的时序,发送,同时接收,只是,这个接收到的数据,不看他就行了,如果只想接收,不想发送,怎么办,同理还是调用交换字节的时序,发送,同时接收,只是 ,我们会随便发送一个数据,只要能把从机数据置换过来就行了,我们读取置换过来的数据,不就是接收了吗。从机也不会看发过去的数据。一般接收的时候,统一发0X00,0XFF,去跟从机换数据。
SPI通信的基础是交换一个字节,有了交换一个字节,就可以实现,发送一个字节,接收一个字节,和发送同时接收一个字节,这三种功能。可以看出,SPI在只执行发送和只执行接收的时候,会存在资源浪费的现象。

二、SPI通信软件

在这里插入图片描述
首先,是SPI的起始和终止。
起始条件是:SS从高电平切换到低电平,SS是低电平有效的,那SS从高变低,代表刚选中某个从机了,这就是通信的开始。
终止条件:SS从低电平切换到高电平,就是结束了从机选中状态。
那在从机的整个选中状态中,要始终保持低电平。

在这里插入图片描述
CPOL=0:空闲状态时,SCK为低电平,CPHA=1:SCK第一个边沿移出数据,第二个边沿移入数据。
在SS未被选中时,SCK默认是低电平的,在通信过程中,SS始终保持低电平,通信结束SS恢复高电平。
MISO,主机输入,从机输出,因为有多个从机输出连在一起,如果同时开启输出,会造成冲突,所以,解决方法是在SS未被选中时,从机的MISO引脚必须关断输出,即配置输出为高阻态,在这里,SS高电平,MISO用一条中间的线,表示高阻态,SS下降沿之后,从机的MISO被允许开启输出,SS上升沿之后,从机的MISO必须置回高阻态。
SCK第一个边沿,就是上升沿,主机和从机同时移出数据,主机通过MOSI移出最高位,此时MOSI的电平就表示了主机要发送数据的B7,从机通过MISO移出最高位,此时MISO表示从机要发送数据的B7,然后时钟运行,产生下降沿,此时主机和从机同时移入数据,也就是进行数据采样,这里主机移出的B7,进入从机移位寄存器的最低位,从机移出的B7位,进入主机移位寄存器的最低位,这样一个时钟脉冲完成,一个数据传输完毕。接下来是同样的过程。如果主机只想交换一个字节,那这个时候就可以置SS为高电平,结束通信了。在SS的上升沿,MOSI还可以再变化一次,将MOSI置到一个默认的高电平或低电平。当然也可以不去管它。MISO从机必须置回高阻态。如果多个就不要置SS,重复前面的时序。

在这里插入图片描述
SCK第一个边沿之前,就要提前开始移出数据了,或者称作第0个边沿移出,第一个边沿移入。SS下降沿 就要立刻触发移位移出。模式0把这个数据变化的时机提前了,可以认为在SS的下降沿,立刻移位出来,相当于提前了。
在这里插入图片描述
在这里插入图片描述
CPHA表示的是时钟相位,决定是第一个时钟采样移入还是第二个时钟采样移入,并不是规定上升沿采样还是下降沿采样的,当然在CPOL确定的情况下,CPHA确实会改变采样时刻的上升沿河下降沿。 模式0和模式3都是SCK上升沿采样,模式1和模式2,都是SCK下降沿采样,这就是时序基本单元。

在SPI中,通常采用的是指令码加读写数据的模型,这个过程就是,SPI起始后,第一个交换发送给从机的数据,一般叫做指令码,在从机中对应的会定义一个指令集,第一个字节发送指令集里面的数据,这样就能指导从机完成相应的功能了。有的指令,后面就需要再跟要读写的数据,写数据,指令后面就得跟上,我要在哪里写,我要写什么,读数据,指令后面就得跟上,我要在哪里读,我读到的是什么。在SPI从机的芯片手册里,都会定义好指令集。什么指令对应什么功能,什么指令,后面得跟上什么数据。

在这里插入图片描述
在这代表的是写使能,在SS的下降沿时刻,MOSI和MISO就用变换数据了,MOSI由于指令码的最高位仍然是0,所以这里保持低电平不变,MISO,从机现在还没有数据发给主机,引脚电平没有变化,实际上W25Q64不需要回传数据时,手册里规定的是MISO仍然为高阻态,从机并没有开启输出,因为STM32的 MISO是上拉输入,所以这里的MISO呈现高电平。

在这里插入图片描述
因为是软件模拟的,所以MOSI的数据变化有些延迟,没有紧贴SCK的下降沿,不过也没关系,时钟是主机控制的,只要在下一个SCK上升沿之前完成变化就行了。然后SCK上升沿,数据采样输入,在最后一位,下降沿,数据变化,MOSI变为0,上升沿,数据采样,从机接收数据0,SCK低电平时变化的时期,高电平是读取的时期。只需要一个字节,所以最后,在SCK下降沿之后,SS置回高电平,结束通信。
主机用0x06换来了从机的0x FF,当然实际上从机没有输出,这个0x FF是默认的高电平。不过我们不用管,没有意义,整个时序的功能,就是发送指令,指令码是0x06,从机一比对事先定义好的指令集,发现0x06是写使能的指令,那从机就会控制硬件,进行写使能,这样一个指令从发送到执行,就完成了,这就是发送单字节指令的时序。

在这里插入图片描述

首先,SS下降沿开始时序,这里MOSI空闲时是高电平,所以在下降沿之后,SCK第一个时钟之前,可以看到,MOSI交换数据,由高电平变为低电平,然后SCK上升沿,数据采样输入,后面还是一样,下降沿变换数据,上升沿采样数据,8个时钟之后,一个字节交换完成。还需呀交换数据,所以在这个下降沿,要把下一个字节的最高位放到MOSI上。通过3个字节的交换,24位地址就发送完毕了,就要发送写入指定地址的内容了,发送了0x 55,这就表示,要在0x123456地址下,写入0x55这个数据,最后如果只想写入一个数据的话,就可以置SS高电平,结束通信了。当然也可以继续发送,SPI里,也会有和I2C一样的地址指针,每读写一个字节,地址指针自动加1,继续发送的字节就会依次写入到后续的存储空间里,这样就可以实现从指定地址开始,写入多个字节了。MISO属于挂机的状态。

在这里插入图片描述

在这里插入图片描述
3个字节地址交换完成后,我们要把从机的数据搞过来,还是交换一个数据,来个抛砖引玉,随便给从机一个数据,给FF就行,这时从机就会,把0X123456地址下的数据通过MISO发给主机,这样主机就实现了指定地址读一个字节的目的,如果继续抛砖引玉,那从机内部的地址指针自动加1,从机就会继续把指定地址下一个为止的数据发过来,这样依次进行,就可以实现指定地址接收多个字节的目的了,然后最后,数据传输完毕,SS置回高电平,时序结束。
由于MISO是从机硬件控制的,所以他的数据变化,都可以紧贴时钟的下降沿,另外可以看到,MISO数据的最高位,实际上是在上一个字节,最后一个下降沿,提前发生的,因为这是SPI模式0,所以数据变化都要提前半个周期。

问题

总结

本节课主要学了SPI的硬件原理和软件时序的过程。

相关文章:

  • C++中extern关键字
  • 【微信开发者工具】解决微信开发工具的调试器加载错误,从任务栏打开工具可能导致该问题,请不要从任务栏启动工具
  • Redis 常问知识
  • MCP的另一面
  • Spark-SQL
  • 贪心算法(18)(java)距离相等的条形码
  • Docker实战:从零构建高可用的MySQL主从集群与Redis集群
  • 破产计划:openmv烧写UVC.bin直接让openmv作为电脑免驱动摄像头
  • 考研单词笔记 2025.04.14
  • Linux vagrant 导入ubuntu到virtualbox
  • 《小王子》E04:这是一个箱子还是羊?
  • RK3506+net9+VS2022跨平台调试C#程序
  • 30天学Java第十天——反射机制
  • 游戏引擎学习第225天
  • visual studio 常用的快捷键(已经熟悉的就不记录了)
  • 《轨道力学讲义》——第八讲:行星际轨道设计
  • 项目执行中的目标管理:从战略到落地的闭环实践
  • 远程开发之devcontainer[简单介绍版]
  • Kitex Release v0.13.0正式发布!
  • linux 系统编程基础部分 day1
  • 上海银行换帅:顾建忠出任党委书记,金煜辞任董事长
  • 继微软之后,亚马逊也放缓人工智能数据中心计划
  • 泰国总理佩通坦:推迟与美国的关税谈判
  • 民建吉林省委提案:当前生育政策集中鼓励多孩生育,应该转变思路
  • 上海开展数据产品知识产权登记存证试点,243个产品许可收益超20亿元
  • 上交所召开私募机构座谈会,与会机构:中国资产具备显著估值修复和提升潜力,将坚定持有