嵌入式Frambuff编程

一、如何编写LCD驱动程序

在Linux系统中通过Framebuffer驱动程序来控制LCD。Frame是帧的意思,buffer是缓冲的
意思,这意味着Framebuffer就是一块内存,里面保存着一帧图像。Framebuffer中保存着
一帧图像的每一个像素颜色值,假设LCD的分辨率是1024x768,每一个像素的颜色用32
位来表示,那么Framebuffer的大小就是:1024x768x32/8=3145728字节。

二、编写LCD驱动程序需要做什么呢

1、需要控制LCD上哪个像素点会亮,像素是组成图形的最小单位
2、LCD对应着内核里面的Frambuff,即需要配置Frambuff这个空间
3、Framebuffer是LCD的驱动程序,Framebuffer是保留屏幕上每个像素数据,
Framebuffer是从头取到尾,周而复始。
Fram是保存一帧数据的内存
bpp:每个像素用多少位表示,
内存里,每个位置保留的是帧
一个像素用三原色表示,即32位或16位,那么要知道该像素的地址,地址即字节(像素>字节)
那就需要知道LCD驱动屏幕里的可变信息放入一个静态结构体,注意驱动里的变量static
4、要知道像素绝对地址,修改地址值表示像素颜色
5、要知道把这个像素变成什么颜色*pen = color

在这里插入图片描述
三、运行程序:

============================
开发步骤:
1、打开dev下LCD驱动
2、获取LCD屏幕可变参数(分辨率、BPP)放入var
3、main中计算出屏幕相关数据,行字节,像素字节,总字节,屏幕的绝对地址
4、初始化屏幕全白memset,设置某些点为红
5、取消内存映射munmap
6、关闭驱动文件
============================
1、
#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>
#include <sys/ioctl.h>

static int fd_fb;
static struct fb_var_screeninfo var;	/* Current var   把可变的参数放var里面*/
static int screen_size;
static unsigned char *fb_base;
static unsigned int line_width;//行宽
static unsigned int pixel_width;//每个像素占据多少字节

/**********************************************************************
 * 函数名称: lcd_put_pixel
 * 功能描述: 在LCD指定位置上输出指定颜色(描点)
 * 输入参数: x坐标,y坐标,颜色
 * 输出参数: 无
 * 返 回 值: 会
 * 修改日期        版本号     修改人	      修改内容
 * -----------------------------------------------
 * 2020/05/12	     V1.0	  zh(angenao)	      创建
 ***********************************************************************/ 
void lcd_put_pixel(int x, int y, unsigned int color)//某个点描成什么颜色
{
	unsigned char *pen_8 = fb_base+y*line_width+x*pixel_width;//偏移地址加fb_base就是绝对地址
	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://32位转换为16位
		{
			/* 565 */
			//原本8棕,8红,8绿,8蓝
			red   = (color >> 16) & 0xff;
			green = (color >> 8) & 0xff;
			blue  = (color >> 0) & 0xff;
			color = ((red >> 3) << 11) | ((green >> 2) << 5) | (blue >> 3);//取红高5位,绿高6位,蓝高5位
			
			*pen_16 = color;
			break;
		}
		case 32:
		{
			*pen_32 = color;
			break;
		}
		default:
		{
			printf("can't surport %dbpp\n", var.bits_per_pixel);
			break;
		}
	}
}

