cancel
Showing results for 
Search instead for 
Did you mean: 

xpt2046 touch controller

kob77
Associate III

Hi, I'm having trouble with the resistive touch screen on my display. Here's some info about my project - I'm using an stm32f401cdu6 microcontroller, a 7" display with an SSD1963 controller, and a touch controller XPT2046. I based my project on the offpic user libraries on GitHub. You can find my microcontroller configuration and source files at https://github.com/koob7/stm32-f401-display. The display works fine, but the touch only works in power mode (XPT PD1=0, PD0=1). The first issue is the synhronic generation of interrupts on the irq pin even when the screen isn't touched. This happens even when interrupts are disabled (XPT PD0=1). In the future, I'd like to only read touch input when it's actually pressed. The second problem occurs in power-down mode (XPT PD0=0): when I change the X position on the touch, both X and Y values change. A similar issue happens when changing Y.

29 REPLIES 29

I'm currently using hardware SPI handling in my project. I'm not using DMA in my project. You can find the entire project on my GitHub linked in the post description. Here, I'm sending the library for handling XPT.

#include "xpt2046.h"




extern SPI_HandleTypeDef hspi2;

inline static float remap(float x, float in_min, float in_max, float out_min, float out_max)
{
	float new = ((x - in_min)  / (in_max - in_min))* (out_max - out_min) + out_min;
	return ((x - in_min)  / (in_max - in_min))* (out_max - out_min) + out_min;
}

void XPT2046_Init(void)
{
	uint8_t data;
	HAL_GPIO_WritePin(GPIOB, GPIO_PIN_13, GPIO_PIN_RESET);
	HAL_Delay(1);
	HAL_SPI_Transmit(&hspi2, (uint8_t*)XPT2046_ADDR_I, 1, 1000);
	HAL_SPI_TransmitReceive(&hspi2, (uint8_t*)XPT2046_ADDR_I, &data, sizeof(data), 1000);
	HAL_SPI_TransmitReceive(&hspi2, (uint8_t*)XPT2046_ADDR_I, &data, sizeof(data), 1000);
	HAL_GPIO_WritePin(GPIOB, GPIO_PIN_13, GPIO_PIN_SET);

}

uint16_t getRaw(uint8_t address)
{

	uint8_t data;
	uint16_t LSB, MSB;
	HAL_GPIO_WritePin(GPIOB, GPIO_PIN_13, GPIO_PIN_RESET);
	HAL_Delay(1);
	HAL_SPI_Transmit(&hspi2, &address, 1, 1000);
	address = 0x00;
	HAL_SPI_TransmitReceive(&hspi2, &address, &data, sizeof(data), 1000);
	MSB = data;
	address = 0x00;
	HAL_SPI_TransmitReceive(&hspi2, &address, &data, sizeof(data), 1000);
	LSB = data;
	HAL_GPIO_WritePin(GPIOB, GPIO_PIN_13, GPIO_PIN_SET);
	return ((MSB << 8) | (LSB)) >> 3;


}

inline static uint16_t X(void)
{
	uint16_t x;
	x = (uint16_t) remap(getRaw(XPT2046_ADDR_X), RAW_MIN_X, RAW_MAX_X, OUT_MIN_X, OUT_MAX_X);
	if (XPT2046_MIRROR_X) x = OUT_MAX_X - x;
	if (x > OUT_MIN_X && x < OUT_MAX_X) return x;
	else return 0;
}

inline static uint16_t Y(void)
{
	uint16_t y;
	y = (uint16_t) remap(getRaw(XPT2046_ADDR_Y), RAW_MIN_Y, RAW_MAX_Y, OUT_MIN_Y, OUT_MAX_Y);
	if (XPT2046_MIRROR_Y) y = OUT_MAX_Y - y;
	if (y > OUT_MIN_Y && y < OUT_MAX_Y) return y;
	else return 0;
}

