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

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

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

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

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

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

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

wince 触摸屏 学习相关推荐

  1. 威纶通触摸屏学习资料,附带软件eb8000和eb pro助你熟练掌握应用威纶通触摸屏

    威纶通触摸屏学习资料,附带软件eb8000和eb pro助你熟练掌握应用威纶通触摸屏8518622232839557剑指工控

  2. wince大排档学习

    norains相对我辈来说是个大家,专业人士.他的书籍对我帮助很大.现列出一些资源,这些大多是他本博客的内容.先表示感谢. <Windows CE大排档>源代码 http://blog.c ...

  3. x210---电容式触摸屏学习笔记

    1.电容触摸屏的原理 电容式触摸屏技术是利用人体的电流感应进行工作的.电容式触摸屏是一块四层复合玻璃屏,玻璃屏的内表面和夹层各涂有一层ITO,最外层是一薄层矽土玻璃保护层,夹层ITO涂层作为工作面,四 ...

  4. 基于STM32的触摸屏学习笔记

    本文共有三个内容:一.电阻触摸屏的原理:二.XPT2046的控制字与数字接口:三.程序源码讲解(参考正点原子的代码) 一.电阻触摸屏的原理,上图: 图上的文字介绍了触摸的原理,下面总结一下触摸的原理: ...

  5. x210---电阻式触摸屏学习笔记

    1.输入类设备简介 1.1.input/output (1)IO输入输出,是计算机系统中的一个概念.计算机的主要功能就是从外部获取数据然后进行计算加工得到输出数据并输出给外部(计算机可以看成数据处理器 ...

  6. wince 蓝牙 学习

    参考链接:http://blog.csdn.net/csu_yang/article/details/5912659 http://blog.csdn.net/lailzhihou/article/d ...

  7. 触摸屏学习笔记(1)-- 触摸屏基础(百度百科)

    https://baike.baidu.com/item/%E8%A7%A6%E6%91%B8%E5%B1%8F

  8. 都来说说你是如何学习wince 驱动的(请大牛们也来凑凑热闹)

    我想大家也知道,论坛和一些QQ技术交流群很多新手都会问:应该如何学习wince驱动?以前很多时间,也打字打的手痛.也不敢说的太多,怕误人子弟.现在在这里开个帖子,希望老牛们不吝赐教新手,呵呵.大家照着 ...

  9. STM32学习笔记一一触摸屏

    前言: 为了方便查看博客,特意申请了一个公众号,附上二维码,有兴趣的朋友可以关注,和我一起讨论学习,一起享受技术,一起成长. 1. 简介 1.1 电阻式触摸屏 电阻式触摸屏利用压力感应进行触点检测控制 ...

  10. WINCE6.0+S3C6410的触摸屏驱动

    ********************************LoongEmbedded******************************** 作者:LoongEmbedded(kandi ...

最新文章

  1. 把BERT拉下神坛!ACL论文只靠一个“Not”,就把AI阅读理解骤降到盲猜水平
  2. 11旋转编码器原理图_雷恩PRECILEC I9H系列增量式编码器
  3. 迷宫游戏c语言代码讲解,迷宫游戏C语言代码讲解.doc
  4. 每日一笑 | 今天是植树节,我想在你心里种点逼树
  5. 牛客20701 神秘钥匙
  6. 怎么打败腾讯[纯讨论]
  7. 06-基本查询-进阶一
  8. 使用 ES6 的浏览器兼容性问题
  9. kernel: TCP: time wait bucket table overflow
  10. javamall 3.0 mysql_Java数据类型与MySQL数据类型对照表
  11. Java小题,通过JNI调用本地C++共享库中的对应方法实现杨辉三角的绘制
  12. c语言关于完数(完全数,完美数)的判断及寻找
  13. 【MIKE21】批处理依次打开模型文件运行模型
  14. 病毒周报(081208至081214)
  15. 色彩空间与色彩深度。详细分析相机中sRGB/AdobeRGB、RAW/JPG到底该怎么选
  16. PhotoScan软件进行无人机数据处理流程
  17. cass简码大全_CASS简码与实体编码对照表
  18. 随着人工智能发展的少儿编程教育
  19. 别再骂百度难用了,问题是你根本就不会用搜索引擎!
  20. Kafka 3.1的KRaft模式里的broker与controller

热门文章

  1. 东野奎吾--《新参者》--读后感
  2. 建房遮阳标准的计算机方法,农村建房遮阴计算标准,看看怎么计算?
  3. 《地球概论》(第3版)笔记 第四章 地球运动的地理意义
  4. RT-Thread操作系统
  5. 认同和确定性矩阵(Ralph Stacey's Agreement and Certainty Matrix)-译
  6. 如何辨别u盘是否为缩水,并恢复u盘真实容量
  7. android发短信!渣本毕业两年经验,终局之战
  8. 国内第一本许可式订阅E-mail营销书籍
  9. 全球知名虚拟服务器,国外十大虚拟主机
  10. Chromium Embedded Framework (CEF) 介绍