Touch Driver介绍

 Touch Driver介绍

一.相关知识介绍

1Touch Driver的加载过程

GWES[HKEY_LOCAL_MACHINE/HARDWARE/DEVICEMAP/TOUCH]的“Driver name”获取Driver DLL的名字,如果没有找到该键值,则使用默认名字Touch.dll

Touch Driver的加载内容非常简单,就是调用一下导出函数TouchPanelEnable()

2.导出函数介绍

与其它很多Driver类似,Touch Driver采用PDD+MDD双层架构,如下图所示:

         OEM厂商只需要修改微软PDD层的代码,实现PDD层的API即可,必须实现的PDD层的API包括:

Programming element

Description

DdsiTouchPanelAttach

This function executes when the MDD's DLL entry point receives a DLL_PROCESS_ATTACH message.

DdsiTouchPanelDetach

This function executes when the MDD's DLL entry point receives a DLL_PROCESS_DETACH message.

DdsiTouchPanelDisable

This function disables the touch screen device.

DdsiTouchPanelEnable

This function applies power to the touch screen device and initializes it for operation.

DdsiTouchPanelGetDeviceCaps

This function queries capabilities of the touch screen device.

DdsiTouchPanelGetPoint

This function returns the most recently acquired point and its associated tip state information.

DdsiTouchPanelPowerHandler

This function indicates to the driver that the system is entering or leaving the suspend state.

DdsiTouchPanelSetMode

This function sets information about the touch screen device.

         由于PDD层的代码非常简单,所以这里不做讨论。

         下面对MDD层的API,同时也是Touch Panel Driver必须的导出的一些接口,做一些简单分析,很多东西是我个人理解,有可能是错误的。

1> TouchPanelGetDeviceCaps

         获取Touch Driver的一些参数,根据GWES传入的Code不同,返回不同的信息给GWES

>> TPDC_SAMPLE_RATE_ID

         返回Touch Driver的采样速率给上层,包括当前速率,高速和慢速。

struct TPDC_SAMPLE_RATE {

  INT SamplesPerSecondLow;

  INT SamplesPerSecondHigh;

  INT CurrentSampleRateSetting;

};

>> TPDC_CALIBRATION_POINT_COUNT_ID

         告诉上层进行屏幕校准的时候采样点是多少。

struct TPDC_CALIBRATION_POINT_COUNT {

  DDI_TOUCH_PANEL_CALIBRATION_FLAGS flags;

  INT cCalibrationPoints;

};

>> TPDC_CALIBRATION_POINT_ID

         告诉上层采样点总共有多少个,及其坐标位置。

struct TPDC_CALIBRATION_POINT {

  INT PointNumber;

  INT cDisplayWidth;

  INT cDisplayHeight;

  INT CalibrationX;

  INT CalibrationY;

};

2> TouchPanelEnable

         初始化Touch Driver,所有相关硬件有关的初始化以及数据采集中断线程的创建都是在这里完成。

         值得一提的是其传入参数是一个处理EventCallback函数,在数据采集中断线程中需要通过该Callback反馈给GWES坐标信息。所以这里需要将这个Callback函数指针给保存下来以供后续使用。

         另外,该函数除了在初始化的时候调用,按照帮助文档的解释,在系统运行的某个时间还会再次调用,所以该函数中有一些中断处理的操作。小弟一介菜鸟,不知道啥时候会再次调用,呵呵。

3> TouchPanelDisable

无语了。

4> TouchPanelSetMode

         根据Code的不同配置Touch Panel的不同工作状态,MDD层代码中主要有下面的一些分支:

>> TPSM_PRIORITY_HIGH_ID

         重新配置IST优先级为高优先级。

>> TPSM_PRIORITY_NORMAL_ID

         重新配置IST的优先级为正常优先级。

         该线程的默认优先级是109,可以在注册表//Drivers//BuiltIn//Touch下的"Priority256""HighPriority256"进行配置

5> TouchPanelReadCalibrationPoint

         GWES直接调用,在校准屏幕的时候使用,后面会对该函数详细的讲述。    

