玩过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函数来显示一个字符。