SPI通信协议

目录

什么是SPI

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

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

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

四根通信线:SCK(Serial Clock)时钟线、MOSI(Master Output Slave Input)主机输出从机输入、MISO(Master Input Slave Output)主机输入从机输出、SS(Slave Select)从机选择线。

有的设备也叫这四根线为SCLK, SDI, SDO, CS

CS/SS:从设备片选信号,由主设备控制。它的功能是用来作为"片选引脚",也就是选择指定的从设备,让主设备可以单独地与特定从设备通讯,避免数据线上的冲突。SPI通信协议规定每个从机都有一根片选信号线,所以这个线不止一根。

对于IIC来说,由于IIC是开漏输出的结构,属于弱上拉,这就使得由低到高时,上升沿时间较长,这就限制了最大通信速度。
SPI是全双工且SPI没有定义速度限制,一般的实现通常能达到甚至超过10 Mbps。

相较于IIC,SPI也是没有应答机制的,发送和接收都不会检查对方是否存在。

SPI硬件电路

所有SPI设备的SCK、MOSI、MISO分别连在一起。

主机另外引出多条SS控制线,分别接到各从机的SS引脚。
输出 引脚配置为推挽 输出,推挽输出的上拉和下拉能力都很强,所以上拉或者下拉的速度就很快,这就保证了他可以快速的完成电平的转换,这就保证了更高的传输速度。输入 引脚配置为浮空或上拉输入。

可以看到,所以设备的MISO都是连接在一起的,所以此时如果多个从机发数据就会造成信号的冲突,所以需要一些办法规避这些冲突。于是SPI协议就规定当从机没有被选中的时候,MISO引脚要表现为高阻态,高阻态就相当于引脚断开,不输出任何电平。只有被选中了(SS为低电平)才能选为推挽输出。

没被选中和被选中了,这些状态都是在从机中自动变化的,我们配置的是主机的程序,所以我们不需要关心。

SPI收发数据

SPI基本的收发电路就是下面这样的一个移位的模型

主机和从机都有一个八位的移位寄存器,主机上的时钟线每来一个时钟,数据左移一位。主机移除的数据移到从机的右边,从机移除的数据移到主机的右边,形成一个环。在时钟的上升沿,数据移出,移到引脚上,其实就是放在了数据输出寄存器上。时钟的下降沿,数据移入,写入到移位寄存器。 八个时钟后,主机和从机的数据就完成了交换。SPI的数据收发就是基于字节交换这个基本单元来进行的。我们只接收不发送就可以随便发一个数据,将从机的数据置换过来就好了。或者只发送不接收,那就接收到的数据不读就好了。

SPI时序

起始条件:SS从高电平切换到低电平

终止条件:SS从低电平切换到高电平

下降沿是通信的开始,上升沿是通信的结束。
时序基本单元:
SPI通信有4种不同的操作模式,不同的从设备可能在出厂时就是配置为某种模式,这是不能改变的;但我们的通信双方必须是工作在同一模式下,所以我们可以对我们的主设备的SPI模式进行配置,通过CPOL(时钟极性)和CPHA(时钟相位)来控制我们主设备的通信模式。这就有了四种通信模式。

软件模拟SPI的话,我们只需要遵守从机的模式就行了,不同的从机支持一种或者多种模式,选择任意一种即可,使用的时候需要注意时序,我们给不同的时序,从机可以识别到,因为不同的模式时序是不一样的,时序对,才能正确的控制从机。

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

  • CPOL=0,表示当SCLK=0时处于空闲态,所以有效状态就是SCLK处于高电平时
  • CPOL=1,表示当SCLK=1时处于空闲态,所以有效状态就是SCLK处于低电平时

时钟相位(CPHA)定义数据的采集时间:

  • CPHA=0,在时钟的第一个跳变沿(上升沿或下降沿)进行数据采样。在第2个边沿发送数据
  • CPHA=1,在时钟的第二个跳变沿(上升沿或下降沿)进行数据采样。在第1个边沿发送数据

需要注意的是CPHA只是决定第几个边沿采样,并不决定是上升沿还是下降沿采样。

模式0

模式0在第一个边沿就要移入数据,所以在SCK第一个边沿之前就要提前移出数据。所以在SS的下降沿就要移出数据了。

模式0也是最常用的模式。

模式1

上图中,在SS未被选中的时候,MISO用一条中间的线来表示高阻态。

模式2

与模式0的SCK相反

模式3

与模式1的SCK相反

SPI第一个时钟周期发送的信息根据芯片的不同也会不同,有的是指令(比如写使能),如果指令长度大于一个字节,那么也可能多个周期都是指令。

IIC的有效数据流第一个字节是寄存器地址,之后是读写的数据,使用的是读写寄存器的模型。

而SPI通常采用指令码+读写数据的模型。SPI起始后,第一个发送给从机的数据,一般叫做指令码。在从机中对应的会定义一个指令集,当我们需要发送什么指令时,就可以在起始后的第一个字节发送指令集里面的数据。这样就能指导从机完成相应的功能了。不同的指令可以由不同的数据个数。

W25Q64简介

W25Qxx系列是一种低成本、小型化、使用简单的非易失性存储器,常应用于数据存储、字库存储、固件程序存储等场景。

存储介质:Nor Flash(闪存)

时钟频率:80MHz / 160MHz (Dual SPI) / 320MHz (Quad SPI)

存储容量(24位地址):

W25Q40: 4Mbit / 512KByte

W25Q80: 8Mbit / 1MByte

W25Q16: 16Mbit / 2MByte

W25Q32: 32Mbit / 4MByte

W25Q64: 64Mbit / 8MByte

W25Q128: 128Mbit / 16MByte

W25Q256: 256Mbit / 32MByte

硬件电路



