接下來我們來看看具體的DDSI函數.
1.DdsiTouchPanelAttach和DdsiTouchPanelDetach
DLL entry進入或結束執行,這裡不需要任何工作,直接傳回0
LONG
DdsiTouchPanelAttach(VOID)
{
return(0);
}
LONG
DdsiTouchPanelDetach(VOID)
{
return(0);
}
2.DdsiTouchPanelEnable
DdsiTouchPanelEnable使能觸摸屏并進行相應的初始化工作.
首先調用TSP_VirtualAlloc給寄存器(GPIO,ADC,INTR,PWM)配置設定虛拟位址空間.調用TSP_RegAlloc,而TSP_RegAlloc則調用VirtualAlloc和VirtualCopy進行配置設定虛拟位址.
TSP_VirtualAlloc:
PRIVATE BOOL
TSP_VirtualAlloc(VOID)
{
BOOL r = FALSE;
RETAILMSG(0,(TEXT("::: TSP_VirtualAlloc()/r/n")));
do
{
v_pIOPregs = (volatile S3C2410X_IOPORT_REG *)TSP_RegAlloc((PVOID)S3C2410X_BASE_REG_PA_IOPORT, sizeof(S3C2410X_IOPORT_REG));
if (v_pIOPregs == NULL)
{
ERRORMSG(1,(TEXT("For IOPreg: VirtualAlloc failed!/r/n")));
break;
}
v_pADCregs = (volatile S3C2410X_ADC_REG *)TSP_RegAlloc((PVOID)S3C2410X_BASE_REG_PA_ADC, sizeof(S3C2410X_ADC_REG));
if (v_pADCregs == NULL)
{
ERRORMSG(1,(TEXT("For ADCreg: VirtualAlloc failed!/r/n")));
break;
}
v_pINTregs = (volatile S3C2410X_INTR_REG *)TSP_RegAlloc((PVOID)S3C2410X_BASE_REG_PA_INTR, sizeof(S3C2410X_INTR_REG));
if (v_pADCregs == NULL)
{
ERRORMSG(1,(TEXT("For INTregs: VirtualAlloc failed!/r/n")));
break;
}
v_pPWMregs = (volatile S3C2410X_PWM_REG *)TSP_RegAlloc((PVOID)S3C2410X_BASE_REG_PA_PWM, sizeof(S3C2410X_PWM_REG));
if (v_pPWMregs == NULL)
{
ERRORMSG(1,(TEXT("For PWMregs: VirtualAlloc failed!/r/n")));
break;
}
r = TRUE;
} while (0);
if (!r)
{
TSP_VirtualFree();
RETAILMSG(0,(TEXT("::: TSP_VirtualAlloc() - Fail/r/n")));
}
else
{
RETAILMSG(0,(TEXT("::: TSP_VirtualAlloc() - Success/r/n")));
}
return (r);
}
TSP_RegAlloc:
PRIVATE PVOID
TSP_RegAlloc(PVOID addr, INT sz)
{
PVOID reg;
reg = (PVOID)VirtualAlloc(0, sz, MEM_RESERVE, PAGE_NOACCESS);
if (reg)
{
if (!VirtualCopy(reg, (PVOID)((UINT32)addr >> 8), sz, PAGE_PHYSICAL | PAGE_READWRITE | PAGE_NOCACHE ))
{
VirtualFree(reg, 0, MEM_RELEASE);
reg = NULL;
}
}
return (reg);
}
然和調用KernelIoControl來申請ADC和TIMER3的邏輯中斷号,獲得CPU的時鐘,進而計算出給Timer3的分頻值.
最後調用TSP_PowerOn來初始化GPIO,ADC,INTR,TIMER3寄存器.
關于具體寄存器引腳的含義可以參考s3c2410的datasheet.
TSP_PowerOn:
PRIVATE VOID
TSP_PowerOn(VOID)
{
RETAILMSG(0,(TEXT("::: TSP_PowerOn()/r/n")));
RETAILMSG(1,(TEXT("++TSP_PowerOn for GEC2410 TouchPanel/r/n")));
/* Use TSXM, TSXP, TSYM, TSYP */
v_pIOPregs->GPGCON |= ((0x3 << 30) | (0x3 << 28) | (0x3 << 26) | (0x3 << 24));
v_pADCregs->ADCDLY = 50000;
v_pADCregs->ADCCON = (1 << 14) | /* A/D Converter Enable */
(ADCPRS << 6) | /* Prescaler Setting */
(0 << 3) | /* Analog Input Channel : 0 */
(0 << 2) | /* Normal Operation Mode */
(0 << 1) | /* Disable Read Start */
(0 << 0); /* No Operation */
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]) */
(0 << 3) | /* Pull Up Disable */
(0 << 2) | /* Normal ADC Conversion Mode */
(3 << 0); /* Waiting Interrupt */
v_pINTregs->INTSUBMSK &= ~(1<<IRQ_SUB_TC);
v_pPWMregs->TCFG1 &= ~(0xf << 12); /* Timer3's Divider Value */
v_pPWMregs->TCFG1 |= (0 << 12); /* 1/2 */
v_pPWMregs->TCNTB3 = g_timer3_sampleticks; /* Interrupt Frequency */
}
DdsiTouchPanelEnable:
PUBLIC BOOL
DdsiTouchPanelEnable(VOID)
{
BOOL r;
UINT32 Irq;
PROCESSOR_INFO procInfo;
RETAILMSG(0, (TEXT("::: DdsiTouchPanelEnable()/r/n")));
r = TSP_VirtualAlloc();
// Obtain sysintr values from the OAL for the touch and touch changed interrupts.
//
Irq = IRQ_ADC;
if (!KernelIoControl(IOCTL_HAL_REQUEST_SYSINTR, &Irq, sizeof(UINT32), &gIntrTouch, sizeof(UINT32), NULL))
{
RETAILMSG(1, (TEXT("ERROR: Failed to request the touch sysintr./r/n")));
gIntrTouch = SYSINTR_UNDEFINED;
return(FALSE);
}
Irq = IRQ_TIMER3;
if (!KernelIoControl(IOCTL_HAL_REQUEST_SYSINTR, &Irq, sizeof(UINT32), &gIntrTouchChanged, sizeof(UINT32), NULL))
{
RETAILMSG(1, (TEXT("ERROR: Failed to request the touch changed sysintr./r/n")));
gIntrTouchChanged = SYSINTR_UNDEFINED;
return(FALSE);
}
// Get the processor's pclk frequency.
//
if (!KernelIoControl(IOCTL_PROCESSOR_INFORMATION, NULL, 0, &procInfo, sizeof(PROCESSOR_INFO), NULL))
{
DEBUGMSG(1, (TEXT("WARNING: Touch driver failed to obtain processor frequency - using default value(%d Hz)./r/n"), g_s3c2410_pclk));
}
else
{
g_s3c2410_pclk = procInfo.dwClockSpeed;
DEBUGMSG(1, (TEXT("INFO: Touch driver using processor frequency reported by the OAL (%d Hz)./r/n"), g_s3c2410_pclk));
}
// Compute the OS timer frequency and number of pen-down sampling ticks.
//
g_timer3_freq = (g_s3c2410_pclk / TIMER3_DIVIDER);
g_timer3_sampleticks = (g_timer3_freq / TSP_SAMPLE_RATE_LOW);
if (r)
{
TSP_PowerOn();
}
return (r);
}
3.DdsiTouchPanelDisable
對應的就有DdsiTouchPanelDisable函數,将DdsiTouchPanelEnable申請的資源進行釋放.屏蔽中斷.
PUBLIC VOID
DdsiTouchPanelDisable(VOID)
{
RETAILMSG(0, (TEXT("::: DdsiTouchPanelDisable()/r/n")));
if (v_pADCregs)
{
TSP_PowerOff();
TSP_VirtualFree();
}
}
4.DdsiTouchPanelSetMode
DdsiTouchPanelSetMode用來設定觸摸屏的采樣率周期.函數傳入TPSM_SAMPLERATE_LOW_ID或TPSM_SAMPLERATE_HIGH_ID來進行相應的設定.這裡隻有一個采樣率周期,是以函數不做實際工作.
PUBLIC BOOL
DdsiTouchPanelSetMode(INT iIndex, LPVOID lpInput)
{
BOOL ReturnCode = FALSE;
RETAILMSG(0, (TEXT("::: DdsiTouchPanelSetMode()/r/n")));
switch ( iIndex )
{
case TPSM_SAMPLERATE_LOW_ID:
case TPSM_SAMPLERATE_HIGH_ID:
SetLastError( ERROR_SUCCESS );
ReturnCode = TRUE;
break;
default:
SetLastError( ERROR_INVALID_PARAMETER );
break;
}
return( ReturnCode );
}
5.DdsiTouchPanelPowerHandler
DdsiTouchPanelPowerHandler用來通知驅動系統正進入或離開挂起狀态.
參數bOff為TRUE表明系統正在關閉, FALSE表明系統正在開啟.
該函數調用TSP_PowerOff和TSP_PowerOn來處理關閉或開啟狀态.
TSP_PowerOn前面已經看到了,TSP_PowerOff如下,屏蔽了INT_TC觸摸屏中斷(這裡定義為IRQ_SUB_TC):
TSP_PowerOff:
PRIVATE VOID
TSP_PowerOff(VOID)
{
RETAILMSG(0,(TEXT("::: TSP_PowerOff()/r/n")));
v_pINTregs->INTSUBMSK |= (1<<IRQ_SUB_TC);
}
DdsiTouchPanelPowerHandler:
PUBLIC VOID
DdsiTouchPanelPowerHandler(BOOL bOff)
{
RETAILMSG(0, (TEXT("::: DdsiTouchPanelPowerHandler()/r/n")));
if (bOff)
{
TSP_PowerOff();
}
else
{
TSP_PowerOn();
}
}
6.DdsiTouchPanelGetDeviceCaps
DdsiTouchPanelGetDeviceCaps用來查詢觸摸屏裝置的性能參數.
iIndex可能為以下3個參數:
TPDC_SAMPLE_RATE_ID: 傳回采樣周期,這裡TSP_SAMPLE_RATE_LOW,TSP_SAMPLE_RATE_HIGH,TSP_CurRate都為100,即一直使用同一個采樣周期.
TPDC_CALIBRATION_POINT_ID:傳回需要校準點的XY坐标.校準點坐标索引在PointNumber(lpOutput傳遞的結構成員).調用TSP_CalibrationPointGet來獲得.這5個點分别位于四個角和中間,也就是我們調用觸摸屏校準程式時會調用到的函數.
TPDC_CALIBRATION_POINT_COUNT_ID:傳回用來校準觸摸屏的校準點數目.這裡的數目設定為5.
lpOutput傳遞iIndex值對應的結構.
TPDC_SAMPLE_RATE_ID | Pointer to a TPDC_SAMPLE_RATE structure. |
TPDC_CALIBRATION_POINT_ID | Pointer to a TPDC_CALIBRATION_POINT structure. |
TPDC_CALIBRATION_POINT_COUNT_ID | Pointer to a TPDC_CALIBRATION_POINT_COUNT structure. |
CalibrationPointGet:
用來計算校準觸摸屏時的5個校準點的位置:
PRIVATE BOOL
TSP_CalibrationPointGet(TPDC_CALIBRATION_POINT *pTCP)
{
INT32 cDisplayWidth = pTCP->cDisplayWidth;
INT32 cDisplayHeight = pTCP->cDisplayHeight;
int CalibrationRadiusX = cDisplayWidth / 20;
int CalibrationRadiusY = cDisplayHeight / 20;
switch (pTCP -> PointNumber)
{
case 0:
pTCP->CalibrationX = cDisplayWidth / 2;
pTCP->CalibrationY = cDisplayHeight / 2;
break;
case 1:
pTCP->CalibrationX = CalibrationRadiusX * 2;
pTCP->CalibrationY = CalibrationRadiusY * 2;
break;
case 2:
pTCP->CalibrationX = CalibrationRadiusX * 2;
pTCP->CalibrationY = cDisplayHeight - CalibrationRadiusY * 2;
break;
case 3:
pTCP->CalibrationX = cDisplayWidth - CalibrationRadiusX * 2;
pTCP->CalibrationY = cDisplayHeight - CalibrationRadiusY * 2;
break;
case 4:
pTCP->CalibrationX = cDisplayWidth - CalibrationRadiusX * 2;
pTCP->CalibrationY = CalibrationRadiusY * 2;
break;
default:
pTCP->CalibrationX = cDisplayWidth / 2;
pTCP->CalibrationY = cDisplayHeight / 2;
SetLastError(ERROR_INVALID_PARAMETER);
return (FALSE);
}
RETAILMSG(0, (TEXT("::: TSP_CalibrationPointGet()/r/n")));
RETAILMSG(0, (TEXT("cDisplayWidth : %4X/r/n"), cDisplayWidth ));
RETAILMSG(0, (TEXT("cDisplayHeight : %4X/r/n"), cDisplayHeight ));
RETAILMSG(0, (TEXT("CalibrationRadiusX : %4d/r/n"), CalibrationRadiusX));
RETAILMSG(0, (TEXT("CalibrationRadiusY : %4d/r/n"), CalibrationRadiusY));
RETAILMSG(0, (TEXT("pTCP -> PointNumber : %4d/r/n"), pTCP->PointNumber));
RETAILMSG(0, (TEXT("pTCP -> CalibrationX : %4d/r/n"), pTCP->CalibrationX));
RETAILMSG(0, (TEXT("pTCP -> CalibrationY : %4d/r/n"), pTCP->CalibrationY));
return (TRUE);
}
DdsiTouchPanelGetDeviceCaps:
PUBLIC BOOL
DdsiTouchPanelGetDeviceCaps(INT iIndex, LPVOID lpOutput)
{
RETAILMSG(0, (TEXT("::: DdsiTouchPanelGetDeviceCaps/r/n")));
if ( lpOutput == NULL )
{
ERRORMSG(1, (__TEXT("TouchPanelGetDeviceCaps: invalid parameter./r/n")));
SetLastError(ERROR_INVALID_PARAMETER);
DebugBreak();
return (FALSE);
}
switch ( iIndex )
{
case TPDC_SAMPLE_RATE_ID:
{
TPDC_SAMPLE_RATE *pTSR = (TPDC_SAMPLE_RATE*)lpOutput;
RETAILMSG(0, (TEXT("TouchPanelGetDeviceCaps::TPDC_SAMPLE_RATE_ID/r/n")));
pTSR->SamplesPerSecondLow = TSP_SAMPLE_RATE_LOW;
pTSR->SamplesPerSecondHigh = TSP_SAMPLE_RATE_HIGH;
pTSR->CurrentSampleRateSetting = TSP_CurRate;
}
break;
case TPDC_CALIBRATION_POINT_COUNT_ID:
{
TPDC_CALIBRATION_POINT_COUNT *pTCPC = (TPDC_CALIBRATION_POINT_COUNT*)lpOutput;
RETAILMSG(0, (TEXT("TouchPanelGetDeviceCaps::TPDC_CALIBRATION_POINT_COUNT_ID/r/n")));
pTCPC->flags = 0;
pTCPC->cCalibrationPoints = 5;
}
break;
case TPDC_CALIBRATION_POINT_ID:
RETAILMSG(0, (TEXT("TouchPanelGetDeviceCaps::TPDC_CALIBRATION_POINT_ID/r/n")));
return(TSP_CalibrationPointGet((TPDC_CALIBRATION_POINT*)lpOutput));
default:
ERRORMSG(1, (__TEXT("TouchPanelGetDeviceCaps: invalid parameter./r/n")));
SetLastError(ERROR_INVALID_PARAMETER);
DebugBreak();
return (FALSE);
}
return (TRUE);
}
7.DdsiTouchPanelGetPoint
傳回最近查詢點的資訊,如坐标等.這個函數被TouchPanelpISR調用,而TouchPanelpISR就是觸摸屏中斷IST.在TouchPanelEnable中會進行中斷線程IST的建立.TouchPanelpISR會等待hTouchPanelEvent事件的發生.hTouchPanelEvent關聯了兩個中斷,觸摸屏中斷(INT_TC)和TIMER3中斷.即觸點中斷和觸點變化中斷.當中斷發生時,TouchPanelpISR調用DdsiTouchPanelGetPoint來獲得采樣點資訊.
詳情可參考tchmain.c(/WINCE500/PUBLIC/COMMON/OAK/DRIVERS/TOUCH/TCHMAIN)
代碼及注釋如下:
PUBLIC VOID
DdsiTouchPanelGetPoint(TOUCH_PANEL_SAMPLE_FLAGS * pTipStateFlags,
INT * pUncalX,
INT * pUncalY)
{
static INT x, y;
//DEBUGMSG(1, (TEXT("::: DdsiTouchPanelGetPoint()/r/n")));
if (v_pINTregs->SUBSRCPND & (1<<IRQ_SUB_TC)) /* SYSINTR_TOUCH Interrupt Case */
{ //觸點中斷(INT_TC)
*pTipStateFlags = TouchSampleValidFlag;
//讀取ADC Conversion DATA Register, 1-Stylus down state 0-Stylus up state
if ( (v_pADCregs->ADCDAT0 & (1 << 15)) |
(v_pADCregs->ADCDAT1 & (1 << 15)) )
{
//Stylus up state
bTSP_DownFlag = FALSE;
DEBUGMSG(ZONE_TIPSTATE, (TEXT("up/r/n")));
//重新設定ADC TOUCH SCREEN CONTROL REGISTER
v_pADCregs->ADCTSC &= 0xff;
//讀取上次的坐标值
*pUncalX = x;
*pUncalY = y;
//停止取樣(停止Timer3 PWM)
TSP_SampleStop();
//停止取樣後忽略TIMER3中斷
/* At this point SYSINTR_TOUCH_CHANGED (timer3) interrupt could also be pending (and masked).
Since we do not care about the timer3 interrupt after calling TSP_SampleStop, signal it Done.
If we do not signal done and it was indeed pending and masked, IRQ_TIMER3 will not be unmasked
and won't fire again unless unmasked */
if (v_pINTregs->SRCPND & (1<<IRQ_TIMER3))
InterruptDone(gIntrTouchChanged);
}
else
{
//Stylus down state
bTSP_DownFlag = TRUE;
//讀觸摸屏AD資料
if (!TSP_GetXY(&x, &y))
*pTipStateFlags = TouchSampleIgnore;
//轉換成LCD上的坐标
TSP_TransXY(&x, &y);
*pUncalX = x;
*pUncalY = y;
*pTipStateFlags |= TouchSampleDownFlag;
DEBUGMSG(ZONE_TIPSTATE, (TEXT("down %x %x/r/n"), x, y));
//啟動Timer3 PWM
TSP_SampleStart();
}
//屏蔽觸摸屏中斷,清除中斷标志
v_pINTregs->SUBSRCPND = (1<<IRQ_SUB_TC);
v_pINTregs->INTSUBMSK &= ~(1<<IRQ_SUB_TC);
//通知系統中斷處理完成
InterruptDone(gIntrTouch);
}
else /* SYSINTR_TOUCH_CHANGED Interrupt Case */
{
// TSP_SampleStart();
//Timer3 PWM中斷發生
if (bTSP_DownFlag)
{
//觸摸筆還處于down狀态
INT tx, ty;
INT dx, dy;
//讀取觸摸屏AD資料
if (!TSP_GetXY(&tx, &ty))
*pTipStateFlags = TouchSampleIgnore;
else
{ //讀到的是壞資料,進行相應處理,如不能修正則忽略該資料
TSP_TransXY(&tx, &ty);
// insert by [email protected]
#define X_ERRV 0x3bf
#define Y_ERRV 0x4ff
// Subsequent info: If the ADC provides a bad reading, this catches it and
// skips over it. Instead, it should be fixed at the source
// so as not to provide a bad reading.
if ((tx == X_ERRV) || (ty == Y_ERRV))
{
tx = x;
ty = y;
}
// =================== mostek
dx = (tx > x) ? (tx - x) : (x - tx);
dy = (ty > y) ? (ty - y) : (y - ty);
if (dx > TSP_CHANGE || dy > TSP_CHANGE)
{
*pUncalX = x = tx;
*pUncalY = y = ty;
DEBUGMSG(ZONE_TIPSTATE, (TEXT("down-c-v %x %x/r/n"), x, y));
*pTipStateFlags = TouchSampleValidFlag | TouchSampleDownFlag;
}
else
{
*pUncalX = x;
*pUncalY = y;
DEBUGMSG(ZONE_TIPSTATE, (TEXT("down-c %x %x/r/n"), x, y));
*pTipStateFlags = TouchSampleIgnore;
}
}
}
else
{
*pTipStateFlags = TouchSampleIgnore;
TSP_SampleStop();
}
InterruptDone(gIntrTouchChanged);
}
}
TSP_GetXY:
TSP_GetXY從觸摸屏的ADC寄存器讀取觸摸點資料,讀4次計算平均值.
PRIVATE BOOL
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 */
x = (0x3ff & v_pADCregs->ADCDAT1);
y = 0x3ff - (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);
}
TSP_TransXY:
PRIVATE void
TSP_TransXY(INT *px, INT *py)
{
*px = (*px - TSP_MINX) * TSP_LCDX / (TSP_MAXX - TSP_MINX);
*py = (*py - TSP_MINY) * TSP_LCDY / (TSP_MAXY - TSP_MINY);
if (*px < 0) *px = 0;
if (*px >= TSP_LCDX) *px = TSP_LCDX - 1;
if (*py < 0) *py = 0;
if (*py >= TSP_LCDY) *py = TSP_LCDY - 1;
}
讀到的坐标資料最終交由MDD層的函數進行諸如最小二乘法的數學運算,這裡就不仔細分析了,有興趣可以參考tchmdd.lib和tch_cal.lib裡的代碼(/WINCE500/PUBLIC/COMMON/OAK/DRIVERS).