嵌入式Linux C应用编程——tslib库的使用
tslib 库
在上一个笔记中,我们基于读取到的 input_event 类型的数据进行了解析,以得到各个触摸点的坐标,然后进一步进行开发。而这一章,为了使开发更为方便,我们将使用Linux系统下专门为触摸屏开发的tslib库进行开发。tslib 是专门为触摸屏设备所开发的 Linux 应用层函数库,并且是开源的。
tslib 为触摸屏驱动和应用层之间的适配层,它把应用程序中读取触摸屏 struct input_event 类型数据(这
是输入设备上报给应用层的原始数据)并进行解析的操作过程进行了封装,向使用者提供了封装好的 API 接
口。 tslib 从触摸屏中获得原始的坐标数据,并通过一系列的去噪、去抖、坐标变换等操作,来去除噪声并将
原始的触摸屏坐标转换为相应的屏幕坐标。
tslib 可以作为 Qt 的触摸屏输入插件,为 Qt 提供触摸输入支持。虽然不是唯一的输入开发插件,但是大部分时候都会选择使用tslib库。
tslib 移植
正点原子开发板的出厂系统中自带了tslib库,所以可以不用移植,但是还是有必要了解一下如何移植的。如果你不知道是不是已经移植了tslib,可以输入 ts_finddev来查看版本信息:

相关的源码可以在github上下载: https://github.com/libts/tslib/releases 。
下载并解压
将下载的 tar.bz2 或 tar.gz 格式压缩包拷贝到 Ubuntu 系统的用户家目录下并解压:
# 以 tslib-1.16 为例
tar -xzf tslib-1.16.tar.gz
cd tslib-1.16/
注意还要在~文件夹下创建一个tools的文件夹:

配置工程
配置前需要设置交叉编译器的路径(确保已安装交叉编译器并加入 PATH),然后执行 configure 脚本。注意: --prefix 路径要根据自己的用户名修改,不要照抄我写的路径。
# 配置 tslib 工程,arm-linux-gnueabihf-gcc 为交叉编译器
./configure --host=arm-linux-gnueabihf --prefix=$HOME/tools/tslib
各选项说明:
--host=arm-linux-gnueabihf:文档中使用的是arm-poky-linux-gnueabi,这里也可也用这个。--prefix=$HOME/tools/tslib:指定安装路径,$HOME会自动展开为当前用户的家目录。
编译并安装
make
make install
编译安装完成后,安装目录下会生成以下几个子目录:
安装目录介绍
bin/ — 测试工具目录
包含 tslib 提供的一些小工具,用于测试触摸屏是否工作正常:
- ts_print — 单点触摸测试,在终端打印触摸点坐标信息
- ts_print_mt — 多点触摸测试,可打印多个触摸点的信息
- ts_test — 单点触摸画线测试,支持在 LCD 上画线
- ts_test_mt — 多点触摸画线测试
etc/ — 配置文件目录
里面有一个核心配置文件 ts.conf,tslib 工作的时候会去解析这个文件,加载相应的插件。文件内容及含义如下:
| 配置项 | 作用 |
|---|---|
module_raw input |
使能支持 input 输入事件(默认被注释,需要取消注释) |
module pthres pmin=1 |
压力阈值插件,pmin 调节按压力灵敏度,默认=1 |
module dejitter delta=100 |
去噪算法插件,用于过滤噪声样本,delta=100 为默认参数 |
module linear |
坐标变换插件,可实现 X/Y 坐标互换、坐标旋转等 |

