第三阶段应用层——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工具链
  • 源码仓库:https://gitee.com/d_1254436976/Embedded-Linux-Phase-3


一、前言

之前的博文中介绍了数码相册的框架以及字符编码的知识,在这篇中,编程实现在开发板上显示英文和中文。
此处主要实现功能,对于程序的框架考虑可能不足。

二、编程实现

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中”

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_42813232/article/details/106952284