STM32学习笔记一一触摸屏

前言:

为了方便查看博客,特意申请了一个公众号,附上二维码,有兴趣的朋友可以关注,和我一起讨论学习,一起享受技术,一起成长。

在这里插入图片描述


1. 简介

1.1 电阻式触摸屏

电阻式触摸屏利用压力感应进行触点检测控制,需要直接应力接触, 通过检测电阻来定位触摸位置 。

1.1.1 电阻式触摸屏的原理

电阻触摸屏的主要部分是一块与显示器表面非常配合的电阻薄膜屏,这是一种多层的复合薄膜,它以一层玻璃或硬塑料平板作为基层,表面涂有一层透明氧化金属(透明的导电电阻)导电层,上面再盖有一层外表面硬化处理、光滑防擦的塑料层、它的内表面也涂有一层涂层、在他们之间有许多细小的(小于 1/1000 英寸)的透明隔离点把两层导电层隔开绝缘。

STM32学习笔记一一触摸屏_第1张图片

当手指触摸屏幕时,两层导电层在触摸点位置就有了接触,电阻发生变化,在 X 和 Y 两个方向上产生信号,然后送触摸屏控制器。控制器侦测到这一接触并计算出( X Y )的位置,再根据获得的位置模拟鼠标的方式运作。

电阻式触摸屏都需要一个AD 转换器, 所以一般来说驱动屏幕需要一个控制器芯片。这种屏幕可以用四线、五线、七线或八线来产生屏幕偏置电压,同时读回触摸点的电压。

1.1.2 电阻式触摸屏得到触点坐标

STM32学习笔记一一触摸屏_第2张图片

如上图所示,但在 X 轴方向的点极施加一定的电压,而 Y 轴方向不加电压时,在 X 轴的平行电场中,触点处的电压值可以在 Y 轴的测量点得到,知道了测量点处 Y 轴的电压(X对地电阻的电压),也就确定了 X 轴上的坐标;同理,当 Y 轴方向施加固定的电压时,可在 X 轴的测量点上得到对应的电压。经过两次的测量,就可以得出触点(X,Y)的坐标了。

1.1.3 电阻式触摸屏的优缺点

优点: 精度高、价格便宜、抗干扰能力强、稳定性好 。

缺点: 容易被划伤、透光性不太好、不支持多点触摸。直接的感觉就是体验不如电容屏幕。

1.2 电容式触摸屏

电容屏利用人体感应进行触点检测控制,不需要直接接触或只需要轻微接触,通过检测感应电流来定位触摸坐标 。

STM32学习笔记一一触摸屏_第3张图片
当手指点击屏幕,会从接触点吸收小量电流,造成角落电极的压降,利用感应人体微弱电流的方式来达到触控的目的。

1.2.1 表面电容式电容触摸屏

表面电容式触摸屏技术是利用 ITO 铟锡氧化物,是 一种透明的导电材料导电膜,通过电场感应方式感测屏幕表面的触摸行为进行。但是表面电容式触摸屏有一些局限性,它只能识别一个手指或者一次触摸。

1.2.2 投射式电容触摸屏

投射电容式触摸屏是传感器利用触摸屏电极发射出静电场线。一般用于投射电容传感技术的电容类型有两种: 自我电容和交互电容 。

自我电容: 又称绝对电容,自我电容通常是指扫描电极与地构成的电容。在玻璃表面有用 ITO 制成的横向与纵向的扫描电极,这些电极和地之间就构成一个电容的两极。当用手或触摸笔触摸的时候就会并联一个电容到电路中去,从而使在该条扫描线上的总体的电容量有所改变。在扫描的时候,控制 IC 依次扫描纵向和横向电极,并根据扫描前后的电容变化来确定触摸点坐标位置。笔记本电脑触摸输入板就是采用的这种方式。笔记本电脑的输入板采用 X*Y 的传感电极阵列形成一个传感格子, 当手指靠近触摸输入板时,在手指和传感电极之间产生一个小量电荷。采用特定的运算法则处理来自行、列传感器的信号来确定手指的位置。

