嵌入式Linux系统的电子书阅读器项目4——Input Event System
Posted zhou_chenz
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了嵌入式Linux系统的电子书阅读器项目4——Input Event System相关的知识,希望对你有一定的参考价值。
1.输入事件系统(Input EventSystem)架构与API
1). 输入事件系统框架设计
输入事件系统的框架如图1所示。
图1输入事件系统框架
类似其它几个子系统,输入事件子系统也是采用一个核心层+可装载组件的模式,
并且将底层实际输入设备(如 touchscreen)
所产生的原始数据(raw input data)封装层格式化的输入事件(input event)提供给Book Engine访问。
这样就隐藏了事件输入设备的复杂性与多样性。作为一个学习型项目,在输入事件系统的核心函数采用
可配置的多种实现方法,可让爱好者学习比较各种方法的优劣。
2).输入事件系统框架API概述
input eventsystem 的API声明位于include/myinpu.h头文件中。
输入事件 input_ev:
输入事件的封装结构声明如下
typedef structinput_ev
struct timeval time;
int type; /* stdin, touchsceen */
int val; /* */
input_ev;
time——事件发生的系统时间
type——判断是哪个具体设备产生的输入事件(目前只有 stdin/touchscreen),相关的宏值如下:
#defineINPUT_TYPE_STDIN 0
#defineINPUT_TYPE_TSC 1
val——经过抽象化的输入事件值,目前支持以下几个宏值:
#defineINPUT_VAL_UP 0
#defineINPUT_VAL_DOWN 1
#defineINPUT_VAL_EXIT 2
#defineINPUT_VAL_UNKNOWN -1
该值是输入设备(inputdev)通过获取原始输入值(如键盘“U”、"N"、touchscreen的向右滑动10%x坐标)
经过设备模块组件内部的get_input_ev()对原始值进行逻辑处理和转换,提交给input event system core层的。
INPUT_VAL_UP——表示向上翻页事件,INPUT_VAL_DOWN——表示向下翻页事件,INPUT_VAL_EXIT——
表示退出电子书事件,INPUT_VAL_UNKNOWN——表示获取了未知值,需要异常处理。
这样就把底层输入设备不同的输入特性和采用值封装起来,提供了统一的抽象input event接口。
核心描述符 input_dev:
核心描述符声明如下,
typedef structinput_dev
char *name;
union input_method mthd;
int (*init_dev)(void);
int (*exit_dev)(void);
int (*get_input_ev)(struct input_ev*pev);
struct input_dev *next;
input_dev;
mthd是一个联合体,根据不同是输入策略(如select机制,多线程等)实际实现方法,根据配置,由宏开关隔开,
根据实际需要配置成select文件描述符,或者多线程下的线程id。
next:
系统所加载的input_dev列表,注意,加载的输入设备不一定被激活使能。
加载之后,只有通过enable_input_dev_set()函数使能的设备,才能产生输入响应。
init_dev() 与 exit_dev():
实际输入设备的初始化与退出函数,需要根据设备的特性在不同输入设备组件函数中实现,
在enable_input_dev_set()与 disable_input_dev_set()函数中被调用。
get_input_ev():
实际输入设备组件的输入事件获取函数,将原始输入值经过一点逻辑处理,转换成input_ev
然后提交给input eventsystem core层。
get_rt_input_ev():
该API被用户调用,获取input_ev()的。它会从所有被enble的input dev中探测是否有输入事件产生,
然后返回结果,不同实现方法的差异,在下一节再叙述。
enable_input_dev_set()与 disable_input_dev_set():
该对函数根据输入参数列表中input dev的name使能/关闭输入加载到系统中的input_dev,只有使能的
input dev才会有输入响应。
3).输入事件系统函数多种实现方法比较
input eventsystem 的函数实现位于myinput/myinput.c文件中。
其它函数和之前的系统类似,都是链表相关的加载,调试操作,最重要的函数是get_rt_input_ev()
get_rt_input_ev()的实现方法:
轮询(query)方法:
轮询方法的get_rt_input_ev()函数实现方法如下。它是轮流调用各个输入设备的get_input_ev()函数,
有输入事件发生,返回0表示成功,否则query完所有设备之后,没有任何输入事件,则返回-1。
int ret;
struct input_dev *tmp_dev = idev_h;
while(tmp_dev)
//printf("use inputdev: %s\\n",tmp_dev->name);
if(tmp_dev->get_input_ev(pev)== 0)
return 0;
tmp_dev =tmp_dev->next;
return -1;
该方法,适合单个enable输入设备进行初次项目验证与调试。主要缺点有:
1.CPU占用率高(轮询的老毛病)
2.当不同input_dev的get_input_ev()函数实现机制不同时(如stdin采用非阻塞机制,而tscreen设备用阻塞机制),
则容易导致非阻塞机制的设备处于饥饿状态,电子书应用进程会阻塞在tscreen设备直到获取输入值,
这样stdin设备的输入将得不到响应。这样,上层接口就得考虑底层组件的实现机制。
所以这并不是正式release项目的一个好机制,只是做初次调试而已。
Select 机制:
select机制的get_rt_input_ev()函数实现方法如下。它在每个input_dev组件的init_dev()函数的实现中,将设备描述符fd
加入了input event system core的fd_set中,来通过select机制查询input_ev是否发生。
int ret;
struct input_dev *tmp_dev = idev_h;
fd_set tmp_rd_set = idev_fd_set;
ret = select(max_fd_val,&tmp_rd_set, NULL, NULL, NULL);
if(ret > 0)
while(tmp_dev)
if(FD_ISSET(tmp_dev->mthd.fd,&tmp_rd_set))
if(tmp_dev->get_input_ev(pev)== 0)
return0;
tmp_dev =tmp_dev->next;
return -1;
采用select机制,就能解
决轮询(query)方法中所叙述的2个问题,不管底层input_dev的
get_input_ev()
函数如何实现,
只要select函数中的tmp_rd_set集中的描述符有输入事件发生,则select函数将返回,而不会产生一个设备处于饥饿状态。
但是该方法也有缺点——使得电子书应用进程在select函数处阻塞,影响系统整体效率。
多线程机制:
多线程机制的get_rt_input_ev()函数实现方法如下,它为每个输入设备在每个input_dev组件的init_dev()时,开启一个线程,
然后在用户调用get_rt_input_ev()函数时,等待产生input_ev的线程提交条件信号,然后去获取该input_dev所产生的input_ev的值。
pthread_mutex_lock(&input_mutex);
pthread_cond_wait(&input_cond,&input_mutex);
memcpy(pev, &sh_iev,sizeof(struct input_ev));
pthread_mutex_unlock(&input_mutex);
return 0;
采用多线程的方法,CPU占用较低,处理相对高效,也不会出现某设备由于实现机制而产生的饥饿问题。
2.输入事件系统(Input EventSystem)模块组件
1). stdin dev
stdin_dev 组件的实现的源代码在myinput/stdin_dev.c文件中。类似于其它子系统,stdin_dev主要也是填充
实现核心input_dev描述符,实现填充代码如下:
static structinput_dev stdin_dev =
.name = "stdin_dev",
.init_dev = init_stdin_dev,
.exit_dev = exit_stdin_dev,
.get_input_ev =get_stdin_dev_ev,
;
init_stdin_dev():
由于stdin_dev设备主要是利用tty终端的标准输入stdin关联键盘的"U"/"N"/"Q"三键控制翻页与结束,
该函数作为stdin_dev的初始化函数,首选要把终端的标准输入stdin设置为non-canon模式,缓冲设为最小。
这个函数所使用的Linux系统终端相关的函数和结构的用法细节,可以参考《Unix环境高级编程一书》。
exit_stdin_dev():
该函数也是调用Linux系统终端相关API,将终端tty的状态恢复正常模式。
get_stdin_dev_ev():
该函数获取终端stdin的原始输入,并把如果遇到U/N/Q三个键输入,则input_ev的val值设为对应的
INPUT_VAL_UP/INPUT_VAL_DOWN/INPUT_VAL_EXIT,然后将输入事件提交给input system core。
2). touchscreen
Smart210 开发板触屏特性与使用方法:
Smart210 开发板触屏是7寸多点触控电容触屏,而不是@韦东山老师视频里用的电阻屏。并且,由于Smart210
使用了goodix公司非开源的电容触屏驱动,用过一个叫onewire的驱动,封装了触摸屏的event输入事件,因而韦东山
老师的源代码,需要一定修改才能在在Smart210开发板上使用。
Smart210 开发板的原版文件系统(Linux 3.0.8内核),其实在usr/lib 和 usr/lib/ts已经安装好了tslib的库,交叉工具链中也有tslib相关头文件,
它的库与原版的tslib的差异截图如下:
图2 Smart210 tslib环境变量的差异
图3 Smart210 tslib ts.config 差异
图4 Smart210 tslib 动态链接库 lib/ts 下的文件差异
图2所示为Smart210 开发板tslib相关环境的配置,注意红线处,Smart210 所用的 TSLIB_TSDEVICE 不是 通常的/dev/input/event* ,
而应该是goodix芯片电容触屏1wire模式的的专用非开源驱动设备。
图3 是ts.config 的差异,其中黄线处,module_raw 所用的库,和韦东山视频里配置的不同,Smart210 开发板有专门为它的触摸屏
设备在应用层封装了一个底层驱动使用的库,该库所在路径如图4黄线所示。该库是tslib原版代码编译出来所没有的。
Smart210 开发板只有使用了该配置,配对了相关的device和module_raw,应用层程序才能正常获取数据工作,
以上就是Smart210 电容触屏和韦东山视频里JZ2440 电阻屏配置上有差异的部分。
touchsreen模块组件的实现
touchsreen模块组件在 myinput/tscreen.c文件中实现。该组件使用了开源的tslib触摸屏库,相关源代码可以在 tslib开源库代码下载 下载到。
tslib的需要先安装配置,相关的教程可以参考这篇文章tslib编译安装方法 。
touchsreen组件也需要填充input_dev结构体,其中 init_tscreen_dev()主要是调用tslib的API进行相关的初始化,可以参考
API使用教程。
get_tscreen_ev():
该函数获取touchscreen的原始输入数据(raw input),并将其转换为input_ev对应的值,然后提交。
input_ev事件的与触屏输入的关系如图5所示
图5 input_ev与触屏动作的对应关系
通过图5可知,该函数将向左滑动10%触屏x轴横坐标定义为向上翻页,向右滑动10%定义为向下翻页。该函数即处理相关逻辑,并判断手是否一直在触屏之上,有无中途离开。is_out_of_time()函数则是用来延时消除抖动等误操作。
以上是关于嵌入式Linux系统的电子书阅读器项目4——Input Event System的主要内容,如果未能解决你的问题,请参考以下文章
嵌入式Linux系统的电子书阅读器项目2——Display System