在Windows CE操作系统中触摸屏驱动是一种分层驱动。其驱动模型如图1所示。上层是模型设备驱动程序(Model Device Driver, MDD),下层是依赖平台的驱动程序(Platform Dependent Driver, PDD)。MDD通常无需修改直接使用,MDD链接PDD层并定义它希望调用的函数接口:设备驱动程序提供器接口(Device Driver Service Provider Interface, DDSI)。同时MDD把不同的函数集提供给操作系统,这些函数叫做设备驱动程序接口(Device Driver Interface, DDI),这部分为也就是我们通常驱动需要实现的部分。
Windows CE的触摸屏驱动链接了tch_cal.lib和tchmdd.lib两个静态链接库。触摸屏驱动由GWES加载,GWES通过DDI调用驱动程序获取设备状态,设置驱动功能等,而驱动本身通过DDSI直接获得硬件信息来确定当前触摸屏的状态。
Windows CE触摸屏驱动要求的DDI接口包括:TouchPanelGetDeviceCaps、TouchPanelEnable、TouchPanelDisable、TouchPanelSetMode、TouchPanelReadCalibrationPoint、TouchPanelReadCalibrationAbort、TouchPanelSetCalibration、TouchPanelCalibrateAPoint、TouchPanelPowerHandler。
Windows CE触摸屏驱动要求的DDSI接口包括:DdsiTouchPanelAttach、DdsiTouchPanelDetach、DdsiTouchPanelDisable、DdsiTouchPanelEnable、DdsiTouchPanelGetDeviceCaps、DdsiTouchPanelGetPoint、DdsiTouchPanelPowerHandler。
Windows CE触摸屏驱动程序采用中断方式对触摸笔的按下状态进行检测,如果检测到触摸笔按下将产生中断并触发一个事件通知一个工作线程开始采集数据。同时,驱动将打开一个硬件定时器,只要检测到触摸笔仍然在按下状态将定时触发同一个事件通知工作线程采集数据,直到触摸笔抬起后关闭该定时器,并重新检测按下状态。驱动中采用了触摸屏中断以及定时器中断两个中断源,不仅可以监控触摸笔按下和抬起状态,而且可以检测触摸笔按下时的拖动轨迹。
触摸屏驱动在初始化过程调用TouchPanelEnable函数使能触摸屏。该函数调用的DDSI函数为:DdsiTouchPanelEnable和DdsiTouchPanelDisable。该函数实现如下内容:
1) 创建事件hTouchPanelEvent和hCalibrationSampleAvailable。hTouchPanelEvent事件在正常状态下当有触摸笔按下或者按下后需要定时采集数据时被触发。而hCalibrationSampleAvailable事件在校准状态下当有校准数据输入时被触发;
2) 检查并初始化所需的中断gIntrTouch(触摸屏中断)和gIntrTouchChanged(定时器中断),并将中断gIntrTouch、gIntrTouchChanged关联到事件hTouchPanelEvent。当gIntrTouch,gIntrTouchChanged中断产生时将触发hTouchPanelEvent事件;
3) 创建一个ISR线程TouchPanelpISR。TouchPanelpISR用于等待和处理触摸屏事件hTouchPanelEvent,它是整个驱动程序中唯一的事件源。
TouchPanelEnable()代码如下:
- BOOL
- TouchPanelEnable(
- PFN_TOUCH_PANEL_CALLBACK pfnCallback
- )
- {
- BOOL ReturnValue;
-
-
-
-
-
-
-
-
- TouchPanelpAttach();
-
- EnterCriticalSection( &csMutex );
-
-
-
-
-
-
-
-
- InterruptDone( gIntrTouch );
- InterruptDisable( gIntrTouch );
-
- if( SYSINTR_NOP != gIntrTouchChanged )
- {
- InterruptDone( gIntrTouchChanged );
- InterruptDisable( gIntrTouchChanged );
- }
-
- v_pfnCgrPointCallback = pfnCallback;
- if (v_pfnCgrCallback != NULL)
- v_pfnPointCallback = v_pfnCgrCallback;
- else
- v_pfnPointCallback = pfnCallback;
-
- ghevCalibrationActivity = NULL;
-
-
-
- ReturnValue = DdsiTouchPanelEnable();
-
- if (ReturnValue && !InterruptInitialize(gIntrTouch, hTouchPanelEvent, NULL, 0))
- {
- DEBUGMSG(ZONE_ERROR, (TEXT("TouchPanelEnable: InterruptInitialize(gIntrTouch %d failed/r/n"),
- gIntrTouch));
- DdsiTouchPanelDisable();
- ReturnValue = FALSE;
- }
-
- if ( ( SYSINTR_NOP != gIntrTouchChanged ) &&
- ReturnValue && !InterruptInitialize( gIntrTouchChanged, hTouchPanelEvent, NULL, 0))
- {
- DEBUGMSG(ZONE_ERROR, (TEXT("TouchPanelEnable: InterruptInitialize(gIntrTouchChanged %d failed/r/n"),
- gIntrTouchChanged));
- InterruptDisable(gIntrTouch);
- DdsiTouchPanelDisable();
- ReturnValue = FALSE;
- }
- if (ReturnValue)
- {
-
-
-
- bTerminate=FALSE;
- if (!(hThread = CreateThread( NULL, 0, TouchPanelpISR, 0, 0, NULL)))
- {
-
- TouchPanelpDetach();
- InterruptDisable(gIntrTouch);
- if( SYSINTR_NOP != gIntrTouchChanged )
- InterruptDisable(gIntrTouchChanged);
- DdsiTouchPanelDisable();
- ReturnValue = FALSE;
- }
- else
- {
-
- TouchPanelpGetPriority(&gThreadPriority, &gThreadHighPriority);
-
-
- CeSetThreadPriority(hThread, gThreadPriority);
- }
- }
- LeaveCriticalSection(&csMutex);
- return(ReturnValue);
- }
BOOL TouchPanelEnable( PFN_TOUCH_PANEL_CALLBACK pfnCallback ) { BOOL ReturnValue; // // Do the 'attach' code. Normally, this would have been // done in the ThreadAttach block, but this driver is set // up to be statically linked to GWE, in which case none of // the DLL related calls would even be invoked. // //创建事件hTouchPanelEvent和hCalibrationSampleAvailable TouchPanelpAttach(); EnterCriticalSection( &csMutex ); // // Insure the device is disabled and no one is attached to the logical // interrupt. // Power on the device. // Connect the logical interrupt to the device. // InterruptDone( gIntrTouch ); InterruptDisable( gIntrTouch ); // SYSINTR_NOP是什么意思? if( SYSINTR_NOP != gIntrTouchChanged ) { InterruptDone( gIntrTouchChanged ); InterruptDisable( gIntrTouchChanged ); } v_pfnCgrPointCallback = pfnCallback; if (v_pfnCgrCallback != NULL) v_pfnPointCallback = v_pfnCgrCallback; else v_pfnPointCallback = pfnCallback; ghevCalibrationActivity = NULL; //检查并初始化所需的中断gIntrTouch(触摸屏中断)和gIntrTouchChanged(定时器中断), //并将中断gIntrTouch、gIntrTouchChanged关联到事件hTouchPanelEvent。 ReturnValue = DdsiTouchPanelEnable(); // 将hTouchPanelEvent事件与中断号gIntrTouch绑定,并使能中断 if (ReturnValue && !InterruptInitialize(gIntrTouch, hTouchPanelEvent, NULL, 0)) { DEBUGMSG(ZONE_ERROR, (TEXT("TouchPanelEnable: InterruptInitialize(gIntrTouch %d failed/r/n"), gIntrTouch)); DdsiTouchPanelDisable(); ReturnValue = FALSE; } // 将hTouchPanelEvent事件与中断号gIntrTouchChanged绑定,并使能中断 if ( ( SYSINTR_NOP != gIntrTouchChanged ) && ReturnValue && !InterruptInitialize( gIntrTouchChanged, hTouchPanelEvent, NULL, 0)) { DEBUGMSG(ZONE_ERROR, (TEXT("TouchPanelEnable: InterruptInitialize(gIntrTouchChanged %d failed/r/n"), gIntrTouchChanged)); InterruptDisable(gIntrTouch); DdsiTouchPanelDisable(); ReturnValue = FALSE; } if (ReturnValue) { // Create the ISR thread. If creation fails, perform cleanup and return failure. //创建一个ISR线程TouchPanelpISR。TouchPanelpISR用于等待和处理 //触摸屏事件hTouchPanelEvent,它是整个驱动程序中唯一的事件源。 bTerminate=FALSE; if (!(hThread = CreateThread( NULL, 0, TouchPanelpISR, 0, 0, NULL))) { // 创建线程失败 TouchPanelpDetach(); InterruptDisable(gIntrTouch); if( SYSINTR_NOP != gIntrTouchChanged ) InterruptDisable(gIntrTouchChanged); DdsiTouchPanelDisable(); ReturnValue = FALSE; } else { // Get thread priority from registry TouchPanelpGetPriority(&gThreadPriority, &gThreadHighPriority); // Set our interrupt thread's priority //设置中断优先级 CeSetThreadPriority(hThread, gThreadPriority); } } LeaveCriticalSection(&csMutex); return(ReturnValue); }
TouchPanelpISR函数是实现触摸屏数据采集关键函数,它实现的内容为:
1) 等待循环,用于接收hTouchPanelEvent事件,并构成函数的主体;
2) 通过调用DdsiTouchPanelGetPoint函数获取当前触摸屏位置和状态信息;
3) 在获取有效数据且在校准状态下,收集并提交按下的位置信息;
4) 在正常状态下,校准数据,并检查校准后数据的有效性;
5) 最后调用由GWES传入的回调函数,提交位置信息和状态信息。
TouchPanelpISR()代码如下:
- static ULONG
- TouchPanelpISR(
- PVOID Reserved
- )
- {
- TOUCH_PANEL_SAMPLE_FLAGS SampleFlags = 0;
- INT32 RawX, CalX;
- INT32 RawY, CalY;
- UINT32 MaxX = DisplayWidth * X_SCALE_FACTOR;
- UINT32 MaxY = DisplayHeight * Y_SCALE_FACTOR;
- UINT32 CurrentDown = 0;
- static LONG CX;
- static LONG CY;
- static LONG XBase;
- static LONG YBase;
- static int CalibrationSampleCount;
- static BOOL fSetBase;
- static DWORD BaseTime;
- static BOOL fGotSample;
-
- PFN_TOUCH_PANEL_CALLBACK pfnCallback;
-
-
-
- while ( !bTerminate )
- {
-
- WaitForSingleObject( hTouchPanelEvent, gdwTouchIstTimeout );
- EnterCriticalSection( &csMutex );
- DEBUGMSG(ZONE_THREAD, (TEXT("TCH_INTR/r/n")) );
-
-
- if ( CurrentDown )
- SampleFlags |= TouchSamplePreviousDownFlag;
- else
- SampleFlags &= ~TouchSamplePreviousDownFlag;
-
- DdsiTouchPanelGetPoint( &SampleFlags, &RawX, &RawY );
-
- if ( SampleFlags & TouchSampleIgnore )
- {
-
- LeaveCriticalSection( &csMutex );
- continue;
- }
-
- if ( SampleFlags & TouchSampleValidFlag )
- {
-
-
-
-
- if ( CurrentDown )
- SampleFlags |= TouchSamplePreviousDownFlag;
- else
- SampleFlags &= ~TouchSamplePreviousDownFlag;
-
-
- CurrentDown = SampleFlags & TouchSampleDownFlag;
- }
-
- if ( CalibrationState )
- {
-
-
-
-
-
-
-
-
- DEBUGMSG(ZONE_SAMPLES, (TEXT("**** Calibration point (%d, %d), flags 0x%4.4X/r/n"),
- RawX, RawY, SampleFlags) );
-
-
- if ( !(SampleFlags & TouchSampleValidFlag) )
- {
- LeaveCriticalSection( &csMutex );
- continue;
- }
-
-
- if ( ghevCalibrationActivity != NULL)
- {
- SetEvent(ghevCalibrationActivity);
- }
-
-
-
- if ( (SampleFlags & (TouchSampleDownFlag|TouchSamplePreviousDownFlag)) ==
- TouchSampleDownFlag )
- {
- CalibrationState = CalibrationDown;
- fSetBase = TRUE;
- CalibrationSampleCount = 0;
- fGotSample = FALSE;
- }
-
-
- if ( (CalibrationState == CalibrationDown) && !fGotSample )
- {
- if ( SampleFlags & TouchSampleDownFlag )
- {
- long DeltaX, DeltaY;
-
- CalibrationSampleCount++;
- CX = RawX;
- CY = RawY;
- if ( fSetBase )
- {
- XBase = CX;
- YBase = CY;
- BaseTime = GetTickCount();
- fSetBase = FALSE;
- }
- DeltaX = CX - XBase;
- DeltaY = CY - YBase;
- if ( (GetTickCount() - BaseTime) > CAL_HOLD_STEADY_TIME )
- {
- fGotSample = TRUE;
- }
- else if ( ( ABS(DeltaX) > CAL_DELTA_RESET ) ||
- ( ABS(DeltaY) > CAL_DELTA_RESET ) )
- {
- RETAILMSG(1, (TEXT("M %ld,%ld %ld,%ld %ld,%ld"),
- XBase,YBase, CX,CY, DeltaX,DeltaY));
- fSetBase = TRUE;
- }
- }
- else
- {
-
- if ( CalibrationSampleCount >= MIN_CAL_COUNT )
- {
- fGotSample = TRUE;
- }
- else
- {
- CalibrationState = CalibrationWaiting;
- }
- }
-
- if ( fGotSample )
- {
- CalibrationState = CalibrationValid;
- lCalibrationXCoord = CX;
- lCalibrationYCoord = CY;
- SetEvent(hCalibrationSampleAvailable);
- }
- }
- LeaveCriticalSection( &csMutex );
- }
- else
- {
- pfnCallback = v_pfnPointCallback;
- if ( pfnCallback != NULL )
- {
- if( SampleFlags & TouchSampleIsCalibratedFlag )
- {
-
- CalX = RawX;
- CalY = RawY;
- }
- else
- {
-
- TouchPanelCalibrateAPoint( RawX, RawY, &CalX, &CalY );
-
-
- SampleFlags |= TouchSampleIsCalibratedFlag;
- }
-
- LeaveCriticalSection( &csMutex );
-
-
- if( CalX < 0 )
- CalX = 0;
- else if( MaxX && ((UINT32)CalX >= MaxX) )
- CalX = MaxX - X_SCALE_FACTOR;
- if( CalY < 0 )
- CalY = 0;
- else if( MaxY && ((UINT32)CalY >= MaxY) )
- CalY = MaxY - Y_SCALE_FACTOR ;
-
- DEBUGMSG( ZONE_SAMPLES,
- (TEXT("**** Queuing point (%d, %d), flags 0x%4.4X/r/n"),
- CalX, CalY, SampleFlags) );
- #ifdef DEBUG
- {
- static DWORD SampleCt;
-
- if( SampleFlags & TouchSampleDownFlag )
- SampleCt++;
- else
- {
- DEBUGMSG( ZONE_TIMING,
- (TEXT("%d down samples queued/r/n"),
- SampleCt) );
- SampleCt = 0;
- }
- }
- #endif
-
- (pfnCallback)( SampleFlags, CalX, CalY);
- }
- else
- {
- LeaveCriticalSection( &csMutex );
- }
- }
- }
- ExitThread(1);
- return ( TRUE );
- }
从基于S3C6410的Touch驱动详解(之一)可以看到,在触摸屏驱动程序中DdsiTouchPanelEnable、DdsiTouchPanelDisable和DdsiTouchPanelGetPoint三个DDSI接口函数是驱动实现的关键所在。
这段代码有点难理解,要通过两个中断源以及事件一起理解。 我们先看MDD层是如何调用这个函数的。
DdsiTouchPanelGetPoint( &SampleFlags, &RawX, &RawY );
在MDD层中,通过上面的调用方式,从DdsiTouchPanelGetPoint()函数中回传了三个参数,第一个用于表明触摸正处于的状态,RawX,RawY是调用的函数从TP触摸点取样得到的点的数据,这里的数据还是原始的ADC采样的数据,即直接从ADC转换的寄存器里面得到的,并没有对该数据进行处理。
先看看SampleFlags这个变量,一起有如下几种状态:
TouchSampleValidFlag = 0x01, The sample is valid.
TouchSampleDownFlag = 0x02, The finger/stylus is down.
TouchSampleIsCalibratedFlag = 0x04, The XY data has already been calibrated.
TouchSamplePreviousDownFlag = 0x08, The state of the previous valid sample.
TouchSampleIgnore = 0x10, Ignore this sample.
TouchSampleMouse = 0x40000000 // reserved
TouchSampleValidFlag标志位表示已经获得TP采样事件,准备开始采样;
TouchSampleDownFlag标志位表示检测到TP已经按下去;
TouchSampleIsCalibratedFlag标志位表示TP已经校准,没有必要再校准了;
TouchSamplePreviousDownFlag标志位是用于校准计数用的,每校准一次要采样五组数据;
TouchSampleIgnore标志位表示触摸失败,采样数据无效;
上面的程序中,大的框架如下:
if (g_pVIC1Reg->VICRAWINTR & (1<<(PHYIRQ_PENDN-VIC1_BIT_OFFSET)))
{
......
if ((g_pADCReg->ADCDAT0 & D_UPDOWN_UP)
|| (g_pADCReg->ADCDAT1 & D_UPDOWN_UP))
{
.........
TSP_SampleStop();
}
else
{
........
TSP_SampleStart();
}
}
else
{
if ((g_pADCReg->ADCDAT0 & D_UPDOWN_UP)
|| (g_pADCReg->ADCDAT1 & D_UPDOWN_UP))
{
........
TSP_SampleStop();//停止定时器中断
}
else
{
........
}
}
在这里,第一个大的if里面,是检测到了触摸屏中断,然后判断TP是否按下,按下则开起定时器3,用于定时产生中断,这时会结束触摸屏中断,中断转交给定时器。在大的else中,就是定时器中断所需执行的代码了。这里并不是第一个if语句判断有效,后面的else就不执行了,因为触摸屏中断结束后,定时器中断会定时的继续产生中断,从而又会继续判断前面的if标志位,这样就会执行到else语句中来。
在else语句中,会调用TSP_GetXY(&TmpX, &TmpY)函数,用于采样TP按下的点。具体代码如下:
- static BOOL
- TSP_GetXY(int *px, int *py)
- {
- int i,j,k;
- int temp;
- int x[TSP_SAMPLE_NUM], y[TSP_SAMPLE_NUM];
- int dx, dy;
- int TimeOut = 100;
-
- EnterCriticalSection(&g_csTouchADC);
-
- for (i = 0; i < TSP_SAMPLE_NUM; i++)
- {
- g_pADCReg->ADCTSC = ADCTSC_AUTO_ADC;
- g_pADCReg->ADCCON |= ENABLE_START_EN;
-
- while (g_pADCReg->ADCCON & ENABLE_START_EN)
- {
- if(TimeOut-- < 0)
- {
- RETAILMSG(ZONE_ERROR,(TEXT("ADC cannot start/n")));
- goto ADCfails;
- }
- Sleep(1);
- }
-
- TimeOut = 100;
- while (!(g_pADCReg->ADCCON & ECFLG_END))
- {
- if(TimeOut-- < 0)
- {
- RETAILMSG(ZONE_ERROR,(TEXT("ADC Conversion cannot be done/n")));
- goto ADCfails;
- }
- Sleep(1);
- }
-
- x[i] = D_XPDATA_MASK(g_pADCReg->ADCDAT0);
- y[i] = D_YPDATA_MASK(g_pADCReg->ADCDAT1);
- }
-
- ADCfails:
- LeaveCriticalSection(&g_csTouchADC);
-
- for (j = 0; j < TSP_SAMPLE_NUM -1; ++j)
- {
- for (k = j+1; k < TSP_SAMPLE_NUM; ++k)
- {
- if(x[j]>x[k])
- {
- temp = x[j];
- x[j]=x[k];
- x[k]=temp;
- }
-
- if(y[j]>y[k])
- {
- temp = y[j];
- y[j]=y[k];
- y[k]=temp;
- }
- }
- }
-
- #ifdef DETAIL_SAMPLING
-
-
- *px = (x[2] + ((x[3]+x[4])<<1) + (x[3]+x[4]) + x[5]);
- *py = (y[2] + ((y[3]+y[4])<<1) + (y[3]+y[4]) + y[5]);
-
- if ((*px & 0x7) > 3) *px = (*px>>3) + 1;
- else *px = *px>>3;
-
- if ((*py & 0x7) > 3) *py = (*py>>3) + 1;
- else *py = *py>>3;
-
- dx = x[5] - x[2];
- dy = y[5] - y[2];
- #else
-
-
- *px = (x[1] + x[2] + 1)>>1;
- *py = (y[1] + y[2] + 1)>>1;
-
- dx = x[2] - x[1];
- dy = y[2] - y[1];
- #endif
-
- if ((dx > TSP_INVALIDLIMIT) || (dy > TSP_INVALIDLIMIT))
- {
- return FALSE;
- }
- else
- {
- return TRUE;
- }
- }
这里用到了一组简单的数学算法,详细见程序的注释。ADC采样一起采样8次,舍掉最小值,最大值,再加权求平均,最终得到一组TP采样值,回传。
采样的数据会继续过滤一次,通过调用Touch_Pen_Filtering(&TmpX, &TmpY)函数实现。具体代码如下:
- static BOOL
- Touch_Pen_Filtering(INT *px, INT *py)
- {
- BOOL RetVal = TRUE;
-
-
- INT Filter_Margin;
- static int count = 0;
- static INT x[2], y[2];
- INT TmpX, TmpY;
- INT dx, dy;
-
- if(*px <0 && *py <0)
- {
- count = 0;
- return FALSE;
- }
- else
- {
- count++;
- }
-
- if (count > 2)
- {
-
- count = 2;
-
-
-
- TmpX = (x[0] + *px)>>1;
- TmpY = (y[0] + *py)>>1;
-
-
-
- dx = (x[1] > TmpX) ? (x[1] - TmpX) : (TmpX - x[1]);
- dy = (y[1] > TmpY) ? (y[1] - TmpY) : (TmpY - y[1]);
-
-
- Filter_Margin = (x[1] > x[0]) ? (x[1]-x[0]) : (x[0]-x[1]);
- Filter_Margin += (y[1] > y[0]) ? (y[1]-y[0]) : (y[0]-y[1]);
- Filter_Margin += TSP_FILTER_LIMIT;
-
-
-
- if ((dx > Filter_Margin) || (dy > Filter_Margin))
- {
-
- *px = x[1];
- *py = y[1];
- RetVal = FALSE;
- count = 0;
- }
- else
- {
-
- x[0] = x[1]; y[0] = y[1];
- x[1] = *px; y[1] = *py;
-
- RetVal = TRUE;
- }
- }
- else
- {
-
-
-
- x[0] = x[1]; y[0] = y[1];
- x[1] = *px; y[1] = *py;
-
- RetVal = FALSE;
- }
-
- return RetVal;
- }
看到这里也许会觉得奇怪,为什么会是count>2呢?
这里是联合定时器3的定时中断实现。只有当采样了两组数据之后,该函数才会返回真,否则采样的数据是不作数的。实际上是一起采样了三组数据,具体算法见程序中注释。
经过过滤后的数据再回传给MDD,然后提交。
这里提交的数据其实仍然是直接从ADCDAT寄存器中读出的数据,并没有做相应处理。由于TP本身的非线性,程序为了兼容各种不同的非线性TP,微软通过一组数学算法将这个很复杂的问题轻而易举的通过程序解决了。
在Windows CE中通过在函数DdsiTouchPanelGetDeviceCaps 中设置校准点的个数,在TouchDriverCalibrationPointGet中获取每个校准点的屏幕坐标。常用的校准点数量为5。校准UI将在校准点坐标处相应显示一个十字叉,用户需要精确地在该十字叉位置按下触摸屏,驱动通过TouchPanelReadCalibrationPoint函数读取相应的触摸屏坐标值,然后开始下一个校准点。循环设定的次数后,将采集到的触摸屏坐标值和校准点屏幕坐标送到TouchPanelSetCalibration函数中进行处理。该函数将产生校准基准参数。
TouchPanelSetCalibration函数执行的动作是一套数学算法,具体内容为:
在触摸屏数据与其位置偏移关系且屏幕像素与其位置偏移关系同为线性关系假设情况下,触摸屏返回的位置信息与像素位置信息之间成2D坐标变换关系。则对于触摸屏按下点的触摸屏坐标(Tx,Ty)与其在显示设备位置关系上匹配的点的屏幕坐标(Sx,Sy)之间的转换关系,可以通过下述坐标变换表示:
Sx = A1*Tx + B1*Ty + C1
Sy = A2*Tx + B2*Ty + C2
TouchPanelSetCalibration的具体工作就是通过校准的动作获取的屏幕坐标和触摸屏坐标TouchCoordinate来确定A1,B1,C1和A2, B2, C2。
在开发产品时,针对不同的屏,注册表中默认的校正值通常是不准的,这时就需要得到一组准确的值。这组值怎么样获得呢?很明显,上面的函数DdsiTouchPanelGetPoint()回传的点的数据并不是我们想要的,因为它并没有做任何的处理。我们真正需要的是经过上面的公式换算后得到的值。其实我们只需要校正一遍TP后,Touch驱动程序会自动将校正好的一组正确的值重刷到注册表,我们只需要打开我们手上机器的注册表,查看其键值就行了。具体在localmachine->hardware->touch中可以看到。