欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

12.LCD驱动

程序员文章站 2022-03-22 14:44:04
...

 

LCD相关知识

 

0.1硬件

12.LCD驱动

12.LCD驱动

板载的lcd是24数据线的,但是这里接了5+6+5,低位连地,因为2440支持16、24bpp显示

12.LCD驱动

12.LCD驱动

上图流程:

  1. 电子枪移到下一个像素,“VCLK”把LCD想象成里面有很多像素,像素一行一行的排下来,后面有一个电子枪把颜色喷打到像素点上,喷完一个像素点后,电子枪移动到下一个像素电上喷打颜色,它之所以知道移动到下一个像素点,是因为有一个“时钟”,每来一个 “时钟”,每来一个“时钟”,就从左向右移动一个像素,然后打出颜色。
  2. 颜色的数据来源:RGB

VDxx(vedio data视频数据)从R0-B4一共16条线上为颜色(有些线没有使用,本来是VD0~VD23上图只使用了16条),电子枪是从这里得到颜色数据后打到像素点上

  1. 移动到LCD最右边,行(水平方向)同步信号-HSYNC,当电子枪从最左边喷打颜色到最右边后,就要转到下一个行像素点上,接收到HSYNC同步信号,会从行最右边跳转到下一行的最左边,接着再从数据线上得到颜色喷打到行像素上。
  2. 垂直方向同步信号:VSYNC(或称为帧同步信号VFRAME)

当喷打到最后一行的最右边,就会跳转到最上一行的最左边。

  1. 使用显示器时会发现有4条黑框(里面没有喷打颜色)VM(VDENGPC4)

VDEN:vedio data enable当这个VDEN使能有效时,喷枪才会打出颜色,只移动而喷打颜色时就会出现黑框

  1. 喷枪从“线”上得到颜色,颜色是从显存framebuffer上得到,分配显存—>将地址告诉LCD控制器—>LCD从显存取出一个颜色发送到“线”上—>LCD控制器控制VDEN、VCLK、VFRAME等把颜色喷打到像素上

0.2LCD时序原理

从喷枪图可以看出,LCD实际范围是240*320,但是喷枪实际走过的的范围是

(240+ HSPW+1 HBPD+1+HFPD+1) * (320+VSPW+1 VBPD+1+ HFPD+1)比LCD的要大

从时序图可以得出我们需要设置以下的参数

HSPW

HBPD

HFPD

VSPW

VBPD

HFPD+

VCLK

CPU三星S3C2440A芯片手册418

12.LCD驱动

12.LCD驱动

0.3.framebuffer数据格式   

12.LCD驱动

对于16bpp来说我们可以如下设置

12.LCD驱动

如果是8bpp就是如下

12.LCD驱动

0.4lcd控制器

fbmem.c <--- 底层硬件驱动提供

1.Fbmem.c 分析:

1,先看“入口函数”:

int __init fbmem_init(void)

-->register_chrdev(FB_MAJOR,"fb",&fb_fops) 注册"file_operations”结构体“fb_fops”。

1.主设备号:

12.LCD驱动

2.File_operations 结构体:

12.LCD驱动

因为“fbmem.c”是通用的文件,故并不能直接使用这个 file_operations 结构

中的.read 等函数。

int __init fbmem_init(void)

-->register_chrdev(FB_MAJOR,"fb",&fb_fops)

-->fb_class = class_create(THIS_MODULE, "graphics");//创建类(是额外代码自动创建设备节点。)这里 fbmem.c 没有在设备类下创建设备,只有真正有硬件设备时才有必要在这个类下去创建设备。,在“register_framebuffer()”中可以看到创建设备

“fb_info->dev = device_create(fb_class,fb_info->device,MKDEV(FB_MAJOR, i), "fb%d", i);”

假设

3.app: open

open("/dev/fb0", ...) 假设 APP 打开一个主设备号: 29, 次设备号:

0 的设备时,最终会找到 file_operations fb_fops 结构中的“.open =

fb_open,”函数(其中用到: registered_fb[iminor(inode)]数组)。

--------------------------------------------------------------

Int fb_open(struct inode *inode, struct file *file)

-->int fbidx = iminor(inode); //iminor(inode)得到这个设备节点的“次设备号”。

-->struct fb_info *info; //帧缓冲区结构体。

-->info = registered_fb[fbidx];//即 struct fb_info *info =registered_fb[fbidx];

假设“次设备号”为 0.即:

struct fb_info *info = registered_fb[iminor(inode)] =registered_fb[0]。从这个 registered_fb[]数组里得到“以次设备号为 0 为下标”的一项。

-->info->fbops->fb_open //若这个 info = registered_fd[0] 的“fbops”有“fb_open”函数时就:

