嵌入式单片机高级篇(二)Stm32F103电阻触摸屏
Stm32F103電阻觸摸屏
1、原理:
lcd屏與觸摸屏中間有支撐點將兩層導電涂料隔開,當某點按下時,使得兩層涂料接觸,平常絕緣的兩層導電層在觸摸點位置就有了一個接觸,控制器偵測到這個接通后,其中一面導電層接通У軸方向的5Ⅴ均勻電壓場,另一導電層將接觸點的電壓引至控制卡進行A/D轉換,得到電壓值后與5Ⅴ相比即可得觸摸點的у軸坐標,同理得出Χ軸的坐標
2、驅動芯片XPT2046:
(1)采用SPI三線制通信接口
(2)原理框圖:
利用控制邏輯控制四個MOS管導通和截止,從而實現測量按下的x軸坐標以及按下的Y軸坐標(XN,YN,XP,YP接觸摸屏)
(3)管腳:
BUSY:高電平,忙
~CS:低電平有效
DIN:上升沿采集數據
DCLK:時鐘線
~PENIRQ:筆接觸引腳,低電平有效
DOUT:下降沿準備數據
(4)時序圖:
順序圖:
時序圖:
第一,確認初始狀態:
~CS:高電平
DCLK:低電平
DIN:低電平
第二,MCU發送8位數據給XPT2046,XPT2046返回16位數據給MCU
根據時序圖寫偽代碼:
(5)命令:
測量Y軸坐標:ox90;測量X軸坐標:0xD0;
3、觸摸屏的驅動程序思路:
(1)硬件連接:
(2)程序設計思路:
4、電阻式觸摸屏校正:
(1)目的:為了讓觸摸屏和LCD屏建立連接關系,在觸摸屏上對應圖標按下,表示當前真的在圖標位置按下。
(2)LCD和觸摸屏屬于線性坐標系
(3)LCD屏與觸摸屏的線性坐標系的原點不在同一位置,且線性關系不一樣
(4)LCD坐標:x:0-320 y:0-480;觸摸屏坐標:x:0-4096 y:0-4096
(5)當前兩個坐標系中,X=ax+b;Y=cy+d;需要計算的值為a、b、c和d。利用一條橫線的兩個點可求出a和b;利用一條豎線的兩個點可求出c和d。
計算公式,求a、b為例(c、d同理):X1=ax1+b;X2=ax2+b;a=(X1-X2)/(x1-x2);b=((X1-X2)-a(x1-x2))/2
具體程序:
(6)讀取xy坐標取平均值程序(讀取5次舍棄最小值與最大值取平均從而提高精度):
u16 TP_Read_XOY(u8 xy) {u16 i, j;u16 buf[5];u16 sum=0;u16 temp;for(i=0;i<5;i++)buf[i]=TP_Read_AD(xy); for(i=0;i<4; i++)//排序{for(j=i+1;j<5;j++){if(buf[i]>buf[j])//升序排列{temp=buf[i];buf[i]=buf[j];buf[j]=temp;}}} sum=0;for(i=1;i<5-1;i++)sum+=buf[i];temp=sum/(3);return temp; }(7)限定誤差范圍提高精度程序:
//讀取x,y坐標 //最小值不能少于100. //x,y:讀取到的坐標值 //返回值:0,失敗;1,成功。 u8 TP_Read_XY(u16 *x,u16 *y) {u16 xtemp,ytemp; xtemp=TP_Read_XOY(CMD_RDX);ytemp=TP_Read_XOY(CMD_RDY); //if(xtemp<100||ytemp<100)return 0;//讀數失敗*x=xtemp;*y=ytemp;return 1;//讀數成功 } //連續2次讀取觸摸屏IC,且這兩次的偏差不能超過 //ERR_RANGE,滿足條件,則認為讀數正確,否則讀數錯誤. //該函數能大大提高準確度 //x,y:讀取到的坐標值 //返回值:0,失敗;1,成功。 #define ERR_RANGE 50 //誤差范圍 u8 TP_Read_XY2(u16 *x,u16 *y) {u16 x1,y1;u16 x2,y2;u8 flag; flag=TP_Read_XY(&x1,&y1); if(flag==0)return(0);flag=TP_Read_XY(&x2,&y2); if(flag==0)return(0); if(((x2<=x1&&x1<x2+ERR_RANGE)||(x1<=x2&&x2<x1+ERR_RANGE))//前后兩次采樣在+-50內&&((y2<=y1&&y1<y2+ERR_RANGE)||(y1<=y2&&y2<y1+ERR_RANGE))){*x=(x1+x2)/2;*y=(y1+y2)/2;return 1;}else return 0; }(8)觸摸按鍵掃描程序:
u8 TP_Scan(u8 tp) { if(PEN==0)//有按鍵按下{if(tp)TP_Read_XY2(&tp_dev.x[0],&tp_dev.y[0]);//讀取物理坐標else if(TP_Read_XY2(&tp_dev.x[0],&tp_dev.y[0]))//讀取屏幕坐標{tp_dev.x[0]=tp_dev.xfac*tp_dev.x[0]+tp_dev.xoff;//將結果轉換為屏幕坐標tp_dev.y[0]=tp_dev.yfac*tp_dev.y[0]+tp_dev.yoff; } if((tp_dev.sta&TP_PRES_DOWN)==0)//之前沒有被按下{ tp_dev.sta=TP_PRES_DOWN|TP_CATH_PRES;//按鍵按下 tp_dev.x[4]=tp_dev.x[0];//記錄第一次按下時的坐標tp_dev.y[4]=tp_dev.y[0]; } }else{if(tp_dev.sta&TP_PRES_DOWN)//之前是被按下的{tp_dev.sta&=~(1<<7);//標記按鍵松開 }else//之前就沒有被按下{tp_dev.x[4]=0;tp_dev.y[4]=0;tp_dev.x[0]=0xffff;tp_dev.y[0]=0xffff;} }return tp_dev.sta&TP_PRES_DOWN;//返回當前的觸屏狀態 }(9)電阻觸摸屏校正程序:
void TP_Adjust(void) { u16 pos_temp[4][2];//坐標緩存值u8 cnt=0; u16 d1,d2;u32 tem1,tem2;double fac; u16 outtime=0;cnt=0; POINT_COLOR=BLUE;BACK_COLOR =WHITE;LCD_Clear(WHITE);//清屏 POINT_COLOR=RED;//紅色 LCD_Clear(WHITE);//清屏 POINT_COLOR=BLACK;LCD_ShowString(40,40,160,100,16,(u8*)TP_REMIND_MSG_TBL);//顯示提示信息TP_Drow_Touch_Point(20,20,RED);//畫點1 tp_dev.sta=0;//消除觸發信號 tp_dev.xfac=0;//xfac用來標記是否校準過,所以校準之前必須清掉!以免錯誤 while(1)//如果連續10秒鐘沒有按下,則自動退出{tp_dev.scan(1);//掃描物理坐標if((tp_dev.sta&0xc0)==TP_CATH_PRES)//按鍵按下了一次(此時按鍵松開了.){ outtime=0; tp_dev.sta&=~(1<<6);//標記按鍵已經被處理過了.pos_temp[cnt][0]=tp_dev.x[0];pos_temp[cnt][1]=tp_dev.y[0];cnt++; switch(cnt){ case 1: TP_Drow_Touch_Point(20,20,WHITE); //清除點1 TP_Drow_Touch_Point(lcddev.width-20,20,RED); //畫點2break;case 2:TP_Drow_Touch_Point(lcddev.width-20,20,WHITE); //清除點2TP_Drow_Touch_Point(20,lcddev.height-20,RED); //畫點3break;case 3:TP_Drow_Touch_Point(20,lcddev.height-20,WHITE); //清除點3TP_Drow_Touch_Point(lcddev.width-20,lcddev.height-20,RED); //畫點4break;case 4: //全部四個點已經得到//對邊相等tem1=abs(pos_temp[0][0]-pos_temp[1][0]);//x1-x2tem2=abs(pos_temp[0][1]-pos_temp[1][1]);//y1-y2tem1*=tem1;tem2*=tem2;d1=sqrt(tem1+tem2);//得到1,2的距離tem1=abs(pos_temp[2][0]-pos_temp[3][0]);//x3-x4tem2=abs(pos_temp[2][1]-pos_temp[3][1]);//y3-y4tem1*=tem1;tem2*=tem2;d2=sqrt(tem1+tem2);//得到3,4的距離fac=(float)d1/d2;if(fac<0.95||fac>1.05||d1==0||d2==0)//不合格{cnt=0;TP_Drow_Touch_Point(lcddev.width-20,lcddev.height-20,WHITE); //清除點4TP_Drow_Touch_Point(20,20,RED); //畫點1TP_Adj_Info_Show(pos_temp[0][0],pos_temp[0][1],pos_temp[1][0],pos_temp[1][1],pos_temp[2][0],pos_temp[2][1],pos_temp[3][0],pos_temp[3][1],fac*100);//顯示數據 continue;}tem1=abs(pos_temp[0][0]-pos_temp[2][0]);//x1-x3tem2=abs(pos_temp[0][1]-pos_temp[2][1]);//y1-y3tem1*=tem1;tem2*=tem2;d1=sqrt(tem1+tem2);//得到1,3的距離tem1=abs(pos_temp[1][0]-pos_temp[3][0]);//x2-x4tem2=abs(pos_temp[1][1]-pos_temp[3][1]);//y2-y4tem1*=tem1;tem2*=tem2;d2=sqrt(tem1+tem2);//得到2,4的距離fac=(float)d1/d2;if(fac<0.95||fac>1.05)//不合格{cnt=0;TP_Drow_Touch_Point(lcddev.width-20,lcddev.height-20,WHITE); //清除點4TP_Drow_Touch_Point(20,20,RED); //畫點1TP_Adj_Info_Show(pos_temp[0][0],pos_temp[0][1],pos_temp[1][0],pos_temp[1][1],pos_temp[2][0],pos_temp[2][1],pos_temp[3][0],pos_temp[3][1],fac*100);//顯示數據 continue;}//正確了//對角線相等tem1=abs(pos_temp[1][0]-pos_temp[2][0]);//x1-x3tem2=abs(pos_temp[1][1]-pos_temp[2][1]);//y1-y3tem1*=tem1;tem2*=tem2;d1=sqrt(tem1+tem2);//得到1,4的距離tem1=abs(pos_temp[0][0]-pos_temp[3][0]);//x2-x4tem2=abs(pos_temp[0][1]-pos_temp[3][1]);//y2-y4tem1*=tem1;tem2*=tem2;d2=sqrt(tem1+tem2);//得到2,3的距離fac=(float)d1/d2;if(fac<0.95||fac>1.05)//不合格{cnt=0;TP_Drow_Touch_Point(lcddev.width-20,lcddev.height-20,WHITE); //清除點4TP_Drow_Touch_Point(20,20,RED); //畫點1TP_Adj_Info_Show(pos_temp[0][0],pos_temp[0][1],pos_temp[1][0],pos_temp[1][1],pos_temp[2][0],pos_temp[2][1],pos_temp[3][0],pos_temp[3][1],fac*100);//顯示數據 continue;}//正確了//計算結果tp_dev.xfac=(float)(lcddev.width-40)/(pos_temp[1][0]-pos_temp[0][0]);//得到xfac tp_dev.xoff=(lcddev.width-tp_dev.xfac*(pos_temp[1][0]+pos_temp[0][0]))/2;//得到xofftp_dev.yfac=(float)(lcddev.height-40)/(pos_temp[2][1]-pos_temp[0][1]);//得到yfactp_dev.yoff=(lcddev.height-tp_dev.yfac*(pos_temp[2][1]+pos_temp[0][1]))/2;//得到yoff if(abs(tp_dev.xfac)>2||abs(tp_dev.yfac)>2)//觸屏和預設的相反了.{cnt=0;TP_Drow_Touch_Point(lcddev.width-20,lcddev.height-20,WHITE); //清除點4TP_Drow_Touch_Point(20,20,RED); //畫點1LCD_ShowString(40,26,lcddev.width,lcddev.height,16,"TP Need readjust!");tp_dev.touchtype=!tp_dev.touchtype;//修改觸屏類型.if(tp_dev.touchtype)//X,Y方向與屏幕相反{CMD_RDX=0X90;CMD_RDY=0XD0; }else //X,Y方向與屏幕相同{CMD_RDX=0XD0;CMD_RDY=0X90; } continue;} POINT_COLOR=BLUE;LCD_Clear(WHITE);//清屏LCD_ShowString(35,110,lcddev.width,lcddev.height,16,"Touch Screen Adjust OK!");//校正完成delay_ms(1000);TP_Save_Adjdata(); LCD_Clear(WHITE);//清屏 return;//校正完成 }}delay_ms(10);outtime++;if(outtime>1000){TP_Get_Adjdata();break;} } }5、程序源碼(正點原子):
(1)touch.h
(2)touch.c
#include "touch.h" #include "lcd.h" #include "delay.h" #include "stdlib.h" #include "math.h" #include "24cxx.h" _m_tp_dev tp_dev= {TP_Init,TP_Scan,TP_Adjust,0,0, 0,0,0,0, 0,0, }; //默認為touchtype=0的數據. u8 CMD_RDX=0XD0; u8 CMD_RDY=0X90;//SPI寫數據 //向觸摸屏IC寫入1byte數據 //num:要寫入的數據 void TP_Write_Byte(u8 num) { u8 count=0; for(count=0;count<8;count++) { if(num&0x80)TDIN=1; else TDIN=0; num<<=1; TCLK=0; delay_us(1);TCLK=1; //上升沿有效 } } //SPI讀數據 //從觸摸屏IC讀取adc值 //CMD:指令 //返回值:讀到的數據 u16 TP_Read_AD(u8 CMD) { u8 count=0; u16 Num=0; TCLK=0; //先拉低時鐘 TDIN=0; //拉低數據線TCS=0; //選中觸摸屏ICTP_Write_Byte(CMD);//發送命令字delay_us(6);//ADS7846的轉換時間最長為6usTCLK=0; delay_us(1); TCLK=1; //給1個時鐘,清除BUSYdelay_us(1); TCLK=0; for(count=0;count<16;count++)//讀出16位數據,只有高12位有效 { Num<<=1; TCLK=0; //下降沿有效 delay_us(1); TCLK=1;if(DOUT)Num++; } Num>>=4; //只有高12位有效.TCS=1; //釋放片選 return(Num); } //讀取一個坐標值(x或者y) //連續讀取READ_TIMES次數據,對這些數據升序排列, //然后去掉最低和最高LOST_VAL個數,取平均值 //xy:指令(CMD_RDX/CMD_RDY) //返回值:讀到的數據 u16 TP_Read_XOY(u8 xy) {u16 i, j;u16 buf[5];u16 sum=0;u16 temp;for(i=0;i<5;i++)buf[i]=TP_Read_AD(xy); for(i=0;i<4; i++)//排序{for(j=i+1;j<5;j++){if(buf[i]>buf[j])//升序排列{temp=buf[i];buf[i]=buf[j];buf[j]=temp;}}} sum=0;for(i=1;i<5-1;i++)sum+=buf[i];temp=sum/(3);return temp; } //讀取x,y坐標 //最小值不能少于100. //x,y:讀取到的坐標值 //返回值:0,失敗;1,成功。 u8 TP_Read_XY(u16 *x,u16 *y) {u16 xtemp,ytemp; xtemp=TP_Read_XOY(CMD_RDX);ytemp=TP_Read_XOY(CMD_RDY); //if(xtemp<100||ytemp<100)return 0;//讀數失敗*x=xtemp;*y=ytemp;return 1;//讀數成功 } //連續2次讀取觸摸屏IC,且這兩次的偏差不能超過 //ERR_RANGE,滿足條件,則認為讀數正確,否則讀數錯誤. //該函數能大大提高準確度 //x,y:讀取到的坐標值 //返回值:0,失敗;1,成功。 #define ERR_RANGE 50 //誤差范圍 u8 TP_Read_XY2(u16 *x,u16 *y) {u16 x1,y1;u16 x2,y2;u8 flag; flag=TP_Read_XY(&x1,&y1); if(flag==0)return(0);flag=TP_Read_XY(&x2,&y2); if(flag==0)return(0); if(((x2<=x1&&x1<x2+ERR_RANGE)||(x1<=x2&&x2<x1+ERR_RANGE))//前后兩次采樣在+-50內&&((y2<=y1&&y1<y2+ERR_RANGE)||(y1<=y2&&y2<y1+ERR_RANGE))){*x=(x1+x2)/2;*y=(y1+y2)/2;return 1;}else return 0; } // //與LCD部分有關的函數 //畫一個觸摸點 //用來校準用的 //x,y:坐標 //color:顏色 void TP_Drow_Touch_Point(u16 x,u16 y,u16 color) {POINT_COLOR=color;LCD_DrawLine(x-12,y,x+13,y);//橫線LCD_DrawLine(x,y-12,x,y+13);//豎線LCD_DrawPoint(x+1,y+1);LCD_DrawPoint(x-1,y+1);LCD_DrawPoint(x+1,y-1);LCD_DrawPoint(x-1,y-1);LCD_Draw_Circle(x,y,6);//畫中心圈 } //畫一個大點(2*2的點) //x,y:坐標 //color:顏色 void TP_Draw_Big_Point(u16 x,u16 y,u16 color) { POINT_COLOR=color;LCD_DrawPoint(x,y);//中心點 LCD_DrawPoint(x+1,y);LCD_DrawPoint(x,y+1);LCD_DrawPoint(x+1,y+1); } // //觸摸按鍵掃描 //tp:0,屏幕坐標;1,物理坐標(校準等特殊場合用) //返回值:當前觸屏狀態. //0,觸屏無觸摸;1,觸屏有觸摸 u8 TP_Scan(u8 tp) { if(PEN==0)//有按鍵按下{if(tp)TP_Read_XY2(&tp_dev.x[0],&tp_dev.y[0]);//讀取物理坐標else if(TP_Read_XY2(&tp_dev.x[0],&tp_dev.y[0]))//讀取屏幕坐標{tp_dev.x[0]=tp_dev.xfac*tp_dev.x[0]+tp_dev.xoff;//將結果轉換為屏幕坐標tp_dev.y[0]=tp_dev.yfac*tp_dev.y[0]+tp_dev.yoff; } if((tp_dev.sta&TP_PRES_DOWN)==0)//之前沒有被按下{ tp_dev.sta=TP_PRES_DOWN|TP_CATH_PRES;//按鍵按下 tp_dev.x[4]=tp_dev.x[0];//記錄第一次按下時的坐標tp_dev.y[4]=tp_dev.y[0]; } }else{if(tp_dev.sta&TP_PRES_DOWN)//之前是被按下的{tp_dev.sta&=~(1<<7);//標記按鍵松開 }else//之前就沒有被按下{tp_dev.x[4]=0;tp_dev.y[4]=0;tp_dev.x[0]=0xffff;tp_dev.y[0]=0xffff;} }return tp_dev.sta&TP_PRES_DOWN;//返回當前的觸屏狀態 } // //保存在EEPROM里面的地址區間基址,占用14個字節(RANGE:SAVE_ADDR_BASE~SAVE_ADDR_BASE+13) #define SAVE_ADDR_BASE 40 //保存校準參數 void TP_Save_Adjdata(void) {AT24CXX_Write(SAVE_ADDR_BASE,(u8*)&tp_dev.xfac,14); //強制保存&tp_dev.xfac地址開始的14個字節數據,即保存到tp_dev.touchtypeAT24CXX_WriteOneByte(SAVE_ADDR_BASE+14,0X0A); //在最后,寫0X0A標記校準過了 } //得到保存在EEPROM里面的校準值 //返回值:1,成功獲取數據 // 0,獲取失敗,要重新校準 u8 TP_Get_Adjdata(void) { u8 temp;temp=AT24CXX_ReadOneByte(SAVE_ADDR_BASE+14);//讀取標記字,看是否校準過! if(temp==0X0A)//觸摸屏已經校準過了 { AT24CXX_Read(SAVE_ADDR_BASE,(u8*)&tp_dev.xfac,14);//讀取之前保存的校準數據 if(tp_dev.touchtype)//X,Y方向與屏幕相反{CMD_RDX=0X90;CMD_RDY=0XD0; }else //X,Y方向與屏幕相同{CMD_RDX=0XD0;CMD_RDY=0X90; } return 1; }return 0; } //提示字符串 u8* const TP_REMIND_MSG_TBL="Please use the stylus click the cross on the screen.The cross will always move until the screen adjustment is completed.";//提示校準結果(各個參數) void TP_Adj_Info_Show(u16 x0,u16 y0,u16 x1,u16 y1,u16 x2,u16 y2,u16 x3,u16 y3,u16 fac) { POINT_COLOR=RED;LCD_ShowString(40,160,lcddev.width,lcddev.height,16,"x1:");LCD_ShowString(40+80,160,lcddev.width,lcddev.height,16,"y1:");LCD_ShowString(40,180,lcddev.width,lcddev.height,16,"x2:");LCD_ShowString(40+80,180,lcddev.width,lcddev.height,16,"y2:");LCD_ShowString(40,200,lcddev.width,lcddev.height,16,"x3:");LCD_ShowString(40+80,200,lcddev.width,lcddev.height,16,"y3:");LCD_ShowString(40,220,lcddev.width,lcddev.height,16,"x4:");LCD_ShowString(40+80,220,lcddev.width,lcddev.height,16,"y4:"); LCD_ShowString(40,240,lcddev.width,lcddev.height,16,"fac is:"); LCD_ShowNum(40+24,160,x0,4,16); //顯示數值LCD_ShowNum(40+24+80,160,y0,4,16); //顯示數值LCD_ShowNum(40+24,180,x1,4,16); //顯示數值LCD_ShowNum(40+24+80,180,y1,4,16); //顯示數值LCD_ShowNum(40+24,200,x2,4,16); //顯示數值LCD_ShowNum(40+24+80,200,y2,4,16); //顯示數值LCD_ShowNum(40+24,220,x3,4,16); //顯示數值LCD_ShowNum(40+24+80,220,y3,4,16); //顯示數值LCD_ShowNum(40+56,240,fac,3,16); //顯示數值,該數值必須在95~105范圍之內.}//觸摸屏校準代碼 //得到四個校準參數 void TP_Adjust(void) { u16 pos_temp[4][2];//坐標緩存值u8 cnt=0; u16 d1,d2;u32 tem1,tem2;double fac; u16 outtime=0;cnt=0; POINT_COLOR=BLUE;BACK_COLOR =WHITE;LCD_Clear(WHITE);//清屏 POINT_COLOR=RED;//紅色 LCD_Clear(WHITE);//清屏 POINT_COLOR=BLACK;LCD_ShowString(40,40,160,100,16,(u8*)TP_REMIND_MSG_TBL);//顯示提示信息TP_Drow_Touch_Point(20,20,RED);//畫點1 tp_dev.sta=0;//消除觸發信號 tp_dev.xfac=0;//xfac用來標記是否校準過,所以校準之前必須清掉!以免錯誤 while(1)//如果連續10秒鐘沒有按下,則自動退出{tp_dev.scan(1);//掃描物理坐標if((tp_dev.sta&0xc0)==TP_CATH_PRES)//按鍵按下了一次(此時按鍵松開了.){ outtime=0; tp_dev.sta&=~(1<<6);//標記按鍵已經被處理過了.pos_temp[cnt][0]=tp_dev.x[0];pos_temp[cnt][1]=tp_dev.y[0];cnt++; switch(cnt){ case 1: TP_Drow_Touch_Point(20,20,WHITE); //清除點1 TP_Drow_Touch_Point(lcddev.width-20,20,RED); //畫點2break;case 2:TP_Drow_Touch_Point(lcddev.width-20,20,WHITE); //清除點2TP_Drow_Touch_Point(20,lcddev.height-20,RED); //畫點3break;case 3:TP_Drow_Touch_Point(20,lcddev.height-20,WHITE); //清除點3TP_Drow_Touch_Point(lcddev.width-20,lcddev.height-20,RED); //畫點4break;case 4: //全部四個點已經得到//對邊相等tem1=abs(pos_temp[0][0]-pos_temp[1][0]);//x1-x2tem2=abs(pos_temp[0][1]-pos_temp[1][1]);//y1-y2tem1*=tem1;tem2*=tem2;d1=sqrt(tem1+tem2);//得到1,2的距離tem1=abs(pos_temp[2][0]-pos_temp[3][0]);//x3-x4tem2=abs(pos_temp[2][1]-pos_temp[3][1]);//y3-y4tem1*=tem1;tem2*=tem2;d2=sqrt(tem1+tem2);//得到3,4的距離fac=(float)d1/d2;if(fac<0.95||fac>1.05||d1==0||d2==0)//不合格{cnt=0;TP_Drow_Touch_Point(lcddev.width-20,lcddev.height-20,WHITE); //清除點4TP_Drow_Touch_Point(20,20,RED); //畫點1TP_Adj_Info_Show(pos_temp[0][0],pos_temp[0][1],pos_temp[1][0],pos_temp[1][1],pos_temp[2][0],pos_temp[2][1],pos_temp[3][0],pos_temp[3][1],fac*100);//顯示數據 continue;}tem1=abs(pos_temp[0][0]-pos_temp[2][0]);//x1-x3tem2=abs(pos_temp[0][1]-pos_temp[2][1]);//y1-y3tem1*=tem1;tem2*=tem2;d1=sqrt(tem1+tem2);//得到1,3的距離tem1=abs(pos_temp[1][0]-pos_temp[3][0]);//x2-x4tem2=abs(pos_temp[1][1]-pos_temp[3][1]);//y2-y4tem1*=tem1;tem2*=tem2;d2=sqrt(tem1+tem2);//得到2,4的距離fac=(float)d1/d2;if(fac<0.95||fac>1.05)//不合格{cnt=0;TP_Drow_Touch_Point(lcddev.width-20,lcddev.height-20,WHITE); //清除點4TP_Drow_Touch_Point(20,20,RED); //畫點1TP_Adj_Info_Show(pos_temp[0][0],pos_temp[0][1],pos_temp[1][0],pos_temp[1][1],pos_temp[2][0],pos_temp[2][1],pos_temp[3][0],pos_temp[3][1],fac*100);//顯示數據 continue;}//正確了//對角線相等tem1=abs(pos_temp[1][0]-pos_temp[2][0]);//x1-x3tem2=abs(pos_temp[1][1]-pos_temp[2][1]);//y1-y3tem1*=tem1;tem2*=tem2;d1=sqrt(tem1+tem2);//得到1,4的距離tem1=abs(pos_temp[0][0]-pos_temp[3][0]);//x2-x4tem2=abs(pos_temp[0][1]-pos_temp[3][1]);//y2-y4tem1*=tem1;tem2*=tem2;d2=sqrt(tem1+tem2);//得到2,3的距離fac=(float)d1/d2;if(fac<0.95||fac>1.05)//不合格{cnt=0;TP_Drow_Touch_Point(lcddev.width-20,lcddev.height-20,WHITE); //清除點4TP_Drow_Touch_Point(20,20,RED); //畫點1TP_Adj_Info_Show(pos_temp[0][0],pos_temp[0][1],pos_temp[1][0],pos_temp[1][1],pos_temp[2][0],pos_temp[2][1],pos_temp[3][0],pos_temp[3][1],fac*100);//顯示數據 continue;}//正確了//計算結果tp_dev.xfac=(float)(lcddev.width-40)/(pos_temp[1][0]-pos_temp[0][0]);//得到xfac tp_dev.xoff=(lcddev.width-tp_dev.xfac*(pos_temp[1][0]+pos_temp[0][0]))/2;//得到xofftp_dev.yfac=(float)(lcddev.height-40)/(pos_temp[2][1]-pos_temp[0][1]);//得到yfactp_dev.yoff=(lcddev.height-tp_dev.yfac*(pos_temp[2][1]+pos_temp[0][1]))/2;//得到yoff if(abs(tp_dev.xfac)>2||abs(tp_dev.yfac)>2)//觸屏和預設的相反了.{cnt=0;TP_Drow_Touch_Point(lcddev.width-20,lcddev.height-20,WHITE); //清除點4TP_Drow_Touch_Point(20,20,RED); //畫點1LCD_ShowString(40,26,lcddev.width,lcddev.height,16,"TP Need readjust!");tp_dev.touchtype=!tp_dev.touchtype;//修改觸屏類型.if(tp_dev.touchtype)//X,Y方向與屏幕相反{CMD_RDX=0X90;CMD_RDY=0XD0; }else //X,Y方向與屏幕相同{CMD_RDX=0XD0;CMD_RDY=0X90; } continue;} POINT_COLOR=BLUE;LCD_Clear(WHITE);//清屏LCD_ShowString(35,110,lcddev.width,lcddev.height,16,"Touch Screen Adjust OK!");//校正完成delay_ms(1000);TP_Save_Adjdata(); LCD_Clear(WHITE);//清屏 return;//校正完成 }}delay_ms(10);outtime++;if(outtime>1000){TP_Get_Adjdata();break;} } } //觸摸屏初始化 //返回值:0,沒有進行校準 // 1,進行過校準 u8 TP_Init(void) { GPIO_InitTypeDef GPIO_InitStructure;//注意,時鐘使能之后,對GPIO的操作才有效//所以上拉之前,必須使能時鐘.才能實現真正的上拉輸出RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOF, ENABLE); //使能PB,PF端口時鐘GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1; // PB1端口配置GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽輸出GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOB, &GPIO_InitStructure);//B1推挽輸出GPIO_SetBits(GPIOB,GPIO_Pin_1);//上拉GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; // PB2端口配置GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //上拉輸入GPIO_Init(GPIOB, &GPIO_InitStructure);//B2上拉輸入GPIO_SetBits(GPIOB,GPIO_Pin_2);//上拉 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11|GPIO_Pin_9; // F9,PF11端口配置GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽輸出GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOF, &GPIO_InitStructure);//PF9,PF11推挽輸出GPIO_SetBits(GPIOF, GPIO_Pin_11|GPIO_Pin_9);//上拉GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; // PF10端口配置GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //上拉輸入GPIO_Init(GPIOF, &GPIO_InitStructure);//PF10上拉輸入GPIO_SetBits(GPIOF,GPIO_Pin_10);//上拉 TP_Read_XY(&tp_dev.x[0],&tp_dev.y[0]);//第一次讀取初始化 AT24CXX_Init(); //初始化24CXXif(TP_Get_Adjdata())return 0;//已經校準else //未校準?{ LCD_Clear(WHITE); //清屏TP_Adjust(); //屏幕校準 } TP_Get_Adjdata(); return 1; }(3)main.c
#include "led.h" #include "delay.h" #include "key.h" #include "sys.h" #include "lcd.h" #include "usart.h" #include "24cxx.h" #include "w25qxx.h" #include "touch.h" //清空屏幕并在右上角顯示"RST" void Load_Drow_Dialog(void) {LCD_Clear(WHITE); //清屏 POINT_COLOR=BLUE; //設置字體為藍色 LCD_ShowString(lcddev.width-24,0,200,16,16,"RST");//顯示清屏區域POINT_COLOR=RED; //設置畫筆藍色 } //電容觸摸屏專有部分 //畫水平線 //x0,y0:坐標 //len:線長度 //color:顏色 void gui_draw_hline(u16 x0,u16 y0,u16 len,u16 color) {if(len==0)return;LCD_Fill(x0,y0,x0+len-1,y0,color); } //畫實心圓 //x0,y0:坐標 //r:半徑 //color:顏色 void gui_fill_circle(u16 x0,u16 y0,u16 r,u16 color) { u32 i;u32 imax = ((u32)r*707)/1000+1;u32 sqmax = (u32)r*(u32)r+(u32)r/2;u32 x=r;gui_draw_hline(x0-r,y0,2*r,color);for (i=1;i<=imax;i++) {if ((i*i+x*x)>sqmax)// draw lines from outside {if (x>imax) {gui_draw_hline (x0-i+1,y0+x,2*(i-1),color);gui_draw_hline (x0-i+1,y0-x,2*(i-1),color);}x--;}// draw lines from inside (center) gui_draw_hline(x0-x,y0+i,2*x,color);gui_draw_hline(x0-x,y0-i,2*x,color);} } //兩個數之差的絕對值 //x1,x2:需取差值的兩個數 //返回值:|x1-x2| u16 my_abs(u16 x1,u16 x2) { if(x1>x2)return x1-x2;else return x2-x1; } //畫一條粗線 //(x1,y1),(x2,y2):線條的起始坐標 //size:線條的粗細程度 //color:線條的顏色 void lcd_draw_bline(u16 x1, u16 y1, u16 x2, u16 y2,u8 size,u16 color) {u16 t; int xerr=0,yerr=0,delta_x,delta_y,distance; int incx,incy,uRow,uCol; if(x1<size|| x2<size||y1<size|| y2<size)return; delta_x=x2-x1; //計算坐標增量 delta_y=y2-y1; uRow=x1; uCol=y1; if(delta_x>0)incx=1; //設置單步方向 else if(delta_x==0)incx=0;//垂直線 else {incx=-1;delta_x=-delta_x;} if(delta_y>0)incy=1; else if(delta_y==0)incy=0;//水平線 else{incy=-1;delta_y=-delta_y;} if( delta_x>delta_y)distance=delta_x; //選取基本增量坐標軸 else distance=delta_y; for(t=0;t<=distance+1;t++ )//畫線輸出 { gui_fill_circle(uRow,uCol,size,color);//畫點 xerr+=delta_x ; yerr+=delta_y ; if(xerr>distance) { xerr-=distance; uRow+=incx; } if(yerr>distance) { yerr-=distance; uCol+=incy; } } } //5個觸控點的顏色 const u16 POINT_COLOR_TBL[CT_MAX_TOUCH]={RED,GREEN,BLUE,BROWN,GRED}; //電阻觸摸屏測試函數 void rtp_test(void) {u8 key;u8 i=0; while(1){key=KEY_Scan(0);tp_dev.scan(0); if(tp_dev.sta&TP_PRES_DOWN) //觸摸屏被按下{ if(tp_dev.x[0]<lcddev.width&&tp_dev.y[0]<lcddev.height){ if(tp_dev.x[0]>(lcddev.width-24)&&tp_dev.y[0]<16)Load_Drow_Dialog();//清除else TP_Draw_Big_Point(tp_dev.x[0],tp_dev.y[0],RED); //畫圖 }}else delay_ms(10); //沒有按鍵按下的時候 if(key==KEY0_PRES) //KEY0按下,則執行校準程序{LCD_Clear(WHITE);//清屏TP_Adjust(); //屏幕校準 Load_Drow_Dialog();}i++;if(i%20==0)LED0=!LED0;} } //電容觸摸屏測試函數 void ctp_test(void) {u8 t=0;u8 i=0; u16 lastpos[5][2]; //記錄最后一次的數據 while(1){tp_dev.scan(0);for(t=0;t<CT_MAX_TOUCH;t++){if((tp_dev.sta)&(1<<t)){if(tp_dev.x[t]<lcddev.width&&tp_dev.y[t]<lcddev.height){if(lastpos[t][0]==0XFFFF){lastpos[t][0] = tp_dev.x[t];lastpos[t][1] = tp_dev.y[t];}lcd_draw_bline(lastpos[t][0],lastpos[t][1],tp_dev.x[t],tp_dev.y[t],2,POINT_COLOR_TBL[t]);//畫線lastpos[t][0]=tp_dev.x[t];lastpos[t][1]=tp_dev.y[t];if(tp_dev.x[t]>(lcddev.width-24)&&tp_dev.y[t]<16){Load_Drow_Dialog();//清除}}}else lastpos[t][0]=0XFFFF;}delay_ms(5);i++;if(i%20==0)LED0=!LED0;} }int main(void){ delay_init(); //延時函數初始化 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//設置中斷優先級分組為組2:2位搶占優先級,2位響應優先級uart_init(115200); //串口初始化為115200LED_Init(); //LED端口初始化LCD_Init(); KEY_Init(); tp_dev.init();POINT_COLOR=RED;//設置字體為紅色 LCD_ShowString(60,50,200,16,16,"ELITE STM32"); LCD_ShowString(60,70,200,16,16,"TOUCH TEST"); LCD_ShowString(60,90,200,16,16,"ATOM@ALIENTEK");LCD_ShowString(60,110,200,16,16,"2015/1/11");LCD_ShowString(60,130,200,16,16,"Press KEY0 to Adjust"); if(tp_dev.touchtype!=0XFF)LCD_ShowString(60,130,200,16,16,"Press KEY0 to Adjust");//電阻屏才顯示delay_ms(1500);Load_Drow_Dialog(); if(tp_dev.touchtype&0X80)ctp_test(); //電容屏測試else rtp_test(); //電阻屏測試 }6、原理總結:
觸摸屏被按下時,兩層導電層導電,通過adc、spi可以讀取觸摸屏的x、y坐標(0-4095),x、y的坐標與LCD屏的坐標可以通過公式X=ax+b計算得出;系數a、b可以通過在LCD屏上設置已知的四個點計算得出,同時這四個點也可以校準觸摸屏。
總結
以上是生活随笔為你收集整理的嵌入式单片机高级篇(二)Stm32F103电阻触摸屏的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: uniapp加载本地图片的坑
- 下一篇: 2023年03月个人工作生活总结