STM32F4_中英文显示

news2024/11/16 19:45:32

目录

1. 液晶显示逻辑

2. 汉字显示原理

3. 实验程序

3.1 main.c

3.2 text.c

3.3 text.h

3.4 fontupd.c

3.5 fontupd.h


1. 液晶显示逻辑

字符编码:

        由于计算机只能识别 0 和 1,文字也只能以 0 和 1 的形式在计算机里存储,所以我们需要对文字进行编码才能让计算机进行处理,编码的过程就是规定特定的 01数字串 来表示特定的文字,最简单的字符编码例子就是ASCII码。例如在C语言的编译环境下,我们输入"abcd",实际上存储到内存中的是0x61 0x62 0x63 0x64(也就是字符对应的ASCII码)

ASCII编码:

        在程序设计中使用ASCII编码表约定了一些控制字符、英文及数字。它们在存储器中,本质也是二进制数,只是我们约定这些二进制数可以表示某些特殊意义,如以ASCII编码解释数字 “0x41” 时,它表示英文字符 “A” 

        ASCII码表分为两部分,第一部分是控制字符和通讯专用字符,它们的数字编码从0~31,它们并没有特定的图形显示,但会根据不同的应用程序,对文本显示有不同的影响。ASCII码的第二部分包括空格、阿拉伯数字、标点符号、大小写英文字母以及 “DEL(删除控制)”,这部分符号的数字编码从32~127,除最后一个DEL符号外,都能以图形的方式来表示,它们属于传统文字书写系统的一部分。

        后来,随着计算机被引入到其他国家,由于它们使用的不是英语,他们使用的字母在ASCII码表中没有定义,所以他们采用了127号之后的位来表示这些新的字母,在此基础之上加入了各种形状,一直编码到255。从128到255这些字符被称为ASCII扩展字符集

中文编码:

        英文书写系统是由26个基本字母组成,利用26个字母可以组合出不同的单次,所以用ASCII码表就能表示整个英文书写系统。

        中文书写系统中的汉字是独立的方块,由于汉字非常多,常用字就有6000多个,如果像ASCII编码表那样只使用1个字节最多只能表示256个汉字,所以我们使用2个字节来编码。打个比方说:B0A3 =哎 ,通过两个字节的1011 0000 1010 0011来表示一个中文汉字哎。

GB2312标准:

        我国首先定义的是GB2312标准。它把ASCII码表127号之后的扩展字符集直接取消掉,规定小于127的编码按原来ASCII标准解释字符。当两个大于127的字符连在一起时,就表示一个汉字,第一个字节使用(0xA1~0xFE)编码,第二个字节使用(0xA1~0xFE)编码,这样的编码组合起来可以表示7000多个符号,其中包括6763个汉字。

        如上表,当我们设定系统使用GB2312标准的时候,他遇到一个字符串时,会按字节检测字符值的大小,若遇到连续两个字节的数值都大于127时就把这两个连续的字节合在一起,用GB2312解码,若遇到的数值小于127,就直接用ASCII把它解码。

区位码:

        GB2312编码对所收录字符进行了 “分区” 处理,共94个区,每个区含94个位,共8836个码位。这种表示方式也称为区位码

  • 01-09区收录除汉字外的682个字符。
  • 10-15区为空白区,没有使用。
  • 16-55区收录3755个一级汉字,按拼音排序。
  • 56-87区收录3008个二级汉字,按部首/笔画排序。
  • 88-94区为空白区,没有使用。

比如说: “啊” 字是GB2312编码中的第一个汉字,它位于 16 区的 01 位,所以它的区位码就是1601.

GBK编码:

        虽然GB2312编码中表示的6763个汉字已经覆盖中国大陆99.75%的使用率,但是有些生僻字在人名、文言文中出现的频率还是非常高的。

        为此在GB2312标准的基础上又增加了14240个新汉字和符号,这个方案被称为GBK标准。如果按照GB2312原来的标准进行编码,2个字节已经存储不下了,因此GBK标准中只要第一个字节大于127就表示这是一个汉字的开始,这样同时也兼容ASCII和GB2312标准。

Gig5编码:

        在台湾、香港等地区,使用较多的是Gig5编码,它的主要特点是收录了繁体字。Big5编码和GBK编码是不兼容的。

Unicode编码:

        由于各个国家或地区都根据使用自己的文字系统制定标准,同一个编码在不同的标准里表示不一样的字符,各个标准互不兼容,无法用一个标准表示所有的字符。国际标准化组织ISO舍弃了地区性的方案,重新给全球上所有文化使用的字母和符号进行编号,该编号集被称为Universal Multiple-OcteCoded Character Set,简称UCS,也被称为Unicode

