FrameBuffer的原理 FrameBuffer 是出现在 2.2.xx 内核当中的一种驱动程序接口。
Linux是工作在保护模式下,所以用户态进程是无法象DOS那样使用显卡BIOS里提供的中断调用来实现直接写屏,Linux抽象出 FrameBuffer这个设备来供用户态进程实现直接写屏。Framebuffer机制模仿显卡的功能,将显卡硬件结构抽象掉,可以通过 Framebuffer的读写直接对显存进行操作。用户可以将Framebuffer看成是显示内存的一个映像,将其映射到进程地址空间之后,就可以直接进行读写操作,而写操作可以立即反应在屏幕上。这种操作是抽象的,统一的。用户不必关心物理显存的位置、换页机制等等具体细节。这些都是由 Framebuffer设备驱动来完成的。
但Framebuffer本身不具备任何运算数据的能力,就只好比是一个暂时存放水的水池.CPU将运算后的结果放到这个水池,水池再将结果流到显示器. 中间不会对数据做处理. 应用程序也可以直接读写这个水池的内容.在这种机制下,尽管Framebuffer需要真正的显卡驱动的支持,但所有显示任务都有CPU完成,因此CPU 负担很重
framebuffer的设备文件一般是 /dev/fb0、/dev/fb1 等等。
可以用命令: #dd if=/dev/zero of=/dev/fb 清空屏幕. 如果显示模式是 1024x768-8 位色,
用命令:$ dd if=/dev/zero of=/dev/fb0 bs=1024 count=768 清空屏幕;
用命令: #dd if=/dev/fb of=fbfile 可以将fb中的内容保存下来;
可以重新写回屏幕: #dd if=fbfile of=/dev/fb;
在使用Framebuffer时,Linux是将显卡置于图形模式下的.
在应用程序中,一般通过将 FrameBuffer 设备映射到进程地址空间的方式使用,比如下面的程序就打开 /dev/fb0 设备,
并通过 mmap 系统调用进行地址映射,随后用 memset 将屏幕清空(这里假设显示模式是 1024x768-8 位色模式,线性内存模式):
int fb;
unsigned char* fb_mem;
fb = open ("/dev/fb0", O_RDWR);
fb_mem = mmap (NULL, 1024*768, PROT_READ|PROT_WRITE,MAP_SHARED,fb,0);
memset (fb_mem, 0, 1024*768); //这个命令应该只有在root可以执行
仿照写一个
1 fd_fb = open("/dev/fb0",O_RDWR); 2 if(fd_fb < 0) 3 { 4 printf("can't open /dev/fb0 "); 5 return -1; 6 } 7 if(ioctl(fd_fb, FBIOGET_VSCREENINFO, &var)) //取出可变信息 8 { 9 printf("can't get var "); 10 return -1; 11 } 12 if(ioctl(fd_fb, FBIOGET_FSCREENINFO, &fix)) //取出固定信息 13 { 14 printf("can't get fix "); 15 return -1; 16 } 17 screen_size = var.xres * var.yres * var.bits_per_pixel / 8; //占内存大小 单位字节 18 line_width = var.xres * var.bits_per_pixel / 8; //一行像素大小 19 pixel_width = var.bits_per_pixel / 8; //一点像素大小 20 21 fb_mem = (unsigned char *)mmap(NULL, screen_size, //mmap 系统调用进行地址映射 22 PROT_READ | PROT_WRITE, MAP_SHARED, fd_fb, 0); 23 if(fb_mem == (unsigned char *) -1) 24 { 25 printf("can't mmap "); 26 return -1; 27 } 28 memset(fb_mem, 0, screen_size); //清屏,黑色
字符点阵显示
8*16像素的字符点阵
一个字节8位来表示一行的8个像素是否被选中点亮
每个字符由16个字节表示
仅需要用asii码值乘以16就可以定位到当前字符的点阵位置
1 static const unsigned char fontdata_8x16[FONTDATAMAX] = { 2 3 /* 0 0x00 '^@' */ 4 0x00, /* 00000000 */ 5 0x00, /* 00000000 */ 6 0x00, /* 00000000 */ 7 0x00, /* 00000000 */ 8 0x00, /* 00000000 */ 9 0x00, /* 00000000 */ 10 0x00, /* 00000000 */ 11 0x00, /* 00000000 */ 12 0x00, /* 00000000 */ 13 0x00, /* 00000000 */ 14 0x00, /* 00000000 */ 15 0x00, /* 00000000 */ 16 0x00, /* 00000000 */ 17 0x00, /* 00000000 */ 18 0x00, /* 00000000 */ 19 0x00, /* 00000000 */ 20 21 /* 1 0x01 '^A' */ 22 0x00, /* 00000000 */ 23 0x00, /* 00000000 */ 24 0x7e, /* 01111110 */ 25 0x81, /* 10000001 */ 26 0xa5, /* 10100101 */ 27 0x81, /* 10000001 */ 28 0x81, /* 10000001 */ 29 0xbd, /* 10111101 */ 30 0x99, /* 10011001 */ 31 0x81, /* 10000001 */ 32 0x81, /* 10000001 */ 33 0x7e, /* 01111110 */ 34 0x00, /* 00000000 */ 35 0x00, /* 00000000 */ 36 0x00, /* 00000000 */ 37 0x00, /* 00000000 */ 38 39 /***** 40 **** 41 **** 42 **** 43 **** 44 **** 45 **** 46 **** 47 *****/ 48 49 /* 255 0xff '' */ 50 0x00, /* 00000000 */ 51 0x00, /* 00000000 */ 52 0x00, /* 00000000 */ 53 0x00, /* 00000000 */ 54 0x00, /* 00000000 */ 55 0x00, /* 00000000 */ 56 0x00, /* 00000000 */ 57 0x00, /* 00000000 */ 58 0x00, /* 00000000 */ 59 0x00, /* 00000000 */ 60 0x00, /* 00000000 */ 61 0x00, /* 00000000 */ 62 0x00, /* 00000000 */ 63 0x00, /* 00000000 */ 64 0x00, /* 00000000 */ 65 0x00, /* 00000000 */ 66 67 };
刷写8*16字符点阵
1 lcd_put_ascii(int x, int y, unsigned char c ) 2 { 3 unsigned char *dots = (unsigned char *)&fontdata_8x16[c*16]; 4 int i, b; 5 unsigned char byte; 6 7 for(i = 0; i < 16; i++) 8 { 9 byte = dots[i]; 10 11 for(b = 7; b >= 0; b --) 12 { 13 if(byte & (1<<b)) 14 { 15 /* 显示 */ 16 lcd_put_pixel(x + 7 - b, y + i, 0xffffff);//白 17 } 18 else 19 { 20 /* 不显示 */ 21 lcd_put_pixel(x + 7 - b, y + i, 0);//黑 22 23 } 24 } 25 } 26
使用HZK16 字库,将它拷贝到内存中,使用时直接用数组指向某个汉字所在地址
使用#include <sys/stat.h>中的fstat()函数来统计HZK16文件信息
fd_hzk16 = open("HZK16",O_RDWR); if(fd_hzk16 < 0) { printf("can't open HZK16 "); return -1; }
if(fstat(fd_hzk16, &hzk_stat)) //得到文件统计信息 { printf("can't get fstat "); return -1; } hzk_mem = (unsigned char *)mmap(NULL, hzk_stat.st_size, PROT_READ, MAP_SHARED, fd_hzk16, 0); if(hzk_mem == (unsigned char *) -1) { printf("can't mmap hzk_mem "); return -1; }
HZK16 字库是符合GB2312标准的16×16点阵字库,HZK16的GB2312-80支持的汉字有6763个,符号682个。其中一级汉字有3755个,按 声序排列,二级汉字有3008个,按偏旁部首排列。我们在一些应用场合根本用不到这么多汉字字模,所以在应用时就可以只提取部分字体作为己用。
HZK16字库里的16×16汉字一共需要256个点来显示,也就是说需要32个字节才能达到显示一个普通汉字的目的。
我们知道一个GB2312汉字是由两个字节编码的,范围为A1A1~FEFE。A1-A9为符号区,B0到F7为汉字区。每一个区有94个字符(注意:这只是编码的许可范围,不一定都有字型对应,比如符号区就有很多编码空白区域)。下面以汉字“我”为例,介绍如何在HZK16文件中找到它对应的32个字节的字模数据。
前面说到一个汉字占两个字节,这两个中前一个字节为该汉字的区号,后一个字节为该字的 位号。其中,每个区记录94个汉字,位号为该字在该区中的位置。所以要找到“我”在hzk16库中的位置就必须得到它的区码和位码。(为了区别使用了区码 和区号,其实是一个东西,别被我误导了)
区码:区号(汉字的第一个字节)-0xa0 (因为汉字编码是从0xa0区开始的,所以文件最前面就是从0xa0区开始,要算出相对区码)
位码:位号(汉字的第二个字节)-0xa0
这样我们就可以得到汉字在HZK16中的绝对偏移位置:
offset=(94*(区码-1)+(位码-1))*32
注解:1、区码减1是因为数组是以0为开始而区号位号是以1为开始的
2、(94*(区号-1)+位号-1)是一个汉字字模占用的字节数
3、最后乘以32是因为汉字库文应从该位置起的32字节信息记录该字的字模信息(前面提到一个汉字要有32个字节显示)
1 void lcd_put_chinese(int x, int y, unsigned char *str) 2 { 3 unsigned int area = str[0] - 0xa1; 4 unsigned int where = str[1] - 0xa1; 5 unsigned char *dots = hzk_mem + (area * 94 + where) * 32; 6 unsigned char byte; 7 int i,j,b; 8 9 for(i=0; i < 16; i++) 10 for(j=0; j < 2; j++) 11 { 12 byte = dots[i*2 + j]; 13 for(b=7; b >=0; b--) 14 { 15 if(byte & (1<<b)) 16 { 17 /* 显示 */ 18 lcd_put_pixel(x + j * 8 + 7 - b, y + i, 0xffffff);//白 19 } 20 else 21 { 22 /* 不显示 */ 23 lcd_put_pixel(x + j * 8 + 7 - b, y + i, 0);//黑 24 25 } 26 } 27 } 28 29 }
对像素进行瞄颜色
1 void lcd_put_pixel(int x, int y, unsigned int color) 2 { 3 unsigned char *pen_8 = fb_mem + y * line_width + x * pixel_width; //当前像素对应内存位置 4 unsigned short *pen_16; 5 unsigned int *pen_32; 6 7 unsigned int red, blue, green; 8 9 pen_16 = (unsigned short *)pen_8; 10 pen_32 = (unsigned int *)pen_8; 11 12 switch(var.bits_per_pixel) 13 { 14 case 8: 15 { 16 *pen_8 = color; //对应调色板颜色 17 18 break; 19 } 20 case 16: 21 { 22 /* 5*6*5 */ 23 red = (color >> 16) & 0xff; 24 green = (color >> 8) & 0xff; 25 blue = (color >> 0) & 0xff; 26 27 color = ((red >> 3 ) << 11) | ((green >> 2) << 5) | ( blue >> 3); 28 29 /* 颜色数据为高位 */ 30 *pen_16 = color; 31 32 break; 33 } 34 case 32: 35 { 36 *pen_32 = color; 37 break; 38 } 39 40 } 41 42 }
lcd_put_ascii(var.xres / 2, var.yres / 2, 'a'); printf("中: chinese code: %02x %02x ", str[0], str[1]); lcd_put_chinese(var.xres / 2 + 32, var.yres / 2, str);
屏幕输出‘a’,“中”