嵌入式project_数码相框_03_在LCD上显示字符

嵌入式Linux系列project->数码相框_03_在LCD上显示字符

LCD显示字符原理

要想在LCD上显示数据,需要在FrameBuffer中放入字符点阵数据,然后LCD控制器自动将数据打印在屏幕上;LCD屏幕上是很多密集的像素点,通过对各像素点的填色实现显示不同的图案;FrameBuffer是显存,操作系统在内存区域划分出一块区域当作显存,显存里存放的数据就是显示屏显示的内容,通过对显存里数据的刷新就会将屏幕刷新。下面是LCD显示的框架图:
在这里插入图片描述

想要编程实现字符显示需要以下几步:
  • 打开屏幕设备
  • 获得屏幕的可变和固定信息
  • 得到屏幕尺寸进行内存映射
  • 打开汉字库文件
  • 汉字库内存映射
  • 在屏幕上显示字符

该程序只是实现字符显示,框架并不完善。

LCD显示字符代码附上:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <linux/fb.h>

#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/

修改drivers/video/Makefile
在这里插入图片描述
make uImage
在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值