LV17-01-输入类设备-04-触摸屏应用开发实例

本文主要是输入类设备控制——触摸屏设备应用开发实例的相关笔记,若笔记中有错误或者不合适的地方,欢迎批评指正😃。

点击查看使用工具及版本
PC端开发环境 Windows Windows11
Ubuntu Ubuntu20.04.2的64位版本
VMware® Workstation 17 Pro 17.6.0 build-24238078
终端软件 MobaXterm(Professional Edition v23.0 Build 5042 (license))
Win32DiskImager Win32DiskImager v1.0
Linux开发板环境 Linux开发板 正点原子 i.MX6ULL Linux 阿尔法开发板
uboot NXP官方提供的uboot,NXP提供的版本为uboot-imx-rel_imx_4.1.15_2.1.0_ga(使用的uboot版本为U-Boot 2016.03)
linux内核 linux-4.15(NXP官方提供)
点击查看本文参考资料
分类 网址 说明
官方网站 https://www.arm.com/ ARM官方网站,在这里我们可以找到Cotex-Mx以及ARMVx的一些文档
https://www.nxp.com.cn/ NXP官方网站
https://www.nxpic.org.cn/NXP 官方社区
https://u-boot.readthedocs.io/en/latest/u-boot官网
https://www.kernel.org/linux内核官网
其他网站 kernel - Linux source code (v4.15) - Bootlin linux内核源码在线查看
点击查看相关文件下载
分类 网址 说明
NXP https://github.com/nxp-imx NXP imx开发资源GitHub组织,里边会有u-boot和linux内核的仓库
https://elixir.bootlin.com/linux/latest/source 在线阅读linux kernel源码
nxp-imx/linux-imx/releases/tag/rel_imx_4.1.15_2.1.0_ga NXP linux内核仓库tags中的rel_imx_4.1.15_2.1.0_ga
nxp-imx/uboot-imx/releases/tag/rel_imx_4.1.15_2.1.0_ga NXP u-boot仓库tags中的rel_imx_4.1.15_2.1.0_ga
I.MX6ULL i.MX 6ULL Applications Processors for Industrial Products I.MX6ULL 芯片手册(datasheet,可以在线查看)
i.MX 6ULL Applications ProcessorReference Manual I.MX6ULL 参考手册(下载后才能查看,需要登录NXP官网)

一、获取触摸屏点数

1. 代码编写

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
/** =====================================================
* Copyright © hk. 2022-2025. All rights reserved.
* File name : lcd_touch_read_slot.c
* Author : 苏木
* Date : 2024-09-22
* Version :
* Description:
* ======================================================
*/

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <linux/input.h>

// /proc/bus/input/devices 命令查看输入事件
int main(int argc, char *argv[])
{
struct input_absinfo info;
int fd = -1;
int max_slots;

/* 校验传参 */
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);
}

/* 获取slot信息 */
if (0 > ioctl(fd, EVIOCGABS(ABS_MT_SLOT), &info))
{
perror("ioctl error");
close(fd);
exit(EXIT_FAILURE);
}

max_slots = info.maximum + 1 - info.minimum;
printf("max_slots: %d\n", max_slots);

/* 关闭、退出 */
close(fd);
exit(EXIT_SUCCESS);
}

2. 开发板测试

image-20240922095138182

可以看到这个是一个5点的触摸屏。

二、单点触摸

这里我的触摸屏好像并不会上报按下的这个事件,所以流程与多点触摸一致了,直接看多点触摸的实例。

三、多点触摸

1. 代码编写

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <string.h>
#include <linux/input.h>
/* 用于描述MT多点触摸每一个触摸点的信息 */
struct ts_mt
{
int x; //X坐标
int y; //Y坐标
int id; //对应ABS_MT_TRACKING_ID
int valid; //数据有效标志位(=1表示触摸点信息发生更新)
};

/* 一个触摸点的x坐标和y坐标 */
struct tp_xy
{
int x;
int y;
};

