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)。
///
|
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
|