高通字库
版本 V1.3 · 更新于 2026-08-17

4.字符显示及各种问题

4.1.字符排置解释(横置横排/竖置横排/竖置竖排)

使用字库显示字符前,用户需先确认其排置方式。可通过型号尾缀进行区分:尾缀为 W 或无尾缀则代表横置横排,尾缀为 Y 代表竖置横排,尾缀为 Z代表竖置竖排。也可以在https://www.hmi.gaotongfont.cn/docs/font-chip-ds看对应型号排置。

横置横排:比如宽度是16bit位,高度是16bit位的字符。那么“横置”指的就是两个字节水平放置,即宽度。“横排”指的是每个字节从做到右横向放两个字节,满两个字节换行继续放,一共有16排,即高度。如下图所示。

横置横排示意图横置横排示意图

竖置横排:比如宽度是16bit位,高度是16bit位的字符。那么“竖置”指的就是每个字节都是竖直放置,放16个字节,即宽度。“横排”指的从左到右放16个字节,满16个字节换行继续放,一共有2排,即高度。如下图所示。

竖置横排示意图竖置横排示意图

竖置竖排:比如宽度是24bit位,高度是24bit位的字符。那么“竖置”指的就是每个字节都是竖直放置,放24个字节,即宽度。“竖排”指的是从上到下放3个字节,满3个字节换列继续放,一共有24列,即高度。如下图所示。 竖置竖排示意图竖置竖排示意图

4.2.显示函数(TFT彩屏)

首先用户要确定使用的字库芯片是横置横排还是竖置横排。如果是竖置横排可以参考Display_Y函数,反之参考Display_W函数,这里两个是打点显示的代码,LCD_Display_Area是区域刷新显示代码。以下是打点显示以及区域刷新显示代码。如果想快速显示可以使用区域刷新显示代码,TFT屏绝大多数采用排置为横置横排的字库芯片。

/**
 * @brief 竖置横排显示
 *
 * @param pBits 存放字符数据的数组
 * @param x 竖轴坐标
 * @param y 横轴坐标
 * @param widt 字符宽度
 * @param high 字符高度
 */
void Display_Y(unsigned char *pBits,unsigned int x,unsigned int y,unsigned
int widt,unsigned int high)
{
    unsigned int i,j,k,n;
    unsigned char temp;
    n = 0;
    for( i = 0;i < ((high+7)>> 3); i++)//遍历每一行
    {
        for( j = 0;j < widt;j++)//遍历每个字节
        {
            temp = pBits[n++];
            for(k = 0;k < 8;k++)//遍历每一位
            {
                if(((temp >> k)& 0x01) == 0 )
                {
                    /* 显示一个像素点 */
                    LCD_Fast_DrawPoint( x+j, y+k+(i*8), 0xFFFF);
                }
                else
                {
                    /* 显示一个像素点 */
                    LCD_Fast_DrawPoint( x+j, y+k+(i*8), 0x0000);
                }
            }
        }
    }
}
/**
 * @brief 横置横排显示
 *
 * @param pBits 存放字符数据的数组
 * @param x 竖轴坐标
 * @param y 横轴坐标
 * @param widt 字符宽度
 * @param high 字符高度
 */
void Display_W(unsigned char *pBits,unsigned int x,unsigned int y,unsigned
int widt,unsigned int high)
{
    unsigned int i,j,k,n;
    unsigned char temp;
    n = 0;
    for( i = 0;i < high; i++)//遍历每一行
    {
        for( j = 0;j < ((widt+7)>> 3);j++)//遍历每个字节
        {
            temp = pBits[n++];
            for(k = 0;k < 8;k++)
            {
                if(((temp << k)& 0x80) == 0 )//遍历每一位
                {
                    /* 显示一个像素点 */
                    LCD_Fast_DrawPoint( x+k+(j*8), y+i, 0xFFFF);
                }
                else
                {
                    /* 显示一个像素点 */
                    LCD_Fast_DrawPoint( x+k+(j*8), y+i, 0x0000);
                }
            }
        }
    }
}
/**
 * @brief 设置显示区域,快刷显示
 *
 * @param buff 存放字符数据的数组
 * @param x 竖轴坐标
 * @param y 横轴坐标
 * @param widt 字符宽度
 * @param high 字符高度
 */
void LCD_Display_Area(unsigned char* buff,unsigned int x,unsigned int
y,unsigned int widt,unsigned int high )
{
    uint16_t i,j,k;
    uint16_t da, color;
    unsigned char* p = buff;
    unsigned char c;
    uint32_t t, h = high;
    k=0;
    t = (widt+7)/8;
    LCD_Set_Window(x,y,(((widt+7)/8)*8),high);  //设置窗口
    LCD_WriteRAM_Prepare();     //开始写入GRAM
    for(i=0;i<t*h;i++)
    {
        c=*p++;
        for(j=0;j<8;j++) //循环判断每一位是1还是0
        {
            if(c&0x80)
                color=0x0000;
            else
                color=0xffff;
            c<<=1;
            //写16位数据
            LCD_WriteRAM(color);
        }
    }
}

灰度矢量的显示函数可以参考下文代码。重点关注设置显示区域的代码和设置写入GRAM寄存器指令的代码。最后通过LCD_WR_DATA将每个像素点的灰度数据写到GRAM中。注意:用户需根据使用的LCD进行代码调整。

