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

嵌入式Linux基于IMX6ULL tslib学习总结

目录

    • 1. tslib开源库介绍
      • 1.1 tslib主要功能
      • 1.2 架构
    • 2. tslib代码简单分析
      • 2.1 ts_print_mt.c分析代码
      • 2.2 ts_setup代码分析
      • 2.3 ts_open代码分析
      • 2.4 ts_config代码分析
      • 2.5 ts_read_mt代码分析
      • 2.6 tslib中4个模块的含义
    • 3. 使用tslib库打印触摸屏2点之间的距离

基于韦东山IMX6ULL开发板和配套资料学习

参考教程:韦东山老师教程

1. tslib开源库介绍

tslib开源库地址是:http://www.tslib.org/

tslib 是一个开源的触摸屏校准和事件处理库,广泛用于嵌入式系统和 Linux 系统中。它提供了一套工具和库函数,用于校准触摸屏、处理触摸事件,并将原始触摸数据转换为可用于应用程序的标准化事件。

1.1 tslib主要功能

  • 触摸屏校准tslib 提供了一个校准工具 ts_calibrate,用于校准触摸屏,生成校准参数文件。
  • 事件处理tslib 可以处理触摸屏的原始事件,过滤噪声,平滑触摸轨迹,并将处理后的事件传递给应用程序。
  • 插件架构tslib 采用插件架构,支持多种输入设备和不同的校准算法。
  • 标准化输出tslib 将不同触摸屏设备的原始数据转换为标准化的事件格式,便于应用程序使用。

1.2 架构

tslib 的架构主要包括以下几个部分:

  • 核心库

    • libts:核心库,提供了触摸屏事件处理的基本功能,包括读取事件、校准、滤波等。
  • 工具

    • ts_calibrate:用于校准触摸屏。

    • ts_test:用于测试触摸屏的响应。

    • ts_print:用于打印触摸屏事件。

    • ts_uinput:用于生成虚拟的触摸屏事件。

  • 插件

    • linear:线性校准插件。

    • dejitter:去抖动插件。

    • palm_detect:手掌检测插件。

    • 其他插件:根据需要扩展的其他处理模块。

在这里插入图片描述

核心在于“plugins”目录里的“插件”,或称为“module”。这个目录下的每个文件都是一个module,每个module都提供2个函数:read、read_mt,前者用于读取单点触摸屏的数据,后者用于读取多点触摸屏的数据。

参考ts_test.c和ts_test_mt.c,前者用于一般触摸屏(比如电阻屏、单点电容屏),后者用于多点触摸屏。

2. tslib代码简单分析

tslib的框架图:

在这里插入图片描述

顺着tslib的框架图对ts_print_mt.c代码进行逐步分析。

2.1 ts_print_mt.c分析代码

ts_print_mt.c文件大致内容分析:

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <getopt.h>
#include <tslib.h>
#include <linux/input.h> // 可能需要包含此头文件以支持ioctl和ABS_MT_SLOT// 假设usage函数在其他地方定义,用于打印程序的使用方法
void usage(char **argv);// 假设errfn和openfn函数在其他地方定义,用于错误处理和特定打开逻辑
void errfn(const char *fmt, ...);
int openfn(const char *device, int flags);int main(int argc, char **argv)
{struct tsdev *ts; // 指向触摸屏设备的指针char *tsdevice = NULL; // 触摸屏设备名称struct ts_sample_mt **samp_mt = NULL; // 指向多点触控样本数据的指针数组#ifdef TS_HAVE_EVDEVstruct input_absinfo slot; // 用于存储触摸槽位信息的结构体
#endifint32_t user_slots = 0; // 用户指定的槽位数int32_t max_slots = 1; // 最大槽位数,默认为1int ret, i, j; // 返回值、循环变量int read_samples = 1; // 要读取的样本数short non_blocking = 0; // 是否非阻塞模式short raw = 0; // 是否读取原始数据struct ts_lib_version_data *ver = ts_libversion(); // 获取tslib版本信息// 检查tslib版本,如果低于1.10,则提示升级
#ifndef TSLIB_VERSION_MT /* < 1.10 */printf("You are running an old version of tslib. Please upgrade.\n");
#endif// 解析命令行参数while (1) {const struct option long_options[] = {{ "help",         no_argument,       NULL, 'h' },{ "idev",         required_argument, NULL, 'i' },{ "samples",      required_argument, NULL, 's' },{ "non-blocking", no_argument,       NULL, 'n' },{ "raw",          no_argument,       NULL, 'r' },{ "slots",        required_argument, NULL, 'j' },{ "version",      no_argument,       NULL, 'v' },};int option_index = 0;int c = getopt_long(argc, argv, "hvi:s:nrj:", long_options, &option_index);errno = 0;if (c == -1)break;switch (c) {case 'h':usage(argv);return 0;case 'v':printf("%s\n", tslib_version());return 0;case 'i':tsdevice = optarg; // 设置触摸屏设备名称break;case 'n':non_blocking = 1; // 设置非阻塞模式break;case 'r':raw = 1; // 设置读取原始数据break;case 's':read_samples = atoi(optarg); // 设置要读取的样本数if (read_samples <= 0) {usage(argv);return 0;}break;case 'j':user_slots = atoi(optarg); // 设置用户指定的槽位数if (user_slots <= 0) {usage(argv);return 0;}break;default:usage(argv);return 0;}if (errno) {char str[9];sprintf(str, "option ?");str[7] = c & 0xff;perror(str);}}ts_error_fn = errfn; // 设置错误处理函数#ifdef TSLIB_VERSION_OPEN_RESTRICTED// 如果tslib支持受限打开功能,则设置特定的打开函数if (ver->features & TSLIB_VERSION_OPEN_RESTRICTED)ts_open_restricted = openfn;
#endif// 根据是否非阻塞模式,设置触摸屏设备if (non_blocking)ts = ts_setup(tsdevice, 1);elsets = ts_setup(tsdevice, 0);if (!ts) {perror("ts_setup");return errno;}// 打印打开的tslib版本和设备路径printf("libts %06X opened device %s\n",ver->version_num, ts_get_eventpath(ts));#ifdef TS_HAVE_EVDEV// 获取触摸槽位信息,并计算最大槽位数if (ioctl(ts_fd(ts), EVIOCGABS(ABS_MT_SLOT), &slot) < 0) {perror("ioctl EVIOGABS");ts_close(ts);return errno;}max_slots = slot.maximum + 1 - slot.minimum;
#endif// 如果用户指定了槽位数,则使用用户指定的值if (user_slots > 0)max_slots = user_slots;// 分配内存以存储样本数据samp_mt = malloc(read_samples * sizeof(struct ts_sample_mt *));if (!samp_mt) {ts_close(ts);return -ENOMEM;}for (i = 0; i < read_samples; i++) {samp_mt[i] = calloc(max_slots, sizeof(struct ts_sample_mt));if (!samp_mt[i]) {free(samp_mt);ts_close(ts);return -ENOMEM;}}// 循环读取触摸数据while (1) {if (raw)ret = ts_read_raw_mt(ts, samp_mt, max_slots, read_samples); // 读取原始多点触控数据elseret = ts_read_mt(ts, samp_mt, max_slots, read_samples); // 读取多点触控数据if (ret < 0) {if (non_blocking) {printf("ts_print_mt: read returns %d\n", ret);continue;}perror("ts_read_mt");ts_close(ts);exit(1);}// 打印读取到的触摸数据for (j = 0; j < ret; j++) {for (i = 0; i < max_slots; i++) {if (!(samp_mt[j][i].valid & TSLIB_MT_VALID))continue;// 假设YELLOW和RESET是定义的宏,用于改变输出文本的颜色printf(YELLOW "sample %d - %ld.%06ld -" RESET " (slot %d) %6d %6d %6d\n",j,samp_mt[j][i].tv.tv_sec,samp_mt[j][i].tv.tv_usec,samp_mt[j][i].slot,samp_mt[j][i].x,samp_mt[j][i].y,samp_mt[j][i].pressure);}}}ts_close(ts); // 关闭触摸屏设备
}

遵循tslib架构图中ts_setup、ts_read_mt、ts_close顺序来读取触摸屏的输入数据。

2.2 ts_setup代码分析

tslib中ts_setup函数简单分析,ts_setup中主要是执行ts_open和ts_config函数,对tsdev结构体进行设置:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <tslib.h>// 定义默认设备名称数组
const char *ts_name_default[] = {"/dev/input/touchscreen0","/dev/input/event0",NULL
};// 设置错误处理函数
void ts_error(const char *fmt, ...) {va_list args;va_start(args, fmt);vfprintf(stderr, fmt, args);va_end(args);
}// 扫描设备函数(假设已实现)
char *scan_devices(void) {// 实现扫描设备的逻辑return "/dev/input/event0"; // 示例返回值
}/*** @brief 设置并打开触摸屏设备** @param dev_name 设备名称,如果为NULL则使用环境变量TSLIB_TSDEVICE或默认设备* @param nonblock 是否使用非阻塞模式* @return 成功返回触摸屏设备句柄,失败返回NULL*/
struct tsdev *ts_setup(const char *dev_name, int nonblock)
{const char * const *defname;struct tsdev *ts = NULL;
#if defined (__linux__)char *fname = NULL;
#endif /* __linux__ */// 如果dev_name为空,则尝试从环境变量TSLIB_TSDEVICE获取设备名称dev_name = dev_name ? dev_name : getenv("TSLIB_TSDEVICE");// 尝试打开指定的设备if (dev_name != NULL) {ts = ts_open(dev_name, nonblock); // 打开设备} else {// 如果没有指定设备名称,尝试打开默认设备列表中的设备defname = &ts_name_default[0];while (*defname != NULL) {ts = ts_open(*defname, nonblock); // 尝试打开默认设备if (ts != NULL)break; // 找到设备后跳出循环++defname; // 继续下一个默认设备}}#if defined (__linux__)// 如果仍然没有找到设备,尝试扫描所有设备if (!ts) {fname = scan_devices(); // 扫描设备if (!fname)return NULL; // 扫描失败,返回NULLts = ts_open(fname, nonblock); // 打开扫描到的设备free(fname); // 释放扫描结果的内存}
#endif /* __linux__ */// 如果成功打开设备,尝试配置设备if (ts && ts_config(ts) != 0) {ts_error("ts_config: %s\n", strerror(errno)); // 配置失败,打印错误信息ts_close(ts); // 关闭设备return NULL; // 返回NULL}return ts; // 返回设备句柄
}

其中tsdev结构体:

/*** @brief 触摸屏设备结构体** 该结构体用于表示触摸屏设备及其相关信息。*/
struct tsdev {int fd; // 设备文件描述符char *eventpath; // 设备文件路径struct tslib_module_info *list; // 模块链表头指针/*** 指向模块链表中提供原始读取功能的模块。* 默认情况下,指向 `ts_read_raw` 模块。*/struct tslib_module_info *list_raw;unsigned int res_x; // 触摸屏的X轴分辨率unsigned int res_y; // 触摸屏的Y轴分辨率int rotation; // 触摸屏的旋转角度
};

2.3 ts_open代码分析

ts_open函数的主要作用是打开设备文件,把文件描述符保存到tsdev的fd设备描述符中:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <tslib.h>// 假设 print_host_os 函数已经实现
void print_host_os(void) {// 打印主机操作系统信息printf("Host OS: Linux\n"); // 示例输出
}// 假设 ts_open_restricted 函数已经实现
int (*ts_open_restricted)(const char *path, int flags, void *data) = NULL;/*** @brief 打开触摸屏设备** @param name 设备文件名* @param nonblock 是否使用非阻塞模式* @return 成功返回触摸屏设备句柄,失败返回NULL*/
struct tsdev *ts_open(const char *name, int nonblock)
{struct tsdev *ts;int flags = O_RDWR; // 默认以读写模式打开设备#ifdef DEBUGprint_host_os(); // 打印主机操作系统信息printf(", trying to open %s\n", name); // 打印尝试打开的设备文件名
#endif// 如果需要非阻塞模式if (nonblock) {
#ifndef WIN32flags |= O_NONBLOCK; // 在非Windows系统上设置非阻塞标志
#endif}// 分配内存用于tsdev结构体ts = malloc(sizeof(struct tsdev));if (!ts)return NULL; // 内存分配失败,返回NULL// 初始化tsdev结构体memset(ts, 0, sizeof(struct tsdev));// 复制设备文件名ts->eventpath = strdup(name);if (!ts->eventpath)goto free; // 复制设备文件名失败,跳转到free标签// 如果设置了受限打开函数if (ts_open_restricted) {ts->fd = ts_open_restricted(name, flags, NULL); // 使用受限打开函数打开设备if (ts->fd == -1)goto free; // 打开设备失败,跳转到free标签return ts; // 打开设备成功,返回设备句柄}// 使用标准open函数打开设备ts->fd = open(name, flags);/** 如果打开失败且错误码为EACCES(权限不足),尝试以只读模式打开设备* 这对于大多数驱动程序来说是足够的*/if (ts->fd == -1 && errno == EACCES) {
#ifndef WIN32flags = nonblock ? (O_RDONLY | O_NONBLOCK) : O_RDONLY; // 设置只读模式和非阻塞标志
#elseflags = O_RDONLY; // Windows系统上设置只读模式
#endifts->fd = open(name, flags); // 以只读模式重新打开设备}if (ts->fd == -1)goto free; // 打开设备失败,跳转到free标签return ts; // 打开设备成功,返回设备句柄free:// 释放已分配的内存if (ts->eventpath)free(ts->eventpath); // 释放设备文件名字符串free(ts); // 释放tsdev结构体return NULL; // 返回NULL
}

