嵌入式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()的。它会从所有被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()函数则是用来延时消除抖动等误操作。


以上是关于嵌入式Linux系统的电子书阅读器项目4——Input Event System的主要内容,如果未能解决你的问题,请参考以下文章

嵌入式Linux系统的电子书阅读器项目2——Display System

嵌入式Linux系统的电子书阅读器项目3——Encode & Font System

Java精品项目项目源码第107期在线电子书小说阅读系统

嵌入式Linux文件系统保护

完整的嵌入式学习路线是怎样的?

嵌入式linux怎么学