基于HAL库的STM32F704的电阻式触摸屏的学习


内容基于正点原子的代码分析

一、XPT2046介绍

XPT2046是一款4导线制触摸屏控制器,内含12位分辨率125KHz转换速率逐步逼近型A/D转换器。XPT2046支持从1.5V到5.25V的低电压I/O接口。XPT2046能通过执行两次A/D转换查出被按的屏幕位置, 除此之外,还可以测量加在触摸屏上的压力。内部自带2.5V参考电压可以作为辅助输入、温度测量和电池监测模式之用,电池监测的电压范围可以从0V到6V。XPT2046片内集成有一个温度传感器

芯片引脚图
在这里插入图片描述

在这里插入图片描述

红框内为XPT2046与MCU通讯用引脚(错了,NO BUSY;YES PENIRQ )

二、XPT2046底层代码的编写

1.根据时序图编写读写程序

在这里插入图片描述
在这里插入图片描述

时序图,课根据时序图,写出伪代码,再讲伪代码编辑为实际代码。关于延时部分参考XPT2046数据手册(请点进去后复制网址)。这里不再重复造轮子


/*
*************************************************************
*伪代码,函数功能:MCU发送数据给XPT2046,XPT2046返回数据给MCU
**************************************************************
*/
void xpt2046_write_byte(u8 write_data)
{
	for(i=0;i<8;i++)
	{
		DCLK = 0;		
		if(write_data & 0x80)//S为1时有效
			TDIN = 1;//MOSI
		else 
			TDIN = 0;
		//1us		//MCU准备数据完成
		DCLK = 1;
		//1us
		rite_data <<= 1;
	}
}

u16 xpt2046_read(u8 CMD)
{
	u16 xpt2046_read = 0;
	CS = 0;//片选
	DCLK  = 0;//拉低
	DIN = 0;

	xpt2046_write_byte(CMD);
	/*  过滤忙信号 */
		DCLK = 0;
		//延时		
		DCLK = 1;
		//延时

	for(i = 0 ; i<16;i++)
	{
		DCLK = 0;	//XPT2046开始准备数据
		//1us
		DCLK = 1;	//XPT2046开始准备数据
		xpt2046_read<<=1;//空出最低位来保存读取到的数据 
		xpt2046_read |= DOUT;//MISO
		//1us
	}
	DCLK = 0;	//完整周期
	CS = 1;		//取消选择
	return (xpt2046_read<<=4);
}
/*
*************************************************************
*正点原子的代码
**************************************************************
*/
//SPI写数据
//向触摸屏IC写入1byte数据    
//num:要写入的数据
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; 
		delay_us(1);
		TCLK=1;		//上升沿有效	        
	}		 			    
} 		 
//SPI读数据 
//从触摸屏IC读取adc值
//CMD:指令
//返回值:读到的数据	   
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
	delay_us(1);    
	TCLK=0; 	     	    
	for(count=0;count<16;count++)//读出16位数据,只有高12位有效 
	{ 				  
		Num<<=1; 	 
		TCLK=0;	//下降沿有效  	    	   
		delay_us(1);    
 		TCLK=1;
 		if(DOUT)Num++; 		 
	}  	
	Num>>=4;   	//只有高12位有效.
	TCS=1;		//释放片选	 
	return(Num);   
}

2.XPT2046命令

命令

在这里插入图片描述

在这里插入图片描述

对应的两个命令:

u8 CMD_RDX=0XD0;//0XD0 为 11010000,读取X轴
u8 CMD_RDY=0X90;//0X90 为 10010000,读取Y轴

三、主要代码分析

1.结构体以及初始化

typedef struct
{
	u8 (*init)(void);			//初始化触摸屏控制器
	u8 (*scan)(u8);				//扫描触摸屏.0,屏幕扫描;1,物理坐标;	 
	void (*adjust)(void);		//触摸屏校准 
	u16 x[CT_MAX_TOUCH]; 		//当前坐标
	u16 y[CT_MAX_TOUCH];		//电容屏有最多5组坐标,电阻屏则用x[0],y[0]代表:此次扫描时,触屏的坐标,用
								//x[4],y[4]存储第一次按下时的坐标. 
	u8  sta;					//笔的状态 
								//b7:按下1/松开0; 
	                            //b6:0,没有按键按下;1,有按键按下. 
								//b5:保留
								//b4~b0:电容触摸屏按下的点数(0,表示未按下,1表示按下)
/触摸屏校准参数(电容屏不需要校准)//								
	float xfac;					
	float yfac;
	short xoff;
	short yoff;	   
//新增的参数,当触摸屏的左右上下完全颠倒时需要用到.
//b0:0,竖屏(适合左右为X坐标,上下为Y坐标的TP)
//   1,横屏(适合左右为Y坐标,上下为X坐标的TP) 
//b1~6:保留.
//b7:0,电阻屏
//   1,电容屏 
	u8 touchtype;
}_m_tp_dev;
/*
*****************************************************
结构体初始化
*****************************************************
*/
_m_tp_dev tp_dev=
{
	TP_Init,
	TP_Scan,
	TP_Adjust,
	0,
	0, 
	0,
	0,
	0,
	0,	  	 		
	0,
	0,	  	 		
};	

