【连载】【FPGA黑金开发板】NIOSII那些事儿LCD中英文字符显示(二十三)

声明:本文为原创作品,版权归本博文作者所有,如需转载,请注明出处http://www.cnblogs.com/kingst/

3

      这一节,我们在上一节的基础上,来研究英文变宽字体以及中文的字体显示的解决方案。

      在LCD上显示字符,不管是英文还是中文,都需要有字库支持。在有些LCD模块中,已经将字库文件烧写到了芯片当中,这样的字库有优点也有缺点。优点是操作简单,而缺点就是不灵活,显示的效果不够好,而且性价比也不高。FPGA黑金开发板选用的COG液晶是本身不带字库文件的液晶,我们在这里将研究一种通用的中英文字符显示方法,让大家充分了解如何利用外部字库文件实现字符显示,让我们的液晶显示更漂亮的字体。

      英文字库

      我们首先来研究英文字库的结构。英文字库使用的是ASCII码,起码值是0到127,其寻址公式为:

英文点阵数据在英文点阵字库中的偏移 = 英文的ASCII码 * 一个英文字模占用的字节数

我们使用的字库是tahoma字体,高度为16位,而字体的宽度是变宽的。在下表的tahoma_font_offset[]中,注释部分与偏移量是一一对应的,比如!,它的地址偏移量为7,它的宽度为11-7=4个,也就是说

字符宽度 = 它的下一个字符的偏移量 - 它本身的偏移量

#ifndef TAHOMA_H_
#define TAHOMA_H_
//字符的相对偏移量
unsigned int  tahoma_font_offset[]={
          //    !   "   #   $   %   &  '    (
        0  ,3  ,7  ,11 ,19 ,25 ,36 ,43 ,45 ,49 ,
     // )   *   +   ,   -   .   /   0   1   2
        53 ,59 ,67 ,71 ,75 ,79 ,83 ,89 ,95 ,101,
     // 3   4   5   6   7   8   9   :   ;   <
        107,113,119,125,131,137,143,147,152,159,
     // =   >   ?   @   A   B   C   D   E   F
        168,175,180,190,197,203,210,217,223,229,
     // G   H   I   J   K   L   M   N   O   P
        236,243,247,252,258,263,271,278,286,292,
     // Q   R   S   T   U   V   W   X   Y   Z
        300,307,313,319,326,332,342,348,354,360,
     // [   \   ]   ^   _   `   a   b   c   d
        364,368,372,380,387,392,398,404,409,415,
     // e   f   g   h   i   j   k   l   m   n
        421,425,431,437,439,442,447,449,457,463,
     // o   p   q   r   s   t   u   v   w   x
        469,475,481,485,490,494,500,506,514,520,
     // y   z   {   |   }   ~
        526,531,536,540,545,553
};

