1. 程式人生 > 其它 >linux驅動移植-LCD驅動案例 Mini2440裸機開發之LCD程式設計(GB2312、ASCII字型檔製作)16.Linux-LCD驅動(詳解)

linux驅動移植-LCD驅動案例 Mini2440裸機開發之LCD程式設計(GB2312、ASCII字型檔製作)16.Linux-LCD驅動(詳解)

由於我使用的Mini2440開發板採用的LCD為TFT屏,型號為LCD-P35(LQ035Q1DG04和ZQ3506_V0手冊通用) 。這一節,我們將參考s3c2410fb.c編寫LCD驅動程式。

一、LCD驅動編寫基礎函式

1.1 dma_alloc_wc

static inline void *dma_alloc_wc(struct device *dev, size_t size,
                                 dma_addr_t *dma_addr, gfp_t gfp)

該函式用於申請一段DMA緩衝區,分配出來的記憶體會禁止cache快取(因為DMA傳輸不需要CPU)。

返回值為:申請到的DMA緩衝區的虛擬地址,若為NULL,表示分配失敗,則需要使用dma_free_wc釋放記憶體,避免記憶體洩漏。

引數如下: 

  • dev:裝置指標;
  • size:分配的地址大小(位元組單位);
  • dma_addr:申請到的物理起始地址;
  • gfp:分配出來的記憶體引數,標誌定義在<linux/gfp.h>,常用標誌如下:
    • GFP_ATOMIC 用來從中斷處理和程序上下文之外的其他程式碼中分配記憶體. 從不睡眠;
    • GFP_KERNEL 核心記憶體的正常分配. 可能睡眠;
    • GFP_USER 用來為使用者空間頁來分配記憶體; 它可能睡眠.;

1.2 dma_free_wc

static inline void dma_free_wc(struct device *dev, size_t size,
                               void *cpu_addr, dma_addr_t dma_addr)

該函式用於釋放DMA緩衝區,引數和dma_alloc_wc一樣。

1.3 framebuffer_alloc

struct fb_info *framebuffer_alloc(size_t size, struct device *dev)

動態申請一個fb_info結構體,引數如下:

  • size:額外的記憶體大小;
  • dev:裝置指標,用於初始化fb_info->device成員;

二、LCD驅動編寫步驟

2.1 入口函式

在驅動入口函式中實現:

(1) 動態分配fb_info結構體;

(2) 設定fb_info

(2.1).設定固定的引數fb_info->fix

(2.2) 設定可變的引數fb_info->var

(2.3) 設定操作函式fb_info->fbops

(2.4) 設定fb_info其它的成員

(3) 設定硬體相關的操作

(3.1) 配置LCD引腳

(3.2) 根據LCD手冊設定LCD控制器

(3.3) 分配視訊記憶體(framebuffer),把視訊記憶體地址高速LCD控制器和fb_info

(4) 開啟LCD,並註冊fb_info

(4.1) 開啟LCD

    • 控制LCDCON5允許PWREN訊號
    • 控制LCDCON1輸出PWREN訊號
    • 輸出GPB0高電平來開啟背光

(4.2) 註冊fb_info

2.2 出口函式

在驅動出口函式中實現:

(1) 解除安裝核心中的fb_info

(2) 控制LCDCON1關閉PWREN訊號,關背光,iounmap登出地址

(3) 釋放DMA快取地址dma_free_wc

(4) 釋放註冊的fb_info

三、LCD驅動編寫 

首先在/work./sambashare/drivers建立專案資料夾11.lcd_dev,然後我們在11.lcd_dev下建立lcd_dev.c檔案。

3.1 分配一個fb_info結構

   /* 1. 初始化fb_info */
   s3c_lcd = framebuffer_alloc(0,0);
       if (!s3c_lcd)
           return -ENOMEM;

3.2 設定fb_info(設定固定的引數fb_info->fix)

首先設定fb_info->fix,成員變數fix型別為struct fb_fix_screeninfo,用於儲存LCD螢幕的相關引數,定義在include/uapi/linux/fb.h中:

