查看: 823|回复: 0
打印 上一主题 下一主题

触摸屏控制方法,个人总结(欢迎拍砖,但不要打脸)

[复制链接]
跳转到指定楼层
沙发
发表于 2016-5-6 16:17:01 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
最近几天研究了下触摸屏,发现也并不像感觉中的那么神秘。

本人用的触摸屏方案是 4线电阻屏+xpt2046(这个和ADS7843完全一样)。

控制过程主要分一下几步:

1,读数——这里读出来的是触摸屏控制芯片的AD值,是屏的物理坐标
2,滤波——触摸屏类似按键,按下和放开时会有抖动
3,转化——把屏的物理坐标转化成逻辑坐标,这里的逻辑坐标在LCD的范围内对应LCD的像素点坐标。
4,定位——触摸屏的定位,这个其实应该放到最开始。

读数:

这里按照控制芯片的时序使用管脚模拟SPI的方式读出来的,用过STM32的SPI,也可以,不过习惯模拟,不用那么复杂的配置了。
(程序见后面部分)

滤波:

这里使用了2种方式的滤波,一种是像按键一样,检测到控制芯片INT引脚变低之后,延时20ms,然后如果在检测还是为低,则是真正的按下。
第二种是软件滤波,程序读取了10次触摸屏的物理坐标,然后冒泡排序,最后去掉最前面的和最后面的,只保留中间3个,再对中间3个取平均。
(程序见后面部分)

转化:

这个很简单,在任何一个介绍触摸屏的文章估计都能见到。
xp——x的物理坐标  xl—— x的逻辑坐标  LCDXSIZE ——LCD的x方向做大值   xpmin —— 在LCD(0,0)坐标处的x的物理坐标 xpmax LCD最大处x物理坐标
yp——y的物理坐标  yl—— y的逻辑坐标  LCDYSIZE ——LCD的y方向最大值   ypmin —— 在LCD(0,0)坐标处的y的物理坐标 ypmax LCD最大处y物理坐标

xl = (xp-xpmin)*LCDXSIZE/(xpmax-xpmin)
yl = (yp-ypmin)*LCDYSIZE/(ypmax-ypmin)

定位:

这里定位的作用是求处上面的xpmin,xpmax,ypmin和ypmax,方法就是在屏幕上知道2点,求这两点所在直线上的一点(而且知道要求点的某一个坐标)
在屏上分别画出4个点,其实3个点足以,但是一般都用4个点,取得这四个点的物理坐标。假设分别为:
          |                        |
       --x1,y1-------------------x2,y2----
          |                        |
       --x3,y3-------------------x4,y4----
          |                        |
对应的物理坐标为 cx1,cy1     cx2,cy2,    cx3,cy3    cx4,cy4

利用比例关系                   x1/(cx1-xpmin)   =  x2/(cx2-xpmin)    —————————————— 这里x1和x2不相等
可以求出xpmin,同样用比例关系   x2/(cx2-xpmin)   =  LCDXSIZE/(xpmax-xpmin)———————————— 这里最好x2>x1,更准些
可以求出xpmax

然后用同样的方法求出ypmin和ypmax
_____________________________________________________分割线__________________________________________________________________
————————————————————————————————————————————————————————————————
用中断读控制芯片的INT引脚还是用定时器读?

用中断比较节省资源,但是我在做一个画图板的时候,发现滑动坐标没办法求出来,于是就去想定时器读。
用定时器读有个好处:延时操作可以在定时器里设置一个标志字,然后如果有按下就置位这个标志,下次再去真正读取。
                    定时器里可以给触摸屏设置多种不同的状态,这里按照Windows的情况设置了down,move,up还有none4种状态
这样用定时器解决了一个消抖和滑动坐标检测的问题,我选择定时器。

无图无真相,无码无真相:下面是真相


(原文件名:touch.jpg)

头文件::
#ifndef __TOUCH_H__
#define __TOUCH_H__

#include "stm32f10x_lib.h"

enum
{
    TOUCH_NONE=0,             //
    TOUCH_DOWN,
    TOUCH_MOVE,
    TOUCH_UP,
};


#define TOUCH_CLK_LOW()     GPIO_ResetBits(GPIOB, GPIO_Pin_13)
#define TOUCH_CLK_HIGH()    GPIO_SetBits(GPIOB, GPIO_Pin_13)
#define TOUCH_DOUT_LOW()    GPIO_ResetBits(GPIOB, GPIO_Pin_15)
#define TOUCH_DOUT_HIGH()   GPIO_SetBits(GPIOB, GPIO_Pin_15)

#define TOUCH_READ_DIN()    GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_14)

#define TOUCH_CS_LOW()     GPIO_ResetBits(GPIOB, GPIO_Pin_12)
#define TOUCH_CS_HIGH()    GPIO_SetBits(GPIOB, GPIO_Pin_12)
#define TOUCH_READ_INT()   GPIO_ReadInputDataBit(GPIOG, GPIO_Pin_7)
#define TOUCH_READ_BUSY()  GPIO_ReadInputDataBit(GPIOG, GPIO_Pin_8)

#define TOUCH_CHX   0x90                //差分方式读取
#define TOUCH_CHY   0xD0
#define TOUCH_GETTIMES  10

extern vu16 TouchX, TouchY;
extern vu8 TouchPress,TouchState;
extern u8 TouchCalibrated;

void Touch_Init(void);
u16 Touch_GetX(void);
u16 Touch_GetY(void);
void Touch_Calibrate(void);
void Touch_GetState(void);

#endif


C文件::
#include "Touch.h"
#include "systick.h"
#include "Graphics.h"


vu16 TouchX, TouchY;
vu8 TouchPress=0, TouchState=TOUCH_NONE; //state有4种状态,0无按键,1按下,2抬起,3move
u8 TouchCalibrated = 0;

u16 TouchXMin, TouchXMax, TouchYMin, TouchYMax;
/********************************
初始化触摸屏需要的端口
芯片--TSC2046
********************************/
void Touch_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    //EXTI_InitTypeDef EXTI_InitStructure;
    //NVIC_InitTypeDef NVIC_InitStructure;
    //SPI_InitTypeDef  SPI_InitStructure;
     
    /* Enable GPIOB, GPIOC and AFIO clock */
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOG|RCC_APB2Periph_AFIO, ENABLE);  //RCC_APB2Periph_AFIO
    //RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);

    //SPI
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13|GPIO_Pin_15;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_Init(GPIOB, &GPIO_InitStructure);

    //SPI_MISO
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
    GPIO_Init(GPIOB, &GPIO_InitStructure);

        /* CS pins configuration */
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_Init(GPIOB, &GPIO_InitStructure);

    /*INI Pin*/
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7|GPIO_Pin_8;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
    GPIO_Init(GPIOG, &GPIO_InitStructure);
/*   
    SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
    SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
    SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
    SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;                      //时钟空闲为低
    SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;                    //上升沿所存
    SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
    SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
    SPI_InitStructure.SPI_CRCPolynomial = 7;
    SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_32;
    SPI_Init(SPI2, &SPI_InitStructure);
    SPI_Cmd(SPI2,ENABLE);  */
#if 0
    EXTI_ClearITPendingBit(EXTI_Line7);
    GPIO_EXTILineConfig(GPIO_PortSourceGPIOG, GPIO_PinSource7);  

    /* Configure Button EXTI line */
    EXTI_InitStructure.EXTI_Line = EXTI_Line7;
    EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
    EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;  
    EXTI_InitStructure.EXTI_LineCmd = ENABLE;
    EXTI_Init(&EXTI_InitStructure);
#endif

}

/*=====================================================================*/
u16 Touch_AdjY(u16 adx) //240
{
  u16 sx=0;
  int r = adx - TouchYMin;
  r *= 240;
  sx=r / (TouchYMax - TouchYMin);
  if (sx>=240)
    return 0xFFFF;
  return sx;
}


u16 Touch_AdjX(u16 ady) //320
{
  u16 sy=0;
  int r = ady - TouchXMin;
  r *= 320;
  sy=r/(TouchXMax - TouchXMin);
  if (sy>=320)
    return 0xFFFF;
  return sy;
}


u16 Touch_Read(u8 cmd)
{
    u8 i;
    u16 pos=0;

    TOUCH_CLK_LOW();
    TOUCH_CS_LOW();
    for(i=0; i<8; i++)
    {
        if(cmd&0x80) TOUCH_DOUT_HIGH();
        else TOUCH_DOUT_LOW();
        cmd <<= 1;
        TOUCH_CLK_HIGH();
        TOUCH_CLK_LOW();
    }
    Delay(50);
    for(i=0; i<12 ;i++)
    {
       pos <<= 1;
       TOUCH_CLK_HIGH();
       if(TOUCH_READ_DIN() == Bit_SET)
        pos |= 0x01;
       TOUCH_CLK_LOW();
    }
    TOUCH_CS_HIGH();

    return pos;
}

u16 Touch_GetX(void)
{
    u8 count=0, i,j;
    u16 pos[TOUCH_GETTIMES]={0};
    u16 res=0xffff, temp;

    while((count<TOUCH_GETTIMES)&&(TOUCH_READ_INT() == Bit_RESET))
    {
        count++;
        //TOUCH_CS_LOW();                                           //选中器件
        //SPI_I2S_SendData(SPI2,TOUCH_CHX);                           //
        //while(TOUCH_READ_BUSY() == Bit_SET);                      //等待busy信号
        //pos[count] = (u8 )SPI_I2S_ReceiveData(SPI2);
        //TOUCH_CS_HIGH();
        pos[count] = Touch_Read(TOUCH_CHX);
    }
    if(count < TOUCH_GETTIMES)                                                  //干扰,丢弃
        return res;
    for(i=0; i<TOUCH_GETTIMES-1; i++)
    {
        for(j=0; j<TOUCH_GETTIMES-i-1; j++)  
        {
            if(pos[j]>pos[j+1])
            {
                temp = pos[j];
                pos[j] = pos[j+1];
                pos[j+1] = temp;
            }
        }
    }
   
    res = (pos[TOUCH_GETTIMES/2-1]+pos[TOUCH_GETTIMES/2]+pos[TOUCH_GETTIMES/2+1])/3;
    if(TouchCalibrated==1) //已经校准过了,否则这里只输出物理值
        res = Touch_AdjX(res);
                           
    return res;   
}

u16 Touch_GetY(void)
{
    u8 count=0, i,j;
    u16 pos[TOUCH_GETTIMES]={0};
    u16 res = 0xffff, temp;

    while((count<TOUCH_GETTIMES)&&(TOUCH_READ_INT() == Bit_RESET))
    {
        count++;
       // TOUCH_CS_LOW();                                           //选中器件
        //SPI_I2S_SendData(SPI2,TOUCH_CHY);                           //
        //while(TOUCH_READ_BUSY() == Bit_SET);                      //等待busy信号
        //pos[count] = (u8 )SPI_I2S_ReceiveData(SPI2);  
        //TOUCH_CS_HIGH();  
        pos[count] = Touch_Read(TOUCH_CHY);
    }
    if(count < TOUCH_GETTIMES)                                                  //干扰,丢弃
        return 0xffff;
    for(i=0; i<TOUCH_GETTIMES-1; i++)
    {
        for(j=0; j<TOUCH_GETTIMES-i-1; j++)  
        {
            if(pos[j]>pos[j+1])
            {
                temp = pos[j];
                pos[j] = pos[j+1];
                pos[j+1] = temp;
            }
        }
    }
   
    res = (pos[TOUCH_GETTIMES/2-1]+pos[TOUCH_GETTIMES/2]+pos[TOUCH_GETTIMES/2+1])/3;
    if(TouchCalibrated==1) //已经校准过了,否则这里只输出物理值
        res = Touch_AdjY(res);

    return res;
}


void Touch_Calibrate(void)
{
  u16 x[4] = {30, 290, 30, 290};
  u16 y[4] = {20, 20, 220, 220};
  u16 cx[4],cy[4], tempx[2], tempy[2];
  u8 i;
  u16 color;
   
  //画出需要的点,然后点击  
  TouchXMin = 0;
  TouchYMin = 0;

  color = GetColor();
  SetColor(BLUE);
  ClearDevice();
  
  while(TRUE)
  {
    for(i=0; i<4; i++)                  //画出5个点,点击后记录标志值
    {
        SetColor(RED);
        FillCircle(x,y,3);
        while((TouchPress==0)||(TouchState!=TOUCH_DOWN));
        TouchPress = 0;
        cx= TouchX;
        cy= TouchY;
        SetColor(BLUE);
        ClearDevice();
    }
   
    tempx[0] = (290*cx[0]-30*cx[1])/260;
    tempx[1] = (290*cx[2]-30*cx[3])/260;
    tempy[0] = (220*cy[0]-20*cy[2])/200;
    tempy[1] = (220*cy[1]-20*cy[3])/200;

    if( (tempx[0]>tempx[1]-20) && ((tempx[0]<tempx[1]+20)))
    {
        TouchXMin = (tempx[0]+tempx[1])/2;
    }
    if( (tempy[0]>tempy[1]-20) && ((tempy[0]<tempy[1]+20)))
    {
        TouchYMin = (tempy[0]+tempy[1])/2;
    }
    if(TouchXMin != 0 && TouchYMin !=0)
    {
        TouchXMax = (cx[1]-TouchXMin)*320/290 + TouchXMin;
        TouchYMax = (cy[2]-TouchYMin)*240/220 + TouchYMin;
        break;
    }
  }
  SetColor(color);
  TouchCalibrated = 1;
}



void Touch_GetState(void)           //定时器里调用
{
    u16 x, y;
    static BOOL islow = FALSE;   

    if(TOUCH_READ_INT() != Bit_RESET)          //没有按下的情况下,如果之前是按下的,则
    {                                           //是抬起
        if((TouchState == TOUCH_DOWN)||(TouchState == TOUCH_MOVE))
        {
            TouchState = TOUCH_UP;
            TouchPress = 1;
        }
        else
            TouchState = TOUCH_NONE;
        return;
    }
    else
    {
        if(islow == FALSE)
            islow = TRUE;
        else
        {
            x = Touch_GetX();         
            y = Touch_GetY();
            if((x != 0xffff) && (x != 0xffff))      //有真的按下
            {
              switch(TouchState)
              {
                case TOUCH_NONE:            //原来没有按下,现在一定是按下了
                    TouchState = TOUCH_DOWN;
                    break;
                case TOUCH_DOWN:            //原来按下了,现在一定是move了
                    TouchState = TOUCH_MOVE;
                    break;
                case TOUCH_MOVE:            //之前是move,现在还是move
                    
                    break;
                case TOUCH_UP:              //之前是up,这种情况应该不会出现
                default:
                    TouchState = TOUCH_NONE;
                    break;
              }
              TouchX = x;
              TouchY = y;
              TouchPress = 1;
            }
            islow = FALSE;
        }
    }
   

}

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 加入因仑

本版积分规则

快速回复 返回顶部 返回列表