STM32学习笔记一一触摸屏_第4张图片
交互电容: 又叫做跨越电容,它是在玻璃表面的横向和纵向的 ITO 电极的交叉处形成电容。交互电容的扫描方式就是扫描每个交叉处的电容变化,来判定触摸点的位置。当触摸的时候就会影响到相邻电极的耦合,从而改变交叉处的电容量,交互电容的扫面方法可以侦测到每个交叉点的电容值和触摸后电容变化,因而它需要的扫描时间与自我电容的扫描方式相比要长一些,需要扫描检测 X*Y 根电极。 目前智能手机 平板电脑等的触摸屏 ,都是采用 交互电容技术。

1.2.3 投射式电容触摸屏——交互电容详解

投射式电容触摸屏采用纵横两列电极组成感应矩阵,来感应触摸。以两个交叉的电极矩阵,即: X 轴电极和 Y 轴电极,来检测每一 格感应单元的电容变化 。
STM32学习笔记一一触摸屏_第5张图片如下图,当手指触碰到屏幕的时候,人体自身的感应电流会引起屏幕上排布的电容的变化,进而让处理器知道有触摸动作发生,得到触摸点坐标。

STM32学习笔记一一触摸屏_第6张图片X 、 Y 轴的透明电极电容屏的精度、分辨率与 X 、 Y 轴的通道数有关,通道数越多,精度越高。

1.2.4 电容式触摸屏的优缺点

优点: 手感好、无需校准、支持多点触摸、透光性好;
缺点: 成本高、精度不高、抗干扰能力差。

2. 电容触摸驱动 IC——OTT2001A介绍

OTT2001A ,最多支持 208 个通道。支持 SPI/IIC 接口。 IIC 接口模式下,该驱动 IC 与 STM32 的连接仅需要 4 根线: SDA 、 SCL 、 RST 和 INT,SDA 和 SCL 是 IIC 通信用的, RST 是复位脚(低电平有效), INT 是中断输出信号。

2.1 寄存器介绍

(1)手势 ID 寄存器

手势 ID 寄存器( 00H )用于告诉 MCU ,哪些点有效,哪些点无效,从而读取对应的数据。

STM32学习笔记一一触摸屏_第7张图片
可知模块只支持最多 5 点触摸。表中只有 5 个位用来表示对应点坐标是否有效,其余位为保留位(读为 0 ),通过读取该寄存器,可知哪些点有数据,哪些点无数据,如果读到的全是 0 ,则说明没有任何触摸。

(2)传感器控制寄存器(ODH)

传感器控制寄存器(ODH ),该寄存器也是 8 位,仅最高位有效,其他位都是保留,当最高位为 1 的时候,打开传感器(开始检测),当最高位设置为 0 的时候,关闭传感器(停止检测)。

(3)坐标数据寄存器(共 20 个)

坐标数据寄存器总共有 20 个,每个坐标占用 4 个寄存器,坐标寄存器与坐标的对应关系下图;

STM32学习笔记一一触摸屏_第8张图片
每个坐标的值,可以通过 4 个寄存器读出,比如读取坐标 A: (X1,Y1),可以读取 01H~04H ,就可以知道当前坐标 1 的具体数值了。也可以只发送寄存器 01 ,然后连续读取 4 个字节,也可以正常读取坐标 A ,寄存器地址会自动增加,从而提高读取速度。

注:

(1)OTT2001A 的寄存器是 8 位的,但是发送的时候要发送 16 位(高八位有效),才可以正常使用。

(2)OTT2001A 的输出坐标,默认是以: X 坐标最大值是 2700 Y 坐标最大值是 1500 的分辨率输出的,也就是输出范围为: X:0-2700,Y:0-1500 。MCU 在读取到坐标后,必须根据 LCD 分辨率做一个换算,才能得到真实的 LCD 坐标。

