stm32+lcd显示汉字之DIY图形及导入字库_lcd多个中文的字库存储方式-程序员宅基地

技术标签: stm32  keil mdk  c语言  SD卡相关应用  嵌入式  单片机  

一、建立自己的点阵集

上一篇博客,给大家介绍了在LCD上显示汉字/特殊图形需要知道对应的显示编码(GBK码/ASCII码),显示编码对应着每个图形的点阵集(数组),我们可以通过这个点阵集,将自己DIY的图形显示在LCD上面。现在先说一下怎么做一个自己的点阵集。一般情况下,用PCtoLCD2002这个软件,可以自动帮我们生成每个图形对应着的点阵集。
​​​​
​​​​​​​​​​​​​​​​​​在这里插入图片描述
打开这个软件先点1选择字符模式,再点2,3设置每个图形的大小,然后从4处在这里插入图片描述写入我们的要显示的图形。生成字模之前点击齿轮(设置)来设置我们生成字模的格式。
在这里插入图片描述
一般的我们选择默认就好了,更改一下点阵大小和显示格式就行。到这一步,我们就可以生成自己的图形的点阵集了。
在这里插入图片描述
点击生成字模后,我们便得到了这三个汉字的字模了。返回最开始,我们选择模式时更改为图形模式,便可以DIY自己想要显示的图形,自己可以选择图形的大小,这里我选择64*64大小的点阵集来DIY。
在这里插入图片描述
至此,我们就得到了4个数组,每个数组显示一个图形。

二、将自定义图形显示在LCD上

我们先定义3个文件,show.c show.h graph.h,并且将上面的4个数组放在graph.h里。
在这里插入图片描述
因为特殊字符囧并没有自己的显示编码,所以我们不采用显示编码方式显示图形,而是直接通过数组名来显示。我们直接上函数:

//显示一个DIY图形
//x,y:图形显示的坐标
//num:第几个图形
//size:图形大小
//mode:0,正常显示,1,叠加显示	
void Show_Graph(u16 x,u16 y,u8 *num,u8 size,u8 mode)
{
    
	u8 temp;
	u16 t,t1;
	u16 y0 = y;
	u16 x0 = x;
	u16 csize=(size/8+((size%8)?1:0))*(size);//得到字体一个字符对应点阵集所占的字节数	 
	for(t = 0;t < csize;t++)
	{
    
		temp = num[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;
			}
			if((x-x0) == size)
				{
    
					x = x0;
					break;
				}
		}
	}
}

这个函数是把原子的Show_Font()显示汉字函数改了改,其实本质就是取得字模的差别,Show_Font()里通过读取W25Q128的相应地址来取得字模,这里我们自己建立了字模,直接显示即可。下面上主函数:

#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "lcd.h"
#include "show.h"
#include "graph.h"


int main(void)
{
     
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组2
	delay_init(168);      //初始化延时函数
	uart_init(115200);		//初始化串口波特率为115200
	
	LED_Init();					  //初始化LED
 	LCD_Init();           //初始化LCD FSMC接口
	POINT_COLOR=RED;      //画笔颜色:红色
	
	Show_Graph(100,100,(u8*)jiong_64,64,0);
	Show_Graph(25,200,(u8*)str1,16,0);
	Show_Graph(75,200,(u8*)str2,16,0);
	Show_Graph(125,200,(u8*)str3,16,0);
	
  while(1);
}

在这里插入图片描述
通过图片,我们可以看到,原来建立好的点阵集(数组),都已经显示在了LCD上面。

三、通过GBK码寻址显示汉字

上面我们是通过自己建立字模,来显示特定的几个汉字,但是在一个完整的系统里,可能需要大量的汉字来供我们使用。这里就要用到我们的GBK库了,通过对应汉字在这个库的偏移量,找到相应汉字的字模,并且显示出来。

大体的思路是,我打算用EEPROM存储GBK库,GBK库我们可以从网上随便找一个都行,这里我还是用原子的SYSTEM文件夹下面自带的GBK16这个库来做。因为探索者板载的24C02大小为2K字节,不足以存储常用的三种大小的库,所以选择一个中间大小。我们这里找到SD卡根目录下SYSTEM/FONT文件夹里的字库:
在这里插入图片描述
我们可以看到3个不同大小的GBK字库,和一个UNIGBK.BIN,这个文件一般用在FATFS文件系统时支持中文路径来使用的。如果不需要支持中文路径的话可以不加。GBK16的大小为749k字节,大于24C02的2K字节。!!!!方案错误,24C02大小不足以存储字库!

