wince 触摸屏 学习

Microsoft Windows CE .NET 中的中断体系结构

winceinterruptarch_fig1

图 1.

在调用了单个 ISR 或其相关 ISR 链之后,返回值可能为下列值之一:

返回值

操作

SYSINTR_NOP

中断不与设备的任何已注册 ISR 关联。内核启用所有其他中断。

SYSINTR

中断与已知的已注册 ISR 和设备关联。

SYSINTR_RESCHED

中断是由请求 OS 重新调度的计时器到期引起的。

SYSINTR 返回值是我们讨论的重点。一旦 ISR 完成,内核将重新启

ISR 返回下列值之一:

  • SYSINTR_NOP — 没有任何 ISR 包含该中断

  • SYSINTR_RESCHED — 重新调度计时器已到期

  • SYSINTR — ISR 已经包含该中断

  • SYSINTR_RTC_ALARM — 闹钟已到期

  • 触摸屏原理

S3C2440A内置一个带8个模拟输入通道的10位逐次逼近型(recycling type)CMOS模数转换器。在2.5MHz的模数转换时钟频率下,转换速率可达到500KSPS(Kilo Samples Per Second),并且支持片内采样保持功能和省电模式。S3C2440A还带有触摸屏接口,可以控制/选择触摸屏的XP,XM,YP,YM输入以进行X,Y位置转换。

AD转换器频率=GCLK/(p+1);

AD转换时间=1/(AD转换器频率/5)=5*(p+1)/GCLK;

GCLK是系统主时钟频率,一般为50Mhz,p(0~255)是预分频值,除以5表示每次转换需要5个时钟周期。AD转换器的设计最大时钟频率为2.5MHz,在系统时钟为50Mhz、AD转换器时钟频率为2.5Mhz时,p最大为19;最大转换频率为0.5MHz,所以最大转换速率为0.5M个采样每秒,即500KSPS。

触摸屏有四种转换模式:

  • 普通转换模式:与普通AD转换器的使用一样,通过设置ADCCON来初始化,并以一个读和写ADCDAT0的操作完成。
  • X/Y分别转换模式:分为X位置转换模式和Y位置转换模式,这两种模式下,触摸屏分别把X、Y位置转换数据写入到ADCDAT0和ADCDAT1中之后,向中断控制器发起中断请求。
  • 自动X/Y位置转换:触摸屏依次转换X和Y位置,把转换的结果分别写入到ADCCON0和ADCDAT1中,然后向中断控制器发起中断请求。
  • 等待中断模式:设置ADCTSC为0xD3,当触控笔按下时,想中断控制器发起中断请求。本篇文章主要讲等待中断模式。

一 硬件部分


硬件上的原理不是本文的重点,只讲一下大概的原理(主要是我也只知道大概的原理, 毕竟咱不是搞硬件的. 嘻嘻!)


我移植用的这个屏是320*240 的TFT屏, 四线电阻式触屏. 这种触屏的原理是由两个电阻层组成, 一个实现X位置的测量,一个用于Y位置上的测量. 简单来说,就是当用触笔按下屏幕时,两个电阻层接触, 电阻发生变化,然后在X Y方向上产生信号, 这个信号是电压信号, 再经过CPU内部分AD转换为坐标值. 这个原理有点像高中物理课用的滑动电阻,有一个最大上限,滑动到不同的地方,阻值不同. 2410本身集成了touch的控制器,通过简单的配置和读取相关的寄存器,就可以实现触摸屏的操作. 


二 驱动部分

Wince下的touch驱动跟很多其它的驱动一样, 是分层的, 有MDD 和PDD两层. MDD层被系统隐藏起来, 一般不用我们来修改. 而我们真正关心的是PDD 层. 也就是要由开发者来修改的这一层. 


分析touch驱动时,以我最近刚刚移植到一个基于2410的板子上的6.0的BSP包的触屏驱动为例.到C:\WINCE600\PLATFORM\DEVICEEMULATOR\SRC\DRIVERS\TOUCH下. 找到s3c2410x_touch.cpp文件. 这里面正是PDD层的实现代码. 容易发现这里面的函数分为两类,一类是以TSP开头的函数,一类是以DDSI开头的函数. TSP开头的函数为内部私有的函数,是被DDSI调用的, 而DDSI开头的函数则是对外的接口, 也就是被MDD层的函数调用的接口. 


DdsiTouchPanelEnable是首先被调用的一个外部接口, 它的实现可参见源程序,  它主

要做了下面几个事情:

1 通过调用TSP_VirtualAlloc函数为驱动所用的IO,中断等硬件中断分配内存空间. 


2 通过调用KernelIoControl向系统申请两个中断,如果申请成功,赋予相应的逻辑中断号. KernelIoControl向底层是调用OEMIoControl函数, OEMIoControl根据KernelIoControl传进来的IOCTL代码,做相应的操作,比如这里, IOCTL是IOCTL_HAL_REQUEST_SYSINTR, 它是向内核申请一个物理中断和逻辑中断的映射. 


3 通过调用TSP_PowerOn来初始化中断控制器,ADC寄存器,定时器等, 在TSP_PowerOn的实现中,有几点要说明一下:

ADCDLY 这个值在不同的模式下意义不同, 因为前面通过ADCTSC已经配置为wait for interrupt mode, 所以这个值的意义和你的触笔按下时,  从产生中断信号到开始自动转换X,Y时的时间间隔是相关的,它的单位是ms


v_pPWMregs->TCNTB3  = g_timer3_sampleticks

TCNTB3是timer3的count buffer, 当定时器启动时, 0,这个值以一个设置好的频率递减,直到减到0, 这时会产生一个定时器中断. 这个有什么用呢. 要理解它,得知道触摸屏在中断模式下是如何工作的. 


当我们按下的触摸屏时,会产生一个ADC的中断, 同时我们的驱动还会启动一个定时器, 这个定时器触发一个事件做数据采集, 在我们的手或触笔抬起来前,这个定时器不断的触发采集事件,直到它被关闭, 而它什么时候会被关闭呢,就是在触笔的抬起来时. 下面截取的代码很好的说明的这个原理:

if ( (v_pADCregs->ADCDAT0 & (1 << 15)) |(v_pADCregs->ADCDAT1 & (1 << 15)) )

{

bTSP_DownFlag = FALSE;

DEBUGMSG(ZONE_TIPSTATE, (TEXT("up\r\n")));

v_pADCregs->ADCTSC &= 0xff;

        *pUncalX = x;

*pUncalY = y;

TSP_SampleStop();

            ……

}

上面的代码,if判断的正是是否抬起. 

而g_timer3_sampleticks的值是这样计算出来的. 

g_timer3_freq        = (g_s3c2410_pclk / TIMER3_DIVIDER);

g_timer3_sampleticks = (g_timer3_freq / TSP_SAMPLE_RATE_LOW); 

TIMER3_DIVIDER 的值是2, TSP_SAMPLE_RATE_LOW的值是100, 由

v_pPWMregs->TCFG1  &= ~(0xf << 12);  

v_pPWMregs->TCFG1  |=  (0   << 12); 

可知定时器1/2分频, 所以,很容易计算出,所设置的定时器是每10ms产生一次定时器中断

而触摸屏中断是在你按下和抬起时产生的. 


DdsiTouchPanelGetPoint是采样的主要实现函数,当MDD检测到中断事件发生时,该函数会被调用. 触摸屏的中断是SYSINTR_TOUCH, 而定时器的中断是SYSINTR_TOUCH_CHANGED 

该函数用if else分别处理两种中断, 如下:

if (v_pINTregs->SUBSRCPND & (1<<IRQ_SUB_TC))      /* 触摸屏中断*/

{

    ……

}


else        /*定时器中断 */

{

}

DdsiTouchPanelGetPoint函数的实现代码中,调用了两个很重要的函数TSP_TransXY和TSP_GetXY

需要说明的是,这两个函数的实现跟LCD本身的分辨率是没有关系的.

TSP_GetXY用来获到AD采样值,TSP_TransXY把它转化为屏上的坐标. 我移植touch驱动时,遇到过点屏幕上面,下面有反应,或者点左上角,右上角有反应等类似的问题, 都是因为这两个函数没实现好.