6> TouchPanelReadCalibrationAbort

         终止校准屏幕的过程,方法是直接给校准屏幕的函数TouchPanelReadCalibrationPoint发送一个hCalibrationSampleAvailable Event,并由函数TouchPanelReadCalibrationPoint通知GWES里面的校准线程(是否有该线程,我不太确定,我是推测的)校准失败。

         GWES看来,它只通过TouchPanelReadCalibrationPoint的返回值来判断校准的正确与否,无论任何原因引起的校准没有完成,它都认为是失败,包括用户的Abort

7> TouchPanelCalibrateAPoint

         Touch Driver通过中断数据采集线程IST告诉GWES当前触摸点的坐标,方式是调用Callback函数,然后GWES再通过调用TouchPanelCalibrateAPoint()对坐标位置进行校准,是由TouchPanelpISR直接调用,不是由GWES直接调用。校准公式如下:

         校准的公式如下:

  Sx = A1*Tx + B1*Ty + C1

Sy = A2*Tx + B2*Ty + C2

         其中,TxTy是触摸屏坐标,而SxSy是显示设备坐标,其中A1/B1/C1/A2/B2/C2是校准参数,在屏幕进行校准的时候计算出来,在GWES调用TouchPanelSetCalibration()的时候会使用到。

         该函数的声明如下:

VOID TouchPanelCalibrateAPoint(

  INT32 UncalX,

  INT32 UncalY,

  INT32* pCalX,

  INT32* pCalY

);

Parameters

UncalX

[in] Noncalibrated x-coordinate.

UncalY

[in] Noncalibrated y-coordinate.

pCalX

[out] Pointer to the returned calibrated x-coordinate.

pCalY

[out] Pointer to the returned calibrated y-coordinate.

8> TouchPanelSetCalibration

         该函数利用Touch Panel的采样点坐标和屏幕的显示坐标来计算Touch Panel Driver的校正参数。

Microsoft通过下列的方式来计算坐标点:

  Sx = A1*Tx + B1*Ty + C1

Sy = A2*Tx + B2*Ty + C2

 

         其中,TxTy是触摸屏坐标,而SxSy是显示设备坐标。

         该函数的功能就是根据采样点和屏幕上的十字号的值计算出A1B1C1A2B2C2

9> TouchPanelPowerHandler

         这个函数虽然简单,但是在设备电源管理中有用处。与其它Driver中的PowerHandle作用类似。

二.Touch DriverGWES的交互

1Touch Driver中的 Callback 函数

GWES调用TouchPanelEnable()初始化Touch Driver,并传入Callback函数指针pfnCallback作为实参。在TouchPanelEnable()中将会保存该Callback函数的指针,并在数据采集中断线程IST中调用该Callback函数,以通知GWES目前的坐标点。

函数TouchPanelEnable()中对该Callback函数的处理如下:

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.

    //

    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 );

    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) {

        // Create the ISR thread.  If creation fails, perform cleanup and return failure.

        //

        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);

}

         数据采集中断线程TouchPanelpISRCallback函数的调用参照后面的函数介绍。

2.触摸板坐标数据的采集

         想象一下,GWES不停的去向Touch Panel Driver索取坐标点,如果用户一直没有点击的话,这将是多么大的一种资源的浪费。所以在微软设计Touch DriverGWES的时候,Touch Driver主动地去通知GWES触摸板的坐标,而不是反过来GWES主动地去向Touch Driver索取触摸板的坐标。其它与用户输入和显示有关的驱动也是这样的设计思路,这种设计思路和系统设备管理器的设计思路恰好相反,NND,微软太有才了。

         不扯了,言归正传。

         GWES通过调用函数TouchPanelEnable()初始化Touch Driver。在函数TouchPanelEnable()中将会创建线程TouchPanelpISR,该线程负责触摸板坐标的采集和使用Callback函数通知GWES Touch Panel采集的结果。其默认优先级是109,可以在注册表//Drivers//BuiltIn//Touch下的"Priority256""HighPriority256"进行配置,并通过TouchPanelSetMode()改变其优先级。

         其实触摸板坐标的采集全部都是在TouchPanelpISR中完成,有关该函数的操作,可以参照后面对该函数的详细解释。

