Chinaunix首页 | 论坛 | 博客
  • 博客访问: 98121
  • 博文数量: 38
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 384
  • 用 户 组: 普通用户
  • 注册时间: 2014-04-06 16:52
文章分类

全部博文(38)

文章存档

2014年(38)

我的朋友

分类: 嵌入式

2014-05-07 22:28:40

1.LCD内核框架
    所谓的驱动程序就是调用LINUX内核提供的函数来操作硬件,如LCD,IIC 设备
    内核自带的LCD驱动程序drivers/video/samsung/s3cfb.c
    这个驱动程序通过提供最底层的操作来被核心层调用,这个核心层提供了应用程序所需open/close ,read/write
    这个核心层到底是啥呢?
    drivers/video/fbmem.c , 它提供了对LCD的所有抽象操作

2.硬件接线及原理

    VDD_LCD:LCD电源引脚
    VD0-VD23: video data引脚,lcd控制器发的数据在这几根线上
    LVDEN: video data 使能引脚
    LHSYNC: 能发出HSYNC信号,用于切换到下一行扫描
    LVSYNC: 能发出VSYNC信号,用于返回到最开始行的扫描
    LVCLK:控制器发给lcd的时钟引脚,根据lcd手册设置

3.几个重要的结构体
    /include/uapi/linux/fb.h中的

点击(此处)折叠或打开

  1. struct fb_var_screeninfo {
  2.                 __u32 xres; // X物理坐标
  3.                 __u32 yres; // Y物理坐标
  4.                 __u32 xres_virtual; // X虚拟坐标
  5.                 __u32 yres_virtual; // Y虚拟坐标
  6.                 __u32 xoffset;     // X虚拟坐标与物理坐标差距
  7.                 __u32 yoffset;    // Y虚拟坐标与物理坐标差距
  8.                 __u32 bits_per_pixel; // 每一个像素占几位
  9.                 __u32 grayscale; //灰度模式
  10.                 struct fb_bitfield red; //绿色位域
  11.                 struct fb_bitfield green; //绿色位域
  12.                 struct fb_bitfield blue; //蓝色位域
  13.                 struct fb_bitfield transp; //透明色
  14.                 __u32 nonstd; // = 1 非标准,= 0 标准
  15.                 __u32 activate; // 活动模式
  16.                 __u32 height; //,物理尺寸/mm
  17.                 __u32 width; //,物理尺寸/mm
  18.                 __u32 accel_flags; // 加速标志
  19.                 __u32 pixclock; // 像素时钟
  20.                 __u32 left_margin; //左边框,驱动程序一般不设置
  21.                 __u32 right_margin; //右边框,驱动程序一般不设置
  22.                 __u32 upper_margin; // 上边框,驱动程序一般不设置
  23.                 __u32 lower_margin;//底边框,驱动程序一般不设置
  24.                 __u32 hsync_len; // 行同步信号长度
  25.                 __u32 vsync_len; // 垂直同步信号长度
  26.                 __u32 sync; /*see FB_SYNC_ */
  27.                 __u32 vmode; /* see FB_VMODE_ */
  28.                 __u32 reserved[6];// 预留字节
  29.             };

点击(此处)折叠或打开

  1. struct fb_bitfield {
  2.                 _u32 offset; // 位域偏移
  3.                 __u32 length; // 位域长度
  4.                 __u32 msb_right; //=1,最重要一位在右边
  5.                          //=0,最重要一位在左边
  6.                     };

4.驱动的编写步骤
        a.分配核心结构体fb_info
            使用函数s3c_lcd = framebuffer_alloc(0, NULL);
            每一个lcd驱动程序都要分配一个fb_info结构体,framebuffer_alloc的用法可以参考其他lcd驱动,第一个参数0表示只需要分配本结构体的长度,第二个NULL,表示无device结构
            返回值为fb_info结构体

        b.设置结构体成员
            设置s3c_lcd内的各个结构体成员的值,如:
            s3c_lcd->fix.smem_len = 480 *272*32/8; 
            s3c_lcd->fix.type = FB_TYPE_PACKED_PIXELS;
            s3c_lcd->fix.visual = FB_VISUAL_TRUECOLOR; 
            s3c_lcd->fix.line_length = 480 *4;

        c.硬件相关的操作
            配置GPIO用于LCD
            根据LCD手册设置LCD控制器,比如VCLK的频率等
            分配显存(framebuffer),并把地址告诉LCD控制器

        d.注册
            使用函数register_framebuffer(s3c_lcd);

5.LCD驱动的代码如下

点击(此处)折叠或打开

  1. #include <linux/module.h>
  2. #include <linux/kernel.h>
  3. #include <linux/errno.h>
  4. #include <linux/string.h>
  5. #include <linux/mm.h>
  6. #include <linux/tty.h>
  7. #include <linux/slab.h>
  8. #include <linux/delay.h>
  9. #include <linux/fb.h>
  10. #include <linux/init.h>
  11. #include <linux/dma-mapping.h>
  12. #include <linux/interrupt.h>
  13. #include <linux/workqueue.h>
  14. #include <linux/wait.h>
  15. #include <linux/platform_device.h>
  16. #include <linux/clk.h>

  17. #include <asm/io.h>
  18. #include <asm/uaccess.h>
  19. #include <asm/div64.h>

  20. #include <mach/regs-gpio.h>
  21. #include <mach/regs-fb.h>
  22. #include <mach/regs-lcd.h>

  23. static struct fb_info *s3c_lcd;
  24. static volatile struct lcd_regs* lcd_regs;
  25. static volatile unsigned long *mifpcon;
  26. static volatile unsigned long *spcon;
  27. static volatile unsigned long *gpfcon;
  28. static volatile unsigned long *gpfdat;
  29. static volatile unsigned long *gpicon;
  30. static volatile unsigned long *gpjcon;
  31. static u32 pseudo_palette[16];

  32. struct lcd_regs{
  33.      unsigned long vidcon0;
  34.      unsigned long vidcon1;
  35.      unsigned long vidcon2;
  36.      unsigned long reserver1;
  37.      unsigned long vidtcon0;
  38.      unsigned long vidtcon1;
  39.      unsigned long vidtcon2;
  40.      unsigned long reserver2;
  41.      unsigned long wincon0;
  42.      unsigned long wincon1;
  43.      unsigned long wincon2;
  44.      unsigned long wincon3;
  45.      unsigned long wincon4;
  46.      unsigned long reserver3[3];
  47.      unsigned long vidosd0a;
  48.      unsigned long vidosd0b;
  49.      unsigned long vidosd0c;
  50.      unsigned long reserver4;
  51.      unsigned long vidosd1a;
  52.      unsigned long vidosd1b;
  53.      unsigned long vidosd1c;
  54.      unsigned long vidosd1d;
  55.      unsigned long vidosd2a;
  56.      unsigned long vidosd2b;
  57.      unsigned long vidosd2c;
  58.      unsigned long vidosd2d;
  59.      unsigned long vidosd3a;
  60.      unsigned long vidosd3b;
  61.      unsigned long vidosd3c;
  62.      unsigned long reserver5;
  63.      unsigned long vidosd4a;
  64.      unsigned long vidosd4b;
  65.      unsigned long vidosd4c;
  66.      unsigned long reserver6[5];
  67.      unsigned long vidw00add0b0;
  68.      unsigned long vidw00add0b1;
  69.      unsigned long vidw01add0b0;
  70.      unsigned long vidw01add0b1;
  71.      unsigned long vidw02add0;
  72.      unsigned long reserver7;
  73.      unsigned long vidw03add0;
  74.      unsigned long reserver8;
  75.      unsigned long vidw04add0;
  76.      unsigned long reserver9[3];
  77.      unsigned long vidw00add1b0;
  78.      unsigned long vidw00add1b1;
  79. };

  80. /*调色板*/
  81. static int s3c_lcdfb_setcolreg(unsigned int regno, unsigned int red, unsigned int green, unsigned int blue, unsigned int transp, struct fb_info *info)
  82. {
  83.     u32 color = 0;
  84.     u32 *p;
  85.  
  86.     red = (red >> (16 - info->var.red.length)) << info->var.red.offset; // 0xAA0000
  87.     green = (green >> (16 - info->var.green.length)) << info->var.green.offset; // 0xCC00
  88.     blue = (blue >> (16 - info->var.blue.length)) << info->var.blue.offset; // 0xEE
  89.  
  90.     color = red | green | blue; // 0xAACCEE
  91.     p = info->pseudo_palette;
  92.     p[regno] = color;
  93.     
  94.     return 0;    
  95. }

  96. static struct fb_ops s3c_lcdfb_ops = {
  97.     .owner        = THIS_MODULE,
  98.     .fb_setcolreg    = s3c_lcdfb_setcolreg,
  99.     .fb_fillrect    = cfb_fillrect,/*以下三个在内核中,需要从内核提取模块*/
  100.     .fb_copyarea    = cfb_copyarea,
  101.     .fb_imageblit    = cfb_imageblit,
  102. };

  103. static int lcd_init(void)
  104. {
  105.     struct clk *clk;
  106.     int hclk;
  107.     int clkval;
  108.  
  109.     /*1.分配一个fb_info结构体*/
  110.     s3c_lcd = framebuffer_alloc(0, NULL);
  111.     
  112.     /*2.设置*/
  113.     //2.1设置固定的参数
  114.     strcpy(s3c_lcd->fix.id, "mylcd");
  115.     s3c_lcd->fix.smem_len = 480*272*32/8;//看lcd手册,4位
  116.     s3c_lcd->fix.type = FB_TYPE_PACKED_PIXELS;//在fb.h里的FB_TYPE里第一个
  117.     s3c_lcd->fix.visual = FB_VISUAL_TRUECOLOR;//TFT屏是真彩色
  118.     s3c_lcd->fix.line_length = 480*32/8 ;//一行480个像素,一个像素4个字节(32bit)
  119.     //2.2设置可变的参数
  120.     s3c_lcd->var.xres = 480;
  121.     s3c_lcd->var.yres = 272;
  122.     s3c_lcd->var.xres_virtual = 480;
  123.     s3c_lcd->var.yres_virtual = 272;
  124.     s3c_lcd->var.bits_per_pixel = 32;//每个像素用多少位
  125.     /*RGB:888*/
  126.     s3c_lcd->var.red.offset = 16;//从16位开始,长度为8
  127.     s3c_lcd->var.red.length = 8;
  128.     s3c_lcd->var.green.offset = 8;
  129.     s3c_lcd->var.green.length = 8;
  130.     s3c_lcd->var.blue.offset = 0;
  131.     s3c_lcd->var.blue.length = 8;
  132.     
  133.     s3c_lcd->var.activate = FB_ACTIVATE_NOW;// 看不懂就用默认值为0 的
  134.     
  135.     
  136.     //2.3设置操作函数
  137.     s3c_lcd->fbops = &s3c_lcdfb_ops;
  138.     
  139.     //2.4其他设置
  140.     s3c_lcd->pseudo_palette = pseudo_palette;/*调色板*/
  141.     //3c_lcd->screen_base = ;/*显存的虚拟地址*/
  142.     s3c_lcd->screen_size = 480*272*32/8;
  143.     
  144.     
  145.     /*3.硬件相关操作*/
  146.     //3.1配置GPIO用于LCD
  147.     gpicon = ioremap(0x7F008100, 4);
  148.     *gpicon = 0xAAAAAAAA;
  149.     gpjcon = ioremap(0x7F008120, 4);
  150.     *gpjcon = 0xAAAAAA;

  151.     gpfcon = ioremap(0x7F0080A0, 4);//把GPF14设置为CLKOUT功能
  152.     gpfdat = gpfcon + 1;
  153.     *gpfcon &= ~(3<<28);
  154.     *gpfcon |= (1<<28);
  155.     
  156.     mifpcon = ioremap(0x7410800C, 4);//参考6410芯片手册,When LCD Bypass mode is set (MIFPCON[3]=1) in sleep mode, GPISLPCON and GPIPUDSLP cannot control I port.
  157.     *mifpcon &= ~(1<<3);
  158.     spcon = ioremap(0x7F0081A0, 4);//参考6410芯片手册,SPCON: LCD_SEL[1:0] value @ 0x7F0081A0 must be set as ‘00’ to use Host I/F Style or as ‘01’ to use RGB I/F Style
  159.     *spcon &= ~(3);
  160.     *spcon |= 1;
  161.     
  162.     //3.2根据LCD手册设置LCD控制器,比如VCLK的频率等
  163.     lcd_regs = ioremap(0x77100000, sizeof(struct lcd_regs));
  164.     
  165.     clk = clk_get(NULL, "lcd");//struct clk *clk_get(struct device *dev, const char *id)
  166.     clk_enable(clk);
  167.  
  168.     clk = clk_get(NULL, "hclk");
  169.     hclk = clk_get_rate(clk);
  170.     clkval = hclk/9000000 - 1;
  171.     printk("hclk = %d, clkval = %d\n", hclk, clkval);
  172.     
  173.     lcd_regs->vidcon0 = (0<<29) | (0<<26) | (3<<23) | (3<<20) | (0<<17) | (clkval<<6) | (1<<4);
  174.     lcd_regs->vidcon1 = (0<<7) | (1<<6) | (1<<5) | (0<<4);/* 在vclk的下降沿获取数据, HSYNC高电平有效, VSYNC高电平有效 */
  175.     lcd_regs->vidtcon0 = (1<<16) | (1<<8) | (9<<0);//垂直
  176.     lcd_regs->vidtcon1 = (1<<16) | (1<<8) | (40<<0);//水平
  177.     lcd_regs->vidtcon2 = (271<<11) | (479<<0);
  178.     //配置窗口0
  179.     lcd_regs->wincon0 &= ~(0xf << 2);
  180.     lcd_regs->wincon0 |= (0xb<<2);
  181.     lcd_regs->vidosd0a = (0<<11) | (0<<0);
  182.     lcd_regs->vidosd0b = (479<<11) | (271<< 0);
  183.     lcd_regs->vidosd0c = 480*272;//窗口尺寸

  184.     
  185.     //3.3分配显存(framebuffer),并把地址告诉LCD控制器
  186.     s3c_lcd->screen_base = dma_alloc_writecombine(NULL, s3c_lcd->screen_size, (dma_addr_t *)&s3c_lcd->fix.smem_start, GFP_KERNEL);
  187.     lcd_regs->vidw00add0b0 = s3c_lcd->fix.smem_start;
  188.     lcd_regs->vidw00add1b0 = (s3c_lcd->fix.smem_start + s3c_lcd->fix.smem_len) & 0xffffff;
  189.     
  190.     //s3c_lcd->fix.smem_start = ;/*显存的物理地址*/
  191.     /*启动/使能LCD*/
  192.     *gpfdat |= (1<<14);
  193.     lcd_regs->vidcon0 |= (3);
  194.     lcd_regs->wincon0 |= (1<<0);
  195.     
  196.     /*4.注册*/
  197.     register_framebuffer(s3c_lcd);
  198.     return 0;
  199. }

  200. static void lcd_exit(void)
  201. {
  202.     struct clk *clk;
  203.     
  204.     unregister_framebuffer(s3c_lcd);
  205.     *gpfdat &= ~(1<<14);
  206.     lcd_regs->vidcon0 &= ~(1<<1);
  207.     lcd_regs->wincon0 &= ~(1<<0);

  208.     clk = clk_get(NULL, "lcd");
  209.     clk_disable(clk);
  210.     iounmap(lcd_regs);
  211.     iounmap(gpfcon);
  212.     iounmap(gpicon);
  213.     iounmap(gpjcon);
  214.     iounmap(mifpcon);
  215.     iounmap(spcon);
  216.     dma_free_writecombine(NULL, s3c_lcd->screen_size, s3c_lcd->screen_base, s3c_lcd->fix.smem_start);
  217.     framebuffer_release(s3c_lcd);
  218.     
  219. }

  220. module_init(lcd_init);
  221. module_exit(lcd_exit);

  222. MODULE_LICENSE("GPL");

6.测试
    首先要配置内核,去除内核中的LCD驱动,步骤如下
    Device Drivers  --->
        Graphics support  --->
            < > S3C Framebuffer Support (eXtended) 
            <*> Support for frame buffer devices  --->
                     Samsung S3C Framebuffer Support 
    重新make uImage,并make modules,把新内核烧到开发板。
    然后把/drivers/video中的cfbcopyarea.ko 、cfbfillrect.ko 、cfbimgblt.ko 加载到开发板

    测试1:
    
重启开发板,然后执行以下命令
        insmod cfbcopyarea.ko 
        insmod cfbfillrect.ko 
        insmod cfbimgblt.ko 
        insmod lcd.ko
        echo hello > /dev/tty1
        可以看到屏幕打印了文字hello
        cat lcd.ko > /dev/fb0 
        可以看到花屏了
        

    测试2:
    在开发板的文件系统的/etc/inittab文件中添加一行tty1::askfirst:-/bin/sh
    重启开发板,然后执行以下命令
        insmod cfbcopyarea.ko 
        insmod cfbfillrect.ko 
        insmod cfbimgblt.ko 
        insmod lcd.ko
    可以看到有文字在屏幕上显示,再装载我们之前做的输入子系统的按键驱动
        insmod button_input.ko
    就可以在开发板屏幕上看到shell命令行了


    
    


    


    

    
阅读(1169) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~