先来看TSP_GetXY函数.它的实现如下:

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*/


y = (0x3ff & v_pADCregs->ADCDAT1);

x = (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);

}


关于这个函数有几点要说明. 

根据2410的手册, ADCDAT0 保存是X方向上采样的结果,  ADCDAT1 保存是Y方向上采样的结果,  所以, 我们看到下面的两行代码

y = (0x3ff & v_pADCregs->ADCDAT1);

x = (0x3ff & v_pADCregs->ADCDAT0);

与上0x3ff, 是因为, ADCDAT寄存器只用了前面 10位来保存AD采样的结果, 而这和2410内部的AD模块只有10位精度是相一致的.所以,AD转换后的最大值不会超过1024-1. 

当然上在那种计算方法并不是绝对的 , 根据硬件构造的不同, 比如有可能你x方向的坐标值和采样值成反比,就要按下面的方式计算:

x = 0x3ff - (0x3ff & v_pADCregs->ADCDAT0);


再看TSP_TransXY函数. 我移植的版本的实现如下:

PRIVATE void

TSP_TransXY(INT *px, INT *py)

{

*px = (*px >= TSP_MAXX) ? (TSP_MAXX) : *px;

*py = (*py >= TSP_MAXY) ? (TSP_MAXY) : *py;


*px = (*px - TSP_MINX);

*py = (*py - TSP_MINY);


*px = (*px >= 0) ? *px : 0;

*py = (*py >= 0) ? *py : 0;


*px = *px * TSP_LCDY / (TSP_MAXX - TSP_MINX);

*py = *py * TSP_LCDX / (TSP_MAXY - TSP_MINY);


*px = (*px >= TSP_LCDY)? (TSP_LCDY - 1) : *px;

*py = (*py >= TSP_LCDX)? (TSP_LCDX - 1) : *py;


*px = TSP_LCDY - *px - 1;

*py = TSP_LCDX - *py - 1;


}

这个实现是我在模拟器的实现代码基础上修改的. 这个函数计算X,Y的坐标用的是一个公式,至于这个公式是怎么来的,我就不太清楚了. 只说明一点.

#define TSP_MINX 88

#define TSP_MINY 84


#define TSP_MAXX 952

#define TSP_MAXY 996

上面四个值是定义X+, X-, Y+, Y-四个有效的采样值, 理论上应该是0和1023(10 bit ADC), 但实际肯定有偏差,准确来讲, 换了不同的硬件平台,这四个值应该是要重新测过的. 我就直接沿用原BSP中的值了. 

 

 

从基于S3C6410的Touch驱动详解(之一)可以看到,在触摸屏驱动程序中DdsiTouchPanelEnableDdsiTouchPanelDisableDdsiTouchPanelGetPoint三个DDSI接口函数是驱动实现的关键所在。

