SPI是串行外设接口(Serial Peripheral Interface)的缩写,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为PCB的布局上节省空间,提供方便,正是出于这种简单易用的特性,越来越多的芯片集成了这种通信协议。
SPI:高速同步串行口。是一种标准的四线同步双向串行总线,是串行外围设备接口。是Motorola首先在其MC68HCXX系列处理器上定义的。SPI接口主要应用在 EEPROM,FLASH,实时时钟,AD转换器,还有数字信号处理器和数字信号****之间。
该接口一般使用4条线:串行时钟线(SCLK)、主机输入/从机输出数据线MISO、主机输出/从机输入数据线MOSI和低电平有效的从机选择线SS(有的SPI接口芯片带有中断信号线INT、有的SPI接口芯片没有主机输出/从机输入数据线MOSI)。
SPI根据时钟极性(CPOL)和时钟相位(CPHA)的不同,能够产生4时钟时序。时钟极性(CPOL)控制时钟线空闲电平状态,时钟相位(CPHA)用来控制数据采样极性。
模式0:CPOL=0,CPHA=0
时钟线空闲电平为低电平,第一个边沿采样数据,第二个边沿发送数据;
模式1:CPOL=0,CPHA=1
时钟线空闲电平为低电平,第一个边沿发送数据,第二个边沿采样数据;
模式2:CPOL=1,CPHA=0
时钟线空闲电平为高电平,第一个边沿采样数据,第二个边沿发送数据;
模式3:CPOL=1,CPHA=1
时钟线空闲电平为高电平,第一个边沿发送数据,第二个边沿采样数据;
CPU:STM32F103ZE
屏幕:3.5寸TFTLCD屏
Flash: W25Q64(SPI方式)
软件平台: STM32CubeMx
W25Q64(64M-bit),W25Q16(16M-bit)和W25Q32(32M-bit)是为系统提供一个最小的空间、引脚和功耗的存储器解决方案的串行Flash存储器。25Q系列比普通的串行Flash存储器更灵活,性能更优越。基于双倍/四倍的SPI,它们能够可以立即完成提供数据给RAM,包括存储声音、文本和数据。芯片支持的工作电压2.7V到3.6V,正常工作时电流小于5mA,掉电时低于1uA。所有芯片提供标准的封装。
W25Q64/16/32由每页256字节组成。每页的256字节用一次页编程指令即可完成。每次可以擦除16页(1个扇区)、128 页(32KB块)、256页(64KB块)和全片擦除。
W25Q64的内存空间结构:一页256字节,4K(4096字节)为一个扇区,16个扇区为1块,容量为8M字节,共有128个块,2048个扇区。
W25Q64可擦写周期至少10万次,数据保存20年。
W25Q64驱动方式为SPI,支持SPI总线的工作模式0(0,0)和3( 1,1)。模式0和模式3。
引脚 | 说明 |
CS | 片选(低电平选中) PB12 |
SPI2_MISO | 主机输入从机输出PB14 |
SPI2_MOSI | 主机输出从机输入PB15 |
SPI_SCK | 时钟线PB13 |
CS | 片选(低电平选中) PB12 |
SPI2配置:
NSS引脚配置:
3 代码生成 3.1 SPI初始化
SPI配置信息可参考STM32中文参考手册第23.5.1SPI控制寄存器小结。
uint8_t SPI2_WROneByte(uint8_t data) { uint8_t dat_rx=0; HAL_SPI_TransmitReceive(&hspi2,&data,&dat_rx,1,100); return dat_rx; }3.3 W25Q64 编程
3.3.1 读取W25Q64制造商/芯片ID
/*获取W25Q64设备ID*/ uint16_t W25Q64_GetDeviceID(void) { uint16_t id; W25Q64_CS(0);//选中W25Q64 SPI2_WROneByte(0x90);//发送指令0x90 //发送24位地址 SPI2_WROneByte(0); SPI2_WROneByte(0); SPI2_WROneByte(0); id=SPI2_WROneByte(0xff);//制造商ID:0xef id<<=8; id|=SPI2_WROneByte(0xff);//设备ID:0x16 W25Q64_CS(1);//取消选中 return id; }
3.3.2 W25Q64页编程0x02
页编程指令允许从一个字节到256字节的数据编程(一页)(编程之前必须保证内存空间是
0XFF)。允许写入指令之前,必须先发送设备写使能指令。写使能开启后,设备才能接收编程指令。开启页编程先拉底/ CS, 然后发送指令代码“02
h”,接着发送一个 24 位地址(A23-A0)(发送3次,每次 8 位)
和至少一个数据字节(数据字节不能超过256字节)。数据字节发送完毕,需要拉高片选线 CS/,并判断状态位,等待写入结束。
进行页编程时,如果数据字节数超过了256字节,地址将自动回到页的起始地址,覆盖掉之前的数据。在某些情况下,数据字节小于256字节(同一页内), 也可以正常对其他字节存放,不会有任何影响。如果存放超过256字节的数据,需要分次编程存放。
3.3.3 W25Q64读数据0x03
读取数据指令允许按顺序读取一个字节的内存数据。当片选CS/拉低之后, 紧随其后是一个24位的地址(A23-A0)(需要发送3次,每次8个字节,先发高位)。芯片收到地址后,将要读的数据按字节大小转移出去,数据是先转移高位,对于单片机,时钟下降沿发送数据,上升沿接收数据。读数据时,地址会自动增加,允许连续的读取数据。这意味着读取整个内存的数据,只要用一个指令就可以读完。数据读取完成之后,片选信号/ CS 拉高。读取数据的指令序列,如上图所示。如果一个读数据指令而发出的时候,设备正在擦除扇区,或者(忙= 1),该读指令将被忽略,也不会对当前周期有什么影响。
3.3.4 扇区擦除0x20
扇区擦除指令可以擦除指定一个扇区(4 k字节)内所有数据,将内存空间恢复到 0xFF 状态。写入扇区擦除指令之前必须执行设备写使能(发送设备写使能指令 0x06),并判断状态寄存器(状态寄存器位最低位必须等于0才能操作)。发送的扇区擦除指令前,先拉低/ CS, 接着发送扇区擦除指令码”20 h”,和24位地址(A23-A0),地址发送完毕后,拉高片选线 CS/,并判断状态位,等待擦除结束。擦除一个扇区的最少需要 150ms 时间。
3.3.5 读状态0x05和0x35
读取状态寄存器的指令是8位的指令。发送指令之前,先将/ CS 拉低,再发送指令码“05
h”或者“35h”。设备收到读取状态寄存器的指令后,将状态信息(高位)依次移位发送出去,读出的状态信息,最低位为1代表忙,最低位为0代表可以操作,状态信息读取完毕,将片选线拉高。
读状态寄存器指令可以使用在任何时候,即使程序在擦除的过程中或者写状态寄存器周期正在进行中。这可以检测忙碌状态来确定周期是否完成,以确定设备是否可以接受另一个指令。
3.3.6 W25Q64指令表
4 主函数MX_GPIO_Init(); MX_FSMC_Init(); MX_USART1_UART_Init(); MX_SPI2_Init(); /* USER CODE BEGIN 2 */ char buff[200]; char buff_tx[]="HAL库配置SPI硬件时序驱动W25Q64S数据读写测试 -- Ver1.0"; char buf_rx[100]; NT35310_Init();//LCD初始化 LCD_Display_Str(LCD_WIDTH/2-strlen("W25Q64初始化")/2*8,20,16,(u8 *)"W25Q64初始化",BLACK); LCD_Display_Str(20,40,16,(u8 *)"W25Q64 OK",RED); uint16_t id=W25Q64_GetDeviceID(); snprintf(buff,sizeof(buff),"ID信息:%#x",id); LCD_Display_Str(20,60,16,(u8 *)buff,RED); LCD_Display_Str(LCD_WIDTH/2-strlen("W25Q64读写测试")/2*8,90,16,(u8 *)"W25Q64读写测试",BLACK); W25Q64_WriteData(100,buf_tx,sizeof(buf_tx)); W25Q64_ReadData(100,buf_rx,sizeof(buf_tx)); LCD_Display_Str(20,120,16,(u8 *)"W25Q64写数据:OK",RED); LCD_Display_Str(20,140,16,(u8 *)"W25Q64读数据:OK",RED); LCD_Display_Str(20,160,16,(u8 *)"数据内容:",RED); LCD_Display_Str(20,180,16,(u8 *)buf_rx,BLUE); while(1) { }