const char  font_tahoma_8[]={
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0B,0xF0,0x00,0x00,0x00,0x00,0x00,0x38,
0x00,0x00,0x00,0x38,0x00,0x00,0x02,0x00,0x0E,0x40,0x03,0xC0,0x0E,0x70,0x03,0xC0,
0x02,0x70,0x00,0x40,0x00,0x00,0x08,0xC0,0x09,0x20,0x3F,0xF8,0x09,0x20,0x06,0x20,
0x00,0x00,0x00,0x60,0x00,0x90,0x00,0x90,0x0C,0x60,0x03,0x00,0x00,0xC0,0x06,0x30,
0x09,0x00,0x09,0x00,0x06,0x00,0x00,0x00,0x07,0x60,0x08,0x90,0x08,0x90,0x09,0x60,
0x06,0x00,0x05,0x80,0x08,0x00,0x00,0x38,0x00,0x00,0x07,0xC0,0x18,0x30,0x20,0x08,
0x00,0x00,0x20,0x08,0x18,0x30,0x07,0xC0,0x00,0x00,0x02,0x80,0x01,0x00,0x07,0xC0,
0x01,0x00,0x02,0x80,0x00,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x0F,0xE0,0x01,0x00,
0x01,0x00,0x01,0x00,0x00,0x00,0x20,0x00,0x1C,0x00,0x00,0x00,0x00,0x00,0x01,0x00,
0x01,0x00,0x01,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x38,0x00,
0x07,0xC0,0x00,0x38,0x00,0x00,0x07,0xE0,0x08,0x10,0x08,0x10,0x08,0x10,0x07,0xE0,
0x00,0x00,0x00,0x00,0x08,0x20,0x0F,0xF0,0x08,0x00,0x00,0x00,0x00,0x00,0x0C,0x20,
0x0A,0x10,0x09,0x10,0x08,0x90,0x08,0x60,0x00,0x00,0x04,0x20,0x08,0x10,0x08,0x90,
0x08,0x90,0x07,0x60,0x00,0x00,0x01,0x80,0x01,0x40,0x01,0x20,0x0F,0xF0,0x01,0x00,
0x00,0x00,0x04,0xF0,0x08,0x90,0x08,0x90,0x08,0x90,0x07,0x10,0x00,0x00,0x07,0xC0,
0x08,0xA0,0x08,0x90,0x08,0x90,0x07,0x00,0x00,0x00,0x00,0x10,0x0C,0x10,0x03,0x10,
0x00,0xD0,0x00,0x30,0x00,0x00,0x07,0x60,0x08,0x90,0x08,0x90,0x08,0x90,0x07,0x60,
0x00,0x00,0x00,0xE0,0x09,0x10,0x09,0x10,0x05,0x10,0x03,0xE0,0x00,0x00,0x00,0x00,
0x0C,0xC0,0x00,0x00,0x00,0x00,0x20,0x00,0x1C,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,
0x01,0x00,0x02,0x80,0x02,0x80,0x04,0x40,0x04,0x40,0x08,0x20,0x00,0x00,0x02,0x80,
0x02,0x80,0x02,0x80,0x02,0x80,0x02,0x80,0x02,0x80,0x02,0x80,0x00,0x00,0x00,0x00,
0x08,0x20,0x04,0x40,0x04,0x40,0x02,0x80,0x02,0x80,0x01,0x00,0x00,0x00,0x00,0x10,
0x0B,0x10,0x00,0x90,0x00,0x60,0x00,0x00,0x07,0xC0,0x08,0x20,0x13,0x90,0x14,0x50,
0x14,0x50,0x17,0xD0,0x04,0x10,0x04,0x20,0x03,0xC0,0x00,0x00,0x0E,0x00,0x03,0xC0,
0x02,0x30,0x02,0x30,0x03,0xC0,0x0E,0x00,0x00,0x00,0x0F,0xF0,0x08,0x90,0x08,0x90,
0x08,0x90,0x07,0x60,0x00,0x00,0x03,0xC0,0x04,0x20,0x08,0x10,0x08,0x10,0x08,0x10,
0x08,0x10,0x00,0x00,0x0F,0xF0,0x08,0x10,0x08,0x10,0x08,0x10,0x04,0x20,0x03,0xC0,
0x00,0x00,0x0F,0xF0,0x08,0x90,0x08,0x90,0x08,0x90,0x08,0x10,0x00,0x00,0x0F,0xF0,
0x00,0x90,0x00,0x90,0x00,0x90,0x00,0x90,0x00,0x00,0x03,0xC0,0x04,0x20,0x08,0x10,
0x09,0x10,0x09,0x10,0x0F,0x10,0x00,0x00,0x0F,0xF0,0x00,0x80,0x00,0x80,0x00,0x80,
0x00,0x80,0x0F,0xF0,0x00,0x00,0x08,0x10,0x0F,0xF0,0x08,0x10,0x00,0x00,0x08,0x00,
0x08,0x10,0x08,0x10,0x07,0xF0,0x00,0x00,0x0F,0xF0,0x01,0x80,0x02,0x40,0x04,0x20,
0x08,0x10,0x00,0x00,0x0F,0xF0,0x08,0x00,0x08,0x00,0x08,0x00,0x00,0x00,0x0F,0xF0,
0x00,0x30,0x00,0xC0,0x03,0x00,0x00,0xC0,0x00,0x30,0x0F,0xF0,0x00,0x00,0x0F,0xF0,
0x00,0x30,0x00,0xC0,0x03,0x00,0x0C,0x00,0x0F,0xF0,0x00,0x00,0x03,0xC0,0x04,0x20,
0x08,0x10,0x08,0x10,0x08,0x10,0x04,0x20,0x03,0xC0,0x00,0x00,0x0F,0xF0,0x01,0x10,
0x01,0x10,0x01,0x10,0x00,0xE0,0x00,0x00,0x03,0xC0,0x04,0x20,0x08,0x10,0x08,0x10,
0x18,0x10,0x24,0x20,0x23,0xC0,0x00,0x00,0x0F,0xF0,0x01,0x10,0x01,0x10,0x03,0x10,
0x04,0xE0,0x08,0x00,0x00,0x00,0x08,0x60,0x08,0x90,0x08,0x90,0x08,0x90,0x07,0x10,
0x00,0x00,0x00,0x10,0x00,0x10,0x0F,0xF0,0x00,0x10,0x00,0x10,0x00,0x00,0x07,0xF0,
0x08,0x00,0x08,0x00,0x08,0x00,0x08,0x00,0x07,0xF0,0x00,0x00,0x00,0x70,0x03,0x80,
0x0C,0x00,0x03,0x80,0x00,0x70,0x00,0x00,0x00,0x70,0x03,0x80,0x0C,0x00,0x03,0x80,
0x00,0x70,0x03,0x80,0x0C,0x00,0x03,0x80,0x00,0x70,0x00,0x00,0x0C,0x30,0x02,0x40,
0x01,0x80,0x02,0x40,0x0C,0x30,0x00,0x00,0x00,0x30,0x00,0xC0,0x0F,0x00,0x00,0xC0,
0x00,0x30,0x00,0x00,0x0C,0x10,0x0A,0x10,0x09,0x90,0x08,0x50,0x08,0x30,0x00,0x00,
0x3F,0xF8,0x20,0x08,0x20,0x08,0x00,0x00,0x00,0x38,0x07,0xC0,0x38,0x00,0x00,0x00,
0x20,0x08,0x20,0x08,0x3F,0xF8,0x00,0x00,0x00,0x80,0x00,0x40,0x00,0x20,0x00,0x10,
0x00,0x20,0x00,0x40,0x00,0x80,0x00,0x00,0x20,0x00,0x20,0x00,0x20,0x00,0x20,0x00,
0x20,0x00,0x20,0x00,0x00,0x00,0x00,0x08,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x00,
0x06,0x00,0x09,0x40,0x09,0x40,0x09,0x40,0x0F,0x80,0x00,0x00,0x0F,0xF8,0x08,0x40,
0x08,0x40,0x08,0x40,0x07,0x80,0x00,0x00,0x07,0x80,0x08,0x40,0x08,0x40,0x08,0x40,
0x00,0x00,0x07,0x80,0x08,0x40,0x08,0x40,0x08,0x40,0x0F,0xF8,0x00,0x00,0x07,0x80,
0x09,0x40,0x09,0x40,0x09,0x40,0x05,0x80,0x00,0x00,0x0F,0xF0,0x00,0x48,0x00,0x48,
0x00,0x00,0x07,0x80,0x28,0x40,0x28,0x40,0x28,0x40,0x1F,0xC0,0x00,0x00,0x0F,0xF8,
0x00,0x40,0x00,0x40,0x00,0x40,0x0F,0x80,0x00,0x00,0x0F,0xD0,0x00,0x00,0x20,0x40,
0x1F,0xD0,0x00,0x00,0x0F,0xF8,0x01,0x00,0x02,0x80,0x04,0x40,0x08,0x00,0x0F,0xF8,
0x00,0x00,0x0F,0xC0,0x00,0x40,0x00,0x40,0x0F,0x80,0x00,0x40,0x00,0x40,0x0F,0x80,
0x00,0x00,0x0F,0xC0,0x00,0x40,0x00,0x40,0x00,0x40,0x0F,0x80,0x00,0x00,0x07,0x80,
0x08,0x40,0x08,0x40,0x08,0x40,0x07,0x80,0x00,0x00,0x3F,0xC0,0x08,0x40,0x08,0x40,
0x08,0x40,0x07,0x80,0x00,0x00,0x07,0x80,0x08,0x40,0x08,0x40,0x08,0x40,0x3F,0xC0,
0x00,0x00,0x0F,0xC0,0x00,0x80,0x00,0x40,0x00,0x00,0x09,0x80,0x09,0x40,0x0A,0x40,
0x06,0x40,0x00,0x00,0x07,0xF0,0x08,0x40,0x08,0x40,0x00,0x00,0x07,0xC0,0x08,0x00,
0x08,0x00,0x08,0x00,0x0F,0xC0,0x00,0x00,0x00,0xC0,0x03,0x00,0x0C,0x00,0x03,0x00,
0x00,0xC0,0x00,0x00,0x03,0xC0,0x0C,0x00,0x03,0x00,0x00,0xC0,0x03,0x00,0x0C,0x00,
0x03,0xC0,0x00,0x00,0x08,0x40,0x04,0x80,0x03,0x00,0x04,0x80,0x08,0x40,0x00,0x00,
0x00,0xC0,0x33,0x00,0x0C,0x00,0x03,0x00,0x00,0xC0,0x00,0x00,0x0C,0x40,0x0A,0x40,
0x09,0x40,0x08,0xC0,0x00,0x00,0x01,0x00,0x01,0x00,0x1E,0xF0,0x20,0x08,0x00,0x00,
0x00,0x00,0x3F,0xF8,0x00,0x00,0x00,0x00,0x20,0x08,0x1E,0xF0,0x01,0x00,0x01,0x00,
0x00,0x00,0x03,0x00,0x00,0x80,0x00,0x80,0x01,0x00,0x02,0x00,0x02,0x00,0x01,0x80,
0x00,0x00};

#endif /*TAHOMA_H_*/

      中文字库

      相对于英文字库,中文字库就要复杂一些,为了让大家更明白,我们先了解一下汉字点阵字库的原理。

  • 汉字编码

      区位码在孤寂GD2312-80中规定,所有的国际汉字及符号分配在一个94行、94列的方阵中,方阵的每一行称为一个“区”,编号为01区到94区,每一列为一个“位”,比那好为01位到94位,方阵中的每一个汉字和符号所在的区号和位号组成在一起形成的四个阿拉伯数字就是他们的“区位码”。区位码的前两位是他的区号,后两位是它的位号。用区位码就可以唯一的确定一个汉字或字符,反过来说,任何一个汉字或字符也都对应着一个唯一的区位码。例如,汉字“母”字的区位码是3624,表明它在方阵的36区24位,问好“?”的区位码为0331,则它在03区31位。

  • 机内码

      汉字的机内码是指在计算机中表示一个汉字的编码。机内码与区位码稍有区别。如上所述,汉字区位码的区码和位码的取值均在1-94之间,如直接用区位码作为机内码,就会与基本的ASCII码混淆。为了避免机内码与基本ASCII码的冲突,需要避开基本ASCII码中的控制码(00H-1FH),还需要与基本ASCII码中的字符相区别。为了实现这两点,可以先在区码和位码分别加上20H,在此基础上再加80H(此处“H”表示前两位数字为十六进制)。经过这些处理,用机内码表示一个汉字需要占两个字节,分别称为高位字节和地位字节,这两位字节的机内码按如下规则表示:高位字节 = 区码 + 20H +80H(或区码+A0H)低位码=位码+20H+80H(或位码+A0H)由于汉字的区码与位码的取值范围的十六进制数均为01H-5EH(即十进制的01-94),所以汉字的高位字节与低位字节的取值范围则为A1H-FEH(即十进制的161-254)。例如,汉字“啊”的区位码为1601,区码和位码分别用十六进制表示即为1001H,它的机内码的高位字节为B0H,低位字节为A1H,机内码就是B0A1H。

      有了上面的基础后,我们来看看我们点阵字库的结构。

      点阵字库存储在汉字的点阵字库中,每个字节的每个位代表一个汉字的一个点,每个汉字都是由一个矩形的点阵组成,0代表没有,1代表有点,将0和1用不同的颜色画出,就形成了汉字,常用的点阵矩阵有12*12,14*14,16*16三种字库。我们常用的字库文件来自UCDOC中,DOC前辈们经过将制作好的字模放到一个个标准的苦衷,这就是点阵字库文件。一般我们使用的是16*16的点阵宋体字库,所谓16*16,是每一个汉字在纵、横各16点的区域内显示的。现在又有了HZK12,HZK24,HZK32和HZK48字库及黑体、楷体和隶书字库。虽然汉字库种类繁多,但都是按照区位的顺序排列的。前一个字节为该汉字的区号,后一个字节为该字的位号。每一个区记录94个汉字,位号则为该字在该区中的位置。因此,汉字在汉字库中的具体位置采用机内码的计算公式为:

