⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 touchpad.c

📁 触摸屏算法////////////////////////////
💻 C
字号:

/***************************************************************************************
****************************************************************************************
* FILE		: TouchPad.c
* Description	: 
*			  
* Copyright (c) 2003~2008 by 创易电子(Design Easy). All Rights Reserved.
* 
* History:
* Version		Name       		Date			Description
   1.0		Wang Shaowei	2008/12/17	Initial Version (For DesignEasy LCM MCU board)
   1.1		Liu Ying			2008/12/22	Comments
   
****************************************************************************************
****************************************************************************************/



#include "touchpad.h"



#ifdef _DESIGN_EASY_LCM_MCU_PLATFORM_


sbit TP_nCS = P1^2;
sbit TP_DCLK = P1^3;
sbit TP_DIN = P1^4;
sbit TP_nBUSY = P1^5;
sbit TP_DOUT = P1^6;


#else

//add your own code here

#endif



#ifdef _DESIGN_EASY_TP_TYPE_ET2046_

/*该部分宏,参看触摸屏控制电路芯片文档中命令时序格式的介绍*/

#define TOUCH_PANEL_START_BIT    0x80

#define TOUCH_PANEL_TEMP0        0x00
#define TOUCH_PANEL_X_PLUS       0x10
#define TOUCH_PANEL_VBAT         0x20
#define TOUCH_PANEL_Z1_POS       0x30
#define TOUCH_PANEL_Z2_POS       0x40
#define TOUCH_PANEL_Y_PLUS       0x50
#define TOUCH_PANEL_TEMP1        0x70

#define TOUCH_PANEL_ADC3         0x20
#define TOUCH_PANEL_ADC4         0x60

#define TOUCH_PANEL_12BIT_SAMPLE 0x0
#define TOUCH_PANEL_8BIT_SAMPLE  0x8

#define TOUCH_PANEL_DIFF_MODE    0x0
#define TOUCH_PANEL_SINGLE_MODE  0x4

#define TOUCH_PANEL_PWD_ENBLE    0x0
#define TOUCH_PANEL_IRQ_DISABLE  0x1
#define TOUCH_PANEL_PWD_DISABLE  0x3
#define TOUCH_PANEL_PWD_RESET    0x2


/*坐标转化参数的默认值,可以直接使用*/
/*
AX=0B 86 AY=0A FE BX=04 9E BY=04 44 
XK=00 14 XA=01 2E YK=00 0F YA=01 0B
*/
#define TOUCH_PANEL_ARG_X_K		0x14
#define TOUCH_PANEL_ARG_X_A		0x12E
#define TOUCH_PANEL_ARG_Y_K		0x0F
#define TOUCH_PANEL_ARG_Y_A		0x10B

#endif



/*
考虑到非校准情况下的触摸屏,通常都是写字、画画等连续的,
在我们的系统中,对于触摸功能是这样处理的:
首次触摸是由中断检测得到,之后响应中断进行处理;
处理完后开启一个5ms定时器,
定时器到时处理时主动读取触摸坐标,
     若是有效坐标则处理,处理完后同样开启一个5ms定时器
     若是无效坐标则表示没有触摸,此时再清除中断标记、
     重新打开中断响应,等待下一次触摸中断
注意不要太早打开中断,一则因为系统中断不宜太频繁,二者避免误触发

bTP_INT用于标记该次处理是否由中断触发,
true: 是中断触发,
false: 是定时器主动读取
*/
bool bTP_INT = FALSE;


/*
bTP_Cali用于标记是否处于校准界面,
true: 处于校准界面
false: 非校准界面
*/
bool bTP_Cali = FALSE;



/*
最近一次得到的触摸坐标,
包括中断响应中或者定时器中得到的两种情况,
都记录在这里
*/
U16 TP_X = 0, TP_Y = 0;


typedef struct 
{
	U16 X;
	U16 Y;
}TP_POS;

