以下是关于Linux LCD的介绍和基本使用,博客结尾有LCDdemo,希望对您有所帮助。
如果我的文章让你感兴趣,可以点赞收藏关注一波。
目录
1.FrameBuffer
Frame 是帧的意思, buffer 是缓冲的意思,所以 Framebuffer 就是帧缓冲,这意味着Framebuffer 就是一 块内存,里面保存着一帧图像。帧缓冲(framebuffer)是 Linux 系统中的一种显示驱动接口,它将显示设备 (譬如 LCD) 进行抽象、 屏蔽了不同显示设备硬件的实现,对应用层抽象为一块显示内存(显存),它允 许上层应用程序直接对显示缓冲区进行读写操作,而用户不必关心物理显存的位置等具体细节,这些都由 Framebuffer 设备驱动来完成。
在 Linux 系统中,显示设备被称为 FrameBuffer 设备(帧缓冲设备),所以 LCD 显示屏自然而言就是 FrameBuffer 设备。 FrameBuffer 设备对应的设备文件为/dev/fbX(X 为数字, 0、 1、 2、 3 等) , Linux下可支持多个 FrameBuffer 设备,最多可达 32 个,分别为/dev/fb0 到/dev/fb31,开发板出厂系统中, /dev/fb0设备节点便是 LCD 屏。
应用程序读写/dev/fbX 就相当于读写显示设备的显示缓冲区(显存),譬如 LCD 的分辨率是 1024*600,每一个像素点的颜色用 24 位(譬如RGB888)来表示,那么这个显示缓冲区的大小就是1024 x 600 x 24 / 8 =1843200 个字节。
2. lcd屏基本概念
2.1像素
像素(pixel)是屏幕上显示颜色的最小单位,一个像素点就相当于一个RGB 小灯,通过控制R、G、B 这三种颜色的亮度就 可以显示出各种各样的色彩,而图像实际上是由非常多的像素点拼凑而成的,每个像素点各司其职,分别显示自己的颜色,这样也就构成了我们看到的一副完整的图像
像素点由红绿蓝(R\G\B)三个子像素构成,通过混合三个子像素的颜色比例,来合成一切想要的颜色
2.2分辨率
分辨率=画面水平方向的像素值 * 画面垂直方向的像素值,指的是像素点的数目,我们平常所说的1080p就是水平1920个像素点,竖直1080个像素点,也就是1920 * 1080, 2k是2560 * 1440,4k则是3840*2160,分辨率越高,所需要的显存越大。
分辨率与屏幕尺寸没有任何关系,有23寸的1080p屏幕,也有16寸的4k屏。只不过在不同尺寸的屏幕塞下更多的像素点罢了。
2.3色深
色深指在位图或视频帧缓冲区中用于表示单个像素的颜色的比特数,或者是用于单个像素的每个颜色分量的比特数,简单来说就是用来描述色彩丰富程度的参数。
色深越高,色彩数量就越多,所以颜色过渡越自然、丝滑,条纹状的等高线(或者说「色彩断层」)也越少。当色深足够高的时候,颜色和颜色之间可以非常平滑地过渡。
比如色深为1bit表示只能显示2^1=2个色阶,也就是指的是红/绿/蓝三者的任何一个原色都能被平均分成2份。以红色举例,一份是最暗的黑,一份是最亮的红。 色深为2bit表示红/绿/蓝能被平均分成2^2=4份,这时候除了最亮和最暗,就会有中间的过度。色深为8bit表示红/绿/蓝能被平均分成2^8=256份。
一般一个R、G、B 这三部分分别使用8bit 的数据,那么一个像素点就是8bit*3=24bit,也就是说一个像素点3 个字节,这种像素格式称为RGB888。如果在加入8bit 的Alpha(透明)通道的话一个像素点就是32bit,也就是4 个字节,这种像素格式称ARGB8888。
3.LCD 应用编程介绍
本节介绍如何对 FrameBuffer 设备(譬如 LCD)进行应用编程,应用程序通过对 LCD 设备节点/dev/fb0(假设 LCD 对应的设备节点是 /dev/fb0)进行 I/O 操作即可实现对 LCD 的显示控制,实质就相当于读写了 LCD 的显存,而显存是 LCD 的 显示缓冲区, LCD 硬件会从显存中读取数据显示到 LCD 液晶面板上。
在应用程序中,操作/dev/fbX 的一般步骤如下:
①、首先打开/dev/fbX 设备文件。
②、 使用 ioctl()函数获取到当前显示设备的参数信息,譬如屏幕的分辨率大小、像素格式,根据屏幕参数计算显示缓冲区的大小。
③、通过存储映射 I/O 方式将屏幕的显示缓冲区映射到用户空间(mmap)。
④、映射成功后就可以直接读写屏幕的显示缓冲区,进行绘图或图片显示等操作了。
⑤、完成显示后, 调用 munmap()取消映射、并调用 close()关闭设备文件。
4.使用 ioctl()获取屏幕参数信息
当打开 LCD 设备文件之后,需要先获取到 LCD 屏幕的参数信息,譬如 LCD 的 X 轴分辨率、 Y 轴分辨 率以及像素格式等信息,通过这些参数计算出 LCD 显示缓冲区的大小。
通 过 ioctl() 函 数 来 获 取 屏 幕 参 数 信 息 , 对 于 Framebuffer 设 备 来 说 , 常 用 的 request 包 括 FBIOGET_VSCREENINFO、 FBIOPUT_VSCREENINFO、 FBIOGET_FSCREENINFO。
- FBIOGET_VSCREENINFO:
表示获取 FrameBuffer 设备的可变参数信息,可变参数信息使用 struct fb_var_screeninfo 结 构 体 来 描 述 , 所 以 此 时 ioctl() 需 要 有 第 三 个 参 数 , 它 是 一 个 struct fb_var_screeninfo *指针,指向 struct fb_var_screeninfo 类型对象, 调用 ioctl()会将 LCD 屏的可变参 数信息保存在 struct fb_var_screeninfo 类型对象中,如下所示:
struct fb_var_screeninfo fb_var;
ioctl(fd, FBIOGET_VSCREENINFO, &fb_var);
- FBIOPUT_VSCREENINFO:
表示设置 FrameBuffer 设备的可变参数信息,既然是可变参数,那说明应用层可对其进行修改、重新配置,当然前提条件是底层驱动支持这些参数的动态调整,譬如在我们的 Windows 系统中,用户可以修改屏幕的显示分辨率,这就是一种动态调整。同样此时 ioctl() 需要有第三个参数, 也是一个 struct fb_var_screeninfo *指针,指向 struct fb_var_screeninfo 类型对 象,表示用 struct fb_var_screeninfo 对象中填充的数据设置 LCD, 如下所示:
struct fb_var_screeninfo fb_var = {0};
/* 对 fb_var 进行数据填充 */
......
......
/* 设置可变参数信息 */
ioctl(fd, FBIOPUT_VSCREENINFO, &fb_var);
- FBIOGET_FSCREENINFO:
表示获取 FrameBuffer 设备的固定参数信息,既然是固定参数,那就 意味着应用程序不可修改。固定参数信息使用struct fb_fix_screeninfo结构体来描述,所以此时ioctl() 需要有第三个参数,它是一个 struct fb_fix_screeninfo *指针,指向 struct fb_fix_screeninfo 类型对象, 调用 ioctl()会将 LCD 的固定参数信息保存在 struct fb_fix_screeninfo 对象中,如下所示:
struct fb_fix_screeninfo fb_fix;
ioctl(fd, FBIOGET_FSCREENINFO, &fb_fix);
5.存储映射
存储映射 I/O(memory-mapped I/O) 是一种基于内存区域的高级 I/O 操作,它能将一个文件映射到进程 地址空间中的一块内存区域中, 当从这段内存中读数据时,就相当于读文件中的数据(对文件进行 read 操 作) ,将数据写入这段内存时,则相当于将数据直接写入文件中(对文件进行 write 操作) 。这样就可以在不使用基本 I/O 操作函数 read()和 write()的情况下执行 I/O 操作。
5.1 mmap()函数
为了实现存储映射 I/O 这一功能,我们需要告诉内核将一个给定的文件映射到进程地址空间中的一块 内存区域中,这由系统调用 mmap()来实现。 其函数原型如下所示:
//函数原型
#include <sys/mman.h>
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
函数参数和返回值含义如下:
addr:
参数 addr 用于指定映射到内存区域的起始地址。通常将其设置为 NULL,这表示由系统选择该 映射区的起始地址,这是最常见的设置方式;如果参数 addr 不为 NULL,则表示由自己指定映射区的起始 地址,此函数的返回值是该映射区的起始地址。
length:
参数 length 指定映射长度, 表示将文件中的多大部分映射到内存区域中,以字节为单位,譬如 length=1024 * 4,表示将文件的 4K 字节大小映射到内存区域中。
offset:
文件映射的偏移量,通常将其设置为 0,表示从文件头部开始映射;所以参数 offset 和参数 length 就确定了文件的起始位置和长度, 将文件的这部分映射到内存区域中。
fd:
文件描述符,指定要映射到内存区域中的文件。
prot:
参数 prot 指定了映射区的保护要求,可取值如下:
PROT EXEC:映射区可执行,
PROT READ:映射区可读;
PROT WRITE:映射区可写;
PROT NONE:映射区不可访问。对指定映射区的保护要求不能超过文件 open()时的访问权限,譬 如,文件是以只读权限方式打开的,那么对映射区的不能指定为 PROT_WRITE。
flags:
参数 flags 可影响映射区的多种属性, 参数 flags 必须要指定以下两种标志之一:
MAP SHARED:
此标志指定当对映射区写入数据时,数据会写入到文件中,也就是会将写入到映射区中的数据更新到文件中,并且允许其它进程共享。
MAP PRIVATE:
此标志指定当对映射区写入数据时,会创建映射文件的一个私人副本(copy-onwrite),对映射区的任何操作都不
会更新到文件中,仅仅只是对文件副本进行读写。返回值:
成功情况下,函数的返回值便是映射区的起始地址;发生错误时,返回(void *)-1, 通常使用 MAP_FAILED 来表示, 并且会设置 errno 来指示错误原因。
5.2 munmap()函数
通过 open()打开文件,需要使用 close()将将其关闭;同理,通过 mmap()将文件映射到进程地址空间中的一块内存区域中,当不再需要时,必须解除映射,使用munmap()解除映射关系,其函数原型如下所示:
//函数原型
#include <sys/mman.h>
int munmap(void *addr, size_t length);
函数参数含义如下:
munmap()系统调用解除指定地址范围内的映射, 参数 addr 指定待解除映射地址范围的起始地址,它必须是系统页大小的整数倍;
参数 length 是一个非负整数,指定了待解除映射区域的大小(字节数),被解除映射的区域对应的大小也必须是系统页大小的整数倍
需要注意的是,当进程终止时也会自动解除映射(如果程序中没有显式调用 munmap()),但调用 close() 关闭文件时并不会解除映射。
通常将参数 addr 设置为 mmap()函数的返回值,将参数 length 设置为 mmap()函数的参数 length,表示解除整个由 mmap()函数所创建的映射。
6.颜色显示demo
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <linux/fb.h>
int main()
{
struct fb_var_screeninfo fb_var; //可变参数信息
struct fb_fix_screeninfo fb_fix; //固定参数信息
unsigned int size; //定义无符号屏幕缓冲区大小
int *screen = NULL; //定义内存映射到内存区域的起始地址为NULL
int fd = open("/dev/fb0", O_RDWR); //以可读、可写方式打开文件 fd文件描述符
if(fd < 0) //判断一下,打开失败
{
perror("open fd error");
exit(-1);
}
//ioctl函数获取显示屏信息并存储
ioctl(fd, FBIOGET_VSCREENINFO, &fb_var);
ioctl(fd, FBIOGET_FSCREENINFO, &fb_fix);
size = fb_fix.line_length * fb_var.yres; //屏幕显示缓冲区的大小
int width = fb_var.xres; //屏幕的水平分辨率
int height = fb_var.yres; //屏幕的垂直分辨率
int i = 0;
//printf("%d, %d, %d", size, width, height);
//内存映射
screen = mmap(NULL, size, PROT_WRITE, MAP_SHARED, fd, 0);
if(screen == NULL)
{
perror("mmap error");
exit(-1);
}
for(i = 0; i < size/4; i++)
{
//rgb
//red = 11111111 = 0xFF
//green = blue = 0xFF
//
screen[i] = 0x4caeb1;
}
munmap(screen, size); // 取消映射
close(fd); // 关闭文件
return 1;
}
参数解析:
xres、 yres 获取到屏幕的水平分辨率和垂直分辨率,bits_per_pixel表示像素深度 bpp,即每一个像素点使用多少个 bit 位来描述它的颜色,通过 xres * yres * bits_per_pixel / 8 计算可得到整个显示缓存区的大小。
red、 green、 blue 描述了 RGB 颜色值中 R、 G、 B 三种颜色通道分别使用多少 bit 来表示以及它们各自 的偏移量,通过 red、 green、 blue 变量可知道 LCD 的 RGB 像素格式,譬如是 RGB888 还是 RGB565,亦或 者是 BGR888、 BGR565 等。
line_length 表示屏幕的一行像素点有多少个字节, 通常可以使用 line_length * yres 来得到屏幕显示缓冲区的大小。
使用交叉编译工具编译上述示例代码,将编译得到的可执行文件拷贝到开发板 Linux 系统的用户家目录下,并直接运行它,便可得到结果!
感谢阅读我的博客,如果你喜欢的话,不妨点赞收藏关注一波!