正点原子 发表于 2021-8-25 15:09:02

《I.MX6U嵌入式Linux C应用编程指南》第十九章 使用tslib库

本帖最后由 正点原子 于 2021-8-25 15:10 编辑

1)实验平台:正点原子i.MX6ULL Linux阿尔法开发板
2)章节摘自【正点原子】I.MX6U嵌入式Linux C应用编程指南
3)购买链接:https://item.taobao.com/item.htm?&id=603672744434
4)全套实验源码+手册+视频下载地址:http://www.openedv.com/docs/boards/arm-linux/zdyz-i.mx6ull.html
5)正点原子官方B站:https://space.bilibili.com/394620890
6)正点原子Linux技术交流群:1027879335







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

1.1tslib简介

图 19.1.1 tslib
       tslib是专门为触摸屏设备所开发的Linux应用层函数库,并且是开源,也就意味着我们可以直接获取到tslib的源代码,下一小节将向大家介绍如何获取到tslib的源代码。
       tslib为触摸屏驱动和应用层之间的适配层,它把应用程序中读取触摸屏struct input_event类型数据(这是输入设备上报给应用层的原始数据)并进行解析的操作过程进行了封装,向使用者提供了封装好的API接口。tslib从触摸屏中获得原始的坐标数据,并通过一系列的去噪、去抖、坐标变换等操作,来去除噪声并将原始的触摸屏坐标转换为相应的屏幕坐标。
       tslib有一个配置文件ts.conf,该配置文件中提供了一些配置参数、用户可以对其进行修改,具体的配置信息稍后介绍!
tslib可以作为Qt的触摸屏输入插件,为Qt提供触摸输入支持,如果在嵌入式Linux硬件平台下开发过Qt应用程序的读者应该知道;当然,并不是只有tslib才能作为Qt的插件、为其提供触摸输入支持,还有很多插件都可以,只不过大部分都会选择使用tslib。
       关于tslib就介绍这么多,接下来看看如何将tslib库移植到我们的开发板平台上。
1.2tslib移植
1.2.1下载tslib源码
       首先下载tslib源码包,进入到tslib的git仓库下载源码https://github.com/libts/tslib/releases,如下:

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

图 19.2.2 查看tslib版本
       所以为了统一,我们也下载1.16版本的tslib,往下翻找到1.16版本的下载链接:

图 19.2.3 1.16版本
点击红框字样进入下载页面:

图 19.2.4 tslib源码包
      推荐下载tar.bz2或tar.gz格式压缩包,或者tar.xz压缩包,这里笔者下载tar.gz格式的压缩包文件,点击文字即可下载。

图 19.2.5 tslib-1.16.tar.gz
1.2.2编译tslib源码
      将tslib-1.16.tar.gz源码包拷贝到Ubuntu系统的用户家目录下:

图 19.2.6 将tslib源码拷贝到Ubuntu系统
将其解压到当前目录下:
tar -xzf tslib-1.16.tar.gz

图 19.2.7 解压tslib压缩包
       解压之后会生成tslib-1.16目录,在家目录下创建一个tools目录,然后在tools目录下创建tslib目录,等会编译tslib库的时候将安装目录指定到这里,如下所示:

图 19.2.8 创建tslib目录
进入到tslib-1.16目录,准备进行编译tslib源码:

图 19.2.9 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目录。

图 19.2.10 配置工程
接着编译工程,直接执行make:
make

图 19.2.11 make编译
最后执行make install安装:
make install
图 19.2.12 安装
1.2.3tslib安装目录下的文件夹介绍
进入到tslib安装目录下:

图 19.2.13 tslib安装目录下的文件夹
bin目录
bin目录下有一些tslib提供的小工具,可以用于测试触摸屏,如下所示:

图 19.2.14 bin目录下的文件
etc目录
etc目录下有一个配置文件ts.conf,前面给大家提到过,

图 19.2.15 配置文件ts.conf
打开ts.conf文件看看它有哪些配置选项:

图 19.2.16 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目录下存放的是一些插件库。

图 19.2.17 lib目录
share目录
可以忽略!
1.2.4在开发板上测试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:用于配置插件所在路径。

图 19.2.18 配置环境变量
       如果想每次启动系统都能生效,可以把这些命令放置在/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命令:

图 19.2.19 ts_print测试工具
执行ts_print命令之后,在触摸屏上滑动、或按下、松开触摸屏将会在终端打印出相应的信息。同理,ts_print_mt也是如此,不过它支持多点触摸,可以打印多个触摸点的信息:

图 19.2.20 ts_print_mt测试工具
ts_test和ts_test_mt支持触摸屏画线操作,这里就不再给演示了,自己去测试即可!如果大家想看这些测试工具的源码实现,可以在tslib源码中找到,具体路径为tslib源码目录下的tests文件夹中:

图 19.2.21 tests文件夹
譬如ts_test程序对应的源码实现为ts_test.c,不管它怎么做最终都是落实到上一章给大家介绍的内容中。
1.3tslib库函数介绍
       本小节介绍如何使用tslib提供的API接口来编写触摸屏应用程序,使用tslib库函数需要在我们的应用程序中包含tslib的头文件tslib.h,使用tslib编程其实非常简单,基本步骤如下所示:
       第一步打开触摸屏设备;
       第二步配置触摸屏设备;
       第三步读取触摸屏数据。
1.3.1打开触摸屏设备
       使用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 *);
1.3.2配置触摸屏设备
      调用ts_config()函数进行配置,其函数原型如下所示:
#include "tslib.h"

int ts_config(struct tsdev *ts)
      参数ts指向触摸屏句柄。
      成功返回0,失败返回-1。
      所谓配置其实指的就是解析ts.conf文件中的配置信息,加载相应的插件。
1.3.3读取触摸屏数据
       读取触摸屏数据使用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结构体内容如下所示:
示例代码 19.3.1 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结构体内容如下所示:
示例代码 19.3.2 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;            //此次样本是否有效标志 触摸点数据是否发生更新
};
1.4基于tslib编写触摸屏应用程序
       通过对tslib库函数的简单介绍,本小节来编写基于tslib的触摸屏应用程序,包括单点触摸应用程序和多点触摸应用程序。
1.4.1单点触摸应用程序
本例程源码对应的路径为:开发板光盘->11、Linux C应用编程例程源码->19_tslib->ts_read.c。
示例代码 19.4.1 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

图 19.4.1 编译单点触摸应用程序
-I选项指定头文件的路径,也就是指定tslib安装目录下的include目录,如果不指定头文件路径,编译时将会找不到tslib.h头文件;-L选项用于指定库文件的路径,也就是指定tslib安装目录下的lib目录;我们将tslib编译成了动态库文件,以库文件的形式提供,编译时需要链接到这些库文件;而-l选项则用于指定链接库(也可写成-l ts,也就是libts.so库文件,Linux中,动态库文件的命名方式为lib+名字+.so)。
将编译得到的可执行文件拷贝到开发板Linux系统的用户家目录下,执行应用程序,进行测试:

图 19.4.2 tslib单点触摸应用程序测试
1.4.2多点触摸应用程序
本小节我们来写基于tslib的多点触摸应用程序,使用ts_read_mt()函数读取多点触摸数据。
本例程源码对应的路径为:开发板光盘->11、Linux C应用编程例程源码->19_tslib->ts_read_mt.c。
示例代码 19.4.2 tslib多点触摸应用程序
<font size="4">#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 = {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</font><font size="4">.valid) {//有效表示有更新!
                if (mt_ptr</font><span style="font-style: italic;"><font size="4" style="font-style: normal;">.pressure) { //如果按压力>0
                  if (pressure)//如果上一次的按压力>0
                        printf("slot<%d>, 移动(%d, %d)\n", mt_ptr</font><font size="4" style="font-style: normal;">.slot, mt_ptr</font><font size="4" style="font-style: normal;">.x, mt_ptr</font><font size="4" style="font-style: normal;">.y);
                  else
                        printf("slot<%d>, 按下(%d, %d)\n", mt_ptr</font><font size="4" style="font-style: normal;">.slot, mt_ptr</font><font size="4" style="font-style: normal;">.x, mt_ptr</font><font size="4" style="font-style: normal;">.y);
                }
                else
                  printf("slot<%d>, 松开\n", mt_ptr</font><font size="4" style="font-style: normal;">.slot);

                pressure = mt_ptr</font><font size="4" style="font-style: normal;">.pressure;
            }
      }
    }

    /* 关闭设备、释放内存、退出 */
    ts_close(ts);
    free(mt_ptr);
    exit(EXIT_SUCCESS);
}</font></span>
       整个思路与单点触摸应用程序相同,关注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
图 19.4.3 编译多点触摸应用程序
将编译得到的可执行文件拷贝到开发板Linux系统的用户家目录下,执行程序:

图 19.4.4 多点触摸应用程序测试结果
slot<0>表示触摸点0,slot<1>表示触摸点1,以此类推,大家自己去测试,没什么可说的!
页: [1]
查看完整版本: 《I.MX6U嵌入式Linux C应用编程指南》第十九章 使用tslib库