ARM2440 touch screen programming (bare metal programming)

As the saying goes: the wood of the embrace is born at the end; the nine-story platform starts from tired soil; the journey of a thousand miles begins with one step. Only when the foundation is solid can we do things more easily. The same is true for programming, and only by understanding the working principle of each device can we write good code efficiently. So when learning to drive and bare metal programming, I like to first pass the working principle of the device.

From the basic principle of the touch screen, we can know that when using the touch screen, if the SOC integrates a touch screen controller, our main task is to obtain the position of the touch screen when it is pressed, that is, the coordinate value in the xy direction. After conversion, the value of the acquired voltage is converted using ADC.

note: The resistive touch screen is essentially a resistive voltage divider, and the voltage division values ​​of different contacts in the x and y directions are different.

  • To measure the x-coordinate, xp is connected to the 3.3V point source XM to ground, and YP / YM is not connected to the power supply. You can know the x coordinate by measuring the point pressure of Yp.
  • To measure the y coordinate, yp connects to the 3.3V point source yM to ground, and xP / xM does not connect to the power supply. You can know the y coordinate by measuring the point pressure of xp.

①When the touch screen is not pressed, the equivalent circuit diagram:

In the wait interrupt mode: s5 and s4 are closed. When a contact is pressed, the touch screen is equivalent to ground. Y_ADC switches from high level to low level, this can be used as an interrupt trigger signal to notify the CPU that the touch screen has been pressed.

②When s1 and s3 are closed, the coordinates of x can be obtained by collecting the voltage of x_adc (the voltage dividing point on the y axis) (similar to the sliding rheostat). Similarly, when s2 and s4 are closed, the voltage dividing point on y_adc (the voltage dividing point on the x axis ) The y coordinate can be obtained by collecting the voltage.

There are 4 working modes for the touch screen in ARM2440:

  • Waiting for interrupt mode : Set the ADCTSC register to 0xD3. In this mode, the touch screen is waiting to be pressed. When the touch screen is pressed, the Y_ADC potential changes and an interrupt INT_TC is generated. For S3C2440, bit8 can be set to indicate whether to wait for pen down or pen up interrupt .
    • There are two waiting modes: waiting for pen down (when the touch screen has not been pressed) mode, and waiting for pen up (after the touch screen is pressed) mode
  • Separate x / y axis coordinate conversion mode : Set the corresponding control bits (that is, the closed state of s1-s5) to enter the x / y coordinate conversion mode. After the x coordinate value conversion is completed, it is written to ADCDAT0, and after the y coordinate conversion is completed, it is written to ADCDAT1, and then the corresponding INT_ADC interrupt is triggered.
  • Automatic (continuous) x / y axis coordinate conversion mode : set the corresponding register. Let the touch screen controller enter the automatic x / y axis coordinate conversion mode, and the touch screen controller will automatically convert the x and y coordinate values ​​of the contacts. And write to ADCDAT0 and ADCDAT1 respectively, then issue INT_ADC interrupt.
  • Normal conversion mode : When the touch screen is not used, it is used as a normal ADC conversion. The data is stored in ADCDAT0.

Therefore, special attention should be paid when programming: control these switches (S1 ~ S5) to set the corresponding working mode, and then obtain the coordinate value through ADC voltage conversion.

The touch screen controller and ADC conversion in ARM2440 are put together. XP, XM, YP, YM are mainly used on the touch screen. XP / YP are connected to the positive pole of the X / Y axis of the touch screen, and XM / YM are respectively connected to the negative pole of the X / Y axis of the touch screen. (Ie grounded). When the touch screen is not used, these pins can be used as ordinary ADC analog signal input pins.

It can be known from the above figure that the touch screen control unit can generate two interrupts: ① INT_TC touch screen interrupt ② INT_ADC, after the voltage conversion in the xy direction is successful, that is, the interrupt after the ADC conversion is completed. These two interrupts share the same interrupt number in the S3C2440 interrupter. Therefore, it is necessary to distinguish between the ADC interrupt and the touch screen interrupt in the interrupt service program.

ADC use process:

  • Set the ADCCON register, select the input signal channel, and set the conversion clock.
  • Set ADCTSC register, used to set the use of touch screen function and working mode.
  • Set ADCCON register to start A / D conversion. (There are read registers to start conversion and set bit1 active conversion).
  • After the conversion, you can read the values ​​in the ADCDAT0 and ADCDAT1 registers to obtain the x / y coordinates, respectively.

Process of using touch screen:

  • Press the touch screen to generate a touch interrupt
  • Turn on ADC conversion in the touch interrupt and convert the voltage of xy.
  • After the ADC conversion is completed, an ADC interrupt is generated
  • Reading ADCDAT data or xy voltage
  • In the timer interrupt, see if the touch screen is still pressed. (The timer is added to support the sliding function)
    • If it is pressed, the touch interrupt service function is executed again.
    • If you let go, it's over

Touch screen programming framework:

#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();
	
}

Important points for programming:

  • Since the ADC conversion interrupt and the touch screen interrupt share the same interrupt number, it is necessary to distinguish the source of the interrupt
  • There are two types of touch screen interrupts: press and release, so the wait interrupt mode needs to be divided into up and down (can be distinguished by the ADCUPDN register, it needs to be cleared after each read)
  • To support long press or swipe, you need to add a timer. In the timer interrupt service function, check the state of the touch screen, if it is still pressed, continue ADC conversion. Otherwise, enter the wait interrupt mode.
  • In the ADC interrupt service function, a short delay needs to be added. Otherwise, there will be problems in reading the highest bit of data in DATA0. I don't know whether it is a chip problem or which logic of my own code has not been dealt with. Friends who want to know can give pointers

 

 

 

 

 

 

Published 35 original articles · Like1 · Visits 1870

Guess you like

Origin blog.csdn.net/lzj_linux188/article/details/101372847