2.2 初始化流程

STM32学习笔记一一触摸屏_第9张图片

3. 软件分析

这里简要的分析一下电阻屏的驱动。

3.1 通信方式实现

/*软件模拟SPI写数据*/
void TP_Write_Byte(u8 num)    
{  
	u8 count = 0;
	
	for(count = 0;count < 8;count++)  
	{ 	  
		if(num&0x80)
			TDIN = 1;  
		else 
			TDIN = 0;   
		num <<= 1;    
		TCLK = 0; 	 
		TCLK = 1;		//上升沿有效	        
	}		 			    
} 	

3.2 触摸屏驱动实现

/*从触摸屏读取 ADC 的数值*/
u16 TP_Read_AD(u8 CMD)	  
{ 	 
	u8 count = 0; 	  
	u16 Num = 0; 
	
	TCLK = 0;		//先拉低时钟 	 
	TDIN = 0; 	//拉低数据线
	TCS = 0; 		//选中触摸屏IC
	TP_Write_Byte(CMD);//发送命令字
	delay_us(6);//ADS7846的转换时间最长为6us
	TCLK = 0; 	     	    
	delay_us(1);    	   
	TCLK = 1;		//给1个时钟,清除BUSY	    	    
	TCLK = 0; 	     	    
	for(count = 0;count < 16;count++)//读出16位数据,只有高12位有效 
	{ 				  
		Num <<= 1; 	 
		TCLK = 0;	//下降沿有效  	    	   
		TCLK = 1;
		if(DOUT)
			Num++; 		 
	}  	
	Num >>= 4;   	//只有高12位有效,移除低四位
	TCS = 1;		//释放片选	 
	
	return(Num);   
}
/*读取x或y的坐标值,并且多次读取,去掉最大、最小值,减少测量误差*/

#define READ_TIMES 5 	//读取次数
#define LOST_VAL 1	  	//丢弃值
u16 TP_Read_XOY(u8 xy)
{
	u16 i, j;
	u16 buf[READ_TIMES];
	u16 sum=0;
	u16 temp;

	for(i=0;i<READ_TIMES;i++)
		buf[i] = TP_Read_AD(xy);		 		    
	for(i = 0;i < READ_TIMES-1; i++)//排序
	{
		for(j=i+1;j<READ_TIMES;j++)
		{
			if(buf[i]>buf[j])//升序排列
			{
				temp = buf[i];
				buf[i] = buf[j];
				buf[j] = temp;
			}
		}
	}	  
	sum = 0;
	for(i = LOST_VAL;i < READ_TIMES-LOST_VAL;i++)	//丢掉最大最小值
	sum += buf[i];
	temp = sum/(READ_TIMES-2*LOST_VAL);
	
	return temp;   
} 
/*读取x,y的坐标值*/
u8 TP_Read_XY(u16 *x,u16 *y)
{
	u16 xtemp,ytemp;

	xtemp = TP_Read_XOY(CMD_RDX);
	ytemp = TP_Read_XOY(CMD_RDY);	  												   
	//if(xtemp<100||ytemp<100)return 0;//读数失败
	*x = xtemp;
	*y = ytemp;
	
	return 1;//读数成功
}
/*连续两次读取触摸屏数值,并且设定读数误差范围*/

#define ERR_RANGE 50 //误差范围 
u8 TP_Read_XY2(u16 *x,u16 *y) 
{
	u16 x1,y1;
 	u16 x2,y2;
 	u8 flag;  

    flag = TP_Read_XY(&x1,&y1);   
    if(flag==0)
		return(0);
    flag = TP_Read_XY(&x2,&y2);	   
    if(flag==0)
		return(0);   
    if(((x2<=x1&&x1<x2+ERR_RANGE)||(x1<=x2&&x2<x1+ERR_RANGE))//前后两次采样在+-50内
    &&((y2<=y1&&y1<y2+ERR_RANGE)||(y1<=y2&&y2<y1+ERR_RANGE)))
    {
        *x = (x1+x2)/2;
        *y = (y1+y2)/2;
        return 1;
    }
	else 
		return 0;	  
} 

