自学内容网 自学内容网

嵌入式驱动开发详解12(LCD驱动)

前言

LCD 是现在最常用到的显示器,手机、 电脑、各种人机交互设备等基本都用到了 LCD,最常见就是手机和电脑显示器了。通过 LCD 可以显示绚丽的图形、界面等,提高人机交互的效率。

LCD

LCD 的构造是在两片平行的玻璃基板当中放置液晶盒,下基板玻璃上设置 TFT(薄膜晶体 管),上基板玻璃上设置彩色滤光片,通过 TFT 上的信号与电压改变来控制液晶分子的转动方 向,从而达到控制每个像素点偏振光出射与否而达到显示目的。

LCD 简介

  • 分辨率:提起 LCD 显示器,我们都会听到 720P、1080P、2K 或 4K 这样的字眼,这个就是 LCD 显 示器分辨率。LCD 显示器都是由一个一个的像素点组成,像素点就类似一个灯(在 OLED 显示器中,像素点就是一个小灯),这个小灯是 RGB 灯,也就是由 R(红色)、G(绿色)和 B(蓝色)这三 种颜色组成的,而 RGB 就是光的三原色。1080P 的意思就是一个 LCD 屏幕上的像素数量是 1920*1080 个,也就是这个屏幕一列 1080 个像素点,一共 1920 列。
    在这里插入图片描述

    但是并不是分辨率越高的 LCD 就 越好。衡量一款 LCD 的好坏,分辨率只是其中的一个参数,还有色彩还原程度、色彩偏离、亮 度、可视角度、屏幕刷新率等其他参数。

  • 像素格式:一个像素点就相当于一个 RGB 小灯,通过控制 R、G、B 这三种颜色的亮度就 可以显示出各种各样的色彩。一般一个 R、 G、B 这三部分分别使用 8bit 的数据,那么一个像素点就是 8bit*3=24bit,也就是说一个像素点 3 个字节,这种像素格式称为 RGB888。如果再加入 8bit 的 Alpha(透明)通道的话一个像素点就是 32bit,也就是 4 个字节,这种像素格式称为 ARGB8888。如果学习过 STM32 的话应该还听 过 RGB565 这种像素格式。

  • 屏幕接口:LCD 屏幕或者说显示器有很多种接口,比如在显示器上常见的 VGA、HDMI、DP 等等,
    在这里插入图片描述
    R[7:0]、G[7:0]和 B[7:0]这 24 根是数据线,DE、VSYNC、 HSYNC 和 PCLK 这四根是控制信号线。RGB LCD 一般有两种驱动模式:DE 模式和 HV 模式, 这两个模式的区别是 DE 模式需要用到 DE 信号线,而 HV 模式不需要用到 DE 信号线,在 DE 模式下是可以不需要 HSYNC 信号线的,即使不接 HSYNC 信号线 LCD 也可以正常工作。
    以 ATK-7016 这款屏幕为例讲解, ATK-7016 (7 寸,1024*600)的屏幕接口原理图如图:
    在这里插入图片描述
    右侧的几个电阻,并不是都焊接的, 而是可以用户自己选择。默认情况,R1 和 R6 焊接,设置 LCD_LR 和 LCD_UD,控制 LCD 的 扫描方向,是从左到右,从上到下(横屏看)。而 LCD_R7/G7/B7 则用来设置 LCD 的 ID,由于 RGBLCD 没有读写寄存器,也就没有所谓的 ID,这里我们通过在模块上面,控制 R7/G7/B7 的 上/下拉,来自定义 LCD 模块的 ID,帮助 MCU 判断当前 LCD 面板的分辨率和相关参数,以提 高程序兼容性。其中LCD_BL是控制背光灯的,TP开头的是控制触摸屏的。

