Microsoft Windows CE .NET 中的中断体系结构
在调用了单个 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接口函数是驱动实现的关键所在。
- //
对触摸屏数据的采样,采样的数据以*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;//ADCTSC设置为等待抬起模式 -
-
*pTipState |= TouchSampleIgnore; -
-
*pUncalX = PrevX; -
*pUncalY = PrevY; -
//added by lqm.must be recovered later. -
//RETAILMSG(1,(_T("[gIntrTouch] *pUncalX = %d, *pUncalY = %d/n"),*pUncalX,*pUncalY)); -
-
*pTipState |= TouchSampleDownFlag; -
-
TSP_SampleStart(); //笔针按下后会开启定时器 -
} -
-
g_pADCReg->ADCCLRWK = CLEAR_ADCWK_INT; -
// 通知触摸中断已经完成 -
InterruptDone(gIntrTouch); // Not handled in MDD -
} -
// 当触摸屏中断产生时,同时会打开一个定时器,当笔针未松开时定时触发同一个 -
// 事件通知这个工作线程继续采集数据,直到触摸笔抬起后关闭该定时器。 -
// 故上面的if虽然执行了,也会继续执行后面的else。 -
// 处理定时器中断.触摸笔按下后,定时器被打开, -
// 定时器将定时产生中断gIntrTouchChanged,并触发事件,直到触摸笔抬起为止。 -
else // 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组数据并取平均 -
{ - #ifdef
NEW_FILTER_SCHEME -
if(Touch_Pen_Filtering(&TmpX, &TmpY))// 过滤采集的数据.这里通过定时器实现3次数据采集 - #else
-
if(Touch_Pen_Filtering_Legacy(&TmpX, &TmpY)) - #endif
-
{ -
*pTipState = TouchSampleValidFlag | TouchSampleDownFlag; -
*pTipState &= ~TouchSampleIgnore;//数据过滤成功,清掉TouchSampleIgnore标识 -
} -
else // Invalid touch pen.数据过滤不成功,则返回TouchSampleIgnore -
{ -
// *pTipState = TouchSampleValidFlag; -
// *pTipState |= TouchSampleIgnore; -
*pTipState = TouchSampleIgnore; -
} -
// 将采样的笔针坐标存放起来 -
*pUncalX = PrevX = TmpX; -
*pUncalY = PrevY = TmpY; -
-
g_pADCReg->ADCTSC = ADCTSC_WAIT_PENUP;//等待笔针抬起 -
} -
else -
{ -
*pTipState = TouchSampleIgnore; -
} -
} -
else -
{ -
RETAILMSG(TSP_ZONE_ERROR,(_T("[TSP] Unknown State/r/n"))); -
-
*pTipState = TouchSampleIgnore; -
TSP_SampleStop(); -
} -
// timer3 interrupt status clear, Do not use OR/AND operation on TINTC_CSTAT directly -
// 清除定时器3中断位??? -
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"))); - }
这段代码有点难理解,要通过两个中断源以及事件一起理解。
DdsiTouchPanelGetPoint( &SampleFlags, &RawX, &RawY );
在MDD层中,通过上面的调用方式,从DdsiTouchPanelGetPoint()函数中回传了三个参数,第一个用于表明触摸正处于的状态,RawX,RawY是调用的函数从TP触摸点取样得到的点的数据,这里的数据还是原始的ADC采样的数据,即直接从ADC转换的寄存器里面得到的,并没有对该数据进行处理。
先看看SampleFlags这个变量,一起有如下几种状态:
TouchSampleValidFlag标志位表示已经获得TP采样事件,准备开始采样;
TouchSampleDownFlag标志位表示检测到TP已经按下去;
TouchSampleIsCalibratedF
TouchSamplePreviousDownF
TouchSampleIgnore标志位表示触摸失败,采样数据无效;
上面的程序中,大的框架如下:
if (g_pVIC1Reg->VICRAWINTR & (1<<(PHYIRQ_PENDN-VIC1_BIT_OFFSET)))
{
}
else
{
}
- static
BOOL - TSP_GetXY(int
*px, int *py) - {
-
int i,j,k; -
int temp; -
int x[TSP_SAMPLE_NUM], y[TSP_SAMPLE_NUM]; -
int dx, dy; -
int TimeOut = 100; // about 100ms -
-
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)// ADC开始转换后100ms内等待该位清0,超过100ms将提示不能转换。 -
{ // Wait for Start Bit Cleared -
if(TimeOut-- < 0) -
{ -
RETAILMSG(ZONE_ERROR,(TEXT("ADC cannot start/n")));//ADC cannot start就是在这里打印出来的??? -
goto ADCfails; -
} -
Sleep(1); -
} -
-
TimeOut = 100; // about 100ms -
while (!(g_pADCReg->ADCCON & ECFLG_END))//继续等待ADC转换100ms,超时还没转换完则提示不能转换完成。 -
{ // Wait for ADC Conversion Ended -
if(TimeOut-- < 0) -
{ -
RETAILMSG(ZONE_ERROR,(TEXT("ADC Conversion cannot be done/n"))); -
goto ADCfails; -
} -
Sleep(1); -
} -
// 从ADCDAT0,ADCDAT1中读取转换的数据 -
x[i] = D_XPDATA_MASK(g_pADCReg->ADCDAT0); -
y[i] = D_YPDATA_MASK(g_pADCReg->ADCDAT1); -
} -
- ADCfails:
-
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; -
} - }
- static
BOOL - Touch_Pen_Filtering(INT
*px, INT *py) - {
-
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; -
}
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转换。