轉(zhuǎn)載請注明出處
作者:小馬
前段時間移植 6.0 BSP,目前已移植到觸摸屏部分了. 移植過程中學(xué)到了不少東西. 由其是關(guān)于觸摸屏這部分, 掌握了很多以前不會的東西. 覺得有必要把這些知識點整理一下.
一 硬件部分
硬件上的原理不是本文的重點,只講一下大概的原理(主要是我也只知道大概的原理, 畢竟咱不是搞硬件的. 嘻嘻!)
我移植用的這個屏是320*240 的TFT屏, 四線電阻式觸屏. 這種觸屏的原理是由兩個電阻層組成, 一個實現(xiàn)X位置的測量,一個用于Y位置上的測量. 簡單來說,就是當(dāng)用觸筆按下屏幕時,兩個電阻層接觸, 電阻發(fā)生變化,然后在X Y方向上產(chǎn)生信號, 這個信號是電壓信號, 再經(jīng)過CPU內(nèi)部分AD轉(zhuǎn)換為坐標(biāo)值. 這個原理有點像高中物理課用的滑動電阻,有一個最大上限,滑動到不同的地方,阻值不同. 2410本身集成了touch的控制器,通過簡單的配置和讀取相關(guān)的寄存器,就可以實現(xiàn)觸摸屏的操作.
二 驅(qū)動部分
Wince下的touch驅(qū)動跟很多其它的驅(qū)動一樣, 是分層的, 有MDD 和PDD兩層. MDD層被系統(tǒng)隱藏起來, 一般不用我們來修改. 而我們真正關(guān)心的是PDD 層. 也就是要由開發(fā)者來修改的這一層.
分析touch驅(qū)動時,以我最近剛剛移植到一個基于2410的板子上的6.0的BSP包的觸屏驅(qū)動為例.到C:\WINCE600\PLATFORM\DEVICEEMULATOR\SRC\DRIVERS\TOUCH下. 找到s3c2410x_touch.cpp文件. 這里面正是PDD層的實現(xiàn)代碼. 容易發(fā)現(xiàn)這里面的函數(shù)分為兩類,一類是以TSP開頭的函數(shù),一類是以DDSI開頭的函數(shù). TSP開頭的函數(shù)為內(nèi)部私有的函數(shù),是被DDSI調(diào)用的, 而DDSI開頭的函數(shù)則是對外的接口, 也就是被MDD層的函數(shù)調(diào)用的接口.
DdsiTouchPanelEnable是首先被調(diào)用的一個外部接口, 它的實現(xiàn)可參見源程序, 它主
要做了下面幾個事情:
1 通過調(diào)用TSP_VirtualAlloc函數(shù)為驅(qū)動所用的IO,中斷等硬件中斷分配內(nèi)存空間.
2 通過調(diào)用KernelIoControl向系統(tǒng)申請兩個中斷,如果申請成功,賦予相應(yīng)的邏輯中斷號. KernelIoControl向底層是調(diào)用OEMIoControl函數(shù), OEMIoControl根據(jù)KernelIoControl傳進(jìn)來的IOCTL代碼,做相應(yīng)的操作,比如這里, IOCTL是IOCTL_HAL_REQUEST_SYSINTR, 它是向內(nèi)核申請一個物理中斷和邏輯中斷的映射.
3 通過調(diào)用TSP_PowerOn來初始化中斷控制器,ADC寄存器,定時器等, 在TSP_PowerOn的實現(xiàn)中,有幾點要說明一下:
ADCDLY 這個值在不同的模式下意義不同, 因為前面通過ADCTSC已經(jīng)配置為wait for interrupt mode, 所以這個值的意義和你的觸筆按下時, 從產(chǎn)生中斷信號到開始自動轉(zhuǎn)換X,Y時的時間間隔是相關(guān)的,它的單位是ms
v_pPWMregs->TCNTB3 = g_timer3_sampleticks
TCNTB3是timer3的count buffer, 當(dāng)定時器啟動時, 0,這個值以一個設(shè)置好的頻率遞減,直到減到0, 這時會產(chǎn)生一個定時器中斷. 這個有什么用呢. 要理解它,得知道觸摸屏在中斷模式下是如何工作的.
當(dāng)我們按下的觸摸屏?xí)r,會產(chǎn)生一個ADC的中斷, 同時我們的驅(qū)動還會啟動一個定時器, 這個定時器觸發(fā)一個事件做數(shù)據(jù)采集, 在我們的手或觸筆抬起來前,這個定時器不斷的觸發(fā)采集事件,直到它被關(guān)閉, 而它什么時候會被關(guān)閉呢,就是在觸筆的抬起來時. 下面截取的代碼很好的說明的這個原理:
if ( (v_pADCregs->ADCDAT0 & (1 << 15)) |(v_pADCregs->ADCDAT1 & (1 << 15)) )
{
bTSP_DownFlag = FALSE;
DEBUGMSG(ZONE_TIPSTATE, (TEXT("up\r\n")));
v_pADCregs->ADCTSC &= 0xff;
*pUncalX = x;
*pUncalY = y;
TSP_SampleStop();
……
}
上面的代碼,if判斷的正是是否抬起.
而g_timer3_sampleticks的值是這樣計算出來的.
g_timer3_freq = (g_s3c2410_pclk / TIMER3_DIVIDER);
g_timer3_sampleticks = (g_timer3_freq / TSP_SAMPLE_RATE_LOW);
TIMER3_DIVIDER 的值是2, TSP_SAMPLE_RATE_LOW的值是100, 由
v_pPWMregs->TCFG1 &= ~(0xf << 12);
v_pPWMregs->TCFG1 |= (0 << 12);
可知定時器1/2分頻, 所以,很容易計算出,所設(shè)置的定時器是每10ms產(chǎn)生一次定時器中斷
而觸摸屏中斷是在你按下和抬起時產(chǎn)生的.
DdsiTouchPanelGetPoint是采樣的主要實現(xiàn)函數(shù),當(dāng)MDD檢測到中斷事件發(fā)生時,該函數(shù)會被調(diào)用. 觸摸屏的中斷是SYSINTR_TOUCH, 而定時器的中斷是SYSINTR_TOUCH_CHANGED
該函數(shù)用if else分別處理兩種中斷, 如下:
if (v_pINTregs->SUBSRCPND & (1<<IRQ_SUB_TC)) /* 觸摸屏中斷*/
{
……
}
else /*定時器中斷 */
{
}
DdsiTouchPanelGetPoint函數(shù)的實現(xiàn)代碼中,調(diào)用了兩個很重要的函數(shù)TSP_TransXY和TSP_GetXY
需要說明的是,這兩個函數(shù)的實現(xiàn)跟LCD本身的分辨率是沒有關(guān)系的.
TSP_GetXY用來獲到AD采樣值,TSP_TransXY把它轉(zhuǎn)化為屏上的坐標(biāo). 我移植touch驅(qū)動時,遇到過點屏幕上面,下面有反應(yīng),或者點左上角,右上角有反應(yīng)等類似的問題, 都是因為這兩個函數(shù)沒實現(xiàn)好.
先來看TSP_GetXY函數(shù).它的實現(xiàn)如下:
TSP_GetXY(INT *px, INT *py)
{
INT i;
INT xsum, ysum;
INT x, y;
INT dx, dy;
xsum = ysum = 0;
for (i = 0; i < TSP_SAMPLE_NUM; i++)
{
v_pADCregs->ADCTSC = (0 << 8) | /* UD_Sen*/
(1 << 7) | /* YMON 1 (YM = GND)*/
(1 << 6) | /* nYPON 1 (YP Connected AIN[n])*/
(0 << 5) | /* XMON 0 (XM = Z)*/
(1 << 4) | /* nXPON 1 (XP = AIN[7])*/
(1 << 3) | /* Pull Up Enable*/
(1 << 2) | /* Auto ADC Conversion Mode*/
(0 << 0); /* No Operation Mode*/
v_pADCregs->ADCCON |= (1 << 0); /* Start Auto conversion*/
while (v_pADCregs->ADCCON & 0x1); /* check if Enable_start is low*/
while (!(v_pADCregs->ADCCON & (1 << 15))); /* Check ECFLG*/
y = (0x3ff & v_pADCregs->ADCDAT1);
x = (0x3ff & v_pADCregs->ADCDAT0);
xsum += x;
ysum += y;
}
*px = xsum / TSP_SAMPLE_NUM;
*py = ysum / TSP_SAMPLE_NUM;
v_pADCregs->ADCTSC = (1 << 8) | /* UD_Sen*/
(1 << 7) | /* YMON 1 (YM = GND)*/
(1 << 6) | /* nYPON 1 (YP Connected AIN[n])*/
(0 << 5) | /* XMON 0 (XM = Z)*/
(1 << 4) | /* nXPON 1 (XP = AIN[7])*/
(0 << 3) | /* Pull Up Disable*/
(0 << 2) | /* Normal ADC Conversion Mode*/
(3 << 0); /* Waiting Interrupt*/
dx = (*px > x) ? (*px - x) : (x - *px);
dy = (*py > y) ? (*py - y) : (y - *py);
return((dx > TSP_INVALIDLIMIT || dy > TSP_INVALIDLIMIT) ? FALSE : TRUE);
}
關(guān)于這個函數(shù)有幾點要說明.
根據(jù)2410的手冊, ADCDAT0 保存是X方向上采樣的結(jié)果, ADCDAT1 保存是Y方向上采樣的結(jié)果, 所以, 我們看到下面的兩行代碼
y = (0x3ff & v_pADCregs->ADCDAT1);
x = (0x3ff & v_pADCregs->ADCDAT0);
與上0x3ff, 是因為, ADCDAT寄存器只用了前面 10位來保存AD采樣的結(jié)果, 而這和2410內(nèi)部的AD模塊只有10位精度是相一致的.所以,AD轉(zhuǎn)換后的最大值不會超過1024-1.
當(dāng)然上在那種計算方法并不是絕對的 , 根據(jù)硬件構(gòu)造的不同, 比如有可能你x方向的坐標(biāo)值和采樣值成反比,就要按下面的方式計算:
x = 0x3ff - (0x3ff & v_pADCregs->ADCDAT0);
再看TSP_TransXY函數(shù). 我移植的版本的實現(xiàn)如下:
PRIVATE void
TSP_TransXY(INT *px, INT *py)
{
*px = (*px >= TSP_MAXX) ? (TSP_MAXX) : *px;
*py = (*py >= TSP_MAXY) ? (TSP_MAXY) : *py;
*px = (*px - TSP_MINX);
*py = (*py - TSP_MINY);
*px = (*px >= 0) ? *px : 0;
*py = (*py >= 0) ? *py : 0;
*px = *px * TSP_LCDY / (TSP_MAXX - TSP_MINX);
*py = *py * TSP_LCDX / (TSP_MAXY - TSP_MINY);
*px = (*px >= TSP_LCDY)? (TSP_LCDY - 1) : *px;
*py = (*py >= TSP_LCDX)? (TSP_LCDX - 1) : *py;
*px = TSP_LCDY - *px - 1;
*py = TSP_LCDX - *py - 1;
}
這個實現(xiàn)是我在模擬器的實現(xiàn)代碼基礎(chǔ)上修改的. 這個函數(shù)計算X,Y的坐標(biāo)用的是一個公式,至于這個公式是怎么來的,我就不太清楚了. 只說明一點.
#define TSP_MINX 88
#define TSP_MINY 84
#define TSP_MAXX 952
#define TSP_MAXY 996
上面四個值是定義X+, X-, Y+, Y-四個有效的采樣值, 理論上應(yīng)該是0和1023(10 bit ADC), 但實際肯定有偏差,準(zhǔn)確來講, 換了不同的硬件平臺,這四個值應(yīng)該是要重新測過的. 我就直接沿用原BSP中的值了.