(4)高大上的LCD

/* AUTHOR: Pinus

* Creat on : 2018-11-1

* KERNEL : linux-4.4.145

* BOARD : JZ2440(arm9 s3c2440)

* REFS : 韋東山視頻教程第二期

S3C2440上LCD驅動(FrameBuffer)實例開發講解

*/

概述

        不要說我這取得名字low,我感覺吧,一個東西它能顯示出來圖像,色彩鮮明,便會給人非常直觀的感受,和串口打印信息完全不一樣,就是高大上,有檔次。

        LCD是Liquid  Crystal  Display的簡稱,也就是經常所說的液晶顯示器LCD能夠支持彩色圖像的顯示和視頻的播放,是一種非常重要的輸出設備。

這篇文章只講籠統的概念,爲什麼呢?

因爲LCD不學之前一頭霧水,學會之後就很清晰,要想學會重要的是理解LCD顯示的原理,和實現控制的原理。只要理解,就可以實現。具體的分析請結合這篇文章S3C2440上LCD驅動(FrameBuffer)實例開發講解,這篇文章我已經通讀過,講解的很細緻,如果仔細看一定收益很多,我就不搬運了。

我會大致介紹,並提供實現內核源碼支持LCD,請細讀那篇推薦文章,加深理解。

原理簡述

1. LCD工作的硬件需求:

    要使一塊LCD正常的顯示文字或圖像,需要LCD驅動器和LCD控制器兩個部分。在通常情況下,生產廠商把LCD驅動器會以各種形式與LCD玻璃基板封裝在一起,而LCD控制器則是由外部的電路來實現。現在很多的MCU內部都集成了LCD控制器,如手中的s3c2440等。通過LCD控制器就可以產生LCD驅動器所需要的控制信號來控制STN/TFT屏。

2.顯示屏顯示的大致方式

    首先有一個概念,圖像是由一個個像素點組成的,LCD在顯示的時候也是一個個像素呈現出來的,只是速度很快,在人眼中就是圖像了。那麼必然要涉及幾個問題,一個是圖像大小,一個是像素點的大小和顏色組成,一個是像素刷新的時序(一是圖像刷新換行時,一是顯示完要顯示下一幀圖像時)等等。而這些在是現實無非是要設置s3c2440對應的寄存器(將LCD控制器的所有寄存器一位一位設置就好了)。

3.frambuffer

    幀緩衝區,實際中自然不可能是每動一次就單獨把數據向LCD寫一次,那樣無疑效率會很低,所以常常是開闢一個空間,這個空間裏存有一幀的圖像,就像顯存一樣,軟件修改這個幀緩衝區,然後一次將所有變化都刷新向LCD。frambuffer就大致如此。

內核以此爲中心,應該是模擬了一臺虛擬總線,就像前文的input。

幀緩衝設備爲標準的字符型設備,在Linux中主設備號29。次設備號定義幀緩衝的個數,最大允許有32個FrameBuffer。

4.怎麼寫LCD驅動程序?

    1. 分配一個fb_info結構體: framebuffer_alloc

    2. 設置

    3. 註冊: register_framebuffer

    4. 硬件相關的操作

先去看那篇推薦文章S3C2440上LCD驅動(FrameBuffer)實例開發講解

實現移植LINUX內核的LCD驅動

在devs.c中
 

/* LCD Controller */
#ifdef CONFIG_PLAT_S3C24XX
static struct resource s3c_lcd_resource[] = {
    [0] = DEFINE_RES_MEM(S3C24XX_PA_LCD, S3C24XX_SZ_LCD),
    [1] = DEFINE_RES_IRQ(IRQ_LCD),
};

struct platform_device s3c_device_lcd = {
    .name = "s3c2410-lcd",
    .id = -1,
    .num_resources = ARRAY_SIZE(s3c_lcd_resource),
    .resource = s3c_lcd_resource,
    .dev = {
        .dma_mask = &samsung_device_dma_mask,
        .coherent_dma_mask = DMA_BIT_MASK(32),
    }
};

