嵌入式Linux系统的电子书阅读器项目4——Input Event System

来源:互联网 发布:中国古代名菜 知乎 编辑:程序博客网 时间:2024/06/05 19:39

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()的。它会从所有被enbleinput dev中探测是否有输入事件产生,

然后返回结果,不同实现方法的差异,在下一节再叙述。

enable_input_dev_set() disable_input_dev_set():

该对函数根据输入参数列表中input devname使能/关闭输入加载到系统中的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 corefd_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_evval值设为对应的

       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()主要是调用tslibAPI进行相关的初始化,可以参考

API使用教程。

get_tscreen_ev():

         该函数获取touchscreen的原始输入数据(raw input),并将其转换为input_ev对应的值,然后提交。

input_ev事件的与触屏输入的关系如图5所示

                                                                                            图5 input_ev与触屏动作的对应关系

通过图5可知,该函数将向左滑动10%触屏x轴横坐标定义为向上翻页,向右滑动10%定义为向下翻页。该函数即处理相关逻辑,并判断手是否一直在触屏之上,有无中途离开。is_out_of_time()函数则是用来延时消除抖动等误操作。


1 0