如果开发板出厂系统已移植 tslib,一般使用默认配置即可,不需要改动。
include/ — 头文件目录
只包含一个头文件 tslib.h,里面定义了 tslib 相关的数据结构(如 struct ts_sample、struct ts_sample_mt)以及 API 函数的声明。编写应用程序时需要包含这个头文件。
lib/ — 库文件目录
包含编译 tslib 源码得到的动态库文件(如 libts.so),以及 ts/ 子目录下存放的各插件库(如 pthres.so、dejitter.so、linear.so 等)。如果需要静态库,可以在配置 tslib 工程时通过选项指定。
在开发板上测试 tslib
移植完成后,需要将编译得到的文件拷贝到开发板上。由于正点原子开发板的出厂系统已经移植了 tslib,所以一般不需要手动拷贝。但如果是自己做的根文件系统,需要按以下步骤操作:
① 拷贝文件到开发板
将安装目录下的测试工具(bin/)、配置文件(etc/ts.conf)以及动态库文件(lib/)分别拷贝到开发板对应的 /usr/bin、/etc 和 /usr/lib 目录下。
② 配置环境变量
tslib 工作时需要依赖以下环境变量来找到配置文件、库文件以及触摸屏设备节点:
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 |
显示设备节点,用于在 LCD 上显示画线内容 |
TSLIB_TSDEVICE |
触摸屏对应的设备节点,根据实际情况配置 |
TSLIB_CONFFILE |
ts.conf 配置文件的路径 |
TSLIB_PLUGINDIR |
插件库所在路径 |
注意: 在出厂系统中,输入
export可能看不到这些环境变量,但 tslib 工具依然能正常使用。这是因为 tslib 内部有默认路径——当环境变量未设置时,tslib 会使用编译时内嵌的默认值(如/etc/ts.conf、/usr/lib/ts、/dev/input/event1等)。出厂系统已将 tslib 安装到标准系统路径,所以无需环境变量也能正常工作。这些环境变量主要在你将 tslib 安装到非标准路径或自己移植时才需要手动设置。
如果想每次启动都自动生效,可以将这些命令添加到 /etc/profile 脚本中。出厂系统已默认配置好,无需用户配置。
③ 运行测试工具
- ts_print — 单点触摸测试,按下/移动/松开触摸屏时在终端打印坐标信息
- ts_print_mt — 多点触摸测试,可打印多个触摸点的信息
- ts_test — 单点触摸画线测试,支持在 LCD 上画线
- ts_test_mt — 多点触摸画线测试
执行 ts_print 后,在触摸屏上操作即可看到坐标输出:
$ ts_print