/*记录在两次校准中得到的触摸坐标*/
TP_POS TPposA, TPposB;



typedef struct 
{
	U16 K;
	U16 A;
}TP_ARG;

/*坐标转化参数,从触摸获得的数值到LCD的坐标之间需要进行映射转换*/
TP_ARG TPargX, TPargY;


/*
校准说明: 
Xt = Kx * Xl + Ax
Yt = Ky * Yl + Ay

Xt: TP ADC 触摸获得的坐标
Kx: X arg
Xl: LCD pos lcd的坐标
Ax: X offset

Yt: TP ADC 触摸获得的坐标
Ky: Y arg
Yl: LCD pos lcd的坐标
Ay: Y offset

lcd和触摸屏的坐标之间的转换就是上面这两个公式,
用于校准,(从两组Xl Yl Xt Yt求出Kx Ky Ax Ay)
也用于转化得到lcd的坐标(从Xt Yt Kx Ky Ax Ay求出Xl Yl )
*/





/*==================================================================
* Function	: serial_write_bit_high
* Description	: 写bit1,TP_nCS 之前已经拉低
* Input Para	: void
* Output Para	: void
* Return Value: void
==================================================================*/
void serial_write_bit_high(void)
{

   	TP_DIN = 1;   
   
   	TP_DCLK = 0;          
   	TP_DCLK = 1;
   	TP_DCLK = 0;

	return;
}


/*==================================================================
* Function	: serial_write_bit_low
* Description	: 写bit0,TP_nCS 之前已经拉低
* Input Para	: void
* Output Para	: void
* Return Value: void
==================================================================*/
void serial_write_bit_low(void)
{
   	TP_DIN = 0;  
   
   	TP_DCLK = 0;          
   	TP_DCLK = 1;
   	TP_DCLK = 0;   

	return;
}


/* 
关于TP_nCS何时置1何时置0的说明:
因为对触摸屏控制电路的控制,都是先写命令、然后读数据的,
所以下面的serial_write_data、serial_read_data总是成对出现,
我们总是在serial_write_data开始时将TP_nCS置0,
在serial_read_data结束时将TP_nCS置1,
这部分请看触摸控制电路芯片时序图
*/


/*==================================================================
* Function	: serial_write_bit_low
* Description	: 写8bit字节,开始将TP_nCS置0
* Input Para	: U8 SendData
* Output Para	: void
* Return Value: void
==================================================================*/
void serial_write_data(U8 SendData)
{
   	S8 i;
	
	TP_DCLK = 0;
	TP_nCS = 0;

	/*从bit7到bit0依次传送*/
	for (i=7; i>=0; i--)
	{
		if (SendData & (1<<i))
		{
		   serial_write_bit_high();
		}
		else
		{
			serial_write_bit_low();
		}
	}
	
   	TP_DIN = 0;

	return;
}



/*==================================================================
* Function	: serial_read_data
* Description	: 读12bit数据,TP_nCS 之前已经置零,结束时需将TP_nCS置1
* Input Para	: void
* Output Para	: void
* Return Value: U16 读到的数据,只有12bit有效
==================================================================*/
U16 serial_read_data(void)
{ 
	S8 i;  
   	U16 RcvData=0; 
    U32 retry=0;	
	
   	TP_DCLK = 0; 
   	TP_DCLK = 1; 

	/*等待一段时间*/
	while(TP_nBUSY)
   	{

 	   	TP_DCLK = 0; 
   		TP_DCLK = 1; 
   		retry++;
   		if(retry>100000)
   		{
   			return 0;
   		}
   	};
	
   	for(i=11; i>=0; i--)
   	{
      	TP_DCLK = 1;
		if(TP_DOUT)
		{
			RcvData |= (1<<i); 
		}
		TP_DCLK = 0; 
   	}
	
	TP_nCS = 1;
	
   	return(RcvData);
}



