首页 > 系统相关 >Linux驱动开发之LCD显示和触摸

Linux驱动开发之LCD显示和触摸

时间:2024-11-29 15:28:31浏览次数:13  
标签:200 00 触摸 LCD 240 Linux 230

目录

LCD屏幕显示

LCD相关参数

Framebuffer

DRM驱动框架

LCD屏幕触摸

MT协议

LCD屏幕显示

LCD(Liquid Crystal Display),即液晶显示器,是一种数显技术,可以通过液晶和彩色过滤器过滤光源并在平面板上产生图像,是现在最常用到的显示器。而液晶本身不能发光,只能通过对光线的穿透和反射来实现显示功能,属于被动显示方式。因此,LCD都需要背光来照亮液晶,通常背光电路和LCD做在一起。背光的控制通过PWM完成,PWM 全称是 Pulse Width Modulation,也就是脉冲宽度调制。通过给LCD 的背光引脚输入一个 PWM 信号,这样就可以通过调整占空比的方式来调整LCD 背光亮度了。LCD 的构造简单来说是在两片平行的玻璃基板当中放置液晶盒,下基板玻璃上设置 TFT(薄膜晶体管),上基板玻璃上设置彩色滤光片,通过 TFT 上的信号与电压改变来控制液晶分子的转动方向,从而达到控制每个像素点偏振光出射与否而达到显示目的。LCD显示器按物理结构可分为TN-LCD、STN-LCD、DSYN-LCD和TFT-LCD,具体实现原理不作介绍。

LCD相关参数
  1. 提到显示器,都会有 720P、1080P、2K 或 4K 这样的参数,这个就是分辨率。LCD显示器都是由一个一个的像素点组成,1080P 的意思就是一个LCD屏幕上的像素点数量是1920*1080 个,也就是这个屏幕一列 1080 个像素点,一共 1920 列。

  2. 一个像素点就相当于一个RGB小灯,通过控制 R、G、B 这三种颜色的亮度就可以显示出各种各样的色彩。一般R、G、B 这三部分分别使用 8bit 的数据表示,一个像素点就是24bit,也就是说一个像素点3 个字节,这种像素格式称为 RGB888。当再加入 8bit 的 Alpha(透明)通道的话一个像素点就是 32bit,也就是 4 个字节,这种像素格式称为 ARGB8888。

  3. LCD 屏幕支持很多种接口,比如在显示器上常见的 VGA、HDMI、eDP和MIPI-DSI等。

  4. 像素时钟指 LCD 的时钟信号,它和屏幕的时间参数和显示时序有关,是屏幕出厂就必须设定好的参数,这些参数需要在设备树中进行相应配置。

  5. 如果采用 ARGB8888 格式的话一个像素需要 4 个字节的内存来存放像素数据,那么 1024*600 分辨率就需要 1024*600*4=2457600B≈2.4MB 内存。但是 LCD 内部是没有内存的,所以就需要在开发板上分出一段内存作为LCD屏幕的显存,应用程序一般通过操作显存来控制LCD的显示。

Framebuffer

Frame即帧,buffer是缓冲的意思,那么Framebuffer表示的就是帧缓冲,是一块内存,里面保存着一帧图像。Framebuffer是Linux内核提供的一种显示驱动接口,它将显示设备进行抽象,虚拟出一个fb设备,当我们编写好LCD驱动以后会生成一个名为/dev/fbX的设备节点,应用程序通过访问/dev/fbX这个设备节点就可以访问LCD。Linux内核将所有的Framebuffer抽象为一个叫做 fb_info 的结构体,fb_info 结构体包含了 Framebuffer 设备的完整属性和操作集合,因此每一个 Framebuffer 设备都必须有一个 fb_info。

DRM驱动框架

DRM驱动框架是Rockchip平台规定的显示子系统框架DRM 全称是 Direct Rendering Manager,进行显示输出管理、buffer 分配、帧缓冲等。其显示通路如下:

其中,CRTC指显示控制器,在rockchip平台下则是SOC内部的VOP模块或者VOP2 中 Video Port 的抽象。Encoder则是输出转换器,指RGB、LVDS、DSI、eDP、DP、HDMI、CVBS、VGA等显示接口。整个DRM 驱动是一系列显示相关模块驱动的结合,包含了backlight、panel、mipi、lvds、dsi、edp、lvds、hdmi、vop 等显示通路上的依赖模块。只有这些相互依赖的模块都加载起来,整个DRM系统才能启动成功。显示驱动在 U-Boot 中主要提供开机 logo 显示和充电界面显示这两个功能,开机 logo ⼀般分为两个阶段:显示U-Boot logo 和 显示Kernel logo。

