LCD底層驅動分析


 根據分析的框架,自己寫一個LCD驅動程序

    1分析LCD硬件原理圖

        

Von和Voff接的是一個電源電路,通過LCD_POWER接的是GPG4來控制LCD電源,高電平表示開啟LCD電源

VM接的是CPU的VM:VDEN /GPC4為數據使能信號,

VLINE接的CPU:HSYNC/GPC2,HSYNC信號有效時,表示一行數據的開始;

VFRAME接的CPU:VSYNC/GPC3,VSYNC信號有效時,表示一幀數據的開始

VCLK接的CPU:VCLK/GPC1 表示像素時鍾信號,每個VCLK信號表示正在傳輸一個像素的數據;

LED-和LED+接到背光電路上,背光電路是由GPB0控制;高電平表示開啟背光

VD[3~7],VD[10~15],VD[19~23]代表的是數據接口,CPU通過LCD專用的DMA來給傳輸像素的數據,

 

 

    2 配置LCD用到GPIO引腳

   根據原理圖分析用的CPU 引腳,需要進行一一的配置

    2.1先配置數據引腳VD;

  *gpccon = 0xaaaaaaaa; 
  *gpdcon = 0xaaaaaaaa;

  2.2配置控制引腳
   /*3.注冊配置GPIO,用於LCD*/
   /*GPC4為配置為VDEN
   *GPC2配置為HSYNC
  *GPC3配置為VSYNC
  *GPC1配置為VCLK
  */
  *gpccon =(2<<2)|(2<<4) |(2<<6) |(2<<8);

 

    3 配置S3C2440對應的寄存器的值

     3.1 詳細分析LCD時序

      打開S3C2440中的LCD數據手冊

              S3C2440數據手冊中的LCD控制時序圖                              這是LCD數據手冊中的時序圖

   

   那么S3C2440手冊中的時序圖的配置需要根據LCD數據手冊的時序時間來進行配置,只有這樣才能對這個LCD正確操作;

根據時序圖就可以分析出LCD是怎么傳輸數據的

3.1.1一幀數據的傳輸

  一幀數據的傳輸的時鍾基准是VCLK,它代表每個VCLK信號都代表一個正在傳輸的像素數據;對比兩個數據手冊可以得出這個VCLK時鍾信號時間為5~12MHz

(1)INT_FrSyn是幀同步信號,如果有圖像顯示,就會發出一個中斷信號,產生一個同步信號

(2)VSYNC是一幀的同步信號,一幀數據開始傳輸;通過對比可知,VSYNC脈沖寬度為Tvp,而且和S3C2440中的信號互為反向;

(3)VSPW表示VSYNC脈沖的寬度為(VDPW+1)個HSYNC信號周期=Tvp:表示VSPW+1行數據無效

(4)VBPD表示表示VSYNC信號后,還需要經過(VBPD+1)個HSYNC信號周期=Tvb;需要經過VBPD+1行無效數據后,才會出現有效數據

(5)VDEN表示有效數據開始傳輸;開始連續傳輸LINEVAL+1行數數據;

       傳輸完后,VDEN變為低電平,還需要經過VFPD+1=Tvf個無效行數據,一幀數據才能結束;然后會進行下一幀數據傳輸;

3.1.2一幀數據中一行數據是怎么傳輸的呢

     根據時序圖可知;一幀數據的傳輸,是通過一行行數據才完成的;

   (1)當HSYNC發出一個高脈沖時;一行數據開始傳輸;

   (2)HSPW+1代表HSYNC脈沖寬度;需要經過HSPW+1個VCLK個周期=Thp;也就是需要經過HSPW+1個無效像素;

   (3)HBPD+1代表還需要經過HBPD+1個VCLK時鍾=Thb;也就是需要經過HBPD+1個無效像素,才可以出現有效像素。

   (4)從HSYNC產生開始;在出現有效像素之前,一共需要經過HSPW+HBPD+2個無效像素,才能出現有效像素;

   (5)VDEN代表數據使能信號;然后會連續傳輸HOZVAL+1個像素;當VDEN無效時,還需要經過HFDP+1個像素才能表示一行像素傳輸完成;

    (6)HFPD+1代表一行有效數據傳輸完后,還需要經過HFPD+1個VCLK才能表示一行像素傳輸完畢;

     3.2 詳細解析LCD控制的配置方法

      

