W25QXX保存图片并在LCD屏幕显示

前言

本文使用CubeMx新建工程,使用硬件spi驱动W25Q64和LCD屏幕,本文使用的屏幕为1.54寸240x240屏幕。

                                             

本文中使用的MCU为STM32F411CEU6;其他MUC配置方式雷同。

一、新建工程

1.设置时钟

在主界面选择好MCU后开始配置时钟、spi等。

时钟源设置

 STM32F411CEU6的主频最高可以设置为100MHZ,根据实际MCU设置。

2.配置spi1

W25Q64与SPI1连接。 

这里我把PA4设置为W25Q64的CS引脚。

 3.配置spi2

 

这里我把PB2设置为LCD的CS引脚。

配置1us定时器,这里使用的是TIM9, F1系列把99改为71,其他MCU按照情况设置。 

 

起个名字,不能包含中文 ,路径也不要有中文。

二、代码

1.在TIM.C添加us延时函数

void delay_us(uint16_t us){
	uint16_t differ = 0xffff-us-5;				
	__HAL_TIM_SET_COUNTER(&htim9,differ);	//设定TIM7计数器起始值
	HAL_TIM_Base_Start(&htim9);		//启动定时器	
	
	while(differ < 0xffff-5){	//判断
		differ = __HAL_TIM_GET_COUNTER(&htim9);		//查询计数器的计数值
	}
	HAL_TIM_Base_Stop(&htim9);
}

使用了哪个定时器就把&htim9改为对应的定时器。 

2.在TIM.H声明函数

void delay_us(uint16_t us);

3.在SPI.C添加函数

uint8_t SPI1_ReadWriteByte(uint8_t TxData)
{
    uint8_t Rxdata;
    HAL_SPI_TransmitReceive(&hspi1,&TxData,&Rxdata,1, 1000);       
 	return Rxdata;          		    //返回收到的数据		
}

4.在SPI.H声明函数

uint8_t SPI1_ReadWriteByte(uint8_t TxData);

5.修改W25QXX代码

找到的w25qxx驱动代码一次只能写入和读取不超过65532字节,修改一下,可写入、读取超过65535字节的数据。

W25Q64.C

#include "25Q64.h"
#include "spi.h"
#include "tim.h"
#define u8 uint8_t
#define u16 uint16_t
#define u32 uint32_t

u16 W25QXX_TYPE=W25Q64;	//默认是W25Q256

//4Kbytes为一个Sector
//16个扇区为1个Block
//W25Q256
//容量为32M字节,共有512个Block,8192个Sector 
													 
//初始化SPI FLASH的IO口
void W25QXX_Init(void)
{ 
    u8 temp;

	W25QXX_CS_Set();;			                //SPI FLASH不选中
	W25QXX_TYPE=W25QXX_ReadID();	        //读取FLASH ID.
    if(W25QXX_TYPE==W25Q256)                //SPI FLASH为W25Q256
    {
        temp=W25QXX_ReadSR(3);              //读取状态寄存器3,判断地址模式
        if((temp&0X01)==0)			        //如果不是4字节地址模式,则进入4字节地址模式
		{
			W25QXX_CS_Clr();			        //选中
			SPI1_ReadWriteByte(W25X_Enable4ByteAddr);//发送进入4字节地址模式指令   
			W25QXX_CS_Set();    		        //取消片选   
		}
    }
}  

