设计与开发

单片机led显示汉字 单片机入门-旋转屏,用16个LED灯显示16个汉字

小编 2024-10-23 设计与开发 23 0

单片机入门-旋转屏,用16个LED灯显示16个汉字

所谓的旋转显示屏,是在电路中只有一列发光二极管,通过电动机带动发光二极管转动,当这列发光二极管转到不同位置,用单片机控制相应的发光二极管点亮和熄灭,由于人眼的视觉暂留现象形成图形或文字,达到悬浮在空中神奇梦幻般的效果。

神奇梦幻般的效果

下面本文就带您一起DIY一个属于自己的LED旋转显示屏。

由于显示屏是靠转动的发光二极管的残留影像显示信息的,整个电路所需的发光二极管的数量很少,本电路共使用16只发光二极管,所以电路原理图非常简单,几乎和流水灯电路无异,很适合手工制作。

旋转屏仿真原理图

原理图为:单片机最小系统-供电电路、复位电路、振荡电路,16个发光二极管接到P1口和P3口,霍尔接到P2.0。单片机P1、P3为推挽输出,驱动发光二极管;P2.0为上拉输入,用来测速。

旋转屏实物图

但由于整个电路板处于高速旋转状态,所以我们首先要解决两问题:一是如何给运动的系统供电、二是如何保证显示信息稳定显示;给运动的系统供电,常用的供电方式有三种:电池供电、电刷供电、无线感应供电。

旋转屏拆分图

高速旋转电机选用电磁炉散热风扇电机,这个电机转速快、稳定、静音、控制简单。

旋转电机——电磁炉散热风扇

搭好硬件电路,就可以搞软件了,软件定义P2.0为霍尔输入。

霍尔检测I/O定义P2.0

由霍尔测得速度来调整扫描速度,到达显示宽度不变的效果,霍尔也起到检测起点的作用,当检测到起点是发光二极管就开始扫描,这样就能在固定位置显示内容。下面的延时函数是用来控制扫描速度的,在电机转速不变的情况下,延时越大字体越宽,反之亦然。

很简单的程序

字体取模时要根据电路连接、发光二极管的布置选择取模方式:横向取模、纵向取模、字节倒序等一系列选项。

字体取模软件——取模

把取模好的数据表导入程序,编译,烧录就完成了。就是这么简单!!!

字体的数据

经验分享:确定二极管位置时,电机轴心位置到板子最下边缘的距离不要超过电机轴心到最近一个发光二极管的距离,不然转起来板子会和发光二极管有重合,影响亮度;要用同种颜色发光二极管,那样效果会更好,显示内容清晰。二极管尽量离电机轴心远一些,可以做成发光二极管在两侧,竖着排放效果更好。要尽量降低成品重量,二极管什么的能用贴片的就用贴片。

旋转屏效果图

「正点原子STM32Mini板资料连载」第三十五章 汉字显示实验

1)实验平台:正点原子STM32mini开发板2)摘自《正点原子 STM32 不完全手册(HAL 库版) 关注官方微信号公众号,获取更多资料:正点原子

第三十五章 汉字显示实验

汉字显示在很多单片机系统都需要用到,少则几个字,多则整个汉字库的支持,更有甚者

还要支持多国字库,那就更麻烦了。本章,我们将向大家介绍,如何用 STM32 控制 LCD 显示

汉字。在本章中,我们将使用外部 FLASH 来存储字库,并可以通过 SD 卡更新字库。STM32

读取存在 FLASH 里面的字库,然后将汉字显示在 LCD 上面。本章分为如下几个部分:

35.1 汉字显示原理简介

35.2 硬件设计

35.3 软件设计

35.4 下载验证

35.1 汉字显示原理简介

常用的汉字内码系统有 GB2312,GB13000,GBK,BIG5(繁体)等几种,其中 GB2312

支持的汉字仅有几千个,很多时候不够用,而 GBK 内码不仅完全兼容 GB2312,还支持了繁体

字,总汉字数有 2 万多个,完全能满足我们一般应用的要求。