/*==================================================================
* Function	: tp_init
* Description	: 初始化tp
* Input Para	: void
* Output Para	: void
* Return Value: void
==================================================================*/
void tp_init(void)
{
	U8 cmd = 0;

	/*在校准之前也可以使用触摸屏,用这一组默认参数。
	校准之后用校准得到的参数*/
	TPargX.K = TOUCH_PANEL_ARG_X_K;
	TPargX.A = TOUCH_PANEL_ARG_X_A;	
	TPargY.K = TOUCH_PANEL_ARG_Y_K;
	TPargY.A = TOUCH_PANEL_ARG_Y_A;

	/*送初始化命令*/
   	cmd=TOUCH_PANEL_START_BIT|TOUCH_PANEL_Y_PLUS|TOUCH_PANEL_12BIT_SAMPLE| TOUCH_PANEL_DIFF_MODE;
   	serial_write_data(cmd);
	serial_read_data();

	return;
}



/*==================================================================
* Function	: tp_read_adc
* Description	: 得到触摸坐标
* Input Para	: void
* Output Para	: U16 * X, U16 * Y,若全为0表示获得坐标不成功
* Return Value: void
==================================================================*/
void tp_read_adc(U16 * X, U16 * Y)
{
	U8 cmd = 0;
	
   /*读取X 坐标X ADC*/
	cmd=TOUCH_PANEL_START_BIT|TOUCH_PANEL_Y_PLUS|TOUCH_PANEL_12BIT_SAMPLE| TOUCH_PANEL_DIFF_MODE;
   	serial_write_data(cmd);
	*X=serial_read_data();
	
	cmd = 0;
   /*读取Y 坐标Y ADC*/
   	cmd=TOUCH_PANEL_START_BIT|TOUCH_PANEL_X_PLUS|TOUCH_PANEL_12BIT_SAMPLE| TOUCH_PANEL_DIFF_MODE ;
   	serial_write_data(cmd);
	*Y=serial_read_data();

	/*坐标转化*/
	/*触摸屏和LCD屏幕的原点不同,一个在左下,一个在左上,需要将Y坐标转化一下*/
	*Y = 4096 - *Y;
	/*触摸屏的范围大于LCD屏幕范围,判断是否超出了四周的边框*/
	if(*X < 0x0080 || *Y < 0x0080 || *X > 0xF80 || *Y > 0xF80)
	{
		*X = 0;
		*Y = 0;
	}

	return;
}



/*
由于触摸部分的应用和各自系统紧密相关,
单纯的触摸屏驱动只需提供tp_init()和tp_read_adc()两个函数即可。
用户可以用类似下面的函数打印函数或者触摸坐标,以此知道是否调试成功:
void TestTP(void)
{
	U16 XPos = 0;
	U16 YPos = 0;
	
	while (1)
	{
		tp_read_adc(&XPos, &YPos);

		if ((XPos == 0) || (YPos == 0))
		{
			uty_delay(0x1000);
		}
		else
		{
			XPos = (XPos - TPargX.A) / TPargX.K;
			YPos = (YPos - TPargY.A) / TPargY.K;
			
			uprintf("\nX=");
			uputchar(XPos >> 8);
			uputchar(XPos& 0xFF);
			uprintf("  Y=");
			uputchar(YPos >> 8);
			uputchar(YPos & 0xFF);
			break;
		}
	}

	return;
}


在设计各自的触摸应用程序时,可以参考以下3个函数,
完成校准和显示轨迹
*/


#ifdef _DESIGN_EASY_TP_TEST_


/*以下两部分,在创易demo系统中放在别的文件中,这里拷贝过来,供参考*/
#if 0  