/*4.設置硬件相關的設置*/
/*設置LCDCON1
*設置CLKVAL[17:8]像素時鍾,這個值要參考LCD手冊VCLK的范圍5~12MHz
*設置掃描模式TFT[6:5]=11
*設置像素模式為16BPP
*禁止LCD ENVID[0]控制信號
*/
lcd_reg->LCDCON1=(4<<8) |(0x03<<5)|(0x0c<<1);

/*設置垂直方向
*設置VBPD參考LCD手冊時序圖
* 設置VBPD+1=Tvb[31:24] =2; VBPD=1
*設置VSPW+1=Tvp[13:6]=10; VSPW=9;
*設置VFPD+1=Tvf[5:0]=2 VFPD=1;
*LINEVAL垂直長度為272-1=271[23:14]
*/
lcd_reg->LCDCON2=(1<<24) |(271<<14)|(1<<6) |(9<<0);

/*設置水平方向
*
*HBPD[25:9] >>2clk HBPD+1>2;HBPD=1;
*HOZVAL[18:9] 水平方向的480-1=479
*HFPD [7:0] HFPD+1 =Thf >=2;HFPD=1;
*/
lcd_reg->LCDCON3=(1<<19) |(479<<8) |(1<<0);

/*HSPW[7:0]Thp=41;HSPW+1=41;*/

   lcd_reg->LCDCON4=(40<<0);

/*
*FRM565[11]=1
*INVVCLK[10]=1 來讀取數據的觸發方=1
*INVVLINE [9]判斷HSYNC極性為反轉1
*INVVFRAME[8]控制VSYNC極性反轉=1
*INVVDEN[6]判斷VD極性,不反轉=0
*POWER[3] =0
*HWSWP=1
*BSWP=0
*/
lcd_reg->LCDCON5=(1<<11) | (0<<10) | (1<<9) | (1<<8) | (1<<1);
/*把緩沖區的地址寫到寄存器中*/
lcd_reg->LCDSADDR1= (s3c_lcd->fix.smem_start >> 1) &~(3<<30);
lcd_reg->LCDSADDR2=((s3c_lcd->fix.smem_start+ s3c_lcd->screen_size)>>1) &0x1FFFFF;
lcd_reg->LCDSADDR3=(480*16/16);

 

    4 編寫LCD驅動框架

     根據之前對LCD硬件的分析,現在開始編寫代碼

    源碼如下:

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/fb.h>
#include <linux/init.h>
#include <linux/dma-mapping.h>
#include <linux/interrupt.h>
#include <linux/workqueue.h>
#include <linux/wait.h>
#include <linux/platform_device.h>
#include <linux/clk.h>

#include <asm/io.h>
#include <asm/uaccess.h>
#include <asm/div64.h>

#include <asm/mach/map.h>
#include <asm/arch/regs-lcd.h>
#include <asm/arch/regs-gpio.h>
#include <asm/arch/fb.h>

static struct fb_info *s3c_lcd;
static u32 pseudo_palette[16];
/*定義LCD配置寄存器*/
static struct s3c_reg {
   u32 LCDCON1 ;
   u32 LCDCON2 ;
   u32 LCDCON3 ;
   u32 LCDCON4 ;
   u32 LCDCON5 ;
   u32 LCDSADDR1;
   u32 LCDSADDR2; 
   u32 LCDSADDR3;
   u32 REDLUT; 
   u32 GREENLUT; 
   u32 BLUELUT; 
   u32 resver[9];
   u32 DITHMODE;
   u32 TPAL; 
   u32 LCDINTPND; 
   u32 LCDSRCPND; 
   u32 LCDINTMSK; 
   u32 TCONSEL; 
   };


static volatile unsigned int *gpccon;
static volatile unsigned int *gpcdat;
static volatile unsigned int *gpdcon;
static volatile unsigned int *gpbcon;
static volatile unsigned int *gpgcon;
static volatile unsigned int *gpbdat;
static volatile struct s3c_reg *lcd_reg;

static inline unsigned int chan_to_field(unsigned int chan,
                     struct fb_bitfield *bf)
{
    chan &= 0xffff;
    chan >>= 16 - bf->length;
    return chan << bf->offset;
}
static int lcdfb_setcolreg(unsigned regno,
                   unsigned red, unsigned green, unsigned blue,
                   unsigned transp, struct fb_info *info)
{  unsigned int val;
     if(regno >16)
           return 1;
            val  = chan_to_field(red,   &info->var.red);
            val |= chan_to_field(green, &info->var.green);
            val |= chan_to_field(blue,  &info->var.blue);
            pseudo_palette[regno] = val;
            return 0;
}