94*(区号-0XA0-1)+(位号-0XA0-1)

      减1是因为数组是以0开始而区号位号是以1开始的。这仅为汉字为单位该汉字在汉字库中的位置,如果想得到以字节为单位,该汉字所在汉字库中的位置,那么只需要乘以一个汉字字模占用的字节数即可,即:

(94*(区号-0XA1)+(位号-0XA1))* 一个汉字字模占用字节数

      正常来说,一个汉字字模占用的字节数为横纵点数/8,以16*16点阵为例,它的一个汉字字模占用的字节数就为16*16/8=32。不过也有例外,在下面将会有所提及。

我们所使用的字库是HZK12,它是12*12的点阵字库,字库设计者为了使用方便,字模每一行的位数均补齐为8的整数倍,有的设计者也将列补齐为8的整数倍。我们使用的HZK12即为这种,于是实际该字库的为长度是16*16,多余的4为都是0(不显示),在显示效果上不受影响。

      下面,我们就来研究一下程序是如何实现的。

      首先,我们在工程目录中inc下建立gui.h文件,内容如下表所示

/*
 * =================================================================
 *       Filename:  gui.h
 *   Description:  
 *        Version:  1.0.0
 *        Created:  2010.4.16
 *       Revision:  none
 *       Compiler:  Nios II 9.0 IDE
 *         Author:   (AVIC)
 *          Email:  avic633@gmail.com  
 * =================================================================
 */
#ifndef GUI_H_
#define GUI_H_

typedef struct{
        unsigned char x;
        unsigned char y;
        unsigned char * matrix;     
        unsigned char wide;
        unsigned char reverse;
}X_16_T;

typedef struct{
    unsigned char x;
    unsigned char y;
    unsigned char * str;
    unsigned char reverse;
}CHARACTER_STRING;

typedef struct{
    unsigned char x;
    unsigned char y;
    unsigned char reverse;
}STRING_T;

extern unsigned char printf_string(STRING_T * p, char * fmt, ...);

#define BUFFER_SIZE 200

#endif /*GUI_H_*/

然后,我们在工程目录的drvier下建立gui.c文件,内容如下表所示

/*
 * =================================================================
 *       Filename:  gui.c
 *    Description:  
 *        Version:  1.0.0
 *        Created:  2010.4.16
 *       Revision:  none
 *       Compiler:  Nios II 9.0 IDE
 *         Author:   (AVIC)
 *          Email:  avic633@gmail.com  
 * ================================================================
 */
#include "../inc/gui.h"
#include "../inc/lcd.h"
#include "../inc/tahoma.h"
#include "../inc/hzk12.h"
#include <string.h>
#include <stdarg.h>

static int _gui_x_16(X_16_T *p);
static int  _print_tahoma(CHARACTER_STRING *p);
static int _tahoma_string_length(char * p);
unsigned char printf_string(STRING_T * p, char * fmt, ...);
static int h_to_v( unsigned char *dest, unsigned char *source);
int _print_simsun(CHARACTER_STRING *p);

/* 
 * ===  FUNCTION  ===================================================
 *         Name:  _gui_x_16
 * Description: 这个函数是根据字库的结构,将字库中的点打印到LCD屏幕上,结构体
*                 X_16_T在gui.h中定义,可以通过参数确定点的位置,字库的宽度,
*                 是否反色,以及字库文件的指针。
 * =================================================================
 */
int _gui_x_16(X_16_T *p)
{
    unsigned char i;

    if(p == NULL)return -1;
    if(p->x > 128)return -1;
    if(p->y > 8)return -1 ;
    if(p->matrix == NULL)return -1;
    
    if(p->reverse > 1)return -1;

    lcd.set_x(p->x);
    lcd.set_y(p->y);
    for(i=0;i<p->wide;i++){
   lcd.write_data(p->reverse?p->matrix[2*i+1]^0xfe:p->matrix[2*i+1]);
    }

    lcd.set_x(p->x);
    lcd.set_y(p->y+1);
    for(i=0;i<p->wide;i++){
            lcd.write_data(p->reverse?~p->matrix[2*i]:p->matrix[2*i]);
    }

   return 0;

}
/* 
 * ===  FUNCTION  ===================================================
 *         Name:  _print_tahoma
 * Description: 这个函数是英文字库的处理函数,使用的字库为定义在tomato.h中,
*                 CHARACTER_STRING结构体在gui.h当中定义,可以通过参数确定点
*                 的位置,是否反色,以及字库文件的指针。
 * =================================================================
 */
int  _print_tahoma(CHARACTER_STRING *p)
{
    unsigned char l,i=0;
    unsigned int wide;
    X_16_T tmp;
   
    if(p == NULL)return 0;
    if(p->x > 128)return 0;
    if(p->y > 8)return 0;
    if(p->str == NULL)return 0;
    if(p->reverse > 1)return 0;
   
    l=strlen((const char *)p->str);

    for(i=0;i<l;i++){
        wide=tahoma_font_offset[p->str[i]-' '+1];
        wide-=tahoma_font_offset[p->str[i]-' '];        
            tmp.x=p->x;
            tmp.y=p->y;
            tmp.matrix=(unsigned char *)font_tahoma_8+tahoma_font_offset[p->str[i]-' ']*2;//*2是因为字模高为16位,两个字节
            tmp.reverse=p->reverse;
            tmp.wide=wide;

            _gui_x_16(&tmp);

            p->x+=wide;
    }
    
    return 0;
}
/* 
 * ===  FUNCTION  ===================================================
 *         Name:  _tahoma_string_length
 *  Description:  这个函数求英文字模的宽度
 * =================================================================
 */
int _tahoma_string_length(char * p)
{
    int temp=0;

    while(*p !='\0'){
        temp+=tahoma_font_offset[*p-' '+1];
        temp-=tahoma_font_offset[*p-' '];
        p++;
    }
    return temp;
}
/* 
 * ===  FUNCTION  ====================================================
 *         Name:  printf_string
 * Description: 这个函数是实现中英文字体自动切换显示的,利用它可以显示中英文混合
*                 字体的字符串。里面还涉及到了变参的使用
 * ==================================================================
 */
