当前位置: 首页 > news >正文

从tslib源码看触摸屏滤波:手把手实现一个自定义的‘filter’插件

从tslib源码看触摸屏滤波:手把手实现一个自定义的‘filter’插件

触摸屏设备在现代交互系统中扮演着关键角色,但原始触摸数据往往包含噪声、抖动和不准确性。tslib作为Linux生态中广泛使用的触摸屏支持库,其核心价值在于提供了一套可扩展的滤波处理框架。本文将带您深入tslib的模块化架构,理解其数据处理流程,并最终实现一个能够记录原始坐标的自定义filter插件。

1. tslib架构深度解析

tslib采用插件化设计,所有滤波模块都以动态库形式存在plugins目录下。这种设计使得开发者可以灵活组合不同的处理模块,构建适合特定硬件的处理流水线。

1.1 模块链工作原理

在ts.conf配置文件中,模块按照处理顺序排列,形成处理链。例如一个典型配置可能如下:

module_raw input module median module dejitter module linear

数据流从底层驱动读取后,依次经过每个模块的read/read_mt函数处理。每个模块都接收前一个模块的输出作为输入,并将处理结果传递给下一个模块。

1.2 核心数据结构

tslib使用以下关键结构体传递触摸数据:

struct ts_sample { int x; // 归一化后的X坐标 int y; // 归一化后的Y坐标 unsigned int pressure; // 压力值 struct timeval tv; // 时间戳 }; struct ts_sample_mt { int x; int y; unsigned int pressure; int slot; // 触点ID int tracking_id; // 触点跟踪ID int tool_type; // 工具类型(手指/笔等) int tool_x; // 工具原始X坐标 int tool_y; // 工具原始Y坐标 struct timeval tv; };

2. 开发自定义filter插件

2.1 创建插件基础结构

每个tslib插件需要实现以下基本结构:

#include <tslib.h> static int myfilter_read(struct tsdev *ts, struct ts_sample *samp, int nr) { // 处理逻辑 return nr; } static int myfilter_read_mt(struct tsdev *ts, struct ts_sample_mt **samp, int max_slots, int nr) { // 多点触摸处理逻辑 return nr; } static const struct tslib_ops myfilter_ops = { .read = myfilter_read, .read_mt = myfilter_read_mt, }; TSAPI struct tslib_module_info *myfilter_mod_init(struct tsdev *dev, const char *params) { struct tslib_module_info *m; m = malloc(sizeof(struct tslib_module_info)); if (m == NULL) return NULL; m->ops = &myfilter_ops; return m; } #ifndef TSLIB_STATIC_MYFILTER_MODULE TSLIB_MODULE_INIT(myfilter_mod_init); #endif

2.2 实现坐标记录插件

下面是一个完整的坐标记录插件实现,它会将原始触摸数据写入系统日志:

#include <tslib.h> #include <syslog.h> #include <stdio.h> static int coordlog_read(struct tsdev *ts, struct ts_sample *samp, int nr) { int i; for (i = 0; i < nr; i++) { syslog(LOG_INFO, "RAW COORD: x=%d y=%d pressure=%d", samp[i].x, samp[i].y, samp[i].pressure); } return nr; // 继续传递原始数据 } static int coordlog_read_mt(struct tsdev *ts, struct ts_sample_mt **samp, int max_slots, int nr) { int i, j; for (i = 0; i < nr; i++) { for (j = 0; j < max_slots; j++) { if (samp[i][j].valid) { syslog(LOG_INFO, "MT[%d]: x=%d y=%d slot=%d id=%d", j, samp[i][j].x, samp[i][j].y, samp[i][j].slot, samp[i][j].tracking_id); } } } return nr; } static const struct tslib_ops coordlog_ops = { .read = coordlog_read, .read_mt = coordlog_read_mt, }; TSAPI struct tslib_module_info *coordlog_mod_init(struct tsdev *dev, const char *params) { struct tslib_module_info *m; openlog("tslib_coordlog", LOG_PID|LOG_CONS, LOG_USER); m = malloc(sizeof(struct tslib_module_info)); if (m == NULL) return NULL; m->ops = &coordlog_ops; return m; } #ifndef TSLIB_STATIC_COORDLOG_MODULE TSLIB_MODULE_INIT(coordlog_mod_init); #endif

3. 编译与集成插件

3.1 编译动态库

创建Makefile文件:

CC = gcc CFLAGS = -fPIC -Wall -Wextra LDFLAGS = -shared TARGET = coordlog.so all: $(TARGET) $(TARGET): coordlog.c $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^ clean: rm -f $(TARGET)

执行编译:

make

3.2 配置tslib使用新插件

将生成的coordlog.so复制到tslib插件目录,然后在ts.conf中添加配置:

module_raw input module coordlog module median module dejitter module linear

4. 高级插件开发技巧

4.1 处理模块参数

tslib支持向模块传递参数,格式为"key1=value1:key2=value2"。在mod_init函数中可以解析这些参数:

TSAPI struct tslib_module_info *coordlog_mod_init(struct tsdev *dev, const char *params) { // 参数解析示例 if (params) { char *p = strdup(params); char *tok = strtok(p, ":"); while (tok) { char *eq = strchr(tok, '='); if (eq) { *eq = '\0'; if (strcmp(tok, "log_level") == 0) { // 处理log_level参数 } } tok = strtok(NULL, ":"); } free(p); } // ... 其余初始化代码 }

4.2 维护模块状态

复杂的滤波算法可能需要维护状态信息。可以通过扩展tslib_module_info结构来实现:

struct coordlog_data { struct tslib_module_info module; int log_level; FILE *logfile; }; TSAPI struct tslib_module_info *coordlog_mod_init(struct tsdev *dev, const char *params) { struct coordlog_data *d; d = calloc(1, sizeof(struct coordlog_data)); if (d == NULL) return NULL; d->module.ops = &coordlog_ops; d->log_level = LOG_INFO; // 初始化自定义字段 d->logfile = fopen("/var/log/ts_coord.log", "a"); return &d->module; }

5. 调试与性能优化

5.1 调试技巧

  • 使用ts_printts_print_mt工具验证数据流
  • 通过strace跟踪系统调用
  • 在模块中添加调试日志:
#define DEBUG(fmt, ...) fprintf(stderr, "DEBUG: " fmt "\n", ##__VA_ARGS__) static int myfilter_read(struct tsdev *ts, struct ts_sample *samp, int nr) { DEBUG("Processing %d samples", nr); // ... }

5.2 性能考量

触摸屏数据处理对实时性要求较高,应注意:

  • 避免在滤波模块中进行内存分配
  • 限制日志输出频率
  • 使用查表法替代复杂计算
  • 针对ARM平台进行编译优化:
CFLAGS += -O2 -mcpu=cortex-a7 -mfpu=neon-vfpv4 -mfloat-abi=hard
http://www.jsqmd.com/news/689584/

相关文章:

  • 老MacBook Pro A1278升级Catalina保姆级避坑指南:从换SSD到打补丁全流程
  • 从HBM到IEC:深入解析产品ESD测试模型与实战配置
  • Visual C++运行库全版本集成包:告别DLL缺失的烦恼
  • 计算机毕业设计:Python雪球网股票数据采集与可视化系统 Flask框架 数据分析 可视化 大数据 大模型 爬虫(建议收藏)✅
  • 生成器与迭代器
  • 别再死记硬背了!用Python仿真带你搞懂发电机纵差、横差保护原理
  • 保姆级教程:在Ubuntu 20.04 ROS Noetic下,用奥比中光Astra Pro完成相机标定(附常见报错解决)
  • 国信QMT vs 国金MiniQMT:实测哪个能真正下载可用的历史Tick数据?
  • 用Python和OpenCV搞定车道线曲率计算:从图像处理到实际距离的保姆级教程
  • 别再傻傻分不清!VCC、VDD、VSS、VEE、VPP,5分钟帮你理清电路图上的电源符号
  • 2026年头皮抗衰行业靠谱GEO优化服务商选型与能力评估分析报告 - 商业小白条
  • 车载ECU开发效率飙升217%?VSCode 2026适配实测报告:12家OEM验证的4项必须启用的隐藏设置
  • MTK Filogic 630方案首秀:中兴E1630拆解看MT7916的升级点
  • 【2026年最新600套毕设项目分享】微信小程序的专利服务系统(30146)
  • 保姆级教程:用OpenCV和PCL库给激光雷达点云上色(附完整C++代码)
  • 2026年少儿编程行业专业AI搜索优化服务商选型分析与主流机构推荐 - 商业小白条
  • 从Flash到SAR:一张图看懂主流ADC结构怎么选(2024版)
  • 26-4-23日志 - Ghost
  • 保姆级教程:在Ubuntu上为AM5728开发板交叉编译GPSD 3.18(附libusb/ncurses依赖库完整配置)
  • 避开Latex!用Word向ACM会议投稿的完整攻略:从模板适配到TAPS最终提交
  • 智能合约开发框架对比
  • 别再只盯着运放了!用TI INA826这类仪表放大器搞定传感器信号调理,实测避坑指南
  • 从入门到精通:AI产品经理的完整学习指南与实战路径
  • 告别Grbl依赖:手把手教你用STM32CubeMX和emWin搭建带U盘脱机功能的CNC控制界面
  • 电荷泵在嵌入式系统中的应用:从LCD驱动到EEPROM编程
  • IGBT驱动信号里的‘空白时间’:手把手教你分析SVPWM/SPWM中的死区效应与谐波
  • Spring Boot Admin Server 2.3.1 保姆级搭建教程:从零到UI界面,含Spring Security安全配置避坑指南
  • ADS负载牵引实战:从CGH40010F管子的1.6GHz仿真到稳定电路设计,一步步教你优化PA性能
  • 【2026年最新600套毕设项目分享】微信小程序的酒店管理系统(30147)
  • 虾皮 大数据开发工程师面试题精选:10道高频考题+答案解析(附PDF)