基于 32G12K128 实验箱V9.62_实验之TFT彩屏显示全屏图片
基于 32G12K128 实验箱V9.62_实验之TFT彩屏显示全屏图片本实验目的很简单,显示三幅全屏的图片。但是对于新手(高手请绕行)来说,实现过程并不简单。要面临一系列问题需要解决:首先,一幅320*240的彩色图片,占153600字节,存放在哪里?显然,仅有64K的MCU片内是无法存放的,只有存放在片外的Flash存储器中。实验箱配有W25Q40芯片,具有512K空间,可以存放三幅全屏图片的数据(三图先用STCAI-ISP工具,转为Bin文件)。既然启用了外部存储器,那么必然要先实现对存储器的读写功能吧?这就对W25Q40要有一个基本认识:它具有512K容量,分16个块,每块64K字节;共有128个扇区,每扇区4096字节,擦除是按扇区为单位进行的;一次读写最大字节数是按页进行的,每页256字节。对存储器的读写,总得有个操作界面、同时要考虑上位机如何将图片数据传输到Flash存储器中去、并且能观察读写是否正确吧?于是要有与PC机通讯的功能,这就要启用实验箱的UART2(RS232)功能了。当用上位机传输数据时总要有个指定地址和数据长度的对话操作吧?那么就涉及到实验箱键盘的对话使用了。当上位机传入数据后,要验证一下写入数据是否正确,总要有个读出来被查看的功能吧?假如数据不正确、或地址不对,总要有个擦除重新传输的功能吧?最后实现将图片数据传输到屏中依次显示出来吧?总之,其实显示三幅全屏的图片,实现并不简单,涉及到了多个方面。
本实验先设计一个简单的主菜单界面。启用实验箱矩阵键盘中的下面四个键,K4…K7。 K4键:上加K5键:下减K6键:右移K7键:OK输入地址时,16进制数据, 输入长度时,10进制数据。菜单第一项: PC<->W25Q40 进入与PC机通讯界面(类似官方DEMO实验),按K7键返回主菜单. 上位机使用STCAI-ISP软件串口功能, 连接实验箱UART2(RS232)接口通讯… 菜单第二项: FlashW25Qx 进入与PC机发送数据文件的界面……每次传输一幅图,需要输入存储地址。注意:第一幅图地址输入0x00000,第二幅图0x25800,第三幅图0x4B000,等待上位机数据发送。按K7键返回主菜单,或上位机发送完毕自动返回主菜单。此时,利用STCAI-ISP软件,串口发送文件功能。注意:选择数据包每次间隔50ms,让实验箱有时间擦除写入。 菜单第三项: Read W25Qx 可以指定地址查阅已存储在W25Q40中的数据……可用K4、K5键前后地址翻阅,按K7键返回主菜单. 菜单第四项: Del_Setctor可以指定地址删除一个扇区数据(4096字节)为了防止误删,有确认提示,默认是No! 不删除。K6移位键选择,K7确认返回。 菜单第五项: Del_Block可以指定地址删除一个块数据(64K字节),可加快大量数据扇区删除。为了防止误删,也有确认提示,默认是No! 不删除。K6移位键选择,K7确认返回。菜单第六项: Disp PICx 执行全屏彩色图片显示,按任意键继续,三幅图显示完毕,返回主菜单界面。
完整工程文件包, 供有兴趣坛友参考或指正:
贴一下主文件
//=================================================================================================
// 文件名称: Main.C
// 功能说明: 测试液晶屏模块驱动函数 2.4寸彩色显示屏 240*320 8位并行口模式+ 测试通信以及W25Q40读写
// 基于电路: STC32G12K128 实验箱V9.62
// 整理改编: 浦晓明(浦江一水) 2024-09-14
// 实验内容: 实验全屏彩色图片显示
//**************************************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "STC32G.H"
#include "STC32G_SYS.H"
#include "STC32G_UART2.H"
#include "LCD9341.H"
#include "KEY8.H"
#include "W25Q40.H"//存储512K
//全局变量说明
unsigned char k; //键值
unsigned char m,n; //菜单项
char S; //显示缓存
unsigned long ADDR = 0;//0x25800/0x4B000
unsigned long Length=153600;
u16 W25Q40_ID; //设备ID
u32 T_Addr; //目标地址
u8R_Buf; //读取数据缓存
extern u8 xdata F_Buf; //扇区缓存
//菜单项目定义...
char * MN={ "0.PC<->W25Qx", "1.FlashW25Qx","2.Read W25Qx","3.Del_Sector","4.Del_Block ","5.Disp PICx ", };
//函数说明(凡是排列在main()之后的函数须先说明一下)
void PCRW_W25Q(void);//PC机通信实验W25Q读写
void FlashW25Q(void); //PC机通信刷新扇区数据
void Read_W25Q(void); //读出扇区数据-页256字节
void DelSector(void); //删除指定扇区4096字节
void DelBlock(void); //删除块数据(64K)
void Disp_PIC(void); //显示图片
//=============================================================================
// 输入整型数 (10进制数显示)
//=============================================================================
unsigned long InputI(unsigned int X,unsigned int Y,char * I)
{ unsigned char set=1;
unsigned int i=0,Val=0,l;
l=strlen(I)-1;
while(set)
{ LCD_Str12(X,Y,I,7,0);
LCD_A12(X+i*8,Y,I,14,4);
k=0;k=GETCH();
switch(k)
{ case K40 : I=I<0x39? I+1:0x30; break;
case K50 : I=I>0x30? I-1:0x39; break;
case K60 : i=i<l?i+1:0; break;
case K61 : i=i>0?i-1:l; break;
case K70 : LCD_Str12(X,Y,I,15,0); set=0; break;
} if(i>l)i=0;
}
return(atol(I));
}
unsigned long Adr=0;
//=============================================================================
// 输入存储器入口地址 (16进制数显示)
//=============================================================================
unsigned long InputAdr(unsigned int X,unsigned int Y,char * F)
{ unsigned char set=1;
unsigned int i=0,val,l;
l=strlen(F)-1;
while(set)
{ LCD_Str12(X,Y,F,7,0);
LCD_A12(X+i*8,Y,F,14,4);
k=0;k=GETCH();
switch(k)
{ case K40 : F=F<0x46? F+1:0x30; if(F==0x3A)F=0x41; break;
case K50 : F=F>0x30? F-1:0x46; if(F==0x40)F=0x39; break;
case K60 : i=i<l?i+1:0; break;//短按
case K61 : i=i>0?i-1:l; break;//长按
case K70 : LCD_Str12(X,Y,F,15,0); set=0; break;
} if(i>l)i=0;
} Adr=0;
for(i=0;i<=l;i++)
{ val=F<0x3A?F-48:F-55;
Adr=16*Adr+val;
}
return(Adr);
}
//=============================================================================
// 主函数
//=============================================================================
void main(void)
{ u16 i;
u8 disp=1;
SYS_Init(); //系统初始化(35MHz)
KEY8_Init(); //键盘初始化
LCD_Init(); //液晶屏初始化
//LCD_SetDIR(0);//原显示方向(纵向屏)
LCD_SetDIR(3);//调整显示方向(横屏)
LCD_CLS(0); //清屏
W25Q40_Init();
W25Q40_ID=W25Q40_ReadID(); //读取ID(验证设备是否存在)
UART2_Init(1,1,115200); //IO切换1,模式1,波特率115200
UART2_SendStr("STC32G12K128_UART2_OK!\r\n");
printf("Printf()_UART2_OK!\r\n");
m=0;
while(1)
{
if(disp)
{ disp=0;
LCD_CLS(0); //清全屏
LCD_CLSA(0,0,319,22,1);//局部清屏
LCD_Str12(8,2,(char *)"Test TFT9341 W25Q40...",15,1);
LCD_CLSA(0,220,319,20,7);
for(i=0;i<6;i++) //显示主菜单项
{ if(m==i)LCD_Str12(10,30+20*i,(char *)MN,14,4);
else LCD_Str12(10,30+20*i,(char *)MN,15,0);
} LCD_Str12(10,222,"KEY=00",0,7);//显示按键值
}
k=0;k=GETCH(); //RDKEY();
if(k!=0)
{ sprintf(S,"KEY=%02X",k);
LCD_Str12(10,222,S,0,7);
switch(k)
{ case K40: LCD_Str12(10,30+20*m,(char *)MN,15,0);
m=m>0?m-1:5;
LCD_Str12(10,30+20*m,(char *)MN,14,4); break;
case K50: LCD_Str12(10,30+20*m,(char *)MN,15,0);
m=m<5?m+1:0;
LCD_Str12(10,30+20*m,(char *)MN,14,4); break;
case K60: break;
case K70: switch(m)
{ case 0: PCRW_W25Q(); disp=1; break;//实验与PC机通讯读写W25Q
case 1: FlashW25Q(); disp=1; break;//与PC机通讯刷新扇区数据(图片文件)
case 2: Read_W25Q(); disp=1; break;//读取W25Q扇区数据显示
case 3: DelSector(); disp=1; break;//删除W25Q指定扇区数据4096字节
case 4: DelBlock();disp=1; break;//删除W25Q指定块区数据64K字节
case 5: Disp_PIC();disp=1; break;//实验显示图片
}
break;
}
}
delay_ms(20); //延时
}
}
//PC机读写W25Q40(512K存储器)
void PCRW_W25Q(void)
{ unsigned char run=1,ts=1,ch,n;
u16 i,len;
LCD_CLSA(0,25,160,150,0);
LCD_Str12(10, 30,(char *)"PC<==>UART2+W25Q40 Read & Write...",10,0);
LCD_Str12(10,222,(char *)"Press K7 return...",0,7);
while(run)
{ k=0;k=RDKEY();
if(k==K70)run=0;
if(ts)
{ //实验操作提示...
printf("\r\n** STC32G单片机对外部Flash_W25Q40读写实验(主频35MHz) **\r\n");
printf("- 实验箱_USART2(RS232)串口连接PC机串口.波特率115200,8,1,N\r\n");
printf("- PC机使用串口助手,串口命令例\xfd举如下:(无需回车换行)\r\n");
printf("E 0x000020 --> 删除\xfd字节地址0x000020所在扇区.\r\n");
printf("W 0x000020 ABCDE12345 --> 写入地址0x000020字符串ABCDE12345\r\n");
printf("R 0x000020 10 --> 读出地址0x000020指定长度字节.\r\n");
printf("Q 0x000000 --> 返回主菜单...\r\n");
printf("读写地址范围: 0x000000 -- 0x07FFFF (512K)\r\n\r\n");
ts=0;
}
delay_ms(10);
if(RX2_Cnt>=10) //串口2收到指令信息...
{ for(i=0;i<8;i++)
{ if((RX2_Buf >= 'a') && (RX2_Buf <= 'z'))RX2_Buf -= 0x20; } //小写转大写
if(((RX2_Buf=='E')||(RX2_Buf=='W')||(RX2_Buf=='R')||(RX2_Buf=='Q'))&&(RX2_Buf==' '))
{ //解析地址...
ch=RX2_Buf-0x30; if(ch>9)ch=ch-0x07; T_Addr=(u32)ch;
ch=RX2_Buf-0x30; if(ch>9)ch=ch-0x07; T_Addr=16*T_Addr+(u32)ch;
ch=RX2_Buf-0x30; if(ch>9)ch=ch-0x07; T_Addr=16*T_Addr+(u32)ch;
ch=RX2_Buf-0x30; if(ch>9)ch=ch-0x07; T_Addr=16*T_Addr+(u32)ch;
ch=RX2_Buf-0x30; if(ch>9)ch=ch-0x07; T_Addr=16*T_Addr+(u32)ch;
ch=RX2_Buf-0x30; if(ch>9)ch=ch-0x07; T_Addr=16*T_Addr+(u32)ch;
//解析指令
switch(RX2_Buf)
{ case 'E': W25Q40_SectorErase(T_Addr);
printf("OK! Sector Erase.\r\n");
break;
case 'W': n=RX2_Cnt-11; //取指令数据长度
W25Q40_Write(T_Addr,RX2_Buf+11,n);
printf("OK! Write %2d Byte.\r\n",n);
break;
case 'R': n=RX2_Cnt-11; len=0;//取指令数据长度
for(i=0;i<n;i++) //取需要读取的数据长度
{ ch=RX2_Buf;
if((ch>='0')&&(ch<='9'))len=10*len+RX2_Buf-'0';
else break;
}
if(len>256)len=256;
W25Q40_Read(T_Addr,R_Buf,len);
printf("OK! Read out %3d Bytes:\r\n",len);
for(i=0;i<len;i++)UART2_SendChar(R_Buf);
UART2_SendChar(0x0D); UART2_SendChar(0x0A);
break;
case 'Q': n=RX2_Cnt-11; len=0; run=0; break;
}
} else { printf("指令错误!!\r\n");ts=1; }
RX2_Cnt=0;RX2_Buf=0; //计数清零
}
}
}
// 刷新存储器...
void FlashW25Q(void)
{ unsigned int Dh,Dl,Cnt=0;
unsigned char wait=1;
LCD_CLSA(0,25,160,150,0);
LCD_Str12(10,30,(char *)"Address=0x00000 (<=0x7FF00)",10,0);
Dh=ADDR / 65536; Dl=ADDR % 65536;
sprintf(S,"%1X%04X",Dh,Dl); //C251无法实现长整型数格式化字符串
ADDR=InputAdr(90,30,S); //输入要写入存储器的首地址
LCD_Str12(10,55,(char *)"Length=153600",10,0);
Length=153600; //一幅320*240图片数据长度
S=Length/100000+'0';
S=(Length%100000)/10000+'0';
S=(Length%10000)/1000+'0';
S=(Length%1000)/100+'0';
S=(Length%100)/10+'0';
S=Length%10+'0';
S=0;
//sprintf(S,"%02d%06d",Dh,Dl);
Length=InputI(66,55,S); //输入要写入存储器的首地址
LCD_Str12(10,80,(char *)"FlashSector Start...",11,0);
sprintf(S,"FlashSector=%03d",Cnt);
LCD_Str12(10,105,S,11,0);
while(wait)
{ k=0;k=RDKEY();
if(k==K70)wait=0;
if(RX2_TimeOut>0) //如果已经有接收到字节
{ if(--RX2_TimeOut == 0) //超时计数减1等于0了:接收到一包数据了
{ if(RX2_Cnt>0)W25Q40_Write(ADDR,RX2_Buf,RX2_Cnt);//将数据写入制定地址的扇区中
Cnt++;sprintf(S,"%03d",Cnt); //接收计数++
LCD_Str12(106,105,S,15,0); //显示已经刷入的扇区(256字节)数
ADDR=ADDR+RX2_Cnt; //目标地址递增
RX2_Cnt=0; //计数清零
}
}
if(Cnt>=(Length/256))wait=0; //如果接收包数到达预定数,结束返回
}
LCD_Str12(10,130,"Flash End.",15,0);
GETCH(); //按任意键继续...
delay_ms(500);//延时
}
//读一页数据
void Read_W25Q(void)
{ unsigned int i,Dh,Dl;
unsigned char loop=1;
LCD_CLSA(0,25,160,150,0);
LCD_Str12(10,30,(char *)"Address=0x00000(<=0x7FF00)",10,0);
Dh=ADDR / 65536; Dl=ADDR % 65536;
sprintf(S,"%1X%04X",Dh,Dl);
ADDR=InputAdr(90,30,S); //输入要读出存储器的首地址
ADDR &= 0x7FF00; //转页(256字节)地址
LCD_CLS(0);
while(loop)
{ Dh=ADDR / 65536; Dl=ADDR % 65536;
sprintf(S,"ADDR=0x%01X%04X",Dh,Dl);
LCD_Str12(0,0,S,10,0);
W25Q40_Read(ADDR,RX2_Buf,256);
for(i=0;i<256;i++)
{ sprintf(S,"%02X ",(unsigned int)RX2_Buf);
LCD_Str12((i%16)*20,16+(i/16)*14,S,15,0);
}
k=0;k=GETCH();
switch(k) //长按或短按都可以...
{ case K40: case K41: ADDR=ADDR>0xFF?ADDR-256:0x7FF00; break;
case K50: case K51: ADDR=ADDR<0x7FF00?ADDR+256:0; break;
case K70: case K71: loop=0; break;
}
delay_ms(10); //延时
}
}
//删除扇区4096字节数据
void DelSector(void)
{ unsigned int Dh,Dl;
unsigned char set=1,OK=0;
LCD_CLSA(0,25,160,150,0);
LCD_Str12(10,30,(char *)"Address=0x00000(<=0x7F000)",10,0);
Dh=ADDR / 65536; Dl=ADDR % 65536;
sprintf(S,"%1X%04X",Dh,Dl);
ADDR=InputAdr(90,30,S); //输入要读出存储器的首地址
ADDR &= 0x7F000; //转扇区(4096字节)地址
LCD_Str12(10,60,(char *)"Del Sector ??!",12,0);
while(set)
{ if(OK){ LCD_Str12(10,90,(char *)"NO!",15,0);LCD_Str12(50,90,(char *)"YES",14,4); }
else{ LCD_Str12(10,90,(char *)"NO!",14,4);LCD_Str12(50,90,(char *)"YES",15,0); }
k=0;k=GETCH();
switch(k)
{ case K60: OK=OK<1?1:0; break;
case K70: set=0; break;
}
}
if(OK)
{ W25Q40_SectorErase(ADDR);
LCD_Str12(10,120,(char *)"Del Sector OK!",14,0);
delay_ms(1000); //延时
}
}
//删除块区64K字节数据
void DelBlock(void)
{ unsigned int Dh,Dl;
unsigned char set=1,OK=0;
LCD_CLSA(0,25,160,150,0);
LCD_Str12(10,30,(char *)"Address=0x00000(<=0x70000)",10,0);
Dh=ADDR / 65536; Dl=ADDR % 65536;
sprintf(S,"%1X%04X",Dh,Dl);
ADDR=InputAdr(90,30,S); //输入要读出存储器的首地址
ADDR &= 0x70000; //转扇区(256字节)地址
LCD_Str12(10,60,(char *)"Del Block ??!",12,0);
while(set)
{ if(OK){ LCD_Str12(10,90,(char *)"NO!",15,0);LCD_Str12(50,90,(char *)"YES",14,4); }
else{ LCD_Str12(10,90,(char *)"NO!",14,4);LCD_Str12(50,90,(char *)"YES",15,0); }
k=0;k=GETCH();
switch(k)
{ case K60: OK=OK<1?1:0; break;
case K70: set=0; break;
}
}
if(OK)
{ W25Q40_BlockErase(ADDR);
LCD_Str12(10,120,(char *)"Del Sector OK!",14,0);
delay_ms(1000); //延时
}
}
//显示全屏彩色图片
void DispBMP(unsigned long Addr)
{ unsigned int i,j,Col;
unsigned char h,l;
LCD_SetWindow(0,0,319,239);//设置显示窗口
for(i=0;i<600;i++)
{ W25Q40_Read(Addr,RX2_Buf,256);
for(j=0;j<128;j++)
{ h=RX2_Buf;l=RX2_Buf;
Col=256*h+l; //图像数据:高位在前.
LCD_WR_DAT_16Bit(Col);
}
Addr=Addr+256;
}
}
//显示BMP图片...
void Disp_PIC(void)
{
DispBMP(0x00000); //指定图片0数据的首地址
GETCH();
DispBMP(0x25800); //指定图片1数据的首地址
GETCH();
DispBMP(0x4B000); //指定图片2数据的首地址
GETCH();
delay_ms(500); //延时(防止菜单项连续被触发)
}
//=== Main.C END =================================================================================
{:4_174:} 感谢 autopccopy 版主的鲜花鼓励. 感谢 芯征程 版主的鲜花鼓励.
页:
[1]