LCD 硬件原理
利用液晶制成的显示器称为 LCD,依据驱动方式可分为静态驱动、简单矩阵驱动以及主动矩阵驱动 3 种。其中,简单矩阵型又可再细分扭转向列型(TN)和超扭
转式向列型(STN)两种,而主动矩阵型则以薄膜式晶体管型(TFT)为主流。表18.1 列出了 TN、STN 和 TFT 显示器的区别。
TN 型液晶显示技术是 LCD 中最基本的,其他种类的 LCD 都以 TN 型为基础改进而得。TN 型 LCD 显示质量很差,色彩单一,对比度低,反映速度很慢,故主要用于简单的数字符与文字的显示,如电子表及电子计算器等。
STN LCD 的显示原理与 TN 类似,区别在于 TN 型的液晶分子将入射光旋转 90°,而 STN 则可将入射光旋转 180°~270°。STN 改善了 TN 视角狭小的缺点,并提高了对比度,显示品质较 TN 高。 STN 搭配彩色滤光片,将单色显示矩阵的任一像素分成 3 个子像素,分别透过彩色滤光片显示红、绿、蓝三原色,再经由三原色按比例调和,显示出逼近全彩模式的色彩。STN 显示的画面色彩对比度仍较小,反应速度也较慢,可以作为一般的操作显示接口。 随后出现的 DSTN 通过双扫描方式来显示,显示效果相对 STN 而言有了较大幅度的提高。DSTN 的反应速度可达到 100ms,但是在电场反复改变电压的过程中,每一像素的恢复过程较慢。因此,当在屏幕画面快速变化时,会产生“拖尾”现象。
TN 与 STN 型液晶显示器都是使用场电压驱动方式,如果显示尺寸加大,中心部位对电极变化的反应时间就会拉长,显示器的速度跟不上。为了解决这个问题,主动式矩阵驱动被提出,主动式 TFT 型的液晶显示器的结构较为复杂,它包括背光管、导光板、偏光板、滤光板、玻璃基板、配向膜、液晶材料和薄膜式晶体管等。
在 TFT 型 LCD 中,晶体管矩阵依显示信号开启或关闭液晶分子的电压,使液晶分子轴转向而成“亮”或“暗”的对比,避免了显示器对电场效应的依靠。因此,TFT LCD 的显示质量较 TN/STN 更佳,画面显示对比度可达 150:1以上,反应速度逼近 30ms甚至更快,适用于 PDA、笔记本电脑、数码相机、MP4 等。
一块 LCD 屏显示图像不但需要 LCD 驱动器,还需要有相应的 LCD 控制器。通常 LCD 驱动器会以 COF/COG 的形式与 LCD 玻璃基板制作在一起,而 LCD 控制器则由外部电路来实现。许多 MCU 内部直接集成了 LCD 控制器,通过 LCD控制器可以方便地控制 STN 和 TFT 屏。
TFT 屏是目前嵌入式系统应用的主流,图 18.1 给出了 TFT 屏的典型时序。时序图中的 VCLK、HSYNC 和 VSYNC 分别为像素时钟信号(用于锁存图像数据的像素
时钟)、行同步信号和帧同步信号,VDEN 为数据有效标志信号,VD 为图像的数据信号。
作为帧同步信号的 VSYNC,每发出一个脉冲,都意味着新的一屏图像数据开始发送。而作为行同步信号的 HSYNC,每发出一个脉冲都表明新的一行图像资料开始发送。在帧同步以及行同步的头尾都必须留有回扫时间。这样的时序安排起源于 CRT显示器电子枪偏转所需要的时间,但后来成为实际上的工业标准,因此 TFT 屏也包含了回扫时间。
下图给出了 LCD 控制器中应该设置的 TFT 屏的参数,其中的上边界和下边界即为帧切换的回扫时间,左边界和右边界即为行切换的回扫时间,水平同步和垂直同步分别是行和帧同步本身需要的时间。xres 和 yres 则分别是屏幕的水平和垂直分辨率,常见的嵌入式设备的 LCD 分辨率主要为 320*240、640*480 等。
帧缓冲
帧缓冲的概念
帧缓冲(framebuffer)是 Linux 系统为显示设备提供的一个接口,它将显示缓冲区抽象,屏蔽图像硬件的底层差异,允许上层应用程序在图形模式下直接对显示缓冲区进行读写操作。用户不必关心物理显示缓冲区的具体位置及存放方式,这些都由帧缓冲设备驱动本身来完成。对于帧缓冲设备而言,只要在显示缓冲区中与显示点对应的区域写入颜色值,对应的颜色会自动在屏幕上显示。
帧缓冲设备为标准字符设备,主设备号为 29,对应于/dev/fb%d 设备文件。帧缓冲驱动的应用非常广泛,在 Linux 的桌面系统中,Xwindow 服务器就是利用帧缓冲进行窗口的绘制。嵌入式系统中的 Qt/Embedded 等图形用户界面环境也基于帧缓冲而设计。另外,通过帧缓冲可支持汉字点阵的显示,因此帧缓冲也成为 Linux 汉化的可行方案。
显示缓冲区与显示点
在帧缓冲设备中,对屏幕显示点的操作通过读写显示缓冲区来完成,在不同的色彩模式下,显示缓冲区和屏幕上的显示点有不同的对应关系,表 18.2~表 18.4 分别给出了 16 级灰度、8 位色和 16 位情况下显示缓冲区与显示点的对应关系。
Linux 帧缓冲相关数据结构与函数
1.fb_info 结构体
帧缓冲设备最关键的一个数据结构体是 fb_info 结构体(为了便于记忆,我们把它简称为“FBI”),FBI 中包括了关于帧缓冲设备属性和操作的完整描述,这个结构体的定义如代码清单所示。
1 struct fb_info
2 {
3 int node;
4 int flags;
5 struct fb_var_screeninfo var; /*可变参数 */
6 struct fb_fix_screeninfo fix; /*固定参数 */
7 struct fb_monspecs monspecs; /*显示器标准 */
8 struct work_struct queue; /* 帧缓冲事件队列 */
9 struct fb_pixmap pixmap; /* 图像硬件 mapper */
10 struct fb_pixmap sprite; /* 光标硬件 mapper */
11 struct fb_cmap cmap; /* 目前的颜色表*/
12 struct list_head modelist;
13 struct fb_videomode *mode; /* 目前的 video 模式 */
14
15 #ifdef CONFIG_FB_BACKLIGHT
16 struct mutex bl_mutex;
17 /* 对应的背光设备 */
18 struct backlight_device *bl_dev;
19 /* 背光调整 */
20 u8 bl_curve[FB_BACKLIGHT_LEVELS];
21 #endif
22
23 struct fb_ops *fbops; /* fb_ops,帧缓冲操作 */
24 struct device *device;
25 struct class_device *class_device; /
26 int class_flag; /* 私有 sysfs 标志 */
27 #ifdef CONFIG_FB_TILEBLITTING
28 struct fb_tile_ops *tileops; /* 图块 Blitting */
29 #endif
30 char _ _iomem *screen_base; /* 虚拟基地址 */
31 unsigned long screen_size; /* ioremapped 的虚拟内存大小 */
32 void *pseudo_palette; /* 伪 16 色颜色表 */
33 #define FBINFO_STATE_RUNNING 0
34 #define FBINFO_STATE_SUSPENDED 1
35 u32 state; /* 硬件状态,如挂起 */
36 void *fbcon_par;
37 void *par;
38 };
FBI 中记录了帧缓冲设备的全部信息,包括设备的设置参数、状态以及操作函数指针。每一个帧缓冲设备都必须对应一个 FBI。
2.fb_ops 结构体
FBI 的成员变量 fbops 为指向底层操作的函数的指针,这些函数是需要驱动程序开发人员编写的,其定义如代码清单所示。
1 struct fb_ops
2 {
3 struct module *owner;
4 /* 打开/释放 */
5 int(*fb_open)(struct fb_info *info, int user);
6 int(*fb_release)(struct fb_info *info, int user);
7
8 /* 对于非线性布局的/常规内存映射无法工作的帧缓冲设备需要 */
9 ssize_t(*fb_read)(struct file *file, char _ _user *buf, size_t count,
10 loff_t*ppos);
11 ssize_t(*fb_write)(struct file *file, const char _ _user *buf, size_t count,
12 loff_t *ppos);
13
14 /* 检测可变参数,并调整到支持的值*/
15 int(*fb_check_var)(struct fb_var_screeninfo *var, struct fb_info *info);
16
17 /* 根据 info->var 设置 video 模式 */
18 int(*fb_set_par)(struct fb_info *info);
19
20 /* 设置 color 寄存器 */
21 int(*fb_setcolreg)(unsigned regno, unsigned red, unsigned green, unsigned
22 blue, unsigned transp, struct fb_info *info);
23
24 /* 批量设置 color 寄存器,设置颜色表 */
25 int(*fb_setcmap)(struct fb_cmap *cmap, struct fb_info *info);
26
27 /*显示空白 */
28 int(*fb_blank)(int blank, struct fb_info *info);
29
30 /* pan 显示 */
31 int(*fb_pan_display)(struct fb_var_screeninfo *var, struct fb_info *info);
32
33 /* 矩形填充 */
34 void(*fb_fillrect)(struct fb_info *info, const struct fb_fillrect *rect);
35 /* 数据复制 */
36 void(*fb_copyarea)(struct fb_info *info, const struct fb_copyarea *region);
37 /* 图形填充 */
38 void(*fb_imageblit)(struct fb_info *info, const struct fb_image *image);
39
40 /* 绘制光标 */
41 int(*fb_cursor)(struct fb_info *info, struct fb_cursor *cursor);
42
43 /* 旋转显示 */
44 void(*fb_rotate)(struct fb_info *info, int angle);
45
46 /* 等待 blit 空闲 (可选) */
47 int(*fb_sync)(struct fb_info *info);
48
49 /* fb 特定的 ioctl (可选) */
50 int(*fb_ioctl)(struct fb_info *info, unsigned int cmd, unsigned long arg);
51
52 /* 处理 32 位的 compat ioctl (可选) */
53 int(*fb_compat_ioctl)(struct fb_info *info, unsigned cmd, unsigned long arg);
54
55 /* fb 特定的 mmap */
56 int(*fb_mmap)(struct fb_info *info, struct vm_area_struct *vma);
57
58 /* 保存目前的硬件状态 */
59 void(*fb_save_state)(struct fb_info *info);
60
61 /* 恢复被保存的硬件状态 */
62 void(*fb_restore_state)(struct fb_info *info);
63 };
fb_ops 的 fb_check_var()成员函数用于检查可以修改的屏幕参数并调整到合适的值,而 fb_set_par()则使得用户设置的屏幕参数在硬件上有效。
FBI 的 fb_var_screeninfo 和 fb_fix_screeninfo 成员也是结构体,fb_var_screeninfo记录用户可修改的显示控制器参数,包括屏幕分辨率和每个像素点的比特数。fb_var_screeninfo 中的 xres 定义屏幕一行有多少个点,yres 定义屏幕一列有多少个点,bits_per_pixel 定义每个点用多少个字节表示。而 fb_fix_screeninfo 中记录用户不能修改的显示控制器的参数,如屏幕缓冲区的物理地址、长度。当对帧缓冲设备进行映射操作的时候,就是从 fb_fix_screeninfo 中取得缓冲区物理地址的。上述数据成员都需要在驱动程序中初始化和设置。
fb_var_screeninfo 和 fb_fix_screeninfo 结构体的定义如代码清单所示。
1 struct fb_var_screeninfo
2 {
3 /* 可见解析度 */
4 _ _u32 xres;
5 _ _u32 yres;
6 /* 虚拟解析度 */
7 _ _u32 xres_virtual;
8 _ _u32 yres_virtual;
9 /* 虚拟到可见之间的偏移 */
10 _ _u32 xoffset;
11 _ _u32 yoffset;
12
13 _ _u32 bits_per_pixel; /* 每像素位数,BPP */
14 _ _u32 grayscale; /非 0 时指灰度 */
15
16 /* fb 缓存的 R\G\B 位域 */
17 struct fb_bitfield red;
18 struct fb_bitfield green;
19 struct fb_bitfield blue;
20 struct fb_bitfield transp; /* 透明度 */
21
22 _ _u32 nonstd; /* != 0 非标准像素格式 */
23
24 _ _u32 activate;
25
26 _ _u32 height; /*高度 */
27 _ _u32 width; /*宽度 */
28
29 _ _u32 accel_flags; /* 看 fb_info.flags */
30
31 /* 定时: 除了 pixclock 本身外,其他的都以像素时钟为单位 */
32 _ _u32 pixclock; /* 像素时钟(皮秒) */
33 _ _u32 left_margin; /* 行切换:从同步到绘图之间的延迟 */
34 _ _u32 right_margin; /* 行切换:从绘图到同步之间的延迟 */
35 _ _u32 upper_margin; /* 帧切换:从同步到绘图之间的延迟 */
36 _ _u32 lower_margin; /* 帧切换:从绘图到同步之间的延迟 */
37 _ _u32 hsync_len; /* 水平同步的长度 */
38 _ _u32 vsync_len; /* 垂直同步的长度 */
39 _ _u32 sync;
40 _ _u32 vmode;
41 _ _u32 rotate; /* 顺时钟旋转的角度 */
42 _ _u32 reserved[5]; /* 保留 */
43 };
1 struct fb_fix_screeninfo
2 {
3 char id[16]; /* 字符串形式的标识符 */
4 unsigned long smem_start; /* fb 缓存的开始位置 */
5 _ _u32 smem_len; /* fb 缓存的长度 */
6 _ _u32 type; /* FB_TYPE_* */
7 _ _u32 type_aux; /* 分界 */
8 _ _u32 visual; /* FB_VISUAL_* */
9 _ _u16 xpanstep; /* 如果没有硬件 panning ,赋 0 */
10 _ _u16 ypanstep;
11 _ _u16 ywrapstep;/
12 _ _u32 line_length; /* 1 行的字节数 */
13 unsigned long mmio_start; /* 内存映射 I/O 的开始位置 */
14 _ _u32 mmio_len; /* 内存映射 I/O 的长度 */
15 _ _u32 accel;
16 _ _u16 reserved[3]; /* 保留*/
17 };
4.fb_bitfield 结构体
fb_var_screeninfo 代码清单第 17、18、19 行分别记录 R、G、B 的位域,fb_bitfield 结构体描述每一像素显示缓冲区的组织方式,包含位域偏移、位域长度和 MSB 指示,如代码清单所示。
1 struct fb_bitfield
2 {
3 _ _u32 offset; /* 位域偏移 */
4 _ _u32 length; /* 位域长度 */
5 _ _u32 msb_right; /* MSB */
6 };
5.fb_cmap 结构体
fb_cmap 结构体记录设备无关的颜色表信息,用户空间可以通过 ioctl()的FBIOGETCMAP 和 FBIOPUTCMAP 命令读取或设定颜色表。
1 struct fb_cmap
2 {
3 _ _u32 start; /* 第 1 个元素入口 */
4 _ _u32 len; /* 元素数量 */
5 /* R、G、B、透明度*/
6 _ _u16 *red;
7 _ _u16 *green;
8 _ _u16 *blue;
9 _ _u16 *transp;
10 };
下面代码清单所示为用户空间获取颜色表的例程,若 BPP 为 8 位,则颜色表长度为 256;若 BPP 为 4 位,则颜色表长度为 16;否则,颜色表长度为 0,这是因为,对于 BPP 大于等于 16 的情况,使用颜色表是不划算的。
1 // 读入颜色表
2 if ((vinfo.bits_per_pixel == 8) || (vinfo.bits_per_pixel == 4))
3 {
4 screencols = (vinfo.bits_per_pixel == 8) ? 256 : 16;//颜色表大小
5 int loopc;
6 startcmap = new fb_cmap;
7 startcmap->start = 0;
8 startcmap->len = screencols;
9 //分配颜色表的内存
10 startcmap->red = (unsigned short int*)malloc(sizeof(unsigned short int)
11 *screencols);
12 startcmap->green = (unsigned short int*)malloc(sizeof(unsigned short int)
13 *screencols);
14 startcmap->blue = (unsigned short int*)malloc(sizeof(unsigned short int)
15 *screencols);
16 startcmap->transp = (unsigned short int*)malloc(sizeof(unsigned short int)
17 *screencols);
18 //获取颜色表
19 ioctl(fd, FBIOGETCMAP, startcmap);
20 for (loopc = 0; loopc < screencols; loopc++)
21 {
22 screenclut[loopc] = qRgb(startcmap->red[loopc] >> 8, startcmap
23 ->green[loopc] >> 8, startcmap->blue[loopc] >> 8);
24 }
25 }
26 else
27 {
28 screencols = 0;
29 }
对于一个 256 色(BPP=8)的 800*600 分辨率的图像而言,若红、绿、蓝分别用一个字节描述,则需要 800*600*3=1440000Byte 的空间,而若使用颜色表,则只需要 800*600*1+256*3= 480768Byte 的空间。
6.文件操作结构体
作为一种字符设备,帧缓冲设备的文件操作结构体定义于/linux/drivers/vedio/fbmem.c 文件中,如代码清单所示。
1 static struct file_operations fb_fops =
2 {
3 .owner = THIS_MODULE,
4 .read = fb_read, //读函数
5 .write = fb_write, //写函数
6 .ioctl = fb_ioctl, //I/O 控制函数
7 #ifdef CONFIG_COMPAT
8 .compat_ioctl = fb_compat_ioctl,
9 #endif
10 .mmap = fb_mmap, //内存映射函数
11 .open = fb_open, //打开函数
12 .release = fb_release, //释放函数
13 #ifdef HAVE_ARCH_FB_UNMAPPED_AREA
14 .get_unmapped_area = get_fb_unmapped_area,
15 #endif
16 };
帧缓冲设备驱动的文件操作接口函数已经在 fbmem.c 中被统一实现,一般不需要由驱动工程师再编写。
7.注册与注销帧缓冲设备
Linux 内核提供了 register_framebuffer()和 unregister_framebuffer()函数分别注册和注销帧缓冲设备,这两个函数都接受 FBI 指针为参数,原型为:
int register_framebuffer(struct fb_info *fb_info);
int unregister_framebuffer(struct fb_info *fb_info);
对于 register_framebuffer()函数而言,如果注册的帧缓冲设备数超过了 FB_MAX(目前定义为 32),则函数返回-ENXIO,注册成功则返回 0。