电子大神的日记本,供应链专家的功夫茶盘,在这里记录、分享与共鸣。

登录以开始

ZLG GUI学习试验心得及GB2312字库移植(转)

 

zlggui是一个简单的GUI,前后共花了半天时间来试验,有些心得;个人觉得这个UI用在一些需求一个简单的UI嵌入式系统中还是挺好的,移植方便,容易修改。其实现了基本的画线、画长方形、画圆/椭圆、画弧、曲线、填充、5*7\8*8字\24*32ASCII字符、简单窗体、菜单、按键等功能。在试验过程中,修改了一处BUG及增加了几个用来输出GB2312字库的函数。以下详细说明。

源程序中的各功能部分文件逻辑关系结构:
顶层:font_24_32 Font5_7 Font8_8 Menu Spline Windows Loadbit
中层:Gui_basic GUI_StockC ConvertColor 
底层:需要由用户根据硬件编写LCD的驱动,并且要符合中层的函数声明要求
配置层:Gui_CONFIG.h,另外需要用户提供一个全局的配置文件(数据类型等)
在移植过程中,只需设计四个文件即可(文件名自定,文件数量也算定,只要功能实现即可):lcd_driver.h lcd_driver.c config.h。其中lcd_driver.c、lcd_driver.h和 config.h需要由用户提供。除了要符合中层Gui_BASIC.H所要求的函数定义外,在lcd_driver.c/h  中还需要定义两个宏(函数):GUI_CmpColor和GUI_CopyColor,注意这两个宏需要根据LCD的显示类型来定义,单色屏和彩色屏不同。
lcd_driver.h中的一些示例:
//这两个宏根据LCD的像素位数不同而不同,以 下是彩色屏定义
#define  GUI_CmpColor(color1, color2) ((color1) == (color2))
#define  GUI_CopyColor(color1, color2)  ((*color1) = (color2))

//单色屏定义
#define  GUI_CmpColor(color1, color2) ( (color1&0x01) == (color2&0x01) )
#define  GUI_CopyColor(color1, color2)  ((*color1) =( color2&0x01))

//定义一些颜色值
#define  red   0x001f
#define  blue   0xf800
#define  green   0x07e0
#define  black   0x0000
=====================================
lcd_driver.c中的一些示例:
在这个驱动文件中,需要定义几个函数:画点、取点、画水平线、画垂直线、画任意两点线四个函数,且要和Gui_basic.h中的函数声明符合。在我的移植中,我在lcd_driver.h中用宏来联系这些函数,这样就不用改变我lcd_driver.c中的函数名,如下:
#define GUI_Point set_pixel
#define GUI_HLine draw_hline
#define GUI_RLine draw_vline
#define GUI_ReadPoint get_pixel 

config.h中的一些示例:
typedef unsigned char  uint8;                    /* 无符号8位整型变量  */
typedef signed   char  int8;                    /* 有符号8位整型变量  */
typedef unsigned short uint16;                   /* 无符号16位整型变量 */
typedef signed   short int16;                   /* 有符号16位整型变量 */
typedef unsigned int   uint32;                   /* 无符号32位整型变量 */
typedef signed   int   int32;                   /* 有符号32位整型变量 */
typedef unsigned short TCOLOR;
//包含文件,根据工程的环境来包含
#include "GUI_BASIC.H"
#include "font5_7.h"
#include "font24_32.h"
#include "font8_8.h"
#include "font_macro.h"
#include "windows.h"
在移植过程中,发现zlggui的一个BUG。在font_24_32.c中,GUI_PutChar24_32函数设计有误,导致不能正确输出字体数据。按照我的理解逻辑,修改后如下:
uint8  GUI_PutChar24_32(uint32 x, uint32 y, uint8 ch)
{  uint8   font_dat;
   uint8   i, j, k;
   TCOLOR  bakc;

   /* 参数过滤 */
   if( x>(GUI_LCM_XMAX-32) ) return(0);
   if( y>(GUI_LCM_YMAX-32) ) return(0);
   for(i=0; i<14; i++)
   {  if(FONT24x32_TAB[i]==ch) break;
   }
   ch = i;
    
   for(i=0; i<32; i++)      // 显示共32行
   { 
      for(j=0; j<3; j++)     // 每行共3字节 
      {  
   font_dat = FONT24x32[ch][i*3+j];      
         /* 设置相应的点为color或为back_color */
    for(k=0; k<8; k++)
         {
         if( (font_dat&DCB2HEX_TAB[k])==0 ) 
     GUI_CopyColor(&bakc, back_color);
         else  
      GUI_CopyColor(&bakc, disp_color);
    GUI_Point(x, y, bakc);       
          x++;
   }
      } 
      y++;         

// 指向下一行
      x -= 24;        

// 恢复x值
   }
   
   return(1);
}
==========================================================
原函数:
uint8  GUI_PutChar24_32(uint32 x, uint32 y, uint8 ch)
{  uint8   font_dat;
   uint8   i, j;
   TCOLOR  bakc;

   /* 参数过滤 */
   if( x>(GUI_LCM_XMAX-32) ) return(0);
   if( y>(GUI_LCM_YMAX-32) ) return(0);
   for(i=0; i<14; i++)
   {  if(FONT24x32_TAB[i]==ch) break;
   }
   ch = i;
    
   for(i=0; i<32; i++)      // 显示共32行
   { 
    for(j=0; j<24; j++)     // 每行共24点
      {  /* 若当前点为0、8、16点,读取点阵数据 */
        if( (j&0x07)==0 ) font_dat = FONT24x32[ch][i*3+j>>3];      
          /*设置相应的点为color或为back_color */
         f( (font_dat&DCB2HEX_TAB[j])==0 ) GUI_CopyColor(&bakc, back_color);
            else  GUI_CopyColor(&bakc, disp_color);
         GUI_Point(x, y, bakc);       
         x++;
      }
      y++;         

// 指向下一行
      x -= 24;        

// 恢复x值
   }
   
   return(1);
}
===============================================
//另外,我设计一个可以输出24*32字符串的函数,如下:
uint8  GUI_PutString24_32(uint32 x, uint32 y, uint8 *str)
{
 while(1)
   {  if( (*str)=='\0' ) break;
      if( GUI_PutChar24_32(x, y, *str++)==0 ) break;
      x += 24;        

// 下一个字符显示位置,y不变(即不换行)
   }
 return 1;
}

===============================================================================================================

显示GB2312汉字。字库的生成由minigui提供的工具来生成,我生成的是16*16的字库。在实际应用中,显示的字体可能是12*12、16*16、24*32等。因此,需要设计一个通用的字体数据输出函数,能够自动识别字体的参数如高度、宽度等。在loadbit.c中有一个函数_GUI_PutHZ可以用来显示汉字,利用这个函数我设计了几个函数来输出不同宽高的GB2312字库。说明如下:

//这个函数可以显示任意高宽的字体数据。其中hz_addr=(uint8 *)HZ_FONT_ADDR是字

库的起始地址。hz_addr=(uint8 *)HZ_FONT_ADDR+128*hz_bytes1是汉字的起始地址。

重要的:tmp=(*chr)*hz_bytes1和tmp=((*(chr)-0xa0-1)*94+(*(chr+1)-0xa0-1))*hz_bytes2

,这个是根据GB2312的编码规则和生成字库的参数来计算汉字的点阵数据起始偏移量,

其中hz_bytes1是表示西文字体的一个字共需要的字节数;hz_bytes2是表示中文字体的

一个字共需要的字节数。94表示字库的区号。可以参考GPB2312规则来理解。

void GUI_PutHZ(uint32 x, uint32 y, uint8 *chr)
{
 uint32 tmp;
 if(!font_lib_valid_flag) //字库是否有效
  return;
  if(*chr<128)
 {
  hz_addr=(uint8 *)HZ_FONT_ADDR;
  tmp=(*chr)*hz_bytes1;  //西文字偏移量
  _GUI_PutHZ(x, y, hz_addr+tmp, hz_width1, hz_height);//输出西文字
 }
 else
 {
  hz_addr=(uint8 *)HZ_FONT_ADDR+128*hz_bytes1; 
    tmp=((*(chr)-0xa0-1)*94+(*(chr+1)-0xa0-1))*hz_bytes2; //中文字偏移量
  _GUI_PutHZ(x, y, hz_addr+tmp, hz_width2 ,hz_height); //输出中文字
 }    
}

 

