资讯详情

STM32学习笔记一一触摸屏

前言:

为了方便查看博客,专门申请了微信官方账号,附上二维码。感兴趣的朋友可以关注我,和我讨论学习,享受技术,一起成长。

在这里插入图片描述


1. 简介

1.1 电阻式触摸屏

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

1.1.1 电阻触摸屏的原理

电阻触摸屏的主要部分是一个与显示器表面非常匹配的电阻膜屏,是一种多层复合膜,以玻璃或硬塑料板为基层,表面涂有透明氧化金属(透明导电阻)导电层,上面覆盖了一层外表面硬化处理、光滑防擦的塑料层,其内表面也覆盖了一层涂层,两者之间有许多小(小于 1/1000 英寸)透明隔离点隔离两层导电层。

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

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

1.1.2 电阻触摸屏获得触点坐标

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

1.1.3 电阻触摸屏的优缺点

精度高,价格低,抗干扰能力强,稳定性好 。

易划伤,透光性差,不支持多点触摸。直接的感觉是体验不如电容屏。

1.2 电容式触摸屏

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

当手指点击屏幕时,它会从接触点吸收少量电流,导致角电极的压降,并通过感应人体的弱电流来达到触摸的目的。

1.2.1 表面电容电容触摸屏

表面电容触摸屏技术的使用 ITO 是的 透明导电材料导电膜通过电场感应感知屏幕表面的触摸行为。但表面电容式触摸屏有一些局限性,只能识别一个手指或一次触摸。

1.2.2 投影电容触摸屏

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

自我电容器又称绝对电容器,通常是由扫描电极和地面组成的电容器。玻璃表面有用 ITO 横向和纵向扫描电极形成电容的两极。当用手或触摸笔触摸时,电容并联到电路中,从而改变扫描线上的整体电容。控制扫描时 IC 纵向和横向电极依次扫描,触摸点坐标位置根据扫描前后电容变化确定。这就是笔记本电脑触摸输入板的方法。笔记本电脑的输入板采用 X*Y 传感电极阵列形成传感格子, 当手指靠近触摸输入板时,在手指和传感电极之间产生少量电荷。用特定的操作规则处理手指的位置,列出传感器的信号。

又称跨越电容,是玻璃表面的横向和纵向 ITO 电极的交叉处形成电容。扫描交互电容器的方法是扫描每个交叉点的电容器变化,以确定触点的位置。触摸时会影响相邻电极的耦合,从而改变交叉处的电容。交互式电容器的扫描方法可以检测到每个交叉点的电容值和触摸后的电容变化。因此,与自我电容器的扫描方法相比,它需要更长的扫描时间和扫描检测 X*Y 根电极。 目前智能手机 平板电脑等触摸屏 ,都是采用 交互式电容技术。

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

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

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 ,读取相应的数据点有效,点无效。

模块只支持最多 5 触点。只有表中 5 用于表示对应点坐标是否有效,其余为保留位(读为 0 ),通过读取寄存器,我们可以知道哪些点有数据,哪些点没有数据。如果我们读到的都是 0 ,说明没有触摸。

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

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

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

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

通过 4 个寄存器读出,比如读取坐标 A: (X1,Y一、可读取 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 初始化流程

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
        标签: 金属电容触屏笔

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台