您的位置 首页 开关

嵌入式Linux之我行——S3C2440上LCD驱动(FrameBuffer)实例开发解说

一、开发环境主机:VMWare–Fedora9开发板:Mini2440–64MBNand,Kernel:2.6.30.4编译器:arm-linux-gcc-4.3.2二、背景知识1.LCD…

一、开发环境

  • 机:VMWare–Fedora 9
  • 开发板:Mini2440–64MB Nand, Kernel:2.6.30.4
  • 编译器:arm-linux-gcc-4.3.2

二、布景常识

1. LCD作业的硬件需求:
要使一块LCD正常的显现文字或图画,不只需求LCD驱动器,并且还需求相应的LCD操控器。在通常情况下,出产厂商把LCD驱动器会以COF/COG的方式与LCD玻璃基板制造在一起,而LCD操控器则是由外部的电路来完结,现在许多的MCU内部都集成了LCD操控器,如S3C2410/2440等。经过LCD操控器就能够发生LCD驱动器所需求的操控信号来操控STN/TFT屏了。
2. S3C2440内部LCD操控器结构图:
咱们依据数据手册来描绘一下这个集成在S3C2440内部的LCD操控器:
a:LCD操控器由REGBANK、LCDCDMA、TIMEGEN、VIDPRCS寄存器组成;
b:REGBANK由17个可编程的寄存器组和一块256*16的调色板内存组成,它们用来装备LCD操控器的;
c:LCDCDMA是一个专用的DMA,它能主动地把在侦内存中的视频数据传送到LCD驱动器,经过运用这个DMA通道,视频数据在不需求CPU的干涉的情况下显现在LCD屏上;
d:VIDPRCS接纳来自LCDCDMA的数据,将数据转换为适宜的数据格式,比方说4/8位单扫,4位双扫显现形式,然后经过数据端口VD[23:0]传送视频数据到LCD驱动器;
e:TIMEGEN由可编程的逻辑组成,他生成LCD驱动器需求的操控信号,比方VSYNC、HSYNC、VCLK和LEND等等,而这些操控信号又与REGBANK寄存器组中的LCDCON1/2/3/4/5的装备密切相关,经过不同的装备,TIMEGEN就能发生这些信号的不同形状,然后支撑不同的LCD驱动器(即不同的STN/TFT屏)。
3. 常见TFT屏作业时序剖析:
LCD供给的外部接口信号:

VSYNC/VFRAME/STV:笔直同步信号(TFT)/帧同步信号(STN)/SEC TFT信号;
HSYNC/VLINE/CPV:水平同步信号(TFT)/行同步脉冲信号(STN)/SEC TFT信号;
VCLK/LCD_HCLK:象素时钟信号(TFT/STN)/SEC TFT信号;
VD[23:0]:LCD像素数据输出端口(TFT/STN/SEC TFT);
VDEN/VM/TP:数据使能信号(TFT)/LCD驱动沟通偏置信号(STN)/SEC TFT 信号;
LEND/STH:行完毕信号(TFT)/SEC TFT信号;
LCD_LPCOE:SEC TFT OE信号;
LCD_LPCREV:SEC TFT REV信号;
LCD_LPCREVB:SEC TFT REVB信号。

一切显现器显现图画的原理都是从上到下,从左到右的。这是什么意思呢?这么说吧,一副图画能够看做是一个矩形,由许多摆放规整的点一行一行组成,这些点称之为像素。那么这幅图在LCD上的显现原理便是:

