第三階段應用層——1.3 數碼相冊—英文和漢字的點陣顯示

數碼相冊——英文和漢字的點陣顯示

  • 硬件平臺:韋東山嵌入式Linxu開發板(S3C2440.v3)
  • 軟件平臺:運行於VMware Workstation 12 Player下UbuntuLTS16.04_x64 系統
  • 參考資料:《嵌入式Linux應用開發手冊》、《嵌入式Linux應用開發手冊第2版》
  • 開發環境:Linux 3.4.2內核、arm-linux-gcc 4.3.2工具鏈


一、前言

之前的博文中介紹了數碼相冊的框架以及字符編碼的知識,在這篇中,編程實現在開發板上顯示英文和中文。
此處主要實現功能,對於程序的框架考慮可能不足。

二、編程實現

1、打開LCD設備

	static int fd_fb;
	
	/* 打開設備:支持讀寫 */
	fd_fb = open("/dev/fb0", O_RDWR);
	if (fd_fb < 0) {
		printf("can not open /dev/fb0 , err code :%d\n", fd_fb);
		return -1;
	}

2、獲取LCD信息

	static struct fb_var_screeninfo var;
	static struct fb_fix_screeninfo fix;
	
	/* 獲得可變信息 */
	if (ioctl(fd_fb, FBIOGET_VSCREENINFO, &var)) {
		printf("can not get var\n");
		return -1;
	}

	/* 獲得固定信息 */
	if (ioctl(fd_fb, FBIOGET_FSCREENINFO, &fix)) {
		printf("can not get var\n");
		return -1;
	}

3、映射Framebuffer到內存

	/* 直接映射到內存的Framebuffer */
	screen_size = var.xres * var.yres * var.bits_per_pixel / 8;	// 屏幕總像素所佔的字節數
	line_width   = var.xres * var.bits_per_pixel / 8;	// 每行像素所佔的字節數
	pixel_width  = var.bits_per_pixel / 8;	// 每個像素所佔字節數
	
	fbmem = (unsigned char *)mmap(NULL, screen_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd_fb, 0);
	if (fbmem == (unsigned char *)-1) {
		printf("can not mmap\n");
		return -1;
	}

4、打開漢字庫並獲取漢字庫文件的大小

  • 需要在可執行程序的當前目錄下放置文件名爲HZK16的漢字庫
  • 使用fstat()函數來獲取文件名爲HZK16的漢字庫的相關信息,關注大小
1、定義函數 int fstat(int fildes,struct stat *buf);
2、函數說明 fstat()用來將參數fildes所指的文件狀態,複製到參數buf所指的結構中(struct stat)
	/* 打開漢字庫 */
	fd_hzk16 = open("HZK16", O_RDONLY);
	if (fd_hzk16 < 0) {
		printf("can not open /dev/fb0 , err code :%d\n", fd_hzk16);
		return -1;
	}
	
	/* 計算字庫文件大小 */
	if (fstat(fd_hzk16, &hzk_stat)) {
		printf("can not get fstat\n");
		return -1;
	}

5、把漢字庫直接映射到內存中

  • 目的:方便的使用漢字庫中的信息
	/* 漢字庫直接映射到內存 */
	hzkmem = (unsigned char *)mmap(NULL, hzk_stat.st_size, PROT_READ, MAP_SHARED, fd_hzk16, 0);
	if (hzkmem == (unsigned char *)-1) {
		printf("can not mmap for hzk16err code :%d\n", fd_hzk16);
		return -1;
	}

6、顯示英文與中文

	/* 清屏 */
	memset(fbmem, 0, screen_size);

	/* 屏幕中間顯示字母 */
	lcd_put_ascii(var.xres/2, var.yres/2, 'A');
	
	/* 屏幕中間顯示中文 */
	printf("chinese code : %02x  %02x\n", str[0], str[1]);	//打印編碼
	lcd_put_chinese(var.xres/2 + 8, var.yres/2, str);

7、LCD顯示屏描色函數實現

/* lcd描色   color : 0x00RRGGBB */
void lcd_put_pixel(int x, int y, unsigned int color)
{	
	unsigned char  *pen_8;
	unsigned short *pen_16;
	unsigned int   *pen_32;
	unsigned int 	red, green, blue;

	/* 該座標在內存中對應像素的位置 */
	pen_8  = fbmem+y*line_width+x*pixel_width;
	pen_16 = (unsigned short *)pen_8;
	pen_32 = (unsigned int   *)pen_8;

	switch (var.bits_per_pixel) {
	case 8:
		*pen_8 = color;
		break;
	case 16:
		/* RGB:565 */
		red   = ((color >> 16) & 0xffff) >> 3;
		green = ((color >> 8)  & 0xffff) >> 2;
		blue  = ((color >> 0)  & 0xffff) >> 3;
		*pen_16 = (red << 11) | (green << 5) | blue;
		break;
	case 32:
		*pen_32 = color;
		break;
	default:
		printf("can not surport &dbpp\n", var.bits_per_pixel);
		break;
	}
}

