一、RGB LCD经典显示器件介绍:
1、LCD屏幕的重要属性参数:
① 分辨率:也就是屏幕上的像素点的个数;
② 像素格式:即单个像素点RGB三种颜色的表达方式,包括RGB888、ARGB8888和RGB565等。
③ LCD屏幕硬件接口:
这里指的是RGB LCD排线接口,
如下图所示:
R[7:0]、G[7:0]和B[7:0]这24根是数据线,DE、VSYNC、HSYNC和PCLK这四根是控制信号线
。RGB LCD一般有两种驱动模式:
DE模式和HV模式,这两个模式的区别是DE模式需要用到DE信号线,而HV模式不需要用到DE信号线,
在DE模式下是可以不需要HSYNC信号线的(
DE与HSYNC功能相同
),即使不接HSYNC信号线LCD也可以正常工作。
///
2、LCD的时间参数:(
重要)
如果将LCD显示一帧图像的过程想象成绘画,那么在显示的过程中就是用一根“笔”在不同的像素点画上不同的颜色。
这根笔按照
从左至右、从上到下
的顺序扫描每个像素点,并且在像素画上对应的颜色,当画到最后一个像素点的时候一幅图像就绘制好了。
假设一款LCD屏幕的分辨率为1024*600,如下图所示:
HSYNC是水平同步信号,也叫做行同步信号,当产生此信号的话就表示开始显示新的一行数据;
VSYNC信号是垂直同步信号,也叫做帧同步信号,当产生此信号的话就表示开始显示新的一帧图像了。
当显示完一行以后会发出HSYNC信号,此时电子枪就会关闭,然后迅速的移动到屏幕的左边,当HSYNC信号结束以后就可以显示新的一行数据了,电子枪就会重新打开;
在HSYNC信号结束到电子枪重新打开之间会插入一段延时,这段延时就图 50.1.5中的HBP;当显示完一行以后就会关闭电子枪等待HSYNC信号产生,关闭电子枪到HSYNC信号产生之间会插入一段延时,这段延时就是图 50.1.5中的HFP信号。同理,
当显示完一帧图像以后电子枪也会关闭,然后等到VSYNC信号产生,
期间也会加入一段延时,这段延时就是图 50.1.5中的VFP;VSYNC信号产生,电子枪移动到左上角,当VSYNC信号结束以后电子枪重新打开,中间也会加入一段延时,这段延时就是图 50.1.5中的VBP。
///
3、RGB LCD屏幕的时序(
底层核心
):
① 行显示的时序:
HSYNC:行同步信号,当此信号有效的话就表示开始显示新的一行数据,查阅所使用的LCD数据手册可以知道此信号是低电平有效还是高电平有效,
假设此时是低电平有效。
HSPW:有些地方也叫做thp,是HSYNC信号宽度,
也就是HSYNC信号持续时间。HSYNC信号不是一个脉冲,而是需要持续一段时间才是有效的,
单位为CLK。
HBP:有些地方叫做thb,前面已经讲过了,术语
叫做行同步信号后肩,
单位是CLK。
HOZNAL:有些地方叫做thd,
显示一行数据所需的时间,假如屏幕分辨率为1024*600,那么HOZVAL就是1024,
单位为CLK。
HFP:有些地方叫做thf,前面已经讲过了,术语叫做行
同步信号前肩,
单位是CLK。
显示一行所需要的时间就是:HSPW + HBP + HOZVAL + HFP。单位:CLK。
② 帧显示的时序:
VSYNC:
帧同步信号,当此信号有效的话就表示开始显示新的一帧数据,查阅所使用的LCD数据手册可以知道此信号是低电平有效还是高电平有效,
假设此时是低电平有效。
VSPW:些地方也叫做tvp,是VSYNC信号宽度,也就是
VSYNC信号持续时间,
单位为1行的时间(
多少个HSYNC
)。
VBP:有些地方叫做tvb,前面已经讲过了,术语叫做
帧同步信号后肩,
单位为1行的时间。
LINE:有些地方叫做tvd,
显示一帧有效数据所需的时间,假如屏幕分辨率为1024*600,那么LINE就是600行的时间。
单位是1行的时间。
VFP:有些地方叫做tvf,前面已经讲过了,术语叫做
帧同步信号前肩,
单位为1行的时间。
显示一帧所需要的时间就是:VSPW+VBP+LINE+VFP个行时间。
显示完整一帧图像的
clk时间
是:T = (VSPW+VBP+LINE+VFP) * (HSPW + HBP + HOZVAL + HFP)。
///
|
//
二、Linux的FrameBuffer驱动框架:
1、驱动框架的介绍:
帧缓冲(framebuffer)是Linux为显示设备提供的一个接口,
把显存抽象后的一种设备,他允许上层应用程序在图形模式下直接对显示缓冲区进行读写操作。所以在Linux系统中,凡是显示设备都被称为FrameBuffer设备(帧缓冲设备),所以LCD自然而言就是FrameBuffer设备。
FrameBuffer设备对应的设备文件为/dev/fb*,
Linux下可支持多个FrameBuffer设备,最多可达32个,分别为/dev/fb0到/dev/fb31,如果没有指定系统所使用的显示设备,通常指向/dev/fb0,在嵌入式系统中支持一个显示设备就够了。在Linux系统中,FrameBuffer设备为标准字符设备,主设备号为29,次设备号则从0到31。分别对应/dev/fb0-/dev/fb31。
在Linux系统中,FrameBuffer设备也有对应的设备驱动框架,我们把它叫做FrameBuffer驱动框架,所以Linux下编写LCD驱动我们就可以使用FrameBuffer驱动框架。
FrameBuffer驱动架构其实也是基于字符设备驱动来开发的,其内核源码位置在
drivers/video/fbdev/core/fbmem.c中。
其原理大概如下:
① 内核源码基于字符设备驱动
在/dev目录下挂载fbxx设备,其
主设备号为29,此设备0~31;
② 最多允许同时存在32个显示设备
③ 在字符设备的file_operations中,
实现了open、release、read、write以及memey、ioctl等函数;
④ 这些函数其本质是调用了内核链表的一种
结构体对象struct fb_info;
⑤ 作为驱动开发者,实际上
我们就是要描述并挂载一个struct fb_info对象,并初始化配置一下外设;
2、LCD驱动开发的基本流程(
基于ZYNQ开发板
):
① 根据实际电路结构,在设备树上创建设备节点,比如:DMA、pclk、gpio、lcd外设驱动等;
② 在设备树上
按照内核的规定格式,描述lcd显示器件,包括lcd的
刷新频率、分辨率、像素格式等参数;
③ 在设备树上描述的
LCD设备,需要基于某个CPU总线来扩展;
④ 编写
基于platform_device的驱动程序,匹配设备树上的lcd设备节点;
⑤ 使用
函数接口
framebuffer_alloc动态创建一个struct fb_info对象,同时也为自定义结构体动态申请内存空间;
⑥ 先从设备节点上
获取lcd的pclk时钟(
devm_clk_get函数
),并调用
函数
clk_disable_unprepare先禁止时钟输出;
⑦ 从设备树上获取到lcd的属性参数描述信息(
timingxx节点),并
返回到结构体struct videomode;
⑧ 调用
函数
dma_alloc_wc获取lcd的dma对应的DDR物理缓存地址和虚拟地址;
⑨
配置struct fb_ops结构体,指定设备读写操作函数的具体实现,其中,
有些功能函数内核已经提供(
drivers/video/fbdev/core/cfbfillrect.c)可以直接引用;
⑩ 开始根据LCD的属性,
配置struct fb_info,
配置内容包括:显存缓存地址、LCD的fb_fix_screeninfo固定参数、LCD的fb_var_screeninfo动态参数;
11 调用函数fb_alloc_cmap来为lcd分配载流数据,
目前无法确定载流输出是啥?
12 为显示设备的16色彩调色板动态分配内存空间;
13 设置LCD的pclk时钟KHz数值,并启动;
14
根据实际电路需求,配置vtc外设和dma外设,DMA外设配置有内核提供的架构可以使用;
15
调用函数
register_framebuffer,向内核注册struct fb_info对象;
16、有些驱动到这里,需要启动背光灯,当然也可以独立驱动;
3、涉及到的结构体与函数API:
相关结构体:
①
struct fb_info
//framebuffer核心描述结构体
struct fb_info {
465
atomic_t count;
/* 原子变量 */
466 int node;
467 int flags;
468 struct mutex lock;
/*
互斥锁,用于
open/release/ioctl
函数
*/
469 struct mutex mm_lock; /*
互斥锁,用于
fb_mmap
和
smem_*
域 */
470
struct fb_var_screeninfo var;
/* 可变参数信息 */
471
struct fb_fix_screeninfo fix;
/*固定参数信息 */
472 struct fb_monspecs monspecs;
473 struct work_struct queue;
/*
帧缓冲事件队列
*/
474 struct fb_pixmap pixmap;
/* 图像硬件映射,与图像显示有关 */
475 struct fb_pixmap sprite;
/* 光标硬件映射,与光标显示有关 */
476
struct fb_cmap cmap;
/* 调色板 */
477 struct list_head modelist;
/* 模式列表*/
478
struct fb_videomode *mode;
/*当前模式,指定LCD的属性参数值*/
479
480 #ifdef CONFIG_FB_BACKLIGHT
481 /* assigned backlight device */
482 /* set before framebuffer registration,
483 remove after unregister */
484 struct backlight_device *bl_dev;
485
486 /* Backlight level curve */
487 struct mutex bl_curve_mutex;
488 u8 bl_curve[FB_BACKLIGHT_LEVELS];
489 #endif
490 #ifdef CONFIG_FB_DEFERRED_IO
491 struct delayed_work deferred_work;
492 struct fb_deferred_io *fbdefio;
493 #endif
494
495 s
truct fb_ops *fbops; /*
提供显存的具体操作实现函数
*/
496 struct device *device;
/* 此设备的父设备*/
497 struct device *dev; /*
当前设备的设备描述节点
*/
498 int class_flag; /*
私有文件系统参数*/
499 #ifdef CONFIG_FB_TILEBLITTING
500 struct fb_tile_ops *tileops; /* Tile Blitting */
501 #endif
502 union {
503 char __iomem *screen_base;
/* 显存的虚拟映射地址*/
504 char *screen_buffer;
505 };
506 unsigned long screen_size;
/* 虚拟显存的字节大小 */
507 void *pseudo_palette;
/*16种颜色的虚拟调色板 */
508 #define FBINFO_STATE_RUNNING 0
509 #define FBINFO_STATE_SUSPENDED 1
510 u32 state; /* Hardware state i.e suspend */
511 void *fbcon_par; /* fbcon use-only private area */
512
/* 这里可以用与存放用户自定义的数据内存地址 */
513 void *par;
514 /* we need the PCI or similar aperture base/size not
515 smem_start/size as smem_start may just be an object
516 allocated inside the aperture so may not actually overlap */
517 struct apertures_struct {
518 unsigned int count;
519 struct aperture {
520 resource_size_t base;
521 resource_size_t size;
522 } ranges[0];
523 } *apertures;
524
525 bool skip_vt_switch; /* no VT switch on suspend/resume required */
526 };
②
struct videomode
//
独立的
显示设备属性描述结构体,
可以从设备树节点display_timming上自动生成
19 struct videomode {
20
unsigned long pixelclock;
/* 画面额定刷新频率Hz */
21
22
u32 hactive;
/*LCD横轴分辨率*/
23 u32 hfront_porch;
/*HFP的长度*/
24 u32 hback_porch;
/* HBP的长度 */
25 u32 hsync_len;
/* HSYNC的长度 */
26
27
u32 vactive;
/* LCD的竖轴分辨率 */
28 u32 vfront_porch;
/* VFP的HSYNC个数 */
29 u32 vback_porch;
/* VBP的HSYNC个数 */
30 u32 vsync_len;
/* VSYNC的HSYNC个数 */
31
32 enum display_flags flags; /* display flags */
};
③
struct fb_videomode
//向fb_info传递LCD的基本属性数据
777 struct fb_videomode {
778 const char *name; /* optional */
779 u32 refresh; /* optional */
780 u32 xres;
/* LCD显示的横轴分辨率 */
781 u32 yres;
/* LCD显示的竖轴分辨率 */
782 u32 pixclock;
/* 显示器的时钟频率 */
783 u32 left_margin;
784 u32 right_margin;
785 u32 upper_margin;
786 u32 lower_margin;
787 u32 hsync_len;
/* hsync的时钟长度 */
788 u32 vsync_len;
/* vsync的hsync个数 */
789 u32 sync;
790 u32 vmode;
791 u32 flag;
792 };
④ struct fb_ops
//显示设备具体操作函数的实现结构体
256 struct fb_ops {
257 /* open/release and usage marking */
258
struct module *owner;
259
int (*fb_open)(struct fb_info *info, int user);
/* 设备打开执行函数,在用户层open时被执行 */
260
int (*fb_release)(struct fb_info *info, int user);
/* 设备关闭执行函数,在用户层close时被执行 */
261
262 /* 显存非连续性的或无法进行内存映射的显示设备将使用read和write函数
264 */
265 ssize_t (*fb_read)(struct fb_info *info, char __user *buf,
266 size_t count, loff_t *ppos);
267 ssize_t (*fb_write)(struct fb_info *info, const char __user *buf,
268 size_t count, loff_t *ppos);
269
270
/* 用于检查和
备的可变参数
修改显示设
*/
272 int (*fb_check_var)(struct fb_var_screeninfo *var, struct fb_info *info);
273
274
/* 修改info结构体内部的共享成员 */
275 int (*fb_set_par)(struct fb_info *info);
276
277
/* 配置颜色寄存器 */
278 int (*fb_setcolreg)(unsigned regno, unsigned red, unsigned green,
279 unsigned blue, unsigned transp, struct fb_info *info);
280
281
/* 配置显存数据流*/
282 int (*fb_setcmap)(struct fb_cmap *cmap, struct fb_info *info);
283
284
/*空白显示 */
285 int (*fb_blank)(int blank, struct fb_info *info);
286
287
/* 画笔显示 */
288 int (*fb_pan_display)(struct fb_var_screeninfo *var, struct fb_info *info);
289
290
/*矩形绘画 */
291 void (*fb_fillrect) (struct fb_info *info, const struct fb_fillrect *rect);
292
/* 数据拷贝*/
293 void (*fb_copyarea) (struct fb_info *info, const struct fb_copyarea *region);
294
/* 显示器上显示图片 */
295 void (*fb_imageblit) (struct fb_info *info, const struct fb_image *image);
296
297
/* 绘制光标*/
298 int (*fb_cursor) (struct fb_info *info, struct fb_cursor *cursor);
299
300 /* wait for blit idle, optional */
301 int (*fb_sync)(struct fb_info *info);
302
303 /* perform fb specific ioctl (optional) */
304 int (*fb_ioctl)(struct fb_info *info, unsigned int cmd,
305 unsigned long arg);
306
307 /* Handle 32bit compat ioctl (optional) */
308 int (*fb_compat_ioctl)(struct fb_info *info, unsigned cmd,
309 unsigned long arg);
310
311 /* perform fb specific mmap */
312 int (*fb_mmap)(struct fb_info *info, struct vm_area_struct *vma);
313
314 /* get capability given var */
315 void (*fb_get_caps)(struct fb_info *info, struct fb_blit_caps *caps,
316 struct fb_var_screeninfo *var);
317
318 /* teardown any resources to do with this framebuffer */
319 void (*fb_destroy)(struct fb_info *info);
320
321 /* called at KDB enter and leave time to prepare the console */
322 int (*fb_debug_enter)(struct fb_info *info);
323 int (*fb_debug_leave)(struct fb_info *info);
324 };
⑤ struct fb_var_screeninfo
//显示器件的可变参数描述结构体
241 struct fb_var_screeninfo {
242 __u32 xres;
/* 显示器的横轴像素值 */
243 __u32 yres;
/* 显示器的竖轴像素值 */
244 __u32 xres_virtual;
/* 虚拟值 横轴像素起始地址 */
245 __u32 yres_virtual;
/* 虚拟值 竖轴像素起始地址 */
246 __u32 xoffset;
/* 虚拟横轴相对于物理值得偏移量 */
247 __u32 yoffset; /* resolution */
248
249 __u32 bits_per_pixel;
/* 单个像素点的位数 */
250
__u32 grayscale;
/* 0=彩色 1=灰度 */
251 /* >1 = FOURCC */
252
struct fb_bitfield red;
/* 红色在像素点中的位域 */
253 struct fb_bitfield green;
254 struct fb_bitfield blue;
255 struct fb_bitfield transp;
256
257
__u32 nonstd;
/* 如果!=0则表示非标准的像素格式 */
258
259
__u32 activate;
/* 配置设备启动方式FB_ACTIVATE_XXX*/
260
261 __u32 height;
/* 画面高度,单位:mm */
262 __u32 width;
263
264
__u32 accel_flags;
/* 这里配置为FB_ACCEL_NONE*/
265
266
/* 从设备节点上获取的Timing值,除了pixclk以外 */
267 __u32 pixclock; /* pixel clock in ps (pico seconds) */
268 __u32 left_margin; /* time from sync to picture */
269 __u32 right_margin; /* time from picture to sync */
270 __u32 upper_margin; /* time from sync to picture */
271 __u32 lower_margin;
272 __u32 hsync_len; /* length of horizontal sync */
273 __u32 vsync_len; /* length of vertical sync */
274 __u32 sync; /* see FB_SYNC_* */
275 __u32 vmode; /* see FB_VMODE_* */
276 __u32 rotate; /* angle we rotate counter clockwise */
277 __u32 colorspace; /* colorspace for FOURCC-based modes */
278 __u32 reserved[4]; /* Reserved for future compatibility */
279 };
⑥ struct fb_fix_screeninfo
//显示器件固定参数结构体
157 struct fb_fix_screeninfo {
158
char id[16];
/* 设备器件的名称 */
159
unsigned long smem_start; /*
显存物理缓存的起始地址
*/
160
161
__u32 smem_len;
/* 显存的字节大小 */
162
__u32 type; /*
显存的数据格式FB_TYPE_XXX
*/
163 __u32 type_aux;
/* 交换已经交换的画面?? */
164
__u32 visual;
/* 画面显示格式FB_VISUAL_XXX */
165 __u16 xpanstep;
/*0=没有硬件画笔 */
166 __u16 ypanstep;
/* 0=没有硬件画笔 */
167 __u16 ywrapstep;
/* zero if no hardware ywrap */
168
__u32 line_length;
/* 显示一行的字节数 */
169 unsigned long mmio_start;
/* IO内存映射的物理起始地址 */
170
171 __u32 mmio_len;
172
__u32 accel;
/* FB_ACCEL_NONE=没有硬件加速度器 */
173
174 __u16 capabilities; /* see FB_CAP_* */
175 __u16 reserved[2];
/* 保留的可扩展成员 */
176 };
⑦
struct fb_cmap
//调色板结构体
281 struct fb_cmap {
282 __u32 start;
/* 起始地址*/
283 __u32 len;
/* 长度 */
284 __u16 *red;
/* 红色占用地址 */
285 __u16 *green;
286 __u16 *blue;
287 __u16 *transp; /* transparency, can be NULL */
288 };
⑧
struct fb_bitfield
//用于表示像素点中,某个颜色的位域
struct fb_bitfield {
189
__u32 offset;
/* 在像素中的位偏移 */
190
__u32 length;
/* 占用的位个数 */
191 __u32 msb_right; /*
最高有效位有几位 */
193 };
⑨
struct dma_chan
//描述一个DMA设备
struct dma_chan {
107 int dev_id; /*
DMA的设备ID号 */
108
109 void __iomem *io;
/* 虚拟内存映射地址 */
110 const char *dev_str;
/* 设备的名字 */
111 int irq;
112 void *irq_dev;
113 unsigned int fifo_addr;
/* DMA缓存区的首地址 */
114 unsigned int mode;
/* DMA的模式 */
115 };
⑩
struct dma_interleaved_template
//DMA的配置结构体
struct dma_interleaved_template {
160
dma_addr_t src_start;
/*源地址的起始地址*/
161 dma_addr_t dst_start;
/*目标地址的起始地址*/
162
enum dma_transfer_direction dir;
/*DMA数据的传输方向
enum dma_transfer_direction {
89 DMA_MEM_TO_MEM,
90 DMA_MEM_TO_DEV,
91 DMA_DEV_TO_MEM,
92 DMA_DEV_TO_DEV,
93 DMA_TRANS_NONE,
94 };
*/
163
bool src_inc;
/* 1=源地址每次自动递增 */
164
bool dst_inc;
/* 1 = 目标地址每次自动递增 */
165
bool src_sgl;
/* 1 = 每次初始后,源地址根据sgl来偏移*/
166
bool dst_sgl;
167
size_t numf;
/* DMA中缓存的帧数 */
168
size_t frame_size;
/* 每帧画面的块数 */
169
struct data_chunk sgl[0];
/*配置非连续性的地址偏移 */
170 };
相关的函数接口:
① fb_info *
framebuffer_alloc(size_t size, struct device *dev);
//动态分配基于设备节点的struct fb_info结构体,
同时分配更多内存在info->per指针上指定(可以作为自定义驱动变量)
② void
framebuffer_release(struct fb_info *info);
//对应释放struct fb_info的内存
③ void
clk_disable_unprepare(struct clk *clk);
//用于禁用时钟的输出
④ int
of_get_videomode(struct device_node *np, struct videomode *vm,
int index);
//从折本树节点上获取对应的设备描述结构体
⑤ void *
dma_alloc_wc(struct device *dev, size_t size,dma_addr_t *dma_addr, gfp_t gfp);
//获取显存对应的物理地址并返回映射的虚拟地址
⑥ int
fb_videomode_from_videomode(const struct videomode *vm,struct fb_videomode *fbmode);
//将struct videomode 转换为struct fb_videomode
⑦ void
fb_videomode_to_var(struct fb_var_screeninfo *var,const struct fb_videomode *mode);
//将struct fb_videomode的值转换为struct fb_var_screeninfo里
⑧ int
fb_alloc_cmap(struct fb_cmap *cmap, int len, int transp);
//动态分配载流数据,
一个颜色映射表??
⑨ int
clk_set_rate(struct clk *clk, unsigned long rate);
//配置时钟的频率,单位Hz
⑩ int
clk_prepare_enable(struct clk *clk);
//使能启动时钟
11 int
dmaengine_terminate_all(struct dma_chan *chan);
//停止DMA的工作
12 struct dma_async_tx_descriptor *
dmaengine_prep_interleaved_dma(struct dma_chan *chan, struct dma_interleaved_template *xt,unsigned long flags);
//配置DMA
13 void
dma_async_issue_pending(struct dma_chan *chan);
//启动DMA
14 int
register_framebuffer(struct fb_info *fb_info);
//向内核注册struct fb_info
15 int
unregister_framebuffer(struct fb_info *fb_info);
//注销struct fb_info
|
//
三、FrameBuffer驱动框架的底层源码分析:
//