ARM2440触摸屏编程(裸机编程)

俗话说:合抱之木,生于毫末;九层之台,起于累土;千里之行,始于足下。只有基础扎实了,做事情才能更得心应手。对于编程也是一样的,只有对各个器件的工作原理摸得一清二楚,才能高效地写出好代码。所以学习驱动和裸机编程时我都喜欢先把器件的工作原理先过一下。

触摸屏的基本原理可以知道,在使用触摸屏时,若SOC内部集成了触摸屏控制器,我们主要的任务就是获取触摸屏按下的的时候的位置,即xy方向上的坐标值,此值可以通过电压转换得出,获取电压的值使用ADC转换。

note:电阻触摸屏本质上就是个电阻分压器,不同的触点在x、y方向上的分压值不一样。

  • 要测量x坐标时,xp接3.3V点源XM接地,YP/YM不接电源,通过计测出Yp的点压既可以知道x的坐标。
  • 要测量y坐标时,yp接3.3V点源yM接地,xP/xM不接电源,通过计测出xp的点压既可以知道y的坐标。

①平时触摸屏没有按下时,等效电路图:

处于等待中断模式中:s5、s4闭合,当有触点按下时,触摸屏相当于接地。Y_ADC从高电平切换到低电平,这个可以用来做中断触发信号来通知CPU触摸屏已经按下了。

②s1、s3闭合时通过对x_adc(y轴上的分压点)电压的采集可以获取到x的坐标(滑动变阻器类似),同样s2和s4闭合,可以通过对y_adc(x轴上的分压点)电压的采集可以获取到y的坐标。

ARM2440中触摸屏有4种工作模式:

  • 等待中断模式:设置ADCTSC寄存器为0xD3、此模式下触摸屏在等待着被按下,触摸屏被按下时Y_ADC电位发生变化,产生中断INT_TC。对S3C2440可以设置bit8来表示等待的是pen down还是pen up中断
    • 等待模式有两种:等待pen down(触摸屏还没有按下的时候)模式、等待pen up(触摸屏按下后)模式
  • 分离的x/y轴坐标转换模式:设置相应的控制位(即s1-s5的闭合状态),进入x/y坐标转换模式。x坐标值转换完毕后被写入ADCDAT0,y坐标转换完毕后被写入ADCDAT1,然后触发相应的INT_ADC中断。
  • 自动(连续)x/y轴坐标转换模式:设置相应的寄存器。让触摸屏控制器进入自动x/y轴坐标转换模式,触摸屏控制器就会自动转换触点的x、y坐标值。并分别写入ADCDAT0和ADCDAT1中,然后发出INT_ADC中断。
  • 普通转换模式:不使用触摸屏时,当做普通的ADC转换。数据存入ADCDAT0中。

所以编程的时候需要特别注意的是:控制这些开关(S1~S5)即设置相应的工作模式,然后通过ADC电压转换获取坐标的值。

ARM2440中的触摸屏控制器和ADC转换放在一起。XP、XM、YP、YM这些引脚主要用在触摸屏上面,XP/YP分别是接在触摸屏X/Y轴上的正极处,而XM/YM则分别是接在触摸屏的X/Y轴的负极处(即接地)。不使用触摸屏时这些引脚可以用来当做普通的ADC模拟信号输入引脚。

从上图中可以知道触摸屏控制单元,能产生两种中断:①INT_TC触摸屏按下中断②INT_ADC,xy方向上的电压转换成功后,即ADC转换完成后的中断。这两个中断在S3C2440中断器中共用同一个中断号。所以中断服务程序中需要区分是ADC中断还是触摸屏中断。

ADC使用流程:

  • 设置ADCCON寄存器,选择输入信号通道,设置转换时钟。
  • 设置ADCTSC寄存器,用来设置使用触摸屏功能以及工作的模式。
  • 设置ADCCON寄存器,启动A/D转换。(有读寄存器开始转换和设置bit1主动转换)。
  • 转换结束后,可以读取ADCDAT0和ADCDAT1寄存器中的值,分别获取x/y的坐标。

触摸屏使用的流程:

  • 按下触摸屏,产生触摸中断
  • 在触摸中断中开启ADC转换,转换xy的电压。
  • ADC转换结束后,产生ADC中断
  • 读取ADCDAT数据或许xy的电压
  • 在定时器中断中,看触摸屏是否还被按下。(加定时器是为了支持滑动功能)
    • 要是按下,重新执行触摸中断服务函数。
    • 要是松开的话,就结束

触摸屏编程框架:

#include"../timer.h"

#define INT_ADC 		(31)
#define INT_ADC_S 	(10)
#define INT_TC		(9)

