(1)原理:本文拋開技術層次上來說說LINUX觸摸屏設備驅動原理。 觸摸屏,就是用一塊AD轉換模塊來將屏幕上的觸摸信號轉成數字信號。 觸摸屏常用的是四線電阻,當觸摸時候,功能模塊會將模擬信號轉換成數字信號,就是通常說的AD轉換。 在LINUX中,通常是通過中斷來讀取這些數字的。 觸摸屏幕的時候,中斷發生,LINUX通過串口或者I2C,SPI或者內部數據通道等去讀取轉換後的數字,然後把數值傳給INPUT層。


      為什麼要校驗? 原因A,觸摸屏與LCD顯示屏是兩個不同的物理器件。 LCD處理的像素,例如我們通常所說的分辨率是600x800,實際就是指每行的寬度是600個像素,高度是800個像素,而觸摸屏處理的數據是點的物理坐標,該坐標是通過觸摸屏控制器採集到的。 兩者之間需要一定的轉換。 B,其次在安裝觸摸屏時,不可避免的存在著一定的誤差,如旋轉,平移的,這同樣需要校正解決。 C,再次,電阻式觸摸屏的材料本身有差異而且隨著時間的推移,其參數也會有所變化,因此需要經常性的校正(電容式觸摸屏只需要一次校正即可)。


                                   


      比較常用的校驗程序是TSLIB。 通過TSLIB校驗後,應用程序讀取TSLIB裡的數值,這個時候就能準確定位了。 校正原理:觸摸屏的校正過程一般為:依次在屏幕的幾個不同位置顯示某種標記(如"+"),用觸摸筆點擊這些標記,完成校正。 如果P T (x, y)表示觸摸屏上的一個點,P L (x, y)表示LCD上的一個點,校正的結果就是得到一個轉換矩陣M,使P L (x, y) = M· P T (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中


}

    全站熱搜

    立你斯 發表在 痞客邦 留言(0) 人氣()