## TFT ILI9341實作
## Demo
{%youtube _0Ot_U6uRhs %}
## STM32CubeIDE設定
### PIN 設定

---
### GPIO設定

---
### RCC設定

---
### SYS設定

---
### TIM設定

---
### SPI設定
選



---
### NVIC 設定

---
### CRC設定

___
### touchGFX設定


___
### Clock設定

---
### 外設檔案分開+生成

#### 生成程式碼

---
### Nucleo_Pinou

---
### 電路圖

---
## 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設定
點擊



調整UI介面

添加背景

把原本的背景刪除

可以模擬看看成效

完成後就可以生成程式碼了

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