//这个函数用来显示水平输出汉字字符串,可混合输出汉字和ASCIi字符,能自动识别字体的宽度
//调用示例: GUI_PutHZStringH(0,0,"山光物态弄春晖,莫为轻阴便拟归");
//  GUI_PutHZStringH(0,100,"abcdef一二三四");
void GUI_PutHZStringH(uint32 x, uint32 y, uint8 *str)
{
   if(!font_lib_valid_flag) //字库是否有效
 return;
   while(1)
   {  if( (*str)=='\0' ) break;
     
   if(*str > 127)
   {
       GUI_PutHZ(x, y, str);
    str+=2;  //每个中文字由两个字节数据组成
    x+=hz_width2; //中文字体
   }
   else
   {
      GUI_PutHZ(x, y, str);
    str+=1;
    x+=hz_width1; //西文字体
   } 
   }
}

 

//这个函数用来显示垂直输出汉字字符串,可混合输出汉字和ASCII字符,能自动识别字体的宽度
//调用示例: GUI_PutHZStringV(0,0,"山光物态弄春晖,莫为轻阴便拟归");
//  GUI_PutHZStringV(0,100,"abcdef一二三四");
void GUI_PutHZStringV(uint32 x, uint32 y, uint8 *str)
{
 if(!font_lib_valid_flag) //字库是否有效
  return;
   while(1)
   {  if( (*str)=='\0' ) break;
     
   if(*str > 127)
   {
       GUI_PutHZ(x, y, str);
    str+=2;  
    y+=hz_height; //中文字体
   }
   else
   {
      GUI_PutHZ(x, y, str);
    str+=1;
    y+=hz_height; //西文字体
   } 
   }
}

//字库参数读取及处理:字体统一高度、西文字体宽度、中文字体宽度、编码方式、校验字节数据
//在输出GB2312字库数据之前,必须先调用这个函数,仅需调用一次即可,用来读取字库的参数如:字体统一高度、宽度、编码方式(输出函数只支持GB2312)、校验等。
void GUI_HZParameterInit(void)
{
 uint8 tmp;
  hz_addr=(uint8 *)HZ_FONT_ADDR; 
    hz_height=*hz_addr;  //字体统一高度
 hz_width1=*(hz_addr+1);  //西文字体宽度
 hz_width2=*(hz_addr+2);  //中文字体宽度
 hz_code_mode=*(hz_addr+3);  //字体编码方式
 
 tmp=~(hz_height+hz_width1+hz_width2+hz_code_mode);//计算校验
 if(*(hz_addr+4)==tmp) //比较校验是否有效
 {
  font_lib_valid_flag=0xff;
  hz_bytes1=((hz_width1%8)? hz_width1/8+1:hz_width1/8)

*hz_height; //计算西文字体字节数
  hz_bytes2=((hz_width2%8)? hz_width2/8+1:hz_width2/8)

*hz_height;//计算中文字体字节数
 }
 else
  font_lib_valid_flag=0x00; //无效的字库
}

 

//一些用到的变量
#define HZ_FONT_ADDR 0x26000 //字库存放的起始地址,根据实际情况处理
uint8 *hz_addr;  //访问字库数据地址指针
uint8 hz_width1,hz_width2; //西文字宽度,中文字宽度
uint8 hz_height;     /字体统一高度
uint8 hz_code_mode; //编码方式
uint8 hz_bytes1,hz_bytes2; //西文字占用字节数,中文字字节数
uint8 font_lib_valid_flag; //字库有效性

 

