第一:tslib库基本简介
上篇文章为了获取触摸点,主要是对读取到的struct input_event类型数据进行分析,得到各个触摸点坐标。接下来,主要使用tslib库进行学习,这是Linux系统下,专门为触摸屏开发的应用层函数库。
tslib库是开源的,也就是可以直接获取到tslib的源代码。tslib为触摸屏驱动和应用层之间的适配层,它把应用程序中读取触摸屏struct input_event类型数据(这是输入设备上报给应用层的原始数据)并进行解析的操作过程进行了封装,向使用者提供了封装好的API接口。tslib从触摸屏中获得原始的坐标数据,并通过一系列的去抖、坐标变换等操作,来去除噪声并将原始的触摸屏坐标转换为相应的屏幕坐标。
tslib可以作为Qt的触摸屏输入插件,为Qt提供触摸输入支持,如果在嵌入式Linux硬件平台上开发过Qt应用程序应该知道,并不是只有 tslib 才能作为 Qt 的插件、为其提供触摸输入支持,还有很多插件都可以。
第二:tslib安装目录下的文件夹介绍
进入到tslib安装目录下:
bin目录
bin目录下有一些tslib提供的小工具,可以用于测试触摸屏,如下所示:
etc目录
etc目录下有一个配置文件ts.conf,前面给大家提到过:
打开ts.conf文件看看它有哪些配置选项:
module_raw input:取消注释,使能支持input输入事件;
module pthres pmin=1:如果我们的设备支持按压力大小测试,那么可以把它的注释取消,pmin 用于调节按压力灵敏度,默认就是等于 1。
module dejitter delta=100:tslib 提供了触摸屏去噪算法插件,如果需要过滤噪声样本,取消注释,默认参数 delta=100。
module linear:tslib 提供了触摸屏坐标变换的功能,譬如将 X、Y 坐标互换、坐标旋转等之类,如果我们需要实现坐标变换,可以把注释去掉。
这里就不去改动了,直接使用默认的配置就行了。
include 目录
include目录下只有一个头文件tslib.h,该头文件中包含了一些结构体数据结构以及API接口的申明,使用tslib提供的API就需要包含该头文件。
lib目录
lib目录下包含了编译tslib源码所得到的库文件,默认这些都是动态库文件,也可以通过配置tslib工程使其生成静态库文件;ts目录下存放的是一些插件库。
第三:在开发板上测试tslib库
使用tslib提供的API接口来编写触摸屏的应用程序,使用tslib库函数需要在我们的应用程序中包含tslib的头文件tslib.h,使用tslib编程其实非常简单,基本步骤如下所示:
第一步打开触摸屏设备;
第二步配置触摸屏设备;
第三步读取触摸屏数据;
1、打开触摸屏设备
使用tslib提供的库函数ts_open打开触摸屏设备,其函数原型如下所示:
#include "tslib.h" struct tsdev *ts_open(const char *dev_name,int nonblock);
参数dev_name 指定了触摸屏的设备节点;参数nonblock表示是否以非阻塞方式打开触摸屏设备,如果nonblock等于0表示阻塞方式,如果为非0值则表示以非阻塞方式打开。
调用成功返回一个struct sdev *指针,指向触摸屏设备句柄;如果打开设备失败,将返回NULL。除了使用ts_open()打开设备外,还可以使用ts_setup()函数,其函数原型如下所示:
#inlcude "tslib.h" struct tsdev *ts_setup(const char *dev_name,int nonblock)
参数 dev_name 指定触摸屏的设备节点,与 ts_open()函数中的 dev_name 参数意义相同;但对于 ts_setup()来说,参数 dev_name 可以设置为 NULL,当 dev_name 设置为 NULL 时,ts_setup()函数内部会读取TSLIB_TSDEVICE 环境变量,获取该环境变量的内容以得知触摸屏的设备节点。
ts_setup()相比ts_open(),除了打开触摸屏设备外,还可以对触摸屏设备进行配置。
2、配置触摸屏设备
调用ts_config()函数进行配置,其函数原型如下所示:
#include "tslib.h" int ts_config(struct tsdev *ts)
参数ts指向触摸屏句柄。
成功返回0,失败返回-1。
所谓配置其实指的就是解析ts.conf文件中的配置信息,加载相应的插件。
第四:读取触摸屏数据
读取触摸屏数据使用 ts_read()或 ts_read_mt()函数,区别在于 ts_read 用于读取单点触摸数据,而ts_read_mt 则用于读取多点触摸数据,其函数原型如下所示:
#include "tslib.h" int ts_read(struct tsdev *ts,struct ts_sample *samp,int nr) int ts_read_mt(struct tsdev *ts, struct ts_sample_mt **samp, int max_slots, int nr)
参数ts指向一个触摸屏设备句柄,参数nr表示对一个触摸点的采样数,设置为1即可。
ts_read_mt()函数有一个 max_slots 参数,表示触摸屏支持的最大触摸点数,应用程序可以通过调用 ioctl()函数来获取触摸屏支持的最大触摸点数以及触摸屏坐标的最大分辨率等信息,稍后向大家介绍。
ts_read()函数的 samp 参数是一个 struct ts_sample *类型的指针,指向一个 struct ts_sample 对象,struct ts_sample 数据结构描述了触摸点的信息;调用 ts_read()函数获取到的数据会存放在 samp 指针所指向的内存中。struct ts_sample 结构体内容如下所示:
struct ts_sample{ int x; //X坐标 int y; //Y坐标 unsigned int pressure; //按压力大小 struct timeval tv; //时间 };
ts_read_mt()函数的 samp 参数是一个 struct ts_sample_mt **类型的指针,多点触摸应用程序,每一个触摸点的信息使用 struct ts_sample_mt 数据结构来描述;一个触摸点的数据使用一个 struct ts_sample_mt 对象来装载,将它们组织成一个 struct ts_sample_mt 数组,调用 ts_read_mt()时,将数组地址赋值给 samp 参数。
struct ts_sample结构体内容如下所示:
struct ts_sample_mt { /* ABS_MT_* event codes. linux/include/uapi/linux/input-event-codes.h * has the definitions. */ int x; //X 坐标 int y; //Y 坐标 unsigned int pressure; //按压力大小 int slot; //触摸点 slot int tracking_id; //ID int tool_type; int tool_x; int tool_y; unsigned int touch_major; unsigned int width_major; unsigned int touch_minor; unsigned int width_minor; int orientation; int distance; int blob_id; struct timeval tv; //时间 /* BTN_TOUCH state */ short pen_down; //BTN_TOUCH 的状态 short valid; //此次样本是否有效标志 };
第五:单点触摸应用程序实现
#include <stdio.h> #include <stdlib.h> #include <tslib.h> //包含tslib.h的头文件 int main(int argc, char *argv[]) { struct tsdev *ts = NULL; struct ts_sample samp; int pressure = 0;//用于保存上一次的按压力,初始为 0,表示松开 /* 打开并配置触摸屏设备 */ ts = ts_setup(NULL, 0); if (NULL == ts) { fprintf(stderr, "ts_setup error"); exit(EXIT_FAILURE); } /* 读数据 */ for ( ; ; ) { if (0 > ts_read(ts, &samp, 1)) { fprintf(stderr, "ts_read error"); ts_close(ts); exit(EXIT_FAILURE); } if (samp.pressure) {//按压力>0 if (pressure) //若上一次的按压力>0 printf("移动(%d,%d)\n",samp.x,samp.y); else printf("按下(%d,%d)\n",samp.x,samp.y); } else printf("松开\n");//打印坐标 pressure = samp.pressure; } ts_close(ts); exit(EXIT_SUCCESS); }
分析:利用代码直接打开、配置设备、接着读取数据,通过判断按压大小确定触摸的状态。
${CC} -I /home/dt/tools/tslib/include -L /home/dt/tools/tslib/lib -lts -o testApp testApp.c
-I选项指定头文件的路径,也就是指定tslib安装目录下的include目录,如果不指定头文件路径,编译时将会找不到tslib.h头文件;-L 选项用于指定库文件的路径,也就是指定 tslib 安装目录下的 lib 目录;我们将 tslib 编译成了动态库文件,以库文件的形式提供,编译时需要链接到这些库文件;而-l 选项则用于指定链接库(也可写成-l ts,也就是 libts.so 库文件,Linux 中,动态库文件的命名方式为 lib+名字+.so)。
将编译得到的可执行文件拷贝到开发板 Linux 系统的用户家目录下,执行应用程序,进行测试:
第六:多点触摸应用程序实现
接下来基于tslib的多点触摸应用程序,使用ts_read_mt()函数读取多点触摸数据。
#include <stdio.h> #include <stdlib.h> #include <sys/ioctl.h> #include <linux/input.h> #include <tslib.h> int main(int argc, char *argv[]) { struct tsdev *ts = NULL; struct ts_sample_mt *mt_ptr = NULL; struct input_absinfo slot; int max_slots; unsigned int pressure[12] = {0}; //用于保存每一个触摸点上一次的按压力,初始为 0,表示松开 int i; /* 打开并配置触摸屏设备 */ ts = ts_setup(NULL, 0); if (NULL == ts) { fprintf(stderr, "ts_setup error"); exit(EXIT_FAILURE); } /* 获取触摸屏支持的最大触摸点数 */ if (0 > ioctl(ts_fd(ts), EVIOCGABS(ABS_MT_SLOT), &slot)) { perror("ioctl error"); ts_close(ts); exit(EXIT_FAILURE); } max_slots = slot.maximum + 1 - slot.minimum; printf("max_slots: %d\n", max_slots); /* 内存分配 */ mt_ptr = calloc(max_slots, sizeof(struct ts_sample_mt)); /* 读数据 */ for ( ; ; ) { if (0 > ts_read_mt(ts, &mt_ptr, max_slots, 1)) { perror("ts_read_mt error"); ts_close(ts); free(mt_ptr); exit(EXIT_FAILURE); } for (i = 0; i < max_slots; i++) { if (mt_ptr[i].valid) {//有效表示有更新! if (mt_ptr[i].pressure) { //如果按压力>0 if (pressure[mt_ptr[i].slot])//如果上一次的按压力>0 printf("slot<%d>, 移动(%d, %d)\n", mt_ptr[i].slot, mt_ptr[i].x, mt_ptr[i].y); else printf("slot<%d>, 按下(%d, %d)\n", mt_ptr[i].slot, mt_ptr[i].x, mt_ptr[i].y); } else printf("slot<%d>, 松开\n", mt_ptr[i].slot); pressure[mt_ptr[i].slot] = mt_ptr[i].pressure; } } } /* 关闭设备、释放内存、退出 */ ts_close(ts); free(mt_ptr); exit(EXIT_SUCCESS); }
整个思路与单点触摸应用程序相同,通过ts_read_mt()函数读取触摸点数据,将这些数据存放在 mt_ptr 数组中,接着在 for()循环中判断每一个触摸点数据是否有效,有效则表示该触摸点信息发生更新。
${CC} -I /home/dt/tools/tslib/include -L /home/dt/tools/tslib/lib -lts -o testApp testApp.c
将编译得到的可执行文件复制到开发板Linux系统用户家目录下,执行程序:
总结:触摸屏在很多场合下都会使用到,掌握tslib库的使用方法,对未来个人的发展具有重要作用。