Skip to main content
Associate
April 6, 2024
Solved

xpt2046 touch controller

  • April 6, 2024
  • 11 replies
  • 17176 views

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.

    Best answer by kob77

    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?

    11 replies

    Harvey White
    Senior III
    April 7, 2024

    Taking a very cursory look at my code for the XPT2046, I do not switch power modes.  I enable interrupts (from the XPT), but do not enable them in the processor.  I'm using FreeRTOS, so there's a task that looks (when needed) for the interrupt pin being low.  Things happen after that. 

    If the interrupts are being generated even if there is no touch, have you put a pullup on the IRQ pin?  A floating line would give you problems.  You might first try the processor's internal pullup. 

    IIRC, the chip only enables interrupts in certain modes, and will not in other power modes. 

    Since I don't change power modes, I can't comment on the power down mode.

    Hope this helps a bit.

    kob77Author
    Associate
    April 7, 2024

    Thank you for your response. The pull-up resistor is already built-in into my ssd1963 display, so unfortunately that's not the issue. You mentioned that interrupts are only available in certain power modes, which is why I tried changing them, but even that didn't block the interrupts :C. However, that's not the biggest problem. The bigger issue is the dependency between the X and Y axes. One of the videos shows the correct solution, while the other shows the current one.

    Harvey White
    Senior III
    April 7, 2024

    OK, have you looked at the IRQ pin from the chip with a scope to see what it's doing?  That may give you a few clues.

    If the interrupts are always on, then something is very odd.  I'd check to see if the IRQ pin in the code agrees with the actual physical pin as well, and if possible, look at both pins with a scope.

    I also wonder at the setup, since the power modes rewire how the pins are connected.  I do the following for a setup:

    // sets basic parameters
    void XPT_2046::init(uint16_t width, uint16_t height)
    {
    	uint8_t					ctrl;
    	uint8_t					data[4];
    
    
    	_width = width;
    	_height = height;
    	ctrl = 0;
    		hal_spi[interface]->receive(1, &ctrl, 2, (unsigned char*) &data, 0,XPT_BAUDRATE_PRESCALER,
    				CS.port, CS.pin, A0.port, A0.pin, RESET.port, RESET.pin);
    
    
    	ctrl = CTRL_HI_Y | CTRL_LO_SER;
    	hal_spi[interface]->receive(1, &ctrl, 2, (unsigned char*) &data, 0,XPT_BAUDRATE_PRESCALER,
    			CS.port, CS.pin, A0.port, A0.pin, RESET.port, RESET.pin);
    

    The SPI interface is a bit more complicated, but it sends out (one byte ctrl) of data with the A0 pin down, and 2 bytes of data with the A0 pin high.  No waiting between transmission/reception, CS down for all of it, reset (in this case) not used.  Baudrate is changed per call.  SPI interface is semaphore protected.  OK, so that explains the call. 

    Note that in the setup, 0 is written to the chip, then the combination of CTRL_HI_Y and CTRL_LO_SER works out to writing 0xD4 to the chip.

    Reading the chip is done by:

    // reads data until either max_samples is exceeded or data is stable from one sample to the next
    // reads binary xxxx xxxx xxxx 0000
    // reading must be stable from one to the next
    // note no current delay in readings
    
    uint16_t XPT_2046::_readLoop(uint8_t ctrl, uint8_t max_samples, uint8_t* samples_taken) const
    {
    	uint16_t prev = {0};
    	uint16_t cur = {0};
    
    	uint8_t i = 0;
    	uint8_t data[2] = {0};
    
    	// get first reading for comparison
    	hal_spi[interface]->receive(1, &ctrl, 2, (unsigned char*) &data, 0,XPT_BAUDRATE_PRESCALER,
    			CS.port, CS.pin, A0.port, A0.pin, RESET.port, RESET.pin);
    
    	prev = (data[0] << 8) | data[1];
    
    	// force at least one more read
    	cur = 0xffff;
    
    	while ((cur != prev) && (i < max_samples))
    	{
    		prev = cur;
    		hal_spi[interface]->receive(1, &ctrl, 2, (unsigned char*) &data, 0,XPT_BAUDRATE_PRESCALER,
    				CS.port, CS.pin, A0.port, A0.pin, RESET.port, RESET.pin);
    		cur = (data[0] << 8) | data[1];
    //		Delay_us(100);
    		vTaskDelay(2);
    	}
    	*samples_taken = i;
    	return cur;
    }
    

     

    where the call to readloop is:

    #define XPT_IDLE		0xD0
    #define XPT_X			0XD3
    #define XPT_Y			0X93
    #define XPT_Z1			0XB3
    #define XPT_Z2			0XC3
    
    // uses bubble sort
    #define SAMPLES			10
    
    void XPT_2046::getRaw(int* x, int* y, int* z, adc_ref_t mode,
    		uint8_t max_samples)
    {
    
    	uint16_t				SY[SAMPLES] = {0};
    	uint16_t				SX[SAMPLES] = {0};
    	uint16_t				SZ[SAMPLES] = {0};
    	uint16_t				tz1[SAMPLES] = {0};
    	uint16_t				tz2[SAMPLES] = {0};
    	uint16_t				data[SAMPLES] = {0};
    	int						nx = {0}, ny = {0}, nz = {0};
    
    	// Implementation based on TI Technical Note http://www.ti.com/lit/an/sbaa036/sbaa036.pdf
    
    	// preferred mode is differential
    	uint8_t	ctrl = {0};
    	uint8_t samples_taken = {0};
    //	uint8_t data = {0};
    
    	// control mode is Mode (0x3) + control HIX = 0x93 or HIY 0xD3
    
    	// NOTE: actually measures Y (when rotated) since panel is normally vertical to match display design
    	// X mode is start bit, A0 selected, 12 bits, DFR, power mode 3
    
    	// NOTE:
    	// Y mode is start bit, A2, A0 selected, 12 bits, DFR, power mode 3
    
    	// power mode 3 leaves xpt2046 constantly on
    
    
    	// NOTE: data is formatted as 12 bits, 4 lsb set to zero
    
    	for (int i = 0; i < SAMPLES; i++)
    	{
    		SX[i] = (_readLoop(XPT_X, max_samples, &samples_taken)) >> 4;
    		SY[i] = (_readLoop(XPT_Y, max_samples, &samples_taken)) >> 4;
    		tz1[i] = (_readLoop(XPT_Z1, max_samples, &samples_taken)) >> 4;
    		tz2[i] = (_readLoop(XPT_Z2, max_samples, &samples_taken)) >> 4;
    		SZ[i] = tz1[i] + 4095 - tz2[i];
    		vTaskDelay(2);
    	}
    	nx = bubble_sort (SX, SAMPLES);
    	ny = bubble_sort (SY, SAMPLES);
    	nz = bubble_sort (SZ, SAMPLES);
    
    	// Turn off ADC by issuing one more read (throwaway)
    	// This needs to be done, because PD=0b11 (needed for MODE_DFR) will disable PENIRQ
    	ctrl = 0;
    		hal_spi[interface]->receive(1, &ctrl, 2, (unsigned char*) &data, 0,XPT_BAUDRATE_PRESCALER,
    				CS.port, CS.pin, A0.port, A0.pin, RESET.port, RESET.pin);
    
    
    	ctrl = CTRL_HI_Y | CTRL_LO_SER;
    	hal_spi[interface]->receive(1, &ctrl, 2, (unsigned char*) &data, 0,XPT_BAUDRATE_PRESCALER,
    			CS.port, CS.pin, A0.port, A0.pin, RESET.port, RESET.pin);
    
    	*x = SX[nx];
    	*y = SY[ny];
    	*z = SZ[nz];
    }
    

     

    Not at all sure what your driver does, but I'd consider checking to see if the right parameters are passed.  Note that Idle sets the enable for the chip to generate interrupts, and the measurement modes turn that off.

    Can't comment too much more about the modes, but I wonder if the modes are wrong when you're reading. 

    I'd also suspect that the constant generation of interrupts is messing up something, but I don't know how interrupts are handled. 

    Since I poll for pen down (in a task), then go read data, the question of an IRQ is not relevant.  All I need to ensure is that the display is polled frequently enough.

    Hope this helps a bit.

     

     

     

    kob77Author
    Associate
    April 10, 2024

    Solution:
    The issue lies in generating interrupts during communication with xpt2046. The solution is to disable interrupts before starting the communication. After the communication is finished, we need to clear the pending interrupts and then re-enable them. 
    Currently, I am experiencing an issue with communication with xpt2046 from the interrupt handling function. During communication, the STM32 freezes.

    Harvey White
    Senior III
    April 10, 2024

    The code I use for reading the chip is:

     

    // **************************************************************************************************************************
    // ************************************************ TOUCH DETECT ************************************************************
    // **************************************************************************************************************************
    
    
    
    
    // used IRQ pin, normally high, goes low with touch
    
    
    bool XPT_2046::is_touch()
    {
    	if (HAL_GPIO_ReadPin(TOUCH_IRQ_GPIO_Port, TOUCH_IRQ_Pin) == GPIO_PIN_SET)
    	{
    		return false;
    	}
    	return true;
    }
    
    // **************************************************************************************************************************
    // ************************************************ GET DISPLAY TOUCHED NO WAIT *********************************************
    // **************************************************************************************************************************
    
    //
    // if touch, then return true and raw data
    // if no touch, then return false and P = { 0, 0}
    // returns true/false
    // DOES NOT WAIT
    //
    //
    //modified for IRQ pin
    // returns raw data
    
    
    bool XPT_2046::get_touch_NOWAIT(tpoint* P)
    {
    	int			x = {0};
    	int			y = {0};
    	int			z = {0};
    
    
    	P->x = -1;
    	P->y = -1;
    	if (is_touch())
    	{
    		// noted: default samples = 0xFF
    		getPosition(&x, &y, &z, MODE_DFR,20);
    		P->x = x & 0xFFF;
    		P->y = y & 0xFFF;
    		// return true or false depending on z threshold
    	}
    	else
    	{
    		return false;
    	}
    	return true;
    }
    
    

     

    Remember that this is run by a task, which means that the get_touch_NOWAIT routine is called periodically.

    You cannot easily DMA or IRQ driven reads for getting the data from the chip, there's no "data ready". 

    If you set up a DMA (there's not that much data, and speed is not of the essence), then you set up the DMA for 3 bytes (I think first is thrown away), then you have to poll to see that the DMA is over (or get the right callback).  If you were writing to an SPI display (not memory mapped), then the faster DMA is needed.

    Did you enable all the right interrupts if you did DMA?  If you tried IRQ, what was the trigger?

     

     

    kob77Author
    Associate
    April 10, 2024

    I'm not currently using dma. I read the data when the touch is activated and an IRQ interrupt is detected. Interrupts seem to be working properly. I set their priority to 14. For some reason unknown to me, the xpt2046 reading functions hang in the interrupt handling function.

    Interrupt handling code:

    void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
    {
    
    if(GPIO_Pin == T_IRQ_Pin)
    {
    was_touched=1;
    
    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();
    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();//turn on IRQ
    __HAL_GPIO_EXTI_CLEAR_IT(T_IRQ_Pin);//clearing interrupt flag
    NVIC_EnableIRQ(EXTI15_10_IRQn);
    
    }
    }

    XPT2046 lib:

    #include "xpt2046.h"
    
    #define XPT2046_ADDR_I 0xD0
    #define XPT2046_ADDR_X 0b11010001//0xD0
    #define XPT2046_ADDR_Y 0b10010001//0x90
    
    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)//turn on interrupts
    {
    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();
    
    }

    I can set global variable (was_touched) and read X, Y position in main loop. 

    while (1)
    {
    if(was_touched==1){
    was_touched=0;
    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();
    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();
    __HAL_GPIO_EXTI_CLEAR_IT(T_IRQ_Pin);
    NVIC_EnableIRQ(EXTI15_10_IRQn);
    
    if(touchx >=696 && touchx<=696+88 && touchy>=9 && touchy<=9+47)// 696, pos_y, 88, 47,
    {
    uint16_t counter = TFT_Draw_List(400, 200, 100, "TYPE:", "powitanie", save, _Open_Sans_Bold_14);
    //HAL_Delay(1000);//THIS DELAY
    TFT_Restore_Area(400, 200, 100, 47+1+34+35*counter, save);
    }
    }

    Unfortunately, there is also a problem here, I cannot insert a delay there because the interrupts start generating spontaneously. Without delay the screen works fine like in the video

    Harvey White
    Senior III
    April 10, 2024

    There can be two interrupts.  One generated by the chip when PEN_IRQ is active.  For that to work successfully, you either have to disable/reset the PEN_IRQ  once triggered, or you need to do all the data transfer within the interrupt.  Best that within the interrupt, you reset the XPT's IRQ.  The second method is  how you handle the data reads from the XPT.  Since there's no data ready interrupt on a register basis (that I know of), you cannot have an interrupt driven data transfer from the XPT.  Since data is ready once the PEN_IRQ is generated, you can just go read the data.

    That's PD1, 0 = 0,0 (PEN_IRQ = DISABLED), and you can read data, I think.  Then PD1, PD0 = 3, which enables everything but keeps PEN_IRQ disabled. 

    You don't want the PEN_IRQ low when you enable interrupts, since it'll immediately create an interrupt.  Make sure that the IRQ is edge sensitive, not level sensitive.

     

     

    kob77Author
    Associate
    April 11, 2024

    I have a feeling that we don't understand each other :C. Interrupts generated and received are handled correctly (according to my tests - they are triggered when the screen is touched). The problem is that when I am in the interrupt handling function, for some strange reasons, I can't read data from XPT. Trying to communicate with XPT, the program freezes.

    Harvey White
    Senior III
    April 11, 2024

    The the question becomes (to me) what method of SPI transfer are you using?  Programmed, IRQ driven, DMA?

    The programmed option is likely the only one to work.  I have an NRF24L01 that generates an interrupt, and in the callback for the IRQ, I read the chip (SPI) but it is a programmed read, not using DMA or IRQ.

    I do as little as possible in that callback.

    kob77Author
    Associate
    April 11, 2024

    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_ */
    Harvey White
    Senior III
    April 11, 2024

    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?

     

    Harvey White
    Senior III
    April 11, 2024

    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

     

    kob77Author
    Associate
    April 11, 2024

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

     

    Harvey White
    Senior III
    April 11, 2024

    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?

     

     

     

    Harvey White
    Senior III
    April 12, 2024

    OK, the person designing the card reader was thinking (I hope). The 74HC125 isolates the card and provides more drive than the card itself will. So the problem is very likely not the card reader.  (Until we make sure)

    I assume you have a voltmeter?  (and good idea on the scope, if you start dealing with hardware, you'll likely need one). 

    With the processor paused, and not in the routine to read the chip, check that the CS on the XPT is high.  With that high, the MISO line will likely be low (not floating).  The MOSI line should be floating or high.  The CLK line should be floating or high.

    With the MISO line low, the HC125 should not be active.  Make sure that the CS line on the card reader is high.  A good test would be to disconnect the MISO, MOSI, and CLK lines from the card reader, then with the CS line high, check that the lines are floating.  They should be. 

    Without an oscilloscope, you have some difficulty in determining what is going on with the circuit. 

    The problem with the XPT is that, in an SPI chip, the outputs are supposed to be floating when the chip is not selected.  This allows paralleling two SPI chips and controlling each with the separate CS line.  The XPT hogs the data out line (MISO) and prevents data from getting through from other chips. 

    Now if the card reader is properly designed, the HC125 isolates the card, and therefore does not drive the MISO line when the card reader CS is high.

    IF.......

    That's what the check on the card reader by itself will do, see if the card reader is well designed. 

    On the boards that I use, I designed in the HC125 chips for each line paralleled from the card and display that is an output, the MISO line from each. I don't have the problem.  The reason that I don't have the problem is that I DID have the problem.

    A logic probe might work well if it handles 3.3 volt levels properly.  It can show levels and activity. 

     

     

     

    kob77Author
    Associate
    April 12, 2024

    I took measurements and the results are as follows: MISO and MOSI are high, SCK is low (SD card reading program), MISO is high, MOSI is low, SCK is low (display handling program). In both cases, both CS pins were high. What you're saying seems to make sense. I think I can confidently consider the problem solved by adding the HC125.

    Now, I would like to design a board with my supervisor using a Cortex-M7. On the board, most of the pins will be GPIO + SPI pins + programmer pins. Do you think this is feasible with relatively little effort based on existing schematics? Or do I need to have a full understanding of the processor documentation? Or perhaps you'd like to help me with this for some fee?

    Harvey White
    Senior III
    April 12, 2024

    I thank you for the vote of confidence. 

    I do have pointers for processor designs, but I will warn you that it may be more useful to start off with a smaller and potentially cheaper processor for learning purposes. 

    While you can do a 2 layer board for microprocessors, once you get into the high clock speed  (say over 50 Mhz), I'd recommend a 4 layer board:  top layer, ground plane (no traces), possible signal plane or power plane, and then bottom layer.  Someone needs to know how to use a PCB design program and have some experience.  It made a difference in the design to go to 4 layers.

    You also need to play a few games when dealing with high speed clocks (SPI signals) and perhaps programming signals.

    You'll also need an oscilloscope, preferably a 4 channel, 100 Mhz plus model, or access to one.

    The existing schematics are good starters, but you need to understand why they did what they did, and at times, why they shouldn't have done that.

    This can be more complicated than you think, so I'd start out really simple.  I'm not sure how happy you are with some of the design processes, nor how much experience you (or your supervisor) happen to have.  Is this in an academic or a work situation?

    There will be a fair amount of learning involved, so you may be moving a bit fast.  You might want to check out projects with the Nucleo board series. 

    So what are your goals?  What do you want to learn?

     

    kob77Author
    Associate
    April 12, 2024

    So, starting from the beginning. Yes, I'm a student of Computer Science at POLS. In my second year, I decided that for my engineering project, I'll create a 6DOF robot (I have support from the project supervisor). The project involves:

    -creating my own design (done - maybe not perfect, but sufficient for me)

    -formulating inverse kinematics equations for robot control (also done)

    -creating a controller responsible for:
    *controlling the robot's movement (done on a Nucleo board)
    *creating a GUI for robot control (I connected a 7" SSD1963 screen and touch panel to a Blackpill, but it requires GUI programming, which seems like the easiest part to me)
    * controlling the robot using G-code read from an SD card.

    All of this results in a mass of cables that are unsightly and create a huge mess. Not to mention that I have to transfer all of this to one microcontroller (a project requirement), which will introduce even more confusion.

    To reduce noises my idea is to divide the board into two parts :
    - a part for stepper motor drivers (requiring a lot of current)
    - a part for the microcontroller

    Unfortunately, I don't have experience in PCB design :C. Fortunately, the board will be relatively simple (reset circuit, quartz oscillator, SDRAM memory (if I manage), programming interface, basic short circuit and overvoltage protection).

    Of course, starting with simpler circuits is more sensible, but unfortunately, the time I can dedicate to the project is severely limited. That's why I decided on a project with a large microcontroller. I plan to solder the components gradually, measuring all voltages and potential soldering errors. In case of major problems, I'll resort to a ready-made Nucleo solution.

    I'm attaching a short video showing what I've managed to do so far :D

    Harvey White
    Senior III
    April 12, 2024

    I've tried and worked a bit with some things like what you're doing.

    It's actually a bit easier to have multiple processors, but that's not the requirement.

    Considering all that you have to do, I'd buy as much of the hardware (ex: CPU board) as reasonable.

    I'd use a nucleo-144 board with a processor that has lots of memory. 

    I'd design a PC board into which the N-144 board plugs, which takes connections and brings them out (through circuitry if needed) to plugs. This ought to considerably neaten up the wiring.  You might consider a small OLED display to do status, not the main display.  This PC board can be a 2 layer board, and should be reasonably inexpensive.  If you don't design a board, then perf board on a base board (standoffs!) and make it modular. IDC connectors between boards. 

    The two sections are good, with all the drivers on one board, and the sensors on the CPU backboard. 

    There's a problem with the Nucleo boards, and that is "no idea how they routed the signals".   High speed FMC signals don't work well for dynamic memory, static memory, where you can control the timing a lot better.  You can do SPI interfaces reasonably because they can be slowed down.  There are some SPI memories that can function as RAM and might be easier to use.  For ROM, I'd suggest an SD card. I2C can also be used for ROM (24LCxxxx memory). 

    Check I2C specs and SPI specs to see how far they can be run. 

    Look into power grounding techniques with high current vs low current.

    What you might want to do for overall development, is get a board set up where you can plug in an N-144 board, then wire to the appropriate connectors.  Do you have wire-wrap available?

    I really suggest not doing your own CPU board, using the N-144 board you will have something that works out of the box, you just have to be moderately careful with how you connect things.  Note that the evaluation boards, while more complete, have a limited amount of I/O, please check.

    I will not design stuff for you, but I'll be able to help to a limited extent.

     

    kob77Author
    Associate
    April 12, 2024

    I'm well aware that using the Nucleo board significantly simplifies things. Actually, I've already designed such a "sandwich" for this project, but many things have changed and require redesigning. I'll reconsider the memory issue; it's possible that the 2MB on the Nucleo F144 will suffice. On one hand, I realize the immense challenge I'm facing, but on the other hand, I felt similarly when starting the entire project. I think I'll at least give it a try. If the expected results aren't achieved quickly, I'll stick with the Nucleo F144.

    I'm not sure what you mean by "wire-wrap." I soldered my project on a prototyping board, replacing the traces with wires.