Chinaunix首页 | 论坛 | 博客
  • 博客访问: 299684
  • 博文数量: 76
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 715
  • 用 户 组: 普通用户
  • 注册时间: 2015-05-20 20:38
文章分类
文章存档

2016年(20)

2015年(56)

分类: 嵌入式

2015-08-23 16:32:17

字符的点阵显示

15年8月21日11:22:32


#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] = {


/* 0 0x00 '^@' */

0x00, /* 00000000 */

0x00, /* 00000000 */

0x00, /* 00000000 */

0x00, /* 00000000 */

0x00, /* 00000000 */

0x00, /* 00000000 */

0x00, /* 00000000 */

0x00, /* 00000000 */

0x00, /* 00000000 */

0x00, /* 00000000 */

0x00, /* 00000000 */

0x00, /* 00000000 */

0x00, /* 00000000 */

0x00, /* 00000000 */

0x00, /* 00000000 */

0x00, /* 00000000 */


/* 1 0x01 '^A' */

0x00, /* 00000000 */

0x00, /* 00000000 */

0x7e, /* 01111110 */

0x81, /* 10000001 */

0xa5, /* 10100101 */

0x81, /* 10000001 */

0x81, /* 10000001 */

0xbd, /* 10111101 */

0x99, /* 10011001 */

0x81, /* 10000001 */

0x81, /* 10000001 */

0x7e, /* 01111110 */

0x00, /* 00000000 */

0x00, /* 00000000 */

0x00, /* 00000000 */

0x00, /* 00000000 */


...................

};


int fd_fb;

struct fb_var_screeninfo var; /* Current var */

struct fb_fix_screeninfo fix; /* Current fix */

int screen_size;

unsigned char *fbmem;

unsigned int line_width;

unsigned int pixel_width;


int fd_hzk16;

struct stat hzk_stat;

unsigned char *hzkmem;


/* color : 0x00RRGGBB */

void lcd_put_pixel(int x, int y, unsigned int color)

{

unsigned char *pen_8 = fbmem + y*line_width +x*pixel_width;

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 (var.bits_per_pixel)

{

case 8:

{

*pen_8 = color;

break;

}

case 16:

{

/* 565:RGB */

red = (color >> 16) & 0xff;

green = (color >> 8) & 0xff;

blue = (color >> 0) & 0xff;

color = ((red >> 3) << 11) | ((green >> 2) << 5) | (blue >> 3);

*pen_16 = color;

break;

}

case 32:

{

*pen_32 = color;

break;

}

default:

{

printf("cannot support!\n");

break;

}

}

}


void lcd_put_ascii(int x, int y, char c)

{

unsigned char *dots = (unsigned char *)&fontdata_8x16[c*16];

int i, b;

unsigned char byte;


for (i = 0; i < 16; i++)

{

byte = dots[i];

for (b = 7; b >= 0; b--)

{

if (byte & (1<<b))

{

/* show */

lcd_put_pixel(x+7-b, y+i, 0xffffff); /* white */

}

else

{

/* hide */

lcd_put_pixel(x+7-b, y+i, 0); /* black */

}

}

}

}


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;

unsigned char byte;


int i, j, b;

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))

{

/* show */

lcd_put_pixel(x+j*8+7-b, y+i, 0xffffff); /*white*/

}

else

{

/* hide */

lcd_put_pixel(x+j*8+7-b, y+i, 0); /* black */

}

}

}

}



int main(int argc, char **argv)

{

unsigned char str[] = "";


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

if (fd_fb < 0)

{

printf("cannot open /dev/fb0!\n");

return -1;

}


if (ioctl(fd_fb, FBIOGET_VSCREENINFO, &var))

{

printf("cannot get var\n");

return -1;

}


if (ioctl(fd_fb, FBIOGET_FSCREENINFO, &fix))

{

printf("cannot 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 (fbmem == (unsigned char *)-1)

{

printf("cannot mmap\n");

return -1;

}


fd_hzk16 = open("HZK16", O_RDONLY);

if (fd_hzk16 < 0)

{

printf("cannot open HZK16\n");

return -1;

}

if (fstat(fd_hzk16, &hzk_stat))

{

printf("cannot 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("cannot mmap for hzkmem\n");

return -1;

}


/* clear screen: set all the color black */

memset(fbmem, 0, screen_size);


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 + 8, var.yres/2, str);


return 0;

}



(一)首先来写main函数:

1)打开/dev/fb0,通过ioctl函数来获取屏幕的一些可变参数和固定参数。

2)将设备通过mmap函数映射到内存里面,这样我们就可以像访问数组一样访问文件了。

映射的时候,需要用到映射多大的内存(即mmap函数的第二个参数),这时候需要计算出来屏幕的大小:screen_size。通过下面的公式计算即可。

screen_size = var.xres * var.yres * var.bits_per_pixel / 8;

3)显示一个英文字符。在main函数中通过调用lcd_put_ascii()函数来实现。这个函数我们下面再分析。

4)显示一个中文字符。由于我们需要从HZK16这个文件里面取值,可以调用read(),write()等函数,但是我们想要方便一点,也将这个文件映射到内存中,这样也可以像操作数组一样从这个文件中取值了。

先打开HZK16这个文件,然后通过mmap函数来实现映射,这时候,同样需要知道映射的大小,通过调用fstat()函数来获取这个文件的一些相关信息。

fstat()函数原型:

int fstat(int fd, struct stat *buf);

这个函数返回一个stat结构体,结构体成员如下所示:

struct stat {

dev_t st_dev; /* ID of device containing file */

ino_t st_ino; /* inode number */

mode_t st_mode; /* protection */

nlink_t st_nlink; /* number of hard links */

uid_t st_uid; /* user ID of owner */

gid_t st_gid; /* group ID of owner */

dev_t st_rdev; /* device ID (if special file) */

off_t st_size; /* total size, in bytes */

blksize_t st_blksize; /* blocksize for file system I/O */

blkcnt_t st_blocks; /* number of 512B blocks allocated */

time_t st_atime; /* time of last access */

time_t st_mtime; /* time of last modification */

time_t st_ctime; /* time of last status change */

};

所以在程序中,我们首先定义一个stat结构体,然后再使用fstat函数来获取信息,获取的信息就存在这个结构体中。

struct stat hzk_stat;

fstat(fd_hzk16, &hzk_stat)

获取信息以后,就可以mmap映射了。

5)显示之前先清屏。用memset函数即可,简单粗暴。


(二)下面分析lcd_put_ascii()函数:

1)英文字体的点阵存在fontdata_8x16[]这个数组中,这个数组是我们直接从内核的font_8*16.c中拷贝过来的。当然也可以像HZK16那样来调用。

2)可以看到,在这个数组中,每个字符用16byte来存储它的点阵。对于某一个字符c,因为它使用ascii来编码的,我们直接去数组的c*16位置取点阵即可。

unsigned char *dots = (unsigned char *)&fontdata_8x16[c*16];

3)每个字符是8*16的点阵,将对应的每一位,1的位我们就显示为白色,0的位不显示(黑色)。

显示的时候用到了lcd_put_pixel(int x, int y, unsigned int color)函数,这个函数最后再分析。描画的时候,注意xy坐标的表示方法,自己在草稿纸上仔细算算。


(三)下面分析lcd_put_chinese()函数:

1)我们将汉字存储在一个数组中,它是用GBK码来存储的,每一个汉字用两个byte来存储。

由于我们已经将HZK16映射到内存中,我们需要从哪取值,可以参考《HZK16的简单介绍和使用方法》这篇文章,用下面的方法算出偏移值(area * 94 + where) * 32,然后通过 hzkmem这个指针加偏移值,就可以计算出汉字点阵在内存数组中的位置。

unsigned int area = str[0] - 0xA1;

unsigned int where = str[1] - 0xA1;

unsigned char *dots = hzkmem + (area * 94 + where) * 32;

2)由于汉字是16*16点阵来表示的,所以一行中有两个byte,所以每行需要取两次byte的值。仔细分析分析这个函数中的循环。


(四)最后分析lcd_put_pixel(int x, int y, unsigned int color)这个函数:

12440LCD控制器直接从fbmem开始的内存取值显示在LCD上面,所以想要在坐标为(xy)的位置显示颜色,需要去设置内存中相应位置的颜色。

由于内核中struct fb_var_screeninfo var;中是以像素为单位来存储的,而内存中是以byte为单位来存储的,所以需要转换一下。每一行的行宽line_width = var.xres * var.bits_per_pixel / 8;而像素宽度pixel_width = var.bits_per_pixel / 8;,这样,坐标(xy)对应内存中的位置就是fbmem + y*line_width +x*pixel_width;如下所示:

line_width = var.xres * var.bits_per_pixel / 8;

pixel_width = var.bits_per_pixel / 8;

unsigned char *pen_8 = fbmem + y*line_width +x*pixel_width;

2)函数中的第三个参数color中是以0x00RRGGBB的形式来存储颜色的。

var.bits_per_pixel = 8时,用到了调色板的知识,我们就直接用*pen_8 = color;来表示。

var.bits_per_pixel = 16时,需要将color中的颜色取出来,重新组合成16位的颜色。16位颜色的组成为:565:RGB。用下面的代码从新组合:

red = (color >> 16) & 0xff;

green = (color >> 8) & 0xff;

blue = (color >> 0) & 0xff;

color = ((red >> 3) << 11) | ((green >> 2) << 5) | (blue >> 3);

*pen_16 = color;


阅读(2075) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~