3.校准Touch Panel的过程描述

         在前面介绍函数TouchPanelCalibrateAPoint()的时候,曾经提到下面的一个公式:

  Sx = A1*Tx + B1*Ty + C1

Sy = A2*Tx + B2*Ty + C2

         校准屏幕的目的就是确定A1/B1/C1/A2/B2/C2的值。

         校准屏幕分为即开机自动校准和利用校准工具校准。

1> 开机自动校准     

         注册表项HKEY_LOCAL_MACHINE/HARDWARE/DEVICEMAP/TOUCH"CalibrationData"下保存着默认的屏幕校准数据。

         每次开机启动后,GWES会从该注册表项下获取默认的屏幕校准数据,具体校准数据的采样点数与GWES中通过TouchPanelGetDeviceCapsTPDC_CALIBRATION_POINT_COUNT_ID分支获取的采样点数相同。从MDD层的代码中没有找到获取该默认值的代码,我猜测可能是GWES直接去获取该值。

         GWES获取到该值后,将会去调用函数TouchPanelSetCalibration()来对Touch Panel进行校准,以此来获取A1/B1/C1/A2/B2/C2的值,并存放到Driver的全局变量v_CalcParam中。

         顺便在此列出来v_CalcParam的类型定义:

typedef struct {

    INT32   a1;

    INT32   b1;

    INT32   c1;

    INT32   a2;

    INT32   b2;

    INT32   c2;

    INT32   delta;

}   CALIBRATION_PARAMETER, *PCALIBRATION_PARAMETER;

2> 使用微软校准工具进行校准

         通过添加SYSGEN_TOUCH对应的Feature可以在OS中添加该校准工具,其实就是在控制面板中看到的屏幕校准工具。当然,你也可以自己写一个校准工具,直接调用API就行了。

         校准过程如下:

>> 获取校准基础信息

         调用TouchPanelGetDeviceCapsTPDC_CALIBRATION_POINT_COUNT_ID分支获取屏幕校准的坐标点数,在这里假设是5个;

>> GWES调用Touch Driver获取屏幕坐标点位置和触摸板坐标点位置

         GWES调用TouchPanelGetDeviceCapsTPDC_CALIBRATION_POINT_ID分支获取第1个校准点的屏幕坐标位置(注意:并不是触摸板的坐标位置),然后由GWES调用Display Driver在屏幕上画出十字号,接着GWES调用函数TouchPanelReadCalibrationPoint()获取触摸板坐标点位置。

         按照相同的方法依次对剩余的四个坐标点做类似操作。

>> 校准

         GWES将前面一步获取的屏幕坐标点和触摸板坐标点传递给Touch Driver,由函数TouchPanelSetCalibration()进行校准参数的计算,并将结果写入到全局变量v_CalcParam中。

 

         下面详细的介绍一下与校准屏幕有关的线程TouchPanelpISR和函数TouchPanelReadCalibrationPoint()

         当校准屏幕应用程序运行的时候,GWES将会直接去调用获取完Touch Panel支持的采样点个数后,将会直接去调用TouchPanelReadCalibrationPoint()。该函数的主要用途是告知IST需要校准屏幕,并等待IST中校准屏幕的结果,每校准一个坐标点,该函数和IST会通过Event联系一次。

         函数TouchPanelReadCalibrationPoint()的详细解释如下:

/*++

 

@func VOID | TouchPanelReadCalibrationPoint |

Initates the process of getting a calibration point.  This function

causes the device driver to foward the last valid x and y coordinates

between the tip states of initial down and up to the calibration callback

function. The tip state of the forwarded coordinates is reported as

initial down.

 

@parm LONG | UcalX |

The uncalibrated X coordinate under calibration.

@parm LONG | UcalY |

The uncalibrated Y coordinate under calibration.

 

@rdesc

If the function succeeds, TRUE; otherwise FALSE. Extended error information

is available via the GetLastError function.

 

 

--*/

BOOL

TouchPanelReadCalibrationPoint(

                               INT *pRawX,

                               INT *pRawY

                               )

