目录
这是
Linux
系统下,专 门为触摸屏开发的应用层函数库。
tslib 简介
tslib
是专门为触摸屏设备所开发的
Linux
应用层函数库,并且是开源,也就意味着我们可以直接获取到 tslib 的源代码,下一小节将向大家介绍如何获取到
tslib
的源代码。
tslib
为触摸屏驱动和应用层之间的适配层,它把应用程序中读取触摸屏
struct input_event
类型数据(这 是输入设备上报给应用层的原始数据)并进行解析的操作过程进行了封装,向使用者提供了封装好的 API
接 口。tslib
从触摸屏中获得原始的坐标数据,并通过一系列的去噪、去抖、坐标变换等操作,来去除噪声并将 原始的触摸屏坐标转换为相应的屏幕坐标。
tslib
有一个配置文件
ts.conf
,该配置文件中提供了一些配置参数、用户可以对其进行修改,具体的配置 信息稍后介绍!
tslib
可以作为
Qt
的触摸屏输入插件,为
Qt
提供触摸输入支持,如果在嵌入式
Linux
硬件平台下开发过 Qt 应用程序的读者应该知道;当然,并不是只有
tslib
才能作为
Qt
的插件、为其提供触摸输入支持,还有很 多插件都可以,只不过大部分都会选择使用 tslib
。
tslib 移植
下载 tslib 源码
首先下载
tslib
源码包,进入到
tslib
的
git
仓库下载源码
https://github.com/libts/tslib/releases
,如下:
ALPHA/Mini
开发板出厂系统中已经移植了
tslib
,并且版本为
1.16
,可以在开发板执行
ts_finddev
命令 查看到它的版本信息,如下所示:
所以为了统一,我们也下载 1.16 版本的 tslib,往下翻找到 1.16 版本的下载链接:
点击红框字样进入下载页面:
推荐下载
tar.bz2
或
tar.gz
格式压缩包,或者
tar.xz
压缩包,这里笔者下载
tar.gz
格式的压缩包文件,点 击文字即可下载。
编译 tslib 源码
将
tslib-1.16.tar.gz
源码包拷贝到
Ubuntu
系统的用户家目录下:
将其解压到当前目录下:
tar -xzf tslib-1.16.tar.gz
解压之后会生成
tslib-1.16
目录,在家目录下创建一个
tools
目录,然后在
tools
目录下创建
tslib
目录, 等会编译 tslib
库的时候将安装目录指定到这里,如下所示:
进入到 tslib-1.16 目录,准备进行编译 tslib 源码:
接下来进行编译,整个源码的编译分为
3
个步骤:
⚫
首先第一步是配置工程;
⚫
第二步是编译工程;
⚫
第三步是安装,将编译得到的库文件、可执行文件等安装到一个指定的目录下。
首先在配置工程之前,先对交叉编译工具的环境进行设置,使用
source
执行交叉编译工具安装目录下 的 environment-setup-cortexa7hf-neon-poky-linux-gnueabi
脚本文件:
source /opt/fsl-imx-x11/4.1.15-2.1.0/environment-setup-cortexa7hf-neon-poky-linux-gnueabi
执行下面这条命令对 tslib 源码工程进行配置:
./configure --host=arm-poky-linux-gnueabi --prefix=/home/dt/tools/tslib/
至于工程是如何配置的,大家可以执行
./configure --help
查看它的配置选项以及含义,
--host
选项用于指 定交叉编译得到的库文件是运行在哪个平台,通常将--host
设置为交叉编译器名称的前缀,譬如
arm-poky-linux-gnueabi-gcc 前缀就是
arm-poky-linux-gnueabi
;
--prefix
选项则用于指定库文件的安装路径,我们将安装 路径设置为之前在家目录下创建的 tools/tslib
目录。
接着编译工程,直接执行 make:
make
最后执行 make install 安装:
make install
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
目录下存放的是一些插件库。
share 目录
可以忽略!
在开发板上测试 tslib
移植的最后一步就是把
tslib
安装目录下的库文件、
etc
下的配置文件以及编译得到的测试工具拷贝到开 发板 Linux
系统目录下,由于开发板出厂系统中已经移植了
tslib
库,所以我们这里就不用拷贝了。但如果 大家是自己做的根文件系统,并没有移植 tslib
,那么就需要把这些库、可执行文件以及配置文件拷贝到根文 件系统中,那怎么去拷贝?这里简单地提一下:
⚫
将安装目录
bin/
目录下的所有可执行文件拷贝到开发板
/usr/bin
目录下;
⚫
将安装目录
etc/
目录下的配置文件
ts.conf
拷贝到开发板
/etc
目录下;
⚫
将安装目录
lib/
目录下的所有库文件拷贝到开发板
/usr/lib
目录下。
将安装目录下的测试工具、库文件以及配置文件拷贝到开发板之后,接着需要配置一些环境变量,因为 tslib 工作的时候它需要依赖于一些环境变量,譬如它会通过读取环境变量来得知
ts.conf
配置文件、库文件 的路径以及我们要测试的触摸屏对应的设备节点等。
export TSLIB_CONSOLEDEVICE=noneexport TSLIB_FBDEVICE=/dev/fb0export TSLIB_TSDEVICE=/dev/input/event1export TSLIB_CONFFILE=/etc/ts.confexport TSLIB_PLUGINDIR=/usr/lib/ts
TSLIB_CONSOLEDEVICE
:用于配置控制台设备文件名,直接配置为
none
即可!
TSLIB_FBDEVICE
:用于配置显示设备的名称,
tslib
提供了手指触摸画线的测试工具,需要在
LCD
上 显示,所以这里需要指定一个显示设备的设备节点。
TSLIB_TSDEVICE
:用于配置触摸屏对应的设备节点,根据实际情况配置。
TSLIB_CONFFILE
:用于配置
ts.conf
文件的所在路径。
TSLIB_PLUGINDIR
:用于配置插件所在路径。
如果想每次启动系统都能生效,可以把这些命令放置在
/etc/profile
脚本中执行;出厂系统中已经配置好 了,无需用户进行配置。
接着我们使用
tslib
提供的测试工具测试触摸屏,它提供了单点触摸测试工具(
ts_print
、
ts_test
)和多点
触摸测试工具(
ts_print_mt
、
ts_test_mt
),
ts_print
和
ts_print_mt
可以在终端打印触摸点信息,而
ts_test
和 ts_test_mt 则支持在
LCD
上画线。
执行
ts_print
命令:
执行
ts_print
命令之后,在触摸屏上滑动、或按下、松开触摸屏将会在终端打印出相应的信息。同理, ts_print_mt 也是如此,不过它支持多点触摸,可以打印多个触摸点的信息:
ts_test
和
ts_test_mt
支持触摸屏画线操作,这里就不再给演示了,自己去测试即可!如果大家想看这些 测试工具的源码实现,可以在 tslib
源码中找到,具体路径为
tslib
源码目录下的
tests
文件夹中:
tslib 库函数介绍
本小节介绍如何使用
tslib
提供的
API
接口来编写触摸屏应用程序,使用
tslib
库函数需要在我们的应用 程序中包含 tslib
的头文件
tslib.h
,使用
tslib
编程其实非常简单,基本步骤如下所示:
第一步打开触摸屏设备;
第二步配置触摸屏设备;
第三步读取触摸屏数据。
打开触摸屏设备
使用
tslib
提供的库函数
ts_open
打开触摸屏设备,其函数原型如下所示:
#include "tslib.h"struct tsdev *ts_open(const char *dev_name, int nonblock);
参数
dev_name
指定了触摸屏的设备节点;参数
nonblock
表示是否以非阻塞方式打开触摸屏设备,如果 nonblock 等于
0
表示阻塞方式,如果为非
0
值则表示以非阻塞方式打开。 调用成功返回一个 struct tsdev *
指针,指向触摸屏设备句柄;如果打开设备失败,将返回
NULL
。 除了使用 ts_open()
打开设备外,还可以使用
ts_setup()
函数,其函数原型如下所示:
#include "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 环境变量,获取该环境变量的内容以得知触摸屏的设备节点。
参数
nonblock
的意义与
ts_open()
函数的
nonblock
参数相同。
ts_setup()
相比
ts_open()
,除了打开触摸屏设备外,还对触摸屏设备进行了配置,也就是接下来说到的第 二步操作。
关闭触摸屏设备使用
ts_close()
函数:
int ts_close(struct tsdev *);
配置触摸屏设备
调用
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 的状态
/* valid is set != 0 if this sample
* contains new data; see below for the
* bits that get set.
* valid is set to 0 otherwise
*/
short valid; //此次样本是否有效标志 触摸点数据是否发生更新
};
基于 tslib 编写触摸屏应用程序
通过对
tslib
库函数的简单介绍,本小节来编写基于
tslib
的触摸屏应用程序,包括单点触摸应用程序和 多点触摸应用程序。
单点触摸应用程序
#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);
}
代码非常简单,就不再解释了,直接打开、配置设备,接着读取数据即可!通过判断按压力大小确定触 摸的状态,如果按压力等于 0
则表示手指已经松开;按压力大于
0
,则需根据上一次的按压力是否大于
0
来 判断。
读取数据出错时,ts_read()
返回一个负数。
接下来编译应用程序,编译代码时,需要通过交叉编译器来指定头文件、库文件的路径以及动态链接库 文件名:
${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);
}
整个思路与单点触摸应用程序相同,关注
for()
循环内部,通过
ts_read_mt()
函数读取触摸点数据,将这 些数据存放在 mt_ptr
数组中,接着在
fof()
循环中判断每一个触摸点数据是否有效,有效则表示该触摸点信 息发生更新。
编译应用程序:
${CC} -I /home/dt/tools/tslib/include -L /home/dt/tools/tslib/lib -lts -o testApp testApp.c
将编译得到的可执行文件拷贝到开发板 Linux 系统的用户家目录下,执行程序:
slot<0>表示触摸点 0,slot<1>表示触摸点 1,以此类推,大家自己去测试,没什么可说的!