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 小时前
【STM32设计】数控直流稳压电源的设计与实现(实物+资料+论文)
stm32·嵌入式硬件·mongodb
march_birds3 小时前
FreeRTOS 与 RT-Thread 事件组对比分析
c语言·单片机·算法·系统架构
小麦嵌入式3 小时前
Linux驱动开发实战(十一):GPIO子系统深度解析与RGB LED驱动实践
linux·c语言·驱动开发·stm32·嵌入式硬件·物联网·ubuntu
触角010100015 小时前
STM32F103低功耗模式深度解析:从理论到应用实践(上) | 零基础入门STM32第九十二步
驱动开发·stm32·单片机·嵌入式硬件·物联网
昊虹AI笔记5 小时前
使用STM32CubeMX和Keil在STM32上创建并运行一个简单的FreeRTOS多任务程序
stm32·单片机·嵌入式硬件
王光环5 小时前
单片机使用printf,不用微库
单片机·嵌入式硬件
LS_learner6 小时前
小智机器人关键函数解析,Application::OutputAudio()处理音频数据的输出的函数
人工智能·嵌入式硬件
西城微科方案开发7 小时前
体重秤PCBA电路方案组成结构
单片机·嵌入式硬件
深圳市青牛科技实业有限公司7 小时前
「青牛科技 」GC4931P/4938/4939 12-24V三相有感电机驱动芯片 对标Allegro A4931/瑞盟MS4931
科技·单片机·扫地机器人吸尘·筋膜枪电机·驱动轮电机·服务机器人驱动轮电机·工业机器人减速电机
集和诚JHCTECH7 小时前
集和诚携手Intel重磅发布BRAV-7820边缘计算新品,为车路云一体化场景提供强大算力支撑
人工智能·嵌入式硬件·边缘计算