【驱动大全】二、LCD显示子系统
【驱动大全】二、LCD显示子系统
本文最后更新于305 天前,其中的信息可能已经过时,如有错误请发送邮件到273925452@qq.com
Avatar
整理自韦老师驱动大全学习文档,配合视频使用;

准备

  • 阅读LCD芯片数据手册
  • LCD显示模块规格书
  • 会看原理图
  • 视频配套代码:IMX6ULL\source\03_LCD
  • 内核自带的IMX6ULL LCD驱动程序
    • 驱动源码:Linux-4.9.88\drivers\video\fbdev\mxsfb.c
    • 设备树:
      • arch/arm/boot/dts/imx6ull.dtsi
      • arch/arm/boot/dts/100ask_imx6ull-14×14.dts
  • 芯片资料
    • IMX6ULL\开发板配套资料\datasheet\Core_board\CPU\IMX6ULLRM.pdf
      • 《Chapter 34 Enhanced LCD Interface (eLCDIF)》
  • IMX6ULL的LCD裸机程序
    • IMX6ULL\source\03_LCD\05_参考的裸机源码\03_font_test


查看厂家已经支持的LCD驱动

1.内核路径:/drivers/video/fbdev ls *.o

2.内核路径:/Linux-4.9.88 make menuconfig

/Device Drivers/Graphics support/Frame buffer Devices


不同接口的LCD硬件

像素:

像素的颜色怎么表示:

用红绿蓝三颜色来表示,可以用24位数据来表示红绿蓝,也可以用16位等等格式,比如:

  • bpp:bits per pixel,每个像素用多少位来表示
  • 24bpp:实际上会用到32位,其中8位未使用,其余24位中分别用8位表示红(R)、绿(G)、蓝(B)
  • 16bpp:有rbg565,rgb555
    • rgb565:用5位表示红、6位表示绿、5位表示蓝
    • rgb555:16位数据中用5位表示红、5位表示绿、5位表示蓝,浪费一位

怎么把颜色发给LCD:

假设每个像素的颜色用16位来表示,那么一个LCD的所有像素点假设有xres * y res个, 需要的内存为:xres * yres * 16 / 8,也就是要设置所有像素的颜色,需要这么大小的内存。 这块内存就被称为framebuffer:

  • Framebuffer中每块数据对应一个像素
  • 每块数据的大小可能是16位、32位,这跟LCD上像素的颜色格式有关
  • 设置好LCD硬件后,只需要把颜色数据写入Framebuffer即可
a

统一的LCD硬件模型:

2

不同接口:

MCU常用的8080接口LCD模组;

LCD(Liquid Crystal Display)的8080接口是一种并行通信接口标准,它得名于早期微处理器Intel 8080。这种接口允许LCD控制器与微处理器之间进行直接的数据交换,而不需要额外的控制逻辑。8080接口通常用于较大型的LCD显示模块,尤其是那些需要快速数据传输和高分辨率的应用中。

8080接口的主要特点包括:

  1. 并行数据总线:通常使用8位或16位并行数据总线来传输数据,这比串行接口快得多,因为一次可以传输多个比特。
  2. 独立的读写控制:有单独的读和写控制信号,允许微处理器读取LCD的状态或向LCD写入数据。
  1. 地址/数据复用:在一些设计中,地址和数据可能共享相同的总线,通过地址/数据复用(A/D)信号来区分。
  1. 控制信号:除了数据总线外,还需要一些控制信号线,如片选(CS)、读/写(R/W)、使能(E)等,以协调数据的传输。
  1. 时序要求:8080接口有特定的时序要求,包括设置时间和保持时间,确保数据在传输过程中被正确捕获。

由于其较高的数据传输速率,8080接口在过去非常流行,但在现代设备中,由于成本、功耗和引脚数量的考虑,更倾向于使用SPI、I2C等串行接口。然而,在一些高性能或专业应用中,8080接口仍然被使用。

MPU常用的TFT RGB接口;

TFT(Thin Film Transistor)LCD屏幕的RGB接口是一种直接传输红绿蓝三基色信号的并行接口。这种接口用于将图像数据从主控芯片(通常是微控制器或图形处理器)传输到TFT LCD面板,以显示图像。RGB接口可以提供高质量的图像,因为它直接传输颜色信息,而不是经过压缩或转换。
RGB接口的关键组成部分包括:
1.数据线:

