SPI

什么是SPI

SPI 是英语Serial Peripheral interface的缩写,顾名思义就是串行外围设备接口。是Motorola(摩托罗拉)首先在其MC68HCXX系列处理器上定义的。

SPI,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为PCB的布局上节省空间,提供方便,主要应用在 EEPROM,FLASH,实时时钟,AD转换器,还有数字信号处理器和数字信号解码器之间。

SPI主从模式
SPI分为主、从两种模式,一个SPI通讯系统需要包含一个(且只能是一个)主设备,一个或多个从设备。提供时钟的为主设备(Master),接收时钟的设备为从设备(Slave),SPI接口的读写操作,都是由主设备发起。当存在多个从设备时,通过各自的片选信号进行管理。

SPI是全双工且SPI没有定义速度限制,一般的实现通常能达到甚至超过10 Mbps

SPI信号线
SPI接口一般使用四条信号线通信:
SDI(数据输入),SDO(数据输出),SCK(时钟),CS(片选)

MISO: 主设备输入/从设备输出引脚。该引脚在从模式下发送数据,在主模式下接收数据。
MOSI: 主设备输出/从设备输入引脚。该引脚在主模式下发送数据,在从模式下接收数据。
SCLK:串行时钟信号,由主设备产生。
CS/SS:从设备片选信号,由主设备控制。它的功能是用来作为“片选引脚”,也就是选择指定的从设备,让主设备可以单独地与特定从设备通讯,避免数据线上的冲突。
 

数据交换(Data Exchanges)

SPI 设备间的数据传输之所以又被称为数据交换, 是因为 SPI 协议规定一个 SPI 设备不能在数据通信过程中仅仅只充当一个 “发送者(Transmitter)” 或者 “接收者(Receiver)”. 在每个 Clock 周期内, SPI 设备都会发送并接收一个 bit 大小的数据, 相当于该设备有一个 bit 大小的数据被交换了. 一个 Slave 设备要想能够接收到 Master 发过来的控制信号, 必须在此之前能够被 Master 设备进行访问 (Access). 所以, Master 设备必须首先通过SS/CS pin 对 Slave 设备进行片选, 把想要访问的 Slave 设备选上. 在数据传输的过程中, 每次接收到的数据必须在下一次数据传输之前被采样. 如果之前接收到的数据没有被读取, 那么这些已经接收完成的数据将有可能会被丢弃, 导致 SPI 物理模块最终失效. 因此, 在程序中一般都会在 SPI 传输完数据后, 去读取 SPI 设备里的数据, 即使这些数据(Dummy Data)在我们的程序里是无用的。

SPI数据发送接收

SPI主机和从机都有一个串行移位寄存器,主机通过向它的SPI串行寄存器写入一个字节来发起一次传输。

首先拉低对应SS信号线,表示与该设备进行通信

主机通过发送SCLK时钟信号,来告诉从机写数据或者读数据

这里要注意,SCLK时钟信号可能是低电平有效,也可能是高电平有效,因为SPI有四种模式,这个我们在下面会介绍 

主机(Master)将要发送的数据写到发送数据缓存区(Menory),缓存区经过移位寄存器(0~7),串行移位寄存器通过MOSI信号线将字节一位一位的移出去传送给从机,,同时MISO接口接收到的数据经过移位寄存器一位一位的移到接收缓存区。

从机(Slave)也将自己的串行移位寄存器(0~7)中的内容通过MISO信号线返回给主机。同时通过MOSI信号线接收主机发送的数据,这样,两个移位寄存器中的内容就被交换。

如图所示:

SPI

SPI只有主模式和从模式之分,没有读和写的说法,外设的写操作和读操作是同步完成的。如果只进行写操作,主机只需忽略接收到的字节;反之,若主机要读取从机的一个字节,就必须发送一个空字节来引发从机的传输。也就是说,你发一个数据必然会收到一个数据;你要收一个数据必须也要先发一个数据。

数据传输