本实例我们将制作三个 GBK 字库,制作好的字库放在 SD 卡里面,然后通过 SD 卡,将字

库文件复制到外部 FLASH 芯片 W25Q64 里,这样,W25Q64 就相当于一个汉字字库芯片了。

汉字在液晶上的显示原理与前面显示字符的是一样的。汉字在液晶上的显示其实就是一些

点的显示与不显示,这就相当于我们的笔一样,有笔经过的地方就画出来,没经过的地方就不

画。所以要显示汉字,我们首先要知道汉字的点阵数据,这些数据可以由专门的软件来生成。

只要知道了一个汉字点阵的生成方法,那么我们在程序里面就可以把这个点阵数据解析成一个

汉字。

知道显示了一个汉字,就可以推及整个汉字库了。汉字在各种文件里面的存储不是以点阵

数据的形式存储的(否则那占用的空间就太大了),而是以内码的形式存储的,就是

GB2312/GBK/BIG5 等这几种的一种,每个汉字对应着一个内码,在知道了内码之后再去字库

里面查找这个汉字的点阵数据,然后在液晶上显示出来。这个过程我们是看不到,但是计算机

是要去执行的。

单片机要显示汉字也与此类似:汉字内码(GBK/GB2312)→查找点阵库→解析→显示。

所以只要我们有了整个汉字库的点阵,就可以把电脑上的文本信息在单片机上显示出来了。

这里我们要解决的最大问题就是制作一个与汉字内码对得上号的汉字点阵库。而且要方便单片

机的查找。每个 GBK 码由 2 个字节组成,第一个字节为 0X81~0XFE,第二个字节分为两部分,

一是 0X40~0X7E,二是 0X80~0XFE。其中与 GB2312 相同的区域,字完全相同。

我们把第一个字节代表的意义称为区,那么 GBK 里面总共有 126 个区(0XFE-0X81+1),

每个区内有 190 个汉字(0XFE-0X80+0X7E-0X40+2),总共就有 126*190=23940 个汉字。我

们的点阵库只要按照这个编码规则从 0X8140 开始,逐一建立,每个区的点阵大小为每个汉字

所用的字节数*190。这样,我们就可以得到在这个字库里面定位汉字的方法:

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

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

其中 GBKH、GBKL 分别代表 GBK 的第一个字节和第二个字节(也就是高位和低位),Hp

为对应汉字点阵数据在字库里面的起始地址(假设是从 0 开始存放),csize 代表一个汉字点阵所

占的字节数。假定采用与 15.3 节 ASCII 字库一样的提取方法(从上到下,从左到右),可以得

出字体大小与点阵所占字节数的对应关系为:

csize=(size/8+((size%8)?1:0))*size;

size 为字体大小,比如 12(12*12)、16(16*16)、24(24*24)等。

这样我们只要得到了汉字的 GBK 码,就可以得到该汉字点阵在点阵库里面的位置,从而

获取其点阵数据,显示这个汉字了。

上一章,我们提到要用 cc936.c,以支持长文件名,但是 cc936.c 文件里面的两个数组太大

了(172KB),直接刷在单片机里面,太占用 flash 了,所以我们必须把这两个数组存放在外部

flash。cc936 里面包含的两个数组 oem2uni 和 uni2oem 存放 unicode 和 gbk 的互相转换对照表,

这两个数组很大,这里我们利用 ALIENTEK 提供的一个 C 语言数组转 BIN(二进制)的软件:

C2B 转换助手 V1.1.exe,将这两个数组转为 BIN 文件,我们将这两个数组拷贝出来存放为一个

新的文本文件,假设为 UNIGBK.TXT,然后用 C2B 转换助手打开这个文本文件,如图 35.1.1

所示:

图 35.1.1 C2B 转换助手

然后点击转换,就可以在当前目录下(文本文件所在目录下)得到一个 UNIGBK.bin 的文

件。这样就完成将 C 语言数组转换为.bin 文件,然后只需要将 UNIGBK.bin 保存到外部 FLASH

就实现了该数组的转移。

在 cc936.c 里面,主要是通过 ff_convert 调用这两个数组,实现 UNICODE 和 GBK 的互转,

该函数原代码如下:

WCHAR ff_convert ( /* Converted code, 0 means conversion error */

WCHAR src, /* Character code to be converted */

UINT

dir

/* 0: Unicode to OEMCP, 1: OEMCP to Unicode */

)

{

const WCHAR *p;

WCHAR c;

int i, n, li, hi;

if (src < 0x80) {

/* ASCII */

c = src;

} else {

if (dir) {

/* OEMCP to unicode */

p = oem2uni;

hi = sizeof(oem2uni) / 4 - 1;

} else {

/* Unicode to OEMCP */

p = uni2oem;

hi = sizeof(uni2oem) / 4 - 1;

}

li = 0;

for (n = 16; n; n--) {

i = li + (hi - li) / 2;

if (src == p[i * 2]) break;

if (src > p[i * 2]) li = i;

else hi = i;

}

c = n ? p[i * 2 + 1] : 0;

}

return c;

}

此段代码,通过二分法(16 阶)在数组里面查找 UNICODE(或 GBK)码对应的 GBK(或

UNICODE)码。当我们将数组存放在外部 flash 的时候,将该函数修改为:

WCHAR ff_convert ( /* Converted code, 0 means conversion error */

WCHAR src,

/* Character code to be converted */

UINT

dir

/* 0: Unicode to OEMCP, 1: OEMCP to Unicode */

)

{

WCHAR t[2];

WCHAR c;

u32 i, li, hi;

u16 n;

u32 gbk2uni_offset=0;

if (src < 0x80)c = src;//ASCII,直接不用转换.

else

{

if(dir) gbk2uni_offset=ftinfo.ugbksize/2;

//GBK 2 UNICODE

else gbk2uni_offset=0;

//UNICODE 2 GBK

/* Unicode to OEMCP */

hi=ftinfo.ugbksize/2;//对半开.

hi =hi / 4 - 1;

li = 0;

for (n = 16; n; n--)

{

i = li + (hi - li) / 2;

SPI_Flash_Read((u8*)&t,ftinfo.ugbkaddr+i*4+gbk2uni_offset,4);//读出 4 个字节

if (src == t[0]) break;

if (src > t[0])li = i;

else hi = i;

}

c = n ? t[1] : 0;

}

return c;

}

代码中的 ftinfo.ugbksize 为我们刚刚生成的 UNIGBK.bin 的大小,而 ftinfo.ugbkaddr 是我们

存放 UNIGBK.bin 文件的首地址。这里同样采用的是二分法查找,关于 cc936.c 的修改,我们就

介绍到这。

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

软件可以在 WINDOWS 系统下生成任意点阵大小的 ASCII,GB2312(简体中文)、GBK(简体中

文)、BIG5(繁体中文)、HANGUL(韩文)、SJIS(日文)、Unicode 以及泰文,越南文、俄文、乌克

兰文,拉丁文,8859 系列等共二十几种编码的字库,不但支持生成二进制文件格式的文件,也

可以生成 BDF 文件,还支持生成图片功能,并支持横向,纵向等多种扫描方式,且扫描方式

可以根据用户的需求进行增加。该软件的界面如图 35.1.1 所示:

图 35.1.2 点阵字库生成器默认界面

本章,我们总共要生成 3 个字库:12*12 字库、16*16 字库和 24*24 字库。这里以 16*16

字库为例进行介绍,其他两个字库的制作方法类似。

要生成 16*16 的 GBK 字库,则选择:936 中文 PRC GBK,字宽和高均选择 16,字体大小

选择 12,然后模式选择纵向取模方式二(字节高位在前,低位在后),最后点击创建,就可以

开始生成我们需要的字库了(.DZK 文件)。具体设置如图 35.1.3 所示:

图 35.1.3 生成 GBK16*16 字库的设置方法

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

fsize=dsize*6/8

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

对应的是 12 字体。

生成完以后,我们把文件名和后缀改成:GBK16.FON。同样的方法,生成 12*12 的点阵库

(GBK12.FON)和 24*24 的点阵库(GBK24.FON),总共制作 3 个字库。

另外,该软件还可以生成其他很多字库,字体也可选,大家可以根据自己的需要按照上面

的方法生成即可。该软件的详细介绍请看软件自带的《点阵字库生成器说明书》,关于汉字显示

原理,我们就介绍到这。

35.2 硬件设计

本章实验功能简介:开机的时候先检测 W25Q64 中是否已经存在字库,如果存在,则按次

序显示汉字(两种字体都显示)。如果没有,则检测 SD 卡和文件系统,并查找 SYSTEM 文件夹

下的 FONT 文件夹,在该文件夹内查找 UNIGBK.BIN、GBK12.FON、GBK16.FON 和

GBK24.FON(这几个文件的由来,我们前面已经介绍了)。在检测到这些文件之后,就开始

更新字库,更新完毕才开始显示汉字。通过按按键 KEY0,可以强制更新字库。同样我们也是

用 DS0 来指示程序正在运行。

所要用到的硬件资源如下:

1) 指示灯 DS0

2) KEY0 按键

3) 串口

4) TFTLCD 模块

5) SD 卡

6) SPI FLASH

这几部分分,在之前的实例中都介绍过了,我们在此就不介绍了。

35.3 软件设计

打开上一章的工程,首先在 HARDWARE 文件夹所在的文件夹下新建一个 TEXT 的文件夹。

在 TEXT 文件夹下新建 fontupd.c、fontupd.h、text.c、text.h 这 4 个文件。并将该文件夹加入头

文件包含路径。

打开 fontupd.c,在该文件内输入如下代码:

//字库区域占用的总扇区数大小(3 个字库+unigbk 表+字库信息=3238700 字节,

//约占 791 个 W25QXX 扇区)

#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;

//字库存放在 sd 卡中的路径

const u8 *GBK24_PATH="0:/SYSTEM/FONT/GBK24.FON"; //GBK24 的存放位置

const u8 *GBK16_PATH="0:/SYSTEM/FONT/GBK16.FON"; //GBK16 的存放位置

const u8 *GBK12_PATH="0:/SYSTEM/FONT/GBK12.FON"; //GBK12 的存放位置

const u8 *UNIGBK_PATH="0:/SYSTEM/FONT/UNIGBK.BIN";//UNIGBK.BIN 的存放位置

//显示当前字体更新进度

//x,y:坐标

//size:字体大小

//fsize:整个文件大小

//pos:当前文件指针位置

u32 fupd_prog(u16 x,u16 y,u8 size,u32 fsize,u32 pos)

