---
# System prepended metadata

title: '[STM32]ILI9341'
tags: [stm32]

---


## TFT ILI9341實作  
## Demo  
{%youtube _0Ot_U6uRhs %}

## STM32CubeIDE設定  
### PIN 設定  
![PinoutClear](https://imgur.com/VJNPOvV.png)

---  
### GPIO設定  
![image](https://hackmd.io/_uploads/S1v2Erwyll.png)


---
### RCC設定  
![RccSet](https://imgur.com/7kPTJ9A.png)

---
### SYS設定  
![SysSet](https://imgur.com/rNjjVDv.png)

---
### TIM設定  
![image](https://hackmd.io/_uploads/SJGgSSvylg.png)

---
### SPI設定  
選
![image](https://hackmd.io/_uploads/BJKrBBvkxl.png)
![image](https://hackmd.io/_uploads/rJNQrSP1gx.png)
![image](https://hackmd.io/_uploads/S1_NrrvJgl.png)

---
### NVIC 設定  
![image](https://hackmd.io/_uploads/HJLtSrvJgl.png)

---
### CRC設定
![image](https://hackmd.io/_uploads/SkSP0Bw1gx.png)

___
### touchGFX設定
![image](https://hackmd.io/_uploads/rkcM0SP1lg.png)
![image](https://hackmd.io/_uploads/Bkc40SPkeg.png)

___
### Clock設定  
![ClockSet](https://imgur.com/SlT0y70.png)

---
### 外設檔案分開+生成  
![GeneratorSet](https://imgur.com/Lo1x0Fc.png)
#### 生成程式碼
![CodeGenerator](https://imgur.com/LDLzPZb.png)

---
### Nucleo_Pinou  
![nucleo_pinout](https://imgur.com/pW4VU4f.png)

---
### 電路圖  
![ILI9341接腳](https://hackmd.io/_uploads/r1qqjrDyxx.png)


---

## ILI9341實作  

[下載touchGFX](https://www.st.com/en/development-tools/touchgfxdesigner.html)

---

### 程式碼  

#### main.c  
:::spoiler   
```c=
/* USER CODE BEGIN Includes */
#include "z_touch_XPT2046.h"
#include "z_displ_ILI9XXX.h"
#include "stdio.h"
#include "string.h"
/* USER CODE END Includes */
/* USER CODE BEGIN 2 */
HAL_Delay(200);
Displ_Init(Displ_Orientat_0);
HAL_Delay(200);
touchgfxSignalVSync();
/* USER CODE END 2 */
/* USER CODE BEGIN WHILE */
while (1)
{
  if (Touch_GotATouch(2)){
      touchgfxSignalVSync();
  }
  if ("other events needing a display update")
        touchgfxSignalVSync();
/* USER CODE END WHILE */

MX_TouchGFX_Process();
/* USER CODE BEGIN 3 */
}
```
:::  

#### z_displ_ILI9XXX.h 
:::spoiler   
```c=
/*
 * 	z_displ_ILI9488.h
 *	rel. TouchGFX.1.30
 *
 *  Created on: 05 giu 2023
 *      Author: mauro
 *
 *  licensing: https://github.com/maudeve-it/ILI9XXX-XPT2046-STM32/blob/c097f0e7d569845c1cf98e8d930f2224e427fd54/LICENSE
 *
 *	Installing and using this library follow instruction on: https://github.com/maudeve-it/ILI9XXX-XPT2046-STM32
 *
 *  These are the init instruction to put in you main() USER CODE BEGIN 2
 *
 *  (if Direct Handling)
 *  Displ_Init(Displ_Orientat_0);			// initialize display controller - set orientation parameter as per your needs
 *  Displ_CLS(BLACK);						// clear the screen - BLACK or any other color you prefer
 *  Displ_BackLight('I');  					// initialize backlight
 *
 *  (if using TouchGFX - button mode)
 *  Displ_Init(Displ_Orientat_0);			// initialize display controller - set orientation parameter as per TouchGFX setup
 * 	touchgfxSignalVSync();					// ask display syncronization
 *  Displ_BackLight('I');  					// initialize backlight
 *
 *  (if using TouchGFX - full mode)
 *  Displ_Init(Displ_Orientat_0);			// initialize display controller - set orientation parameter as per TouchGFX setup
 *  Displ_BackLight('I');  					// initialize backlight
 *  HAL_TIM_Base_Start_IT(&TGFX_T);			// start TouchGFX tick timer
 *
 *  see also z_touch_XPT2046.h
 *
 */


#ifndef __Z_DISPL_ILI9XXX_H
#define __Z_DISPL_ILI9XXX_H


/*||||||||||| USER/PROJECT PARAMETERS |||||||||||*/

/******************    STEP 0    ******************
 *** if mapping flash on the uC addresses space ***
 ********** uncomment the below #define ***********
 ******** end assign it the correct value *********
 ***** If external flash handled by TOUCHGFX,******
 ************* let #define commented **************
 **************************************************/

/*****************     STEP 1      *****************
 ************ Enable TouchGFX interface ************
 * uncommenting the below #define to enable
 * functions interfacing TouchGFX
 ***************************************************/
#define DISPLAY_USING_TOUCHGFX


/******************    STEP 2    *****************
 * which display are you using?
 *************************************************/
#define ILI9341
//#define ILI9488_V1
//#define ILI9488_V2
#include "fonts.h"

/******************    STEP 3    ******************
 **************** PORT PARAMETERS *****************
 ** properly set the below th 2 defines to address
 ********  the SPI port defined on CubeMX *********
 **************************************************/
#define DISPL_SPI_PORT 	hspi2
#define DISPL_SPI 		SPI2


/******************    STEP 4     ******************
 ***************** SPI PORT SPEED  *****************
 * define HERE the prescaler value to assign SPI port 
 * when transferring data to/from DISPLAY or TOUCH
 * Keep in mind that Touch SPI Baudrate should be no more than 1 Mbps
 ***************************************************/
#define DISPL_PRESCALER SPI_BAUDRATEPRESCALER_4     //prescaler assigned to display SPI port
#define TOUCH_PRESCALER SPI_BAUDRATEPRESCALER_256	//prescaler assigned to touch device SPI port


/*****************     STEP 5      *****************
 ************* SPI COMMUNICATION MODE **************
 *** enable SPI mode want, uncommenting ONE row ****
 **** (Setup the same configuration on CubeMX) *****
 ***************************************************/
//#define DISPLAY_SPI_POLLING_MODE
//#define DISPLAY_SPI_INTERRUPT_MODE
#define DISPLAY_SPI_DMA_MODE // (mixed: polling/DMA, see below)


/*****************     STEP 6      *****************
 ***************** Backlight timer *****************
 * if you want dimming backlight UNCOMMENT the
 * DISPLAY_DIMMING_MODE below define and properly
 * set other defines.
 * Using backlight as a switch (only on/off) leave
 * DISPLAY_DIMMING_MODE commented
 * if DIMMING:
 * On CubeMX set DISPL_LED pin as a timer PWM pin.
 * Timer COUNTER PERIOD (ARR) defines dimming light steps:
 * keep it low value - e.g. 10 - if dimming with buttons,
 * use higher value - e.g. 100 - if dimming with encoder, ...
 * Avoiding display flickering timer PRESCALER should
 * let timer clock to be higher than COUNTER PERIOD * 100 Hz.
 * Set all other defines below 
 ***************************************************/
#define DISPLAY_DIMMING_MODE						// uncomment this define to enable dimming function otherwise there is an on/off switching function
#define BKLIT_TIMER 				TIM3			//timer used (PWMming DISPL_LED pin)
#define BKLIT_T 					htim3			//timer used
#define BKLIT_CHANNEL				TIM_CHANNEL_2	//channel used
#define BKLIT_CCR					CCR2			//Capture-compare register used (same number as channel)
#define BKLIT_STBY_LEVEL 			1				//Display backlight level when in stand-by (levels are CNT values)
#define BKLIT_INIT_LEVEL 			100				//Display backlight level on startup


/*****************     STEP 7      *****************
 ***************** TouchGFX Time base timer *****************
 * If using library in TouchGFX-full-mode
 * (see GitHub page indicated on top for details)
 * you have to set #define DELAY_TO_KEY_REPEAT -1
 * in "z_touch_XPT2046.h" and setup a timer as a 
 * time base for TouchGFX. 
 * It has to be set to generate a
 * HAL_TIM_PeriodElapsedCallback 60 times per second
 * That timer has to be assigned to the below macros.
 * if not in TouchGFX-full-mode: assign macros to 
 * an unused timer
 ***************************************************/
#define TGFX_TIMER			TIM3
#define TGFX_T				htim3


/*****************     STEP 8      *****************
 ************* frame buffer DEFINITION *************
 * IF NO TOUCHGFX: 
 * BUFLEVEL defines size of each one of the 2 SPI 
 * buffers: buffer size is 2^BUFLEVEL so 2 means 
 * 4 bytes buffer and 10 means 1 kbyte (each).
 * It must be not below 10!
 * If TOUCHGFX:
 * buffers are not used if display handles RGB565. 
 * If display uses RGB666 one buffer will be used for 
 * color format translation. In this case set
 * BUFLEVEL following this table:
 * TouchGFX buffers>10KB need BUFLEVEL 15
 * TouchGFX buffers>5KB  need BUFLEVEL 14
 * TouchGFX buffers>2700bytes need BUFLEVEL 13
 * TouchGFX buffers>1300bytes need BUFLEVEL 12
***************************************************/
#define BUFLEVEL 11

/*|||||||| END OF USER/PROJECT PARAMETERS ||||||||*/


/*|||||||||||||| DEVICE PARAMETERS |||||||||||||||||*/
/* you shouldn't need to change anything here after */

#ifdef ILI9488_V1
#define ILI9488
#endif
#ifdef ILI9488_V2
#define ILI9488
#endif


/***************   color depth      ****************
 *** choose one of the two color depth available *** 
 ***** to use on the display RGB565 and RGB666 *****
 ***************************************************/
#ifdef ILI9341
#define Z_RGB565
#endif
#ifdef ILI9488_V1
#define Z_RGB666
#endif
#ifdef ILI9488_V2
#define Z_RGB565
#endif


/***************   display size      ***************
 ***************************************************/
#ifdef ILI9341
#define DISPL_WIDTH  240		// 0 orientation
#define DISPL_HEIGHT 320		// 0 orientation
#endif
#ifdef ILI9488
#define DISPL_WIDTH  320		// 0 orientation
#define DISPL_HEIGHT 480		// 0 orientation
#endif


/************* from POLLING to DMA *****************
 *** below DISPL_DMA_CUTOFF data size, transfer ****
 ****** will be polling, even if DMA enabled *******
 ***************************************************/
#define DISPL_DMA_CUTOFF 	20    // (bytes) used only in DMA_MODE

/*||||||||||| END OF DEVICE PARAMETERS ||||||||||||*/


#include <string.h>

typedef enum {
	Displ_Orientat_0,
	Displ_Orientat_90,
	Displ_Orientat_180,
	Displ_Orientat_270
} Displ_Orientat_e;


#define SPI_COMMAND 	GPIO_PIN_RESET  	//DISPL_DC_Pin level sending commands
#define SPI_DATA 		GPIO_PIN_SET		//DISPL_DC_Pin level sending data

// set the buffers size as per BUFLEVEL and DISPLAY_USING_TOUCHGFX
// (if using TouchGFX, don't buffers from this library)
#define SIZEBUF (1<<BUFLEVEL)


/*******************************
 * Color names
 *******************************/
#define	RED			0xF800
#define	GREEN		0x07E0
#define	BLUE		0x001F
#define YELLOW		0xFFE0
#define MAGENTA		0xF81F
#define ORANGE		0xFD00
#define CYAN		0x07FF
#define	D_RED 		0xC000
#define	D_GREEN		0x0600
#define	D_BLUE		0x0018
#define D_YELLOW	0xC600
#define D_MAGENTA	0xC018
#define D_ORANGE	0xC300
#define D_CYAN		0x0618
#define	DD_RED		0x8000
#define	DD_GREEN	0x0400
#define DD_BLUE		0x0010
#define DD_YELLOW	0x8400
#define DD_MAGENTA	0x8020
#define DD_ORANGE	0x8200
#define DD_CYAN		0x0410
#define WHITE		0xFFFF
#define D_WHITE		0xC618
#define DD_WHITE	0x8410
#define DDD_WHITE	0x4208
#define DDDD_WHITE	0x2104
#define	BLACK		0x0000
#define color565(r, g, b) ((((r) & 0xF8) << 8) | (((g) & 0xFC) << 3) | (((b) & 0xF8) >> 3))


/**********************************
/ ILI9XXX LCD family commands
 **********************************/
#define ILI9XXX_SLEEP_OUT			0x11	//wake up display
#define ILI9XXX_DISPLAY_ON			0x29	// enable display
#define ILI9XXX_PIXEL_FORMAT    	0x3A	// RGB565/RGB666/...
#define ILI9XXX_RGB_INTERFACE   	0xB0	// type of communication (full duplex, half, etc.)
#define ILI9XXX_MEMWR				0x2C	// writes into memory
#define ILI9XXX_COLUMN_ADDR			0x2A	// set area display to write into
#define ILI9XXX_PAGE_ADDR			0x2B	// set area display to write into
#define ILI9XXX_MADCTL				0x36	// order followed writing into memory (-> screen orientation)
#define ILI9XXX_MADCTL_0DEG 		0X00	// parameter of MADCTL command
#define ILI9XXX_MADCTL_90DEG 		0x20	// parameter of MADCTL command
#define ILI9XXX_MADCTL_180DEG 		0x40	// parameter of MADCTL command
#define ILI9XXX_MADCTL_270DEG 		0x60	// parameter of MADCTL command

#define ILI9XXX_INIT_SHORT_DELAY	5		// Hal_Delay parameter
#define ILI9XXX_INIT_LONG_DELAY		150		// Hal_Delay parameter

#define ILI9XXX_POWER0				0xC0
#define ILI9XXX_POWER1				0xC1
#define ILI9488_POWER2				0xC2
#define ILI9341_POWERA				0xCB
#define ILI9341_POWERB				0xCF

/**********************************************************
 * macro setting SPI baudrate prescaler
 **********************************************************/
#define SET_DISPL_SPI_BAUDRATE			DISPL_SPI->CR1 &= (uint16_t) ~SPI_CR1_BR_Msk; \
										DISPL_SPI->CR1 |= DISPL_PRESCALER

#define SET_TOUCH_SPI_BAUDRATE			TOUCH_SPI->CR1 &= (uint16_t) ~SPI_CR1_BR_Msk; \
										TOUCH_SPI->CR1 |= TOUCH_PRESCALER
/**********************************************************/


#define _swap_int16_t(a, b)  { int16_t t = a; a = b; b = t; }


#ifndef DISPLAY_USING_TOUCHGFX
void Displ_drawCircle(int16_t x0, int16_t y0, int16_t r, uint16_t color);
void Displ_fillCircle(int16_t x0, int16_t y0, int16_t r, uint16_t color);

void Displ_fillRoundRect(int16_t x, int16_t y, int16_t w, int16_t h, int16_t r, uint16_t color);
void Displ_drawRoundRect(int16_t x, int16_t y, int16_t w, int16_t h, int16_t r, uint16_t color);

void Displ_Border(int16_t x, int16_t y, int16_t w, int16_t h, int16_t t,  uint16_t color);
void Displ_CLS(uint16_t bgcolor);
void Displ_CString(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, const char* str, sFONT font, uint8_t size, uint16_t color, uint16_t bgcolor);
void Displ_fillTriangle(int16_t x0, int16_t y0, int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint16_t color);
void Displ_drawTriangle(int16_t x0, int16_t y0, int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint16_t color);
void Displ_Init(Displ_Orientat_e orientation);
void Displ_Line(int16_t x0, int16_t y0, int16_t x1, int16_t y1, uint16_t color);
void Displ_Orientation(Displ_Orientat_e orientation);
void Displ_Pixel(uint16_t x, uint16_t y, uint16_t color);
void Displ_WChar(uint16_t x, uint16_t y, char ch, sFONT font, uint8_t size, uint16_t color, uint16_t bgcolor);
void Displ_WString(uint16_t x, uint16_t y, const char* str, sFONT font, uint8_t size, uint16_t color, uint16_t bgcolor);
void Displ_DrawImage(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint8_t *data);
#endif /* ! DISPLAY_USING_TOUCHGFX */
void Displ_FillArea(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t color);
void Displ_Orientation(Displ_Orientat_e orientation);
void Displ_Init(Displ_Orientat_e orientation);

void HAL_SPI_ErrorCallback(SPI_HandleTypeDef *hspi);
void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi);

uint32_t  Displ_BackLight(uint8_t cmd);


#ifdef DISPLAY_USING_TOUCHGFX
int touchgfxDisplayDriverTransmitActive();
void touchgfxDisplayDriverTransmitBlock(const uint8_t* pixels, uint16_t x, uint16_t y, uint16_t w, uint16_t h);
extern void DisplayDriver_TransferCompleteCallback();
extern void touchgfxSignalVSync(void);
#endif /* DISPLAY_USING_TOUCHGFX */

#endif /* __Z_DISPL_ILI9XXX_H */

```
:::  
#### z_displ_ILI9XXX.c 
:::spoiler   
```c=
/*
 * 	z_displ_ILI94XX.c
 * 	rel. TouchGFX.1.30
 *
 *  Created on: 5 giu 2023
 *      Author: mauro
 *
 *  licensing: https://github.com/maudeve-it/ILI9XXX-XPT2046-STM32/blob/c097f0e7d569845c1cf98e8d930f2224e427fd54/LICENSE
 *
 *	To install and use this library follow instruction on: https://github.com/maudeve-it/ILI9XXX-XPT2046-STM32
 *
 */


#include "main.h"

extern SPI_HandleTypeDef DISPL_SPI_PORT;

#ifdef DISPLAY_DIMMING_MODE
extern TIM_HandleTypeDef BKLIT_T;
#endif
extern TIM_HandleTypeDef TGFX_T;

extern volatile uint8_t Touch_PenDown;				// set to 1 by pendown interrupt callback, reset to 0 by sw
Displ_Orientat_e current_orientation;				// it records the active display orientation. Set by Displ_Orientation
volatile uint8_t Displ_SpiAvailable=1;  			// 0 if SPI is busy or 1 if it is free (transm cplt)
int16_t _width;       								///< (oriented) display width
int16_t _height;      								///< (oriented) display height

// if using TouchGFX buffer are not used (size set to 2 bytes for software convenience)
// unless using ILI9488V1.0 (RGB666) which needs dispBuffer1 for color format conversion
// if not using TouchGFX double buffering is needed
#ifdef DISPLAY_USING_TOUCHGFX
static uint8_t dispBuffer2[2];
#ifdef Z_RGB666
static uint8_t dispBuffer1[SIZEBUF];
#else
static uint8_t dispBuffer1[2];
#endif // Z_RGB666
#else
static uint8_t dispBuffer1[SIZEBUF];
static uint8_t dispBuffer2[SIZEBUF];
#endif //DISPLAY_USING_TOUCHGFX
static uint8_t *dispBuffer=dispBuffer1;




/******************************************
 * @brief	enable display, disabling touch
 * 			device selected if CS low
 ******************************************/
void Displ_Select(void) {
	if (TOUCH_SPI==DISPL_SPI){														// if SPI port shared (display <-> touch)
		if (HAL_GPIO_ReadPin(DISPL_CS_GPIO_Port, DISPL_CS_Pin)) {					// if display not yet selected
			HAL_GPIO_WritePin(TOUCH_CS_GPIO_Port, TOUCH_CS_Pin, GPIO_PIN_SET); 		// unselect touch
			SET_DISPL_SPI_BAUDRATE;				   									//change SPI port speed as per display needs
			HAL_GPIO_WritePin(DISPL_CS_GPIO_Port, DISPL_CS_Pin, GPIO_PIN_RESET);	// select display
		}
	}
}




/**************************
 * @BRIEF	engages SPI port communicating with displayDC_Status
 * 			depending on the macro definition makes transmission in Polling/Interrupt/DMA mode
 * @PARAM	DC_Status 	indicates if sending command or data
 * 			data		buffer data to send
 * 			dataSize	number of bytes in "data" to be sent
 * 			isTouchGFXBuffer 1 only when called by touchgfxDisplayDriverTransmitBlock (for byte endian conversion). All other cases 0
 **************************/
void Displ_Transmit(GPIO_PinState DC_Status, uint8_t* data, uint16_t dataSize, uint8_t isTouchGFXBuffer ){

	while (!Displ_SpiAvailable) {};  // waiting for a free SPI port. Flag is set to 1 by transmission-complete interrupt callback

	Displ_Select();
	HAL_GPIO_WritePin(DISPL_DC_GPIO_Port, DISPL_DC_Pin, DC_Status);

	if (isTouchGFXBuffer){
#ifdef Z_RGB565
//if color format is RGB565 just swap even and odd bytes correcting endianess for ILI driver
		uint32_t *limit=(uint32_t*)(data+dataSize);
		for (uint32_t *data32=(uint32_t*)data; data32<limit; data32++) {
			*data32=__REV16(*data32);
		}
#else
//if display color format is RGB666: convert RGB565 received by TouchGFX and swap bytes

		uint8_t *buf8Pos=dispBuffer1; 							//using a local pointer

		uint16_t *limit=(uint16_t*)(data+dataSize);
		for (uint16_t *data16=(uint16_t*)data; (data16<limit) & ((buf8Pos-dispBuffer1)<(SIZEBUF-3)); data16++) {

			*(buf8Pos++)=((*data16 & 0xF800)>>8);  // R color
			*(buf8Pos++)=((*data16 & 0x07E0)>>3);  // G color
			*(buf8Pos++)=((*data16 & 0x001F)<<3);  // B color
		}

		data=dispBuffer1; 				//data (pointer to data to transfer via SPI) has to point to converted buffer
		dataSize=(buf8Pos-dispBuffer1);	//and dataSize has to contain the converted buffer size

#endif //Z_RGB565
	}


#ifdef DISPLAY_SPI_INTERRUPT_MODE
		Displ_SpiAvailable=0;
		HAL_SPI_Transmit_IT(&DISPL_SPI_PORT , data, dataSize);
#else
#ifdef DISPLAY_SPI_DMA_MODE
		if (dataSize<DISPL_DMA_CUTOFF) {
#endif //DISPLAY_SPI_DMA_MODE
			Displ_SpiAvailable=0;
			HAL_SPI_Transmit(&DISPL_SPI_PORT , data, dataSize, HAL_MAX_DELAY);
			Displ_SpiAvailable=1;
#ifdef DISPLAY_USING_TOUCHGFX
			if (isTouchGFXBuffer){
				DisplayDriver_TransferCompleteCallback();
			}
#endif //DISPLAY_USING_TOUCHGFX
#ifdef DISPLAY_SPI_DMA_MODE
		} else {
			Displ_SpiAvailable=0;
			HAL_SPI_Transmit_DMA(&DISPL_SPI_PORT , data, dataSize);
		}
#endif //DISPLAY_SPI_DMA_MODE
#endif //DISPLAY_SPI_INTERRUPT_MODE
	}




/**********************************
 * @BRIEF	transmit a byte in a SPI_COMMAND format
 **********************************/
void Displ_WriteCommand(uint8_t cmd){
	Displ_Transmit(SPI_COMMAND, &cmd, sizeof(cmd),0);
}




/**********************************
 * @BRIEF	transmit a set of data in a SPI_DATA format
 * @PARAM 	data		buffer data to send
 * 			dataSize	number of bytes in "data" to be sent
 * 			isTouchGFXBuffer 1 only when called by touchgfxDisplayDriverTransmitBlock (for byte endian conversion). All other cases 0
 **********************************/
void Displ_WriteData(uint8_t* buff, size_t buff_size, uint8_t isTouchGFXBuffer){
	if (buff_size==0) return;
	Displ_Transmit(SPI_DATA, buff, buff_size, isTouchGFXBuffer);
}





/**********************************
 * @brief	ILIXXX initialization sequence
 **********************************/
void ILI9XXX_Init(){
	Displ_Select();

	HAL_GPIO_WritePin(DISPL_RST_GPIO_Port, DISPL_RST_Pin, GPIO_PIN_RESET);
	HAL_Delay(1);
	HAL_GPIO_WritePin(DISPL_RST_GPIO_Port, DISPL_RST_Pin, GPIO_PIN_SET);
	HAL_Delay(150);

/******************************************
 * below lines shlould empower brightness
 * and quality with a higher power
 * consumption. I haven't seen meaningful
 * differences on both displays: REMOVED
 * ****************************************
	uint8_t data[10];

		Displ_WriteCommand(ILI9XXX_POWER0);
	#ifdef ILI9341
		data[0]=0x3F; //default 0x21, 3.3V=0x09, 5V=2B
		Displ_WriteData(data,1);
	#endif
	#ifdef ILI9488
		data[0]=0x1F; //default 0E, 3.3V=0x09, 5V=17
		data[1]=0x1F; //default 0E, 5V=17
		Displ_WriteData(data,2);
	#endif

		Displ_WriteCommand(ILI9XXX_POWER1);
	#ifdef ILI9341
		data[0]=0x00; //default 0x00
	#endif
	#ifdef ILI9488
		data[0]=0x40; //default 0x44
	#endif
		Displ_WriteData(data,1);

	#ifdef ILI9488
		Displ_WriteCommand(ILI9488_POWER2);
		data[0]=0x44; //default 0x44
		Displ_WriteData(data,1);
	#endif

	#ifdef ILI9341
		Displ_WriteCommand(ILI9341_POWERA);
		data[0]=0x39; //fixed
		data[1]=0x2C; //fixed
		data[2]=0x00; //fixed
		data[3]=0x35; //default 0x34
		data[4]=0x00; //default 0x02
		Displ_WriteData(data,5);
		Displ_WriteCommand(ILI9341_POWERB);
		data[0]=0x00; //fixed
		data[1]=0x99; //default 0x81
		data[2]=0x30; //default 0x30
		Displ_WriteData(data,3);
	#endif
*/


	Displ_WriteCommand(ILI9XXX_PIXEL_FORMAT);
#ifdef Z_RGB666
	Displ_WriteData((uint8_t *)"\x66",1,0);		// RGB666
#endif
#ifdef Z_RGB565
	Displ_WriteData((uint8_t *)"\x55",1,0);		// RGB565
#endif
	Displ_WriteCommand(ILI9XXX_RGB_INTERFACE);
	Displ_WriteData((uint8_t *)"\x80",1,0);        // disable MISO pin

	Displ_WriteCommand(ILI9XXX_RGB_INTERFACE);
	Displ_WriteData((uint8_t *)"\x80",1,0);        // disable MISO pin

	Displ_WriteCommand(ILI9XXX_SLEEP_OUT);
	HAL_Delay(120);

	Displ_WriteCommand(ILI9XXX_DISPLAY_ON);
	HAL_Delay(5);

}



/**********************************************
 * @brief	defines the display area involved
 * 			in a writing operation and set
 * 			display ready to receive pixel
 * 			information
 * @param  x1,y1,x2,y2 top left and bottom
 * 					   right corner of the area
 * 					   to write
 **********************************************/
void Displ_SetAddressWindow(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2) {
	static uint8_t data[4];

	((uint32_t *)data)[0]=(((x2 & 0xFF)<<24) | ((x2 & 0xFF00)<<8) | ((x1 & 0xFF)<<8) | ((x1 & 0xFF00)>>8) );
	Displ_WriteCommand(ILI9XXX_COLUMN_ADDR);
	Displ_WriteData(data, 4,0);

	((uint32_t *)data)[0]=(((y2 & 0xFF)<<24) | ((y2 & 0xFF00)<<8) | ((y1 & 0xFF)<<8) | ((y1 & 0xFF00)>>8) );
	Displ_WriteCommand(ILI9XXX_PAGE_ADDR);
	Displ_WriteData(data, 4,0);
	Displ_WriteCommand(ILI9XXX_MEMWR);
}




/*****************************************************
 * @brief				first display initialization.
 * @param	orientation	display orientation
 *****************************************************/
void Displ_Init(Displ_Orientat_e orientation){
	if (TOUCH_SPI==DISPL_SPI){													// if touch and display share the same SPI port
		HAL_GPIO_WritePin(DISPL_CS_GPIO_Port, DISPL_CS_Pin, GPIO_PIN_SET); 		// unselect display (will be selected at writing time)
		HAL_GPIO_WritePin(TOUCH_CS_GPIO_Port, TOUCH_CS_Pin, GPIO_PIN_SET);		// unselect touch (will be selected at writing time)
	} else {																	// otherwise leave both port permanently selected
		HAL_GPIO_WritePin(DISPL_CS_GPIO_Port, DISPL_CS_Pin, GPIO_PIN_RESET); 	// select display
		SET_DISPL_SPI_BAUDRATE;
		HAL_GPIO_WritePin(TOUCH_CS_GPIO_Port, TOUCH_CS_Pin, GPIO_PIN_RESET);	// select touch
		SET_TOUCH_SPI_BAUDRATE;
	}
	ILI9XXX_Init();
	Displ_Orientation(orientation);
}







/**********************************************
 * @brief		set orientation of the display
 * @param  	m	orientation
 **********************************************/
void Displ_Orientation(Displ_Orientat_e orientation){
	static uint8_t data[1];
	switch(orientation) {
		case Displ_Orientat_0:
			data[0]=ILI9XXX_MADCTL_0DEG;
			_height = DISPL_HEIGHT;
			_width = DISPL_WIDTH;
			break;
		case Displ_Orientat_90:
			data[0]=ILI9XXX_MADCTL_90DEG;
			_height = DISPL_WIDTH;
			_width = DISPL_HEIGHT;
			break;
		case Displ_Orientat_180:
			data[0]=ILI9XXX_MADCTL_180DEG;
			_height = DISPL_HEIGHT;
			_width = DISPL_WIDTH;
			break;
		case Displ_Orientat_270:
			data[0]=ILI9XXX_MADCTL_270DEG;
			_height = DISPL_WIDTH;
			_width = DISPL_HEIGHT;
			break;
	}
	Displ_WriteCommand(ILI9XXX_MADCTL);
	Displ_WriteData(data,1,0);
	current_orientation = orientation;  //stores active orientation into a global variable for touch routines
}




void HAL_SPI_ErrorCallback(SPI_HandleTypeDef *hspi){
	if (hspi->Instance==DISPL_SPI) {
		Displ_SpiAvailable=1;
	}
}






void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi) {
	if (hspi->Instance==DISPL_SPI) {
		Displ_SpiAvailable=1;

	#ifdef DISPLAY_USING_TOUCHGFX
		DisplayDriver_TransferCompleteCallback();
	#endif

	}
}





/*****************************
 * @brief	fill a rectangle with a color
 * @param	x, y	top left corner of the rectangle
 * 			w, h 	width and height of the rectangle
 ******************************/
void Displ_FillArea(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t color) {
/* four steps:
 * -	define area size to file and data size to transfer
 * -	setup data buffer to transfer
 * -	transfer data to display
 * -	swap buffers
 */

	uint32_t k,x1,y1,area,times;

	if((x >= _width) || (y >= _height) || (w == 0) || (h == 0)) return;//

	x1=x + w - 1;
	if (x1 > _width) {
		x1=_width;
	}

	y1=y + h - 1;
	if (y1 > _height) {
		y1=_height;
	}


// SETUP DISPLAY DATA BUFFER TO TRANSFER
#ifdef Z_RGB565 // setting up dispBuffer in RGB565 format

	uint32_t data32;

	data32=(color>>8) | (color<<8) | (color<<24); 	// supposing color is 0xABCD, data32 becomes 0xCDABCDAB - set a 32 bit variable with swapped endians
	area=((y1-y+1)*(x1-x+1)); 						// area to fill in 16bit pixels
	uint32_t *buf32Pos=(uint32_t *)dispBuffer; 		//dispBuffer defined in bytes, buf32Pos access it as 32 bit words
	if (area<(SIZEBUF>>1)) 							// if area is smaller than dispBuffer
		times=(area>>1)+1; 							// number of times data32 has to be loaded into buffer
	else
		times=(SIZEBUF>>2);  						// dispBuffer size as 32bit-words
	for (k = 0; k < times; k++)
		*(buf32Pos++)=data32; 						// loads buffer moving 32bit-words

#endif
#ifdef Z_RGB666 // setting up dispBuffer in RGB666 format
	uint32_t datasize;

	uint8_t Rbyte=(color & 0xF800)>>8;
	uint8_t Gbyte=(color & 0x07E0)>>3;
	uint8_t Bbyte=(color & 0x001F)<<3;

	area=(((y1-y+1)*(x1-x+1))*3); 		// area to fill in bytes (3 bytes per pixel)
	uint8_t *buf8Pos=dispBuffer; 		//using a local pointer: changing values next

	datasize = (area<(SIZEBUF-3) ? area : (SIZEBUF-3));  //as buf8Pos receives 3 bytes each cycle we must be sure that SIZEBUF will be not overridden in the next loop

	k=0;
	while ((buf8Pos-dispBuffer)<=datasize){
		*(buf8Pos++)=Rbyte;
		*(buf8Pos++)=Gbyte;
		*(buf8Pos++)=Bbyte;
	}
	datasize=(buf8Pos-dispBuffer);
#endif

//START WRITING TO DISPLAY
	Displ_SetAddressWindow(x, y, x1, y1);

#ifdef Z_RGB565 // transferring RGB666 format dispBuffer
	times=(area>>(BUFLEVEL-1));  					//how many times buffer must be sent via SPI. It is (BUFFLEVEL-1) because area is 16-bit while dispBuffer is 8-bit
	for  (k=0;k<times;k++) {
		Displ_WriteData(dispBuffer,SIZEBUF,0);
	}
	Displ_WriteData(dispBuffer,(area<<1)-(times<<BUFLEVEL),0);
#endif
#ifdef Z_RGB666 // transferring RGB666 format dispBuffer
	times=(area/datasize);  					//how many times buffer must be sent via SPI.
	for  (k=0;k<times;k++) {
		Displ_WriteData(dispBuffer,datasize,0);
	}
	Displ_WriteData(dispBuffer,(area-times*datasize),0);      //transfer last data frame
#endif

//BUFFER SWAP
	dispBuffer = (dispBuffer==dispBuffer1 ? dispBuffer2 : dispBuffer1); // swapping buffer

}



#ifndef DISPLAY_USING_TOUCHGFX

/*****************************************
 * WARNING: non tested, never used
 *****************************************/
void ILI9488_DrawImage(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint8_t *data, uint32_t size){
	Displ_SetAddressWindow(x, y, w+x-1, h+y-1);
	Displ_WriteData(data,size,0);
}







void Displ_DrawImage(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint8_t *data){
#ifdef Z_RGB565
	uint32_t size=((uint32_t)w*(uint32_t)h)<<1;
#endif
#ifdef Z_RGB666
	uint32_t size=((uint32_t)w*(uint32_t)h)*3;
#endif

	Displ_SetAddressWindow(x, y, w+x-1, h+y-1);

#ifdef EXT_FLASH_BASEADDRESS
	if (((uint32_t)data>=EXT_FLASH_BASEADDRESS) && ((uint32_t)data<EXT_FLASH_BASEADDRESS+EXT_FLASH_SIZE)) {
		data-=EXT_FLASH_BASEADDRESS;
		while (size>SIZEBUF){
			Flash_Read((uint32_t)data, dispBuffer , SIZEBUF);
			Displ_WriteData(dispBuffer,SIZEBUF,0);
			data+=SIZEBUF;
			size-=SIZEBUF;
			dispBuffer = (dispBuffer==dispBuffer1 ? dispBuffer2 : dispBuffer1); // swapping buffer
		}
		Flash_Read((uint32_t)data, dispBuffer , size);
		data=dispBuffer;
	}
#endif
	Displ_WriteData(data,size,0);
	dispBuffer = (dispBuffer==dispBuffer1 ? dispBuffer2 : dispBuffer1); // swapping buffer
}




/***********************
 * @brief	print a single pixel
 * @params	x, y	pixel position on display
 * 			color	... to be printed
 ***********************/
void Displ_Pixel(uint16_t x, uint16_t y, uint16_t color) {
    if((x >= _width) || (y >= _height))
        return;
    Displ_FillArea(x, y, 1, 1, color);

}




void Displ_drawCircle(int16_t x0, int16_t y0, int16_t r, uint16_t color) {
    int16_t f = 1 - r;
    int16_t ddF_x = 1;
    int16_t ddF_y = -2 * r;
    int16_t x = 0;
    int16_t y = r;

//    writePixel(x0  , y0+r, color);
    Displ_Pixel(x0  , y0+r, color);
    Displ_Pixel(x0  , y0-r, color);
    Displ_Pixel(x0+r, y0  , color);
    Displ_Pixel(x0-r, y0  , color);

    while (x<y) {
        if (f >= 0) {
            y--;
            ddF_y += 2;
            f += ddF_y;
        }
        x++;
        ddF_x += 2;
        f += ddF_x;

        Displ_Pixel(x0 + x, y0 + y, color);
        Displ_Pixel(x0 - x, y0 + y, color);
        Displ_Pixel(x0 + x, y0 - y, color);
        Displ_Pixel(x0 - x, y0 - y, color);
        Displ_Pixel(x0 + y, y0 + x, color);
        Displ_Pixel(x0 - y, y0 + x, color);
        Displ_Pixel(x0 + y, y0 - x, color);
        Displ_Pixel(x0 - y, y0 - x, color);
    }
}





/*****************
 * @brief	clear display with a color.
 * @param	bgcolor
 *****************/
void Displ_CLS(uint16_t bgcolor){
	Displ_FillArea(0, 0, _width, _height, bgcolor);
}
















void drawCircleHelper( int16_t x0, int16_t y0, int16_t r, uint8_t cornername, uint16_t color)
{
    int16_t f     = 1 - r;
    int16_t ddF_x = 1;
    int16_t ddF_y = -2 * r;
    int16_t x     = 0;
    int16_t y     = r;

    while (x<y) {
        if (f >= 0) {
            y--;
            ddF_y += 2;
            f     += ddF_y;
        }
        x++;
        ddF_x += 2;
        f     += ddF_x;
        if (cornername & 0x4) {
            Displ_Pixel(x0 + x, y0 + y, color);
            Displ_Pixel(x0 + y, y0 + x, color);
        }
        if (cornername & 0x2) {
        	Displ_Pixel(x0 + x, y0 - y, color);
        	Displ_Pixel(x0 + y, y0 - x, color);
        }
        if (cornername & 0x8) {
        	Displ_Pixel(x0 - y, y0 + x, color);
        	Displ_Pixel(x0 - x, y0 + y, color);
        }
        if (cornername & 0x1) {
        	Displ_Pixel(x0 - y, y0 - x, color);
        	Displ_Pixel(x0 - x, y0 - y, color);
        }
    }
}









void fillCircleHelper(int16_t x0, int16_t y0, int16_t r, uint8_t cornername, int16_t delta, uint16_t color)
{
	int16_t f     = 1 - r;
  int16_t ddF_x = 1;
  int16_t ddF_y = -2 * r;
  int16_t x     = 0;
  int16_t y     = r;

  while (x<y) {
    if (f >= 0) {
      y--;
      ddF_y += 2;
      f     += ddF_y;
    }
    x++;
    ddF_x += 2;
    f     += ddF_x;

    if (cornername & 0x1) {
    	 Displ_Line(x0+x, y0-y, x0+x, y0+y+1+delta, color);
    	 Displ_Line(x0+y, y0-x,x0+y, y0+x+1+delta, color);
    }
    if (cornername & 0x2) {
      Displ_Line(x0-x, y0-y, x0-x, y0+y+1+delta, color);
      Displ_Line(x0-y, y0-x, x0-y, y0+x+1+delta, color);
    }
  }
}






void Displ_fillCircle(int16_t x0, int16_t y0, int16_t r, uint16_t color) {
    Displ_Line(x0, y0-r, x0, y0+r, color);
    fillCircleHelper(x0, y0, r, 3, 0, color);
}






/************************************************************************
 * @brief	draws a line from "x0","y0" to "x1","y1" of the given "color"
 ************************************************************************/
void Displ_Line(int16_t x0, int16_t y0, int16_t x1, int16_t y1, uint16_t color)
{
	int16_t l,x,steep,ystep,err,dx, dy;

    if (x0==x1){  // fast solve vertical lines
    	if (y1>y0){
    		Displ_FillArea(x0, y0, 1, y1-y0+1, color);
    	}
    	else {
    		Displ_FillArea(x0, y1, 1, y0-y1+1, color);
    	}
    	return;
    }
    if (y0==y1){ // fast solve horizontal lines
    	if (x1>x0)
    		Displ_FillArea(x0, y0, x1-x0+1, 1, color);
    	else
    		Displ_FillArea(x1, y1, x0-x1+1, 1, color);
    	return;
    }

    steep = (y1>y0 ? y1-y0 : y0-y1) > (x1>x0 ? x1-x0 : x0-x1);

    if (steep) {
        _swap_int16_t(x0, y0);
        _swap_int16_t(x1, y1);
    }

    if (x0 > x1) {
        _swap_int16_t(x0, x1);
        _swap_int16_t(y0, y1);
    }

    dx = x1 - x0;
    err = dx >> 1;
    if (y0 < y1) {
        dy = y1-y0;
        ystep =  1 ;
    } else {
        dy = y0-y1;
        ystep =  -1 ;
    }

    l=00;
    for (x=x0; x<=x1; x++) {
    	l++;
        err -= dy;
        if (err < 0) {
        	if (steep) {
        		Displ_FillArea(y0, x0, 1, l, color);
            } else {
            	Displ_FillArea(x0, y0, l, 1, color);
            }
            y0 += ystep;
            l=0;
            x0=x+1;
            err += dx;
        }
    }
    if (l!=0){
    	if (steep) {
    		Displ_FillArea(y0, x0, 1, l-1, color);
    	} else {
    		Displ_FillArea(x0, y0, l-1,1, color);
    	}
    }
}










/***********************
 * @brief	print an empty rectangle of a given thickness
 * @params	x, y	top left corner
 * 			w, h	width and height
 * 			t		border thickness
 * 			color	border color, inner part unchanged
 ***********************/
void Displ_Border(int16_t x, int16_t y, int16_t w, int16_t h, int16_t t,  uint16_t color){
	Displ_FillArea(x, y, w, t, color);
	Displ_FillArea(x, y+h-t, w, t, color);
	Displ_FillArea(x, y, t, h, color);
	Displ_FillArea(x+w-t, y, t, h, color);
}





void Displ_drawTriangle(int16_t x0, int16_t y0, int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint16_t color)
{
    Displ_Line(x0, y0, x1, y1, color);
    Displ_Line(x1, y1, x2, y2, color);
    Displ_Line(x2, y2, x0, y0, color);
}







void Displ_fillTriangle(int16_t x0, int16_t y0, int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint16_t color)
{

    int16_t a, b, y, last;

    // Sort coordinates by Y order (y2 >= y1 >= y0)
    if (y0 > y1) {
        _swap_int16_t(y0, y1); _swap_int16_t(x0, x1);
    }
    if (y1 > y2) {
        _swap_int16_t(y2, y1); _swap_int16_t(x2, x1);
    }
    if (y0 > y1) {
        _swap_int16_t(y0, y1); _swap_int16_t(x0, x1);
    }

    if(y0 == y2) { // Handle awkward all-on-same-line case as its own thing
        a = b = x0;
        if(x1 < a)      a = x1;
        else if(x1 > b) b = x1;
        if(x2 < a)      a = x2;
        else if(x2 > b) b = x2;
//        drawFastHLine(a, y0, b-a+1, color);
        Displ_Line(a, y0, b, y0, color);
        return;
    }

    int16_t
    dx01 = x1 - x0,
    dy01 = y1 - y0,
    dx02 = x2 - x0,
    dy02 = y2 - y0,
    dx12 = x2 - x1,
    dy12 = y2 - y1;
    int32_t
    sa   = 0,
    sb   = 0;

    // For upper part of triangle, find scanline crossings for segments
    // 0-1 and 0-2.  If y1=y2 (flat-bottomed triangle), the scanline y1
    // is included here (and second loop will be skipped, avoiding a /0
    // error there), otherwise scanline y1 is skipped here and handled
    // in the second loop...which also avoids a /0 error here if y0=y1
    // (flat-topped triangle).
    if(y1 == y2) last = y1;   // Include y1 scanline
    else         last = y1-1; // Skip it

    for(y=y0; y<=last; y++) {
        a   = x0 + sa / dy01;
        b   = x0 + sb / dy02;
        sa += dx01;
        sb += dx02;
        /* longhand:
        a = x0 + (x1 - x0) * (y - y0) / (y1 - y0);
        b = x0 + (x2 - x0) * (y - y0) / (y2 - y0);
        */
        if(a > b) _swap_int16_t(a,b);
//        drawFastHLine(a, y, b-a+1, color);
        Displ_Line(a, y, b, y, color);
    }

    // For lower part of triangle, find scanline crossings for segments
    // 0-2 and 1-2.  This loop is skipped if y1=y2.
    sa = (int32_t)dx12 * (y - y1);
    sb = (int32_t)dx02 * (y - y0);
    for(; y<=y2; y++) {
        a   = x1 + sa / dy12;
        b   = x0 + sb / dy02;
        sa += dx12;
        sb += dx02;
        /* longhand:
        a = x1 + (x2 - x1) * (y - y1) / (y2 - y1);
        b = x0 + (x2 - x0) * (y - y0) / (y2 - y0);
        */
        if(a > b) _swap_int16_t(a,b);
//      drawFastHLine(a, y, b-a+1, color);
        Displ_Line(a, y, b, y, color);
    }
}









/***********************
 * @brief	display one character on the display
 * @param 	x,y: top left corner of the character to be printed
 * 			ch, font, color, bgcolor: as per parameter name
 * 			size: (1 or 2) single or double wided printing
 **********************/
void Displ_WChar(uint16_t x, uint16_t y, char ch, sFONT font, uint8_t size, uint16_t color, uint16_t bgcolor) {
    uint32_t i, b, bytes, j, bufSize, mask;

    const uint8_t *pos;
	uint8_t wsize=font.Width; //printing char width

	if (size==2)
		wsize<<= 1;
	bufSize=0;
	bytes=font.Height * font.Size ;
	pos=font.table+(ch - 32) * bytes ;//that's char position in table
	switch (font.Size) {
		case 3:
			mask=0x800000;
			break;
		case 2:
			mask=0x8000;
			break;
		default:
			mask=0x80;
	}

#ifdef Z_RGB565

	uint16_t color1, bgcolor1;
	uint16_t *dispBuffer16=(uint16_t *)dispBuffer;

	color1 = ((color & 0xFF)<<8 | (color >> 8));      		//swapping byte endian: STM32 is little endian, ST7735 is big endian
	bgcolor1 = ((bgcolor & 0xFF)<<8 | (bgcolor >> 8));		//swapping byte endian: STM32 is little endian, ST7735 is big endian

	for(i = 0; i < (bytes); i+=font.Size){
		b=0;
		switch (font.Size) {
			case 3:
				b=pos[i]<<16 | pos[i+1]<<8 | pos[i+2];
				break;
			case 2:
				b=pos[i]<<8 | pos[i+1];
				break;
			default:
				b=pos[i];
		}

		for(j = 0; j < font.Width; j++) {
			if((b << j) & mask)  {
				dispBuffer16[bufSize++] = color1;
				if (size==2){
					dispBuffer16[bufSize++] = color1;
				}
			} else {
				dispBuffer16[bufSize++] = bgcolor1;
				if (size==2) {
					dispBuffer16[bufSize++] = bgcolor1;
				}
			}
		}
	}
	bufSize<<=1;
#endif

#ifdef Z_RGB666
//  setting up char image in RGB666 format

	uint8_t Rcol=(color & 0xF800)>>8;
	uint8_t Gcol=(color & 0x07E0)>>3;
	uint8_t Bcol=(color & 0x001F)<<3;
	uint8_t Rbak=(bgcolor & 0xF800)>>8;
	uint8_t Gbak=(bgcolor & 0x07E0)>>3;
	uint8_t Bbak=(bgcolor & 0x001F)<<3;

	for(i = 0; i < (bytes); i+=font.Size){
		b=0;
		switch (font.Size) {
			case 3:
				b=pos[i]<<16 | pos[i+1]<<8 | pos[i+2];
				break;
			case 2:
				b=pos[i]<<8 | pos[i+1];
				break;
			default:
				b=pos[i];
		}

		for(j = 0; j < font.Width; j++) {
			if((b << j) & mask)  {
				dispBuffer[bufSize++] = Rcol;
				dispBuffer[bufSize++] = Gcol;
				dispBuffer[bufSize++] = Bcol;

				if (size==2){
					dispBuffer[bufSize++] = Rcol;
					dispBuffer[bufSize++] = Gcol;
					dispBuffer[bufSize++] = Bcol;
				}
			} else {
				dispBuffer[bufSize++] = Rbak;
				dispBuffer[bufSize++] = Gbak;
				dispBuffer[bufSize++] = Bbak;
				if (size==2) {
					dispBuffer[bufSize++] = Rbak;
					dispBuffer[bufSize++] = Gbak;
					dispBuffer[bufSize++] = Bbak;
				}
			}
		}
	}
#endif

	Displ_SetAddressWindow(x, y, x+wsize-1, y+font.Height-1);
	Displ_WriteData(dispBuffer,bufSize,0);
	dispBuffer = (dispBuffer==dispBuffer1 ? dispBuffer2 : dispBuffer1); // swapping buffer

}










void Displ_drawRoundRect(int16_t x, int16_t y, int16_t w, int16_t h, int16_t r, uint16_t color)
{
    int16_t max_radius = ((w < h) ? w : h) / 2; // 1/2 minor axis
    if(r > max_radius) r = max_radius;
    Displ_Line(x+r, y, x+w-r-1, y, color);
    Displ_Line(x+r, y+h-1, x-1+w-r, y+h-1, color);
    Displ_Line(x, y+r, x, y-1+h-r, color); // Left
    Displ_Line(x+w-1, y+r, x+w-1, y-1+h-r, color); // Right
    drawCircleHelper(x+r    , y+r    , r, 1, color);
    drawCircleHelper(x+w-r-1, y+r    , r, 2, color);
    drawCircleHelper(x+w-r-1, y+h-r-1, r, 4, color);
    drawCircleHelper(x+r    , y+h-r-1, r, 8, color);
}





void Displ_fillRoundRect(int16_t x, int16_t y, int16_t w, int16_t h, int16_t r, uint16_t color)
{
    int16_t max_radius = ((w < h) ? w : h) / 2; // 1/2 minor axis
    if(r > max_radius) r = max_radius;
    Displ_FillArea(x+r, y, w-2*r, h, color);
    fillCircleHelper(x+w-r-1, y+r, r, 1, h-2*r-1, color);
    fillCircleHelper(x+r    , y+r, r, 2, h-2*r-1, color);
}







/************************
 * @brief	print a string on display starting from a defined position
 * @params	x, y	top left area-to-print corner
 * 			str		string to print
 * 			font	to bu used
 * 			size	1 (normal), 2 (double width)
 * 			color	font color
 * 			bgcolor	background color
 ************************/
void Displ_WString(uint16_t x, uint16_t y, const char* str, sFONT font, uint8_t size, uint16_t color, uint16_t bgcolor) {
	uint16_t delta=font.Width;
	if (size>1)
		delta<<=1;

    while(*str) {
/*
 *  these rows split oversize string in more screen lines
        if(x + font.Width >= _width) {
            x = 0;
            y += font.Height;
            if(y + font.Height >= _height) {
                break;
            }
            if(*str == ' ') {
                // skip spaces in the beginning of the new line
                str++;
                continue;
            }
        }
 */
        Displ_WChar(x, y, *str, font, size, color, bgcolor);
        x += delta;
        str++;
    }
}





/************************
 * @brief	print a string on display centering into a defined area
 * @params	x0, y0	top left area corner
 * 			x1, y1	bottom right corner
 * 			str		string to print
 * 			font	to bu used
 * 			size	1 (normal), 2 (double width)
 * 			color	font color
 * 			bgcolor	background color
 ************************/
void Displ_CString(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, const char* str, sFONT font, uint8_t size, uint16_t color, uint16_t bgcolor) {
	uint16_t x,y;
	uint16_t wsize=font.Width;
	static uint8_t cambia=0;
	if (size>1)
		wsize<<=1;
	if ((strlen(str)*wsize)>(x1-x0+1))
		x=x0;
	else
		x=(x1+x0+1-strlen(str)*wsize) >> 1;
	if (font.Height>(y1-y0+1))
		y=y0;
	else
		y=(y1+y0+1-font.Height) >> 1;

	if (x>x0){
		Displ_FillArea(x0,y0,x-x0,y1-y0+1,bgcolor);
	} else
		x=x0; // fixing here mistake could be due to roundings: x lower than x0.
	if (x1>(strlen(str)*wsize+x0))
		Displ_FillArea(x1-x+x0-1,y0,x-x0+1,y1-y0+1,bgcolor);

	if (y>y0){
		Displ_FillArea(x0,y0,x1-x0+1,y-y0,bgcolor);
	} else
		y=y0; //same comment as above
	if (y1>=(font.Height+y0))
		Displ_FillArea(x0,y1-y+y0,x1-x0+1,y-y0+1,bgcolor);

	cambia = !cambia;

	Displ_WString(x, y, str, font, size, color, bgcolor);

}


#endif





/**************************************
 * @brief		set backlight level
 * 				PLEASE NOTE: if not in "DIMMING MODE" only 'F', '1', '0' and 'Q' available
 * @param	cmd	'S'		put display in stby (light level=BKLIT_STBY_LEVEL)
 * 				'W' 	wake-up from stdby restoring previous level
 *				'+'		add 1 step to the current light level
 *				'-'		reduce 1 step to the current light level
 *				'F','1'	set the display level to max
 *				'0'		set the display level to 0 (off)
 *				'I'		'Initialize'  IT MUST BE in dimming mode
 *              'Q'		do nothing, just return current level
 * @return		current backlight level
 **************************************/
uint32_t  Displ_BackLight(uint8_t cmd) {

#ifdef DISPLAY_DIMMING_MODE
	static uint16_t memCCR1=0;  			//it stores CCR1 value while in stand-by
#endif

	switch (cmd) {
	case 'Q':
		__NOP();
		break;
#ifndef DISPLAY_DIMMING_MODE
	case 'F':
	case '1':
		HAL_GPIO_WritePin(DISPL_LED_GPIO_Port, DISPL_LED_Pin, GPIO_PIN_SET);
		break;
	case '0':
		HAL_GPIO_WritePin(DISPL_LED_GPIO_Port, DISPL_LED_Pin, GPIO_PIN_RESET);
		break;
#else
	case 'F':
	case '1':
		BKLIT_TIMER->BKLIT_CCR=BKLIT_TIMER->ARR;
		break;
	case '0':
		BKLIT_TIMER->BKLIT_CCR=0;
		break;
	case 'W':
		BKLIT_TIMER->BKLIT_CCR=memCCR1;					//restore previous level
		break;
	case 'S':
		memCCR1=BKLIT_TIMER->BKLIT_CCR;
		if (BKLIT_TIMER->BKLIT_CCR>=(BKLIT_STBY_LEVEL))	//set stby level only if current level is higher
			BKLIT_TIMER->BKLIT_CCR=(BKLIT_STBY_LEVEL);
		break;
	case '+':
		if (BKLIT_TIMER->ARR>BKLIT_TIMER->BKLIT_CCR)		// if CCR1 has not yet the highest value (ARR)
			++BKLIT_TIMER->BKLIT_CCR;
		else
			BKLIT_TIMER->BKLIT_CCR=BKLIT_TIMER->ARR;
		break;
	case '-':
		if (BKLIT_TIMER->BKLIT_CCR>0)					// if CCR1 has not yet the lowest value (0)
			--BKLIT_TIMER->BKLIT_CCR;
		else
			BKLIT_TIMER->BKLIT_CCR=0;
		break;
	case 'I':
	  	HAL_TIM_PWM_Start(&BKLIT_T, BKLIT_CHANNEL);
		BKLIT_TIMER->BKLIT_CCR=BKLIT_INIT_LEVEL;
		break;
#endif
	default:
		break;
	}
#ifndef DISPLAY_DIMMING_MODE
	return HAL_GPIO_ReadPin(DISPL_LED_GPIO_Port, DISPL_LED_Pin);
#else
	return (BKLIT_TIMER->BKLIT_CCR);
#endif
}



/*********************************************************
 * @brief	TouchGFX integration: returns status of
 * 			communication to the display
 * @return	1 = there is a transmission running
 * 			0 = no transmission
 *********************************************************/
int touchgfxDisplayDriverTransmitActive(){
	// using the flag indicating SPI port availability
	// already used to drive communication via DMA
	return (!Displ_SpiAvailable);
}





/*********************************************************
 * @brief	TouchGFX integration: write to display the
 * 			block indicated by parameters
 *********************************************************/
void touchgfxDisplayDriverTransmitBlock(const uint8_t* pixels, uint16_t x, uint16_t y, uint16_t w, uint16_t h){
	//START WRITING TO DISPLAY
		Displ_SetAddressWindow(x, y, x+w-1, y+h-1);
		Displ_WriteData((uint8_t* )pixels,((w*h)<<1),1);
}





/*********************************************************
 * @brief	TouchGFX integration: this is the callback
 * 			function run by timer interrupt implementing
 * 			the tick timer for TouchGFX
 *********************************************************/
#ifdef DISPLAY_USING_TOUCHGFX
void HAL_TIM_PeriodElapsedCallback (TIM_HandleTypeDef * htim){
	if (htim==&TGFX_T){
		  touchgfxSignalVSync();
	}
}
#endif //DISPLAY_USING_TOUCHGFX

```
:::  
#### z_touch_XPT2046.h 
:::spoiler   
```c=
/*
 * 	z_touch_XPT2046.h
 *	rel. TouchGFX.1.30
 *
 *  Created on: 05 giu 2023
 *      Author: mauro
 *
 *  licensing: https://github.com/maudeve-it/ILI9XXX-XPT2046-STM32/blob/c097f0e7d569845c1cf98e8d930f2224e427fd54/LICENSE
 *
 *  Installing and using this library follow instruction on: https://github.com/maudeve-it/ILI9XXX-XPT2046-STM32
 *
 *  WARNING:
 *	in main.h put the #insert of this file BELOW the #insert of z_displ_ILIxxxx.h
 *
 *  If using TouchGFX,
 *  you have also to add the below include:
#include "main.h"
 *  into STM32TouchController.cpp file
 *  changing also sampleTouch()
 *  as shown here:
bool STM32TouchController::sampleTouch(int32_t& x, int32_t& y)
{
	return ((bool) Touch_TouchGFXSampleTouch(&x, &y));
}
 *
 *  see also z_displ_ili9XXX.h
 *
 */
#ifndef __XPT2046_H
#define __XPT2046_H


/*||||||||||| USER/PROJECT PARAMETERS |||||||||||*/

/*****************     STEP 1      *****************
 **************** PORT PARAMETERS *****************
 ** properly set the below the 2 defines to address
 ********  the SPI port defined on CubeMX *********/

#define TOUCH_SPI_PORT 	hspi2
#define TOUCH_SPI 		SPI2


/*****************     STEP 2      *****************
 **********   KEY REPEAT FOR TOUCHGFX   ***********
 * used only in TouchGFX integration
 * - set a value above 0 defining the timeout (ms)
 *   before starting key repeat, reading touch sensor
 * - set 0 disabling key repeat (single pulse)
 * - set -1 for a continuous touch needed by
 *   "dragging" widgets
 * (see GitHub page indicated on top for details)
 **************************************************/
#define DELAY_TO_KEY_REPEAT -1

/*|||||||| END OF USER/PROJECT PARAMETERS ||||||||*/




/*|||||||||||||| DEVICE PARAMETERS |||||||||||||||||*/
/* you should need to change nothing from here on */

/**************************************************
 * this is the command to send to XPT2046 asking to
 * poll axis and return corresponging value.
 ****************************************	**********/
#define X_AXIS		0xD0
#define Y_AXIS		0x90
#define Z_AXIS		0xB0


/**********************************************************************************
 *	polling XPT2046 axis, the returning value exceeding the below limit
 *	indicates there is no touch. WARNING: a "random within limit" value is returned
 *	sometimes (often) even if there is no touch, so at least two consecutive r
 *	eadings must be performed to confirm a touch
 **********************************************************************************/
#ifdef ILI9341
#define X_THRESHOLD		0x0200	//below threeshold there is no touch
#define Z_THRESHOLD		0x0200	//below threeshold there is no touch
#endif
#ifdef ILI9488
#define X_THRESHOLD		0x0500	//below threeshold there is no touch
#define Z_THRESHOLD		0x0500	//below threeshold there is no touch
#endif


/**********************************************************************************
 ***************************** CALIBRATION PARAMETERS *****************************
 **********************************************************************************
 * parameters for the linear conversion from a touch sensor reading, to
 * the XY display position
 * using the formula:
 * Xdispl = AX * Xtouch + BX
 * Ydispl = AY * Ytouch + BY
 *
 **********************************************************************************/

#ifdef ILI9341
#define T_ROTATION_0
#define AX 0.00801f
#define BX -11.998f
#define AY 0.01119f
#define BY -39.057f
/*
#define AX -0.00801f
#define BX 320.0f
#define AY -0.01119f
#define BY 240.0f
*/

#endif

#ifdef ILI9488_V1
#define T_ROTATION_270
#define AX 0.016f
#define BX -20.0f
#define AY 0.011f
#define BY -15.0f
#endif

#ifdef ILI9488_V2
#define T_ROTATION_0
#define AX -0.0112f
#define BX 336.0f
#define AY 0.0166f
#define BY -41.38f
#endif




/**********************************************************************************
 * parameters screen/touch orientation: set the touch orientation to the corresponding
 * screen orientation:
 * on ILI9341 0° on touch correspond to 0° of the screen
 * on ILI9341 0° on touch correspond to 270° of the screen
 * set also the size of a 0° row and a 90° row (a 0° height)
 **********************************************************************************/
#ifdef T_ROTATION_0
#define TOUCH0 			Displ_Orientat_0
#define TOUCH90 		Displ_Orientat_90
#define TOUCH180 		Displ_Orientat_180
#define TOUCH270 		Displ_Orientat_270
#define TOUCH_0_WIDTH 	DISPL_WIDTH
#define TOUCH_0_HEIGHT	DISPL_HEIGHT
#endif

#ifdef T_ROTATION_90
#define TOUCH0 			Displ_Orientat_90
#define TOUCH90 		Displ_Orientat_180
#define TOUCH180 		Displ_Orientat_270
#define TOUCH270 		Displ_Orientat_0
#define TOUCH_0_WIDTH 	DISPL_HEIGHT
#define TOUCH_0_HEIGHT	DISPL_WIDTH
#endif

#ifdef T_ROTATION_180
#define TOUCH0 			Displ_Orientat_180
#define TOUCH90 		Displ_Orientat_270
#define TOUCH180 		Displ_Orientat_0
#define TOUCH270 		Displ_Orientat_90
#define TOUCH_0_WIDTH 	DISPL_WIDTH
#define TOUCH_0_HEIGHT	DISPL_HEIGHT
#endif

#ifdef T_ROTATION_270
#define TOUCH0 			Displ_Orientat_270
#define TOUCH90 		Displ_Orientat_0
#define TOUCH180 		Displ_Orientat_90
#define TOUCH270 		Displ_Orientat_180
#define TOUCH_0_WIDTH 	DISPL_HEIGHT
#define TOUCH_0_HEIGHT	DISPL_WIDTH
#endif



/*|||||||||||||| INTERFACE PARAMETERS |||||||||||||||||*/

/**********************************************************************************
 * next parameters are used only in TouchGFX: in Touch_TouchGFXSampleTouch() and
 * Touch_GotATouch(), helping in using dragging widgets like scrolling lists
 * You can try to change the below parameters only if your display looses quality or over-used
 **********************************************************************************/
#define TOUCHGFX_TIMING 60		//delay between 2 consecutive Touch_GotATouch(2)readings		(0=disabled)
#define TOUCHGFX_SENSITIVITY 1  //square of X pixels size having the same value					(1 disabled)
#define TOUCHGFX_MOVAVG 1		//makes position based on average of the last X readings		(1 disabled)
#define TOUCHGFX_REPEAT_IT 0	// after a long touch (dragging) repeat X times last position	(0=disabled)
#if DELAY_TO_KEY_REPEAT==-1
#define TOUCHGFX_REPEAT_NO 0	// after a REPEAT_IT repeat X times a no touch					(0=disabled)
#else
#define TOUCHGFX_REPEAT_NO 5	// after a REPEAT_IT repeat X times a no touch					(0=disabled)
#endif

/*||||||||||| END OF INTERFACE PARAMETERS ||||||||||||*/


/*|||||||||||||| FUNCTION DECLARATIONS |||||||||||||||||*/

void HAL_GPIO_EXTI_Falling_Callback(uint16_t GPIO_Pin);
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin);

uint8_t Touch_In_XY_area(uint16_t xpos,uint16_t ypos,uint16_t width,uint16_t height);
uint8_t Touch_GotATouch(uint8_t reset);
uint8_t Touch_WaitForUntouch(uint16_t delay);
uint8_t Touch_WaitForTouch(uint16_t delay);
uint8_t Touch_PollTouch();
void Touch_GetXYtouch(uint16_t *x, uint16_t *y, uint8_t *isTouch);


#ifdef DISPLAY_USING_TOUCHGFX
uint8_t Touch_TouchGFXSampleTouch(int32_t *x, int32_t *y);
#endif /* DISPLAY_USING_TOUCHGFX */

#endif /* __XPT2046_H */


```
:::  
#### z_touch_XPT2046.c 
:::spoiler   
```c=
/*
 * z_touch_XPT2046.c
 * 	rel. TouchGFX.1.30
 *
 *  Created on: 5 giu 2023
 *      Author: mauro
 *
 *  licensing: https://github.com/maudeve-it/ILI9XXX-XPT2046-STM32/blob/c097f0e7d569845c1cf98e8d930f2224e427fd54/LICENSE
 *
 *	Install and use this library following instruction on: https://github.com/maudeve-it/ILI9XXX-XPT2046-STM32
 *
*/




#include "main.h"

extern SPI_HandleTypeDef TOUCH_SPI_PORT;
volatile extern uint8_t Displ_SpiAvailable;  			// 0 if SPI is busy or 1 if it is free (transm cplt)

extern Displ_Orientat_e current_orientation;			// indicates the active display orientation. Set by Displ_Orientation

volatile uint8_t Touch_PenDown=0;						// set to 1 by pendown interrupt callback, reset to 0 by sw
volatile uint8_t Touch_Int_Enabled=1;					// while reading touch sensor touch interrupt handling is disabled through this flag



void Touch_HandlePenDownInterrupt (){
	if (Touch_Int_Enabled) {
		Touch_PenDown=1;
	}
}



void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin){
	if (GPIO_Pin==TOUCH_INT_Pin){
		Touch_HandlePenDownInterrupt();
	}
}



void HAL_GPIO_EXTI_Falling_Callback(uint16_t GPIO_Pin){
	HAL_GPIO_EXTI_Callback(GPIO_Pin);
}

void HAL_GPIO_EXTI_Rising_Callback(uint16_t GPIO_Pin){
	HAL_GPIO_EXTI_Callback(GPIO_Pin);
}




/******************************************
 * @brief	enable touch, disabling display
 * 			set SPI baudrate as needed
 ******************************************/
void Touch_Select(void) {
	if (TOUCH_SPI==DISPL_SPI){														// if touch and display share the same SPI port
		if (!HAL_GPIO_ReadPin(DISPL_CS_GPIO_Port, DISPL_CS_Pin)) {					// if display selected
			while (!Displ_SpiAvailable) {};  										// waiting for completing display communication. Flag is set to 1 by transmission-complete interrupt callback
			HAL_GPIO_WritePin(DISPL_CS_GPIO_Port, DISPL_CS_Pin, GPIO_PIN_SET); 		// unselect display
		}
		SET_TOUCH_SPI_BAUDRATE;				   										//change SPI port speed as per display needs
		HAL_GPIO_WritePin(TOUCH_CS_GPIO_Port, TOUCH_CS_Pin, GPIO_PIN_RESET);
	}
}



/******************************************
 * @brief	disable touch
 ******************************************/
void Touch_UnSelect(void) {
	if (TOUCH_SPI==DISPL_SPI){														// if touch and display share the same SPI port
		HAL_GPIO_WritePin(TOUCH_CS_GPIO_Port, TOUCH_CS_Pin, GPIO_PIN_SET);			// unselect touch
	}
}





/*******************************************************************************
 * @brief			Poll display for the current level of X, Y, or Z
 * @params	axis	use only one of the three options X_AXIS, Y_AXIS or Z_AXIS
 * @return			the level measured on the "axis" axis
 * 					PLEASE NOTE this function should be only for internal usage
 * 								Use Touch_GetXYTouch() instead
 *******************************************************************************/
uint16_t Touch_PollAxis(uint8_t axis) {
	uint8_t poll[2] = {0,0};
	uint32_t poll16;
	
	if (TOUCH_SPI==DISPL_SPI){				// if touch and display share the same SPI port
		Touch_Select();						// enable CS on touch device
	}

	Touch_Int_Enabled=0;	//disable interrupt handling: sensor reading triggers interrupt
	// disable interrupt while enquiring the touch sensor because it triggers the interrupt pin
	HAL_NVIC_DisableIRQ(TOUCH_INT_EXTI_IRQn);

	HAL_SPI_Transmit(&TOUCH_SPI_PORT, &axis, 1, 10);
	if (HAL_SPI_Receive(&TOUCH_SPI_PORT, poll, 2, 10) == HAL_OK) {
		poll16 = (poll[0]<<8) + poll[1];
	}
	else {
		poll16 = 0;
	}

//enable back interrupt after reading the sensor
	HAL_NVIC_ClearPendingIRQ(TOUCH_INT_EXTI_IRQn);
	HAL_NVIC_EnableIRQ(TOUCH_INT_EXTI_IRQn);

	Touch_Int_Enabled=1;

	if (TOUCH_SPI==DISPL_SPI){	// if touch and display share the same SPI port
		Touch_UnSelect();
	}

	return poll16;
}





/*********************************************************************************
 * @brief			polls touch screen just on Z axis returning if
 * 					it is currently touched.
 * 					That's regardless touch recording flag (interrupt received)
 * @return	1/0 	1 if detected a touch, otherwise 0;
 *********************************************************************************/
uint8_t Touch_PollTouch(){

const uint8_t pollingLevel=4;
uint8_t k;
uint32_t touch;

// reading Z making an average over (1<<pollingLevel) readings
	touch=0;
	for (k=0;k<(1<<pollingLevel);k++)
		touch += Touch_PollAxis(Z_AXIS);
	touch >>= pollingLevel;  //get the average value
//	*isTouch= ((touch<=Z_THRESHOLD) ? 0 : 1);
	return (touch>Z_THRESHOLD);
}





/*********************************************************************************
 * @brief			polls touch screen and returning its XY screen position
 * 					that's regardless touch recording flag (interrupt received)
 * @return	x,y		in case isTouch=1 contain touch coordinates
 * 			isTouch is 1 if detected a touch, otherwise 0;
 *********************************************************************************/
void Touch_GetXYtouch(uint16_t *x, uint16_t *y, uint8_t *isTouch){

const uint8_t pollingLevel=4;
//sTouchData XYposition;
uint8_t k;

uint32_t touchx,touchy,touch;



// get the average value (over "1<<pollingLevel" attempts of X, Y and Z axes readings)

	// reading Z
	touch=0;
	for (k=0;k<(1<<pollingLevel);k++)
		touch += Touch_PollAxis(Z_AXIS);
	touch >>= pollingLevel;  //get the average value
	if (touch<=Z_THRESHOLD) {
		*isTouch=0;
		HAL_NVIC_ClearPendingIRQ(TOUCH_INT_EXTI_IRQn);
		return;	// no touch: return 0
	}

	// reading X
	touch=0;
	for (k=0;k<(1<<pollingLevel);k++)
		touch += Touch_PollAxis(X_AXIS);
	touch >>= pollingLevel;  //get the average value
	if (touch<=X_THRESHOLD) {
		*isTouch=0;
		HAL_NVIC_ClearPendingIRQ(TOUCH_INT_EXTI_IRQn);
		return;	// no touch: return 0
	}
	touchx=(AX*touch+BX);

	// reading Y - there is no a threshold for Y
	touch=0;
	for (k=0;k<(1<<pollingLevel);k++)
		touch += Touch_PollAxis(Y_AXIS);
	touch >>= pollingLevel; //get the average value

	touchy=(AY*touch+BY);


//having X and Y axis average values
// calculating coordinates as per screen orientation
	switch (current_orientation)
	{
	case TOUCH0:
		*x=touchx;
		*y=touchy;
		break;
	case TOUCH90:
		*x=touchy;
		*y=(TOUCH_0_WIDTH-touchx);
		break;
	case TOUCH180:
		*x=(TOUCH_0_WIDTH-touchx);
		*y=(TOUCH_0_HEIGHT - touchy);
		break;
	case TOUCH270:
		*x=(TOUCH_0_HEIGHT- touchy);
		*y=touchx;
		break;
	}

// set flag indicating there was a touch
	*isTouch=1;
	return;
}





/***********************************************************
 * @brief			wait for a touch within an assigned time
 * @params	delay	max time (ms) waiting for a touch, 0=infinite
 * #return	1 		if touched within "delay" period
 * 			0		if elapsed time with no touch
 * 					PLEASE NOTE: doesn't reset Touch recording flag
 ***********************************************************/
uint8_t Touch_WaitForTouch(uint16_t delay) {
	uint16_t starttime;

	starttime = HAL_GetTick();
	while (!Touch_PenDown) {
		if ((delay!=0) && ((HAL_GetTick()-starttime)>delay))
			return 0;
	};
	return 1;
}






/*************************************************************
 * @brief			wait for the pen left within an assigned time
 * @params	delay	max time (ms) waiting for leaving touch, 0=infinite
 * #return	1 		if no touch on display
 * 			0		if elapsed time still touching display
 * PLEASE NOTE		if pen up, it resets the touch recording flag
 *************************************************************/
uint8_t Touch_WaitForUntouch(uint16_t delay) {
	uint16_t starttime;
	uint8_t pen_up=0;

	starttime = HAL_GetTick();
	while (1) {
		if ((delay!=0) && ((HAL_GetTick()-starttime)>delay))
			return 0;
		if (Touch_PollAxis(Z_AXIS)<=Z_THRESHOLD)
			pen_up=1;
//		if (Touch_PollAxis(Y_AXIS)>=Y_THRESHOLD)     // check on Y_AXIS no more used since introducing ILI9488
//			pen_up=1;
		if (Touch_PollAxis(X_AXIS)<=X_THRESHOLD)
			pen_up=1;
		if (pen_up) {            //	Pen is now up: reset Touch_PenDown anyway.
			HAL_Delay(10);		// pen is Up just now: wait just a few
			Touch_PenDown=0;
			return 1;
		}
	}
}




/***********************************************************
 * @brief			check if there is a touch inside the
 * 					display area defined by parameters
 * @params	xpos,
 * 			ypos,
 * 			width,
 * 			height	display area to be polled for a touch
 * @return	1		if there is a touch inside area
 * 			0		if no touch or touch outside area defined
 ***********************************************************/
uint8_t Touch_In_XY_area(uint16_t xpos,uint16_t ypos,uint16_t width,uint16_t height) {
//sTouchData posXY;
uint16_t x,y;
uint8_t isTouch;
	Touch_GetXYtouch(&x, &y, &isTouch);
	if (!isTouch)
		return 0;
	if (x>=xpos)
		if (x<xpos+width)
			if (y>=ypos)
				if (y<ypos+height)
					return 1;
	return 0;
}






/***********************************************************
 * @brief			check if interrupt registered a touch
 * 					resetting touch flag if asked by parameter
 * @params	reset	0	returns touch flag value without resetting it
 * 					1	returns touch flag value resetting it
 * 					2	returns touch flag only every TOUCHGFX_TIMING timeinterval. It doesn't reset flag.
 * 						(use "2" in main loop activating touchgfxSignalVSync()
 * @returns		1	if recorded a touch
 * 				0	if no touch recorded
 ***********************************************************/

uint8_t Touch_GotATouch(uint8_t reset) {
static uint32_t touchTime=0;
uint8_t result = Touch_PenDown;

//	if (result)
//		result=Touch_PollTouch();

	if (reset==2){
		if ((HAL_GetTick()-touchTime) >= TOUCHGFX_TIMING)
			touchTime=HAL_GetTick();
		else
			result=0;
	}
	if (reset==1)
		Touch_PenDown=0;
	return result;
}





#ifdef DISPLAY_USING_TOUCHGFX

/***********************************************************
 * @brief	Linking function to TouchGFX
 * 			Handles key repeat (controlled by PAUSE_TO_KEY_REPEAT)
 * @return	1 if detected a touch, otherwise 0
 * @usage	in STM32TouchController.cpp add this line
 *			#include "main.h",
 *			then, into STM32TouchController::sampleTouch(int32_t& x, int32_t& y)
 *			change "return false;"
 *			into:  "return ((bool) Touch_TouchGFXSampleTouch(&x, &y));"
 * 			that's enough for touch integration in TouchGFX
***********************************************************/
uint8_t Touch_TouchGFXSampleTouch(int32_t *x, int32_t *y){
	//	sTouchData result;
	uint8_t isTouch=0;				// preset to no touch
	uint16_t xx=0,yy=0;  			// need to convert library coordinates type (uint16_t) to TouchGFX ones (int32_t)
	static uint8_t flipTouch=0;		// switches 0/1, on every function call, until sensor is touched allowing to return key repeat
	static uint32_t touchTime=1; 	// tick value get on the first touch. 0 means display untouched.
	static uint16_t avgXX=0, avgYY=0;  			// need to convert library coordinates type (uint16_t) to TouchGFX one (int32_t)
	static uint8_t repetition=TOUCHGFX_REPEAT_IT+TOUCHGFX_REPEAT_NO;

	if (Touch_GotATouch(0)){				// polls interrupt flag not resetting it
		Touch_GetXYtouch(&xx,&yy,&isTouch);	// get touch sensor position
		if (!isTouch){						// received a "no touch"
			if (touchTime != 0){			// if previously touched
				if ((repetition--)>TOUCHGFX_REPEAT_NO+1){		// n-repetition of last touch sending
					*x = avgXX;
					*y = avgYY;
					isTouch=1;
				} else if ((repetition==255)) {  // that's -1
					touchTime=0;					// set display as untouched
					Touch_GotATouch(1);				// reset interrupt touch flag
					repetition=TOUCHGFX_REPEAT_IT+TOUCHGFX_REPEAT_NO;  //reset repetition counter
				}
			}
		} else {					// display touched

			if (touchTime==0) {				// if previously untouched
				avgXX =(xx/TOUCHGFX_SENSITIVITY)*TOUCHGFX_SENSITIVITY;
				avgYY =(yy/TOUCHGFX_SENSITIVITY)*TOUCHGFX_SENSITIVITY;
				touchTime=HAL_GetTick();	// store tick value at touch time
				flipTouch=1;				// set switch to send touch now
			} else {						// not a new touch
				if (((HAL_GetTick()-touchTime)>DELAY_TO_KEY_REPEAT) && (DELAY_TO_KEY_REPEAT > 0)){	// if timeout to key repeat is over (0 means no key repeat)
					flipTouch=!flipTouch;	// alternate every time function is called
				} else
					if (DELAY_TO_KEY_REPEAT == 0)
						flipTouch=0;		// (DELAY_TO_KEY_REPEAT == 0) means a single pulse, "-1" keep pulse as long as touch
			}
			if (flipTouch) { 	// return position only if the switching flag is on
				*x=(((avgXX*(TOUCHGFX_MOVAVG-1)+((xx/TOUCHGFX_SENSITIVITY)*TOUCHGFX_SENSITIVITY)))/TOUCHGFX_MOVAVG);
				*y=(((avgYY*(TOUCHGFX_MOVAVG-1)+((yy/TOUCHGFX_SENSITIVITY)*TOUCHGFX_SENSITIVITY)))/TOUCHGFX_MOVAVG);
				avgXX = *x;
				avgYY = *y;
			} else {			// otherwise return "no touch" from display
				isTouch = 0;
			}
		}
	}
	return isTouch;
}

#endif



```
:::  
#### STM32TouchController.cpp 
:::spoiler   
```c=
#include "main.h"
#include "z_touch_XPT2046.h"
bool STM32TouchController::sampleTouch(int32_t& x, int32_t& y)
{
    /**
     * By default sampleTouch returns false,
     * return true if a touch has been detected, otherwise false.
     *
     * Coordinates are passed to the caller by reference by x and y.
     *
     * This function is called by the TouchGFX framework.
     * By default sampleTouch is called every tick, this can be adjusted by HAL::setTouchSampleRate(int8_t);
     *
     */
	return ((bool) Touch_TouchGFXSampleTouch(&x, &y));
}
```
:::  


___
### touchGFX設定
點擊
![image](https://hackmd.io/_uploads/H1i6ABvJll.png)
![image](https://hackmd.io/_uploads/rk4H1UvJex.png)
![image](https://hackmd.io/_uploads/SJRSyLD1gg.png)
調整UI介面
![image](https://hackmd.io/_uploads/H1IuJIDkgg.png)
添加背景
![image](https://hackmd.io/_uploads/SJPqkLw1eg.png)
把原本的背景刪除
![image](https://hackmd.io/_uploads/HkFTy8Pklx.png)
可以模擬看看成效
![image](https://hackmd.io/_uploads/rkiWeLPyxl.png)
完成後就可以生成程式碼了
![image](https://hackmd.io/_uploads/HJGEeUDyle.png)

##
[📖參考資料](https://www.youtube.com/watch?v=g1siKaPox88)