static int ts_read(const int fd, const int max_slots, struct ts_mt *mt)
{
struct input_event in_ev = {{0}};
static int slot = 0;//用于保存上一个slot
static struct tp_xy xy[12] = {{0}};//用于保存上一次的x和y坐标值,假设触摸屏支持的最大触摸点数不会超过12
int i = 0;

/* 对缓冲区初始化操作 */
memset(mt, 0x0, max_slots * sizeof(struct ts_mt)); //清零
for (i = 0; i < max_slots; i++)
{
mt[i].id = -2;//将id初始化为-2, id=-1表示触摸点删除, id>=0表示创建
}

while(1)
{

if (sizeof(struct input_event) != read(fd, &in_ev, sizeof(struct input_event)))
{
perror("read error");
return -1;
}
#if 1
printf("[INFO]event type:%d code:%d value:%d\n", in_ev.type, in_ev.code, in_ev.value);
#endif
switch (in_ev.type)
{
case EV_ABS:
switch (in_ev.code)
{
case ABS_MT_SLOT:
slot = in_ev.value;
break;
case ABS_MT_POSITION_X:
xy[slot].x = in_ev.value;
mt[slot].valid = 1;
break;
case ABS_MT_POSITION_Y:
xy[slot].y = in_ev.value;
mt[slot].valid = 1;
break;
case ABS_MT_TRACKING_ID:
mt[slot].id = in_ev.value;
mt[slot].valid = 1;
break;
default:
break;
}
break;
//case EV_KEY://按键事件对单点触摸应用比较有用
// break;
case EV_SYN:
if (SYN_REPORT == in_ev.code)
{
for (i = 0; i < max_slots; i++)
{
mt[i].x = xy[i].x;
mt[i].y = xy[i].y;
}
}
return 0;
default:
break;
}
}
return 0;
}

// /proc/bus/input/devices 命令查看输入事件
int main(int argc, char *argv[])
{
struct input_absinfo slot = {0};
struct ts_mt *mt = NULL;
int max_slots = 0;
int fd = -1;
int i = 0;

/* 校验传参 */
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);
}

/* 获取触摸屏支持的最大触摸点数 */
if (0 > ioctl(fd, EVIOCGABS(ABS_MT_SLOT), &slot))
{
perror("ioctl error");
close(fd);
exit(EXIT_FAILURE);
}

max_slots = slot.maximum + 1 - slot.minimum;
printf("max_slots: %d\n", max_slots);

/* 申请内存空间并清零 */
mt = calloc(max_slots, sizeof(struct ts_mt));

/* 读数据 */
while(1)
{

if (0 > ts_read(fd, max_slots, mt))
{
break;
}

for (i = 0; i < max_slots; i++)
{
if (mt[i].valid)
{
//判断每一个触摸点信息是否发生更新(关注的信息发生更新)
if (0 <= mt[i].id)
printf("slot<%d>, press(%d, %d)\n", i, mt[i].x, mt[i].y);
else if (-1 == mt[i].id)
printf("slot<%d>, lift\n", i);
else
printf("slot<%d>, move(%d, %d)\n", i, mt[i].x, mt[i].y);
}
}
}

/* 关闭设备、退出 */
close(fd);
free(mt);
exit(EXIT_FAILURE);
}

示例代码中定义了 struct ts_mt 数据结构,用于描述多点触摸情况下每一个触摸点的信息。

首先来看下 main()函数,定义了 max|_slots 变量,用于指定触摸屏设备的支持的最大触摸点数,通过:

1
ioctl(fd, EVIOCGABS(ABS_MT_SLOT), &slot)

获取到触摸屏该信息。

接着根据 max_slots 变量的值,为 mt 指针申请内存:

1
mt = calloc(max_slots, sizeof(struct ts_mt));

while循环中调用 ts_read()函数,该函数是自定义函数,用于获取触摸屏上报的数据, 第一个参数表示文件描述符 fd、第二个参数表示触摸屏支持的最大触摸点数、第三个参数则是 struct ts_mt 数组, ts_read()函数会将获取到的数据存放在数组中, mt[0]表示 slot<0>数据、 mt[1]表示 slot<1>的数据依次类推。

在内部的 for 循环中,则对获取到的数据进行分析,判断数据是否有效,并根据 id 判断手指的动作,在单点触摸应用程序中,我们是通过 BTN_TOUCH 事件来判断手指的动作;而在多点触摸应用中,我们需要通过 id 来判断多个手指的动作。

2. 开发板测试

我们编译后执行,然后多个手指按下的时候会有如下打印:

image-20240922212310023

上图是按下和移动的打印信息,当手指拿开的时候如下:

image-20240922212353769