单片机学习小组
直播中

吴凡

10年用户 915经验值
擅长:48674
私信 关注

如何实现任意汉字的液晶屏显示呢

液晶屏汉字显示的原理是什么?
如何实现任意汉字的显示?有哪些步骤?

回帖(1)

刘璐

2022-1-19 10:13:01
还记得通常在MCU驱动LCD,OLED是怎样显示汉字的吗?采用取字模工具,生成字模数组,然后要显示某个字符,直接索引这个字符的数组,然后对这个数组数据进行显示就行了,就像这样:

   
最近有开发一个物联网项目,将网络端的发过来的汉字显示到屏幕上,但问题来了,我还不知道网络端要发送具体哪些汉字,我是无法对具体的汉字取模,因此,得想办法先对所有汉字进行取模,或使用字库,于是就有了下文。
1 字符编码

先从字符编码说起:参考阮一峰博客字符编码



  • ASCII码:一共规定了128个字符的编码,这128个符号(包括32个不能打印出来的控制符号),只占用了一个字节的后面7位,最前面的一位统一规定为0。
  • 非ASCII码:英语用128个符号编码就够了,但是用来表示其他语言,128个符号是不够的。
  • GB2312,GBK,Unicode,UTF-8均为非ASCII编码
  • Unicode码:如果有一种编码,将世界上所有的符号都纳入其中。每一个符号都给予一个独一无二的编码,那么乱码问题就会消失。这就是 Unicode,就像它的名字都表示的,这是一种所有符号的编码。
  • 注意:Unicode 只是一个符号集,它只规定了符号的二进制代码,却没有规定这个二进制代码应该如何存储
  • UTF-8码:UTF-8 就是在互联网上使用最广的一种 Unicode 的实现方式,是Unicode 的实现方式之一。
  • GB2312,GBK:GB2312是常用汉字的专用编码,GBK是所有汉字的专用编码。
  • 注意:GB类的汉字编码与后文的 Unicode 和 UTF-8 是毫无关系的
2 液晶屏汉字显示原理




  • ASCII字符显示,由于ASCII字符仅128个,可以实现对每个字符一一取模,且占用MCU的RAM不会过大,此处不再介绍ASCII字符的显示。
  • 为什么显示英文一般不用字库芯片? 英文的单词都是由26个字母构成了,加上大小写的区别和其它一些字符,也不过才95个。假如要显示8 * 16像素大小的字符,每一个字符需要16个字节的字库空间,95个字符即是95 * 16=570个字节。即占用570个字节的RAM。对于小型MCU几K字节的RAM来说,绰绰有余了。
  • 为什么显示中文需要字库芯片? 显示中文的话,需要每一个字的字模,16*16像素大小的中文,每一个中文都要32个字节。GBK收录了中文两万多个,如果要都能显示,需要700多K字节的空间。所以,我们选择了把这些字库放在外部存储器当中,可选择2M的FLASH存储芯片W25Q16做为存储媒介。放个700多K的字库足够了,并且,同时放两种字体的字库都没问题。



  • 常规汉字显示,采用相关的汉字取字模工具(PCtoLCD2002完美版),取出字模,相关参数设置如下所示:

   
由于我采用的液晶屏是逐行显示的,不同的液晶屏采用不同的显示模式,需根据具体情况选择字模。 取出“你”字模后,将字模数据复制到notepad++,调整成设置好的16列,21行的样式,在将16进制数据转换成2进制数据,然后将0替换成空格,即可找出汉字“你”的原型。

   



  • 对于液晶屏而言,字符,图片的显示都是像素点的点阵显示,因此,只要将上述数组中的数据转换成点阵数据显示到液晶屏中即可,对于二进制位1的位数据,显示出该像素点,对于二进制位0的位数据,不显示该像素点,即可显示出该汉字(暂不考虑颜色显示)。
3 字符显示的实现

