tslib is an open source library of touch screen. You can use it to access touch screen devices. You can input various "filter" addresses to the devices here
After compiling tslib, you can get the libts library and various tools: calibration tools and testing tools
1, tslib framework analysis
The main codes of tslib are as follows:
├── src # src / interface function │ ├── ts_setup.c │ ├── ts_open.c │ ├── ts_config.c ├── plugins # plugins / plug-ins / module s │ ├── linear.c │ ├── dejitter.c │ ├── pthres.c │ ├── input-raw.c ├── tests # tests / test program │ ├── ts_test.c │ ├── ts_test_mt.c │ ├── ts_print.c │ ├── ts_print_mt.c
The core lies in the "plug-ins" or "modules" in the "plugins" directory. Each file in this directory is a module, and each module provides two functions
read,read_mt function, the former is used to read the data of single touch screen, and the latter is used to read the data of multi touch screen.
1.1 tslib framework analysis
To analyze the framework of tslib, let's see how to use the sample program. We refer to ts_test.c and ts_test_mt.c, the former is used for general touch screen (such as resistance screen and single point capacitance screen), and the latter is used for multi-point touch screen.
- Learning TS_ Source code of setup. C
struct tsdev *ts_setup(const char *dev_name, int nonblock) //dev_name specifies the device, and nonblock specifies whether to block the mode { const char * const *defname; struct tsdev *ts = NULL; #if defined (__linux__) char *fname = NULL; #endif /* __linux__ */ /* Judge dev_ Whether name is empty. If it is empty, it will be retrieved from tslib in the system environment_ Specify the device name in tsdevice */ dev_name = dev_name ? dev_name : getenv("TSLIB_TSDEVICE"); if (dev_name != NULL) { ts = ts_open(dev_name, nonblock); //Open if not empty } else { defname = &ts_name_default[0]; //From TS_ name_ Find in default while (*defname != NULL) { ts = ts_open(*defname, nonblock); if (ts != NULL) break; ++defname; } } #if defined (__linux__) if (!ts) { fname = scan_devices(); //If the above method is still not found, you can only call the search function to find it if (!fname) return NULL; ts = ts_open(fname, nonblock); free(fname); } #endif /* __linux__ */ /* if detected try to configure it */ if (ts && ts_config(ts) != 0) { //If it is open, call ts_config for configuration ts_error("ts_config: %s\n", strerror(errno)); ts_close(ts); return NULL; } return ts; } static const char * const ts_name_default[] = { //Default ts_name_default has these "/dev/input/ts", "/dev/input/touchscreen", "/dev/touchscreen/ucb1x00", NULL };
If the dev passed in_ Name and TS_ name_ If default cannot be opened, call scan_devices()
static char *scan_devices(void) { //... ndev = scandir(DEV_INPUT_EVENT, &namelist, is_event_device, alphasort); if (ndev <= 0) return NULL; for (i = 0; i < ndev; i++) { //... fd = open(fname, O_RDONLY); if (fd < 0) continue; if ((ioctl(fd, EVIOCGPROP(sizeof(propbit)), propbit) < 0) || //Get device properties !(propbit[BIT_WORD(INPUT_PROP_DIRECT)] & //If the attribute is INPUT_PROP_DIRECT, you'll find it BIT_MASK(INPUT_PROP_DIRECT))) { close(fd); continue; } else { close(fd); filename = malloc(strlen(DEV_INPUT_EVENT) + strlen(EVENT_DEV_NAME) + 12); if (!filename) break; //.... } } //... return filename; }
- Call TS_ After opening, you can open a device node to construct a tsdev structure.
struct tsdev *ts_open(const char *name, int nonblock) { struct tsdev *ts; int flags = O_RDWR; //.... if (nonblock) { #ifndef WIN32 flags |= O_NONBLOCK; //Add file identifier #endif } ts = malloc(sizeof(struct tsdev)); if (!ts) return NULL; memset(ts, 0, sizeof(struct tsdev)); //Construct a tsdev structure ts->eventpath = strdup(name); //Should be the save file name if (!ts->eventpath) goto free; //.... ts->fd = open(name, flags); //open device //.... return ts; //.... }
- Re TS_ In setup, when TS is executed_ After the open is successful, TS will be executed_ config
int ts_config(struct tsdev *ts) { return __ts_config(ts, NULL, NULL, NULL); } 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) { //Get environment variable value conffile = strdup(TS_CONF); //If the environment variable is not set, the default value is used. TS_CONF is / etc/ts.conf //Error handling } f = fopen(conffile, "r"); //... error handling buf[BUF_SIZE - 2] = '\0'; while ((p = fgets(buf, BUF_SIZE, f)) != NULL) { //Read one line //The code for reading the content of the configuration is omitted here /* Search for the option. */ if (strcasecmp(tok, "module") == 0) { //Start with module #if !defined HAVE_STRSEP module_name = ts_strsep(&p, " \t"); #else module_name = strsep(&p, " \t"); #endif discard_null_tokens(&p, &module_name); if (!conffile_modules) { ret = ts_load_module(ts, module_name, p); //Call ts_load_module to load the module } else { #ifdef DEBUG printf("TSLIB_CONFFILE: module %s %s\n", module_name, p); #endif sprintf(conffile_modules[line], "%s", module_name); if (conffile_params) sprintf(conffile_params[line], "%s", p); } } else if (strcasecmp(tok, "module_raw") == 0) { //module_ Start with raw #if !defined HAVE_STRSEP module_name = ts_strsep(&p, " \t"); #else module_name = strsep(&p, " \t"); #endif discard_null_tokens(&p, &module_name); if (!conffile_modules) { ret = ts_load_module_raw(ts, module_name, p); //Call ts_load_module_raw load module_raw module } else { #ifdef DEBUG printf("TSLIB_CONFFILE: module_raw %s %s\n", module_name, p); #endif sprintf(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; } } //... error handling return ret; }
ts_config configuration file is also available in the source code, etc/ts.conf
module_raw input module pthres pmin=1 module dejitter delta=100 module linear
- ts_config will call ts_load_module or ts_load_module_raw
int ts_load_module(struct tsdev *ts, const char *module, const char *params) { return __ts_load_module(ts, module, params, 0); //Finally, it is called__ ts_load_module } int ts_load_module_raw(struct tsdev *ts, const char *module, const char *params) { return __ts_load_module(ts, module, params, 1); //But the parameters are different. One is 1 and the other is 0 } 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; //... info = __ts_load_module_static(ts, module, params); //Load static library #ifdef HAVE_LIBDL if (!info) info = __ts_load_module_shared(ts, module, params); //If not, load the dynamic library #endif if (!info) return -1; if (raw) ret = __ts_attach_raw(ts, info); //Attach info to ts structure else ret = __ts_attach(ts, info); if (ret) { //.... handle = info->handle; if (info->ops->fini) info->ops->fini(info); //....close } return ret; } int __ts_attach(struct tsdev *ts, struct tslib_module_info *info) { info->dev = ts; info->next = ts->list; //Info - > next execute the contents pointing to TS - > list ts->list = info; //TS - > list points to the latest info return 0; } 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; //Info - > next points to prev_list ts->list_raw = info; //ts->list_ Raw points to the info you want most /* * ensure the last item in the normal list now points to the * top of the raw list. */ if (ts->list == NULL || ts->list == prev_list) { /* main list is empty, ensure it points here */ ts->list = info; //TS - > list points to TS - > list_ Raw, if TS_ If the list is empty return 0; } for (next = ts->list, prev = next; next != NULL && next != prev_list; next = prev->next, prev = next); prev->next = info; return 0; }
The summary is TS - > list_ Raw points to module_ Latest inserted module of raw_ Raw module
TS - > list points to the newly inserted module
- ts_read processes the module s in turn
int ts_read(struct tsdev *ts, struct ts_sample *samp, int nr) { int result; //.... result = ts->list->ops->read(ts->list, samp, nr); //Starting from TS list, call the read function in ops to read //.... return result; } //The first module is the linear module, which is found in plugins/linear.c static const struct tslib_ops linear_ops = { .read = linear_read, .read_mt = linear_read_mt, .fini = linear_fini, }; static int linear_read(struct tslib_module_info *info, struct ts_sample *samp, int nr_samples) { struct tslib_linear *lin = (struct tslib_linear *)info; int ret; int xtemp, ytemp; ret = info->next->ops->read(info->next, samp, nr_samples); //Continue to call the next module recursively if (ret >= 0) { //... do something of your own } return ret; }
Therefore, although the read sequence of modules starts from the last, it is read in a recursive way,
The first module to be processed should be the first module in the ts.conf file.
2, Cross compile and test tslib
- Configuration tool chain
export ARCH=arm export CROSS_COMPILE=arm-linux-gnueabihf- export PATH=$PATH:/home/book/100ask_imx6ull-sdk/ToolChain/gcc-linaro-6.2.1-2016.11-x86_64_arm-linux-gnueabihf/bin
- Cross compilation
./configure --host=arm-linux-gnueabihf --prefix=/ make make install DESTDIR=$PWD/tmp
- Determine the directory of header files and library files in the tool chain
echo 'main(){}'| arm-linux-gnueabihf-gcc -E -v -
- Put the header file and library file in the tool chain directory
cd tslib-1.21/tmp/ cp include/* /home/book/100ask_imx6ull-sdk/ToolChain/gcc-linaro-6.2.1-2016.11-x86_64_arm-linux-gnueabihf/bin/../arm-linux-gnueabihf/libc/usr/include cp -d lib/*so* /home/book/100ask_imx6ull-sdk/ToolChain/gcc-linaro-6.2.1-2016.11-x86_64_arm-linux-gnueabihf/bin/../arm-linux-gnueabihf/libc/usr/lib/
- Copy the library file to the development board
cp /mnt/tslib-1.21/tmp/lib/* -drf /lib cp /mnt/tslib-1.21/tmp/bin/* /bin cp /mnt/tslib-1.21/tmp/etc/ts.conf -d /etc
- Perform the test procedure
ts_test_mt
3, ts_read_mt learning
- Analysis of tslib interface function
When two fingers click on the screen, you can get the data with hexdump /dev/input/event2.
The driver uses slot and tracking_id to identify a contact when tracking_ When ID equals - 1, the identification contact is released.
The touch screen can support multiple contacts, such as five: tslib to simplify processing, even if there are only two contacts, ts_read_mt function will also return 5 contact data. You can judge whether the data is valid according to the flag bit.
ts_ read_ The prototype of MT function is as follows:
//ts is the open device, ts_sample_mt is the storage place for reading data, max_slots is the maximum number of contacts and nr is the number of groups of data read int ts_read_mt(struct tsdev *ts, struct ts_sample_mt **samp, int max_slots, int nr) //For example, nr is 1, max_ If slots is 5, the data is saved in samp[0][0]~samp[0][4] //If nr is 2, Max_ If slots is 5, the data is saved in samp[0][0]~samp[0][4], samp[1][0]~samp[1][4]
Learn ts_sample_mt structure:
struct ts_sample_mt { /* ABS_MT_* event codes. linux/include/uapi/linux/input-event-codes.h * has the definitions. */ int x; int y; unsigned int pressure; int slot; int tracking_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; /* 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; //If void is not 0, it indicates that it contains new data };
4, Write code
Writing idea: continuously print the distance of 2 contacts, read 5 new data each time, and print the contact distance if 2 of them have contacts.
Because we have put the file in the compiler directory before, we can just use the header file directly.
The compile command is RM Linux gnueabihf GCC touch_ distance.c -o touch_ distance -lts
#include <stdio.h> #include <tslib.h> #include <sys/ioctl.h> #include <linux/input.h> #include <stdlib.h> #include <string.h> 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; } int main(int argc, char *argv[]) { int touch_cnt = 0; int i = 0; int ret = 0; struct tsdev *ts = NULL; int max_slots = 0; struct ts_sample_mt **samp_mt = NULL; struct ts_sample_mt **pre_samp_mt = NULL; struct input_absinfo slot; int point_pressed[20]; ts = ts_setup("/dev/input/event2", 0); if(!ts) { printf("ts_setup err\n"); return -1; } if (ioctl(ts_fd(ts), EVIOCGABS(ABS_MT_SLOT), &slot) < 0) { printf("ioctl EVIOGABS\n"); ts_close(ts); return -1; } max_slots = slot.maximum + 1 - slot.minimum; samp_mt = malloc(sizeof(struct ts_sample_mt *)); if (!samp_mt) { ts_close(ts); return -1; } samp_mt[0] = calloc(max_slots, sizeof(struct ts_sample_mt)); if (!samp_mt[0]) { free(samp_mt); ts_close(ts); return -1; } pre_samp_mt = malloc(sizeof(struct ts_sample_mt *)); if (!pre_samp_mt) { ts_close(ts); return -1; } 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 -1; } 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]])); } } return 0; }