LCD屏幕时序

  • LCD时间参数:。HSYNC 是水平同步信号,也叫做行同步信号,当产生此信号的话就表示开始显示新的 一行了,所以此信号都是在图的最左边。当 VSYNC 信号是垂直同步信号,也叫做帧同步信号,当产生此信号的话就表示开始显示新的一帧图像了,所以此信号在图的左上角。在这里插入图片描述
    以看到有一圈“黑边”,真正有效的显示区域是中间的白色部分。RGB LCD 屏幕内部是有一个 IC 的,发送一行或者一帧数据给 IC,IC 是需要反应时间的。通过这段反应 时间可以让 IC 识别到一行数据扫描完了,要换行了,或者一帧图像扫描完了,要开始下一帧图 像显示了。因此,在 LCD 屏幕中继续存在 HBP、HFP、VPB 和 VFP 这四个参数的主要目的是 为了锁定有效的像素数据。 当显示完一行以后会发出 HSYNC 信号,此时电子枪就会关闭,然后迅速的移动到屏幕的左边,当 HSYNC 信号结束以后就可以显示新的一行数据了,电子枪就会重新打开。在 HSYNC 信号结束到电子枪重新打开之间会插入一段延时,这段延时就图中的 HBP。当显示完一行以后就会关闭电子枪等待 HSYNC 信号产生,关闭电子枪到 HSYNC 信号产生之间会插入一段延时,这段延时就是图中的 HFP 信号。同理,当显示完一帧图像以后电子枪也会关闭,然后等到 VSYNC 信号产生,期间也会加入一段延时,这段延时就是图中的 VFP。 VSYNC 信号产生,电子枪移动到左上角,当 VSYNC 信号结束以后电子枪重新打开,中间也会加入一段延时,这段延时就是图中的 VBP。

  • LCD屏幕时序
    上面讲了行显示和帧显示,我们来看一下行显示对应的时序图
    在这里插入图片描述
    HSYNC:行同步信号,当此信号有效的话就表示开始显示新的一行数据;HSPW:有些地方也叫做 thp,是 HSYNC 信号宽度,也就是 HSYNC 信号持续时间。HSYNC 信号不是一个脉冲,而是需要持续一段时间才是有效的;HBP和HFP:前面已经说了;HOZVAL:有些地方叫做 thd,显示一行数据所需的时间,假如屏幕分辨率为 1024*600, 那么 HOZVAL 就是 1024;所以显示一行所需要的时间就是:HSPW + HBP + HOZVAL + HFP。

    一帧图像就是由很多个行组成的,RGB LCD 的帧显示时序如图
    在这里插入图片描述
    以上单位都是1行的时间,因此行显示一致,显示一帧所需要的时间就是:VSPW+VBP+LINE+VFP 个行时间。因此显示一帧数据所需要的时间为:T = (VSPW+VBP+LINE+VFP) * (HSPW + HBP + HOZVAL + HFP)
    在这里插入图片描述

单片机eLCDIF接口

eLCDIF 是 I.MX6U 自带的液晶屏幕接口,用于连接 RGB LCD 接口的屏幕
①、支持 RGB LCD 的 DE 模式。
②、支持 VSYNC 模式以实现高速数据传输。
③、支持 ITU-R BT.656 格式的 4:2:2 的 YCbCr 数字视频,并且将其转换为模拟 TV 信号。
④、支持 8/16/18/24/32 位 LCD。
具体的逻辑配置不做详细描述,需要裸机驱动的话可以参考芯片手册。

Linux 下 LCD 驱动简析

Framebuffer 设备

裸机的时候 LCD 驱动是怎么编写的,裸机 LCD 驱动编写流程如下:
①、初始化 I.MX6U 的 eLCDIF 控制器,重点是 LCD 屏幕宽(width)、高(height)、hspw、 hbp、hfp、vspw、vbp 和 vfp 等信息。
②、初始化 LCD 像素时钟。
③、设置 RGBLCD 显存。
④、应用程序直接通过操作显存来操作 LCD,实现在 LCD 上显示字符、图片等信息。

在 Linux 中应用程序最终也是通过操作 RGB LCD 的显存来实现在 LCD 上显示字符、图片 等信息。在裸机中我们可以随意的分配显存,但是在 Linux 系统中内存的管理很严格,显存是 需要申请的,不是你想用就能用的。而且因为虚拟内存的存在,驱动程序设置的显存和应用程 序访问的显存要是同一片物理内存。 为了解决上述问题,Framebuffer 诞生了, Framebuffer 翻译过来就是帧缓冲,简称 fb,因 此大家在以后的 Linux 学习中见到“Framebuffer”或者“fb”的话第一反应应该想到 RGBLCD 或者显示设备。fb 是一种机制,将系统中所有跟显示有关的硬件以及软件集合起来,虚拟出一 个 fb 设备,当我们编写好 LCD 驱动以后会生成一个名为/dev/fbX(X=0~n)的设备,应用程序通 过访问/dev/fbX 这个设备就可以访问 LCD。关于 fb 的详细处理过程就不去深究了,我们的重点是驱动 LCD。

设备树配置

  • NXP 官方的设备树已经添加了 LCD 设备节点,只是此节点的 LCD 屏幕信息是针对 NXP 官方 EVK 开发板所使用的 4.3 寸 480*272 编写的,我们需要将其改为我们所使用的屏幕参数。

  • 我们简单看一下 NXP 官方编写的 Linux 下的 LCD 驱动,打开 imx6ull.dtsi,然后找到 lcdif 节点内容,可以看出 lcdif 节点 的 compatible 属性值为“fsl,imx6ul-lcdif”和“ fsl,imx28-lcdif”,因此在 Linux 源码中搜索这两个 字符串即可找到 I.MX6ULL 的 LCD 驱动文件,这个文件为 drivers/video/fbdev/mxsfb.c。

  • 可以看出该文件是一个标准的 platform 驱动,当驱动和设备匹配以后 mxsfb_probe 函数就会执行 。

  • Linux 内核将所有的 Framebuffer 抽象为一个叫做 fb_info 的结构 体,fb_info 结构体包含了 Framebuffer 设备的完整属性和操作集合,因此每一个 Framebuffer 设 备都必须有一个 fb_info。换言之就是,LCD 的驱动就是构建 fb_info,并且向系统注册 fb_info 的过程。

  • mxsfb_probe 函数实现非常负责,如果不从事LCD驱动相关工作的话没必要了解那么深入,这里我们就简单了解 一下 Linux 下 Framebuffer 驱动的框架。

  • 设备树配置

    pinctrl_lcdif_dat: lcdifdatgrp {
    fsl,pins = <
    MX6UL_PAD_LCD_DATA00__LCDIF_DATA00  0x49
    MX6UL_PAD_LCD_DATA01__LCDIF_DATA01  0x49
    MX6UL_PAD_LCD_DATA02__LCDIF_DATA02  0x49
    MX6UL_PAD_LCD_DATA03__LCDIF_DATA03  0x49
    MX6UL_PAD_LCD_DATA04__LCDIF_DATA04  0x49
    MX6UL_PAD_LCD_DATA05__LCDIF_DATA05  0x49
    MX6UL_PAD_LCD_DATA06__LCDIF_DATA06  0x49
    MX6UL_PAD_LCD_DATA07__LCDIF_DATA07  0x49
    MX6UL_PAD_LCD_DATA08__LCDIF_DATA08  0x49
    MX6UL_PAD_LCD_DATA09__LCDIF_DATA09  0x49
    MX6UL_PAD_LCD_DATA10__LCDIF_DATA10  0x49
    MX6UL_PAD_LCD_DATA11__LCDIF_DATA11  0x49
    MX6UL_PAD_LCD_DATA12__LCDIF_DATA12  0x49
    MX6UL_PAD_LCD_DATA13__LCDIF_DATA13  0x49
    MX6UL_PAD_LCD_DATA14__LCDIF_DATA14  0x49
    MX6UL_PAD_LCD_DATA15__LCDIF_DATA15  0x49
    MX6UL_PAD_LCD_DATA16__LCDIF_DATA16  0x49
    MX6UL_PAD_LCD_DATA17__LCDIF_DATA17  0x49
    MX6UL_PAD_LCD_DATA18__LCDIF_DATA18  0x49
    MX6UL_PAD_LCD_DATA19__LCDIF_DATA19  0x49
    MX6UL_PAD_LCD_DATA20__LCDIF_DATA20  0x49
    MX6UL_PAD_LCD_DATA21__LCDIF_DATA21  0x49
    MX6UL_PAD_LCD_DATA22__LCDIF_DATA22  0x49
    MX6UL_PAD_LCD_DATA23__LCDIF_DATA23  0x49
    >;
    };
    pinctrl_lcdif_ctrl: lcdifctrlgrp {
    fsl,pins = <
    MX6UL_PAD_LCD_CLK__LCDIF_CLK    0x49
    MX6UL_PAD_LCD_ENABLE__LCDIF_ENABLE  0x49
    MX6UL_PAD_LCD_HSYNC__LCDIF_HSYNC    0x49
    MX6UL_PAD_LCD_VSYNC__LCDIF_VSYNC    0x49
    >;
    };
    pinctrl_pwm1: pwm1grp {
    fsl,pins = <
    MX6UL_PAD_GPIO1_IO08__PWM1_OUT   0x110b0
    >;
    };
    
    &lcdif {
    pinctrl-names = "default";
    pinctrl-0 = <&pinctrl_lcdif_dat
         &pinctrl_lcdif_ctrl>;
    /*     &pinctrl_lcdif_reset>;  */
    display = <&display0>;
    status = "okay";
    display0: display {
    bits-per-pixel = <24>;
    bus-width = <24>;
    display-timings {
    native-mode = <&timing0>;
    timing0: timing0 {
    clock-frequency = <51200000>;
    hactive = <1024>;
    vactive = <600>;
    hfront-porch = <160>;
    hback-porch = <140>;
    hsync-len = <20>;
    vback-porch = <20>;
    vfront-porch = <12>;
    vsync-len = <3>;
    hsync-active = <0>;
    vsync-active = <0>;
    de-active = <1>;
    pixelclk-active = <0>;
    };
    };
    };
    };
    
    &pwm1 {
    pinctrl-names = "default";
    pinctrl-0 = <&pinctrl_pwm1>;
    status = "okay";
    };
    backlight {
    compatible = "pwm-backlight";
    pwms = <&pwm1 0 5000000>;
    brightness-levels = <0 4 8 16 32 64 128 255>;
    default-brightness-level = <6>;
    status = "okay";
    };
    

    以上便是对LCD设备树的展示,需要配置好LCD所需要用到的数据引脚,控制引脚以及背光引脚,随后需要对lcd屏幕的相关参数进行设置,并配置好背光引脚,但是linux系统并不知道pwm1_out就是对应LCD的背光引脚,这里我们需要参考设备树绑定文档Documentation/devicetree/indings/video/backlight/pwm-backlight.txt 这个文档,此文档详细讲解了 backlight 节点该如何去创建。

LCD相关系统设置

  1. 将前面设备树小节所修改的文件编译生成新的设备树文件,
  2. Linux 内核启动的时候可以选择显示小企鹅 logo,只要这个小企鹅 logo 显示没问题那么我 们的 LCD 驱动基本就工作正常了,我们可以通过图形化界面使能 Linux logo 显示。
    在这里插入图片描述
  3. 设置 LCD 作为终端控制台
    重启开发板,进入 Linux 命令行,重新设置 bootargs 参数的 console 内容,命令如下所示:
    setenv bootargs 'console=tty1 console=ttymxc0,115200 root=/dev/nfs rw nfsroot=192.168.1.250: /home/zuozhongkai/linux/nfs/rootfs ip=192.168.1.251:192.168.1.250:192.168.1.1:255.255.255.0::eth0: off'
    
    打开开发板根文件系统中的/etc/inittab 文件,在里面加入下面这一行:
    tty1::askfirst:-/bin/sh
    
    修改完成后重启开发板即可。

后续

本文对LCD驱动进行了基本的讲解,主要是讲解了LCD的基本工作原理,以及如何修改LCD的设备树使其适应不同的LCD,对LCD的framebuffer没有深入讲解,需要深入研究的时候会继续更新此文。

参考文献

  1. 个人专栏系列文章
  2. 正点原子嵌入式驱动开发指南
  3. 对代码有兴趣的同学可以查看链接https://github.com/NUAATRY/imx6ull_dev

原文地址:https://blog.csdn.net/weixin_63699001/article/details/144231125

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!