(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
 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值