通过结构体方便控制底层,HAL库也是如此,值得学习,包含了函数指针、变量等。其中
TP_Init(),TP_Scan(),TP_Adjust()是重点掌握的函数。

1.判断是否触摸

//		  
//触摸按键扫描
//tp:0,屏幕坐标;1,物理坐标(校准等特殊场合用)
//返回值:当前触屏状态.
//0,触屏无触摸;1,触屏有触摸
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;//LCDx=xfac*Px+xoff;	将结果转换为屏幕坐标
			tp_dev.y[0]=tp_dev.yfac*tp_dev.y[0]+tp_dev.yoff;//LCDy=yfac*Py+yoff;	需要校准
																
	 	} 
		if((tp_dev.sta&TP_PRES_DOWN)==0)//之前没有被按下
		{		 
			tp_dev.sta=TP_PRES_DOWN|TP_CATH_PRES;//按键按下		  #define TP_PRES_DOWN 0x80  	  
												触屏被按	下	  #define TP_CATH_PRES 0x40  
			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);//b1置0,标志标记按键松开	
		}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;//返回当前的触屏状态
}	  

2.校准

//触摸屏校准代码
//得到四个校准参数
void TP_Adjust(void)
{								 
	u16 pos_temp[4][2];//坐标缓存值
	u8  cnt=0;	
	u16 d1,d2;
	u32 tem1,tem2;
	double fac; 	
	u16 outtime=0;
 	cnt=0;				
	POINT_COLOR=BLUE;
	BACK_COLOR =WHITE;
	LCD_Clear(WHITE);//清屏   
	POINT_COLOR=RED;//红色 
	LCD_Clear(WHITE);//清屏 	   
	POINT_COLOR=BLACK;
	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秒钟没有按下,则自动退出
	{
		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:	 //全部四个点已经得到
	    		    //对边相等
					tem1=abs(pos_temp[0][0]-pos_temp[1][0]);//x1-x2
					tem2=abs(pos_temp[0][1]-pos_temp[1][1]);//y1-y2
					tem1*=tem1;
					tem2*=tem2;
					d1=sqrt(tem1+tem2);//得到1,2的距离
					
					tem1=abs(pos_temp[2][0]-pos_temp[3][0]);//x3-x4
					tem2=abs(pos_temp[2][1]-pos_temp[3][1]);//y3-y4
					tem1*=tem1;
					tem2*=tem2;
					d2=sqrt(tem1+tem2);//得到3,4的距离
					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);//显示数据   
 						continue;
					}
					tem1=abs(pos_temp[0][0]-pos_temp[2][0]);//x1-x3
					tem2=abs(pos_temp[0][1]-pos_temp[2][1]);//y1-y3
					tem1*=tem1;
					tem2*=tem2;
					d1=sqrt(tem1+tem2);//得到1,3的距离
					
					tem1=abs(pos_temp[1][0]-pos_temp[3][0]);//x2-x4
					tem2=abs(pos_temp[1][1]-pos_temp[3][1]);//y2-y4
					tem1*=tem1;
					tem2*=tem2;
					d2=sqrt(tem1+tem2);//得到2,4的距离
					fac=(float)d1/d2;
					if(fac<0.95||fac>1.05)//不合格
					{
						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);//显示数据   
						continue;
					}//正确了
								   
					//对角线相等
					tem1=abs(pos_temp[1][0]-pos_temp[2][0]);//x1-x3
					tem2=abs(pos_temp[1][1]-pos_temp[2][1]);//y1-y3
					tem1*=tem1;
					tem2*=tem2;
					d1=sqrt(tem1+tem2);//得到1,4的距离
	
					tem1=abs(pos_temp[0][0]-pos_temp[3][0]);//x2-x4
					tem2=abs(pos_temp[0][1]-pos_temp[3][1]);//y2-y4
					tem1*=tem1;
					tem2*=tem2;
					d2=sqrt(tem1+tem2);//得到2,3的距离
					fac=(float)d1/d2;
					if(fac<0.95||fac>1.05)//不合格
					{
						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);//显示数据   
						continue;
					}//正确了
					//计算结果
					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;
	 	} 
 	}
}

算了,就这样吧,不想写了

四、实战

在这里插入图片描述

在这里插入图片描述

CUBUMX相关配置

/*
*************************************************************************************
简易画板
*************************************************************************************
*/
static u8 Dft_Scan_Dir = 0;
u16 Ssd_Hor_Resolution	=320;		//LCD水平分辨率
u16 Ssd_Ver_Resolution	=240;		//LCD垂直分辨率