在每⼀个支持DRM显示功能的soc核心设备树文件中,都会有display_subsystem节点。该节点控制 rockchip_drm_drv.c 驱动的加载,主要是对开机logo显示的配置。其中,connect 属性描述该显示接口和 VOP 的哪个 Video Port(VOP2.0 架构) 或者哪个 VOP(VOP1.0 架构)连接。而vop节点则用来描述VOP 硬件资源,控制着vop驱动 rockchip_drm_vop.c/rockchip_drm_vop2.c的加载。RK3588显示通路连接关系如下:

RK3588采用VOP2.0 架构,其内部包含四路独立的Video Port, 所以在最基础的条件下,RK3588 可以实4 路独立的显示输出。其中,VOP2.0提供Connector-mirror 技术支持⼀个 Video Port 同时驱动多路显示接口,使其输出相同的显示时序并显示相同的内容。在RK3588 上,通过connector-mirror 技术,把两路HDMI/eDP 连接在VP0 上,把两路DP 连接在VP1 上,把两路MIPI DSI 连接在VP2 上,VP3 通过BT656,BT1120 可以同时输出7 路,四组独立的显示输出,其中每一组(同一个VIdeo Port 上的两个显示接口)输出的显示时序相同,且显示内容相同。

除此之外,VOP2 提供的 vop-split 功能则是⼀种类似 mipi 双通道模式的技术,可以让⼀路 Video Port 输出按水平方向平分成左右两路,同时驱动两个显示接口,显示时序相同,内容独立的画面。需要注意的是,每⼀个 VP 上参与 split 输出的两个显示接口,输出的时序,帧率必须相同。

此次实验使用的是eDP接口的屏幕,设备树配置如下:

 panel-edp0 {

        compatible = "simple-panel";

        backlight = <&backlight_edp>;

        power-supply = <&vcc3v3_lcd_n>;

        init-delay-ms = <120>;

        prepare-delay-ms = <120>;

        enable-delay-ms = <120>;

        unprepare-delay-ms = <120>;

        disable-delay-ms = <120>;

        width-mm = <129>;

        height-mm = <171>;

  panel-timing {

                        clock-frequency = <150000000>;

                        hactive = <1920>;

                        vactive = <1080>;

                        hfront-porch = <160>;

                        hsync-len = <32>;

                        hback-porch = <160>;

                        vfront-porch = <3>;

                        vsync-len = <5>;

                        vback-porch = <23>;

                        hsync-active = <0>;

                        vsync-active = <0>;

                        de-active = <0>;

                        pixelclk-active = <0>;

        };

     port {

            panel_in_edp0: endpoint {

                remote-endpoint = <&edp0_out_panel>;

            };

        };

    };

};

&backlight_edp {

        compatible = "pwm-backlight";

        pwms = <&pwm0 0 25000 1>;

        status = "okay";

        brightness-levels = <

            80 82 84 86 88 90 92 94

            100 100 100 100 100 100 100 100

            110 110 110 110 110 110 110 110

            120 120 120 120 120 120 120 120

            130 130 130 130 130 130 130 130

            140 150 150 150 150 150 150 150

            170 170 170 170 170 170 170 170

            170 170 170 170 170 170 170 170

            180 180 180 180 180 180 180 180

            180 180 180 180 180 180 180 180

            190 190 190 190 190 190 190 190

            190 190 190 190 190 190 190 190

            200 200 200 200 200 200 200 200

            200 200 200 200 200 200 200 200

            200 200 200 200 200 200 200 200

            200 200 200 200 200 200 200 200

            200 200 200 200 200 200 200 200

            200 200 200 200 200 200 200 200

            200 200 200 200 200 200 200 200

            210 210 210 210 210 210 210 210

            220 220 220 220 220 220 220 220

            220 220 220 220 220 220 220 220

            220 220 220 220 220 220 220 220

            230 230 230 230 230 230 230 230

            230 230 230 230 230 230 230 230

            230 230 230 230 230 230 230 230

            240 240 240 240 240 240 240 240

            240 240 240 240 240 240 240 240

            240 240 240 240 240 240 240 240

            240 240 240 240 240 240 240 240

            240 241 242 243 244 245 246 247

            248 249 250 251 252 253 254 255

        >;

        default-brightness-level = <200>;

};

&edp0 {

    force-hpd;

    status = "okay";

    ports {

        port@1 {

            reg = <1>;

            edp0_out_panel: endpoint {

                remote-endpoint = <&panel_in_edp0>;

            };

        };

    };

};

&route_edp0 {

  status = "okay";

    connect = <&vp0_out_edp0>;

};

&edp0_in_vp0 {

    status = "okay";

};

&edp0_in_vp1 {

    status = "disabled";

};

&edp0_in_vp2 {

    status = "disabled";

};

&hdptxphy0 {

    status = "okay";

};

其中,panel-edp0是panel相关配置,根据不同的显示接口使用不同的panel配置。panel-timing中即是时间参数、显示时序的配置。backlight_edp中则是PWM背光相关配置。从设备树来看,该屏幕配置为vp0上的edp0通道输出显示信号。最终点亮屏幕启动logo如下:

LCD屏幕触摸

触摸屏实际上并不完全指显示的屏幕,可以简单理解为覆盖在显示屏表面的一层触摸面板,主要用于提供方便的人机交互功能。根据触摸屏的工作原理和传输信息的介质不同主要可以分为电阻触摸屏和电容触摸屏,和电阻触摸屏相比,电容触摸屏最大的优点是支持多点触摸。

电容触摸屏中设有驱动IC,也就是触摸芯片,驱动IC一般会提供一个I2C接口给主控制器,主控制器通过I2C来读取驱动IC里面的触摸点坐标数据等信息。除此之外,触摸IC还提供了中断引脚,可以通过中断来获取信息。此次使用的eDP屏幕集成了gt911触摸芯片,支持多点触摸。

触摸屏设备是一个绝对位移设备,可以上报绝对位移事件。单点触摸设备只支持单点触摸,一轮完整的数据只包含一个触摸点信息;单点触摸设备以 ABS_XXX 事件承载、上报触摸点的信息。对于多点触摸设备,一轮完整的数据可能包含多个触摸点信息。多点触摸设备则是以 ABS_MT_XXX事件承载、上报触摸点的信息。

MT协议

Linux内核支持多点触摸协议,即MT协议。MT 协议分为两种类型,TypeA 和 TypeB,Type A适用于触摸点不能被区分或者追踪的设备;Type B适用于有硬件追踪能力并能区分触摸点的触摸设备,此类型设备通过 slot 更新某一个触摸点的信息。驱动中触摸点的信息通过一系列的 ABS_MT 事件上报给linux内核,ABS_MT 事件定义在文件 include/uapi/linux/input.h中。并且,Type B设备驱动可以通过slot的 ABS_MT_TRACKING_ID 来新增、替换或删除触摸点。一个非负数的ID表示一个有效的触摸点,-1则表示未使用 slot。

触摸屏设备除了上报绝对位移事件之外,还可以上报按键类事件和同步类事件。同步事件用于告知应用层本轮数据是否完整;当手指点击触摸屏或手指从触摸屏离开时,此时就会上报按键类事件,用于描述按下触摸屏和松开触摸屏;具体的按键事件为BTN_TOUCH(code=0x14a,也就是 330),手指在触摸屏上滑动不会上报 BTN_TOUCH 事件。

由此可见,屏幕触摸驱动是由I2C驱动框架,中断和input子系统等组合而成的。该触摸屏设备树配置如下:

