新聞中心

        EEPW首頁 > 嵌入式系統 > 設計應用 > s3c2440的觸摸屏應用與校正

        s3c2440的觸摸屏應用與校正

        作者: 時間:2016-11-19 來源:網絡 收藏
        觸摸屏是當今最流行的一種人機交互接口,它被廣泛地應用于手機等消費類電子產品中,目前這種技術有向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;iPutPixel(i,y, color);
        for(i=y-10;iPutPixel(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坐標轉換模式,可正確讀取觸點的坐標值。在中斷程序中,當觸筆落下時進入中斷,當觸筆抬起時退出中斷,程序只記錄觸筆落下時觸點的坐標值。

        該段程序的演示圖如下:



        關鍵詞: s3c2440觸摸屏校

        評論


        技術專區

        關閉
        主站蜘蛛池模板: 甘德县| 谷城县| 仙桃市| 阳东县| 蒙山县| 临西县| 宁安市| 桑日县| 漠河县| 江口县| 堆龙德庆县| 育儿| 宁安市| 静安区| 靖宇县| 上蔡县| 彭泽县| 昔阳县| 鹰潭市| 门源| 西贡区| 扶风县| 北碚区| 鸡东县| 阿巴嘎旗| 隆尧县| 西城区| 民权县| 大同县| 新兴县| 伽师县| 招远市| 盐源县| 丹江口市| 西乌| 阿图什市| 临泉县| 西华县| 汝南县| 唐河县| 大关县|