上面讲到了字符显示的原理,接下来将讲解字符显示的实现,相关代码不依赖于底层,具有很好的移植性,且在文章最后放出了Github链接,要实现该代码的功能,需要以下前提条件:



  • 已经在液晶屏上实现了画点功能,该函数将直接调用该功能
  • 液晶屏在显示时,逐行扫描,且需要高位在前(参考上一节相关参数设置,其它类型的屏幕可能有所不同)
注:由于代码过长,为不影响阅读,仅放部分关键代码,如有需求,更多请参考文末的Github
   for  
此代码简单地实现了索引字模数组中的一个unsigned char类型的元素中的8位,将该8位绘制成像素点,此为字符显示的基本实现,
4 任意汉字显示的实现(采用取模法)

本节将讲述如何实现任意汉字的显示。 需要准备以下工具:



  • GBK字库
  • 取模软件
  • 二进制文件生成工具 如下图所示,且相关工具可从文末Github中直接获取得:

   
具体操作步骤如下:



  • 将字库取模
在工具栏处点“打开”按钮,打开gbk_ziku.txt文件,然后根据自己的需要,设置想要的取模方式,然后点工具栏上的“输出”按钮 并等待其完成,完成后会在取模软件所在路径生成了一个temp.txt文件。现在我是按照“宋体、点阵数为16、字重为4、取模为为16*16、对齐设置为左上、方向设置为横向取模,高位在左”的方式来取的字模,也就是我平时TFT液晶屏常用的一种字模。

   
打开temp.txt文件可知, 该文件包含了所有字库的点阵,且采用GBK编码排序:

   



  • 将取模的文件生成二进制文件
将该temp.txt文件转换为二进制文件,供程序读取。在windows下进入CMD命令控制台,进入到相关文件所在的路径,接着,执行命令ziku.exe temp.txt命令,执行效果如下图所示,执行后将生成一个ziku.bin的文件:

   



  • 将二进制文件存入外部Flash,或Linux系统用户文件夹下: 得到ziku.bin文件后,可将该字库二进制文件存入外部Flash如W25Q16中(MCU),或者Linux系统用户文件夹下(Linux);其中,将该bin文件存放如外部Flash的方法为:
  • 单片机与电脑采用串口连接,单片机与W25Q16采用SPI方式连接。所以,我们只需要给单片机写一个接收串口数据再把数据通过SPI口传输到W25Q16中的程序即可。
  • 使用FT232H芯片工具,该工具实现了USB转SPI,USB转I2C的功能,可直接通过PC的USB接口下载到带I2C或SPI接口的Flash或E2PROM中。



  • 检验二进制文件的可用性
本文将只在Linux系统下进行检验,MCU环境下的检验同理:
该bin文件的检验程序如下所示(相关程序均已上传文章末尾的Github):
   #include  
将以上程序保存为:fontTest.c,gcc编译,执行,结果如下图所示:

   
  由执行结果可见,程序能够从二进制bin文件中提取有用信息,并且,该bin文件保存了GBK编码的所有汉字,将“你好”替换成其他任意汉字也是可从bin文件中提取出子模的,生成的bin文件是有效的。 接下来讲解程序中的几个注意事项:



  • 该程序源文件必须以GBK编码或GB2312编码保存(GBK编码兼容GB2312编码),在notepad++中,保存方式如下图所示:

   



  • 解释下如何获得内存中字符的偏移量: GBK编码:每个GBK码由2个字节组成:
   第一个字节为0X81~0XF: 第二个字节分为两部分:  
一是0X40-0X7E  
二是0X80-0XFE
例如汉字“瑞”的GBK编码为C8 F0,第一字节C8,位于0X81-0XFE之间,第二字节F0,位于0X40-0XFE之间
   第一个字节代表的意义称为区,那么GBK里面总共有126个区(0XFE-0X81+1=126) 第二个字节代表的意义就是每个区内有多少个汉字,算了一下,一共有190个(0XFE-0X80+0X7E-0X40+2=190)。