unsigned char printf_string(STRING_T * p, char * fmt, ...)
{
     unsigned char buf1[BUFFER_SIZE];
     CHARACTER_STRING tmp;
     unsigned int i=0,offset,c=0;
     va_list arg_ptr;
     unsigned char buf[BUFFER_SIZE];
     
    if(p == NULL)return 0;
    if(p->x > 191)return 0;
    if(p->y > 6)return 0 ;

    memset(buf,'\0',sizeof(buf));
    //这部分就是C语言中变参的使用方法
    va_start(arg_ptr,fmt);
    vsprintf(buf,fmt,arg_ptr);
    va_end(arg_ptr);

    offset=p->x;

    while(buf[c]!='\0'){
        //通过判断来决定字符是英文还是中文
        if(buf[c] < 0x7f && buf[c]!='\n'){
            i=0;
            memset(buf1,'\0',sizeof(buf1));

            while(buf[c]<0x7f && buf[c]!='\0' &&buf[c]!='\n'){
                buf1[i++]=buf[c++];
            }

            tmp.x=offset;
            offset+=_tahoma_string_length(buf1);
            tmp.y=p->y;
            tmp.str=buf1;

            tmp.reverse=p->reverse;

            if(_print_tahoma(&tmp)==-1)return 0;

        }

        if(buf[c] > 0x7f){
            i=0;
            memset(buf1,'\0',sizeof(buf1));

            while(buf[c] >0x7f){
                buf1[i++]=buf[c++];
                buf1[i++]=buf[c++];
            }
            
            tmp.x=offset;
            offset+=i*6;

            tmp.y=p->y;
            tmp.str=buf1;
            tmp.reverse=p->reverse;

            if(_print_simsun(&tmp)==-1)return 0;
        }
        //new line symbol
        if(buf[c] =='\n'){
            p->y+=2;
            offset=p->x;
            c++;
        }
    }
    p->x=tmp.x;

    return 0;
}
/* 
 * ===  FUNCTION  ===================================================
 *         Name:  h_to_v
 * Description:  将汉字字模有横向转换为纵向,字库中的字模是横着放的,如果不经过转
*                 化,显示出来的就是倒着的字。
 * =================================================================
 */   
static int h_to_v( unsigned char *dest, unsigned char *source)
{
    int i,j;

    for(j=0;j<8;j++){
        for(i=0;i<8;i++)if(source[30-i*2]&(0x80>>j))dest[j*2]|=(0x80>>i);
        for(i=0;i<8;i++)if(source[14-i*2]&(0x80>>j))dest[j*2+1]|=(0x80>>i);  
    }

    for(j=0;j<4;j++){
        for(i=0;i<8;i++)if(source[31-i*2]&(0x80>>j))dest[j*2+16]|=(0x80>>i);
        for(i=0;i<8;i++)if(source[15-i*2]&(0x80>>j))dest[j*2+17]|=(0x80>>i);     
    }

    return 1;
}
/* 
 * ===  FUNCTION  ===================================================
 *         Name:  _print_simsun
 * Description: 这个函数是打印中文宋体汉字, 
 * =================================================================
 */
int _print_simsun(CHARACTER_STRING *p)
{
    unsigned char l,i;
    X_16_T tmp;
    unsigned int offset=0;
    unsigned char bua[32],bub[32];  

    if(p == NULL)return 0;
    if(p->x > 128)return 0;
    if(p->y > 64)return 0 ;
    if(p->str == NULL)return 0;
    if(p->reverse > 1)return 0;

    l=strlen(p->str);
    l/=2;

    for(i=0;i<l;i++){                 
        //这部分是根据机内码,求字模所在的位置,在前面以后有所提及
         offset=p->str[2*i];
        offset-=0xa1;
        offset*=94;
        offset+=p->str[2*i+1];
        offset-=0xa1;
        offset*=32;
          
        memcpy(bua,achzk12+offset,32);//achzk12是存放汉字字库的数组

         memset(bub,0,32);
        h_to_v(bub,bua); 

        tmp.x=p->x;
        tmp.y=p->y;
        tmp.reverse=p->reverse;
        tmp.matrix=bub;
        tmp.wide=12;     

        _gui_x_16(&tmp);

        p->x+=12;
    }
    return 0;
}

      在上面涉及到了achzk12,它是存放中文字库的数组。对于中文字库,有两种解决方案,一种是将字库文件(bin格式)烧写到flash中,每次用的时候先从flash读出来;另一种是将字库文件(将bin格式文件转化为c格式)数组的形式放到程序中。这两种方案根据硬件系统来选取。在这里,我们选用了第二种,因为我们的外部sdram很大,将字库文件放到程序中,每次上电都会将字库文件导入到sdram中,这样调用的时候速度就会很快,那么大的sdram不用也是浪费。而第一种方案一般是在ram比较小的情况下使用的,这样可以节省ram的空间。由于ackzk12文件比较大,在这里就不放出来了。

      最后,我们来测试一下,写个测试程序吧。

