STM32SPI通信原理

Posted 想成为大师啊

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了STM32SPI通信原理相关的知识,希望对你有一定的参考价值。

参考正点原子视频

SPI接口简介

SPI是串行外设接口(Serial Peripheral Interface)的缩写,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为PCB的布局上节省空间,提供方便,主要应用于EEPROM,FLASH,实时时钟,AD转换器,还有数字信号处理器和数字信号解码器之间,正是出于这种简单易用的特性,越来越多的芯片集成了这种通信协议,比如AT91RM9200

SPI内部结构简明图

在这里插入图片描述

SPI工作原理总结

  • 硬件上为4根线
  • 主机和从机都有一个串行移位寄存器,主机通过向它的SPI串行寄存器写入一个字节来发起一次传输
  • 串行移位寄存器通过MOSI信号线将字节传送给从机,从机也将自己的串行移位寄存器中的内容通过MISO信号线返回给主机。这样,两个移位寄存器中的内容就被交换
  • 外设的写操作和读操作是同步完成的。如果只进行写操作,主机只需忽略接收到的字节;反之,若主机要读取从机的一个字节,就必须发送一个空字节来引发从机的传输

SPI的通信原理很简单,它以主从方式工作,这种模式通常有一个主设备和一个或多个从设备,需要至少4根线,事实上3根也可以(单向传输时)。也是所有基于SPI的设备共有的,它们是MISO(主设备数据输入)、MOSI(主设备数据输出)、SCLK(时钟)、CS(片选)。

SPI接口一般使用4条线通信:

  • MISO(Master Input Slave Output):主设备数据输入,从设备数据输出
  • MOSI(Master Output Slave Input):主设备数据输出,从设备数据输入
  • SCLK(Serial Clock):时钟信号,由主设备产生
  • CS(Chip Select):从设备片选信号,由主设备控制

其中,CS是从芯片是否被主芯片选中的控制信号,也就是说只有片选信号为预先规定的使能信号时(高电位或低电位),主芯片对此从芯片的操作才有效。这就使在同一条总线上连接多个SPI设备成为可能。

接下来就负责通讯的3根线了。通讯是通过数据交换完成的,这里先要知道SPI是串行通讯协议,也就是说数据是一位一位的传输的。这就是SCLK时钟线存在的原因,由SCLK提供时钟脉冲,SDI,SDO则基于此脉冲完成数据传输。数据输出通过 SDO线,数据在时钟上升沿或下降沿时改变,在紧接着的下降沿或上升沿被读取。完成一位数据传输,输入也使用同样原理。因此,至少需要8次时钟信号的改变(上沿和下沿为一次),才能完成8位数据的传输。

时钟信号线SCLK只能由主设备控制,从设备不能控制。同样,在一个基于SPI的设备中,至少有一个主设备。这样的传输方式有一个优点,在数据位的传输过程中可以暂停,也就是时钟的周期可以为不等宽,因为时钟线由主设备控制,当没有时钟跳变时,从设备不采集或传送数据。SPI还是一个数据交换协议:因为SPI的数据输入和输出线独立,所以允许同时完成数据的输入和输出。芯片集成的SPI串行同步时钟极性和相位可以通过寄存器配置,IO模拟的SPI串行同步时钟需要根据从设备支持的时钟极性和相位来通讯。

最后,SPI接口的一个缺点:没有指定的流控制,没有应答机制确认是否接收到数据。

SPI接口框图

在这里插入图片描述
STM32 SPI接口可配置为支持SPI协议或者支持I2S音频协议,默认为SPI模式。可以通过软件切换到I2S方式

SPI特征

  • 3线全双工同步传输
  • 8或16位传输帧格式选择
  • 主或从操作
  • 支持多主模式
  • 8个主模式波特率预分频系数(最大为fpclk/2)
  • 从模式频率(最大为fpclk/2)
  • 主模式和从模式的快速通信
  • 主模式和从模式下均可以由软件或硬件进行NSS管理,主/从操作模式的动态改变
  • 可编程的时钟极性和相位
  • 可编程的数据顺序,MSB在前或LSB在前
  • 可触发中断的专用发送和接收标志
  • SPI总线忙状态标志
  • 支持可靠通信的硬件CRC
  • ----在发送模式下,CRC值可以被作为最后一个字节发送
  • ----在全双工模式中对接收到的最后一个字节自动进行CRC校验
  • 可触发中断的主模式故障,过载以及CRC错误标志
  • 支持DMA功能的1字节发送和接收缓冲器:产生发送和接收请求

NSS脚

NSS:从器件选择。这是用于选择从器件的可选引脚。此引脚用作“片选”,可让 SPI主器件与从器件进行单独通信,从而并避免数据线上的竞争。从器件的 NSS 输入可由主器件上的标准 IO 端口驱动。 NSS 引脚在使能(SSOE 位)时还可用作输出,并可在SPI 处于主模式配置时驱动为低电平。通过这种方式,只要器件配置成 NSS 硬件管理模式,所有连接到该主器件 NSS 引脚的它器件 NSS 引脚都将呈现低电平,并因此而作为从器件。当配置为主模式,且 NSS 配置为输入(MSTR=1 且 SSOE=0)时,如果NSS 拉至低电平, SPI 将进入主模式故障状态: MSTR 位自动清零,并且器件配置为从模式。

1) NSS管脚为输出时

主模式配置,并且使能(SSOE 位)时可用作输出。对外输出低电平,那么当其他SPI器件的NSS管脚与之相连,并且为硬件从器件管理时,则其他器件会被选定为从器件。另外,还可以把NSS管脚当做片选管脚使用。

2) NSS管脚为输入时

  • 软件从器件管理:SSM = 1,从器件选择信息在内部由 SPI_CR1 寄存器中的 SSI
    位的值驱动。此时SSI位的值替代了NSS管脚的电平信号
  • 硬件从器件管理:SSM = 0,当器件在主模式下工作时才使用此配置。当NSS管脚输入为高电平,芯片会被配置为主器件;反之会被芯片会被配置为从器件。

结论:

  • 假如选择硬件从器件管理,则NSS管脚应该接高电平,此时STM32为SPI主机。

  • 假如选择软件从器件管理,则SS I位应该配置1,此时STM32为SPI主机。

建议选择软件从器件管理,能省下一个IO口,此时 NSS管脚可以作为普通IO口使用。

时钟信号的相位和极性

  • SPI_CR寄存器的CPOL和CPHA位,能够组合成四种可能的时序关系。CPOL(时钟极性)位控制在没有数据传输时时钟的空闲状态电平,此位对主模式和从模式下的设备都有效。如果CPOL被清’0‘,SCK引脚在空闲状态保持低电平;如果CPOL被置’1‘,SCK引脚在空闲状态保持高电平。
  • 如果CPHA(时钟相位)位被置’1‘,SCK时钟的第二个边沿(CPOL位为0时就是下降沿,CPOL位为’1‘时就是上升沿)进行数据位的采样,数据在第二个时钟边沿被锁存。如果CPHA位被清’0‘,SCK时钟的第一边沿(CPOL位为’0‘时就是下降沿,CPOL位为’1‘时就是上升沿)进行数据位采样,数据在第一个时钟沿被所存。
  • CPOL时钟极性和CPHA时钟相位的组合选择数据捕捉的时钟边沿。

在这里插入图片描述
数据帧格式

  • 根据SPI_CR1寄存器中的LSBFIRST位,输出数据位时可以MSB在先也可以LSB在先
  • 根据SPI_CR1寄存器的DFF位,每个数据帧可以是8位或是16位。所选择的数据帧格式对发送和/或接收都有效

状态标志

  • 应用程序通过3个状态标志可以完全监控SPI总线的状态

