目录
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相关参数
-
提到显示器,都会有 720P、1080P、2K 或 4K 这样的参数,这个就是分辨率。LCD显示器都是由一个一个的像素点组成,1080P 的意思就是一个LCD屏幕上的像素点数量是1920*1080 个,也就是这个屏幕一列 1080 个像素点,一共 1920 列。
-
一个像素点就相当于一个RGB小灯,通过控制 R、G、B 这三种颜色的亮度就可以显示出各种各样的色彩。一般R、G、B 这三部分分别使用 8bit 的数据表示,一个像素点就是24bit,也就是说一个像素点3 个字节,这种像素格式称为 RGB888。当再加入 8bit 的 Alpha(透明)通道的话一个像素点就是 32bit,也就是 4 个字节,这种像素格式称为 ARGB8888。
-
LCD 屏幕支持很多种接口,比如在显示器上常见的 VGA、HDMI、eDP和MIPI-DSI等。
-
像素时钟指 LCD 的时钟信号,它和屏幕的时间参数和显示时序有关,是屏幕出厂就必须设定好的参数,这些参数需要在设备树中进行相应配置。
-
如果采用 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