3.3 显示处理

/*调用LCD显示函数,显示出触摸点*/
void TP_Drow_Touch_Point(u16 x,u16 y,u16 color)
{
	POINT_COLOR = color;
	LCD_DrawLine(x-12,y,x+13,y);//横线
	LCD_DrawLine(x,y-12,x,y+13);//竖线
	LCD_DrawPoint(x+1,y+1);
	LCD_DrawPoint(x-1,y+1);
	LCD_DrawPoint(x+1,y-1);
	LCD_DrawPoint(x-1,y-1);
	LCD_Draw_Circle(x,y,6);//画中心圈
}

void TP_Draw_Big_Point(u16 x,u16 y,u16 color)
{	    
	POINT_COLOR=color;
	LCD_DrawPoint(x,y);//中心点 
	LCD_DrawPoint(x+1,y);
	LCD_DrawPoint(x,y+1);
	LCD_DrawPoint(x+1,y+1);	 	  	
}	

3.4 触摸屏数据处理

/*扫描触摸按键*/
u8 TP_Scan(u8 tp)
{			   
	if(PEN==0)//有按键按下
	{
		if(tp)
			TP_Read_XY2(&tp_dev.x[0],&tp_dev.y[0]);//读取物理坐标
		else if(TP_Read_XY2(&tp_dev.x[0],&tp_dev.y[0]))//读取屏幕坐标
			{
	 			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;//返回当前的触屏状态
}	

#define SAVE_ADDR_BASE 40
//保存校准参数										    
void TP_Save_Adjdata(void)
{  
	s32 temp;			 
	//保存校正结果!		   							  
	temp = tp_dev.xfac*100000000;//保存x校正因素      
    AT24CXX_WriteLenByte(SAVE_ADDR_BASE,temp,4);   
	temp = tp_dev.yfac*100000000;//保存y校正因素    
    AT24CXX_WriteLenByte(SAVE_ADDR_BASE+4,temp,4);
	//保存x偏移量
    AT24CXX_WriteLenByte(SAVE_ADDR_BASE+8,tp_dev.xoff,2);		    
	//保存y偏移量
	AT24CXX_WriteLenByte(SAVE_ADDR_BASE+10,tp_dev.yoff,2);	
	//保存触屏类型
	AT24CXX_WriteOneByte(SAVE_ADDR_BASE+12,tp_dev.touchtype);	
	temp = 0X0A;//标记校准过了
	AT24CXX_WriteOneByte(SAVE_ADDR_BASE+13,temp); 
}
/*读出保存的校准参数,检查屏幕状态*/
u8 TP_Get_Adjdata(void)
{		  
	s32 tempfac;			  
	u8 temp;

	temp = AT24CXX_ReadOneByte(SAVE_ADDR_BASE+13);//读取标记字,看是否校准过! 		 
	if(temp==0X0A)//触摸屏已经校准过了			   
	{    												 
		tempfac = AT24CXX_ReadLenByte(SAVE_ADDR_BASE,4);		   
		tp_dev.xfac = (float)tempfac/100000000;//得到x校准参数
		tempfac = AT24CXX_ReadLenByte(SAVE_ADDR_BASE+4,4);			          
		tp_dev.yfa c= (float)tempfac/100000000;//得到y校准参数
	    //得到x偏移量
		tp_dev.xoff = AT24CXX_ReadLenByte(SAVE_ADDR_BASE+8,2);			   	  
 	    //得到y偏移量
		tp_dev.yoff = AT24CXX_ReadLenByte(SAVE_ADDR_BASE+10,2);				 	  
 		tp_dev.touchtype = AT24CXX_ReadOneByte(SAVE_ADDR_BASE+12);//读取触屏类型标记
		if(tp_dev.touchtype)//X,Y方向与屏幕相反
		{
			CMD_RDX = 0X90;
			CMD_RDY = 0XD0;	 
		}
		else				   //X,Y方向与屏幕相同
		{
			CMD_RDX = 0XD0;
			CMD_RDY = 0X90;	 
		}		 
		return 1;	 
	}
	return 0;
}	 
/*显示出校准的坐标参数*/
void TP_Adj_Info_Show(u16 x0,u16 y0,u16 x1,u16 y1,u16 x2,u16 y2,u16 x3,u16 y3,u16 fac)
{	  
	POINT_COLOR = RED;
	LCD_ShowString(40,160,lcddev.width,lcddev.height,16,"x1:");
 	LCD_ShowString(40+80,160,lcddev.width,lcddev.height,16,"y1:");
 	LCD_ShowString(40,180,lcddev.width,lcddev.height,16,"x2:");
 	LCD_ShowString(40+80,180,lcddev.width,lcddev.height,16,"y2:");
	LCD_ShowString(40,200,lcddev.width,lcddev.height,16,"x3:");
 	LCD_ShowString(40+80,200,lcddev.width,lcddev.height,16,"y3:");
	LCD_ShowString(40,220,lcddev.width,lcddev.height,16,"x4:");
 	LCD_ShowString(40+80,220,lcddev.width,lcddev.height,16,"y4:");  
 	LCD_ShowString(40,240,lcddev.width,lcddev.height,16,"fac is:");     
	LCD_ShowNum(40+24,160,x0,4,16);		//显示数值
	LCD_ShowNum(40+24+80,160,y0,4,16);	//显示数值
	LCD_ShowNum(40+24,180,x1,4,16);		//显示数值
	LCD_ShowNum(40+24+80,180,y1,4,16);	//显示数值
	LCD_ShowNum(40+24,200,x2,4,16);		//显示数值
	LCD_ShowNum(40+24+80,200,y2,4,16);	//显示数值
	LCD_ShowNum(40+24,220,x3,4,16);		//显示数值
	LCD_ShowNum(40+24+80,220,y3,4,16);	//显示数值
 	LCD_ShowNum(40+56,lcddev.width,fac,3,16); 	//显示数值,该数值必须在95~105范围之内.

}
const u8 TP_ADJDIS_TBL[3][4]={{0,1,2,3},{0,2,1,3},{1,2,0,3}};//校准距离计算表
//触摸屏校准代码
//得到四个校准参数
void TP_Adjust(void)
{								 
	u16 pos_temp[4][2];//坐标缓存值
	u8  cnt = 0;	
	u16 d1,d2;
	u32 tem1,tem2;
	float fac; 	
	u16 outtime=0; 

	LCD_Clear(WHITE);	//清屏 	    
	POINT_COLOR = BLUE;	//蓝色
	LCD_ShowString(40,40,160,100,16,(u8*)TP_REMIND_MSG_TBL);//显示提示信息
	TP_Drow_Touch_Point(20,20,RED);//画点1 
	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;
	 	} 
 	}
}	  

