自学STM32 – SPI同步串行通信

自学STM32 – SPI同步串行通信作者 junziyang 注 如非特別声明 以下笔记内容均针对 STM32F103ZET 而言 不同型号 细节可能存在差别 在头条发技术贴是最没成就感的 为了看到更多有营养的资讯 碰到自己喜欢的技术贴请一定记得 收藏 转发 点赞

大家好,欢迎来到IT知识分享网。

作者:junziyang


(注:如非特別声明,以下笔记内容均针对STM32F103ZET6而言。不同型号,细节可能存在差别。)

在头条发技术贴是最没成就感的,为了看到更多有营养的资讯,碰到自己喜欢的技术贴请一定记得,收藏+转发+点赞。

一、SPI接口简介

SPI是Serial Peripheral Interface的缩写,即串行外设接口。通过SPI接口可以与外设进行半/全双工、同步、串行通信。SPI支持双向通信且最多只需要四根线,有利于节省芯片管脚和PCB板上的空间。与USART异步通信不同,SPI是一种同步通信方式,由主机提供通信时钟(SCK),主从机在SCK同步下工作。SPI支持CRC校验,以提高通信的可靠性。SPI通信常用于主机与多个从机的同步通信。

注意:

  • 在STM32的high-density、XL-density和Connectivity line系列芯片中,SPI接口可支持SPI协议或I2S音频协议。默认情况下实现的是SPI协议,但通过软件可以将该接口切换到I2S。
  • 由于JTAG调试接口占用了SPI3/I2S3的部分引脚,配置SPI3/I2S3之前需要停用JTAG接口,如需调试接口,可以改为SWD接口。

二、SPI工作原理

任何外设的功能实现都是通过操作专用寄存器实现的。STM32为SPI配置了9个32bit的寄存器,寄存器名称、地址分配及复位值表如图1所示。在后续的学习中会经常提到相关的配置位,可参考图1.

自学STM32 - SPI同步串行通信

图1. SPI寄存器地址分配与复位值表

这9个寄存器中,用于SPI接口的实际只有7个,最后两个用来配置I2S的功能。

2.1 原理框图

自学STM32 - SPI同步串行通信

图2. SPI原理框图

SPI框图如图2所示。最左侧是SPI的4个引脚,用来连接外部设备,各引脚功能如下:

  • MISO(Master In / Slave Out):主入从出数据引脚。主模式下作为数据输入(接收)端,从模式下作为数据输出(发送)端。
  • MOSI(Master Out / Slave In):主出从入数据引脚。主模式下作为数据输出(发送)端,从模式下作为数据输入(接收)端。
  • SCK(Serial Clock):同步时钟引脚。SPI是一种同步通信方式,主模式下,由此引脚输出通信时钟;从模式下,由此引脚输入通信时钟。
  • NSS(Slave Select):片选引脚。这是一个可选引脚,可输入和输出,通过SPI_CR2的SSOE位配置。当有多个从机时,主机和所有从机的NSS引脚连接在一起,主机通过NSS引脚选择与之单独通信的从机,以避免数据线上的冲突。通过将主机SPI的SSOE置1,可将其NSS引脚电平拉低。这样,所有连到该引脚的其他设备的NSS引脚就会收到低电平。其中被配置为NSS硬件模式(SPI_CR1的SSM=0)的设备,NSS引脚收到低电平后会变为从机。NSS被配置为输入的主模式SPI(MSTR=1且SSOE=0),如果NSS被拉低,则进入主模式故障状态:MSTR位被清除,也会自动由主模式切换为从模式。
自学STM32 - SPI同步串行通信

图3. 单主机单从机互联示意图

图3所示是单个主机与单个从机互联示意图。同名引脚连接到一起(输入接输出),这样可以实现主从设备间数据的串行传输。若NSS由软件管理,则无需连接。

通信由主机发起,通过MOSI引脚将数据发送到从机,从机则通过MISO引脚进行应答。这样,在主机SCK引脚提供的通信时钟信号的同步下,实现了数据的同时接收和发送,即全双工通信。在不用NSS的情况下,SPI仅需3条线,即可实现全双工通信。

2.2 从机选择(NSS)

在存在多个从机的情况下,从机选择可以通过硬件或软件方式实现,通过SPI_CR1寄存器的SSM(Software Slave Management )位配置。

1. 软件NSS管理(SSM=1)

软件模式下,通过设置SPI_CR1寄存器SSI位的值来选择从机。软件模式下NSS引脚闲置,可以用于其他目的。

2. 硬件NSS管理(SSM=0)

硬件模式下,根据NSS输出配置的不同(即SSOE位的值),有两种方案:

(1) NSS输出使能(SSM=0,SSOE=1)

此方案仅适用于处于主模式的设备。开始通信时,主机将NSS引脚被拉低并一直保持,直至通信结束。这样NSS引脚与主机NSS引脚相连的所有设备都被设为从机。

(2) NSS输出关闭(SSM=0,SSOE=0)

此方案对处于主模式和从模式的设备均适用,但有不同的影响:

  • 对处于主模式的设备,赋予其多主能力;
  • 对于处于从模式的设备,NSS引脚仍执行典型的NSS输入,即NSS为低电平时,从机被选中;NSS为高电平时,从机被取消选中。

2.3 时钟相位与极性

SPI传输数据是在时钟的同步下实现的,每个时钟脉冲传输1个bit。为了适应不同设备SPI接口的收发规则,STM32的SPI接口可以通过软件配置出四种可能的时序关系。配置方法是设置SPI_CR1寄存器的CPOL和CPHA位:

  • CPOLclock polarity)位控制无数据传输时SCK引脚的电平(闲时电平)。若CPOL=0,空闲时SCK引脚为低电平;若CPOL=1,空闲时SCK引脚为高电平。该位在主模式和从模式下均有效。
  • CPHAclock phase)位控制在时钟脉冲的第几个边沿进行数据采样。若CPHA=1,在时钟信号的第2个边沿(CPOL=0时为下降沿,CPOL=1时为上升沿)启动MSB位捕获。即,SCK引脚出现第2次时钟电平转换时锁存数据;若CPHA=0,则在时钟信号的第1个边沿(CPOL=0时为上升沿,CPOL=1时为下降沿)启动MSB位捕获。即,SCK引脚出现第1次时钟电平转换时,锁存数据。
自学STM32 - SPI同步串行通信

图4. SPI四种时序关系示意图

图4所示为4种时序关系的示意图。实践中需要按照SPI从设备的时钟要求进行配置,因为从设备的协议在出厂时就设置好了,且一般不可配置。通信双方在时钟脉冲的一个边缘发送,另一个边沿接收。

2.4 数据帧格式

数据从SPI_DR寄存器中移出时,既可以MSB优先,也可以LSB优先。通过SPI_CR1寄存器的LSBFRST位进行设置:LSBFRST=0,高位优先;LSBFRST=1,低位优先。图4所示为高位优先的时序。

数据帧的长度可以为8位或16位,由SPI_CR1寄存器的DFF位进行设置。数据帧格式选定后,发送和接收都按此格式。

三、SPI工作模式配置

SPI不仅支持主/从模式、全/半双工模式,主、从机还可只接收或只发送。根据需要可以配置出多达8种工作模式。如图5所示。

自学STM32 - SPI同步串行通信

图5. STM32CubeIDE中SPI可模式和参数设置选项

不同的模式是通过配置SPI_CR1寄存器来实现的。下面分别说明这些模式的设置方法和工作过程。

3.1 将SPI配置为从模式

在从模式下,SPI在主机时钟的同步下工作,从SCK引脚接收来自主机的时钟。数据传输速率由主机中的设置决定,从机的数据速率设置(SPI_CR1寄存器BR[2:0]位)无效。从模式下,数据从MOSI引脚输入,从MISO引脚输出。

注意事项:

  • 为了避免出现数据传输错误,建议在主机发送时钟前先使能从机
  • 在通信时钟的第一个边沿到来前,或正在进行的通信结束前,从机的数据寄存器都需要一定的准备时间。从机和主机使能前,通信时钟的极性必须稳定。

1. 配置步骤

按下述步骤配置SPI_CR1寄存器,可将SPI配置为从模式:

  • 设置DFF位,选择数据格式:8位或16位;
  • 设置CPOLCPHA,配置时钟极性和相位。从机设置必须与主机一致
  • 设置SPI_CR1的LSBFIRST,配置帧格式(MSB/LSB优先)从机设置必须与主机一致
  • 硬件模式下(SSM=0),在传输序列结束字节传输期间,NSS引脚必须为低电平;软件模式下,SPI_CR1寄存器中设置:SSM=1,SSI=0;
  • SPI_CR1寄存器中设置:MSTR=0,SPE=1。前者设为从模式,后者使能SPI。

2. 发送过程

  • 一个写周期中数据被并行载入Tx缓存(Tx buffer,参见图3左上角)。
  • 第一个数据位发送出去后(从设备收到时钟信号,MOSI引脚上出现第一个数据位),剩余数据位被并行载入移位寄存器(Shift register)。
  • 数据被移走后Tx缓存为空,SPI_SR寄存器的TXE (Tx buffer empty)被置1。如果设置了SPI_CR2中的TXEIE=1,则会产生中断。

3. 接收过程

对接收器,数据传输结束时:

  • 移位寄存器中的数据被转移到Rx缓存中,SPI_SR寄存器的RXNE位被置1;
  • 如果设置了RXNEIE位,则会产生中断。

接收器在接收到最后一个取样时钟边沿时,将移位寄存器中收到的数据移入Rx缓存。读SPI_DR寄存器时,返回的是Rx缓存中的数据,读取后会自动清除RXNE位。

3.2 将SPI配置为主模式

在主模式配置下,SCK引脚上发出串行时钟。主模式下,数据从MISO引脚输入,从MOSI引脚输出。

1. 配置步骤

按下述步骤配置SPI_CR1寄存器,可将SPI配置为主模式:。

  • 置SPI_CR1寄存器的BR[2:0]位,定义串行时钟SCK的波特率。波特率为fPCLK/2^(n+1),其中n为BR位的值,n越大,波特率越低。fPCLK是SPI挂载到的外设总线的时钟频率;
  • 设置CPOLCPHA位,定义串行时钟极性和相位;
  • 设置DFF位,定义数据格式:8位或16位;
  • 设置SPI_CR1的LSBFIRST,定义帧格式(MSB/LSB优先);
  • 硬件模式下,如果输入模式中需要NSS引脚,在传输结束期间将NSS拉高;软件模式下,在SPI_CR1寄存器中设置:SSM=1,SSI=1。如果输出模式中需要NSS引脚,只需设置SSOE=1即可。
  • SPI_CR1寄存器中设置:MSTR=1,SPE=1。如果NSS引脚连接到高电平,这两个位始终保持置1状态。

2. 发送过程

  • 数据字节被写入Tx缓存;
  • 在第1个bit发送期间,数据字节经由内部总线被并行载入移位寄存器,然后串行移出到MOSI引脚。高位优先还是低位优先由LSBFIRST位设置。
  • 数据从Tx缓存被移入移位寄存器后,TXE位被置1。如果设置了TXEIE位,则会产生中断。

3. 接收过程

对接收器,数据传输结束时:

  • 移位寄存器中的数据被转移到Rx缓存中,RXNE位被置1;
  • 如果设置了RXNEIE位,则会产生中断。

接收器在接收到最后一个取样时钟边沿时,设置RXNE位,并将移位寄存器中收到的数据移入Rx缓存。读DR寄存器时,返回的是该缓存中的数据。读DR寄存器会自动清除RXNE位。

总结:

可以看出,主、从机发送和接收过程是一模一样的。配置过程也非常类似,只是主机需要配置波特率,而从机不用;从模式MSTR和SSI均为0,而主模式二者均为1。

发送一旦启动,只要有待发送数据被移入Tx缓存,发送过程会一直持续下去。需要注意的是,每次准备写入Tx缓存前,应确认TXE标志位是1。

注意事项:

当一个主机正在与多个从机通信时,如果需要在两次传输之间断开从机,可以配置一个GPIO来充当从机的NSS引脚并通过软件操控该引脚的电平进行切换。

3.3 将SPI配置为半双工通信

SPI可以工作于半双工模式,有两种配置方法:

  • 1时钟线+1双向数据线
  • 1时钟线+1单向数据线(单接收或单发送)

1. 1时钟线+1双向数据线(BIDIMODE=1)

将SPI_CR1寄存器的BIDIMODE位置1,即可开启这种模式。SCK引脚作为时钟,主机的MOSI或从机的MISO引脚作为数据通道。传输方向由SPI_CR1寄存器的BIDIOE位设置。BIDIOE=1,数据线作为输出端,反之则作为输入端。

2. 1时钟线+1单向数据线(BIDIMODE=0)

这种模式下,SPI可工作于单向发送或单向接收模式,由RXONLY位进行进一步配置。

1) 单向发送模式(BIDIMODE=0,RXONLY=0)

此模式与全双工模式类似:数据由发送引脚(主机的MOSI或从机的MISO)发送,闲置的接收引脚(主机的MISO或从机的MOSI)可用作GPIO。配置为单向发送模式的SPI,Rx缓存中没有发送的值,程序中忽略它即可。

2) 单向接收模式(BIDIMODE=0,RXONLY=1)

程序中将SPI的RXONLY位置1,将会关闭其输出功能。闲置的输出引脚(主机的MOSI或从机的MISO)可作为GPIO,用作其他目的。

按如下步骤配置和使能SPI,以启动单向接收模式通信:

  • 主模式下,SPI使能后通信立即开始,清除SPE位后通信立即停止。该模式下无需读取SPI_SR寄存器的BSY标志位,来判断SPI是否正忙。只要SPI通信正在进行,该位一定为1。
  • 从模式下,只要NSS被拉低(或软件模式下SSI被清除)且SCK仍在运行,SPI就会持续接收。

总结:

通过配置BIDIMODE、MSTR、BIDIOE和RXONLY这4个位,可配置出8中SPI工作模式:

  • 全双工主机(BIDIMODE=0,RXONLY=0,MSTR=1)
  • 全双工从机(BIDIMODE=0,RXONLY=0,MSTR=0)
  • 半双工主机(BIDIMODE=1,BIDIOE=1,MSTR=1),双向模式-发送
  • 半双工从机(BIDIMODE=1,BIDIOE=0,MSTR=0),双向模式-接收
  • 单向发送主机(BIDIMODE=0,RXONLY=0,MSTR=1)
  • 单向发送从机(BIDIMODE=0,RXONLY=0,MSTR=0)
  • 单向接收主机(BIDIMODE=0,RXONLY=1,MSTR1)
  • 单向接收从机(BIDIMODE=0,RXONLY=1,MSTR=0)

STM32CubeIDE中的配置界面如图5所示。

四、数据发送和接收过程

4.1 Rx和Tx缓存

如图3所示,SPI的收、发端内部各有一个缓存寄存器。传输过程中,接收端将接收到的数据先存入Rx缓存,发送端数据在被发送前也是先被存入Tx缓存。这两个内部缓存对外部程序不可见,程序中对SPI_DR的读操作,返回的是Rx缓存中的值;而对SPI_DR的写操作,则是将数据存入Tx缓存。

4.2 主模式下的启动顺序

1. 全双工模式(BIDIMODE=0,RXONLY=0)

  • 数据写入SPI_DR寄存器(Tx buffer)时,开始传输。
  • 在第1个bit传输期间,数据被从Tx buffer并行加载到移位寄存器,随后从MOSI引脚串行移出。
  • 与此同时,MISO引脚上接收到的数据被串行移入移位寄存器,然后并行载入SPI_DR寄存器(Rx buffer)

2. 单向接收模式(BIDIMODE=0, RXONLY=1)

  • 当SPE=1时,即可开始传输。
  • 只有接收器被激活,MISO引脚上接收到的数据被串行移入移位寄存器,然后并行载入SPI_DR寄存器(Rx buffer)。

3. 双向模式-发送(BIDIMODE=1,BIDIOE=1)

  • 数据写入SPI_DR寄存器(Tx buffer)时,即可开始传输。
  • 在第1个bit传输期间,数据被从Tx buffer并行加载到移位寄存器,随后从MOSI引脚串行移出。
  • 无数据被接收

4. 双向模式-接收(BIDIMODE=1,BIDIOE=0)

  • 当SPE=1时,即可开始传输。
  • MISO引脚上接收到的数据被串行移入移位寄存器,然后并行载入SPI_DR寄存器(Rx buffer)。
  • 因发送器未被激活,所以MOSI引脚上无串行数据移出。

4.3 从模式下的启动顺序

1. 全双工模式(BIDIMODE=0,RXONLY=0)

  • 从设备收到时钟信号且数据的第1个bit出现在MOSI引脚上时,开始传输。剩余bits被载入移位寄存器。
  • 与此同时(在第1个bit传输期间),数据被从Tx buffer并行加载到移位寄存器,随后从MISO引脚串行移出。在SPI主设备启动传输前,待发送数据必须已由软件写入完毕。

2. 单向接收模式(BIDIMODE=0, RXONLY=1)

  • 从设备收到时钟信号且数据的第1个bit出现在MOSI引脚上时,开始传输。剩余bits被载入移位寄存器。
  • 因发送器未被激活,所以MISO引脚上无串行数据移出。

3. 双向模式-发送(BIDIMODE=1,BIDIOE=1)

  • 从设备收到时钟信号且Tx buffer中的第1个bit从MISO引脚上被发送出去时,开始传输。
  • 在第1个bit传输期间,数据被从Tx buffer并行加载到移位寄存器,随后从MISO引脚串行移出。在SPI主设备启动传输前,待发送数据必须已由软件写入完毕。
  • 无数据被接收

4. 双向模式-接收(BIDIMODE=1,BIDIOE=0)

  • 从设备收到时钟信号且第1个bit数据出现在MOSI引脚上时,开始传输。
  • MOSI引脚上接收到的数据被串行移入移位寄存器,然后并行载入SPI_DR寄存器(Rx buffer)。
  • 因发送器未被激活,所以MISO引脚上无串行数据移出。

总结:

SPI通信总有一个主设备和一个从设备,一方发送,另一方接收。从主设备来看,执行的就是“主模式下的启动顺序”,而从配对的从设备来看,则执行的是“从模式下的启动顺序”。只是观察的角度不同而已。

4.4 发送和接收数据的处理

发送过程中,当数据从Tx buffer被移入移位寄存器后,TXE标志位被置1。这说明Tx buffer已准备好接收下一次数据载入。如果设置了TXEIE位,会产生相应的中断。程序中拟向Tx buffer写入数据前,应确认TXE=1,否则会覆盖上一次写入的数据。向SPI_DR寄存器写入数据时(实际写入的是Tx buffer),会自动清除TXE位。

接收过程中,当数据从移位寄存器移入Rxbuffer时,在最后一个取样时钟边沿RXNE被置1。这说明,SPI_DR寄存器中的数据已经可以读取。如果设置了RXNEIE位,会产生相应的中断。读SPI_DR寄存器,即可清除RXNE位。

对某些配置,在最后一个数据传输期间,可以通过等待BSY标志位来确认传输完成。下面分不同的传输模式进行详述:

1. 主/从模式下的在全双工发送和接收过程(BIDIMODE=0,RXONLY=0)

程序中必须遵循如下步骤来发送和接收数据:

  • 设置SPE=1,使能SPI。
  • 将第1个待发送数据项写入SPI_DR寄存器(这会清除TXE)。
  • 等待TXE=1,写入下一个数据项。然后等待RXNE=1,读SPI_DR,得到第一个接收到的数据项(这会清除RXNE)。重复此操作,直至第n-1个接收到的数据。
  • 等待RXNE=1,读取最后1个接收的数据。
  • 等待TX=1,然后等待BSY=0,关闭SPI。

这个过程也可以用专门的中断回调函数来实现。开启中断后,在每个RXNE或TXE上升沿,中断函数都会被触发。

全双工模式下,主机和从机的时序图分别如图6和图7所示。

自学STM32 - SPI同步串行通信

图6. 主机全双工模式收发时序图

自学STM32 - SPI同步串行通信

图7. 从机全双工模式收发时序图

2. 单向发送过程(BIDIMODE=0,RXONLY=0)

该模式下,可简化为如下步骤:

  • 设置SPE=1,使能SPI。
  • 将第1个待发送数据项写入SPI_DR寄存器(这会清除TXE)。
  • 等待TXE=1,写入下一个数据项。对每个数据项重复此操作。
  • 将最后1个数据项吸入SPI_DR寄存器后,等待TXE=1。然后等待直至BSY=0。

这个过程也可以用专门的中断回调函数来实现。开启中断后,在每个TXE上升沿,中断函数都会被触发。

注意事项:

  • 在不连续通信期间,写SPI_DR和BSY被置1之间有2个APB时钟周期的延迟。因此,在单发送模式下,写入最后一个数据后,必须首先等待TXE=1,然后等待至BSY=0
  • 单发送模式下,发送两个数据项后OVR标志位会被置1,这种模式下不会读取数据寄存器。

单发送模式下,主机和从机的时序图分别如图8和图9所示。

自学STM32 - SPI同步串行通信

图8. 主机单发送模式时序图

自学STM32 - SPI同步串行通信

图9. 从机单发送模式时序图

3. 双向发送过程(BIDIMODE=1,BIDIOE=1)

除了使能SPI前要置BIDIMODE=1和BIDIOD=1之外,此模式下其余步骤与单向发送模式完全相同。

4. 单向接收过程(BIDIMODE=0,RXONLY=1)

此模式下,工作步骤简化简化如下:

  • 设置RXONLY=1。
  • 设置SPE=1,使能SPI。主模式下,这会立即激活SCK时钟和数据串行接收,直至SPI关闭(SPE=0);从模式下,SPI主机将NSS拉低并产生SCK时钟后,开始接收数据。
  • 等待RXNE=1,读取SPI_DR寄存器中收到的数据(这会清除RXNE)。对每个接收到的数据项重复此操作。

注意:最后一个数据传输完毕欧,要关闭SPI。上述流程也可以基于专门的中断回调函数来实现。开启中断后,每个RXNE上升沿都会触发。单向接收模式的时序图如图10所示。

自学STM32 - SPI同步串行通信

图10. 单向接收模式时序图

5. 双向接收过程(BIDIMODE=1,BIDIOE=0)

除了使能SPI前要置BIDIMODE=1和BIDIOD=0之外,此模式下其余步骤与单向接收模式完全相同。

6. 连续和间断传输

以主模式发送数据时,如果软件足够快,能够检测到TXE的每个上升沿(或TXE中断),并且在正在进行中的数据传输结束前迅速写入SPI_DR寄存器,这种传输就可称为连续传输。这种情况下,在每个数据项之间SPI时钟没有中断,BSY位在每个数据传输之间也不会被清除。

反之,如果软件不够快,就会导致通信过程出现间断。这种情况下,在每个数据项传输之间,BSY位被清除。

在主机单向接收模式下(RXONLY=1),可以保持连续通信,BSY始终为1。

在从模式下,通信的连续性取决于主机的SPI始终。即使通信是连续的,每次传输之间BSY位至少有一个SPI始终周期会被拉低。

出现间断发送情况下,TXE和BSY位的时序如图11所示。

自学STM32 - SPI同步串行通信

图11. 间断传输情况下TXE和BSY位时序图

五、标志位

5.1 状态标志位

通过SPI_SR寄存器中的3个状态标志位可以监控SPI总线的状态。

1. 发送缓存为空 – TXE

TXE=1时,表明发送缓存为空,下一个待发送数据可以载入。写SPI_DR寄存器可以清除该位。

2. 接收缓存非空 – RXNE

RXNE=1时,表明接收缓存非空,数据已成功接收正待读取。读SPI_DR寄存器可以清除该位。

3. 忙碌 – BSY

BSY位的设置和清除由硬件管理,软件写入会被忽略。BSY位用来指示SPI通信层的状态。当BSY=1时,表明SPI正忙于通信。例外情况是,在主模式/双向接收模式(MSTR=1、BIDIMODE=1、BDOE=0)中,在接收期间BSY始终为0。

BSY为常用来检查传输是否结束。在关闭SPI并进入停机状态(关闭外设时钟)前,必须严格执行这一检查,否则可能会破坏最后传输的数据。

BSY标志位还可以用来避免多主机系统中的写入冲突。

除了主模式/双向接收模式外,传输开始BSY位即被置1。当发生如下情况时,BSY被清除:

  • 传输结束(主模式连续通信除外)
  • SPI被关闭
  • 发生主模式错误(MODF=1)

当非连续通信时,BSY在每次通信之间为0。当连续通信时:

  • 主模式下,传输过程中BSY保持为1
  • 从模式下,没次传输之间BSY有一个SPI时钟周期为0

注意:

不要用BSY来处理每次数据发送或接收,应该用TXE和RXNE标志位。

5.2 错误标志位

1. 主模式失效(MODF)

当主设备的NSS引脚被拉低(NSS硬件模式)或SSI被置0(NSS软件模式)时,会出现主模式失效。此时,MODF位会被自动置1。主模式失效会对SPI模块产生如下影响:

  • MODF位被置1,如果ERRIE=1,会产生SPI中断
  • SPE位被清除。当前设备的所有输出被阻断,SPI接口被关闭。
  • MSTR位被清除,当前设备被强制进入从模式。

要清除MODF位,软件中需要执行如下步骤:

  • MODF=1时,读或写一次SPI_DR寄存器
  • 接着,写SPI_CR1寄存器(去重新配置或使能SPI)

在一个由多个MCU构成的系统中,为了避免多个从机间的冲突,在执行上述清除MODF位的过程中,NSS引脚必须被拉高。清除程序完成后,可将SPE和MSTR位恢复其原来的状态。作为一项安全措施,在MODF=1期间,硬件会禁止对SPE和MSTR位的设置。

从设备不会出现主模式失效,所以从设备的MODF位不能被置1。但是,在多主配置中,设备可以在MODF=1的情况下处于从模式。这种情况下,MODF=1表明系统控制过程中可能出现过多主冲突(multimaster conflict)。可以用中断程序来执行一次复位或恢复默认状态来清理这种状态。

2. 溢出错误(OVR)

主机已经发送数据,但从机没有清除上次接收数据后的RXNE位,就会出现溢出错误。出现溢出错误时,OVR为被置1,如果ERRIE=1,则会产生中断。

出现这种情况时,接收缓存中的内容不会被主机新接收到的数据更新。读SPI_DR寄存器仍可读取缓存中的内容,但OVR=1期间发送的字节就都丢失了。

读SPI_DR寄存器后接着读一次SPI_SR寄存器,即可清除OVR。HAL库中__HAL_SPI_CLEAR_OVRFLAG宏函数即是用来完成这个任务的。其代码如下:

#define __HAL_SPI_CLEAR_OVRFLAG(__HANDLE__) \ do{ \ __IO uint32_t tmpreg_ovr = 0x00U; \ tmpreg_ovr = (__HANDLE__)->Instance->DR; \//读DR tmpreg_ovr = (__HANDLE__)->Instance->SR; \//读SR UNUSED(tmpreg_ovr); \ } while(0U) 

3. CRC错误(CRCERR)

通过SPI_CR1寄存器中CRCEN=1使能CRC后,可通过CRCERR位来确认接收到的值的有效性。如果移位寄存器中接收到的CRC与SPI_RXCRCR中的值不匹配,CRCERR位就会被置1。

5.3 SPI中断

SPI共有5个标志位可触发3个中断。如下表所示:

中断事件

标志位

使能位

发送缓存为空

TXE

TXEIE

接收缓存非空

RXNE

RXNEIE

主模式失效

MODF

ERRIE

溢出

OVR

CRC校验错误

CRCERR

以上5个标志位均在SPI_SR寄存器中,硬件设置,软件复位;3个使能位在SPI_CR2寄存器中设置。

六、关闭SPI

传输完成后,应用程序中可以关闭SPI模块来停止通信。这可以通过清除SPE位来实现。在某些配置下,如果传输正在进行中,就关闭SPI并进入停机模式,可能导致当前的传输被破坏,并可能使BSY标志位变得不可信。为避免这类情况,建议关闭SPI时严格执行如下步骤:

1. 主/从全双工 (BIDIMODE=0, RXONLY=0)

  • 等待RXNE=1,接收最后一个数据
  • 等待TXE=1
  • 等待BSY=0
  • 关闭SPI(SPE=0),最终进入停机模式(关闭外设时钟)

2. 主/从单向发送(BIDIMODE=0,RXONLY =0)或双向发送(BIDIMODE=1,BIDIOE=1)

  • 等待TXE=1
  • 等待BSY=0
  • 关闭SPI(SPE=0),最终进入停机模式(关闭外设时钟)

3. 主单向接收(MSTR=1,BIDIMODE=0,RXONLY=1)或双向接收(MSTR=1,BIDIMODE=1, BIDIOE=0)

此模式下必须进行特殊处理,防止SPI启动新的传输。

  • 等待倒数第二(n-1)次RXNE=1
  • 等待一个SPI时钟周期(软件中循环)再关闭SPI(SPE=0)
  • 等待最后一次RXNE=1,然后进入停机模式(关闭外设时钟)

注意:在主双向接收模式(MSTR=1,BIDIMODE=1, BIDIOE=0),传输期间始终BSY=0。

4. 从单向接收(MSTR=0,BIDIMODE=0,RXONLY=1)或双向接收(MSTR=0,BIDIMODE=0,BIDIOE=1)

  • 可随时关闭SPI(SPE=0):当前传输会在SPI被关闭前完成
  • 然后,如果想进入停机模式,必须首先等待BSY=0.。

七、CRC计算

为实现可靠通信,SPI中为发送数据和接收数据分别设置了一个单独的CRC(Cyclic Redundancy Check,循环冗余校验)计算器。通过设置SPI_CR1寄存器中的CRCEN=1来开启。开启后,在每个数据取样时钟边沿,都会基于一个可编程多项式对每个bit串行计算CRC值。由于SPI支持8-bit和16-bit两种数据长度,CRC计算时会采取与之对应的CRC8(8-bit数据)和CRC16(16-bit数据)两种标准。

开启CRC计算的同时会将SPI_RXCRCR和SPI_TXCRCR两个寄存器复位。此后由根据收/发数据进行CRC计算得到的值填充。

CRC校验过程分为两步:

  • 在发送端,数据发送过程中逐位计算CRC并存入SPI_TXCRCR。在最后一个数据发送完成后,SPI_TXCRCR中的值被发送出去。
  • 在接收端,数据接收过程中逐位计算CRC并存入SPI_RXCRCR。在最后一个数据接收完成后,会接收到来自发送端的CRC值。将之与SPI_RXCRCR中的值进行比较,如果不匹配,则说明数据在传输过程中被损坏,CRCERR位会被置1。

显然,发送端和接收端对CRC值不应再进行CRC计算。因此,在软件管理(CPU mode)的全双工或单向发送模式中,必须在最后一个数据写入SPI_DR后立即将CRCNEXT置1。在软件管理(CPU mode)的单向接收模式中,必须在收到倒数第2个数据时将CRCNEXT置1。发送端或接收端在查询到CRCNEXT=1时,将关闭CRC计数器。接收端在接收完下一个数据后,会马上用之进行CRC校验。

SPI通信可以通过以下步骤使用CRC:

  • 在程序中配置CPOL, CPHA, LSBFirst, BR, SSM, SSI 和MSTR的值。
  • 在SPI_CRCPR寄存器中设置CRC计算所需的多项式。
  • 将SPI_CR1寄存器的CRCEN位置1,使能CRC计算。这会同时清除SPI_RXCRCR和SPI_TXCRCR寄存器。
  • 将SPI_CR1寄存器的SPE位置1,使能SPI。
  • 开始并保持通信,直至最后一个数据(字节或半字)被发送或接收前:软件管理的全双工或单向发送模式下,在最后一个数据被写入时设置CRCNEXT,以表明最后一个字节传输完毕后将发送CRC;单向接收模式下,在收到倒数第2个数据后,设置CRCNEXT,以便SPI做好准备在接收完最后1个数据后进入CRC阶段。CRC传输期间,CRC计算被冻结。
  • 最后一个数据传输完毕后,SPI进入CRC传输和校验阶段。在全双工或单向接收模式下,接收到的CRC值会与SPR_RXCRCR中的值进行比较。如果二者不匹配,SPI_SR寄存器中的CRCERR标志位会被置1。如果设置了SPI_CR2寄存器中的ERRIE位,会产生中断

注意事项:

  • 当SP处于从模式时要当心,只有时钟稳定(即,时钟处于稳态)时,才可使能CRC计算。否则可能会发生错误的CRC计算。事实上,只要设置了CRCEN=1,CRC即开始对SCK输入时钟进行响应,而不管SPE位是何状态。换句话说,即使SPI还没启动,如果输入时钟抖动,也会被认为是数据bit,并对其进行CRC计算。
  • 高比特率情况下,发送CRC时必须小心。CRC传输阶段所用的CPU周期该越少越好。在CRC发送过程中,严禁在程序中调用函数,以防在接收最后的数据和CRC时出错。实际上,在发送/接收最后一个数据之前,必须将CRCNEXT位置1。
  • 由于对CPU的访问不可避免的会影响SPI的带宽,因此高比特率情况下,建议采用DMA模式以避免影响SPI的速度。
  • 当SPI被配置为从模式,并且使用了NSS硬件模式时,NSS引脚应该在数据传输和CRC传输期间保持为低电平。

当SPI被配置为从模式且开启CRC时,即使在NSS引脚上施加高电平,仍会执行CRC计算。当主机交替地与多个从机进行通信时就可能会出现这种情况。为保持主机和从机各自CRC计算的同步,在断开一个从机(NSS拉高)到选通另一个从机中间,应清除双方的CRC值。步骤如下:

  • 关闭SPI(SPE=0)
  • 关闭CRC(CRCEN=0)
  • 使能CRC(CRCEN=1)
  • 使能SPI(SPE=1)

八、使用DMA进行SPI通信

为了达到最大通信速度,SPI通信过程中要不断的向Tx buffer中送入待发送数据,并及时读取Rx buffer中接收到的数据,否则会发生溢出。为了便于实现高速传输,SPI的DMA功能采用了一种简单的请求/应答协议。

对Tx buffer和Rx buffer的DMA访问必须单独发出,对应的使能位分别为SPI_CR2寄存器中的TXDMAEN和RXDMAEN。

  • 发送时,每次TXE=1都会发出DMA请求。然后DMA写入SPI_DR寄存器(这会清除TXE)
  • 接收时,每次RXNE=1都会发出DMA请求。然后DMA读取SPI_DR寄存器(这会清除RXNE)

