s3c2440的触摸屏应用与校正

原文地址 :http://blog.csdn.net/zhaocj/archive/2010/04/21/5510536.aspx


触摸屏是当今最流行的一种人机交互接口,它被广泛地应用于手机等消费类电子产品中,目前这种技术有向 PC 机方向发展的趋势。基于原理的不同,触摸屏可以分为电阻式、电容式、表面声波式等。电阻式是应用较广的一种触摸屏,它的原理是通过测量横向和纵向的电阻值来获得触点的坐标。

 

s3c2440 集成了 4 线制电阻式的触摸屏接口,触点坐标的检测是通过 A/D 转换来实现的。 s3c2440 一共有 4 种触摸屏接口模式,其中,自动(连续) XY 坐标转换模式和等待中断模式应用地比较常见。等待中断模式是在触笔落下时产生一个中断,在这种模式下, A/D 触摸屏控制寄存器 ADCTSC 的值应为 0xD3 ,在系统响应中断后, XY 坐标的测量模式必须为无操作模式,即寄存器 ADCTSC 的低两位必须清零。自动(连续) XY 坐标转换模式是系统依次转换触点的 X 轴坐标和 Y 轴坐标,其中 X 轴坐标值写入寄存器 ADCDAT0 的低 10 位中, Y 轴坐标写入寄存器 ADCDAT1 的低 10 位中,在这种模式下,系统同样会产生中断信号。在一般情况下,为实现触摸屏功能,先是设置为等待中断模式,在产生中断后,再设置为自动(连续) XY 坐标转换模式,依次读取触点的坐标值。在实现触摸屏功能的过程中,除了上面介绍的几个寄存器外,还会用到以下寄存器。寄存器 ADCTSC 的第 8 位能够实现是触笔落下中断还是触笔抬起中断,如果写过基于视窗应用程序的人对这一点会很熟悉,它就好像单击鼠标操作一样,一次单击操作包括两个动作:按下和释放,这两个动作可以完成不同的命令。寄存器 ADCTSC 的第 3 位可以选择上拉电阻的使能,在等待中断模式下,上拉电阻要有效,在触发中断后,上拉电阻要无效。寄存器 ADCTSC 的第 2 位用于选择自动(连续) XY 坐标转换模式。触笔抬起 / 落下中断状态寄存器 ADCUPDN 的低 2 位能够判断触笔在何种状态下引起的中断。 A/D 延时寄存器 ADCDLY 可以设置开始中断到真正开始 A/D 转换这段时间的延时长度,它的时钟源频率为 3.68MHz

 

       在开始实现触摸屏功能之前,还需要解决一个问题,那就是触摸屏的校正。触摸屏和 LCD 是两种不同的物理器件。对于一个分辨率为 320 × 240 LCD ,它的宽度为 320 个像素,高度为 240 个像素。而触摸屏处理的数据是点的物理坐标,该坐标是通过触摸屏控制器采集得到的。要想实现触摸屏上的物理坐标与 LCD 上的像素点坐标一一对应上,两者之间就需要一定的转换,即校正。而且电阻式触摸屏由于自身的原因参数会发生变化,因此需要经常性的校正。比较常见的校正方法是三点校正法,它的原理是:

 

       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

 

       下面的程序是实现触摸屏功能的简单实例——以触点为中心,绘制出一个红色的边长为 10 个 像素的正方形。触点的坐标是用下面方法得到的:当触笔落下时,进入中断,然后读取触点处的坐标,直到触笔的抬起,才退出该次中断。由于触摸屏需要校正,因 此在使用之前需要进行校正处理。但并不是每次使用都要校正,只要坐标没有发生漂移,就不需要再次校正。所以在进行一次校正后,只要把那几个参数保存起来, 下次需要时直接使用上次保存下来的参数即可。在这里,我们利用 EEPROM 来保存这几个参数,即 A,B,C,D,E,F,K 分别保存在以 0x20,0x30,0x40,0x50,0x60,0x70,0x80 为首地址内存的连续 4 个字节空间内,另外内存地址 0x1F 保存一个标识信息,当为 0x6A 时,表示这几个参数已计算并保存好了,只需从上述内存地址中读取参数就行,而当为其他值时,就需要进行校正。校正时,需要三个取样点,在这里我们选取 LCD 上的 (32,24) (160,216) (288,120) 为这三个取样点,我们在这三个取样点上画一个十字(如下图所示),只需要依次点击这三个点,即可完成触摸屏的校正。

 

