什么是输入设备?输入设备其实就是能够产生输入事件的设备就称为输入设备,常见的输入设备包括鼠标、键盘、触摸屏、按钮等等,它们都能够产生输入事件,产生输入数据给计算机系统。
对于输入设备的应用编程其主要是获取输入设备上报的数据、输入设备当前状态等,譬如获取触摸屏当前触摸点的 X、Y 轴位置信息以及触摸屏当前处于按下还是松开状态。
先来了解什么是输入设备(也称为 input 设备),常见的输入设备有鼠标、键盘、触摸屏、遥控器、电脑画图板等,用户通过输入设备与系统进行交互。
由上面的介绍可知,输入设备种类非常多,每种设备上报的数据类型又不一样,那么 Linux 系统如何管理呢?Linux 系统为了统一管理这些输入设备,实现了一套能够兼容所有输入设备的框架,那么这个框架就是 input 子系统。驱动开发人员基于 input 子系统开发输入设备的驱动程序,input 子系统可以屏蔽硬件的差异,向应用层提供一套统一的接口。
基于 input 子系统注册成功的输入设备,都会在/dev/input 目录下生成对应的设备节点(设备文件),设备节点名称通常为 eventX(X 表示一个数字编号 0、1、2、3 等),譬如/dev/input/event0、/dev/input/event1、/dev/input/event2 等,通过读取这些设备节点可以获取输入设备上报的数据。
如果我们要读取触摸屏的数据,假设触摸屏设备对应的设备节点为/dev/input/event0
,那么数据读取流程如下:
/dev/input/event0
设备文件;read
),如果没有数据可读则会进入休眠(阻塞 I/O 情况下);当无数据可读时,程序会进入休眠状态(也就是阻塞),譬如应用程序读触摸屏数据,如果当前并没有去触碰触摸屏,自然是无数据可读;当我们用手指触摸触摸屏或者在屏上滑动时,此时就会产生触摸数据、应用程序就有数据可读了,应用程序会被唤醒,成功读取到数据。那么对于其它输入设备亦是如此,无数据可读时应用程序会进入休眠状态(阻塞式 I/O 方式下),当有数据可读时才会被唤醒。
首先我们要知道,应用程序打开输入设备对应的设备文件,向其发起读操作,那么这个读操作获取到的是什么样的数据呢?其实每一次 read 操作获取的都是一个 struct input_event 结构体类型数据,该结构体定义在
头文件中,它的定义如下:
struct input_event {
struct timeval time;
__u16 type;
__u16 code;
__s32 value;
};
结构体中的 time 成员变量是一个 struct timeval 类型的变量,该结构体在前面给大家介绍过,内核会记录每个上报的事件其发生的时间,并通过变量 time 返回给应用程序。时间参数通常不是那么重要,而其它3 个成员变量 type、code、value 更为重要。
/*
* Event types
*/
#define EV_SYN 0x00 //同步类事件,用于同步事件
#define EV_KEY 0x01 //按键类事件
#define EV_REL 0x02 //相对位移类事件(譬如鼠标)
#define EV_ABS 0x03 //绝对位移类事件(譬如触摸屏)
#define EV_MSC 0x04 //其它杂类事件
#define EV_SW 0x05
#define EV_LED 0x11
#define EV_SND 0x12
#define EV_REP 0x14
#define EV_FF 0x15
#define EV_PWR 0x16
#define EV_FF_STATUS 0x17
#define EV_MAX 0x1f
#define EV_CNT (EV_MAX+1)
以上这些宏定义也是在
#define KEY_RESERVED 0
#define KEY_ESC 1 //ESC 键
#define KEY_1 2 //数字 1 键
#define KEY_2 3 //数字 2 键
#define KEY_TAB 15 //TAB 键
#define KEY_Q 16 //字母 Q 键
#define KEY_W 17 //字母 W 键
#define KEY_E 18 //字母 E 键
#define KEY_R 19 //字母 R 键
……
相对位移事件
#define REL_X 0x00 //X 轴
#define REL_Y 0x01 //Y 轴
#define REL_Z 0x02 //Z 轴
#define REL_RX 0x03
#define REL_RY 0x04
#define REL_RZ 0x05
#define REL_HWHEEL 0x06
#define REL_DIAL 0x07
#define REL_WHEEL 0x08
#define REL_MISC 0x09
#define REL_MAX 0x0f
#define REL_CNT (REL_MAX+1)
绝对位移事件
触摸屏设备是一种绝对位移设备,它能够产生绝对位移事件;譬如对于触摸屏来说,一个触摸点所包含的信息可能有多种,譬如触摸点的 X 轴坐标、Y 轴坐标、Z 轴坐标、按压力大小以及接触面积等,所以 code变量告知应用程序当前上报的是触摸点的哪一种信息(X 坐标还是 Y 坐标、亦或者其它);绝对位移事件如下:
#define ABS_X 0x00 //X 轴
#define ABS_Y 0x01 //Y 轴
#define ABS_Z 0x02 //Z 轴
#define ABS_RX 0x03
#define ABS_RY 0x04
#define ABS_RZ 0x05
#define ABS_THROTTLE 0x06
#define ABS_RUDDER 0x07
#define ABS_WHEEL 0x08
#define ABS_GAS 0x09
#define ABS_BRAKE 0x0a
#define ABS_HAT0X 0x10
#define ABS_HAT0Y 0x11
#define ABS_HAT1X 0x12
#define ABS_HAT1Y 0x13
#define ABS_HAT2X 0x14
#define ABS_HAT2Y 0x15
#define ABS_HAT3X 0x16
#define ABS_HAT3Y 0x17
#define ABS_PRESSURE 0x18
#define ABS_DISTANCE 0x19
#define ABS_TILT_X 0x1a
#define ABS_TILT_Y 0x1b
#define ABS_TOOL_WIDTH 0x1c
......
除了以上列举出来的之外,还有很多,大家可以自己浏览
数据 同步
上面我们提到了同步事件类型 EV_SYN,同步事件用于实现同步操作、告知接收者本轮上报的数据已经完整。应用程序读取输入设备上报的数据时,一次 read 操作只能读取一个 struct input_event 类型数据,譬如对于触摸屏来说,一个触摸点的信息包含了 X 坐标、Y 坐标以及其它信息,对于这样情况,应用程序需要执行多次 read 操作才能把一个触摸点的信息全部读取出来,这样才能得到触摸点的完整信息。
那么应用程序如何得知本轮已经读取到完整的数据了呢?其实这就是通过同步事件来实现的,内核将
本轮需要上报、发送给接收者的数据全部上报完毕后,接着会上报一个同步事件,以告知应用程序本轮数据已经完整、可以进行同步了。
同步类事件中也包含了多种不同的事件,如下所示:
/*
* Synchronization events.
*/
#define SYN_REPORT 0
#define SYN_CONFIG 1
#define SYN_MT_REPORT 2
#define SYN_DROPPED 3
#define SYN_MAX 0xf
#define SYN_CNT (SYN_MAX+1)
所以的输入设备都需要上报同步事件,上报的同步事件通常是 SYN_REPORT,而 value 值通常为 0。
根据前面的介绍可知,对输入设备调用 read()会读取到一个 struct input_event 类型数据,本小节编写一个简单地应用程序,将读取到的 struct input_event 类型数据中的每一个元素打印出来、并对它们进行解析。
#include
#include
#include
#include
#include
#include
#include
int main(int argc, char *argv[])
{
struct input_event in_ev = {0};
int fd = -1;
/* 校验传参 */
if (2 != argc) {
fprintf(stderr, "usage: %s \n" , argv[0]);
exit(-1);
}
/* 打开文件 */
if (0 > (fd = open(argv[1], O_RDONLY))) {
perror("open error");
exit(-1);
}
for ( ; ; ) {
/* 循环读取数据 */
if (sizeof(struct input_event) !=
read(fd, &in_ev, sizeof(struct input_event))) {
perror("read error");
exit(-1);
}
printf("type:%d code:%d value:%d\n",
in_ev.type, in_ev.code, in_ev.value);
}
}
执行程序时需要传入参数,这个参数就是对应的输入设备的设备节点(设备文件),程序中会对传参进行校验。程序中首先调用 open()函数打开设备文件,之后在 for 循环中调用 read()函数读取文件,将读取到的数据存放在 struct input_event 结构体对象中,之后将结构体对象中的各个成员变量打印出来。注意,程序中使用了阻塞式 I/O 方式读取设备文件,所以当无数据可读时 read 调用会被阻塞,知道有数据可读时才会被唤醒!
Tips:设备文件不同于普通文件,读写设备文件之前无需设置读写位置偏移量。
使用交叉编译工具编译上述代码得到可执行文件,在使用交叉编译链之前要使能环境变量。
source /opt/fsl-imx-x11/4.1.15-2.1.0/environment-setup-cortexa7hf-neon-poky-linux-gnueabi
${CC} -o read_input read_input.c
开发板启动后通过nfs挂载Ubuntu目录的方式,将相应的文件拷贝到开发板上。简单来说,就是通过NFS在开发板上通过网络直接访问ubuntu虚拟机上的文件,并且就相当于自己本地的文件一样。
开发板想访问/home/zhiguoxin/myproject/alientek_app_development_source
这个目录中的文件,就要把/home/zhiguoxin/myproject/alientek_app_development_source
挂载到开发板的mnt
目录,这样就可以通过nfs来访问/home/zhiguoxin/myproject/alientek_app_development_source
了。
因为我的代码都放在/home/zhiguoxin/myproject/alientek_app_development_source
这个目录下,所以我们将这个目录作为NFS共享文件夹。设置方法参考移植SQLite3、OpenCV到RV1126开发板上开发人脸识别项目第一章。
Ubuntu IP为192.168.10.100,然后一般都是挂载在开发板的mnt目录下,这个目录是专门用来给我们作为临时挂载的目录。
然后使用MobaXterm软件通过SSH访问开发板。
ubuntu ip:192.168.10.100
windows ip:192.168.10.200
开发板ip:192.168.10.50
在开发板上执行以下命令:
mount -t nfs -o nolock,vers=3 192.168.10.100:/home/zhiguoxin/myproject/alientek_app_development_source /mnt
就将开饭的mnt
目录挂载在ubuntu的/home/zhiguoxin/myproject/alientek_app_development_source
目录下了。这样我们就可以在Ubuntu下修改文件,然后可以直接在开发板上执行可执行文件了。当然我这里的/home/zhiguoxin/myproject/
和windows
之间是一个共享目录,我也可以直接在windows
上面修改文件,然后ubuntu和开发板直接进行文件同步了。
Mini 开发板上都有一个用户按键 KEY0,它就是一个典型的输入设备,如下图所示:
该按键是提供给用户使用的一个 GPIO 按键,在出厂系统中,该按键驱动基于 input 子系统而实现,所以在/dev/input
目录下存在 KEY0 的设备节点,可以通过查看/proc/bus/input/devices
文件得知,查看该文件可以获取到系统中注册的所有输入设备相关的信息,如下所示:
cat /proc/bus/input/devices
接下来我们使用这个按键进行测试,执行下面的命令:
程序运行后,执行按下KEY0、松开KEY0等操作,终端将会打印出相应的信息,如上图所示。
第一行中type等于 1,表示上报的是按键事件EV_KEY,code=114,打开 input-event-codes.h 头文件进行查找,可以发现code=114对应的是键盘上的KEY_VOLUMEDOWN按键,这个是Mini开发板出厂系统已经配置好的。而value=1表示按键按下,所以整个第一行的意思就是按键KEY_VOLUMEDOWN被按下。
第二行,表示上报了EV_SYN同步类事件(type=0)中的SYN_REPORT事件(code=0),表示本轮数据已经完整、报告同步。
第三行,type等于1,表示按键类事件,code等于 114、value 等于0,所以表示按键KEY_VOLUMEDOWN被松开。
第四行,又上报了同步事件。
所以整个上面4行的打印信息就是开发板上的KEY0按键被按下以及松开这个过程,内核所上报的事件以及发送给应用层的数据value。
我们试试长按按键 KEY0,按住不放,如下所示:
可以看到上报按键事件时,对应的value等于 2,表示长按状态。
本小节编写一个应用程序,获取按键状态,判断按键当前是按下、松开或长按状态。从上面打印的信息可知,对于按键来说,它的事件上报流程如下所示:
# 以字母 A 键为例
KEY_A //上报 KEY_A 事件
SYN_REPORT //同步
如果是按下,则上报KEY_A事件时,value=1;如果是松开,则value=0;如果是长按,则value=2。
接下来编写按键应用程序,读取按键状态并将结果打印出来,代码如下所示:
#include
#include
#include
#include
#include
#include
#include
int main(int argc, char *argv[])
{
struct input_event in_ev = {0};
int fd = -1;
int value = -1;
/* 校验传参 */
if (2 != argc) {
fprintf(stderr, "usage: %s \n" , argv[0]);
exit(-1);
}
/* 打开文件 */
if (0 > (fd = open(argv[1], O_RDONLY))) {
perror("open error");
exit(-1);
}
for ( ; ; ) {
/* 循环读取数据 */
if (sizeof(struct input_event) !=
read(fd, &in_ev, sizeof(struct input_event))) {
perror("read error");
exit(-1);
}
if (EV_KEY == in_ev.type) { //按键事件
switch (in_ev.value) {
case 0:
printf("code<%d>: 松开\n", in_ev.code);
break;
case 1:
printf("code<%d>: 按下\n", in_ev.code);
break;
case 2:
printf("code<%d>: 长按\n", in_ev.code);
break;
}
}
}
}
在for循环中,调用read()读取输入设备上报的数据,当按键按下或松开(以及长按)动作发生时,read()会读取到输入设备上报的数据,首先判断此次上报的事件是否是按键类事件(EV_KEY),如果是按键类事件、接着根据value值来判断按键当前的状态是松开、按下还是长按。
将上述代码进行编译:
然后在开发板中测试
./read_key /dev/input/event2
运行程序之后,按下 KEY0 或松开 KEY0 以及长按情况下,终端会打印出相应的信息,如上图所示。code=114(KEY_VOLUMEDOWN 按键)。
除了测试开发板上的 KEY0 按键之外,我们还可以测试键盘上的按键,首先找到一个 USB 键盘连接到开发板的 USB HOST接口上,驱动加载成功之后,可以查看下该键盘设备对应的设备节点,使用命令cat /proc/bus/input/devices
,在打印信息中找到键盘设备的信息:
我使用的是一个Ducky的USB键盘"Ducky Akko Keyboard",对应的设备节点为/dev/input/event3,
运行测试程序并按下、松开键盘上的按键:
大家可以根据code值查询到对应的按键(通过input-event-codes.h头文件),譬如code=30对应的是键盘上的字母A键,code=48对应的是字母B键。
上面介绍了如何编写触摸屏应用程序,包括单点触摸和多点触摸,主要是对读取到的struct input_event
类型数据进行剖析,得到各个触摸点的坐标。tslib 库,这是 Linux 系统下,专门为触摸屏开发的应用层函数库。
tslib是专门为触摸屏设备所开发的 Linux 应用层函数库,并且是开源,也就意味着我们可以直接获取到tslib 的源代码。
tslib为触摸屏驱动和应用层之间的适配层,它把应用程序中读取触摸屏struct input_event类型数据(这是输入设备上报给应用层的原始数据)并进行解析的操作过程进行了封装,向使用者提供了封装好的 API 接口。tslib 从触摸屏中获得原始的坐标数据,并通过一系列的去噪、去抖、坐标变换等操作,来去除噪声并将原始的触摸屏坐标转换为相应的屏幕坐标。
tslib 有一个配置文件ts.conf,该配置文件中提供了一些配置参数、用户可以对其进行修改,具体的配置信息稍后介绍!
tslib可以作为Qt的触摸屏输入插件,为Qt提供触摸输入支持,如果在嵌入式 Linux 硬件平台下开发过
Qt 应用程序的读者应该知道;当然,并不是只有 tslib 才能作为 Qt 的插件、为其提供触摸输入支持,还有很多插件都可以,只不过大部分都会选择使用 tslib。
首先下载tslib源码包,进入到tslib的 git 仓库下载源码https://github.com/libts/tslib/releases,如下:
所以为了统一,我们也下载 1.16 版本的 tslib,往下翻找到 1.16 版本的下载链接:
将其解压到当前目录下:
tar -xzf tslib-1.16.tar.gz
解压之后会生成tslib-1.16目录,在/home/zhiguoxin/linux/tool
目录下创建 tslib 目录,等会编译tslib库的时候将安装目录指定到这里,如下所示:
进入到 tslib-1.16 目录,准备进行编译 tslib 源码:
接下来进行编译,整个源码的编译分为3个步骤:
source /opt/fsl-imx-x11/4.1.15-2.1.0/environment-setup-cortexa7hf-neon-poky-linux-gnueabi
执行下面这条命令对 tslib 源码工程进行配置:
./configure --host=arm-poky-linux-gnueabi --prefix=/home/zhiguoxin/linux/tool/tslib/
至于工程是如何配置的,大家可以执行./configure --help
查看它的配置选项以及含义,--host
选项用于指定交叉编译得到的库文件是运行在哪个平台,通常将--host
设置为交叉编译器名称的前缀,譬如 arm-poky-linux-gnueabi-gcc
前缀就是arm-poky-linux-gnueabi
;--prefix
选项则用于指定库文件的安装路径,我们将安装路径设置为之前创建的/home/zhiguoxin/linux/tool/tslib
目录。
接着编译工程,直接执行 make:
make
最后执行 make install 安装:
make install
进入到tslib安装目录下:
cd /home/zhiguoxin/linux/tool/tslib
bin目录下有一些 tslib 提供的小工具,可以用于测试触摸屏,如下所示:
etc目录下有一个配置文件 ts.conf,前面给大家提到过,
打开ts.conf
文件看看它有哪些配置选项:
这里的配置不需要去改动了,直接使用默认的配置就行了。
include 目录下只有一个头文件tslib.h,该头文件中包含了一些结构体数据结构以及 API 接口的申明,使用tslib提供的API就需要包含该头文件。
lib目录下包含了编译tslib源码所得到的库文件,默认这些都是动态库文件,也可以通过配置tslib工程使其生成静态库文件;ts目录下存放的是一些插件库。
可以忽略!
移植的最后一步就是把tslib安装目录下的库文件、etc下的配置文件以及编译得到的测试工具拷贝到开发板Linux系统目录下,由于开发板出厂系统中已经移植了tslib库,所以我们这里就不用拷贝了。但如果大家是自己做的根文件系统,并没有移植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
执行下面命令后,点击屏幕可以试一试效果:
ts_print
ts_print_mt
使用 tslib 编程其实非常简单,基本步骤如下所示:
#include "tslib.h"
struct tsdev *ts_open(const char *dev_name, int nonblock);
除了使用 ts_open()打开设备外,还可以使用 ts_setup()函数。ts_setup()相比 ts_open(),除了打开触摸屏设备外,还对触摸屏设备进行了配置。所以如果你使用了ts_setup()函数,就不需要在使用ts_config()函数进行配置了。
#include "tslib.h"
struct tsdev *ts_setup(const char *dev_name, int nonblock)
调用ts_config()函数进行配置,其函数原型如下所示:
#include "tslib.h"
int ts_config(struct tsdev *ts)
所谓配置其实指的就是解析 ts.conf 文件中的配置信息,加载相应的插件。
读取触摸屏数据使用 ts_read()或 ts_read_mt()函数,区别在于 ts_read 用于读取单点触摸数据,而
ts_read_mt 则用于读取多点触摸数据,其函数原型如下所示:
#include "tslib.h"
int ts_read(struct tsdev *ts, struct ts_sample *samp, int nr)
struct ts_sample {
int x; //X 坐标
int y; //Y 坐标
unsigned int pressure; //按压力大小
struct timeval tv; //时间
};
读取触摸屏数据还有一个函数就是ts_read_mt()函数。
#include "tslib.h"
int ts_read_mt(struct tsdev *ts, struct ts_sample_mt **samp, int max_slots, int nr)
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; //触摸点 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; //时间
/* BTN_TOUCH state */
short pen_down; //BTN_TOUCH 的状态
/* 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; //此次样本是否有效标志 触摸点数据是否发生更新
};
#include
#include
#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) //若上一次的按压力>0
printf("移动(%d, %d)\n", samp.x, samp.y);
else
printf("按下(%d, %d)\n", samp.x, samp.y);
}
else
printf("松开\n");//打印坐标
pressure = samp.pressure;
}
ts_close(ts);
exit(EXIT_SUCCESS);
}
代码非常简单,就不再解释了,直接打开、配置设备,接着读取数据即可!通过判断按压力大小确定触摸的状态,如果按压力等于 0 则表示手指已经松开;按压力大于 0,则需根据上一次的按压力是否大于 0 来判断。读取数据出错时,ts_read()返回一个负数。
接下来编译应用程序,编译代码时,需要通过交叉编译器来指定头文件、库文件的路径以及动态链接库文件名:
${CC} -I /home/zhiguoxin/linux/tool/tslib/include -L /home/zhiguoxin/linux/tool/tslib/lib -lts -o ts_read ts_read.c
-I
选项指定头文件的路径,也就是指定tslib安装目录下的include目录,如果不指定头文件路径,编译时将会找不到 tslib.h 头文件;-L
选项用于指定库文件的路径,也就是指定tslib安装目录下的lib目录;我们将tslib编译成了动态库文件,以库文件的形式提供,编译时需要链接到这些库文件;而-l
选项则用于指定链接库(也可写成-l ts
,也就是libts.so 库文件,Linux 中,动态库文件的命名方式为 lib+名字+.so)。
如果不想指定目录怎么办?我们要把tslib的头文件和库文件,也就是include和lib文件夹中的文件拷贝到编译器下面include和lib文件夹中
确定工具链中头文件、库文件目录,对于 IMX6ULL ,命令如 下
echo 'main(){}'| arm-linux-gnueabihf-gcc -E -v -
找到了编译器arm-linux-gnueabihf的include和lib文件夹
/usr/local/arm/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf/arm-linux-gnueabihf/libc/usr/include
/usr/local/arm/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf/arm-linux-gnueabihf/libc/usr/lib
进入tslib的安装目录,我的ubuntu的tslib安装在/home/zhiguoxin/linux/tool/tslib中
cd /home/zhiguoxin/linux/tool/tslib
然后将include文件夹下的所有文件和lib文件夹下的所有文件都拷贝到编译器arm-linux-gnueabihf的include和lib文件夹下
cp include/* /usr/local/arm/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf/arm-linux-gnueabihf/libc/usr/include
cp -d lib/*so* /usr/local/arm/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf/arm-linux-gnueabihf/libc/usr/lib
开发板启动后通过nfs挂载Ubuntu目录的方式,将相应的文件拷贝到开发板上。简单来说,就是通过NFS在开发板上通过网络直接访问ubuntu虚拟机上的文件,并且就相当于自己本地的文件一样。
开发板想访问/home/zhiguoxin/myproject/alientek_app_development_source
这个目录中的文件,就要把/home/zhiguoxin/myproject/alientek_app_development_source
挂载到开发板的mnt
目录,这样就可以通过nfs来访问/home/zhiguoxin/myproject/alientek_app_development_source
了。
因为我的代码都放在/home/zhiguoxin/myproject/alientek_app_development_source
这个目录下,所以我们将这个目录作为NFS共享文件夹。设置方法参考移植SQLite3、OpenCV到RV1126开发板上开发人脸识别项目第一章。
Ubuntu IP为192.168.10.100,然后一般都是挂载在开发板的mnt目录下,这个目录是专门用来给我们作为临时挂载的目录。
然后使用MobaXterm软件通过SSH访问开发板。
ubuntu ip:192.168.10.100
windows ip:192.168.10.200
开发板ip:192.168.10.50
在开发板上执行以下命令:
mount -t nfs -o nolock,vers=3 192.168.10.100:/home/zhiguoxin/myproject/alientek_app_development_source /mnt
就将开饭的mnt
目录挂载在ubuntu的/home/zhiguoxin/myproject/alientek_app_development_source
目录下了。这样我们就可以在Ubuntu下修改文件,然后可以直接在开发板上执行可执行文件了。当然我这里的/home/zhiguoxin/myproject/
和windows
之间是一个共享目录,我也可以直接在windows
上面修改文件,然后ubuntu和开发板直接进行文件同步了。
然后进入18_tslib目录下执行命令即可
./ts_read
#include
#include
#include
#include
#include
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}; //用于保存每一个触摸点上一次的按压力,初始为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])//如果上一次的按压力>0
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
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);
}
操作于与上面差不对,就不多说了。