from:https://blog.csdn.net/xiaodingqq/article/details/84893316
1、原理
在SDRAM内存里划出一块空间为FrameBuffer显存,LCD控制器会从FrameBuffer里取出若干节的数据(像素),发给LCD。
2、ASCII码字库文件使用
echo hello > /dev/tty1 就可以在LED上显示hello字样,说明内核是有英文字母的点阵。
在内核中搜索font,找到内核有一个font_8x16.c文件。
如下图所示,找到8*16的点阵存在fontdata_8x16[]数组里:
我们以0x41(A)为例子,找到该点阵信息为:
可以看到一个ASCII代表了16字节,后面我们直接将fontdata_8x16[]数组拷贝到应用程序里,用来显示ASCII
3、HZK16汉字库文件使用
https://blog.twofei.com/embedded/hzk.html
(1)HZK16描述
HZK16是按分区表排列的点阵文件,由于每个汉字是2个字节,每个字节的点阵是8*16
所以HZK16里的每个汉字点阵大小:2 * 8 * 16=32字节
(2)然后还要将编码转为点阵码。
4、编写代码
(1)首先要获得LCD的参数
1)打开/dev/fb0
int fd_fb;
fd_fb = open("/dev/fb0", O_RDWR);
if(fd_fb < 0)
{
printf("can't open /dev/fb0\n");
return -1;
}
2)通过ioctl()获得可变信息,固定信息
ioctl()是设备驱动程序中设备控制接口函数
#include <sys/ioctl.h>
详解:https://blog.csdn.net/qq_19923217/article/details/82698787
FBIOGET_VSCREENINFO:获取可变信息:xy分辨率,像素位数等)
FBIOGET_FSCREENINFO:获取固定信息:缓存地址,每行字节数)
如何所示:
在fbmem.c
将可变信息和固定信息存放在 var和fix里
struct fb_var_screeninfo var;
struct fb_fix_screeninfo fix;
通过ioctl()函数获得可变信息,固定信息
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;
}
(2)通过mmap映射FramBuffer
mmamp()函数: 申请一段用户空间的内存区域,并映射到内核空间某个内存区域。
头文件: #include<sys/mman.h>
返回值: 失败返回-1,并设置errno值.成功,返回映射的地址指针.若指定start则返回0
—
start: 需要映射的内存起始地址,通常填NULL,表示让系统自动映射,映射成功后返回该地址.
length: 映射地址的大小,填LCD显存字节数即可,因为2440一个地址存8位.
—
prot: 对映射地址的保护(protect)方式,常用组合如下:PROT_EXEC 映射区域可被执行
PROT_READ 映射区域可被读取
PROT_WRITE 映射区域可被写入
PROT_NONE映射区域不可访问
—
flag: 填MAP_SHARED即可,表示共享此映射,对其它进程可见.
—
fd: 需要将内存映射到哪个文件描述符(以后便可以直接通过内存来直接操作该文件)
—
offset: 映射偏移值,填0即可.
—
mmap失败的原因:如果第二个参数不是页面大小的整数倍则会出错
mmap的参数详情使用请参考:http://blog.csdn.net/dlutbrucezhang/article/details/9080173
int screen_size = var.xres * var.yres * var.bits_per_pixel / 8;
//x分辨率 * y分辨率 * 每个像素占多少位/8bit
//(每个像素占多少位/8bit = 每个像素占据多少字节)
unsinged char* 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;
}
(3)显示字母、汉字
unsigned char str[] = '中'; //将汉字存在数组里
lcd_put_ascii( var.xres/2, var.yres/2, 'A'); //在LCD中间显示字母
printf("chinese code: %02x %02x\n", str[0], str[1]);
lcd_put_chinese(var.xres/2 + 8, var.yres/2, str);//在字母后面显示汉字
1)lcd_put_ascii
void lcd_put_ascii(int x, int y, unsigned 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); /* 白 */
}
else
{
/* hide */
lcd_put_pixel(x+7-b, y+i, 0); /* 黑 */
}
}
}
}
2)打开汉字库
int fd_hzk16 = open("HZK16", O_RDONLY);
if(fd_hzk16 < 0)
{
printf("can't open HZK16\n");
return -1;
}
或者用mmap()映射成内存
但是我们需要知道HZK16该文件大小
因此使用int fstat(int fd, struct stat *statbuf);函数
struct stat hzk_stat;
if( fstat(fd_hzk16, &hzk_stat)
{
printf("can't get fstat\n");
return -1;
}
unsinged char* hzkmem = (unsigned char*)mmap(NULL, hzk_stat.st_size, PROT_READ , MAP_SHARED,fd_hzk16, 0);
if(hzkmem == (unsigned char*)-1)
{
printf("can't mmap\n");
return -1;
} //这样就可以访问数组一样访问汉字库
3)lcd_put_chinese
void lcd_put_chinese(int x, int y, unsigned char* str)
{
unsigned int area = str[0] - 0xA1; //区码,减去0xA1的原因是汉字库的行和列是从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+7-b+j*8, y+i, 0xffffff); /* 白 */
}
else
{
/* hide */
lcd_put_pixel(x+7-b+j*8, y+i, 0); /* 黑 */
}
}
}
}
4)lcd_put_pixel
unsigned int line_width;//LCD每行有多少字节
unsigned int pixel_width;//每个像素占多少字节
line_width = var.xres * var.bit_per_pixel / 8;
pixel_width = var.bit_per_pixel / 8;
/*color : 0x00RRGGBB*/
void lcd_put_pixel(int x, int y, unsigned int color)
{
unsigned char *pen_8 = fbmem+y*line_width+x*pixel_width; //char 1个字节
unsigned short *pen_16; //short 2个字节
unsigned int *pen_32; //4个字节
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 */
//因为16进制的,颜色码取位是565
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("can't surport %dbpp\n", var.bits_per_pixel);
break;
}
}
}
5)清屏: 全部设为黑色
memset(fbmem, 0, screen_size);