2. 汉字显示原理

        汉字在液晶上的显示其实就是一些点的显示与不显示,这就相当于我们的笔一样,有笔经过的地方画出来,没有经过的地方就不画出来。所以我们要显示汉字,首先要知道汉字的点阵数据,这些数据可以由专门的软件来生成。只要知道了汉字点阵的生成方法,那么我们在程序里面就可以把这个点阵数据解析成一个汉字了。(同51点阵屏的显示原理一样)

        知道显示了一个汉字,就可以推及整个汉字库了。汉字在各种文件里面的存储不是以点阵数据的形式存储的(否则那占用的空间就太大了),而是以内码的形式(1个高8位,1个低8位)存储的,就是GB2312/GBK/BIG5等这几种的一种,每个汉字对应着一个内码,再知道这个内码之后再去字库里面查找这个汉字的点阵数据,然后在液晶上显示出来。这个过程我们是看不到的,但是计算机是要去执行的。

        汉字内码(GBK/GB2312)--->查找点阵库--->解析--->显示

        接下来很大的问题就是:制作一个与汉字内码对得上号的汉字点阵库。方便单片机的查找。每个GBK码由2个字节组成,第一个字节为0x81~0xFE,第二个字节分成两部分,一是0x40~0x7E,二是0x80~0xFE。

        我们习惯上把第一个字节代表的意义称为区,那么GBK里面总共有126个区(0xFE-0x81+1),每个区内有190个汉字(0xFE-0x80+0x7E-0x40+2),总共就有126*190=23940个汉字。我们的点阵库只要按照这个编码规则从0x8140开始,逐一建立,每个区的点阵大小为每个汉字所用的字节数*190(每个区有190个汉字,190乘以每个汉字所占的字节数=该区总的字节数,也就是每个区的点阵大小)

当 GBKL<0X7F时:Hp=((GBKH-0x81)*190+GBKL-0X40)*(size*2);

当 GBKL>0X80 时:Hp=((GBKH-0x81)*190+GBKL-0X41)*(size*2);

(注:因为第二字节被分成两部分,所以分开考虑)

其中 GBKH、GBKL 分别代表 GBK 的第一个字节和第二个字节(也就是高位和低位),size 代表汉字字体的大小(比如 16 字体,12 字体等),Hp 则为对应汉字点阵数据在字库里面的起 始地址(假设是从 0 开始存放)。

(GBKH-0x81)*190表示第一个字节相对于起始位置的偏移量,*乘以190表示这个字相对于起始位的总的偏移字节大小;GBKL-0X40表示第二个字节的偏移量

字库的生成,我们要用到一款由易木雨软件工作室设计的点阵字库生成器V3.8。

注意:

        电脑端的字体大小与我们生成点阵大小的关系为:

        fsize=dsize*6/8

        其中,fsize是电脑端字体大小,dsize是点阵大小(12、16、24等)。所以16*16点阵大小对应的是12字体。

3. 实验程序

实验功能:

        开机的时候先检测W25Q128中是否已经存在字库,如果存在,则按次序显示汉字(三种字体都显示)。如果没有,则检测SD卡和文件系统,并查找SYSTEM文件夹下的FONT文件夹。在该文件夹内查找 UNIGBK.BIN、GBK12.FON、GBK16.FON 和GBK24.FON。检测到这些文件之后,开始更新字库,更新完毕后才开始显示汉字。

3.1 main.c

