嵌入式Linux C应用编程——tslib库的使用
2026/7/24 6:53:47 网站建设 项目流程

使开发更为方便,我们将使用Linux系统下专门为触摸屏开发的tslib库进行开发。tslib 是专门为触摸屏设备所开发的 Linux 应用层函数库,并且是开源的。

tslib 为触摸屏驱动和应用层之间的适配层,它把应用程序中读取触摸屏 struct input_event 类型数据(这
是输入设备上报给应用层的原始数据)并进行解析的操作过程进行了封装,向使用者提供了封装好的 API 接
口。 tslib 从触摸屏中获得原始的坐标数据,并通过一系列的去噪、去抖、坐标变换等操作,来去除噪声并将
原始的触摸屏坐标转换为相应的屏幕坐标。

tslib 可以作为 Qt 的触摸屏输入插件,为 Qt 提供触摸输入支持。虽然不是唯一的输入开发插件,但是大部分时候都会选择使用tslib库。

tslib 移植
正点原子开发板的出厂系统中自带了tslib库,所以可以不用移植,但是还是有必要了解一下如何移植的。如果你不知道是不是已经移植了tslib,可以输入 ts_finddev来查看版本信息:
img

相关的源码可以在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的文件夹:
img

配置工程
配置前需要设置交叉编译器的路径(确保已安装交叉编译器并加入 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/tools/tslib:指定安装路径,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 坐标互换、坐标旋转等
img

如果开发板出厂系统已移植 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
img

这些测试工具的源码可以在 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 数组)

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询