{

    BOOL    retval;

    HANDLE  hevActivity = NULL;

    DWORD   dwStatus;

    HKEY    hk;

 

    NKDbgPrintfW(L"++TouchPanelReadCalibrationPoint()/r/n");

 

    if(!pRawX  || !pRawY ) {

        SetLastError(ERROR_INVALID_PARAMETER);       

        return ( FALSE ) ;

    }

 

    // Get a path to the GWES activity event.  We need to set it ourselves

    // because calibration is essentially a modal loop and doesn't put

    // events onto the user input queue, which is where this event gets

    // set inside GWES.

    // 从注册表项"System//GWE"下获取ActivityEvent的键值,它用来和GWES直接进行通信,不知道具体做些什么工作

    // 如果有人知道的话,欢迎告诉我

    dwStatus = RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"System//GWE", 0, 0, &hk);

    if(dwStatus == ERROR_SUCCESS) {

        WCHAR szEventPath[MAX_PATH];

        DWORD dwSize = sizeof(szEventPath);

        DWORD dwType;

 

        // Read the path to the event and open it -- do this on every call

        // to this API so that we're not forced to keep an event open that

        // is almost never used.

        dwStatus = RegQueryValueEx(hk, L"ActivityEvent", NULL, &dwType, (LPBYTE) szEventPath, &dwSize);

        NKDbgPrintfW(L"Event name: %s/r/n", szEventPath);

        szEventPath[MAX_PATH - 1] = 0;      // enforce null termination

        if(dwStatus == ERROR_SUCCESS && dwType == REG_SZ) {

            hevActivity = OpenEvent(EVENT_ALL_ACCESS, FALSE, szEventPath);

        }

        RegCloseKey(hk);

    }

 

    EnterCriticalSection( &csMutex );

 

    //

    // If a calibration is already active, error.

    //

 

    if ( CalibrationState )

    {

        SetLastError( ERROR_POSSIBLE_DEADLOCK );

        LeaveCriticalSection( &csMutex );

        if(hevActivity != NULL) {

            CloseHandle(hevActivity);

        }

        return ( FALSE );

    }

 

    //

    // Set sample count and active flag.

    // Wait for calibration to happen.

    // Update the memory with the x and y coordinates.

    // Clear active flag.

    // We be done.

    // 标志呀,啥也不说了,非常重要,控制IST

    CalibrationState = CalibrationWaiting;

 

    // let the IST know about the event

    // 时间呀

    ghevCalibrationActivity = hevActivity;

 

    LeaveCriticalSection( &csMutex );

 

    // 等待IST校准数据获取的结果,如果IST数据获取结束将会通过该Event通知该函数

    WaitForSingleObject( hCalibrationSampleAvailable, INFINITE );

    EnterCriticalSection( &csMutex );

 

    *pRawX = lCalibrationXCoord;

    *pRawY = lCalibrationYCoord;

 

    // 可以看到,该函数反馈给GWES的结果就是简单的False或者True

    retval = ( CalibrationState == CalibrationValid );

    CalibrationState = CalibrationInactive;

 

    // done with the event

    ghevCalibrationActivity = NULL;

 

    LeaveCriticalSection( &csMutex );

 

    // close the event handle

    CloseHandle(hevActivity);

 

    NKDbgPrintfW(L"--TouchPanelReadCalibrationPoint(x: 0x%8x, y: 0x%8x, retval= 0x%x)/r/n", *pRawX, *pRawY, retval);

 

    return retval;

}

         线程TouchPanelpISR比较复杂,主要用途是进行采样点坐标的采集,并在采样点数据采集完毕的时候通过Event hCalibrationSampleAvailable告知TouchPanelReadCalibrationPoint()。两个条件下该线程采集坐标数据成功:一是PDD函数DdsiTouchPanelGetPoint()采集有效数据连续满足( ABS(DeltaX) > CAL_DELTA_RESET )&& ( ABS(DeltaY) > CAL_DELTA_RESET )超过CAL_HOLD_STEADY_TIME,二是PDD函数DdsiTouchPanelGetPoint()采集的有效数据且满足( ABS(DeltaX) > CAL_DELTA_RESET )&& ( ABS(DeltaY) > CAL_DELTA_RESET )的个数超过MIN_CAL_COUNT

         如果在上述过程中,发现任意一次PDD函数DdsiTouchPanelGetPoint()采集的数据是无效的,则进行下一次循环,没啥影响。但是如果采集的有效数据不能够满足( ABS(DeltaX) > CAL_DELTA_RESET )&& ( ABS(DeltaY) > CAL_DELTA_RESET ),则一切推倒重来,舍弃之前所有的坐标采样数据。        