//调用到的一些其他函数
/****************************************************************************
* 名称:GUI_LoadLine()
* 功能:输出单色图形的一行数据。
* 入口参数: x  指定显示位置,x坐标
*           y  指定显示位置,y坐标
*           dat  要输出显示的数据。
*           no      要显示此行的点个数
* 出口参数:返回值为1时表示操作成功,为0时表示操作失败。
* 说明:操作失败原因是指定地址超出有效范围。
****************************************************************************/
uint8  GUI_LoadLine(uint32 x, uint32 y, uint8 *dat, uint32 no)
{  uint8   bit_dat;
   uint8   i;
   TCOLOR  bakc;

   /* 参数过滤 */
   if(x>=GUI_LCM_XMAX) return(0);
   if(y>=GUI_LCM_YMAX) return(0);
   
   for(i=0; i=GUI_LCM_XMAX ) return(0);
   }
   
   return(1);
}

/****************************************************************************
* 名称:_GUI_PutHZ()
* 功能:显示汉字。
* 入口参数: x  指定显示位置,x坐标
*           y  指定显示位置,y坐标
*           dat  要输出显示的汉字点阵数据。
*           hno     要显示此行的点个数
*           lno     要显示此列的点个数
* 出口参数:无
* 说明:操作失败原因是指定地址超出有效范围。
****************************************************************************/
//这个函数我改了名字,_GUI_PutHZ用来输出字体点阵数据,GUI_PutHZ用来输出一个

汉字
void  _GUI_PutHZ(uint32 x, uint32 y, uint8 *dat, uint8 hno, uint8 lno)
{  uint8  i;

   for(i=0; i>3);        

 // 计算下一行的数据
      if( (hno&0x07)!=0 ) dat++;
   }
}
====================================
关于GB2312字库的存放。16*16的GB2312字库共需要258KB的存储空间。可以根据硬

件环境把它存放在SD卡、外部Flash或者内部的Flash中。我使用的是LPC1768,共有

512KB内部Flash空间,因此就把它存放在了内部的Flash中的后半部分空间。如何把它烧

录进去呢?有几种方法:一是先把字库生成BIN文件,然后用烧录器烧录到指定空间;二

是在主程序中设计一个烧录字库的程序,使用IAP来烧录到指定空间 ;三是先把字库文件

和主程序一起编译,并把字库定义到指定空间,烧录一次,然后把字库文件从主程序剔除

,那么以后的的程序编译和烧录就只编译和烧录主程序了(这样可以加快编译和烧录速度)

,要注意的是主程序的需求空间不能覆盖到字体空间。因为是学习验证,我使用了最后一

种方法,方法的关键是把字库编译到指定空间:const unsigned char FONT[263744]

__attribute__((at(0x26000))),这是我使用的字库空间起始地址。烧录之后,字库的起始

地址就是0x26000了。字库数据一般也不会存放于内部的Flash空间,这是一种极度的浪

费(除非空间绰绰有余)。在实际产品中,字库一般存放于SD卡或者外部Flash中。

关于GB2312的编码规则,在网上摘录一些重要信息如下:
GB 2312中对所收汉字进行"分区"处理,每区含有94个汉字/符号。这种表示方式也称为

区位码。   
01-09区为特殊符号。   
16-55区为一级汉字,按拼音排序。   
56-87区为二级汉字,按部首/笔画排序。   
10-15区及88-94区则未有编码。   
举例来说,"啊"字是GB2312之中的第一个汉字,它的区位码就是1601。
每个汉字及符号以两个字节来表示。
第一个字节称为"高位字节"(也称区字节),第二个字节称为"低位字节"(也称位字节)。 
"高位字节"使用了0xA1-0xF7(把01-87区的区号加上0xA0),"低位字节"使用了0xA1-0xFE(

把01-94加上 0xA0)。
由于一级汉字从16区起始,汉字区的"高位字节"的范围是0xB0-0xF7,"低位字节"的范围

是0xA1-0xFE,占用的码位是 72*94=6768。其中有5个空位是D7FA-D7FE。   
例如"啊"字在大多数程序中,会以两个字节,0xB0(第一个字节) 0xA1(第二个字节)

储存。区位码=区字节+位字节(与区位码对比:0xB0=0xA0+16,0xA1=0xA0+1)。

================================================================end

博主
21231460@qq.com
YGH的电子技术小屋
记录在产品开发过程中的点点滴滴,备份技术亮点,展示劳动成果!
点击跳转