天天看点

Windows CE 触摸屏(TouchPanel)驱动简析(2)-DDSI函数-(基于WinCE5.0 SMDK2410 BSP的TouchPanel驱动)

接下来我们来看看具体的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).

继续阅读