2.4 ts_config代码分析

ts_config读取/etc/ts.conf文件内容,和“plugins”目录里的“插件(module)”对应:

在这里插入图片描述

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <tslib.h>#define BUF_SIZE 1024// 假设 discard_null_tokens 和 ts_load_module、ts_load_module_raw 函数已经实现
void discard_null_tokens(char **p, char **tok) {// 处理空tokenwhile (*tok && **tok == '\0') {*tok = *p;*p = strchr(*p, ' ');if (*p) {**p = '\0';*p += 1;}}
}int ts_load_module(struct tsdev *ts, const char *module, const char *params)
{return __ts_load_module(ts, module, params, 0);
}int ts_load_module_raw(struct tsdev *ts, const char *module, const char *params)
{return __ts_load_module(ts, module, params, 1);
}/*** @brief 配置触摸屏设备** @param ts 触摸屏设备句柄* @param conffile_modules 模块名称数组* @param conffile_params 模块参数数组* @param raw 标记是否为原始模块的数组* @return 成功返回0,失败返回-1*/
static int __ts_config(struct tsdev *ts, char **conffile_modules,char **conffile_params, int *raw)
{char buf[BUF_SIZE], *p;FILE *f;int line = 0;int ret = 0;short strdup_allocated = 0;char *conffile;// 获取配置文件路径if ((conffile = getenv("TSLIB_CONFFILE")) == NULL) {conffile = strdup(TS_CONF); // 使用默认配置文件路径if (conffile) {strdup_allocated = 1; // 标记已分配内存} else {ts_error("Couldn't find tslib config file: %s\n", strerror(errno));return -1; // 获取配置文件路径失败}}// 打开配置文件f = fopen(conffile, "r");if (!f) {if (strdup_allocated)free(conffile); // 释放已分配的内存ts_error("Couldn't open tslib config file %s: %s\n", conffile, strerror(errno));return -1; // 打开配置文件失败}buf[BUF_SIZE - 2] = '\0'; // 确保缓冲区末尾为null终止while ((p = fgets(buf, BUF_SIZE, f)) != NULL) {char *e;char *tok;char *module_name;line++; // 行号递增// 去除行尾换行符e = strchr(p, '\n');if (e)*e = '\0';// 检查是否读取了完整的一行if (buf[BUF_SIZE - 2] != '\0') {ts_error("%s: line %d too long\n", conffile, line);break; // 行太长,退出循环}#if !defined HAVE_STRSEPtok = ts_strsep(&p, " \t"); // 使用自定义的ts_strsep函数
#elsetok = strsep(&p, " \t"); // 使用标准的strsep函数
#endifdiscard_null_tokens(&p, &tok); // 处理空token// 忽略注释或空白行if (p == NULL || *tok == '#')continue;// 搜索选项if (strcasecmp(tok, "module") == 0) {
#if !defined HAVE_STRSEPmodule_name = ts_strsep(&p, " \t"); // 使用自定义的ts_strsep函数
#elsemodule_name = strsep(&p, " \t"); // 使用标准的strsep函数
#endifdiscard_null_tokens(&p, &module_name); // 处理空tokenif (!conffile_modules) {ret = ts_load_module(ts, module_name, p); // 加载模块} else {
#ifdef DEBUGprintf("TSLIB_CONFFILE: module %s %s\n", module_name, p); // 调试信息
#endifsprintf(conffile_modules[line], "%s", module_name); // 存储模块名称if (conffile_params)sprintf(conffile_params[line], "%s", p); // 存储模块参数}} else if (strcasecmp(tok, "module_raw") == 0) {
#if !defined HAVE_STRSEPmodule_name = ts_strsep(&p, " \t"); // 使用自定义的ts_strsep函数
#elsemodule_name = strsep(&p, " \t"); // 使用标准的strsep函数
#endifdiscard_null_tokens(&p, &module_name); // 处理空tokenif (!conffile_modules) {ret = ts_load_module_raw(ts, module_name, p); // 加载原始模块} else {
#ifdef DEBUGprintf("TSLIB_CONFFILE: module_raw %s %s\n", module_name, p); // 调试信息
#endifsprintf(conffile_modules[line], "%s", module_name); // 存储模块名称if (conffile_params)sprintf(conffile_params[line], "%s", p); // 存储模块参数if (raw)raw[line] = 1; // 标记为原始模块}} else {ts_error("%s: Unrecognised option %s:%d:%s\n", conffile, line, tok);break; // 未知选项,退出循环}if (ret != 0) {ts_error("Couldn't load module %s\n", module_name);break; // 加载模块失败,退出循环}}if (ts->list_raw == NULL) {ts_error("No raw modules loaded.\n");ret = -1; // 没有加载任何原始模块}fclose(f); // 关闭配置文件if (strdup_allocated)free(conffile); // 释放已分配的内存return ret; // 返回结果
}

ts_load_module()和ts_load_module_raw()代码分析:

#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h> // 用于动态链接库操作
#include <tslib.h>// 假设 __ts_load_module_static 和 __ts_attach、__ts_attach_raw 函数已经实现
/*** @brief 静态加载触摸屏模块** @param ts 触摸屏设备句柄* @param module 模块名称* @param params 模块参数* @return 成功返回模块信息指针,失败返回NULL*/
static struct tslib_module_info *__ts_load_module_static(struct tsdev *ts,const char *module,const char *params)
{struct tslib_module_info *info = NULL;struct tslib_module_desc *result;struct tslib_module_desc key;// 设置查找键key.name = module;// 使用二分查找在模块列表中查找匹配的模块描述result = bsearch(&key, tslib_modules, countof(tslib_modules),sizeof(struct tslib_module_desc), cmp_name);// 如果没有找到匹配的模块,返回NULLif (!result)return NULL;// 调用模块的初始化函数info = result->mod_init(ts, params);#ifdef DEBUG// 调试信息:模块初始化结果fprintf(stderr, "static module %s init %s\n", module,info ? "succeeded" : "failed");
#endif// 如果初始化成功,设置模块句柄为NULLif (info)info->handle = NULL;// 返回模块信息指针return info;
}/*** @brief 将模块附加到触摸屏设备的原始模块链表** @param ts 触摸屏设备句柄* @param info 模块信息指针* @return 成功返回0,失败返回-1*/
int __ts_attach_raw(struct tsdev *ts, struct tslib_module_info *info)
{struct tslib_module_info *next, *prev, *prev_list = ts->list_raw;// 将模块信息中的设备指针设置为当前设备info->dev = ts;// 将新模块插入到原始模块链表的头部info->next = prev_list;ts->list_raw = info;/** 确保正常模块链表的最后一个模块指向原始模块链表的头部。*/if (ts->list == NULL || ts->list == prev_list) {/* 如果主链表为空,或者主链表的最后一个模块就是原始模块链表的第一个模块 *//* 则将主链表的头部指向新插入的模块 */ts->list = info;return 0;}// 遍历正常模块链表,找到最后一个模块for (next = ts->list, prev = next;next != NULL && next != prev_list;next = prev->next, prev = next);// 将正常模块链表的最后一个模块的 next 指针指向新插入的模块prev->next = info;return 0;
}/*** @brief 将模块附加到触摸屏设备的模块链表** @param ts 触摸屏设备句柄* @param info 模块信息指针* @return 成功返回0,失败返回-1*/
int __ts_attach(struct tsdev *ts, struct tslib_module_info *info)
{// 将模块信息中的设备指针设置为当前设备info->dev = ts;// 将新模块插入到模块链表的头部info->next = ts->list;ts->list = info;// 返回0表示成功return 0;
}/*** @brief 加载触摸屏模块** @param ts 触摸屏设备句柄* @param module 模块名称* @param params 模块参数* @param raw 是否为原始模块* @return 成功返回0,失败返回-1*/
static int __ts_load_module(struct tsdev *ts, const char *module,const char *params, int raw)
{struct tslib_module_info *info;void *handle;int ret;#ifdef DEBUGif (params)printf("Loading module %s (%s)\n", module, params); // 调试信息:加载模块及参数elseprintf("Loading module %s\n", module); // 调试信息:加载模块
#endif// 尝试静态加载模块info = __ts_load_module_static(ts, module, params);#ifdef HAVE_LIBDL// 如果静态加载失败,尝试动态加载模块if (!info)info = __ts_load_module_shared(ts, module, params);
#endif// 如果加载失败,返回-1if (!info)return -1;// 根据是否为原始模块,调用相应的附加函数if (raw)ret = __ts_attach_raw(ts, info); // 附加原始模块elseret = __ts_attach(ts, info); // 附加普通模块// 如果附加失败,进行清理if (ret) {
#ifdef DEBUGts_error("Can't attach %s\n", module); // 调试信息:无法附加模块
#endifhandle = info->handle; // 获取模块句柄// 调用模块的fini函数进行清理if (info->ops->fini)info->ops->fini(info);#ifdef HAVE_LIBDL// 如果模块句柄有效,关闭动态链接库if (handle)dlclose(handle);
#endif}return ret; // 返回结果
}

2.5 ts_read_mt代码分析

tslib架构中看到添加了4个模块:module_raw input、module pthres pmin=1、module dejitter delta=100、module linear,经过ts_setup配置后形成的链表是:

在这里插入图片描述

数据的流向是:触摸屏数据->input模块->pthres模块->dejitter模块->linear模块。

从代码层面分析:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

2.6 tslib中4个模块的含义

  • module_raw input:读取原始触摸屏数据。

  • module pthres pmin=1:设置触摸屏的最小压力阈值,过滤掉压力值低于指定阈值的触摸点。

  • module dejitter delta=100:减少触摸点的抖动,通过平滑算法使触摸点的移动更加平滑。

  • module linear:进行线性校准,将触摸点的坐标映射到屏幕的实际坐标系中。

对每个模块的详细解析:

  • module_raw input

    • 功能:读取原始触摸屏数据。

    • 用途:这个模块通常作为其他模块的基础,提供最原始的触摸屏数据。它不进行任何处理,直接将数据传递给下一个模块。

    • 示例配置:

      module_raw input
      
    • 说明:

      • input 是一个常见的原始输入模块,它从触摸屏设备读取原始数据。
      • 这个模块通常放在模块链的最前面,确保其他模块可以接收到未经处理的原始数据。
  • module pthres pmin=1

    • 功能:设置触摸屏的最小压力阈值。

    • 用途:这个模块用于过滤掉压力值低于指定阈值的触摸点,从而减少误触。

    • 配置示例:

      module pthres pmin=1
      
    • 参数:

      • pmin:最小压力阈值。只有当触摸点的压力值大于或等于 pmin 时,才会被认为是有效的触摸点。
    • 说明:

      • 通过设置 pmin,可以过滤掉轻微的触摸或误触,提高触摸屏的准确性。
      • 例如,pmin=1 表示只有当压力值大于或等于 1 时,触摸点才被认为是有效的。
  • module dejitter delta=100

    • 功能:减少触摸点的抖动。

    • 用途:这个模块通过平滑算法来减少触摸点的微小抖动,提高触摸屏的稳定性。

    • 配置示例:

      module dejitter delta=100
      
    • 参数:

      • delta:抖动阈值。只有当触摸点的位置变化超过 delta 时,才会被认为是有效的移动。
    • 说明:

      • 通过设置 delta,可以过滤掉触摸点的微小抖动,使触摸点的移动更加平滑。
      • 例如,delta=100 表示只有当触摸点的位置变化超过 100 个单位时,才会被认为是有效的移动。
  • module linear

    • 功能:进行线性校准。

    • 用途:这个模块通过线性变换将触摸点的坐标映射到屏幕的实际坐标系中。它通常用于校正触摸屏的偏移和缩放。

    • 配置示例:

      module linear
      
    • 说明:

      • linear 模块通过线性变换将触摸点的坐标从触摸屏的物理坐标系转换到屏幕的逻辑坐标系。
      • 通常需要通过校准工具(如 ts_calibrate)来生成校准参数,并将其保存到配置文件中。
      • 这个模块确保触摸点的坐标与屏幕的实际位置对齐,提高触摸屏的准确性。