#define DISMODE 1 // 1  刷图显示  0 打点显示
//描点函数
#if DISMODE == 0
static void Gui_DrawPoint(unsigned int x, unsigned int y, unsigned int
Data)
{
  //显示一个点
  putPixel( x, y, Data);
}
#endif
/*----------------------------------------------------------------------------------------
 * 灰度数据显示函数 1阶灰度/2阶灰度/4阶灰度
 * 参数 :
 * data灰度数据;  x,y=显示起始坐标 ; w 宽度, h 高度,grade
灰度阶级[1阶/2阶/4阶]
 * HB_par 1 白底黑字  0 黑底白字
 *------------------------------------------------------------------------------------------*/
void Gray_Display_hz(unsigned char *data,unsigned short x,unsigned short y, unsigned short w ,unsigned short h,unsigned char grade, unsigned char HB_par)
{
  unsigned int temp=0,gray,x_temp=x;
  unsigned int i=0,j=0,k=0,t;
  unsigned char c,c2,*p;
  unsigned long color8bit,color4bit,color3bit[8],color2bit,color;

  t=(w+7)/8*grade;//
  p=data;
    t=(w+7)/8*4;//
  #if DISMODE
  LCD_Set_Window(x,y,(((w+7)/8)*8),h);  //设置显示区域
  LCD_WriteRAM_Prepare();   //设置写入GRAM寄存器指令
  #endif
  if(grade==2)  //2bits
  {
    for(i=0;i<t*h;i++)
    {
      c=*p++;
      for(j=0;j<4;j++)
      {
        color2bit=(c>>6);//获取像素点的2bit颜色值
        if(HB_par==1)
          color2bit= (3-color2bit)*250/3;//白底黑字
        else
          color2bit= color2bit*250/3;//黑底白字
        gray=color2bit/8;
        color=(0x001f&gray)<<11;              //r-5
        color=color|(((0x003f)&(gray*2))<<5); //g-6
        color=color|(0x001f&gray);            //b-5
        temp=color;
        temp=temp;
        c<<=2;
        #if DISMODE
        //写16位数据
        LCD_WriteRAM(temp);
        #else
        //打点
        if(x<(x_temp+w))
        {
          Gui_DrawPoint(x,y,temp);
        }
        x++;
        if(x>=x_temp+(w+7)/8*8) {x=x_temp; y++;}
        #endif
      }
    }
  }
  else if(grade==3)//3bits
  {
    for(i=0;i<t*h;i+=3)
    {
      c=*p; c2=*(p+1);
      color3bit[0]=(c>>5)&0x07;
      color3bit[1]=(c>>2)&0x07;
      color3bit[2]=((c<<1)|(c2>>7))&0x07;
      p++;
      c=*p; c2=*(p+1);
      color3bit[3]=(c>>4)&0x07;
      color3bit[4]=(c>>1)&0x07;
      color3bit[5]=((c<<2)|(c2>>6))&0x07;
      p++;
      c=*p;
      color3bit[6]=(c>>3)&0x07;
      color3bit[7]=(c>>0)&0x07;
      p++;
      for(j=0;j<8;j++)
      {
        if(HB_par==1)
        color3bit[j]= (7-color3bit[j])*255/7;//白底黑字
        else
        color3bit[j]=color3bit[j]*255/7;//黑底白字
        gray =color3bit[j]/8;
        color=(0x001f&gray)<<11;              //r-5
        color=color|(((0x003f)&(gray*2))<<5); //g-6
        color=color|(0x001f&gray);            //b-5
        temp =color;
        #if DISMODE
        //写16位数据
        LCD_WriteRAM(temp);
        #else
        //打点
        if(x<(x_temp+w))
        {
          Gui_DrawPoint(x,y,temp);
        }
        x++;
        if(x>=x_temp+(w+7)/8*8) {x=x_temp; y++;}
        #endif
      }
    }
  }
  else if(grade==4) //4bits
  {
    for(i=0;i<t*h;i++)
    {
      c=*p++;
      for(j=0;j<2;j++)
      {
        color4bit=(c>>4);
        if(HB_par==1)
          color4bit= (15-color4bit)*255/15;//白底黑字
        else
          color4bit= color4bit*255/15;//黑底白字
        gray=color4bit/8;
        color=(0x001f&gray)<<11;        //r-5
        color=color|(((0x003f)&(gray*2))<<5); //g-6
        color=color|(0x001f&gray);        //b-5
        //printf("color = 0x%X\r\n",color4bit);
        temp=color;
        c<<=4;
        #if DISMODE
        //写16位数据
        LCD_WriteRAM(temp);
        #else
        //打点
        if(x<(x_temp+w)){
          Gui_DrawPoint(x,y,temp);
        }
        x++;
        if(x>=x_temp+(w+7)/8*8) {x=x_temp; y++;}
        #endif
      }
    }
  }
  else if(grade == 5 || grade == 6)
  {
   for(i=0;i<t*h;i++)
    {
      c=*p++;
      for(j=0;j<2;j++)
      {
        color4bit=(c>>4);
        if(HB_par==1)
          color4bit= (15-color4bit)*255/15;//白底黑字
        else
          color4bit= color4bit*255/15;//黑底白字

        if(color4bit > 0x00 && color4bit < 0xFF && grade == 5)
          color4bit = color4bit - 0x11;
        else if(color4bit >= 0x11 && color4bit < 0xFF && grade == 6)
          color4bit =    (color4bit == 0x11) ? (color4bit - 0x11) :(color4bit - 0x22);

        gray=color4bit/8;
        color=(0x001f&gray)<<11;        //r-5
        color=color|(((0x003f)&(gray*2))<<5); //g-6
        color=color|(0x001f&gray);        //b-5
        //printf("color = 0x%X\r\n",color4bit);
        temp=color;
        c<<=4;
        #if DISMODE
        //写16位数据
        LCD_WriteRAM(temp);
        #else
        //打点
        if(x<(x_temp+w)){
          Gui_DrawPoint(x,y,temp);
        }
        x++;
        if(x>=x_temp+(w+7)/8*8) {x=x_temp; y++;}
        #endif
      }
    }
  }
  else  //1bits
  {
    for(i=0;i<t*h;i++)
    {
      c=*p++;
      for(j=0;j<8;j++)
      {
        if(c&0x80) color=0x0000;
        else color=0xffff;
        c<<=1;
        #if DISMODE
        //写16位数据
        LCD_WriteRAM(color);
        #else
        //打点
        if(x<(x_temp+w))
        {
          if(color == 0x0000 && HB_par == 1)
          {
            Gui_DrawPoint(x,y,color); //打点
          }
          else if(HB_par == 0 && color == 0x0000)
          {
            Gui_DrawPoint(x,y,~color);  //打点
          }
        }
        x++;
        if(x>=x_temp+(w+7)/8*8) {x=x_temp; y++;}
        #endif
      }
    }
  }
}

4.3.显示函数(STN黑白屏和OLED屏)

STN黑白屏和OLED屏绝大多数采用排置为竖置横排的字库芯片

/**
 * @brief 设置坐标
 *
 * @param page 竖轴起始坐标
 * @param column 横轴起始坐标
 */
void LCD_SetPos(uint8_t page, uint8_t column)
{
    column = column - 1; //我们平常所说的第 1 列,在 LCD 驱动 IC 里是第 0列。所以在这里减去 1.
    page = page - 1;
    write_com(0xb0 + page); /*设置页地址。每页是 8 行。一个画面的 64行被分成 8 个页。我们平常所说的第 1 页,在 LCD驱动 IC 里是第 0页,所以在这里减去 1*/
    write_com(column & 0x0f); //设置列地址的低 4 位
    column >>= 4;
    column = column | 0x10;
    write_com(column); //取高4位行地址
}

/**
 * @brief 显示 16x16 点阵图像、汉字、生僻字或 16x16
 *
 * @param y 横轴坐标
 * @param x 竖轴坐标
 * @param dp 存放字符数据的数组
 */
void display_graphic_16x16(uint8_t y, uint8_t x, unsigned char *dp)
{
    uint8_t i;
    LCD_SetPos(y, x); //发送地址,第一页
    for (i = 0; i < 16; i++) //第一页的数据16字节,列号自动累加
    {
        write_data(*dp); //写数据到LCD,如果要反色显示,就在*P前用~取反。
        dp++;
    }
    LCD_SetPos(y + 1, x); //发送地址,第二页
    for (i = 0; i < 16; i++) //第二页的数据16字节
    {
        write_data(*dp); //写数据到LCD,如果要反色显示,就在*P前用~取反。
        dp++;
    }
}

4.4.点阵字库的显示

通过上述的“4.1、点阵字库API接口”,能获取到文字的点阵数据。数据会存放在定义的数组里,那么接下来就是将数组里的数据显示到屏幕上。我们以显示8x16字号大小的ASCII字符为例:

char pBits[16] = {0};
ASCII_GetData(0x41, ASCII_8X16, pBits);
Display_W(pBits, 100, 100, 8, 16);

4.5.矢量字库的显示

通过上述的“4.2、矢量字库API接口”,以下面是两种矢量字库API接口的使用方式。

使用get_font_st获取矢量字符时,注意宏定义ZK_BUFFER_LEN的大小,这里决定了存放矢量字符数据的数组的大小,用户可以根据自己的需求进行改动。示例中调用的是ASCII中的‘A’字符,type选的是ASCII圆角类型,size字号大小是16,粗细thick建议跟size一样,这里一样是16,zkBuffer就是用来存矢量字符数据的数组。get_font_st返回值表示字符显示时需要的宽度。

VECFONT_ST font_st = {.fontCode = 0x41, .type = VEC_FT_ASCII_STY, .size =16, .thick = 16, .zkBuffer = {0}};
uint32_t width =  get_font_st(&font_st);
Display_W(font_st.zkBuffer, 100, 100, width, 16);

使用get_font获取矢量字符的方法与get_font_st类似,这里定义了一个256字节大小的数组。get_font返回值表示字符显示时需要的宽度。

char pBits[256] = {0};
uint32_t width = get_font(pBits, VEC_FT_ASCII_STY, 0x41, 16, 16, 16);
Display_W(pBits, 100, 100, width, 16);