……   ……

volatile U32 LCD_BUFFER[LCD_HEIGHT][LCD_WIDTH];

unsigned char iic_buffer[8];

unsigned char devAddr=0xa0;

int A,B,C,D,E,F,K;

 

volatile int xdata, ydata;

 

int flagIIC;            //IIC 标志

int flagTS;             // 触摸屏标志

……   ……

 

// 触摸屏中断

void __irq ADCTs(void)

{

       rADCTSC = (1<<3)|(1<<2);         // 上拉电阻无效,自动连续 XY 坐标转换模式开启

       rADCDLY = 40000;                    // 延时

      

       rADCCON|=0x1;                 // 开始 A/D 转换

 

       while(rADCCON & 0x1)

;                                  // 检查 A/D 转换是否开始

       while(!(rADCCON & 0x8000))

;                                  // 等待 A/D 转换的结束

             

       while(!(rSRCPND & ((U32)0x1<<31)))     

;                                  // 判断 A/D 中断的悬挂位

 

       xdata=(rADCDAT0&0x3ff);                // 读取 X 轴坐标

ydata=(rADCDAT1&0x3ff);                // 读取 Y 轴坐标

      

       flagTS = 1;            // 置标志

      

       rSUBSRCPND|=0x1<<9;

       rSRCPND = 0x1<<31;

       rINTPND = 0x1<<31;         

       rINTSUBMSK=~(0x1<<9);

       rINTMSK=~(0x1<<31);                            // A/D 中断,开启 A/D 中断屏蔽

                      

       rADCTSC =0xd3;                // 再次设置等待中断模式,这一次是判断触笔的抬起

       rADCTSC=rADCTSC|(1<<8);             // 设置触笔抬起中断

 

       while(1)         // 等待触笔的抬起

       {

              if(rSUBSRCPND & (0x1<<9))     // 检查 A/D 触摸屏中断悬挂

              {

                     break;                   // 如果触笔抬起,则跳出该循环

              }

       }    

      

       rADCDLY=50000;

       rSUBSRCPND|=0x1<<9;

       rINTSUBMSK=~(0x1<<9);

       rSRCPND = 0x1<<31;

       rINTPND = 0x1<<31;                 // 再次清 A/D 中断,开启 A/D 中断屏蔽

       rADCTSC =0xd3;                // 设置等待中断模式,为下一次触笔的落下做准备

}

 

// 绘制“十”字型

void drawCross(U32 x,U32 y,U32 color)

{

       int i;

       for(i=x-10;i<x+11;i++)

              PutPixel(i,y, color);

       for(i=y-10;i<y+11;i++)

              PutPixel(x,i, color);

}

 

// 触摸屏校正

void TSCal(void)

{

       int i=0;

       int xt[3],yt[3];

       Brush_Background(0,0,LCD_WIDTH,LCD_HEIGHT,0xFFFFFF);

       drawCross(32,24,0xFF0000);

       Draw_ASCII(36,28,0xFF0000,one);

       drawCross(160,216,0xFF0000);

       Draw_ASCII(164,220,0xFF0000,two);

       drawCross(288,120,0xFF0000);

       Draw_ASCII(292,124,0xFF0000,three);

 

       // 依次读取三个采样点的坐标值

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

       {    

              while(flagTS==0)

                     delay(500);

              xt[i]=xdata;

              yt[i]=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);

}

 

// 把一个 32 位整型转换为 4 8 位字节型,并写入 EEPROM

void wrTStoIIC(int coef,unsigned char address)

{

       iic_buffer[0]=(unsigned char)((coef&0xFF000000)>>24);

       iic_buffer[1]=(unsigned char)((coef&0x00FF0000)>>16);

     iic_buffer[2]=(unsigned char)((coef&0x0000FF00)>>8);

     iic_buffer[3]=(unsigned char)(coef&0x000000FF);

     wr24c02a(address,iic_buffer,4);

}

 