发送缓冲器空闲标志(TXE)

  • 此标志为’1‘时表明发送缓冲器为空,可以写下一个待发送的数据进入缓冲器中。当写入SPI_DR时,TXE标志被清除

接收缓冲器非空(RXNE)

  • 此标志为’1‘时表明在接收缓冲器中包含有效的接收数据。读SPI数据寄存器可以清除此标志

忙(Busy)标志

  • BSY标志由硬件设置与清除(写入此位无效果),此标志表明SPI通信层的状态

SPI中断

在这里插入图片描述

SPI引脚配置

在这里插入图片描述
在这里插入图片描述

常用寄存器

  • SPI控制寄存器1(SPI_CR1)
  • SPI控制寄存器2(SPI_CR2)
  • SPI状态寄存器(SPI_SR)
  • SPI数据寄存器(SPI_DR)
  • SPI_I2S配置寄存器(SPI_I2S_CFGR)
  • SPI_I2S预分频寄存器(SPI_I2SPR)

SPI相关库函数

初始化函数

void SPI_Init(SPI_TypeDef* SPIx, SPI_InitTypeDef* SPI_InitStruct);

作用:初始化SPI的相关参数,比如方向(全双工)、主从模式、数据大小、CPOL、CPHA、片选软件模式、预分频系数等。

使能函数

void SPI_Cmd(SPI_TypeDef* SPIx, FunctionalState NewState);
void SPI_I2S_ITConfig(SPI_TypeDef* SPIx, uint8_t SPI_I2S_IT, FunctionalState NewState);
void SPI_I2S_DMACmd(SPI_TypeDef* SPIx, uint16_t SPI_I2S_DMAReq, FunctionalState NewState);

作用:使能SPI接口;使能SPI中断;使能SPI的DMA功能。

数据传输函数

void SPI_I2S_SendData(SPI_TypeDef* SPIx, uint16_t Data);
uint16_t SPI_I2S_ReceiveData(SPI_TypeDef* SPIx);

作用:分别用于SPI传输数据、接收数据。

状态位函数

FlagStatus SPI_I2S_GetFlagStatus(SPI_TypeDef* SPIx, uint16_t SPI_I2S_FLAG);
void SPI_I2S_ClearFlag(SPI_TypeDef* SPIx, uint16_t SPI_I2S_FLAG);
ITStatus SPI_I2S_GetITStatus(SPI_TypeDef* SPIx, uint8_t SPI_I2S_IT);
void SPI_I2S_ClearITPendingBit(SPI_TypeDef* SPIx, uint8_t SPI_I2S_IT);

作用:前两者用于获得和清除SPI的各种状态位;后两者则针对SPI的中断标志位。

typedef struct
{
	u16 SPI_Direction;//用于设置SPI单向或者双向数据模式。
	u16 SPI_Mode;//用于设置SPI的工作模式;
	u16 SPI_DataSize;//用于设置SPI数据大小;
	u16 SPI_CPOL;//用于选择串行时钟的稳态;
	u16 SPI_CPHA;//用于设置位捕捉的时钟活动沿;
	u16 SPI_NSS;//用于指定NSS信号由硬件(NSS引脚)还是软件(SSI位)管理
	u16 SPI_BaudRatePrescaler;//用于定义比特率预分频的值,该值用于设置发送和接收的SCK时钟
	u16 SPI_FirstBit;//用于指定数据传输从MSB位还是LSB位开始
	u16 SPI_CRCPolynomial;//定义用于CRC值计算的多项式
}SPI_InitTypeDef;

其中每一个参数初始化可取的值可以查询相关的手册得到,以下用一个例子来说明每一个参数的初始化使用情况。

1.SPI_DeInit函数的功能是将外设SPIx寄存器重设为默认值。输入参数SPIx可以是1和2,是用来选择SPI外设的。

2.SPI_Init函数的功能是根据SPI_InitStruct中指定的参数初始化外设SPIx寄存器。SPI_InitDefType的结构体是定义在文件stm32f10x_spi.h里。

// 依据SPI_InitTsructure中指定的参数初始化SPI1

SPI_InitTypeDef SPI_InitStructure;

SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;//SPI设置双线双向全双工

SPI_InitStructure.SPI_Mode = SPI_Mode_Master;//设置为主SPI;

SPI_InitStructure.SPI_DataSize = SPI_DataSize_16b;//SPI发送接收16位帧;

SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;//时钟悬空高;

SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;//数据捕获于第2个时钟沿;

SPI_InitStructure.SPI_NSS = SPI_NSS_Hard;//NSS由外部引脚管理

SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_128;//比特率预分频为128;

SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;//数据传输从MSB位开始

SPI_InitStructure.SPI_CRCPolynomial =7//CRC计算的初值为7;

SPI(SPI1,SPI_InitStructure)

3.SPI_Cmd函数的功能是使能或者失能SPI外设。

SPI_Cmd(SPI1,ENABLE);

4.SPI_ITCinfig函数的功能是使能或者失能指定的SPI中断,该函数可取的参数值是SPI_IT_TXE/RXNE/ERR:发送缓存空中断频屏蔽/接受缓存非空中断屏蔽/错误中断屏蔽。

SPI_ITConfig(SPI2,SPI_IT_TXE,ENABLE);

5.SPI_DMACmd函数的功能是使能或者失能指定SPI的DMA请求。该函数可取的值为SPI_DMAReq_Tx/Rx:选择Tx/Rx缓存DMA传输请求。

SPI_DMACmd(SPI2,SPI_CmdRq_Tx,ENABLE);

6.SPI_SendData函数的功能是通过外设SIPx发送一个数据

SPI_SendData(SPI1,0XA5);

7.SPI_ReceiveData函数的功能是返回通过SPIx最近接受的数据,其中接收到的数据是16位的数据。

u16 ReceiveData;
ReceiveData = SPI_ReceiveData(SPI2);

8.SPI_GetFlagStatus函数的功能是检查指定的SPI标志位置位与否。一共有4种可取值:SPI_FLAG_BSY/OVR/MODF/RXNE:忙/超出/模式错误/接收缓存非空标志位。

9.SPI_ClearFlag函数的功能是清楚SPIx的待处理标志位。

SPI_ClearFlag(SPI2,SPI_FLAG_OVER);

10.spi_GetITStatus函数的功能是检查指定的SP中断发生与否。参数可取的值为:SPI_IT_OVR/MODF/CRC/RXNE/TXE.

ITStatus = Status;

Status = SPI_GetITStatus(SPI1,SPI_IT_OVR);

11.SPI_ClearITPendingBit函数的功能是清除SPIx的中断处理位。

SPI_ClearITPendingBit(SPI2,SPI_IT_CRCERR);

程序配置过程

①配置相关引脚的复用功能,使能SPIx时钟

void GPIO_Init(GPIO_TypeDef* GPIOx,GPIO_InitTypeDef* GPIO_InitStruct);

②初始化SPIx,设置SPIx工作模式

void SPI_Init(SPI_TypeDef* SPIx, SPI_InitTypeDef* SPI_InitStruct);

③使能SPIx

void SPI_Cmd(SPI_TypeDef* SPIx, FunctionalState NewState);

④SPI传输数据

void SPI_I2S_SendData(SPI_TypeDef* SPIx, uint16_t Data);
uint16_t SPI_I2S_ReceiveData(SPI_TypeDef* SPIx);

⑤查看SPI传输状态

SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE);

硬件连接

在这里插入图片描述

以上是关于STM32SPI通信原理的主要内容,如果未能解决你的问题,请参考以下文章

STM32与FPGA进行SPI通信

STM32 SPI 与 HAL 通信

STM32和ADXL345之间的SPI通信

STM32 SPI通讯

STM32 & TLV5628 SPI 通信

STM32F103(二十六)SPI通信(+两块STM32之间的SPI通信)