一、屏幕介绍
ATK-7016 这款屏幕其实是由 TFT LCD+触摸屏组合起来的。底下是 LCD 面板,上面是触摸面板,将两个封装到一起就成了带有触摸屏的 LCD 屏幕。电容触摸屏也是需要一个驱动 IC的,驱动 IC 一般会提供一个 I2C 接口给主控制器,主控制器可以通过 I2C 接口来读取驱动 IC里面的触摸坐标数据。ATK-7016、ATK-7084 这两款屏幕使用的触摸控制 IC 是 FT5426,ATK-4342 使用的驱动 IC 是 GT9147,ATK-4384 使用的驱动 IC 是 GT1151。这些电容屏触摸 IC 都是 I2C 接口的,使用方法基本一样。
ATK-4384 的电容触摸屏部分有 4 个 IO 用于连接主控制器:SCL、SDA、RST 和 INT,SCL 和 SDA 是 I2C 引脚,RST 是复位引脚,INT 是中断引脚。一般通过 INT 引脚来通知主控制器有触摸点按下,然后在 INT 中断服务函数中读取触摸数据。也可以不使用中断功能,采用轮询的方式不断查询是否有触摸点按下,本章实验我们使用中断方式来获取触摸数据。
二、触摸驱动分析
1、驱动框架分析
按照https://blog.csdn.net/qq_41709234/article/details/128661071的说法:
驱动程序编写主要参考《正点原子开发指南》,在裸机开发中进行触摸屏的驱动,主要流程如下:
①、电容触摸屏是IIC接口的,需要触摸 IC,以正点原子的 ATK4384 为例,其所使用的触
摸屏控制 IC 为GT1151,因此所谓的电容触摸驱动就是 IIC设备驱动。
②、触摸IC提供了中断信号引脚(INT),可以通过中断来获取触摸信息。
③、电容触摸屏得到的是触摸位置绝对信息以及触摸屏是否有按下。
④、电容触摸屏不需要校准,当然了,这只是理论上的,如果电容触摸屏质量比较差,或
者触摸玻璃和 TFT 之间没有完全对齐,那么也是需要校准的。
那么电容触摸屏的Linux驱动主要需要以下几个驱动框架的组合:
①、IIC 设备驱动,因为电容触摸IC基本都是IIC接口的,因此大框架就是IIC设备驱动。
②、通过中断引脚(INT)向linux内核上报触摸信息,因此需要用到linux中断驱动框架。坐
标的上报在中断服务函数中完成。
③、触摸屏的坐标信息、屏幕按下和抬起信息都属于linux的input子系统,因此向 linux 内
核上报触摸屏坐标信息就得使用input子系统。
2、多点触摸(MT)协议详解
MT 协议隶属于 linux的 input 子系统,驱动通过大量的 ABS_MT 事件向 linux 内核上报多点触摸坐标数据。根据触摸 IC 的不同,分为 Type A 和 Type B 两种类型,目前使用最多的是 Type B 类型。
老版本的 linux 内核是不支持多点电容触摸的(Multi-touch,简称 MT),MT 协议是后面加入的,因此如果使用 2.x 版本 linux 内核的话可能找不到 MT 协议。
触摸点的信息通过一系列的 ABS_MT 事件(有的资料也叫消息)上报给 linux 内核,只有ABS_MT 事件是用于多点触摸的:
852 #define ABS_MT_SLOT 0x2f /* MT slot being modified */
853 #define ABS_MT_TOUCH_MAJOR 0x30 /* Major axis of touching ellipse */
854 #define ABS_MT_TOUCH_MINOR 0x31 /* Minor axis (omit if circular) */
855 #define ABS_MT_WIDTH_MAJOR 0x32 /* Major axis of approaching ellipse */
856 #define ABS_MT_WIDTH_MINOR 0x33 /* Minor axis (omit if circular) */
857 #define ABS_MT_ORIENTATION 0x34 /* Ellipse orientation */
858 #define ABS_MT_POSITION_X 0x35 /* Center X touch position */
859 #define ABS_MT_POSITION_Y 0x36 /* Center Y touch position */
860 #define ABS_MT_TOOL_TYPE 0x37 /* Type of touching device */
861 #define ABS_MT_BLOB_ID 0x38 /* Group a set of packets as a blob */
862 #define ABS_MT_TRACKING_ID 0x39 /* Unique ID of initiated contact */
863 #define ABS_MT_PRESSURE 0x3a /* Pressure on contact area */
864 #define ABS_MT_DISTANCE 0x3b /* Contact hover distance */
865 #define ABS_MT_TOOL_X 0x3c /* Center X tool position */
866 #define ABS_MT_TOOL_Y 0x3d /* Center Y tool position */
🎍 TypeA
对于 Type A 类型的设备,通过 input_mt_sync()函数来隔离不同的触摸点数据信息:
void input_mt_sync(struct input_dev *dev)
该函数会触发 SYN_MT_REPORT 事件,此事件会通知接收者获取当前触摸数据,并且准备接收下一个触摸点数据。
不管是哪个类型的设备,最终都要调用 input_sync()函数来标识多点触摸信息传输完成,告诉接收者处理之前累计的所有消息,并且准备好下一次接收。
对于 Type A 类型的设备,发送触摸点信息的时序如下所示,这里以 2 个触摸点为例:
ABS_MT_POSITION_X x[0]// ABS_MT_POSITION_X 、 ABS_MT_POSITION_Y事件
ABS_MT_POSITION_Y y[0]// input_report_abs来完成坐标上报
SYN_MT_REPORT// input_mt_sync()函数会触发SYN_MT_REPORT事件
ABS_MT_POSITION_X x[1]
ABS_MT_POSITION_Y y[1]
SYN_MT_REPORT
SYN_REPORT
实例:
static irqreturn_t st1232_ts_irq_handler(int irq, void *dev_id)
{
...
// 获取所有触摸点的信息
ret = st1232_ts_read_data(ts);
if (ret < 0)
goto end;
/* multi touch protocol */
for (i = 0; i < MAX_FINGERS; i++) {
if (!finger[i].is_valid)
continue;
// 按照TypeA时序上报所有触摸点的信息
input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, finger[i].t);/ ABS_MT_TOUCH_MAJOR常数用于表示触摸接触区域的主轴长度
input_report_abs(input_dev, ABS_MT_POSITION_X, finger[i].x);
input_report_abs(input_dev, ABS_MT_POSITION_Y, finger[i].y);
input_mt_sync(input_dev);
count++;
}
/* SYN_REPORT */
input_sync(input_dev);
end:
return IRQ_HANDLED;
}
🎍 TypeB
Type B 使用 slot 协议区分具体的触摸点,Type B 设备驱动需要给每个识别出来的触摸点分配一个 slot,后面使用这个 slot 来上报触摸点信息。可以通过 slot 的 ABS_MT_TRACKING_ID 来新增、替换或删除触摸点。
ABS_MT_TRACKING_ID跟踪ID:一个非负数的 ID 表示一个有效的触摸点,-1 这个 ID 表示未使用 slot。一个以前不存在的 ID 表示这是一个新加的触摸点,一个 ID 如果再也不存在了就表示删除了。
上报触摸点信息的时候需要通过 input_mt_slot()函数区分是哪一个触摸点:
void input_mt_slot(struct input_dev *dev, int slot)
该函数会触发 ABS_MT_SLOT 事件,此事件会告诉接收者当前正在更新的是哪个触摸点(slot)的数据。
不管是哪个类型的设备,最终都要调用 input_sync()函数来标识多点触摸信息传输完成,告诉接收者处理之前累计的所有消息,并且准备好下一次接收。
对于 Type B 类型的设备,发送触摸点信息的时序如下所示,这里以 2 个触摸点为例:
ABS_MT_SLOT 0// 使用 input_mt_slot 函数上报当前触摸点的 ABS_MT_SLOT 事件,触摸IC提供
ABS_MT_TRACKING_ID 45// input_mt_report_slot_state完成 Type B 的要求,即每个 SLOT 必须关联一个ABS_MT_TRACKING_ID
ABS_MT_POSITION_X x[0]
ABS_MT_POSITION_Y y[0]
ABS_MT_SLOT 1
ABS_MT_TRACKING_ID 46
ABS_MT_POSITION_X x[1]
ABS_MT_POSITION_Y y[1]
SYN_REPORT// 当所有的触摸点坐标都上传完毕以后就得发送 SYN_REPORT 事件,使用 input_sync 函数来完成。
// 当一个触摸点移除以后
ABS_MT_TRACKING_ID -1 // 负1表示这个ID不使用SLOT,同样使用input_mt_report_slot_state函数,第三个参数为false即可,无需手动置为-1
SYN_REPORT// 使用input_sync函数表示上报结束
实例:
static void ili210x_report_events(struct input_dev *input, const struct touchdata *touchdata)
{
int i;
bool touch;
unsigned int x, y;
const struct finger *finger;
// 循环上报所有触摸点
for (i = 0; i < MAX_TOUCHES; i++) {
input_mt_slot(input, i);// ABS_MT_SLOT事件
finger = &touchdata->finger[i];
touch = touchdata->status & (1 << i);
input_mt_report_slot_state(input, MT_TOOL_FINGER, touch);// 上报ABS_MT_TRACKING_ID事件,也就是给 SLOT 关联一个ABS_MT_TRACKING_ID。
if (touch) {
x = finger->x_low | (finger->x_high << 8);
y = finger->y_low | (finger->y_high << 8);
input_report_abs(input, ABS_MT_POSITION_X, x);
input_report_abs(input, ABS_MT_POSITION_Y, y);
}
}
input_mt_report_pointer_emulation(input, false);
input_sync(input);// 上报SYN_REPORT事件
}
🎍 MT触摸协议A、B总结
🎍 疑问及解答
1.TypeA和TypeB两种类型的区别如下:
1.TypeA协议
触摸点数量:TypeA协议可以检测到屏幕上同时触摸的触摸点数量,通常只能检测到一个或多个点。
触摸点的持续时间:TypeA协议可以检测到每个触摸点的持续时间,即触摸点在屏幕上停留的时间。
触摸点的坐标:TypeA协议不能直接获取每个触摸点的具体位置信息,但可以通过触摸点数量和持续时间等参数来判断触摸点的大致位置。
需要注意的是,TypeA协议是一种较为简单的触摸协议,主要用于实现一些简单的触摸操作,如滑动、点击和长按等。
2.TypeB协议
相比于TypeA协议,TypeB协议支持多点触控,并且能够获取更为详细的触摸信息:
触摸点数量:TypeB协议可以检测到屏幕上同时触摸的多个触摸点数量,通常可以检测到两个或更多个点。
触摸点的坐标:TypeB协议可以获取每个触摸点的具体位置信息,包括横纵坐标和压力值等。
触摸点的面积:TypeB协议可以获取每个触摸点的面积,即触摸点在屏幕上占据的面积大小。
触摸点的速度和方向:TypeB协议可以根据触摸点的坐标和时间信息,计算触摸点的速度和方向,从而实现手势识别等功能。
多点触控:TypeB协议支持多点触控,可以同时处理多个触摸点的信息,从而实现更为复杂的触摸操作,如缩放、旋转和拖拽等。
TypeB协议相比于TypeA协议,需要更高级的硬件支持,如电容式触摸屏等,而且在一些低端设备上可能无法使用。
3.总结
TypeA协议主要关注触摸点的数量,但并不追踪每个触摸点的具体位置。相反,TypeB协议会为每个触摸点分配一个slot,这个slot包含了触摸点的识别信息(ID)和位置信息。
4.举例说明
假设你有一个触摸屏,用户同时按下了两个点。在TypeA协议下,你只能知道屏幕上有两个点被按下,但无法得知这两个点的具体位置。这会导致触摸不够准确,因为你无法区分这两个点的具体信息。
而在TypeB协议下,每个触摸点都会被分配一个slot。例如,第一个触摸点被分配到slot 0,第二个触摸点被分配到slot 1。这样一来,你就可以知道每个触摸点的具体位置了。
2.什么是slot?什么是ID?
- slot:是一个用于存储触摸点信息的容器,包括触摸点的ID和位置信息。每个触摸点都会被分配一个slot,以便系统能够追踪多个触摸点。
- ID:是触摸点的唯一标识符,用于区分不同的触摸点。系统可以通过ID来追踪触摸点的移动。
3.MT多点触摸协议中TypeB中的slot,按下屏幕上的按键,分配一个slot和一个唯一的ID,在不抬起的情况下移动这个点,slot和ID会发生什么变化?
-
该触摸点所占用的slot和ID不会发生改变。它会继续使用原来分配给它的那个slot和ID。
-
其他新的触摸点如果产生,会分配新的未使用的slot和新的唯一ID。
-
如果原来的某个触摸点被抬起,其所占用的slot和ID将被释放,供新触摸点使用。
-
如果某个触摸点由于移动越过屏幕边缘被结束,其所占用的slot和ID也将被释放。
4.MT多点触摸协议中TypeB,如果首先按下一个点,再按下一个点,再次按下一个点,然后抬起第二个按下的点,再按下一个新的点,slot和ID如何发生变化?
- 首先按下第一个点,分配slot 1和 ID 1;
- 按下第二个点,分配slot 2和 ID 2;
- 按下第三个点,如果slot 1和2都被占用,会分配slot 3和 ID 3;
- 抬起第二个点(ID 2),释放其slot 2;
- 按下新的第四个点,由于slot 2已空闲,会分配slot 2和新的唯一ID,例如 ID 4。
5.为什么需要ID和slot配合,单独使用slot或者ID为什么不能完成多点触摸功能呢?
-
slot 数量是有限的,并且slot的使用可以确保每个触摸点都分配一个独占的通道来传输触摸相关信息。但是,(原因解释),如果在同一帧内,其中4个点(A,B,C和D)持续活动,我们可能会分配slot1-4。如果我们增加了第5个和第6个点E和F,slot 5和6也可能被分配,超出原有范围,在这种情况下,slot冲突就可能发生。另一方面,如果在后续帧中,部分点(例如B和D)不再活动,但新的点(G和H)出现,我们会释放slot 2和4,并分配新的slot 5和6。此时,slot 映射会变得非常混乱和不直观。很难通过slot数来确定某个点的信息通道,也就是说,我想对第二个按下的点进行操作,但是slot2并不对应第二个按下的点。
- ID只是用来标识每个触摸点的唯一标志符,并不能告诉我们这些触摸点的位置、状态或其他属性。为了实现多点触摸识别功能,我们需要使用ID和其他信息(如坐标、状态、时间戳等)来跟踪和识别每个触摸点,并进行相应的操作。
6.什么情况下需要使用 input_mt_report_pointer_emulation() 函数禁止指针模拟器?
-
硬件设备跟踪到的触摸点数量多于正在上报的触摸点数量,启用指针模拟器可能导致触摸点数量不匹配、误判触摸点位置等问题。
-
应用程序需要精确地跟踪多个触摸点的位置和状态,禁用指针模拟器可以确保触摸点信息能够准确地传递到应用程序中,而不受指针模拟器的影响。
-
一些应用程序需要使用指针模拟器来模拟单点触控,禁用指针模拟器可能会导致这些应用程序无法正常工作。
-
在特殊的应用场景下,例如绘图或游戏等需要高精度触摸控制的应用程序中,禁用指针模拟器可以提高触摸点的准确性和响应性。
三、多点触摸API函数
1、input_mt_init_slots 函数
用于初始化 MT 的输入 slots,编写 MT 驱动的时候必须先调用此函数初始化 slots:
int input_mt_init_slots( struct input_dev *dev,
unsigned int num_slots,
unsigned int flags)
dev: MT 设备对应的 input_dev,因为 MT 设备隶属于 input_dev。
num_slots:多点触控设备支持的最大触摸点数量。
flags:其他一些 flags 信息,可设置的 flags 如下所示:#define INPUT_MT_POINTER 0x0001 /* pointer device, e.g. trackpad */
#define INPUT_MT_DIRECT 0x0002 /* direct device, e.g. touchscreen */
#define INPUT_MT_DROP_UNUSED 0x0004 /* drop contacts not seen in frame */
#define INPUT_MT_TRACK 0x0008 /* use in-kernel tracking */
#define INPUT_MT_SEMI_MT 0x0010 /* semi-mt device, finger count handled manually */可以采用‘|’运算来同时设置多个 flags 标识。
返回值:0,成功;负值,失败。
2、input_mt_slot 函数
此函数用于 Type B 类型,此函数用于产生 ABS_MT_SLOT 事件,告诉内核当前上报的是哪个触摸点的坐标数据:
void input_mt_slot(struct input_dev *dev,
int slot)dev: MT 设备对应的 input_dev。
slot:当前发送的是哪个 slot 的坐标信息,也就是哪个触摸点。
返回值:无。
3、input_mt_report_slot_state 函数
此函数用于 Type B 类型,用于产生 ABS_MT_TRACKING_ID 和 ABS_MT_TOOL_TYPE事 件 , ABS_MT_TRACKING_ID 事 件 给 slot 关 联 一 个 ABS_MT_TRACKING_ID ,ABS_MT_TOOL_TYPE 事件指定触摸 型(是笔还是手指等):
void input_mt_report_slot_state( struct input_dev *dev,
unsigned int tool_type,
bool active)
dev: MT 设备对应的 input_dev。
tool_type:触摸类型,可以选择 MT_TOOL_FINGER(手指)、MT_TOOL_PEN(笔)或
MT_TOOL_PALM(手掌),对于多点电容触摸屏来说一般都是手指。
active:true,连续触摸,input 子系统内核会自动分配一个 ABS_MT_TRACKING_ID 给slot。
false,触摸点抬起,表示某个触摸点无效了,input 子系统内核会分配一个-1 给 slot,表示触摸点溢出。
返回值:无。
4、input_report_abs 函数
Type A 和 Type B 类型都使用此函数上报触摸点坐标信息,通过 ABS_MT_POSITION_X 和ABS_MT_POSITION_Y 事件实现 X 和 Y 轴坐标信息上报。
void input_report_abs( struct input_dev *dev,
unsigned int code,
int value)dev: MT 设备对应的 input_dev。
code:要上报的是什么数据,可以设置为 ABS_MT_POSITION_X 或ABS_MT_POSITION_Y,也就是 X 轴或者 Y 轴坐标数据。
value:具体的 X 轴或 Y 轴坐标数据值。
返回值:无。
5、input_mt_report_pointer_emulation 函数
如果追踪到的触摸点数量多于当前上报的数量,驱动程序使用 BTN_TOOL_TAP 事件来通知用户空间当前追踪到的触摸点总数量,然后调用 input_mt_report_pointer_emulation 函数将 use_count 参数设置为 false。否则的话将 use_count 参数设置为 true,表示当前的触摸点数量(此函数会获取到具体的触摸点数量,不需要用户给出):
void input_mt_report_pointer_emulation(struct input_dev *dev,
bool use_count)
dev: MT 设备对应的 input_dev。
use_count:true,有效的触摸点数量;false,追踪到的触摸点数量多于当前上报的数量。
返回值:无。
四、触摸驱动分析