&i2c6 {

    status = "okay";

    pinctrl-names = "default";

    pinctrl-0 = <&i2c6m0_xfer>;

    goodix_ts:goodix_ts@5d {

        status = "okay";

        compatible = "goodix,gt9xx";

        reg = <0x5d>;

        gtp_resolution_x = <1920>;

        gtp_resolution_y = <1080>;

        gtp_int_tarigger = <1>;

        gtp_change_x2y = <0>;

        gtp_overturn_x = <0>;

        gtp_overturn_y = <0>;

        gtp_send_cfg = <1>;

        gtp_touch_wakeup = <1>;

        goodix,cfg-group0 = [

         43 80 07 38 04 0A 3D 00 01 06

         28 08 55 32 03 05 00 00 00 00

         00 00 06 18 1A 1E 14 95 35 FF

         2D 2F A6 0F 00 00 00 01 03 2C

         00 00 00 00 00 00 00 00 00 00

         00 2D 5A 94 D0 42 00 08 00 04

         79 30 00 6E 37 00 65 3F 00 5D

         49 00 57 54 00 57 00 00 00 00

         00 00 00 00 00 00 00 00 00 00

         00 00 00 00 00 00 00 00 00 00

         00 00 00 00 00 00 00 00 00 00

         00 00 1D 1C 1B 1A 19 18 17 16

         15 14 13 12 11 10 0F 0E 0D 0C

         0B 0A 09 08 07 06 05 04 03 02

         01 00 00 01 02 03 04 05 06 07

         08 09 0A 0B 0C 0D 0E 0F 10 11

         12 13 14 15 16 17 18 19 1B 1C

         1D 1E 1F 20 21 22 23 24 25 26

         27 28 29 2A 86 01

        ];

    };

};

其中,gtp_resolution_x/y是屏幕分辨率配置;gtp_int_tarigger设置中断触发方式,0 代表上升沿触发,1 代表下降沿触发;gtp_change_x2y设置是否交换 x,y 轴触摸,0 代表不交换,1 代表交换;gtp_overturn_x设置是否调转 x 轴方向,0 代表不调转,1 代表调转;gtp_overturn_y设置是否调转 y 轴方向,0 代表不调转,1 代表调转;gtp_send_cfg设置 cfg 是否下发,0 代表不下发,1 代表下发;gtp_touch_wakeup设置触摸唤醒,0 代表关闭触摸唤醒,1 代表开启触摸唤醒。

编写应用程序读实现取触摸驱动上报的input结构体信息,解析并打印触摸点状态、坐标等信息。

#include <stdio.h>

#include <stdlib.h>

#include <sys/types.h>

#include <sys/stat.h>

#include <fcntl.h>

#include <unistd.h>

#include <linux/input.h>

int main(int argc, char *argv[])

{

    struct input_event in_ev;

    int x, y; //触摸点的坐标

    int down; //用于记录BTN_TOUCH,1 表示点击,0 表示离开,-1 表示滑动

    int valid; //用于记录数据是否有效

    int fd = -1;

    int id;

    if (2 != argc) {

        fprintf(stderr, "usage: %s <input-dev>\n", argv[0]);

        exit(EXIT_FAILURE);

    }

    if (0 > (fd = open(argv[1], O_RDONLY))) {

        perror("open error");

        exit(EXIT_FAILURE);

    }

    x = y = 0;

    down = -1;

    valid = 0;

    id = -1;

    for ( ; ; ) {

        if (sizeof(struct input_event) !=

            read(fd, &in_ev, sizeof(struct input_event))) {

            perror("read error");

            exit(EXIT_FAILURE);

        }

        switch (in_ev.type) {

        case EV_KEY:

            if (BTN_TOUCH == in_ev.code) {

            down = in_ev.value;

            valid = 1;

            }

            break;

        case EV_ABS:

            switch (in_ev.code) {

            case ABS_MT_POSITION_X:

                x = in_ev.value;

                valid = 1;

                break;

            case ABS_MT_POSITION_Y:

                y = in_ev.value;

                valid = 1;

                break;

            case ABS_MT_TRACKING_ID:

                id = in_ev.value;

                valid = 1;

            }

            break;

        case EV_SYN:

            if (SYN_REPORT == in_ev.code) {

                if (valid) {//判断是否有效

                switch (down) {

                    case 1:

                        printf("点击:(%d, %d);id = %d\n", x, y, id);

                        break;

                    case 0:

                        printf("离开\n");

                        break;

                    case -1:

                        printf("滑动:(%d, %d);id = %d\n", x, y, id);

                        break;

            }

            valid = 0; //重置 valid

            down = -1; //重置 down

            }

        }

        break;

        }

    }

}

首先使用 cat /proc/bus/input/devices 指令查看input设备,确定触摸设备的事件节点。

可以看出触摸属于event1,将测试程序拷入开发板并进行编译,分别测试单点触摸和多点触摸的情况,ID值即表示当前有几个触摸点。测试结果如下:

单点触摸:

多点触摸:

总结:本篇详细介绍了LCD屏幕显示原理及DRM驱动框架和LCD屏幕触摸原理及其驱动框架,并且分别介绍了显示和触摸的设备树相关配置,最后通过编写测试程序对屏幕触摸驱动进行了测试。