#include "stm32f4xx.h"                 
#include "delay.h"
#include "usart.h"
#include "LED.h"
#include "lcd.h"
#include "Key.h"
#include "usmart.h"
#include "SRAM.h"
#include "Malloc.h"
#include "SDIO_Card.h"
#include "W25Q128.h"
#include "ff.h"
#include "exfuns.h"
#include "fontupd.h"
#include "text.h"

 
//LCD状态设置函数
void led_set(u8 sta)//只要工程目录下有usmart调试函数,主函数就必须调用这两个函数
{
	LED1=sta;
}
//函数参数调用测试函数
void test_fun(void(*ledset)(u8),u8 sta)
{
	led_set(sta);
}
int main(void)
{        
	u32 fontcnt;		  
	u8 i,j;
	u8 fontx[2];//gbk码
	u8 key,t;
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组2
	delay_init(168);  //初始化延时函数
	uart_init(115200);		//初始化串口波特率为115200
	
	LED_Init();					//初始化LED  
 	LCD_Init();					//LCD初始化  
 	Key_Init();					//按键初始化  
	W25Q128_Init();				//初始化W25Q128
	usmart_dev.init(168);		//初始化USMART
	my_mem_init(SRAMIN);		//初始化内部内存池 
	my_mem_init(SRAMCCM);		//初始化CCM内存池 
	exfuns_init();				//为fatfs相关变量申请内存  
  	f_mount(fs[0],"0:",1); 		//挂载SD卡 
 	f_mount(fs[1],"1:",1); 		//挂载FLASH.
	while(font_init()) 			//检查字库
	{
UPD:    
		LCD_Clear(WHITE);		   	//清屏
 		POINT_COLOR=RED;			//设置字体为红色	   	   	  
		LCD_ShowString(30,50,200,16,16,"Explorer STM32F4");
		while(SD_Init())			//检测SD卡
		{
			LCD_ShowString(30,70,200,16,16,"SD Card Failed!");
			delay_ms(200);
			LCD_Fill(30,70,200+30,70+16,WHITE);
			delay_ms(200);		    
		}								 						    
		LCD_ShowString(30,70,200,16,16,"SD Card OK");
		LCD_ShowString(30,90,200,16,16,"Font Updating...");
		key=update_font(20,110,16,"0:");//更新字库
		while(key)//更新失败		
		{			 		  
			LCD_ShowString(30,110,200,16,16,"Font Update Failed!");
			delay_ms(200);
			LCD_Fill(20,110,200+20,110+16,WHITE);
			delay_ms(200);		       
		} 		  
		LCD_ShowString(30,110,200,16,16,"Font Update Success!   ");
		delay_ms(1500);	
		LCD_Clear(WHITE);//清屏	       
	}  
	POINT_COLOR=RED;       
	Show_Str(30,50,200,16,"探索者STM32F407开发板",16,0);	//在指定位置上开始显示一个字符			    	 
	Show_Str(30,70,200,16,"GBK字库测试程序",16,0);				    	 
	Show_Str(30,90,200,16,"正点原子@ALIENTEK",16,0);				    	 
	Show_Str(30,110,200,16,"2023年20月23日",16,0);
	Show_Str(30,130,200,16,"按KEY0,更新字库",16,0);
 	POINT_COLOR=BLUE;  
	Show_Str(30,150,200,16,"内码高字节:",16,0);				    	 
	Show_Str(30,170,200,16,"内码低字节:",16,0);				    	 
	Show_Str(30,190,200,16,"汉字计数器:",16,0);

	Show_Str(30,220,200,24,"对应汉字为:",24,0); 
	Show_Str(30,244,200,16,"对应汉字(16*16)为:",16,0);			 
	Show_Str(30,260,200,12,"对应汉字(12*12)为:",12,0);			 
	while(1)
	{
		fontcnt=0;
		for(i=0x81;i<0xff;i++) //0x81第一个字节的起始位
		{		
			fontx[0]=i;
			LCD_ShowNum(30+8*11,150,i,3,16);	//显示内码高字节,1个汉字占两个字节    
			for(j=0x40;j<0xfe;j++) //第二字节
			{
				if(j==0x7f) //第二字节被分成两部分,一是0x40~0x7E,二是0x80~0xFE。中间间隔了0x7F,碰到0X7F跳过一次即可
					continue;//continue语句的作用是跳过循环体中剩余的语句而强行执行下一次循环
				fontcnt++;
				LCD_ShowNum(118,170,j,3,16);	//显示内码低字节	 
				LCD_ShowNum(118,190,fontcnt,5,16);//汉字计数显示	 
			 	fontx[1]=j;
				Show_Font(30+132,220,fontx,24,0);	  
				Show_Font(30+144,244,fontx,16,0);	  		 		 
				Show_Font(30+108,260,fontx,12,0);	  		 		 
				t=200;
				while(t--)//延时,同时扫描按键
				{
					delay_ms(1);
					key=KEY_Scan(0);
					if(key==1)
						goto UPD;
				}
				LED0=!LED0;
			}   
		}	
	} 
}



3.2 text.c

#include "sys.h" 
#include "fontupd.h"
#include "W25Q128.h"
#include "lcd.h"
#include "text.h"	
#include "string.h"												    
#include "usart.h"												    