3. 使用tslib库打印触摸屏2点之间的距离

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <getopt.h>#include <linux/input.h>#include <sys/ioctl.h>#include <tslib.h>/*** @brief 计算两点之间的距离平方** @param point1 第一个点* @param point2 第二个点* @return 两点之间的距离平方*/
int distance(struct ts_sample_mt *point1, struct ts_sample_mt *point2)
{int x = point1->x - point2->x;int y = point1->y - point2->y;return x * x + y * y;
}/*** @brief 主函数** @param argc 参数个数* @param argv 参数列表* @return 0 表示成功,非0表示失败*/
int main(int argc, char **argv)
{struct tsdev *ts; // 触摸屏设备句柄int i;int ret;struct ts_sample_mt **samp_mt; // 当前触摸点数据struct ts_sample_mt **pre_samp_mt; // 上一次触摸点数据int max_slots; // 最大触摸点数int point_pressed[20]; // 存储按下的触摸点索引struct input_absinfo slot; // 触摸点槽的信息int touch_cnt = 0; // 当前按下的触摸点数量// 初始化触摸屏设备ts = ts_setup(NULL, 0);if (!ts) {printf("ts_setup err\n");return -1;}// 获取触摸点槽的最大值和最小值if (ioctl(ts_fd(ts), EVIOCGABS(ABS_MT_SLOT), &slot) < 0) {perror("ioctl EVIOGABS");ts_close(ts);return errno;}// 计算最大触摸点数max_slots = slot.maximum + 1 - slot.minimum;// 分配内存用于存储当前触摸点数据samp_mt = malloc(sizeof(struct ts_sample_mt *));if (!samp_mt) {ts_close(ts);return -ENOMEM;}samp_mt[0] = calloc(max_slots, sizeof(struct ts_sample_mt));if (!samp_mt[0]) {free(samp_mt);ts_close(ts);return -ENOMEM;}// 分配内存用于存储上一次触摸点数据pre_samp_mt = malloc(sizeof(struct ts_sample_mt *));if (!pre_samp_mt) {ts_close(ts);return -ENOMEM;}pre_samp_mt[0] = calloc(max_slots, sizeof(struct ts_sample_mt));if (!pre_samp_mt[0]) {free(pre_samp_mt);ts_close(ts);return -ENOMEM;}// 初始化上一次触摸点数据的有效标志for (i = 0; i < max_slots; i++) {pre_samp_mt[0][i].valid = 0;}// 主循环while (1) {// 读取当前触摸点数据ret = ts_read_mt(ts, samp_mt, max_slots, 1);if (ret < 0) {printf("ts_read_mt err\n");ts_close(ts);return -1;}// 更新上一次触摸点数据for (i = 0; i < max_slots; i++) {if (samp_mt[0][i].valid) {memcpy(&pre_samp_mt[0][i], &samp_mt[0][i], sizeof(struct ts_sample_mt));}}// 统计当前按下的触摸点数量touch_cnt = 0;for (i = 0; i < max_slots; i++) {if (pre_samp_mt[0][i].valid && pre_samp_mt[0][i].tracking_id != -1) {point_pressed[touch_cnt++] = i;}}// 如果有两个触摸点按下,计算它们之间的距离if (touch_cnt == 2) {printf("distance: %08d\n", distance(&pre_samp_mt[0][point_pressed[0]], &pre_samp_mt[0][point_pressed[1]]));}}// 释放资源free(samp_mt[0]);free(samp_mt);free(pre_samp_mt[0]);free(pre_samp_mt);ts_close(ts);return 0;
}

需要明确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;             // 触摸点的槽位编号,用于多点触控int tracking_id;      // 跟踪ID,用于标识同一个触摸点的连续事件int tool_type;        // 工具类型(例如手指、笔等)int tool_x;           // 工具的X坐标(如果有工具类型信息)int tool_y;           // 工具的Y坐标(如果有工具类型信息)unsigned int touch_major; // 触摸区域的主要轴长度unsigned int width_major; // 触摸工具的主要轴宽度unsigned int touch_minor; // 触摸区域的次要轴长度unsigned int width_minor; // 触摸工具的次要轴宽度int orientation;       // 触摸点的方向(角度)int distance;          // 触摸点与触摸屏表面的距离int blob_id;           // 触摸点的Blob ID,用于标识多个触摸点的合并struct timeval tv;     // 时间戳,记录事件发生的时间short pen_down;        // 笔触状态,1表示按下,0表示抬起/* 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;           // 标记该样本是否包含新数据,非0表示有新数据,0表示无新数据
};

相关文章:

嵌入式Linux基于IMX6ULL tslib学习总结

目录 1. tslib开源库介绍1.1 tslib主要功能1.2 架构 2. tslib代码简单分析2.1 ts_print_mt.c分析代码2.2 ts_setup代码分析2.3 ts_open代码分析2.4 ts_config代码分析2.5 ts_read_mt代码分析2.6 tslib中4个模块的含义 3. 使用tslib库打印触摸屏2点之间的距离 基于韦东山IMX6ULL…...

pinia的使用

一、安装 Pinia 首先&#xff0c;确保你的项目已经安装了 Vue.js&#xff08;版本 3.x&#xff09;。如果没有&#xff0c;请先安装 Vue。 然后使用包管理器&#xff08;如 npm 或 yarn&#xff09;安装 Pinia&#xff1a; 通过 npm 安装&#xff1a;npm install pinia通过 ya…...

亚信安全携手飞书“走近先进” 与保隆科技探索制造业数字化转型

亚信安全携手飞书组织举办“走近先进”活动。近日活动“走近”了中国汽车供应链百强、上海市制造业五十强企业——上海保隆汽车科技股份有限公司&#xff08;以下简称“保隆科技”&#xff09;。活动围绕“突破桎梏 加速升级”的主题&#xff0c;聚焦企业数字化转型的核心议题&…...

mybatis-plus方法无效且字段映射失败错误排查

问题&#xff1a; Invalid bound statement (not found): com.htlc.assetswap.mapper.WalletMapper.insert&#xff0c;并且select * 进行查询时带下划线的字段未成功映射。 排查&#xff1a; 1.检查WalletMapper接口&#xff0c;确保继承自BaseMapper 2.启用驼峰命名法映射。a…...

poi模板动态导出,下拉框联动,公式设置

背景&#xff1a;有一个动态导出模板的需求&#xff0c;根据页面维护的数据比如模板名称&#xff0c;模板的sheet名称&#xff0c;列名称宽度高度等&#xff0c;导出excel文件。打破以往把excel上传到代码的resource中&#xff0c;或者文件服务中&#xff0c;再下载出来导入。这…...

string的实际应用 -- 大数相加 、大数相乘

前言&#xff1a;哎&#xff0c;做题好难o(╥﹏╥)o&#xff0c;有时候想不到&#xff0c;而有时候则是想到了却没办法理清思路&#xff0c;转化为代码。有必要反思了┓(;_&#xff40;)┏&#xff0c;是否是做的太少了&#xff0c;或是自己的基础欠缺。 大学总是有些迷茫~ ​​…...

16:(标准库)ADC三:使用外部触发启动ADC/模拟看门狗

使用外部触发启动ADC 1、外部中断线EXTI11触发ADC2、外部定时器TIM2_CH2触发ADC3、ADC中模拟看门狗的使用 1、外部中断线EXTI11触发ADC ADC的触发方式有很多&#xff0c;一般情况都是使用软件触发反式启动ADC转换。除了软件触发方式还能使用外部事件触发启动ADC转换。如下图所…...

前端框架 react 性能优化

目录 一、不使用任何性能优化API进行优化 二、通过性能优化API优化 1、React.memo 2、useCallback 3、useMemo 4、PureComponent 三、总结​ 总览&#xff1a;react的优化核心思想就是让react跳过重新渲染那个些没有改变的Component&#xff0c;而只重新渲染发生变化的C…...

一加ACE 3 Pro手机无法连接电脑传输文件问题

先说结论&#xff1a;OnePlus手机无法连接电脑传输数据的原因&#xff0c;大概率是一加数据线的问题。尝试其他手机品牌的数据线&#xff08;比如华为&#xff09;&#xff0c;再次尝试。 连接电脑方法&#xff1a; 1 打开开发者模式&#xff08;非必要操作&#xff09; 进入…...

✅ Qt流式布局

Qt流式布局 前段时间&#xff0c;曾经对某个软件的一个“流式布局”有点感兴趣&#xff0c;什么叫“流式布局”呢&#xff1f;请看下图: 简而言之&#xff0c;流式布局就是布局应能够根据界面尺寸的变化自动调整其内部控件的位置。然而&#xff0c;Qt 提供的标准布局&#xff…...

【微服务】RabbitMQ与SpringAMQP消息队列

一、初识MQ 1. 同步通讯 同步通讯就好比双方打电话&#xff0c;可以实时响应&#xff0c;但只能一对一&#xff0c;只能同时和一个人聊天。 异步通讯就好比两个人发信息&#xff0c;你发信息给对方&#xff0c;对方不一定给你回复&#xff0c;但是可以一对多&#xff0c;可…...

C++设计模式-中介者模式

动机(Motivation) 多个对象相互关联的情况&#xff0c;对象之间常常会维持一种复杂的引用关系&#xff0c;如果遇到一些需求的更改&#xff0c;这种直接的引用关系将面临不断的变化。在这种情况下&#xff0c;可以使用一种”中介对象“来管理对象间的关联关系&#xff0c;避免…...

js实现分页效果

分页总结&#xff1a; 先写好html格式和css样式 再写js时先将他们都获取过来&#xff0c;设置一个k为页数的下标&#xff0c;num为每页的数据数量&#xff0c;pages为页数&#xff0c;并且获取json数据&#xff0c;再将data获取到全局 写for循环&#xff0c;设置一个变量为i…...

【WRF-Urban】多层建筑能源参数化模型概述:原理

【WRF-Urban】多层建筑能源参数化模型概述&#xff1a;原理 1 概述1.1 原理1.2 使用步骤 2参考 多层建筑能源参数化&#xff08;Multi-layer Building Energy Parameterization, BEP&#xff09;模型是一种用于模拟城市环境中多层建筑群的能量交换和微气候影响的参数化模型。该…...

ShuffleNet V2:高效卷积神经网络架构设计的实用指南

摘要 https://arxiv.org/pdf/1807.11164 当前&#xff0c;神经网络架构设计大多以计算复杂度的间接指标&#xff0c;即浮点运算数&#xff08;FLOPs&#xff09;为指导。然而&#xff0c;直接指标&#xff08;例如速度&#xff09;还取决于其他因素&#xff0c;如内存访问成本…...

1123--collection接口,list接口,set接口

目录 一 java 1. 集合 2. 集合框架图--remember 3. collection接口 3.1 collection接口的常用方法 3.1.1 add&#xff08;&#xff09; 3.1.2 remove&#xff08;&#xff09;-返回删除后的对象 3.1.3 contains&#xff08;&#xff09;--返回布尔值 3.1.4 size&…...

基于BindingList的WinForm数据绑定机制与DataGridView动态刷新技术

前言&#xff1a; 本文以连接SQLite为例进行代码演示 一、首先建立里一个模型类 public class MyData{public int id { get; set; }public string name { get; set; }public int age { get; set; }public string sex { get; set; }public string address { get; set; }} 二、…...

大数据新视界 -- Hive 数据分区:精细化管理的艺术与实践(上)(7/ 30)

&#x1f496;&#x1f496;&#x1f496;亲爱的朋友们&#xff0c;热烈欢迎你们来到 青云交的博客&#xff01;能与你们在此邂逅&#xff0c;我满心欢喜&#xff0c;深感无比荣幸。在这个瞬息万变的时代&#xff0c;我们每个人都在苦苦追寻一处能让心灵安然栖息的港湾。而 我的…...

对于相对速度的重新理解 - 插一句

因为发了太多的公式&#xff0c;系统提示插入图片太频繁&#xff0c;下一个部分稍后再写。 这里要强调一下&#xff1a; 狭义相对论的平方和形式&#xff0c;其实就是因为分不清虚数单位的大小才写成这个样子。或者用物理语言来说&#xff0c;就是认为所有惯性系的“光速”都…...

css基础(27)_行内、行内块元素之间的空白问题