要开始SPI通信,主机必须发送时钟信号,并通过使能CS信号选择从机。片选通常是低电平有效信号。因此,主机必须在该信号上发送逻辑0以选择从机。SPI是全双工接口,主机和从机可以分别通过MOSI和MISO线路同时发送数据。在SPI通信期间,数据的发送(串行移出到MOSI/SDO总线上)和接收(采样或读入总线(MISO/SDI)上的数据)同时进行。串行时钟沿同步数据的移位和采样。SPI接口允许用户灵活选择时钟的上升沿或下降沿来采样和/或移位数据。欲确定使用SPI接口传输的数据位数,请参阅器件数据手册。

     在一个SPI时钟周期内,会完成如下操作:
1)主机通过MOSI线发送1位数据,从机通过该线读取这1位数据;
2)从机通过MISO线发送1位数据,主机通过该线读取这1位数据。

这是通过移位寄存器来实现的。如下图所示,主机和从机各有一个移位寄存器,且二者连接成环。随着时钟脉冲,数据按照从高位到低位的方式依次移出主机寄存器和从机寄存器,并且依次移入从机寄存器和主机寄存器。当寄存器中的内容全部移出时,相当于完成了两个寄存器内容的交换。

SPI通信的四种模式

/*四种模式其实就是确定 SCK初始电平和是上升沿还是下降沿数据采样*/

主机和从机是同时进行数据发送或接收 

 采样是指 从机或者主机得到来自串行移位寄存器的数据   发送是指 发送数据到对应的串行移位寄存器

例如采用 SPI模式 3 主模式 即CPOL=1,CPHA=1

即SCK初始电平为高电平 下降沿发送 上升沿采样

意思就是

主机:

在上升沿通过MOSI发送一位数据到主机串行移位寄存器

在下降沿通过MISO接收来自从机串行移位寄存器的一位数据

从机:

在上升沿通过SOMI发送一位数据给从机串行移位寄存器

在下降沿通过SIMO接收来自主机串行移位寄存器的一位数据

所以我们一般使用SPI模式3即CPOL=1,CPHA=1

SPI

看懂这个图!!!!!!!!!

SPI

 要看懂这些图!!!!!

SPI的四种模式,简单地讲就是设置SCLK时钟信号线的那种信号为有效信号

SPI通信有4种不同的操作模式,不同的从设备可能在出厂是就是配置为某种模式,这是不能改变的;但我们的通信双方必须是工作在同一模式下,所以我们可以对我们的主设备的SPI模式进行配置,通过CPOL(时钟极性)和CPHA(时钟相位)来

控制我们主设备的通信模式,具体如下:

SPI

*****时钟极性(CPOL)定义了总线空闲状态时的时钟电平:

CPOL=0,即SCLK起始电平为0,表示当总线处于空闲态 SCLK=0

CPOL=1,即SCLK起始电平为1,表示当总线处于空闲态 SCLK=1

*****时钟相位(CPHA)定义数据的采样时刻:

CPHA=0,在时钟的第一个跳变沿(上升沿或下降沿)对MOSI或MISO上的信号进行数据采样

CPHA=1,在时钟的第二个跳变沿(上升沿或下降沿)对MOSI或MISO上的信号进行数据采样

Mode0:CPOL=0,CPHA=0:此时空闲态时,SCLK处于低电平,数据采样是在第1个边沿,也就是SCLK由低电平到高电平的跳变,所以数据采样是在上升沿,(发送数据到移位寄存器)数据发送是在下降沿。

Mode1:CPOL=0,CPHA=1:此时空闲态时,SCLK处于低电平,数据发送是在第1个边沿,也就是SCLK由低电平到高电平的跳变,所以数据采样是在下降沿,数据发送是在上升沿。

Mode2:CPOL=1,CPHA=0:此时空闲态时,SCLK处于高电平,数据采集是在第1个边沿,也就是SCLK由高电平到低电平的跳变,所以数据采集是在下降沿,数据发送是在上升沿。

Mode3:CPOL=1,CPHA=1:此时空闲态时,SCLK处于高电平,数据发送是在第1个边沿,也就是SCLK由高电平到低电平的跳变,所以数据采集是在上升沿,数据发送是在下降沿。