//读取W25QXX的状态寄存器,W25QXX一共有3个状态寄存器
//状态寄存器1:
//BIT7  6   5   4   3   2   1   0
//SPR   RV  TB BP2 BP1 BP0 WEL BUSY
//SPR:默认0,状态寄存器保护位,配合WP使用
//TB,BP2,BP1,BP0:FLASH区域写保护设置
//WEL:写使能锁定
//BUSY:忙标记位(1,忙;0,空闲)
//默认:0x00
//状态寄存器2:
//BIT7  6   5   4   3   2   1   0
//SUS   CMP LB3 LB2 LB1 (R) QE  SRP1
//状态寄存器3:
//BIT7      6    5    4   3   2   1   0
//HOLD/RST  DRV1 DRV0 (R) (R) WPS ADP ADS
//regno:状态寄存器号,范:1~3
//返回值:状态寄存器值
u8 W25QXX_ReadSR(u8 regno)   
{  
	u8 byte=0,command=0; 
    switch(regno)
    {
        case 1:
            command=W25X_ReadStatusReg1;    //读状态寄存器1指令
            break;
        case 2:
            command=W25X_ReadStatusReg2;    //读状态寄存器2指令
            break;
        case 3:
            command=W25X_ReadStatusReg3;    //读状态寄存器3指令
            break;
        default:
            command=W25X_ReadStatusReg1;    
            break;
    }    
	W25QXX_CS_Clr();                           //使能器件   
	SPI1_ReadWriteByte(command);            //发送读取状态寄存器命令    
	byte=SPI1_ReadWriteByte(0Xff);          //读取一个字节  
	W25QXX_CS_Set();                         //取消片选     
	return byte;   
} 
//写W25QXX状态寄存器
void W25QXX_Write_SR(u8 regno,u8 sr)   
{   
    u8 command=0;
    switch(regno)
    {
        case 1:
            command=W25X_WriteStatusReg1;    //写状态寄存器1指令
            break;
        case 2:
            command=W25X_WriteStatusReg2;    //写状态寄存器2指令
            break;
        case 3:
            command=W25X_WriteStatusReg3;    //写状态寄存器3指令
            break;
        default:
            command=W25X_WriteStatusReg1;    
            break;
    }   
	W25QXX_CS_Clr();                           //使能器件   
	SPI1_ReadWriteByte(command);            //发送写取状态寄存器命令    
	SPI1_ReadWriteByte(sr);                 //写入一个字节  
	W25QXX_CS_Set();                         //取消片选     	      
}   
//W25QXX写使能	
//将WEL置位   
void W25QXX_Write_Enable(void)   
{
	W25QXX_CS_Clr();                           //使能器件   
    SPI1_ReadWriteByte(W25X_WriteEnable);   //发送写使能  
	W25QXX_CS_Set();                         //取消片选     	      
} 
//W25QXX写禁止	
//将WEL清零  
void W25QXX_Write_Disable(void)   
{  
	W25QXX_CS_Clr();                           //使能器件   
    SPI1_ReadWriteByte(W25X_WriteDisable);  //发送写禁止指令    
	W25QXX_CS_Set();                         //取消片选     	      
} 

//读取芯片ID
//返回值如下:				   
//0XEF13,表示芯片型号为W25Q80  
//0XEF14,表示芯片型号为W25Q16    
//0XEF15,表示芯片型号为W25Q32  
//0XEF16,表示芯片型号为W25Q64 
//0XEF17,表示芯片型号为W25Q128 	  
//0XEF18,表示芯片型号为W25Q256
u16 W25QXX_ReadID(void)
{
	u16 Temp = 0;	  
	W25QXX_CS_Clr();;				    
	SPI1_ReadWriteByte(0x90);//发送读取ID命令	    
	SPI1_ReadWriteByte(0x00); 	    
	SPI1_ReadWriteByte(0x00); 	    
	SPI1_ReadWriteByte(0x00); 	 			   
	Temp|=SPI1_ReadWriteByte(0xFF)<<8;  
	Temp|=SPI1_ReadWriteByte(0xFF);	 
	W25QXX_CS_Set();;				    
	return Temp;
}   		    
//读取SPI FLASH  
//在指定地址开始读取指定长度的数据
//pBuffer:数据存储区
//ReadAddr:开始读取的地址(24bit)
//NumByteToRead:要读取的字节数(最大65535)//修改为uint32_t 可读取超过65535字节
void W25QXX_Read(u8* pBuffer,u32 ReadAddr,uint32_t NumByteToRead)   
{ 
 	uint32_t i;   										    
	W25QXX_CS_Clr();                           //使能器件   
    SPI1_ReadWriteByte(W25X_ReadData);      //发送读取命令  
    if(W25QXX_TYPE==W25Q256)                //如果是W25Q256的话地址为4字节的,要发送最高8位
    {
        SPI1_ReadWriteByte((u8)((ReadAddr)>>24));    
    }
    SPI1_ReadWriteByte((u8)((ReadAddr)>>16));   //发送24bit地址    
    SPI1_ReadWriteByte((u8)((ReadAddr)>>8));   
    SPI1_ReadWriteByte((u8)ReadAddr);   
    for(i=0;i<NumByteToRead;i++)
	{ 
        pBuffer[i]=SPI1_ReadWriteByte(0XFF);    //循环读数  
    }
	W25QXX_CS_Set();;  				    	      
}  
//SPI在一页(0~65535)内写入少于256个字节的数据
//在指定地址开始写入最大256字节的数据
//pBuffer:数据存储区
//WriteAddr:开始写入的地址(24bit)
//NumByteToWrite:要写入的字节数(最大256),该数不应该超过该页的剩余字节数!!!	 
void W25QXX_Write_Page(u8* pBuffer,u32 WriteAddr,uint32_t NumByteToWrite)
{
 	uint32_t i;  
    W25QXX_Write_Enable();                  //SET WEL 
	W25QXX_CS_Clr();                           //使能器件   
    SPI1_ReadWriteByte(W25X_PageProgram);   //发送写页命令   
    if(W25QXX_TYPE==W25Q256)                //如果是W25Q256的话地址为4字节的,要发送最高8位
    {
        SPI1_ReadWriteByte((u8)((WriteAddr)>>24)); 
    }
    SPI1_ReadWriteByte((u8)((WriteAddr)>>16)); //发送24bit地址    
    SPI1_ReadWriteByte((u8)((WriteAddr)>>8));   
    SPI1_ReadWriteByte((u8)WriteAddr);   
    for(i=0;i<NumByteToWrite;i++)SPI1_ReadWriteByte(pBuffer[i]);//循环写数  
	W25QXX_CS_Set();                         //取消片选 
	W25QXX_Wait_Busy();					   //等待写入结束
} 
//无检验写SPI FLASH 
//必须确保所写的地址范围内的数据全部为0XFF,否则在非0XFF处写入的数据将失败!
//具有自动换页功能 
//在指定地址开始写入指定长度的数据,但是要确保地址不越界!
//pBuffer:数据存储区
//WriteAddr:开始写入的地址(24bit)
//NumByteToWrite:要写入的字节数(最大65535)//修改为uint32_t 可写入超过65535字节
//CHECK OK
void W25QXX_Write_NoCheck(u8* pBuffer,u32 WriteAddr,uint32_t NumByteToWrite)   
{ 			 		 
	uint32_t pageremain;	   
	pageremain=256-WriteAddr%256; //单页剩余的字节数		 	    
	if(NumByteToWrite<=pageremain)pageremain=NumByteToWrite;//不大于256个字节
	while(1)
	{	   
		W25QXX_Write_Page(pBuffer,WriteAddr,pageremain);
		if(NumByteToWrite==pageremain)break;//写入结束了
	 	else //NumByteToWrite>pageremain
		{
			pBuffer+=pageremain;
			WriteAddr+=pageremain;	

			NumByteToWrite-=pageremain;			  //减去已经写入了的字节数
			if(NumByteToWrite>256)pageremain=256; //一次可以写入256个字节
			else pageremain=NumByteToWrite; 	  //不够256个字节了
		}
	};	    
} 
//写SPI FLASH  
//在指定地址开始写入指定长度的数据
//该函数带擦除操作!
//pBuffer:数据存储区
//WriteAddr:开始写入的地址(24bit)						
//NumByteToWrite:要写入的字节数(最大65535)//修改为uint32_t 可读取超过65535字节   
u8 W25QXX_BUFFER[4096];		 
void W25QXX_Write(u8* pBuffer,u32 WriteAddr,uint32_t NumByteToWrite)   
{ 
	uint32_t secpos;
	uint32_t secoff;
	uint32_t secremain;	   
 	uint32_t i;    
	u8 * W25QXX_BUF;	  
   	W25QXX_BUF=W25QXX_BUFFER;	     
 	secpos=WriteAddr/4096;//扇区地址  
	secoff=WriteAddr%4096;//在扇区内的偏移
	secremain=4096-secoff;//扇区剩余空间大小   
 	//printf("ad:%X,nb:%X\r\n",WriteAddr,NumByteToWrite);//测试用
 	if(NumByteToWrite<=secremain)secremain=NumByteToWrite;//不大于4096个字节
	while(1) 
	{	
		W25QXX_Read(W25QXX_BUF,secpos*4096,4096);//读出整个扇区的内容
		for(i=0;i<secremain;i++)//校验数据
		{
			if(W25QXX_BUF[secoff+i]!=0XFF)break;//需要擦除  	  
		}
		if(i<secremain)//需要擦除
		{
			W25QXX_Erase_Sector(secpos);//擦除这个扇区
			for(i=0;i<secremain;i++)	   //复制
			{
				W25QXX_BUF[i+secoff]=pBuffer[i];	  
			}
			W25QXX_Write_NoCheck(W25QXX_BUF,secpos*4096,4096);//写入整个扇区  

		}else W25QXX_Write_NoCheck(pBuffer,WriteAddr,secremain);//写已经擦除了的,直接写入扇区剩余区间. 				   
		if(NumByteToWrite==secremain)break;//写入结束了
		else//写入未结束
		{
			secpos++;//扇区地址增1
			secoff=0;//偏移位置为0 	 

		   	pBuffer+=secremain;  //指针偏移
			WriteAddr+=secremain;//写地址偏移	   
		   	NumByteToWrite-=secremain;				//字节数递减
			if(NumByteToWrite>4096)secremain=4096;	//下一个扇区还是写不完
			else secremain=NumByteToWrite;			//下一个扇区可以写完了
		}	 
	};	 
}
//擦除整个芯片		  
//等待时间超长...
void W25QXX_Erase_Chip(void)   
{                                   
    W25QXX_Write_Enable();                  //SET WEL 
    W25QXX_Wait_Busy();   
  	W25QXX_CS_Clr();                           //使能器件   
    SPI1_ReadWriteByte(W25X_ChipErase);        //发送片擦除命令  
	W25QXX_CS_Set();                         //取消片选     	      
	W25QXX_Wait_Busy();   				   //等待芯片擦除结束
}   
//擦除一个扇区
//Dst_Addr:扇区地址 根据实际容量设置
//擦除一个扇区的最少时间:150ms
void W25QXX_Erase_Sector(u32 Dst_Addr)   
{  
	//监视falsh擦除情况,测试用   
 	//printf("fe:%x\r\n",Dst_Addr);	  
 	Dst_Addr*=4096;
    W25QXX_Write_Enable();                  //SET WEL 	 
    W25QXX_Wait_Busy();   
  	W25QXX_CS_Clr();                           //使能器件   
    SPI1_ReadWriteByte(W25X_SectorErase);   //发送扇区擦除指令 
    if(W25QXX_TYPE==W25Q256)                //如果是W25Q256的话地址为4字节的,要发送最高8位
    {
        SPI1_ReadWriteByte((u8)((Dst_Addr)>>24)); 
    }
    SPI1_ReadWriteByte((u8)((Dst_Addr)>>16));  //发送24bit地址    
    SPI1_ReadWriteByte((u8)((Dst_Addr)>>8));   
    SPI1_ReadWriteByte((u8)Dst_Addr);  
	W25QXX_CS_Set();                         //取消片选     	      
    W25QXX_Wait_Busy();   				    //等待擦除完成
}  
//等待空闲
void W25QXX_Wait_Busy(void)   
{   
	while((W25QXX_ReadSR(1)&0x01)==0x01);   // 等待BUSY位清空
}  
//进入掉电模式
void W25QXX_PowerDown(void)   
{ 
  	W25QXX_CS_Clr();                           //使能器件   
    SPI1_ReadWriteByte(W25X_PowerDown);     //发送掉电命令  
	W25QXX_CS_Set();                         //取消片选     	      
    delay_us(3);                            //等待TPD  
}   
//唤醒
void W25QXX_WAKEUP(void)   
{  
  	W25QXX_CS_Clr();                               //使能器件   
    SPI1_ReadWriteByte(W25X_ReleasePowerDown);  //  send W25X_PowerDown command 0xAB    
	W25QXX_CS_Set();                             //取消片选     	      
    delay_us(3);                                //等待TRES1
}   

 W25Q64.H

#ifndef __W25QXX_H
#define __W25QXX_H
#include "stm32f4xx_hal.h"
#include "main.h"
#define u8 uint8_t
#define u16 uint16_t
#define u32 uint32_t

//W25X系列/Q系列芯片列表	   
//W25Q80  ID  0XEF13
//W25Q16  ID  0XEF14
//W25Q32  ID  0XEF15
//W25Q64  ID  0XEF16	
//W25Q128 ID  0XEF17	
//W25Q256 ID  0XEF18
#define W25Q80 	0XEF13 	
#define W25Q16 	0XEF14
#define W25Q32 	0XEF15
#define W25Q64 	0XEF16
#define W25Q128	0XEF17
#define W25Q256 0XEF18

extern u16 W25QXX_TYPE;					//定义W25QXX芯片型号		   

//#define	W25QXX_CS 		PFout(6)  		//W25QXX的片选信号
#define W25QXX_CS_Clr() HAL_GPIO_WritePin(SPI1_CS_GPIO_Port,SPI1_CS_Pin,GPIO_PIN_RESET)//RES
#define W25QXX_CS_Set() HAL_GPIO_WritePin(SPI1_CS_GPIO_Port,SPI1_CS_Pin,GPIO_PIN_SET)

// 
//指令表
#define W25X_WriteEnable		0x06 
#define W25X_WriteDisable		0x04 
#define W25X_ReadStatusReg1		0x05 
#define W25X_ReadStatusReg2		0x35 
#define W25X_ReadStatusReg3		0x15 
#define W25X_WriteStatusReg1    0x01 
#define W25X_WriteStatusReg2    0x31 
#define W25X_WriteStatusReg3    0x11 
#define W25X_ReadData			0x03 
#define W25X_FastReadData		0x0B 
#define W25X_FastReadDual		0x3B 
#define W25X_PageProgram		0x02 
#define W25X_BlockErase			0xD8 
#define W25X_SectorErase		0x20 
#define W25X_ChipErase			0xC7 
#define W25X_PowerDown			0xB9 
#define W25X_ReleasePowerDown	0xAB 
#define W25X_DeviceID			0xAB 
#define W25X_ManufactDeviceID	0x90 
#define W25X_JedecDeviceID		0x9F 
#define W25X_Enable4ByteAddr    0xB7
#define W25X_Exit4ByteAddr      0xE9
void W25QXX_Init(void);
u16  W25QXX_ReadID(void);  	    		//读取FLASH ID
u8 W25QXX_ReadSR(u8 regno);             //读取状态寄存器 
void W25QXX_4ByteAddr_Enable(void);     //使能4字节地址模式
void W25QXX_Write_SR(u8 regno,u8 sr);   //写状态寄存器
void W25QXX_Write_Enable(void);  		//写使能 
void W25QXX_Write_Disable(void);		//写保护
void W25QXX_Write_NoCheck(u8* pBuffer,u32 WriteAddr,uint32_t NumByteToWrite);
void W25QXX_Read(u8* pBuffer,u32 ReadAddr,uint32_t NumByteToRead);   //读取flash
void W25QXX_Write(u8* pBuffer,u32 WriteAddr,uint32_t NumByteToWrite);//写入flash
void W25QXX_Erase_Chip(void);    	  	//整片擦除
void W25QXX_Erase_Sector(u32 Dst_Addr);	//扇区擦除
void W25QXX_Wait_Busy(void);           	//等待空闲
void W25QXX_PowerDown(void);        	//进入掉电模式
void W25QXX_WAKEUP(void);				//唤醒