行内、行内块元素之间的空白问题 产生的原因&#xff1a;行内元素、行内块元素&#xff0c;彼此之间的换行会被浏览器解析为一个空白字符。 案例一&#xff1a; <!DOCTYPE html> <html lang"zh"> <head><meta charset"UTF-8">&…...

如果接口返回值图片有很长一串码,需要添加前缀

需要在前面添加前缀&#xff1a;data:image/jpeg;base64,然后将值赋值给<img :src"originalImage" /> this.tableLists.map((item)>{item.originalImage "data:image/jpeg;base64,"item.originalImage})以上方法会导致出现一个小bug&#xff0c;…...

《AI大模型开发笔记》——ollama应用全面解析

入门篇 1 ollama是什么? Ollama 是一个支持在本地运行大语言模型的工具,兼容 Windows、Linux 和 MacOS 操作系统。使用 Ollama,您仅需一行命令即可启动模型。 2 如何安装? Windows和MacOS用户,从下面链接下载安装即可: 下载地址:https://ollama.com/download Linux系…...

Paddle Inference部署推理(三)

三&#xff1a;Paddle Inference推理 导出模型 Paddle Inference支持使用飞桨静态图模型进行推理&#xff0c;您可以通过以下两种方式获取静态图模型&#xff1a; &#xff08;1&#xff09;飞桨框架导出推理模型 飞桨框架在训练模型过程中&#xff0c;会在本地存储最终训练…...

CSP/信奥赛C++语法基础刷题训练(23):洛谷P1217:[USACO1.5] 回文质数 Prime Palindromes

CSP/信奥赛C语法基础刷题训练&#xff08;23&#xff09;&#xff1a;洛谷P1217&#xff1a;[USACO1.5] 回文质数 Prime Palindromes 题目描述 因为 151 151 151 既是一个质数又是一个回文数&#xff08;从左到右和从右到左是看一样的&#xff09;&#xff0c;所以 151 151 …...

《跨越语言壁垒:Python 人工智能原型到 C++可执行程序的转型之路》

在人工智能的广阔天地里&#xff0c;Python 以其简洁易用和丰富的库资源成为众多开发者快速搭建人工智能原型的首选语言。然而&#xff0c;在一些对性能和资源控制要求极高的场景下&#xff0c;C则展现出无可比拟的优势。那么&#xff0c;如何将 Python 中开发的人工智能原型代…...

flowable流程图详细绘制教程

文章目录 前言一、flowable是什么&#xff1f;回答下之前的问题 二、flowable-modeler使用1. 使用步骤2.开始绘制弄一个请假的流程 三 加载该流程总结 前言 flowable有些晦涩难懂的东西&#xff1a; 我最开始接触的时候,还是用的activity,当时觉得好复杂,那么这次经过我自己在…...

Figma入门-基本操作制作登录页

Figma入门-基本操作制作登录页 前言 在之前的工作中&#xff0c;大家的原型图都是使用 Axure 制作的&#xff0c;印象中 Figma 一直是个专业设计软件。 最近&#xff0c;很多产品朋友告诉我&#xff0c;很多原型图都开始用Figma制作了&#xff0c;并且很多组件都是内置的&am…...

在windows操作系统上,用git与github账户连接

一、环境准备 1.1 git软件 1.2 github账号 1.3 创建一个项目目录&#xff0c;比如 D:\project\gitproject 二、开始操作 1. 进入项目目录下&#xff0c;右键&#xff0c;如图&#xff0c;打开git bash命令行 2. 在命令行输入以下三个命令 $ git config --global user.name &quo…...

springboot系列--拦截器执行原理

一、拦截器核心概念 一、定义 拦截器&#xff08;Interceptor&#xff09;是框架级别的组件&#xff0c;用于在请求的不同阶段&#xff08;如到达控制器之前&#xff08;也就是接口&#xff09;、处理完成之后&#xff09;动态地拦截和处理 HTTP 请求。 二、使用场景 一、用户…...

数据可视化复习2-绘制折线图+条形图(叠加条形图,并列条形图,水平条形图)+ 饼状图 + 直方图

目录 目录 一、绘制折线图 1.使用pyplot 2.使用numpy ​编辑 3.使用DataFrame ​编辑 二、绘制条形图&#xff08;柱状图&#xff09; 1.简单条形图 2.绘制叠加条形图 3.绘制并列条形图 4.水平条形图 ​编辑 三、绘制饼状图 四、绘制散点图和直方图 1.散点图 2…...

STM32F10x 定时器

使用定时器实现&#xff1a;B5 E5的开关 添加相关的.h路径文件 添加相关的.c配置文件 led.h文件 用于声明LED函数 #ifndef __LED_H //没有定义__LED_H #define __LED_H //就定义__LED_H #define LED1_ON GPIO_ResetBits(GPIOB,GPIO_Pin_5) #defi…...

VBA技术资料MF230:展开所有折叠视图并恢复

我给VBA的定义&#xff1a;VBA是个人小型自动化处理的有效工具。利用好了&#xff0c;可以大大提高自己的工作效率&#xff0c;而且可以提高数据的准确度。“VBA语言専攻”提供的教程一共九套&#xff0c;分为初级、中级、高级三大部分&#xff0c;教程是对VBA的系统讲解&#…...

【数据结构OJ】【图论】图综合练习--拓扑排序

题目描述 已知有向图&#xff0c;顶点从0开始编号&#xff0c;求它的求拓扑有序序列。 拓扑排序算法&#xff1a;给出有向图邻接矩阵 1.逐列扫描矩阵&#xff0c;找出入度为0且编号最小的顶点v 2.输出v&#xff0c;并标识v已访问 3.把矩阵第v行全清0 重复上述步骤&#xff0…...

网络安全防范

网络安全防范技术 所属课程网络攻防实践作业要求第六次作业 实践内容 学习总结 PDR&#xff0c;$$P^2$$DR安全模型。 防火墙&#xff08;Firewall&#xff09;&#xff1a; 网络访问控制机制&#xff0c;布置在网际间通信的唯一通道上。 不足&#xff1a;无法防护内部威胁&…...

Linux nc 命令详解

简介 nc 全称 netcat&#xff0c;是一个在 Linux 中多功能的网络工具&#xff0c;通常用于通过 TCP 或 UDP 读取和写入网络连接&#xff0c;也能作为客户端或服务端用来 debug&#xff0c;测试&#xff0c;网络问题分析。 常用示例 检查端口是否是打开的 nc -zv <hostna…...