A:显现指针从矩形左上角的榜首行榜首个点开端,一个点一个点的在LCD上显现,在上面的时序图上用时刻线一共就为VCLK,咱们称之为像素时钟信号;
B:当显现指针一向显现到矩形的右边就完毕这一行,那么这一行的动作在上面的时序图中就称之为1 Line;
C:接下来显现指针又回到矩形的左面从第二行开端显现,留心,显现指针在从榜首行的右边回到第二行的左面是需求必定的时刻的,咱们称之为行切换;
D:如此类推,显现指针就这样一行一行的显现至矩形的右下角才把一副图显现完结。因而,这一行一行的显现在时刻线上看,便是时序图上的HSYNC;
E:但是,LCD的显现并不是对一副图画快速的显现一下,为了继续和安稳的在LCD上显现,就需求切换到另一幅图上(另一幅图能够和上一副图一样或许不一样,意图仅仅为了将图画继续的显现在LCD上)。那么这一副一副的图画就称之为帧,在时序图上就一共为1 Frame,因而从时序图上能够看出1 Line仅仅1 Frame中的一行;
F:相同的,在帧与帧切换之间也是需求必定的时刻的,咱们称之为帧切换,那么LCD整个显现的进程在时刻线上看,就可一共为时序图上的VSYNC。

上面时序图上各时钟延时参数的意义如下:(这些参数的值,LCD发生厂商会供给相应的数据手册)

VBPD(vertical back porch):一共在一帧图画开端时,笔直同步信号今后的无效的行数,对应驱动中的upper_margin;
VFBD(vertical front porch):一共在一帧图画完毕后,笔直同步信号曾经的无效的行数,对应驱动中的lower_margin;
VSPW(vertical sync pulse width):一共笔直同步脉冲的宽度,用行数核算,对应驱动中的vsync_len;
HBPD(horizontal back porch):一共从水平同步信号开端到一行的有用数据开端之间的VCLK的个数,对应驱动中的left_margin;
HFPD(horizontal front porth):一共一行的有用数据完毕到下一个水平同步信号开端之间的VCLK的个数,对应驱动中的right_margin;
HSPW(horizontal sync pulse width):一共水平同步信号的宽度,用VCLK核算,对应驱动中的hsync_len;

关于以上这些参数的值将别离保存到REGBANK寄存器组中的LCDCON1/2/3/4/5寄存器中:(对寄存器的操作请查看S3c2440数据手册LCD部分)

LCDCON1:17– 8位CLKVAL
6– 5位扫描形式(关于STN屏:4位单/双扫、8位单扫)
4– 1位色位形式(1BPP、8BPP、16BPP等)

LCDCON2:31 – 24位VBPD
23 – 14位LINEVAL
13 – 6位VFPD
5 – 0位VSPW

LCDCON3:25 – 19位HBPD
18 – 8位HOZVAL
7 – 0位HFPD

LCDCON4: 7 – 0位HSPW

LCDCON5:

4. 帧缓冲(FrameBuffer):
帧缓冲是Linux为显现设备供给的一个接口,它把一些显现设备描绘成一个缓冲区,答应运用程序经过FrameBuffer界说好的接口拜访这些图形设备,然后不必去关怀详细的硬件细节。关于帧缓冲设备而言,只要在显现缓冲区与显现点对应的区域写入色彩值,对应的色彩就会主动的在屏幕上显现。下面来看一下在不同色位形式下缓冲区与显现点的对应联系:
三、帧缓冲(FrameBuffer)设备驱动结构:
帧缓冲设备为规范的字符型设备,在Linux中主设备号29,界说在/include/linux/major.h中的FB_MAJOR,次设备号界说帧缓冲的个数,最大答应有32个FrameBuffer,界说在/include/linux/fb.h中的FB_MAX,对应于文件体系下/dev/fb%d设备文件。

1. 帧缓冲设备驱动在Linux子体系中的结构如下:

咱们从上面这幅图看,帧缓冲设备在Linux中也能够看做是一个完好的子体系,大体由fbmem.c和xxxfb.c组成。向上给运用程序供给完善的设备文件操作接口(即对FrameBuffer设备进行read、write、ioctl等操作),接口在Linux供给的fbmem.c文件中完结;向下供给了硬件操作的接口,仅仅这些接口Linux并没有供给完结,由于这要依据详细的LCD操控器硬件进行设置,所以这便是咱们要做的工作了(即xxxfb.c部分的完结)。

