一、字符顯示機(jī)制
1. 字符顯示原理
要在LCD上顯示一個(gè)字符,需要以下兩步:
- ① 占據(jù)屏幕上的一塊地方,大小由字體大小說了算;
- ② 在占據(jù)的地方上依次控制每個(gè)像素點(diǎn)是否顯示。
比如中文字符24×24字體大小表示水平需要24個(gè)像素點(diǎn)、垂直需要24個(gè)像素點(diǎn)。
在這塊24×24的地盤上,每個(gè)像素點(diǎn)是否顯示由字模說了算,字模中的每1位數(shù)據(jù)表示一個(gè)像素點(diǎn),如果該位為0則表示此處像素點(diǎn)不顯示、為1則表示顯示(陰碼規(guī)則)。
所以,字符顯示的函數(shù)只需要基于打點(diǎn)函數(shù)實(shí)現(xiàn)即可。在程序中 逐位讀取字模 ,如果該位為0則該點(diǎn)寫入背景顏色,如果該位為1則寫入前景顏色。
2. 字模生成(點(diǎn)陣字體)
正因?yàn)長(zhǎng)CD顯示字符的機(jī)制,所以該種字體被稱為點(diǎn)陣字體。
為了方便大家理解原理,這里我首先使用小工具生成字模。
設(shè)置工具的字模生成規(guī)則如下,陰碼、逐行式、順向取模(圖中有誤)、C51格式:
接著生成漢字的字模:
接著復(fù)制生成數(shù)據(jù),在程序中定義為一個(gè)二維數(shù)組作為字庫,第一個(gè)值表示字庫中的元素個(gè)數(shù),可以由編譯器自行判斷,第二個(gè)值是每個(gè)元素的大小,必須要指明,這樣我們就可以用 hz_16x16[0]
來找到漢字 ”春”在字庫中的位置:
#ifndef _HZ_H_
#define _HZ_H_
const unsigned char hz_16x16[][32] = {
{0x01,0x00,0x01,0x00,0x7F,0xFC,0x01,0x00,0x3F,0xF8,0x02,0x00,0xFF,0xFE,0x08,0x20,
0x10,0x10,0x2F,0xE8,0xC8,0x26,0x08,0x20,0x0F,0xE0,0x08,0x20,0x08,0x20,0x0F,0xE0},/*"春",0*/
/* (16 X 16 , 宋體 )*/
};
#endif /* _HZ_H_*/
我們選擇的字體是16x16,所以:
- 水平方向有16個(gè)像素點(diǎn), 每個(gè)像素點(diǎn)占1位,需要16bit,兩個(gè)字節(jié)來表示一行 ;
- 垂直方向有16行,所以整體有2*16=32個(gè)字節(jié);
此處需要注意,軟件每16個(gè)字節(jié)生成一對(duì)花括號(hào),這會(huì)影響二維數(shù)組取值,需要將中間多余的花括號(hào)去除。
二、如何將字符顯示到LCD
1. 打點(diǎn)函數(shù)支持
字符顯示需要打點(diǎn)函數(shù)的支持,這里我使用RGB-LCD的打點(diǎn)函數(shù):
void lcd_draw_point(uint16_t x, uint16_t y, uint16_t color);
2. 字庫支持
引入剛剛我們創(chuàng)建二維數(shù)組的頭文件:
#include "hz.h"
3. 讀取字模顯示
void lcd_show_chinese(uint16_t x, uint16_t y, char ch, uint16_t back_color, uint16_t font_color, uint8_t font_size)
{
uint16_t i, j;
uint16_t x_pos, y_pos, size, font_width, font_height;
uint8_t *font_ptr;
uint8_t bit_width, temp;
if((x > (LCD_WIDTH - font_size)) || (y > (LCD_HEIGHT - font_size))) {
return;
}
x_pos = x;
y_pos = y;
font_height = font_size;
font_width = font_size;
bit_width = 8;
size = (font_width / 8 + ((font_width % 8) ? 1 : 0)) * font_height;
font_ptr = (uint8_t*)&hz_16x16[ch];
for (i = 0; i < size; i++) {
temp = *(font_ptr + i);
for (j = 0; j < bit_width; j++) {
if(temp & 0x80){
lcd_draw_point(x_pos, y_pos, font_color);
} else {
lcd_draw_point(x_pos, y_pos, back_color);
}
temp < <= 1;
x_pos++;
}
if (x_pos >= (x + font_width)) {
y_pos++;
x_pos = x;
}
}
}
實(shí)現(xiàn)之后記得聲明:
/**
* @brief Show a chinese char.
* @param[in] x1 horizontal start position.
* @param[in] y1 vertical start position.
* @param[in] x2 horizontal end position.
* @param[in] y2 vertical end position.
* @param[in] ch offset in hz library.
* @param[in] back_color rgb565
* @param[in] font_color rgb565
* @param[in] font_size support 16.
* @return None
* @note This function need hz library.
*/
void lcd_show_chinese(uint16_t x, uint16_t y, char ch, uint16_t back_color, uint16_t font_color, uint8_t font_size);
4. 測(cè)試顯示
在main函數(shù)中調(diào)用:
lcd_show_chinese(0, 0, 0, BLACK, GREEN, 16);
編譯、下載即可看到效果
三、小字庫的使用
1. 制作小字庫
在項(xiàng)目中,我們需要顯示一些中文,但也沒必要將整個(gè)漢字庫包含進(jìn)來,所以經(jīng)常是制作本項(xiàng)目專屬小字庫。
這里取模工具使用安富萊電子的MakeDot,非常方便:
這里選擇了輸入文字重排,可以去除重復(fù)漢字,減少字庫體積。
將生成的整個(gè)數(shù)組復(fù)制作為之前我們創(chuàng)建數(shù)組 hz_16x16
的內(nèi)容:
#ifndef _HZ_H_
#define _HZ_H_
const unsigned char hz_16x16[][32] = {
{0x00,0x00,0x10,0x00,0x10,0x00,0x10,0x00,0x10,0x00,0x10,0x00,0x10,0x00,0x10,0x00,// ! //
0x10,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x10,0x00,0x10,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x20,0x00,0x17,0xFE,0x10,0x08,0x80,0x08,0x43,0xC8,0x42,0x48,0x12,0x48,// 河 //
0x12,0x48,0x22,0x48,0xE3,0xC8,0x22,0x48,0x20,0x08,0x20,0x08,0x20,0x28,0x00,0x10},
{0x10,0x00,0x10,0x00,0x10,0x00,0x10,0x7C,0xFE,0x44,0x12,0x44,0x12,0x44,0x12,0x44,// 加 //
0x12,0x44,0x12,0x44,0x12,0x44,0x12,0x44,0x22,0x44,0x22,0x7C,0x4A,0x44,0x84,0x00},
{0x01,0x00,0x01,0x00,0xFF,0xFE,0x01,0x00,0x01,0x00,0x7F,0xFC,0x48,0x24,0x44,0x44,// 南 //
0x4F,0xE4,0x41,0x04,0x41,0x04,0x5F,0xF4,0x41,0x04,0x41,0x04,0x41,0x14,0x40,0x08},
{0x00,0x40,0x20,0x40,0x10,0x40,0x10,0x40,0x87,0xFC,0x44,0x44,0x44,0x44,0x14,0x44,// 油 //
0x14,0x44,0x27,0xFC,0xE4,0x44,0x24,0x44,0x24,0x44,0x24,0x44,0x27,0xFC,0x04,0x04},
};
#endif /* _HZ_H_*/
這樣就完成了我們整個(gè)項(xiàng)目小字庫的制作,同樣的方法,我們?cè)僦谱鞒鰄z_24x24字庫,hz_32x32字庫。
const unsigned char hz_24x24[][72] = {
//...
};
const unsigned char hz_32x32[][128] = {
};
2. 使用小字庫
進(jìn)一步完善中文顯示函數(shù),找到獲取字模數(shù)據(jù)的代碼:
font_ptr = (uint8_t*)&hz_16x16[ch]
優(yōu)化為:
switch (font_size) {
case 16:
font_ptr = (uint8_t*)&hz_16x16[ch];
break;
case 24:
font_ptr = (uint8_t*)&hz_24x24[ch];
break;
case 32:
font_ptr = (uint8_t*)&hz_32x32[ch];
break;
default:
return;
}
在main函數(shù)中進(jìn)一步添加測(cè)試程序:
lcd_show_chinese(0, 0, 1, BLACK, RED, 16); // 河
lcd_show_chinese(16, 0, 3, BLACK, RED, 16); // 南
lcd_show_chinese(32, 0, 2, BLACK, RED, 16); // 加
lcd_show_chinese(48, 0, 4, BLACK, RED, 16); // 油
lcd_show_chinese(64, 0, 0, BLACK, RED, 16); // !
lcd_show_chinese(0, 20, 1, BLACK, RED, 24);
lcd_show_chinese(24, 20, 3, BLACK, RED, 24);
lcd_show_chinese(48, 20, 2, BLACK, RED, 24);
lcd_show_chinese(72, 20, 4, BLACK, RED, 24);
lcd_show_chinese(96, 20, 0, BLACK, RED, 24);
lcd_show_chinese(0, 60, 1, BLACK, RED, 32);
lcd_show_chinese(32, 60, 3, BLACK, RED, 32);
lcd_show_chinese(64, 60, 2, BLACK, RED, 32);
lcd_show_chinese(96, 60, 4, BLACK, RED, 32);
lcd_show_chinese(128, 60, 0, BLACK, RED, 32);
四、進(jìn)一步的優(yōu)化
本文中講述的僅僅是最基本的中文顯示方法,還可以進(jìn)一步進(jìn)行優(yōu)化。
① 字符偏移優(yōu)化
這里使用小字庫不太方便的點(diǎn)是,字符在字庫中的偏移位置需要自己控制,如果想自動(dòng)控制就涉及編碼問題,實(shí)現(xiàn)后可以直接寫漢字字符串來顯示。
② 字庫優(yōu)化
項(xiàng)目中比較穩(wěn)妥的辦法是使用全字庫,片內(nèi)Flash肯定不夠用,通常的做法是外掛一片SPI Flash,將整個(gè)全字庫文件燒寫到Flash里,使用的時(shí)候讀出來即可。
③ 字體優(yōu)化
本文中我們?nèi)∧r(shí)都使用的是宋體,好不容易建立的字庫,可能某天老板突然過來說要換成什么奇奇怪怪的字體,那不是完蛋了~
所以在實(shí)現(xiàn)的時(shí)候我們要考慮在SPI Flash上建立文件系統(tǒng),直接使用文件,方便字庫文件替換。
評(píng)論
查看更多