[cpp]  
  1. // 对触摸屏数据的采样,采样的数据以*pUncalX,*pUncalY形式回传   
  2. VOID  
  3. DdsiTouchPanelGetPoint(TOUCH_PANEL_SAMPLE_FLAGS *pTipState, INT *pUncalX, INT *pUncalY)  
  4.  
  5.     static int PrevX=0;  
  6.     static int PrevY=0;  
  7.     int TmpX 0;  
  8.     int TmpY 0;  
  9.   
  10.     TSPMSG((_T("[TSP] ++DdsiTouchPanelGetPoint()/r/n")));  
  11.   
  12.     // 处理触摸屏中断.触摸笔按下时产生触摸屏中断gIntrTouch时触发   
  13.     if (g_pVIC1Reg->VICRAWINTR (1<<(PHYIRQ_PENDN-VIC1_BIT_OFFSET)))        // gIntrTouch Interrupt Case   
  14.      
  15.         TSPMSG((_T("[TSP] gIntrTouch(PHYIRQ_PENDN) Case/r/n")));  
  16.   
  17.         *pTipState TouchSampleValidFlag;  
  18.         //笔针没有按下   
  19.         if ((g_pADCReg->ADCDAT0 D_UPDOWN_UP)  
  20.         || (g_pADCReg->ADCDAT1 D_UPDOWN_UP))  
  21.          
  22.             TSPMSG((_T("[TSP] Pen Up/r/n")));  
  23.   
  24.             g_bTSP_DownFlag FALSE;  
  25.   
  26.             g_pADCReg->ADCTSC ADCTSC_WAIT_PENDOWN;  
  27.   
  28.             *pUncalX PrevX;  
  29.             *pUncalY PrevY;  
  30.   
  31.             TSP_SampleStop();  
  32.          
  33.         else//笔针按下状态    
  34.          
  35.             TSPMSG((_T("[TSP] Pen Down/r/n")));  
  36.   
  37.             g_bTSP_DownFlag TRUE;//设置笔针按下去状态为真   
  38.   
  39.             g_pADCReg->ADCTSC ADCTSC_WAIT_PENUP;//ADCTSC设置为等待抬起模式   
  40.   
  41.             *pTipState |= TouchSampleIgnore;  
  42.   
  43.             *pUncalX PrevX;  
  44.             *pUncalY PrevY;  
  45.             //added by lqm.must be recovered later.   
  46.             //RETAILMSG(1,(_T("[gIntrTouch] *pUncalX %d, *pUncalY %d/n"),*pUncalX,*pUncalY));   
  47.   
  48.             *pTipState |= TouchSampleDownFlag;  
  49.   
  50.             TSP_SampleStart();  //笔针按下后会开启定时器   
  51.          
  52.   
  53.         g_pADCReg->ADCCLRWK CLEAR_ADCWK_INT;  
  54.         // 通知触摸中断已经完成   
  55.         InterruptDone(gIntrTouch);        // Not handled in MDD   
  56.      
  57.     // 当触摸屏中断产生时,同时会打开一个定时器,当笔针未松开时定时触发同一个   
  58.     // 事件通知这个工作线程继续采集数据,直到触摸笔抬起后关闭该定时器。   
  59.     // 故上面的if虽然执行了,也会继续执行后面的else。   
  60.     // 处理定时器中断.触摸笔按下后,定时器被打开,   
  61.     // 定时器将定时产生中断gIntrTouchChanged,并触发事件,直到触摸笔抬起为止。   
  62.     else    // gIntrTouchTimer Interrupt Case   
  63.      
  64.         TSPMSG((_T("[TSP] gIntrTouchChanged(PHYIRQ_TIMER3) Case/r/n")));  
  65.   
  66.         // Check for Pen-Up case on the event of timer3 interrupt   
  67.         // 在开启定时器后检测到笔针已经抬起   
  68.         if ((g_pADCReg->ADCDAT0 D_UPDOWN_UP)  
  69.         || (g_pADCReg->ADCDAT1 D_UPDOWN_UP))  
  70.          
  71.             TSPMSG((_T("[TSP] Pen Up +/r/n")));  
  72.   
  73.             g_bTSP_DownFlag FALSE;  
  74.   
  75.             g_pADCReg->ADCTSC ADCTSC_WAIT_PENDOWN;//等待按下去模式   
  76.             // 将采样的笔针坐标存放起来   
  77.             *pUncalX PrevX;  
  78.             *pUncalY PrevY;  
  79.             *pTipState TouchSampleValidFlag;  
  80.   
  81.             TSP_SampleStop();//停止定时器中断   
  82.          
  83.         else if (g_bTSP_DownFlag)//定时器开启的同时笔针仍然处于按下状态   
  84.          
  85.             if (TSP_GetXY(&TmpX, &TmpY) == TRUE)//采集触摸笔按下的位置.一次采样8组数据并取平均  
  86.              
  87. #ifdef NEW_FILTER_SCHEME   
  88.                 if(Touch_Pen_Filtering(&TmpX, &TmpY))// 过滤采集的数据.这里通过定时器实现3次数据采集   
  89. #else   
  90.                 if(Touch_Pen_Filtering_Legacy(&TmpX, &TmpY))  
  91. #endif   
  92.                  
  93.                     *pTipState TouchSampleValidFlag TouchSampleDownFlag;  
  94.                     *pTipState &= ~TouchSampleIgnore;//数据过滤成功,清掉TouchSampleIgnore标识  
  95.                  
  96.                 else // Invalid touch pen.数据过滤不成功,则返回TouchSampleIgnore   
  97.                  
  98.                     //                    *pTipState TouchSampleValidFlag;   
  99.                     //                    *pTipState |= TouchSampleIgnore;   
  100.                     *pTipState TouchSampleIgnore;  
  101.                  
  102.                 // 将采样的笔针坐标存放起来   
  103.                 *pUncalX PrevX TmpX;  
  104.                 *pUncalY PrevY TmpY;  
  105.   
  106.                 g_pADCReg->ADCTSC ADCTSC_WAIT_PENUP;//等待笔针抬起   
  107.              
  108.             else  
  109.              
  110.                 *pTipState TouchSampleIgnore;  
  111.              
  112.          
  113.         else  
  114.          
  115.             RETAILMSG(TSP_ZONE_ERROR,(_T("[TSP] Unknown State/r/n")));  
  116.   
  117.             *pTipState TouchSampleIgnore;  
  118.             TSP_SampleStop();  
  119.          
  120.         // timer3 interrupt status clear, Do not use OR/AND operation on TINTC_CSTAT directly  
  121.         // 清除定时器3中断位???   
  122.         g_pPWMReg->TINT_CSTAT TINT_CSTAT_INTMASK(g_pPWMReg->TINT_CSTAT) TIMER3_PENDING_CLEAR;  
  123.         // 结束定时器3中断   
  124.         InterruptDone(gIntrTouchChanged);        // Not Handled in MDD   
  125.      
  126.   
  127.     TSPMSG((_T("[TSP] --DdsiTouchPanelGetPoint()/r/n")));  
  128.  

 

这段代码有点难理解,要通过两个中断源以及事件一起理解。 我们先看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标志位表示触摸失败,采样数据无效;

 

上面的程序中,大的框架如下:

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按下的点。具体代码如下:

 

[cpp]   view plain copy print ?
  1. static BOOL  
  2. TSP_GetXY(int *px, int *py)  
  3.  
  4.     int i,j,k;  
  5.     int temp;  
  6.     int x[TSP_SAMPLE_NUM], y[TSP_SAMPLE_NUM];  
  7.     int dx, dy;  
  8.     int TimeOut 100;  // about 100ms   
  9.   
  10.     EnterCriticalSection(&g_csTouchADC);  
  11.     //一起采样8次坐标值   
  12.     for (i 0; TSP_SAMPLE_NUM; i++)  
  13.      
  14.         g_pADCReg->ADCTSC ADCTSC_AUTO_ADC;    // Auto Conversion   
  15.         g_pADCReg->ADCCON |= ENABLE_START_EN;    // ADC Conversion Start   
  16.   
  17.         while (g_pADCReg->ADCCON ENABLE_START_EN)// ADC开始转换后100ms内等待该位清0,超过100ms将提示不能转换。   
  18.            // Wait for Start Bit Cleared   
  19.             if(TimeOut-- 0)  
  20.              
  21.                 RETAILMSG(ZONE_ERROR,(TEXT("ADC cannot start/n")));//ADC cannot start就是在这里打印出来的???   
  22.                 goto ADCfails;  
  23.              
  24.             Sleep(1);  
  25.          
  26.   
  27.         TimeOut 100;  // about 100ms   
  28.         while (!(g_pADCReg->ADCCON ECFLG_END))//继续等待ADC转换100ms,超时还没转换完则提示不能转换完成。   
  29.            // Wait for ADC Conversion Ended   
  30.             if(TimeOut-- 0)  
  31.              
  32.                 RETAILMSG(ZONE_ERROR,(TEXT("ADC Conversion cannot be done/n")));  
  33.                 goto ADCfails;  
  34.              
  35.             Sleep(1);  
  36.          
  37.         // 从ADCDAT0,ADCDAT1中读取转换的数据   
  38.         x[i] D_XPDATA_MASK(g_pADCReg->ADCDAT0);  
  39.         y[i] D_YPDATA_MASK(g_pADCReg->ADCDAT1);  
  40.      
  41.   
  42. ADCfails:  
  43.     LeaveCriticalSection(&g_csTouchADC);  
  44.     // x[i],y[i]从小到大排序   
  45.     for (j 0; TSP_SAMPLE_NUM -1; ++j)  
  46.      
  47.         for (k j+1; TSP_SAMPLE_NUM; ++k)  
  48.          
  49.             if(x[j]>x[k])  
  50.              
  51.                 temp x[j];  
  52.                 x[j]=x[k];  
  53.                 x[k]=temp;  
  54.              
  55.   
  56.             if(y[j]>y[k])  
  57.              
  58.                 temp y[j];  
  59.                 y[j]=y[k];  
  60.                 y[k]=temp;  
  61.              
  62.          
  63.      
  64.   
  65. #ifdef    DETAIL_SAMPLING   
  66.     // samples Interpolation (weighted samples)   
  67.     // 8次采样时最小一位,最大两位为无效数据   
  68.     *px (x[2] ((x[3]+x[4])<<1) (x[3]+x[4]) x[5]);  
  69.     *py (y[2] ((y[3]+y[4])<<1) (y[3]+y[4]) y[5]);  
  70.     // 求8次采样的平均值   
  71.     if ((*px 0x7) 3) *px (*px>>3) 1;  
  72.         else *px *px>>3;  
  73.   
  74.     if ((*py 0x7) 3) *py (*py>>3) 1;  
  75.         else *py *py>>3;  
  76.     //求出有效值的最大偏差   
  77.     dx x[5] x[2];  
  78.     dy y[5] y[2];  
  79. #else   
  80.     // samples average   
  81.     // 求两次采样的平均值   
  82.     *px (x[1] x[2] 1)>>1;  
  83.     *py (y[1] y[2] 1)>>1;  
  84.     //求出有效值的最大偏差   
  85.     dx x[2] x[1];  
  86.     dy y[2] y[1];  
  87. #endif   
  88.     // 有效值的最大偏差大于40时,返回失败   
  89.     if ((dx TSP_INVALIDLIMIT) || (dy TSP_INVALIDLIMIT))  
  90.      
  91.         return FALSE;  
  92.      
  93.     else  
  94.      
  95.         return TRUE;  
  96.      
  97.  

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

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

[cpp]   view plain copy print ?
  1. static BOOL  
  2. Touch_Pen_Filtering(INT *px, INT *py)  
  3.  
  4.     BOOL RetVal TRUE;  
  5.     // TRUE  Valid pen sample   
  6.     // FALSE Invalid pen sample   
  7.     INT Filter_Margin;  
  8.     static int count 0;  
  9.     static INT x[2], y[2];  
  10.     INT TmpX, TmpY;  
  11.     INT dx, dy;  
  12.   
  13.     if(*px <0 && *py <0)  
  14.      
  15.         count 0;  
  16.         return FALSE;  
  17.      
  18.     else  
  19.      
  20.         count++;  
  21.      
  22.   
  23.     if (count 2)// 只采样两组数据,两次后开始下面的Filter算法   
  24.      
  25.         // apply filtering rule   
  26.         count 2;  
  27.   
  28.         // average between x,y[0] and *px,y   
  29.         // 第一组数据和第三组数据取平均   
  30.         TmpX (x[0] *px)>>1;  
  31.         TmpY (y[0] *py)>>1;  
  32.   
  33.         // difference between x,y[1] and TmpX,Y   
  34.         // 求出第二组数据与第一,三组数据的平均值的差值   
  35.         dx (x[1] TmpX) (x[1] TmpX) (TmpX x[1]);  
  36.         dy (y[1] TmpY) (y[1] TmpY) (TmpY y[1]);  
  37.   
  38.     // Filter_Margin = 第一,二组数据的X,Y坐标的差值 TSP_FILTER_LIMIT(3)   
  39.         Filter_Margin (x[1] x[0]) (x[1]-x[0]) (x[0]-x[1]);// 求出第一组数据与第二组数据的X坐标的差值   
  40.         Filter_Margin += (y[1] y[0]) (y[1]-y[0]) (y[0]-y[1]);//求出第一组数据与第二组数据的Y坐标的差值并累加到Filter_Margin   
  41.         Filter_Margin += TSP_FILTER_LIMIT;  
  42.   
  43.     // 如果dx,dy大于Filter_Margin,则保存上一组采样数据[第二组],并返回失败   
  44.     // 这里可以拓展实现触摸屏的滑动???   
  45.         if ((dx Filter_Margin) || (dy Filter_Margin))   
  46.       
  47.             // Invalid pen sample   
  48.             *px x[1];  
  49.             *py y[1]; // previous valid sample   
  50.             RetVal FALSE;  
  51.             count 0;  
  52.          
  53.         else//否则保留后两组数据到x[0],y[0]以及x[1],y[1].程序执行到这里,笔针采样数据才有效   
  54.          
  55.             // Valid pen sample   
  56.             x[0] x[1]; y[0] y[1];  
  57.             x[1] *px; y[1] *py; // reserve pen samples   
  58.   
  59.             RetVal TRUE;  
  60.          
  61.      
  62.     else // (count 2)   
  63.     // till samples, no filtering rule   
  64.         // 保存前两组采样得到的数据。   
  65.         // 第一组采样的数据放到x[0],y[0]   
  66.         // 第二组采样的数据放到x[1],y[1]   
  67.         x[0] x[1]; y[0] y[1];  
  68.         x[1] *px; y[1] *py; // reserve pen samples   
  69.   
  70.         RetVal FALSE;  
  71.      

         

         

        S3C2440之ADC

        S3C2440包含一个8通道A/D转换器,有10位分辨率下面简要介绍一下S3C2440中ADC的用法:

        用到的寄存器:

              ADCCON:ADC控制寄存器

              ADCDAT0:ADC转换数据寄存器

              SUBSRCPND:次级源挂起寄存器

              INTSUBMSK:中断次级屏蔽寄存器

              SRCPND:源挂起寄存器

              INTPND:中断挂起寄存器

              INTMSK:中断屏蔽寄存器

              ADCCON:AD控制寄存器

              ADCDAT0:AD转换数据寄存器

        1,首先设置控制寄存器ADCCON的相应位来选择频率和通道:

            rADCCON = (1<<14)|(preScaler<<6)|(ch<<3);	//setup channel,分频使能,写入分频值,选择通道
        其中PreScaler是预分频值,决定 A/D转换器频率 = 50MHz/(49+1) = 1MHz

        如图:


        2,然后启动AD并等待启动位ADCCON[0]清0和转换结束位ADCCON[15]置1:

            rADCCON|=0x1;   //start ADC开始转换
        
            while(rADCCON & 0x1);	//check if Enable_start is low等待启动位清零,转换启动结束
            while(!(rADCCON & 0x8000));	//check if EC(End of Conversion) flag is high等待转换结束

        3,最后读取相应通道转换结果寄存器的值:

        寄存器ADCDAT0的低10位用于存储A/D转换后的数据。

            return ( (int)rADCDAT0 & 0x3ff );//返回0~9位的采样值,0~1023
        	    a0=ReadAdc(0); //对应开发板上W1可调电阻
        	    a1=ReadAdc(1); //对应开发板上W2可调电阻

        OK!

        有必要介绍一下寄存器ADCCON:

        如下图:

        寄存器ADCCON的第15位用于标识A/D转换是否结束。第14位用于使能是否进行预分频,而第6位到第13位则存储的是预分频数值,因为A/D转换的速度不能太快,所以要通过预分频处理才可以得到正确的A/D转换速度,如我们想要得到A/D转换频率为1MHz,则预分频的值应为49。第3位到第5位表示的是A/D转换的通道选择。第2位可以实现A/D转换的待机模式。第1位用于是否通过读取操作来使能A/D转换的开始。第0位则是在第1位被清零的情况下用于开启A/D转换。

        http://blog.csdn.net/qq872318306/article/details/6877592

        评论
        添加红包

        请填写红包祝福语或标题

        红包个数最小为10个

        红包金额最低5元

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

        抵扣说明:

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

        余额充值