/*S3C2440中ADCTSC的定义
*UD_SEN : Detect Stylus Up or Down status.0 = Detect Stylus Down Interrupt Signal.1 = Detect Stylus Up Interrupt Signal.
*YM_SEN: YM Switch Enable(0 =0 = YM Output Driver Disable 1 = YM Output Driver Enable )
*YP_SEN:YP Switch Enable(0 = YP Output Driver Enable 1 = YP Output Driver Disable)
*XM_SEN: XM Switch Enable(0 =0 = XM Output Driver Disable 1 = XM Output Driver Enable )
*XP_SEN:XP Switch Enable(0 = XP Output Driver Enable 1 = XP Output Driver Disable)
*PULL_UP :Pull-up Switch Enable(0 = XP Pull-up Enable 1 = XP Pull-up Disable)
****
*AUTO_PST:Automatically sequencing conversion of X-Position and Y-Position
*XY_PST:Manually measurement of X-Position or Y-Position(00 = No operation mode 11 = Waiting for Interrupt Mode).
*/

#define DETECT_UP 			(1<<8)
#define DETECT_DOWN   		(0<<8)
#define YM_ENBALE			(1<<7)
#define YM_DISENBALE		(0<<7)
#define YP_ENBALE			(0<<6)
#define YP_DISENBALE		(1<<6)
#define XM_ENBALE			(1<<5)
#define XM_DISENBALE		(0<<5)
#define XP_ENBALE			(0<<4)
#define XP_DISENBALE		(1<<4)
#define PULL_UP_ENABLE		(0<<3)
#define PULL_UP_DISENABLE	(1<<3)
#define AUTO_PST                   (1<<2)
#define NORMAL_XYPST		(00)
#define WAIT_XYPST			(3)


static int global_ts_enable = 0;
static void ts_disable_timer_handler(void);
static void ts_enable_timer_handler(void);


/*
*设置ADCTSC寄存器,控制s1-s5,进入等待中断状态。(等待按下)
*/
void enter_wait_pendown_mode(void)
{
	volatile unsigned int *ADCTSC = (volatile unsigned int*)0x58000004;
	*ADCTSC =DETECT_DOWN|PULL_UP_ENABLE|YP_DISENBALE|\
		YM_ENBALE|XP_DISENBALE|XM_DISENBALE|WAIT_XYPST;
}
/*
*设置ADCTSC寄存器,控制s1-s5,进入等待中断状态。(等待松开)
*/
void enter_wait_pendup_mode(void)
{
	volatile unsigned int *ADCTSC = (volatile unsigned int*)0x58000004;
	*ADCTSC =DETECT_UP|PULL_UP_ENABLE|YP_DISENBALE|\
		YM_ENBALE|XP_DISENBALE|XM_DISENBALE|WAIT_XYPST;
}
/*
*设置ADCTSC寄存器,进入自动连续转换状态。
*/
void enter_auto_convert_mode(void)
{
	volatile unsigned int *ADCTSC = (volatile unsigned int*)0x58000004;
	*ADCTSC =AUTO_PST |NORMAL_XYPST;
}
/*
*开始ADC转换
*/
void start_adc_convert(void)
{
	volatile  unsigned int*ADCCON = (volatile unsigned int*)0x58000000;	
	*ADCCON |= 1;
}


void  irq_touch(void)
{
	/*需要辨别是触摸屏按下中断还是松开中断
	*ADCUPDN : 用来区分中断产生是松开还是按下
	*/
	volatile unsigned int *ADCUPDN = (volatile unsigned int*)0x58000014;
	volatile  unsigned int*ADCCON = (volatile unsigned int*)0x58000000;
	volatile unsigned int *ADCTSC = (volatile unsigned int*)0x58000004;
	
	if(*ADCUPDN&(1<<0)){/*按下中断*/
		/*开启数据转换功能、触摸屏的模式设置为自动(连续转换)
		*让触摸屏控制进入"等待中断"状态,等待触摸屏松开。
		*/
		puts_("stylus is down\n\r");
		enter_auto_convert_mode();
		start_adc_convert();
	}
	if(*ADCUPDN&(1<<1)){/*松开中断*/
		puts_("stylus is up\n\r");
		/*如果是触摸屏松开中断、则需要让控制器进入"等待中断"状态,可以再次使用触摸屏*/
		enter_wait_pendown_mode();
	}
	*ADCUPDN=0;

}

void irq_adc(void)
{
	/*读取转换后的数据*/
	volatile unsigned int* ADCDAT0 = (volatile unsigned int*)0x5800000C;
	volatile unsigned int* ADCDAT1 = (volatile unsigned int*)0x58000010;
	volatile unsigned int x=0,y=0;
	x = *ADCDAT0 &0x3ff;
	y = *ADCDAT1 &0x3ff;
	(*ADCDAT0);
	int i;
	for(i = 0;i<100;i++);
	if(!(*ADCDAT0&(1<<15))){
		putHex_(x);
		putHex_(y);
		puts_("\n\r");
		enter_wait_pendup_mode();
		ts_enable_timer_handler();
	}else{/*松开*/
		ts_disable_timer_handler();
		enter_wait_pendown_mode();
	}

}

/*总中断里面辨别是ADC中断还是触摸屏中断*/
void adcts_irq_handle(int irq)
{
	/*由于触摸屏按下中断和ADC转换结束完毕中断共用同一个中断号
	*所以执行中断的时候需要先分辨是什么中断然后执行相应的中断服务函数
	*/
	unsigned int *SUBSRCPND = (unsigned int *)0X4A000018;
	unsigned int subsrcirq = *SUBSRCPND;
	//puts_("this is touch screen interrupt\n\r");
	if(subsrcirq&(1<<INT_TC)){
		/*如果是触摸屏按下中断,进入此服务函数*/
		irq_touch();
	}else if(subsrcirq&(1<<INT_ADC_S)){
		/*如果是中断转换完成中断,进入此服务函数*/
		irq_adc();
		/*ADC读取转换数据成功后,在此进入等待中断状态(等待松触摸屏松开)*/
		enter_wait_pendup_mode();
	}
	
	*SUBSRCPND = subsrcirq;
}
void init_adcts_irq(void)
{
	/*开启ADC和触摸屏中断
	*禁止中断屏蔽。让中断控制器可以把中断发送
	*禁止子中断屏蔽,让产生的中断可以发送到中断控制器处
	*/
	register_irq(INT_ADC,adcts_irq_handle);
	enbale_subsrc_irq(INT_ADC_S);
	enbale_subsrc_irq(INT_TC);
}

init_touchscreen_register(void)
{
	/*设置ADC的时钟
	*(主要是有预分频系数和使能、选择转换通道(触摸屏使用不上)、模式的选择)
	*/

	volatile unsigned int *ADCCON = (volatile unsigned int *)0x58000000;
	
	*ADCCON = (1<<14) |(49<<6);
}

/*
*开启定时器中断服务函数中的触摸屏定时器功能
*/
static void ts_enable_timer_handler(void)
{
	global_ts_enable = 1;
}
/*
*关闭定时器中断服务函数中的触摸屏定时器功能
*/
static void ts_disable_timer_handler(void)
{
	global_ts_enable = 0;
}
/*获取定时器的使能位
*/
static int get_status_of_ts_timer(void)
{
	return global_ts_enable;
}

/*
*定时器TIMER0中断服务函数,每10ns执行一次
*/
void touch_timer_handler(void)
{
	volatile unsigned int* ADCDAT0 = (volatile unsigned int*)0x5800000C;

	/*判断触摸屏是否仍然处于按下状态,要是处于按下状态,进入自动转换模式,启动ADC再次转换*/
	if(!get_status_of_ts_timer()){/*刚上电还没有启动触摸屏时,不需要进行处理*/
		return ;
	}
	//putHex_(*ADCDAT0);
	//puts_("\n\r");
	if(!(*ADCDAT0&(1<<15))){/*触摸屏仍然按下,继续进入自动转换和开始ADC转换*/
		enter_auto_convert_mode();
		start_adc_convert();
	}else{/*要是已经松开,关闭定时器转换,则进入"等待中断模式",等待触摸屏再次按下.可以执行后续的处理*/
		ts_disable_timer_handler();
		enter_wait_pendown_mode();
	}
	*ADCDAT0 = 0;
}


void init_touch_screen(void)
{
	/*开启INT_ADC/INT_TC中断*/
	init_adcts_irq();
	
	/*初始化触摸屏控制器*/
	init_touchscreen_register();
	
	/*注册定时器中断,用来现实长按和滑动之类的*/
	register_timer_handler("touch", touch_timer_handler);
	
	/*进入中断等待模式*/
	 enter_wait_pendown_mode();
	
}

编程注意要点:

  • 由于ADC转换中断和触摸屏中断共用同一个中断号,所以需要区分中断的来源
  • 触摸屏中断有按下和松开两种,所以等待中断模式需要区分成up 和down两种(可以通过ADCUPDN寄存器来区分,每次读完后需要清0)
  • 要想支持长按或者是滑动的话,需要添加定时器。在定时器中断服务函数中,检测触摸屏的状态,要是仍然被按下,则继续ADC转换。否则进入等待中断模式。
  • 在ADC中断服务函数中,需要加个短暂的延迟。要不DATA0中的数据最高位读出来会存在问题。具体的不知道是芯片问题,还是自己的代码哪个逻辑没有处理好。希望知道的朋友可以指点下

 

 

 

 

 

 

发布了35 篇原创文章 · 获赞 1 · 访问量 1870

猜你喜欢

转载自blog.csdn.net/lzj_linux188/article/details/101372847