/* 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