1. 输入子系统
连接操作系统的输入设备,可不止一种,也许是一个标准PS/2键盘,也许是一个USB鼠标,或者是一块触摸屏,甚至是一个游戏机摇杆,Linux在处理这些纷繁各异的输入设备的时候,采用的办法还是找中间层来屏蔽各种细节,请看下图:
在Linux的内核中,对输入设备的使用,实际上运用了3大块来管理,他们分别是所谓的输入设备驱动层、输入子系统核心层,以及事件触发层。他们各自的工作分别是:
-
输入设备驱动层:
每一种设备都有其特定的驱动程序,他们被妥当地装载到操作系统的设备模型框架内,封装硬件所提供的功能,向上提供规定的接口。 -
核心层:
此处将收集由设备驱动层发来的数据,整合之后触发某一事件。 -
事件触发层:
这一层是我们需要关注的,我们可以通过在用户空间读取相应设备的节点文件来获知某设备的某一个动作。
以触摸屏为例,当手指在屏幕上滑动的时候,数据流大致是这样的:驱动层中的触摸屏驱动会源源不断地产生触摸屏相关数据,并向上递送给内核输入子系统,输入子系统进一步将这些信息规整为统一的结构体,并借助事件触发层发往对应的设备节点,至此,应用程序即可从这些设备节点读取相关信息。
值得注意的是,底层驱动产生的设备数据与上层应用读取设备数据是两个完全异步的过程,彼此之间是没有耦合和约束的,例如:当底层驱动产生的触摸屏坐标信息比应用层读取的速度要快时,应用程序将会丢失一部分坐标信息。
2. 输入信息结构体
在最靠近应用程序的事件触发层上,内核所获知的各类输入事件,比如键盘被按了一下,触摸屏被滑了一下等,都将被统一封装在一个叫做 input_even
的输入信息结构体当中,这个结构体定义如下:
vincent@ubuntu:/usr/include/linux/$ cat input.h -n
1 #ifndef _INPUT_H
2 #define _INPUT_H
3
...
...
20
21 struct input_event {
22 struct timeval time;
23 __u16 type;
24 __u16 code;
25 __s32 value;
26 };
27
...
1. time 输入事件发生的时间戳,精确到微秒。时间结构体定义如下:
struct timeval
{
__time_t tv_sec; // 秒
long int tv_usec; // 微秒(1微秒 = 10-3毫秒 = 10-6秒)
};
2. type 输入事件的类型。比如:
事件类型(type) | 说明 |
---|---|
EV_SYN | 事件间的分割标志,有些事件可能会在时间和空间上产生延续,比如持续按住一个按键 为了更好地管理这些持续的事件,EV_SYN用以将他们分割成一个个的小的数据包。 |
EV_KEY | 用以描述键盘,按键或者类似键盘的设备的状态变化。 |
EV_REL | 相对位移,比如鼠标的移动,滚轮的转动等。 |
EV_ABS | 绝对位移,比如触摸屏上的坐标值。 |
EV_MSC | 不能匹配现有的类型,这相当于当前暂不识别的事件 比如在Linux系统中按下键盘中针对Windows系统的“一键杀毒”按键,将会产生该事件。 |
EV_LED | 用于控制设备上的LED灯的开关,比如按下键盘的大写锁定键 会同时产生 ”EV_KEY” 和 ”EV_LED” 两个事件。 |
… … | … … |
3. code
这个 事件代码 用于对事件的类型作进一步的描述。比如:当发生EV_KEY事件时,则可能是键盘被按下了,那么究竟是哪个按键被按下了呢?此时查看code就知道了。当发生EV_REL事件时,也许是鼠标动了,也许是滚轮动了。这时可以用code的值来加以区分。
事件类型(type) | 事件代码(code) | 说明 |
---|---|---|
EV_KEY | BTN_TOUCH | 触摸屏发生了按压、松开事件 |
EV_KEY | KEY_LEFT | 键盘的左箭头发生了按压、松开事件 |
EV_KEY | KEY_RIGHT | 键盘的右箭头发生了按压、松开事件 |
EV_ABS | ABS_X | 触摸屏发生了x轴坐标事件 |
EV_ABS | ABS_Y | 触摸屏发生了y轴坐标事件 |
EV_REL | ABS_Y | 触摸屏发生了y轴坐标事件 |
EV_ABS | ABS_Y | 触摸屏发生了y轴坐标事件 |
… … | … … | … … |
4. value
当code都不足以区分事件的性质的时候,可以用value来确认。比如由EV_REL和REL_WHEEL确认发生了鼠标滚轮的动作,但是究竟是向上滚还是向下滚呢?再比如由由EV_KEY和KEY_F确认了发生键盘上F键的动作,但究竟是按下呢还是弹起呢?这时都可以用value值来进一步判断。
事件类型(type) | 事件代码(code) | 发生值(value) | 说明 |
---|---|---|---|
EV_KEY | BTN_TOUCH | 大于0 | 手指按压了触摸屏 |
EV_KEY | BTN_TOUCH | 0 | 手指松开了触摸屏 |
EV_KEY | KEY_LEFT | 大于0 | 左箭头被按下 |
EV_KEY | KEY_LEFT | 0 | 左箭头被松开 |
EV_ABS | ABS_X | 213 | 触摸屏产生了一个233的 x 轴坐标 |
EV_ABS | ABS_Y | 448 | 触摸屏产生了一个448的 y 轴坐标 |
… … | … … | … … | … … |
3. 触作摸屏操作
对于触摸屏而言,该设备会产生三种数据:
- X轴坐标值
- Y轴坐标值
- P压力值
理论上来说,从手指放上屏幕开始,到滑动一段距离,离开屏幕结束,会产生如下所示的一系列数据:
(X Y P1) SYN (X Y) SYN (X Y) SYN (X Y) ... ... (X Y) SYN (X Y P2)
有如下地方需要注意:文章来源:https://www.toymoban.com/news/detail-451943.html
- 在刚开始的第一个坐标值和最后一个坐标值中,都连带会读到压力值P
- P1是手指刚落下时,产生压力值大于0的数据
- P2是手指离开时,产生压力值等于0的数据
- 应用层并不能保证能严格交替读取
(X,Y)
坐标值,它们会由于异步等原因出现断续,比如(X X X X Y)
- 若手指滑动的方向刚好垂直与坐标轴,会导致其中一个维度的坐标值不变,那么也可能会导致只出现一个维度坐标值的情形,例如:
... (X) SYN (X) SYN (X Y) SYN (Y) ... ... (X Y) SYN ...
这种情况下需要借助系统自动产生的 SYN 事件来整理出成对的坐标值。文章来源地址https://www.toymoban.com/news/detail-451943.html
4、触摸屏相关函数代码
//触摸屏划屏函数
#include <stdio.h> //usr/include/stdio.h
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
/*输入子系统头文件*/
#include <linux/input.h> //usr/include/linux/input.h
int main(int argc,char **argv)
{
/*打开触摸屏*/
int fd;
fd = open("/dev/input/event0",O_RDWR);
if(fd < 0)
{
perror("open ts fail");
return -1;
}
/*操作触摸屏*/
//定义一个存储触摸屏信息的结构体
struct input_event ts;
int x1=0,y1=0; //按下去的坐标值
int x2=0,y2=0; //松手后的坐标值
while(1)//循环读取数据
{
/*抓取按下去的坐标值*/
while(1)
{
//在这个地方read这条语句是阻塞(根本原因此处的fd带有阻塞属性)
read(fd,&ts,sizeof(struct input_event));
if(ts.type == EV_ABS && ts.code==ABS_X) //x轴的坐标值
{
//printf("x=%d ",ts.value); //蓝色触摸屏的坐标值
x1 = ts.value;
//printf("x=%d ",(int)(ts.value*0.78)); //蓝色触摸屏的坐标值 1024 方法一
//printf("x=%d ",ts.value*800/1024); //方法二
}
if(ts.type == EV_ABS && ts.code==ABS_Y) //y轴的坐标值
{
//printf("y=%d\n",ts.value);//蓝色触摸屏的坐标值
y1 = ts.value;
//printf("x=%d ",(int)(ts.value*0.8));//黑色触摸屏的坐标值 600 方法一
//printf("y=%d\n",ts.value*480/600); //方法二
}
//当按下去的那一刻,跳出循环;只抓取按下去的坐标值
if(ts.type == EV_KEY && ts.code==BTN_TOUCH && ts.value == 1)//
{
printf("按下去:x1=%d y1=%d\n",x1,y1);
break;
}
}
/*抓取松手后的坐标值*/
while(1)
{
//在这个地方read这条语句是阻塞(根本原因此处的fd带有阻塞属性)
read(fd,&ts,sizeof(struct input_event));
if(ts.type == EV_ABS && ts.code==ABS_X) //x轴的坐标值
{
//printf("x=%d ",ts.value); //蓝色触摸屏的坐标值
x2 = ts.value;
//printf("x=%d ",(int)(ts.value*0.78)); //蓝色触摸屏的坐标值 1024 方法一
//printf("x=%d ",ts.value*800/1024); //方法二
}
if(ts.type == EV_ABS && ts.code==ABS_Y) //y轴的坐标值
{
//printf("y=%d\n",ts.value);//蓝色触摸屏的坐标值
y2 = ts.value;
//printf("x=%d ",(int)(ts.value*0.8));//黑色触摸屏的坐标值 600 方法一
//printf("y=%d\n",ts.value*480/600); //方法二
}
//当按下去的那一刻,跳出循环,只抓取松手后的坐标值
if(ts.type == EV_KEY && ts.code==BTN_TOUCH && ts.value == 0)//
{
printf("松手:x2=%d y2=%d\n",x2,y2);
break;
}
}
if(x2-x1 > 50) //50差值决定你滑动的灵敏度
printf("right\n");
if(x2-x1 < -50)
printf("left\n");
if(y2-y1 > 50)
printf("down\n");
if(y2-y1 < -50)
printf("up\n");
}
/*关闭触摸屏*/
close(fd);
return 0;
}
//触摸屏单击函数
#include <stdio.h> //usr/include/stdio.h
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
/*输入子系统头文件*/
#include <linux/input.h> //usr/include/linux/input.h
int main(int argc,char **argv)
{
/*打开触摸屏*/
int fd;
fd = open("/dev/input/event0",O_RDWR);
if(fd < 0)
{
perror("open ts fail");
return -1;
}
/*操作触摸屏*/
//定义一个存储触摸屏信息的结构体
struct input_event ts;
//读取触摸屏的设备文件
while(1)
{
//在这个地方read这条语句是阻塞(根本原因此处的fd带有阻塞属性)
read(fd,&ts,sizeof(struct input_event));
if(ts.type == EV_ABS && ts.code==ABS_X) //x轴的坐标值
{
//printf("x=%d ",ts.value); //蓝色触摸屏的坐标值
//printf("x=%d ",(int)(ts.value*0.78)); //蓝色触摸屏的坐标值 1024 方法一
printf("x=%d ",ts.value*800/1024); //方法二
}
if(ts.type == EV_ABS && ts.code==ABS_Y) //y轴的坐标值
{
//printf("y=%d\n",ts.value);//蓝色触摸屏的坐标值
//printf("x=%d ",(int)(ts.value*0.8));//黑色触摸屏的坐标值 600 方法一
printf("y=%d\n",ts.value*480/600); //方法二
}
}
/*关闭触摸屏*/
close(fd);
return 0;
}
到了这里,关于基于GEC6818的触摸屏的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!