-->res = info->fbops->fb_open(info,1);

简明过程:

kernel:

fb_open

int fbidx = iminor(inode);

struct fb_info *info = = registered_fb[0];

//fb 是 frame buffer 帧缓冲区

----------------------------------------------------

4.App:read()

想知道内存上有什么内容。看“file_operations fb_fops”结

构中的“.read = fb_read”

ssize_t fb_read(struct file *file, char __user *buf, size_t count,loff_t *ppos)

-->struct fb_info *info = registered_fb[iminor(inode)];以次设备号为下

标从 registered_fd 数组中得一项赋给"info"结构。

-->info->fbops->fb_read //若这个 info 数组项中提供了“fbops”结构的

“fb_read”读函数时:就调用此读函数。

-->return info->fbops->fb_read(info, buf, count, ppos);

-->若没有提供"info"项,接着就:

total_size = info->screen_size;(屏幕大小)等。

-->buffer = kmalloc((count > PAGE_SIZE) ? PAGE_SIZE : count,GFP_KERNEL);//分配一个缓冲区。

-->src = (u32 __iomem *) (info->screen_base + p);

//screen_base 是指显存的基地址。这里是读源 src 等于显存的基地址加上某个偏移值。

-->*dst++ = fb_readl(src++);

//读源(从显存基地址+P 偏移)那里读到一个数据放到目标“*dst++”里。 dst 是 buffer, buffer 是 kmalloc()上面分配的空间。

-->copy_to_user(buf, buffer, c); //把数据拷贝到用户空间。

总结:

就是说有"if (info->fbops->fb_read)"函数时就从读函数中读

“info->fbops->fb_read(info, buf, count, ppos);”;若没有则从“src = (u32 __iomem *) (info->screen_base + p);”里读(从screen_base 显存基地址加一个 P 偏移处)。从这个“src”源处读到用 kmalloc()分配的一个目标地址“dst”中(*dst++ = fb_readl(src++);)。最后“copy_to_user(buf, buffer, c)”把读到的数据拷贝到用户空间。

简明过程:

kernel:

12.LCD驱动

5.小结

从上面知道“.open”和“.read”都依赖一个结构体 fb_info 结构体。这个结

构体是从数组“registered_fd[]”经“次设备号”为下标得到一项为 fd_info结构体。

“struct fb_info *info = registered_fb[fbidx]”。registered_fb 这个 fb_info 结构数组的定义:

Fbmem.c 提供的都是抽象出来的东西。最终都得依赖这个“registered_fb”数

组里的“fb_info”结构体。搜索源代码找“registered_fb”这个 fb_info 结构数组的出处。

1,“registered_fd” bf_inof 结构数组的定义:

struct fb_info *registered_fb[FB_MAX] __read_mostly;

2,“registered_fd” bf_inof 结构数组的设置:

Int register_framebuffer(struct fb_info *fb_info); //注册framebuffer。

-->registered_fb[i] = fb_info;

12.LCD驱动

这样分层之后, APP 就知道读写函数里面的形参是什么含义了。 Ioctl()要传什

么参数就固定下来了。

分析“register_framebuffer()”:

Int register_framebuffer(struct fb_info *fb_info)

-->if (!registered_fb[i]) //先找出一个空项。

-->fb_info->dev = device_create(fb_class, fb_info->device,

MKDEV(FB_MAJOR, i), "fb%d", i);

在“fd_class”类下面创建设备。只有真正有硬件设备时才有必要在这个类下去创建设备。这样 mdev 或 udev 才能去自动创建设备节点。 Fbmem.c 只是抽象出来的 LCD 驱动框架程序,并不能支持具体的驱动。它需要依赖底层的某个驱 动程序给它注册一个“fb_info”结构体(由“register_framebuffer()来注册”)。搜索内核会发现有各种 LCD 驱动程序调用这个“register_framebuffer()”(如 6832fb.c、 amifb.c、 atmel_lcdfb.c、还有 2410 的如 s3fb.c、

s3c2410fb.c 等)。所以想要用"fbmem.c"这一套代码时,就要按上图的框架来写代码,要自已定义底层的硬件驱动程序(如上面内核中有 s3c2410fb.c 这个LCD 底层驱动程序。)

问 1. registered_fb 在哪里被设置?

答 1. register_framebuffer

2.分析层底硬件驱动程序

1.分析过程

如: inux/drivers/video/s3c2410fb.c(从入口函数看起)

__devinit s3c2410fb_init(void)

-->platform_driver_register(&s3c2410fb_driver);注册一个平台驱动(总线设备驱动模型时关心.probe 函数)

12.LCD驱动

分配一个"fb_info"结构。接着就开始设置这个 fb_info 结构体.

-->

12.LCD驱动