void drow_block_colar(void)
{   
 	LCD_Fill(0,Ssd_Ver_Resolution-40,Ssd_Hor_Resolution*1/6,Ssd_Ver_Resolution,RED);
	LCD_Fill(Ssd_Hor_Resolution*1/6,Ssd_Ver_Resolution-40,Ssd_Hor_Resolution*2/6,Ssd_Ver_Resolution,YELLOW);
	LCD_Fill(Ssd_Hor_Resolution*2/6,Ssd_Ver_Resolution-40,Ssd_Hor_Resolution*3/6,Ssd_Ver_Resolution,BLUE);
	LCD_Fill(Ssd_Hor_Resolution*3/6,Ssd_Ver_Resolution-40,Ssd_Hor_Resolution*4/6,Ssd_Ver_Resolution,GREEN);
	LCD_Fill(Ssd_Hor_Resolution*4/6,Ssd_Ver_Resolution-40,Ssd_Hor_Resolution*5/6,Ssd_Ver_Resolution,BLACK);
	LCD_ShowString(Ssd_Hor_Resolution*5/6+1,Ssd_Ver_Resolution-40,Ssd_Hor_Resolution,16,16,"ERASER");//显示清屏区域
}
//清空屏幕并在右上角显示"RST"	
void Load_Drow_Dialog(void)
{
	LCD_Clear(WHITE);//清屏   
 	POINT_COLOR=BLUE;//设置字体为蓝色 
	LCD_ShowString(lcddev.width-24,0,200,16,16,"RST");//显示清屏区域
  	POINT_COLOR=RED;//设置画笔蓝色 
	drow_block_colar();
}
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 if(tp_dev.y[0]>Ssd_Ver_Resolution-40&&tp_dev.y[0]<Ssd_Ver_Resolution)
					{	
						 if(tp_dev.x[0]>0&&tp_dev.x[0]<=Ssd_Hor_Resolution*1/6)
							  
							POINT_COLOR = RED;
						
						else if(tp_dev.x[0]>Ssd_Hor_Resolution*1/6&&tp_dev.x[0]<=Ssd_Hor_Resolution*2/6)
							POINT_COLOR = YELLOW;
						
						else if(tp_dev.x[0]>Ssd_Hor_Resolution*2/6&&tp_dev.x[0]<=Ssd_Hor_Resolution*3/6)
							POINT_COLOR = BLUE;
						
						else if(tp_dev.x[0]>Ssd_Hor_Resolution*3/6&&tp_dev.x[0]<=Ssd_Hor_Resolution*4/6)
							POINT_COLOR = GREEN;
						
						else if(tp_dev.x[0]>Ssd_Hor_Resolution*4/6&&tp_dev.x[0]<=Ssd_Hor_Resolution*5/6)
							POINT_COLOR = BLACK;
						
						else if(tp_dev.x[0]>Ssd_Hor_Resolution*5/6&&tp_dev.x[0]<=Ssd_Hor_Resolution)
							POINT_COLOR = BACK_COLOR;	
				   }
				else
				{	
					if(POINT_COLOR == BACK_COLOR)
					LCD_Fill(tp_dev.x[0],tp_dev.y[0],tp_dev.x[0]+5,tp_dev.y[0]+5,POINT_COLOR);
					else
					TP_Draw_Big_Point(tp_dev.x[0],tp_dev.y[0],POINT_COLOR);		//画图
				}					
					
			}
		}else delay_ms(10);	//没有按键按下的时候 	    
		if(key==KEY0_PRES)	//KEY0按下,则执行校准程序
		{
			LCD_Clear(WHITE);	//清屏
		    TP_Adjust();  		//屏幕校准 
			TP_Save_Adjdata();	 
			Load_Drow_Dialog();
		}
		i++;
		if(i%20==0)PFout(9)=!PFout(9);
	}
}
/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */
	u8 lcd_id[12];				//存放LCD ID字符串
	u8 datatemp[SIZE];
	u8 KEY;
	u8 test[256];
	u8 i;
  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_USART1_UART_Init();
  MX_FSMC_Init();
  MX_TIM6_Init();
  /* USER CODE BEGIN 2 */
	delay_init(168);
	HAL_TIM_Base_Start_IT(&htim6);
	LCD_Init();           			//初始化LCD FSMC接口
	AT24CXX_Init();				    //初始化IIC
	if(Dft_Scan_Dir)
	{
		Ssd_Hor_Resolution = 320;
		Ssd_Ver_Resolution = 240;
	}		
	else
	{
		Ssd_Hor_Resolution = 240;
		Ssd_Ver_Resolution = 320;		
	}
	LCD_Display_Dir(Dft_Scan_Dir);
	tp_dev.init();				    //触摸屏初始化 
	drow_block_colar();
   	if(tp_dev.touchtype!=0XFF)
	{
		LCD_ShowString(0,0,200,16,16,"Press KEY0 to Adjust");//电阻屏才显示
	}
	delay_ms(1500);
 	Load_Drow_Dialog();
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
	rtp_test();  
  }
  /* USER CODE END 3 */
}

效果图:

在这里插入图片描述

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值