/*++

 

Autodoc Information:

 

@func ULONG | TouchPanelpISR |

This routine is the thread which handles touch panel events.

The event that this thread synchronizes on is signaled by the PDD based on

the sampling rate, typically 10ms.

 

@rdesc

Never returns.

 

--*/

static ULONG

TouchPanelpISR(

               PVOID   Reserved  //@parm Reserved, not used.

               )

{

    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;

 

    // Need to be all kmode so that we can write to shared memory.

    //

    SetKMode(TRUE);

 

    NKDbgPrintfW(L"TouchPanelpISR/r/n");

    while  ( !bTerminate )

    {

 

        // IST等待TimerTouch中断事件hTouchPanelEvent唤醒

        // gdwTouchIstTimeout = INFINITE

        WaitForSingleObject( hTouchPanelEvent, gdwTouchIstTimeout );

        EnterCriticalSection( &csMutex );

        DEBUGMSG(ZONE_THREAD, (TEXT("TCH_INTR/r/n")) );

 

        // Give the pdd the down state of the previous sample

        // CurrentDown:不用说了吧,就是记录Touch是否被按下,添加这个Flag的目的主要是为了配合Timer中断

        if ( CurrentDown )

            SampleFlags |= TouchSamplePreviousDownFlag;

        else

            SampleFlags &= ~TouchSamplePreviousDownFlag;

 

        // 废话少说,先得到触摸屏坐标点再说,同时在SampleFlags返回一些标记,包括坐标点是否有效等等

        DdsiTouchPanelGetPoint( &SampleFlags, &RawX, &RawY );    // Get the point info

 

            NKDbgPrintfW(L"(0) 0x%8x/r/n", SampleFlags);

        // 坐标点不合法,下次再说吧

        if ( SampleFlags & TouchSampleIgnore )

        {

            // do nothing, not a valid sample

            LeaveCriticalSection( &csMutex );

            continue;

        }

 

        if ( SampleFlags & TouchSampleValidFlag )

        {

            // Set the previous down state for our use, since the pdd may not

            // have preserved it.

            // 下面的四行代码和前面一段类似,为啥?微软怎么会这么傻呢?

            // 看了半天,原来是MDD层代码不信任PDD层的API DdsiTouchPanelGetPoint,担心该函数会将SampleFlags得值给Rewrite

            if ( CurrentDown )

                SampleFlags |= TouchSamplePreviousDownFlag;

            else

                SampleFlags &= ~TouchSamplePreviousDownFlag;

 

            CurrentDown = SampleFlags & TouchSampleDownFlag;

        }

 

        // 这里的CalibrationState是一个全局变量,当GWES调用TouchPanelReadCalibrationPoint获取用户的校准坐标的时候,会将其赋值为CalibrationWaiting

        // 所以,也只有在校准屏幕的时候程序才会走到这里

        if ( CalibrationState )

        {

            //

            // At this point we know that calibration is active.

            //

            // Typically, the user touches the panel then converges to the

            // displayed crosshair. When the tip state transitions to

            // the up state, we forward the last valid point to the callback

            // function.

            //

            DEBUGMSG(ZONE_SAMPLES, (TEXT("**** Calibration point (%d, %d), flags 0x%4.4X/r/n"),

                RawX, RawY, SampleFlags) );

 

            //  Skip if not valid.

            // 下面这三行代码纯属多余,必须砍掉,呵呵

            if ( !(SampleFlags & TouchSampleValidFlag) )

            {

                LeaveCriticalSection( &csMutex );

                continue;

            }

            NKDbgPrintfW(L"(1) 0x%8x/r/n", SampleFlags);

            //  Signal the Power Manager activity event if one has been set up

            // ghevCalibrationActivity是一个和TouchPanelReadCalibrationPoint进行通信的Event Handle

            // 该事件就是"System//GWE"下的"ActivityEvent"配置的那个事件,TouchPanelReadCalibrationPoint中会去创建该事件

            // 这种类似的操作也是GWES中应用比较多的一种操作

            if ( ghevCalibrationActivity != NULL)

            {

                // 我猜测GWES需要这个事件做一些操作,可能会需要用该事件去告之它ISR中已经知道了要校准屏幕,现在可以为GWES提供相关的服务了

                SetEvent(ghevCalibrationActivity);

            }

 

            //  Must see down transition.

            //  符合下面的条件其实就是:

            //  1. 校准屏幕第一次采集到有效数据;

            //  所以,第二次采集到有效数据的话,这里的条件就不符合了

            if ( (SampleFlags & (TouchSampleDownFlag|TouchSamplePreviousDownFlag)) ==

                TouchSampleDownFlag )

            {

                CalibrationState = CalibrationDown;

                //  fSetBase用来表示是否需要设置Base时间,一般的校准屏幕时间对每个采样点的校准是由时间限制的,如果超过时间的话,自动采样结束

                fSetBase = TRUE;

                //  变量CalibrationSampleCount用来记录采样点的个数

                CalibrationSampleCount = 0;

                //  fGotSample用来标记数据是否已经采集结束

                fGotSample = FALSE;

            }

 

            NKDbgPrintfW(L"(2) 0x%8x/r/n", SampleFlags);

            //  Only look at stuff if we saw a down transition.

            if ( (CalibrationState == CalibrationDown) && !fGotSample )

            {

                //  值得一说的就是SampleFlagsTouchSampleDownFlag位是从PDD层返回来的,而不是在IST里面进行赋值的

                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;

                    //  超过采样点采样时间,自动采样

                    //  意思就是说,如果你拿着笔针在一个采样点上点击超过.5s依然没有拿起来的话,采样自动结束

                    if ( (GetTickCount() - BaseTime) > CAL_HOLD_STEADY_TIME )

                    {

                        fGotSample = TRUE;

                    }

                    //  如果第二次采样点与第一次采样点的偏差超过CAL_DELTA_RESET的话,需要重新开始数据的采集

                    //  意思就是说,如果你在十字号上触摸<1.5后,在不抬起来的情况下移动笔针,则你将发现.5s后自动数据采集才结束

                    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

                {

                    NKDbgPrintfW(L"%d/%d/r/n", CalibrationSampleCount, MIN_CAL_COUNT);

                    //  They lifted the pen, see if we will accept coordinate.

                    //  当最后一次抬起笔针的时候,程序将会走到这里

                    //  针对最后笔针最后一次抬起,将会做两种处理:

                    //  1. 如果采样点个数已经>MIN_CAL_COUNT的话,结束当前采样点的采样,并通知校准线程

                    //  2. 如果采样点不足的话,则将开启下一次全新的采样过程,也即舍弃之前所有的采样点坐标

                    if ( CalibrationSampleCount >= MIN_CAL_COUNT )

                    {

                        fGotSample = TRUE;

                    }

                    else

                    {

                        CalibrationState = CalibrationWaiting;

                    }

                }

 

                if ( fGotSample )

                {

                    CalibrationState = CalibrationValid;

                    lCalibrationXCoord = CX;

                    lCalibrationYCoord = CY;

                    SetEvent(hCalibrationSampleAvailable);

                }

            }

            LeaveCriticalSection( &csMutex );

        }

        else

        {

            //  获取Callback函数指针,供后续调用

            //  Callback函数在TouchPanelEnable中由GWES传递

            pfnCallback = v_pfnPointCallback;

            if ( pfnCallback != NULL )

            {

                   //  这里由PDD层代码告诉MDD层,是否需要对触摸板坐标进行校准,如果屏幕的线性度很好,就可以不需要这个步骤

                if( SampleFlags & TouchSampleIsCalibratedFlag )

                {   // Sample already calibrated by PDD

                    CalX = RawX;

                    CalY = RawY;

                }

                else

                {   // Not previously calibrated, do it now.

                    TouchPanelCalibrateAPoint( RawX, RawY, &CalX, &CalY );

                    SampleFlags |= TouchSampleIsCalibratedFlag;

                }

 

                LeaveCriticalSection( &csMutex );

 

                // Bounds check this value

                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) );

           

                   //  采集到数据后,通过Callback函数告知GWES

                (pfnCallback)( SampleFlags, CalX, CalY);

            }

            else

            {

                LeaveCriticalSection( &csMutex );

            }

 

        }

    }

    ExitThread(1);

    return ( TRUE );

}

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/243286.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

