1. 程式人生 > 其它 >s3c2440裸機-電阻觸控式螢幕程式設計(6.觸控式螢幕校準實現-五點校準法)

s3c2440裸機-電阻觸控式螢幕程式設計(6.觸控式螢幕校準實現-五點校準法)

前面我們講過觸控式螢幕觸控式螢幕校準原理就是讓lcd能夠與觸控式螢幕座標對應起來。

一、五點法校準實現

一、我們取A,B,C,D,E這五個點,那麼這個時候我們需要把該5個點的觸控式螢幕和LCD的座標對應起來,這就是校準的過程。

①在LCD顯示屏上A點顯示一個“十字”形狀

②使用者在觸控式螢幕上點選對應A點的“十字”形狀

③記錄觸控式螢幕的資料座標

同理在B,C, D, E點迴圈該①②③過程,就能得到這五點觸控式螢幕座標。

二 、然後根據這5個觸控式螢幕座標資料確定公式。

三 、以後得到TS觸點座標,即可校準出期待的TS座標。

下面開始函式實現:

在LCD上顯示"十字"形狀,定義為函式fb_disp_cross()

記錄觸控式螢幕座標,定義函式為ts_read_raw()

根據這觸控式螢幕座標資料確定公式,定義函式為ts_calibrate()

以後得到TS觸點座標,即可校準出期待的TS座標,定位函式為ts_read()

1.畫線畫圓函式實現

前面的五、LCD上實現畫點、線、圓實現了畫點函式fb_put_pixel。基於畫點函式即可畫出線段以及各種圖案。

 //-------------畫圓函式。引數:圓心,半徑,顏色----------        
 //            畫1/8圓 然後其他7/8對稱畫  
 //                  ---------------->X  
 //                  |(0,0)   0  
 
// | 7 1 // | 6 2 // | 5 3 // (Y)V 4 // // L = x^2 + y^2 - r^2 void draw_circle(int x, int y, int r, int color) { int a, b, num; a
= 0; b = r; while(22 * b * b >= r * r) // 1/8圓即可 { fb_put_pixel(x + a, y - b,color); // 0~1 fb_put_pixel(x - a, y - b,color); // 0~7 fb_put_pixel(x - a, y + b,color); // 4~5 fb_put_pixel(x + a, y + b,color); // 4~3 fb_put_pixel(x + b, y + a,color); // 2~3 fb_put_pixel(x + b, y - a,color); // 2~1 fb_put_pixel(x - b, y - a,color); // 6~7 fb_put_pixel(x - b, y + a,color); // 6~5 a++; num = (a * a + b * b) - r*r; if(num > 0) { b--; a--; } } } //-----------畫線。引數:起始座標,終點座標,顏色-------- void draw_line(int x1,int y1,int x2,int y2,int color) { int dx,dy,e; dx=x2-x1; dy=y2-y1; if(dx>=0) { if(dy >= 0) // dy>=0 { if(dx>=dy) // 1/8 octant { e=dy-dx/2; while(x1<=x2) { fb_put_pixel(x1,y1,color); if(e>0){y1+=1;e-=dx;} x1+=1; e+=dy; } } else // 2/8 octant { e=dx-dy/2; while(y1<=y2) { fb_put_pixel(x1,y1,color); if(e>0){x1+=1;e-=dy;} y1+=1; e+=dx; } } } else // dy<0 { dy=-dy; // dy=abs(dy) if(dx>=dy) // 8/8 octant { e=dy-dx/2; while(x1<=x2) { fb_put_pixel(x1,y1,color); if(e>0){y1-=1;e-=dx;} x1+=1; e+=dy; } } else // 7/8 octant { e=dx-dy/2; while(y1>=y2) { fb_put_pixel(x1,y1,color); if(e>0){x1+=1;e-=dy;} y1-=1; e+=dx; } } } } else //dx<0 { dx=-dx; //dx=abs(dx) if(dy >= 0) // dy>=0 { if(dx>=dy) // 4/8 octant { e=dy-dx/2; while(x1>=x2) { fb_put_pixel(x1,y1,color); if(e>0){y1+=1;e-=dx;} x1-=1; e+=dy; } } else // 3/8 octant { e=dx-dy/2; while(y1<=y2) { fb_put_pixel(x1,y1,color); if(e>0){x1-=1;e-=dy;} y1+=1; e+=dx; } } } else // dy<0 { dy=-dy; // dy=abs(dy) if(dx>=dy) // 5/8 octant { e=dy-dx/2; while(x1>=x2) { fb_put_pixel(x1,y1,color); if(e>0){y1-=1;e-=dx;} x1-=1; e+=dy; } } else // 6/8 octant { e=dx-dy/2; while(y1>=y2) { fb_put_pixel(x1,y1,color); if(e>0){x1-=1;e-=dy;} y1-=1; e+=dx; } } } } }
View Code

2.畫一個十字架

要畫一個十字架,只需要用畫兩條短線段即可。

void fb_disp_cross(int x, int y, unsigned int color)
{
    draw_line(x-10, y, x+10, y, color);
    draw_line(x, y-10, x, y+10, color);
}

3.記錄觸控點原始座標

以前4.1獲取觸控式螢幕座標)記錄觸控式螢幕座標是在Isr_Adc中實現的,只是把轉換結束後的座標列印了出來,那現在我們把它先記錄下來。

static int g_ts_x;
static int g_ts_y;
static volatile  int g_ts_data_valid = 0;
void report_ts_xy(int x, int y)
{
    //printf("x = %08d, y = %08d\n\r", x, y);

    if (g_ts_data_valid == 0)
    {
        g_ts_x = x;
        g_ts_y = y;
        g_ts_data_valid = 1;
    }
}
void ts_read_raw(int *px, int *py)
{
    while (g_ts_data_valid == 0);
    *px = g_ts_x;
    *py = g_ts_y;
    g_ts_data_valid = 0;
}
void get_calibrate_point_data(int lcd_x, int lcd_y, int *px, int *py)
{ fb_disp_cross(lcd_x, lcd_y, 0xffffff); /* 等待點選 */ ts_read_raw(px, py); }

這裡 ts_read_raw是阻塞式的讀取資料,當一開始沒有按下觸控式螢幕,那麼不會產生adc interrupt,那麼ts_read_raw會被阻塞住。只有當用戶按下後,才會立馬返回座標資料。

這裡對g_ts_data_validvolatile來修飾就是不讓編譯器優化,表示要讓這個變數讀取時一定是從記憶體裡面讀到的,對這個變數寫入時,一定會把它寫到記憶體中去。

4.校準公式ts_calibrate()

先獲取lcd引數

再顯示5個校準點,且獲取5個點的ts實際座標

確定校準公式

    static double g_kx;
    static double g_ky;
    
    static int g_ts_xc, g_ts_yc;
    static int g_lcd_xc, g_lcd_yc;
    
    static int g_ts_xy_swap = 0;int is_ts_xy_swap(int a_ts_x, int a_ts_y, int b_ts_x, int b_ts_y)
    {
        int dx = b_ts_x - a_ts_x;
        int dy = b_ts_y - a_ts_y;
    
        if (dx < 0)
            dx = 0 - dx;
        if (dy < 0)
            dy = 0 - dy;
    
        if(dx > dy)
            return 0; /* xy沒有反轉 */
        else
            return 1; /* xy反了 */
    }
    void swap_xy(int *px, int *py)
    {
        int tmp = *px;
        *px = *py;
        *py = tmp;
    }
/*
----------------------------
|                          |
|  +(A)              (B)+  |
|                          |
|                          |
|                          |
|            +(E)          |
|                          |
|                          |
|                          |
|  +(D)              (C)+  |
|                          |
----------------------------
*/
void ts_calibrate(void)
{
    unsigned int fb_base;
    int xres, yres, bpp;

    int a_ts_x, a_ts_y;
    int b_ts_x, b_ts_y;
    int c_ts_x, c_ts_y;
    int d_ts_x, d_ts_y;
    int e_ts_x, e_ts_y;

    /* X軸方向 */
    int ts_s1, ts_s2;
    int lcd_s;

    /* Y軸方向 */
    int ts_d1, ts_d2;
    int lcd_d;

    /* 獲得LCD的引數: fb_base, xres, yres, bpp */
    get_lcd_params(&fb_base, &xres, &yres, &bpp);

    /* 對於ABCDE, 迴圈: 顯示"+"、點選、讀ts原始值 */
    /* A(50, 50) */
    get_calibrate_point_data(50, 50, &a_ts_x, &a_ts_y);

    /* B(xres-50, 50) */
    get_calibrate_point_data(xres-50, 50, &b_ts_x, &b_ts_y);

    /* C(xres-50, yres-50) */
    get_calibrate_point_data(xres-50, yres-50, &c_ts_x, &c_ts_y);

    /* D(50, yres-50) */
    get_calibrate_point_data(50, yres-50, &d_ts_x, &d_ts_y);
    
    /* E(xres/2, yres/2) */
    get_calibrate_point_data(xres/2, yres/2, &e_ts_x, &e_ts_y);

    /* 確定觸控式螢幕資料XY是否反轉 */
    g_ts_xy_swap = is_ts_xy_swap(a_ts_x, a_ts_y, b_ts_x, b_ts_y);

    if (g_ts_xy_swap)
    {
        /* 對調所有點的XY座標 */
        swap_xy(&a_ts_x, &a_ts_y);
        swap_xy(&b_ts_x, &b_ts_y);
        swap_xy(&c_ts_x, &c_ts_y);
        swap_xy(&d_ts_x, &d_ts_y);
        swap_xy(&e_ts_x, &e_ts_y);
    }

    /* 確定公式的引數並儲存 */
    ts_s1 = b_ts_x - a_ts_x;
    ts_s2 = c_ts_x - d_ts_x;
    lcd_s = xres-50 - 50;

    ts_d1 = d_ts_y - a_ts_y;
    ts_d2 = c_ts_y - b_ts_y;
    lcd_d = yres-50-50;

    g_kx = ((double)(2*lcd_s)) / (ts_s1 + ts_s2);
    g_ky = ((double)(2*lcd_d)) / (ts_d1 + ts_d2);

    g_ts_xc = e_ts_x;
    g_ts_yc = e_ts_y;

    g_lcd_xc = xres/2;
    g_lcd_yc = yres/2;
}