这些线分别传输红色、绿色和蓝色的信号。数据线的数量取决于接口的位深,例如6-bit、16-bit、18-bit或24-bit。例如,一个24-bit的RGB接口会有8条线分别对应红、绿、蓝三种颜色的8-bit数据。
2.同步信号:

  • 垂直同步信号(VSYNC):表示一行扫描完成,新的一行开始。
  • 水平同步信号(HSYNC):表示一帧扫描完成,新的一帧开始。
  • 数据使能信号(DE):指示何时数据线上的数据是有效的。

3.控制信号:

  • 片选信号(CS):选择特定的LCD模块或芯片。
  • 数据/命令选择(DC):指示接下来的数据是数据还是命令。


4.电源线:

包括VCC(正电压)和GND(地),以及可能的LED背光控制线。
TFT RGB接口的时序非常重要,必须严格遵守,以确保图像数据正确地显示在屏幕上。时序包括数据的有效期、同步信号的宽度和间隔等。
使用RGB接口的TFT LCD屏幕常见于需要高分辨率和色彩准确性的应用,如工业控制面板、高端消费电子产品和专业监视器。然而,RGB接口的使用也意味着更高的成本和复杂性,因为需要较多的引脚和信号线,这在空间受限或成本敏感的设计中可能不是最佳选择。因此,对于一些应用,更简单的接口如SPI、I2C或串行RGB(如LVDS)可能会被选用。

MIPI标准:

MIPI表示Mobile Industry Processor Interface,即移动产业处理器接口。是MIPI联盟发起的为移动应用处理器制定的开放标准和一个规范。主要是手机内部的接口(摄像头、显示屏接口、射频/基带接口)等标准化,从而减少手机内部接口的复杂程度及增加设计的灵活性。

对于LCD,MIPI接口可以分为3类:

  • MIPI-DBI (Display Bus Interface)
    • 既然是Bus(总线),就是既能发送数据,也能发送命令,常用的8080接口就属于DBI接口。
    • Type B (i-80 system), 8-/9-/16-/18-/24-bit bus
    • Type C (Serial data transfer interface, 3/4-line SPI)
  • MIPI-DPI (Display Pixel Interface)
    • Pixel(像素),强调的是操作单个像素,在MPU上的LCD控制器就是这种接口
    • Supports 24 bit/pixel (R: 8-bit, G: 8-bit, B: 8-bit)
    • Supports 18 bit/pixel (R: 6-bit, G: 6-bit, B: 6-bit)
    • Supports 16 bit/pixel (R: 5-bit, G: 6-bit, B: 5-bit)
  • MIPI-DSI (Display Serial Interface)
    • Serial,相比于DBI、DPI需要使用很多接口线,DSI需要的接口线大为减少
    • Supports one data lane/maximum speed 500Mbps
    • Supports DSI version 1.01
    • Supports D-PHY version 1.00

Frambuffer驱动程序框架

介绍:

Linux的帧缓冲(framebuffer)驱动程序框架是一个用于支持各种显示设备的标准接口。它将显存抽象成一种设备,允许用户空间的应用程序直接访问显示缓冲区进行读写操作,从而实现图形输出。

分为上下两层:

  • fbmem.c:承上启下
    • 实现、注册file_operations结构体
    • 把APP的调用向下转发到具体的硬件驱动程序
  • xxx_fb.c:硬件相关的驱动程序
    • 实现、注册fb_info结构体
    • 实现硬件操作

驱动核心:

分配 struct fb_info:

  • framebuffer_alloc
  • 这是帧缓冲的核心数据结构,包含了设备的各种信息,如屏幕大小、颜色深度、缓冲区地址等。

设置fb_info:

  • var
  • struct fb_var_screeninfo: 包含了可变的屏幕信息,如分辨率、位深度等。
  • struct fb_fix_screeninfo: 包含了固定不变的屏幕信息,如物理尺寸、视频内存地址等。
  • fbops:
  • 这是file_operations结构体的一个子集,专门用于处理帧缓冲设备的特定操作,如fb_read()、fb_write()等。
  • 硬件相关操作

注册fb_info

register_framebuffer


不同接口时序分析

8080接口LCD时序分析

以LCD_3.5寸_320x480_ILI9488液晶显示模块为例

8080接口原理图:

8080接口:

时序图:ILI9488驱动芯片数据手册第325页:

