1. 程式人生 > >韋東山視訊第二期之心得體會

韋東山視訊第二期之心得體會

   最近一直在收看韋東山老師的嵌入式視訊,收穫頗豐。第二期主要是關於一些裝置驅動的編寫示例,從最基礎的按鍵驅動、LED驅動到複雜的LCD、觸控式螢幕、網絡卡、以及各種匯流排驅動。今天主要對之前學習的LCD驅動做一個學習心得,將學習中的一些問題和收穫和大家一起分享。
   我的開發板的Linux系統是linux-2.6.32.2,和我在Ubuntu虛擬機器中的專用於編譯模組檔案的核心原始碼包一致(在剛開始因為兩者的版本不一致導致在mini2440中載入.ko檔案一直都報錯:"invalid module format",也是個印象深刻的教訓)。我的mini2440的LCD是滕寶的TD35,雖然和視訊中的JZ2440不一樣,但是大同小異,具體步驟如下。
   首先在模組驅動程式中的初始化函式中進行一些基礎操作,包括fb_info結構體的分配、設定LCD的固定引數(fb_info結構體中的fb_fix_screeninfo結構體例項fix)、設定可變的引數(fb_info中的fb_var_screeninfo結構體例項var)、設定操作函式(fb_ops結構體)、處理一些硬體相關的操作(如配置用於LCD的GPIO引腳)、以及最後的註冊工作等等。
   在這些韋東山老師整理出來的配置步驟中,我主要與大家分享下面幾點:
   1、第二步設定固定引數中有幾點要格外注意。第一,fb_info.fix結構體中的成員smem_len和line_length變數的單位都是位元組,很多時候單位是位的話要記得轉換為位元組。第二,成員visual要設為真彩色(FB_VISUAL_TRUECOLOR)。第三,成員smem_len表示了影象大小,要檢視資料手冊,單位同樣也是位元組。
   2、第三步設定可變引數也有幾點要注意。第一,var的成員xres表示的是一行的畫素點,yres表示一列的畫素點,而虛擬畫素點xres_virtual和yres_virtual只需和xres,yres取相同值即可。第二,我的畫素深度是16位,而初始畫素深度為24位,其中RGB各佔一個位元組,但在16位畫素中,RGB分別佔5、6、5位,8位取高5位或高6位實現裁剪到5位或6位。
   3、第四步操作函式中記得加上調色盤相關的函式,在16位畫素深度的影象處理中要用到調色盤(palette)。將16位的畫素值作為調色盤陣列的索引值,然後從資料型別為24位或32位的除錯板中取色,這樣達到了要求的畫素深度。
   4.第五步中主要是設定LCD相關的暫存器,包括LCDCON1至LCDCON5,以及LCDSADDR1到LCDSADDR3。這些暫存器的配置既需要閱讀s3c2440的資料手冊,也需要閱讀LCD的資料手冊,尤其的行訊號的場訊號的時序圖,通過在其引數範圍內進行調整,直到螢幕上出現理想的檢視。
   前面這些步驟就實現了基礎的配置了,具體的程式碼如下:
static int __init lcd_init(void)
{
    /*1.分配一個fb_info結構體*/
    s3c_lcd=framebuffer_alloc(0,NULL);
    /*2.1設定固定的引數*/
    strcpy(s3c->fix.id,"mylcd");
    //s3c->fix.smem_start之後再設
    s3c_lcd.fix.smem_len=320*240*16/8; 
    s3c_lcd.fix.type=FB_TYPE_PACKED_PIXELS;
    s3c_lcd.fix.visual=FB_VISUAL_TRUECOLOR;
    s3c_lcd.fix.line_length=240*2; 
    /*2.2設定可變引數*/
   s3c_lcd->var.xres=240;
   s3c_lcd->var.yres=320;
   s3c_lcd->var.xres_virtual=240;
   s3c_lcd->var.yres_virtual=320;
   s3c_lcd->var.bits_per_pixel=16;
   s3c_lcd->var.red.offset=11;
   s3c_lcd->var.red.length=5;
   s3c_lcd->var.green.offset=5;
   s3c_lcd->var.red.length=6;
   s3c_lcd->var.blue.offset=0;
   s3c_lcd->var.red.length=5;
   s3c_lcd->var.active=FB_ACTIVATE_NOW;
    /**設定操作函式***/
   s3c_lcd->fbops=&s3c_lcdfb_ops;
   /**其他設定*****/
   s3c_lcd->pseudo_palette=pseudo_palette;//調色盤
   s3c_lcd->screen_size=240*320*2;
   /**3.硬體相關的操作*******/
   /*配置GPIO用於LCD*****/
   GPCCON=ioremap(0x56000010, 4);
   GPDCON=ioremap(0x56000030,4);
   GPGCON=ioremap(0x56000060,4);
   *GPCCON=0xaaaaaaaa;
   *GPDCON=0xaaaaaaaa;
   //配置LCD背光使能口
   //GPGCON做LCD_POWER使能LCD電源口
   *GPGCON | =(3<<8);
    /**3.2根據LCD手冊設定LCD控制器*****/
   lcd_regs=ioremap(0x4D000000,sizeof(struct lcd_regs));
   lcd_regs->lcdcon1=(6<<8)|(3<<5)|(0x0c<<1);
   lcd_regs->lcdcon2=(3<<24)|(319<<14)|(1<<6)|(0<<0);
   lcd_regs->lcdcon3=(19<<19)|(239<<8)|(9<<0);
   lcd_regs->lcdcon4=9;
   lcd_regs->lcdcon5=(1<<11)|(0<<10)|(1<<9)|(1<<8)|(1<<0);
   /*3.3分配視訊記憶體,並把地址告訴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
   lcd_regs->lcdcon1 | =(1<<0);
   lcd_regs->lcdcon5 | =(1<<3);
   /**4.註冊****/
   register_framebuffer(s3c_lcd);
}




   5.在完成這些初始化過程之後,就是自己發揮的部分了,可以通過讀取HZK16檔案來實現漢字型檔的匯入來實現漢字,也可以通過Linux原始碼包的ASCII庫檔案來來實現顯示字母和數字等常用標識。下面是我的部分字型檔讀取函式,程式碼如下:
   void lcd_show_ascii(unsigned char *fbmem_start,int x,int y,unsigned char c,unsigned int forecolor,int backcolor)
{
    //指向核心中已定義的ASCII陣列
    unsigned char *dots=(unsigned char *)&fontdata_8x16[c*16];
    int i,b;
    unsigned char by;
    for(i=0;i<16;i++)  //一個字母佔16個位元組共16*8位
    {
        by=dots[i];
        for(b=7;b>=0;b--)  //16表示行數,8表示列數
        {
             if(by&(1<<b))  //如果當前位為1說明要點亮
             {
                 lcd_show_point(fbmem_start,x+7-b,y+i,forecolor);
             }
     else  //否則為背景色,即不顯示
             {
        lcd_show_point(fbmem_start,x+7-b,y+i,backcolor);
     }
        }
    } 
}




   這整個過程有些複雜,但只需按部就班加上對實際情況的具體分析,相信就可以實現想要的效果了。當實現之後,就可以利用這個LCD螢幕來作為自己顯示的又一個終端了。