android实现触摸屏校准
时间:2010-04-13 来源:nbupt
-
触摸校准算法
-
触摸屏校准通用方法。
-
(XL, YL是显示屏坐标,XT, YT是触摸屏坐标,)
XL = XT*A+YT*B+C
YL = YT*D+YT*E+F
-
由于具体计算是希望是整数运算,所以实际中保存的ABCDEF为整数,而增加一个参数Div
XL = (XT*A+YT*B+C) / Div
YL = (YT*D+YT*E+F) / Div
-
TSLIB把以上的7个参数 ABCDEF Div 保存在 pointercal 文件中。
不校准的数据: A=1, B=0, C=0, D=0, E=1, F=0, Div=1
A |
B |
C |
D |
E |
F |
Div |
-411 |
37818 |
-3636780 |
-51325 |
39 |
47065584 |
65536 |
-
Android 事件处理机制
-
android 事件的传入是从EventHub开始的,EventHub是 事件的抽象结构,维护着系统设备的运行情况(设备文件放在/dev/input里),设备类型包括Keyboard、TouchScreen、TraceBall。它在系统启动的时候会通过 open_device方法将系统提供的输入设备都增加到这个抽象结构中,并维护一个所有输入设备的文件描述符,如果输入设备是键盘的话还会读取 /system/usr/keylayout/目录下对应键盘设备的映射文件(修 改./development/emulator/keymaps/qwerty.kl来改变键值的映射关系),另外getEvent方法是对EventHub中的设备文件描述符使用poll操作等侍驱动层事件的发生,如果发生的事件是键盘事件,则调用Map函数按照映射文件转换成相应的键值并将扫描码和键码返回给KeyInputQueue.
-
frameworks/base/services/jni/com_android_server_KeyInputQueue.cpp
-
根据事件的类型以及事件值进行判断处理,从而确定这个事件对应的设备状态是否发生了改变并相应的改变对这个设备的描述结构InputDevice。
-
Windowmanager会创建一个线程(InputDispatcherThread),在这个线程里从事件队列中读取发生的事件 (QueuedEvent ev = mQueue.getEvent()),并根据读取到事件类型的不同分成三类(KEYBOARD、TOUCHSCREEN、TRACKBALL),分别进 行处理,例如键盘事件会调用dispatchKey((KeyEvent)ev.event, 0, 0)以将事件通过Binder发送给具有焦点的窗口应用程序,然后调用 mQueue.recycleEvent(ev)继续等侍键盘事件的发生;如果是触摸屏事件则调用dispatchPointer(ev, (MotionEvent)ev.event, 0, 0),这里会根据事件的种类(UP、DOWN、MOVE、OUT_SIDE等)进行判断并处理,比如Cancel或将事件发送到具有权限的指定的窗口中 去;
-
移植方案
-
Android本身并不带触摸屏校准。Android获取到的数据就是驱动上报的原始数据。
-
方案一 : 移植TSLIB,通过TSLIB产生 pointercal 校准参数文件。
-
方案二 : 从Android框架层获取OnTouch事件产生 pointercal 校准参数文件
-
方案一: 数据的校准在驱动中完成。 即把 pointercal 的参数数据通过某种方式(sysfs)传递给驱动程序进行校准。
-
方案二: 驱动上报原始点,原始点在框架层拦截后进行校验处理。
-
-
TSLIB移植过程
-
修改源码以适应android的文件结构。
-
设定Android.mk 编译选项,生成库即应用。
-
etc/ts.conf module_raw input
-
src/ts_config.c #define TS_CONF "/system/etc/ts.conf"
-
src/ts_load_module.c
char *plugin_directory="/system/lib/ts/plugins/"; -
tests/fbutils.c
char *defaultfbdevice = "/dev/graphics/fb0"; -
COPY ts.conf 到 /system/etc/ts.conf
-
init.rc. mkdir /data/etc/pointercal
-
通过 ts_calibrate 产生pointercal 数据文件。
-
-
-
框架内获取参数文件
-
制作APK 应用,仿效ts_calibrate采点并计算出各参数,产生 pointercal
-
-
框架内实现触摸屏校准
-
在 InputDevive.java 中 拦截触摸屏原始数据
-
进行pointercal参数校验后再分发
-
-
驱动内实现触摸屏校准
-
在init.rc 中添加event,在触摸屏加载后把 pointercal参数输送给驱动。
-
-
结果-效果
实现细节:
- 扩展init - proper_serivce 系统支持的属性权限,对自定义的特殊系统属性进行权限开放。
- 使用自定义系统属性在 init.rc 中 on property 事件中处理 pointercal的读写权限。
- 使用自定义系统属性 触摸屏校准程序.apk 和 InputDevice.java 中的输入事件的同步。
(在触摸屏校准期间 inputDevice 在输入事件中不能采用算法。 校准程序完成有inputDevice重新启用校准算法) - 模拟器中至今无法进入 device.absX/Y != null 的代码, 需要了解以下 inputDevice 被调用的步骤。
触摸屏的时间流程:
驱动层:
/*
/*
* 对设备进行初始化设置
*/
set_bit(EV_ABS, wm->input_dev->evbit);
/*
* 事件发生时,提供原始点 */
input_report_abs(wm->input_dev, ABS_X, data.x & 0xfff);
/* * 提供给驱动外查询input_dev 的接口
* struct input_absinfo info; * ioctl(fd, EVIOCGABS(axis), &info)
* src file: evDev.c */
if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCGABS(0))) { |
Android 底层驱动
EventHub.cpp
static const char *device_path = "/dev/input"; fd = open(deviceName, O_RDWR);
/*
* 对外接口,getEvent,
* inotify 监控device_path目录, 使用poll机制轮询 inotify 和各个输入设备的可用状态。 解析事件或输入信息,放入各个传出参数中。
*/
bool EventHub::getEvent(int32_t* outDeviceId, int32_t* outType, |
JNI 部分: com_android_server_KeyInputQueue.cpp. 提供接口
static JNINativeMethod gInputMethods[] = { |
java service 部分: KeyInputQueue.java. 循环查询输入设备信息或目录状态并处理
Thread mThread = new Thread("InputDeviceReader") { |
//检测到新设备后
InputDevice.AbsoluteInfo absX; |
我们对触摸屏的数据修订是在 InputDevice.java 中基于 absX, absY, absPressure != null 的状态下的,当绝对原始点数据从驱动报上来之后,传递到InputDevice.java 经过我们的校准后再dispatch出去到windowManager -> activity 。 这样就是起到了校准效果。
需要注意的补助说明
EventHub 中有使用IOCTL 对触摸屏的EVIOCGABS(axis)进行了采样,取出内容struct input_absinfo info;
struct input_absinfo { |
scaledX = ((scaledX-device.absX.minValue) |