时序最小时间要求:

步骤:因为STM32系列中的高端型号一般含有FSMC(Flexible Static Memory Controller,灵活静态存储器控制器);

TFT-RGB接口LCD时序分析

LCD数据手册参考:7.0-13SPEC(7寸1024600TN-RGB)

水平和垂直时序图:

时间延迟要求:

电子枪模型:

  • 每来一个DCLK,电子枪向右移动一点;
  • 到达1这个位置后,通过水平同步脉冲信号HSD控制电子枪到达2这个位置;
  • 当电子枪移动到3这个位置后,通过垂直同步脉冲信号VSD控制控制电子枪回到原点位置;

IMX6ULL的LCD控制器

介绍:

LCD控制器是专门设计用于控制和驱动LCD(Liquid Crystal Display,液晶显示器)显示内容的集成电路或子系统。它在嵌入式系统中扮演着类似计算机显卡的角色,负责将存储在显存中的图像数据转换成适当的信号,然后发送给LCD面板,以实现图像的显示。

LCD控制器的主要功能包括:

  1. 数据传输:从系统的内存中读取图像数据,转换成适合LCD面板的信号格式。
  2. 控制信号生成:生成必要的控制信号,如时序信号、刷新率信号等,确保LCD面板正确地显示图像。
  3. 显示参数配置:支持不同的显示模式和分辨率,可以根据不同的LCD屏幕规格进行配置,比如像素数量、灰度级别、色彩深度等。
  4. 图形处理:可能包含一些基本的图形处理能力,如图像缩放、旋转、翻转和颜色空间转换等。
  5. 接口连接:提供与主机处理器或其他组件的接口,如8080接口、SPI、I2C、RGB或TFT-LCD接口等。
  6. 省电模式:在不显示图像时,可以进入低功耗模式以节省电力。

不同类型和规模的LCD控制器可以支持不同的LCD类型,包括段式LCD、字符型LCD、图形LCD和TFT-LCD等。例如,有些控制器专门设计用于驱动TFT-LCD,它们通常具备更高级的特性,如高分辨率支持和更快的刷新率。

在嵌入式系统中,LCD控制器可以是独立的芯片,也可以集成在微控制器、微处理器或SoC(System on Chip,系统级芯片)中。例如,S3C44B0X和S3C2440就是包含LCD控制器的SoC,可以支持不同分辨率和色彩深度的LCD屏幕。

IMX6ULL的LCD控制器:

参考资料:IMX6ULL\开发板配套资料\datasheet\Core_board\CPU\IMX6ULLRM.pdf

  • 《Chapter 34 Enhanced LCD Interface (eLCDIF)》

硬件框图:

IMX6ULL的LCD控制器名称为eLCDIF(Enhanced LCD Interface,增强型LCD接口),主要特性如下:

  • 支持MPU模式:有些显示屏自带显存,只需要把命令、数据发送给显示屏即可;就是前面讲的8080接口
  • VSYNC模式:跟MPU模式类似,多了VSYNC信号。针对高速数据传输(行场信号)
  • 支持DOTCLK模式:RGB接口,就是前面讲的TFT-RGB接口
  • 支持ITU-R BT.656接口,可以把4:2:2 YcbCr格式的数据转换为模拟电视信号
  • 8/16/18/24/32 bit 的bpp数据都支持,取决于IO的复用设置及寄存器配置
  • MPU模式,VSYNC模式,DOTCLK模式,都可以配置时序参数。

上图是IMX6ULL的LCD控制器框图。 我们在内存中划出一块内存,称之为显存,软件把数据写入显存。 设置好LCD控制器之后,它会通过AXI总线协议从显存把RGB数据读入FIFO,再到达LCD接口(LCD Interface)。 LCD控制器有两个时钟域:外设总线时钟域,LCD像素时钟域。前者是用来让LCD控制器正常工作,后者是用来控制电子枪移动。 上图的Read_Data操作,在MPU模式下才用到;我们采用的是DCLK模式,因此不予考虑。更详细的内容可以查看IMX6ull芯片手册《Chapter 34 Enhanced LCD Interface (eLCDIF)》。

数据传输与处理:

P213

LCD控制器寄存器简介:

查看任何芯片的LCD控制器寄存器时,记住几个要点:

① 怎么把LCD的信息告诉LCD控制器:即分辨率、行列时序、像素时钟等; ② 怎么把显存地址、像素格式告诉LCD控制器。

上图是我们将要使用到的寄存器,点击上图右边Section/page自动跳转到对应寄存器详细信息;

P126,34.6 eLCDIF Memory Map/Register Definition-eLCDIF内存映射/寄存器定义


LCD驱动程序分析-IMX6ULL

mxsfb.c

LCD驱动程序框架

驱动程序框架+硬件操作

  • 分配 fb_info
  • 设置 fb_info
  • 注册 fb_info
  • 硬件相关的设置(参考裸机代码):
    • 引脚设置
    • 时钟设置
    • LCD控制器设置

配置引脚

查看LCD原理图确定需要使用的所有引脚;

使用pinctrl子系统配置引脚(有专门工具生成);

放入设备树

根节点下添加子节点信息:

配置时钟

阅读芯片手册找到需要配置的Clocks;

给设备树子节点添加时钟信息;

参考:arch/arm/boot/dts/imx6ull.dtsi

定义了3个时钟:

  • pix:Pixel clock,用于LCD接口,设置为LCD手册上的参数
  • axi:AXI clock,用于传输数据、读写寄存器,使能即可
  • disp_axi:一个虚拟的时钟,可以不用设置
                       lcdif: lcdif@021c8000 {
                                compatible = "fsl,imx6ul-lcdif", "fsl,imx28-lcdif";
                                reg = <0x021c8000 0x4000>;
                                interrupts = <GIC_SPI 5 IRQ_TYPE_LEVEL_HIGH>;
                                clocks = <&clks IMX6UL_CLK_LCDIF_PIX>,
                                         <&clks IMX6UL_CLK_LCDIF_APB>,
                                         <&clks IMX6UL_CLK_DUMMY>;
                                clock-names = "pix", "axi", "disp_axi";
                                status = "disabled";
                        };

驱动获得时钟:

host->clk_pix = devm_clk_get(&host->pdev->dev, "pix");
    if (IS_ERR(host->clk_pix)) {
        host->clk_pix = NULL;
        ret = PTR_ERR(host->clk_pix);
        goto fb_release;
    }

    host->clk_axi = devm_clk_get(&host->pdev->dev, "axi");
    if (IS_ERR(host->clk_axi)) {
        host->clk_axi = NULL;
        ret = PTR_ERR(host->clk_axi);
        dev_err(&pdev->dev, "Failed to get axi clock: %d\n", ret);
        goto fb_release;
    }

    host->clk_disp_axi = devm_clk_get(&host->pdev->dev, "disp_axi");
    if (IS_ERR(host->clk_disp_axi)) {
        host->clk_disp_axi = NULL;
        ret = PTR_ERR(host->clk_disp_axi);
        dev_err(&pdev->dev, "Failed to get disp_axi clock: %d\n", ret);
        goto fb_release;
    }

设置频率:只需要设置pixel clock的频率

ret = clk_set_rate(host->clk_pix,
                PICOS2KHZ(fb_info->var.pixclock) * 1000U);

使能时钟

clk_enable_pix(host);
            clk_prepare_enable(host->clk_pix);
        clk_enable_axi(host);
            clk_prepare_enable(host->clk_axi);
        clk_enable_disp_axi(host);
            clk_prepare_enable(host->clk_disp_axi);

配置LCD控制器之获得LCD参数

在设备树里指定LCD参数

framebuffer-mylcd {
            compatible = "100ask,lcd_drv";
            pinctrl-names = "default";
            pinctrl-0 = <&mylcd_pinctrl>;
            backlight-gpios = <&gpio1 8 GPIO_ACTIVE_HIGH>;

            clocks = <&clks IMX6UL_CLK_LCDIF_PIX>,
                     <&clks IMX6UL_CLK_LCDIF_APB>;
            clock-names = "pix", "axi";
            
            display = <&display0>;

            display0: display {
                bits-per-pixel = <24>;
                bus-width = <24>;

                display-timings {
                    native-mode = <&timing0>;

                     timing0: timing0_1024x768 {
                     clock-frequency = <50000000>;
                     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>;
                     };

                };
            };            
    };

从设备树获得参数

参考内核文件:

  • drivers\video\of_display_timing.c
  • drivers\video\fbdev\mxsfb.c

配置LCD控制器之寄存器操作

配合视频食用

单buffer和多buffer区别

源码

  • 内核自带的LCD驱动程序
    • IMX6ULL驱动源码:Linux-4.9.88\drivers\video\fbdev\mxsfb.c
    • STM32MP157的驱动程序是基于GPU的,在Linux-5.4里没有mxsfb.c,可以参考另一个:
      • Linux-5.4\drivers\video\fbdev\goldfishfb.c
    • 在视频里基于IMX6ULL的mxsfb.c来讲解,我们把这个驱动程序也放到GIT仓库里
      • IMX6ULL\source\03_LCD\12_lcd_drv_imx6ull_from_kernel_4.9.88
      • STM32MP157\source\A7\03_LCD\12_lcd_drv_imx6ull_from_kernel_4.9.88
    • 使用多buffer的APP参考程序,在GIT仓库中
      • IMX6ULL\source\03_LCD\13_multi_framebuffer_example\testcamera
      • STM32MP157\source\A7\03_LCD\13_multi_framebuffer_example\testcamera

单Buffer的缺点

  • 如果APP速度很慢,可以看到它在LCD上缓慢绘制图案
  • 即使APP速度很高,LCD控制器不断从Framebuffer中读取数据来显示,而APP不断把数据写入Framebuffer
    • 假设APP想把LCD显示为整屏幕的蓝色、红色
    • 很大几率出现这种情况:
      • LCD控制器读取Framebuffer数据,读到一半时,在LCD上显示了半屏幕的蓝色
      • 这是APP非常高效地把整个Framebuffer的数据都改为了红色
      • LCD控制器继续读取数据,于是LCD上就会显示半屏幕蓝色、半屏幕红色
      • 人眼就会感觉到屏幕闪烁、撕裂

使用多Buffer来改进

上述两个缺点的根源是一致的:Framebuffer中的数据还没准备好整帧数据,就被LCD控制器使用了。 使用双buffer甚至多buffer可以解决这个问题:

  • 假设有2个Framebuffer:FB0、FB1
  • LCD控制器正在读取FB0
  • APP写FB1
  • 写好FB1后,让LCD控制器切换到FB1
  • APP写FB0
  • 写好FB0后,让LCD控制器切换到FB0

流程如下:

驱动:分配多个buffer

fb_info->fix.smem_len = SZ_32M;
fbi->screen_base = dma_alloc_writecombine(fbi->device,
                fbi->fix.smem_len,
                (dma_addr_t *)&fbi->fix.smem_start,
                GFP_DMA | GFP_KERNEL);

驱动:保存buffer信息

fb_info->fix.smem_len  // 含有总buffer大小 
fb_info->var           // 含有单个buffer信息

APP:读取buffer信息

ioctl(fd_fb, FBIOGET_FSCREENINFO, &fix);
ioctl(fd_fb, FBIOGET_VSCREENINFO, &var);

// 计算是否支持多buffer,有多少个buffer
screen_size = var.xres * var.yres * var.bits_per_pixel / 8;
nBuffers = fix.smem_len / screen_size;

APP:使能多buffer

var.yres_virtual = nBuffers * var.yres;
ioctl(fd_fb, FBIOPUT_VSCREENINFO, &var);

APP:写buffer

fb_base = (unsigned char *)mmap(NULL , fix.smem_len, PROT_READ | PROT_WRITE, MAP_SHARED, fd_fb, 0);

/* get buffer */
pNextBuffer =  fb_base + nNextBuffer * screen_size;

/* set buffer */
lcd_draw_screen(pNextBuffer, colors[i]);

APP:开始切换buffer

/* switch buffer */
var.yoffset = nNextBuffer * var.yres;
ioctl(fd_fb, FBIOPAN_DISPLAY, &var);

驱动:切换buffer

// fbmem.c
fb_ioctl
    do_fb_ioctl
        fb_pan_display(info, &var);
            err = info->fbops->fb_pan_display(var, info) // 调用硬件相关的函数    

      

APP:等待切换完成(在驱动程序中已经等待切换完成了,所以这个调用并无必要)

ret = 0;
ioctl(fd_fb, FBIO_WAITFORVSYNC, &ret);
💡商业转载请联系作者获得授权,非商业转载请注明出处。
协议(License):署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0)。
使用这些免费资源的时候应遵守版权意识,切勿非法利用,售卖,尊重原创内容知识产权。未经允许严禁转载。

评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