首页 > 代码库 > linux驱动开发之framebuffer驱动介绍
linux驱动开发之framebuffer驱动介绍
framebuffer是linux里面的显示设备。在驱动底下如何操作lcd实现图形的显示。
1、什么是framebuffer?
(1)首先想一下在裸机中我们是怎么操作LCD的。
Soc内部有lcd的控制器,Soc外面有lcd的驱动器,lcd的驱动器连接着lcd的屏幕,Soc的内部还有CPU,外部还有DDR内存。这些设备都参与到了lcd的显示中。
在裸机中我们是怎么搞的呢,lcd的驱动器和lcd的控制器之间通过排线进行链接,连接的接口就是lcd所特有的一个接口。lcd控制器里面是很多和lcd相关的寄存器。开始cpu设置lcd控制器内部的一些寄存器,达到在DDR内存空间中分出一块内存来作为显存,这个显存和lcd之间建立起一个双向映射关系,以后CPU只要将要显示的内容丢到显存中去就行,硬件会自动的从显存中读取内容刷新到lcd驱动器中,然后lcd驱动器操作lcd屏幕将内容刷新到lcd屏幕上显示。所以在编程的时候,我们根本不用去纠结与lcd驱动器和lcd屏幕的部分,我们编程只需要管lcd控制器内部的寄存器,以及将要显示的内容丢到显存中去。这是我们在裸机中的操作过程。
(2)在操作系统下操作lcd有什么难点?
在裸机中我们操作lcd的代码是在一个代码中的,可以比喻中成一个进程,但是在操作系统下,操作lcd的代码就不是在一个代码中了,因为分为了驱动和应用两部分。内核驱动做的部分是和底层硬件相关的一些部分,应用做的是让lcd屏幕显示具体内容的那一部分,所以在操作系统下,我们任务量就被分为了两个不分,不能像裸机中那样一个代码就可以初始化设置lcd的控制器和显存建立映射和将具体内容刷新到lcd屏幕上。在操作系统下,第一部分是要设置初始化一些lcd的控制器,让显存和lcd之间建立起映射的关系,这第一部分的工作是由内核驱动完成的,驱动只做这一部分。第二部分为应用完成的,将要具体显示的内容丢到显存中去从而硬件的lcd驱动器自动将显存中的内容刷新到驱动器中然后驱动器让lcd显示相关的内容,所以应用部分是来实现lcd屏幕上具体显示什么的。所以问题出来了,难点就是在于应用和驱动之间怎么配合的问题。
在裸机中内存我们可以随便用,自己想用哪里就用哪里,所以裸机中将lcd和哪一部分的显存建立起映射是简单的。但是在操作系统中,内存都是被严格管理的,想要使用内存你需要向内核申请。这也是有操作系统和无操作系统的区别和难点。
内核驱动部分设置的显存部分要对应映射到真正的物理地址中,应用层中将显示的内容丢到显存中,这部分的显存同样的也要映射到真正的物理地址中去,,所以驱动部分的显存空间和应用层的显存空间都需要映射到相同的物理地址中去,这样你无论在驱动中还是在应用层中去像显存中丢数据的时候才会都正确的显示到屏幕上去。
为了解决上面的问题,也是framebuffer诞生的关键原因。
(3)framebuffer帧缓冲(简称fb),一帧数据就是一屏数据。在串口中一帧数据表示的是一个数据段,在显示中一帧数据表示的是一屏幕的数据。fb从编程角度来讲是linux内核中虚拟出来的一个设备,也就是说fb在内核中的体现是内核用代码来实现虚拟出来的一个fb设备,这个设备在应用程序空间有一个设备文件节点,一般就是/dev/fb0。为什么说fb是一个设备呢,因为我们可以向操作一个设备一样去open等操作这个文件,但是我们要知道在现实世界中并不存在一个真实的fb设备。他是linux内核设计的时候抽象出来的,他之所以被抽象出来是为了来代替lcd显示器这个硬件设备和lcd显示器所需要的软件设施(用来做显示的软件设施,如显卡),这种东西叫做fb(frambuffer)
(4)什么是framebuffer?
综上所述,fb是linux内核把系统里面所有跟显示有关的硬件和软件等等这些部分集合起来,总体的虚拟出来的一个设备fb,这个设备有一个特性,就是可以为应用层提供统一的接口,这个接口是标准的,所以应用层来让屏幕显示一些东西的时候,不需要去具体的硬件特性,比如说你的硬件lcd是多少位的,分辨率是多少等等这些特性都是应用层不需要去关心的,应用层只需要操作统一的接口,而不需要去关心这些特性就可以完成要做的显示任务了。所以fb是一个好东西,因为他向应用层屏蔽了驱动层的一些细节。这就是framebuffer。
我们学的是framebuffer中的驱动程序部分。像VGA接口和HDMI接口的东西也都能和framebuffer扯上关系。在驱动层配置一些framebuffer的内容也都和这些VGS接口和HDMI接口的设备实现对接。所以我们分析的fb驱动代码可以在各个和fb有关的设备中都可以用。像支持HDMI等。
2、framebuffer的使用
framebuffer向应用层提供了一个统一的接口,所以在应用层中使用fb很简单,如果你不管fb的内部实现原理而是单纯的使用fb,那么是非常简单的。所以在应用层中去使用fb是非常简单的。
(1)应用层使用frambuffer,首先要打开所对应的设备文件,一般是/dev/fb0,fb后面0是序号,有几个显示器那么系统中就要几个序号。这个fb0设备文件是fb驱动对上层应用层开放出来的一个设备文件。从驱动层来看其实fb设备也是一个典型的字符设备(在驱动层),而且创建了一个类,这个类是/sys/class/graphics
,graphics是图形图像的意思,因为fb自动创建了一个类在系统中,所以会自动创建一个设备文件节点(/dev/fb0)。我们应用层使用的时候第一部分就是去打开这个设备文件。
(2)第二步是要获取设备信息(#include <linux/fb.h>),应用层读写的时候并不知道lcd屏幕的分辨率是多大的。不同的lcd设备他的分辨率是不一样的,所以当你在应用层要去操作一个lcd设备的时候,你必须要先知道这个lcd设备的信息。应用程序自己是无法知道自己程序将来是要运行在哪个lcd上的,应用程序是无法知道lcd设备信息的,但是驱动知道lcd设备硬件的具体信息。因为应用层不知道lcd硬件的具体参数,所以驱动层开放了一些接口给应用来让应用知道,应用可以通过调用这些接口从驱动中得知具体的lcd硬件设备的信息,所以当应用第一步打开了fb设备,第二步就是获取lcd设备的相关信息。获取了设备信息之后在决定再买操作这个lcd设备。
(3)第三步,mmap映射。因为应用层的程序是自己工作在自己的虚拟地址空间中的,我们驱动程序是工作在内核的地址空间的。驱动已经在内核中申请了一段显存了,驱动申请的这段显存空间也是一段虚拟地址,这个虚拟地址对应了一块物理内存,所以有一段物理内存地址是被显存占用的。那么怎么让应用程序将自己的虚拟地址空间和这段物理显存地址的内存空间进行绑定起来呢,就是调用这个mmap函数来进行映射,来让真实的显存的这段物理内存映射到应用程序当前进程的虚拟地址空间中,此时当前应用读写这段虚拟地址空间实际映射到的就是真正显存所对应的物理地址,同时显存对应的物理地址又在驱动中设置好了使其绑定到了lcd的屏幕中。所以当应用操作这段mmap映射的虚拟地址空间时,相应的屏幕上就会发生变化。
如果两个应用程序都调用了mmap将应用当前所在进程的虚拟地址空间和显存对应的物理地址空间建立起了映射,那么a应用和b应用都像虚拟地址空间写内容的时候,因为两个应用的虚拟地址空间都映射到了同一个物理地址中去,所以在屏幕上会出现两个应用写的内容叠加的情况,后写的会覆盖前面的东西。就跟我们在电脑中打开两个软件界面一样,后打开的软件的界面会将前面打开的软件的界面进行覆盖。所以是可以多个进程同时操作同一个lcd对应的物理地址空间的。因为虚拟地址空间映射到显存的物理地址空间中是可以多对一进行映射的,可以多个应用层的虚拟地址空间同时映射到同一段物理地址中去。mmap是一个很重要的函数。
(4)当应用层使用mmap后,那么在当前应用层的虚拟地址空间就会和实际显存物理地址建立映射,也就是有了一段fb的虚拟地址空间,我操作fb的这段虚拟地址空间实际上操作的就是显存的物理地址空间实际上操作的就是lcd屏幕。所以第四步就是将要显示的内容填充到fb中,就是mmap出来的虚拟地址空间中,就会在屏幕上进行显示。
本文出自 “whylinux” 博客,谢绝转载!
linux驱动开发之framebuffer驱动介绍