/*
 * ==================================================================
 *       Filename:  main.c
 *    Description:  
 *        Version:  1.0.0
 *        Created:  2010.4.16
 *       Revision:  none
 *       Compiler:  Nios II 9.0 IDE
 *         Author:  马瑞 (AVIC)
 *          Email:  avic633@gmail.com  
 * ==================================================================
 */
#include <stdio.h>
#include <sys/unistd.h>
#include <io.h>
#include <string.h>
#include "../inc/lcd.h"
#include "../inc/gui.h"
#include "../inc/ds1302.h"

#include "system.h"
#include "altera_avalon_pio_regs.h"
#include "altera_avalon_timer_regs.h"
#include "alt_types.h"
#include "sys/alt_irq.h"

unsigned char time[7] = {0x00,0x19,0x14,0x17,0x03,0x03,0x10};//格式为: 秒 分 时 日 月 星期 年
alt_u8 segtab[10]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90};  //0~9
unsigned char led_buffer[8]={0};
unsigned char bittab[6]={0xfe,0xfd,0xfb,0xf7,0xef,0xdf}; 
static unsigned char cnt=0;
unsigned char ti[][7]={"一","二","三","四","五","六","日"};  

/***********************************************/
static void timer_init(void);    //初始化中断

/***********************************************/
int main(void)
{ 
    unsigned char i=0;
    unsigned char buf[20];
    
    unsigned char en_str[]="Hello! What's your name?";
    unsigned char cn_str[] = "你好!你叫什么名字?";
    unsigned char str[] = " O(∩_∩)O~";

    STRING_T *string_t;
    
    timer_init();      
    ds1302.set_time(time);
    
    lcd.initialize();
    lcd.clear();
    
    string_t->x = 0;
    string_t->y = 0;
    string_t->reverse = 0;
    
    printf_string(string_t,"%s",en_str);
  
    string_t->x = 0;
    string_t->y = 2;
    string_t->reverse = 0;
    
    printf_string(string_t,"%s",cn_str);
        
    string_t->x = 0;
    string_t->y = 4;
    string_t->reverse = 0;
    
    printf_string(string_t,"%s",str);
while(1){
    //我们将实时时钟加入进来,这样就可以在LCD上显示时间了
        ds1302.get_time(time);          //采集时
        
        string_t->x = 0;
        string_t->y = 6;
        string_t->reverse = 1;
        
        printf_string(string_t,"%02d-%02d-%02d %02d:%02d:%02d 星期%s\n",time[6],time[4],time[3],time[2],time[1],time[0],ti[time[5]-1]);
        
    }

  return 0;
}

      写好以后,我们就可以编译了。编译通过以后运行,我们来看看效果如何,上张效果图吧。

LSFJP6R9L{AWN`3PI)SU5FW

      还不错吧,中文是宋体,英文是变宽的,显示效果很不错,连笑脸都可以打印出来,看来这个字库还是很全的,在此谢谢前辈们为我们做出的努力和贡献,我们将没齿难忘。

     这一节内容就结束了,谢谢大家的支持!

原文地址:https://www.cnblogs.com/kingst/p/1773909.html