那么,GBK一共存储了126X190=23940个汉字。
   仔细看GBK编码第二个字节两部分中,0X40-0X7E和0X80-0XFE,也就是说它是从0X40~到0XFF,中间的0x7F和最后的0xFF没有用到。  
但是为了能够线性查找,我们暂且认为这两个字节也存在,就是我们强制把每个区190个汉字当做每个区192个汉字,不过0X7F和0XFF上没有汉字。
定义GBKH代表第一个字节,GBKL代表第二个字节,字库的偏移量offset,那么其计算方法如下:
   GBKH
实现了在控制台窗口打印汉字字模,那么在TFTLCD,OLED,墨水屏等屏幕上,只要先实现了画点功能,那么,显示任意汉字也可以实现的了。
5 任意汉字显示的实现(采用字库法)

最后再介绍一种显示汉字的方法,该方法采用Python实现,直接调用字体库,仅在Linux系统上可实现,下面将以树莓派为例,相关的操作如下:
首先安装好Pillow库以及必要液晶屏显示必要的SPI库,GPIO库等:

sudo apt-get install python3-pip
sudo apt-get install python-imaging
sudo pip3 install spidev
sudo pip3 install RPi.GPIO
sudo pip3 install Pillow

安装 Pillow 如果报错: ImportError: libopenjp2.so.7: cannot open shared object file: No such file or directory,则先执行如下指令:
   sudo apt-get install libopenjp2-7-dev
安装必要的字体:
   sudo apt-get install ttf-wqy-zenhei ttf-wqy-microhei
接下来是调用微软雅黑字体进行显示的部分示例(python3),详情代码参考文章末尾留下的
#!/usr/bin/python
# -*- coding:utf-8 -*-

import epd7in5b
import time
from PIL import Image,ImageDraw,ImageFont
import traceback

try:
    epd = epd7in5b.EPD()
    epd.init()
    print("Clear...")
    epd.Clear(0xFF)

    # Drawing on the Horizontal image
    HBlackimage = Image.new('1', (epd7in5b.EPD_WIDTH, epd7in5b.EPD_HEIGHT), 255)
    HRedimage = Image.new('1', (epd7in5b.EPD_WIDTH, epd7in5b.EPD_HEIGHT), 255)

    # Horizontal
    print("Drawing")
    drawblack = ImageDraw.Draw(HBlackimage)
    drawred = ImageDraw.Draw(HRedimage)
    font24 = ImageFont.truetype('/usr/share/fonts/truetype/wqy/wqy-microhei.ttc', 24)
    drawblack.text((10, 0), 'hello world', font = font24, fill = 0)
    drawblack.text((10, 20), '7.5inch e-Paper B', font = font24, fill = 0)
    drawblack.text((150, 0), u'微雪电子', font = font24, fill = 0)   
    drawblack.line((20, 50, 70, 100), fill = 0)
    drawblack.line((70, 50, 20, 100), fill = 0)
    drawblack.rectangle((20, 50, 70, 100), outline = 0)   
    drawred.line((165, 50, 165, 100), fill = 0)
    drawred.line((140, 75, 190, 75), fill = 0)
    drawred.arc((140, 50, 190, 100), 0, 360, fill = 0)
    drawred.rectangle((80, 50, 130, 100), fill = 0)
    drawred.chord((200, 50, 250, 100), 0, 360, fill = 0)
    epd.display(epd.getbuffer(HBlackimage), epd.getbuffer(HRedimage))
    epd.sleep()
except:
    print('traceback.format_exc():n%s',traceback.format_exc())
    exit()

该代码的精髓在于,先创建一段缓存,然后采用pillow库的相关方法通过字符串获取对应字库中字符的字模,将字模数据存入缓存中,然后将缓存数据显示到屏幕上,详情参考pillow库中的Image,ImageDraw,ImageFont方法对应的手册。

   注意,代码中的中文采用UTF-8编码,保存的时候,为防止乱码,必须以UTF-8编码保存(可采用notepad++工具保存为UTF-8编码)
举报

更多回帖

发帖
×
20
完善资料,
赚取积分