传统的鼠标是一种相对定位系统,只和前一次鼠标的位置坐标有关。而触摸屏则
是一种绝对坐标系统,要选哪就直接点哪,与相对定位系统有着本质的区别。绝对坐标系统的特点是每一次定位坐标与上一次定位坐标没有关系,每次触摸的数据通过校准转为屏幕上的坐标,不管在什么情况下,触摸屏这套坐标在同一点的输出数据是稳定的。不过由于技术原理的原因,并不能保证同一点触摸每一次采样数据相同,不能保证绝对坐标定位,点不准,这就是触摸屏最怕出现的问题:漂移。对于性能质量好的触摸屏来说,漂移的情况出现并不是很严重。所以很多应用触摸屏的系统启 动后,进入应用程序前,先要执行校准程序。 通常应用程序中使用的 LCD 坐标是以像素为单位的。比如说:左上角的坐标是一组非 0 的数值,比如( 20,20)而右下角的坐标为( 220,300 )。这些点的坐标都是以像素为单位的,而从触摸屏中读出的是点的物理坐标,其坐标轴的方向、 XY 值的比例因子、偏移量都与 LCD 坐标不同,所以, 需要在程序 中把物理坐标首先转换为像素坐标,然后再赋给 POS 结构,达到坐标转换的目的。