标签:200,00,触摸,LCD,240,Linux,230
From: https://blog.csdn.net/woaidandanhou/article/details/144112665

相关文章

  • 【Linux合集】clickhouse单机部署
    clickhouse单机部署1、前言之前由于设置安装clickhouse默认安装最新版本发现每次时隔两天都会因为打满内存导致clickhouse崩溃,先由于观察发现24版本稳定,现将版本定格在24.2.1.2248版本2、安装clickhousesudoyuminstall-yyum-utilssudoyum-config-manager--add-repoh......
  • 【Linux合集】单机部署zk集群
    创建存放目录/zk的多集群目录mkdir-p/data/{softwares,applications}/mkdir-p/data/applications/{zookeeper-server1,zookeeper-server2,zookeeper-server3}下载3.7.2版本zk先切目录到softwares下cd/data/softwares/wgethttps://archive.apache.org/dist/zookeeper/z......
  • 【应急响应】Linux 计划任务与 DFIR 排查实践(三)
    原创Y1x1nY1X1n安全2024年11月29日07:04免责声明本公众号文章中的程序、方法、信息和工具仅用于安全研究、教学、网络安全人员对网站及服务器的检测维护,禁止用于其他非法用途。使用者对不当使用造成的后果承担全部法律及连带责任,作者和公众号不担责。文中安全漏洞情报依公......
  • linux基本命令——sort详解
    sort命令作用、语法及参数作用Linuxsort命令用于将文本文件内容加以排序。sort可针对文本文件的内容,以行为单位来排序。注意1、排序时,默认是按每行/每个域的首字符排序,数字的优先级要大于字符的优先级2、不指定升序还是降序时,默认是升序语法sort[-bcdfimMnr][-o<......
  • linux一键部署apache脚本
    分享一下自己制作的一键部署apache脚本:脚本已和当前文章绑定,请移步下载(免费!免费!免费!)(单纯的分享!)步骤:将文件/内容上传到终端中(这里使用的是SecureCRT的远程)当然。可以使用其他方式将脚本上传到linux中然后在弹出的目录中选择脚本上传进入后使用sh指令运行脚本(记得要ro......
  • 基于Linux下单进程的网络客户端和服务端开发
    基于Linux下单进程的网络客户端和服务端开发前言一、socket是什么?二、网络客户端1.创建流式套截字2.connect()3.send()4.recv()5.关闭socket三、网络服务端1.创建服务端监听套接字2.用于指定通信的IP和端口3.把socket设置为监听的socket4.接收客户端的连接5.recv()/send......
  • Linux——跟时间相关的命令:hwclock -w
    1、date[root@localhost~]#dateFriNov2910:31:50CST20242、hwclock-w 这个命令在Linux系统中用于将当前的系统时间写入到硬件时钟(也称为实时时钟,RTC)。简单来说,这个命令的作用是将软件层面的系统时间同步到硬件层面的时钟上,确保即使在系统关闭后,时间也能保持准确。解释......
  • Linux安装RabbitMQ详细教程(最详细的图文教程)
    一、环境准备1、RabbitMQ版本和Erlang版本兼容性关系https://www.rabbitmq.com/which-erlang.html2、ErLang安装教程https://www.cnblogs.com/haoliyou/p/17666817.html3、RabbitMQ的安装依赖于erlang所以先安装4、RabbitMQ CentOS 参考安装步骤https://www.rabbitmq.co......
  • Linux 下编辑器vi介绍(二)
    光标跳转字符间跳转:h,j,k,lh:左l:右k:上j:下#COMMAND:跳转由#指定的个数的字符;行首行尾跳转:^:跳转至行首的第一个非空白字符;0:跳转至绝对的行首;$:跳转至绝对行尾;行间移动:#G:跳转至由#指定行;G:跳转至最后一行;1G,gg:跳转至第一行;vim的编辑命令字符编辑:x:删除光标处的单个字......
  • Linux物理内存管理
    1物理内存初始化——引导分配器memblock  Linux内核启动时,先要初始化物理内存,这个阶段的作用主要是确定物理内存大小,哪些是可用的?哪些是预留的?完成这一阶段工作的是memblock引导分配器。  内核启动时初始化物理内存的处理函数调用路径大概是(基于Linux5.10.1源码查看): ......