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