8、LCD顯示英文函數實現

  • 實現方法簡述:如下圖所示
    1、對於A的ASCII的由16個字節組成每行佔據一個字節
    2、對於其編碼中的1位,對應LCD則會進行顯示,0位則不顯示
    3、對每一行的像素進行判斷for (i = 0; i < 16; i++)
    3.1 判斷一行中點亮的像素點位置
    每一行的像素點由8位二進制數表示,從高位開始判斷for (b = 7; b >= 0; b--)該爲是否爲1if (byte & (1<<b))
    4、如果從高位開始判斷,則在下圖中每個像素點的座標爲(7-b, i)
    在這裏插入圖片描述
/* lcd顯示ASCII碼 */
void lcd_put_ascii(int x, int y, unsigned char c)
{
	unsigned char *dots = (unsigned char *)&fontdata_8x16[c*16];
	unsigned char byte;
	int i, b;

	/* 每行進行每位檢查,爲1則亮,否則滅 */
	for (i = 0; i < 16; i++) {
		byte = dots[i];
		for (b = 7; b >= 0; b--) {
			if (byte & (1<<b)) 
				lcd_put_pixel(x+7-b, y+i, 0xffffff);	//亮
			else
				lcd_put_pixel(x+7-b, y+i, 0x000000);	//滅
		}
	}
}

9、LCD顯示中文函數實現

  • 實現方法簡述:
    1、HZK16字庫裏的漢字爲16×16,即需要256個點來顯示,也就是說需要32個字節才能達到顯示一個普通漢字的目的
    2、一個漢字佔兩個字節,這兩個中前一個字節爲該漢字的區號後一個字節爲該字的位號
    3、爲了兼容其他字符編碼,所以漢字編碼是從0xA1區開始的,如下圖。所以代碼爲str[0] - 0xA1;, str[1] - 0xA1;
    在這裏插入圖片描述
    4、其在內存的絕對的位置爲:hzkmem + (area * 94 + where) * 32
    5、對每一行的像素進行判斷for (i = 0; i < 16; i++)
    5.1 對每一行的每個字節進行判斷for (j = 0; j < 2; j++)
    5.2 對每個字節的每一位for (b = 7; b >= 0; b--)進行判斷是否等於1if (byte & 1 << b)
    在這裏插入圖片描述
/* lcd顯示中文 */
void lcd_put_chinese(int x, int y, unsigned char *str)
{
	unsigned int area  = str[0] - 0xA1;
	unsigned int where = str[1] - 0xA1;
	unsigned char *dots = hzkmem + (area * 94 + where) * 32;
	
	int i, j, b;
	unsigned char byte;

	/* 由於漢字需要16*16個點陣,即一行需要2個字節的數據
	 * 所以在提取字節時需要:i*2跳到對應行,j跳到對應的字節
	 * 在描點時需要x+7-b跳到該行該字節的每個位,j*8跳到對應的字節
	 */
	for (i = 0; i < 16; i++) {
		for (j = 0; j < 2; j++) {
			byte = dots[i*2  + j];
			for (b = 7; b >= 0; b--) {
				if (byte & 1 << b)
					lcd_put_pixel(x+7-b+j*8, y+i, 0xffffff);	//亮
				else
					lcd_put_pixel(x+7-b+j*8, y+i, 0x000000);	//滅
			}
		}
	}
}

10、完整的代碼

#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <linux/fb.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>

#define FONTDATAMAX 4096

static const unsigned char fontdata_8x16[FONTDATAMAX] = {
	/* 方便顯示,這裏省略了字庫編碼,可以到內核的font_8x16.c文件中找*/

};

/* 文件操作 */
static int fd_fb;
static unsigned char *fbmem;

/* hzk16字庫操作 */
static int fd_hzk16;
static struct stat hzk_stat;
static unsigned char *hzkmem;

/* lcd參數 */
static int screen_size;
static unsigned int line_width;
static unsigned int pixel_width;
static struct fb_var_screeninfo var;
static struct fb_fix_screeninfo fix;

/* lcd描色   color : 0x00RRGGBB */
void lcd_put_pixel(int x, int y, unsigned int color)
{	
	unsigned char  *pen_8;
	unsigned short *pen_16;
	unsigned int   *pen_32;
	unsigned int 	red, green, blue;

	/* 該座標在內存中對應像素的位置 */
	pen_8  = fbmem+y*line_width+x*pixel_width;
	pen_16 = (unsigned short *)pen_8;
	pen_32 = (unsigned int   *)pen_8;

	switch (var.bits_per_pixel) {
	case 8:
		*pen_8 = color;
		break;
	case 16:
		/* RGB:565 */
		red   = ((color >> 16) & 0xffff) >> 3;
		green = ((color >> 8)  & 0xffff) >> 2;
		blue  = ((color >> 0)  & 0xffff) >> 3;
		*pen_16 = (red << 11) | (green << 5) | blue;
		break;
	case 32:
		*pen_32 = color;
		break;
	default:
		printf("can not surport &dbpp\n", var.bits_per_pixel);
		break;
	}
}