2. 帧缓冲相关的重要数据结构:
从帧缓冲设备驱动程序结构看,该驱动首要跟fb_info结构体有关,该结构体记载了帧缓冲设备的悉数信息,包含设备的设置参数、状况以及对底层硬件操作的函数指针。在Linux中,每一个帧缓冲设备都必须对应一个fb_info,fb_info在/linux/fb.h中的界说如下:(只列出重要的一些)

structfb_info{
intnode;
intflags;
structfb_var_screeninfo var;
structfb_fix_screeninfo fix;
structfb_monspecs monspecs;
structwork_structqueue;
structfb_pixmap pixmap;
structfb_pixmap sprite;
structfb_cmap cmap;
structfb_videomode*mode;

#ifdefCONFIG_FB_BACKLIGHT
structbacklight_device*bl_dev;
structmutex bl_curve_mutex;
u8 bl_curve[FB_BACKLIGHT_LEVELS];
#endif
#ifdefCONFIG_FB_DEFERRED_IO
structdelayed_work deferred_work;
structfb_deferred_io*fbdefio;
#endif

structfb_ops*fbops;
structdevice*device;
structdevice*dev;
intclass_flag;
#ifdefCONFIG_FB_TILEBLITTING
structfb_tile_ops*tileops;
#endif
char__iomem*screen_base;
unsignedlongscreen_size;
void*pseudo_palette;
#defineFBINFO_STATE_RUNNING0
#defineFBINFO_STATE_SUSPENDED1
u32 state;
void*fbcon_par;
void*par;
};

其间,比较重要的成员有structfb_var_screeninfo var、structfb_fix_screeninfo fix和structfb_ops*fbops,他们也都是结构体。下面咱们一个一个的来看。

fb_var_screeninfo结构体首要记载用户能够修正的操控器的参数,比方屏幕的分辨率和每个像素的比特数等,该结构体界说如下:

structfb_var_screeninfo{
__u32 xres;
__u32 yres;
__u32 xres_virtual;
__u32 yres_virtual;
__u32 xoffset;
__u32 yoffset;
__u32 bits_per_pixel;
__u32 grayscale;

structfb_bitfield red;
structfb_bitfield green;
structfb_bitfield blue;
structfb_bitfield transp;

__u32 nonstd;
__u32 activate;
__u32 height;
__u32 width;
__u32 accel_flags;


__u32 pixclock;
__u32 left_margin;
__u32 right_margin;
__u32 upper_margin;
__u32 lower_margin;
__u32 hsync_len;
__u32 vsync_len;
__u32 sync;
__u32 vmode;
__u32rotate;
__u32 reserved[5];
};

而fb_fix_screeninfo结构体又首要记载用户不能够修正的操控器的参数,比方屏幕缓冲区的物理地址和长度等,该结构体的界说如下:

structfb_fix_screeninfo{
charid[16];
unsignedlongsmem_start;
__u32 smem_len;
__u32 type;
__u32 type_aux;
__u32 visual;
__u16 xpanstep;
__u16 ypanstep;
__u16 ywrapstep;
__u32 line_length;
unsignedlongmmio_start;
__u32 mmio_len;
__u32 accel;
__u16 reserved[3];
};

fb_ops结构体是对底层硬件操作的函数指针,该结构体中界说了对硬件的操作有:(这儿只列出了常用的操作)

structfb_ops{

structmodule*owner;

//查看可变参数并进行设置
int(*fb_check_var)(structfb_var_screeninfo*var,structfb_info*info);

//依据设置的值进行更新,使之有用
int(*fb_set_par)(structfb_info*info);

//设置色彩寄存器
int(*fb_setcolreg)(unsignedregno,unsignedred,unsignedgreen,
unsignedblue,unsignedtransp,structfb_info*info);

//显现空白
int(*fb_blank)(intblank,structfb_info*info);

//矩形填充
void(*fb_fillrect)(structfb_info*info,conststructfb_fillrect*rect);

//仿制数据
void(*fb_copyarea)(structfb_info*info,conststructfb_copyarea*region);

//图形填充
void(*fb_imageblit)(structfb_info*info,conststructfb_image*image);
};