#endif

2.Main函数

需要设置一个数组,用来存数据和读数据,如果单片机内存较小,可以将图片裁剪并分成几次传输到flash

1.定义数组的大小(根据图片大小决定)。

2.数组名称。


这里使用lmage2LCD将图片转换为.c文件

gImage_1为图片名称,注意多张图片放在pic.h文件中不要有重名。

115200为图片所占用字节。 

放在pic.h文件中。

 在main文件包含

#include "pic.h"
#include "25Q64.h"

并初始化w25q64

  W25QXX_Init();    

3.读写W25Q64

  写入

1.全片擦除,第一次使用最好全片擦除一下,等待时间十几秒钟。

2. 1为要写入的图片名(在pic.h文件中)2.从地址0开始写入  3.写入115200字节数据(图片大小为115200根据图片大小决定)。

接着依次写入剩下的图片数据 注意起始位置。

  读取  

 

依次读取四张图片并显示。

源码

https://download.csdn.net/download/nnnzn/87801820?spm=1001.2014.3001.5501

  • 4
    点赞
  • 44
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
W25Qxx 是一款常用的闪芯片,它使用 SPI 总线进行数据通信,是许多嵌入式系统的不可或缺的部件。Verilog 是一种硬件描述语言,可以用来描述电路的行为模型和结构。W25Qxx Verilog 是将 W25Qxx芯片的行为模型和控制器的硬件实现用 Verilog 语言描述出来。 W25Qxx Verilog 的实现包括对 W25Qxx 芯片的 FPGA 硬件平台,SPI 原语(Primitive)和 SPI 控制器的集成设计。为了将闪芯片和 FPGA 驱动程序连接起来,需要使用 SPI 协议控制器。设计时需要确保 SPI 控制器和芯片的工作方式兼容,并且不会产生冲突。控制器可以支持多种不同的 SPI 命令,包括写入、读取、擦除和其他控制命令。 在实现了 W25Qxx 芯片控制器之后,可以通过使用各种模拟工具进行仿真和测试。在进行测试时需要逐步验证 SPI 的基本功能,包括初始化、写入、读取、擦除和保护等,以确保闪芯片能够按预期方式正常工作。在应用程序调试阶段可以使用 JTAG 或其他 Debug 工具来检测系统是否正常工作,并且可以通过波形跟踪分析数据传输和电信号传递的行为。 总的来说, W25Qxx Verilog 实现的目的是为了方便嵌入式系统开发人员通过硬件描述语言实现 W25Qxx 芯片驱动程序的硬件实现,并将其与特定的 FPGA 硬件平台集成。这种硬件描述语言实现可以提高系统的性能和可靠性,并且更易于开发人员进行定制和控制。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值