12.LCD驱动

2.总结

总结上面的过程:抽象出驱动程序:怎么写 LCD 驱动程序?

1. 分配一个 fb_info 结构体: framebuffer_alloc

2. 设置

3. 注册: register_framebuffer

4. 硬件相关的操作要得到 LCD 的分辨率等信息: 从上往下分析(fbmem.c 开始)

Fbmem.c: 看“.ioctl”。

int __init fbmem_init(void)

-->register_chrdev(FB_MAJOR,"fb",&fb_fops) //看 fb_ops 结构

(file_operations)。

-->

struct file_operations fb_fops = {

.. .. ..

.ioctl = fb_ioctl, //看 fb_ioctrl()中获得什么内容.

.. .. ..

}

-->

//有各个 cmd

int fb_ioctl(struct inode *inode, struct file *file, unsigned intcmd,unsigned long arg)

-->以次设备号为下标,在 registered_fb[]数组中得到 fb_info 结构体变量"info"。

int fbidx = iminor(inode);

struct fb_info *info = registered_fb[fbidx];

--> 将这个 info 结构中的 var 成员拷贝回用户空间。

switch (cmd) {

case FBIOGET_VSCREENINFO: //GET 获得.V(var)可变的.SCREEN 屏幕.INFO 信息.

return copy_to_user(argp, &info->var,

sizeof(var)) ? -EFAULT : 0;

---------------------------------------------------------------------

看 info->var 这个成员中有什么内容:

12.LCD驱动

12.LCD驱动

分析 fbmem.c 中的 LCD 分辨率也证实了 fbmem.c 是抽象出来的内容,最终它得

依赖具体的底层设备驱动提供的“fb_info”结构体。

3.LCD 硬件操作步骤

在写驱动之前,先看硬件的具体操作:

参考以前的 LCD 裸机程序笔记。

12.LCD驱动

12.LCD驱动

上图流程:

  1. 电子枪移到下一个像素,“VCLK”把LCD想象成里面有很多像素,像素一行一行的排下来,后面有一个电子枪把颜色喷打到像素点上,喷完一个像素点后,电子枪移动到下一个像素电上喷打颜色,它之所以知道移动到下一个像素点,是因为有一个“时钟”,每来一个 “时钟”,每来一个“时钟”,就从左向右移动一个像素,然后打出颜色。
  2. 颜色的数据来源:RGB

VDxx(vedio data视频数据)从R0-B4一共16条线上为颜色(有些线没有使用,本来是VD0~VD23上图只使用了16条),电子枪是从这里得到颜色数据后打到像素点上

  1. 移动到LCD最右边,行(水平方向)同步信号-HSYNC,当电子枪从最左边喷打颜色到最右边后,就要转到下一个行像素点上,接收到HSYNC同步信号,会从行最右边跳转到下一行的最左边,接着再从数据线上得到颜色喷打到行像素上。
  2. 垂直方向同步信号:VSYNC(或称为帧同步信号VFRAME)

当喷打到最后一行的最右边,就会跳转到最上一行的最左边。

  1. 使用显示器时会发现有4条黑框(里面没有喷打颜色)VM(VDENGPC4)

VDEN:vedio data enable当这个VDEN使能有效时,喷枪才会打出颜色,只移动而喷打颜色时就会出现黑框

  1. 喷枪从“线”上得到颜色,颜色是从显存framebuffer上得到,分配显存—>将地址告诉LCD控制器—>LCD从显存取出一个颜色发送到“线”上—>LCD控制器控制VDEN、VCLK、VFRAME等把颜色喷打到像素上

 

 

设置 LCD 控制器时,不同的 LCD 的"VCLK"(频率)可能不会一样,所以要

调整 LCD 控制器来设置频率的快和慢,使其发出合适的时钟(若 1 秒一次,则

喷打颜色到像素点时都看的清晰太慢了。若 VCLK 为 1GHz,可能又反应不过

来。所以要调 LCD 控制器使其发出合适的时钟)。

发出使命的“时钟”要看 LCD 屏的硬件手册。

 

硬件操作过程:

1, 根据 LCD 手册设置 LCD 控制器。(时钟参数等)

2, 分配显存,并将显存的地址告诉 LCD 控制器。

还要说明颜色格式。一个像素用多少个字节来表示等。

  1. 之后就是 2440 相关的操作:配置引脚用于 LCD。芯片的管脚可以配置成输入输出,还可以配置为 LCD 引脚。

1.LCD 硬件参数设置:

1,头文件:

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/fb.h>
#include <linux/init.h>
#include <linux/dma-mapping.h>
#include <linux/interrupt.h>
#include <linux/workqueue.h>
#include <linux/wait.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <asm/div64.h>
#include <asm/mach/map.h>
#include <asm/arch/regs-lcd.h>
#include <asm/arch/regs-gpio.h>
#include <asm/arch/fb.h>

2,入口、出口函数以及修饰它们的框架:

1.1,先写入口函数:

static int lcd_init(void)

{

return 0;

}

1.2,出口函数;

static void lcd_exit()

{ }

1.3,修饰入口中与出口函数.

module_init(lcd_init);

module_exit(lcd_exit);

MODULE_LICENSE("GPL");

static int lcd_init(void)

{  }

2.,分配一个 fb_info 结构体.

static struct fb_info *s3c_lcd; //定义一个 fb_info 结构体变量 s3c_lcd.

 

3.,设置 fb_info 结构体。

 

1.分析

struct fb_info *framebuffer_alloc(size_t size, struct device *dev)

-->传入了一个“size”大小,本来 fb_info 结构体的大小如下:

int fb_info_size = sizeof(struct fb_info);

-->然后传入的 size 大小与原本定义的“fb_info”结构体大小相加:分配这一段 p 空间。

p = kzalloc(fb_info_size + size, GFP_KERNEL);

-->然后 par 指向额外分配的那段 size 空间。

info->par = p + fb_info_size;我们不需要这段额外空间就不设置。就把size=0.

 

s3c_lcd = framebuffer_alloc(0, NULL);//可能内存不足要判断返回值.

分析: struct fb_info *framebuffer_alloc(size_t size, struct device*dev)原型:

参 1 为大小。在定义 fb_info 结构时,结构体的大小是固定了的。内核中经常有这个取巧的方法,本来一个结构体原本分配的大小,而内核紧接着再分配了一段大小给这个结构体。定义时的结构体里面有一个指针,指向这个内核又分配的一段空间,这个空间里放“私有数据”。

2.设置

设置“fb_info”结构体:

  • 先看 fb_info 结构的具体成员: fix,var,fbops 等。

12.LCD驱动

②,设置固定的参数:

fb_info 结构定义中的“struct fb_fix_screeninfo fix;”

固定参数的内容:

12.LCD驱动

a,__u32 smem_len; 设置显存长度。

显存长度设置查看 LCD 手册“F:\embedded\第二期:深入驱动\源码_文档_图片_原理图_芯片手册\原理图及芯片手册\JZ2440v2\芯片手册/液晶屏.pdf”

分辨率:

12.LCD驱动

颜色:

12.LCD驱动

RGB 红绿蓝每个像素占 6bit,实际上在 2410 里面, RGB 只能是 R-5 位, G-6

位, B-5 位,不能是 666,一色里丢弃 1 位没关系。这是因为 2440 不支持 18

位,只支持 16 位或其他如 24 位等。但这里浪费了 2 位也没关系。

则显存的长度:分辨率*颜色位数---240*320*16(位)

12.LCD驱动

b,__u32 type:看 FB_TYPE_*宏

12.LCD驱动

此实例中是用“FB_TYPE_PACKED_PIXELS”。是个默认值。(一般看不懂时就使用默认值)。默认值就是可以支持大部分类的 LCD。

12.LCD驱动

c,__u32 type_aux:附加的类型

若是平板要用到这个附加的类型。但此实例中不用设置。

d,__u32 visual:

查看宏“FB_VISUAL_*”

12.LCD驱动

我们是 TFT 真彩色屏:

12.LCD驱动

e,__u16 xpanstep;

12.LCD驱动

f,__u32 line_length;

12.LCD驱动

最终固定信息设置结果为:

12.LCD驱动

只有一个“显存”的起始地址没有设置,在之后分配显存的时候再去设置它。

 

③,设置可变的参数:

fb_info 结构定义中的“struct fb_var_screeninfo var;”

可变信息的结构定义:

12.LCD驱动

A, __u32 xres:X 方向的分辨率。

12.LCD驱动

B, __u32 xres_virtual:虚拟分辨率。

买回的 LCD 分辨率固定死了,但还能在 PC 桌面上右键设置虚拟分辨率。__u32 yres_virtual:这里 x,y 方向的虚拟分辨率都设置成和实际的分辨率一样。

12.LCD驱动

C, __u32 xoffset:虚拟和实际分辨率之间的偏移值(差值)。

__u32 yoffset:

上面设置了 x, y 方向上的实际和虚拟的分辨率是相同的,所以这两个项设置为

0 即可。 因为是 0 则此项可以不设置。 p = kzalloc(fb_info_size + size,GFP_KERNEL);分配空间默认也填 0

D,__u32 bits_per_pixel:每个像素用多少位。

(此项重要)。这里从 LCD 手册知道是 16 位。

12.LCD驱动

。。。。。。。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

相关标签: Driver