static struct fb_ops s3c_lcd_ops = {
    .owner        = THIS_MODULE,
    .fb_setcolreg    = lcdfb_setcolreg,
    .fb_fillrect    = cfb_fillrect,
    .fb_copyarea    = cfb_copyarea,
    .fb_imageblit    = cfb_imageblit,
};
static int __init lcd_init(void)
{
/*1.分配一個fb_info結構體*/
  s3c_lcd = framebuffer_alloc(0,NULL);
  gpgcon=(volatile unsigned int *)ioremap(0x56000060,8);
  gpccon = (volatile unsigned int *)ioremap(0x56000020,8);
  gpcdat=gpccon+1;
  gpdcon =(volatile unsigned int *)ioremap(0x56000030,8);
  gpbcon=(volatile unsigned int *)ioremap(0x56000010,8);
  gpbdat=gpbcon+1;
 
    /*2.設置*/
   lcd_reg =ioremap(0x4d000000,sizeof(struct s3c_reg));
    /*2.2 設置可變參數  */
   s3c_lcd->var.xres                 = 480;//X軸的實際像素
   s3c_lcd->var.yres                 =272;//y軸實際像素
   s3c_lcd->var.xres_virtual      =480;//虛擬像素設置和實際像素一樣
   s3c_lcd->var.yres_virtual      =272;
   s3c_lcd->var.xoffset             =0;//實際像素和虛擬像素偏移值為0
   s3c_lcd->var.yoffset             =0;
   s3c_lcd->var.bits_per_pixel   =16;//每個像素點有16個位組成
   s3c_lcd->var.red.offset         =11;//red在16位域中偏移值為11
   s3c_lcd->var.red.length         =5;
   s3c_lcd->var.red.msb_right   =0;
   s3c_lcd->var.green.offset      =5;//red在16位域中偏移值為11
   s3c_lcd->var.green.length      =6;
   s3c_lcd->var.green.msb_right=0;
   s3c_lcd->var.blue.offset       =0;//red在16位域中偏移值為11
   s3c_lcd->var.blue.length       =5;
   s3c_lcd->var.blue.msb_right =0;
   s3c_lcd->var.activate =FB_ACTIVATE_NOW;
   /*2.3 設置固定參數*/
   strcpy(s3c_lcd->fix.id, "mylcd");
   s3c_lcd->fix.smem_len        =480*272*16/8;//緩沖區大小
   s3c_lcd->fix.type                 =FB_TYPE_PACKED_PIXELS;
   s3c_lcd->fix.visual                =FB_VISUAL_TRUECOLOR;
   s3c_lcd->fix.line_length        =480*16/8;
   /*2.4 設置其他設置*/
   s3c_lcd->pseudo_palette = pseudo_palette;
   s3c_lcd->fbops                    =&s3c_lcd_ops;
   s3c_lcd->screen_size   = 480*272*16/8;//屏幕像素的個數
   s3c_lcd->screen_base =dma_alloc_writecombine(NULL,  s3c_lcd->screen_size , &s3c_lcd->fix.smem_start,
                          GFP_KERNEL);    
   /*
   *配置GPC引腳為數據引腳
   *VD3~VD7   
   *gpccon = (0x10<<22)|(0x10<<24)|(0x10<<26)|(0x10<<28)|(0x10<<30);
   /*VD10~VD15 VD19~VD23
     gpdcon =(0x10<<4) |(0x10<<6) |(0x10<<8) |(0x10<<10) |(0x10<<12)|(0x10<<14) |\
    (0x10<<22) |(0x10<<24) |(0x10<<26) |(0x10<<28) |(0x10<<23);
   */
 *gpccon  = 0xaaaaaaaa;   /* GPIO管腳用於VD[7:0],LCDVF[2:0],VM,VFRAME,VLINE,VCLK,LEND */
 *gpdcon  = 0xaaaaaaaa;   /* GPIO管腳用於VD[23:8] */
  
   /*GPG4為LCD_POWER*/
     *gpgcon |=(3<<8);
   /*GPB0為輸出,控制LCD背光*/
     *gpbcon &=~(3<<0);
     *gpbcon |=1;
     *gpbdat &=~(1<<0);
     /*
      *配置GPD
     */
      /*3.注冊配置GPIO,用於LCD*/
     /*GPC4為配置為VDEN
       *GPC2配置為HSYNC
       *GPC3配置為VSYNC
       *GPC1配置為VCLK
       */
     *gpccon =(2<<2)|(2<<4) |(2<<6) |(2<<8);
     /*4.設置硬件相關的設置*/
    /*設置LCDCON1
      *設置CLKVAL[17:8]像素時鍾,這個值要參考LCD手冊CLOCK CYCLE
      *設置MMODE為[7]=0
      *設置掃描模式TFT[6:5]=11
      *設置像素模式為16BPP
      *禁止LCD控制信號
      */
      
     lcd_reg->LCDCON1=(4<<8) |(0x03<<5)|(0x0c<<1);

       /*設置垂直方向
      *設置VBPD參考LCD手冊時序圖
      *  設置VBPD+1=Tvb[31:24] =2; VBPD=1
      *設置VSPW+1=Tvp[13:6]=10;   VSPW=9;
      *設置VFPD+1=Tvf[5:0]=2           VFPD=1;
      *LINEVAL垂直長度為272-1=271[23:14]
     */
    lcd_reg->LCDCON2=(1<<24) |(271<<14)|(1<<6) |(9<<0);
    /*設置水平方向
    * 
    *HBPD[25:9] >>2clk  HBPD+1>2;HBPD=1;
        *HOZVAL[18:9]  水平方向的480-1=479
        *HFPD  [7:0] HFPD+1 =Thf >=2;HFPD=1;
        *
    */
    lcd_reg->LCDCON3=(1<<19) |(479<<8) |(1<<0);
         /*
         * HSPW[7:0]Thp=41;HSPW+1=41;
         *
         */
         lcd_reg->LCDCON4=(40<<0);
        /*
        *CON5 
        *FRM565[11]=1
        *INVVCLK[10]=1 來讀取數據的觸發方=1
        *為巍⑽仙厥劍?
        *INVVLINE [9]判斷HSYNC極性為反轉1
        *INVVFRAME[8]控制VSYNC極性反轉=1
        *INVVDEN[6]判斷VD極性,不反轉=0
            *POWER[3]  =0
            *HWSWP=1
            *BSWP=0
        */
   lcd_reg->LCDCON5=(1<<11) | (0<<10) | (1<<9) | (1<<8) | (1<<1);        
 /*把緩沖區的地址寫到寄存器中*/
   lcd_reg->LCDSADDR1=(s3c_lcd->fix.smem_start >> 1)  &~(3<<30);
   lcd_reg->LCDSADDR2=((s3c_lcd->fix.smem_start+ s3c_lcd->screen_size)>>1) &0x1FFFFF;
   lcd_reg->LCDSADDR3=(480*16/16);
   /*開啟LCD控制信號*/
   lcd_reg->LCDCON1 |=(1<<0);
  *gpbdat |= 1; /* 輸出高電平, 使能背光 */
  /*POWER輸出*/
    lcd_reg->LCDCON5 |=(1<<3);
  /*根據LCD手冊設置LCD控制器,比如VCLK頻率*/
   /*分配frambuffer,並把地址告訴LCD控制器,*/
register_framebuffer(s3c_lcd);
return 0;
}
static void lcd_exit(void)
{
unregister_framebuffer(s3c_lcd);
framebuffer_release(s3c_lcd);
iounmap(gpgcon);
iounmap(gpccon);
iounmap(gpbcon);
iounmap(gpdcon);
dma_free_writecombine(NULL,  s3c_lcd->screen_size , &s3c_lcd->fix.smem_start,
                          GFP_KERNEL);
 lcd_reg->LCDCON1&= ~(1<<0); /* 關閉LCD本身 */
 
*gpbdat &= ~1;     /* 關閉背光 */
}
module_init(lcd_init);
module_exit(lcd_exit);
MODULE_LICENSE("GPL");

  重新編譯內核,要把原來的內核配置中的驅動自帶的LCD驅動變成模塊;然后make uImage 重新生成一個內核,然后再make module 生成一個模塊;因為要用到

.fb_fillrect = cfb_fillrect,
.fb_copyarea = cfb_copyarea,
.fb_imageblit = cfb_imageblit,這三個函數,所以要把關於這三個函數的.c文件編譯成模塊,

然后在編譯新編的LCD驅動,

insmod cfbfillrect.ko

insmod cfbcopyarea.ko

insmod cfbimageblit.ko

insmod LCD.ko

這樣就把新編譯的LCD驅動加載到內核中;

echo hello > /dev/tty1命令,則hello就會顯示在LCD屏上

echo LCD.ko > /dev/fb0 則LCD屏會花屏;

    顯示結果


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM