最近几天研究了下触摸屏,发现也并不像感觉中的那么神秘。
本人用的触摸屏方案是 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;
}
}
}
|