struct fb_fix_screeninfo {
        char id[16];                    /* identification string eg "TT Builtin" */
        unsigned long smem_start;       /* Start of frame buffer mem */
                                        /* (physical address) */
        __u32 smem_len;                 /* Length of frame buffer mem */
        __u32 type;                     /* see FB_TYPE_*                */
        __u32 type_aux;                 /* Interleave for interleaved Planes */
        __u32 visual;                   /* see FB_VISUAL_*              */
        __u16 xpanstep;                 /* zero if no hardware panning  */
        __u16 ypanstep;                 /* zero if no hardware panning  */
        __u16 ywrapstep;                /* zero if no hardware ywrap    */
        __u32 line_length;              /* length of a line in bytes    */
        unsigned long mmio_start;       /* Start of Memory Mapped I/O   */
                                        /* (physical address) */
        __u32 mmio_len;                 /* Length of Memory Mapped I/O  */
        __u32 accel;                    /* Indicate to driver which     */
                                        /*  specific chip/card we have  */
        __u16 capabilities;             /* see FB_CAP_*                 */
        __u16 reserved[2];              /* Reserved for future compatibility */
};

其中引數含義如下:

  • id:唯一識別符號;
  • smem_start:framebuffer緩衝區物理起始位置(一般是顯示控制器DMA起始地址);
  • smem_len:framebuffer緩衝區的的長度,單位為位元組;
  • type:lcd型別,預設FB_TYPE_PACKED_PIXELS即可,可選引數如下;
#define FB_TYPE_PACKED_PIXELS           0       /* Packed Pixels        */
#define FB_TYPE_PLANES                  1       /* Non interleaved planes */
#define FB_TYPE_INTERLEAVED_PLANES      2       /* Interleaved planes   */
#define FB_TYPE_TEXT                    3       /* Text/attributes      */
#define FB_TYPE_VGA_PLANES              4       /* EGA/VGA planes       */
#define FB_TYPE_FOURCC                  5       /* Type identified by a V4L2 FOURCC */
  • type_aux:附加型別,預設0即可;
  • visual:顏色設定,常用引數如下:
#define FB_VISUAL_MONO01                0       /* Monochr. 1=Black 0=White  單側 0白色 1黑色 */
#define FB_VISUAL_MONO10                1       /* Monochr. 1=White 0=Black  單色 0黑色 1白色 */
#define FB_VISUAL_TRUECOLOR             2       /* True color   真彩 */
#define FB_VISUAL_PSEUDOCOLOR           3       /* Pseudo color (like atari) 偽彩 */
#define FB_VISUAL_DIRECTCOLOR           4       /* Direct color 直彩 */
#define FB_VISUAL_STATIC_PSEUDOCOLOR    5       /* Pseudo color readonly 只讀偽彩 */
#define FB_VISUAL_FOURCC                6       /* Visual identified by a V4L2 FOURCC */
  • xpanstep:如果沒有硬體panning賦值0;
  • ypanstep:如果沒有硬體panning賦值0;
  • ywrapstep:若果沒有硬體ywarp賦值0;
  • line_length:一行所佔的位元組數,例:(RGB565)240*320,那麼這裡就等於240*16/8;
  • mmio_start:記憶體對映IO的起始地址,用於應用層直接訪問暫存器,可以不需要;
  • mmio_len:記憶體對映IO的長度,可以不需要;
  • accel:指明使用的晶片,用於硬體加速,預設FB_ACCEL_NONE即可,可選引數如下;:
#define FB_ACCEL_NONE           0       /* no hardware accelerator      */
#define FB_ACCEL_ATARIBLITT     1       /* Atari Blitter                */
#define FB_ACCEL_AMIGABLITT     2       /* Amiga Blitter                */
#define FB_ACCEL_S3_TRIO64      3       /* Cybervision64 (S3 Trio64)    */
#define FB_ACCEL_NCR_77C32BLT   4       /* RetinaZ3 (NCR 77C32BLT)      */
......
  • capabilities:檢視FB_CAP_;
  • reserved:為將來的相容保留位;

所以:

   /* 2.1設定固定引數 */
    strcpy(s3c_lcd->fix.id, "mylcd");
    s3c_lcd->fix.smem_len        = LCD_xres * LCD_yres * 2;   // framebuffer緩衝區的大小 每個畫素兩個位元組
    s3c_lcd->fix.type            = FB_TYPE_PACKED_PIXELS;
    s3c_lcd->fix.type_aux        = 0;
    s3c_lcd->fix.xpanstep        = 0;
    s3c_lcd->fix.ypanstep        = 0;
    s3c_lcd->fix.ywrapstep       = 0;
    s3c_lcd->fix.accel           = FB_ACCEL_NONE;
    s3c_lcd->fix.visual          = FB_VISUAL_TRUECOLOR;      //真彩色
    s3c_lcd->fix.line_length     = LCD_xres * 2;             // LCD一行所佔位元組數

3.3 設定fb_info(設定可變的引數fb_info->var)

然後設定fb_info->var,成員變數var型別為struct fb_var_screeninfo,用於儲存LCD螢幕的相關引數,定義在include/uapi/linux/fb.h中:

struct fb_var_screeninfo {
        __u32 xres;                     /* visible resolution           */
        __u32 yres;
        __u32 xres_virtual;             /* virtual resolution           */
        __u32 yres_virtual;
        __u32 xoffset;                  /* offset from virtual to visible */
        __u32 yoffset;                  /* resolution                   */

        __u32 bits_per_pixel;           /* guess what                   */
        __u32 grayscale;                /* 0 = color, 1 = grayscale,    */
                                        /* >1 = FOURCC                  */
        struct fb_bitfield red;         /* bitfield in fb mem if true color, */
        struct fb_bitfield green;       /* else only length is significant */
        struct fb_bitfield blue;
        struct fb_bitfield transp;      /* transparency                 */

        __u32 nonstd;                   /* != 0 Non standard pixel format */

        __u32 activate;                 /* see FB_ACTIVATE_*            */

        __u32 height;                   /* height of picture in mm    */
        __u32 width;                    /* width of picture in mm     */

        __u32 accel_flags;              /* (OBSOLETE) see fb_info.flags */

        /* Timing: All values in pixclocks, except pixclock (of course) */
        __u32 pixclock;                 /* pixel clock in ps (pico seconds) */
        __u32 left_margin;              /* time from sync to picture    */
        __u32 right_margin;             /* time from picture to sync    */
        __u32 upper_margin;             /* time from sync to picture    */
        __u32 lower_margin;
        __u32 hsync_len;                /* length of horizontal sync    */
        __u32 vsync_len;                /* length of vertical sync      */
        __u32 sync;                     /* see FB_SYNC_*                */
        __u32 vmode;                    /* see FB_VMODE_*               */
        __u32 rotate;                   /* angle we rotate counter clockwise */
        __u32 colorspace;               /* colorspace for FOURCC-based modes */
        __u32 reserved[4];              /* Reserved for future compatibility */
};

其中引數含義如下:

  • xres:行解析度
  • yres:列解析度
  • xres_virtual:行虛擬解析度,設定和硬體一樣即可;
  • yres_virtual:列虛擬解析度,設定和硬體一樣即可;
  • xoffset:行偏移,設定為0即可;
  • yoffset:列偏移,設定為0即可;
  • bits_per_pixel:每個畫素用多少位,對於s3c2440不支援18位,只支援16位;
  • grayscale:灰度值,預設即可;
  • red:RGB:565對於R的offset為從最低位(右起)偏移11位,佔5bit;
  • green:RGB:565對於G的offset為從最低位(右起)偏移5位,佔6bit;
  • blue:RGB:565對於B的offset為從最低位(右起)偏移0位,佔5bit;
  • transp:透明度,預設即可;
  • nonstd:0標準像素格式,預設即可;
  • activate:預設即可;
  • height:LCD物理高度,單位mm;
  • width:LCD物理寬度,單位mm;
  • accel_flags:預設即可,過時引數;
  • pixclock:畫素時鐘,單位皮秒;
  • left_margin:行切換,從同步到繪圖之間的延遲;
  • right_margin:行切換,從繪圖到同步之間的延遲;
  • upper_margin:幀切換,從同步到繪圖之間的延遲;
  • lower_margin:幀切換,從繪圖到同步之間的延遲;
  • hsync_len:水平同步的長度;
  • vsync_len:垂直同步的長度;
  • sync:參考FB_SYNC_*;
  • vmode:參考FB_BMODE_*,預設即可;
  • rotate::時針旋轉的角度;
  • colorspace:色彩空間;
  • reserved:保留值;

 所以:

    /* 2.2 設定可變引數 */
     s3c_lcd->var.xres            = LCD_xres;         // 行解析度
     s3c_lcd->var.yres            = LCD_yres;         // 列解析度
     s3c_lcd->var.xres_virtual    = LCD_xres;         // 行虛擬解析度
     s3c_lcd->var.yres_virtual    = LCD_yres;         // 列虛擬解析度
     s3c_lcd->var.bits_per_pixel  = 16;               // 每畫素使用位數

     /* RGB:565 */
     s3c_lcd->var.red.offset      = 11;
     s3c_lcd->var.red.length      = 5;
     s3c_lcd->var.green.offset    = 5;
     s3c_lcd->var.green.length    = 6;
     s3c_lcd->var.blue.offset     = 0;
     s3c_lcd->var.blue.length     = 5;

     s3c_lcd->var.nonstd          = 0;
     s3c_lcd->var.activate        = FB_ACTIVATE_NOW;   // 使設定的值立即生效
     s3c_lcd->var.accel_flags     = 0;
     s3c_lcd->var.vmode           = FB_VMODE_NONINTERLACED;

3.4 設定fb_info(設定操作函式fb_info->fbops)

/* 調色盤陣列,被fb_info->pseudo_palette呼叫 */
static u32 pseudo_palette[16];

/*
 * 將核心單色使用bf表示
 * @param chan:單色 核心中的單色都是16位
 * @param bf:顏色位資訊
 */
static inline unsigned int chan_to_field(unsigned int chan,
                     struct fb_bitfield *bf)
{
    chan &= 0xffff;
    chan >>= 16 - bf->length;
    return chan << bf->offset;
}

/*
 * 調色盤操作函式
 *  @param regno: 調色盤陣列元素索引
 */
static int s3c2440fb_setcolreg(unsigned regno,
                   unsigned red, unsigned green, unsigned blue,
                   unsigned transp, struct fb_info *info)
{
    unsigned int val;
    //調色盤陣列不能大於16
    if (regno > 16)
         return 1;

    /* 小於16位,進行轉換 */
    u32 *pal = info->pseudo_palette;

    /* 用red,green,blue三個顏色值構造出16色資料val */
    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;
}


/*
 * fb_info操作函式fbops
 */
static struct fb_ops s3c2440fb_ops = {
    .owner        = THIS_MODULE,
    .fb_setcolreg    = s3c2440fb_setcolreg,   // 調色盤操作函式 設定調色盤fb_info-> pseudo_palette
    .fb_fillrect    = cfb_fillrect,          // 填充矩形  函式在drivers/video/fbdev/core/cfbfillrect.c中定義
    .fb_copyarea    = cfb_copyarea,          // 複製資料  函式在drivers/video/fbdev/core/cfbcopyarea.c中定義
    .fb_imageblit    = cfb_imageblit,         // 繪製圖形  函式在drivers/video/fbdev/core/cfbimgblt.c中定義
};

3.5 設定fb_info其它的成員

     /* 2.3 設定操作函式 */
     s3c_lcd->fbops               = &s3c2440fb_ops;

     /* 2.4 其他設定 */
     s3c_lcd->flags               = FBINFO_FLAG_DEFAULT;
     s3c_lcd->pseudo_palette      = pseudo_palette;             // 儲存調色盤陣列
     s3c_lcd->screen_size         = LCD_xres * LCD_yres * 2;    // framebuffer緩衝區的大小

3.6 設定硬體相關的操作(配置GPIO用於LCD)

參考之前介紹的Mini2440裸機開發之LCD程式設計(GB2312、ASCII字型檔製作)初始化GPIO,主要是GPCCON、GPDCON暫存器:

    /* 3.硬體相關操作 */
    /* 3.1 配置GPIO口用於LCD */
    gpccon = ioremap(0x56000020,4);
    gpdcon = ioremap(0x56000030,4);
    gpgcon = ioremap(0x56000030,4);

    // GPIO管腳用於VD[7:0],LCDVF[2:0],VM,VFRAME,VLINE,VCLK,LEND
    *gpccon = 0xaaaaaaaa;
    // GPIO管腳用於VD[23:8]
    *gpdcon = 0xaaaaaaaa;

    // 設定GPG4引腳為LCD_PWREN模式
    *gpgcon |= (3 << 8);

 

 

3.7 設定硬體相關的操作(根據LCD手冊設定LCD控制器)

 

3.8 設定硬體相關的操作(分配視訊記憶體)

 

四、測試

4.1 配置核心

我們切換到linux核心目錄下,

cd /wqoroot@zhengyang:~# cd /work/sambashare/linux-5.2.8/

在linux核心根目錄下執行,生成預設配置檔案.config:

make distclean
make s3c2440_defconfig # 這個是之前我之前配置的

進行核心配置:

root@zhengyang:/work/sambashare/linux-5.2.8# make menuconfig

配置步驟如下:

 Device Drivers  --->

  • Graphics support  --->
    • Frame buffer Device  --->
      • Support for frame buffer devices --->
        •  <M> S3C2410 LCD framebuffer support
        •  <M> Samsung S3C framebuffer support

儲存檔案,輸入檔名s3c2440_defconfig,在當前路徑下生成s3c2440_defconfig:存檔:

mv s3c2440_defconfig ./arch/arm/configs/

此時重新執行:

 

make s3c2440_defconfig

 

 

 

檢視.config檔案可以看到:

 

4.2 編譯核心和模組

編譯核心:

make V=1 uImage

