(1)原理:本文拋開技術層次上來說說LINUX觸摸屏設備驅動原理。觸摸屏,就是用一塊AD轉換模塊來將屏幕上的觸摸信號轉成數字信號。觸摸屏常用的是四線電阻,當觸摸時候,功能模塊會將模擬信號轉換成數字信號,就是通常說的AD轉換。在LINUX中,通常是通過中斷來讀取這些數字的。觸摸屏幕的時候,中斷發生,LINUX通過串口或者I2C,SPI或者內部數據通道等去讀取轉換后的數字,然后把數值傳給INPUT層。 為什么要校驗?原因A,觸摸屏與LCD顯示屏是兩個不同的物理器件。LCD處理的像素,例如我們通常所說的分辨率是600x800,實際就是指每行的寬度是600個像素,高度是800個像素,而觸摸屏處理的數據是點的物理坐標,該坐標是通過觸摸屏控制器采集到的。兩者之間需要一定的轉換。B,其次在安裝觸摸屏時,不可避免的存在着一定的誤差,如旋轉,平移的,這同樣需要校正解決。C,再次,電阻式觸摸屏的材料本身有差異而且隨着時間的推移,其參數也會有所變化,因此需要經常性的校正(電容式觸摸屏只需要一次校正即可)。
比較常用的校驗程序是TSLIB。通過TSLIB校驗后,應用程序讀取TSLIB里的數值,這個時候就能准確定位了。校正原理: 觸摸屏的校正過程一般為:依次在屏幕的幾個不同位置顯示某種標記(如"+"),用觸摸筆點擊這些標記, 完成校正。如果PT(x, y)表示觸摸屏上的一個點,PL(x, y)表示LCD上的一個點,校正的結果就是得到一個轉換矩陣M,使PL(x, y) = M·PT(x, y)。最終,假設LCD三個點的坐標為(XL1, YL1),(XL2, YL2),(XL2, YL2), 對應觸摸屏上的三個點是(XT1, YT1),(XT2, YT2),(XT3, YT3),則聯立兩個方程組為:
這樣,觸摸屏的校正實際上就是解上面的方程組,得到6個系數:A、B、C、D、E、F。而上面方程組按照克萊姆法則解即可。在得到6個系數后,以后通過觸摸屏得到的所有坐標,帶入公式(1)中就可以得到LCD上以像素表示的坐標。
實際上,在校正時,采集的觸摸屏的點坐標有一定的誤差,也就是說采集幾個三組點坐標,分別計算A、B、C、D、E、F,其結果不盡相同。在tslib的ts_calibrate中,采集了五組點坐標,具體代碼參見ts_calibrate.c中的perform_calibration()。一般來說,采集的點越多,校正的精確性就越高。只是采集點過多就會冗余,對校正精確性的提高作用很少,反而增加了計算時間。
歸結過程如下:
android 的坐標轉換處理:This implementation is a linear transformation using 7 parameters
(a, b, c, d, e, f and s) to transform the device coordinates (Xd, Yd) into screen coordinates (Xs, Ys) using the following equations:
s*Xs = a*Xd + b*Yd + c
s*Ys = d*Xd + e*Yd + f
其中:Xs,Ys:LCD坐標;Xd,Yd:觸摸屏坐標。
(2)調試:觸摸屏一直定位不准,會有偏移。同事經過一段時間的調試之后,解決了此問題。解決方式如下:
1,原理:Calibrate: 下載tslibonandroid,通過它來在屏幕上顯示五個坐標,點擊這五個坐標,得到需要的7個參數,實現坐標轉換。
過程:icon(apk)->jni->*.so(tslib)->生成校正參數(a0~a6)。校正時候,必須通知driver,校正完成或者校正失敗都要通知driver。而touch driver每次上電后需要讀取一次校正參數,如果讀取失敗則使用默認參數。
2,改動:
- 在$kernel/driver/input/touchscreen/s3c-ts.c,添加跟上層cupcake接口的函數。
- 改動$kernel/dirver/char/vt_ioctl.c
- 在$cupcake/development/中添加calibrate整個文件夾(實現java icon和JNI調用),該文件夾負責生成一個android應用軟件apk
- 在$cupcake/external/中添加tslibonandroid整個文件夾(實現生成校正參數)
- 在$cupcake/vendor/sec/smdk6410/init.rc,改動跟touch screen相關文件的屬性權限。
把kernel和cupcake完整編譯后,燒錄進板子。cupcake會在out/target/product/smdk6410/obj/APPS/Calibrate_intermediates下生成一個對應的apk文件,將該apk文件改名后安裝入平台,點擊即可運行與WINCE相似的校准過程。
3,程序過程:
A,點擊APK,開始運行程序。在Calibrate.java中:onCreate-》tsmainloop。同時:
public native void tsmainloop(); //JNI函數申明
System.loadLibrary("calibrate-jni"); //載入庫libcalibrate-jni.o
B,JNI的C函數原型定義在com_android_calibrate_Calibrate.cpp中:
#include "../../../external/tslibonandroid/tests/calibratejni.h" //該文件所在的文件夾作為當前目錄回退3層再進入其他子目錄
static void tsmainloop(JNIEnv *env, jobject object)
{
ts_main();
}
在該文件中,還定義了方法跟class:
static const char *classPathName = "com/android/calibrate/Calibrate"; //指向該CPP被調用的class
static JNINativeMethod methods[] = {
{"tsmainloop", "()V", (void*)tsmainloop },
};
完成JNI的注冊。
C,在external/tslibonandroid/tests路徑下的ts_calibrate.c完成TSLIB過程,如下:
#define TSLIB_TSDEVICE "/dev/input/event1" //內核為TS分配的INPUT設備名
#define DEVICE_NAME "/dev/myts" //要訪問的TS字符設備名
#define TSLIB_CALIBFILE "/system/etc/pointercal"
#define TS_POINTERCAL "/system/etc/pointercal" //最終需要用到的pointercal文件,寫入7個參數
#define TSLIB_CONFFILE "/system/etc/tslib/ts.conf" //tslib中的配置文件
typedef struct {
int x[5], xfb[5]; //x方向的觸摸屏參數,LCD參數
int y[5], yfb[5]; //y方向的觸摸屏參數,LCD參數
int a[7]; //存儲那七個參數
} calibration;
ts_main()
{
calibration cal;
char cal_buffer[256];
char buffer[5] = "OK";
int fd = open("/dev/myts",O_RDWR); //打開內核定義的TS字符設備,寫入OK字符
write(fd,buffer,2);
close(fd);
struct tsdev *ts = ts_open(getenv("TSLIB_TSDEVICE"),0) //打開內核TS設備
ts_config(ts); //讀取ts.conf,給ts配置
open_framebuffer(); //打開顯示設備,准備畫圖
put_string_center (xres / 2, yres / 4, "TSLIB calibration utility", 1); //在屏幕中心放置字符串
put_string_center (xres / 2, yres / 4 + 20, "Touch crosshair to calibrate", 2); //表示程序運行開始
clearbuf(ts); //清空TS
get_sample (ts, &cal, 0, 50, 50, "Top left");
get_sample (ts, &cal, 1, xres - 50, 50, "Top right");
get_sample (ts, &cal, 2, xres - 50, yres - 50, "Bot right");
get_sample (ts, &cal, 3, 50, yres - 50, "Bot left"); //按照左上,右上,右下,左下,中五個次序,讀取采樣值,按
get_sample (ts, &cal, 4, xres / 2, yres / 2, "Center"); //0,1,2,3,4這個index編號存儲在cal變量中
if (perform_calibration (&cal)) //如果校驗成功
{
cal_fd = open (getenv("TSLIB_CALIBFILE"), O_CREAT | O_RDWR,S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
//打開系統的pointercal文件
sprintf (cal_buffer,"%d %d %d %d %d %d %d",cal.a[1], cal.a[2], cal.a[0],cal.a[4], cal.a[5], cal.a[3], cal.a[6]);
//把cal的7個參數值組合到cal_buffer變量中
write (cal_fd, cal_buffer, strlen (cal_buffer) + 1); //寫入7個參數到pointercal文件中
}
strcpy(buffer,"END");
fd = open("/dev/myts",O_RDWR);
printf("fd: %d/n",fd);
write(fd,buffer,3);
close(fd); //寫END到內字符設備myts中
}