電阻屏是通過檢測觸點處的電壓來確定位置的,電壓受到電阻材料的影響,而生產中不同批次的電阻材料可能會有偏差,因此需要先定位幾個點來確定屏幕的偏移量 ( 也就是校準 ) ,以后通過校準得來的偏移量調整坐標輸出,才能準確通過電壓反映坐標。而電容屏是直接由多個電容組成的矩陣,檢測時可獲知整個電容矩陣中哪些電容發生了改變,而且各個電容在生產時就確認了它在觸摸屏中的坐標,所以只要獲知哪些電容發生了變化,就可直接得出觸點位置,無須校準。
由于用的是電阻屏,所以這里需要進行校準。
屏幕坐標與AD轉換結果也不是相等的,面是存在一定比例關系,也就是說AD轉換結果與實際屏幕坐標之間是一條不過坐標原點,且斜率不為1的直線,線性關系可表示為:
**LCD_X=KxADC_x+offset_x
LCD_Y=KyADC_y+offset_y *
式中LCD_X和LCD_ Y是LCD屏幕上的坐標位置,ADC_x和ADCy為點擊屏上(LCDX.,LCD Y)時所采集的AD值,Kx和Ky是兩者之同的比例關系,而 offset_x和 offset_y則為兩者相對于坐標原點時的的偏移量。屏幕上點的坐標與其AD值之間的關系可用圖表示:
由圖可知,只要確定線性關系當中的Kx、Ky及 Offset_x和 Offset_y,當獲取到觸摸點對應的AD值時,即可確定觸摸點的位置,比較簡單的方法是采集屏幕的左上角(0,0)位置和右下角(239,319)位置的AD值,假定為(ad_x0,ad_y0)和(ad_x1,ad_y1),有了坐標和AD值,即可確定線性關系,Kx=(ad_x1- ad_x1)/239;Ky=( ade y1-ad_y0)/319,確定了Kx和Ky即可確定 Offset_x和Offset_y,從而最終確定線性關系,以后點擊屏幕任意點,都可根據線性關系確定屏幕坐標位置。
通常,由于LCD屏幕邊界處存在較多的噪點,為了更為準確地校準屏幕,并確定線性關系,往往采用四點校屏法或五點校屏法,但本質都是一樣的。四點校屏即在屏幕靠近四個角落的地方給定四個點,以提示用戶點擊這四點,并假定用戶點擊后所得的AD值就是這四個點的AD值(當然,實際觸摸位置總是會存在一定偏差,若在一定誤差范圍內,則認為觸摸點就是給定點),從而根據給定位置和所得AD值,就可確定線性關系,原理與點擊左上角與右下角是一樣的。至于五點校屏,無非就是在屏幕中心位置又增加一參考點而已 。
先為校準的參數定義一個結構體:
typedef struct{
float kx;
float ky;
float dx;
float dy;
u8 flag; //用來標識,是否校準過
}TouchParam_Typedef;
校準思路:在在屏幕上給出參考的點擊點,當用戶觸摸屏幕時,假定所得的觸點AD值就是參考點的ADC,利用實際所得的AD值和參考坐標,計算出一個線性關系。然后,再把觸點AD值代入所得的線性關系中,計算實際觸點的坐標,若實際坐標與給定坐標滿足的誤差條件,則認為該直線即為線性關系。下面給出校準示意圖:
這里采用四點校屏法,來確定線性關系中的比例系數和偏移量,即屏幕校準。選擇4個參考點,參考點選取越靠邊界,范圍就越大,這樣校準精度就越高,但是越靠近邊界,噪聲就越大,所以我們去距離邊界20的4個點來校準:(20,20)、(220,20)、(20,300)、(220,300)。
void Touch_Adjust()
{
u16 ref_Point[4][2] = {{20,20},{220,20},{20,300},{220,300}};
u16 real_Point[4][2] = {0};
Point_Typedef pt = {0xffff,0xffff};
u16 err[4] = {0};
u8 i = 0;
s8 error = 0;
LCD_ClearScreen(0xffff);
while(1)
{
for(i=0;i< 4;i++)
{
//畫點
//畫水平線
LCD_DrawLine(ref_Point[i][0]-7,ref_Point[i][1],ref_Point[i][0]+7,ref_Point[i][1],RED);
//畫垂直線
LCD_DrawLine(ref_Point[i][0],ref_Point[i][1]-7,ref_Point[i][0],ref_Point[i][1]+7,RED);
//畫圓圈
LCD_DrawCircle(ref_Point[i][0],ref_Point[i][1],5,RED);
while(GPIOB- >IDR & (1< 1)){} //未觸摸
Delay_ms(30);
while(GPIOB- >IDR & (1< 1)){} //未觸摸
//暫時把采用的ADC值,存儲到real_Point中
pt = Touch_GetPointADC();
real_Point[i][0] = pt.x;
real_Point[i][1] = pt.y;
printf("%d,%drn",real_Point[i][0],real_Point[i][1]);
//等待松手
while((GPIOB- >IDR & (1< 1)) == 0){}
//清除已觸摸的點
LCD_DrawLine(ref_Point[i][0]-7,ref_Point[i][1],ref_Point[i][0]+7,ref_Point[i][1],0xffff);
//畫垂直線
LCD_DrawLine(ref_Point[i][0],ref_Point[i][1]-7,ref_Point[i][0],ref_Point[i][1]+7,0xffff);
//畫圓圈
LCD_DrawCircle(ref_Point[i][0],ref_Point[i][1],5,0xffff);
}
//4個點的ADC值已經獲取,根據AD值和參考坐標,求線性關系
//利用第1點和第4點,求線性關系
/*
ref_Point[0][0] = Kx * real_Point[0][0] + dx; (1)
ref_Point[3][0] = Kx * real_Point[3][0] + dx; (2)
由(1)和(2)可得:
Kx = (float)(ref_Point[3][0] - ref_Point[0][0])/(real_Point[3][0] - real_Point[0][0]);
dx = (float)(ref_Point[3][0]*real_Point[0][0] - ref_Point[0][0]*real_Point[3][0])/(real_Point[0][0] - real_Point[3][0]);
ref_Point[0][1] = Ky * real_Point[0][1] + dy; (3)
ref_Point[3][1] = Ky * real_Point[3][1] + dy; (4)
由(3)和(4)可得:
Ky = (float)(ref_Point[3][1] - ref_Point[0][1])/(real_Point[3][1] - real_Point[0][1]);
dy = (float)(ref_Point[3][1]*real_Point[0][1] - ref_Point[0][1]*real_Point[3][1])/(real_Point[0][1] - real_Point[3][1]);
*/
touchParam.kx = (float)(ref_Point[3][0] - ref_Point[0][0])/(real_Point[3][0] - real_Point[0][0]);
touchParam.dx = (float)(ref_Point[3][0]*real_Point[0][0] - ref_Point[0][0]*real_Point[3][0])/(real_Point[0][0] - real_Point[3][0]);
touchParam.ky = (float)(ref_Point[3][1] - ref_Point[0][1])/(real_Point[3][1] - real_Point[0][1]);
touchParam.dy = (float)(ref_Point[3][1]*real_Point[0][1] - ref_Point[0][1]*real_Point[3][1])/(real_Point[0][1] - real_Point[3][1]);
//利用計算的線性關系,求實際觸點坐標
for(i=0;i< 4;i++)
{
real_Point[i][0] = touchParam.kx * real_Point[i][0] + touchParam.dx;
real_Point[i][1] = touchParam.ky * real_Point[i][1] + touchParam.dy;
printf("(%d,%d)rn",real_Point[i][0],real_Point[i][1] );
}
//引入誤差條件(如果4個實際點都在小圓內,此認為校準通過,)
for(i=0;i< 4;i++)
{
//這里計算距離平方
err[i] = (real_Point[i][0] - ref_Point[i][0]) * (real_Point[i][0] - ref_Point[i][0])
+ (real_Point[i][1] - ref_Point[i][1]) * (real_Point[i][1] - ref_Point[i][1]);
printf("err[%d]=%drn",i,err[i]);
}
//誤差判斷
if(err[0]< 100 && err[1]< 100 && err[2]< 100 && err[3]< 100)
{
//認為校準通過(存儲到24c02或w25q64中,以24c02為例)
touchParam.flag = 0xaa;
AT24C02_ContinueWrite(TOUCH_PARAM_ADD,(u8 *)&touchParam,sizeof(touchParam),&error);
break;
}
}
//校準通過
LCD_Print(50,160,(u8 *)"Adjust ok",0,0xffff,1,24);
Delay_ms(3000);
LCD_ClearScreen(0xffff);
}
校準函數編寫完成,需要添加到觸摸屏初始化函數中,初始化GPIO時判斷是否已經校準過,沒有校準過就進行校準。
void Touch_Init()
{
s8 error = 0;
Touch_gpio_Init();
AT24C02_ContinueRead(TOUCH_PARAM_ADD,(u8 *)&touchParam,sizeof(touchParam),&error);
if(touchParam.flag != 0x77) //沒有校準
Touch_Adjust();
}
接著編寫主函數進行測試。
#include "stm32f4xx.h"
#include "usart.h"
#include "delay.h"
#include "stdio.h"
#include "touch.h"
#include "AT24C02.h"
#include "ili9341.h"
#include "lcd.h"
int main()
{
Usart1_Init(115200);
AT24C02_Init();
LCD_Init();
Touch_Init();
while(1)
{}
}
運行后,顯示一個校準點,點擊該點校準后,第一個校準點被擦除,顯示第二個校準點,這樣依次校準四個校準點,校準結果在誤差允許范圍內,屏幕顯示Adjust ok,延時3秒后,屏幕被清為白色,觸摸屏校準成功,接下來就可以在校準好的基礎上在觸摸屏上做其他應用了。
測試結果如下圖所示:
評論
查看更多