這裡還補充了一點,這裡可以軟體進行矯正觸控式螢幕與LCD貼錯位的情況導致x,y軸座標反轉的問題。這裡用a,b兩個點的觸控式螢幕實際座標進行比對,如果從a點到b點的x座標變化明顯,y座標變化不明顯,表示x,y方向沒有貼反。反之則表示x,y方向貼反了,需要反轉座標軸。

5. ts_read()讀出校準後(期望)的座標

/*
 * 讀TS原始資料, 轉換為期望的座標
 */
void ts_read(int *lcd_x, int *lcd_y)
{
  int ts_x, ts_y;
  ts_read_raw(&ts_x, ts_y);
  if (g_ts_xy_swap)
{
  swap_xy(&ts_x, &ts_y);
}
  /* 使用公式計算 */
  *lcd_x = g_kx * (ts_x - g_ts_xc) + g_lcd_xc;
  *lcd_y = g_ky * (ts_y - g_ts_yc) + g_lcd_yc;
}

二、測試

void clear_screen(unsigned int color)
{
    int x, y;
    unsigned char *p0;
    unsigned short *p;
    unsigned int *p2;

    /* 往framebuffer中寫資料 */
    if (bpp == 8)
    {
        /* bpp: palette[color] */

    p0 = (unsigned char *)fb_base;
    for (x = 0; x < xres; x++)
        for (y = 0; y < yres; y++)
            *p0++ = color;
    }
    else if (bpp == 16)
    {
        /* 讓LCD輸出整屏的紅色 */

    /* 565: 0xf800 */

    p = (unsigned short *)fb_base;
    for (x = 0; x < xres; x++)
        for (y = 0; y < yres; y++)
            *p++ = convert32bppto16bpp(color);
        
    }
    else if (bpp == 32)
    {
        p2 = (unsigned int *)fb_base;
        for (x = 0; x < xres; x++)
            for (y = 0; y < yres; y++)
                *p2++ = color;

    }
}
void touchscreen_test(void)
{
    unsigned int fb_base;
    int xres, yres, bpp;

    int x, y, pressure;

    /* 獲得LCD的引數: fb_base, xres, yres, bpp */
    get_lcd_params(&fb_base, &xres, &yres, &bpp);

    touchscreen_init();

    /* 清屏 */
    clear_screen(0);/*LCD章節有介紹*/

    /* 顯示文字提示較準 */
    fb_print_string(70, 70, "Touc cross to calibrate touchscreen", 0xffffff);
    ts_calibrate();

    /* 顯示文字提示繪畫 */
    fb_print_string(70, yres - 70, "OK! To draw!", 0xffffff);
    while (1)
    {
        if (ts_read(&x, &y) == 0)
        {
            printf(" x = %d, y = %d\n\r", x, y);
            fb_put_pixel(x, y, 0xff00); //顯示點
        }
    }
}

這裡我只點選了A點,怎麼一下修改子跳出5個十字架。這裡由於我們是點選一下可能時間會很長,幾十甚至幾百毫秒,那麼會產生很多次TS intterrupt和ADC中斷,那麼我們需要使用者鬆開後才認為觸控動作完成。