Linux论坛
直播中

Riaan

7年用户 99经验值
擅长:嵌入式技术 接口/总线/驱动
私信 关注
[经验]

看我如何写LCD驱动

本帖最后由 wytt 于 2017-9-6 23:52 编辑

        掌握LCD裸板编程和LCD的驱动对于一个嵌入式驱动工程师来说是十分重要以及必要的,在这段时间里,本人也学习了LCD方面的知识,在这里和大家分享交流一下对于LCD驱动学习过程中我自己的一些总结,希望大家能多多提出批评以及本帖的不足
        要写一个LCD驱动程序时我们需要具有对应开发板的原理图开发板CPU的芯片手册以及LCD液晶手册三样东西。
        一般我自己在写驱动程序的时候习惯先把入口函数、出口函数、以及写上声明
        module_init(XXX_init)module_exit(XXX_exit)

写LCD驱动程序的第一大部分:软件相关的设置
        在LCD驱动程序中,对fb_info结构体的填充可以算是整个驱动程序中的关键部分了。fb_info结构体的主要内容如下:
        struct fb_info
         {
                int node;
                int flags;
                struct fb_var_screeninfo var;            /* 可变参数结构体 */               
                struct fb_fix_screeninfo fix;               /* 固定参数结构体 */
                struct fb_monspecs monspecs;        /* LCD显示器标准 */
                struct work_struct queue;      
                struct fb_pixmap pixmap;      
                struct fb_pixmap sprite;   
                struct fb_cmap cmap;            
                struct list_head modelist;      
                struct fb_videomode *mode;      

                struct fb_ops *fbops;                    /*对底层硬件操作的函数*/
                struct device *device;               
                struct device *dev;               
                int class_flag;      

        一:首先我们需要在初始化函数中为fb_info结构体分配空间,使用的函数是:
        s3c_lcd = framebuffer_alloc(0, NULL);
       framebuffer_alloc函数原型如下:
      struct fb_info *framebuffer_alloc(size_t size, struct device *dev)
        {

        }
        在init函数中,还有一个重要的任务,那就是需要通过比较id来确定是否支持我们的LCD,比较函数如下:
       strcpy (s3c_lcd->fix.id, "mylcd");

        二:接下来一步我们可以先填充fb_info结构体中的 fb_var_screeninfo var,也就是可变参数结构体
        /*这是我使用的LCD对应的数据参数,用来举例*/
        s3c_lcd -> var.xres                   = 240;       /*xres和yres是所使用的LCD液晶屏的屏幕大小*/
        s3c_lcd -> var.yres                   = 320;
        s3c_lcd -> var.xres_virtual       = 240;       /*这两个是我们所使用的LCD液晶屏虚拟屏幕的大小*/
        s3c_lcd -> var.yres_virtual       = 320;       /*我们平时看见的液晶屏黑边大小就跟这个有关*/
        s3c_lcd -> var.bits_per_pixel   = 16;         /*这个是我们所设置的每个像素所占的位数,这里是16位每像素*/


        /*RGB   5:6:5 这个是在LCDCON5寄存器中设置的模式,在后面设置LCD控制器时会进一步介绍*/
        /*.offset表示的是数据起始位,.length表示的是数据大小长度*/
        s3c_lcd -> var.blue.offset        = 0;
        s3c_lcd -> var.blue.length       = 5;

        s3c_lcd -> var.green.offset     = 5;
        s3c_lcd -> var.green.length    = 6;
        
        s3c_lcd -> var.red.offset         = 11;
        s3c_lcd -> var.red.length        = 5;


        /*除了红绿蓝三个颜色之外还有一个透明色,但因为我们这里不需要,所以不用设置*/
        //s3c_lcd -> var.transp.length   


        /*对于一些看不懂的宏,我们一般选择默认,默认值一般都为0*/
        s3c_lcd -> var.activate            = FB_ACTIVATE_NOW;
        



        当然,fb_info中fb_var_screeninfo var结构体的参数肯定不止这些,但因为本帖是本着让大家能够初步了解和编写LCD驱动的目的,所以只列出了一些比较主要的参数设置。

       三:第三步我们对fb_info结构体中的 fb_fix_screeninfo fix 固定参数结构体来进行填充
        /*我所使用的液晶是240*320,每个像素占16位,所以总的大小就为240*320*16*/
       s3c_lcd ->fix.smem_len      = 240*320*16;
        s3c_lcd ->fix.type               = FB_TYPE_PACKED_PIXELS;
        s3c_lcd ->fix.visual             = FB_VISUAL_TRUECOLOR;        
        s3c_lcd ->fix.line_length     =240*2;       /*每一行240个像素,一个像素16位,也就是两个字节了*/

        到这里我们一些基本的LCD的参数就设置完毕了

        四:第四步的话我们需要进一步设置一些操作函数
        s3c_lcd -> fb_ops    =  &s3c_fb_ops;
        s3c_fb_ops结构体的内容如下:
        /*在fb_ops结构体中我们只加入一些基本的功能实现函数*/
        static struct fb_ops s3c_fb_ops = {
                .owner                   = THIS_MODULE,
                .fb_setcolreg        = s3c_lcd_setcolreg,     /*配置一个虚拟的调色板*/
                .fb_copyarea        = cfb_copyarea,          /*这三个函数为内核中自带的功能函数*/
                .fb_imageblit        = cfb_imageblit,
                .fb_mmap                = mc68x328fb_mmap,
        };

        
LCD驱动中的第二大部分:硬件相关的操作:
        这部分的重要就是参照所使用的LCD控制器的手册来进行设置
        为了方便,我们先把需要设置的LCD寄存器全部放入一个结构体中:
        struct lcd_regs {
                unsigned long        lcdcon1;
                unsigned long        lcdcon2;
                unsigned long        lcdcon3;
                unsigned long        lcdcon4;
                unsigned long        lcdcon5;
                unsigned long        lcdsaddr1;
                unsigned long        lcdsaddr2;
                unsigned long        lcdsaddr3;
                unsigned long        redlut;
                unsigned long        greenlut;
                unsigned long        bluelut;
                unsigned long        reserved[9];
                unsigned long        dithmode;
                unsigned long        tpal;
                unsigned long        lcdintpnd;
                unsigned long        lcdsrcpnd;
                unsigned long        lcdintmsk;
                unsigned long        lpcsel;
        };


        一:硬件相关操作的第一步我们需要将LCD用到的I/O口配置成LCD管脚(在这里就需要用到开发板的原理图以及手册,下面是我所使用的jz2440开发板作为示例):
        gpbcon = ioremap(0x56000010, 8);
        gpbdat = gpbcon + 1;

        gpccon = ioremap(0x56000020, 4);
        gpdcon = ioremap(0x56000030, 4);
        gpgcon = ioremap(0x56000060, 4);

        *gpccon = 0xaaaaaaaa;
        *gpdcon = 0xaaaaaaaa;
        *gpbcon &= ~(3);
        *gpbcon |= 1;
        
        *gpbdat &= 0;

        *gpgcon &= ~(3<<8);
        *gpgcon |= (3<<8);


        二:第二步根据LCD 手册设置LCD控制器,比如VCLK 的频率等(这个时候需要用到对应的LCD手册了):
        lcd_regs = ioremap(0x4D000000, sizeof(struct lcd_regs));
        lcd_regs->lcdcon1 = (4<<8) | (3<<5) | (0x0c<<1) ;
        lcd_regs->lcdcon2 = (3<<24) | (319<<14) | (1<<6) | (0<<0);
        lcd_regs->lcdcon3 = (16<<19) | (239<<8) | (10<<0);
        lcd_regs->lcdcon4 = (4<<0);
        lcd_regs->lcdcon5 = (1<<11) | (1<<9) | (1<<8) | (0<<1) | (1<<0);


        三:分配显存framebuffer, 并把地址告诉LCD 控制器
        s3c_lcd->screen_base = dma_alloc_writecombine(NULL, s3c_lcd ->fix.smem_len, &s3c_lcd->fix.smem_start, GFP_KERNEL)
        lcd_regs->lcdsaddr1 = (s3c_lcd->fix.smem_start >> 1) &  ~(3<<30);
        lcd_regs->lcdsaddr2 = (s3c_lcd->fix.smem_start + s3c_lcd ->fix.smem_len) >> 1 &  ~0x1fffff;
        lcd_regs->lcdsaddr3 = (240*16/16);

        lcd_regs->lcdcon1 |= (1<<0);
        lcd_regs->lcdcon5 |= (1<<3);
        *gpbdat |= 1;

        
        硬件方面的操作主要是个人硬件知识的理解能力以及查看芯片手册的能力,也没什么可以详细说明的。
        在最后我们需要注册我们的fb_info结构体
        register_framebuffer(s3c_lcd);


        一个简单的基本的LCD驱动程序的框架就如上所总结,本人也是正在学习中,也存在许多许多不足,在最后感谢各位的观看,希望各位能提出你们宝贵的建议以及经验,谢谢!!!





回帖(2)

Riaan

2017-9-11 11:43:23
希望对大家的学习有帮助。
举报

伍小伟

2018-7-17 10:50:14
好文章,从头到尾看了两次。不过,我有些地方还是不怎么懂
举报

更多回帖

发帖
×
20
完善资料,
赚取积分