我们在写lcd驱动程序时,测试时可以在lcd上显示信息,那么他怎么能显示出信息呢,内核里有字符点阵编码,我们在内核里打开字符编码,可以看到很多字符点阵也称为字体。下面我们来自已写一个应用程序在lcd显示屏上显示字母以及显示中文。字符编码 在前面有一个随笔里有写,对于英文字母,用ascii码即可,那么如果我们要显示中文的话应该是要用到汉字库编码即GBK编码表,下面来写一段程序来试验和理解如何在开发板的lcd上显示。
一、首先我们要获得lcd的信息,例:固定信息和可变信息等 这些我们都需要知道可以操作lcd。
二、得到lcd的信息后,把 framebuffer进行内存映射,然后就可以直接操作framebuffer来显示了。
三、然后就可以根据ascii点阵一个一个的把点显示在framebuffer中即可实现了,
四、显示中文不同的是,需要找到一个汉字库文件,然后打开这个文件获得统计信息
五、得到文件统信息后,同样可以把这个字库文件进行内存映射,然后直接操作这个内存进行访问
六、和上面第三步一样,写出显示中文的函数,进行描点即可。
注意:显示的位置坐标要经过计算,要小心算,不然很容易出错和乱码。汉字库的文件解压后要放到根目录下,否则在打开汉字库文件时位置要改为你存放的位置
下面列出列子,实测在板子上可以运行的 ascii 字体头文件在内核里有,复制过来即可 汉字库可以去网上下载
#include <sys/mman.h>
#include "ascii.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>
#include <linux/fb.h>
#include <fcntl.h>
#include <string.h>
static int fd_fb;
static struct fb_var_screeninfo var; /* 定义一个可变信息结构体 用于保存获得的可变信息 */
static struct fb_fix_screeninfo fix; /* 定义一个固定信息结构体 用于保存获得的固定信息 */
static int screen_size; /* 定义一个变量 表示 framebuffer 进行内存映射 */
static unsigned char *fbmem; /* 定认一个指针变量 用于保存内存映射后的地址 */
static int fd_hzk;
static struct stat hzk_stat; /* 定义一个结构体用于保存统计信息 */
static unsigned char *hzkmem; /* 定义一个指针变量 用于保存央存映射的地址 */
static unsigned int line_width;
static unsigned int pixel_whdth;
static unsigned char *str = "谢";
/* 描点函数 根据 x,y,值 确定对应framebuffer的位置
* 然后依次的在对应的framebuffer上写入颜色值即可
*/
void lcd_put_pixel(int x, int y, unsigned int color)
{
unsigned char *pen = fbmem + y*line_width + x*pixel_whdth;
*pen = color;
}
/* 显示ascii字符 那么如何显示呢?
* 1:根据字符获得字体的点阵
* 2:根据x,y 的坐标值把点阵的数据放入framebuffer 即可显示出来
*/
void lcd_put_ascii(int x, int y, unsigned char c)
{
/* 得到字符点阵在数组中的起始位置 */
unsigned char *dots = (unsigned char *)&fontdata_8x16[c*16];
int i,j;
unsigned char byte;
for (i = 0;i < 16;i ++)
{
/* 取出一个字节 */
byte = dots[i];
for (j = 7;j >= 0;j --)
{
if (byte & (1 << j))
{
/* 传入参数的值是lcd要显示的起点坐标 这里每个点的坐标每描一个点要移动一次 */
lcd_put_pixel(x + 7 - j, y + i, 0xffffff); /* 0xffffff 表示白色 */
}
else
{
/* 传入参数的值是lcd要显示的起点坐标 这里每个点的坐标每描一个点要移动一次 */
lcd_put_pixel(x + 7 - j, y + i, 0); /* 0 表示黑色 */
}
}
}
}
/* 显示中文 如何显示呢?
* 1: 先得到汉字库的文件 然后可以像打开framebuffe一样打开这个文件
* 2: 打开后就可以去读了,我们也可以把这个文件当成内存一样用 即映射一下
* 3: 然后就可以去里面得到字体点阵数据了,接着和显示字符一样描点即可
* 把文件当成内存去映射先要得到文件大小 可以用 fstat 这个函数来获得大小
* 如何使用这个函数 可以在服务器上输入 man fstat 得到使用说明
* 汉字库的使用方法可以去"度娘"去找,基本上就是下面几个注意的地方
* 1: GBK编码用四个字节表示一个中文 第一个字节表示区码 第二个字节表示位码
* 2: 为了兼容ascii码 编码从a1 开始 例:"中"字 值是 D6 D0 D6=区码 D0=位码
* 3: 所以编码的值是 区码+A1 位码+A1
*/
void lcd_put_chinses(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;
unsigned char byte;
int i,j,k;
for (i = 0;i < 16;i ++)
{
for (j = 0;j < 2;j ++)
{
byte = dots[i * 2 + j];
for (k = 7;k >=0;k --)
{
if (byte & (1 << k))
{
/* 传入参数的值是lcd要显示的起点坐标 这里每个点的坐标每描一个点要移动一次 */
lcd_put_pixel(x + j * 8 + 7 - k, y + i, 0xffffff); /* 0xffffff 表示白色 */
}
else
{
/* 传入参数的值是lcd要显示的起点坐标 这里每个点的坐标每描一个点要移动一次 */
lcd_put_pixel(x + j * 8 + 7 - k, y + i, 0); /* 0 表示黑色 */
}
}
}
}
}
int main(int argc, char **agrv)
{
fd_fb = open("/dev/fb0", O_RDWR); /* 打开lcd设备 可读可写 */
if (fd_fb < 0)
{
printf("cnt't open /dev/fb0 !\n");
return -1;
}
if (ioctl(fd_fb, FBIOGET_VSCREENINFO, &var)) /* 获得可变信息 */
{
/* 正常获得信息的话 ioctl 会返回0 如果返回值不为0时表示出错 */
printf("can't get var! \n");
return -1;
}
if (ioctl(fd_fb,FBIOGET_FSCREENINFO, &fix)) /* 获得固定信息 */
{
/* 正常获得信息的话 ioctl 会返回0 如果返回值不为0时表示出错 */
printf("can't get fix! \n ");
return -1;
}
/* 计算 framebuffer 的大小 用于内存映射单位字节 用x分辩率*y分辩率*每个像素得到总大小这时的单位是bit 除以8 转换成字节 */
screen_size = var.xres * var.yres * var.bits_per_pixel / 8;
/* 内存映射 mmap 怎么用呢,可以在服务器上输入man mmap 得到说明
* void *mmap(void *addr, size_t length, int prot, int flags,int fd, off_t offset);
* 参数说明:void *addr 设置为0 让内核自动给我们分配地址
* :size_t length 映射内存大小
* :int prot 属性为可读可写
* :int flags 共享 其它进程都可见
* :int fd framebuffer
* :off_t offset 偏移值
*/
fbmem = (unsigned char *)mmap(NULL, screen_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd_fb, 0);
if (fbmem == (unsigned char *)-1)
{
printf("can't mmap!\n");
return -1;
}
line_width = var.xres * var.bits_per_pixel / 8;
pixel_whdth = var.bits_per_pixel / 8;
/* 打开当前目录下的 HZK16 文件 属性设为只读 */
fd_hzk = open("HZK16", O_RDONLY);
if (fd_hzk < 0)
{
printf("can't open HZK16\n");
return -1;
}
/* 得到这个件的统计信息当然也包含了大小 */
if (fstat(fd_hzk, &hzk_stat))
{
printf("can't get hzk_stat! \n ");
return -1;
}
/* 内存映射 mmap 怎么用呢,可以在服务器上输入man mmap 得到说明
* void *mmap(void *addr, size_t length, int prot, int flags,int fd, off_t offset);
* 参数说明:void *addr 设置为0 让内核自动给我们分配地址
* :size_t length 映射内存大小
* :int prot 属性为可读可写
* :int flags 共享 其它进程都可见
* :int fd 文件
* :off_t offset 偏移值
*/
hzkmem = (unsigned char *)mmap(NULL, hzk_stat.st_size, PROT_READ, MAP_SHARED, fd_hzk, 0);
if (hzkmem == (unsigned char *)-1)
{
printf("can't mmap!\n");
return -1;
}
/* 这里先清屏 全部显黑色 */
memset(fbmem, 0, screen_size);
/* 在lcd上显示ascii字符 即然是显示,肯定要有位置 在那显示先定在中间 */
lcd_put_ascii(var.xres / 2, var.yres / 2, 'F');
/* 在lcd上显示中文 即然是显示,肯定要有位置 在那显示 */
lcd_put_chinses(var.xres / 2 + 8, var.yres / 2, str);
return 0;
}