所以一般用最多就是 SPI模式3 即CPOL=1,CPHA=1

数据采集是在上升沿,数据发送是在下降沿。即第一个边沿发送数据到


看一个例子:

 

假设下面的8位寄存器装的是待发送的数据10101010,上升沿发送、下降沿接收、高位先发送。

SPI

SPI

SPI

 可见 数据采样就是主机或从机从移位寄存器得到数据

数据发送就是主机或从机发送数据到移位寄存器

基于STM32的SPI基本代码

void SPI2_Init(void)
{
	GPIO_InitTypeDef	SPI2_GPIO_init;
	SPI_InitTypeDef		SPI2_Initstructure;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2,ENABLE);
	
	//SPI2 SCK 主模式推挽复用输出
	SPI2_GPIO_init.GPIO_Mode=GPIO_Mode_AF_PP;
	SPI2_GPIO_init.GPIO_Pin=GPIO_Pin_13;
	SPI2_GPIO_init.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOB,&SPI2_GPIO_init);
	
	//SPI2 MISO	全双工模式/主模式 推挽复用输出
	SPI2_GPIO_init.GPIO_Mode=GPIO_Mode_AF_PP;
	SPI2_GPIO_init.GPIO_Pin=GPIO_Pin_14;
	SPI2_GPIO_init.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOB,&SPI2_GPIO_init);
	
	//SPI2 MOSI 全双工模式/主模式 浮空输入或带上拉输入
	SPI2_GPIO_init.GPIO_Mode=GPIO_Mode_IPU;
	SPI2_GPIO_init.GPIO_Pin=GPIO_Pin_15;
	SPI2_GPIO_init.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOB,&SPI2_GPIO_init);
	
	//SPI2初始化
	SPI2_Initstructure.SPI_Direction=SPI_Direction_2Lines_FullDuplex;//设置为双线全双工
	SPI2_Initstructure.SPI_Mode=SPI_Mode_Master;//设置为主模式
	SPI2_Initstructure.SPI_DataSize=SPI_DataSize_8b;//设置数据帧大小为8位
	SPI2_Initstructure.SPI_CPOL=SPI_CPOL_High;//设置SCK起始电平为高电平  这两步设置了CPOL=1和CPAL=1
	SPI2_Initstructure.SPI_CPHA=SPI_CPHA_2Edge;//设置为第二个边沿采样		其实就是SPI模式3
	SPI2_Initstructure.SPI_BaudRatePrescaler=SPI_BaudRatePrescaler_256;//时钟预分频为256 SPI2是挂载在APB2(速度最高为36M)上的
	SPI2_Initstructure.SPI_NSS=SPI_NSS_Soft;//片选信号线由软件提供
	SPI2_Initstructure.SPI_FirstBit=SPI_FirstBit_MSB;//数据高位先行
	SPI2_Initstructure.SPI_CRCPolynomial=7;//设置CRC校验
	
	SPI_Init(SPI2,&SPI2_Initstructure);
	SPI_Cmd(SPI2,ENABLE);

	
}
//SPI读写一个字节
u8 SPI2_ReadWriteByte(u8 data)
{
	u8 ReceiveData;
	while(SPI_I2S_GetFlagStatus(SPI2,SPI_I2S_FLAG_BSY))//判断总线是否繁忙
	
	
	while(!SPI_I2S_GetFlagStatus(SPI2,SPI_I2S_FLAG_TXE))//等待发送缓存器为空
	{
		u8 i=0;
		i++;
		if(i>200)
		{
			return 0;		//超时返回错误标志
		}
	}
	SPI_I2S_SendData(SPI2,data);//发送一个字节数据data
	while(!SPI_I2S_GetFlagStatus(SPI2,SPI_I2S_FLAG_RXNE))//等待接收缓冲区非空
	{
		u8 i=0;
		i++;
		if(i>200)
		{
			return 0;		//超时返回错误标志
		}
	}
	ReceiveData=SPI_I2S_ReceiveData(SPI2);
	return ReceiveData;
}

上一篇:温故而知新之spi


下一篇:简单了解SPI机制