/* lcd顯示ASCII碼 */
void lcd_put_ascii(int x, int y, unsigned char c)
{
	unsigned char *dots = (unsigned char *)&fontdata_8x16[c*16];
	unsigned char byte;
	int i, b;

	/* 每行進行每位檢查,爲1則亮,否則滅 */
	for (i = 0; i < 16; i++) {
		byte = dots[i];
		for (b = 7; b >= 0; b--) {
			if (byte & (1<<b)) 
				lcd_put_pixel(x+7-b, y+i, 0xffffff);	//亮
			else
				lcd_put_pixel(x+7-b, y+i, 0x000000);	//滅
		}
	}
}

/* lcd顯示中文 */
void lcd_put_chinese(int x, int y, unsigned char *str)
{
	unsigned int area  = str[0] - 0xA1;
	unsigned int where = str[1] - 0xA1;
	unsigned char *dots = hzkmem + (area * 94 + where) * 32;
	
	int i, j, b;
	unsigned char byte;

	/* 由於漢字需要16*16個點陣,即一行需要2個字節的數據
	 * 所以在提取字節時需要:i*2跳到對應行,j跳到對應的字節
	 * 在描點時需要x+7-b跳到該行該字節的每個位,j*8跳到對應的字節
	 */
	for (i = 0; i < 16; i++) {
		for (j = 0; j < 2; j++) {
			byte = dots[i*2  + j];
			for (b = 7; b >= 0; b--) {
				if (byte & 1 << b)
					lcd_put_pixel(x+7-b+j*8, y+i, 0xffffff);	//亮
				else
					lcd_put_pixel(x+7-b+j*8, y+i, 0x000000);	//滅
			}
		}
	}
}

int main(int argc, char **argv)
{	
	unsigned char str[] = "中";
	
	/* 打開設備:支持讀寫 */
	fd_fb = open("/dev/fb0", O_RDWR);
	if (fd_fb < 0) {
		printf("can not open /dev/fb0 , err code :%d\n", fd_fb);
		return -1;
	}
	
	/* 獲得可變信息 */
	if (ioctl(fd_fb, FBIOGET_VSCREENINFO, &var)) {
		printf("can not get var\n");
		return -1;
	}

	/* 獲得固定信息 */
	if (ioctl(fd_fb, FBIOGET_FSCREENINFO, &fix)) {
		printf("can not get var\n");
		return -1;
	}

	/* 直接映射到內存的Framebuffer */
	screen_size = var.xres * var.yres * var.bits_per_pixel / 8;	// 屏幕總像素所佔的字節數
	line_width   = var.xres * var.bits_per_pixel / 8;	// 每行像素所佔的字節數
	pixel_width  = var.bits_per_pixel / 8;	// 每個像素所佔字節數
	
	fbmem = (unsigned char *)mmap(NULL, screen_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd_fb, 0);
	if (fbmem == (unsigned char *)-1) {
		printf("can not mmap\n");
		return -1;
	}

	/* 打開漢字庫 */
	fd_hzk16 = open("HZK16", O_RDONLY);
	if (fd_hzk16 < 0) {
		printf("can not open /dev/fb0 , err code :%d\n", fd_hzk16);
		return -1;
	}
	
	/* 計算字庫文件大小 */
	if (fstat(fd_hzk16, &hzk_stat)) {
		printf("can not get fstat\n");
		return -1;
	}

	/* 漢字庫直接映射到內存 */
	hzkmem = (unsigned char *)mmap(NULL, hzk_stat.st_size, PROT_READ, MAP_SHARED, fd_hzk16, 0);
	if (hzkmem == (unsigned char *)-1) {
		printf("can not mmap for hzk16err code :%d\n", fd_hzk16);
		return -1;
	}

	/* 清屏 */
	memset(fbmem, 0, screen_size);

	/* 屏幕中間顯示字母 */
	lcd_put_ascii(var.xres/2, var.yres/2, 'A');
	
	/* 屏幕中間顯示中文 */
	printf("chinese code : %02x  %02x\n", str[0], str[1]);	//打印編碼
	lcd_put_chinese(var.xres/2 + 8, var.yres/2, str);

	return 0;
}

三、編譯與運行

1、編譯

使用arm-linux-gcc -o show_font show_font.c生成可執行文件

2、運行

可執行文件與HZK16字庫放到開發板根文件系統的同一目錄下
在這裏插入圖片描述
執行./show_font,可以看到開發板中正確顯示了“A中”

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章