ARM技术论坛
直播中

vinww特烦恼

8年用户 1121经验值
擅长:存储技术
私信 关注
[经验]

如何利用Arm-2D函数去实现一种显示汉字的设计呢

玩过Arm-2D的同学都知道,官方的lcd_printf函数只提供了一种6 * 8点阵的字库,而且是不支持显示汉字的。想想也是,Arm-2D是给小资源单片机提供显示驱动的,一个汉字字库需要的flash少则也得几十上百KB。
可是,我们又有显示汉字的需求怎么办?
贴图片好像也很占空间,
有没有又能省空间又可以显示汉字的方法呢?
哈哈哈,这种两全其美的事应该没有,
不过,
如果我们只是显示几个特定的汉字而不是整个字库的话倒是可以,大家应该已经想到了,那就是把这几个特定的汉字做成字库,然后调用lcd_printf函数显示。为了和官方的函数名有所区别,我们就起名为 My_lcd_printf。
以后就可以这样显示汉字了,如下所示

我们的字库虽然小,但是My_lcd_printf函数也可以实现 %s 的功能。

汉字字库制作
那我们先开始制作字库。字符取模软件的使用我就不讲了,网上有很多。字符取模可以分为横向取模、竖向取模等,为了和官方保持一致,我们采用横向取模。
好,我们讲一下怎么把一个汉字转换成十六进制数组,如下图

横向取模原理如下,

这样一行一行的转换,最后保存到数组中就是字库了。

用子tile表示资源
接下来讲一下待会要用到的Arm-2D的知识,那就是上一篇我们没有讲到的子Tile的另一种用法,资源也可以用子tile来建立(即取一个大图的局部作为新的资源)
如下图所示:


父Tile和子Tile都可以当做是图片资源使用,但是他们是共用同一张图片资源(节省空间)。
使用方法如下所示:

父Tile和子Tile共用同一张图片资源c_bmpColours[]
此时子Tile的bDerivedResource属性必须为true
bHasEnforcedColour属性也设置为true,且和父Tile的颜色信息要一致。
这就是Arm-2D的设计思想,它把所有的资源都看成是Tile,字库也是一个Tile,而且是一个大Tile,而里边的一个字符就是他的一个子Tile,所以会用到上面讲到的子Tile用法。
如下图:


有了这个Tile思想,还可以把一个汉字拆成多个。

用Arm-2D显示汉字程序实现
好了,那我们现在就去实现我们的My_lcd_printf函数,如下

那接下来我们实现lcd_puts_chinese函数,如下

最后实现我们的mylcd_draw_char函数,如下
static void mylcd_draw_char(int16_t iX, int16_t iY, char chChar)
{   // 显示到默认缓冲区
    //! use default frame buffer
    arm_2d_tile_t *ptFrameBuffer = (arm_2d_tile_t *) -1;
    // 父Tile,汉字字库资源
    const static arm_2d_tile_t s_tileFont16x16 = {
        .tRegion = {
            .tSize = {
                .iWidth = 16,
                .iHeight = 16 * 9,
            },
        },
        .tInfo = {
            .bIsRoot = true,
            .bHasEnforcedColour = true,
            .tColourInfo = {
                .chScheme = ARM_2D_COLOUR_BIN,
            },
        },
        .pchBuffer = (uint8_t *)Font_16x16_h,
    };   
    // 子Tile,要显示的汉字
    static arm_2d_tile_t s_tileChar = {
      .tRegion = {
        .tSize = {
          .iWidth = 16,
          .iHeight = 16,
        },
      },      
      .tInfo = {
          .bIsRoot = false,
          .bDerivedResource = true,
          .bHasEnforcedColour = true,
          .tColourInfo = {
              .chScheme = ARM_2D_COLOUR_BIN,
          },
        },            
        .ptParent = (arm_2d_tile_t *)&s_tileFont16x16,
    };
    s_tileChar.tRegion.tLocation.iY = chChar * 16;
    // 在屏幕中显示的区域
    arm_2d_region_t tDrawRegion = {
        .tLocation = {.iX = iX, .iY = iY},
        .tSize = s_tileChar.tRegion.tSize,
    };
    // 绘制汉字   
    arm_2d_rgb16_draw_pattern(  &s_tileChar,
          ptFrameBuffer,
          &tDrawRegion,
              ARM_2D_DRW_PATN_MODE_COPY           
          //| ARM_2D_DRW_PATN_MODE_NO_FG_COLOR
          //| ARM_2D_DRW_PATN_MODE_WITH_BG_COLOR
          //| ARM_2D_DRW_PATH_MODE_COMP_FG_COLOUR
          ,
          GLCD_COLOR_GREEN,
          GLCD_COLOR_BLACK);   
}
程序中用到的结构体和字库我也贴到下边,如下