我们这里只能说一下步骤了(虽然实现不了,但是我想看看结果会是什么样。。):
1、基于FATFS文件系统,通过SD卡把GBK16这个库的文件读取出来,然后写入到EEROM里面。
2、编写显示汉字函数,其入口参数为每个汉字字符的GBK码。
3、调用显示函数,将要显示的汉字显示在LCD上面。

理论上讲,这三部有关文件的操作只在SD卡上运行,所以EEPROM可以不用移植到FATFS文件系统里面,我们读取SD卡上的数据,然后存入EEPROM当中即可。

首先FATFS文件管理,把SD卡里的文件读取出来,然后写入到24C02中,文件路径为:
u8*const GBK16_PATH="/SYSTEM/FONT/GBK16.FON";
这里上一下相关程序:

u8 updata_fontx(u16 x,u16 y,u8 size,u8 *fxpath)
{
    
	u8 res,rval = 0;
	u16 bread;
	u32 flashaddr = 0,offset = 0;
	FIL* ftemp;
	u8* tempbuf;
	fupfont.upfont_OK = 0xFF;
	tempbuf = (u8*)mymalloc(SRAMIN,4096);
	if(tempbuf == NULL) rval = 1;
	ftemp = (FIL*)mymalloc(SRAMIN,sizeof(FIL));
	if(ftemp == NULL) rval = 1;
	res = f_open(ftemp,(const TCHAR*)fxpath,FA_READ);
	if(res) rval = 2;
	printf("f_open的res的值为:%d\r\n",res);
	if(rval == 0)
	{
    
		fupfont.gbk16_addr = EERPOM_ADDR + sizeof(fupfont); //字库开始地址
		fupfont.gbk16_size = ftemp->fsize; //字库大小	
		flashaddr = fupfont.gbk16_addr ;//GBK16字库起始地址
		while(res == FR_OK)
		{
    
			res = f_read(ftemp,tempbuf,4096,(UINT*)&bread);
			printf("bread的值为:%d\r\n",bread);
			if(res != FR_OK) break;
			AT24CXX_Write(flashaddr + offset,tempbuf,4096);
			offset += bread;
			fupd_prog(x,y,size,ftemp->fsize,offset);	 			//进度显示 fftemp->fsize具体每个文件的大小,我们可以掌握
			if(bread != 4096)break;
		}
		f_close(ftemp);
		fupfont.upfont_OK = 0xAA;
		AT24CXX_Write(EERPOM_ADDR,(u8*)&fupfont,sizeof(fupfont));	
	}
	myfree(SRAMIN,ftemp);	//释放内存
	myfree(SRAMIN,tempbuf);	//释放内存
	return res;
}

这里要说一下这个结构体,_up_font fupfont :

//这个结构体使用关键字__packed取消了编译器自动优化结构体地址,所以这个结构体总共9字节大小!
//我们把这个结构体存储到EEPROM中,即可知道字库是否更新,和存储字库地址
__packed typedef struct
{
    
	u8 upfont_OK;   // 设定这个值为0xAA时,更新字库成功!否则为0xFF
	u32 gbk16_addr; // GBK16字库的地址
	u32 gbk16_size; // GBK16字库的大小
}_up_font;

这个结构体标注了字库地址,字库大小和是否更新标志位,因为我们加了__packed关键字,所以我们结构体变量占用了9字节空间。我们把这9字节空间放进24C02的首地址中,帮助我们标识字库基本信息。那么我们的字库数据就要放在这9字节数据后面,AT24CXX_Write(flashaddr + offset,tempbuf,4096);//flashaddr 即为9字节大小

至此,我们通过SD卡根据文件路径读取文件信息,并将信息写入24C02中,然后我们根据入口参数(一般为字符串)GBK码的大小,算出其偏移,根据偏移在GBK库中找到相应的字模读取出来,然后通过将其打印在LCD屏幕上。通过入口参数(字符串等)得到GBK码的方法可以参照我的上一篇博客,里面有很详细的演示:
stm32+lcd显示汉字(一)

因为打印字模数组的方法我们上面演示过了,所以我们直接上从24C02中,取出字模的函数:

//这个函数其实就是读取到字符后,取出其GBK码,然后通过GBK码,找到其对应的字模数组。
//code:输入的字符,通过*运算,可以取其GBK码
//mat:通过GBK码,判断每个汉字的字模数据
void Get_Font_Mat(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+1);
	if(qh<0x81||ql<0x40||ql==0xff||qh==0xff)//非 常用汉字
	{
       		    
	    for(i=0;i<csize;i++)*mat++=0x00;//填充满格
	    return; //结束访问
	}
	if(ql<0x7f)ql-=0x40;//注意!
	else ql-=0x41;
	qh-=0x81;   
	foffset=((unsigned long)190*qh+ql)*csize;	//得到字库中的字节偏移量  		  

	AT24CXX_Read(foffset+fupfont.gbk16_addr,mat,csize);
}

fupfont.gbk16_addr 就是刚才说的结构体大小,字模存储在这个大小后面,我们通过入口参数,算出偏移,然后用偏移+fupfont.gbk16_addr得到一个地址,从这个地址开始取出csize大小的字模,把它放到mat中去。至此,通过GBK寻址显示汉字的方法我们已经说完了。
在这里插入图片描述
实验的结果如下:
在这里插入图片描述
在这里插入图片描述
第一幅图是在更新字库,第二幅图即为将对应字符显示在LCD上面,因为24C02的大小限制,导致我白忙活了一下午加一晚上,这也是粗心导致把。正常的显示如下:
在这里插入图片描述

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/su_fei_ma_su/article/details/104486754

智能推荐

linux centos7安装redis_linux7安装redis客户端-程序员宅基地

文章浏览阅读312次。linux安装redis_linux7安装redis客户端

动态规划入门感悟_动态规划的基本思想及感悟-程序员宅基地

文章浏览阅读357次,点赞2次,收藏3次。不会动态规划的可以看看我博客里动态规划入门一文哦http://blog.csdn.net/qq_39670434/article/details/77414362在这里为了帮助萌新们学习动态规划,调用几篇大佬的好文章http://blog.csdn.net/baidu_28312631/article/details/47418773http://blog.csdn.net_动态规划的基本思想及感悟

单片机上电不断重启复位_stc8f单片机勾选低压复位单片机不断复位怎么解决-程序员宅基地

文章浏览阅读7.7k次,点赞4次,收藏15次。单片机不断重启复位的原因主要是一个原因;就是单片机在不断复位造成复位的原因有几个;1、单片机硬件复位,这个要检查硬件电路中的复位电路是不是有错误2、单片机程序中有代码造成软件复位3、单片机在不断上电断电循环过程造成不断复位主要讲一下第三种情况,这种情况的主要原因的因为外部电源供电不足导致单片机不断复位。你先检查一下自己的电路中是否有很多需要供电的模块,比如电机,制冷发热类的模块等等需要..._stc8f单片机勾选低压复位单片机不断复位怎么解决

哈尔滨工业大学计算机科学与技术学院许博文,王轩-哈尔滨工业大学(深圳)计算机科学与技术学院...-程序员宅基地

文章浏览阅读573次。一、近三年发表期刊论文[1]第一作者及通讯作者论文1)Yulin Wu, Xuan Wang, Zoe L. Jiang, etc. Efficient Server-Aided Secure Two-Party Computation in Heterogeneous Mobile Cloud Computing, IEEE Transactions on Dependable and Secu..._ansactions on dependable and secure computing

Prometheus源码学习(8) scrape总体流程_promethus scrapes-程序员宅基地

文章浏览阅读1.2k次。1. main 函数中初始化 scrapeManager 实例// 初始化 scrapeManager,fanout Storage 是一个读写多个底层存储的代理scrapeManager = scrape.NewManager(log.With(logger, "component", "scrape manager"), fanoutStorage)fanoutStorage 是读写多个底层存储的代理,实现了 storage.Appendable 接口。scrape.Manager 结构体._promethus scrapes

Windows10 Atom安装和运行Python的使用教程(详细)-程序员宅基地

文章浏览阅读2.6w次,点赞19次,收藏85次。目录一、下载Atom二、Atom安装Python相关组件1.检查Python库支持2.安装Python的适合Atom的IDE、UI、Server和运行工具*三、运行代码范例(爬取以杉原杏璃为关键字的百度图片)一、下载Atom1.官网:Atom官网2.打开这个网页,可以看到Atom针对于操作系统Windows7或以上的版本3.下载完成,双击exe4.加..._atom安装

随便推点

ubuntu怎么卸载matlab,卸载Ubuntu下Matlab (uninstall matlab2009 for linux)-程序员宅基地

文章浏览阅读422次。2.在/var目录搜索lm,我只找到以lm-sensors开头的文件,而lm-sensors是一个硬件状况监视器,用来得到温度、电压、风扇速度传感器信我在matlab官网搜到如下帮助文档Problem Description:I would like to uninstall MATLAB on a UNIX or Linux machine.Solution:There is not unins..._ubantu 无法移除matlab

运算放大器的关键指标详解二(噪声)_运放pid电路的噪声-程序员宅基地

文章浏览阅读2.1w次,点赞43次,收藏192次。噪声指标(Noise)一个正常工作的放大电路,当输入端接地时,用示波器观察输出,你看到的可能不是平直的细线,而是在一定幅度之内的杂乱无章的波形。这就是噪声。 你在示波器上看到线越粗,就说明噪声幅度越大。放大电路的输出端噪声,小至 μV 以下,大至百 mV 以上,完全取决于电路设计,能否在示波器上看见,则取决于示波器选择和设置。噪声定义:1) 它的波形在任意时刻都是不确定的,因此它是广谱的,有低频也有高频;2) 它的幅度又是有限制的,这与数学上的高斯分布近似但不完全一致;3) 它具有无限积分趋零性_运放pid电路的噪声

数据分析案例分析:日化公司社群营销场景,产品SKU,用户转化率,用户流转地图_leads数-程序员宅基地

文章浏览阅读3k次。近期参加了一个业务数据分析的3天课程,锻炼自己的业务实例数据分析能力。接下来的内容是第一天课程的作业,里面涉及到许多自己的知识盲区(社群营销,产品SKU,用户转化率,用户流转地图)。分享在这里,期待感兴趣的同志们多多指点~目录作业要求问题分析背景分析概念厘清1. 社群营销是什么?拉群卖货吗?2. 产品SKU3. 用户转化率Q2为什么展示后4张图(图6-图9)【作业点评总结Q2】Q1 从图中看,存在什么问题,出现这些问题的原因是什么Step1. 分..._leads数

数据库多维度水平切分设想 --- 分库,分表,多维度,水平切分,mysql,负载均衡_ssas 加入维度后数据没有切分-程序员宅基地

文章浏览阅读3k次。随着互联网应用的普及,海量数据存储早已经成了大型网站技术人员关注的焦点。每天上亿的访问量对数据库的压力可想而知。因此,为了维持数据库的稳定性和可扩展性,我们常常选择在业务垂直切分的基础上(抽象出业务中心单元如用户中心),再进行数据库的水平切分。 那么为什么要进行数据库的水平切分呢?有些人会说,如果单台数据库服务器过载过高,则可以采用replication的机制,分别指定读写服务器实现读写_ssas 加入维度后数据没有切分

eclipse中导航栏名称主要功能_echarls的导航栏意思-程序员宅基地

文章浏览阅读375次。A-mapper 里面主要写数据库的A-parent 类,父类方法A-pojo POJO实质上可以理解为简单的实体类,顾名思义POJO类的作用是方便程序员使用数据库中的数据表,对于广大的程序员,可以很方便的将POJO类当做对象来进行使用,当然也是可以方便的调用其get,set方法。POJO类也给我们在struts框架中的配置带来了很大的方便。A-service 提供与数据..._echarls的导航栏意思

WebSocket 的 PHP 实现 - phpwebsocket_websocket chr(1)-程序员宅基地

文章浏览阅读1.3k次。从名字上也可看出,这是一个 WebSocket 的 PHP 实现。示例客户端代码:1var host = "ws://localhost:12345/websocket/server.php";2try{3 socket = new WebSocket(host);_websocket chr(1)

推荐文章

热门文章

相关标签