rk3399/rk3399pro 触摸屏从底层到应用层分析
1.背景
需要了解触摸屏当人按下之后,触摸点的一些信息,比如触摸点的坐标(x,y),以及触摸力度等等。以官方rk3399pro开发板硬件为基础,来了解相关信息。
2.内核驱动
2.1层次框图
该部分内核驱动属于输入子系统部分,大概的层次关系如下图:
其中事件处理层的函数都是通过input_register_handler()函数注册到input_handler_list链表中,搜索input_register_handler注册函数,就可以看到都是事件处理层里的函数:
所以最终如下图所示:
右边的驱动事件处理,内核是已经写好了的,所以我们的触摸屏只需要写具体的驱动设备,然后内核会与触摸屏驱动tsdev.c自动连接
2.2相关结构体和函数
结构体:
struct input_dev {
void *private;
const char *name; //设备名字
const char *phys; //文件路径,比如 input/buttons
const char *uniq;
struct input_id id;
unsigned long evbit[NBITS(EV_MAX)]; //表示支持哪类事件,常用有以下几种事件(可以多选)
//EV_SYN 同步事件,当使用input_event()函数后,就要使用这个上报个同步事件
//EV_KEY 键盘事件
//EV_REL (relative)相对坐标事件,比如鼠标
//EV_ABS (absolute)绝对坐标事件,比如摇杆、触摸屏感应
//EV_MSC 其他事件,功能
//EV_LED LED灯事件
//EV_SND (sound)声音事件
//EV_REP 重复键盘按键事件
//(内部会定义一个定时器,若有键盘按键事件一直按下/松开,就重复定时,时间一到就上报事件)
//EV_FF 受力事件
//EV_PWR 电源事件
//EV_FF_STATUS 受力状态事件
unsigned long keybit[NBITS(KEY_MAX)]; //存放支持的键盘按键值
//键盘变量定义在:include/linux/input.h, 比如: KEY_L(按键L)、BTN_TOUCH(触摸屏的按键)
unsigned long relbit[NBITS(REL_MAX)]; //存放支持的相对坐标值
unsigned long absbit[NBITS(ABS_MAX)]; //存放支持的绝对坐标值,存放下面4个absxxx[]
unsigned long mscbit[NBITS(MSC_MAX)]; //存放支持的其它事件,也就是功能
unsigned long ledbit[NBITS(LED_MAX)]; //存放支持的各种状态LED
unsigned long sndbit[NBITS(SND_MAX)]; //存放支持的各种声音
unsigned long ffbit[NBITS(FF_MAX)]; //存放支持的受力设备
unsigned long swbit[NBITS(SW_MAX)]; //存放支持的开关功能
... ...
/*以下4个数组都会保存在上面成员absbit[]里,数组号为:ABS_xx ,位于include/linux/input.h */
/*比如数组0,标志就是ABS_X,以下4个的absXXX[0]就是表示绝对位移X方向的最大值、最小值... */
/*对于触摸屏常用的标志有:
ABS_X(X坐标方向), ABS_Y(Y坐标方向), ABS_PRESSURE(压力方向,比如绘图,越用力线就越粗)* /
int absmax[ABS_MAX + 1]; //绝对坐标的最大值
int absmin[ABS_MAX + 1]; //绝对坐标的最小值
int absfuzz[ABS_MAX + 1]; //绝对坐标的干扰值,默认为0,
int absflat[ABS_MAX + 1]; //绝对坐标的平焊位置,默认为0
... ...
函数:
struct input_dev *input_allocate_device(void); //向内存中分配input_dev结构体
input_free_device(struct input_dev *dev); //释放内存中的input_dev结构体
input_register_device(struct input_dev *dev); //注册一个input_dev,若有对应的驱动事件,
则在/sys/class/input下创建这个类设备
input_unregister_device(struct input_dev *dev); //卸载/sys/class/input目录下的
input_dev这个类设备
set_bit(nr,p); //设置某个结构体成员p里面的某位等于nr,支持这个功能
/* 比如:
set_bit(EV_KEY,buttons_dev->evbit); //设置input_dev结构体buttons_dev->evbit支持EV_KEY
set_bit(KEY_S,buttons_dev->keybit); //设置input_dev结构体buttons_dev->keybit支持按键”S”
*/
input_set_abs_params(struct input_dev *dev, int axis, int min, int max, int fuzz, int flat);
//设置绝对位移的支持参数
//dev: 需要设置的input_dev结构体
//axis : 需要设置的数组号,常用的有: ABS_X(X坐标方向), ABS_Y(Y坐标方向), ABS_PRESSURE(压力方向)//min: axis方向的最小值, max:axis方向的最大值, fuzz: axis方向的干扰值, flat:axis方向的平焊位置
input_report_abs(struct input_dev *dev, unsigned int code, int value);
//上报EV_ABS事件
//该函数实际就是调用的input_event(dev, EV_ABS, code, value);
//*dev :要上报哪个input_dev驱动设备的事件
// code: EV_ABS事件里支持的哪个方向,比如X坐标方向则填入: ABS_X
//value:对应的方向的值,比如X坐标126
input_report_key(struct input_dev *dev, unsigned int code, int value);
//上报EV_KEY事件
input_sync(struct input_dev *dev); //同步事件通知,通知系统有事件上报
struct clk *clk_get(struct device *dev, const char *id);
//获得*id模块的时钟,返回一个clk结构体
//*dev:填0即可, *id:模块名字, 比如"adc","i2c"等,名字定义在clock.c中
clk_enable(struct clk *clk);
//开启clk_get()到的模块时钟,就是使能CLKCON寄存器的某个模块的位
2.3不同触摸芯片驱动位置
不同触摸芯片驱动位置在内核里面,路径如下:
kernel/drivers/input/touchscreen
大概的文件结构:
zl@zl-Lenovo:~/workspace-rk/rk3399pro/rk3399pro-linux/kernel/drivers/input$ tree touchscreen/
touchscreen/
├── 88pm860x-ts.c
├── ad7877.c
├── ad7879.c
├── ad7879.h
├── ad7879-i2c.c
├── ad7879-spi.c
├── ads7846.c
├── ar1021_i2c.c
├── atmel_mxt_ts.c
├── atmel_mxt_ts.o
├── atmel-wm97xx.c
├── auo-pixcir-ts.c
├── bcm_iproc_tsc.c
├── bu21013_ts.c
├── built-in.o
├── chipone_icn8318.c
├── colibri-vf50-ts.c
├── cy8ctmg110_ts.c
├── cy8c_touchpad.c
├── cyttsp4_core.c
├── cyttsp4_core.h
├── cyttsp4_i2c.c
├── cyttsp4_spi.c
├── cyttsp_core.c
├── cyttsp_core.h
├── cyttsp_i2c.c
├── cyttsp_i2c_common.c
├── cyttsp_spi.c
├── da9034-ts.c
├── da9052_tsi.c
├── dynapro.c
├── edt-ft5x06.c
├── eeti_ts.c
├── egalax_ts.c
├── elants_i2c.c
├── elants_i2c.o
├── elo.c
├── ft6236.c
├── fujitsu_ts.c
├── goodix.c
├── gsl3673_800x1280.c
├── gsl3673_800x1280.h
├── gsl3673.c
├── gsl3673.h
├── gsl3673.o
├── gsl_point_id.c
├── gsl_point_id.o
├── gslx6801.c
├── gslx6801.h
├── gslx680a.c
├── gslx680.c
├── gslx680_d708
│ ├── Makefile
│ ├── rockchip_gsl3670.h
│ ├── rockchip_gslX680_88v.h
│ └── rockchip_gslX680.c
├── gslx680_firefly.c
├── gslx680_firefly.h
├── gslx680.h
├── gslx680.o
├── gslx680_pad.c
├── gslx680_pad.h
├── gt1x
│ ├── built-in.o
│ ├── gt1x.c
│ ├── gt1x_extents.c
│ ├── gt1x_extents.o
│ ├── gt1x_firmware.h
│ ├── gt1x_generic.c
│ ├── gt1x_generic.h
│ ├── gt1x_generic.o
│ ├── gt1x.h
│ ├── gt1x.o
│ ├── gt1x_tools.c
│ ├── gt1x_tools.o
│ ├── gt1x_update.c
│ ├── gt1x_update.o
│ ├── Makefile
│ ├── modules.builtin
│ └── modules.order
├── gt9xx
│ ├── built-in.o
│ ├── goodix_gt9xx.o
│ ├── goodix_tool.c
│ ├── GT9110P_Config_20160217_1526_2048_97.cfg
│ ├── GT9271_Config_20170526.cfg
│ ├── gt9xx.c
│ ├── gt9xx_cfg.h
│ ├── gt9xx_firmware.h
│ ├── gt9xx.h
│ ├── gt9xx.o
│ ├── gt9xx_update.c
│ ├── gt9xx_update.o
│ ├── Makefile
│ ├── modules.builtin
│ ├── modules.order
│ ├── WGJ10162B_GT9271_1060_Config_20140821_1341110X42.cfg
│ ├── WGJ10162_GT9271_Config_20140820_182456.cfg
│ ├── WGJ10187_GT910_Config_20140623_104014_0X41.cfg
│ ├── WGJ10187_GT9271_Config_20140623_104014_0X41.cfg
│ ├── WGJ89006B_GT911_Config_20140625_085816_0X43.cfg
│ └── WGJ89006B_GT9271_Config_20140625_085816_0X41.cfg
├── gunze.c
├── hampshire.c
├── hp680_ts_input.c
├── htcpen.c
├── ili210x.c
├── imx6ul_tsc.c
├── inexio.c
├── intel-mid-touch.c
├── ipaq-micro-ts.c
├── jornada720_ts.c
├── Kconfig
├── lpc32xx_ts.c
├── mainstone-wm97xx.c
├── Makefile
├── max11801_ts.c
├── mc13783_ts.c
├── mcs5000_ts.c
├── migor_ts.c
├── mk712.c
├── mms114.c
├── modules.builtin
├── modules.order
├── mtouch.c
├── of_touchscreen.c
├── of_touchscreen.o
├── pcap_ts.c
├── penmount.c
├── pixcir_i2c_ts.c
├── rohm_bu21023.c
├── s3c2410_ts.c
├── st1232.c
├── stmpe-ts.c
├── sun4i-ts.c
├── sur40.c
├── sx8654.c
├── ti_am335x_tsc.c
├── touchit213.c
├── touchright.c
├── touchwin.c
├── tp_gslx680_board.h
├── tps6507x-ts.c
├── tp_suspend.h
├── tsc2004.c
├── tsc2005.c
├── tsc2007.c
├── tsc200x-core.c
├── tsc200x-core.h
├── tsc40.c
├── ucb1400_ts.c
├── usbtouchscreen.c
├── usbtouchscreen.o
├── vtl_ts
│ ├── apk.c
│ ├── apk.h
│ ├── chip.c
│ ├── chip.h
│ ├── f307&f317&f316_CT363S_01_V10_F7E9_140212.dat
│ ├── LX20JS06_A1_CT363_V03_5198_121015.dat
│ ├── lx--js77_97_CT365_V01_E7DA_130419.dat
│ ├── Makefile
│ ├── tp_fw.h
│ ├── vtl_ts.c
│ ├── vtl_ts.h
│ └── wgj36js07_A1_CT363_V01_0187_140306.dat
├── w90p910_ts.c
├── wacom_i2c.c
├── wacom_w8001.c
├── wdt87xx_i2c.c
├── wm831x-ts.c
├── wm9705.c
├── wm9712.c
├── wm9713.c
├── wm97xx-core.c
├── zforce_ts.c
└── zylonite-wm97xx.c
4 directories, 173 files
2.4触摸芯片确认
对于一个开发板用的什么触摸芯片,可以从硬件资料查看,也可以从启动log里面查看,比如rk3399pro开发板buildroot文件系统开机log如下:
[0.548500] input: gsl3673 as /devices/platform/ff3d0000.i2c/i2c-4/4-0040/input/input1
从这个开机信息中,可以看到用的触摸芯片是gsl3673,并且注册的是input1设备,这样当这个触摸芯片有事件上报的时候,我们可以通过查看/dev/input/event1的信息来查看,命令行中查看的方法是:
[root@rk3399pro:/userdata]# hexdump /dev/input/event1
0000000 f7bd 62aa 0000 0000 7f40 0002 0000 0000
0000010 0003 0039 0001 0000 f7bd 62aa 0000 0000
0000020 7f40 0002 0000 0000 0003 003a 002a 0000
0000030 f7bd 62aa 0000 0000 7f40 0002 0000 0000
0000040 0003 0018 002a 0000 f7bd 62aa 0000 0000
0000050 7f40 0002 0000 0000 0003 0030 002a 0000
0000060 f7bd 62aa 0000 0000 7f40 0002 0000 0000
0000070 0003 0035 0324 0000 f7bd 62aa 0000 0000
关于hexdump分析键盘或者触摸屏的信息的有关内容,后面会继续整理。
3.应用层解析
从内核驱动的分析来看,触摸屏驱动拿到数据之后,会上报相应的事件,我们可以在应用层,通过读取/dev/input/event1的内容,来解析上报的信息。
3.1解析源码
#include <stdio.h>
#include <stdio.h>
#include <errno.h>
#include <linux/input.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
static int fd = 0;
static struct input_event ts;
int touch_init()
{
fd = open("/dev/input/event1", O_RDONLY);
if(fd == -1)
{
printf("[%s]:[%d] open event file error\r\n", __FUNCTION__, __LINE__);
return -1;
}
printf("open event1 success\n");
}
int get_xy(int *x,int *y)
{
read(fd,&ts,sizeof(ts));
printf("ts.type:%d,ts.code:0x%x\n",ts.type,ts.code);
if(ts.type == EV_ABS && ts.code == ABS_MT_POSITION_X)
{
*x = ts.value;
return 0;
}
if(ts.type == EV_ABS && ts.code == ABS_MT_POSITION_Y)
{
*y = ts.value;
return 0;
}
if(ts.type == EV_ABS && ts.code == ABS_PRESSURE)
{
printf("ts.value:%d\n",ts.value);
return 0;
}
return -1;
}
int main()
{
int x,y,tmp;
touch_init();
while(1)
{
tmp = get_xy(&x, &y);
if(tmp == 0)
{
printf("get position x:%d,y:%d\r\n",x,y);
}
else
{
printf("type error\n");
}
}
}
上面的源码,只解析了x,y坐标和压力数据,需要其他数据的话,可以根据相应类型,进行解析。type和code的值,定义在input-event-codes.h里面。
关于input_event结构体:
struct timeval {
__kernel_time_t tv_sec; /* seconds,32bit /
__kernel_suseconds_t tv_usec; / microseconds,32bit */
};
struct input_event {
struct timeval time; //时间
__u16 type; //事件类型
__u16 code; //事件键值
__s32 value; //值
};
4.关于压力值说明
有时候需要读取压力值,也就是手指按下的力度,对于触摸屏解码芯片和屏幕,有的是支持的,而有的不支持,并且支持的里面,也是分不同情况的,所以总结罗列下大概的情况:
第一种,触摸芯片不支持,这种情况下,只能拿到坐标值,压力值拿不到。
第二种,触摸芯片支持,但是因为无法准确提供压力值,所以提供了手指接触面的直径,从侧面反映手指按下的力度,这个时候会通过两个事件上报相应的值:ABS_MT_TOUCH_MAJOR和ABS_MT_WIDTH_MAJOR 。 ABS_MT_TOUCH_MAJOR表示了手指接触TP的直径的近似。ABS_MT_WIDTH_MAJOR是手指的直径的近似。当手指和触摸屏接触的越紧密,则压力越大,手指和屏幕接触的面积(直径)也会越大。而手指的直径通常是一个常量。这样ABS_MT_TOUCH_MAJOR / ABS_MT_WIDTH_MAJOR就可以用来表示压力了。而且这个值在[0,255)间。
第三种,触摸芯片支持,并且可以准确提供压力值,这个时候也会通过一个事件来上报: ABS_MT_PRESSUR,这个用来上报更准确的手指触摸屏幕时候的压力值。
5.扩展hexdump分析event值
hexdump: 查看文件的内容,比如二进制文件中包含的某些字符串,通常用来调试驱动用
结合前面的 input_event 结构体:
struct timeval {
__kernel_time_t tv_sec; /* seconds,32bit /
__kernel_suseconds_t tv_usec; / microseconds,32bit */
};
struct input_event {
struct timeval time; //时间
__u16 type; //事件类型
__u16 code; //事件键值
__s32 value; //值
};
5.1调试键盘驱动
以按开发板的按键 KEY_L,为例(因为数据是从低到高打印的,所以数据是反的):
//按键键盘驱动
# hexdump /dev/input/event0
/*按下时:*/
//hexdump序列号 秒 微妙 键盘事件 code=KEY_L value=1(按下)
0000000 07c6 0000 faa2 000b 0001 0026 0001 0000
//hexdump序列号 秒 微妙 同步事件 code value=0
0000010 07c6 0000 faac 000b 0000 0000 0000 0000
/*松开时:*/
//hexdump序列号 秒 微妙 键盘事件 code=0x26 value=0(松开)
0000020 07c6 0000 cf67 000d 0001 0026 0000 0000
//hexdump序列号 秒 微妙 同步事件 code value=0
0000030 07c6 0000 cf70 000d 0000 0000 0000 0000
5.2调试触摸屏驱动
//触摸屏驱动
# hexdump /dev/input/event1
//hexdump序列号 秒 微妙 绝对坐标事件 code=ABS_X X坐标值
0000000 0412 0000 6ef0 000c 0003 0000 0239 0000
//hexdump序列号 秒 微妙 绝对坐标事件 code=ABS_Y Y坐标值
0000010 0412 0000 6f08 000c 0003 0001 01ae 0000
//hexdump序列号 秒 微妙 绝对坐标事件 code=压力 压力值
0000020 0412 0000 6f0c 000c 0003 0018 0001 0000
//hexdump序列号 秒 微妙 键盘事件 code=触摸按键 value=1(按下)
0000030 0412 0000 6f10 000c 0001 014a 0001 0000
//hexdump序列号 秒 微妙 同步事件
0000040 0412 0000 6f13 000c 0000 0000 0000 0000
//hexdump序列号 秒 微妙 绝对坐标事件 code=压力 压力值
00000b0 023b 0000 872d 000c 0003 0018 0000 0000
//hexdump序列号 秒 微妙 键盘事件 code=触摸按键 value=0(松开)
00000b0 0412 0000 1f5b 000d 0001 014a 0000 0000
//hexdump序列号 秒 微妙 同步事件
00000c0 0412 0000 1f70 000d 0000 0000 0000 0000
类似的方法,也可以使用getevent命令,但是这个命令有的系统默认是不带的。
- 分享
- 举报
-
浏览量:2271次2022-05-17 09:00:33
-
浏览量:648次2023-12-21 18:17:30
-
浏览量:669次2023-12-21 18:18:43
-
浏览量:1155次2023-12-29 17:53:39
-
浏览量:2586次2023-12-29 17:55:25
-
浏览量:1030次2024-02-04 17:43:11
-
浏览量:2185次2022-10-09 10:41:47
-
浏览量:5459次2019-12-05 17:50:07
-
浏览量:895次2023-12-26 18:03:31
-
浏览量:5901次2021-12-10 16:15:34
-
浏览量:975次2023-12-16 16:39:06
-
浏览量:2988次2022-05-17 09:00:35
-
浏览量:3947次2021-04-02 09:51:10
-
浏览量:1567次2024-02-04 17:13:47
-
浏览量:6815次2020-12-17 09:45:27
-
浏览量:3641次2018-02-20 00:36:12
-
浏览量:2421次2022-02-25 15:20:03
-
浏览量:907次2023-10-16 17:32:09
-
浏览量:4824次2021-07-22 18:16:29
-
广告/SPAM
-
恶意灌水
-
违规内容
-
文不对题
-
重复发帖
林
感谢您的打赏,如若您也想被打赏,可前往 发表专栏 哦~
举报类型
- 内容涉黄/赌/毒
- 内容侵权/抄袭
- 政治相关
- 涉嫌广告
- 侮辱谩骂
- 其他
详细说明