//code 字符指针开始
//从字库中查找出字模
//code 字符串的开始地址,GBK码
//mat  数据存放地址 (size/8+((size%8)?1:0))*(size) bytes大小	
//size:字体大小
void Get_HzMat(unsigned char *code,unsigned char *mat,u8 size)
{		    
	unsigned char qh,ql;
	unsigned char i;					  
	unsigned long foffset; 
	u8 csize=(size/8+((size%8)?1:0))*(size);//得到字体一个字符对应点阵集所占的字节数	 
	qh=*code;
	ql=*(++code);
	if(qh<0x81||ql<0x40||ql==0xff||qh==0xff)//非常用汉字,不在GBK定义的库里面
	{   		    
	    for(i=0;i<csize;i++)
			*mat++=0x00;//填充满格
	    return; //结束访问
	} 
	//第二个字节分成两部分,一是0x40~0x7E,二是0x80~0xFE。
	if(ql<0x7f)//第二字节第一部分的偏移量
		ql-=0x40;
	else       //因为第二字节的第一部分和第二部分之间间隔了一个0x7F,所以当位于第二字节的第二部分时,需要多减一个1
		ql-=0x41;
	qh-=0x81; //GBK码中第一个字节的偏移量,第一个字节为0x81~0xFE   
	foffset=((unsigned long)190*qh+ql)*csize;	//得到字库中的字节偏移量(190的意思是1个区能存放190个汉字)  		  
	switch(size)
	{
		case 12:
			W25Q128_Read(mat,foffset+ftinfo.f12addr,csize);
			break;
		case 16:
			W25Q128_Read(mat,foffset+ftinfo.f16addr,csize);
			break;
		case 24:
			W25Q128_Read(mat,foffset+ftinfo.f24addr,csize);
			break;
			
	}     												    
}  
//显示一个指定大小的汉字
//x,y :汉字的坐标
//font:汉字GBK码
//size:字体大小
//mode:0,正常显示,1,叠加显示	   
void Show_Font(u16 x,u16 y,u8 *font,u8 size,u8 mode)
{
	u8 temp,t,t1;
	u16 y0=y;
	u8 dzk[72];   
	u8 csize=(size/8+((size%8)?1:0))*(size);//得到字体一个字符对应点阵集所占的字节数	 
	if(size!=12&&size!=16&&size!=24)
		return;	//不支持的size
	Get_HzMat(font,dzk,size);	//得到相应大小的点阵数据 
	for(t=0;t<csize;t++)
	{   												   
		temp=dzk[t];			//得到点阵数据                          
		for(t1=0;t1<8;t1++)
		{
			if(temp&0x80)
				LCD_Fast_DrawPoint(x,y,POINT_COLOR);
			else if(mode==0)
				LCD_Fast_DrawPoint(x,y,BACK_COLOR); 
			temp<<=1;
			y++;
			if((y-y0)==size)
			{
				y=y0;
				x++;
				break;
			}
		}  	 
	}  
}
//在指定位置开始显示一个字符串	    
//支持自动换行
//(x,y):起始坐标
//width,height:区域
//str  :字符串
//size :字体大小
//mode:0,非叠加方式;1,叠加方式    	   		   
void Show_Str(u16 x,u16 y,u16 width,u16 height,u8*str,u8 size,u8 mode)
{					
	u16 x0=x;
	u16 y0=y;							  	  
    u8 bHz=0;     //字符或者中文  	    				    				  	  
    while(*str!=0)//数据未结束
    { 
        if(!bHz)
        {
	        if(*str>0x80)
				bHz=1;//中文 
	        else      //字符
	        {      
                if(x>(x0+width-size/2))//换行
				{				   
					y+=size;
					x=x0;	   
				}							    
		        if(y>(y0+height-size))
					break;//越界返回      
		        if(*str==13)//换行符号
		        {         
		            y+=size;
					x=x0;
		            str++; 
		        }  
		        else 
					LCD_ShowChar(x,y,*str,size,mode);//有效部分写入 
				str++; 
		        x+=size/2; //字符,为全字的一半 
	        }
        }
		else//中文 
        {     
            bHz=0;//有汉字库    
            if(x>(x0+width-size))//换行
			{	    
				y+=size;
				x=x0;		  
			}
	        if(y>(y0+height-size))
				break;//越界返回  						     
	        Show_Font(x,y,str,size,mode); //显示这个汉字,空心显示 
	        str+=2; 
	        x+=size;//下一个汉字偏移	    
        }						 
    }   
}  			 		 
//在指定宽度的中间显示字符串
//如果字符长度超过了len,则用Show_Str显示
//len:指定要显示的宽度			  
void Show_Str_Mid(u16 x,u16 y,u8*str,u8 size,u8 len)
{
	u16 strlenth=0;
   	strlenth=strlen((const char*)str);
	strlenth*=size/2;
	if(strlenth>len)
		Show_Str(x,y,lcddev.width,lcddev.height,str,size,1);
	else
	{
		strlenth=(len-strlenth)/2;
	    Show_Str(strlenth+x,y,lcddev.width,lcddev.height,str,size,1);
	}
}   




3.3 text.h

#ifndef __TEXT_H__
#define __TEXT_H__	 
#include <stm32f4xx.h>
#include "fontupd.h"


 					     
void Get_HzMat(unsigned char *code,unsigned char *mat,u8 size);			//得到汉字的点阵码
void Show_Font(u16 x,u16 y,u8 *font,u8 size,u8 mode);					//在指定位置显示一个汉字
void Show_Str(u16 x,u16 y,u16 width,u16 height,u8*str,u8 size,u8 mode);	//在指定位置显示一个字符串 
void Show_Str_Mid(u16 x,u16 y,u8*str,u8 size,u8 len);
#endif

3.4 fontupd.c

#include "fontupd.h"
#include "ff.h"	  
#include "W25Q128.h"   
#include "lcd.h"  
#include "string.h"
#include "Malloc.h"
#include "delay.h"
#include "usart.h"



//字库区域占用的总扇区数大小(3个字库+unigbk表+字库信息=3238700字节,约占791个W25Q128扇区)
#define FONTSECSIZE	 	791
//字库存放起始地址 
#define FONTINFOADDR 	1024*1024*12 					//Explorer STM32F4是从12M地址以后开始存放字库
														//前面12M被fatfs占用了.
														//12M以后紧跟3个字库+UNIGBK.BIN,总大小3.09M,被字库占用了,不能动!
														//15.10M以后,用户可以自由使用.建议用最后的100K字节比较好.
														
//用来保存字库基本信息,地址,大小等
_font_info ftinfo;

//字库存放在磁盘中的路径
u8*const GBK24_PATH="/SYSTEM/FONT/GBK24.FON";		//GBK24的存放位置
u8*const GBK16_PATH="/SYSTEM/FONT/GBK16.FON";		//GBK16的存放位置
u8*const GBK12_PATH="/SYSTEM/FONT/GBK12.FON";		//GBK12的存放位置
u8*const UNIGBK_PATH="/SYSTEM/FONT/UNIGBK.BIN";		//UNIGBK.BIN的存放位置

//显示当前字体更新进度
//x,y:坐标
//size:字体大小
//Entirefilesize:整个文件大小
//position:当前文件指针位置
u32 fupd_prog(u16 x,u16 y,u8 size,u32 Entirefilesize,u32 position)
{
	float prog;
	u8 t=0XFF;
	prog=(float)position/Entirefilesize; //打印出小数
	prog*=100;//乘以100 转换成百分比的形式
	if(t!=prog)
	{
		LCD_ShowString(x+3*size/2,y,240,320,size,"%");		
		t=prog;
		if(t>100) //最多100%
			t=100;
		LCD_ShowNum(x,y,t,3,size);//显示数值
	}
	return 0;					    
}
//更新某一个
//x,y:坐标
//size:字体大小
//fxpath:路径
//fx:更新的内容 0,ungbk;1,gbk12;2,gbk16;3,gbk24;
//返回值:0,成功;其他,失败.
u8 updata_fontx(u16 x,u16 y,u8 size,u8 *fxpath,u8 fx)
{
	u32 flashaddr=0;								    
	FIL * fftemp;
	u8 *tempbuf;
 	u8 res;	
	u16 bread;
	u32 offx=0;
	u8 rval=0;	     //错误判断标志
	fftemp=(FIL*)mymalloc(SRAMIN,sizeof(FIL));	//分配内存	
	if(fftemp==NULL)
		rval=1;
	tempbuf=mymalloc(SRAMIN,4096);				//分配4096个字节空间
	if(tempbuf==NULL)
		rval=1;
 	res=f_open(fftemp,(const TCHAR*)fxpath,FA_READ); 
 	if(res)
		rval=2;//打开文件失败  
 	if(rval==0)//表示没有出错
	{
		switch(fx) //fx表示更新的内容
		{
			case 0:												//更新UNIGBK.BIN
				ftinfo.ugbkaddr=FONTINFOADDR+sizeof(ftinfo);	//信息头之后,紧跟UNIGBK转换码表
				ftinfo.ugbksize=fftemp->fsize;					//UNIGBK大小
				flashaddr=ftinfo.ugbkaddr;
				break;
			case 1:
				ftinfo.f12addr=ftinfo.ugbkaddr+ftinfo.ugbksize;	//UNIGBK之后,紧跟GBK12字库
				ftinfo.gbk12size=fftemp->fsize;					//GBK12字库大小
				flashaddr=ftinfo.f12addr;						//GBK12的起始地址
				break;
			case 2:
				ftinfo.f16addr=ftinfo.f12addr+ftinfo.gbk12size;	//GBK12之后,紧跟GBK16字库
				ftinfo.gbk16size=fftemp->fsize;					//GBK16字库大小
				flashaddr=ftinfo.f16addr;						//GBK16的起始地址
				break;
			case 3:
				ftinfo.f24addr=ftinfo.f16addr+ftinfo.gbk16size;	//GBK16之后,紧跟GBK24字库
				ftinfo.gkb24size=fftemp->fsize;					//GBK24字库大小
				flashaddr=ftinfo.f24addr;						//GBK24的起始地址
				break;
		} 
			
		while(res==FR_OK)//死循环执行
		{
	 		res=f_read(fftemp,tempbuf,4096,(UINT *)&bread);		//读取数据	 
			if(res!=FR_OK)break;								//执行错误
			W25QXX_Write(tempbuf,offx+flashaddr,4096);		//从0开始写入4096个数据  
	  		offx+=bread;	  
			fupd_prog(x,y,size,fftemp->fsize,offx);	 			//进度显示
			if(bread!=4096)break;								//读完了.
	 	} 	
		f_close(fftemp);		
	}			 
	myfree(SRAMIN,fftemp);	//释放内存
	myfree(SRAMIN,tempbuf);	//释放内存
	return res;
} 
//更新字体文件,UNIGBK,GBK12,GBK16,GBK24一起更新
//x,y:提示信息的显示地址
//size:字体大小
//src:字库来源磁盘."0:",SD卡;"1:",FLASH盘,"2:",U盘.
//提示信息字体大小										  
//返回值:0,更新成功;
//		 其他,错误代码.	  
u8 update_font(u16 x,u16 y,u8 size,u8* src)
{	
	u8 *pname;
	u32 *buf;
	u8 res=0;		   
 	u16 i,j;
	FIL *fftemp;
	u8 rval=0; 
	res=0XFF;		
	ftinfo.fontok=0XFF;
	pname=mymalloc(SRAMIN,100);	//申请100字节内存  
	buf=mymalloc(SRAMIN,4096);	//申请4K字节内存  
	fftemp=(FIL*)mymalloc(SRAMIN,sizeof(FIL));	//分配内存	
	if(buf==NULL||pname==NULL||fftemp==NULL)
	{
		myfree(SRAMIN,fftemp);
		myfree(SRAMIN,pname);
		myfree(SRAMIN,buf);
		return 5;	//内存申请失败
	}
	//先查找文件是否正常 
	strcpy((char*)pname,(char*)src);	//copy src内容到pname
	strcat((char*)pname,(char*)UNIGBK_PATH); 
 	res=f_open(fftemp,(const TCHAR*)pname,FA_READ); 
 	if(res)rval|=1<<4;//打开文件失败  
	strcpy((char*)pname,(char*)src);	//copy src内容到pname
	strcat((char*)pname,(char*)GBK12_PATH); 
 	res=f_open(fftemp,(const TCHAR*)pname,FA_READ); 
 	if(res)rval|=1<<5;//打开文件失败  
	strcpy((char*)pname,(char*)src);	//copy src内容到pname
	strcat((char*)pname,(char*)GBK16_PATH); 
 	res=f_open(fftemp,(const TCHAR*)pname,FA_READ); 
 	if(res)rval|=1<<6;//打开文件失败  
	strcpy((char*)pname,(char*)src);	//copy src内容到pname
	strcat((char*)pname,(char*)GBK24_PATH); 
 	res=f_open(fftemp,(const TCHAR*)pname,FA_READ); 
 	if(res)rval|=1<<7;//打开文件失败   
	myfree(SRAMIN,fftemp);//释放内存
	if(rval==0)//字库文件都存在.
	{  
		LCD_ShowString(x,y,240,320,size,"Erasing sectors... ");//提示正在擦除扇区	
		for(i=0;i<FONTSECSIZE;i++)	//先擦除字库区域,提高写入速度
		{
			fupd_prog(x+20*size/2,y,size,FONTSECSIZE,i);//进度显示
			W25Q128_Read((u8*)buf,((FONTINFOADDR/4096)+i)*4096,4096);//读出整个扇区的内容
			for(j=0;j<1024;j++)//校验数据
			{
				if(buf[j]!=0XFFFFFFFF)break;//需要擦除  	  
			}
			if(j!=1024)W25Q128_Erase_Sector((FONTINFOADDR/4096)+i);	//需要擦除的扇区
		}
		myfree(SRAMIN,buf);
		LCD_ShowString(x,y,240,320,size,"Updating UNIGBK.BIN");		
		strcpy((char*)pname,(char*)src);				//copy src内容到pname
		strcat((char*)pname,(char*)UNIGBK_PATH); 
		res=updata_fontx(x+20*size/2,y,size,pname,0);	//更新UNIGBK.BIN
		if(res){myfree(SRAMIN,pname);return 1;}
		LCD_ShowString(x,y,240,320,size,"Updating GBK12.BIN  ");
		strcpy((char*)pname,(char*)src);				//copy src内容到pname
		strcat((char*)pname,(char*)GBK12_PATH); 
		res=updata_fontx(x+20*size/2,y,size,pname,1);	//更新GBK12.FON
		if(res){myfree(SRAMIN,pname);return 2;}
		LCD_ShowString(x,y,240,320,size,"Updating GBK16.BIN  ");
		strcpy((char*)pname,(char*)src);				//copy src内容到pname
		strcat((char*)pname,(char*)GBK16_PATH); 
		res=updata_fontx(x+20*size/2,y,size,pname,2);	//更新GBK16.FON
		if(res){myfree(SRAMIN,pname);return 3;}
		LCD_ShowString(x,y,240,320,size,"Updating GBK24.BIN  ");
		strcpy((char*)pname,(char*)src);				//copy src内容到pname
		strcat((char*)pname,(char*)GBK24_PATH); 
		res=updata_fontx(x+20*size/2,y,size,pname,3);	//更新GBK24.FON
		if(res){myfree(SRAMIN,pname);return 4;}
		//全部更新好了
		ftinfo.fontok=0XAA;
		W25QXX_Write((u8*)&ftinfo,FONTINFOADDR,sizeof(ftinfo));	//保存字库信息
	}
	myfree(SRAMIN,pname);//释放内存 
	myfree(SRAMIN,buf);
	return rval;//无错误.			 
} 
//初始化字体
//返回值:0,字库完好.
//		 其他,字库丢失
u8 font_init(void)
{		
	u8 t=0;
	W25Q128_Init();  
	while(t<10)//连续读取10次,都是错误,说明确实是有问题,得更新字库了
	{
		t++;
		W25Q128_Read((u8*)&ftinfo,FONTINFOADDR,sizeof(ftinfo));//读出ftinfo结构体数据
		if(ftinfo.fontok==0XAA)break;
		delay_ms(20);
	}
	if(ftinfo.fontok!=0XAA)return 1;
	return 0;		    
}




3.5 fontupd.h

#ifndef __FONTUPD_H__
#define __FONTUPD_H__	 
#include <stm32f4xx.h>




//字体信息保存地址,占33个字节,第1个字节用于标记字库是否存在.后续每8个字节一组,分别保存起始地址和文件大小														   
extern u32 FONTINFOADDR;	
//字库信息结构体定义
//用来保存字库基本信息,地址,大小等
__packed typedef struct 
{
	u8 fontok;				//字库存在标志,0XAA,字库正常;其他,字库不存在
	u32 ugbkaddr; 			//unigbk的地址
	u32 ugbksize;			//unigbk的大小	 
	u32 f12addr;			//gbk12地址	
	u32 gbk12size;			//gbk12的大小	 
	u32 f16addr;			//gbk16地址
	u32 gbk16size;			//gbk16的大小		 
	u32 f24addr;			//gbk24地址
	u32 gkb24size;			//gbk24的大小 
}_font_info; 


extern _font_info ftinfo;	//字库信息结构体

u32 fupd_prog(u16 x,u16 y,u8 size,u32 fsize,u32 pos);	//显示更新进度
u8 updata_fontx(u16 x,u16 y,u8 size,u8 *fxpath,u8 fx);	//更新指定字库
u8 update_font(u16 x,u16 y,u8 size,u8* src);			//更新全部字库
u8 font_init(void);										//初始化字库
#endif



