在我们的生活中,触摸屏可以说是无处不在。在各种触控面板我们都可以看到触摸屏的身影,一些产品也因为有了触摸屏而价值大涨。接下来,小蛋糕带你深入了解触摸屏。
触摸屏可以分为电阻式触摸屏和电容式触摸屏,在这里我们介绍的是电阻式触摸屏(为什么使用电阻式触摸屏进行讲解嘞,它更好用或者应用面更广吗?no no no 其实现在使用更加广泛的式电容式触摸屏,电容式触摸屏是我们主流手机所使用的屏幕。那么我为什么使用电阻式触摸屏呢,因为它便宜(手动无奈表情))。
言归正传,我来介绍一下电阻屏的优缺点,电阻屏的优缺点如下表所示:
电阻触摸屏的优点 | 电阻触摸屏的缺点 |
精度高、价格便宜、抗干扰能力强、稳定性好 | 容易被划伤、透光性不太好、不支持多点触摸 |
在我们眼中,触摸屏一直是一种科技感慢慢的高科技设备,but当你看完小蛋糕下面对触摸屏原理的介绍后你就会感觉,触摸屏,just so so。
首先,我们需要知道触摸屏的组成结构。电阻触摸屏主要部分是一块与显示器表面非常配合的电阻薄膜屏,这是一种多层的复合薄膜,它以一层玻璃或硬塑料平板作为基层,表面涂有一层透明氧化物薄膜导电层,上面再盖有一层外表面硬化处理、光滑防擦的塑料层、它的内表面也涂有一层涂层、在他们之间有许多细小的透明隔离点把两层导电层隔开绝缘。当手指触摸屏幕时,两层导电层在接触点位置就有了接触,电阻就会发生变化,在X和Y两个方向上会产生对应的信号,然后送至触摸屏控制器。控制器侦测到这一接触并计算出(X,Y)变量,再根据获得的位置模拟鼠标的方式运作,以上就是电阻触摸屏的基本原理。怎么样,是不是老简单了!
在本篇中,小蛋糕以一个实验项目带大家了解触摸屏的工作过程。
我们以STM32F103系列的MINI开发板、2.8电阻触摸屏为例,对我们的相关硬件以及程序代码进行讲解。在这里我们暂且不考虑触摸屏信号的AD转换,关于AD转换小蛋糕会在以后的文章中进行讲解。
在本实验中,首先需要初始化LCD,根据LCD的ID判断是否是电阻式触摸屏,不是的话,我们需要按照电容式触摸屏的设计算法程序继续,是的话,我们继续【经过判断再进入相关测试板块是逻辑性较强的通用式方法,由于数据庞大,在本片代码只对电阻屏的相关代码进行详细讲解】。获得ID判断为电阻式触摸屏后,我们需要判断触摸屏是否经过校准(触摸屏可以对校准进行记忆),如果没有那么需要进行校准,校准后(或者之前校准过)则进入电阻屏测试测序(测试界面右上角会有一个清空的操作区域RST,点击这个地方就会全部清除,恢复白板状态)
对实验内容了解后我们就开始我们的代码编写之旅,are you ready?
由于我们需要使用LCD显示相关信息,这里小蛋糕带你手把手进行LCD初始化以及相关应用。
由于LDC初始化部分代码过多,这边仅对关键代码进行讲解
//进行结构体声明
GPIO_InitTypeDef GPIO_InitStructure;
//使能相关IO口的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC|RCC_APB2Periph_GPIOB|RCC_APB2Periph_AFIO, ENABLE);
//开启SWD,使能JTAG
GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable , ENABLE);
//对使用到的IO口(具体IO口可以查看开发手册)进行初始化
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10|GPIO_Pin_9|GPIO_Pin_8|GPIO_Pin_7|GPIO_Pin_6;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOC, &GPIO_InitStructure); //GPIOC
GPIO_SetBits(GPIOC,GPIO_Pin_10|GPIO_Pin_9|GPIO_Pin_8|GPIO_Pin_7|GPIO_Pin_6);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_All; // PORTBÍÆÍìÊä³ö
GPIO_Init(GPIOB, &GPIO_InitStructure); //GPIOB
GPIO_SetBits(GPIOB,GPIO_Pin_All);
完成LCD初始化后,我们需要在LCD上显示相关信息,所以我们需要编写相关函数即LCD_ShowString()函数。
//x,y代表打印信息的起始地址
//width和heigth代表信息显示区间的长度和高度
//size代表字符的大小
//*p是指向我们想要输出信息地址的指针
void LCD_ShowString(u16 x,u16 y,u16 width,u16 height,u8 size,u8 *p)
{
u8 x0=x;
//根据输入的起始地址进行显示区域的更新
width+=x;
height+=y;
while((*p<='~')&&(*p>=' '))//判断需要显示的字符是不是有效字符
{
if(x>=width){x=x0;y+=size;}//判断输入的信息是否超过了显示的长度,如果超过了显示长度则,换行显示
if(y>=height)break;
//调用提供的函数进行信息的显示(真正显示信息的部分)
LCD_ShowChar(x,y,*p,size,0);
x+=size/2;
p++;
}
}
LCD的相关工作结束后,我们开始触摸屏的相关操作
首先我们查看电阻屏控制器结构体的定义
typedef struct
{
u8 (*init)(void);
u8 (*scan)(u8); //扫描触摸屏
void (*adjust)(void);
u16 x[CT_MAX_TOUCH];
u16 y[CT_MAX_TOUCH];
u8 sta;
float xfac;
float yfac;
short xoff;
short yoff;
u8 touchtype;
}_m_tp_dev;
然后我们要对触摸屏进行初始化,对于电阻触摸屏初始化的代码如下所示:
//首先我们还是需要使能相关的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC | RCC_APB2Periph_AFIO, ENABLE);
/*********************************************************************************/
//对触摸屏的IO口进行初始化
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3|GPIO_Pin_0|GPIO_Pin_13;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOC, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1|GPIO_Pin_2;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU ;
GPIO_Init(GPIOC, &GPIO_InitStructure);
/************************************************************************************/
//读取第一次初始化时xy的坐标
TP_Read_XY(&tp_dev.x[0],&tp_dev.y[0]);
//初始化
AT24CXX_Init();
//判断是否校准
if(TP_Get_Adjdata())return 0;
//若校准不成功,再次进行校准
else
{
LCD_Clear(WHITE);
TP_Adjust();
TP_Save_Adjdata();
}
TP_Get_Adjdata();
扫描触摸屏,获得触摸地址的代码如下:
//tp用来区分屏幕坐标或者是物理坐标
u8 TP_Scan(u8 tp)
{
if(PEN==0)
{
if(tp)TP_Read_XY2(&tp_dev.x[0],&tp_dev.y[0]);//tp为真,读取物理坐标
else if(TP_Read_XY2(&tp_dev.x[0],&tp_dev.y[0]))//tp为假,读取屏幕坐标
{
//对读取的结果进行转化,转化为可使用的屏幕坐标
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;//返回当前触摸屏或者按键的状态
}
触摸屏校准部分的核心代码如下所示:其中xfac yfac xoff yoff用来保存计算坐标偏差时的一些数据。
tp_dev.sta = 0;//消除触发信号
tp_dev.xfac = 0;//xfac用来标记是否校准过,所以校准之前必须清掉!以免错误
while(1)//如果连续10秒钟没有按下,则自动退出
{
READJ:
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); //画点2
break;
case 2:
TP_Drow_Touch_Point(lcddev.width-20,20,WHITE); //清除点2
TP_Drow_Touch_Point(20,lcddev.height-20,RED); //画点3
break;
case 3:
TP_Drow_Touch_Point(20,lcddev.height-20,WHITE); //清除点3
TP_Drow_Touch_Point(lcddev.width-20,lcddev.height-20,RED); //画点4
break;
case 4: //全部四个点已经得到
for(cnt = 0;cnt < 3;cnt++)//计算三组点的距离是否在允许范围内?
{
tem1 = abs(pos_temp[TP_ADJDIS_TBL[cnt][0]][0]-pos_temp[TP_ADJDIS_TBL[cnt][1]][0]);//x1-x2/x1-x3/x2-x3
tem2 = abs(pos_temp[TP_ADJDIS_TBL[cnt][0]][1]-pos_temp[TP_ADJDIS_TBL[cnt][1]][1]);//y1-y2/y1-y3/y2-y3
tem1 *= tem1;
tem2 *= tem2;
d1 = sqrt(tem1+tem2);//得到两点之间的距离
tem1 = abs(pos_temp[TP_ADJDIS_TBL[cnt][2]][0]-pos_temp[TP_ADJDIS_TBL[cnt][3]][0]);//x3-x4/x2-x4/x1-x4
tem2 = abs(pos_temp[TP_ADJDIS_TBL[cnt][2]][1]-pos_temp[TP_ADJDIS_TBL[cnt][3]][1]);//y3-y4/y2-y4/y1-y4
tem1 *= tem1;
tem2 *= tem2;
d2 = sqrt(tem1+tem2);//得到两点之间的距离
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); //清除点4
TP_Drow_Touch_Point(20,20,RED); //画点1
TP_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);//显示数据
goto READJ; //不合格,重新校准
}
}
//正确了
//计算结果
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;//得到xoff
tp_dev.yfac = (float)(lcddev.height-40)/(pos_temp[2][1]-pos_temp[0][1]);//得到yfac
tp_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); //清除点4
TP_Drow_Touch_Point(20,20,RED); //画点1
LCD_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;
}
}
}
完成上述操作后,我们就可以完成最神奇的一步——在触摸的点画图。这一步的核心代码如下图所示。
所谓将触摸点显示出来其实就是,触摸屏控制器获得触摸点位置之后,利用画图函数,以触摸点为中心,给该中心极小范围内的像素点上色的过程。怎么样,是不是简单的出乎意料?
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) //按键被按下,进行强制校准
{
LCD_Clear(WHITE);
TP_Adjust();
TP_Save_Adjdata();
Load_Drow_Dialog();
}
i++;
if(i%20==0)LED0=!LED0;//使用LED灯闪烁表示程序运行
}
}
all right ,关于电阻式触摸屏就到这里,你学废了吗?我们下篇文章见!!!拜拜!!!