4.6.灰度矢量字库/灰度字库的显示

通过上述的“4.3、灰度矢量字库API接口”,使用get_Font_Gray函数获取灰度数据,并返回灰度字符的宽度数据和灰度值。显示的时候必须要用到这两个参数显示字符,否则无法正常显示。

unsigned int* width = NULL;
width = get_Font_Gray(pBits, VEC_SONG_STY, 0xB0A1, 24, 24);
//获取灰度矢量字符
Gray_Display_hz(pBits,150,100, width[0] ,24, width[1], 1);
 //width[0]:宽度数据,width[1]:灰度数据

4.7.点阵,灰度,矢量不等宽字符的显示

4.7.1点阵/灰度字库芯片不等宽字符显示处理

点阵/灰度字库 不等宽字符排列格式

以16点阵ASCII不等宽字符为例(不等宽字符类型详见各字库芯片宏定义中的注释),16点阵不等宽字符的信息需要34个字节(BYTE 0 – BYTE33)来表示。

  • 存储格式

    由于字符是不等宽的,因此在存储格式中 BYTE0~BYTE1 存放文字宽度数据,BYTE2-33 存放点阵数据。具体格式见下图:

    figure 21figure 21

  • 存储结构

    不等宽字符的点阵存储宽度是以 BYTE(8bit)为单位取整的,根据不同字符宽度会出现相应的空白区。

    BYTE0~BYTE1 所存放点阵的实际宽度数据,即 x 坐标加上该字符的宽度数据,做为下一个字符显示的 x 轴的起始位置。

    例如: ASCII 圆角字符 B

    0-33BYTE 的全部数据是:

    00 0C 00 00 00 00 00 00 7F 80 7F C0 60 C0 60 C0 60 C0 7F 80 7F C0 60 E0 60 60 60 60 7F C0 7F 80 00 00

    其中头两个字节:

    BYTE0~BYTE1:00 0C 为 ASCII 圆角字符 B 的点阵宽度数据,即:12 位宽度。字符后面有 4 位空白区,可以在排版下一个字时考虑到这一点,将下一个字的起始位置前移。即 x 坐标加上该字符的宽度数据,即为下一个字符的 x 轴的起始位置。

    BYTE2-33:00 00 00 00 00 00 7F 80 7F C0 60 C0 60 C0 60 C0 7F 80 7F C0 60 E0 60 60 60 60 7F C0 7F 80 00 00 为 ASCII 圆角字符 B 的点阵数据。

不等宽示例代码

/*
 * 示例:使用 ASCII_12_A 显示 "Ab"。
 * ASCII_12_A 是不等宽 12x12 Arial 字体。
 */
unsigned char DZ_Data[512];
int x_position = 10, y_position = 10;
unsigned int a_width = 0;
/* 读取并显示字符 'A'。 */
memset(DZ_Data, 0, sizeof(DZ_Data));
ASCII_GetData(0x41, ASCII_12_A, DZ_Data);
a_width = (DZ_Data[0] << 8) | DZ_Data[1];
Gray_Display_hz(&DZ_Data[2], x_position, y_position, 16, 12, 1, 1);

/* 读取并显示字符 'b',x 坐标使用 'A' 的实际宽度计算。 */
memset(DZ_Data, 0, sizeof(DZ_Data));
ASCII_GetData(0x62, ASCII_12_A, DZ_Data);
Gray_Display_hz(&DZ_Data[2], x_position + a_width, y_position, 16, 12, 1, 1);

4.7.2 矢量字库芯片不等宽字符显示处理

如果使用get_font函数则打开“05_屏幕显示资料\获取宽度.c”,直接拷贝获取宽度的代码。如果使用get_font_gray函数,则打开“05_屏幕显示资料\获取灰度字形的宽度”拷贝获取灰度宽度的代码。

figure 26figure 26 具体的代码以及参数含义如下文所示:

/**
 * @brief Get the width object
 *
 * @param p 字符数据数组首地址
 * @param zfwidth 获取字符时填写的宽度
 * @param zfhigh 获取字符时填写的高度
 * @return unsigned int 实际宽度
 */
unsigned int get_width(unsigned char *p,unsigned int zfwidth,unsigned int zfhigh)
{
  unsigned char *q;
  unsigned int i,j,tem,tem1,witdh1=0,witdh2=0;
  q=p;
  for (i=0;i<zfwidth/16;i++)
  {
      tem=0;
    tem1=0;
    for (j=0;j<zfhigh;j++)
    {
       tem=(*(q+j*(zfwidth/8)+i*2)|(*(q+1+j*(zfwidth/8)+i*2))<<8);
       tem1=tem1|tem;
    }
    witdh1=0;
    for (j=0;j<16;j++)
    {
      if (((tem1<<j)&0x8000)==0x8000)
      {
        witdh1=j;
      }
    }
    witdh2+=witdh1;
  }
  return witdh2;
}
/**
 * @brief 获取灰度字库宽度
 *
 * @param data 带灰度的字符数据数组
 * @param dot_w 获取字符时填写的宽度
 * @param dot_h 获取字符时填写的高度
 * @param gray 取值: 1 / 2 / 4
 * @return int 实际宽度
 */
uint16_t get _width_gray(const uint8_t* data, uint16_t dot_w, uint16_t dot_h, uint8_t gray)
{
    uint16_t byte_w, i;
    int j, len;
    uint8_t tmp = 0;
    byte_w = ((dot_w + 7) >> 3) * gray;
    for(j = byte_w-1;j >= 0; j--) {
        tmp = 0;
        for(i = 0; i < dot_h; i++) {
            tmp |= data[i*byte_w + j];
        }
        if(0 != tmp) {
            for(i = 0; i < 8; i++){
                if((tmp >> i)&0x01){
                    break;
                }
            }
            len = ((j / gray) * 8) + ((j%gray)*8+(8-i))/gray;
            if (len >= dot_h) {
                continue;
            }
            return len;
        }
    }
    return dot_w;
}

矢量输出灰度数据时获取宽度示例:

    unsigned int *tmp, real_w = 0;
    tmp = get_Font_Gray(pBits,(*text) < 0x80?VEC_FT_ASCII_STY:VEC_SONG_STY,*text,size, size);   //
    real_w = get_width_gray(pBits,tmp[0],size, tmp[1]); //获取灰度的宽度
    Gray_Display_hz(pBits,x,y,tmp[0],size,tmp[1]);
    x = x + real_w;

矢量输出点阵数据时获取宽度示例:

VECFONT_ST font_st = {.fontCode = 0x41, .type = VEC_FT_ASCII_STY, .size = 16, .thick = 16, .zkBuffer = {0}};
get_font_st(&font_st);  //获取矢量字符数据
int width = get_width(font_st.zkBuffer, 16, 16);  //获取实际的字符宽度
Display_W(font_st.zkBuffer, 100, 100, width, 16);


char pBits[256] = {0};
get_font(pBits, VEC_FT_ASCII_STY, 0x41, 16, 16, 16);    //获取矢量字符数据
int width = get_width(pBits, 16, 16);  //获取实际的字符宽度
Display_W(pBits, 100, 100, width, 16);

注:无论点阵/灰度/矢量字库芯片,中文的宽度都是等宽。

4.8.字符显示的横竖转换问题

在上述我们有提到显示字符前,要确认使用的字库是横置横排还是竖置横排。比如字库里的字符都是横置横排的,但是显示的时候要用竖置横排的方式显示,那么我们就需要进行一次横竖转换。我们以GT30L24A3W为例。打开“05_屏幕显示资料\横竖转换.c”文件,具体代码如下文所示,可以直接拷贝到工程中。如果要实现横置横排转竖置横排,选择宏定义HZ_MODE00即可。注意:目前横竖转换的函数只能用在点阵字库。

#define  HZ_MODE00 0//竖置横排(Y)
#define  HZ_MODE01 1//竖置竖排(Z)
#define  HZ_MODE10 2//横置横排(W)
#define  HZ_MODE11 3//横置竖排(X)
#define  HZ_MODE4  4//Y-->W
//条件putdata的数据写入X,Y的LCD显示缓冲RAM中
/*
*getdate: 转换后的字形数据
*putdate: 转换签订字形数据
*width: 字宽
*high:  字高
*style: 转换功能的选择, HZ_MODE00 0//竖置横排(Y)
                        HZ_MODE01 1//竖置竖排(Z)
                        HZ_MODE10 2//横置横排(W)
                        HZ_MODE11 3//横置竖排(X)
                        HZ_MODE4  4//Y-->W

*使用示例:lcdram_map(getdate,putdate,32,32,HZ_MODE4);//将32x32的字形数据由Y
排置(竖置横排)转换成W排置(横置横排)保存在getdate数组中
*/
void lcdram_map( u8 *getdate,u8 *putdata, u8 width, u8 high, u8 style )
{
  u16 i,j,hbyte,wbyte;
  unsigned char i_8,j_8;
  wbyte = (width+7)/8;
  hbyte = (high+7)/8;
   //--------------------------------------------------------------------------
   // Y--> W; Y-->X; Y-->Z;
   //--------------------------------------------------------------------------
  if( style == HZ_MODE4 )   //竖置横排 转 横置横排 ( Y-->W )
  {
    for( i = 0; i < high; i++ )
        for( j = 0; j < width; j++ )
        {
            i_8 = i/8;
            if((*(putdata+i_8*width+j)&(0x01<<(i%8))) > 0)
                getdate[wbyte*i+j/8] |= (0x80>>(j%8));
            else
                getdate[wbyte*i+j/8] &= (~(0x80>>(j%8)));
        }
  }
   //--------------------------------------------------------------------------
   // W--> Y;  W-->Z;W-->X;
   //--------------------------------------------------------------------------

  if( style == HZ_MODE00 )  //竖置横排 (W--> Y)
  {
      for( i = 0; i < high; i++ )
          for( j = 0; j < width; j++ )
          {
            i_8 = i/8;
            if((*(putdata+wbyte*i+j/8)&(0x80>>(j%8))) > 0)
                getdate[i_8*width+j] |= (0x01<<(i%8));
            else
                getdate[i_8*width+j] &= (~(0x01<<(i%8)));
          }
  }
  else if(style == HZ_MODE01)   //竖置竖排 (W-->Z)
  {
    for( i = 0; i < high; i++ )
      for( j = 0; j < width; j++ )
      {
        if((*(putdata+wbyte*i+j/8)&(0x80>>(j%8))) > 0)
            getdate[j*hbyte+i/8] |= (0x01<<(i%8));
        else
            getdate[j*hbyte+i/8]  &= (~(0x01<<(i%8)));
      }
  }
  else if(style == HZ_MODE11)//横置竖排 (W-->X)
  {
    for( i = 0; i < high; i++ )
      for( j = 0; j < width; j++ )
      {
        j_8 = j/8;
        if((*(putdata+wbyte*i+j/8)&(0x80>>(j%8))) > 0)
            getdate[j_8*high+i] |= (0x80>>(j%8));
        else
            getdate[j_8*high+i] &= (~(0x80>>(j%8)));
      }
  }
  else if(style == HZ_MODE10)//横置横排 做镜像(W-->W')
  {
    for( i = 0; i < high; i++ )
      for( j = 0; j < width; j++ )
      {
        if((*(putdata+wbyte*i+j/8)&(0x80>>(j%8)))>0 )
            *(getdate+wbyte*i+(width-j)/8) |=(0x80>>((width-j)%8));
        else
            *(getdate+wbyte*i+(width-j)/8) &=~(0x80>>((width-j)%8));
      }
  }
}

转换代码的使用方法示例如下: 提前定义两个同样大小的数组,先使用pBits存放从字库芯片里读取到的数据。然后使用get_pBits在lcdram_map中获取转换后的数据,接着再通过竖置横排的显示函数进行显示即可。

char pBits[16] = {0};
char get_pBits[16] = {0};
ASCII_GetData(0x41, ASCII_16_A, pBits); //按照16x16大小获取
void lcdram_map(get_pBits, pBits, 16, 16, HZ_MODE00);  //开始转换
Display_Y(pBits, 100, 100, 16, 16);  //显示实际宽度

4.9.一维码获取及显示

打开“05_屏幕显示资料\一维码调用示例.c”,将代码拷贝到工程中即可。首先用户需要实现SPI_Address和r_dat_bat两个函数,参考代码如下。SPI_Address函数中,用户只需将发送字节的函数替换成自己的SPI发送函数即可,r_dat_bat函数的实现同理,并将CS拉高和拉低的代码替换成自己的即可。具体的一维码代码接口及调用示例如下。

void SPI_Address(unsigned char AddH,unsigned char AddM,unsigned char AddL)
{
    Send_Byte(AddH);    //更换成开发者的spi发送字节函数;
    Send_Byte(AddM);
    Send_Byte(AddL);
}
/******************************************************************************/
//                    Get N bytes sub-pro  (STM8,STM32,51)
                   //
/******************************************************************************/
//客户自己实现,从address地址读取len个字节的数据并存入到pBuff数组当中
unsigned char r_dat_bat(unsigned long address,unsigned long DataLen,unsigned char *pBuff)
{
    unsigned long i;
    unsigned char addrHigh;
    unsigned char addrMid;
    unsigned char addrLow;
    addrHigh=address>>16;
    addrMid=address>>8;
    addrLow=(unsigned char)address;
    Rom_csL;          //更换成开发者的cs拉低,片选选中字库芯片
    Send_Byte(0x03);
    //更换成开发者的spi发送字节函数;普通读取首先送0X03,然后发送地址高八位addrHigh,中八位addrMid,低八位addrLow。
    SPI_Address(addrHigh,addrMid,addrLow);
    for(i=0;i<DataLen;i++)
    *(pBuff+i)=Get_Byte();
    Rom_csH;        //这里更换成开发者的cs拉高,片选选中字库芯片
    return 0;
}
/**
 * @brief 条形码 EAN13 :将数组条形码转为对应条形码图形地址
 * @param bar_num_p 条形码 EAN13 数字数组指针,数组包含13个数字。
 * @return unsigned long * 数组条形码[13]对应条形码图形地址
 */
unsigned long *BAR_CODE_EAN13(unsigned char *bar_num_p);
/* code_128_type */
#define CODE_128_A         (1)
#define CODE_128_B         (2)
#define CODE_128_C         (3)
/**
 * @brief GB/T 18347-2001(CODE128)条形码:将数组条形码转为对应条形码图形地址
 * @param bar_num_p 条形码数字数组指针,bar_num_p[4]数组包含4个条形码ASCII符(数组取值为0~94)。
 * @param bar_num_len 条形码数字数组 的长度
 * @param code_type 起始符 编码格式类型 @ref code_128_type
 * @return unsigned long * 对应条形码图形地址
 */
unsigned long *BAR_CODE128(unsigned char *bar_num_p, unsigned char bar_num_len, unsigned char code_type);

/**************************************
arr[] : 13个数组
**************************************/
void  BAR_codeEAN13(unsigned int x, unsigned int y, unsigned char arr[])
{
  unsigned char buff_temp[60];
  unsigned long *tmp ;
  unsigned char i = 0;

  tmp = BAR_CODE_EAN13(arr);
  for(i = 0;i < 13;i++)
  {
    memset(buff_temp,0,sizeof(buff_temp));
    r_dat_bat(*tmp, 54, buff_temp);
    DisZK_DZ_W(x,y,16,28,BLACK,WHITE,&buff_temp[2],1);
    x+=buff_temp[1];
    tmp++;
  }
}
/**************************************
ascii_arr[] : 字符数组 ASCII
arr_len:  字符数组的长度
1、Code 39码只接受如下43个有效输入字符:
2、26个大写字母(A – Z);
3、十个数字(0 – 9);
连接号(-),句号(.),空格,美圆符号($),斜扛(/),加号(+)以及百分号(%)。其余的输入将被忽略。

**************************************/
void BAR_codeEAN39(unsigned int x, unsigned int y, unsigned char ascii_arr[], unsigned int arr_len) {
  unsigned char buff_tmp[50], i;

  for(i=0; i<arr_len; i++) {
    BAR_CODE39(ascii_arr[i], buff_tmp);
    DisZK_DZ_W(x,y,16,22,BLACK,WHITE,&buff_tmp[0],1);
    x+=11;
  }
}
/**************************************
arr[]: 条形码编码 整数
arr_len: 数组长度
flag: 起始符有3种模式
  当flag=1时为Code-128-A;
  当flag=2时为Code-128-B;
  当flag=3时为Code-128-C;
Code128编码表参考: https://blog.csdn.net/rodulf/article/details/51276820
**************************************/
void BAR_codeEAN128(unsigned int x, unsigned y, unsigned char arr[], unsigned int arr_len, unsigned char flag) {
  unsigned char buff_tmp[60];
  unsigned long *tmp;
  unsigned char i=0;
  const unsigned char verify = 3;

  tmp = BAR_CODE128(arr, arr_len, flag);

  for(i=0; i<arr_len+verify; i++) {
    memset(buff_tmp, 0, sizeof(buff_tmp));
    r_dat_bat(*tmp, 40, buff_tmp);
    DisZK_DZ_W(x, y, 16, 20, BLACK, WHITE, &buff_tmp[0], 1);  //显示函数,根据实际修改, 参考显示函数文件
    x+=11;  //可修改
    tmp++;
  }
}

一维码的显示函数可以参考“05_屏幕显示资料\显示函数.txt”中的代码。具体代码如下:

/*横置横排打点函数 ----------------------------------------------------------------*/
/*
  Xpos,Ypos:显示的起点坐标
  data:显示的数据
  charColor:字体颜色
  bkColor:背景颜色
  sizeType:放大倍数,默认为1
*/
void WriteData( uint16_t Xpos, uint16_t Ypos, uint8_t data, uint16_t charColor, uint16_t bkColor,u8 sizeType)
{
    uint16_t j,i;
    unsigned char count=0;
    for( j=0; j<8; j++ )
    {
        if( ((data >> (7-j))&0x01)== 0x01 ){
            for(count=0;count<sizeType;count++){
              for(i=0;i<sizeType;i++){
                LCD_SetPoint( Xpos + sizeType*j+count, Ypos+i, charColor );//修改此函数
              }
            }
        }
        else{
            for(count=0;count<sizeType;count++){
              for(i=0;i<sizeType;i++){
                LCD_SetPoint( Xpos + sizeType*j+count, Ypos+i, bkColor );//修改此函数
              }
            }
        }
    }
}
/*竖置横排打点函数 -----------------------------------------------------------------*/
/*
  Xpos,Ypos:显示的起点坐标
  data:显示的数据
  charColor:字体颜色
  bkColor:背景颜色
  sizeType:放大倍数,默认为1
*/
void WriteDataY( uint16_t Xpos, uint16_t Ypos, uint8_t data, uint16_t charColor, uint16_t bkColor,u8 sizeType)
{
    uint16_t i,j;
    unsigned char count = 0;
    for( j = 0; j < 8; j++ )
    {
        if( ((data >> (7-j))&0x01)== 0x01 ){
          for(count = 0;count<sizeType;count++){
            for(i = 0;i < sizeType;i++)
            {
                LCD_SetPoint( Xpos+i, Ypos- sizeType*j+count, charColor );//修改此函数
            }
          }
        }
        else{
          for(count=0;count<sizeType;count++){
            for(i=0;i<sizeType;i++)
            {
              LCD_SetPoint( Xpos+i, Ypos- sizeType*j+count, bkColor );//修改此函数
            }
          }
        }
    }
}
/******* 竖置横排显示函数 ***********/
/*
  Xpos,Ypos:显示的起点坐标
  W:字体显示的宽
  H:字体显示的高
  charColor:字体颜色
  bkColor:背景颜色
  DZ_Data:显示的字体数据
  sizeType:放大倍数,默认为1
*/
void DisZK_DZ_Y(uint16_t Xpos, uint16_t Ypos, uint16_t W,uint16_t H, uint16_t charColor, uint16_t bkColor,uint8_t*DZ_Data,u8 sizeType)
{
  static u16 Vertical,Horizontal;
  u32 bit = 0;
  u8 temp = 0;
  Vertical = Ypos + 7*sizeType;
  Horizontal = Xpos;
  for(bit = 0;bit < ((H+7)/8*W);bit++) //data sizeof (byte)
  {
        if((bit%W==0)&&(bit>0))//W/8 sizeof
        {
            temp += 8 * sizeType;
            Horizontal = Xpos;
            Vertical = Ypos + 7*sizeType + temp;
            //Vertical = Ypos + 7*sizeType + temp;
        }
        else if(bit > 0)
        {
            Horizontal += sizeType;
            Vertical = Ypos+7*sizeType + temp;
        }

        WriteDataY(Horizontal,Vertical,DZ_Data[bit],charColor,bkColor,sizeType);
  }
}
/********º横置横排显示函数************/
/*
  Xpos,Ypos:显示的起点坐标
  W:字体显示的宽
  H:字体显示的高
  charColor:字体颜色
  bkColor:背景颜色
  DZ_Data:显示的字体数据
  sizeType:放大倍数,默认为1
*/
void DisZK_DZ_W(uint16_t Xpos, uint16_t Ypos, uint16_t W, uint16_t H, uint16_t charColor, uint16_t bkColor, uint8_t*DZ_Data, u8 sizeType)
{
  static u16 Vertical,Horizontal;
  u32 bit=0;
  Vertical=Ypos;
  Horizontal=Xpos;
  for(bit=0;bit<((W+7)/8*H);bit++) //data sizeof (byte)
  {
        if((bit%((W+7)/8)==0)&&(bit>0))//W/8 sizeof
        {
            Vertical+=sizeType;
            Horizontal=Xpos;
        }
        else if(bit>0)
          Horizontal+=sizeType*8;

    WriteData(Horizontal,Vertical,DZ_Data[bit],charColor,bkColor,sizeType);
  }
}
/*使用示例 */
unsigned char code[13]={6,9,0,1,6,8,7,5,8,2,4,4,2};
unsigned char code1[13]={6,9,3,8,7,0,9,3,0,0,4,4,9};
unsigned char code3[13] = {16,33,45,31,27,26,40};
BAR_codeEAN13(0, 0, code);
BAR_codeEAN13(0, 0, code1);
BAR_codeEAN128(0, 200, code3, 7, 1);

4.10.字符串及中英文混合显示

首先定义一个数组,用来存放字符的编码。字符的编码查询可以使用高通的MindCraft(高通智匠)应用软件中的“字符编码查询器”中进行编码查询。例如查询“我爱中国”: figure 27figure 27

大扩号内,从左到右,每两个字节表示一个字符。

uint16_t buff[] = {0xCED2,0xB0AE,0xD6D0,0xB9FA};
uint8_t msb = 0, lsb = 0, xpos = 100, ypos = 100;
for(int i = 0; i < sizeof(buff)/sizeof(buff[0]); i++)
{
    msb = buff[i]>>8;
    lsb = buff[i]&0xff;
    gt_16_GetData(msb, lsb, pBits);
    Display_W(pBits, xpos, ypos, 16, 16);
    xpos += 16;
}

注意:显示字符串不推荐在定义的数组里存放文字。例如:

unsigned char buff = "我爱中国";

因为高通字库的中文使用的是GB编码格式,但有可能用户使用的编译器在编译的过程中可能会将这些中文编译成Unicode编码或者UTF-8编码。最后导致无法正常显示。 中英文混合显示示例,如果显示的字不是自己想要的字,则需要查看编译器默认编码是否支持中文。

void show_char(char * text, unsigned int x, unsigned int y, unsigned int size)
{
    unsigned int * tmp;
    while (*text != '\0')//判断是否是结尾
    {
        memset(pBits, 0, 192 * 192 / 8);
        if ((*text) < 0x80) {  //ASCII 码 *text是ASCII码
            tmp =
                get_Font_Gray(pBits, (*text) < 0x80 ? VEC_FT_ASCII_STY : VEC_SONG_STY, *text, size,
                    size);    //示例, 按实际ASCII调用函数名进行修改
            Gray_Display_hz(pBits, x, y, tmp[0], size, tmp[1]);
            x += size + 2;
            text++; //ASCII 码一个字节
        }
        else {  //汉字编码*text是汉字编码的高位,*(text+1)是汉字编码的低位
            tmp =
                get_Font_Gray(pBits, (*text) < 0x80 ? VEC_FT_ASCII_STY : VEC_SONG_STY, *text * 256 + *(t
                    ext + 1), size, size);  //示例, 按实际中文调用函数名进行修改
            Gray_Display_hz(pBits, x, y, tmp[0], size, tmp[1]);
            x += size + 2;
            text++;//汉字两个字节
            text++;
        }
    }
}

char * zf_code = "高通字库ABCabc";
show_char(zf_code, 0, 0, 24);//在0,0坐标显示字符串"高通字库ABCabc",点阵大小为24点

4.11.字节逆序

如果用户使用的MCU存储的方式是大端的存储方式(数据的低位存储在地址的高位),那么需要提前进行一次转换。转换函数可以在“05_屏幕显示资料\字节逆序.c”可以查找到。具体代码如下:

void byte_cov(unsigned char * p, unsigned long len)
{
    unsigned long i;
    unsigned char tem;
    for (i = 0;i < len;i++) {
        tem = p[i];
        p[i] = ((tem & 0x01) << 7) | ((tem & 0x02) << 5) | ((tem & 0x04) << 3) | ((tem & 0x08) << 1)  \
            | ((tem & 0x10) >> 1) | ((tem & 0x20) >> 3) | ((tem & 0x40) >> 5) | ((tem &0x80) >> 7);
    }
}