3. 帧缓冲设备作为渠道设备:
在S3C2440中,LCD操控器被集成在芯片的内部作为一个相对独立的单元,所以Linux把它看做是一个渠道设备,故在内核代码/arch/arm/plat-s3c24xx/devs.c中界说有LCD相关的渠道设备及资源,代码如下:

//LCD操控器的资源信息
staticstructresource s3c_lcd_resource[]={
[0]={
.start=S3C24XX_PA_LCD,//操控器IO端口开端地址
.end=S3C24XX_PA_LCD+S3C24XX_SZ_LCD1,//操控器IO端口完毕地址
.flags=IORESOURCE_MEM,//标识为LCD操控器IO端口,在驱动中引证这个就一共引证IO端口
},
[1]={
.start=IRQ_LCD,//LCD中止
.end=IRQ_LCD,
.flags=IORESOURCE_IRQ,//标识为LCD中止
}
};

staticu64 s3c_device_lcd_dmamask=0xffffffffUL;

structplatform_device s3c_device_lcd={
.name=“s3c2410-lcd”,//作为渠道设备的LCD设备名
.id=-1,
.num_resources=ARRAY_SIZE(s3c_lcd_resource),//资源数量
.resource=s3c_lcd_resource,//引证上面界说的资源
.dev={
.dma_mask=&s3c_device_lcd_dmamask,
.coherent_dma_mask=0xffffffffUL
}
};

EXPORT_SYMBOL(s3c_device_lcd);//导出界说的LCD渠道设备,好在mach-smdk2440.c的smdk2440_devices[]中添加到渠道设备列表中

除此之外,Linux还在/arch/arm/mach-s3c2410/include/mach/fb.h中为LCD渠道设备界说了一个s3c2410fb_mach_info结构体,该结构体首要是记载LCD的硬件参数信息(比方该结构体的s3c2410fb_display成员结构中就用于记载LCD的屏幕尺度、屏幕信息、可变的屏幕参数、LCD装备寄存器等),这样在写驱动的时分就直接运用这个结构体。下面,咱们来看一下内核是假如运用这个结构体的。在/arch/arm/mach-s3c2440/mach-smdk2440.c中界说有:

//LCD硬件的装备信息,留心这儿我运用的LCD是NEC 3.5寸TFT屏,这些参数要依据详细的LCD屏进行设置
staticstructs3c2410fb_display smdk2440_lcd_cfg __initdata={

//这个当地的设置是装备LCD寄存器5,这些宏界说在regs-lcd.h中,核算后二进制为:111111111111,然后对照数据手册上LCDCON5的各位来看,留心是从右边开端
.lcdcon5=S3C2410_LCDCON5_FRM565|
S3C2410_LCDCON5_INVVLINE|
S3C2410_LCDCON5_INVVFRAME|
S3C2410_LCDCON5_PWREN|
S3C2410_LCDCON5_HWSWP,

.type=S3C2410_LCDCON1_TFT,//TFT类型


.width=240,//屏幕宽度
.height=320,//屏幕高度

//以下一些参数在上面的时序图剖析中讲到过,各参数的值请跟据详细的LCD屏数据手册结合上面时序剖析来设定
.pixclock=100000,//像素时钟
.xres=240,//水平可见的有用像素
.yres=320,//笔直可见的有用像素
.bpp=16,//色位形式
.left_margin=19,//行切换,从同步到绘图之间的推迟
.right_margin=36,//行切换,从绘图到同步之间的推迟
.hsync_len=5,//水平同步的长度
.upper_margin=1,//帧切换,从同步到绘图之间的推迟
.lower_margin=5,//帧切换,从绘图到同步之间的推迟
.vsync_len=1,//笔直同步的长度
};

staticstructs3c2410fb_mach_info smdk2440_fb_info __initdata={
.displays=&smdk2440_lcd_cfg,//运用上面界说的装备信息
.num_displays=1,
.default_display=0,

.gpccon=0xaaaa555a,//将GPC0、GPC1装备成LEND和VCLK,将GPC8-15装备成VD0-7,其他装备成一般输出IO口
.gpccon_mask=0xffffffff,
.gpcup=0x0000ffff,//制止GPIOC的上拉功用
.gpcup_mask=0xffffffff,
.gpdcon=0xaaaaaaaa,//将GPD0-15装备成VD8-23
.gpdcon_mask=0xffffffff,
.gpdup=0x0000ffff,//制止GPIOD的上拉功用
.gpdup_mask=0xffffffff,

.lpcsel=0x0,//这个是三星TFT屏的参数,这儿不必
};

留心:可能有许多朋友不知道上面赤色部分的参数是做什么的,其值又是怎样设置的?其实它是跟你的开发板LCD操控器密切相关的,看了下面两幅图信任就大约知道他们是干什么用的:

上面榜首幅图是开发板原理图的LCD操控器部分,第二幅图是S3c2440数据手册中IO端口C和IO端口D操控器部分。原理图中运用了GPC8-15和GPD0-15来用做LCD操控器VD0-VD23的数据端口,又别离运用GPC0、GPC1端口用做LCD操控器的LEND和VCLK信号,关于GPC2-7则是用做STN屏或许三星专业TFT屏的相关信号。但是,S3C2440的各个IO口并不是单一的功用,都是复用端口,要运用他们首要要对他们进行装备。所以上面赤色部分的参数便是把GPC和GPD的部分端口装备成LCD操控功用形式。

从以上推荐的内容来看,要使LCD操控器支撑其他的LCD屏,重要的是依据LCD的数据手册修正以上这些参数的值。下面,咱们再看一下在驱动中是假如引证到s3c2410fb_mach_info结构体的(留心上面讲的是在内核中怎么运用的)。在mach-smdk2440.c中有:

//S3C2440初始化函数
staticvoid__init smdk2440_machine_init(void)
{

//调用该函数将上面界说的LCD硬件信息保存到渠道数据中
s3c24xx_fb_set_platdata(&smdk2440_fb_info);

s3c_i2c0_set_platdata(NULL);

platform_add_devices(smdk2440_devices,ARRAY_SIZE(smdk2440_devices));
smdk_machine_init();
}

s3c24xx_fb_set_platdata界说在plat-s3c24xx/devs.c中:

void__init s3c24xx_fb_set_platdata(structs3c2410fb_mach_info*pd)
{
structs3c2410fb_mach_info*npd;

npd=kmalloc(sizeof(*npd),GFP_KERNEL);
if(npd){
memcpy(npd,pd,sizeof(*npd));

//这儿便是将内核中界说的s3c2410fb_mach_info结构体数据保存到LCD渠道数据中,所以在写驱动的时分就能够直接在渠道数据中获取s3c2410fb_mach_info结构体的数据(即LCD各种参数信息)进行操作
s3c_device_lcd.dev.platform_data=npd;
}else{
printk(KERN_ERR“no memory for LCD platform data\n”);
}
}

这儿再讲一个小常识:不知我们有没有留心,在渠道设备驱动中,platform_data能够保存各自渠道设备实例的数据,但这些数据的类型都是不同的,为什么都能够保存?这就要看看platform_data的界说,界说在/linux/device.h中,void *platform_data是一个void类型的指针,在Linux中void可保存任何数据类型。

声明:本文内容来自网络转载或用户投稿,文章版权归原作者和原出处所有。文中观点,不代表本站立场。若有侵权请联系本站删除(kf@86ic.com)https://www.86ic.net/dianyuan/kaiguan/264536.html

为您推荐

联系我们

联系我们

在线咨询: QQ交谈

邮箱: kf@86ic.com

关注微信
微信扫一扫关注我们

微信扫一扫关注我们

返回顶部