> 技术文档 > Linux下的lcd屏幕显示操作

Linux下的lcd屏幕显示操作


1.lcd屏幕显示的基本原理

  • 像素
    屏幕上显示颜色的最小单位,英文叫 pixel。注意,位图(如jpg、bmp等格式的常见图片)也是由一个个的像素点构成的,跟屏幕的像素点的概念一样。原理上讲,将一张位图显示到屏幕上,就是将图片上的像素点一个个复制到屏幕像素点上。

  • 分辨率:
    • 宽、高两个维度上的像素点数目。
    • 分辨率越高,所需要的显存越大。

  • 色深:
    • 每个像素所对应的内存字节数,一般有8位、16位、24位或32位
    • GEC6818开发板的屏幕的色深是32位的
    • 32位色深的屏幕一般被称为真彩屏,或1600万色屏。

色深决定了一个像素点所能表达的颜色的丰富程度,色深越大,色彩表现力越强。

 2. mmap映射

虽然LCD设备本质上也可以看作是一个文件,在文件系统中有其对应的设备节点,可以像普通文件一样对其进行读写操作(read/write),但由于对字符设备的读写操作是以字节流的方式进行的,因此除非操作的图像尺寸刚好与屏幕尺寸完全一致,如下图所示,图片的宽高与LCD的宽高完全一致,否则将会画面会乱。并且效率相对较低

图像的尺寸大小是随机的,因此更方便的做法是为LCD做内存映射,将屏幕的每一个像素点跟映射内存一一对应,而映射内存可以是二维数组,因此就可以非常方便地通过操作二维数组中的任意元素,来操作屏幕中的任意像素点了。这里的映射内存,有时被称为显存。

  1. LCD上面显示的图像色彩,由其对应的内存的数据决定
  2. 映射内存的大小至少得等于LCD的真实尺寸大小
  3. 映射内存的大小可以大于LCD的真实尺寸,有利于优化动态画面(视频)体验

bmp格式

文件组成

        特征是都没有任何的压缩,因此文件尺寸都比较大,不适合在互联网上传播,优点是数据读取出来即可使用,无需任何解码器支持。

数据段名称 对应大小 bmp头文件 14 信息头

40

调色板 从54开始,有像素点的大小决定

注意 

a. 4字节倍数行距
BMP图片文件的一个重要规则是,每行数据字节数必须是4的倍数,假设某BMP图片的分辨率是 65 × 200,也就是说宽是 65 像素,假设每个字节色深是24bits(即3字节),那么这张图片一行的实际数据量是 65×3=195个字节,但195不是4的倍数,因此在每一行的末尾都会添加一个无效字节,将行距尺寸补到196个字节。

int pad = ((4-( width * bpp/8 ) % 4)) % 4;//计算增加的无效字节

其次,在处理图像数据的时候,直接跳过这些无效字节就好了。

 b. 上下颠倒
BMP图片中的RGB数据是上下颠倒的,因此文件数据中的最后一行是图像的最上面第一行。需要注意的是,上下是颠倒的,但是左右是正常的,因此在处理数据的时候不能从最后一个字节开始,而是从最末一行的首字节开始。

3.jpg格式

在bmp格式下可以直接读文件中RGB值进行显示,但是在jpg格式下的文件必须先进行解码

 jpg第三方开源解码器下载与配置使用

在嵌入式来发中,运用第三方库套路基本一致,所以在这详细介绍一下

(1)下载

直接官方进行下载Independent JPEG Group

不要在Windows下进行解压,会导致问价损坏,将压缩包放在Linux下进行解压

可以阅读README了解开发原理

如何安装

如何使用和例程

在install.txt中

移植三件套./config make makefile

当运行config的时候会检查各种配置,配置检查完成会生成makefile

在开发板中使用的编译工具为gcc

在install,txt中说明需要使用CC=命令进行改变便以及

 将所有的编译命令的前缀改为arrch64-linux

 生成makefile

指定其他路径 

 开启4条线程

 

 提取

提取完成后需要将。文件夹中的lib 与include 中的文件导入到所要使用的文件对应目录中使用一下命令进行编译

 -L:指定库路径    -l指定库名称 -I(i)指定头文件路径 

需要将lib的函数库文件拷贝到开发板的/lib/里面  解决掉找不到库文件的问题

用于指明编译工具

--host=编译工具 

用于指明存放路径

--prifix=存放路径                                         

 ​​​​​

(2)通过官方例程example进行修改 

以下为例程

METHODDEF(void)my_error_exit (j_common_ptr cinfo){ /* cinfo->err really points to a my_error_mgr struct, so coerce pointer */ my_error_ptr myerr = (my_error_ptr) cinfo->err; /* Always display the message. */ /* We could postpone this until after returning, if we chose. */ (*cinfo->err->output_message) (cinfo); /* Return control to the setjmp point */ longjmp(myerr->setjmp_buffer, 1);}struct RGB{ char rad; cahr green; char blue;};int read_JPEG_file(char *filename, int *screen_buf){ struct jpeg_decompress_struct cinfo; struct my_error_mgr jerr; FILE *infile; /* source file */ struct RGB *buffer; /* Output row buffer */ int row_stride; /* physical row width in output buffer */ if ((infile = fopen(filename, \"rb\")) == NULL) { fprintf(stderr, \"can\'t open %s\\n\", filename); return 0; } /* Step 1: allocate and initialize JPEG decompression object */ /* We set up the normal JPEG error routines, then override error_exit. */ cinfo.err = jpeg_std_error(&jerr.pub); jerr.pub.error_exit = my_error_exit; /* Establish the setjmp return context for my_error_exit to use. */ if (setjmp(jerr.setjmp_buffer)) { /* If we get here, the JPEG code has signaled an error. * We need to clean up the JPEG object, close the input file, and return. */ jpeg_destroy_decompress(&cinfo); fclose(infile); return 0; } if ((infile = fopen(filename, \"r\")) == NULL) // 打开源文件 { fprintf(stderr, \"can\'t open %s\\n\", filename); return 0; } /* Step 1: allocate and initialize JPEG decompression object */ jpeg_create_decompress(&cinfo); /* Step 2: specify data source (eg, a file) */ jpeg_stdio_src(&cinfo, infile); /* Step 3: read file parameters with jpeg_read_header() */ (void)jpeg_read_header(&cinfo, TRUE); /*开始解码*/ (void)jpeg_start_decompress(&cinfo); row_stride = cinfo.output_width * cinfo.output_components; /* Make a one-row-high sample array that will go away when done with image */ buffer = malloc(row_stride); int x, y; int calor; /*逐行进行数据读取 */ for (y = 0; cinfo.output_scanline < cinfo.output_height; y++) { /*将数据的一行读取出来方入buffer中 */ (void)jpeg_read_scanlines(&cinfo, (JSAMPARRAY)&buffer, 1); for (x = 0; x < cinfo.output_width; x++) { calor = (buffer[x].rad << 16) | (buffer[x].green << 8) | (buffer[x].blue); screen_disp(x, y, screen_buf, calor); } /* Assume put_scanline_someplace wants a pointer and sample count. */ // put_scanline_someplace(buffer[0], row_stride); } (void)jpeg_finish_decompress(&cinfo); jpeg_destroy_decompress(&cinfo); fclose(infile); return 1;}

4.获取lcd硬件设备参数

硬件参数都是根据产品手册,来填入内核所规定的某一个结构体当中,我们要做的就是去读取这些硬件参数

struct fb_fix_screeninfo{ char id[16];  /* identification string eg \"TT Builtin\" */ unsigned long smem_start; /* Start of frame buffer mem */ /* (physical address) */ __u32 smem_len;  /* Length of frame buffer mem */ __u32 type;  /* see FB_TYPE_* */ __u32 type_aux;  /* Interleave for interleaved Planes */ __u32 visual; /* see FB_VISUAL_* */ __u16 xpanstep;  /* zero if no hardware panning */ __u16 ypanstep;  /* zero if no hardware panning */ __u16 ywrapstep; /* zero if no hardware ywrap */ __u32 line_length; /* length of a line in bytes */ ... ...};struct fb_var_screeninfo{ __u32 xres;  /* 可见区宽度(单位:像素) */ __u32 yres;  /* 可见区高度(单位:像素) */ __u32 xres_virtual; /* 虚拟区宽度(单位:像素) */ __u32 yres_virtual; /* 虚拟区高度(单位:像素) */ __u32 xoffset; /* 虚拟区到可见区x轴偏移量 */ __u32 yoffset; /* 虚拟区到可见区y轴偏移量 */ __u32 bits_per_pixel; /* 色深 */ // 像素内颜色结构 struct fb_bitfield red; // 红色 struct fb_bitfield green; // 绿色 struct fb_bitfield blue; // 蓝色 struct fb_bitfield transp;// 透明度 ... ...};struct fb_bitfield{ __u32 offset; /* 颜色在像素内偏移量 */ __u32 length; /* 颜色占用数位长度 */ ... ...};

一下是显示lcd信息获取的实例(使用ioctl)

int lcd;struct fb_fix_screeninfo fixinfo; // 固定属性struct fb_var_screeninfo varinfo; // 可变属性//注意在初始化结构体的时候一定要清空结构体void get_fixinfo(){ if(ioctl(lcd, FBIOGET_FSCREENINFO, &fixinfo) != 0) { perror(\"获取LCD设备固定属性信息失败\"); return; }}void get_varinfo(){ if(ioctl(lcd, FBIOGET_VSCREENINFO, &varinfo) != 0) { perror(\"获取LCD设备可变属性信息失败\"); return; }}

注意:在初始化结构体的时候一定要清空结构体,不然会出现乱码

5.双缓冲机制

双缓冲机制就是在虚拟区中填写数据,以调整(__u32 xoffset;)/* 虚拟区到可见区x轴偏移量 */
    __u32 yoffset; /* 虚拟区到可见区y轴偏移量 */来实现