int main(int argc, char **argv)
{
	int i;
	
	fd_fb = open("/dev/fb0", O_RDWR);//打开LCD驱动
	if (fd_fb < 0)
	{
		printf("can't open /dev/fb0\n");
		return -1;
	}
	if (ioctl(fd_fb, FBIOGET_VSCREENINFO, &var))//获得屏幕里的可变信息
	{
		printf("can't get var\n");
		return -1;
	}

	line_width  = var.xres * var.bits_per_pixel / 8;//一行宽度就是行像素数*一个像素多少位/8,即一行占多少字节
	pixel_width = var.bits_per_pixel / 8;//bpp/8,一个像素占多少字节
	screen_size = var.xres * var.yres * var.bits_per_pixel / 8;
	fb_base = (unsigned char *)mmap(NULL , screen_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd_fb, 0);
	//通过这个固定操作可以得到frambuff地址,mmap,驱动空间地址映射到用户空间
	if (fb_base == (unsigned char *)-1)
	{
		printf("can't mmap\n");
		return -1;
	}

	/* 清屏: 全部设为白色 */
	memset(fb_base, 0xff, screen_size);//把frambuff里面的全部变为白色

	/* 随便设置出100个为红色 */
	for (i = 0; i < 100; i++)
		lcd_put_pixel(var.xres/2+i, var.yres/2, 0xFF0000);
	
	munmap(fb_base , screen_size);//取消内存映射,后关闭文件
	close(fd_fb);
	
	return 0;	
}




2、arm-buildroot-linux-gnueabihf-gcc frambuff1.c -o frambuff1(ubuntu下交叉编译)
3、scp frambuff1  root@192.168.5.9:/root/frambuff(传给imx6ull)
4、运行

二、字符编程

ASCII->ANSI(GB2312)->UNICODE
1、ASCII
他们常用字母就 26 个,区分大小写、加上标点
符号也没超过 127 个,每个字符用一个字节来表示就足够了。一个字节的 7 位就
可以表示 128 个数值,在 ASCII 码中最高位永远是 0。
2、ASNI
ASNI 是 ASCII 的扩展,向下包含 ASCII。对于 ASCII 字符仍以一个字节来
表示,对于非 ASCII 字符则使用 2 字节来表示。
比如在中国大陆地区,ANSI 的默认编码是 GB2312;在港澳台地区默认编码是 BIG5。
以数值“0xd0d6”为例,对于 GB2312 编码它表示“中”;对于 BIG5 编码它表示“笢”。
所以对于 ANSI 编码的 TXT 文件,如果你打开它发现乱码,那么还得再次细分它的具体编码。
!!!最高位0表示ASCII码,最高位1表示非ASCII码
3、 UNICODE(每个字符唯一对应,三个字节表示)
在 ANSI 标准中,很多种文字都有自己的编码标准,汉字简体字有 GB2312、繁体字有 BIG5,
这难免同一个数值对应不同字符。比如数值“0xd0d6”,对于GB2312 编码它表示“中”;
对于 BIG5 编码它表示“笢”。这造成了使用 ANSI 编码保存的文件,不适合跨地区交流。
UNICODE 编码就是解决这类问题:对于地球上任意一个字符,都给它一个唯一的数值。
UNICODE 仍然向下兼容 ASCII,但是对于其他字符会有对应的数值,比如对于“中”、“笢”,
它们的数值分别是:0x4e2d、0x7b22UNICODE 中的数值范围是 0x0000 至 0x10FFFF,有 1,114,111 即 100 多万
个数值,可以表示 100 多万个字符,足够地球人使用了。
问题:假如表示A中,0X41 0X4e  0X2d,如何知道后面两个字节共同表示‘中’呢,这就用的我们的编码实现,
或许可以每个字符都用3字节表示,但有些浪费。那就用2字节表示(16位),也可以表示绝大多数字符。
但这个方法最大的问题,会造成差错传播,中间传丢一个文件,后面全错。

!!!怎么表示一个 UNICODE 数值?
(1)UTF-16 LE(小端字节序,不重要的放尾部)
(2)UTF-16 BE
(3)UTF8 (容错率最高)
(4)BOM_UTF-8(表示编出来的码前面会多出来一个头部表示这个码是UTF-8)

