W25Q与AT24C系列的区别及基于STM32F103驱动W25Q系列 Flash储存模块读写图片
文章目录
一、W25QXX系列和AT24CXX系列的简介
AT24C系列简介:
AT24C系列是由Microchip等公司生产的经典 EEPROM(带电可擦除可编程只读存储器)系列芯片。
- 接口类型:使用 I2C 总线通信(仅需两根线:SDA和SCL)。
- 容量范围:通常较小,常见型号如 AT24C02(2 Kb / 256 KB)至 AT24C512(512 Kb / 64 KB)。
- 主要特点:支持字节级的独立擦写;读写寿命极高(通常可达100万次),数据保存期可长达100年。
- 典型应用:主要用于保存少量的配置参数、校准数据、密码或设备序列号(例如:智能家居设备配网信息、温湿度传感器的校准值)。
W25Q系列简介:
W25Q系列是由华邦(Winbond)等公司生产的 串行 NOR Flash(闪存)系列芯片。
- 接口类型:支持标准 SPI、双重 SPI(160MHz 等效频率)、四重 SPI(320MHz 等效频率),传输速率快。
- 容量范围:容量较大,以兆比特(Mb)计算。常见型号如 W25Q16(16 Mb / 2 MB)、W25Q64(64 Mb / 8 MB)、W25Q128(128 Mb / 16 MB)。
- 主要特点:写入前必须先进行 “擦除” 操作,且通常只能以块或扇区为单位进行擦除;存储密度高,成本低。
- 典型应用:用于存储大容量的代码、文件或多媒体数据(例如:单片机/路由器固件、汉字字库、音频文件、图片资源或系统运行日志)。

二、W25QXX系列和AT24CXX系列的区别
| AT24C 系列 EEPROM | W25Q 系列 SPI Flash | |
|---|---|---|
| 存储器类型 | EEPROM(电可擦除可编程只读存储器) | NOR Flash(串行闪存) |
| 常见型号 | AT24C01 / 02 / 04 / 08 / 16 / 64 / 256 | W25Q16 / 32 / 64 / 128 / 256 |
| 通信接口 | I²C | SPI / Dual SPI / Quad SPI |
| 通信速度 | 100kHz~1MHz | 几MHz~上百MHz |
| 读数据方式 | 任意地址读取 | 任意地址读取 |
| 写数据方式 | 可直接覆盖写入 | 必须先擦除后写入 |
| 是否需要擦除 | 不需要 | 需要(最小擦除单位:4KB) |
| 最小写入单位 | 1 Byte | 1 Byte |
| 页(Page)大小 | 8B、16B、32B、64B(不同型号不同) | 256 Byte |
| 扇区 / 块 | 无 | 4KB / 64KB |
| 容量范围 | 128B ~ 512KB左右 | 2MB ~ 数百MB |
| 数据修改效率 | 高 | 较低(涉及擦除) |
| 数据读取速度 | 较慢 | 很快 |
| 数据写入速度 | 较慢 | 较快 |
| 擦写寿命 | 约100万次 | 约10万次 |
| 数据保存时间 | 约100年 | 约20年 |
| 地址空间 | 较小 | 非常大 |
| 功耗 | 较低 | 较低 |
常见容量对比
| AT24C 系列 | 容量(bit) | 容量(Byte) |
|---|---|---|
| AT24C01 | 1Kb | 128B |
| AT24C02 | 2Kb | 256B |
| AT24C04 | 4Kb | 512B |
| AT24C08 | 8Kb | 1KB |
| AT24C16 | 16Kb | 2KB |
| AT24C32 | 32Kb | 4KB |
| AT24C64 | 64Kb | 8KB |
| AT24C128 | 128Kb | 16KB |
| AT24C256 | 256Kb | 32KB |
| AT24C512 | 512Kb | 64KB |
| W25Q 系列 | 容量(Mbit) | 容量(MB) |
|---|---|---|
| W25Q16 | 16Mb | 2MB |
| W25Q32 | 32Mb | 4MB |
| W25Q64 | 64Mb | 8MB |
| W25Q128 | 128Mb | 16MB |
| W25Q256 | 256Mb | 32MB |
| W25Q512 | 512Mb | 64MB |
三、框图和主要指令码说明
这里主要以W25QXX系列进行说明,AT24CXX系列可参考以下这篇文章:STM32驱动AT24C02存储模块
框图

框图重点:
① 存储器划分:整个存储空间,首先划分若干块,对于每个快,再划分为若干个扇区。对于整个空间,会划分为很多页,每页256字节。
存储阵列组织:总容量 16MB(128Mb),采用分层结构:
块(Block):256个块,每块 64KB。整片擦除(Chip Erase) 会擦除所有块。
扇区(Sector):每块分为 16 个扇区,每个扇区 4KB。这是 最小的擦除单元。
页(Page):每扇区分为 16 页,每页 256 字节。这是 最大的连续写入单元。
② SPI控制逻辑:芯片的管理员,执行指令,读写数据
③ 状态寄存器:与忙状态,写使能,写保护等功能有关
④ 256字节页缓存:对一次性写入的数据量产生限制
框图的读写执行流程:
1. 写入流程:写入数据会先放在页缓存区存放着,写入时序结束后就会把缓存区的数据转移到Flash存储器里,在转移的过程中,芯片会进入一段忙状态,状态寄存器的BUSY位就会置1。在忙的时候,芯片不会响应新的读写时序。
为什么不直接往Flash存储器写入数据,而要经过缓存区再写入?
因为SPI写入频率很快,Flash写入需要掉电不丢失,写入时比较慢,而缓存区是RAM,速度快,可以跟得上SPI总线速度。
注意:缓存区只有256字节,写入的时序有限制,即写入一个时序,连续写入的数据量,不能超过256字节。
2. 读取流程:通过缓存区读取,但读取只需要查看电路状态即可,基本不花时间,因此,读取的限制很少,速度也很快。
Flash操作注意事项
写入操作时:
- 写入操作前,必须先进行写使能
- 每个数据位只能由1改写为0,不能由0改写为1
- 写入数据前必须先擦除,擦除后,所有数据位变为1
- 擦除必须按最小擦除单元(一个扇区4KB)进行
- 连续写入多字节时,最多写入一页的数据,超过页尾位置的数据,会回到页首覆盖写入
- 写入操作结束后,芯片进入忙状态,不响应新的读写操作
读取操作时:
- 直接调用读取时序,无需使能,无需额外操作,没有页的限制,读取操作结束后不会进入忙状态,但不能在忙状态时读取
主要指令码

-
Write Enable(0x06):写使能,在执行任何修改操作前必须发送。
-
Write Disable(0x04):写失能,保护性操作,防止误写。
-
JEDEC ID(0x9F):读取厂家ID和设备ID。ID号如下:

-
Read Data(0x03):读取数据,最常用的读取指令,需24位地址。
-
Page Program(0x02):页编程,写入数据,需24位地址,最大256字节。
-
Sector Erase(0x20):扇区擦除 (4KB),最常用的擦除指令,需24位地址。
-
Block Erase (32KB)(0x52):块擦除 (32KB),擦除较大区块。
-
Block Erase (64KB)(0xD8):块擦除 (64KB),擦除更大区块。
-
Chip Erase(0xC7 / 0x60):整片擦除,擦除整个芯片,谨慎使用。
-
Read Status Register-1(0x05):读状态寄存器1,必用:检查BUSY和WEL位。
-
Read Status Register-2 (0x35):读状态寄存器2,检查其他状态位(如QE位)。
四、STM32F103驱动W25QXX系列 Flash存储模块
准备工作
STM32F103C8T6最小系统板,W25Q128 Flash存储模块,1.69寸彩色TFT显示屏,导线若干等。
接线说明
| STM32F103C8T6 | W25Q128 |
|---|---|
| 3V3 | VCC |
| GND | GND |
| PB12 | CS |
| PB13 | CLK |
| PB14 | DO |
| PB15 | DI |
| TFT显示屏 | |
| 3V3 | VCC |
| GND | GND |
| PA0 | SCL |
| PA1 | SDA |
| PA2 | RES |
| PA3 | DC |
| PA4 | CS |
| PA5 | BLK |
代码示例
W25QXX.c
#include "w25qxx.h"
#include "myspi.h"
#include "w25qxx_ins.h"
void W25QXX_Init(void)
{
MySPI_Init();
}
void W25QXX_ReadID(uint8_t *MID, uint16_t *DID)
{
MySPI_Start();
MySPI_SwapByte(W25QXX_JEDEC_ID);
*MID = MySPI_SwapByte(W25QXX_DUMMY_BYTE);
*DID = MySPI_SwapByte(W25QXX_DUMMY_BYTE);
*DID <<= 8;
*DID |= MySPI_SwapByte(W25QXX_DUMMY_BYTE);
MySPI_Stop();
}
void W25QXX_WriteEnable(void)
{
MySPI_Start();
MySPI_SwapByte(W25QXX_WRITE_ENABLE);
MySPI_Stop();
}
void W25QXX_WaitBusy(void)
{
uint32_t Timeout;
MySPI_Start();
MySPI_SwapByte(W25QXX_READ_STATUS_REGISTER_1);
Timeout = 5000;
while((MySPI_SwapByte(W25QXX_DUMMY_BYTE) & 0x01) == 0x01)
{
Timeout --;
if(Timeout == 0)
{
break;
}
}
MySPI_Stop();
}
void W25QXX_SectorErase(uint32_t Address)
{
W25QXX_WriteEnable();
MySPI_Start();
MySPI_SwapByte(W25QXX_BLOCK_ERASE_64KB);
MySPI_SwapByte(Address >> 16);
MySPI_SwapByte(Address >> 8);
MySPI_SwapByte(Address);
MySPI_Stop();
W25QXX_WaitBusy();
}
void W25QXX_PageProgram(uint32_t Address, uint8_t *DataArray, uint16_t Count)
{
uint16_t i;
W25QXX_WriteEnable();
MySPI_Start();
MySPI_SwapByte(W25QXX_PAGE_PROGRAM);
MySPI_SwapByte(Address >> 16);
MySPI_SwapByte(Address >> 8);
MySPI_SwapByte(Address);
for(i = 0; i < Count; i++)
{
MySPI_SwapByte(DataArray[i]);
}
MySPI_Stop();
W25QXX_WaitBusy();
}
void W25QXX_ReadData(uint32_t Address, uint8_t *DataArray, uint32_t Count)
{
uint32_t i;
MySPI_Start();
MySPI_SwapByte(W25QXX_READ_DATA);
MySPI_SwapByte(Address >> 16);
MySPI_SwapByte(Address >> 8);
MySPI_SwapByte(Address);
for(i = 0; i < Count; i++)
{
DataArray[i] = MySPI_SwapByte(W25QXX_DUMMY_BYTE);
}
MySPI_Stop();
}
void W25QXX_WriteData(uint32_t Address,uint8_t *DataArray,uint32_t Count)
{
uint32_t PageRemain;
while(Count > 0)
{
PageRemain = 256 - (Address % 256);
if(Count < PageRemain)
{
PageRemain = Count;
}
W25QXX_PageProgram(Address, DataArray, PageRemain);
Address += PageRemain;
DataArray += PageRemain;
Count -= PageRemain;
}
}
void W25QXX_WritePicture(uint32_t FlashAddress,const uint8_t *pic,uint32_t Size)
{
W25QXX_SectorErase(FlashAddress);
W25QXX_WriteData(FlashAddress,(uint8_t *)pic,Size);
}
lcd.c
#include "lcd.h"
#include "lcd_init.h"
#include "lcdfont.h"
#include "delay.h"
#include "w25qxx.h"
#define PIC_BUF_SIZE 512
/******************************************************************************
函数说明:在指定区域填充颜色
入口数据:xsta,ysta 起始坐标
xend,yend 终止坐标
color 要填充的颜色
返回值: 无
******************************************************************************/
void LCD_Fill(u16 xsta,u16 ysta,u16 xend,u16 yend,u16 color)
{
u16 i,j;
LCD_Address_Set(xsta,ysta,xend-1,yend-1);//设置显示范围
for(i=ysta;i<yend;i++)
{
for(j=xsta;j<xend;j++)
{
LCD_WR_DATA(color);
}
}
}
/******************************************************************************
函数说明:在指定位置画点
入口数据:x,y 画点坐标
color 点的颜色
返回值: 无
******************************************************************************/
void LCD_DrawPoint(u16 x,u16 y,u16 color)
{
LCD_Address_Set(x,y,x,y);//设置光标位置
LCD_WR_DATA(color);
}
/******************************************************************************
函数说明:画线
入口数据:x1,y1 起始坐标
x2,y2 终止坐标
color 线的颜色
返回值: 无
******************************************************************************/
void LCD_DrawLine(u16 x1,u16 y1,u16 x2,u16 y2,u16 color)
{
u16 t;
int xerr=0,yerr=0,delta_x,delta_y,distance;
int incx,incy,uRow,uCol;
delta_x=x2-x1; //计算坐标增量
delta_y=y2-y1;
uRow=x1;//画线起点坐标
uCol=y1;
if(delta_x>0)incx=1; //设置单步方向
else if (delta_x==0)incx=0;//垂直线
else {incx=-1;delta_x=-delta_x;}
if(delta_y>0)incy=1;
else if (delta_y==0)incy=0;//水平线
else {incy=-1;delta_y=-delta_y;}
if(delta_x>delta_y)distance=delta_x; //选取基本增量坐标轴
else distance=delta_y;
for(t=0;t<distance+1;t++)
{
LCD_DrawPoint(uRow,uCol,color);//画点
xerr+=delta_x;
yerr+=delta_y;
if(xerr>distance)
{
xerr-=distance;
uRow+=incx;
}
if(yerr>distance)
{
yerr-=distance;
uCol+=incy;
}
}
}
/******************************************************************************
函数说明:画矩形
入口数据:x1,y1 起始坐标
x2,y2 终止坐标
color 矩形的颜色
返回值: 无
******************************************************************************/
void LCD_DrawRectangle(u16 x1, u16 y1, u16 x2, u16 y2,u16 color)
{
LCD_DrawLine(x1,y1,x2,y1,color);
LCD_DrawLine(x1,y1,x1,y2,color);
LCD_DrawLine(x1,y2,x2,y2,color);
LCD_DrawLine(x2,y1,x2,y2,color);
}
/******************************************************************************
函数说明:画圆
入口数据:x0,y0 圆心坐标
r 半径
color 圆的颜色
返回值: 无
******************************************************************************/
void Draw_Circle(u16 x0,u16 y0,u8 r,u16 color)
{
int a,b;
a=0;b=r;
while(a<=b)
{
LCD_DrawPoint(x0-b,y0-a,color); //3
LCD_DrawPoint(x0+b,y0-a,color); //0
LCD_DrawPoint(x0-a,y0+b,color); //1
LCD_DrawPoint(x0-a,y0-b,color); //2
LCD_DrawPoint(x0+b,y0+a,color); //4
LCD_DrawPoint(x0+a,y0-b,color); //5
LCD_DrawPoint(x0+a,y0+b,color); //6
LCD_DrawPoint(x0-b,y0+a,color); //7
a++;
if((a*a+b*b)>(r*r))//判断要画的点是否过远
{
b--;
}
}
}
/******************************************************************************
函数说明:显示汉字串
入口数据:x,y显示坐标
*s 要显示的汉字串
fc 字的颜色
bc 字的背景色
sizey 字号 可选 16 24 32
mode: 0非叠加模式 1叠加模式
返回值: 无
******************************************************************************/
void LCD_ShowChinese(u16 x,u16 y,u8 *s,u16 fc,u16 bc,u8 sizey,u8 mode)
{
while(*s!=0)
{
if(sizey==12) LCD_ShowChinese12x12(x,y,s,fc,bc,sizey,mode);
else if(sizey==16) LCD_ShowChinese16x16(x,y,s,fc,bc,sizey,mode);
else if(sizey==24) LCD_ShowChinese24x24(x,y,s,fc,bc,sizey,mode);
else if(sizey==32) LCD_ShowChinese32x32(x,y,s,fc,bc,sizey,mode);
else return;
s+=2;
x+=sizey;
}
}
/******************************************************************************
函数说明:显示单个12x12汉字
入口数据:x,y显示坐标
*s 要显示的汉字
fc 字的颜色
bc 字的背景色
sizey 字号
mode: 0非叠加模式 1叠加模式
返回值: 无
******************************************************************************/
void LCD_ShowChinese12x12(u16 x,u16 y,u8 *s,u16 fc,u16 bc,u8 sizey,u8 mode)
{
u8 i,j,m=0;
u16 k;
u16 HZnum;//汉字数目
u16 TypefaceNum;//一个字符所占字节大小
u16 x0=x;
TypefaceNum=(sizey/8+((sizey%8)?1:0))*sizey;
HZnum=sizeof(tfont12)/sizeof(typFNT_GB12); //统计汉字数目
for(k=0;k<HZnum;k++)
{
if((tfont12[k].Index[0]==*(s))&&(tfont12[k].Index[1]==*(s+1)))
{
LCD_Address_Set(x,y,x+sizey-1,y+sizey-1);
for(i=0;i<TypefaceNum;i++)
{
for(j=0;j<8;j++)
{
if(!mode)//非叠加方式
{
if(tfont12[k].Msk[i]&(0x01<<j))LCD_WR_DATA(fc);
else LCD_WR_DATA(bc);
m++;
if(m%sizey==0)
{
m=0;
break;
}
}
else//叠加方式
{
if(tfont12[k].Msk[i]&(0x01<<j)) LCD_DrawPoint(x,y,fc);//画一个点
x++;
if((x-x0)==sizey)
{
x=x0;
y++;
break;
}
}
}
}
}
continue; //查找到对应点阵字库立即退出,防止多个汉字重复取模带来影响
}
}
/******************************************************************************
函数说明:显示单个16x16汉字
入口数据:x,y显示坐标
*s 要显示的汉字
fc 字的颜色
bc 字的背景色
sizey 字号
mode: 0非叠加模式 1叠加模式
返回值: 无
******************************************************************************/
void LCD_ShowChinese16x16(u16 x,u16 y,u8 *s,u16 fc,u16 bc,u8 sizey,u8 mode)
{
u8 i,j,m=0;
u16 k;
u16 HZnum;//汉字数目
u16 TypefaceNum;//一个字符所占字节大小
u16 x0=x;
TypefaceNum=(sizey/8+((sizey%8)?1:0))*sizey;
HZnum=sizeof(tfont16)/sizeof(typFNT_GB16); //统计汉字数目
for(k=0;k<HZnum;k++)
{
if ((tfont16[k].Index[0]==*(s))&&(tfont16[k].Index[1]==*(s+1)))
{
LCD_Address_Set(x,y,x+sizey-1,y+sizey-1);
for(i=0;i<TypefaceNum;i++)
{
for(j=0;j<8;j++)
{
if(!mode)//非叠加方式
{
if(tfont16[k].Msk[i]&(0x01<<j))LCD_WR_DATA(fc);
else LCD_WR_DATA(bc);
m++;
if(m%sizey==0)
{
m=0;
break;
}
}
else//叠加方式
{
if(tfont16[k].Msk[i]&(0x01<<j)) LCD_DrawPoint(x,y,fc);//画一个点
x++;
if((x-x0)==sizey)
{
x=x0;
y++;
break;
}
}
}
}
}
continue; //查找到对应点阵字库立即退出,防止多个汉字重复取模带来影响
}
}
/******************************************************************************
函数说明:显示单个24x24汉字
入口数据:x,y显示坐标
*s 要显示的汉字
fc 字的颜色
bc 字的背景色
sizey 字号
mode: 0非叠加模式 1叠加模式
返回值: 无
******************************************************************************/
void LCD_ShowChinese24x24(u16 x,u16 y,u8 *s,u16 fc,u16 bc,u8 sizey,u8 mode)
{
u8 i,j,m=0;
u16 k;
u16 HZnum;//汉字数目
u16 TypefaceNum;//一个字符所占字节大小
u16 x0=x;
TypefaceNum=(sizey/8+((sizey%8)?1:0))*sizey;
HZnum=sizeof(tfont24)/sizeof(typFNT_GB24); //统计汉字数目
for(k=0;k<HZnum;k++)
{
if ((tfont24[k].Index[0]==*(s))&&(tfont24[k].Index[1]==*(s+1)))
{
LCD_Address_Set(x,y,x+sizey-1,y+sizey-1);
for(i=0;i<TypefaceNum;i++)
{
for(j=0;j<8;j++)
{
if(!mode)//非叠加方式
{
if(tfont24[k].Msk[i]&(0x01<<j))LCD_WR_DATA(fc);
else LCD_WR_DATA(bc);
m++;
if(m%sizey==0)
{
m=0;
break;
}
}
else//叠加方式
{
if(tfont24[k].Msk[i]&(0x01<<j)) LCD_DrawPoint(x,y,fc);//画一个点
x++;
if((x-x0)==sizey)
{
x=x0;
y++;
break;
}
}
}
}
}
continue; //查找到对应点阵字库立即退出,防止多个汉字重复取模带来影响
}
}
/******************************************************************************
函数说明:显示单个32x32汉字
入口数据:x,y显示坐标
*s 要显示的汉字
fc 字的颜色
bc 字的背景色
sizey 字号
mode: 0非叠加模式 1叠加模式
返回值: 无
******************************************************************************/
void LCD_ShowChinese32x32(u16 x,u16 y,u8 *s,u16 fc,u16 bc,u8 sizey,u8 mode)
{
u8 i,j,m=0;
u16 k;
u16 HZnum;//汉字数目
u16 TypefaceNum;//一个字符所占字节大小
u16 x0=x;
TypefaceNum=(sizey/8+((sizey%8)?1:0))*sizey;
HZnum=sizeof(tfont32)/sizeof(typFNT_GB32); //统计汉字数目
for(k=0;k<HZnum;k++)
{
if ((tfont32[k].Index[0]==*(s))&&(tfont32[k].Index[1]==*(s+1)))
{
LCD_Address_Set(x,y,x+sizey-1,y+sizey-1);
for(i=0;i<TypefaceNum;i++)
{
for(j=0;j<8;j++)
{
if(!mode)//非叠加方式
{
if(tfont32[k].Msk[i]&(0x01<<j))LCD_WR_DATA(fc);
else LCD_WR_DATA(bc);
m++;
if(m%sizey==0)
{
m=0;
break;
}
}
else//叠加方式
{
if(tfont32[k].Msk[i]&(0x01<<j)) LCD_DrawPoint(x,y,fc);//画一个点
x++;
if((x-x0)==sizey)
{
x=x0;
y++;
break;
}
}
}
}
}
continue; //查找到对应点阵字库立即退出,防止多个汉字重复取模带来影响
}
}
/******************************************************************************
函数说明:显示单个字符
入口数据:x,y显示坐标
num 要显示的字符
fc 字的颜色
bc 字的背景色
sizey 字号
mode: 0非叠加模式 1叠加模式
返回值: 无
******************************************************************************/
void LCD_ShowChar(u16 x,u16 y,u8 num,u16 fc,u16 bc,u8 sizey,u8 mode)
{
u8 temp,sizex,t,m=0;
u16 i,TypefaceNum;//一个字符所占字节大小
u16 x0=x;
sizex=sizey/2;
TypefaceNum=(sizex/8+((sizex%8)?1:0))*sizey;
num=num-' '; //得到偏移后的值
LCD_Address_Set(x,y,x+sizex-1,y+sizey-1); //设置光标位置
for(i=0;i<TypefaceNum;i++)
{
if(sizey==12)temp=ascii_1206[num][i]; //调用6x12字体
else if(sizey==16)temp=ascii_1608[num][i]; //调用8x16字体
else if(sizey==24)temp=ascii_2412[num][i]; //调用12x24字体
else if(sizey==32)temp=ascii_3216[num][i]; //调用16x32字体
else return;
for(t=0;t<8;t++)
{
if(!mode)//非叠加模式
{
if(temp&(0x01<<t))LCD_WR_DATA(fc);
else LCD_WR_DATA(bc);
m++;
if(m%sizex==0)
{
m=0;
break;
}
}
else//叠加模式
{
if(temp&(0x01<<t))LCD_DrawPoint(x,y,fc);//画一个点
x++;
if((x-x0)==sizex)
{
x=x0;
y++;
break;
}
}
}
}
}
/******************************************************************************
函数说明:显示字符串
入口数据:x,y显示坐标
*p 要显示的字符串
fc 字的颜色
bc 字的背景色
sizey 字号
mode: 0非叠加模式 1叠加模式
返回值: 无
******************************************************************************/
void LCD_ShowString(u16 x,u16 y,const u8 *p,u16 fc,u16 bc,u8 sizey,u8 mode)
{
while(*p!='\0')
{
LCD_ShowChar(x,y,*p,fc,bc,sizey,mode);
x+=sizey/2;
p++;
}
}
/******************************************************************************
函数说明:显示数字
入口数据:m底数,n指数
返回值: 无
******************************************************************************/
u32 mypow(u8 m,u8 n)
{
u32 result=1;
while(n--)result*=m;
return result;
}
/******************************************************************************
函数说明:显示整数变量
入口数据:x,y显示坐标
num 要显示整数变量
len 要显示的位数
fc 字的颜色
bc 字的背景色
sizey 字号
返回值: 无
******************************************************************************/
void LCD_ShowIntNum(u16 x,u16 y,u16 num,u8 len,u16 fc,u16 bc,u8 sizey)
{
u8 t,temp;
u8 enshow=0;
u8 sizex=sizey/2;
for(t=0;t<len;t++)
{
temp=(num/mypow(10,len-t-1))%10;
if(enshow==0&&t<(len-1))
{
if(temp==0)
{
LCD_ShowChar(x+t*sizex,y,' ',fc,bc,sizey,0);
continue;
}else enshow=1;
}
LCD_ShowChar(x+t*sizex,y,temp+48,fc,bc,sizey,0);
}
}
/******************************************************************************
函数说明:显示两位小数变量
入口数据:x,y显示坐标
num 要显示小数变量
len 要显示的位数
fc 字的颜色
bc 字的背景色
sizey 字号
返回值: 无
******************************************************************************/
void LCD_ShowFloatNum1(u16 x,u16 y,float num,u8 len,u16 fc,u16 bc,u8 sizey)
{
u8 t,temp,sizex;
u16 num1;
sizex=sizey/2;
num1=num*100;
for(t=0;t<len;t++)
{
temp=(num1/mypow(10,len-t-1))%10;
if(t==(len-2))
{
LCD_ShowChar(x+(len-2)*sizex,y,'.',fc,bc,sizey,0);
t++;
len+=1;
}
LCD_ShowChar(x+t*sizex,y,temp+48,fc,bc,sizey,0);
}
}
void LCD_ShowHexNum(u16 x, u16 y, u32 num, u8 len, u16 fc, u16 bc, u8 sizey)
{
u8 i;
u8 temp;
u8 sizex;
sizex = sizey / 2;
for(i = 0; i < len; i++)
{
temp = (num >> ((len - i - 1) * 4)) & 0x0F;
if(temp < 10)
{
LCD_ShowChar(x + i * sizex, y, temp + '0', fc, bc, sizey, 0);
}
else
{
LCD_ShowChar(x + i * sizex, y, temp - 10 + 'A', fc, bc, sizey, 0);
}
}
}
/******************************************************************************
函数说明:显示图片
入口数据:x,y起点坐标
length 图片长度
width 图片宽度
pic[] 图片数组
返回值: 无
******************************************************************************/
void LCD_ShowPicture(u16 x,u16 y,u16 length,u16 width,const u8 pic[])
{
u16 i,j;
u32 k=0;
LCD_Address_Set(x,y,x+length-1,y+width-1);
for(i=0;i<length;i++)
{
for(j=0;j<width;j++)
{
LCD_WR_DATA8(pic[k*2]);
LCD_WR_DATA8(pic[k*2+1]);
k++;
}
}
}
void LCD_ShowPictureFromW25Q_Fast(uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint32_t FlashAddress)
{
uint32_t TotalBytes;
uint32_t ReadBytes = 0;
uint16_t i;
uint8_t Buffer[PIC_BUF_SIZE];
TotalBytes = width * height * 2;
LCD_Address_Set(x,y,x+width-1,y+height-1);
while(ReadBytes < TotalBytes)
{
uint32_t CurrentRead;
CurrentRead = TotalBytes - ReadBytes;
if(CurrentRead > PIC_BUF_SIZE)
{
CurrentRead = PIC_BUF_SIZE;
}
W25QXX_ReadData(FlashAddress + ReadBytes,
Buffer,
CurrentRead);
for(i = 0; i < CurrentRead; i++)
{
LCD_WR_DATA8(Buffer[i]);
}
ReadBytes += CurrentRead;
}
}
main.c
#include "stm32f10x.h" // Device header
#include "w25qxx.h"
#include "lcd_init.h"
#include "lcd.h"
#include "pic.h"
#include "delay.h"
uint8_t MID;
uint16_t DID;
int main(void)
{
LCD_Init();//LCD初始化
LCD_Fill(0,0,LCD_W,LCD_H,WHITE);
W25QXX_Init();
LCD_ShowString(72,20,"W25QXX",RED,WHITE,32,0);
LCD_ShowString(25,54,"MID:",RED,WHITE,24,0);
LCD_ShowString(120,54,"DID:",RED,WHITE,24,0);
W25QXX_ReadID(&MID, &DID);
LCD_ShowHexNum(80,54,MID,2,RED,WHITE,24);
LCD_ShowHexNum(175,54,DID,4,RED,WHITE,24);
LCD_ShowChinese(25,80,"优信电子",RED,WHITE,24,0);
/*写完注释掉或者删除掉这段代码,文件大可分多次写入*/
// W25QXX_WritePicture(0x000000, gImage_1, sizeof(gImage_1)); //写入图片
// W25QXX_WritePicture(0x010000, gImage_2, sizeof(gImage_2));
while(1)
{
LCD_Fill(20,120,260,260,WHITE); //前图片大,后图片小,无法覆盖前显示的大图片,每次显示前先清屏对应区域
LCD_ShowPictureFromW25Q_Fast(20,120,200,143,0x000000);
Delay_ms(1000);
LCD_Fill(20,120,260,260,WHITE);
LCD_ShowPictureFromW25Q_Fast(20,120,200,135,0x010000);
Delay_ms(1000);
}
}
效果展示

更多推荐
所有评论(0)