本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1121556.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

算法通过村第十六关-滑动窗口|青铜笔记|滑动很简单

文章目录 前言滑动窗口的基本思想入门题目练习子数组最大平均数最长连续递增序列 总结 前言 提示&#xff1a;我宁愿做自己&#xff0c;做卑微的自己&#xff0c;也不愿做别人&#xff0c;无论那会多么快乐。 --《美丽新世界》 我们在数组和链表的部分就已经接触到了双指针的思…

IMX6ULL开发——第一个驱动程序

实现第一个应用程序&#xff1a;在IMX6ULL开发板上运行驱动程序hello_drv_test hello_drv_test #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <stdio.h> #include <string.h>/** ./hel…

生产者消费者模式(c++实现)

生产者消费者模式思路就是&#xff1a;一批专门生产资源的线程 和一批专门处理资源的线程以及一个线程安全的任务队列组成的 。并且当任务队列满的时候阻塞生产线程&#xff0c;任务队列为空的时候阻塞消费线程。 要实现一个生产者消费者队列 1。需要实现线程同步&#xff0c;…

react的setState做了什么

1、为什么需要setState setState的作用是帮助我们更改数据的同时并且通知视图进行渲染。因为React并不会绑定视图和state&#xff0c;需要我们手动去更新视图。 2、setState什么时候是同步的&#xff0c;什么时候是异步的 setState这个方法在调用的时候是同步的&#xff0c;…

如果面试问到你redis的常用数据类型,你怎么和面试官聊上十分钟?

最近组长把一些简历推到我这边让我帮他面试几份&#xff0c;问到这种基础题目时收到的回答总是不太理想 1、最简单的回答&#xff1a; Redis存储的是key-value结构的数据&#xff0c;其中key是字符串类型&#xff0c;value有5种常用的数据类型&#xff1a; 字符串 string哈希 …

为什么spring默认采用单例bean

概 述 熟悉 Spring开发的朋友都知道 Spring 提供了 5种 scope&#xff0c;分别是&#xff1a; singleton: 单例模式&#xff0c;当spring创建applicationContext容器的时候&#xff0c;spring会欲初始化所有的该作用域实例&#xff0c;加上lazy-init就可以避免预处理&#xf…

磁盘分区如何分? 电脑磁盘分区免费软件指南!

列出并比较顶级免费磁盘分区管理器软件&#xff0c;以选择适用于 Windows 的最佳分区软件&#xff1a; 系统分区在现代计算机设备中起着非常重要的作用。它们可以存储数据&#xff0c;使系统文件远离用户数据&#xff0c;并在同一台设备上安装多个操作系统。但是&#xff0c;这…

MyBatis-Plus 实战教程一

这里写目录标题 简介快速上手数据库建立创建实体类修改参数引入依赖测试常见注解介绍TableNameTableIdTableField 常见配置仓库地址 简介 MyBatis-Plus&#xff08;简称 MP&#xff09;是一个 MyBatis 的增强工具&#xff0c;在 MyBatis 的基础上只做增强不做改变&#xff0c;…

