简介:
本文主要介绍触摸屏校准的实现原理以及以及代码的编写。
所用开发板:JZ2440 V3
触摸屏校准原理:
其实我们做触摸屏校准主要实现的功能是计算出我们触摸屏在横竖两个方向上一个像素点占用几个ADC值。而本开发板的ADC为片内资源,他是一个十位的ADC,所以他的取值范围是0~1023 。而我们的显示屏是480*272的,即在横向上我们有480个像素点,而在纵向上我们有272个像素点。所以我们就要使用触摸屏校准来确定每个方向上每个像素占用几个ADC。当我们确定下这些后我们就可以操作LCD在我们所点的地方实现显示了。而我们就可以为下面的工作:点不同的区域实现对不同模块的选择了,做准备了。
好,我们在上面已经明确了我们的目标,那么我们现在要做的就是如何来实现这个目标。我们先一步一步的来思考。我们先来思考算法的实现,如下图:
在上图中我标出了我们要校准的四个校准点他们分别为S1,S2,S3,S4。我们要做的就是先确定这四个点的位置,我将他们分别放到了S1(14,14),S2(464,14),S3(14,256),以及S4(464,256)的位置。所以这几个点在横向上差450个像素,而在纵向上差242个像素。我们要做的下一步就是记录点击到这个四个点上的ADC值AD1(tc_x[0],tc_y[0]), AD2(tc_x[1],tc_y[1]), AD3(tc_x[2],tc_y[2]), AD4(tc_x[3],tc_y[3])。然后我们用AD1与AD2比较,从他们的纵坐标选出较小的值chy1以及差值vy1,用AD1与AD3比较他们的横坐标选出较小的值chx1以及差值vx1,接着我们比较AD4与AD3的纵坐标选出他们中较大的值chy2以及差值vy2,最后我们比较AD4与AD2从他们的横坐标选出较大的值chx2以及差值vx2。
如果我们vx1与vx2的差值以及vy1与vy2的差值在误差允许范围内,我们将使用这组值。如果不在这个范围内我们将重新对触摸屏进行数据采集。直到数据结果符合要求。这时我们将使用获得的chx1,chx2,chy1和chy2以及上面提到的vx1,vx2,vy1和vy2算的每个像素点占用几个ADC值。他的公式为:
XADC_PerPix = (chx2 - chx1 - (vx1+vx2)/2)/45; //这里本来应该是450,我将他扩大十倍是为了使结果更精确
YADC_PerPix = (chy2 - chy1 - (vy1+vy2)/2)/24; //这里本来应该是242
通过上面的式子我们就可求出每个像素点占用几个ADC值。也就实现了我们触摸屏校准的目的。
实现步骤:
要介绍触摸屏的校准我们就要想一下,触摸屏的校准过程,即先在屏幕的左上角出现一个十字,当我们点了他之后,他获得数据,然后到下一个校准点。直到四个点都点完,获得了四个点的数据,进行比较,如果合格,校准通过,记录数据。那么我们的一个难点就是如何实现在点这个点之前,他会一直在这里等着你点,而当你获取数据之后他在跳到下一个点。并且屏幕的显示也要跟着一起动。 而实现这个功能我们同样使用了状态机的思想。我将我的思路用伪代码表达为:
int count=0;
unsigned char i;
unsigned mode_correct = MODE_COR;
ClearScr(0x0); // 清屏
DrawCross(15,15,16);
while(1){
if(flag_out){ //此处设置标志位,当在case3中符合校准要求我们就要通过该标志位离开while循环
break;
}
switch (count){ //选择校准点
case 0:{
switch(mode_correct){ //选择校准点的状态,分为校准和校准后的下步善后工作
case MODE_COR:{
while(触摸屏没有按下);
关TC总时钟
记录纵向和横向的值
mode_correct = MODE_NEX;
break;
}
case MODE_NEX:{
count++(准备进入下个点)
LCD显示下一个校准点
mode_correct = MODE_COR;
开TC总时钟
break;
}
}
break;
}
case 1:{
switch(mode_correct){
case MODE_COR:{
while(触摸屏没有按下);
关TC总时钟
记录纵向和横向的值
mode_correct = MODE_NEX;
break;
}
case MODE_NEX:{
count++(准备进入下个点)
LCD显示下一个校准点
mode_correct = MODE_COR;
开TC总时钟
break;
}
}
break;
}
case 2:{
switch(mode_correct){
case MODE_COR:{
while(触摸屏没有按下);
关TC总时钟
记录纵向和横向的值
mode_correct = MODE_NEX;
break;
}
case MODE_NEX:{
count++(准备进入下个点)
LCD显示下一个校准点
mode_correct = MODE_COR;
开TC总时钟
break;
}
}
break;
}
case 3:{
switch(mode_correct){
case MODE_COR:{
while(触摸屏没有按下);
关TC总时钟
记录纵向和横向的值
mode_correct = MODE_NEX;
break;
}
case MODE_NEX:{
用AD1与AD2比较他们的纵坐标选出他们中较小的值chy1以及他们的差值vy1
用AD1于AD3比较他们的横坐标选出他们中较小的值chx1以及他们的差值vx1
比较AD4与AD3的纵坐标选出他们中较大的值chy2以及他们的差值vy2
比较AD4与AD2比较他们的横坐标选出他们中较大的值chx2以及他们的差值vx1
如果我们vx1与vx2的差值以及vy1与vy2的差值在误差允许范围内,我们将使用这组值。
如果不在这个范围内我们将重新对触摸屏进行数据采集。
break;
}
}
break;
}
}
}
而其具体的代码为:
void TC_correct(void){
int count=0;
unsigned char i;
unsigned mode_correct = MODE_COR;
ClearScr(0x0); // 清屏
DrawCross(15,15,16);
while(1){
if(flag_out){ //此处设置标志位,当在case3中符合校准要求我们就要通过该标志位离开while循环
break;
}
switch (count){ //选择校准点
case 0:{
switch(mode_correct){ //选择校准点的状态,分为校准和校准后的下步善后工作
case MODE_COR:{
while(!flag_up);
/* 关TC总时钟 */
INTSUBMSK |= (1<<9);
tc_x[count] = ADC_x;
tc_y[count] = ADC_y;
mode_correct = MODE_NEX;
break;
}
case MODE_NEX:{
count++;
ClearScr(0x0); // 清屏
DrawCross(464,15,16);
mode_correct = MODE_COR;
flag_up = 0;
/* 开TC总时钟 */
INTSUBMSK &= ~(1<<9);
break;
}
}
break;
}
case 1:{
switch(mode_correct){
case MODE_COR:{
while(!flag_up);
/* 关TC总时钟 */
INTSUBMSK |= (1<<9);
tc_x[count] = ADC_x;
tc_y[count] = ADC_y;
mode_correct = MODE_NEX;
break;
}
case MODE_NEX:{
count++;
ClearScr(0x0); // 清屏
DrawCross(15,256,16);
mode_correct = MODE_COR;
flag_up = 0;
/* 开TC总时钟 */
INTSUBMSK &= ~(1<<9);
break;
}
}
break;
}
case 2:{
switch(mode_correct){
case MODE_COR:{
while(!flag_up);
/* 关TC总时钟 */
INTSUBMSK |= (1<<9);
tc_x[count] = ADC_x;
tc_y[count] = ADC_y;
mode_correct = MODE_NEX;
break;
}
case MODE_NEX:{
count++;
ClearScr(0x0); // 清屏
DrawCross(464,256,16);
mode_correct = MODE_COR;
flag_up = 0;
/* 开TC总时钟 */
INTSUBMSK &= ~(1<<9);
break;
}
}
break;
}
case 3:{
switch(mode_correct){
case MODE_COR:{
while(!flag_up);
/* 关TC总时钟 */
INTSUBMSK |= (1<<9);
tc_x[count] = ADC_x;
tc_y[count] = ADC_y;
mode_correct = MODE_NEX;
break;
}
case MODE_NEX:{
for(i=0;i<4;i++){
printf("x[%d]= %d,y[%d]=%d \n",i,tc_x[i],i,tc_y[i]);
}
vx1 = tc_x[0]>tc_x[1]?tc_x[0]-tc_x[1]:tc_x[1]-tc_x[0];
chx1 = tc_x[0]>tc_x[1]?tc_x[1]:tc_x[0];
vy1 = tc_y[0]>tc_y[2]?tc_y[0]-tc_y[2]:tc_y[2]-tc_y[0];
chy1 = tc_y[0]>tc_y[2]?tc_y[2]:tc_y[0];
vx2 = tc_x[3]>tc_x[2]?tc_x[3]-tc_x[2]:tc_x[2]-tc_x[3];
chx2 = tc_x[3]>tc_x[2]?tc_x[3]:tc_x[2];
vy2 = tc_y[3]>tc_y[1]?tc_y[3]-tc_y[1]:tc_y[1]-tc_y[3];
chy2 = tc_y[3]>tc_y[1]?tc_y[3]:tc_y[1];
if((vx1>vx2&&vx1>vx2+tp_xiaozhun)||(vx1<vx2&&vx1<vx2-tp_xiaozhun)||(vy1>vy2&&vy1>vy2+tp_xiaozhun)||(vy1<vy2&&vy1<vy2-tp_xiaozhun))
{
count=0;
ClearScr(0x0); // 清屏
DrawCross(15,15,16);
mode_correct = MODE_COR;
flag_up = 0;
/* 开TC总时钟 */
INTSUBMSK &= ~(1<<9);
}else{
//count=0;
ClearScr(0x0); // 清屏
flag_out = 1;
XADC_PerPix = (chx2 - chx1 - (vx1+vx2)/2)/45;
YADC_PerPix = (chy2 - chy1 - (vy1+vy2)/2)/24;
printf("XADC_PerPix = %d,YADC_PerPix =%d \n",XADC_PerPix,YADC_PerPix);
mode_correct = MODE_COR;
flag_up = 0;
/* 开TC总时钟 */
INTSUBMSK &= ~(1<<9);
}
break;
}
}
break;
}
}
}
}
关于触摸屏校准就讲完了。我会在下一篇文章中讲解各个模块的实现。