TQ2440之触摸屏
1.触摸屏原理
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,当触控笔按下时,想中断控制器发起中断请求。本篇文章主要讲等待中断模式。
触摸屏初始化:
#define ADCPRS 9 void Init_Touchpanel(void) { rADCDLY=50000; //ADC转换启动延时值 rADCCON=(1<<14)+(ADCPRS<<6); //预分频使能, 预分频值为ADCPRS rADCTSC=0xd3; //等待中断模式 pISR_ADC = (int)AdcTsAuto;//中断入口函数 rINTMSK &= ~BIT_ADC; //使能ADC中断 rINTSUBMSK &= ~(BIT_SUB_TC);//使能ADC子中断 }
中断服务程序:
void __irq AdcTsAuto(void) { U32 saveAdcdly; rADCTSC=(1<<3)|(1<<2); //XP上拉禁止,自动测量x方向和y方向 saveAdcdly=rADCDLY; rADCDLY=40000; //延时 rADCCON|=0x1; //开始ADC转换 while(rADCCON & 0x1); //成功启动后此位被清零 while(!(rADCCON & 0x8000)); //等待ADC转换结束 while(!(rSRCPND & (BIT_ADC))); //等待ADC转换完成中断标志位 xdata=(rADCDAT0 & 0x3ff); //读取转换后的值 ydata=(rADCDAT1 & 0x3ff); flagTS=1;//此标志用于触摸屏校准标志 rSUBSRCPND|=BIT_SUB_TC;//清中断 ClearPending(BIT_ADC); rINTSUBMSK&=~(BIT_SUB_TC);//使能中断 rINTMSK&=~(BIT_ADC); rADCTSC =0xd3; //再次设置成等待中断模式 rADCTSC=rADCTSC|(1<<8); //检测笔尖抬起中断 while(1) //to check Pen-up state { if(rSUBSRCPND & (BIT_SUB_TC)) //监测到 { break;//退出循环 } } rADCDLY=saveAdcdly; rADCTSC=rADCTSC&~(1<<8); // 禁止检测笔尖抬起中断 rSUBSRCPND|=BIT_SUB_TC;//清中断 rINTSUBMSK&=~(BIT_SUB_TC); ClearPending(BIT_ADC); }
2.触摸屏校准
我们在点击触摸屏时,实际上得到的是该点的电压值,而不是该点的位置坐标,需要一定的计算才能得到该点的实际坐标。
这里介绍一下三点法校准。
设LCD上每个点PD的坐标为[XD,YD],触摸屏上每个点PT的坐标为[XT,YT]。要实现触摸屏上的坐标转换为LCD上的坐标,需要下列公式进行转换:
XD=A×XT+B×YT+C;YD=D×XT+E×YT+F;
因为其中一共有六个参数(A,B,C,D,E,F),因此只需要三个取样点就可以求得这六个参数。这六个参数一旦确定下来,只要给出任意触摸屏上的坐标点PT,代入这个公式,就可以得到它所对应的LCD上像素点的坐标PD。
已知LCD上的三个取样点为:PD0,PD1,PD2,它们所对应的触摸屏上的三个点为:PT0,PT1,PT2。A,B,C,D,E,F这六个参数最终的结果都是一个分式,而且都有一个共同的分母,为:K=(XT0-XT2)×(YT1-YT2)-(XT1-XT2)×(YT0-YT2)
那么这六个参数分别为:
A=[(XD0-XD2)×(YT1-YT2)-(XD1-XD2)×(YT0-YT2)] / K
B=[(XT0-XT2)×(XD1-XD2)-(XD0-XD2)×(XT1-XT2)] / K
C=[YT0×(XT2×XD1-XT1×XD2)+YT1×(XT0×XD2-XT2×XD0)+YT2×(XT1×XD0-XT0×XD1)] / K
D=[(YD0-YD2)×(YT1-YT2)-(YD1-YD2)×(YT0-YT2)] / K
E=[(XT0-XT2)×(YD1-YD2)-(YD0-YD2)×(XT1-XT2)] / K
F=[YT0×(XT2×YD1-XT1×YD2)+YT1×(XT0×YD2-XT2×YD0)+YT2×(XT1×YD0-XT0×YD1)] / K
计算A、B、C、D、E、F的值 :
void TSCal(void) { int i=0; int xt[3],yt[3]; Lcd_ClearScr(0xFFFF); drawCross(32,24,0xFF0000); drawCross(160,216,0xFF0000); drawCross(288,120,0xFF0000); //依次读取三个采样点的坐标值 for(i=0;i<3;i++) { while(flagTS==0); xt[i]=xdata; yt[i]=ydata; Uart_Printf("xdata=%d\n",xdata); Uart_Printf("ydata=%d\n",ydata); flagTS=0; } //计算参数 K=(xt[0]-xt[2])*(yt[1]-yt[2])-(xt[1]-xt[2])*(yt[0]-yt[2]); A=(32-288)*(yt[1]-yt[2])-(160-288)*(yt[0]-yt[2]); B=(xt[0]-xt[2])*(160-288)-(32-288)*(xt[1]-xt[2]); C=yt[0]*(xt[2]*160-xt[1]*288)+yt[1]*(xt[0]*288-xt[2]*32)+yt[2]*(xt[1]*32-xt[0]*160); D=(24-120)*(yt[1]-yt[2])-(216-120)*(yt[0]-yt[2]); E=(xt[0]-xt[2])*(216-120)-(24-120)*(xt[1]-xt[2]); F=yt[0]*(xt[2]*216-xt[1]*120)+yt[1]*(xt[0]*120-xt[2]*24)+yt[2]*(xt[1]*24-xt[0]*216); }
将计算出来的A、B、C、D、E、F的值存入EEPROM,0x1f地址存放是否存储过的标志位。读取0x1f地址的值赋予iic_buffer[0],如果iic_buffer[0]不等于0x55,证明数据没有存储到EEPROM,则计算A、B、C、D、E、F的值,并写入EEPROM,如果等于0x55证明数据已经写入EEPROM,直接读取即可,这样就不用每次都校验触摸屏了。
void CalcuateTouch(void) { flagTS = 0; Rd24C080(0xa0,0x1f,&(iic_buffer[0])); if(iic_buffer[0]!=0x55) { TSCal(); Wr24C080(0xa0,0x1f,0x55); iic_writekey(0xa0,0x20,A); iic_writekey(0xa0,0x30,B); iic_writekey(0xa0,0x40,C); iic_writekey(0xa0,0x50,D); iic_writekey(0xa0,0x60,E); iic_writekey(0xa0,0x70,F); iic_writekey(0xa0,0x80,K); } else { A = iic_readkey(0xa0,0x20); B = iic_readkey(0xa0,0x30); C = iic_readkey(0xa0,0x40); D = iic_readkey(0xa0,0x50); E = iic_readkey(0xa0,0x60); F = iic_readkey(0xa0,0x70); K = iic_readkey(0xa0,0x80); } Lcd_ClearScr(0xFFFF); }
这时A、B、C、D、E、F的值都已经计算出来,将触摸屏ADC值带入公式就可以得到LCD上的物理位置。