如何通过软件SPI读写W25Q64

STM32F1之SPI通信·软件SPI代码编写-CSDN博客

目录

1.  W25Qxx系列简介

2.  W25Q64硬件电路

 3.  W25Q64框图

4.  Flash操作注意事项

5.  代码编写

5.1  初始化

5.2  W25Q64读取ID号

5.3  W25Q64写使能

5.4  W25Q64等待忙

5.5  W25Q64页编程

5.6  W25Q64扇区擦除(4KB)

5.7  W25Q64读取数据


1.  W25Qxx系列简介

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

非易失性存储器(Non-Volatile Memory, NVM)和易失性存储器(Volatile Memory)的主要区别在于电源断电后的数据保存能力:

  1. 非易失性存储器(NVM)

    • 数据持久性:即使在断电的情况下,数据也能保持不丢失。
    • 常见类型:闪存(如USB闪存盘、固态硬盘)、硬盘驱动器(HDD)、只读存储器(ROM)、磁带等。
    • 应用场景:适用于需要长期保存数据的场合,如计算机硬盘、手机存储、固态硬盘等。
  2. 易失性存储器(Volatile Memory)

    • 数据持久性:在断电时,存储在其中的数据会丢失。
    • 常见类型:随机存取存储器(RAM),如动态随机存取存储器(DRAM)和静态随机存取存储器(SRAM)。
    • 应用场景:适用于需要高速访问数据但不需要长期保存的场合,如计算机的主内存(RAM)等。

存储介质:Nor Flash(闪存)

时钟频率: 80MHz ;

                   160MHz (Dual SPI)(双重SPI);

                   320MHz (Quad SPI)(四重SPI)。

存储容量(24位地址):

W25Q40:      4Mbit / 512KByte     

W25Q80:      8Mbit / 1MByte     

W25Q16:      16Mbit / 2MByte     

W25Q32:      32Mbit / 4MByte     

W25Q64:      64Mbit / 8MByte     

W25Q128:  128Mbit / 16MByte     

W25Q256:  256Mbit / 32MByte

2.  W25Q64硬件电路

引脚功能
VCC、GND电源(2.7V~3.6V)
CS(SS)SPI片选
CLK(SCK)SPI时钟
DI(MOSI)SPI主机输出从机输入
DO(MISO)SPI主机输入从机输出
WP写保护
HOLD数据保持

 3.  W25Q64框图

4.  Flash操作注意事项

写入操作时:

写入操作前,必须先进行写使能;

每个数据位只能由1改写为0,不能由0改写为1;

写入数据前必须先擦除,擦除后,所有数据位变为1;

擦除必须按最小擦除单元进行 连续写入多字节时,最多写入一页的数据,超过页尾位置的数据,会回到页首覆盖写入;

写入操作结束后,芯片进入忙状态,不响应新的读写操作。

读取操作时:

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

5.  代码编写

5.1  初始化

        首先初始化SPI,可见本文顶部往期回顾链接,初始化SPI:

void W25Q64_Init(void)
{
	MySPI_Init();					//先初始化底层的SPI
}

5.2  W25Q64读取ID号

        对于下图规格书,我们现将其进行宏定义:

#ifndef __W25Q64_INS_H
#define __W25Q64_INS_H

#define W25Q64_WRITE_ENABLE							0x06
#define W25Q64_WRITE_DISABLE						0x04
#define W25Q64_READ_STATUS_REGISTER_1				0x05
#define W25Q64_READ_STATUS_REGISTER_2				0x35
#define W25Q64_WRITE_STATUS_REGISTER				0x01
#define W25Q64_PAGE_PROGRAM							0x02
#define W25Q64_QUAD_PAGE_PROGRAM					0x32
#define W25Q64_BLOCK_ERASE_64KB						0xD8
#define W25Q64_BLOCK_ERASE_32KB						0x52
#define W25Q64_SECTOR_ERASE_4KB						0x20
#define W25Q64_CHIP_ERASE							0xC7
#define W25Q64_ERASE_SUSPEND						0x75
#define W25Q64_ERASE_RESUME							0x7A
#define W25Q64_POWER_DOWN							0xB9
#define W25Q64_HIGH_PERFORMANCE_MODE				0xA3
#define W25Q64_CONTINUOUS_READ_MODE_RESET			0xFF
#define W25Q64_RELEASE_POWER_DOWN_HPM_DEVICE_ID		0xAB
#define W25Q64_MANUFACTURER_DEVICE_ID				0x90
#define W25Q64_READ_UNIQUE_ID						0x4B
#define W25Q64_JEDEC_ID								0x9F
#define W25Q64_READ_DATA							0x03
#define W25Q64_FAST_READ							0x0B
#define W25Q64_FAST_READ_DUAL_OUTPUT				0x3B
#define W25Q64_FAST_READ_DUAL_IO					0xBB
#define W25Q64_FAST_READ_QUAD_OUTPUT				0x6B
#define W25Q64_FAST_READ_QUAD_IO					0xEB
#define W25Q64_OCTAL_WORD_READ_QUAD_IO				0xE3

#define W25Q64_DUMMY_BYTE							0xFF

#endif

        找到其规格书,找到其框住的部分,可以看到其获取ID的指令为9F,并且需要接收到3个字节,我们可以将其流程看做:起始、先交换发送指令9F、随后连续交换接收3个字节、停止。

对于上面三个字节的解读:

        第一个字节是厂商的ID,表示是哪个厂家生产的。

        第二个字节是设备ID,设备ID高八位,表示存储器类型。

        第三个字节也是设备ID,设备ID低八位,表示容量。

void W25Q64_ReadID(uint8_t *MID, uint16_t *DID)
{
	MySPI_Start();								//SPI起始
	MySPI_SwapByte(W25Q64_JEDEC_ID);			//交换发送读取ID的指令
	*MID = MySPI_SwapByte(W25Q64_DUMMY_BYTE);	//交换接收MID,通过输出参数返回,其中括号内的数据可以为任何数,通常我们使用0xFF,目的是将有用的数据置换过来。
	*DID = MySPI_SwapByte(W25Q64_DUMMY_BYTE);	//交换接收DID高8位,其内数据同上
	*DID <<= 8;									//高8位移到高位
	*DID |= MySPI_SwapByte(W25Q64_DUMMY_BYTE);	//或上交换接收DID的低8位,通过输出参数返回,其内数据同上,
	MySPI_Stop();								//SPI终止
}

          其中,MID 工厂ID,使用输出参数的形式返回,DID 设备ID,使用输出参数的形式返回。

5.3  W25Q64写使能

void W25Q64_WriteEnable(void)
{
	MySPI_Start();								//SPI起始
	MySPI_SwapByte(W25Q64_WRITE_ENABLE);		//交换发送写使能的指令
	MySPI_Stop();								//SPI终止
}

5.4  W25Q64等待忙

void W25Q64_WaitBusy(void)
{
	uint32_t Timeout;
	MySPI_Start();								//SPI起始
	MySPI_SwapByte(W25Q64_READ_STATUS_REGISTER_1);				//交换发送读状态寄存器1的指令
	Timeout = 100000;							//给定超时计数时间
	while ((MySPI_SwapByte(W25Q64_DUMMY_BYTE) & 0x01) == 0x01)	//循环等待忙标志位
	{
		Timeout --;								//等待时,计数值自减
		if (Timeout == 0)						//自减到0后,等待超时
		{
			/*超时的错误处理代码,可以添加到此处*/
			break;								//跳出等待,不等了
		}
	}
	MySPI_Stop();								//SPI终止
}

5.5  W25Q64页编程

void W25Q64_PageProgram(uint32_t Address, uint8_t *DataArray, uint16_t Count)
{
	uint16_t i;
	
	W25Q64_WriteEnable();						//写使能
	
	MySPI_Start();								//SPI起始
	MySPI_SwapByte(W25Q64_PAGE_PROGRAM);		//交换发送页编程的指令
	MySPI_SwapByte(Address >> 16);				//交换发送地址23~16位
	MySPI_SwapByte(Address >> 8);				//交换发送地址15~8位
	MySPI_SwapByte(Address);					//交换发送地址7~0位
	for (i = 0; i < Count; i ++)				//循环Count次
	{
		MySPI_SwapByte(DataArray[i]);			//依次在起始地址后写入数据
	}
	MySPI_Stop();								//SPI终止
	
	W25Q64_WaitBusy();							//等待忙
}

5.6  W25Q64扇区擦除(4KB)

void W25Q64_SectorErase(uint32_t Address)
{
	W25Q64_WriteEnable();						//写使能
	
	MySPI_Start();								//SPI起始
	MySPI_SwapByte(W25Q64_SECTOR_ERASE_4KB);	//交换发送扇区擦除的指令
	MySPI_SwapByte(Address >> 16);				//交换发送地址23~16位
	MySPI_SwapByte(Address >> 8);				//交换发送地址15~8位
	MySPI_SwapByte(Address);					//交换发送地址7~0位
	MySPI_Stop();								//SPI终止
	
	W25Q64_WaitBusy();							//等待忙
}

5.7  W25Q64读取数据

void W25Q64_ReadData(uint32_t Address, uint8_t *DataArray, uint32_t Count)
{
	uint32_t i;
	MySPI_Start();								//SPI起始
	MySPI_SwapByte(W25Q64_READ_DATA);			//交换发送读取数据的指令
	MySPI_SwapByte(Address >> 16);				//交换发送地址23~16位
	MySPI_SwapByte(Address >> 8);				//交换发送地址15~8位
	MySPI_SwapByte(Address);					//交换发送地址7~0位
	for (i = 0; i < Count; i ++)				//循环Count次
	{
		DataArray[i] = MySPI_SwapByte(W25Q64_DUMMY_BYTE);	//依次在起始地址后读取数据
	}
	MySPI_Stop();								//SPI终止
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

时光の尘

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值