將uImage複製到tftp伺服器路徑下:

 cp /work/sambashare/linux-5.2.8/arch/arm/boot/uImage /work/tftpboot/

編譯模組:

make modules

4.3 燒錄核心

開發板uboot啟動完成後,核心啟動前,按下任意鍵,進入uboot,可以通過print檢視uboot中已經設定的環境變數。

設定開發板ip地址,從而可以使用網路服務:

SMDK2440 # set ipaddr 192.168.0.105
SMDK2440 # save
Saving Environment to NAND...
Erasing NAND...

Erasing at 0x40000 -- 100% complete.
Writing to NAND... OK
SMDK2440 # ping 192.168.0.200
dm9000 i/o: 0x20000000, id: 0x90000a46 
DM9000: running in 16 bit mode
MAC: 08:00:3e:26:0a:5b
operating at unknown: 0 mode
Using dm9000 device
host 192.168.0.200 is alive

設定tftp伺服器地址,也就是我們ubuntu伺服器地址:

set serverip 192.168.0.200
save

下載核心到記憶體,並寫NAND FLASH:

tftp 30000000 uImage
nand erase.part kernel
nand write 30000000 kernel

執行結果如下:

SMDK2440 # tftp 30000000 uImage
dm9000 i/o: 0x20000000, id: 0x90000a46 
DM9000: running in 16 bit mode
MAC: 08:00:3e:26:0a:5b
operating at unknown: 0 mode
Using dm9000 device
TFTP from server 192.168.0.200; our IP address is 192.168.0.188
Filename 'uImage'.
Load address: 0x30000000
Loading: *#################################################################
     #################################################################
     #################################################################
     ##############################################################
     429.7 KiB/s
done
Bytes transferred = 3766128 (397770 hex)
SMDK2440 # nand erase.part kernel

NAND erase.part: device 0 offset 0x60000, size 0x400000

Erasing at 0x60000 --   3% complete.
Erasing at 0x80000 --   6% complete.
Erasing at 0xa0000 --   9% complete.
Erasing at 0xc0000 --  12% complete.
Erasing at 0xe0000 --  15% complete.
Erasing at 0x100000 --  18% complete.
Erasing at 0x120000 --  21% complete.
Erasing at 0x140000 --  25% complete.
Erasing at 0x160000 --  28% complete.
Erasing at 0x180000 --  31% complete.
Erasing at 0x1a0000 --  34% complete.
Erasing at 0x1c0000 --  37% complete.
Erasing at 0x1e0000 --  40% complete.
Erasing at 0x200000 --  43% complete.
Erasing at 0x220000 --  46% complete.
Erasing at 0x240000 --  50% complete.
Erasing at 0x260000 --  53% complete.
Erasing at 0x280000 --  56% complete.
Erasing at 0x2a0000 --  59% complete.
Erasing at 0x2c0000 --  62% complete.
Erasing at 0x2e0000 --  65% complete.
Erasing at 0x300000 --  68% complete.
Erasing at 0x320000 --  71% complete.
Erasing at 0x340000 --  75% complete.
Erasing at 0x360000 --  78% complete.
Erasing at 0x380000 --  81% complete.
Erasing at 0x3a0000 --  84% complete.
Erasing at 0x3c0000 --  87% complete.
Erasing at 0x3e0000 --  90% complete.
Erasing at 0x400000 --  93% complete.
Erasing at 0x420000 --  96% complete.
Erasing at 0x440000 -- 100% complete.
OK
SMDK2440 # nand write 30000000 kernel

NAND write: device 0 offset 0x60000, size 0x400000
 4194304 bytes written: OK

 

 

4.4 編譯LCD驅動

在11.lcd_dev路徑下編譯:

root@zhengyang:/work/sambashare/drivers/11.lcd_dev# cd /work/sambashare/drivers/11.lcd_dev
root@zhengyang:/work/sambashare/drivers/11.lcd_dev# make

拷貝驅動檔案到nfs檔案系統:

root@zhengyang:/work/sambashare/drivers/11.lcd_dev# cp /work/sambashare/drivers/11.lcd_dev/lcd_dev.ko /work/nfs_root/rootfs/ 

4.5 安裝驅動

重啟開發板,執行如下命令:

insmod lcd_dev.ko

掛載LCD驅動後, 如下圖,可以通過如下命令檢視已掛載的LCD裝置節點::

ls -l /dev/fb*  

4.6 測試執行

比如向LCD螢幕輸出hello字串:

echo hello> /dev/tty1

參考文章

[1]16.Linux-LCD驅動(詳解)

[2]十二、Linux驅動之LCD驅動

[3]S3C2440之LCD驅動