嵌入式Linux系列project->数码相框_03_在LCD上显示字符
要想在LCD上显示数据,需要在FrameBuffer中放入字符点阵数据,然后LCD控制器自动将数据打印在屏幕上;LCD屏幕上是很多密集的像素点,通过对各像素点的填色实现显示不同的图案;FrameBuffer是显存,操作系统在内存区域划分出一块区域当作显存,显存里存放的数据就是显示屏显示的内容,通过对显存里数据的刷新就会将屏幕刷新。下面是LCD显示的框架图:
该程序只是实现字符显示,框架并不完善。
#include
#include
#include
#include
#include
#include
#include
#include "font_16_8.h"
int fd_fb ;
int fd_hzk_16 ;
/* 接收LCD可变信息的结构体 */
static struct fb_var_screeninfo var;
/* 接收LCD固定信息的结构体 */
static struct fb_fix_screeninfo fix;
/* framebuffer映射的内存 */
static unsigned char *fb_mem ;
/* 屏幕尺寸 */
static int screen_size ;
/* 汉字库映射的内存 */
static unsigned char *hzk_mem ;
/* fstat状态结构体 */
static struct stat hzk_stat ;
unsigned int line_width ; //一行宽度
unsigned int pixel_width ; //像素宽度
unsigned char str[] = "中" ;
将变量全部定义为全局的,以方便函数使用
int main(int argc, char **argv)
{
/* 打开lcd屏幕设备 失败返回-1 */
fd_fb = open("/dev/fb0", O_RDWR) ; //以读写方式打开设备
if(fd_fb < 0)
{
printf("can't open /dev/fb0\n") ;
return -1 ;
}
/* 获取屏幕固定信息
* 如果获取失败返回-1
*/
if(ioctl(fd_fb, FBIOGET_FSCREENINFO, &var))
{
printf("can't get var\n") ;
return -1 ;
}
/* 获取屏幕固定信息
* 如果获取失败返回-1
*/
if(ioctl(fd_fb, FBIOGET_FSCREENINFO, &fix))
{
printf("can't get fix\n") ;
return -1 ;
}
/* 屏幕尺寸:X方向分辨率*Y方向分辨率*每个像素占多少位/8
* 得到尺寸的字节数
*/
screen_size = var.xres * var.yres * var.bits_per_pixel / 8 ;
/* 得到显示屏一行的大小和每个像素占的大小 */
line_width = var.xres * var.bits_per_pixel / 8 ;
pixel_width = var.bits_per_pixel / 8 ;
/* 映射frame buffer内存 */
fb_mem = (unsigned char *)mmap(NULL, screen_size, PROT_READ | PROT_WRITE, MAP_SHARED,fd_fb, 0) ;
if(fb_mem == (unsigned char *)-1)
{
printf("can't mmap mem\n") ;
return -1 ;
}
/* 打开汉字库文件 失败返回-1 */
fd_hzk_16 = open("HZK16", O_RDONLY) ;
if(fd_hzk_16 < 0)
{
printf("can't open hzk_16\n") ;
return -1 ;
}
/* 得到fd_hzk_16文件里的信息
* 失败返回-1
*/
if(fstat(fd_hzk_16, &hzk_stat))
{
printf("can't get fstat\n") ;
return -1 ;
}
/* 映射汉字库的内存 */
hzk_mem = (unsigned char *)mmap(NULL, hzk_stat.st_size, PROT_READ, MAP_SHARED, fd_hzk_16, 0) ;
if(hzk_mem == (unsigned char *)-1)
{
printf("can't mmap for hzk16\n") ;
return -1 ;
}
/* 清屏:全部设为黑色
* frame buffer里的值设为0, 大小为整个屏幕screen_size
*/
memset(fb_mem, 0, screen_size) ;
/* 在屏幕中央显示字符 */
lcd_put_ascii(var.xres/2, var.yres/2, 'A') ;
printf("chinese code:%02x %02x\n", str[0], str[1]) ;
/* 在字符后面显示中文字符 */
lcd_put_chinese(var.xres/2+8, var.yres/2, str) ;
return 0 ;
}
/* 在LCD屏上用颜色color填充像素点
* color :0x00RRGGBB
*/
void lcd_put_pixel(int x, int y, unsigned int color)
{
/* 得到当前位置像素点的内存
* 第y行,第x列
* fb_mem为显存基址
*/
unsigned char *pen_8 = fb_mem + y*line_width + x*pixel_width ;
unsigned short *pen_16 ;
unsigned int *pen_32 ;
unsigned int blue, red, green ;
pen_16 = (unsigned short *)pen_8 ;
pen_32 = (unsigned int *)pen_8 ;
/* 根据显示屏的bits_per_pixel参数进行颜色匹配 */
switch(var.bits_per_pixel)
{
case 8:
*pen_8 = color ;
break ;
case 16 :
/* 重新构造16位颜色
* color:565
*/
red = (color >> 16) & 0xff ;
green = (color >> 8) & 0xff ;
blue = (color >> 0) & 0xff ;
/* red取高5位
* green取高6位
* blue取高5位
* 通过移位合并rgb颜色
*/
color = ((red>>3)<<11) | ((green>>2)<<5) | (blue>>3) ;
break ;
case 32 :
/* 32位模式无需变化 */
*pen_32 = color ;
break ;
default:
printf("can't surport %dbpp\n", var.bits_per_pixel) ;
break ;
}
}
该函数主要实现在LCD的像素点上填色,根据lcd的bits_per_pixel参数实现不同位颜色的显示;另一种含义就是在映射的fb_mem中填入数据,y参数为显示的行数,x参数为在第y行的第几列显示,unsigned char pen_8 = fb_mem + yline_width + x*pixel_width 这句程序是获得当前显示的显存地址,然后在该地址填入数据,实现显示。
输入的颜色为32位,格式:0x00RRGGBB,还要支持8bpp,16bpp的显示,8bpp需要使用调色板,16bpp需要将32位颜色分解位16位,也就是颜色格式为565,需要移位将颜色数据重构。
/* Lcd显示ascii编码的字符 */
void lcd_put_ascii(int x, int y, unsigned char ch)
{
/* 取字符ch在字体头文件的地址 */
unsigned char *dots = (unsigned char *)&fontdata_8x16[ch*16] ;
int i, b ;
unsigned char byte ;
/* 一个字符占16字节 循环16次*/
for(i = 0; i < 16; i ++)
{
byte = dots[i] ; //取出当前行
/* 每个字节的8位 从高位到低位 */
for(b = 7; b >= 0; b--)
{
/* 当前位置值是1则显示黑色 */
if(byte & (1 << b))
{
/* show
* x+7-b 在内存里是从低位到高位
*/
lcd_put_pixel(x+7-b, y+i, 0xffffff) ; //黑
}
/* 当前位置值是0则显示白色 */
else
{
/* hide
* x+7-b 在内存里是从低位到高位
*/
lcd_put_pixel(x+7-b, y+i, 0) ; //白
}
}
}
}
在屏幕上显示ASCII字符,需要ASCII字符的字模点阵数据;这里采用16*8的点阵:
显示一个ASCII字符需要16字节来存储点阵,点阵为0的地方不显示,为1的地方绘颜色,就能实现在LCD上显示了。
/* 显示中文汉字 */
void lcd_put_chinese(int x, int y, unsigned char *str)
{
/* 汉字的编码前面一个字节为区码
* 后面一个字节为位码
* 区码和位码都从0xA1开始
*/
unsigned int area = str[0] - 0xA1 ;
unsigned int where = str[1] - 0xA1 ;
unsigned int *dots = (unsigned int *)hzk_mem + (area*94+where)*32 ;
unsigned char byte ;
int i, j, b ;
/* 该汉字库为16位 */
for(i = 0; i < 16; i ++)
{
/* 汉字占两个字节 */
for(j = 0; j < 2; j ++)
{
byte = dots[i*2 + j] ;
/* 每个字节的8位 从高位到低位 */
for(b = 7; b >= 0; b --)
{
/* 当前位置值是1则显示蓝色 */
if(byte & (1 << b))
{
/* show */
lcd_put_pixel(x+j*8+7-b, y+i, 0x0000ff) ;
}
/* 当前位置值是1则显示黑色 */
else
{
/* hide */
lcd_put_pixel(x+j*8+7-b, y+i, 0) ;
}
}
}
}
}
显示中文汉字比ASCII稍复杂点,汉字的编码为两字节,高字节为区码,低字节为位码,区码和位码都是从0xA1开始进行汉字编码,所以区码和位码都要减去0xA1。
配置、修改内核支持把lcd_jz2440.c编译进去
cp lcd_jz2440.c ~/work/linux_source/linux-3.4.2/drivers/video/