13.STM32F40x SPI协议内容接收及代码配置

发布时间:2024年01月19日

一、SPI协议内容

? ? ? SPI是英语Serial Peripheralinterface的缩写,顾名思义就是串行外围设备接口。是Motorola首先在其MC68HCXX系列处理器上定义的。

? ? ? SPI是一种高速的、全双工的、同步、串行通信总线,并且在芯片的引脚上只占用四根线。主要应用于EEPROM、FLASH、实时时钟、AD转换器(之间的通信)等等。

四线制SPI:(全双工)

???? ? MOSI:串行数据输出,主机输出,从机输入。

???? ? MISO:串行数据输入,主机输入,从机输出。

???? ? SCLK: 时钟信号,由主设备产生。

???? ? CS: ??从设备片选信号,由主设备控制。?? 通过CS拉低可以选中器件

? ? ? ?串行的特点:可以同时发出和接收串行数据;可以当作主机或从机工作;提供频率可编程时钟;发送结束中断标志;写冲突保护;总线竞争保护等。抗干扰能力强、成本低、占用空间小。

SPI内部简明图:

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

二、SPI时序图

1.不同时钟相位下的总线数据传输时序如图:

? ? ? 2. STM32F4的SPI功能很强大,SPI时钟最高可以到37.5Mhz,支持DMA,可以配置为SPI协议或者I2S协议(支持全双工I2S)。

三、SPI功能框图

? ? NSS从器件选择(片选)

? ? SSI0->禁止软件从器件管理,就是说不能用程序来选择从机,相当于SPI总线上只能挂载一个从器件。

? ? ? ? ? ?1->使能软件从器件管理,可以用代码来选择从器件,把NSS这个SPI的硬件引脚设置为普通IO使用,这个时候SPI总线可以外接多个从器件,只需要把从机的CS接口接不通IO即可。

四、STM32 SPI 相位时钟和时钟极性

SPI时序一共有四种模式

? ? (1)空闲状态SCLK为低电平,第一个时钟边沿采样数据。

? ? ? ? ? ? 模式0:时钟线空闲为低电平,上升沿读取数据(CPOL = 0CPHA = 0

? ?(2)空闲状态SCLK为低电平,第二个时钟沿采样。

? ? ? ? ? 模式1:时钟线空闲为低电平,下降沿读取数据(CPOL = 0,CPHA = 1)

? ? ?(3)空闲状态SCLK为高电平,第一个时钟沿采样。

模式2:时钟线空闲为高电平,下降沿读取数据(CPOL = 1 ,CPHA = 0)

(4)空闲状态SCLK为高电平,第二个时钟沿采样。

模式3:时钟线空闲为高电平,上升沿沿读取数据(CPOL = 1CPHA = 1

注意:模式0和模式3兼容(常用),模式1和模式2兼容。

五、配置SPI(注意:本文以STM32F405的SPI1为例)

(一)配置相关引脚的GPIO及复用功能,使能SPI1/GPIOB时钟
? ??(1)使能SPI1时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);  //使能SPI1时钟
?(2)使能GPIOB时钟
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);  // 使能GPIOB时钟
?(3)GPIO引脚初始化
//GPIO初始化SPI1_SCK-PB3  SPI1_MISO-PB4  SPI1_MOSI-PB5

GPIO_InitTypeDef GPIO_SPI1_Init;  //定义结构体变量
GPIO_SPI1_Init.GPIO_Mode  = GPIO_Mode_AF;  //复用模式
GPIO_SPI1_Init.GPIO_OType = GPIO_OType_PP;  //推挽输出
GPIO_SPI1_Init.GPIO_Pin   = GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_5 ;
GPIO_SPI1_Init.GPIO_PuPd  = GPIO_PuPd_NOPULL;
GPIO_SPI1_Init.GPIO_Speed = GPIO_Speed_50MHz;  //速度50MHz
GPIO_Init(GPIOB, &GPIO_SPI1_Init);
(4)复用PB3,PB4,PB5为SPI1
GPIO_PinAFConfig(GPIOB,GPIO_PinSource3,GPIO_AF_SPI1);//PB3复用为SPI1
GPIO_PinAFConfig(GPIOB,GPIO_PinSource4,GPIO_AF_SPI1); //PB4复用为SPI1
GPIO_PinAFConfig(GPIOB,GPIO_PinSource5,GPIO_AF_SPI1); //PB5复用为SPI1
(二)初始化SPI1,设置SPI1工作模式等。

?????????? SPI初始化函数 :? ??

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

? SPI初始化结构体里面各个参数说明:

?????????? 第一个参数SPI_Direction:是用来设置SPI的通信方式,可以选择为半双工,全双工,以及串行发和串行收方式,这里我们选择全双工模式SPI_Direction_2Lines_FullDuplex

第二个参数SPI_Mode用来设置SPI的主从模式,这里我们设置为主机模式SPI_Mode_Master根据自己的需求也可以选择为从机模式SPI_Mode_Slave。

第三个参数SPI_DataSiz为8位还是16位帧格式选择项,这里我们是8位传输,选择SPI_DataSize_8b

第四个参数SPI_CPOL用来设置时钟极性,我们设置串行同步时钟的空闲状态为高电平所以我们选择SPI_CPOL_High

第五个参数SPI_CPHA用来设置时钟相位,也就是选择在串行同步时钟的第几个跳变沿(上升或下降)数据被采样,可以为第一个或者第二个条边沿采集,这里我们选择第二个跳变沿,所以选择SPI_CPHA_2Edge

第六个参数SPI_NSS设置NSS信号由硬件(NSS管脚)还是软件控制,这里我们通过软件控制NSS关键,而不是硬件自动控制,所以选择SPI_NSS_Soft

第七个参数SPI_BaudRatePrescaler很关键,就是设置SPI波特率预分频值也就是决定SPI的时钟的参数,从2分频到256分频8个可选值,初始化的时候我们选择256分频值SPI_BaudRatePrescaler_256,传输速度为84M/256=328.125KHz。

第八个参数SPI_FirstBit设置数据传输顺序是MSB位在前还是LSB位在前,,这里我们选择SPI_FirstBit_MSB高位在前

第九个参数SPI_CRCPolynomial是用来设置CRC校验多项式,提高通信可靠性,大于1即可。?????

?? 以上为九个参数的介绍,下面是这九个参数的配置代码:

SPI_InitTypeDef  SPI_InitStructure;
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;  //双线双向全双工
SPI_InitStructure.SPI_Mode = SPI_Mode_Master;  //主SPI 
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;  // SPI发送接收8位帧结构
SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;  //串行同步时钟的空闲状态为高电平
SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge; //第二个跳变沿数据被采样
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;  //NSS信号由软件控制
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256;  //预分频256
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;  //数据传输从MSB位开始
SPI_InitStructure.SPI_CRCPolynomial = 7;  //CRC值计算的多项式
SPI_Init(SPI1, &SPI_InitStructure);  //根据指定的参数初始化外设SPIx寄存器
(三)使能SPI1
SPI_Cmd(SPI1, ENABLE);  //使能SPI1外设
(四)SPI传输数据

? ? ?通信接口当然需要有发送数据和接受数据的函数,固件库提供的发送数据函数原型为:

void SPI_I2S_SendData(SPI_TypeDef* SPIx, uint16_t Data);

(五)SPI接收数据函数? ?? ? ? ? ? ?
uint16_t SPI_I2S_ReceiveData(SPI_TypeDef* SPIx);
? ? ? ? ? ? ? ? ? ? ?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
(六)查看SPI传输状态? ? ? ? ?
SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE);
? ? ? ? ? ? ? ? ? ? ? ??

六、SPI物理拓扑结构

? ? ? 在点对点的通信中,SPI接口不需要进行寻址操作,在多个从器件的系统中,每个从器件需要独立的使能信号。结构图如下:

七、总结

?? (1)spi1.c
void SPI1_Init(void){
GPIO_InitTypeDef  GPIO_InitStructure;  //GPIO初始化结构体
SPI_InitTypeDef    SPI_InitStructure;   //SPI初始化结构体

RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB,ENABLE); //使能GPIOB
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);//使能SPI1时钟

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用功能
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_5;//PB3~5
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化

GPIO_PinAFConfig(GPIOB,GPIO_PinSource3,GPIO_AF_SPI1);//PB3复用为SPI1
GPIO_PinAFConfig(GPIOB,GPIO_PinSource4,GPIO_AF_SPI1); //PB4复用为SPI1
GPIO_PinAFConfig(GPIOB,GPIO_PinSource5,GPIO_AF_SPI1); //PB5复用为SPI1

SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;  //设置SPI全双工
SPI_InitStructure.SPI_Mode = SPI_Mode_Master; //设置SPI工作模式:主SPI
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; //设置SPI的数据大小: 8位帧结构
SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;//串行同步时钟的空闲状态
SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;//数据捕获于第二个时钟沿
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; //NSS信号由软件管理
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256; //预分频256
SPI_InitStructure.SPI_CRCPolynomial = 7; //CRC值计算的多项式
SPI_Init(SPI1, &SPI_InitStructure);//根据指定的参数初始化外设SPIx寄存器

SPI_Cmd(SPI1, ENABLE); //使能SPI1
}
(2)spi1.h
#ifndef _SPI1_H_
#define _SPI1_H_

#include "stm32f4xx.h"


void SPI1_Init(void);

#endif

? ?注意:本人所写的文章均用于?记录自己在学习嵌入式过程!!!

文章来源:https://blog.csdn.net/2301_78324535/article/details/135683388
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。