解决 Gradle 报错:`Plugin with id ‘maven‘ not found` 在 SDK 开发中的问题

在 SDK 开发过程中&#xff0c;使用 Gradle 构建和发布 SDK 是常见的任务。在将 SDK 发布为 AAR 或 JAR 包时&#xff0c;你可能会使用 apply plugin: maven 来发布到本地或远程的 Maven 仓库。但是&#xff0c;随着 Gradle 版本的更新&#xff0c;特别是从 Gradle 7 版本开始&…...

stm32cubemx+VSCODE+GCC+makefile 开发环境搭建

title: stm32cubemxVSCODEGCCmakefile 开发环境搭建 tags: FreertosHalstm32cubeMx 文章目录 内容往期内容导航第一步准备环境vscode 插件插件配置点灯 内容 往期内容导航 第一步准备环境 STM32CubeMXVSCODEMinGWOpenOcdarm-none-eabi-gcc 然后把上面下载的软件 3 4 5 bin 文…...

postgresql|数据库开发|python的psycopg2库按指定顺序批量执行SQL文件(可离线化部署)

一、 psycopg2简介 psycopg2库是python的一个可直接操作postgresql数据库的类库&#xff0c;是一个用于Python编程语言的PostgreSQL数据库适配器。它允许开发人员使用Python语言与PostgreSQL数据库进行交互和操作&#xff0c;不同于java&#xff0c;需要专用的一个驱动&#…...

学习ASP.NET Core的身份认证(基于Cookie的身份认证3)

用户通过验证后调用HttpContext.SignInAsync函数将用户的身份信息保存在认证Cookie中,以便后续的请求可以验证用户的身份,该函数原型如下所示&#xff0c;其中properties参数的主要属性已在前篇文章中学习&#xff0c;本文学习scheme和principal的意义及用法。 public static …...

Java 中的 HashMap 原理详解:底层结构与实现机制

HashMap 是 Java 中最常用的数据结构之一&#xff0c;它以其高效的存取速度在众多应用场景中被广泛使用。理解 HashMap 的底层实现原理&#xff0c;对提升开发效率、优化性能以及编写高效的代码都至关重要。本文将深入探讨 HashMap 的数据结构、存储机制、解决冲突的策略、扩容…...

数据库MYSQL——表的设计

文章目录 前言三大范式&#xff1a;几种实体间的关系&#xff1a;一对一关系&#xff1a;一对多关系&#xff1a;多对多关系&#xff1a; 前言 之前的博客中我们讲解的是关于数据库的增删改查与约束的基本操作&#xff0c; 是在已经创建数据库&#xff0c;表之上的操作。 在实…...

CUDA补充笔记

文章目录 一、不同核函数前缀二、指定kernel要执行的线程数量三、线程需要两个内置坐标变量来唯一标识线程四、不是blocksize越大越好&#xff0c;上限一般是1024个blocksize 一、不同核函数前缀 二、指定kernel要执行的线程数量 总共需要线程数是&#xff1a; 1 * N N个线程…...

OSI七层模型和TCP/IP五层模型详细介绍

这里写目录标题 一.OSI含义二.OSI七层模型1.应用层2.表示层3.会话层4.传输层5.网络层6.数据链路层7.物理层 TCP/IP五层协议1.应用层2.运输层运行在TCP上的协议运行在UDP上的协议 3.网络层IP协议配套使用的协议 4.数据链路层 四.网络协议分层的好处 一.OSI含义 OSI即是开放式通…...

mac安装Pytest、Allure、brew

安装环境 安装pytest 命令 pip3 install pytest 安装allure 命令&#xff1a;brew install allure 好吧 那我们在安装allure之前 我们先安装brew 安装brew 去了官网复制了命令 还是无法下载 如果你们也和我一样可以用这个方法哦 使用国内的代码仓库来执行brew的安装脚本…...

Linux/Windows/OSX 上面应用程序重新启动运行。

1、Linux/OSX 上面重新运行程序&#xff0c;直接使用 execvp 函数就可以了&#xff0c;把main 函数传递来的 argv 二维数组&#xff08;命令行参数&#xff09;传进去就可以&#xff0c;注意不要在 fork 出来的子进程搞。 2、Windows 平台可以通过 CreateProcess 函数来创建新的…...

自定义 Kafka 脚本 kf-use.sh 的解析与功能与应用示例

Kafka&#xff1a;分布式消息系统的核心原理与安装部署-CSDN博客 自定义 Kafka 脚本 kf-use.sh 的解析与功能与应用示例-CSDN博客 Kafka 生产者全面解析&#xff1a;从基础原理到高级实践-CSDN博客 Kafka 生产者优化与数据处理经验-CSDN博客 Kafka 工作流程解析&#xff1a…...

Odoo :免费且开源的农牧行业ERP管理系统

文 / 开源智造Odoo亚太金牌服务 引言 提供农牧企业数字化、智能化、无人化产品服务及全产业链高度协同的一体化解决方案&#xff0c;提升企业智慧种养、成本领先、产业互联的核心竞争力。 行业典型痛点 一、成本管理粗放&#xff0c;效率低、管控弱 产品研发过程缺少体系化…...

AI的自我陷阱:大型神经网络训练中的模型崩溃现象

10月7日&#xff0c;发布在arxiv上的一篇名为Strong Model Collapse的论文&#xff0c;由Meta 、纽约大学和加州大学洛杉矶分校的研究人员共同发表的研究表明在训练大型神经网络&#xff08;如 ChatGPT 和 Llama&#xff09;时&#xff0c;由于训练语料库中包含合成数据而导致的…...

Python(下载安装)

简介 开发工具&#xff1a;pycharm, VS Code 1. 下载&#xff08;解释器程序&#xff09; Download Python | Python.org 2. 安装&#xff08;解释器程序&#xff09; 双击下载后的exe文件 查看版本&#xff1a;python&#xff08;并可以执行python代码&#xff09; 3. 下载…...

记录一种在内核空间向用户空间通知中断的方法

记录一种在内核空间向用户空间通知中断的方法 0.前言1.代码实现1)内核设备驱动实现2)消息通知实现3)测试程序 2.解析 参考文章&#xff1a;Linux驱动实践&#xff1a;中断处理函数如何【发送信号】给应用层&#xff1f; 0.前言 最近在项目中遇到一个需求&#xff0c;需要将一个…...