qt-C++笔记之信号与槽

qt-C笔记之信号与槽 code review! 本文抄自公众号&#xff1a;嵌入式小生 文章目录 qt-C笔记之信号与槽一.信号2.1.信号的发出2.2.信号的处理 二.槽函数2.1.带有默认参数的信号和槽函数2.2.使用QObject::connect()将信号连接到槽函数的三种方法2.2.1.第一种方法&#xff1a;使…

整理MongoDB文档:身份验证

整理MongoDB文档:身份验证 个人博客&#xff0c;求关注。 文章概叙 本文主要讲MongoDB在单机状态下的账户配置。理解了MongoDB的语法&#xff0c;对于如何配置用户权限会知道怎么配置&#xff0c;但是请注意给谁配置什么权限才是最重要的。 最小权限原则 系统的每个程序或者…

【C++编程语言】STL常用算法 算术生成和集合算法

1.算术生成算法概念 算法简介&#xff1a; accumlate 计算容器元素累计总和fill 向容器中添加元素 注意&#xff1a;算术生成算法属于小型算法 使用时包含头文件为#include<numeric> 2.accumulate /*函数原型&#xff1a;int accumulate(iterator beg ,iterator end…

热点不热!如何修复笔记本电脑未连接到移动热点的问题

当你远离常规Wi-Fi时,移动热点是让你的笔记本电脑上网的关键,但当它没有按计划运行时,你会怎么办?以下是Windows笔记本电脑无法连接到移动热点时的几种修复方法。 为什么我的笔记本电脑没有连接到我的热点 由于你的笔记本电脑正试图连接到另一个有限制和可能存在问题的设…

前端学成在线项目详细解析二

12-banner区域-课程表布局 HTML布局 <div class"right"><h3>我的课程表</h3><div class"content">1</div> </div> CSS样式 /* 课程表 */ .banner .right {margin-top: 60px;width: 218px;height: 305px;background-…

STM32F4_照相机

目录 前言 1. BMP编码 2. JPEG编码 前言 我们所要实现的照相机&#xff0c;支持BMP图片格式的照片和JPEG图片格式的照片。 1. BMP编码 BMP文件是由文件头、位图信息头、颜色信息和图形数据四部分构成。 1. BMP文件头&#xff08;14个字节&#xff09;&#xff1a;BMP文件…

在ESP32-Arduino开发中添加其它Arduino库

目录 前言 原理说明 操作步骤 下载Bounce 安装Bounce 将下载的文件夹(压缩包需要解压)移动到components/arduino/libraries路径下&#xff0c;并重命名为Bounce2 查看添加库里所有的源文件位置 在arduino的CMakeList.txt里添加库源文件 使用Bounce 前言 乐鑫官方的es…

HTTP介绍 原理 消息结构 客户端请求 服务器响应 HTTP状态码

一、HTTP介绍二、HTTP工作原理HTTP三点注意事项 三、HTTP消息结构四、客户端请求消息五、服务器响应消息HTTP请求方法 七、HTTP响应头信息八、HTTP状态码&#xff08;HTTP Status Code&#xff09;下面是常见的HTTP状态码&#xff1a;HTTP状态码分类HTTP状态码列表 一、HTTP介绍…

旁注、目录越权、跨库查询、cdn绕过

原理&#xff1a; 搭建网站多IP多端口&#xff0c;更多一个域名多网站&#xff0c;IIS的在属性-高级里面设置主机头设置域名&#xff0c;域名是收费的需要自己买一个 旁注&#xff1a;在同一服务器上有多个站点&#xff0c;要攻击的这个站点假设没有漏洞&#xff0c;可以攻击…

Spark大数据分析与实战笔记(第一章 Scala语言基础-5)

文章目录 每日一句正能量章节概要1.5 Scala的模式匹配与样例类1.5.1 模式匹配字符匹配匹配字符串守卫匹配类型匹配数组、元组、集合 1.5.2 样例类 课外补充偏函数 每日一句正能量 “成功的秘诀&#xff0c;在于对目标的执着追求。”——爱迪生 无论是在工作、学习、还是生活中&…

控制台的设置

目录 win32 API &#xff1a; 什么是API &#xff1a; 控制台&#xff1a; 控制台与VS&#xff1a; 控制台的控制&#xff1a; 控制台窗口设置&#xff1a; 1、mode函数&#xff1a; 2、title 函数&#xff1a; 在C语言中的实现&#xff1a; 控制台的坐标设置&#…

python 之numpy 之随机生成数

文章目录 1. **生成均匀分布的随机浮点数**&#xff1a;2. **生成随机整数**&#xff1a;3. **生成标准正态分布随机数**&#xff1a;4. **生成正态分布随机数**&#xff1a;5. **生成均匀分布的随机浮点数**&#xff1a;6. **生成随机抽样**&#xff1a;7. **设置随机数种子**…