Linux_应用篇(16) tslib 库

上一章我们学习了如何编写触摸屏应用程序,包括单点触摸和多点触摸, 主要是对读取到的 struct input_event 类型数据进行剖析, 得到各个触摸点的坐标。本章向大家介绍 tslib 库, 这是 Linux 系统下, 专门为触摸屏开发的应用层函数库, 本章我们将学习如何基于 tslib 库编写触摸屏应用程序。本章将会讨论如下主题内容。
⚫ tslib 简介;
⚫ tslib 移植;
⚫ tslib 库函数的使用介绍;
⚫ 基于 tslib 库函数编写触摸屏应用程序。

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 源码

首先下载 tslib 源码包,进入到 tslib 的 git 仓库下载源码 https://github.com/libts/tslib/releases,如下:

ALPHA/Mini 开发板出厂系统中已经移植了 tslib,并且版本为 1.16,可以在开发板执行 ts_finddev 命令查看到它的版本信息,如下所示:

编译 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-pokylinux-gnueabi-gcc 前缀就是 arm-poky-linux-gnueabi; --prefix 选项则用于指定库文件的安装路径,我们将安装路径设置为之前在家目录下创建的 tools/tslib 目录。

接着编译工程,直接执行 make

最后执行 make install 安装

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 安装目录下的库文件、 etc 下的配置文件以及编译得到的测试工具拷贝到开发板 Linux 系统目录下, 由于开发板出厂系统中已经移植了 tslib 库,所以我们这里就不用拷贝了。但如果大家是自己做的根文件系统,并没有移植 tslib,那么就需要把这些库、可执行文件以及配置文件拷贝到根文件系统中,那怎么去拷贝?这里简单地提一下:
⚫ 将安装目录 bin/目录下的所有可执行文件拷贝到开发板/usr/bin 目录下;
⚫ 将安装目录 etc/目录下的配置文件 ts.conf 拷贝到开发板/etc 目录下;
⚫ 将安装目录 lib/目录下的所有库文件拷贝到开发板/usr/lib 目录下。
将安装目录下的测试工具、库文件以及配置文件拷贝到开发板之后,接着需要配置一些环境变量,因为tslib 工作的时候它需要依赖于一些环境变量,譬如它会通过读取环境变量来得知 ts.conf 配置文件、库文件的路径以及我们要测试的触摸屏对应的设备节点等。

export TSLIB_CONSOLEDEVICE=none
export TSLIB_FBDEVICE=/dev/fb0
export TSLIB_TSDEVICE=/dev/input/event1
export TSLIB_CONFFILE=/etc/ts.conf
export 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 文件夹中:

譬如 ts_test 程序对应的源码实现为 ts_test.c,不管它怎么做最终都是落实到上一章给大家介绍的内容中。

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_mt结构体内容如下所示:

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 编写触摸屏应用程序

#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 系统的用户家目录下,执行应用程序,进行测试:

多点触摸应用程序

#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()循环中判断每一个触摸点数据是否有效,有效则表示该触摸点信息发生更新。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Absorbed_w

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值