校正思路:在了解了校正原理之后,我们可以得出下面的一个从物理 坐标到像素坐标的转换关系式:

LCDx = xfac*Px+xoff
LCDy= yfac*Py+yoff

其中(LCDx, 是在 LCD 上的像素坐标,( Px,Py )是从触摸屏读到的物理坐标。 xfac,yfac 分别是 X 轴方向和 Y 轴方向的比例因子,而 xoff 和 yoff 则是这两个方向的偏移量。这样我们只要事先在屏幕上面显示 4 个点(这四个点的坐标是已 知的),分别按这四个点就可以从触摸屏读到 4 个物理坐标,这样就可以通过待定系数法求出 xfac 、 yfac 、 xoff 、 yoff 这四个参数。我们保存好这四个参数,在以后的使用中,我们把所有得到的物理坐标都按照这个关系式来计算,得到的就是准确的屏幕坐标,达到了触摸屏校准的目的。

/*显示屏初始化*/
u8 TP_Init(void)
{		
	GPIO_InitTypeDef GPIO_InitStructure;//GPIO 

	if(lcddev.id==0X5510)				//4.3寸电容触摸屏
	{
		if(GT9147_Init()==0)			//是GT9147
		{ 
			tp_dev.scan=GT9147_Scan;	//扫描函数指向GT9147触摸屏扫描
		}
		else
		{
			OTT2001A_Init();
			tp_dev.scan=OTT2001A_Scan;	//扫描函数指向OTT2001A触摸屏扫描
		}
		tp_dev.touchtype|=0X80;			//电容屏 
		tp_dev.touchtype|=lcddev.dir&0X01;//横屏还是竖屏 
		return 0;
	}
	else if(lcddev.id==0X1963)			//7寸电容触摸屏
	{
		FT5206_Init();
		tp_dev.scan=FT5206_Scan;		//扫描函数指向GT9147触摸屏扫描		
		tp_dev.touchtype|=0X80;			//电容屏 
		tp_dev.touchtype|=lcddev.dir&0X01;//横屏还是竖屏 
		return 0;
	}
	else
	{ 
		//注意,时钟使能之后,对GPIO的操作才有效
		//所以上拉之前,必须使能时钟.才能实现真正的上拉输出

		RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC  | RCC_APB2Periph_AFIO, ENABLE);

		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);
		//GPIOC->ODR|=0X200f;    //PC0~3 13 全部上拉  
		TP_Read_XY(&tp_dev.x[0],&tp_dev.y[0]);//第一次读取初始化	 
		AT24CXX_Init();//初始化24CXX
		if(TP_Get_Adjdata())
			return 0;//已经校准
		else			   //未校准?
		{ 										    
			LCD_Clear(WHITE);//清屏
			TP_Adjust();  //屏幕校准 
			TP_Save_Adjdata();	 
		}			
		TP_Get_Adjdata();	
	}
	return 1; 									 
}

参考:

  1. 电阻式触摸屏组成结构及工作原理
  2. android 电容屏(一):电容屏基本原理篇
  3. 触摸屏手机的工作原理是怎样的?
  4. 基于STM32的触摸屏学习笔记
  5. 第29章 电容触摸屏—触摸画板—零死角玩转STM32-F429系列

你可能感兴趣的:(stm32开发,STM32学习笔记)