小结

lcd_puts_chinese函数里计算字符显示坐标
mylcd_draw_char函数里设置字库资源和要显示的字符资源(在这里设置我们的字库)
调用arm_2d_rgb16_draw_pattern接口绘制一个字符(可以修改字体颜色)
GLCD_FONT结构体很重要,修改字库就是在这个类型的变量中修改
补充
我们现在去看看Arm-2D的lcd_puts函数是怎么实现的吧,顺便也和arm公司的工程师学些编程的技巧,(* ̄︶ ̄)
lcd_puts函数,如下
void lcd_puts(const char *str)
{
    while(*str) {
    ||处理特殊字符
        if (*str == 'r') {
            s_tLCDTextControl.tTextLocation.chX = 0;
        } else if (*str == 'n') {
            s_tLCDTextControl.tTextLocation.chX = 0;
            s_tLCDTextControl.tTextLocation.chY++;
        } else if (*str == 't') {
            s_tLCDTextControl.tTextLocation.chX += 8;
            s_tLCDTextControl.tTextLocation.chX &= ~(_BV(3)-1);
            if (    s_tLCDTextControl.tTextLocation.chX * GLCD_Font_6x8.width
                >=  s_tLCDTextControl.tRegion.tSize.iWidth ) {
                s_tLCDTextControl.tTextLocation.chX = 0;
                s_tLCDTextControl.tTextLocation.chY++;
            }
        }else if (*str == 'b') {
            if (s_tLCDTextControl.tTextLocation.chX) {
                s_tLCDTextControl.tTextLocation.chX--;
            }
        } else {
       ||计算字符显示坐标
            int16_t iX = s_tLCDTextControl.tTextLocation.chX * GLCD_Font_6x8.width;
            int16_t iY = s_tLCDTextControl.tTextLocation.chY * GLCD_Font_6x8.height;
       ||显示一个字符
            lcd_draw_char(  s_tLCDTextControl.tRegion.tLocation.iX + iX,
                            iY,
                            *str);
       ||右移一个字符的坐标,方便下个字符显示                     
            s_tLCDTextControl.tTextLocation.chX++;
            ||判断是否显示到屏幕外面
            if (    s_tLCDTextControl.tTextLocation.chX * GLCD_Font_6x8.width
                >=  s_tLCDTextControl.tRegion.tSize.iWidth ) {
                s_tLCDTextControl.tTextLocation.chX = 0;
                ||显示到一行的末尾后换行
                s_tLCDTextControl.tTextLocation.chY++;
                if (    s_tLCDTextControl.tTextLocation.chY * GLCD_Font_6x8.height
                    >= s_tLCDTextControl.tRegion.tSize.iHeight) {
                    s_tLCDTextControl.tTextLocation.chY = 0;
                }
            }
        }
  ||显示完一个字符,指向下一个字符      
        str++;
    }
}
是不是看到了我们熟悉的C语言可变长参数使用的宏(__va_list、va_start、va_end)
把格式化好的字符串放到了s_chBuffer里,然后调用lcd_puts_chinese函数。
在此函数中计算字符在屏幕中显示的坐标(iX,iY),然后传人mylcd_draw_char函数中。
注意我们多了一个函数 get_chinese_index,因为我们的汉字字库只有几个字而不是整个汉字字库,所以获取一个汉字在字库中的位置需要自己实现,正常整个汉字字库编码是按一定顺序排放的,下标很容易计算。
是不是看到了熟悉的arm_2d_tile_t类型了
首先找到了父Tile(s_tileFont16x16 ),它是整个字库资源,字库数组为pchBuffer指向的Font_16x16_h
s_tileChar 就是我们今天的主角,要显示的字符子Tile,
tColourInfo 颜色信息和父Tile保持一致。
最后调用arm_2d_rgb16_draw_pattern接口绘制一个字符
这个函数比我们的长多了,不过最主要的还是调用lcd_draw_char函数来显示一个字符。

更多回帖

发帖
×
20
完善资料,
赚取积分