Wince6.0 s3c6410触摸屏驱动分析

http://blog.sina.com.cn/s/blog_4b4b54da01012gbz.html

 

Wince6.0 s3c6410触摸屏驱动分析

 


 

 

一、wince 触摸屏驱动概述... 3

二、触摸屏驱动接口分析... 4

2.1  Windows CE触摸屏驱动工作过程... 4

2.2 触摸屏驱动程序的实现... 4

2.3四线电阻式触摸屏的工作原理... 4

三、wince 6.0 触摸屏驱动分析... 6

3.1  TouchPanelEnable() 函数分析... 6

3.2 TouchPanelpISR()函数分析... 8

3.3 DdsiTouchPanelGetPoint()函数分析... 13

3.5 TSP_GetXY()函数分析... 17

3.6 Touch_Pen_Filtering()函数分析... 19

3.7 触摸屏校准分析... 21

 


 

 

一、wince 触摸屏驱动概述

在Windows CE操作系统中触摸屏驱动是一种分层驱动。其驱动模型如下图所示。上层是模型设备驱动程序(Model Device Driver, MDD),下层是依赖平台的驱动程序(Platform Dependent Driver, PDD)。MDD通常无需修改直接使用,MDD链接PDD层并定义它希望调用的函数接口:设备驱动程序提供器接口(Device Driver Service Provider Interface, DDSI)。同时MDD把不同的函数集提供给操作系统,这些函数叫做设备驱动程序接口(Device Driver Interface, DDI),这部分为也就是我们通常驱动需要实现的部分。

 

 

 

 

 

 

 

 

 

二、触摸屏驱动接口分析

2.1  Windows CE触摸屏驱动工作过程

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。

 

 

2.2 触摸屏驱动程序的实现

Windows CE6.0触摸屏驱动程序采用中断方式对触摸笔的按下状态进行检测,如果检测到触摸笔按下将产生中断并触发一个事件通知一个工作线程开始采集数据。同时,驱动将打开一个硬件定时器,只要检测到触摸笔仍然在按下状态将定时触发同一个事件通知工作线程采集数据,直到触摸笔抬起后关闭该定时器,并重新检测按下状态。驱动中采用了触摸屏中断以及定时器中断2个中断源,不仅可以监控触摸笔按下和抬起状态,而且可以检测触摸笔按下时的拖动轨迹。

2.3四线电阻式触摸屏的工作原理

四线电阻式触摸屏的结构如下图,在玻璃或丙烯酸基板上覆盖有两层透平,均匀导电的ITO层,分别做为X电极和Y电极,它们之间由均匀排列的透明格点分开绝缘。其中下层的ITO与玻璃基板附着,上层的ITO附着在PET薄膜上。X电极和Y电极的正负端由“导电条”(图中黑色条形部分)分别从两端引出,且X电极和Y电极导电条的位置相互垂直。引出端X-,X+,Y-,Y+一共四条线,这就是四线电阻式触摸屏名称的由来。当有物体接触触摸屏表面并施以一定的压力时,上层的ITO导电层发生形变与下层ITO发生接触,该结构可以等效为相应的电路

计算触点的X,Y坐标分为如下两步:

1. 计算Y坐标,在Y+电极施加驱动电压Vdrive, Y-电极接地,X+做为引出端测量得到接触点的电压,由于ITO层均匀导电,触点电压与Vdrive电压之比等于触点Y坐标与屏高度之比。

2. 计算X坐标,在X+电极施加驱动电压Vdrive, X-电极接地,Y+做为引出端测量得到接触点的电压,由于ITO层均匀导电,触点电压与Vdrive电压之比等于触点X坐标与屏宽度之比。

测得的电压由ADC转化为触摸点的原始坐标(数值范围由所选用的A/D转换器位数决定)后,还要根据具体使用的液晶屏实际像素进行转换,转换后通过校准直接转化为屏幕上的坐标,供GWES使用

 

 

 

 


 

 

三、wince 6.0 触摸屏驱动分析

Windows CE触摸屏驱动程序采用中断方式对触摸笔的按下状态进行检测,如果检测到触摸笔按下将产生中断并触发一个事件通知一个工作线程开始采集数据。同时,驱动将打开一个硬件定时器,只要检测到触摸笔仍然在按下状态将定时触发同一个事件通知工作线程采集数据,直到触摸笔抬起后关闭该定时器,并重新检测按下状态。驱动中采用了触摸屏中断以及定时器中断两个中断源,不仅可以监控触摸笔按下和抬起状态,而且可以检测触摸笔按下时的拖动轨迹。

3.1  TouchPanelEnable() 函数分析

触摸屏驱动在初始化过程调用TouchPanelEnable函数使能触摸屏。该函数调用的DDSI函数为:DdsiTouchPanelEnable和DdsiTouchPanelDisable。该函数实现如下内容:

1) 创建事件hTouchPanelEvent和hCalibrationSampleAvailable。hTouchPanelEvent事件在正常状态下当有触摸笔按下或者按下后需要定时采集数据时被触发。而hCalibrationSampleAvailable事件在校准状态下当有校准数据输入时被触发;

2) 检查并初始化所需的中断gIntrTouch(触摸屏中断)和gIntrTouchChanged(定时器中断),并将中断gIntrTouch、gIntrTouchChanged关联到事件hTouchPanelEvent。当gIntrTouch,gIntrTouchChanged中断产生时将触发hTouchPanelEvent事件;

3) 创建一个ISR线程TouchPanelpISR。TouchPanelpISR用于等待和处理触摸屏事件hTouchPanelEvent,它是整个驱动程序中唯一的事件源。

TouchPanelEnable()实现 C:\WINCE600\PUBLIC\COMMON\OAK\DRIVERS\TOUCH目录下

代码如下:

 

BOOL

TouchPanelEnable(

    PFN_TOUCH_PANEL_CALLBACK    pfnCallback

    )

{

    BOOL    ReturnValue;

//创建hTouchPanelEvent 触摸屏事件和hCalibrationSampleAvaiable校准采样事件,确保设备被禁用,设置采样频率

    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;

//检查并初始化所需的中断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);

}

 

3.2 TouchPanelpISR()函数分析

TouchPanelpISR函数是实现触摸屏数据采集关键函数,它实现的内容为:

1) 等待循环,用于接收hTouchPanelEvent事件,并构成函数的主体;

2) 通过调用DdsiTouchPanelGetPoint函数获取当前触摸屏位置和状态信息;

3) 在获取有效数据且在校准状态下,收集并提交按下的位置信息;

4) 在正常状态下,校准数据,并检查校准后数据的有效性;

5) 最后调用由GWES传入的回调函数,提交位置信息和状态信息。

TouchPanelpISR()代码如下

// 中断服务函数,采样频率10ms

 

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.

    //

 

    while  ( !bTerminate )

    {

//等待触摸屏产生中断,用于接收hTouchPanelEvent事件,并构成函数的主体

        WaitForSingleObject( hTouchPanelEvent, gdwTouchIstTimeout );

        EnterCriticalSection( &csMutex );

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

 

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

        if ( CurrentDown )

            SampleFlags |= TouchSamplePreviousDownFlag;

        else

            SampleFlags &= ~TouchSamplePreviousDownFlag;

  //调用DdsiTouchPanelGetPoint函数获取当前触摸屏位置[RawX,RawY]和状态信息[SampleFlags]

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

        //忽略采集到的数据

        if ( SampleFlags & TouchSampleIgnore )

        {

            // do nothing, not a valid sample

            LeaveCriticalSection( &csMutex );

            continue;

        }

        if ( SampleFlags & TouchSampleValidFlag )

            {

// 之前有校准笔针按下时,会将SampleFlags的TouchSamplePreviousDownFlag
位置1

            if ( CurrentDown )

                SampleFlags |= TouchSamplePreviousDownFlag;

            else

                SampleFlags &= ~TouchSamplePreviousDownFlag;

// 当笔针有按下动作时,会将SampleFlags的TouchSampleDownFlag位置1.
// 这时CurrentDown的TouchSampleDownFlag位也会置1

            CurrentDown = SampleFlags & TouchSampleDownFlag;

            }

 

        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;

                }

 

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

            if ( ghevCalibrationActivity != NULL)

                {

                SetEvent(ghevCalibrationActivity); //通知校准事件

                }

 

//  Must see down transition.

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

                        TouchSampleDownFlag )

                {// 进入笔针校准状态

                CalibrationState = CalibrationDown;

                fSetBase = TRUE;

                CalibrationSampleCount = 0;

                fGotSample = FALSE;

                }

 

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

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

                {

                if ( SampleFlags & TouchSampleDownFlag )

                    {

                    long DeltaX, DeltaY;

//校准次数累加。需校准5次

                    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

                    {

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

                    if ( CalibrationSampleCount >= MIN_CAL_COUNT )

                        {

                        fGotSample = TRUE;

                        }

                    else

                        {

                        CalibrationState = CalibrationWaiting;

                        }

                    }

  //收集并提交校准按下的位置信息

                if ( fGotSample )

                    {

//保存校验数据到lCalibrationXCoord,lCalibrationYCoord

                    CalibrationState = CalibrationValid;

                    lCalibrationXCoord = CX;

                    lCalibrationYCoord = CY;

                    SetEvent(hCalibrationSampleAvailable);

                    }

                }

            LeaveCriticalSection( &csMutex );

        }

        else

        {//在正常状态下,校准数据,并检查校准后数据的有效性;

            pfnCallback = v_pfnPointCallback;

            if ( pfnCallback != NULL )

            { // XY坐标植已经校准过,直接读取采样的值。

                if( SampleFlags & TouchSampleIsCalibratedFlag )

                 // Sample already calibrated by PDD

                    CalX = RawX;

                    CalY = RawY;

                }

                else  // 之前没有校准屏幕,调用校准函数进行校准。这里只校准一个点.

                 // Not previously calibrated, do it now.

                    TouchPanelCalibrateAPoint( RawX, RawY, &CalX, &CalY ); //看看是如何校准的.不是要校准5个点吗?

// 校准完后将已经校准标志位置1,以后就不用校准了

// 为什么校准一个点就置校准标志位为1?

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

#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);  // 调用由GWES传入的回调函数,提交位置信息和状态信息

            }

            else

            {

                LeaveCriticalSection( &csMutex );

            }

 

        }

    }

    ExitThread(1);

    return ( TRUE );

}

3.3 DdsiTouchPanelGetPoint()函数分析

在触摸屏驱动程序中DdsiTouchPanelEnable、DdsiTouchPanelDisable和DdsiTouchPanelGetPoint三个DDSI接口函数是驱动实现的关键所在。

// 对触摸屏数据的采样,采样的数据以*pUncalX,*pUncalY形式回传

 

VOID

DdsiTouchPanelGetPoint(TOUCH_PANEL_SAMPLE_FLAGS *pTipState, INT *pUncalX, INT *pUncalY)

{

    static int PrevX=0;

    static int PrevY=0;

    int TmpX = 0;

    int TmpY = 0;

 

    TSPMSG((_T("[TSP] ++DdsiTouchPanelGetPoint()\r\n")));

 // 处理触摸屏中断.触摸笔按下时产生触摸屏中断gIntrTouch时触发

    if (g_pVIC1Reg->VICRAWINTR & (1<<(PHYIRQ_PENDN-VIC1_BIT_OFFSET)))        // gIntrTouch Interrupt Case

    {

        TSPMSG((_T("[TSP] gIntrTouch(PHYIRQ_PENDN) Case\r\n")));

 

        *pTipState = TouchSampleValidFlag;

 

        if ((g_pADCReg->ADCDAT0 & D_UPDOWN_UP)

            || (g_pADCReg->ADCDAT1 & D_UPDOWN_UP)) //笔针抬起

        {

            TSPMSG((_T("[TSP] Pen Up\r\n")));

 

            g_bTSP_DownFlag = FALSE;

 

            g_pADCReg->ADCTSC = ADCTSC_WAIT_PENDOWN;

 

            *pUncalX = PrevX;

            *pUncalY = PrevY;

 

            TSP_SampleStop();

        }

        else//笔针按下状态

        {

            TSPMSG((_T("[TSP] Pen Down\r\n")));

 

            g_bTSP_DownFlag = TRUE;

 

            g_pADCReg->ADCTSC = ADCTSC_WAIT_PENUP;

 

            *pTipState |= TouchSampleIgnore;

 

            *pUncalX = PrevX;

            *pUncalY = PrevY;

 

            *pTipState |= TouchSampleDownFlag;

//笔针按下后会开启定时器

            TSP_SampleStart();

        }

 

        g_pADCReg->ADCCLRWK = CLEAR_ADCWK_INT;

 // 通知触摸中断已经完成

        InterruptDone(gIntrTouch);        // Not handled in MDD

}

// 当触摸屏中断产生时,同时会打开一个定时器,当笔针未松开时定时触发同一个事件通知这个工作线程继续采集数据,直到触摸笔抬起后关闭该定时器。故上面的if虽然执行了,也会继续执行后面的else。处理定时器中断.触摸笔按下后,定时器被打开,定时器将定时产生中断gIntrTouchChanged,并触发事件,直到触摸笔抬起为止。

    else// if (g_pVIC0Reg->VICRAWINTR & (1<<(PHYIRQ_TIMER3)))        // gIntrTouchTimer Interrupt Case

    {

        //TSPMSG((_T("[TSP] gIntrTouchChanged(PHYIRQ_TIMER3) Case\r\n")));

// 在开启定时器后检测到笔针已经抬起

        // Check for Pen-Up case on the event of timer3 interrupt

        if ((g_pADCReg->ADCDAT0 & D_UPDOWN_UP)

            || (g_pADCReg->ADCDAT1 & D_UPDOWN_UP))

        {

            TSPMSG((_T("[TSP] Pen Up +\r\n")));

 

            g_bTSP_DownFlag = FALSE;

 

            g_pADCReg->ADCTSC = ADCTSC_WAIT_PENDOWN;

  // 将采样的笔针坐标存放起来

            *pUncalX = PrevX;

            *pUncalY = PrevY;

 

            *pTipState = TouchSampleValidFlag;

 

            TSP_SampleStop();//停止定时器中断

        }

        else if (g_bTSP_DownFlag)        {

            if (TSP_GetXY(&TmpX, &TmpY) == TRUE) //采集触摸笔按下的位置.一次采样8组数据,去掉最大两个,最小一个,处于中间大小的两个乘以2变为8个后采样数据除8,得平均值

            {

                //TSP_TransXY(&TmpX, &TmpY);

 

                if(Touch_Pen_Filtering(&TmpX, &TmpY)) ) // 过滤采集的数据.这里通过定时器实现3次数据采集

                {

                    *pTipState = TouchSampleValidFlag | TouchSampleDownFlag;

                    *pTipState &= ~TouchSampleIgnore; //数据过滤成功,清掉TouchSampleIgnore标识(标志)

                }

                else        // Invalid touch pen

                {

                    *pTipState = TouchSampleValidFlag;

                    *pTipState |= TouchSampleIgnore;

                }

   // 将采样的笔针坐标存放起来

                *pUncalX = PrevX = TmpX;

                *pUncalY = PrevY = TmpY;

 

                g_pADCReg->ADCTSC = ADCTSC_WAIT_PENUP;

 

                //TSPMSG((_T("[TSP] UncalX = %d, UncalY = %d\r\n"), *pUncalX,*pUncalY));

            }

            else

            {

                *pTipState = TouchSampleIgnore;

            }

        }

        else

        {

            TSPERR((_T("[TSP] Unknown State\r\n")));

 

            *pTipState = TouchSampleIgnore;

            TSP_SampleStop();

        }

 // 清除定时器3中断位

        // timer3 interrupt status clear

        //g_pPWMReg->TINT_CSTAT |= (1<<8);        // Do not use OR/AND operation on TINTC_CSTAT

        g_pPWMReg->TINT_CSTAT = TINT_CSTAT_INTMASK(g_pPWMReg->TINT_CSTAT) | TIMER3_PENDING_CLEAR;

// 结束定时器3中断

        InterruptDone(gIntrTouchChanged);        // Not Handled in MDD

    }

 

    //TSPMSG((_T("[TSP] --DdsiTouchPanelGetPoint()\r\n")));

}

 

这段代码的分析,要通过两个中断源以及事件一起理解。 

我们先看MDD层是如何调用这个函数的。

DdsiTouchPanelGetPoint( &SampleFlags, &RawX, &RawY ); 

在MDD层中,通过上面的调用方式,从DdsiTouchPanelGetPoint()函数中回传了三个参数,第一个用于表明触摸正处于的状态,RawX,RawY是调用的函数从TP触摸点取样得到的点的数据,这里的数据还是原始的ADC采样的数据,即直接从ADC转换的寄存器里面得到的,并没有对该数据进行处理。

先看看SampleFlags这个变量,一起有如下几种状态:

TouchSampleValidFlag   = 0x01,  //@EMEM The sample is valid.

 TouchSampleDownFlag    = 0x02,  //@EMEM The finger/stylus is down.

 TouchSampleIsCalibratedFlag  = 0x04,  //@EMEM The XY data has already been calibrated.

 TouchSamplePreviousDownFlag  = 0x08,  //@EMEM The state of the previous valid sample.

 TouchSampleIgnore    = 0x10,     //@EMEM Ignore this sample.

 TouchSampleMouse    = 0x40000000 // reserved

TouchSampleValidFlag标志位表示已经获得TP采样事件,准备开始采样;

TouchSampleDownFlag标志位表示检测到TP已经按下去;

TouchSampleIsCalibratedFlag标志位表示TP已经校准,没有必要再校准了;

TouchSamplePreviousDownFlag标志位是用于校准计数用的,每校准一次要采样五组数据;

TouchSampleIgnore标志位表示触摸失败,采样数据无效;

  3.5 TSP_GetXY()函数分析

在这里,第一个大的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;

 

    EnterCriticalSection(&g_csTouchADC);

 //一起采样8次坐标值

    for (i = 0; i < TSP_SAMPLE_NUM; i++)

    {

        g_pADCReg->ADCTSC = ADCTSC_AUTO_ADC;    // Auto Conversion

        g_pADCReg->ADCCON |= ENABLE_START_EN;    // ADC Conversion Start

 

        while (g_pADCReg->ADCCON & ENABLE_START_EN)

          // Wait for Start Bit Cleared

            Sleep(1);

        }

 

        while (!(g_pADCReg->ADCCON & ECFLG_END))

          // Wait for ADC Conversion Ended

            Sleep(1);

        }

// 从ADCDAT0,ADCDAT1中读取转换的数据

        x[i] = D_XPDATA_MASK(g_pADCReg->ADCDAT0);

        y[i] = D_YPDATA_MASK(g_pADCReg->ADCDAT1);

    }

 

    LeaveCriticalSection(&g_csTouchADC);

// x[i],y[i]从小到大排序

    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

// 8 samples Interpolation (weighted 4 samples)

 // 8次采样时最小一位,最大两位为无效数据

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

// 求8次采样的平均值

    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

// 2 samples average

//求两次采样的平均值

    *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

 

// 有效值的最大偏差大于40时,返回失败

    if ((dx > TSP_INVALIDLIMIT) || (dy > TSP_INVALIDLIMIT))

    {

        return FALSE;

    }

    else

    {

        return TRUE;

    }

}

 

这里用到了一组简单的数学算法,详细见程序的注释。ADC采样一起采样8次,舍掉最小值,最大值,再加权求平均,最终得到一组TP采样值。

3.6 Touch_Pen_Filtering()函数分析

  采样的数据会继续过滤一次,通过调用Touch_Pen_Filtering(&TmpX, &TmpY)函数实现。具体代码如下:

static BOOL

Touch_Pen_Filtering(INT *px, INT *py)

{

#ifdef    NEW_FILTER_SCHEME    // New Scheme from PJR

    BOOL RetVal = TRUE;

    // TRUE  : Valid pen sample

    // FALSE : Invalid pen sample

    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) // 只采样两组数据,两次后开始下面的Filter算法

    {

        // apply filtering rule

        count = 2;

 // 第一组数据和第三组数据取平均

        // average between x,y[0] and *px,y

        TmpX = (x[0] + *px)>>1;

        TmpY = (y[0] + *py)>>1;

 // 求出第二组数据与第一,三组数据的平均值的差值

        // difference between x,y[1] and TmpX,Y

        dx = (x[1] > TmpX) ? (x[1] - TmpX) : (TmpX - x[1]);

        dy = (y[1] > TmpY) ? (y[1] - TmpY) : (TmpY - y[1]);

// Filter_Margin = 第一,二组数据的X,Y坐标的差值 + TSP_FILTER_LIMIT(3)

        Filter_Margin = (x[1] > x[0]) ? (x[1]-x[0]) : (x[0]-x[1]); // 求出第一组数据与第二组数据的X坐标的差值

        Filter_Margin += (y[1] > y[0]) ? (y[1]-y[0]) : (y[0]-y[1]);

//求出第一组数据与第二组数据的Y坐标的差值并累加到Filter_Margin

        Filter_Margin += TSP_FILTER_LIMIT;

 // 如果dx,dy大于Filter_Margin,则保存上一组采样数据[第二组],并返回失败

    // 这里可以拓展实现触摸屏的滑动

        if ((dx > Filter_Margin) || (dy > Filter_Margin)) {

            // Invalid pen sample

            *px = x[1];

            *py = y[1]; // previous valid sample

            RetVal = FALSE;

            count = 0;

        }

        else

        {//否则保留后两组数据到x[0],y[0]以及x[1],y[1].程序执行到这里,笔针采样数据才有效

            // Valid pen sample

            x[0] = x[1]; y[0] = y[1];

            x[1] = *px; y[1] = *py; // reserve pen samples

 

            RetVal = TRUE;

        }

    }

    else // (count > 2)

{ // till 2 samples, no filtering rule

// 保存前两组采样得到的数据。

  // 第一组采样的数据放到x[0],y[0]

  // 第二组采样的数据放到x[1],y[1]

        x[0] = x[1]; y[0] = y[1];

        x[1] = *px; y[1] = *py; // reserve pen samples

 

        RetVal = FALSE;    // <- TRUE jylee 2003.03.04

    }

 

    return RetVal;

#endif

}

看到这里也许会觉得奇怪,为什么会是count>2呢?

  这里是联合定时器3的定时中断实现。只有当采样了两组数据之后,该函数才会返回真,否则采样的数据是不作数的。实际上是一起采样了三组数据,具体算法见程序中注释。经过过滤后的数据再回传给MDD,然后提交。

   这里提交的数据其实仍然是直接从ADCDAT寄存器中读出的数据,并没有做相应处理。由于TP本身的非线性,程序为了兼容各种不同的非线性TP,微软通过一组数学算法将这个很复杂的问题轻而易举的通过程序解决了。

3.7 触摸屏校准分析

触摸屏的校准过程如下:

⑴ 调用TouchPanelEnable函数来启动触摸屏采样。

⑵ 调用TouchPanelGetDeviceCaps函数来获得校准触点的个数。

⑶ 去校准点,对于每一个校准点都有一下过程:

I:调用TouchPanelGetDeviceCaps函数来得到校准点的坐标。

II:在上述校准点处画十字,等待用户点击.

III:用户点击十字图形后,调用TouchPanelReadCalibrationPoint函数来获得上述校准点处触摸屏对应的坐标。

⑷ 当取完所需用的校准点,调用TouchPanelSetCalibration函数来计算校准系数A1,B1,C1和A2,B2,C2。

 

内容概要:本文研究了基于有限控制集模型预测控制(FCS-MPC)的三相并网逆变器双模态调控策略,深入探讨了电流与功率双模式预测控制之间的等效机理及其性能边界。通过Simulink仿真平台与Matlab编程实现,构建了一个融合电流预测和功率预测的闭环控制系统,旨在提升逆变器在复杂电网环境下的动态响应能力、电能质量和并网稳定性。文章系统阐述了FCS-MPC的基本原理及其在三相并网系统中的应用,提出了一种兼顾稳态精度与动态抗扰性的双模态控制架构,并通过多工况仿真验证了该策略在抑制电流畸变、实现功率无差拍响应等方面的优越性能,揭示了其在高渗透率新能源系统中稳定并网的应用潜力。; 适合人群:具备一定电力电子与自动控制理论基础,从事新能源发电、微电网控制、电力系统仿真等相关领域的科研人员及工程技术人员,尤其适合研究生及以上学历或工作1-3年的研发人员; 使用场景及目标:①用于研究三相并网逆变器在电网不平衡、电压波动等非理想条件下的高性能控制策略;②为实现高渗透率新能源系统的稳定并网提供技术参考与仿真验证手段;③支持学术论文复现、课题研究及工程项目前期技术探索; 阅读建议:建议结合提供的Simulink模型与Matlab代码进行同步仿真操作,深入理解双模态预测控制的设计逻辑与参数整定方法,重点关注不同工况下的系统响应特性,以掌握其在实际应用中的优势与局限性。
内容概要:本文聚焦电网故障下分布式能源系统的多目标无功优化问题,以并网转换器(GCC)为核心,提出并实现了基于Matlab/Simulink的高性能控制策略仿真方案。研究采用有源中点箝位(ANPC)三电平逆变器拓扑,结合双极性倍频脉宽调制(DPWMA)、正负序分离锁相环与电网电压前馈控制,构建一体化控制体系,旨在提升系统在电网电压不平衡、对称跌落及动态扰动等复杂工况下的并网电能质量、动态响应速度与运行稳定性。通过多场景仿真验证,该方案能有效抑制谐波、稳定中点电位、实现对称并网电流与平滑功率输出,尤其在电网不平衡和动态切换条件下展现出卓越的抗扰能力和快速恢复特性,为高比例新能源并网提供了可靠的技术路径。; 适合人群:具备电力电子、自动控制或新能源并网等相关专业背景,从事电力系统仿真研究、攻读硕士及以上学位或从事新能源并网技术研发的工程技术人员。; 使用场景及目标:①深入研究高比例新能源接入背景下并网逆变器在电网故障时的无功支撑与稳定控制机制;②掌握ANPC三电平拓扑与先进调制、锁相、前馈控制技术的协同设计方法;③通过Matlab/Simulink搭建复杂电力系统仿真模型,服务于科研项目开发、高水平论文复现或工程化方案验证。; 阅读建议:建议结合文中提供的完整仿真资源与参考文献,按照目录结构系统学习,重点关注控制策略的设计原理、模块实现细节与仿真结果对比分析,动手实践仿真模型以深入理解各子系统间的耦合关系及整体性能表现。
内容概要:本文针对高渗透率电动汽车随机充电行为对配电网承载能力的影响开展系统性研究,深入分析了大规模电动汽车无序接入导致的配电网脆弱性问题,构建了涵盖电动汽车充电负荷、分布式电源及电网运行约束的综合仿真模型,并基于Matlab平台进行多场景仿真。研究采用多维度指标体系评估不同渗透率下配电网的安全性、电能质量和运行效率,结合熵权法与模糊综合评价方法实现承载能力的量化评分,进一步提出广义需求响应协同优化策略,通过引导用户充电行为以缓解负荷压力、改善系统性能,提升配电网韧性与适应性。研究成果为高比例电动汽车接入背景下的电网规划、运行调控及基础设施建设提供了理论支撑与决策依据。; 适合人群:具备电力系统、电气工程或相关领域专业知识,熟悉Matlab仿真环境,从事新能源并网、智能配电网优化、电动汽车与电网互动(V2G)、需求响应等领域研究的研究生、科研人员及工程技术人员。; 使用场景及目标:①评估高比例电动汽车接入对配电网电压偏差、线路负载率、变压器容量等关键设备运行状态的影响;②设计并验证广义需求响应策略在平抑负荷波动、降低网损、提升电能质量与系统承载能力方面的有效性;③为新型电力系统中充电设施规划、有序充电管理及电网升级改造提供科学依据和技术支持。; 阅读建议:建议结合文中提供的Matlab代码进行仿真实践,重点关注电动汽车充电模型的随机性建模、多指标评价体系的构建逻辑以及需求响应优化机制的实现过程,可进一步拓展至V2G双向互动、可再生能源协同调度等应用场景进行深化研究。
内容概要:本文围绕有源中点箝位(ANPC)三电平并网逆变器,提出一套融合双极性倍频脉宽调制(DPWMA)、正负序分离锁相及电网电压前馈控制的复合控制策略,旨在解决传统逆变器在谐波抑制、电网不平衡适应性及动态响应方面的不足。文章首先深入分析ANPC三电平拓扑在开关损耗均衡、中点电位稳定和低谐波输出等方面的硬件优势,继而系统阐述DPWMA调制如何通过等效倍频效应提升开关频率以优化波形质量,正负序分离锁相如何在电网不平衡工况下实现精准同步,以及电网电压前馈控制如何通过扰动预补偿机制提升系统的动态抗扰能力。通过构建“精准同步-扰动补偿-优质调制”的三层协同控制架构,并在Simulink中搭建完整的仿真模型,全面验证了该策略在稳态运行、电网电压不平衡及动态扰动等多种复杂工况下的卓越性能。结果表明,该复合策略能显著降低系统谐波含量,确保并网电流高度对称,提升动态响应速度,有效兼顾了逆变器的稳态电能质量、工况适应性与运行稳定性,具备突出的工程应用价值与广阔的推广前景。; 适合人群:具备电力电子、自动控制或电气工程相关背景,从事新能源并网、逆变器控制、电能质量研究的研究生、科研人员及工程技术人员。; 使用场景及目标:①研究高性能三电平并网逆变器的控制策略设计;②解决电网电压不平衡、动态扰动下的并网稳定性问题;③提升大功率逆变系统的电能质量和动态响应能力。; 阅读建议:建议结合Simulink仿真模型,深入理解DPWMA调制、正负序分离与前馈控制的实现细节,并通过改变工况参数对比传统控制策略,以充分掌握该复合控制方法的优势与适用边界。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值