BreadCrumb控件

BreadCrumb控件&#xff0c;如上图所示&#xff0c;即面包屑导航控件&#xff0c;类似于TreeCtrl&#xff0c;但不是一次显示所有的Item&#xff0c;VC 2010可以编译通过&#xff0c;稍微修改一下其他的也可以编译&#xff0c;源代码下载&#xff1a; http://download.csdn.net…

foreach数组循环结构体

foreach数组循环结构体 foreach循环遍历任何维数的数组 Systemverilog增加了foreach循环&#xff0c;它可用来对一维或多维数组中的元素进行迭代&#xff0c;而不必指定数组每个维度的宽度。foreach循环的自变量是数组名&#xff0c;它后面是方括号内用逗号隔开的循环变量列表…

Android简介

最近Android很火&#xff0c;小弟也想了解一下它的结构。跟CE或者Mobile比起来&#xff0c;它的结构是有点凌乱&#xff0c;也难怪&#xff0c;毕竟是基于别人的内核在上层开发了一些应用 ---------------------------------------------------------------------------------…

说不尽的刘恒

认识刘恒快三十年了&#xff0c;作为曾经的同事和他小说的责任编辑&#xff0c;我只写过他一篇文章&#xff0c;还是在二十多年前。有时候特别熟悉的人反而不知道从何写起&#xff0c;因为一想起往事&#xff0c;各种记忆像开闸的水一样涌满眼前&#xff0c;让人很难落笔。1985…

印前处理的“发动机”——RIP

对于许多印刷厂来说&#xff0c;数字印前技术仍是个谜&#xff0c;尤其是光栅处理器RIP。除了RIP之处&#xff0c;我们也总听说打印机的内置控制单元。其实&#xff0c;RIP与内置控制单元在本质上是一样的&#xff0c;但也有所不同。听说起来好像有些玄乎&#xff0c;下面我们来…

组合逻辑过程块

组合逻辑过程块 always_comb代表组合逻辑 always_comb过程块表示建立组合逻辑模型 always_comb if(!mode)y a b; elsey a - b;always_comb能推断出其敏感表 与通用always过程块不同&#xff0c;always_comb块的后面不需要指明敏感表。软件工具已经知道设计的意图是建立一个…

外行看Flash的存储原理

突然在网上看到别人两年前写的一篇关于nor和nand的好文章&#xff0c;做为csdn的合法公民&#xff0c;有必要转 一、存储数据的原理 两种闪存都是用三端器件作为存储单元&#xff0c;分别为源极、漏极和栅极&#xff0c;与场效应管的工作原理相同&#xff0c;主要是利用电场的…

数码印刷

数码印刷 目前&#xff0c;RIP已经变成了印前生产的核心问题。它影响到从色彩和文件管理到印刷的整个生产过程的方方面面。而且&#xff0c;像陷印和拼大版这些以前需要单独的应用程序处理的功能&#xff0c;现在也被加到了RIP中。    新的RIP产品和销售商有很多。象Agfa,Ha…

锁存逻辑过程块

锁存逻辑过程块 always_latch描述锁存逻辑 always_latch过程块表示过程块描述的是基于锁存器的逻辑。和always_comb一样&#xff0c;always_latch的敏感表示推断出来的。 always_latchif(enable) q < d;always_latch与always_comb语义相同 always_latch过程的语义规则与al…