uint16_t getX(void)
{
	if (XPT2046_ACCURACY)
	{
		uint16_t x[2] = { 1, 2 };
		while (x[0] != x[1])
		{
			if (XPT2046_REVERSED) { x[0] = Y(); x[1] = Y(); }
			else { x[0] = X(); x[1] = X(); }
		}
		return x[0];
	}
	else if (XPT2046_REVERSED) return Y(); else return X();
}

uint16_t getY(void)
{
	if (XPT2046_ACCURACY)
	{
		uint16_t y[2] = { 1, 2 };
		while (y[0] != y[1])
		{
			if (XPT2046_REVERSED) { y[0] = X(); y[1] = X(); }
			else { y[0] = Y(); y[1] = Y(); }
		}
		return y[0];
	}
	else if (XPT2046_REVERSED) return X(); else return Y();

}


 

#ifndef _XPT2046_H_
#define _XPT2046_H_

#include "main.h"



#define	XPT2046_ACCURACY 	1
#define	XPT2046_REVERSED 	0
#define	XPT2046_MIRROR_X 	0
#define	XPT2046_MIRROR_Y 	0

#define RAW_MIN_X	130
#define RAW_MAX_X	3936
#define OUT_MIN_X	0
#define OUT_MAX_X	800

#define RAW_MIN_Y	310
#define RAW_MAX_Y	3910
#define OUT_MIN_Y	0
#define OUT_MAX_Y	480

#define	XPT2046_SPI 			hspi2
#define	XPT2046_NSS_SOFT	1
#define	XPT2046_NSS_PORT 	GPIOB
#define	XPT2046_NSS_PIN 	GPIO_PIN_13

#define	XPT2046_ADDR_I 	0xD0
#define	XPT2046_ADDR_X 	0b11010001//0xD0
#define	XPT2046_ADDR_Y 	0b10010001//0x90

void XPT2046_Init(void);
uint16_t getRaw(uint8_t address);
uint16_t getX(void);
uint16_t getY(void);



#endif /* _XPT2046_H_ */

Ok, then polling/programmed SPI.

All I can suggest is that once the program freezes, pause it, and see where it is and what it's doing.  When does it hang?  First access to SPI or somewhere in the read/write cycle?

 

I am reading data from spi in programing metod in PD1,0 = 0,1. When the read is completed i change PD1,0 into 0,0. After that i wait for PENIRQ. When I notice falling edge on IRQ pin i disable interrupts and then again start read data from XPT.

OK, I use mode 1,1 to read data.  Not sure why the program freezes, but I find that 1,1 works better.  Going back to 0,0 enables PEN_IRQ and puts it in low power mode. 

If the program hangs, it would be good to know where.  If it just returns bad data, then I'd try the 1,1

 

I debugged the code as you instructed, and I discovered that the problem arises when the delay is called in the interrupt handler. At this point, the entire program freezes. The priority of the penirq interrupt is set to 14. Do you know what could be causing this?

handling interrupts:

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{

	if(GPIO_Pin == T_IRQ_Pin)
	{
		was_touched=1;
		__HAL_GPIO_EXTI_CLEAR_IT(EXTI15_10_IRQn);
					NVIC_DisableIRQ(EXTI15_10_IRQn);
					uint16_t touchx, touchy;
					char buffer1[10]=""; 
					char buffer2[10]=""; 
					TFT_Draw_Fill_Round_Rect (280, 180, 200, 60, 10,  0xCFFF);
					touchx = getX();
					sprintf(buffer1, "X%d", touchx); 
					touchy = getY();//pobrannie wartości Y
					sprintf(buffer2, "Y%d", touchy); 

					LCD_Font(300, 200,  buffer1, _Open_Sans_Bold_28, 1, BLACK);
					LCD_Font(300, 220,  buffer2, _Open_Sans_Bold_28, 1, BLACK);
					HAL_Delay(100);
					XPT2046_Init();//occurs error
					__HAL_GPIO_EXTI_CLEAR_IT(T_IRQ_Pin);
					NVIC_EnableIRQ(EXTI15_10_IRQn);

	}
}

 