void __init s3c24xx_fb_set_platdata(struct s3c2410fb_mach_info *pd)
{
    struct s3c2410fb_mach_info *npd;

    npd = s3c_set_platdata(pd, sizeof(*npd), &s3c_device_lcd);//s3c_device_lcd->dev.platform_data=pd

    if (npd) {
        npd->displays = kmemdup(pd->displays,
        sizeof(struct s3c2410fb_display) * npd->num_displays, GFP_KERNEL);

    if (!npd->displays)
        printk(KERN_ERR "no memory for LCD display data\n");
    } else {
        printk(KERN_ERR "no memory for LCD platform data\n");
    }
}
#endif /* CONFIG_PLAT_S3C24XX */

在mach-smdk2440.c中

/* LCD driver info */

這個結構體包含lcd設置的參數,需要根據實際,也就是你自己的LCD數據手冊的實際參數設置
static struct s3c2410fb_display smdk2440_lcd_cfg __initdata = {
    .lcdcon5 = S3C2410_LCDCON5_FRM565 |
                S3C2410_LCDCON5_INVVLINE |
                S3C2410_LCDCON5_INVVFRAME |
                S3C2410_LCDCON5_PWREN |
                S3C2410_LCDCON5_HWSWP,
    .type = S3C2410_LCDCON1_TFT,
    .width = 480,
    .height = 272,
    .pixclock = 1000000, //166667, /* HCLK 60 MHz, divisor 10 */
    .xres = 480,
    .yres = 272,
    .bpp = 16,
    .left_margin = 4, // Hsync front porch
    .right_margin = 4, // Hsync back porch
    .hsync_len = 41, // Hsync pulse width
    .upper_margin = 2, // Vsync back porch
    .lower_margin = 2, // Vsync front porch
    .vsync_len = 10, // Vsync pulse width
};

其中.pixclock一項單獨說一下

在內核的參考文檔中有這樣一段話:

The speed at which the electron beam paints the pixels is determined by the

dotclock in the graphics board. For a dotclock of e.g. 28.37516 MHz (millions

of cycles per second), each pixel is 35242 ps (picoseconds) long:

 

    1/(28.37516E6 Hz) = 35.242E-9 s

也就是說VCLK的倒數,再乘10^12即爲pixclk。picoseconds單位表示微微秒,即10^12。VCLK的值由具體LCD手冊確定。
 

static struct s3c2410fb_mach_info smdk2440_fb_info __initdata = {
    .displays = &smdk2440_lcd_cfg,
    .num_displays = 1,
    .default_display = 0,
#if 1
    /* currently setup by downloader 根據自己的板子指定實際的數據引腳 */
    .gpccon = 0xaaaaaaaa, /* PC全部用作LCD功能,VD[7:0], LCD_LPC VM VFRAME VLINE VCLK LEND */
    .gpccon_mask = 0xffffffff,
    .gpcup = 0xffff, /* 禁止GPC內部上拉 */
    .gpcup_mask = 0x0000,
    .gpdcon = 0xaaaaaaaa, /* VD[23:8] */
    .gpdcon_mask = 0xffffffff,
    .gpdup = 0xffff, /* 禁止GPD內部上拉 */
    .gpdup_mask = 0x0000,
#endif
    //.lpcsel = ((0xCE6) & ~7) | 1<<4,
};


static struct platform_device *smdk2440_devices[] __initdata = {
    ...
    &s3c_device_lcd,
    ...
};

下面這步,就是把資源放在platform device裏,並且打開背光燈

static void __init smdk2440_machine_init(void)
{
    s3c24xx_fb_set_platdata(&smdk2440_fb_info); /* 觸摸屏 */
    s3c_i2c0_set_platdata(NULL);
    ...
    /* jz2440 LCD BackLight */
    writel((readl(S3C2410_GPBCON) & ~(3)) | 1, S3C2410_GPBCON); // 初始化背光控制引腳爲輸出
    writel((readl(S3C2410_GPBDAT) | 1), S3C2410_GPBDAT); // 打開背光
}

修改 /etc/inittab

tty1::askfirst:-/bin/sh

 

用新內核重啓開發板

ok

附上一個根據教程自己編寫的LCD驅動,有興趣可以和推薦文章對照着看,增加理解。lcd.c


 
lcd.c
 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章