通用外设-W25Q64

前言

一、SPI通信

二、W25Q64基初时序

1.各种命令代码

2.代码

1.写使能指令

2.读取芯片是否忙碌状态并等待

3.写入数据

4.擦除函数操作

5.读取代码

三.验证

四.擦除说明

总结


前言

在单片机中一般32K FLASH就够用了,但是当我们使用图片或其他大量数据时就不够用了,又因为数据为固定数据,不需要为此而换大容量芯片。因此用外挂存储芯片来解决这个问题。

本篇文章采用SPI软件通信的方式进行讲解,了解存储芯片同时加深SPI通信了解。

环境:

芯片:STM32F103C8T6

Keil:V5.24.2.0

外设:4针OLED屏,W25Q64芯片


一、SPI通信

关于SPI通信简介看我之前文章:

写文章-CSDN创作中心

还包含了读ID指令。本篇就不在赘述。

二、W25Q64基初时序

1.各种命令代码

标黄色的为重要代码,我们应尽量掌握。

通用外设-W25Q64_第1张图片

各个代码进行宏定义,方便我们使用,也方便我们以后查阅

#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        //无用数据(不是无用)

2.代码

1.写使能指令

在写入数据时必须进行的操作,防止误操作

void W25Q64_WriteEnable(void)		//书写使能命令
{
	MySPI_Start();
	MySPI_SwapByte(W25Q64_WRITE_ENABLE);
	MySPI_Stop();
}

2.读取芯片是否忙碌状态并等待

也是比较简单:

开始传输,发送读取指令,等待芯片忙碌结束,停止。

void W25Q64_WaitBusy(void)		//读取芯片是否忙碌状态并等待
{
	uint32_t Timeout;
	MySPI_Start();
	MySPI_SwapByte(W25Q64_READ_STATUS_REGISTER_1);
	Timeout =1000000;			//防死循环措施,时间可以加长
	while((MySPI_SwapByte(W25Q64_DUMMY_BYTE) & 0x01) == 0x01)
	{
		Timeout --;
		if(Timeout == 0)
		{
			break;
		}
	}
	MySPI_Stop();
}

3.写入数据

1.地址:指定写入的地址

2.数据:需要写入的数据,根据芯片描述,每页写入数据不大于64K,大于64K需要进行分页写入

3.数组的大小,方便进行循环

/**************************
@:地址
@:要写入的数据
@:数组数
***************************/
void W25Q64_PageProgram(uint32_t Address,uint32_t *DataArray,uint16_t Count)//写入代码
{
	uint16_t i;
	
	W25Q64_WriteEnable();
	
	MySPI_Start();
	MySPI_SwapByte(W25Q64_PAGE_PROGRAM);
	MySPI_SwapByte(Address >> 16);	//总共24位地址,右移16后剩高8位
	MySPI_SwapByte(Address >> 8);	//右移8位后剩高位16,但是只能发送8位,高8位舍弃
	MySPI_SwapByte(Address);
	for(i = 0;i < Count ;i ++)	//对数组进行写入
	{
		MySPI_SwapByte(DataArray[i]);
	}
	MySPI_Stop();
	
	W25Q64_WaitBusy();				//等待芯片空闲,在读写后进行等待

注:因为芯片写入数据需要时间,不是我们MCU发送完那边就全部写入了。因此我们要等待其数据全部存储完,不然后果就是我们的下次写入操作会数据不准确。当然我们也可以把这行代码放在操作函数前。判断芯片是否忙碌状态。

4.擦除函数操作

 代码注释较全。

void W25Q64_SectorErase(uint32_t Address)	//擦除函数操作
{
	W25Q64_WriteEnable();
	W25Q64_WaitBusy();
	
	MySPI_Start();
	MySPI_SwapByte(W25Q64_SECTOR_ERASE_4KB);
	MySPI_SwapByte(Address >> 16);	//总共24位地址,右移16后剩高8位
	MySPI_SwapByte(Address >> 8);	//右移8位后剩高位16,但是只能发送8位,高8位舍弃
	MySPI_SwapByte(Address);
	MySPI_Stop();
	
	W25Q64_WaitBusy();				//等待芯片空闲,需要在读写后进行等待
}

5.读取代码

void W25Q64_ReadData(uint32_t Address,uint32_t *DataArray,uint32_t Count)//读取代码
{
	uint32_t i;
	W25Q64_WaitBusy();
	MySPI_Start();

	MySPI_SwapByte(W25Q64_READ_DATA);
	MySPI_SwapByte(Address >> 16);	//总共24位地址,右移16后剩高8位
	MySPI_SwapByte(Address >> 8);	//右移8位后剩高位16,但是只能发送8位,高8位舍弃
	MySPI_SwapByte(Address);
	for(i = 0;i < Count ;i++)
	{
		DataArray[i] = MySPI_SwapByte(W25Q64_DUMMY_BYTE);//以此来置换回需要的数据
	}
	MySPI_Stop();
}

三.验证

写入和读取

uint32_t ArrayWrite[] = {0x01,0x02,0x03};
uint32_t ArrayRead[3];

int main(void)
{	
	OLED_Init();
	W25Q64_Init();
	
	W25Q64_SectorErase(0x000000);        //擦除操作
	W25Q64_PageProgram(0x000000,ArrayWrite,3);        //写入数据
	W25Q64_ReadData(0x000000,ArrayRead,3);        读取数据
	
	OLED_ShowHexNum(2, 1, ArrayWrite[0], 2);    //显示写入的三个数据
	OLED_ShowHexNum(2, 5, ArrayWrite[1], 2);
	OLED_ShowHexNum(2, 9, ArrayWrite[2], 2);
	
	OLED_ShowHexNum(3, 1, ArrayRead[0], 2);       //显示读取的三个数据
	OLED_ShowHexNum(3, 5, ArrayRead[1], 2);
	OLED_ShowHexNum(3, 9, ArrayRead[2], 2);
	
	while (1)
	{
		
	}
}

四.擦除说明

因芯片设计问题,数据在写入时,只能从1转为0,不可以从0转为1,如果要想从0 转为1,就只能进行擦除作业。擦除分整个擦除,扇区,块擦除三个等级,范围大小不一样,看需要进行操作

因此。我们在写入数据前应尽量进行扇区擦除来保证数据的准确性。大家可以屏闭擦除函数进行验证。


总结

W25Q64芯片存储数据还是比较好用,原理比较清楚。但还是有一些问题待解决

1.写入大数据(如200KB)而MCU除了必要的程序数据(不包含要写入W25Q64的数据)外还剩40K数据。我们需要对200K数据进行5次分割才能写入数据,比较麻烦且不方便量产

2.因芯片每页数据最大64K,因此我们还需对数据进行准确分割,不能超过。

你可能感兴趣的:(学习,单片机,stm32,嵌入式硬件)