一种User Mode下访问物理内存及Kernel Space的简单实现

一种User Mode下访问物理内存及Kernel Space的实现 一&#xff0e;背景 WinCE发展到6.0之后&#xff0c;内存结构和管理方法进行了完善。对应用程序影响比较大的有Virtual Memory Layout的变化&#xff0c;如每个进程的虚拟内存空间扩展为2GB。对驱动程序影响比较大的有Pointe…

什么是ICC曲线

什么是ICC曲线 很多人都问及ICC曲线&#xff0c;但又都很不了解&#xff0c;甚至有些人认为有了ICC曲线就能打印出很漂亮的图片&#xff0c;其实不然&#xff0c;下面就我所掌握的知识给大家做个通俗的理解.国际色彩协会The international Color Consortium简称ICC&#xff0c;…

时序逻辑过程块

时序逻辑过程块 always_ff描述时序逻辑 always_ff专用过程块表示设计的意图是描述可综合的时序逻辑。 always_ff&#xff08;posedge clock,negedge resetN&#xff09;if(!resetN) q <0;else q < d;always_ff过程块的敏感表必须明确列出。这样就可以根据敏感表的内容&…

快速计算整数的二进制表示法中1的个数

快速计算整数的二进制表示法中1的个数 题目&#xff1a;给定一个无符号32位整数x&#xff0c;求x的二进制表示法中含1的个数&#xff1f; 第一种算法&#xff1a; int OneCount(unsigned int x){ for(int count0; x>0; count) x&x-1;//把最后面的1变0 return …

对任务和函数的改进

对任务和函数的改进 1、任务和函数的隐式语句组 SystemVerilog会推断出begin…end SystemVerilog简化了任务和函数的定义&#xff0c;有多条语句时不在需要begin …end对多条语句进行打包。打包省略之后&#xff0c;任务或函数中的语句将会顺序执行&#xff0c;就像仍然在begi…

RAPI简单说明及Sample Code

RAPI简单说明及Sample Code 一&#xff0e;什么是RAPI RAPI用来通过ActiveSync来建立PC与Device的通信。通过RAPI可以实现PC 对Device的控制和同步。 二&#xff0e;使用说明 常用的RAPI包括File I/O的一些操作&#xff0c;以及获取系统信息的一些API。 在使用RAPI来建立PC与…

在C/C++代码中使用SSE等指令集的指令(1)介绍

http://blog.csdn.net/gengshenghong/article/details/7007100我们知道&#xff0c;在C/C代码中&#xff0c;可以插入汇编代码提高性能。现在的指令集有了很多的高级指令&#xff0c;如果我们希望使用这些高级指令来实现一些高效的算法&#xff0c;就可以在代码中嵌入汇编&…

递增和递减操作符

递增和递减操作符 和- -操作符 for(i 0;i < 31; i) begin... end先加与后加 通常原则是&#xff0c;对组合逻辑使用阻塞赋值&#xff0c;而对时序逻辑赋值使用非阻塞赋值。 注意&#xff1a;和--操作符是阻塞赋值i;//使用阻塞赋值对i进行递增 ii1;//使用阻塞赋值对i进行…

User Mode Driver Management介绍(一)

User Mode Driver介绍 Windows CE 6.0中引入了User Mode Driver的概念&#xff0c;可是无论是网上&#xff0c;还是各个芯片厂商提供的方案中&#xff0c;都很少提及这方面的内容。 本文以小郭对存储管理和User Mode Driver Host的理解为基础&#xff0c;结合具体的代码实现&am…

CString内存泄露

经常见到 unicode 设置不一致造成内存泄露。。。

赋值操作符

赋值操作符 和其他赋值操作符 out in;//将out和in相加并将结果赋值给out //out out in;注意&#xff1a;这些操作符都是阻塞赋值packdage definitions;typedef enum logic[2:0] {ADD,SUB,MULT,DIV,SL.SR} opcode_t;typedef enum logic{UNSIGNED,SIGNED}operand_type_t;typ…