对于其中的 ASCII 字符,在 UTF8 文件中直接用其 ASCII 码来表示,比如上
图中的 0x61 表示字符 a、0x62 表示字符 b。上图中的 3 个字节“0xe4 0xb8 
0xad”表示的数值是 0x4e2d,对应“中”的 UNICODE 码。
对于非 ASCII 字符,使用变长的编码:每一个字节的高位都自带长度信息。
0xe4 的二进制是“11100100”,高位有 3 个 1,表示从当前字节起
有 3 字节参与表示 UNICODE;
0xb8 的二进制是“10111000”,高位有 1 个 1,表示从当前字节起有 1 字节
参与表示 UNICODE;
0xad 的二进制是“10101101”,高位有 1 个 1,表示从当前字节起有 1 字节
参与表示 UNICODE;
除去高位的“1110”、“10”、“10”后,剩下的二进制数组合起来得到
“01001110001101”,它就是 0x4e2d,即“中”的 UNICODE 值。
使用 UTF8 编码时,即使 TXT 文件中丢失了某些数据,也只会影响到当前字
符的显示,后面的字符不受影响。

要在 LCD 中显示一个 ASCII 字符,即英文字母这些字符,首先是要找到字
符对应的点阵(8*16的点阵)(8个像素,16个字节)。在 Linux 内核源码中有这个文件:lib\fonts\font_8x16.c,

在这里插入图片描述

#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>
#include <sys/ioctl.h>

#define FONTDATAMAX 4096



int fd_fb;
struct fb_var_screeninfo var;	/* Current var */
int screen_size;
unsigned char *fbmem;
unsigned int line_width;
unsigned int pixel_width;
/**********************************************************************
 * 函数名称: lcd_put_pixel
 * 功能描述: 在LCD指定位置上输出指定颜色(描点)
 * 输入参数: x坐标,y坐标,颜色
 * 输出参数: 无
 * 返 回 值: 会
 * 修改日期        版本号     修改人	      修改内容
 * -----------------------------------------------
 * 2020/05/12	     V1.0	  zh(angenao)	      创建
 ***********************************************************************/ 
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 */
			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;
		}
	}
}
/**********************************************************************
 * 函数名称: lcd_put_ascii
 * 功能描述: 在LCD指定位置上显示一个8*16的字符
 * 输入参数: x坐标,y坐标,ascii码
 * 输出参数: 无
 * 返 回 值: 无
 * 修改日期        版本号     修改人	      修改内容
 * -----------------------------------------------
 * 2020/05/12	     V1.0	  zh(angenao)	      创建
 ***********************************************************************/ 
void lcd_put_ascii(int x, int y, unsigned char c)
{
	unsigned char *dots = (unsigned char *)&fontdata_8x16[c*16];//16字节数据的起始地址放在dots里面
	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 *///一个像素8*16,像素最开始的地方(x,y)
				lcd_put_pixel(x+7-b, y+i, 0xffffff); /* 白 */
			}
			else
			{
				/* hide */
				lcd_put_pixel(x+7-b, y+i, 0); /* 黑 */
			}
		}
	}
}

int main(int argc, char **argv)
{
	fd_fb = open("/dev/fb0", O_RDWR);
	if (fd_fb < 0)
	{
		printf("can't open /dev/fb0\n");
		return -1;
	}
	if (ioctl(fd_fb, FBIOGET_VSCREENINFO, &var))
	{
		printf("can't get var\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("can't mmap\n");
		return -1;
	}

	/* 清屏: 全部设为黑色 */
	memset(fbmem, 0, screen_size);

	lcd_put_ascii(var.xres/2, var.yres/2, 'A'); /*在屏幕中间显示8*16的字母A*/
	
	munmap(fbmem , screen_size);
	close(fd_fb);
	
	return 0;	
}
2、arm-buildroot-linux-gnueabihf-gcc frambuff1.c -o frambuff1(ubuntu下交叉编译)
3、scp frambuff1  root@192.168.5.9:/root/frambuff(传给imx6ull)
4、运行

三、中文字符的点阵显示

要用点阵表示汉字,这里我们需要知道点阵文件进行比对,用到的标准是ANSI里面的(GB2312),
32字节表示,16*8*2;
1、arm-buildroot-linux-gnueabihf-gcc -finput-charset=UTF-8 -fexec-charset=GB2312 chin.c -o chin
2、
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

￴ㅤ￴￴ㅤ9527超级帅

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值