`
isiqi
  • 浏览: 16028570 次
  • 性别: Icon_minigender_1
  • 来自: 济南
社区版块
存档分类
最新评论

Linux lcd 帧缓冲驱动详解

阅读更多

Linux lcd 帧缓冲驱动详解

1Linux的帧缓冲设备(Framebuffer)简介

帧缓冲(framebuffer)是Linux为显示设备提供的一个接口,把显存抽象后的一种设备,他允许上层应用程序在图形模式下直接对显示缓冲区进行读写操作。这种操作是抽象的,统一的。用户不必关心物理显存的位置、换页机制等等具体细节。这些都是由Framebuffer设备驱动来完成的。
帧缓冲驱动的应用广泛,在linux的桌面系统中,Xwindow服务器就是利用帧缓冲进行窗口的绘制。尤其是通过帧缓冲可显示汉字点阵,成为Linux汉化的唯一可行方案。
Linux FrameBuffer 本质上只是提供了对图形设备的硬件抽象,在开发者看来,FrameBuffer 是一块显示缓存,往显示缓存中写入特定格式的数据就意味着向屏幕输出内容。所以说FrameBuffer就是一块白板。例如对于初始化为16 位色的FrameBuffer 来说, FrameBuffer中的两个字节代表屏幕上一个点,从上到下,从左至右,屏幕位置与内存地址是顺序的线性关系。
帧缓存可以在系统存储器(内存)的任意位置,视频控制器通过访问帧缓存来刷新屏幕。 帧缓存也叫刷新缓存 Frame buffer refresh buffer, 这里的帧(frame)是指整个屏幕范围。
帧缓存有个地址,是在内存里。我们通过不停的向frame buffer中写入数据, 显示控制器就自动的从frame buffer中取数据并显示出来。全部的图形都共享内存中同一个帧缓存。
CPU指定显示控制器工作,则显示控制器根据CPU的控制到指定的地方去取数据 指令, 目前的数据一般是从显存里取, 如果显存里存不下,则从内存里取, 内存也放不下,则从硬盘里取,当然也不是内存放不下,而是为了节省内存的话,可以放在硬盘里,然后通过 指令控制显示控制器去取。帧缓存 Frame Buffer,里面存储的东西是一帧一帧的, 显卡会不停的刷新Frame Buffer, 这每一帧如果不捕获的话, 则会被丢弃,也就是说是实时的。这每一帧不管是保存在内存还是显存里, 都是一个显性的信息,这每一帧假设是800x600的分辨率, 则保存的是800x600个像素点,和颜色值。
显示器可以显示无限种颜色,目前普通电脑的显卡可以显示32位真彩、24位真彩、16位增强色、256色。除256色外,大家可以根据自己的需要在显卡的允许范围之内随意选择。很多用户有一种错误概念,认为256色是最高级的选项,而实际上正好相反。256色是最低级的选项,它已不能满足彩色图像的显示需要。16位不是16种颜色,而是216次平方(256×256)种颜色,但256色就是256(28次平方)种颜色。所以16位色要比256色丰富得多。

帧缓冲设备对应的设备文件为/dev/fb*,如果系统有多个显示卡,Linux下还可支持多个帧缓冲设备,最多可达32 个,分别为/dev/fb0/dev/fb31,而/dev/fb则为当前缺省的帧缓冲设备,通常指向/dev/fb0。当然在嵌入式系统中支持一个显示设备就够了。帧缓冲设备为标准字符设备,主设备号为29,次设备号则从031。分别对应/dev/fb0-/dev/fb31。通过/dev/fb,应用程序的操作主要有这几种:

1 /写(read/write/dev/fb:相当于读/写屏幕缓冲区。例如用 cp /dev/fb0 tmp命令可将当前屏幕的内容拷贝到一个文件中,而命令cp tmp > /dev/fb0 则将图形文件tmp显示在屏幕上。

2.映射(map)操作:由于Linux工作在保护模式,每个应用程序都有自己的虚拟地址空间,在应用程序中是不能直接访问物理缓冲区地址的。为此, Linux在文件操作 file_operations结构中提供了mmap函数,可将文件的内容映射到用户空间。对于帧缓冲设备,则可通过映射操作,可将屏幕缓冲区的物理地址映射到用户空间的一段虚拟地址中,之后用户就可以通过读写这段虚拟地址访问屏幕缓冲区,在屏幕上绘图了。实际上,使用帧缓冲设备的应用程序都是通过映射操作来显示图形的。由于映射操作都是由内核来完成,下面我们将看到,帧缓冲驱动留给开发人员的工作并不多。

3 I/O控制:对于帧缓冲设备,对设备文件的ioctl操作可读取/设置显示设备及屏幕的参数,如分辨率,显示颜色数,屏幕大小等等。ioctl的操作是由底层的驱动程序来完成的。

在应用程序中,操作/dev/fb的一般步骤如下:

1 打开/dev/fb设备文件。
2 ioctrl操作取得当前显示屏幕的参数,如屏幕分辨率,每个像素点的比特数。根据屏幕参数可计算屏幕缓冲区的大小。
3 将屏幕缓冲区映射到用户空间。
4 映射后就可以直接读写屏幕缓冲区,进行绘图和图片显示了。

典型程序段如下:

#include <linux/fb.h>
int main()
{
int fbfd = 0;
struct fb_var_screeninfo vinfo;
struct fb_fix_screeninfo finfo;
long int screensize = 0;
/*打开设备文件*/
fbfd = open("/dev/fb0", O_RDWR);
/*取得屏幕相关参数*/
ioctl(fbfd, FBIOGET_FSCREENINFO, &finfo);
ioctl(fbfd, FBIOGET_VSCREENINFO, &vinfo);
/*计算屏幕缓冲区大小*/
screensize = vinfo.xres * vinfo.yres * vinfo.bits_per_pixel / 8;
/*映射屏幕缓冲区到用户地址空间*/
fbp=(char*)mmap(0,screensize,PROT_READ|PROT_WRITE,MAP_SHARED, fbfd, 0);
/*下面可通过fbp指针读写缓冲区*/

(2)帧缓冲file_operations

帧缓冲设备也属于字符设备,采用文件层-驱动层的接口方式。在文件层为之定义了以下数据结构。

Static struct file_operations fb_fops={

ower: THIS_MODULE,

read: fb_read, /*读操作*/

write: fb_write, /*写操作*/

ioct1: fb_ioct1, /*I/O操作*/

mmap: fb_mmap, /*映射操作*/

open: fb_open, /*打开操作*/

release: fb_release, /*关闭操作*/

}

其成员函数都在linux/driver/video/fbmem.c中定义,其中的函数对具体的硬件进行操作,对寄存器进行设置,对显示缓冲进行映射。主要结构体还有以下几个。

● Struct fb_fix_screeninfo:记录了帧缓冲设备和指定显示模式的不可修改信息。它包含了屏幕缓冲区的物理地址和长度。

● Struct fb_var_screeninfo:记录了帧缓冲设备和指定显示模式的可修改信息。它包括显示屏幕的分辨率、每个像素的比特数和一些时序变量。其中变量 xres定义了屏幕一行所占的像素数,yres定义了屏幕一列所占的像素数,bits_per_pixel定义了每个像素用多少个位来表示。

● Struct fb_infoLinux为帧缓冲设备定义的驱动层接口。它不仅包含了底层函数,而且还有记录设备状态的数据。每个帧缓冲设备都与一个fb_info结构相对应。其中成员变量modename为设备名称,fontname为显示字体,fbops为指向底层操作的函数的指针。

LCD驱动开发的主要工作

1 编写初始化函数

初始化函数首先初始化LCD 控制器,通过写寄存器设置显示模式和颜色数,然后分配LCD显示缓冲区。Linux中可以用kmalloc()函数分配一段连续的空间。缓冲区大小为:点阵行数×点阵列数×用于表示一个像素的比特数/8。缓冲区通常分配在大容量的片外SDRAM中,起始地址保存在LCD控制寄存器中。本文采用的LCD显示方式为640×48016位彩色,则需要分配的显示缓冲区为640×480×2=600kb最后是初始化一个fb_info结构,填充其中的成员变量,并调用register_framebuffer(&fb_info),将fb_info登记入内核

2 编写成员函数

编写结构fb_info中函数指针fb_ops对应的成员函数,对于嵌入式系统的简单实现,只需要下列三个函数就可以了。

struct fb_ops{

……

int (*fb_get_fix)(struct fb_fix_screeninfo *fix, int con, struct fb_info *info);

int (*fb_get_var)(struct fb_var_screeninfo *var, int con, struct fb_info *info);

int (*fb_set_var)(struct fb_var_screeninfo *var, int con, struct fb_info *info);

……}

Struct fb_opsinclude/linux/fb.h中定义。这些函数都是用来设置/获取fb_info结构中的成员变量的。当应用程序对设备文件进行 ioctl操作时候会调用它们。对于fb_get_fix(),应用程序传入的是fb_fix_screeninfo结构,在函数中对其成员变量赋值,主要是smem_start(缓冲区起始地址)和smem_len(缓冲区长度),最终返回给应用程序。而fb_set_var()函数的传入参数是 fb_var_screeninfo,函数中需要对xresyresbits_per_pixel赋值。

对于/dev/fb,对显示设备的操作主要有以下几种。

/写(read/write/dev/fb:相当于读/写屏幕缓冲区。

映射(map)操作:由于Linux工作在保护模式,每个应用程序都有自己的虚拟地址空间,在应用程序中是不能直接访问物理缓冲区地址的。为此,Linux在文件操作 file_operations结构中提供了mmap函数,可将文件的内容映射到用户空间。对于帧缓冲设备,则可通过映射操作,可将屏幕缓冲区的物理地址映射到用户空间的一段虚拟地址中,之后用户就可以通过读写这段虚拟地址访问屏幕缓冲区,在屏幕上绘图了。

● I/O控制:对于帧缓冲设备,对设备文件的ioctl操作可读取/设置显示设备及屏幕的参数,如分辨率、显示颜色数和屏幕大小等。ioctl的操作是由底层的驱动程序来完成的。在应用程序中,操作/dev/fb的一般步骤如下:打开/dev/fb设备文件;用ioctrl操作取得当前显示屏幕的参数,如屏幕分辨率和每个像素的比特数,根据屏幕参数可计算屏幕缓冲区的大小;将屏幕缓冲区映射到用户空间;映射后即可直接读写屏幕缓冲区,进行绘图和图片显示了。

LCD模块化驱动

在对S3C2410 LCD编写模块化驱动程序时,首先要从内核中去除LCD驱动。这里需要做一些改动,系统调用被加在以下文件中,需去除: /root/usr/src/arm/linux/kernel/sys.c/root/usr/src/arm/linux/include/arm -arm下的unistd.hlcd.h/root/usr/src/arm/linux/arch/arm/kernel下的calls.s

编写模块化驱动程序,有以下几个关键的函数。

● lcd_kernel_init(void)//当模块被载入时执行

● lcd_kernel_exit(void)//当模块被移出内核空间时被执行

● lcd_kernel1_ioctl(struct*inode, struct*file, unsigned int cmd, unsigned longarg) //其他功能

每当装配设备驱动程序时,系统自动调用初始化模块lcd_kernel_init(void)

另一个必须提供的函数是lcd_kernel_exit(void),它在模块被卸载时调用,负责进行设备驱动程序的工作。

执行insmod lcd.o命令即可将LCD驱动添加到内核中,执行rmmod lcd命令即可从内核中删除LCD驱动。

静态加载LCD驱动

将写好的lcd 驱动程序lcd.c放到arm/linux/drivers/char目录下,修改arm/linux/drivers/char/config.in文件,加上一行:Bool'LCD driver support'CONFIG_LCD;修改arm/linux/drivers/char/Makefile文件,加上一行:obj-$ (CONFIG_LCD)+=lcd.o

这样,当再进行make xconfig时,就会选择是否将LCD驱动编译进内核。同样的办法也可用在其他设备上。

3Framebuffer驱动程序模型

Framebuffer驱动程序模型
下图会向你展示目前的framebuffer设备驱动的结构,最常用的是非标准驱动。很明显他所处的层次最高,程序编写是最容易的。理解了这个图的,你已经很轻松的去完成一个fb驱动,比如给sa1100,s2410,s2440系列的ARMLCD控制器写驱动。

Color Map 剖析
framebuffer驱动程序设计中,cmap这个东东太晕了。现在我要把他赤裸裸的剖析给大家:)
1
struct fb_cmap


/*颜色映射表*/
struct fb_cmap {
__u32 start; /* First entry */
__u32 len; /* Number of entries */
__u16 *red; /* 红色 */
__u16 *green; /*绿色*/
__u16 *blue; /*蓝色*/
__u16 *transp; /* 透明度,允许 NULL */
};


该结构在fb.h文件中定义,在struct fb_ops结构中有两个成员函数与其相关:

/*获取颜色表*/
int (*fb_get_cmap)(struct fb_cmap *cmap, int kspc, int con, struct fb_info *info);
/*设定颜色表*/
int (*fb_set_cmap)(struct fb_cmap *cmap, int kspc, int con, struct fb_info *info);



struct fb_info结构中有变量:

struct fb_cmap cmap; /* Current cmap */


fpgen基础操作下提供:

extern int

fbgen_get_cmap(struct fb_cmap *cmap, int kspc, int con, struct fb_info *info);
extern int

fbgen_set_cmap(struct fb_cmap *cmap, int kspc, int con, struct fb_info *info);


在文件/* drivers/video/fbcmap.c */中提供更多的cmap应用

extern int fb_alloc_cmap(struct fb_cmap *cmap, int len, int transp);
extern void fb_copy_cmap(struct fb_cmap *from, struct fb_cmap *to, int fsfromto);
extern int fb_get_cmap(struct fb_cmap *cmap, int kspc,
int (*getcolreg)(u_int, u_int *, u_int *, u_int *,u_int *, struct fb_info *),

extern int fb_set_cmap(struct fb_cmap *cmap, int kspc, struct fb_info *fb_info);

int (*setcolreg)(u_int, u_int, u_int, u_int, u_int,struct fb_info *),
struct fb_info *fb_info);
extern struct fb_cmap *fb_default_cmap(int len);
extern void fb_invert_cmaps(void);


2 通过文件解析
anakinfb.c文件中,cmap如图


stifb.c

本文介绍的设备是位于/video目录下面的anakinfb.c驱动程序。虽然我不清楚那个设备的特性,但是从对程序的分析中我们仍然知道如何编写一个frame buffer设备驱动。

本文是个标准的fb驱动。共221行,包含函数如下:

1. static int anakinfb_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue, u_int *transp, struct fb_info *info) 31

2. static int anakinfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,u_int transp, struct fb_info *info) 45

3. static int anakinfb_get_fix(struct fb_fix_screeninfo *fix, int con, struct fb_info *info) 57

4. static int anakinfb_get_var(struct fb_var_screeninfo *var, int con, struct fb_info *info) 75

5. static int anakinfb_set_var(struct fb_var_screeninfo *var, int con, struct fb_info *info) 111

6. static int anakinfb_get_cmap(struct fb_cmap *cmap, int kspc, int con, struct fb_info *info) 117

7. static int anakinfb_set_cmap(struct fb_cmap *cmap, int kspc, int con, struct fb_info *info) 130

8. static int anakinfb_switch_con(int con, struct fb_info *info) 147

9. static int anakinfb_updatevar(int con, struct fb_info *info) 155

10. static void anakinfb_blank(int blank, struct fb_info *info) 161

11. int __init anakinfb_init(void) 178

函数12是寄存器操作用。函数34567fb_ops函数。函数8用于切换控制台。函数9用于更新变量。函数10用于闪烁屏幕。函数11用于初始化设备。
很奇怪,对fb设备的读写函数怎么没有!值得说明的是open,release,read,write,ioctl,mmap等函数的实现是由 fbmem.c文件实现了。也就是说所有的fb设备在给定了fb_info后,所有的操作都是一样的。在明确的fb_info前提下,fbmem.c中的函数可以工作的很好。这样大家应该感到非常轻松了吧,只要完成上述的几个设备相关的函数,frame buffer设备的驱动就写完了:)
系统的结构如图:

Stifb驱动模型
linux/drivers/video/stifb.c - Generic frame buffer driver for HP * workstations with STI (standard text interface) video firmware.
这个驱动程序和前面的anakin设备完全不同,因为他不是采用标准的格式,而是根据 based on skeletonfb, which wasCreated 28 Dec 1997 by Geert Uytterhoeven也就是skeletonfb.c提供的框架完成的。

230行,包含函数如下:

static int sti_encode_fix(struct fb_fix_screeninfo *fix, const void *par, struct fb_info_gen *info) 60
static int sti_decode_var(const struct fb_var_screeninfo *var,void *par, struct fb_info_gen *info) 71
static int sti_encode_var(struct fb_var_screeninfo *var, const void *par, struct fb_info_gen *info) 78
static void sti_get_par(void *par, struct fb_info_gen *info) 94
static void sti_set_par(const void *par, struct fb_info_gen *info) 99
static int sti_getcolreg(unsigned regno, unsigned *red, unsigned *green, unsigned *blue, unsigned *transp, struct fb_info *info) 104
static int sti_setcolreg(unsigned regno, unsigned red, unsigned green, unsigned blue, unsigned transp, struct fb_info *info) 111
static void sti_set_disp(const void *par, struct display *disp, struct fb_info_gen *info) 118
static void sti_detect(void) 127
static int sti_blank(

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics