• 5 linux lcd驱动程序编写


    1 lcd硬件操作原理

    Lcd显示的过程如下:

    1. 从显存中输出显示颜色的数据,在屏幕左上角的第一个点开始显示,每间隔一个像素时钟VCLK,向右移动一个点,当移到最右边时,会根据水平同步信号HSYNC跳到下一行的最左边;
    2. 又重复步骤1的操作,直到显示到右下角最后一个点为止,这时根据垂直同步信号YSYNC,又跳回到左上角第一个点开始下一帧图片的显示。

    2 编写驱动

    在上一章节结尾已经理出了lcd驱动程序的大致流程。首先来实现入口函数lcd_init函数。

    1. 分配一个fb_info空间;
    2. 对fb_info指向的结构体进行配置;
      1. 设置可变参数;
      2. 设置固定参数;
      3. 设置fops;
    3. 设置其他参数。
    4. 硬件相关的配置;
      1. 设置lcd相关的引脚;
      2. 设置lcd控制器的寄存器;
      3. 设置显存,并将显存的地址通知到lcd控制器;
      4. 开启lcd,lcd控制器以及背光。
    5. 注册framebuffer。

    出口函数lcd_exit主要实现对资源的释放,具体操作如下:

    1. 卸载framebuffer;
    2. 关闭lcd控制器,背光;
    3. 释放显存空间;
    4. 解除所有io资源的映射;
    5. 释放申请的fb_info空间。

    详细的代码如下所示。

    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/slab.h>  
    7. #include <linux/delay.h>  
    8. #include <linux/fb.h>  
    9. #include <linux/init.h>  
    10. #include <linux/dma-mapping.h>  
    11. #include <linux/interrupt.h>  
    12. #include <linux/workqueue.h>  
    13. #include <linux/wait.h>  
    14. #include <linux/platform_device.h>  
    15. #include <linux/clk.h>  
    16.     
    17. #include <asm/io.h>  
    18. #include <asm/uaccess.h>  
    19. #include <asm/div64.h>  
    20.     
    21. #include <asm/mach/map.h>  
    22. #include <asm/arch/regs-lcd.h>  
    23. #include <asm/arch/regs-gpio.h>  
    24. #include <asm/arch/fb.h>  
    25.     
    26. static volatile unsigned long *gpbcon;  
    27. static volatile unsigned long *gpbdat;  
    28. static volatile unsigned long *gpccon;  
    29. static volatile unsigned long *gpdcon;  
    30. static volatile unsigned long *gpgcon;  
    31. static u32 pseudo_pal[16];  
    32.     
    33. struct lcd_regs{  
    34.     unsigned long lcdcon1;  
    35.     unsigned long lcdcon2;  
    36.     unsigned long lcdcon3;  
    37.     unsigned long lcdcon4;  
    38.     unsigned long lcdcon5;  
    39.     unsigned long lcdsaddr1;  
    40.     unsigned long lcdsaddr2;  
    41.     unsigned long lcdsaddr3;  
    42.     unsigned long redlut;  
    43.     unsigned long greenlut;  
    44.     unsigned long bluelut;  
    45.     unsigned long reserved[9];  
    46.     unsigned long dithmode;  
    47.     unsigned long tpal;  
    48.     unsigned long lcdintpnd;  
    49.     unsigned long lcdsrcpnd;  
    50.     unsigned long lcdintmask;  
    51.     unsigned long tconsel;  
    52. };  
    53. static volatile struct lcd_regs *lcdregs;  
    54.         
    55. static struct fb_info      *s3cxx_lcd;  
    56. static int s3cfb_setcolreg(unsigned regno,  
    57.                    unsigned red, unsigned green, unsigned blue,  
    58.                    unsigned transp, struct fb_info *info);  
    59.     
    60. static struct fb_ops s3c_lcdfb_ops = {  
    61.     .owner      = THIS_MODULE,  
    62.     .fb_setcolreg   = s3cfb_setcolreg,  
    63.     .fb_fillrect    = cfb_fillrect,  
    64.     .fb_copyarea    = cfb_copyarea,  
    65.     .fb_imageblit   = cfb_imageblit,  
    66. };  
    67.     
    68. static inline unsigned int chan_to_field(unsigned int chan, struct fb_bitfield *bf)  
    69. {  
    70.     chan &= 0xffff;  
    71.     chan >>= 16 - bf->length;  
    72.     return chan << bf->offset;  
    73. }  
    74.     
    75. static int s3cfb_setcolreg(unsigned regno,  
    76.                    unsigned red, unsigned green, unsigned blue,  
    77.                    unsigned transp, struct fb_info *info)  
    78. {  
    79.     unsigned int val;  
    80.         
    81.     if(regno > 16)  
    82.         return 1;  
    83.     
    84.     /* red green blue三原色构造出val */  
    85.     val  = chan_to_field(red,   &info->var.red);  
    86.     val |= chan_to_field(green, &info->var.green);  
    87.     val |= chan_to_field(blue,  &info->var.blue);  
    88.     
    89.     pseudo_pal[regno] = val;  
    90.     return 0;     
    91. }  
    92.     
    93. static int lcd_init(void)  
    94. {  
    95.     /* 1、分配一个fb_info空间 */  
    96.     s3cxx_lcd = framebuffer_alloc(0, NULL);  
    97.         
    98.     /* 2、配置 */  
    99.     /* 2.1、设置可变参数 */  
    100.     s3cxx_lcd->var.xres          = 480;  
    101.     s3cxx_lcd->var.yres          = 272;  
    102.     s3cxx_lcd->var.xres_virtual  = 480;  
    103.     s3cxx_lcd->var.yres_virtual  = 272;  
    104.     s3cxx_lcd->var.bits_per_pixel    = 16;  
    105.     /* RGB:565 */  
    106.     s3cxx_lcd->var.red.offset        = 11;  
    107.     s3cxx_lcd->var.red.length        = 5;  
    108.     s3cxx_lcd->var.green.offset      = 5;  
    109.     s3cxx_lcd->var.green.length      = 6;  
    110.     s3cxx_lcd->var.blue.offset       = 0;  
    111.     s3cxx_lcd->var.blue.length       = 5;      
    112.     s3cxx_lcd->var.activate          = FB_ACTIVATE_NOW;  
    113.             
    114.     /* 2.2、设置固定参数 */  
    115.     strcpy(s3cxx_lcd->fix.id, "mylcd");  
    116.     s3cxx_lcd->fix.smem_len      = 480*272*16/8;  
    117.     s3cxx_lcd->fix.type          = FB_TYPE_PACKED_PIXELS;  
    118.     s3cxx_lcd->fix.visual            = FB_VISUAL_TRUECOLOR;  
    119.     s3cxx_lcd->fix.line_length       = 480*2;  
    120.         
    121.     /* 2.3、设置fbops */  
    122.     s3cxx_lcd->fbops             = &s3c_lcdfb_ops;  
    123.     /* 2.4、设置其他参数 */  
    124.     s3cxx_lcd->pseudo_palette        = pseudo_pal;  
    125.     s3cxx_lcd->screen_size           = 480*272*16/8;  
    126.         
    127.     /* 3、硬件相关的操作 */  
    128.     /* 3.1、设置lcd的引脚 */  
    129.     gpbcon = ioremap(0x56000010, 8);  
    130.     gpbdat = gpbcon + 1;  
    131.     gpccon = ioremap(0x56000020, 4);  
    132.     gpdcon = ioremap(0x56000030, 4);  
    133.     gpgcon = ioremap(0x56000060, 4);  
    134.     
    135.     /* GPIOc管脚用于VD[7:0]LCD_LPCREVBLCD_LPCREV LCD_LPCOEVMVFRAMEVLINEVCLKLEND */  
    136.     *gpccon = 0Xaaaaaaaa;  
    137.     /*GPIOd管脚用于VD[23:8] */  
    138.     *gpdcon = 0Xaaaaaaaa;  
    139.     /* GPIOB0管脚设为输出引脚 */  
    140.     *gpbcon &= ~0x03;  
    141.     *gpbcon |= 0x01;  
    142.     *gpbdat &= ~0x01;  
    143.     /*gpiog4管脚用于LCD_PWRDN */  
    144.     *gpgcon |= (3<<8);  
    145.         
    146.     /* 3.2、设置lcd控制器的寄存器 */  
    147.     lcdregs = ioremap(0X4D000000, sizeof(struct lcd_regs));  
    148.     /* lcdcon1 
    149.      * bit[17:8] vclk = hclk /[(clkval + 1)*2] 
    150.               10000000Hz(100nS) = 100000000/[(clkval + 1)*2] 
    151.               clkval = 4 
    152.      * bit[6:5]  11 = TFT LCD panel 
    153.      * bit[4:1]  1100 = 16 bpp for TFT 
    154.      * bit[0]    0 = Disable the video output and the LCD control signal 
    155.      */  
    156.     lcdregs->lcdcon1 = (4 << 8)| (3 << 5) | (0x0c << 1) ;  
    157.     /* 
    158.      * lcdcon2 垂直方向的时间参数 
    159.      * bit[31:24] VBPD VSYNC之后多久才能发出第一行数据 
    160.      *            LCD手册上  T0-T2-T1 = VBPD + 1 = 4; 
    161.                   VBPD=3 
    162.      * bit[23:14] LINEVAL 显示的行数 320 = LINEVAL + 1; 
    163.      *            LINEVAL = 319; 
    164.      * bit[13:6]  VFPD 最有一行数据发出多久后,再发出VSYNC 
    165.      *            VFPD  T2-T5 = VFPD + 1; 
    166.      *            VFPD=1; 
    167.      * bit[5:0]   VSPW VSYNC信号的脉冲宽度  
    168.      *            VSPW + 1 = T1 = 1 
    169.      *            VSPW = 0; 
    170.      */  
    171.     lcdregs->lcdcon2 = (1 << 24) | (271 << 14) | (1 << 6) |(9);  
    172.     /* 
    173.      * lcdcon3 水平方向的时间参数 
    174.      * bit[25:19] HBPD HSYNC之后多久才能发出第一行数据 
    175.      *            LCD手册上  T6-T8-T7 = HBPD + 1 =273-251-5=17; 
    176.                   HBPD=17 
    177.      * bit[18:8]  HOZVAL 显示的列数 240 = HOZVAL + 1; 
    178.      *            LINEVAL = 239; 
    179.      * bit[7:0]   HFPD 最后一行的最后一个数据发出多久后,再发出HSYNC 
    180.      *            VFPD  T8-T11 = 251-240=11=VFPD+1; 
    181.      *            VFPD=10; 
    182.      */  
    183.      lcdregs->lcdcon3 = (1 << 19) | (479 << 8) | (1 << 0);  
    184.     /* 
    185.      * lcdcon4 水平方向的时间参数 
    186.      * bit[7:0]   HSPW HSYNC信号的脉冲宽度 
    187.      *            HSPW  T7 = HSPW+1 = 5; 
    188.      *            HSPW=4; 
    189.      */  
    190.     lcdregs->lcdcon4 = (40 << 0);  
    191.     /* 
    192.      * 信号的极性 
    193.      * bit[11] 1 = 5:6:5 Format 
    194.      * bit[10] 0 = The video data is fetched at VCLK falling edge 
    195.      * bit[9]  This bit indicates the VLINE/HSYNC pulse polarity. 1 = Inverted 
    196.      * bit[8]  This bit indicates the VFRAME/VSYNC pulse polarity. 1 = Inverted 
    197.      * bit[6]  This bit indicates the VDEN signal polarity. 0 = Normal 
    198.      * bit[3]  PWREN  LCD_PWREN output signal enable/disable.0 = Disable PWREN signal 1 = Enable PWREN signal 
    199.      * bit[1]  BSWP  =0 
    200.      * bit[0]  HWSWP = 1 参照2440手册 
    201.      */  
    202.     lcdregs->lcdcon5 = (1 << 11) | (0 << 10) | (1 << 9) | (1 << 8) | (1 << 0);  
    203.         
    204.     /* 3.3、设置显存,并将显存的地址通知到lcd控制器*/  
    205.     s3cxx_lcd->screen_base = dma_alloc_writecombine(NULL, s3cxx_lcd->fix.smem_len, &s3cxx_lcd->fix.smem_start, GFP_KERNEL);  
    206.     lcdregs->lcdsaddr1 = (s3cxx_lcd->fix.smem_start >> 1) &~(3<<30);  
    207.     lcdregs->lcdsaddr2 = ((s3cxx_lcd->fix.smem_start + s3cxx_lcd->fix.smem_len) >> 1) & 0x1fffff;  
    208.     lcdregs->lcdsaddr3 = (480*16/16); /* 一行的长度(单位:2字节) */  
    209.     
    210.     lcdregs->lcdcon1 |= (1<<0);    /* 使能LCD控制器 */  
    211.     lcdregs->lcdcon5 |= (1<<3);    /* 使能lcd */  
    212.     *gpbdat |= 1;               /*输出高电平,打开背光 */  
    213.         
    214.     //s3cxx_lcd->fix.smem_start          /* 显存的物理地址 */  
    215.     /* 4、注册 */  
    216.     register_framebuffer(s3cxx_lcd);  
    217.         
    218.     return 0;  
    219. }  
    220.     
    221.     
    222. static void lcd_exit()  
    223. {  
    224.     unregister_framebuffer(s3cxx_lcd);  
    225.     lcdregs->lcdcon1 &= ~(1<<0);  
    226.     *gpbdat &= ~1;  
    227.     dma_free_writecombine(NULL, s3cxx_lcd->fix.smem_len, s3cxx_lcd->screen_base, s3cxx_lcd->fix.smem_start);  
    228.         
    229.     iounmap(lcdregs);  
    230.     iounmap(gpbcon);  
    231.     iounmap(gpccon);  
    232.     iounmap(gpdcon);  
    233.     iounmap(gpgcon);  
    234.     framebuffer_release(s3cxx_lcd);  
    235. }  
    236.     
    237.     
    238. module_init(lcd_init);  
    239. module_exit(lcd_exit);  
    240.     
    241. MODULE_LICENSE("GPL");  

    3 编译调试

    1. 去掉内核中原有的lcd驱动

      使用make menuconfig命令,对内核重新配置。

      如上图所示,由->Device Drivers进入,选择->Graphics support,最终将sc2410 lcd framebuffer support设置成M,作为模块编译。因为在fb_ops结构体中,cfb_fillrect、cfb_copyarea、cfb_imageblit三个模块被调用了,所以需要将sc2410 lcd framebuffer作为模块进行编译,方便我们后面对这三个模块进行挂载。

    2. 对内核设置完成后,使用make uImage命令编译内核,使用make modules命令编译模块。编译结束后,将uImage和cfbfillrect.ko、cfbcopyarea.ko、cfbimageblit.ko分别拷贝到可被NFS挂接的目录下/work/nfs_root/...。
    3. 通过nfs服务,使用新的uImage启动系统。
    4. 装载cfbfillrect.ko、cfbcopyarea.ko、cfbimageblit.ko和lcd.ko驱动模块。

    5. 测试。

      使用echo hello > /dev/tty1命令,将hello输出到lcd终端。会发现lcd屏幕上打印出"hello"字符。

      使用cat lcd.ko > /dev/fb0命令,将lcd.ko的内容显示到lcd上。这里显示的是花屏的效果。

    视频中使用的是3.5寸的lcd屏,这里需要将对应的参数修改成4.3寸屏的,才能看到正确的实验现象。

  • 相关阅读:
    滚屏到相应位置才加载图片
    cs文件编译成dll文件
    SPAM搜索引擎垃圾技术
    新HTML元素:Canonical Link(权威链接),解决网络复制内容
    Asp.Net 文件操作基类(读取,删除,批量拷贝,删除,写入,获取文件夹大小,文件属性,遍历目录)
    VS2005发布网站项目时整个站点生成为一个自定义名称的dll文件
    301转向代码合集
    区别不同浏览器,CSS hack写法
    教你识别交换购买友情链接里的八种骗局真相!
    C#中Cookies的存取
  • 原文地址:https://www.cnblogs.com/beijiqie1104/p/11511916.html
Copyright © 2020-2023  润新知