打印
[资料分享与下载]

MCU显示中文字库的方法

[复制链接]
1710|6
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
Luis德华|  楼主 | 2015-8-27 09:19 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 Luis德华 于 2015-8-27 09:23 编辑

==预备知识
汉字机内码,又称“汉字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;
            
     }
…………
}


相关帖子

沙发
Luis德华|  楼主 | 2015-8-27 09:20 | 只看该作者
核心:首先判断是否是汉字通,然后根据机内码找到你存在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);
//最后调用显示函数即可

使用特权

评论回复
板凳
Luis德华|  楼主 | 2015-8-27 09:20 | 只看该作者
三)建立带索引的小字库[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;
                                }
                        }
                }  
        }        
}

使用特权

评论回复
地板
Luis德华|  楼主 | 2015-8-27 09:21 | 只看该作者
(四)个人总结:
  方法一:
  标准字库使用确实非常方便快捷,维护简单,但是对MCU资源有要求或者需要外扩FLASH,成本提高了,而且最重要的如果外扩时,烧录字库很不方便。

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

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

使用特权

评论回复
5
IversonCar| | 2015-8-27 14:45 | 只看该作者
按地址内部或者外扩FLASH存储标准字库中所取字模,这个方法最可行了

使用特权

评论回复
6
yiyigirl2014| | 2015-8-27 17:12 | 只看该作者
我是这样想的,如果需要显示,肯定需要调取点阵字库,字库根据字符编码存放到一个指定的数组文件里。

使用特权

评论回复
7
天灵灵地灵灵| | 2015-8-27 19:55 | 只看该作者
直接通过函数调取闪存文件里的字库对应的点阵。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

40

主题

370

帖子

4

粉丝