- 在LCD上显示字母、汉字
- 从字母和汉字的点阵取出数据,在LCD上显示
- LCD就可以理解为一个非常大的点阵,我们对每个点进行操作得出我们想要的文字或图片
- 在内存中划出一块空间,称为framebuffer或显存
- LCD控制器会从framebuffer取出若干字节数据(代表一个像素),发给LCD
- LCD控制器会循环不断的从framebuffer取数据发给LCD
- 所以说,我们只要修改frame buffer中的数据,就可以在LCD上显示了
- 想在某个位置显示一个字符,那么就要在frame buffer里找到那个位置,根据点阵设置frame buffer里的数据
- 终端上输入:echo hello > /dev/tty1 这样就可以在LCD上显示”hello”
- 显然,在LCD上显示一个字符,肯定要有它的点阵
- 所以内核中肯定有英文字母的点阵
- 内核中的font字体文件:
- 对于英文字母,点阵就是一个数组
- 816个像素点阵,每个像素要么显示要么不显示,则大小为816bit=16字节;其中的每一位表示一个像素点
- 例如字母A:
每行占一个字节,一共16行 ```c //dot_font
int fd_fb; struct fb_var_screeninfo var; / current var / struct fb_fix_screeninfo fix; / current fix / int screen_size; unsigned char fbmem = null; unsigned int line_width; unsigned int pixel_width; int fd_hzk16; unsigned char hzkmem; struct stat hzk_stat;
//点阵数组
define FONTDATAMAX 4096
static const unsigned char fontdata_8x16[FONTDATAMAX] = {…};
/ color: 0x00RRGGBB / void lcd_put_pixel(int x, ing y, unsigned int color) { / 在屏幕上怎么显示一个点? / unsigned char pen_8 = fbmem + yline_width + xpixel_width; unsigned short pen_16; unsigned int pen_32; / 对于不同的像素宽度,用不同的变量表示 / pen_16 = (unsigned short )pen_8; pen_32 = (unsigned int *)pen_8;
unsigned int red, green, blue;
switch(var.bits_per_pixel)
{
case 8:
{
*pen_8 = color;
break;
}
case 16:
{
/* RGB:565bit */
red = (color >> 16) & 0xff;
green = (color >> 8) & 0xff;
blue = color & 0xff;
*pen_16 = ((red >> 3) << 11) | ((green >> 2) << 5) | (blue >> 3);
break;
}
case 32: //大多数RGB的LCD都会用到32位来表示一个像素,方便取整
{
*pen_32 = color;
break;
}
default:
{
printf("can't surport %dbpp\n",var.bits_per_pixel);
}
}
}
void lcd_put_ascii(int x, int y, unsigned char c) { unsigned char dots = &fontdata_8x16[c16]; unsigned char byte; int i,b;
for(i=0; i<16; i++)
{
byte = dots[i]; 取出每一行的数据
for(b=7; b>=0; b--)
{
if(byte & (1<<b)) //一位一位地判断数据是否为1
{
/* show */
lcd_put_pixel(x-b+7, y+i, 0xffffff); /* 白 */
}
else
{
/* hide */
lcd_put_pixel(x-b+7, y+i, 0x000000); /* 黑 */
}
}
}
}
void lcd_put_chinese(int x, int y, unsigned char str_ch) { //显示汉字需要有所谓的汉字库,如:HZK16 //需要先打开HZK16文件才能使用 unsigned int area = str_ch[0] - 0xA1; unsigned int where = str_ch[1] - 0xA1; unsigned int dots = = hzkmem + (area 94 + where) 32; unsigned char byte;
int i, j, b;
for(i=0; i<16; i++)
{
for(j=0; j<2; j++)
{
byte = dots[2*i + j];
for(b=7; b>=0; b++)
{
if(byte & (1<<b)) //一位一位地判断数据是否为1
{
/* show */
lcd_put_pixel(x + j*8 + 7-b, y+i, 0xffffff); /* 白 */
}
else
{
/* hide */
lcd_put_pixel(x + j*8 + 7-b, y+i, 0x000000); /* 黑 */
}
}
}
}
}
int main(int argc, char **argv)
{
unsigned char str_ch[] = “中”;
fd_fb = open("/dev/fb0",O_RDWR);
if(fd_fb < 0)
{
printf("can't open /dev/fb0\n");
}
/* 获得可变信息 */
if( ioctl(fd_fb, FBIOGET_VSCREENINFO, &var) )
{
printf("can't get var\n");
return -1;
}
/* 获得固定信息 */
if( ioctl(fd_fb, FBIOGET_FSCREENINFO, &fix) )
{
printf("can't get fix\n");
return -1;
}
line_width = var.xres * var.bits_per_pixel /8;
pixel_width = var.bits_per_pixel / 8;
screen_size = var.xres * var.yres * var.bits_per_pixel / 8; //屏幕大小
fbmem = (unsigned char *)mmap(NULL, screen_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd_fb, 0);
if((unsigned char*)-1 == fbmem)
{
printf("can't mmap\n");
}
fd_hzk16 = open("HZK16", O_RDONLY);
if(fd_hzk16 < 0)
{
printf("cant't open HZK16\n");
}
if( fstat(fd_hzk16, &hzk_stat) )
{
printf("can't get stat");
}
/* 进行内存映射 */
hzkmem = (unsigned char *)mmap(NULL, hzk_stat.st_size, PROT_READ, MAP_SHARED, fd_hzk16, 0);
if((unsigned char*)-1 == hzkmem)
{
printf("can't mmap\n");
}
/* 清屏 */
memset(fbmem, 0, screen_size);
lcd_put_ascii(var.xres/2, var.yres/2, 'A'); //在屏幕的中间显示一个字母
printf("chinese code:%02x %02x\n",str_ch[0], str_ch[1]); //打印出中文的编码值
lcd_put_chinese(var.res/2 + 8, var.yres/2, str_ch); //显示中文
} ```
- 首先要获得LCD的参数
- 每个像素用多少位来表示
- LCD屏幕的x,y分辨率是多少
- 内核fbmem.c中有很多ioctl,我们获取要LCD的可变信息
- 从可变信息中,我们就可以知道x、y分辨率;每个像素用多少位表示
- LCD提供了mmap函数,那么就可以直接映射内存到程序用户空间(直接使用frame buffer)
- mmap用法
- 一个字体点阵,坐标从左上角开始
- GBK的汉字,是用”两个字节”来表示的
- 在显示汉字之前,需要得到其对应的点阵
- HZK16这样一个汉字字体文件中包含了汉字的编码和对应的点阵
- 在对一个文件进行mmap之前需要知道文件有多大,可以使用fstat函数获取
- 汉字在HZK16文件中是怎样存储的?
- 按区号和位号来
- GBK中用两个字节来存储一个汉字,第一个字节就用来表示区号,第二个字节用来表示位号
- 每个区由94个汉字组成
- 因为是1616的点阵,所以每个汉字在文件中都占1616bit=32字节
区码和位码都是从A1开始的
- 交叉编译工具链:/usr/local/arm/4.3.2/
- 查找一个文件:find -name “fb.h”
启动了console和tty1两个控制台
- tty1::askfirst: ~/bin/sh 把LCD也当作控制台输出
- 将程序拷贝到nfs网络文件系统目录下
- 要先配置Linux内核(因为原有内核没有加入LCD的支持,即无相关驱动,原先LCD驱动是作为模块在启动完后通过脚本配置的),要把lcd.c编译进内核
- 要修改drivers/video/Makefile使得编译内核时能包含lcd.c
- 执行make menuconfig配置内核编译选项,把lcd包含进来
- 执行make uImage生成新的内核镜像
- 拷贝生成的内核到网络文件系统nfs上
- 使用编译了lcd的内核启动
- 在uboot中,加载新的内核镜像启动
- 启动完成后,直接执行程序