{

float prog; u8 t=0XFF;

prog=(float)pos/fsize;

prog*=100;

if(t!=prog)

{

LCD_ShowString(x+3*size/2,y,240,320,size,"%");

t=prog;

if(t>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; u16 bread; u32 offx=0;

FIL * fftemp;

u8 *tempbuf; u8 res; u8 rval=0;

fftemp=(FIL*)mymalloc(sizeof(FIL)); //分配内存

if(fftemp==NULL)rval=1;

tempbuf=mymalloc(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)

{

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; //GBK12 字库地址

ftinfo.gbk12size=fftemp->fsize;

//GBK12 字库大小

flashaddr=ftinfo.f12addr;

//GBK12 的起始地址

break;

case 2:

ftinfo.f16addr=ftinfo.f12addr+ftinfo.gbk12size; // GBK16 字库地址

ftinfo.gbk16size=fftemp->fsize;

//GBK16 字库大小

flashaddr=ftinfo.f16addr;

//GBK16 的起始地址

break;

case 3:

ftinfo.f24addr=ftinfo.f16addr+ftinfo.gbk16size; // 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;

//执行错误

SPI_Flash_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(fftemp);

//释放内存

myfree(tempbuf); //释放内存

return res;

}

//更新字体文件,UNIGBK,GBK12,GBK16,GBK24 一起更新

//x,y:提示信息的显示地址

//size:字体大小

//提示信息字体大小

//返回值:0,更新成功;

//

其他,错误代码.

u8 update_font(u16 x,u16 y,u8 size)

{

u8 *gbk24_path=(u8*)GBK24_PATH;

u8 *gbk16_path=(u8*)GBK16_PATH;

u8 *gbk12_path=(u8*)GBK12_PATH;

u8 *unigbk_path=(u8*)UNIGBK_PATH;

u8 res;

res=0XFF;

ftinfo.fontok=0XFF;

SPI_Flash_Write((u8*)&ftinfo,FONTINFOADDR,sizeof(ftinfo));

//清除之前字库成功的标志.防止更新到一半重启,导致的字库部分数据丢失.

SPI_Flash_Read((u8*)&ftinfo,FONTINFOADDR,sizeof(ftinfo));

//重新读出 ftinfo 结构体数据

LCD_ShowString(x,y,240,320,size,"Updating UNIGBK.BIN");

res=updata_fontx(x+20*size/2,y,size,unigbk_path,0);

//更新 UNIGBK.BIN

if(res)return 1;

LCD_ShowString(x,y,240,320,size,"Updating GBK12.BIN ");

res=updata_fontx(x+20*size/2,y,size,gbk12_path,1);

//更新 GBK12.FON

if(res)return 2;

LCD_ShowString(x,y,240,320,size,"Updating GBK16.BIN ");

res=updata_fontx(x+20*size/2,y,size,gbk16_path,2);

//更新 GBK16.FON

if(res)return 3;

LCD_ShowString(x,y,240,320,size,"Updating GBK24.BIN ");

res=updata_fontx(x+20*size/2,y,size,gbk24_path,3);

//更新 GBK24.FON

if(res)return 4;

ftinfo.fontok=0XAA; //全部更新好了

SPI_Flash_Write((u8*)&ftinfo,FONTINFOADDR,sizeof(ftinfo));

//保存字库信息

return 0;//无错误.

}

//初始化字体

//返回值:0,字库完好.

//

其他,字库丢失

u8 font_init(void)

{

SPI_Flash_Init();

SPI_Flash_Read((u8*)&ftinfo,FONTINFOADDR,sizeof(ftinfo));//读出 ftinfo 结构体数据

if(ftinfo.fontok!=0XAA)return 1;

//字库错误.

return 0;

}

此部分代码主要用于字库的更新操作(包含 UNIGBK 的转换码表更新),其中 ftinfo 是我

们在 fontupd.h 里面定义的一个结构体,用于记录字库首地址及字库大小等信息。因为我们将

W25Q64 的前 4.8M 字节给 FATFS 管理(用做本地磁盘),然后又预留了 100K 字节给用户自己

使用,最后的 3.1M 字节(W25Q64 总共 8M 字节),才是 UNIGBK 码表和字库的存储空间,所

以,我们的存储地址是从(4916+100)*1024 处开始的。最开始的 33 个字节给 ftinfo 用,用于保

存 ftinfo 结构体数据,之后依次是:UNIGBK.BIN、GBK12.FON、GBK16.FON 和 GBK24.FON。

保存该部分代码,并在工程里面新建一个 TEXT 的组,把 fontupd.c 加入到这个组里面,然

后打开 fontupd.h 在该文件里面输入如下代码:

#ifndef __FONTUPD_H__

#define __FONTUPD_H__

#include <stm32f10x.h>

//前面 4.8M 被 fatfs 占用了.

//4.8M 以后紧跟的 100K 字节,用户可以随便用.

//4.8M+100K 字节以后的字节,被字库占用了,不能动!

//字体信息保存地址,占 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 font_init(void);

#endif

这里,我们可以看到 ftinfo 的结构体定义,总共占用 25 个字节,第一个字节用来标识字库

是否 OK,其他的用来记录地址和文件大小。保存此部分代码,然后打开 text.c 文件,在该文件

里面输入如下代码:

#include "sys.h"

#include "fontupd.h"

#include "flash.h"

#include "lcd.h"

#include "text.h"

#include "string.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)//非 常用汉字

{

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; //得到字库中的字节偏移量

switch(size)

{

case 12:SPI_Flash_Read(mat,foffset+ftinfo.f12addr,24);break;

case 16:SPI_Flash_Read(mat,foffset+ftinfo.f16addr,32);break;

case 24:SPI_Flash_Read(mat,foffset+ftinfo.f24addr,72);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)

{

……此处代码省略

}

//在指定宽度的中间显示字符串

//如果字符长度超过了 len,则用 Show_Str 显示

//len:指定要显示的宽度

void Show_Str_Mid(u16 x,u16 y,u8*str,u8 size,u8 len)

{

……//此处代码省略

}

此部分代码总共有 4 个函数,我们省略了两个函数(Show_Str_Mid 和 Show_Str)的代码,

另外两个函数,Get_HzMat 函数用于获取 GBK 码对应的汉字字库,通过我们 35.1 节介绍的办

法,在外部 flash 查找字库,然后返回对应的字库点阵。Show_Font 函数用于在指定地址显示一

个指定大小的汉字,采用的方法和 LCD_ShowChar 所采用的方法一样,都是画点显示,这里就

不细说了。保存此部分代码,并把 text.c 文件加入 TEXT 组下。text.h 里面都是一些函数申明,

这里我们就不贴出来了,详见光盘本例程源码。

前面提到我们队 cc936.c 文件做了修改,我们将其命名为 mycc936.c,并保存在 exfuns 文件

夹下,将工程 FATFS 组下的 cc936.c 删除,然后重新添加 mycc936.c 到 FATFS 组下,mycc936.c

的源码就不贴出来了,其实就是在 cc936.c 的基础上去掉了两个大数组,然后对 ff_convert 进行

了修改,详见光盘本例程源码。

最后,我们在 main.c 里面修改 main 函数如下:

int main(void)

{

u32 fontcnt;

u8 i,j,key,t;

u8 fontx[2];//gbk 码

HAL_Init();

//初始化 HAL 库

Stm32_Clock_Init(RCC_PLL_MUL9); //设置时钟,72M

delay_init(72);

//初始化延时函数

uart_init(115200);

//初始化串口

usmart_dev.init(84);

//初始化 USMART

LED_Init();

//初始化 LED

KEY_Init();

//初始化按键

LCD_Init();

//初始化 LCD

mem_init();

//初始化内存池

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(60,50,200,16,16,"Mini STM32");

while(SD_Initialize())

//检测 SD 卡

{

LCD_ShowString(60,70,200,16,16,"SD Card Failed!");

delay_ms(200);

LCD_Fill(60,70,200+60,70+16,WHITE);

delay_ms(200);

}

LCD_ShowString(60,70,200,16,16,"SD Card OK");

LCD_ShowString(60,90,200,16,16,"Font Updating...");

key=update_font(20,110,16);//更新字库

while(key)//更新失败

{

LCD_ShowString(60,110,200,16,16,"Font Update Failed!"); delay_ms(200);

LCD_Fill(20,110,200+20,110+16,WHITE); delay_ms(200);

}

LCD_ShowString(60,110,200,16,16,"Font Update Success!");

delay_ms(1500);

LCD_Clear(WHITE);//清屏

}

POINT_COLOR=RED;

Show_Str(30,50,200,16,"Mini STM32 开发板",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,"2019 年 11 月 18 日",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++)

{

fontx[0]=i;

LCD_ShowNum(148,150,i,3,16);

//显示内码高字节

for(j=0x40;j<0xfe;j++)

{

if(j==0x7f)continue;

fontcnt++;

LCD_ShowNum(148,170,j,3,16); //显示内码低字节

LCD_ShowNum(148,190,fontcnt,5,16);//汉字计数显示

fontx[1]=j;

Show_Font(60+132,220,fontx,24,0);

Show_Font(60+144,244,fontx,16,0);

Show_Font(60+108,260,fontx,12,0);

t=200;

while(t--)//延时,同时扫描按键

{

delay_ms(1);

key=KEY_Scan(0);

if(key==KEY0_PRES)goto UPD;

}

LED0=!LED0;

}

}

}

}

此部分代码就实现了我们在硬件描述部分所描述的功能,至此整个软件设计就完成了

35.4 下载验证

本例程支持 12*12、16*16 和 24*24 等三种字体的显示,在代码编译成功之后,我们通过下

载代码到 ALIENTEK MiniSTM32 开发板上,可以看到 LCD 开始显示三种大小的汉字及汉字内

码,如图 35.4.1 所示:

图 35.4.1 汉字显示实验显示效果

一开始就显示汉字,是因为 ALIENTEK MiniSTM32 开发板在出厂的时候都是测试过的,

里面刷了综合测试程序,已经把字库写入到了 W25Q64 里面,所以并不会提示更新字库。如果

你想要更新字库,那么则必须先找一张 SD 卡,把:光盘\5,SD 卡根目录文件 文件夹下面的

SYSTEM 文件夹拷贝到 SD 卡根目录下,插入开发板,并按复位,之后,在显示汉字的时候,

按下 KEY0,就可以开始更新字库了。字库更新界面如图 35.4.2 所示:

图 35.4.2 汉字字库更新界面

我们还可以通过 USMART 来测试该实验,我们可以通过 USMART 调用 Show_Str 函数,

来实现任意位置显示任何字符串,有兴趣的朋友可以测试一下。

相关问答

单片机 点阵式 led显示屏 显示汉字 是镜像怎么处理-ZOL问答

显示部分需要编常用字字库,内部ram不够得扩展(显示缓冲,20个16*16的汉字最少要640字节,加上滚屏等还要加)另外两部分我没做过,帮不了你,希望对你有所帮助我感...

32 单片机 怎么 显示 中文?

不管何种单片机显示中文文字,首先要看液晶屏是否自带字库如果带字库只管送中文文字的Uinicode编码就行(编译软件会自选查找)如果不带字库,就要制作或复制...

单片机 如何识别串口的数据是中文还是字母?

串口有两种通讯模式,一种是十六进制模式,一种是文本模式。选择文本模式就可以发送中文。其实文本模式发送的ASCII码,接收时再转换回相应字符(包括中文)。串口...

求一篇 单片机 方面的外文文献和中文翻译,谢谢大侠们了!_作业帮

[最佳回答]我的百度文库里有很多单片机方面的英文文献带翻译的的大多符合你的要求,你可以去挑选下!

新琦数码中文编程适合 单片机 吗?

新琦数码中文编程不适合单片机新琦数码中文编程软件,采用先进的“图示化和全中文”的编程语言,它使得软件编程变的异常简单和不可思议的高效。但除了汇编和C...

如何让尽量少的 单片机 引脚控制多个 LED

[最佳回答]建议楼主找找LED汉字显示屏的电路,你的问题就解决了。

ti是什么意思 单片机 ?

1."ti"是单片机的意思。2.单片机是一种集成电路芯片,具有微处理器、存储器和各种输入输出接口等功能,可以独立完成特定任务的计算机系统。它通常用于控制和...

单片机 中8*8点阵中 汉字 的编码是怎么获得的?

8*8造汉字很困难的,你可以找一个8*8的汉字库(如果有的话),用字模提取软件(网上很多的)获取点阵字库数据,之后写入这个8*8点阵就OK了。8*8造汉字很困难的,你可...

疯壳AI开源无人机GPIO( LED 航情灯、信号灯控制) - OSCHINA - ...

LEDLED简介LED(LightEmittingDiode)即为发光二极管的缩写。LED是一种在生活中非常常见的照明发光器件。LED的在我们生活中形态种类非常的多,如下图所示。...

串口 显示汉字 找不到是为啥?

串口显示汉字时未找到的原因可能是:串口发送数据的速度过快,导致显示设备无法跟上接收的速度。这可能导致接收方无法正确解析接收到的数据,从而无法找到指定...

猜你喜欢