/*==================================================================
* Function	: extern_interrupt1
* Description	: 触摸中断处理函数,检测到触摸后需要调用tp_task
* Input Para	: 
* Output Para	: 
* Return Value: 
==================================================================*/
void extern_interrupt1(void) interrupt 2
{
	EX1 = 0;
	
	tp_read_adc(&TP_X, &TP_Y);
	
	if(TP_X > 0)
	{
		/*一笔的开始*/
		bTP_INT = TRUE;
		
		MSTimerStart(MSTIMERMODE_HANDLE_IN_MSG, 10, tp_task);
	}
	else
	{
		IE1 = 0;
		EX1 = 1;	
	}
}



/*==================================================================*/
extern void lcd_display(EColor color);
/*case LCD_TP_CALI: 分支,显示了校准的界面*/


#endif


U8 Cali_Cnt = 0;


/*==================================================================
* Function	: tp_task
* Description	: 触摸应用的主要函数,在触摸中断处理中需要调用这个函数
			  在5ms定时器处理中也需要调用这个函数
* Input Para	: void
* Output Para	: void
* Return Value: void
==================================================================*/
void tp_task(void)
{
	if(bTP_Cali == TRUE)
	{
		/*校准,需要先后采集两个点,都是从中断处理中获得的坐标,
		所以处理完不要完了开中断*/

		if(Cali_Cnt == 0)
		{
			/*采集第一个点*/

			#if 0
			/*大致判断是否在非左下*/
			if(TP_X < 2048 || TP_Y < 2048)
			{
				IE1 = 0;
				EX1 = 1;
				return;
			}
			#endif
			
			TPposA.X= TP_X;
			TPposA.Y = TP_Y;
			
			Cali_Cnt++;

			/* 清除第一个点,画第二个点??? */
			lcd_display_rectangle_color(42, 53, 46, 57, BLUE);
			lcd_display_rectangle_color(130, 163, 134, 167, WHITE);			
		}
		else
		{
			/*采集第二个点*/

			#if 0
			/*大致判断是否在非右上*/
			if(TP_X > 2048 || TP_Y > 2048)
			{
				IE1 = 0;
				EX1 = 1;
				return;
			}
			#endif
			
			TPposB.X= TP_X;
			TPposB.Y = TP_Y;

			/*校准结束,恢复标志变量*/
			Cali_Cnt = 0;
			bTP_Cali = FALSE;

			/*清除第二个点*/
			lcd_display_rectangle_color(42, 53, 46, 57, WHITE);
			
			/*重新计算参数??? */
			TPargX.K = (TPposA.X - TPposB.X) / 88;
			TPargY.K = (TPposA.Y - TPposB.Y) / 110;

			TPargX.A = TPposB.X -(TPargX.K * 44); 
			TPargY.A = TPposB.Y -(TPargY.K * 55); 
			
			/*用户可以自己考虑校验失败的可能,
			最简单的方法就是限定TPargX TPargY的四个值在一定范围内*/
		}
		
		IE1 = 0;
		EX1 = 1;
	}
	else
	{
		/*显示触摸轨迹*/
		
		if(bTP_INT)
		{
			/*一笔的起点,坐标从中断中获得*/
			lcd_display_dot((TP_X - TPargX.A) / TPargX.K, (TP_Y - TPargY.A) / TPargY.K);
			
			bTP_INT = FALSE;

			/*5ms之后再次调用tp_task*/
			MSTimerStart(MSTIMERMODE_HANDLE_IN_MSG, 5, tp_task);
		}
		else
		{
			/*定时器处理,需要主动读取坐标*/
			tp_read_adc(&TP_X, &TP_Y);

			if(TP_X > 0)
			{
				lcd_display_dot((TP_X - TPargX.A) / TPargX.K, (TP_Y - TPargY.A) / TPargY.K);

				/*5ms之后再次调用tp_task*/
				MSTimerStart(MSTIMERMODE_HANDLE_IN_MSG, 5, tp_task);
			}
			else
			{
				/*没有读到有效的坐标,一笔结束,重新打开中断*/
				IE1 = 0;
				EX1 = 1;
			}
		}
	}

	return;
}

  
#endif

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -