一、帧缓冲机制
在谈到lcd驱动时,先谈谈,帧缓冲机制,为什么呢?因为Linux系统是工作在保护模式下,所以用户态进程无法像DOS那要使用显卡BIOS里提供的中断调用来实现直接写屏蔽,Framebuffer是出现在linux 2.2及以后的一种驱动程序接口,它是一项重要技术,基本上都是用它来实现图形界面的。
下面是LCD驱动程序与应用程序以及帧缓冲机制的关系
应用程序要操作LCD,就操作设备节点(/dev/fb0由帧缓冲创建),fbmem.c是提供应用程序的操作接口,fbmem.c本身并不实现这些功能,这就需要下一层接口实现,就是XXXfb.c要实现的与lcd底层硬件相关的操作接口。具体可以参考s3c2410fb.c。
帧缓冲技术是与LCD驱动混在一起从而形成帧缓冲LCD设备驱动程序,主要是由下面几个重要数据结构起关联。
1、Struct fb_info
Struct fb_info这结构记录了帧缓冲设备的全部信息,包括设备的设置参数、状态、以及对底层硬件操作的函数指针。下面具体分析一下。
- struct fb_info {
- int node;
- int flags;
- struct fb_var_screeninfo var; /*LCD可变参数结构体 */
- struct fb_fix_screeninfo fix; /* LCD固定参数结构体 */
- struct fb_monspecs monspecs; /* LCD显示器标准 */
- struct work_struct queue; /* 帧缓冲事件队列 */
- struct fb_pixmap pixmap; /*图像硬件 mapper */
- struct fb_pixmap sprite; /* 光标硬件mapper */
- struct fb_cmap cmap; /*当前颜色表 */
- struct list_head modelist; /* mode list */
- struct fb_videomode *mode; /* 当前的显示模式 */
- #ifdef CONFIG_FB_BACKLIGHT
- struct backlight_device *bl_dev;/*对应背光设备*/
- /* Backlight level curve */
- struct mutex bl_curve_mutex;
- u8 bl_curve[FB_BACKLIGHT_LEVELS];/*背光调整*/
- #endif
- 。。。。。。。。
- struct fb_ops *fbops;/*对底层硬件操作的函数指针*/
- ……
- struct device *dev; /* FB设备 */
- ……
- #ifdef CONFIG_FB_TILEBLITTING
- struct fb_tile_ops *tileops; /*图块 Blitting */
- #endif
- char __iomem *screen_base; /* 虚拟基地址 */
- unsigned long screen_size; /* LCD IO 映射的虚拟内存大小*/
- void *pseudo_palette; /*伪16色颜色表 */
- #define FBINFO_STATE_RUNNING 0
- #define FBINFO_STATE_SUSPENDED 1
- u32 state; /* LCD挂起或者恢复的状态*/
- void *fbcon_par; /* fbcon use-only private area */
- /* From here on everything is device dependent */
- void *par;
- };
其中比较重要的就是struct fb_var_screeninfo var;struct fb_fix_screeninfo fix;
跟struct fb_ops *fbops;
下面各自分析一下
2、struct fb_var_screeninfo
struct fb_var_screeninfo 主要是记录用户可以修改的控制器可变参数
- struct fb_var_screeninfo {
- __u32 xres; /* 可见屏幕一行有多少个像素点*/
- __u32 yres; /*可见屏幕一列有多少个像素点*/
- __u32 xres_virtual; /*虚拟屏幕一列有多少个像素点*/
- __u32 yres_virtual; /*虚拟屏幕一列有多少个像素点*/
- __u32 xoffset; /* 虚拟到可见屏幕之间的行偏移*/
- __u32 yoffset; /*虚拟到可见屏幕之间的列偏移 */
- __u32 bits_per_pixel; /* 每个像素由多少位组成即BPP*/
- __u32 grayscale; /* != 0 Graylevels instead of colors */
- /*fb缓存的RGB位域*/
- struct fb_bitfield red; /* bitfield in fb mem if true color, */
- struct fb_bitfield green; /* else only length is significant */
- struct fb_bitfield blue;
- struct fb_bitfield transp; /* transparency */
- __u32 nonstd; /* != 0 Non standard pixel format */
- __u32 activate; /* see FB_ACTIVATE_* */
- __u32 height; /*高度mm*/
- __u32 width; /* 宽度 mm*/
- __u32 accel_flags; /* (OBSOLETE) see fb_info.flags */
- /*时间选择:除了像素时钟外,所有的值都以像素时钟为单位*/
- /* Timing: All values in pixclocks(像素时钟), except pixclock (of course) */
- __u32 pixclock; /* 像素时钟(pico seconds皮秒) */
- __u32 left_margin; /* time from sync to picture行切换,从同步到绘图之间的延迟 */
- __u32 right_margin; /* time from picture to sync行切换,从绘图到同步延迟 */
- __u32 upper_margin; /* time from sync to picture 帧切换,从同步到绘图之间的延迟*/
- __u32 lower_margin; /*帧切换,从绘图到同步之间的延迟*/
- __u32 hsync_len; /* length of horizontal sync水平同步的长度*/
- __u32 vsync_len; /* length of vertical sync 垂直同步的长度*/
- __u32 sync; /* see FB_SYNC_* */
- __u32 vmode; /* see FB_VMODE_* */
- __u32 rotate; /* angle we rotate counter clockwise */
- __u32 reserved[5]; /* Reserved for future compatibility */
- };
3、struct fb_fix_screeninfo
而struct fb_fix_screeninfo fix;就是固定的控制器配置,比如屏幕缓冲区的物理地址和长度,定义如下:
- struct fb_fix_screeninfo {
- char id[16]; /* identification string eg "TT Builtin" */
- unsigned long smem_start; /* Start of frame buffer mem帧缓冲缓存的开始地址 */
- /* (physical address) 物理地址*/
- __u32 smem_len; /* Length of frame buffer mem 缓冲的长度*/
- __u32 type; /* see FB_TYPE_* */
- __u32 type_aux; /* Interleave for interleaved Planes */
- __u32 visual; /* see FB_VISUAL_* */
- __u16 xpanstep; /* 没硬件panning就置0zero if no hardware panning */
- __u16 ypanstep; /*没硬件panning就置0 zero if no hardware panning */
- __u16 ywrapstep; /*没硬件ywrap就置0 zero if no hardware ywrap */
- __u32 line_length; /*一行的字节数 length of a line in bytes */
- unsigned long mmio_start; /*内存映射的开始地址 Start of Memory Mapped I/O */
- /* (physical address) */
- __u32 mmio_len; /*内存映射的长度 Length of Memory Mapped I/O */
- __u32 accel; /* Indicate to driver which */
- /* specific chip/card we have */
- __u16 reserved[3]; /* Reserved for future compatibility */
- };
4、struct fb_ops
struct fb_ops,帧缓冲操作,关联硬件跟应用程序。
- /*
- * Frame buffer operations
- *
- * LOCKING NOTE: those functions must _ALL_ be called with the console
- * semaphore held, this is the only suitable locking mechanism we have
- * in 2.6. Some may be called at interrupt time at this point though.
- */
- struct fb_ops {
- /* open/release and usage marking */
- struct module *owner;
- 。。。。。。
- /*检查可变参数并进行设置*/
- /* checks var and eventually tweaks it to something supported,
- * DO NOT MODIFY PAR */
- int (*fb_check_var)(struct fb_var_screeninfo *var, struct fb_info *info);
- //根据设置的值进行更新,根据info->var
- /* set the video mode according to info->var */
- int (*fb_set_par)(struct fb_info *info);
- //设置颜色寄存器
- /* set color register */
- int (*fb_setcolreg)(unsigned regno, unsigned red, unsigned green,
- unsigned blue, unsigned transp, struct fb_info *info);
- /* set color registers in batch */
- int (*fb_setcmap)(struct fb_cmap *cmap, struct fb_info *info);
- //显示空白
- /* blank display */
- int (*fb_blank)(int blank, struct fb_info *info);
- //矩形填充
- /* pan display */
- int (*fb_pan_display)(struct fb_var_screeninfo *var, struct fb_info *info);
- /* Draws a rectangle */
- void (*fb_fillrect) (struct fb_info *info, const struct fb_fillrect *rect);
- /* Copy data from area to another */
- void (*fb_copyarea) (struct fb_info *info, const struct fb_copyarea *region);
- /* Draws a image to the display */
- void (*fb_imageblit) (struct fb_info *info, const struct fb_image *image);
- /* Draws cursor */
- int (*fb_cursor) (struct fb_info *info, struct fb_cursor *cursor);
- /* Rotates the display */
- void (*fb_rotate)(struct fb_info *info, int angle);
- /* wait for blit idle, optional */
- int (*fb_sync)(struct fb_info *info);
- /* perform fb specific ioctl (optional) */
- int (*fb_ioctl)(struct fb_info *info, unsigned int cmd,
- unsigned long arg);
- /* Handle 32bit compat ioctl (optional) */
- int (*fb_compat_ioctl)(struct fb_info *info, unsigned cmd,
- unsigned long arg);
- /* perform fb specific mmap */
- int (*fb_mmap)(struct fb_info *info, struct vm_area_struct *vma);
- /* save current hardware state */
- void (*fb_save_state)(struct fb_info *info);
- /* restore saved state */
- void (*fb_restore_state)(struct fb_info *info);
- /* get capability given var */
- void (*fb_get_caps)(struct fb_info *info, struct fb_blit_caps *caps,
- struct fb_var_screeninfo *var);
- };
5、帧缓冲设备以及设备资源
LCD帧缓冲设备在Linux里是作为一个平台设备,在内核arch/arm/plat-s3c24xx/devs.c中定义LCD相关平台设备如下:
- /* LCD Controller */
- static struct resource s3c_lcd_resource[] = {
- [0] = {
- .start = S3C24XX_PA_LCD,//寄存器的开始地址
- .end = S3C24XX_PA_LCD + S3C24XX_SZ_LCD - 1,//长度
- .flags = IORESOURCE_MEM,
- },
- [1] = {
- .start = IRQ_LCD,
- .end = IRQ_LCD,
- .flags = IORESOURCE_IRQ,
- }
- };
- static u64 s3c_device_lcd_dmamask = 0xffffffffUL;
- struct platform_device s3c_device_lcd = {
- .name = "s3c2410-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);
这里导出s3c_device_lcd是为了在arch/asm/mach-s3c2410/mach-smdk2410.c里的smdk2410_devices[](或者其它smdk2440_devices[])中添加到平台设备列表中。
下面准备分析具体实例,但分析前还要了解LCD的特性以及读写时序。
实在不如别人做得漂亮,做得详细,大家还是去看原文吧,我这里就不接了。
二、LCD的硬件知识
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驱动器;
……
在这里我加上《S3c2410 LCD驱动学习心得》因为这里面分析如何确定驱动里的lcd配置参数写得很明白
S3C2410实验箱上的LCD是一款3.5寸TFT真彩LCD屏,分辩率为240*320,下图为该屏的时序要求。
图1.3
通过对比图1.2和图1.3,我们不难看出:
VSPW+1=2 -> VSPW=1
VBPD+1=2 -> VBPD=1
LINVAL+1=320-> LINVAL=319
VFPD+1=3 -> VFPD=2
HSPW+1=4 -> HSPW=3
HBPD+1=7 -> HBPW=6
HOZVAL+1=240-> HOZVAL=239
HFPD+1=31 -> HFPD=30
以上各参数,除了LINVAL和HOZVAL直接和屏的分辩率有关,其它的参数在实际操作过程中应以上面的为参考,不应偏差太多。
1.3 LCD控制器主要寄存器功能详解
图1.4
LINECNT :当前行扫描计数器值,标明当前扫描到了多少行。
CLKVAL :决定VCLK的分频比。LCD控制器输出的VCLK是直接由系统总线(AHB)的工作频率HCLK直接分频得到的。做为240*320的TFT屏,应保证得出的VCLK在5~10MHz之间。
MMODE :VM信号的触发模式(仅对STN屏有效,对TFT屏无意义)。
PNRMODE :选择当前的显示模式,对于TFT屏而言,应选择[11],即TFT LCD panel。
BPPMODE :选择色彩模式,对于真彩显示而言,选择16bpp(64K色)即可满足要求。
ENVID :使能LCD信号输出。
图1.5
VBPD , LINEVAL , VFPD , VSPW 的各项含义已经在前面的时序图中得到体现。
图1.6
HBPD , HOZVAL , HFPD 的各项含义已经在前面的时序图中得到体现。
图1.7
HSPW 的含义已经在前面的时序图中得到体现。
MVAL 只对 STN屏有效,对TFT屏无意义。
HSPW 的含义已经在前面的时序图中得到体现,这里不再赘述。
MVAL 只对 STN屏有效,对TFT屏无意义。
图1.8
VSTATUS :当前VSYNC信号扫描状态,指明当前VSYNC同步信号处于何种扫描阶段。
HSTATUS :当前HSYNC信号扫描状态,指明当前HSYNC同步信号处于何种扫描阶段。
BPP24BL :设定24bpp显示模式时,视频资料在显示缓冲区中的排列顺序(即低位有效还是高位有效)。对于16bpp的64K色显示模式,该设置位无意义。
FRM565 :对于16bpp显示模式,有2中形式,一种是RGB=5:5:5:1,另一种是5:6:5。后一种模式最为常用,它的含义是表示64K种色彩的16bit RGB资料中,红色(R)占了5bit,绿色(G)占了6bit,蓝色(B)占了5bit
INVVCLK , INVLINE , INVFRAME , INVVD :通过前面的时序图,我们知道,CPU的LCD控制器输出的时序默认是正脉冲,而LCD需要VSYNC(VFRAME)、VLINE(HSYNC)均为负脉冲,因此 INVLINE 和 INVFRAME 必须设为“1 ”,即选择反相输出。
INVVDEN , INVPWREN , INVLEND 的功能同前面的类似。
PWREN 为LCD电源使能控制。在CPU LCD控制器的输出信号中,有一个电源使能管脚LCD_PWREN,用来做为LCD屏电源的开关信号。
ENLEND 对普通的TFT屏无效,可以不考虑。
BSWP 和 HWSWP 为字节(Byte)或半字(Half-Word)交换使能。由于不同的GUI对FrameBuffer(显示缓冲区)的管理不同,必要时需要通过调整 BSWP 和 HWSWP 来适应GUI。
分析之后,我们能否把这LCD弄成模块?如果可以,又怎么弄?(这跟平台设备驱动有很大关系)
1、修改
linux-2.6.24/arch/arm/mach-s3c2410/mach-smdk2410.c
先加头文件
#include <asm/arch/fb.h>
再加入如下
- static struct s3c2410fb_display qt2410_lcd_cfg[] __initdata = {
- {
- /* Configuration for 640x480 SHARP LQ080V3DG01 */
- .lcdcon5 = S3C2410_LCDCON5_FRM565 |
- S3C2410_LCDCON5_INVVLINE |
- S3C2410_LCDCON5_INVVFRAME |
- S3C2410_LCDCON5_PWREN |
- S3C2410_LCDCON5_HWSWP,
- .type = S3C2410_LCDCON1_TFT,
- .width = 640,
- .height = 480,
- .pixclock = 40000, /* HCLK/4 */
- .xres = 640,
- .yres = 480,
- .bpp = 16,
- .left_margin = 44,
- .right_margin = 116,
- .hsync_len = 96,
- .upper_margin = 19,
- .lower_margin = 11,
- .vsync_len = 15,
- }
- };
- static struct s3c2410fb_mach_info qt2410_fb_info __initdata = {
- .displays = qt2410_lcd_cfg,
- .num_displays = 1,
- .default_display = 0,
- //设置引脚模式
- .gpccon= 0xaa8002a8,
- .gpccon_mask= 0xffc003fc,
- .gpcup= 0xf81e,
- .gpcup_mask= 0xf81e,
- .gpdcon= 0xaa80aaa0,
- .gpdcon_mask= 0xffc0fff0,
- .gpdup= 0xf8fc,
- .gpdup_mask= 0xf8fc,
- //.lpcsel = ((0xCE6) & ~7) | 1<<4,
- };
然后找到
static void __init smdk2410_init(void)函数
加入
s3c24xx_fb_set_platdata(&qt2410_fb_info);
OK,文件修改完毕
在make menuconfig 中
Device Drivers --->
Graphics support --->
Display device support --->
勾上S3C2410 LCD framebuffer support
想看开机的小企鹅就勾上
Bootup logo
想换一个启动图案的话,如下
(1)进入linux的kde图形界面,使用The GIMP 图像编辑器打开你想要的图像文件,依次选择图像->模式->索引颜色,将颜色改为224色;至于图片大小,不要大于你的显示器分辨率就好(我只试过80*80和320*240的大小),最后将文件保存为ppm格式(ASCii码),文件名为:logo_linux_clut224.ppm。
(2)将logo_linux_clut224.ppm拷贝到/drivers/video/logo文件夹下,替换原有的文件(记得备份啊,以防万一)。
(3)重新编译内核,tftp到开发板启动。
直接分析源码
Drivers/video/s3c2410fb.c
前面就是注册LCD平台驱动层,看到结构体static struct platform_driver s3c2410fb_driver,
里面就是这个平台设备的函数指针,通过调用platform_driver_register,如果系统里有LCD设备已经注册了(如果直接移植,就是在mach-smdk2410.c里加的内容注册的),就会调用s3c24xxfb_probe
分析s3c24xxfb_probe(假设是采用移植直接编译在内核里,而不是写成模块)
- static int __init s3c24xxfb_probe(struct platform_device *pdev,
- enum s3c_drv_type drv_type)
- {
- ……
- // 这语句是为了得到lcd平台设备数据,也就是lcd的配置(在mach-smdk2410.c定义)
- mach_info = pdev->dev.platform_data;
- ……
- //拿出lcd配置数据
- display = mach_info->displays + mach_info->default_display;
- //获中断
- irq = platform_get_irq(pdev, 0);
- ……
- //新创一个struct fb_info
- fbinfo = framebuffer_alloc(sizeof(struct s3c2410fb_info), &pdev->dev);
- if (!fbinfo)
- return -ENOMEM;
- //放在dev->driver_data里
- platform_set_drvdata(pdev, fbinfo);
- info = fbinfo->par;
- info->dev = &pdev->dev;
- info->drv_type = drv_type;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- ……
- //映射
- size = (res->end - res->start) + 1;
- info->mem = request_mem_region(res->start, size, pdev->name);
- ……
- info->io = ioremap(res->start, size);
- ……
- info->irq_base = info->io + ((drv_type == DRV_S3C2412) ? S3C2412_LCDINTBASE : S3C2410_LCDINTBASE);
- strcpy(fbinfo->fix.id, driver_name);
- /* Stop the video */
- lcdcon1 = readl(info->io + S3C2410_LCDCON1);
- writel(lcdcon1 & ~S3C2410_LCDCON1_ENVID, info->io + S3C2410_LCDCON1);
- fbinfo->fix.type = FB_TYPE_PACKED_PIXELS;
- fbinfo->fix.type_aux = 0;
- fbinfo->fix.xpanstep = 0;
- fbinfo->fix.ypanstep = 0;
- fbinfo->fix.ywrapstep = 0;
- fbinfo->fix.accel = FB_ACCEL_NONE;
- fbinfo->var.nonstd = 0;
- fbinfo->var.activate = FB_ACTIVATE_NOW;
- fbinfo->var.accel_flags = 0;
- fbinfo->var.vmode = FB_VMODE_NONINTERLACED;
- //主要的接口
- fbinfo->fbops = &s3c2410fb_ops;
- fbinfo->flags = FBINFO_FLAG_DEFAULT;
- fbinfo->pseudo_palette = &info->pseudo_pal;
- for (i = 0; i < 256; i++)
- info->palette_buffer[i] = PALETTE_BUFF_CLEAR;
- ret = request_irq(irq, s3c2410fb_irq, IRQF_DISABLED, pdev->name, info);
- ……
- //开时钟
- info->clk = clk_get(NULL, "lcd");
- ……
- clk_enable(info->clk);
- ……
- //将一开始加入的lcd设备配置的尺寸加入
- /* find maximum required memory size for display */
- for (i = 0; i < mach_info->num_displays; i++) {
- unsigned long smem_len = mach_info->displays[i].xres;
- smem_len *= mach_info->displays[i].yres;
- smem_len *= mach_info->displays[i].bpp;
- smem_len >>= 3;
- if (fbinfo->fix.smem_len < smem_len)
- fbinfo->fix.smem_len = smem_len;
- }
- /* Initialize video memory */
- //申请DRAM作为缓存,用于DMA操作
- ret = s3c2410fb_map_video_memory(fbinfo);
- ……
- fbinfo->var.xres = display->xres;
- fbinfo->var.yres = display->yres;
- fbinfo->var.bits_per_pixel = display->bpp;
- //下面这函数设置lcd寄存器
- s3c2410fb_init_registers(fbinfo);
- //这个是将lcd的配置写入,这里主要搞清几个结构体的关系
- //特别注意fb_info后面还加了一点空间用来保存info的,而info->dev = &pdev->dev,即可以直接调用到传递进来的lcd配置。
- s3c2410fb_check_var(&fbinfo->var, fbinfo);
- //注册framebuffer创建设备节点
- ret = register_framebuffer(fbinfo);
- ……
- /* create device files */
- ret = device_create_file(&pdev->dev, &dev_attr_debug);
- ……
- }
在注册framebuffer,即register_framebuffer里注册完后,假如你将这编译成模块,会发现最终程序死在fb_notifier_call_chain,一直等待,最后出现Segmentation fault。具体做法是将这语句注释掉,但如果你是把这驱动编译进内核,注释掉这步不能显示开机logo,原因在于,模块编译缺少依赖的内核模块,主要为cfbcopyarea.ko cfbfillrect.ko cfbimgblt.ko,这几个主要是给下面s3c2410fb_ops里的后三个,因为要 显示logo,在int fb_show_logo(struct fb_info *info)函数中,需要应用fb_ops结构体中的fb_imageblit域来在屏幕上画一幅图象,且是必须的,所以,fb_ops结构中fb_imageblit域不能为NULL,如果为NULL,那么这个函数就没有办法完成功能,也就是一个无用的函数,Fb_ops里面的内容就是完成应用程序系统调用。那register_framebuffer>>fb_notifier_call_chain》》》blocking_notifier_call_chain里的fb_notifier_list,这个内核链表又是哪里定义的?static BLOCKING_NOTIFIER_HEAD(fb_notifier_list);慢慢看就知道定义成什么样子,那接上面的函数深入,到__blocking_notifier_call_chain》》notifier_call_chain,最终会调用nb->notifier_call(nb, val, v);,那什么时候加入到这内核链表?
加的地方有两个,一个是fbcon.c一个是backlight.c,fbcon.c里的是fb_register_client(&fbcon_event_notifier);,backlight.c里的是info->fb_notif
其中
- static struct notifier_block fbcon_event_notifier = {
- .notifier_call = fbcon_event_notify,
- };
fbcon_event_notify,这函数就根据不同的标志做不同的处理,再跟踪到较里面就发现,原来根据 是不是编译成内核模块来执行不同的东西,如果是编译进内核,就会显示logo了。大概完了。
现在更专业的方法是什么?当然是把那三个内核模块都加载进来
- static struct fb_ops s3c2410fb_ops = {
- .owner = THIS_MODULE,
- .fb_check_var = s3c2410fb_check_var,
- .fb_set_par = s3c2410fb_set_par,
- .fb_blank = s3c2410fb_blank,
- .fb_setcolreg = s3c2410fb_setcolreg,
- .fb_fillrect = cfb_fillrect,
- .fb_copyarea = cfb_copyarea,
- .fb_imageblit = cfb_imageblit,
- };
这里有一个关于pixclock像素时钟计算问题,它是怎么计算得来的?
其实它就是VCLK的另一种表述,VCLK是每秒多少像素,耐pixclock是每像素多少微微秒,1Hz = 10^12picoseconds,举个例子,当pixclock = 80000(pic) ,VCLK=1/(80000*10^12)/10^6 = 12.5MHz
由于LCD时钟是使用AHB分频得到的HCLK时钟再分频得来的,pixclock确定之后,怎么确定分频值呢?在函数s3c2410fb_set_par》》》》s3c2410fb_activate_var》》》》》s3c2410fb_calc_pixclk就是计算分频值也就是CLKAL的。还有一个提一下,就是在s3c2410fb_calc_pixclk里有一个unsigned long clk = clk_get_rate(fbi->clk);,其实它是得到HCLK的,具体是在clk_get(NULL,”lcd”)时得到的。
应用层操作lcd
采用fb_open(),fb_mmap(),fb_write()等
以华清的例子为例分析
具体参考《嵌入式linux_Framebuffer驱动开发》,复制不过来,这里我在网上找了一个例子。
1. 显存大小计算:xres * yres * bits_per_pixel/8 (BYTES)
2.编程流程:
(1)打开设备 open("/dev/fb0",O_RDWR);调用fb_open()
(2) 获取framebuffer设备信息.ioctl(int fb,FBIOGET_FSCREENINFO,&finfo);调用fb_ioctl
ioctl函数是实现对设备的信息获取和设定,第一个参数为文件描述符,第二个参数为具体设备的参数,对于framebuffer,参数在linux/fb.h中定义的。
#define FBIOGET_VSCREENINFO 0x4600 //获取设备无关的数据信息fb_var_screeninfo
#define FBIOPUT_VSCREENINFO 0x4601 //设定设备无关的数据信息
#define FBIOGET_FSCREENINFO 0x4602 //获取设备无关的常值信息fb_fix_screeninfo
#define FBIOGETCMAP 0x4604 //获取设备无关颜色表信息
#define FBIOPUTCMAP 0x4605 //设定设备无关颜色表信息
#define FBIOPAN_DISPLAY 0x4606
#define FBIO_CURSOR _IOWR('F', 0x08, struct fb_cursor)
第三个参数是存放信息的结构体或者缓冲区
(3)内存映射 mmap函数。头文件:sys/mman.h .常用用法:mmap(0,screensize,PROT_RD |PROT_WR,MAP_SHARED,int fb,0)返回映射的首地址。
3。实例
程序实现在lcd 上全屏写 blue 色
- #include <unistd.h>
- #include <stdio.h>
- #include <fcntl.h>
- #include <linux/fb.h>
- #include <sys/mman.h>
- #include <stdlib.h>
- int main()
- {
- int fbfd = 0;
- struct fb_var_screeninfo vinfo;
- struct fb_fix_screeninfo finfo;
- long int screensize = 0;
- char *fbp = 0;
- int x = 0, y = 0;
- long int location = 0;
- int sav=0;
- /* open device*/
- fbfd = open("/dev/fb0", O_RDWR);
- if (!fbfd) {
- printf("Error: cannot open framebuffer device./n");
- exit(1);
- }
- printf("The framebuffer device was opened successfully./n");
- /* Get fixed screen information */
- if (ioctl(fbfd, FBIOGET_FSCREENINFO, &finfo)) {
- printf("Error reading fixed information./n");
- exit(2);
- }
- /* Get variable screen information */
- if (ioctl(fbfd, FBIOGET_VSCREENINFO, &vinfo)) {
- printf("Error reading variable information./n");
- exit(3);
- }
- /* show these information*/
- printf("vinfo.xres=%d/n",vinfo.xres);
- printf("vinfo.yres=%d/n",vinfo.yres);
- printf("vinfo.bits_per_bits=%d/n",vinfo.bits_per_pixel);
- printf("vinfo.xoffset=%d/n",vinfo.xoffset);
- printf("vinfo.yoffset=%d/n",vinfo.yoffset);
- printf("finfo.line_length=%d/n",finfo.line_length);
- /* Figure out the size of the screen in bytes */
- screensize = vinfo.xres * vinfo.yres * vinfo.bits_per_pixel / 8;
- 总共有多少像素,一像素多少位,转化为字节
- /* Map the device to memory */
- fbp = (char *)mmap(0, screensize, PROT_READ | PROT_WRITE, MAP_SHARED,
- fbfd, 0);
- if ((int)fbp == -1) { printf("Error: failed to map framebuffer device to memory./n"); exit(4);
- }
- printf("The framebuffer device was mapped to memory successfully./n");
- memset(fbp,0,screensize);
- /* Where we are going to put the pixel */
- for(x=0;x<vinfo.xres;x )
- for(y=0;y<vinfo.yres;y )
- {
- location = (x+vinfo.xoffset) * (vinfo.bits_per_pixel/8)
- (y+vinfo.yoffset) * finfo.line_length;
- *(fbp location) = 0xff; /* blue */
- *(fbp location 1) = 0x00;
- }
- munmap(fbp, screensize); /* release the memory */
- close(fbfd);
- return 0;
- }