当SPI仅用来发送数据时,可以仅使能DMA发送(TXDMAEN=1)通道。这种情况下,OVR会被置1,因为接收到的数据未被读取。不用管他即可。同理,当SPI仅用来接收数据时,可以仅使能DMA接收(RXDMAEN)通道。

在发送模式下,当DMA已经写完所有待传输数据(DMA_ISR寄存器的TCIF=1),为了避免最后的数据被损坏,关闭SPI或进入STOP模式前,必须监测BSY是否被置0。需要注意的是:在非连续通信中,写入SPI_DR和设置BSY的操作之间有2个APB时钟周期的延迟。由于这一原因,写入最后一个数据后,必须首先等待TXE=1,任何等待BSY=0。

图12和13分别为发送和接收过程DMA时序图。

自学STM32 - SPI同步串行通信

图12. 发送过程DMA时序图

自学STM32 - SPI同步串行通信

图13. 接收过程DMA时序图

DMA模式启用CRC

当SPI启用DMA模式且使能CRC通信时,通信结束前的CRC发送和接收是自动的,即无需使用CRCNEXT位。接收到CRC后,必须将其读入SPI_DR寄存器来清除RXNE位。如果传输过程中出现数据损耗,在数据和CRC传输结束后,SPI_SR寄存器中的CRCERR位会被置1。

九. SPI寄存器

STM32F103ZET6中与SPI相关的寄存器共有9个,地址映射与复位值表如图1所示。这些寄存器必须按按半字(16 bits)或全字(32 bits)访问。

1. SPI_CR1寄存器

该寄存器主要管理SPI工作模式、时钟极性和相位、波特率、数据格式,以及使能SPI。

位置

名称

功能说明

15

BIDIMODE

Bidirectional data mode enable

0:选择双线单向模式;1:选择单线双向模式。

注: I2S模式下不使用。

14

BIDIOE

Output enable in bidirectional mode

0:输出禁止(只收模式);1:输出使能(只发模式)。

与BIDIMODE位一起决定在单线双向模式下数据的传输方向

注: I2S模式下不使用;主模式下,用MOSI,从模式下用MISO。

13

CRCEN

Hardware CRC calculation enable

0:禁止CRC计算;1:启动CRC计算。

注:只有在SPE=0时,才能写该位,否则出错; I2S模式下不使用

12

CRCNEXT

CRC transfer next

0:数据阶段;1:下一次发送CRC(CRC阶段)

注意:全双工或单发模式,在最后一个数据写入DR寄存器后必须立即设置该位;单收模式,必须收到倒数第2个数据时设置该位;DMA模式下应保持该位为0; I2S模式下不使用。

11

DFF

Data frame format

0:8位格式;1:16位格式

注意:只有当SPE=0时,才能写该位,否则出错; I2S模式下不使用

10

RXONLY

Receive only

0:全双工(收&发);1:单收模式

该位与BIDIMODE位一起决定在双线双向模式下的传输方向。也常用于多从设备系统,该位为1的从设备不会输出,避免数据线上的冲突。

注:I2S模式下不使用

9

SSM

Software slave management

0:禁用软件从设备管理;1:启用软件从设备管理。

当SSM被置1时, NSS引脚上的电平由SSI位的值决定。

注: I2S模式下不使用。

8

SSI

Internal slave select

该位仅在SSM=1时有效。此位的值决定NSS引脚的电平,NSS引脚上的输入输出值被忽略。

注: I2S模式下不使用。

7

LSBFIRST

Frame format

0:先发送MSB;1:先发送LSB

注:当通信正在进行时,不要改变该位的值;I2S模式下不使用。

6

SPE

SPI enable

0:禁用SPI;1:使能SPI。

注: I2S模式下不使用;禁用SPI时,必须按所需步骤操作。

5:3

BR[2:0]

Baud rate control

xxx=000-111对应波特率=FPCLK/(2^(xxx+1))

注意:通信正在进行时,不能修改这些位; I2S模式下不使用。

2

MSTR

Master selection

0:配置为从设备;1:配置为主设备。

注:当通信正在进行的时候,不能修改该位;I2S模式下不使用。

1

CPOL

Clock polarity

0: 闲时SCK保持低电平;1: 闲时SCK保持高电平。

注:当通信正在进行的时候,不能修改该位;I2S模式下不使用。

0

CPHA

Clock phase

0: 从第1个时钟边沿开始数据采样;1:第2个时钟边沿开始采样。

注:当通信正在进行的时候,不能修改该位; I2S模式下不使用。

2. SPI_CR2寄存器

这个寄存器主要管理SPI中断、片选引脚以及收、发DMA的使能。

位置

名称

功能说明

7

TXEIE

Tx buffer empty interrupt enable

0:屏蔽TXE中断;1:使能TXE中断。TXE标志位被置1时产生中断。

6

RXNEIE

RX buffer not empty interrupt enable

0:屏蔽RXNE中断;1:使能RXNE中断。RXNE标志位被置1时产生中断。

5

ERRIE

Error interrupt enable

0:屏蔽错误中断;1:使能错误中断。

此位控制出现错误时是否产生中断。响应的错误包括SPI模式下的CRCERR,OVR,MODF,以及I2S模式下的UDR和OVR。

2

SSOE

SS output enable

0:禁用主模式下的SS输出;1:启用主模式下的SS输出。

注:此bit在I2S模式下无效。

1

TXDMAEN

Tx buffer DMA enable

0:禁用Tx缓存DMA;1:使能Tx缓存DMA。

当该位为1时,置TXE=1,立即会产生DMA请求。

0

RXDMAEN

Rx buffer DMA enable

0:禁用Rx缓存DMA;1:使能Rx缓存DMA。

当该位为1时,置RXNE=1,立即会产生DMA请求。

3. SPI_SR寄存器

该寄存器管理SPI的工作状态和错误标志。

位置

名称

功能说明

7

BSY

Busy flag

0:SPI空闲;1:SPI工作中,或者发送缓冲非空。

该位由硬件置位或者复位。

注:使用这个位时需要特别小心

6

OVR

Overrun flag

0:没有出现溢出错误;1:出现溢出错误。

该位由硬件置位,由软件序列复位。

5

MODF

Mode fault

0:没有出现模式错误;1:出现模式错误。

该位由硬件置位,由软件序列复位。

注: I2S模式下不使用

4

CRCERR

CRC error flag

0:收到的CRC值和SPI_RXCRCR寄存器中的值匹配;

1:收到的CRC值和SPI_RXCRCR寄存器中的值不匹配。

该位由硬件置位,由软件写0复位。

注: I2S模式下不使用。

3

UDR

Underrun flag

0:未发生下溢;1:发生下溢。

该标志位由硬件置,由一个软件序列清0。

注:在SPI模式下不使用。

2

CHSIDE

Channel side

0:需要传输或者接收左声道;1:需要传输或者接收右声道。

注:在SPI模式下不使用。在PCM模式下无意义。

1

TXE

Transmit buffer empty

0:发送缓存非空;1:发送缓存为空。

0

RXNE

Receive buffer not empty

0:接收缓冲为空;1:接收缓冲非空。

4. SPI_DR寄存器

这是一个16位的数据寄存器。存储接收或待发送数据。数据寄存器被分为2个缓存。写入时写到Tx 缓存,读取时返回的是Rx缓存中的值。

注意:(仅对SPI模式)根据DFF位设置,数据可以为8或16-bit。该选择必须在使能SPI前完成。对8-bit,仅使用低位,接收时MSB置0。

5. SPI_CRCPR寄存器

该寄存器包含了CRC计算时用到的多项式。其复位值为0x0007,根据应用可以设置其他数值。

注:在I2S模式下不使用

6. SPI_RXCRCR寄存器

启用CRC时,此寄存器存储依据接收的字节使用CRCPR中的多项式计算出的CRC值。当CRCEN=1时,该寄存器被复位。

对8位数据帧,仅低8位参与计算,执行CRC8的标准;对16位数据帧,该寄存器中的16位均参与计算,执行CRC16的标准。

注:BSY=1时可能读到错误的值。I2S模式不使用。

7. SPI_TXCRCR寄存器

启用CRC时,此寄存器存储依据发送的字节使用CRCPR中的多项式计算出的CRC值,并作为最后一个数据发送出去。当CRCEN=1时,该寄存器被复位。

对8位数据帧,仅低8位参与计算,执行CRC8的标准;对16位数据帧,该寄存器中的16位均参与计算,执行CRC16的标准。

注:BSY=1时可能读到错误的值。I2S模式不使用。

8. SPI_I2SCFGR寄存器

该寄存器主要进行I2S的配置。对SPI,仅用到I2SMOD位,将该位设为0时,外设为SPI接口,设为1时,则为I2S接口。

9. SPI_I2SPR寄存器

该寄存器设置I2S预分频参数。

十、HAL库SPI相关函数

HAL库中USART相关的常规库函数有29个,在stm32f1xx_hal_spi.h中声明,stm32f1xx_hal_spi.c中定义。如果基于STM32CubeIDE编程,大多函数都可以自动生成。只需适当添加代码和编写所需的*Callback即可。仅将函数名称罗列如下,大多可以从名称看出功能。

/* Initialization/de-initialization functions */ HAL_SPI_Init(); HAL_SPI_DeInit(); HAL_SPI_MspInit(); HAL_SPI_MspDeInit(); /* I/O operation functions / HAL_SPI_Transmit(); HAL_SPI_Receive(); HAL_SPI_TransmitReceive(); HAL_SPI_Transmit_IT(); HAL_SPI_Receive_IT(); HAL_SPI_TransmitReceive_IT(); HAL_SPI_Transmit_DMA(); HAL_SPI_Receive_DMA(); HAL_SPI_TransmitReceive_DMA(); HAL_SPI_DMAPause(); HAL_SPI_DMAResume(); HAL_SPI_DMAStop(); /* Transfer Abort functions / HAL_SPI_Abort(); HAL_SPI_Abort_IT(); /* Request and Callcack functions / HAL_SPI_IRQHandler(); HAL_SPI_TxCpltCallback(); HAL_SPI_RxCpltCallback(); HAL_SPI_TxRxCpltCallback(); HAL_SPI_TxHalfCpltCallback(); HAL_SPI_RxHalfCpltCallback(); HAL_SPI_TxRxHalfCpltCallback(); HAL_SPI_ErrorCallback(); HAL_SPI_AbortCpltCallback(); /* Peripheral State and Error functions / HAL_SPI_GetState(); HAL_SPI_GetError();

附:血泪教训

不要忽视SPI的极性和相位,如果一个SPI接口的器件,写入寄存器的值和读取的值出现错误,很可能是极性和相位设置有问题。主从机的极性和相位要相反!比如,主机在时钟的下降沿发送,从机在时钟的上升沿接收。

数据bit错位先怀疑相位,数据全错或收不到,先检查时钟极性。

读SPI接口器件的说明书时,一定要搞清它的极性和相位。从时钟和读/写时序图就可看出。

例子:ADS1256的SPI主机设置

自学STM32 - SPI同步串行通信

从机:ADS1256在SCLK的下降沿从DIN引脚接收数据,在上升沿向DOUT引脚发送数据

主机:在SCLK的下降沿从MOSI引脚接收数据(极性为0时,第2个边沿),在上升沿发送数据。

这解释了为什么CPHA设为1Edge时,读取的寄存器数据左移一位(少读了1位)。若设为CPOL=1,CPHA=1,则上升沿读不到数。CPOL=1,CPHA=0时能行吗?

自学STM32 - SPI同步串行通信

ADS1256说明书中关于数据时序的描述

简而言之:发送和接收数据应该在时钟脉冲的前后边沿,前一边沿发送,下一边沿接收。

#精品长文创作季##文章首发挑战赛#

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/97306.html

(0)

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注微信