这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » 嵌入式开发 » MCU » STM32CubeMx之硬件SPI驱动W25Q64

共7条 1/1 1 跳转至

STM32CubeMx之硬件SPI驱动W25Q64

工程师
2022-06-19 00:01:59     打赏
1.SPI简介

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

  时钟线空闲电平为低电平,第一个边沿采样数据,第二个边沿发送数据;

poYBAGKTe8WAafPkAAEtiNNazwU314.png

模式1:CPOL=0,CPHA=1
  时钟线空闲电平为低电平,第一个边沿发送数据,第二个边沿采样数据;

poYBAGKTe_-AYcN_AAFaGKTnV3Y039.png

模式2:CPOL=1,CPHA=0
  时钟线空闲电平为高电平,第一个边沿采样数据,第二个边沿发送数据;

pYYBAGKTfBaARsLDAAFm1Hq5LOk142.png

模式3:CPOL=1,CPHA=1
  时钟线空闲电平为高电平,第一个边沿发送数据,第二个边沿采样数据;

poYBAGKTfDWADXExAAF4HWrm9dE456.png 2 硬件接口
  • CPUSTM32F103ZE

  • 屏幕:3.5寸TFTLCD屏

  • Flash: W25Q64(SPI方式)

  • 软件平台: STM32CubeMx

2.1 W25Q64简介

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。

2.2 硬件接口pYYBAGKTfJSAYDy3AADB-jTd_-k869.png
引脚 说明
CS 片选(低电平选中) PB12
SPI2_MISO 主机输入从机输出PB14
SPI2_MOSI 主机输出从机输入PB15
SPI_SCK 时钟线PB13
CS 片选(低电平选中) PB12
2.3 软件设置

SPI2配置:

pYYBAGKTfNKAHzCBAAE1vYRf4dw724.png

NSS引脚配置:

pYYBAGKTfN6AU-D6AABxrCCnUVE084.png 3 代码生成 3.1 SPI初始化

  SPI配置信息可参考STM32中文参考手册第23.5.1SPI控制寄存器小结。

在这里插入图片描述poYBAGKTfRSAL1HiAAPFTjVUqZc348.png 3.2 SPI读写一字节函数
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

pYYBAGKTfWyAUFjLAAKwLRdV-bs914.png
/*获取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

poYBAGKTfZeAMV_cAAGfmsDKw0o490.png

页编程指令允许从一个字节到256字节的数据编程(一页)(编程之前必须保证内存空间是 0XFF)。允许写入指令之前,必须先发送设备写使能指令。写使能开启后,设备才能接收编程指令。开启页编程先拉底/ CS, 然后发送指令代码“02 h”,接着发送一个 24 位地址(A23-A0)(发送3次,每次 8 位) 和至少一个数据字节(数据字节不能超过256字节)。数据字节发送完毕,需要拉高片选线 CS/,并判断状态位,等待写入结束。
  进行页编程时,如果数据字节数超过了256字节,地址将自动回到页的起始地址,覆盖掉之前的数据。在某些情况下,数据字节小于256字节(同一页内), 也可以正常对其他字节存放,不会有任何影响。如果存放超过256字节的数据,需要分次编程存放。
3.3.3 W25Q64读数据0x03

pYYBAGKTfb2AZEtqAAEDsmbzBOU116.png

读取数据指令允许按顺序读取一个字节的内存数据。当片选CS/拉低之后, 紧随其后是一个24位的地址(A23-A0)(需要发送3次,每次8个字节,先发高位)。芯片收到地址后,将要读的数据按字节大小转移出去,数据是先转移高位,对于单片机,时钟下降沿发送数据,上升沿接收数据。读数据时,地址会自动增加,允许连续的读取数据。这意味着读取整个内存的数据,只要用一个指令就可以读完。数据读取完成之后,片选信号/ CS 拉高。读取数据的指令序列,如上图所示。如果一个读数据指令而发出的时候,设备正在擦除扇区,或者(忙= 1),该读指令将被忽略,也不会对当前周期有什么影响。

3.3.4 扇区擦除0x20

poYBAGKTfeKAEK5OAACvOjcL-6Q865.png


  扇区擦除指令可以擦除指定一个扇区(4 k字节)内所有数据,将内存空间恢复到 0xFF 状态。写入扇区擦除指令之前必须执行设备写使能(发送设备写使能指令 0x06),并判断状态寄存器(状态寄存器位最低位必须等于0才能操作)。发送的扇区擦除指令前,先拉低/ CS, 接着发送扇区擦除指令码”20 h”,和24位地址(A23-A0),地址发送完毕后,拉高片选线 CS/,并判断状态位,等待擦除结束。擦除一个扇区的最少需要 150ms 时间。
3.3.5 读状态0x05和0x35

pYYBAGKTfjSAOGxWAAJhn-GvoXU455.png

读取状态寄存器的指令是8位的指令。发送指令之前,先将/ CS 拉低,再发送指令码“05 h”或者“35h”。设备收到读取状态寄存器的指令后,将状态信息(高位)依次移位发送出去,读出的状态信息,最低位为1代表忙,最低位为0代表可以操作,状态信息读取完毕,将片选线拉高。
  读状态寄存器指令可以使用在任何时候,即使程序在擦除的过程中或者写状态寄存器周期正在进行中。这可以检测忙碌状态来确定周期是否完成,以确定设备是否可以接受另一个指令。

3.3.6 W25Q64指令表

pYYBAGKTfmCARbQZAAHgC4ptNvs288.png 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)
{
}
pYYBAGKTfoGAM8FRAAf258bgxKI662.png                                          





工程师
2022-06-19 17:50:04     打赏
2楼

谢谢楼主分享的文章~!


工程师
2022-06-19 17:53:14     打赏
3楼

谢谢分享


工程师
2022-06-19 17:57:08     打赏
4楼

感谢楼主的分享


工程师
2022-06-19 18:00:06     打赏
5楼

谢谢分享


工程师
2022-06-19 18:13:53     打赏
6楼

谢谢楼主分享的文章~!


工程师
2022-06-19 18:16:45     打赏
7楼

感谢分享


共7条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]