? ? ? SPI是英语Serial Peripheralinterface的缩写,顾名思义就是串行外围设备接口。是Motorola首先在其MC68HCXX系列处理器上定义的。
? ? ? SPI是一种高速的、全双工的、同步、串行通信总线,并且在芯片的引脚上只占用四根线。主要应用于EEPROM、FLASH、实时时钟、AD转换器(之间的通信)等等。
四线制SPI:(全双工)
???? ? MOSI:串行数据输出,主机输出,从机输入。
???? ? MISO:串行数据输入,主机输入,从机输出。
???? ? SCLK: 时钟信号,由主设备产生。
???? ? CS: ??从设备片选信号,由主设备控制。?? 通过CS拉低可以选中器件
? ? ? ?串行的特点:可以同时发出和接收串行数据;可以当作主机或从机工作;提供频率可编程时钟;发送结束中断标志;写冲突保护;总线竞争保护等。抗干扰能力强、成本低、占用空间小。
SPI内部简明图:
? ? ? 由SPI内部简明图可知:主机和从机都有一个串行移位寄存器,主机通过向它的SPI串行寄存器写入一个字节来发起一次传输。寄存器通过MOSI信号线将字节传送给从机,从机也将自己的移位寄存器中的内容通过MISO信号线返回给主机。这样,两个移位寄存器中的内容就被交换。外设的写操作和读操作是同步完成的。如果只进行写操作,主机只需忽略接收到的字节;反之,若主机要读取从机的一个字节,就必须发送一个空字节来引发从机的传输。
1.不同时钟相位下的总线数据传输时序如图:
? ? ? 2. STM32F4的SPI功能很强大,SPI时钟最高可以到37.5Mhz,支持DMA,可以配置为SPI协议或者I2S协议(支持全双工I2S)。
? ? NSS:从器件选择(片选)
? ? SSI:0->禁止软件从器件管理,就是说不能用程序来选择从机,相当于SPI总线上只能挂载一个从器件。
? ? ? ? ? ?1->使能软件从器件管理,可以用代码来选择从器件,把NSS这个SPI的硬件引脚设置为普通IO使用,这个时候SPI总线可以外接多个从器件,只需要把从机的CS接口接不通IO即可。
SPI时序一共有四种模式
? ? (1)空闲状态SCLK为低电平,第一个时钟边沿采样数据。
? ? ? ? ? ? 模式0:时钟线空闲为低电平,上升沿读取数据(CPOL = 0,CPHA = 0)
? ?(2)空闲状态SCLK为低电平,第二个时钟沿采样。
? ? ? ? ? 模式1:时钟线空闲为低电平,下降沿读取数据(CPOL = 0,CPHA = 1)
? ? ?(3)空闲状态SCLK为高电平,第一个时钟沿采样。
模式2:时钟线空闲为高电平,下降沿读取数据(CPOL = 1 ,CPHA = 0)
(4)空闲状态SCLK为高电平,第二个时钟沿采样。
模式3:时钟线空闲为高电平,上升沿沿读取数据(CPOL = 1,CPHA = 1)
注意:模式0和模式3兼容(常用),模式1和模式2兼容。
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE); //使能SPI1时钟
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE); // 使能GPIOB时钟
//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);
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初始化函数 :? ??
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寄存器
SPI_Cmd(SPI1, ENABLE); //使能SPI1外设
? ? ?通信接口当然需要有发送数据和接受数据的函数,固件库提供的发送数据函数原型为:
void SPI_I2S_SendData(SPI_TypeDef* SPIx, uint16_t Data);
|
uint16_t SPI_I2S_ReceiveData(SPI_TypeDef* SPIx);
SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE);
? ? ? 在点对点的通信中,SPI接口不需要进行寻址操作,在多个从器件的系统中,每个从器件需要独立的使能信号。结构图如下:
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
}
#ifndef _SPI1_H_
#define _SPI1_H_
#include "stm32f4xx.h"
void SPI1_Init(void);
#endif
? ?注意:本人所写的文章均用于?记录自己在学习嵌入式过程!!!