这些测试工具的源码可以在 tslib 源码目录的 tests/ 文件夹中找到。
tslib库函数的使用
使用 tslib 库函数需要在应用程序中包含头文件 tslib.h。tslib 编程的基本流程分为三步:打开设备 、 配置设备 、 读取数据。
打开触摸屏设备
tslib 提供了两个函数来打开触摸屏设备:
ts_open() — 基础打开函数
#include "tslib.h"
struct tsdev *ts_open(const char *dev_name, int nonblock);
dev_name:触摸屏的设备节点(如/dev/input/event1)nonblock:0 表示阻塞方式,非 0 表示非阻塞方式(非阻塞下CPU不会一直在此阻塞)- 返回值:成功返回
struct tsdev *句柄,失败返回 NULL
ts_setup() — 推荐使用,打开 + 配置一步到位
struct tsdev *ts_setup(const char *dev_name, int nonblock);
- 与
ts_open()的区别:dev_name可设为 NULL,此时函数内部会自动读取环境变量TSLIB_TSDEVICE来获取设备节点 - 调用
ts_setup()后无需再单独调用ts_config(),因为它内部已经完成了配置
ts_close() — 关闭设备
int ts_close(struct tsdev *ts);
ts_fd() — 获取文件描述符
int ts_fd(struct tsdev *ts);
- 从 tslib 句柄中取出底层的文件描述符,用于传递给
ioctl()等系统调用 - 例如获取触摸屏最大触点数时:
ioctl(ts_fd(ts), EVIOCGABS(ABS_MT_SLOT), &slot)
配置触摸屏设备
int ts_config(struct tsdev *ts);
- 作用:解析
ts.conf配置文件,加载相应的插件(去噪、去抖、坐标变换等) - 参数
ts:触摸屏句柄 - 返回值:成功返回 0,失败返回 -1
如果使用 ts_setup() 打开设备,则不需要再调用 ts_config()。
读取触摸屏数据
tslib 提供两个读取函数,分别对应单点和多点触摸:
ts_read() — 单点触摸
int ts_read(struct tsdev *ts, struct ts_sample *samp, int nr);
samp:指向struct ts_sample对象的指针,用于存放读取到的触摸数据nr:采样数,表示每次调用读取几个样本数据,一般设置为 1
ts_read_mt() — 多点触摸
int ts_read_mt(struct tsdev *ts, struct ts_sample_mt **samp, int max_slots, int nr);
samp:指向struct ts_sample_mt数组的指针max_slots:触摸屏支持的最大触摸点数,可通过 ioctl 获取nr:采样数,表示每次调用读取几个样本数据,一般设置为 1
关于采样数
nr: 这个参数表示每次调用ts_read()/ts_read_mt()时读取多少个样本数据。触摸屏上报数据是流式的、逐个到达的,每个样本对应一次触摸事件(按下、移动、松开)。设置为 1 意味着每次调用只读一个样本,处理完后再读取下一个,这种"一次读一个"的方式逻辑最简单、最常用。如果设置大于 1,函数会尝试一次读取多个样本,但需要底层有足够的数据堆积才会返回,实际使用中一般不需要。
获取最大触摸点数的方法:
struct input_absinfo slot;
ioctl(ts_fd(ts), EVIOCGABS(ABS_MT_SLOT), &slot);
max_slots = slot.maximum + 1 - slot.minimum;
数据结构
struct ts_sample — 单点触摸数据结构
struct ts_sample {
int x; // X 坐标
int y; // Y 坐标
unsigned int pressure; // 按压力大小
struct timeval tv; // 时间戳
};
struct ts_sample_mt — 多点触摸数据结构
struct ts_sample_mt {
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; // 时间戳
short pen_down; // BTN_TOUCH 状态
short valid; // 样本是否有效(数据是否发生更新)
};
valid字段很重要——只有当触摸点数据发生更新时该字段才为非 0 值。在多触摸应用程序中,应通过判断valid来确定哪些 slot 的数据发生了变化,而不是处理所有 slot。
基于 tslib 库编写的触摸屏应用程序
单点触摸
使用 ts_read() 读取单点触摸数据,通过判断 pressure(按压力)来确定触摸状态:
#include <stdio.h>
#include <stdlib.h>
#include <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) // 上一次也是按下状态 → 移动
printf("移动(%d, %d)\n", samp.x, samp.y);
else // 上一次是松开状态 → 按下
printf("按下(%d, %d)\n", samp.x, samp.y);
}
else // 按压力 = 0 → 松开
printf("松开\n");
pressure = samp.pressure; // 更新上一次的按压力
}
ts_close(ts);
exit(EXIT_SUCCESS);
}
通过 pressure 变量记录上一次的按压力状态,结合当前 samp.pressure 来判断是"按下"、"移动"还是"松开"。读取错误时 ts_read() 返回负数。
编译命令:
arm-linux-gnueabihf-gcc -I /home/duanwenjie/tools/tslib/include \
-L /home/duanwenjie/tools/tslib/lib \
-lts -o ts_read_test ts_read.c
编译选项说明:
| 选项 | 含义 | 作用 |
|---|---|---|
-I |
Include(头文件路径) | 指定头文件搜索目录,编译时需要找到 tslib.h |
-L |
Library(库文件路径) | 指定库文件搜索目录,链接时需要找到 libts.so |
-lts |
Link library(链接库名) | 指定链接 libts.so 库,-lts 会自动补全为 libts.so |
-o |
Output(输出文件) | 指定生成的可执行文件名 |
${CC}是一个环境变量,代表交叉编译器的名称。如果你设置了export CC=arm-linux-gnueabihf-gcc,那么${CC}就等价于直接写arm-linux-gnueabihf-gcc。为清晰起见,这里直接用实际的编译器名称。之前编译时没有加
-I、-L这些选项,是因为当时只用了标准库(如stdio.h、stdlib.h),这些库是交叉编译器自带的,编译时会自动搜索。而 tslib 是第三方库,需要手动指定头文件和库文件的路径,编译器才能找到它们。
多点触摸
使用 ts_read_mt() 读取多点触摸数据,每个触摸点用 struct ts_sample_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}; // 保存每个触摸点上一次的按压力
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]) // 上一次也是按下 → 移动
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 // 按压力 = 0 → 松开
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);
}
与单点的区别:
- 先通过
ioctl(ts_fd(ts), EVIOCGABS(ABS_MT_SLOT))获取触摸屏支持的最大触摸点数 - 使用
calloc为每个触摸点分配一个struct ts_sample_mt结构体(一次性分配的内存是连续的,所以可以使用数组的形式来表示) - 每次读完数据后遍历所有 slot,通过
valid字段判断该触摸点是否有数据更新 - 每个触摸点的状态需要单独记录(
pressure数组)
编译命令与单点触摸相同。

浙公网安备 33010602011771号