// 读取 EEPROM 中的 4 8 位字节,并把它们组合成一个 32 位的整型。

int rdTStoIIC(unsigned char address)

{

       int temp;

       rd24c02a(address,iic_buffer,4);

       temp=(iic_buffer[0]<<24)|(iic_buffer[1]<<16)|(iic_buffer[2]<<8)|(iic_buffer[3]);

       return temp;

}

 

void Main(void)

{

      

LCD_Init();   

rLCDCON1|=1;

   

rADCDLY=50000;               // 设置延时

       rADCCON=(1<<14)+(9<<6);              // 设置 A/D 预分频

 

       rADCTSC=0xd3;                 // 设置触摸屏为等待中断模式。

 

       pISR_ADC = (U32)ADCTs;

      

……   ……

 

flagTS = 0;

       flagIIC = 1;

 

       // 读取 EEPROM 中的标志地址内容,用于判断触摸屏校正的参数是否已计算并保存好

       rd24c02a(0x1F,iic_buffer,1);

 

       if(iic_buffer[0]!=0x6A)        // 如果触摸屏的校正参数没有计算并保存,重新校正

{

       TSCal();

              rINTMSK=~((0x1<<31)|(0x1<<27));          // 开启 IIC 中断屏蔽

              iic_buffer[0]=0x6A;

              wr24c02a(0x1F,iic_buffer,1);                     // 置“触摸屏校正参数计算并保持好”的标志信息

              delay(3000);                        // 等待一段时间,一定要有,否则 EEPROM 不能正确读写

              wrTStoIIC(A,0x20);

              delay(3000);

            wrTStoIIC(B,0x30);

            delay(3000);

            wrTStoIIC(C,0x40);

            delay(3000);

              wrTStoIIC(D,0x50);

              delay(3000);

              wrTStoIIC(E,0x60);

              delay(3000);

              wrTStoIIC(F,0x70);

              delay(3000);

              wrTStoIIC(K,0x80);

             

       }

       else                // 如果触摸屏校正参数已准备好,则直接读取

       {

              A=rdTStoIIC(0x20);

              delay(3000);

              B=rdTStoIIC(0x30);

              delay(3000);

              C=rdTStoIIC(0x40);

              delay(3000);

              D=rdTStoIIC(0x50);

              delay(3000);

              E=rdTStoIIC(0x60);

              delay(3000);

              F=rdTStoIIC(0x70);

              delay(3000);

              K=rdTStoIIC(0x80);

       }

     

       Brush_Background(0,0,LCD_WIDTH,LCD_HEIGHT,0xFFFFFF);

  

while(1)

{

              if(flagTS)

       {

                     flagTS=0;

              xLcd = (A*xdata+B*ydata+C)/K;                // 计算 X 轴坐标

                     yLcd = (D*xdata+E*ydata+F)/K;                // 计算 Y 轴坐标

              Brush_Background(xLcd-5,yLcd-5,xLcd+5,yLcd+5,0xFF0000);        // 绘制正方形

       }

       delay(1000000);

}

}

 

 

       本文介绍的程序要略显复杂一些,这里对触摸屏校正和中断再做一总结:

1 、一般地,在第一次使用触摸屏时,需要校正一次,以后可以不再校正,除非发生了明显的漂移。我们是把校正参数存储在 EEPROM 中,下次再使用时,只需读取该组数据即可。 EEPROM 中的 0x1F 地址用于存储校正参数标识信息,在已经校正过的情况下,该位内容为 0x6A ,这样在下次开机使用触摸屏时,只要读取该位内容,就可知道触摸屏是否已校正,没有校正则需要进行校正,已经校正过了则无需再重复校正了。

2 、先设置等待中断模式,以等待触摸屏中断的发生,当中断发生并进入中断以后,再转换为自动连续 XY 坐标转换模式,可正确读取触点的坐标值。在中断程序中,当触笔落下时进入中断,当触笔抬起时退出中断,程序只记录触笔落下时触点的坐标值。

 

该段程序的演示图如下:

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值