Linux内核中使用了内建的字库用于在framebuffer上显示ascii码字形信息,具体定义在文件

linux-stable/lib/fonts/fonts.c

通过fbcon=符号指定,

fbcon=font:VGA8x16

fbcon指定的信息在内核中会被以下函数解析:

从一个例子看ASCII点阵字库的应用原理_ASCII

内核的自形库数据组织结构:

从一个例子看ASCII点阵字库的应用原理_#include_02

 以其中的font_vga_8x16为例:

从一个例子看ASCII点阵字库的应用原理_ASCII_03

 其结构中内部指定了fontdata_8x16字库文件,字库文件中存储有字形信息:

从一个例子看ASCII点阵字库的应用原理_#define_04

下面以一个程序为例,来说明字形库的用法: 

Framebuffer书写字符A的代码:

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

#define RED 0xF800
#define YELLOW 0xFFE0
#define BLUE 0x001F
#define WHITE 0xFFFF
#define BLACK 0x0000

struct fb_var_screeninfo vinfo;
static const unsigned char fontdata_8x16[16] = {
0x00,
0x00,
0x10,
0x38,
0x6c,
0xc6,
0xc6,
0xfe,
0xc6,
0xc6,
0xc6,
0xc6,
0x00,
0x00,
0x00,
0x00,
};
unsigned char *fbmem;
/* color : 0x00RRGGBB */
void lcd_put_pixel(int x, int y, unsigned int color)
{
unsigned char *pen_8 = fbmem+y*4 + x*vinfo.xres*4;
//找到对应的像素在内存中的位置,后面向其(*pen_8)写颜色值。
//通过pen_8来操作fbmem,实现LCD显示
unsigned short *pen_16;
unsigned int *pen_32;

unsigned int red, green, blue;

pen_16 = (unsigned short *)pen_8;
pen_32 = (unsigned int *)pen_8;

switch (vinfo.bits_per_pixel)
//不同的像素位数,颜色值格式不同。根据像素位数设置对应的颜色值格式,并写值。
{
case 8:
{
*pen_8 = color;//8位像素的话,颜色值直接赋给这个内存中即可。
break;
}
case 16:
{
/* color : 0x00RRGGBB ,unsigned int 32位color的格式*/
/* 565 */
red = (color >> 16) & 0xff;
//右移16位后剩下高16位,再&0xff,又剩下低8位。即取出32位color的16-23位
green = (color >> 8) & 0xff;//取出32color的8-15位
blue = (color >> 0) & 0xff;//取出32color的0-7位
color = ((red >> 3) << 11) | ((green >> 2) << 5) | (blue >> 3);
//取出对应的组成565
*pen_16 = color;
break;
}
case 32:
{
*pen_32 = color;//32位像素也直接写即可。
break;
}
default:
{
printf("can't surport %dbpp\n", vinfo.bits_per_pixel);
break;
}
}
}

void fill_color16(short *fb_addr, short bit_map, int psize)
{
int i;
for(i=0; i<psize; i++) {
*fb_addr = bit_map;
fb_addr++;
}
}

void lcd_put_ascii(int x, int y, unsigned char c)
{
c = 0;
unsigned char *dots = (unsigned char *)&fontdata_8x16[c*16];
//从fontdata_8x16[FONTDATAMAX]数组中获得点阵起始位置
int i, b;
unsigned char byte;

for (i = 0; i < 16; i++)//点阵有16行
{
byte = dots[i];
for (b = 7; b >= 0; b--)//点阵有8列
{
if (byte & (1<<b))//判断点阵中的各个点是否为1
{
/* show */
lcd_put_pixel(x+7-b, y+i, 0xffff0000); /* 白 */
}
else
{
/* hide */
lcd_put_pixel(x+7-b, y+i, 0); /* 黑 */
}
}
}
}


int main ()
{
int fp=0;
struct fb_fix_screeninfo finfo;
long screensize=0;
char *fbp = NULL, *test_fbp=NULL;
int x = 0, y = 0;
long location = 0;
int i;
int num = 5;
int pix_size=0;

fp = open("/dev/fb0", O_RDWR);

if(fp < 0) {
printf("Error : Can not open framebuffer device/n");
exit(1);
}

if(ioctl(fp, FBIOGET_FSCREENINFO, &finfo)){
printf("Error reading fixed information/n");
exit(2);
}

if(ioctl(fp, FBIOGET_VSCREENINFO, &vinfo)){
printf("Error reading variable information/n");
exit(3);
}

screensize = vinfo.xres * vinfo.yres * vinfo.bits_per_pixel / 8;

printf("The phy mem = 0x%lx, total size = %d(byte)\n", finfo.smem_start, finfo.smem_len);
printf("xres = %d, yres = %d, bits_per_pixel = %d\n", vinfo.xres, vinfo.yres, vinfo.bits_per_pixel);
printf("So the screensize = %ld(byte), using %ld frame\n", screensize, finfo.smem_len/screensize);
printf("vinfo.xoffset = %d, vinfo.yoffset = %d\n", vinfo.xoffset, vinfo.yoffset);
printf("vinfo.vmode is :%d\n", vinfo.vmode);
printf("finfo.ypanstep is :%d\n", finfo.ypanstep);
printf("vinfo.red.offset=0x%x\n", vinfo.red.offset);
printf("vinfo.red.length=0x%x\n", vinfo.red.length);
printf("vinfo.green.offset=0x%x\n", vinfo.green.offset);
printf("vinfo.green.length=0x%x\n", vinfo.green.length);
printf("vinfo.blue.offset=0x%x\n", vinfo.blue.offset);
printf("vinfo.blue.length=0x%x\n", vinfo.blue.length);
printf("vinfo.transp.offset=0x%x\n", vinfo.transp.offset);
printf("vinfo.transp.length=0x%x\n", vinfo.transp.length);


fbp =(char *)mmap(0, screensize, PROT_READ | PROT_WRITE, MAP_SHARED, fp,0);
if ((int)fbp == -1)
{
printf ("Error: failed to map framebuffer device to memory.\n");
exit (4);
}
printf("Get virt mem = %p\n", fbp);

fbmem = fbp;

pix_size = vinfo.xres * vinfo.yres;
/* using first frame, for FBIOPAN_DISPLAY
* 当刷新需要调用FBIOPAN_DISPLAY, 要告知驱动刷哪块帧, 用到下面两个参数
* 如果使用第二帧buffer -> vinfo.xoffset = 0; vinfo.yoffset = vinfo.yres;
*/
vinfo.xoffset = 0;
vinfo.yoffset = 0;

int xx = 0, yy = 0;
int j = 10;

while(j--)
{
lcd_put_ascii(xx, yy, 1);
xx = xx + 32;
yy = yy + 32;
}

#if 0
/* show color loop */
while(num--) {
printf("\ndrawing YELLOW......\n");
fill_color16((short *)fbp, YELLOW, pix_size);
//ioctl(fp, FBIOPAN_DISPLAY, &vinfo);
sleep(3);

printf("\ndrawing BLUE......\n");
fill_color16((short *)fbp, BLUE, pix_size);
//ioctl(fp, FBIOPAN_DISPLAY, &vinfo);
sleep(3);

printf("\ndrawing RED......\n");
fill_color16((short *)fbp, RED, pix_size);
//ioctl(fp, FBIOPAN_DISPLAY, &vinfo);
sleep(3);
}
#if 1
/*这是你想画的点的位置坐标,(0,0)点在屏幕左上角*/
x = 10;
y = 10;
location = x * (vinfo.bits_per_pixel / 8) + y * finfo.line_length;
test_fbp = fbp + location;
printf("draw line.......\n");
for(i = 0; i < (vinfo.xres - x); i++)
*test_fbp++ = i+30;

//ioctl(fp, FBIOPAN_DISPLAY, &vinfo);
#endif
#endif

munmap(fbp, screensize); /*解除映射*/

close (fp);
return 0;
}

结果:

从一个例子看ASCII点阵字库的应用原理_#define_05

至于为什么是躺着的,还不清楚原因。

结束!