WP:写保护,配合内部的寄存器配置可以实现硬件的写保护。写保护低电平有效,WP接低电平,保护住不让写。

HOLD:数据保持,低电平有效。如果在进行正常读写时,突然产生中断,然后想用SPI通信线去操控其他器件,这时如果把CS置回高电平,时序就终止了,但如果又不想终止总线,又想操作其他器件,这就可以HOLD引脚置低电平,这样芯片就HOLD住了。芯片释放总线,但是时序不会终止,他会记住当前的状态。当操作完其他器件可以回过来,HOLD置回高电平,然后继续HOLD之前的时序。

对于W25Q64内部的存储是将整个空间划分为很块,64KB为一块,一块划分为若干扇区,一个扇区4KB,一个扇区划分为若干页,一页256字节。

Flash操作注意事项

Flash为了保证掉电不丢失,存储容量足够大,成本足够低,所以Flash存储器回在其他地方,比如操作的便捷性等做出一些让步。所以Flash的写入和读取并不像RAM那样简单直接。

写入操作时:

  • 写入操作前,必须先进行写使能
  • 每个数据位只能由1改写为0,不能由0改写为1
  • 写入数据前必须先擦除,擦除后,所有数据位变为1
  • 擦除必须按最小擦除单元进行
  • 连续写入多字节时,最多写入一页的数据,超过页尾位置的数据,会回到页首覆盖写入。写入操作是先写入到RAM(缓存区)中,W25Q64的RAM大小是256字节,就是一页的大小,所以写操作不能超过页的大小。而且写入操作必须是在页首的地址才行,如果是页中间,那就是在RAM中间开始写的,此时写到RAM底部,再向下写,就会回到RAM顶部开始写。此时写完,再拷贝到Flash中,就会造成数据错乱。
  • 写入(包括擦除)操作结束后,芯片进入忙状态,不响应新的读写操作。因为要将缓冲区的数据写入到FLASH。

读取操作时:

  • 直接调用读取时序,无需使能,无需额外操作,没有页的限制,读取操作结束后不会进入忙状态,但不能在忙状态时读取。

SPI代码实现

c 复制代码
/*
	将W25Q64的
	CS端接PA4
	DO端接PA6
	CLK接PA5
	DI端接PA7
*/
void MySPI_W_CS(uint8_t bit_value)
{
	GPIO_WriteBit(GPIOA, GPIO_Pin_4, (BitAction)bit_value);
}

void MySPI_W_SCK(uint8_t bit_value)
{
	GPIO_WriteBit(GPIOA, GPIO_Pin_5, (BitAction)bit_value);
}

void MySPI_W_MOSI(uint8_t bit_value)
{
	GPIO_WriteBit(GPIOA, GPIO_Pin_7, (BitAction)bit_value);
}

uint8_t MySPI_R_MISO()
{
	return GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_6);
}

void MySPI_Init()
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_7;
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_6;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	//修改引脚默认电平
	MySPI_W_SCK(0);//默认选择模式0
	MySPI_W_CS(1);//默认不选中
	
}

//W25Q64支持模式0和模式3
void MySPI_Start()//使用模式0
{
	MySPI_W_CS(0);
}

void MySPI_Stop()
{
	MySPI_W_CS(1);
}

//使用模式0的时序
uint8_t MySPI_SwapByte(uint8_t ByteSend)//返回值为读取一个数据。ByteSend为交换的时候要给的数据
{
	for(int i = 0; i < 8; i++)
	{
		MySPI_W_MOSI(ByteSend & 0x80);
		ByteSend <<= 1;//左移1位,最低位补0
		MySPI_W_SCK(1);
		if(MySPI_R_MISO() == 1)
		{
			ByteSend |= 0x01;
		}
		
		MySPI_W_SCK(0);
	}
	return ByteSend;
}
//uint8_t MySPI_SwapByte(uint8_t ByteSend)
//{
//	uint8_t i, ByteReceive = 0x00;
//	
//	for (i = 0; i < 8; i ++)
//	{
//		MySPI_W_MOSI(ByteSend & (0x80 >> i));
//		MySPI_W_SCK(1);
//		if (MySPI_R_MISO() == 1){ByteReceive |= (0x80 >> i);}
//		MySPI_W_SCK(0);
//	}
//	
//	return ByteReceive;
//}
相关推荐
智商偏低2 小时前
单片机之helloworld
单片机·嵌入式硬件
青牛科技-Allen3 小时前
GC3910S:一款高性能双通道直流电机驱动芯片
stm32·单片机·嵌入式硬件·机器人·医疗器械·水泵、
望获linux4 小时前
【实时Linux实战系列】CPU 隔离与屏蔽技术
java·linux·运维·服务器·操作系统·开源软件·嵌入式软件
森焱森5 小时前
无人机三轴稳定控制(2)____根据目标俯仰角,实现俯仰稳定化控制,计算出升降舵输出
c语言·单片机·算法·架构·无人机
白鱼不小白5 小时前
stm32 USART串口协议与外设(程序)——江协教程踩坑经验分享
stm32·单片机·嵌入式硬件
S,D6 小时前
MCU引脚的漏电流、灌电流、拉电流区别是什么
驱动开发·stm32·单片机·嵌入式硬件·mcu·物联网·硬件工程
芯岭技术9 小时前
PY32F002A单片机 低成本控制器解决方案,提供多种封装
单片机·嵌入式硬件
youmdt9 小时前
Arduino IDE ESP8266连接0.96寸SSD1306 IIC单色屏显示北京时间
单片机·嵌入式硬件
嘿·嘘10 小时前
第七章 STM32内部FLASH读写
stm32·单片机·嵌入式硬件
Meraki.Zhang10 小时前
【STM32实践篇】:I2C驱动编写
stm32·单片机·iic·驱动·i2c