I can't quite track it down, but you've disabled interrupts, then called HAL_delay, which requires the system tick.

System tick uses interrupts.  That may be the problem.

You need to rewrite the HAL_DELAY so that it works differently, if you're going to do that at all.

putting delays in an interrupt routine is  generally not a good idea. 

To find out if this is the problem, put a breakpoint at line 19, but before the delay.

Once that's been reached, go into the main routine and  put a breakpoint at
HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim).

Resume the program.  If you never get the interrupt at the timer callback (where it increments the system tick), then  you have the solution. 

Why do you need the HAL_DELAY anyway?

 

 

 

I used HAL_delay in SPI communication after setting CS to low. As a temporary measure, I replaced it with nop instructions. Another reason (not visible in this case) is triggering alerts for a specific duration. I'll try to read more about HAL_delay and debug the code tomorrow.

uint16_t getRaw(uint8_t address)
{

	uint8_t data;
	uint16_t LSB, MSB;
	HAL_GPIO_WritePin(GPIOB, GPIO_PIN_13, GPIO_PIN_RESET);
	//HAL_Delay(1);
			for (int i =0; i<1000;i+=1)
			{
				asm("nop");
			}
	HAL_SPI_Transmit(&hspi2, &address, 1, 1000);
	address = 0x00;
	HAL_SPI_TransmitReceive(&hspi2, &address, &data, sizeof(data), 1000);
	MSB = data;
	address = 0x00;
	HAL_SPI_TransmitReceive(&hspi2, &address, &data, sizeof(data), 1000);
	LSB = data;
	HAL_GPIO_WritePin(GPIOB, GPIO_PIN_13, GPIO_PIN_SET);
	return ((MSB << 8) | (LSB)) >> 3;


}

Not sure that I've got it right, but you *are* in an interrupt.  If all interrupts are disabled, then you absolutely cannot call HAL_DELAY since that uses the system tick which requires interrupts.  Interrupts are turned back on once the touch interrupt is done.  This is a function of the NVIC controller, and not necessarily the code you've written.

I'd take the HAL_DELAY out, permanently, and put it into the routine that determines that there had been a touch, after that touch is detected.  Then do your determination of what's going on. 

You might want to reconsider that mechanism, since depending on delay times can be a bit iffy.

 

I have a solution! The issue was the delay after resetting the CS pin in the function communicating with XPT. I also changed the display mode to 11. In this case, the mentioned delay is not necessary, and all the problems were fixed. Regarding the delay in interrupt handling, the solution was to increase the priority of the systick interrupt so that it is more important than the touch interrupt. In this case, the delay in communicating with XPT also works (but I no longer need it). Thank you very much for your help and dedicated time. I found another problem - I connected an SD card SPI reader (without a card inserted, just physical connection without uploading code) and its presence interferes with reading from XPT. It's very strange because I set the CS pin of the reader to a high level. Do you have any idea what the problem might be?

Yes, in fact, I think I do.

I assume that you are using the same lines for the XPT as the card, and have a separate CS for the card?

If so, this will not work with the XPT chip. 

Someplace, buried in the XPT spec, is that the XPT MISO line is held low when the chip is not selected.  This is contrary to the specification for SPI chips.  I have to put a 74LS125 gate in the MISO line from the XPT to the SPI interface, using the CS for the XPT to enable the gate.

You say "card reader".  IF there is any active circuitry on that reader, then something is likely holding down one (or more) of the data lines.  Especially if it happens to behave like the XPT.

On the little Chinese made displays I frequently use, the card is brought out separately, and I put a 74LS125 on that, too.  The "reader" on those displays is simply the socket with no active circuitry.

If you don't have an oscilloscope, you should probably get one.  100 Mhz would be ok (but not ideal) for a bandwidth.  Look to see what activity is on those lines and if one is being held down.

You should also check the wiring, just because.