查看: 5156|回复: 5

[其他] 每天解决一个问题2 :MCU显示中文字库的方法

[复制链接]
  • TA的每日心情
    擦汗
    2021-9-9 22:51
  • 签到天数: 415 天

    连续签到: 1 天

    [LV.9]以坛为家II

    79

    主题

    3088

    帖子

    21

    金牌会员

    Rank: 6Rank: 6

    积分
    5181
    最后登录
    2022-5-23
    发表于 2015-8-26 21:00:45 | 显示全部楼层 |阅读模式
    本帖最后由 ALTIUM2 于 2015-8-26 21:07 编辑

    每天解决一个问题2 :MCU显示中文字库的方法

    -------------------------------------------------------------------------------------------------------------
      电子工程师在做电子产品的时候,经常会用到人机界面,而人机界面中我们常常需要显示中文汉字,以下我就为大伙整理一下常用的三种方法。


    ==预备知识
    汉字机内码,又称“汉字ASCII码”,简称“内码”,指计算机内部存储,处理加工和传输汉字时所用的由0和1符号组成的代码。输入码被接受后就由汉字操作系统的“输入码转换模块”转换为机内码,与所采用的键盘输入法无关。机内码是汉字最基本的编码,不管是什么汉字系统和汉字输入方法,输入的汉字外码到机器内部都要转换成机内码,才能被存储和进行各种处理。
    因为汉字处理系统要保证中西文的兼容,当系统中同时存在ASCII码和汉字国标码时,将会产生二义性。例如:有两个字节的内容为30H和21H,它既可表示汉字“啊”的国标码,又可表示西文“0”和“!”的ASCII码。为此,汉字机内码应对国标码加以适当处理和变换。

    国标码的机内码为二字节长的代码,它是在相应国标码的每个字节最高位上加“1”,即
    汉字机内码=汉字国标码+8080H
    例如,上述“啊”字的国标码是3021H,其汉字机内码则是B0A1H。
    汉字机内码的基础是汉字国标码。

    机内码:为了避免ASCII码和国标码同时使用时产生二义性问题,大部分汉字系统都采用将国标码每个字节高位置1作为汉字机内码。这样既解决了汉字机内码与西文机内码之间的二义性,又使汉字机内码与国标码具有极简单的对应关系。
    汉字机内码、国标码和区位码三者之间的关系为:区位码(十进制)的两个字节分别转换为十六进制后加2020H得到对应的国标码;机内码是汉字交换码(国标码)两个字节的最高位分别加1,即汉字交换码(国标码)的两个字节分别加80H得到对应的机内码;区位码(十进制)的两个字节分别转换为十六进制后加A0H得到对应的机内码。

    总的就是一个汉字对应一个机内码,在 IDE 中,汉字将会被“翻译”成唯一的机内码。


    -------------------------------------------------------------------------------------------------------------
    (一)按地址内部或者外扩FLASH存储标准字库中所取字模

    该方法的原理就是将一个标准的汉字库装入ROM存储器,再根据汉字的机内码在字库中寻址,找到对应的字模,提取后送到显示器显示。因为采用了和PC机相同的编码(机内码),软件的开发和维护非常简单,基本上与写PC机软件差不多。而对单片机系统自身的要求则相对高多了,16×16点阵的字库需要256K字节,但是一般8位单片机的寻址能力只有64K字节,要进行存储器扩充,除增加很大一部分硬件成本外,还因为要进行存储器分页管理、地址切换,显示速度明显受影响,而且只能显示一种点阵字体。

    实际举例1:

    void GUI_HZDispChar(U16 c)
    {
    …………

        if(c >= 0xa1a1)
        {

                temp = c;
                qh = (temp>>8)&0xff;
                
                wh = temp&0xff;
                
                c0 = 94*(qh-0xB0)+wh-0xA1 + 95;
                
         }
         else
         {
                 c0 = c - 0x20;
                 XDist = XDist/2;
                
         }
    …………

    }

    核心:首先判断是否是汉字通,然后根据机内码找到你存在FLASH中字库位置的字模数据,最后提取显示出来

    -------------------------------------------------------------------------------------------------------------
    (二)直接固化显示字模,按地址选取

    该方法的将要显示的语句中全部汉字的字模数据依次提取出来,顺序存放在存储器中,当显示时,直接取出字模数据送至显示器即可。这种方法占用空间少,程序实现简单,显示速度快;但是字模数据的提取和存储安排是一件委有繁琐的事件,要想大量显示汉字或进行程序修改几乎是不可能的,软件的可维护性很差。

    实际举例2://先定义所用字模库
      code uint8 shi[9][28] =
      {
      /* 业 */
      0x08,0x80,0x08,0x80,0x08,0x80,0x88,0x88,0x48,0x88,0x48,0x90,0x28,0x90,0x28,0xA0,
      0x28,0xA0,0x08,0x80,0x08,0x80,0x08,0x80,0xFF,0xF8,0x00,0x00,

      /* 务 */
      0x10,0x00,0x1F,0xE0,0x30,0x40,0x4D,0x80,0x82,0x00,0x0D,0x80,0x70,0x78,0x84,0x10,
      0x3F,0xC0,0x04,0x40,0x08,0x40,0x10,0x40,0x61,0x80,0x00,0x00,
      ……
      };


    //再取所用字模
    memcpy(g_uca_HZFont14,shi[0],28);
    //最后调用显示函数即可

    -------------------------------------------------------------------------------------------------------------(三)建立带索引的小字库[3]

    将全部要显示的汉字统一建成一个小字库,字库分为2部分:索引素和字模表。索引表由若干定长记录组成,记录的内容为:汉字机内码、地址码、识别码。其中地址码是该汉字字模在字模表中的位置,识别码标志该汉字的点阵形式或字体等。字模表中按素引存放汉字字模。显示汉字时先根据待显汉字的机内码在索引表中寻找,找到对应索引记录后,读出地址码和识别码,再根据此从字模表中读出字模,送显即可。这种方法可根据实际使用对字库进行裁剪,硬件开销较小,但是要进行复杂的查询运算,字多了平均寻找时间就会变长,效率降低。
    实际举例3:
    // ------------------  汉字字模的数据结构定义 ------------------------ //
    struct  typFNT_GB16                 // 汉字字模数据结构
    {
           unsigned char  Index[3];               // 汉字内码索引        
           unsigned char   Msk[32];                        // 点阵码数据
    };


    /////////////////////////////////////////////////////////////////////////
    // 汉字字模表                                                          //
    // 汉字库: 宋体16.dot,横向取模左高位,数据排列:从左到右从上到下           //

    /////////////////////////////////////////////////////////////////////////

    code struct  typFNT_GB16 codeGB_16[] =          // 数据表
    {

    /*--  文字:  上  --*/
    /*--  宋体12;  此字体下对应的点阵为:宽x高=16x16   --*/
    "上",0x00,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0xF8,0x01,0x00,
    0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x04,0x7F,0xFE,0x00,0x00,

    /*--  文字:  海  --*/
    /*--  宋体12;  此字体下对应的点阵为:宽x高=16x16   --*/
    "海",0x21,0x00,0x11,0x00,0x11,0xFE,0x02,0x00,0x97,0xF8,0x52,0x88,0x52,0x48,0x12,0x08,
    0x2F,0xFE,0x22,0x88,0xE2,0x48,0x22,0x08,0x23,0xFE,0x20,0x08,0x20,0x28,0x20,0x10,

    …………
    };


    //调用函数+索引
    void PutGB1616(unsigned short x, unsigned short  y, unsigned char c[2], unsigned int fColor,unsigned int bColor)
    {
            unsigned int i,j,k;
            LCD_SetPos(x,  x+16-1,y, y+16-1);
            for (k=0;k<64;k++)
            { //64标示自建汉字库中的个数,循环查询内码
                    if ((codeGB_16[k].Index[0]==c[0])&&(codeGB_16[k].Index[1]==c[1]))
                    {
                        for(i=0;i<32;i++)
                            {
                              unsigned short m=codeGB_16[k].Msk;
                                    for(j=0;j<8;j++)
                                    {
                                            if((m&0x80)==0x80)
                                            {
                                                    Write_Data_U16(fColor);
                                            }
                                            else
                                            {
                                                    Write_Data_U16(bColor);
                                            }
                                    m<<=1;
                                    }
                            }
                    }  
            }        
    }

    -------------------------------------------------------------------------------------------------------------
    (四)个人总结:
      方法一:
      标准字库使用确实非常方便快捷,维护简单,但是对MCU资源有要求或者需要外扩FLASH,成本提高了,而且最重要的如果外扩时,烧录字库很不方便。

      方法二:
      固定的字库数组虽然可以减少MCU 的flash需求,但是编写程序寻找字模非常不便,维护困难,可以说是一个吃力不讨好的辛苦活。

      方法三:
      索引的方法寻找字模,结合一二两种方法的优点,即降低才成本而且维护也较方便,但是在大量字模时,会影响MCU速度。

      通过这三种方法的对比,找到你最需的方法了吗?


    ALTIUM2.JPG













    我知道答案 目前已有5人回答

    评分

    参与人数 1NXP金币 +30 收起 理由
    小七 + 30 打赏!

    查看全部评分

    该会员没有填写今日想说内容.
    回复

    使用道具 举报

  • TA的每日心情
    开心
    2017-1-24 09:50
  • 签到天数: 2 天

    连续签到: 1 天

    [LV.1]初来乍到

    654

    主题

    3262

    帖子

    0

    金牌会员

    Rank: 6Rank: 6

    积分
    13179
    最后登录
    2019-1-27
    发表于 2015-8-26 21:18:50 | 显示全部楼层
    真的很拼了!
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    擦汗
    2021-9-9 22:51
  • 签到天数: 415 天

    连续签到: 1 天

    [LV.9]以坛为家II

    79

    主题

    3088

    帖子

    21

    金牌会员

    Rank: 6Rank: 6

    积分
    5181
    最后登录
    2022-5-23
     楼主| 发表于 2015-8-26 22:52:42 | 显示全部楼层

    呵呵,算是一种温故而知新把
    该会员没有填写今日想说内容.
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    开心
    2017-1-24 09:50
  • 签到天数: 2 天

    连续签到: 1 天

    [LV.1]初来乍到

    654

    主题

    3262

    帖子

    0

    金牌会员

    Rank: 6Rank: 6

    积分
    13179
    最后登录
    2019-1-27
    发表于 2015-8-27 08:52:47 | 显示全部楼层
    ALTIUM2 发表于 2015-8-26 22:52
    呵呵,算是一种温故而知新把

    恩,那也可以了
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    开心
    2017-2-9 14:16
  • 签到天数: 17 天

    连续签到: 1 天

    [LV.4]偶尔看看III

    25

    主题

    1785

    帖子

    0

    金牌会员

    Rank: 6Rank: 6

    积分
    2250
    最后登录
    2024-6-11
    发表于 2015-8-27 16:43:59 | 显示全部楼层
    谢谢楼主分享
    freescaleic.org.png
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    擦汗
    2021-9-9 22:51
  • 签到天数: 415 天

    连续签到: 1 天

    [LV.9]以坛为家II

    79

    主题

    3088

    帖子

    21

    金牌会员

    Rank: 6Rank: 6

    积分
    5181
    最后登录
    2022-5-23
     楼主| 发表于 2015-8-28 22:27:32 | 显示全部楼层

    咦,我又看到你了,谢谢支持啊
    该会员没有填写今日想说内容.
    回复 支持 反对

    使用道具 举报

    您需要登录后才可以回帖 注册/登录

    本版积分规则

    关闭

    站长推荐上一条 /3 下一条

    Archiver|手机版|小黑屋|恩智浦技术社区

    GMT+8, 2025-8-10 14:58 , Processed in 0.103830 second(s), 28 queries , MemCache On.

    Powered by Discuz! X3.4

    Copyright © 2001-2024, Tencent Cloud.

    快速回复 返回顶部 返回列表