From 80b5d91ba4bb96d107ee7a4066b8141b40e76be2 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Wed, 11 Oct 2017 00:59:14 +0100 Subject: [PATCH 001/150] Fix right edge jpeg image corruption Jpeg images with non integer number of MCU pixels showed corrupted right edge. Fix is to concatenate pixels into a contiguous block. Examples corrected. --- .../TFT_flash_jpg.ino} | 22 ++++++++++++++++++- .../{TFT_FLASH_Jpeg => TFT_flash_jpg}/jpeg1.h | 0 .../{TFT_FLASH_Jpeg => TFT_flash_jpg}/jpeg2.h | 0 .../{TFT_FLASH_Jpeg => TFT_flash_jpg}/jpeg3.h | 0 .../{TFT_FLASH_Jpeg => TFT_flash_jpg}/jpeg4.h | 0 .../480 x 320/TFT_flash_jpg/TFT_flash_jpg.ino | 21 +++++++++++++++++- 6 files changed, 41 insertions(+), 2 deletions(-) rename examples/160 x 128/{TFT_FLASH_Jpeg/TFT_FLASH_Jpeg.ino => TFT_flash_jpg/TFT_flash_jpg.ino} (94%) rename examples/160 x 128/{TFT_FLASH_Jpeg => TFT_flash_jpg}/jpeg1.h (100%) rename examples/160 x 128/{TFT_FLASH_Jpeg => TFT_flash_jpg}/jpeg2.h (100%) rename examples/160 x 128/{TFT_FLASH_Jpeg => TFT_flash_jpg}/jpeg3.h (100%) rename examples/160 x 128/{TFT_FLASH_Jpeg => TFT_flash_jpg}/jpeg4.h (100%) diff --git a/examples/160 x 128/TFT_FLASH_Jpeg/TFT_FLASH_Jpeg.ino b/examples/160 x 128/TFT_flash_jpg/TFT_flash_jpg.ino similarity index 94% rename from examples/160 x 128/TFT_FLASH_Jpeg/TFT_FLASH_Jpeg.ino rename to examples/160 x 128/TFT_flash_jpg/TFT_flash_jpg.ino index 7d0f662..4da9af4 100644 --- a/examples/160 x 128/TFT_FLASH_Jpeg/TFT_FLASH_Jpeg.ino +++ b/examples/160 x 128/TFT_flash_jpg/TFT_flash_jpg.ino @@ -130,12 +130,32 @@ void renderJPEG(int xpos, int ypos) { int mcu_x = JpegDec.MCUx * mcu_w + xpos; // Calculate coordinates of top left corner of current MCU int mcu_y = JpegDec.MCUy * mcu_h + ypos; - // check if the image block size needs to be changed for the right and bottom edges + + // check if the image block size needs to be changed for the right edge if (mcu_x + mcu_w <= max_x) win_w = mcu_w; else win_w = min_w; + + // check if the image block size needs to be changed for the bottom edge if (mcu_y + mcu_h <= max_y) win_h = mcu_h; else win_h = min_h; + // copy pixels into a contiguous block + if (win_w != mcu_w) + { + uint16_t *cImg; + int p = 0; + cImg = pImg + win_w; + for (int h = 1; h < win_h; h++) + { + p += mcu_w; + for (int w = 0; w < win_w; w++) + { + *cImg = *(pImg + w + p); + cImg++; + } + } + } + // draw image MCU block only if it will fit on the screen if (( mcu_x + win_w ) <= tft.width() && ( mcu_y + win_h ) <= tft.height()) { diff --git a/examples/160 x 128/TFT_FLASH_Jpeg/jpeg1.h b/examples/160 x 128/TFT_flash_jpg/jpeg1.h similarity index 100% rename from examples/160 x 128/TFT_FLASH_Jpeg/jpeg1.h rename to examples/160 x 128/TFT_flash_jpg/jpeg1.h diff --git a/examples/160 x 128/TFT_FLASH_Jpeg/jpeg2.h b/examples/160 x 128/TFT_flash_jpg/jpeg2.h similarity index 100% rename from examples/160 x 128/TFT_FLASH_Jpeg/jpeg2.h rename to examples/160 x 128/TFT_flash_jpg/jpeg2.h diff --git a/examples/160 x 128/TFT_FLASH_Jpeg/jpeg3.h b/examples/160 x 128/TFT_flash_jpg/jpeg3.h similarity index 100% rename from examples/160 x 128/TFT_FLASH_Jpeg/jpeg3.h rename to examples/160 x 128/TFT_flash_jpg/jpeg3.h diff --git a/examples/160 x 128/TFT_FLASH_Jpeg/jpeg4.h b/examples/160 x 128/TFT_flash_jpg/jpeg4.h similarity index 100% rename from examples/160 x 128/TFT_FLASH_Jpeg/jpeg4.h rename to examples/160 x 128/TFT_flash_jpg/jpeg4.h diff --git a/examples/480 x 320/TFT_flash_jpg/TFT_flash_jpg.ino b/examples/480 x 320/TFT_flash_jpg/TFT_flash_jpg.ino index 39232a0..b3d7b96 100644 --- a/examples/480 x 320/TFT_flash_jpg/TFT_flash_jpg.ino +++ b/examples/480 x 320/TFT_flash_jpg/TFT_flash_jpg.ino @@ -146,12 +146,31 @@ void renderJPEG(int xpos, int ypos) { int mcu_x = JpegDec.MCUx * mcu_w + xpos; // Calculate coordinates of top left corner of current MCU int mcu_y = JpegDec.MCUy * mcu_h + ypos; - // check if the image block size needs to be changed for the right and bottom edges + // check if the image block size needs to be changed for the right edge if (mcu_x + mcu_w <= max_x) win_w = mcu_w; else win_w = min_w; + + // check if the image block size needs to be changed for the bottom edge if (mcu_y + mcu_h <= max_y) win_h = mcu_h; else win_h = min_h; + // copy pixels into a contiguous block + if (win_w != mcu_w) + { + uint16_t *cImg; + int p = 0; + cImg = pImg + win_w; + for (int h = 1; h < win_h; h++) + { + p += mcu_w; + for (int w = 0; w < win_w; w++) + { + *cImg = *(pImg + w + p); + cImg++; + } + } + } + // calculate how many pixels must be drawn uint32_t mcu_pixels = win_w * win_h; From f907ac31b7e02af1cbc56c4606b4398a76cd763f Mon Sep 17 00:00:00 2001 From: Bodmer Date: Mon, 16 Oct 2017 23:26:54 +0100 Subject: [PATCH 002/150] Tidy up code merge --- README.md | 5 ++--- TFT_eSPI.h | 4 ++-- User_Setup.h | 44 ++++++++++++++++++++++++++++---------------- 3 files changed, 32 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index d059391..4382b4b 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,11 @@ # TFT_eSPI -In addition to the original library, this library supports the XPT2046 touch screen controller, which is part of many RPi displays. The SPI is shared with the TFT and only an additional chip select line is needed. - - An Arduino IDE compatible graphics and fonts library for ESP8266 and ESP32 processors with a driver for ILI9341, ILI9163, ST7735 and S6D02A1 based TFT displays that support SPI. The library also supports TFT displays designed for the Raspberry Pi that are based on a ILI9486 driver chip with a 480 x 320 pixel screen. This display must be of the Waveshare design and use a 16 bit serial interface based on the 74HC04, 74HC4040 and 2 x 74HC4094 logic chips. A modification to these displays is possible (see mod image in Tools folder) to make many graphics functions much faster (e.g. 23ms to clear the screen, 1.2ms to draw a 72 pixel high numeral). +The XPT2046 touch screen controller is supported. The SPI bus for the touch controller is shared with the TFT and only an additional chip select line is needed. + The library supports SPI overlap so the TFT screen can share MOSI, MISO and SCLK pins with the program FLASH. The library contains proportional fonts, different sizes can be enabled/disabled at compile time to optimise the use of FLASH memory. The library has been tested with the NodeMCU (ESP8266 based) and an ESP32 demo board. diff --git a/TFT_eSPI.h b/TFT_eSPI.h index d3a37da..92d0401 100644 --- a/TFT_eSPI.h +++ b/TFT_eSPI.h @@ -113,8 +113,8 @@ #define T_CS_L // No macro allocated so it generates no code #define T_CS_H // No macro allocated so it generates no code #else - #define T_CS_L digitalWrite(TOUCH_CS, LOW) - #define T_CS_H digitalWrite(TOUCH_CS, HIGH) + #define T_CS_L digitalWrite(TOUCH_CS, LOW) + #define T_CS_H digitalWrite(TOUCH_CS, HIGH) #endif diff --git a/User_Setup.h b/User_Setup.h index 70f798a..41bdf10 100644 --- a/User_Setup.h +++ b/User_Setup.h @@ -14,11 +14,11 @@ // ################################################################################## // Only define one driver, the other ones must be commented out -//#define ILI9341_DRIVER +#define ILI9341_DRIVER //#define ST7735_DRIVER //#define ILI9163_DRIVER //#define S6D02A1_DRIVER -#define RPI_ILI9486_DRIVER // 20MHz maximum SPI +//#define RPI_ILI9486_DRIVER // 20MHz maximum SPI // For ST7735 ONLY, define the type of display, originally this was based on the // colour of the tab on the screen protector film but this is not always true, so try @@ -31,6 +31,7 @@ //#define ST7735_GREENTAB //#define ST7735_GREENTAB2 //#define ST7735_GREENTAB3 +//#define ST7735_GREENTAB128 // For 128 x 128 display //#define ST7735_REDTAB //#define ST7735_BLACKTAB @@ -52,7 +53,7 @@ // Display LED to NodeMCU pin VIN (or 5V, see below) // Display SCK to NodeMCU pin D5 // Display SDI/MOSI to NodeMCU pin D7 -// Display DC (or AO)to NodeMCU pin D3 +// Display DC (RS/AO)to NodeMCU pin D3 // Display RESET to NodeMCU pin D4 (or RST, see below) // Display CS to NodeMCU pin D8 (or GND, see below) // Display GND to NodeMCU pin GND (0V) @@ -60,6 +61,8 @@ // // The TFT RESET pin can be connected to the NodeMCU RST pin or 3.3V to free up a control pin // +// The DC (Data Command) pin may be labelled AO or RS (Register Select) +// // With some displays such as the ILI9341 the TFT CS pin can be connected to GND if no more // SPI deivces (e.g. an SD Card) are connected, in this case comment out the #define TFT_CS // line below so it is NOT defined. Other displays such at the ST7735 require the TFT CS pin @@ -73,26 +76,35 @@ // If 5V is not available at a pin you can use 3.3V but backlight brightness // will be lower. -// ###### EDIT THE PIN NUMBERS IN THE LINES FOLLOWING TO SUIT YOUR SETUP ###### +// ###### EDIT THE PIN NUMBERS IN THE LINES FOLLOWING TO SUIT YOUR ESP8266 SETUP ###### -// ModeMCU +// For NodeMCU - use pin numbers in the form PIN_Dx where Dx is the NodeMCU pin designation #define TFT_CS PIN_D8 // Chip select control pin D8 #define TFT_DC PIN_D3 // Data Command control pin -//#define TFT_RST PIN_D4 // Reset pin (could connect to NodeMCU RST, see next line) -#define TFT_RST -1 // Set TFT_RST to -1 if the display RESET is connected to NodeMCU RST or 3.3V -#define TOUCH_CS D4 // Chip select pin (T_CS) of touch screen +#define TFT_RST PIN_D4 // Reset pin (could connect to NodeMCU RST, see next line) +//#define TFT_RST -1 // Set TFT_RST to -1 if the display RESET is connected to NodeMCU RST or 3.3V + +//#define TOUCH_CS PIN_D1 // Chip select pin (T_CS) of touch screen //#define TFT_WR PIN_D2 // Write strobe for modified Raspberry Pi TFT only -// ESP32 Dev board (planned, not supported yet) -//#define TFT_CS 5 // Chip select control pin -//#define TFT_DC 2 // Data Command control pin -//#define TFT_RST 4 // Reset pin (could connect to Arduino RESET pin) -//#define TFT_RST -1 // Set TFT_RST to -1 if display RESET is connected to ESP32 board RST +// ###### EDIT THE PIN NUMBERS IN THE LINES FOLLOWING TO SUIT YOUR ESP32 SETUP ###### + +// For ESP32 Dev board (only tested with ILI9341 display) +// The hardware SPI can be mapped to any pins + +//#define TFT_MISO 19 +//#define TFT_MOSI 23 +//#define TFT_SCLK 18 +//#define TFT_CS 15 // Chip select control pin +//#define TFT_DC 2 // Data Command control pin +//#define TFT_RST 4 // Reset pin (could connect to RST pin) + +//#define TFT_WR 22 // Write strobe for modified Raspberry Pi TFT only // ################################################################################## // -// Section 2. Define the way the DC and/or CS lines are driven +// Section 2. Define the way the DC and/or CS lines are driven (ESP8266 only) // // ################################################################################## @@ -144,9 +156,9 @@ // #define SPI_FREQUENCY 1000000 // #define SPI_FREQUENCY 5000000 - #define SPI_FREQUENCY 10000000 +// #define SPI_FREQUENCY 10000000 // #define SPI_FREQUENCY 20000000 -// #define SPI_FREQUENCY 27000000 // Actually sets it to 26.67MHz = 80/3 + #define SPI_FREQUENCY 27000000 // Actually sets it to 26.67MHz = 80/3 // #define SPI_FREQUENCY 40000000 // Maximum to use SPIFFS // #define SPI_FREQUENCY 80000000 From 0bcf7eaa6b0f87ec2e826410eccc9ceb9a7d6e16 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Tue, 17 Oct 2017 00:07:49 +0100 Subject: [PATCH 003/150] Correct jpeg decoder image edge rendering Revert setup file and add setup 10 --- User_Setup.h | 5 ++++- User_Setup_Select.h | 1 + .../160 x 128/TFT_flash_jpg/TFT_flash_jpg.ino | 22 +------------------ .../480 x 320/TFT_flash_jpg/TFT_flash_jpg.ino | 3 --- 4 files changed, 6 insertions(+), 25 deletions(-) diff --git a/User_Setup.h b/User_Setup.h index 41bdf10..62e6ff0 100644 --- a/User_Setup.h +++ b/User_Setup.h @@ -99,6 +99,9 @@ //#define TFT_CS 15 // Chip select control pin //#define TFT_DC 2 // Data Command control pin //#define TFT_RST 4 // Reset pin (could connect to RST pin) +//#define TFT_RST -1 // Set TFT_RST to -1 if display RESET is connected to ESP32 board RST + +//#define TOUCH_CS 21 // Chip select pin (T_CS) of touch screen //#define TFT_WR 22 // Write strobe for modified Raspberry Pi TFT only @@ -162,7 +165,7 @@ // #define SPI_FREQUENCY 40000000 // Maximum to use SPIFFS // #define SPI_FREQUENCY 80000000 -#define SPI_TOUCH_FREQUENCY 2500000 +// #define SPI_TOUCH_FREQUENCY 2500000 // Comment out the following #define if "SPI Transactions" do not need to be diff --git a/User_Setup_Select.h b/User_Setup_Select.h index 3d8c600..8129612 100644 --- a/User_Setup_Select.h +++ b/User_Setup_Select.h @@ -27,6 +27,7 @@ //#include // Setup file configured for my ST7735 128x128 display //#include // Setup file configured for my ILI9163 128x128 display //#include // Setup file configured for my ST7735 +//#include // Setup file configured for my stock RPi TFT with touch //#include // Setup file template for copying/editting diff --git a/examples/160 x 128/TFT_flash_jpg/TFT_flash_jpg.ino b/examples/160 x 128/TFT_flash_jpg/TFT_flash_jpg.ino index 4da9af4..7d0f662 100644 --- a/examples/160 x 128/TFT_flash_jpg/TFT_flash_jpg.ino +++ b/examples/160 x 128/TFT_flash_jpg/TFT_flash_jpg.ino @@ -130,32 +130,12 @@ void renderJPEG(int xpos, int ypos) { int mcu_x = JpegDec.MCUx * mcu_w + xpos; // Calculate coordinates of top left corner of current MCU int mcu_y = JpegDec.MCUy * mcu_h + ypos; - - // check if the image block size needs to be changed for the right edge + // check if the image block size needs to be changed for the right and bottom edges if (mcu_x + mcu_w <= max_x) win_w = mcu_w; else win_w = min_w; - - // check if the image block size needs to be changed for the bottom edge if (mcu_y + mcu_h <= max_y) win_h = mcu_h; else win_h = min_h; - // copy pixels into a contiguous block - if (win_w != mcu_w) - { - uint16_t *cImg; - int p = 0; - cImg = pImg + win_w; - for (int h = 1; h < win_h; h++) - { - p += mcu_w; - for (int w = 0; w < win_w; w++) - { - *cImg = *(pImg + w + p); - cImg++; - } - } - } - // draw image MCU block only if it will fit on the screen if (( mcu_x + win_w ) <= tft.width() && ( mcu_y + win_h ) <= tft.height()) { diff --git a/examples/480 x 320/TFT_flash_jpg/TFT_flash_jpg.ino b/examples/480 x 320/TFT_flash_jpg/TFT_flash_jpg.ino index b3d7b96..5af0f13 100644 --- a/examples/480 x 320/TFT_flash_jpg/TFT_flash_jpg.ino +++ b/examples/480 x 320/TFT_flash_jpg/TFT_flash_jpg.ino @@ -23,9 +23,6 @@ TFT_eSPI tft = TFT_eSPI(); // JPEG decoder library #include -// Chip Select Pin for SD card -#define SD_CS 53 - // Return the minimum of two values a and b #define minimum(a,b) (((a) < (b)) ? (a) : (b)) From 167dcd53739fe3e0ceb03c1edd7f99e7413f5a7a Mon Sep 17 00:00:00 2001 From: Bodmer Date: Tue, 17 Oct 2017 00:11:13 +0100 Subject: [PATCH 004/150] Revert 160 x 128 jpeg code --- .../160 x 128/TFT_flash_jpg/TFT_flash_jpg.ino | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/examples/160 x 128/TFT_flash_jpg/TFT_flash_jpg.ino b/examples/160 x 128/TFT_flash_jpg/TFT_flash_jpg.ino index 7d0f662..7179945 100644 --- a/examples/160 x 128/TFT_flash_jpg/TFT_flash_jpg.ino +++ b/examples/160 x 128/TFT_flash_jpg/TFT_flash_jpg.ino @@ -130,12 +130,31 @@ void renderJPEG(int xpos, int ypos) { int mcu_x = JpegDec.MCUx * mcu_w + xpos; // Calculate coordinates of top left corner of current MCU int mcu_y = JpegDec.MCUy * mcu_h + ypos; - // check if the image block size needs to be changed for the right and bottom edges + // check if the image block size needs to be changed for the right edge if (mcu_x + mcu_w <= max_x) win_w = mcu_w; else win_w = min_w; + + // check if the image block size needs to be changed for the bottom edge if (mcu_y + mcu_h <= max_y) win_h = mcu_h; else win_h = min_h; + // copy pixels into a contiguous block + if (win_w != mcu_w) + { + uint16_t *cImg; + int p = 0; + cImg = pImg + win_w; + for (int h = 1; h < win_h; h++) + { + p += mcu_w; + for (int w = 0; w < win_w; w++) + { + *cImg = *(pImg + w + p); + cImg++; + } + } + } + // draw image MCU block only if it will fit on the screen if (( mcu_x + win_w ) <= tft.width() && ( mcu_y + win_h ) <= tft.height()) { From 5591c6bae0cd6ff84347f3b1ed06a9e308ae8572 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Fri, 20 Oct 2017 18:05:21 +0100 Subject: [PATCH 005/150] Change to always use mutex handling with ESP32 Grabbing the mutex for exclusive SPI access by the TFT library causes a crash when OTA updates are done. Always switching the hal mutex is only marginally slower so that is now the default. --- TFT_eSPI.cpp | 66 +++++++++++++++++++++++++--------------------------- 1 file changed, 32 insertions(+), 34 deletions(-) diff --git a/TFT_eSPI.cpp b/TFT_eSPI.cpp index a335193..8fc7fc9 100644 --- a/TFT_eSPI.cpp +++ b/TFT_eSPI.cpp @@ -21,11 +21,13 @@ #include -//#include -//#include "pins_arduino.h" -//#include "wiring_private.h" #include + // SUPPORT_TRANSACTIONS is manadatory for ESP32 so the hal mutex is toggled +#if defined (ESP32) && !defined (SUPPORT_TRANSACTIONS) + #define SUPPORT_TRANSACTIONS +#endif + // If it is a 16bit serial display we must transfer 16 bits every time #ifdef RPI_ILI9486_DRIVER #define SEND_16_BITS @@ -191,18 +193,12 @@ void TFT_eSPI::init(void) inTransaction = false; locked = true; -#ifndef SUPPORT_TRANSACTIONS - + // SUPPORT_TRANSACTIONS is manadatory for ESP32 so the hal mutex is toggled + // so the code here is for ESP8266 only +#if !defined (SUPPORT_TRANSACTIONS) && defined (ESP8266) SPI.setBitOrder(MSBFIRST); SPI.setDataMode(SPI_MODE0); SPI.setFrequency(SPI_FREQUENCY); - - #ifdef ESP32 // Unlock the SPI hal mutex and set the lock management flags - SPI.beginTransaction(SPISettings(SPI_FREQUENCY, MSBFIRST, SPI_MODE0)); - inTransaction = true; // Flag to stop intermediate spi_end calls - locked = false; // Flag to stop repeat beginTransaction calls - #endif - #endif // Set to output once again in case D6 (MISO) is used for CS @@ -3647,13 +3643,15 @@ void spiWriteBlock(uint16_t color, uint32_t repeat) } #endif - +// The following touch screen support code added by maxpautsch 1/10/17 +// https://github.com/maxpautsch +// Define TOUCH_CS is the user setup file to enable this code +// An example is provided is 480x320 folder +#ifdef TOUCH_CS /*************************************************************************************** ** Function name: getTouchRaw ** Description: read raw position of touchpad if pressed. Return false if not pressed. ***************************************************************************************/ -#ifdef TOUCH_CS - uint8_t TFT_eSPI::getTouchRaw(uint16_t *x, uint16_t *y){ uint16_t tmp; CS_H; @@ -3733,9 +3731,9 @@ uint8_t TFT_eSPI::getTouch(uint16_t *x, uint16_t *y){ /*************************************************************************************** ** Function name: calibrateTouch -** Description: generates calibration data for touchscreen. +** Description: generates calibration parameters for touchscreen. ***************************************************************************************/ -uint8_t TFT_eSPI::calibrateTouch(uint16_t *data, uint32_t color_bg, uint32_t color_fg, uint8_t size){ +uint8_t TFT_eSPI::calibrateTouch(uint16_t *parameters, uint32_t color_bg, uint32_t color_fg, uint8_t size){ int16_t values[] = {0,0,0,0,0,0,0,0}; uint16_t x_tmp, y_tmp; @@ -3823,33 +3821,33 @@ uint8_t TFT_eSPI::calibrateTouch(uint16_t *data, uint32_t color_bg, uint32_t col touchCalibration_x1 -= touchCalibration_x0; touchCalibration_y1 -= touchCalibration_y0; - // export data, if pointer valid - if(data != NULL){ - data[0] = touchCalibration_x0; - data[1] = touchCalibration_x1; - data[2] = touchCalibration_y0; - data[3] = touchCalibration_y1; - data[4] = touchCalibration_rotate | (touchCalibration_invert_x <<1) | (touchCalibration_invert_y <<2); + // export parameters, if pointer valid + if(parameters != NULL){ + parameters[0] = touchCalibration_x0; + parameters[1] = touchCalibration_x1; + parameters[2] = touchCalibration_y0; + parameters[3] = touchCalibration_y1; + parameters[4] = touchCalibration_rotate | (touchCalibration_invert_x <<1) | (touchCalibration_invert_y <<2); } } /*************************************************************************************** ** Function name: setTouch -** Description: imports calibration data for touchscreen. +** Description: imports calibration parameters for touchscreen. ***************************************************************************************/ -void TFT_eSPI::setTouch(uint16_t *data){ - touchCalibration_x0 = data[0]; - touchCalibration_x1 = data[1]; - touchCalibration_y0 = data[2]; - touchCalibration_y1 = data[3]; +void TFT_eSPI::setTouch(uint16_t *parameters){ + touchCalibration_x0 = parameters[0]; + touchCalibration_x1 = parameters[1]; + touchCalibration_y0 = parameters[2]; + touchCalibration_y1 = parameters[3]; - touchCalibration_rotate = data[4] & 0x01; - touchCalibration_invert_x = data[4] & 0x02; - touchCalibration_invert_y = data[4] & 0x04; + touchCalibration_rotate = parameters[4] & 0x01; + touchCalibration_invert_x = parameters[4] & 0x02; + touchCalibration_invert_y = parameters[4] & 0x04; } -#endif +#endif // TOUCH_CS /*************************************************** The majority of code in this file is "FunWare", the only condition of use of From d484a5657464f0222b3e5b00fb05573a5cfb00f8 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Fri, 20 Oct 2017 22:13:47 +0100 Subject: [PATCH 006/150] Update RPi screen connection image Identify touch controller chip select and required MISO line. --- Tools/RPi_TFT_Connections.png | Bin 393832 -> 390583 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/Tools/RPi_TFT_Connections.png b/Tools/RPi_TFT_Connections.png index 96bad1ebe5fde6285f1003b419c9a37c75f62f04..4e82b2cdfca63e6ceaa28f23c18fa57f13deb7ff 100644 GIT binary patch literal 390583 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%0A>u2A9Nf%HVod|+-6(G_~N~Os1&|MO^ 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 Xk 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

3S6MED@atOs zPY@oo6u{4+Iikr~b{bsWB8Zh)z^06XIQd0NW&x-1^29c9gcT450fbH3BxB+NE4K*1 z0}yi1ESm*56y%nK`DLj2StOK*Nd$p;^K@_`2*!*B@oJ@-Cz*WC~7-FH}{kh^SlAz!q4 z)3*EWeF&%tj_~>$Zn^IIYwmsUft&BXcine?@ZBqKdSTbeh5=D0%T!A;N^H4kamE+V zd1GmNBqO&5Bql%C;I}xvBA$NJfUbS$O~b@Fy*H*-n2&U;YP{q0`42nzEeUH1zap(qcY#}P}Sk%2M-+CwG%l) zfZyT6hihwVtE;OafKp#y4^Z5@=irHxbuVpv{krR}TmRGl`oXu>{pq8NYoGo}s?c_| z)G+%R);|05+TZ`44u!{uhv2?-{=JWDyC`^(nF;>{dOuI+1xF~h#F*v~L+@o9Jxq;@ zZwkomF^R_h!OwsD-Y-6womm+z2LJfc{MtYMF;NPB`pKK{&yF9UJooHNm?H!W1^fUV zK6DJNpg{XEAuk0KL8>HRJ8-CO_m(|7cJ17>v+DNSAH4ReE3f+Qx0%%&e|vt?VN`D0 z^vdq%AMi^DY*M0D%v7?-?d|QUKyZA1(U2(N^@-lx!l~?RC)Xm(* z<0=VV1&%8M2A6LNb4_8MIRdr78k5))LQ9luOYs#>m)oyY+xH#ndH(T7x{p7{?5S!! zv`Nu>Xg+07@pzkF+PH7uwjFzR@7RmnEi$<1!&mm^-Co&PW8Z;o+xKkVvJ*fE4<&o{ z9@@Nl_iLN?Jn_t{SKhSYs_XB(;fFu!-uX;pYx|A2+wox@@BQqWpFaHf z3r{@tdVPJDTxpf-JxY5(ZE>>AegL^t?WTuH&FT3=A{UM*)aY#_tDEA_GfK0##5mbA zN)3+phf4U!AUqvz2`}7ukkZ-KOH#VK19{Te*@xKiorh7^Xum1MPEC_ylf*FK5M|az z`PHK1gzzXnGy*jmFAR?KM^H~GHkAer(MuH&{wx15FNmQLA1XhrO|Pq0efQbg?Ts|4 zIiMDB`g(B^fzWCfCF0S)|Md$HUhW$GD$8XMeq9R!;o9PbHwMhvW3phCy-Y7G0QtZU zVl0H1U11?6PGOmxUhLQT1yVnZm8I(=t zODzd$(y-+CD>&$hv&2ZYi_dRs$ zZFk*t^R3sd-|+Cg_ujaE{knDEUiV)&U;pslhh9DP$i}u!C)lkFmo2pvDlR*tWk)FE zisylOuGp~Mmoxh!9ENe*fEhP=(LOk@j}{aX_pW|TU;Z2^IKkINrFzHS#-`VgoVx#s z*RHzx@yA{|c;C}oZ@lYCoiR`SgF!iZ1f^Jm0O$zk z9N1q4EpX=6ZM$E5Y2(c|-+av#Kl#ZIzHhVH{`>ErN;ST{`**dp)=n?JxqR^tu3++1 zTL%tD52nhU6d^%up=JCmSa>c*@_pjIf@z--_&c87pUHjYVwY9(f?)~5V@xt)h zCx4zCe{et2fwku7nYakeeV z(#7z;Ty=72JJWisk-^vM*fu{tk|E~ix}wv)-V!4^-W4bl{6$=#K#5OojV#<)MeXhG zr)j;NzU;v8(!-p@uBsjYAvRg2KRANAM!^Dl0BL9x5R4!;K!}Y6f+IxZf_?-)XvCZ% zoQ%yB1!1NTbB7>ec>Hm7dR?t@-6O|dJU$>ZyC?GD!O;<^SYft^V$twF{sw>dXy=(q zPL0duD=L>k_;oFxp{(0WZ@k%W%2bJchixTV5t%kxYzpm$lbQo}h^;Av8U?A@f!GW< zLaxN#s*Be7$~c*u!8bPRQUF4FdI19=yTVagcAlP|1<_Jyi8LIN)AQu)0;9Crt#NVn z-asJba=3Tw+PY`oX0Ju6qj!-{Rr!@{1+nL;JMMV$KD2z|u6rN4@8QSpzVG2XE1jj_ z1%ZxG0}F{Hy2I%eiey%s`}m1E@Q9B-_S9{+-+9B0H(z(vhJ7zS)_35!O%LC2-+lM| z@S0ohyZfR0)?az^ZTCO<;`XZgeu2X6@Z}ULr@<7AB`3YUvMD&I^A-%gT&6VpnJoU=<=XM@@?8QAlyYsFe-*wLqZhiLKH@tYu%ljVPv}5<~BV7ch zKVjalIK~*=5TP3b^<@6DuWh{c&WEnP`_U)1 zA9;LJ?MpjOxI>BY$+Oib8_?#Cl`WR4s*VCGAOJ5|U0r+l@DYrEh#7#6J$v@=N8i!T zO&fRadi|vx2d=;Kp{s7V>3iS)7PDpB$AA7^c5JD>p|$qF#(cs$w0Jf+yh7tks+w?W zwbtPc5VfIZNf2!|B6moPA+9;VHATVnbQe~S8xqwLXN|;rT$ng24jz?;jw&K2)N#;p zdE~e>T&IlhSI7F@2}f>1q04dAF@G%c_L(<6{Pm~9V`qN%!6f|p=BI!8%!>!M?%KO? z&*uF*j{pe44#Lw$rFZu*fN=BcXr?Iqj_usN4M4ta+ZHr?bla{?TXsJB)C=pcd*q5A z-~7KtUKdpf9Q+N`-48pIhTn6FSwNz$}F28xP-4pYOE``?ub*=wKw{@n5~v8Um=NA6&g+J67RYB8u{^*5?HRI`Zv*4$V= z72z4J+;EQP%qT1YgCnZ+CYlI#1jh z49YFu z^w@u0fB(0Bvf-xto_pY_J$K!5+m`Kna71HVGJXK&IJk&Sx-Vs@} zo24MqRNWL!H(Q9KYkR2bZl<=4s_tayy4dDcD#Y4;va*%sIN7Oq{>7cQ-+SlJZocKp zyPo{7yPv)0kr#0kVs3Dd$W^|)aR*Qwjr?sxBZBaaz)$$VLG%+wODcB4v&rVIyWyMM ze01mG!&{H;*|uxX-u*jvAA0DqM}KPf`|Brv?>O!yWJkZ{>dy$OJw( zIuIHqM8?7A^@j!t>9O7M^LJO#@Vx^pO=Q5EC5~OZj~?4w)xwbK`ZWPG;O9qcC-A;e zq7SX9$9gz%KGf|AG0Mapt?WkucF;Ej))0ZvUF>z_F>45Mw=D~YXf)A3*zGR#7=nk~ zW8Z7Ct$U{afg^oXo_J}{b>{7l6b7%uB+JCZfBWq3*usf_e9=LAxqKDnG6=t}_SfL$_z5dTxfRcdw;K**BXPnnW!Q3OIF!_=rxF~=|PvuO7T41|o# zJUR8nmjg<<=>>qRxO_%B_6EOvnyd*Bgyv9s{N11bDLpvliIw8z1y5uoTN?fR^R-kY za`(+QK79WJ4^_qm@45HE2k(ERGJWQ;d+&Sb?mO=SS9s4|_uqftV~;%X^2?jH-+$l3 zx8HilLytZA=p#?vaogS3Zn*AS>((X1;kD2IF)>_dtvmX{lMgC@i+}&q#fvKvnfUGZ zKZ2)Vc%#3rt(7i(g!dGC$Ihr%f3Av;=xqsuH*5E%NdwYjEGK*BJ$-)B>&fsnmKvcH zuW_j?0beE?EUXxU6AfhZNrJf=Z)_uLXgu{RuWsJ-+O|sw#3r6VjdjyuhkN&+)Mb13 z9XfcZ3P5<|$l?0Nh8JFZ^}1_sy!M*wuD*KxuKkDaf8zOXefz509@@OKhSofw#?cg9 zrPUNF3T;V_(#cYL!SnIV37XO+Fa@OcsK^@On}TekpQR7b)E=7J$2WzArij20P#I!M z`?%DYB%M=uoNd>JgNYiev2EKn8Z=2`+vdb*)Yxil+cp~8wvB(@|J$35XJh7>?2X4aTj?Tl?_I9}LL9I9d1?U!+ zqcOG_&4hu&aXGralS)9B$(W@aJ1WN|-#hQE=bKo2ug{f?Cc7@Pv!=J0CHo6=mMm1m zjsZCYSvK^#{Q_eCKB$EKNu=Pq$rrYVG2)Oy(ufPS))c0P_(GYsiOEI4hzeKu-+NS3 zyYxP(WUo?EHuW>|5???Z;-yqrK3tkyhKKe@o3*o3+5Vp~{RL53{+(rLCKK zL=Ao5ATpsZ=y*T6Zrm-$l-spkwXI!#F1j{#IqrP!RRjp~ z%N(9L4S0u5ZAc(p@T^ZHp1d^#d=f7Y|6tG61Sc7*yI?{Fz=(X|zn2$ilc~3XjJ$e; z8j3d6gTZQNBG*Y$B}7e4p=$n|PyT!^(24c`_?8c|Cx5vb36D{_$y_SSY}u> zMfe%64P1v&|HpqtTuO^!Dv*}4g!jL z0CDiJX3W6m-$)La;nx94upJrGU{;s(q*RzYsnNfcv?Dh(vPQUF_~4kQ&+HuoQ;fIY z=>G0Fr>}>PU>edzWqsUZab0vBCckyvCE$JS{pPFkdrqR#7iw`eUzk(sc)x~J+H#&Y zeiC@!zj#^)E_KGP`)wIFZ@e96fzr^Wji*ib=}z}`$0Y)HA%{H%_btC+U3XC6`|%{? zaRnK>`EaeewBdFY-Mn$vpQE#|zAD7y%ZV~Zz@z1TN8nB*xJaCs8kB9M87y7Ft_Q_RRY8YtQDs%`7DYyxkY^`o3Dy5G7f!e^rONnK1%GH5#2u{2ty z9w>hz|2Vd%t0bQ?vicoWh!wh* z-5YOj*I!v$5pSjqQY|@JME$w42VwoY44MoZD^<)$PTVH)n++-$$uzzQE3`>W>QKU> zx2l!58am@y!ktv<=h83n_KX#PrMTrc2vI#h&+$Als;3LSHdA=){`@eKO>aPhyAtL= zyIY{AfJ;Hy;da2DdRRGV17?HmTlU5qQg>79>0vuzi&Z~|(ED`G2b=|mEv2#egjG)$)6Cy2_TK|)Gwi*!Iw^08N^O7j>TjXw0Gp2*=*lp*{|$Q z`FtuO1~EB61AOmeXZ~%KZ*Ert9u`ZNo$XKOke}eI?lxQ9wr86R8UOIpAo-AjG}c?J z$Kf-Y?N++%=MFX8iIyRQ+fP>UIBi-ULy11_A7QTE4`02%E`VRpO?*TxXP;6;zUQUp z*_*e!@OajEtJM6M)7yp?Vji`z#mwIx7^`@Mo1u>%VVTr+&CI;}(1k^!d<$y1wgJwTs5#dx}na1N*r_rW*@RzBBK{iHCfux@8^fqh_ zx{XWlZ8qP1>*3W|spI-u_elpRp}W8+h~5mGhIdefu5X0LUw9N)b3YFOGvycI!y$mU zf+7wULkL#BS_3|h?Bu^8<9nX2Xn=YeP1aQ{PW_ldJRR0&|9so^y*_T%?7F=_)VVhO z)}pyU@xh1E)v9xcMjl6En7K`WywKJJE0tC#wOpi^ct{b-G}>+|giB>c-d%Wwp(+Rm z{I2#0rkilN=bghR`nAbW{q-DUE7bO|VqMi;+E`zGIx@@|;_ z)8`c}x5cPwEkd7cQOI}<4B4o4f2$H$+qr@8Yr0k-;CH|v6o2gR9*o)GYrf*Hd3xT| zmD1a4xOp)r+-Ucl?%e?G23~!dL9ey}y=d1nu65Ug*WsMDulA*NkGn~(AUMIFOPi|< zyvl6CyQQL|9V-OJW0@|kSrpCnA&OnqJvf=h7-r;yre(n3W1@WM;)or`XGE$HTG*&4 z#9+yrO_FqETV5r_CDa~V>Y6jxU)dJ3vfY#k} zo?fkJGb@g!#}yW7(nbYlAldudQIb|ZsKm68JWl-HmMpt;Jd0-!0o#j8=!mQefA>BH zOGN4Ag@gjDQ&4E8)hcm0110HJq;!ErE1I5wu{;uyRqNXWw zviO^>V;zl%qEt)#Lf`C0(wpaCstjgJEtKlzHMI{lMeChEI;pyG2g)g3ToFA2*I{Ep zH;Vxhk^qvMF-Pp8)Skb#`fy}3eeX@%3i*!51KY!lk`pWe%MPdPmiWgTRhh3*KK6(B?G(1NEH8|+o078Xr zz%~&xq|Z(sB#b*qIbRnAxHi0&UahNEulIe)QOh!D3#I}c6)gx3FnqM_|m5Cci4ULB}2i5`(7@K5<{X zd3`d8cQtDCaRfueFt0B9VTj%Io}TA-6fs4t+r7j%gHt5+xP=>Pp`KhG$HVzKDHz(Z zG*b>Hf<6kClAIjf?T-5BOr47jl-1-l)cAz<-`z~3-Aum&$jS5e^jdg$px)ba_gBa5 zA|ICD%bK&$*Takc`yp0N1?F@oU2KXEy7|`YTn_LfV~Zin+y3w)M=3|&_iPsr^xzdo z#6BF_8F#=ES-o_>mT6hZXq1IB+v;owD_7-&lB^2Z?0pIG&+mg$!L>hr8jYM!^V;l` zx!DDItJGarF=A_E;L}PeTSvGmPr$sy1%=Y<5uryAs09>e7-O!4Z4@?l6yEvdKe+?L z*2c`G9?yMG4ivPTFQ9M1@BG7av)Ui~cbYERMKJk1K$Q-c;!uO5?4U5D1$=IuyT0*^ z>J5jD&9*ZXtM<~ z=Jmhy{?fFzz8Vs4@%|#|!MAwJ{?K;oE8c+783;y24Ft*R<+_{0*QPHF@`cw$BSG_~ z*Hx(XqW(+Pl{74j(94CkVc5aQ<&x65p9?!_W_W58MCcrK#^@{X^DvET^Huq9%0bNG zZ3;yPE|h81r4U5e2a^0w3F8}Fx#&DqAb3CHbh{|SF*qUfcgrPK-y@^dVQ-Ahho`Ch z(MZA1uQoy@%Ff^EHhc)y>^VQabmzqudZ}Mw#=*KmH{I#-yiL;3W2`?pB>;NILS0Wi zEFP=NUB*k1Ox*@eC&fpS`YRsyRd1I#$VoUM*O33Rs$=p*($49T>bs(5%Nv%v?4-Y%dyax0L3I6MkFRHpwVG#|~ zvvgHp#L6zjMa%{X8*BZyU}tWgyUzD{o}4X45+)$GVt_uMwF{Od7XzPUz@-I5sAZ=8 zEi7&k8?VTt7#gAVzDMBb?vb9pb-F97tuHI(!b#)VmrYQu7yw~&1+>+g#ak>0ISUwA zjR&T`JTNAuCbO$k*j8AW!!Y!Xz%q&y|%{Avt6AX3iXla zv``ZWFcI<4F;-JkF`XW69U)cK{gzw*$F@6BF@^4G*|DJeJE4|lC9V0ODtF*}+?uhh zSojdzvhE@yRtH> z!?fRP0`TilTKe^6^%rHjK%1?$dw&xM4>z>EV5(<})Qde!m=^qaTm9wYBJn5lRAIs; zFE6Tq6Y?)m9v^Q@>7n{^%I|VqH-`8cn`_iJ{7naBI^B@%w)8Uff%lPq#H&b_;_tIw;-&5Yi_H{tB z%~tcxLsmv}o@bttz}M}+(>7Jz&gssYz0wqYkNd$R{T25HJFsd8ZS9)^zANvW)ie8- zlh`V+SC}n9EUK@)Y9fb$v6LL|-}+DYMAK&GQr}ohXG#~GX}A-HXQh@1%S(y2@ot*9 zSvs$OdtaBXuREiC(M6l+FiTHOxqAHewZPnVsJ2JZTt1_DB8&V1-)HFgCffIkSiP{*QXm$Uc(-nCSGDztY2 zD4f>$SvNH4Lo!F{N~gO8we?XLzr>Ma{$ymsSfS;kPdp>^5sr!QBC0`JgU)MQdZuv^ z*1!Gqc~XFd)Yd>CahLSS6kGjWUmvLu8x>8KA*DIsKtj?f7#}+?gi<2IoR)J~T7(MtI4QM2i!PxHW88GkZAiS2WiN^GyV;NaG$r8z9;|MsI zs#%O<%cz$xMU~now6*cRKZxd9|5|?KBYJtdss>(1q!9X?m*%|RpCUTgf2?q5=y+bZ z;@NfG6s_5}y&%niKDU{DJUDo)T|5c0)_s;Hn>P@jc0t*-%frGMmyY+w ztF>L*;cJ*(%h^F#Ht?a_uETf(X#1@H^%iRH^Y**kuG{)94&B!GoUkmFv6dMFsi%H3 zLxrbL)?H! zg<2T=o=}KF-&+3Uhc{?jM=tyC(9|+d8G1v@$6ZIEt8u`Bpj@ z`DH5IVNIF{ldG~V`NcU1j@n{bb=B$TP?K%x)Z3G)uGA&Z_;89-Pcz)N3B4bP)s4*0 z-VFfY!6t>VWk}2oB=L-*aoFm4*c<0p6eaUcQgzIkxEYPSv)!);_f*zjGhW{bJx#A- z`n{Xa*MSC4y|#Q$wi~rr)UaFs?wGEYTOO``V>?`yAg4R;Z_AlFn>_cL_K<~MUYjkFeaZQGAVm#SO6`hlOvVbeNK8;wusIlhl@N*CT22Psim)fK)mkF1NW(I+OCDc!Fd z?ZD6dLS2H4=5FDAE1G2H`kLnkx-L(b$JOEH&6>-0Bb*;y&wlzY2*T0`F-QPOXwf8& z+ihRSpu~xRn3>ZH80pRp?z;7@J%#3j6#L${M|?|>80EdI*r?s0y%q;J2UupYGtv%< z>p;k)mAq7}0gEuSXbAo8OyWCWG%Bh3A2@spuUqZ=R{NjUO9IZjJ3+em5$_Ww76%*p z#>T;|CJlYvAW1Fzk4>r^KhLng&tT;^UB1}{oMI?U5bLOO?wR_g=|^Yi?jNOuh?$SJ zt`Me$Fw}71sb2hJX&O^HNVkfnd0d7v`+m_fFqYSOvNFme`(2Co{2OfHjinjI1Fp_D z+#f`ihEOx%yg%5&<7WD%|5n)S?v}YYsH^CKlYYB}|GOK{DgX#&<#l~^C;HmG(0_lg zeA@Cm0yXx~k52y4Xp`RT;EiK#c}=I{pXQDNWHqQX6lABRC8r|b>FVC&D>`WD83R08 zB_!=It7xVtEkb9WDXKjJ60{8|ijUJA?fld^KNyRd^WcZn(dPcyiD*AYJ@>7tXir#2 zu0YC!3E9`1M^!b<xk0}3^kR@QdjV0-7r$=O;}ZRoi> zsJ`87Fbumd5cyP9S0D65gr+pv3_ajqRc+Oq@6~%Z3$%Oh8Ea>Ef9>}!DdKzij?*b! zthIVRJ=(rKf(Em?zg?af{6iwJwK*YoB$o^lsB$Og!9~`;Qe72;)?ddC*AA5NCbvdD zx`;ynOlcAV!X)ig4B=oK6+_4v8$(M}=5~fIKR7C@?f9OL6RJ^0-`k8`xpeAxlJV?x zfCrew2G^n1)!jF*8Fn2vhkW)OM@^t9ZHKi=9SP`3JmWU+&7)Z zZMila_8#!6I`?Au7WLgPK*-lePeO*C$H#X6bXV8sA`H-JBTJ~|aq+>O|9PN%%{}X! z|DT9032fU5EyR{M+OKPVw(yC16pt9eDrXIJYbmn!5L1DFOdA{0Cs=8t->B3Nil_xr zrJazqb_z=&)mqz=NH_dCK1wHH2t6Ffqo0Isa+W~$j~~c{yjtaKeAe1+_APgL!+Oi_ z$9LL2?TFWo5Hf!-j~@j2>sp< ziuKnYmh1UIcjnQg#`{vGkY+KeGNs0pyOSEIdePY4D*4w*S@T=K#QaRCx9NBlStBCi zMHW>$?i*mtPb@WAv*a!@y$>#ZNlcxNflaJ#=gz-JPXiU77r?3r z-!Fn4DQHx1WcsHR-TSNhWUl+AUuf~O zuBnKDiB9(JdjIn>4A^pg*4X@YO_kLzfwGLZ@>A3*9r=Bn0dCHDw=W)y-pnroUTta$ zzOH_CM_t`h=7*>^qbMI6kHkb&ePl5x&EDOq2hCeAWEhR=wma@_&X)P!B8G6#&a{}c zm8>T2l)J%hp0k9|<=vRxe@0E^us_Ea3A3Uwc|oN0jwT_0K5rFowYAiFnLX@E ziJ1#^Jnw|C0iUI+$Mn|ZXq6Z{zKVqyb@_p2C+jC{N8eLYHs7{C4i+%O<}RGxjBn(r z6IIg0ereBdkETLO$T`elW2>~@k*cbgjg?$@)&oZ{37m%|1#VdEjb^bjU<;|pR!%hS z7~|96$SHO5P&2A+ZZJZMu(wd+RucHLVymCVCoQPR^7``PBhJcV33*y7U$2{r(D@#1 zd|bY)WAQg$=c`gcUNF15@}kyK=5nr+|FReN)Yo!0K5sjj1e~YorZ=AE`?-W3dKjt& z-)7*I1Y2GfuyVX_Yl}HAH+(A^%ktcTuEV{rmwUz6lOOaDN|VZ7T2?~3J0x=W-eqwln-ce2s^wb)f+V-6`WY7>E zNB+T|MKogi>17zM^xyGtH{RR0ki1;+jyv8tF)mhxGRpiBo+vj05_&>DSRWtq53e>a z@L=0ZHCrn)-*(TfIL;#Zt@7I-$MXk-2LL=|p5w&(!|PaQFNv}~MGyk#Eli+BfSUvC zsoTD={{$n}sls3j>;=K2PCJl9?%2=fr4p)x6 z9t26>Hb_p@|7+GPn!Zn*fIB}HusY2P4KY+1hHA=?GAsMFn0sYZqq3?1&i^)81i1FSn>=6-&VxTSf&nSUd z(g+O^rKm?r5!G0(;TesEW5e<~M1`#TYc1XOmi>Kl`ty(}0ls(*CQ%C7MULp>_I(X& zt#r&Pxm-w4L@8h1zG8%%FMu&nH!UZfSzf0`vD=i8&pfAtShlYmeSSdA-KT*_nLrx@ z5*}V|b-raY>(I$%?e(H%o%ZNlYGB9o@LJ351>f2tgtxATFx~iju~s3@)HZ%Qvw}jj z${g9mIX4|wuXM~4beQ*?b7Ef^dv~8>(aRL}BG<57VmP-sUxUNt=2$7ym5LJzh%tx?Q&Wkd z!?0>*OmU;A)-WEw+SZ4Zmub-0)U8aZ@%`zNSLNbfq|uOnoQJ+f3ZEBBf2gHJk~`)l zp*ac3V3N=UshR$vc7u5F2d4Um2#dt`$w~0IkdjM)F+HFZ>JgJF1h{3OVeh&WVlhgf zrXP#iYB1f*^nQ3Zck<5Y&AD4$MTp^n7#*+Yk(j+OuDWn3#9w+?aV~Y8`Jk=LU%e^K z9bRA7N?RQ_FW!27&&ica>ouNxs!9S6Gmyvv&wF%LT?aAky#d`#)<;9W?XjHfjmRi% z5$xGI#m!;+Ua_KAFpdL@e%>9!e8Epq|5VxLZ}|l|WWwbixxppE1Ggc1Q3HvE>F<-6 z0#IC|X@j!5=>4zT+{OdQF2MTiLa-()91)EYOlwwHmI>uk{UZgkNja^;qp^%2p21=B zYS*HvVPOYw*KD=e`A>*8ZSX%8JDoNXhO_y8V~v>5<@Xjo2?1iQ)vhB?*!@vVlSKHG z{+nOX0L`yh>q=^9cs-1gp?!%g5wG$w3n(f4Ji}RqdAsR6~J?ciGh@CZoh4yexmXU#X)w zoM7|!a4$4;X(m2nq2^^a|=7n5Bc zwn9PvrjG9Y{I|t*htGQjEvJP$2ft zJY472Mb_nGY+FaA1;Eg0CmCYbKjY0EEB(@i;}G zPIjk90YB#UxlI0DqOzEng~4Ioad_Q>ki&a%RjUpTj)m;3i;>C3gTvLkyW(5t+{ax} zYrnkwj#-`tgi~Bek>PEXr;$@($6BH8;Bm%#v%|d0aR#GPrD^oLNP*Im&prrG4=DXq zBI=1p^CH+ZQ)MZKibR(lyq2NOnJ<{@@6-Fefyi-$Gs_rCdz*G&OSnnjyo|9F2Y z)txE`G_$Z|;oweWJS;XV5*X|i3@H*Tsv`Yyij82(U&<6S{%!8`HnEJIW;{Al)%d!B zJUklC2%Xf!AAbLBU5g!njRU#0ctpE{zx`g+o(W}uOu`=e5dPQnA*ojs4AbAi{P})E zyrYlU)RCY^LlDV=>!<)&ANUn4qw=wHeg#YLSaQcQuc%EFip4ikjCZGz zKmOm(Q^36)d_CZP31h?VoS_63_8}@x^I@og0Rt^^;(W z>?5f}`z5hKWh3Za|Obij!tP z&-WN=b&X%{5R;*0cJLiA=&5C>#qNxlHbA9REzLf(8BL z@hWFSV{nWY7S%fG=C&vBx%w>gzN^;#BCunpjHQEc9a@G{xXvAD&6ukAo`%OTTGb~i zWz&^?RCZy=xr==PSOFWKnPx$9P!JEmk+C3;kY^8x&T~=eMI9qBDXq`3helyl1239V zTJoTg6(;^OEu!9PX$4BY$fa!^!ZUNwkT%#u!pa=tDQ1rUmUXy96It2RpiD`Lg<4UG z*bR@{m|*@=2G@s@AC%iBuq6*cq+(^BXRIZm=EyT8m(NepPmP=Q9SM*5_J9*XGI)1b z8j7E7V{sGO1eb|nH;|@@cBq7B#(1ijvqbOX|0*WFiNl}$7U>{8q7c__oK@wcojZ?CkGNbfPPJU^I1wDs17+lf64^U#NZfG* z`%5`C8f5=`)d$mqsv>d9nO0uRYK_4oI*rA5V>AIN64fU8Q4;wuOA`3E8G*58JR7PQ z_Fc?4Y*PfqDL9#3xwT*5zM|_n+G_O6ugXiGPrvLZQ&i~T7BtE!;x{+Jv|LwQY#LI6yb25zKvvje-we`P{~>wR(~;nQhyYHxJkkHwm>lgQuLr8Va+S1-e}}$ z&g}WY6ESn@2rWs3E|YkOZ_!V~Lnj6lbP%`TU(5ib@94q3n2_Z2C>p$qKZp(sKW;=& zz>L-e4kEk1&fbW=n#A@B*Th0fp=>SvUOV-NjUx2Q-m#Z>U#DQqQ>Gtw3!{x-@>;}Y zr)P{3=xg{&Ehmg-ja0dv-Fi}i7E(tFN`ywIb!7wmbgY9@S%*|dmZLJitP*q54XDg- z(s&N%wF6t9S}&=$&YcH-i^{=XZsR($C*drFL0HLY8b=?WvviVq%0QdT8n~66`?da~ zU~d(NJ|Ys*!qb1f*5pbVLZXynv|8^%_WNir9o%AnOye1v3H$mun%%AtF3>0uaEjJ1 zxcxTom%Tci$m8i0CVYZcD&P~6z{tr){=I2^Dt@Jfm9JwUe)WVDKE$5f@c~3fk}@lY zxc%;t+|>%_C!>T|@;^GJkrxOkBB{OCmt9ke+moJUY^zEtIsRMvc6{oWo;mR6{(dfz zSFSXbhyhWypbR=Qw@JHf26vch)_88uDkG?P{;&PyDLPIELRuP4f!&>MVb_x_Yiw`uxuAEy(DK@5mi)L5H z3gPQXR`&Gbm10V!ZkroC@(v$Q*zDnc zKSU1_3%+JA5elyh4ZJ&h#CQ`6>ijfHaW5hIgHCvHkJYx37GMC^=nZet8Ejtz^Sn<0 zf>NkKP$&rt1aZd`b$*>sA0)92BmKs|t2OVEBzm<*X@aazr&xDb;c%awl+1RMTV@m^ zh_5iXXBgsh$3v1KMZ)XETBwI&`B6#ViiLDvl>}{fmFFyTYd6r&3d*}voy+sE?xS&b zXbQHR-x|WhgVjPZO%F933+s0)8yX`_%*yuPJfSf4wl->-to~ zR9B$3fCVSwgZwtb63>bF;J-!?&*~M#n3dL>*;->WB_%DO#!X(Ndx$*UB@qhbsQo)e$p&7 z6(tX+W|kz7+W=iCDWrdfcB<=SEo>THZVOjpI#i;G6@T3S%~|JY!fJx0k)?{2++?*y z6BD&G&`1}chl#t3DIr2?EN>Je!{GR50A;T{h}c47t^S*HeED^+Mz8r^i7%RQc(GHd zykEdNwxY@B&C9Ac2q8F5nBFnBZibfkTB`oMACCRH*#f@0Tx@K8{KF2HwG|YjrecNy z(?yd5bm^Ey(#+HihL2zb{<4VxTl4Vhp2rPI$^Bu0AX|x=L|5uo+^`unfUCisz^jjh zgc;K^lmebt_{X7+2LYl&_Ton?Th(nruI`Sk|F*JGWY60zLU`6Sqm#GJg#s>_Wj>Rc z+wit}yucKW&ha%pI|?G;?|jaP*)SG>luP^2ur0ozr)6fh&B5+5mZ%$exH}Ei^&`+X zl~8y90uZ=O0!NfdF1f8`i!>uzVe%XNFi&j>Ob(cafeS7C_$K8zwJU5<0B;Mfc^Mhg z1O>l8Lwt><2`fso*o99f-o?xeh;fjzz!Y9yT2o4VYkzN5Jy=+K?es2JjH5_5E6tzO zK7T}f0>B^d>NAFcmF2y!CKcZM2ILX?>xTfZX&Vd#{%S@GAAcCVh=z}8 zR0|gu#=7q0KboMshE8$_6%x&-oXMRM(sjp*X#Xc2jQ^|3`92Psq|w%1iCe^HX9o!x zROn+k2P+$^%JhzsNMU22V`T6^m`Cy~q2ctyRT$8N>gb?Y3yg$K5R!O*AB(5dQ}(-U z4?uKIp&RsbbaS;Dav`E&{aS|y3sbgD6rnFvd&foI2c=24w6z@L($C;xSCWA@i}s6~ zA1^r-{c?~yo=Yppg@<5a=5}an@{vayqi9O|g1tBKV|3?va}El2UfC&HC`mslg3M{+ zBD~ZQ?B)M07*=r()ndX-@Gt|;C%CB)uQT$@m7HgbU*MjvoilG`?R`3uOY_6|FA$7# zcyF9AE5YrLc*aDfWU=u;Yzg2WiCqiW?(q1!MI4U2a|eqI@vtK$Pd4}3CF3Lmgxcoo z9ns2-O3EyN=pldWouj>B-na%`ZA$;^Huj{j8JkjQ8Wn3ffz&(D94?@KC#{1tL;o}? z+(x8}22Aps4T{*sZbsmCdF~;OSdZ~&^Io20ljqWufdea ze}eLym=_ddrXU07o^g@6c>>m_k~D*9n{|ldE#9{|m_SZQ2#*p$e~7>$54L%b<*Z1m@|fk zQI}8jdFmX`vUm#Tie(0f8DRo`mM;i>P;j{4tb2D8zQ5>G_pV$DCvR8pOWa|>#*v0& zi*VWzbHjC^kT7sV6xA>>gC>EbE}5DC9S~6h`~wf{xO)X=cc`C2q$~xwZ2^2lr+Q%I z%=Fx{u+ckWWCiosiyU5X!3={3eAl18!>cq-x(Vwm-$BoJX-b8IE_{5#aAldQP|CQU zi)y(sGh;Tm6Ie#X0PAoPlp}iowsRhK{h@d-*#upU>gVR+Gd!(>W3_ZsDLNYpuk!gVd8lMM3H3@$$_8x zJBb55DQy|irO@`M!yHe}Q46mCfo@sZ$pC%w0jeITK1hy32$rKT%I$%FDM8}^-8%o7 za5$rxLN^piDTHJTsbYLtAK56yTo~IgW)5B&>I#%Pb0p^po34DheSx$z-opC!`_1oV z6|%{>F3ES_kgnSbv+cG+;A#8}(Xk1}u@bK70 z&ZA7gtBK7i9VKJX{Ab4Ak>h6^^4Di$^ld~|$`eXMP=MK|%H1MK=hpz%A>1I8sAsLcgQ0;*E6}4fpL>od zhuFlN=78e#n$~uoJ)FM|NKnhzhp$o%yC9fwma z5k|ek%D_4j5zE{cWHDr8lDa#cKp%ko)y664-X>wOOY^`QCS?=R`E=(#JncJA>ikG^ zam1LdA#ylz2zEd2ZB_qh5L+1g!%Eg|pYqrWnC?xiP!co4YzcB%e9ShC&&0yR#9-Vzb2$(0|g!hXGm-w0hRFlHGml! z9Si|1q8E9pmAoe7i`Nad?--V&!8ixJ)QOUY75u_q=BH<^mgSK{G{s1=z zORubgGi&{MAAdf#62nCNy-0G8W3G%SE5aSSW1fKiv`e!~8E?c8XIWcBRTYJmw&tPL z?}BVf`#juJ%VgZ9Pi8saOmn`NrKqW{udwbaQ>vn)EuXjDoTX#lX&B(EuZfEpm!h^9 zSNBuc7PPrv7jiW(tqV~~Xn9LEv}J=Nwv8IO;uyLV#Se1N_Yzh|l?;mM4fjTjU2=+kC*X zh$&aBpIBS(gZom}1e+(64!SFYpLy^EFU}i5SiG~5x-UsnyH*+}F77Y#S2L}fvuT&> z->MpxHS|u2^iGKQHn`e;znB)%_dZ(6snYA%rePxQ3y8e>(v8=$yZMg)5~u&=s1Og_OJrEO zA{u@hdK1Ve4g^A=^RO!* z{YxeFzt9&6_mb+|hJ~S{$3J&D6h2nxk}V0nK@J{*rz?kJ(O)(n7buXf=3{)Hnww-= zd>q9`Dj%fLC?D`i7onp|aRPxH)z*}Z)j7|<$3P7n1^7jlz<_K22@L{#bCS8xTRUUo zfudLl2o|>CiJZ%r7`IVd8|3pKF2Si>v_rT8Bb4>X$V7h`nxiG=xMgU6|EzQ9D%No=EIZ2IiL%^esgr2|q)rZ{A>znH8=CI)t)1m#YU?~P=H z=XYvOm@NpVt{xeBhdH_Tby^|5Q8ubFJ^-I}s)ymNW_MnZK1o7L3vG(#)laK+T;s&z zyB~wRwX3}-+M}YFy17`=1hlsPlua{)m z9SXL9;fkfOCb8I*z%Gqp%ctP}yU4oGDx)+Vsje#0>i91n-^q|$Sy!~3g?Sq9@*!tY zUQ$&#Re8qBkYr3=GCl5gsg&82j>na|^Bi6G$5mtBJq(?F7hlI~`3AQkhfJH(&14*x zpI>U;%Jdx3cf9h0%{`;5A8ALaBhEU9LZz^T8UV#I(T7#dN?s?G)9RlTB2){dRtjyB z8Q=?|xrz%G^$$fxL5)7MI^1>>1T1DEDERe9ifz9ax~YkN@XDT$bABsu03_2z zjVpH|4vEXwjOQ5gZa|B|W34;b*jG}sOYacet|2`_6G$D=)$E$sb8Ee(J|6d;fKQ{< zz=w?P%E|-Et;peoBj61joxlnPbxUM|(ANT0-#Xq#0E>w8#t=!JvfXtFiIG zHVW)rq``N`0F~rG z{4i)~UA4@K6-4N&j#sc1zk6}2xZ<p+&*S&A?fxja*X%)-1;t*sFSyDOx z-AY0q_s$d8dW>IJ|M5c#@Tb@xHnt9Sh5e~K1+Z#iy=WvL?+Io;*v(6oCF=Q#*_Ekl zi{j<6L%wu+(CVRg|DMo%%NXvvzr9?~Eb|F)#3n~6DE2CMkSCTPY9LjZT4H}Z7xTA6 z_%4%3c$97-JVr3f)ogi+-mb8+mR;{CgCU3suPk>QONjK=rAO~OiVLX2vyzb!0{$f+i6rK>%d%aOD8)fuO zf=xx{t_qszNSjSU)+Y;w#wLx~>W1r1N6!v&>BEpT*6AGM@<0er6nj{<;^d(2w@Pvj zajcbzrk1|be9K8lA2KCeBWr&AXeq;KmPEq<7TWsfq$cd?KYy^%aHT}dWMv-Ug2<|C z3mRiGC-7BQFXtaP}=3^l)BAgaORECEP7Dj3Mri&mg?w~j#?o_nMgslcJ$+U zd?5oFnYy^#AK%Yc^g#u0jH<(nEx(TUso^Ygs3hy%8ylOZtjW9C7B|*uf3qs52NVOA zxO~Wm`gvb@b*3};2j(R1(7M>FbHfrxd|hO@IYkZsRf}6yH5@<|LO;znFuggECCCwN z>9U`7xVRZOZO~=7;YiCvR?3hvT5>F$EEALRs$?58H=^6i{oJ%~8hDi{vI~f00Vcb@_I(dkQPw-4qD*VT#+7hBT3$rhN;h2V z$G29wWV~Z=jNzGR5D&RT_(f=dTx%;5M@C=s{9=YYeF{|@r5MNIWZ4L&bcK~JN~#)O z{dDJQn9Vt$@|H$RqyhOJDT~OG#yIxF3-Alf5Tk^KL%d-1k33fL85x*#1W7J)Xkt-~ z{MpeszD`6mQ?1;^kFHG^z2s7-xPtG$C)iotKb3&GZ~evAz}MkbzV5fkAyq}B=_rPe zcvklK+c3f$f>4>gUAG^PaH2g^ z^2`k>sE|kIDDvEJNl#J_Z@7gZ=S=6Q;08{#=w(N+^Vd#Ak<;{eRSO5Ac>$2gv+0hw zf^SBS&?uR|BHjnr${ywzGbZCz!JA;0-P*Z}9cSgG?+c8IO*XI0c2{sk?saVKYu}7$ z@MnXshMU7~o_b&H{5`WZ1W_sXb+#jbJRCn281u?8d*w-d6F=Vbdc9Fv!QCOc-Jmb8 zAO#%o(Kj~lXFp(!lU;1?`j$;UjwYI{xOs2#EiW;Vb$tt5NTc@}QMfqiTt3&(H7jQm z#q@CSZDA7%Rh-$4oXt~6OqIoVDO;KuV(#ovV5!HRm>lX)e-r-kf-TY`HO8FkT9Y5GOT}PWk@V02hNEstn0+{9kDyB%L<6I+l`5AwGc?sV3lAhs5HFNhY;?O%YMq> zR4+=4-9I05Slx9n>XaxhWjD2sD7A`cTgd&&b!(NemFLrIn*J*hTLR?4cVBn?T#!t4 zcyp+uLsGEL%Hf3+!?Ien?$T!T^2vpCTE6ijj4yzZdmfME!2DE?BQ);-T85t z^(YK*UhN<=mcHSscy7x`{^Oe6WDxlH3PS`6j9j06-?HHOw0=U_E&E+3HM(?bMlj!i zTUhJy+=On-&@;kB`+s{2_?0xcL7zkvjBW@=@yH_K-_#^JPq2mREjc-}h@|R;h$zG_ zh%swmlhA%pfjEQ@XXFuOKS*=*GQkAj$l&A~oUBhxcf?;ME$*+LB|1I9@-SKv)rsKy zH9K8jqMOZVqDqH|?A6B0)|xylC7{$Asx4a`T9tY~afO`x z-oa^9qws4yg=fODl@^LY7Oax{HSVT8F@|i^G?VAhfL~b0Tj~&(?W@>3&SUt8u(mBz zMGtkf)~8Z@DPutX$h6e~cxusRo-W`ulp<=*M(&nzCOpW)YRgR8dZ;QmiT>7@ai!&8 z`P>oMAtF3am}QHs>Q$EE%gOY;zl!%=sjX1 zje+)kJHmP$RrB9qg+Cn&esU@w)Q%0q8n+xV-4y8w${Ig~kZij!t=bTJ5e$ORZg#dio!V8k6yo%0h?=7DUZU9i4^QifnLBskxwdBFbMo za}I(>@0F?hXL$AR2YVXV^d-w3m$vHUKlU85&Q`Nj7xKCM~!L(@XXw zziC^?6~Bu}zMFZ4P9D5_{O>E8j21DPRG#K@NO_@ovv}iBuq6E;jU)0hP|`6c7if8I z;u1AG^DsQ}LW7UL?kqt-IbD$2mC6`!ESGud(N)F5Cp*oW0=M`0=-XuEwx(5rwCh!Ut3i0 z>vqD4AKz$;+ST!^lCABR!YyJRk&T{9c>}N?zbgf)RHhuOeXHWN-03ey1b&`aB6Pbi zhZFwXM>LJ3OBiu?N zZ&ea|&z-DN>tTOk*2+V_z`vqFrD`7@s203|5o~zW+_C5@Ygp)hbwHaSHH&SxYEA|a z&Q`DUf};LH@wSHPjs(K7xs+>9tj>f!hW3;-vtjpCoW6L-ASo7>G`IW<$T3Lh9X5G< zN0P{EUO8ff1vdJWjxp;nQ&p}T9lIaGgcUk{!l#$JLch?m`5D>%R!{u*p`b3HC}(Is z_g>*Mg_)567oBDls-*Pepe!IP#QVSL|Nr4d+4aBSn^U>5bgh`#{h$c(R3MH@fQJhX z1jA4O4aA(~;#Aa|_inUiz5v_X8`vH?+yNr2jc)DX5@50`{7jJ0fT5 zmD1w(q5dsB+P4uoB`WyH;c{r|nn6ljPFqnG&5#z&f`N!jFBGk+bVoPW4dedvI8HiO zy)cd2c1zhZ1Ys;rm5bADAHq!|Qb+&Y{C z?WhSdcM_)?^n)=<-QrYxM}G%7`#VK6k;W~id0$K4Zv$b}e7@lZd?pvTwuY2EDN1MQ z_QK9TQypG4(sk+GP)aePiHQUBrgc|#{?hS1rk8^u&U{+gl9mZM6m5)C>OD+UToF%< ziE?L|xs>2W3TBRCgHlZKpkXyd7WpCy#yMH3k*pQmQECpl(ugFf4`IZ0h#@enz+f>P z>=R1!04?(<`FIx)z4~Qw<|m zdIR53gkJ*>7oJJwlF~;m@2FITN8^#Ir*Dp9R9NB6W7!rx#+{TcL;Fj{=93&6JSjGz zFQulesjWD@>De{2(FdbVMeR1tmXBxwY|4w0+Fpx1M(##@(p+M^cc2!Y&DZCzb|n4I zF_uo?DuXJ6ZFeT4MTyL|Iml;jMO5|QQoOC~N}rk4;l)?wv|0xjk*A$w%8^b3gHC=+xms7xz)D_2Y7S0kzA-c&|tqp?|g)uo<^yeC>BAqW+}?1NcHw z)}n!y>KXZmE1KROK?hT-icBNP!^S4qNAj9^@;aqbx6}*?z^&tD#G9D`W`=@_F3wW1 zjFZXhSDJ|Sz6NT9!zhZ6$MmYz<}__|lj!(F1!?SAIKEB7iUxL=Sp#;eJ`(ujnxq^d zO!G$y*D<#}GtAoNM_t(8+g4z!i3^~O1La%TP{B=To;#1zAl-xr3_$tot7v3`L0XI@ z)n{gvfHtFlTzfA2`$7!*lLSmX99gb=QFqqm7t(`&FU>r&IuEW>DxDfB=yozk<=P4_ zbIx!tlSugHlc#K%_2vJ?VOvb~X;SuLKaW_ZGtU#|!)*oq8|aeZcryzqCB(3sN4VK; zGP`MENJ*_Ud=V5tBGd0*_;SyRse4d2D*rcnqi68Lb`w)im6@-14v6do3djC&`L`RQZDo6+7(i|V0^|P2g{M825~8K*Nn6rcpQ1Db%9`nJS15ClW`vz z|AdUJ5^8G(h8E`#qWp3}M4`v;vZs7a9CozQ5n- zoWRcRXxz?i>9zL697@%XFt!;ih1;|ptC2^dPFUGw ztMMD7l8U$|YnzOW+*9O|IMB@iP0^B z)_51A5@>5nnnOsAk<}5wNu$}tSd5u%xYKNMyq)(MhHR&bX6eY0zR%-DzPWTHPd0+O zdBiERXAaUAn&;l;s**Z#RlF)8=H9IM#g*YAvPU9t&3wxsa&{ZTqQ2szBbq@*zxUqK~1P!-wge?q|v zPM?^!95%cx5Tlk&Fv3^-eHcRmoxG7vI3|Oz9N(^lU|&*EP&JPULheZL;g0QE2YOBy zFN3(E(QUG#c*ggxXBtvckIF}{W^Cu(^8Gx_X5jlb+NIO0cGLgI_th<+aXfg4n}P#= zpQz>09ev+UmqizL`zWj>w@_jq@08Vj;sUDn`#4ICUf zs+ME90gm%gvVJSaKeS7&893oXpLfZhT368dLx5m^zBfIhk^9kY!$8E0q}CJd7r*T3 zhDCfqw6&;)g>Bu45OPQ%g|IF`UKP61ug41e(7pKilkycb2zq07`H-QTYcEZ!T5cZF z67}J!2I~EZ^mzkAyGE{2)i|@HQRb)5^m(v6=`%c;v)>qN-P2~>F$yk-1%G|?@3q;7 z1M>Z~{(VdWzej+a`o$uIkk7Tq?~2)KAC30(SbuFr0N8+cEOxDkY!y`|%}@Z7|; zIbLSV&}%4@Aps35geK&t99r5~k+_0))E*1Fk$xA` zGpNxm;L4|d=$N7Jr}}fAfN6ZPS<0&`qx$k-)fd4tha0eR zM&L{6##A8szWU>lSLM*!jYY{P30G-xP#}g)`k}(E7q=gaqI*y4OUl_~$VvhCVmhF4 zg8>9c6U)FSDWx8L#xX^T?4I9hvweb{(H%li=vdgDOc@KN)ex1GRP$R&R?njfNckqB zQF2mlU;?hG`zSJJFPE%YQ+muinEZzm{qnMz$NjjAI=kBdSJj;faW{5zUclNljD*k; zFY}>g{d9a8L;o%Tao7zI5a^zjOo(e`bJAO7ZC4#ZBC5~^sYY!lRsUSBZ+F5V;i#jP zWwzoPzbnNuI$sklI>B1&_hV~Yhxwvd^f#G(4Yk#m~aKghNaorDUuT=nY3e3SR^kUWr9S)jcWWlU}v! z=Azxs!t|PeVm?h(^5;7Su!G+$Mc>&)H^>DRiHYnI;MA?VWq+%@J^fj3q&N$Ic&QLM zme0E!n`*B(64Z1vI(gdK(D>p~*4~10I?3{Iq>G4B6A+H47UraL*)D3y0LIXDyjzR$9@;z9bvb}&ET6)9pkZg*|#zKl~N}c7KCeK zYpf~Idyf-Uwb0m_N=#j7@g;SY*j&^a5_gutb&e+Bf}yw$q28{KfmnXcI))q~27&Bo z-vfBcaR46Z`nrnqp~T1KG7+5x)_{CV192WlKltB9vu@%rA={<=K~)CWwdvswrA0WD z?;ODo4Z_Y^cp`Pt=7L4>WSX)G^pc}I*Enl{<5E_As7gf41qP~Q-L$s3<_CaIi5-yb z)mqCsM$`+)U98GgJ}3--Bd4+5_&@ z38lrcj^3Tww9cAR&t0Z1C-Dy-?5ug4tWw-z-0X=WCu-72bNs>+RXkoIipon5 zs|3=uLUC0tR$LLkRt*et0?D8)Z)zox|_h%Mj{p2NoAW*Fll)~Ik?!6{~r z{5m4p^(!~|p8kGpgPC7Bx>R{1x-KKEG_P44#SN!grK|n8y4n5n{evzaN9iYlUZDq# zE@pXi+hBY`njE)C#MgI^v%lTnHsmz*Jm)OD$SGR%C0c9?PDrde4^@(cMZ3IyyJ3E& zJWHv^DNcl7(-#mj@lPgT{yjjU$*&WB6y;WK;Z#PlDR| zD@d)vBQf3v=Igf-sgmPA@00TbP&vWpKRLnWi2$@LLu-pCyFwtLX&rS8dXglzM}7ng>viBp%&mqR@3xFkICB+YO%yZ#!Bc8 z=IynAsN_2?l#{fq8W^?7q;Ji-e>qhvFctKpxz;h>1`oPu;eF?`&aFK_95wB+C>7zI zw~yvZGmc)S)9QR87L_EyK7p6WH!1vkljhXB#yhEYu9c~*&O*AP^R3GnOE_ePML`}BV6y}I>1cUZeZ8eL?e64AQCVYs@e6WFh z4pbFDt`F-I13vAbL|}%|g!eBGSWNEh=n@?G zAQ#=S2JEPV*7nKHwaifr#)i4-oh6ZSPXgd%dj>X%h%1OH)`yD*zjwd}UjT)=zkV8} zNJz=aJGn{NuC>c^x^j-R{6ph{`Y9t9;@Bk|kgN)pD@!63o?* zPyX{Cf9td^n09nMzLVUlH}xRE$zgH6n)V0HU#-M{yIqF<=n}oldHo}Bw;!P!-xT=;6Wkp@E}USnmWv> z>%n#1xz5=bALi}bzi1qxt`7fZRm$Wv^R}IA`R^ky0p{81OwE%Wg28iBdn9G%bctUMouws`;}}}zeGm}|qQ~dCf>4-Quw`XS z9oVA97ENKJi_R(-f$n$(#UUdiU0spI$K**ZdFD>P*WEfIdS3uLQb?Gxe1Ea$A#pkp z)#=twR1Pq_oX~kaDkC#R6$5j_NLWVY3V!BlDz*dZ`km~KQuRloe6H_JBM0SC3)t}T zalq`#W$Carb_lkll9K{G3q!a71J{UjIkhUm@Q?<=d}S8x)RIhCPLdo6{uw2hIqA5> zxwnsWmk8c4^aIWi6k``Towj2qN$LR;HtmB&WZ`d=RCDbH5h>sWbBBrRp|U?>XB8dN z=i5q?4IzHe{bS{@39g$NlS)hp9nhi^om(ot@uA$+_IAi2XgN}ib|yG;7k+FtA*8eT%>5JXlTbFRf(@KwRDz*hrb`q57g_n@gy_$g zcid^3J*q*tFF8}(*!O?&0~_ghBhJY(#+kV{aL9uPdmjl_=h;yPD3&>)hGU;PCK7Q| zAvqX?;P4Ar@Gc=k`!nZYdj+B@t`JG-Utf!+2n&Z~DTA`NJ@+%GlzY(cxjkifpCf|} zoYAy?Mje<6Lh7@HY0BT^)!$W4F5>ZgEp9JH;5uG;wSD`ttoUPkM&^6mAB+=lzU7@L zPPh8e5#ye)N~e5gn0I`#)_luIU6^)cD&zi@Z+LQ*2$9$LI03hnGI(Z@c#rh+e!Fq- zr5dPU)XfzS9aa(}K+IMKMko)OBLoq? zW+CGc{@7kA6|9FGn7n9phwr6fPQIN$Qqjtk)s|=}Qh}qrIqd8SBS$|t2(p;l*b&xxVvNv=A_EAkti#IOK z@^$TyS^X^>Akyigw6~Eu01gT`ivVXLQ>`+}$2zoFlI95RrGUdemV~Csp=I}aH+Nry zIrLUh*~gX;Sdkl0oTc5ggPN+@!dzRAvGWvW2+BEMr6~#dwPZULL=_Ls#y%>AXHq=O zTN&p}DdKm_VIwI*qeO6y=^p?hBvR5;fletK@Wu22TIt`)G1RoaHZ90zB4uf`aFeqR zC&70HIQM_;?If%Jq8^rG84MdNS$xfv&inYwjF9Z()?L+=nu+fO`%417sAypW<6Xa# zGVmC|kpvi5)bspfu&00`?*oue0Oaj%wp;hPs;M&muF+q-Hra{bpa|ha?`a;LId%ScF~q2fAY)sNcoo>`uN8Ab9vfoD}EiCwc;Ms4h>+r~DP2 zD`|Y{iW+y)Rthv?QdYQ9%B{|l`#8s;*3TAG=1)%7f+3+$?QcQU<_c%KWe zTTqzYKIu(%8*1bm+xwCt%ep;j^Bh|G>xsh{|6*f^nHgmPrxpUP;AR+8Ms2$NamURjFs`Z`-PF9h zk;l$8s2mrKQ?dZ*pckQx!-(FaT3Csq7p6zaKq$Z+#oFt{HV@P}p9UJ*?oqVygm~h* zQa@$Tj}n@bgw#&dNrJn!ra7O2j@1_3S*w@FxqUlzT0+5crU_#4pXV_+yfqtn(2@|Z zEY4*gW$zF$PjPI8C0K$)qi$l0^$RNr2yUykof<{90?F##e{SVNs# z+aL^&;5W^=D8!YJ!JeI+x;ax-+e)@L&vOZ(<7cFojn#so6_UnMV@&xh4q!nVyPctRegj&*k(m@pp(YON0r0nWEL78_PxVH2Ef*!Sg zHjs@ve4%RJ8{4$B`k)+GZL*=9(T)>df#{8Hw6PaoJHqQJ`ynu>?zl){{aS**s7!Ww ztwO+G!MUlezAk@GQXMqTi17(EqI(ki&t2v^)|LVx@MqBPOw=Qe?O?SETPqTjDXamr zcB)@zVm>xtr*@m?4ar%D$YnCzw{;x4Z=zd+8o4aK_iBW}YTsQA3wWMIwf_(2!aw}l zz~i`(ay~D|g4%jpXF>Nwfl`V#I(XNG0}E zo8ijYI++hLra6fixy4*}ub{M{0veez-X80Ad*0~d8xI7KN?Q-%40!)m< zyM(xYrRM*coW;Iz+h=~QwZ87umv9NzTXjj&$+N&@sDR!4bua+9Cxmv=>(DA&}og&~if;Pr#r0mRlvk zB`S_qmRjd5Hm%j!AXzzt$`+uO9DR3)7UnFvaHk|SSM)ZZEZgjA8@k-cHZ5w4m4p_!w%4reu?9CvcD8NO z00d;okEF7##QG$lnyv%Ne4(io&qrsh4T_b19mSOGnI3!MeFGs6A6#s`vyXMvIM_l5(! z4u(Y$O5NJBv+gopi&+wX_(pRu19Y9NKCaxr#+&Gos8XeVKq*y;hOnj!Sh~{56}i_y zjLRmdbUQ4~Beg6EP-`iB5$q{unYVMmhmAb`2|D!bazob1bM)J1tA^s?daCRzZ&Cd{d|nB%!wPM3Z; zZ*^OGZA4dm-Ip@^PxQns7Fddy_GTmo{3AMbC%Cg{;#8Wtlda3h=jfC+Jwr?MGWN;d z2Ezq{E*i-g|{l@f=<(`419{k)1BrIREc5=bpl}BA!#+dMWu}N^)ao6 zN5qIrLJ{^xgUWBnYR!SgH0;q!h2?$xecb~5^T!`T3GG3o1!(bPnDGU`#QZqb5HLSJ zY4AwIqq~Fkj+Q4Dxm8X|b5`GPU0E52VhfM!=n=?W)w#co3z4uFlCxmDzx=-^Jpb!E zF*CeOqf?gI2XxjT=K~)@yGA~~U-{X=U2J^Fr?aZ)xaIHKwtq(M(16DhvGJOeC6Hr6 z8o{ZAOua@*CJnLZISBz@cGQrdKTOR5($y^T&9u=4!mOMCI6A2#A|i94QBtcgg|K5a z6Ts94{cDI3oS9V%rtoi&#__k7P#ON6capG5vQJW&wAknz3GR=R`P;M>CGgktZ^!H$ zoKmWB_ea)9OvIj0`(cFX>A8g2x;HeXk;7ArNLZpUE!xl3 zst2C%tPlPCa}@Y+U9EfON7I(1Uo$J?A-Sl@m~Q7U?DhXzTb74S@?b!h_~8jJk4s%}{EeA{oM~X`Hwyb4R9U?eJ3pI-b zdln8QlmN9l)F7=9cGBu8tZWhRvbVOuH<=G}M3UgLEjI0L8b$(${ddLhUZIx6k%pgj zZAlecCCgeSt$4E`jQH6>%iyAwAp&B4VV3j?x+aL4#R(IGAz9SWav|OZGg)ex4Z8;} z?L@weWo0zCHk)#&4GEGSqVObpAH*bwXHA9IeUZh8`xYwcwl?LJ#Fs{n;5Ri!JKB~L z=AKxqQOW-ngYaP8-)l+2Zvv_LEJPBzpForFS-79QGP6CjX4S9b(J8gnWrIE%7(I**0kWn)ojS@-Q(Ii=u~_v zP9+z4px@kQ)21~(V;sU7OjTZ)inG+_EMjaq!hfC4$_Ma5_Ipi_Ly<7z#>#Q(z_H^G~s;c?=a8ls6_|qaEiJ_Fu-l z-_Rzjm-($=qyC~cv8p9q{sUQ(9nT0_KM75vw|R^QkaOjv(82}D1$tb&jEqXStbyd< zbZ~#hHq>0Z6!Gu&dtm8{%JN;ZMQQ8>BW@}`9O;sLNsLYOR&i7nhacj;`qmH-WV(jn*r zzK#`w1&89(1a`260QcY)XGt2B1|Z9iW3Xt1u~lRYWgs~8KKx)} z>Lvg+KDUpy8LHxiS>s2*18p|?1zFSuE`6&^S{q>wtp$c+oOpCU71vX#OugI@wk@f| zcbrtEJy_vbZPvyS7OkSvOo>jTQs*%moovD%JjI-pEf7i&tPz?7@>*pgTN8>jZ}Q5_ zL3S4N)uQim=B>vGGfR9`5hnx;9CL(EcPMs_qt0*%D!(WxJU8*~s*5eZEUjLXyU_`` z!&OIg;WL6iEv#IZo0x#Z^gxncRnakJYi+14v@S1;Hmh?awH03}CvNdV`7pkB0x$q}W!nog)^G5=B{b`3P%Nqk1&W7L zgc>Ef;X(uIG=6%4aupWONY3xT9xrp2T8^6mbc775SoTVN*h;p5x+YsP9E{KRFtp5x z9!7AL&;F8++qT^->t?fNXrY;6AT4t+kw&5)2xrJ>MkAKXZ32n~NNACk`2;)V)B=J# znt^ZFsoB=Z)Qj3GYuem?|02NDy`U?D-j6F0&*9pq>pL#edI|GKv_Pb@6*?Mt;^veV zaU#smT{?q4=B#?xKh<7R(*M}ymH(MF+F^hnJJr#`;?wir)p)Z}fQ@gnifx#19rHmZ zQk~Z3djZMFYJu)4Nm&%7YNP5_exmODNm;|AN70Q|XntVdt|p=YS9}~Ap~&yiGPYAQ z^Eaqj$HR*)1B^87O5@D)w>7m3`k-cLOiXleCg0jhN9_EZk?&m^HE)VO-wZSV0PWiz znxu0MPsaqQ;bEGtkEBgl$&omyA+{o0x?|1G(+&Y=ExP^3#0L>3LII@R_&l?W44B!L zUAUf>F?zOsX+24%K2!cwb;Du;eDSFPY_4vYJ85PlYyvf*&H--_g#d!^A#R2k8sNl^ zb0Sv|mWS@+puEa0_M%#|#^TgIE|b7;yy`Vh>W@bN75@+#x|_uwNHP}8+mE1ygaQV7 z7hwY(cAjsTO)!RPqb_Dcz2D&IG)yCJYsE4yW?kU~68PYmN1a)L`%iDAqp1;+6!-Q`8@x(Qx7#qXpcm^ z`p(l`YQku~e@3p8O3nTOU(jC~_YpSu$@t4oinE9*1wT7CFcN*HsIYh<9ci7kO2w;f zRQJ)G%I^!gW+r9>ia(3Q+y$E?x&*Wo)J{z|0zMh!+q}62MH-Gpy5PdWPz2$FFz8HE zrQf6bV)|&!b0HSXASDWG(9WVr(Feo!%$E z_5I?uzfl5gfH3oO^lL$U!U*-d#Zj4JveAR`H{@}OU`I&X)Mfpv0@ z5p@NdT|tU|AAs$jW+#m|azH7ySO9*Hg>=gXS_!)>8%(v0xO?TICN$+OAcCNR>nHmN zV`}_!JK$@EP|Fk%Af_a>YSj*!SzwiznAO~dKRTr7(OZGi)gi^qqN%{NPf2)5yjbdJrF_WNWOs;~K|oqXS<^xiFWxlN zOf57jnZn#OiI}KtbVNMJd0P;Yb_HA#mAFAm>9LZrO(V;PX{6k{+XjwmO1Tnr6j1)w z#Yc<~gL?-HkZfnq*vX1Ki|9TMyoBWYuT^Jjh(a zuyz}C+S@k=f3wjQ@Z*oDW=~Z*eH$W+y)tVtc}~HC{dX=dt6B%#>RBe(E>y$+n*j|# z<(IJW1(NQNyL1#aVF~j~z?iXZMD)SwfTBpvoPYdIj4<9#2*pe^^P=3xph~4bQF8ym z$32gebAdWb@rRK3JRak|*s$dG@mML)+aAn`9#Y80jm*O#tQ<2X79S1DflDqiq27!@ zg<}iO*;hFI3^K&IZl$zEno&=M`5_=iAmM0_!7if&H(qFk@j-T-m*R`T>PM7NdP5>9 zCT30O>nm9JN6ZlgpbL*RX#4(8ao{fiv0-P?Hqp@ElPHXrLzU(4=%Mq~_>N++=2f!V zyYm5^nAEB#XdX-X=a8`B2da+@!m{yGngebi&>rgk9p}f!sW=ua^X8#P($}cMa$5Yc zMgb6wvAF92<@<-fZ(L(3%pYd~s4&PNcyrr*X8zKPy6FYS&%Yc$dv#>nsyL;0$_ zUDY(iVkO;*6)4}ePZtcVt+lJ?bkFAaoD5kc%a;w##?V-t!v4JE|JuCO;d!8Ix{>AY z_4NPxO8-#HKO2zfN?09VG03(106^J{(a>@U>p8#Ko8)!3YWth$L;~}}>O+R_Pe<%^ z>ff{2WBtTdlj+`);MPvMZm18_!_@MVvwQ4wWxfAh37w>@aW5|8A5ha*#otga?2o$Z zu1j~xFKvx%zaXM$>%#036}_sS#MNih+;>OBND;Xq0(t=^#3B+&LrUS)?63GfpRa&l zeH;fJkS&RQWr@`S|9FRbS;jJTuVU4Epu$=GB)s=iG++>t8}N47HeMqJcVfJv&)oG9E@UFLh_X3-xwtV8cX$_}}DZ z{ev@{!on(94*?*4byX>;IAp$Z1j~Ue`Tc&dz~=243d&l(egCYv`0{e$C;CQB3X<-y zQN1Mw{;aY<4^#bUro^ z7dzB8p3OOI?eJ~bMI}COC0`gNfvZ@8sg*=V*K&hDpU6w53BRG?tT}waMzrzsy1ajD z%vaOBTH4Hq<>*7U++Cp{RwIm-)(>^g3XXIUL+}M!Q=)rWg--M$i-r4B$~n`{0u{l4 z7|qZ60vix`ftt)>_*N3ogcCyi54)3O&C;EOeSR*A-EI6F1M1`m;|qg2%2HmcUOr^v zB|ysk$B;@|DO5E0!J8Cu--A6g2oJ&n#NOF-&c4Cd6Pc047*!5XIu{XisuK35gU6+t zHW-A0tBhm_i)rbOE-xl~?`ry}4BzMYx6JJ7zzw~ZniBG0dDi7ZUUx=RhhcfegrxHa z{+AW4Vk@d>x<-d(p&A!^h%;dy+vA^!rRO^y-UfSB4M+hU!YZ8rQVMwDXGBvPakzqx2`Dgh-}vIIKhpCN4Fw=t_w-Bm_Xww`!Tlqem5=F=pMW z>ydxwgSO;N*=j5&xs+;$uI-N2Ck6(kZ5H&Yc6xwljmjsg9mW6p>*zfzvIL|ji0{Lv z%Ck1jqH4f~Asj-QQxeo#0R_z;likWZ3J11K4$uaYD5CVfq$5$ll@V`$3o-9Oms|cd zlM9A)%LmI3*^z8Nc&`efkWwzaAMN$peEs|OC*bAagLd!Vov$wi40|`bBNC8&7eAnfuXLX;L8G<+ z@3P~Ko|>+|$avi);;FPGZ!&}@>u;nJ@l{Xa+ECMdQDfSm5jUHP7fHEg#?!&^caHAL zzL#p<2_ju!eMwpPR6{8LjDnk9)QD(s^(pwgG&F|_11p>#+65Y@^X^lW68agtcq|N* z7S4Y)-^(|Vq-U6^V4sEmPQT#wnLUxP+%@Hq^@K5uO*pBif(G={8U~M_`*DaOP73Sy z<36y7^VYJBeIFU#T8%YsJXsBHiN2|jSV`MN+`QmELGL>YC6A2>oJ*NM^1hK@OFzS) zH@g;gJ*pQfsug2eI55PY=qlC)9FGBK*Af0$4$H`$G4RMP3`Z4YhL^AAEr$D8af_A! zQkgDcXhxf$CNAR)uZL4i&f=CTl%za0$935#mR0xhLGc4T;kYIhjVUXr_c8ec2pOZr zw#>u(dEF2w&?)%&zy|AL(r#=&`vFw8xwY4UFFxCRU!MGzPBNX_$EIs+1W;6YkP_I@==4E(lWVPz%#NgoFID**qo2J@mm$RFcA8kxI( zLQD!HRcqCv5X_1N98+h;PQ~7xj1JLqv)Kj%+NavcWxm;ffI8mew*1W{*#5(-xSz{0 zK?7K!Ia=Qu2&5SBzfM7)(sWgkCQ%Gv#c$)9Y|`w2BtVpgwrf-gUzrL6OVqzS zU)#RncF-U&sE6|dMA1~~sBcl0Ox?vawMd9^XjNh7<$W#)giE9lcd==`0#Zm~Ld$U} zcY7FM8BcZo;qT;@Ho`p%>B1%VnyyoAu2G)hS=ohr9F|zr!$+2Cz>8o9tx~2V^ZJn3 zTcbDy2M*Z1?_r5l!Ry;jxg{$GEvnTFATBG(DUre@xoT1=cD{>)+Cb(MK?G|SOzZ#@Jjj5DjR?u9o?J2-%I~;MFVaz-;RgB@%Uej ziz|L}``yV!CE2;y?HS0C6T7EsvaC`Yd^Jw)0QOhFTy^)eG7Iz~jJaA(%&!mmj1pFS z+Q+3@J(6GRNyBdmoCyw>;AgGln=x0wnn=n$^~Q}qPRCFpT(^C8teRhj=Q?CDyvfKz zPmkt$LV_Jt8I2b)Q2hZq~X;epW|fy0;$mo=I887 zT#f{JA48BzI!9?J!2&6A)j|M5|$-w@9*r%GxeJ5LkL+#aDIh-f-B!Hhu5!GLpNCS}10r%F5Hj~50E zGuh&Wkp!Zt=sSqQnE@#Hk>2JzLJIPW4+oI9LKgSq-n&)dXJ4=41FttT0}UT$>O%7e z#gFp+2oSmGL|^>*#4DL3oZ8wvg_HkV9j}wWMN)3GR6Nji{~O*cxu0>27cx9k%(W11 zy5URue4G;eA>l44$~GkWHR#>!7a6&XEa+4Cu6Sm<3V=befvEDK3my|N4qH$a?&X@0DN&<9e_WQplA?) zKgb@9B&{q-rg3dsFsqaZE|@GH;fM!%E%SPt_dlU@mw3c)v3;%gi_byY14O)U z;_y>dtKcKHkR$b$r0^S7K?eoNhTIxq=&(ngBHj^crl=w-P(wkP5_^M=xUV zs{)6c?`Ut&(<=sOCv6-IvCr zU7q(V!x6yj?%6`l;ex>ihmXeNk?^bT61n1v(#xXldC@_V<7;|imS$C}L}WYhYPvCg z_#6B?A8)iBl@_-GeZPH9_B(ENFlr6c^P+wkq-v`V5THYefGWHjMaHg+^zYbugl^Mf zw1TLCERY%}hA%FpZ(q5eW#@N4Xx@J&ez1G_Nh$wCn(<=&t&gjq_jz%m3_EbSQ!)Rp z5r44!%E(xJmeTmI@fJaTr&_>B&U04Md+}skYp@L_Zk#R-Dd;TwITFSOxiDSJqH@u~ zjTdwppx(|Bc^4?!B?xv_{TfY2k;GoLh%7$)1iw*7F%o^Fx5Nq~XIElTG&$%8osI1P zKG16d0dkCIa@O>p6-)4*MvNxOM-|b#r6@waYpT6-^O*&6M%nNPI{k>36@Kz>%B*6j zGK+{%FfBG`EEY!wM_7VO|DxOuFP!2tjtqPK_Sg*Z-Fq!Ho=+3N?9OmMi^)xSsvq1` zK9z2AVQHdk{Gw@fN?Lh!=f@+ug2PiZ3cH)cnNyL}1p-397{;)msaxOrBe!YPor~n( z)8#u|fQB-$Wy-Zklj({atV2Qg1RW}ec;fN^c@ARFGMk%3f%s>_@>HY;gLKe->MgWm z0Y@VNAf@0B628s{EqEcJO#R^VtOCHU4si$tjZkgXJZ1ka z!?|wi<$6c;3f1v}U&Ah32bh*-+sH_yem9ffAGVp_SdC}Q{Qb!k8{n>K&Z5lpur-v} zR|H&!U;Ve5N+Z=X#%E4X$@fbV;nJ!8^i_sMxqia*t#ywvMR5qSkNB9x;H#! zh4?_cdihg3%cP{fQzBi6qaC$R%LYl=#%j@!nn!ZS_vU=ZvB|@$el0N}-#X}67Q6$b zAlX*V=uaA!f21yf)ZgTW?b0Y~;aYmUEI^FeoO9D0jT9Oe7O=5h(&JR#)QvhPj;k=` zuIz-7=BnX5FL)nSAop?B=*$U?me+zfaEL#Gk&=1$hY3=kI)Yh*k$D?WS@(&qK*RPf+-avRKWPxy=BD7xQ&2FC#N{g z)kb+7awGuCJr;>74kN7)S$aNuFbQXsL=b!Ms!*MQA{Ep;@gH~~$2If1M4+9fX=uLQ zp6W0HbX3i8ylq0~srQOU0-s@(VABizuscxbaKB$1a-ZkhR}t9N(d-SQYk&F%7ZUUk zmPnT9KqT9KDLcsNhq>leN*SCJ$KG}ln6H1k*)B)M-*4j^mYLT^!Fl7F`SLx6t8zVw zq!Yk6uf+|&6raaG6?<2h<`LWL(;z zr}zTNM{<_RP8pgZ#7?cK6hRnISMqLAo$;~Io-!LF3g8d!Ep{I?^Dm@*2J1;Mr)x-a zQ=EgV7|cwOk|?HY(Nu6trJgOCdkR)ESk*IAo?@51B^oQ8rP8e3-@=+IsA;HD>$EjS z8_O9)YohOrVFKUE!;TiwHcJ*4*YPYn#(x`_CB^N{ zF8w*>Y18UU-`_Blt@T!t2`JR+J;zJn^I>En{>xARxKfMFj{BG4X^dFA9(j3;rB^IT zL86FtPJ}obB%EwQr$%_uEX8x{C^K+ZK-_I#cE8o6e59cwdD!fq}=$mN8 zUfD9`FXDQ|QsKl>^Mf&koU$qx{CE-5MYhTsPu3l;oVRMHn4u#+^l>XL(hmqPI>bsL;e^Ws?5AjSeN#(b{oEbxAw0rF)J?#b%kBlE%iu98W10&c@?Sf1nTu; zEtVdInufxJhhIu1SQnm6F;yQh@|^-FAo;8$k7FWqkDVSkXAtnwDN zr7d*OuA>M1xI%Pd@r2RtZb1@2%@8JTg3xN6pym#}h_vabC&c^bEfAffd z`e0!$NA-MUbbagg#&P@uOFlvV8^6(q^GEeGJzWpYxSxu_%l`=4!VW1d2)3QyYJjC1 z|98zlX)NOZ{@3rq{mTh9Pz^(FuN$vG=Drv!(%VgkXphja#EU(HwjVLO9VO3bghuJ9 z#c}H$1n@PF@pH(>CW;JjG}Zf)kl`FgJxL*1Tn`1d=k@Gvap|Qwij{tMY?3s=Cc|{L zEHeO7y6hnm%qi zZ8$J!fhbb~Nz@6$!x%mb{DX@H1qI3%?Gu!q6BUCDFo02-?jqrmbG( ztyl;Ic*H)$>c4{nCg+akPO78bRWC9N# znM&W^!N0y94E(;KfjWEDtcW=WhZ8VMuXOat`K_ef9VI|RtH?%;kfpjL*W0ot=s&G4HoPw)2yM+RDmkedS_Ko9M z)Fnh_@LVk7MSR}+VLv9DyG{I7?}1ZpcCC0tA$GCV#Tpdwji2?l7_}yb|b3uNkoDoKR8Oc zEXc9nZG$++lzgXPggDtChaCA`{?fY=>neXeVcGX=sRR26RT6dZGHAnlwMsDg z_10=Dzc*^o(yEsIs`g*B!`qvmrw`kkA$_k$>%xQ;U;nt*@Qbh0$ha>1KcB6twW+Dy zr@_Hv==;z7pGVxMGG?da6GVu`b%GM@nk0{5Qz#Ie`s3$f#AK&QipSYJyGCo&dsO@i zNGY*w%N9m7lGVOoML8SJI{3|-Ce4TFZ995y;APee0bHB0Pb9JkVIwbmZfx>zqSwA< zIMk#++q2CQ?YR)J+$lK-0whbMAKYj8#7&5Ug)|H7S^}?FDBr58R4Rx3lFK4=UtkY{ zkxDa?-D2~^jWBLON>*8e-vP^oprqX;m7`!Owtn4e$r4pn1!yiulYaW-+H3odC5coB zU2Yk+5?IOVka2`ee3m*Hi#&85oi<#q)X%LBOC;`81e4{0cm_GyrLG&oD{vz{XrihN zZRvN%UcUIE{fZ}vTy{)N+9eMbf3}DP4YGQN$kBr*llveKTXb>Xic)&*^Oxa7hO$$l zQw^z6HF|T`&D*+&tI9l^8ANyH?>{lJ_(9u{wy2#K$-i!W>(4-G0{!NN#Eau-8ZlPEaLXmw_IjO7QK@|F?=Q9A{~E{8!aUIPjH|_GeGGh z70@Y)gX&L}*lvsmjtG107)4w%d2wG_fivkLt&4X1)We}I^#Qr?XymPuI$yIv(^)NZq*;g1s$5*A$~&j42UKA zt4XqThrskrDohYDEeg@vD^U70uSY?eEttON&Ns+Py;oOAZ09Zh;G)@Evc~)NSWLoD_E zn8Si;)D*A=>s+3KHb2wfm{(eO=H_LC#`?$RtxsE=sP4ySn#o^+jA$oPCbCkkdx;QU zvC6DCPC!xcl{g5?z(NB>xDjWHRTy_vIVWka+zq9dRqoD~9?B-1Qbzy=znpMy4jA?n ze{;oU|J1{W%EUqB*nz0_NOXX*KlyYw=DQY|Z-)LgV)D30ANEXT9^4t)PG7agE3CIM zn-Q0323=nNw26!)?)v}tMWwgCbRi_ctV-qqeUy-(Ip)oUC+*|(RX!ipT(^s+2Pp2= zC9I3i(m+&WbTLVgz1adn1OUaj;5R?{Ju_H|SQc0`wvrTv$iw0Cf+YQmKaV&2kYoPZ zpy3U}E^E@I*kCJpvF^+Ho*KcjRnz_v6Q+TB~P$?ag(iHGoU5|o1T%vz#D@8Sp ziYk1#QAj~neExvdY#oL*D;gFXPAat;JUjiIl1CmQ2a1FY@Y@dR;c%7;gU^4I$*En~ z5|LBgS^n{~7hl1O+_JqG3O=iJlY&|D;^idL^MfV(JiIDuJ5^#A70hlbws;6VqoPVBj%$FRCG|r2 zQ6LpoH8vtki&UZF5#s#-Abx_a38#b(Cl$5#ITRs`%+5I>!nwRR7s(FhAj95 zSJE{sWG&_DlSR-~SIE_GmbC81Q_Jtk=-uAwmV{h|BR^O1_9tstRl_W-x(K-Rq9 zSo|QB^b7@=d}XrCHSF=UR)+bt4-D2IU84g9deZPzKh~0$OZmiH8n2#AmGtdQ^pNai zT+HjQxc7|6u-<&U#xq7?ckfKTIymI6)>#c(90=wKt#M8%|KnP9#Z@{|eM;Y{f5MEF zp9}IHPkEDsnM3pXWP0{#EHv_LD0QuBKej1g%nVjhN+qmV@rRK<*uxItXQ`Cy-V_+02hKs+&t{bJxX4ImW3Qu)oOwW)~#vF6ZRn9-_#b=4DT@D{1zepP`5>1DP zs%WEujY=REj2&oY=NgP<6(*(QPTY6McvBpfj5!7OW&Rag+}G@{zw9DojFvo=ZePuR z-WoWQS)P#i2<1T;_Vo?@3ed^YwyQwLbN{x0wYtE?NWKXL#IuYQv9CO4Z?=Yf$dA7;j1v1q{?HT z7ORx%roX5k74iObqJ}W2SH0O9hiyXAQJ9ONR6rl{UfIcb@zE!L_C-O7KqnXh_3tFW z2N-d(KJ@G?G)is@9KUlbELbHc8(Tyb^iSs-#~0lzv|L!?^JK=;$UX%v)19R5;agG9Gy!f3hP~x<{WW$ z|HLW*2;~+2hR8{RSpP4F`hg>t3lfb+DJkD*bfPTJbzh&n*7@J!-?33($Q8fXiOdV>>FVMM>fXq#24pPg zU>_i~8i-tbil`iB`#qxf8K54saJ9C=)pu#Gomv{f3+B?Ur|~Y~_h<17fur>6D^GDc zPMQSa_D)6D_F)P3di1)B|1b<$HGqD-a1a_LV(8`278Jk?SY47?J<#k~l0297aEbJJ zuG$7Y3Y$+RN*|z$K9<1Lt|Q|4rxPl7_5Ke{ zeamfwmb^f=MmZfw^i6U)! zN>&&J${u6T?UMnRI9v0iU<^?GOF*N;PQ1av@AFrlQ9>xKy}Z?K254NEj_q}UPK+gO z7TPq(+@#dxF4PnllKbu)&yq>@oE)DfqxE74E-YWsLNd{I{uRH;rwRG4FY=#fJ^S~<-?{l|XQ_42Gvuq(U^w0M*3!8LZ{=r7ILJj6?bs23ys@+8di~tC3UQHhi zshDD{*>NZZr@GwcBgX3tOHZ-3?R10nA2woEwjS?x~Iul89`YAb78aeOvj8cCi*~_qVjB@48&U^xK zs(=p*CgP!9UmUESl1W5CrFb`jw?$NKnBQITV|o!F{9#KMq7(z6|8h3a-2$U!kLivSdrIN|7p#1HPb+~OlSPYX;knRo@5HmOf z&>zYc6u)8JB9P6Du`GJQjX)a1HQnqdABBVRP(TB;n^`=6wGgu<*s_uo-~qEJHuqT1l5;780J!G%XyXz~{|KU9PK zZUTNexIX;+c=PCHG~{vq3S~bqV>W`u65jxb0HbAWRL%3sJjx;@nd8Yzrq?vH|4bVL0$Z-tlV$R${iGL>cT{HQsC|}HY@Ug< z`aKsVCv6k9s>RTbql8U8SNZs&!;hVG`}bp`LC_A}6fW>-AfTrL>k3LTOK_sIFe*`` zS1=}Pca}^Z2a!+L(x)JXq~$OSq4!b)VA$Kc1fuYjWpZ4D73vr!~w2Y_Le)*xh|07>GUrt!>D6NSep{S38HJj7TZbzIA6$ zERIj!AD4&ON3Ey{zMeijZFk#F)+ zc>3Y5XTxlqV(UQvGZEf~*=}1j!Zijoj6D@9w4>3#^cW*3e`3}?LQuw}M2qo#^_B~! zf5kuX72o4J@8!#xyQ@zj=Xr+r)_Fl${-XOm!KVT4=Vx_^G6H~YljVcd=a>_Ffw`%9 ziutr(K)U7aDXWF&DS$1ih0pm*{Ti6Rk!(B8fT5+rXPD<$Tbb@K*!qDp$E38e>LoX= zF_6e0uz*9Pfxp$o({ecAuukSn^S@fx{QZ$hg2V-J#qh@yNi@&;xkRHu6j6|2x^2fF zZ5Q#Z)B9Uip=aOL?=v@jrMrF&IoyBjdvH2b66oG&^fcW1^nS=R_*u)knDpWLqR8QV z`P3{Yu+ou^z~Om#K?Rm}N!skRRy-T%u{QgG-T-vF%9ak!WjYgDf1IKpr4UV}*aX@L z6&if@FLI_%O#;opVxKZzarF6mM~$;w7#GcPXH@4j_#74=3r;r+TaY8Og~D8Zspgk6 zTZblLoPAmyp=W&rhC6Wy+N|xX0ZS2_1`0P_iI+8Dc@nKTg@NgRX`Mz342E_ zZfvXdE{mKyonCRiEYA1S?A}+`ZzFdliz_nF^RmEY4yK*3`}QVwY8M1S@G#!|{A$ku zOcQq|pQli3SdfT<)BY5eqvZpS2Bg_A`L*b7U3e{5LHUf65XxKVI#zvX7 z;b0(o5!^OX_KJ}eo&Mo+H*?tk@1N(tcQJJ=iRRbUMJIve7>WMceXVzant=eQ$;@Di zyZzJ)tqm0$HVA5qa%=Um8Qq?UCxPl?yb0g*^;E$Tsr*Si!%^84ufALeI^cJ_+x}x znLiHJ%Z{8nDnjp$Wb4NGIerv!=;6r4DgAvx>KTcp!PJ#qBHn)Z`W^<28aCrl3aI1u7W zGTyvxo=lVZ^0IV_$f0eQa zWtto4^DfW;O9-iY#-9eSXx@^A(9ni7i#5HmIQ!OCBlE?}V+aXd!}C?=%f`5UN?d~~ z<<(;bgvpqj$yFu%hQz_EUlUWMZ&1c zwBl0Mzgi7V~B5*ce*ai^Dul0EH17bWw}+=Y&V{lWg7R%Cq0pt@kp>GVN{A3Nt(Fe zKe-q4tVHM9YMAD`8?5amw||o1v*zg;XVv+EPkx@K2mO5b$WU7!bmZ()D*l1OSS7OR z7u14-6Ah#=M|q$W6*yi9trtmqKo7)#{NvP5Y|CjEMq|HEr29B5|NY*Y+lT3FS&c=i z>nkxf*_5+(d`zzPNz5YcIiMc;k{Q)%t@D(!1*}1ww3p+86?GnVIkJ284g)mLUeBw(cYhk5O zrLf^-Qo*~TlblldTXqw`-vAYWCRH~n=g$As%6p1#dubo(m`bLzm83Ep2bCKhX8%RF zg!*xjab{q8yx6w(F!^w~9P)eqvdtt+X0_?|ix1sa`l%yzzfLX- zk7leNF8f?=XJqPDdzT*!KUh3pZYMrdTsM-UWhx0T!f^^^U!ddgq#6NoN<4oT09JgA z(&Qjk$gZrLHpxC_O-0!C&s4GMM&@E805J=?YJ4S=RM0Bvj~S5iFQKl&b0bC(*!i1f z-`^Km7LtHkFPE`Xor|@zb&DllXe_p`Q2(2}Ja)f7G41<%^8KOn{6gTX$>`@WEp+hQ z+9*q~$K~EtRVy3N%y>S-)83}R?=Fnxf6!>+^LyaFL!SrPo#lzA5EjmHo zStD1CN4uKq{A#S?Ag)yF1)O%Gz?NZVUT>WEP_w4B!jQtGod~H?$EOMk+*4!5Ds}Yb zZachoiu8@i>>HLOoIRFkm`zbO{#UM3ICoUT#L_?$Zm>ci4#VH8Uy?b$4M4_4)MFYb zP_Hct-f}geBqlyez(E*4t;k`Ex8-sI&S}zG`$&n&m)wwk^W)Tf^S8z3ilL0x+u&u7 za0VCU1)q=AJ>XQvDS{D+=lJQ3wXI}qHuIHxGfEMIl$AGGA%efbw$JD90O{m?W3GbKM9%H zun4#%_Uy!^`N9Mv^95$Zn`+FRG@@PjsyXK^sb6H*eg;x+NE>|>KAV4)5pp{!&}(#e z`PCISq|5uX^W_GU6zd0bycY>)bhz;~D#i{am3AIO4EG_|5BDp5*Zv_U578It{{KRH z?8x1a-h63OtPOUT9LA;S0zsX^gzV4#GAPh?oFXj6Svgh>lC;N4tYI;;h6RngPW8y; z8)6(oSkC|oJ1FGJrZ6u^Vp*flb*JqbpuyLVmMmFrn!}k&Ka1_IYGpMR*GR0m(wN+^o>r5mPk!ckWZJ=cKSTP4HA|x z@r*8`Nd^PM>f5>_3i&g{i7Z3eV~6ZgC0{y38~BkduIxexgyr!7)FXF~Xy19!GTtmm zSXTvjluX~97vW%4p&d&gZi{c4#fit^vPx#QKek>w3EmR){kwhm{%|3492G}H9;e^# zt-oRRCex+GKI0X=*N|=&5xxUhM{ybEJgp=L?u^lQCMO(M$W1oGQbE4*i>Ydq>nGWx zysE5wk?Z@I=X{&Q24Gf%EEBcm$=z<2)~PV@h@RMzYVcpK>87d`PE)efEL*+6SH#&) z$%!i&=$L1z@r+c<9pH~l2=@75?tAm4+BG=-Qr1|t0v_cE>r2B{?vLwc4A&9os%~<% z++{x9$1ObR!l@75ue)#mou0V5_xnddf@$=#^ss_SODN7joCb^O9~0A+S1fKD zyRR>pzTYJBBLwgeuvxxG0%+yp2G+f^qJH~S6i2^@A|rQ#kKSi#U$Yg~q7$Meyg`uT z4aw8^Em8Mhbs#8XJBKz4%ICOT9A5RSp|5Txv@mfoV z!K@8sx|k?Q%AQP6epi#YtST zdprGFl&38$cxjXXuS`_xL_PbfWaJh*!OZ^CNZ0W$mGbGv>)j7Gi87l~L5z+yjch;i zR-FA?=cMA;1Z@$;i8VeJ!IL-~CEEB|grV_HbslVFL|!AF(MobAzw2_Pj=)sSi}KWY ze!~>h&_acO4mjkQB-{KEpE-dVL`?w|Sd$-b)1KB);AmSCHvatq8O{zuEp`kttdBPh zuaGttKYVcVPj_!&?rUU}eVm(V%u~fOzehO>!+neAEsLF16pS>KX?8^vLK(=h$X0uk*SJD0u<0&vGpm2AQsLCr=a*RfIH&Rk1pRvRA6!p5Z3f zSR%5HM8oq-<_)ueLcw~CI0%&@O(^I^5C5MNDJa+A<~_Mgh~MXZDgI9#Mgs6JEKIk5 zQrF89n9L^xaM9J(*B|ONpO60!V>xUjp?b{EcorUVnQ;-AFTr~=ssW0*)bT;WhUut= zJJwQhfYo=#TnJ@3`B~YDif97{4bB;t_5rm7?i7!9JlyAB#tHL3NuB2OW)lt_j`2Sh zvUP_pH?yKJme3BD9u_+M4Q#e1Mf5L;wp@C^ew9U9sFi5MP|3O5mMnPe-Qz|wgWJ@6 zrcMdLRQE2i3xmrzWo*-mRi=!^O~+p9hq0H1Ii7@d2aejog?psaScQUL4uI)dm$YzY z-&iF}6A_#$S(-t5TNcTl{M55BB!N5%$d?!jLdrvSlA?THtQbV7?CHdVVRDo+)>W%~ zdg>a=;AGor4{f}G@?`ZANXvqH~PQarG9sP{HD=0W5)0Ky(%0w$UxiA zdaMnh67|Z6H<2G$U0Q{e@&_yA2dGJ3OrShVvpo!#UR5EqK`A2Xa1;JO$8)mupp}0| zfyfJ%E}qNl4l2!l8!7_aOUEs`ZxFZf&rseEwItkEVwCqnEO-hi5OvsV_QeT4XTpLU zCRGvHs2FM@%vf$ETT|m>^Zi&DFCJn~JuI=msa4t!NrTdohcbTxmT%Wd{C((3?99L3 zZvAiDH~B?RW#!D}v47Wie=zp`rG=SzFs!e$NJ3r#yuUmG%wPCVIxK*>oFqJ~cO{&L z{Cs@MQ2oKdE>!D7n0WK61wf(J>^r0^nQB}4sxdw(J7Z{)qwG+;ALBT7m}>s;@XI2+ zwxsf;h`d~9Qe5}!8q~mdb}J4s+1UD}2O(4R?&Bszgoiq<8jskh4vxOt6p~IA;TuaQ zEKNcs8abK;LaHZlDv+7pb(pZHXt|DqNIeZ(IfB|p@~eL^R$VAg_E_2KoZP5p@=aGj zH`!Pik;>CM{mi+^A}f5`=%<4NY!?|GbaQWe~vp*ZpMQ&bNmBG;i$^Wd= zh|u7Cb(9Fv$L3fNn!}(eeNnMP6qOW{qzFn0!3>5}|0QwvV2}Ac&V>Bu>pR18{2|Ea z&i0K15!LW1J_un{G{*MM5w~tc6(5<6lVbZb0hc!ZQoct_-|Ep)QS0keg>ws=8k6+c zCR&yV#{F3M_8Y{hW#Ha*am&lGj0*Ibw9vt@vUDzkJT_@MEOIQAXXT@+qj;N=wFcL+u(RW zj&jSg}d=j(;PNP9B%eB4cu0VRfO|ep>LLpT+t3V^|<{Cm8V~IQ6F0ZZy%IyB= zaYkMHgx>RNOv%zk`EfJTD8CeedDTzMz+Ow0K6N)Yi6FE{h@*CjQo&dQ<0vQu$4+F{ zwJ7{xsvK$6ir3GLn0D;pM=f=4$e8TXTY2q6N@)>V%x1shE@Y#*h#eBZuw0eB%3tDj zY<<2TYYrgPvW_}h!oPqF>SF_(50>+RPBq^BrRp=<9BqHLRq_fZm=np)^&1=w2o<9v z&>YuVqH0!&T%mxBe&C?6X(!b~B>;9}Sa0vp5@uL__SvdgM2A%>2A`**E@^@t7Y&DK zVTTi0RFri}K|d7Y{9qKu?UD1iv+y2eh>w$^O_;BUOh_-Y-0 zNpf!GhGwa0_r`iose2R;h6|%=mta8%4qIxb=rTALmsp1*k4&lOIN~!1`K35pnh__f zlobu3QbK}8Hnc;N_yA|Nkh5hBt|*@`V8hoY@aiYG`{DXd!oNf0m55AmJY>!H3@)+e zd%e1%#n*A0NKh-X`SU*~7zEErr zuYkxfwFsA=ObS6(DMGI3Sv+MlDUWn)QmZLAC%&Do?lv?sW(Y%apOr+B@4A3FY$MuD z*V7S{m1P!X;XhINE_v)5TN8TEn%?DRch*JoU)tgV;$=)u6SnfQ3{F(X`Jf+2TgIu^ zy|{BNzm7`doy(8Yl6Z?HORJlf-`QoErpw>?Ef0E3cS;=5#-Hqmhi zx4ekA*wKezc)<)}%v*eaAPcH=f#DjayxRI3M^#rNkB-Mv-v3g4Xry2E=%^*9tu_E+ zi*UgR5l1o@gaFQR4K#ik9GZV8?4jr^C8tq0U=o=0it^RM7q%j8c?z_~YutN~wSjMh z+HupzmwB!QgHTE1V!jDkdeui1ywfTl(%JBeu-;2lQ+P81^_W@(Z{jr3Ke+UFW8*C8 ziw=brBZ|WDx00i}sgzueh$y+LUTEiHl8LX^Wt);F$n8-LovsBhu+LxFL}Ec}HfU~s zZj6b@tdfE5yMN{hn3$$OA1a%bMiLpxE<0H^x*IxMxI!K*w^Yr0KmNb@vLB6|P0wY1 zcL?0YCi2Uy?h2edi<8;%3hbO(x7dzN4;21*>65NT6Pg(yEYK18^bN)0TUkW<)3$-s z^#hi=U*B_Vg9{yNctTpIWOQ~urH2+1<$il<(=NZ7Q@2f&2$XFxb4+;EsrxR_%W>;y zEqk@!<5?6}HJ05gYFQ^s+UsTi#HaX*aNV&y*O#ncyOb{6*Sz1g^lC$B3J}4qe;A$5 zmBm`$=zi_;{_wVvWFlpZ$O(s*$sI^ncgkG*gFaQ-JW83}Er)R&-=T9(%IwGZ`1`F; zH^a^m%E}}9+H-QluI3MqKMcjjHIBPYJO9-5i*=X3c_Isj4CGgx4UhRkJ2^jZsV_uC8eu<4)SL)eJJomGXJS#unQ(>xS7q}2-JD6i;l$a|f1=o;jyp*rT3wg$}4 z!^U3Y)gU}EoSt2tj*jy#8` z;I;6IZ}+i3W{CU)rTroYj8w;&pID#MaiBG>{C0`=3d&VX%@=JXlnd8DlwED~XZ5N~Jq z*DvQ{ra?kj^qIbJG--bNWOacKBFc>-Vdbc#3eR-z6WuP7G9y;P(PweG4Gn4rNsAWg z#84RZBaCXL{^VaO-p^@nUO9wx{u~u^KXr2b>%Wpxc`!~HS#KuY*CJm3a8Vt0zxoUC zKbEpK5@);o{p@D&EE&jvj8Ul>c<$7zMT4q=APOuFrEzl$U@(aOso&t^$dMm z@2Gm890R9m#~a78JP;ARvBXa@AjEz&w&<4_YWm?N<-Fe`N7>dY1caS38PpP1hhaG< znRtDN?G`@gAs4Tdj{X%xtm7q3ztLCuP0l~<`QZBznz^{YT68_TgKIOZgJ2T<#nk+SWC}^0lqg95HmzqC1Dz>jUdRY%!22emU z{uIMFUIFuGxCTv}w01xcsEcJi-j2HJYwp+bU;c8)rC}K)L@WE)Kv$55 zI}OF{I_k)!<`B248gGg_fiO}_`d5?~tv_4Jy#|wvcX?x&=;G6e^Ew50k&U&&O{YpO zS4Fr&iary*=wMj5G`sE{O!qFVHjqN^b9Q39E*cckVQ2tUD*rN z3{YP`yj}e})odE_kZ5sjwfHWd#l`c@>TdK|@=)=k>3s97{Fl3%|4*1c$J!pP+nU*M zz20`SxY$w=jm3=XBh4(lt_H6$MI%fv;^P>fc?xI0_|`P8{Dd-wl=OSbiY?z0suyKm z>vDx~aoVU+$y?S{Th^5#F6KYs&iaB*vqThf7B#D+5jPafPfJ?bU@$SQ3GYo6gM zZPh+`de`>#&fTPQ!<+or<@Zk)&z~)SpFRonHwy4{bGdYH{Q)Edm%{l55=rT_DIck| z4BrmmGm^o*4uM{{z*q4rSy7te4mZNfI8Dw$v}k`yuQP&**)1D{ z^FXz?F!iWDMt#odv5{Wjr?Jtyw8h)-s6H~pFQ=&itU?~c09{WtAZNW0zMQ`CQ~LL0 z5hh5fsFAUE@ zX8Zh_wc%A}LcX++-_G#Wc(D?hMqHl6o1mZ|A6u8#cL(x(Oqz-I1Sy9|4?gBZg0Mnl z;el4buoSJ^g|946^t=v)>Sd`sZOsU0JO?IsdcsBjWqp%jt^gi07KO%iR2Dg@KO?D6 zI7xK8K|n;I@T<<@_6EQkBD_UTS_Q4D@eC|1BG*uJPqg#ewuyj=rs(MWpWnn3f;zgd zUHE3x-dg4Z4FE@$VHIA(lgD68V-2rmSrSJ#_QE22leO*VP%f0JkzJICRmxG8Lan97 zv{7-5{-ax81+KEob(;8{xF&f6_35L@kajiJ5rDSylO&&X25;2XWNG1|BS&37r!{;y zgX?&-%S(iw8)yNmme!#GM2fL(_b!PMrMApxLm_CDf*U~rl8=P}SJ9Zd3h zh`}9G(rl8ks?j(y9%L9Y7;x8*pk+EzFbWGk*gPS+A4&APPn8L}zqOP6>uVNOj3!`y?=@-~&nTLiUM%JK~QZAb(j2I@nb>nY>IQE_iq?D!Ajuw_FW1+ ze9a?j`SK+FNo*;+Xqyz>KVdV-{IRAIt@>-r5IiLQ+pU(@^aS)el(FCuoa<9v{v>(9 zcG3BwMFu zL`*mo<4ypmFxzDkbrBdnl}+|0Or%QBo^QP5LaNo%x|+UG4%ei}^vkU}=z zpTY}}MH{HK*rG!yYBYw4Pf=!Hse*5m3Nbz_2w-Qz#jxtao>WLsdlLniS}wY2*)KlG zQ_DgFdlX=$5E(T6C}Pw5;qCI4h|HJ!uFbdDF&=*hmM$*^G!)te6J_!~>@tY|-`yoV zTZ!e%?frf6G)o8=3;4_5d;$EMvG>V`&OgcSTjA4xCNtzcM=i5Onj86bFm0`pMVOg` zC5+eGRA%9<(Yu-)P$|`P2=~zSE}0kBS<47_b$Q_;E~vLgW4=uD_9J~@FTb9(O2(>4 z$Y=V1xeUHT^~|=&*wrBcG4i@Lj+m1b(vxVITIa$uvo#5WAJ2`QBn|AdD?Ax18xj^f zM%rE{>fMsNvPc+y^|duUo0{HQ8R?&E8_F~%fB$tOQn>d^*cSss;(?z6rHHo=p6XPDacyb~^ zD@&8i;BVg%xsAdzR-)l!@;(u`IUM+~ou-kz$9`m)?cV9qD{smvOg_J>{*}0RV>gu| zm?6RCsw#qObvE#Xj4ozIKTNorQxVj}OOZf`NRDQRTOe0Sa8H$FBDLSrQO#I!{ z08Jo>7sV!em4tB76|~cqoI->&M>#XxU2{#Vabcj19rkHac}qrltb9iaCz6PN62HG( zOsGxB66)=cEZWR*g@2nr^Gf=<9IF+DqM>@`m4ROr3>Wl+haPm5`xJK&?e#HJ6GI+@jnEsuM)#RJfv2`!a5NFo$>Vi%rW=PT4-`axC#eX2ro9M$=VrR|2@E-^MmlFx*;tsq`vV-Rj0e zW@Sw=N4-=`XNMGAZ*kDRu~o~;s<%hEFiyz=+J2Rzs#Vteeq~jz!#dJoenrID=D7Rl zL1R{~T{ladzFHtG3>m&DrQ-KGg{SCRu7Is}86RwSHK%iL1|+d)nWT%;JQ$=7r&WfG zuzZ?$qfr*iTJlph^F9bcO;;&oG2D;t{1*3RGniToBJS%ZfH56N6sF?AUB)x;9d^mHr z3g5fuc7B_;S14K|l`6L(G2h1Xq~Mw_Yl$k+b7K&NA&MY&;B==!|~qP!~%oxEFMYpFpxg_Ah0s(*}*cS zBF6dpqz4CP)$zG-w&ZB*K@|HL*d@MfK((YMhCEs+ee%n7fa!a>iZ`f402xHvjUD1& zj6{QlBYu12`zhe`rsHYd#Y`v@j8oOf%|Fh~l|>75tPS}o$MVtG=oZ=EG|D7`keaN$hC%sH2VaB0ukx#Z`%hLpc$m;=;N-w*O{H+O|J5EeeDCF0K1q?_Bq}Fp<6+}HRvc?MXDpmddK}qn8_3Qi z6G5MzyN7nSPVJPU1%8Dtmvfc$| zJ(G>>)Mxqul?Pv-P3337aHClPlyZp7cX0+)2_z(k+SOiEt6!{*ea8DH+UH*x9F+q8 z?GOc<_QqkMOFKVCO~aDpLT`z1R6?#QwdqQ1)i4RR;;6)YN`bh#YJu%Rb7^?z28*EBK55 z&R9v$;8Hs2K<^FEE?gSbOLn-WmSf?^A`)TJz=zH@zF&8JUC)hP{o76rwWm2O=Rc9L zHGdB#*wy2%-8_UrZ=ds}Zp&N!cAjS;)EV19|CZK51}w7SXi0JPTc~VeY;-R9;TaX>!LP6RO)2Wh@qs~U?5wji{_JXJ9%j8C*wzu)ya)Y zgH&w?DORC{HK^JwZ67szY)?LUgUm1L;!a)f9@l#^=j;s;>{C5GZ&HzbU8CA=Q4DzU zmAOrJutVb|Zs9+rToI=O*p*Nch-0bc9aZ$np(Ee#!ei3%#vS>?Qsp-;Cg}+i6BCxS z?_%l2oWIZ{^YPaaEffAV4COFfd-pgg#{X{r_?XJ}i1a>>McqPq_Cb~fc_RkRTdd)# z$wWubp_f?R6|4VR56MB>s|*qHLXu@(B5Vkp$esb+ zAKwTGpEg2nkbdGtHCP_|Hq$Eq=@P2$BV0JdLG&m$zFG`&V_Uug8(^otZfkMAb3d!z zRq@nb@Nb(WmgPS+Aq|k0VTw>~y$W35aD2%M$jtZxJH@w!2Pg6Pr#?wnL}tZ@y-dIq-()+^vL=e5=b)dcQDa$X0AW| zcoU$5!8D*KEAx?gEuuIq*e9d{{DjcdB<0I5lM{n@xw0a&%q2ibHf3SaWc|hj%4KL45(8>s3 zl64s2U~jXEt2mJ(&&-ml&$;w)vOs#fG)c5@C{sW;Tn68xXiYJi)GX>CmlInNytRMH z*Rexn!;g`bZL@(_#S4?;c9z6^Fg3PFLZ-9r&1su!CTT3~tBx5y0TER9prSdg`pc)8 z-DcSvQ|R90x!2YK2 z?4QvOWqqGN1|7atf84}PQ?*>tTd5|jweF^BUqN%rA-_XMiJRARZ+k8-q6X}&-9L7= zcEiZnGF`ch_O6wP->wl28KSupQX?ozf^C%CV`B3lsY zk9q5HQty2%akDx-ZFoBqjdTqsQnwpYJnvwmlk?5xR4_9_ywyJgluxXmYN*$co} z`fdJjv*9yV+a8#`Q!|6~3Z=p?<{Z{RJxc_Bbe5@0%>+O!F;+;k8uH*_3Q*Q!+n!@Y zVZ%(BL%7B70@k}%2S%1!)bovRCW6}*uuV>9gCQ0x);Gts9*KjO*+3F1_}{qjD6J|( z5x@InM&a{RyXvxbTu(_GdnZOyEoUi}f(~H#ePjxGViwdCZ*ifUCJ7NX~kUytWLrcaS(@eYBiV|B< zpK_aIv}w7%N#mW*Rt4iV@1ee@M(L5`=*m|i&az%ot>3`ma-N&F{BjRm3 ztypt)6=VAZe&vNZ-$VC@<(`IPZGYi>@JPVdn>39y=wme7uO<7ax!Z0HgM3=h_GY$C znmxQ~mt3+gs-N_WjP}l$`oomsF}=0nw$j`cmX=OyTux^RTNONK?s$xq&FU`d#=Oz{ zQj_wR&nQu+=LRV(y7W$Vb)^*T4qk^l2}(7E?Jv>Z@?;yqSNO2()E81p9kJREungMW z%rr7{GYvCzChaR(SEfzR21k(g%d`1?&0M_m2Jo3Lq{|GDYw8+Lqi>4R&V*Jc4o58f z(i61T6q2Qs8nFH(7IRxX=8Zj@O?mn7?dv}@+lNPWl>_tJ*Y<1iTG!-Nj-?AZ?(Lb$l6WN>@5Mwh zBrM^HPXCa8bVYp13gn}qeTUA&4ErhuM#|4qiNPPrtF7ToSmJVF8JTtMeT(VT-}>P< zwcwf@Bcmlsa}g&x;c;0A3fTceJvZ%cgz8(LEHNJ_HEQA8k6l{d8CykCrcEUOMmJhU zDCZ;ug|>yn=^2^)aZP8&8`9!W)@VT6DklN^B23RnD!f7S_$YwJMYPVjgu7rPzf1r7 zBrSj4tJz9qHewqW&S^fbEJW;#&*Q(&p;z5~;c>6JJH5xwpW2`Ib9w@>#rZv)oz1Q6 z>r9N>d7XAsl1gBYC36P_aYOP7d(t`p$AB2~3g-i_*8gjYsHa zSIWUBn}y9Tgia%PWh1B8ORvS{mV7;@)6JqzWBi58?h{-y z2@Q0^^I6_ooKjNhtJBJj zS-;5v&-Pc0;ijQ_<*~9_Gox{2ZdfSMu_zY-7wlnC*X$NLN0jsvW)!V#~Nb{n5}X7++=5)O}xFkUokRy zm>ZIHW{U|l^T!M??1fLAYcwAu#kn^Hr^NOWJ3vNY4-jr2ll`t*A46j&H(uN|OlG>p z%;Bgy>&C5uG^#__FQocX=Tcn>3rn+ni7}fbC#+SQRP2>dn+x2A(1&3d)_g}BOvI-a zN{~!Mm!sWm(9sy+kKbm>EXjWvDRPJYTO8o+Gh8VZ6-}dVIZ1-i!i1b;YMu&AU97`Q zE{zZmp+n5Czwo7~@QsUJZd3Q?QM~1a%&PI$m>Si6A0_-enH0KLfsxidB+@2VC_m-< zzoOKF^Yo>~eMp%K1)0rSu%RC020c!_3D&j7jRNm8cvB63F#yzk|Mb#h2Ue3d5H-n( zkE@I_fbB6g8}_LyG|3!Y5T+#qd6~mpli}zOsNPSIl08r)Sk3B&YPIFU0Vu^Wy-L6 zl;l7I3a8WBYzF6?2}RvKcCT0FAd>eJ<7ay*GtGA&nYyozu`bJ{&PIFh~{PH?3i zlJ-Or)oOO7RXT?5BZVUd*T$ocJv2}kc=!UAkA{gmv1Bv+YRx^IwXo`Fu%Zq}uvSYv zTkqn|OtH?4Lu@6h3>zyA_FM6o4rIPljMvh_<+X-G+N7v-hqN+d!$*7p+gv|qQ?wCt zJ}L}8+v$uZAIWMj-#URpD=Jv2I{A=&qZw*Hb3R+^&4YFs&YQ#3$SWvaelN_T&Md9fU3_b6n=Qs-&+@^^sYK-^3VBY}+K(_>wb zV-W`Z5kN*=J3N;ZL7!?iQzMkd#zW%fCuE}zuSV-o4LKww>dd1e>OhzL5!bl7O5CbF z$bCT!z3k!^2Ruqq5AjL>Sl5WL>k-VDQ{10`N!c<%mG^{8K{eg{YHHn%TD0$aylG*t zWEz&Sh>G8%1yz+a7uSPxb<&+8#FK(h@^j)|Yf+D*eBbM$tj0so7}Mg|iGr$sOLeGe z&k0t~2%aLEOZ;wRg`WFBMvtxcAh8wy>ZukgYI2INxkBE~h@9yE%wDem7AJ}wQ|}+| z9%ei5CmRRTKiZ#o-x<%lZEi1z4$ftFwLr0&5{)JVy&ixa)oJe1h|Cl5LLy>N9y_^d zTjq}bLglaFeLu-jkK;=&it6v=^lai!Ky76Pvf6`}4pSS>bK_VYJr0Di?c6%bw|MG@rqN za&n7m=Hi>!Ea3t$y8^on1Z5{V>G_SotwHj-Hxu;e?M*8Hi} z)o6-(Ij<7#F1t?=X^qIwd0V$K`W|;7X?vTPn|;;8rtGYCdNCdr*VZ%ih@X2^O?Cl8 zYiInayRL_v&Tw#wbK}^~tv-_=DF^;gKa;d%8mZZU7H63C)|g|AgVhH4pS>20n>Y4* zfQ6o=u#3x()1`SR0y;IkJ3LEx0OKkd`(8+Q0##JBD)*J;(5w$HL#7ilcp|AZ?DfZM zq+%DhVzg&-sHI}D9;H_{Fc{*1=jQarh4rLSO*aRb1@@X-nAg$itS3-TAoyEpr|6RK z?%|LwG=Sm{d`45E5tQ!@zY#?Mk`2+ltU(GyYU$$$z}68Ho(4Bi2j$}5`?lAU;vBcP zHy?JyMfqQ)EOFhlMSg(temE*2`N}~Uq96n9`bCWYm+c&@RH1e~nj8;m7J6PSZ$^w* zy5R?@hg(-2A#(cC6(RuQ?$rU;Ovp3yt+T*4oaO~M#|oY8kwY!64`^lFLA3G6{}GDm zul52@15+qjcso{u5Qq5i|2h_Z%G+No92${BKIE*eUq6%FgnNw%XX$6az8{qDjb!^X z0pw%N^d5t=eS;s`jv+bYe*UW)N~e^nGHT@3TOa};#+l@klx~l#eV}lDm(=SJsNBV9VI?-*t*V}m73nv??gF<9KPzI z0LZ>sfNblyLq{5$)fkI3X@<>7F6vxxaqfM1(U4EvZ>#i$xjTRUEhJ-sbxbe(uqmsj zBY!8rRSbq_80gHT1~FFJAwH*LrRn>`l;r9#!Jwd4)9(-C zpWL0eIPMBGn{AS~`uF5~7DRZsZ+0inCpXuT<~xNvj%OeMzpA#KQT=S)tS;a$oe-ua z3NrRu>{)(iTEV9{Esl6)M*v&Ec$)@5@_Wi_$qaPn*I1ITWO%fe4gwe&EW9fpjFoQc$f8&4kAfe%bqXGnU-gLf| z!5LE5UeGKmvde>0J{APmtlS;&`Mty$J}-vd=-Ddej(z6m=lq>oo(S}70wm$UuF^maY znq0q>r2Kh$mpU#h#b_pM<#$czaIR5YVVVXmKSMiF0^7tb*6yTTlv`A;R35!sa?M-$ zgws`!-Q+{$-%|)r0Ey(UMbf2D*xH)%BvDW*_Dhv+wu-FLTAa_s_U@)11ne$M?byO! zI|%PYgbB5@w!W#$t{gDbdWnphJEdPsaOrzUB5Y)cGsq30Wd%tCg}Gu1J7kZ#z#Y6z z6Dbe9lYHE*O%>oJy&2JW{Iz}#EAw04z*hwj*Zu1r>&C;kbqjB-w|~O!I}{9b2dWgR zEN_xSFv94x*t29=YO8;5UFeS0=*b){$N!q4Sh$_In1Zd3ZVC5ho7`T$8OGDyBv5t^ zrppn&!W(I>i%0`w$5I(N>{T66vNWQVFRx*Mws*m1cAndC+h_RCRG_RqJTnL>TvFuT zvSu*zV-2c}&l&TIHtGOLgqFwe7$#^^zs*NbZ#gUSiT2Z66+FWTI#$IRT$7$;?<2ao5LCk_~kWJRPP+Gesy!2b~U%v!vTR-<1Yy89T@uQ&ekbd zXtkg>WFy(p)eQ=Gx^nU>L)cdXk!YAuJsfMJFIAF)Z~_msD)MZS?>JE&31j`RLon*6 zzjGa;;g!XGqUoM4NHSYv6h?75;9LyEE`w3zLqNfZ!)VB@eB|zv+^r+3DgJvyU{-9T~z2FSlm9 z=~T8a?P6Yg4y0nXH7z3ci^kRd|L>Q4t(~6jbJ@asTL6MRJI57samEs(jFYx-divW# zI>4RtslwOB&Yos_(Av*sR&%^Z{rmfhir%kerz$1o*pPfM(Y(<7;j=H_Hwk_@f&Bc! zIXVW#CRQUgFDa`axa)KG7Zj1PHRXS23d#6Lv%5$tKAz+cf1_u#SCqi)SR!?$XhBh> z4uaH2pSHab9_eRLN^uYQDMUL_cZ1iF_h$RNoPV@pB;U!wy04ukheVAB_cclicibr-|@V?f9Ca`{{ z@a_V4`?L2(3iO)@%t^0pf`SbJ6OPOhe6ie{_>BdT=!Sx=xjm2iH<#ce4G6_h6^J!d z0YS_g&&2iAt6V8Y&pg7lUBptF9`%XPl7oReRJ-Q{pL=s&ueC3syecPysoth~sWGv; z7Qz3{G`oJG#r;8ZbGEW~{^$>!x~7Mw=q4E(x>2hUzXKnUmET}XY<%WV!i&8Y&lP%y z&6qUDo~t`ce6q81R{mpkNxjhIcR$4!A?*WlE63>RhkrJ{-%A<=Zzf=M zn2oby5|R^}yVRayLkK-7TjXJai>DoGr)PZJ%WgY{Udt@E_I4WYL$C?s1q+vI)1R%n zv||moFXgrpce^HC>;==Ng2H+G>L4C_75sHZfP|b^*Q#()UcygK6LP@;wML{qbSVJP zlp$iwQX%3ctc(78imnjg9Q}pgM%|8wS>(5M2tgR|Y{r(9Moq$k!7&uCHh!Kk=OcTD zM*%vi4k51sQ$=m~uzn)4j~pMmO0G6RnH$#ZLO~yz_vgbalE%v*yXgp~G?@dpZx{BC zso(ozjlnYR#Gz8>U3sXlCHFAx$l~?nO2W{I046Vs#rYOF%V!TOPczEa?&P+Of)#cN z->4)8PVk5PW373g_L-ks4TW-g|2u1F>*(Zkh&9*My@We75`^3)`4#fQ+}5u*AJ^Sw z+qx4(E&yI84U8^LFwzwPM{7Of_0f(EDilUiWI8n5l~=LHBkX?j6c1)Dn^A8`&5qUz z5W7_ZvZGR1CF}EQqX%tD$MkTj_Rgum<3e_`7p8xse_i8PwooJnd8<#|py;>t=uQ%W z^tQqnsLfy|FkKcVWBP|}hkZGqp849eK#P+4XmkHOROaZgn|Z*=MWeNinat7XH4(4Q z&JLM?DvT0sD%Pko%w76g)zA9{FAoPRtEq8*n)lh+q6khb;ALU0 zG+ed0qr8&7aHyamDR<{Gt~!}JJNu%kH`Z(uS9CGi=*d{MY!JXR+dpRb+|;8pHLGTx zWasMML|edDoSJvI-GCM~oQvwx2eAwmnn3uyU8OVBWQ(-Yj>j;!Hb@*KS8UIQcld=h98O~Uky?#fiAQ^4~hIKeZLdr%f-QckF*3K7%X zZZiHZdGAr<;Vp@v8ZbpsOQWEh#xZG)m#>Le%ieR(oO^O&;RMLSBB5$VuH7Gw2|@rQ zWgk(_6W`O~)Mv2zy_z|ciVr(AD@8_1mxc`D3|6S{^49-pz-0}=K*0>nMx~af7MEiK zWHYj$;zwXDKCJDw+S0Z81`IRK^ZsnTeJE>0dzm7cSh-(2K#(2%Z#MRi*s_(v+fC#G z$Fq3lG0LP;k1HvZSN{_Y@UBo;_O31{IQe%1@pq`AqV6@?0FJIbUnGGnc0Pl!G+<#I zqPh}O2uZ}>Y~?Q|+h9y~^Hu6n5u~egVe0D3?rHrSyH77+9B1XTYL;vlQ&L^p zdpS9(Mvy@Ua^@R)X1&(+e`As(*&TcaGvr>+tKT;N0D6<=j(G%v%sMU^WXpOPa@WA_ zI3%DyK|56JtTuZ|X~4yxOxCk5?j&wZ-SrVLAChe2Jx(W)z?IYOq+ULz5rfi+Qm&Yj z1G?~O3Lt<-mbm2y4~c!xvs1$ zvWA5xYB|u{l|g*=uM3W{6JSue28X6eA*3&YXp%0NQ{@+NsU#OSG!_>u;wOiPtZ#Rx zFAem*740KZsCukV;r3!#ItJ-Ep_()9XyHIXvpbV)P}6S_Qwo8Yu${`0_8D4>$^tg{ zAyDe=XEzJdVA&{LpN?B5+JL+p^9$T_*#6J$Ag}AgE-rRA$y30O)HK@ zlcbP-zReVuH}PSoSf`_dAN~*D+ujC{&_>_r_lQ-yfrVTaH`#rpLDG{|%@w&|w&`&p zB)5l}!iVWnDGy{dpr@tRO)q%;={&oXf_N<5hL1XlimgHkZ0S+{;aa_btoZ|tr(F#z zqB$cX7gP3x<8}EY>Q3yb>>KRcz(5p4t0(9y2ND+brl2H2lxcT)9t}Fwl(O!J!gwsr z2%5N=Q(Ipv=lQu4bHfr|uOx&P4O5(n29-KQ3Xi~#{~$}v%>;#ifXagYlZ2dpdfKFu zd`ZtqGWMjj5lm9X&R0D9FdHn+^h>=bu&JUZ?2H>fp2hl@1~VaYRc*8@Vbkth#``t> zG^IzyfGnQm^_rOQJBxpad1pYP5a6_6eDnZ??;;+APd6PGQJr=k55~n))>fFZL7_jZ z|8txP1rh$QX-bx)#uIy+gS&6wFn?Z z2b=B>%+DyM(Pgvv3*-2{Vy>p<-~GOu-y~Kjljyh@(ESF4k1hYr^{V01+8)*9SDn0{PyApvm{R~!v0NYD z%a}FRv0(K*BP}Bpb~5jQQzo;p;IP&F&^w<&;1s}8gP2K{OUNewK9ge^de%GccK59Z zi=0!Sr+*}^Jpb@nz@_2n>8u9*ad2!zF}Ka@3bS}l^eyBPEpxdeNel6T8cHlW-mN`~ z+Ke&I=6q4E9Uy)9%Uxg1kL|3)4tUWI=QueOn{(ftRb<26`eOw|D1g`VyZ5Q5#Ip3> z+eioo7k@+jxu>$TulFCr4o$HjBdp`IBwz*up}A(m_#lf$4wLRah8`(V`PruE^`%jV z6M82j`zWjO&JN>_*-YG{}q) z`kmzPoRMPD#om$E*(8gSKU&~obgu$7$`a(f@+)4DBPWKabbMjSYC0@4%m#ZcxdAsm6cLqw3=mwet^ilngK+^B zDn@2y>SQh(_A)Lfnh$?LH(&fV7VKU&Kz?@@MIc=F7Q5?MM^=8(R1yE<>Hk;DcvR!O z1B>ZL?&!Q5xSq?-4-4G5UG!^U`|Nl&+un?ZJsL=AvHd8uz)(>rc4zHfG8CN3qQ93^ zwF_=6T#3m^H=HrwNfmVtHM;un*>%(XevB#wKRh(Ob?(~J>*u0L%F!~^R7vz*({HmX zh4r!4S)rDEkqQ6ZEJ%y%=Hu2ma-iyooW>kK?0CQ9`+EW#Y#hqY+7h;6XcVo!MHJ(h z!DRhbSCR!#PwHxUgXr}FFPJ7i%pO^ku~Ha`*SjBwq%pJ-y2;3{&t|vB;$l|*xFG0{ z(zakcX4?>YGRE;6l7yI$r>8HwqYqK}u6FUC)8;7IZRX4`8h11i6M6SphFI9j-k!<7 zz{>C_%$u{@h2g5hxc13(ufjtAlU)}JaAb2j%on-u+w3L#yU6BJL-zOyXDl>Q#m>sjO9hfFbT7WRCxrEm&%Kc4 zNUBONn8ZPOv|_WE_>5Wex;O6a;`*!a*+StfA1Y0~?CoE-xM&V5FBzZ+ovnz+AnuSe zP8ta&`fqaOR|vFyC459wHBE`P_pS7MR#V;cXashRmuds(99+SQvv$u`fJP+BVKbfl zJ|0~sFv!D%2~|$YiJn%%GGiJMCs-Y_K1?oBtqE%iQNBfZhrZ%bo#QCIcPiHuT2<&x z^qIsTvTL0wp>fMuS|=xOD2_&f^`PW$;s%zO|D(4f`dLWK=jf*MDJoaJ(>GAU^nozuWOwwjX#4bycUf;Xe4G>`r@6T) z(Vxf7-yhV}Ps--@r~I&{_OEoQ*)A{ebmXda7`DC|d6B6r`B!`Uyr+=JZuNGi_S&}L zB!acvF>YuGBZ67$a6aX$olao2$?krI`|f&7;?7pR_Ow>$f^7*&or9Chc?mp2Yk_{u zRU+^7AiHB_w@NG0T)AQ7J3KfNbV7^sak2kU1-_&Z&|CU6Kc4dOW@{WNHe0Oo_g3-t zc0T4}t;Jl!&74;_i%9q>VHeQ|E5|65-Lh_Y*kU`i;@`2MfnmD7p`nhBrz2=vYHXZ= zO!uya)S>J*%j~Z5oS@mCY7C6AboPC6uYBxLR|YpLH&wlsRwfr6T!Xe*|FmUyasKk~=V7S`+K~}3GFWkib>xUc+0IOuK*`o~ew3cn zXV>L54oxaQjJ~GlE|~z!1UT}7Oe*_Bsi?m1!(P|{UG#V7qKpIYav78P&r{0(Nd@y0 zN^ZWF(Ef$aWcfRRIs_qUaB@Y{pwQrd0O9}Mo_-~XC@5eSWX+EKJC=oHJ(4DY!^wr4 z77t=TeIJ9rx@z=3kXEEiX3rJYd+EzB308SMjyBS>(+BTwwhZ5f02nO%`XL|Pc*NVB9e7bl*^Nuz0=I3s2 zcFF6_Y2oB(b)g`S=v)&1W*sFzwI@HP8)SJPMoyZkmu`SiZ0)s(_er4i zL)BPz&h#-ymC~>u3v5r8CA~Fju@$F%Zy27P-CQ-hNYQ>@UCO4q_P+emjC%Oh#%+yS ziNGs&6Nv}bUwZy|JL22I(hJjV(VDSYt+4TR1l>#O&UYQ%pw-u3S5Ife29#D0>X99u z=0oME>IZw~hk<{H;v&qb$OJ6)EiWQ36o)9cl&Mx2Et#lcX^jE$)C=7b4yb~Q7Zz!|n zwi`lQ<+0<$CLvdp<(5%eH>PDzgM!`!4z*ChcS7zIyv@kxRh2k7ZH6ZX`#_y@wixs|LH}K0B0o#$_BZNBd5{s${occ3Nzn6{=Cq@tCdm({Fya~ zU{E9E@A{0WWl+W&f^b-)y;8|#ZvNv|wy%O;=9ypeiW+U32-yT9kX5e#t=|3VLCo9U z_x-Jfc2DD>yvE>tickr_|A6rXA>zh`wk<`$c-0ksah!TDppNL5Sr>=LE`PtP6H@r| zx_J5Aa)#11g@CZv-DLCDhex^tYIP_A7(M>Ky@$c*n6SrfhS{p@NxiQ_RVU;Ipc|i- z(NlAwmNez!Hu^-yRvHC_1r;4(hXBEYS2?Doe4*F)Ba8Fz?~Hff!FBt;=73LVp82y3!|u>9d1J=~IR|qW*JJJ6!`Ir3$^7k3yWJek-Hz9u^lm7Z zI{fZ#Q(VG}C=F-p%}^F`vzH>oyjRw0ub*!thX<^8PfI7C+VM(L`~sa)gfx&0))6DJ zKzM858694RWmTL(h$DHxbBVhJ%MzE9I_XE*+UX3~v}aQZug2`Bdu#7&Gi9VWHFwT; zq8H!ZuB$#DE`NwO&*nu?Sk5y*w?l`{qMaD&yaGZ8yGNDJ-KzG(8uch*joy`tV!wu} zSa-Cl#t_9r^zwH3d~FFE=X)`C;lSDtH(l^umZL#MHipZ zjlsfa86Lownz$9MRvuM;@vm5mOhlHCV%I?_JJVpAI^3-BVC!yqIlt^$)Uv9GJ^cc~ z>SBf9OuH5z7$5)@+L2=Vhvcv=;F4!Oe;sG8QBEgsaXXc;zJ27ZcM_RF0_gfqIg1Q! zZF?t`q`t3r`3_@e!Yy@e)E7U(!E$tzI-E}fPI;WkpJ`<9HUa0zZ;Qs%4D3=ppA@Do z=5>e?!fnV3SG}1y_>k>!QFC9Zj{V9!AYL0u9U#eGg7)ah@RvSAq<_Yno|ybcv;|?v zz@K=#Xlj|aBWd`s_dgO6(iyQDH<7!K`5ixGN$C<{<0VQ6nVj$}aryp= zrBT!_VHkLeKpl%St(71T1u4*P#v?$EryBVxrdU5aQz@;YpHUyc~X4 zH&!cWE&t2bo1pyQKA`{ZZa2{sM{sDDTB-z44oe|dHwpO`lDV8OW;OV{T~{>><#KcQ z+^FY;oVuoPvZp5Npx@c{v-9f);-UCo19UyY0KF{K2yqMZJJ-CZGrH7rkNbV5J4e1R z#Ld@PJ#Belw34xq*0IJf(%MH*<=9V+$G2dbgg_nD&J!#xrJ%!gjyi<3B6E)r`tfY; z-;;G`+4>T?^TE^6au!l)ZDbgoUPYzNZtQio6L^(hqe`{uwd& zX2Lq*_XiM;|Kv=EZ7D}P6kovg=$KB?g-S&0$JuOCk6FiN(O<{HduhMy4;k$up`B)+ z%juE8%uZ9g8NG*YFTYfvHAnGGNN7&}5W8fcKSQ~l#?UY`oP?`qN14`X;r!iQHM5?CD&Sv*J#4=bM7MP#hafb&sepGC&{o|I~rZzXJ zcW#zf=2l%q*Vac`Dlo2R1623?Qhpfen~yKuTK7kBOA9eU9-e;rnKk%Bb{+ZT@6&_U z)@+-*?38;k!%)SVF2W*Bm0CqiRvXP3tQ?T8SAlPQdbXkws*xM>HEcrGlpdw=mG>`! z!O%NS|4&zOKQc_$W9M4cQ6f0wGDJzgM%E309y zD=^V!{8eBxXF3@d6B%15jtF*?R8hc|!YEGJmI8mXjNxU?4012)i}+bUP-krOZda_i zyn|n&fJ0w>1p%1Kg)n|Y4Y7uQbopz*O%;Gg5ML067fJ;7<9qBUY6hFf64hEr9S6uQ zEEiSO%c88Ky>Tf8QP(Ab!FP|FosrydZ``wfo(TBc_Qn(Kc{yh|*pOtWUfBzm1SGxx zKpjeV1wA}lR7+xkjkH1POGXufbK=HF6qI)I3zZbD5FocldCNo675HJN&)yr-yuCdu zPAuo@uGqBHeq2nEi(V+m#9Pyb%GavZEDfCpc*)TXmw1DE{XC~l%=`m&hK<1rY5YC`kzksN&+R1-= z!sg&^&U_?wRr&6%dm+%gHXa3qMqy`GOCqAqae%f?1|eDZ4M<8i=vEokAoY@!~un@J>S8ehxpvG6E|%QH8n4GYgZ*!1ZqrXXpT{~MhN(0Vw?*}qIt*lSp6Wt< z2SuY=Y5DlTv(}0I?c2*y^ZzZ+-mA$lY6jh0Z$n-Ooc==#Q^4!D7i)Ps)qIjTV0rNV zDR^(xfb*K|L+n8Rwy4#&tjL&gpW|r;t$Ph1`8U?uFF)-jXnFXKHwC5L3+0y? zY?^u#*qtqg+H?kl>f;XbFOc4s2o4OzaAFTyur=Y0CRyIQtO){t zNN=W&MP%EQP-2rvxH&R?<{a@hPf4SO<&<6qaf|SU#HNCiP#!yS_$_Mn1f@gmzy4!Z zYOZl~%b_5Q2KdWuGNj<3w*~UelUmn;0t1_B%m&z$GPZMK10z{*;b_2`zKZ_rZdL|9 z!vG3;D{i|hu`3ibw15t_Tj9^xvAdsbE;kkLjQh$UO>Kfgxw+Z8ztRm1awdpR*ue1( z4%p;+4ud=`1Si_UjYT^<7ceQN=Dou3C@!O~Fj32Zt-C&=^p60@6?u#Pl&H3u@(cv~ znDSj_F9*^UMejoMmQ(?qg;H*of%a?Uiz#kgUhoGHMXiRmS(N4?g7Ffs2ataJ)$!}# zYzQOh+jHICTc>`2tDH15r$hugr}=vHuYxds!jKV7_L8z0@Gi6VB<$&@JHO@LJ5w@JZk=f?@E!{fk* z`vMC}s|(;$`=@Zm-SeAzH`JrRuHaO5J>%+`sn)so^E*dRf2OX|dm8^OJgKxE82tO2 znVQ2V*t$}CWoLI`XLa?N{L51!o9#I)wxf2t5)2k`V{0!& zJLQISnsyc!iF`BJLrB=k4Pm6VXV#AmjhE}Vdgj>LbY{@$H{O2`AXi8q!kUCpqMA3C z&hBpZQ|Ms=huGVtsy=WkT@d&Z+(s5Ffi;6Veja9MM+J6Q^vP-36>{S9Sd%z@c3z*A9P1lI z-g6lTJF2ao zzR@zx1fAYK^>HJf4b3Tfo^0+W`Aaz=`eEusPTO*}mT|u1f__3A+`=cUgMSJ-)Y1^! zBl|rps*cYhowW4&h6%HnNH}Z@Pi=295RcpT32xO%QS}Yd%7)^VtB@)vQtbJbFH;+0 ze0Tahzz6Oiu5IILXyQRu0#S(?zz-b``%M_~m<1WxY0G6{LytqhA;M9r$0Jq3x(Om( zbR;C!;Ez1xlkJCcgZ)@Q(iPA zREGg0GlXU7BXpM(Eah~l_QZ?Vje%DJ)IQUAVN}4-7$)O)jQG`t+RV_as`C{`^TQz) zw_2bwl5ly&3t?HHHg!4@v!rQIG;PV>$z9A8%c08(EWGacEE+HWkF+c(7}N`O)|au) z`tgMFeK51?>REy^5R1(1A=%4CZrkZaOJg1&uF!|X-sl>OAyW@#P6;>C)m4?md-()L z@BT0N(@I2r&Qi_hC2qz3HDI_U3@9kYaYE%t)hbvFInkX=H9t>MgLVJ@%*Xoz5H$a8 zhNDYg#iqcvq&Hbati?d3U@$|>U_qs?m(d~ea@xO~xl_bg1#fkE?r%t%0v#KQg6_8) zqLM(B2BL14ux4&8b_2?Ee&^%q-a!+Sf{o@Hx`O62R_NJ;DJM~6?4OqK`|$PPHR%%X zymCH*{m4F6k*CUUB{L_DZdW?KzN3{n&`L)etxXqMQvI2|gy1${Yq+Q5GP z&a~V)1hxOV5c)8-bkO-J(<861iX)tT zQ~(OU<8ei~d#gjEtoBO7&EiKC9YKrp%RHUk<@Mv4j>P$G@D`y9tW@BrcVr*o3|Mb- z_mrtq*8gaI+@JY+em}4O8mE4{GAEnRSD136%sA?1yeM^*xbrZ=fcfdS zQ#$hR0mj2SUc7ZhM3#GrIFJ@41TRLLKv20emd2_y``b3~Hl%_x}yT8W0yi4xI}zs`A$ zILR2C`LPNq`H_4Xo91Ob*VJMP9O-WZ;sLBYA*)YPI72A!O(OGmyJtK0V~yKcZnN9h zRy>Z~f*e<#Q#Y($QU=Ka<*uVRZWNW_YX0~Cfk8$Nx>tVr7W zP(D}b;iZ3Y%-m&et}BW4`|$LGRt9rXc>`N9p1ZAq;8ZDY$P^Glcu_g!(nY2_))(4q zz~<-u(b+^axajU$_1yk>yzO)B8<2)jZC=b62bGbI0E83d=Xn-Zlx^^VQE@ga>w&jc@DXQ#qgv z#3*$_BRfoBRxT;yFodNrxzjU~*REJa+I;)Gea*N}9?T0ziPt>hSD*ZJeleSG^naLo z3$M1jwhI#{SP8Dh-Q6{~ySuxWLXkosxVsdG;Oph6UV!Kmout!s?7hP!Ug`znW~n8tBiubjmhq?c=)l&0E``j^{NF& zOw4f5jVJJC0Lt=Oo;bqwR+ad9!XJLsG*+%f?$HSNg_2goWE3jwx<_>g5Iu;WLAeC#Z z$wDPMcR*FE86MHx*9avl@r+}=2Hl?(WmFZg2i&5?#QvA=*^ zPR{Quwt8ksU*%kG3{EeJ{^0$`2J<#??WMmRM?@rZ9V5!!X=ry4G*(#baEPldC0o&f z3E8T9ZOGiB7L6DMJ|()hCg}-b&kHdsi#8{KcgS8wes-(7CYje=Qr4Y|yt=d&+Jz%7NvRpDI@~EB%tT2>YpaawlaI+#J(HyT2Dy zU+tUlbPG$XVQf=^6iyj-;8p`&*2n(Ya9Eov)|CFv;Mr7-xZ6EY|t- z1R3LH0hz;eoyZ{IDvxCq$ngS0T2T&=dF+QJ7u}uW{Pzxcl_D)Y%mPXO5NX-W+)ll? zsrfFpQKvWeii&WVKaz^>5!T5uj$VA)Qe?o-ztGcQR!$jZA;6=_%P*v(oP?w1FI1NZ z_v{&$^3nxG%i%$~NpK+L%4!4EE8%}ui{FB%V9ApjAy+RAA3ygrbe$Tqc;2q78F=iD zbAHRq>G@iskzxc7xUPP0C|c0}->mq)GvL-+N8tk1gZ#ncYk>00vbBohP%`h=F=4uu z(U~ay&YzhPpILkuXG}9!U~a<_l^)qX?__3QI>GK8SgJOzM>&k1>BSJ|Dg$-=V7evW zDw-(G#Nr8^O=EAOi>-}G$n;loAQdU^WY@KicvAgljrMI-_@Ua(GoQZsd!~Pv-N8H| zAo^r#EE%$Ip^7E?;N~V>ZiY!+gWvE1|rR5COn+&<)7yZR(B`BeGNr9jnG)* z{!Lubu$Aa+OrpgvGix{nCOr@#^3X)u$(Ro~kFy0sPoxjc&O~!)8K2G^6 z?=Out%Hch+AY?1HVM-A)?F!7kSOP0iq_rt12J(GB+{vo1l?>b>MCqS{OW&>gx6vAH z{UUtsuQNn6XL}`Bjd6DAnIPgs8#)SVChUVv7LUespLi0^8pG6-C8+*l5{+?Hs4`T; z!b^jd)KhEa3f$p%P|{1NJbM>1>STiZL0PrEh?N(xoHCF0s1A`7jdyR`SnFWpijb(f zh8dPX&+zoN^{wi5sovgi6Ws9*Q8<=d1F*Gk{}dcG$EAeuo`W{lO<0VLXL?r-j;7U| z>7g*cd?f-rnGgejTH}(E5d_n5YT5#Hh?7xR+fWFQkz+fDy5>p@0n|{2u+)eiWV7fN zQx+hCKM&5FRBq$QwCx30Lz>wU$!XL};lcU08ytZI9m+H^ zexSB(Ql=DK7p@SIs5Si^@k#)fRFH$I;-NB4<0y-d$@B^hlEW^;IKiruEkbX^XmtA+ zuDtOkDjRsrG)SX+@=M+2zChL9*RiyRWfzN}hO@PmNr~lV6*qkvwQ5oLI)*(0(=j$p zi`+VPVp@w5gW)k+Vg}!fGMAnr8|6L+Rv!hqrD6#-q7KX27^i5sIW(fn_>b`4kOFu? zI36g=+uyweXjNjA6^>L0^ys&en>#Vm#2!IZtp9{JjiO!R?V2O2^?6`daSt-BnXC}$ zmjpDkIUU4~3#%Sv1ptz_4+hqsgV&?MnTC^KPGgClVOuU2_GqA(*ZCw-U7OxoY9IetCf@p5Mn&cYIcUXG zqh+H*gF|`XLK;v295MwKchP~U;pk!%$}8bAqft=t;o*o;C9XK{erA1vNh>?8`>nzi z`t0E-l93nyIH;T4)Zn{x_R=9QW%tolKHbP6khBmhK%67}2kG{w$I(u@(+k{-FK|hm zetPa#nxp7#dMPOcK53+co^j>Q99>%zVcEqj#?~|lI`C7Ys9T*#$lp*vUUOliA|BoA zjG}wf9v2Rmv@+ti22#--@_iXO#7zfyMn&A3RqKt=rdmB z*^r2(`EA^`1^Q4bhgvk~&b(T86yDZOf{u~{P0YyDW5xpq(#b04DTG!2B&i1cycVx* zXywZcOLV(rW?j~+5gNgfZrM*$lu?z=Ix3R`43L0b@yM%}3qV4MkW@0~%47y@RkdX1 zcEBB-EDq7-BfYsSBJy&}Pjb3{we4S`{6 zX8%hvU$OZ+JM5ByDkd3FaXM0W`E#9II$C~%uLUQH)AG8^vY)_7;Rk*gkl{kJgASNe zNe+i8-lwsPUq;QT77O@H_3b66xxTe=`n|^qMP!DwgZ~HJ{?I6vLPIELgix~Sg z?sT}g0 z^)$(WrSKpF0*GKmkPXWZ#uCH4p`mYP`*FVU7%p&84c<{nwiM#W+$?3!wA72#Xka()>-vho*-EIx_s3eaNw)ZZ!7X2QHYR7mug5LbGlNKxd4nygk@ihLt_ zgRROWw<(V<)xM0C{jN^Jj;f(GaZraZ*w?`DdiL>A&>9^EbHeut^Z>Wm#*hS-hCj=Zb* zTpzg%kzz50r09GwuVOa^fRqf2IN?EK7XO;9hT%Z)1x?Nf@)=IBzgkYwKsyrqL!CUKXFu&BhS#vQ%aOQ*T^%sQ|J&0;VNrb$(abqY!4+s277L79YE z(~llmf#^E(^fa5UQbcHS_y`c#kW^2(kw%bt9FCfaY1~a(LQ|7EO^6QXi)pyz5&7$g zm0=_KROVE^SWtS#+S@;7M(sS z=urwwvFCdl`oUl#Ygl=~P#S#1!6E#Sskgk^Vh$6P@gOt>LFF&+sZDZrEw180Ga9ws z*@rGv$oS7)oI-U`?}#ef@!w&)+Dh|oah``Pd_|*Qxi(R~%6OCkvm-fvPVpWR$j#P# z_&<%&5Z07*BQv68UN83%)Ns@ln90*IP+w&7v|DcrJ_JiisDeH3LeSCX6d4h`47PO2 zA4iuQq!%ll_So?taFWSVyFkk@awZlJQWOi+NL=$-#43QoLr$br$uOV2UF4vZei)KM z=K`x)DIHi!%Srkpc37C*U9@Ts0-`QxPR(*6?4n^q*fvbFU{n(_st}d)Q_ax&rf)#8 zb7Y!dGa(lU0i^FbU*Iw*;eYuL8{^q8>?kpyVPBff3#z-yFmn`*neq~yM`odGQ6Yby zb5x~93$Vi$AYPB$F4XNWxt`@Sr2%^FYS)!g0lJc93e>3yy&&Z*l|xTHqE zOTtPUZ#gLh@!}u&!`VRg(%5)_t`lAI#b8ct^EiN>#^5pPIEZ%s9X&8u1Q0|1Ll8?i zR26}-K@-nXO4T&C!;=AU>nOkWAQT_2LL6jHV=4mHBS&UbqTf<}$ejUl+)8^BByg+I zzO(Y@OPM1)K#@`}t5AmCe?mY(qDFw3V4%1#Z0;79gmg6T@1Iac8NCs8^+xz11Vn!{ z%KmA)LT%WMMT-;mH9kpbmmF+NY#wd+kY|9l+vvM+S=vez6CIEm5d_>Fbk)*JXUf?W zXX2nKo2?0OxpYC))p^X8cPE%lf&D=3a~d?~N)8<9GZ<<}T^GJWyOOSu8OiDb>@%&H z6`Y=gB8?%jsn+RH+&Q4t0VIz9(jhWtr^sc;65 z!3T;8-(;5#Vp{N&|$x)f?;pxu{>A+d}QdJ~fRE^?C7sWY^ zLKyt?-hr4i*vW}^HdALRk2EQX5xvGt?8)a$F^$mj@MVQ%jr&znHO@$lbxN4SHE1z# zu)HR?VHldq(C=FQzX`0mR)F=u&s?&+gE%A(>)K^F4>-PbSpDI91!H4e>mWX2X-6Jh z2NBu0GJYvQIHIFhuXK(coP2CD>+B6#eokm|ztrTFY;84%xn8@sQ&0xXYtYe?6v2F* zQkz_{;`JrclcRSNYF@(qRe>HJdA>(&u0l#8HHZOVc3K?0n($peAXJ|p2mk-Ls2q@C62u0mEl)uFTyfy zMJj~U@*sFCqd-Yjsmi^?%#QoTPlF+SlCKl@9d9*~Azp(EQ^qE2nh?Ko_W)2%d-(Is z7*8aPDa>9V`agRC0*GnAVMFL)73>My`9f*+oC%ah2udb> zRZZYPWvP+oTS!&?AUs`bu+`y(?r_Fanw{BIFXJe(R8w_eXSoaU4GkbidAHPR4;EX9rc3s#djj$qsNRUsbg<;F2J;W2#ZGtZF;DU1$JyQkR@qk(vU__ljAc zA(UWnl(bMIR+cKv(Ghu#+o34^?qQ5w4nOoeBo+<#Q9p&&6oG}<8#teqTT`nUqL~b5 z4{+D>zj0RiheZl5hok1q5hkfBO>E@byR8(`{0y?|s>##{Wg4gs3&7Qt6*`U|72Co6 zp+emD-UrB5^{P?EQxP6!yP1i3BHiSR85RV9XmX$=uY_;fA)0sMM$o_~aI4sY%D_c{ zA=t&uh9C^g9L;P_n&5rUkR|S~XTXA^DCZbEz_K|le6hwES@2ln24^*sxR$9tGLv_{ zS4wu*=YQSD`2ohvfhp@?wT1QnRfmr&ZDa}0VjCeHx19Ibncdc3*rtP=_@1Z;{}#Q2 z8&0LE$bRaS?Vc8p!WV(VG%7cPPZybfssPrF(qFq+?!pJ}$~azX1GWf}QI0}YJpqhi zv041!cw<^lJSBWdT)hHEC|R9}ai96_c*!30Oc828WLQMiEEfchtrq63y&X-a2+B89 z&IHrgRfZZMIaVzKgFd#Et>ve^%&68d^i8~1$itl<{)w3*+{JWqLmz9ZwVWY8N8vuk z^Vj*5mfUm!Nn5W^Hiy&bZ`R*y$9piuIi$Ni5a~QE79Ag)BT=b;mO0is zn^pNFhokPMklfGo3<-;z45uc=yo|_y=m;(6#wVto#sP}leAXf^Ib>^<2XgRQ+$7+a zY?24rGEH!GbEF{BAZ9`dgW+j-xqZ@V=V~eEl4UXw5K9u{0Q1eDma9s=d}BG<7frLQ zJf3jfhYqSHO}<0=$I3)aeUv%b;j(L;;9DTrQ+F-0i~+$z^V*96iTV>7t5(l$>31sw zuA_dt5D4M2JYu(Mb`JOgI(Zje%aA_hTD9MXIsXyxC?&l%vDKgW{YaqgmK2%QoAy!n zRhH`gPuVhr`0u*%X|Y~z@_(elT@EBab6-rfn7sZieA|9plyGWWO0s`hq+I)eh3R+l z9Q5&{u|!grvnvN}aGA?U+JCzMm@#OvVPn;M`fc_36wWG!x4Vf~+x537Ci648wBdRE_p#S>OXNYYhdPF>HP1<36qOMJAiDnAeONt&q(KW^@4BZcYJ7*b z6t9os(?6Bi_4#Men%FDpFU{)J=cU_C&2_f;o|#q^34+6l3a8IxVDTW^Za_1Umw2O6 z0L+9XwM__yryxZi^?>lfZ9{1^Y2 zuei&a-RQ}yAJpKREiwI<_sk{&XGsO-C+ z?DJmEpm+MeIUZGv&>wk4`M^mq_Stjcq zPN!n|r+wnVBQsfU8_zJjG{DqsYw!Pe(vf35$H8z?554wUVww=Sg%PtNz6ZxMFBE{1 zG)~aFWdl;vp}!0Uxc;GrkR%}SE|qJk%-%2%BIMC57duVdmFoLSK8aLZKc?G2E3Nor z6SFYV1SAUu$T`JKAjm~h*G;!CbkE*Sy5`d&(quk+f1$cfC$}}AOT~k5X=6()q%6xoR4#EQMdfqTXq&~WTiR2e zDXd?|#3#s?3YE&Z1w-|ZB2`b|RIPRXY>q!8I9B}v2w~*)RRgvLf$xHjvUOL(;3*=) zJw+h#R*JipJTV=f0%`*U0H+DyyJ)*q>`2D}+<7&Y4_c>RNz93VPr$cX8$Gq`aQJ0v zjgui6SMx+i=B7n-;y+jK@fJs05HqY>_Rz0r>7hvAQ`=k-+LT0ACh3}klVdd-9nc0Q zUbWG5owelx#i;cUpy_!^qF1Wqw0RvA8^|4VH|pTbBR-T4I2PV_lRSKt`m8jlLv(;K zvj>o}?)qpuFfY~NJLG*BFsF0~hQ|H2MD*{}!S7|`bsU4!?Xj^Adi|ofQCIb6;Y{~W zR^jBY{|^xXW1lBBdNtfXpSOShrnhiG@COo+?q%=5fQ9)?3Wz%qA%$TG(b?d(7%IwW zE<6RK0TS>OY{V|I)hIjrZ2dQW(55=I~c_Y}7x zX`Vxy^aAC7Q1pZ=yzy?hqy3)u$Kifz zge1c-8H>;geCokpCMC%8VUk^O4#kRX2fsglXc?se9d5~1!`6L2E4Z%Nkcx-68OHm8 zuu0qBmy(F4or#YQ51jWSpZB}+-=@wRHg0~L-3c5&&b+!{dwf8_)r4GMx42Nf$p1$a z>Kz%zFgX6^8FGHQx4*KZ_nG89zT7DEowLjg2pASblZwv*+g<1v9p2MHiRRMSZp&lW z&>pA?Zz8&V)2~uLp-15t5;Hk^RSC0cB7Qa!#V$PHshi8nh$(G$s?f&1gU#yK;O761 z!}nTp{X`yC$)BABX=kI+Q$imBVp1vxPzE>c_eZs-%IW^J!)Jh~%d*9Ep&P|-P}>?u z7UBM;Llz?*11J!}gFQMqCMu;{wnA2O1!2uYY*fqiB+1q4$4ns2wYOzGR)Um{qK-n6 z77gEzW)!hCc*a5SK2$N6!V15OU)&&4dVFR;xJD~b(jF93OQo>Rl`%;{%fRVHXxhX% zcjq5Y+?*h>-M={vWZF7u)$Pbe!%Q?sqC|3F61dq>VoUDFxv9e0EfxxP&f&Z$i^__u7R=6#T z-rop7{fC<~O}x*o2uw$@$tA#Ff}FQs`wK*+2^5ta!hoj|(g4aW1V(<%D5>wM@E}c~ zE0x)1HiKyV=$dJlN7;m+@zxin`^2m+`Re>G+HFQ^ zs%Lc;<0vc$TOX6BsGT`>N$TyXxL?r;`12h98vlw~J{O)$%d zCIMSws>Ft2)jZ_B;VCpi;cn^SL)ZyUUYWbd+8R-5Jf_Ml>CbvV zJ_vxlaI1gNGDm%lH!t?!oijY9O$_5P8_ z+XESVx?r2r${;dhdS6UccvP>;H?SNZKW;s*6DtKdaKaMO!bz1X$1#o$H7VcH?yp+F z{~5)TJ}vtS>xM*jshB#Opr}G@ZpTEv(Ip9!+CwSWUBu#*Y+=LSiKE{v9eKKRgV#z{ z_#YHdX}?{DpT6DiFEj-DEM}qFO8=WTIwWnQSZ(PP`X*qs_dhDn4aNSH84BRroBMGR z8*^xKi;bd$H->|yDl;)ev4auoXqqqBI6=92j4*{$rE-`6QqrGvP@juY;`NwtxFZ zs319?284Q0U*UzhG}Wdt+`F(dJ~32_?da88Rd%Dh)Ykeq+L(B>Id>t^se8VgNZVD^ zQl zjV7{kC;x!D99|UP5MLayFg#*6nHx6#fuv#1Hv`8ua0yqc)SBq~j-E3%L=>T|RQu%c z;I?L$_;p(6J&%tG15t;Ke0M3*LL6IPE=dxVOIG}m4kUe=;~`}H6`Y>h`AwKs5}|l# zNN$Yt`f&gZX@KKcQBjjNh^9uxs+iq0JcO422H*~kW0<#O0H_#0Q=tnRk2xY)#mHfWv3&maSYsg2({#KEZZB0jPjG@Su6vAwLL)%H zddKE$82uCB|IY78j!N6t{HeahyTwFPJ*mAo1Si>ac**w=niFy*!x(XR)^;$g>D$g? zem|xg9Z4=Fo%3&N?k`n8+`p(=q*!M(BjpE34;VUymwV3)y-0M!*7xE z4u|L1(2~-TB-2j+y_tL$Y$?d{k4VNoV*H{9naC?KS45 zr|$GO81E=dEQHc0AUiQ<>-CFI^2fbOd`159cWAsaR~i)3f8l7~QESJEmO|@#ee2fm zT`62FEa(WxCg~MMd`2|LyS*=m{$h+w<&jGU+47~R1w22m`^dF}kWMhtTcwK1SbCPV z3s*c%;lp1LdpD0SXuH#se7c^F@Ztd}CMffcVS10Bin}#3NM!&6IEpqwNVA$WG8dz8 zD3hsQR&dld@P8e&;)^qyeNP&v5e~(YPS=J;Aa8l={E^byr}E-(VVqOQ%8t!;n3?EV zAweFcP z5cJ4C8btgPS;7i)?pZE_hRuVFNn%OoFU5;`3)4WzH4&$KK(K*{%6?4x+vJ3qiot&& zkemN)z-|pQu&>>Pfe#)Y?c$3hbpX0JR}zxbYx`JnCnG?mJUl#H5rAt6=J=IL1EYHi zESI(5cL4FIPZ*HzDiOY;K$spY7ghqx6ts5BSCS&rY2+Xr=n1~r>(D`qouZ9evi&hVC|WVI#XAGr0Tfi=c9Ur?ap42X3mt6H?Zy6%+Xt?T!ohWncT^~2=E#5jbH zTU1hlQw`fZShLcDqhFlZ@iKrE!zy0a_(_mBFwU*F>&`!dy>o0%-}Y{Q>DfD>X*TkQ z3`4@=d6(bm)x&ODMN4T%`Ch%o6jsuN!dcRXZ`l-i$=00ZJ4p&0J>=hb1#X{y4ecoS zHM|=!S;-Z2s*ND6tS>MPoU+HLbGJAkXA`)~s`)qE?%CH{F}tE;eOh`MtuIONHS;^} zAJ3Go3fUb4fzn#+eWSqf+TdfV9YyUOYCqRTzY?k+4h>XXNDG2~b-pxeoQVj$1c`9P zGg86stZs;8p+FzMKA*~sDxc~u?N{*g-6XjEJE*>TB@I?G>nX@g|%I$m}7nYbu*e0+zl7*~=6Ksb`BC|tZ)LJ&MlYIw;Xj1gL&NQ);& z(vl_IWXPTj9GWNLsKS0M7Zu$N%`g?ooFNX!2pgZKC{5lu#6Raq4Uv@l5pas2E~|#a zpv7e0P0h@ld%uZvHFtrQ%xQ7VG2IzAPjSYtOocRn?00zF0&(pB>`Wyo%!A{y5FQe( z>VWYRq(2~Nh)8)5@cLR|@>*eGhkst94TD+l{K94|kjBZ?<3Fs{k_L4-->l_e4&2Y} z?eCwj_x3h5-G+;=Xkg7Wx4VZX+mg>;;UHh@*w9IRN3h|;0{Mfx@Ti6eNmBx2c`&4< zBe)^#{XfQl_C{mgY2tOzO+}cHi23Ib-HbpPqh5EtQR`USJN$NE1A+G3FJ@B|>pznG zRC7ZLvOn{2h!dx*t12bgmqp>>>FA{HZ=cSi7$_YZaReD5LRDe$R%Z9sZ(=1u%cS{0wgIDr*bL53U@d`*Bh_ zDsY=F0zh;5GnKs1igeBPPcGp-irMYjwS&EaMUqmBLHVAk#$L$5XI)zf9cQtMX-vDy zCw+lCyXxobR>WmB@rXzr6LIWnb;&#NFW(8EU6;#?f~X?BBV=mB{F5NFq~=dWqbKYJ z9qC)h$h)3Wio>xN@3;7ecM-{`Mzl-|F7EiAOOaQkz|4m5UYpVIHplU=)}@S#pBcty z(vA-}9VxVlMXvcMq?pEmqrTci%VCL=-X<1R0-DxIyN6XAO-mG&p7I&hiM~HFeKQ3# zyN1Xsw3nocXn^H`8)dd6d^9ei*4pTn>6TXanuIyd=3^f zqN|Kghzf92yLevFPWN}oqnTGqScR5AZ=O{F1VyF$Lfkk2um2`s5NFCNv~il;61Xj^ z$(|qj+mj*M>I&@UX-{y?`F`)AHDHjrh=`B!02#RFROyT#+i0pn`}grPprqsq?<*#V zPF-7opH*;l1YkSTwHo{(T>J)H^+w6Dek&Q0UiWZjDAW6-95OOEQvp{jXNDoCh?M>Z zu+cR}6{LYh)#JOrw_-f9aiSeuhQ{(fh1|^{aT5FWJGj>tk7|?g2t?xVImCuUjmrzb z!A9RZLJXz+us#)*EJxj#B~OLGMu_af(?rT1d&4e_W70!^QeS*%jTq z461542mrn{v#%d;FhwZvW#GQZQsKUcXcSH)zid|<*X znD^Vii(j3$hq%h9sk2MP>8i5ipN~%}?P^Om`W3yVqa$Zygd^aaufJLFaeH~@DOTMV z4P$3lX&aN)u>~!cQ=u}C$whydP#fJuqn@c5!-A1WY(h9a|3c!K`c&7kT_!x~ z?Z%R6x%cmE#mKyaErb&-FE-7~LL_R@f*@Q7LZF0rLgz$47V-!&{R}R5#8qF49mlpuv@`3an6;|Op zA)MV##yBaeL2QNJi86})bfxK_d3ZB8EAYBW(KVCrIuG^L18yElOo}A64&5FGb%Ag$ z3iUg$NKZKLV3qwsLEqQ>srI)sB00Ih8Zt7zMU-A_`GP|e8w`SC%mF`F9>9jh*H6zs za!Ey{9-1=Irx9fG=-yK?E}=9+>8V1q>Jii^!#I(s+aJq4DU?OvK{Lhp`wTVmAvEw^ z^7x6_bnm~=j+RZV#NpP(FI1h&Yki&B6badyt))v)oL!6Qdd8a_SIVXE3UGg2(JeWj zA)BgOOnHu}H;GBhUqlZTSK;K}(aOR*${0)}_D|_;D?3dG;nlm}iv@VUhxQ+n9KL4X zY|o#JLih@bx)-#R0t5u_KiCJDrW$j88(r-7!QA3e;2g4RqAh8SNPkL3xT^H+)Fd+B zu=I^6nO!=L5mxfOlLK4%w+Zx-)_Tjc)&^&QL|Ke!>?YxZN|aSB6U2>OPxZ6E!X9M; zf!q{zCo~q!^^EFi9<>m;zgv^$KC9Af-e;qt1ft4Zm0uFv9f_!dNt`8y z*a^7xz@4{ItxnoJ1nP_MQx6n?O_ezdCd)}!1bUl7%U(+o_78mXTuL1&B9I4L3Fho3#LuJfqu(>tiDVcWBPy6jVN?hB5p9hj9|iyweLJ~g z6mtz_c=CIp-6z9Iq=LyPZ3JUdPCa|lpop*tCm@3k4J!pt(iBIvcoX#JZ2BwW#f3w^ z6$*7ye*Z@|pBoDU)R6tsUvDSZLBnThY6;ynhy_s|l?lLD&yOE-35u!y`{o>A-<-}M zHZ|IScsdnjr3jdALaaBMNM}MSdt^YUmlB-^Pz~3F9&r3T^Xv*wFUztn6>d}mZ^1d2Yp`? z>_s)mm0l{TS9z?n6mTW_RQ0<&l=W(>TmgbpH1qVn_b;hI2K4Xk7e?OMn;oaa_0AjH zpdk5FbLH7#w5r=X-P7so;FLelgM!B>g=b!cCC&3nJ2Hwepv}+RuF_wYnIf%qGoDUN zu~AEpo_!N5Sw{OTybzX&Gwqg|C5j>efoMe?53qo)BOo+LdBn1wxxk~yowgs&yS_~0 zJxgRHuQcaf-JCC{!H8hnYA2@auPMy?djtJHJgr-<4ju(i7jcSG=JG}edYyEhq4NPp zjh0g4K ze%C+i*YDRUtc#rfz2-IJ_u`sfwfw+awO`6=Ya053ka5>OYn9M?yGu|^7{{EEd=!r$ z5>UG|-3WgH0Wue3|NV#%lh<__&43x`4VMB8`$Rew>x|l-d{PS_DzCt-8=Gg$rXjcA zA6mktL)uRj(J&25SsKs!79c7+6uFn z_t0|zsOd={hbopT@O0rlzy0I*C>*6D7V4%uujcCunLsGiQQyV1!3Yo}m8}~IItcAK z&7gH#)C%eQElHPy7fy+d%Z<1B*uTf09V-`24}HZ+Ke>Or6}n0A@oC`!gs?J%Ux@R+ z0P9zKVrL1dUUVs?rLpA{-Tav&?(u+xY|*ypsb568a5JV5cvIQ%9|0AKrMuR}G|6aI zvLFPWcSgP67ninMLA|uvuuzm6!H2dc9Uq+-!D~p3;Eqn`P6gUn(3V}w-&T;u37x0F zJy_ettr*>*v01ozhLoKM7OM?`V@Cy6P5bKQtl>=+((jd_rp=})9X^tU>WPT59&(Bq zZ7iTB+wy(fa}I2oznsS!GpTkxE%rLgjGhG@>xR3?qpJ1+4XH{>1*FuTgAG$hdcLIwP%yB;~%zY++6d2e${ZR@%eB9b7lNO{9C>r zGja(PVna9(v=FT_vi#18~dc^Y!(riVH|Vr;6+SoX=WcPz?}7GViN zZj@f)yZi?x_F3dm!RloE`E0--iQ>tGq-Lx)kT;747sOT5B(Ct+8pr^jSx-bjWjnw< z&&0+wPPFa9ZN@Tufi{pKloHwJ$#{Jg9$I>tr(^tB!dwOZW*Qhgi35OD5tN* z%s#nI-WZHgr(0@i{9YTms?+DErfwk(u1N_I2gIzd!SEQlpsOWb`;=q&IE6zPf`bGVjojt+DpGzU!zbH!cR!{wOPk8!5DLMM{kFTy;ulyf4P_WBHb(IR8 zeWx@2Hv|&M`K&FluOc(M@auOR2L&B3*;8y zWmdYkho%_PCos9+xtM;Y~!R2$w#WA3)sD_F<>DZ&V#wJOQ`qV+OIv zV)crL8OcCG{=uj2PL_0!qJkV{8C|P$7GKPa8k?wJTYcbHhkFk7zRzNkk|8t!)en%P z#xd`w53X0sU57apZu=g!?wIv@Z3T|LpG0~)rtZXhg1Wi~3q8CIj02nJn;Q7ms*C?H z)wyrZ+Ml7R+q$Gsyn;fS$=03G%Cefl7k5PJm0Vj^+W8 zv+nNb^lProfiL6-Lc-z#DmBx+Bw4sq_KLRO)D>f7w{!TKI-b81VHG1d7)0>*Pz;P7 zFmuU|vj-p7!cRvmWjfQ+Ne{X?F(A;9-_X32as8Ndwb}O%^~CBU(&+?3+Ygjw&I$K` zMV(xzRCrF6E%E={42ZUuJsj2cfY+%&`b1m?qE?)%V!oHgaVrT~Rb}S()^E@_aCf-2 zzEo9$e#s`%DD~+QbP+-8eK$-B#!H$LGUgA@1&x?i)Wxd{m37Q-Fg>=Bids=?xey$`2Uq)-$LJe-_p?5__eWwK z-QP`|jul9n`siCx{=Q#2Bxm8hB{B(!Gk$gy`x?<~_rUFFM74K_5UGh|OZ-J$EVm$- zPdM_TszN(5pwaXcYAnI%uJ@t-yF#PpV)1q+SXl>?K4q3r7Qwi726swmW__7}B8BdH zn25FJ&(E8_gv@$t+Eyo~De~31P0y|s#zNDqJqO0JM{Z_rY9-^Z<3x#S2?@$EBu=SM zC$|OPdMg$JMAmr)`DuP7PI=9$4m~8r#St&9n0}G`r+q%w*t;Sl; z1mmv8_f||x4ZAn7r`uhzx?X~8DA%>pGYc?%4NmTkbB0d-&E#B9?F|@E{F(g5mltx_ z$$8k2Kvv&_DVBfOdCT%*S-t;>CdhC5s`AF!m{cR)A}8hUhq*&dKwKVo4^sMJm}YCn zk9BmFtXbSjSG3KXP>mrLUMw10;-U}bIPYi})ELwwrB;4lT9R>cr^IhbKqomu8<*JV zHp8IkB#^%Xo~%_hj^kqLW(a>en6_i5k*S6_Mt;Y1LVy}nI?x1pQOhg>ve`0TFfV?Z zn2O*yHEWS>C{W?|VbfpYGL7)$CIvV=o)jCVyxeyv?{GA1?&Rojf4lNHW4{7oBBV-- zc+^#~3yW$n)M9Q{E%3)KaD-B&M?+=bUHodFwpdVjokS&jeK@}IRrumubIftqDnTAx z;+6=)nVj13NT`-~VojnAih@0}|JukpD0SyV-kt*IM@Vx^Ws9-*XAb(yd|Y5f<)CN% zzAB|P6u=3rA!tUl*Yw3y5rQ+Ku|K!vbSK2O)kyeQQ4E+gPq&V!3m3jSpfGt3-^UC6 zQQDM~l|~zy`S<0TATkj*&^9f@eyDs|VHggL+Yl7rr=i9qgXhpOgzk9w^#R4Fw)L@f zV72$pR^ZjqI+;?ym2vL-{6a-K^#tYPyb4EOoeK?Bu$ePj$pyRM2n+coO<*)i!V-ue zlIw$247K()`J}F|Bbxv|^%?3;%~9BBra}97(};3yFxgyPQ$0{qR7And!(3$+>U6kD zd-%8I_izGVw3WOuM!HSv|SDBsewU$`%T>Y{?=+d%S9S3KISMH%@VT1Sc;RnotA zVm#8EmF2`)xHW*$EBg>d>+0_~X>_L5^H^4)STQ)8x!ST<%=m@xFr?6Dr#s|Bam-|z zpg+WBfPj6i7kt8g8TV0$)=$fw%af%DR$udg6x=x0EhdDyvESs{<&`mfLJ^%%=sI^O z$wy&IS#86c2bDkY&0DXG%bhn!EsUfORTFMs~p#>>RUKFBhB z(xjLPfbgMjvtxios^?Yp><5G=qJ;8X=m=+H1dQvGaxuS42Id{BlR%hznK}>39>vlM zoN19TQY?d2Jiw)bGUqcxPtu7gB&3|+)PFzXx=z`9bL1(1PJ7F=PRy7@`(4ZtOXrPy z9~>~d#1Qb0u)3}tYl?`Xgz^R7U!?Oreozk(WADw)`%JBbIzVLLjPP^MF()5| zQFG=5kSSCg{#9AEbshN^fu)=qM2=@v1Q+r2sD091ih68?bf8wS=O8fG{@7eUegc!E*|jtaOJqZBEAb&I=T~ zS|~{o(#q={F2Yod#P{1@4ZxsDSIVqgjj=(I<1ftLyGG2!^$0%z8(&TxL?pBD{^l7? zj%;PhSU0dw&>123E!)4m8R}`>ARZNXbYg#pe~%IZ9X4>rV6L4Z*I}$|zlPinEElKb z4Bh_eIO{D~TJdh0KClNik0K(ZWn`9!V)7%wx{9F9960LiC^K?9;DAyIRNoOqoT#V? zq(tIIaHT`iu^=%EvvE8enJbc!E$97&!3a-RPL;w&4-bzf-BuWud@T0+a>+XzJI2Q= zJs`PHjlo&x6OnEiDx zJT$IBZcp}(F~k(cpUMdA%BuRfNR4wp)oOXbW4I3K3@znGtTLT6=qV5Lf&SQ^U*rQs z)c&alo~sj8ck`;PMNCQ;DSe z#ah2JwisctTeE+5zS1QfR8I3~7#0=1wYQbv7U47_%UbBjv+!D-A*7V`x^|XFKo0j~ zh*KTxQV@mZyAPQ>_&A+4)8(J^L8mc;&3@-w#S@7rvSg5^os;m@_ph{E$;QbSYm<|4#da5cGn?2p}~z3BAL#py}-|s%3isE5ULJxo~p`j z>pTBK&*hYlPv2kNw4^u#8)k=*+wfz7e$eI9NR4hM@m3&pD&ahSpgAt?4>~sA0>6{{O`90`CnJYjlexA-^HSIlqzP88g`EEU^%dHdXmL8v z#`A;Kcsqh2)lZeD{sQ8Y{WTK7{;|9g!3hatiz&;CDL>VhdOsP3TJ$`YxZ2bV00Lr| zug{tsmZ@qQ=6!u5mz-v_;ceL4GgnuM>Iwc-?r8UiD&0tlx9&7CHBMTQ4gb9-lZ*qo zxqEnb!t~QQpBHbsi){;5P$NKNC1SBJPndUoNLf-Lh16|$E_eSt@e4|5bK z6ZpsB{cs-)`?!m^BJoiq4Gk;LO&#LbbCb<`NQ^u_5FNcJi{4g1RlYcOQ>K5rxnbKf z^{K7xYJmIM*R*qT>ykW^HC3oZ(N@R*!K}+K`dL`{HAW| z&rpRzNuIRMF&L=Yl6|DA>aR9mOsMUOL>HV4R8%XgBJ7+aMvKL#+T9bkryh<=Kf$Px z|9xSSjmdqZ-U5U$!zOwAT6ZZsjaZ{b|4^2OQWPjrKYet(3OYg9)AYEt0JAq8Jj-J zjs+3|vNFfZ9tETk${uFS+1nQ!Y_Z;o7k_-b-S zdYOQhn!EdAbel7*p#JnU4xN-!dp`;YZgV0Q3ZOy@ra(w5D;0?~l`5RkJag~|SKq6V z$!BxY<5_5+xi_-R65xioP5_T{@*hwfZGd*S@>@ZiAJ3un7epZVV5FN6qa>8`Mm zrWBoMx(ZNU>)XyfdYPD!DlL5(I_ghYM$=Koi!Hrd9D%z}U!Zt-E=jfVTVGcNax^~s zzy$zrYHs5Ci^+BN)63M+^#<$B2RG)W$(NGkI`jP7Iv=oC;Rq=8u5x3z($tNDuh{Gc z321&FQ#fDeQJQ0`>jN#r_ocb1B#?Xdzq zaTmo!v)qeGB!DWwRWF&!78E*-`;Efdp6dCLL57FUfj3$Yc| zr^#kQkSql-f*+%i!`R{wkb7uLBS%mYn?Ul7pmTW`03U^TAZgm*l@D8;e4m|{t6ZN8e z7RUT11)&RtgnHYg$1G&0%0^o>+tj(EweR6*Vr+Wu>D26mBwf7os6y9@{7XzIZM&2X zVjhZ~bcsn*$t>_9OBc#ST&vXHQ)Ulm8+=E@XD6oTpG_woPfgx^J^gHaeq5TIm1gGg zZagzPJwG`u&CSkEOS7|xX$+ppZSv84)@I5o-x{V~GuHT8tLO1#Vq`o~VeZWpqX^0^ zAzUR-9p3Bdc{4FRJu`phRbq{OFvmJr(rAsIy)rFHGtzYPnUM^2WVIu5z!!Y-`pMk< z^o>brd;7T@T@X)zihk;Q5>pz(3VpE9LKTwWr~CbrQ!J(V<)_)H@94#O-Pqc_3UZy*%W`Fja?lZ zogJB#w5KP&t?|Te-R-*mFr&$@gxE4i@$vyG6R`vc>8|je0Yn+7MQ|YqnSxBsbd@PX zj*~IrcsF@U3WVtx-c5oqbu?07Ya`zPHV>S(w^r1~v9lEA?olN-7IUx(Y{Ut~q?N z`^u~&y&jzznVfqyAw8dx9*oXEosu4pOAkkB=C<2&G^lQDd}e%3stTUXQ=`=Dh8!%m zc0*9+8}LHVz1it|J~Ma!$}*<=oYNm&iPeg3}D1TBbh;qJ3ay??beUaaZ3 zaQh+3nvul3rt49*rhS>Z?O5Q{*c6yM-+OPO$P~#1Urk-5mL8&@m}d*G4teHor8Y=M zjgcIkSD|wiSbH{^BNH>z{kLD_H@0Qjx^dAp7Fs$Vk6N^4b_1eNkd^iTeI43R=<0*e zwP&B!d-i&V@BqD$;rv|~vVa44h(k

yH(QS36P5U%7UkN_=#Pv#2VV8BA`A~!sF%5wqOinU^fm$d#% zVUiAsl-Q;NIK4~^?4xt_G)A6w!7u?%fRHqz&~{v<$>T8?gq#VpjoLC(AS76(lpm!L zx4{CG6Z38<6k)1Hl*0_d3Zw5|9`XPEk2nAM>Wa!LZEd6aa=iJ;`1na}yG30~!zbo} zza$8S%l0lz2H|^Z_$3SspU>Q{`kQ^4#g<;BHIl0jDXdf}%BqeXRKqr1)yZIEMTh{E z-4KX^h3U>RNQ_8M$qo?0ISlbvBnH>B5Tvk;`njwz_ZB(3a~wVEEV0W^M#m-T>Gb^g z%-qwFv3(s;rPdD+Qu#{@*!C8nm`Ih*V%oE=3LA|_1PDnoQsLEIX6`{zn`sQ~4-CJW zNW7etUW`jGCMTXujlY?fMrY?Iq{)#vn!5DWOk#A78t-t?{hk;I1Qm!|qTWL`6u~`t z`mVL+_9v4wcizlbs(p}&gkdYKLaa~+_PF8?k)1Si|M}#0TVSQhuhfT5-F!ATH#aFs z_V`d);MJ7$^iurc)YP7yr(acf723mPD))u^&m2QfRy2hR?7>n;f4Pm;cw+K_ zLlCAkS_3Rx077c*OMwv3`Uwc(X#}Q0$nhMgXo;6PVuV=XT&=zE5}Y%xj*kWq;!C6! zP|;`yaxgrI9HQe4c8NU#ga}i8rbxpLfk?*jtOyx|h=GG86{2?ZEm1`6C7jvA-BKvP z3M>U(Fb~^rN%}h^Ep70DT(^2zYZ&Q)j)PK2~TA?0crWyhiFM8ILh}C+LlB-V< zpah}^6H7h7lJF@y!E&D(>@x5m5Jk0is)wqm=tfw)AK-KSD4q}TjS*Hwryg9I6Ckk0 zM3v+?ij<5lIG{*e6x~+owgn5Z1xsl8Kv7eQT_#i736|n?4sdR1ZXv+V7$zb@lOd%z z5vT(TE2&Q}ZcLz5fDI|B;n^4kJ`U>{lncwn9xJ-X8wb+US4z(b~UURkZems;aL3Vwdg7_?v1&n?tX{ z&z`j49DZK(pV-RwE=&gDdusFZ2?F6^;|KeUUs;EWo2l164W4W6QQCWp?07K`Ryfa; zS%!#?)?VljP>nnVLWn#ZN;es%W7wHp%L-4DmBv)WAxSGtkrG=R zMZbRV#*`$z8J~YJF+VL$M^9fZu5so-Gtt{UIpGyVeVBrI*VI) z;^sbZWxXwU>Ba18NqRh!cseOf%uAZC;XKnov9bR<*F}Y8cwOtI-ToUp+OO>J-J%6= zEn-NYwG%kU&6nb%nPb_8fam5D6dctpw1828qpwUCtT6kLf415myZH*p2Iel@y1AuB zmC+d48@&8@W*(nnc6P3|#rpFba+MN|J(+?wqE!d|jZ})Sa&m=No=sI0W9X92A2KnKTFx=Y-DV8~{Q(#nwp-6A9qNBv3O|HHxd# zQE^cO*bzpWB114eFcGZfkrl*3F_0heoEW>6#$?SZ9TnVygJDFw^z8|}(IHV+i4V@Z z*dokbX&8IF#UB{6kg*46A;|GeK?WfbBwHxK`xFSNQa2?8k+?v#7ZU2kvh^el#XK^^ zLM=^=MhIkI07A}z6PNNT6Qa`%2LMGTD78~kml-j+j4k{J$9(_z<8>coucLU5JZb$a#SLm zGr?|&qen~}+LdqY%Fy@^Mb3a0qmvVlCjr8_hYw!tH@R|cex<92$|k*UEfu4rDrhzt zr)Ygl&QQ}?X&X>Fy2_h7k!FpfPXS@7^9FB>OwZ1|nv(90&Pvi$_o)jjj(c)#L-}}* z2G`ZzB8@L!<5w5}ziwLX&qf8MxaU}jDGK)Bu8stTAo8LEm$IrX+2+U^3rf;ZQClou z<2@L?nV6HF&PwBR(&e*fw;Wez);D*(l13BKxHNbF$%O+t+qVZ?jYChKLKDtSkBmRB zw>Ib3x2-nx6ziiUDp$5XvO*W)(j4+P#}vub`4om&c0;>5(k;y<8v7sQs9d?0AdMQ- z0TN++Db(8*%FG%p)O+%1J>H(Irl3B4;o`l~&BqO=2fN24>0tNsrPZCm>o4)ioxafO zddF5%*X2huwZU^adeXZnYN@(1rO6N=oTNa=nh(3I2;l@XNH?t?G%D5yu?wk}P5=s0 z&8N_m*d;~Cd=*3Tfkws>ABCrrVT(-I5eaAbabm(AciRE&uob;{lDE15gxCT?%q=1l zE!Iz;h^~^-b~uD>S`p$e3WxY8oWpPi9-L2s16stbR2UR2)O(q85PaBzSZioq5kXv> zo5C1!P>O0&C^?*qNTQ%I+0Y1p(em_yg50x|K}aHRAvL+vsc=3WSOo~NMGXGPi!?Op zc7;9iFOPcv%TLyQq*z~hLUW?F;ZnC9AZ)VxT1;B}>fP8@TKcv*dcU)DS6Cx?c3Q2Q+6z-4ROrJBV+3JpAhdPC zA$O>ykX_VvGUO0XhXe?fD6(nH-%o}WCI=cq-xSaa>~UNRGUCh=>el&ua(ZG~O3crO z`}&KjP1(-gqK<(wb8sEXR)?6Q%R%$h(AY#F2T|r$*ar|(;pkk40@@ZYu*M5?;kv$i z(wy{iN_spwHa0u<>hY^1nsyR=ZPAiuN-vtMqJ`FANlSNeYj?g~v`7KlrWh0FL?IS?SD~Gsi5tLoL4NQ_}b> zH9g*cc&AQpC_QP8KY%D5LHYCW^&^$TU4Fz|*c8Y$cIFs5v(>=@V;IjWPv2Rf_pjEu zGtAw}+LqyKcM|h+n_A9fXo9(7$a$$A1uOLtrCMcOyrjunY7698+|;OMh#m|Moxc0{ z`al=RcckaVH;uikHIWQ;XWv6wnD9hP$Ib@FH@5T@iSZRgyVN`~Z)qwL6PW}EsXo&hBbkmr1Fm09u@fhz zLCEAmnR-VM)MO6sC{e9g1j9^Ii4y9BBE(r- zy@_*^wBrt|0)+ShA`H%@``tkNfvGG3aU$chQ0q~u-6bkFK=_Bp{r~8r^&jSK*idh( zZcv?znjZ-ewwiVL*~=jOt(XkL_toa-x=x?VJZb%_L#9R60h*0P9az&8&OI9ZxH|kN zHIb#Y-Mdu1TP(4B8>CMkILB&_ImgT+I@-J8+&ajSRB@VWM5F{@p$+M2kwUQQE%}5q z4w16h)UBW?MNp6hbDDyd{@0U|^lWnc{u^mRnx7hf?r3opH@2>J3@Y1(sEj~)CuXVy z1)+_QIhVghM(U4KY7IjXR$9ADXuLsubL*MgQ_`a;>Dl!3gM>6WEuFt`wWOh0;W%C3 zj2AWc1HdK5c!e>JHvno8#J!pQWdNa(Bnu68G{hYdB2eCUgJvW_r zGdDASdT98V!L~--0zC?y4G2*uRlGN7efj3zuNvJ|wiao2E_8P!r!kyujOLlckYXra z@k#N}@X<=lJvghxOcK1fC7i2s?{{`4=Eg_or2~VnzHSUdEUs^*-lSPI{(;9cW7FdU zo&L)YAMW&pv-K?y9BVBk+43;G@78;u&MaLh87i zthYqANj?cV2&dEa0m6Ao6e1>_GvfIuE{8y&&W`{eN;+Oy7?z@QDTA;aTmsg0b@!2i2x%l zO}Jt^)ELP`N?=d3s*|cQe>~|LJ5AAI=|pLQU$R2ix!r#D)RV*;D5t5J`{UBsob=|! z{npmz$|ifZwY%Igh`T~5hJx?yFbq+@S4a(wG?W6el9msoF^h49KH7Bd3CKD!Aw3?G zM&_hxX*|>yUE9!JYW7t+s5GoJhc;M3Ys@sYCT>P)A$6tHQ<+F>h?~1}8c=?Ppru}q zzuK)fWjBQI5y~AXl%vJAaE7`yQ`-}INSV{p6JnPCH*ZG0*Crm$(yEGMKw@HMe0rw) z?#SQ>^sY1}&Cblclx81azj- zZ|SDNAwnb-T4-8>5>qTk>nU>d2*4FO-&GW^-q;BfK8WFn2BF7C}W+Q367E8o(03!vYRlgh5KG6g5R~ z6dX#o9pVr)9|c%nilrPGgq#40DNm3!HG>Qy8GZ~rK1!SuAWX+222KII{fOF8`mSk_ zNJ1i&zCLBSkVdv0S}agz&dWB&;V zNg#^4P-15)j4gl){_$eG1sci|L@d|(D(#`)KjQhjAFTQFm1RXoHPtHZl^*AV*H3Fr z9daAtZ_#8BzPC2lHFP%fnE6-x3`;G8Mb=Pubz4cD|F1Uf{73)bpZw0>`+xmO=?^)L z?sbhVm4ty9PcB0ho6arMHw8e5SmLPZZsWYLR9p$0R0j`Z9+{hB6G1Hhh3(t2a> zR`>P5)0q)y@VVI$Nz$Lbm08!{@n9P2GBH1gQt5>xZEy9iZfq&k#{k{jCf_%jP)?P} z-RhZ4OxbTfUtQk?$%vPUe9?8K*cgE{%mpOCPIC~z&TsbR;N9YcC)RsvW_Iq*xKwQE zudHbU1MpxK=I%mAZ?Ux_tHGaN8fj2SZHXa?4F(6sqQ8gxj2bv1KPG1N3galw-sxwaVDm+5NM!j_k4K#p+k(o|| zm~_(ihlps2D6J71=^?1hDSrY&LY?S?gQL`FCz|dM52`Wtz_x{2kWNXBaAyiK z2mvP?BGj2^9#LEZyQ~NSLK0;7jTn;El?dmmJ80mCn1&nN!XZ+AS`Sx^A|A>Q^a2Gl z>YKCbnhP4*e-?xk5b(pFI-DgVLy-oAn5cn}R=O7;1QAQso;8l}zd9NGyC1Ck<0bjo zduvZNssX}BBhO_J{&r0U;d^T|d?M?p`PX|5Uz-PuOo8Rc>?kD_dk-yI@)!T^%fI|& zcU9g|&)Pct{<{?>n(dQ8NIhkY$)2LrS;i1yE@BsH5R+%=%r(>CLheLE0Wo$$X+(Jx z$uWefWl=zsG0Dix?eNmd57q&tW3Z?-oN4!EXyWEmkEiCOk*SGSQ*$@o%s|XYi5HKr zT`)I$N>uJ5m0PLyf)IrunKeSa_Q_H5#)@sdg|;4$10^O3xbo)iyvEKS!k3;&#HYK{ z()|gE`pqQ9hX;D=OwDWR0ijM@I@i>dZ|%tg2<;(ANl2wqOR(GlnG!Ct#Bt{tjS>5$ zSJM2_7>}6Yw+>azU7UfLepUV&Fk8G#uQ$q3S=50#hSL|wt;W!0(DxwB)z)$ zN-9wGuU569Gz25FHExE1L?IQ6U#wtNd=1KnUEe*udtEoLRk?4WB8horqCQH1SWN(9$sl}BEg?*5Qu;uSV(%#KwRX?Qeqz9XQhV2B3==~ zmK;G3Imix8gDus93a2VlF=aMRM`A7(QFNk212v(*G#6h!#xFM^jlQ0KG$!4fktU=$ zY4Z8YYZv+>A!o?n5@@%YE#F&WS@z)~3*`XkG7(!Ukwq2UT4!WceWd=>qnFgBMSA*X z_Sy8ztLa&3ethiBO?LZtD)ah!=i0iqit5(&D9#-50&5SI^>&&Hwb)3XBG=Nh zv@T*e^%y0}y~}5RXtHJ1yA`&cQZ4B->g{3*=bA#x8+|L)U8@bJm6iY^^K|hQHPMx| zoduQwsLy<}PhkwLX!5Vr#ufInO6S=;b5FKDf_I1&x_I_6jj_cbC7wM@b3Q!q~#I%I5nbNgZfLU>MkJVT}6mCF|^Yn&Ozc#)}nt--g(Dc&TEB!=?EVo&($ z1wu##lIqgoGp4FZQ+-~*OVVd4rAYALekiF38xk8nzps9nF6%_qB!7wl~S$CT1n}bY9L5dbf((y(= zWFlCYuXR&Okuw}ugd=k}QgY=g-A`)XZG^$sB3%f$U2E{HRkf2+)Y8PT3S3Lq@u#)n z|K{V3|Hq1w{DZZ}j#rcWyEW-&32HX5zzFGY=a-+GqKS zmVk)m)VJpxcYL$gu==1iVag=LHY1E)-8cgdv zrI~uiu%{`A$e(&@r9&_kDN&qJi6%%^3|VQ2tZwSHoO|+gYI;tZelt0F@6F7^N$JH5 z{hnv%W=4TU>A5s|*WT5iq3Wn`P{k%wu);wj*pL+*yC}}qx1ls2Pz_B!m!z4wvFER* z9!yD(M01^#NKCza@bLQC%ctWPPxrugw{(JMkd;(+I|=zT&cPB{Yw;`8fn_zF$9kTR z<1xOtVzKM;YaQ9fzAQBrnkeiDhlU6WrgDudTNle{^5<%Ud3s-#+LfX5W@@Qcj*Qh)jZNWdT3j8;pZT>*9vGj!c_=%d~PUG`8&#FysyIzC8bb_A@!gp>m z2;W1g_O?w)I2tY{f=osGG zI+US~oalf4i0VjZpCgCq`3IAek0-}Q<`ZM0;uN}SR+7x8uj7TCrUNz8Y$@PGsgY=! zXYQ^*@u&5yXbA1}UAaDqi%(xT9owyKQJ%ErH-)m)G+AA-EnH{~W*L21x?r|8kcXEE zeKb!;BTpdC2=KaauGX*ALodcMwegkp-hvv_E{*Qah3@HjY4Dj;VLiLFrY+ypff7GQ z)0Jlk=IFfHnhw&P>R7&^S7GhWM0tHk*B|qbNYbvr4O-E{63eKuhi~2+fAjd^-Ak{Z z-??||{Hy2p?%cUDKJ{jLcH-*&*Vmp*JbwA|!Bj%kckTMC(cvo>3#!_4@IvG4!xu_Y zPwOf%`+$>j9d)8%Jx4&y9LC47m@Hg=rLH;gb6FmecQ z!daQoAwf8X(?Spuob-VbjkieUh9V?h0?dNV?AT>T823OuOYz>Lbr*_Z7|?=Tg9=Ah z)DS4HO9&L}U@-a8ijXM?uhdfADc!j_RH%0qYuYLr+A6hen;qdlt_l9rFSh+ku|avH z>BPy}^Kt9bH%|dVr%{&%p-}aZLV&PP6DU&oOSBPe zl}$eE7DA?=1UGb|u+F!2frXHUC~8>?az!YLPTozjP}G{jnSAUELCCv50U;|w9HQb( zpi7irG`Cr4YdqHyT;9}~qr232cKXV^^irbLMaO2PiCO9Sc;eN_G(L>8_hv3YXbO}j zN7g6j;D*zxzi?YGq0#PF+Ct0gBAeST^}mpwB`BY?32;NlSv73^VxJ_LYj9B zU(cv>;Nz45?Kb*=C5BGqpKs|_=!4}}6sCc%8bS)kg|=&BxD#poUQfhzOsy-gwq)1) zR%tpjOr3eQ=yFqdwJDILcW0}b^VF>cCO?GcDor3;OKHAU^{lG)mmbw0Z>k&Yb4iIi zqX|iK`sFgs*`;+sh)brWgZB zg~?TD@MSf$)C}H`X2)+#O6IFG#;en|8?&BgQs*Uo_j_|(%53V|*)lLP zCxr&iQf z>5`p)nY*FjP*ZhP!^K|v)3N6@W>2$8p9JCjtPH|;Y%&PnQv(PC{pY?utoy6|CV;R| z-$_H+)P99AM7m8M$klmsjKLgJ1RyLjb`{uZ4PH2aw-AJ^|JZ>Isv{MiCSMwU*s`Z1 zDc&66WZ78>~r>5^x?9uq$IcZ#yrV_Jc(=+okAnUB5 z|MKcuN0B97My;Vid|pn3x^AeRa_1 zvR0`zJL=6vM@?DBS~99!s~WtkwZYZuKt_!_`&hf;Xshx-{jLUGjjplXS%3dZOp+!* zsrEb4I@gV5b^eu_K#nDvW584D%GCz54S_6OFC=(@!GrIpz}T0m4OOZwk>Mdpn%UX) z^2@r;3Tqgz6!>a#)!ses0eU9G&ptfnU#9C@+8Fq%)|Yv*t?m4y7r2s?xH&8Bi{8uE zMlu>BKX}g1N%P^p3nkUM0!x6tN39soX9+5u^j(kw6Y7eqMj3k*mbW7xfTc>Y1}}Yg zzypgdQBj%XnfOBa6(=3WmbDtk&@K=tI?;fLgszmx!xeEMY!PPSQl8=}1T)bKhPDep z$PSA91cZD_IsiuiXK{5d$Afmfnl7AXCAHF!_JtH=1Tw3DE+DYdNOQ_S&k?u8s6iH> zOhLMn)LnsOG|ECtLJ(Vk5EKL*(;!3uhd@4Dg7_zh%e#y~IFjX>eCtfDJIubF&O!W| zGELq2miQWrd$S|>FRT6kvLugqOE<0U|-4dht~MTHulLhZ>lp*W^VWJ-+Gv6Dau{U<8-(v!=i^dGev zCL0UI6xd8YZX--%j~%<*a7Ybf1~7(7a0VfX579eA6qJ;6rO68lib~fgO$DxXXQ?Br zw5LkjE^hH%+#kJN6~C_o2|-rrzFK#2BKDkC03IEG{qoVR>lcPjb@{@94ujKHYckguO?tB_ zE!AC06gjVaSrz@V&O;M4o4lE7S1yge_GCAzrX6XR?-h41p@BKb_tFq->rXiM% z=V_-|Q8F6*x%$D)o~x?9TU%V0zSeY?n0;l=&K0$R2LFZegkDix7~nCgX_2@wW|sQFC@HDaU~O33YhU<0m2zOdhlsOx4BvNO+c`dhp$IGRS| z!jx&Ua(ccB%P-zCZx}foAS?->LaH*8uthx8D5mN{5s5H^5CJ~QP-Olw{F2xc>JW;J zMO%`W3u-LkfEFW=Geje9{N~_VV`zgexJHK;Tbk1g{3C3z7UU)_Z79t` z8g(u@jwUAPNbqN=GDL-&155D6vNhZLT6( zH=w`5*s0X_<~I2<>q0AQJ6G5HiVV^9)_8^9wGf2qFg_O~FUpPzv$xy^%F%tuscm^wAP~KVJ5WO1Gz@wRqWAoSM68EI}2Q%Z3Ctp9E9(g!B zaVs%%X?8mFLbBaW96dE!(tLeoW8dofKz?I;xvrI}AC10TO>?2%rBM5F)HJ7Prn)&_ z4c$O}1+#S>E7f*|5jYQ5Scbo>jTUP?*_w_5b4X!^d<+*gHD?>6t7_Uao4j~|$kY3h z0>uzkr~_Hr&Lt-U5Q6wb3QHhYPs1p`IpNp!Ty;O3DNuWqkV_-*tqTdrEM!LvT|$5` z?SwAjIK))L%^)k>KR`%05#&QW_Zme?#uWl-3evEIbXas2WeSoY6Ck8Kg(5@%AVf;g zjx?ND$dFkGP^CeLOpq0vF$}@S6vQ`zx4PWMw!Efx?rxff9KuM(Dv)o3fSMAZe2LA* z$XcCu*%9CWUaI|d{=P-!NB@oD(9*-YT^(KjR^RiF7H$3Ki*vJg*J$eumwO!#N1v#g z!)~Wd2H|hZWDvfmHa|Dr(Q{_$apT_{buM@G6>6hms9fhdLvXjH>uB@f502hSb2!f& z%(F!D3}LSM1noe#E`h$PGwD@sY_uC(sCK*SfLdKBJq#f7kNTz z&=eOngw|+5#BjcY)`Y6m2R7um24oJ*( z`ed(B!q0*>LO2bj__0bun2?#O8f8FoJaPaC;h-Aj#u+jQwSHzHBbMRHr@+%7L?jL+ z?}3wnD=7yB0Wcz;aQwA^b0qRClnHQ<6L<{v^8Yv;h{Bb;XbDG*93c);_%PeG3h`%v7 zcJa}W5>8MA3x!@JnreK7b>2V9YWkgj`t|Srn{U^CulaCI^{=*^{La<=C%SHc@OPIe zGImteA8$GrGv6D1tZ9vUTO2qkee-vCkYA$fO%;mw0Jbn?$H~~9~=`?8Bag?U%GK+Xo{vuLo0h>iw z6uwlri$WRkVrX5myoR${N@i;$+(O;vK|@<2xuy{CmaF#WXna{J&+3Mbm33_^8`?8e z?rg0OUZD5qX*}6Y9XTo&0tz*Gq0XPL3*am`tw$&nGr0-+JIIds9GxGL04LRm0-U%d z0e%v`xCfDj6LXIwdigLSrC*`-<{JYFgCEW}&yavaGa&)FmJ}gAd-AE`G16RaD zAfFCrUC8GW;Y4pz&?~u21i&dy#D`+w@#jLA*+y3s@)27F6rs8UAmkpWX-!F0quS&Y z-lbaFQiEb@rf;e*)%z>8tywkQf0=Xq|M_44e}3;@{L8%Wo0fiOSho4_HmCQ`>cju- z;({E2o>hn2zxmGkr^}CiQfjTN?OF%m>4U&tmfoLhjFsqniu7H%rrr$e zK#n=KQq^7-JOAy_twn+UubrV)jcp|cDi#&SK%vpgJP?h4G&+K&1cSarj8Kzss#8rD ztZ*BNJT6iNz>@q+7P26v=v<0OKKfQ$uyjbV5D94TyBNhUvZABRJcb{K=@e_S%8t7! za!HCv)-LH17R4y1=T02ycs|4~KoWujJYXSYE*u=nR(k<9Vw=_vB4W#A!Zu&$1D0~t zC<@&O=WD!hfPqMsA}Acl1oo&URpbN0rL#g94)&xyohA)JW+C!q3WB(Kdg_UaU67CY z$DaBW)C-jOlp+m1F_vhiFyetp9HMGcmU_bZ$v=iHFh9}BNaqRK*-sGTet`MjsyrnDQweOF{0k)oN6*m@6s&^ z>d*j*6bL~<>OPa|eTsNGB!(Wsk)CrvWIBv3@lt?oAqWe^9HiV#_!AH^HEB1sxC9c@ z{fH}q%>|9l)koYJJ8hq>F8^$`V#Ve|-|lWIs6(K}hpc$RK>TCWG*Owb^M`-|)9J9e;Do`jvG+VT>$0+OhheJEQQ>-~X@v z_+R{4?uNte%Ifwab$gLLyjmAR*_uPOJ<$SPXP&8Rg*mi$=;9u|@xTA)*?(TySnRu$ ztMXT>sgpeZh zhpCb?^%(ha(f2~*`KXqU>M$8FY38MRh460Lsl;c5qFs%qVPh1*X9&W*xOFl7^Klhj;bGKpDj=GiGj^yq+SypXb zZw_y8`2V0P`2SnAYuUzw%EP*oSb0EFI-_9O_UIc^}7LHN#02I2c^iMiIU zQ%g=Xe^}*QW*bge3_bLX3> zFwHmmGOe)_H=Z2QHT~Pa`#a0>@-{`!WE=aG8fu3tutgL!2@8QOi8MhqkO|J!hB?L{ zG$QryFb3eFDl$pIGzhUpn8ve;k1p{kJgX!t^Lo znR#$7%GtT}=kSlt;+O^LK@mJXQhN|&Dqle(^)~?v5tB)^ok8l0tMX=<`4%U{*pjiNJ>Mf)D|kS0to;^(ovMd(BLM+Vz>JQH zK?@8*b~=iD(zUg?c>yh8jCx~<0f=jBPrT`>ep~{7n)Nx7YrFlUi`}pMmu*Bbh zVBybz5br2rQ42at+yF5ZvR>y}3*mmEHS?$~ugY3kYgw;qQL5V3*@J)F(DmOf+xB_I zo{B0r2BTe^_yL~+>_14x>%l;mch#~#v!vx^}*!^ceW#%W$0Vs9JIb1^9-7gpAMFE zo%+@s%h81Lb)H;{KhN1sU2Ndg-G=H&71pT2-U%k zVUIybG!?r^Ofp{)lbVG&O*jNftt13l5yFv-3VPAuf?3GB9OhHFNKF^49N>bLV}4rN z^0^|+&dea{X#I*NFF*)asC|%t)E1}>APfp-*0pEVcjTx%EDfoaQaFf6aAN9UyP%R2 z0j3@0toEfHd8R>_7KrH>0GR7e=`6urLDr-q1phdIEuH{>I{FmpCPU_N>T?k?Q||=) z9N+*UP{+@ND-xnqLF$jN}ZQ4`aHPiV3O9U+eA;Ml#kR>@y zkpfz!+T_$QI7-!+3qS~`_Chr+I;gaWYFiDlkn^Dgkm+KEHe^MzGF3Y&xCkhMh#mwg zsS7JU0@8a6b#0q$oon>re3hq2<1W{@%2jQpnwCmS;9oa(|35w{{Wr^%%EMZ%#&|aB zeERCK+1(ZLd1Vm(woC@$duoZqeEZF2r zE-=!tiXvmDs7(#SX?%rQOkpD$2un=V3nMN5SO$@l5e;l4>_o{5oV56HON=LJl(sxzHyTa;Ykg!V!ZamCARq%S9~}uc`8vJAKemQT{T(sO1co7R-Z_ zXcM}P=&1JOHo3AJ+Q~)Nm|V4+`Nxju#;OaMAVa1UA7Z_T_G=n927Z z+*^882ay%?iSy)BI1>0F4)M9--jZZuTEDHCOlnF>CInm>gh+!sM|wIXl`TLBSR$VS z6LlXgR(oje6diTj;RLMz`08}I6bMVG9@SH-_f=4r9OO?REYw4Qwi12ugpCls(5#^{ zZAj5Pm}vkU$*0PertbzM}MY`1YEH_zEbTjG33X}0i7-PEJRq$jhKkd! z=;8>+r=xg1wJlNNbP3C=3mH-ctH_W^z#svqm3r_d*|rA+GNJ$ykPrN0uwhF=P&CwW z4D%TH5t)!OLnOk;mg&f3!bd}Hpc+#U9~n;r?+w_}r)JeCoIA_lP8NcYv!dJ#h$kh= z+49Z9ab12U9M7;pfO$zzNjTj}3WP|B6S3t(dY0i}8G7 zXauLHUHqL0{!xZ0Z1X8N1YCiFyg*W=IXU{CQ}V|F)WtARaLb>gr-?>EL0*-fZ-8=3 zb+<`_5L+0&xbXuQ}Ju!B4`rR%WgugA5LHM3pLYiwHIJ>OD^B;~j|5=UyuWDNULu2qS zY6Bk}ZvWt@=fe}8Ust<6RtG=UcKx+F^fw0IN5;<2^*xL2y^DYG+xjwE`w+->4hqku(wUm&xSuC5OgDoVtrtQN4))~jefK|`Z}hj)($=aEr8T>ZYK@i}HA~S}joQSh zJt9KwT~!oCTdlp-CT0?ZC@MAvpkI`8v7 z=e!TT#oW!bxcn|)QTIPK)&G=azP{wYM|+J{qjYCi|LUgQ--xJN4iol_b6)qTg*^9Z zM6QbON;3WCz<-0sg{YcaasWa^hk~Y z8|J8|*tgp{kaDGx{2wI-beG$^sENymnAexJOy1v7AhF8Up#C#=E=(tlO&0L*)j}Hu zE~vdweSeMBv#Q&NgWgzZlM)BxPHlZ#OL zEMSP-Ea4~fa1zrPprzC?apz>&UnkywgNyJe1PM6b5hrLVJjvaZ2|J5)9I`%M2!#!h z%~d9yDNFAO$?9aiL^)-pnYzYidoSq0;vzwSA9T9*aigAlwgieCz@Nl(f5^p9IGc^y(7Ha#{7DW?_isep#*ty-6c}H@-VDVwUSR;-3-Z6@AD|%zvCY!0jcTPm+8z0p{3`V43$FZ2JNx%Syq*TUuTw^|UNpP? z>{3ds?G}@$h)hlzv+U=jmtg(?Ha2G}LCBHW@+y+2NZxqoLc~JVrL9Mnpnhw+fQ_y( zkBnc>aP;i}ruD+iM^yL2nb+v8c~|nK_NyXZj{CBQSA+}Nt|*GNe4(SE#~ILG;0gH3 zc2E6rNc~HT#k=gAQ6j&^f11UzY%|Uqa=C`|g?-GAZdY`>f9v*IG?z4VwL&2-p+W6- zQ<_b}V~$M;2O6N|RV}SDOsg)}L%1JE($~Y_SSksA)=q!?s;au~$Ji(93b4em-%94w zTMR$k{EHxlZ`h5bDe?HKZKi~_-}uv6mZLC#N(yco3LGWqFqcbB^9^Vcn=SKIYuL9V z2hixNoV{e?{~({_Z$|9gXLZfKQSuN;{{?@ui8XXN>G~LqYqY@@V6@dCv*qATyi!t7 zUR2twVUDp37&iCp`078J5&Gx5_rzz_q1!s`&!BeUhUA&(N&&o7xC;lQ7iX@=hk%bm zaOQA3(`mSZ8*<7T>VxgMj&(}`S4D@{LsvG?#@aHx=kFF{t7S|>o2s1F$rkQh;415e zBDWzJUczsb4xURe5j^`btmu#qID7~CE zD{EGmcR@||+koR=@c#TCRh^%)#vtc+)6P;BF(z;(Cx52Z!2XE3bWr5YD!QxAU=N^; zpY|4hEax+9rY~iN3E-bn1^B0qO{*qkgPg3cxwu=^PR0D#xt>Qs-a1IddI8-FgvJ&qwexB2Mn4nq2SsrhhpCnqQA*16Yyz{v4%oPL9Z!D)%w z;sTS`cYV;+4;ejJ_Wg#34DEFIG4O7u7b}&Q<1G?&@XeuZ-T#ZQ{ZN`XK}QKjaui&|vzzjka)aZ&QT_$r?dBW}@z>@lt^)|Hm#)K}*! zzt%}&6E|pz_iE^gk`#}Cmp*BxvF6%di2xNnGO*qdmmjNZDPHcZ5<7R_ftzDU1&XK+@b1ANms3ONn$xSs9pMCc>8`cVg zA!TtVhx@HA4H7d_mv72xMGD)Hy=?g-# zqf+i#9NQ)^V^0%`vxPHYypZXzO54RZn5oEpAj@c#3a*i;uik~^mZSZl#k3sbBN)@L zU3WM7Tj$Y&dUQeLQE%tiZsbZGolpRU4cPUD2UZl**Bd(ZEi$^U&?#WZwtn}^73{ad zvEfegZlSImtws!JNo!L>D@!BBs5eq4iot06Bs3v#R`!?o!KgFA?au_J#$wJ70|`2e zcl&nJ^YzEsF`Oud?mZ#lW#VZ8Kvv z?!k90x1sa(*$JDuTW%H&2)46H)@Z%9@lKUl;0K8ruzAQ}?b`d(MLM(q(L1DJmeMmn zHjvb$th92p*%&JzHB(0n0$un5-P|LE9dkW#dh!ja%KENM z7s9ST4KEsNGvS$dn|DQ{tV{x$c#C%Pvw_@$wubtC6hn7tUC6ytE&F-8F1d2N$eX5D z7!UpACoWNmPC<7)xfL~Z(1KbSQhw0$neq&kQ@hpO;nVZZa1n~XJcD1H*4Od5hvg&@ zZoa>~ITCbq;e{J#m7LYLW(w#LyABn@6FV_lAI>|4qijGEP?Ko`({agbBt@RUG0e1o z(4nI0pKbfA9gUJ-2w*HV5mzlED~Q&4LE0|+J$iW3w};#NeX>UtCR^K^_}lY!)v!+E5r7yEQwF zj;C)%$f67(+CM@`FR{h!98JVDS2SB^^P-bvF#65nl)caw#9j?#A+2`v6Vkn!C~SQc z30n%*X-8Ug&2R_IutFyhz&d0_Kh8Y6wougmxOUSDW3Eu4IthmHS|MBAx>!oF><{3P zzxQW7ssXS_s%Dk14Y({Kytc`9T?R9*AZ_DeDlu7X(X38)ZU-E3p1r9u3pxr(3LH8} z(Fm@}-GX0eh-Xzu-2|GqRHvV)ZhTWGXeatSRDN}~e&xVfSSgp+zn#nY7+K2v5V9iy zxgu^7HTT=fH|>-WJG-mM!QY8qup=Q$pppg@~|>4Gra4RcrGJt%&y$A|17 zw=m%ZXL3*oW=jSotv(Gd3;tfuwX?0p6ZjRXAYV2DXlVVNnGuFqb5Ddp}lbhLdXm6OkP#fg9mH$%IeC=($fW=Lg zvBy1)9~!<0G5X%t*X7!!q8l@H65+P~FQsojAH8urayk#3Z>s8Zwdm{V1YOl((7pID z9?I_!#iM%fxr2v2(90g!)Sm4>`crY3JjC6(b<)au-V?r~V6&Qf`Vw`fVr^1e+hgsv ze%}ZVa&`X;VhY0(C>5tU;U@~`=d5Q76?|0?_iZSw=+EFz@GSg1$wnk~Ysz%yt%tk8 zLHDUFae9X;p*vvn`ixxWX9VF6E%~S11$CY3ds&}6eDYAu$n;JI>raY6u$@VU_O18! zJ~zPM%V-q%G`J5t=#1#3q$JQCt1Bh!-i9Rak?o7(vd3;=D~@*fuD!#N*xK&J!X)dq zmgCKy1ZP&rFM-+CfGt!yAXRU=QSMKeuUr_#XuPX^qG_go#43Np>g$MAHr-c!#Wg&0 zaF7}?ty*mJ9X*y1A(IKm3=a=4c*<@1Ax74p|8j@kKnpeYxX7+mc~+rD^{ZVC)}L4y z+9wkO3U|yR=UrO)0U!0s`;6VRp{SsSGRoU*PClLPi^x8v~tnNHA_;PsDnoedil&bK#h;76F(zF~$q)80W9OjUHo9(a}l(k}I?i zWKsw~(b;OXyhB8Eht>WPf@jx%e3ej+ra)v^U-@f+P%ykPw%i0~6=Q7D26kl$c z%MU(2bopDbNYF0F*^E(AMybb(6jrd)t`b>67vFGO(zi@WH*RcfANnMkAWQa`dKzp3 zW3D9LxKjz+Uu}x6Q=pf^9!Xgo*HOcEI^+s$ZxzmZe~O;%50Fol2H@ze(p0bKn#48L zTxLjDShhhh4Oo(zOc)Ne+CVJjh~O!_2I82SwGoTnLzewRRn|Z73P)R z&l-#^Xg#N{Pk+dZoR?;U^-{UR&{x?p78jX`YF1ibQ+S2%FbrqQJukeEqn=+H)J4D(%M(Ec)_|I3mddkGX<#>9_A}oAA`o#W;MnZIdp@J5j~ z*P50DHF{8x`NydVK9iFbLk|pY(N-3g1vsA<{t#5T;Lv+Bylq`N_x7%yLO{>i;@VGk z&17>gX7d=*sot#l6C7P4Mnz>2Hu*bXjox?x% zwX%9PZ2^Z1>#e2(0xbdW*v~eH_8I)eXOLklGgYJmu#M^*zANGZ*O5f_Mq2=Et0Ud| zkTRd+T@s|XA9J*b%tXyfK8UsQr`aa?jnL1DrM>)JdI zOXe7#^!Ke1wSLa6H=T@rV-a<|N}|gL7G03yO1}M@3U0r~H$ludGl8a{O6%4#C(O#z z!#MQ*Us+5j0!rM_!Dx>ghc=Oy#(CV3lLqJeDa{hHqvBY|2Wn8YzJX@80U3uvty+Cz zJbGl%&a>+(lVwri&DsZ4-k}7sA0H+eT05}2ZvEGp!9ADnT}apjWs*%87N00BAwRV{ z)RU4_p`ctr`lV9l6>70@)-rP*X#ciW`SQ7%7V^2k(J+U(U1FH)`bEbGvqJ;m<^#Yu zbv6bVa6H!pQkO^5$&;0*U>D#eF4)I3#UJV}@s-pp)3BRP^4d$h4veU0N1|}Xw)y=i zMOuakYAAx^UDVCDywof9VHx!{eNBn6q}eW#6}N1a*YJAlzHGq!T~%X+70}k9M;8N?O){+xJCoCjfe%PM1v=ZM{%{7&7UAdaP4^vG7bs%%u%3j96DG`$x&G)wsx%uB=#QXZ zSmL^i2dEYbeX2O)2j@}H&Ft*$7@r(=ubpD%tZ270-O$l_syYFYQrKTHp=pE-nN8BAbIg_m0Um$rL04h5(>Dh|3#07}mDU)7gbkkk`w{h=PmS1>7y zcs^mDZBVx~HgO%sJ5r@oa9?H6tPYWXh&UQnN^qQ&y-)QrTa`Un>mLY$!whU{vkGGl zD1Qt$aRgOFJ9{Xn*REe2M7&eS^rD|^FY}bQO`Emac@uiYeBBuS~136J1A;Edp#WJ0WVAA&EnHLaf2)*NZydJh8ti_wl3q0L)*;v&As6;}P zrZQ6MZJu0Ak@+S8VeM>l`5acZe$+dG=)<-3J>G^kg*V-A!SPbYW}0x5(1NC5N9_=3 zy+Y?u_Tne!ry^f4xMX!4VF0*c+OYSdfrazQ!76rPvuVW)_9#wFFZtpChM^9Me3+2h zJ+O2mQgOOP?c8t1J0Pb$H;ZVzYFes9$yky^t7$h<)uKmW_oYFTLTeg!>96aiK6e%m8q#uVQ_-d?J_g&aK>@4FN!Wv6*&M4O1Fa8!KiybZy$^Y@!? z7wQVVSl%^UWttcf5`Ug{uUm|b%P@AXy}kp_(t#PhAGjqIbfrMqQE7NYa9$GxX)swTK~1FNo4c!@~`o)C9q@@H#ZpleJOTq z{tt08>|V@%O$-WFq4SAnpDNl$rg{8)j{qGxD>ZFUskLbTsMhIcHYL<)qfsYAc@k^B zzCMl99@T%B*If=^{sCCaFlR*fg1Z!eHz-o0MAp9IiN&5%=H!gKFHDA;Z<{{3aLlE8 z44~(RHs&^jj)hn{Dp-P_ngQkX>BjKntih0Q0|ZZ4AI+@qbGUDA&?#%VZ?G|tbxo8N z--`t66Cq+J5PItxgwPhE9A_n5YrI+BGiy>b5poAes>Ng`RpHdIpLM!TKxNjoMR=@^ zFmfi~q(Y~=4d=5Nk6!xHWCMbnz0=+wCP20Q$0SZ6wmts)xm%T&Y`C6{6K}m?)HvQ@ zK|5og#KVE%t$(+N_({USAb~A-c*6j_)?EE*BCB3QnDweTZtl1Nwg1wWfj2^9e`X-# zLvDrIM408)acO@%p(NqLp@IDx4{-Id_8yP7LsSvF1KI~wpBY>SP^bqK#EqHR0{nBN zGJwps@!O)#rK31wcf7dk?gL}KzX?^^6=F7aEh!tD@xq({TWmwHMSKZ+kj-W@B18AYV4+x{Iw!45*}>=vTsJ-)#|@ z&(w?BULENSZM}f`+dsbSm`nf<{RYyt^7i$AfJlvujO0CVzvl>^X$d?%+OIIJw!RvC zw(#g=y*?(1e>Qk~o8{yz5_>?LZ4O>VwIXEa{XJ~jyiRwa6n?KCPh!*^Ep0(|=k1Mu&G zN$RHjo4S%$P4yPF`wqmF#XvxfQU;Xr0)h)*tl0(x8&pN6MS|IT0tQu}yPm-!mwSQxCkW{qKF zp>>6|O*X8id%K-=RP~t3A+fT;LjryhIv4r$8{(7T63Rn6ld)ty-G)$0%$b*OcMOI= z#p>opBNxtF`k~JW=dXCo%i}{pYbO>w?eKD-f8F;wNl!09M<-D?q0K4xMyR~1gEBE z_yV%&OLVYf?jGRUUw~}R7|h!`^D5n#LMVC+=hX1jM>A7XkHxn_3OmnTXl(Dty$O1% zFa&?)sBBWLqF*!>Ugrun9yt)^_Tw#^c{g{6Iu(ZVm0`mg!a9-cz&4e*X|WhL8G%Z1 ztf5r{Nv>t@7{4$r>94nNJGEjJNe*RCnq{iE~;%=5V^D0kW)_QwB-y9m_1}7*rkyO$B>b_XkQP8AK{aH*h z&`+S^gy^zERu@!YN!?V0;Cvs0k7N6)giXLz7SQFTxL%Y7)=i2$?j&pe7rzPN+$i$S z;o$-^W#fbQ)-Hr+y0RPg{rM+)neSfL+J-VZZX2@PWA7_!Kgb2E;Maa5I1Upw4^6n{ zq}N|CuPzllO1Y(^koq<^BRwZABP%QCL(=C2WDw~Hz4mAAaANImiG6iWLQBBz<~*VM zz(XV0~Lg4UL;w)$w)8cf-1eb9MOokN99hyBYVaB-Gt zITcSr$oanaDOXH$|6X+aD7D$#G&;DS)h7qflEo9(F~Fj!dvIyKGjeEz?rT0$A6#lL zx1us~UGDlmYjkUg0-dvcetqo=(~=76{mTqPbr7t+vbftvn9M^Uc?#T~8Yg@gz=(lq z{JKjt0{ifNRt0)&%FbI(%nyNCsmXZglsx2TPy}%;RrzT$j<2#pt}vQDm6&Y_+!>x+ z9mw6a=ctxE@jS0@^PfE1O8pH3*&sL0bW|_>5*=s)OxQjrKgw0 z#@6oa4E6MQ9sc>VL3L7J+Cm=hu23?^2Xd6cFNag`JtVFq0p;dk(iuh}$paVeL+U>T z%7U#v%p$!B{nHz8Viv;pcoPk9u4(n&*sS8|l&#Ks`qRPIH2P3ae#Oub!ek*3UTxLreSlw7827-n&}uYp$8*|R>caeO{}&@%?E04S`Dmba zDgC_6J301aQGtHk2MOBCuIGBw9oP}Wlti|`wkQ4B?KL#^sy%vEuYnEkC3`;hSgEoK z*Q?8PXv~44dGNueG&HoYUp{|k)G?dUD2?s7*dzTVg0bQ&drRq8$=?!kvaaB}ztJZx zhUDkFxu|+Z;%;FS$zGF*WEsLaRh&HVsqAs_A2ua*Lx)=%=GS};6g80pOCy})DgW2S zRex@*yC$|-X+NAZQxv&AaVe3Rt5+q_`r1)XY)JlWBN5zj*D>q;!)&2!vaH^J2fI>d zWcr%aW)Z?qqQgA%!#rYX NQbYGSMD6W|{{vk4YT^I@ From e4ea506b65cb9dff846093730794a31f719cefea Mon Sep 17 00:00:00 2001 From: Bodmer Date: Mon, 23 Oct 2017 01:59:37 +0100 Subject: [PATCH 007/150] Update touch, add Button class Touch reliability improved by using pressure and double sampling. Added Adafruit compatible Button class. Added touchscreen on/off and keypad examples. --- TFT_eSPI.cpp | 247 +++++++++++++-- TFT_eSPI.h | 69 ++++- User_Setup.h | 10 +- User_Setups/Setup10_RPi_touch_ILI9486.h | 9 +- .../Keypad_240x320/Keypad_240x320.ino | 284 +++++++++++++++++ .../Keypad_480x320/Keypad_480x320.ino | 287 ++++++++++++++++++ .../Generic/On_Off_Button/On_Off_Button.ino | 206 +++++++++++++ 7 files changed, 1069 insertions(+), 43 deletions(-) create mode 100644 examples/320 x 240/Keypad_240x320/Keypad_240x320.ino create mode 100644 examples/480 x 320/Keypad_480x320/Keypad_480x320.ino create mode 100644 examples/Generic/On_Off_Button/On_Off_Button.ino diff --git a/TFT_eSPI.cpp b/TFT_eSPI.cpp index 8fc7fc9..5fea5b9 100644 --- a/TFT_eSPI.cpp +++ b/TFT_eSPI.cpp @@ -1179,6 +1179,15 @@ uint8_t TFT_eSPI::getRotation(void) return rotation; } +/*************************************************************************************** +** Function name: getTextDatum +** Description: Return the text datum value (as used by setTextDatum()) +***************************************************************************************/ +uint8_t TFT_eSPI::getTextDatum(void) +{ + return textdatum; +} + /*************************************************************************************** ** Function name: width @@ -3151,7 +3160,7 @@ int16_t TFT_eSPI::drawString(const char *string, int poX, int poY, int font) int16_t sumX = 0; uint8_t padding = 1, baseline = 0; uint16_t cwidth = textWidth(string, font); // Find the pixel width of the string in the font - uint16_t cheight = 8; + uint16_t cheight = 8 * textsize; #ifdef LOAD_GFXFF if (font == 1) { @@ -3643,14 +3652,15 @@ void spiWriteBlock(uint16_t color, uint32_t repeat) } #endif -// The following touch screen support code added by maxpautsch 1/10/17 +// The following touch screen support code by maxpautsch was merged 1/10/17 // https://github.com/maxpautsch // Define TOUCH_CS is the user setup file to enable this code -// An example is provided is 480x320 folder +// A demo is provided in examples Generic folder +// Additions by Bodmer to double sample and use Z value to improve detection reliability #ifdef TOUCH_CS /*************************************************************************************** ** Function name: getTouchRaw -** Description: read raw position of touchpad if pressed. Return false if not pressed. +** Description: read raw touch position. Return false if not pressed. ***************************************************************************************/ uint8_t TFT_eSPI::getTouchRaw(uint16_t *x, uint16_t *y){ uint16_t tmp; @@ -3671,13 +3681,6 @@ uint8_t TFT_eSPI::getTouchRaw(uint16_t *x, uint16_t *y){ tmp = tmp <<5; tmp |= 0x1f & (SPI.transfer(0)>>3); - if(tmp == 0 || tmp == 0x3ff){ - T_CS_H; - SPI.setFrequency(SPI_FREQUENCY); - spi_end(); - return false; - } - *x = tmp; // Start bit + XP sample request for y position @@ -3692,23 +3695,87 @@ uint8_t TFT_eSPI::getTouchRaw(uint16_t *x, uint16_t *y){ SPI.setFrequency(SPI_FREQUENCY); spi_end(); - if(tmp == 0 || tmp == 0x3ff){ - return false; - } return true; } /*************************************************************************************** -** Function name: getTouch -** Description: read callibrated position of touchpad if pressed. Return false if not pressed. +** Function name: getTouchRawZ +** Description: read raw pressure on touchpad and return Z value. ***************************************************************************************/ +uint16_t TFT_eSPI::getTouchRawZ(void){ + CS_H; + T_CS_L; + +#ifdef SPI_HAS_TRANSACTION + #ifdef SUPPORT_TRANSACTIONS + if (locked) {locked = false; SPI.beginTransaction(SPISettings(SPI_TOUCH_FREQUENCY, MSBFIRST, SPI_MODE0));} + #endif +#endif + + SPI.setFrequency(SPI_TOUCH_FREQUENCY); + + // Z sample request + uint16_t tz = 0xFFF; + SPI.transfer(0xb1); + tz += SPI.transfer16(0xc1) >> 3; + tz -= SPI.transfer16(0x91) >> 3; + + T_CS_H; + SPI.setFrequency(SPI_FREQUENCY); + spi_end(); + + return tz; +} + +/*************************************************************************************** +** Function name: validTouch +** Description: read validated position. Return false if not pressed. +***************************************************************************************/ +#define _RAWERR 10 // Deadband in position samples +uint8_t TFT_eSPI::validTouch(uint16_t *x, uint16_t *y, uint16_t threshold){ + uint16_t x_tmp, y_tmp, x_tmp2, y_tmp2; + + + // uint16_t z_tmp = getTouchRawZ(); // Save value for debug message + // Debug messages used for tuning + // Serial.print("Z = ");Serial.println(z_tmp); + + if (getTouchRawZ() <= threshold) return false; + + delay(2); // Small debounce delay to the next sample + getTouchRaw(&x_tmp,&y_tmp); + + // z_tmp = getTouchRawZ(); + // Serial.print("Sample 1 x,y = "); Serial.print(x_tmp);Serial.print(",");Serial.print(y_tmp); + // Serial.print(", Z = ");Serial.println(z_tmp); + + delay(2); // Small debounce delay to the next sample + if (getTouchRawZ() <= threshold) return false; + + delay(2); // Small debounce delay to the next sample + getTouchRaw(&x_tmp2,&y_tmp2); + + // Serial.print("Sample 2 x,y = "); Serial.print(x_tmp2);Serial.print(",");Serial.println(y_tmp2); + // Serial.print("Sample difference = ");Serial.print(abs(x_tmp - x_tmp2));Serial.print(",");Serial.println(abs(y_tmp - y_tmp2)); + + if (abs(x_tmp - x_tmp2) > _RAWERR) return false; + if (abs(y_tmp - y_tmp2) > _RAWERR) return false; + + *x = x_tmp; + *y = y_tmp; + + return true; +} + +/*************************************************************************************** +** Function name: getTouch +** Description: read callibrated position. Return false if not pressed. +***************************************************************************************/ +#define Z_THRESHOLD 0x200 // Touch pressure threshold for validating touches uint8_t TFT_eSPI::getTouch(uint16_t *x, uint16_t *y){ - uint8_t retVal; uint16_t x_tmp, y_tmp; - retVal = getTouchRaw(&x_tmp,&y_tmp); - if(!retVal) - return retVal; + if(!validTouch(&x_tmp, &y_tmp, Z_THRESHOLD)) return false; if(!touchCalibration_rotate){ *x=(x_tmp-touchCalibration_x0)*_width/touchCalibration_x1; @@ -3726,14 +3793,14 @@ uint8_t TFT_eSPI::getTouch(uint16_t *x, uint16_t *y){ *y = _height - *y; } - return retVal; + return true; } /*************************************************************************************** ** Function name: calibrateTouch ** Description: generates calibration parameters for touchscreen. ***************************************************************************************/ -uint8_t TFT_eSPI::calibrateTouch(uint16_t *parameters, uint32_t color_bg, uint32_t color_fg, uint8_t size){ +void TFT_eSPI::calibrateTouch(uint16_t *parameters, uint32_t color_fg, uint32_t color_bg, uint8_t size){ int16_t values[] = {0,0,0,0,0,0,0,0}; uint16_t x_tmp, y_tmp; @@ -3774,7 +3841,8 @@ uint8_t TFT_eSPI::calibrateTouch(uint16_t *parameters, uint32_t color_bg, uint32 if(i>0) delay(1000); for(uint8_t j= 0; j<8; j++){ - while(!getTouchRaw(&x_tmp, &y_tmp)) delay(100); + // Use a lower detect threshold as corners tend to be less sensitive + while(!validTouch(&x_tmp, &y_tmp, Z_THRESHOLD/2)) delay(10); values[i*2 ] += x_tmp; values[i*2+1] += y_tmp; } @@ -3847,8 +3915,141 @@ void TFT_eSPI::setTouch(uint16_t *parameters){ touchCalibration_invert_y = parameters[4] & 0x04; } +#else // TOUCH CS is not defined so generate dummy functions that do nothing + +/*************************************************************************************** +** Function name: getTouchRaw +** Description: read raw touch position. Return false if not pressed. +***************************************************************************************/ +uint8_t TFT_eSPI::getTouchRaw(uint16_t *x, uint16_t *y){ + return true; +} + +/*************************************************************************************** +** Function name: getTouchRawZ +** Description: read raw pressure on touchpad and return Z value. +***************************************************************************************/ +uint16_t TFT_eSPI::getTouchRawZ(void){ + return true; +} + +/*************************************************************************************** +** Function name: validTouch +** Description: read validated position. Return false if not pressed. +***************************************************************************************/ +uint8_t TFT_eSPI::validTouch(uint16_t *x, uint16_t *y, uint16_t threshold){ + return true; +} + +/*************************************************************************************** +** Function name: getTouch +** Description: read callibrated position. Return false if not pressed. +***************************************************************************************/ +uint8_t TFT_eSPI::getTouch(uint16_t *x, uint16_t *y){ + return true; +} + +/*************************************************************************************** +** Function name: calibrateTouch +** Description: generates calibration parameters for touchscreen. +***************************************************************************************/ +void TFT_eSPI::calibrateTouch(uint16_t *parameters, uint32_t color_bg, uint32_t color_fg, uint8_t size){ +} + + +/*************************************************************************************** +** Function name: setTouch +** Description: imports calibration parameters for touchscreen. +***************************************************************************************/ +void TFT_eSPI::setTouch(uint16_t *parameters){ +} + #endif // TOUCH_CS + + +/*************************************************************************************** +** Code for the GFX button UI element +** Grabbed from Adafruit_GFX library and enhanced to handle any label font +***************************************************************************************/ +TFT_eSPI_Button::TFT_eSPI_Button(void) { + _gfx = 0; +} + +// Classic initButton() function: pass center & size +void TFT_eSPI_Button::initButton( + TFT_eSPI *gfx, int16_t x, int16_t y, uint16_t w, uint16_t h, + uint16_t outline, uint16_t fill, uint16_t textcolor, + char *label, uint8_t textsize) +{ + // Tweak arguments and pass to the newer initButtonUL() function... + initButtonUL(gfx, x - (w / 2), y - (h / 2), w, h, outline, fill, + textcolor, label, textsize); +} + +// Newer function instead accepts upper-left corner & size +void TFT_eSPI_Button::initButtonUL( + TFT_eSPI *gfx, int16_t x1, int16_t y1, uint16_t w, uint16_t h, + uint16_t outline, uint16_t fill, uint16_t textcolor, + char *label, uint8_t textsize) +{ + _x1 = x1; + _y1 = y1; + _w = w; + _h = h; + _outlinecolor = outline; + _fillcolor = fill; + _textcolor = textcolor; + _textsize = textsize; + _gfx = gfx; + strncpy(_label, label, 9); +} + +void TFT_eSPI_Button::drawButton(boolean inverted) { + uint16_t fill, outline, text; + + if(!inverted) { + fill = _fillcolor; + outline = _outlinecolor; + text = _textcolor; + } else { + fill = _textcolor; + outline = _outlinecolor; + text = _fillcolor; + } + + uint8_t r = min(_w, _h) / 4; // Corner radius + _gfx->fillRoundRect(_x1, _y1, _w, _h, r, fill); + _gfx->drawRoundRect(_x1, _y1, _w, _h, r, outline); + + _gfx->setTextColor(text); + _gfx->setTextSize(_textsize); + + static byte tempdatum = _gfx->getTextDatum(); + _gfx->setTextDatum(MC_DATUM); + _gfx->drawString(_label, _x1 + (_w/2), _y1 + (_h/2)); + _gfx->setTextDatum(tempdatum); +} + +boolean TFT_eSPI_Button::contains(int16_t x, int16_t y) { + return ((x >= _x1) && (x < (_x1 + _w)) && + (y >= _y1) && (y < (_y1 + _h))); +} + +void TFT_eSPI_Button::press(boolean p) { + laststate = currstate; + currstate = p; +} + +boolean TFT_eSPI_Button::isPressed() { return currstate; } +boolean TFT_eSPI_Button::justPressed() { return (currstate && !laststate); } +boolean TFT_eSPI_Button::justReleased() { return (!currstate && laststate); } + + + + + + /*************************************************** The majority of code in this file is "FunWare", the only condition of use of those portions is that users have fun! Most of the effort has been spent on diff --git a/TFT_eSPI.h b/TFT_eSPI.h index 92d0401..5d174fd 100644 --- a/TFT_eSPI.h +++ b/TFT_eSPI.h @@ -393,7 +393,8 @@ class TFT_eSPI : public Print { // Set w and h to 1 to read 1 pixel's colour. The data buffer must be at least w * h * 3 bytes void readRectRGB(int32_t x0, int32_t y0, int32_t w, int32_t h, uint8_t *data); - uint8_t getRotation(void); + uint8_t getRotation(void), + getTextDatum(void); uint16_t fontsLoaded(void), color565(uint8_t r, uint8_t g, uint8_t b); @@ -425,37 +426,38 @@ class TFT_eSPI : public Print { textWidth(const String& string), fontHeight(int16_t font); - void setAddrWindow(int32_t xs, int32_t ys, int32_t xe, int32_t ye); + void setAddrWindow(int32_t xs, int32_t ys, int32_t xe, int32_t ye); -#ifdef TOUCH_CS - uint8_t getTouch(uint16_t *x, uint16_t *y); - uint8_t getTouchRaw(uint16_t *x, uint16_t *y); - uint8_t calibrateTouch(uint16_t *data, uint32_t color_bg, uint32_t color_fg, uint8_t size); - void setTouch(uint16_t *data); -#endif + // These are associated with the Touch Screen handlers + uint8_t getTouchRaw(uint16_t *x, uint16_t *y); + uint16_t getTouchRawZ(void); + uint8_t getTouch(uint16_t *x, uint16_t *y); + + void calibrateTouch(uint16_t *data, uint32_t color_fg, uint32_t color_bg, uint8_t size); + void setTouch(uint16_t *data); virtual size_t write(uint8_t); private: -inline void spi_begin() __attribute__((always_inline)); -inline void spi_end() __attribute__((always_inline)); + inline void spi_begin() __attribute__((always_inline)); + inline void spi_end() __attribute__((always_inline)); - void readAddrWindow(int32_t xs, int32_t ys, int32_t xe, int32_t ye); + void readAddrWindow(int32_t xs, int32_t ys, int32_t xe, int32_t ye); uint8_t tabcolor, colstart = 0, rowstart = 0; // some ST7735 displays need this changed volatile uint32_t *dcport, *csport;//, *mosiport, *clkport, *rsport; - uint32_t cspinmask, dcpinmask, wrpinmask;//, mosipinmask, clkpinmask; + uint32_t cspinmask, dcpinmask, wrpinmask;//, mosipinmask, clkpinmask; uint32_t lastColor = 0xFFFF; -#ifdef TOUCH_CS + // These are associated with the Touch Screen handlers + uint8_t validTouch(uint16_t *x, uint16_t *y, uint16_t threshold); uint16_t touchCalibration_x0, touchCalibration_x1, touchCalibration_y0, touchCalibration_y1; uint8_t touchCalibration_rotate, touchCalibration_invert_x, touchCalibration_invert_y; -#endif protected: @@ -482,6 +484,45 @@ inline void spi_end() __attribute__((always_inline)); }; +/*************************************************************************************** +// The following button class has been ported over from the Adafruit_GFX library so +// should be compatible. +// A slightly different implementation in this TFT_eSPI library allows the button +// legends to be in any font +***************************************************************************************/ + +class TFT_eSPI_Button { + + public: + TFT_eSPI_Button(void); + // "Classic" initButton() uses center & size + void initButton(TFT_eSPI *gfx, int16_t x, int16_t y, + uint16_t w, uint16_t h, uint16_t outline, uint16_t fill, + uint16_t textcolor, char *label, uint8_t textsize); + + // New/alt initButton() uses upper-left corner & size + void initButtonUL(TFT_eSPI *gfx, int16_t x1, int16_t y1, + uint16_t w, uint16_t h, uint16_t outline, uint16_t fill, + uint16_t textcolor, char *label, uint8_t textsize); + void drawButton(boolean inverted = false); + boolean contains(int16_t x, int16_t y); + + void press(boolean p); + boolean isPressed(); + boolean justPressed(); + boolean justReleased(); + + private: + TFT_eSPI *_gfx; + int16_t _x1, _y1; // Coordinates of top-left corner + uint16_t _w, _h; + uint8_t _textsize; + uint16_t _outlinecolor, _fillcolor, _textcolor; + char _label[10]; + + boolean currstate, laststate; +}; + #endif /*************************************************** diff --git a/User_Setup.h b/User_Setup.h index 62e6ff0..8fe0a82 100644 --- a/User_Setup.h +++ b/User_Setup.h @@ -152,10 +152,12 @@ // // ################################################################################## -// Define the SPI clock frequency +// Define the SPI clock frequency, this affects the graphics rendering speed. Too +// fast and the TFT driver will not keep up and display corruption appears. // With an ILI9341 display 40MHz works OK, 80MHz sometimes fails // With a ST7735 display more than 27MHz may not work (spurious pixels and lines) -// With an ILI9163 display TBD MHz works OK, +// With an ILI9163 display 27 MHz works OK. +// The RPi typically only works at 20MHz maximum. // #define SPI_FREQUENCY 1000000 // #define SPI_FREQUENCY 5000000 @@ -172,6 +174,10 @@ // supported. Tranaction support is required if other SPI devices are connected. // When commented out the code size will be smaller and sketches will // run slightly faster, so leave it commented out unless you need it! + // Transaction support is needed to work with SD library but not needed with TFT_SdFat +// Transactions are automatically enabled by the library for an ESP32 (to use HAL mutex) +// so changing it here has noo effect + // #define SUPPORT_TRANSACTIONS diff --git a/User_Setups/Setup10_RPi_touch_ILI9486.h b/User_Setups/Setup10_RPi_touch_ILI9486.h index b572e0f..34e23c1 100644 --- a/User_Setups/Setup10_RPi_touch_ILI9486.h +++ b/User_Setups/Setup10_RPi_touch_ILI9486.h @@ -76,12 +76,13 @@ // ###### EDIT THE PIN NUMBERS IN THE LINES FOLLOWING TO SUIT YOUR SETUP ###### -// ModeMCU +// For NodeMCU - use pin numbers in the form PIN_Dx where Dx is the NodeMCU pin designation #define TFT_CS PIN_D8 // Chip select control pin D8 #define TFT_DC PIN_D3 // Data Command control pin -//#define TFT_RST PIN_D4 // Reset pin (could connect to NodeMCU RST, see next line) -#define TFT_RST -1 // Set TFT_RST to -1 if the display RESET is connected to NodeMCU RST or 3.3V -#define TOUCH_CS D4 // Chip select pin (T_CS) of touch screen controller XPT2046 +#define TFT_RST PIN_D4 // Reset pin (could connect to NodeMCU RST, see next line) +//#define TFT_RST -1 // Set TFT_RST to -1 if the display RESET is connected to NodeMCU RST or 3.3V + +#define TOUCH_CS PIN_D1 // Chip select pin (T_CS) of touch screen, also define SPI frequency in Section 5 below //#define TFT_WR PIN_D2 // Write strobe for modified Raspberry Pi TFT only diff --git a/examples/320 x 240/Keypad_240x320/Keypad_240x320.ino b/examples/320 x 240/Keypad_240x320/Keypad_240x320.ino new file mode 100644 index 0000000..a6c503f --- /dev/null +++ b/examples/320 x 240/Keypad_240x320/Keypad_240x320.ino @@ -0,0 +1,284 @@ +/* + The TFT_eSPI library incorporates an Adafruit_GFX compatible + button handling class, this sketch is based on the Arduin-o-phone + example. + + This example diplays a keypad where numbers can be entered and + send to the Serial Monitor window. + + The sketch has been tested on the ESP8266 (which supports SPIFFS) + + The minimum screen size is 320 x 240 as that is the keypad size. +*/ + +// The SPIFFS (FLASH filing system) is used to hold touch screen +// calibration data + +#include "FS.h" + +#include +#include // Hardware-specific library + +TFT_eSPI tft = TFT_eSPI(); // Invoke custom library + +// This is the file name used to store the calibration data +// You can change this to create new calibration files. +// The SPIFFS file name must start with "/". +#define CALIBRATION_FILE "/TouchCalData1" + +// Set REPEAT_CAL to true instead of false to run calibration +// again, otherwise it will only be done once. +// Repeat calibration if you change the screen rotation. +#define REPEAT_CAL false + +// Keypad start position, key sizes and spacing +#define KEY_X 40 // Centre of key +#define KEY_Y 96 +#define KEY_W 62 // Width and height +#define KEY_H 30 +#define KEY_SPACING_X 18 // X and Y gap +#define KEY_SPACING_Y 20 +#define KEY_TEXTSIZE 1 // Font size multiplier + +// Using two fonts since numbers are nice when bold +#define LABEL1_FONT &FreeSansOblique12pt7b // Key label font 1 +#define LABEL2_FONT &FreeSansBold12pt7b // Key label font 2 + +// Numeric display box size and location +#define DISP_X 1 +#define DISP_Y 10 +#define DISP_W 238 +#define DISP_H 50 +#define DISP_TSIZE 3 +#define DISP_TCOLOR TFT_CYAN + +// Number length, buffer for storing it and character index +#define NUM_LEN 12 +char numberBuffer[NUM_LEN + 1] = ""; +uint8_t numberIndex = 0; + +// We have a status line for messages +#define STATUS_X 120 // Centred on this +#define STATUS_Y 65 + +// Create 15 keys for the keypad +char keyLabel[15][5] = {"New", "Del", "Send", "1", "2", "3", "4", "5", "6", "7", "8", "9", ".", "0", "#" }; +uint16_t keyColor[15] = {TFT_RED, TFT_DARKGREY, TFT_DARKGREEN, + TFT_BLUE, TFT_BLUE, TFT_BLUE, + TFT_BLUE, TFT_BLUE, TFT_BLUE, + TFT_BLUE, TFT_BLUE, TFT_BLUE, + TFT_BLUE, TFT_BLUE, TFT_BLUE + }; + +// Invoke the TFT_eSPI button class and create all the button objects +TFT_eSPI_Button key[15]; + +//------------------------------------------------------------------------------------------ + +void setup() { + // Use serial port + Serial.begin(9600); + + // Initialise the TFT screen + tft.init(); + + // Set the rotation before we calibrate + tft.setRotation(0); + + // Calibrate the touch screen and retrieve the scaling factors + touch_calibrate(); + + // Clear the screen + tft.fillScreen(TFT_BLACK); + + // Draw keypad background + tft.fillRect(0, 0, 240, 320, TFT_DARKGREY); + + // Draw number display area and frame + tft.fillRect(DISP_X, DISP_Y, DISP_W, DISP_H, TFT_BLACK); + tft.drawRect(DISP_X, DISP_Y, DISP_W, DISP_H, TFT_WHITE); + + // Draw keypad + drawKeypad(); +} + +//------------------------------------------------------------------------------------------ + +void loop(void) { + uint16_t t_x = 0, t_y = 0; // To store the touch coordinates + + // Pressed will be set true is there is a valid touch on the screen + boolean pressed = tft.getTouch(&t_x, &t_y); + + // / Check if any key coordinate boxes contain the touch coordinates + for (uint8_t b = 0; b < 15; b++) { + if (pressed && key[b].contains(t_x, t_y)) { + key[b].press(true); // tell the button it is pressed + } else { + key[b].press(false); // tell the button it is NOT pressed + } + } + + // Check if any key has changed state + for (uint8_t b = 0; b < 15; b++) { + + if (b < 3) tft.setFreeFont(LABEL1_FONT); + else tft.setFreeFont(LABEL2_FONT); + + if (key[b].justReleased()) key[b].drawButton(); // draw normal + + if (key[b].justPressed()) { + key[b].drawButton(true); // draw invert + + // if a numberpad button, append the relevant # to the numberBuffer + if (b >= 3) { + if (numberIndex < NUM_LEN) { + numberBuffer[numberIndex] = keyLabel[b][0]; + numberIndex++; + numberBuffer[numberIndex] = 0; // zero terminate + } + status(""); // Clear the old status + } + + // Del button, so delete last char + if (b == 1) { + numberBuffer[numberIndex] = 0; + if (numberIndex > 0) { + numberIndex--; + numberBuffer[numberIndex] = 0;//' '; + } + status(""); // Clear the old status + } + + if (b == 2) { + status("Sent value to serial port"); + Serial.println(numberBuffer); + } + // we dont really check that the text field makes sense + // just try to call + if (b == 0) { + status("Value cleared"); + numberIndex = 0; // Reset index to 0 + numberBuffer[numberIndex] = 0; // Place null in buffer + } + + // Update the number display field + tft.setTextDatum(TL_DATUM); // Use top left corner as text coord datum + tft.setFreeFont(&FreeSans18pt7b); // Choose a nicefont that fits box + tft.setTextColor(DISP_TCOLOR); // Set the font colour + + // Draw the string, the value returned is the width in pixels + int xwidth = tft.drawString(numberBuffer, DISP_X + 4, DISP_Y + 12); + + // Now cover up the rest of the line up by drawing a black rectangle. No flicker this way + // but it will not work with italic or oblique fonts due to character overlap. + tft.fillRect(DISP_X + 4 + xwidth, DISP_Y + 1, DISP_W - xwidth - 5, DISP_H - 2, TFT_BLACK); + + delay(10); // UI debouncing + } + } +} + +//------------------------------------------------------------------------------------------ + +void drawKeypad() +{ + // Draw the keys + for (uint8_t row = 0; row < 5; row++) { + for (uint8_t col = 0; col < 3; col++) { + uint8_t b = col + row * 3; + + if (b < 3) tft.setFreeFont(LABEL1_FONT); + else tft.setFreeFont(LABEL2_FONT); + + key[b].initButton(&tft, KEY_X + col * (KEY_W + KEY_SPACING_X), + KEY_Y + row * (KEY_H + KEY_SPACING_Y), // x, y, w, h, outline, fill, text + KEY_W, KEY_H, TFT_WHITE, keyColor[b], TFT_WHITE, + keyLabel[b], KEY_TEXTSIZE); + key[b].drawButton(); + } + } +} + +//------------------------------------------------------------------------------------------ + +void touch_calibrate() +{ + uint16_t calData[5]; + uint8_t calDataOK = 0; + + // check file system exists + if (!SPIFFS.begin()) { + Serial.println("Formating file system"); + SPIFFS.format(); + SPIFFS.begin(); + } + + // check if calibration file exists and size is correct + if (SPIFFS.exists(CALIBRATION_FILE)) { + if (REPEAT_CAL) + { + // Delete if we want to re-calibrate + SPIFFS.remove(CALIBRATION_FILE); + } + else + { + File f = SPIFFS.open(CALIBRATION_FILE, "r"); + if (f) { + if (f.readBytes((char *)calData, 14) == 14) + calDataOK = 1; + f.close(); + } + } + } + + if (calDataOK && !REPEAT_CAL) { + // calibration data valid + tft.setTouch(calData); + } else { + // data not valid so recalibrate + tft.fillScreen(TFT_BLACK); + tft.setCursor(20, 0); + tft.setTextFont(2); + tft.setTextSize(1); + tft.setTextColor(TFT_WHITE, TFT_BLACK); + + tft.println("Touch corners as indicated"); + + tft.setTextFont(1); + tft.println(); + + if (REPEAT_CAL) { + tft.setTextColor(TFT_RED, TFT_BLACK); + tft.println("Set REPEAT_CAL to false to stop this running again!"); + } + + tft.calibrateTouch(calData, TFT_MAGENTA, TFT_BLACK, 15); + + tft.setTextColor(TFT_GREEN, TFT_BLACK); + tft.println("Calibration complete!"); + + // store data + File f = SPIFFS.open(CALIBRATION_FILE, "w"); + if (f) { + f.write((const unsigned char *)calData, 14); + f.close(); + } + } +} + +//------------------------------------------------------------------------------------------ + +// Print something in the mini status bar +void status(const char *msg) { + tft.setTextPadding(240); + //tft.setCursor(STATUS_X, STATUS_Y); + tft.setTextColor(TFT_WHITE, TFT_DARKGREY); + tft.setTextFont(0); + tft.setTextDatum(TC_DATUM); + tft.setTextSize(1); + tft.drawString(msg, STATUS_X, STATUS_Y); +} + +//------------------------------------------------------------------------------------------ + diff --git a/examples/480 x 320/Keypad_480x320/Keypad_480x320.ino b/examples/480 x 320/Keypad_480x320/Keypad_480x320.ino new file mode 100644 index 0000000..427cb6a --- /dev/null +++ b/examples/480 x 320/Keypad_480x320/Keypad_480x320.ino @@ -0,0 +1,287 @@ +/* + The TFT_eSPI library incorporates an Adafruit_GFX compatible + button handling class, this sketch is based on the Arduin-o-phone + example. + + This example diplays a keypad where numbers can be entered and + send to the Serial Monitor window. + + The sketch has been tested on the ESP8266 (which supports SPIFFS) + + The minimum screen size is 320 x 240 as that is the keypad size. + + TOUCH_CS and SPI_TOUCH_FREQUENCY must be defined in the User_Setup.h file + for the touch functions to do anything. +*/ + +// The SPIFFS (FLASH filing system) is used to hold touch screen +// calibration data + +#include "FS.h" + +#include +#include // Hardware-specific library + +TFT_eSPI tft = TFT_eSPI(); // Invoke custom library + +// This is the file name used to store the calibration data +// You can change this to create new calibration files. +// The SPIFFS file name must start with "/". +#define CALIBRATION_FILE "/TouchCalData2" + +// Set REPEAT_CAL to true instead of false to run calibration +// again, otherwise it will only be done once. +// Repeat calibration if you change the screen rotation. +#define REPEAT_CAL false + +// Keypad start position, key sizes and spacing +#define KEY_X 40 // Centre of key +#define KEY_Y 96 +#define KEY_W 62 // Width and height +#define KEY_H 30 +#define KEY_SPACING_X 18 // X and Y gap +#define KEY_SPACING_Y 20 +#define KEY_TEXTSIZE 1 // Font size multiplier + +// Using two fonts since numbers are nice when bold +#define LABEL1_FONT &FreeSansOblique12pt7b // Key label font 1 +#define LABEL2_FONT &FreeSansBold12pt7b // Key label font 2 + +// Numeric display box size and location +#define DISP_X 1 +#define DISP_Y 10 +#define DISP_W 238 +#define DISP_H 50 +#define DISP_TSIZE 3 +#define DISP_TCOLOR TFT_CYAN + +// Number length, buffer for storing it and character index +#define NUM_LEN 12 +char numberBuffer[NUM_LEN + 1] = ""; +uint8_t numberIndex = 0; + +// We have a status line for messages +#define STATUS_X 120 // Centred on this +#define STATUS_Y 65 + +// Create 15 keys for the keypad +char keyLabel[15][5] = {"New", "Del", "Send", "1", "2", "3", "4", "5", "6", "7", "8", "9", ".", "0", "#" }; +uint16_t keyColor[15] = {TFT_RED, TFT_DARKGREY, TFT_DARKGREEN, + TFT_BLUE, TFT_BLUE, TFT_BLUE, + TFT_BLUE, TFT_BLUE, TFT_BLUE, + TFT_BLUE, TFT_BLUE, TFT_BLUE, + TFT_BLUE, TFT_BLUE, TFT_BLUE + }; + +// Invoke the TFT_eSPI button class and create all the button objects +TFT_eSPI_Button key[15]; + +//------------------------------------------------------------------------------------------ + +void setup() { + // Use serial port + Serial.begin(9600); + + // Initialise the TFT screen + tft.init(); + + // Set the rotation before we calibrate + tft.setRotation(1); + + // Calibrate the touch screen and retrieve the scaling factors + touch_calibrate(); + + // Clear the screen + tft.fillScreen(TFT_BLACK); + + // Draw keypad background + tft.fillRect(0, 0, 240, 320, TFT_DARKGREY); + + // Draw number display area and frame + tft.fillRect(DISP_X, DISP_Y, DISP_W, DISP_H, TFT_BLACK); + tft.drawRect(DISP_X, DISP_Y, DISP_W, DISP_H, TFT_WHITE); + + // Draw keypad + drawKeypad(); +} + +//------------------------------------------------------------------------------------------ + +void loop(void) { + uint16_t t_x = 0, t_y = 0; // To store the touch coordinates + + // Pressed will be set true is there is a valid touch on the screen + boolean pressed = tft.getTouch(&t_x, &t_y); + + // / Check if any key coordinate boxes contain the touch coordinates + for (uint8_t b = 0; b < 15; b++) { + if (pressed && key[b].contains(t_x, t_y)) { + key[b].press(true); // tell the button it is pressed + } else { + key[b].press(false); // tell the button it is NOT pressed + } + } + + // Check if any key has changed state + for (uint8_t b = 0; b < 15; b++) { + + if (b < 3) tft.setFreeFont(LABEL1_FONT); + else tft.setFreeFont(LABEL2_FONT); + + if (key[b].justReleased()) key[b].drawButton(); // draw normal + + if (key[b].justPressed()) { + key[b].drawButton(true); // draw invert + + // if a numberpad button, append the relevant # to the numberBuffer + if (b >= 3) { + if (numberIndex < NUM_LEN) { + numberBuffer[numberIndex] = keyLabel[b][0]; + numberIndex++; + numberBuffer[numberIndex] = 0; // zero terminate + } + status(""); // Clear the old status + } + + // Del button, so delete last char + if (b == 1) { + numberBuffer[numberIndex] = 0; + if (numberIndex > 0) { + numberIndex--; + numberBuffer[numberIndex] = 0;//' '; + } + status(""); // Clear the old status + } + + if (b == 2) { + status("Sent value to serial port"); + Serial.println(numberBuffer); + } + // we dont really check that the text field makes sense + // just try to call + if (b == 0) { + status("Value cleared"); + numberIndex = 0; // Reset index to 0 + numberBuffer[numberIndex] = 0; // Place null in buffer + } + + // Update the number display field + tft.setTextDatum(TL_DATUM); // Use top left corner as text coord datum + tft.setFreeFont(&FreeSans18pt7b); // Choose a nicefont that fits box + tft.setTextColor(DISP_TCOLOR); // Set the font colour + + // Draw the string, the value returned is the width in pixels + int xwidth = tft.drawString(numberBuffer, DISP_X + 4, DISP_Y + 12); + + // Now cover up the rest of the line up by drawing a black rectangle. No flicker this way + // but it will not work with italic or oblique fonts due to character overlap. + tft.fillRect(DISP_X + 4 + xwidth, DISP_Y + 1, DISP_W - xwidth - 5, DISP_H - 2, TFT_BLACK); + + delay(10); // UI debouncing + } + } +} + +//------------------------------------------------------------------------------------------ + +void drawKeypad() +{ + // Draw the keys + for (uint8_t row = 0; row < 5; row++) { + for (uint8_t col = 0; col < 3; col++) { + uint8_t b = col + row * 3; + + if (b < 3) tft.setFreeFont(LABEL1_FONT); + else tft.setFreeFont(LABEL2_FONT); + + key[b].initButton(&tft, KEY_X + col * (KEY_W + KEY_SPACING_X), + KEY_Y + row * (KEY_H + KEY_SPACING_Y), // x, y, w, h, outline, fill, text + KEY_W, KEY_H, TFT_WHITE, keyColor[b], TFT_WHITE, + keyLabel[b], KEY_TEXTSIZE); + key[b].drawButton(); + } + } +} + +//------------------------------------------------------------------------------------------ + +void touch_calibrate() +{ + uint16_t calData[5]; + uint8_t calDataOK = 0; + + // check file system exists + if (!SPIFFS.begin()) { + Serial.println("Formating file system"); + SPIFFS.format(); + SPIFFS.begin(); + } + + // check if calibration file exists and size is correct + if (SPIFFS.exists(CALIBRATION_FILE)) { + if (REPEAT_CAL) + { + // Delete if we want to re-calibrate + SPIFFS.remove(CALIBRATION_FILE); + } + else + { + File f = SPIFFS.open(CALIBRATION_FILE, "r"); + if (f) { + if (f.readBytes((char *)calData, 14) == 14) + calDataOK = 1; + f.close(); + } + } + } + + if (calDataOK && !REPEAT_CAL) { + // calibration data valid + tft.setTouch(calData); + } else { + // data not valid so recalibrate + tft.fillScreen(TFT_BLACK); + tft.setCursor(20, 0); + tft.setTextFont(2); + tft.setTextSize(1); + tft.setTextColor(TFT_WHITE, TFT_BLACK); + + tft.println("Touch corners as indicated"); + + tft.setTextFont(1); + tft.println(); + + if (REPEAT_CAL) { + tft.setTextColor(TFT_RED, TFT_BLACK); + tft.println("Set REPEAT_CAL to false to stop this running again!"); + } + + tft.calibrateTouch(calData, TFT_MAGENTA, TFT_BLACK, 15); + + tft.setTextColor(TFT_GREEN, TFT_BLACK); + tft.println("Calibration complete!"); + + // store data + File f = SPIFFS.open(CALIBRATION_FILE, "w"); + if (f) { + f.write((const unsigned char *)calData, 14); + f.close(); + } + } +} + +//------------------------------------------------------------------------------------------ + +// Print something in the mini status bar +void status(const char *msg) { + tft.setTextPadding(240); + //tft.setCursor(STATUS_X, STATUS_Y); + tft.setTextColor(TFT_WHITE, TFT_DARKGREY); + tft.setTextFont(0); + tft.setTextDatum(TC_DATUM); + tft.setTextSize(1); + tft.drawString(msg, STATUS_X, STATUS_Y); +} + +//------------------------------------------------------------------------------------------ + diff --git a/examples/Generic/On_Off_Button/On_Off_Button.ino b/examples/Generic/On_Off_Button/On_Off_Button.ino new file mode 100644 index 0000000..035cf6e --- /dev/null +++ b/examples/Generic/On_Off_Button/On_Off_Button.ino @@ -0,0 +1,206 @@ +// Example of drawing a graphical "switch" and using +// the touch screen to change it's state. + +// This sketch does not use the libraries button drawing +// and handling functions. + +// Based on Adafruit_GFX library onoffbutton example. + +// Touch handling for XPT2046 based screens is handled by +// the TFT_eSPI library. + +// Calibration data is stored in SPIFFS so we need to include it +#include "FS.h" + +#include + +#include // Hardware-specific library + +TFT_eSPI tft = TFT_eSPI(); // Invoke custom library + +// This is the file name used to store the touch coordinate +// calibration data. Cahnge the name to start a new calibration. +#define CALIBRATION_FILE "/TouchCalData3" + +// Set REPEAT_CAL to true instead of false to run calibration +// again, otherwise it will only be done once. +// Repeat calibration if you change the screen rotation. +#define REPEAT_CAL false + +boolean SwitchOn = false; + +// Comment out to stop drawing black spots +#define BLACK_SPOT + +// Switch position and size +#define FRAME_X 100 +#define FRAME_Y 64 +#define FRAME_W 120 +#define FRAME_H 50 + +// Red zone size +#define REDBUTTON_X FRAME_X +#define REDBUTTON_Y FRAME_Y +#define REDBUTTON_W (FRAME_W/2) +#define REDBUTTON_H FRAME_H + +// Green zone size +#define GREENBUTTON_X (REDBUTTON_X + REDBUTTON_W) +#define GREENBUTTON_Y FRAME_Y +#define GREENBUTTON_W (FRAME_W/2) +#define GREENBUTTON_H FRAME_H + +//------------------------------------------------------------------------------------------ +//------------------------------------------------------------------------------------------ +void setup(void) +{ + Serial.begin(9600); + tft.init(); + + // Set the rotation before we calibrate + tft.setRotation(1); + + // call screen calibration + touch_calibrate(); + + // clear screen + tft.fillScreen(TFT_BLUE); + + // Draw button (this example does not use library Button class) + redBtn(); +} +//------------------------------------------------------------------------------------------ +//------------------------------------------------------------------------------------------ +void loop() +{ + uint16_t x, y; + + // See if there's any touch data for us + if (tft.getTouch(&x, &y)) + { + // Draw a block spot to show where touch was calculated to be + #ifdef BLACK_SPOT + tft.fillCircle(x, y, 2, TFT_BLACK); + #endif + + if (SwitchOn) + { + if ((x > REDBUTTON_X) && (x < (REDBUTTON_X + REDBUTTON_W))) { + if ((y > REDBUTTON_Y) && (y <= (REDBUTTON_Y + REDBUTTON_H))) { + Serial.println("Red btn hit"); + redBtn(); + } + } + } + else //Record is off (SwitchOn == false) + { + if ((x > GREENBUTTON_X) && (x < (GREENBUTTON_X + GREENBUTTON_W))) { + if ((y > GREENBUTTON_Y) && (y <= (GREENBUTTON_Y + GREENBUTTON_H))) { + Serial.println("Green btn hit"); + greenBtn(); + } + } + } + + Serial.println(SwitchOn); + + } +} +//------------------------------------------------------------------------------------------ + +void touch_calibrate() +{ + uint16_t calData[5]; + uint8_t calDataOK = 0; + + // check file system exists + if (!SPIFFS.begin()) { + Serial.println("Formating file system"); + SPIFFS.format(); + SPIFFS.begin(); + } + + // check if calibration file exists and size is correct + if (SPIFFS.exists(CALIBRATION_FILE)) { + if (REPEAT_CAL) + { + // Delete if we want to re-calibrate + SPIFFS.remove(CALIBRATION_FILE); + } + else + { + File f = SPIFFS.open(CALIBRATION_FILE, "r"); + if (f) { + if (f.readBytes((char *)calData, 14) == 14) + calDataOK = 1; + f.close(); + } + } + } + + if (calDataOK && !REPEAT_CAL) { + // calibration data valid + tft.setTouch(calData); + } else { + // data not valid so recalibrate + tft.fillScreen(TFT_BLACK); + tft.setCursor(20, 0); + tft.setTextFont(2); + tft.setTextSize(1); + tft.setTextColor(TFT_WHITE, TFT_BLACK); + + tft.println("Touch corners as indicated"); + + tft.setTextFont(1); + tft.println(); + + if (REPEAT_CAL) { + tft.setTextColor(TFT_RED, TFT_BLACK); + tft.println("Set REPEAT_CAL to false to stop this running again!"); + } + + tft.calibrateTouch(calData, TFT_MAGENTA, TFT_BLACK, 15); + + tft.setTextColor(TFT_GREEN, TFT_BLACK); + tft.println("Calibration complete!"); + + // store data + File f = SPIFFS.open(CALIBRATION_FILE, "w"); + if (f) { + f.write((const unsigned char *)calData, 14); + f.close(); + } + } +} + +void drawFrame() +{ + tft.drawRect(FRAME_X, FRAME_Y, FRAME_W, FRAME_H, TFT_BLACK); +} + +// Draw a red button +void redBtn() +{ + tft.fillRect(REDBUTTON_X, REDBUTTON_Y, REDBUTTON_W, REDBUTTON_H, TFT_RED); + tft.fillRect(GREENBUTTON_X, GREENBUTTON_Y, GREENBUTTON_W, GREENBUTTON_H, TFT_DARKGREY); + drawFrame(); + tft.setTextColor(TFT_WHITE); + tft.setTextSize(2); + tft.setTextDatum(MC_DATUM); + tft.drawString("ON", GREENBUTTON_X + (GREENBUTTON_W / 2), GREENBUTTON_Y + (GREENBUTTON_H / 2)); + SwitchOn = false; +} + +// Draw a green button +void greenBtn() +{ + tft.fillRect(GREENBUTTON_X, GREENBUTTON_Y, GREENBUTTON_W, GREENBUTTON_H, TFT_GREEN); + tft.fillRect(REDBUTTON_X, REDBUTTON_Y, REDBUTTON_W, REDBUTTON_H, TFT_DARKGREY); + drawFrame(); + tft.setTextColor(TFT_WHITE); + tft.setTextSize(2); + tft.setTextDatum(MC_DATUM); + tft.drawString("OFF", REDBUTTON_X + (REDBUTTON_W / 2) + 1, REDBUTTON_Y + (REDBUTTON_H / 2)); + SwitchOn = true; +} + From 19672f97e407e5f880d2e4f0873b8f0a239fe107 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Mon, 23 Oct 2017 02:09:10 +0100 Subject: [PATCH 008/150] Update version and ReadMe --- README.md | 4 +++- library.json | 2 +- library.properties | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 4382b4b..76a1129 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,9 @@ The library also supports TFT displays designed for the Raspberry Pi that are ba The XPT2046 touch screen controller is supported. The SPI bus for the touch controller is shared with the TFT and only an additional chip select line is needed. -The library supports SPI overlap so the TFT screen can share MOSI, MISO and SCLK pins with the program FLASH. +The Button class from Adafruit_GFX has been added, with the enhancement that the button labels can be in any font. + +The library supports SPI overlap on the ESP8266 so the TFT screen can share MOSI, MISO and SCLK pins with the program FLASH. The library contains proportional fonts, different sizes can be enabled/disabled at compile time to optimise the use of FLASH memory. The library has been tested with the NodeMCU (ESP8266 based) and an ESP32 demo board. diff --git a/library.json b/library.json index 4cfc6a5..8f2ba01 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "TFT_eSPI", - "version": "0.16.15", + "version": "0.16.16", "keywords": "TFT, ESP8266, NodeMCU, ESP32, ILI9341, ST7735, ILI9163, S6D02A1, ILI9486", "description": "A TFT SPI graphics library for ESP8266", "repository": diff --git a/library.properties b/library.properties index b35285f..e0ba602 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=TFT_eSPI -version=0.16.15 +version=0.16.16 author=Bodmer maintainer=Bodmer sentence=A fast TFT library for ESP8266 processors and the Arduino IDE From 94520c4071145d4d47ededc64a9408516e720669 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Mon, 23 Oct 2017 15:11:57 +0100 Subject: [PATCH 009/150] XPT2046 SPI frequency can be always defined One less thing to edit or forget! --- User_Setup.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/User_Setup.h b/User_Setup.h index 8fe0a82..b67d6a4 100644 --- a/User_Setup.h +++ b/User_Setup.h @@ -167,7 +167,8 @@ // #define SPI_FREQUENCY 40000000 // Maximum to use SPIFFS // #define SPI_FREQUENCY 80000000 -// #define SPI_TOUCH_FREQUENCY 2500000 +// The XPT2046 required a lower SPI clock rate of 2.5MHz so we define that here: + #define SPI_TOUCH_FREQUENCY 2500000 // Comment out the following #define if "SPI Transactions" do not need to be From df4e658fe43ba1852f65e77eb0ea431f88fa29b6 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Wed, 25 Oct 2017 01:41:01 +0100 Subject: [PATCH 010/150] Improved touch detection and position reliability --- TFT_eSPI.cpp | 51 ++++++++++++++++++++++++++++++--------------------- 1 file changed, 30 insertions(+), 21 deletions(-) diff --git a/TFT_eSPI.cpp b/TFT_eSPI.cpp index 5fea5b9..23f87ea 100644 --- a/TFT_eSPI.cpp +++ b/TFT_eSPI.cpp @@ -3735,24 +3735,29 @@ uint16_t TFT_eSPI::getTouchRawZ(void){ uint8_t TFT_eSPI::validTouch(uint16_t *x, uint16_t *y, uint16_t threshold){ uint16_t x_tmp, y_tmp, x_tmp2, y_tmp2; + // Wait until pressure stops increasing + uint16_t z1 = 1; + uint16_t z2 = 0; + while (z1 > z2) + { + z2 = z1; + z1 = getTouchRawZ(); + delay(1); + } - // uint16_t z_tmp = getTouchRawZ(); // Save value for debug message - // Debug messages used for tuning - // Serial.print("Z = ");Serial.println(z_tmp); - - if (getTouchRawZ() <= threshold) return false; - - delay(2); // Small debounce delay to the next sample + // Serial.print("Z = ");Serial.println(z1); + + if (z1 <= threshold) return false; + getTouchRaw(&x_tmp,&y_tmp); - // z_tmp = getTouchRawZ(); // Serial.print("Sample 1 x,y = "); Serial.print(x_tmp);Serial.print(",");Serial.print(y_tmp); - // Serial.print(", Z = ");Serial.println(z_tmp); + // Serial.print(", Z = ");Serial.println(z1); - delay(2); // Small debounce delay to the next sample + delay(1); // Small delay to the next sample if (getTouchRawZ() <= threshold) return false; - delay(2); // Small debounce delay to the next sample + delay(2); // Small delay to the next sample getTouchRaw(&x_tmp2,&y_tmp2); // Serial.print("Sample 2 x,y = "); Serial.print(x_tmp2);Serial.print(",");Serial.println(y_tmp2); @@ -3771,28 +3776,32 @@ uint8_t TFT_eSPI::validTouch(uint16_t *x, uint16_t *y, uint16_t threshold){ ** Function name: getTouch ** Description: read callibrated position. Return false if not pressed. ***************************************************************************************/ -#define Z_THRESHOLD 0x200 // Touch pressure threshold for validating touches +#define Z_THRESHOLD 350 // Touch pressure threshold for validating touches uint8_t TFT_eSPI::getTouch(uint16_t *x, uint16_t *y){ - uint16_t x_tmp, y_tmp; + uint16_t x_tmp, y_tmp, xx, yy; if(!validTouch(&x_tmp, &y_tmp, Z_THRESHOLD)) return false; if(!touchCalibration_rotate){ - *x=(x_tmp-touchCalibration_x0)*_width/touchCalibration_x1; - *y=(y_tmp-touchCalibration_y0)*_height/touchCalibration_y1; + xx=(x_tmp-touchCalibration_x0)*_width/touchCalibration_x1; + yy=(y_tmp-touchCalibration_y0)*_height/touchCalibration_y1; if(touchCalibration_invert_x) - *x = _width - *x; + xx = _width - xx; if(touchCalibration_invert_y) - *y = _height - *y; + yy = _height - yy; } else { - *y=(x_tmp-touchCalibration_x0)*_height/touchCalibration_x1; - *x=(y_tmp-touchCalibration_y0)*_width/touchCalibration_y1; + yy=(x_tmp-touchCalibration_x0)*_height/touchCalibration_x1; + xx=(y_tmp-touchCalibration_y0)*_width/touchCalibration_y1; if(touchCalibration_invert_x) - *x = _width - *x; + xx = _width - xx; if(touchCalibration_invert_y) - *y = _height - *y; + yy = _height - yy; } + if (xx >= _width || yy >= _height) return false; + + *x = xx; + *y = yy; return true; } From acd3fe7b85e1c8ed63b38fe441ce2bd95d112c71 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Wed, 25 Oct 2017 01:57:42 +0100 Subject: [PATCH 011/150] Add simple touch calibrate and test sketch This one does not need SPIFFS, cal data is embedded in sketch by user. --- .../Touch_calibrate/Touch_calibrate.ino | 103 ++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 examples/Generic/Touch_calibrate/Touch_calibrate.ino diff --git a/examples/Generic/Touch_calibrate/Touch_calibrate.ino b/examples/Generic/Touch_calibrate/Touch_calibrate.ino new file mode 100644 index 0000000..26475ef --- /dev/null +++ b/examples/Generic/Touch_calibrate/Touch_calibrate.ino @@ -0,0 +1,103 @@ +/* + Sketch to generate the setup() calibration values, these are reported + to the Serial Monitor. + + The sketch has been tested on the ESP8266 and screen with XPY2046 driver. +*/ + +#include +#include // Hardware-specific library + +TFT_eSPI tft = TFT_eSPI(); // Invoke custom library + +//------------------------------------------------------------------------------------------ + +void setup() { + // Use serial port + Serial.begin(115200); + + // Initialise the TFT screen + tft.init(); + + // Set the rotation before we calibrate + tft.setRotation(1); + + // Calibrate the touch screen and retrieve the scaling factors + touch_calibrate(); + +/* + // Replace above line with the code sent to Serial Monitor + // once calibration is complete, e.g.: + uint16_t calData[5] = { 286, 3534, 283, 3600, 6 }; + tft.setTouch(calData); +*/ + + // Clear the screen + tft.fillScreen(TFT_BLACK); + tft.drawCentreString("Touch screen to test!",tft.width()/2, tft.height()/2, 2); +} + +//------------------------------------------------------------------------------------------ + +void loop(void) { + uint16_t x = 0, y = 0; // To store the touch coordinates + + // Pressed will be set true is there is a valid touch on the screen + boolean pressed = tft.getTouch(&x, &y); + + // Draw a shite spot at the detected coordinates + if (pressed) { + tft.fillCircle(x, y, 2, TFT_WHITE); + //Serial.print("x,y = "); + //Serial.print(x); + //Serial.print(","); + //Serial.println(y); + } +} + +//------------------------------------------------------------------------------------------ + +// Code to run a screen calibration, not needed when calibration values set in setup() +void touch_calibrate() +{ + uint16_t calData[5]; + uint8_t calDataOK = 0; + + // Calibrate + tft.fillScreen(TFT_BLACK); + tft.setCursor(20, 0); + tft.setTextFont(2); + tft.setTextSize(1); + tft.setTextColor(TFT_WHITE, TFT_BLACK); + + tft.println("Touch corners as indicated"); + + tft.setTextFont(1); + tft.println(); + + tft.calibrateTouch(calData, TFT_MAGENTA, TFT_BLACK, 15); + + Serial.println(); Serial.println(); + Serial.println("// Use this calibration code in setup():"); + Serial.print(" uint16_t calData[5] = "); + Serial.print("{ "); + + for (uint8_t i = 0; i < 5; i++) + { + Serial.print(calData[i]); + if (i < 4) Serial.print(", "); + } + + Serial.println(" };"); + Serial.print(" tft.setTouch(calData);"); + Serial.println(); Serial.println(); + + tft.fillScreen(TFT_BLACK); + + tft.setTextColor(TFT_GREEN, TFT_BLACK); + tft.println("Calibration complete!"); + tft.println("Calibration code sent to Serial port."); + + delay(4000); +} + From 5d497f751b9b5e214fe5998e6d7eb2eabe7a389c Mon Sep 17 00:00:00 2001 From: Bodmer Date: Wed, 25 Oct 2017 02:05:59 +0100 Subject: [PATCH 012/150] Correct typo --- examples/Generic/Touch_calibrate/Touch_calibrate.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/Generic/Touch_calibrate/Touch_calibrate.ino b/examples/Generic/Touch_calibrate/Touch_calibrate.ino index 26475ef..ff13392 100644 --- a/examples/Generic/Touch_calibrate/Touch_calibrate.ino +++ b/examples/Generic/Touch_calibrate/Touch_calibrate.ino @@ -2,7 +2,7 @@ Sketch to generate the setup() calibration values, these are reported to the Serial Monitor. - The sketch has been tested on the ESP8266 and screen with XPY2046 driver. + The sketch has been tested on the ESP8266 and screen with XPT2046 driver. */ #include From fbdc65a3c78e29afa5e26c6d473013d68b72742c Mon Sep 17 00:00:00 2001 From: Bodmer Date: Wed, 25 Oct 2017 02:08:52 +0100 Subject: [PATCH 013/150] Whoops, another typo - who put S and W keys next to each other ;-) --- examples/Generic/Touch_calibrate/Touch_calibrate.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/Generic/Touch_calibrate/Touch_calibrate.ino b/examples/Generic/Touch_calibrate/Touch_calibrate.ino index ff13392..4f10110 100644 --- a/examples/Generic/Touch_calibrate/Touch_calibrate.ino +++ b/examples/Generic/Touch_calibrate/Touch_calibrate.ino @@ -45,7 +45,7 @@ void loop(void) { // Pressed will be set true is there is a valid touch on the screen boolean pressed = tft.getTouch(&x, &y); - // Draw a shite spot at the detected coordinates + // Draw a white spot at the detected coordinates if (pressed) { tft.fillCircle(x, y, 2, TFT_WHITE); //Serial.print("x,y = "); From 0562594ae30fbdcf7b487b8c35534df6a2970a8a Mon Sep 17 00:00:00 2001 From: Bodmer Date: Thu, 2 Nov 2017 01:49:35 +0000 Subject: [PATCH 014/150] Fix issue #49 --- TFT_eSPI.h | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/TFT_eSPI.h b/TFT_eSPI.h index 5d174fd..40afc2d 100644 --- a/TFT_eSPI.h +++ b/TFT_eSPI.h @@ -253,6 +253,11 @@ swap_coord(T& a, T& b) { T t = a; a = b; b = t; } // This is a structure to conveniently hold infomation on the default fonts // Stores pointer to font character image address table, width table and height +// Create a null set in case some fonts not used (to prevent crash) +const uint8_t widtbl_null[1] = {0}; +PROGMEM const uint8_t chr_null[1] = {0}; +PROGMEM const uint8_t* const chrtbl_null[1] = {chr_null}; + typedef struct { const uint8_t *chartbl; const uint8_t *widthtbl; @@ -262,45 +267,45 @@ typedef struct { // Now fill the structure const PROGMEM fontinfo fontdata [] = { - { 0, 0, 0, 0 }, + { (const uint8_t *)chrtbl_null, widtbl_null, 0, 0 }, // GLCD font (Font 1) does not have all parameters - { 0, 0, 8, 7 }, + { (const uint8_t *)chrtbl_null, widtbl_null, 8, 7 }, #ifdef LOAD_FONT2 { (const uint8_t *)chrtbl_f16, widtbl_f16, chr_hgt_f16, baseline_f16}, #else - { 0, 0, 0, 0 }, + { (const uint8_t *)chrtbl_null, widtbl_null, 0, 0 }, #endif // Font 3 current unused - { 0, 0, 0, 0 }, + { (const uint8_t *)chrtbl_null, widtbl_null, 0, 0 }, #ifdef LOAD_FONT4 { (const uint8_t *)chrtbl_f32, widtbl_f32, chr_hgt_f32, baseline_f32}, #else - { 0, 0, 0, 0 }, + { (const uint8_t *)chrtbl_null, widtbl_null, 0, 0 }, #endif // Font 5 current unused - { 0, 0, 0, 0 }, + { (const uint8_t *)chrtbl_null, widtbl_null, 0, 0 }, #ifdef LOAD_FONT6 { (const uint8_t *)chrtbl_f64, widtbl_f64, chr_hgt_f64, baseline_f64}, #else - { 0, 0, 0, 0 }, + { (const uint8_t *)chrtbl_null, widtbl_null, 0, 0 }, #endif #ifdef LOAD_FONT7 { (const uint8_t *)chrtbl_f7s, widtbl_f7s, chr_hgt_f7s, baseline_f7s}, #else - { 0, 0, 0, 0 }, + { (const uint8_t *)chrtbl_null, widtbl_null, 0, 0 }, #endif #ifdef LOAD_FONT8 { (const uint8_t *)chrtbl_f72, widtbl_f72, chr_hgt_f72, baseline_f72} #else - { 0, 0, 0, 0 } + { (const uint8_t *)chrtbl_null, widtbl_null, 0, 0 } #endif }; From 8734a05d3ed54e6af18b57b71074d212bdd104b4 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Sat, 4 Nov 2017 19:11:50 +0000 Subject: [PATCH 015/150] Update touch handler to work with ESP32 Adde Setup11... for testing with ESP32 --- TFT_eSPI.cpp | 55 ++++---- TFT_eSPI.h | 2 + User_Setups/Setup10_RPi_touch_ILI9486.h | 4 +- User_Setups/Setup11_RPi_touch_ILI9486.h | 169 ++++++++++++++++++++++++ 4 files changed, 201 insertions(+), 29 deletions(-) create mode 100644 User_Setups/Setup11_RPi_touch_ILI9486.h diff --git a/TFT_eSPI.cpp b/TFT_eSPI.cpp index 23f87ea..ee1ac08 100644 --- a/TFT_eSPI.cpp +++ b/TFT_eSPI.cpp @@ -44,21 +44,32 @@ void spiWriteBlock(uint16_t color, uint32_t repeat); // libraries. Otherwise, they simply do nothing. inline void TFT_eSPI::spi_begin(void){ -#ifdef SPI_HAS_TRANSACTION - #ifdef SUPPORT_TRANSACTIONS - if (locked) {locked = false; SPI.beginTransaction(SPISettings(SPI_FREQUENCY, MSBFIRST, SPI_MODE0));} - #endif +#if defined (SPI_HAS_TRANSACTION) && defined (SUPPORT_TRANSACTIONS) + if (locked) {locked = false; SPI.beginTransaction(SPISettings(SPI_FREQUENCY, MSBFIRST, SPI_MODE0));} #endif } inline void TFT_eSPI::spi_end(void){ -#ifdef SPI_HAS_TRANSACTION - #ifdef SUPPORT_TRANSACTIONS +#if defined (SPI_HAS_TRANSACTION) && defined (SUPPORT_TRANSACTIONS) if(!inTransaction) {if (!locked) {locked = true; SPI.endTransaction();}} - #endif #endif } +inline void TFT_eSPI::spi_begin_touch(void){ +#if defined (SPI_HAS_TRANSACTION) && defined (SUPPORT_TRANSACTIONS) + if (locked) {locked = false; SPI.beginTransaction(SPISettings(SPI_TOUCH_FREQUENCY, MSBFIRST, SPI_MODE0));} +#else + SPI.setFrequency(SPI_TOUCH_FREQUENCY); +#endif +} + +inline void TFT_eSPI::spi_end_touch(void){ +#if defined (SPI_HAS_TRANSACTION) && defined (SUPPORT_TRANSACTIONS) + if(!inTransaction) {if (!locked) {locked = true; SPI.endTransaction();}} +#else + SPI.setFrequency(SPI_FREQUENCY); +#endif +} /*************************************************************************************** ** Function name: TFT_eSPI @@ -3665,16 +3676,11 @@ void spiWriteBlock(uint16_t color, uint32_t repeat) uint8_t TFT_eSPI::getTouchRaw(uint16_t *x, uint16_t *y){ uint16_t tmp; CS_H; + + spi_begin_touch(); + T_CS_L; -#ifdef SPI_HAS_TRANSACTION - #ifdef SUPPORT_TRANSACTIONS - if (locked) {locked = false; SPI.beginTransaction(SPISettings(SPI_TOUCH_FREQUENCY, MSBFIRST, SPI_MODE0));} - #endif -#endif - - SPI.setFrequency(SPI_TOUCH_FREQUENCY); - // Start bit + YP sample request for x position tmp = SPI.transfer(0xd0); tmp = SPI.transfer(0); @@ -3692,8 +3698,8 @@ uint8_t TFT_eSPI::getTouchRaw(uint16_t *x, uint16_t *y){ *y = tmp; T_CS_H; - SPI.setFrequency(SPI_FREQUENCY); - spi_end(); + + spi_end_touch(); return true; } @@ -3704,15 +3710,10 @@ uint8_t TFT_eSPI::getTouchRaw(uint16_t *x, uint16_t *y){ ***************************************************************************************/ uint16_t TFT_eSPI::getTouchRawZ(void){ CS_H; - T_CS_L; - -#ifdef SPI_HAS_TRANSACTION - #ifdef SUPPORT_TRANSACTIONS - if (locked) {locked = false; SPI.beginTransaction(SPISettings(SPI_TOUCH_FREQUENCY, MSBFIRST, SPI_MODE0));} - #endif -#endif - SPI.setFrequency(SPI_TOUCH_FREQUENCY); + spi_begin_touch(); + + T_CS_L; // Z sample request uint16_t tz = 0xFFF; @@ -3721,8 +3722,8 @@ uint16_t TFT_eSPI::getTouchRawZ(void){ tz -= SPI.transfer16(0x91) >> 3; T_CS_H; - SPI.setFrequency(SPI_FREQUENCY); - spi_end(); + + spi_end_touch(); return tz; } diff --git a/TFT_eSPI.h b/TFT_eSPI.h index 40afc2d..1e754a4 100644 --- a/TFT_eSPI.h +++ b/TFT_eSPI.h @@ -447,6 +447,8 @@ class TFT_eSPI : public Print { inline void spi_begin() __attribute__((always_inline)); inline void spi_end() __attribute__((always_inline)); + inline void spi_begin_touch() __attribute__((always_inline)); + inline void spi_end_touch() __attribute__((always_inline)); void readAddrWindow(int32_t xs, int32_t ys, int32_t xe, int32_t ye); diff --git a/User_Setups/Setup10_RPi_touch_ILI9486.h b/User_Setups/Setup10_RPi_touch_ILI9486.h index 34e23c1..856256f 100644 --- a/User_Setups/Setup10_RPi_touch_ILI9486.h +++ b/User_Setups/Setup10_RPi_touch_ILI9486.h @@ -146,8 +146,8 @@ // #define SPI_FREQUENCY 1000000 // #define SPI_FREQUENCY 5000000 - #define SPI_FREQUENCY 10000000 -// #define SPI_FREQUENCY 20000000 +// #define SPI_FREQUENCY 10000000 + #define SPI_FREQUENCY 20000000 // #define SPI_FREQUENCY 27000000 // Actually sets it to 26.67MHz = 80/3 // #define SPI_FREQUENCY 40000000 // Maximum to use SPIFFS // #define SPI_FREQUENCY 80000000 diff --git a/User_Setups/Setup11_RPi_touch_ILI9486.h b/User_Setups/Setup11_RPi_touch_ILI9486.h new file mode 100644 index 0000000..de3baaa --- /dev/null +++ b/User_Setups/Setup11_RPi_touch_ILI9486.h @@ -0,0 +1,169 @@ +// USER DEFINED SETTINGS +// Set driver type, fonts to be loaded, pins used and SPI control method etc +// +// See the User_Setup_Select.h file if you wish to be able to define multiple +// setups and then easily select which setup file is used by the compiler. +// +// If this file is editted correctly then all the library example sketches should +// run without the need to make any more changes for a particular hardware setup! + +// ################################################################################## +// +// Section 0. Call up the right driver file and any options for it +// +// ################################################################################## + +// Only define one driver, the other ones must be commented out +//#define ILI9341_DRIVER +//#define ST7735_DRIVER +//#define ILI9163_DRIVER +//#define S6D02A1_DRIVER +#define RPI_ILI9486_DRIVER // 20MHz maximum SPI +// to enable XPT2046 touch controller, define TOUCH_CS + +// For ST7735 ONLY, define the type of display, originally this was based on the +// colour of the tab on the screen protector film but this is not always true, so try +// out the different options below if the screen does not display graphics correctly, +// e.g. colours wrong, mirror images, or tray pixels at the edges. +// Comment out ALL BUT ONE of these options for a ST7735 display driver, save this +// this User_Setup file, then rebuild and upload the sketch to the board again: + +//#define ST7735_INITB +//#define ST7735_GREENTAB +//#define ST7735_GREENTAB2 +//#define ST7735_GREENTAB3 +//#define ST7735_REDTAB +//#define ST7735_BLACKTAB + +// For ST7735 ONLY, define the pixel width and height in portrait orientation +//#define TFT_WIDTH 128 +//#define TFT_HEIGHT 160 +//#define TFT_HEIGHT 128 + +// ################################################################################## +// +// Section 1. Define the pins that are used to interface with the display here +// +// ################################################################################## + +// We must use hardware SPI, a minimum of 3 GPIO pins is needed. +// Typical setup for NodeMCU ESP-12 is : +// +// Display SDO/MISO to NodeMCU pin D6 (or leave disconnected if not reading TFT) +// Display LED to NodeMCU pin VIN (or 5V, see below) +// Display SCK to NodeMCU pin D5 +// Display SDI/MOSI to NodeMCU pin D7 +// Display DC (or AO)to NodeMCU pin D3 +// Display RESET to NodeMCU pin D4 (or RST, see below) +// Display CS to NodeMCU pin D8 (or GND, see below) +// Display GND to NodeMCU pin GND (0V) +// Display VCC to NodeMCU 5V or 3.3V +// +// The TFT RESET pin can be connected to the NodeMCU RST pin or 3.3V to free up a control pin +// +// With some displays such as the ILI9341 the TFT CS pin can be connected to GND if no more +// SPI deivces (e.g. an SD Card) are connected, in this case comment out the #define TFT_CS +// line below so it is NOT defined. Other displays such at the ST7735 require the TFT CS pin +// to be toggled during setup, so in these cases the TFT_CS line must be defined and connected. +// +// The NodeMCU D0 pin can be used for RST +// +// See Section 2. below if DC or CS is connected to D0 +// +// Note: only some versions of the NodeMCU provide the USB 5V on the VIN pin +// If 5V is not available at a pin you can use 3.3V but backlight brightness +// will be lower. + +// ###### EDIT THE PIN NUMBERS IN THE LINES FOLLOWING TO SUIT YOUR SETUP ###### + +// For NodeMCU - use pin numbers in the form PIN_Dx where Dx is the NodeMCU pin designation +//#define TFT_CS PIN_D8 // Chip select control pin D8 +//#define TFT_DC PIN_D3 // Data Command control pin +//#define TFT_RST PIN_D4 // Reset pin (could connect to NodeMCU RST, see next line) +//#define TFT_RST -1 // Set TFT_RST to -1 if the display RESET is connected to NodeMCU RST or 3.3V + +//#define TOUCH_CS PIN_D1 // Chip select pin (T_CS) of touch screen, also define SPI frequency in Section 5 below + +//#define TFT_WR PIN_D2 // Write strobe for modified Raspberry Pi TFT only + +// ESP32 Dev board +#define TFT_MISO 19 +#define TFT_MOSI 23 +#define TFT_SCLK 18 +#define TFT_CS 15 // Chip select control pin +#define TFT_DC 2 // Data Command control pin +#define TFT_RST 4 // Reset pin (could connect to Arduino RESET pin) +//#define TFT_RST -1 // Set TFT_RST to -1 if display RESET is connected to ESP32 board RST + +#define TOUCH_CS 22 // Chip select pin (T_CS) of touch screen, also define SPI frequency in Section 5 below + +// ################################################################################## +// +// Section 2. Define the way the DC and/or CS lines are driven +// +// ################################################################################## + +// Normally the library uses direct register access for the DC and CS lines for speed +// If D0 (GPIO16) is used for CS or DC then a different slower method must be used +// Uncomment one line if D0 is used for DC or CS +// DC on D0 = 6% performance penalty at 40MHz SPI running graphics test +// CS on D0 = 2% performance penalty at 40MHz SPI running graphics test + +// #define D0_USED_FOR_DC +// #define D0_USED_FOR_CS + +// ################################################################################## +// +// Section 3. Define the fonts that are to be used here +// +// ################################################################################## + +// Comment out the #defines below with // to stop that font being loaded +// The ESP8366 had plenty of memory so commenting out fonts is not normally necessary +// If all fonts are loaded the extra FLASH space required is about 17Kbytes... +// To save FLASH space only enable the fonts you need! + +#define LOAD_GLCD // Font 1. Original Adafruit 8 pixel font needs ~1820 bytes in FLASH +#define LOAD_FONT2 // Font 2. Small 16 pixel high font, needs ~3534 bytes in FLASH, 96 characters +#define LOAD_FONT4 // Font 4. Medium 26 pixel high font, needs ~5848 bytes in FLASH, 96 characters +#define LOAD_FONT6 // Font 6. Large 48 pixel font, needs ~2666 bytes in FLASH, only characters 1234567890:-.apm +#define LOAD_FONT7 // Font 7. 7 segment 48 pixel font, needs ~2438 bytes in FLASH, only characters 1234567890:. +#define LOAD_FONT8 // Font 8. Large 75 pixel font needs ~3256 bytes in FLASH, only characters 1234567890:-. +#define LOAD_GFXFF // FreeFonts. Include access to the 48 Adafruit_GFX free fonts FF1 to FF48 and custom fonts + +// ################################################################################## +// +// Section 4. Not used +// +// ################################################################################## + + +// ################################################################################## +// +// Section 5. Other options +// +// ################################################################################## + +// Define the SPI clock frequency +// With an ILI9341 display 40MHz works OK, 80MHz sometimes fails +// With a ST7735 display more than 27MHz may not work (spurious pixels and lines) +// With an ILI9163 display TBD MHz works OK, + +// #define SPI_FREQUENCY 1000000 +// #define SPI_FREQUENCY 5000000 +// #define SPI_FREQUENCY 10000000 + #define SPI_FREQUENCY 20000000 +// #define SPI_FREQUENCY 27000000 // Actually sets it to 26.67MHz = 80/3 +// #define SPI_FREQUENCY 40000000 // Maximum to use SPIFFS +// #define SPI_FREQUENCY 80000000 + +#define SPI_TOUCH_FREQUENCY 2500000 + + +// Comment out the following #define if "SPI Transactions" do not need to be +// supported. Tranaction support is required if other SPI devices are connected. +// When commented out the code size will be smaller and sketches will +// run slightly faster, so leave it commented out unless you need it! +// Transaction support is needed to work with SD library but not needed with TFT_SdFat + +// #define SUPPORT_TRANSACTIONS From 2333e1ab5c48532669baea6c430efc069824c478 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Sat, 4 Nov 2017 20:56:26 +0000 Subject: [PATCH 016/150] Initialise touch parameterswith example calibration values This avoids a processor crash it setTouch() not called in setup() --- TFT_eSPI.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/TFT_eSPI.h b/TFT_eSPI.h index 1e754a4..29571f5 100644 --- a/TFT_eSPI.h +++ b/TFT_eSPI.h @@ -463,8 +463,9 @@ class TFT_eSPI : public Print { // These are associated with the Touch Screen handlers uint8_t validTouch(uint16_t *x, uint16_t *y, uint16_t threshold); - uint16_t touchCalibration_x0, touchCalibration_x1, touchCalibration_y0, touchCalibration_y1; - uint8_t touchCalibration_rotate, touchCalibration_invert_x, touchCalibration_invert_y; + // Initialise with example calibration values so processor does not crash if setTouch() not called in setup() + uint16_t touchCalibration_x0 = 300, touchCalibration_x1 = 3600, touchCalibration_y0 = 300, touchCalibration_y1 = 3600; + uint8_t touchCalibration_rotate = 1, touchCalibration_invert_x = 2, touchCalibration_invert_y = 0; protected: From 2e91bcd137fb072f152286dc44058407e6f532fa Mon Sep 17 00:00:00 2001 From: Bodmer Date: Mon, 13 Nov 2017 21:15:28 +0000 Subject: [PATCH 017/150] Issue #53 fix In case of a non- supported BMP, the _tft->setRotation() should not be executed. --- examples/320 x 240/weather-station-v8/GfxUi.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/examples/320 x 240/weather-station-v8/GfxUi.cpp b/examples/320 x 240/weather-station-v8/GfxUi.cpp index 4716da0..989ccd1 100644 --- a/examples/320 x 240/weather-station-v8/GfxUi.cpp +++ b/examples/320 x 240/weather-station-v8/GfxUi.cpp @@ -156,9 +156,12 @@ void GfxUi::drawBmp(String filename, uint8_t x, uint16_t y) { } // End of bitmap access } // End of bitmap file check - bmpFile.close(); - if(!goodBmp) Serial.println(F("BMP format not recognised.")); - _tft->setRotation(rotation); // Put back original rotation + if(!goodBmp) { + Serial.print(F("BMP format not recognised. File:")); + Serial.println(filename); + } + else + _tft->setRotation(rotation); // Put back original rotation } // These read 16- and 32-bit types from the SD card file. From 62c02b67f72a63a96f4c92f779428ccd3da46e45 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Wed, 15 Nov 2017 10:19:45 +0000 Subject: [PATCH 018/150] Replace accidentally deleted line! --- examples/320 x 240/weather-station-v8/GfxUi.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/examples/320 x 240/weather-station-v8/GfxUi.cpp b/examples/320 x 240/weather-station-v8/GfxUi.cpp index 989ccd1..e222adf 100644 --- a/examples/320 x 240/weather-station-v8/GfxUi.cpp +++ b/examples/320 x 240/weather-station-v8/GfxUi.cpp @@ -156,6 +156,8 @@ void GfxUi::drawBmp(String filename, uint8_t x, uint16_t y) { } // End of bitmap access } // End of bitmap file check + bmpFile.close(); + if(!goodBmp) { Serial.print(F("BMP format not recognised. File:")); Serial.println(filename); From 943c18facf40b0f086ec73f02bb45a35587074e5 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Wed, 15 Nov 2017 20:26:22 +0000 Subject: [PATCH 019/150] Add sprite class Sprites (images) can now be created in RAM, written to with text and graphics and rendered to screen quickly, this makes it easier to get flicker frre screen updates. ESP8266 can typically create up to a 160x128 sprite, and ESP32 about 200x200 pixels. 16 bit colour only at the moment, may soon implement 8 bit colour to reduce RAM. --- Keywords.txt | 32 + TFT_eSPI.cpp | 938 +++++++++++++++--- TFT_eSPI.h | 123 ++- User_Setup_Select.h | 1 + .../Sprite_RLE_Font_test.ino | 199 ++++ .../Sprite_TFT_Rainbow/Sprite_TFT_Rainbow.ino | 140 +++ .../Sprite_drawPixel/Sprite_drawPixel.ino | 134 +++ .../Sprite_scroll_text/Sprite_scroll_text.ino | 194 ++++ 8 files changed, 1625 insertions(+), 136 deletions(-) create mode 100644 examples/Sprite/Sprite_RLE_Font_test/Sprite_RLE_Font_test.ino create mode 100644 examples/Sprite/Sprite_TFT_Rainbow/Sprite_TFT_Rainbow.ino create mode 100644 examples/Sprite/Sprite_drawPixel/Sprite_drawPixel.ino create mode 100644 examples/Sprite/Sprite_scroll_text/Sprite_scroll_text.ino diff --git a/Keywords.txt b/Keywords.txt index 47a42a8..0dc74be 100644 --- a/Keywords.txt +++ b/Keywords.txt @@ -5,6 +5,7 @@ drawPixel KEYWORD2 drawChar KEYWORD2 setAddrWindow KEYWORD2 setWindow KEYWORD2 +readAddrWindow KEYWORD2 pushColor KEYWORD2 pushColor KEYWORD2 pushColors KEYWORD2 @@ -49,8 +50,10 @@ readcommand32 KEYWORD2 readPixel KEYWORD2 readRect KEYWORD2 pushRect KEYWORD2 +pushSprite KEYWORD2 readRectRGB KEYWORD2 getRotation KEYWORD2 +getTextDatum KEYWORD2 fontsLoaded KEYWORD2 color565 KEYWORD2 drawChar KEYWORD2 @@ -64,3 +67,32 @@ width KEYWORD2 textWidth KEYWORD2 fontHeight KEYWORD2 +getTouchRaw KEYWORD2 +getTouchRawZ KEYWORD2 +getTouch KEYWORD2 +calibrateTouch KEYWORD2 +setTouch KEYWORD2 +validTouch KEYWORD2 + + +TFT_eSPI_Button KEYWORD1 + +initButton KEYWORD2 +textcolor KEYWORD2 +initButtonUL KEYWORD2 +drawButton KEYWORD2 +contains KEYWORD2 +press KEYWORD2 +isPressed KEYWORD2 +justPressed KEYWORD2 +justReleased KEYWORD2 + + +TFT_eSprite KEYWORD1 + +createSprite KEYWORD2 +deleteSprite KEYWORD2 +fillSprite KEYWORD2 +setWindow KEYWORD2 +pushBitmap KEYWORD2 +pushSprite KEYWORD2 diff --git a/TFT_eSPI.cpp b/TFT_eSPI.cpp index ee1ac08..edf7860 100644 --- a/TFT_eSPI.cpp +++ b/TFT_eSPI.cpp @@ -512,13 +512,14 @@ uint16_t TFT_eSPI::readPixel(int32_t x0, int32_t y0) ***************************************************************************************/ void TFT_eSPI::pushRect(uint32_t x, uint32_t y, uint32_t w, uint32_t h, uint16_t *data) { - if ((x > _width) || (y > _height) || (w == 0) || (h == 0)) return; + if ((x >= _width) || (y >= _height) || (w == 0) || (h == 0)) return; spi_begin(); setAddrWindow(x, y, x + w - 1, y + h - 1); // Sets CS low and sent RAMWR uint32_t len = w * h * 2; + // Push pixels into window rectangle, data is a 16 bit pointer thus increment is halved while ( len >=32 ) {SPI.writeBytes((uint8_t*)data, 32); data += 16; len -= 32; } if (len) SPI.writeBytes((uint8_t*)data, len); @@ -529,6 +530,62 @@ uint16_t TFT_eSPI::readPixel(int32_t x0, int32_t y0) } +/*************************************************************************************** +** Function name: push rectangle (for SPI Interface II i.e. IM [3:0] = "1101") +** Description: push 565 pixel colours into a defined area +***************************************************************************************/ + void TFT_eSPI::pushSprite(int32_t x, int32_t y, uint32_t w, uint32_t h, uint16_t *data) +{ + + if ((x >= (int32_t)_width) || (y >= (int32_t)_height)) return; + + int32_t dx = 0; + int32_t dy = 0; + int32_t dw = w; + int32_t dh = h; + + if (x < 0) { dw += x; dx = -x; x = 0; } + if (y < 0) { dh += y; dy = -y; y = 0; } + + if ((x + w) >= _width ) dw = _width - x; + if ((y + h) >= _height) dh = _height - y; + + if (dw < 0 || dh < 0) return; + + //Serial.print("dx="); Serial.println(dx); + //Serial.print("dy="); Serial.println(dy); + //Serial.print("dw="); Serial.println(dw); + //Serial.print("dh="); Serial.println(dh); + + spi_begin(); + + setAddrWindow(x, y, x + dw - 1, y + dh - 1); // Sets CS low and sent RAMWR + + data += dx + dy * w; + + while (dh--) + { + /* + uint32_t len = dw; + uint16_t* ptr = data; + // Push pixels into window rectangle, data is a 16 bit pointer thus increment is halved + while ( len--) SPI.write16(*ptr++, 0); + data += w; + */ + uint32_t len = dw * 2; + uint8_t* ptr = (uint8_t*)data; + // Push pixels into window rectangle, data is a 16 bit pointer thus increment is halved + while ( len>32) { SPI.writePattern(ptr, 32, 1); len -= 32; ptr += 32; } + if (len) SPI.writePattern((uint8_t*)ptr, len, 1); + data += w; + } + + CS_H; + + spi_end(); +} + + /*************************************************************************************** ** Function name: read rectangle (for SPI Interface II i.e. IM [3:0] = "1101") ** Description: Read RGB pixel colours from a defined area @@ -3906,7 +3963,7 @@ void TFT_eSPI::calibrateTouch(uint16_t *parameters, uint32_t color_fg, uint32_t parameters[2] = touchCalibration_y0; parameters[3] = touchCalibration_y1; parameters[4] = touchCalibration_rotate | (touchCalibration_invert_x <<1) | (touchCalibration_invert_y <<2); - } + } } @@ -4057,134 +4114,787 @@ boolean TFT_eSPI_Button::justReleased() { return (!currstate && laststate); } + /*************************************************************************************** +// The following class creates Sprites in RAM, graphics can then be drawn in the Sprite +// and rendered quickly onto the TFT screen. The class inherits the graphics functions +// from the TFT_eSPI class. Some functions are overridden by this class so that the +// graphics are written to the Sprite rather than the TFT. +// Coded by Bodmer +***************************************************************************************/ + +/*************************************************************************************** +** Function name: TFT_eSprite +** Description: Class constructor +*************************************************************************************x*/ +TFT_eSprite::TFT_eSprite(TFT_eSPI *tft) +{ + _tft = tft; // Pointer to tft class so we can call member functions + + _iwidth = 0; // Initialise width and height to 0 (it does not exist yet) + _iheight = 0; + + _xs = 0; // window bounds for pushColor + _ys = 0; + _xe = 0; + _ye = 0; + + _xptr = 0; // pushColor coordinate + _yptr = 0; + + _icursor_y = _icursor_x = 0; // Text cursor position +} +/*************************************************************************************** +** Function name: createSprite +** Description: Create a sprite (bitmap) of defined width and height +*************************************************************************************x*/ +uint16_t* TFT_eSprite::createSprite(int16_t w, int16_t h) +{ + if ( w < 1 || h < 1) return 0; + _iwidth = w; + _iheight = h; + _img = (uint16_t*) malloc(w * h * sizeof(uint16_t)); + return _img; +} -/*************************************************** - The majority of code in this file is "FunWare", the only condition of use of - those portions is that users have fun! Most of the effort has been spent on - the creation and incorporation of the proportional Run Length Encoded fonts - that can be rendered over an SPI bus at high speeds. + +/*************************************************************************************** +** Function name: deleteSprite +** Description: Delete the sprite to free up memory (RAM) +*************************************************************************************x*/ +void TFT_eSprite::deleteSprite(void) +{ + free(_img); +} + + +/*************************************************************************************** +** Function name: pushSprite +** Description: Delete the sprite to free up memory (RAM) +*************************************************************************************x*/ +void TFT_eSprite::pushSprite(int32_t x, int32_t y) +{ + _tft->pushSprite(x, y, _iwidth, _iheight, _img); +} + + +/*************************************************************************************** +** Function name: readPixel +** Description: Read 565 colour of a pixel at deined coordinates +*************************************************************************************x*/ +uint16_t TFT_eSprite::readPixel(int32_t x, int32_t y) +{ + uint16_t color = _img[x + y * _iwidth]; + return (color >> 8) | (color << 8); +} + + +/*************************************************************************************** +** Function name: pushRect (same as pushBitmap) +** Description: push 565 colour bitmap into a defined area +*************************************************************************************x*/ +void TFT_eSprite::pushRect(uint32_t x, uint32_t y, uint32_t w, uint32_t h, uint16_t *data) +{ + if ((x > _iwidth) || (y > _iheight) || (w == 0) || (h == 0)) return; + + for (uint32_t yp = y; yp < y + h; yp++) + { + for (uint32_t xp = x; xp < x + w; xp++) + { + _img[xp + yp * _iwidth] = *data++; + } + } +} + + +/*************************************************************************************** +** Function name: pushBitmap (same as pushRect) +** Description: push 565 colour bitmap into a defined area +***************************************************************************************/ +void TFT_eSprite::pushBitmap(uint32_t x, uint32_t y, uint32_t w, uint32_t h, uint16_t *data) +{ + if ((x > _iwidth) || (y > _iheight) || (w == 0) || (h == 0)) return; + + for (uint32_t yp = y; yp < y + h; yp++) + { + for (uint32_t xp = x; xp < x + w; xp++) + { + _img[xp + yp * _iwidth] = *data++; + } + } +} + + +/*************************************************************************************** +** Function name: spriteWindow +** Description: Set the bounds of a window for pushColor +*************************************************************************************x*/ +void TFT_eSprite::setWindow(int32_t x0, int32_t y0, int32_t x1, int32_t y1) +{ + // Bounds will be checked by drawPixel + if (x0 > x1) swap_coord(x0, x1); + if (y0 > y1) swap_coord(y0, y1); + + _xs = x0; + _ys = y0; + _xe = x1; + _ye = y1; - A significant number of new features have been added to the original source - libraries. Functions names have been retained where practical to allow old - Adafruit_GFX TFT screen compatible sketches to be easily adapted. - - A significant level of effort has been made to optimise the library for speed - so that graphics intensive sketches can run at an acceptable speed over the - SPI bus. SPI bus speeds up to 80MHz can be used with some driver chips. At - this clock rate screen and block clears can achieve an average bit rate of - 75Mbps. - - The functions incldued are comaptible with the JPEGDecoder library here: - https://github.com/Bodmer/JPEGDecoder - - This allows allows the ESP8266 (or ESP32) sketches to retrieve images from the - internet, store them in SPIFFS and render the images to the screen at an - acceptable speed. So some really cool IoT sketches are possible without tedious - manual loading of images. - - Other portions of code are protected by the licenses as noted below. - - The library would not have been created without the initial inspiration from - Adafruit_ILI9341 and Adafruit_GFX libraries. + _xptr = _xs; + _yptr = _ys; +} - If any other conditions of use have been missed then please raise this as an - issue on GitHub: +/*************************************************************************************** +** Function name: pushColor +** Description: Send a new pixel to the sprite window +*************************************************************************************x*/ +void TFT_eSprite::pushColor(uint32_t color) +{ + drawPixel(_xptr++, _yptr, color); + if (_xptr > _xe) { _xptr = _xs; _yptr++; } + if (_yptr > _ye) { _yptr = _ys; } +} + +void TFT_eSprite::pushColor(uint32_t color, uint16_t len) +{ + drawPixel(_xptr++, _yptr, color); + if (_xptr > _xe) { _xptr = _xs; _yptr++; } + if (_yptr > _ye) { _yptr = _ys; } +} -/*************************************************** - The Adafruit_ILI9341 library has been used as a starting point - for this library. - - ORIGINAL LIBRARY HEADER - - This is our library for the Adafruit ILI9341 Breakout and Shield - ----> http://www.adafruit.com/products/1651 - - Check out the links above for our tutorials and wiring diagrams - These displays use SPI to communicate, 4 or 5 pins are required to - interface (RST is optional) - Adafruit invests time and resources providing this open source code, - please support Adafruit and open-source hardware by purchasing - products from Adafruit! - - Written by Limor Fried/Ladyada for Adafruit Industries. - MIT license, all text above must be included in any redistribution - - ****************************************************/ +/*************************************************************************************** +** Function name: fillSprite +** Description: Fill the whole sprite with defined colour +*************************************************************************************x*/ +void TFT_eSprite::fillSprite(uint32_t color) +{ + // Use memset if possible as it is super fast + if((uint8_t)color == (color>>8)) memset(_img, (uint8_t)color, _iwidth * _iheight * 2); + else fillRect(0, 0, _iwidth, _iheight, color); +} -/**************************************************** - - Some member funtions have been imported from the Adafruit_GFX - library. The license associated with these is reproduced below. - - ORIGINAL LIBRARY HEADER from Adafruit_GFX.cpp - - This is the core graphics library for all our displays, providing a common - set of graphics primitives (points, lines, circles, etc.). It needs to be - paired with a hardware-specific library for each display device we carry - (to handle the lower-level functions). - - Adafruit invests time and resources providing this open source code, please - support Adafruit & open-source hardware by purchasing products from Adafruit! - - Copyright (c) 2013 Adafruit Industries. All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - -- Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. -- Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. - - ****************************************************/ +/*************************************************************************************** +** Function name: setCursor +** Description: Set the sprite text cursor x,y position +*************************************************************************************x*/ +void TFT_eSprite::setCursor(int16_t x, int16_t y) +{ + _icursor_x = x; + _icursor_y = y; +} -/**************************************************** +/*************************************************************************************** +** Function name: width or spriteWidth +** Description: Return the width of sprite +*************************************************************************************x*/ +// Return the size of the display +int16_t TFT_eSprite::width(void) +{ + return _iwidth; +} - In compliance with the licence.txt file for the Adafruit_GFX library the - following is included. +/* +// Return the size of the display +int16_t TFT_eSprite::spriteWidth(void) +{ + return _iwidth; +} +*/ - Software License Agreement (BSD License) +/*************************************************************************************** +** Function name: height or spriteHeight +** Description: Return the height of sprite +*************************************************************************************x*/ +int16_t TFT_eSprite::height(void) +{ + return _iheight; +} - Copyright (c) 2012 Adafruit Industries. All rights reserved. +/* +int16_t TFT_eSprite::spriteHeight(void) +{ + return _iheight; +} +*/ - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: +/*************************************************************************************** +** Function name: drawChar +** Description: draw a single character in the Adafruit GLCD or freefont +*************************************************************************************x*/ +void TFT_eSprite::drawChar(int32_t x, int32_t y, unsigned char c, uint32_t color, uint32_t bg, uint8_t size) +{ + if ((x >= (int16_t)_iwidth) || // Clip right + (y >= (int16_t)_iheight) || // Clip bottom + ((x + 6 * size - 1) < 0) || // Clip left + ((y + 8 * size - 1) < 0)) // Clip top + return; -- Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. -- Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. +#ifdef LOAD_GLCD +//>>>>>>>>>>>>>>>>>> +#ifdef LOAD_GFXFF + if(!gfxFont) { // 'Classic' built-in font +#endif +//>>>>>>>>>>>>>>>>>> - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. + boolean fillbg = (bg != color); + + if ((size==1) && fillbg) + { + byte column[6]; + byte mask = 0x1; + + for (int8_t i = 0; i < 5; i++ ) column[i] = pgm_read_byte(font + (c * 5) + i); + column[5] = 0; + + int8_t j, k; + for (j = 0; j < 8; j++) { + for (k = 0; k < 5; k++ ) { + if (column[k] & mask) { + drawPixel(x + k, y + j, color); + } + else { + drawPixel(x + k, y + j, bg); + } + } + + mask <<= 1; + + drawPixel(x + k, y + j, bg); + } + } + else + { + for (int8_t i = 0; i < 6; i++ ) { + uint8_t line; + if (i == 5) + line = 0x0; + else + line = pgm_read_byte(font + (c * 5) + i); + + if (size == 1) // default size + { + for (int8_t j = 0; j < 8; j++) { + if (line & 0x1) drawPixel(x + i, y + j, color); + line >>= 1; + } + } + else { // big size + for (int8_t j = 0; j < 8; j++) { + if (line & 0x1) fillRect(x + (i * size), y + (j * size), size, size, color); + else if (fillbg) fillRect(x + i * size, y + j * size, size, size, bg); + line >>= 1; + } + } + } + } + +//>>>>>>>>>>>>>>>>>>>>>>>>>>> +#ifdef LOAD_GFXFF + } else { // Custom font +#endif +//>>>>>>>>>>>>>>>>>>>>>>>>>>> +#endif // LOAD_GLCD + +#ifdef LOAD_GFXFF + +//>>>>>>>>>>>>>>>>>>>>>>>>>>> + // Character is assumed previously filtered by write() to eliminate + // newlines, returns, non-printable characters, etc. Calling drawChar() + // directly with 'bad' characters of font may cause mayhem! + if (c > pgm_read_byte(&gfxFont->last)) c = pgm_read_byte(&gfxFont->first);; + c -= pgm_read_byte(&gfxFont->first); + GFXglyph *glyph = &(((GFXglyph *)pgm_read_dword(&gfxFont->glyph))[c]); + uint8_t *bitmap = (uint8_t *)pgm_read_dword(&gfxFont->bitmap); + + uint16_t bo = pgm_read_word(&glyph->bitmapOffset); + uint8_t w = pgm_read_byte(&glyph->width), + h = pgm_read_byte(&glyph->height), + xa = pgm_read_byte(&glyph->xAdvance); + int8_t xo = pgm_read_byte(&glyph->xOffset), + yo = pgm_read_byte(&glyph->yOffset); + uint8_t xx, yy, bits, bit=0; + int16_t xo16, yo16; + + if(size > 1) { + xo16 = xo; + yo16 = yo; + } + + uint16_t hpc = 0; // Horizontal foreground pixel count + for(yy=0; yy>= 1; + } + // Draw pixels for this line as we are about to increment yy + if (hpc) { + if(size == 1) drawFastHLine(x+xo+xx-hpc, y+yo+yy, hpc, color); + else fillRect(x+(xo16+xx-hpc)*size, y+(yo16+yy)*size, size*hpc, size, color); + hpc=0; + } + } + +#endif + + +#ifdef LOAD_GLCD + #ifdef LOAD_GFXFF + } // End classic vs custom font + #endif +#endif + +} + + +/*************************************************************************************** +** Function name: drawPixel +** Description: push a single pixel at an arbitrary position +*************************************************************************************x*/ +void TFT_eSprite::drawPixel(uint32_t x, uint32_t y, uint32_t color) +{ + // x and y are unsigned so that -ve coordinates turn into large positive ones + // this make bounds checking a bit faster + if ((x >= _iwidth) || (y >= _iheight)) return; + color = (color >> 8) | (color << 8); + _img[x+y*_iwidth] = (uint16_t) color; +} + + +/*************************************************************************************** +** Function name: drawLine +** Description: draw a line between 2 arbitrary points +*************************************************************************************x*/ +void TFT_eSprite::drawLine(int32_t x0, int32_t y0, int32_t x1, int32_t y1, uint32_t color) +{ + boolean steep = abs(y1 - y0) > abs(x1 - x0); + if (steep) { + swap_coord(x0, y0); + swap_coord(x1, y1); + } + + if (x0 > x1) { + swap_coord(x0, x1); + swap_coord(y0, y1); + } + + int32_t dx = x1 - x0, dy = abs(y1 - y0);; + + int32_t err = dx >> 1, ystep = -1, xs = x0, dlen = 0; + + if (y0 < y1) ystep = 1; + + // Split into steep and not steep for FastH/V separation + if (steep) { + for (; x0 <= x1; x0++) { + dlen++; + err -= dy; + if (err < 0) { + err += dx; + if (dlen == 1) drawPixel(y0, xs, color); + else drawFastVLine(y0, xs, dlen, color); + dlen = 0; y0 += ystep; xs = x0 + 1; + } + } + if (dlen) drawFastVLine(y0, xs, dlen, color); + } + else + { + for (; x0 <= x1; x0++) { + dlen++; + err -= dy; + if (err < 0) { + err += dx; + if (dlen == 1) drawPixel(xs, y0, color); + else drawFastHLine(xs, y0, dlen, color); + dlen = 0; y0 += ystep; xs = x0 + 1; + } + } + if (dlen) drawFastHLine(xs, y0, dlen, color); + } +} + + +/*************************************************************************************** +** Function name: drawFastVLine +** Description: draw a vertical line +*************************************************************************************x*/ +void TFT_eSprite::drawFastVLine(int32_t x, int32_t y, int32_t h, uint32_t color) +{ + if ((x < 0) || (x >= _iwidth) || (y >= _iheight)) return; + if (y < 0) { h += y; y = 0; } + if ((y + h) > _iheight) h = _iheight - y; + + if (h < 1) return; + color = (color >> 8) | (color << 8); + while (h--) _img[x + _iwidth * y++] = (uint16_t) color; +} + + +/*************************************************************************************** +** Function name: drawFastHLine +** Description: draw a horizontal line +*************************************************************************************x*/ +void TFT_eSprite::drawFastHLine(int32_t x, int32_t y, int32_t w, uint32_t color) +{ + if ((y < 0) || (x >= _iwidth) || (y >= _iheight)) return; + if (x < 0) { w += x; x = 0; } + if ((x + w) > _iwidth) w = _iwidth - x; + + if (w < 1) return; + color = (color >> 8) | (color << 8); + while (w--) _img[_iwidth * y + x++] = (uint16_t) color; +} + + +/*************************************************************************************** +** Function name: fillRect +** Description: draw a filled rectangle +*************************************************************************************x*/ +void TFT_eSprite::fillRect(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color) +{ + if (x < 0) { w += x; x = 0; } + + if ((x < 0) || (y < 0) || (x >= _iwidth) || (y >= _iheight)) return; + if ((x + w) > _iwidth) w = _iwidth - x; + if ((y + h) > _iheight) h = _iheight - y; + if ((w < 1) || (h < 1)) return; + color = (color >> 8) | (color << 8); + + while (h--) { + int32_t ix = x, iw = w; + while (iw--) _img[_iwidth * y + ix++] = (uint16_t) color; + y++; + } +} + + +/*************************************************************************************** +** Function name: write +** Description: draw characters piped through serial stream +*************************************************************************************x*/ +size_t TFT_eSprite::write(uint8_t utf8) +{ + if (utf8 == '\r') return 1; + + uint8_t uniCode = utf8; // Work with a copy + if (utf8 == '\n') uniCode+=22; // Make it a valid space character to stop errors + + uint16_t width = 0; + uint16_t height = 0; + +//vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv DEBUG vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv + //Serial.print((uint8_t) uniCode); // Debug line sends all printed TFT text to serial port + //Serial.println(uniCode, HEX); // Debug line sends all printed TFT text to serial port + //delay(5); // Debug optional wait for serial port to flush through +//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ DEBUG ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< +#ifdef LOAD_GFXFF + if(!gfxFont) { +#endif +//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + +#ifdef LOAD_FONT2 + if (textfont == 2) + { + // This is 20us faster than using the fontdata structure (0.443ms per character instead of 0.465ms) + width = pgm_read_byte(widtbl_f16 + uniCode-32); + height = chr_hgt_f16; + // Font 2 is rendered in whole byte widths so we must allow for this + width = (width + 6) / 8; // Width in whole bytes for font 2, should be + 7 but must allow for font width change + width = width * 8; // Width converted back to pixles + } + #ifdef LOAD_RLE + else + #endif +#endif + +#ifdef LOAD_RLE + { + if ((textfont>2) && (textfont<9)) + { + // Uses the fontinfo struct array to avoid lots of 'if' or 'switch' statements + // A tad slower than above but this is not significant and is more convenient for the RLE fonts + width = pgm_read_byte( (uint8_t *)pgm_read_dword( &(fontdata[textfont].widthtbl ) ) + uniCode-32 ); + height= pgm_read_byte( &fontdata[textfont].height ); + } + } +#endif + +#ifdef LOAD_GLCD + if (textfont==1) + { + width = 6; + height = 8; + } +#else + if (textfont==1) return 0; +#endif + + height = height * textsize; + + if (utf8 == '\n') + { + _icursor_y += height; + _icursor_x = 0; + } + else + { + if (textwrap && (_icursor_x + width * textsize > _iwidth)) + { + _icursor_y += height; + _icursor_x = 0; + } + _icursor_x += drawChar(uniCode, _icursor_x, _icursor_y, textfont); + } + +//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< +#ifdef LOAD_GFXFF + } // Custom GFX font + else + { + + if(utf8 == '\n') { + _icursor_x = 0; + _icursor_y += (int16_t)textsize * (uint8_t)pgm_read_byte(&gfxFont->yAdvance); + } else if(uniCode != '\r') { + if (uniCode > (uint8_t)pgm_read_byte(&gfxFont->last)) uniCode = pgm_read_byte(&gfxFont->first); + + if(uniCode >= pgm_read_byte(&gfxFont->first)) { + uint8_t c2 = uniCode - pgm_read_byte(&gfxFont->first); + GFXglyph *glyph = &(((GFXglyph *)pgm_read_dword(&gfxFont->glyph))[c2]); + uint8_t w = pgm_read_byte(&glyph->width), + h = pgm_read_byte(&glyph->height); + if((w > 0) && (h > 0)) { // Is there an associated bitmap? + int16_t xo = (int8_t)pgm_read_byte(&glyph->xOffset); + if(textwrap && ((_icursor_x + textsize * (xo + w)) > _iwidth)) { + // Drawing character would go off right edge; wrap to new line + _icursor_x = 0; + _icursor_y += (int16_t)textsize * (uint8_t)pgm_read_byte(&gfxFont->yAdvance); + } + drawChar(_icursor_x, _icursor_y, uniCode, textcolor, textbgcolor, textsize); + } + _icursor_x += pgm_read_byte(&glyph->xAdvance) * (int16_t)textsize; + } + } + + } +#endif // LOAD_GFXFF +//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + + return 1; +} + + +/*************************************************************************************** +** Function name: drawChar +** Description: draw a unicode onto the screen +*************************************************************************************x*/ +int16_t TFT_eSprite::drawChar(unsigned int uniCode, int x, int y) +{ + return drawChar(uniCode, x, y, textfont); +} + +int16_t TFT_eSprite::drawChar(unsigned int uniCode, int x, int y, int font) +{ + + if (font==1) + { +#ifdef LOAD_GLCD + #ifndef LOAD_GFXFF + drawChar(x, y, uniCode, textcolor, textbgcolor, textsize); + return 6 * textsize; + #endif +#else + #ifndef LOAD_GFXFF + return 0; + #endif +#endif + +#ifdef LOAD_GFXFF + drawChar(x, y, uniCode, textcolor, textbgcolor, textsize); + if(!gfxFont) { // 'Classic' built-in font + #ifdef LOAD_GLCD + return 6 * textsize; + #else + return 0; + #endif + } + else + { + if (uniCode > pgm_read_byte(&gfxFont->last)) uniCode = pgm_read_byte(&gfxFont->first); + + if(uniCode >= pgm_read_byte(&gfxFont->first)) + { + uint8_t c2 = uniCode - pgm_read_byte(&gfxFont->first); + GFXglyph *glyph = &(((GFXglyph *)pgm_read_dword(&gfxFont->glyph))[c2]); + return pgm_read_byte(&glyph->xAdvance) * textsize; + } + else + { + return 0; + } + } +#endif + } + + int width = 0; + int height = 0; + uint32_t flash_address = 0; + uniCode -= 32; + +#ifdef LOAD_FONT2 + if (font == 2) + { + // This is faster than using the fontdata structure + flash_address = pgm_read_dword(&chrtbl_f16[uniCode]); + width = pgm_read_byte(widtbl_f16 + uniCode); + height = chr_hgt_f16; + } + #ifdef LOAD_RLE + else + #endif +#endif + +#ifdef LOAD_RLE + { + if ((font>2) && (font<9)) + { + // This is slower than above but is more convenient for the RLE fonts + flash_address = pgm_read_dword( pgm_read_dword( &(fontdata[font].chartbl ) ) + uniCode*sizeof(void *) ); + width = pgm_read_byte( (uint8_t *)pgm_read_dword( &(fontdata[font].widthtbl ) ) + uniCode ); + height= pgm_read_byte( &fontdata[font].height ); + } + } +#endif + + int w = width; + int pX = 0; + int pY = y; + byte line = 0; + +#ifdef LOAD_FONT2 // chop out code if we do not need it + if (font == 2) { + w = w + 6; // Should be + 7 but we need to compensate for width increment + w = w / 8; + if (x + width * textsize >= (int16_t)_iwidth) return width * textsize ; + + for (int i = 0; i < height; i++) + { + if (textcolor != textbgcolor) fillRect(x, pY, width * textsize, textsize, textbgcolor); + + for (int k = 0; k < w; k++) + { + line = pgm_read_byte((uint8_t *)flash_address + w * i + k); + if (line) { + if (textsize == 1) { + pX = x + k * 8; + if (line & 0x80) drawPixel(pX, pY, textcolor); + if (line & 0x40) drawPixel(pX + 1, pY, textcolor); + if (line & 0x20) drawPixel(pX + 2, pY, textcolor); + if (line & 0x10) drawPixel(pX + 3, pY, textcolor); + if (line & 0x08) drawPixel(pX + 4, pY, textcolor); + if (line & 0x04) drawPixel(pX + 5, pY, textcolor); + if (line & 0x02) drawPixel(pX + 6, pY, textcolor); + if (line & 0x01) drawPixel(pX + 7, pY, textcolor); + } + else { + pX = x + k * 8 * textsize; + if (line & 0x80) fillRect(pX, pY, textsize, textsize, textcolor); + if (line & 0x40) fillRect(pX + textsize, pY, textsize, textsize, textcolor); + if (line & 0x20) fillRect(pX + 2 * textsize, pY, textsize, textsize, textcolor); + if (line & 0x10) fillRect(pX + 3 * textsize, pY, textsize, textsize, textcolor); + if (line & 0x08) fillRect(pX + 4 * textsize, pY, textsize, textsize, textcolor); + if (line & 0x04) fillRect(pX + 5 * textsize, pY, textsize, textsize, textcolor); + if (line & 0x02) fillRect(pX + 6 * textsize, pY, textsize, textsize, textcolor); + if (line & 0x01) fillRect(pX + 7 * textsize, pY, textsize, textsize, textcolor); + } + } + } + pY += textsize; + } + } + + #ifdef LOAD_RLE + else + #endif +#endif //FONT2 + +#ifdef LOAD_RLE //674 bytes of code + // Font is not 2 and hence is RLE encoded + { + w *= height; // Now w is total number of pixels in the character + + if (textcolor != textbgcolor) fillRect(x, pY, width * textsize, textsize * height, textbgcolor); + int px = 0, py = pY; // To hold character block start and end column and row values + int pc = 0; // Pixel count + byte np = textsize * textsize; // Number of pixels in a drawn pixel + + byte tnp = 0; // Temporary copy of np for while loop + byte ts = textsize - 1; // Temporary copy of textsize + // 16 bit pixel count so maximum font size is equivalent to 180x180 pixels in area + // w is total number of pixels to plot to fill character block + while (pc < w) + { + line = pgm_read_byte((uint8_t *)flash_address); + flash_address++; // 20 bytes smaller by incrementing here + if (line & 0x80) { + line &= 0x7F; + line++; + if (ts) { + px = x + textsize * (pc % width); // Keep these px and py calculations outside the loop as they are slow + py = y + textsize * (pc / width); + } + else { + px = x + pc % width; // Keep these px and py calculations outside the loop as they are slow + py = y + pc / width; + } + while (line--) { + pc++; + setWindow(px, py, px + ts, py + ts); + + if (ts) { + tnp = np; + while (tnp--) { + pushColor(textcolor); + } + } + else { + pushColor(textcolor); + } + px += textsize; + + if (px >= (x + width * textsize)) + { + px = x; + py += textsize; + } + } + } + else { + line++; + pc += line; + } + } + } + // End of RLE font rendering +#endif + return width * textsize; // x + +} - *****************************************************/ diff --git a/TFT_eSPI.h b/TFT_eSPI.h index 29571f5..ffb1c23 100644 --- a/TFT_eSPI.h +++ b/TFT_eSPI.h @@ -30,6 +30,11 @@ #define SPI_FREQUENCY 20000000 #endif +// If the frequency is not defined, set a default +#ifndef SPI_TOUCH_FREQUENCY + #define SPI_TOUCH_FREQUENCY 2500000 +#endif + // Only load the fonts defined in User_Setup.h (to save space) // Set flag so RLE rendering code is optionally compiled #ifdef LOAD_GLCD @@ -267,8 +272,11 @@ typedef struct { // Now fill the structure const PROGMEM fontinfo fontdata [] = { + #ifdef LOAD_GLCD + { (const uint8_t *)font, widtbl_null, 0, 0 }, + #else { (const uint8_t *)chrtbl_null, widtbl_null, 0, 0 }, - + #endif // GLCD font (Font 1) does not have all parameters { (const uint8_t *)chrtbl_null, widtbl_null, 8, 7 }, @@ -320,25 +328,32 @@ class TFT_eSPI : public Print { void init(void), begin(void); // Same - begin included for backwards compatibility - void drawPixel(uint32_t x, uint32_t y, uint32_t color); + // These are virtual so the TFT_eSprite class can override them with sprite specific functions + virtual void drawPixel(uint32_t x, uint32_t y, uint32_t color), + drawChar(int32_t x, int32_t y, unsigned char c, uint32_t color, uint32_t bg, uint8_t font), + setWindow(int16_t x0, int16_t y0, int16_t x1, int16_t y1), + pushColor(uint16_t color), + pushColor(uint16_t color, uint16_t len), + drawLine(int32_t x0, int32_t y0, int32_t x1, int32_t y1, uint32_t color), + drawFastVLine(int32_t x, int32_t y, int32_t h, uint32_t color), + drawFastHLine(int32_t x, int32_t y, int32_t w, uint32_t color), + fillRect(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color); - void drawChar(int32_t x, int32_t y, unsigned char c, uint32_t color, uint32_t bg, uint8_t font), - setWindow(int16_t x0, int16_t y0, int16_t x1, int16_t y1), + virtual int16_t drawChar(unsigned int uniCode, int x, int y, int font), + drawChar(unsigned int uniCode, int x, int y), + height(void), + width(void); - pushColor(uint16_t color), - pushColor(uint16_t color, uint16_t len), + virtual size_t write(uint8_t); - pushColors(uint16_t *data, uint8_t len), + + // The TFT_eSprite class inherits the following functions + void pushColors(uint16_t *data, uint8_t len), pushColors(uint8_t *data, uint32_t len), - fillScreen(uint32_t color), + fillScreen(uint32_t color); - drawLine(int32_t x0, int32_t y0, int32_t x1, int32_t y1, uint32_t color), - drawFastVLine(int32_t x, int32_t y, int32_t h, uint32_t color), - drawFastHLine(int32_t x, int32_t y, int32_t w, uint32_t color), - - drawRect(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color), - fillRect(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color), + void drawRect(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color), drawRoundRect(int32_t x0, int32_t y0, int32_t w, int32_t h, int32_t radius, uint32_t color), fillRoundRect(int32_t x0, int32_t y0, int32_t w, int32_t h, int32_t radius, uint32_t color), @@ -392,6 +407,7 @@ class TFT_eSPI : public Print { void readRect(uint32_t x0, uint32_t y0, uint32_t w, uint32_t h, uint16_t *data); // Write a block of pixels to the screen void pushRect(uint32_t x0, uint32_t y0, uint32_t w, uint32_t h, uint16_t *data); + void pushSprite(int32_t x0, int32_t y0, uint32_t w, uint32_t h, uint16_t *data); // This next function has been used successfully to dump the TFT screen to a PC for documentation purposes // It reads a screen area and returns the RGB 8 bit colour values of each pixel @@ -403,10 +419,8 @@ class TFT_eSPI : public Print { uint16_t fontsLoaded(void), color565(uint8_t r, uint8_t g, uint8_t b); - - int16_t drawChar(unsigned int uniCode, int x, int y, int font), - drawChar(unsigned int uniCode, int x, int y), - drawNumber(long long_num,int poX, int poY, int font), + + int16_t drawNumber(long long_num,int poX, int poY, int font), drawNumber(long long_num,int poX, int poY), drawFloat(float floatNumber,int decimal,int poX, int poY, int font), drawFloat(float floatNumber,int decimal,int poX, int poY), @@ -423,9 +437,7 @@ class TFT_eSPI : public Print { drawCentreString(const String& string, int dX, int poY, int font), // Deprecated, use setTextDatum() and drawString() drawRightString(const String& string, int dX, int poY, int font); // Deprecated, use setTextDatum() and drawString() - int16_t height(void), - width(void), - textWidth(const char *string, int font), + int16_t textWidth(const char *string, int font), textWidth(const char *string), textWidth(const String& string, int font), textWidth(const String& string), @@ -441,7 +453,6 @@ class TFT_eSPI : public Print { void calibrateTouch(uint16_t *data, uint32_t color_fg, uint32_t color_bg, uint8_t size); void setTouch(uint16_t *data); - virtual size_t write(uint8_t); private: @@ -531,6 +542,74 @@ class TFT_eSPI_Button { boolean currstate, laststate; }; + +/*************************************************************************************** +// The following class creates Sprites in RAM, graphics can then be drawn in the Sprite +// and rendered quickly onto the TFT screen. The class inherits the graphics functions +// from the TFT_eSPI class. Some functions are overridden by this class so that the +// graphics are written to the Sprite rather than the TFT. +***************************************************************************************/ + +class TFT_eSprite : public TFT_eSPI { + + public: + + TFT_eSprite(TFT_eSPI *tft); + + uint16_t* createSprite(int16_t w, int16_t y); // 16 bpp + void deleteSprite(void); + + void drawPixel(uint32_t x, uint32_t y, uint32_t color); + + void drawChar(int32_t x, int32_t y, unsigned char c, uint32_t color, uint32_t bg, uint8_t size), + + fillSprite(uint32_t color), + + setWindow(int32_t x0, int32_t y0, int32_t x1, int32_t y1), + pushColor(uint32_t color), + pushColor(uint32_t color, uint16_t len), + + drawLine(int32_t x0, int32_t y0, int32_t x1, int32_t y1, uint32_t color), + drawFastVLine(int32_t x, int32_t y, int32_t h, uint32_t color), + drawFastHLine(int32_t x, int32_t y, int32_t w, uint32_t color), + + fillRect(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color), + + setCursor(int16_t x, int16_t y); + + // Read the colour of a pixel at x,y and return value in 565 format + uint16_t readPixel(int32_t x0, int32_t y0); + + // Write a block of pixels to the sprite + void pushRect(uint32_t x0, uint32_t y0, uint32_t w, uint32_t h, uint16_t *data); + void pushBitmap(uint32_t x0, uint32_t y0, uint32_t w, uint32_t h, uint16_t *data); + + void pushSprite(int32_t x, int32_t y); + + int16_t drawChar(unsigned int uniCode, int x, int y, int font), + drawChar(unsigned int uniCode, int x, int y); + + int16_t height(void), + width(void); + + size_t write(uint8_t); + + private: + + TFT_eSPI *_tft; + + protected: + + uint16_t *_img; + + int32_t _icursor_x, _icursor_y, _xs, _ys, _xe, _ye, _xptr, _yptr; + + int32_t _iwidth, _iheight; // Display w/h as modified by current rotation + +}; + + + #endif /*************************************************** diff --git a/User_Setup_Select.h b/User_Setup_Select.h index 8129612..71050e4 100644 --- a/User_Setup_Select.h +++ b/User_Setup_Select.h @@ -28,6 +28,7 @@ //#include // Setup file configured for my ILI9163 128x128 display //#include // Setup file configured for my ST7735 //#include // Setup file configured for my stock RPi TFT with touch +//#include // Setup file configured for my stock RPi TFT with touch //#include // Setup file template for copying/editting diff --git a/examples/Sprite/Sprite_RLE_Font_test/Sprite_RLE_Font_test.ino b/examples/Sprite/Sprite_RLE_Font_test/Sprite_RLE_Font_test.ino new file mode 100644 index 0000000..32c22b3 --- /dev/null +++ b/examples/Sprite/Sprite_RLE_Font_test/Sprite_RLE_Font_test.ino @@ -0,0 +1,199 @@ +/* + Display all the fast rendering fonts. + + Make sure all the display driver and pin comnections are correct by + editting the User_Setup.h file in the TFT_eSPI library folder. + + ######################################################################### + ###### DON'T FORGET TO UPDATE THE User_Setup.h FILE IN THE LIBRARY ###### + ######################################################################### +*/ + +// Create a sprite 160 x 128 pixels (needs 40Kbytes of RAM!) +#define IWIDTH 160 +#define IHEIGHT 128 + +// New background colour +#define TFT_BROWN 0x38E0 + +// Pause in milliseconds between screens, change to 0 to time font rendering +#define WAIT 500 + +#include // Graphics and font library for ST7735 driver chip +#include + +TFT_eSPI tft = TFT_eSPI(); // Invoke library, pins defined in User_Setup.h + +TFT_eSprite img = TFT_eSprite(&tft); + +unsigned long targetTime = 0; // Used for testing draw times + +void setup(void) { + tft.init(); + tft.setRotation(0); + + tft.fillScreen(TFT_BLUE); + + img.createSprite(IWIDTH, IHEIGHT); + img.fillSprite(TFT_BLACK); +} + +void loop() { + targetTime = millis(); + + // First we test them with a background colour set + img.setTextSize(1); + img.fillSprite(TFT_BLACK); + img.setTextColor(TFT_GREEN, TFT_BLACK); + + img.drawString(" !\"#$%&'()*+,-./0123456", 0, 0, 2); + img.drawString("789:;<=>?@ABCDEFGHIJKL", 0, 16, 2); + img.drawString("MNOPQRSTUVWXYZ[\\]^_`", 0, 32, 2); + img.drawString("abcdefghijklmnopqrstuvw", 0, 48, 2); + + int xpos = 0; + xpos += img.drawString("xyz{|}~", 0, 64, 2); + img.drawChar(127, xpos, 64, 2); + img.pushSprite(0, 0); delay(WAIT); + + img.fillSprite(TFT_BLACK); + img.setTextColor(TFT_GREEN, TFT_BLACK); + + img.drawString(" !\"#$%&'()*+,-.", 0, 0, 4); + img.drawString("/0123456789:;", 0, 26, 4); + img.drawString("<=>?@ABCDE", 0, 52, 4); + img.drawString("FGHIJKLMNO", 0, 78, 4); + img.drawString("PQRSTUVWX", 0, 104, 4); + img.pushSprite(0, 0); delay(WAIT); + + img.fillSprite(TFT_BLACK); + img.drawString("YZ[\\]^_`abc", 0, 0, 4); + img.drawString("defghijklmno", 0, 26, 4); + img.drawString("pqrstuvwxyz", 0, 52, 4); + xpos = 0; + xpos += img.drawString("{|}~", 0, 78, 4); + img.drawChar(127, xpos, 78, 4); + img.pushSprite(0, 0); delay(WAIT); + + img.fillSprite(TFT_BLACK); + img.setTextColor(TFT_BLUE, TFT_BLACK); + + img.drawString("012345", 0, 0, 6); + img.drawString("6789", 0, 40, 6); + img.drawString("apm-:.", 0, 80, 6); + img.pushSprite(0, 0); delay(WAIT); + + img.fillSprite(TFT_BLACK); + img.setTextColor(TFT_RED, TFT_BLACK); + + img.drawString("0123", 0, 0, 7); + img.drawString("4567", 0, 60, 7); + img.pushSprite(0, 0); delay(WAIT); + + img.fillSprite(TFT_BLACK); + img.drawString("890:.", 0, 0, 7); + img.drawString("", 0, 60, 7); + img.pushSprite(0, 0); delay(WAIT); + + img.fillSprite(TFT_BLACK); + img.setTextColor(TFT_YELLOW, TFT_BLACK); + + img.drawString("01", 0, 0, 8); + img.pushSprite(0, 0); delay(WAIT); + + img.drawString("23", 0, 0, 8); + img.pushSprite(0, 0); delay(WAIT); + + img.drawString("45", 0, 0, 8); + img.pushSprite(0, 0); delay(WAIT); + + img.drawString("67", 0, 0, 8); + img.pushSprite(0, 0); delay(WAIT); + + img.drawString("89", 0, 0, 8); + img.pushSprite(0, 0); delay(WAIT); + + img.drawString("0:.", 0, 0, 8); + img.pushSprite(0, 0); delay(WAIT); + + img.setTextColor(TFT_WHITE); + img.drawNumber(millis() - targetTime, 0, 100, 4); + img.pushSprite(0, 0); delay(WAIT); + delay(4000); + + // Now test them with transparent background + targetTime = millis(); + + img.setTextSize(1); + img.fillSprite(TFT_BROWN); + img.setTextColor(TFT_GREEN); + + img.drawString(" !\"#$%&'()*+,-./0123456", 0, 0, 2); + img.drawString("789:;<=>?@ABCDEFGHIJKL", 0, 16, 2); + img.drawString("MNOPQRSTUVWXYZ[\\]^_`", 0, 32, 2); + img.drawString("abcdefghijklmnopqrstuvw", 0, 48, 2); + xpos = 0; + xpos += img.drawString("xyz{|}~", 0, 64, 2); + img.drawChar(127, xpos, 64, 2); + img.pushSprite(0, 0); delay(WAIT); + + img.fillSprite(TFT_BROWN); + img.setTextColor(TFT_GREEN); + + img.drawString(" !\"#$%&'()*+,-.", 0, 0, 4); + img.drawString("/0123456789:;", 0, 26, 4); + img.drawString("<=>?@ABCDE", 0, 52, 4); + img.drawString("FGHIJKLMNO", 0, 78, 4); + img.drawString("PQRSTUVWX", 0, 104, 4); + + img.pushSprite(0, 0); delay(WAIT); + img.fillSprite(TFT_BROWN); + img.drawString("YZ[\\]^_`abc", 0, 0, 4); + img.drawString("defghijklmno", 0, 26, 4); + img.drawString("pqrstuvwxyz", 0, 52, 4); + xpos = 0; + xpos += img.drawString("{|}~", 0, 78, 4); + img.drawChar(127, xpos, 78, 4); + img.pushSprite(0, 0); delay(WAIT); + + img.fillSprite(TFT_BROWN); + img.setTextColor(TFT_BLUE); + + img.drawString("012345", 0, 0, 6); + img.drawString("6789", 0, 40, 6); + img.drawString("apm-:.", 0, 80, 6); + img.pushSprite(0, 0); delay(WAIT); + + img.fillSprite(TFT_BROWN); + img.setTextColor(TFT_RED); + + img.drawString("0123", 0, 0, 7); + img.drawString("4567", 0, 60, 7); + img.pushSprite(0, 0); delay(WAIT); + + img.fillSprite(TFT_BROWN); + img.drawString("890:.", 0, 0, 7); + img.drawString("", 0, 60, 7); + img.pushSprite(0, 0); delay(WAIT); + + img.fillSprite(TFT_BROWN); + img.setTextColor(TFT_YELLOW); + + img.drawString("0123", 0, 0, 8); + img.pushSprite(0, 0); delay(WAIT); + + img.fillSprite(TFT_BROWN); + img.drawString("4567", 0, 0, 8); + img.pushSprite(0, 0); delay(WAIT); + + img.fillSprite(TFT_BROWN); + img.drawString("890:.", 0, 0, 8); + img.pushSprite(0, 0); delay(WAIT); + + img.setTextColor(TFT_WHITE); + + img.drawNumber(millis() - targetTime, 0, 100, 4); + img.pushSprite(0, 0); delay(WAIT); + delay(4000);; +} + diff --git a/examples/Sprite/Sprite_TFT_Rainbow/Sprite_TFT_Rainbow.ino b/examples/Sprite/Sprite_TFT_Rainbow/Sprite_TFT_Rainbow.ino new file mode 100644 index 0000000..8a680a8 --- /dev/null +++ b/examples/Sprite/Sprite_TFT_Rainbow/Sprite_TFT_Rainbow.ino @@ -0,0 +1,140 @@ +/* + An example showing rainbow colours on a 160x128 TFT LCD screen + and to show a basic example of font use. + + This example plots the text in a sprite then pushes the sprite to the + TFT screen. + + Make sure all the display driver and pin comnenctions are correct by + editting the User_Setup.h file in the TFT_eSPI library folder. + + Note that yield() or delay(0) must be called in long duration for/while + loops to stop the ESP8266 watchdog triggering. + + ######################################################################### + ###### DON'T FORGET TO UPDATE THE User_Setup.h FILE IN THE LIBRARY ###### + ######################################################################### +*/ + +#define IWIDTH 160 +#define IHEIGHT 128 + +#include // Graphics and font library +#include + +TFT_eSPI tft = TFT_eSPI(); // Invoke library, pins defined in User_Setup.h + +TFT_eSprite img = TFT_eSprite(&tft); + +unsigned long targetTime = 0; +byte red = 31; +byte green = 0; +byte blue = 0; +byte state = 0; +unsigned int colour = red << 11; + +void setup(void) { + tft.init(); + tft.setRotation(1); + tft.fillScreen(TFT_BLACK); + + imgPtr = img.createSprite(IWIDTH, IHEIGHT); + img.fillSprite(TFT_BLACK); + + targetTime = millis() + 1000; +} + +void loop() { + + if (targetTime < millis()) { + targetTime = millis() + 100;//10000; + + // Colour changing state machine + for (int i = 0; i < 160; i++) { + img.drawFastVLine(i, 0, img.height(), colour); + switch (state) { + case 0: + green += 2; + if (green == 64) { + green = 63; + state = 1; + } + break; + case 1: + red--; + if (red == 255) { + red = 0; + state = 2; + } + break; + case 2: + blue ++; + if (blue == 32) { + blue = 31; + state = 3; + } + break; + case 3: + green -= 2; + if (green == 255) { + green = 0; + state = 4; + } + break; + case 4: + red ++; + if (red == 32) { + red = 31; + state = 5; + } + break; + case 5: + blue --; + if (blue == 255) { + blue = 0; + state = 0; + } + break; + } + colour = red << 11 | green << 5 | blue; + } + + // The standard ADAFruit font still works as before + img.setTextColor(TFT_BLACK); + img.setCursor (12, 5); + img.print("Original ADAfruit font!"); + + // The new larger fonts do not use the .setCursor call, coords are embedded + img.setTextColor(TFT_BLACK, TFT_BLACK); // Do not plot the background colour + + // Overlay the black text on top of the rainbow plot (the advantage of not drawing the backgorund colour!) + img.drawCentreString("Font size 2", 80, 14, 2); // Draw text centre at position 80, 12 using font 2 + + //img.drawCentreString("Font size 2",81,12,2); // Draw text centre at position 80, 12 using font 2 + + img.drawCentreString("Font size 4", 80, 30, 4); // Draw text centre at position 80, 24 using font 4 + + img.drawCentreString("12.34", 80, 54, 6); // Draw text centre at position 80, 24 using font 6 + + img.drawCentreString("12.34 is in font size 6", 80, 92, 2); // Draw text centre at position 80, 90 using font 2 + + // Note the x position is the top left of the font! + + // draw a floating point number + float pi = 3.14159; // Value to print + int precision = 3; // Number of digits after decimal point + int xpos = 50; // x position + int ypos = 110; // y position + int font = 2; // font number only 2,4,6,7 valid. Font 6 only contains characters [space] 0 1 2 3 4 5 6 7 8 9 0 : a p m + xpos += img.drawFloat(pi, precision, xpos, ypos, font); // Draw rounded number and return new xpos delta for next print position + img.drawString(" is pi", xpos, ypos, font); // Continue printing from new x position + + tft.pushSprite(0, 0, IWIDTH, IHEIGHT, imgPtr); + } +} + + + + + + diff --git a/examples/Sprite/Sprite_drawPixel/Sprite_drawPixel.ino b/examples/Sprite/Sprite_drawPixel/Sprite_drawPixel.ino new file mode 100644 index 0000000..bc64952 --- /dev/null +++ b/examples/Sprite/Sprite_drawPixel/Sprite_drawPixel.ino @@ -0,0 +1,134 @@ +/* + + Sketch to show how a Sprite is created, how to draw pixels + and text within the Sprite and then split the Sprite onto + the display screen. + + Example for library: + https://github.com/Bodmer/TFT_eSPI + + A Sprite is notionally an invisibly graphics screen that is + kept in the processors RAM. Graphics can be drawn into the + Sprite just as it can be drawn directly to the screen. Once + the Sprite is completed it can be plotted onto the screen in + any position. If there is sufficient RAM then the Sprite can + be the same size as the screen and used as a frame buffer. + + The Sprite occupies (2 * width * height) bytes in RAM. + + On a ESP8266 Sprite sizes up to 126 x 160 can be accomodated, + this size requires 40kBytes of RAM. + +*/ + +// Set delay after plotting the sprite +#define DELAY 1000 + +// Width and height of sprite +#define WIDTH 128 +#define HEIGHT 128 + +#include // Include the graphics library (this includes the sprite functions) + +TFT_eSPI tft = TFT_eSPI(); // Declare object "tft" + +TFT_eSprite spr = TFT_eSprite(&tft); // Declare Sprite object "spr" with pointer to "tft" object + +void setup() +{ + Serial.begin(250000); + Serial.println(); + + // Initialise the TFT registers + tft.init(); + + // Create a sprite of defined size + spr.createSprite(WIDTH, HEIGHT); + + // Clear the TFT screen to blue + tft.fillScreen(TFT_BLUE); +} + +void loop(void) +{ + // Fill the whole sprite with black (Sprite is in memory so not visible yet) + spr.fillSprite(TFT_BLACK); + + // Number of pixels to draw + uint16_t n = 100; + + // Draw 100 random colour pixels at random positions in sprite + while (n--) + { + uint16_t colour = random(0x10000); // Returns colour 0 - 0xFFFF + int16_t x = random(WIDTH); // Random x coordinate + int16_t y = random(HEIGHT); // Random y coordinate + spr.drawPixel( x, y, colour); // Draw pixel in sprite + } + + // Draw some lines + spr.drawLine(1, 0, WIDTH, HEIGHT-1, TFT_GREEN); + spr.drawLine(0, 0, WIDTH, HEIGHT, TFT_GREEN); + spr.drawLine(0, 1, WIDTH-1, HEIGHT, TFT_GREEN); + spr.drawLine(0, HEIGHT-1, WIDTH-1, 0, TFT_RED); + spr.drawLine(0, HEIGHT, WIDTH, 0, TFT_RED); + spr.drawLine(1, HEIGHT, WIDTH, 1, TFT_RED); + + // Draw some text with Middle Centre datum + spr.setTextDatum(MC_DATUM); + spr.drawString("Sprite", WIDTH / 2, HEIGHT / 2, 4); + + // Now push the sprite to the TFT at position 0,0 on screen + spr.pushSprite(-40, -40); + spr.pushSprite(tft.width() / 2 - WIDTH / 2, tft.height() / 2 - HEIGHT / 2); + spr.pushSprite(tft.width() - WIDTH + 40, tft.height() - HEIGHT + 40); + + delay(DELAY); + + // Fill TFT screen with blue + tft.fillScreen(TFT_BLUE); + + // Draw a blue rectangle in sprite so when we move it 1 pixel it does not leave a trail + // on the blue screen background + spr.drawRect(0, 0, WIDTH, HEIGHT, TFT_BLUE); + + int x = tft.width() / 2 - WIDTH / 2; + int y = tft.height() / 2 - HEIGHT / 2; + + uint32_t updateTime = 0; // time for next update + + while (true) + { + // Random movement direction + int dx = 1; if (random(2)) dx = -1; + int dy = 1; if (random(2)) dy = -1; + + // Pull it back onto screen if it wanders off + if (x < -WIDTH/2) dx = 1; + if (x >= tft.width()-WIDTH/2) dx = -1; + if (y < -HEIGHT/2) dy = 1; + if (y >= tft.height()-HEIGHT/2) dy = -1; + + // Draw it 50 time, moving in random direct or staying still + n = 50; + int wait = random (50); + while (n) + { + if (updateTime <= millis()) + { + // Use time delay so sprtie does not move fast when not all on screen + updateTime = millis() + wait; + + // Push the sprite to the TFT screen + spr.pushSprite(x, y); + + // Change coord for next loop + x += dx; + y += dy; + n--; + yield(); // Stop watchdog reset + } + } + } // Infinite while, will not exit! +} + diff --git a/examples/Sprite/Sprite_scroll_text/Sprite_scroll_text.ino b/examples/Sprite/Sprite_scroll_text/Sprite_scroll_text.ino new file mode 100644 index 0000000..ce8cc1b --- /dev/null +++ b/examples/Sprite/Sprite_scroll_text/Sprite_scroll_text.ino @@ -0,0 +1,194 @@ +/* + Display "flicker free" scrolling text and updating number + + Example for library: + https://github.com/Bodmer/TFT_eSPI + + The sketch has been tested on a 320x240 ILI9341 based TFT, it + coule be adapted for other screen sizes. + + A Sprite is notionally an invisibly graphics screen that is + kept in the processors RAM. Graphics can be drawn into the + Sprite just as it can be drawn directly to the screen. Once + the Sprite is completed it can be plotted onto the screen in + any position. If there is sufficient RAM then the Sprite can + be the same size as the screen and used as a frame buffer. + + The Sprite occupies (2 * width * height) bytes. + + On a ESP8266 Sprite sizes up to 128 x 160 can be accomodated, + this size requires 128*160*2 bytes (40kBytes) of RAM, this must be + available or the processor will crash. You need to make the sprite + small enough to fit, with RAM spare for any "local variables" that + may be needed by your sketch and libraries. + + Created by Bodmer 15/11/17 + + ######################################################################### + ###### DON'T FORGET TO UPDATE THE User_Setup.h FILE IN THE LIBRARY ###### + ######################################################################### +*/ + +// Size of sprite image for the scrolling text, this requires ~14 Kbytes of RAM +#define IWIDTH 240 +#define IHEIGHT 30 + +// Pause in milliseconds to set scroll speed +#define WAIT 0 + +#include // Include the graphics library (this includes the sprite functions) + +TFT_eSPI tft = TFT_eSPI(); // Create object "tft" + +TFT_eSprite img = TFT_eSprite(&tft); // Create Sprite object "img" with pointer to "tft" object +// // the pointer is used by pushSprite() to push it onto the TFT + +// ------------------------------------------------------------------------- +// Setup +// ------------------------------------------------------------------------- +void setup(void) { + tft.init(); + tft.setRotation(0); + + tft.fillScreen(TFT_BLUE); +} + +// ------------------------------------------------------------------------- +// Main loop +// ------------------------------------------------------------------------- +void loop() { + + while (1) + { + // Create the sprite and clear background to black + img.createSprite(IWIDTH, IHEIGHT); + //img.fillSprite(TFT_BLACK); // Optional here as we fill the sprite later anyway + + for (int pos = IWIDTH; pos > 0; pos--) + { + build_banner("Hello World", pos); + img.pushSprite(0, 0); + + build_banner("TFT_eSPI sprite" , pos); + img.pushSprite(0, 50); + + delay(WAIT); + } + + // Delete sprite to free up the memory + img.deleteSprite(); + + // Create a sprite of a different size + numberBox(random(100), 60, 100); + + } +} + +// ######################################################################### +// Build the scrolling sprite image from scratch, draw text at x = xpos +// ######################################################################### + +void build_banner(String msg, int xpos) +{ + int h = IHEIGHT; + + // We could just use fillSprite(color) but lets be a bit more creative... + + // Fill with rainbow stripes + while (h--) img.drawFastHLine(0, h, IWIDTH, rainbow(h * 4)); + + // Draw some graphics, the text will apear to scroll over these + img.fillRect (IWIDTH / 2 - 20, IHEIGHT / 2 - 10, 40, 20, TFT_YELLOW); + img.fillCircle(IWIDTH / 2, IHEIGHT / 2, 10, TFT_ORANGE); + + // Now print text on top of the graphics + img.setTextSize(1); // Font size scaling is x1 + img.setTextFont(4); // Font 4 selected + img.setTextColor(TFT_BLACK); // Black text, no background colour + img.setTextWrap(false); // Turn of wrap so we can print past end of sprite + + // Need to print twice so text appears to wrap around at left and right edges + img.setCursor(xpos, 2); // Print text at xpos + img.print(msg); + + img.setCursor(xpos - IWIDTH, 2); // Print text at xpos - sprite width + img.print(msg); +} + +// ######################################################################### +// Create sprite, plot graphics in it, plot to screen, then delete sprite +// ######################################################################### +void numberBox(int num, int x, int y) +{ + // Create a sprite 80 pixels wide, 50 high (8kbytes of RAM needed) + img.createSprite(80, 50); + + // Fill it with black + img.fillSprite(TFT_BLACK); + + // Draw a backgorund of 2 filled triangles + img.fillTriangle( 0, 0, 0, 49, 40, 25, TFT_RED); + img.fillTriangle( 79, 0, 79, 49, 40, 25, TFT_DARKGREEN); + + // Set the font parameters + img.setTextSize(1); // Font size scaling is x1 + img.setFreeFont(&FreeSerifBoldItalic24pt7b); // Select free font + img.setTextColor(TFT_WHITE); // White text, no background colour + + // Set text coordinate datum to middle centre + img.setTextDatum(MC_DATUM); + + // Draw the number in middle of 80 x 50 sprite + img.drawNumber(num, 40, 25); + + // Push sprite to TFT screen CGRAM at coordinate x,y (top left corner) + img.pushSprite(x, y); + + // Delete sprite to free up the RAM + img.deleteSprite(); +} + + +// ######################################################################### +// Return a 16 bit rainbow colour +// ######################################################################### +unsigned int rainbow(byte value) +{ + // Value is expected to be in range 0-127 + // The value is converted to a spectrum colour from 0 = red through to 127 = blue + + byte red = 0; // Red is the top 5 bits of a 16 bit colour value + byte green = 0;// Green is the middle 6 bits + byte blue = 0; // Blue is the bottom 5 bits + + byte sector = value >> 5; + byte amplit = value & 0x1F; + + switch (sector) + { + case 0: + red = 0x1F; + green = amplit; + blue = 0; + break; + case 1: + red = 0x1F - amplit; + green = 0x1F; + blue = 0; + break; + case 2: + red = 0; + green = 0x1F; + blue = amplit; + break; + case 3: + red = 0; + green = 0x1F - amplit; + blue = 0x1F; + break; + } + + return red << 11 | green << 6 | blue; +} + + From 2c6f1c70e4f65f3ecbe762fa70552cd7e77955a0 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Wed, 15 Nov 2017 21:55:06 +0000 Subject: [PATCH 020/150] typo --- examples/Sprite/Sprite_drawPixel/Sprite_drawPixel.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/Sprite/Sprite_drawPixel/Sprite_drawPixel.ino b/examples/Sprite/Sprite_drawPixel/Sprite_drawPixel.ino index bc64952..8dc295e 100644 --- a/examples/Sprite/Sprite_drawPixel/Sprite_drawPixel.ino +++ b/examples/Sprite/Sprite_drawPixel/Sprite_drawPixel.ino @@ -1,7 +1,7 @@ /* Sketch to show how a Sprite is created, how to draw pixels - and text within the Sprite and then split the Sprite onto + and text within the Sprite and then push the Sprite onto the display screen. Example for library: From fbb07be73b5285c9f6389d9861b10476001fbd46 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Wed, 15 Nov 2017 22:38:57 +0000 Subject: [PATCH 021/150] Stop pointer error --- examples/Sprite/Sprite_TFT_Rainbow/Sprite_TFT_Rainbow.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/Sprite/Sprite_TFT_Rainbow/Sprite_TFT_Rainbow.ino b/examples/Sprite/Sprite_TFT_Rainbow/Sprite_TFT_Rainbow.ino index 8a680a8..13ecd63 100644 --- a/examples/Sprite/Sprite_TFT_Rainbow/Sprite_TFT_Rainbow.ino +++ b/examples/Sprite/Sprite_TFT_Rainbow/Sprite_TFT_Rainbow.ino @@ -38,7 +38,7 @@ void setup(void) { tft.setRotation(1); tft.fillScreen(TFT_BLACK); - imgPtr = img.createSprite(IWIDTH, IHEIGHT); + img.createSprite(IWIDTH, IHEIGHT); img.fillSprite(TFT_BLACK); targetTime = millis() + 1000; From 1b5ffe418935e88f06d0648147ad3c57e492a9d7 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Wed, 15 Nov 2017 22:41:17 +0000 Subject: [PATCH 022/150] Fn changed --- examples/Sprite/Sprite_TFT_Rainbow/Sprite_TFT_Rainbow.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/Sprite/Sprite_TFT_Rainbow/Sprite_TFT_Rainbow.ino b/examples/Sprite/Sprite_TFT_Rainbow/Sprite_TFT_Rainbow.ino index 13ecd63..e384ea2 100644 --- a/examples/Sprite/Sprite_TFT_Rainbow/Sprite_TFT_Rainbow.ino +++ b/examples/Sprite/Sprite_TFT_Rainbow/Sprite_TFT_Rainbow.ino @@ -129,7 +129,7 @@ void loop() { xpos += img.drawFloat(pi, precision, xpos, ypos, font); // Draw rounded number and return new xpos delta for next print position img.drawString(" is pi", xpos, ypos, font); // Continue printing from new x position - tft.pushSprite(0, 0, IWIDTH, IHEIGHT, imgPtr); + img.pushSprite(0, 0); } } From 25a8e87f33f65af44bbf47bba077e01f13f0716c Mon Sep 17 00:00:00 2001 From: Bodmer Date: Thu, 16 Nov 2017 21:24:35 +0000 Subject: [PATCH 023/150] Add sprite text --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 76a1129..5615613 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,8 @@ An Arduino IDE compatible graphics and fonts library for ESP8266 and ESP32 proce The library also supports TFT displays designed for the Raspberry Pi that are based on a ILI9486 driver chip with a 480 x 320 pixel screen. This display must be of the Waveshare design and use a 16 bit serial interface based on the 74HC04, 74HC4040 and 2 x 74HC4094 logic chips. A modification to these displays is possible (see mod image in Tools folder) to make many graphics functions much faster (e.g. 23ms to clear the screen, 1.2ms to draw a 72 pixel high numeral). +A new "Sprite" class has been added, this enabled flicker free updates of complex graphics. Exmaples are in the "Examples/Sprite" folder. A Sprite is notionally an invisible graphics screen that is kept in the processors RAM. Graphics can be drawn into the Sprite just as it can be drawn directly to the screen. Once the Sprite is completed it can be plotted onto the screen in any position. If there is sufficient RAM then the Sprite can be the same size as the screen and used as a frame buffer. On an ESP8266 the largest Sprite is about 160x128 pixels, this consumes 40Kbytes of RAM. On an ESP32 the workspace RAM is more limited than the datsheet implies so the Sprite is limited to about 200x200 pixels (~80Kbytes). The RAM needed is 2 x width x height of sprite. Drawing graphics into a sprite is very fast, for those familiar with the Adafruit "graphicstest" example, this whole test completes in less than 27ms in a 160x128 sprite. + The XPT2046 touch screen controller is supported. The SPI bus for the touch controller is shared with the TFT and only an additional chip select line is needed. The Button class from Adafruit_GFX has been added, with the enhancement that the button labels can be in any font. From 85698be07fdb82ca8fa40aa6426dc82b90d4b8ab Mon Sep 17 00:00:00 2001 From: Bodmer Date: Thu, 16 Nov 2017 21:27:38 +0000 Subject: [PATCH 024/150] Sprite text update --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5615613..3165854 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ An Arduino IDE compatible graphics and fonts library for ESP8266 and ESP32 proce The library also supports TFT displays designed for the Raspberry Pi that are based on a ILI9486 driver chip with a 480 x 320 pixel screen. This display must be of the Waveshare design and use a 16 bit serial interface based on the 74HC04, 74HC4040 and 2 x 74HC4094 logic chips. A modification to these displays is possible (see mod image in Tools folder) to make many graphics functions much faster (e.g. 23ms to clear the screen, 1.2ms to draw a 72 pixel high numeral). -A new "Sprite" class has been added, this enabled flicker free updates of complex graphics. Exmaples are in the "Examples/Sprite" folder. A Sprite is notionally an invisible graphics screen that is kept in the processors RAM. Graphics can be drawn into the Sprite just as it can be drawn directly to the screen. Once the Sprite is completed it can be plotted onto the screen in any position. If there is sufficient RAM then the Sprite can be the same size as the screen and used as a frame buffer. On an ESP8266 the largest Sprite is about 160x128 pixels, this consumes 40Kbytes of RAM. On an ESP32 the workspace RAM is more limited than the datsheet implies so the Sprite is limited to about 200x200 pixels (~80Kbytes). The RAM needed is 2 x width x height of sprite. Drawing graphics into a sprite is very fast, for those familiar with the Adafruit "graphicstest" example, this whole test completes in less than 27ms in a 160x128 sprite. +A new "Sprite" class has been added, this enables flicker free updates of complex graphics. Exmaples are in the "examples/Sprite" folder. A Sprite is notionally an invisible graphics screen that is kept in the processors RAM. Graphics can be drawn into the Sprite just as it can be drawn directly to the screen. Once the Sprite is completed it can be plotted onto the screen in any position. If there is sufficient RAM then the Sprite can be the same size as the screen and used as a frame buffer. On an ESP8266 the largest Sprite that can be created is about 160x128 pixels, this consumes 40Kbytes of RAM. On an ESP32 the workspace RAM is more limited than the datsheet implies so the Sprite is limited to about 200x200 pixels (~80Kbytes). The RAM needed is 2 x width x height of sprite. Drawing graphics into a sprite is very fast, for those familiar with the Adafruit "graphicstest" example, this whole test completes in less than 27ms in a 160x128 sprite. The XPT2046 touch screen controller is supported. The SPI bus for the touch controller is shared with the TFT and only an additional chip select line is needed. From 3e3d638bba1f01ac80a9a5b7a7d09cc1ecc6e13d Mon Sep 17 00:00:00 2001 From: Bodmer Date: Tue, 21 Nov 2017 22:57:11 +0000 Subject: [PATCH 025/150] Add support for 8 bit color sprites (needs less RAM) Added setColorDepth(d) where "d" is 8 or 16. Default is 16 bit if not specified. --- Keywords.txt | 1 + TFT_eSPI.cpp | 330 ++++++++++++++---- TFT_eSPI.h | 16 +- .../Sprite_scroll_16bit.ino} | 0 .../Sprite_scroll_8bit/Sprite_scroll_8bit.ino | 205 +++++++++++ 5 files changed, 472 insertions(+), 80 deletions(-) rename examples/Sprite/{Sprite_scroll_text/Sprite_scroll_text.ino => Sprite_scroll_16bit/Sprite_scroll_16bit.ino} (100%) create mode 100644 examples/Sprite/Sprite_scroll_8bit/Sprite_scroll_8bit.ino diff --git a/Keywords.txt b/Keywords.txt index 0dc74be..97f9b28 100644 --- a/Keywords.txt +++ b/Keywords.txt @@ -91,6 +91,7 @@ justReleased KEYWORD2 TFT_eSprite KEYWORD1 createSprite KEYWORD2 +setColorDepth KEYWORD2 deleteSprite KEYWORD2 fillSprite KEYWORD2 setWindow KEYWORD2 diff --git a/TFT_eSPI.cpp b/TFT_eSPI.cpp index edf7860..d84b966 100644 --- a/TFT_eSPI.cpp +++ b/TFT_eSPI.cpp @@ -552,11 +552,6 @@ uint16_t TFT_eSPI::readPixel(int32_t x0, int32_t y0) if (dw < 0 || dh < 0) return; - //Serial.print("dx="); Serial.println(dx); - //Serial.print("dy="); Serial.println(dy); - //Serial.print("dw="); Serial.println(dw); - //Serial.print("dh="); Serial.println(dh); - spi_begin(); setAddrWindow(x, y, x + dw - 1, y + dh - 1); // Sets CS low and sent RAMWR @@ -574,11 +569,71 @@ uint16_t TFT_eSPI::readPixel(int32_t x0, int32_t y0) */ uint32_t len = dw * 2; uint8_t* ptr = (uint8_t*)data; + // Push pixels into window rectangle, data is a 16 bit pointer thus increment is halved while ( len>32) { SPI.writePattern(ptr, 32, 1); len -= 32; ptr += 32; } if (len) SPI.writePattern((uint8_t*)ptr, len, 1); data += w; + } + + CS_H; + + spi_end(); +} + + void TFT_eSPI::pushSprite(int32_t x, int32_t y, uint32_t w, uint32_t h, uint8_t *data) +{ + + if ((x >= (int32_t)_width) || (y >= (int32_t)_height)) return; + + int32_t dx = 0; + int32_t dy = 0; + int32_t dw = w; + int32_t dh = h; + + if (x < 0) { dw += x; dx = -x; x = 0; } + if (y < 0) { dh += y; dy = -y; y = 0; } + + if ((x + w) >= _width ) dw = _width - x; + if ((y + h) >= _height) dh = _height - y; + + if (dw < 0 || dh < 0) return; + + spi_begin(); + + setAddrWindow(x, y, x + dw - 1, y + dh - 1); // Sets CS low and sent RAMWR + + data += dx + dy * w; + +#ifdef ESP8266 + uint32_t spimask = ~((SPIMMOSI << SPILMOSI) | (SPIMMISO << SPILMISO)); + SPI1U1 = (SPI1U1 & spimask) | (15 << SPILMOSI) | (15 << SPILMISO); +#endif + + while (dh--) + { + uint32_t len = dw; + uint8_t* ptr = data; + + while(len--) + { + uint16_t color = *ptr++; + color = (color & 0xE0)<<8 | (color & 0xC0)<<5 + | (color & 0x1C)<<6 | (color & 0x1C)<<3 + | (color & 0x03)<<3 | (color & 0x03)<<1 | (color & 0x03)>>1; + +#ifdef ESP32 + SPI.write16(color); +#else + color = (color<<8) | (color>>8); + SPI1W0 = color; + SPI1CMD |= SPIBUSY; + while(SPI1CMD & SPIBUSY) {} +#endif + } + data += w; + } CS_H; @@ -4108,8 +4163,8 @@ void TFT_eSPI_Button::press(boolean p) { currstate = p; } -boolean TFT_eSPI_Button::isPressed() { return currstate; } -boolean TFT_eSPI_Button::justPressed() { return (currstate && !laststate); } +boolean TFT_eSPI_Button::isPressed() { return currstate; } +boolean TFT_eSPI_Button::justPressed() { return (currstate && !laststate); } boolean TFT_eSPI_Button::justReleased() { return (!currstate && laststate); } @@ -4132,8 +4187,11 @@ TFT_eSprite::TFT_eSprite(TFT_eSPI *tft) _iwidth = 0; // Initialise width and height to 0 (it does not exist yet) _iheight = 0; + _bpp16 = true; - _xs = 0; // window bounds for pushColor + _created = false; + + _xs = 0; // window bounds for pushColor _ys = 0; _xe = 0; _ye = 0; @@ -4149,13 +4207,30 @@ TFT_eSprite::TFT_eSprite(TFT_eSPI *tft) ** Function name: createSprite ** Description: Create a sprite (bitmap) of defined width and height *************************************************************************************x*/ -uint16_t* TFT_eSprite::createSprite(int16_t w, int16_t h) +void TFT_eSprite::createSprite(int16_t w, int16_t h) { - if ( w < 1 || h < 1) return 0; + if ( w < 1 || h < 1) return; + _iwidth = w; _iheight = h; - _img = (uint16_t*) malloc(w * h * sizeof(uint16_t)); - return _img; + + if(_bpp16) _img = (uint16_t*) malloc(w * h * 2); + else _img8 = ( uint8_t*) malloc(w * h); + + _created = true; +} + + +/*************************************************************************************** +** Function name: setDepth +** Description: Set bits per pixel for colour (8 or 16) +*************************************************************************************x*/ + +void TFT_eSprite::setColorDepth(int8_t b) +{ + if (_created) deleteSprite(); + if ( b > 8 ) _bpp16 = true; // Bytes per pixel + else _bpp16 = false; } @@ -4165,7 +4240,10 @@ uint16_t* TFT_eSprite::createSprite(int16_t w, int16_t h) *************************************************************************************x*/ void TFT_eSprite::deleteSprite(void) { - free(_img); + if (!_created ) return; + if (_bpp16) free(_img); + else free(_img8); + _created = false; } @@ -4175,18 +4253,30 @@ void TFT_eSprite::deleteSprite(void) *************************************************************************************x*/ void TFT_eSprite::pushSprite(int32_t x, int32_t y) { - _tft->pushSprite(x, y, _iwidth, _iheight, _img); + if (_bpp16) _tft->pushSprite(x, y, _iwidth, _iheight, _img ); + else _tft->pushSprite(x, y, _iwidth, _iheight, _img8); } /*************************************************************************************** ** Function name: readPixel -** Description: Read 565 colour of a pixel at deined coordinates +** Description: Read 565 colour of a pixel at defined coordinates *************************************************************************************x*/ uint16_t TFT_eSprite::readPixel(int32_t x, int32_t y) { - uint16_t color = _img[x + y * _iwidth]; - return (color >> 8) | (color << 8); + if (_bpp16) + { + uint16_t color = _img[x + y * _iwidth]; + return (color >> 8) | (color << 8); + } + + uint16_t color = _img8[x + y * _iwidth]; + if (color != 0) + color = (color & 0xE0)<<8 | (color & 0xC0)<<5 + | (color & 0x1C)<<6 | (color & 0x1C)<<3 + | (color & 0x03)<<3 | (color & 0x03)<<1 | (color & 0x03)>>1; + + return color; } @@ -4198,11 +4288,25 @@ void TFT_eSprite::pushRect(uint32_t x, uint32_t y, uint32_t w, uint32_t h, uint { if ((x > _iwidth) || (y > _iheight) || (w == 0) || (h == 0)) return; - for (uint32_t yp = y; yp < y + h; yp++) + if (_bpp16) { - for (uint32_t xp = x; xp < x + w; xp++) + for (uint32_t yp = y; yp < y + h; yp++) { - _img[xp + yp * _iwidth] = *data++; + for (uint32_t xp = x; xp < x + w; xp++) + { + _img[xp + yp * _iwidth] = *data++; + } + } + } + else + { + for (uint32_t yp = y; yp < y + h; yp++) + { + for (uint32_t xp = x; xp < x + w; xp++) + { + uint16_t color = *data++; + _img8[xp + yp * _iwidth] = (uint8_t)((color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3); + } } } } @@ -4214,28 +4318,29 @@ void TFT_eSprite::pushRect(uint32_t x, uint32_t y, uint32_t w, uint32_t h, uint ***************************************************************************************/ void TFT_eSprite::pushBitmap(uint32_t x, uint32_t y, uint32_t w, uint32_t h, uint16_t *data) { - if ((x > _iwidth) || (y > _iheight) || (w == 0) || (h == 0)) return; - - for (uint32_t yp = y; yp < y + h; yp++) - { - for (uint32_t xp = x; xp < x + w; xp++) - { - _img[xp + yp * _iwidth] = *data++; - } - } + pushRect(x, y, w, h, data); } /*************************************************************************************** -** Function name: spriteWindow -** Description: Set the bounds of a window for pushColor +** Function name: setWindow +** Description: Set the bounds of a window for pushColor and writeColor *************************************************************************************x*/ void TFT_eSprite::setWindow(int32_t x0, int32_t y0, int32_t x1, int32_t y1) { - // Bounds will be checked by drawPixel if (x0 > x1) swap_coord(x0, x1); if (y0 > y1) swap_coord(y0, y1); + if (x0 < 0) x0 = 0; + if (x0 >= _iwidth) x0 = _iwidth; + if (x1 < 0) x1 = 0; + if (x1 >= _iwidth) x1 = _iwidth; + + if (y0 < 0) y0 = 0; + if (y0 >= _iheight) y0 = _iheight; + if (y1 < 0) y1 = 0; + if (y1 >= _iheight) y1 = _iheight; + _xs = x0; _ys = y0; _xe = x1; @@ -4248,20 +4353,69 @@ void TFT_eSprite::setWindow(int32_t x0, int32_t y0, int32_t x1, int32_t y1) /*************************************************************************************** ** Function name: pushColor -** Description: Send a new pixel to the sprite window +** Description: Send a new pixel to the set window *************************************************************************************x*/ void TFT_eSprite::pushColor(uint32_t color) { - drawPixel(_xptr++, _yptr, color); - if (_xptr > _xe) { _xptr = _xs; _yptr++; } - if (_yptr > _ye) { _yptr = _ys; } + + // Write the colour to RAM in set window + if (_bpp16) + _img [_xptr + _yptr * _iwidth] = (uint16_t) (color >> 8) | (color << 8); + else + _img8[_xptr + _yptr * _iwidth] = (uint8_t )((color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3); + + // Increment x + _xptr++; + + // Wrap on x and y to start, increment y if needed + if (_xptr > _xe) + { + _xptr = _xs; + _yptr++; + if (_yptr > _ye) _yptr = _ys; + } + } + +/*************************************************************************************** +** Function name: pushColor +** Description: Send a "len" new pixels to the set window +*************************************************************************************x*/ void TFT_eSprite::pushColor(uint32_t color, uint16_t len) { - drawPixel(_xptr++, _yptr, color); - if (_xptr > _xe) { _xptr = _xs; _yptr++; } - if (_yptr > _ye) { _yptr = _ys; } + uint16_t pixelColor; + if (_bpp16) + pixelColor = (uint16_t) (color >> 8) | (color << 8); + else + pixelColor = (color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3; + + while(len--) writeColor(pixelColor); +} + + +/*************************************************************************************** +** Function name: writeColor +** Description: Write a pixel with pre-formatted colour to the set window +*************************************************************************************x*/ +void TFT_eSprite::writeColor(uint16_t color) +{ + // Write 16 bit RGB 565 encoded colour to RAM + if (_bpp16) _img [_xptr + _yptr * _iwidth] = color; + + // Write 8 bit RGB 332 encoded colour to RAM + else _img8[_xptr + _yptr * _iwidth] = (uint8_t) color; + + // Increment x + _xptr++; + + // Wrap on x and y to start, increment y if needed + if (_xptr > _xe) + { + _xptr = _xs; + _yptr++; + if (_yptr > _ye) _yptr = _ys; + } } @@ -4272,7 +4426,10 @@ void TFT_eSprite::pushColor(uint32_t color, uint16_t len) void TFT_eSprite::fillSprite(uint32_t color) { // Use memset if possible as it is super fast - if((uint8_t)color == (color>>8)) memset(_img, (uint8_t)color, _iwidth * _iheight * 2); + if(( (uint8_t)color == (uint8_t)(color>>8) ) && _bpp16) + memset(_img, (uint8_t)color, _iwidth * _iheight * 2); + else if (!_bpp16) memset(_img8, (uint8_t)color, _iwidth * _iheight); + else fillRect(0, 0, _iwidth, _iheight, color); } @@ -4289,7 +4446,7 @@ void TFT_eSprite::setCursor(int16_t x, int16_t y) /*************************************************************************************** -** Function name: width or spriteWidth +** Function name: width ** Description: Return the width of sprite *************************************************************************************x*/ // Return the size of the display @@ -4298,16 +4455,9 @@ int16_t TFT_eSprite::width(void) return _iwidth; } -/* -// Return the size of the display -int16_t TFT_eSprite::spriteWidth(void) -{ - return _iwidth; -} -*/ /*************************************************************************************** -** Function name: height or spriteHeight +** Function name: height ** Description: Return the height of sprite *************************************************************************************x*/ int16_t TFT_eSprite::height(void) @@ -4315,12 +4465,6 @@ int16_t TFT_eSprite::height(void) return _iheight; } -/* -int16_t TFT_eSprite::spriteHeight(void) -{ - return _iheight; -} -*/ /*************************************************************************************** ** Function name: drawChar @@ -4471,8 +4615,16 @@ void TFT_eSprite::drawPixel(uint32_t x, uint32_t y, uint32_t color) // x and y are unsigned so that -ve coordinates turn into large positive ones // this make bounds checking a bit faster if ((x >= _iwidth) || (y >= _iheight)) return; - color = (color >> 8) | (color << 8); - _img[x+y*_iwidth] = (uint16_t) color; + + if (_bpp16) + { + color = (color >> 8) | (color << 8); + _img[x+y*_iwidth] = (uint16_t) color; + } + else + { + _img8[x+y*_iwidth] = (uint8_t)((color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3); + } } @@ -4541,8 +4693,17 @@ void TFT_eSprite::drawFastVLine(int32_t x, int32_t y, int32_t h, uint32_t color) if ((y + h) > _iheight) h = _iheight - y; if (h < 1) return; - color = (color >> 8) | (color << 8); - while (h--) _img[x + _iwidth * y++] = (uint16_t) color; + + if (_bpp16) + { + color = (color >> 8) | (color << 8); + while (h--) _img[x + _iwidth * y++] = (uint16_t) color; + } + else + { + color = (color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3; + while (h--) _img8[x + _iwidth * y++] = (uint8_t) color; + } } @@ -4557,8 +4718,18 @@ void TFT_eSprite::drawFastHLine(int32_t x, int32_t y, int32_t w, uint32_t color) if ((x + w) > _iwidth) w = _iwidth - x; if (w < 1) return; - color = (color >> 8) | (color << 8); - while (w--) _img[_iwidth * y + x++] = (uint16_t) color; + + if (_bpp16) + { + color = (color >> 8) | (color << 8); + while (w--) _img[_iwidth * y + x++] = (uint16_t) color; + } + else + { + color = (color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3; + //while (w--) _img8[_iwidth * y + x++] = (uint8_t) color; + memset(_img8+_iwidth * y + x, (uint8_t)color, w); + } } @@ -4574,12 +4745,27 @@ void TFT_eSprite::fillRect(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t if ((x + w) > _iwidth) w = _iwidth - x; if ((y + h) > _iheight) h = _iheight - y; if ((w < 1) || (h < 1)) return; - color = (color >> 8) | (color << 8); - while (h--) { + if (_bpp16) + { + color = (color >> 8) | (color << 8); + while (h--) + { int32_t ix = x, iw = w; while (iw--) _img[_iwidth * y + ix++] = (uint16_t) color; y++; + } + } + else + { + color = (color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3; + while (h--) + { + //int32_t ix = x, iw = w; + //while (iw--) _img8[_iwidth * y + ix++] = (uint8_t) color; + memset(_img8 + _iwidth * y + x, (uint8_t)color, w); + y++; + } } } @@ -4708,7 +4894,7 @@ size_t TFT_eSprite::write(uint8_t utf8) *************************************************************************************x*/ int16_t TFT_eSprite::drawChar(unsigned int uniCode, int x, int y) { - return drawChar(uniCode, x, y, textfont); + return drawChar(uniCode, x, y, textfont); } int16_t TFT_eSprite::drawChar(unsigned int uniCode, int x, int y, int font) @@ -4842,11 +5028,13 @@ int16_t TFT_eSprite::drawChar(unsigned int uniCode, int x, int y, int font) w *= height; // Now w is total number of pixels in the character if (textcolor != textbgcolor) fillRect(x, pY, width * textsize, textsize * height, textbgcolor); + int16_t color; + if (_bpp16) color = (textcolor >> 8) | (textcolor << 8); + else color = ((textcolor & 0xE000)>>8 | (textcolor & 0x0700)>>6 | (textcolor & 0x0018)>>3); int px = 0, py = pY; // To hold character block start and end column and row values int pc = 0; // Pixel count byte np = textsize * textsize; // Number of pixels in a drawn pixel - byte tnp = 0; // Temporary copy of np for while loop byte ts = textsize - 1; // Temporary copy of textsize // 16 bit pixel count so maximum font size is equivalent to 180x180 pixels in area // w is total number of pixels to plot to fill character block @@ -4869,15 +5057,9 @@ int16_t TFT_eSprite::drawChar(unsigned int uniCode, int x, int y, int font) pc++; setWindow(px, py, px + ts, py + ts); - if (ts) { - tnp = np; - while (tnp--) { - pushColor(textcolor); - } - } - else { - pushColor(textcolor); - } + if (ts) while (np--) writeColor(color); + else writeColor(color); + px += textsize; if (px >= (x + width * textsize)) diff --git a/TFT_eSPI.h b/TFT_eSPI.h index ffb1c23..3042561 100644 --- a/TFT_eSPI.h +++ b/TFT_eSPI.h @@ -344,9 +344,6 @@ class TFT_eSPI : public Print { height(void), width(void); - virtual size_t write(uint8_t); - - // The TFT_eSprite class inherits the following functions void pushColors(uint16_t *data, uint8_t len), pushColors(uint8_t *data, uint32_t len), @@ -408,7 +405,7 @@ class TFT_eSPI : public Print { // Write a block of pixels to the screen void pushRect(uint32_t x0, uint32_t y0, uint32_t w, uint32_t h, uint16_t *data); void pushSprite(int32_t x0, int32_t y0, uint32_t w, uint32_t h, uint16_t *data); - + void pushSprite(int32_t x0, int32_t y0, uint32_t w, uint32_t h, uint8_t *data); // This next function has been used successfully to dump the TFT screen to a PC for documentation purposes // It reads a screen area and returns the RGB 8 bit colour values of each pixel // Set w and h to 1 to read 1 pixel's colour. The data buffer must be at least w * h * 3 bytes @@ -453,6 +450,7 @@ class TFT_eSPI : public Print { void calibrateTouch(uint16_t *data, uint32_t color_fg, uint32_t color_bg, uint8_t size); void setTouch(uint16_t *data); + size_t write(uint8_t); private: @@ -556,9 +554,12 @@ class TFT_eSprite : public TFT_eSPI { TFT_eSprite(TFT_eSPI *tft); - uint16_t* createSprite(int16_t w, int16_t y); // 16 bpp + void createSprite(int16_t w, int16_t y); // 2 bytes per pixel + void deleteSprite(void); + void setColorDepth(int8_t b); + void drawPixel(uint32_t x, uint32_t y, uint32_t color); void drawChar(int32_t x, int32_t y, unsigned char c, uint32_t color, uint32_t bg, uint8_t size), @@ -568,6 +569,7 @@ class TFT_eSprite : public TFT_eSPI { setWindow(int32_t x0, int32_t y0, int32_t x1, int32_t y1), pushColor(uint32_t color), pushColor(uint32_t color, uint16_t len), + writeColor(uint16_t color), drawLine(int32_t x0, int32_t y0, int32_t x1, int32_t y1, uint32_t color), drawFastVLine(int32_t x, int32_t y, int32_t h, uint32_t color), @@ -601,10 +603,12 @@ class TFT_eSprite : public TFT_eSPI { protected: uint16_t *_img; + uint8_t *_img8; + bool _created; int32_t _icursor_x, _icursor_y, _xs, _ys, _xe, _ye, _xptr, _yptr; - int32_t _iwidth, _iheight; // Display w/h as modified by current rotation + uint32_t _iwidth, _iheight, _bpp16; }; diff --git a/examples/Sprite/Sprite_scroll_text/Sprite_scroll_text.ino b/examples/Sprite/Sprite_scroll_16bit/Sprite_scroll_16bit.ino similarity index 100% rename from examples/Sprite/Sprite_scroll_text/Sprite_scroll_text.ino rename to examples/Sprite/Sprite_scroll_16bit/Sprite_scroll_16bit.ino diff --git a/examples/Sprite/Sprite_scroll_8bit/Sprite_scroll_8bit.ino b/examples/Sprite/Sprite_scroll_8bit/Sprite_scroll_8bit.ino new file mode 100644 index 0000000..beca732 --- /dev/null +++ b/examples/Sprite/Sprite_scroll_8bit/Sprite_scroll_8bit.ino @@ -0,0 +1,205 @@ +/* + Display "flicker free" scrolling text and updating number + + This sketch uses 8 bit colour sprites to save RAM. + + Example for library: + https://github.com/Bodmer/TFT_eSPI + + The sketch has been tested on a 320x240 ILI9341 based TFT, it + coule be adapted for other screen sizes. + + A Sprite is notionally an invisible graphics screen that is + kept in the processors RAM. Graphics can be drawn into the + Sprite just as it can be drawn directly to the screen. Once + the Sprite is completed it can be plotted onto the screen in + any position. If there is sufficient RAM then the Sprite can + be the same size as the screen and used as a frame buffer. + + A 16 bit colour Sprite occupies (2 * width * height) bytes. + + An 8 bit colour Sprite occupies (width * height) bytes. + + On a ESP8266, 16 bit Sprite sizes up to 128 x 160 can be accomodated, + this size requires 128*160*2 bytes (40kBytes) of RAM. + + This sketch sets the colour depth to 8 bits so larger sprites can be + created. 8 bit colour sprites use half amount of RAM. If the colour + depth is not specified then 16 bits is assumed. + + You need to make the sprite small enough to fit, with RAM spare for + any "local variables" that may be needed by your sketch and libraries. + + Created by Bodmer 21/11/17 + + ######################################################################### + ###### DON'T FORGET TO UPDATE THE User_Setup.h FILE IN THE LIBRARY ###### + ######################################################################### +*/ + +// Size of sprite image for the scrolling text, this requires ~14 Kbytes of RAM +#define IWIDTH 240 +#define IHEIGHT 30 + +// Pause in milliseconds to set scroll speed +#define WAIT 0 + +#include // Include the graphics library (this includes the sprite functions) + +TFT_eSPI tft = TFT_eSPI(); // Create object "tft" + +TFT_eSprite img = TFT_eSprite(&tft); // Create Sprite object "img" with pointer to "tft" object +// // the pointer is used by pushSprite() to push it onto the TFT + +// ------------------------------------------------------------------------- +// Setup +// ------------------------------------------------------------------------- +void setup(void) { + tft.init(); + tft.setRotation(0); + + tft.fillScreen(TFT_BLUE); +} + +// ------------------------------------------------------------------------- +// Main loop +// ------------------------------------------------------------------------- +void loop() { + + while (1) + { + // Set colour depth of Sprite to 8 (or 16) bits + img.setColorDepth(8); + + // Create the sprite and clear background to black + img.createSprite(IWIDTH, IHEIGHT); + //img.fillSprite(TFT_BLACK); // Optional here as we fill the sprite later anyway + + for (int pos = IWIDTH; pos > 0; pos--) + { + build_banner("Hello World", pos); + img.pushSprite(0, 0); + + build_banner("TFT_eSPI sprite" , pos); + img.pushSprite(0, 50); + + delay(WAIT); + } + + // Delete sprite to free up the memory + img.deleteSprite(); + + // Create a sprite of a different size + numberBox(random(100), 60, 100); + + } +} + +// ######################################################################### +// Build the scrolling sprite image from scratch, draw text at x = xpos +// ######################################################################### + +void build_banner(String msg, int xpos) +{ + int h = IHEIGHT; + + // We could just use fillSprite(color) but lets be a bit more creative... + + // Fill with rainbow stripes + while (h--) img.drawFastHLine(0, h, IWIDTH, rainbow(h * 4)); + + // Draw some graphics, the text will apear to scroll over these + img.fillRect (IWIDTH / 2 - 20, IHEIGHT / 2 - 10, 40, 20, TFT_YELLOW); + img.fillCircle(IWIDTH / 2, IHEIGHT / 2, 10, TFT_ORANGE); + + // Now print text on top of the graphics + img.setTextSize(1); // Font size scaling is x1 + img.setTextFont(4); // Font 4 selected + img.setTextColor(TFT_BLACK); // Black text, no background colour + img.setTextWrap(false); // Turn of wrap so we can print past end of sprite + + // Need to print twice so text appears to wrap around at left and right edges + img.setCursor(xpos, 2); // Print text at xpos + img.print(msg); + + img.setCursor(xpos - IWIDTH, 2); // Print text at xpos - sprite width + img.print(msg); +} + +// ######################################################################### +// Create sprite, plot graphics in it, plot to screen, then delete sprite +// ######################################################################### +void numberBox(int num, int x, int y) +{ + // Create a sprite 80 pixels wide, 50 high (8kbytes of RAM needed) + img.createSprite(80, 50); + + // Fill it with black + img.fillSprite(TFT_BLACK); + + // Draw a backgorund of 2 filled triangles + img.fillTriangle( 0, 0, 0, 49, 40, 25, TFT_RED); + img.fillTriangle( 79, 0, 79, 49, 40, 25, TFT_DARKGREEN); + + // Set the font parameters + img.setTextSize(1); // Font size scaling is x1 + img.setFreeFont(&FreeSerifBoldItalic24pt7b); // Select free font + img.setTextColor(TFT_WHITE); // White text, no background colour + + // Set text coordinate datum to middle centre + img.setTextDatum(MC_DATUM); + + // Draw the number in middle of 80 x 50 sprite + img.drawNumber(num, 40, 25); + + // Push sprite to TFT screen CGRAM at coordinate x,y (top left corner) + img.pushSprite(x, y); + + // Delete sprite to free up the RAM + img.deleteSprite(); +} + + +// ######################################################################### +// Return a 16 bit rainbow colour +// ######################################################################### +unsigned int rainbow(byte value) +{ + // Value is expected to be in range 0-127 + // The value is converted to a spectrum colour from 0 = red through to 127 = blue + + byte red = 0; // Red is the top 5 bits of a 16 bit colour value + byte green = 0;// Green is the middle 6 bits + byte blue = 0; // Blue is the bottom 5 bits + + byte sector = value >> 5; + byte amplit = value & 0x1F; + + switch (sector) + { + case 0: + red = 0x1F; + green = amplit; + blue = 0; + break; + case 1: + red = 0x1F - amplit; + green = 0x1F; + blue = 0; + break; + case 2: + red = 0; + green = 0x1F; + blue = amplit; + break; + case 3: + red = 0; + green = 0x1F - amplit; + blue = 0x1F; + break; + } + + return red << 11 | green << 6 | blue; +} + + From 3b5e673603bf3016dc848e50b472659cc96c3279 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Wed, 22 Nov 2017 00:53:09 +0000 Subject: [PATCH 026/150] Version update, minor tweaks and typos --- TFT_eSPI.cpp | 14 ++++++++-- .../Sprite_RLE_Font_test.ino | 26 +++++++++---------- .../Sprite_drawPixel/Sprite_drawPixel.ino | 13 +++++++--- .../Sprite_scroll_16bit.ino | 2 +- library.json | 2 +- library.properties | 2 +- 6 files changed, 37 insertions(+), 22 deletions(-) diff --git a/TFT_eSPI.cpp b/TFT_eSPI.cpp index d84b966..e0cdf53 100644 --- a/TFT_eSPI.cpp +++ b/TFT_eSPI.cpp @@ -4228,9 +4228,19 @@ void TFT_eSprite::createSprite(int16_t w, int16_t h) void TFT_eSprite::setColorDepth(int8_t b) { - if (_created) deleteSprite(); + // Can't change an existing sprite's colour depth so delete it + if (_created) + { + if (_bpp16) free(_img); + else free(_img8); + } + + // Now define the new colour depth if ( b > 8 ) _bpp16 = true; // Bytes per pixel else _bpp16 = false; + + // If it existed, re-create the sprite with the new colour depth + if (_created) createSprite(_iwidth, _iheight); } @@ -4724,7 +4734,7 @@ void TFT_eSprite::drawFastHLine(int32_t x, int32_t y, int32_t w, uint32_t color) color = (color >> 8) | (color << 8); while (w--) _img[_iwidth * y + x++] = (uint16_t) color; } - else + else { color = (color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3; //while (w--) _img8[_iwidth * y + x++] = (uint8_t) color; diff --git a/examples/Sprite/Sprite_RLE_Font_test/Sprite_RLE_Font_test.ino b/examples/Sprite/Sprite_RLE_Font_test/Sprite_RLE_Font_test.ino index 32c22b3..8a8d519 100644 --- a/examples/Sprite/Sprite_RLE_Font_test/Sprite_RLE_Font_test.ino +++ b/examples/Sprite/Sprite_RLE_Font_test/Sprite_RLE_Font_test.ino @@ -1,5 +1,5 @@ /* - Display all the fast rendering fonts. + Display all the fast rendering fonts in a sprite Make sure all the display driver and pin comnections are correct by editting the User_Setup.h file in the TFT_eSPI library folder. @@ -9,13 +9,10 @@ ######################################################################### */ -// Create a sprite 160 x 128 pixels (needs 40Kbytes of RAM!) +// Specify sprite 160 x 128 pixels (needs 40Kbytes of RAM for 16 bit colour) #define IWIDTH 160 #define IHEIGHT 128 -// New background colour -#define TFT_BROWN 0x38E0 - // Pause in milliseconds between screens, change to 0 to time font rendering #define WAIT 500 @@ -34,6 +31,7 @@ void setup(void) { tft.fillScreen(TFT_BLUE); + //img.setColorDepth(8); // Optionally set depth to 8 to reduce RAM use img.createSprite(IWIDTH, IHEIGHT); img.fillSprite(TFT_BLACK); } @@ -125,7 +123,7 @@ void loop() { targetTime = millis(); img.setTextSize(1); - img.fillSprite(TFT_BROWN); + img.fillSprite(TFT_BLACK); img.setTextColor(TFT_GREEN); img.drawString(" !\"#$%&'()*+,-./0123456", 0, 0, 2); @@ -137,7 +135,7 @@ void loop() { img.drawChar(127, xpos, 64, 2); img.pushSprite(0, 0); delay(WAIT); - img.fillSprite(TFT_BROWN); + img.fillSprite(TFT_BLACK); img.setTextColor(TFT_GREEN); img.drawString(" !\"#$%&'()*+,-.", 0, 0, 4); @@ -147,7 +145,7 @@ void loop() { img.drawString("PQRSTUVWX", 0, 104, 4); img.pushSprite(0, 0); delay(WAIT); - img.fillSprite(TFT_BROWN); + img.fillSprite(TFT_BLACK); img.drawString("YZ[\\]^_`abc", 0, 0, 4); img.drawString("defghijklmno", 0, 26, 4); img.drawString("pqrstuvwxyz", 0, 52, 4); @@ -156,7 +154,7 @@ void loop() { img.drawChar(127, xpos, 78, 4); img.pushSprite(0, 0); delay(WAIT); - img.fillSprite(TFT_BROWN); + img.fillSprite(TFT_BLACK); img.setTextColor(TFT_BLUE); img.drawString("012345", 0, 0, 6); @@ -164,29 +162,29 @@ void loop() { img.drawString("apm-:.", 0, 80, 6); img.pushSprite(0, 0); delay(WAIT); - img.fillSprite(TFT_BROWN); + img.fillSprite(TFT_BLACK); img.setTextColor(TFT_RED); img.drawString("0123", 0, 0, 7); img.drawString("4567", 0, 60, 7); img.pushSprite(0, 0); delay(WAIT); - img.fillSprite(TFT_BROWN); + img.fillSprite(TFT_BLACK); img.drawString("890:.", 0, 0, 7); img.drawString("", 0, 60, 7); img.pushSprite(0, 0); delay(WAIT); - img.fillSprite(TFT_BROWN); + img.fillSprite(TFT_BLACK); img.setTextColor(TFT_YELLOW); img.drawString("0123", 0, 0, 8); img.pushSprite(0, 0); delay(WAIT); - img.fillSprite(TFT_BROWN); + img.fillSprite(TFT_BLACK); img.drawString("4567", 0, 0, 8); img.pushSprite(0, 0); delay(WAIT); - img.fillSprite(TFT_BROWN); + img.fillSprite(TFT_BLACK); img.drawString("890:.", 0, 0, 8); img.pushSprite(0, 0); delay(WAIT); diff --git a/examples/Sprite/Sprite_drawPixel/Sprite_drawPixel.ino b/examples/Sprite/Sprite_drawPixel/Sprite_drawPixel.ino index 8dc295e..78e02f2 100644 --- a/examples/Sprite/Sprite_drawPixel/Sprite_drawPixel.ino +++ b/examples/Sprite/Sprite_drawPixel/Sprite_drawPixel.ino @@ -7,17 +7,21 @@ Example for library: https://github.com/Bodmer/TFT_eSPI - A Sprite is notionally an invisibly graphics screen that is + A Sprite is notionally an invisible graphics screen that is kept in the processors RAM. Graphics can be drawn into the Sprite just as it can be drawn directly to the screen. Once the Sprite is completed it can be plotted onto the screen in any position. If there is sufficient RAM then the Sprite can be the same size as the screen and used as a frame buffer. - The Sprite occupies (2 * width * height) bytes in RAM. + A 16 bit Sprite occupies (2 * width * height) bytes in RAM. On a ESP8266 Sprite sizes up to 126 x 160 can be accomodated, - this size requires 40kBytes of RAM. + this size requires 40kBytes of RAM for a 16 bit colour depth. + + When 8 bit colour depth sprites are created they occupy + (width * height) bytes in RAM, so larger sprites can be + created, or the RAM required is halved. */ @@ -42,6 +46,9 @@ void setup() // Initialise the TFT registers tft.init(); + // Optionally set colour depth to 8 or 16 bits, default is 16 if not spedified + // spr.setColorDepth(8); + // Create a sprite of defined size spr.createSprite(WIDTH, HEIGHT); diff --git a/examples/Sprite/Sprite_scroll_16bit/Sprite_scroll_16bit.ino b/examples/Sprite/Sprite_scroll_16bit/Sprite_scroll_16bit.ino index ce8cc1b..9d2c81b 100644 --- a/examples/Sprite/Sprite_scroll_16bit/Sprite_scroll_16bit.ino +++ b/examples/Sprite/Sprite_scroll_16bit/Sprite_scroll_16bit.ino @@ -7,7 +7,7 @@ The sketch has been tested on a 320x240 ILI9341 based TFT, it coule be adapted for other screen sizes. - A Sprite is notionally an invisibly graphics screen that is + A Sprite is notionally an invisible graphics screen that is kept in the processors RAM. Graphics can be drawn into the Sprite just as it can be drawn directly to the screen. Once the Sprite is completed it can be plotted onto the screen in diff --git a/library.json b/library.json index 8f2ba01..272da16 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "TFT_eSPI", - "version": "0.16.16", + "version": "0.16.20", "keywords": "TFT, ESP8266, NodeMCU, ESP32, ILI9341, ST7735, ILI9163, S6D02A1, ILI9486", "description": "A TFT SPI graphics library for ESP8266", "repository": diff --git a/library.properties b/library.properties index e0ba602..e6d2e96 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=TFT_eSPI -version=0.16.16 +version=0.16.20 author=Bodmer maintainer=Bodmer sentence=A fast TFT library for ESP8266 processors and the Arduino IDE From 898718c872902646dfa0e27ecda83a6f46073d60 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Wed, 22 Nov 2017 00:59:32 +0000 Subject: [PATCH 027/150] Update ReadMe Add 8 bit colour depth. --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 3165854..7e7dea9 100644 --- a/README.md +++ b/README.md @@ -4,11 +4,13 @@ An Arduino IDE compatible graphics and fonts library for ESP8266 and ESP32 proce The library also supports TFT displays designed for the Raspberry Pi that are based on a ILI9486 driver chip with a 480 x 320 pixel screen. This display must be of the Waveshare design and use a 16 bit serial interface based on the 74HC04, 74HC4040 and 2 x 74HC4094 logic chips. A modification to these displays is possible (see mod image in Tools folder) to make many graphics functions much faster (e.g. 23ms to clear the screen, 1.2ms to draw a 72 pixel high numeral). -A new "Sprite" class has been added, this enables flicker free updates of complex graphics. Exmaples are in the "examples/Sprite" folder. A Sprite is notionally an invisible graphics screen that is kept in the processors RAM. Graphics can be drawn into the Sprite just as it can be drawn directly to the screen. Once the Sprite is completed it can be plotted onto the screen in any position. If there is sufficient RAM then the Sprite can be the same size as the screen and used as a frame buffer. On an ESP8266 the largest Sprite that can be created is about 160x128 pixels, this consumes 40Kbytes of RAM. On an ESP32 the workspace RAM is more limited than the datsheet implies so the Sprite is limited to about 200x200 pixels (~80Kbytes). The RAM needed is 2 x width x height of sprite. Drawing graphics into a sprite is very fast, for those familiar with the Adafruit "graphicstest" example, this whole test completes in less than 27ms in a 160x128 sprite. +A new "Sprite" class has been added, this enables flicker free updates of complex graphics. Exmaples are in the "examples/Sprite" folder. A Sprite is notionally an invisible graphics screen that is kept in the processors RAM. Graphics can be drawn into the Sprite just as it can be drawn directly to the screen. Once the Sprite is completed it can be plotted onto the screen in any position. If there is sufficient RAM then the Sprite can be the same size as the screen and used as a frame buffer. On an ESP8266 the largest 16 bit colour Sprite that can be created is about 160x128 pixels, this consumes 40Kbytes of RAM. On an ESP32 the workspace RAM is more limited than the datsheet implies so the Sprite is limited to about 200x200 pixels (~80Kbytes). + +The RAM needed for a 16 bit colour depth Sprite is (2 x width x height) bytes, for a Sprite with 8 bit colour depth the RAM needed is (width x height) bytes . Drawing graphics into a sprite is very fast, for those familiar with the Adafruit "graphicstest" example, this whole test completes in less than 27ms in a 160x128 sprite. The XPT2046 touch screen controller is supported. The SPI bus for the touch controller is shared with the TFT and only an additional chip select line is needed. -The Button class from Adafruit_GFX has been added, with the enhancement that the button labels can be in any font. +The Button class from Adafruit_GFX is incorporated, with the enhancement that the button labels can be in any font. The library supports SPI overlap on the ESP8266 so the TFT screen can share MOSI, MISO and SCLK pins with the program FLASH. From 3e31f162fe69ab9db79a5abd5506c51d10032130 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Wed, 22 Nov 2017 01:00:37 +0000 Subject: [PATCH 028/150] Typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7e7dea9..59eae77 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ An Arduino IDE compatible graphics and fonts library for ESP8266 and ESP32 proce The library also supports TFT displays designed for the Raspberry Pi that are based on a ILI9486 driver chip with a 480 x 320 pixel screen. This display must be of the Waveshare design and use a 16 bit serial interface based on the 74HC04, 74HC4040 and 2 x 74HC4094 logic chips. A modification to these displays is possible (see mod image in Tools folder) to make many graphics functions much faster (e.g. 23ms to clear the screen, 1.2ms to draw a 72 pixel high numeral). -A new "Sprite" class has been added, this enables flicker free updates of complex graphics. Exmaples are in the "examples/Sprite" folder. A Sprite is notionally an invisible graphics screen that is kept in the processors RAM. Graphics can be drawn into the Sprite just as it can be drawn directly to the screen. Once the Sprite is completed it can be plotted onto the screen in any position. If there is sufficient RAM then the Sprite can be the same size as the screen and used as a frame buffer. On an ESP8266 the largest 16 bit colour Sprite that can be created is about 160x128 pixels, this consumes 40Kbytes of RAM. On an ESP32 the workspace RAM is more limited than the datsheet implies so the Sprite is limited to about 200x200 pixels (~80Kbytes). +A new "Sprite" class has been added, this enables flicker free updates of complex graphics. Examples are in the "examples/Sprite" folder. A Sprite is notionally an invisible graphics screen that is kept in the processors RAM. Graphics can be drawn into the Sprite just as it can be drawn directly to the screen. Once the Sprite is completed it can be plotted onto the screen in any position. If there is sufficient RAM then the Sprite can be the same size as the screen and used as a frame buffer. On an ESP8266 the largest 16 bit colour Sprite that can be created is about 160x128 pixels, this consumes 40Kbytes of RAM. On an ESP32 the workspace RAM is more limited than the datsheet implies so the Sprite is limited to about 200x200 pixels (~80Kbytes). The RAM needed for a 16 bit colour depth Sprite is (2 x width x height) bytes, for a Sprite with 8 bit colour depth the RAM needed is (width x height) bytes . Drawing graphics into a sprite is very fast, for those familiar with the Adafruit "graphicstest" example, this whole test completes in less than 27ms in a 160x128 sprite. From 312b7c1128837861b834511157eed0a4e31b4de1 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Fri, 24 Nov 2017 14:12:29 +0000 Subject: [PATCH 029/150] Tidy up 8 bit sprite code and fix gugs Minor tweaks and speed improvements --- README.md | 8 +- README.txt | 6 +- TFT_eSPI.cpp | 104 +++++++++++------- TFT_eSPI.h | 4 +- .../TFT_SPIFFS_Jpeg/JPEG_functions.ino | 23 +++- .../Sprite_RLE_Font_test.ino | 2 +- library.json | 2 +- library.properties | 2 +- 8 files changed, 101 insertions(+), 50 deletions(-) diff --git a/README.md b/README.md index 59eae77..61895de 100644 --- a/README.md +++ b/README.md @@ -4,9 +4,13 @@ An Arduino IDE compatible graphics and fonts library for ESP8266 and ESP32 proce The library also supports TFT displays designed for the Raspberry Pi that are based on a ILI9486 driver chip with a 480 x 320 pixel screen. This display must be of the Waveshare design and use a 16 bit serial interface based on the 74HC04, 74HC4040 and 2 x 74HC4094 logic chips. A modification to these displays is possible (see mod image in Tools folder) to make many graphics functions much faster (e.g. 23ms to clear the screen, 1.2ms to draw a 72 pixel high numeral). -A new "Sprite" class has been added, this enables flicker free updates of complex graphics. Examples are in the "examples/Sprite" folder. A Sprite is notionally an invisible graphics screen that is kept in the processors RAM. Graphics can be drawn into the Sprite just as it can be drawn directly to the screen. Once the Sprite is completed it can be plotted onto the screen in any position. If there is sufficient RAM then the Sprite can be the same size as the screen and used as a frame buffer. On an ESP8266 the largest 16 bit colour Sprite that can be created is about 160x128 pixels, this consumes 40Kbytes of RAM. On an ESP32 the workspace RAM is more limited than the datsheet implies so the Sprite is limited to about 200x200 pixels (~80Kbytes). +A new "Sprite" class has been added, this enables flicker free updates of complex graphics. Direct writes to the TFT with graphics functions are still available, so existing sketches do not need to be changed. -The RAM needed for a 16 bit colour depth Sprite is (2 x width x height) bytes, for a Sprite with 8 bit colour depth the RAM needed is (width x height) bytes . Drawing graphics into a sprite is very fast, for those familiar with the Adafruit "graphicstest" example, this whole test completes in less than 27ms in a 160x128 sprite. +A Sprite is notionally an invisible graphics screen that is kept in the processors RAM. Graphics can be drawn into the Sprite just as they can be drawn directly to the screen. Once the Sprite is completed it can be plotted onto the screen in any position. If there is sufficient RAM then the Sprite can be the same size as the screen and used as a frame buffer. Sprites by default use 16 bit colours, the bit depth can be set to 8 bits to reduce the RAM needed. On an ESP8266 the largest 16 bit colour Sprite that can be created is about 160x128 pixels, this consumes 40Kbytes of RAM. On an ESP32 the workspace RAM is more limited than the datsheet implies so the Sprite is limited to about 200x200 pixels (~80Kbytes). + +One or more sprites can be created, a sprite can be any width and height, limited only by available RAM. The RAM needed for a 16 bit colour depth Sprite is (2 x width x height) bytes, for a Sprite with 8 bit colour depth the RAM needed is (width x height) bytes. Sprites can be created and deleted as needed in the sketch, this means RAM can be freed up after the sprite has been plotted on the screen, more RAM intensive WiFi based code can then be run and normal graphics operations still work. + +Drawing graphics into a sprite is very fast, for those familiar with the Adafruit "graphicstest" example, this whole test completes in less than 27ms in a 160x128 sprite. Examples of sprite use can be found in the "examples/Sprite" folder. The XPT2046 touch screen controller is supported. The SPI bus for the touch controller is shared with the TFT and only an additional chip select line is needed. diff --git a/README.txt b/README.txt index db56bbe..8262ce5 100644 --- a/README.txt +++ b/README.txt @@ -10,5 +10,9 @@ older. Use the latest version. New functions have been added in particular it contains proportional fonts in addition to the original Adafruit font. -Note: This version of the library might not be fully compatible with the original. +A sprite class has been added to aid the generation of flicker free complex +raphics. + +Note: This version of the library might not be fully compatible with the +original. diff --git a/TFT_eSPI.cpp b/TFT_eSPI.cpp index e0cdf53..3047e58 100644 --- a/TFT_eSPI.cpp +++ b/TFT_eSPI.cpp @@ -520,8 +520,12 @@ uint16_t TFT_eSPI::readPixel(int32_t x0, int32_t y0) uint32_t len = w * h * 2; - // Push pixels into window rectangle, data is a 16 bit pointer thus increment is halved - while ( len >=32 ) {SPI.writeBytes((uint8_t*)data, 32); data += 16; len -= 32; } + // Check alignment of pointer to 32 bits + uint8_t offset = (uint32_t)data & 0x3; + if (offset > len) offset = len; + + // Make pointer 32 bit align using immune call then use the faster writeBytes() + if (offset) { SPI.writePattern((uint8_t*)data, offset, 1); len -= offset; data += offset; } if (len) SPI.writeBytes((uint8_t*)data, len); CS_H; @@ -531,8 +535,8 @@ uint16_t TFT_eSPI::readPixel(int32_t x0, int32_t y0) /*************************************************************************************** -** Function name: push rectangle (for SPI Interface II i.e. IM [3:0] = "1101") -** Description: push 565 pixel colours into a defined area +** Function name: push sprite +** Description: plot 16 bit sprite in a defined area with clipping ***************************************************************************************/ void TFT_eSPI::pushSprite(int32_t x, int32_t y, uint32_t w, uint32_t h, uint16_t *data) { @@ -550,7 +554,7 @@ uint16_t TFT_eSPI::readPixel(int32_t x0, int32_t y0) if ((x + w) >= _width ) dw = _width - x; if ((y + h) >= _height) dh = _height - y; - if (dw < 0 || dh < 0) return; + if (dw < 1 || dh < 1) return; spi_begin(); @@ -558,21 +562,21 @@ uint16_t TFT_eSPI::readPixel(int32_t x0, int32_t y0) data += dx + dy * w; + // Check alignment of pointer to 32 bits + uint8_t offset = (uint32_t)data & 0x3; + dw <<= 1; + if (offset > dw) offset = dw; + while (dh--) { - /* - uint32_t len = dw; - uint16_t* ptr = data; - // Push pixels into window rectangle, data is a 16 bit pointer thus increment is halved - while ( len--) SPI.write16(*ptr++, 0); - data += w; - */ - uint32_t len = dw * 2; + int32_t len = dw; uint8_t* ptr = (uint8_t*)data; - // Push pixels into window rectangle, data is a 16 bit pointer thus increment is halved - while ( len>32) { SPI.writePattern(ptr, 32, 1); len -= 32; ptr += 32; } - if (len) SPI.writePattern((uint8_t*)ptr, len, 1); + // Make pointer 32 bit align using imune call then use the faster writeBytes() + if (offset) { SPI.writePattern(ptr, offset, 1); len -= offset; ptr += offset; } + + if (len) SPI.writeBytes(ptr, len); + data += w; } @@ -581,9 +585,13 @@ uint16_t TFT_eSPI::readPixel(int32_t x0, int32_t y0) spi_end(); } + +/*************************************************************************************** +** Function name: push sprite +** Description: plot 8 bit sprite with clipping using a line buffer +***************************************************************************************/ void TFT_eSPI::pushSprite(int32_t x, int32_t y, uint32_t w, uint32_t h, uint8_t *data) { - if ((x >= (int32_t)_width) || (y >= (int32_t)_height)) return; int32_t dx = 0; @@ -597,7 +605,7 @@ uint16_t TFT_eSPI::readPixel(int32_t x0, int32_t y0) if ((x + w) >= _width ) dw = _width - x; if ((y + h) >= _height) dh = _height - y; - if (dw < 0 || dh < 0) return; + if (dw < 1 || dh < 1) return; spi_begin(); @@ -605,33 +613,41 @@ uint16_t TFT_eSPI::readPixel(int32_t x0, int32_t y0) data += dx + dy * w; -#ifdef ESP8266 - uint32_t spimask = ~((SPIMMOSI << SPILMOSI) | (SPIMMISO << SPILMISO)); - SPI1U1 = (SPI1U1 & spimask) | (15 << SPILMOSI) | (15 << SPILMISO); -#endif + // Line buffer makes plotting faster, pointer is aligned to 32 bits + uint32_t lineBuf[1+(dw>>1)]; + + uint8_t blue[] = {0, 11, 21, 31}; // blue 2 to 5 bit colour lookup table + + _lastColor = -1; // Set to illegal value + + // Used to store last shifted colour + uint8_t msbColor = 0; + uint8_t lsbColor = 0; while (dh--) { uint32_t len = dw; uint8_t* ptr = data; + uint8_t* linePtr = (uint8_t*)lineBuf; while(len--) { - uint16_t color = *ptr++; - color = (color & 0xE0)<<8 | (color & 0xC0)<<5 - | (color & 0x1C)<<6 | (color & 0x1C)<<3 - | (color & 0x03)<<3 | (color & 0x03)<<1 | (color & 0x03)>>1; - -#ifdef ESP32 - SPI.write16(color); -#else - color = (color<<8) | (color>>8); - SPI1W0 = color; - SPI1CMD |= SPIBUSY; - while(SPI1CMD & SPIBUSY) {} -#endif - + uint32_t color = *ptr++; + // Shifts are slow so check if colour has changed first + if (color != _lastColor) { + // =====Green===== ===============Red============== + msbColor = (color & 0x1C)>>2 | (color & 0xC0)>>3 | (color & 0xE0); + // =====Green===== =======Blue====== + lsbColor = (color & 0x1C)<<3 | blue[color & 0x03]; + _lastColor = color; + } + *linePtr++ = msbColor; + *linePtr++ = lsbColor; } + + // lineBuf is always 32 bit aligned so can use writeBytes! + if (dw) SPI.writeBytes((uint8_t*)lineBuf, dw<<1); + data += w; } @@ -4282,9 +4298,10 @@ uint16_t TFT_eSprite::readPixel(int32_t x, int32_t y) uint16_t color = _img8[x + y * _iwidth]; if (color != 0) + uint8_t blue[] = {0, 11, 21, 31}; color = (color & 0xE0)<<8 | (color & 0xC0)<<5 | (color & 0x1C)<<6 | (color & 0x1C)<<3 - | (color & 0x03)<<3 | (color & 0x03)<<1 | (color & 0x03)>>1; + | blue[color & 0x03]; return color; } @@ -4482,8 +4499,8 @@ int16_t TFT_eSprite::height(void) *************************************************************************************x*/ void TFT_eSprite::drawChar(int32_t x, int32_t y, unsigned char c, uint32_t color, uint32_t bg, uint8_t size) { - if ((x >= (int16_t)_iwidth) || // Clip right - (y >= (int16_t)_iheight) || // Clip bottom + if ((x >= _iwidth) || // Clip right + (y >= _iheight) || // Clip bottom ((x + 6 * size - 1) < 0) || // Clip left ((y + 8 * size - 1) < 0)) // Clip top return; @@ -4698,8 +4715,11 @@ void TFT_eSprite::drawLine(int32_t x0, int32_t y0, int32_t x1, int32_t y1, uint3 *************************************************************************************x*/ void TFT_eSprite::drawFastVLine(int32_t x, int32_t y, int32_t h, uint32_t color) { + if ((x < 0) || (x >= _iwidth) || (y >= _iheight)) return; + if (y < 0) { h += y; y = 0; } + if ((y + h) > _iheight) h = _iheight - y; if (h < 1) return; @@ -4723,8 +4743,11 @@ void TFT_eSprite::drawFastVLine(int32_t x, int32_t y, int32_t h, uint32_t color) *************************************************************************************x*/ void TFT_eSprite::drawFastHLine(int32_t x, int32_t y, int32_t w, uint32_t color) { + if ((y < 0) || (x >= _iwidth) || (y >= _iheight)) return; + if (x < 0) { w += x; x = 0; } + if ((x + w) > _iwidth) w = _iwidth - x; if (w < 1) return; @@ -4737,7 +4760,6 @@ void TFT_eSprite::drawFastHLine(int32_t x, int32_t y, int32_t w, uint32_t color) else { color = (color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3; - //while (w--) _img8[_iwidth * y + x++] = (uint8_t) color; memset(_img8+_iwidth * y + x, (uint8_t)color, w); } } @@ -4989,7 +5011,7 @@ int16_t TFT_eSprite::drawChar(unsigned int uniCode, int x, int y, int font) if (font == 2) { w = w + 6; // Should be + 7 but we need to compensate for width increment w = w / 8; - if (x + width * textsize >= (int16_t)_iwidth) return width * textsize ; + if (x + width * textsize >= _iwidth) return width * textsize ; for (int i = 0; i < height; i++) { diff --git a/TFT_eSPI.h b/TFT_eSPI.h index 3042561..91f364e 100644 --- a/TFT_eSPI.h +++ b/TFT_eSPI.h @@ -494,6 +494,8 @@ class TFT_eSPI : public Print { boolean locked, inTransaction; // Transaction and mutex lock flags for ESP32 + int32_t _lastColor; + #ifdef LOAD_GFXFF GFXfont *gfxFont; @@ -608,7 +610,7 @@ class TFT_eSprite : public TFT_eSPI { int32_t _icursor_x, _icursor_y, _xs, _ys, _xe, _ye, _xptr, _yptr; - uint32_t _iwidth, _iheight, _bpp16; + int32_t _iwidth, _iheight, _bpp16; }; diff --git a/examples/160 x 128/TFT_SPIFFS_Jpeg/JPEG_functions.ino b/examples/160 x 128/TFT_SPIFFS_Jpeg/JPEG_functions.ino index ce4dd80..313a2f6 100644 --- a/examples/160 x 128/TFT_SPIFFS_Jpeg/JPEG_functions.ino +++ b/examples/160 x 128/TFT_SPIFFS_Jpeg/JPEG_functions.ino @@ -79,15 +79,34 @@ void jpegRender(int xpos, int ypos) { pImg = JpegDec.pImage; // calculate where the image block should be drawn on the screen - int mcu_x = JpegDec.MCUx * mcu_w + xpos; + int mcu_x = JpegDec.MCUx * mcu_w + xpos; // Calculate coordinates of top left corner of current MCU int mcu_y = JpegDec.MCUy * mcu_h + ypos; - // check if the image block size needs to be changed for the right and bottom edges + // check if the image block size needs to be changed for the right edge if (mcu_x + mcu_w <= max_x) win_w = mcu_w; else win_w = min_w; + + // check if the image block size needs to be changed for the bottom edge if (mcu_y + mcu_h <= max_y) win_h = mcu_h; else win_h = min_h; + // copy pixels into a contiguous block + if (win_w != mcu_w) + { + uint16_t *cImg; + int p = 0; + cImg = pImg + win_w; + for (int h = 1; h < win_h; h++) + { + p += mcu_w; + for (int w = 0; w < win_w; w++) + { + *cImg = *(pImg + w + p); + cImg++; + } + } + } + // draw image MCU block only if it will fit on the screen if ( ( mcu_x + win_w) <= tft.width() && ( mcu_y + win_h) <= tft.height()) { diff --git a/examples/Sprite/Sprite_RLE_Font_test/Sprite_RLE_Font_test.ino b/examples/Sprite/Sprite_RLE_Font_test/Sprite_RLE_Font_test.ino index 8a8d519..407c53f 100644 --- a/examples/Sprite/Sprite_RLE_Font_test/Sprite_RLE_Font_test.ino +++ b/examples/Sprite/Sprite_RLE_Font_test/Sprite_RLE_Font_test.ino @@ -31,7 +31,7 @@ void setup(void) { tft.fillScreen(TFT_BLUE); - //img.setColorDepth(8); // Optionally set depth to 8 to reduce RAM use + //img.setColorDepth(8); // Optionally set depth to 8 to halve RAM use img.createSprite(IWIDTH, IHEIGHT); img.fillSprite(TFT_BLACK); } diff --git a/library.json b/library.json index 272da16..f2654c5 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "TFT_eSPI", - "version": "0.16.20", + "version": "0.17.10", "keywords": "TFT, ESP8266, NodeMCU, ESP32, ILI9341, ST7735, ILI9163, S6D02A1, ILI9486", "description": "A TFT SPI graphics library for ESP8266", "repository": diff --git a/library.properties b/library.properties index e6d2e96..f288915 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=TFT_eSPI -version=0.16.20 +version=0.17.10 author=Bodmer maintainer=Bodmer sentence=A fast TFT library for ESP8266 processors and the Arduino IDE From 449c42191549ad30cbcac50bc9c266a28b2bfbbf Mon Sep 17 00:00:00 2001 From: Bodmer Date: Fri, 24 Nov 2017 14:26:27 +0000 Subject: [PATCH 030/150] Clarification on sizes --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 61895de..53c9222 100644 --- a/README.md +++ b/README.md @@ -6,9 +6,9 @@ The library also supports TFT displays designed for the Raspberry Pi that are ba A new "Sprite" class has been added, this enables flicker free updates of complex graphics. Direct writes to the TFT with graphics functions are still available, so existing sketches do not need to be changed. -A Sprite is notionally an invisible graphics screen that is kept in the processors RAM. Graphics can be drawn into the Sprite just as they can be drawn directly to the screen. Once the Sprite is completed it can be plotted onto the screen in any position. If there is sufficient RAM then the Sprite can be the same size as the screen and used as a frame buffer. Sprites by default use 16 bit colours, the bit depth can be set to 8 bits to reduce the RAM needed. On an ESP8266 the largest 16 bit colour Sprite that can be created is about 160x128 pixels, this consumes 40Kbytes of RAM. On an ESP32 the workspace RAM is more limited than the datsheet implies so the Sprite is limited to about 200x200 pixels (~80Kbytes). +A Sprite is notionally an invisible graphics screen that is kept in the processors RAM. Graphics can be drawn into the Sprite just as they can be drawn directly to the screen. Once the Sprite is completed it can be plotted onto the screen in any position. If there is sufficient RAM then the Sprite can be the same size as the screen and used as a frame buffer. Sprites by default use 16 bit colours, the bit depth can be set to 8 bits to reduce the RAM needed. On an ESP8266 the largest 16 bit colour Sprite that can be created is about 160x128 pixels, this consumes 40Kbytes of RAM. On an ESP32 the workspace RAM is more limited than the datsheet implies so a 16 bit colour Sprite is limited to about 200x200 pixels (~80Kbytes) and an 8 bit sprite to 320x240 pixels (~76kbytes). -One or more sprites can be created, a sprite can be any width and height, limited only by available RAM. The RAM needed for a 16 bit colour depth Sprite is (2 x width x height) bytes, for a Sprite with 8 bit colour depth the RAM needed is (width x height) bytes. Sprites can be created and deleted as needed in the sketch, this means RAM can be freed up after the sprite has been plotted on the screen, more RAM intensive WiFi based code can then be run and normal graphics operations still work. +One or more sprites can be created, a sprite can be any width and height, limited only by available RAM. The RAM needed for a 16 bit colour depth Sprite is (2 x width x height) bytes, for a Sprite with 8 bit colour depth the RAM needed is (width x height) bytes. Sprites can be created and deleted dynamically as needed in the sketch, this means RAM can be freed up after the sprite has been plotted on the screen, more RAM intensive WiFi based code can then be run and normal graphics operations still work. Drawing graphics into a sprite is very fast, for those familiar with the Adafruit "graphicstest" example, this whole test completes in less than 27ms in a 160x128 sprite. Examples of sprite use can be found in the "examples/Sprite" folder. From 218dd1b06e0d9d134d4cf0181f563e1a20d7cd22 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Fri, 24 Nov 2017 15:03:24 +0000 Subject: [PATCH 031/150] Bug fix Should have tested that last minute edit! --- TFT_eSPI.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/TFT_eSPI.cpp b/TFT_eSPI.cpp index 3047e58..6955efe 100644 --- a/TFT_eSPI.cpp +++ b/TFT_eSPI.cpp @@ -4297,11 +4297,12 @@ uint16_t TFT_eSprite::readPixel(int32_t x, int32_t y) } uint16_t color = _img8[x + y * _iwidth]; - if (color != 0) + if (color != 0) { uint8_t blue[] = {0, 11, 21, 31}; color = (color & 0xE0)<<8 | (color & 0xC0)<<5 | (color & 0x1C)<<6 | (color & 0x1C)<<3 | blue[color & 0x03]; + } return color; } From e2d019b65613d9dd6798edf7eae49ee83c96f7c4 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Fri, 24 Nov 2017 15:04:15 +0000 Subject: [PATCH 032/150] Update version number --- library.json | 2 +- library.properties | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/library.json b/library.json index f2654c5..35f59b2 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "TFT_eSPI", - "version": "0.17.10", + "version": "0.17.11", "keywords": "TFT, ESP8266, NodeMCU, ESP32, ILI9341, ST7735, ILI9163, S6D02A1, ILI9486", "description": "A TFT SPI graphics library for ESP8266", "repository": diff --git a/library.properties b/library.properties index f288915..9078977 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=TFT_eSPI -version=0.17.10 +version=0.17.11 author=Bodmer maintainer=Bodmer sentence=A fast TFT library for ESP8266 processors and the Arduino IDE From 58fa8326a202f4dbd6efe0b5d0008164b669d2aa Mon Sep 17 00:00:00 2001 From: Bodmer Date: Sun, 26 Nov 2017 12:38:20 +0000 Subject: [PATCH 033/150] Correct uninitialised warning on ESP32 int16_t xo16 = 0, yo16 = 0; --- TFT_eSPI.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/TFT_eSPI.cpp b/TFT_eSPI.cpp index 6955efe..44eb0e0 100644 --- a/TFT_eSPI.cpp +++ b/TFT_eSPI.cpp @@ -1580,7 +1580,7 @@ void TFT_eSPI::drawChar(int32_t x, int32_t y, unsigned char c, uint32_t color, u int8_t xo = pgm_read_byte(&glyph->xOffset), yo = pgm_read_byte(&glyph->yOffset); uint8_t xx, yy, bits, bit=0; - int16_t xo16, yo16; + int16_t xo16 = 0, yo16 = 0; if(size > 1) { xo16 = xo; @@ -4590,7 +4590,7 @@ void TFT_eSprite::drawChar(int32_t x, int32_t y, unsigned char c, uint32_t color int8_t xo = pgm_read_byte(&glyph->xOffset), yo = pgm_read_byte(&glyph->yOffset); uint8_t xx, yy, bits, bit=0; - int16_t xo16, yo16; + int16_t xo16 = 0, yo16 = 0; if(size > 1) { xo16 = xo; From b97324875dd4a7e1a2ff01275a86eaa086957274 Mon Sep 17 00:00:00 2001 From: turiphro Date: Sun, 26 Nov 2017 14:49:28 -0800 Subject: [PATCH 034/150] Fix width/height override issue when using setRotation This commit fixes an issue when setting screen width and height from the constructor (e.g., user sketch), followed by setRotation(i). Previously, setRotation used TFT_WIDTH and TFT_HEIGHT, ignoring user overrides. --- TFT_Drivers/ILI9163_Rotation.h | 16 +++++++------- TFT_Drivers/ILI9341_Rotation.h | 34 +++++++++++++++--------------- TFT_Drivers/RPI_ILI9486_Rotation.h | 34 +++++++++++++++--------------- TFT_Drivers/S6D02A1_Rotation.h | 16 +++++++------- TFT_Drivers/ST7735_Rotation.h | 34 +++++++++++++++--------------- TFT_eSPI.cpp | 4 ++-- TFT_eSPI.h | 1 + 7 files changed, 70 insertions(+), 69 deletions(-) diff --git a/TFT_Drivers/ILI9163_Rotation.h b/TFT_Drivers/ILI9163_Rotation.h index a78a9c4..7232430 100644 --- a/TFT_Drivers/ILI9163_Rotation.h +++ b/TFT_Drivers/ILI9163_Rotation.h @@ -7,8 +7,8 @@ switch (rotation) { case 0: writedata(TFT_MAD_MX | TFT_MAD_MY | TFT_MAD_BGR); - _width = TFT_WIDTH; - _height = TFT_HEIGHT; + _width = _width_orig; + _height = _height_orig; #ifdef CGRAM_OFFSET colstart = 0; rowstart = 0; @@ -16,8 +16,8 @@ break; case 1: writedata(TFT_MAD_MV | TFT_MAD_MY | TFT_MAD_BGR); - _width = TFT_HEIGHT; - _height = TFT_WIDTH; + _width = _height_orig; + _height = _width_orig; #ifdef CGRAM_OFFSET colstart = 0; rowstart = 0; @@ -25,8 +25,8 @@ break; case 2: writedata(TFT_MAD_BGR); - _width = TFT_WIDTH; - _height = TFT_HEIGHT; + _width = _width_orig; + _height = _height_orig; #ifdef CGRAM_OFFSET colstart = 0; rowstart = 32; @@ -34,8 +34,8 @@ break; case 3: writedata(TFT_MAD_MX | TFT_MAD_MV | TFT_MAD_BGR); - _width = TFT_HEIGHT; - _height = TFT_WIDTH; + _width = _height_orig; + _height = _width_orig; #ifdef CGRAM_OFFSET colstart = 32; rowstart = 0; diff --git a/TFT_Drivers/ILI9341_Rotation.h b/TFT_Drivers/ILI9341_Rotation.h index c856dea..0af4fa7 100644 --- a/TFT_Drivers/ILI9341_Rotation.h +++ b/TFT_Drivers/ILI9341_Rotation.h @@ -7,44 +7,44 @@ switch (rotation) { case 0: writedata(TFT_MAD_MX | TFT_MAD_BGR); - _width = TFT_WIDTH; - _height = TFT_HEIGHT; + _width = _width_orig; + _height = _height_orig; break; case 1: writedata(TFT_MAD_MV | TFT_MAD_BGR); - _width = TFT_HEIGHT; - _height = TFT_WIDTH; + _width = _height_orig; + _height = _width_orig; break; case 2: writedata(TFT_MAD_MY | TFT_MAD_BGR); - _width = TFT_WIDTH; - _height = TFT_HEIGHT; + _width = _width_orig; + _height = _height_orig; break; case 3: writedata(TFT_MAD_MX | TFT_MAD_MY | TFT_MAD_MV | TFT_MAD_BGR); - _width = TFT_HEIGHT; - _height = TFT_WIDTH; + _width = _height_orig; + _height = _width_orig; break; // These next rotations are for bottum up BMP drawing case 4: writedata(TFT_MAD_MX | TFT_MAD_MY | TFT_MAD_BGR); - _width = TFT_WIDTH; - _height = TFT_HEIGHT; + _width = _width_orig; + _height = _height_orig; break; case 5: writedata(TFT_MAD_MV | TFT_MAD_MX | TFT_MAD_BGR); - _width = TFT_HEIGHT; - _height = TFT_WIDTH; + _width = _height_orig; + _height = _width_orig; break; case 6: writedata(TFT_MAD_BGR); - _width = TFT_WIDTH; - _height = TFT_HEIGHT; + _width = _width_orig; + _height = _height_orig; break; case 7: writedata(TFT_MAD_MY | TFT_MAD_MV | TFT_MAD_BGR); - _width = TFT_HEIGHT; - _height = TFT_WIDTH; + _width = _height_orig; + _height = _width_orig; break; - } \ No newline at end of file + } diff --git a/TFT_Drivers/RPI_ILI9486_Rotation.h b/TFT_Drivers/RPI_ILI9486_Rotation.h index cd6b0f7..53afc2a 100644 --- a/TFT_Drivers/RPI_ILI9486_Rotation.h +++ b/TFT_Drivers/RPI_ILI9486_Rotation.h @@ -5,43 +5,43 @@ switch (rotation) { case 0: // Portrait writedata(TFT_MAD_BGR | TFT_MAD_MX); - _width = TFT_WIDTH; - _height = TFT_HEIGHT; + _width = _width_orig; + _height = _height_orig; break; case 1: // Landscape (Portrait + 90) writedata(TFT_MAD_BGR | TFT_MAD_MV); - _width = TFT_HEIGHT; - _height = TFT_WIDTH; + _width = _height_orig; + _height = _width_orig; break; case 2: // Inverter portrait writedata( TFT_MAD_BGR | TFT_MAD_MY); - _width = TFT_WIDTH; - _height = TFT_HEIGHT; + _width = _width_orig; + _height = _height_orig; break; case 3: // Inverted landscape writedata(TFT_MAD_BGR | TFT_MAD_MV | TFT_MAD_MX | TFT_MAD_MY); - _width = TFT_HEIGHT; - _height = TFT_WIDTH; + _width = _height_orig; + _height = _width_orig; break; case 4: // Portrait writedata(TFT_MAD_BGR | TFT_MAD_MX | TFT_MAD_MY); - _width = TFT_WIDTH; - _height = TFT_HEIGHT; + _width = _width_orig; + _height = _height_orig; break; case 5: // Landscape (Portrait + 90) writedata(TFT_MAD_BGR | TFT_MAD_MV | TFT_MAD_MX); - _width = TFT_HEIGHT; - _height = TFT_WIDTH; + _width = _height_orig; + _height = _width_orig; break; case 6: // Inverter portrait writedata( TFT_MAD_BGR); - _width = TFT_WIDTH; - _height = TFT_HEIGHT; + _width = _width_orig; + _height = _height_orig; break; case 7: // Inverted landscape writedata(TFT_MAD_BGR | TFT_MAD_MV | TFT_MAD_MY); - _width = TFT_HEIGHT; - _height = TFT_WIDTH; + _width = _height_orig; + _height = _width_orig; break; } - \ No newline at end of file + diff --git a/TFT_Drivers/S6D02A1_Rotation.h b/TFT_Drivers/S6D02A1_Rotation.h index a3a2981..dfa6cdb 100644 --- a/TFT_Drivers/S6D02A1_Rotation.h +++ b/TFT_Drivers/S6D02A1_Rotation.h @@ -7,22 +7,22 @@ switch (rotation) { case 0: writedata(TFT_MAD_MX | TFT_MAD_MY | TFT_MAD_BGR); - _width = TFT_WIDTH; - _height = TFT_HEIGHT; + _width = _width_orig; + _height = _height_orig; break; case 1: writedata(TFT_MAD_MV | TFT_MAD_MY | TFT_MAD_BGR); - _width = TFT_HEIGHT; - _height = TFT_WIDTH; + _width = _height_orig; + _height = _width_orig; break; case 2: writedata(TFT_MAD_BGR); - _width = TFT_WIDTH; - _height = TFT_HEIGHT; + _width = _width_orig; + _height = _height_orig; break; case 3: writedata(TFT_MAD_MX | TFT_MAD_MV | TFT_MAD_BGR); - _width = TFT_HEIGHT; - _height = TFT_WIDTH; + _width = _height_orig; + _height = _width_orig; break; } diff --git a/TFT_Drivers/ST7735_Rotation.h b/TFT_Drivers/ST7735_Rotation.h index 6b38560..6113886 100644 --- a/TFT_Drivers/ST7735_Rotation.h +++ b/TFT_Drivers/ST7735_Rotation.h @@ -23,8 +23,8 @@ } else { writedata(TFT_MAD_MX | TFT_MAD_MY | TFT_MAD_BGR); } - _width = TFT_WIDTH; - _height = TFT_HEIGHT; + _width = _width_orig; + _height = _height_orig; break; case 1: if (tabcolor == INITR_BLACKTAB) { @@ -44,8 +44,8 @@ } else { writedata(TFT_MAD_MY | TFT_MAD_MV | TFT_MAD_BGR); } - _width = TFT_HEIGHT; - _height = TFT_WIDTH; + _width = _height_orig; + _height = _width_orig; break; case 2: if (tabcolor == INITR_BLACKTAB) { @@ -65,8 +65,8 @@ } else { writedata(TFT_MAD_BGR); } - _width = TFT_WIDTH; - _height = TFT_HEIGHT; + _width = _width_orig; + _height = _height_orig; break; case 3: if (tabcolor == INITR_BLACKTAB) { @@ -86,30 +86,30 @@ } else { writedata(TFT_MAD_MX | TFT_MAD_MV | TFT_MAD_BGR); } - _width = TFT_HEIGHT; - _height = TFT_WIDTH; + _width = _height_orig; + _height = _width_orig; break; // These next rotations are for bottum up BMP drawing /* case 4: writedata(ST7735_TFT_MAD_MX | ST7735_TFT_MAD_MY | ST7735_TFT_MAD_BGR); - _width = TFT_WIDTH; - _height = TFT_HEIGHT; + _width = _width_orig; + _height = _height_orig; break; case 5: writedata(ST7735_TFT_MAD_MV | ST7735_TFT_MAD_MX | ST7735_TFT_MAD_BGR); - _width = TFT_HEIGHT; - _height = TFT_WIDTH; + _width = _height_orig; + _height = _width_orig; break; case 6: writedata(ST7735_TFT_MAD_BGR); - _width = TFT_WIDTH; - _height = TFT_HEIGHT; + _width = _width_orig; + _height = _height_orig; break; case 7: writedata(ST7735_TFT_MAD_MY | ST7735_TFT_MAD_MV | ST7735_TFT_MAD_BGR); - _width = TFT_HEIGHT; - _height = TFT_WIDTH; + _width = _height_orig; + _height = _width_orig; break; */ - } \ No newline at end of file + } diff --git a/TFT_eSPI.cpp b/TFT_eSPI.cpp index 44eb0e0..4db20cf 100644 --- a/TFT_eSPI.cpp +++ b/TFT_eSPI.cpp @@ -109,8 +109,8 @@ TFT_eSPI::TFT_eSPI(int16_t w, int16_t h) } #endif - _width = w; // Set by specific xxxxx_Defines.h file or by users sketch - _height = h; // Set by specific xxxxx_Defines.h file or by users sketch + _width_orig = _width = w; // Set by specific xxxxx_Defines.h file or by users sketch + _height_orig = _height = h; // Set by specific xxxxx_Defines.h file or by users sketch rotation = 0; cursor_y = cursor_x = 0; textfont = 1; diff --git a/TFT_eSPI.h b/TFT_eSPI.h index 91f364e..d38bf6e 100644 --- a/TFT_eSPI.h +++ b/TFT_eSPI.h @@ -480,6 +480,7 @@ class TFT_eSPI : public Print { int32_t cursor_x, cursor_y, win_xe, win_ye, padX; + uint32_t _width_orig, _height_orig; // Display w/h as input, used by setRotation() uint32_t _width, _height; // Display w/h as modified by current rotation uint32_t textcolor, textbgcolor, fontsloaded, addr_row, addr_col; From e18383a29c576555020058c1bcfb5154aa017f3f Mon Sep 17 00:00:00 2001 From: Bodmer Date: Wed, 29 Nov 2017 00:38:05 +0000 Subject: [PATCH 035/150] Bounds check character codes (issue #58) Correct isuue #58 --- TFT_eSPI.cpp | 112 ++++++++++++++++++++++++++++++--------------------- 1 file changed, 66 insertions(+), 46 deletions(-) diff --git a/TFT_eSPI.cpp b/TFT_eSPI.cpp index 4db20cf..f955847 100644 --- a/TFT_eSPI.cpp +++ b/TFT_eSPI.cpp @@ -551,8 +551,8 @@ uint16_t TFT_eSPI::readPixel(int32_t x0, int32_t y0) if (x < 0) { dw += x; dx = -x; x = 0; } if (y < 0) { dh += y; dy = -y; y = 0; } - if ((x + w) >= _width ) dw = _width - x; - if ((y + h) >= _height) dh = _height - y; + if ((x + w) > _width ) dw = _width - x; + if ((y + h) > _height) dh = _height - y; if (dw < 1 || dh < 1) return; @@ -602,8 +602,8 @@ uint16_t TFT_eSPI::readPixel(int32_t x0, int32_t y0) if (x < 0) { dw += x; dx = -x; x = 0; } if (y < 0) { dh += y; dy = -y; y = 0; } - if ((x + w) >= _width ) dw = _width - x; - if ((y + h) >= _height) dh = _height - y; + if ((x + w) > _width ) dw = _width - x; + if ((y + h) > _height) dh = _height - y; if (dw < 1 || dh < 1) return; @@ -1377,7 +1377,7 @@ int16_t TFT_eSPI::textWidth(const char *string) int16_t TFT_eSPI::textWidth(const char *string, int font) { unsigned int str_width = 0; - char uniCode; + unsigned char uniCode; char *widthtable; if (font>1 && font<9) @@ -1387,8 +1387,9 @@ int16_t TFT_eSPI::textWidth(const char *string, int font) while (*string) { uniCode = *(string++); - + if (uniCode > 31 && uniCode < 128) str_width += pgm_read_byte( widthtable + uniCode); // Normally we need to subract 32 from uniCode + else str_width += pgm_read_byte( widthtable + 32); // Set illegal character = space width } } else @@ -1400,7 +1401,8 @@ int16_t TFT_eSPI::textWidth(const char *string, int font) while (*string) { uniCode = *(string++); - if (uniCode > (uint8_t)pgm_read_byte(&gfxFont->last)) uniCode = pgm_read_byte(&gfxFont->first); + if (uniCode < (uint8_t)pgm_read_byte(&gfxFont->first)) uniCode = pgm_read_byte(&gfxFont->first); + if (uniCode > (uint8_t)pgm_read_byte(&gfxFont->last )) uniCode = pgm_read_byte(&gfxFont->first); uniCode -= pgm_read_byte(&gfxFont->first); GFXglyph *glyph = &(((GFXglyph *)pgm_read_dword(&gfxFont->glyph))[uniCode]); // If this is not the last character then use xAdvance @@ -1464,6 +1466,7 @@ void TFT_eSPI::drawChar(int32_t x, int32_t y, unsigned char c, uint32_t color, u ((y + 8 * size - 1) < 0)) // Clip top return; + if (c < 32) return; #ifdef LOAD_GLCD //>>>>>>>>>>>>>>>>>> #ifdef LOAD_GFXFF @@ -1478,6 +1481,7 @@ void TFT_eSPI::drawChar(int32_t x, int32_t y, unsigned char c, uint32_t color, u byte column[6]; byte mask = 0x1; spi_begin(); + //inTransaction = true; setAddrWindow(x, y, x+5, y+8); for (int8_t i = 0; i < 5; i++ ) column[i] = pgm_read_byte(font + (c * 5) + i); column[5] = 0; @@ -1522,6 +1526,7 @@ void TFT_eSPI::drawChar(int32_t x, int32_t y, unsigned char c, uint32_t color, u } #endif CS_H; + //inTransaction = false; spi_end(); } else @@ -1568,7 +1573,8 @@ void TFT_eSPI::drawChar(int32_t x, int32_t y, unsigned char c, uint32_t color, u // Character is assumed previously filtered by write() to eliminate // newlines, returns, non-printable characters, etc. Calling drawChar() // directly with 'bad' characters of font may cause mayhem! - if (c > pgm_read_byte(&gfxFont->last)) c = pgm_read_byte(&gfxFont->first);; + if (c < (uint8_t)pgm_read_byte(&gfxFont->first)) c = pgm_read_byte(&gfxFont->first); + if (c > (uint8_t)pgm_read_byte(&gfxFont->last )) c = pgm_read_byte(&gfxFont->first); c -= pgm_read_byte(&gfxFont->first); GFXglyph *glyph = &(((GFXglyph *)pgm_read_dword(&gfxFont->glyph))[c]); uint8_t *bitmap = (uint8_t *)pgm_read_dword(&gfxFont->bitmap); @@ -2886,6 +2892,7 @@ size_t TFT_eSPI::write(uint8_t utf8) uint8_t uniCode = utf8; // Work with a copy if (utf8 == '\n') uniCode+=22; // Make it a valid space character to stop errors + else if (utf8 < 32) return 0; uint16_t width = 0; uint16_t height = 0; @@ -2905,6 +2912,7 @@ size_t TFT_eSPI::write(uint8_t utf8) #ifdef LOAD_FONT2 if (textfont == 2) { + if (utf8 > 127) return 0; // This is 20us faster than using the fontdata structure (0.443ms per character instead of 0.465ms) width = pgm_read_byte(widtbl_f16 + uniCode-32); height = chr_hgt_f16; @@ -2921,6 +2929,7 @@ size_t TFT_eSPI::write(uint8_t utf8) { if ((textfont>2) && (textfont<9)) { + if (utf8 > 127) return 0; // Uses the fontinfo struct array to avoid lots of 'if' or 'switch' statements // A tad slower than above but this is not significant and is more convenient for the RLE fonts width = pgm_read_byte( (uint8_t *)pgm_read_dword( &(fontdata[textfont].widthtbl ) ) + uniCode-32 ); @@ -2952,6 +2961,7 @@ size_t TFT_eSPI::write(uint8_t utf8) cursor_y += height; cursor_x = 0; } + if (cursor_y >= _height) cursor_y = 0; cursor_x += drawChar(uniCode, cursor_x, cursor_y, textfont); } @@ -2965,28 +2975,27 @@ size_t TFT_eSPI::write(uint8_t utf8) cursor_x = 0; cursor_y += (int16_t)textsize * (uint8_t)pgm_read_byte(&gfxFont->yAdvance); - } else if(uniCode != '\r') { - if (uniCode > (uint8_t)pgm_read_byte(&gfxFont->last)) uniCode = pgm_read_byte(&gfxFont->first); + } else { + if (uniCode > (uint8_t)pgm_read_byte(&gfxFont->last )) return 0; + if (uniCode < (uint8_t)pgm_read_byte(&gfxFont->first)) return 0; - if(uniCode >= pgm_read_byte(&gfxFont->first)) { - uint8_t c2 = uniCode - pgm_read_byte(&gfxFont->first); - GFXglyph *glyph = &(((GFXglyph *)pgm_read_dword(&gfxFont->glyph))[c2]); - uint8_t w = pgm_read_byte(&glyph->width), - h = pgm_read_byte(&glyph->height); - if((w > 0) && (h > 0)) { // Is there an associated bitmap? - int16_t xo = (int8_t)pgm_read_byte(&glyph->xOffset); - if(textwrap && ((cursor_x + textsize * (xo + w)) > _width)) { - // Drawing character would go off right edge; wrap to new line - cursor_x = 0; - cursor_y += (int16_t)textsize * - (uint8_t)pgm_read_byte(&gfxFont->yAdvance); - } - drawChar(cursor_x, cursor_y, uniCode, textcolor, textbgcolor, textsize); + uint8_t c2 = uniCode - pgm_read_byte(&gfxFont->first); + GFXglyph *glyph = &(((GFXglyph *)pgm_read_dword(&gfxFont->glyph))[c2]); + uint8_t w = pgm_read_byte(&glyph->width), + h = pgm_read_byte(&glyph->height); + if((w > 0) && (h > 0)) { // Is there an associated bitmap? + int16_t xo = (int8_t)pgm_read_byte(&glyph->xOffset); + if(textwrap && ((cursor_x + textsize * (xo + w)) > _width)) { + // Drawing character would go off right edge; wrap to new line + cursor_x = 0; + cursor_y += (int16_t)textsize * + (uint8_t)pgm_read_byte(&gfxFont->yAdvance); } - cursor_x += pgm_read_byte(&glyph->xAdvance) * (int16_t)textsize; + if (cursor_y >= _height) cursor_y = 0; + drawChar(cursor_x, cursor_y, uniCode, textcolor, textbgcolor, textsize); } + cursor_x += pgm_read_byte(&glyph->xAdvance) * (int16_t)textsize; } - } #endif // LOAD_GFXFF //<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -3047,6 +3056,8 @@ int16_t TFT_eSPI::drawChar(unsigned int uniCode, int x, int y, int font) #endif } + if ((font>1) && (font<9) && ((uniCode < 32) || (uniCode > 122))) return 0; + int width = 0; int height = 0; uint32_t flash_address = 0; @@ -3392,10 +3403,13 @@ int16_t TFT_eSPI::drawString(const char *string, int poX, int poY, int font) { cheight = (glyph_ab + glyph_bb) * textsize; // Get the offset for the first character only to allow for negative offsets - uint8_t c2 = *string - pgm_read_byte(&gfxFont->first); + uint8_t c2 = *string; + if (c2 < (uint8_t)pgm_read_byte(&gfxFont->first)) c2 = pgm_read_byte(&gfxFont->first); + if (c2 > (uint8_t)pgm_read_byte(&gfxFont->last )) c2 = pgm_read_byte(&gfxFont->first); + c2 -= pgm_read_byte(&gfxFont->first); GFXglyph *glyph = &(((GFXglyph *)pgm_read_dword(&gfxFont->glyph))[c2]); xo = pgm_read_byte(&glyph->xOffset) * textsize; - // Adjust for negative xOffset, also see line 3095 below + // Adjust for negative xOffset //if (xo < 0) cwidth -= xo; // Add 1 pixel of padding all round @@ -4578,7 +4592,8 @@ void TFT_eSprite::drawChar(int32_t x, int32_t y, unsigned char c, uint32_t color // Character is assumed previously filtered by write() to eliminate // newlines, returns, non-printable characters, etc. Calling drawChar() // directly with 'bad' characters of font may cause mayhem! - if (c > pgm_read_byte(&gfxFont->last)) c = pgm_read_byte(&gfxFont->first);; + if (c < (uint8_t)pgm_read_byte(&gfxFont->first)) c = pgm_read_byte(&gfxFont->first); + if (c > (uint8_t)pgm_read_byte(&gfxFont->last )) c = pgm_read_byte(&gfxFont->first); c -= pgm_read_byte(&gfxFont->first); GFXglyph *glyph = &(((GFXglyph *)pgm_read_dword(&gfxFont->glyph))[c]); uint8_t *bitmap = (uint8_t *)pgm_read_dword(&gfxFont->bitmap); @@ -4813,6 +4828,7 @@ size_t TFT_eSprite::write(uint8_t utf8) uint8_t uniCode = utf8; // Work with a copy if (utf8 == '\n') uniCode+=22; // Make it a valid space character to stop errors + else if (utf8 < 32) return 0; uint16_t width = 0; uint16_t height = 0; @@ -4832,6 +4848,7 @@ size_t TFT_eSprite::write(uint8_t utf8) #ifdef LOAD_FONT2 if (textfont == 2) { + if (utf8 > 127) return 0; // This is 20us faster than using the fontdata structure (0.443ms per character instead of 0.465ms) width = pgm_read_byte(widtbl_f16 + uniCode-32); height = chr_hgt_f16; @@ -4848,6 +4865,7 @@ size_t TFT_eSprite::write(uint8_t utf8) { if ((textfont>2) && (textfont<9)) { + if (utf8 > 127) return 0; // Uses the fontinfo struct array to avoid lots of 'if' or 'switch' statements // A tad slower than above but this is not significant and is more convenient for the RLE fonts width = pgm_read_byte( (uint8_t *)pgm_read_dword( &(fontdata[textfont].widthtbl ) ) + uniCode-32 ); @@ -4880,6 +4898,7 @@ size_t TFT_eSprite::write(uint8_t utf8) _icursor_y += height; _icursor_x = 0; } + if (_icursor_y >= _iheight) _icursor_y = 0; _icursor_x += drawChar(uniCode, _icursor_x, _icursor_y, textfont); } @@ -4892,27 +4911,26 @@ size_t TFT_eSprite::write(uint8_t utf8) if(utf8 == '\n') { _icursor_x = 0; _icursor_y += (int16_t)textsize * (uint8_t)pgm_read_byte(&gfxFont->yAdvance); - } else if(uniCode != '\r') { - if (uniCode > (uint8_t)pgm_read_byte(&gfxFont->last)) uniCode = pgm_read_byte(&gfxFont->first); + } else { + if (uniCode > (uint8_t)pgm_read_byte(&gfxFont->last )) return 0; + if (uniCode < (uint8_t)pgm_read_byte(&gfxFont->first)) return 0; - if(uniCode >= pgm_read_byte(&gfxFont->first)) { - uint8_t c2 = uniCode - pgm_read_byte(&gfxFont->first); - GFXglyph *glyph = &(((GFXglyph *)pgm_read_dword(&gfxFont->glyph))[c2]); - uint8_t w = pgm_read_byte(&glyph->width), - h = pgm_read_byte(&glyph->height); - if((w > 0) && (h > 0)) { // Is there an associated bitmap? - int16_t xo = (int8_t)pgm_read_byte(&glyph->xOffset); - if(textwrap && ((_icursor_x + textsize * (xo + w)) > _iwidth)) { - // Drawing character would go off right edge; wrap to new line - _icursor_x = 0; - _icursor_y += (int16_t)textsize * (uint8_t)pgm_read_byte(&gfxFont->yAdvance); - } - drawChar(_icursor_x, _icursor_y, uniCode, textcolor, textbgcolor, textsize); + uint8_t c2 = uniCode - pgm_read_byte(&gfxFont->first); + GFXglyph *glyph = &(((GFXglyph *)pgm_read_dword(&gfxFont->glyph))[c2]); + uint8_t w = pgm_read_byte(&glyph->width), + h = pgm_read_byte(&glyph->height); + if((w > 0) && (h > 0)) { // Is there an associated bitmap? + int16_t xo = (int8_t)pgm_read_byte(&glyph->xOffset); + if(textwrap && ((_icursor_x + textsize * (xo + w)) > _iwidth)) { + // Drawing character would go off right edge; wrap to new line + _icursor_x = 0; + _icursor_y += (int16_t)textsize * (uint8_t)pgm_read_byte(&gfxFont->yAdvance); } - _icursor_x += pgm_read_byte(&glyph->xAdvance) * (int16_t)textsize; + if (_icursor_y >= _iheight) _icursor_y = 0; + drawChar(_icursor_x, _icursor_y, uniCode, textcolor, textbgcolor, textsize); } + _icursor_x += pgm_read_byte(&glyph->xAdvance) * (int16_t)textsize; } - } #endif // LOAD_GFXFF //<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -4973,6 +4991,8 @@ int16_t TFT_eSprite::drawChar(unsigned int uniCode, int x, int y, int font) #endif } + if ((font>1) && (font<9) && ((uniCode < 32) || (uniCode > 122))) return 0; + int width = 0; int height = 0; uint32_t flash_address = 0; From 6fc63061d7e5343443c61487ba7e695de75ca035 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Wed, 29 Nov 2017 02:00:47 +0000 Subject: [PATCH 036/150] Filter bad character codes Do not substitute bad characters for first valid one. --- TFT_eSPI.cpp | 308 +++++++++++++++++++++++++-------------------------- 1 file changed, 152 insertions(+), 156 deletions(-) diff --git a/TFT_eSPI.cpp b/TFT_eSPI.cpp index f955847..f19f1bb 100644 --- a/TFT_eSPI.cpp +++ b/TFT_eSPI.cpp @@ -572,7 +572,7 @@ uint16_t TFT_eSPI::readPixel(int32_t x0, int32_t y0) int32_t len = dw; uint8_t* ptr = (uint8_t*)data; - // Make pointer 32 bit align using imune call then use the faster writeBytes() + // Make pointer 32 bit align using immune call then use the faster writeBytes() if (offset) { SPI.writePattern(ptr, offset, 1); len -= offset; ptr += offset; } if (len) SPI.writeBytes(ptr, len); @@ -1401,14 +1401,15 @@ int16_t TFT_eSPI::textWidth(const char *string, int font) while (*string) { uniCode = *(string++); - if (uniCode < (uint8_t)pgm_read_byte(&gfxFont->first)) uniCode = pgm_read_byte(&gfxFont->first); - if (uniCode > (uint8_t)pgm_read_byte(&gfxFont->last )) uniCode = pgm_read_byte(&gfxFont->first); - uniCode -= pgm_read_byte(&gfxFont->first); - GFXglyph *glyph = &(((GFXglyph *)pgm_read_dword(&gfxFont->glyph))[uniCode]); - // If this is not the last character then use xAdvance - if (*string) str_width += pgm_read_byte(&glyph->xAdvance); - // Else use the offset plus width since this can be bigger than xAdvance - else str_width += ((int8_t)pgm_read_byte(&glyph->xOffset) + pgm_read_byte(&glyph->width)); + if ((uniCode >= (uint8_t)pgm_read_byte(&gfxFont->first)) && (uniCode <= (uint8_t)pgm_read_byte(&gfxFont->last ))) + { + uniCode -= pgm_read_byte(&gfxFont->first); + GFXglyph *glyph = &(((GFXglyph *)pgm_read_dword(&gfxFont->glyph))[uniCode]); + // If this is not the last character then use xAdvance + if (*string) str_width += pgm_read_byte(&glyph->xAdvance); + // Else use the offset plus width since this can be bigger than xAdvance + else str_width += ((int8_t)pgm_read_byte(&glyph->xOffset) + pgm_read_byte(&glyph->width)); + } } } else @@ -1567,31 +1568,30 @@ void TFT_eSPI::drawChar(int32_t x, int32_t y, unsigned char c, uint32_t color, u #endif // LOAD_GLCD #ifdef LOAD_GFXFF - spi_begin(); - inTransaction = true; + // Filter out bad characters not present in font + if ((c >= (uint8_t)pgm_read_byte(&gfxFont->first)) && (c <= (uint8_t)pgm_read_byte(&gfxFont->last ))) + { + spi_begin(); + inTransaction = true; //>>>>>>>>>>>>>>>>>>>>>>>>>>> - // Character is assumed previously filtered by write() to eliminate - // newlines, returns, non-printable characters, etc. Calling drawChar() - // directly with 'bad' characters of font may cause mayhem! - if (c < (uint8_t)pgm_read_byte(&gfxFont->first)) c = pgm_read_byte(&gfxFont->first); - if (c > (uint8_t)pgm_read_byte(&gfxFont->last )) c = pgm_read_byte(&gfxFont->first); - c -= pgm_read_byte(&gfxFont->first); - GFXglyph *glyph = &(((GFXglyph *)pgm_read_dword(&gfxFont->glyph))[c]); - uint8_t *bitmap = (uint8_t *)pgm_read_dword(&gfxFont->bitmap); - uint16_t bo = pgm_read_word(&glyph->bitmapOffset); - uint8_t w = pgm_read_byte(&glyph->width), - h = pgm_read_byte(&glyph->height), - xa = pgm_read_byte(&glyph->xAdvance); - int8_t xo = pgm_read_byte(&glyph->xOffset), - yo = pgm_read_byte(&glyph->yOffset); - uint8_t xx, yy, bits, bit=0; - int16_t xo16 = 0, yo16 = 0; + c -= pgm_read_byte(&gfxFont->first); + GFXglyph *glyph = &(((GFXglyph *)pgm_read_dword(&gfxFont->glyph))[c]); + uint8_t *bitmap = (uint8_t *)pgm_read_dword(&gfxFont->bitmap); - if(size > 1) { - xo16 = xo; - yo16 = yo; - } + uint16_t bo = pgm_read_word(&glyph->bitmapOffset); + uint8_t w = pgm_read_byte(&glyph->width), + h = pgm_read_byte(&glyph->height), + xa = pgm_read_byte(&glyph->xAdvance); + int8_t xo = pgm_read_byte(&glyph->xOffset), + yo = pgm_read_byte(&glyph->yOffset); + uint8_t xx, yy, bits, bit=0; + int16_t xo16 = 0, yo16 = 0; + + if(size > 1) { + xo16 = xo; + yo16 = yo; + } // Here we have 3 versions of the same function just for evaluation purposes // Comment out the next two #defines to revert to the slower Adafruit implementation @@ -1612,90 +1612,91 @@ void TFT_eSPI::drawChar(int32_t x, int32_t y, unsigned char c, uint32_t color, u //FIXED_SIZE is an option in User_Setup.h that only works with FAST_LINE enabled #ifdef FIXED_SIZE - x+=xo; // Save 88 bytes of FLASH - y+=yo; + x+=xo; // Save 88 bytes of FLASH + y+=yo; #endif #ifdef FAST_HLINE #ifdef FAST_SHIFT - uint16_t hpc = 0; // Horizontal foreground pixel count - for(yy=0; yy>= 1; - } - // Draw pixels for this line as we are about to increment yy - if (hpc) { + if(bits & bit) hpc++; + else { + if (hpc) { #ifndef FIXED_SIZE - if(size == 1) drawFastHLine(x+xo+xx-hpc, y+yo+yy, hpc, color); - else fillRect(x+(xo16+xx-hpc)*size, y+(yo16+yy)*size, size*hpc, size, color); + if(size == 1) drawFastHLine(x+xo+xx-hpc, y+yo+yy, hpc, color); + else fillRect(x+(xo16+xx-hpc)*size, y+(yo16+yy)*size, size*hpc, size, color); #else - drawFastHLine(x+xx-hpc, y+yy, hpc, color); + drawFastHLine(x+xx-hpc, y+yy, hpc, color); #endif - hpc=0; + hpc=0; + } + } + bit >>= 1; + } + // Draw pixels for this line as we are about to increment yy + if (hpc) { +#ifndef FIXED_SIZE + if(size == 1) drawFastHLine(x+xo+xx-hpc, y+yo+yy, hpc, color); + else fillRect(x+(xo16+xx-hpc)*size, y+(yo16+yy)*size, size*hpc, size, color); +#else + drawFastHLine(x+xx-hpc, y+yy, hpc, color); +#endif + hpc=0; + } } - } #else - uint16_t hpc = 0; // Horizontal foreground pixel count - for(yy=0; yy pgm_read_byte(&gfxFont->last)) uniCode = pgm_read_byte(&gfxFont->first); - - if(uniCode >= pgm_read_byte(&gfxFont->first)) + if((uniCode >= pgm_read_byte(&gfxFont->first)) && (uniCode <= pgm_read_byte(&gfxFont->last) )) { uint8_t c2 = uniCode - pgm_read_byte(&gfxFont->first); GFXglyph *glyph = &(((GFXglyph *)pgm_read_dword(&gfxFont->glyph))[c2]); @@ -3056,7 +3055,7 @@ int16_t TFT_eSPI::drawChar(unsigned int uniCode, int x, int y, int font) #endif } - if ((font>1) && (font<9) && ((uniCode < 32) || (uniCode > 122))) return 0; + if ((font>1) && (font<9) && ((uniCode < 32) || (uniCode > 127))) return 0; int width = 0; int height = 0; @@ -3404,18 +3403,19 @@ int16_t TFT_eSPI::drawString(const char *string, int poX, int poY, int font) cheight = (glyph_ab + glyph_bb) * textsize; // Get the offset for the first character only to allow for negative offsets uint8_t c2 = *string; - if (c2 < (uint8_t)pgm_read_byte(&gfxFont->first)) c2 = pgm_read_byte(&gfxFont->first); - if (c2 > (uint8_t)pgm_read_byte(&gfxFont->last )) c2 = pgm_read_byte(&gfxFont->first); - c2 -= pgm_read_byte(&gfxFont->first); - GFXglyph *glyph = &(((GFXglyph *)pgm_read_dword(&gfxFont->glyph))[c2]); - xo = pgm_read_byte(&glyph->xOffset) * textsize; - // Adjust for negative xOffset - //if (xo < 0) - cwidth -= xo; - // Add 1 pixel of padding all round - //cheight +=2; - //fillRect(poX+xo-1, poY - 1 - glyph_ab * textsize, cwidth+2, cheight, textbgcolor); - fillRect(poX+xo, poY - glyph_ab * textsize, cwidth, cheight, textbgcolor); + if((c2 >= pgm_read_byte(&gfxFont->first)) && (c2 <= pgm_read_byte(&gfxFont->last) )) + { + c2 -= pgm_read_byte(&gfxFont->first); + GFXglyph *glyph = &(((GFXglyph *)pgm_read_dword(&gfxFont->glyph))[c2]); + xo = pgm_read_byte(&glyph->xOffset) * textsize; + // Adjust for negative xOffset + //if (xo < 0) + cwidth -= xo; + // Add 1 pixel of padding all round + //cheight +=2; + //fillRect(poX+xo-1, poY - 1 - glyph_ab * textsize, cwidth+2, cheight, textbgcolor); + fillRect(poX+xo, poY - glyph_ab * textsize, cwidth, cheight, textbgcolor); + } padding -=100; } #endif @@ -4587,56 +4587,54 @@ void TFT_eSprite::drawChar(int32_t x, int32_t y, unsigned char c, uint32_t color #endif // LOAD_GLCD #ifdef LOAD_GFXFF - + // Filter out bad characters not present in font + if ((c >= (uint8_t)pgm_read_byte(&gfxFont->first)) && (c <= (uint8_t)pgm_read_byte(&gfxFont->last ))) + { //>>>>>>>>>>>>>>>>>>>>>>>>>>> - // Character is assumed previously filtered by write() to eliminate - // newlines, returns, non-printable characters, etc. Calling drawChar() - // directly with 'bad' characters of font may cause mayhem! - if (c < (uint8_t)pgm_read_byte(&gfxFont->first)) c = pgm_read_byte(&gfxFont->first); - if (c > (uint8_t)pgm_read_byte(&gfxFont->last )) c = pgm_read_byte(&gfxFont->first); - c -= pgm_read_byte(&gfxFont->first); - GFXglyph *glyph = &(((GFXglyph *)pgm_read_dword(&gfxFont->glyph))[c]); - uint8_t *bitmap = (uint8_t *)pgm_read_dword(&gfxFont->bitmap); - uint16_t bo = pgm_read_word(&glyph->bitmapOffset); - uint8_t w = pgm_read_byte(&glyph->width), - h = pgm_read_byte(&glyph->height), - xa = pgm_read_byte(&glyph->xAdvance); - int8_t xo = pgm_read_byte(&glyph->xOffset), - yo = pgm_read_byte(&glyph->yOffset); - uint8_t xx, yy, bits, bit=0; - int16_t xo16 = 0, yo16 = 0; + c -= pgm_read_byte(&gfxFont->first); + GFXglyph *glyph = &(((GFXglyph *)pgm_read_dword(&gfxFont->glyph))[c]); + uint8_t *bitmap = (uint8_t *)pgm_read_dword(&gfxFont->bitmap); - if(size > 1) { - xo16 = xo; - yo16 = yo; - } + uint16_t bo = pgm_read_word(&glyph->bitmapOffset); + uint8_t w = pgm_read_byte(&glyph->width), + h = pgm_read_byte(&glyph->height), + xa = pgm_read_byte(&glyph->xAdvance); + int8_t xo = pgm_read_byte(&glyph->xOffset), + yo = pgm_read_byte(&glyph->yOffset); + uint8_t xx, yy, bits, bit=0; + int16_t xo16 = 0, yo16 = 0; - uint16_t hpc = 0; // Horizontal foreground pixel count - for(yy=0; yy 1) { + xo16 = xo; + yo16 = yo; + } + + uint16_t hpc = 0; // Horizontal foreground pixel count + for(yy=0; yy>= 1; + } + // Draw pixels for this line as we are about to increment yy + if (hpc) { + if(size == 1) drawFastHLine(x+xo+xx-hpc, y+yo+yy, hpc, color); + else fillRect(x+(xo16+xx-hpc)*size, y+(yo16+yy)*size, size*hpc, size, color); + hpc=0; } - bit >>= 1; - } - // Draw pixels for this line as we are about to increment yy - if (hpc) { - if(size == 1) drawFastHLine(x+xo+xx-hpc, y+yo+yy, hpc, color); - else fillRect(x+(xo16+xx-hpc)*size, y+(yo16+yy)*size, size*hpc, size, color); - hpc=0; } } - #endif @@ -4975,9 +4973,7 @@ int16_t TFT_eSprite::drawChar(unsigned int uniCode, int x, int y, int font) } else { - if (uniCode > pgm_read_byte(&gfxFont->last)) uniCode = pgm_read_byte(&gfxFont->first); - - if(uniCode >= pgm_read_byte(&gfxFont->first)) + if((uniCode >= pgm_read_byte(&gfxFont->first)) && (uniCode <= pgm_read_byte(&gfxFont->last) )) { uint8_t c2 = uniCode - pgm_read_byte(&gfxFont->first); GFXglyph *glyph = &(((GFXglyph *)pgm_read_dword(&gfxFont->glyph))[c2]); @@ -4991,7 +4987,7 @@ int16_t TFT_eSprite::drawChar(unsigned int uniCode, int x, int y, int font) #endif } - if ((font>1) && (font<9) && ((uniCode < 32) || (uniCode > 122))) return 0; + if ((font>1) && (font<9) && ((uniCode < 32) || (uniCode > 127))) return 0; int width = 0; int height = 0; From 1e60eda376b83e795c9b2dc3dc400f7ab31c119e Mon Sep 17 00:00:00 2001 From: Bodmer Date: Wed, 29 Nov 2017 10:00:11 +0000 Subject: [PATCH 037/150] Raise version to 0.17.12 --- library.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library.json b/library.json index 35f59b2..21dc8df 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "TFT_eSPI", - "version": "0.17.11", + "version": "0.17.12", "keywords": "TFT, ESP8266, NodeMCU, ESP32, ILI9341, ST7735, ILI9163, S6D02A1, ILI9486", "description": "A TFT SPI graphics library for ESP8266", "repository": From 75bc8c45d7c6d5e56a059d3b8f8dc6e19cbdd2ad Mon Sep 17 00:00:00 2001 From: Bodmer Date: Wed, 29 Nov 2017 10:00:59 +0000 Subject: [PATCH 038/150] Raise version to 0.17.12 --- library.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library.properties b/library.properties index 9078977..4a754b7 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=TFT_eSPI -version=0.17.11 +version=0.17.12 author=Bodmer maintainer=Bodmer sentence=A fast TFT library for ESP8266 processors and the Arduino IDE From 706af163c00049f7dcbf5a7eb8d3cf2c62a2d5b3 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Thu, 30 Nov 2017 00:27:56 +0000 Subject: [PATCH 039/150] Add some crash prevention Prevent memory access to non-existant Sprite memory areas which would cause a processor reset. --- TFT_eSPI.cpp | 53 +++++++++++++++++++++++++++++++++++++--------- library.json | 2 +- library.properties | 2 +- 3 files changed, 45 insertions(+), 12 deletions(-) diff --git a/TFT_eSPI.cpp b/TFT_eSPI.cpp index f19f1bb..4423ccd 100644 --- a/TFT_eSPI.cpp +++ b/TFT_eSPI.cpp @@ -4239,15 +4239,21 @@ TFT_eSprite::TFT_eSprite(TFT_eSPI *tft) *************************************************************************************x*/ void TFT_eSprite::createSprite(int16_t w, int16_t h) { - if ( w < 1 || h < 1) return; + if ( w < 1 || h < 1 || _created) return; _iwidth = w; _iheight = h; - if(_bpp16) _img = (uint16_t*) malloc(w * h * 2); - else _img8 = ( uint8_t*) malloc(w * h); - - _created = true; + if(_bpp16) + { + _img = (uint16_t*) malloc(w * h * 2); + if (_img) _created = true; + } + else + { + _img8 = ( uint8_t*) malloc(w * h); + if (_img8) _created = true; + } } @@ -4270,7 +4276,11 @@ void TFT_eSprite::setColorDepth(int8_t b) else _bpp16 = false; // If it existed, re-create the sprite with the new colour depth - if (_created) createSprite(_iwidth, _iheight); + if (_created) + { + _created = false; + createSprite(_iwidth, _iheight); + } } @@ -4281,6 +4291,7 @@ void TFT_eSprite::setColorDepth(int8_t b) void TFT_eSprite::deleteSprite(void) { if (!_created ) return; + if (_bpp16) free(_img); else free(_img8); _created = false; @@ -4293,6 +4304,8 @@ void TFT_eSprite::deleteSprite(void) *************************************************************************************x*/ void TFT_eSprite::pushSprite(int32_t x, int32_t y) { + if (!_created ) return; + if (_bpp16) _tft->pushSprite(x, y, _iwidth, _iheight, _img ); else _tft->pushSprite(x, y, _iwidth, _iheight, _img8); } @@ -4304,6 +4317,8 @@ void TFT_eSprite::pushSprite(int32_t x, int32_t y) *************************************************************************************x*/ uint16_t TFT_eSprite::readPixel(int32_t x, int32_t y) { + if (!_created ) return 0; + if (_bpp16) { uint16_t color = _img[x + y * _iwidth]; @@ -4328,7 +4343,7 @@ uint16_t TFT_eSprite::readPixel(int32_t x, int32_t y) *************************************************************************************x*/ void TFT_eSprite::pushRect(uint32_t x, uint32_t y, uint32_t w, uint32_t h, uint16_t *data) { - if ((x > _iwidth) || (y > _iheight) || (w == 0) || (h == 0)) return; + if ((x > _iwidth) || (y > _iheight) || (w == 0) || (h == 0) || !_created) return; if (_bpp16) { @@ -4399,6 +4414,7 @@ void TFT_eSprite::setWindow(int32_t x0, int32_t y0, int32_t x1, int32_t y1) *************************************************************************************x*/ void TFT_eSprite::pushColor(uint32_t color) { + if (!_created ) return; // Write the colour to RAM in set window if (_bpp16) @@ -4426,6 +4442,8 @@ void TFT_eSprite::pushColor(uint32_t color) *************************************************************************************x*/ void TFT_eSprite::pushColor(uint32_t color, uint16_t len) { + if (!_created ) return; + uint16_t pixelColor; if (_bpp16) pixelColor = (uint16_t) (color >> 8) | (color << 8); @@ -4442,6 +4460,8 @@ void TFT_eSprite::pushColor(uint32_t color, uint16_t len) *************************************************************************************x*/ void TFT_eSprite::writeColor(uint16_t color) { + if (!_created ) return; + // Write 16 bit RGB 565 encoded colour to RAM if (_bpp16) _img [_xptr + _yptr * _iwidth] = color; @@ -4467,6 +4487,8 @@ void TFT_eSprite::writeColor(uint16_t color) *************************************************************************************x*/ void TFT_eSprite::fillSprite(uint32_t color) { + if (!_created ) return; + // Use memset if possible as it is super fast if(( (uint8_t)color == (uint8_t)(color>>8) ) && _bpp16) memset(_img, (uint8_t)color, _iwidth * _iheight * 2); @@ -4494,6 +4516,7 @@ void TFT_eSprite::setCursor(int16_t x, int16_t y) // Return the size of the display int16_t TFT_eSprite::width(void) { + if (!_created ) return 0; return _iwidth; } @@ -4504,6 +4527,7 @@ int16_t TFT_eSprite::width(void) *************************************************************************************x*/ int16_t TFT_eSprite::height(void) { + if (!_created ) return 0; return _iheight; } @@ -4514,6 +4538,8 @@ int16_t TFT_eSprite::height(void) *************************************************************************************x*/ void TFT_eSprite::drawChar(int32_t x, int32_t y, unsigned char c, uint32_t color, uint32_t bg, uint8_t size) { + if (!_created ) return; + if ((x >= _iwidth) || // Clip right (y >= _iheight) || // Clip bottom ((x + 6 * size - 1) < 0) || // Clip left @@ -4655,7 +4681,7 @@ void TFT_eSprite::drawPixel(uint32_t x, uint32_t y, uint32_t color) { // x and y are unsigned so that -ve coordinates turn into large positive ones // this make bounds checking a bit faster - if ((x >= _iwidth) || (y >= _iheight)) return; + if ((x >= _iwidth) || (y >= _iheight) || !_created) return; if (_bpp16) { @@ -4675,6 +4701,8 @@ void TFT_eSprite::drawPixel(uint32_t x, uint32_t y, uint32_t color) *************************************************************************************x*/ void TFT_eSprite::drawLine(int32_t x0, int32_t y0, int32_t x1, int32_t y1, uint32_t color) { + if (!_created ) return; + boolean steep = abs(y1 - y0) > abs(x1 - x0); if (steep) { swap_coord(x0, y0); @@ -4730,7 +4758,7 @@ void TFT_eSprite::drawLine(int32_t x0, int32_t y0, int32_t x1, int32_t y1, uint3 void TFT_eSprite::drawFastVLine(int32_t x, int32_t y, int32_t h, uint32_t color) { - if ((x < 0) || (x >= _iwidth) || (y >= _iheight)) return; + if ((x < 0) || (x >= _iwidth) || (y >= _iheight) || !_created) return; if (y < 0) { h += y; y = 0; } @@ -4758,7 +4786,7 @@ void TFT_eSprite::drawFastVLine(int32_t x, int32_t y, int32_t h, uint32_t color) void TFT_eSprite::drawFastHLine(int32_t x, int32_t y, int32_t w, uint32_t color) { - if ((y < 0) || (x >= _iwidth) || (y >= _iheight)) return; + if ((y < 0) || (x >= _iwidth) || (y >= _iheight) || !_created) return; if (x < 0) { w += x; x = 0; } @@ -4785,6 +4813,8 @@ void TFT_eSprite::drawFastHLine(int32_t x, int32_t y, int32_t w, uint32_t color) *************************************************************************************x*/ void TFT_eSprite::fillRect(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color) { + if (!_created ) return; + if (x < 0) { w += x; x = 0; } if ((x < 0) || (y < 0) || (x >= _iwidth) || (y >= _iheight)) return; @@ -4822,6 +4852,8 @@ void TFT_eSprite::fillRect(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t *************************************************************************************x*/ size_t TFT_eSprite::write(uint8_t utf8) { + if (!_created ) return 0; + if (utf8 == '\r') return 1; uint8_t uniCode = utf8; // Work with a copy @@ -4948,6 +4980,7 @@ int16_t TFT_eSprite::drawChar(unsigned int uniCode, int x, int y) int16_t TFT_eSprite::drawChar(unsigned int uniCode, int x, int y, int font) { + if (!_created ) return 0; if (font==1) { diff --git a/library.json b/library.json index 21dc8df..c9113e0 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "TFT_eSPI", - "version": "0.17.12", + "version": "0.17.14", "keywords": "TFT, ESP8266, NodeMCU, ESP32, ILI9341, ST7735, ILI9163, S6D02A1, ILI9486", "description": "A TFT SPI graphics library for ESP8266", "repository": diff --git a/library.properties b/library.properties index 4a754b7..aab6e34 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=TFT_eSPI -version=0.17.12 +version=0.17.14 author=Bodmer maintainer=Bodmer sentence=A fast TFT library for ESP8266 processors and the Arduino IDE From 8b24e61b999087adeb73ccc40e062dad2c61ecdc Mon Sep 17 00:00:00 2001 From: Bodmer Date: Thu, 30 Nov 2017 22:41:23 +0000 Subject: [PATCH 040/150] Fix issue #60 Do not adjust x datum for characters that have a positive offset. --- TFT_eSPI.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/TFT_eSPI.cpp b/TFT_eSPI.cpp index 4423ccd..32a24c9 100644 --- a/TFT_eSPI.cpp +++ b/TFT_eSPI.cpp @@ -3409,8 +3409,8 @@ int16_t TFT_eSPI::drawString(const char *string, int poX, int poY, int font) GFXglyph *glyph = &(((GFXglyph *)pgm_read_dword(&gfxFont->glyph))[c2]); xo = pgm_read_byte(&glyph->xOffset) * textsize; // Adjust for negative xOffset - //if (xo < 0) - cwidth -= xo; + if (xo > 0) xo = 0; + else cwidth -= xo; // Add 1 pixel of padding all round //cheight +=2; //fillRect(poX+xo-1, poY - 1 - glyph_ab * textsize, cwidth+2, cheight, textbgcolor); From 0a2c84212b41c768e25e4102e1fd6e1164888d28 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Thu, 30 Nov 2017 22:42:47 +0000 Subject: [PATCH 041/150] Raise version for #60 fix --- library.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library.json b/library.json index c9113e0..fab143b 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "TFT_eSPI", - "version": "0.17.14", + "version": "0.17.15", "keywords": "TFT, ESP8266, NodeMCU, ESP32, ILI9341, ST7735, ILI9163, S6D02A1, ILI9486", "description": "A TFT SPI graphics library for ESP8266", "repository": From 6b924c5504a101f5c0d1ce7b3d429a36e465996e Mon Sep 17 00:00:00 2001 From: Bodmer Date: Thu, 30 Nov 2017 22:43:10 +0000 Subject: [PATCH 042/150] Raise version for #60 fix --- library.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library.properties b/library.properties index aab6e34..174717c 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=TFT_eSPI -version=0.17.14 +version=0.17.15 author=Bodmer maintainer=Bodmer sentence=A fast TFT library for ESP8266 processors and the Arduino IDE From 9d2c7f21fcfb14793218522f386fd9986c959580 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Fri, 1 Dec 2017 15:37:08 +0000 Subject: [PATCH 043/150] Correct Sprite odd pixel count width crash The 32 bit alignment offset must be checked for every line to prevent crash with odd pixel count lines. --- TFT_eSPI.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/TFT_eSPI.cpp b/TFT_eSPI.cpp index 32a24c9..8be3a37 100644 --- a/TFT_eSPI.cpp +++ b/TFT_eSPI.cpp @@ -562,13 +562,14 @@ uint16_t TFT_eSPI::readPixel(int32_t x0, int32_t y0) data += dx + dy * w; - // Check alignment of pointer to 32 bits - uint8_t offset = (uint32_t)data & 0x3; dw <<= 1; - if (offset > dw) offset = dw; while (dh--) { + // Check alignment of pointer to 32 bits + uint8_t offset = (uint32_t)data & 0x3; + if (offset > dw) offset = dw; + int32_t len = dw; uint8_t* ptr = (uint8_t*)data; From d2104f6d851126ff55a83773d51e0c8ac51e0d10 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Sun, 3 Dec 2017 02:29:39 +0000 Subject: [PATCH 044/150] Add ability to plot Sprites with a "transparent" colour Can now specify a colour that will not be plotted so some part of the Sprite are "transparent" and the TFT backforund shows through. New example added for demonstration. --- TFT_eSPI.cpp | 188 ++++++++++++++++-- TFT_eSPI.h | 18 +- .../Transparent_Sprite_Demo.ino | 144 ++++++++++++++ 3 files changed, 327 insertions(+), 23 deletions(-) create mode 100644 examples/Sprite/Transparent_Sprite_Demo/Transparent_Sprite_Demo.ino diff --git a/TFT_eSPI.cpp b/TFT_eSPI.cpp index 8be3a37..8091383 100644 --- a/TFT_eSPI.cpp +++ b/TFT_eSPI.cpp @@ -387,7 +387,7 @@ void TFT_eSPI::writedata(uint8_t c) ** Function name: readcommand8 (for ILI9341 Interface II i.e. IM [3:0] = "1101") ** Description: Read a 8 bit data value from an indexed command register ***************************************************************************************/ - uint8_t TFT_eSPI::readcommand8(uint8_t cmd_function, uint8_t index) +uint8_t TFT_eSPI::readcommand8(uint8_t cmd_function, uint8_t index) { spi_begin(); index = 0x10 + (index & 0x0F); @@ -415,7 +415,7 @@ void TFT_eSPI::writedata(uint8_t c) ** Function name: readcommand16 (for ILI9341 Interface II i.e. IM [3:0] = "1101") ** Description: Read a 16 bit data value from an indexed command register ***************************************************************************************/ - uint16_t TFT_eSPI::readcommand16(uint8_t cmd_function, uint8_t index) +uint16_t TFT_eSPI::readcommand16(uint8_t cmd_function, uint8_t index) { uint32_t reg = 0; reg |= (readcommand8(cmd_function, index + 0) << 8); @@ -429,7 +429,7 @@ void TFT_eSPI::writedata(uint8_t c) ** Function name: readcommand32 (for ILI9341 Interface II i.e. IM [3:0] = "1101") ** Description: Read a 32 bit data value from an indexed command register ***************************************************************************************/ - uint32_t TFT_eSPI::readcommand32(uint8_t cmd_function, uint8_t index) +uint32_t TFT_eSPI::readcommand32(uint8_t cmd_function, uint8_t index) { uint32_t reg; @@ -472,7 +472,7 @@ uint16_t TFT_eSPI::readPixel(int32_t x0, int32_t y0) ** Function name: read rectangle (for SPI Interface II i.e. IM [3:0] = "1101") ** Description: Read 565 pixel colours from a defined area ***************************************************************************************/ - void TFT_eSPI::readRect(uint32_t x, uint32_t y, uint32_t w, uint32_t h, uint16_t *data) +void TFT_eSPI::readRect(uint32_t x, uint32_t y, uint32_t w, uint32_t h, uint16_t *data) { if ((x > _width) || (y > _height) || (w == 0) || (h == 0)) return; @@ -510,7 +510,7 @@ uint16_t TFT_eSPI::readPixel(int32_t x0, int32_t y0) ** Function name: push rectangle (for SPI Interface II i.e. IM [3:0] = "1101") ** Description: push 565 pixel colours into a defined area ***************************************************************************************/ - void TFT_eSPI::pushRect(uint32_t x, uint32_t y, uint32_t w, uint32_t h, uint16_t *data) +void TFT_eSPI::pushRect(uint32_t x, uint32_t y, uint32_t w, uint32_t h, uint16_t *data) { if ((x >= _width) || (y >= _height) || (w == 0) || (h == 0)) return; @@ -538,7 +538,7 @@ uint16_t TFT_eSPI::readPixel(int32_t x0, int32_t y0) ** Function name: push sprite ** Description: plot 16 bit sprite in a defined area with clipping ***************************************************************************************/ - void TFT_eSPI::pushSprite(int32_t x, int32_t y, uint32_t w, uint32_t h, uint16_t *data) +void TFT_eSPI::pushSprite(int32_t x, int32_t y, uint32_t w, uint32_t h, uint16_t *data) { if ((x >= (int32_t)_width) || (y >= (int32_t)_height)) return; @@ -586,12 +586,68 @@ uint16_t TFT_eSPI::readPixel(int32_t x0, int32_t y0) spi_end(); } +/*************************************************************************************** +** Function name: push sprite +** Description: plot 16 bit sprite with 1 colour being transparent +***************************************************************************************/ +void TFT_eSPI::pushSprite(int32_t x, int32_t y, uint32_t w, uint32_t h, uint16_t *data, uint16_t transp) +{ + + if ((x >= (int32_t)_width) || (y >= (int32_t)_height)) return; + + int32_t dx = 0; + int32_t dy = 0; + int32_t dw = w; + int32_t dh = h; + + if (x < 0) { dw += x; dx = -x; x = 0; } + if (y < 0) { dh += y; dy = -y; y = 0; } + + if ((x + w) > _width ) dw = _width - x; + if ((y + h) > _height) dh = _height - y; + + if (dw < 1 || dh < 1) return; + + spi_begin(); + + data += dx + dy * w; + + int32_t xe = x + dw - 1, ye = y + dh - 1; + + while (dh--) + { + int32_t len = dw; + uint16_t* ptr = data; + int32_t px = x; + boolean move = true; + + while (len--) + { + if (transp != *ptr) + { + if (move) { move = false; setAddrWindow(px, y, xe, ye); } + SPI.write16(*ptr>>8 | *ptr <<8); + } + else move = true; + px++; + ptr++; + } + + y++; + data += w; + } + + CS_H; + + spi_end(); +} + /*************************************************************************************** ** Function name: push sprite ** Description: plot 8 bit sprite with clipping using a line buffer ***************************************************************************************/ - void TFT_eSPI::pushSprite(int32_t x, int32_t y, uint32_t w, uint32_t h, uint8_t *data) +void TFT_eSPI::pushSprite(int32_t x, int32_t y, uint32_t w, uint32_t h, uint8_t *data) { if ((x >= (int32_t)_width) || (y >= (int32_t)_height)) return; @@ -658,12 +714,85 @@ uint16_t TFT_eSPI::readPixel(int32_t x0, int32_t y0) } +/*************************************************************************************** +** Function name: push sprite +** Description: plot 8 bit sprite with 1 colour being transparent +***************************************************************************************/ +void TFT_eSPI::pushSprite(int32_t x, int32_t y, uint32_t w, uint32_t h, uint8_t *data, uint8_t transp) +{ + if ((x >= (int32_t)_width) || (y >= (int32_t)_height)) return; + + int32_t dx = 0; + int32_t dy = 0; + int32_t dw = w; + int32_t dh = h; + + if (x < 0) { dw += x; dx = -x; x = 0; } + if (y < 0) { dh += y; dy = -y; y = 0; } + + if ((x + w) > _width ) dw = _width - x; + if ((y + h) > _height) dh = _height - y; + + if (dw < 1 || dh < 1) return; + + spi_begin(); + + data += dx + dy * w; + + int32_t xe = x + dw - 1, ye = y + dh - 1; + + uint8_t blue[] = {0, 11, 21, 31}; // blue 2 to 5 bit colour lookup table + + _lastColor = -1; // Set to illegal value + + // Used to store last shifted colour + uint16_t color565 = 0; + + while (dh--) + { + int32_t len = dw; + uint8_t* ptr = data; //<<<<<<<<< changed to 8 bit + int32_t px = x; + boolean move = true; + + while (len--) + { + if (transp != *ptr) + { + if (move) { move = false; setAddrWindow(px, y, xe, ye); } + uint32_t color = *ptr; + + // Shifts are slow so check if colour has changed first + if (color != _lastColor) { + // =====Green===== ===============Red============== + color565 = (color & 0x1C)<<6 | (color & 0xC0)<<5 | (color & 0xE0)<<8 + // =====Green===== =======Blue====== + | (color & 0x1C)<<3 | blue[color & 0x03]; + _lastColor = color; + } + SPI.write16(color565); + } + else move = true; + px++; + ptr++; + } + + y++; + data += w; + } + + CS_H; + + spi_end(); +} + + /*************************************************************************************** ** Function name: read rectangle (for SPI Interface II i.e. IM [3:0] = "1101") ** Description: Read RGB pixel colours from a defined area ***************************************************************************************/ // If w and h are 1, then 1 pixel is read, *data array size must be 3 bytes per pixel - void TFT_eSPI::readRectRGB(int32_t x0, int32_t y0, int32_t w, int32_t h, uint8_t *data) +void TFT_eSPI::readRectRGB(int32_t x0, int32_t y0, int32_t w, int32_t h, uint8_t *data) { spi_begin(); @@ -4301,7 +4430,7 @@ void TFT_eSprite::deleteSprite(void) /*************************************************************************************** ** Function name: pushSprite -** Description: Delete the sprite to free up memory (RAM) +** Description: Push the sprite to the TFT at x, y *************************************************************************************x*/ void TFT_eSprite::pushSprite(int32_t x, int32_t y) { @@ -4312,6 +4441,23 @@ void TFT_eSprite::pushSprite(int32_t x, int32_t y) } +/*************************************************************************************** +** Function name: pushSprite +** Description: Push the sprite to the TFT at x, y with transparent colour +*************************************************************************************x*/ +void TFT_eSprite::pushSprite(int32_t x, int32_t y, uint16_t transp) +{ + if (!_created ) return; + + if (_bpp16) _tft->pushSprite(x, y, _iwidth, _iheight, _img, transp ); + else + { + transp = (uint8_t)((transp & 0xE000)>>8 | (transp & 0x0700)>>6 | (transp & 0x0018)>>3); + _tft->pushSprite(x, y, _iwidth, _iheight, _img8, (uint8_t) transp); + } +} + + /*************************************************************************************** ** Function name: readPixel ** Description: Read 565 colour of a pixel at defined coordinates @@ -4493,7 +4639,11 @@ void TFT_eSprite::fillSprite(uint32_t color) // Use memset if possible as it is super fast if(( (uint8_t)color == (uint8_t)(color>>8) ) && _bpp16) memset(_img, (uint8_t)color, _iwidth * _iheight * 2); - else if (!_bpp16) memset(_img8, (uint8_t)color, _iwidth * _iheight); + else if (!_bpp16) + { + color = (color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3; + memset(_img8, (uint8_t)color, _iwidth * _iheight); + } else fillRect(0, 0, _iwidth, _iheight, color); } @@ -4770,7 +4920,8 @@ void TFT_eSprite::drawFastVLine(int32_t x, int32_t y, int32_t h, uint32_t color) if (_bpp16) { color = (color >> 8) | (color << 8); - while (h--) _img[x + _iwidth * y++] = (uint16_t) color; + int32_t yp = x + _iwidth * y; + while (h--) {_img[yp] = (uint16_t) color; yp += _iwidth;} } else { @@ -4823,14 +4974,17 @@ void TFT_eSprite::fillRect(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t if ((y + h) > _iheight) h = _iheight - y; if ((w < 1) || (h < 1)) return; + int32_t yp = _iwidth * y; + if (_bpp16) { color = (color >> 8) | (color << 8); + while (h--) { - int32_t ix = x, iw = w; - while (iw--) _img[_iwidth * y + ix++] = (uint16_t) color; - y++; + uint32_t ix = x, iw = w; + while (iw--) _img[yp + ix++] = (uint16_t) color; + yp += _iwidth; } } else @@ -4838,10 +4992,8 @@ void TFT_eSprite::fillRect(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color = (color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3; while (h--) { - //int32_t ix = x, iw = w; - //while (iw--) _img8[_iwidth * y + ix++] = (uint8_t) color; - memset(_img8 + _iwidth * y + x, (uint8_t)color, w); - y++; + memset(_img8 + yp + x, (uint8_t)color, w); + yp += _iwidth; } } } diff --git a/TFT_eSPI.h b/TFT_eSPI.h index d38bf6e..ef4ecf8 100644 --- a/TFT_eSPI.h +++ b/TFT_eSPI.h @@ -246,10 +246,14 @@ #define TFT_MAGENTA 0xF81F /* 255, 0, 255 */ #define TFT_YELLOW 0xFFE0 /* 255, 255, 0 */ #define TFT_WHITE 0xFFFF /* 255, 255, 255 */ -#define TFT_ORANGE 0xFD20 /* 255, 165, 0 */ -#define TFT_GREENYELLOW 0xAFE5 /* 173, 255, 47 */ -#define TFT_PINK 0xF81F +#define TFT_ORANGE 0xFDA0 /* 255, 180, 0 */ +#define TFT_GREENYELLOW 0xB7E0 /* 180, 255, 0 */ +#define TFT_PINK 0xFC9F +// Next is a special 16 bit colour value that encodes to 8 bits +// and will then decode back to the same 16 bit value. +// Convenient for 8 bit and 16 bit transparent sprites. +#define TFT_TRANSPARENT 0x0120 // Swap any type template static inline void @@ -406,6 +410,9 @@ class TFT_eSPI : public Print { void pushRect(uint32_t x0, uint32_t y0, uint32_t w, uint32_t h, uint16_t *data); void pushSprite(int32_t x0, int32_t y0, uint32_t w, uint32_t h, uint16_t *data); void pushSprite(int32_t x0, int32_t y0, uint32_t w, uint32_t h, uint8_t *data); + void pushSprite(int32_t x0, int32_t y0, uint32_t w, uint32_t h, uint16_t *data, uint16_t transparent); + void pushSprite(int32_t x0, int32_t y0, uint32_t w, uint32_t h, uint8_t *data, uint8_t transparent); + // This next function has been used successfully to dump the TFT screen to a PC for documentation purposes // It reads a screen area and returns the RGB 8 bit colour values of each pixel // Set w and h to 1 to read 1 pixel's colour. The data buffer must be at least w * h * 3 bytes @@ -590,6 +597,7 @@ class TFT_eSprite : public TFT_eSPI { void pushBitmap(uint32_t x0, uint32_t y0, uint32_t w, uint32_t h, uint16_t *data); void pushSprite(int32_t x, int32_t y); + void pushSprite(int32_t x, int32_t y, uint16_t transparent); int16_t drawChar(unsigned int uniCode, int x, int y, int font), drawChar(unsigned int uniCode, int x, int y); @@ -607,11 +615,11 @@ class TFT_eSprite : public TFT_eSPI { uint16_t *_img; uint8_t *_img8; - bool _created; + bool _created, _bpp16; int32_t _icursor_x, _icursor_y, _xs, _ys, _xe, _ye, _xptr, _yptr; - int32_t _iwidth, _iheight, _bpp16; + int32_t _iwidth, _iheight; }; diff --git a/examples/Sprite/Transparent_Sprite_Demo/Transparent_Sprite_Demo.ino b/examples/Sprite/Transparent_Sprite_Demo/Transparent_Sprite_Demo.ino new file mode 100644 index 0000000..c8039e7 --- /dev/null +++ b/examples/Sprite/Transparent_Sprite_Demo/Transparent_Sprite_Demo.ino @@ -0,0 +1,144 @@ +/* + Sketch to show creation of a sprite with a transparent + background, then plot it on the TFT. + + Example for library: + https://github.com/Bodmer/TFT_eSPI + + A Sprite is notionally an invisible graphics screen that is + kept in the processors RAM. Graphics can be drawn into the + Sprite just as it can be drawn directly to the screen. Once + the Sprite is completed it can be plotted onto the screen in + any position. If there is sufficient RAM then the Sprite can + be the same size as the screen and used as a frame buffer. + + A 16 bit Sprite occupies (2 * width * height) bytes in RAM. + + On a ESP8266 Sprite sizes up to 126 x 160 can be accomodated, + this size requires 40kBytes of RAM for a 16 bit colour depth. + + When 8 bit colour depth sprites are created they occupy + (width * height) bytes in RAM, so larger sprites can be + created, or the RAM required is halved. +*/ + +#include // Include the graphics library (this includes the sprite functions) + +TFT_eSPI tft = TFT_eSPI(); // Create object "tft" + +TFT_eSprite img = TFT_eSprite(&tft); // Create Sprite object "img" with pointer to "tft" object + // the pointer is used by pushSprite() to push it onto the TFT + +void setup(void) { + Serial.begin(250000); + + tft.init(); + + tft.setRotation(0); +} + +void loop() { + + tft.fillScreen(TFT_NAVY); + + // Draw 10 sprites containing a "transparent" colour + for (int i = 0; i < 10; i++) + { + int x = random(240-70); + int y = random(320-80); + int c = random(0x10000); // Random colour + drawStar(x, y, c); + } + + delay(2000); + + uint32_t dt = millis(); + + // Now go bananas and draw 500 nore + for (int i = 0; i < 500; i++) + { + int x = random(240-70); + int y = random(320-80); + int c = random(0x10000); // Random colour + drawStar(x, y, c); + yield(); // Stop watchdog reset + } + + // Show time in milliseconds to draw and then push 1 sprite to TFT screen + numberBox( 10, 10, (millis()-dt)/500.0 ); + + delay(2000); + +} + +// ######################################################################### +// Create sprite, plot graphics in it, plot to screen, then delete sprite +// ######################################################################### +void drawStar(int x, int y, int star_color) +{ + // Create an 8 bit sprite 70x 80 pixels (uses 5600 bytes of RAM) + img.setColorDepth(8); + img.createSprite(70, 80); + + // Fill Sprite with a "transparent" colour + // TFT_TRANSPARENT is already defined for convenience + // We could also fill with any colour as "transparent" and later specify that + // same colour when we push the Sprite onto the screen. + img.fillSprite(TFT_TRANSPARENT); + + // Draw 2 triangles to create a filled in star + img.fillTriangle(35, 0, 0,59, 69,59, star_color); + img.fillTriangle(35,79, 0,20, 69,20, star_color); + + // Punch a star shaped hole in the middle with a smaller transparent star + img.fillTriangle(35, 7, 6,56, 63,56, TFT_TRANSPARENT); + img.fillTriangle(35,73, 6,24, 63,24, TFT_TRANSPARENT); + + // Push sprite to TFT screen CGRAM at coordinate x,y (top left corner) + // Specify what colour is to be treated as transparent. + img.pushSprite(x, y, TFT_TRANSPARENT); + + // Delete it to free memory + img.deleteSprite(); + +} + +// ######################################################################### +// Draw a number in a rounded rectangle with some transparent pixels +// ######################################################################### +void numberBox(int x, int y, float num ) +{ + + // Size of sprite + #define IWIDTH 80 + #define IHEIGHT 35 + + // Create a 8 bit sprite 80 pixels wide, 35 high (2800 bytes of RAM needed) + img.setColorDepth(8); + img.createSprite(IWIDTH, IHEIGHT); + + // Fill it with black (this will be the transparent colour this time) + img.fillSprite(TFT_BLACK); + + // Draw a background for the numbers + img.fillRoundRect( 0, 0, 80, 35, 15, TFT_RED); + img.drawRoundRect( 0, 0, 80, 35, 15, TFT_WHITE); + + // Set the font parameters + img.setTextSize(1); // Font size scaling is x1 + img.setTextColor(TFT_WHITE); // White text, no background colour + + // Set text coordinate datum to middle right + img.setTextDatum(MR_DATUM); + + // Draw the number to 3 decimal places at 70,20 in font 4 + img.drawFloat(num, 3, 70, 20, 4); + + // Push sprite to TFT screen CGRAM at coordinate x,y (top left corner) + // All black pixels will not be drawn hence will show as "transparent" + img.pushSprite(x, y, TFT_BLACK); + + // Delete sprite to free up the RAM + img.deleteSprite(); +} + From ae8571b4748f2d89bf79bdafaf356ae92e2ab9e0 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Sun, 3 Dec 2017 02:30:42 +0000 Subject: [PATCH 045/150] Raise version --- library.json | 2 +- library.properties | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/library.json b/library.json index fab143b..6e1aae2 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "TFT_eSPI", - "version": "0.17.15", + "version": "0.17.16", "keywords": "TFT, ESP8266, NodeMCU, ESP32, ILI9341, ST7735, ILI9163, S6D02A1, ILI9486", "description": "A TFT SPI graphics library for ESP8266", "repository": diff --git a/library.properties b/library.properties index 174717c..e1a4888 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=TFT_eSPI -version=0.17.15 +version=0.17.16 author=Bodmer maintainer=Bodmer sentence=A fast TFT library for ESP8266 processors and the Arduino IDE From 0841635b4e81c5a07029a3c05af6f65c65fe4462 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Sun, 3 Dec 2017 02:34:38 +0000 Subject: [PATCH 046/150] Update ReadMe --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 53c9222..bfb03eb 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,8 @@ One or more sprites can be created, a sprite can be any width and height, limite Drawing graphics into a sprite is very fast, for those familiar with the Adafruit "graphicstest" example, this whole test completes in less than 27ms in a 160x128 sprite. Examples of sprite use can be found in the "examples/Sprite" folder. +Sprites can be plotted to the TFT with pne colour being specified as "transparent", see Transparent_Sprite_Demo example. + The XPT2046 touch screen controller is supported. The SPI bus for the touch controller is shared with the TFT and only an additional chip select line is needed. The Button class from Adafruit_GFX is incorporated, with the enhancement that the button labels can be in any font. From 73c7f46f37a53a494659475b257655aa01e18414 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Sun, 3 Dec 2017 02:35:41 +0000 Subject: [PATCH 047/150] Typo - doh! --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bfb03eb..580e6ff 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ One or more sprites can be created, a sprite can be any width and height, limite Drawing graphics into a sprite is very fast, for those familiar with the Adafruit "graphicstest" example, this whole test completes in less than 27ms in a 160x128 sprite. Examples of sprite use can be found in the "examples/Sprite" folder. -Sprites can be plotted to the TFT with pne colour being specified as "transparent", see Transparent_Sprite_Demo example. +Sprites can be plotted to the TFT with one colour being specified as "transparent", see Transparent_Sprite_Demo example. The XPT2046 touch screen controller is supported. The SPI bus for the touch controller is shared with the TFT and only an additional chip select line is needed. From 7f909a152734b116c3c0af3ae4c6431138800309 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Sun, 3 Dec 2017 02:55:43 +0000 Subject: [PATCH 048/150] Fix bug in 16 bit transparent Sprite --- TFT_eSPI.cpp | 5 +++-- library.json | 2 +- library.properties | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/TFT_eSPI.cpp b/TFT_eSPI.cpp index 8091383..70939c2 100644 --- a/TFT_eSPI.cpp +++ b/TFT_eSPI.cpp @@ -613,7 +613,8 @@ void TFT_eSPI::pushSprite(int32_t x, int32_t y, uint32_t w, uint32_t h, uint16_t data += dx + dy * w; int32_t xe = x + dw - 1, ye = y + dh - 1; - + + transp = transp >> 8 | transp << 8; while (dh--) { int32_t len = dw; @@ -626,7 +627,7 @@ void TFT_eSPI::pushSprite(int32_t x, int32_t y, uint32_t w, uint32_t h, uint16_t if (transp != *ptr) { if (move) { move = false; setAddrWindow(px, y, xe, ye); } - SPI.write16(*ptr>>8 | *ptr <<8); + SPI.write16(*ptr>>8 | *ptr<<8); } else move = true; px++; diff --git a/library.json b/library.json index 6e1aae2..f7d7113 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "TFT_eSPI", - "version": "0.17.16", + "version": "0.17.17", "keywords": "TFT, ESP8266, NodeMCU, ESP32, ILI9341, ST7735, ILI9163, S6D02A1, ILI9486", "description": "A TFT SPI graphics library for ESP8266", "repository": diff --git a/library.properties b/library.properties index e1a4888..787cc6f 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=TFT_eSPI -version=0.17.16 +version=0.17.17 author=Bodmer maintainer=Bodmer sentence=A fast TFT library for ESP8266 processors and the Arduino IDE From 93ba6e3716fab6c2dd80fd55e4702352da4c29a1 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Sun, 10 Dec 2017 23:04:31 +0000 Subject: [PATCH 049/150] Add scrolling inside a sprite Added example "Sprite_scroll" Tidy up virtual function list Add PROGMEM bitmap image functions --- Keywords.txt | 4 +- TFT_eSPI.cpp | 274 +++++++++++++++--- TFT_eSPI.h | 86 ++++-- .../Sprite/Sprite_scroll/Sprite_scroll.ino | 118 ++++++++ library.json | 2 +- library.properties | 2 +- 6 files changed, 420 insertions(+), 66 deletions(-) create mode 100644 examples/Sprite/Sprite_scroll/Sprite_scroll.ino diff --git a/Keywords.txt b/Keywords.txt index 97f9b28..86f33fb 100644 --- a/Keywords.txt +++ b/Keywords.txt @@ -50,7 +50,7 @@ readcommand32 KEYWORD2 readPixel KEYWORD2 readRect KEYWORD2 pushRect KEYWORD2 -pushSprite KEYWORD2 +pushImage KEYWORD2 readRectRGB KEYWORD2 getRotation KEYWORD2 getTextDatum KEYWORD2 @@ -97,3 +97,5 @@ fillSprite KEYWORD2 setWindow KEYWORD2 pushBitmap KEYWORD2 pushSprite KEYWORD2 +setScrollRect KEYWORD2 +scroll KEYWORD2 diff --git a/TFT_eSPI.cpp b/TFT_eSPI.cpp index 70939c2..641874a 100644 --- a/TFT_eSPI.cpp +++ b/TFT_eSPI.cpp @@ -535,10 +535,10 @@ void TFT_eSPI::pushRect(uint32_t x, uint32_t y, uint32_t w, uint32_t h, uint16_t /*************************************************************************************** -** Function name: push sprite -** Description: plot 16 bit sprite in a defined area with clipping +** Function name: pushImage +** Description: plot 16 bit colour sprite or image onto TFT ***************************************************************************************/ -void TFT_eSPI::pushSprite(int32_t x, int32_t y, uint32_t w, uint32_t h, uint16_t *data) +void TFT_eSPI::pushImage(int32_t x, int32_t y, uint32_t w, uint32_t h, uint16_t *data) { if ((x >= (int32_t)_width) || (y >= (int32_t)_height)) return; @@ -587,10 +587,10 @@ void TFT_eSPI::pushSprite(int32_t x, int32_t y, uint32_t w, uint32_t h, uint16_t } /*************************************************************************************** -** Function name: push sprite -** Description: plot 16 bit sprite with 1 colour being transparent +** Function name: pushImage +** Description: plot 16 bit sprite or image with 1 colour being transparent ***************************************************************************************/ -void TFT_eSPI::pushSprite(int32_t x, int32_t y, uint32_t w, uint32_t h, uint16_t *data, uint16_t transp) +void TFT_eSPI::pushImage(int32_t x, int32_t y, uint32_t w, uint32_t h, uint16_t *data, uint16_t transp) { if ((x >= (int32_t)_width) || (y >= (int32_t)_height)) return; @@ -643,12 +643,75 @@ void TFT_eSPI::pushSprite(int32_t x, int32_t y, uint32_t w, uint32_t h, uint16_t spi_end(); } +//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< TEMPORARY TEST +/*************************************************************************************** +** Function name: pushImage - for FLASH (PROGMEM) stored images +** Description: plot 16 bit sprite or image with 1 colour being transparent +***************************************************************************************/ +void TFT_eSPI::pushImage(int32_t x, int32_t y, uint32_t w, uint32_t h, const uint16_t *data, uint16_t transp, bool swap) +{ + // Swap=true swaps the two bytes in the color to cater for big+little endian formats + // default is false if parameter is missing + + if ((x >= (int32_t)_width) || (y >= (int32_t)_height)) return; + + int32_t dx = 0; + int32_t dy = 0; + int32_t dw = w; + int32_t dh = h; + + if (x < 0) { dw += x; dx = -x; x = 0; } + if (y < 0) { dh += y; dy = -y; y = 0; } + + if ((x + w) > _width ) dw = _width - x; + if ((y + h) > _height) dh = _height - y; + + if (dw < 1 || dh < 1) return; + + spi_begin(); + + data += dx + dy * w; + + int32_t xe = x + dw - 1, ye = y + dh - 1; + + if (swap) transp = transp >> 8 | transp << 8; + + while (dh--) + { + int32_t len = dw; + uint16_t* ptr = (uint16_t*)data; + int32_t px = x; + boolean move = true; + + while (len--) + { + uint16_t color = pgm_read_word(ptr); + if (transp != color) + { + if (move) { move = false; setAddrWindow(px, y, xe, ye); } + if (swap) color = color>>8 | color<<8; + SPI.write16(color); + } + else move = true; + px++; + ptr++; + } + + y++; + data += w; + } + + CS_H; + + spi_end(); +} + /*************************************************************************************** -** Function name: push sprite -** Description: plot 8 bit sprite with clipping using a line buffer +** Function name: pushImage +** Description: plot 8 bit image or sprite using a line buffer ***************************************************************************************/ -void TFT_eSPI::pushSprite(int32_t x, int32_t y, uint32_t w, uint32_t h, uint8_t *data) +void TFT_eSPI::pushImage(int32_t x, int32_t y, uint32_t w, uint32_t h, uint8_t *data) { if ((x >= (int32_t)_width) || (y >= (int32_t)_height)) return; @@ -716,10 +779,10 @@ void TFT_eSPI::pushSprite(int32_t x, int32_t y, uint32_t w, uint32_t h, uint8_t /*************************************************************************************** -** Function name: push sprite -** Description: plot 8 bit sprite with 1 colour being transparent +** Function name: pushImage +** Description: plot 8 bit image or sprite with 1 colour being transparent ***************************************************************************************/ -void TFT_eSPI::pushSprite(int32_t x, int32_t y, uint32_t w, uint32_t h, uint8_t *data, uint8_t transp) +void TFT_eSPI::pushImage(int32_t x, int32_t y, uint32_t w, uint32_t h, uint8_t *data, uint8_t transp) { if ((x >= (int32_t)_width) || (y >= (int32_t)_height)) return; @@ -2945,7 +3008,7 @@ void TFT_eSPI::drawFastHLine(int32_t x, int32_t y, int32_t w, uint32_t color) ** Description: draw a filled rectangle ***************************************************************************************/ #if defined (ESP8266) && !defined (RPI_WRITE_STROBE) -void TFT_eSPI::fillRect(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color) +void TFT_eSPI::fillRect(int32_t x, int32_t y, uint32_t w, uint32_t h, uint32_t color) { // rudimentary clipping (drawChar w/big text requires this) if ((x > _width) || (y > _height) || (w < 1) || (h < 1)) return; @@ -2964,7 +3027,7 @@ void TFT_eSPI::fillRect(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t col #else -void TFT_eSPI::fillRect(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color) +void TFT_eSPI::fillRect(int32_t x, int32_t y, uint32_t w, uint32_t h, uint32_t color) { // rudimentary clipping (drawChar w/big text requires this) if ((x > _width) || (y > _height) || (w < 1) || (h < 1)) return; @@ -4368,23 +4431,42 @@ TFT_eSprite::TFT_eSprite(TFT_eSPI *tft) ** Function name: createSprite ** Description: Create a sprite (bitmap) of defined width and height *************************************************************************************x*/ -void TFT_eSprite::createSprite(int16_t w, int16_t h) +// returns a uint8_t* pointer, cast returned value to (uint16_t*) for 16 bit colours +uint8_t* TFT_eSprite::createSprite(int16_t w, int16_t h) { - if ( w < 1 || h < 1 || _created) return; + if ( w < 1 || h < 1 || _created) return NULL; _iwidth = w; _iheight = h; + _sx = 0; + _sy = 0; + _sw = w; + _sh = h; + _scolor = TFT_BLACK; + if(_bpp16) { _img = (uint16_t*) malloc(w * h * 2); - if (_img) _created = true; + if (_img) + { + _created = true; + fillSprite(TFT_BLACK); + return (uint8_t*)_img; + } } else { _img8 = ( uint8_t*) malloc(w * h); - if (_img8) _created = true; + if (_img8) + { + _created = true; + fillSprite(TFT_BLACK); + return _img8; + } } + + return NULL; } @@ -4437,8 +4519,8 @@ void TFT_eSprite::pushSprite(int32_t x, int32_t y) { if (!_created ) return; - if (_bpp16) _tft->pushSprite(x, y, _iwidth, _iheight, _img ); - else _tft->pushSprite(x, y, _iwidth, _iheight, _img8); + if (_bpp16) _tft->pushImage(x, y, _iwidth, _iheight, _img ); + else _tft->pushImage(x, y, _iwidth, _iheight, _img8); } @@ -4450,11 +4532,11 @@ void TFT_eSprite::pushSprite(int32_t x, int32_t y, uint16_t transp) { if (!_created ) return; - if (_bpp16) _tft->pushSprite(x, y, _iwidth, _iheight, _img, transp ); + if (_bpp16) _tft->pushImage(x, y, _iwidth, _iheight, _img, transp ); else { transp = (uint8_t)((transp & 0xE000)>>8 | (transp & 0x0700)>>6 | (transp & 0x0018)>>3); - _tft->pushSprite(x, y, _iwidth, _iheight, _img8, (uint8_t) transp); + _tft->pushImage(x, y, _iwidth, _iheight, _img8, (uint8_t) transp); } } @@ -4486,10 +4568,10 @@ uint16_t TFT_eSprite::readPixel(int32_t x, int32_t y) /*************************************************************************************** -** Function name: pushRect (same as pushBitmap) -** Description: push 565 colour bitmap into a defined area +** Function name: pushImage +** Description: push 565 colour bitmap image into a defined area *************************************************************************************x*/ -void TFT_eSprite::pushRect(uint32_t x, uint32_t y, uint32_t w, uint32_t h, uint16_t *data) +void TFT_eSprite::pushImage(uint32_t x, uint32_t y, uint32_t w, uint32_t h, uint16_t *data) { if ((x > _iwidth) || (y > _iheight) || (w == 0) || (h == 0) || !_created) return; @@ -4518,12 +4600,37 @@ void TFT_eSprite::pushRect(uint32_t x, uint32_t y, uint32_t w, uint32_t h, uint /*************************************************************************************** -** Function name: pushBitmap (same as pushRect) -** Description: push 565 colour bitmap into a defined area -***************************************************************************************/ -void TFT_eSprite::pushBitmap(uint32_t x, uint32_t y, uint32_t w, uint32_t h, uint16_t *data) +** Function name: pushImage +** Description: push 565 colour FLASH (PROGMEM) image into a defined area +*************************************************************************************x*/ +void TFT_eSprite::pushImage(uint32_t x, uint32_t y, uint32_t w, uint32_t h, const uint16_t *data, bool swap) { - pushRect(x, y, w, h, data); + if ((x > _iwidth) || (y > _iheight) || (w == 0) || (h == 0) || !_created) return; + + if (_bpp16) + { + for (uint32_t yp = y; yp < y + h; yp++) + { + for (uint32_t xp = x; xp < x + w; xp++) + { + uint16_t color = pgm_read_word(data++); + if(!swap) color = color<<8 | color>>8; + _img[xp + yp * _iwidth] = color; + } + } + } + else + { + for (uint32_t yp = y; yp < y + h; yp++) + { + for (uint32_t xp = x; xp < x + w; xp++) + { + uint16_t color = pgm_read_word(data++); + if(swap) color = color<<8 | color>>8; + _img8[xp + yp * _iwidth] = (uint8_t)((color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3); + } + } + } } @@ -4629,6 +4736,99 @@ void TFT_eSprite::writeColor(uint16_t color) } +/*************************************************************************************** +** Function name: setScrollRect +** Description: Set scroll area within the sprite and the gap fill colour +*************************************************************************************x*/ +void TFT_eSprite::setScrollRect(int32_t x, int32_t y, uint32_t w, uint32_t h, uint16_t color) +{ + if ((x >= _iwidth) || (y >= _iheight) || !_created ) return; + + if (x < 0) x = 0; + if (y < 0) y = 0; + + if ((x + w) > _iwidth ) w = _iwidth - x; + if ((y + h) > _iheight) h = _iheight - y; + + if ( w < 1 || h < 1) return; + + _sx = x; + _sy = y; + _sw = w; + _sh = h; + + _scolor = color; +} + + +/*************************************************************************************** +** Function name: scroll +** Description: Scroll dx,dy pixels, positive right,down, negative left,up +*************************************************************************************x*/ +void TFT_eSprite::scroll(int16_t dx, int16_t dy) +{ + if (abs(dx) >= _sw || abs(dy) >= _sh) + { + fillRect (_sx, _sy, _sw, _sh, _scolor); + return; + } + + // Fetch the scroll area wodth and height set by setScrollRect() + uint32_t w = _sw - abs(dx); // line width to copy + uint32_t h = _sh - abs(dy); // lines to copy + int32_t iw = _iwidth; // width of sprite + + // Fetch the x,y origin set by setScrollRect() + uint32_t tx = _sx; // to x + uint32_t fx = _sx; // from x + uint32_t ty = _sy; // to y + uint32_t fy = _sy; // from y + + // Adjust for x delta + if (dx <= 0) fx -= dx; + else tx += dx; + + // Adjust for y delta + if (dy <= 0) fy -= dy; + else + { // Scrolling down so start copy from bottom + ty = ty + _sh - 1; // "To" pointer + iw = -iw; // Pointer moves backwards + fy = ty - dy; // "From" pointer + } + + // Calculate "from y" and "to y" pointers in RAM + uint32_t fyp = fx + fy * _iwidth; + uint32_t typ = tx + ty * _iwidth; + + // Now move the pixels in RAM + if (_bpp16) + { + while (h--) + { // move pixel lines (to, from, byte count) + memmove( _img + typ, _img + fyp, w<<1); + typ += iw; + fyp += iw; + } + } + else + { + while (h--) + { // move pixel lines (to, from, byte count) + memmove( _img8 + typ, _img8 + fyp, w); + typ += iw; + fyp += iw; + } + } + + // Fill the gap left by the scrolling + if (dx > 0) fillRect(_sx, _sy, dx, _sh, _scolor); + if (dx < 0) fillRect(_sx + _sw + dx, _sy, -dx, _sh, _scolor); + if (dy > 0) fillRect(_sx, _sy, _sw, dy, _scolor); + if (dy < 0) fillRect(_sx, _sy + _sh + dy, _sw, -dy, _scolor); +} + + /*************************************************************************************** ** Function name: fillSprite ** Description: Fill the whole sprite with defined colour @@ -4964,7 +5164,7 @@ void TFT_eSprite::drawFastHLine(int32_t x, int32_t y, int32_t w, uint32_t color) ** Function name: fillRect ** Description: draw a filled rectangle *************************************************************************************x*/ -void TFT_eSprite::fillRect(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color) +void TFT_eSprite::fillRect(int32_t x, int32_t y, uint32_t w, uint32_t h, uint32_t color) { if (!_created ) return; @@ -4975,17 +5175,19 @@ void TFT_eSprite::fillRect(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t if ((y + h) > _iheight) h = _iheight - y; if ((w < 1) || (h < 1)) return; - int32_t yp = _iwidth * y; + int32_t yp = _iwidth * y + x; if (_bpp16) { color = (color >> 8) | (color << 8); - + uint32_t iw = w; + int32_t ys = yp; + if(h--) {while (iw--) _img[yp++] = (uint16_t) color;} + yp = ys; while (h--) { - uint32_t ix = x, iw = w; - while (iw--) _img[yp + ix++] = (uint16_t) color; yp += _iwidth; + memcpy( _img+yp, _img+ys, w<<1); } } else @@ -4993,7 +5195,7 @@ void TFT_eSprite::fillRect(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color = (color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3; while (h--) { - memset(_img8 + yp + x, (uint8_t)color, w); + memset(_img8 + yp, (uint8_t)color, w); yp += _iwidth; } } diff --git a/TFT_eSPI.h b/TFT_eSPI.h index ef4ecf8..ae7f3c0 100644 --- a/TFT_eSPI.h +++ b/TFT_eSPI.h @@ -334,22 +334,20 @@ class TFT_eSPI : public Print { // These are virtual so the TFT_eSprite class can override them with sprite specific functions virtual void drawPixel(uint32_t x, uint32_t y, uint32_t color), - drawChar(int32_t x, int32_t y, unsigned char c, uint32_t color, uint32_t bg, uint8_t font), - setWindow(int16_t x0, int16_t y0, int16_t x1, int16_t y1), - pushColor(uint16_t color), - pushColor(uint16_t color, uint16_t len), + drawChar(int32_t x, int32_t y, unsigned char c, uint32_t color, uint32_t bg, uint8_t size), drawLine(int32_t x0, int32_t y0, int32_t x1, int32_t y1, uint32_t color), drawFastVLine(int32_t x, int32_t y, int32_t h, uint32_t color), drawFastHLine(int32_t x, int32_t y, int32_t w, uint32_t color), - fillRect(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color); + fillRect(int32_t x, int32_t y, uint32_t w, uint32_t h, uint32_t color); virtual int16_t drawChar(unsigned int uniCode, int x, int y, int font), - drawChar(unsigned int uniCode, int x, int y), - height(void), - width(void); + drawChar(unsigned int uniCode, int x, int y); // The TFT_eSprite class inherits the following functions - void pushColors(uint16_t *data, uint8_t len), + void setWindow(int16_t x0, int16_t y0, int16_t x1, int16_t y1), + pushColor(uint16_t color), + pushColor(uint16_t color, uint16_t len), + pushColors(uint16_t *data, uint8_t len), pushColors(uint8_t *data, uint32_t len), fillScreen(uint32_t color); @@ -408,10 +406,16 @@ class TFT_eSPI : public Print { void readRect(uint32_t x0, uint32_t y0, uint32_t w, uint32_t h, uint16_t *data); // Write a block of pixels to the screen void pushRect(uint32_t x0, uint32_t y0, uint32_t w, uint32_t h, uint16_t *data); - void pushSprite(int32_t x0, int32_t y0, uint32_t w, uint32_t h, uint16_t *data); - void pushSprite(int32_t x0, int32_t y0, uint32_t w, uint32_t h, uint8_t *data); - void pushSprite(int32_t x0, int32_t y0, uint32_t w, uint32_t h, uint16_t *data, uint16_t transparent); - void pushSprite(int32_t x0, int32_t y0, uint32_t w, uint32_t h, uint8_t *data, uint8_t transparent); + + // These are used by pushSprite and can also be used to push bitmap images to the screen + // "565" 16 bit and "332" 8 bit colour encodings are supported + void pushImage(int32_t x0, int32_t y0, uint32_t w, uint32_t h, uint16_t *data); + void pushImage(int32_t x0, int32_t y0, uint32_t w, uint32_t h, uint8_t *data); + // The next two support a "transparent" colour so those image areas are not rendered + void pushImage(int32_t x0, int32_t y0, uint32_t w, uint32_t h, uint16_t *data, uint16_t transparent); + void pushImage(int32_t x0, int32_t y0, uint32_t w, uint32_t h, uint8_t *data, uint8_t transparent); + // This one has an optional flag to swap byte order in colours + void pushImage(int32_t x0, int32_t y0, uint32_t w, uint32_t h, const uint16_t *data, uint16_t transparent, bool swap = false); // This next function has been used successfully to dump the TFT screen to a PC for documentation purposes // It reads a screen area and returns the RGB 8 bit colour values of each pixel @@ -441,7 +445,9 @@ class TFT_eSPI : public Print { drawCentreString(const String& string, int dX, int poY, int font), // Deprecated, use setTextDatum() and drawString() drawRightString(const String& string, int dX, int poY, int font); // Deprecated, use setTextDatum() and drawString() - int16_t textWidth(const char *string, int font), + int16_t height(void), + width(void), + textWidth(const char *string, int font), textWidth(const char *string), textWidth(const String& string, int font), textWidth(const String& string), @@ -564,10 +570,16 @@ class TFT_eSprite : public TFT_eSPI { TFT_eSprite(TFT_eSPI *tft); - void createSprite(int16_t w, int16_t y); // 2 bytes per pixel + // Create a sprite of width x height pixels, return a pointer to the RAM area + // Sketch can cast returned value to (uint16_t*) for 16 bit depth if needed + // RAM required is 1 byte per pixel for 8 bit colour depth, 2 bytes for 16 bit + uint8_t* createSprite(int16_t width, int16_t height); + // Delete the sprite to free up the RAM void deleteSprite(void); + // Set the colour depth to 8 or 16 bits + // Can be used to change depth an existing sprite, but clears it to black void setColorDepth(int8_t b); void drawPixel(uint32_t x, uint32_t y, uint32_t color); @@ -576,35 +588,51 @@ class TFT_eSprite : public TFT_eSPI { fillSprite(uint32_t color), + // Define a window to push 16 bit colour pixels into is a raster order + // Colours are converted to 8 bit if depth is set to 8 setWindow(int32_t x0, int32_t y0, int32_t x1, int32_t y1), pushColor(uint32_t color), pushColor(uint32_t color, uint16_t len), + // Push a pixel preformatted as a 8 or 16 bit colour (avoids conversion overhead) writeColor(uint16_t color), + // Set the scroll zone, top left corner at x,y with defined width and height + // The colour (optional, black is default) is used to fill the gap after the scroll + setScrollRect(int32_t x, int32_t y, uint32_t w, uint32_t h, uint16_t color = TFT_BLACK), + // Scroll the defined zone dx,dy pixels. Negative values left,up, positive right,down + // dy is optional (default is then no up/down scroll). + // The sprite coordinate frame does not move because pixels are moved + scroll(int16_t dx, int16_t dy = 0), + drawLine(int32_t x0, int32_t y0, int32_t x1, int32_t y1, uint32_t color), drawFastVLine(int32_t x, int32_t y, int32_t h, uint32_t color), drawFastHLine(int32_t x, int32_t y, int32_t w, uint32_t color), - fillRect(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color), + fillRect(int32_t x, int32_t y, uint32_t w, uint32_t h, uint32_t color), + // Set the sprite text cursor position for print class (does not change the TFT screen cursor) setCursor(int16_t x, int16_t y); // Read the colour of a pixel at x,y and return value in 565 format uint16_t readPixel(int32_t x0, int32_t y0); - // Write a block of pixels to the sprite - void pushRect(uint32_t x0, uint32_t y0, uint32_t w, uint32_t h, uint16_t *data); - void pushBitmap(uint32_t x0, uint32_t y0, uint32_t w, uint32_t h, uint16_t *data); + // Write an image (bitmap) to the sprite + void pushImage(uint32_t x0, uint32_t y0, uint32_t w, uint32_t h, uint16_t *data); + void pushImage(uint32_t x0, uint32_t y0, uint32_t w, uint32_t h, const uint16_t *data, bool swap = false); + // Push the sprite to the TFT screen, this fn calls pushImage() in the TFT class. + // Optionally a "transparent" colour can be defined, pixels of that colour will not be rendered void pushSprite(int32_t x, int32_t y); void pushSprite(int32_t x, int32_t y, uint16_t transparent); int16_t drawChar(unsigned int uniCode, int x, int y, int font), drawChar(unsigned int uniCode, int x, int y); - - int16_t height(void), - width(void); + // Return the width and height of the sprite + int16_t width(void), + height(void); + + // Used by print class to print text to cursor position size_t write(uint8_t); private: @@ -613,13 +641,17 @@ class TFT_eSprite : public TFT_eSPI { protected: - uint16_t *_img; - uint8_t *_img8; - bool _created, _bpp16; + uint16_t *_img; // pointer to 16 bit sprite + uint8_t *_img8; // pointer to 8 bit sprite + bool _created, _bpp16; // created and bits per pixel depth flags - int32_t _icursor_x, _icursor_y, _xs, _ys, _xe, _ye, _xptr, _yptr; + int32_t _icursor_x, _icursor_y; + int32_t _xs, _ys, _xe, _ye, _xptr, _yptr; // for setWindow + int32_t _sx, _sy; // x,y for scroll zone + uint32_t _sw, _sh; // w,h for scroll zone + uint32_t _scolor; // gap fill colour for scroll zone - int32_t _iwidth, _iheight; + int32_t _iwidth, _iheight; // Sprite image width and height }; diff --git a/examples/Sprite/Sprite_scroll/Sprite_scroll.ino b/examples/Sprite/Sprite_scroll/Sprite_scroll.ino new file mode 100644 index 0000000..dfe8ad9 --- /dev/null +++ b/examples/Sprite/Sprite_scroll/Sprite_scroll.ino @@ -0,0 +1,118 @@ +/* + Sketch to show scrolling of the graphics in sprites. + Scrolling in this way moves the pixels in a defined rectangle + within the Sprite. By defalt the whole sprite is scrolled. + The gap left by scrolling is filled with a defined colour. + + Example for library: + https://github.com/Bodmer/TFT_eSPI + + A Sprite is notionally an invisible graphics screen that is + kept in the processors RAM. Graphics can be drawn into the + Sprite just as it can be drawn directly to the screen. Once + the Sprite is completed it can be plotted onto the screen in + any position. If there is sufficient RAM then the Sprite can + be the same size as the screen and used as a frame buffer. + + A 16 bit Sprite occupies (2 * width * height) bytes in RAM. + + An 8 bit Sprite occupies (width * height) bytes in RAM. + +*/ + +#include + +TFT_eSPI tft = TFT_eSPI(); + +TFT_eSprite graph1 = TFT_eSprite(&tft); // Sprite object graph1 + +TFT_eSprite stext1 = TFT_eSprite(&tft); // Sprite object stext1 + +TFT_eSprite stext2 = TFT_eSprite(&tft); // Sprite object stext2 + +int graphVal = 1; +int delta = 1; +int grid = 0; +int tcount = 0; + +//========================================================================================== +void setup() { + tft.init(); + tft.fillScreen(TFT_BLACK); + + // Create a sprite for the graph + graph1.setColorDepth(8); + graph1.createSprite(128, 61); + graph1.fillSprite(TFT_BLUE); // Note: Sprite is filled with black when created + + // The scroll area is set to the full sprite size upon creation of the sprite + // but we can change that by defining a smaller area using "setScrollRect()"if needed + // parameters are x,y,w,h,color as in drawRect(), the color fills the gap left by scrolling + //graph1.setScrollRect(64, 0, 64, 61, TFT_DARKGREY); // Try this line to change the graph scroll area + + // Create a sprite for the scrolling numbers + stext1.setColorDepth(8); + stext1.createSprite(32, 64); + stext1.fillSprite(TFT_BLUE); // Fill sprite with blue + stext1.setScrollRect(0, 0, 32, 64, TFT_BLUE); // here we set scroll gap fill color to blue + stext1.setTextColor(TFT_WHITE); // White text, no background + stext1.setTextDatum(BR_DATUM); // Bottom right coordinate datum + + // Create a sprite for Hello World + stext2.setColorDepth(8); + stext2.createSprite(80, 16); + stext2.fillSprite(TFT_DARKGREY); + stext2.setScrollRect(0, 0, 40, 16, TFT_DARKGREY); // Scroll the "Hello" in the first 40 pixels + stext2.setTextColor(TFT_WHITE); // White text, no background +} + +//========================================================================================== +void loop() { + // Draw point in graph1 sprite at far right edge (this will scroll left later) + graph1.drawFastVLine(127,60-graphVal,2,TFT_YELLOW); // draw 2 pixel point on graph + + // Draw number in stext1 sprite at 31,63 (bottom right datum set) + stext1.drawNumber(graphVal, 31, 63, 2); // plot value in font 2 + + // Push the sprites onto the TFT at specied coordinates + graph1.pushSprite(0, 0); + stext1.pushSprite(0, 64); + stext2.pushSprite(40, 70); + + // Change the value to plot + graphVal+=delta; + + // If the value reaches a limit, then change delta of value + if (graphVal >= 60) delta = -1; // ramp down value + else if (graphVal <= 1) delta = +1; // ramp up value + + delay(50); // wait so things do not scroll too fast + + // Now scroll the sprites scroll(dt, dy) where: + // dx is pixels to scroll, left = negative value, right = positive value + // dy is pixels to scroll, up = negative value, down = positive value + graph1.scroll(-1, 0); // scroll graph 1 pixel left, 0 up/down + stext1.scroll(0,-16); // scroll stext 0 pixels left/right, 16 up + stext2.scroll(1); // scroll stext 1 pixel right, up/down default is 0 + + // Draw the grid on far right edge of sprite as graph has now moved 1 pixel left + grid++; + if (grid >= 10) + { // Draw a vertical line if we have scrolled 10 times (10 pixels) + grid = 0; + graph1.drawFastVLine(127, 0, 61, TFT_NAVY); // draw line on graph + } + else + { // Otherwise draw points spaced 10 pixels for the horizontal grid lines + for (int p = 0; p <= 60; p += 10) graph1.drawPixel(127, p, TFT_NAVY); + } + + tcount--; + if (tcount <=0) + { // If we have scrolled 40 pixels the redraw text + tcount = 40; + stext2.drawString("Hello World", 6, 0, 2); // draw at 6,0 in sprite, font 2 + } + +} // Loop back and do it all again +//========================================================================================== diff --git a/library.json b/library.json index f7d7113..1de946d 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "TFT_eSPI", - "version": "0.17.17", + "version": "0.17.19", "keywords": "TFT, ESP8266, NodeMCU, ESP32, ILI9341, ST7735, ILI9163, S6D02A1, ILI9486", "description": "A TFT SPI graphics library for ESP8266", "repository": diff --git a/library.properties b/library.properties index 787cc6f..59804c6 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=TFT_eSPI -version=0.17.17 +version=0.17.19 author=Bodmer maintainer=Bodmer sentence=A fast TFT library for ESP8266 processors and the Arduino IDE From abd5f34cd91e05b97d32a217ef4b8e2b10df76e1 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Thu, 14 Dec 2017 01:17:47 +0000 Subject: [PATCH 050/150] Add license file in response to #65 --- license.txt | 128 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 license.txt diff --git a/license.txt b/license.txt new file mode 100644 index 0000000..5ea280b --- /dev/null +++ b/license.txt @@ -0,0 +1,128 @@ +The original starting point for this library was the Adafruit_ILI9341 +library, as they were in January 2015. + +The licence for those libraries is MIT. + +The first evolution of the library that led to TFT_eSPI is recorded here: + +https://www.instructables.com/id/Arduino-TFT-display-and-font-library/ + +Adafruit_ILI9341 ORIGINAL LIBRARY HEADER: + +vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvStartvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv + This is our library for the Adafruit ILI9341 Breakout and Shield + ----> http://www.adafruit.com/products/1651 + + Check out the links above for our tutorials and wiring diagrams + These displays use SPI to communicate, 4 or 5 pins are required to + interface (RST is optional) + Adafruit invests time and resources providing this open source code, + please support Adafruit and open-source hardware by purchasing + products from Adafruit! + + Written by Limor Fried/Ladyada for Adafruit Industries. + MIT license, all text above must be included in any redistribution + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^End^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + +Selected functions from the Adafruit_GFX library (as it was in 2015) have +been imported into the TFT_eSPI.cpp file and modified to improve +perfromance, add features and make them compatible with the ESP8266 and +ESP32. + +The fonts from the Adafruit_GFX and Button functions were added later. +The fonts can be found with the license.txt file in the "Fonts\GFXFF" +folder. + +The Adafruit_GFX functions are covered by the BSD licence. + +Adafruit_GFX ORIGINAL LIBRARY LICENSE: + +vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvStartvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv + +Software License Agreement (BSD License) + +Copyright (c) 2012 Adafruit Industries. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +- Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. +- Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^End^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Due to the evolution of the TFT_eSPI library the orignal code may no longer +be recognisable, however in ost cases the function names can be used as a +reference point since the aim is to retain a level of compatibility with +the popular Adafruit_GFX graphics functions. + +Contributions from other authors are recorded on GitHub: +https://github.com/Bodmer/TFT_eSPI + +The major addition to the original library was the addition of fast +rendering proportional fonts of different sizes as documented here: + +https://www.instructables.com/id/Arduino-TFT-display-and-font-library/ + +The larger fonts are "Run Length Encoded (RLE)", this was done to +reduce the font memory footprint for AVR processors that have limited +FLASH, with the added benefit of a significant improvement in rendering +speed. + +In 2016 the library evolved significantly to support the ESP8266 and then +the ESP32. In 2017 new Touch Screen functions were added and a new Sprite +class called TFT_eSprite to permit "flicker free" screen updates of complex +graphics. + +Many of the example sketches are original work, that contain code created +for my own projects. For all the original code the FreeBSD licence applies +and is compatible with the GNU GPL. + +vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvStartvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +Software License Agreement (FreeBSD License) + +Copyright (c) 2017 Bodmer (https://github.com/Bodmer) + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +The views and conclusions contained in the software and documentation are those +of the authors and should not be interpreted as representing official policies, +either expressed or implied, of the FreeBSD Project. +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^End^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ From 7569f18c012eaa6961f46e8959be844961a0c07a Mon Sep 17 00:00:00 2001 From: Bodmer Date: Thu, 14 Dec 2017 01:21:14 +0000 Subject: [PATCH 051/150] typo --- license.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/license.txt b/license.txt index 5ea280b..3272382 100644 --- a/license.txt +++ b/license.txt @@ -1,5 +1,5 @@ The original starting point for this library was the Adafruit_ILI9341 -library, as they were in January 2015. +library in January 2015. The licence for those libraries is MIT. From d708012d5ffff3f1db8b5af638d1ba53cd1cd9bb Mon Sep 17 00:00:00 2001 From: Bodmer Date: Thu, 14 Dec 2017 01:21:51 +0000 Subject: [PATCH 052/150] typo --- license.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/license.txt b/license.txt index 3272382..552e725 100644 --- a/license.txt +++ b/license.txt @@ -1,7 +1,7 @@ The original starting point for this library was the Adafruit_ILI9341 library in January 2015. -The licence for those libraries is MIT. +The licence for that libraries is MIT. The first evolution of the library that led to TFT_eSPI is recorded here: From 6092522a366fb2798b4b07d5950f172504e893be Mon Sep 17 00:00:00 2001 From: Bodmer Date: Thu, 14 Dec 2017 01:22:26 +0000 Subject: [PATCH 053/150] typo 3 - too much of a hurry! --- license.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/license.txt b/license.txt index 552e725..dda745d 100644 --- a/license.txt +++ b/license.txt @@ -1,7 +1,7 @@ The original starting point for this library was the Adafruit_ILI9341 library in January 2015. -The licence for that libraries is MIT. +The licence for that library is MIT. The first evolution of the library that led to TFT_eSPI is recorded here: From 907401d7cb751c887cc7dde7fc012945d23436ad Mon Sep 17 00:00:00 2001 From: Bodmer Date: Thu, 14 Dec 2017 02:03:16 +0000 Subject: [PATCH 054/150] Doh! --- license.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/license.txt b/license.txt index dda745d..befa9a4 100644 --- a/license.txt +++ b/license.txt @@ -69,7 +69,7 @@ POSSIBILITY OF SUCH DAMAGE. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^End^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Due to the evolution of the TFT_eSPI library the orignal code may no longer -be recognisable, however in ost cases the function names can be used as a +be recognisable, however in most cases the function names can be used as a reference point since the aim is to retain a level of compatibility with the popular Adafruit_GFX graphics functions. From 16f6b9cc0b673e523a9099385145640439dd9919 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Fri, 15 Dec 2017 11:15:38 +0000 Subject: [PATCH 055/150] Update time --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 580e6ff..f788d19 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ A Sprite is notionally an invisible graphics screen that is kept in the processo One or more sprites can be created, a sprite can be any width and height, limited only by available RAM. The RAM needed for a 16 bit colour depth Sprite is (2 x width x height) bytes, for a Sprite with 8 bit colour depth the RAM needed is (width x height) bytes. Sprites can be created and deleted dynamically as needed in the sketch, this means RAM can be freed up after the sprite has been plotted on the screen, more RAM intensive WiFi based code can then be run and normal graphics operations still work. -Drawing graphics into a sprite is very fast, for those familiar with the Adafruit "graphicstest" example, this whole test completes in less than 27ms in a 160x128 sprite. Examples of sprite use can be found in the "examples/Sprite" folder. +Drawing graphics into a sprite is very fast, for those familiar with the Adafruit "graphicstest" example, this whole test completes in 18ms in a 160x128 sprite. Examples of sprite use can be found in the "examples/Sprite" folder. Sprites can be plotted to the TFT with one colour being specified as "transparent", see Transparent_Sprite_Demo example. @@ -22,7 +22,7 @@ The library supports SPI overlap on the ESP8266 so the TFT screen can share MOSI The library contains proportional fonts, different sizes can be enabled/disabled at compile time to optimise the use of FLASH memory. The library has been tested with the NodeMCU (ESP8266 based) and an ESP32 demo board. -The library is based on the Adafruit GFX and Adafruit driver libraries and the aim is to retain compatibility. Significant additions have been made to the library to boost the speed for ESP8266/ESP32 processors (it is typically 3 to 10 times faster) and to add new features. The new graphics functions include different size proportional fonts and formatting features. There are a significant number of example sketches to demonstrate the different features. +The library is based on the Adafruit GFX and Adafruit driver libraries and the aim is to retain compatibility. Significant additions have been made to the library to boost the speed for ESP8266/ESP32 processors (it is typically 3 to 10 times faster) and to add new features. The new graphics functions include different size proportional fonts and formatting features. There are lots of example sketches to demonstrate the different features. Configuration of the library font selections, pins used to interface with the TFT and other features is made by editting the User_Setup.h file in the library folder. Fonts and features can easily be disabled by commenting out lines. From ca13757c9bc1543b76d84c6108475ccfb9984058 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Sat, 16 Dec 2017 01:36:16 +0000 Subject: [PATCH 056/150] Allow larger images width or height over 127 pixels got converted to a negative number! --- examples/160 x 128/Flash_Bitmap2/Flash_Bitmap2.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/160 x 128/Flash_Bitmap2/Flash_Bitmap2.ino b/examples/160 x 128/Flash_Bitmap2/Flash_Bitmap2.ino index 3cf517a..09fe021 100644 --- a/examples/160 x 128/Flash_Bitmap2/Flash_Bitmap2.ino +++ b/examples/160 x 128/Flash_Bitmap2/Flash_Bitmap2.ino @@ -80,7 +80,7 @@ void loop() // Draw array "icon" of defined width and height at coordinate x,y // Maximum icon size is 255x255 pixels to avoid integer overflow -void drawIcon(const unsigned short* icon, int16_t x, int16_t y, int8_t width, int8_t height) { +void drawIcon(const unsigned short* icon, int16_t x, int16_t y, uint16_t width, uint16_t height) { uint16_t pix_buffer[BUFF_SIZE]; // Pixel buffer (16 bits per pixel) From 0dacb156ed40dff8952c96c6ee6a3327627d4c71 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Sat, 16 Dec 2017 01:37:22 +0000 Subject: [PATCH 057/150] Allow larger images --- examples/320 x 240/TFT_Flash_Bitmap/TFT_Flash_Bitmap.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/320 x 240/TFT_Flash_Bitmap/TFT_Flash_Bitmap.ino b/examples/320 x 240/TFT_Flash_Bitmap/TFT_Flash_Bitmap.ino index 081087c..780dab1 100644 --- a/examples/320 x 240/TFT_Flash_Bitmap/TFT_Flash_Bitmap.ino +++ b/examples/320 x 240/TFT_Flash_Bitmap/TFT_Flash_Bitmap.ino @@ -76,7 +76,7 @@ void loop() // Draw array "icon" of defined width and height at coordinate x,y // Maximum icon size is 255x255 pixels to avoid integer overflow -void drawIcon(const unsigned short* icon, int16_t x, int16_t y, int8_t width, int8_t height) { +void drawIcon(const unsigned short* icon, int16_t x, int16_t y, uint16_t width, uint16_t height) { uint16_t pix_buffer[BUFF_SIZE]; // Pixel buffer (16 bits per pixel) From 5ed1bb6777fa5af96ba2c2d76cc12a579f3cee6e Mon Sep 17 00:00:00 2001 From: Bodmer Date: Sat, 16 Dec 2017 01:38:09 +0000 Subject: [PATCH 058/150] Allow larger images --- examples/480 x 320/Flash_Bitmap/Flash_Bitmap.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/480 x 320/Flash_Bitmap/Flash_Bitmap.ino b/examples/480 x 320/Flash_Bitmap/Flash_Bitmap.ino index 0329e5f..fb58180 100644 --- a/examples/480 x 320/Flash_Bitmap/Flash_Bitmap.ino +++ b/examples/480 x 320/Flash_Bitmap/Flash_Bitmap.ino @@ -83,7 +83,7 @@ void loop() // Draw array "icon" of defined width and height at coordinate x,y // Maximum icon size is 255x255 pixels to avoid integer overflow -void drawIcon(const unsigned short* icon, int16_t x, int16_t y, int8_t width, int8_t height) { +void drawIcon(const unsigned short* icon, int16_t x, int16_t y, uint16_t width, uint16_t height) { uint16_t pix_buffer[BUFF_SIZE]; // Pixel buffer (16 bits per pixel) From ef543a652ca7012a96eaeaa74ef2071a1c624813 Mon Sep 17 00:00:00 2001 From: atanisoft Date: Sat, 16 Dec 2017 06:50:23 -0800 Subject: [PATCH 059/150] Update User_Setup_Select.h Adding ability to load settings from the calling program rather than modifying the library for each project that uses it. This is mostly for PlatformIO IDE which manages the libraries differently than Adruino IDE. --- User_Setup_Select.h | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/User_Setup_Select.h b/User_Setup_Select.h index 71050e4..3875450 100644 --- a/User_Setup_Select.h +++ b/User_Setup_Select.h @@ -15,7 +15,7 @@ // Customised User_Setup files are stored in the "User_Setups" folder. // Only ONE line should be uncommented. Add extra lines and files as needed. - +#ifndef USER_SETUP_LOADED #include // Default setup is root library folder //#include // Setup file configured for my ILI9341 @@ -31,8 +31,7 @@ //#include // Setup file configured for my stock RPi TFT with touch //#include // Setup file template for copying/editting - - +#endif ///////////////////////////////////////////////////////////////////////////////////// // // @@ -74,4 +73,4 @@ #define PIN_HWCS 0 #define PIN_D11 9 -#define PIN_D12 10 \ No newline at end of file +#define PIN_D12 10 From 2e84218ecbd5105dd0007268d87de70f9d3529fc Mon Sep 17 00:00:00 2001 From: Bodmer Date: Thu, 21 Dec 2017 23:16:11 +0000 Subject: [PATCH 060/150] Add touch pressure hysterisis and user defined sensitivity getTouch accepts a third optional pressure threshold, default is 600, increasing means harder presses are needed. getTouch(uint16_t *x, uint16_t *y, uint16_t threshold = 600); --- TFT_eSPI.cpp | 26 ++++++++++++++++++++------ TFT_eSPI.h | 4 +++- library.json | 2 +- library.properties | 2 +- 4 files changed, 25 insertions(+), 9 deletions(-) diff --git a/TFT_eSPI.cpp b/TFT_eSPI.cpp index 641874a..15578d5 100644 --- a/TFT_eSPI.cpp +++ b/TFT_eSPI.cpp @@ -4114,10 +4114,22 @@ uint8_t TFT_eSPI::validTouch(uint16_t *x, uint16_t *y, uint16_t threshold){ ** Description: read callibrated position. Return false if not pressed. ***************************************************************************************/ #define Z_THRESHOLD 350 // Touch pressure threshold for validating touches -uint8_t TFT_eSPI::getTouch(uint16_t *x, uint16_t *y){ +uint8_t TFT_eSPI::getTouch(uint16_t *x, uint16_t *y, uint16_t threshold){ uint16_t x_tmp, y_tmp, xx, yy; - if(!validTouch(&x_tmp, &y_tmp, Z_THRESHOLD)) return false; + if (threshold<20) threshold = 20; + if (_pressTime > millis()) threshold=20; + + byte n = 5; + byte valid = 0; + while (n--) + { + if (validTouch(&x_tmp, &y_tmp, threshold)) valid++;; + } + + if (valid<1) { _pressTime = 0; return false; } + + _pressTime = millis() + 50; if(!touchCalibration_rotate){ xx=(x_tmp-touchCalibration_x0)*_width/touchCalibration_x1; @@ -4135,11 +4147,13 @@ uint8_t TFT_eSPI::getTouch(uint16_t *x, uint16_t *y){ yy = _height - yy; } - if (xx >= _width || yy >= _height) return false; + if (xx >= _width || yy >= _height) return valid; - *x = xx; - *y = yy; - return true; + _pressX = xx; + _pressY = yy; + *x = _pressX; + *y = _pressY; + return valid; } /*************************************************************************************** diff --git a/TFT_eSPI.h b/TFT_eSPI.h index ae7f3c0..c291c92 100644 --- a/TFT_eSPI.h +++ b/TFT_eSPI.h @@ -458,7 +458,7 @@ class TFT_eSPI : public Print { // These are associated with the Touch Screen handlers uint8_t getTouchRaw(uint16_t *x, uint16_t *y); uint16_t getTouchRawZ(void); - uint8_t getTouch(uint16_t *x, uint16_t *y); + uint8_t getTouch(uint16_t *x, uint16_t *y, uint16_t threshold = 600); void calibrateTouch(uint16_t *data, uint32_t color_fg, uint32_t color_bg, uint8_t size); void setTouch(uint16_t *data); @@ -488,6 +488,8 @@ class TFT_eSPI : public Print { // Initialise with example calibration values so processor does not crash if setTouch() not called in setup() uint16_t touchCalibration_x0 = 300, touchCalibration_x1 = 3600, touchCalibration_y0 = 300, touchCalibration_y1 = 3600; uint8_t touchCalibration_rotate = 1, touchCalibration_invert_x = 2, touchCalibration_invert_y = 0; + uint32_t _pressTime; + uint16_t _pressX, _pressY; protected: diff --git a/library.json b/library.json index 1de946d..d189858 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "TFT_eSPI", - "version": "0.17.19", + "version": "0.17.20", "keywords": "TFT, ESP8266, NodeMCU, ESP32, ILI9341, ST7735, ILI9163, S6D02A1, ILI9486", "description": "A TFT SPI graphics library for ESP8266", "repository": diff --git a/library.properties b/library.properties index 59804c6..61ea335 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=TFT_eSPI -version=0.17.19 +version=0.17.20 author=Bodmer maintainer=Bodmer sentence=A fast TFT library for ESP8266 processors and the Arduino IDE From 936b5cccc72dfb8a82b309a85b59be3a23b7c3d1 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Fri, 22 Dec 2017 00:02:31 +0000 Subject: [PATCH 061/150] Add default touch detect threshold --- TFT_eSPI.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TFT_eSPI.h b/TFT_eSPI.h index c291c92..8b521f8 100644 --- a/TFT_eSPI.h +++ b/TFT_eSPI.h @@ -484,7 +484,7 @@ class TFT_eSPI : public Print { uint32_t lastColor = 0xFFFF; // These are associated with the Touch Screen handlers - uint8_t validTouch(uint16_t *x, uint16_t *y, uint16_t threshold); + uint8_t validTouch(uint16_t *x, uint16_t *y, uint16_t threshold = 600); // Initialise with example calibration values so processor does not crash if setTouch() not called in setup() uint16_t touchCalibration_x0 = 300, touchCalibration_x1 = 3600, touchCalibration_y0 = 300, touchCalibration_y1 = 3600; uint8_t touchCalibration_rotate = 1, touchCalibration_invert_x = 2, touchCalibration_invert_y = 0; From b536af5062038d45ad83842814477bf44196ee2a Mon Sep 17 00:00:00 2001 From: Bodmer Date: Fri, 22 Dec 2017 00:50:24 +0000 Subject: [PATCH 062/150] Fix bug If touch not used dummy getTouch missing the new threshold parameter. --- TFT_eSPI.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TFT_eSPI.cpp b/TFT_eSPI.cpp index 15578d5..04007fc 100644 --- a/TFT_eSPI.cpp +++ b/TFT_eSPI.cpp @@ -4305,7 +4305,7 @@ uint8_t TFT_eSPI::validTouch(uint16_t *x, uint16_t *y, uint16_t threshold){ ** Function name: getTouch ** Description: read callibrated position. Return false if not pressed. ***************************************************************************************/ -uint8_t TFT_eSPI::getTouch(uint16_t *x, uint16_t *y){ +uint8_t TFT_eSPI::getTouch(uint16_t *x, uint16_t *y, uint16_t threshold){ return true; } From 5e98ce5294f6c19d712235f0d00850bc1c236399 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Wed, 27 Dec 2017 14:57:01 +0000 Subject: [PATCH 063/150] Avoid divide by zero crash if touch screen not fitted or not wired up --- TFT_eSPI.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/TFT_eSPI.cpp b/TFT_eSPI.cpp index 04007fc..cf7fb64 100644 --- a/TFT_eSPI.cpp +++ b/TFT_eSPI.cpp @@ -4249,6 +4249,11 @@ void TFT_eSPI::calibrateTouch(uint16_t *parameters, uint32_t color_fg, uint32_t touchCalibration_x1 -= touchCalibration_x0; touchCalibration_y1 -= touchCalibration_y0; + if(touchCalibration_x0 == 0) touchCalibration_x0 = 1; + if(touchCalibration_x1 == 0) touchCalibration_x1 = 1; + if(touchCalibration_y0 == 0) touchCalibration_y0 = 1; + if(touchCalibration_y1 == 0) touchCalibration_y1 = 1; + // export parameters, if pointer valid if(parameters != NULL){ parameters[0] = touchCalibration_x0; @@ -4270,6 +4275,11 @@ void TFT_eSPI::setTouch(uint16_t *parameters){ touchCalibration_y0 = parameters[2]; touchCalibration_y1 = parameters[3]; + if(touchCalibration_x0 == 0) touchCalibration_x0 = 1; + if(touchCalibration_x1 == 0) touchCalibration_x1 = 1; + if(touchCalibration_y0 == 0) touchCalibration_y0 = 1; + if(touchCalibration_y1 == 0) touchCalibration_y1 = 1; + touchCalibration_rotate = parameters[4] & 0x01; touchCalibration_invert_x = parameters[4] & 0x02; touchCalibration_invert_y = parameters[4] & 0x04; From 1cc63795e6000c50953a2c62140d712fe4bf1bc0 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Fri, 29 Dec 2017 17:29:34 +0000 Subject: [PATCH 064/150] Add future developments section --- README.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/README.md b/README.md index f788d19..5fffa09 100644 --- a/README.md +++ b/README.md @@ -26,3 +26,23 @@ The library is based on the Adafruit GFX and Adafruit driver libraries and the a Configuration of the library font selections, pins used to interface with the TFT and other features is made by editting the User_Setup.h file in the library folder. Fonts and features can easily be disabled by commenting out lines. +## The future... ## + +**1. Performance improvements** +I have made some changes that will be uploaded soon that improves sprite and image rendering performance by up to 3x faster on the ESP8266. These updates are currently being tested/debugged. + +**2. Anti-aliased fonts** + +I have been experimenting with anti-aliased font files in "vlw" format generated by the free [Processing IDE](https://processing.org/). This IDE can be used to generate font files from you computers font set and include **any** Unicode characters. This means Greek, Japanese and any other UTF-16 glyphs can be used. + +It would be possible to compress the vlw font files but the rendering performance to a TFT is still good when storing the font file(s) in SPIFFS. + +Here are some screen grabs (from an ILI9341 240x320 pixel TFT) showing 32pt characters. Can you spot the difference between anti-aliased and 2 colour "bitmap" fonts? + +![Anti-aliased fonts](https://i.imgur.com/HjGbUhG.png) + +Here is another screenshot, showing the anti-aliased Hiragana character Unicode block (0x3041 to 0x309F) in 24pt from the Microsoft Yahei font: + +![Hiragana glyphs](https://i.imgur.com/jeXf2st.png) + +Currently these are generated from a sketch, but when I have the Alpha blending sorted for colour backgrounds the plan is to build the rendering code into the TFT_eSPI library. Watch this space " " for updates! From 53cc05d9e3996b2f0c39aa2902d42f6770601333 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Fri, 29 Dec 2017 20:08:58 +0000 Subject: [PATCH 065/150] Correct typo and mark-up format errors --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 5fffa09..6a109c8 100644 --- a/README.md +++ b/README.md @@ -29,11 +29,12 @@ Configuration of the library font selections, pins used to interface with the TF ## The future... ## **1. Performance improvements** + I have made some changes that will be uploaded soon that improves sprite and image rendering performance by up to 3x faster on the ESP8266. These updates are currently being tested/debugged. **2. Anti-aliased fonts** -I have been experimenting with anti-aliased font files in "vlw" format generated by the free [Processing IDE](https://processing.org/). This IDE can be used to generate font files from you computers font set and include **any** Unicode characters. This means Greek, Japanese and any other UTF-16 glyphs can be used. +I have been experimenting with anti-aliased font files in "vlw" format generated by the free [Processing IDE](https://processing.org/). This IDE can be used to generate font files from your computer's font set and include **any** Unicode characters. This means Greek, Japanese and any other UTF-16 glyphs can be used. It would be possible to compress the vlw font files but the rendering performance to a TFT is still good when storing the font file(s) in SPIFFS. From aa2c79a25f2a0eeeb50141320c9111fc5b80273d Mon Sep 17 00:00:00 2001 From: Bodmer Date: Mon, 8 Jan 2018 23:19:42 +0000 Subject: [PATCH 066/150] Performance enhancements, new functions, bug fixes 1. Ability to swap bytes in pushColors() and pushImage() 2. pushImage() will handle FLASH stroed images 3. pushImage() will accept a transparent color 4. pushRect() deprecated but still works 5. setSwapBytes() to set swapping bytes in color for pushImage() and pushColors() true or false 6. getSwapBytes() returns swap bytes parameter 7. fillCircle() and fillCircleHelper() changed to use horizontal lines to speed up plotting in a sprite 8. fillTriangle bug fix by moving spi_begin() 9 Small performance improvement to setAddrWindow() 10. pushColor() bug fix for swapped bytes 11. pushColors() performance improvement for ESP8266 and add optional byte swap parameter, accepts higher pixel count 12. move spi_begin() later in drawLine() incase fn returns early 13. add spi_end() in drawLine incdae return occurs early 14. Add color332() fn to convert 16 bit to 8 bit colour 15. Sprite: createSprite() checks if sprite already created and returns pointer if it does 16. Sprite: one extra "off-screen pixel added to a sprite to point out-of-bounds setWindow coords to this avoids extra bounds checks in push/write color 17. Sprite: setColorDepth allows changing of depthe for existing sprtie and returns a new pointer to the new sprite 18. Sprite: pushImage() accepts a swapped byte parameter 19. Sprite: setSwapBytes() to set swapping bytes in color for pushImage() and pushColors() true or false 20. Sprite: getSwapBytes() returns swap bytes parameter 21. Sprite: setWindow deals with duff out of sprite coords. 22. Sprite: bug in draw char corrected which could draw too many over-writing pixels --- Keywords.txt | 5 +- TFT_eSPI.cpp | 750 ++++++++++++++++++++++++++++++++------------ TFT_eSPI.h | 78 ++--- User_Setup_Select.h | 13 +- library.properties | 2 +- 5 files changed, 603 insertions(+), 245 deletions(-) diff --git a/Keywords.txt b/Keywords.txt index 86f33fb..dba1241 100644 --- a/Keywords.txt +++ b/Keywords.txt @@ -50,12 +50,15 @@ readcommand32 KEYWORD2 readPixel KEYWORD2 readRect KEYWORD2 pushRect KEYWORD2 -pushImage KEYWORD2 +pushImage KEYWORD2 +setSwapBytes KEYWORD2 +getSwapBytes KEYWORD2 readRectRGB KEYWORD2 getRotation KEYWORD2 getTextDatum KEYWORD2 fontsLoaded KEYWORD2 color565 KEYWORD2 +color332 KEYWORD2 drawChar KEYWORD2 drawNumber KEYWORD2 drawFloat KEYWORD2 diff --git a/TFT_eSPI.cpp b/TFT_eSPI.cpp index cf7fb64..ea82c7b 100644 --- a/TFT_eSPI.cpp +++ b/TFT_eSPI.cpp @@ -1,10 +1,6 @@ /*************************************************** Arduino TFT graphics library targetted at ESP8266 - based boards. (ESP32 support is planned!) - - This library has been derived from the Adafruit_GFX - library and the associated driver library. See text - at the end of this file. + and ESP32 based boards. This is a standalone library that contains the hardware driver, the graphics funtions and the @@ -122,6 +118,8 @@ TFT_eSPI::TFT_eSPI(int16_t w, int16_t h) textdatum = TL_DATUM; // Top Left text alignment is default fontsloaded = 0; + _swapBytes = false; // Do not swap colour bytes by default + locked = true; // ESP32 transaction mutex lock flags inTransaction = false; @@ -512,25 +510,9 @@ void TFT_eSPI::readRect(uint32_t x, uint32_t y, uint32_t w, uint32_t h, uint16_t ***************************************************************************************/ void TFT_eSPI::pushRect(uint32_t x, uint32_t y, uint32_t w, uint32_t h, uint16_t *data) { - if ((x >= _width) || (y >= _height) || (w == 0) || (h == 0)) return; - - spi_begin(); - - setAddrWindow(x, y, x + w - 1, y + h - 1); // Sets CS low and sent RAMWR - - uint32_t len = w * h * 2; - - // Check alignment of pointer to 32 bits - uint8_t offset = (uint32_t)data & 0x3; - if (offset > len) offset = len; - - // Make pointer 32 bit align using immune call then use the faster writeBytes() - if (offset) { SPI.writePattern((uint8_t*)data, offset, 1); len -= offset; data += offset; } - if (len) SPI.writeBytes((uint8_t*)data, len); - - CS_H; - - spi_end(); + // Function deprecated, remains for backwards compatibility + // pushImage() is better as it will crop partly off-screen image blocks + pushImage(x, y, w, h, data); } @@ -550,39 +532,28 @@ void TFT_eSPI::pushImage(int32_t x, int32_t y, uint32_t w, uint32_t h, uint16_t if (x < 0) { dw += x; dx = -x; x = 0; } if (y < 0) { dh += y; dy = -y; y = 0; } - + if ((x + w) > _width ) dw = _width - x; if ((y + h) > _height) dh = _height - y; if (dw < 1 || dh < 1) return; spi_begin(); + inTransaction = true; setAddrWindow(x, y, x + dw - 1, y + dh - 1); // Sets CS low and sent RAMWR data += dx + dy * w; - dw <<= 1; - while (dh--) { - // Check alignment of pointer to 32 bits - uint8_t offset = (uint32_t)data & 0x3; - if (offset > dw) offset = dw; - - int32_t len = dw; - uint8_t* ptr = (uint8_t*)data; - - // Make pointer 32 bit align using immune call then use the faster writeBytes() - if (offset) { SPI.writePattern(ptr, offset, 1); len -= offset; ptr += offset; } - - if (len) SPI.writeBytes(ptr, len); - + pushColors(data, dw, _swapBytes); data += w; } CS_H; + inTransaction = false; spi_end(); } @@ -609,30 +580,45 @@ void TFT_eSPI::pushImage(int32_t x, int32_t y, uint32_t w, uint32_t h, uint16_t if (dw < 1 || dh < 1) return; spi_begin(); + inTransaction = true; data += dx + dy * w; int32_t xe = x + dw - 1, ye = y + dh - 1; - transp = transp >> 8 | transp << 8; + uint16_t lineBuf[dw]; + + if (!_swapBytes) transp = transp >> 8 | transp << 8; + while (dh--) { int32_t len = dw; uint16_t* ptr = data; int32_t px = x; boolean move = true; - + uint16_t np = 0; + while (len--) { if (transp != *ptr) { if (move) { move = false; setAddrWindow(px, y, xe, ye); } - SPI.write16(*ptr>>8 | *ptr<<8); + lineBuf[np] = *ptr; + np++; + } + else + { + move = true; + if (np) + { + pushColors((uint16_t*)lineBuf, np, _swapBytes); + np = 0; + } } - else move = true; px++; ptr++; } + if (np) pushColors((uint16_t*)lineBuf, np, _swapBytes); y++; data += w; @@ -640,18 +626,17 @@ void TFT_eSPI::pushImage(int32_t x, int32_t y, uint32_t w, uint32_t h, uint16_t CS_H; + inTransaction = false; spi_end(); } -//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< TEMPORARY TEST + /*************************************************************************************** ** Function name: pushImage - for FLASH (PROGMEM) stored images -** Description: plot 16 bit sprite or image with 1 colour being transparent +** Description: plot 16 bit image ***************************************************************************************/ -void TFT_eSPI::pushImage(int32_t x, int32_t y, uint32_t w, uint32_t h, const uint16_t *data, uint16_t transp, bool swap) +void TFT_eSPI::pushImage(int32_t x, int32_t y, uint32_t w, uint32_t h, const uint16_t *data) { - // Swap=true swaps the two bytes in the color to cater for big+little endian formats - // default is false if parameter is missing if ((x >= (int32_t)_width) || (y >= (int32_t)_height)) return; @@ -669,12 +654,77 @@ void TFT_eSPI::pushImage(int32_t x, int32_t y, uint32_t w, uint32_t h, const uin if (dw < 1 || dh < 1) return; spi_begin(); + inTransaction = true; + + data += dx + dy * w; + + uint16_t buffer[64]; + uint16_t* pix_buffer = buffer; + + setAddrWindow(x, y, x + dw - 1, y + dh - 1); + + // Work out the number whole buffers to send + uint16_t nb = (dw * dh) / 64; + + // Fill and send "nb" buffers to TFT + for (int i = 0; i < nb; i++) { + for (int j = 0; j < 64; j++) { + pix_buffer[j] = pgm_read_word(&data[i * 64 + j]); + } + pushColors(pix_buffer, 64, !_swapBytes); + } + + // Work out number of pixels not yet sent + uint16_t np = (dw * dh) % 64; + + // Send any partial buffer left over + if (np) { + for (int i = 0; i < np; i++) + { + pix_buffer[i] = pgm_read_word(&data[nb * 64 + i]); + } + pushColors(pix_buffer, np, !_swapBytes); + } + + CS_H; + + inTransaction = false; + spi_end(); +} + + +/*************************************************************************************** +** Function name: pushImage - for FLASH (PROGMEM) stored images +** Description: plot 16 bit image with 1 colour being transparent +***************************************************************************************/ +void TFT_eSPI::pushImage(int32_t x, int32_t y, uint32_t w, uint32_t h, const uint16_t *data, uint16_t transp) +{ + + if ((x >= (int32_t)_width) || (y >= (int32_t)_height)) return; + + int32_t dx = 0; + int32_t dy = 0; + int32_t dw = w; + int32_t dh = h; + + if (x < 0) { dw += x; dx = -x; x = 0; } + if (y < 0) { dh += y; dy = -y; y = 0; } + + if ((x + w) > _width ) dw = _width - x; + if ((y + h) > _height) dh = _height - y; + + if (dw < 1 || dh < 1) return; + + spi_begin(); + inTransaction = true; data += dx + dy * w; int32_t xe = x + dw - 1, ye = y + dh - 1; - if (swap) transp = transp >> 8 | transp << 8; + uint16_t lineBuf[dw]; + + if (_swapBytes) transp = transp >> 8 | transp << 8; while (dh--) { @@ -682,20 +732,31 @@ void TFT_eSPI::pushImage(int32_t x, int32_t y, uint32_t w, uint32_t h, const uin uint16_t* ptr = (uint16_t*)data; int32_t px = x; boolean move = true; - + + uint16_t np = 0; + while (len--) { uint16_t color = pgm_read_word(ptr); if (transp != color) { if (move) { move = false; setAddrWindow(px, y, xe, ye); } - if (swap) color = color>>8 | color<<8; - SPI.write16(color); + lineBuf[np] = color; + np++; + } + else + { + move = true; + if (np) + { + pushColors(lineBuf, np, !_swapBytes); + np = 0; + } } - else move = true; px++; ptr++; } + if (np) pushColors(lineBuf, np, !_swapBytes); y++; data += w; @@ -703,6 +764,7 @@ void TFT_eSPI::pushImage(int32_t x, int32_t y, uint32_t w, uint32_t h, const uin CS_H; + inTransaction = false; spi_end(); } @@ -729,13 +791,14 @@ void TFT_eSPI::pushImage(int32_t x, int32_t y, uint32_t w, uint32_t h, uint8_t * if (dw < 1 || dh < 1) return; spi_begin(); + inTransaction = true; setAddrWindow(x, y, x + dw - 1, y + dh - 1); // Sets CS low and sent RAMWR data += dx + dy * w; - // Line buffer makes plotting faster, pointer is aligned to 32 bits - uint32_t lineBuf[1+(dw>>1)]; + // Line buffer makes plotting faster + uint16_t lineBuf[dw]; uint8_t blue[] = {0, 11, 21, 31}; // blue 2 to 5 bit colour lookup table @@ -754,6 +817,7 @@ void TFT_eSPI::pushImage(int32_t x, int32_t y, uint32_t w, uint32_t h, uint8_t * while(len--) { uint32_t color = *ptr++; + // Shifts are slow so check if colour has changed first if (color != _lastColor) { // =====Green===== ===============Red============== @@ -762,18 +826,19 @@ void TFT_eSPI::pushImage(int32_t x, int32_t y, uint32_t w, uint32_t h, uint8_t * lsbColor = (color & 0x1C)<<3 | blue[color & 0x03]; _lastColor = color; } + *linePtr++ = msbColor; *linePtr++ = lsbColor; } - // lineBuf is always 32 bit aligned so can use writeBytes! - if (dw) SPI.writeBytes((uint8_t*)lineBuf, dw<<1); + pushColors(lineBuf, dw, false); data += w; } CS_H; + inTransaction = false; spi_end(); } @@ -800,57 +865,100 @@ void TFT_eSPI::pushImage(int32_t x, int32_t y, uint32_t w, uint32_t h, uint8_t * if (dw < 1 || dh < 1) return; spi_begin(); + inTransaction = true; data += dx + dy * w; int32_t xe = x + dw - 1, ye = y + dh - 1; + // Line buffer makes plotting faster + uint16_t lineBuf[dw]; + uint8_t blue[] = {0, 11, 21, 31}; // blue 2 to 5 bit colour lookup table _lastColor = -1; // Set to illegal value // Used to store last shifted colour - uint16_t color565 = 0; + uint8_t msbColor = 0; + uint8_t lsbColor = 0; + + int32_t spx = x, spy = y; while (dh--) { int32_t len = dw; - uint8_t* ptr = data; //<<<<<<<<< changed to 8 bit + uint8_t* ptr = data; + uint8_t* linePtr = (uint8_t*)lineBuf; + int32_t px = x; boolean move = true; - + uint16_t np = 0; + while (len--) { if (transp != *ptr) { - if (move) { move = false; setAddrWindow(px, y, xe, ye); } - uint32_t color = *ptr; + if (move) { move = false; setAddrWindow(px, y, xe, ye);} + uint8_t color = *ptr; // Shifts are slow so check if colour has changed first if (color != _lastColor) { // =====Green===== ===============Red============== - color565 = (color & 0x1C)<<6 | (color & 0xC0)<<5 | (color & 0xE0)<<8 + msbColor = (color & 0x1C)>>2 | (color & 0xC0)>>3 | (color & 0xE0); // =====Green===== =======Blue====== - | (color & 0x1C)<<3 | blue[color & 0x03]; + lsbColor = (color & 0x1C)<<3 | blue[color & 0x03]; _lastColor = color; } - SPI.write16(color565); + *linePtr++ = msbColor; + *linePtr++ = lsbColor; + np++; + } + else + { + move = true; + if (np) + { + pushColors(lineBuf, np, false); + linePtr = (uint8_t*)lineBuf; + np = 0; + } } - else move = true; px++; ptr++; } + if (np) pushColors(lineBuf, np, false); + y++; data += w; } CS_H; + inTransaction = false; spi_end(); } +/*************************************************************************************** +** Function name: setSwapBytes +** Description: Used by 16 bit pushImage() to swap byte order in colours +***************************************************************************************/ +void TFT_eSPI::setSwapBytes(bool swap) +{ + _swapBytes = swap; +} + + +/*************************************************************************************** +** Function name: getSwapBytes +** Description: Return the swap byte order for colours +***************************************************************************************/ +bool TFT_eSPI::getSwapBytes(void) +{ + return _swapBytes; +} + /*************************************************************************************** ** Function name: read rectangle (for SPI Interface II i.e. IM [3:0] = "1101") ** Description: Read RGB pixel colours from a defined area @@ -1012,15 +1120,7 @@ void TFT_eSPI::drawCircleHelper( int32_t x0, int32_t y0, int32_t r, uint8_t corn ** Function name: fillCircle ** Description: draw a filled circle ***************************************************************************************/ -/* -void TFT_eSPI::fillCircle(int32_t x0, int32_t y0, int32_t r, uint32_t color) -{ - drawFastVLine(x0, y0 - r, r + r + 1, color); - fillCircleHelper(x0, y0, r, 3, 0, color); -} -*/ - -// Optimised midpoint circle algorithm +// Optimised midpoint circle algorithm, changed to horizontal lines (faster in sprites) void TFT_eSPI::fillCircle(int32_t x0, int32_t y0, int32_t r, uint32_t color) { @@ -1032,7 +1132,7 @@ void TFT_eSPI::fillCircle(int32_t x0, int32_t y0, int32_t r, uint32_t color) spi_begin(); inTransaction = true; - drawFastVLine(x0, y0 - r, dy+1, color); + drawFastHLine(x0 - r, y0, dy+1, color); while(x= 0) { r--; ddF_y += 2; f += ddF_y; } - x++; + y++; + //x++; ddF_x += 2; f += ddF_x; - if (cornername & 0x1) { - drawFastVLine(x0 + x, y0 - r, r + r + delta, color); - drawFastVLine(x0 + r, y0 - x, x + x + delta, color); + if (cornername & 0x1) + { + drawFastHLine(x0 - r, y0 + y, r + r + delta, color); + drawFastHLine(x0 - y, y0 + r, y + y + delta, color); } if (cornername & 0x2) { - drawFastVLine(x0 - x, y0 - r, r + r + delta, color); - drawFastVLine(x0 - r, y0 - x, x + x + delta, color); + drawFastHLine(x0 - r, y0 - y, r + r + delta, color); // 11995, 1090 + drawFastHLine(x0 - y, y0 - r, y + y + delta, color); } } } @@ -1274,19 +1360,19 @@ void TFT_eSPI::drawRoundRect(int32_t x, int32_t y, int32_t w, int32_t h, int32_t ** Function name: fillRoundRect ** Description: Draw a rounded corner filled rectangle ***************************************************************************************/ -// Fill a rounded rectangle +// Fill a rounded rectangle, changed to horizontal lines (faster in sprites) void TFT_eSPI::fillRoundRect(int32_t x, int32_t y, int32_t w, int32_t h, int32_t r, uint32_t color) { spi_begin(); inTransaction = true; // smarter version - fillRect(x + r, y, w - r - r, h, color); + fillRect(x, y + r, w, h - r - r, color); // draw four corners - fillCircleHelper(x + w - r - 1, y + r, r, 1, h - r - r - 1, color); - fillCircleHelper(x + r , y + r, r, 2, h - r - r - 1, color); - + fillCircleHelper(x + r, y + h - r - 1, r, 1, w - r - r - 1, color); + fillCircleHelper(x + r , y + r, r, 2, w - r - r - 1, color); + inTransaction = false; spi_end(); } @@ -1320,9 +1406,6 @@ void TFT_eSPI::fillTriangle ( int32_t x0, int32_t y0, int32_t x1, int32_t y1, in { int32_t a, b, y, last; - spi_begin(); - inTransaction = true; - // Sort coordinates by Y order (y2 >= y1 >= y0) if (y0 > y1) { swap_coord(y0, y1); swap_coord(x0, x1); @@ -1344,6 +1427,9 @@ void TFT_eSPI::fillTriangle ( int32_t x0, int32_t y0, int32_t x1, int32_t y1, in return; } + spi_begin(); + inTransaction = true; + int32_t dx01 = x1 - x0, dy01 = y1 - y0, @@ -1673,8 +1759,8 @@ void TFT_eSPI::drawChar(int32_t x, int32_t y, unsigned char c, uint32_t color, u if ((size==1) && fillbg) { - byte column[6]; - byte mask = 0x1; + uint8_t column[6]; + uint8_t mask = 0x1; spi_begin(); //inTransaction = true; setAddrWindow(x, y, x+5, y+8); @@ -1929,9 +2015,6 @@ inline void TFT_eSPI::setAddrWindow(int32_t xs, int32_t ys, int32_t xe, int32_t { //spi_begin(); - addr_col = 0xFFFF; - addr_row = 0xFFFF; - #ifdef CGRAM_OFFSET xs+=colstart; xe+=colstart; @@ -1950,6 +2033,10 @@ inline void TFT_eSPI::setAddrWindow(int32_t xs, int32_t ys, int32_t xe, int32_t SPI1W0 = TFT_CASET; SPI1CMD |= SPIBUSY; + + addr_col = 0xFFFF; + addr_row = 0xFFFF; + while(SPI1CMD & SPIBUSY) {} DC_D; @@ -2595,9 +2682,11 @@ void TFT_eSPI::pushColor(uint16_t color) spi_begin(); CS_L; - +#if defined (ESP8266) + SPI.write16(color, true); +#else SPI.write16(color); - +#endif CS_H; spi_end(); @@ -2628,6 +2717,136 @@ void TFT_eSPI::pushColor(uint16_t color, uint16_t len) } +/*************************************************************************************** +** Function name: pushColors +** Description: push an array of pixels, for image drawing +***************************************************************************************/ +void TFT_eSPI::pushColors(uint16_t *data, uint32_t len, bool swap) +{ + spi_begin(); + + CS_L; + +#if defined (ESP32) + +if (swap) SPI.writePixels(data,len<<1); +else SPI.writeBytes((uint8_t*)data,len<<1); + +#else + + uint32_t color[8]; + + uint32_t mask = ~((SPIMMOSI << SPILMOSI) | (SPIMMISO << SPILMISO)); + + SPI1U1 = (SPI1U1 & mask) | (255 << SPILMOSI) | (255 << SPILMISO); + + while(len>15) + { + + if (swap) + { + uint32_t i = 0; + while(i<8) + { + color[i] = (*data >> 8) | (uint16_t)(*data << 8); + data++; + color[i] |= ((*data >> 8) | (*data << 8)) << 16; + data++; + i++; + } + } + else + { + memcpy(color,data,32); + data+=16; + } + + len -= 16; + while(SPI1CMD & SPIBUSY) {} + SPI1W0 = color[0]; + SPI1W1 = color[1]; + SPI1W2 = color[2]; + SPI1W3 = color[3]; + SPI1W4 = color[4]; + SPI1W5 = color[5]; + SPI1W6 = color[6]; + SPI1W7 = color[7]; + SPI1CMD |= SPIBUSY; + } + + if(len) + { + uint32_t bits = (len*16-1); // bits left to shift - 1 + if (swap) + { + uint16_t* ptr = (uint16_t*)color; + while(len--) + { + *ptr++ = (*(data) >> 8) | (uint16_t)(*(data) << 8); + data++; + } + } + else + { + memcpy(color,data,len<<1); + } + while(SPI1CMD & SPIBUSY) {} + SPI1U1 = (SPI1U1 & mask) | (bits << SPILMOSI) | (bits << SPILMISO); + SPI1W0 = color[0]; + SPI1W1 = color[1]; + SPI1W2 = color[2]; + SPI1W3 = color[3]; + SPI1W4 = color[4]; + SPI1W5 = color[5]; + SPI1W6 = color[6]; + SPI1W7 = color[7]; + SPI1CMD |= SPIBUSY; + } + +/* // Smaller version but slower + uint32_t count = 0; + while(len) + { + if(len>15) {count = 16; len -= 16;} + else {count = len; len = 0;} + uint32_t bits = (count*16-1); // bits left to shift - 1 + if (swap) + { + uint16_t* ptr = (uint16_t*)color; + while(count--) + { + *ptr++ = (*(data) >> 8) | (uint16_t)(*(data) << 8); + data++; + } + } + else + { + memcpy(color,data,count<<1); + data += 16; + } + while(SPI1CMD & SPIBUSY) {} + SPI1U1 = (SPI1U1 & mask) | (bits << SPILMOSI) | (bits << SPILMISO); + SPI1W0 = color[0]; + SPI1W1 = color[1]; + SPI1W2 = color[2]; + SPI1W3 = color[3]; + SPI1W4 = color[4]; + SPI1W5 = color[5]; + SPI1W6 = color[6]; + SPI1W7 = color[7]; + SPI1CMD |= SPIBUSY; + } +*/ + + while(SPI1CMD & SPIBUSY) {} + +#endif + + CS_H; + + spi_end(); +} + /*************************************************************************************** ** Function name: pushColors ** Description: push an aray of pixels for BMP image drawing @@ -2636,7 +2855,8 @@ void TFT_eSPI::pushColor(uint16_t color, uint16_t len) // externally by BMP examples. Assumes that setWindow() has // previously been called to define the bounds. Max 255 pixels at // a time (BMP examples read in small chunks due to limited RAM). - +/* +#define SWAP_COLOR_BYTES // We want to swap color bytes in this fn void TFT_eSPI::pushColors(uint16_t *data, uint8_t len) { spi_begin(); @@ -2645,27 +2865,48 @@ void TFT_eSPI::pushColors(uint16_t *data, uint8_t len) #if defined (ESP32) - while (len--) SPI.write16(*(data++)); + SPI.writePixels(data,len<<1); #else uint32_t mask = ~((SPIMMOSI << SPILMOSI) | (SPIMMISO << SPILMISO)); + SPI1U1 = (SPI1U1 & mask) | (255 << SPILMOSI) | (255 << SPILMISO); - SPI1U1 = (SPI1U1 & mask) | (63 << SPILMOSI) | (63 << SPILMISO); - while(len>3) + uint32_t color[8]; + + while(len>15) { - uint32_t color0 = (*(data) >> 8) | (uint16_t)(*(data) << 8); - data++; - color0 |= ((*(data) >> 8) | (*(data) << 8)) << 16; - data++; - uint32_t color1 = (*(data) >> 8) | (uint16_t)(*(data) << 8); - data++; - color1 |= ((*(data) >> 8) | (*(data) << 8)) << 16; - data++; len -= 4; +#ifdef SWAP_COLOR_BYTES + uint32_t i = 0; + while(i<8) + { + color[i] = (*(data) >> 8) | (uint16_t)(*(data) << 8); + data++; + color[i] |= ((*(data) >> 8) | (*(data) << 8)) << 16; + data++; + i++; + } +#else + uint32_t i = 0; + while(i<8) + { + color[i] = *data++; + color[i] |= (*data++) << 16; + i++; + } +#endif + + len -= 16; while(SPI1CMD & SPIBUSY) {} - SPI1W0 = color0; - SPI1W1 = color1; + SPI1W0 = color[0]; + SPI1W1 = color[1]; + SPI1W2 = color[2]; + SPI1W3 = color[3]; + SPI1W4 = color[4]; + SPI1W5 = color[5]; + SPI1W6 = color[6]; + SPI1W7 = color[7]; SPI1CMD |= SPIBUSY; } if (len) @@ -2689,38 +2930,74 @@ void TFT_eSPI::pushColors(uint16_t *data, uint8_t len) spi_end(); } - +*/ /*************************************************************************************** ** Function name: pushColors -** Description: push an aray of pixels for 16 bit raw image drawing +** Description: push an array of pixels for 16 bit raw image drawing ***************************************************************************************/ // Assumed that setWindow() has previously been called - -void TFT_eSPI::pushColors(uint8_t *data, uint32_t len) +// There is no control of endianness +/*void TFT_eSPI::pushColors(uint8_t *data, uint32_t len) { spi_begin(); CS_L; -#if defined (RPI_WRITE_STROBE) - //while ( len ) {SPI.writePattern(data, 2, 1); data += 2; len -= 2; } + // We cannot align to 32 bits since array may be created as 8 bit originally while ( len >=64 ) {SPI.writePattern(data, 64, 1); data += 64; len -= 64; } if (len) SPI.writePattern(data, len, 1); -#else - #if (SPI_FREQUENCY == 80000000) - while ( len >=64 ) {SPI.writePattern(data, 64, 1); data += 64; len -= 64; } - if (len) SPI.writePattern(data, len, 1); - #else - SPI.writeBytes(data, len); - #endif -#endif CS_H; spi_end(); } +*/ +/*************************************************************************************** +** Function name: pushColors +** Description: push an aray of pixels with byte swap option +***************************************************************************************/ +/* +void TFT_eSPI::pushColors(uint16_t *data, uint32_t len, bool swap) +{ + spi_begin(); + + CS_L; + +#if defined (ESP32) + if (swap) + { + //while (len--) SPI.write16(*(data++)); + SPI.writePixels(data,len<<1); + } + else + { + SPI.writeBytes((uint8_t*)data,len<<1); + } +#else + if (swap) + { + while (len>239) { pushColors(data, 240); len -= 240; data += 240; } + if (len) pushColors(data, len); + } + else + { + // Check alignment of 16 bit pointer to 32 bits + uint8_t offset = ((uint32_t)data & 0x2)>>1; + if (offset > len) offset = len; + + // Make pointer 32 bit align using immune call then use the faster writeBytes() + if (offset) { SPI.writePattern((uint8_t*)data, offset<<1, 1); len -= offset; data += offset; } + + if (len) SPI.writeBytes((uint8_t*)data, len<<1); + } +#endif + CS_H; + + spi_end(); +} +*/ /*************************************************************************************** ** Function name: drawLine @@ -2789,7 +3066,6 @@ void TFT_eSPI::drawLine(int32_t x0, int32_t y0, int32_t x1, int32_t y1, uint32_t // This is a weeny bit faster void TFT_eSPI::drawLine(int32_t x0, int32_t y0, int32_t x1, int32_t y1, uint32_t color) { - spi_begin(); boolean steep = abs(y1 - y0) > abs(x1 - x0); @@ -2812,6 +3088,8 @@ void TFT_eSPI::drawLine(int32_t x0, int32_t y0, int32_t x1, int32_t y1, uint32_t int16_t err = dx / 2; int8_t ystep = (y0 < y1) ? 1 : (-1); + spi_begin(); + uint32_t mask = ~((SPIMMOSI << SPILMOSI) | (SPIMMISO << SPILMISO)); mask = (SPI1U1 & mask) | (15 << SPILMOSI) | (15 << SPILMISO); SPI1U = SPIUMOSI | SPIUSSE; @@ -2830,7 +3108,7 @@ void TFT_eSPI::drawLine(int32_t x0, int32_t y0, int32_t x1, int32_t y1, uint32_t } } - if (x0 > x1) return; + if (x0 > x1) {spi_end(); return;} setAddrWindow(y0, x0, y0, _height); SPI1U1 = mask; @@ -2864,7 +3142,7 @@ void TFT_eSPI::drawLine(int32_t x0, int32_t y0, int32_t x1, int32_t y1, uint32_t } } - if (x0 > x1) return; + if (x0 > x1) {spi_end(); return;} setAddrWindow(x0, y0, _width, y0); SPI1U1 = mask; @@ -2881,7 +3159,7 @@ void TFT_eSPI::drawLine(int32_t x0, int32_t y0, int32_t x1, int32_t y1, uint32_t while(SPI1CMD & SPIBUSY) {} setAddrWindow(x0+1, y0, _width, y0); SPI1U1 = mask; - SPI1W0 = swapped_color; + SPI1W0 = swapped_color; } } } @@ -2891,6 +3169,7 @@ void TFT_eSPI::drawLine(int32_t x0, int32_t y0, int32_t x1, int32_t y1, uint32_t CS_H; spi_end(); } + #endif @@ -3063,6 +3342,16 @@ uint16_t TFT_eSPI::color565(uint8_t r, uint8_t g, uint8_t b) } +/*************************************************************************************** +** Function name: color332 +** Description: convert 16 bit colour to an 8 bit 332 RGB colour value +***************************************************************************************/ +uint8_t TFT_eSPI::color332(uint16_t c) +{ + return ((c & 0xE000)>>8) | ((c & 0x0700)>>6) | ((c & 0x0018)>>3); +} + + /*************************************************************************************** ** Function name: invertDisplay ** Description: invert the display colours i = 1 invert, i = 0 normal @@ -3284,7 +3573,7 @@ int16_t TFT_eSPI::drawChar(unsigned int uniCode, int x, int y, int font) int w = width; int pX = 0; int pY = y; - byte line = 0; + uint8_t line = 0; #ifdef LOAD_FONT2 // chop out code if we do not need it if (font == 2) { @@ -3293,6 +3582,8 @@ int16_t TFT_eSPI::drawChar(unsigned int uniCode, int x, int y, int font) if (x + width * textsize >= (int16_t)_width) return width * textsize ; if (textcolor == textbgcolor || textsize != 1) { + spi_begin(); + inTransaction = true; for (int i = 0; i < height; i++) { @@ -3328,6 +3619,9 @@ int16_t TFT_eSPI::drawChar(unsigned int uniCode, int x, int y, int font) } pY += textsize; } + + inTransaction = false; + spi_end(); } else // Faster drawing of characters and background using block write @@ -3335,7 +3629,7 @@ int16_t TFT_eSPI::drawChar(unsigned int uniCode, int x, int y, int font) spi_begin(); setAddrWindow(x, y, (x + w * 8) - 1, y + height - 1); - byte mask; + uint8_t mask; for (int i = 0; i < height; i++) { for (int k = 0; k < w; k++) @@ -3376,10 +3670,10 @@ int16_t TFT_eSPI::drawChar(unsigned int uniCode, int x, int y, int font) if (textcolor != textbgcolor) fillRect(x, pY, width * textsize, textsize * height, textbgcolor); int px = 0, py = pY; // To hold character block start and end column and row values int pc = 0; // Pixel count - byte np = textsize * textsize; // Number of pixels in a drawn pixel + uint8_t np = textsize * textsize; // Number of pixels in a drawn pixel - byte tnp = 0; // Temporary copy of np for while loop - byte ts = textsize - 1; // Temporary copy of textsize + uint8_t tnp = 0; // Temporary copy of np for while loop + uint8_t ts = textsize - 1; // Temporary copy of textsize // 16 bit pixel count so maximum font size is equivalent to 180x180 pixels in area // w is total number of pixels to plot to fill character block while (pc < w) @@ -3701,7 +3995,7 @@ int16_t TFT_eSPI::drawCentreString(const String& string, int dX, int poY, int fo int16_t TFT_eSPI::drawCentreString(const char *string, int dX, int poY, int font) { - static byte tempdatum = textdatum; + uint8_t tempdatum = textdatum; int sumX = 0; textdatum = TC_DATUM; sumX = drawString(string, dX, poY, font); @@ -3724,7 +4018,7 @@ int16_t TFT_eSPI::drawRightString(const String& string, int dX, int poY, int fon int16_t TFT_eSPI::drawRightString(const char *string, int dX, int poY, int font) { - static byte tempdatum = textdatum; + uint8_t tempdatum = textdatum; int16_t sumX = 0; textdatum = TR_DATUM; sumX = drawString(string, dX, poY, font); @@ -3896,7 +4190,13 @@ void TFT_eSPI::setTextFont(uint8_t f) ** Function name: spiBlockWrite ** Description: Write a block of pixels of the same colour ***************************************************************************************/ -#if defined (ESP8266) && (SPI_FREQUENCY != 80000000) +//Clear screen test 76.8ms theoretical. 81.5ms TFT_eSPI, 967ms Adafruit_ILI9341 +//Performance 26.15Mbps@26.66MHz, 39.04Mbps@40MHz, 75.4Mbps@80MHz SPI clock +//Efficiency: +// TFT_eSPI 98.06% 97.59% 94.24% +// Adafruit_GFX 19.62% 14.31% 7.94% +// +#if defined (ESP8266) // && (SPI_FREQUENCY != 80000000) void spiWriteBlock(uint16_t color, uint32_t repeat) { uint16_t color16 = (color >> 8) | (color << 8); @@ -3935,6 +4235,9 @@ void spiWriteBlock(uint16_t color, uint32_t repeat) SPI1U1 = mask | (511 << SPILMOSI); while(repeat>31) { +#if defined SPI_FREQUENCY && (SPI_FREQUENCY == 80000000) + if(SPI1CMD & SPIBUSY) // added to sync with flag change +#endif while(SPI1CMD & SPIBUSY) {} SPI1CMD |= SPIBUSY; repeat -= 32; @@ -4120,8 +4423,8 @@ uint8_t TFT_eSPI::getTouch(uint16_t *x, uint16_t *y, uint16_t threshold){ if (threshold<20) threshold = 20; if (_pressTime > millis()) threshold=20; - byte n = 5; - byte valid = 0; + uint8_t n = 5; + uint8_t valid = 0; while (n--) { if (validTouch(&x_tmp, &y_tmp, threshold)) valid++;; @@ -4395,7 +4698,7 @@ void TFT_eSPI_Button::drawButton(boolean inverted) { _gfx->setTextColor(text); _gfx->setTextSize(_textsize); - static byte tempdatum = _gfx->getTextDatum(); + uint8_t tempdatum = _gfx->getTextDatum(); _gfx->setTextDatum(MC_DATUM); _gfx->drawString(_label, _x1 + (_w/2), _y1 + (_h/2)); _gfx->setTextDatum(tempdatum); @@ -4417,14 +4720,20 @@ boolean TFT_eSPI_Button::justReleased() { return (!currstate && laststate); } - /*************************************************************************************** + /************************************************************************************** // The following class creates Sprites in RAM, graphics can then be drawn in the Sprite // and rendered quickly onto the TFT screen. The class inherits the graphics functions // from the TFT_eSPI class. Some functions are overridden by this class so that the // graphics are written to the Sprite rather than the TFT. // Coded by Bodmer ***************************************************************************************/ - +/*************************************************************************************** +// Color bytes are swapped when writing to RAM, this introduces a small overhead but +// then rendering to screen can use the faster call. In general rendering graphics in +// the Sprite is very fast, but writing to the TFT is slow, so there is a performance +// gain by using swapped bytes. +***************************************************************************************/ + /*************************************************************************************** ** Function name: TFT_eSprite ** Description: Class constructor @@ -4436,6 +4745,7 @@ TFT_eSprite::TFT_eSprite(TFT_eSPI *tft) _iwidth = 0; // Initialise width and height to 0 (it does not exist yet) _iheight = 0; _bpp16 = true; + _iswapBytes = false; // Do not swap pushImage colour bytes by default _created = false; @@ -4458,7 +4768,14 @@ TFT_eSprite::TFT_eSprite(TFT_eSPI *tft) // returns a uint8_t* pointer, cast returned value to (uint16_t*) for 16 bit colours uint8_t* TFT_eSprite::createSprite(int16_t w, int16_t h) { - if ( w < 1 || h < 1 || _created) return NULL; + + if ( _created ) + { + if ( _bpp16 ) return ( uint8_t*)_img; + return _img8; + } + + if ( w < 1 || h < 1 ) return NULL; _iwidth = w; _iheight = h; @@ -4469,9 +4786,11 @@ uint8_t* TFT_eSprite::createSprite(int16_t w, int16_t h) _sh = h; _scolor = TFT_BLACK; + // Add one extra "off screen" pixel to point out-of-bounds setWindow() coordinates + // this means push/writeColor functions do not need additional bounds checks. if(_bpp16) { - _img = (uint16_t*) malloc(w * h * 2); + _img = (uint16_t*) malloc(w * h * 2 + 1); if (_img) { _created = true; @@ -4481,7 +4800,7 @@ uint8_t* TFT_eSprite::createSprite(int16_t w, int16_t h) } else { - _img8 = ( uint8_t*) malloc(w * h); + _img8 = ( uint8_t*) malloc(w * h + 1); if (_img8) { _created = true; @@ -4499,7 +4818,7 @@ uint8_t* TFT_eSprite::createSprite(int16_t w, int16_t h) ** Description: Set bits per pixel for colour (8 or 16) *************************************************************************************x*/ -void TFT_eSprite::setColorDepth(int8_t b) +uint8_t* TFT_eSprite::setColorDepth(int8_t b) { // Can't change an existing sprite's colour depth so delete it if (_created) @@ -4516,8 +4835,10 @@ void TFT_eSprite::setColorDepth(int8_t b) if (_created) { _created = false; - createSprite(_iwidth, _iheight); + return createSprite(_iwidth, _iheight); } + + return NULL; } @@ -4531,6 +4852,7 @@ void TFT_eSprite::deleteSprite(void) if (_bpp16) free(_img); else free(_img8); + _created = false; } @@ -4560,7 +4882,7 @@ void TFT_eSprite::pushSprite(int32_t x, int32_t y, uint16_t transp) else { transp = (uint8_t)((transp & 0xE000)>>8 | (transp & 0x0700)>>6 | (transp & 0x0018)>>3); - _tft->pushImage(x, y, _iwidth, _iheight, _img8, (uint8_t) transp); + _tft->pushImage(x, y, _iwidth, _iheight, _img8, (uint8_t)transp); } } @@ -4593,9 +4915,9 @@ uint16_t TFT_eSprite::readPixel(int32_t x, int32_t y) /*************************************************************************************** ** Function name: pushImage -** Description: push 565 colour bitmap image into a defined area +** Description: push 565 colour image into a defined area of a sprite *************************************************************************************x*/ -void TFT_eSprite::pushImage(uint32_t x, uint32_t y, uint32_t w, uint32_t h, uint16_t *data) +void TFT_eSprite::pushImage(int32_t x, int32_t y, uint32_t w, uint32_t h, uint16_t *data) { if ((x > _iwidth) || (y > _iheight) || (w == 0) || (h == 0) || !_created) return; @@ -4605,7 +4927,9 @@ void TFT_eSprite::pushImage(uint32_t x, uint32_t y, uint32_t w, uint32_t h, uin { for (uint32_t xp = x; xp < x + w; xp++) { - _img[xp + yp * _iwidth] = *data++; + uint16_t color = *data++; + if(!_iswapBytes) color = color<<8 | color>>8; + _img[xp + yp * _iwidth] = color; } } } @@ -4616,6 +4940,7 @@ void TFT_eSprite::pushImage(uint32_t x, uint32_t y, uint32_t w, uint32_t h, uin for (uint32_t xp = x; xp < x + w; xp++) { uint16_t color = *data++; + if(_iswapBytes) color = color<<8 | color>>8; _img8[xp + yp * _iwidth] = (uint8_t)((color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3); } } @@ -4627,7 +4952,7 @@ void TFT_eSprite::pushImage(uint32_t x, uint32_t y, uint32_t w, uint32_t h, uin ** Function name: pushImage ** Description: push 565 colour FLASH (PROGMEM) image into a defined area *************************************************************************************x*/ -void TFT_eSprite::pushImage(uint32_t x, uint32_t y, uint32_t w, uint32_t h, const uint16_t *data, bool swap) +void TFT_eSprite::pushImage(int32_t x, int32_t y, uint32_t w, uint32_t h, const uint16_t *data) { if ((x > _iwidth) || (y > _iheight) || (w == 0) || (h == 0) || !_created) return; @@ -4638,7 +4963,7 @@ void TFT_eSprite::pushImage(uint32_t x, uint32_t y, uint32_t w, uint32_t h, con for (uint32_t xp = x; xp < x + w; xp++) { uint16_t color = pgm_read_word(data++); - if(!swap) color = color<<8 | color>>8; + if(!_iswapBytes) color = color<<8 | color>>8; _img[xp + yp * _iwidth] = color; } } @@ -4650,7 +4975,7 @@ void TFT_eSprite::pushImage(uint32_t x, uint32_t y, uint32_t w, uint32_t h, con for (uint32_t xp = x; xp < x + w; xp++) { uint16_t color = pgm_read_word(data++); - if(swap) color = color<<8 | color>>8; + if(_iswapBytes) color = color<<8 | color>>8; _img8[xp + yp * _iwidth] = (uint8_t)((color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3); } } @@ -4658,30 +4983,62 @@ void TFT_eSprite::pushImage(uint32_t x, uint32_t y, uint32_t w, uint32_t h, con } +/*************************************************************************************** +** Function name: setSwapBytes +** Description: Used by 16 bit pushImage() to swap byte order in colours +***************************************************************************************/ +void TFT_eSprite::setSwapBytes(bool swap) +{ + _iswapBytes = swap; +} + + +/*************************************************************************************** +** Function name: getSwapBytes +** Description: Return the swap byte order for colours +***************************************************************************************/ +bool TFT_eSprite::getSwapBytes(void) +{ + return _iswapBytes; +} + + /*************************************************************************************** ** Function name: setWindow ** Description: Set the bounds of a window for pushColor and writeColor *************************************************************************************x*/ void TFT_eSprite::setWindow(int32_t x0, int32_t y0, int32_t x1, int32_t y1) { + bool duff_coord = false; + if (x0 > x1) swap_coord(x0, x1); if (y0 > y1) swap_coord(y0, y1); if (x0 < 0) x0 = 0; - if (x0 >= _iwidth) x0 = _iwidth; + if (x0 >= _iwidth) duff_coord = true; if (x1 < 0) x1 = 0; - if (x1 >= _iwidth) x1 = _iwidth; + if (x1 >= _iwidth) x1 = _iwidth - 1; if (y0 < 0) y0 = 0; - if (y0 >= _iheight) y0 = _iheight; + if (y0 >= _iheight) duff_coord = true; if (y1 < 0) y1 = 0; - if (y1 >= _iheight) y1 = _iheight; + if (y1 >= _iheight) y1 = _iheight - 1; + + if (duff_coord) + { // Point to that extra "off screen" pixel + _xs = 0; + _ys = _iheight; + _xe = 0; + _ye = _iheight; + } + else + { + _xs = x0; + _ys = y0; + _xe = x1; + _ye = y1; + } - _xs = x0; - _ys = y0; - _xe = x1; - _ye = y1; - _xptr = _xs; _yptr = _ys; } @@ -4698,6 +5055,7 @@ void TFT_eSprite::pushColor(uint32_t color) // Write the colour to RAM in set window if (_bpp16) _img [_xptr + _yptr * _iwidth] = (uint16_t) (color >> 8) | (color << 8); + else _img8[_xptr + _yptr * _iwidth] = (uint8_t )((color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3); @@ -4726,6 +5084,7 @@ void TFT_eSprite::pushColor(uint32_t color, uint16_t len) uint16_t pixelColor; if (_bpp16) pixelColor = (uint16_t) (color >> 8) | (color << 8); + else pixelColor = (color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3; @@ -4797,7 +5156,7 @@ void TFT_eSprite::scroll(int16_t dx, int16_t dy) return; } - // Fetch the scroll area wodth and height set by setScrollRect() + // Fetch the scroll area width and height set by setScrollRect() uint32_t w = _sw - abs(dx); // line width to copy uint32_t h = _sh - abs(dy); // lines to copy int32_t iw = _iwidth; // width of sprite @@ -4933,8 +5292,8 @@ void TFT_eSprite::drawChar(int32_t x, int32_t y, unsigned char c, uint32_t color if ((size==1) && fillbg) { - byte column[6]; - byte mask = 0x1; + uint8_t column[6]; + uint8_t mask = 0x1; for (int8_t i = 0; i < 5; i++ ) column[i] = pgm_read_byte(font + (c * 5) + i); column[5] = 0; @@ -5179,7 +5538,7 @@ void TFT_eSprite::drawFastHLine(int32_t x, int32_t y, int32_t w, uint32_t color) else { color = (color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3; - memset(_img8+_iwidth * y + x, (uint8_t)color, w); + memset(_img8+_iwidth * y + x, (uint8_t)color, w); } } @@ -5435,7 +5794,7 @@ int16_t TFT_eSprite::drawChar(unsigned int uniCode, int x, int y, int font) int w = width; int pX = 0; int pY = y; - byte line = 0; + uint8_t line = 0; #ifdef LOAD_FONT2 // chop out code if we do not need it if (font == 2) { @@ -5495,9 +5854,9 @@ int16_t TFT_eSprite::drawChar(unsigned int uniCode, int x, int y, int font) else color = ((textcolor & 0xE000)>>8 | (textcolor & 0x0700)>>6 | (textcolor & 0x0018)>>3); int px = 0, py = pY; // To hold character block start and end column and row values int pc = 0; // Pixel count - byte np = textsize * textsize; // Number of pixels in a drawn pixel - - byte ts = textsize - 1; // Temporary copy of textsize + uint8_t np = textsize * textsize; // Number of pixels in a drawn pixel + uint8_t tnp = 0; // Temporary copy of np for while loop + uint8_t ts = textsize - 1; // Temporary copy of textsize // 16 bit pixel count so maximum font size is equivalent to 180x180 pixels in area // w is total number of pixels to plot to fill character block while (pc < w) @@ -5518,8 +5877,7 @@ int16_t TFT_eSprite::drawChar(unsigned int uniCode, int x, int y, int font) while (line--) { pc++; setWindow(px, py, px + ts, py + ts); - - if (ts) while (np--) writeColor(color); + if (ts) { tnp = np; while (tnp--) writeColor(color); } else writeColor(color); px += textsize; diff --git a/TFT_eSPI.h b/TFT_eSPI.h index 8b521f8..840c50b 100644 --- a/TFT_eSPI.h +++ b/TFT_eSPI.h @@ -1,10 +1,6 @@ /*************************************************** Arduino TFT graphics library targetted at ESP8266 - based boards. (ESP32 support is planned!) - - This library has been derived from the Adafruit_GFX - library and the associated driver library. See text - at the end of this file. + and ESP32 based boards. This is a standalone library that contains the hardware driver, the graphics funtions and the @@ -347,8 +343,9 @@ class TFT_eSPI : public Print { void setWindow(int16_t x0, int16_t y0, int16_t x1, int16_t y1), pushColor(uint16_t color), pushColor(uint16_t color, uint16_t len), - pushColors(uint16_t *data, uint8_t len), - pushColors(uint8_t *data, uint32_t len), + //pushColors(uint16_t *data, uint8_t len), + //pushColors(uint8_t *data, uint32_t len), + pushColors(uint16_t *data, uint32_t len, bool swap = true), // With byte swap option fillScreen(uint32_t color); @@ -407,23 +404,30 @@ class TFT_eSPI : public Print { // Write a block of pixels to the screen void pushRect(uint32_t x0, uint32_t y0, uint32_t w, uint32_t h, uint16_t *data); - // These are used by pushSprite and can also be used to push bitmap images to the screen - // "565" 16 bit and "332" 8 bit colour encodings are supported + // These are used to render images or sprites stored in RAM arrays void pushImage(int32_t x0, int32_t y0, uint32_t w, uint32_t h, uint16_t *data); - void pushImage(int32_t x0, int32_t y0, uint32_t w, uint32_t h, uint8_t *data); - // The next two support a "transparent" colour so those image areas are not rendered void pushImage(int32_t x0, int32_t y0, uint32_t w, uint32_t h, uint16_t *data, uint16_t transparent); + + // These are used to render images stored in FLASH (PROGMEM) + void pushImage(int32_t x0, int32_t y0, uint32_t w, uint32_t h, const uint16_t *data, uint16_t transparent); + void pushImage(int32_t x0, int32_t y0, uint32_t w, uint32_t h, const uint16_t *data); + + // These are used by pushSprite for 8 bit colours + void pushImage(int32_t x0, int32_t y0, uint32_t w, uint32_t h, uint8_t *data); void pushImage(int32_t x0, int32_t y0, uint32_t w, uint32_t h, uint8_t *data, uint8_t transparent); - // This one has an optional flag to swap byte order in colours - void pushImage(int32_t x0, int32_t y0, uint32_t w, uint32_t h, const uint16_t *data, uint16_t transparent, bool swap = false); - + + // Swap the byte order for pushImage() - corrects endianness + void setSwapBytes(bool swap); + bool getSwapBytes(void); + // This next function has been used successfully to dump the TFT screen to a PC for documentation purposes // It reads a screen area and returns the RGB 8 bit colour values of each pixel // Set w and h to 1 to read 1 pixel's colour. The data buffer must be at least w * h * 3 bytes void readRectRGB(int32_t x0, int32_t y0, int32_t w, int32_t h, uint8_t *data); uint8_t getRotation(void), - getTextDatum(void); + getTextDatum(void), + color332(uint16_t color565); // Convert 16 bit colour to 8 bits uint16_t fontsLoaded(void), color565(uint8_t r, uint8_t g, uint8_t b); @@ -506,9 +510,9 @@ class TFT_eSPI : public Print { textdatum, // Text reference datum rotation; // Display rotation (0-3) - boolean textwrap; // If set, 'wrap' text at right edge of display - - boolean locked, inTransaction; // Transaction and mutex lock flags for ESP32 + bool textwrap; // If set, 'wrap' text at right edge of display + bool _swapBytes; // Swap the byte order for TFT pushImage() + bool locked, inTransaction; // Transaction and mutex lock flags for ESP32 int32_t _lastColor; @@ -580,9 +584,9 @@ class TFT_eSprite : public TFT_eSPI { // Delete the sprite to free up the RAM void deleteSprite(void); - // Set the colour depth to 8 or 16 bits - // Can be used to change depth an existing sprite, but clears it to black - void setColorDepth(int8_t b); + // Set the colour depth to 8 or 16 bits. Can be used to change depth an existing + // sprite, but clears it to black, returns a new pointer if sprite is re-created. + uint8_t* setColorDepth(int8_t b); void drawPixel(uint32_t x, uint32_t y, uint32_t color); @@ -618,9 +622,13 @@ class TFT_eSprite : public TFT_eSPI { // Read the colour of a pixel at x,y and return value in 565 format uint16_t readPixel(int32_t x0, int32_t y0); - // Write an image (bitmap) to the sprite - void pushImage(uint32_t x0, uint32_t y0, uint32_t w, uint32_t h, uint16_t *data); - void pushImage(uint32_t x0, uint32_t y0, uint32_t w, uint32_t h, const uint16_t *data, bool swap = false); + // Write an image (colour bitmap) to the sprite + void pushImage(int32_t x0, int32_t y0, uint32_t w, uint32_t h, uint16_t *data); + void pushImage(int32_t x0, int32_t y0, uint32_t w, uint32_t h, const uint16_t *data); + + // Swap the byte order for pushImage() - corrects different image endianness + void setSwapBytes(bool swap); + bool getSwapBytes(void); // Push the sprite to the TFT screen, this fn calls pushImage() in the TFT class. // Optionally a "transparent" colour can be defined, pixels of that colour will not be rendered @@ -653,6 +661,8 @@ class TFT_eSprite : public TFT_eSPI { uint32_t _sw, _sh; // w,h for scroll zone uint32_t _scolor; // gap fill colour for scroll zone + boolean _iswapBytes; // Swap the byte order for Sprite pushImage() + int32_t _iwidth, _iheight; // Sprite image width and height }; @@ -660,23 +670,3 @@ class TFT_eSprite : public TFT_eSPI { #endif - -/*************************************************** - - ORIGINAL LIBRARY HEADER - - This is our library for the Adafruit ILI9341 Breakout and Shield - ----> http://www.adafruit.com/products/1651 - - Check out the links above for our tutorials and wiring diagrams - These displays use SPI to communicate, 4 or 5 pins are required to - interface (RST is optional) - Adafruit invests time and resources providing this open source code, - please support Adafruit and open-source hardware by purchasing - products from Adafruit! - - Written by Limor Fried/Ladyada for Adafruit Industries. - MIT license, all text above must be included in any redistribution - - Updated with new functions by Bodmer 14/4/15 - ****************************************************/ diff --git a/User_Setup_Select.h b/User_Setup_Select.h index 3875450..53be50d 100644 --- a/User_Setup_Select.h +++ b/User_Setup_Select.h @@ -14,8 +14,11 @@ // Customised User_Setup files are stored in the "User_Setups" folder. -// Only ONE line should be uncommented. Add extra lines and files as needed. -#ifndef USER_SETUP_LOADED +#ifndef USER_SETUP_LOADED // Lets PlatformIO users define user settings in + // platformio.ini, see notes in "Tools" folder. + +// Only ONE line below should be uncommented. Add extra lines and files as needed. + #include // Default setup is root library folder //#include // Setup file configured for my ILI9341 @@ -31,7 +34,11 @@ //#include // Setup file configured for my stock RPi TFT with touch //#include // Setup file template for copying/editting -#endif + + +#endif // USER_SETUP_LOADED + + ///////////////////////////////////////////////////////////////////////////////////// // // diff --git a/library.properties b/library.properties index 61ea335..d2ace71 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=TFT_eSPI -version=0.17.20 +version=0.18.10 author=Bodmer maintainer=Bodmer sentence=A fast TFT library for ESP8266 processors and the Arduino IDE From 523fec4e3f25b54cb4fee0b25b00a2f47c565a0e Mon Sep 17 00:00:00 2001 From: Bodmer Date: Mon, 8 Jan 2018 23:37:02 +0000 Subject: [PATCH 067/150] Update --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6a109c8..da4a689 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ Configuration of the library font selections, pins used to interface with the TF ## The future... ## -**1. Performance improvements** +**1. Performance improvements - done 8/1/18** I have made some changes that will be uploaded soon that improves sprite and image rendering performance by up to 3x faster on the ESP8266. These updates are currently being tested/debugged. From c7ee9c27e3d90840ca39e03e8c67ab5b5cab14a9 Mon Sep 17 00:00:00 2001 From: per1234 Date: Thu, 25 Jan 2018 18:28:41 -0800 Subject: [PATCH 068/150] Correct capitalization of keywords.txt Incorrect capitalization of the filename caused keywords highlighting to not work on filename case sensitive operating systems. --- Keywords.txt => keywords.txt | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename Keywords.txt => keywords.txt (100%) diff --git a/Keywords.txt b/keywords.txt similarity index 100% rename from Keywords.txt rename to keywords.txt From 6c993f8fd2b6bc9bf2abff402b737bf9dd96b2fa Mon Sep 17 00:00:00 2001 From: Bodmer Date: Fri, 26 Jan 2018 15:19:40 +0000 Subject: [PATCH 069/150] Add getCursorX() and getCursorY() functions As requested in Issue #79 Function names are compatible with Adafruit_GFX Also trimmed out duplicate keywords --- TFT_eSPI.cpp | 19 +++++++++++++++++++ TFT_eSPI.h | 3 +++ keywords.txt | 8 ++------ 3 files changed, 24 insertions(+), 6 deletions(-) diff --git a/TFT_eSPI.cpp b/TFT_eSPI.cpp index ea82c7b..7243981 100644 --- a/TFT_eSPI.cpp +++ b/TFT_eSPI.cpp @@ -1525,6 +1525,25 @@ void TFT_eSPI::setCursor(int16_t x, int16_t y, uint8_t font) } +/*************************************************************************************** +** Function name: getCursorX +** Description: Get the text cursor x position +***************************************************************************************/ +int16_t TFT_eSPI::getCursorX(void) +{ + return cursor_x; +} + +/*************************************************************************************** +** Function name: getCursorY +** Description: Get the text cursor y position +***************************************************************************************/ +int16_t TFT_eSPI::getCursorY(void) +{ + return cursor_y; +} + + /*************************************************************************************** ** Function name: setTextSize ** Description: Set the text size multiplier diff --git a/TFT_eSPI.h b/TFT_eSPI.h index 840c50b..bb08447 100644 --- a/TFT_eSPI.h +++ b/TFT_eSPI.h @@ -429,6 +429,9 @@ class TFT_eSPI : public Print { getTextDatum(void), color332(uint16_t color565); // Convert 16 bit colour to 8 bits + int16_t getCursorX(void), + getCursorY(void); + uint16_t fontsLoaded(void), color565(uint8_t r, uint8_t g, uint8_t b); diff --git a/keywords.txt b/keywords.txt index dba1241..13748fc 100644 --- a/keywords.txt +++ b/keywords.txt @@ -7,8 +7,6 @@ setAddrWindow KEYWORD2 setWindow KEYWORD2 readAddrWindow KEYWORD2 pushColor KEYWORD2 -pushColor KEYWORD2 -pushColors KEYWORD2 pushColors KEYWORD2 fillScreen KEYWORD2 writeBegin KEYWORD2 @@ -32,8 +30,8 @@ drawTriangle KEYWORD2 fillTriangle KEYWORD2 drawBitmap KEYWORD2 setCursor KEYWORD2 -setCursor KEYWORD2 -setTextColor KEYWORD2 +getCursorX KEYWORD2 +getCursorY KEYWORD2 setTextColor KEYWORD2 setTextSize KEYWORD2 setTextFont KEYWORD2 @@ -59,7 +57,6 @@ getTextDatum KEYWORD2 fontsLoaded KEYWORD2 color565 KEYWORD2 color332 KEYWORD2 -drawChar KEYWORD2 drawNumber KEYWORD2 drawFloat KEYWORD2 drawString KEYWORD2 @@ -97,7 +94,6 @@ createSprite KEYWORD2 setColorDepth KEYWORD2 deleteSprite KEYWORD2 fillSprite KEYWORD2 -setWindow KEYWORD2 pushBitmap KEYWORD2 pushSprite KEYWORD2 setScrollRect KEYWORD2 From b2e4e69f7a32b499826645afc345b0d294764f90 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Sat, 27 Jan 2018 02:35:25 +0000 Subject: [PATCH 070/150] Fix Issue #81 --- TFT_eSPI.cpp | 6 +++--- TFT_eSPI.h | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/TFT_eSPI.cpp b/TFT_eSPI.cpp index 7243981..4cb0ef9 100644 --- a/TFT_eSPI.cpp +++ b/TFT_eSPI.cpp @@ -3306,7 +3306,7 @@ void TFT_eSPI::drawFastHLine(int32_t x, int32_t y, int32_t w, uint32_t color) ** Description: draw a filled rectangle ***************************************************************************************/ #if defined (ESP8266) && !defined (RPI_WRITE_STROBE) -void TFT_eSPI::fillRect(int32_t x, int32_t y, uint32_t w, uint32_t h, uint32_t color) +void TFT_eSPI::fillRect(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color) { // rudimentary clipping (drawChar w/big text requires this) if ((x > _width) || (y > _height) || (w < 1) || (h < 1)) return; @@ -3325,7 +3325,7 @@ void TFT_eSPI::fillRect(int32_t x, int32_t y, uint32_t w, uint32_t h, uint32_t c #else -void TFT_eSPI::fillRect(int32_t x, int32_t y, uint32_t w, uint32_t h, uint32_t color) +void TFT_eSPI::fillRect(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color) { // rudimentary clipping (drawChar w/big text requires this) if ((x > _width) || (y > _height) || (w < 1) || (h < 1)) return; @@ -5566,7 +5566,7 @@ void TFT_eSprite::drawFastHLine(int32_t x, int32_t y, int32_t w, uint32_t color) ** Function name: fillRect ** Description: draw a filled rectangle *************************************************************************************x*/ -void TFT_eSprite::fillRect(int32_t x, int32_t y, uint32_t w, uint32_t h, uint32_t color) +void TFT_eSprite::fillRect(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color) { if (!_created ) return; diff --git a/TFT_eSPI.h b/TFT_eSPI.h index bb08447..84e0b00 100644 --- a/TFT_eSPI.h +++ b/TFT_eSPI.h @@ -334,7 +334,7 @@ class TFT_eSPI : public Print { drawLine(int32_t x0, int32_t y0, int32_t x1, int32_t y1, uint32_t color), drawFastVLine(int32_t x, int32_t y, int32_t h, uint32_t color), drawFastHLine(int32_t x, int32_t y, int32_t w, uint32_t color), - fillRect(int32_t x, int32_t y, uint32_t w, uint32_t h, uint32_t color); + fillRect(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color); virtual int16_t drawChar(unsigned int uniCode, int x, int y, int font), drawChar(unsigned int uniCode, int x, int y); @@ -617,7 +617,7 @@ class TFT_eSprite : public TFT_eSPI { drawFastVLine(int32_t x, int32_t y, int32_t h, uint32_t color), drawFastHLine(int32_t x, int32_t y, int32_t w, uint32_t color), - fillRect(int32_t x, int32_t y, uint32_t w, uint32_t h, uint32_t color), + fillRect(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color), // Set the sprite text cursor position for print class (does not change the TFT screen cursor) setCursor(int16_t x, int16_t y); From 1f84ab08859514516ec3611b02d6909c5519338c Mon Sep 17 00:00:00 2001 From: Bodmer Date: Sat, 27 Jan 2018 13:46:59 +0000 Subject: [PATCH 071/150] Reinstate 8bit pushColors() Permits backwards compatibility --- TFT_eSPI.cpp | 216 +++++++-------------------------------------------- TFT_eSPI.h | 2 +- 2 files changed, 31 insertions(+), 187 deletions(-) diff --git a/TFT_eSPI.cpp b/TFT_eSPI.cpp index 4cb0ef9..ae21acf 100644 --- a/TFT_eSPI.cpp +++ b/TFT_eSPI.cpp @@ -2736,6 +2736,36 @@ void TFT_eSPI::pushColor(uint16_t color, uint16_t len) } +/*************************************************************************************** +** Function name: pushColors +** Description: push an aray of pixels for 16 bit raw image drawing +***************************************************************************************/ +// Assumed that setWindow() has previously been called + +void TFT_eSPI::pushColors(uint8_t *data, uint32_t len) +{ + spi_begin(); + + CS_L; + +#if defined (RPI_WRITE_STROBE) + while ( len >=64 ) {SPI.writePattern(data, 64, 1); data += 64; len -= 64; } + if (len) SPI.writePattern(data, len, 1); +#else + #if (SPI_FREQUENCY == 80000000) + while ( len >=64 ) {SPI.writePattern(data, 64, 1); data += 64; len -= 64; } + if (len) SPI.writePattern(data, len, 1); + #else + SPI.writeBytes(data, len); + #endif +#endif + + CS_H; + + spi_end(); +} + + /*************************************************************************************** ** Function name: pushColors ** Description: push an array of pixels, for image drawing @@ -2822,41 +2852,6 @@ else SPI.writeBytes((uint8_t*)data,len<<1); SPI1CMD |= SPIBUSY; } -/* // Smaller version but slower - uint32_t count = 0; - while(len) - { - if(len>15) {count = 16; len -= 16;} - else {count = len; len = 0;} - uint32_t bits = (count*16-1); // bits left to shift - 1 - if (swap) - { - uint16_t* ptr = (uint16_t*)color; - while(count--) - { - *ptr++ = (*(data) >> 8) | (uint16_t)(*(data) << 8); - data++; - } - } - else - { - memcpy(color,data,count<<1); - data += 16; - } - while(SPI1CMD & SPIBUSY) {} - SPI1U1 = (SPI1U1 & mask) | (bits << SPILMOSI) | (bits << SPILMISO); - SPI1W0 = color[0]; - SPI1W1 = color[1]; - SPI1W2 = color[2]; - SPI1W3 = color[3]; - SPI1W4 = color[4]; - SPI1W5 = color[5]; - SPI1W6 = color[6]; - SPI1W7 = color[7]; - SPI1CMD |= SPIBUSY; - } -*/ - while(SPI1CMD & SPIBUSY) {} #endif @@ -2866,157 +2861,6 @@ else SPI.writeBytes((uint8_t*)data,len<<1); spi_end(); } -/*************************************************************************************** -** Function name: pushColors -** Description: push an aray of pixels for BMP image drawing -***************************************************************************************/ -// Sends an array of 16-bit color values to the TFT; used -// externally by BMP examples. Assumes that setWindow() has -// previously been called to define the bounds. Max 255 pixels at -// a time (BMP examples read in small chunks due to limited RAM). -/* -#define SWAP_COLOR_BYTES // We want to swap color bytes in this fn -void TFT_eSPI::pushColors(uint16_t *data, uint8_t len) -{ - spi_begin(); - - CS_L; - -#if defined (ESP32) - - SPI.writePixels(data,len<<1); - -#else - - uint32_t mask = ~((SPIMMOSI << SPILMOSI) | (SPIMMISO << SPILMISO)); - SPI1U1 = (SPI1U1 & mask) | (255 << SPILMOSI) | (255 << SPILMISO); - - uint32_t color[8]; - - while(len>15) - { - -#ifdef SWAP_COLOR_BYTES - uint32_t i = 0; - while(i<8) - { - color[i] = (*(data) >> 8) | (uint16_t)(*(data) << 8); - data++; - color[i] |= ((*(data) >> 8) | (*(data) << 8)) << 16; - data++; - i++; - } -#else - uint32_t i = 0; - while(i<8) - { - color[i] = *data++; - color[i] |= (*data++) << 16; - i++; - } -#endif - - len -= 16; - while(SPI1CMD & SPIBUSY) {} - SPI1W0 = color[0]; - SPI1W1 = color[1]; - SPI1W2 = color[2]; - SPI1W3 = color[3]; - SPI1W4 = color[4]; - SPI1W5 = color[5]; - SPI1W6 = color[6]; - SPI1W7 = color[7]; - SPI1CMD |= SPIBUSY; - } - if (len) - { - while(SPI1CMD & SPIBUSY) {} - SPI1U1 = (SPI1U1 & mask) | (15 << SPILMOSI) | (15 << SPILMISO); - while(len--) - { - uint16_t color = (*(data) >> 8) | (*(data) << 8); - data++; - while(SPI1CMD & SPIBUSY) {} - SPI1W0 = color; - SPI1CMD |= SPIBUSY; - } - } - while(SPI1CMD & SPIBUSY) {} - -#endif - - CS_H; - - spi_end(); -} -*/ - -/*************************************************************************************** -** Function name: pushColors -** Description: push an array of pixels for 16 bit raw image drawing -***************************************************************************************/ -// Assumed that setWindow() has previously been called -// There is no control of endianness -/*void TFT_eSPI::pushColors(uint8_t *data, uint32_t len) -{ - spi_begin(); - - CS_L; - - // We cannot align to 32 bits since array may be created as 8 bit originally - while ( len >=64 ) {SPI.writePattern(data, 64, 1); data += 64; len -= 64; } - if (len) SPI.writePattern(data, len, 1); - - CS_H; - - spi_end(); -} -*/ - -/*************************************************************************************** -** Function name: pushColors -** Description: push an aray of pixels with byte swap option -***************************************************************************************/ -/* -void TFT_eSPI::pushColors(uint16_t *data, uint32_t len, bool swap) -{ - spi_begin(); - - CS_L; - -#if defined (ESP32) - if (swap) - { - //while (len--) SPI.write16(*(data++)); - SPI.writePixels(data,len<<1); - } - else - { - SPI.writeBytes((uint8_t*)data,len<<1); - } -#else - if (swap) - { - while (len>239) { pushColors(data, 240); len -= 240; data += 240; } - if (len) pushColors(data, len); - } - else - { - // Check alignment of 16 bit pointer to 32 bits - uint8_t offset = ((uint32_t)data & 0x2)>>1; - if (offset > len) offset = len; - - // Make pointer 32 bit align using immune call then use the faster writeBytes() - if (offset) { SPI.writePattern((uint8_t*)data, offset<<1, 1); len -= offset; data += offset; } - - if (len) SPI.writeBytes((uint8_t*)data, len<<1); - } -#endif - CS_H; - - spi_end(); -} -*/ /*************************************************************************************** ** Function name: drawLine diff --git a/TFT_eSPI.h b/TFT_eSPI.h index 84e0b00..6bcc4ae 100644 --- a/TFT_eSPI.h +++ b/TFT_eSPI.h @@ -344,7 +344,7 @@ class TFT_eSPI : public Print { pushColor(uint16_t color), pushColor(uint16_t color, uint16_t len), //pushColors(uint16_t *data, uint8_t len), - //pushColors(uint8_t *data, uint32_t len), + pushColors(uint8_t *data, uint32_t len), pushColors(uint16_t *data, uint32_t len, bool swap = true), // With byte swap option fillScreen(uint32_t color); From 9de702f8b39958c74ddc5193616e0079c4b29183 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Sat, 27 Jan 2018 13:47:57 +0000 Subject: [PATCH 072/150] Update issue --- library.json | 2 +- library.properties | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/library.json b/library.json index d189858..e9ebbbc 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "TFT_eSPI", - "version": "0.17.20", + "version": "0.18.11", "keywords": "TFT, ESP8266, NodeMCU, ESP32, ILI9341, ST7735, ILI9163, S6D02A1, ILI9486", "description": "A TFT SPI graphics library for ESP8266", "repository": diff --git a/library.properties b/library.properties index d2ace71..1f03c92 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=TFT_eSPI -version=0.18.10 +version=0.18.11 author=Bodmer maintainer=Bodmer sentence=A fast TFT library for ESP8266 processors and the Arduino IDE From 139fe8dc4c1631517ce3b6dd8963932d23a5caac Mon Sep 17 00:00:00 2001 From: Bodmer Date: Sat, 27 Jan 2018 14:20:22 +0000 Subject: [PATCH 073/150] Stop y wrapping I need this in my project but will add a switch. --- TFT_eSPI.cpp | 4 ++-- library.json | 2 +- library.properties | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/TFT_eSPI.cpp b/TFT_eSPI.cpp index ae21acf..e14335d 100644 --- a/TFT_eSPI.cpp +++ b/TFT_eSPI.cpp @@ -3308,7 +3308,7 @@ size_t TFT_eSPI::write(uint8_t utf8) cursor_y += height; cursor_x = 0; } - if (cursor_y >= _height) cursor_y = 0; + //if (cursor_y >= _height) cursor_y = 0; cursor_x += drawChar(uniCode, cursor_x, cursor_y, textfont); } @@ -3338,7 +3338,7 @@ size_t TFT_eSPI::write(uint8_t utf8) cursor_y += (int16_t)textsize * (uint8_t)pgm_read_byte(&gfxFont->yAdvance); } - if (cursor_y >= _height) cursor_y = 0; + //if (cursor_y >= _height) cursor_y = 0; drawChar(cursor_x, cursor_y, uniCode, textcolor, textbgcolor, textsize); } cursor_x += pgm_read_byte(&glyph->xAdvance) * (int16_t)textsize; diff --git a/library.json b/library.json index e9ebbbc..b1658d7 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "TFT_eSPI", - "version": "0.18.11", + "version": "0.18.12", "keywords": "TFT, ESP8266, NodeMCU, ESP32, ILI9341, ST7735, ILI9163, S6D02A1, ILI9486", "description": "A TFT SPI graphics library for ESP8266", "repository": diff --git a/library.properties b/library.properties index 1f03c92..8874389 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=TFT_eSPI -version=0.18.11 +version=0.18.12 author=Bodmer maintainer=Bodmer sentence=A fast TFT library for ESP8266 processors and the Arduino IDE From f3190fde710efe0b98163a080510b98cffe1fd2e Mon Sep 17 00:00:00 2001 From: Bodmer Date: Sat, 27 Jan 2018 19:22:42 +0000 Subject: [PATCH 074/150] Allow init() to be called in sketch to reset and re-initialise TFT Solution to Issue #85 --- TFT_eSPI.cpp | 39 ++++++++++++++++++++++++--------------- TFT_eSPI.h | 1 + library.json | 2 +- library.properties | 2 +- 4 files changed, 27 insertions(+), 17 deletions(-) diff --git a/TFT_eSPI.cpp b/TFT_eSPI.cpp index e14335d..184dbfe 100644 --- a/TFT_eSPI.cpp +++ b/TFT_eSPI.cpp @@ -123,6 +123,8 @@ TFT_eSPI::TFT_eSPI(int16_t w, int16_t h) locked = true; // ESP32 transaction mutex lock flags inTransaction = false; + _booted = true; + addr_row = 0xFFFF; addr_col = 0xFFFF; @@ -169,6 +171,8 @@ void TFT_eSPI::begin(void) ***************************************************************************************/ void TFT_eSPI::init(void) { + if (_booted) + { #if !defined (ESP32) #ifdef TFT_CS cspinmask = (uint32_t) digitalPinToBitMask(TFT_CS); @@ -189,7 +193,7 @@ void TFT_eSPI::init(void) SPI.pins(6, 7, 8, 0); #endif - SPI.begin(); // This will set HMISO to input + SPI.begin(); // This will set HMISO to input #else #if defined (TFT_MOSI) && !defined (TFT_SPI_OVERLAP) SPI.begin(TFT_SCLK, TFT_MISO, TFT_MOSI, -1); @@ -199,31 +203,35 @@ void TFT_eSPI::init(void) #endif - inTransaction = false; - locked = true; + inTransaction = false; + locked = true; // SUPPORT_TRANSACTIONS is manadatory for ESP32 so the hal mutex is toggled // so the code here is for ESP8266 only #if !defined (SUPPORT_TRANSACTIONS) && defined (ESP8266) - SPI.setBitOrder(MSBFIRST); - SPI.setDataMode(SPI_MODE0); - SPI.setFrequency(SPI_FREQUENCY); + SPI.setBitOrder(MSBFIRST); + SPI.setDataMode(SPI_MODE0); + SPI.setFrequency(SPI_FREQUENCY); #endif // Set to output once again in case D6 (MISO) is used for CS #ifdef TFT_CS - digitalWrite(TFT_CS, HIGH); // Chip select high (inactive) - pinMode(TFT_CS, OUTPUT); + digitalWrite(TFT_CS, HIGH); // Chip select high (inactive) + pinMode(TFT_CS, OUTPUT); #else - SPI.setHwCs(1); // Use hardware SS toggling + SPI.setHwCs(1); // Use hardware SS toggling #endif // Set to output once again in case D6 (MISO) is used for DC #ifdef TFT_DC - digitalWrite(TFT_DC, HIGH); // Data/Command high = data mode - pinMode(TFT_DC, OUTPUT); + digitalWrite(TFT_DC, HIGH); // Data/Command high = data mode + pinMode(TFT_DC, OUTPUT); #endif + _booted = false; + } // end of: if just _booted + + // Toggle RST low to reset #ifdef TFT_RST if (TFT_RST >= 0) { @@ -234,13 +242,13 @@ void TFT_eSPI::init(void) digitalWrite(TFT_RST, HIGH); delay(150); } -#endif - +#else + // Or use the software reset spi_begin(); writecommand(TFT_SWRST); // Software reset spi_end(); - - delay(5); // Wait for software reset to complete + delay(120); // Wait for software reset to complete +#endif spi_begin(); @@ -264,6 +272,7 @@ void TFT_eSPI::init(void) spi_end(); + setRotation(rotation); } diff --git a/TFT_eSPI.h b/TFT_eSPI.h index 6bcc4ae..2c54570 100644 --- a/TFT_eSPI.h +++ b/TFT_eSPI.h @@ -516,6 +516,7 @@ class TFT_eSPI : public Print { bool textwrap; // If set, 'wrap' text at right edge of display bool _swapBytes; // Swap the byte order for TFT pushImage() bool locked, inTransaction; // Transaction and mutex lock flags for ESP32 + bool _booted; int32_t _lastColor; diff --git a/library.json b/library.json index b1658d7..67bcfc4 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "TFT_eSPI", - "version": "0.18.12", + "version": "0.18.13", "keywords": "TFT, ESP8266, NodeMCU, ESP32, ILI9341, ST7735, ILI9163, S6D02A1, ILI9486", "description": "A TFT SPI graphics library for ESP8266", "repository": diff --git a/library.properties b/library.properties index 8874389..0dfd3e6 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=TFT_eSPI -version=0.18.12 +version=0.18.13 author=Bodmer maintainer=Bodmer sentence=A fast TFT library for ESP8266 processors and the Arduino IDE From cf80bc22ad9fae43fc856c56d66f4795a9be090e Mon Sep 17 00:00:00 2001 From: Bodmer Date: Tue, 6 Feb 2018 23:39:41 +0000 Subject: [PATCH 075/150] Update keywords --- library.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library.json b/library.json index 67bcfc4..026f299 100644 --- a/library.json +++ b/library.json @@ -1,7 +1,7 @@ { "name": "TFT_eSPI", - "version": "0.18.13", - "keywords": "TFT, ESP8266, NodeMCU, ESP32, ILI9341, ST7735, ILI9163, S6D02A1, ILI9486", + "version": "0.18.14", + "keywords": "tft, display, ESP8266, NodeMCU, ESP32, ILI9341, ST7735, ILI9163, S6D02A1, ILI9486", "description": "A TFT SPI graphics library for ESP8266", "repository": { From fe36cbda99e99b8eddbbb7d9b0cc279f74045337 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Tue, 6 Feb 2018 23:40:45 +0000 Subject: [PATCH 076/150] Update keywords --- library.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library.properties b/library.properties index 0dfd3e6..c6ff549 100644 --- a/library.properties +++ b/library.properties @@ -1,4 +1,4 @@ -name=TFT_eSPI +4name=TFT_eSPI version=0.18.13 author=Bodmer maintainer=Bodmer From 2854535779ba58cb1e4968a24655c5ac3956b020 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Thu, 8 Feb 2018 00:45:18 +0000 Subject: [PATCH 077/150] Add support for ESP32 based M5Stack --- TFT_Drivers/ILI9341_Init.h | 13 +++++- TFT_Drivers/ILI9341_Rotation.h | 34 +++++++++++++- User_Setup_Select.h | 1 + User_Setups/Setup12_M5Stack.h | 85 ++++++++++++++++++++++++++++++++++ library.properties | 4 +- 5 files changed, 133 insertions(+), 4 deletions(-) create mode 100644 User_Setups/Setup12_M5Stack.h diff --git a/TFT_Drivers/ILI9341_Init.h b/TFT_Drivers/ILI9341_Init.h index c867e4e..124c849 100644 --- a/TFT_Drivers/ILI9341_Init.h +++ b/TFT_Drivers/ILI9341_Init.h @@ -55,7 +55,11 @@ writedata(0x86); //-- writecommand(ILI9341_MADCTL); // Memory Access Control - writedata(0x48); +#ifdef M5STACK + writedata(0xA8); // Rotation 0 (portrait mode) +#else + writedata(0x48); // Rotation 0 (portrait mode) +#endif writecommand(ILI9341_PIXFMT); writedata(0x55); @@ -116,4 +120,11 @@ spi_begin(); writecommand(ILI9341_DISPON); //Display on + +#ifdef M5STACK + // Turn on the back-light LED + digitalWrite(TFT_BL, HIGH); + pinMode(TFT_BL, OUTPUT); +#endif + } \ No newline at end of file diff --git a/TFT_Drivers/ILI9341_Rotation.h b/TFT_Drivers/ILI9341_Rotation.h index 0af4fa7..6ad18e0 100644 --- a/TFT_Drivers/ILI9341_Rotation.h +++ b/TFT_Drivers/ILI9341_Rotation.h @@ -6,43 +6,75 @@ writecommand(TFT_MADCTL); switch (rotation) { case 0: +#ifdef M5STACK + writedata(TFT_MAD_MY | TFT_MAD_MV | TFT_MAD_BGR); +#else writedata(TFT_MAD_MX | TFT_MAD_BGR); +#endif _width = _width_orig; _height = _height_orig; break; case 1: +#ifdef M5STACK + writedata(TFT_MAD_BGR); +#else writedata(TFT_MAD_MV | TFT_MAD_BGR); +#endif _width = _height_orig; _height = _width_orig; break; case 2: +#ifdef M5STACK + writedata(TFT_MAD_MV | TFT_MAD_MX | TFT_MAD_BGR); +#else writedata(TFT_MAD_MY | TFT_MAD_BGR); +#endif _width = _width_orig; _height = _height_orig; break; case 3: +#ifdef M5STACK + writedata(TFT_MAD_MX | TFT_MAD_MY | TFT_MAD_BGR); +#else writedata(TFT_MAD_MX | TFT_MAD_MY | TFT_MAD_MV | TFT_MAD_BGR); +#endif _width = _height_orig; _height = _width_orig; break; - // These next rotations are for bottum up BMP drawing + // These next rotations are for bottom up BMP drawing case 4: +#ifdef M5STACK + writedata(TFT_MAD_MV | TFT_MAD_MX | TFT_MAD_BGR); +#else writedata(TFT_MAD_MX | TFT_MAD_MY | TFT_MAD_BGR); +#endif _width = _width_orig; _height = _height_orig; break; case 5: +#ifdef M5STACK + writedata(TFT_MAD_MV | TFT_MAD_BGR); +#else writedata(TFT_MAD_MV | TFT_MAD_MX | TFT_MAD_BGR); +#endif _width = _height_orig; _height = _width_orig; break; case 6: +#ifdef M5STACK + writedata(TFT_MAD_MX | TFT_MAD_MY | TFT_MAD_MV | TFT_MAD_BGR); +#else writedata(TFT_MAD_BGR); +#endif _width = _width_orig; _height = _height_orig; break; case 7: +#ifdef M5STACK + writedata(TFT_MAD_MY | TFT_MAD_BGR); +#else writedata(TFT_MAD_MY | TFT_MAD_MV | TFT_MAD_BGR); +#endif _width = _height_orig; _height = _width_orig; break; diff --git a/User_Setup_Select.h b/User_Setup_Select.h index 53be50d..b62a8b0 100644 --- a/User_Setup_Select.h +++ b/User_Setup_Select.h @@ -32,6 +32,7 @@ //#include // Setup file configured for my ST7735 //#include // Setup file configured for my stock RPi TFT with touch //#include // Setup file configured for my stock RPi TFT with touch +//#include // Setup file for the ESP32 based M5Stack //#include // Setup file template for copying/editting diff --git a/User_Setups/Setup12_M5Stack.h b/User_Setups/Setup12_M5Stack.h new file mode 100644 index 0000000..f307497 --- /dev/null +++ b/User_Setups/Setup12_M5Stack.h @@ -0,0 +1,85 @@ +// USER DEFINED SETTINGS +// +// The User_Setup header that will be called up is defined in User_Setup_Select.h +// +// Set driver type, fonts to be loaded, pins used and SPI control method etc +// +// If this file is edited correctly then all the library example sketches should +// run without the need to make any more changes for a particular hardware setup! + +// ################################################################################## +// +// Section 0. Call up the right driver file and any options for it +// +// ################################################################################## + +#define ILI9341_DRIVER + +#define M5STACK + +// ################################################################################## +// +// Section 1. Define the pins that are used to interface with the display here +// +// ################################################################################## + +// M5Stack +#define TFT_MISO 19 +#define TFT_MOSI 23 +#define TFT_SCLK 18 +#define TFT_CS 14 // Chip select control pin +#define TFT_DC 27 // Data Command control pin +#define TFT_RST 33 // Reset pin (could connect to Arduino RESET pin) +#define TFT_BL 32 // LED back-light + +// ################################################################################## +// +// Section 2. Define the way the DC and/or CS lines are driven +// +// ################################################################################## + +// Not used + +// ################################################################################## +// +// Section 3. Define the fonts that are to be used here +// +// ################################################################################## + +// Comment out the #defines below with // to stop that font being loaded +// The ESP8366 had plenty of memory so commenting out fonts is not normally necessary +// If all fonts are loaded the extra FLASH space required is about 17Kbytes... +// To save FLASH space only enable the fonts you need! + +#define LOAD_GLCD // Font 1. Original Adafruit 8 pixel font needs ~1820 bytes in FLASH +#define LOAD_FONT2 // Font 2. Small 16 pixel high font, needs ~3534 bytes in FLASH, 96 characters +#define LOAD_FONT4 // Font 4. Medium 26 pixel high font, needs ~5848 bytes in FLASH, 96 characters +#define LOAD_FONT6 // Font 6. Large 48 pixel font, needs ~2666 bytes in FLASH, only characters 1234567890:-.apm +#define LOAD_FONT7 // Font 7. 7 segment 48 pixel font, needs ~2438 bytes in FLASH, only characters 1234567890:. +#define LOAD_FONT8 // Font 8. Large 75 pixel font needs ~3256 bytes in FLASH, only characters 1234567890:-. +#define LOAD_GFXFF // FreeFonts. Include access to the 48 Adafruit_GFX free fonts FF1 to FF48 and custom fonts + +// ################################################################################## +// +// Section 4. Not used +// +// ################################################################################## + + +// ################################################################################## +// +// Section 5. Other options +// +// ################################################################################## + +// Define the SPI clock frequency +// With an ILI9341 display 40MHz works OK, 80MHz sometimes fails +// With a ST7735 display more than 27MHz may not work (spurious pixels and lines) + +// #define SPI_FREQUENCY 1000000 +// #define SPI_FREQUENCY 5000000 +// #define SPI_FREQUENCY 10000000 +// #define SPI_FREQUENCY 20000000 +// #define SPI_FREQUENCY 27000000 // Actually sets it to 26.67MHz = 80/3 + #define SPI_FREQUENCY 40000000 // Maximum to use SPIFFS +// #define SPI_FREQUENCY 80000000 diff --git a/library.properties b/library.properties index c6ff549..efbb286 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ -4name=TFT_eSPI -version=0.18.13 +name=TFT_eSPI +version=0.18.14 author=Bodmer maintainer=Bodmer sentence=A fast TFT library for ESP8266 processors and the Arduino IDE From e9fa103aae9e4b6ff9afad1141c1d87b8d4a2ce0 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Thu, 8 Feb 2018 00:53:14 +0000 Subject: [PATCH 078/150] Updaye keywords --- library.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library.json b/library.json index 026f299..fd529ff 100644 --- a/library.json +++ b/library.json @@ -1,7 +1,7 @@ { "name": "TFT_eSPI", "version": "0.18.14", - "keywords": "tft, display, ESP8266, NodeMCU, ESP32, ILI9341, ST7735, ILI9163, S6D02A1, ILI9486", + "keywords": "tft, display, ESP8266, NodeMCU, ESP32, M5Stack, ILI9341, ST7735, ILI9163, S6D02A1, ILI9486", "description": "A TFT SPI graphics library for ESP8266", "repository": { From b737a398b37210010c9844e675709a0464ff3a68 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Thu, 8 Feb 2018 00:56:50 +0000 Subject: [PATCH 079/150] Correct version add ESP32 --- library.json | 4 ++-- library.properties | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/library.json b/library.json index fd529ff..8e8e334 100644 --- a/library.json +++ b/library.json @@ -1,8 +1,8 @@ { "name": "TFT_eSPI", - "version": "0.18.14", + "version": "0.18.15", "keywords": "tft, display, ESP8266, NodeMCU, ESP32, M5Stack, ILI9341, ST7735, ILI9163, S6D02A1, ILI9486", - "description": "A TFT SPI graphics library for ESP8266", + "description": "A TFT SPI graphics library for ESP8266 and ESP32", "repository": { "type": "git", diff --git a/library.properties b/library.properties index efbb286..b08cc2f 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=TFT_eSPI -version=0.18.14 +version=0.18.15 author=Bodmer maintainer=Bodmer sentence=A fast TFT library for ESP8266 processors and the Arduino IDE From 299eafc45aba3e230189d9552f985c80007a3d24 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Thu, 8 Feb 2018 00:59:30 +0000 Subject: [PATCH 080/150] Add ESP32 to platform list --- library.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library.json b/library.json index 8e8e334..d315019 100644 --- a/library.json +++ b/library.json @@ -21,5 +21,5 @@ } ], "frameworks": "arduino", - "platforms": "espressif8266" + "platforms": "espressif8266, espressif32" } From e86f76c15bb152c73743aaddf20824d910b9d206 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Fri, 9 Feb 2018 20:32:55 +0000 Subject: [PATCH 081/150] Correct mirrored rotations 4-7 for M5Stack --- TFT_Drivers/ILI9341_Rotation.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/TFT_Drivers/ILI9341_Rotation.h b/TFT_Drivers/ILI9341_Rotation.h index 6ad18e0..6966f37 100644 --- a/TFT_Drivers/ILI9341_Rotation.h +++ b/TFT_Drivers/ILI9341_Rotation.h @@ -44,7 +44,7 @@ // These next rotations are for bottom up BMP drawing case 4: #ifdef M5STACK - writedata(TFT_MAD_MV | TFT_MAD_MX | TFT_MAD_BGR); + writedata(TFT_MAD_MX | TFT_MAD_MY | TFT_MAD_MV | TFT_MAD_BGR); #else writedata(TFT_MAD_MX | TFT_MAD_MY | TFT_MAD_BGR); #endif @@ -53,7 +53,7 @@ break; case 5: #ifdef M5STACK - writedata(TFT_MAD_MV | TFT_MAD_BGR); + writedata(TFT_MAD_MY | TFT_MAD_BGR); #else writedata(TFT_MAD_MV | TFT_MAD_MX | TFT_MAD_BGR); #endif @@ -62,7 +62,7 @@ break; case 6: #ifdef M5STACK - writedata(TFT_MAD_MX | TFT_MAD_MY | TFT_MAD_MV | TFT_MAD_BGR); + writedata(TFT_MAD_MV | TFT_MAD_BGR); #else writedata(TFT_MAD_BGR); #endif @@ -71,7 +71,7 @@ break; case 7: #ifdef M5STACK - writedata(TFT_MAD_MY | TFT_MAD_BGR); + writedata(TFT_MAD_MX | TFT_MAD_BGR); #else writedata(TFT_MAD_MY | TFT_MAD_MV | TFT_MAD_BGR); #endif From ff335f61c5f20519d81ec829b99c3e46a764f55a Mon Sep 17 00:00:00 2001 From: Bodmer Date: Fri, 9 Feb 2018 21:52:26 +0000 Subject: [PATCH 082/150] Make setup files consistent User setups are now all based on the same template. --- User_Setup.h | 63 +++++++--- User_Setup_Select.h | 4 +- User_Setups/Setup10_RPi_touch_ILI9486.h | 97 +++++++++++---- User_Setups/Setup11_RPi_touch_ILI9486.h | 93 +++++++++++---- User_Setups/Setup12_M5Stack.h | 150 ++++++++++++++++++++++-- User_Setups/Setup1_ILI9341.h | 105 +++++++++++++---- User_Setups/Setup2_ST7735.h | 102 ++++++++++++---- User_Setups/Setup3_ILI9163.h | 116 ++++++++++++++---- User_Setups/Setup4_S6D02A1.h | 118 +++++++++++++++---- User_Setups/Setup5_RPi_ILI9486.h | 100 ++++++++++++---- User_Setups/Setup6_RPi_Wr_ILI9486.h | 92 +++++++++++---- User_Setups/Setup7_ST7735_128x128.h | 111 +++++++++++++----- User_Setups/Setup8_ILI9163_128x128.h | 116 ++++++++++++++---- User_Setups/Setup9_ST7735_Overlap.h | 132 ++++++++++++++------- User_Setups/SetupX_Template.h | 107 +++++++++++++---- library.json | 2 +- library.properties | 2 +- 17 files changed, 1159 insertions(+), 351 deletions(-) diff --git a/User_Setup.h b/User_Setup.h index b67d6a4..c1b33dc 100644 --- a/User_Setup.h +++ b/User_Setup.h @@ -4,7 +4,7 @@ // See the User_Setup_Select.h file if you wish to be able to define multiple // setups and then easily select which setup file is used by the compiler. // -// If this file is editted correctly then all the library example sketches should +// If this file is edited correctly then all the library example sketches should // run without the need to make any more changes for a particular hardware setup! // ################################################################################## @@ -20,6 +20,14 @@ //#define S6D02A1_DRIVER //#define RPI_ILI9486_DRIVER // 20MHz maximum SPI +// For M5Stack ESP32 module with integrated display ONLY, remove // in line below +//#define M5STACK + +// For ST7735 and ILI9163 ONLY, define the pixel width and height in portrait orientation +//#define TFT_WIDTH 128 +//#define TFT_HEIGHT 160 +//#define TFT_HEIGHT 128 + // For ST7735 ONLY, define the type of display, originally this was based on the // colour of the tab on the screen protector film but this is not always true, so try // out the different options below if the screen does not display graphics correctly, @@ -35,11 +43,6 @@ //#define ST7735_REDTAB //#define ST7735_BLACKTAB -// For ST7735 ONLY, define the pixel width and height in portrait orientation -//#define TFT_WIDTH 128 -//#define TFT_HEIGHT 160 -//#define TFT_HEIGHT 128 - // ################################################################################## // // Section 1. Define the pins that are used to interface with the display here @@ -47,7 +50,7 @@ // ################################################################################## // We must use hardware SPI, a minimum of 3 GPIO pins is needed. -// Typical setup for NodeMCU ESP-12 is : +// Typical setup for ESP8266 NodeMCU ESP-12 is : // // Display SDO/MISO to NodeMCU pin D6 (or leave disconnected if not reading TFT) // Display LED to NodeMCU pin VIN (or 5V, see below) @@ -61,10 +64,10 @@ // // The TFT RESET pin can be connected to the NodeMCU RST pin or 3.3V to free up a control pin // -// The DC (Data Command) pin may be labelled AO or RS (Register Select) +// The DC (Data Command) pin may be labeled AO or RS (Register Select) // // With some displays such as the ILI9341 the TFT CS pin can be connected to GND if no more -// SPI deivces (e.g. an SD Card) are connected, in this case comment out the #define TFT_CS +// SPI devices (e.g. an SD Card) are connected, in this case comment out the #define TFT_CS // line below so it is NOT defined. Other displays such at the ST7735 require the TFT CS pin // to be toggled during setup, so in these cases the TFT_CS line must be defined and connected. // @@ -76,6 +79,7 @@ // If 5V is not available at a pin you can use 3.3V but backlight brightness // will be lower. + // ###### EDIT THE PIN NUMBERS IN THE LINES FOLLOWING TO SUIT YOUR ESP8266 SETUP ###### // For NodeMCU - use pin numbers in the form PIN_Dx where Dx is the NodeMCU pin designation @@ -86,7 +90,19 @@ //#define TOUCH_CS PIN_D1 // Chip select pin (T_CS) of touch screen -//#define TFT_WR PIN_D2 // Write strobe for modified Raspberry Pi TFT only +//#define TFT_WR PIN_D2 // Write strobe for modified Raspberry Pi TFT only + + +// ###### FOR ESP8266 OVERLAP MODE EDIT THE PIN NUMBERS IN THE FOLLOWING LINES ###### + +// In ESP8266 overlap mode the TFT chip select MUST connect to pin D3 +//#define TFT_CS PIN_D3 +//#define TFT_DC PIN_D5 // Data Command control pin +//#define TFT_RST PIN_D4 // Reset pin (could connect to NodeMCU RST, see next line) +//#define TFT_RST -1 // Set TFT_RST to -1 if the display RESET is connected to NodeMCU RST or 3.3V + +// In ESP8266 overlap mode the following must be defined +//#define TFT_SPI_OVERLAP // ###### EDIT THE PIN NUMBERS IN THE LINES FOLLOWING TO SUIT YOUR ESP32 SETUP ###### @@ -101,6 +117,15 @@ //#define TFT_RST 4 // Reset pin (could connect to RST pin) //#define TFT_RST -1 // Set TFT_RST to -1 if display RESET is connected to ESP32 board RST +// For the M5Stack module use these #define lines +//#define TFT_MISO 19 +//#define TFT_MOSI 23 +//#define TFT_SCLK 18 +//#define TFT_CS 14 // Chip select control pin +//#define TFT_DC 27 // Data Command control pin +//#define TFT_RST 33 // Reset pin (could connect to Arduino RESET pin) +//#define TFT_BL 32 // LED back-light + //#define TOUCH_CS 21 // Chip select pin (T_CS) of touch screen //#define TFT_WR 22 // Write strobe for modified Raspberry Pi TFT only @@ -127,9 +152,9 @@ // ################################################################################## // Comment out the #defines below with // to stop that font being loaded -// The ESP8366 had plenty of memory so commenting out fonts is not normally necessary -// If all fonts are loaded the extra FLASH space required is about 17Kbytes... -// To save FLASH space only enable the fonts you need! +// The ESP8366 and ESP32 have plenty of memory so commenting out fonts is not +// normally necessary. If all fonts are loaded the extra FLASH space required is +// about 17Kbytes. To save FLASH space only enable the fonts you need! #define LOAD_GLCD // Font 1. Original Adafruit 8 pixel font needs ~1820 bytes in FLASH #define LOAD_FONT2 // Font 2. Small 16 pixel high font, needs ~3534 bytes in FLASH, 96 characters @@ -163,22 +188,22 @@ // #define SPI_FREQUENCY 5000000 // #define SPI_FREQUENCY 10000000 // #define SPI_FREQUENCY 20000000 - #define SPI_FREQUENCY 27000000 // Actually sets it to 26.67MHz = 80/3 +#define SPI_FREQUENCY 27000000 // Actually sets it to 26.67MHz = 80/3 // #define SPI_FREQUENCY 40000000 // Maximum to use SPIFFS // #define SPI_FREQUENCY 80000000 -// The XPT2046 required a lower SPI clock rate of 2.5MHz so we define that here: - #define SPI_TOUCH_FREQUENCY 2500000 +// The XPT2046 requires a lower SPI clock rate of 2.5MHz so we define that here: +#define SPI_TOUCH_FREQUENCY 2500000 // Comment out the following #define if "SPI Transactions" do not need to be -// supported. Tranaction support is required if other SPI devices are connected. -// When commented out the code size will be smaller and sketches will +// supported. When commented out the code size will be smaller and sketches will // run slightly faster, so leave it commented out unless you need it! // Transaction support is needed to work with SD library but not needed with TFT_SdFat +// Transaction support is required if other SPI devices are connected. // Transactions are automatically enabled by the library for an ESP32 (to use HAL mutex) -// so changing it here has noo effect +// so changing it here has no effect // #define SUPPORT_TRANSACTIONS diff --git a/User_Setup_Select.h b/User_Setup_Select.h index b62a8b0..739db4c 100644 --- a/User_Setup_Select.h +++ b/User_Setup_Select.h @@ -1,7 +1,7 @@ // This header file contains a list of user setup files and defines which one the -// compliler uses when the IDE performs a Verify/Compile or Upload. +// compiler uses when the IDE performs a Verify/Compile or Upload. // -// Users can create configurations for different Espressiff boards and TFT displays. +// Users can create configurations for different Espressif boards and TFT displays. // This makes selecting between hardware setups easy by "uncommenting" one line. // The advantage of this hardware configuration method is that the examples provided diff --git a/User_Setups/Setup10_RPi_touch_ILI9486.h b/User_Setups/Setup10_RPi_touch_ILI9486.h index 856256f..4bf52c5 100644 --- a/User_Setups/Setup10_RPi_touch_ILI9486.h +++ b/User_Setups/Setup10_RPi_touch_ILI9486.h @@ -4,7 +4,7 @@ // See the User_Setup_Select.h file if you wish to be able to define multiple // setups and then easily select which setup file is used by the compiler. // -// If this file is editted correctly then all the library example sketches should +// If this file is edited correctly then all the library example sketches should // run without the need to make any more changes for a particular hardware setup! // ################################################################################## @@ -19,7 +19,14 @@ //#define ILI9163_DRIVER //#define S6D02A1_DRIVER #define RPI_ILI9486_DRIVER // 20MHz maximum SPI -// to enable XPT2046 touch controller, define TOUCH_CS + +// For M5Stack ESP32 module with integrated display ONLY, remove // in line below +//#define M5STACK + +// For ST7735 and ILI9163 ONLY, define the pixel width and height in portrait orientation +//#define TFT_WIDTH 128 +//#define TFT_HEIGHT 160 +//#define TFT_HEIGHT 128 // For ST7735 ONLY, define the type of display, originally this was based on the // colour of the tab on the screen protector film but this is not always true, so try @@ -32,14 +39,10 @@ //#define ST7735_GREENTAB //#define ST7735_GREENTAB2 //#define ST7735_GREENTAB3 +//#define ST7735_GREENTAB128 // For 128 x 128 display //#define ST7735_REDTAB //#define ST7735_BLACKTAB -// For ST7735 ONLY, define the pixel width and height in portrait orientation -//#define TFT_WIDTH 128 -//#define TFT_HEIGHT 160 -//#define TFT_HEIGHT 128 - // ################################################################################## // // Section 1. Define the pins that are used to interface with the display here @@ -47,13 +50,13 @@ // ################################################################################## // We must use hardware SPI, a minimum of 3 GPIO pins is needed. -// Typical setup for NodeMCU ESP-12 is : +// Typical setup for ESP8266 NodeMCU ESP-12 is : // // Display SDO/MISO to NodeMCU pin D6 (or leave disconnected if not reading TFT) // Display LED to NodeMCU pin VIN (or 5V, see below) // Display SCK to NodeMCU pin D5 // Display SDI/MOSI to NodeMCU pin D7 -// Display DC (or AO)to NodeMCU pin D3 +// Display DC (RS/AO)to NodeMCU pin D3 // Display RESET to NodeMCU pin D4 (or RST, see below) // Display CS to NodeMCU pin D8 (or GND, see below) // Display GND to NodeMCU pin GND (0V) @@ -61,8 +64,10 @@ // // The TFT RESET pin can be connected to the NodeMCU RST pin or 3.3V to free up a control pin // +// The DC (Data Command) pin may be labeled AO or RS (Register Select) +// // With some displays such as the ILI9341 the TFT CS pin can be connected to GND if no more -// SPI deivces (e.g. an SD Card) are connected, in this case comment out the #define TFT_CS +// SPI devices (e.g. an SD Card) are connected, in this case comment out the #define TFT_CS // line below so it is NOT defined. Other displays such at the ST7735 require the TFT CS pin // to be toggled during setup, so in these cases the TFT_CS line must be defined and connected. // @@ -74,7 +79,8 @@ // If 5V is not available at a pin you can use 3.3V but backlight brightness // will be lower. -// ###### EDIT THE PIN NUMBERS IN THE LINES FOLLOWING TO SUIT YOUR SETUP ###### + +// ###### EDIT THE PIN NUMBERS IN THE LINES FOLLOWING TO SUIT YOUR ESP8266 SETUP ###### // For NodeMCU - use pin numbers in the form PIN_Dx where Dx is the NodeMCU pin designation #define TFT_CS PIN_D8 // Chip select control pin D8 @@ -82,19 +88,51 @@ #define TFT_RST PIN_D4 // Reset pin (could connect to NodeMCU RST, see next line) //#define TFT_RST -1 // Set TFT_RST to -1 if the display RESET is connected to NodeMCU RST or 3.3V -#define TOUCH_CS PIN_D1 // Chip select pin (T_CS) of touch screen, also define SPI frequency in Section 5 below +#define TOUCH_CS PIN_D1 // Chip select pin (T_CS) of touch screen -//#define TFT_WR PIN_D2 // Write strobe for modified Raspberry Pi TFT only +//#define TFT_WR PIN_D2 // Write strobe for modified Raspberry Pi TFT only -// ESP32 Dev board (planned, not supported yet) -//#define TFT_CS 5 // Chip select control pin -//#define TFT_DC 2 // Data Command control pin -//#define TFT_RST 4 // Reset pin (could connect to Arduino RESET pin) + +// ###### FOR ESP8266 OVERLAP MODE EDIT THE PIN NUMBERS IN THE FOLLOWING LINES ###### + +// In ESP8266 overlap mode the TFT chip select MUST connect to pin D3 +//#define TFT_CS PIN_D3 +//#define TFT_DC PIN_D5 // Data Command control pin +//#define TFT_RST PIN_D4 // Reset pin (could connect to NodeMCU RST, see next line) +//#define TFT_RST -1 // Set TFT_RST to -1 if the display RESET is connected to NodeMCU RST or 3.3V + +// In ESP8266 overlap mode the following must be defined +//#define TFT_SPI_OVERLAP + +// ###### EDIT THE PIN NUMBERS IN THE LINES FOLLOWING TO SUIT YOUR ESP32 SETUP ###### + +// For ESP32 Dev board (only tested with ILI9341 display) +// The hardware SPI can be mapped to any pins + +//#define TFT_MISO 19 +//#define TFT_MOSI 23 +//#define TFT_SCLK 18 +//#define TFT_CS 15 // Chip select control pin +//#define TFT_DC 2 // Data Command control pin +//#define TFT_RST 4 // Reset pin (could connect to RST pin) //#define TFT_RST -1 // Set TFT_RST to -1 if display RESET is connected to ESP32 board RST +//#define TOUCH_CS 21 // Chip select pin (T_CS) of touch screen + +//#define TFT_WR 22 // Write strobe for modified Raspberry Pi TFT only + +// For the M5Stack module use these #define lines +//#define TFT_MISO 19 +//#define TFT_MOSI 23 +//#define TFT_SCLK 18 +//#define TFT_CS 14 // Chip select control pin +//#define TFT_DC 27 // Data Command control pin +//#define TFT_RST 33 // Reset pin (could connect to Arduino RESET pin) +//#define TFT_BL 32 // LED back-light + // ################################################################################## // -// Section 2. Define the way the DC and/or CS lines are driven +// Section 2. Define the way the DC and/or CS lines are driven (ESP8266 only) // // ################################################################################## @@ -114,9 +152,9 @@ // ################################################################################## // Comment out the #defines below with // to stop that font being loaded -// The ESP8366 had plenty of memory so commenting out fonts is not normally necessary -// If all fonts are loaded the extra FLASH space required is about 17Kbytes... -// To save FLASH space only enable the fonts you need! +// The ESP8366 and ESP32 have plenty of memory so commenting out fonts is not +// normally necessary. If all fonts are loaded the extra FLASH space required is +// about 17Kbytes. To save FLASH space only enable the fonts you need! #define LOAD_GLCD // Font 1. Original Adafruit 8 pixel font needs ~1820 bytes in FLASH #define LOAD_FONT2 // Font 2. Small 16 pixel high font, needs ~3534 bytes in FLASH, 96 characters @@ -139,26 +177,33 @@ // // ################################################################################## -// Define the SPI clock frequency +// Define the SPI clock frequency, this affects the graphics rendering speed. Too +// fast and the TFT driver will not keep up and display corruption appears. // With an ILI9341 display 40MHz works OK, 80MHz sometimes fails // With a ST7735 display more than 27MHz may not work (spurious pixels and lines) -// With an ILI9163 display TBD MHz works OK, +// With an ILI9163 display 27 MHz works OK. +// The RPi typically only works at 20MHz maximum. // #define SPI_FREQUENCY 1000000 // #define SPI_FREQUENCY 5000000 // #define SPI_FREQUENCY 10000000 - #define SPI_FREQUENCY 20000000 +#define SPI_FREQUENCY 20000000 // #define SPI_FREQUENCY 27000000 // Actually sets it to 26.67MHz = 80/3 // #define SPI_FREQUENCY 40000000 // Maximum to use SPIFFS // #define SPI_FREQUENCY 80000000 +// The XPT2046 requires a lower SPI clock rate of 2.5MHz so we define that here: #define SPI_TOUCH_FREQUENCY 2500000 // Comment out the following #define if "SPI Transactions" do not need to be -// supported. Tranaction support is required if other SPI devices are connected. -// When commented out the code size will be smaller and sketches will +// supported. When commented out the code size will be smaller and sketches will // run slightly faster, so leave it commented out unless you need it! + // Transaction support is needed to work with SD library but not needed with TFT_SdFat +// Transaction support is required if other SPI devices are connected. + +// Transactions are automatically enabled by the library for an ESP32 (to use HAL mutex) +// so changing it here has no effect // #define SUPPORT_TRANSACTIONS diff --git a/User_Setups/Setup11_RPi_touch_ILI9486.h b/User_Setups/Setup11_RPi_touch_ILI9486.h index de3baaa..d6218e3 100644 --- a/User_Setups/Setup11_RPi_touch_ILI9486.h +++ b/User_Setups/Setup11_RPi_touch_ILI9486.h @@ -4,7 +4,7 @@ // See the User_Setup_Select.h file if you wish to be able to define multiple // setups and then easily select which setup file is used by the compiler. // -// If this file is editted correctly then all the library example sketches should +// If this file is edited correctly then all the library example sketches should // run without the need to make any more changes for a particular hardware setup! // ################################################################################## @@ -19,7 +19,14 @@ //#define ILI9163_DRIVER //#define S6D02A1_DRIVER #define RPI_ILI9486_DRIVER // 20MHz maximum SPI -// to enable XPT2046 touch controller, define TOUCH_CS + +// For M5Stack ESP32 module with integrated display ONLY, remove // in line below +//#define M5STACK + +// For ST7735 and ILI9163 ONLY, define the pixel width and height in portrait orientation +//#define TFT_WIDTH 128 +//#define TFT_HEIGHT 160 +//#define TFT_HEIGHT 128 // For ST7735 ONLY, define the type of display, originally this was based on the // colour of the tab on the screen protector film but this is not always true, so try @@ -32,14 +39,10 @@ //#define ST7735_GREENTAB //#define ST7735_GREENTAB2 //#define ST7735_GREENTAB3 +//#define ST7735_GREENTAB128 // For 128 x 128 display //#define ST7735_REDTAB //#define ST7735_BLACKTAB -// For ST7735 ONLY, define the pixel width and height in portrait orientation -//#define TFT_WIDTH 128 -//#define TFT_HEIGHT 160 -//#define TFT_HEIGHT 128 - // ################################################################################## // // Section 1. Define the pins that are used to interface with the display here @@ -47,13 +50,13 @@ // ################################################################################## // We must use hardware SPI, a minimum of 3 GPIO pins is needed. -// Typical setup for NodeMCU ESP-12 is : +// Typical setup for ESP8266 NodeMCU ESP-12 is : // // Display SDO/MISO to NodeMCU pin D6 (or leave disconnected if not reading TFT) // Display LED to NodeMCU pin VIN (or 5V, see below) // Display SCK to NodeMCU pin D5 // Display SDI/MOSI to NodeMCU pin D7 -// Display DC (or AO)to NodeMCU pin D3 +// Display DC (RS/AO)to NodeMCU pin D3 // Display RESET to NodeMCU pin D4 (or RST, see below) // Display CS to NodeMCU pin D8 (or GND, see below) // Display GND to NodeMCU pin GND (0V) @@ -61,8 +64,10 @@ // // The TFT RESET pin can be connected to the NodeMCU RST pin or 3.3V to free up a control pin // +// The DC (Data Command) pin may be labeled AO or RS (Register Select) +// // With some displays such as the ILI9341 the TFT CS pin can be connected to GND if no more -// SPI deivces (e.g. an SD Card) are connected, in this case comment out the #define TFT_CS +// SPI devices (e.g. an SD Card) are connected, in this case comment out the #define TFT_CS // line below so it is NOT defined. Other displays such at the ST7735 require the TFT CS pin // to be toggled during setup, so in these cases the TFT_CS line must be defined and connected. // @@ -74,7 +79,8 @@ // If 5V is not available at a pin you can use 3.3V but backlight brightness // will be lower. -// ###### EDIT THE PIN NUMBERS IN THE LINES FOLLOWING TO SUIT YOUR SETUP ###### + +// ###### EDIT THE PIN NUMBERS IN THE LINES FOLLOWING TO SUIT YOUR ESP8266 SETUP ###### // For NodeMCU - use pin numbers in the form PIN_Dx where Dx is the NodeMCU pin designation //#define TFT_CS PIN_D8 // Chip select control pin D8 @@ -82,24 +88,52 @@ //#define TFT_RST PIN_D4 // Reset pin (could connect to NodeMCU RST, see next line) //#define TFT_RST -1 // Set TFT_RST to -1 if the display RESET is connected to NodeMCU RST or 3.3V -//#define TOUCH_CS PIN_D1 // Chip select pin (T_CS) of touch screen, also define SPI frequency in Section 5 below +//#define TOUCH_CS PIN_D1 // Chip select pin (T_CS) of touch screen -//#define TFT_WR PIN_D2 // Write strobe for modified Raspberry Pi TFT only +//#define TFT_WR PIN_D2 // Write strobe for modified Raspberry Pi TFT only + + +// ###### FOR ESP8266 OVERLAP MODE EDIT THE PIN NUMBERS IN THE FOLLOWING LINES ###### + +// In ESP8266 overlap mode the TFT chip select MUST connect to pin D3 +//#define TFT_CS PIN_D3 +//#define TFT_DC PIN_D5 // Data Command control pin +//#define TFT_RST PIN_D4 // Reset pin (could connect to NodeMCU RST, see next line) +//#define TFT_RST -1 // Set TFT_RST to -1 if the display RESET is connected to NodeMCU RST or 3.3V + +// In ESP8266 overlap mode the following must be defined +//#define TFT_SPI_OVERLAP + +// ###### EDIT THE PIN NUMBERS IN THE LINES FOLLOWING TO SUIT YOUR ESP32 SETUP ###### + +// For ESP32 Dev board (only tested with ILI9341 display) +// The hardware SPI can be mapped to any pins -// ESP32 Dev board #define TFT_MISO 19 #define TFT_MOSI 23 #define TFT_SCLK 18 #define TFT_CS 15 // Chip select control pin -#define TFT_DC 2 // Data Command control pin -#define TFT_RST 4 // Reset pin (could connect to Arduino RESET pin) +#define TFT_DC 2 // Data Command control pin +#define TFT_RST 4 // Reset pin (could connect to RST pin) //#define TFT_RST -1 // Set TFT_RST to -1 if display RESET is connected to ESP32 board RST -#define TOUCH_CS 22 // Chip select pin (T_CS) of touch screen, also define SPI frequency in Section 5 below +#define TOUCH_CS 22 // Chip select pin (T_CS) of touch screen + +//#define TFT_WR 21 // Write strobe for modified Raspberry Pi TFT only + +// For the M5Stack module use these #define lines +//#define TFT_MISO 19 +//#define TFT_MOSI 23 +//#define TFT_SCLK 18 +//#define TFT_CS 14 // Chip select control pin +//#define TFT_DC 27 // Data Command control pin +//#define TFT_RST 33 // Reset pin (could connect to Arduino RESET pin) +//#define TFT_BL 32 // LED back-light + // ################################################################################## // -// Section 2. Define the way the DC and/or CS lines are driven +// Section 2. Define the way the DC and/or CS lines are driven (ESP8266 only) // // ################################################################################## @@ -119,9 +153,9 @@ // ################################################################################## // Comment out the #defines below with // to stop that font being loaded -// The ESP8366 had plenty of memory so commenting out fonts is not normally necessary -// If all fonts are loaded the extra FLASH space required is about 17Kbytes... -// To save FLASH space only enable the fonts you need! +// The ESP8366 and ESP32 have plenty of memory so commenting out fonts is not +// normally necessary. If all fonts are loaded the extra FLASH space required is +// about 17Kbytes. To save FLASH space only enable the fonts you need! #define LOAD_GLCD // Font 1. Original Adafruit 8 pixel font needs ~1820 bytes in FLASH #define LOAD_FONT2 // Font 2. Small 16 pixel high font, needs ~3534 bytes in FLASH, 96 characters @@ -144,26 +178,33 @@ // // ################################################################################## -// Define the SPI clock frequency +// Define the SPI clock frequency, this affects the graphics rendering speed. Too +// fast and the TFT driver will not keep up and display corruption appears. // With an ILI9341 display 40MHz works OK, 80MHz sometimes fails // With a ST7735 display more than 27MHz may not work (spurious pixels and lines) -// With an ILI9163 display TBD MHz works OK, +// With an ILI9163 display 27 MHz works OK. +// The RPi typically only works at 20MHz maximum. // #define SPI_FREQUENCY 1000000 // #define SPI_FREQUENCY 5000000 // #define SPI_FREQUENCY 10000000 - #define SPI_FREQUENCY 20000000 +#define SPI_FREQUENCY 20000000 // #define SPI_FREQUENCY 27000000 // Actually sets it to 26.67MHz = 80/3 // #define SPI_FREQUENCY 40000000 // Maximum to use SPIFFS // #define SPI_FREQUENCY 80000000 +// The XPT2046 requires a lower SPI clock rate of 2.5MHz so we define that here: #define SPI_TOUCH_FREQUENCY 2500000 // Comment out the following #define if "SPI Transactions" do not need to be -// supported. Tranaction support is required if other SPI devices are connected. -// When commented out the code size will be smaller and sketches will +// supported. When commented out the code size will be smaller and sketches will // run slightly faster, so leave it commented out unless you need it! + // Transaction support is needed to work with SD library but not needed with TFT_SdFat +// Transaction support is required if other SPI devices are connected. + +// Transactions are automatically enabled by the library for an ESP32 (to use HAL mutex) +// so changing it here has no effect // #define SUPPORT_TRANSACTIONS diff --git a/User_Setups/Setup12_M5Stack.h b/User_Setups/Setup12_M5Stack.h index f307497..767edf3 100644 --- a/User_Setups/Setup12_M5Stack.h +++ b/User_Setups/Setup12_M5Stack.h @@ -1,9 +1,9 @@ // USER DEFINED SETTINGS -// -// The User_Setup header that will be called up is defined in User_Setup_Select.h -// // Set driver type, fonts to be loaded, pins used and SPI control method etc -// +// +// See the User_Setup_Select.h file if you wish to be able to define multiple +// setups and then easily select which setup file is used by the compiler. +// // If this file is edited correctly then all the library example sketches should // run without the need to make any more changes for a particular hardware setup! @@ -13,17 +13,115 @@ // // ################################################################################## +// Only define one driver, the other ones must be commented out #define ILI9341_DRIVER +//#define ST7735_DRIVER +//#define ILI9163_DRIVER +//#define S6D02A1_DRIVER +//#define RPI_ILI9486_DRIVER // 20MHz maximum SPI +// For M5Stack ESP32 module with integrated display ONLY, remove // in line below #define M5STACK +// For ST7735 and ILI9163 ONLY, define the pixel width and height in portrait orientation +//#define TFT_WIDTH 128 +//#define TFT_HEIGHT 160 +//#define TFT_HEIGHT 128 + +// For ST7735 ONLY, define the type of display, originally this was based on the +// colour of the tab on the screen protector film but this is not always true, so try +// out the different options below if the screen does not display graphics correctly, +// e.g. colours wrong, mirror images, or tray pixels at the edges. +// Comment out ALL BUT ONE of these options for a ST7735 display driver, save this +// this User_Setup file, then rebuild and upload the sketch to the board again: + +//#define ST7735_INITB +//#define ST7735_GREENTAB +//#define ST7735_GREENTAB2 +//#define ST7735_GREENTAB3 +//#define ST7735_GREENTAB128 // For 128 x 128 display +//#define ST7735_REDTAB +//#define ST7735_BLACKTAB + // ################################################################################## // // Section 1. Define the pins that are used to interface with the display here // // ################################################################################## -// M5Stack +// We must use hardware SPI, a minimum of 3 GPIO pins is needed. +// Typical setup for ESP8266 NodeMCU ESP-12 is : +// +// Display SDO/MISO to NodeMCU pin D6 (or leave disconnected if not reading TFT) +// Display LED to NodeMCU pin VIN (or 5V, see below) +// Display SCK to NodeMCU pin D5 +// Display SDI/MOSI to NodeMCU pin D7 +// Display DC (RS/AO)to NodeMCU pin D3 +// Display RESET to NodeMCU pin D4 (or RST, see below) +// Display CS to NodeMCU pin D8 (or GND, see below) +// Display GND to NodeMCU pin GND (0V) +// Display VCC to NodeMCU 5V or 3.3V +// +// The TFT RESET pin can be connected to the NodeMCU RST pin or 3.3V to free up a control pin +// +// The DC (Data Command) pin may be labeled AO or RS (Register Select) +// +// With some displays such as the ILI9341 the TFT CS pin can be connected to GND if no more +// SPI devices (e.g. an SD Card) are connected, in this case comment out the #define TFT_CS +// line below so it is NOT defined. Other displays such at the ST7735 require the TFT CS pin +// to be toggled during setup, so in these cases the TFT_CS line must be defined and connected. +// +// The NodeMCU D0 pin can be used for RST +// +// See Section 2. below if DC or CS is connected to D0 +// +// Note: only some versions of the NodeMCU provide the USB 5V on the VIN pin +// If 5V is not available at a pin you can use 3.3V but backlight brightness +// will be lower. + + +// ###### EDIT THE PIN NUMBERS IN THE LINES FOLLOWING TO SUIT YOUR ESP8266 SETUP ###### + +// For NodeMCU - use pin numbers in the form PIN_Dx where Dx is the NodeMCU pin designation +//#define TFT_CS PIN_D8 // Chip select control pin D8 +//#define TFT_DC PIN_D3 // Data Command control pin +//#define TFT_RST PIN_D4 // Reset pin (could connect to NodeMCU RST, see next line) +//#define TFT_RST -1 // Set TFT_RST to -1 if the display RESET is connected to NodeMCU RST or 3.3V + +//#define TOUCH_CS PIN_D1 // Chip select pin (T_CS) of touch screen + +//#define TFT_WR PIN_D2 // Write strobe for modified Raspberry Pi TFT only + + +// ###### FOR ESP8266 OVERLAP MODE EDIT THE PIN NUMBERS IN THE FOLLOWING LINES ###### + +// In ESP8266 overlap mode the TFT chip select MUST connect to pin D3 +//#define TFT_CS PIN_D3 +//#define TFT_DC PIN_D5 // Data Command control pin +//#define TFT_RST PIN_D4 // Reset pin (could connect to NodeMCU RST, see next line) +//#define TFT_RST -1 // Set TFT_RST to -1 if the display RESET is connected to NodeMCU RST or 3.3V + +// In ESP8266 overlap mode the following must be defined +//#define TFT_SPI_OVERLAP + +// ###### EDIT THE PIN NUMBERS IN THE LINES FOLLOWING TO SUIT YOUR ESP32 SETUP ###### + +// For ESP32 Dev board (only tested with ILI9341 display) +// The hardware SPI can be mapped to any pins + +//#define TFT_MISO 19 +//#define TFT_MOSI 23 +//#define TFT_SCLK 18 +//#define TFT_CS 15 // Chip select control pin +//#define TFT_DC 2 // Data Command control pin +//#define TFT_RST 4 // Reset pin (could connect to RST pin) +//#define TFT_RST -1 // Set TFT_RST to -1 if display RESET is connected to ESP32 board RST + +//#define TOUCH_CS 21 // Chip select pin (T_CS) of touch screen + +//#define TFT_WR 22 // Write strobe for modified Raspberry Pi TFT only + +// For the M5Stack module use these #define lines #define TFT_MISO 19 #define TFT_MOSI 23 #define TFT_SCLK 18 @@ -34,11 +132,18 @@ // ################################################################################## // -// Section 2. Define the way the DC and/or CS lines are driven +// Section 2. Define the way the DC and/or CS lines are driven (ESP8266 only) // // ################################################################################## -// Not used +// Normally the library uses direct register access for the DC and CS lines for speed +// If D0 (GPIO16) is used for CS or DC then a different slower method must be used +// Uncomment one line if D0 is used for DC or CS +// DC on D0 = 6% performance penalty at 40MHz SPI running graphics test +// CS on D0 = 2% performance penalty at 40MHz SPI running graphics test + +// #define D0_USED_FOR_DC +// #define D0_USED_FOR_CS // ################################################################################## // @@ -47,9 +152,9 @@ // ################################################################################## // Comment out the #defines below with // to stop that font being loaded -// The ESP8366 had plenty of memory so commenting out fonts is not normally necessary -// If all fonts are loaded the extra FLASH space required is about 17Kbytes... -// To save FLASH space only enable the fonts you need! +// The ESP8366 and ESP32 have plenty of memory so commenting out fonts is not +// normally necessary. If all fonts are loaded the extra FLASH space required is +// about 17Kbytes. To save FLASH space only enable the fonts you need! #define LOAD_GLCD // Font 1. Original Adafruit 8 pixel font needs ~1820 bytes in FLASH #define LOAD_FONT2 // Font 2. Small 16 pixel high font, needs ~3534 bytes in FLASH, 96 characters @@ -72,14 +177,33 @@ // // ################################################################################## -// Define the SPI clock frequency +// Define the SPI clock frequency, this affects the graphics rendering speed. Too +// fast and the TFT driver will not keep up and display corruption appears. // With an ILI9341 display 40MHz works OK, 80MHz sometimes fails // With a ST7735 display more than 27MHz may not work (spurious pixels and lines) +// With an ILI9163 display 27 MHz works OK. +// The RPi typically only works at 20MHz maximum. // #define SPI_FREQUENCY 1000000 // #define SPI_FREQUENCY 5000000 // #define SPI_FREQUENCY 10000000 // #define SPI_FREQUENCY 20000000 -// #define SPI_FREQUENCY 27000000 // Actually sets it to 26.67MHz = 80/3 - #define SPI_FREQUENCY 40000000 // Maximum to use SPIFFS +#define SPI_FREQUENCY 27000000 // Actually sets it to 26.67MHz = 80/3 +// #define SPI_FREQUENCY 40000000 // Maximum to use SPIFFS // #define SPI_FREQUENCY 80000000 + +// The XPT2046 requires a lower SPI clock rate of 2.5MHz so we define that here: +#define SPI_TOUCH_FREQUENCY 2500000 + + +// Comment out the following #define if "SPI Transactions" do not need to be +// supported. When commented out the code size will be smaller and sketches will +// run slightly faster, so leave it commented out unless you need it! + +// Transaction support is needed to work with SD library but not needed with TFT_SdFat +// Transaction support is required if other SPI devices are connected. + +// Transactions are automatically enabled by the library for an ESP32 (to use HAL mutex) +// so changing it here has no effect + +// #define SUPPORT_TRANSACTIONS diff --git a/User_Setups/Setup1_ILI9341.h b/User_Setups/Setup1_ILI9341.h index 82b0469..ad08dd7 100644 --- a/User_Setups/Setup1_ILI9341.h +++ b/User_Setups/Setup1_ILI9341.h @@ -1,10 +1,10 @@ // USER DEFINED SETTINGS -// -// The User_Setup header that will be called up is defined in User_Setup_Select.h -// // Set driver type, fonts to be loaded, pins used and SPI control method etc -// -// If this file is editted correctly then all the library example sketches should +// +// See the User_Setup_Select.h file if you wish to be able to define multiple +// setups and then easily select which setup file is used by the compiler. +// +// If this file is edited correctly then all the library example sketches should // run without the need to make any more changes for a particular hardware setup! // ################################################################################## @@ -16,8 +16,14 @@ // Only define one driver, the other ones must be commented out #define ILI9341_DRIVER //#define ST7735_DRIVER +//#define ILI9163_DRIVER +//#define S6D02A1_DRIVER +//#define RPI_ILI9486_DRIVER // 20MHz maximum SPI -// For ST7735 and ILI9163 ONLY, define the pixel width and height in portrait orientation +// For M5Stack ESP32 module with integrated display ONLY, remove // in line below +//#define M5STACK + +// For ST7735 and ILI9163 ONLY, define the pixel width and height in portrait orientation //#define TFT_WIDTH 128 //#define TFT_HEIGHT 160 //#define TFT_HEIGHT 128 @@ -32,6 +38,8 @@ //#define ST7735_INITB //#define ST7735_GREENTAB //#define ST7735_GREENTAB2 +//#define ST7735_GREENTAB3 +//#define ST7735_GREENTAB128 // For 128 x 128 display //#define ST7735_REDTAB //#define ST7735_BLACKTAB @@ -42,13 +50,13 @@ // ################################################################################## // We must use hardware SPI, a minimum of 3 GPIO pins is needed. -// Typical setup for NodeMCU ESP-12 is : +// Typical setup for ESP8266 NodeMCU ESP-12 is : // // Display SDO/MISO to NodeMCU pin D6 (or leave disconnected if not reading TFT) // Display LED to NodeMCU pin VIN (or 5V, see below) // Display SCK to NodeMCU pin D5 // Display SDI/MOSI to NodeMCU pin D7 -// Display DC (or AO)to NodeMCU pin D3 +// Display DC (RS/AO)to NodeMCU pin D3 // Display RESET to NodeMCU pin D4 (or RST, see below) // Display CS to NodeMCU pin D8 (or GND, see below) // Display GND to NodeMCU pin GND (0V) @@ -56,8 +64,10 @@ // // The TFT RESET pin can be connected to the NodeMCU RST pin or 3.3V to free up a control pin // +// The DC (Data Command) pin may be labeled AO or RS (Register Select) +// // With some displays such as the ILI9341 the TFT CS pin can be connected to GND if no more -// SPI deivces (e.g. an SD Card) are connected, in this case comment out the #define TFT_CS +// SPI devices (e.g. an SD Card) are connected, in this case comment out the #define TFT_CS // line below so it is NOT defined. Other displays such at the ST7735 require the TFT CS pin // to be toggled during setup, so in these cases the TFT_CS line must be defined and connected. // @@ -69,23 +79,60 @@ // If 5V is not available at a pin you can use 3.3V but backlight brightness // will be lower. -// ###### EDIT THE PIN NUMBERS IN THE LINES FOLLOWING TO SUIT YOUR SETUP ###### -// ModeMCU +// ###### EDIT THE PIN NUMBERS IN THE LINES FOLLOWING TO SUIT YOUR ESP8266 SETUP ###### + +// For NodeMCU - use pin numbers in the form PIN_Dx where Dx is the NodeMCU pin designation #define TFT_CS PIN_D8 // Chip select control pin D8 #define TFT_DC PIN_D3 // Data Command control pin #define TFT_RST PIN_D4 // Reset pin (could connect to NodeMCU RST, see next line) //#define TFT_RST -1 // Set TFT_RST to -1 if the display RESET is connected to NodeMCU RST or 3.3V -// ESP32 Dev board (planned, not supported yet) -//#define TFT_CS 5 // Chip select control pin -//#define TFT_DC 2 // Data Command control pin -//#define TFT_RST 4 // Reset pin (could connect to Arduino RESET pin) +//#define TOUCH_CS PIN_D1 // Chip select pin (T_CS) of touch screen + +//#define TFT_WR PIN_D2 // Write strobe for modified Raspberry Pi TFT only + + +// ###### FOR ESP8266 OVERLAP MODE EDIT THE PIN NUMBERS IN THE FOLLOWING LINES ###### + +// In ESP8266 overlap mode the TFT chip select MUST connect to pin D3 +//#define TFT_CS PIN_D3 +//#define TFT_DC PIN_D5 // Data Command control pin +//#define TFT_RST PIN_D4 // Reset pin (could connect to NodeMCU RST, see next line) +//#define TFT_RST -1 // Set TFT_RST to -1 if the display RESET is connected to NodeMCU RST or 3.3V + +// In ESP8266 overlap mode the following must be defined +//#define TFT_SPI_OVERLAP + +// ###### EDIT THE PIN NUMBERS IN THE LINES FOLLOWING TO SUIT YOUR ESP32 SETUP ###### + +// For ESP32 Dev board (only tested with ILI9341 display) +// The hardware SPI can be mapped to any pins + +//#define TFT_MISO 19 +//#define TFT_MOSI 23 +//#define TFT_SCLK 18 +//#define TFT_CS 15 // Chip select control pin +//#define TFT_DC 2 // Data Command control pin +//#define TFT_RST 4 // Reset pin (could connect to RST pin) //#define TFT_RST -1 // Set TFT_RST to -1 if display RESET is connected to ESP32 board RST +//#define TOUCH_CS 21 // Chip select pin (T_CS) of touch screen + +//#define TFT_WR 22 // Write strobe for modified Raspberry Pi TFT only + +// For the M5Stack module use these #define lines +//#define TFT_MISO 19 +//#define TFT_MOSI 23 +//#define TFT_SCLK 18 +//#define TFT_CS 14 // Chip select control pin +//#define TFT_DC 27 // Data Command control pin +//#define TFT_RST 33 // Reset pin (could connect to Arduino RESET pin) +//#define TFT_BL 32 // LED back-light + // ################################################################################## // -// Section 2. Define the way the DC and/or CS lines are driven +// Section 2. Define the way the DC and/or CS lines are driven (ESP8266 only) // // ################################################################################## @@ -105,9 +152,9 @@ // ################################################################################## // Comment out the #defines below with // to stop that font being loaded -// The ESP8366 had plenty of memory so commenting out fonts is not normally necessary -// If all fonts are loaded the extra FLASH space required is about 17Kbytes... -// To save FLASH space only enable the fonts you need! +// The ESP8366 and ESP32 have plenty of memory so commenting out fonts is not +// normally necessary. If all fonts are loaded the extra FLASH space required is +// about 17Kbytes. To save FLASH space only enable the fonts you need! #define LOAD_GLCD // Font 1. Original Adafruit 8 pixel font needs ~1820 bytes in FLASH #define LOAD_FONT2 // Font 2. Small 16 pixel high font, needs ~3534 bytes in FLASH, 96 characters @@ -130,23 +177,33 @@ // // ################################################################################## -// Define the SPI clock frequency +// Define the SPI clock frequency, this affects the graphics rendering speed. Too +// fast and the TFT driver will not keep up and display corruption appears. // With an ILI9341 display 40MHz works OK, 80MHz sometimes fails // With a ST7735 display more than 27MHz may not work (spurious pixels and lines) +// With an ILI9163 display 27 MHz works OK. +// The RPi typically only works at 20MHz maximum. // #define SPI_FREQUENCY 1000000 // #define SPI_FREQUENCY 5000000 // #define SPI_FREQUENCY 10000000 // #define SPI_FREQUENCY 20000000 -// #define SPI_FREQUENCY 27000000 // Actually sets it to 26.67MHz = 80/3 - #define SPI_FREQUENCY 40000000 // Maximum to use SPIFFS +#define SPI_FREQUENCY 27000000 // Actually sets it to 26.67MHz = 80/3 +// #define SPI_FREQUENCY 40000000 // Maximum to use SPIFFS // #define SPI_FREQUENCY 80000000 +// The XPT2046 requires a lower SPI clock rate of 2.5MHz so we define that here: +#define SPI_TOUCH_FREQUENCY 2500000 + // Comment out the following #define if "SPI Transactions" do not need to be -// supported. Tranaction support is required if other SPI devices are connected. -// When commented out the code size will be smaller and sketches will +// supported. When commented out the code size will be smaller and sketches will // run slightly faster, so leave it commented out unless you need it! + // Transaction support is needed to work with SD library but not needed with TFT_SdFat +// Transaction support is required if other SPI devices are connected. + +// Transactions are automatically enabled by the library for an ESP32 (to use HAL mutex) +// so changing it here has no effect // #define SUPPORT_TRANSACTIONS diff --git a/User_Setups/Setup2_ST7735.h b/User_Setups/Setup2_ST7735.h index 8f1c1c2..d7c7fdc 100644 --- a/User_Setups/Setup2_ST7735.h +++ b/User_Setups/Setup2_ST7735.h @@ -1,10 +1,10 @@ // USER DEFINED SETTINGS -// -// The User_Setup header that will be called up is defined in User_Setup_Select.h -// // Set driver type, fonts to be loaded, pins used and SPI control method etc -// -// If this file is editted correctly then all the library example sketches should +// +// See the User_Setup_Select.h file if you wish to be able to define multiple +// setups and then easily select which setup file is used by the compiler. +// +// If this file is edited correctly then all the library example sketches should // run without the need to make any more changes for a particular hardware setup! // ################################################################################## @@ -16,8 +16,14 @@ // Only define one driver, the other ones must be commented out //#define ILI9341_DRIVER #define ST7735_DRIVER +//#define ILI9163_DRIVER +//#define S6D02A1_DRIVER +//#define RPI_ILI9486_DRIVER // 20MHz maximum SPI -// For ST7735 and ILI9163 ONLY, define the pixel width and height in portrait orientation +// For M5Stack ESP32 module with integrated display ONLY, remove // in line below +//#define M5STACK + +// For ST7735 and ILI9163 ONLY, define the pixel width and height in portrait orientation #define TFT_WIDTH 128 #define TFT_HEIGHT 160 //#define TFT_HEIGHT 128 @@ -33,6 +39,7 @@ //#define ST7735_GREENTAB //#define ST7735_GREENTAB2 //#define ST7735_GREENTAB3 +//#define ST7735_GREENTAB128 // For 128 x 128 display #define ST7735_REDTAB //#define ST7735_BLACKTAB @@ -43,13 +50,13 @@ // ################################################################################## // We must use hardware SPI, a minimum of 3 GPIO pins is needed. -// Typical setup for NodeMCU ESP-12 is : +// Typical setup for ESP8266 NodeMCU ESP-12 is : // // Display SDO/MISO to NodeMCU pin D6 (or leave disconnected if not reading TFT) // Display LED to NodeMCU pin VIN (or 5V, see below) // Display SCK to NodeMCU pin D5 // Display SDI/MOSI to NodeMCU pin D7 -// Display DC (or AO)to NodeMCU pin D3 +// Display DC (RS/AO)to NodeMCU pin D3 // Display RESET to NodeMCU pin D4 (or RST, see below) // Display CS to NodeMCU pin D8 (or GND, see below) // Display GND to NodeMCU pin GND (0V) @@ -57,8 +64,10 @@ // // The TFT RESET pin can be connected to the NodeMCU RST pin or 3.3V to free up a control pin // +// The DC (Data Command) pin may be labeled AO or RS (Register Select) +// // With some displays such as the ILI9341 the TFT CS pin can be connected to GND if no more -// SPI deivces (e.g. an SD Card) are connected, in this case comment out the #define TFT_CS +// SPI devices (e.g. an SD Card) are connected, in this case comment out the #define TFT_CS // line below so it is NOT defined. Other displays such at the ST7735 require the TFT CS pin // to be toggled during setup, so in these cases the TFT_CS line must be defined and connected. // @@ -70,23 +79,60 @@ // If 5V is not available at a pin you can use 3.3V but backlight brightness // will be lower. -// ###### EDIT THE PIN NUMBERS IN THE LINES FOLLOWING TO SUIT YOUR SETUP ###### -// ModeMCU +// ###### EDIT THE PIN NUMBERS IN THE LINES FOLLOWING TO SUIT YOUR ESP8266 SETUP ###### + +// For NodeMCU - use pin numbers in the form PIN_Dx where Dx is the NodeMCU pin designation #define TFT_CS PIN_D8 // Chip select control pin D8 #define TFT_DC PIN_D3 // Data Command control pin #define TFT_RST PIN_D4 // Reset pin (could connect to NodeMCU RST, see next line) //#define TFT_RST -1 // Set TFT_RST to -1 if the display RESET is connected to NodeMCU RST or 3.3V -// ESP32 Dev board (planned, not test/supported yet) -//#define TFT_CS 5 // Chip select control pin -//#define TFT_DC 2 // Data Command control pin -//#define TFT_RST 4 // Reset pin (could connect to Arduino RESET pin) +//#define TOUCH_CS PIN_D1 // Chip select pin (T_CS) of touch screen + +//#define TFT_WR PIN_D2 // Write strobe for modified Raspberry Pi TFT only + + +// ###### FOR ESP8266 OVERLAP MODE EDIT THE PIN NUMBERS IN THE FOLLOWING LINES ###### + +// In ESP8266 overlap mode the TFT chip select MUST connect to pin D3 +//#define TFT_CS PIN_D3 +//#define TFT_DC PIN_D5 // Data Command control pin +//#define TFT_RST PIN_D4 // Reset pin (could connect to NodeMCU RST, see next line) +//#define TFT_RST -1 // Set TFT_RST to -1 if the display RESET is connected to NodeMCU RST or 3.3V + +// In ESP8266 overlap mode the following must be defined +//#define TFT_SPI_OVERLAP + +// ###### EDIT THE PIN NUMBERS IN THE LINES FOLLOWING TO SUIT YOUR ESP32 SETUP ###### + +// For ESP32 Dev board (only tested with ILI9341 display) +// The hardware SPI can be mapped to any pins + +//#define TFT_MISO 19 +//#define TFT_MOSI 23 +//#define TFT_SCLK 18 +//#define TFT_CS 15 // Chip select control pin +//#define TFT_DC 2 // Data Command control pin +//#define TFT_RST 4 // Reset pin (could connect to RST pin) //#define TFT_RST -1 // Set TFT_RST to -1 if display RESET is connected to ESP32 board RST +//#define TOUCH_CS 21 // Chip select pin (T_CS) of touch screen + +//#define TFT_WR 22 // Write strobe for modified Raspberry Pi TFT only + +// For the M5Stack module use these #define lines +//#define TFT_MISO 19 +//#define TFT_MOSI 23 +//#define TFT_SCLK 18 +//#define TFT_CS 14 // Chip select control pin +//#define TFT_DC 27 // Data Command control pin +//#define TFT_RST 33 // Reset pin (could connect to Arduino RESET pin) +//#define TFT_BL 32 // LED back-light + // ################################################################################## // -// Section 2. Define the way the DC and/or CS lines are driven +// Section 2. Define the way the DC and/or CS lines are driven (ESP8266 only) // // ################################################################################## @@ -106,9 +152,9 @@ // ################################################################################## // Comment out the #defines below with // to stop that font being loaded -// The ESP8366 had plenty of memory so commenting out fonts is not normally necessary -// If all fonts are loaded the extra FLASH space required is about 17Kbytes... -// To save FLASH space only enable the fonts you need! +// The ESP8366 and ESP32 have plenty of memory so commenting out fonts is not +// normally necessary. If all fonts are loaded the extra FLASH space required is +// about 17Kbytes. To save FLASH space only enable the fonts you need! #define LOAD_GLCD // Font 1. Original Adafruit 8 pixel font needs ~1820 bytes in FLASH #define LOAD_FONT2 // Font 2. Small 16 pixel high font, needs ~3534 bytes in FLASH, 96 characters @@ -131,23 +177,33 @@ // // ################################################################################## -// Define the SPI clock frequency +// Define the SPI clock frequency, this affects the graphics rendering speed. Too +// fast and the TFT driver will not keep up and display corruption appears. // With an ILI9341 display 40MHz works OK, 80MHz sometimes fails // With a ST7735 display more than 27MHz may not work (spurious pixels and lines) +// With an ILI9163 display 27 MHz works OK. +// The RPi typically only works at 20MHz maximum. // #define SPI_FREQUENCY 1000000 // #define SPI_FREQUENCY 5000000 // #define SPI_FREQUENCY 10000000 // #define SPI_FREQUENCY 20000000 - #define SPI_FREQUENCY 27000000 // Maximum for my ST7735. It is actually 26.67MHz = 80/3 +#define SPI_FREQUENCY 27000000 // Actually sets it to 26.67MHz = 80/3 // #define SPI_FREQUENCY 40000000 // Maximum to use SPIFFS // #define SPI_FREQUENCY 80000000 +// The XPT2046 requires a lower SPI clock rate of 2.5MHz so we define that here: +#define SPI_TOUCH_FREQUENCY 2500000 + // Comment out the following #define if "SPI Transactions" do not need to be -// supported. Tranaction support is required if other SPI devices are connected. -// When commented out the code size will be smaller and sketches will +// supported. When commented out the code size will be smaller and sketches will // run slightly faster, so leave it commented out unless you need it! + // Transaction support is needed to work with SD library but not needed with TFT_SdFat +// Transaction support is required if other SPI devices are connected. + +// Transactions are automatically enabled by the library for an ESP32 (to use HAL mutex) +// so changing it here has no effect // #define SUPPORT_TRANSACTIONS diff --git a/User_Setups/Setup3_ILI9163.h b/User_Setups/Setup3_ILI9163.h index 56cfccd..fc8062e 100644 --- a/User_Setups/Setup3_ILI9163.h +++ b/User_Setups/Setup3_ILI9163.h @@ -1,10 +1,10 @@ // USER DEFINED SETTINGS -// -// The User_Setup header that will be called up is defined in User_Setup_Select.h -// // Set driver type, fonts to be loaded, pins used and SPI control method etc -// -// If this file is editted correctly then all the library example sketches should +// +// See the User_Setup_Select.h file if you wish to be able to define multiple +// setups and then easily select which setup file is used by the compiler. +// +// If this file is edited correctly then all the library example sketches should // run without the need to make any more changes for a particular hardware setup! // ################################################################################## @@ -17,12 +17,32 @@ //#define ILI9341_DRIVER //#define ST7735_DRIVER #define ILI9163_DRIVER +//#define S6D02A1_DRIVER +//#define RPI_ILI9486_DRIVER // 20MHz maximum SPI -// For ST7735 and ILI9163 ONLY, define the pixel width and height in portrait orientation +// For M5Stack ESP32 module with integrated display ONLY, remove // in line below +//#define M5STACK + +// For ST7735 and ILI9163 ONLY, define the pixel width and height in portrait orientation #define TFT_WIDTH 128 #define TFT_HEIGHT 160 //#define TFT_HEIGHT 128 +// For ST7735 ONLY, define the type of display, originally this was based on the +// colour of the tab on the screen protector film but this is not always true, so try +// out the different options below if the screen does not display graphics correctly, +// e.g. colours wrong, mirror images, or tray pixels at the edges. +// Comment out ALL BUT ONE of these options for a ST7735 display driver, save this +// this User_Setup file, then rebuild and upload the sketch to the board again: + +//#define ST7735_INITB +//#define ST7735_GREENTAB +//#define ST7735_GREENTAB2 +//#define ST7735_GREENTAB3 +//#define ST7735_GREENTAB128 // For 128 x 128 display +//#define ST7735_REDTAB +//#define ST7735_BLACKTAB + // ################################################################################## // // Section 1. Define the pins that are used to interface with the display here @@ -30,13 +50,13 @@ // ################################################################################## // We must use hardware SPI, a minimum of 3 GPIO pins is needed. -// Typical setup for NodeMCU ESP-12 is : +// Typical setup for ESP8266 NodeMCU ESP-12 is : // // Display SDO/MISO to NodeMCU pin D6 (or leave disconnected if not reading TFT) // Display LED to NodeMCU pin VIN (or 5V, see below) // Display SCK to NodeMCU pin D5 // Display SDI/MOSI to NodeMCU pin D7 -// Display DC (or AO)to NodeMCU pin D3 +// Display DC (RS/AO)to NodeMCU pin D3 // Display RESET to NodeMCU pin D4 (or RST, see below) // Display CS to NodeMCU pin D8 (or GND, see below) // Display GND to NodeMCU pin GND (0V) @@ -44,8 +64,10 @@ // // The TFT RESET pin can be connected to the NodeMCU RST pin or 3.3V to free up a control pin // +// The DC (Data Command) pin may be labeled AO or RS (Register Select) +// // With some displays such as the ILI9341 the TFT CS pin can be connected to GND if no more -// SPI deivces (e.g. an SD Card) are connected, in this case comment out the #define TFT_CS +// SPI devices (e.g. an SD Card) are connected, in this case comment out the #define TFT_CS // line below so it is NOT defined. Other displays such at the ST7735 require the TFT CS pin // to be toggled during setup, so in these cases the TFT_CS line must be defined and connected. // @@ -57,23 +79,60 @@ // If 5V is not available at a pin you can use 3.3V but backlight brightness // will be lower. -// ###### EDIT THE PIN NUMBERS IN THE LINES FOLLOWING TO SUIT YOUR SETUP ###### -// ModeMCU +// ###### EDIT THE PIN NUMBERS IN THE LINES FOLLOWING TO SUIT YOUR ESP8266 SETUP ###### + +// For NodeMCU - use pin numbers in the form PIN_Dx where Dx is the NodeMCU pin designation #define TFT_CS PIN_D8 // Chip select control pin D8 #define TFT_DC PIN_D3 // Data Command control pin #define TFT_RST PIN_D4 // Reset pin (could connect to NodeMCU RST, see next line) //#define TFT_RST -1 // Set TFT_RST to -1 if the display RESET is connected to NodeMCU RST or 3.3V -// ESP32 Dev board (planned, not supported yet) -//#define TFT_CS 5 // Chip select control pin -//#define TFT_DC 2 // Data Command control pin -//#define TFT_RST 4 // Reset pin (could connect to Arduino RESET pin) +//#define TOUCH_CS PIN_D1 // Chip select pin (T_CS) of touch screen + +//#define TFT_WR PIN_D2 // Write strobe for modified Raspberry Pi TFT only + + +// ###### FOR ESP8266 OVERLAP MODE EDIT THE PIN NUMBERS IN THE FOLLOWING LINES ###### + +// In ESP8266 overlap mode the TFT chip select MUST connect to pin D3 +//#define TFT_CS PIN_D3 +//#define TFT_DC PIN_D5 // Data Command control pin +//#define TFT_RST PIN_D4 // Reset pin (could connect to NodeMCU RST, see next line) +//#define TFT_RST -1 // Set TFT_RST to -1 if the display RESET is connected to NodeMCU RST or 3.3V + +// In ESP8266 overlap mode the following must be defined +//#define TFT_SPI_OVERLAP + +// ###### EDIT THE PIN NUMBERS IN THE LINES FOLLOWING TO SUIT YOUR ESP32 SETUP ###### + +// For ESP32 Dev board (only tested with ILI9341 display) +// The hardware SPI can be mapped to any pins + +//#define TFT_MISO 19 +//#define TFT_MOSI 23 +//#define TFT_SCLK 18 +//#define TFT_CS 15 // Chip select control pin +//#define TFT_DC 2 // Data Command control pin +//#define TFT_RST 4 // Reset pin (could connect to RST pin) //#define TFT_RST -1 // Set TFT_RST to -1 if display RESET is connected to ESP32 board RST +//#define TOUCH_CS 21 // Chip select pin (T_CS) of touch screen + +//#define TFT_WR 22 // Write strobe for modified Raspberry Pi TFT only + +// For the M5Stack module use these #define lines +//#define TFT_MISO 19 +//#define TFT_MOSI 23 +//#define TFT_SCLK 18 +//#define TFT_CS 14 // Chip select control pin +//#define TFT_DC 27 // Data Command control pin +//#define TFT_RST 33 // Reset pin (could connect to Arduino RESET pin) +//#define TFT_BL 32 // LED back-light + // ################################################################################## // -// Section 2. Define the way the DC and/or CS lines are driven +// Section 2. Define the way the DC and/or CS lines are driven (ESP8266 only) // // ################################################################################## @@ -93,9 +152,9 @@ // ################################################################################## // Comment out the #defines below with // to stop that font being loaded -// The ESP8366 had plenty of memory so commenting out fonts is not normally necessary -// If all fonts are loaded the extra FLASH space required is about 17Kbytes... -// To save FLASH space only enable the fonts you need! +// The ESP8366 and ESP32 have plenty of memory so commenting out fonts is not +// normally necessary. If all fonts are loaded the extra FLASH space required is +// about 17Kbytes. To save FLASH space only enable the fonts you need! #define LOAD_GLCD // Font 1. Original Adafruit 8 pixel font needs ~1820 bytes in FLASH #define LOAD_FONT2 // Font 2. Small 16 pixel high font, needs ~3534 bytes in FLASH, 96 characters @@ -118,24 +177,33 @@ // // ################################################################################## -// Define the SPI clock frequency +// Define the SPI clock frequency, this affects the graphics rendering speed. Too +// fast and the TFT driver will not keep up and display corruption appears. // With an ILI9341 display 40MHz works OK, 80MHz sometimes fails // With a ST7735 display more than 27MHz may not work (spurious pixels and lines) -// With an ILI9163 display TBD MHz works OK, +// With an ILI9163 display 27 MHz works OK. +// The RPi typically only works at 20MHz maximum. // #define SPI_FREQUENCY 1000000 // #define SPI_FREQUENCY 5000000 // #define SPI_FREQUENCY 10000000 // #define SPI_FREQUENCY 20000000 - #define SPI_FREQUENCY 27000000 // Actually sets it to 26.67MHz = 80/3 +#define SPI_FREQUENCY 27000000 // Actually sets it to 26.67MHz = 80/3 // #define SPI_FREQUENCY 40000000 // Maximum to use SPIFFS // #define SPI_FREQUENCY 80000000 +// The XPT2046 requires a lower SPI clock rate of 2.5MHz so we define that here: +#define SPI_TOUCH_FREQUENCY 2500000 + // Comment out the following #define if "SPI Transactions" do not need to be -// supported. Tranaction support is required if other SPI devices are connected. -// When commented out the code size will be smaller and sketches will +// supported. When commented out the code size will be smaller and sketches will // run slightly faster, so leave it commented out unless you need it! + // Transaction support is needed to work with SD library but not needed with TFT_SdFat +// Transaction support is required if other SPI devices are connected. + +// Transactions are automatically enabled by the library for an ESP32 (to use HAL mutex) +// so changing it here has no effect // #define SUPPORT_TRANSACTIONS diff --git a/User_Setups/Setup4_S6D02A1.h b/User_Setups/Setup4_S6D02A1.h index 72a6fe4..3136d5d 100644 --- a/User_Setups/Setup4_S6D02A1.h +++ b/User_Setups/Setup4_S6D02A1.h @@ -1,10 +1,10 @@ // USER DEFINED SETTINGS -// -// The User_Setup header that will be called up is defined in User_Setup_Select.h -// // Set driver type, fonts to be loaded, pins used and SPI control method etc -// -// If this file is editted correctly then all the library example sketches should +// +// See the User_Setup_Select.h file if you wish to be able to define multiple +// setups and then easily select which setup file is used by the compiler. +// +// If this file is edited correctly then all the library example sketches should // run without the need to make any more changes for a particular hardware setup! // ################################################################################## @@ -18,6 +18,30 @@ //#define ST7735_DRIVER //#define ILI9163_DRIVER #define S6D02A1_DRIVER +//#define RPI_ILI9486_DRIVER // 20MHz maximum SPI + +// For M5Stack ESP32 module with integrated display ONLY, remove // in line below +//#define M5STACK + +// For ST7735 and ILI9163 ONLY, define the pixel width and height in portrait orientation +//#define TFT_WIDTH 128 +//#define TFT_HEIGHT 160 +//#define TFT_HEIGHT 128 + +// For ST7735 ONLY, define the type of display, originally this was based on the +// colour of the tab on the screen protector film but this is not always true, so try +// out the different options below if the screen does not display graphics correctly, +// e.g. colours wrong, mirror images, or tray pixels at the edges. +// Comment out ALL BUT ONE of these options for a ST7735 display driver, save this +// this User_Setup file, then rebuild and upload the sketch to the board again: + +//#define ST7735_INITB +//#define ST7735_GREENTAB +//#define ST7735_GREENTAB2 +//#define ST7735_GREENTAB3 +//#define ST7735_GREENTAB128 // For 128 x 128 display +//#define ST7735_REDTAB +//#define ST7735_BLACKTAB // ################################################################################## // @@ -26,13 +50,13 @@ // ################################################################################## // We must use hardware SPI, a minimum of 3 GPIO pins is needed. -// Typical setup for NodeMCU ESP-12 is : +// Typical setup for ESP8266 NodeMCU ESP-12 is : // // Display SDO/MISO to NodeMCU pin D6 (or leave disconnected if not reading TFT) // Display LED to NodeMCU pin VIN (or 5V, see below) // Display SCK to NodeMCU pin D5 // Display SDI/MOSI to NodeMCU pin D7 -// Display DC (or AO)to NodeMCU pin D3 +// Display DC (RS/AO)to NodeMCU pin D3 // Display RESET to NodeMCU pin D4 (or RST, see below) // Display CS to NodeMCU pin D8 (or GND, see below) // Display GND to NodeMCU pin GND (0V) @@ -40,8 +64,10 @@ // // The TFT RESET pin can be connected to the NodeMCU RST pin or 3.3V to free up a control pin // +// The DC (Data Command) pin may be labeled AO or RS (Register Select) +// // With some displays such as the ILI9341 the TFT CS pin can be connected to GND if no more -// SPI deivces (e.g. an SD Card) are connected, in this case comment out the #define TFT_CS +// SPI devices (e.g. an SD Card) are connected, in this case comment out the #define TFT_CS // line below so it is NOT defined. Other displays such at the ST7735 require the TFT CS pin // to be toggled during setup, so in these cases the TFT_CS line must be defined and connected. // @@ -53,23 +79,60 @@ // If 5V is not available at a pin you can use 3.3V but backlight brightness // will be lower. -// ###### EDIT THE PIN NUMBERS IN THE LINES FOLLOWING TO SUIT YOUR SETUP ###### -// ModeMCU +// ###### EDIT THE PIN NUMBERS IN THE LINES FOLLOWING TO SUIT YOUR ESP8266 SETUP ###### + +// For NodeMCU - use pin numbers in the form PIN_Dx where Dx is the NodeMCU pin designation #define TFT_CS PIN_D8 // Chip select control pin D8 #define TFT_DC PIN_D3 // Data Command control pin #define TFT_RST PIN_D4 // Reset pin (could connect to NodeMCU RST, see next line) //#define TFT_RST -1 // Set TFT_RST to -1 if the display RESET is connected to NodeMCU RST or 3.3V -// ESP32 Dev board (planned, not supported yet) -//#define TFT_CS 5 // Chip select control pin -//#define TFT_DC 2 // Data Command control pin -//#define TFT_RST 4 // Reset pin (could connect to Arduino RESET pin) +//#define TOUCH_CS PIN_D1 // Chip select pin (T_CS) of touch screen + +//#define TFT_WR PIN_D2 // Write strobe for modified Raspberry Pi TFT only + + +// ###### FOR ESP8266 OVERLAP MODE EDIT THE PIN NUMBERS IN THE FOLLOWING LINES ###### + +// In ESP8266 overlap mode the TFT chip select MUST connect to pin D3 +//#define TFT_CS PIN_D3 +//#define TFT_DC PIN_D5 // Data Command control pin +//#define TFT_RST PIN_D4 // Reset pin (could connect to NodeMCU RST, see next line) +//#define TFT_RST -1 // Set TFT_RST to -1 if the display RESET is connected to NodeMCU RST or 3.3V + +// In ESP8266 overlap mode the following must be defined +//#define TFT_SPI_OVERLAP + +// ###### EDIT THE PIN NUMBERS IN THE LINES FOLLOWING TO SUIT YOUR ESP32 SETUP ###### + +// For ESP32 Dev board (only tested with ILI9341 display) +// The hardware SPI can be mapped to any pins + +//#define TFT_MISO 19 +//#define TFT_MOSI 23 +//#define TFT_SCLK 18 +//#define TFT_CS 15 // Chip select control pin +//#define TFT_DC 2 // Data Command control pin +//#define TFT_RST 4 // Reset pin (could connect to RST pin) //#define TFT_RST -1 // Set TFT_RST to -1 if display RESET is connected to ESP32 board RST +//#define TOUCH_CS 21 // Chip select pin (T_CS) of touch screen + +//#define TFT_WR 22 // Write strobe for modified Raspberry Pi TFT only + +// For the M5Stack module use these #define lines +//#define TFT_MISO 19 +//#define TFT_MOSI 23 +//#define TFT_SCLK 18 +//#define TFT_CS 14 // Chip select control pin +//#define TFT_DC 27 // Data Command control pin +//#define TFT_RST 33 // Reset pin (could connect to Arduino RESET pin) +//#define TFT_BL 32 // LED back-light + // ################################################################################## // -// Section 2. Define the way the DC and/or CS lines are driven +// Section 2. Define the way the DC and/or CS lines are driven (ESP8266 only) // // ################################################################################## @@ -89,9 +152,9 @@ // ################################################################################## // Comment out the #defines below with // to stop that font being loaded -// The ESP8366 had plenty of memory so commenting out fonts is not normally necessary -// If all fonts are loaded the extra FLASH space required is about 17Kbytes... -// To save FLASH space only enable the fonts you need! +// The ESP8366 and ESP32 have plenty of memory so commenting out fonts is not +// normally necessary. If all fonts are loaded the extra FLASH space required is +// about 17Kbytes. To save FLASH space only enable the fonts you need! #define LOAD_GLCD // Font 1. Original Adafruit 8 pixel font needs ~1820 bytes in FLASH #define LOAD_FONT2 // Font 2. Small 16 pixel high font, needs ~3534 bytes in FLASH, 96 characters @@ -114,24 +177,33 @@ // // ################################################################################## -// Define the SPI clock frequency +// Define the SPI clock frequency, this affects the graphics rendering speed. Too +// fast and the TFT driver will not keep up and display corruption appears. // With an ILI9341 display 40MHz works OK, 80MHz sometimes fails // With a ST7735 display more than 27MHz may not work (spurious pixels and lines) -// With an ILI9163 display TBD MHz works OK, +// With an ILI9163 display 27 MHz works OK. +// The RPi typically only works at 20MHz maximum. // #define SPI_FREQUENCY 1000000 // #define SPI_FREQUENCY 5000000 // #define SPI_FREQUENCY 10000000 // #define SPI_FREQUENCY 20000000 - #define SPI_FREQUENCY 27000000 // Actually sets it to 26.67MHz = 80/3 +#define SPI_FREQUENCY 27000000 // Actually sets it to 26.67MHz = 80/3 // #define SPI_FREQUENCY 40000000 // Maximum to use SPIFFS // #define SPI_FREQUENCY 80000000 +// The XPT2046 requires a lower SPI clock rate of 2.5MHz so we define that here: +#define SPI_TOUCH_FREQUENCY 2500000 + // Comment out the following #define if "SPI Transactions" do not need to be -// supported. Tranaction support is required if other SPI devices are connected. -// When commented out the code size will be smaller and sketches will +// supported. When commented out the code size will be smaller and sketches will // run slightly faster, so leave it commented out unless you need it! + // Transaction support is needed to work with SD library but not needed with TFT_SdFat +// Transaction support is required if other SPI devices are connected. + +// Transactions are automatically enabled by the library for an ESP32 (to use HAL mutex) +// so changing it here has no effect // #define SUPPORT_TRANSACTIONS diff --git a/User_Setups/Setup5_RPi_ILI9486.h b/User_Setups/Setup5_RPi_ILI9486.h index 5517041..38c12f6 100644 --- a/User_Setups/Setup5_RPi_ILI9486.h +++ b/User_Setups/Setup5_RPi_ILI9486.h @@ -4,7 +4,7 @@ // See the User_Setup_Select.h file if you wish to be able to define multiple // setups and then easily select which setup file is used by the compiler. // -// If this file is editted correctly then all the library example sketches should +// If this file is edited correctly then all the library example sketches should // run without the need to make any more changes for a particular hardware setup! // ################################################################################## @@ -20,6 +20,14 @@ //#define S6D02A1_DRIVER #define RPI_ILI9486_DRIVER // 20MHz maximum SPI +// For M5Stack ESP32 module with integrated display ONLY, remove // in line below +//#define M5STACK + +// For ST7735 and ILI9163 ONLY, define the pixel width and height in portrait orientation +//#define TFT_WIDTH 128 +//#define TFT_HEIGHT 160 +//#define TFT_HEIGHT 128 + // For ST7735 ONLY, define the type of display, originally this was based on the // colour of the tab on the screen protector film but this is not always true, so try // out the different options below if the screen does not display graphics correctly, @@ -31,14 +39,10 @@ //#define ST7735_GREENTAB //#define ST7735_GREENTAB2 //#define ST7735_GREENTAB3 +//#define ST7735_GREENTAB128 // For 128 x 128 display //#define ST7735_REDTAB //#define ST7735_BLACKTAB -// For ST7735 ONLY, define the pixel width and height in portrait orientation -//#define TFT_WIDTH 128 -//#define TFT_HEIGHT 160 -//#define TFT_HEIGHT 128 - // ################################################################################## // // Section 1. Define the pins that are used to interface with the display here @@ -46,13 +50,13 @@ // ################################################################################## // We must use hardware SPI, a minimum of 3 GPIO pins is needed. -// Typical setup for NodeMCU ESP-12 is : +// Typical setup for ESP8266 NodeMCU ESP-12 is : // // Display SDO/MISO to NodeMCU pin D6 (or leave disconnected if not reading TFT) // Display LED to NodeMCU pin VIN (or 5V, see below) // Display SCK to NodeMCU pin D5 // Display SDI/MOSI to NodeMCU pin D7 -// Display DC (or AO)to NodeMCU pin D3 +// Display DC (RS/AO)to NodeMCU pin D3 // Display RESET to NodeMCU pin D4 (or RST, see below) // Display CS to NodeMCU pin D8 (or GND, see below) // Display GND to NodeMCU pin GND (0V) @@ -60,8 +64,10 @@ // // The TFT RESET pin can be connected to the NodeMCU RST pin or 3.3V to free up a control pin // +// The DC (Data Command) pin may be labeled AO or RS (Register Select) +// // With some displays such as the ILI9341 the TFT CS pin can be connected to GND if no more -// SPI deivces (e.g. an SD Card) are connected, in this case comment out the #define TFT_CS +// SPI devices (e.g. an SD Card) are connected, in this case comment out the #define TFT_CS // line below so it is NOT defined. Other displays such at the ST7735 require the TFT CS pin // to be toggled during setup, so in these cases the TFT_CS line must be defined and connected. // @@ -73,25 +79,60 @@ // If 5V is not available at a pin you can use 3.3V but backlight brightness // will be lower. -// ###### EDIT THE PIN NUMBERS IN THE LINES FOLLOWING TO SUIT YOUR SETUP ###### -// ModeMCU +// ###### EDIT THE PIN NUMBERS IN THE LINES FOLLOWING TO SUIT YOUR ESP8266 SETUP ###### + +// For NodeMCU - use pin numbers in the form PIN_Dx where Dx is the NodeMCU pin designation #define TFT_CS PIN_D8 // Chip select control pin D8 #define TFT_DC PIN_D3 // Data Command control pin #define TFT_RST PIN_D4 // Reset pin (could connect to NodeMCU RST, see next line) //#define TFT_RST -1 // Set TFT_RST to -1 if the display RESET is connected to NodeMCU RST or 3.3V -//#define TFT_WR PIN_D2 // Write strobe for modified Raspberry Pi TFT only +//#define TOUCH_CS PIN_D1 // Chip select pin (T_CS) of touch screen -// ESP32 Dev board (planned, not supported yet) -//#define TFT_CS 5 // Chip select control pin -//#define TFT_DC 2 // Data Command control pin -//#define TFT_RST 4 // Reset pin (could connect to Arduino RESET pin) +//#define TFT_WR PIN_D2 // Write strobe for modified Raspberry Pi TFT only + + +// ###### FOR ESP8266 OVERLAP MODE EDIT THE PIN NUMBERS IN THE FOLLOWING LINES ###### + +// In ESP8266 overlap mode the TFT chip select MUST connect to pin D3 +//#define TFT_CS PIN_D3 +//#define TFT_DC PIN_D5 // Data Command control pin +//#define TFT_RST PIN_D4 // Reset pin (could connect to NodeMCU RST, see next line) +//#define TFT_RST -1 // Set TFT_RST to -1 if the display RESET is connected to NodeMCU RST or 3.3V + +// In ESP8266 overlap mode the following must be defined +//#define TFT_SPI_OVERLAP + +// ###### EDIT THE PIN NUMBERS IN THE LINES FOLLOWING TO SUIT YOUR ESP32 SETUP ###### + +// For ESP32 Dev board (only tested with ILI9341 display) +// The hardware SPI can be mapped to any pins + +//#define TFT_MISO 19 +//#define TFT_MOSI 23 +//#define TFT_SCLK 18 +//#define TFT_CS 15 // Chip select control pin +//#define TFT_DC 2 // Data Command control pin +//#define TFT_RST 4 // Reset pin (could connect to RST pin) //#define TFT_RST -1 // Set TFT_RST to -1 if display RESET is connected to ESP32 board RST +//#define TOUCH_CS 21 // Chip select pin (T_CS) of touch screen + +//#define TFT_WR 22 // Write strobe for modified Raspberry Pi TFT only + +// For the M5Stack module use these #define lines +//#define TFT_MISO 19 +//#define TFT_MOSI 23 +//#define TFT_SCLK 18 +//#define TFT_CS 14 // Chip select control pin +//#define TFT_DC 27 // Data Command control pin +//#define TFT_RST 33 // Reset pin (could connect to Arduino RESET pin) +//#define TFT_BL 32 // LED back-light + // ################################################################################## // -// Section 2. Define the way the DC and/or CS lines are driven +// Section 2. Define the way the DC and/or CS lines are driven (ESP8266 only) // // ################################################################################## @@ -111,9 +152,9 @@ // ################################################################################## // Comment out the #defines below with // to stop that font being loaded -// The ESP8366 had plenty of memory so commenting out fonts is not normally necessary -// If all fonts are loaded the extra FLASH space required is about 17Kbytes... -// To save FLASH space only enable the fonts you need! +// The ESP8366 and ESP32 have plenty of memory so commenting out fonts is not +// normally necessary. If all fonts are loaded the extra FLASH space required is +// about 17Kbytes. To save FLASH space only enable the fonts you need! #define LOAD_GLCD // Font 1. Original Adafruit 8 pixel font needs ~1820 bytes in FLASH #define LOAD_FONT2 // Font 2. Small 16 pixel high font, needs ~3534 bytes in FLASH, 96 characters @@ -136,24 +177,33 @@ // // ################################################################################## -// Define the SPI clock frequency +// Define the SPI clock frequency, this affects the graphics rendering speed. Too +// fast and the TFT driver will not keep up and display corruption appears. // With an ILI9341 display 40MHz works OK, 80MHz sometimes fails // With a ST7735 display more than 27MHz may not work (spurious pixels and lines) -// With an ILI9163 display TBD MHz works OK, +// With an ILI9163 display 27 MHz works OK. +// The RPi typically only works at 20MHz maximum. // #define SPI_FREQUENCY 1000000 // #define SPI_FREQUENCY 5000000 // #define SPI_FREQUENCY 10000000 - #define SPI_FREQUENCY 20000000 +#define SPI_FREQUENCY 20000000 // #define SPI_FREQUENCY 27000000 // Actually sets it to 26.67MHz = 80/3 // #define SPI_FREQUENCY 40000000 // Maximum to use SPIFFS // #define SPI_FREQUENCY 80000000 +// The XPT2046 requires a lower SPI clock rate of 2.5MHz so we define that here: +#define SPI_TOUCH_FREQUENCY 2500000 + // Comment out the following #define if "SPI Transactions" do not need to be -// supported. Tranaction support is required if other SPI devices are connected. -// When commented out the code size will be smaller and sketches will +// supported. When commented out the code size will be smaller and sketches will // run slightly faster, so leave it commented out unless you need it! + // Transaction support is needed to work with SD library but not needed with TFT_SdFat +// Transaction support is required if other SPI devices are connected. + +// Transactions are automatically enabled by the library for an ESP32 (to use HAL mutex) +// so changing it here has no effect // #define SUPPORT_TRANSACTIONS diff --git a/User_Setups/Setup6_RPi_Wr_ILI9486.h b/User_Setups/Setup6_RPi_Wr_ILI9486.h index 2f1552a..06777cd 100644 --- a/User_Setups/Setup6_RPi_Wr_ILI9486.h +++ b/User_Setups/Setup6_RPi_Wr_ILI9486.h @@ -4,7 +4,7 @@ // See the User_Setup_Select.h file if you wish to be able to define multiple // setups and then easily select which setup file is used by the compiler. // -// If this file is editted correctly then all the library example sketches should +// If this file is edited correctly then all the library example sketches should // run without the need to make any more changes for a particular hardware setup! // ################################################################################## @@ -20,7 +20,10 @@ //#define S6D02A1_DRIVER #define RPI_ILI9486_DRIVER // 20MHz maximum SPI -// For ST7735 and ILI9163 ONLY, define the pixel width and height in portrait orientation +// For M5Stack ESP32 module with integrated display ONLY, remove // in line below +//#define M5STACK + +// For ST7735 and ILI9163 ONLY, define the pixel width and height in portrait orientation //#define TFT_WIDTH 128 //#define TFT_HEIGHT 160 //#define TFT_HEIGHT 128 @@ -36,6 +39,7 @@ //#define ST7735_GREENTAB //#define ST7735_GREENTAB2 //#define ST7735_GREENTAB3 +//#define ST7735_GREENTAB128 // For 128 x 128 display //#define ST7735_REDTAB //#define ST7735_BLACKTAB @@ -46,13 +50,13 @@ // ################################################################################## // We must use hardware SPI, a minimum of 3 GPIO pins is needed. -// Typical setup for NodeMCU ESP-12 is : +// Typical setup for ESP8266 NodeMCU ESP-12 is : // // Display SDO/MISO to NodeMCU pin D6 (or leave disconnected if not reading TFT) // Display LED to NodeMCU pin VIN (or 5V, see below) // Display SCK to NodeMCU pin D5 // Display SDI/MOSI to NodeMCU pin D7 -// Display DC (or AO)to NodeMCU pin D3 +// Display DC (RS/AO)to NodeMCU pin D3 // Display RESET to NodeMCU pin D4 (or RST, see below) // Display CS to NodeMCU pin D8 (or GND, see below) // Display GND to NodeMCU pin GND (0V) @@ -60,8 +64,10 @@ // // The TFT RESET pin can be connected to the NodeMCU RST pin or 3.3V to free up a control pin // +// The DC (Data Command) pin may be labeled AO or RS (Register Select) +// // With some displays such as the ILI9341 the TFT CS pin can be connected to GND if no more -// SPI deivces (e.g. an SD Card) are connected, in this case comment out the #define TFT_CS +// SPI devices (e.g. an SD Card) are connected, in this case comment out the #define TFT_CS // line below so it is NOT defined. Other displays such at the ST7735 require the TFT CS pin // to be toggled during setup, so in these cases the TFT_CS line must be defined and connected. // @@ -73,25 +79,60 @@ // If 5V is not available at a pin you can use 3.3V but backlight brightness // will be lower. -// ###### EDIT THE PIN NUMBERS IN THE LINES FOLLOWING TO SUIT YOUR SETUP ###### -// ModeMCU +// ###### EDIT THE PIN NUMBERS IN THE LINES FOLLOWING TO SUIT YOUR ESP8266 SETUP ###### + +// For NodeMCU - use pin numbers in the form PIN_Dx where Dx is the NodeMCU pin designation #define TFT_CS PIN_D8 // Chip select control pin D8 #define TFT_DC PIN_D3 // Data Command control pin #define TFT_RST PIN_D4 // Reset pin (could connect to NodeMCU RST, see next line) //#define TFT_RST -1 // Set TFT_RST to -1 if the display RESET is connected to NodeMCU RST or 3.3V -#define TFT_WR PIN_D2 // Write strobe for modified Raspberry Pi TFT only +//#define TOUCH_CS PIN_D1 // Chip select pin (T_CS) of touch screen -// ESP32 Dev board (planned, not supported yet) -//#define TFT_CS 5 // Chip select control pin -//#define TFT_DC 2 // Data Command control pin -//#define TFT_RST 4 // Reset pin (could connect to Arduino RESET pin) +#define TFT_WR PIN_D2 // Write strobe for modified Raspberry Pi TFT only + + +// ###### FOR ESP8266 OVERLAP MODE EDIT THE PIN NUMBERS IN THE FOLLOWING LINES ###### + +// In ESP8266 overlap mode the TFT chip select MUST connect to pin D3 +//#define TFT_CS PIN_D3 +//#define TFT_DC PIN_D5 // Data Command control pin +//#define TFT_RST PIN_D4 // Reset pin (could connect to NodeMCU RST, see next line) +//#define TFT_RST -1 // Set TFT_RST to -1 if the display RESET is connected to NodeMCU RST or 3.3V + +// In ESP8266 overlap mode the following must be defined +//#define TFT_SPI_OVERLAP + +// ###### EDIT THE PIN NUMBERS IN THE LINES FOLLOWING TO SUIT YOUR ESP32 SETUP ###### + +// For ESP32 Dev board (only tested with ILI9341 display) +// The hardware SPI can be mapped to any pins + +//#define TFT_MISO 19 +//#define TFT_MOSI 23 +//#define TFT_SCLK 18 +//#define TFT_CS 15 // Chip select control pin +//#define TFT_DC 2 // Data Command control pin +//#define TFT_RST 4 // Reset pin (could connect to RST pin) //#define TFT_RST -1 // Set TFT_RST to -1 if display RESET is connected to ESP32 board RST +//#define TOUCH_CS 21 // Chip select pin (T_CS) of touch screen + +//#define TFT_WR 22 // Write strobe for modified Raspberry Pi TFT only + +// For the M5Stack module use these #define lines +//#define TFT_MISO 19 +//#define TFT_MOSI 23 +//#define TFT_SCLK 18 +//#define TFT_CS 14 // Chip select control pin +//#define TFT_DC 27 // Data Command control pin +//#define TFT_RST 33 // Reset pin (could connect to Arduino RESET pin) +//#define TFT_BL 32 // LED back-light + // ################################################################################## // -// Section 2. Define the way the DC and/or CS lines are driven +// Section 2. Define the way the DC and/or CS lines are driven (ESP8266 only) // // ################################################################################## @@ -111,9 +152,9 @@ // ################################################################################## // Comment out the #defines below with // to stop that font being loaded -// The ESP8366 had plenty of memory so commenting out fonts is not normally necessary -// If all fonts are loaded the extra FLASH space required is about 17Kbytes... -// To save FLASH space only enable the fonts you need! +// The ESP8366 and ESP32 have plenty of memory so commenting out fonts is not +// normally necessary. If all fonts are loaded the extra FLASH space required is +// about 17Kbytes. To save FLASH space only enable the fonts you need! #define LOAD_GLCD // Font 1. Original Adafruit 8 pixel font needs ~1820 bytes in FLASH #define LOAD_FONT2 // Font 2. Small 16 pixel high font, needs ~3534 bytes in FLASH, 96 characters @@ -136,24 +177,33 @@ // // ################################################################################## -// Define the SPI clock frequency +// Define the SPI clock frequency, this affects the graphics rendering speed. Too +// fast and the TFT driver will not keep up and display corruption appears. // With an ILI9341 display 40MHz works OK, 80MHz sometimes fails // With a ST7735 display more than 27MHz may not work (spurious pixels and lines) -// With an ILI9163 display TBD MHz works OK, +// With an ILI9163 display 27 MHz works OK. +// The RPi typically only works at 20MHz maximum. // #define SPI_FREQUENCY 1000000 // #define SPI_FREQUENCY 5000000 // #define SPI_FREQUENCY 10000000 - #define SPI_FREQUENCY 20000000 +#define SPI_FREQUENCY 20000000 // #define SPI_FREQUENCY 27000000 // Actually sets it to 26.67MHz = 80/3 // #define SPI_FREQUENCY 40000000 // Maximum to use SPIFFS // #define SPI_FREQUENCY 80000000 +// The XPT2046 requires a lower SPI clock rate of 2.5MHz so we define that here: +#define SPI_TOUCH_FREQUENCY 2500000 + // Comment out the following #define if "SPI Transactions" do not need to be -// supported. Tranaction support is required if other SPI devices are connected. -// When commented out the code size will be smaller and sketches will +// supported. When commented out the code size will be smaller and sketches will // run slightly faster, so leave it commented out unless you need it! + // Transaction support is needed to work with SD library but not needed with TFT_SdFat +// Transaction support is required if other SPI devices are connected. + +// Transactions are automatically enabled by the library for an ESP32 (to use HAL mutex) +// so changing it here has no effect // #define SUPPORT_TRANSACTIONS diff --git a/User_Setups/Setup7_ST7735_128x128.h b/User_Setups/Setup7_ST7735_128x128.h index 6d9140e..7f4f267 100644 --- a/User_Setups/Setup7_ST7735_128x128.h +++ b/User_Setups/Setup7_ST7735_128x128.h @@ -1,10 +1,10 @@ // USER DEFINED SETTINGS -// -// The User_Setup header that will be called up is defined in User_Setup_Select.h -// // Set driver type, fonts to be loaded, pins used and SPI control method etc -// -// If this file is editted correctly then all the library example sketches should +// +// See the User_Setup_Select.h file if you wish to be able to define multiple +// setups and then easily select which setup file is used by the compiler. +// +// If this file is edited correctly then all the library example sketches should // run without the need to make any more changes for a particular hardware setup! // ################################################################################## @@ -16,8 +16,14 @@ // Only define one driver, the other ones must be commented out //#define ILI9341_DRIVER #define ST7735_DRIVER +//#define ILI9163_DRIVER +//#define S6D02A1_DRIVER +//#define RPI_ILI9486_DRIVER // 20MHz maximum SPI -// For ST7735 and ILI9163 ONLY, define the pixel width and height in portrait orientation +// For M5Stack ESP32 module with integrated display ONLY, remove // in line below +//#define M5STACK + +// For ST7735 and ILI9163 ONLY, define the pixel width and height in portrait orientation #define TFT_WIDTH 128 //#define TFT_HEIGHT 160 #define TFT_HEIGHT 128 @@ -29,12 +35,12 @@ // Comment out ALL BUT ONE of these options for a ST7735 display driver, save this // this User_Setup file, then rebuild and upload the sketch to the board again: -//#define ST7735_INITB // No display -//#define ST7735_GREENTAB // 2 pixel left border -//#define ST7735_GREENTAB2 // Colours wrong RB swap -//#define ST7735_GREENTAB3 // 2 pixel left border +//#define ST7735_INITB +//#define ST7735_GREENTAB +//#define ST7735_GREENTAB2 +//#define ST7735_GREENTAB3 #define ST7735_GREENTAB128 // For 128 x 128 display -//#define ST7735_REDTAB // colours wrong rotation 0 needs y shift of 32, 1 an x shift of 32 +//#define ST7735_REDTAB //#define ST7735_BLACKTAB // ################################################################################## @@ -44,7 +50,7 @@ // ################################################################################## // We must use hardware SPI, a minimum of 3 GPIO pins is needed. -// Typical setup for NodeMCU ESP-12 is : +// Typical setup for ESP8266 NodeMCU ESP-12 is : // // Display SDO/MISO to NodeMCU pin D6 (or leave disconnected if not reading TFT) // Display LED to NodeMCU pin VIN (or 5V, see below) @@ -58,10 +64,10 @@ // // The TFT RESET pin can be connected to the NodeMCU RST pin or 3.3V to free up a control pin // -// The DC (Data Command) pin may be labell AO or RS (Register Select) +// The DC (Data Command) pin may be labeled AO or RS (Register Select) // // With some displays such as the ILI9341 the TFT CS pin can be connected to GND if no more -// SPI deivces (e.g. an SD Card) are connected, in this case comment out the #define TFT_CS +// SPI devices (e.g. an SD Card) are connected, in this case comment out the #define TFT_CS // line below so it is NOT defined. Other displays such at the ST7735 require the TFT CS pin // to be toggled during setup, so in these cases the TFT_CS line must be defined and connected. // @@ -73,23 +79,60 @@ // If 5V is not available at a pin you can use 3.3V but backlight brightness // will be lower. -// ###### EDIT THE PIN NUMBERS IN THE LINES FOLLOWING TO SUIT YOUR SETUP ###### -// ModeMCU +// ###### EDIT THE PIN NUMBERS IN THE LINES FOLLOWING TO SUIT YOUR ESP8266 SETUP ###### + +// For NodeMCU - use pin numbers in the form PIN_Dx where Dx is the NodeMCU pin designation #define TFT_CS PIN_D8 // Chip select control pin D8 #define TFT_DC PIN_D3 // Data Command control pin #define TFT_RST PIN_D4 // Reset pin (could connect to NodeMCU RST, see next line) //#define TFT_RST -1 // Set TFT_RST to -1 if the display RESET is connected to NodeMCU RST or 3.3V -// ESP32 Dev board (planned, not test/supported yet) -//#define TFT_CS 5 // Chip select control pin -//#define TFT_DC 2 // Data Command control pin -//#define TFT_RST 4 // Reset pin (could connect to Arduino RESET pin) +//#define TOUCH_CS PIN_D1 // Chip select pin (T_CS) of touch screen + +//#define TFT_WR PIN_D2 // Write strobe for modified Raspberry Pi TFT only + + +// ###### FOR ESP8266 OVERLAP MODE EDIT THE PIN NUMBERS IN THE FOLLOWING LINES ###### + +// In ESP8266 overlap mode the TFT chip select MUST connect to pin D3 +//#define TFT_CS PIN_D3 +//#define TFT_DC PIN_D5 // Data Command control pin +//#define TFT_RST PIN_D4 // Reset pin (could connect to NodeMCU RST, see next line) +//#define TFT_RST -1 // Set TFT_RST to -1 if the display RESET is connected to NodeMCU RST or 3.3V + +// In ESP8266 overlap mode the following must be defined +//#define TFT_SPI_OVERLAP + +// ###### EDIT THE PIN NUMBERS IN THE LINES FOLLOWING TO SUIT YOUR ESP32 SETUP ###### + +// For ESP32 Dev board (only tested with ILI9341 display) +// The hardware SPI can be mapped to any pins + +//#define TFT_MISO 19 +//#define TFT_MOSI 23 +//#define TFT_SCLK 18 +//#define TFT_CS 15 // Chip select control pin +//#define TFT_DC 2 // Data Command control pin +//#define TFT_RST 4 // Reset pin (could connect to RST pin) //#define TFT_RST -1 // Set TFT_RST to -1 if display RESET is connected to ESP32 board RST +//#define TOUCH_CS 21 // Chip select pin (T_CS) of touch screen + +//#define TFT_WR 22 // Write strobe for modified Raspberry Pi TFT only + +// For the M5Stack module use these #define lines +//#define TFT_MISO 19 +//#define TFT_MOSI 23 +//#define TFT_SCLK 18 +//#define TFT_CS 14 // Chip select control pin +//#define TFT_DC 27 // Data Command control pin +//#define TFT_RST 33 // Reset pin (could connect to Arduino RESET pin) +//#define TFT_BL 32 // LED back-light + // ################################################################################## // -// Section 2. Define the way the DC and/or CS lines are driven +// Section 2. Define the way the DC and/or CS lines are driven (ESP8266 only) // // ################################################################################## @@ -109,9 +152,9 @@ // ################################################################################## // Comment out the #defines below with // to stop that font being loaded -// The ESP8366 had plenty of memory so commenting out fonts is not normally necessary -// If all fonts are loaded the extra FLASH space required is about 17Kbytes... -// To save FLASH space only enable the fonts you need! +// The ESP8366 and ESP32 have plenty of memory so commenting out fonts is not +// normally necessary. If all fonts are loaded the extra FLASH space required is +// about 17Kbytes. To save FLASH space only enable the fonts you need! #define LOAD_GLCD // Font 1. Original Adafruit 8 pixel font needs ~1820 bytes in FLASH #define LOAD_FONT2 // Font 2. Small 16 pixel high font, needs ~3534 bytes in FLASH, 96 characters @@ -128,31 +171,39 @@ // ################################################################################## - - // ################################################################################## // // Section 5. Other options // // ################################################################################## -// Define the SPI clock frequency +// Define the SPI clock frequency, this affects the graphics rendering speed. Too +// fast and the TFT driver will not keep up and display corruption appears. // With an ILI9341 display 40MHz works OK, 80MHz sometimes fails // With a ST7735 display more than 27MHz may not work (spurious pixels and lines) +// With an ILI9163 display 27 MHz works OK. +// The RPi typically only works at 20MHz maximum. // #define SPI_FREQUENCY 1000000 // #define SPI_FREQUENCY 5000000 // #define SPI_FREQUENCY 10000000 // #define SPI_FREQUENCY 20000000 - #define SPI_FREQUENCY 27000000 // Maximum for my ST7735. It is actually 26.67MHz = 80/3 +#define SPI_FREQUENCY 27000000 // Actually sets it to 26.67MHz = 80/3 // #define SPI_FREQUENCY 40000000 // Maximum to use SPIFFS // #define SPI_FREQUENCY 80000000 +// The XPT2046 requires a lower SPI clock rate of 2.5MHz so we define that here: +#define SPI_TOUCH_FREQUENCY 2500000 + // Comment out the following #define if "SPI Transactions" do not need to be -// supported. Tranaction support is required if other SPI devices are connected. -// When commented out the code size will be smaller and sketches will +// supported. When commented out the code size will be smaller and sketches will // run slightly faster, so leave it commented out unless you need it! + // Transaction support is needed to work with SD library but not needed with TFT_SdFat +// Transaction support is required if other SPI devices are connected. + +// Transactions are automatically enabled by the library for an ESP32 (to use HAL mutex) +// so changing it here has no effect // #define SUPPORT_TRANSACTIONS diff --git a/User_Setups/Setup8_ILI9163_128x128.h b/User_Setups/Setup8_ILI9163_128x128.h index f5d6607..ec017fc 100644 --- a/User_Setups/Setup8_ILI9163_128x128.h +++ b/User_Setups/Setup8_ILI9163_128x128.h @@ -1,10 +1,10 @@ // USER DEFINED SETTINGS -// -// The User_Setup header that will be called up is defined in User_Setup_Select.h -// // Set driver type, fonts to be loaded, pins used and SPI control method etc -// -// If this file is editted correctly then all the library example sketches should +// +// See the User_Setup_Select.h file if you wish to be able to define multiple +// setups and then easily select which setup file is used by the compiler. +// +// If this file is edited correctly then all the library example sketches should // run without the need to make any more changes for a particular hardware setup! // ################################################################################## @@ -17,12 +17,32 @@ //#define ILI9341_DRIVER //#define ST7735_DRIVER #define ILI9163_DRIVER +//#define S6D02A1_DRIVER +//#define RPI_ILI9486_DRIVER // 20MHz maximum SPI -// For ST7735 and ILI9163 ONLY, define the pixel width and height in portrait orientation +// For M5Stack ESP32 module with integrated display ONLY, remove // in line below +//#define M5STACK + +// For ST7735 and ILI9163 ONLY, define the pixel width and height in portrait orientation #define TFT_WIDTH 128 //#define TFT_HEIGHT 160 #define TFT_HEIGHT 128 +// For ST7735 ONLY, define the type of display, originally this was based on the +// colour of the tab on the screen protector film but this is not always true, so try +// out the different options below if the screen does not display graphics correctly, +// e.g. colours wrong, mirror images, or tray pixels at the edges. +// Comment out ALL BUT ONE of these options for a ST7735 display driver, save this +// this User_Setup file, then rebuild and upload the sketch to the board again: + +//#define ST7735_INITB +//#define ST7735_GREENTAB +//#define ST7735_GREENTAB2 +//#define ST7735_GREENTAB3 +//#define ST7735_GREENTAB128 // For 128 x 128 display +//#define ST7735_REDTAB +//#define ST7735_BLACKTAB + // ################################################################################## // // Section 1. Define the pins that are used to interface with the display here @@ -30,13 +50,13 @@ // ################################################################################## // We must use hardware SPI, a minimum of 3 GPIO pins is needed. -// Typical setup for NodeMCU ESP-12 is : +// Typical setup for ESP8266 NodeMCU ESP-12 is : // // Display SDO/MISO to NodeMCU pin D6 (or leave disconnected if not reading TFT) // Display LED to NodeMCU pin VIN (or 5V, see below) // Display SCK to NodeMCU pin D5 // Display SDI/MOSI to NodeMCU pin D7 -// Display DC (or AO)to NodeMCU pin D3 +// Display DC (RS/AO)to NodeMCU pin D3 // Display RESET to NodeMCU pin D4 (or RST, see below) // Display CS to NodeMCU pin D8 (or GND, see below) // Display GND to NodeMCU pin GND (0V) @@ -44,8 +64,10 @@ // // The TFT RESET pin can be connected to the NodeMCU RST pin or 3.3V to free up a control pin // +// The DC (Data Command) pin may be labeled AO or RS (Register Select) +// // With some displays such as the ILI9341 the TFT CS pin can be connected to GND if no more -// SPI deivces (e.g. an SD Card) are connected, in this case comment out the #define TFT_CS +// SPI devices (e.g. an SD Card) are connected, in this case comment out the #define TFT_CS // line below so it is NOT defined. Other displays such at the ST7735 require the TFT CS pin // to be toggled during setup, so in these cases the TFT_CS line must be defined and connected. // @@ -57,23 +79,60 @@ // If 5V is not available at a pin you can use 3.3V but backlight brightness // will be lower. -// ###### EDIT THE PIN NUMBERS IN THE LINES FOLLOWING TO SUIT YOUR SETUP ###### -// ModeMCU +// ###### EDIT THE PIN NUMBERS IN THE LINES FOLLOWING TO SUIT YOUR ESP8266 SETUP ###### + +// For NodeMCU - use pin numbers in the form PIN_Dx where Dx is the NodeMCU pin designation #define TFT_CS PIN_D8 // Chip select control pin D8 #define TFT_DC PIN_D3 // Data Command control pin #define TFT_RST PIN_D4 // Reset pin (could connect to NodeMCU RST, see next line) //#define TFT_RST -1 // Set TFT_RST to -1 if the display RESET is connected to NodeMCU RST or 3.3V -// ESP32 Dev board (planned, not supported yet) -//#define TFT_CS 5 // Chip select control pin -//#define TFT_DC 2 // Data Command control pin -//#define TFT_RST 4 // Reset pin (could connect to Arduino RESET pin) +//#define TOUCH_CS PIN_D1 // Chip select pin (T_CS) of touch screen + +//#define TFT_WR PIN_D2 // Write strobe for modified Raspberry Pi TFT only + + +// ###### FOR ESP8266 OVERLAP MODE EDIT THE PIN NUMBERS IN THE FOLLOWING LINES ###### + +// In ESP8266 overlap mode the TFT chip select MUST connect to pin D3 +//#define TFT_CS PIN_D3 +//#define TFT_DC PIN_D5 // Data Command control pin +//#define TFT_RST PIN_D4 // Reset pin (could connect to NodeMCU RST, see next line) +//#define TFT_RST -1 // Set TFT_RST to -1 if the display RESET is connected to NodeMCU RST or 3.3V + +// In ESP8266 overlap mode the following must be defined +//#define TFT_SPI_OVERLAP + +// ###### EDIT THE PIN NUMBERS IN THE LINES FOLLOWING TO SUIT YOUR ESP32 SETUP ###### + +// For ESP32 Dev board (only tested with ILI9341 display) +// The hardware SPI can be mapped to any pins + +//#define TFT_MISO 19 +//#define TFT_MOSI 23 +//#define TFT_SCLK 18 +//#define TFT_CS 15 // Chip select control pin +//#define TFT_DC 2 // Data Command control pin +//#define TFT_RST 4 // Reset pin (could connect to RST pin) //#define TFT_RST -1 // Set TFT_RST to -1 if display RESET is connected to ESP32 board RST +//#define TOUCH_CS 21 // Chip select pin (T_CS) of touch screen + +//#define TFT_WR 22 // Write strobe for modified Raspberry Pi TFT only + +// For the M5Stack module use these #define lines +//#define TFT_MISO 19 +//#define TFT_MOSI 23 +//#define TFT_SCLK 18 +//#define TFT_CS 14 // Chip select control pin +//#define TFT_DC 27 // Data Command control pin +//#define TFT_RST 33 // Reset pin (could connect to Arduino RESET pin) +//#define TFT_BL 32 // LED back-light + // ################################################################################## // -// Section 2. Define the way the DC and/or CS lines are driven +// Section 2. Define the way the DC and/or CS lines are driven (ESP8266 only) // // ################################################################################## @@ -93,9 +152,9 @@ // ################################################################################## // Comment out the #defines below with // to stop that font being loaded -// The ESP8366 had plenty of memory so commenting out fonts is not normally necessary -// If all fonts are loaded the extra FLASH space required is about 17Kbytes... -// To save FLASH space only enable the fonts you need! +// The ESP8366 and ESP32 have plenty of memory so commenting out fonts is not +// normally necessary. If all fonts are loaded the extra FLASH space required is +// about 17Kbytes. To save FLASH space only enable the fonts you need! #define LOAD_GLCD // Font 1. Original Adafruit 8 pixel font needs ~1820 bytes in FLASH #define LOAD_FONT2 // Font 2. Small 16 pixel high font, needs ~3534 bytes in FLASH, 96 characters @@ -118,24 +177,33 @@ // // ################################################################################## -// Define the SPI clock frequency +// Define the SPI clock frequency, this affects the graphics rendering speed. Too +// fast and the TFT driver will not keep up and display corruption appears. // With an ILI9341 display 40MHz works OK, 80MHz sometimes fails // With a ST7735 display more than 27MHz may not work (spurious pixels and lines) -// With an ILI9163 display 40 MHz works OK, +// With an ILI9163 display 27 MHz works OK. +// The RPi typically only works at 20MHz maximum. // #define SPI_FREQUENCY 1000000 // #define SPI_FREQUENCY 5000000 // #define SPI_FREQUENCY 10000000 // #define SPI_FREQUENCY 20000000 - #define SPI_FREQUENCY 27000000 // Actually sets it to 26.67MHz = 80/3 +#define SPI_FREQUENCY 27000000 // Actually sets it to 26.67MHz = 80/3 // #define SPI_FREQUENCY 40000000 // Maximum to use SPIFFS // #define SPI_FREQUENCY 80000000 +// The XPT2046 requires a lower SPI clock rate of 2.5MHz so we define that here: +#define SPI_TOUCH_FREQUENCY 2500000 + // Comment out the following #define if "SPI Transactions" do not need to be -// supported. Tranaction support is required if other SPI devices are connected. -// When commented out the code size will be smaller and sketches will +// supported. When commented out the code size will be smaller and sketches will // run slightly faster, so leave it commented out unless you need it! + // Transaction support is needed to work with SD library but not needed with TFT_SdFat +// Transaction support is required if other SPI devices are connected. + +// Transactions are automatically enabled by the library for an ESP32 (to use HAL mutex) +// so changing it here has no effect // #define SUPPORT_TRANSACTIONS diff --git a/User_Setups/Setup9_ST7735_Overlap.h b/User_Setups/Setup9_ST7735_Overlap.h index c1fb0c0..d790a86 100644 --- a/User_Setups/Setup9_ST7735_Overlap.h +++ b/User_Setups/Setup9_ST7735_Overlap.h @@ -1,16 +1,12 @@ // USER DEFINED SETTINGS -// -// The User_Setup header that will be called up is defined in User_Setup_Select.h -// // Set driver type, fonts to be loaded, pins used and SPI control method etc -// -// If this file is editted correctly then all the library example sketches should +// +// See the User_Setup_Select.h file if you wish to be able to define multiple +// setups and then easily select which setup file is used by the compiler. +// +// If this file is edited correctly then all the library example sketches should // run without the need to make any more changes for a particular hardware setup! -// IMPORTANT -// This particular setup uses the SPI overlap capabiltiy of the ESP8266, this allows -// the FLASH SPI pins to be re-used with the TFT, saving pins for other functions. - // ################################################################################## // // Section 0. Call up the right driver file and any options for it @@ -20,8 +16,14 @@ // Only define one driver, the other ones must be commented out //#define ILI9341_DRIVER #define ST7735_DRIVER +//#define ILI9163_DRIVER +//#define S6D02A1_DRIVER +//#define RPI_ILI9486_DRIVER // 20MHz maximum SPI -// For ST7735 and ILI9163 ONLY, define the pixel width and height in portrait orientation +// For M5Stack ESP32 module with integrated display ONLY, remove // in line below +//#define M5STACK + +// For ST7735 and ILI9163 ONLY, define the pixel width and height in portrait orientation #define TFT_WIDTH 128 #define TFT_HEIGHT 160 //#define TFT_HEIGHT 128 @@ -37,6 +39,7 @@ //#define ST7735_GREENTAB //#define ST7735_GREENTAB2 //#define ST7735_GREENTAB3 +//#define ST7735_GREENTAB128 // For 128 x 128 display #define ST7735_REDTAB //#define ST7735_BLACKTAB @@ -47,46 +50,89 @@ // ################################################################################## // We must use hardware SPI, a minimum of 3 GPIO pins is needed. -// Typical setup for NodeMCU ESP-12 with SPI overlap is : +// Typical setup for ESP8266 NodeMCU ESP-12 is : // -// Display SDO/MISO to NodeMCU SD0 (or leave disconnected if not reading TFT) +// Display SDO/MISO to NodeMCU pin D6 (or leave disconnected if not reading TFT) // Display LED to NodeMCU pin VIN (or 5V, see below) -// Display SCK to NodeMCU pin CLK -// Display SDI/MOSI to NodeMCU pin SD1 -// Display DC (or AO)to NodeMCU pin D5 +// Display SCK to NodeMCU pin D5 +// Display SDI/MOSI to NodeMCU pin D7 +// Display DC (RS/AO)to NodeMCU pin D3 // Display RESET to NodeMCU pin D4 (or RST, see below) -// Display CS to NodeMCU pin D3 +// Display CS to NodeMCU pin D8 (or GND, see below) // Display GND to NodeMCU pin GND (0V) // Display VCC to NodeMCU 5V or 3.3V // // The TFT RESET pin can be connected to the NodeMCU RST pin or 3.3V to free up a control pin // +// The DC (Data Command) pin may be labeled AO or RS (Register Select) +// +// With some displays such as the ILI9341 the TFT CS pin can be connected to GND if no more +// SPI devices (e.g. an SD Card) are connected, in this case comment out the #define TFT_CS +// line below so it is NOT defined. Other displays such at the ST7735 require the TFT CS pin +// to be toggled during setup, so in these cases the TFT_CS line must be defined and connected. +// // The NodeMCU D0 pin can be used for RST // -// See Section 2. below if DC is connected to D0 +// See Section 2. below if DC or CS is connected to D0 // // Note: only some versions of the NodeMCU provide the USB 5V on the VIN pin // If 5V is not available at a pin you can use 3.3V but backlight brightness // will be lower. -// ###### EDIT THE PIN NUMBERS IN THE LINES FOLLOWING TO SUIT YOUR SETUP ###### -// ModeMCU -// Do not define TFT_CS in overlap mode, TFT chip select must connect to pin D3 +// ###### EDIT THE PIN NUMBERS IN THE LINES FOLLOWING TO SUIT YOUR ESP8266 SETUP ###### + +// For NodeMCU - use pin numbers in the form PIN_Dx where Dx is the NodeMCU pin designation +//#define TFT_CS PIN_D8 // Chip select control pin D8 +//#define TFT_DC PIN_D3 // Data Command control pin +//#define TFT_RST PIN_D4 // Reset pin (could connect to NodeMCU RST, see next line) +//#define TFT_RST -1 // Set TFT_RST to -1 if the display RESET is connected to NodeMCU RST or 3.3V + +//#define TOUCH_CS PIN_D1 // Chip select pin (T_CS) of touch screen + +//#define TFT_WR PIN_D2 // Write strobe for modified Raspberry Pi TFT only + + +// ###### FOR ESP8266 OVERLAP MODE EDIT THE PIN NUMBERS IN THE FOLLOWING LINES ###### + +// In ESP8266 overlap mode the TFT chip select MUST connect to pin D3 #define TFT_CS PIN_D3 #define TFT_DC PIN_D5 // Data Command control pin #define TFT_RST PIN_D4 // Reset pin (could connect to NodeMCU RST, see next line) //#define TFT_RST -1 // Set TFT_RST to -1 if the display RESET is connected to NodeMCU RST or 3.3V -// ESP32 Dev board -//#define TFT_CS 5 // Chip select control pin -//#define TFT_DC 2 // Data Command control pin -//#define TFT_RST 4 // Reset pin (could connect to Arduino RESET pin) +// In ESP8266 overlap mode the following must be defined +#define TFT_SPI_OVERLAP + +// ###### EDIT THE PIN NUMBERS IN THE LINES FOLLOWING TO SUIT YOUR ESP32 SETUP ###### + +// For ESP32 Dev board (only tested with ILI9341 display) +// The hardware SPI can be mapped to any pins + +//#define TFT_MISO 19 +//#define TFT_MOSI 23 +//#define TFT_SCLK 18 +//#define TFT_CS 15 // Chip select control pin +//#define TFT_DC 2 // Data Command control pin +//#define TFT_RST 4 // Reset pin (could connect to RST pin) //#define TFT_RST -1 // Set TFT_RST to -1 if display RESET is connected to ESP32 board RST +//#define TOUCH_CS 21 // Chip select pin (T_CS) of touch screen + +//#define TFT_WR 22 // Write strobe for modified Raspberry Pi TFT only + +// For the M5Stack module use these #define lines +//#define TFT_MISO 19 +//#define TFT_MOSI 23 +//#define TFT_SCLK 18 +//#define TFT_CS 14 // Chip select control pin +//#define TFT_DC 27 // Data Command control pin +//#define TFT_RST 33 // Reset pin (could connect to Arduino RESET pin) +//#define TFT_BL 32 // LED back-light + // ################################################################################## // -// Section 2. Define the way the DC and/or CS lines are driven +// Section 2. Define the way the DC and/or CS lines are driven (ESP8266 only) // // ################################################################################## @@ -106,9 +152,9 @@ // ################################################################################## // Comment out the #defines below with // to stop that font being loaded -// The ESP8366 had plenty of memory so commenting out fonts is not normally necessary -// If all fonts are loaded the extra FLASH space required is about 17Kbytes... -// To save FLASH space only enable the fonts you need! +// The ESP8366 and ESP32 have plenty of memory so commenting out fonts is not +// normally necessary. If all fonts are loaded the extra FLASH space required is +// about 17Kbytes. To save FLASH space only enable the fonts you need! #define LOAD_GLCD // Font 1. Original Adafruit 8 pixel font needs ~1820 bytes in FLASH #define LOAD_FONT2 // Font 2. Small 16 pixel high font, needs ~3534 bytes in FLASH, 96 characters @@ -131,33 +177,33 @@ // // ################################################################################## -// Define the SPI clock frequency +// Define the SPI clock frequency, this affects the graphics rendering speed. Too +// fast and the TFT driver will not keep up and display corruption appears. // With an ILI9341 display 40MHz works OK, 80MHz sometimes fails // With a ST7735 display more than 27MHz may not work (spurious pixels and lines) +// With an ILI9163 display 27 MHz works OK. +// The RPi typically only works at 20MHz maximum. // #define SPI_FREQUENCY 1000000 // #define SPI_FREQUENCY 5000000 // #define SPI_FREQUENCY 10000000 // #define SPI_FREQUENCY 20000000 - #define SPI_FREQUENCY 27000000 // Maximum for my ST7735. It is actually 26.67MHz = 80/3 +#define SPI_FREQUENCY 27000000 // Actually sets it to 26.67MHz = 80/3 // #define SPI_FREQUENCY 40000000 // Maximum to use SPIFFS // #define SPI_FREQUENCY 80000000 +// The XPT2046 requires a lower SPI clock rate of 2.5MHz so we define that here: +#define SPI_TOUCH_FREQUENCY 2500000 + // Comment out the following #define if "SPI Transactions" do not need to be -// supported. Tranaction support is required if other SPI devices are connected. -// When commented out the code size will be smaller and sketches will +// supported. When commented out the code size will be smaller and sketches will // run slightly faster, so leave it commented out unless you need it! + // Transaction support is needed to work with SD library but not needed with TFT_SdFat - +// Transaction support is required if other SPI devices are connected. + +// Transactions are automatically enabled by the library for an ESP32 (to use HAL mutex) +// so changing it here has no effect + // #define SUPPORT_TRANSACTIONS - -// If this next #define is not commented out then the SPI pins used by the program FLASH -// can be shared with the TFT, this frees up the HSPI SCK, MOSI and MISO pins. -// The TFT must be connected as follows for this to work: -// TFT Chip Select to GPIO0 (pin D3 on a NodeMCU) -// TFT MOSI/SDA to GPIO8/SDD1 (pin SD1 on a NodeMCU) -// TFT MISO to GPIO7/SDD0 (pin SD0 on a NodeMCU) - does not need to be connected -// TFT SCK to GPIO6/SDCLK (pin CLK on a NodeMCU) - -#define TFT_SPI_OVERLAP diff --git a/User_Setups/SetupX_Template.h b/User_Setups/SetupX_Template.h index cc28faa..c1b33dc 100644 --- a/User_Setups/SetupX_Template.h +++ b/User_Setups/SetupX_Template.h @@ -1,12 +1,10 @@ // USER DEFINED SETTINGS -// -// The User_Setup header that will be called up is defined in User_Setup_Select.h -// This file is a default template that can be copied to create new setup files -// Add the new header file to the list in User_Setup_Select.h -// // Set driver type, fonts to be loaded, pins used and SPI control method etc -// -// If this file is editted correctly then all the library example sketches should +// +// See the User_Setup_Select.h file if you wish to be able to define multiple +// setups and then easily select which setup file is used by the compiler. +// +// If this file is edited correctly then all the library example sketches should // run without the need to make any more changes for a particular hardware setup! // ################################################################################## @@ -18,8 +16,14 @@ // Only define one driver, the other ones must be commented out #define ILI9341_DRIVER //#define ST7735_DRIVER +//#define ILI9163_DRIVER +//#define S6D02A1_DRIVER +//#define RPI_ILI9486_DRIVER // 20MHz maximum SPI -// For ST7735 and ILI9163 ONLY, define the pixel width and height in portrait orientation +// For M5Stack ESP32 module with integrated display ONLY, remove // in line below +//#define M5STACK + +// For ST7735 and ILI9163 ONLY, define the pixel width and height in portrait orientation //#define TFT_WIDTH 128 //#define TFT_HEIGHT 160 //#define TFT_HEIGHT 128 @@ -34,6 +38,8 @@ //#define ST7735_INITB //#define ST7735_GREENTAB //#define ST7735_GREENTAB2 +//#define ST7735_GREENTAB3 +//#define ST7735_GREENTAB128 // For 128 x 128 display //#define ST7735_REDTAB //#define ST7735_BLACKTAB @@ -44,13 +50,13 @@ // ################################################################################## // We must use hardware SPI, a minimum of 3 GPIO pins is needed. -// Typical setup for NodeMCU ESP-12 is : +// Typical setup for ESP8266 NodeMCU ESP-12 is : // // Display SDO/MISO to NodeMCU pin D6 (or leave disconnected if not reading TFT) // Display LED to NodeMCU pin VIN (or 5V, see below) // Display SCK to NodeMCU pin D5 // Display SDI/MOSI to NodeMCU pin D7 -// Display DC (or AO)to NodeMCU pin D3 +// Display DC (RS/AO)to NodeMCU pin D3 // Display RESET to NodeMCU pin D4 (or RST, see below) // Display CS to NodeMCU pin D8 (or GND, see below) // Display GND to NodeMCU pin GND (0V) @@ -58,8 +64,10 @@ // // The TFT RESET pin can be connected to the NodeMCU RST pin or 3.3V to free up a control pin // +// The DC (Data Command) pin may be labeled AO or RS (Register Select) +// // With some displays such as the ILI9341 the TFT CS pin can be connected to GND if no more -// SPI deivces (e.g. an SD Card) are connected, in this case comment out the #define TFT_CS +// SPI devices (e.g. an SD Card) are connected, in this case comment out the #define TFT_CS // line below so it is NOT defined. Other displays such at the ST7735 require the TFT CS pin // to be toggled during setup, so in these cases the TFT_CS line must be defined and connected. // @@ -71,23 +79,60 @@ // If 5V is not available at a pin you can use 3.3V but backlight brightness // will be lower. -// ###### EDIT THE PIN NUMBERS IN THE LINES FOLLOWING TO SUIT YOUR SETUP ###### -// ModeMCU +// ###### EDIT THE PIN NUMBERS IN THE LINES FOLLOWING TO SUIT YOUR ESP8266 SETUP ###### + +// For NodeMCU - use pin numbers in the form PIN_Dx where Dx is the NodeMCU pin designation #define TFT_CS PIN_D8 // Chip select control pin D8 #define TFT_DC PIN_D3 // Data Command control pin #define TFT_RST PIN_D4 // Reset pin (could connect to NodeMCU RST, see next line) //#define TFT_RST -1 // Set TFT_RST to -1 if the display RESET is connected to NodeMCU RST or 3.3V -// ESP32 Dev board (planned, not supported yet) -//#define TFT_CS 5 // Chip select control pin -//#define TFT_DC 2 // Data Command control pin -//#define TFT_RST 4 // Reset pin (could connect to Arduino RESET pin) +//#define TOUCH_CS PIN_D1 // Chip select pin (T_CS) of touch screen + +//#define TFT_WR PIN_D2 // Write strobe for modified Raspberry Pi TFT only + + +// ###### FOR ESP8266 OVERLAP MODE EDIT THE PIN NUMBERS IN THE FOLLOWING LINES ###### + +// In ESP8266 overlap mode the TFT chip select MUST connect to pin D3 +//#define TFT_CS PIN_D3 +//#define TFT_DC PIN_D5 // Data Command control pin +//#define TFT_RST PIN_D4 // Reset pin (could connect to NodeMCU RST, see next line) +//#define TFT_RST -1 // Set TFT_RST to -1 if the display RESET is connected to NodeMCU RST or 3.3V + +// In ESP8266 overlap mode the following must be defined +//#define TFT_SPI_OVERLAP + +// ###### EDIT THE PIN NUMBERS IN THE LINES FOLLOWING TO SUIT YOUR ESP32 SETUP ###### + +// For ESP32 Dev board (only tested with ILI9341 display) +// The hardware SPI can be mapped to any pins + +//#define TFT_MISO 19 +//#define TFT_MOSI 23 +//#define TFT_SCLK 18 +//#define TFT_CS 15 // Chip select control pin +//#define TFT_DC 2 // Data Command control pin +//#define TFT_RST 4 // Reset pin (could connect to RST pin) //#define TFT_RST -1 // Set TFT_RST to -1 if display RESET is connected to ESP32 board RST +// For the M5Stack module use these #define lines +//#define TFT_MISO 19 +//#define TFT_MOSI 23 +//#define TFT_SCLK 18 +//#define TFT_CS 14 // Chip select control pin +//#define TFT_DC 27 // Data Command control pin +//#define TFT_RST 33 // Reset pin (could connect to Arduino RESET pin) +//#define TFT_BL 32 // LED back-light + +//#define TOUCH_CS 21 // Chip select pin (T_CS) of touch screen + +//#define TFT_WR 22 // Write strobe for modified Raspberry Pi TFT only + // ################################################################################## // -// Section 2. Define the way the DC and/or CS lines are driven +// Section 2. Define the way the DC and/or CS lines are driven (ESP8266 only) // // ################################################################################## @@ -107,9 +152,9 @@ // ################################################################################## // Comment out the #defines below with // to stop that font being loaded -// The ESP8366 had plenty of memory so commenting out fonts is not normally necessary -// If all fonts are loaded the extra FLASH space required is about 17Kbytes... -// To save FLASH space only enable the fonts you need! +// The ESP8366 and ESP32 have plenty of memory so commenting out fonts is not +// normally necessary. If all fonts are loaded the extra FLASH space required is +// about 17Kbytes. To save FLASH space only enable the fonts you need! #define LOAD_GLCD // Font 1. Original Adafruit 8 pixel font needs ~1820 bytes in FLASH #define LOAD_FONT2 // Font 2. Small 16 pixel high font, needs ~3534 bytes in FLASH, 96 characters @@ -132,23 +177,33 @@ // // ################################################################################## -// Define the SPI clock frequency +// Define the SPI clock frequency, this affects the graphics rendering speed. Too +// fast and the TFT driver will not keep up and display corruption appears. // With an ILI9341 display 40MHz works OK, 80MHz sometimes fails // With a ST7735 display more than 27MHz may not work (spurious pixels and lines) +// With an ILI9163 display 27 MHz works OK. +// The RPi typically only works at 20MHz maximum. // #define SPI_FREQUENCY 1000000 // #define SPI_FREQUENCY 5000000 // #define SPI_FREQUENCY 10000000 // #define SPI_FREQUENCY 20000000 -// #define SPI_FREQUENCY 27000000 // Actually sets it to 26.67MHz = 80/3 - #define SPI_FREQUENCY 40000000 // Maximum to use SPIFFS +#define SPI_FREQUENCY 27000000 // Actually sets it to 26.67MHz = 80/3 +// #define SPI_FREQUENCY 40000000 // Maximum to use SPIFFS // #define SPI_FREQUENCY 80000000 +// The XPT2046 requires a lower SPI clock rate of 2.5MHz so we define that here: +#define SPI_TOUCH_FREQUENCY 2500000 + // Comment out the following #define if "SPI Transactions" do not need to be -// supported. Tranaction support is required if other SPI devices are connected. -// When commented out the code size will be smaller and sketches will +// supported. When commented out the code size will be smaller and sketches will // run slightly faster, so leave it commented out unless you need it! + // Transaction support is needed to work with SD library but not needed with TFT_SdFat +// Transaction support is required if other SPI devices are connected. + +// Transactions are automatically enabled by the library for an ESP32 (to use HAL mutex) +// so changing it here has no effect // #define SUPPORT_TRANSACTIONS diff --git a/library.json b/library.json index d315019..411480b 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "TFT_eSPI", - "version": "0.18.15", + "version": "0.18.16", "keywords": "tft, display, ESP8266, NodeMCU, ESP32, M5Stack, ILI9341, ST7735, ILI9163, S6D02A1, ILI9486", "description": "A TFT SPI graphics library for ESP8266 and ESP32", "repository": diff --git a/library.properties b/library.properties index b08cc2f..c28e7ec 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=TFT_eSPI -version=0.18.15 +version=0.18.16 author=Bodmer maintainer=Bodmer sentence=A fast TFT library for ESP8266 processors and the Arduino IDE From 2fbc2e4adfe56a81d956a9a18fc12471c4ef9d01 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Thu, 22 Feb 2018 22:28:23 +0000 Subject: [PATCH 083/150] Add text for ESP8266 overlap mode, add workaround for ESP32 silicon bug --- TFT_eSPI.h | 2 +- User_Setup.h | 4 ++++ User_Setups/Setup10_RPi_touch_ILI9486.h | 4 ++++ User_Setups/Setup11_RPi_touch_ILI9486.h | 4 ++++ User_Setups/Setup12_M5Stack.h | 4 ++++ User_Setups/Setup1_ILI9341.h | 4 ++++ User_Setups/Setup2_ST7735.h | 4 ++++ User_Setups/Setup3_ILI9163.h | 4 ++++ User_Setups/Setup4_S6D02A1.h | 4 ++++ User_Setups/Setup5_RPi_ILI9486.h | 4 ++++ User_Setups/Setup6_RPi_Wr_ILI9486.h | 4 ++++ User_Setups/Setup7_ST7735_128x128.h | 4 ++++ User_Setups/Setup8_ILI9163_128x128.h | 4 ++++ User_Setups/Setup9_ST7735_Overlap.h | 4 ++++ User_Setups/SetupX_Template.h | 4 ++++ 15 files changed, 57 insertions(+), 1 deletion(-) diff --git a/TFT_eSPI.h b/TFT_eSPI.h index 2c54570..bae7afe 100644 --- a/TFT_eSPI.h +++ b/TFT_eSPI.h @@ -102,7 +102,7 @@ //#define CS_L digitalWrite(TFT_CS, HIGH); GPIO.out_w1tc = (1 << TFT_CS)//digitalWrite(TFT_CS, LOW) //#define CS_H digitalWrite(TFT_CS, LOW); GPIO.out_w1ts = (1 << TFT_CS)//digitalWrite(TFT_CS, HIGH) #define CS_L GPIO.out_w1ts = (1 << TFT_CS);GPIO.out_w1tc = (1 << TFT_CS) - #define CS_H GPIO.out_w1ts = (1 << TFT_CS) + #define CS_H GPIO.out_w1tc = (1 << TFT_CS);GPIO.out_w1ts = (1 << TFT_CS) #else #define CS_L GPOC=cspinmask #define CS_H GPOS=cspinmask diff --git a/User_Setup.h b/User_Setup.h index c1b33dc..3b635f9 100644 --- a/User_Setup.h +++ b/User_Setup.h @@ -95,6 +95,10 @@ // ###### FOR ESP8266 OVERLAP MODE EDIT THE PIN NUMBERS IN THE FOLLOWING LINES ###### +// Overlap mode shares the ESP8266 FLASH SPI bus with the TFT so has a performance impact +// but saves pins for other functions. +// Use NodeMCU SD0=MISO, SD1=MOSI, CLK=SCLK to connect to TFT in overlap mode + // In ESP8266 overlap mode the TFT chip select MUST connect to pin D3 //#define TFT_CS PIN_D3 //#define TFT_DC PIN_D5 // Data Command control pin diff --git a/User_Setups/Setup10_RPi_touch_ILI9486.h b/User_Setups/Setup10_RPi_touch_ILI9486.h index 4bf52c5..248020c 100644 --- a/User_Setups/Setup10_RPi_touch_ILI9486.h +++ b/User_Setups/Setup10_RPi_touch_ILI9486.h @@ -95,6 +95,10 @@ // ###### FOR ESP8266 OVERLAP MODE EDIT THE PIN NUMBERS IN THE FOLLOWING LINES ###### +// Overlap mode shares the ESP8266 FLASH SPI bus with the TFT so has a performance impact +// but saves pins for other functions. +// Use NodeMCU SD0=MISO, SD1=MOSI, CLK=SCLK to connect to TFT in overlap mode + // In ESP8266 overlap mode the TFT chip select MUST connect to pin D3 //#define TFT_CS PIN_D3 //#define TFT_DC PIN_D5 // Data Command control pin diff --git a/User_Setups/Setup11_RPi_touch_ILI9486.h b/User_Setups/Setup11_RPi_touch_ILI9486.h index d6218e3..a30d502 100644 --- a/User_Setups/Setup11_RPi_touch_ILI9486.h +++ b/User_Setups/Setup11_RPi_touch_ILI9486.h @@ -95,6 +95,10 @@ // ###### FOR ESP8266 OVERLAP MODE EDIT THE PIN NUMBERS IN THE FOLLOWING LINES ###### +// Overlap mode shares the ESP8266 FLASH SPI bus with the TFT so has a performance impact +// but saves pins for other functions. +// Use NodeMCU SD0=MISO, SD1=MOSI, CLK=SCLK to connect to TFT in overlap mode + // In ESP8266 overlap mode the TFT chip select MUST connect to pin D3 //#define TFT_CS PIN_D3 //#define TFT_DC PIN_D5 // Data Command control pin diff --git a/User_Setups/Setup12_M5Stack.h b/User_Setups/Setup12_M5Stack.h index 767edf3..9229925 100644 --- a/User_Setups/Setup12_M5Stack.h +++ b/User_Setups/Setup12_M5Stack.h @@ -95,6 +95,10 @@ // ###### FOR ESP8266 OVERLAP MODE EDIT THE PIN NUMBERS IN THE FOLLOWING LINES ###### +// Overlap mode shares the ESP8266 FLASH SPI bus with the TFT so has a performance impact +// but saves pins for other functions. +// Use NodeMCU SD0=MISO, SD1=MOSI, CLK=SCLK to connect to TFT in overlap mode + // In ESP8266 overlap mode the TFT chip select MUST connect to pin D3 //#define TFT_CS PIN_D3 //#define TFT_DC PIN_D5 // Data Command control pin diff --git a/User_Setups/Setup1_ILI9341.h b/User_Setups/Setup1_ILI9341.h index ad08dd7..7831b6a 100644 --- a/User_Setups/Setup1_ILI9341.h +++ b/User_Setups/Setup1_ILI9341.h @@ -95,6 +95,10 @@ // ###### FOR ESP8266 OVERLAP MODE EDIT THE PIN NUMBERS IN THE FOLLOWING LINES ###### +// Overlap mode shares the ESP8266 FLASH SPI bus with the TFT so has a performance impact +// but saves pins for other functions. +// Use NodeMCU SD0=MISO, SD1=MOSI, CLK=SCLK to connect to TFT in overlap mode + // In ESP8266 overlap mode the TFT chip select MUST connect to pin D3 //#define TFT_CS PIN_D3 //#define TFT_DC PIN_D5 // Data Command control pin diff --git a/User_Setups/Setup2_ST7735.h b/User_Setups/Setup2_ST7735.h index d7c7fdc..14dac9b 100644 --- a/User_Setups/Setup2_ST7735.h +++ b/User_Setups/Setup2_ST7735.h @@ -95,6 +95,10 @@ // ###### FOR ESP8266 OVERLAP MODE EDIT THE PIN NUMBERS IN THE FOLLOWING LINES ###### +// Overlap mode shares the ESP8266 FLASH SPI bus with the TFT so has a performance impact +// but saves pins for other functions. +// Use NodeMCU SD0=MISO, SD1=MOSI, CLK=SCLK to connect to TFT in overlap mode + // In ESP8266 overlap mode the TFT chip select MUST connect to pin D3 //#define TFT_CS PIN_D3 //#define TFT_DC PIN_D5 // Data Command control pin diff --git a/User_Setups/Setup3_ILI9163.h b/User_Setups/Setup3_ILI9163.h index fc8062e..36d334d 100644 --- a/User_Setups/Setup3_ILI9163.h +++ b/User_Setups/Setup3_ILI9163.h @@ -95,6 +95,10 @@ // ###### FOR ESP8266 OVERLAP MODE EDIT THE PIN NUMBERS IN THE FOLLOWING LINES ###### +// Overlap mode shares the ESP8266 FLASH SPI bus with the TFT so has a performance impact +// but saves pins for other functions. +// Use NodeMCU SD0=MISO, SD1=MOSI, CLK=SCLK to connect to TFT in overlap mode + // In ESP8266 overlap mode the TFT chip select MUST connect to pin D3 //#define TFT_CS PIN_D3 //#define TFT_DC PIN_D5 // Data Command control pin diff --git a/User_Setups/Setup4_S6D02A1.h b/User_Setups/Setup4_S6D02A1.h index 3136d5d..3f94e71 100644 --- a/User_Setups/Setup4_S6D02A1.h +++ b/User_Setups/Setup4_S6D02A1.h @@ -95,6 +95,10 @@ // ###### FOR ESP8266 OVERLAP MODE EDIT THE PIN NUMBERS IN THE FOLLOWING LINES ###### +// Overlap mode shares the ESP8266 FLASH SPI bus with the TFT so has a performance impact +// but saves pins for other functions. +// Use NodeMCU SD0=MISO, SD1=MOSI, CLK=SCLK to connect to TFT in overlap mode + // In ESP8266 overlap mode the TFT chip select MUST connect to pin D3 //#define TFT_CS PIN_D3 //#define TFT_DC PIN_D5 // Data Command control pin diff --git a/User_Setups/Setup5_RPi_ILI9486.h b/User_Setups/Setup5_RPi_ILI9486.h index 38c12f6..3301daa 100644 --- a/User_Setups/Setup5_RPi_ILI9486.h +++ b/User_Setups/Setup5_RPi_ILI9486.h @@ -95,6 +95,10 @@ // ###### FOR ESP8266 OVERLAP MODE EDIT THE PIN NUMBERS IN THE FOLLOWING LINES ###### +// Overlap mode shares the ESP8266 FLASH SPI bus with the TFT so has a performance impact +// but saves pins for other functions. +// Use NodeMCU SD0=MISO, SD1=MOSI, CLK=SCLK to connect to TFT in overlap mode + // In ESP8266 overlap mode the TFT chip select MUST connect to pin D3 //#define TFT_CS PIN_D3 //#define TFT_DC PIN_D5 // Data Command control pin diff --git a/User_Setups/Setup6_RPi_Wr_ILI9486.h b/User_Setups/Setup6_RPi_Wr_ILI9486.h index 06777cd..8a3c00c 100644 --- a/User_Setups/Setup6_RPi_Wr_ILI9486.h +++ b/User_Setups/Setup6_RPi_Wr_ILI9486.h @@ -95,6 +95,10 @@ // ###### FOR ESP8266 OVERLAP MODE EDIT THE PIN NUMBERS IN THE FOLLOWING LINES ###### +// Overlap mode shares the ESP8266 FLASH SPI bus with the TFT so has a performance impact +// but saves pins for other functions. +// Use NodeMCU SD0=MISO, SD1=MOSI, CLK=SCLK to connect to TFT in overlap mode + // In ESP8266 overlap mode the TFT chip select MUST connect to pin D3 //#define TFT_CS PIN_D3 //#define TFT_DC PIN_D5 // Data Command control pin diff --git a/User_Setups/Setup7_ST7735_128x128.h b/User_Setups/Setup7_ST7735_128x128.h index 7f4f267..481eebc 100644 --- a/User_Setups/Setup7_ST7735_128x128.h +++ b/User_Setups/Setup7_ST7735_128x128.h @@ -95,6 +95,10 @@ // ###### FOR ESP8266 OVERLAP MODE EDIT THE PIN NUMBERS IN THE FOLLOWING LINES ###### +// Overlap mode shares the ESP8266 FLASH SPI bus with the TFT so has a performance impact +// but saves pins for other functions. +// Use NodeMCU SD0=MISO, SD1=MOSI, CLK=SCLK to connect to TFT in overlap mode + // In ESP8266 overlap mode the TFT chip select MUST connect to pin D3 //#define TFT_CS PIN_D3 //#define TFT_DC PIN_D5 // Data Command control pin diff --git a/User_Setups/Setup8_ILI9163_128x128.h b/User_Setups/Setup8_ILI9163_128x128.h index ec017fc..4b643b1 100644 --- a/User_Setups/Setup8_ILI9163_128x128.h +++ b/User_Setups/Setup8_ILI9163_128x128.h @@ -95,6 +95,10 @@ // ###### FOR ESP8266 OVERLAP MODE EDIT THE PIN NUMBERS IN THE FOLLOWING LINES ###### +// Overlap mode shares the ESP8266 FLASH SPI bus with the TFT so has a performance impact +// but saves pins for other functions. +// Use NodeMCU SD0=MISO, SD1=MOSI, CLK=SCLK to connect to TFT in overlap mode + // In ESP8266 overlap mode the TFT chip select MUST connect to pin D3 //#define TFT_CS PIN_D3 //#define TFT_DC PIN_D5 // Data Command control pin diff --git a/User_Setups/Setup9_ST7735_Overlap.h b/User_Setups/Setup9_ST7735_Overlap.h index d790a86..b378791 100644 --- a/User_Setups/Setup9_ST7735_Overlap.h +++ b/User_Setups/Setup9_ST7735_Overlap.h @@ -95,6 +95,10 @@ // ###### FOR ESP8266 OVERLAP MODE EDIT THE PIN NUMBERS IN THE FOLLOWING LINES ###### +// Overlap mode shares the ESP8266 FLASH SPI bus with the TFT so has a performance impact +// but saves pins for other functions. +// Use NodeMCU SD0=MISO, SD1=MOSI, CLK=SCLK to connect to TFT in overlap mode + // In ESP8266 overlap mode the TFT chip select MUST connect to pin D3 #define TFT_CS PIN_D3 #define TFT_DC PIN_D5 // Data Command control pin diff --git a/User_Setups/SetupX_Template.h b/User_Setups/SetupX_Template.h index c1b33dc..3b635f9 100644 --- a/User_Setups/SetupX_Template.h +++ b/User_Setups/SetupX_Template.h @@ -95,6 +95,10 @@ // ###### FOR ESP8266 OVERLAP MODE EDIT THE PIN NUMBERS IN THE FOLLOWING LINES ###### +// Overlap mode shares the ESP8266 FLASH SPI bus with the TFT so has a performance impact +// but saves pins for other functions. +// Use NodeMCU SD0=MISO, SD1=MOSI, CLK=SCLK to connect to TFT in overlap mode + // In ESP8266 overlap mode the TFT chip select MUST connect to pin D3 //#define TFT_CS PIN_D3 //#define TFT_DC PIN_D5 // Data Command control pin From 8df115ab4d8bc4f8837c385dcfd4e3788534cfbc Mon Sep 17 00:00:00 2001 From: Bodmer Date: Thu, 22 Feb 2018 22:33:49 +0000 Subject: [PATCH 084/150] Raise version --- TFT_eSPI.cpp | 10 +++++----- library.json | 2 +- library.properties | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/TFT_eSPI.cpp b/TFT_eSPI.cpp index 184dbfe..31ab2f5 100644 --- a/TFT_eSPI.cpp +++ b/TFT_eSPI.cpp @@ -1,9 +1,9 @@ /*************************************************** - Arduino TFT graphics library targetted at ESP8266 + Arduino TFT graphics library targeted at ESP8266 and ESP32 based boards. This is a standalone library that contains the - hardware driver, the graphics funtions and the + hardware driver, the graphics functions and the proportional fonts. The larger fonts are Run Length Encoded to reduce their @@ -19,7 +19,7 @@ #include - // SUPPORT_TRANSACTIONS is manadatory for ESP32 so the hal mutex is toggled + // SUPPORT_TRANSACTIONS is mandatory for ESP32 so the hal mutex is toggled #if defined (ESP32) && !defined (SUPPORT_TRANSACTIONS) #define SUPPORT_TRANSACTIONS #endif @@ -206,7 +206,7 @@ void TFT_eSPI::init(void) inTransaction = false; locked = true; - // SUPPORT_TRANSACTIONS is manadatory for ESP32 so the hal mutex is toggled + // SUPPORT_TRANSACTIONS is mandatory for ESP32 so the hal mutex is toggled // so the code here is for ESP8266 only #if !defined (SUPPORT_TRANSACTIONS) && defined (ESP8266) SPI.setBitOrder(MSBFIRST); @@ -2876,7 +2876,7 @@ else SPI.writeBytes((uint8_t*)data,len<<1); ** Description: draw a line between 2 arbitrary points ***************************************************************************************/ // Bresenham's algorithm - thx wikipedia - speed enhanced by Bodmer to use -// an eficient FastH/V Line draw routine for line segments of 2 pixels or more +// an efficient FastH/V Line draw routine for line segments of 2 pixels or more #if defined (RPI_ILI9486_DRIVER) || defined (ESP32) || defined (RPI_WRITE_STROBE) diff --git a/library.json b/library.json index 411480b..2041a92 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "TFT_eSPI", - "version": "0.18.16", + "version": "0.18.17", "keywords": "tft, display, ESP8266, NodeMCU, ESP32, M5Stack, ILI9341, ST7735, ILI9163, S6D02A1, ILI9486", "description": "A TFT SPI graphics library for ESP8266 and ESP32", "repository": diff --git a/library.properties b/library.properties index c28e7ec..e235cd1 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=TFT_eSPI -version=0.18.16 +version=0.18.17 author=Bodmer maintainer=Bodmer sentence=A fast TFT library for ESP8266 processors and the Arduino IDE From a44ea8d94511de8018736bc1a8b4646758a9eeda Mon Sep 17 00:00:00 2001 From: Bodmer Date: Fri, 23 Feb 2018 23:42:34 +0000 Subject: [PATCH 085/150] Add comment about Smooth_font branch --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index da4a689..d509063 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ Configuration of the library font selections, pins used to interface with the TF I have made some changes that will be uploaded soon that improves sprite and image rendering performance by up to 3x faster on the ESP8266. These updates are currently being tested/debugged. -**2. Anti-aliased fonts** +**2. Anti-aliased fonts - see Smooth_font branch for beta test version ** I have been experimenting with anti-aliased font files in "vlw" format generated by the free [Processing IDE](https://processing.org/). This IDE can be used to generate font files from your computer's font set and include **any** Unicode characters. This means Greek, Japanese and any other UTF-16 glyphs can be used. From 3d993afe7e71a3b10a6fead3a4c0115ed49be95f Mon Sep 17 00:00:00 2001 From: Bodmer Date: Fri, 23 Feb 2018 23:44:12 +0000 Subject: [PATCH 086/150] see Smooth_font branch for beta version --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d509063..4647721 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ Configuration of the library font selections, pins used to interface with the TF I have made some changes that will be uploaded soon that improves sprite and image rendering performance by up to 3x faster on the ESP8266. These updates are currently being tested/debugged. -**2. Anti-aliased fonts - see Smooth_font branch for beta test version ** +**2. Anti-aliased fonts - see Smooth_font branch for beta version** I have been experimenting with anti-aliased font files in "vlw" format generated by the free [Processing IDE](https://processing.org/). This IDE can be used to generate font files from your computer's font set and include **any** Unicode characters. This means Greek, Japanese and any other UTF-16 glyphs can be used. From 02a902f617cbfa37f0143dd423bf050c41b601b4 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Sat, 24 Feb 2018 18:35:03 +0000 Subject: [PATCH 087/150] Potential fix for issue #96 --- TFT_eSPI.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/TFT_eSPI.cpp b/TFT_eSPI.cpp index 31ab2f5..a7c535c 100644 --- a/TFT_eSPI.cpp +++ b/TFT_eSPI.cpp @@ -4377,7 +4377,7 @@ void TFT_eSPI::calibrateTouch(uint16_t *parameters, uint32_t color_fg, uint32_t for(uint8_t j= 0; j<8; j++){ // Use a lower detect threshold as corners tend to be less sensitive - while(!validTouch(&x_tmp, &y_tmp, Z_THRESHOLD/2)) delay(10); + while(!validTouch(&x_tmp, &y_tmp, Z_THRESHOLD/4)); values[i*2 ] += x_tmp; values[i*2+1] += y_tmp; } @@ -4389,14 +4389,14 @@ void TFT_eSPI::calibrateTouch(uint16_t *parameters, uint32_t color_fg, uint32_t // check orientation // from case 0 to case 1, the y value changed. - // If the meassured delta of the touch x axis is bigger than the delta of the y axis, the touch and TFT axes are switched. + // If the measured delta of the touch x axis is bigger than the delta of the y axis, the touch and TFT axes are switched. touchCalibration_rotate = false; if(abs(values[0]-values[2]) > abs(values[1]-values[3])){ touchCalibration_rotate = true; - touchCalibration_x1 = (values[0] + values[4])/2; // calc min x - touchCalibration_x0 = (values[2] + values[6])/2; // calc max x - touchCalibration_y1 = (values[1] + values[3])/2; // calc min y - touchCalibration_y0 = (values[5] + values[7])/2; // calc max y + touchCalibration_x0 = (values[0] + values[4])/2; // calc min x + touchCalibration_x1 = (values[2] + values[6])/2; // calc max x + touchCalibration_y0 = (values[1] + values[3])/2; // calc min y + touchCalibration_y1 = (values[5] + values[7])/2; // calc max y } else { touchCalibration_x0 = (values[0] + values[2])/2; // calc min x touchCalibration_x1 = (values[4] + values[6])/2; // calc max x From 8752236ac28847349e43f01a92a4046e27b520ec Mon Sep 17 00:00:00 2001 From: Bodmer Date: Sat, 24 Feb 2018 19:02:20 +0000 Subject: [PATCH 088/150] Add smooth (antialiased) fonts --- Extensions/Button.cpp | 76 + Extensions/Button.h | 38 + Extensions/Smooth_font.cpp | 511 +++++ Extensions/Smooth_font.h | 54 + Extensions/Sprite.cpp | 1409 +++++++++++++ Extensions/Sprite.h | 111 + Extensions/Touch.cpp | 319 +++ Extensions/Touch.h | 24 + Fonts/Font7srle.c | 8 +- README.md | 4 +- TFT_Drivers/ILI9163_Rotation.h | 16 +- TFT_Drivers/ILI9341_Rotation.h | 32 +- TFT_Drivers/RPI_ILI9486_Rotation.h | 32 +- TFT_Drivers/S6D02A1_Rotation.h | 16 +- TFT_Drivers/ST7735_Rotation.h | 32 +- TFT_eSPI.cpp | 1866 ++--------------- TFT_eSPI.h | 228 +- .../Create_font_5/Create_font_5.pde | 483 +++++ .../Create_font_5/data/Final-Frontier.ttf | Bin 0 -> 19800 bytes Tools/PlatformIO/Configuring options.txt | 33 + User_Setup.h | 14 +- User_Setup_Select.h | 4 +- .../Print_Smooth_Font/Print_Smooth_Font.ino | 195 ++ .../data/Final-Frontier-28.vlw | Bin 0 -> 25287 bytes .../Unicode_test/SPIFFS_functions.ino | 83 + .../Unicode_test/Unicode_test.ino | 148 ++ .../Unicode_test/data/Final-Frontier-28.vlw | Bin 0 -> 25287 bytes .../Unicode_test/data/Latin-Hiragana-24.vlw | Bin 0 -> 54478 bytes .../Unicode_test/data/Unicode-Test-72.vlw | Bin 0 -> 36469 bytes .../alphaBlend_Test/alphaBlend_Test.ino | 194 ++ keywords.txt | 8 +- library.json | 2 +- library.properties | 2 +- license.txt | 4 +- 34 files changed, 4031 insertions(+), 1915 deletions(-) create mode 100644 Extensions/Button.cpp create mode 100644 Extensions/Button.h create mode 100644 Extensions/Smooth_font.cpp create mode 100644 Extensions/Smooth_font.h create mode 100644 Extensions/Sprite.cpp create mode 100644 Extensions/Sprite.h create mode 100644 Extensions/Touch.cpp create mode 100644 Extensions/Touch.h create mode 100644 Tools/Create_Smooth_Font/Create_font_5/Create_font_5.pde create mode 100644 Tools/Create_Smooth_Font/Create_font_5/data/Final-Frontier.ttf create mode 100644 Tools/PlatformIO/Configuring options.txt create mode 100644 examples/Smooth Fonts/Print_Smooth_Font/Print_Smooth_Font.ino create mode 100644 examples/Smooth Fonts/Print_Smooth_Font/data/Final-Frontier-28.vlw create mode 100644 examples/Smooth Fonts/Unicode_test/SPIFFS_functions.ino create mode 100644 examples/Smooth Fonts/Unicode_test/Unicode_test.ino create mode 100644 examples/Smooth Fonts/Unicode_test/data/Final-Frontier-28.vlw create mode 100644 examples/Smooth Fonts/Unicode_test/data/Latin-Hiragana-24.vlw create mode 100644 examples/Smooth Fonts/Unicode_test/data/Unicode-Test-72.vlw create mode 100644 examples/Smooth Fonts/alphaBlend_Test/alphaBlend_Test.ino diff --git a/Extensions/Button.cpp b/Extensions/Button.cpp new file mode 100644 index 0000000..88a8bc0 --- /dev/null +++ b/Extensions/Button.cpp @@ -0,0 +1,76 @@ +/*************************************************************************************** +** Code for the GFX button UI element +** Grabbed from Adafruit_GFX library and enhanced to handle any label font +***************************************************************************************/ +TFT_eSPI_Button::TFT_eSPI_Button(void) { + _gfx = 0; +} + +// Classic initButton() function: pass center & size +void TFT_eSPI_Button::initButton( + TFT_eSPI *gfx, int16_t x, int16_t y, uint16_t w, uint16_t h, + uint16_t outline, uint16_t fill, uint16_t textcolor, + char *label, uint8_t textsize) +{ + // Tweak arguments and pass to the newer initButtonUL() function... + initButtonUL(gfx, x - (w / 2), y - (h / 2), w, h, outline, fill, + textcolor, label, textsize); +} + +// Newer function instead accepts upper-left corner & size +void TFT_eSPI_Button::initButtonUL( + TFT_eSPI *gfx, int16_t x1, int16_t y1, uint16_t w, uint16_t h, + uint16_t outline, uint16_t fill, uint16_t textcolor, + char *label, uint8_t textsize) +{ + _x1 = x1; + _y1 = y1; + _w = w; + _h = h; + _outlinecolor = outline; + _fillcolor = fill; + _textcolor = textcolor; + _textsize = textsize; + _gfx = gfx; + strncpy(_label, label, 9); +} + +void TFT_eSPI_Button::drawButton(boolean inverted) { + uint16_t fill, outline, text; + + if(!inverted) { + fill = _fillcolor; + outline = _outlinecolor; + text = _textcolor; + } else { + fill = _textcolor; + outline = _outlinecolor; + text = _fillcolor; + } + + uint8_t r = min(_w, _h) / 4; // Corner radius + _gfx->fillRoundRect(_x1, _y1, _w, _h, r, fill); + _gfx->drawRoundRect(_x1, _y1, _w, _h, r, outline); + + _gfx->setTextColor(text); + _gfx->setTextSize(_textsize); + + uint8_t tempdatum = _gfx->getTextDatum(); + _gfx->setTextDatum(MC_DATUM); + _gfx->drawString(_label, _x1 + (_w/2), _y1 + (_h/2)); + _gfx->setTextDatum(tempdatum); +} + +boolean TFT_eSPI_Button::contains(int16_t x, int16_t y) { + return ((x >= _x1) && (x < (_x1 + _w)) && + (y >= _y1) && (y < (_y1 + _h))); +} + +void TFT_eSPI_Button::press(boolean p) { + laststate = currstate; + currstate = p; +} + +boolean TFT_eSPI_Button::isPressed() { return currstate; } +boolean TFT_eSPI_Button::justPressed() { return (currstate && !laststate); } +boolean TFT_eSPI_Button::justReleased() { return (!currstate && laststate); } diff --git a/Extensions/Button.h b/Extensions/Button.h new file mode 100644 index 0000000..e44a8f4 --- /dev/null +++ b/Extensions/Button.h @@ -0,0 +1,38 @@ +/*************************************************************************************** +// The following button class has been ported over from the Adafruit_GFX library so +// should be compatible. +// A slightly different implementation in this TFT_eSPI library allows the button +// legends to be in any font +***************************************************************************************/ + +class TFT_eSPI_Button { + + public: + TFT_eSPI_Button(void); + // "Classic" initButton() uses center & size + void initButton(TFT_eSPI *gfx, int16_t x, int16_t y, + uint16_t w, uint16_t h, uint16_t outline, uint16_t fill, + uint16_t textcolor, char *label, uint8_t textsize); + + // New/alt initButton() uses upper-left corner & size + void initButtonUL(TFT_eSPI *gfx, int16_t x1, int16_t y1, + uint16_t w, uint16_t h, uint16_t outline, uint16_t fill, + uint16_t textcolor, char *label, uint8_t textsize); + void drawButton(boolean inverted = false); + boolean contains(int16_t x, int16_t y); + + void press(boolean p); + boolean isPressed(); + boolean justPressed(); + boolean justReleased(); + + private: + TFT_eSPI *_gfx; + int16_t _x1, _y1; // Coordinates of top-left corner + uint16_t _w, _h; + uint8_t _textsize; + uint16_t _outlinecolor, _fillcolor, _textcolor; + char _label[10]; + + boolean currstate, laststate; +}; diff --git a/Extensions/Smooth_font.cpp b/Extensions/Smooth_font.cpp new file mode 100644 index 0000000..dc41edb --- /dev/null +++ b/Extensions/Smooth_font.cpp @@ -0,0 +1,511 @@ + // Coded by Bodmer 10/2/18, see license in root directory. + // This is part of the TFT_eSPI class and is associated with anti-aliased font functions + + +//////////////////////////////////////////////////////////////////////////////////////// +// New anti-aliased (smoothed) font functions added below +//////////////////////////////////////////////////////////////////////////////////////// + +/*************************************************************************************** +** Function name: loadFont +** Description: loads parameters from a new font vlw file stored in SPIFFS +*************************************************************************************x*/ +void TFT_eSPI::loadFont(String fontName) +{ + /* + The vlw font format does not appear to be documented anywhere, so some reverse + engineering has been applied! + + Header of vlw file comprises 6 uint32_t parameters (24 bytes total): + 1. The gCount (number of character glyphs) + 2. A version number (0xB = 11 for the one I am using) + 3. The font size (in points, not pixels) + 4. Deprecated mboxY parameter (typically set to 0) + 5. Ascent in pixels from baseline to top of "d" + 6. Descent in pixels from baseline to bottom of "p" + + Next are gCount sets of values for each glyph, each set comprises 7 int32t parameters (28 bytes): + 1. Glyph Unicode stored as a 32 bit value + 2. Height of bitmap bounding box + 3. Width of bitmap bounding box + 4. gxAdvance for cursor (setWidth in Processing) + 5. dY = distance from cursor baseline to top of glyph bitmap (signed value +ve = up) + 6. dX = distance from cursor to left side of glyph bitmap (signed value -ve = left) + 7. padding value, typically 0 + + The bitmaps start next at 24 + (28 * gCount) bytes from the start of the file. + Each pixel is 1 byte, an 8 bit Alpha value which represents the transparency from + 0xFF foreground colour, 0x00 background. The sketch uses a linear interpolation + between the foreground and background RGB component colours. e.g. + pixelRed = ((fgRed * alpha) + (bgRed * (255 - alpha))/255 + To gain a performance advantage fixed point arithmetic is used with rounding and + division by 256 (shift right 8 bits is faster). + + After the bitmaps is: + 1 byte for font name string length (excludes null) + a zero terminated character string giving the font name + 1 byte for Postscript name string length + a zero/one terminated character string giving the font name + last byte is 0 for non-anti-aliased and 1 for anti-aliased (smoothed) + + Then the font name seen by Java when it's created + Then the postscript name of the font + Then a boolean to tell if smoothing is on or not. + + Glyph bitmap example is: + // Cursor coordinate positions for this and next character are marked by 'C' + // C<------- gxAdvance ------->C gxAdvance is how far to move cursor for next glyph cursor position + // | | + // | | ascent is top of "d", descent is bottom of "p" + // +-- gdX --+ ascent + // | +-- gWidth--+ | gdX is offset to left edge of glyph bitmap + // | + x@.........@x + | gdX may be negative e.g. italic "y" tail extending to left of + // | | @@.........@@ | | cursor position, plot top left corner of bitmap at (cursorX + gdX) + // | | @@.........@@ gdY | gWidth and gHeight are glyph bitmap dimensions + // | | .@@@.....@@@@ | | + // | gHeight ....@@@@@..@@ + + <-- baseline + // | | ...........@@ | + // | | ...........@@ | gdY is the offset to the top edge of the bitmap + // | | .@@.......@@. descent plot top edge of bitmap at (cursorY + yAdvance - gdY) + // | + x..@@@@@@@..x | x marks the corner pixels of the bitmap + // | | + // +---------------------------+ yAdvance is y delta for the next line, font size or (ascent + descent) + // some fonts can overlay in y direction so may need a user adjust value + + */ + + _gFontFilename = "/" + fontName + ".vlw"; + + fontFile = SPIFFS.open( _gFontFilename, "r"); + + if(!fontFile) return; + + //unloadFont(); + + fontFile.seek(0, fs::SeekSet); + + gFont.gCount = (uint16_t)readInt32(); // glyph count in file + readInt32(); // vlw encoder version - discard + gFont.yAdvance = (uint16_t)readInt32(); // Font size in points, not pixels + readInt32(); // discard + gFont.ascent = (uint16_t)readInt32(); // top of "d" + gFont.descent = (uint16_t)readInt32(); // bottom of "p" + + // These next gFont values will be updated when the Metrics are fetched + gFont.maxAscent = gFont.ascent; // Determined from metrics + gFont.maxDescent = gFont.descent; // Determined from metrics + gFont.yAdvance = gFont.ascent + gFont.descent; + gFont.spaceWidth = gFont.yAdvance / 4; // Guess at space width + + fontLoaded = true; + + // Fetch the metrics for each glyph + loadMetrics(gFont.gCount); + + //fontFile.close(); +} + + +/*************************************************************************************** +** Function name: loadMetrics +** Description: Get the metrics for each glyph and store in RAM +*************************************************************************************x*/ +//#define SHOW_ASCENT_DESCENT +void TFT_eSPI::loadMetrics(uint16_t gCount) +{ + uint32_t headerPtr = 24; + uint32_t bitmapPtr = 24 + gCount * 28; + + gUnicode = (uint16_t*)malloc( gCount * 2); // Unicode 16 bit Basic Multilingual Plane (0-FFFF) + gHeight = (uint8_t*)malloc( gCount ); // Height of glyph + gWidth = (uint8_t*)malloc( gCount ); // Width of glyph + gxAdvance = (uint8_t*)malloc( gCount ); // xAdvance - to move x cursor + gdY = (int8_t*)malloc( gCount ); // offset from bitmap top edge from lowest point in any character + gdX = (int8_t*)malloc( gCount ); // offset for bitmap left edge relative to cursor X + gBitmap = (uint32_t*)malloc( gCount * 4); // seek pointer to glyph bitmap in SPIFFS file + +#ifdef SHOW_ASCENT_DESCENT + Serial.print("ascent = "); Serial.println(gFont.ascent); + Serial.print("descent = "); Serial.println(gFont.descent); +#endif + + uint16_t gNum = 0; + fontFile.seek(headerPtr, fs::SeekSet); + while (gNum < gCount) + { + gUnicode[gNum] = (uint16_t)readInt32(); // Unicode code point value + gHeight[gNum] = (uint8_t)readInt32(); // Height of glyph + gWidth[gNum] = (uint8_t)readInt32(); // Width of glyph + gxAdvance[gNum] = (uint8_t)readInt32(); // xAdvance - to move x cursor + gdY[gNum] = (int8_t)readInt32(); // y delta from baseline + gdX[gNum] = (int8_t)readInt32(); // x delta from cursor + readInt32(); // ignored + + // Different glyph sets have different ascent values not always based on "d", so get maximum glyph ascent + if (gdY[gNum] > gFont.maxAscent) + { + // Avoid UTF coding values and characters that tend to give duff values + if (((gUnicode[gNum] > 0x20) && (gUnicode[gNum] < 0xA0) && (gUnicode[gNum] != 0x7F)) || (gUnicode[gNum] > 0xFF)) + { + gFont.maxAscent = gdY[gNum]; +#ifdef SHOW_ASCENT_DESCENT + Serial.print("Unicode = 0x"); Serial.print(gUnicode[gNum], HEX); Serial.print(", maxAscent = "); Serial.println(gFont.maxAscent); +#endif + } + } + + // Different glyph sets have different descent values not always based on "p", so get maximum glyph descent + if (((int16_t)gHeight[gNum] - (int16_t)gdY[gNum]) > gFont.maxDescent) + { + // Avoid UTF coding values and characters that tend to give duff values + if (((gUnicode[gNum] > 0x20) && (gUnicode[gNum] < 0xA0) && (gUnicode[gNum] != 0x7F)) || (gUnicode[gNum] > 0xFF)) + { + gFont.maxDescent = gHeight[gNum] - gdY[gNum]; +#ifdef SHOW_ASCENT_DESCENT + Serial.print("Unicode = 0x"); Serial.print(gUnicode[gNum], HEX); Serial.print(", maxDescent = "); Serial.println(gHeight[gNum] - gdY[gNum]); +#endif + } + } + + gBitmap[gNum] = bitmapPtr; + + headerPtr += 28; + + bitmapPtr += gWidth[gNum] * gHeight[gNum]; + + gNum++; + yield(); + } + + gFont.yAdvance = gFont.maxAscent + gFont.maxDescent; + + gFont.spaceWidth = (gFont.ascent + gFont.descent) * 2/7; // Guess at space width +} + + +/*************************************************************************************** +** Function name: deleteMetrics +** Description: Delete the old glyph metrics and free up the memory +*************************************************************************************x*/ +void TFT_eSPI::unloadFont( void ) +{ + if (gUnicode) + { + free(gUnicode); + gUnicode = NULL; + } + + if (gHeight) + { + free(gHeight); + gHeight = NULL; + } + + if (gWidth) + { + free(gWidth); + gWidth = NULL; + } + + if (gxAdvance) + { + free(gxAdvance); + gxAdvance = NULL; + } + + if (gdY) + { + free(gdY); + gdY = NULL; + } + + if (gdX) + { + free(gdX); + gdX = NULL; + } + + if (gBitmap) + { + free(gBitmap); + gBitmap = NULL; + } + fontFile.close(); + fontLoaded = false; +} + + +/*************************************************************************************** +** Function name: decodeUTF8 +** Description: Line buffer UTF-8 decoder with fall-back to extended ASCII +*************************************************************************************x*/ +#define DECODE_UTF8 +uint16_t TFT_eSPI::decodeUTF8(uint8_t *buf, uint16_t *index, uint16_t remaining) +{ + byte c = buf[(*index)++]; + //Serial.print("Byte from string = 0x"); Serial.println(c, HEX); + +#ifdef DECODE_UTF8 + // 7 bit Unicode + if ((c & 0x80) == 0x00) return c; + + // 11 bit Unicode + if (((c & 0xE0) == 0xC0) && (remaining > 1)) + return ((c & 0x1F)<<6) | (buf[(*index)++]&0x3F); + + // 16 bit Unicode + if (((c & 0xF0) == 0xE0) && (remaining > 2)) + return ((c & 0x0F)<<12) | ((buf[(*index)++]&0x3F)<<6) | ((buf[(*index)++]&0x3F)); + + // 21 bit Unicode not supported so fall-back to extended ASCII + // if ((c & 0xF8) == 0xF0) return c; +#endif + + return c; // fall-back to extended ASCII +} + +/*************************************************************************************** +** Function name: decodeUTF8 +** Description: Serial UTF-8 decoder with fall-back to extended ASCII +*************************************************************************************x*/ +uint16_t TFT_eSPI::decodeUTF8(uint8_t c) +{ + +#ifdef DECODE_UTF8 + if (decoderState == 0) + { + // 7 bit Unicode + if ((c & 0x80) == 0x00) return (uint16_t)c; + + // 11 bit Unicode + if ((c & 0xE0) == 0xC0) + { + decoderBuffer = ((c & 0x1F)<<6); + decoderState = 1; + return 0; + } + + // 16 bit Unicode + if ((c & 0xF0) == 0xE0) + { + decoderBuffer = ((c & 0x0F)<<12); + decoderState = 2; + return 0; + } + // 21 bit Unicode not supported so fall-back to extended ASCII + if ((c & 0xF8) == 0xF0) return (uint16_t)c; + } + else + { + if (decoderState == 2) + { + decoderBuffer |= ((c & 0x3F)<<6); + decoderState--; + return 0; + } + else + { + decoderBuffer |= (c & 0x3F); + decoderState = 0; + return decoderBuffer; + } + } +#endif + + return (uint16_t)c; // fall-back to extended ASCII +} + + + +/*************************************************************************************** +** Function name: alphaBlend +** Description: Blend foreground and background and return new colour +*************************************************************************************x*/ +uint16_t TFT_eSPI::alphaBlend(uint8_t alpha, uint16_t fgc, uint16_t bgc) +{ + // For speed use fixed point maths and rounding to permit a power of 2 division + uint16_t fgR = ((fgc >> 10) & 0x3E) + 1; + uint16_t fgG = ((fgc >> 4) & 0x7E) + 1; + uint16_t fgB = ((fgc << 1) & 0x3E) + 1; + + uint16_t bgR = ((bgc >> 10) & 0x3E) + 1; + uint16_t bgG = ((bgc >> 4) & 0x7E) + 1; + uint16_t bgB = ((bgc << 1) & 0x3E) + 1; + + // Shift right 1 to drop rounding bit and shift right 8 to divide by 256 + uint16_t r = (((fgR * alpha) + (bgR * (255 - alpha))) >> 9); + uint16_t g = (((fgG * alpha) + (bgG * (255 - alpha))) >> 9); + uint16_t b = (((fgB * alpha) + (bgB * (255 - alpha))) >> 9); + + // Combine RGB565 colours into 16 bits + return (r << 11) | (g << 5) | (b << 0); +} + + +/*************************************************************************************** +** Function name: readInt32 +** Description: Get a 32 bit integer from the font file +*************************************************************************************x*/ +uint32_t TFT_eSPI::readInt32(void) +{ + uint32_t val = 0; + val |= fontFile.read() << 24; + val |= fontFile.read() << 16; + val |= fontFile.read() << 8; + val |= fontFile.read(); + return val; +} + + +/*************************************************************************************** +** Function name: getUnicodeIndex +** Description: Get the font file index of a Unicode character +*************************************************************************************x*/ +bool TFT_eSPI::getUnicodeIndex(uint16_t unicode, uint16_t *index) +{ + for (uint16_t i = 0; i < gFont.gCount; i++) + { + if (gUnicode[i] == unicode) + { + *index = i; + return true; + } + } + return false; +} + + +/*************************************************************************************** +** Function name: drawGlyph +** Description: Write a character to the TFT cursor position +*************************************************************************************x*/ +// Expects file to be open +void TFT_eSPI::drawGlyph(uint16_t code) +{ + if (code < 0x21) + { + if (code == 0x20) { + cursor_x += gFont.spaceWidth; + return; + } + + if (code == '\n') { + cursor_x = 0; + cursor_y += gFont.yAdvance; + if (cursor_y >= _height) cursor_y = 0; + return; + } + } + + uint16_t gNum = 0; + bool found = getUnicodeIndex(code, &gNum); + + uint16_t fg = textcolor; + uint16_t bg = textbgcolor; + + if (found) + { + + if (textwrapX && (cursor_x + gWidth[gNum] + gdX[gNum] > _width)) + { + cursor_y += gFont.yAdvance; + cursor_x = 0; + } + if (textwrapY && ((cursor_y + gFont.yAdvance) >= _height)) cursor_y = 0; + if (cursor_x == 0) cursor_x -= gdX[gNum]; + + fontFile.seek(gBitmap[gNum], fs::SeekSet); // This is taking >30ms for a significant position shift + + uint8_t pbuffer[gWidth[gNum]]; + + uint16_t xs = 0; + uint16_t dl = 0; + + for (int y = 0; y < gHeight[gNum]; y++) + { + fontFile.read(pbuffer, gWidth[gNum]); //= width()) { + cursorX = -gdX[i]; + + cursorY += gFont.yAdvance; + if (cursorY + gFont.maxAscent + gFont.descent >= height()) { + cursorX = -gdX[i]; + cursorY = 0; + delay(timeDelay); + timeDelay = td; + fillScreen(textbgcolor); + } + } + + setCursor(cursorX, cursorY); + drawGlyph(gUnicode[i]); + cursorX += gxAdvance[i]; + //cursorX += printToSprite( cursorX, cursorY, i ); + yield(); + } + + delay(timeDelay); + fillScreen(textbgcolor); + //fontFile.close(); + +} diff --git a/Extensions/Smooth_font.h b/Extensions/Smooth_font.h new file mode 100644 index 0000000..6ab09fc --- /dev/null +++ b/Extensions/Smooth_font.h @@ -0,0 +1,54 @@ + // Coded by Bodmer 10/2/18, see license in root directory. + // This is part of the TFT_eSPI class and is associated with anti-aliased font functions + + public: + + // These are for the new antialiased fonts + void loadFont(String fontName); + void unloadFont( void ); + bool getUnicodeIndex(uint16_t unicode, uint16_t *index); + + uint16_t decodeUTF8(uint8_t *buf, uint16_t *index, uint16_t remaining); + uint16_t decodeUTF8(uint8_t c); + + uint16_t alphaBlend(uint8_t alpha, uint16_t fgc, uint16_t bgc); + + void drawGlyph(uint16_t code); + void showFont(uint32_t td); + + fs::File fontFile; + + // This is for the whole font + typedef struct + { + uint16_t gCount; // Total number of characters + uint16_t yAdvance; // Line advance + uint16_t spaceWidth; // Width of a space character + int16_t ascent; // Height of top of 'd' above baseline, other characters may be taller + int16_t descent; // Offset to bottom of 'p', other characters may have a larger descent + uint16_t maxAscent; // Maximum ascent found in font + uint16_t maxDescent; // Maximum descent found in font + } fontMetrics; + +fontMetrics gFont = { 0, 0, 0, 0, 0, 0, 0 }; + + // These are for the metrics for each individual glyph (so we don't need to seek this in file and waste time) + uint16_t* gUnicode = NULL; //UTF-16 code, the codes are searched so do not need to be sequential + uint8_t* gHeight = NULL; //cheight + uint8_t* gWidth = NULL; //cwidth + uint8_t* gxAdvance = NULL; //setWidth + int8_t* gdY = NULL; //topExtent + int8_t* gdX = NULL; //leftExtent + uint32_t* gBitmap = NULL; //file pointer to greyscale bitmap + + String _gFontFilename; + + uint8_t decoderState = 0; // UTF8 decoder state + uint16_t decoderBuffer; // Unicode code-point buffer + + bool fontLoaded = false; // Flags when a anti-aliased font is loaded + + private: + + void loadMetrics(uint16_t gCount); + uint32_t readInt32(void); diff --git a/Extensions/Sprite.cpp b/Extensions/Sprite.cpp new file mode 100644 index 0000000..ab5d368 --- /dev/null +++ b/Extensions/Sprite.cpp @@ -0,0 +1,1409 @@ +/************************************************************************************** +// The following class creates Sprites in RAM, graphics can then be drawn in the Sprite +// and rendered quickly onto the TFT screen. The class inherits the graphics functions +// from the TFT_eSPI class. Some functions are overridden by this class so that the +// graphics are written to the Sprite rather than the TFT. +// Coded by Bodmer, see license file in root folder +***************************************************************************************/ +/*************************************************************************************** +// Color bytes are swapped when writing to RAM, this introduces a small overhead but +// there is a nett performance gain by using swapped bytes. +***************************************************************************************/ + +/*************************************************************************************** +** Function name: TFT_eSprite +** Description: Class constructor +*************************************************************************************x*/ +TFT_eSprite::TFT_eSprite(TFT_eSPI *tft) +{ + _tft = tft; // Pointer to tft class so we can call member functions + + _iwidth = 0; // Initialise width and height to 0 (it does not exist yet) + _iheight = 0; + _bpp16 = true; + _iswapBytes = false; // Do not swap pushImage colour bytes by default + + _created = false; + + _xs = 0; // window bounds for pushColor + _ys = 0; + _xe = 0; + _ye = 0; + + _xptr = 0; // pushColor coordinate + _yptr = 0; + + _icursor_y = _icursor_x = 0; // Text cursor position +} + + +/*************************************************************************************** +** Function name: createSprite +** Description: Create a sprite (bitmap) of defined width and height +*************************************************************************************x*/ +// cast returned value to (uint8_t*) for 8 bit or (uint16_t*) for 16 bit colours +void* TFT_eSprite::createSprite(int16_t w, int16_t h) +{ + + if ( _created ) + { + if ( _bpp16 ) return _img; + return _img8; + } + + if ( w < 1 || h < 1 ) return NULL; + + _iwidth = w; + _iheight = h; + + _icursor_x = 0; + _icursor_y = 0; + + // Default scroll rectangle and gap fill colour + _sx = 0; + _sy = 0; + _sw = w; + _sh = h; + _scolor = TFT_BLACK; + + // Add one extra "off screen" pixel to point out-of-bounds setWindow() coordinates + // this means push/writeColor functions do not need additional bounds checks and + // hence will run faster in normal circumstances. + if(_bpp16) + { + _img = (uint16_t*) calloc(w * h + 1, sizeof(uint16_t)); + if (_img) + { + _created = true; + return _img; + } + } + else + { + _img8 = ( uint8_t*) calloc(w * h + 1, sizeof(uint8_t)); + if (_img8) + { + _created = true; + return _img8; + } + } + + return NULL; +} + + +/*************************************************************************************** +** Function name: setDepth +** Description: Set bits per pixel for colour (8 or 16) +*************************************************************************************x*/ + +void* TFT_eSprite::setColorDepth(int8_t b) +{ + // Can't change an existing sprite's colour depth so delete it + if (_created) + { + if (_bpp16) free(_img); + else free(_img8); + } + + // Now define the new colour depth + if ( b > 8 ) _bpp16 = true; // Bytes per pixel + else _bpp16 = false; + + // If it existed, re-create the sprite with the new colour depth + if (_created) + { + _created = false; + return createSprite(_iwidth, _iheight); + } + + return NULL; +} + + +/*************************************************************************************** +** Function name: deleteSprite +** Description: Delete the sprite to free up memory (RAM) +*************************************************************************************x*/ +void TFT_eSprite::deleteSprite(void) +{ + if (!_created ) return; + + if (_bpp16) free(_img); + else free(_img8); + + _created = false; +} + + +/*************************************************************************************** +** Function name: pushSprite +** Description: Push the sprite to the TFT at x, y +*************************************************************************************x*/ +void TFT_eSprite::pushSprite(int32_t x, int32_t y) +{ + if (!_created ) return; + + if (_bpp16) _tft->pushImage(x, y, _iwidth, _iheight, _img ); + //if (_bpp16) TFT_eSPI::pushImage(x, y, _iwidth, _iheight, _img ); + else _tft->pushImage(x, y, _iwidth, _iheight, _img8); +} + + +/*************************************************************************************** +** Function name: pushSprite +** Description: Push the sprite to the TFT at x, y with transparent colour +*************************************************************************************x*/ +void TFT_eSprite::pushSprite(int32_t x, int32_t y, uint16_t transp) +{ + if (!_created ) return; + + if (_bpp16) _tft->pushImage(x, y, _iwidth, _iheight, _img, transp ); + else + { + transp = (uint8_t)((transp & 0xE000)>>8 | (transp & 0x0700)>>6 | (transp & 0x0018)>>3); + _tft->pushImage(x, y, _iwidth, _iheight, _img8, (uint8_t)transp); + } +} + + +/*************************************************************************************** +** Function name: readPixel +** Description: Read 565 colour of a pixel at defined coordinates +*************************************************************************************x*/ +uint16_t TFT_eSprite::readPixel(int32_t x, int32_t y) +{ + if (!_created ) return 0; + + if (_bpp16) + { + uint16_t color = _img[x + y * _iwidth]; + return (color >> 8) | (color << 8); + } + + uint16_t color = _img8[x + y * _iwidth]; + if (color != 0) + { + uint8_t blue[] = {0, 11, 21, 31}; + color = (color & 0xE0)<<8 | (color & 0xC0)<<5 + | (color & 0x1C)<<6 | (color & 0x1C)<<3 + | blue[color & 0x03]; + } + + return color; +} + + +/*************************************************************************************** +** Function name: pushImage +** Description: push 565 colour image into a defined area of a sprite +*************************************************************************************x*/ +void TFT_eSprite::pushImage(int32_t x, int32_t y, uint32_t w, uint32_t h, uint16_t *data) +{ + if ((x > _iwidth) || (y > _iheight) || (w == 0) || (h == 0) || !_created) return; + + if (_bpp16) + { + for (uint32_t yp = y; yp < y + h; yp++) + { + for (uint32_t xp = x; xp < x + w; xp++) + { + uint16_t color = *data++; + if(!_iswapBytes) color = color<<8 | color>>8; + _img[xp + yp * _iwidth] = color; + } + } + } + else + { + for (uint32_t yp = y; yp < y + h; yp++) + { + for (uint32_t xp = x; xp < x + w; xp++) + { + uint16_t color = *data++; + if(_iswapBytes) color = color<<8 | color>>8; + _img8[xp + yp * _iwidth] = (uint8_t)((color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3); + } + } + } +} + + +/*************************************************************************************** +** Function name: pushImage +** Description: push 565 colour FLASH (PROGMEM) image into a defined area +*************************************************************************************x*/ +void TFT_eSprite::pushImage(int32_t x, int32_t y, uint32_t w, uint32_t h, const uint16_t *data) +{ + if ((x > _iwidth) || (y > _iheight) || (w == 0) || (h == 0) || !_created) return; + + if (_bpp16) + { + for (uint32_t yp = y; yp < y + h; yp++) + { + for (uint32_t xp = x; xp < x + w; xp++) + { + uint16_t color = pgm_read_word(data++); + if(!_iswapBytes) color = color<<8 | color>>8; + _img[xp + yp * _iwidth] = color; + } + } + } + else + { + for (uint32_t yp = y; yp < y + h; yp++) + { + for (uint32_t xp = x; xp < x + w; xp++) + { + uint16_t color = pgm_read_word(data++); + if(_iswapBytes) color = color<<8 | color>>8; + _img8[xp + yp * _iwidth] = (uint8_t)((color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3); + } + } + } +} + + +/*************************************************************************************** +** Function name: setSwapBytes +** Description: Used by 16 bit pushImage() to swap byte order in colours +***************************************************************************************/ +void TFT_eSprite::setSwapBytes(bool swap) +{ + _iswapBytes = swap; +} + + +/*************************************************************************************** +** Function name: getSwapBytes +** Description: Return the swap byte order for colours +***************************************************************************************/ +bool TFT_eSprite::getSwapBytes(void) +{ + return _iswapBytes; +} + + +/*************************************************************************************** +** Function name: setWindow +** Description: Set the bounds of a window for pushColor and writeColor +*************************************************************************************x*/ +void TFT_eSprite::setWindow(int32_t x0, int32_t y0, int32_t x1, int32_t y1) +{ + bool duff_coord = false; + + if (x0 > x1) swap_coord(x0, x1); + if (y0 > y1) swap_coord(y0, y1); + + if (x0 < 0) x0 = 0; + if (x0 >= _iwidth) duff_coord = true; + if (x1 < 0) x1 = 0; + if (x1 >= _iwidth) x1 = _iwidth - 1; + + if (y0 < 0) y0 = 0; + if (y0 >= _iheight) duff_coord = true; + if (y1 < 0) y1 = 0; + if (y1 >= _iheight) y1 = _iheight - 1; + + if (duff_coord) + { // Point to that extra "off screen" pixel + _xs = 0; + _ys = _iheight; + _xe = 0; + _ye = _iheight; + } + else + { + _xs = x0; + _ys = y0; + _xe = x1; + _ye = y1; + } + + _xptr = _xs; + _yptr = _ys; +} + + +/*************************************************************************************** +** Function name: pushColor +** Description: Send a new pixel to the set window +*************************************************************************************x*/ +void TFT_eSprite::pushColor(uint32_t color) +{ + if (!_created ) return; + + // Write the colour to RAM in set window + if (_bpp16) + _img [_xptr + _yptr * _iwidth] = (uint16_t) (color >> 8) | (color << 8); + + else + _img8[_xptr + _yptr * _iwidth] = (uint8_t )((color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3); + + // Increment x + _xptr++; + + // Wrap on x and y to start, increment y if needed + if (_xptr > _xe) + { + _xptr = _xs; + _yptr++; + if (_yptr > _ye) _yptr = _ys; + } + +} + + +/*************************************************************************************** +** Function name: pushColor +** Description: Send a "len" new pixels to the set window +*************************************************************************************x*/ +void TFT_eSprite::pushColor(uint32_t color, uint16_t len) +{ + if (!_created ) return; + + uint16_t pixelColor; + if (_bpp16) + pixelColor = (uint16_t) (color >> 8) | (color << 8); + + else + pixelColor = (color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3; + + while(len--) writeColor(pixelColor); +} + + +/*************************************************************************************** +** Function name: writeColor +** Description: Write a pixel with pre-formatted colour to the set window +*************************************************************************************x*/ +void TFT_eSprite::writeColor(uint16_t color) +{ + if (!_created ) return; + + // Write 16 bit RGB 565 encoded colour to RAM + if (_bpp16) _img [_xptr + _yptr * _iwidth] = color; + + // Write 8 bit RGB 332 encoded colour to RAM + else _img8[_xptr + _yptr * _iwidth] = (uint8_t) color; + + // Increment x + _xptr++; + + // Wrap on x and y to start, increment y if needed + if (_xptr > _xe) + { + _xptr = _xs; + _yptr++; + if (_yptr > _ye) _yptr = _ys; + } +} + + +/*************************************************************************************** +** Function name: setScrollRect +** Description: Set scroll area within the sprite and the gap fill colour +*************************************************************************************x*/ +void TFT_eSprite::setScrollRect(int32_t x, int32_t y, uint32_t w, uint32_t h, uint16_t color) +{ + if ((x >= _iwidth) || (y >= _iheight) || !_created ) return; + + if (x < 0) x = 0; + if (y < 0) y = 0; + + if ((x + w) > _iwidth ) w = _iwidth - x; + if ((y + h) > _iheight) h = _iheight - y; + + if ( w < 1 || h < 1) return; + + _sx = x; + _sy = y; + _sw = w; + _sh = h; + + _scolor = color; +} + + +/*************************************************************************************** +** Function name: scroll +** Description: Scroll dx,dy pixels, positive right,down, negative left,up +*************************************************************************************x*/ +void TFT_eSprite::scroll(int16_t dx, int16_t dy) +{ + if (abs(dx) >= _sw || abs(dy) >= _sh) + { + fillRect (_sx, _sy, _sw, _sh, _scolor); + return; + } + + // Fetch the scroll area width and height set by setScrollRect() + uint32_t w = _sw - abs(dx); // line width to copy + uint32_t h = _sh - abs(dy); // lines to copy + int32_t iw = _iwidth; // width of sprite + + // Fetch the x,y origin set by setScrollRect() + uint32_t tx = _sx; // to x + uint32_t fx = _sx; // from x + uint32_t ty = _sy; // to y + uint32_t fy = _sy; // from y + + // Adjust for x delta + if (dx <= 0) fx -= dx; + else tx += dx; + + // Adjust for y delta + if (dy <= 0) fy -= dy; + else + { // Scrolling down so start copy from bottom + ty = ty + _sh - 1; // "To" pointer + iw = -iw; // Pointer moves backwards + fy = ty - dy; // "From" pointer + } + + // Calculate "from y" and "to y" pointers in RAM + uint32_t fyp = fx + fy * _iwidth; + uint32_t typ = tx + ty * _iwidth; + + // Now move the pixels in RAM + if (_bpp16) + { + while (h--) + { // move pixel lines (to, from, byte count) + memmove( _img + typ, _img + fyp, w<<1); + typ += iw; + fyp += iw; + } + } + else + { + while (h--) + { // move pixel lines (to, from, byte count) + memmove( _img8 + typ, _img8 + fyp, w); + typ += iw; + fyp += iw; + } + } + + // Fill the gap left by the scrolling + if (dx > 0) fillRect(_sx, _sy, dx, _sh, _scolor); + if (dx < 0) fillRect(_sx + _sw + dx, _sy, -dx, _sh, _scolor); + if (dy > 0) fillRect(_sx, _sy, _sw, dy, _scolor); + if (dy < 0) fillRect(_sx, _sy + _sh + dy, _sw, -dy, _scolor); +} + + +/*************************************************************************************** +** Function name: fillSprite +** Description: Fill the whole sprite with defined colour +*************************************************************************************x*/ +void TFT_eSprite::fillSprite(uint32_t color) +{ + if (!_created ) return; + + // Use memset if possible as it is super fast + if(( (uint8_t)color == (uint8_t)(color>>8) ) && _bpp16) + memset(_img, (uint8_t)color, _iwidth * _iheight * 2); + else if (!_bpp16) + { + color = (color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3; + memset(_img8, (uint8_t)color, _iwidth * _iheight); + } + + else fillRect(0, 0, _iwidth, _iheight, color); +} + + +/*************************************************************************************** +** Function name: setCursor +** Description: Set the sprite text cursor x,y position +*************************************************************************************x*/ +void TFT_eSprite::setCursor(int16_t x, int16_t y) +{ + _icursor_x = x; + _icursor_y = y; +} + + +/*************************************************************************************** +** Function name: width +** Description: Return the width of sprite +*************************************************************************************x*/ +// Return the size of the display +int16_t TFT_eSprite::width(void) +{ + if (!_created ) return 0; + return _iwidth; +} + + +/*************************************************************************************** +** Function name: height +** Description: Return the height of sprite +*************************************************************************************x*/ +int16_t TFT_eSprite::height(void) +{ + if (!_created ) return 0; + return _iheight; +} + + +/*************************************************************************************** +** Function name: drawPixel +** Description: push a single pixel at an arbitrary position +*************************************************************************************x*/ +void TFT_eSprite::drawPixel(uint32_t x, uint32_t y, uint32_t color) +{ + // x and y are unsigned so that -ve coordinates turn into large positive ones + // this make bounds checking a bit faster + if ((x >= _iwidth) || (y >= _iheight) || !_created) return; + + if (_bpp16) + { + color = (color >> 8) | (color << 8); + _img[x+y*_iwidth] = (uint16_t) color; + } + else + { + _img8[x+y*_iwidth] = (uint8_t)((color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3); + } +} + + +/*************************************************************************************** +** Function name: drawLine +** Description: draw a line between 2 arbitrary points +*************************************************************************************x*/ +void TFT_eSprite::drawLine(int32_t x0, int32_t y0, int32_t x1, int32_t y1, uint32_t color) +{ + if (!_created ) return; + + boolean steep = abs(y1 - y0) > abs(x1 - x0); + if (steep) { + swap_coord(x0, y0); + swap_coord(x1, y1); + } + + if (x0 > x1) { + swap_coord(x0, x1); + swap_coord(y0, y1); + } + + int32_t dx = x1 - x0, dy = abs(y1 - y0);; + + int32_t err = dx >> 1, ystep = -1, xs = x0, dlen = 0; + + if (y0 < y1) ystep = 1; + + // Split into steep and not steep for FastH/V separation + if (steep) { + for (; x0 <= x1; x0++) { + dlen++; + err -= dy; + if (err < 0) { + err += dx; + if (dlen == 1) drawPixel(y0, xs, color); + else drawFastVLine(y0, xs, dlen, color); + dlen = 0; y0 += ystep; xs = x0 + 1; + } + } + if (dlen) drawFastVLine(y0, xs, dlen, color); + } + else + { + for (; x0 <= x1; x0++) { + dlen++; + err -= dy; + if (err < 0) { + err += dx; + if (dlen == 1) drawPixel(xs, y0, color); + else drawFastHLine(xs, y0, dlen, color); + dlen = 0; y0 += ystep; xs = x0 + 1; + } + } + if (dlen) drawFastHLine(xs, y0, dlen, color); + } +} + + +/*************************************************************************************** +** Function name: drawFastVLine +** Description: draw a vertical line +*************************************************************************************x*/ +void TFT_eSprite::drawFastVLine(int32_t x, int32_t y, int32_t h, uint32_t color) +{ + + if ((x < 0) || (x >= _iwidth) || (y >= _iheight) || !_created) return; + + if (y < 0) { h += y; y = 0; } + + if ((y + h) > _iheight) h = _iheight - y; + + if (h < 1) return; + + if (_bpp16) + { + color = (color >> 8) | (color << 8); + int32_t yp = x + _iwidth * y; + while (h--) {_img[yp] = (uint16_t) color; yp += _iwidth;} + } + else + { + color = (color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3; + while (h--) _img8[x + _iwidth * y++] = (uint8_t) color; + } +} + + +/*************************************************************************************** +** Function name: drawFastHLine +** Description: draw a horizontal line +*************************************************************************************x*/ +void TFT_eSprite::drawFastHLine(int32_t x, int32_t y, int32_t w, uint32_t color) +{ + + if ((y < 0) || (x >= _iwidth) || (y >= _iheight) || !_created) return; + + if (x < 0) { w += x; x = 0; } + + if ((x + w) > _iwidth) w = _iwidth - x; + + if (w < 1) return; + + if (_bpp16) + { + color = (color >> 8) | (color << 8); + while (w--) _img[_iwidth * y + x++] = (uint16_t) color; + } + else + { + color = (color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3; + memset(_img8+_iwidth * y + x, (uint8_t)color, w); + } +} + + +/*************************************************************************************** +** Function name: fillRect +** Description: draw a filled rectangle +*************************************************************************************x*/ +void TFT_eSprite::fillRect(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color) +{ + if (!_created ) return; + + if (x < 0) { w += x; x = 0; } + + if ((x < 0) || (y < 0) || (x >= _iwidth) || (y >= _iheight)) return; + if ((x + w) > _iwidth) w = _iwidth - x; + if ((y + h) > _iheight) h = _iheight - y; + if ((w < 1) || (h < 1)) return; + + int32_t yp = _iwidth * y + x; + + if (_bpp16) + { + color = (color >> 8) | (color << 8); + uint32_t iw = w; + int32_t ys = yp; + if(h--) {while (iw--) _img[yp++] = (uint16_t) color;} + yp = ys; + while (h--) + { + yp += _iwidth; + memcpy( _img+yp, _img+ys, w<<1); + } + } + else + { + color = (color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3; + while (h--) + { + memset(_img8 + yp, (uint8_t)color, w); + yp += _iwidth; + } + } +} + + +/*************************************************************************************** +** Function name: write +** Description: draw characters piped through serial stream +*************************************************************************************x*/ +size_t TFT_eSprite::write(uint8_t utf8) +{ + if (utf8 == '\r') return 1; + +#ifdef SMOOTH_FONT + if(fontLoaded) + { + uint16_t unicode = decodeUTF8(utf8); + if (unicode < 32 && utf8 != '\n') return 0; + + fontFile = SPIFFS.open( _gFontFilename, "r" ); + + if(!fontFile) + { + fontLoaded = false; + return 0; + } + + drawGlyph(unicode); + fontFile.close(); + return 0; + } +#endif + + if (!_created ) return 0; + + + uint8_t uniCode = utf8; // Work with a copy + if (utf8 == '\n') uniCode+=22; // Make it a valid space character to stop errors + else if (utf8 < 32) return 0; + + uint16_t width = 0; + uint16_t height = 0; + +//vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv DEBUG vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv + //Serial.print((uint8_t) uniCode); // Debug line sends all printed TFT text to serial port + //Serial.println(uniCode, HEX); // Debug line sends all printed TFT text to serial port + //delay(5); // Debug optional wait for serial port to flush through +//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ DEBUG ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< +#ifdef LOAD_GFXFF + if(!gfxFont) { +#endif +//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + +#ifdef LOAD_FONT2 + if (textfont == 2) + { + if (utf8 > 127) return 0; + // This is 20us faster than using the fontdata structure (0.443ms per character instead of 0.465ms) + width = pgm_read_byte(widtbl_f16 + uniCode-32); + height = chr_hgt_f16; + // Font 2 is rendered in whole byte widths so we must allow for this + width = (width + 6) / 8; // Width in whole bytes for font 2, should be + 7 but must allow for font width change + width = width * 8; // Width converted back to pixles + } + #ifdef LOAD_RLE + else + #endif +#endif + +#ifdef LOAD_RLE + { + if ((textfont>2) && (textfont<9)) + { + if (utf8 > 127) return 0; + // Uses the fontinfo struct array to avoid lots of 'if' or 'switch' statements + // A tad slower than above but this is not significant and is more convenient for the RLE fonts + width = pgm_read_byte( (uint8_t *)pgm_read_dword( &(fontdata[textfont].widthtbl ) ) + uniCode-32 ); + height= pgm_read_byte( &fontdata[textfont].height ); + } + } +#endif + +#ifdef LOAD_GLCD + if (textfont==1) + { + width = 6; + height = 8; + } +#else + if (textfont==1) return 0; +#endif + + height = height * textsize; + + if (utf8 == '\n') + { + _icursor_y += height; + _icursor_x = 0; + } + else + { + if (textwrapX && (_icursor_x + width * textsize > _iwidth)) + { + _icursor_y += height; + _icursor_x = 0; + } + if (textwrapY && (_icursor_y >= _iheight)) _icursor_y = 0; + _icursor_x += drawChar(uniCode, _icursor_x, _icursor_y, textfont); + } + +//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< +#ifdef LOAD_GFXFF + } // Custom GFX font + else + { + + if(utf8 == '\n') { + _icursor_x = 0; + _icursor_y += (int16_t)textsize * (uint8_t)pgm_read_byte(&gfxFont->yAdvance); + } else { + if (uniCode > (uint8_t)pgm_read_byte(&gfxFont->last )) return 0; + if (uniCode < (uint8_t)pgm_read_byte(&gfxFont->first)) return 0; + + uint8_t c2 = uniCode - pgm_read_byte(&gfxFont->first); + GFXglyph *glyph = &(((GFXglyph *)pgm_read_dword(&gfxFont->glyph))[c2]); + uint8_t w = pgm_read_byte(&glyph->width), + h = pgm_read_byte(&glyph->height); + if((w > 0) && (h > 0)) { // Is there an associated bitmap? + int16_t xo = (int8_t)pgm_read_byte(&glyph->xOffset); + if(textwrapX && ((_icursor_x + textsize * (xo + w)) > _iwidth)) { + // Drawing character would go off right edge; wrap to new line + _icursor_x = 0; + _icursor_y += (int16_t)textsize * (uint8_t)pgm_read_byte(&gfxFont->yAdvance); + } + if (textwrapY && (_icursor_y >= _iheight)) _icursor_y = 0; + drawChar(_icursor_x, _icursor_y, uniCode, textcolor, textbgcolor, textsize); + } + _icursor_x += pgm_read_byte(&glyph->xAdvance) * (int16_t)textsize; + } + } +#endif // LOAD_GFXFF +//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + + return 1; +} + + +/*************************************************************************************** +** Function name: drawChar +** Description: draw a single character in the Adafruit GLCD or freefont +*************************************************************************************x*/ +void TFT_eSprite::drawChar(int32_t x, int32_t y, unsigned char c, uint32_t color, uint32_t bg, uint8_t size) +{ + if (!_created ) return; + + if ((x >= _iwidth) || // Clip right + (y >= _iheight) || // Clip bottom + ((x + 6 * size - 1) < 0) || // Clip left + ((y + 8 * size - 1) < 0)) // Clip top + return; + +#ifdef LOAD_GLCD +//>>>>>>>>>>>>>>>>>> +#ifdef LOAD_GFXFF + if(!gfxFont) { // 'Classic' built-in font +#endif +//>>>>>>>>>>>>>>>>>> + + boolean fillbg = (bg != color); + + if ((size==1) && fillbg) + { + uint8_t column[6]; + uint8_t mask = 0x1; + + for (int8_t i = 0; i < 5; i++ ) column[i] = pgm_read_byte(font + (c * 5) + i); + column[5] = 0; + + int8_t j, k; + for (j = 0; j < 8; j++) { + for (k = 0; k < 5; k++ ) { + if (column[k] & mask) { + drawPixel(x + k, y + j, color); + } + else { + drawPixel(x + k, y + j, bg); + } + } + + mask <<= 1; + + drawPixel(x + k, y + j, bg); + } + } + else + { + for (int8_t i = 0; i < 6; i++ ) { + uint8_t line; + if (i == 5) + line = 0x0; + else + line = pgm_read_byte(font + (c * 5) + i); + + if (size == 1) // default size + { + for (int8_t j = 0; j < 8; j++) { + if (line & 0x1) drawPixel(x + i, y + j, color); + line >>= 1; + } + } + else { // big size + for (int8_t j = 0; j < 8; j++) { + if (line & 0x1) fillRect(x + (i * size), y + (j * size), size, size, color); + else if (fillbg) fillRect(x + i * size, y + j * size, size, size, bg); + line >>= 1; + } + } + } + } + +//>>>>>>>>>>>>>>>>>>>>>>>>>>> +#ifdef LOAD_GFXFF + } else { // Custom font +#endif +//>>>>>>>>>>>>>>>>>>>>>>>>>>> +#endif // LOAD_GLCD + +#ifdef LOAD_GFXFF + // Filter out bad characters not present in font + if ((c >= (uint8_t)pgm_read_byte(&gfxFont->first)) && (c <= (uint8_t)pgm_read_byte(&gfxFont->last ))) + { +//>>>>>>>>>>>>>>>>>>>>>>>>>>> + + c -= pgm_read_byte(&gfxFont->first); + GFXglyph *glyph = &(((GFXglyph *)pgm_read_dword(&gfxFont->glyph))[c]); + uint8_t *bitmap = (uint8_t *)pgm_read_dword(&gfxFont->bitmap); + + uint16_t bo = pgm_read_word(&glyph->bitmapOffset); + uint8_t w = pgm_read_byte(&glyph->width), + h = pgm_read_byte(&glyph->height), + xa = pgm_read_byte(&glyph->xAdvance); + int8_t xo = pgm_read_byte(&glyph->xOffset), + yo = pgm_read_byte(&glyph->yOffset); + uint8_t xx, yy, bits, bit=0; + int16_t xo16 = 0, yo16 = 0; + + if(size > 1) { + xo16 = xo; + yo16 = yo; + } + + uint16_t hpc = 0; // Horizontal foreground pixel count + for(yy=0; yy>= 1; + } + // Draw pixels for this line as we are about to increment yy + if (hpc) { + if(size == 1) drawFastHLine(x+xo+xx-hpc, y+yo+yy, hpc, color); + else fillRect(x+(xo16+xx-hpc)*size, y+(yo16+yy)*size, size*hpc, size, color); + hpc=0; + } + } + } +#endif + + +#ifdef LOAD_GLCD + #ifdef LOAD_GFXFF + } // End classic vs custom font + #endif +#endif + +} + + +/*************************************************************************************** +** Function name: drawChar +** Description: draw a unicode onto the screen +*************************************************************************************x*/ +int16_t TFT_eSprite::drawChar(unsigned int uniCode, int x, int y) +{ + return drawChar(uniCode, x, y, textfont); +} + +int16_t TFT_eSprite::drawChar(unsigned int uniCode, int x, int y, int font) +{ + if (!_created ) return 0; + + if (font==1) + { +#ifdef LOAD_GLCD + #ifndef LOAD_GFXFF + drawChar(x, y, uniCode, textcolor, textbgcolor, textsize); + return 6 * textsize; + #endif +#else + #ifndef LOAD_GFXFF + return 0; + #endif +#endif + +#ifdef LOAD_GFXFF + drawChar(x, y, uniCode, textcolor, textbgcolor, textsize); + if(!gfxFont) { // 'Classic' built-in font + #ifdef LOAD_GLCD + return 6 * textsize; + #else + return 0; + #endif + } + else + { + if((uniCode >= pgm_read_byte(&gfxFont->first)) && (uniCode <= pgm_read_byte(&gfxFont->last) )) + { + uint8_t c2 = uniCode - pgm_read_byte(&gfxFont->first); + GFXglyph *glyph = &(((GFXglyph *)pgm_read_dword(&gfxFont->glyph))[c2]); + return pgm_read_byte(&glyph->xAdvance) * textsize; + } + else + { + return 0; + } + } +#endif + } + + if ((font>1) && (font<9) && ((uniCode < 32) || (uniCode > 127))) return 0; + + int width = 0; + int height = 0; + uint32_t flash_address = 0; + uniCode -= 32; + +#ifdef LOAD_FONT2 + if (font == 2) + { + // This is faster than using the fontdata structure + flash_address = pgm_read_dword(&chrtbl_f16[uniCode]); + width = pgm_read_byte(widtbl_f16 + uniCode); + height = chr_hgt_f16; + } + #ifdef LOAD_RLE + else + #endif +#endif + +#ifdef LOAD_RLE + { + if ((font>2) && (font<9)) + { + // This is slower than above but is more convenient for the RLE fonts + flash_address = pgm_read_dword( pgm_read_dword( &(fontdata[font].chartbl ) ) + uniCode*sizeof(void *) ); + width = pgm_read_byte( (uint8_t *)pgm_read_dword( &(fontdata[font].widthtbl ) ) + uniCode ); + height= pgm_read_byte( &fontdata[font].height ); + } + } +#endif + + int w = width; + int pX = 0; + int pY = y; + uint8_t line = 0; + +#ifdef LOAD_FONT2 // chop out code if we do not need it + if (font == 2) { + w = w + 6; // Should be + 7 but we need to compensate for width increment + w = w / 8; + if (x + width * textsize >= _iwidth) return width * textsize ; + + for (int i = 0; i < height; i++) + { + if (textcolor != textbgcolor) fillRect(x, pY, width * textsize, textsize, textbgcolor); + + for (int k = 0; k < w; k++) + { + line = pgm_read_byte((uint8_t *)flash_address + w * i + k); + if (line) { + if (textsize == 1) { + pX = x + k * 8; + if (line & 0x80) drawPixel(pX, pY, textcolor); + if (line & 0x40) drawPixel(pX + 1, pY, textcolor); + if (line & 0x20) drawPixel(pX + 2, pY, textcolor); + if (line & 0x10) drawPixel(pX + 3, pY, textcolor); + if (line & 0x08) drawPixel(pX + 4, pY, textcolor); + if (line & 0x04) drawPixel(pX + 5, pY, textcolor); + if (line & 0x02) drawPixel(pX + 6, pY, textcolor); + if (line & 0x01) drawPixel(pX + 7, pY, textcolor); + } + else { + pX = x + k * 8 * textsize; + if (line & 0x80) fillRect(pX, pY, textsize, textsize, textcolor); + if (line & 0x40) fillRect(pX + textsize, pY, textsize, textsize, textcolor); + if (line & 0x20) fillRect(pX + 2 * textsize, pY, textsize, textsize, textcolor); + if (line & 0x10) fillRect(pX + 3 * textsize, pY, textsize, textsize, textcolor); + if (line & 0x08) fillRect(pX + 4 * textsize, pY, textsize, textsize, textcolor); + if (line & 0x04) fillRect(pX + 5 * textsize, pY, textsize, textsize, textcolor); + if (line & 0x02) fillRect(pX + 6 * textsize, pY, textsize, textsize, textcolor); + if (line & 0x01) fillRect(pX + 7 * textsize, pY, textsize, textsize, textcolor); + } + } + } + pY += textsize; + } + } + + #ifdef LOAD_RLE + else + #endif +#endif //FONT2 + +#ifdef LOAD_RLE //674 bytes of code + // Font is not 2 and hence is RLE encoded + { + w *= height; // Now w is total number of pixels in the character + + if (textcolor != textbgcolor) fillRect(x, pY, width * textsize, textsize * height, textbgcolor); + int16_t color; + if (_bpp16) color = (textcolor >> 8) | (textcolor << 8); + else color = ((textcolor & 0xE000)>>8 | (textcolor & 0x0700)>>6 | (textcolor & 0x0018)>>3); + int px = 0, py = pY; // To hold character block start and end column and row values + int pc = 0; // Pixel count + uint8_t np = textsize * textsize; // Number of pixels in a drawn pixel + uint8_t tnp = 0; // Temporary copy of np for while loop + uint8_t ts = textsize - 1; // Temporary copy of textsize + // 16 bit pixel count so maximum font size is equivalent to 180x180 pixels in area + // w is total number of pixels to plot to fill character block + while (pc < w) + { + line = pgm_read_byte((uint8_t *)flash_address); + flash_address++; // 20 bytes smaller by incrementing here + if (line & 0x80) { + line &= 0x7F; + line++; + if (ts) { + px = x + textsize * (pc % width); // Keep these px and py calculations outside the loop as they are slow + py = y + textsize * (pc / width); + } + else { + px = x + pc % width; // Keep these px and py calculations outside the loop as they are slow + py = y + pc / width; + } + while (line--) { + pc++; + setWindow(px, py, px + ts, py + ts); + if (ts) { tnp = np; while (tnp--) writeColor(color); } + else writeColor(color); + + px += textsize; + + if (px >= (x + width * textsize)) + { + px = x; + py += textsize; + } + } + } + else { + line++; + pc += line; + } + } + } + // End of RLE font rendering +#endif + return width * textsize; // x + +} + +#ifdef SMOOTH_FONT +/*************************************************************************************** +** Function name: drawGlyph +** Description: Write a character to the sprite cursor position +*************************************************************************************x*/ +void TFT_eSprite::drawGlyph(uint16_t code) +{ + if (code < 0x21) + { + if (code == 0x20) { + if (_created) _icursor_x += _tft->gFont.spaceWidth; + else _tft->cursor_x += _tft->gFont.spaceWidth; + return; + } + + if (code == '\n') { + if (_created) + { + _icursor_x = 0; + _icursor_y += _tft->gFont.yAdvance; + if (_icursor_y >= _height) _icursor_y = 0; + return; + } + else + { + cursor_x = 0; + cursor_y += gFont.yAdvance; + if (cursor_y >= _height) cursor_y = 0; + return; + } + } + } + + uint16_t gNum = 0; + bool found = _tft->getUnicodeIndex(code, &gNum); + + uint16_t fg = _tft->textcolor; + uint16_t bg = _tft->textbgcolor; + + if (found) + { + + bool newSprite = !_created; + + if (newSprite) + { + createSprite(_tft->gWidth[gNum], _tft->gFont.yAdvance); + if(bg) fillSprite(bg); + _icursor_x = -_tft->gdX[gNum]; + _icursor_y = 0; + } + + fontFile.seek(_tft->gBitmap[gNum], fs::SeekSet); // This is slow for a significant position shift! + + uint8_t pbuffer[_tft->gWidth[gNum]]; + + uint16_t xs = 0; + uint16_t dl = 0; + + for (int y = 0; y < _tft->gHeight[gNum]; y++) + { + fontFile.read(pbuffer, _tft->gWidth[gNum]); + for (int x = 0; x < _tft->gWidth[gNum]; x++) + { + uint8_t pixel = pbuffer[x]; + if (pixel) + { + if (pixel != 0xFF) + { + if (dl) { drawFastHLine( xs, y + _icursor_y + _tft->gFont.maxAscent - _tft->gdY[gNum], dl, fg); dl = 0; } + drawPixel(x + _icursor_x + _tft->gdX[gNum], y + _icursor_y + _tft->gFont.maxAscent - _tft->gdY[gNum], alphaBlend(pixel, fg, bg)); + } + else + { + if (dl==0) xs = x + _icursor_x + _tft->gdX[gNum]; + dl++; + } + } + else + { + if (dl) { drawFastHLine( xs, y + _icursor_y + _tft->gFont.maxAscent - _tft->gdY[gNum], dl, fg); dl = 0; } + } + } + if (dl) { drawFastHLine( xs, y + _icursor_y + _tft->gFont.maxAscent - _tft->gdY[gNum], dl, fg); dl = 0; } + } + + if (newSprite) + { + pushSprite(_tft->cursor_x + _tft->gdX[gNum], _tft->cursor_y, bg); + deleteSprite(); + _tft->cursor_x += _tft->gxAdvance[gNum]; + } + else _icursor_x += _tft->gxAdvance[gNum]; + } + else + { + // Not a Unicode in font so draw a rectangle and move on cursor + drawRect(_icursor_x, _icursor_y + _tft->gFont.maxAscent - _tft->gFont.ascent, _tft->gFont.spaceWidth, _tft->gFont.ascent, fg); + _icursor_x += _tft->gFont.spaceWidth + 1; + } +} + + +/*************************************************************************************** +** Function name: printToSprite +** Description: Write a string to the sprite cursor position +*************************************************************************************x*/ +void TFT_eSprite::printToSprite(String string) +{ + if(!_tft->fontLoaded) return; + int16_t len = string.length(); + char cbuffer[len + 1]; // Add 1 for the null + string.toCharArray(cbuffer, len + 1); // Add 1 for the null, otherwise characters get dropped + printToSprite(cbuffer, len); +} + + +/*************************************************************************************** +** Function name: printToSprite +** Description: Write a string to the sprite cursor position +*************************************************************************************x*/ +void TFT_eSprite::printToSprite(char *cbuffer, int len) //String string) +{ + if(!_tft->fontLoaded) return; + + fontFile = SPIFFS.open( _tft->_gFontFilename, "r" ); + + if(!fontFile) + { + _tft->fontLoaded = false; + return; + } + + uint16_t n = 0; + bool newSprite = !_created; + + if (newSprite) + { + int16_t sWidth = 0; + uint16_t index = 0; + + while (n < len) + { + uint16_t unicode = decodeUTF8((uint8_t*)cbuffer, &n, len - n); + if (_tft->getUnicodeIndex(unicode, &index)) + { + if (n == 0) sWidth -= _tft->gdX[index]; + if (n == len-1) sWidth += ( _tft->gWidth[index] + _tft->gdX[index]); + else sWidth += _tft->gxAdvance[index]; + } + else sWidth += _tft->gFont.spaceWidth + 1; + } + + createSprite(sWidth, _tft->gFont.yAdvance); + uint16_t transparent = TFT_BLACK; + + if (_tft->textbgcolor != TFT_BLACK) fillSprite(_tft->textbgcolor); + } + + n = 0; + + while (n < len) + { + uint16_t unicode = decodeUTF8((uint8_t*)cbuffer, &n, len - n); + //Serial.print("Decoded Unicode = 0x");Serial.println(unicode,HEX); + //Serial.print("n = ");Serial.println(n); + drawGlyph(unicode); + } + + if (newSprite) + { + pushSprite(_tft->cursor_x, _tft->cursor_y); + deleteSprite(); + } + + fontFile.close(); +} + + +/*************************************************************************************** +** Function name: printToSprite +** Description: Print character in a Sprite, create sprite if needed +*************************************************************************************x*/ +int16_t TFT_eSprite::printToSprite(int16_t x, int16_t y, uint16_t index) +{ + bool newSprite = !_created; + int16_t sWidth = _tft->gWidth[index]; + + if (newSprite) + { + createSprite(sWidth, _tft->gFont.yAdvance); + uint16_t transparent = TFT_BLACK; + if (_tft->textbgcolor != TFT_BLACK) fillSprite(_tft->textbgcolor); + + drawGlyph(_tft->gUnicode[index]); + + pushSprite(x + _tft->gdX[index], y, _tft->textbgcolor); + deleteSprite(); + } + + else drawGlyph(_tft->gUnicode[index]); + + return _tft->gxAdvance[index]; +} +#endif diff --git a/Extensions/Sprite.h b/Extensions/Sprite.h new file mode 100644 index 0000000..85945ff --- /dev/null +++ b/Extensions/Sprite.h @@ -0,0 +1,111 @@ +/*************************************************************************************** +// The following class creates Sprites in RAM, graphics can then be drawn in the Sprite +// and rendered quickly onto the TFT screen. The class inherits the graphics functions +// from the TFT_eSPI class. Some functions are overridden by this class so that the +// graphics are written to the Sprite rather than the TFT. +***************************************************************************************/ + +class TFT_eSprite : public TFT_eSPI { + + public: + + TFT_eSprite(TFT_eSPI *tft); + + // Create a sprite of width x height pixels, return a pointer to the RAM area + // Sketch can cast returned value to (uint16_t*) for 16 bit depth if needed + // RAM required is 1 byte per pixel for 8 bit colour depth, 2 bytes for 16 bit + void* createSprite(int16_t width, int16_t height); + + // Delete the sprite to free up the RAM + void deleteSprite(void); + + // Set the colour depth to 8 or 16 bits. Can be used to change depth an existing + // sprite, but clears it to black, returns a new pointer if sprite is re-created. + void* setColorDepth(int8_t b); + + void drawPixel(uint32_t x, uint32_t y, uint32_t color); + + void drawChar(int32_t x, int32_t y, unsigned char c, uint32_t color, uint32_t bg, uint8_t size), + + fillSprite(uint32_t color), + + // Define a window to push 16 bit colour pixels into is a raster order + // Colours are converted to 8 bit if depth is set to 8 + setWindow(int32_t x0, int32_t y0, int32_t x1, int32_t y1), + pushColor(uint32_t color), + pushColor(uint32_t color, uint16_t len), + // Push a pixel preformatted as a 8 or 16 bit colour (avoids conversion overhead) + writeColor(uint16_t color), + + // Set the scroll zone, top left corner at x,y with defined width and height + // The colour (optional, black is default) is used to fill the gap after the scroll + setScrollRect(int32_t x, int32_t y, uint32_t w, uint32_t h, uint16_t color = TFT_BLACK), + // Scroll the defined zone dx,dy pixels. Negative values left,up, positive right,down + // dy is optional (default is then no up/down scroll). + // The sprite coordinate frame does not move because pixels are moved + scroll(int16_t dx, int16_t dy = 0), + + drawLine(int32_t x0, int32_t y0, int32_t x1, int32_t y1, uint32_t color), + drawFastVLine(int32_t x, int32_t y, int32_t h, uint32_t color), + drawFastHLine(int32_t x, int32_t y, int32_t w, uint32_t color), + + fillRect(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color), + + // Set the sprite text cursor position for print class (does not change the TFT screen cursor) + setCursor(int16_t x, int16_t y); + + // Read the colour of a pixel at x,y and return value in 565 format + uint16_t readPixel(int32_t x0, int32_t y0); + + // Write an image (colour bitmap) to the sprite + void pushImage(int32_t x0, int32_t y0, uint32_t w, uint32_t h, uint16_t *data); + void pushImage(int32_t x0, int32_t y0, uint32_t w, uint32_t h, const uint16_t *data); + + // Swap the byte order for pushImage() - corrects different image endianness + void setSwapBytes(bool swap); + bool getSwapBytes(void); + + // Push the sprite to the TFT screen, this fn calls pushImage() in the TFT class. + // Optionally a "transparent" colour can be defined, pixels of that colour will not be rendered + void pushSprite(int32_t x, int32_t y); + void pushSprite(int32_t x, int32_t y, uint16_t transparent); + + int16_t drawChar(unsigned int uniCode, int x, int y, int font), + drawChar(unsigned int uniCode, int x, int y); + + // Return the width and height of the sprite + int16_t width(void), + height(void); + + // Used by print class to print text to cursor position + size_t write(uint8_t); + + // Functions associated with anti-aliased fonts + void drawGlyph(uint16_t code); + void printToSprite(String string); + void printToSprite(char *cbuffer, int len); + int16_t printToSprite(int16_t x, int16_t y, uint16_t index); + + private: + + TFT_eSPI *_tft; + + protected: + + uint16_t *_img; // pointer to 16 bit sprite + uint8_t *_img8; // pointer to 8 bit sprite + bool _created, _bpp16; // created and bits per pixel depth flags + + bool _gFont = false; + + int32_t _icursor_x, _icursor_y; + int32_t _xs, _ys, _xe, _ye, _xptr, _yptr; // for setWindow + int32_t _sx, _sy; // x,y for scroll zone + uint32_t _sw, _sh; // w,h for scroll zone + uint32_t _scolor; // gap fill colour for scroll zone + + boolean _iswapBytes; // Swap the byte order for Sprite pushImage() + + int32_t _iwidth, _iheight; // Sprite image width and height + +}; diff --git a/Extensions/Touch.cpp b/Extensions/Touch.cpp new file mode 100644 index 0000000..d077e13 --- /dev/null +++ b/Extensions/Touch.cpp @@ -0,0 +1,319 @@ +// The following touch screen support code by maxpautsch was merged 1/10/17 +// https://github.com/maxpautsch +// Define TOUCH_CS is the user setup file to enable this code +// A demo is provided in examples Generic folder +// Additions by Bodmer to double sample and use Z value to improve detection reliability +// See license in root directory. + +#ifdef TOUCH_CS // If a pin has been allocated to the Touch screen load functions +/*************************************************************************************** +** Function name: getTouchRaw +** Description: read raw touch position. Return false if not pressed. +***************************************************************************************/ +uint8_t TFT_eSPI::getTouchRaw(uint16_t *x, uint16_t *y){ + uint16_t tmp; + CS_H; + + spi_begin_touch(); + + T_CS_L; + + // Start bit + YP sample request for x position + tmp = SPI.transfer(0xd0); + tmp = SPI.transfer(0); + tmp = tmp <<5; + tmp |= 0x1f & (SPI.transfer(0)>>3); + + *x = tmp; + + // Start bit + XP sample request for y position + SPI.transfer(0x90); + tmp = SPI.transfer(0); + tmp = tmp <<5; + tmp |= 0x1f & (SPI.transfer(0)>>3); + + *y = tmp; + + T_CS_H; + + spi_end_touch(); + + return true; +} + +/*************************************************************************************** +** Function name: getTouchRawZ +** Description: read raw pressure on touchpad and return Z value. +***************************************************************************************/ +uint16_t TFT_eSPI::getTouchRawZ(void){ + CS_H; + + spi_begin_touch(); + + T_CS_L; + + // Z sample request + uint16_t tz = 0xFFF; + SPI.transfer(0xb1); + tz += SPI.transfer16(0xc1) >> 3; + tz -= SPI.transfer16(0x91) >> 3; + + T_CS_H; + + spi_end_touch(); + + return tz; +} + +/*************************************************************************************** +** Function name: validTouch +** Description: read validated position. Return false if not pressed. +***************************************************************************************/ +#define _RAWERR 10 // Deadband in position samples +uint8_t TFT_eSPI::validTouch(uint16_t *x, uint16_t *y, uint16_t threshold){ + uint16_t x_tmp, y_tmp, x_tmp2, y_tmp2; + + // Wait until pressure stops increasing + uint16_t z1 = 1; + uint16_t z2 = 0; + while (z1 > z2) + { + z2 = z1; + z1 = getTouchRawZ(); + delay(1); + } + + // Serial.print("Z = ");Serial.println(z1); + + if (z1 <= threshold) return false; + + getTouchRaw(&x_tmp,&y_tmp); + + // Serial.print("Sample 1 x,y = "); Serial.print(x_tmp);Serial.print(",");Serial.print(y_tmp); + // Serial.print(", Z = ");Serial.println(z1); + + delay(1); // Small delay to the next sample + if (getTouchRawZ() <= threshold) return false; + + delay(2); // Small delay to the next sample + getTouchRaw(&x_tmp2,&y_tmp2); + + // Serial.print("Sample 2 x,y = "); Serial.print(x_tmp2);Serial.print(",");Serial.println(y_tmp2); + // Serial.print("Sample difference = ");Serial.print(abs(x_tmp - x_tmp2));Serial.print(",");Serial.println(abs(y_tmp - y_tmp2)); + + if (abs(x_tmp - x_tmp2) > _RAWERR) return false; + if (abs(y_tmp - y_tmp2) > _RAWERR) return false; + + *x = x_tmp; + *y = y_tmp; + + return true; +} + +/*************************************************************************************** +** Function name: getTouch +** Description: read callibrated position. Return false if not pressed. +***************************************************************************************/ +#define Z_THRESHOLD 350 // Touch pressure threshold for validating touches +uint8_t TFT_eSPI::getTouch(uint16_t *x, uint16_t *y, uint16_t threshold){ + uint16_t x_tmp, y_tmp, xx, yy; + + if (threshold<20) threshold = 20; + if (_pressTime > millis()) threshold=20; + + uint8_t n = 5; + uint8_t valid = 0; + while (n--) + { + if (validTouch(&x_tmp, &y_tmp, threshold)) valid++;; + } + + if (valid<1) { _pressTime = 0; return false; } + + _pressTime = millis() + 50; + + if(!touchCalibration_rotate){ + xx=(x_tmp-touchCalibration_x0)*_width/touchCalibration_x1; + yy=(y_tmp-touchCalibration_y0)*_height/touchCalibration_y1; + if(touchCalibration_invert_x) + xx = _width - xx; + if(touchCalibration_invert_y) + yy = _height - yy; + } else { + yy=(x_tmp-touchCalibration_x0)*_height/touchCalibration_x1; + xx=(y_tmp-touchCalibration_y0)*_width/touchCalibration_y1; + if(touchCalibration_invert_x) + xx = _width - xx; + if(touchCalibration_invert_y) + yy = _height - yy; + } + + if (xx >= _width || yy >= _height) return valid; + + _pressX = xx; + _pressY = yy; + *x = _pressX; + *y = _pressY; + return valid; +} + +/*************************************************************************************** +** Function name: calibrateTouch +** Description: generates calibration parameters for touchscreen. +***************************************************************************************/ +void TFT_eSPI::calibrateTouch(uint16_t *parameters, uint32_t color_fg, uint32_t color_bg, uint8_t size){ + int16_t values[] = {0,0,0,0,0,0,0,0}; + uint16_t x_tmp, y_tmp; + + + + for(uint8_t i = 0; i<4; i++){ + fillRect(0, 0, size+1, size+1, color_bg); + fillRect(0, _height-size-1, size+1, size+1, color_bg); + fillRect(_width-size-1, 0, size+1, size+1, color_bg); + fillRect(_width-size-1, _height-size-1, size+1, size+1, color_bg); + + if (i == 5) break; // used to clear the arrows + + switch (i) { + case 0: // up left + drawLine(0, 0, 0, size, color_fg); + drawLine(0, 0, size, 0, color_fg); + drawLine(0, 0, size , size, color_fg); + break; + case 1: // bot left + drawLine(0, _height-size-1, 0, _height-1, color_fg); + drawLine(0, _height-1, size, _height-1, color_fg); + drawLine(size, _height-size-1, 0, _height-1 , color_fg); + break; + case 2: // up right + drawLine(_width-size-1, 0, _width-1, 0, color_fg); + drawLine(_width-size-1, size, _width-1, 0, color_fg); + drawLine(_width-1, size, _width-1, 0, color_fg); + break; + case 3: // bot right + drawLine(_width-size-1, _height-size-1, _width-1, _height-1, color_fg); + drawLine(_width-1, _height-1-size, _width-1, _height-1, color_fg); + drawLine(_width-1-size, _height-1, _width-1, _height-1, color_fg); + break; + } + + // user has to get the chance to release + if(i>0) delay(1000); + + for(uint8_t j= 0; j<8; j++){ + // Use a lower detect threshold as corners tend to be less sensitive + while(!validTouch(&x_tmp, &y_tmp, Z_THRESHOLD/4)); + values[i*2 ] += x_tmp; + values[i*2+1] += y_tmp; + } + values[i*2 ] /= 8; + values[i*2+1] /= 8; + } + + + + // check orientation + // from case 0 to case 1, the y value changed. + // If the measured delta of the touch x axis is bigger than the delta of the y axis, the touch and TFT axes are switched. + touchCalibration_rotate = false; + if(abs(values[0]-values[2]) > abs(values[1]-values[3])){ + touchCalibration_rotate = true; + touchCalibration_x0 = (values[0] + values[4])/2; // calc min x + touchCalibration_x1 = (values[2] + values[6])/2; // calc max x + touchCalibration_y0 = (values[1] + values[3])/2; // calc min y + touchCalibration_y1 = (values[5] + values[7])/2; // calc max y + } else { + touchCalibration_x0 = (values[0] + values[2])/2; // calc min x + touchCalibration_x1 = (values[4] + values[6])/2; // calc max x + touchCalibration_y0 = (values[1] + values[5])/2; // calc min y + touchCalibration_y1 = (values[3] + values[7])/2; // calc max y + } + + // in addition, the touch screen axis could be in the opposit direction of the TFT axis + touchCalibration_invert_x = false; + if(touchCalibration_x0 > touchCalibration_x1){ + values[0]=touchCalibration_x0; + touchCalibration_x0 = touchCalibration_x1; + touchCalibration_x1 = values[0]; + touchCalibration_invert_x = true; + } + touchCalibration_invert_y = false; + if(touchCalibration_y0 > touchCalibration_y1){ + values[0]=touchCalibration_y0; + touchCalibration_y0 = touchCalibration_y1; + touchCalibration_y1 = values[0]; + touchCalibration_invert_y = true; + } + + // pre calculate + touchCalibration_x1 -= touchCalibration_x0; + touchCalibration_y1 -= touchCalibration_y0; + + if(touchCalibration_x0 == 0) touchCalibration_x0 = 1; + if(touchCalibration_x1 == 0) touchCalibration_x1 = 1; + if(touchCalibration_y0 == 0) touchCalibration_y0 = 1; + if(touchCalibration_y1 == 0) touchCalibration_y1 = 1; + + // export parameters, if pointer valid + if(parameters != NULL){ + parameters[0] = touchCalibration_x0; + parameters[1] = touchCalibration_x1; + parameters[2] = touchCalibration_y0; + parameters[3] = touchCalibration_y1; + parameters[4] = touchCalibration_rotate | (touchCalibration_invert_x <<1) | (touchCalibration_invert_y <<2); + } +} + + +/*************************************************************************************** +** Function name: setTouch +** Description: imports calibration parameters for touchscreen. +***************************************************************************************/ +void TFT_eSPI::setTouch(uint16_t *parameters){ + touchCalibration_x0 = parameters[0]; + touchCalibration_x1 = parameters[1]; + touchCalibration_y0 = parameters[2]; + touchCalibration_y1 = parameters[3]; + + if(touchCalibration_x0 == 0) touchCalibration_x0 = 1; + if(touchCalibration_x1 == 0) touchCalibration_x1 = 1; + if(touchCalibration_y0 == 0) touchCalibration_y0 = 1; + if(touchCalibration_y1 == 0) touchCalibration_y1 = 1; + + touchCalibration_rotate = parameters[4] & 0x01; + touchCalibration_invert_x = parameters[4] & 0x02; + touchCalibration_invert_y = parameters[4] & 0x04; +} + + +#else // TOUCH CS is not defined so generate dummy functions that do nothing + +/*************************************************************************************** +** Function name: Dummy functions for case where chip select pin is undefined +** Description: +***************************************************************************************/ + +uint8_t TFT_eSPI::getTouchRaw(uint16_t *x, uint16_t *y){ + return true; +} + +uint16_t TFT_eSPI::getTouchRawZ(void){ + return true; +} + +uint8_t TFT_eSPI::validTouch(uint16_t *x, uint16_t *y, uint16_t threshold){ + return true; +} + +uint8_t TFT_eSPI::getTouch(uint16_t *x, uint16_t *y, uint16_t threshold){ + return true; +} + +void TFT_eSPI::calibrateTouch(uint16_t *parameters, uint32_t color_bg, uint32_t color_fg, uint8_t size){ +} + +void TFT_eSPI::setTouch(uint16_t *parameters){ +} + +#endif // TOUCH_CS diff --git a/Extensions/Touch.h b/Extensions/Touch.h new file mode 100644 index 0000000..7f3b22a --- /dev/null +++ b/Extensions/Touch.h @@ -0,0 +1,24 @@ + // Coded by Bodmer 10/2/18, see license in root directory. + // This is part of the TFT_eSPI class and is associated with the Touch Screen handlers + + public: + + uint8_t getTouchRaw(uint16_t *x, uint16_t *y); + uint16_t getTouchRawZ(void); + uint8_t getTouch(uint16_t *x, uint16_t *y, uint16_t threshold = 600); + + void calibrateTouch(uint16_t *data, uint32_t color_fg, uint32_t color_bg, uint8_t size); + void setTouch(uint16_t *data); + + private: + + inline void spi_begin_touch() __attribute__((always_inline)); + inline void spi_end_touch() __attribute__((always_inline)); + + // These are associated with the Touch Screen handlers + uint8_t validTouch(uint16_t *x, uint16_t *y, uint16_t threshold = 600); + // Initialise with example calibration values so processor does not crash if setTouch() not called in setup() + uint16_t touchCalibration_x0 = 300, touchCalibration_x1 = 3600, touchCalibration_y0 = 300, touchCalibration_y1 = 3600; + uint8_t touchCalibration_rotate = 1, touchCalibration_invert_x = 2, touchCalibration_invert_y = 0; + uint32_t _pressTime; + uint16_t _pressX, _pressY; diff --git a/Fonts/Font7srle.c b/Fonts/Font7srle.c index 6235093..bb292d7 100644 --- a/Fonts/Font7srle.c +++ b/Fonts/Font7srle.c @@ -12,7 +12,7 @@ PROGMEM const unsigned char widtbl_f7s[96] = // character width table { 12, 12, 12, 12, 12, 12, 12, 12, // char 32 - 39 - 12, 12, 12, 12, 12, 17, 12, 12, // char 40 - 47 + 12, 12, 12, 12, 12, 32, 12, 12, // char 40 - 47 32, 32, 32, 32, 32, 32, 32, 32, // char 48 - 55 32, 32, 12, 12, 12, 12, 12, 12, // char 56 - 63 12, 12, 12, 12, 12, 12, 12, 12, // char 64 - 71 @@ -32,10 +32,12 @@ PROGMEM const unsigned char chr_f7s_20[] = 0x7F, 0x7F, 0x7F, 0x7F, 0x3F }; +// Make - sign look like a segment PROGMEM const unsigned char chr_f7s_2D[] = { -0x7F, 0x7F, 0x45, 0x8A, 0x05, 0x8A, 0x05, 0x8A, -0x05, 0x8A, 0x7F, 0x7F, 0x7F, 0x2B +0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x27, 0x8E, 0x0E, +0x92, 0x0A, 0x96, 0x09, 0x94, 0x0C, 0x90, 0x7F, +0x7F, 0x7F, 0x7F, 0x7F, 0x47 }; PROGMEM const unsigned char chr_f7s_2E[] = diff --git a/README.md b/README.md index 4647721..79bf602 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # TFT_eSPI +>>> This branch includes new antialiased font capability, this is a work-in-progress <<< + An Arduino IDE compatible graphics and fonts library for ESP8266 and ESP32 processors with a driver for ILI9341, ILI9163, ST7735 and S6D02A1 based TFT displays that support SPI. The library also supports TFT displays designed for the Raspberry Pi that are based on a ILI9486 driver chip with a 480 x 320 pixel screen. This display must be of the Waveshare design and use a 16 bit serial interface based on the 74HC04, 74HC4040 and 2 x 74HC4094 logic chips. A modification to these displays is possible (see mod image in Tools folder) to make many graphics functions much faster (e.g. 23ms to clear the screen, 1.2ms to draw a 72 pixel high numeral). @@ -32,7 +34,7 @@ Configuration of the library font selections, pins used to interface with the TF I have made some changes that will be uploaded soon that improves sprite and image rendering performance by up to 3x faster on the ESP8266. These updates are currently being tested/debugged. -**2. Anti-aliased fonts - see Smooth_font branch for beta version** +**2. Anti-aliased fonts** I have been experimenting with anti-aliased font files in "vlw" format generated by the free [Processing IDE](https://processing.org/). This IDE can be used to generate font files from your computer's font set and include **any** Unicode characters. This means Greek, Japanese and any other UTF-16 glyphs can be used. diff --git a/TFT_Drivers/ILI9163_Rotation.h b/TFT_Drivers/ILI9163_Rotation.h index 7232430..3323169 100644 --- a/TFT_Drivers/ILI9163_Rotation.h +++ b/TFT_Drivers/ILI9163_Rotation.h @@ -7,8 +7,8 @@ switch (rotation) { case 0: writedata(TFT_MAD_MX | TFT_MAD_MY | TFT_MAD_BGR); - _width = _width_orig; - _height = _height_orig; + _width = _init_width; + _height = _init_height; #ifdef CGRAM_OFFSET colstart = 0; rowstart = 0; @@ -16,8 +16,8 @@ break; case 1: writedata(TFT_MAD_MV | TFT_MAD_MY | TFT_MAD_BGR); - _width = _height_orig; - _height = _width_orig; + _width = _init_height; + _height = _init_width; #ifdef CGRAM_OFFSET colstart = 0; rowstart = 0; @@ -25,8 +25,8 @@ break; case 2: writedata(TFT_MAD_BGR); - _width = _width_orig; - _height = _height_orig; + _width = _init_width; + _height = _init_height; #ifdef CGRAM_OFFSET colstart = 0; rowstart = 32; @@ -34,8 +34,8 @@ break; case 3: writedata(TFT_MAD_MX | TFT_MAD_MV | TFT_MAD_BGR); - _width = _height_orig; - _height = _width_orig; + _width = _init_height; + _height = _init_width; #ifdef CGRAM_OFFSET colstart = 32; rowstart = 0; diff --git a/TFT_Drivers/ILI9341_Rotation.h b/TFT_Drivers/ILI9341_Rotation.h index 6966f37..f5e9b38 100644 --- a/TFT_Drivers/ILI9341_Rotation.h +++ b/TFT_Drivers/ILI9341_Rotation.h @@ -11,8 +11,8 @@ #else writedata(TFT_MAD_MX | TFT_MAD_BGR); #endif - _width = _width_orig; - _height = _height_orig; + _width = _init_width; + _height = _init_height; break; case 1: #ifdef M5STACK @@ -20,8 +20,8 @@ #else writedata(TFT_MAD_MV | TFT_MAD_BGR); #endif - _width = _height_orig; - _height = _width_orig; + _width = _init_height; + _height = _init_width; break; case 2: #ifdef M5STACK @@ -29,8 +29,8 @@ #else writedata(TFT_MAD_MY | TFT_MAD_BGR); #endif - _width = _width_orig; - _height = _height_orig; + _width = _init_width; + _height = _init_height; break; case 3: #ifdef M5STACK @@ -38,8 +38,8 @@ #else writedata(TFT_MAD_MX | TFT_MAD_MY | TFT_MAD_MV | TFT_MAD_BGR); #endif - _width = _height_orig; - _height = _width_orig; + _width = _init_height; + _height = _init_width; break; // These next rotations are for bottom up BMP drawing case 4: @@ -48,8 +48,8 @@ #else writedata(TFT_MAD_MX | TFT_MAD_MY | TFT_MAD_BGR); #endif - _width = _width_orig; - _height = _height_orig; + _width = _init_width; + _height = _init_height; break; case 5: #ifdef M5STACK @@ -57,8 +57,8 @@ #else writedata(TFT_MAD_MV | TFT_MAD_MX | TFT_MAD_BGR); #endif - _width = _height_orig; - _height = _width_orig; + _width = _init_height; + _height = _init_width; break; case 6: #ifdef M5STACK @@ -66,8 +66,8 @@ #else writedata(TFT_MAD_BGR); #endif - _width = _width_orig; - _height = _height_orig; + _width = _init_width; + _height = _init_height; break; case 7: #ifdef M5STACK @@ -75,8 +75,8 @@ #else writedata(TFT_MAD_MY | TFT_MAD_MV | TFT_MAD_BGR); #endif - _width = _height_orig; - _height = _width_orig; + _width = _init_height; + _height = _init_width; break; } diff --git a/TFT_Drivers/RPI_ILI9486_Rotation.h b/TFT_Drivers/RPI_ILI9486_Rotation.h index 53afc2a..495d675 100644 --- a/TFT_Drivers/RPI_ILI9486_Rotation.h +++ b/TFT_Drivers/RPI_ILI9486_Rotation.h @@ -5,43 +5,43 @@ switch (rotation) { case 0: // Portrait writedata(TFT_MAD_BGR | TFT_MAD_MX); - _width = _width_orig; - _height = _height_orig; + _width = _init_width; + _height = _init_height; break; case 1: // Landscape (Portrait + 90) writedata(TFT_MAD_BGR | TFT_MAD_MV); - _width = _height_orig; - _height = _width_orig; + _width = _init_height; + _height = _init_width; break; case 2: // Inverter portrait writedata( TFT_MAD_BGR | TFT_MAD_MY); - _width = _width_orig; - _height = _height_orig; + _width = _init_width; + _height = _init_height; break; case 3: // Inverted landscape writedata(TFT_MAD_BGR | TFT_MAD_MV | TFT_MAD_MX | TFT_MAD_MY); - _width = _height_orig; - _height = _width_orig; + _width = _init_height; + _height = _init_width; break; case 4: // Portrait writedata(TFT_MAD_BGR | TFT_MAD_MX | TFT_MAD_MY); - _width = _width_orig; - _height = _height_orig; + _width = _init_width; + _height = _init_height; break; case 5: // Landscape (Portrait + 90) writedata(TFT_MAD_BGR | TFT_MAD_MV | TFT_MAD_MX); - _width = _height_orig; - _height = _width_orig; + _width = _init_height; + _height = _init_width; break; case 6: // Inverter portrait writedata( TFT_MAD_BGR); - _width = _width_orig; - _height = _height_orig; + _width = _init_width; + _height = _init_height; break; case 7: // Inverted landscape writedata(TFT_MAD_BGR | TFT_MAD_MV | TFT_MAD_MY); - _width = _height_orig; - _height = _width_orig; + _width = _init_height; + _height = _init_width; break; } diff --git a/TFT_Drivers/S6D02A1_Rotation.h b/TFT_Drivers/S6D02A1_Rotation.h index dfa6cdb..7fa6eec 100644 --- a/TFT_Drivers/S6D02A1_Rotation.h +++ b/TFT_Drivers/S6D02A1_Rotation.h @@ -7,22 +7,22 @@ switch (rotation) { case 0: writedata(TFT_MAD_MX | TFT_MAD_MY | TFT_MAD_BGR); - _width = _width_orig; - _height = _height_orig; + _width = _init_width; + _height = _init_height; break; case 1: writedata(TFT_MAD_MV | TFT_MAD_MY | TFT_MAD_BGR); - _width = _height_orig; - _height = _width_orig; + _width = _init_height; + _height = _init_width; break; case 2: writedata(TFT_MAD_BGR); - _width = _width_orig; - _height = _height_orig; + _width = _init_width; + _height = _init_height; break; case 3: writedata(TFT_MAD_MX | TFT_MAD_MV | TFT_MAD_BGR); - _width = _height_orig; - _height = _width_orig; + _width = _init_height; + _height = _init_width; break; } diff --git a/TFT_Drivers/ST7735_Rotation.h b/TFT_Drivers/ST7735_Rotation.h index 6113886..4a8bfdc 100644 --- a/TFT_Drivers/ST7735_Rotation.h +++ b/TFT_Drivers/ST7735_Rotation.h @@ -23,8 +23,8 @@ } else { writedata(TFT_MAD_MX | TFT_MAD_MY | TFT_MAD_BGR); } - _width = _width_orig; - _height = _height_orig; + _width = _init_width; + _height = _init_height; break; case 1: if (tabcolor == INITR_BLACKTAB) { @@ -44,8 +44,8 @@ } else { writedata(TFT_MAD_MY | TFT_MAD_MV | TFT_MAD_BGR); } - _width = _height_orig; - _height = _width_orig; + _width = _init_height; + _height = _init_width; break; case 2: if (tabcolor == INITR_BLACKTAB) { @@ -65,8 +65,8 @@ } else { writedata(TFT_MAD_BGR); } - _width = _width_orig; - _height = _height_orig; + _width = _init_width; + _height = _init_height; break; case 3: if (tabcolor == INITR_BLACKTAB) { @@ -86,30 +86,30 @@ } else { writedata(TFT_MAD_MX | TFT_MAD_MV | TFT_MAD_BGR); } - _width = _height_orig; - _height = _width_orig; + _width = _init_height; + _height = _init_width; break; // These next rotations are for bottum up BMP drawing /* case 4: writedata(ST7735_TFT_MAD_MX | ST7735_TFT_MAD_MY | ST7735_TFT_MAD_BGR); - _width = _width_orig; - _height = _height_orig; + _width = _init_width; + _height = _init_height; break; case 5: writedata(ST7735_TFT_MAD_MV | ST7735_TFT_MAD_MX | ST7735_TFT_MAD_BGR); - _width = _height_orig; - _height = _width_orig; + _width = _init_height; + _height = _init_width; break; case 6: writedata(ST7735_TFT_MAD_BGR); - _width = _width_orig; - _height = _height_orig; + _width = _init_width; + _height = _init_height; break; case 7: writedata(ST7735_TFT_MAD_MY | ST7735_TFT_MAD_MV | ST7735_TFT_MAD_BGR); - _width = _height_orig; - _height = _width_orig; + _width = _init_height; + _height = _init_width; break; */ } diff --git a/TFT_eSPI.cpp b/TFT_eSPI.cpp index a7c535c..901f490 100644 --- a/TFT_eSPI.cpp +++ b/TFT_eSPI.cpp @@ -51,21 +51,25 @@ inline void TFT_eSPI::spi_end(void){ #endif } -inline void TFT_eSPI::spi_begin_touch(void){ -#if defined (SPI_HAS_TRANSACTION) && defined (SUPPORT_TRANSACTIONS) - if (locked) {locked = false; SPI.beginTransaction(SPISettings(SPI_TOUCH_FREQUENCY, MSBFIRST, SPI_MODE0));} -#else - SPI.setFrequency(SPI_TOUCH_FREQUENCY); -#endif -} +#if defined (TOUCH_CS) && defined (SPI_TOUCH_FREQUENCY) + + inline void TFT_eSPI::spi_begin_touch(void){ + #if defined (SPI_HAS_TRANSACTION) && defined (SUPPORT_TRANSACTIONS) + if (locked) {locked = false; SPI.beginTransaction(SPISettings(SPI_TOUCH_FREQUENCY, MSBFIRST, SPI_MODE0));} + #else + SPI.setFrequency(SPI_TOUCH_FREQUENCY); + #endif + } + + inline void TFT_eSPI::spi_end_touch(void){ + #if defined (SPI_HAS_TRANSACTION) && defined (SUPPORT_TRANSACTIONS) + if(!inTransaction) {if (!locked) {locked = true; SPI.endTransaction();}} + #else + SPI.setFrequency(SPI_FREQUENCY); + #endif + } -inline void TFT_eSPI::spi_end_touch(void){ -#if defined (SPI_HAS_TRANSACTION) && defined (SUPPORT_TRANSACTIONS) - if(!inTransaction) {if (!locked) {locked = true; SPI.endTransaction();}} -#else - SPI.setFrequency(SPI_FREQUENCY); #endif -} /*************************************************************************************** ** Function name: TFT_eSPI @@ -105,8 +109,8 @@ TFT_eSPI::TFT_eSPI(int16_t w, int16_t h) } #endif - _width_orig = _width = w; // Set by specific xxxxx_Defines.h file or by users sketch - _height_orig = _height = h; // Set by specific xxxxx_Defines.h file or by users sketch + _init_width = _width = w; // Set by specific xxxxx_Defines.h file or by users sketch + _init_height = _height = h; // Set by specific xxxxx_Defines.h file or by users sketch rotation = 0; cursor_y = cursor_x = 0; textfont = 1; @@ -114,7 +118,8 @@ TFT_eSPI::TFT_eSPI(int16_t w, int16_t h) textcolor = 0xFFFF; // White textbgcolor = 0x0000; // Black padX = 0; // No padding - textwrap = true; // Wrap text when using print stream + textwrapX = true; // Wrap text at end of line when using print stream + textwrapY = false; // Wrap text at bottom of screen when using print stream textdatum = TL_DATUM; // Top Left text alignment is default fontsloaded = 0; @@ -123,8 +128,6 @@ TFT_eSPI::TFT_eSPI(int16_t w, int16_t h) locked = true; // ESP32 transaction mutex lock flags inTransaction = false; - _booted = true; - addr_row = 0xFFFF; addr_col = 0xFFFF; @@ -171,8 +174,6 @@ void TFT_eSPI::begin(void) ***************************************************************************************/ void TFT_eSPI::init(void) { - if (_booted) - { #if !defined (ESP32) #ifdef TFT_CS cspinmask = (uint32_t) digitalPinToBitMask(TFT_CS); @@ -193,7 +194,7 @@ void TFT_eSPI::init(void) SPI.pins(6, 7, 8, 0); #endif - SPI.begin(); // This will set HMISO to input + SPI.begin(); // This will set HMISO to input #else #if defined (TFT_MOSI) && !defined (TFT_SPI_OVERLAP) SPI.begin(TFT_SCLK, TFT_MISO, TFT_MOSI, -1); @@ -203,35 +204,31 @@ void TFT_eSPI::init(void) #endif - inTransaction = false; - locked = true; + inTransaction = false; + locked = true; - // SUPPORT_TRANSACTIONS is mandatory for ESP32 so the hal mutex is toggled + // SUPPORT_TRANSACTIONS is manadatory for ESP32 so the hal mutex is toggled // so the code here is for ESP8266 only #if !defined (SUPPORT_TRANSACTIONS) && defined (ESP8266) - SPI.setBitOrder(MSBFIRST); - SPI.setDataMode(SPI_MODE0); - SPI.setFrequency(SPI_FREQUENCY); + SPI.setBitOrder(MSBFIRST); + SPI.setDataMode(SPI_MODE0); + SPI.setFrequency(SPI_FREQUENCY); #endif // Set to output once again in case D6 (MISO) is used for CS #ifdef TFT_CS - digitalWrite(TFT_CS, HIGH); // Chip select high (inactive) - pinMode(TFT_CS, OUTPUT); + digitalWrite(TFT_CS, HIGH); // Chip select high (inactive) + pinMode(TFT_CS, OUTPUT); #else - SPI.setHwCs(1); // Use hardware SS toggling + SPI.setHwCs(1); // Use hardware SS toggling #endif // Set to output once again in case D6 (MISO) is used for DC #ifdef TFT_DC - digitalWrite(TFT_DC, HIGH); // Data/Command high = data mode - pinMode(TFT_DC, OUTPUT); + digitalWrite(TFT_DC, HIGH); // Data/Command high = data mode + pinMode(TFT_DC, OUTPUT); #endif - _booted = false; - } // end of: if just _booted - - // Toggle RST low to reset #ifdef TFT_RST if (TFT_RST >= 0) { @@ -242,13 +239,13 @@ void TFT_eSPI::init(void) digitalWrite(TFT_RST, HIGH); delay(150); } -#else - // Or use the software reset +#endif + spi_begin(); writecommand(TFT_SWRST); // Software reset spi_end(); - delay(120); // Wait for software reset to complete -#endif + + delay(5); // Wait for software reset to complete spi_begin(); @@ -272,7 +269,6 @@ void TFT_eSPI::init(void) spi_end(); - setRotation(rotation); } @@ -1591,9 +1587,10 @@ void TFT_eSPI::setTextColor(uint16_t c, uint16_t b) ** Function name: setTextWrap ** Description: Define if text should wrap at end of line ***************************************************************************************/ -void TFT_eSPI::setTextWrap(boolean w) +void TFT_eSPI::setTextWrap(boolean wrapX, boolean wrapY) { - textwrap = w; + textwrapX = wrapX; + textwrapY = wrapY; } @@ -1684,7 +1681,35 @@ int16_t TFT_eSPI::textWidth(const char *string) int16_t TFT_eSPI::textWidth(const char *string, int font) { - unsigned int str_width = 0; + int str_width = 0; + +#ifdef SMOOTH_FONT + if(fontLoaded) + { + while (*string) + { + uint16_t unicode = decodeUTF8(*string++); + if (unicode) + { + if (unicode == 0x20) str_width += gFont.spaceWidth; + else + { + uint16_t gNum = 0; + bool found = getUnicodeIndex(unicode, &gNum); + if (found) + { + if(str_width == 0 && gdX[gNum] < 0) str_width -= gdX[gNum]; + if (*string) str_width += gxAdvance[gNum]; + else str_width += (gdX[gNum] + gWidth[gNum]); + } + else str_width += gFont.spaceWidth + 1; + } + } + } + return str_width; + } +#endif + unsigned char uniCode; char *widthtable; @@ -1750,6 +1775,10 @@ uint16_t TFT_eSPI::fontsLoaded(void) ***************************************************************************************/ int16_t TFT_eSPI::fontHeight(int16_t font) { +#ifdef SMOOTH_FONT + if(fontLoaded) return gFont.yAdvance; +#endif + #ifdef LOAD_GFXFF if (font==1) { @@ -2744,10 +2773,9 @@ void TFT_eSPI::pushColor(uint16_t color, uint16_t len) spi_end(); } - /*************************************************************************************** ** Function name: pushColors -** Description: push an aray of pixels for 16 bit raw image drawing +** Description: push an array of pixels for 16 bit raw image drawing ***************************************************************************************/ // Assumed that setWindow() has previously been called @@ -2861,6 +2889,41 @@ else SPI.writeBytes((uint8_t*)data,len<<1); SPI1CMD |= SPIBUSY; } +/* // Smaller version but slower + uint32_t count = 0; + while(len) + { + if(len>15) {count = 16; len -= 16;} + else {count = len; len = 0;} + uint32_t bits = (count*16-1); // bits left to shift - 1 + if (swap) + { + uint16_t* ptr = (uint16_t*)color; + while(count--) + { + *ptr++ = (*(data) >> 8) | (uint16_t)(*(data) << 8); + data++; + } + } + else + { + memcpy(color,data,count<<1); + data += 16; + } + while(SPI1CMD & SPIBUSY) {} + SPI1U1 = (SPI1U1 & mask) | (bits << SPILMOSI) | (bits << SPILMISO); + SPI1W0 = color[0]; + SPI1W1 = color[1]; + SPI1W2 = color[2]; + SPI1W3 = color[3]; + SPI1W4 = color[4]; + SPI1W5 = color[5]; + SPI1W6 = color[6]; + SPI1W7 = color[7]; + SPI1CMD |= SPIBUSY; + } +*/ + while(SPI1CMD & SPIBUSY) {} #endif @@ -3215,15 +3278,33 @@ uint16_t TFT_eSPI::color565(uint8_t r, uint8_t g, uint8_t b) /*************************************************************************************** -** Function name: color332 +** Function name: color16to8 ** Description: convert 16 bit colour to an 8 bit 332 RGB colour value ***************************************************************************************/ -uint8_t TFT_eSPI::color332(uint16_t c) +uint8_t TFT_eSPI::color16to8(uint16_t c) { return ((c & 0xE000)>>8) | ((c & 0x0700)>>6) | ((c & 0x0018)>>3); } +/*************************************************************************************** +** Function name: color8to16 +** Description: convert 8 bit colour to a 16 bit 565 colour value +***************************************************************************************/ +uint16_t TFT_eSPI::color8to16(uint8_t color) +{ + uint8_t blue[] = {0, 11, 21, 31}; // blue 2 to 5 bit colour lookup table + uint16_t color16 = 0; + + // =====Green===== ===============Red============== + color16 = (color & 0x1C)<<6 | (color & 0xC0)<<5 | (color & 0xE0)<<8; + // =====Green===== =======Blue====== + color16 |= (color & 0x1C)<<3 | blue[color & 0x03]; + + return color16; +} + + /*************************************************************************************** ** Function name: invertDisplay ** Description: invert the display colours i = 1 invert, i = 0 normal @@ -3246,6 +3327,27 @@ size_t TFT_eSPI::write(uint8_t utf8) { if (utf8 == '\r') return 1; +#ifdef SMOOTH_FONT + if(fontLoaded) + { + uint16_t unicode = decodeUTF8(utf8); + if (!unicode) return 0; + + //fontFile = SPIFFS.open( _gFontFilename, "r" ); + + if(!fontFile) + { + fontLoaded = false; + return 0; + } + + drawGlyph(unicode); + + //fontFile.close(); + return 1; + } +#endif + uint8_t uniCode = utf8; // Work with a copy if (utf8 == '\n') uniCode+=22; // Make it a valid space character to stop errors else if (utf8 < 32) return 0; @@ -3274,7 +3376,7 @@ size_t TFT_eSPI::write(uint8_t utf8) height = chr_hgt_f16; // Font 2 is rendered in whole byte widths so we must allow for this width = (width + 6) / 8; // Width in whole bytes for font 2, should be + 7 but must allow for font width change - width = width * 8; // Width converted back to pixles + width = width * 8; // Width converted back to pixels } #ifdef LOAD_RLE else @@ -3312,12 +3414,12 @@ size_t TFT_eSPI::write(uint8_t utf8) } else { - if (textwrap && (cursor_x + width * textsize > _width)) + if (textwrapX && (cursor_x + width * textsize > _width)) { cursor_y += height; cursor_x = 0; } - //if (cursor_y >= _height) cursor_y = 0; + if (textwrapY && (cursor_y >= _height)) cursor_y = 0; cursor_x += drawChar(uniCode, cursor_x, cursor_y, textfont); } @@ -3341,13 +3443,13 @@ size_t TFT_eSPI::write(uint8_t utf8) h = pgm_read_byte(&glyph->height); if((w > 0) && (h > 0)) { // Is there an associated bitmap? int16_t xo = (int8_t)pgm_read_byte(&glyph->xOffset); - if(textwrap && ((cursor_x + textsize * (xo + w)) > _width)) { + if(textwrapX && ((cursor_x + textsize * (xo + w)) > _width)) { // Drawing character would go off right edge; wrap to new line cursor_x = 0; cursor_y += (int16_t)textsize * (uint8_t)pgm_read_byte(&gfxFont->yAdvance); } - //if (cursor_y >= _height) cursor_y = 0; + if (textwrapY && (cursor_y >= _height)) cursor_y = 0; drawChar(cursor_x, cursor_y, uniCode, textcolor, textbgcolor, textsize); } cursor_x += pgm_read_byte(&glyph->xAdvance) * (int16_t)textsize; @@ -3362,7 +3464,7 @@ size_t TFT_eSPI::write(uint8_t utf8) /*************************************************************************************** ** Function name: drawChar -** Description: draw a unicode onto the screen +** Description: draw a Unicode onto the screen ***************************************************************************************/ int16_t TFT_eSPI::drawChar(unsigned int uniCode, int x, int y) { @@ -3551,7 +3653,7 @@ int16_t TFT_eSPI::drawChar(unsigned int uniCode, int x, int y, int font) while (pc < w) { line = pgm_read_byte((uint8_t *)flash_address); - flash_address++; // 20 bytes smaller by incrementing here + flash_address++; if (line & 0x80) { line &= 0x7F; line++; @@ -3679,7 +3781,7 @@ int16_t TFT_eSPI::drawString(const char *string, int poX, int poY, int font) baseline = cheight; padding =101; // Different padding method used for Free Fonts - // We need to make an adjustment for the botom of the string (eg 'y' character) + // We need to make an adjustment for the bottom of the string (eg 'y' character) if ((textdatum == BL_DATUM) || (textdatum == BC_DATUM) || (textdatum == BR_DATUM)) { cheight += glyph_bb * textsize; } @@ -3690,7 +3792,15 @@ int16_t TFT_eSPI::drawString(const char *string, int poX, int poY, int font) if (textdatum || padX) { - // If it is not font 1 (GLCD or free font) get the basline and pixel height of the font + // If it is not font 1 (GLCD or free font) get the baseline and pixel height of the font +#ifdef SMOOTH_FONT + if(fontLoaded) { + baseline = gFont.maxAscent; + cheight = fontHeight(0); + } + + else +#endif if (font!=1) { baseline = pgm_read_byte( &fontdata[font].baseline ) * textsize; cheight = fontHeight(font); @@ -3780,7 +3890,28 @@ int16_t TFT_eSPI::drawString(const char *string, int poX, int poY, int font) } #endif - while (*string) sumX += drawChar(*(string++), poX+sumX, poY, font); +#ifdef SMOOTH_FONT + if(fontLoaded) + { + if (textcolor!=textbgcolor) fillRect(poX, poY, cwidth, cheight, textbgcolor); + //drawLine(poX - 5, poY, poX + 5, poY, TFT_GREEN); + //drawLine(poX, poY - 5, poX, poY + 5, TFT_GREEN); + //fontFile = SPIFFS.open( _gFontFilename, "r"); + if(!fontFile) return 0; + uint16_t len = strlen(string); + uint16_t n = 0; + setCursor(poX, poY); + while (n < len) + { + uint16_t unicode = decodeUTF8((uint8_t*)string, &n, len - n); + drawGlyph(unicode); + } + sumX += cwidth; + //fontFile.close(); + } + else +#endif + while (*string) sumX += drawChar(*(string++), poX+sumX, poY, font); //vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv DEBUG vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv // Switch on debugging for the padding areas @@ -4127,16 +4258,17 @@ void spiWriteBlock(uint16_t color, uint32_t repeat) SPI1U = SPIUMOSI | SPIUDUPLEX | SPIUSSE; } -#elif ESP8266 // ESP32 or a ESP8266 running at 80MHz SPI so slow things down -#define BUFFER_SIZE 64 -void spiWriteBlock(uint16_t color, uint32_t repeat) -{ - - uint8_t colorBin[] = { (uint8_t) (color >> 8), (uint8_t) color}; - SPI.writePattern(&colorBin[0], 2, repeat); - -} +//#elif ESP8266 // ESP32 or a ESP8266 running at 80MHz SPI so slow things down +// +//#define BUFFER_SIZE 64 +//void spiWriteBlock(uint16_t color, uint32_t repeat) +//{ +// +// uint8_t colorBin[] = { (uint8_t) (color >> 8), (uint8_t) color}; +// SPI.writePattern(&colorBin[0], 2, repeat); +// +//} #else // Low level register based ESP32 code @@ -4174,1601 +4306,19 @@ void spiWriteBlock(uint16_t color, uint32_t repeat) } #endif -// The following touch screen support code by maxpautsch was merged 1/10/17 -// https://github.com/maxpautsch -// Define TOUCH_CS is the user setup file to enable this code -// A demo is provided in examples Generic folder -// Additions by Bodmer to double sample and use Z value to improve detection reliability + +//////////////////////////////////////////////////////////////////////////////////////// #ifdef TOUCH_CS -/*************************************************************************************** -** Function name: getTouchRaw -** Description: read raw touch position. Return false if not pressed. -***************************************************************************************/ -uint8_t TFT_eSPI::getTouchRaw(uint16_t *x, uint16_t *y){ - uint16_t tmp; - CS_H; - - spi_begin_touch(); - - T_CS_L; - - // Start bit + YP sample request for x position - tmp = SPI.transfer(0xd0); - tmp = SPI.transfer(0); - tmp = tmp <<5; - tmp |= 0x1f & (SPI.transfer(0)>>3); - - *x = tmp; - - // Start bit + XP sample request for y position - SPI.transfer(0x90); - tmp = SPI.transfer(0); - tmp = tmp <<5; - tmp |= 0x1f & (SPI.transfer(0)>>3); - - *y = tmp; - - T_CS_H; - - spi_end_touch(); - - return true; -} - -/*************************************************************************************** -** Function name: getTouchRawZ -** Description: read raw pressure on touchpad and return Z value. -***************************************************************************************/ -uint16_t TFT_eSPI::getTouchRawZ(void){ - CS_H; - - spi_begin_touch(); - - T_CS_L; - - // Z sample request - uint16_t tz = 0xFFF; - SPI.transfer(0xb1); - tz += SPI.transfer16(0xc1) >> 3; - tz -= SPI.transfer16(0x91) >> 3; - - T_CS_H; - - spi_end_touch(); - - return tz; -} - -/*************************************************************************************** -** Function name: validTouch -** Description: read validated position. Return false if not pressed. -***************************************************************************************/ -#define _RAWERR 10 // Deadband in position samples -uint8_t TFT_eSPI::validTouch(uint16_t *x, uint16_t *y, uint16_t threshold){ - uint16_t x_tmp, y_tmp, x_tmp2, y_tmp2; - - // Wait until pressure stops increasing - uint16_t z1 = 1; - uint16_t z2 = 0; - while (z1 > z2) - { - z2 = z1; - z1 = getTouchRawZ(); - delay(1); - } - - // Serial.print("Z = ");Serial.println(z1); - - if (z1 <= threshold) return false; - - getTouchRaw(&x_tmp,&y_tmp); - - // Serial.print("Sample 1 x,y = "); Serial.print(x_tmp);Serial.print(",");Serial.print(y_tmp); - // Serial.print(", Z = ");Serial.println(z1); - - delay(1); // Small delay to the next sample - if (getTouchRawZ() <= threshold) return false; - - delay(2); // Small delay to the next sample - getTouchRaw(&x_tmp2,&y_tmp2); - - // Serial.print("Sample 2 x,y = "); Serial.print(x_tmp2);Serial.print(",");Serial.println(y_tmp2); - // Serial.print("Sample difference = ");Serial.print(abs(x_tmp - x_tmp2));Serial.print(",");Serial.println(abs(y_tmp - y_tmp2)); - - if (abs(x_tmp - x_tmp2) > _RAWERR) return false; - if (abs(y_tmp - y_tmp2) > _RAWERR) return false; - - *x = x_tmp; - *y = y_tmp; - - return true; -} - -/*************************************************************************************** -** Function name: getTouch -** Description: read callibrated position. Return false if not pressed. -***************************************************************************************/ -#define Z_THRESHOLD 350 // Touch pressure threshold for validating touches -uint8_t TFT_eSPI::getTouch(uint16_t *x, uint16_t *y, uint16_t threshold){ - uint16_t x_tmp, y_tmp, xx, yy; - - if (threshold<20) threshold = 20; - if (_pressTime > millis()) threshold=20; - - uint8_t n = 5; - uint8_t valid = 0; - while (n--) - { - if (validTouch(&x_tmp, &y_tmp, threshold)) valid++;; - } - - if (valid<1) { _pressTime = 0; return false; } - - _pressTime = millis() + 50; - - if(!touchCalibration_rotate){ - xx=(x_tmp-touchCalibration_x0)*_width/touchCalibration_x1; - yy=(y_tmp-touchCalibration_y0)*_height/touchCalibration_y1; - if(touchCalibration_invert_x) - xx = _width - xx; - if(touchCalibration_invert_y) - yy = _height - yy; - } else { - yy=(x_tmp-touchCalibration_x0)*_height/touchCalibration_x1; - xx=(y_tmp-touchCalibration_y0)*_width/touchCalibration_y1; - if(touchCalibration_invert_x) - xx = _width - xx; - if(touchCalibration_invert_y) - yy = _height - yy; - } - - if (xx >= _width || yy >= _height) return valid; - - _pressX = xx; - _pressY = yy; - *x = _pressX; - *y = _pressY; - return valid; -} - -/*************************************************************************************** -** Function name: calibrateTouch -** Description: generates calibration parameters for touchscreen. -***************************************************************************************/ -void TFT_eSPI::calibrateTouch(uint16_t *parameters, uint32_t color_fg, uint32_t color_bg, uint8_t size){ - int16_t values[] = {0,0,0,0,0,0,0,0}; - uint16_t x_tmp, y_tmp; - - - - for(uint8_t i = 0; i<4; i++){ - fillRect(0, 0, size+1, size+1, color_bg); - fillRect(0, _height-size-1, size+1, size+1, color_bg); - fillRect(_width-size-1, 0, size+1, size+1, color_bg); - fillRect(_width-size-1, _height-size-1, size+1, size+1, color_bg); - - if (i == 5) break; // used to clear the arrows - - switch (i) { - case 0: // up left - drawLine(0, 0, 0, size, color_fg); - drawLine(0, 0, size, 0, color_fg); - drawLine(0, 0, size , size, color_fg); - break; - case 1: // bot left - drawLine(0, _height-size-1, 0, _height-1, color_fg); - drawLine(0, _height-1, size, _height-1, color_fg); - drawLine(size, _height-size-1, 0, _height-1 , color_fg); - break; - case 2: // up right - drawLine(_width-size-1, 0, _width-1, 0, color_fg); - drawLine(_width-size-1, size, _width-1, 0, color_fg); - drawLine(_width-1, size, _width-1, 0, color_fg); - break; - case 3: // bot right - drawLine(_width-size-1, _height-size-1, _width-1, _height-1, color_fg); - drawLine(_width-1, _height-1-size, _width-1, _height-1, color_fg); - drawLine(_width-1-size, _height-1, _width-1, _height-1, color_fg); - break; - } - - // user has to get the chance to release - if(i>0) delay(1000); - - for(uint8_t j= 0; j<8; j++){ - // Use a lower detect threshold as corners tend to be less sensitive - while(!validTouch(&x_tmp, &y_tmp, Z_THRESHOLD/4)); - values[i*2 ] += x_tmp; - values[i*2+1] += y_tmp; - } - values[i*2 ] /= 8; - values[i*2+1] /= 8; - } - - - - // check orientation - // from case 0 to case 1, the y value changed. - // If the measured delta of the touch x axis is bigger than the delta of the y axis, the touch and TFT axes are switched. - touchCalibration_rotate = false; - if(abs(values[0]-values[2]) > abs(values[1]-values[3])){ - touchCalibration_rotate = true; - touchCalibration_x0 = (values[0] + values[4])/2; // calc min x - touchCalibration_x1 = (values[2] + values[6])/2; // calc max x - touchCalibration_y0 = (values[1] + values[3])/2; // calc min y - touchCalibration_y1 = (values[5] + values[7])/2; // calc max y - } else { - touchCalibration_x0 = (values[0] + values[2])/2; // calc min x - touchCalibration_x1 = (values[4] + values[6])/2; // calc max x - touchCalibration_y0 = (values[1] + values[5])/2; // calc min y - touchCalibration_y1 = (values[3] + values[7])/2; // calc max y - } - - // in addition, the touch screen axis could be in the opposit direction of the TFT axis - touchCalibration_invert_x = false; - if(touchCalibration_x0 > touchCalibration_x1){ - values[0]=touchCalibration_x0; - touchCalibration_x0 = touchCalibration_x1; - touchCalibration_x1 = values[0]; - touchCalibration_invert_x = true; - } - touchCalibration_invert_y = false; - if(touchCalibration_y0 > touchCalibration_y1){ - values[0]=touchCalibration_y0; - touchCalibration_y0 = touchCalibration_y1; - touchCalibration_y1 = values[0]; - touchCalibration_invert_y = true; - } - - // pre calculate - touchCalibration_x1 -= touchCalibration_x0; - touchCalibration_y1 -= touchCalibration_y0; - - if(touchCalibration_x0 == 0) touchCalibration_x0 = 1; - if(touchCalibration_x1 == 0) touchCalibration_x1 = 1; - if(touchCalibration_y0 == 0) touchCalibration_y0 = 1; - if(touchCalibration_y1 == 0) touchCalibration_y1 = 1; - - // export parameters, if pointer valid - if(parameters != NULL){ - parameters[0] = touchCalibration_x0; - parameters[1] = touchCalibration_x1; - parameters[2] = touchCalibration_y0; - parameters[3] = touchCalibration_y1; - parameters[4] = touchCalibration_rotate | (touchCalibration_invert_x <<1) | (touchCalibration_invert_y <<2); - } -} - - -/*************************************************************************************** -** Function name: setTouch -** Description: imports calibration parameters for touchscreen. -***************************************************************************************/ -void TFT_eSPI::setTouch(uint16_t *parameters){ - touchCalibration_x0 = parameters[0]; - touchCalibration_x1 = parameters[1]; - touchCalibration_y0 = parameters[2]; - touchCalibration_y1 = parameters[3]; - - if(touchCalibration_x0 == 0) touchCalibration_x0 = 1; - if(touchCalibration_x1 == 0) touchCalibration_x1 = 1; - if(touchCalibration_y0 == 0) touchCalibration_y0 = 1; - if(touchCalibration_y1 == 0) touchCalibration_y1 = 1; - - touchCalibration_rotate = parameters[4] & 0x01; - touchCalibration_invert_x = parameters[4] & 0x02; - touchCalibration_invert_y = parameters[4] & 0x04; -} - -#else // TOUCH CS is not defined so generate dummy functions that do nothing - -/*************************************************************************************** -** Function name: getTouchRaw -** Description: read raw touch position. Return false if not pressed. -***************************************************************************************/ -uint8_t TFT_eSPI::getTouchRaw(uint16_t *x, uint16_t *y){ - return true; -} - -/*************************************************************************************** -** Function name: getTouchRawZ -** Description: read raw pressure on touchpad and return Z value. -***************************************************************************************/ -uint16_t TFT_eSPI::getTouchRawZ(void){ - return true; -} - -/*************************************************************************************** -** Function name: validTouch -** Description: read validated position. Return false if not pressed. -***************************************************************************************/ -uint8_t TFT_eSPI::validTouch(uint16_t *x, uint16_t *y, uint16_t threshold){ - return true; -} - -/*************************************************************************************** -** Function name: getTouch -** Description: read callibrated position. Return false if not pressed. -***************************************************************************************/ -uint8_t TFT_eSPI::getTouch(uint16_t *x, uint16_t *y, uint16_t threshold){ - return true; -} - -/*************************************************************************************** -** Function name: calibrateTouch -** Description: generates calibration parameters for touchscreen. -***************************************************************************************/ -void TFT_eSPI::calibrateTouch(uint16_t *parameters, uint32_t color_bg, uint32_t color_fg, uint8_t size){ -} - - -/*************************************************************************************** -** Function name: setTouch -** Description: imports calibration parameters for touchscreen. -***************************************************************************************/ -void TFT_eSPI::setTouch(uint16_t *parameters){ -} - -#endif // TOUCH_CS - - - -/*************************************************************************************** -** Code for the GFX button UI element -** Grabbed from Adafruit_GFX library and enhanced to handle any label font -***************************************************************************************/ -TFT_eSPI_Button::TFT_eSPI_Button(void) { - _gfx = 0; -} - -// Classic initButton() function: pass center & size -void TFT_eSPI_Button::initButton( - TFT_eSPI *gfx, int16_t x, int16_t y, uint16_t w, uint16_t h, - uint16_t outline, uint16_t fill, uint16_t textcolor, - char *label, uint8_t textsize) -{ - // Tweak arguments and pass to the newer initButtonUL() function... - initButtonUL(gfx, x - (w / 2), y - (h / 2), w, h, outline, fill, - textcolor, label, textsize); -} - -// Newer function instead accepts upper-left corner & size -void TFT_eSPI_Button::initButtonUL( - TFT_eSPI *gfx, int16_t x1, int16_t y1, uint16_t w, uint16_t h, - uint16_t outline, uint16_t fill, uint16_t textcolor, - char *label, uint8_t textsize) -{ - _x1 = x1; - _y1 = y1; - _w = w; - _h = h; - _outlinecolor = outline; - _fillcolor = fill; - _textcolor = textcolor; - _textsize = textsize; - _gfx = gfx; - strncpy(_label, label, 9); -} - -void TFT_eSPI_Button::drawButton(boolean inverted) { - uint16_t fill, outline, text; - - if(!inverted) { - fill = _fillcolor; - outline = _outlinecolor; - text = _textcolor; - } else { - fill = _textcolor; - outline = _outlinecolor; - text = _fillcolor; - } - - uint8_t r = min(_w, _h) / 4; // Corner radius - _gfx->fillRoundRect(_x1, _y1, _w, _h, r, fill); - _gfx->drawRoundRect(_x1, _y1, _w, _h, r, outline); - - _gfx->setTextColor(text); - _gfx->setTextSize(_textsize); - - uint8_t tempdatum = _gfx->getTextDatum(); - _gfx->setTextDatum(MC_DATUM); - _gfx->drawString(_label, _x1 + (_w/2), _y1 + (_h/2)); - _gfx->setTextDatum(tempdatum); -} - -boolean TFT_eSPI_Button::contains(int16_t x, int16_t y) { - return ((x >= _x1) && (x < (_x1 + _w)) && - (y >= _y1) && (y < (_y1 + _h))); -} - -void TFT_eSPI_Button::press(boolean p) { - laststate = currstate; - currstate = p; -} - -boolean TFT_eSPI_Button::isPressed() { return currstate; } -boolean TFT_eSPI_Button::justPressed() { return (currstate && !laststate); } -boolean TFT_eSPI_Button::justReleased() { return (!currstate && laststate); } - - - - /************************************************************************************** -// The following class creates Sprites in RAM, graphics can then be drawn in the Sprite -// and rendered quickly onto the TFT screen. The class inherits the graphics functions -// from the TFT_eSPI class. Some functions are overridden by this class so that the -// graphics are written to the Sprite rather than the TFT. -// Coded by Bodmer -***************************************************************************************/ -/*************************************************************************************** -// Color bytes are swapped when writing to RAM, this introduces a small overhead but -// then rendering to screen can use the faster call. In general rendering graphics in -// the Sprite is very fast, but writing to the TFT is slow, so there is a performance -// gain by using swapped bytes. -***************************************************************************************/ - -/*************************************************************************************** -** Function name: TFT_eSprite -** Description: Class constructor -*************************************************************************************x*/ -TFT_eSprite::TFT_eSprite(TFT_eSPI *tft) -{ - _tft = tft; // Pointer to tft class so we can call member functions - - _iwidth = 0; // Initialise width and height to 0 (it does not exist yet) - _iheight = 0; - _bpp16 = true; - _iswapBytes = false; // Do not swap pushImage colour bytes by default - - _created = false; - - _xs = 0; // window bounds for pushColor - _ys = 0; - _xe = 0; - _ye = 0; - - _xptr = 0; // pushColor coordinate - _yptr = 0; - - _icursor_y = _icursor_x = 0; // Text cursor position -} - - -/*************************************************************************************** -** Function name: createSprite -** Description: Create a sprite (bitmap) of defined width and height -*************************************************************************************x*/ -// returns a uint8_t* pointer, cast returned value to (uint16_t*) for 16 bit colours -uint8_t* TFT_eSprite::createSprite(int16_t w, int16_t h) -{ - - if ( _created ) - { - if ( _bpp16 ) return ( uint8_t*)_img; - return _img8; - } - - if ( w < 1 || h < 1 ) return NULL; - - _iwidth = w; - _iheight = h; - - _sx = 0; - _sy = 0; - _sw = w; - _sh = h; - _scolor = TFT_BLACK; - - // Add one extra "off screen" pixel to point out-of-bounds setWindow() coordinates - // this means push/writeColor functions do not need additional bounds checks. - if(_bpp16) - { - _img = (uint16_t*) malloc(w * h * 2 + 1); - if (_img) - { - _created = true; - fillSprite(TFT_BLACK); - return (uint8_t*)_img; - } - } - else - { - _img8 = ( uint8_t*) malloc(w * h + 1); - if (_img8) - { - _created = true; - fillSprite(TFT_BLACK); - return _img8; - } - } - - return NULL; -} - - -/*************************************************************************************** -** Function name: setDepth -** Description: Set bits per pixel for colour (8 or 16) -*************************************************************************************x*/ - -uint8_t* TFT_eSprite::setColorDepth(int8_t b) -{ - // Can't change an existing sprite's colour depth so delete it - if (_created) - { - if (_bpp16) free(_img); - else free(_img8); - } - - // Now define the new colour depth - if ( b > 8 ) _bpp16 = true; // Bytes per pixel - else _bpp16 = false; - - // If it existed, re-create the sprite with the new colour depth - if (_created) - { - _created = false; - return createSprite(_iwidth, _iheight); - } - - return NULL; -} - - -/*************************************************************************************** -** Function name: deleteSprite -** Description: Delete the sprite to free up memory (RAM) -*************************************************************************************x*/ -void TFT_eSprite::deleteSprite(void) -{ - if (!_created ) return; - - if (_bpp16) free(_img); - else free(_img8); - - _created = false; -} - - -/*************************************************************************************** -** Function name: pushSprite -** Description: Push the sprite to the TFT at x, y -*************************************************************************************x*/ -void TFT_eSprite::pushSprite(int32_t x, int32_t y) -{ - if (!_created ) return; - - if (_bpp16) _tft->pushImage(x, y, _iwidth, _iheight, _img ); - else _tft->pushImage(x, y, _iwidth, _iheight, _img8); -} - - -/*************************************************************************************** -** Function name: pushSprite -** Description: Push the sprite to the TFT at x, y with transparent colour -*************************************************************************************x*/ -void TFT_eSprite::pushSprite(int32_t x, int32_t y, uint16_t transp) -{ - if (!_created ) return; - - if (_bpp16) _tft->pushImage(x, y, _iwidth, _iheight, _img, transp ); - else - { - transp = (uint8_t)((transp & 0xE000)>>8 | (transp & 0x0700)>>6 | (transp & 0x0018)>>3); - _tft->pushImage(x, y, _iwidth, _iheight, _img8, (uint8_t)transp); - } -} - - -/*************************************************************************************** -** Function name: readPixel -** Description: Read 565 colour of a pixel at defined coordinates -*************************************************************************************x*/ -uint16_t TFT_eSprite::readPixel(int32_t x, int32_t y) -{ - if (!_created ) return 0; - - if (_bpp16) - { - uint16_t color = _img[x + y * _iwidth]; - return (color >> 8) | (color << 8); - } - - uint16_t color = _img8[x + y * _iwidth]; - if (color != 0) { - uint8_t blue[] = {0, 11, 21, 31}; - color = (color & 0xE0)<<8 | (color & 0xC0)<<5 - | (color & 0x1C)<<6 | (color & 0x1C)<<3 - | blue[color & 0x03]; - } - - return color; -} - - -/*************************************************************************************** -** Function name: pushImage -** Description: push 565 colour image into a defined area of a sprite -*************************************************************************************x*/ -void TFT_eSprite::pushImage(int32_t x, int32_t y, uint32_t w, uint32_t h, uint16_t *data) -{ - if ((x > _iwidth) || (y > _iheight) || (w == 0) || (h == 0) || !_created) return; - - if (_bpp16) - { - for (uint32_t yp = y; yp < y + h; yp++) - { - for (uint32_t xp = x; xp < x + w; xp++) - { - uint16_t color = *data++; - if(!_iswapBytes) color = color<<8 | color>>8; - _img[xp + yp * _iwidth] = color; - } - } - } - else - { - for (uint32_t yp = y; yp < y + h; yp++) - { - for (uint32_t xp = x; xp < x + w; xp++) - { - uint16_t color = *data++; - if(_iswapBytes) color = color<<8 | color>>8; - _img8[xp + yp * _iwidth] = (uint8_t)((color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3); - } - } - } -} - - -/*************************************************************************************** -** Function name: pushImage -** Description: push 565 colour FLASH (PROGMEM) image into a defined area -*************************************************************************************x*/ -void TFT_eSprite::pushImage(int32_t x, int32_t y, uint32_t w, uint32_t h, const uint16_t *data) -{ - if ((x > _iwidth) || (y > _iheight) || (w == 0) || (h == 0) || !_created) return; - - if (_bpp16) - { - for (uint32_t yp = y; yp < y + h; yp++) - { - for (uint32_t xp = x; xp < x + w; xp++) - { - uint16_t color = pgm_read_word(data++); - if(!_iswapBytes) color = color<<8 | color>>8; - _img[xp + yp * _iwidth] = color; - } - } - } - else - { - for (uint32_t yp = y; yp < y + h; yp++) - { - for (uint32_t xp = x; xp < x + w; xp++) - { - uint16_t color = pgm_read_word(data++); - if(_iswapBytes) color = color<<8 | color>>8; - _img8[xp + yp * _iwidth] = (uint8_t)((color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3); - } - } - } -} - - -/*************************************************************************************** -** Function name: setSwapBytes -** Description: Used by 16 bit pushImage() to swap byte order in colours -***************************************************************************************/ -void TFT_eSprite::setSwapBytes(bool swap) -{ - _iswapBytes = swap; -} - - -/*************************************************************************************** -** Function name: getSwapBytes -** Description: Return the swap byte order for colours -***************************************************************************************/ -bool TFT_eSprite::getSwapBytes(void) -{ - return _iswapBytes; -} - - -/*************************************************************************************** -** Function name: setWindow -** Description: Set the bounds of a window for pushColor and writeColor -*************************************************************************************x*/ -void TFT_eSprite::setWindow(int32_t x0, int32_t y0, int32_t x1, int32_t y1) -{ - bool duff_coord = false; - - if (x0 > x1) swap_coord(x0, x1); - if (y0 > y1) swap_coord(y0, y1); - - if (x0 < 0) x0 = 0; - if (x0 >= _iwidth) duff_coord = true; - if (x1 < 0) x1 = 0; - if (x1 >= _iwidth) x1 = _iwidth - 1; - - if (y0 < 0) y0 = 0; - if (y0 >= _iheight) duff_coord = true; - if (y1 < 0) y1 = 0; - if (y1 >= _iheight) y1 = _iheight - 1; - - if (duff_coord) - { // Point to that extra "off screen" pixel - _xs = 0; - _ys = _iheight; - _xe = 0; - _ye = _iheight; - } - else - { - _xs = x0; - _ys = y0; - _xe = x1; - _ye = y1; - } - - _xptr = _xs; - _yptr = _ys; -} - - -/*************************************************************************************** -** Function name: pushColor -** Description: Send a new pixel to the set window -*************************************************************************************x*/ -void TFT_eSprite::pushColor(uint32_t color) -{ - if (!_created ) return; - - // Write the colour to RAM in set window - if (_bpp16) - _img [_xptr + _yptr * _iwidth] = (uint16_t) (color >> 8) | (color << 8); - - else - _img8[_xptr + _yptr * _iwidth] = (uint8_t )((color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3); - - // Increment x - _xptr++; - - // Wrap on x and y to start, increment y if needed - if (_xptr > _xe) - { - _xptr = _xs; - _yptr++; - if (_yptr > _ye) _yptr = _ys; - } - -} - - -/*************************************************************************************** -** Function name: pushColor -** Description: Send a "len" new pixels to the set window -*************************************************************************************x*/ -void TFT_eSprite::pushColor(uint32_t color, uint16_t len) -{ - if (!_created ) return; - - uint16_t pixelColor; - if (_bpp16) - pixelColor = (uint16_t) (color >> 8) | (color << 8); - - else - pixelColor = (color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3; - - while(len--) writeColor(pixelColor); -} - - -/*************************************************************************************** -** Function name: writeColor -** Description: Write a pixel with pre-formatted colour to the set window -*************************************************************************************x*/ -void TFT_eSprite::writeColor(uint16_t color) -{ - if (!_created ) return; - - // Write 16 bit RGB 565 encoded colour to RAM - if (_bpp16) _img [_xptr + _yptr * _iwidth] = color; - - // Write 8 bit RGB 332 encoded colour to RAM - else _img8[_xptr + _yptr * _iwidth] = (uint8_t) color; - - // Increment x - _xptr++; - - // Wrap on x and y to start, increment y if needed - if (_xptr > _xe) - { - _xptr = _xs; - _yptr++; - if (_yptr > _ye) _yptr = _ys; - } -} - - -/*************************************************************************************** -** Function name: setScrollRect -** Description: Set scroll area within the sprite and the gap fill colour -*************************************************************************************x*/ -void TFT_eSprite::setScrollRect(int32_t x, int32_t y, uint32_t w, uint32_t h, uint16_t color) -{ - if ((x >= _iwidth) || (y >= _iheight) || !_created ) return; - - if (x < 0) x = 0; - if (y < 0) y = 0; - - if ((x + w) > _iwidth ) w = _iwidth - x; - if ((y + h) > _iheight) h = _iheight - y; - - if ( w < 1 || h < 1) return; - - _sx = x; - _sy = y; - _sw = w; - _sh = h; - - _scolor = color; -} - - -/*************************************************************************************** -** Function name: scroll -** Description: Scroll dx,dy pixels, positive right,down, negative left,up -*************************************************************************************x*/ -void TFT_eSprite::scroll(int16_t dx, int16_t dy) -{ - if (abs(dx) >= _sw || abs(dy) >= _sh) - { - fillRect (_sx, _sy, _sw, _sh, _scolor); - return; - } - - // Fetch the scroll area width and height set by setScrollRect() - uint32_t w = _sw - abs(dx); // line width to copy - uint32_t h = _sh - abs(dy); // lines to copy - int32_t iw = _iwidth; // width of sprite - - // Fetch the x,y origin set by setScrollRect() - uint32_t tx = _sx; // to x - uint32_t fx = _sx; // from x - uint32_t ty = _sy; // to y - uint32_t fy = _sy; // from y - - // Adjust for x delta - if (dx <= 0) fx -= dx; - else tx += dx; - - // Adjust for y delta - if (dy <= 0) fy -= dy; - else - { // Scrolling down so start copy from bottom - ty = ty + _sh - 1; // "To" pointer - iw = -iw; // Pointer moves backwards - fy = ty - dy; // "From" pointer - } - - // Calculate "from y" and "to y" pointers in RAM - uint32_t fyp = fx + fy * _iwidth; - uint32_t typ = tx + ty * _iwidth; - - // Now move the pixels in RAM - if (_bpp16) - { - while (h--) - { // move pixel lines (to, from, byte count) - memmove( _img + typ, _img + fyp, w<<1); - typ += iw; - fyp += iw; - } - } - else - { - while (h--) - { // move pixel lines (to, from, byte count) - memmove( _img8 + typ, _img8 + fyp, w); - typ += iw; - fyp += iw; - } - } - - // Fill the gap left by the scrolling - if (dx > 0) fillRect(_sx, _sy, dx, _sh, _scolor); - if (dx < 0) fillRect(_sx + _sw + dx, _sy, -dx, _sh, _scolor); - if (dy > 0) fillRect(_sx, _sy, _sw, dy, _scolor); - if (dy < 0) fillRect(_sx, _sy + _sh + dy, _sw, -dy, _scolor); -} - - -/*************************************************************************************** -** Function name: fillSprite -** Description: Fill the whole sprite with defined colour -*************************************************************************************x*/ -void TFT_eSprite::fillSprite(uint32_t color) -{ - if (!_created ) return; - - // Use memset if possible as it is super fast - if(( (uint8_t)color == (uint8_t)(color>>8) ) && _bpp16) - memset(_img, (uint8_t)color, _iwidth * _iheight * 2); - else if (!_bpp16) - { - color = (color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3; - memset(_img8, (uint8_t)color, _iwidth * _iheight); - } - - else fillRect(0, 0, _iwidth, _iheight, color); -} - - -/*************************************************************************************** -** Function name: setCursor -** Description: Set the sprite text cursor x,y position -*************************************************************************************x*/ -void TFT_eSprite::setCursor(int16_t x, int16_t y) -{ - _icursor_x = x; - _icursor_y = y; -} - - -/*************************************************************************************** -** Function name: width -** Description: Return the width of sprite -*************************************************************************************x*/ -// Return the size of the display -int16_t TFT_eSprite::width(void) -{ - if (!_created ) return 0; - return _iwidth; -} - - -/*************************************************************************************** -** Function name: height -** Description: Return the height of sprite -*************************************************************************************x*/ -int16_t TFT_eSprite::height(void) -{ - if (!_created ) return 0; - return _iheight; -} - - -/*************************************************************************************** -** Function name: drawChar -** Description: draw a single character in the Adafruit GLCD or freefont -*************************************************************************************x*/ -void TFT_eSprite::drawChar(int32_t x, int32_t y, unsigned char c, uint32_t color, uint32_t bg, uint8_t size) -{ - if (!_created ) return; - - if ((x >= _iwidth) || // Clip right - (y >= _iheight) || // Clip bottom - ((x + 6 * size - 1) < 0) || // Clip left - ((y + 8 * size - 1) < 0)) // Clip top - return; - -#ifdef LOAD_GLCD -//>>>>>>>>>>>>>>>>>> -#ifdef LOAD_GFXFF - if(!gfxFont) { // 'Classic' built-in font -#endif -//>>>>>>>>>>>>>>>>>> - - boolean fillbg = (bg != color); - - if ((size==1) && fillbg) - { - uint8_t column[6]; - uint8_t mask = 0x1; - - for (int8_t i = 0; i < 5; i++ ) column[i] = pgm_read_byte(font + (c * 5) + i); - column[5] = 0; - - int8_t j, k; - for (j = 0; j < 8; j++) { - for (k = 0; k < 5; k++ ) { - if (column[k] & mask) { - drawPixel(x + k, y + j, color); - } - else { - drawPixel(x + k, y + j, bg); - } - } - - mask <<= 1; - - drawPixel(x + k, y + j, bg); - } - } - else - { - for (int8_t i = 0; i < 6; i++ ) { - uint8_t line; - if (i == 5) - line = 0x0; - else - line = pgm_read_byte(font + (c * 5) + i); - - if (size == 1) // default size - { - for (int8_t j = 0; j < 8; j++) { - if (line & 0x1) drawPixel(x + i, y + j, color); - line >>= 1; - } - } - else { // big size - for (int8_t j = 0; j < 8; j++) { - if (line & 0x1) fillRect(x + (i * size), y + (j * size), size, size, color); - else if (fillbg) fillRect(x + i * size, y + j * size, size, size, bg); - line >>= 1; - } - } - } - } - -//>>>>>>>>>>>>>>>>>>>>>>>>>>> -#ifdef LOAD_GFXFF - } else { // Custom font -#endif -//>>>>>>>>>>>>>>>>>>>>>>>>>>> -#endif // LOAD_GLCD - -#ifdef LOAD_GFXFF - // Filter out bad characters not present in font - if ((c >= (uint8_t)pgm_read_byte(&gfxFont->first)) && (c <= (uint8_t)pgm_read_byte(&gfxFont->last ))) - { -//>>>>>>>>>>>>>>>>>>>>>>>>>>> - - c -= pgm_read_byte(&gfxFont->first); - GFXglyph *glyph = &(((GFXglyph *)pgm_read_dword(&gfxFont->glyph))[c]); - uint8_t *bitmap = (uint8_t *)pgm_read_dword(&gfxFont->bitmap); - - uint16_t bo = pgm_read_word(&glyph->bitmapOffset); - uint8_t w = pgm_read_byte(&glyph->width), - h = pgm_read_byte(&glyph->height), - xa = pgm_read_byte(&glyph->xAdvance); - int8_t xo = pgm_read_byte(&glyph->xOffset), - yo = pgm_read_byte(&glyph->yOffset); - uint8_t xx, yy, bits, bit=0; - int16_t xo16 = 0, yo16 = 0; - - if(size > 1) { - xo16 = xo; - yo16 = yo; - } - - uint16_t hpc = 0; // Horizontal foreground pixel count - for(yy=0; yy>= 1; - } - // Draw pixels for this line as we are about to increment yy - if (hpc) { - if(size == 1) drawFastHLine(x+xo+xx-hpc, y+yo+yy, hpc, color); - else fillRect(x+(xo16+xx-hpc)*size, y+(yo16+yy)*size, size*hpc, size, color); - hpc=0; - } - } - } + #include "Extensions/Touch.cpp" #endif +#include "Extensions/Button.cpp" -#ifdef LOAD_GLCD - #ifdef LOAD_GFXFF - } // End classic vs custom font - #endif +#include "Extensions/Sprite.cpp" + +#ifdef SMOOTH_FONT + #include "Extensions/Smooth_font.cpp" #endif -} - - -/*************************************************************************************** -** Function name: drawPixel -** Description: push a single pixel at an arbitrary position -*************************************************************************************x*/ -void TFT_eSprite::drawPixel(uint32_t x, uint32_t y, uint32_t color) -{ - // x and y are unsigned so that -ve coordinates turn into large positive ones - // this make bounds checking a bit faster - if ((x >= _iwidth) || (y >= _iheight) || !_created) return; - - if (_bpp16) - { - color = (color >> 8) | (color << 8); - _img[x+y*_iwidth] = (uint16_t) color; - } - else - { - _img8[x+y*_iwidth] = (uint8_t)((color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3); - } -} - - -/*************************************************************************************** -** Function name: drawLine -** Description: draw a line between 2 arbitrary points -*************************************************************************************x*/ -void TFT_eSprite::drawLine(int32_t x0, int32_t y0, int32_t x1, int32_t y1, uint32_t color) -{ - if (!_created ) return; - - boolean steep = abs(y1 - y0) > abs(x1 - x0); - if (steep) { - swap_coord(x0, y0); - swap_coord(x1, y1); - } - - if (x0 > x1) { - swap_coord(x0, x1); - swap_coord(y0, y1); - } - - int32_t dx = x1 - x0, dy = abs(y1 - y0);; - - int32_t err = dx >> 1, ystep = -1, xs = x0, dlen = 0; - - if (y0 < y1) ystep = 1; - - // Split into steep and not steep for FastH/V separation - if (steep) { - for (; x0 <= x1; x0++) { - dlen++; - err -= dy; - if (err < 0) { - err += dx; - if (dlen == 1) drawPixel(y0, xs, color); - else drawFastVLine(y0, xs, dlen, color); - dlen = 0; y0 += ystep; xs = x0 + 1; - } - } - if (dlen) drawFastVLine(y0, xs, dlen, color); - } - else - { - for (; x0 <= x1; x0++) { - dlen++; - err -= dy; - if (err < 0) { - err += dx; - if (dlen == 1) drawPixel(xs, y0, color); - else drawFastHLine(xs, y0, dlen, color); - dlen = 0; y0 += ystep; xs = x0 + 1; - } - } - if (dlen) drawFastHLine(xs, y0, dlen, color); - } -} - - -/*************************************************************************************** -** Function name: drawFastVLine -** Description: draw a vertical line -*************************************************************************************x*/ -void TFT_eSprite::drawFastVLine(int32_t x, int32_t y, int32_t h, uint32_t color) -{ - - if ((x < 0) || (x >= _iwidth) || (y >= _iheight) || !_created) return; - - if (y < 0) { h += y; y = 0; } - - if ((y + h) > _iheight) h = _iheight - y; - - if (h < 1) return; - - if (_bpp16) - { - color = (color >> 8) | (color << 8); - int32_t yp = x + _iwidth * y; - while (h--) {_img[yp] = (uint16_t) color; yp += _iwidth;} - } - else - { - color = (color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3; - while (h--) _img8[x + _iwidth * y++] = (uint8_t) color; - } -} - - -/*************************************************************************************** -** Function name: drawFastHLine -** Description: draw a horizontal line -*************************************************************************************x*/ -void TFT_eSprite::drawFastHLine(int32_t x, int32_t y, int32_t w, uint32_t color) -{ - - if ((y < 0) || (x >= _iwidth) || (y >= _iheight) || !_created) return; - - if (x < 0) { w += x; x = 0; } - - if ((x + w) > _iwidth) w = _iwidth - x; - - if (w < 1) return; - - if (_bpp16) - { - color = (color >> 8) | (color << 8); - while (w--) _img[_iwidth * y + x++] = (uint16_t) color; - } - else - { - color = (color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3; - memset(_img8+_iwidth * y + x, (uint8_t)color, w); - } -} - - -/*************************************************************************************** -** Function name: fillRect -** Description: draw a filled rectangle -*************************************************************************************x*/ -void TFT_eSprite::fillRect(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color) -{ - if (!_created ) return; - - if (x < 0) { w += x; x = 0; } - - if ((x < 0) || (y < 0) || (x >= _iwidth) || (y >= _iheight)) return; - if ((x + w) > _iwidth) w = _iwidth - x; - if ((y + h) > _iheight) h = _iheight - y; - if ((w < 1) || (h < 1)) return; - - int32_t yp = _iwidth * y + x; - - if (_bpp16) - { - color = (color >> 8) | (color << 8); - uint32_t iw = w; - int32_t ys = yp; - if(h--) {while (iw--) _img[yp++] = (uint16_t) color;} - yp = ys; - while (h--) - { - yp += _iwidth; - memcpy( _img+yp, _img+ys, w<<1); - } - } - else - { - color = (color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3; - while (h--) - { - memset(_img8 + yp, (uint8_t)color, w); - yp += _iwidth; - } - } -} - - -/*************************************************************************************** -** Function name: write -** Description: draw characters piped through serial stream -*************************************************************************************x*/ -size_t TFT_eSprite::write(uint8_t utf8) -{ - if (!_created ) return 0; - - if (utf8 == '\r') return 1; - - uint8_t uniCode = utf8; // Work with a copy - if (utf8 == '\n') uniCode+=22; // Make it a valid space character to stop errors - else if (utf8 < 32) return 0; - - uint16_t width = 0; - uint16_t height = 0; - -//vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv DEBUG vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv - //Serial.print((uint8_t) uniCode); // Debug line sends all printed TFT text to serial port - //Serial.println(uniCode, HEX); // Debug line sends all printed TFT text to serial port - //delay(5); // Debug optional wait for serial port to flush through -//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ DEBUG ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< -#ifdef LOAD_GFXFF - if(!gfxFont) { -#endif -//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< - -#ifdef LOAD_FONT2 - if (textfont == 2) - { - if (utf8 > 127) return 0; - // This is 20us faster than using the fontdata structure (0.443ms per character instead of 0.465ms) - width = pgm_read_byte(widtbl_f16 + uniCode-32); - height = chr_hgt_f16; - // Font 2 is rendered in whole byte widths so we must allow for this - width = (width + 6) / 8; // Width in whole bytes for font 2, should be + 7 but must allow for font width change - width = width * 8; // Width converted back to pixles - } - #ifdef LOAD_RLE - else - #endif -#endif - -#ifdef LOAD_RLE - { - if ((textfont>2) && (textfont<9)) - { - if (utf8 > 127) return 0; - // Uses the fontinfo struct array to avoid lots of 'if' or 'switch' statements - // A tad slower than above but this is not significant and is more convenient for the RLE fonts - width = pgm_read_byte( (uint8_t *)pgm_read_dword( &(fontdata[textfont].widthtbl ) ) + uniCode-32 ); - height= pgm_read_byte( &fontdata[textfont].height ); - } - } -#endif - -#ifdef LOAD_GLCD - if (textfont==1) - { - width = 6; - height = 8; - } -#else - if (textfont==1) return 0; -#endif - - height = height * textsize; - - if (utf8 == '\n') - { - _icursor_y += height; - _icursor_x = 0; - } - else - { - if (textwrap && (_icursor_x + width * textsize > _iwidth)) - { - _icursor_y += height; - _icursor_x = 0; - } - if (_icursor_y >= _iheight) _icursor_y = 0; - _icursor_x += drawChar(uniCode, _icursor_x, _icursor_y, textfont); - } - -//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< -#ifdef LOAD_GFXFF - } // Custom GFX font - else - { - - if(utf8 == '\n') { - _icursor_x = 0; - _icursor_y += (int16_t)textsize * (uint8_t)pgm_read_byte(&gfxFont->yAdvance); - } else { - if (uniCode > (uint8_t)pgm_read_byte(&gfxFont->last )) return 0; - if (uniCode < (uint8_t)pgm_read_byte(&gfxFont->first)) return 0; - - uint8_t c2 = uniCode - pgm_read_byte(&gfxFont->first); - GFXglyph *glyph = &(((GFXglyph *)pgm_read_dword(&gfxFont->glyph))[c2]); - uint8_t w = pgm_read_byte(&glyph->width), - h = pgm_read_byte(&glyph->height); - if((w > 0) && (h > 0)) { // Is there an associated bitmap? - int16_t xo = (int8_t)pgm_read_byte(&glyph->xOffset); - if(textwrap && ((_icursor_x + textsize * (xo + w)) > _iwidth)) { - // Drawing character would go off right edge; wrap to new line - _icursor_x = 0; - _icursor_y += (int16_t)textsize * (uint8_t)pgm_read_byte(&gfxFont->yAdvance); - } - if (_icursor_y >= _iheight) _icursor_y = 0; - drawChar(_icursor_x, _icursor_y, uniCode, textcolor, textbgcolor, textsize); - } - _icursor_x += pgm_read_byte(&glyph->xAdvance) * (int16_t)textsize; - } - } -#endif // LOAD_GFXFF -//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< - - return 1; -} - - -/*************************************************************************************** -** Function name: drawChar -** Description: draw a unicode onto the screen -*************************************************************************************x*/ -int16_t TFT_eSprite::drawChar(unsigned int uniCode, int x, int y) -{ - return drawChar(uniCode, x, y, textfont); -} - -int16_t TFT_eSprite::drawChar(unsigned int uniCode, int x, int y, int font) -{ - if (!_created ) return 0; - - if (font==1) - { -#ifdef LOAD_GLCD - #ifndef LOAD_GFXFF - drawChar(x, y, uniCode, textcolor, textbgcolor, textsize); - return 6 * textsize; - #endif -#else - #ifndef LOAD_GFXFF - return 0; - #endif -#endif - -#ifdef LOAD_GFXFF - drawChar(x, y, uniCode, textcolor, textbgcolor, textsize); - if(!gfxFont) { // 'Classic' built-in font - #ifdef LOAD_GLCD - return 6 * textsize; - #else - return 0; - #endif - } - else - { - if((uniCode >= pgm_read_byte(&gfxFont->first)) && (uniCode <= pgm_read_byte(&gfxFont->last) )) - { - uint8_t c2 = uniCode - pgm_read_byte(&gfxFont->first); - GFXglyph *glyph = &(((GFXglyph *)pgm_read_dword(&gfxFont->glyph))[c2]); - return pgm_read_byte(&glyph->xAdvance) * textsize; - } - else - { - return 0; - } - } -#endif - } - - if ((font>1) && (font<9) && ((uniCode < 32) || (uniCode > 127))) return 0; - - int width = 0; - int height = 0; - uint32_t flash_address = 0; - uniCode -= 32; - -#ifdef LOAD_FONT2 - if (font == 2) - { - // This is faster than using the fontdata structure - flash_address = pgm_read_dword(&chrtbl_f16[uniCode]); - width = pgm_read_byte(widtbl_f16 + uniCode); - height = chr_hgt_f16; - } - #ifdef LOAD_RLE - else - #endif -#endif - -#ifdef LOAD_RLE - { - if ((font>2) && (font<9)) - { - // This is slower than above but is more convenient for the RLE fonts - flash_address = pgm_read_dword( pgm_read_dword( &(fontdata[font].chartbl ) ) + uniCode*sizeof(void *) ); - width = pgm_read_byte( (uint8_t *)pgm_read_dword( &(fontdata[font].widthtbl ) ) + uniCode ); - height= pgm_read_byte( &fontdata[font].height ); - } - } -#endif - - int w = width; - int pX = 0; - int pY = y; - uint8_t line = 0; - -#ifdef LOAD_FONT2 // chop out code if we do not need it - if (font == 2) { - w = w + 6; // Should be + 7 but we need to compensate for width increment - w = w / 8; - if (x + width * textsize >= _iwidth) return width * textsize ; - - for (int i = 0; i < height; i++) - { - if (textcolor != textbgcolor) fillRect(x, pY, width * textsize, textsize, textbgcolor); - - for (int k = 0; k < w; k++) - { - line = pgm_read_byte((uint8_t *)flash_address + w * i + k); - if (line) { - if (textsize == 1) { - pX = x + k * 8; - if (line & 0x80) drawPixel(pX, pY, textcolor); - if (line & 0x40) drawPixel(pX + 1, pY, textcolor); - if (line & 0x20) drawPixel(pX + 2, pY, textcolor); - if (line & 0x10) drawPixel(pX + 3, pY, textcolor); - if (line & 0x08) drawPixel(pX + 4, pY, textcolor); - if (line & 0x04) drawPixel(pX + 5, pY, textcolor); - if (line & 0x02) drawPixel(pX + 6, pY, textcolor); - if (line & 0x01) drawPixel(pX + 7, pY, textcolor); - } - else { - pX = x + k * 8 * textsize; - if (line & 0x80) fillRect(pX, pY, textsize, textsize, textcolor); - if (line & 0x40) fillRect(pX + textsize, pY, textsize, textsize, textcolor); - if (line & 0x20) fillRect(pX + 2 * textsize, pY, textsize, textsize, textcolor); - if (line & 0x10) fillRect(pX + 3 * textsize, pY, textsize, textsize, textcolor); - if (line & 0x08) fillRect(pX + 4 * textsize, pY, textsize, textsize, textcolor); - if (line & 0x04) fillRect(pX + 5 * textsize, pY, textsize, textsize, textcolor); - if (line & 0x02) fillRect(pX + 6 * textsize, pY, textsize, textsize, textcolor); - if (line & 0x01) fillRect(pX + 7 * textsize, pY, textsize, textsize, textcolor); - } - } - } - pY += textsize; - } - } - - #ifdef LOAD_RLE - else - #endif -#endif //FONT2 - -#ifdef LOAD_RLE //674 bytes of code - // Font is not 2 and hence is RLE encoded - { - w *= height; // Now w is total number of pixels in the character - - if (textcolor != textbgcolor) fillRect(x, pY, width * textsize, textsize * height, textbgcolor); - int16_t color; - if (_bpp16) color = (textcolor >> 8) | (textcolor << 8); - else color = ((textcolor & 0xE000)>>8 | (textcolor & 0x0700)>>6 | (textcolor & 0x0018)>>3); - int px = 0, py = pY; // To hold character block start and end column and row values - int pc = 0; // Pixel count - uint8_t np = textsize * textsize; // Number of pixels in a drawn pixel - uint8_t tnp = 0; // Temporary copy of np for while loop - uint8_t ts = textsize - 1; // Temporary copy of textsize - // 16 bit pixel count so maximum font size is equivalent to 180x180 pixels in area - // w is total number of pixels to plot to fill character block - while (pc < w) - { - line = pgm_read_byte((uint8_t *)flash_address); - flash_address++; // 20 bytes smaller by incrementing here - if (line & 0x80) { - line &= 0x7F; - line++; - if (ts) { - px = x + textsize * (pc % width); // Keep these px and py calculations outside the loop as they are slow - py = y + textsize * (pc / width); - } - else { - px = x + pc % width; // Keep these px and py calculations outside the loop as they are slow - py = y + pc / width; - } - while (line--) { - pc++; - setWindow(px, py, px + ts, py + ts); - if (ts) { tnp = np; while (tnp--) writeColor(color); } - else writeColor(color); - - px += textsize; - - if (px >= (x + width * textsize)) - { - px = x; - py += textsize; - } - } - } - else { - line++; - pc += line; - } - } - } - // End of RLE font rendering -#endif - return width * textsize; // x + -} +//////////////////////////////////////////////////////////////////////////////////////// diff --git a/TFT_eSPI.h b/TFT_eSPI.h index bae7afe..8148f59 100644 --- a/TFT_eSPI.h +++ b/TFT_eSPI.h @@ -1,9 +1,9 @@ /*************************************************** - Arduino TFT graphics library targetted at ESP8266 + Arduino TFT graphics library targeted at ESP8266 and ESP32 based boards. This is a standalone library that contains the - hardware driver, the graphics funtions and the + hardware driver, the graphics functions and the proportional fonts. The larger fonts are Run Length Encoded to reduce @@ -74,6 +74,17 @@ #include +#ifdef SMOOTH_FONT + // Call up the SPIFFS FLASH filing system for the anti-aliased fonts + #define FS_NO_GLOBALS + #include + + #ifdef ESP32 + #include "SPIFFS.h" + #endif +#endif + + #if defined (ESP8266) && defined (D0_USED_FOR_DC) #define DC_C digitalWrite(TFT_DC, LOW) #define DC_D digitalWrite(TFT_DC, HIGH) @@ -255,7 +266,11 @@ template static inline void swap_coord(T& a, T& b) { T t = a; a = b; b = t; } -// This is a structure to conveniently hold infomation on the default fonts +#ifndef min +#define min(a,b) (((a) < (b)) ? (a) : (b)) +#endif + +// This is a structure to conveniently hold information on the default fonts // Stores pointer to font character image address table, width table and height // Create a null set in case some fonts not used (to prevent crash) @@ -318,7 +333,6 @@ const PROGMEM fontinfo fontdata [] = { }; - // Class functions and variables class TFT_eSPI : public Print { @@ -343,9 +357,8 @@ class TFT_eSPI : public Print { void setWindow(int16_t x0, int16_t y0, int16_t x1, int16_t y1), pushColor(uint16_t color), pushColor(uint16_t color, uint16_t len), - //pushColors(uint16_t *data, uint8_t len), - pushColors(uint8_t *data, uint32_t len), pushColors(uint16_t *data, uint32_t len, bool swap = true), // With byte swap option + pushColors(uint8_t *data, uint32_t len), fillScreen(uint32_t color); @@ -375,7 +388,7 @@ class TFT_eSPI : public Print { setTextColor(uint16_t fgcolor, uint16_t bgcolor), setTextSize(uint8_t size), - setTextWrap(boolean wrap), + setTextWrap(boolean wrapX, boolean wrapY = false), setTextDatum(uint8_t datum), setTextPadding(uint16_t x_width), @@ -427,13 +440,14 @@ class TFT_eSPI : public Print { uint8_t getRotation(void), getTextDatum(void), - color332(uint16_t color565); // Convert 16 bit colour to 8 bits + color16to8(uint16_t color565); // Convert 16 bit colour to 8 bits int16_t getCursorX(void), getCursorY(void); uint16_t fontsLoaded(void), - color565(uint8_t r, uint8_t g, uint8_t b); + color565(uint8_t r, uint8_t g, uint8_t b), + color8to16(uint8_t color332); // Convert 8 bit colour to 16 bits int16_t drawNumber(long long_num,int poX, int poY, int font), drawNumber(long long_num,int poX, int poY), @@ -462,49 +476,39 @@ class TFT_eSPI : public Print { void setAddrWindow(int32_t xs, int32_t ys, int32_t xe, int32_t ye); - // These are associated with the Touch Screen handlers - uint8_t getTouchRaw(uint16_t *x, uint16_t *y); - uint16_t getTouchRawZ(void); - uint8_t getTouch(uint16_t *x, uint16_t *y, uint16_t threshold = 600); - - void calibrateTouch(uint16_t *data, uint32_t color_fg, uint32_t color_bg, uint8_t size); - void setTouch(uint16_t *data); size_t write(uint8_t); + int32_t cursor_x, cursor_y; + uint32_t textcolor, textbgcolor; + + private: inline void spi_begin() __attribute__((always_inline)); inline void spi_end() __attribute__((always_inline)); - inline void spi_begin_touch() __attribute__((always_inline)); - inline void spi_end_touch() __attribute__((always_inline)); void readAddrWindow(int32_t xs, int32_t ys, int32_t xe, int32_t ye); - + uint8_t tabcolor, colstart = 0, rowstart = 0; // some ST7735 displays need this changed - volatile uint32_t *dcport, *csport;//, *mosiport, *clkport, *rsport; + volatile uint32_t *dcport, *csport; - uint32_t cspinmask, dcpinmask, wrpinmask;//, mosipinmask, clkpinmask; + uint32_t cspinmask, dcpinmask, wrpinmask; uint32_t lastColor = 0xFFFF; - // These are associated with the Touch Screen handlers - uint8_t validTouch(uint16_t *x, uint16_t *y, uint16_t threshold = 600); - // Initialise with example calibration values so processor does not crash if setTouch() not called in setup() - uint16_t touchCalibration_x0 = 300, touchCalibration_x1 = 3600, touchCalibration_y0 = 300, touchCalibration_y1 = 3600; - uint8_t touchCalibration_rotate = 1, touchCalibration_invert_x = 2, touchCalibration_invert_y = 0; - uint32_t _pressTime; - uint16_t _pressX, _pressY; protected: - int32_t cursor_x, cursor_y, win_xe, win_ye, padX; + int32_t win_xe, win_ye, padX; - uint32_t _width_orig, _height_orig; // Display w/h as input, used by setRotation() + uint32_t _init_width, _init_height; // Display w/h as input, used by setRotation() uint32_t _width, _height; // Display w/h as modified by current rotation - uint32_t textcolor, textbgcolor, fontsloaded, addr_row, addr_col; + uint32_t addr_row, addr_col; + + uint32_t fontsloaded; uint8_t glyph_ab, // glyph height above baseline glyph_bb, // glyph height below baseline @@ -513,164 +517,32 @@ class TFT_eSPI : public Print { textdatum, // Text reference datum rotation; // Display rotation (0-3) - bool textwrap; // If set, 'wrap' text at right edge of display + bool textwrapX, textwrapY; // If set, 'wrap' text at right and optionally bottom edge of display bool _swapBytes; // Swap the byte order for TFT pushImage() bool locked, inTransaction; // Transaction and mutex lock flags for ESP32 - bool _booted; int32_t _lastColor; #ifdef LOAD_GFXFF - GFXfont - *gfxFont; + GFXfont *gfxFont; #endif -}; +// Load the Touch extension +#ifdef TOUCH_CS + #include "Extensions/Touch.h" +#endif -/*************************************************************************************** -// The following button class has been ported over from the Adafruit_GFX library so -// should be compatible. -// A slightly different implementation in this TFT_eSPI library allows the button -// legends to be in any font -***************************************************************************************/ +// Load the Anti-aliased font extension +#ifdef SMOOTH_FONT + #include "Extensions/Smooth_font.h" +#endif -class TFT_eSPI_Button { - - public: - TFT_eSPI_Button(void); - // "Classic" initButton() uses center & size - void initButton(TFT_eSPI *gfx, int16_t x, int16_t y, - uint16_t w, uint16_t h, uint16_t outline, uint16_t fill, - uint16_t textcolor, char *label, uint8_t textsize); - - // New/alt initButton() uses upper-left corner & size - void initButtonUL(TFT_eSPI *gfx, int16_t x1, int16_t y1, - uint16_t w, uint16_t h, uint16_t outline, uint16_t fill, - uint16_t textcolor, char *label, uint8_t textsize); - void drawButton(boolean inverted = false); - boolean contains(int16_t x, int16_t y); - - void press(boolean p); - boolean isPressed(); - boolean justPressed(); - boolean justReleased(); - - private: - TFT_eSPI *_gfx; - int16_t _x1, _y1; // Coordinates of top-left corner - uint16_t _w, _h; - uint8_t _textsize; - uint16_t _outlinecolor, _fillcolor, _textcolor; - char _label[10]; - - boolean currstate, laststate; -}; - - -/*************************************************************************************** -// The following class creates Sprites in RAM, graphics can then be drawn in the Sprite -// and rendered quickly onto the TFT screen. The class inherits the graphics functions -// from the TFT_eSPI class. Some functions are overridden by this class so that the -// graphics are written to the Sprite rather than the TFT. -***************************************************************************************/ - -class TFT_eSprite : public TFT_eSPI { - - public: - - TFT_eSprite(TFT_eSPI *tft); - - // Create a sprite of width x height pixels, return a pointer to the RAM area - // Sketch can cast returned value to (uint16_t*) for 16 bit depth if needed - // RAM required is 1 byte per pixel for 8 bit colour depth, 2 bytes for 16 bit - uint8_t* createSprite(int16_t width, int16_t height); - - // Delete the sprite to free up the RAM - void deleteSprite(void); - - // Set the colour depth to 8 or 16 bits. Can be used to change depth an existing - // sprite, but clears it to black, returns a new pointer if sprite is re-created. - uint8_t* setColorDepth(int8_t b); - - void drawPixel(uint32_t x, uint32_t y, uint32_t color); - - void drawChar(int32_t x, int32_t y, unsigned char c, uint32_t color, uint32_t bg, uint8_t size), - - fillSprite(uint32_t color), - - // Define a window to push 16 bit colour pixels into is a raster order - // Colours are converted to 8 bit if depth is set to 8 - setWindow(int32_t x0, int32_t y0, int32_t x1, int32_t y1), - pushColor(uint32_t color), - pushColor(uint32_t color, uint16_t len), - // Push a pixel preformatted as a 8 or 16 bit colour (avoids conversion overhead) - writeColor(uint16_t color), - - // Set the scroll zone, top left corner at x,y with defined width and height - // The colour (optional, black is default) is used to fill the gap after the scroll - setScrollRect(int32_t x, int32_t y, uint32_t w, uint32_t h, uint16_t color = TFT_BLACK), - // Scroll the defined zone dx,dy pixels. Negative values left,up, positive right,down - // dy is optional (default is then no up/down scroll). - // The sprite coordinate frame does not move because pixels are moved - scroll(int16_t dx, int16_t dy = 0), - - drawLine(int32_t x0, int32_t y0, int32_t x1, int32_t y1, uint32_t color), - drawFastVLine(int32_t x, int32_t y, int32_t h, uint32_t color), - drawFastHLine(int32_t x, int32_t y, int32_t w, uint32_t color), - - fillRect(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color), - - // Set the sprite text cursor position for print class (does not change the TFT screen cursor) - setCursor(int16_t x, int16_t y); - - // Read the colour of a pixel at x,y and return value in 565 format - uint16_t readPixel(int32_t x0, int32_t y0); - - // Write an image (colour bitmap) to the sprite - void pushImage(int32_t x0, int32_t y0, uint32_t w, uint32_t h, uint16_t *data); - void pushImage(int32_t x0, int32_t y0, uint32_t w, uint32_t h, const uint16_t *data); - - // Swap the byte order for pushImage() - corrects different image endianness - void setSwapBytes(bool swap); - bool getSwapBytes(void); - - // Push the sprite to the TFT screen, this fn calls pushImage() in the TFT class. - // Optionally a "transparent" colour can be defined, pixels of that colour will not be rendered - void pushSprite(int32_t x, int32_t y); - void pushSprite(int32_t x, int32_t y, uint16_t transparent); - - int16_t drawChar(unsigned int uniCode, int x, int y, int font), - drawChar(unsigned int uniCode, int x, int y); - - // Return the width and height of the sprite - int16_t width(void), - height(void); - - // Used by print class to print text to cursor position - size_t write(uint8_t); - - private: - - TFT_eSPI *_tft; - - protected: - - uint16_t *_img; // pointer to 16 bit sprite - uint8_t *_img8; // pointer to 8 bit sprite - bool _created, _bpp16; // created and bits per pixel depth flags - - int32_t _icursor_x, _icursor_y; - int32_t _xs, _ys, _xe, _ye, _xptr, _yptr; // for setWindow - int32_t _sx, _sy; // x,y for scroll zone - uint32_t _sw, _sh; // w,h for scroll zone - uint32_t _scolor; // gap fill colour for scroll zone - - boolean _iswapBytes; // Swap the byte order for Sprite pushImage() - - int32_t _iwidth, _iheight; // Sprite image width and height - -}; +}; // End of class TFT_eSPI +// Load the Button Class +#include "Extensions/Button.h" +// Load the Sprite Class +#include "Extensions/Sprite.h" #endif diff --git a/Tools/Create_Smooth_Font/Create_font_5/Create_font_5.pde b/Tools/Create_Smooth_Font/Create_font_5/Create_font_5.pde new file mode 100644 index 0000000..d89d6a9 --- /dev/null +++ b/Tools/Create_Smooth_Font/Create_font_5/Create_font_5.pde @@ -0,0 +1,483 @@ +// Select the character range in the user configure section starting at line 100 + +/* +Software License Agreement (FreeBSD License) + + Copyright (c) 2018 Bodmer (https://github.com/Bodmer) + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + The views and conclusions contained in the software and documentation are those + of the authors and should not be interpreted as representing official policies, + either expressed or implied, of the FreeBSD Project. + */ + +//////////////////////////////////////////////////////////////////////////////////////////////// + + +// This is a processing sketch to create font files for the TFT_eSPI library: + +// https://github.com/Bodmer/TFT_eSPI + +// Coded by Bodmer January 2018 + +// See comments below in code for specifying the font parameters +// (point size, unicode blocks to include etc). Ranges of characers or +// specific individual unicodes can be included in the created font file/ + +// Created fonts are saved in the sketches "FontFiles" folder. Press Ctrl+K to +// see that folder. + +// 16 bit unicodes in the range 0x0000 - 0xFFFF are supported. + +// The sketch will convert True Type (a .ttf or .otf file) file stored in the +// sketches "Data" folder as well as your computers system fonts. + +// To maximise rendering performance only include the characters you will use. +// Characters at the start of the file will render faster than those at the end. + +// Once created the files must be loaded into the ESP32 or ESP8266 SPIFFS memory +// using the Arduino IDE plugin detailed here: +// https://github.com/esp8266/arduino-esp8266fs-plugin +// https://github.com/me-no-dev/arduino-esp32fs-plugin + +// The sketch list all the available PC fonts to the console, you may need to increase +// console line count (in preferences.txt) to stop some fonts scrolling out of view. +// See link in File>Preferences to locate "preferences.txt" file. You must close +// Processing then edit the file lines. If Processing is not closed first then the +// edits will be overwritten by defaults! Edit "preferences.txt" as follows for +// 1000 lines, then save, then run Processing again: + + /* + console.length=1000 // Line 4 in file + console.scrollback.lines=1000 // Line 7 in file + */ + +// Useful links: + /* + + https://en.wikipedia.org/wiki/Unicode_font + + https://www.gnu.org/software/freefont/ + https://www.gnu.org/software/freefont/sources/ + https://www.gnu.org/software/freefont/ranges/ + http://savannah.gnu.org/projects/freefont/ + + http://www.google.com/get/noto/ + + https://github.com/Bodmer/TFT_eSPI + https://github.com/esp8266/arduino-esp8266fs-plugin + https://github.com/me-no-dev/arduino-esp32fs-plugin + + */ +//////////////////////////////////////////////////////////////////////////////////////////////// + +import java.awt.Desktop; + +// >>>>>>>>>> USER CONFIGURED PARAMETERS START HERE <<<<<<<<<< + + +// Use font name for ttf files placed in the "Data" folder or the font number seen in IDE Console for system fonts +// the font numbers are listed when the sketch is run. +// | 1 2 | Maximum filename size for SPIFFS is 32 including leading / +// 1234567890123456789012345 and added point size and .vlw extension, so max is 25 +String fontName = "Final-Frontier"; //Manually crop the filename length later after creation if needed + +String fontType = ".ttf"; //SPIFFS does not accept underscore in filename! +//String fontType = ".otf"; + +// Use font number instead of name, -1 means use name above, or a value >=0 means use system font number from list. +int fontNumber = -1; // << Use [Number] in brackets from the fonts listed in console window + +// Define the font size in points for the created font file +int fontSize = 28; + +// Font size to use in the Processing sketch display window that pops up (can be different to above) +int displayFontSize = 28; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Next we specify which unicode blocks from the the Basic Multilingual Plane (BMP) are included in the final font file. // +// Note: The ttf/otf font file MAY NOT contain all possible Unicode characters, refer to the fonts online documentation. // +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +static final int[] unicodeBlocks = { + // The list below has been created from the table here: https://en.wikipedia.org/wiki/Unicode_block + // Remove // at start of lines below to include that unicode block, different code ranges can also be specified by + // editting the start and end of range values. Multiple lines from the list below can be included, limited only by + // the final font file size! + + // Block range, //Block name, Code points, Assigned characters, Scripts + // First, last, //Range is inclusive of first and last codes + 0x0021, 0x007E, //Basic Latin, 128, 128, Latin (52 characters), Common (76 characters) + //0x0080, 0x00FF, //Latin-1 Supplement, 128, 128, Latin (64 characters), Common (64 characters) + //0x0100, 0x017F, //Latin Extended-A, 128, 128, Latin + //0x0180, 0x024F, //Latin Extended-B, 208, 208, Latin + //0x0250, 0x02AF, //IPA Extensions, 96, 96, Latin + //0x02B0, 0x02FF, //Spacing Modifier Letters, 80, 80, Bopomofo (2 characters), Latin (14 characters), Common (64 characters) + //0x0300, 0x036F, //Combining Diacritical Marks, 112, 112, Inherited + //0x0370, 0x03FF, //Greek and Coptic, 144, 135, Coptic (14 characters), Greek (117 characters), Common (4 characters) + //0x0400, 0x04FF, //Cyrillic, 256, 256, Cyrillic (254 characters), Inherited (2 characters) + //0x0500, 0x052F, //Cyrillic Supplement, 48, 48, Cyrillic + //0x0530, 0x058F, //Armenian, 96, 89, Armenian (88 characters), Common (1 character) + //0x0590, 0x05FF, //Hebrew, 112, 87, Hebrew + //0x0600, 0x06FF, //Arabic, 256, 255, Arabic (237 characters), Common (6 characters), Inherited (12 characters) + //0x0700, 0x074F, //Syriac, 80, 77, Syriac + //0x0750, 0x077F, //Arabic Supplement, 48, 48, Arabic + //0x0780, 0x07BF, //Thaana, 64, 50, Thaana + //0x07C0, 0x07FF, //NKo, 64, 59, Nko + //0x0800, 0x083F, //Samaritan, 64, 61, Samaritan + //0x0840, 0x085F, //Mandaic, 32, 29, Mandaic + //0x0860, 0x086F, //Syriac Supplement, 16, 11, Syriac + //0x08A0, 0x08FF, //Arabic Extended-A, 96, 73, Arabic (72 characters), Common (1 character) + //0x0900, 0x097F, //Devanagari, 128, 128, Devanagari (124 characters), Common (2 characters), Inherited (2 characters) + //0x0980, 0x09FF, //Bengali, 128, 95, Bengali + //0x0A00, 0x0A7F, //Gurmukhi, 128, 79, Gurmukhi + //0x0A80, 0x0AFF, //Gujarati, 128, 91, Gujarati + //0x0B00, 0x0B7F, //Oriya, 128, 90, Oriya + //0x0B80, 0x0BFF, //Tamil, 128, 72, Tamil + //0x0C00, 0x0C7F, //Telugu, 128, 96, Telugu + //0x0C80, 0x0CFF, //Kannada, 128, 88, Kannada + //0x0D00, 0x0D7F, //Malayalam, 128, 117, Malayalam + //0x0D80, 0x0DFF, //Sinhala, 128, 90, Sinhala + //0x0E00, 0x0E7F, //Thai, 128, 87, Thai (86 characters), Common (1 character) + //0x0E80, 0x0EFF, //Lao, 128, 67, Lao + //0x0F00, 0x0FFF, //Tibetan, 256, 211, Tibetan (207 characters), Common (4 characters) + //0x1000, 0x109F, //Myanmar, 160, 160, Myanmar + //0x10A0, 0x10FF, //Georgian, 96, 88, Georgian (87 characters), Common (1 character) + //0x1100, 0x11FF, //Hangul Jamo, 256, 256, Hangul + //0x1200, 0x137F, //Ethiopic, 384, 358, Ethiopic + //0x1380, 0x139F, //Ethiopic Supplement, 32, 26, Ethiopic + //0x13A0, 0x13FF, //Cherokee, 96, 92, Cherokee + //0x1400, 0x167F, //Unified Canadian Aboriginal Syllabics, 640, 640, Canadian Aboriginal + //0x1680, 0x169F, //Ogham, 32, 29, Ogham + //0x16A0, 0x16FF, //Runic, 96, 89, Runic (86 characters), Common (3 characters) + //0x1700, 0x171F, //Tagalog, 32, 20, Tagalog + //0x1720, 0x173F, //Hanunoo, 32, 23, Hanunoo (21 characters), Common (2 characters) + //0x1740, 0x175F, //Buhid, 32, 20, Buhid + //0x1760, 0x177F, //Tagbanwa, 32, 18, Tagbanwa + //0x1780, 0x17FF, //Khmer, 128, 114, Khmer + //0x1800, 0x18AF, //Mongolian, 176, 156, Mongolian (153 characters), Common (3 characters) + //0x18B0, 0x18FF, //Unified Canadian Aboriginal Syllabics Extended, 80, 70, Canadian Aboriginal + //0x1900, 0x194F, //Limbu, 80, 68, Limbu + //0x1950, 0x197F, //Tai Le, 48, 35, Tai Le + //0x1980, 0x19DF, //New Tai Lue, 96, 83, New Tai Lue + //0x19E0, 0x19FF, //Khmer Symbols, 32, 32, Khmer + //0x1A00, 0x1A1F, //Buginese, 32, 30, Buginese + //0x1A20, 0x1AAF, //Tai Tham, 144, 127, Tai Tham + //0x1AB0, 0x1AFF, //Combining Diacritical Marks Extended, 80, 15, Inherited + //0x1B00, 0x1B7F, //Balinese, 128, 121, Balinese + //0x1B80, 0x1BBF, //Sundanese, 64, 64, Sundanese + //0x1BC0, 0x1BFF, //Batak, 64, 56, Batak + //0x1C00, 0x1C4F, //Lepcha, 80, 74, Lepcha + //0x1C50, 0x1C7F, //Ol Chiki, 48, 48, Ol Chiki + //0x1C80, 0x1C8F, //Cyrillic Extended-C, 16, 9, Cyrillic + //0x1CC0, 0x1CCF, //Sundanese Supplement, 16, 8, Sundanese + //0x1CD0, 0x1CFF, //Vedic Extensions, 48, 42, Common (15 characters), Inherited (27 characters) + //0x1D00, 0x1D7F, //Phonetic Extensions, 128, 128, Cyrillic (2 characters), Greek (15 characters), Latin (111 characters) + //0x1D80, 0x1DBF, //Phonetic Extensions Supplement, 64, 64, Greek (1 character), Latin (63 characters) + //0x1DC0, 0x1DFF, //Combining Diacritical Marks Supplement, 64, 63, Inherited + //0x1E00, 0x1EFF, //Latin Extended Additional, 256, 256, Latin + //0x1F00, 0x1FFF, //Greek Extended, 256, 233, Greek + //0x2000, 0x206F, //General Punctuation, 112, 111, Common (109 characters), Inherited (2 characters) + //0x2070, 0x209F, //Superscripts and Subscripts, 48, 42, Latin (15 characters), Common (27 characters) + //0x20A0, 0x20CF, //Currency Symbols, 48, 32, Common + //0x20D0, 0x20FF, //Combining Diacritical Marks for Symbols, 48, 33, Inherited + //0x2100, 0x214F, //Letterlike Symbols, 80, 80, Greek (1 character), Latin (4 characters), Common (75 characters) + //0x2150, 0x218F, //Number Forms, 64, 60, Latin (41 characters), Common (19 characters) + //0x2190, 0x21FF, //Arrows, 112, 112, Common + //0x2200, 0x22FF, //Mathematical Operators, 256, 256, Common + //0x2300, 0x23FF, //Miscellaneous Technical, 256, 256, Common + //0x2400, 0x243F, //Control Pictures, 64, 39, Common + //0x2440, 0x245F, //Optical Character Recognition, 32, 11, Common + //0x2460, 0x24FF, //Enclosed Alphanumerics, 160, 160, Common + //0x2500, 0x257F, //Box Drawing, 128, 128, Common + //0x2580, 0x259F, //Block Elements, 32, 32, Common + //0x25A0, 0x25FF, //Geometric Shapes, 96, 96, Common + //0x2600, 0x26FF, //Miscellaneous Symbols, 256, 256, Common + //0x2700, 0x27BF, //Dingbats, 192, 192, Common + //0x27C0, 0x27EF, //Miscellaneous Mathematical Symbols-A, 48, 48, Common + //0x27F0, 0x27FF, //Supplemental Arrows-A, 16, 16, Common + //0x2800, 0x28FF, //Braille Patterns, 256, 256, Braille + //0x2900, 0x297F, //Supplemental Arrows-B, 128, 128, Common + //0x2980, 0x29FF, //Miscellaneous Mathematical Symbols-B, 128, 128, Common + //0x2A00, 0x2AFF, //Supplemental Mathematical Operators, 256, 256, Common + //0x2B00, 0x2BFF, //Miscellaneous Symbols and Arrows, 256, 207, Common + //0x2C00, 0x2C5F, //Glagolitic, 96, 94, Glagolitic + //0x2C60, 0x2C7F, //Latin Extended-C, 32, 32, Latin + //0x2C80, 0x2CFF, //Coptic, 128, 123, Coptic + //0x2D00, 0x2D2F, //Georgian Supplement, 48, 40, Georgian + //0x2D30, 0x2D7F, //Tifinagh, 80, 59, Tifinagh + //0x2D80, 0x2DDF, //Ethiopic Extended, 96, 79, Ethiopic + //0x2DE0, 0x2DFF, //Cyrillic Extended-A, 32, 32, Cyrillic + //0x2E00, 0x2E7F, //Supplemental Punctuation, 128, 74, Common + //0x2E80, 0x2EFF, //CJK Radicals Supplement, 128, 115, Han + //0x2F00, 0x2FDF, //Kangxi Radicals, 224, 214, Han + //0x2FF0, 0x2FFF, //Ideographic Description Characters, 16, 12, Common + //0x3000, 0x303F, //CJK Symbols and Punctuation, 64, 64, Han (15 characters), Hangul (2 characters), Common (43 characters), Inherited (4 characters) + //0x3040, 0x309F, //Hiragana, 96, 93, Hiragana (89 characters), Common (2 characters), Inherited (2 characters) + //0x30A0, 0x30FF, //Katakana, 96, 96, Katakana (93 characters), Common (3 characters) + //0x3100, 0x312F, //Bopomofo, 48, 42, Bopomofo + //0x3130, 0x318F, //Hangul Compatibility Jamo, 96, 94, Hangul + //0x3190, 0x319F, //Kanbun, 16, 16, Common + //0x31A0, 0x31BF, //Bopomofo Extended, 32, 27, Bopomofo + //0x31C0, 0x31EF, //CJK Strokes, 48, 36, Common + //0x31F0, 0x31FF, //Katakana Phonetic Extensions, 16, 16, Katakana + //0x3200, 0x32FF, //Enclosed CJK Letters and Months, 256, 254, Hangul (62 characters), Katakana (47 characters), Common (145 characters) + //0x3300, 0x33FF, //CJK Compatibility, 256, 256, Katakana (88 characters), Common (168 characters) + //0x3400, 0x4DBF, //CJK Unified Ideographs Extension A, 6,592, 6,582, Han + //0x4DC0, 0x4DFF, //Yijing Hexagram Symbols, 64, 64, Common + //0x4E00, 0x9FFF, //CJK Unified Ideographs, 20,992, 20,971, Han + //0xA000, 0xA48F, //Yi Syllables, 1,168, 1,165, Yi + //0xA490, 0xA4CF, //Yi Radicals, 64, 55, Yi + //0xA4D0, 0xA4FF, //Lisu, 48, 48, Lisu + //0xA500, 0xA63F, //Vai, 320, 300, Vai + //0xA640, 0xA69F, //Cyrillic Extended-B, 96, 96, Cyrillic + //0xA6A0, 0xA6FF, //Bamum, 96, 88, Bamum + //0xA700, 0xA71F, //Modifier Tone Letters, 32, 32, Common + //0xA720, 0xA7FF, //Latin Extended-D, 224, 160, Latin (155 characters), Common (5 characters) + //0xA800, 0xA82F, //Syloti Nagri, 48, 44, Syloti Nagri + //0xA830, 0xA83F, //Common Indic Number Forms, 16, 10, Common + //0xA840, 0xA87F, //Phags-pa, 64, 56, Phags Pa + //0xA880, 0xA8DF, //Saurashtra, 96, 82, Saurashtra + //0xA8E0, 0xA8FF, //Devanagari Extended, 32, 30, Devanagari + //0xA900, 0xA92F, //Kayah Li, 48, 48, Kayah Li (47 characters), Common (1 character) + //0xA930, 0xA95F, //Rejang, 48, 37, Rejang + //0xA960, 0xA97F, //Hangul Jamo Extended-A, 32, 29, Hangul + //0xA980, 0xA9DF, //Javanese, 96, 91, Javanese (90 characters), Common (1 character) + //0xA9E0, 0xA9FF, //Myanmar Extended-B, 32, 31, Myanmar + //0xAA00, 0xAA5F, //Cham, 96, 83, Cham + //0xAA60, 0xAA7F, //Myanmar Extended-A, 32, 32, Myanmar + //0xAA80, 0xAADF, //Tai Viet, 96, 72, Tai Viet + //0xAAE0, 0xAAFF, //Meetei Mayek Extensions, 32, 23, Meetei Mayek + //0xAB00, 0xAB2F, //Ethiopic Extended-A, 48, 32, Ethiopic + //0xAB30, 0xAB6F, //Latin Extended-E, 64, 54, Latin (52 characters), Greek (1 character), Common (1 character) + //0xAB70, 0xABBF, //Cherokee Supplement, 80, 80, Cherokee + //0xABC0, 0xABFF, //Meetei Mayek, 64, 56, Meetei Mayek + //0xAC00, 0xD7AF, //Hangul Syllables, 11,184, 11,172, Hangul + //0xD7B0, 0xD7FF, //Hangul Jamo Extended-B, 80, 72, Hangul + //0xD800, 0xDB7F, //High Surrogates, 896, 0, Unknown + //0xDB80, 0xDBFF, //High Private Use Surrogates, 128, 0, Unknown + //0xDC00, 0xDFFF, //Low Surrogates, 1,024, 0, Unknown + //0xE000, 0xF8FF, //Private Use Area, 6,400, 6,400, Unknown + //0xF900, 0xFAFF, //CJK Compatibility Ideographs, 512, 472, Han + //0xFB00, 0xFB4F, //Alphabetic Presentation Forms, 80, 58, Armenian (5 characters), Hebrew (46 characters), Latin (7 characters) + //0xFB50, 0xFDFF, //Arabic Presentation Forms-A, 688, 611, Arabic (609 characters), Common (2 characters) + //0xFE00, 0xFE0F, //Variation Selectors, 16, 16, Inherited + //0xFE10, 0xFE1F, //Vertical Forms, 16, 10, Common + //0xFE20, 0xFE2F, //Combining Half Marks, 16, 16, Cyrillic (2 characters), Inherited (14 characters) + //0xFE30, 0xFE4F, //CJK Compatibility Forms, 32, 32, Common + //0xFE50, 0xFE6F, //Small Form Variants, 32, 26, Common + //0xFE70, 0xFEFF, //Arabic Presentation Forms-B, 144, 141, Arabic (140 characters), Common (1 character) + //0xFF00, 0xFFEF, //Halfwidth and Fullwidth Forms, 240, 225, Hangul (52 characters), Katakana (55 characters), Latin (52 characters), Common (66 characters) + //0xFFF0, 0xFFFF, //Specials, 16, 5, Common + + //0x0030, 0x0039, //Example custom range (numbers 0-9) + //0x0041, 0x005A, //Example custom range (Upper case A-Z) + //0x0061, 0x007A, //Example custom range (Lower case a-z) +}; + +// Here we specify specific individual Unicodes to be included (appended at end of selected range) +static final int[] specificUnicodes = { + + // Commonly used codes, add or remove // in next line + // 0x00A3, 0x00B0, 0x00B5, 0x03A9, 0x20AC, // £ ° µ Ω € + + // Numbers and characters for showing time, change next line to //* to use + /* + 0x002B, 0x002D, 0x002E, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, // - + . 0 1 2 3 4 + 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x0061, 0x006D, // 5 6 7 8 9 : a m + 0x0070, // p + //*/ + + // More characters, change next line to //* to use + /* + 0x0102, 0x0103, 0x0104, 0x0105, 0x0106, 0x0107, 0x010C, 0x010D, + 0x010E, 0x010F, 0x0110, 0x0111, 0x0118, 0x0119, 0x011A, 0x011B, + + 0x0131, 0x0139, 0x013A, 0x013D, 0x013E, 0x0141, 0x0142, 0x0143, + 0x0144, 0x0147, 0x0148, 0x0150, 0x0151, 0x0152, 0x0153, 0x0154, + 0x0155, 0x0158, 0x0159, 0x015A, 0x015B, 0x015E, 0x015F, 0x0160, + 0x0161, 0x0162, 0x0163, 0x0164, 0x0165, 0x016E, 0x016F, 0x0170, + 0x0171, 0x0178, 0x0179, 0x017A, 0x017B, 0x017C, 0x017D, 0x017E, + 0x0192, + + 0x02C6, 0x02C7, 0x02D8, 0x02D9, 0x02DA, 0x02DB, 0x02DC, 0x02DD, + 0x03A9, 0x03C0, 0x2013, 0x2014, 0x2018, 0x2019, 0x201A, 0x201C, + 0x201D, 0x201E, 0x2020, 0x2021, 0x2022, 0x2026, 0x2030, 0x2039, + 0x203A, 0x2044, 0x20AC, + + 0x2122, 0x2202, 0x2206, 0x220F, + + 0x2211, 0x221A, 0x221E, 0x222B, 0x2248, 0x2260, 0x2264, 0x2265, + 0x25CA, + + 0xF8FF, 0xFB01, 0xFB02, + //*/ +}; + + +// >>>>>>>>>> USER CONFIGURED PARAMETERS END HERE <<<<<<<<<< +//////////////////////////////////////////////////////////////////////////////////////////////// + +// Variable to hold the inclusive Unicode range (16 bit values only for this sketch) +int firstUnicode = 0; +int lastUnicode = 0; + +PFont myFont; + +void setup() { + + size(1000, 800); + + // Print the available fonts to the console as a list: + String[] fontList = PFont.list(); + printArray(fontList); + + // Set the fontName from the array number or the defined fontName + if (fontNumber >= 0) + { + fontName = fontList[fontNumber]; + fontType = ""; + } + + char[] charset; + int index = 0, count = 0; + + int blockCount = unicodeBlocks.length; + + for (int i = 0; i < blockCount; i+=2) { + firstUnicode = unicodeBlocks[i]; + lastUnicode = unicodeBlocks[i+1]; + if (lastUnicode < firstUnicode) { + delay(100); + System.err.println("ERROR: Bad Unicode range secified, last < first!"); + System.err.print("first in range = 0x" + hex(firstUnicode, 4)); + System.err.println(", last in range = 0x" + hex(lastUnicode, 4)); + while (true); + } + // calculate the number of characters + count += (lastUnicode - firstUnicode + 1); + } + + count += specificUnicodes.length; + + println(); + println("====================="); + println("Creating font file..."); + println("Unicode blocks included = " + (blockCount/2)); + println("Specific unicodes included = " + specificUnicodes.length); + println("Total number of characters = " + count); + + if (count == 0) { + delay(100); + System.err.println("ERROR: No Unicode range or specific codes have been defined!"); + while (true); + } + + // allocate memory + charset = new char[count]; + + for (int i = 0; i < blockCount; i+=2) { + firstUnicode = unicodeBlocks[i]; + lastUnicode = unicodeBlocks[i+1]; + + // loading the range specified + for (int code = firstUnicode; code <= lastUnicode; code++) { + charset[index] = Character.toChars(code)[0]; + index++; + } + } + + // loading the range specified + for (int i = 0; i < specificUnicodes.length; i++) { + charset[index] = Character.toChars(specificUnicodes[i])[0]; + index++; + } + // Create the font in memory + myFont = createFont(fontName+fontType, 32, true, charset); + + // Print a few characters to the sketch window + fill(0, 0, 0); + textFont(myFont); + + // Set the left and top margin + int margin = displayFontSize; + translate(margin/2, margin); + + int gapx = displayFontSize*10/8; + int gapy = displayFontSize*10/8; + index = 0; + fill(0); + + textSize(displayFontSize); + + for (int y = 0; y < height-gapy; y += gapy) { + int x = 0; + while (x < width) { + + int unicode = charset[index]; + float cwidth = textWidth((char)unicode) + 2; + if ( (x + cwidth) > (width - gapx) ) break; + + // Draw the letter to the screen + text(new String(Character.toChars(unicode)), x, y); + + // Move cursor + x += cwidth; + // Increment the counter + index++; + if (index >= count) break; + } + if (index >= count) break; + } + + + // creating font + PFont font; + + font = createFont(fontName+fontType, fontSize, true, charset); + + println("Created font " + fontName + ".vlw"); + + // creating file + try { + print("Saving to sketch FontFiles folder... "); + + OutputStream output = createOutput("FontFiles/" + fontName + str(fontSize) + ".vlw"); + font.save(output); + output.close(); + + println("OK!"); + + delay(100); + + // Open up the FontFiles folder to access the saved file + String path = sketchPath(); + Desktop.getDesktop().open(new File(path+"/FontFiles")); + + System.err.println("All done! Note: Rectangles are displayed for non-existant characters."); + } + catch(IOException e) { + println("Doh! Failed to create the file"); + } +} \ No newline at end of file diff --git a/Tools/Create_Smooth_Font/Create_font_5/data/Final-Frontier.ttf b/Tools/Create_Smooth_Font/Create_font_5/data/Final-Frontier.ttf new file mode 100644 index 0000000000000000000000000000000000000000..823b9a5aeacae39058bd238b431dd2a3e5a9fd60 GIT binary patch literal 19800 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 // Setup file configured for my stock RPi TFT with touch //#include // Setup file for the ESP32 based M5Stack +//#include + //#include // Setup file template for copying/editting diff --git a/examples/Smooth Fonts/Print_Smooth_Font/Print_Smooth_Font.ino b/examples/Smooth Fonts/Print_Smooth_Font/Print_Smooth_Font.ino new file mode 100644 index 0000000..10f9197 --- /dev/null +++ b/examples/Smooth Fonts/Print_Smooth_Font/Print_Smooth_Font.ino @@ -0,0 +1,195 @@ +/* + Sketch to demonstrate using the print class with smooth fonts + + Sketch is writtent for a 240 x 320 display + + Load the font file into SPIFFS first by using the Arduino IDE + Sketch Data Upload menu option. Font files must be stored in the + sketch data folder (Ctrl+k to view). + https://github.com/esp8266/arduino-esp8266fs-plugin + https://github.com/me-no-dev/arduino-esp32fs-plugin + + New font files in the .vlw format can be created using the Processing + sketch in the library Tools folder. The Processing sketch can convert + TrueType fonts in *.ttf or *.otf files. + + Note: SPIFFS does not accept an underscore _ in filenames! + + The library supports 16 bit unicode characters: + https://en.wikipedia.org/wiki/Unicode_font + + The characters supported are in the in the Basic Multilingal Plane: + https://en.wikipedia.org/wiki/Plane_(Unicode)#Basic_Multilingual_Plane + + Make sure all the display driver and pin connenctions are correct by + editting the User_Setup.h file in the TFT_eSPI library folder. + + ######################################################################### + ###### DON'T FORGET TO UPDATE THE User_Setup.h FILE IN THE LIBRARY ###### + ######################################################################### +*/ + +// Font file is stored in SPIFFS +#define FS_NO_GLOBALS +#include + +// Graphics and font library +#include +#include + +TFT_eSPI tft = TFT_eSPI(); // Invoke library + +// ------------------------------------------------------------------------- +// Setup +// ------------------------------------------------------------------------- +void setup(void) { + Serial.begin(115200); // Used for messages + + tft.init(); + tft.setRotation(1); + + if (!SPIFFS.begin()) { + Serial.println("SPIFFS initialisation failed!"); + while (1) yield(); // Stay here twiddling thumbs waiting + } + Serial.println("\r\nInitialisation done."); + + listFiles(); // Lists the files so you can see what is in the SPIFFS + +} + +// ------------------------------------------------------------------------- +// Main loop +// ------------------------------------------------------------------------- +void loop() { + // Wrap test at right and bottom of screen + tft.setTextWrap(true, true); + + // Name of font file (library adds leading / and .vlw) + String fileName = "Final-Frontier-28"; + + // Font and background colour, background colour is used for anti-alias blending + tft.setTextColor(TFT_WHITE, TFT_BLACK); + + // Load the font + tft.loadFont(fileName); + + // Display all characters of the font + tft.showFont(2000); + + // Set "cursor" at top left corner of display (0,0) + // (cursor will move to next line automatically during printing with 'tft.println' + // or stay on the line is there is room for the text with tft.print) + tft.setCursor(0, 0); + + // Set the font colour to be white with a black background, set text size multiplier to 1 + tft.setTextColor(TFT_WHITE, TFT_BLACK); + + // We can now plot text on screen using the "print" class + tft.println("Hello World!"); + + // Set the font colour to be yellow + tft.setTextColor(TFT_YELLOW, TFT_BLACK); + tft.println(1234.56); + + // Set the font colour to be red + tft.setTextColor(TFT_RED, TFT_BLACK); + tft.println((uint32_t)3735928559, HEX); // Should print DEADBEEF + + // Set the font colour to be green with black background + tft.setTextColor(TFT_GREEN, TFT_BLACK); + tft.println("Anti-aliased font!"); + tft.println(""); + + // Test some print formatting functions + float fnumber = 123.45; + + // Set the font colour to be blue + tft.setTextColor(TFT_BLUE, TFT_BLACK); + tft.print("Float = "); tft.println(fnumber); // Print floating point number + tft.print("Binary = "); tft.println((int)fnumber, BIN); // Print as integer value in binary + tft.print("Hexadecimal = "); tft.println((int)fnumber, HEX); // Print as integer number in Hexadecimal + + // Unload the font to recover used RAM + tft.unloadFont(); + + delay(10000); +} + + +// ------------------------------------------------------------------------- +// List files in ESP8266 or ESP32 SPIFFS memory +// ------------------------------------------------------------------------- +void listFiles(void) { + Serial.println(); + Serial.println("SPIFFS files found:"); + +#ifdef ESP32 + listDir(SPIFFS, "/", true); +#else + fs::Dir dir = SPIFFS.openDir("/"); // Root directory + String line = "====================================="; + + Serial.println(line); + Serial.println(" File name Size"); + Serial.println(line); + + while (dir.next()) { + String fileName = dir.fileName(); + Serial.print(fileName); + int spaces = 25 - fileName.length(); // Tabulate nicely + if (spaces < 0) spaces = 1; + while (spaces--) Serial.print(" "); + fs::File f = dir.openFile("r"); + Serial.print(f.size()); Serial.println(" bytes"); + yield(); + } + + Serial.println(line); +#endif + Serial.println(); + delay(1000); +} + +#ifdef ESP32 +void listDir(fs::FS &fs, const char * dirname, uint8_t levels) { + Serial.printf("Listing directory: %s\n", dirname); + + fs::File root = fs.open(dirname); + if (!root) { + Serial.println("Failed to open directory"); + return; + } + if (!root.isDirectory()) { + Serial.println("Not a directory"); + return; + } + + fs::File file = root.openNextFile(); + while (file) { + + if (file.isDirectory()) { + Serial.print("DIR : "); + String fileName = file.name(); + Serial.print(fileName); + if (levels) { + listDir(fs, file.name(), levels - 1); + } + } else { + String fileName = file.name(); + Serial.print(" " + fileName); + int spaces = 32 - fileName.length(); // Tabulate nicely + if (spaces < 1) spaces = 1; + while (spaces--) Serial.print(" "); + String fileSize = (String) file.size(); + spaces = 8 - fileSize.length(); // Tabulate nicely + if (spaces < 1) spaces = 1; + while (spaces--) Serial.print(" "); + Serial.println(fileSize + " bytes"); + } + + file = root.openNextFile(); + } +} +#endif +// ------------------------------------------------------------------------- diff --git a/examples/Smooth Fonts/Print_Smooth_Font/data/Final-Frontier-28.vlw b/examples/Smooth Fonts/Print_Smooth_Font/data/Final-Frontier-28.vlw new file mode 100644 index 0000000000000000000000000000000000000000..2872fd554bbf34f5e1268770dd8ea3cf55cecdfd GIT binary patch literal 25287 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 literal 0 HcmV?d00001 diff --git a/examples/Smooth Fonts/Unicode_test/SPIFFS_functions.ino b/examples/Smooth Fonts/Unicode_test/SPIFFS_functions.ino new file mode 100644 index 0000000..1415556 --- /dev/null +++ b/examples/Smooth Fonts/Unicode_test/SPIFFS_functions.ino @@ -0,0 +1,83 @@ +/*==================================================================================== + This sketch supports for the ESP6266 and ESP32 SPIFFS filing system + + Created by Bodmer 15th Jan 2017 + ==================================================================================*/ + +//==================================================================================== +// Print a SPIFFS directory list (root directory) +//==================================================================================== + +void listFiles(void) { + Serial.println(); + Serial.println("SPIFFS files found:"); + +#ifdef ESP32 + listDir(SPIFFS, "/", true); +#else + fs::Dir dir = SPIFFS.openDir("/"); // Root directory + String line = "====================================="; + + Serial.println(line); + Serial.println(" File name Size"); + Serial.println(line); + + while (dir.next()) { + String fileName = dir.fileName(); + Serial.print(fileName); + int spaces = 25 - fileName.length(); // Tabulate nicely + if (spaces < 0) spaces = 1; + while (spaces--) Serial.print(" "); + fs::File f = dir.openFile("r"); + Serial.print(f.size()); Serial.println(" bytes"); + yield(); + } + + Serial.println(line); +#endif + Serial.println(); + delay(1000); +} +//==================================================================================== + +#ifdef ESP32 +void listDir(fs::FS &fs, const char * dirname, uint8_t levels) { + Serial.printf("Listing directory: %s\n", dirname); + + fs::File root = fs.open(dirname); + if (!root) { + Serial.println("Failed to open directory"); + return; + } + if (!root.isDirectory()) { + Serial.println("Not a directory"); + return; + } + + fs::File file = root.openNextFile(); + while (file) { + + if (file.isDirectory()) { + Serial.print("DIR : "); + String fileName = file.name(); + Serial.print(fileName); + if (levels) { + listDir(fs, file.name(), levels - 1); + } + } else { + String fileName = file.name(); + Serial.print(" " + fileName); + int spaces = 32 - fileName.length(); // Tabulate nicely + if (spaces < 1) spaces = 1; + while (spaces--) Serial.print(" "); + String fileSize = (String) file.size(); + spaces = 8 - fileSize.length(); // Tabulate nicely + if (spaces < 1) spaces = 1; + while (spaces--) Serial.print(" "); + Serial.println(fileSize + " bytes"); + } + + file = root.openNextFile(); + } +} +#endif diff --git a/examples/Smooth Fonts/Unicode_test/Unicode_test.ino b/examples/Smooth Fonts/Unicode_test/Unicode_test.ino new file mode 100644 index 0000000..aac4926 --- /dev/null +++ b/examples/Smooth Fonts/Unicode_test/Unicode_test.ino @@ -0,0 +1,148 @@ +// Created by Bodmer 24th Jan 2017 - Tested in Arduino IDE 1.8.5 esp8266 Core 2.4.0 + +// The latest Arduino IDE versions support UTF-8 encoding of Unicode characters +// within sketches: +// https://playground.arduino.cc/Code/UTF-8 + +/* + The library expects strings to be in UTF-8 encoded format: + https://www.fileformat.info/info/unicode/utf8.htm + + Creating varaibles needs to be done with care when using character arrays: + char c = 'µ'; // Wrong + char bad[4] = "5µA"; // Wrong + char good[] = "5µA"; // Good + String okay = "5µA"; // Good + + This is because UTF-8 characters outside the basic Latin set occupy more than + 1 byte per character! A 16 bit unicode character occupies 3 bytes! + +*/ + +//==================================================================================== +// Libraries +//==================================================================================== +// Call up the SPIFFS FLASH filing system this is part of the ESP Core + +#include // Hardware-specific library + +TFT_eSPI tft = TFT_eSPI(); // Invoke custom library + +uint16_t bg = TFT_BLACK; +uint16_t fg = TFT_WHITE; + + +//==================================================================================== +// Setup +//==================================================================================== +void setup() +{ + Serial.begin(115200); // Used for messages and the C array generator + + Serial.println("NodeMCU vlw font test!"); + + if (!SPIFFS.begin()) { + Serial.println("SPIFFS initialisation failed!"); + while (1) yield(); // Stay here twiddling thumbs waiting + } + Serial.println("\r\nInitialisation done."); + + listFiles(); // Lists the files so you can see what is in the SPIFFS + + tft.begin(); + tft.setRotation(0); // portrait + + fg = TFT_WHITE; + bg = TFT_BLACK; +} + +//==================================================================================== +// Loop +//==================================================================================== +void loop() +{ + tft.setTextColor(fg, bg); + + //---------------------------------------------------------------------------- + // Anti-aliased font test + + String test1 = "Hello World"; + + // Load a smooth font from SPIFFS + tft.loadFont("Final-Frontier-28"); + + tft.setRotation(0); + + // Show all characters on screen with 2 second (2000ms) delay between screens + tft.showFont(2000); // Note: This function moves the cursor position! + + tft.fillScreen(bg); + tft.setCursor(0,0); + + tft.println(test1); + + // Remove font parameters from memory to recover RAM + tft.unloadFont(); + + delay(2000); + + //---------------------------------------------------------------------------- + // We can have any random mix of characters in the font + + String test2 = "仝倀"; // Unicodes 0x4EDD, 0x5000 + + tft.loadFont("Unicode-Test-72"); + + tft.setRotation(1); + + // Show all characters on screen with 2 second (2000ms) delay between screens + tft.showFont(2000); // Note: This function moves the cursor position! + + tft.fillScreen(bg); + tft.setCursor(0,0); + + tft.setTextColor(TFT_CYAN, bg); + tft.println(test2); + + tft.setTextColor(TFT_YELLOW, bg); + tft.println("12:00pm"); + + tft.setTextColor(TFT_MAGENTA, bg); + tft.println("1000Ω"); + + // Remove font parameters from memory to recover RAM + tft.unloadFont(); + + delay(2000); + + //---------------------------------------------------------------------------- + // Latin and Hiragana font mix + + String test3 = "こんにちは"; + + tft.loadFont("Latin-Hiragana-24"); + + tft.setRotation(0); + + // Show all characters on screen with 2 second (2000ms) delay between screens + tft.showFont(2000); // Note: This function moves the cursor position! + + tft.fillScreen(bg); + tft.setTextColor(TFT_GREEN, bg); + tft.setCursor(0,0); + + tft.println("Konnichiwa"); + tft.println(test3); + tft.println(); + tft.println("Sayonara"); + tft.println("さようなら"); // Sayonara + + // Remove font parameters from memory to recover RAM + tft.unloadFont(); + + delay(2000); + // + //---------------------------------------------------------------------------- +} +//==================================================================================== + diff --git a/examples/Smooth Fonts/Unicode_test/data/Final-Frontier-28.vlw b/examples/Smooth Fonts/Unicode_test/data/Final-Frontier-28.vlw new file mode 100644 index 0000000000000000000000000000000000000000..2872fd554bbf34f5e1268770dd8ea3cf55cecdfd GIT binary patch literal 25287 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 literal 0 HcmV?d00001 diff --git a/examples/Smooth Fonts/Unicode_test/data/Latin-Hiragana-24.vlw b/examples/Smooth Fonts/Unicode_test/data/Latin-Hiragana-24.vlw new file mode 100644 index 0000000000000000000000000000000000000000..b2f128b4ed054b8c3a488b361f6903e4b557722d GIT binary patch literal 54478 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®~4f}smG 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 E06d{_kN^Mx literal 0 HcmV?d00001 diff --git a/examples/Smooth Fonts/Unicode_test/data/Unicode-Test-72.vlw b/examples/Smooth Fonts/Unicode_test/data/Unicode-Test-72.vlw new file mode 100644 index 0000000000000000000000000000000000000000..c4757564c98f7c46a98dc54f913b83808f2bcb79 GIT binary patch literal 36469 zcmeHQ4@e!&cHeiOwUS;!#b>RE&4;x%*fdT1HBHkrwU!_fd_nRE5`u(~R(v6~5(42P zh=dT3AVES9Bm|KVq#_rQGzcOhAxI#INC-g)A|Z$fA|jWIT$r9Yzd5@z`{$bYy|&+1 zT-cp6=lsr@+1dGXW_I>o&-4Ds^SnQxvzbnP{4Je_>HLn)Uzqb(6b>k*rYO|MU(i`& z&Uy;n~GUwvpD+or}zUjEL=?es);iewrq zGF$KO3+SCLAo~ZB{TZFQtbS!^DYI)7|8qJey*i>LFaJ)oj_dOIM?>r9qK|Gm_5FQE zr@nWM6WMat0bHDJ3=zpTKk5Kc35o+I^4XRBO9jo(k6vA2Oot0SE22MCf)FJ$%6dkKZDZ zu!Tj>Cpn1JIgM&y(d0Y_M?}vK53^`-lY7H#J^umu6lA^;Mc zi?2j5EXhHEjlJ`+_r2_09;iT(TB>iklJl{E=W;G6gn(*d!~~vgOTZ7PRz^JU=b%9S z=tnhq9B2jMxZ^vQ3F1>qfC zhmLz)sqg_bW#eby0+!s0SB?_2Fhhqd3rsef0Ro_xD2NcK_U7o~PA<3o?nQ^2fQ{qw zwHaROy*BEw@H}i5s-cZKidJT^s=t_9@D*!?+ zAcer;|7e{^UmTp=$dXU{F>}Aks+*F7Q{%>gL@qY^LmfiZ1O(PK*CWb0Y%xvF0uVXl zyQ5x*EAeBC0=Z;Z`H3q>DTiej2BhiFBEz*9$kmwN1$!mErZa$tISUil?TmRR(dXv1 z^V|Ty6$jvX$F`ZTyZ={AJqrOm?<({26<#T)xNCRW28=s0T_%}l9Ja?G zjAUppSMO#X57U>mz$Yx~gXo9(EWoNh+s;zWjbj1lVv5Z4(I>t`4( z+{3+O8#Qb~>4mL?1H=EP1SzrMm8;u{i1a5;WG&X&Wi2z9-bgg@2-$p_2)F~lPF)hB z695&icRpSKeLg2dn2}695AprzJ(EP@13{frY9glfYb%E_u~TX^0Z{wVBr)VC>JrV; z1@yXnKW55FsTj9WgNlYt3R|&mH2GAs2Jk7NsH;90fYtH%$!o%QFa{iU5IJT%wB4%# z&!OrKVDqfQ4kAa41Gg@oV;}-miYlo#j)f%>H;-cXj4;9>IufNi!vcw$IkLpP41+NPH{}@{NUe<(G80quWVuo?-fjqPxqBgrM1y-PZhbbho!iZR8ov`;xE1}F z7eI6Ia-eCb4s@$xS*dxqrj=l>AIOQ?Zn4Z1qt|l)6uA^Yo*LQWjrx>Ve7a{9@!bZ2 zw1ScKhq2TTD1*;wK#HKU4Zjq;v&gvVKyJXqMKQ@C#-%KbEHPrj0eBuJ4ct620{GJ( z0bXT2o+#aAazP{#qF4W@qc0iJBN7QQ{A6`>g%Qmn@k3_saDq?omRtBJm(nNG|h)sc)$-K~uQco~xe1euyBX5K-lZ1isDd(Rbnsq%|M z(*BZ%+ry8HwX@NLgwq07@KdB(2!I??SV{H_Jg=W2pA*JAZ7Pi+?R;Q1LOo_9wBC_; zASZ7U1T~A*@iyh}@ahyCXB(?sBsMqRDLImSXF4Biopr;9x9dpQF7j z8b;IKJsbnU;MR7Wf$E1S0uzB;iMPOz?aDBudXoiIr0p;=6GD`~XFjhx!-yJlT1TC6 zRc>j;V>wt^2*%^$JEj^=!tzxhl$!21PKL@`24tP)jp=fY>n^w;oDzWNwMpF$q(nK+ zQ0=TE(~*Vn(#*LT2Qw2_`N}U3Pj6;(+0E(Uw%BLwlo(df9abok#)uN)I#JV4I6OCU z-sIcm+Obj@?QNz@kIK!vY%3=7#L)EFLX^wIF&Q#0$s|Hd-D8|(+afEuo({s2#kWYF zO|GO1K+r)qm<14CYoxH~H5slt1|--xlo z{y{!ypHaw3W?56*2&80Lvlgu}CIT7IocuA#Ys>>^U>#?GXHM`K(SYq_BBjd>c(>k4 zCJbq2qjw#ITXdzoKKsr^xDa-Da<11LN(g!X*<3?+YoehC5C=Md#Fa{@GW-r9aByPl zl}V*3R>(|)upH(v)*zmT`9a(uj878q0}6-~=5qzS*Hf(W$ry|at}tRWPGoo(vF{W> z%(9J^0tom{vP2sLtYZ@!F%n^6yv|58CWIgEch)foOtakStP6whb;M=bu?o&;p$TIv zBc@5I?8?a7?cLp-#phScY#Y6d81$N^tVN0Z3#(6sUor5Gf!5_oTI_tbxDL5|6p#{! z>qIuOR4R^^Qn`^!wKTMpw>%QhYMVzz4ELn_FnDySF7{WHTGhN4FudL%`Qoq|p0#>P zu_Z=q+T)VOlo|+m)0euQW!`0@llNLYj1e{&e}SX0OD;>&aR^4Ng_8&pbL*i9KVV?V zV=7=osCwLGgdZ@_s}!m@7y$EuN|fSM3N5%7F=8vK>2r=UV&Tci_|!%=c&xsho(Cgd zz?W~e1U`u_p&PX{uHaIb_#C^#rZz0MMQmJS$+CBXLv=&=eprkdAoV-!VW}^XS>hvB z-6H-?7#a7KK_CsKyRITwQ^QVHI-m!Crx6kLma3#Vs?0QE%lIL84Ma^{qeVBb=Hd(U zx-0M3-hP3&;jUGV*R!sSV{QwoosK|WgePcco$X`!@spBkD=ck$Hno|f3Er!xZISXb zxQh0Xxh-1kT+8$|TB<&I+U}*)m!5EAs~8LC^@}?coR(RzjqCI6)7GrG0#Rzfhn(QK z3wgIME>cHY(lUTXt^D;cGx1b2HKJ2Cfz-B4&pU{yovil^ynfa>hKS{a6C0BVwZlW~ zOGb=I3MPc_uJoXSrWLKIbF8TQF;v-WArqd5w_uCJ=MH!!0`MxcE3d|${U94v949`6 zzQ@TO4RP)0tVYBn;$u==uM^)A5e~)G>1#v@beiWOINp=_Oz(}?Gd(8)BzkXNEWEJ5 znZ{+oZ!9Cspr%&w%93kxx&n4GIzkIt$NViHn!(39CXiD)vhCHy)xzvKZH4Fg8)o0l zhuf!UiLs^)z5;3Lk&El!23Pd;b|g&K`nO0(&t=W(5HXgjd;s-3oIjjx*pD z&9asJ5i-BYZgGQvK^);+%nLpy#9-vV zOphWk=b;x;iN^&_ByJcHl?)>a)XqcfnC(P5Kr!HXXzT^*ui1j^4iJGpQK=oyhVif* zk%=@i;C-CP@X)b8D}cZN5;yFMQjSxJ?aTpz!(C`TjJfQrSd0mgQSF>39dJ}`Cy9xV z>g9W_G00WEm8KYpuu$g7y`$%0u36yGv#C=(<12cH=Hh5qT*(!6algRXq08HdlXn_j zj{qoq!ydZi)9Vl?)Pii;ch+qQ0&=&-w}5s)uJr(ab#(E$v6z*ax4e0K;TK2EEG#V- z!^!}n_k5c~41?Fhyu&E1PzA(;+F*b28pGEtUqCCG9Zz~XXBrCZ>9{$NHrJTYAz3ERd`OJzWNa|ir(T0M_fMvhSejTNbq^Xu>Wk!hN1LP9z~s*%k4bY zUqBpcd5pH9J_RFDMRb9Lj7&OfB(3nLVVtiEEy#mhQ%$H4K4hv+L>!j%1h0U7%nYWL z3^?I5CLf|kE+?qTe*C8d8|9O%GHlzh(Jc> zFNtLd4?VWB$mD|CK5Edc98~_Ms8bmKnU=M#Sz$Ulwf=ziwRzQ-;S(w4JLU2e9vl+8 zCq5v-ZZK{kmTeI^g=ut-*m6+14MejAAP62mSC6{@^g00AqGE&}m>kVD7AF`y?vY{I$y6sQW7 zS_5LoctQ3`4=`5&P&wn|d=BR|4VOZ%t(qmYsn9B$zwA$MtVUt(Ra$bOLz7Jsk&jYt zmS2|}eMs%B)_TwNGfUh?X^(F@Po%t5^~EuM6x($?ZftA4R)J_uIcO1gOVjwwRC&uN&Q_t9^E()l+uEQdCi$lGiO*3G-%z;tY-7M)y!?v!89|X z&`bfdv$!fOW>ISO5{(JxbSi}=J@^WJI!0Ak7*@h9UZgt26Qu>AC(p;HH?mYfp#%H+ zKNEL}?>bt?&^z=IVuTTykOlFzYlx3gH8RP^I6S5mG4lAxxrfx-;;4K9!21%zxQ)2_+|cg_b|Ol>@Hdc-R-Qrocgpf4+ld*B)?&7wGV!lDs*Q*WN? z2p!80TFNPZMKu>6%k1MF-w1jawP7!r6yJvuzP~}AwP|`Ix%&=4bh9`E@{))ooMrw9 zMISS@AOb%`kD-@$;eT}c5+O=ibB&@c?fs*#w}Z)=Akf9`NHi{F8-@C|JvK=cox8ut zHdNGl(dkRQqf3GNX(LL`ca2O&8l;b?>zNPqVBbll;kC_lXtr_K^fH!Ycj{JMV51zR z>dQZm-R4T_BZ}XNEWb3`e?>d#ID7=UTl!eG&6hF4_#`w0qw`z0W&tp9NNk`t-EsRV zjcE`>hKU~N+1G1=K%K3#WuQ$9@ytuiC?IVHcR_?3sl4P=^u7;|%Ia}p&TJV{s>FQy z8g?hER*+An(#CtuH#j+I4qg+V_RIs?=G7p;j@q4nvkYytNY-(j@>|k;+th?*+zhqV z@-<1)3160?M;IczC!#)!%HH(M$IdG{<=RjMp${yN5W5)0`5sa~*qzkQkm&|6v>MkZ z)%57)25ksgpXiUDHR-hI$MEh?Zqk;J)$u#EVDPo7dwf;D#Gklt7AyFMw5ZXqoy3}y zhhHrVdm1;-56fs2OJ~P%dgFb86S%%(;wI=OE;Q0+mTSp%=i6t+LEaFJsfb9?yQjNR zF70Jz<_~;*eZ7u8r)zj*9EH9n=FVNKvAJVr(T-$m=Q-zeF{@Tv`XQ|PmFj!cr+g*b zq04fmDA02krkXDgxJXi6H^OX9Ad(NumHO;2pJ^#>_PNoxXDfK7W!m2klKd)T-#~-D zN4F2mrotOU8W_6;jaeH{^&Z@ZE*JfZDar7@OlX8WVbfPZX0#maWoDYqy*5An1ps3;a+KP4Y4BOfrgX>Lc4Tbi9FqSZlQ8K(2x>1C|z9wQW_E# zsyl^bqAww^->xD06qa|6@u-@4a5~`n77zDZOCBGu3PR-Hui81FQMMWgBR>^!X*ZAv zOc3j}KQ*{YmQuV{MGG;LGiKf;eB~fQsDpB&%V8 zW*hX(_1jsYV^u6--_z)qIA-}nzCMeQ!~I4*Sc%Gu*n`YtE*kTbFjv^{oT(p~ zhyi50Q|jND5TJwAH%df~H*`Me1fI(szy7L4%dhi{FStw&3ER<^)h1uN3*Im9!t&)x z&2`Z47^P0#SvI_DS^+fNJ_}3aUA6~adft`MVuRVh0=}1?Z~-4rncNsTZ6~ohnAL8- zM)-2iYZ`pFP21{TJ=6}l9F|;IPIw^KCEyf^6Zx^_Lkog*I3}G)l6~+Gq$sR|(y%Md z*O!LhP5A1zuDd)%5NFos%xkqiMjT_n?qk@vS-XO`g<00+sGaT=OTZ+?Vgq>;mdgB_ z<#S=FSrNp$=$Sj}sRapq_-rnSsV z9qokk_SsicYRsZRn)PLlr0oSA&FKk-Eb6yP#TkSqd2t~4N)NO*SjhRP2RKJcKUC5= z{yCf5c{gNv2!I6l>QV<%yF7kOY3wyJ*y+$)w+nrlCHgunx3w^Kzl9u^0!ONS+Fc~y zod}$ph+d~%8Kr)Fyb!@LqaJ>lz=Ck9B1Yk=S#ke`2AdK zuZVA46DFU*1PaTWYQjMUk!P^qZaC$bIIhIG_!?4#2lB;CQB3aan+Z&7>KF?27ITZx zJx0CnKxc@vv&szk>?&M@0hr$C!+f*mVTKUgxQ!EflFjR#*ye+&$k-rA4qF+nUtFDG zN`I_V;J^=3>z!Yn44)~0)grj@*W_hF{~qu@HaaZZj@=p;)y4Y%r2I&l)#XO9#A#sb zG_^vF4lj^~b#oQCr+r*limJMY3H_(R+U{oDNmx0>@>>aJ9vzj@bfW&*L0y;^7Rk z>fSwpdD&3M8GBK{{D3x?l};6%V02YT3}EBteTm|;O!wDvM5aUTVkf6q01u_OQ!RnJUk=OE-~S+E|i;AhdQ?m9+j z#f#f!EB#gT+A4Y z6UFgaHnK!EeJmB#3?o*r8?&=I|Cb=|w+r^KL zUDy6PquODl)J@kPcvlOh~QUm za;E@$$~Uojd=Di{T*3ph|FaiGcz?hC)eYNxwH*vW7&Fw@T1aI$%RJPZY+)2pi9b_q z<(|9`3~4itt7XQOP4Z6mIu6fp`u@*p8vJ?G>+GISvucU;-$wiyc=*cwa4DJ_^HV0T zWBL$m##Uf@ut5qVGX)>^m%tkop1_hT10N>NE@S5T*esuljM{Zhq& zNTe6{Nk0=3Wa4li-gcRhB8E{$e=OV3mKf2BX{=YYXH!J(NN*M%U$y`Ga*%w92 ziSv=yq2FZbrY_OF777bmFJNKP|fLH{t*_iT4`oj`??E$RV+Vk zyDtm;zC@+>vYL|a;(5ba8@{^ z;k*9iuYfX(EKNg}utOd!D1W;JtN{nD``ayi&?=vTynVBUCf2alPBu?TCi zlQe~ILFLt>7FldTCWRK%{%0VIounyz3tDT|EVRgCGt!DDlf`%y4bv9v>n(7%K>z$5 z)(FQf<1Tp(i4R9y&abtAw>fIAXx|oIaZ(?tZ!>8kOJ9HW(t2(2OBLPOel2(<(+cl_ z_HDr{$(-e~-Mw%_}={q7svvQaGV!} zu7itf^_rTEt}P{7;6+b+b3%pA)x1AGitS_zc$-`;K(7+(^3y)U5Xlzsw_x`~p&m9-Wo z!`9GJJB~dpnI2guq(7OCIc$xv1^Gd1#?XJ?#J}SyXuCZPlcSx3^VFmuxvDcRH@ // Include the graphics library + +TFT_eSPI tft = TFT_eSPI(); // Create object "tft" + +// ------------------------------------------------------------------------- +// Setup +// ------------------------------------------------------------------------- +void setup(void) { + tft.init(); + tft.setRotation(0); + tft.fillScreen(TFT_DARKGREY); +} + +// ------------------------------------------------------------------------- +// Main loop +// ------------------------------------------------------------------------- +void loop() +{ + // 16 bit colours (5 bits red, 6 bits green, 5 bits blue) + // Blend from white to full spectrum + for (int a = 0; a < 256; a+=2) // Alpha 0 = 100% background, alpha 255 = 100% foreground + { + for (int c = 0; c < 192; c++) tft.drawPixel(c, a/2, tft.alphaBlend(a, rainbow(c), TFT_WHITE)); + } + + // Blend from full spectrum to black + for (int a = 255; a > 2; a-=2) + { + for (int c = 0; c < 192; c++) tft.drawPixel(c, 128 + (255-a)/2, tft.alphaBlend(a, rainbow(c), TFT_BLACK)); + } + + // Blend from white to black (32 grey levels) + for (uint16_t a = 0; a < 255; a++) // Alpha 0 = 100% background, alpha 255 = 100% foreground + { + tft.drawFastHLine(192, a, 12, tft.alphaBlend(a, TFT_BLACK, TFT_WHITE)); + tft.drawFastHLine(204, a, 12, tft.alphaBlend(a, TFT_BLACK, TFT_RED)); + tft.drawFastHLine(216, a, 12, tft.alphaBlend(a, TFT_BLACK, TFT_GREEN)); + tft.drawFastHLine(228, a, 12, tft.alphaBlend(a, TFT_BLACK, TFT_BLUE)); + } + + delay(4000); + + // Blend from white to colour (32 grey levels) + for (uint16_t a = 0; a < 255; a++) // Alpha 0 = 100% background, alpha 255 = 100% foreground + { + //tft.drawFastHLine(192, a, 12, tft.alphaBlend(a, TFT_BLACK, TFT_WHITE)); + tft.drawFastHLine(204, a, 12, tft.alphaBlend(a, TFT_RED, TFT_WHITE)); + tft.drawFastHLine(216, a, 12, tft.alphaBlend(a, TFT_GREEN, TFT_WHITE)); + tft.drawFastHLine(228, a, 12, tft.alphaBlend(a, TFT_BLUE, TFT_WHITE)); + } + + delay(4000); + + //* + // Decrease to 8 bit colour (3 bits red, 3 bits green, 2 bits blue) + // Blend from white to full spectrum + for (int a = 0; a < 256; a+=2) // Alpha 0 = 100% background, alpha 255 = 100% foreground + { + // Convert blended 16 bit colour to 8 bits to reduce colour resolution, then map back to 16 bits for displaying + for (int c = 0; c < 192; c++) tft.drawPixel(c, a/2, tft.color8to16(tft.color16to8(tft.alphaBlend(a, rainbow(c), 0xFFFF)))); + } + + // Blend from full spectrum to black + for (int a = 255; a > 2; a-=2) + { + // Convert blended 16 bit colour to 8 bits to reduce colour resolution, then map back to 16 bits for displaying + for (int c = 0; c < 192; c++) tft.drawPixel(c, 128 + (255-a)/2, tft.color8to16(tft.color16to8(tft.alphaBlend(a, rainbow(c), 0)))); + } + + // Blend from white to black (4 grey levels - it will draw 4 more with a blue tinge due to lower blue bit count) + // Blend from black to a primary colour + for (uint16_t a = 0; a < 255; a++) // Alpha 0 = 100% background, alpha 255 = 100% foreground + { + tft.drawFastHLine(192, a, 12, tft.color8to16(tft.color16to8(tft.alphaBlend(a, TFT_BLACK, TFT_WHITE)))); + tft.drawFastHLine(204, a, 12, tft.color8to16(tft.color16to8(tft.alphaBlend(a, TFT_BLACK, TFT_RED)))); + tft.drawFastHLine(216, a, 12, tft.color8to16(tft.color16to8(tft.alphaBlend(a, TFT_BLACK, TFT_GREEN)))); + tft.drawFastHLine(228, a, 12, tft.color8to16(tft.color16to8(tft.alphaBlend(a, TFT_BLACK, TFT_BLUE)))); + } + + delay(4000); + //*/ + + /* + // 16 bit colours (5 bits red, 6 bits green, 5 bits blue) + for (int a = 0; a < 256; a+=2) // Alpha 0 = 100% background, alpha 255 = 100% foreground + { + for (int c = 0; c < 192; c++) tft.drawPixel(c, a/2, tft.alphaBlend(a, rainbow(c), TFT_CYAN)); + } + + // Blend from full spectrum to cyan + for (int a = 255; a > 2; a-=2) + { + for (int c = 0; c < 192; c++) tft.drawPixel(c, 128 + (255-a)/2, tft.alphaBlend(a, rainbow(c), TFT_YELLOW)); + } + //*/ + + /* + // Blend other colour transitions for test purposes + for (uint16_t a = 0; a < 255; a++) // Alpha 0 = 100% background, alpha 255 = 100% foreground + { + tft.drawFastHLine(192, a, 12, tft.alphaBlend(a, TFT_WHITE, TFT_WHITE)); // Should show as solid white + tft.drawFastHLine(204, a, 12, tft.alphaBlend(a, TFT_BLACK, TFT_BLACK)); // Should show as solid black + tft.drawFastHLine(216, a, 12, tft.alphaBlend(a, TFT_YELLOW, TFT_CYAN)); // Brightness should be fairly even + tft.drawFastHLine(228, a, 12, tft.alphaBlend(a, TFT_CYAN, TFT_MAGENTA));// Brightness should be fairly even + } + + delay(4000); + //*/ +} + + +// ######################################################################### +// Return a 16 bit rainbow colour +// ######################################################################### +unsigned int rainbow(byte value) +{ + // If 'value' is in the range 0-159 it is converted to a spectrum colour + // from 0 = red through to 127 = blue to 159 = violet + // Extending the range to 0-191 adds a further violet to red band + + value = value%192; + + byte red = 0; // Red is the top 5 bits of a 16 bit colour value + byte green = 0; // Green is the middle 6 bits, but only top 5 bits used here + byte blue = 0; // Blue is the bottom 5 bits + + byte sector = value >> 5; + byte amplit = value & 0x1F; + + switch (sector) + { + case 0: + red = 0x1F; + green = amplit; // Green ramps up + blue = 0; + break; + case 1: + red = 0x1F - amplit; // Red ramps down + green = 0x1F; + blue = 0; + break; + case 2: + red = 0; + green = 0x1F; + blue = amplit; // Blue ramps up + break; + case 3: + red = 0; + green = 0x1F - amplit; // Green ramps down + blue = 0x1F; + break; + case 4: + red = amplit; // Red ramps up + green = 0; + blue = 0x1F; + break; + case 5: + red = 0x1F; + green = 0; + blue = 0x1F - amplit; // Blue ramps down + break; + } + + return red << 11 | green << 6 | blue; +} + + diff --git a/keywords.txt b/keywords.txt index 13748fc..5d4f945 100644 --- a/keywords.txt +++ b/keywords.txt @@ -56,7 +56,8 @@ getRotation KEYWORD2 getTextDatum KEYWORD2 fontsLoaded KEYWORD2 color565 KEYWORD2 -color332 KEYWORD2 +color16to8 KEYWORD2 +color8to16 KEYWORD2 drawNumber KEYWORD2 drawFloat KEYWORD2 drawString KEYWORD2 @@ -98,3 +99,8 @@ pushBitmap KEYWORD2 pushSprite KEYWORD2 setScrollRect KEYWORD2 scroll KEYWORD2 + +alphaBlend KEYWORD2 +showFont KEYWORD2 +loadFont KEYWORD2 +unloadFont KEYWORD2 diff --git a/library.json b/library.json index 2041a92..fd60348 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "TFT_eSPI", - "version": "0.18.17", + "version": "0.18.20", "keywords": "tft, display, ESP8266, NodeMCU, ESP32, M5Stack, ILI9341, ST7735, ILI9163, S6D02A1, ILI9486", "description": "A TFT SPI graphics library for ESP8266 and ESP32", "repository": diff --git a/library.properties b/library.properties index e235cd1..a70b692 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=TFT_eSPI -version=0.18.17 +version=0.18.20 author=Bodmer maintainer=Bodmer sentence=A fast TFT library for ESP8266 processors and the Arduino IDE diff --git a/license.txt b/license.txt index befa9a4..8fe1fb4 100644 --- a/license.txt +++ b/license.txt @@ -28,7 +28,7 @@ vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvStartvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv Selected functions from the Adafruit_GFX library (as it was in 2015) have been imported into the TFT_eSPI.cpp file and modified to improve -perfromance, add features and make them compatible with the ESP8266 and +performance, add features and make them compatible with the ESP8266 and ESP32. The fonts from the Adafruit_GFX and Button functions were added later. @@ -68,7 +68,7 @@ POSSIBILITY OF SUCH DAMAGE. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^End^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Due to the evolution of the TFT_eSPI library the orignal code may no longer +Due to the evolution of the TFT_eSPI library the original code may no longer be recognisable, however in most cases the function names can be used as a reference point since the aim is to retain a level of compatibility with the popular Adafruit_GFX graphics functions. From 324ee1a5108dab22a65c4aa65b21a13264710979 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Sat, 24 Feb 2018 19:31:25 +0000 Subject: [PATCH 089/150] Update files for new fonts --- README.md | 2 +- README.txt | 2 +- User_Setup.h | 8 ++++---- library.json | 2 +- library.properties | 2 +- license.txt | 3 +++ 6 files changed, 11 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 79bf602..9382470 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # TFT_eSPI ->>> This branch includes new antialiased font capability, this is a work-in-progress <<< +Update 24th February 2018: Added new smooth (antialiased) fonts. See Smooth Font examples and Tools folder for the font generator. An Arduino IDE compatible graphics and fonts library for ESP8266 and ESP32 processors with a driver for ILI9341, ILI9163, ST7735 and S6D02A1 based TFT displays that support SPI. diff --git a/README.txt b/README.txt index 8262ce5..03ec449 100644 --- a/README.txt +++ b/README.txt @@ -11,7 +11,7 @@ New functions have been added in particular it contains proportional fonts in addition to the original Adafruit font. A sprite class has been added to aid the generation of flicker free complex -raphics. +graphics. Note: This version of the library might not be fully compatible with the original. diff --git a/User_Setup.h b/User_Setup.h index 98f7d50..3d61313 100644 --- a/User_Setup.h +++ b/User_Setup.h @@ -88,7 +88,7 @@ #define TFT_RST PIN_D4 // Reset pin (could connect to NodeMCU RST, see next line) //#define TFT_RST -1 // Set TFT_RST to -1 if the display RESET is connected to NodeMCU RST or 3.3V -#define TOUCH_CS PIN_D2 // Chip select pin (T_CS) of touch screen +//#define TOUCH_CS PIN_D2 // Chip select pin (T_CS) of touch screen //#define TFT_WR PIN_D2 // Write strobe for modified Raspberry Pi TFT only @@ -196,8 +196,8 @@ // #define SPI_FREQUENCY 5000000 // #define SPI_FREQUENCY 10000000 // #define SPI_FREQUENCY 20000000 -//#define SPI_FREQUENCY 27000000 // Actually sets it to 26.67MHz = 80/3 - #define SPI_FREQUENCY 40000000 // Maximum to use SPIFFS +#define SPI_FREQUENCY 27000000 // Actually sets it to 26.67MHz = 80/3 +// #define SPI_FREQUENCY 40000000 // Maximum to use SPIFFS // #define SPI_FREQUENCY 80000000 // The XPT2046 requires a lower SPI clock rate of 2.5MHz so we define that here: @@ -214,4 +214,4 @@ // Transactions are automatically enabled by the library for an ESP32 (to use HAL mutex) // so changing it here has no effect - #define SUPPORT_TRANSACTIONS +//#define SUPPORT_TRANSACTIONS diff --git a/library.json b/library.json index fd60348..019b150 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "TFT_eSPI", - "version": "0.18.20", + "version": "0.18.21", "keywords": "tft, display, ESP8266, NodeMCU, ESP32, M5Stack, ILI9341, ST7735, ILI9163, S6D02A1, ILI9486", "description": "A TFT SPI graphics library for ESP8266 and ESP32", "repository": diff --git a/library.properties b/library.properties index a70b692..1f05e67 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=TFT_eSPI -version=0.18.20 +version=0.18.21 author=Bodmer maintainer=Bodmer sentence=A fast TFT library for ESP8266 processors and the Arduino IDE diff --git a/license.txt b/license.txt index 8fe1fb4..a58b151 100644 --- a/license.txt +++ b/license.txt @@ -91,6 +91,9 @@ the ESP32. In 2017 new Touch Screen functions were added and a new Sprite class called TFT_eSprite to permit "flicker free" screen updates of complex graphics. +In 2018 anti-aliased fonts were added along with a Processing font conversion +sketch. + Many of the example sketches are original work, that contain code created for my own projects. For all the original code the FreeBSD licence applies and is compatible with the GNU GPL. From 1656fa1a5fb2d17eafcf843825a8d7aa5b802bb1 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Sat, 24 Feb 2018 19:38:13 +0000 Subject: [PATCH 090/150] Add image to Readme --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 9382470..9727a82 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,8 @@ Update 24th February 2018: Added new smooth (antialiased) fonts. See Smooth Font examples and Tools folder for the font generator. +![Example](https://i.imgur.com/xJF0Oz7.png) + An Arduino IDE compatible graphics and fonts library for ESP8266 and ESP32 processors with a driver for ILI9341, ILI9163, ST7735 and S6D02A1 based TFT displays that support SPI. The library also supports TFT displays designed for the Raspberry Pi that are based on a ILI9486 driver chip with a 480 x 320 pixel screen. This display must be of the Waveshare design and use a 16 bit serial interface based on the 74HC04, 74HC4040 and 2 x 74HC4094 logic chips. A modification to these displays is possible (see mod image in Tools folder) to make many graphics functions much faster (e.g. 23ms to clear the screen, 1.2ms to draw a 72 pixel high numeral). From a00011c2df1a7f27abb9dcf20cd5f3cbbe1de512 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Sat, 10 Mar 2018 23:08:57 +0000 Subject: [PATCH 091/150] Add 8 bit parallel TFT support for ESP32 Add interface driver macro Add 8 bit parallel support Add 8 bit parallel read support Add ILI9481, ILI9488 and HX8357D drivers Update version and ReadMe Update Create_Font processing sketch Add slightly narrower alternative font 8 --- Extensions/Smooth_font.cpp | 22 +- Fonts/Font72x53rle.c | 247 +++++++ Fonts/Font72x53rle.h | 10 + README.md | 20 +- TFT_Drivers/HX8357D_Defines.h | 86 +++ TFT_Drivers/HX8357D_Init.h | 118 ++++ TFT_Drivers/HX8357D_Rotation.h | 26 + TFT_Drivers/ILI9481_Defines.h | 42 ++ TFT_Drivers/ILI9481_Init.h | 77 +++ TFT_Drivers/ILI9481_Rotation.h | 27 + TFT_Drivers/ILI9488_Defines.h | 42 ++ TFT_Drivers/ILI9488_Init.h | 97 +++ TFT_Drivers/ILI9488_Rotation.h | 27 + TFT_eSPI.cpp | 611 +++++++++++------- TFT_eSPI.h | 88 ++- .../Create_font.pde} | 16 +- .../data/Final-Frontier.ttf | Bin User_Setup.h | 34 + User_Setup_Select.h | 10 + library.json | 2 +- library.properties | 2 +- 21 files changed, 1343 insertions(+), 261 deletions(-) create mode 100644 Fonts/Font72x53rle.c create mode 100644 Fonts/Font72x53rle.h create mode 100644 TFT_Drivers/HX8357D_Defines.h create mode 100644 TFT_Drivers/HX8357D_Init.h create mode 100644 TFT_Drivers/HX8357D_Rotation.h create mode 100644 TFT_Drivers/ILI9481_Defines.h create mode 100644 TFT_Drivers/ILI9481_Init.h create mode 100644 TFT_Drivers/ILI9481_Rotation.h create mode 100644 TFT_Drivers/ILI9488_Defines.h create mode 100644 TFT_Drivers/ILI9488_Init.h create mode 100644 TFT_Drivers/ILI9488_Rotation.h rename Tools/Create_Smooth_Font/{Create_font_5/Create_font_5.pde => Create_font/Create_font.pde} (98%) rename Tools/Create_Smooth_Font/{Create_font_5 => Create_font}/data/Final-Frontier.ttf (100%) diff --git a/Extensions/Smooth_font.cpp b/Extensions/Smooth_font.cpp index dc41edb..b5559e1 100644 --- a/Extensions/Smooth_font.cpp +++ b/Extensions/Smooth_font.cpp @@ -338,6 +338,8 @@ uint16_t TFT_eSPI::alphaBlend(uint8_t alpha, uint16_t fgc, uint16_t bgc) uint16_t b = (((fgB * alpha) + (bgB * (255 - alpha))) >> 9); // Combine RGB565 colours into 16 bits + //return ((r&0x18) << 11) | ((g&0x30) << 5) | ((b&0x18) << 0); // 2 bit greyscale + //return ((r&0x1E) << 11) | ((g&0x3C) << 5) | ((b&0x1E) << 0); // 4 bit greyscale return (r << 11) | (g << 5) | (b << 0); } @@ -419,7 +421,10 @@ void TFT_eSPI::drawGlyph(uint16_t code) uint8_t pbuffer[gWidth[gNum]]; uint16_t xs = 0; - uint16_t dl = 0; + uint32_t dl = 0; + + int16_t cy = cursor_y + gFont.maxAscent - gdY[gNum]; + int16_t cx = cursor_x + gdX[gNum]; for (int y = 0; y < gHeight[gNum]; y++) { @@ -431,24 +436,27 @@ void TFT_eSPI::drawGlyph(uint16_t code) { if (pixel != 0xFF) { - if (dl) { drawFastHLine( xs, y + cursor_y + gFont.maxAscent - gdY[gNum], dl, fg); dl = 0; } - drawPixel(x + cursor_x + gdX[gNum], y + cursor_y + gFont.maxAscent - gdY[gNum], alphaBlend(pixel, fg, bg)); + if (dl) { + if (dl==1) drawPixel(xs, y + cy, fg); + else drawFastHLine( xs, y + cy, dl, fg); + dl = 0; + } + drawPixel(x + cx, y + cy, alphaBlend(pixel, fg, bg)); } else { - if (dl==0) xs = x + cursor_x + gdX[gNum]; + if (dl==0) xs = x + cx; dl++; } } else { - if (dl) { drawFastHLine( xs, y + cursor_y + gFont.maxAscent - gdY[gNum], dl, fg); dl = 0; } + if (dl) { drawFastHLine( xs, y + cy, dl, fg); dl = 0; } } } - if (dl) { drawFastHLine( xs, y + cursor_y + gFont.maxAscent - gdY[gNum], dl, fg); dl = 0; } + if (dl) { drawFastHLine( xs, y + cy, dl, fg); dl = 0; } } - cursor_x += gxAdvance[gNum]; } else diff --git a/Fonts/Font72x53rle.c b/Fonts/Font72x53rle.c new file mode 100644 index 0000000..bcce9cb --- /dev/null +++ b/Fonts/Font72x53rle.c @@ -0,0 +1,247 @@ +// Font 8 +// +// This font has been 8 bit Run Length Encoded to save FLASH space +// +// It is a Arial 75 pixel height font intended to display large numbers +// Width for numerals reduced from 55 to 53 (to fit in 160 pixel screens) +// This font only contains characters [space] 0 1 2 3 4 5 6 7 8 9 0 : - . +// All other characters print as a space + +#include + + +PROGMEM const unsigned char widtbl_f72[96] = // character width table +{ + 29, 29, 29, 29, 29, 29, 29, 29, // char 32 - 39 + 29, 29, 29, 29, 29, 29, 29, 29, // char 40 - 47 + 53, 53, 53, 53, 53, 53, 53, 53, // char 48 - 55 + 53, 53, 29, 29, 29, 29, 29, 29, // char 56 - 63 + 29, 29, 29, 29, 29, 29, 29, 29, // char 64 - 71 + 29, 29, 29, 29, 29, 29, 29, 29, // char 72 - 79 + 29, 29, 29, 29, 29, 29, 29, 29, // char 80 - 87 + 29, 29, 29, 29, 29, 29, 29, 29, // char 88 - 95 + 29, 29, 29, 29, 29, 29, 29, 29, // char 96 - 103 + 29, 29, 29, 29, 29, 29, 29, 29, // char 104 - 111 + 29, 29, 29, 29, 29, 29, 29, 29, // char 112 - 119 + 29, 29, 29, 29, 29, 29, 29, 29 // char 120 - 127 +}; + +// Row format, MSB left + +PROGMEM const unsigned char chr_f72_20[] = +{ +0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, +0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, +0x7E +}; + +PROGMEM const unsigned char chr_f72_2D[] = +{ +0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, +0x36, 0x91, 0x0A, 0x91, 0x0A, 0x91, 0x0A, 0x91, +0x0A, 0x91, 0x0A, 0x91, 0x0A, 0x91, 0x7F, 0x7F, +0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x07 +}; + +PROGMEM const unsigned char chr_f72_2E[] = +{ +0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, +0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x48, 0x88, +0x13, 0x88, 0x13, 0x88, 0x13, 0x88, 0x13, 0x88, +0x13, 0x88, 0x13, 0x88, 0x13, 0x88, 0x13, 0x88, +0x44 +}; + +PROGMEM const unsigned char chr_f72_30[] = +{ +0x7F, 0x68, 0x8A, 0x26, 0x90, 0x21, 0x94, 0x1D, 0x98, 0x1A, 0x9A, 0x18, 0x9C, 0x16, 0x9E, 0x14, +0xA0, 0x13, 0x8C, 0x06, 0x8C, 0x12, 0x8B, 0x0A, 0x8B, 0x10, 0x8A, 0x0E, 0x89, 0x10, 0x89, 0x10, +0x89, 0x0F, 0x88, 0x12, 0x88, 0x0E, 0x89, 0x12, 0x89, 0x0D, 0x88, 0x14, 0x88, 0x0C, 0x89, 0x14, +0x88, 0x0C, 0x88, 0x16, 0x88, 0x0B, 0x88, 0x16, 0x88, 0x0B, 0x88, 0x16, 0x88, 0x0A, 0x88, 0x18, +0x88, 0x09, 0x88, 0x18, 0x88, 0x09, 0x88, 0x18, 0x88, 0x09, 0x88, 0x18, 0x88, 0x09, 0x88, 0x18, +0x88, 0x09, 0x88, 0x18, 0x88, 0x08, 0x88, 0x1A, 0x88, 0x07, 0x88, 0x1A, 0x88, 0x07, 0x88, 0x1A, +0x88, 0x07, 0x88, 0x1A, 0x88, 0x07, 0x88, 0x1A, 0x88, 0x07, 0x88, 0x1A, 0x88, 0x07, 0x88, 0x1A, +0x88, 0x07, 0x88, 0x1A, 0x88, 0x07, 0x88, 0x1A, 0x88, 0x07, 0x88, 0x1A, 0x88, 0x07, 0x88, 0x1A, +0x88, 0x07, 0x88, 0x1A, 0x88, 0x07, 0x88, 0x1A, 0x88, 0x07, 0x88, 0x1A, 0x88, 0x07, 0x88, 0x1A, +0x88, 0x07, 0x88, 0x1A, 0x88, 0x07, 0x88, 0x1A, 0x88, 0x07, 0x88, 0x1A, 0x88, 0x07, 0x88, 0x1A, +0x88, 0x07, 0x88, 0x1A, 0x88, 0x08, 0x88, 0x18, 0x88, 0x09, 0x88, 0x18, 0x88, 0x09, 0x88, 0x18, +0x88, 0x09, 0x88, 0x18, 0x88, 0x09, 0x88, 0x18, 0x88, 0x09, 0x88, 0x18, 0x88, 0x0A, 0x88, 0x16, +0x88, 0x0B, 0x88, 0x16, 0x88, 0x0B, 0x88, 0x16, 0x88, 0x0B, 0x89, 0x14, 0x89, 0x0C, 0x88, 0x14, +0x88, 0x0D, 0x89, 0x12, 0x89, 0x0E, 0x88, 0x12, 0x88, 0x0F, 0x89, 0x10, 0x89, 0x0F, 0x8A, 0x0E, +0x8A, 0x10, 0x8B, 0x0A, 0x8B, 0x12, 0x8C, 0x06, 0x8C, 0x13, 0xA0, 0x14, 0x9E, 0x16, 0x9C, 0x18, +0x9A, 0x1A, 0x98, 0x1D, 0x94, 0x21, 0x90, 0x26, 0x8A, 0x49 +}; + +PROGMEM const unsigned char chr_f72_31[] = +{ +0x7F, 0x70, 0x85, 0x2D, 0x86, 0x2D, 0x86, 0x2C, 0x87, 0x2B, 0x88, 0x2B, 0x88, 0x2A, 0x89, 0x29, +0x8A, 0x28, 0x8B, 0x27, 0x8C, 0x25, 0x8E, 0x24, 0x8F, 0x23, 0x90, 0x22, 0x91, 0x20, 0x93, 0x1E, +0x95, 0x1C, 0x8D, 0x00, 0x88, 0x1B, 0x8C, 0x02, 0x88, 0x1B, 0x8B, 0x03, 0x88, 0x1B, 0x8A, 0x04, +0x88, 0x1B, 0x88, 0x06, 0x88, 0x1B, 0x87, 0x07, 0x88, 0x1B, 0x85, 0x09, 0x88, 0x1B, 0x83, 0x0B, +0x88, 0x1B, 0x81, 0x0D, 0x88, 0x2B, 0x88, 0x2B, 0x88, 0x2B, 0x88, 0x2B, 0x88, 0x2B, 0x88, 0x2B, +0x88, 0x2B, 0x88, 0x2B, 0x88, 0x2B, 0x88, 0x2B, 0x88, 0x2B, 0x88, 0x2B, 0x88, 0x2B, 0x88, 0x2B, +0x88, 0x2B, 0x88, 0x2B, 0x88, 0x2B, 0x88, 0x2B, 0x88, 0x2B, 0x88, 0x2B, 0x88, 0x2B, 0x88, 0x2B, +0x88, 0x2B, 0x88, 0x2B, 0x88, 0x2B, 0x88, 0x2B, 0x88, 0x2B, 0x88, 0x2B, 0x88, 0x2B, 0x88, 0x2B, +0x88, 0x2B, 0x88, 0x2B, 0x88, 0x2B, 0x88, 0x2B, 0x88, 0x2B, 0x88, 0x2B, 0x88, 0x2B, 0x88, 0x2B, +0x88, 0x2B, 0x88, 0x2B, 0x88, 0x2B, 0x88, 0x2B, 0x88, 0x2B, 0x88, 0x2B, 0x88, 0x7B +}; + +PROGMEM const unsigned char chr_f72_32[] = +{ +0x7F, 0x67, 0x8A, 0x25, 0x92, 0x1F, 0x96, 0x1B, 0x9A, 0x18, 0x9C, 0x16, 0x9E, 0x14, 0xA0, 0x12, +0xA2, 0x10, 0x8E, 0x07, 0x8D, 0x0F, 0x8B, 0x0C, 0x8C, 0x0D, 0x8A, 0x10, 0x8A, 0x0D, 0x89, 0x12, +0x8A, 0x0B, 0x89, 0x14, 0x89, 0x0B, 0x89, 0x14, 0x89, 0x0B, 0x88, 0x16, 0x89, 0x0A, 0x88, 0x16, +0x89, 0x09, 0x88, 0x18, 0x88, 0x09, 0x88, 0x18, 0x88, 0x09, 0x88, 0x18, 0x88, 0x0D, 0x84, 0x18, +0x88, 0x2B, 0x88, 0x2B, 0x88, 0x2B, 0x88, 0x2A, 0x89, 0x2A, 0x88, 0x2A, 0x89, 0x2A, 0x89, 0x29, +0x89, 0x2A, 0x89, 0x29, 0x89, 0x29, 0x8A, 0x28, 0x8A, 0x28, 0x8B, 0x27, 0x8B, 0x27, 0x8B, 0x27, +0x8B, 0x27, 0x8B, 0x27, 0x8C, 0x26, 0x8C, 0x26, 0x8C, 0x26, 0x8C, 0x26, 0x8C, 0x25, 0x8C, 0x26, +0x8C, 0x26, 0x8C, 0x26, 0x8C, 0x26, 0x8C, 0x25, 0x8D, 0x25, 0x8D, 0x25, 0x8C, 0x26, 0x8C, 0x26, +0x8C, 0x27, 0x8B, 0x27, 0x8B, 0x27, 0x8A, 0x28, 0x8A, 0x29, 0x89, 0x29, 0x8A, 0x29, 0x89, 0x29, +0x89, 0x2A, 0xAA, 0x08, 0xAB, 0x08, 0xAB, 0x08, 0xAB, 0x07, 0xAC, 0x07, 0xAC, 0x07, 0xAC, 0x07, +0xAC, 0x07, 0xAC, 0x6E +}; + +PROGMEM const unsigned char chr_f72_33[] = +{ +0x7F, 0x67, 0x89, 0x27, 0x90, 0x21, 0x94, 0x1D, 0x97, 0x1B, 0x9A, 0x18, 0x9C, 0x16, 0x9E, 0x14, +0xA0, 0x13, 0x8C, 0x06, 0x8C, 0x12, 0x8B, 0x0A, 0x8B, 0x10, 0x8A, 0x0E, 0x89, 0x10, 0x89, 0x10, +0x89, 0x0F, 0x88, 0x12, 0x88, 0x0E, 0x89, 0x12, 0x89, 0x0D, 0x88, 0x14, 0x88, 0x0D, 0x88, 0x14, +0x88, 0x0C, 0x89, 0x14, 0x88, 0x0C, 0x88, 0x15, 0x88, 0x10, 0x84, 0x15, 0x88, 0x2B, 0x88, 0x2B, +0x88, 0x2A, 0x88, 0x2B, 0x88, 0x2A, 0x89, 0x29, 0x89, 0x29, 0x89, 0x28, 0x8B, 0x26, 0x8C, 0x21, +0x91, 0x22, 0x8F, 0x24, 0x8D, 0x26, 0x8F, 0x23, 0x92, 0x21, 0x94, 0x1F, 0x95, 0x1E, 0x81, 0x07, +0x8C, 0x29, 0x8B, 0x2A, 0x8A, 0x2A, 0x89, 0x2B, 0x89, 0x2B, 0x89, 0x2A, 0x89, 0x2B, 0x88, 0x2B, +0x89, 0x2B, 0x88, 0x2B, 0x88, 0x2B, 0x88, 0x2B, 0x88, 0x2B, 0x88, 0x2B, 0x88, 0x0B, 0x84, 0x1A, +0x88, 0x07, 0x88, 0x1A, 0x88, 0x07, 0x89, 0x18, 0x89, 0x07, 0x89, 0x18, 0x88, 0x09, 0x88, 0x18, +0x88, 0x09, 0x89, 0x16, 0x89, 0x09, 0x89, 0x15, 0x89, 0x0B, 0x89, 0x14, 0x89, 0x0B, 0x8A, 0x12, +0x89, 0x0D, 0x8A, 0x10, 0x8A, 0x0D, 0x8B, 0x0D, 0x8B, 0x0F, 0x8D, 0x07, 0x8D, 0x11, 0xA2, 0x12, +0xA0, 0x14, 0x9D, 0x17, 0x9B, 0x19, 0x99, 0x1C, 0x95, 0x20, 0x91, 0x26, 0x89, 0x4A +}; + +PROGMEM const unsigned char chr_f72_34[] = +{ +0x7F, 0x7F, 0x2A, 0x86, 0x2C, 0x87, 0x2B, 0x88, 0x2A, 0x89, 0x2A, 0x89, 0x29, 0x8A, 0x28, 0x8B, +0x27, 0x8C, 0x26, 0x8D, 0x26, 0x8D, 0x25, 0x8E, 0x24, 0x8F, 0x23, 0x90, 0x23, 0x90, 0x22, 0x91, +0x21, 0x92, 0x20, 0x93, 0x20, 0x93, 0x1F, 0x8A, 0x00, 0x88, 0x1E, 0x8A, 0x01, 0x88, 0x1D, 0x8A, +0x02, 0x88, 0x1C, 0x8B, 0x02, 0x88, 0x1C, 0x8A, 0x03, 0x88, 0x1B, 0x8A, 0x04, 0x88, 0x1A, 0x8A, +0x05, 0x88, 0x19, 0x8A, 0x06, 0x88, 0x19, 0x8A, 0x06, 0x88, 0x18, 0x8A, 0x07, 0x88, 0x17, 0x8A, +0x08, 0x88, 0x16, 0x8A, 0x09, 0x88, 0x16, 0x8A, 0x09, 0x88, 0x15, 0x8A, 0x0A, 0x88, 0x14, 0x8A, +0x0B, 0x88, 0x13, 0x8A, 0x0C, 0x88, 0x13, 0x8A, 0x0C, 0x88, 0x12, 0x8A, 0x0D, 0x88, 0x11, 0x8A, +0x0E, 0x88, 0x10, 0x8A, 0x0F, 0x88, 0x0F, 0x8B, 0x0F, 0x88, 0x0F, 0x8A, 0x10, 0x88, 0x0E, 0x8A, +0x11, 0x88, 0x0D, 0x8A, 0x12, 0x88, 0x0C, 0x8A, 0x13, 0x88, 0x0C, 0xAF, 0x04, 0xAF, 0x04, 0xAF, +0x04, 0xAF, 0x04, 0xAF, 0x04, 0xAF, 0x04, 0xAF, 0x04, 0xAF, 0x04, 0xAF, 0x23, 0x88, 0x2B, 0x88, +0x2B, 0x88, 0x2B, 0x88, 0x2B, 0x88, 0x2B, 0x88, 0x2B, 0x88, 0x2B, 0x88, 0x2B, 0x88, 0x2B, 0x88, +0x2B, 0x88, 0x2B, 0x88, 0x2B, 0x88, 0x2B, 0x88, 0x2B, 0x88, 0x2B, 0x88, 0x75 +}; + +PROGMEM const unsigned char chr_f72_35[] = +{ +0x7F, 0x7F, 0x14, 0xA0, 0x13, 0xA0, 0x12, 0xA1, 0x12, 0xA1, 0x12, 0xA1, 0x12, 0xA1, 0x12, 0xA1, +0x11, 0xA2, 0x11, 0xA2, 0x11, 0x88, 0x2B, 0x88, 0x2B, 0x88, 0x2A, 0x89, 0x2A, 0x88, 0x2B, 0x88, +0x2B, 0x88, 0x2B, 0x88, 0x2A, 0x89, 0x2A, 0x88, 0x2B, 0x88, 0x2B, 0x88, 0x2B, 0x88, 0x2A, 0x89, +0x06, 0x88, 0x1A, 0x89, 0x03, 0x8E, 0x17, 0x88, 0x02, 0x92, 0x15, 0x88, 0x00, 0x96, 0x13, 0xA1, +0x11, 0xA3, 0x10, 0xA4, 0x0F, 0xA5, 0x0E, 0x8F, 0x07, 0x8E, 0x0D, 0x8C, 0x0D, 0x8C, 0x0B, 0x8B, +0x11, 0x8A, 0x0B, 0x8A, 0x13, 0x8A, 0x0A, 0x89, 0x15, 0x89, 0x0E, 0x84, 0x17, 0x89, 0x2A, 0x89, +0x2B, 0x88, 0x2B, 0x88, 0x2B, 0x89, 0x2B, 0x88, 0x2B, 0x88, 0x2B, 0x88, 0x2B, 0x88, 0x2B, 0x88, +0x2B, 0x88, 0x2B, 0x88, 0x2B, 0x88, 0x2B, 0x88, 0x0B, 0x84, 0x1A, 0x88, 0x07, 0x88, 0x19, 0x88, +0x08, 0x89, 0x18, 0x88, 0x08, 0x89, 0x18, 0x88, 0x09, 0x88, 0x17, 0x89, 0x09, 0x89, 0x16, 0x88, +0x0A, 0x89, 0x15, 0x89, 0x0B, 0x89, 0x13, 0x89, 0x0C, 0x8A, 0x11, 0x8A, 0x0C, 0x8B, 0x0F, 0x8A, +0x0E, 0x8B, 0x0D, 0x8A, 0x10, 0x8D, 0x07, 0x8D, 0x10, 0xA2, 0x12, 0xA0, 0x14, 0x9E, 0x17, 0x9B, +0x19, 0x98, 0x1D, 0x95, 0x20, 0x90, 0x26, 0x8A, 0x4A +}; + +PROGMEM const unsigned char chr_f72_36[] = +{ +0x7F, 0x6A, 0x89, 0x26, 0x90, 0x21, 0x95, 0x1C, 0x98, 0x1A, 0x9A, 0x18, 0x9C, 0x16, 0x9E, 0x14, +0xA0, 0x12, 0x8D, 0x06, 0x8D, 0x10, 0x8B, 0x0B, 0x8B, 0x10, 0x8A, 0x0E, 0x8A, 0x0E, 0x89, 0x11, +0x89, 0x0D, 0x8A, 0x12, 0x89, 0x0C, 0x89, 0x13, 0x89, 0x0C, 0x88, 0x15, 0x88, 0x0B, 0x89, 0x15, +0x89, 0x0A, 0x88, 0x16, 0x89, 0x09, 0x89, 0x17, 0x88, 0x09, 0x88, 0x18, 0x84, 0x0D, 0x88, 0x2B, +0x87, 0x2B, 0x88, 0x2B, 0x88, 0x2B, 0x88, 0x2B, 0x88, 0x0A, 0x88, 0x17, 0x87, 0x08, 0x8E, 0x14, +0x87, 0x06, 0x92, 0x11, 0x88, 0x04, 0x96, 0x0F, 0x88, 0x03, 0x98, 0x0E, 0x88, 0x02, 0x9A, 0x0D, +0x88, 0x01, 0x9C, 0x0C, 0x88, 0x00, 0x9E, 0x0B, 0x92, 0x07, 0x8E, 0x0A, 0x90, 0x0C, 0x8C, 0x09, +0x8E, 0x10, 0x8A, 0x09, 0x8D, 0x12, 0x8A, 0x08, 0x8C, 0x14, 0x89, 0x08, 0x8B, 0x16, 0x89, 0x07, +0x8A, 0x17, 0x89, 0x07, 0x89, 0x19, 0x88, 0x07, 0x89, 0x19, 0x88, 0x07, 0x89, 0x19, 0x89, 0x06, +0x88, 0x1B, 0x88, 0x06, 0x88, 0x1B, 0x88, 0x06, 0x88, 0x1B, 0x88, 0x06, 0x88, 0x1B, 0x88, 0x07, +0x87, 0x1B, 0x88, 0x07, 0x87, 0x1B, 0x88, 0x07, 0x87, 0x1B, 0x88, 0x07, 0x87, 0x1B, 0x88, 0x07, +0x88, 0x1A, 0x88, 0x08, 0x87, 0x19, 0x89, 0x08, 0x87, 0x19, 0x88, 0x09, 0x88, 0x18, 0x88, 0x09, +0x88, 0x17, 0x89, 0x0A, 0x88, 0x16, 0x88, 0x0B, 0x88, 0x15, 0x89, 0x0C, 0x88, 0x14, 0x89, 0x0C, +0x89, 0x12, 0x89, 0x0E, 0x89, 0x10, 0x8A, 0x0E, 0x8B, 0x0C, 0x8B, 0x10, 0x8C, 0x07, 0x8D, 0x12, +0xA1, 0x13, 0x9F, 0x15, 0x9D, 0x17, 0x9B, 0x1A, 0x97, 0x1D, 0x95, 0x21, 0x8F, 0x27, 0x89, 0x49 + +}; + +PROGMEM const unsigned char chr_f72_37[] = +{ +0x7F, 0x7F, 0x0D, 0xAB, 0x08, 0xAB, 0x08, 0xAB, 0x08, 0xAB, 0x08, 0xAB, 0x08, 0xAB, 0x08, 0xAB, +0x08, 0xAB, 0x08, 0xAA, 0x2C, 0x86, 0x2C, 0x86, 0x2C, 0x87, 0x2B, 0x87, 0x2B, 0x87, 0x2B, 0x87, +0x2C, 0x87, 0x2B, 0x87, 0x2B, 0x87, 0x2C, 0x87, 0x2B, 0x87, 0x2B, 0x88, 0x2B, 0x87, 0x2B, 0x87, +0x2B, 0x88, 0x2B, 0x87, 0x2B, 0x88, 0x2B, 0x87, 0x2B, 0x88, 0x2A, 0x88, 0x2B, 0x88, 0x2A, 0x88, +0x2B, 0x88, 0x2A, 0x88, 0x2B, 0x88, 0x2A, 0x88, 0x2B, 0x88, 0x2B, 0x88, 0x2A, 0x88, 0x2B, 0x88, +0x2A, 0x88, 0x2B, 0x88, 0x2B, 0x88, 0x2A, 0x88, 0x2B, 0x88, 0x2B, 0x88, 0x2A, 0x88, 0x2B, 0x88, +0x2B, 0x88, 0x2A, 0x88, 0x2B, 0x88, 0x2B, 0x88, 0x2B, 0x88, 0x2A, 0x88, 0x2B, 0x88, 0x2B, 0x88, +0x2B, 0x88, 0x2A, 0x88, 0x2B, 0x88, 0x2B, 0x88, 0x2B, 0x88, 0x2B, 0x88, 0x2B, 0x88, 0x2A, 0x89, +0x2A, 0x88, 0x2B, 0x88, 0x2B, 0x88, 0x2B, 0x88, 0x2B, 0x88, 0x7F, 0x06 +}; + +PROGMEM const unsigned char chr_f72_38[] = +{ +0x7F, 0x68, 0x89, 0x26, 0x91, 0x20, 0x95, 0x1C, 0x99, 0x19, 0x9B, 0x17, 0x9D, 0x15, 0x9F, 0x13, +0xA1, 0x11, 0x8D, 0x07, 0x8C, 0x11, 0x8B, 0x0B, 0x8B, 0x0F, 0x8A, 0x0F, 0x8A, 0x0E, 0x89, 0x11, +0x89, 0x0E, 0x88, 0x13, 0x88, 0x0D, 0x89, 0x13, 0x89, 0x0C, 0x88, 0x15, 0x88, 0x0C, 0x88, 0x15, +0x88, 0x0C, 0x88, 0x15, 0x88, 0x0C, 0x88, 0x15, 0x88, 0x0C, 0x88, 0x15, 0x88, 0x0C, 0x88, 0x15, +0x88, 0x0C, 0x88, 0x15, 0x88, 0x0D, 0x88, 0x13, 0x88, 0x0E, 0x88, 0x13, 0x88, 0x0E, 0x89, 0x11, +0x89, 0x0F, 0x89, 0x0F, 0x89, 0x11, 0x89, 0x0D, 0x89, 0x13, 0x8B, 0x07, 0x8C, 0x14, 0x9D, 0x17, +0x9B, 0x1A, 0x97, 0x1E, 0x93, 0x1E, 0x96, 0x1B, 0x9A, 0x18, 0x9D, 0x15, 0x9F, 0x13, 0x8C, 0x07, +0x8C, 0x11, 0x8A, 0x0C, 0x8B, 0x0F, 0x8A, 0x0F, 0x8A, 0x0D, 0x8A, 0x11, 0x89, 0x0D, 0x89, 0x13, +0x89, 0x0B, 0x89, 0x15, 0x88, 0x0B, 0x89, 0x15, 0x89, 0x0A, 0x88, 0x17, 0x88, 0x0A, 0x88, 0x17, +0x88, 0x09, 0x88, 0x19, 0x88, 0x08, 0x88, 0x19, 0x88, 0x08, 0x88, 0x19, 0x88, 0x08, 0x88, 0x19, +0x88, 0x08, 0x88, 0x19, 0x88, 0x08, 0x88, 0x19, 0x88, 0x08, 0x88, 0x19, 0x88, 0x08, 0x88, 0x19, +0x88, 0x08, 0x88, 0x19, 0x88, 0x08, 0x89, 0x17, 0x89, 0x09, 0x88, 0x17, 0x88, 0x0A, 0x89, 0x15, +0x89, 0x0A, 0x89, 0x15, 0x89, 0x0B, 0x89, 0x13, 0x89, 0x0C, 0x8A, 0x11, 0x8A, 0x0D, 0x8A, 0x0F, +0x8A, 0x0E, 0x8C, 0x0C, 0x8B, 0x0F, 0x8D, 0x07, 0x8D, 0x11, 0xA1, 0x13, 0x9F, 0x15, 0x9D, 0x17, +0x9B, 0x19, 0x99, 0x1C, 0x95, 0x20, 0x91, 0x26, 0x89, 0x4A +}; + +PROGMEM const unsigned char chr_f72_39[] = +{ +0x7F, 0x68, 0x88, 0x27, 0x90, 0x21, 0x94, 0x1E, 0x97, 0x1A, 0x9A, 0x18, 0x9C, 0x16, 0x9E, 0x14, +0xA0, 0x12, 0x8E, 0x07, 0x8B, 0x11, 0x8C, 0x0B, 0x8A, 0x0F, 0x8B, 0x0F, 0x88, 0x0F, 0x8A, 0x11, +0x88, 0x0D, 0x8A, 0x13, 0x88, 0x0C, 0x89, 0x14, 0x88, 0x0B, 0x89, 0x16, 0x87, 0x0B, 0x89, 0x17, +0x87, 0x0A, 0x88, 0x18, 0x87, 0x0A, 0x88, 0x18, 0x87, 0x09, 0x89, 0x19, 0x87, 0x08, 0x88, 0x1A, +0x87, 0x08, 0x88, 0x1A, 0x87, 0x08, 0x88, 0x1A, 0x87, 0x08, 0x88, 0x1A, 0x87, 0x08, 0x88, 0x1A, +0x87, 0x08, 0x88, 0x1A, 0x88, 0x07, 0x88, 0x1A, 0x88, 0x07, 0x88, 0x1A, 0x88, 0x07, 0x88, 0x1A, +0x88, 0x07, 0x89, 0x18, 0x89, 0x08, 0x88, 0x18, 0x89, 0x08, 0x88, 0x18, 0x89, 0x08, 0x89, 0x16, +0x8A, 0x08, 0x89, 0x16, 0x8A, 0x09, 0x89, 0x14, 0x8B, 0x09, 0x8A, 0x12, 0x8C, 0x0A, 0x8A, 0x10, +0x8D, 0x0A, 0x8C, 0x0C, 0x8F, 0x0B, 0x8E, 0x07, 0x91, 0x0C, 0x9D, 0x00, 0x88, 0x0D, 0x9B, 0x01, +0x88, 0x0E, 0x99, 0x02, 0x88, 0x0F, 0x97, 0x03, 0x88, 0x10, 0x95, 0x04, 0x88, 0x11, 0x92, 0x06, +0x87, 0x14, 0x8E, 0x08, 0x87, 0x17, 0x88, 0x0A, 0x88, 0x2B, 0x88, 0x2B, 0x88, 0x2B, 0x88, 0x2B, +0x87, 0x2B, 0x88, 0x0E, 0x84, 0x17, 0x88, 0x0A, 0x88, 0x17, 0x88, 0x0A, 0x89, 0x15, 0x88, 0x0B, +0x89, 0x15, 0x88, 0x0C, 0x88, 0x14, 0x89, 0x0C, 0x89, 0x13, 0x88, 0x0D, 0x89, 0x12, 0x89, 0x0E, +0x89, 0x10, 0x89, 0x0F, 0x8A, 0x0E, 0x8A, 0x0F, 0x8B, 0x0B, 0x8B, 0x11, 0x8C, 0x07, 0x8C, 0x13, +0x9F, 0x14, 0x9E, 0x16, 0x9C, 0x18, 0x9A, 0x1B, 0x97, 0x1D, 0x94, 0x21, 0x90, 0x26, 0x89, 0x4C +}; + +PROGMEM const unsigned char chr_f72_3A[] = +{ +0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x23, 0x88, 0x13, +0x88, 0x13, 0x88, 0x13, 0x88, 0x13, 0x88, 0x13, +0x88, 0x13, 0x88, 0x13, 0x88, 0x13, 0x88, 0x7F, +0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x33, 0x88, +0x13, 0x88, 0x13, 0x88, 0x13, 0x88, 0x13, 0x88, +0x13, 0x88, 0x13, 0x88, 0x13, 0x88, 0x13, 0x88, +0x44 +}; +PROGMEM const unsigned char * const chrtbl_f72[96] = // character pointer table +{ + chr_f72_20, chr_f72_20, chr_f72_20, chr_f72_20, chr_f72_20, chr_f72_20, chr_f72_20, chr_f72_20, + chr_f72_20, chr_f72_20, chr_f72_20, chr_f72_20, chr_f72_20, chr_f72_2D, chr_f72_2E, chr_f72_20, + chr_f72_30, chr_f72_31, chr_f72_32, chr_f72_33, chr_f72_34, chr_f72_35, chr_f72_36, chr_f72_37, + chr_f72_38, chr_f72_39, chr_f72_3A, chr_f72_20, chr_f72_20, chr_f72_20, chr_f72_20, chr_f72_20, + chr_f72_20, chr_f72_20, chr_f72_20, chr_f72_20, chr_f72_20, chr_f72_20, chr_f72_20, chr_f72_20, + chr_f72_20, chr_f72_20, chr_f72_20, chr_f72_20, chr_f72_20, chr_f72_20, chr_f72_20, chr_f72_20, + chr_f72_20, chr_f72_20, chr_f72_20, chr_f72_20, chr_f72_20, chr_f72_20, chr_f72_20, chr_f72_20, + chr_f72_20, chr_f72_20, chr_f72_20, chr_f72_20, chr_f72_20, chr_f72_20, chr_f72_20, chr_f72_20, + chr_f72_20, chr_f72_20, chr_f72_20, chr_f72_20, chr_f72_20, chr_f72_20, chr_f72_20, chr_f72_20, + chr_f72_20, chr_f72_20, chr_f72_20, chr_f72_20, chr_f72_20, chr_f72_20, chr_f72_20, chr_f72_20, + chr_f72_20, chr_f72_20, chr_f72_20, chr_f72_20, chr_f72_20, chr_f72_20, chr_f72_20, chr_f72_20, + chr_f72_20, chr_f72_20, chr_f72_20, chr_f72_20, chr_f72_20, chr_f72_20, chr_f72_20, chr_f72_20 +}; diff --git a/Fonts/Font72x53rle.h b/Fonts/Font72x53rle.h new file mode 100644 index 0000000..b7ce1c9 --- /dev/null +++ b/Fonts/Font72x53rle.h @@ -0,0 +1,10 @@ +#include + +#define nr_chrs_f72 96 +#define chr_hgt_f72 75 +#define baseline_f72 73 +#define data_size_f72 8 +#define firstchr_f72 32 + +extern const unsigned char widtbl_f72[96]; +extern const unsigned char* const chrtbl_f72[96]; diff --git a/README.md b/README.md index 9727a82..c11fd2d 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ # TFT_eSPI +Update 10th March 2018: Added support for 8 bit parallel interface TFTs when used with ESP32. Update 24th February 2018: Added new smooth (antialiased) fonts. See Smooth Font examples and Tools folder for the font generator. @@ -36,7 +37,7 @@ Configuration of the library font selections, pins used to interface with the TF I have made some changes that will be uploaded soon that improves sprite and image rendering performance by up to 3x faster on the ESP8266. These updates are currently being tested/debugged. -**2. Anti-aliased fonts** +**2. Anti-aliased fonts - done 24/2/18** I have been experimenting with anti-aliased font files in "vlw" format generated by the free [Processing IDE](https://processing.org/). This IDE can be used to generate font files from your computer's font set and include **any** Unicode characters. This means Greek, Japanese and any other UTF-16 glyphs can be used. @@ -51,3 +52,20 @@ Here is another screenshot, showing the anti-aliased Hiragana character Unicode ![Hiragana glyphs](https://i.imgur.com/jeXf2st.png) Currently these are generated from a sketch, but when I have the Alpha blending sorted for colour backgrounds the plan is to build the rendering code into the TFT_eSPI library. Watch this space " " for updates! + +**3. Add support 4 8 bit "UNO" style TFTs with ESP32 - done 10/3/18** + +The ESP32 board I have been using for testing has the following pinout: + +![Example](https://i.imgur.com/bvM6leE.jpg) + +UNO style boards with a Wemos R32(ESP32) label are also available at low cost with the same pin-out. + +Unfortunately the typical UNO/mcufriend TFT display board maps LCD_RD, LCD_CS and LCD_RST signals to the ESP32 pins 35, 34 and 36 which are input only. To solve this I linked in the 3 spare pins IO15, IO33 and IO32 by adding wires to the bottom of the board as follows: + +IO15 wired to IO35 +IO33 wired to IO34 +IO32 wired to IO36 + +![Example](https://i.imgur.com/pUZn6lF.jpg) + diff --git a/TFT_Drivers/HX8357D_Defines.h b/TFT_Drivers/HX8357D_Defines.h new file mode 100644 index 0000000..7dcbdad --- /dev/null +++ b/TFT_Drivers/HX8357D_Defines.h @@ -0,0 +1,86 @@ +// Change the width and height if required (defined in portrait mode) +// or use the constructor to over-ride defaults +#define TFT_WIDTH 320 +#define TFT_HEIGHT 480 + + +// Delay between some initialisation commands +#define TFT_INIT_DELAY 0x80 // Not used unless commandlist invoked + + +// Generic commands used by TFT_eSPar.cpp +#define TFT_NOP 0x00 +#define TFT_SWRST 0x01 + +#define TFT_SLPIN 0x10 +#define TFT_SLPOUT 0x11 + +#define TFT_INVOFF 0x20 +#define TFT_INVON 0x21 + +#define TFT_DISPOFF 0x28 +#define TFT_DISPON 0x29 + +#define TFT_CASET 0x2A +#define TFT_PASET 0x2B +#define TFT_RAMWR 0x2C + +#define TFT_RAMRD 0x2E + +#define TFT_MADCTL 0x36 + +#define TFT_MAD_MY 0x80 +#define TFT_MAD_MX 0x40 +#define TFT_MAD_MV 0x20 +#define TFT_MAD_ML 0x10 +#define TFT_MAD_RGB 0x00 +#define TFT_MAD_BGR 0x08 +#define TFT_MAD_MH 0x04 +#define TFT_MAD_SS 0x02 +#define TFT_MAD_GS 0x01 + +#define TFT_IDXRD 0x00 // ILI9341 only, indexed control register read + + +#define HX8357_NOP 0x00 +#define HX8357_SWRESET 0x01 +#define HX8357_RDDID 0x04 +#define HX8357_RDDST 0x09 + +#define HX8357_RDPOWMODE 0x0A +#define HX8357_RDMADCTL 0x0B +#define HX8357_RDCOLMOD 0x0C +#define HX8357_RDDIM 0x0D +#define HX8357_RDDSDR 0x0F + +#define HX8357_SLPIN 0x10 +#define HX8357_SLPOUT 0x11 + +#define HX8357_INVOFF 0x20 +#define HX8357_INVON 0x21 +#define HX8357_DISPOFF 0x28 +#define HX8357_DISPON 0x29 + +#define HX8357_CASET 0x2A +#define HX8357_PASET 0x2B +#define HX8357_RAMWR 0x2C +#define HX8357_RAMRD 0x2E + +#define HX8357_TEON 0x35 +#define HX8357_TEARLINE 0x44 +#define HX8357_MADCTL 0x36 +#define HX8357_COLMOD 0x3A + +#define HX8357_SETOSC 0xB0 +#define HX8357_SETPWR1 0xB1 +#define HX8357_SETRGB 0xB3 +#define HX8357D_SETCOM 0xB6 + +#define HX8357D_SETCYC 0xB4 +#define HX8357D_SETC 0xB9 + +#define HX8357D_SETSTBA 0xC0 + +#define HX8357_SETPANEL 0xCC + +#define HX8357D_SETGAMMA 0xE0 diff --git a/TFT_Drivers/HX8357D_Init.h b/TFT_Drivers/HX8357D_Init.h new file mode 100644 index 0000000..50389bb --- /dev/null +++ b/TFT_Drivers/HX8357D_Init.h @@ -0,0 +1,118 @@ + +// This is the command sequence that initialises the HX8357D driver +// +// This setup information uses simple 8 bit SPI writecommand() and writedata() functions +// +// See ST7735_Setup.h file for an alternative format + + +// Configure HX8357D display + + // setextc + writecommand(HX8357D_SETC); + writedata(0xFF); + writedata(0x83); + writedata(0x57); + delay(300); + + // setRGB which also enables SDO + writecommand(HX8357_SETRGB); + writedata(0x80); //enable SDO pin! +// writedata(0x00); //disable SDO pin! + writedata(0x0); + writedata(0x06); + writedata(0x06); + + writecommand(HX8357D_SETCOM); + writedata(0x25); // -1.52V + + writecommand(HX8357_SETOSC); + writedata(0x68); // Normal mode 70Hz, Idle mode 55 Hz + + writecommand(HX8357_SETPANEL); //Set Panel + writedata(0x05); // BGR, Gate direction swapped + + writecommand(HX8357_SETPWR1); + writedata(0x00); // Not deep standby + writedata(0x15); //BT + writedata(0x1C); //VSPR + writedata(0x1C); //VSNR + writedata(0x83); //AP + writedata(0xAA); //FS + + writecommand(HX8357D_SETSTBA); + writedata(0x50); //OPON normal + writedata(0x50); //OPON idle + writedata(0x01); //STBA + writedata(0x3C); //STBA + writedata(0x1E); //STBA + writedata(0x08); //GEN + + writecommand(HX8357D_SETCYC); + writedata(0x02); //NW 0x02 + writedata(0x40); //RTN + writedata(0x00); //DIV + writedata(0x2A); //DUM + writedata(0x2A); //DUM + writedata(0x0D); //GDON + writedata(0x78); //GDOFF + + writecommand(HX8357D_SETGAMMA); + writedata(0x02); + writedata(0x0A); + writedata(0x11); + writedata(0x1d); + writedata(0x23); + writedata(0x35); + writedata(0x41); + writedata(0x4b); + writedata(0x4b); + writedata(0x42); + writedata(0x3A); + writedata(0x27); + writedata(0x1B); + writedata(0x08); + writedata(0x09); + writedata(0x03); + writedata(0x02); + writedata(0x0A); + writedata(0x11); + writedata(0x1d); + writedata(0x23); + writedata(0x35); + writedata(0x41); + writedata(0x4b); + writedata(0x4b); + writedata(0x42); + writedata(0x3A); + writedata(0x27); + writedata(0x1B); + writedata(0x08); + writedata(0x09); + writedata(0x03); + writedata(0x00); + writedata(0x01); + + writecommand(HX8357_COLMOD); + writedata(0x55); // 16 bit + + writecommand(HX8357_MADCTL); + writedata(0xC0); + + writecommand(HX8357_TEON); // TE off + writedata(0x00); + + writecommand(HX8357_TEARLINE); // tear line + writedata(0x00); + writedata(0x02); + + writecommand(HX8357_SLPOUT); //Exit Sleep + delay(150); + + writecommand(HX8357_DISPON); // display on + delay(50); + +// End of HX8357D display configuration + + + diff --git a/TFT_Drivers/HX8357D_Rotation.h b/TFT_Drivers/HX8357D_Rotation.h new file mode 100644 index 0000000..9df230b --- /dev/null +++ b/TFT_Drivers/HX8357D_Rotation.h @@ -0,0 +1,26 @@ + // This is the command sequence that rotates the ILI9481 driver coordinate frame + + writecommand(TFT_MADCTL); + rotation = m % 4; + switch (rotation) { + case 0: // Portrait + writedata(TFT_MAD_MX | TFT_MAD_MY | TFT_MAD_RGB); + _width = TFT_WIDTH; + _height = TFT_HEIGHT; + break; + case 1: // Landscape (Portrait + 90) + writedata(TFT_MAD_MV | TFT_MAD_MY | TFT_MAD_RGB); + _width = TFT_HEIGHT; + _height = TFT_WIDTH; + break; + case 2: // Inverter portrait + writedata(TFT_MAD_RGB); + _width = TFT_WIDTH; + _height = TFT_HEIGHT; + break; + case 3: // Inverted landscape + writedata(TFT_MAD_MX | TFT_MAD_MV | TFT_MAD_RGB); + _width = TFT_HEIGHT; + _height = TFT_WIDTH; + break; + } diff --git a/TFT_Drivers/ILI9481_Defines.h b/TFT_Drivers/ILI9481_Defines.h new file mode 100644 index 0000000..bd5fb88 --- /dev/null +++ b/TFT_Drivers/ILI9481_Defines.h @@ -0,0 +1,42 @@ +// Change the width and height if required (defined in portrait mode) +// or use the constructor to over-ride defaults +#define TFT_WIDTH 320 +#define TFT_HEIGHT 480 + + +// Delay between some initialisation commands +#define TFT_INIT_DELAY 0x80 // Not used unless commandlist invoked + + +// Generic commands used by TFT_eSPI.cpp +#define TFT_NOP 0x00 +#define TFT_SWRST 0x01 + +#define TFT_SLPIN 0x10 +#define TFT_SLPOUT 0x11 + +#define TFT_INVOFF 0x20 +#define TFT_INVON 0x21 + +#define TFT_DISPOFF 0x28 +#define TFT_DISPON 0x29 + +#define TFT_CASET 0x2A +#define TFT_PASET 0x2B +#define TFT_RAMWR 0x2C + +#define TFT_RAMRD 0x2E + +#define TFT_MADCTL 0x36 + +#define TFT_MAD_MY 0x80 +#define TFT_MAD_MX 0x40 +#define TFT_MAD_MV 0x20 +#define TFT_MAD_ML 0x10 +#define TFT_MAD_RGB 0x00 +#define TFT_MAD_BGR 0x08 +#define TFT_MAD_MH 0x04 +#define TFT_MAD_SS 0x02 +#define TFT_MAD_GS 0x01 + +#define TFT_IDXRD 0x00 // ILI9341 only, indexed control register read diff --git a/TFT_Drivers/ILI9481_Init.h b/TFT_Drivers/ILI9481_Init.h new file mode 100644 index 0000000..038d1fd --- /dev/null +++ b/TFT_Drivers/ILI9481_Init.h @@ -0,0 +1,77 @@ + +// This is the command sequence that initialises the ILI9481 driver +// +// This setup information uses simple 8 bit SPI writecommand() and writedata() functions +// +// See ST7735_Setup.h file for an alternative format + + +// Configure ILI9481 display + + writecommand(TFT_SLPOUT); + delay(20); + + writecommand(0xD0); + writedata(0x07); + writedata(0x42); + writedata(0x18); + + writecommand(0xD1); + writedata(0x00); + writedata(0x07); + writedata(0x10); + + writecommand(0xD2); + writedata(0x01); + writedata(0x02); + + writecommand(0xC0); + writedata(0x10); + writedata(0x3B); + writedata(0x00); + writedata(0x02); + writedata(0x11); + + writecommand(0xC5); + writedata(0x03); + + writecommand(0xC8); + writedata(0x00); + writedata(0x32); + writedata(0x36); + writedata(0x45); + writedata(0x06); + writedata(0x16); + writedata(0x37); + writedata(0x75); + writedata(0x77); + writedata(0x54); + writedata(0x0C); + writedata(0x00); + + writecommand(TFT_MADCTL); + writedata(0x0A); + + writecommand(0x3A); + writedata(0x55); + + writecommand(TFT_CASET); + writedata(0x00); + writedata(0x00); + writedata(0x01); + writedata(0x3F); + + writecommand(TFT_PASET); + writedata(0x00); + writedata(0x00); + writedata(0x01); + writedata(0xDF); + + delay(120); + writecommand(TFT_DISPON); + + delay(25); +// End of ILI9481 display configuration + + + diff --git a/TFT_Drivers/ILI9481_Rotation.h b/TFT_Drivers/ILI9481_Rotation.h new file mode 100644 index 0000000..e80d08e --- /dev/null +++ b/TFT_Drivers/ILI9481_Rotation.h @@ -0,0 +1,27 @@ + // This is the command sequence that rotates the ILI9481 driver coordinate frame + + writecommand(TFT_MADCTL); + rotation = m % 4; + switch (rotation) { + case 0: // Portrait + writedata(TFT_MAD_BGR | TFT_MAD_SS); + _width = TFT_WIDTH; + _height = TFT_HEIGHT; + break; + case 1: // Landscape (Portrait + 90) + writedata(TFT_MAD_MV | TFT_MAD_BGR); + _width = TFT_HEIGHT; + _height = TFT_WIDTH; + break; + case 2: // Inverter portrait + writedata(TFT_MAD_BGR | TFT_MAD_GS); + _width = TFT_WIDTH; + _height = TFT_HEIGHT; + break; + case 3: // Inverted landscape + writedata(TFT_MAD_MV | TFT_MAD_BGR | TFT_MAD_SS | TFT_MAD_GS); + _width = TFT_HEIGHT; + _height = TFT_WIDTH; + break; + } + \ No newline at end of file diff --git a/TFT_Drivers/ILI9488_Defines.h b/TFT_Drivers/ILI9488_Defines.h new file mode 100644 index 0000000..bd5fb88 --- /dev/null +++ b/TFT_Drivers/ILI9488_Defines.h @@ -0,0 +1,42 @@ +// Change the width and height if required (defined in portrait mode) +// or use the constructor to over-ride defaults +#define TFT_WIDTH 320 +#define TFT_HEIGHT 480 + + +// Delay between some initialisation commands +#define TFT_INIT_DELAY 0x80 // Not used unless commandlist invoked + + +// Generic commands used by TFT_eSPI.cpp +#define TFT_NOP 0x00 +#define TFT_SWRST 0x01 + +#define TFT_SLPIN 0x10 +#define TFT_SLPOUT 0x11 + +#define TFT_INVOFF 0x20 +#define TFT_INVON 0x21 + +#define TFT_DISPOFF 0x28 +#define TFT_DISPON 0x29 + +#define TFT_CASET 0x2A +#define TFT_PASET 0x2B +#define TFT_RAMWR 0x2C + +#define TFT_RAMRD 0x2E + +#define TFT_MADCTL 0x36 + +#define TFT_MAD_MY 0x80 +#define TFT_MAD_MX 0x40 +#define TFT_MAD_MV 0x20 +#define TFT_MAD_ML 0x10 +#define TFT_MAD_RGB 0x00 +#define TFT_MAD_BGR 0x08 +#define TFT_MAD_MH 0x04 +#define TFT_MAD_SS 0x02 +#define TFT_MAD_GS 0x01 + +#define TFT_IDXRD 0x00 // ILI9341 only, indexed control register read diff --git a/TFT_Drivers/ILI9488_Init.h b/TFT_Drivers/ILI9488_Init.h new file mode 100644 index 0000000..feaffdd --- /dev/null +++ b/TFT_Drivers/ILI9488_Init.h @@ -0,0 +1,97 @@ + +// This is the command sequence that initialises the ILI9488 driver +// +// This setup information uses simple 8 bit SPI writecommand() and writedata() functions +// +// See ST7735_Setup.h file for an alternative format + + +// Configure ILI9488 display + + writecommand(0x3A); // Pixel Interface Format (16 bit colour) + writedata(0x55); + + writecommand(0xB0); // Interface Mode Control + writedata(0x00); + + writecommand(0xB1); // Frame Rate Control + writedata(0xB0); + writedata(0x11); + + writecommand(0xB4); // Display Inversion Control + writedata(0x02); + + writecommand(0xB6); // Display Function Control + writedata(0x02); + writedata(0x02); + writedata(0x3B); + + writecommand(0xB7); // Entry Mode Set + writedata(0xC6); + + writecommand(0XC0); // Power Control 1 + writedata(0x10); + writedata(0x10); + + writecommand(0xC1); // Power Control 2 + writedata(0x41); + + writecommand(0xC5); // VCOM Control + writedata(0x00); + writedata(0x22); + writedata(0x80); + writedata(0x40); + + writecommand(0xE0); // Positive Gamma Control + writedata(0x00); + writedata(0x03); + writedata(0x09); + writedata(0x08); + writedata(0x16); + writedata(0x0A); + writedata(0x3F); + writedata(0x78); + writedata(0x4C); + writedata(0x09); + writedata(0x0A); + writedata(0x08); + writedata(0x16); + writedata(0x1A); + writedata(0x0F); + + writecommand(0XE1); // Negative Gamma Control + writedata(0x00); + writedata(0x16); + writedata(0x19); + writedata(0x03); + writedata(0x0F); + writedata(0x05); + writedata(0x32); + writedata(0x45); + writedata(0x46); + writedata(0x04); + writedata(0x0E); + writedata(0x0D); + writedata(0x35); + writedata(0x37); + writedata(0x0F); + + writecommand(0xF7); // Adjust Control 3 + writedata(0xA9); + writedata(0x51); + writedata(0x2C); + writedata(0x02); + + writecommand(TFT_MADCTL); // Memory Access Control + writedata(0x48); // MX, BGR + + writecommand(TFT_SLPOUT); //Exit Sleep +delay(120); + + writecommand(TFT_DISPON); //Display on +delay(25); + +// End of ILI9488 display configuration + + + diff --git a/TFT_Drivers/ILI9488_Rotation.h b/TFT_Drivers/ILI9488_Rotation.h new file mode 100644 index 0000000..6ab17bd --- /dev/null +++ b/TFT_Drivers/ILI9488_Rotation.h @@ -0,0 +1,27 @@ + // This is the command sequence that rotates the ILI9488 driver coordinate frame + + writecommand(TFT_MADCTL); + rotation = m % 4; + switch (rotation) { + case 0: // Portrait + writedata(TFT_MAD_MX | TFT_MAD_BGR); + _width = TFT_WIDTH; + _height = TFT_HEIGHT; + break; + case 1: // Landscape (Portrait + 90) + writedata(TFT_MAD_MV | TFT_MAD_BGR); + _width = TFT_HEIGHT; + _height = TFT_WIDTH; + break; + case 2: // Inverter portrait + writedata(TFT_MAD_MY | TFT_MAD_BGR); + _width = TFT_WIDTH; + _height = TFT_HEIGHT; + break; + case 3: // Inverted landscape + writedata(TFT_MAD_MX | TFT_MAD_MY | TFT_MAD_MV | TFT_MAD_BGR); + _width = TFT_HEIGHT; + _height = TFT_WIDTH; + break; + } + \ No newline at end of file diff --git a/TFT_eSPI.cpp b/TFT_eSPI.cpp index 901f490..c5fa4ee 100644 --- a/TFT_eSPI.cpp +++ b/TFT_eSPI.cpp @@ -17,7 +17,9 @@ #include -#include +#ifndef ESP32_PARALLEL + #include +#endif // SUPPORT_TRANSACTIONS is mandatory for ESP32 so the hal mutex is toggled #if defined (ESP32) && !defined (SUPPORT_TRANSACTIONS) @@ -32,26 +34,32 @@ #define CMD_BITS 8-1 #endif -// Fast SPI block write prototype -void spiWriteBlock(uint16_t color, uint32_t repeat); +// Fast block write prototype +void writeBlock(uint16_t color, uint32_t repeat); + +// Byte read prototype +uint8_t readByte(void); + +// GPIO parallel input/output control +void busDir(uint32_t mask, uint8_t mode); // If the SPI library has transaction support, these functions // establish settings and protect from interference from other // libraries. Otherwise, they simply do nothing. inline void TFT_eSPI::spi_begin(void){ -#if defined (SPI_HAS_TRANSACTION) && defined (SUPPORT_TRANSACTIONS) +#if defined (SPI_HAS_TRANSACTION) && defined (SUPPORT_TRANSACTIONS) && !defined(ESP32_PARALLEL) if (locked) {locked = false; SPI.beginTransaction(SPISettings(SPI_FREQUENCY, MSBFIRST, SPI_MODE0));} #endif } inline void TFT_eSPI::spi_end(void){ -#if defined (SPI_HAS_TRANSACTION) && defined (SUPPORT_TRANSACTIONS) +#if defined (SPI_HAS_TRANSACTION) && defined (SUPPORT_TRANSACTIONS) && !defined(ESP32_PARALLEL) if(!inTransaction) {if (!locked) {locked = true; SPI.endTransaction();}} #endif } -#if defined (TOUCH_CS) && defined (SPI_TOUCH_FREQUENCY) +#if defined (TOUCH_CS) && defined (SPI_TOUCH_FREQUENCY) // && !defined(ESP32_PARALLEL) inline void TFT_eSPI::spi_begin_touch(void){ #if defined (SPI_HAS_TRANSACTION) && defined (SUPPORT_TRANSACTIONS) @@ -109,6 +117,39 @@ TFT_eSPI::TFT_eSPI(int16_t w, int16_t h) } #endif +#ifdef ESP32_PARALLEL + // Create a data bus and Write line GPIO bit clear mask + //clr_mask = (1 << TFT_D0) | (1 << TFT_D1) | (1 << TFT_D2) | (1 << TFT_D3) | (1 << TFT_D4) | (1 << TFT_D5) | (1 << TFT_D6) | (1 << TFT_D7) | (1 << TFT_WR); + + // Create a data bus GPIO bit direction mask + //dir_mask = (1 << TFT_D0) | (1 << TFT_D1) | (1 << TFT_D2) | (1 << TFT_D3) | (1 << TFT_D4) | (1 << TFT_D5) | (1 << TFT_D6) | (1 << TFT_D7); + + + // Create a bit set lookup table for data bus - wastes 1kbyte of RAM but speeds things up dramatically + for (int c = 0; c<256; c++) + { + xset_mask[c] = 0; + if ( c & 0x01 ) xset_mask[c] |= (1 << TFT_D0); + if ( c & 0x02 ) xset_mask[c] |= (1 << TFT_D1); + if ( c & 0x04 ) xset_mask[c] |= (1 << TFT_D2); + if ( c & 0x08 ) xset_mask[c] |= (1 << TFT_D3); + if ( c & 0x10 ) xset_mask[c] |= (1 << TFT_D4); + if ( c & 0x20 ) xset_mask[c] |= (1 << TFT_D5); + if ( c & 0x40 ) xset_mask[c] |= (1 << TFT_D6); + if ( c & 0x80 ) xset_mask[c] |= (1 << TFT_D7); + } + + // Make sure read is high before we set the bus to output + digitalWrite(TFT_RD, HIGH); + pinMode(TFT_RD, OUTPUT); + + GPIO.out_w1ts = set_mask(255); // Set data bus to 0xFF + + // Set TFT data bus lines to output + busDir(dir_mask, OUTPUT); + +#endif + _init_width = _width = w; // Set by specific xxxxx_Defines.h file or by users sketch _init_height = _height = h; // Set by specific xxxxx_Defines.h file or by users sketch rotation = 0; @@ -196,18 +237,19 @@ void TFT_eSPI::init(void) SPI.begin(); // This will set HMISO to input #else - #if defined (TFT_MOSI) && !defined (TFT_SPI_OVERLAP) - SPI.begin(TFT_SCLK, TFT_MISO, TFT_MOSI, -1); - #else - SPI.begin(); + #if !defined(ESP32_PARALLEL) + #if defined (TFT_MOSI) && !defined (TFT_SPI_OVERLAP) + SPI.begin(TFT_SCLK, TFT_MISO, TFT_MOSI, -1); + #else + SPI.begin(); + #endif #endif - #endif inTransaction = false; locked = true; - // SUPPORT_TRANSACTIONS is manadatory for ESP32 so the hal mutex is toggled + // SUPPORT_TRANSACTIONS is mandatory for ESP32 so the hal mutex is toggled // so the code here is for ESP8266 only #if !defined (SUPPORT_TRANSACTIONS) && defined (ESP8266) SPI.setBitOrder(MSBFIRST); @@ -215,14 +257,19 @@ void TFT_eSPI::init(void) SPI.setFrequency(SPI_FREQUENCY); #endif - // Set to output once again in case D6 (MISO) is used for CS -#ifdef TFT_CS - digitalWrite(TFT_CS, HIGH); // Chip select high (inactive) - pinMode(TFT_CS, OUTPUT); +#if defined(ESP32_PARALLEL) + digitalWrite(TFT_CS, LOW); // Chip select low permanently + pinMode(TFT_CS, OUTPUT); #else - SPI.setHwCs(1); // Use hardware SS toggling + #ifdef TFT_CS + // Set to output once again in case D6 (MISO) is used for CS + digitalWrite(TFT_CS, HIGH); // Chip select high (inactive) + pinMode(TFT_CS, OUTPUT); + #else + SPI.setHwCs(1); // Use hardware SS toggling + #endif #endif - + // Set to output once again in case D6 (MISO) is used for DC #ifdef TFT_DC digitalWrite(TFT_DC, HIGH); // Data/Command high = data mode @@ -265,6 +312,15 @@ void TFT_eSPI::init(void) #elif defined (RPI_ILI9486_DRIVER) #include "TFT_Drivers/RPI_ILI9486_Init.h" +#elif defined (ILI9481_DRIVER) + #include "TFT_Drivers/ILI9481_Init.h" + +#elif defined (ILI9488_DRIVER) + #include "TFT_Drivers/ILI9488_Init.h" + +#elif defined (HX8357D_DRIVER) + #include "TFT_Drivers/HX8357D_Init.h" + #endif spi_end(); @@ -297,6 +353,15 @@ void TFT_eSPI::setRotation(uint8_t m) #elif defined (RPI_ILI9486_DRIVER) #include "TFT_Drivers/RPI_ILI9486_Rotation.h" +#elif defined (ILI9481_DRIVER) + #include "TFT_Drivers/ILI9481_Rotation.h" + +#elif defined (ILI9488_DRIVER) + #include "TFT_Drivers/ILI9488_Rotation.h" + +#elif defined (HX8357D_DRIVER) + #include "TFT_Drivers/HX8357D_Rotation.h" + #endif delayMicroseconds(10); @@ -350,7 +415,7 @@ void TFT_eSPI::commandList (const uint8_t *addr) ***************************************************************************************/ void TFT_eSPI::spiwrite(uint8_t c) { - SPI.transfer(c); + transfer8(c); } @@ -362,10 +427,9 @@ void TFT_eSPI::writecommand(uint8_t c) { DC_C; CS_L; - #ifdef SEND_16_BITS - SPI.transfer(0); - #endif - SPI.transfer(c); + + transfer8(c); + CS_H; DC_D; } @@ -375,52 +439,71 @@ void TFT_eSPI::writecommand(uint8_t c) ** Function name: writedata ** Description: Send a 8 bit data value to the TFT ***************************************************************************************/ -void TFT_eSPI::writedata(uint8_t c) +void TFT_eSPI::writedata(uint8_t d) { CS_L; - #ifdef SEND_16_BITS - SPI.transfer(0); - #endif - SPI.transfer(c); + + transfer8(d); + CS_H; } /*************************************************************************************** -** Function name: readcommand8 (for ILI9341 Interface II i.e. IM [3:0] = "1101") +** Function name: readcommand8 ** Description: Read a 8 bit data value from an indexed command register ***************************************************************************************/ uint8_t TFT_eSPI::readcommand8(uint8_t cmd_function, uint8_t index) { + uint8_t reg = 0; +#ifdef ESP32_PARALLEL + + writecommand(cmd_function); // Sets DC and CS high + + busDir(dir_mask, INPUT); + + CS_L; + + // Read nth parameter (assumes caller discards 1st parameter or points index to 2nd) + while(index--) reg = readByte(); + + busDir(dir_mask, OUTPUT); + + CS_H; + +#else + // for ILI9341 Interface II i.e. IM [3:0] = "1101" spi_begin(); index = 0x10 + (index & 0x0F); DC_C; CS_L; - SPI.transfer(0xD9); + transfer8(0xD9); DC_D; - SPI.transfer(index); + transfer8(index); CS_H; DC_C; CS_L; - SPI.transfer(cmd_function); + transfer8(cmd_function); DC_D; - uint8_t reg = SPI.transfer(0); + reg = transfer8(0); CS_H; spi_end(); +#endif return reg; } /*************************************************************************************** -** Function name: readcommand16 (for ILI9341 Interface II i.e. IM [3:0] = "1101") +** Function name: readcommand16 ** Description: Read a 16 bit data value from an indexed command register ***************************************************************************************/ uint16_t TFT_eSPI::readcommand16(uint8_t cmd_function, uint8_t index) { - uint32_t reg = 0; + uint32_t reg; + reg |= (readcommand8(cmd_function, index + 0) << 8); reg |= (readcommand8(cmd_function, index + 1) << 0); @@ -429,7 +512,7 @@ uint16_t TFT_eSPI::readcommand16(uint8_t cmd_function, uint8_t index) /*************************************************************************************** -** Function name: readcommand32 (for ILI9341 Interface II i.e. IM [3:0] = "1101") +** Function name: readcommand32 ** Description: Read a 32 bit data value from an indexed command register ***************************************************************************************/ uint32_t TFT_eSPI::readcommand32(uint8_t cmd_function, uint8_t index) @@ -451,26 +534,124 @@ uint32_t TFT_eSPI::readcommand32(uint8_t cmd_function, uint8_t index) ***************************************************************************************/ uint16_t TFT_eSPI::readPixel(int32_t x0, int32_t y0) { +#if defined(ESP32_PARALLEL) + + readAddrWindow(x0, y0, x0, y0); // Sets CS low + + // Set masked pins D0- D7 to input + busDir(dir_mask, INPUT); + + // Dummy read to throw away don't care value + readByte(); + + // Fetch the 16 bit BRG pixel + //uint16_t rgb = (readByte() << 8) | readByte(); + +#if defined (ILI9341_DRIVER) | defined (ILI9488_DRIVER) // Read 3 bytes + + // Read window pixel 24 bit RGB values and fill in LS bits + uint16_t rgb = ((readByte() & 0xF8) << 8) | ((readByte() & 0xFC) << 3) | (readByte() >> 3); + + CS_H; + + // Set masked pins D0- D7 to output + busDir(dir_mask, OUTPUT); + + return rgb; + +#else // ILI9481 16 bit read + + // Fetch the 16 bit BRG pixel + uint16_t bgr = (readByte() << 8) | readByte(); + + CS_H; + + // Set masked pins D0- D7 to output + busDir(dir_mask, OUTPUT); + + // Swap Red and Blue (could check MADCTL setting to see if this is needed) + return (bgr>>11) | (bgr<<11) | (bgr & 0x7E0); +#endif + +#else // Not ESP32_PARALLEL + spi_begin(); readAddrWindow(x0, y0, x0, y0); // Sets CS low // Dummy read to throw away don't care value - SPI.transfer(0); + transfer8(0); // Read window pixel 24 bit RGB values - uint8_t r = SPI.transfer(0); - uint8_t g = SPI.transfer(0); - uint8_t b = SPI.transfer(0); + uint8_t r = transfer8(0); + uint8_t g = transfer8(0); + uint8_t b = transfer8(0); CS_H; spi_end(); return color565(r, g, b); + +#endif } +/*************************************************************************************** +** Function name: read byte - supports class functions +** Description: Read a byte from ESP32 8 bit data port +***************************************************************************************/ +// Bus MUST be set to input before calling this function! +uint8_t readByte(void) +{ + uint8_t b = 0; + +#ifdef ESP32_PARALLEL + RD_L; + uint32_t reg; // Read all GPIO pins 0-31 + reg = gpio_input_get(); // Read three times to allow for bus access time + reg = gpio_input_get(); + reg = gpio_input_get(); // Data should be stable now + RD_H; + + // Check GPIO bits used and build value + b = (((reg>>TFT_D0)&1) << 0); + b |= (((reg>>TFT_D1)&1) << 1); + b |= (((reg>>TFT_D2)&1) << 2); + b |= (((reg>>TFT_D3)&1) << 3); + b |= (((reg>>TFT_D4)&1) << 4); + b |= (((reg>>TFT_D5)&1) << 5); + b |= (((reg>>TFT_D6)&1) << 6); + b |= (((reg>>TFT_D7)&1) << 7); +#endif + + return b; +} + +/*************************************************************************************** +** Function name: masked GPIO direction control - supports class functions +** Description: Set masked ESP32 GPIO pins to input or output +***************************************************************************************/ +void busDir(uint32_t mask, uint8_t mode) +{ +#ifdef ESP32_PARALLEL + + // Supports GPIO 0 - 31 on ESP32 only + gpio_config_t gpio; + + gpio.pin_bit_mask = mask; + gpio.mode = GPIO_MODE_INPUT; + gpio.pull_up_en = GPIO_PULLUP_ENABLE; + gpio.pull_down_en = GPIO_PULLDOWN_DISABLE; + gpio.intr_type = GPIO_INTR_DISABLE; + + if (mode == OUTPUT) gpio.mode = GPIO_MODE_OUTPUT; + + gpio_config(&gpio); + +#endif +} + /*************************************************************************************** ** Function name: read rectangle (for SPI Interface II i.e. IM [3:0] = "1101") ** Description: Read 565 pixel colours from a defined area @@ -478,34 +659,73 @@ uint16_t TFT_eSPI::readPixel(int32_t x0, int32_t y0) void TFT_eSPI::readRect(uint32_t x, uint32_t y, uint32_t w, uint32_t h, uint16_t *data) { if ((x > _width) || (y > _height) || (w == 0) || (h == 0)) return; - + +#if defined(ESP32_PARALLEL) + + readAddrWindow(x, y, x + w - 1, y + h - 1); // Sets CS low + + // Set masked pins D0- D7 to input + busDir(dir_mask, INPUT); + + // Dummy read to throw away don't care value + readByte(); + + // Total pixel count + uint32_t len = w * h; + +#if defined (ILI9341_DRIVER) | defined (ILI9488_DRIVER) // Read 3 bytes + // Fetch the 24 bit RGB value + while (len--) { + // Assemble the RGB 16 bit colour + uint16_t rgb = ((readByte() & 0xF8) << 8) | ((readByte() & 0xFC) << 3) | (readByte() >> 3); + + // Swapped byte order for compatibility with pushRect() + *data++ = (rgb<<8) | (rgb>>8); + } +#else // ILI9481 reads as 16 bits + // Fetch the 16 bit BRG pixels + while (len--) { + // Read the BRG 16 bit colour + uint16_t bgr = (readByte() << 8) | readByte(); + + // Swap Red and Blue (could check MADCTL setting to see if this is needed) + uint16_t rgb = (bgr>>11) | (bgr<<11) | (bgr & 0x7E0); + + // Swapped byte order for compatibility with pushRect() + *data++ = (rgb<<8) | (rgb>>8); + } +#endif + CS_H; + + // Set masked pins D0- D7 to output + busDir(dir_mask, OUTPUT); + +#else // Not ESP32_PARALLEL + spi_begin(); readAddrWindow(x, y, x + w - 1, y + h - 1); // Sets CS low // Dummy read to throw away don't care value - SPI.transfer(0); + transfer8(0); // Read window pixel 24 bit RGB values uint32_t len = w * h; while (len--) { // Read the 3 RGB bytes, colour is actually only in the top 6 bits of each byte // as the TFT stores colours as 18 bits - uint8_t r = SPI.transfer(0); - uint8_t g = SPI.transfer(0); - uint8_t b = SPI.transfer(0); + uint8_t r = transfer8(0); + uint8_t g = transfer8(0); + uint8_t b = transfer8(0); + // Swapped colour byte order for compatibility with pushRect() *data++ = (r & 0xF8) | (g & 0xE0) >> 5 | (b & 0xF8) << 5 | (g & 0x1C) << 11; } - // Write NOP command to stop read mode - //DC_C; - //SPI.transfer(TFT_NOP); - //DC_D; - CS_H; spi_end(); +#endif } @@ -971,25 +1191,27 @@ bool TFT_eSPI::getSwapBytes(void) // If w and h are 1, then 1 pixel is read, *data array size must be 3 bytes per pixel void TFT_eSPI::readRectRGB(int32_t x0, int32_t y0, int32_t w, int32_t h, uint8_t *data) { +#if !defined(ESP32_PARALLEL) spi_begin(); readAddrWindow(x0, y0, x0 + w - 1, y0 + h - 1); // Sets CS low // Dummy read to throw away don't care value - SPI.transfer(0); + transfer8(0); // Read window pixel 24 bit RGB values, buffer must be set in sketch to 3 * w * h uint32_t len = w * h; while (len--) { // Read the 3 RGB bytes, colour is actually only in the top 6 bits of each byte // as the TFT stores colours as 18 bits - *data++ = SPI.transfer(0); - *data++ = SPI.transfer(0); - *data++ = SPI.transfer(0); + *data++ = transfer8(0); + *data++ = transfer8(0); + *data++ = transfer8(0); } CS_H; spi_end(); +#endif } @@ -997,41 +1219,6 @@ void TFT_eSPI::readRectRGB(int32_t x0, int32_t y0, int32_t w, int32_t h, uint8_ ** Function name: drawCircle ** Description: Draw a circle outline ***************************************************************************************/ -/* -// Midpoint circle algorithm, we can optimise this since y = 0 on first pass -// and we can eliminate the multiply as well -void TFT_eSPI::drawCircle(int32_t x0, int32_t y0, int32_t radius, uint32_t color) -{ - int32_t x = radius; - int32_t y = 0; - int32_t err = 0; - - while (x >= y) - { - drawPixel(x0 + x, y0 + y, color); - drawPixel(x0 + x, y0 - y, color); - drawPixel(x0 - x, y0 - y, color); - drawPixel(x0 - x, y0 + y, color); - - drawPixel(x0 + y, y0 + x, color); - drawPixel(x0 + y, y0 - x, color); - drawPixel(x0 - y, y0 - x, color); - drawPixel(x0 - y, y0 + x, color); - - if (err <= 0) - { - y += 1; - err += 2*y + 1; - } - if (err > 0) - { - x -= 1; - err -= 2*x + 1; - } - } -} -*/ - // Optimised midpoint circle algorithm void TFT_eSPI::drawCircle(int32_t x0, int32_t y0, int32_t r, uint32_t color) { @@ -1127,7 +1314,6 @@ void TFT_eSPI::drawCircleHelper( int32_t x0, int32_t y0, int32_t r, uint8_t corn ***************************************************************************************/ // Optimised midpoint circle algorithm, changed to horizontal lines (faster in sprites) void TFT_eSPI::fillCircle(int32_t x0, int32_t y0, int32_t r, uint32_t color) - { int32_t x = 0; int32_t dx = 1; @@ -1848,20 +2034,16 @@ void TFT_eSPI::drawChar(int32_t x, int32_t y, unsigned char c, uint32_t color, u while(SPI1CMD & SPIBUSY) {} } #else // for ESP32 + for (int8_t j = 0; j < 8; j++) { for (int8_t k = 0; k < 5; k++ ) { - if (column[k] & mask) { - SPI.write16(color); - } - else { - SPI.write16(bg); - } + if (column[k] & mask) {transfer16(color);} + else {transfer16(bg);} } - mask <<= 1; - - SPI.write16(bg); + transfer16(bg); } + #endif CS_H; //inTransaction = false; @@ -2051,7 +2233,6 @@ void TFT_eSPI::drawChar(int32_t x, int32_t y, unsigned char c, uint32_t color, u ** Description: define an area to receive a stream of pixels ***************************************************************************************/ // Chip select is high at the end of this function - void TFT_eSPI::setWindow(int16_t x0, int16_t y0, int16_t x1, int16_t y1) { spi_begin(); @@ -2291,49 +2472,34 @@ inline void TFT_eSPI::setAddrWindow(int32_t x0, int32_t y0, int32_t x1, int32_t DC_C; CS_L; -#if defined (RPI_ILI9486_DRIVER) - SPI.write16(TFT_CASET); -#else - SPI.write(TFT_CASET); -#endif + transfer8(TFT_CASET); DC_D; - #if defined (RPI_ILI9486_DRIVER) uint8_t xBin[] = { 0, (uint8_t) (x0>>8), 0, (uint8_t) (x0>>0), 0, (uint8_t) (x1>>8), 0, (uint8_t) (x1>>0), }; SPI.writePattern(&xBin[0], 8, 1); #else - SPI.write32(xaw); + transfer32(xaw); #endif // Row addr set DC_C; - -#if defined (RPI_ILI9486_DRIVER) - SPI.write16(TFT_PASET); -#else - SPI.write(TFT_PASET); -#endif + transfer8(TFT_PASET); DC_D; - #if defined (RPI_ILI9486_DRIVER) uint8_t yBin[] = { 0, (uint8_t) (y0>>8), 0, (uint8_t) (y0>>0), 0, (uint8_t) (y1>>8), 0, (uint8_t) (y1>>0), }; SPI.writePattern(&yBin[0], 8, 1); #else - SPI.write32(yaw); + transfer32(yaw); #endif // write to RAM DC_C; -#if defined (RPI_ILI9486_DRIVER) - SPI.write16(TFT_RAMWR); -#else - SPI.write(TFT_RAMWR); -#endif + transfer8(TFT_RAMWR); DC_D; @@ -2436,23 +2602,25 @@ void TFT_eSPI::readAddrWindow(int32_t x0, int32_t y0, int32_t x1, int32_t y1) DC_C; CS_L; - SPI.write(TFT_CASET); + transfer8(TFT_CASET); + DC_D; - SPI.write32(xaw); + transfer32(xaw); // Row addr set DC_C; - SPI.write(TFT_PASET); + transfer8(TFT_PASET); DC_D; - SPI.write32(yaw); + transfer32(yaw); DC_C; - SPI.transfer(TFT_RAMRD); // Read CGRAM command + transfer8(TFT_RAMRD); // Read CGRAM command + DC_D; //spi_end(); @@ -2669,11 +2837,7 @@ void TFT_eSPI::drawPixel(uint32_t x, uint32_t y, uint32_t color) DC_C; -#if defined (RPI_ILI9486_DRIVER) - SPI.write16(TFT_CASET); -#else - SPI.write(TFT_CASET); -#endif + transfer8(TFT_CASET); DC_D; @@ -2681,7 +2845,7 @@ void TFT_eSPI::drawPixel(uint32_t x, uint32_t y, uint32_t color) uint8_t xBin[] = { 0, (uint8_t) (x>>8), 0, (uint8_t) (x>>0), 0, (uint8_t) (x>>8), 0, (uint8_t) (x>>0), }; SPI.writePattern(&xBin[0], 8, 1); #else - SPI.write32(xaw); + transfer32(xaw); #endif addr_col = x; @@ -2692,11 +2856,7 @@ void TFT_eSPI::drawPixel(uint32_t x, uint32_t y, uint32_t color) DC_C; -#if defined (RPI_ILI9486_DRIVER) - SPI.write16(TFT_PASET); -#else - SPI.write(TFT_PASET); -#endif + transfer8(TFT_PASET); DC_D; @@ -2704,7 +2864,7 @@ void TFT_eSPI::drawPixel(uint32_t x, uint32_t y, uint32_t color) uint8_t yBin[] = { 0, (uint8_t) (y>>8), 0, (uint8_t) (y>>0), 0, (uint8_t) (y>>8), 0, (uint8_t) (y>>0), }; SPI.writePattern(&yBin[0], 8, 1); #else - SPI.write32(yaw); + transfer32(yaw); #endif addr_row = y; @@ -2712,15 +2872,11 @@ void TFT_eSPI::drawPixel(uint32_t x, uint32_t y, uint32_t color) DC_C; -#if defined (RPI_ILI9486_DRIVER) - SPI.write16(TFT_RAMWR); -#else - SPI.write(TFT_RAMWR); -#endif + transfer8(TFT_RAMWR); DC_D; - SPI.write16(color); + transfer16(color); CS_H; @@ -2739,11 +2895,9 @@ void TFT_eSPI::pushColor(uint16_t color) spi_begin(); CS_L; -#if defined (ESP8266) - SPI.write16(color, true); -#else - SPI.write16(color); -#endif + + transfer16(color); + CS_H; spi_end(); @@ -2765,7 +2919,11 @@ void TFT_eSPI::pushColor(uint16_t color, uint16_t len) if(len) SPI.writePattern(&colorBin[0], 2, 1); len--; while(len--) {WR_L; WR_H;} #else - spiWriteBlock(color, len); + #ifdef ESP32_PARALLEL + while (len--) {transfer16(color);} + #else + writeBlock(color, len); + #endif #endif CS_H; @@ -2789,11 +2947,15 @@ void TFT_eSPI::pushColors(uint8_t *data, uint32_t len) while ( len >=64 ) {SPI.writePattern(data, 64, 1); data += 64; len -= 64; } if (len) SPI.writePattern(data, len, 1); #else - #if (SPI_FREQUENCY == 80000000) - while ( len >=64 ) {SPI.writePattern(data, 64, 1); data += 64; len -= 64; } - if (len) SPI.writePattern(data, len, 1); + #ifdef ESP32_PARALLEL + while (len--) {transfer8(*data); data++;} #else - SPI.writeBytes(data, len); + #if (SPI_FREQUENCY == 80000000) + while ( len >=64 ) {SPI.writePattern(data, 64, 1); data += 64; len -= 64; } + if (len) SPI.writePattern(data, len, 1); + #else + SPI.writeBytes(data, len); + #endif #endif #endif @@ -2814,10 +2976,13 @@ void TFT_eSPI::pushColors(uint16_t *data, uint32_t len, bool swap) CS_L; #if defined (ESP32) - -if (swap) SPI.writePixels(data,len<<1); -else SPI.writeBytes((uint8_t*)data,len<<1); - + #ifdef ESP32_PARALLEL + if (swap) while ( len-- ) {transfer16(*data); data++;} + else while ( len-- ) {transwap16(*data); data++;} + #else + if (swap) SPI.writePixels(data,len<<1); + else SPI.writeBytes((uint8_t*)data,len<<1); + #endif #else uint32_t color[8]; @@ -2889,41 +3054,6 @@ else SPI.writeBytes((uint8_t*)data,len<<1); SPI1CMD |= SPIBUSY; } -/* // Smaller version but slower - uint32_t count = 0; - while(len) - { - if(len>15) {count = 16; len -= 16;} - else {count = len; len = 0;} - uint32_t bits = (count*16-1); // bits left to shift - 1 - if (swap) - { - uint16_t* ptr = (uint16_t*)color; - while(count--) - { - *ptr++ = (*(data) >> 8) | (uint16_t)(*(data) << 8); - data++; - } - } - else - { - memcpy(color,data,count<<1); - data += 16; - } - while(SPI1CMD & SPIBUSY) {} - SPI1U1 = (SPI1U1 & mask) | (bits << SPILMOSI) | (bits << SPILMISO); - SPI1W0 = color[0]; - SPI1W1 = color[1]; - SPI1W2 = color[2]; - SPI1W3 = color[3]; - SPI1W4 = color[4]; - SPI1W5 = color[5]; - SPI1W6 = color[6]; - SPI1W7 = color[7]; - SPI1CMD |= SPIBUSY; - } -*/ - while(SPI1CMD & SPIBUSY) {} #endif @@ -3123,7 +3253,7 @@ void TFT_eSPI::drawFastVLine(int32_t x, int32_t y, int32_t h, uint32_t color) setAddrWindow(x, y, x, y + h - 1); - spiWriteBlock(color, h); + writeBlock(color, h); CS_H; @@ -3144,17 +3274,21 @@ void TFT_eSPI::drawFastVLine(int32_t x, int32_t y, int32_t h, uint32_t color) #ifdef RPI_WRITE_STROBE #if defined (ESP8266) + // SPI1U1 will already be set to transfer 16 bits by setAddrWindow() SPI1W0 = (color >> 8) | (color << 8); SPI1CMD |= SPIBUSY; while(SPI1CMD & SPIBUSY) {} #else - SPI.write16(color); + transfer16(color); #endif - h--; - while(h--) {WR_L; WR_H;} + h--; + while(h--) {WR_L; WR_H;} #else - //while(h--) SPI.write16(color); - spiWriteBlock(color, h); + #ifdef ESP32_PARALLEL + while (h--) {transfer16(color);} + #else + writeBlock(color, h); + #endif #endif CS_H; @@ -3178,7 +3312,7 @@ void TFT_eSPI::drawFastHLine(int32_t x, int32_t y, int32_t w, uint32_t color) setAddrWindow(x, y, x + w - 1, y); - spiWriteBlock(color, w); + writeBlock(color, w); CS_H; @@ -3198,17 +3332,21 @@ void TFT_eSPI::drawFastHLine(int32_t x, int32_t y, int32_t w, uint32_t color) #ifdef RPI_WRITE_STROBE #if defined (ESP8266) + // SPI1U1 will already be set to transfer 16 bits by setAddrWindow() SPI1W0 = (color >> 8) | (color << 8); SPI1CMD |= SPIBUSY; while(SPI1CMD & SPIBUSY) {} #else - SPI.write16(color); + transfer16(color); #endif - w--; - while(w--) {WR_L; WR_H;} + w--; + while(w--) {WR_L; WR_H;} #else - //while(w--) SPI.write16(color); - spiWriteBlock(color, w); + #ifdef ESP32_PARALLEL + while (w--) {transfer16(color);} + #else + writeBlock(color, w); + #endif #endif CS_H; @@ -3232,7 +3370,7 @@ void TFT_eSPI::fillRect(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t col spi_begin(); setAddrWindow(x, y, x + w - 1, y + h - 1); - spiWriteBlock(color, w * h); + writeBlock(color, w * h); CS_H; @@ -3254,11 +3392,23 @@ void TFT_eSPI::fillRect(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t col uint32_t n = (uint32_t)w * (uint32_t)h; #ifdef RPI_WRITE_STROBE - if(n) {SPI.write16(color); n--;} + transfer16(color); while(n--) {WR_L; WR_H;} #else - //while(n--) SPI.write16(color); - spiWriteBlock(color, n); + #ifdef ESP32_PARALLEL + if (color>>8 == (uint8_t)color) + { + transfer8(color); + n--; WR_L; WR_H; + while (n) {WR_L; WR_H; n--; WR_L; WR_H;} + } + else + { + while (n--) {transfer16(color);} + } + #else + writeBlock(color, n); + #endif #endif CS_H; @@ -3612,12 +3762,8 @@ int16_t TFT_eSPI::drawChar(unsigned int uniCode, int x, int y, int font) pX = x + k * 8; mask = 0x80; while (mask) { - if (line & mask) { - SPI.write16(textcolor); - } - else { - SPI.write16(textbgcolor); - } + if (line & mask) {transfer16(textcolor);} + else {transfer16(textbgcolor);} mask = mask >> 1; } } @@ -3671,13 +3817,9 @@ int16_t TFT_eSPI::drawChar(unsigned int uniCode, int x, int y, int font) if (ts) { tnp = np; - while (tnp--) { - SPI.write16(textcolor); - } - } - else { - SPI.write16(textcolor); + while (tnp--) {transfer16(textcolor);} } + else {transfer16(textcolor);} px += textsize; if (px >= (x + width * textsize)) @@ -3716,7 +3858,11 @@ int16_t TFT_eSPI::drawChar(unsigned int uniCode, int x, int y, int font) SPI.writePattern(&textcolorBin[0], 2, 1); line--; while(line--) {WR_L; WR_H;} #else - spiWriteBlock(textcolor,line); + #ifdef ESP32_PARALLEL + while (line--) {transfer16(textcolor);} + #else + writeBlock(textcolor,line); + #endif #endif } else { @@ -3725,7 +3871,11 @@ int16_t TFT_eSPI::drawChar(unsigned int uniCode, int x, int y, int font) SPI.writePattern(&textbgcolorBin[0], 2, 1); line--; while(line--) {WR_L; WR_H;} #else - spiWriteBlock(textbgcolor,line); + #ifdef ESP32_PARALLEL + while (line--) {transfer16(textbgcolor);} + #else + writeBlock(textbgcolor,line); + #endif #endif } } @@ -4199,8 +4349,8 @@ void TFT_eSPI::setTextFont(uint8_t f) // TFT_eSPI 98.06% 97.59% 94.24% // Adafruit_GFX 19.62% 14.31% 7.94% // -#if defined (ESP8266) // && (SPI_FREQUENCY != 80000000) -void spiWriteBlock(uint16_t color, uint32_t repeat) +#if defined (ESP8266) +void writeBlock(uint16_t color, uint32_t repeat) { uint16_t color16 = (color >> 8) | (color << 8); uint32_t color32 = color16 | color16 << 16; @@ -4259,23 +4409,12 @@ void spiWriteBlock(uint16_t color, uint32_t repeat) SPI1U = SPIUMOSI | SPIUDUPLEX | SPIUSSE; } -//#elif ESP8266 // ESP32 or a ESP8266 running at 80MHz SPI so slow things down -// -//#define BUFFER_SIZE 64 -//void spiWriteBlock(uint16_t color, uint32_t repeat) -//{ -// -// uint8_t colorBin[] = { (uint8_t) (color >> 8), (uint8_t) color}; -// SPI.writePattern(&colorBin[0], 2, repeat); -// -//} - #else // Low level register based ESP32 code #include "soc/spi_reg.h" #define SPI_NUM 0x3 -void spiWriteBlock(uint16_t color, uint32_t repeat) +void writeBlock(uint16_t color, uint32_t repeat) { uint16_t color16 = (color >> 8) | (color << 8); uint32_t color32 = color16 | color16 << 16; diff --git a/TFT_eSPI.h b/TFT_eSPI.h index 8148f59..22610f8 100644 --- a/TFT_eSPI.h +++ b/TFT_eSPI.h @@ -65,6 +65,12 @@ #ifndef LOAD_RLE #define LOAD_RLE #endif +#elif defined LOAD_FONT8N + #define LOAD_FONT8 + #include + #ifndef LOAD_RLE + #define LOAD_RLE + #endif #endif #include @@ -89,10 +95,13 @@ #define DC_C digitalWrite(TFT_DC, LOW) #define DC_D digitalWrite(TFT_DC, HIGH) #elif defined (ESP32) - //#define DC_C digitalWrite(TFT_DC, HIGH); GPIO.out_w1tc = (1 << TFT_DC)//digitalWrite(TFT_DC, LOW) - //#define DC_D digitalWrite(TFT_DC, LOW); GPIO.out_w1ts = (1 << TFT_DC)//digitalWrite(TFT_DC, HIGH) - #define DC_C GPIO.out_w1ts = (1 << TFT_DC); GPIO.out_w1ts = (1 << TFT_DC); GPIO.out_w1tc = (1 << TFT_DC) - #define DC_D GPIO.out_w1tc = (1 << TFT_DC); GPIO.out_w1ts = (1 << TFT_DC) + #if defined (ESP32_PARALLEL) + #define DC_C GPIO.out_w1tc = (1 << TFT_DC) // Too fast for ST7735 + #define DC_D GPIO.out_w1ts = (1 << TFT_DC) + #else + #define DC_C GPIO.out_w1ts = (1 << TFT_DC); GPIO.out_w1tc = (1 << TFT_DC) + #define DC_D GPIO.out_w1tc = (1 << TFT_DC); GPIO.out_w1ts = (1 << TFT_DC) + #endif #else #define DC_C GPOC=dcpinmask #define DC_D GPOS=dcpinmask @@ -110,10 +119,13 @@ #define CS_L digitalWrite(TFT_CS, LOW) #define CS_H digitalWrite(TFT_CS, HIGH) #elif defined (ESP32) - //#define CS_L digitalWrite(TFT_CS, HIGH); GPIO.out_w1tc = (1 << TFT_CS)//digitalWrite(TFT_CS, LOW) - //#define CS_H digitalWrite(TFT_CS, LOW); GPIO.out_w1ts = (1 << TFT_CS)//digitalWrite(TFT_CS, HIGH) - #define CS_L GPIO.out_w1ts = (1 << TFT_CS);GPIO.out_w1tc = (1 << TFT_CS) - #define CS_H GPIO.out_w1tc = (1 << TFT_CS);GPIO.out_w1ts = (1 << TFT_CS) + #if defined (ESP32_PARALLEL) + #define CS_L // The TFT CS is set permanently low during init() + #define CS_H + #else + #define CS_L GPIO.out_w1ts = (1 << TFT_CS);GPIO.out_w1tc = (1 << TFT_CS) + #define CS_H GPIO.out_w1tc = (1 << TFT_CS);GPIO.out_w1ts = (1 << TFT_CS) + #endif #else #define CS_L GPOC=cspinmask #define CS_H GPOS=cspinmask @@ -133,15 +145,66 @@ #ifdef TFT_WR #if defined (ESP32) #define WR_L GPIO.out_w1tc = (1 << TFT_WR) - //digitalWrite(TFT_WR, LOW) + //#define WR_L digitalWrite(TFT_WR, LOW) #define WR_H GPIO.out_w1ts = (1 << TFT_WR) - //digitalWrite(TFT_WR, HIGH) + //#define WR_H digitalWrite(TFT_WR, HIGH) #else #define WR_L GPOC=wrpinmask #define WR_H GPOS=wrpinmask #endif #endif + +#ifdef ESP32_PARALLEL + + #define dir_mask ((1 << TFT_D0) | (1 << TFT_D1) | (1 << TFT_D2) | (1 << TFT_D3) | (1 << TFT_D4) | (1 << TFT_D5) | (1 << TFT_D6) | (1 << TFT_D7)) + + #define clr_mask (dir_mask | (1 << TFT_WR)) + + #define set_mask(C) xset_mask[C] // 63fps Sprite rendering test 33% faster, graphicstest only 1.8% faster than shifting in real time + + // Real-time shifting alternative to above to save 1KByte RAM, 47 fps Sprite rendering test + //#define set_mask(C) ((C&0x80)>>7)<>6)<>5)<>4)<>3)<>2)<>1)<>0)<> 8)); WR_H; \ + GPIO.out_w1tc = clr_mask; GPIO.out_w1ts = set_mask((uint8_t)(C >> 0)); WR_H + + // 16 bit transfer with swapped bytes + #define transwap16(C) GPIO.out_w1tc = clr_mask; GPIO.out_w1ts = set_mask((uint8_t) (C >> 0)); WR_H; \ + GPIO.out_w1tc = clr_mask; GPIO.out_w1ts = set_mask((uint8_t) (C >> 8)); WR_H + + #define transfer32(C) GPIO.out_w1tc = clr_mask; GPIO.out_w1ts = set_mask((uint8_t) (C >> 24)); WR_H; \ + GPIO.out_w1tc = clr_mask; GPIO.out_w1ts = set_mask((uint8_t) (C >> 16)); WR_H; \ + GPIO.out_w1tc = clr_mask; GPIO.out_w1ts = set_mask((uint8_t) (C >> 8)); WR_H; \ + GPIO.out_w1tc = clr_mask; GPIO.out_w1ts = set_mask((uint8_t) (C >> 0)); WR_H + + #ifdef TFT_RD + #if defined (ESP32) + #define RD_L GPIO.out_w1tc = (1 << TFT_RD) + //#define RD_L digitalWrite(TFT_WR, LOW) + #define RD_H GPIO.out_w1ts = (1 << TFT_RD) + //#define RD_H digitalWrite(TFT_WR, HIGH) + #else + //#define RD_L GPOC=rdpinmask + //#define RD_H GPOS=rdpinmask + #endif + #endif + +#elif defined (SEND_16_BITS) + #define transfer8(C) SPI.transfer(0); SPI.transfer(C) + #define transfer16(C) SPI.write16(C) + #define transfer32(C) SPI.write32(C) + +#else + #define transfer8(C) SPI.transfer(C) + #define transfer16(C) SPI.write16(C) + #define transfer32(C) SPI.write32(C) + +#endif + #ifdef LOAD_GFXFF // We can include all the free fonts and they will only be built into // the sketch if they are used @@ -402,6 +465,7 @@ class TFT_eSPI : public Print { spiwrite(uint8_t), writecommand(uint8_t c), writedata(uint8_t d), + commandList(const uint8_t *addr); uint8_t readcommand8(uint8_t cmd_function, uint8_t index); @@ -497,6 +561,10 @@ class TFT_eSPI : public Print { uint32_t cspinmask, dcpinmask, wrpinmask; +#if defined(ESP32_PARALLEL) + uint32_t xclr_mask, xdir_mask, xset_mask[256]; +#endif + uint32_t lastColor = 0xFFFF; diff --git a/Tools/Create_Smooth_Font/Create_font_5/Create_font_5.pde b/Tools/Create_Smooth_Font/Create_font/Create_font.pde similarity index 98% rename from Tools/Create_Smooth_Font/Create_font_5/Create_font_5.pde rename to Tools/Create_Smooth_Font/Create_font/Create_font.pde index d89d6a9..fbd6b91 100644 --- a/Tools/Create_Smooth_Font/Create_font_5/Create_font_5.pde +++ b/Tools/Create_Smooth_Font/Create_font/Create_font.pde @@ -1,3 +1,5 @@ +// This is a Processing sketch, see https://processing.org/ to download the IDE + // Select the character range in the user configure section starting at line 100 /* @@ -107,7 +109,7 @@ String fontType = ".ttf"; //SPIFFS does not accept underscore in file //String fontType = ".otf"; // Use font number instead of name, -1 means use name above, or a value >=0 means use system font number from list. -int fontNumber = -1; // << Use [Number] in brackets from the fonts listed in console window +int fontNumber = 22; // << Use [Number] in brackets from the fonts listed in console window // Define the font size in points for the created font file int fontSize = 28; @@ -310,7 +312,7 @@ static final int[] specificUnicodes = { //*/ // More characters, change next line to //* to use - /* + //* 0x0102, 0x0103, 0x0104, 0x0105, 0x0106, 0x0107, 0x010C, 0x010D, 0x010E, 0x010F, 0x0110, 0x0111, 0x0118, 0x0119, 0x011A, 0x011B, @@ -413,8 +415,12 @@ void setup() { charset[index] = Character.toChars(specificUnicodes[i])[0]; index++; } + + // Make font smooth + boolean smooth = true; + // Create the font in memory - myFont = createFont(fontName+fontType, 32, true, charset); + myFont = createFont(fontName+fontType, displayFontSize, smooth, charset); // Print a few characters to the sketch window fill(0, 0, 0); @@ -455,9 +461,9 @@ void setup() { // creating font PFont font; - font = createFont(fontName+fontType, fontSize, true, charset); + font = createFont(fontName+fontType, fontSize, smooth, charset); - println("Created font " + fontName + ".vlw"); + println("Created font " + fontName + str(fontSize) + ".vlw"); // creating file try { diff --git a/Tools/Create_Smooth_Font/Create_font_5/data/Final-Frontier.ttf b/Tools/Create_Smooth_Font/Create_font/data/Final-Frontier.ttf similarity index 100% rename from Tools/Create_Smooth_Font/Create_font_5/data/Final-Frontier.ttf rename to Tools/Create_Smooth_Font/Create_font/data/Final-Frontier.ttf diff --git a/User_Setup.h b/User_Setup.h index 3d61313..fc2bb2b 100644 --- a/User_Setup.h +++ b/User_Setup.h @@ -6,6 +6,7 @@ // // If this file is edited correctly then all the library example sketches should // run without the need to make any more changes for a particular hardware setup! +// Note that some sketches are designed for a particular TFT pixel width/height // ################################################################################## // @@ -19,6 +20,9 @@ //#define ILI9163_DRIVER //#define S6D02A1_DRIVER //#define RPI_ILI9486_DRIVER // 20MHz maximum SPI +//#define HX8357D_DRIVER +//#define ILI9481_DRIVER +//#define ILI9488_DRIVER // For M5Stack ESP32 module with integrated display ONLY, remove // in line below //#define M5STACK @@ -134,6 +138,35 @@ //#define TFT_WR 22 // Write strobe for modified Raspberry Pi TFT only +// ###### EDIT THE PINs BELOW TO SUIT YOUR ESP32 PARALLEL TFT SETUP ###### + +// The library supports 8 bit parallel TFTs with the ESP32, the pin +// selection below is compatible with ESP32 boards in UNO format. +// Wemos D32 boards need to be modified, see diagram in Tools folder. +// Only ILI9481 and ILI9341 based displays have been tested! + +// Parallel bus is only supported on ESP32 +// Uncomment line below to use ESP32 Parallel interface instead of SPI + +//#define ESP32_PARALLEL + +// The ESP32 and TFT the pins used for testing are: +//#define TFT_CS 33 // Chip select control pin (library pulls permanently low +//#define TFT_DC 15 // Data Command control pin - use a pin in the range 0-31 +//#define TFT_RST 32 // Reset pin, toggles on startup + +//#define TFT_WR 4 // Write strobe control pin - use a pin in the range 0-31 +//#define TFT_RD 2 // Read strobe control pin - use a pin in the range 0-31 + +//#define TFT_D0 12 // Must use pins in the range 0-31 for the data bus +//#define TFT_D1 13 // so a single register write sets/clears all bits. +//#define TFT_D2 26 // Pins can be randomly assigned, this does not affect +//#define TFT_D3 25 // TFT screen update performance. +//#define TFT_D4 17 +//#define TFT_D5 16 +//#define TFT_D6 27 +//#define TFT_D7 14 + // ################################################################################## // // Section 2. Define the way the DC and/or CS lines are driven (ESP8266 only) @@ -166,6 +199,7 @@ #define LOAD_FONT6 // Font 6. Large 48 pixel font, needs ~2666 bytes in FLASH, only characters 1234567890:-.apm #define LOAD_FONT7 // Font 7. 7 segment 48 pixel font, needs ~2438 bytes in FLASH, only characters 1234567890:-. #define LOAD_FONT8 // Font 8. Large 75 pixel font needs ~3256 bytes in FLASH, only characters 1234567890:-. +//#define LOAD_FONT8N // Font 8. Alternative to Font 8 above, slightly narrower, so 3 digits fit a 160 pixel TFT #define LOAD_GFXFF // FreeFonts. Include access to the 48 Adafruit_GFX free fonts FF1 to FF48 and custom fonts // Comment out the #define below to stop the SPIFFS filing system and smooth font code being loaded diff --git a/User_Setup_Select.h b/User_Setup_Select.h index 93a0ac8..c4e720b 100644 --- a/User_Setup_Select.h +++ b/User_Setup_Select.h @@ -33,6 +33,10 @@ //#include // Setup file configured for my stock RPi TFT with touch //#include // Setup file configured for my stock RPi TFT with touch //#include // Setup file for the ESP32 based M5Stack +//#include // Setup file for the ESP32 with parallel bus TFT +//#include // Setup file for the ESP32 with parallel bus TFT +//#include // Setup file configured for HX8357D (untested) +//#include // Setup file for the ESP32 with parallel bus TFT //#include @@ -62,6 +66,12 @@ #include #elif defined (RPI_ILI9486_DRIVER) #include +#elif defined (ILI9481_DRIVER) + #include +#elif defined (ILI9488_DRIVER) + #include +#elif defined (HX8357D_DRIVER) + #include "TFT_Drivers/HX8357D_Defines.h" #endif // These are the pins for all ESP8266 boards diff --git a/library.json b/library.json index 019b150..bdf5b64 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "TFT_eSPI", - "version": "0.18.21", + "version": "0.19.10", "keywords": "tft, display, ESP8266, NodeMCU, ESP32, M5Stack, ILI9341, ST7735, ILI9163, S6D02A1, ILI9486", "description": "A TFT SPI graphics library for ESP8266 and ESP32", "repository": diff --git a/library.properties b/library.properties index 1f05e67..7b582f6 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=TFT_eSPI -version=0.18.21 +version=0.19.10 author=Bodmer maintainer=Bodmer sentence=A fast TFT library for ESP8266 processors and the Arduino IDE From 90f70109a73bdc14670041e63a11c0d6fd3c2155 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Sat, 10 Mar 2018 23:21:20 +0000 Subject: [PATCH 092/150] Add images showing ESP32 UNO board mod --- README.md | 4 +++- .../ESP32 UNO board mod.jpg | Bin 0 -> 138373 bytes .../ESP32 UNO board pinout.jpg | Bin 0 -> 129500 bytes 3 files changed, 3 insertions(+), 1 deletion(-) create mode 100644 Tools/ESP32 UNO board mod/ESP32 UNO board mod.jpg create mode 100644 Tools/ESP32 UNO board mod/ESP32 UNO board pinout.jpg diff --git a/README.md b/README.md index c11fd2d..344f1a2 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,7 @@ Here is another screenshot, showing the anti-aliased Hiragana character Unicode Currently these are generated from a sketch, but when I have the Alpha blending sorted for colour backgrounds the plan is to build the rendering code into the TFT_eSPI library. Watch this space " " for updates! -**3. Add support 4 8 bit "UNO" style TFTs with ESP32 - done 10/3/18** +**3. Add support for 8 bit "UNO" style TFTs with ESP32 - done 10/3/18** The ESP32 board I have been using for testing has the following pinout: @@ -64,7 +64,9 @@ UNO style boards with a Wemos R32(ESP32) label are also available at low cost wi Unfortunately the typical UNO/mcufriend TFT display board maps LCD_RD, LCD_CS and LCD_RST signals to the ESP32 pins 35, 34 and 36 which are input only. To solve this I linked in the 3 spare pins IO15, IO33 and IO32 by adding wires to the bottom of the board as follows: IO15 wired to IO35 + IO33 wired to IO34 + IO32 wired to IO36 ![Example](https://i.imgur.com/pUZn6lF.jpg) diff --git a/Tools/ESP32 UNO board mod/ESP32 UNO board mod.jpg b/Tools/ESP32 UNO board mod/ESP32 UNO board mod.jpg new file mode 100644 index 0000000000000000000000000000000000000000..10633312abc2f8ce24d8aa953651eea6d45f7536 GIT binary patch literal 138373 zcmbTdbyQnV6fPQyI}|AtEmDdXcT%8ufkJUhDehjJP$=$DTv7@YD^OgEL$Kn-U5h73 z8X$z5-@SL;yVm>by*D{$t*n)qGy6L;v*p`64~q|*016Eibrk>>78c+=<^y^k4 zIJkHik%)*f9hxXG;$ve!!U&6tgM;ZEg83bQLy7zNnUDe=m97;&n?>cE!40 z8og;4hsX!7a6%&5Cv@~rIiGXA;N}q(6PJ*bl2&@HtfH!>uJKOaz|hFp#MIix*3RC+ z(aGD#_oJVGKw!k@$f)R;*toRxFBzFx**Ur2ic3nrmz7sk);BaZHMg|3wfFT83=Tnu zM@DC6=l;wuEG{i?ZSU;v?H~L-Jc3_bUR@*pA#ZN~!-WOF{$H^E53>IUE=mlpN0>Fh z!T%2z)+0a6jZKMz`%DP$v4SqXl_wRO@FxOl#nhs@UP5*eJs8aguW2G$4$&=6_6=`|kR$AqVptDUDnW#$~ z_ozxA+V;n;I5nAQWSvQGg3`6gu)BYRFO-U_dmBGSX~T9s=xr8!T6c#0evOtxnMVGh zL!)vY0K{DVHH%tc74ScIYxd)XGPywA)2&e53(dL?(Y&lUn|hs{1xs6@?LrZdL+ z6H9vz%;bI{PN@qgaR;ZFoxb-M@Y@;rS{G=1m0iyStpRYpQta!WvxqdEB9D9`Y*$bH z4TG>@YpT98GNP&|spI90>leYbFSQ6&Sa@TxR8D&j4Njk}@(T>c+TFCzaK=uV{0xP#PwWqXbNyza7fVcPgq!Qt`hXv6Z4 z0=t79L$Be)qwZ0#Y5L@QO%J@&Xg<8z^F!U!iU)u&v1Xl%W(1V_+YV`NrKuj)B2-R1 z;h=@C)D=E7_=yM@bHBcJ>&KE^iI_SIpE@M6VCbaEMTSmC!;+F48KCY@V;Ko@sjK36VwABkXezS zQe9w4XvD>BB_iMVZ~>KU>d=1460In#q7*JHJ&UbuziSsExi()xe<57~HK*`=l|_cN zdCSawhKrfuNXfh9mwu+3e1QXQlH6_3%+eyU;(=e;p7v$EKiWXu78{ubTq6+%k~MaL znf$Ni{ytjK@O0HL1*93OuJjLCnkz>3HDHtqv|l+>qBaYNY(li0a9 zz&$FSuV`h*<5Xq!%}~7;yE|!~GpP`F@L2xrPukC6GdbeP)dnMm2N$CM(r@ev%Lyf{ zprRKy?zI{}CcJ)yIBp?YIX+!i5Px&M(|VCEMx{nL;YD7`lV8YFEzs-ph5;Ya_oYQv z|IGtH`XH$hxhle}{PZiwzlx7pO!}! zeTjYFxb~?p|0x4xJj!CR${nYtyLvRc@xDMzlQLEIlMHd)S@mns`TknjzMn-6>e(Y2mE%%2>PUlgrQeWZb*EbH1#HuK;)3CFQ-n6(YHS7vJIlMbC!IIb`)`811S?mrMaJ$rot0Zz-sa zu(FY=rU2{K4|2&~{4i4yzC_22{08eVO16rHPjC3c41(nIXbb4g&f&1uwkV+qR>?E+}U_+jM`2% z6YxmwFa_%vBvGipSF0hude)t$fCL=I*;-B(HAX=}hnom{?|(QwPoaw$>+xe?OiMO^IKor{14;wo2Xj4S^`G zI!<5TSN83kim2~*Qf#RL!cU(bxJhyypISUQiN6y<>L3>3?n&R^?g>p0V)>|e@f^8? zugbyw4nprw0iRk(Tdh5B%w$8fS@1PV^qW84jUVeb{zwMHpr^lJdtw!0&S3n%2XX^El#L$-X}0$Q(MsI__xEg&Uy> zRJU}(XB?TI{XxGto}EOrZszqb@R%YF&A+7EjypXfNw^L*X?^&@6!GQ+=gVq*^-qPQ!zj@~9;%2slocW^5REB5l z1@^fscCDqiWjag~O;}Wc3W5a}vbG>|JPAG%iUs-nlxlF_KGAKpib&n45Nq6tLWC}x zzJ@qT$QWM%sn82uSa*0UU_xXKSD|8GP%;xpgFi$2wN}Vwi&D)5K{XWqr+FEb2)mE29^hQMS z@1x-*$ss=%7oQ*JR464^@_r?aD8i(&5RXJ^EJpL+&)zkQgaH`y)1gdNnHf(F*nJn? ze_3VWzWTh;WEhLvFV@*jMcPi%nhbsZBGrGGcvr}Jc5hO0!oRY8;WXLRvC3S%>=?{A zulBL2^#SmCpPj@N^+t86*G+sg!M=#mC=o^T00?&uHopHRE51DUbSR^w#EX}X_7o>I zB8@H|ck%<>LF&0dmux8J4O4;bep#cR8&Nnn3%%lOP2ze0sMl*(?~_UHGWD|;Hcjgl z5P9tuCS;HN((%YcJOCWsQ0J$~iI2SKbN6=!kM}y5+W1nRxIFQ4Wi+*?Uyc$ju%>6B+RW*EGS+5LdSomYP)Q+QU zD2?-$#K|Nah$ztYE;9Kx_>7IgilVhOHHaoz20qJ3)|CI#GtE0G)=ff}%kH22Phbm9 zE>pNDgf>*>)kJWE)56CE8EfGB>f&vh0#nDl?E;E=!MvkA=gBsBsGk3;uA^W4Gs+l@ zr_(nIWTs8 zM|EcA2(e7y=1vvZ`q|Y0pM`sJ?J&9G$ChHGR_=4}e9N-cr)4N=dy`q83S0;jk~q7z zDf?0|n(wupFh1$C4i3K_fPG4csx)x3l9_+TzSSPN1#chP*HhFHh%DxjvKWN4sHNfA)Y#vW*T|9nKf7&eXE}(ote&K z*85a+l>d=A!K8{qHedF?m84(X_l9Y?35_ng6;duC4P_62USqL)qBp&FTEV(`jwVkT zy0IMhN+f%E)&unV=jxfWSv|-}z`X{tp4$wr?@n2o$OoLc>TiDhL(*{Ez2ewCZOJt- z!tQv_e6qN6d9gA$HS!H2-bdJ(VxB2mBb|otj{c=PQ;SlDGYo7jEgN(SvWbgV#e6dd zc$u+U<5x&lhn8ttTs>(#3;ojj0Kf&o?XujS0+)9NO>5H>Kj&?&!fzs)HEXaRX$ScX zInBpn`uHK^rFA&KO-_ApqmmesADQD@k6?20RgmO$e;`o7@ zRD18G)!xeFpq^E`wz?EbghV8eAAmBStc%PAzuSI2g_0|)zMq@mFKUS+m)09Mcm?nt zv06lD%saEx*AAYt4ZY)v5VNCA&9Q$+6>FtCm%ZfAGTFqF9r$)$t17%oqEYu&v>Rs1 zlE@!8bqH#V?up-v6psb<=Rz{q@47~X&O5&IngWr0SzfzA3=aT1vlvr$*>`42e18vq zS|tvfO^2~FZm0Kk!z^l-@ovv9=uuX8V*B$zaVoi~Fs970aBtK!J$e@O4~ zQspHczIlGWXTFVn9XuKJ#AxT>QY zB1CcruI(Pi@)#wx#cyQ(Czx}aiA8-zpr~=q-EGsMYhrfqgJDd{pw74ic~;%2vGC1D zJ!R5%v3^a~*Wn9TE%)Q!a9PtKmX#qd{CWK9KVCdH;1P(qAJRce;_t9Id<={T9k*o2 z9n-q7Q&2v$q}X^&ny22Gtyr$ItGtYf)u;Eh>ETyJDaZph%Dg%b-ne6!9z=^{jY zd*Y0a8aDf^p#$ zH=HQu4-=|56yB>WN}vfu9^86>nYF#XQ0KA#CxMJmb?plAze9a56IjB913gOf8jLgp z?p`AyeO;7MLkrMQo~BtgDgxIJ^Jna_!F}44k6)F2l()q$lty&h^0FGR7M7i7MR#v@ zg;K-iINT!_@pL@E0QSiO1W4*iI_+74TVCAfE>o6yCLL-PR;)y>Rj1N!n~oyKBh zC3Sz-2Nh&=c}fm_08syxXXu#XLn&`}@DtWVuHO1KS9mkEwHj=@b8G+k61@4c-?mkD z)(Sbj?W&}o{8ons8Rx1rjw@iM|3&kckrW?`Uv^ca{l}A};x2Nsn;+|?QmR2L(-sc^ z)RVF;%d8@3#Sw%Ib9PB-2b*~&!7%jEi)@y5g9Y|NFi%4nxHnjy_&Ct>SJQN;;FuLv z_Fg;2zhcqPB*^|K@KZQ>qPq}8g`NqOgKJf;JF`#*JNt?X^QEc>)S3_Lrk}NO_9eF6 z7}v1_gHrV@(W}76rLC=w@_fN6DQ}D>l6Hoqqt7Kb>*;S8S$?Gm3ng57^tL&b3?ov0 z{QxT&LSPh*Mj@gb?irxMuXQp%LtEvJFiLLd z0YKwavAd4UGjDA&p-;8#w0>GB+^aJza-5LwZ90bUla@~AfB+WDQK88I_gTJGAYs86 z35Ty+kS-~}Wh4Rl^THp5g4*eYhlbfQwPrIGZSsb2X zO&1{}cg$R+3T$YVOj~IO5;ELhP;~m@dT!t~_(Bo`5p7v{1E!G!d7tE6Cm~$c;j0o_ zt;2F;#xt3;e)TEds&)6Q5Tu~}Nv*y8!D_JHESn*aHEHHd{iRGhLF^mEDqg^lhtmTf z8ob%|0H{BYqg9RGUWnRM^GpW|{p8QT@AW7*N0miRNKWd4ep}YU?*=VjWJAlH7h2>s z*r5AcIr{bh&-7$im3TLO@GIcFvo|XN`EN!Ae5-$7P^g_dq%Re?Sp{kMJk}+c z2qd>?vBo#~Y!66J3OEFfRWA%#sH_k18gHokyDBIaYczQjNUmE0@4^pd+TPu*p z6>y)N$C05{{ohO@th!+aX10jbuwbFIU=x|p%}RYTZ;Oa zfBX|bN0OSh_*;G!O=Nv1XP%Ip2q8%?y6IGMk(BBkLgVA-qAi1c@7_5V%1( zn5w(73-v}y{om=#GV76Pb|?@|Hj3-FjPh=r_fKlE)46 zS(+hz#J{A@t&osSZoXoiUwzIV0lZ0VAuC`@^fju%5!57i{z-Whx&``tJO1w-J!klN zkEy|0r$uyT@XwwL?-uMbreo_Z_tQ-XneScvSZzdi!@u}W^lN5Tn4*>=tuT2OwU>%S z0zAdz)i&hLbXsFW5c!P7W}|cRNcbP332F8{58Q}SgNc=Q#%Q_OE$|mVp-cnVa1L#H zy;L<%HZ7b*6d|3#M8EL|bnt#tz3^mnrl98WP?$KePMvJMuHBaz1j615k|T4s3GME4 zz_!Q!&BL{1kS)y>A^8(uArN~Q!O2*d=f!U7?~Ty8b*2`6gkU%ezE8~TLEH-?i<~c) z;mP7ZE~q9Ikyk6DS?I-`k_P}j+HSsIc?mLifu07Fqb^xcT!oJX^WQs7FBde0da zL=PqtA4}A&EFrU$yIM1vRY{w6z17*RWeoJwpn!a=#ap8bqjFThbQ7_1La-Q&LzqW6 z=UMwad+G@5ZWSm4^py^A=6nczD!N8{MBExoytUD|WWhNu{Jp4B`5Aj+$IPy9MEaY2@*vpczpE**KJZ{Mwm9+hG97~d27EHPNd*71(zFZ7K3tAQpeU41L&-pge zq0}(f>F8a_#7x=vZSeU3kw_Ye=Ql=@7rk7?q}Op(g~D&7xU0{=CSX|Bp^G9W<~BJS zQoGFJy*@}D`W`Im8{0mH zwmGasDX~9Z2Vw6`NFEyboUin6%gVEa2!(P6>qKapysjI363_Kom40&cNf{7idgM6- zTC1ZiUW3f%2UhV67Yd~ zP41L)IBq{b{Uk!PMW}_;=&DU!DsvW@U z;o4328&~o-o2~dP+D$JqjBSLk)_PAt!|mub7V4G=mO;T-xMzxLj6hZrim&qlP?%BV zdgp<%>`do{2W>A0I6naRQ0jh{k5q(Z&z&;sT9#<;YDI3B?e#i^3(9ctaQ5@Vir+`Q zOGrqt>;6J>-_a$w{G4USXmCKNYX`%8_E`67(;>Y-OPmwL(6dMGgH-yQDG&69ocY$sP#rlVI)XFwrro{nK-QT+}8+n64yJreecfed3Ut%QyBhO zY=FQfgHdMJrSJ5ww=PfiG3teRV+Uj!=IdyI{b)M>CI0tU>{p`u859Yf)=Xa8-Pzrb zF|mak*wLLFxuaJ%h4Gu#luW0q&pD!?2I!u~mSo)+sf&Gz5vO_;g5KpLh9sxK1=JI7 zM8mW(d$3(V@q0Y8R}CvINrc8veq!&rz!!3}$C!dHzJ*LApz~~UbdCJR3bCxhLZl8S z-A#SGD!3r+o@nsoTUBe&1f-V*DaC?_g5gy-3dUUP5F^(IK=fzUS!$Y0if7o7&in8N~zSQbn|vHQ12MQlsT&*D@9Mpk=2(G7cq8sZch zb!mzuNtZPt46N-WwBh@2uK4!zq2BMP-|H=WwXfCF2+WVOigK*pCDJPTBqAM}x^)geygUB`9^#=Uc3JE+s|`D?82wB&jazd5V!7jdpm6j1lpVwmo{mcddThV2QAi)BQofm=T1pJbFOm4$Z?Ui1E%ZMu#o*Z z3bQ5EZMjf0EtSE4HgDc1t@UGtDM;S^x&%2Y$~HYcXwrnO={p&|HL!HTE@r&MPscOCa9Uc=?-XKrR4pS5qL>a-y`q9GC1I+AC zgT^xv%PO@TAKQPpz(=)*DHZFn8t^%`z)K^j9(dVFYHhGGGLrKUxdl^t~17)$qZA4rdeDMCCpJM`d;0E*ck3mgopS9-u;oU+idy>wPdA zGVlv){9)t5D4;J-IDw}U7t{Gw$3ui9Ez34`yb^G%RJvXlTWzYbK)Y8P? z+ju6}U^_(9(0MQdH0_(`9H3RQ5n$WgmPjqC+z@Hi>eE9nhS7VCL#?tFcLC0~$7_79 z@@2ABFPf4zMhR)B-e{~FTeTo{Wc_M+km|KZflr3qiP~e%e2L?myq{#_*g{>XT3X%> zx&&*%XZ)c%+HF-%Q-e%b?Fn}|6q3y$C%>?I&Fv*8JcKL?Ckg~C4Gwp$PR%}Mmi@`~ zLs4^fTVv9}jwwiKCi3m?Uoz|E*0Z+dL{6F!EjWS`)x>9CHZc?l_GLAY0zEg2UIF1n z$DUscMu$?s@nT6=1fxz9&zH~mB_uLjNb0g}%&`*d&u(CKll}AS8-Uz1W0{UYfqn$#J zl$Gf}_`MGQTx}cLTJQL3O6qco(f z8Y!ee9>bp?RN?Z~^0nXA{69PZOfT8Ac$`gqcB53y85K3^ul4XW^1Y;u3i#xtkx(Nl z+8a=Zed~R-rMBrui!pE`q&I$p6g}AvPD5{?(9FxR2rSSco8LmkGRy?MCIL_d?vI7I0gxdk|xT zL0f~j&f@=`$Uif&pVR)v@$dMr z7wj|ic5KEoI}|X;lcahH6gc@bTA7?zMsuuNHeT}mM`^uT4$9DL6#-sWLK8_yoSy!0 zV8)$0X-vlH--36YTTENG{jp9hRxIN9w3DQ)*sDaR`^P&qKTY29zEqaJ$X}_KnWH;| zc|qzKV6{_3<)3OnJlo9qmwj5*u1vYCeI)%z8^3Iqw~g??V6 z6^!iiR)-tdetR?M`P|>#pJj2~w-Vjowsj)o!5Hh^WXJb-ezw1|yfV{vSXfhBc-)Af zjm~al+V5e zo>p)ZX0srg;d0R@dr$iOyBpk4yka-4QQkJGmMfzu7e^Yu_RAr4Z07xT;>iR`6dCf% zp+j?fS(e=g2~uB#&s#sY0Cw4b{j7|^9SW_k)*>Sih_ILtk=3^KSbKHs?F>U7yRvpndE zyZ^L@kUn0<=2L4xFylY5HB>0FsB95QrU`!PES~Blz4w_`+P5sKv8gqU*8sO`iCz1z zf(^rGF49Yi6*7@sQPH!O*uX8V8Wf_Z=y@PZOu==IJ?PvdplFlHu7lh@-~x^ ztw%K$pM3eXa#$qX5j^RAf=L4)dB|f$$thTHEP_R5ea{JzxyK%J)mD%@hsBqV-nnvv zQpz30QLUq7U&Gb~?;KFHNZ@udg;QWl-v%kNUn{cOtt^U#Vl6)8wJHu)rXC3Q(?|C# z+?b&NoI+<^I3JKi{-r}9-&7NW1xUNmEGSPs6+My8g+rPngY~{2ALk%-<`+Jq{r;y8 zv=&Ewd-AdWTL)9Ey~wqwSYQ4wf$N2=4|TEr%=&Ly*F6(a_PKkRW*T{jpXvKY?$>eY z=7|CZ{~#pQaN}IM0<(RYFW(n0dx6}$66|vKkv1Eaia&zCu6(jokQpH%|NH9jyzpCW zP1m0wzA)7AI01di=6es%^iN5}zg&XFkhVWi_V80kBud0-IjXMWaRtk=-WsKTgy zQq2=9P1;!Vw^U0*MlkFC@4iiz`_2UAJl6=0?j~*o)@>}V9^LA8cM7eo+!#NiwR!dq zw_XCf!vPA)yWHuy1&0AYOnx?Hl;?-nL~j4Gem8sOOUEbrBJb_xVw-YvPNulQD|o?5 zGHZBI6hSv;1`IC{o{n@!yqV)ny4!jH#60_Rz9sIBgecafSgtG&ADP(j5L>C;4f|yf zNq|Z1&2Z@#GPM0*SE9wbJZ^&CU^I~sTxZ+pRCyan>krbOa%*CFqE%_)Sf?>lrCdhr zEyBk~N7dr;^(^rVKy8s&h}i^Ih0sTazz7Pg&F951gAUZWePt;X>r714;!3yfdb1NK z7&wz&QfyLTD$;QAknqyv_`uVwsB_Eh6_eIzgO)%1f*RSWy`&7^H$*~J)Guj1akx%6 z5BP-G4|K83@q-($f14ufx90+WoOFGz<6^a8Y#T;7Gct)+TqvJ+*lK&a9UN`+x+D2A zQ^9wy{592Ekzn`D6=F}?tm=>!Lo-AsVZqyRc4wcz9(QU|*bHq4J>bn22uzC{LpanQ zKqncGL8MJ)r{aS?Af&pWDE&#HgOa*OM{!F5)&L);9Z19h*F^v45GER1WI#kBabb23 zfIFpax0v>_xc40<)ir`H-Ay~3;fcQ~CFM<+2qanI=E*49Cy1ry`?@aR@p4pbMQ@eA zx8_guaLXT80h8^Hg!c9-<%^gU+%54<3OQ0Q{ocNWEgfwcE z2eF^OziNFyE?#l>)#}skHL1nMso&SXMjMTP>JR-8|I*cEflh5rZ6^9}dYD#rT-$UT z78tK;R<&sjw$pT7uhdqMiFZ`MA<)we=Wt%~2@a~#K6%R>>1wfJfOG=h0aiM}67r|%M>dr-;W0l*W@J0(!) zZy=4E7_o_corUR(6Rj&f7yl<7?)jVWIh&-_!i?$21nv5OoNs>Lw9;##EfJuOnwtzqrvvC(lsW490 zZ*{lZ8H|P08De=HtWXmylQhBLf3}v(%K)lhw(Obwob#-QU`6AMUTRg6PHbnL0+tV? zLg_kwQ~lVk)t_n2_d6zs+BO^92$0kx^9HH<2V8-kqG;T)9P6E8E7r!-S8!(Mw=5;| z#xo%9=L}8)>Th55Uf1#=rFJa15N=3J=7f;ZG&6QPFP=8_!_STxOK09l*+=URdVvMi zjh`;x=lVN&mXeum%<-se7^!Z6>a+R?|+5V?b=h;qQTt1W9wYe#B2 z&xQ71Jh~m?)VaHbF(6DA904Zrh0ryZ!nA?pD~Ew^1Qvu2S@`ou#jO_hx%08pe)!oZ zWlW;yfQ{pjCs$3HiDpKcA<8@L5ET=VvSl96yDo!|oB!SjGG3a>Qz2o%E%l>yk||O0 zrj~eiO-)_gy3X!2U(z}@g1mY^$QP0wn&F?d$MOp~w?3jtY(%n&PQmA!5L_ql<4{&d zX}qkbzbl=x2%X((FQEJ4fe@Fc_r>=)-J3bxfKW1&&~&FDoJt9n93!jF3l+OZR0kSf z+j?dy>?eXq-m()em{LEQ+7D^=^>657I7@W&Im7E#Y^{aU(MQwm0SOh7jNvcM_9xV6 zZghOHP1XqbtnZWu&e#Ky3IZ3yP}Y!aPJ;@EF@utL;Ga(S*_SPf6273 znU;F>*I9cTcAw_30B`RJ4~81=LV574ZS&0au~ZEfMZdJh3=dNsK!F2m>egCz^NL2& zUpt@L?(FxG7iJHvf*b5nR2^GN!4lKk$TIuw#CK5Xy;a)3^H1}X?BCnCRwfj?nRpfD zW$e`AH3By)z%UIUHhRdfiylPeeA(E3G!}D^TEO_N$Yu`FzFWE5Mi9xS8rcL6#{~X}F!+<0;Airs!kD1Sv0ZDesV?Cq=j(m{Qh95D zyur}Rz+LMt2*csO4O)42M_N+x2K#8p3^LhDbrAH#3}dTzVFyqU!Nzenb*IS>e|EJ?aYxTZ zM^{hpz#=T+R@q9%6iKOxKrG9VI}Byg%Z8>x(cc%a!}0qb0C5pR%;K49(~T82%^7FC zs~{Ioz~9x!57x!&>3F#NLHe>Iw!`9ihOi!w>uKt-qkI<=E>Uy=0|c7Dpg!=QG}AQQVe zSK8cKxNMLs72~;26P#ugc!uxoL4e;BS_keeFQl4Dj_7*nADbqz@V%pMHlYMHZ5FdM zJo%QsUb>wi83xn=ePd`ex3-bKg-${Bwy!@O8kCwYddZ$*vPLUdC=IFOm{=MK!0#{X8r&O3M0Rq z#duoo_fh00-gips<$phdt&zUl2#Kd0&aAxFk>dy(E0d2eKeW*;>SJHzq9FjxKMT$X zPgL2u1u>iRLbsVH^%u${i_GRa}glXG>zVlpNd=0;}rH zEo^b8*==!mcRBy(hr@*f2#|jORMYxVhLV3KrA2i1)*6GzkhgrP)~ ziIu2@pRF68c|x`(T^;}$^YE7V4>0%Wgead(l~MNmy_g+YnCI!SCDXRx(>2Qxar<=V z;9cy0vFD}^O#(7ZWwD(h-yKs{2c6wYztAKZ-Xzm)w?e4=le9u%8q<}0_J9|MJ-=;j z-VlYX14Vj8_fv5u6NC>ch6lCItDc)ByDE$)w1GR4Nco%oCb+I#zPE3B?e;S|lSJ|_ zk61Rn%IP>b1J7%u9+D$k3DPs-(XC zzi*yfON-d-sRRaZnC64VCmY5yqG91TI-}){qYKJL@=I40%gV|-=VVR7tDISO$uj&HOoYw=Z!RSooMSdecBV&Ty&9%U>hY1`NGDd4H$#H;{RQ|b z?2=fEQ!?Eacu*h>Y>wO4e_Hoct;E zvm1x6BqI!@Z`(#H?4-mjR!Cn_jC$&-ZAGdJ2G+7$l|PbN;YI#&%*S46=5f?^DqUcG ziyKrSUPXqi9Y06D_%r`IXlUWldQkS)#(cHuXIAvASRG8iI(C-3H3hkd%oxI;93Q&* zFjAL@PgaA1l?JTVCng-(Js5lfUPzEol(pc)&Ow5CBTy!lwT<0CVe=EQlJ@>%v2ER@facCdX-LwOybKjHTGHU#CMGhYG;yf zRTuE$-RYN+S&H$B^H0?qTWv%J-tpafLO0_1mZXLhdRTqvDh*ro%scpay3ZD*L%- zBnCzVpYQzlc6J{L_LD39J??F?LBcm(S=lCA1@mCQv8QQy-r~uRPA^sKJ6hkqWksA) ztgu>#S7k`9LEJI~PjS5+OM~qf_^-;9lL{O^J^*aP^@>=rBUxSP-LfaExt$G#4tX;s zmaq7Nag;mmzjZR&E>5`h*k(2Bhkd;|U^*7ue)If%o4JEWny|aECWqYy_iey9H3b>? z*Nsu_UwO)()7_XPS~H^A5HaiY@#DgRL)uugBi&Cv<7dy3qyX06Ndw3S2%nNO9a}w% zWlh#?f6LA>R5l47U@=E$d}o{f^1j`C6f&^P?eHYauvy~e^4f@9!kr0a95;5WZ0a0L zXaiH>`sGHRB}=i*`gsO>z;q8WO|vJ;;fIdwWmNCZOnuD(w|66;vR~Fd9XaQ%6isCF zfslpgQ+}ZRdR>0NT6X*s-LVoA{=#CJHOB=fj>a}wJ~!!~T%YD_73A<~rqGjxtaCo@ z%l5rhWk-}tYXTKaoqn+4*G@@9=dQ8&@0(RKp@jv-jEehd|{u2;zJ8DR0x4 zsxCf)F{a`=I+U~ErbUAZdVcxEEOcpKhhm%47JuaJ=@+PWn6=msqppf8Tjd%Pf$!@X zTh&hgdLVRcEerPXI@9}2ng4W>90~p`j=kK(YHCh_3Nvj0H)Y!|74uF3*?Wm!A`{K_ zQ?s^d$UmH!B zv&CPV-4w|5-rK?XqEZW|r~Ih5Vr`xEt-weMoAa9W#LvdJTv3D&Z*kJk=^WhoAp4p1A4lH z8>2>ls-37hnESTi;F^73Ai8er*E^m}BWYI@!if8BVT+z9C1iwV8;F-lh?e$u6U{!c zP$sn1I?0yZ^ktGZa_6JSgT%d*`_eGbky2@XnB=2wOy6?=Y zQk~U~AZQ{@c!$XoxJ9r%vaWoj+*Iz%h~EzJN=6~xQ9{iLP;lHRYQs4(Ads^&PMwZv zBJtS=<7&gp_F*bD$@6f|2vQ2FTIR@;S5PXfSL8^ZKqY-R3^)DjcY1OMw zcm00e#9k#TT1X5^n*Ev7gQBFW>Gk+N6S{jZZPPgb_Mg2m5Ww5QWyj2f#DSGQ4E* z4mfC=Ba`2{x&KX}VUj}A8{1Eu3iKNZp}w7vJ{_@A?=h6cPNFju%{AAQsB-3f|4uij zrp9fMQCv+md9PskExuQZI4XG=QFwYI9ryi-ag_{gmCyndD;NDfPA~U zU^P2=DVX$We^X(8#W0<9f|ox_qIIxd4s;;#{iZ0y8JwN6VX4ZTp_j21c2I-!Xf69I z-$4<;&23SVgQY+BdV>h%m$&)vP{)OUXj5f z;CX3X=syv9v}sT5BJVQzNB)C{D)D5C>EN1Ew6n&6Biw$67UQ9BuQe4F@ei;*>8zV6 zYRWyH8lA4O0qS{})Gg$(5QK`X2D2hRl^s6~>5Bc>oZPwWAE6O%^t1It2@_27A~oDI z#p7eXXYS?UA8Qug(IfHG&CTlx?w0A!l7i~Om*ip~vVQP*h^-^W2^DXJb(Q|0f>|S+ zTGP$Q+?bEIuKc1{k1F@^)08+|H1G_J^bPHJs8Tire+NGRtddWDi&A*Z6uqaGs4pi{ zog(XJw3!L$@2MN^mL_AE|0dF@`7et(s%)v0H$EpXlnG7`t7*b3X`X-jjU%t5p*udL zbP9kKBucXa4HN;9cF<{{RtW;zWufPQk|z>1dltc&3hgB=S%qqJca@;iW7g9hZV-)d5NU(jy+#B_UyAp#$)v!54t-& zjR5yG%&@w@jH%_~0{VL4dEUHBFkFkE4&s{6OJKgg3H~JFc@dX6QTJevZis zoc}@nBb>aco{;~mK;;|pQ$~9Lg^KgAOQ$mb*PA}`^ShO>Rqwf7p6l!5m7mX{$%3d_ z`lCGA5gG_!w5Mf_>9|0G|KO1lSwXq>*>1=4K<$ixEmx(ji3ZNwBjPGmvE0LBYUps{ zo6kD?+;m)S^Gf-6+6uq@ESc`hT|*zExTTFg`vjS>&^Xbj-3^M2TEh9^jV3FDQU3!H zLG8YZH8?EOZ89^k#>GsxiRnaKcr#Zx=_Euk2Z5Hz{C}lC#cmyJPVM__(14%~pqAj` zu3MS?qI=C`YBD^oMC{I6EHK>Q@<}}IEz^u+)~1VBk_#2Kl2TwSTV{G!DJ12i&pmol zn}&R0j{ux!{Qm%wJ!)G^am#b2pIVM-7Boh|AwbVl=qYY|S!$3fp>5~xo#1iEIQGcL z0P>`EH4nqDhuWvay=F^$HI6%bG&A4c&7UVvfDAN`j&YV%0A-6X3JY;sy7$Bgd_AYb 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�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

9PVSZ;4G9lv)c#^?nh5%XS~Uys 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%0A{NBsyxkp)c z+#RcUNq~g}a^*1KS;ZG<(HnT>EB#=5c_|FoLn-~k*JR1}A_Om1*$YLZ%%FKV)^+TF z5kLl|PkNc58Bzk9#=)1IelkXX=T4C?MY{f`6$ zseBmXegIA9dIpd58MDqS^bC-)c72tAzHiN9ymi9k%L|d>Rr_aifI3E*Z~wvkJ;L+! zA^9QzX~P&#<^$zV8p7PJ9B>V@jnon6JcBMJAfy5Y4=sAW;q3dOjdIyRv< zEvk zPKBN+&pOtMd-ctL#1nUKf2ZB}NU+Afd~-tM;nRlQ&SpMRYB)P66t0oy(7Rl z1_^V`yQ{pfR{4DUG{``4a7{Q9a_ws_r;|w)jDj!O=@aREv6J^b3)8RgFdM2Z{_g|t zm1tlwIR0}ke8k$`R^4`mBC}a zRK||%au-%1(v7^z0D}rkl4Px@e4JWB8`{uU%j%WvhBc?t5jY-+RV~8*i7VmnhBNq5 zn5NfhdF7jE3nPGVP_r5V+ESM4Xrdyqr-si*06|HUdEXP87W?#ekxLLp9>j`F|vQKiI+)lkYH6gjE8S zKHjL86KBG-K%=)v(!{vs1iwSU*8>Ax`5R!J#oX*EI;zm-eXr30gpegleQzY|jJXxkUFzLSv&oR&TwlM}9wChF=oSzTheSkr$q zP;Gi$?Bzb4Ils_b%xP3+q`CXTR-|&4g-kJTmg96Q_lwRSMEkuz^V7G^^R}M#>$>Ij zfwUES)G4t6-*voO=38}?WL~1zuXPY>Z#~LK` zA+a_#_IqzHSqJ&ai(wHyII$5=LvlG6jG=aUED1dNSEZH6coI_Z*{0v#X;p6D(1pOk zi4e?VqV7+gN&(&tn_!O5u&rA1^#Ny@05leDx#j#q$m5AQRG$0}4+lbrq;POE#lp;W zDRBlyfgiWWF72AqMhe;B_`8wJ6`;k1SIk^*lF}~%v)mC0%BRf4RsEHTf*0*d$1*tyhRPY4Z}@*HezLayE!KKc`SFfC{D7vw z(NYoX8uQ`%?qy(hW_6sR*oQ8Y_H&8dOJ{cY2G`Z}X0W&m`|tx6ji&`JE;Ntc6KwFB z{9S>LqZsWeG!7C>(5M526TpmFrk!XiPnfJ2as=u{zozXPWb3M?6T$^mpSoAIz$nJ_ z)$^@4Zc-_s3DOOOoRSh8#|f+f{Nb>`Vka&hJf!-~xB3_t3h5EjEA4`}AOaII6n6^QxUcEiea+2VYJEEFj@%wchfiyr&Sk`cTP!~HFj25dC6_H1q*c@? z^H6>LWTLs#@ze6fuz4d+Hl@fe^U2S~1owh@Q87hW!3}Yx{)#q_-X^yCpRyU1>pBt1r*NrA0;P=#XHvcNu0b~Od#V?)b|j#QB7ZN&C8RsdmIUhQ@>yF@%4V( zCv{4JN!~7zhpzH8Z2}1IV?_F~37l+1m@?M(&#Po^1f(sN+wF|_ZBFLp;x`%dY@bWJ z6l?guZGOw|da=6n!yYgM?blPO2l5s#HP-)S)FKlY*;bY`y*B?PNm!F`+^67a0tk%D zGBKBj4`V$t#dv`=e{cEwS?Ayyt7QH@YJ2jZCoGn;ptt9JyL*!7@p|1W!jp&u21hn+#2buG3SD}BloqeZv)=`u^aR+U z!C4vS2}@-sxsNy!Mw8=0^zWPZza%WGyZTA;CfOeyE>GPhPPeU9hvf?46MycRrbWPb z&|o?TB8PsmVe3SFM)jkK#OVw!%#g0N;q7fVmVeqFhL@ky1=Tl)*gc33}7?7lf1l<{x;ZrnWE`A%y%4U=~Vc8a7XcuChHxt(RBY(brjCf!65l{Z1^T z3yvLy(*!*XOo3??o8rs+e1-}aB@Hib9II=hbNS7jyb`S)T-~!akGr=?^sU{$1>{jS z6oh!tlm(q*JFpr`{zm06>g!)H7c6^6=60B~HFmUZ9YXh3v|wD(;QOM}Ig`)caZ$bt zO@|=Vr*6Z6KFa8Z>ZkH}Fh)4O?mNs9Cz4g|Yq#2&*^pZ-Zh`lva{v0V);`NOL?Mxm z;)xHPe$|~NL4{7*0x(9KnRs90O!^PsluQ0Jd3ydq(bV8i2#*|>>&T)z?MOTgPVA=8DNAMPJw?S?&_Br%z!Iu9l;x(vh;H?q-+8%9w8;njvdT3|;rUPLQG z%e2wj0qlr&%>ezD%TNluyUD3!z8qvJ}KXEtmch&@yvs!x@Z5Qzo@7D zn@MtUeN~n4-E$TH)V^n^^2W@VNwE+oZ@qp91(<}^YWyRH7`%ok((l&9MVdVlC< zHLH#2Jxdl`a^^88H1D>e39hbpIr4)>-06;S5$ z@@**vZ~gKbVz1N zCo+a8be%`=vqyt(?J?)F&(`B}SzIgc-4<&W- z#n4;U97?jvCZO~khzhX-Lhq>+0FQ#uz;tNCjug3Z+;1m~S|-nCroYP<{U}~Js&8tL zcdQ?CH$=PC{1Ui&y}?-^t7V~nM z;o=Em4_l^YJ_g(Lu}{+9$)}XA(Df@QI_D)!t|Mu8+m&$PSwBCX%VdT(R4(^^u(o%m zPGdHrmwgpQp*!3|tLv_bVmuooFg5`ztI!J+j=baI62!XW59z+5(baO1>B>C+QDN?& z3t3iI{Iz;rM+1FkV`m%z&U^5Tm#}#yMbK&kn*LtkxqBD1XR?Y{d;`pz+RSoPd6W8< zRl*XtZ!3-oT0e8ilcf+G6)2eAe|x0YfFA%xliRq9PVVs5*=P|cksF_^h0QHwN=AEO ztD}V*%JZmJUXLcC0yxyg$`xI>&Q^l^COT%rtoH#&B=aoZ=`;-}8uwcRhl73<_kcNJ zpa~*ygHy$KI=K8W8{s^PJn#yHb(X*IcnYNL#|VuJKChSsH%JRbOE-51LABZMG_J>{ z5B^6|A22oYAlv_^mI-U;KW|);*dS&iCW=!2GwYa6I55CYShrB`NT4zEt;T>tPZz5x z@o6X~|M)_N`ybhERx_1XsA*nL*GI>{LTpG++`aQ4cx)2XbhICg-9FdDs}$_C!o!J@ z4&01cx`I{em9i~=JY|(#Qk$3sZ@LWyJtv8AAFrB_X&&JkD^ZiT$m3KRQiD%b1w>XlKf3)p z78Q-O@*@a-FKWYHoEKU6Ip3!-cgW?LSeaUJRycepN@qun@1pyBcdK3Ri^&73RNc_) z$%#rEgeqf78Vg8ToVHp@=U7BqxeUS>)aAn`mDR!B(ACJwXH0le(9dDNnRN|bL6M7n zR?^nCFC%s~(YTI$C(gQ6B=++q94x%aRUtjrYF1DF9lRMFyc}kL2eg`-`8cD#JQ82! zy>8_v6H*Xv-`}qralFd6wZ*LiMWPfdS=;Mc`ooFF%=gXBUa9oNt`Yiok&*;3Rt2z( zCZK6)1_Gd^TIOT}eEb4NCp<4{7Ho&+D!M|GizQ)Q;lQ zpTz0byOkJD(JKm_nHeBmlC3DkS%jzA&v`5IX}Bh}Sh)-BvFirCMK92Kk#KH+lj**N zJ&KZCT~O)OE_GmDp+!u4ZPm8osO42RmyBwK(h3qEXhq&ke0taV#P;277;PvZz~#*+ zTVDSP5;4Q`clqQp*Wqq$(0Vzx9+qW#GONB0VR);5l(lv9RlWzwC-y?;pqbU0yhxCB zpvQ{|m^vB+m}bJ;ibaa4O&Rwm2b`$fsfUCd>J|KgKWQqP+V5@qStY~~ z!F6uRx#i`c@sV<0(U%CUbRydCVN*g6TTrrRzYW4H%$w@TD-E9a)KT|uk$4s1&4`ru zt|XRx-PgTt!yOW$;))knd|w<9v`O0ArzCT9eHXOk4t3w8zZp-av#KLsVGdsa3F~Nl zW`wl%dY}~H61z@&t9V}S1cC`bv4LWwwE4l;?SBF;DLi#wS!8vcAL*@sxOU7RHmnOYd3@~`+yrdP#)NKeX|tx5)4kKer;MIK zUwxZUfa0{>2M-3?EV>rp2ej*;yQaJLeNuX zM7`D$<_xO%;(^5A`jpmXGu{CSylIy~ZOZGzsBga{Bsk|l0@bC}-~S_7%LUbo_CP|o zYm1VtG0>Py$QOd(-9#x=aOICoV;C5C%g27_{pv^IvR@pTL_J3>nS4Hy*!4Z9I59)# zg?c4~|3^X(Q(Zss%TG4{b0!$W`Bnom8gG`q`GJSxuwwo}HV4<#a^n3-z1>1=!oDC3 z5QSrcuiiL3LQh4+L#TEp+3%p1#h<;0xOGw@XpelVzM#&HuM@qXWGlYezSZZIaLS$h zebdo0WiHd}m{+aP7U7=Y^SGs;zc$C>9f=TH2ez=@hJ$j`I^ijqdu=UBg@(6i6$AHe zxWph;pnC`?`oO7!ttg>rZ89H~J=a85!pl_k7PZZpjK`8gC)@)A;)h>63?7 z&p)y>KKfeV*b$eGsX6`vlevy`xs|&b4X;n#9+nAEq_wJu0PO*o&bl=j_itU)N);Zw zmbMQ;TiAmqU5KJV6KVdTvos8;jmW#gHn}_S?ShZVVg@<`Bofakf+4K)lETjII>`gJ z>=Hv^J4;^M)t>|b@rVx@4fPa_CW_R=nU1N>Aoyk$Mq6m3*t?r7nHrhT!`bJgZfR2U zE~g8pi>G+C*jkkg4+*?p%ejz6E*z~leJ0WoOo#(`k1G6?<%D%zyi*1OvL3vC{gO3f zk=>=c#y2{w`Qfv;WF zFg404xFZ5%7HZ`IzLm&1Ew!xGMHC6-H=p*bR56<%ytzD>$?&h~%GXFW;ZZY)e?r}E zjn1K9eg+#i8&JeD7bG_64D6hsaQQdMKvSFp#P|2Xua_G4>))Aa*nCnV*Qft; z$TAXiJ80Ag)4f_>-JLbF$~{kj6b&77y^x6{<<1EJexWoXWlSZ!^EI$a0umU&P`AGAfd1$GU z(2N0N4#Lc7?*$GLUbTe1#e%vIREYQ5sMPQ6v3VD}!KJ=Nn-n|AG}LEs zre{k&{}=oHtG?0cL&!)TfrIQfk;??9Wp2>Y+%R!)x@dB%#6)_y(e~$wIA^@fAn^E=ti^Ia&2ipeS;X>SIqxX$6 z+rNU{FeUV|ZNQWaC^*|fr^WtMILTqiXeNHJpJsjS#} zTi4@`=pxd#pSpwenwf4W#W!^Q0F3 zg6;&-;(g8?@b(xew*!)mZ4Yf&U1?gs!aOT^<)p;bHHy9+G$HI-xtZ#4bCT&e-}8Rx z1M%eKY5dSRd>n2Q+6<1?^k<5nGKWZ!7anGrcYrIukrlF*rGc)cTTHW}?p>W=wK^XzI$@ zzaRAJH{^}`@`XjCVF16k_P;g|3c}0r(>5Rzp&Uo@_K5RrGa?Lns^y{_xcDKeePKj0 zY+y*dhf(^o7IXjU7bi>ZKdjK3p{9Sq2*P`=C)8;JmKVCG_S7voMrZyp& zI>(%;a*ro-y?1lEu@d?? zF7Ai+kk2pms5LKzrXB(eAu|%MK)kJu5hWo`<^2kez#ek5@0!{tv@^`JiXuSe{r9yb zoltoP(qpXZS#NzeKN3o1Zds9iRbyWYdIS{M&vq<(pLDlVjUUysGbQHf{+c|a+EaS8 ztLc6Wo$UF!_=z{Q$4pC1P7ae>Eij$0u{$k6wddglysX18t zrI{dy2{C7m0ok;voLAu*5_+`8$nYVie=5CVXqYZ-J*7Wg|9qa<%UkmI>bafWe3HBk z+qD{Q`-|j(kHz>}9k8tb)wghHmJL*XtXUn6EE{IJCCiO)eqt45u=GiT(WzUReu6+l zo#gAJH9?T^@_(txmtfa8#$)@lT@9=*4@y%BQpCV=Ge>0w}Mk|_N&u7m?CSaU} zt3ZMiH=P$PQ#YE^b?@CD%&+?K%Ab>VA^Xy4H_-OYhPi7kUkgm`m9tE@Ue5!2^wo7G zfgW8=_7{GWAdOt$5}tsmFh>HA6x7{r^7M}GibnqA^c*y42hq*@S$dB6PSff+q08)3 zsI-}Y_#M=n>}_tyeA!J>(!HPQTDs0dF_)NZxacLCMZXH;+2}>Xdi5V!^7IY z%v!GFAKbKU9mM1qM2(}#t^DC=WNKMi^xD1wS?@k+R&&#SS;MN(>|qGq3l`MQ?7kUb zdM+iH1HjmN4#*N8TwzR*n1uc`(Otl??djQkNNinZHB<(LEj{bUFrIZ?^0g=*)@y|Rq;f`2&HlZki(N<6g z{9+OgGD!~_XJ8;V(Hzd4{s)0S`&_9OhAt3A*Bi7cLr-t%jr1uIHYqaat|>@qd!)H}woao2}kDI9}2{Vt89ZVlHl-Yf5)TRx8Eu+CIRh%qlxMs7Xe zsD@eR`RBdV)1)}hdS>3gae6ZwC>L=~6J>RNJ?u8CBw&;`k{Dz>#NqYYIj#CG3@MzE zppEp7x6_LLYiRb5o@m++zFK0VAq>u+@N%5;WnacrW6M28-vxb}o#L<|9)!Q>TWR2H za$sE4%X;V|zAcA(tvt)8?7&TR5s!(^sJ1~3ZJuGMvURmehsB#CoILV`Y~DR=bss5P zRfkf!Bsm`l^G%!k=T7ge)*dAwB(RV^f566~Q#6MUG5kYN@FddwLN;C*X2?WxqN7*L zO0qV@%sMXUe>Ojv=wuq{HsnT#P!n4iwaL8b$zWj;gBBymXdSYolE8Uuu z3n%ibg!g&e<}0#@r2-gXW;UsMNL;*>Th{r2ztCN0wDxVyp+LplO&d}o4|WXs#xs|i zU2-vDC2*eMee8Mhlht?M#@<|K!#s)zT+`KxFG?W3m#aNY)%P8q`PxGgR5a-h>Dhnh ziD<&wib)?m?~&kKTVp{wR>xcxA5h@x3~~P(N&&%YzQ4MJ5K^p7zdkU$xEbNLDl|-P zX(B#6uZ$TW7&gzc92zYA_{{q?0B15&k>+QZH?)xPTtBCKA@!0%Lf&@bDB5-D<*~c% zUi78bnol2SA31jjAs;A*@ia|~G$a;MDOoAKT{UkTe~gIUI-WeJf&c7wk}Qec1x=W! z-2BkRolSTK#0lQMu5u${qZE&mUOadZd+5xi6!`nRH13aw#4N}$ zfqfNbd0k8hMB7cfcv)9Ec`QBNj0?^5Dx9ExB3*h_klu@Qq9`CW(gg&R zju4O%I)oaUh}2Mo&_N)OPy>YAckX-Zx7Pb>Rg)6Slmz4!NhE;B-%Vf^XttSyn_ ztudE7YKG;yn->!v2k3cdrWm@d`)6rmFgJi{ zJDFX78tvSc2%`32x{5k&VWp%`O32D)NxI+F*YWOmfIk|JF?L9>7X~2tt1M^UHfsb} z8O~*D4}tiX=n0ku%k@KgZ3SiC?bt9b!hPm7%(v94WqJ2v?I_i|!5Ie-V-`@WS^o%BXzTR$O|a{BMTV?UoYn>3#B@LMENYxc z#Cva~$Y1oyD@3zn#^gE|QSsB(ZDBq_sq+FcGKg2LFGZ_|p#z%7P(*YjAOz@lwbdY- z!Y>UMwh?(^n(ApY&;WydOn=CKl7-->o_?Qk#s?Mn8fbY%3jnvuL&3&an)r2Yvb{p$ zxhAEunIQIR2o=ssRd#yC>Sqm-elcO^vhw_NI z@^_VgJVIKSE~GC%jZsSd>D+0#WGOK}jnBPw<;?*DB!!vjb6j`WAg%?bvb1x}h$dKAM?9}Px68mtw6gl+!goL+I^x|qA3 zf9d57eGZbCdP?%G`Rya33611q^@4qd(1t53`J1n+UpP{4T1x`jAdhj}&;^{uhGA^5 zzUd9~^XW64@{WS^lRGTKUinHd+j^kNyC49P}_tyIM!-TtMAwMNxh4Qynlb}`yp5fcKb;65;RNpAd{|9 z2*xEn2WqrtSxYs9NwADM-z;eK*tK3S5M(Bd5$@G7m1en2rg6&?`h5kQ-`bL?Ye@ZD z0c$^D$g-k)d=yUr_E3=ZUL-AaEVx{B43>{(S^D8u>Tk8Dn?sYH*f}mV>K`MroRaLe z)EvB#y@Z>r@c`&A4pG6*gU(C)k49v|l6YklR!Bv(cXVujUN4hkqd4 z+C#C52k-mBRzkmX8d@;hb9bj7uG(3-Ph0)rSJtrRtNHcX$gW=OU5SX{Fo^ZkhFvm= z@Tk6Jt{lq<&4yRn^g?xcT;@prv?o>cf>c|@{QL$JI9{~_umY~rB8pJ*CKS#BM18j9 zY8(~(9sZH8b*4w`sjYZn85#Xos>eSyNdMHUhph9_R9CO)Q&~rqbq)S(5GZI=0>qP& zZkNzY`Lnrt#0B&|CjXo~m!36Qk{w?c4IJRfnm~y~_ak2{4#k}ra@vMU_O);cOhrDZ zu-A1BTpt_uaZFwktB0s~8S4s+)sVZq{(yct^9I~9^wAr*lTN(1C&n#QyD6-YV<8_U zeej&>smG{4?pMpHIli=M-xt&l|IV8Jy5{dK=-21MjG}oV_i&;cx1%{iul13&JdLw7 zdC!{>Hv3B&z5!AV!RfzrDo&U`$q%>jb6|)MK%R!FSAa)qT`$V%Al!1M#8ARcXV3cl zTjd|g2QTyNOb!FLV)Tev9CGso-VJh$_9tgSsNHX|$^99%QRKb}zmS|a?9En}0rKo^ z|LhX@v85~AnAg9`Xk%pywAP0R^s&tMY7&Cx4jM zl6gGvNX#tS*ORr_`x-Jfv4Q|kOm+32r{I9_X_LT87C59F7h;I4;Y(kM0*DKc!J^Qo zyiasWl?%3h4L;}WHlOHTphyn0{iIDhiliIv|0+ytEl}OgEbVnwvIb;~0N0GbRkdS4 z7jyz)1EJTAkSib*)a|`##99CKeRQrY`^Lbjr88->)hQ@ll-YcqIpYM37RUVUB)xtb zy7465x*bdJc7>j8ofQB}d#-XeihZD0{pB~{XDP3^-KBCKbORNpFHqB<&<9EDvLp^ z=t}T5m@CnhX1JIe&kz4x4loEbdnlNgnW}&4o@~HArpYfV8!!C!?3EgYsY9`H&U>#T zkej#Dn$0ekNb0^B=IP@irgf}O<<(qudHLsLU#l|kkBMSw#HY{Za-s|##YU?;tL|Uj zoo`0< z*bSS+3smnR!`0tc3+7aY<}Exh1R10(Smh}_a1Ap7vOI(fkmL%qqS?p@=Io7Mi`|xD z2H}u8LgX)4E(KDz8N&FSUm1={2hIdn zre55JbpVu0u!(rQ`!!!EXt0nP0PV;3so$SgwTk#=gOj*6&3G}*lc)EhRpBaX;A8p^ zkr<_tOcg%qPd$oHR*(NBDzheE{66u;_455>RHFA5$wSaiSAn8 zKf2#M)GPI^HAYe(gnB?Qgg(&Tw@bhaA$fAM#_w2p6`f@= zyG&LRSm0JlaWM+*P#XDl5bb)5|L!&u({OuE9u(D#_PQ?YL_P`r=I%v!6`DAw|5cv9 zq4oVS)mK860Vn-(ZCE#~|7D4H-N_CzRqDtDVByWp*}1eKBW=niUD{KWJH5cuTbv8c z!hI)}ih2mQ8e3*SKMv}O=Q{N(t;>%N@+sY!ynpU|2z&icg7(UoJjJL#Ku*_yJc!z; zTRhr0Ps41it)$(wt*2N&Hk-+5oCaKdsmEJ0oS!P}!<>#Qg%Sx>pw%oC=D7m};Qo?? zyMB!1Te}1`t7?8%?7&LEWuFMm9=;`ghkx`{z*zbz#6-8^u6Lu3cGc-tlY?ulY4-fL zF!h+k^u)hgvUi1xEB{9&^?ytFXKtLR17d%ioj^%GS$7ijN(k$0ZqT)y1d2DOsi z%@WiYb{U9$)-#=e}6Q1H-rN*q0&MmH^0`HF9*FFRd} z9o)I^{VU*u|8}Zhf2s=7d~Ql3gnulUx-+C`e}lebc!NAtYb}vvfv=LKrlU(eM9(Ht z7W2bRu;uk==j&&m0wJHHj-KAxuFgy~4~A0#TZ1@74?uyU4ynQO0A5;ey|tSyH0pzI za6$Oh+~TRd9%rvYb6QG6jm$sEfW8=1YB{?X0YaVZuZ-LcBCPHE6 zSbhR`=Oj$;AgYSqCfHx1Kt&7s*HZg#Dz zlkRKIN*_6Z>1o#C6mit_ExRTWoU)U`yth_Xb&Z9hx`QUu}cNNLxOKOgKrAs_w$m7Nq zIG*5{PF8)(G#pte4YKajvm5XN5Y$xu77cq|MQ`cU*AJk z+oHkcXD{Lu(N4<@Dps2xg^6`Q!W$W>opV%3`lh?my&+sO$m65J3Q|>b)5Od$%gaTO zKMVqBNFI#)2Q_e~s#9Ba|I8lD7H#e;e^Yd(8mxo}bWi~IJ_fJW+j9a~duSvG!#p_G z&4ZP1BP;%wXprgND8NOsJYvdO_c_u2*tmlpJ)omI;xx1^u-s3V^~cHfVPsJ8IU~ft zosx5`SE=WZf9g&~YLC-OeJU4Sh|ZK5`}|;g{1fkl!B21+E zdW2HNX&a$97~Kl){SNwZ3xciQFzCW24b1s@Fu`d%Gbhp8#GgC@tH5gUHAb=Xpx9FJ z87ji#u0}C(V^Bmg(K4JX>-q@r-$dcHCnciq;LS0AEa-;nz#a=85Vz1k;+Esc$vxEp zktoH($5ku3GL{F?WA7jaUH)?g`lxZmQ@omJWyu;hPH|{mA_oJg7aMMj**WwpgA?Fu zBI&57O?q=nw+aH~pV|8W(!Z?EqVHQJU(MpH4+FEk8h)^f?65%lZ|Ht^yuneqXZI_$9&Mm=Od+!q&Uxtuk z&!Nl`V~YO4KQ$Mut(V`&wxtpNy^B%1I@Y25W*OlKs#waP>YT;M#eb$s&(Tzv3>w5W zrd752;SP4ts5O7fdyGpS4U$mgn6s@hHz$^^wB_!akyJGm>?LoZLvelntJyTXTOH@Z z%0ojg`e|Xf^o5q{^8!|@$HVGbltb;Htc0bj@R=MkkrtJAMMf=5Rh4SkbaF4$ZR_AUUr@<{@8j~m{CgtMFm705L?=9i?n!C{5GpmE| zd-sp*C}JgFHqBkwS;IC15RmE1=R=H#%jyAE9cYkD1lYq`E|pOTx892F!6}KXx0p)ABjLjS-?zpuYk2&vCzNS>s7ns{@YaNs#_8}0gOk!P@<4*XO~D~vyd1V&^=-f1yVJ)+S2E~w zEtQ^zMLqfuv^c20Jd!fmkNSJ1p0KfRdCK{0$8su**SR%sercqucvSZR|O zXN`T%Wf0e)Ju2XkuRiN8>&-QK<|FQGMcpRWK;Pq z6R83et0fzGfh&s_)0Am6A=r1J|*HH1Pz=?;iIlQD7kFh1EP(G8B^AxXof71 zrE&X4gprnO*<%|$e&@>!vFF%-{J-4cAfI^UIbGL%*TIwaJOTj2pf?Zs z9fY%Z(vH(Q7TdgSdf1|32R^~(c3NI>JGWVOIHnVGYZ0Zc8c{xt?{AZr19Eb*ngz91 zBhJ46OGMuccGpM-S6z1Pmt%%vy-#uF5h3R8tWJ)HJwqACo8F2P6m`+cvjyO_eh&po zxy`xevisa>^O`&%s?jX_=qJX9VTbu;OJiSGCIn{cQ5v$MzCKRFZ69h11CP)@YC<_L zik|(A=Kcq%Y%2K_c~_<|biK%T)kc?B^ZG?^=Gn-3hxtE~1#94)bTrFF@q^~%Jyl{wHt`erRm9_O;jUY?HiHgr$V2%<^W~JUCsXYZ zvToV2-HXURGu31&q9^|nsh^GG`WQ28a*RuYRNJckEk{(#k@Y3QdDaYh*dhM5Q%9_TA;nyH7$X2Eny9GtwEQD4u*(UN-GK3Wf%5+o4A&?mP~zydilAO*#=hH zSGUQytv_e5G&uPNmwSr)hH=hz&87@x`ICL093icKnKpA=bDQV=?-*rd@Om2Dp!-1s zqps%Pzqa6yc-`-zsKLc-7}SHAExR6YD1pIY0oU6dtyvc%q8S$jpX>Pp)1$Ly_owCN zvgBDU?qy8-Bb0`@=MreFIkXZ=_o9Vo^$W9JM^)LftT>T3>GQ|QYVbAxmVp^tB_N1Q z^er(kL~y?Ra+|f0azRDWaabi@4eG(gI1@|A0S8yCXw|We`%0xgu8mW$P4ydoRyZKV zQu@2(VbxQ3#KjmQlzXS#Q(sJ|9*O%bmdA-4gJssvnJ;RPj$B#LJ!tpT%J-Tek&cTR z^2qq&ii%M>6m7v8Szyk_L>dF`)+%x!X}H!!!yepM?w2wT?Ny$a-D_xgFVl)3&<+iuJw7>!4#Zc0RbM&CL(d^e(Y~UzOe0ebF~zN1h+)yLv2DQepa`FHEf4+K@02e zf11bIw%7R$L3Tw;aZV4|I>&xSKv{OK0jyi1!SdZ?uQO1EAx_TXKUr2$U|$)Xiu5nr zeIrkhz&4kJ=N(pUZLb5O0Q`w8W5IOAj^rG!{KtU~6=cq5b-#{qa^H8xfs3MgsGJt+ z&%Sa4#~WUBBYyw%S-bmEj)r4#qR89-Nre5cq&3JrKdajU5$>{vQKRsT;Ek>A1txdZ zu*$l;ibLL|#kT{=?}jM~zl6VYn0+6#QwCNCV=O1;7is60&S!Bcb)nV$ghyEBeo*QZ zxKFbfSJ8i71 zoJ%h*ZyCB-=>8>_3neiv;djTL)ry&qjA;4ExKgK5aJBp5%p%8|4DGcPm1|Seh%n9e zg@xRbW*Ky(lzLG8LnGFYN=r>r>{obeHqW~))@K;=oblMA{tLeqmR}?_n)lvseYrWm zjNh_F4){`9E^4qL7E6(ZUM+9xo6}?ib4~Ab^X{5W8ROn6QK%YzQ|36#Stsvq_7-GJ zw@FZK^`5eNuuS>wGOOSnyN$mQg+{8Ulle8*JFhvU0#d3;L1iP=;-pp_$O-b#7rVXV zXG^sj7*@O+s9QYf){Kq}%B9QwkrHP-_0OxvFMIFaLFm*_f=E2sE?e94 z^{}KW4+H($gBnFg`vXeuDM~D`?zS;xr*9yW5Jx2sx^8Pj~X*+mD1Z~WH?T$6) zU2%I2ZkK9Gl^GAh;vdZNcc!Z~+Wf^VYxstRg}BSLKiN?h)#1Bqmo0w1@WT1Kyi$)+ zND&n*h0l6cc0ue&V#%=0(^wALs0RBrw6v}HiKQBn8z8N1M9UB;9p?N}n`yH3(z}Bj z%=4k)D{FPz`5inM+c3%4EM}W_ zT&v^hqwj$ng`ER>Ky>Dpgl5*KsX&Ob4yoReHPdKUGlZ8_)HOL3Kag#ZP#EFX# z`b#>Q(eDkT@<2!yl1~MFpGxa7$xY=v?d`+1W3(h*5*&Mex9>252 zajXRO>=IfRtb^LwX|E;ibHl_O(G7q8Nh_a_vx1EACEx1l<#RZMY7LpuO6?fi9A?uF@1(8n>LQ-O0Z~A4ExDJ~AzqnR#ouN?}1JKgZ0FVP^V7SOT zF=Wr=3ky?sW_~m6bL`6#Rc`r(rgG^C=WOPq9l7mra1WTplbU~Qt>~VuD81@T^HTQ3 z2zS;UjK*NL=0<)WfhTV5*`);Ubtk@`bw^E9f&Jix;cmyOFP@_Wa0#r}#lCFI*PVXZ z?Wj5iMwhjOrYcdT*M^_+q#cUv48n0r)2rIOC<+JtD9;*?6~R;?`EUt{tO5evqY^0W z$Y(cCp3lOux(@%6t4@ojJtK*DfykZO;wvD z=8hV{$IT>f&v+NrLW_p4LvX^9XvSK+0rqUyTkFz%NPh}ikgP2JX}#7Cl%BEE7Q(EaEo)$&Bj5=^s~HM*fpf_Xr1-8yInk4DZ5aos@I@|*P7+uF zAKgQcyJ52Z{r3Z0%s{LG8llr!tfqhKi>yFvv5Ob{|>IJqQ%o$oHwf*`@RLyOE1u<4{@*IgHix{ zY;pso-^vBMq7tb5hpzZT|0VjpyY*_zbzHeNZ-u+VCakn2|H56udy`&Ep?2+r27vy} zzG(mI)$l#zY&;5Zg#h<4SZRbjVFEBZX8c6n5GWGY-K;Es#s_Wl)e2qF9=dX}6|24+!rgpZ2@LIfo332z-tqUTuCvH3tk2_AonYJla3yQ zy#j`IH31Hr8vYJ%oER|39a>mNLUx31H_xwrU|0CZ%cJX$ zsbK6C(A^k-kfc{n0hnaglOAr@B))%%c+Kq2xjRFPdK^*~kq3XVdi~uJ7(5$INz@vy z{G;W5Gi8)hm4k~io17pUZslb&ayVd= z?tx2w(nQ<+MD5;ygO$<^vqa~UD*I*a zJ8{Ar?d>Fb5wA^#Mob2C6I^+Sq`)pmi_(TM^48~&+EC_xE@eznV%|8+lk&O)s!jkk zj!ubM?J?D6*eHo@h;T@C_{p+d;?Abz>6TePqY-hct?tH@Q|6?^W zo#OoER3|_>g_{k&eu6i&3y`GR<)WFc@$x<$qn3OgcBq;kYQ!sxH0>}9lp#JD6Qy4! zGCoE&gYa}Y=gpxLcqi0AZTS1wT%c3sI^aUs%MSNIi%faO-xR3}h56Syumv6yb3WO*CQr#OcGE_u(^2d zZxsmcl?1!%)aohp7$#OE3$w9f_gB(@m7iDC^wuzc)nd?qMjk>tHpua-e+FZtqAPaf zEwJS2*POxsP4KO*ui%2h#}%30kC?k0$^J(UhNx(iJv-9(W#^1KYiI*|6`yFJcS)q< zoLA84>Ra)zp=E9i_xo7+jqb4QYJ=Q?7jO8cq#~^o^$x`72zhIF%K8S#0!-Z?+{wMx zvYFt4tZEOsTK*zUA+ePz$xX7!B&bKvE-DN{J_z+)`My2Vmp=5|^+|1n;`WWO{O9$9 z>AWqeYxIhCsf=61Nj!@r@9je{l0x4`wT14C=1Q9eRG3H=?VoP)0BM_ir8>*I!A&}2D=^atyQZHeg zCMXjCjU(kf8LS<@+_PK zB~i2Uk=)U>xqftLnp)MAHfZXL6QV^ks!%w*1%|_tB$SII`y22bGy1mkzeMTxu{Hr` z4mfQ={FUi;)JLRqm}KhNZshBY0w?p9DKA+SZ|f_)`JknH2&BHntG?Swk%P>N8Jr0S3@g|XK3WlSf!T&9BhMpz|B7z8qA9(#sKK|hz-zeU0L!ZJN zDOrIzdXMZ^j+*9Aa&G^F9HCk1(=`p_d_1D+KmKYN7sI{;{XKtEs2aKAMF`C1JT?nG zAB=FotB9U|@JxYYzIBU7!lg+2Pogsc(vKMD=-(eVTYA7rG~X4?F{cX?Ft+*;nLck- zGf##+A6b}ZWR}}{(U=Qph3dr#I@vrw^yta)re%{|xY{>OI9`8M-Ql7IT#~%gEPuV- z1<)X^envG3qAqzHnB-T_fECJ?0AKy=&_wKe$pdC$4BsKpjkHfzhL;QkJJ4A!d&tO@ z7x6RIpgL@-%&q)WLmIxvjhqwF%zA2L4RWYLxP7L51*Nr$A9x0Gc(8;-VVl=Zm+XOE zhi2^NE59Fj?J3?;#Snxwcz#RdztMEYM_+>12k_>1JGUZL6mZ6smo}<#8-t5{HX{8_ zbx)Uw$dA0YOtimWKHk8NyMp@+8hDcXXeP7Bgoj!T&i1+=+`J{ohMJ!_&Ztx2O<pYp4hAH*(`?`y775h2FvHLybP4}DmiA{YX;ufqD4}Zmbi7=cPPfv*f^(`YYT|A zQ$9I2aIX}|?O_f<2Q-*`1)?BISeE7}fJ&|H&abOw|eE3RwVn@xH#tE2m z;E`IYGO`YW6F{M9?*I3{I0xgk1$Sn`orr4<1%S-*uF(3BdN)^IktmJD47THl&U%>D z8-0jxCZY=%-Z^Eo^}LFFY3{A8dmp5bPG@-|EoHw9jBe{qF`qXIQ%n#OsRJp*mu-K` zA>2v2eo7Jve^qbvhtj)-xh;)NA+`dMzfaN?wL<7w=ebv zd2@eY7K<~CL=y#HA!`?bokbGt_Oa9sPSPzFxl+iPS33k)TEtCpNd^e>jUa+AX^Gz& z=co}E3~gBu$J|M=IIf$N#68Ht)0y@GTcjDB60b80^W_dr>Xiv)H*WfoN-ctz3vRzZ za4hA-oBqg@$YLxF+$#%m8wX}5mKJMe7RHYYw-f^E>hIUrH@dk8ia6?5as0q2gLPg7Y0s+`;1PS5-gqkz*p8S>^Gz(XepKWMAK zOH2Y$_Rw+!;%X1TL~9tw30I`;L>TXBvE@dYZ%eXbAAE6>}g7 zOqpp`(VqKnQP<4XmC=6|jN}&z*Bs48ReG;akY$5=1vml!*FKWv3jF<|Rqw3#Pl37Q zova^Y6&pwfGmTUR)d7+pJy51~T_^x<*SFUA!frR(w%;W608AuYoce+sppYfI!U;BL zOX5YjDH^petZEoGFcr4ZJs@%)NhWu7R-9(JaY|pW1>`x6aPjB+W7l`^?5&#S0Ik$b z^>1n=BJMKY=w2Z9X(AsR&5Bc?o&mcGoeVM_ZXmSSNUg$An*v-=Qf&*ACnS>P@Fhy68_e1gr|T9c5=|`uAlV7L+aLH2FweKZzx0WA0A!t8 z@ms2*V>eP#99&r#@V=Um_W&GglbS>icaSH=iNZu*1NU)xfYcXt1^z$J7>EhK+j9ym zt=n4KzV?|F0EPUSu_loMuvX7Z>l*>eZvZeAR5~w%2W2rBkSJ$QtsGNLgDmzh_nDNaN&|XPQv}~sF<2yf0h%^2k0peujq8Uet cDGwG}4p;AA0-&uOB+zgA$^X{?;(r(Z549sjApigX literal 0 HcmV?d00001 From 16be0b9abb717be1fc8d850779a6d2c7f261d84b Mon Sep 17 00:00:00 2001 From: Bodmer Date: Sun, 11 Mar 2018 00:12:42 +0000 Subject: [PATCH 093/150] Revert to defaults --- Tools/Create_Smooth_Font/Create_font/Create_font.pde | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Tools/Create_Smooth_Font/Create_font/Create_font.pde b/Tools/Create_Smooth_Font/Create_font/Create_font.pde index fbd6b91..1363b6b 100644 --- a/Tools/Create_Smooth_Font/Create_font/Create_font.pde +++ b/Tools/Create_Smooth_Font/Create_font/Create_font.pde @@ -109,7 +109,7 @@ String fontType = ".ttf"; //SPIFFS does not accept underscore in file //String fontType = ".otf"; // Use font number instead of name, -1 means use name above, or a value >=0 means use system font number from list. -int fontNumber = 22; // << Use [Number] in brackets from the fonts listed in console window +int fontNumber = -1; // << Use [Number] in brackets from the fonts listed in console window // Define the font size in points for the created font file int fontSize = 28; @@ -312,7 +312,7 @@ static final int[] specificUnicodes = { //*/ // More characters, change next line to //* to use - //* + /* 0x0102, 0x0103, 0x0104, 0x0105, 0x0106, 0x0107, 0x010C, 0x010D, 0x010E, 0x010F, 0x0110, 0x0111, 0x0118, 0x0119, 0x011A, 0x011B, From 076d5f6f055fc9d37e3d049d839196dce651819c Mon Sep 17 00:00:00 2001 From: Bodmer Date: Sun, 11 Mar 2018 14:09:27 +0000 Subject: [PATCH 094/150] Add missing setup files Thanks to Phaedra on the Arduino forum for pointing this out. --- User_Setups/Setup13_ILI9481_Parallel.h | 90 +++++++++++ User_Setups/Setup14_ILI9341_Parallel.h | 91 +++++++++++ User_Setups/Setup15_HX8357D.h | 214 +++++++++++++++++++++++++ User_Setups/Setup16_ILI9488_Parallel.h | 90 +++++++++++ 4 files changed, 485 insertions(+) create mode 100644 User_Setups/Setup13_ILI9481_Parallel.h create mode 100644 User_Setups/Setup14_ILI9341_Parallel.h create mode 100644 User_Setups/Setup15_HX8357D.h create mode 100644 User_Setups/Setup16_ILI9488_Parallel.h diff --git a/User_Setups/Setup13_ILI9481_Parallel.h b/User_Setups/Setup13_ILI9481_Parallel.h new file mode 100644 index 0000000..e91ccdb --- /dev/null +++ b/User_Setups/Setup13_ILI9481_Parallel.h @@ -0,0 +1,90 @@ +// USER DEFINED SETTINGS +// Set driver type, fonts to be loaded, pins used and SPI control method etc +// +// See the User_Setup_Select.h file if you wish to be able to define multiple +// setups and then easily select which setup file is used by the compiler. +// +// If this file is edited correctly then all the library example sketches should +// run without the need to make any more changes for a particular hardware setup! + +// ################################################################################## +// +// Section 0. Call up the right driver file and any options for it +// +// ################################################################################## + +// Parallel bus is only supported on ESP32 +// Use ESP32 Parallel interface instead of SPI +#define ESP32_PARALLEL + +// Only define one driver, the other ones must be commented out +#define ILI9481_DRIVER + +// ################################################################################## +// +// Section 1. Define the pins that are used to interface with the display here +// +// ################################################################################## + +// ESP32 pins used +#define TFT_CS 33 // Chip select control pin +#define TFT_DC 15 // Data Command control pin - use a pin in the range 0-31 +#define TFT_RST 32 // Reset pin + +#define TFT_WR 4 // Write strobe control pin - use a pin in the range 0-31 +#define TFT_RD 2 + +#define TFT_D0 12 // Must use pins in the range 0-31 for the data bus +#define TFT_D1 13 // so a single register write sets/clears all bits +#define TFT_D2 26 +#define TFT_D3 25 +#define TFT_D4 17 +#define TFT_D5 16 +#define TFT_D6 27 +#define TFT_D7 14 + + +// ################################################################################## +// +// Section 2. Define the way the DC and/or CS lines are driven (ESP8266 only) +// +// ################################################################################## + + +// ################################################################################## +// +// Section 3. Define the fonts that are to be used here +// +// ################################################################################## + +// Comment out the #defines below with // to stop that font being loaded +// The ESP8366 and ESP32 have plenty of memory so commenting out fonts is not +// normally necessary. If all fonts are loaded the extra FLASH space required is +// about 17Kbytes. To save FLASH space only enable the fonts you need! + +#define LOAD_GLCD // Font 1. Original Adafruit 8 pixel font needs ~1820 bytes in FLASH +#define LOAD_FONT2 // Font 2. Small 16 pixel high font, needs ~3534 bytes in FLASH, 96 characters +#define LOAD_FONT4 // Font 4. Medium 26 pixel high font, needs ~5848 bytes in FLASH, 96 characters +#define LOAD_FONT6 // Font 6. Large 48 pixel font, needs ~2666 bytes in FLASH, only characters 1234567890:-.apm +#define LOAD_FONT7 // Font 7. 7 segment 48 pixel font, needs ~2438 bytes in FLASH, only characters 1234567890:. +#define LOAD_FONT8 // Font 8. Large 75 pixel font needs ~3256 bytes in FLASH, only characters 1234567890:-. +#define LOAD_GFXFF // FreeFonts. Include access to the 48 Adafruit_GFX free fonts FF1 to FF48 and custom fonts + +// Comment out the #define below to stop the SPIFFS filing system and smooth font code being loaded +// this will save ~20kbytes of FLASH +#define SMOOTH_FONT + +// ################################################################################## +// +// Section 4. Not used +// +// ################################################################################## + + +// ################################################################################## +// +// Section 5. Other options +// +// ################################################################################## + + diff --git a/User_Setups/Setup14_ILI9341_Parallel.h b/User_Setups/Setup14_ILI9341_Parallel.h new file mode 100644 index 0000000..5e5fc8e --- /dev/null +++ b/User_Setups/Setup14_ILI9341_Parallel.h @@ -0,0 +1,91 @@ +// USER DEFINED SETTINGS +// Set driver type, fonts to be loaded, pins used and SPI control method etc +// +// See the User_Setup_Select.h file if you wish to be able to define multiple +// setups and then easily select which setup file is used by the compiler. +// +// If this file is edited correctly then all the library example sketches should +// run without the need to make any more changes for a particular hardware setup! + +// ################################################################################## +// +// Section 0. Call up the right driver file and any options for it +// +// ################################################################################## + +// Parallel bus is only supported on ESP32 +// Use ESP32 Parallel interface instead of SPI +#define ESP32_PARALLEL + +// Only define one driver, the other ones must be commented out +#define ILI9341_DRIVER + +// ################################################################################## +// +// Section 1. Define the pins that are used to interface with the display here +// +// ################################################################################## + +// ESP32 pins used for the parallel interface TFT +#define TFT_CS 33 // Chip select control pin +#define TFT_DC 15 // Data Command control pin - use a pin in the range 0-31 +#define TFT_RST 32 // Reset pin + +#define TFT_WR 4 // Write strobe control pin - use a pin in the range 0-31 +#define TFT_RD 2 + +#define TFT_D0 12 // Must use pins in the range 0-31 for the data bus +#define TFT_D1 13 // so a single register write sets/clears all bits +#define TFT_D2 26 +#define TFT_D3 25 +#define TFT_D4 17 +#define TFT_D5 16 +#define TFT_D6 27 +#define TFT_D7 14 + + +// ################################################################################## +// +// Section 2. Define the way the DC and/or CS lines are driven (ESP8266 only) +// +// ################################################################################## + + +// ################################################################################## +// +// Section 3. Define the fonts that are to be used here +// +// ################################################################################## + +// Comment out the #defines below with // to stop that font being loaded +// The ESP8366 and ESP32 have plenty of memory so commenting out fonts is not +// normally necessary. If all fonts are loaded the extra FLASH space required is +// about 17Kbytes. To save FLASH space only enable the fonts you need! + +#define LOAD_GLCD // Font 1. Original Adafruit 8 pixel font needs ~1820 bytes in FLASH +#define LOAD_FONT2 // Font 2. Small 16 pixel high font, needs ~3534 bytes in FLASH, 96 characters +#define LOAD_FONT4 // Font 4. Medium 26 pixel high font, needs ~5848 bytes in FLASH, 96 characters +#define LOAD_FONT6 // Font 6. Large 48 pixel font, needs ~2666 bytes in FLASH, only characters 1234567890:-.apm +#define LOAD_FONT7 // Font 7. 7 segment 48 pixel font, needs ~2438 bytes in FLASH, only characters 1234567890:. +//#define LOAD_FONT8 // Font 8. Large 75 pixel font needs ~3256 bytes in FLASH, only characters 1234567890:-. +#define LOAD_FONT8N // Font 8. Alternative to Font 8 above, slightly narrower, so 3 digits fit a 160 pixel TFT +#define LOAD_GFXFF // FreeFonts. Include access to the 48 Adafruit_GFX free fonts FF1 to FF48 and custom fonts + +// Comment out the #define below to stop the SPIFFS filing system and smooth font code being loaded +// this will save ~20kbytes of FLASH +#define SMOOTH_FONT + +// ################################################################################## +// +// Section 4. Not used +// +// ################################################################################## + + +// ################################################################################## +// +// Section 5. Other options +// +// ################################################################################## + + diff --git a/User_Setups/Setup15_HX8357D.h b/User_Setups/Setup15_HX8357D.h new file mode 100644 index 0000000..d07691a --- /dev/null +++ b/User_Setups/Setup15_HX8357D.h @@ -0,0 +1,214 @@ +// USER DEFINED SETTINGS +// Set driver type, fonts to be loaded, pins used and SPI control method etc +// +// See the User_Setup_Select.h file if you wish to be able to define multiple +// setups and then easily select which setup file is used by the compiler. +// +// If this file is edited correctly then all the library example sketches should +// run without the need to make any more changes for a particular hardware setup! + +// ################################################################################## +// +// Section 0. Call up the right driver file and any options for it +// +// ################################################################################## + +// Only define one driver, the other ones must be commented out +//#define ILI9341_DRIVER +//#define ST7735_DRIVER +//#define ILI9163_DRIVER +//#define S6D02A1_DRIVER +//#define RPI_ILI9486_DRIVER // 20MHz maximum SPI +#define HX8357D_DRIVER + +// For M5Stack ESP32 module with integrated display ONLY, remove // in line below +//#define M5STACK + +// For ST7735 and ILI9163 ONLY, define the pixel width and height in portrait orientation +//#define TFT_WIDTH 128 +//#define TFT_HEIGHT 160 +//#define TFT_HEIGHT 128 + +// For ST7735 ONLY, define the type of display, originally this was based on the +// colour of the tab on the screen protector film but this is not always true, so try +// out the different options below if the screen does not display graphics correctly, +// e.g. colours wrong, mirror images, or tray pixels at the edges. +// Comment out ALL BUT ONE of these options for a ST7735 display driver, save this +// this User_Setup file, then rebuild and upload the sketch to the board again: + +//#define ST7735_INITB +//#define ST7735_GREENTAB +//#define ST7735_GREENTAB2 +//#define ST7735_GREENTAB3 +//#define ST7735_GREENTAB128 // For 128 x 128 display +//#define ST7735_REDTAB +//#define ST7735_BLACKTAB + +// ################################################################################## +// +// Section 1. Define the pins that are used to interface with the display here +// +// ################################################################################## + +// We must use hardware SPI, a minimum of 3 GPIO pins is needed. +// Typical setup for ESP8266 NodeMCU ESP-12 is : +// +// Display SDO/MISO to NodeMCU pin D6 (or leave disconnected if not reading TFT) +// Display LED to NodeMCU pin VIN (or 5V, see below) +// Display SCK to NodeMCU pin D5 +// Display SDI/MOSI to NodeMCU pin D7 +// Display DC (RS/AO)to NodeMCU pin D3 +// Display RESET to NodeMCU pin D4 (or RST, see below) +// Display CS to NodeMCU pin D8 (or GND, see below) +// Display GND to NodeMCU pin GND (0V) +// Display VCC to NodeMCU 5V or 3.3V +// +// The TFT RESET pin can be connected to the NodeMCU RST pin or 3.3V to free up a control pin +// +// The DC (Data Command) pin may be labeled AO or RS (Register Select) +// +// With some displays such as the ILI9341 the TFT CS pin can be connected to GND if no more +// SPI devices (e.g. an SD Card) are connected, in this case comment out the #define TFT_CS +// line below so it is NOT defined. Other displays such at the ST7735 require the TFT CS pin +// to be toggled during setup, so in these cases the TFT_CS line must be defined and connected. +// +// The NodeMCU D0 pin can be used for RST +// +// See Section 2. below if DC or CS is connected to D0 +// +// Note: only some versions of the NodeMCU provide the USB 5V on the VIN pin +// If 5V is not available at a pin you can use 3.3V but backlight brightness +// will be lower. + + +// ###### EDIT THE PIN NUMBERS IN THE LINES FOLLOWING TO SUIT YOUR ESP8266 SETUP ###### + +// For NodeMCU - use pin numbers in the form PIN_Dx where Dx is the NodeMCU pin designation +#define TFT_CS PIN_D8 // Chip select control pin D8 +#define TFT_DC PIN_D3 // Data Command control pin +#define TFT_RST PIN_D4 // Reset pin (could connect to NodeMCU RST, see next line) +//#define TFT_RST -1 // Set TFT_RST to -1 if the display RESET is connected to NodeMCU RST or 3.3V + +//#define TOUCH_CS PIN_D1 // Chip select pin (T_CS) of touch screen + +//#define TFT_WR PIN_D2 // Write strobe for modified Raspberry Pi TFT only + + +// ###### FOR ESP8266 OVERLAP MODE EDIT THE PIN NUMBERS IN THE FOLLOWING LINES ###### + +// Overlap mode shares the ESP8266 FLASH SPI bus with the TFT so has a performance impact +// but saves pins for other functions. +// Use NodeMCU SD0=MISO, SD1=MOSI, CLK=SCLK to connect to TFT in overlap mode + +// In ESP8266 overlap mode the TFT chip select MUST connect to pin D3 +//#define TFT_CS PIN_D3 +//#define TFT_DC PIN_D5 // Data Command control pin +//#define TFT_RST PIN_D4 // Reset pin (could connect to NodeMCU RST, see next line) +//#define TFT_RST -1 // Set TFT_RST to -1 if the display RESET is connected to NodeMCU RST or 3.3V + +// In ESP8266 overlap mode the following must be defined +//#define TFT_SPI_OVERLAP + +// ###### EDIT THE PIN NUMBERS IN THE LINES FOLLOWING TO SUIT YOUR ESP32 SETUP ###### + +// For ESP32 Dev board (only tested with ILI9341 display) +// The hardware SPI can be mapped to any pins + +//#define TFT_MISO 19 +//#define TFT_MOSI 23 +//#define TFT_SCLK 18 +//#define TFT_CS 15 // Chip select control pin +//#define TFT_DC 2 // Data Command control pin +//#define TFT_RST 4 // Reset pin (could connect to RST pin) +//#define TFT_RST -1 // Set TFT_RST to -1 if display RESET is connected to ESP32 board RST + +// For the M5Stack module use these #define lines +//#define TFT_MISO 19 +//#define TFT_MOSI 23 +//#define TFT_SCLK 18 +//#define TFT_CS 14 // Chip select control pin +//#define TFT_DC 27 // Data Command control pin +//#define TFT_RST 33 // Reset pin (could connect to Arduino RESET pin) +//#define TFT_BL 32 // LED back-light + +//#define TOUCH_CS 21 // Chip select pin (T_CS) of touch screen + +//#define TFT_WR 22 // Write strobe for modified Raspberry Pi TFT only + +// ################################################################################## +// +// Section 2. Define the way the DC and/or CS lines are driven (ESP8266 only) +// +// ################################################################################## + +// Normally the library uses direct register access for the DC and CS lines for speed +// If D0 (GPIO16) is used for CS or DC then a different slower method must be used +// Uncomment one line if D0 is used for DC or CS +// DC on D0 = 6% performance penalty at 40MHz SPI running graphics test +// CS on D0 = 2% performance penalty at 40MHz SPI running graphics test + +// #define D0_USED_FOR_DC +// #define D0_USED_FOR_CS + +// ################################################################################## +// +// Section 3. Define the fonts that are to be used here +// +// ################################################################################## + +// Comment out the #defines below with // to stop that font being loaded +// The ESP8366 and ESP32 have plenty of memory so commenting out fonts is not +// normally necessary. If all fonts are loaded the extra FLASH space required is +// about 17Kbytes. To save FLASH space only enable the fonts you need! + +#define LOAD_GLCD // Font 1. Original Adafruit 8 pixel font needs ~1820 bytes in FLASH +#define LOAD_FONT2 // Font 2. Small 16 pixel high font, needs ~3534 bytes in FLASH, 96 characters +#define LOAD_FONT4 // Font 4. Medium 26 pixel high font, needs ~5848 bytes in FLASH, 96 characters +#define LOAD_FONT6 // Font 6. Large 48 pixel font, needs ~2666 bytes in FLASH, only characters 1234567890:-.apm +#define LOAD_FONT7 // Font 7. 7 segment 48 pixel font, needs ~2438 bytes in FLASH, only characters 1234567890:. +#define LOAD_FONT8 // Font 8. Large 75 pixel font needs ~3256 bytes in FLASH, only characters 1234567890:-. +#define LOAD_GFXFF // FreeFonts. Include access to the 48 Adafruit_GFX free fonts FF1 to FF48 and custom fonts + +// ################################################################################## +// +// Section 4. Not used +// +// ################################################################################## + + +// ################################################################################## +// +// Section 5. Other options +// +// ################################################################################## + +// Define the SPI clock frequency, this affects the graphics rendering speed. Too +// fast and the TFT driver will not keep up and display corruption appears. +// With an ILI9341 display 40MHz works OK, 80MHz sometimes fails +// With a ST7735 display more than 27MHz may not work (spurious pixels and lines) +// With an ILI9163 display 27 MHz works OK. +// The RPi typically only works at 20MHz maximum. + +// #define SPI_FREQUENCY 1000000 +// #define SPI_FREQUENCY 5000000 +// #define SPI_FREQUENCY 10000000 +// #define SPI_FREQUENCY 20000000 +#define SPI_FREQUENCY 27000000 // Actually sets it to 26.67MHz = 80/3 +// #define SPI_FREQUENCY 40000000 // Maximum to use SPIFFS +// #define SPI_FREQUENCY 80000000 + +// The XPT2046 requires a lower SPI clock rate of 2.5MHz so we define that here: +#define SPI_TOUCH_FREQUENCY 2500000 + + +// Comment out the following #define if "SPI Transactions" do not need to be +// supported. When commented out the code size will be smaller and sketches will +// run slightly faster, so leave it commented out unless you need it! + +// Transaction support is needed to work with SD library but not needed with TFT_SdFat +// Transaction support is required if other SPI devices are connected. + +// Transactions are automatically enabled by the library for an ESP32 (to use HAL mutex) +// so changing it here has no effect + +// #define SUPPORT_TRANSACTIONS diff --git a/User_Setups/Setup16_ILI9488_Parallel.h b/User_Setups/Setup16_ILI9488_Parallel.h new file mode 100644 index 0000000..c603f2c --- /dev/null +++ b/User_Setups/Setup16_ILI9488_Parallel.h @@ -0,0 +1,90 @@ +// USER DEFINED SETTINGS +// Set driver type, fonts to be loaded, pins used and SPI control method etc +// +// See the User_Setup_Select.h file if you wish to be able to define multiple +// setups and then easily select which setup file is used by the compiler. +// +// If this file is edited correctly then all the library example sketches should +// run without the need to make any more changes for a particular hardware setup! + +// ################################################################################## +// +// Section 0. Call up the right driver file and any options for it +// +// ################################################################################## + +// Parallel bus is only supported on ESP32 +// Use ESP32 Parallel interface instead of SPI +#define ESP32_PARALLEL + +// Only define one driver, the other ones must be commented out +#define ILI9488_DRIVER + +// ################################################################################## +// +// Section 1. Define the pins that are used to interface with the display here +// +// ################################################################################## + +// ESP32 pins used +#define TFT_CS 33 // Chip select control pin +#define TFT_DC 15 // Data Command control pin - use a pin in the range 0-31 +#define TFT_RST 32 // Reset pin + +#define TFT_WR 4 // Write strobe control pin - use a pin in the range 0-31 +#define TFT_RD 2 + +#define TFT_D0 12 // Must use pins in the range 0-31 for the data bus +#define TFT_D1 13 // so a single register write sets/clears all bits +#define TFT_D2 26 +#define TFT_D3 25 +#define TFT_D4 17 +#define TFT_D5 16 +#define TFT_D6 27 +#define TFT_D7 14 + + +// ################################################################################## +// +// Section 2. Define the way the DC and/or CS lines are driven (ESP8266 only) +// +// ################################################################################## + + +// ################################################################################## +// +// Section 3. Define the fonts that are to be used here +// +// ################################################################################## + +// Comment out the #defines below with // to stop that font being loaded +// The ESP8366 and ESP32 have plenty of memory so commenting out fonts is not +// normally necessary. If all fonts are loaded the extra FLASH space required is +// about 17Kbytes. To save FLASH space only enable the fonts you need! + +#define LOAD_GLCD // Font 1. Original Adafruit 8 pixel font needs ~1820 bytes in FLASH +#define LOAD_FONT2 // Font 2. Small 16 pixel high font, needs ~3534 bytes in FLASH, 96 characters +#define LOAD_FONT4 // Font 4. Medium 26 pixel high font, needs ~5848 bytes in FLASH, 96 characters +#define LOAD_FONT6 // Font 6. Large 48 pixel font, needs ~2666 bytes in FLASH, only characters 1234567890:-.apm +#define LOAD_FONT7 // Font 7. 7 segment 48 pixel font, needs ~2438 bytes in FLASH, only characters 1234567890:. +#define LOAD_FONT8 // Font 8. Large 75 pixel font needs ~3256 bytes in FLASH, only characters 1234567890:-. +#define LOAD_GFXFF // FreeFonts. Include access to the 48 Adafruit_GFX free fonts FF1 to FF48 and custom fonts + +// Comment out the #define below to stop the SPIFFS filing system and smooth font code being loaded +// this will save ~20kbytes of FLASH +#define SMOOTH_FONT + +// ################################################################################## +// +// Section 4. Not used +// +// ################################################################################## + + +// ################################################################################## +// +// Section 5. Other options +// +// ################################################################################## + + From 972d852346f94550a32df8a8fd07d377de2662c2 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Mon, 12 Mar 2018 22:49:18 +0000 Subject: [PATCH 095/150] Correct issue #104 and update setup files Macro name (transfer16) conflict with SPI library fixed Smooth font option added to User Setup files Narrow font 8 option added to User Setup files --- TFT_eSPI.cpp | 110 ++++++++++++------------ TFT_eSPI.h | 24 +++--- User_Setups/Setup10_RPi_touch_ILI9486.h | 5 ++ User_Setups/Setup11_RPi_touch_ILI9486.h | 5 ++ User_Setups/Setup12_M5Stack.h | 5 ++ User_Setups/Setup13_ILI9481_Parallel.h | 1 + User_Setups/Setup14_ILI9341_Parallel.h | 4 +- User_Setups/Setup15_HX8357D.h | 5 ++ User_Setups/Setup16_ILI9488_Parallel.h | 1 + User_Setups/Setup1_ILI9341.h | 5 ++ User_Setups/Setup2_ST7735.h | 5 ++ User_Setups/Setup3_ILI9163.h | 5 ++ User_Setups/Setup4_S6D02A1.h | 5 ++ User_Setups/Setup5_RPi_ILI9486.h | 5 ++ User_Setups/Setup6_RPi_Wr_ILI9486.h | 5 ++ User_Setups/Setup7_ST7735_128x128.h | 5 ++ User_Setups/Setup8_ILI9163_128x128.h | 5 ++ User_Setups/Setup9_ST7735_Overlap.h | 5 ++ User_Setups/SetupX_Template.h | 5 ++ 19 files changed, 141 insertions(+), 69 deletions(-) diff --git a/TFT_eSPI.cpp b/TFT_eSPI.cpp index c5fa4ee..ccb9642 100644 --- a/TFT_eSPI.cpp +++ b/TFT_eSPI.cpp @@ -415,7 +415,7 @@ void TFT_eSPI::commandList (const uint8_t *addr) ***************************************************************************************/ void TFT_eSPI::spiwrite(uint8_t c) { - transfer8(c); + tft_Write_8(c); } @@ -428,7 +428,7 @@ void TFT_eSPI::writecommand(uint8_t c) DC_C; CS_L; - transfer8(c); + tft_Write_8(c); CS_H; DC_D; @@ -443,7 +443,7 @@ void TFT_eSPI::writedata(uint8_t d) { CS_L; - transfer8(d); + tft_Write_8(d); CS_H; } @@ -478,16 +478,16 @@ uint8_t TFT_eSPI::readcommand8(uint8_t cmd_function, uint8_t index) DC_C; CS_L; - transfer8(0xD9); + tft_Write_8(0xD9); DC_D; - transfer8(index); + tft_Write_8(index); CS_H; DC_C; CS_L; - transfer8(cmd_function); + tft_Write_8(cmd_function); DC_D; - reg = transfer8(0); + reg = tft_Write_8(0); CS_H; spi_end(); @@ -580,12 +580,12 @@ uint16_t TFT_eSPI::readPixel(int32_t x0, int32_t y0) readAddrWindow(x0, y0, x0, y0); // Sets CS low // Dummy read to throw away don't care value - transfer8(0); + tft_Write_8(0); // Read window pixel 24 bit RGB values - uint8_t r = transfer8(0); - uint8_t g = transfer8(0); - uint8_t b = transfer8(0); + uint8_t r = tft_Write_8(0); + uint8_t g = tft_Write_8(0); + uint8_t b = tft_Write_8(0); CS_H; @@ -707,16 +707,16 @@ void TFT_eSPI::readRect(uint32_t x, uint32_t y, uint32_t w, uint32_t h, uint16_t readAddrWindow(x, y, x + w - 1, y + h - 1); // Sets CS low // Dummy read to throw away don't care value - transfer8(0); + tft_Write_8(0); // Read window pixel 24 bit RGB values uint32_t len = w * h; while (len--) { // Read the 3 RGB bytes, colour is actually only in the top 6 bits of each byte // as the TFT stores colours as 18 bits - uint8_t r = transfer8(0); - uint8_t g = transfer8(0); - uint8_t b = transfer8(0); + uint8_t r = tft_Write_8(0); + uint8_t g = tft_Write_8(0); + uint8_t b = tft_Write_8(0); // Swapped colour byte order for compatibility with pushRect() *data++ = (r & 0xF8) | (g & 0xE0) >> 5 | (b & 0xF8) << 5 | (g & 0x1C) << 11; @@ -1197,16 +1197,16 @@ void TFT_eSPI::readRectRGB(int32_t x0, int32_t y0, int32_t w, int32_t h, uint8_ readAddrWindow(x0, y0, x0 + w - 1, y0 + h - 1); // Sets CS low // Dummy read to throw away don't care value - transfer8(0); + tft_Write_8(0); // Read window pixel 24 bit RGB values, buffer must be set in sketch to 3 * w * h uint32_t len = w * h; while (len--) { // Read the 3 RGB bytes, colour is actually only in the top 6 bits of each byte // as the TFT stores colours as 18 bits - *data++ = transfer8(0); - *data++ = transfer8(0); - *data++ = transfer8(0); + *data++ = tft_Write_8(0); + *data++ = tft_Write_8(0); + *data++ = tft_Write_8(0); } CS_H; @@ -2037,11 +2037,11 @@ void TFT_eSPI::drawChar(int32_t x, int32_t y, unsigned char c, uint32_t color, u for (int8_t j = 0; j < 8; j++) { for (int8_t k = 0; k < 5; k++ ) { - if (column[k] & mask) {transfer16(color);} - else {transfer16(bg);} + if (column[k] & mask) {tft_Write_16(color);} + else {tft_Write_16(bg);} } mask <<= 1; - transfer16(bg); + tft_Write_16(bg); } #endif @@ -2472,7 +2472,7 @@ inline void TFT_eSPI::setAddrWindow(int32_t x0, int32_t y0, int32_t x1, int32_t DC_C; CS_L; - transfer8(TFT_CASET); + tft_Write_8(TFT_CASET); DC_D; @@ -2480,12 +2480,12 @@ inline void TFT_eSPI::setAddrWindow(int32_t x0, int32_t y0, int32_t x1, int32_t uint8_t xBin[] = { 0, (uint8_t) (x0>>8), 0, (uint8_t) (x0>>0), 0, (uint8_t) (x1>>8), 0, (uint8_t) (x1>>0), }; SPI.writePattern(&xBin[0], 8, 1); #else - transfer32(xaw); + tft_Write_32(xaw); #endif // Row addr set DC_C; - transfer8(TFT_PASET); + tft_Write_8(TFT_PASET); DC_D; @@ -2493,13 +2493,13 @@ inline void TFT_eSPI::setAddrWindow(int32_t x0, int32_t y0, int32_t x1, int32_t uint8_t yBin[] = { 0, (uint8_t) (y0>>8), 0, (uint8_t) (y0>>0), 0, (uint8_t) (y1>>8), 0, (uint8_t) (y1>>0), }; SPI.writePattern(&yBin[0], 8, 1); #else - transfer32(yaw); + tft_Write_32(yaw); #endif // write to RAM DC_C; - transfer8(TFT_RAMWR); + tft_Write_8(TFT_RAMWR); DC_D; @@ -2602,24 +2602,24 @@ void TFT_eSPI::readAddrWindow(int32_t x0, int32_t y0, int32_t x1, int32_t y1) DC_C; CS_L; - transfer8(TFT_CASET); + tft_Write_8(TFT_CASET); DC_D; - transfer32(xaw); + tft_Write_32(xaw); // Row addr set DC_C; - transfer8(TFT_PASET); + tft_Write_8(TFT_PASET); DC_D; - transfer32(yaw); + tft_Write_32(yaw); DC_C; - transfer8(TFT_RAMRD); // Read CGRAM command + tft_Write_8(TFT_RAMRD); // Read CGRAM command DC_D; @@ -2837,7 +2837,7 @@ void TFT_eSPI::drawPixel(uint32_t x, uint32_t y, uint32_t color) DC_C; - transfer8(TFT_CASET); + tft_Write_8(TFT_CASET); DC_D; @@ -2845,7 +2845,7 @@ void TFT_eSPI::drawPixel(uint32_t x, uint32_t y, uint32_t color) uint8_t xBin[] = { 0, (uint8_t) (x>>8), 0, (uint8_t) (x>>0), 0, (uint8_t) (x>>8), 0, (uint8_t) (x>>0), }; SPI.writePattern(&xBin[0], 8, 1); #else - transfer32(xaw); + tft_Write_32(xaw); #endif addr_col = x; @@ -2856,7 +2856,7 @@ void TFT_eSPI::drawPixel(uint32_t x, uint32_t y, uint32_t color) DC_C; - transfer8(TFT_PASET); + tft_Write_8(TFT_PASET); DC_D; @@ -2864,7 +2864,7 @@ void TFT_eSPI::drawPixel(uint32_t x, uint32_t y, uint32_t color) uint8_t yBin[] = { 0, (uint8_t) (y>>8), 0, (uint8_t) (y>>0), 0, (uint8_t) (y>>8), 0, (uint8_t) (y>>0), }; SPI.writePattern(&yBin[0], 8, 1); #else - transfer32(yaw); + tft_Write_32(yaw); #endif addr_row = y; @@ -2872,11 +2872,11 @@ void TFT_eSPI::drawPixel(uint32_t x, uint32_t y, uint32_t color) DC_C; - transfer8(TFT_RAMWR); + tft_Write_8(TFT_RAMWR); DC_D; - transfer16(color); + tft_Write_16(color); CS_H; @@ -2896,7 +2896,7 @@ void TFT_eSPI::pushColor(uint16_t color) CS_L; - transfer16(color); + tft_Write_16(color); CS_H; @@ -2920,7 +2920,7 @@ void TFT_eSPI::pushColor(uint16_t color, uint16_t len) while(len--) {WR_L; WR_H;} #else #ifdef ESP32_PARALLEL - while (len--) {transfer16(color);} + while (len--) {tft_Write_16(color);} #else writeBlock(color, len); #endif @@ -2948,7 +2948,7 @@ void TFT_eSPI::pushColors(uint8_t *data, uint32_t len) if (len) SPI.writePattern(data, len, 1); #else #ifdef ESP32_PARALLEL - while (len--) {transfer8(*data); data++;} + while (len--) {tft_Write_8(*data); data++;} #else #if (SPI_FREQUENCY == 80000000) while ( len >=64 ) {SPI.writePattern(data, 64, 1); data += 64; len -= 64; } @@ -2977,7 +2977,7 @@ void TFT_eSPI::pushColors(uint16_t *data, uint32_t len, bool swap) #if defined (ESP32) #ifdef ESP32_PARALLEL - if (swap) while ( len-- ) {transfer16(*data); data++;} + if (swap) while ( len-- ) {tft_Write_16(*data); data++;} else while ( len-- ) {transwap16(*data); data++;} #else if (swap) SPI.writePixels(data,len<<1); @@ -3279,13 +3279,13 @@ void TFT_eSPI::drawFastVLine(int32_t x, int32_t y, int32_t h, uint32_t color) SPI1CMD |= SPIBUSY; while(SPI1CMD & SPIBUSY) {} #else - transfer16(color); + tft_Write_16(color); #endif h--; while(h--) {WR_L; WR_H;} #else #ifdef ESP32_PARALLEL - while (h--) {transfer16(color);} + while (h--) {tft_Write_16(color);} #else writeBlock(color, h); #endif @@ -3337,13 +3337,13 @@ void TFT_eSPI::drawFastHLine(int32_t x, int32_t y, int32_t w, uint32_t color) SPI1CMD |= SPIBUSY; while(SPI1CMD & SPIBUSY) {} #else - transfer16(color); + tft_Write_16(color); #endif w--; while(w--) {WR_L; WR_H;} #else #ifdef ESP32_PARALLEL - while (w--) {transfer16(color);} + while (w--) {tft_Write_16(color);} #else writeBlock(color, w); #endif @@ -3392,19 +3392,19 @@ void TFT_eSPI::fillRect(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t col uint32_t n = (uint32_t)w * (uint32_t)h; #ifdef RPI_WRITE_STROBE - transfer16(color); + tft_Write_16(color); while(n--) {WR_L; WR_H;} #else #ifdef ESP32_PARALLEL if (color>>8 == (uint8_t)color) { - transfer8(color); + tft_Write_8(color); n--; WR_L; WR_H; while (n) {WR_L; WR_H; n--; WR_L; WR_H;} } else { - while (n--) {transfer16(color);} + while (n--) {tft_Write_16(color);} } #else writeBlock(color, n); @@ -3762,8 +3762,8 @@ int16_t TFT_eSPI::drawChar(unsigned int uniCode, int x, int y, int font) pX = x + k * 8; mask = 0x80; while (mask) { - if (line & mask) {transfer16(textcolor);} - else {transfer16(textbgcolor);} + if (line & mask) {tft_Write_16(textcolor);} + else {tft_Write_16(textbgcolor);} mask = mask >> 1; } } @@ -3817,9 +3817,9 @@ int16_t TFT_eSPI::drawChar(unsigned int uniCode, int x, int y, int font) if (ts) { tnp = np; - while (tnp--) {transfer16(textcolor);} + while (tnp--) {tft_Write_16(textcolor);} } - else {transfer16(textcolor);} + else {tft_Write_16(textcolor);} px += textsize; if (px >= (x + width * textsize)) @@ -3859,7 +3859,7 @@ int16_t TFT_eSPI::drawChar(unsigned int uniCode, int x, int y, int font) while(line--) {WR_L; WR_H;} #else #ifdef ESP32_PARALLEL - while (line--) {transfer16(textcolor);} + while (line--) {tft_Write_16(textcolor);} #else writeBlock(textcolor,line); #endif @@ -3872,7 +3872,7 @@ int16_t TFT_eSPI::drawChar(unsigned int uniCode, int x, int y, int font) while(line--) {WR_L; WR_H;} #else #ifdef ESP32_PARALLEL - while (line--) {transfer16(textbgcolor);} + while (line--) {tft_Write_16(textbgcolor);} #else writeBlock(textbgcolor,line); #endif diff --git a/TFT_eSPI.h b/TFT_eSPI.h index 22610f8..343a39a 100644 --- a/TFT_eSPI.h +++ b/TFT_eSPI.h @@ -167,16 +167,16 @@ //#define set_mask(C) ((C&0x80)>>7)<>6)<>5)<>4)<>3)<>2)<>1)<>0)<> 8)); WR_H; \ + #define tft_Write_16(C) GPIO.out_w1tc = clr_mask; GPIO.out_w1ts = set_mask((uint8_t)(C >> 8)); WR_H; \ GPIO.out_w1tc = clr_mask; GPIO.out_w1ts = set_mask((uint8_t)(C >> 0)); WR_H // 16 bit transfer with swapped bytes #define transwap16(C) GPIO.out_w1tc = clr_mask; GPIO.out_w1ts = set_mask((uint8_t) (C >> 0)); WR_H; \ GPIO.out_w1tc = clr_mask; GPIO.out_w1ts = set_mask((uint8_t) (C >> 8)); WR_H - #define transfer32(C) GPIO.out_w1tc = clr_mask; GPIO.out_w1ts = set_mask((uint8_t) (C >> 24)); WR_H; \ + #define tft_Write_32(C) GPIO.out_w1tc = clr_mask; GPIO.out_w1ts = set_mask((uint8_t) (C >> 24)); WR_H; \ GPIO.out_w1tc = clr_mask; GPIO.out_w1ts = set_mask((uint8_t) (C >> 16)); WR_H; \ GPIO.out_w1tc = clr_mask; GPIO.out_w1ts = set_mask((uint8_t) (C >> 8)); WR_H; \ GPIO.out_w1tc = clr_mask; GPIO.out_w1ts = set_mask((uint8_t) (C >> 0)); WR_H @@ -194,14 +194,14 @@ #endif #elif defined (SEND_16_BITS) - #define transfer8(C) SPI.transfer(0); SPI.transfer(C) - #define transfer16(C) SPI.write16(C) - #define transfer32(C) SPI.write32(C) + #define tft_Write_8(C) SPI.transfer(0); SPI.transfer(C) + #define tft_Write_16(C) SPI.write16(C) + #define tft_Write_32(C) SPI.write32(C) #else - #define transfer8(C) SPI.transfer(C) - #define transfer16(C) SPI.write16(C) - #define transfer32(C) SPI.write32(C) + #define tft_Write_8(C) SPI.transfer(C) + #define tft_Write_16(C) SPI.write16(C) + #define tft_Write_32(C) SPI.write32(C) #endif @@ -512,12 +512,12 @@ class TFT_eSPI : public Print { uint16_t fontsLoaded(void), color565(uint8_t r, uint8_t g, uint8_t b), color8to16(uint8_t color332); // Convert 8 bit colour to 16 bits - + int16_t drawNumber(long long_num,int poX, int poY, int font), drawNumber(long long_num,int poX, int poY), drawFloat(float floatNumber,int decimal,int poX, int poY, int font), drawFloat(float floatNumber,int decimal,int poX, int poY), - + // Handle char arrays drawString(const char *string, int poX, int poY, int font), drawString(const char *string, int poX, int poY), @@ -529,7 +529,7 @@ class TFT_eSPI : public Print { drawString(const String& string, int poX, int poY), drawCentreString(const String& string, int dX, int poY, int font), // Deprecated, use setTextDatum() and drawString() drawRightString(const String& string, int dX, int poY, int font); // Deprecated, use setTextDatum() and drawString() - + int16_t height(void), width(void), textWidth(const char *string, int font), diff --git a/User_Setups/Setup10_RPi_touch_ILI9486.h b/User_Setups/Setup10_RPi_touch_ILI9486.h index 248020c..bbfe895 100644 --- a/User_Setups/Setup10_RPi_touch_ILI9486.h +++ b/User_Setups/Setup10_RPi_touch_ILI9486.h @@ -166,8 +166,13 @@ #define LOAD_FONT6 // Font 6. Large 48 pixel font, needs ~2666 bytes in FLASH, only characters 1234567890:-.apm #define LOAD_FONT7 // Font 7. 7 segment 48 pixel font, needs ~2438 bytes in FLASH, only characters 1234567890:. #define LOAD_FONT8 // Font 8. Large 75 pixel font needs ~3256 bytes in FLASH, only characters 1234567890:-. +//#define LOAD_FONT8N // Font 8. Alternative to Font 8 above, slightly narrower, so 3 digits fit a 160 pixel TFT #define LOAD_GFXFF // FreeFonts. Include access to the 48 Adafruit_GFX free fonts FF1 to FF48 and custom fonts +// Comment out the #define below to stop the SPIFFS filing system and smooth font code being loaded +// this will save ~20kbytes of FLASH +#define SMOOTH_FONT + // ################################################################################## // // Section 4. Not used diff --git a/User_Setups/Setup11_RPi_touch_ILI9486.h b/User_Setups/Setup11_RPi_touch_ILI9486.h index a30d502..cbb489b 100644 --- a/User_Setups/Setup11_RPi_touch_ILI9486.h +++ b/User_Setups/Setup11_RPi_touch_ILI9486.h @@ -167,8 +167,13 @@ #define LOAD_FONT6 // Font 6. Large 48 pixel font, needs ~2666 bytes in FLASH, only characters 1234567890:-.apm #define LOAD_FONT7 // Font 7. 7 segment 48 pixel font, needs ~2438 bytes in FLASH, only characters 1234567890:. #define LOAD_FONT8 // Font 8. Large 75 pixel font needs ~3256 bytes in FLASH, only characters 1234567890:-. +//#define LOAD_FONT8N // Font 8. Alternative to Font 8 above, slightly narrower, so 3 digits fit a 160 pixel TFT #define LOAD_GFXFF // FreeFonts. Include access to the 48 Adafruit_GFX free fonts FF1 to FF48 and custom fonts +// Comment out the #define below to stop the SPIFFS filing system and smooth font code being loaded +// this will save ~20kbytes of FLASH +#define SMOOTH_FONT + // ################################################################################## // // Section 4. Not used diff --git a/User_Setups/Setup12_M5Stack.h b/User_Setups/Setup12_M5Stack.h index 9229925..3439512 100644 --- a/User_Setups/Setup12_M5Stack.h +++ b/User_Setups/Setup12_M5Stack.h @@ -166,8 +166,13 @@ #define LOAD_FONT6 // Font 6. Large 48 pixel font, needs ~2666 bytes in FLASH, only characters 1234567890:-.apm #define LOAD_FONT7 // Font 7. 7 segment 48 pixel font, needs ~2438 bytes in FLASH, only characters 1234567890:. #define LOAD_FONT8 // Font 8. Large 75 pixel font needs ~3256 bytes in FLASH, only characters 1234567890:-. +//#define LOAD_FONT8N // Font 8. Alternative to Font 8 above, slightly narrower, so 3 digits fit a 160 pixel TFT #define LOAD_GFXFF // FreeFonts. Include access to the 48 Adafruit_GFX free fonts FF1 to FF48 and custom fonts +// Comment out the #define below to stop the SPIFFS filing system and smooth font code being loaded +// this will save ~20kbytes of FLASH +#define SMOOTH_FONT + // ################################################################################## // // Section 4. Not used diff --git a/User_Setups/Setup13_ILI9481_Parallel.h b/User_Setups/Setup13_ILI9481_Parallel.h index e91ccdb..0de1ebd 100644 --- a/User_Setups/Setup13_ILI9481_Parallel.h +++ b/User_Setups/Setup13_ILI9481_Parallel.h @@ -68,6 +68,7 @@ #define LOAD_FONT6 // Font 6. Large 48 pixel font, needs ~2666 bytes in FLASH, only characters 1234567890:-.apm #define LOAD_FONT7 // Font 7. 7 segment 48 pixel font, needs ~2438 bytes in FLASH, only characters 1234567890:. #define LOAD_FONT8 // Font 8. Large 75 pixel font needs ~3256 bytes in FLASH, only characters 1234567890:-. +//#define LOAD_FONT8N // Font 8. Alternative to Font 8 above, slightly narrower, so 3 digits fit a 160 pixel TFT #define LOAD_GFXFF // FreeFonts. Include access to the 48 Adafruit_GFX free fonts FF1 to FF48 and custom fonts // Comment out the #define below to stop the SPIFFS filing system and smooth font code being loaded diff --git a/User_Setups/Setup14_ILI9341_Parallel.h b/User_Setups/Setup14_ILI9341_Parallel.h index 5e5fc8e..d777264 100644 --- a/User_Setups/Setup14_ILI9341_Parallel.h +++ b/User_Setups/Setup14_ILI9341_Parallel.h @@ -67,8 +67,8 @@ #define LOAD_FONT4 // Font 4. Medium 26 pixel high font, needs ~5848 bytes in FLASH, 96 characters #define LOAD_FONT6 // Font 6. Large 48 pixel font, needs ~2666 bytes in FLASH, only characters 1234567890:-.apm #define LOAD_FONT7 // Font 7. 7 segment 48 pixel font, needs ~2438 bytes in FLASH, only characters 1234567890:. -//#define LOAD_FONT8 // Font 8. Large 75 pixel font needs ~3256 bytes in FLASH, only characters 1234567890:-. -#define LOAD_FONT8N // Font 8. Alternative to Font 8 above, slightly narrower, so 3 digits fit a 160 pixel TFT +#define LOAD_FONT8 // Font 8. Large 75 pixel font needs ~3256 bytes in FLASH, only characters 1234567890:-. +//#define LOAD_FONT8N // Font 8. Alternative to Font 8 above, slightly narrower, so 3 digits fit a 160 pixel TFT #define LOAD_GFXFF // FreeFonts. Include access to the 48 Adafruit_GFX free fonts FF1 to FF48 and custom fonts // Comment out the #define below to stop the SPIFFS filing system and smooth font code being loaded diff --git a/User_Setups/Setup15_HX8357D.h b/User_Setups/Setup15_HX8357D.h index d07691a..99ae1d6 100644 --- a/User_Setups/Setup15_HX8357D.h +++ b/User_Setups/Setup15_HX8357D.h @@ -167,8 +167,13 @@ #define LOAD_FONT6 // Font 6. Large 48 pixel font, needs ~2666 bytes in FLASH, only characters 1234567890:-.apm #define LOAD_FONT7 // Font 7. 7 segment 48 pixel font, needs ~2438 bytes in FLASH, only characters 1234567890:. #define LOAD_FONT8 // Font 8. Large 75 pixel font needs ~3256 bytes in FLASH, only characters 1234567890:-. +//#define LOAD_FONT8N // Font 8. Alternative to Font 8 above, slightly narrower, so 3 digits fit a 160 pixel TFT #define LOAD_GFXFF // FreeFonts. Include access to the 48 Adafruit_GFX free fonts FF1 to FF48 and custom fonts +// Comment out the #define below to stop the SPIFFS filing system and smooth font code being loaded +// this will save ~20kbytes of FLASH +#define SMOOTH_FONT + // ################################################################################## // // Section 4. Not used diff --git a/User_Setups/Setup16_ILI9488_Parallel.h b/User_Setups/Setup16_ILI9488_Parallel.h index c603f2c..bcaca48 100644 --- a/User_Setups/Setup16_ILI9488_Parallel.h +++ b/User_Setups/Setup16_ILI9488_Parallel.h @@ -68,6 +68,7 @@ #define LOAD_FONT6 // Font 6. Large 48 pixel font, needs ~2666 bytes in FLASH, only characters 1234567890:-.apm #define LOAD_FONT7 // Font 7. 7 segment 48 pixel font, needs ~2438 bytes in FLASH, only characters 1234567890:. #define LOAD_FONT8 // Font 8. Large 75 pixel font needs ~3256 bytes in FLASH, only characters 1234567890:-. +//#define LOAD_FONT8N // Font 8. Alternative to Font 8 above, slightly narrower, so 3 digits fit a 160 pixel TFT #define LOAD_GFXFF // FreeFonts. Include access to the 48 Adafruit_GFX free fonts FF1 to FF48 and custom fonts // Comment out the #define below to stop the SPIFFS filing system and smooth font code being loaded diff --git a/User_Setups/Setup1_ILI9341.h b/User_Setups/Setup1_ILI9341.h index 7831b6a..7eb6fba 100644 --- a/User_Setups/Setup1_ILI9341.h +++ b/User_Setups/Setup1_ILI9341.h @@ -166,8 +166,13 @@ #define LOAD_FONT6 // Font 6. Large 48 pixel font, needs ~2666 bytes in FLASH, only characters 1234567890:-.apm #define LOAD_FONT7 // Font 7. 7 segment 48 pixel font, needs ~2438 bytes in FLASH, only characters 1234567890:. #define LOAD_FONT8 // Font 8. Large 75 pixel font needs ~3256 bytes in FLASH, only characters 1234567890:-. +//#define LOAD_FONT8N // Font 8. Alternative to Font 8 above, slightly narrower, so 3 digits fit a 160 pixel TFT #define LOAD_GFXFF // FreeFonts. Include access to the 48 Adafruit_GFX free fonts FF1 to FF48 and custom fonts +// Comment out the #define below to stop the SPIFFS filing system and smooth font code being loaded +// this will save ~20kbytes of FLASH +#define SMOOTH_FONT + // ################################################################################## // // Section 4. Not used diff --git a/User_Setups/Setup2_ST7735.h b/User_Setups/Setup2_ST7735.h index 14dac9b..7f79ca8 100644 --- a/User_Setups/Setup2_ST7735.h +++ b/User_Setups/Setup2_ST7735.h @@ -166,8 +166,13 @@ #define LOAD_FONT6 // Font 6. Large 48 pixel font, needs ~2666 bytes in FLASH, only characters 1234567890:-.apm #define LOAD_FONT7 // Font 7. 7 segment 48 pixel font, needs ~2438 bytes in FLASH, only characters 1234567890:. #define LOAD_FONT8 // Font 8. Large 75 pixel font needs ~3256 bytes in FLASH, only characters 1234567890:-. +//#define LOAD_FONT8N // Font 8. Alternative to Font 8 above, slightly narrower, so 3 digits fit a 160 pixel TFT #define LOAD_GFXFF // FreeFonts. Include access to the 48 Adafruit_GFX free fonts FF1 to FF48 and custom fonts +// Comment out the #define below to stop the SPIFFS filing system and smooth font code being loaded +// this will save ~20kbytes of FLASH +#define SMOOTH_FONT + // ################################################################################## // // Section 4. Not used diff --git a/User_Setups/Setup3_ILI9163.h b/User_Setups/Setup3_ILI9163.h index 36d334d..e8bd1b2 100644 --- a/User_Setups/Setup3_ILI9163.h +++ b/User_Setups/Setup3_ILI9163.h @@ -166,8 +166,13 @@ #define LOAD_FONT6 // Font 6. Large 48 pixel font, needs ~2666 bytes in FLASH, only characters 1234567890:-.apm #define LOAD_FONT7 // Font 7. 7 segment 48 pixel font, needs ~2438 bytes in FLASH, only characters 1234567890:. #define LOAD_FONT8 // Font 8. Large 75 pixel font needs ~3256 bytes in FLASH, only characters 1234567890:-. +//#define LOAD_FONT8N // Font 8. Alternative to Font 8 above, slightly narrower, so 3 digits fit a 160 pixel TFT #define LOAD_GFXFF // FreeFonts. Include access to the 48 Adafruit_GFX free fonts FF1 to FF48 and custom fonts +// Comment out the #define below to stop the SPIFFS filing system and smooth font code being loaded +// this will save ~20kbytes of FLASH +#define SMOOTH_FONT + // ################################################################################## // // Section 4. Not used diff --git a/User_Setups/Setup4_S6D02A1.h b/User_Setups/Setup4_S6D02A1.h index 3f94e71..0a1530b 100644 --- a/User_Setups/Setup4_S6D02A1.h +++ b/User_Setups/Setup4_S6D02A1.h @@ -166,8 +166,13 @@ #define LOAD_FONT6 // Font 6. Large 48 pixel font, needs ~2666 bytes in FLASH, only characters 1234567890:-.apm #define LOAD_FONT7 // Font 7. 7 segment 48 pixel font, needs ~2438 bytes in FLASH, only characters 1234567890:. #define LOAD_FONT8 // Font 8. Large 75 pixel font needs ~3256 bytes in FLASH, only characters 1234567890:-. +//#define LOAD_FONT8N // Font 8. Alternative to Font 8 above, slightly narrower, so 3 digits fit a 160 pixel TFT #define LOAD_GFXFF // FreeFonts. Include access to the 48 Adafruit_GFX free fonts FF1 to FF48 and custom fonts +// Comment out the #define below to stop the SPIFFS filing system and smooth font code being loaded +// this will save ~20kbytes of FLASH +#define SMOOTH_FONT + // ################################################################################## // // Section 4. Not used diff --git a/User_Setups/Setup5_RPi_ILI9486.h b/User_Setups/Setup5_RPi_ILI9486.h index 3301daa..c21e181 100644 --- a/User_Setups/Setup5_RPi_ILI9486.h +++ b/User_Setups/Setup5_RPi_ILI9486.h @@ -166,8 +166,13 @@ #define LOAD_FONT6 // Font 6. Large 48 pixel font, needs ~2666 bytes in FLASH, only characters 1234567890:-.apm #define LOAD_FONT7 // Font 7. 7 segment 48 pixel font, needs ~2438 bytes in FLASH, only characters 1234567890:. #define LOAD_FONT8 // Font 8. Large 75 pixel font needs ~3256 bytes in FLASH, only characters 1234567890:-. +//#define LOAD_FONT8N // Font 8. Alternative to Font 8 above, slightly narrower, so 3 digits fit a 160 pixel TFT #define LOAD_GFXFF // FreeFonts. Include access to the 48 Adafruit_GFX free fonts FF1 to FF48 and custom fonts +// Comment out the #define below to stop the SPIFFS filing system and smooth font code being loaded +// this will save ~20kbytes of FLASH +#define SMOOTH_FONT + // ################################################################################## // // Section 4. Not used diff --git a/User_Setups/Setup6_RPi_Wr_ILI9486.h b/User_Setups/Setup6_RPi_Wr_ILI9486.h index 8a3c00c..e5c9303 100644 --- a/User_Setups/Setup6_RPi_Wr_ILI9486.h +++ b/User_Setups/Setup6_RPi_Wr_ILI9486.h @@ -166,8 +166,13 @@ #define LOAD_FONT6 // Font 6. Large 48 pixel font, needs ~2666 bytes in FLASH, only characters 1234567890:-.apm #define LOAD_FONT7 // Font 7. 7 segment 48 pixel font, needs ~2438 bytes in FLASH, only characters 1234567890:. #define LOAD_FONT8 // Font 8. Large 75 pixel font needs ~3256 bytes in FLASH, only characters 1234567890:-. +//#define LOAD_FONT8N // Font 8. Alternative to Font 8 above, slightly narrower, so 3 digits fit a 160 pixel TFT #define LOAD_GFXFF // FreeFonts. Include access to the 48 Adafruit_GFX free fonts FF1 to FF48 and custom fonts +// Comment out the #define below to stop the SPIFFS filing system and smooth font code being loaded +// this will save ~20kbytes of FLASH +#define SMOOTH_FONT + // ################################################################################## // // Section 4. Not used diff --git a/User_Setups/Setup7_ST7735_128x128.h b/User_Setups/Setup7_ST7735_128x128.h index 481eebc..3bb32cf 100644 --- a/User_Setups/Setup7_ST7735_128x128.h +++ b/User_Setups/Setup7_ST7735_128x128.h @@ -166,8 +166,13 @@ #define LOAD_FONT6 // Font 6. Large 48 pixel font, needs ~2666 bytes in FLASH, only characters 1234567890:-.apm #define LOAD_FONT7 // Font 7. 7 segment 48 pixel font, needs ~2438 bytes in FLASH, only characters 1234567890:. #define LOAD_FONT8 // Font 8. Large 75 pixel font needs ~3256 bytes in FLASH, only characters 1234567890:-. +//#define LOAD_FONT8N // Font 8. Alternative to Font 8 above, slightly narrower, so 3 digits fit a 160 pixel TFT #define LOAD_GFXFF // FreeFonts. Include access to the 48 Adafruit_GFX free fonts FF1 to FF48 and custom fonts +// Comment out the #define below to stop the SPIFFS filing system and smooth font code being loaded +// this will save ~20kbytes of FLASH +#define SMOOTH_FONT + // ################################################################################## // // Section 4. Not used diff --git a/User_Setups/Setup8_ILI9163_128x128.h b/User_Setups/Setup8_ILI9163_128x128.h index 4b643b1..05b7cb6 100644 --- a/User_Setups/Setup8_ILI9163_128x128.h +++ b/User_Setups/Setup8_ILI9163_128x128.h @@ -166,8 +166,13 @@ #define LOAD_FONT6 // Font 6. Large 48 pixel font, needs ~2666 bytes in FLASH, only characters 1234567890:-.apm #define LOAD_FONT7 // Font 7. 7 segment 48 pixel font, needs ~2438 bytes in FLASH, only characters 1234567890:. #define LOAD_FONT8 // Font 8. Large 75 pixel font needs ~3256 bytes in FLASH, only characters 1234567890:-. +//#define LOAD_FONT8N // Font 8. Alternative to Font 8 above, slightly narrower, so 3 digits fit a 160 pixel TFT #define LOAD_GFXFF // FreeFonts. Include access to the 48 Adafruit_GFX free fonts FF1 to FF48 and custom fonts +// Comment out the #define below to stop the SPIFFS filing system and smooth font code being loaded +// this will save ~20kbytes of FLASH +#define SMOOTH_FONT + // ################################################################################## // // Section 4. Not used diff --git a/User_Setups/Setup9_ST7735_Overlap.h b/User_Setups/Setup9_ST7735_Overlap.h index b378791..57f9019 100644 --- a/User_Setups/Setup9_ST7735_Overlap.h +++ b/User_Setups/Setup9_ST7735_Overlap.h @@ -166,8 +166,13 @@ #define LOAD_FONT6 // Font 6. Large 48 pixel font, needs ~2666 bytes in FLASH, only characters 1234567890:-.apm #define LOAD_FONT7 // Font 7. 7 segment 48 pixel font, needs ~2438 bytes in FLASH, only characters 1234567890:. #define LOAD_FONT8 // Font 8. Large 75 pixel font needs ~3256 bytes in FLASH, only characters 1234567890:-. +//#define LOAD_FONT8N // Font 8. Alternative to Font 8 above, slightly narrower, so 3 digits fit a 160 pixel TFT #define LOAD_GFXFF // FreeFonts. Include access to the 48 Adafruit_GFX free fonts FF1 to FF48 and custom fonts +// Comment out the #define below to stop the SPIFFS filing system and smooth font code being loaded +// this will save ~20kbytes of FLASH +#define SMOOTH_FONT + // ################################################################################## // // Section 4. Not used diff --git a/User_Setups/SetupX_Template.h b/User_Setups/SetupX_Template.h index 3b635f9..6214165 100644 --- a/User_Setups/SetupX_Template.h +++ b/User_Setups/SetupX_Template.h @@ -165,9 +165,14 @@ #define LOAD_FONT4 // Font 4. Medium 26 pixel high font, needs ~5848 bytes in FLASH, 96 characters #define LOAD_FONT6 // Font 6. Large 48 pixel font, needs ~2666 bytes in FLASH, only characters 1234567890:-.apm #define LOAD_FONT7 // Font 7. 7 segment 48 pixel font, needs ~2438 bytes in FLASH, only characters 1234567890:. +//#define LOAD_FONT8N // Font 8. Alternative to Font 8 above, slightly narrower, so 3 digits fit a 160 pixel TFT #define LOAD_FONT8 // Font 8. Large 75 pixel font needs ~3256 bytes in FLASH, only characters 1234567890:-. #define LOAD_GFXFF // FreeFonts. Include access to the 48 Adafruit_GFX free fonts FF1 to FF48 and custom fonts +// Comment out the #define below to stop the SPIFFS filing system and smooth font code being loaded +// this will save ~20kbytes of FLASH +#define SMOOTH_FONT + // ################################################################################## // // Section 4. Not used From 8e7e2a3b8fe8d46a52e48bdd6fe472ba682f92af Mon Sep 17 00:00:00 2001 From: Bodmer Date: Mon, 12 Mar 2018 22:50:35 +0000 Subject: [PATCH 096/150] Increment version --- library.json | 2 +- library.properties | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/library.json b/library.json index bdf5b64..27cc221 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "TFT_eSPI", - "version": "0.19.10", + "version": "0.19.11", "keywords": "tft, display, ESP8266, NodeMCU, ESP32, M5Stack, ILI9341, ST7735, ILI9163, S6D02A1, ILI9486", "description": "A TFT SPI graphics library for ESP8266 and ESP32", "repository": diff --git a/library.properties b/library.properties index 7b582f6..6c14dcb 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=TFT_eSPI -version=0.19.10 +version=0.19.11 author=Bodmer maintainer=Bodmer sentence=A fast TFT library for ESP8266 processors and the Arduino IDE From ca968e6786aeef50260ef63e00ad77ed1321c92d Mon Sep 17 00:00:00 2001 From: Bodmer Date: Fri, 16 Mar 2018 22:55:48 +0000 Subject: [PATCH 097/150] Add comment about touch on parallel displays --- README.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 344f1a2..b286a0a 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,12 @@ -# TFT_eSPI -Update 10th March 2018: Added support for 8 bit parallel interface TFTs when used with ESP32. -Update 24th February 2018: Added new smooth (antialiased) fonts. See Smooth Font examples and Tools folder for the font generator. +Update 10th March 2018: Added support for 8 bit parallel interface TFTs when used with ESP32 (Touch not supported yet on parallel displays) + +Update 24th February 2018: Added new smooth (antialiased) fonts. See Smooth Font examples and Tools folder for the font generator. Fonts can include characters with 16 bit Unicodes (Basic Multilingual Plane). ![Example](https://i.imgur.com/xJF0Oz7.png) +# TFT_eSPI + An Arduino IDE compatible graphics and fonts library for ESP8266 and ESP32 processors with a driver for ILI9341, ILI9163, ST7735 and S6D02A1 based TFT displays that support SPI. The library also supports TFT displays designed for the Raspberry Pi that are based on a ILI9486 driver chip with a 480 x 320 pixel screen. This display must be of the Waveshare design and use a 16 bit serial interface based on the 74HC04, 74HC4040 and 2 x 74HC4094 logic chips. A modification to these displays is possible (see mod image in Tools folder) to make many graphics functions much faster (e.g. 23ms to clear the screen, 1.2ms to draw a 72 pixel high numeral). @@ -71,3 +73,4 @@ IO32 wired to IO36 ![Example](https://i.imgur.com/pUZn6lF.jpg) +**4. Add support for Touch support 8 bit "UNO" style TFTs with ESP32** From 1eee96a9168b30373867ba07561908d6e2900122 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Fri, 16 Mar 2018 23:19:32 +0000 Subject: [PATCH 098/150] Add ePaper to future section --- README.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index b286a0a..92393f3 100644 --- a/README.md +++ b/README.md @@ -73,4 +73,10 @@ IO32 wired to IO36 ![Example](https://i.imgur.com/pUZn6lF.jpg) -**4. Add support for Touch support 8 bit "UNO" style TFTs with ESP32** +**4. Add support for Touch support 8 bit "UNO" style TFTs with ESP32** +Adding touch support for UNO style displays with resistive screens may be possible - to be investigated. + +**4. Add support for ePaper displays** +The library was intended to support only TFT displays but using a Sprite as a 1 bit per pixel screen buffer permits support for the Waveshare 2 and 3 colour SPI ePaper displays. So far this is just an experiment and no code has been released for this: +![Example](https://i.imgur.com/L2tV129.jpg) + From 8ad0eea2cab3222626097a12969f435504d51df7 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Fri, 16 Mar 2018 23:21:31 +0000 Subject: [PATCH 099/150] Correct markup format errors --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 92393f3..67996c6 100644 --- a/README.md +++ b/README.md @@ -74,9 +74,12 @@ IO32 wired to IO36 ![Example](https://i.imgur.com/pUZn6lF.jpg) **4. Add support for Touch support 8 bit "UNO" style TFTs with ESP32** + Adding touch support for UNO style displays with resistive screens may be possible - to be investigated. **4. Add support for ePaper displays** + The library was intended to support only TFT displays but using a Sprite as a 1 bit per pixel screen buffer permits support for the Waveshare 2 and 3 colour SPI ePaper displays. So far this is just an experiment and no code has been released for this: + ![Example](https://i.imgur.com/L2tV129.jpg) From a6b532e5762e371651557645533e2d577e6e69ad Mon Sep 17 00:00:00 2001 From: Bodmer Date: Fri, 16 Mar 2018 23:28:10 +0000 Subject: [PATCH 100/150] Smaller ePaper image --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 67996c6..4a2e96e 100644 --- a/README.md +++ b/README.md @@ -81,5 +81,5 @@ Adding touch support for UNO style displays with resistive screens may be possib The library was intended to support only TFT displays but using a Sprite as a 1 bit per pixel screen buffer permits support for the Waveshare 2 and 3 colour SPI ePaper displays. So far this is just an experiment and no code has been released for this: -![Example](https://i.imgur.com/L2tV129.jpg) +![Example](https://i.imgur.com/L2tV129.jpg?1) From ba16be5aa85e6435490cd2e49625138d8c9ccca4 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Fri, 16 Mar 2018 23:38:05 +0000 Subject: [PATCH 101/150] Correct Unicode format reference --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4a2e96e..b921d3e 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ I have made some changes that will be uploaded soon that improves sprite and ima **2. Anti-aliased fonts - done 24/2/18** -I have been experimenting with anti-aliased font files in "vlw" format generated by the free [Processing IDE](https://processing.org/). This IDE can be used to generate font files from your computer's font set and include **any** Unicode characters. This means Greek, Japanese and any other UTF-16 glyphs can be used. +I have been experimenting with anti-aliased font files in "vlw" format generated by the free [Processing IDE](https://processing.org/). This IDE can be used to generate font files from your computer's font set and include **any** 16 bit Unicode characters. This means Greek, Japanese and any other UCS-2 glyphs can be used. Character arrays and Strings in UTF-8 format are supported. It would be possible to compress the vlw font files but the rendering performance to a TFT is still good when storing the font file(s) in SPIFFS. From bbacd3a8a3f7af6cf67d89d92109ec65f959bef4 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Thu, 22 Mar 2018 22:27:02 +0000 Subject: [PATCH 102/150] Fix issue in #110 Array name close[] clashes with a ESP32 function name. Renamed array to closeX[] --- examples/160 x 128/Flash_Bitmap2/Close.h | 2 +- examples/160 x 128/Flash_Bitmap2/Flash_Bitmap2.ino | 6 +++--- examples/320 x 240/TFT_Flash_Bitmap/Close.h | 2 +- examples/320 x 240/TFT_Flash_Bitmap/TFT_Flash_Bitmap.ino | 6 +++--- examples/480 x 320/Flash_Bitmap/Close.h | 2 +- examples/480 x 320/Flash_Bitmap/Flash_Bitmap.ino | 6 +++--- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/examples/160 x 128/Flash_Bitmap2/Close.h b/examples/160 x 128/Flash_Bitmap2/Close.h index c16d522..ce676c9 100644 --- a/examples/160 x 128/Flash_Bitmap2/Close.h +++ b/examples/160 x 128/Flash_Bitmap2/Close.h @@ -6,7 +6,7 @@ const uint16_t closeWidth = 32; const uint16_t closeHeight = 32; // The icon file can be created with the "UTFT ImageConverter 565" bitmap to .c file creation utility, more can be pasted in here -const unsigned short close[1024] PROGMEM={ +const unsigned short closeX[1024] PROGMEM={ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x30C3,0x4124,0x61C7,0x61C7,0x4124,0x30E3,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 0, 32 pixels 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x48E3,0xA249,0xEB8E,0xFCB2,0xFD14,0xFD75,0xFD96,0xFD34,0xFCF3,0xEBEF,0xA28A,0x4904,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 1, 64 pixels 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x58E3,0xC228,0xFC10,0xFD34,0xFE18,0xFE59,0xFE79,0xFE9A,0xFE9A,0xFE9A,0xFE9A,0xFE59,0xFD75,0xFC51,0xC28A,0x5904,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 2, 96 pixels diff --git a/examples/160 x 128/Flash_Bitmap2/Flash_Bitmap2.ino b/examples/160 x 128/Flash_Bitmap2/Flash_Bitmap2.ino index 09fe021..42099e9 100644 --- a/examples/160 x 128/Flash_Bitmap2/Flash_Bitmap2.ino +++ b/examples/160 x 128/Flash_Bitmap2/Flash_Bitmap2.ino @@ -28,7 +28,7 @@ TFT_eSPI tft = TFT_eSPI(); // Invoke library, pins defined in User_Setup.h // Include the header files that contain the icons -#include "alert.h" +#include "Alert.h" #include "Close.h" #include "Info.h" @@ -45,7 +45,7 @@ void setup() // Draw the icons drawIcon(info, (tft.width() - infoWidth)/2 - 50, (tft.height() - infoHeight)/2, infoWidth, infoHeight); drawIcon(alert, (tft.width() - alertWidth)/2, (tft.height() - alertHeight)/2, alertWidth, alertHeight); - drawIcon(close, (tft.width() - closeWidth)/2 + 50, (tft.height() - closeHeight)/2, closeWidth, closeHeight); + drawIcon(closeX, (tft.width() - closeWidth)/2 + 50, (tft.height() - closeHeight)/2, closeWidth, closeHeight); // Pause here to admire the icons! delay(4000); @@ -57,7 +57,7 @@ void loop() // Loop filling and clearing screen drawIcon(info, random(tft.width() - infoWidth), random(tft.height() - infoHeight), infoWidth, infoHeight); drawIcon(alert, random(tft.width() - alertWidth), random(tft.height() - alertHeight), alertWidth, alertHeight); - drawIcon(close, random(tft.width() - closeWidth), random(tft.height() - closeHeight), alertWidth, closeHeight); + drawIcon(closeX, random(tft.width() - closeWidth), random(tft.height() - closeHeight), alertWidth, closeHeight); // Clear screen after 100 x 3 = 300 icons drawn if (100 == count++) { diff --git a/examples/320 x 240/TFT_Flash_Bitmap/Close.h b/examples/320 x 240/TFT_Flash_Bitmap/Close.h index b58ec12..dc2a4fd 100644 --- a/examples/320 x 240/TFT_Flash_Bitmap/Close.h +++ b/examples/320 x 240/TFT_Flash_Bitmap/Close.h @@ -5,7 +5,7 @@ const uint16_t closeWidth = 32; const uint16_t closeHeight = 32; -const unsigned short close[1024] PROGMEM={ +const unsigned short closeX[1024] PROGMEM={ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x30C3,0x4124,0x61C7,0x61C7,0x4124,0x30E3,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 0, 32 pixels 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x48E3,0xA249,0xEB8E,0xFCB2,0xFD14,0xFD75,0xFD96,0xFD34,0xFCF3,0xEBEF,0xA28A,0x4904,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 1, 64 pixels 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x58E3,0xC228,0xFC10,0xFD34,0xFE18,0xFE59,0xFE79,0xFE9A,0xFE9A,0xFE9A,0xFE9A,0xFE59,0xFD75,0xFC51,0xC28A,0x5904,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 2, 96 pixels diff --git a/examples/320 x 240/TFT_Flash_Bitmap/TFT_Flash_Bitmap.ino b/examples/320 x 240/TFT_Flash_Bitmap/TFT_Flash_Bitmap.ino index 780dab1..82e09eb 100644 --- a/examples/320 x 240/TFT_Flash_Bitmap/TFT_Flash_Bitmap.ino +++ b/examples/320 x 240/TFT_Flash_Bitmap/TFT_Flash_Bitmap.ino @@ -24,7 +24,7 @@ TFT_eSPI tft = TFT_eSPI(); // Invoke custom library // Include the header files that contain the icons -#include "alert.h" +#include "Alert.h" #include "Close.h" #include "Info.h" @@ -41,7 +41,7 @@ void setup() // Draw the icons drawIcon(info, 100, 100, infoWidth, infoHeight); drawIcon(alert, 140, 100, alertWidth, alertHeight); - drawIcon(close, 180, 100, closeWidth, closeHeight); + drawIcon(closeX, 180, 100, closeWidth, closeHeight); // Pause here to admire the icons! delay(2000); @@ -53,7 +53,7 @@ void loop() // Loop filling and clearing screen drawIcon(info, random(tft.width() - infoWidth), random(tft.height() - infoHeight), infoWidth, infoHeight); drawIcon(alert, random(tft.width() - alertWidth), random(tft.height() - alertHeight), alertWidth, alertHeight); - drawIcon(close, random(tft.width() - closeWidth), random(tft.height() - closeHeight), alertWidth, closeHeight); + drawIcon(closeX, random(tft.width() - closeWidth), random(tft.height() - closeHeight), alertWidth, closeHeight); // Clear screen after 100 x 3 = 300 icons drawn if (100 == count++) { diff --git a/examples/480 x 320/Flash_Bitmap/Close.h b/examples/480 x 320/Flash_Bitmap/Close.h index c16d522..ce676c9 100644 --- a/examples/480 x 320/Flash_Bitmap/Close.h +++ b/examples/480 x 320/Flash_Bitmap/Close.h @@ -6,7 +6,7 @@ const uint16_t closeWidth = 32; const uint16_t closeHeight = 32; // The icon file can be created with the "UTFT ImageConverter 565" bitmap to .c file creation utility, more can be pasted in here -const unsigned short close[1024] PROGMEM={ +const unsigned short closeX[1024] PROGMEM={ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x30C3,0x4124,0x61C7,0x61C7,0x4124,0x30E3,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 0, 32 pixels 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x48E3,0xA249,0xEB8E,0xFCB2,0xFD14,0xFD75,0xFD96,0xFD34,0xFCF3,0xEBEF,0xA28A,0x4904,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 1, 64 pixels 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x58E3,0xC228,0xFC10,0xFD34,0xFE18,0xFE59,0xFE79,0xFE9A,0xFE9A,0xFE9A,0xFE9A,0xFE59,0xFD75,0xFC51,0xC28A,0x5904,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 2, 96 pixels diff --git a/examples/480 x 320/Flash_Bitmap/Flash_Bitmap.ino b/examples/480 x 320/Flash_Bitmap/Flash_Bitmap.ino index fb58180..d3ca697 100644 --- a/examples/480 x 320/Flash_Bitmap/Flash_Bitmap.ino +++ b/examples/480 x 320/Flash_Bitmap/Flash_Bitmap.ino @@ -31,7 +31,7 @@ TFT_eSPI tft = TFT_eSPI(); // Invoke custom library with default width and height // Include the header files that contain the icons -#include "alert.h" +#include "Alert.h" #include "Close.h" #include "Info.h" @@ -48,7 +48,7 @@ void setup() // Draw the icons drawIcon(info, (tft.width() - infoWidth)/2 - 50, (tft.height() - infoHeight)/2, infoWidth, infoHeight); drawIcon(alert, (tft.width() - alertWidth)/2, (tft.height() - alertHeight)/2, alertWidth, alertHeight); - drawIcon(close, (tft.width() - closeWidth)/2 + 50, (tft.height() - closeHeight)/2, closeWidth, closeHeight); + drawIcon(closeX, (tft.width() - closeWidth)/2 + 50, (tft.height() - closeHeight)/2, closeWidth, closeHeight); // Pause here to admire the icons! delay(4000); @@ -60,7 +60,7 @@ void loop() // Loop filling and clearing screen drawIcon(info, random(tft.width() - infoWidth), random(tft.height() - infoHeight), infoWidth, infoHeight); drawIcon(alert, random(tft.width() - alertWidth), random(tft.height() - alertHeight), alertWidth, alertHeight); - drawIcon(close, random(tft.width() - closeWidth), random(tft.height() - closeHeight), alertWidth, closeHeight); + drawIcon(closeX, random(tft.width() - closeWidth), random(tft.height() - closeHeight), alertWidth, closeHeight); // Clear screen after 100 x 3 = 300 icons drawn if (100 == count++) { From 0f22d124f6307460d538a156ef47ff64f0f85fb6 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Thu, 22 Mar 2018 22:33:01 +0000 Subject: [PATCH 103/150] Add note rthat Read ID sketch does not work with parallel displays --- examples/320 x 240/Read_ID_bitbash/Read_ID_bitbash.ino | 2 ++ 1 file changed, 2 insertions(+) diff --git a/examples/320 x 240/Read_ID_bitbash/Read_ID_bitbash.ino b/examples/320 x 240/Read_ID_bitbash/Read_ID_bitbash.ino index 222904b..599d354 100644 --- a/examples/320 x 240/Read_ID_bitbash/Read_ID_bitbash.ino +++ b/examples/320 x 240/Read_ID_bitbash/Read_ID_bitbash.ino @@ -4,6 +4,8 @@ // Bit bashes SPI so it does NOT assume hardware SPI wired up // No other libraries are needed +// NOTE: This sketch does not work with parallel displays! + // Original author unknown // Adapted by Bodmer 22/5/16, updated 16/9/16 From 1db0c30b813f96f7c132a5fecaff540f28d9005e Mon Sep 17 00:00:00 2001 From: Bodmer Date: Sun, 25 Mar 2018 18:55:25 +0100 Subject: [PATCH 104/150] Add new drivers to list --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b921d3e..3c1dc58 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ Update 24th February 2018: Added new smooth (antialiased) fonts. See Smooth Font # TFT_eSPI -An Arduino IDE compatible graphics and fonts library for ESP8266 and ESP32 processors with a driver for ILI9341, ILI9163, ST7735 and S6D02A1 based TFT displays that support SPI. +An Arduino IDE compatible graphics and fonts library for ESP8266 and ESP32 processors with a driver for ILI9341, ILI9163, ST7735, S6D02A1, ILI9481, ILI9488, ILI9486 and HX8357 based TFT displays that support SPI. 8 bit parallel interface TFTs (e.g. UNO format mcufriend shilds) can used with ESP32 The library also supports TFT displays designed for the Raspberry Pi that are based on a ILI9486 driver chip with a 480 x 320 pixel screen. This display must be of the Waveshare design and use a 16 bit serial interface based on the 74HC04, 74HC4040 and 2 x 74HC4094 logic chips. A modification to these displays is possible (see mod image in Tools folder) to make many graphics functions much faster (e.g. 23ms to clear the screen, 1.2ms to draw a 72 pixel high numeral). From 9276b0162db4488bb011ac11fbbb4a946b07c1e3 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Mon, 26 Mar 2018 01:02:23 +0100 Subject: [PATCH 105/150] Add support for 1 bit per pixel in Sprite class This is stage one of support for ePaper displays from Waveshare. Examples for 1 bit per pixel Sprites and 2 or 3 colour ePaper displays to follow soon. --- Extensions/Smooth_font.cpp | 7 +- Extensions/Sprite.cpp | 477 ++++++++++++------ Extensions/Sprite.h | 30 +- TFT_Drivers/EPD_Defines.h | 27 + TFT_eSPI.cpp | 395 ++++++++++++--- TFT_eSPI.h | 94 +++- .../FontFiles/Final-Frontier28.vlw | Bin 0 -> 25287 bytes User_Setup_Select.h | 48 +- User_Setups/SetupX_Template.h | 8 +- User_Setups/ePaper_Template.h | 76 +++ keywords.txt | 7 +- library.json | 4 - 12 files changed, 883 insertions(+), 290 deletions(-) create mode 100644 TFT_Drivers/EPD_Defines.h create mode 100644 Tools/Create_Smooth_Font/Create_font/FontFiles/Final-Frontier28.vlw create mode 100644 User_Setups/ePaper_Template.h diff --git a/Extensions/Smooth_font.cpp b/Extensions/Smooth_font.cpp index b5559e1..14ed796 100644 --- a/Extensions/Smooth_font.cpp +++ b/Extensions/Smooth_font.cpp @@ -74,14 +74,14 @@ void TFT_eSPI::loadFont(String fontName) */ + unloadFont(); + _gFontFilename = "/" + fontName + ".vlw"; fontFile = SPIFFS.open( _gFontFilename, "r"); if(!fontFile) return; - //unloadFont(); - fontFile.seek(0, fs::SeekSet); gFont.gCount = (uint16_t)readInt32(); // glyph count in file @@ -230,7 +230,8 @@ void TFT_eSPI::unloadFont( void ) free(gBitmap); gBitmap = NULL; } - fontFile.close(); + + if(fontFile) fontFile.close(); fontLoaded = false; } diff --git a/Extensions/Sprite.cpp b/Extensions/Sprite.cpp index ab5d368..90fadae 100644 --- a/Extensions/Sprite.cpp +++ b/Extensions/Sprite.cpp @@ -20,7 +20,7 @@ TFT_eSprite::TFT_eSprite(TFT_eSPI *tft) _iwidth = 0; // Initialise width and height to 0 (it does not exist yet) _iheight = 0; - _bpp16 = true; + _bpp = 16; _iswapBytes = false; // Do not swap pushImage colour bytes by default _created = false; @@ -33,7 +33,7 @@ TFT_eSprite::TFT_eSprite(TFT_eSPI *tft) _xptr = 0; // pushColor coordinate _yptr = 0; - _icursor_y = _icursor_x = 0; // Text cursor position + this->cursor_y = this->cursor_x = 0; // Text cursor position } @@ -42,22 +42,18 @@ TFT_eSprite::TFT_eSprite(TFT_eSPI *tft) ** Description: Create a sprite (bitmap) of defined width and height *************************************************************************************x*/ // cast returned value to (uint8_t*) for 8 bit or (uint16_t*) for 16 bit colours -void* TFT_eSprite::createSprite(int16_t w, int16_t h) +void* TFT_eSprite::createSprite(int16_t w, int16_t h, uint8_t frames) { - if ( _created ) - { - if ( _bpp16 ) return _img; - return _img8; - } + if ( _created ) return _img8_1; if ( w < 1 || h < 1 ) return NULL; - _iwidth = w; - _iheight = h; + _iwidth = _dwidth = w; + _iheight = _dheight = h; - _icursor_x = 0; - _icursor_y = 0; + this->cursor_x = 0; + this->cursor_y = 0; // Default scroll rectangle and gap fill colour _sx = 0; @@ -69,28 +65,76 @@ void* TFT_eSprite::createSprite(int16_t w, int16_t h) // Add one extra "off screen" pixel to point out-of-bounds setWindow() coordinates // this means push/writeColor functions do not need additional bounds checks and // hence will run faster in normal circumstances. - if(_bpp16) + if (_bpp == 16) { - _img = (uint16_t*) calloc(w * h + 1, sizeof(uint16_t)); + _img8_1 = ( uint8_t*) calloc(w * h + 1, sizeof(uint16_t)); + _img8_2 = _img8_1; + _img = (uint16_t*) _img8_1; + if (_img) { _created = true; return _img; } } - else + + else if (_bpp == 8) { - _img8 = ( uint8_t*) calloc(w * h + 1, sizeof(uint8_t)); + _img8_1 = ( uint8_t*) calloc(w * h + 1, sizeof(uint8_t)); + + if (_img8_1) + { + _img8 = _img8_1; + _img8_2 = _img8_1; + _created = true; + return _img8; + } + } + + else // Must be 1 bpp + { + //_dwidth Display width+height in pixels always in rotation 0 orientation + //_dheight Not swapped for sprite rotations + // Note: for 1bpp _iwidth and _iheight are swapped during Sprite rotations + + w = (w+7) & 0xFFF8; // width should be the multiple of 8 bits to be compatible with epdpaint + _iwidth = w; // _iwidth is rounded up to be multiple of 8, so might not be = _dwidth + _bitwidth = w; + + if (frames > 2) frames = 2; // Currently restricted to 2 frame buffers + if (frames < 1) frames = 1; + _img8 = ( uint8_t*) calloc(frames * (w>>3) * h + frames, sizeof(uint8_t)); // extra pixel added + if (_img8) { _created = true; - return _img8; + _img8_1 = _img8; + _img8_2 = _img8 + ( (w>>3) * h + 1 ); + return _img8_1; } } return NULL; } +/*************************************************************************************** +** Function name: frameBuffer +** Description: For 1 bpp Sprites, select the frame used for graphics +*************************************************************************************x*/ +// Frames are numbered 1 and 2 +void* TFT_eSprite::frameBuffer(int8_t f) +{ + if (!_created) return NULL; + + if (_bpp == 16) return _img; + + if (_bpp == 8) return _img8; + + if ( f == 2 ) _img8 = _img8_2; + else _img8 = _img8_1; + + return _img8; +} /*************************************************************************************** ** Function name: setDepth @@ -100,15 +144,12 @@ void* TFT_eSprite::createSprite(int16_t w, int16_t h) void* TFT_eSprite::setColorDepth(int8_t b) { // Can't change an existing sprite's colour depth so delete it - if (_created) - { - if (_bpp16) free(_img); - else free(_img8); - } + if (_created) free(_img8_1); // Now define the new colour depth - if ( b > 8 ) _bpp16 = true; // Bytes per pixel - else _bpp16 = false; + if ( b > 8 ) _bpp = 16; // Bytes per pixel + else if ( b > 1 ) _bpp = 8; + else _bpp = 1; // If it existed, re-create the sprite with the new colour depth if (_created) @@ -120,6 +161,17 @@ void* TFT_eSprite::setColorDepth(int8_t b) return NULL; } +/*************************************************************************************** +** Function name: setBitmapColor +** Description: Set the foreground foreground and background colour +***************************************************************************************/ +void TFT_eSprite::setBitmapColor(uint16_t c, uint16_t b) +{ + if (c == b) b = ~c; + _tft->bitmap_fg = c; + _tft->bitmap_bg = b; +} + /*************************************************************************************** ** Function name: deleteSprite @@ -129,8 +181,7 @@ void TFT_eSprite::deleteSprite(void) { if (!_created ) return; - if (_bpp16) free(_img); - else free(_img8); + free(_img8_1); _created = false; } @@ -142,11 +193,12 @@ void TFT_eSprite::deleteSprite(void) *************************************************************************************x*/ void TFT_eSprite::pushSprite(int32_t x, int32_t y) { - if (!_created ) return; + if (!_created) return; + + if (_bpp == 16) _tft->pushImage(x, y, _iwidth, _iheight, _img ); + + else _tft->pushImage(x, y, _dwidth, _dheight, _img8, (bool)(_bpp == 8)); - if (_bpp16) _tft->pushImage(x, y, _iwidth, _iheight, _img ); - //if (_bpp16) TFT_eSPI::pushImage(x, y, _iwidth, _iheight, _img ); - else _tft->pushImage(x, y, _iwidth, _iheight, _img8); } @@ -156,14 +208,15 @@ void TFT_eSprite::pushSprite(int32_t x, int32_t y) *************************************************************************************x*/ void TFT_eSprite::pushSprite(int32_t x, int32_t y, uint16_t transp) { - if (!_created ) return; + if (!_created) return; - if (_bpp16) _tft->pushImage(x, y, _iwidth, _iheight, _img, transp ); - else + if (_bpp == 16) _tft->pushImage(x, y, _iwidth, _iheight, _img, transp ); + else if (_bpp == 8) { transp = (uint8_t)((transp & 0xE000)>>8 | (transp & 0x0700)>>6 | (transp & 0x0018)>>3); - _tft->pushImage(x, y, _iwidth, _iheight, _img8, (uint8_t)transp); + _tft->pushImage(x, y, _dwidth, _dheight, _img8, (uint8_t)transp, (bool)true); } + else _tft->pushImage(x, y, _dwidth, _dheight, _img8, 0, (bool)false); } @@ -173,24 +226,48 @@ void TFT_eSprite::pushSprite(int32_t x, int32_t y, uint16_t transp) *************************************************************************************x*/ uint16_t TFT_eSprite::readPixel(int32_t x, int32_t y) { - if (!_created ) return 0; + if ((x < 0) || (x >= _iwidth) || (y < 0) || (y >= _iheight) || !_created) return 0; - if (_bpp16) + if (_bpp == 16) { uint16_t color = _img[x + y * _iwidth]; return (color >> 8) | (color << 8); } - - uint16_t color = _img8[x + y * _iwidth]; - if (color != 0) + + if (_bpp == 8) { + uint16_t color = _img8[x + y * _iwidth]; + if (color != 0) + { uint8_t blue[] = {0, 11, 21, 31}; - color = (color & 0xE0)<<8 | (color & 0xC0)<<5 - | (color & 0x1C)<<6 | (color & 0x1C)<<3 - | blue[color & 0x03]; + color = (color & 0xE0)<<8 | (color & 0xC0)<<5 + | (color & 0x1C)<<6 | (color & 0x1C)<<3 + | blue[color & 0x03]; + } + return color; } - return color; + if (_rotation == 1) + { + uint16_t tx = x; + x = _dwidth - y - 1; + y = tx; + } + else if (_rotation == 2) + { + x = _dwidth - x - 1; + y = _dheight - y - 1; + } + else if (_rotation == 3) + { + uint16_t tx = x; + x = y; + y = _dheight - tx - 1; + } + + uint16_t color = (_img8[(x + y * _bitwidth)>>3] << (x & 0x7)) & 0x80; + + return color >> 7; } @@ -198,11 +275,12 @@ uint16_t TFT_eSprite::readPixel(int32_t x, int32_t y) ** Function name: pushImage ** Description: push 565 colour image into a defined area of a sprite *************************************************************************************x*/ +// TODO Need to add more area boundary checks void TFT_eSprite::pushImage(int32_t x, int32_t y, uint32_t w, uint32_t h, uint16_t *data) { - if ((x > _iwidth) || (y > _iheight) || (w == 0) || (h == 0) || !_created) return; + if ((x >= _iwidth) || (y >= _iheight) || (w == 0) || (h == 0) || !_created) return; - if (_bpp16) + if (_bpp == 16) { for (uint32_t yp = y; yp < y + h; yp++) { @@ -214,7 +292,7 @@ void TFT_eSprite::pushImage(int32_t x, int32_t y, uint32_t w, uint32_t h, uint1 } } } - else + else if (_bpp == 8) { for (uint32_t yp = y; yp < y + h; yp++) { @@ -226,6 +304,7 @@ void TFT_eSprite::pushImage(int32_t x, int32_t y, uint32_t w, uint32_t h, uint1 } } } + // TODO Currently does nothing for 1 bpp } @@ -233,11 +312,12 @@ void TFT_eSprite::pushImage(int32_t x, int32_t y, uint32_t w, uint32_t h, uint1 ** Function name: pushImage ** Description: push 565 colour FLASH (PROGMEM) image into a defined area *************************************************************************************x*/ +// TODO Need to add more area boundary checks void TFT_eSprite::pushImage(int32_t x, int32_t y, uint32_t w, uint32_t h, const uint16_t *data) { - if ((x > _iwidth) || (y > _iheight) || (w == 0) || (h == 0) || !_created) return; + if ((x >= _iwidth) || (y >= _iheight) || (w == 0) || (h == 0) || !_created) return; - if (_bpp16) + if (_bpp == 16) { for (uint32_t yp = y; yp < y + h; yp++) { @@ -249,7 +329,7 @@ void TFT_eSprite::pushImage(int32_t x, int32_t y, uint32_t w, uint32_t h, const } } } - else + else if (_bpp == 8) { for (uint32_t yp = y; yp < y + h; yp++) { @@ -261,6 +341,7 @@ void TFT_eSprite::pushImage(int32_t x, int32_t y, uint32_t w, uint32_t h, const } } } + // TODO Currently does nothing for 1 bpp } @@ -334,11 +415,13 @@ void TFT_eSprite::pushColor(uint32_t color) if (!_created ) return; // Write the colour to RAM in set window - if (_bpp16) + if (_bpp == 16) _img [_xptr + _yptr * _iwidth] = (uint16_t) (color >> 8) | (color << 8); - else + else if (_bpp == 8) _img8[_xptr + _yptr * _iwidth] = (uint8_t )((color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3); + + else drawPixel(_xptr, _yptr, color); // Increment x _xptr++; @@ -363,12 +446,14 @@ void TFT_eSprite::pushColor(uint32_t color, uint16_t len) if (!_created ) return; uint16_t pixelColor; - if (_bpp16) + if (_bpp == 16) pixelColor = (uint16_t) (color >> 8) | (color << 8); - else + else if (_bpp == 8) pixelColor = (color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3; + // else Nothing to do for 1bpp + while(len--) writeColor(pixelColor); } @@ -382,10 +467,12 @@ void TFT_eSprite::writeColor(uint16_t color) if (!_created ) return; // Write 16 bit RGB 565 encoded colour to RAM - if (_bpp16) _img [_xptr + _yptr * _iwidth] = color; + if (_bpp == 16) _img [_xptr + _yptr * _iwidth] = color; // Write 8 bit RGB 332 encoded colour to RAM - else _img8[_xptr + _yptr * _iwidth] = (uint8_t) color; + else if (_bpp == 8) _img8[_xptr + _yptr * _iwidth] = (uint8_t) color; + + else drawPixel(_xptr, _yptr, color); // Increment x _xptr++; @@ -466,7 +553,7 @@ void TFT_eSprite::scroll(int16_t dx, int16_t dy) uint32_t typ = tx + ty * _iwidth; // Now move the pixels in RAM - if (_bpp16) + if (_bpp == 16) { while (h--) { // move pixel lines (to, from, byte count) @@ -475,7 +562,7 @@ void TFT_eSprite::scroll(int16_t dx, int16_t dy) fyp += iw; } } - else + else if (_bpp == 8) { while (h--) { // move pixel lines (to, from, byte count) @@ -484,6 +571,7 @@ void TFT_eSprite::scroll(int16_t dx, int16_t dy) fyp += iw; } } + else return; // TODO add scroll for 1 bpp // Fill the gap left by the scrolling if (dx > 0) fillRect(_sx, _sy, dx, _sh, _scolor); @@ -502,13 +590,18 @@ void TFT_eSprite::fillSprite(uint32_t color) if (!_created ) return; // Use memset if possible as it is super fast - if(( (uint8_t)color == (uint8_t)(color>>8) ) && _bpp16) + if(( (uint8_t)color == (uint8_t)(color>>8) ) && _bpp == 16) memset(_img, (uint8_t)color, _iwidth * _iheight * 2); - else if (!_bpp16) + else if (_bpp == 8) { color = (color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3; memset(_img8, (uint8_t)color, _iwidth * _iheight); } + else if (_bpp == 1) + { + if(color) memset(_img8, 0xFF, (_iwidth>>3) * _iheight + 1); + else memset(_img8, 0x00, (_iwidth>>3) * _iheight + 1); + } else fillRect(0, 0, _iwidth, _iheight, color); } @@ -518,11 +611,12 @@ void TFT_eSprite::fillSprite(uint32_t color) ** Function name: setCursor ** Description: Set the sprite text cursor x,y position *************************************************************************************x*/ -void TFT_eSprite::setCursor(int16_t x, int16_t y) -{ - _icursor_x = x; - _icursor_y = y; -} +// Not needed - using TFT_eSPI class function and this->cursor_x/y +//void TFT_eSprite::setCursor(int16_t x, int16_t y) +//{ +// this->cursor_x = x; +// this->cursor_y = y; +//} /*************************************************************************************** @@ -533,7 +627,12 @@ void TFT_eSprite::setCursor(int16_t x, int16_t y) int16_t TFT_eSprite::width(void) { if (!_created ) return 0; - return _iwidth; + + if (_bpp > 1) return _iwidth; + + if (_rotation == 1 || _rotation == 3) return _dheight; + + return _dwidth; } @@ -544,7 +643,38 @@ int16_t TFT_eSprite::width(void) int16_t TFT_eSprite::height(void) { if (!_created ) return 0; - return _iheight; + + if (_bpp > 1) return _iheight; + + if (_rotation == 1 || _rotation == 3) return _dwidth; + + return _dheight; +} + + +/*************************************************************************************** +** Function name: setRotation +** Description: Rotate coordinate frame for 1bpp sprite +*************************************************************************************x*/ +// Does nothing for 8 and 16 bpp sprites. TODO allow rotation of these sprites +void TFT_eSprite::setRotation(uint8_t rotation) +{ + _rotation = rotation; + if (rotation == 0 && _iwidth > _iheight) swap_coord(_iwidth, _iheight); + if (rotation == 1 && _iwidth < _iheight) swap_coord(_iwidth, _iheight); + if (rotation == 2 && _iwidth > _iheight) swap_coord(_iwidth, _iheight); + if (rotation == 3 && _iwidth < _iheight) swap_coord(_iwidth, _iheight); +} + + +/*************************************************************************************** +** Function name: getRotation +** Description: Get rotation for 1bpp sprite +*************************************************************************************x*/ + +uint8_t TFT_eSprite::getRotation(void) +{ + return _rotation; } @@ -558,15 +688,38 @@ void TFT_eSprite::drawPixel(uint32_t x, uint32_t y, uint32_t color) // this make bounds checking a bit faster if ((x >= _iwidth) || (y >= _iheight) || !_created) return; - if (_bpp16) + if (_bpp == 16) { color = (color >> 8) | (color << 8); _img[x+y*_iwidth] = (uint16_t) color; } - else + else if (_bpp == 8) { _img8[x+y*_iwidth] = (uint8_t)((color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3); } + else // 1 bpp + { + if (_rotation == 1) + { + uint16_t tx = x; + x = _dwidth - y - 1; + y = tx; + } + else if (_rotation == 2) + { + x = _dwidth - x - 1; + y = _dheight - y - 1; + } + else if (_rotation == 3) + { + uint16_t tx = x; + x = y; + y = _dheight - tx - 1; + } + + if (color) _img8[(x + y * _bitwidth)>>3] |= (0x80 >> (x & 0x7)); + else _img8[(x + y * _bitwidth)>>3] &= ~(0x80 >> (x & 0x7)); + } } @@ -641,17 +794,25 @@ void TFT_eSprite::drawFastVLine(int32_t x, int32_t y, int32_t h, uint32_t color) if (h < 1) return; - if (_bpp16) + if (_bpp == 16) { color = (color >> 8) | (color << 8); int32_t yp = x + _iwidth * y; while (h--) {_img[yp] = (uint16_t) color; yp += _iwidth;} } - else + else if (_bpp == 8) { color = (color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3; while (h--) _img8[x + _iwidth * y++] = (uint8_t) color; } + else + { + while (h--) + { + drawPixel(x, y, color); + y++; + } + } } @@ -670,16 +831,24 @@ void TFT_eSprite::drawFastHLine(int32_t x, int32_t y, int32_t w, uint32_t color) if (w < 1) return; - if (_bpp16) + if (_bpp == 16) { color = (color >> 8) | (color << 8); while (w--) _img[_iwidth * y + x++] = (uint16_t) color; } - else + else if (_bpp == 8) { color = (color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3; memset(_img8+_iwidth * y + x, (uint8_t)color, w); } + else + { + while (w--) + { + drawPixel(x, y, color); + x++; + } + } } @@ -700,7 +869,7 @@ void TFT_eSprite::fillRect(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t int32_t yp = _iwidth * y + x; - if (_bpp16) + if (_bpp == 16) { color = (color >> 8) | (color << 8); uint32_t iw = w; @@ -713,15 +882,25 @@ void TFT_eSprite::fillRect(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t memcpy( _img+yp, _img+ys, w<<1); } } - else + else if (_bpp == 8) { color = (color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3; while (h--) { - memset(_img8 + yp, (uint8_t)color, w); + memset(_img8 + yp, (uint8_t)color, w); yp += _iwidth; } } + else + { + while (h--) + { + int32_t ww = w; + int32_t xx = x; + while (ww--) drawPixel(xx++, y, color); + y++; + } + } } @@ -734,21 +913,23 @@ size_t TFT_eSprite::write(uint8_t utf8) if (utf8 == '\r') return 1; #ifdef SMOOTH_FONT - if(fontLoaded) + if(this->fontLoaded) { uint16_t unicode = decodeUTF8(utf8); if (unicode < 32 && utf8 != '\n') return 0; - fontFile = SPIFFS.open( _gFontFilename, "r" ); + //fontFile = SPIFFS.open( _gFontFilename, "r" ); + //fontFile = SPIFFS.open( this->_gFontFilename, "r" ); - if(!fontFile) - { - fontLoaded = false; - return 0; - } + //if(!fontFile) + //{ + // fontLoaded = false; + // return 0; + //} + //Serial.print("Decoded Unicode = 0x");Serial.println(unicode,HEX); drawGlyph(unicode); - fontFile.close(); + //fontFile.close(); return 0; } #endif @@ -818,18 +999,18 @@ size_t TFT_eSprite::write(uint8_t utf8) if (utf8 == '\n') { - _icursor_y += height; - _icursor_x = 0; + this->cursor_y += height; + this->cursor_x = 0; } else { - if (textwrapX && (_icursor_x + width * textsize > _iwidth)) + if (textwrapX && (this->cursor_x + width * textsize > _iwidth)) { - _icursor_y += height; - _icursor_x = 0; + this->cursor_y += height; + this->cursor_x = 0; } - if (textwrapY && (_icursor_y >= _iheight)) _icursor_y = 0; - _icursor_x += drawChar(uniCode, _icursor_x, _icursor_y, textfont); + if (textwrapY && (this->cursor_y >= _iheight)) this->cursor_y = 0; + this->cursor_x += drawChar(uniCode, this->cursor_x, this->cursor_y, textfont); } //<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -839,8 +1020,8 @@ size_t TFT_eSprite::write(uint8_t utf8) { if(utf8 == '\n') { - _icursor_x = 0; - _icursor_y += (int16_t)textsize * (uint8_t)pgm_read_byte(&gfxFont->yAdvance); + this->cursor_x = 0; + this->cursor_y += (int16_t)textsize * (uint8_t)pgm_read_byte(&gfxFont->yAdvance); } else { if (uniCode > (uint8_t)pgm_read_byte(&gfxFont->last )) return 0; if (uniCode < (uint8_t)pgm_read_byte(&gfxFont->first)) return 0; @@ -851,15 +1032,15 @@ size_t TFT_eSprite::write(uint8_t utf8) h = pgm_read_byte(&glyph->height); if((w > 0) && (h > 0)) { // Is there an associated bitmap? int16_t xo = (int8_t)pgm_read_byte(&glyph->xOffset); - if(textwrapX && ((_icursor_x + textsize * (xo + w)) > _iwidth)) { + if(textwrapX && ((this->cursor_x + textsize * (xo + w)) > _iwidth)) { // Drawing character would go off right edge; wrap to new line - _icursor_x = 0; - _icursor_y += (int16_t)textsize * (uint8_t)pgm_read_byte(&gfxFont->yAdvance); + this->cursor_x = 0; + this->cursor_y += (int16_t)textsize * (uint8_t)pgm_read_byte(&gfxFont->yAdvance); } - if (textwrapY && (_icursor_y >= _iheight)) _icursor_y = 0; - drawChar(_icursor_x, _icursor_y, uniCode, textcolor, textbgcolor, textsize); + if (textwrapY && (this->cursor_y >= _iheight)) this->cursor_y = 0; + drawChar(this->cursor_x, this->cursor_y, uniCode, textcolor, textbgcolor, textsize); } - _icursor_x += pgm_read_byte(&glyph->xAdvance) * (int16_t)textsize; + this->cursor_x += pgm_read_byte(&glyph->xAdvance) * (int16_t)textsize; } } #endif // LOAD_GFXFF @@ -1151,9 +1332,9 @@ int16_t TFT_eSprite::drawChar(unsigned int uniCode, int x, int y, int font) w *= height; // Now w is total number of pixels in the character if (textcolor != textbgcolor) fillRect(x, pY, width * textsize, textsize * height, textbgcolor); - int16_t color; - if (_bpp16) color = (textcolor >> 8) | (textcolor << 8); - else color = ((textcolor & 0xE000)>>8 | (textcolor & 0x0700)>>6 | (textcolor & 0x0018)>>3); + int16_t color = textcolor; + if (_bpp == 16) color = (textcolor >> 8) | (textcolor << 8); + else if (_bpp == 8) color = ((textcolor & 0xE000)>>8 | (textcolor & 0x0700)>>6 | (textcolor & 0x0018)>>3); int px = 0, py = pY; // To hold character block start and end column and row values int pc = 0; // Pixel count uint8_t np = textsize * textsize; // Number of pixels in a drawn pixel @@ -1212,17 +1393,17 @@ void TFT_eSprite::drawGlyph(uint16_t code) if (code < 0x21) { if (code == 0x20) { - if (_created) _icursor_x += _tft->gFont.spaceWidth; - else _tft->cursor_x += _tft->gFont.spaceWidth; + if (_created) this->cursor_x += this->gFont.spaceWidth; + else this->cursor_x += this->gFont.spaceWidth; return; } if (code == '\n') { if (_created) { - _icursor_x = 0; - _icursor_y += _tft->gFont.yAdvance; - if (_icursor_y >= _height) _icursor_y = 0; + this->cursor_x = 0; + this->cursor_y += this->gFont.yAdvance; + if (this->cursor_y >= _height) this->cursor_y = 0; return; } else @@ -1236,10 +1417,10 @@ void TFT_eSprite::drawGlyph(uint16_t code) } uint16_t gNum = 0; - bool found = _tft->getUnicodeIndex(code, &gNum); + bool found = this->getUnicodeIndex(code, &gNum); - uint16_t fg = _tft->textcolor; - uint16_t bg = _tft->textbgcolor; + uint16_t fg = this->textcolor; + uint16_t bg = this->textbgcolor; if (found) { @@ -1248,59 +1429,59 @@ void TFT_eSprite::drawGlyph(uint16_t code) if (newSprite) { - createSprite(_tft->gWidth[gNum], _tft->gFont.yAdvance); + createSprite(this->gWidth[gNum], this->gFont.yAdvance); if(bg) fillSprite(bg); - _icursor_x = -_tft->gdX[gNum]; - _icursor_y = 0; + this->cursor_x = -this->gdX[gNum]; + this->cursor_y = 0; } - fontFile.seek(_tft->gBitmap[gNum], fs::SeekSet); // This is slow for a significant position shift! + this->fontFile.seek(this->gBitmap[gNum], fs::SeekSet); // This is slow for a significant position shift! - uint8_t pbuffer[_tft->gWidth[gNum]]; + uint8_t pbuffer[this->gWidth[gNum]]; uint16_t xs = 0; uint16_t dl = 0; - for (int y = 0; y < _tft->gHeight[gNum]; y++) + for (int y = 0; y < this->gHeight[gNum]; y++) { - fontFile.read(pbuffer, _tft->gWidth[gNum]); - for (int x = 0; x < _tft->gWidth[gNum]; x++) + this->fontFile.read(pbuffer, this->gWidth[gNum]); + for (int x = 0; x < this->gWidth[gNum]; x++) { uint8_t pixel = pbuffer[x]; if (pixel) { if (pixel != 0xFF) { - if (dl) { drawFastHLine( xs, y + _icursor_y + _tft->gFont.maxAscent - _tft->gdY[gNum], dl, fg); dl = 0; } - drawPixel(x + _icursor_x + _tft->gdX[gNum], y + _icursor_y + _tft->gFont.maxAscent - _tft->gdY[gNum], alphaBlend(pixel, fg, bg)); + if (dl) { drawFastHLine( xs, y + this->cursor_y + this->gFont.maxAscent - this->gdY[gNum], dl, fg); dl = 0; } + if (pixel>127) drawPixel(x + this->cursor_x + this->gdX[gNum], y + this->cursor_y + this->gFont.maxAscent - this->gdY[gNum], alphaBlend(pixel, fg, bg)); } else { - if (dl==0) xs = x + _icursor_x + _tft->gdX[gNum]; + if (dl==0) xs = x + this->cursor_x + this->gdX[gNum]; dl++; } } else { - if (dl) { drawFastHLine( xs, y + _icursor_y + _tft->gFont.maxAscent - _tft->gdY[gNum], dl, fg); dl = 0; } + if (dl) { drawFastHLine( xs, y + this->cursor_y + this->gFont.maxAscent - this->gdY[gNum], dl, fg); dl = 0; } } } - if (dl) { drawFastHLine( xs, y + _icursor_y + _tft->gFont.maxAscent - _tft->gdY[gNum], dl, fg); dl = 0; } + if (dl) { drawFastHLine( xs, y + this->cursor_y + this->gFont.maxAscent - this->gdY[gNum], dl, fg); dl = 0; } } if (newSprite) { - pushSprite(_tft->cursor_x + _tft->gdX[gNum], _tft->cursor_y, bg); + pushSprite(this->cursor_x + this->gdX[gNum], this->cursor_y, bg); deleteSprite(); - _tft->cursor_x += _tft->gxAdvance[gNum]; + this->cursor_x += this->gxAdvance[gNum]; } - else _icursor_x += _tft->gxAdvance[gNum]; + else this->cursor_x += this->gxAdvance[gNum]; } else { // Not a Unicode in font so draw a rectangle and move on cursor - drawRect(_icursor_x, _icursor_y + _tft->gFont.maxAscent - _tft->gFont.ascent, _tft->gFont.spaceWidth, _tft->gFont.ascent, fg); - _icursor_x += _tft->gFont.spaceWidth + 1; + drawRect(this->cursor_x, this->cursor_y + this->gFont.maxAscent - this->gFont.ascent, this->gFont.spaceWidth, this->gFont.ascent, fg); + this->cursor_x += this->gFont.spaceWidth + 1; } } @@ -1311,7 +1492,7 @@ void TFT_eSprite::drawGlyph(uint16_t code) *************************************************************************************x*/ void TFT_eSprite::printToSprite(String string) { - if(!_tft->fontLoaded) return; + if(!this->fontLoaded) return; int16_t len = string.length(); char cbuffer[len + 1]; // Add 1 for the null string.toCharArray(cbuffer, len + 1); // Add 1 for the null, otherwise characters get dropped @@ -1325,13 +1506,13 @@ void TFT_eSprite::printToSprite(String string) *************************************************************************************x*/ void TFT_eSprite::printToSprite(char *cbuffer, int len) //String string) { - if(!_tft->fontLoaded) return; + if(!this->fontLoaded) return; - fontFile = SPIFFS.open( _tft->_gFontFilename, "r" ); + //fontFile = SPIFFS.open( this->_gFontFilename, "r" ); - if(!fontFile) + if(!this->fontFile) { - _tft->fontLoaded = false; + this->fontLoaded = false; return; } @@ -1346,19 +1527,19 @@ void TFT_eSprite::printToSprite(char *cbuffer, int len) //String string) while (n < len) { uint16_t unicode = decodeUTF8((uint8_t*)cbuffer, &n, len - n); - if (_tft->getUnicodeIndex(unicode, &index)) + if (this->getUnicodeIndex(unicode, &index)) { - if (n == 0) sWidth -= _tft->gdX[index]; - if (n == len-1) sWidth += ( _tft->gWidth[index] + _tft->gdX[index]); - else sWidth += _tft->gxAdvance[index]; + if (n == 0) sWidth -= this->gdX[index]; + if (n == len-1) sWidth += ( this->gWidth[index] + this->gdX[index]); + else sWidth += this->gxAdvance[index]; } - else sWidth += _tft->gFont.spaceWidth + 1; + else sWidth += this->gFont.spaceWidth + 1; } - createSprite(sWidth, _tft->gFont.yAdvance); + createSprite(sWidth, this->gFont.yAdvance); uint16_t transparent = TFT_BLACK; - if (_tft->textbgcolor != TFT_BLACK) fillSprite(_tft->textbgcolor); + if (this->textbgcolor != TFT_BLACK) fillSprite(this->textbgcolor); } n = 0; @@ -1372,12 +1553,12 @@ void TFT_eSprite::printToSprite(char *cbuffer, int len) //String string) } if (newSprite) - { + { // The sprite had to be created so place at TFT cursor pushSprite(_tft->cursor_x, _tft->cursor_y); deleteSprite(); } - fontFile.close(); + //fontFile.close(); } @@ -1388,22 +1569,22 @@ void TFT_eSprite::printToSprite(char *cbuffer, int len) //String string) int16_t TFT_eSprite::printToSprite(int16_t x, int16_t y, uint16_t index) { bool newSprite = !_created; - int16_t sWidth = _tft->gWidth[index]; + int16_t sWidth = this->gWidth[index]; if (newSprite) { - createSprite(sWidth, _tft->gFont.yAdvance); + createSprite(sWidth, this->gFont.yAdvance); uint16_t transparent = TFT_BLACK; - if (_tft->textbgcolor != TFT_BLACK) fillSprite(_tft->textbgcolor); + if (this->textbgcolor != TFT_BLACK) fillSprite(this->textbgcolor); - drawGlyph(_tft->gUnicode[index]); + drawGlyph(this->gUnicode[index]); - pushSprite(x + _tft->gdX[index], y, _tft->textbgcolor); + pushSprite(x + this->gdX[index], y, this->textbgcolor); deleteSprite(); } - else drawGlyph(_tft->gUnicode[index]); + else drawGlyph(this->gUnicode[index]); - return _tft->gxAdvance[index]; + return this->gxAdvance[index]; } #endif diff --git a/Extensions/Sprite.h b/Extensions/Sprite.h index 85945ff..1ea7645 100644 --- a/Extensions/Sprite.h +++ b/Extensions/Sprite.h @@ -14,15 +14,20 @@ class TFT_eSprite : public TFT_eSPI { // Create a sprite of width x height pixels, return a pointer to the RAM area // Sketch can cast returned value to (uint16_t*) for 16 bit depth if needed // RAM required is 1 byte per pixel for 8 bit colour depth, 2 bytes for 16 bit - void* createSprite(int16_t width, int16_t height); + void* createSprite(int16_t width, int16_t height, uint8_t frames = 1); // Delete the sprite to free up the RAM void deleteSprite(void); + // Select the frame buffer for graphics + void* frameBuffer(int8_t f); + // Set the colour depth to 8 or 16 bits. Can be used to change depth an existing // sprite, but clears it to black, returns a new pointer if sprite is re-created. void* setColorDepth(int8_t b); + void setBitmapColor(uint16_t c, uint16_t b); + void drawPixel(uint32_t x, uint32_t y, uint32_t color); void drawChar(int32_t x, int32_t y, unsigned char c, uint32_t color, uint32_t bg, uint8_t size), @@ -49,10 +54,13 @@ class TFT_eSprite : public TFT_eSPI { drawFastVLine(int32_t x, int32_t y, int32_t h, uint32_t color), drawFastHLine(int32_t x, int32_t y, int32_t w, uint32_t color), - fillRect(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color), + fillRect(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color); // Set the sprite text cursor position for print class (does not change the TFT screen cursor) - setCursor(int16_t x, int16_t y); + //setCursor(int16_t x, int16_t y); + + void setRotation(uint8_t rotation); + uint8_t getRotation(void); // Read the colour of a pixel at x,y and return value in 565 format uint16_t readPixel(int32_t x0, int32_t y0); @@ -92,13 +100,17 @@ class TFT_eSprite : public TFT_eSPI { protected: - uint16_t *_img; // pointer to 16 bit sprite - uint8_t *_img8; // pointer to 8 bit sprite - bool _created, _bpp16; // created and bits per pixel depth flags + uint8_t _bpp; + uint16_t *_img; // pointer to 16 bit sprite + uint8_t *_img8; // pointer to 8 bit sprite + uint8_t *_img8_1; // pointer to frame 1 + uint8_t *_img8_2; // pointer to frame 2 + bool _created; // created and bits per pixel depth flags bool _gFont = false; - int32_t _icursor_x, _icursor_y; +// int32_t _icursor_x, _icursor_y; + uint8_t _rotation = 0; int32_t _xs, _ys, _xe, _ye, _xptr, _yptr; // for setWindow int32_t _sx, _sy; // x,y for scroll zone uint32_t _sw, _sh; // w,h for scroll zone @@ -106,6 +118,8 @@ class TFT_eSprite : public TFT_eSPI { boolean _iswapBytes; // Swap the byte order for Sprite pushImage() - int32_t _iwidth, _iheight; // Sprite image width and height + int32_t _iwidth, _iheight; // Sprite memory image bit width and height (swapped during rotations) + int32_t _dwidth, _dheight; // Real display width and height (for <8bpp Sprites) + int32_t _bitwidth; // Sprite image bit width for drawPixel (for <8bpp Sprites, not swapped) }; diff --git a/TFT_Drivers/EPD_Defines.h b/TFT_Drivers/EPD_Defines.h new file mode 100644 index 0000000..6a6838c --- /dev/null +++ b/TFT_Drivers/EPD_Defines.h @@ -0,0 +1,27 @@ +// Null set for ePaper +#define TFT_WIDTH 1000 +#define TFT_HEIGHT 1000 + +#define TFT_INIT_DELAY 0 + +#define TFT_NOP 0x00 +#define TFT_SWRST 0x00 + +#define TFT_CASET 0x00 +#define TFT_PASET 0x00 +#define TFT_RAMWR 0x00 + +#define TFT_RAMRD 0x00 +#define TFT_IDXRD 0x00 + +#define TFT_MADCTL 0x00 +#define TFT_MAD_MY 0x00 +#define TFT_MAD_MX 0x00 +#define TFT_MAD_MV 0x00 +#define TFT_MAD_ML 0x00 +#define TFT_MAD_BGR 0x00 +#define TFT_MAD_MH 0x00 +#define TFT_MAD_RGB 0x00 + +#define TFT_INVOFF 0x00 +#define TFT_INVON 0x00 diff --git a/TFT_eSPI.cpp b/TFT_eSPI.cpp index ccb9642..6348f65 100644 --- a/TFT_eSPI.cpp +++ b/TFT_eSPI.cpp @@ -153,11 +153,11 @@ TFT_eSPI::TFT_eSPI(int16_t w, int16_t h) _init_width = _width = w; // Set by specific xxxxx_Defines.h file or by users sketch _init_height = _height = h; // Set by specific xxxxx_Defines.h file or by users sketch rotation = 0; - cursor_y = cursor_x = 0; + cursor_y = cursor_x = 0; textfont = 1; textsize = 1; - textcolor = 0xFFFF; // White - textbgcolor = 0x0000; // Black + textcolor = bitmap_fg = 0xFFFF; // White + textbgcolor = bitmap_bg = 0x0000; // Black padX = 0; // No padding textwrapX = true; // Wrap text at end of line when using print stream textwrapY = false; // Wrap text at bottom of screen when using print stream @@ -998,7 +998,7 @@ void TFT_eSPI::pushImage(int32_t x, int32_t y, uint32_t w, uint32_t h, const uin ** Function name: pushImage ** Description: plot 8 bit image or sprite using a line buffer ***************************************************************************************/ -void TFT_eSPI::pushImage(int32_t x, int32_t y, uint32_t w, uint32_t h, uint8_t *data) +void TFT_eSPI::pushImage(int32_t x, int32_t y, uint32_t w, uint32_t h, uint8_t *data, bool bpp8) { if ((x >= (int32_t)_width) || (y >= (int32_t)_height)) return; @@ -1020,45 +1020,79 @@ void TFT_eSPI::pushImage(int32_t x, int32_t y, uint32_t w, uint32_t h, uint8_t * setAddrWindow(x, y, x + dw - 1, y + dh - 1); // Sets CS low and sent RAMWR - data += dx + dy * w; - // Line buffer makes plotting faster uint16_t lineBuf[dw]; - uint8_t blue[] = {0, 11, 21, 31}; // blue 2 to 5 bit colour lookup table - - _lastColor = -1; // Set to illegal value - - // Used to store last shifted colour - uint8_t msbColor = 0; - uint8_t lsbColor = 0; - - while (dh--) + if (bpp8) { - uint32_t len = dw; - uint8_t* ptr = data; - uint8_t* linePtr = (uint8_t*)lineBuf; + uint8_t blue[] = {0, 11, 21, 31}; // blue 2 to 5 bit colour lookup table - while(len--) + _lastColor = -1; // Set to illegal value + + // Used to store last shifted colour + uint8_t msbColor = 0; + uint8_t lsbColor = 0; + + data += dx + dy * w; + while (dh--) { - uint32_t color = *ptr++; + uint32_t len = dw; + uint8_t* ptr = data; + uint8_t* linePtr = (uint8_t*)lineBuf; - // Shifts are slow so check if colour has changed first - if (color != _lastColor) { - // =====Green===== ===============Red============== - msbColor = (color & 0x1C)>>2 | (color & 0xC0)>>3 | (color & 0xE0); - // =====Green===== =======Blue====== - lsbColor = (color & 0x1C)<<3 | blue[color & 0x03]; - _lastColor = color; + while(len--) + { + uint32_t color = *ptr++; + + // Shifts are slow so check if colour has changed first + if (color != _lastColor) { + // =====Green===== ===============Red============== + msbColor = (color & 0x1C)>>2 | (color & 0xC0)>>3 | (color & 0xE0); + // =====Green===== =======Blue====== + lsbColor = (color & 0x1C)<<3 | blue[color & 0x03]; + _lastColor = color; + } + + *linePtr++ = msbColor; + *linePtr++ = lsbColor; } - *linePtr++ = msbColor; - *linePtr++ = lsbColor; + pushColors(lineBuf, dw, false); + + data += w; } + } + else + { + while (dh--) + { + w = (w+7) & 0xFFF8; - pushColors(lineBuf, dw, false); + int32_t len = dw; + uint8_t* ptr = data; + uint8_t* linePtr = (uint8_t*)lineBuf; + uint8_t bits = 8; + while(len>0) + { + if (len < 8) bits = len; + uint32_t xp = dx; + for (uint16_t i = 0; i < bits; i++) + { + uint8_t col = (ptr[(xp + dy * w)>>3] << (xp & 0x7)) & 0x80; + if (col) {*linePtr++ = bitmap_fg>>8; *linePtr++ = (uint8_t) bitmap_fg;} + else {*linePtr++ = bitmap_bg>>8; *linePtr++ = (uint8_t) bitmap_bg;} + //if (col) drawPixel((dw-len)+xp,h-dh,bitmap_fg); + //else drawPixel((dw-len)+xp,h-dh,bitmap_bg); + xp++; + } + *ptr++; + len -= 8; + } - data += w; + pushColors(lineBuf, dw, false); + + dy++; + } } CS_H; @@ -1070,9 +1104,9 @@ void TFT_eSPI::pushImage(int32_t x, int32_t y, uint32_t w, uint32_t h, uint8_t * /*************************************************************************************** ** Function name: pushImage -** Description: plot 8 bit image or sprite with 1 colour being transparent +** Description: plot 8 or 1 bit image or sprite with a transparent colour ***************************************************************************************/ -void TFT_eSPI::pushImage(int32_t x, int32_t y, uint32_t w, uint32_t h, uint8_t *data, uint8_t transp) +void TFT_eSPI::pushImage(int32_t x, int32_t y, uint32_t w, uint32_t h, uint8_t *data, uint8_t transp, bool bpp8) { if ((x >= (int32_t)_width) || (y >= (int32_t)_height)) return; @@ -1092,70 +1126,121 @@ void TFT_eSPI::pushImage(int32_t x, int32_t y, uint32_t w, uint32_t h, uint8_t * spi_begin(); inTransaction = true; - data += dx + dy * w; - int32_t xe = x + dw - 1, ye = y + dh - 1; // Line buffer makes plotting faster uint16_t lineBuf[dw]; - uint8_t blue[] = {0, 11, 21, 31}; // blue 2 to 5 bit colour lookup table - - _lastColor = -1; // Set to illegal value - - // Used to store last shifted colour - uint8_t msbColor = 0; - uint8_t lsbColor = 0; - - int32_t spx = x, spy = y; - - while (dh--) + if (bpp8) { - int32_t len = dw; - uint8_t* ptr = data; - uint8_t* linePtr = (uint8_t*)lineBuf; + data += dx + dy * w; - int32_t px = x; - boolean move = true; - uint16_t np = 0; + uint8_t blue[] = {0, 11, 21, 31}; // blue 2 to 5 bit colour lookup table - while (len--) + _lastColor = -1; // Set to illegal value + + // Used to store last shifted colour + uint8_t msbColor = 0; + uint8_t lsbColor = 0; + + int32_t spx = x, spy = y; + + while (dh--) { - if (transp != *ptr) - { - if (move) { move = false; setAddrWindow(px, y, xe, ye);} - uint8_t color = *ptr; + int32_t len = dw; + uint8_t* ptr = data; + uint8_t* linePtr = (uint8_t*)lineBuf; - // Shifts are slow so check if colour has changed first - if (color != _lastColor) { - // =====Green===== ===============Red============== - msbColor = (color & 0x1C)>>2 | (color & 0xC0)>>3 | (color & 0xE0); - // =====Green===== =======Blue====== - lsbColor = (color & 0x1C)<<3 | blue[color & 0x03]; - _lastColor = color; - } - *linePtr++ = msbColor; - *linePtr++ = lsbColor; - np++; - } - else + int32_t px = x; + boolean move = true; + uint16_t np = 0; + + while (len--) { - move = true; - if (np) + if (transp != *ptr) { - pushColors(lineBuf, np, false); - linePtr = (uint8_t*)lineBuf; - np = 0; + if (move) { move = false; setAddrWindow(px, y, xe, ye);} + uint8_t color = *ptr; + + // Shifts are slow so check if colour has changed first + if (color != _lastColor) { + // =====Green===== ===============Red============== + msbColor = (color & 0x1C)>>2 | (color & 0xC0)>>3 | (color & 0xE0); + // =====Green===== =======Blue====== + lsbColor = (color & 0x1C)<<3 | blue[color & 0x03]; + _lastColor = color; + } + *linePtr++ = msbColor; + *linePtr++ = lsbColor; + np++; } + else + { + move = true; + if (np) + { + pushColors(lineBuf, np, false); + linePtr = (uint8_t*)lineBuf; + np = 0; + } + } + px++; + ptr++; } - px++; - ptr++; + + if (np) pushColors(lineBuf, np, false); + + y++; + data += w; + } + } + else + { + w = (w+7) & 0xFFF8; + while (dh--) + { + int32_t px = x; + boolean move = true; + uint16_t np = 0; + int32_t len = dw; + uint8_t* ptr = data; + uint8_t bits = 8; + while(len>0) + { + if (len < 8) bits = len; + uint32_t xp = dx; + uint32_t yp = (dy * w)>>3; + for (uint16_t i = 0; i < bits; i++) + { + //uint8_t col = (ptr[(xp + dy * w)>>3] << (xp & 0x7)) & 0x80; + if ((ptr[(xp>>3) + yp] << (xp & 0x7)) & 0x80) + { + if (move) + { + move = false; + setAddrWindow(px, y, xe, ye); + } + np++; + } + else + { + if (np) + { + pushColor(bitmap_fg, np); + np = 0; + move = true; + } + } + px++; + xp++; + } + *ptr++; + len -= 8; + } + if (np) pushColor(bitmap_fg, np); + y++; + dy++; } - - if (np) pushColors(lineBuf, np, false); - - y++; - data += w; } CS_H; @@ -1769,6 +1854,18 @@ void TFT_eSPI::setTextColor(uint16_t c, uint16_t b) } +/*************************************************************************************** +** Function name: setBitmapColor +** Description: Set the foreground foreground and background colour +***************************************************************************************/ +void TFT_eSPI::setBitmapColor(uint16_t c, uint16_t b) +{ + if (c == b) b = ~c; + bitmap_fg = c; + bitmap_bg = b; +} + + /*************************************************************************************** ** Function name: setTextWrap ** Description: Define if text should wrap at end of line @@ -2978,7 +3075,7 @@ void TFT_eSPI::pushColors(uint16_t *data, uint32_t len, bool swap) #if defined (ESP32) #ifdef ESP32_PARALLEL if (swap) while ( len-- ) {tft_Write_16(*data); data++;} - else while ( len-- ) {transwap16(*data); data++;} + else while ( len-- ) {tft_Write_16S(*data); data++;} #else if (swap) SPI.writePixels(data,len<<1); else SPI.writeBytes((uint8_t*)data,len<<1); @@ -3013,6 +3110,8 @@ void TFT_eSPI::pushColors(uint16_t *data, uint32_t len, bool swap) } len -= 16; + + // ESP8266 wait time here at 40MHz SPI is ~5.45us while(SPI1CMD & SPIBUSY) {} SPI1W0 = color[0]; SPI1W1 = color[1]; @@ -4446,6 +4545,136 @@ void writeBlock(uint16_t color, uint32_t repeat) #endif +/*************************************************************************************** +** Function name: getSetup +** Description: Get the setup details for diagnostic and sketch access +***************************************************************************************/ +void TFT_eSPI::getSetup(setup_t &tft_settings) +{ + +#if defined (ESP8266) + tft_settings.esp = 8266; +#elif defined (ESP32) + tft_settings.esp = 32; +#else + tft_settings.esp = -1; +#endif + +#if defined (SUPPORT_TRANSACTIONS) + tft_settings.trans = true; +#else + tft_settings.trans = false; +#endif + +#if defined (ESP32_PARALLEL) + tft_settings.serial = false; + tft_settings.tft_spi_freq = 0; +#else + tft_settings.serial = true; + tft_settings.tft_spi_freq = SPI_FREQUENCY/100000; +#endif + + tft_settings.tft_driver = TFT_DRIVER; + tft_settings.tft_width = _init_width; + tft_settings.tft_height = _init_height; + +#ifdef CGRAM_OFFSET + tft_settings.r0_x_offset = colstart; + tft_settings.r0_y_offset = rowstart; + tft_settings.r1_x_offset = 0; + tft_settings.r1_y_offset = 0; + tft_settings.r2_x_offset = 0; + tft_settings.r2_y_offset = 0; + tft_settings.r3_x_offset = 0; + tft_settings.r3_y_offset = 0; +#else + tft_settings.r0_x_offset = 0; + tft_settings.r0_y_offset = 0; + tft_settings.r1_x_offset = 0; + tft_settings.r1_y_offset = 0; + tft_settings.r2_x_offset = 0; + tft_settings.r2_y_offset = 0; + tft_settings.r3_x_offset = 0; + tft_settings.r3_y_offset = 0; +#endif + +#if defined (TFT_MOSI) + tft_settings.pin_tft_mosi = TFT_MOSI; +#else + tft_settings.pin_tft_mosi = -1; +#endif + +#if defined (TFT_MISO) + tft_settings.pin_tft_miso = TFT_MISO; +#else + tft_settings.pin_tft_miso = -1; +#endif + +#if defined (TFT_SCLK) + tft_settings.pin_tft_clk = TFT_SCLK; +#else + tft_settings.pin_tft_clk = -1; +#endif + +#if defined (TFT_CS) + tft_settings.pin_tft_cs = TFT_CS; +#else + tft_settings.pin_tft_cs = -1; +#endif + +#if defined (TFT_DC) + tft_settings.pin_tft_dc = TFT_DC; +#else + tft_settings.pin_tft_dc = -1; +#endif + +#if defined (TFT_RD) + tft_settings.pin_tft_rd = TFT_RD; +#else + tft_settings.pin_tft_rd = -1; +#endif + +#if defined (TFT_WR) + tft_settings.pin_tft_wr = TFT_WR; +#else + tft_settings.pin_tft_wr = -1; +#endif + +#if defined (TFT_RST) + tft_settings.pin_tft_rst = TFT_RST; +#else + tft_settings.pin_tft_rst = -1; +#endif + +#if defined (ESP32_PARALLEL) + tft_settings.pin_tft_d0 = TFT_D0; + tft_settings.pin_tft_d1 = TFT_D1; + tft_settings.pin_tft_d2 = TFT_D2; + tft_settings.pin_tft_d3 = TFT_D3; + tft_settings.pin_tft_d4 = TFT_D4; + tft_settings.pin_tft_d5 = TFT_D5; + tft_settings.pin_tft_d6 = TFT_D6; + tft_settings.pin_tft_d7 = TFT_D7; +#else + tft_settings.pin_tft_d0 = -1; + tft_settings.pin_tft_d1 = -1; + tft_settings.pin_tft_d2 = -1; + tft_settings.pin_tft_d3 = -1; + tft_settings.pin_tft_d4 = -1; + tft_settings.pin_tft_d5 = -1; + tft_settings.pin_tft_d6 = -1; + tft_settings.pin_tft_d7 = -1; +#endif + +#if defined (TOUCH_CS) + tft_settings.pin_tch_cs = TOUCH_CS; + tft_settings.tch_spi_freq = SPI_TOUCH_FREQUENCY/100000; +#else + tft_settings.pin_tch_cs = -1; + tft_settings.tch_spi_freq = 0; +#endif +} + //////////////////////////////////////////////////////////////////////////////////////// #ifdef TOUCH_CS #include "Extensions/Touch.cpp" diff --git a/TFT_eSPI.h b/TFT_eSPI.h index 343a39a..49571eb 100644 --- a/TFT_eSPI.h +++ b/TFT_eSPI.h @@ -155,42 +155,42 @@ #endif -#ifdef ESP32_PARALLEL - +#if defined (ESP32) && defined (ESP32_PARALLEL) + // Mask for the 8 data bits to set pin directions #define dir_mask ((1 << TFT_D0) | (1 << TFT_D1) | (1 << TFT_D2) | (1 << TFT_D3) | (1 << TFT_D4) | (1 << TFT_D5) | (1 << TFT_D6) | (1 << TFT_D7)) + // Data bits and the write line are cleared to 0 in one step #define clr_mask (dir_mask | (1 << TFT_WR)) + // A lookup table is used to set the different bit patterns, this uses 1kByte of RAM #define set_mask(C) xset_mask[C] // 63fps Sprite rendering test 33% faster, graphicstest only 1.8% faster than shifting in real time // Real-time shifting alternative to above to save 1KByte RAM, 47 fps Sprite rendering test //#define set_mask(C) ((C&0x80)>>7)<>6)<>5)<>4)<>3)<>2)<>1)<>0)<> 8)); WR_H; \ - GPIO.out_w1tc = clr_mask; GPIO.out_w1ts = set_mask((uint8_t)(C >> 0)); WR_H + GPIO.out_w1tc = clr_mask; GPIO.out_w1ts = set_mask((uint8_t)(C >> 0)); WR_H - // 16 bit transfer with swapped bytes - #define transwap16(C) GPIO.out_w1tc = clr_mask; GPIO.out_w1ts = set_mask((uint8_t) (C >> 0)); WR_H; \ - GPIO.out_w1tc = clr_mask; GPIO.out_w1ts = set_mask((uint8_t) (C >> 8)); WR_H + // 16 bit write with swapped bytes + #define tft_Write_16S(C) GPIO.out_w1tc = clr_mask; GPIO.out_w1ts = set_mask((uint8_t) (C >> 0)); WR_H; \ + GPIO.out_w1tc = clr_mask; GPIO.out_w1ts = set_mask((uint8_t) (C >> 8)); WR_H + // Write 32 bits to TFT #define tft_Write_32(C) GPIO.out_w1tc = clr_mask; GPIO.out_w1ts = set_mask((uint8_t) (C >> 24)); WR_H; \ - GPIO.out_w1tc = clr_mask; GPIO.out_w1ts = set_mask((uint8_t) (C >> 16)); WR_H; \ - GPIO.out_w1tc = clr_mask; GPIO.out_w1ts = set_mask((uint8_t) (C >> 8)); WR_H; \ - GPIO.out_w1tc = clr_mask; GPIO.out_w1ts = set_mask((uint8_t) (C >> 0)); WR_H + GPIO.out_w1tc = clr_mask; GPIO.out_w1ts = set_mask((uint8_t) (C >> 16)); WR_H; \ + GPIO.out_w1tc = clr_mask; GPIO.out_w1ts = set_mask((uint8_t) (C >> 8)); WR_H; \ + GPIO.out_w1tc = clr_mask; GPIO.out_w1ts = set_mask((uint8_t) (C >> 0)); WR_H #ifdef TFT_RD - #if defined (ESP32) - #define RD_L GPIO.out_w1tc = (1 << TFT_RD) - //#define RD_L digitalWrite(TFT_WR, LOW) - #define RD_H GPIO.out_w1ts = (1 << TFT_RD) - //#define RD_H digitalWrite(TFT_WR, HIGH) - #else - //#define RD_L GPOC=rdpinmask - //#define RD_H GPOS=rdpinmask - #endif + #define RD_L GPIO.out_w1tc = (1 << TFT_RD) + //#define RD_L digitalWrite(TFT_WR, LOW) + #define RD_H GPIO.out_w1ts = (1 << TFT_RD) + //#define RD_H digitalWrite(TFT_WR, HIGH) #endif #elif defined (SEND_16_BITS) @@ -330,9 +330,55 @@ template static inline void swap_coord(T& a, T& b) { T t = a; a = b; b = t; } #ifndef min -#define min(a,b) (((a) < (b)) ? (a) : (b)) + #define min(a,b) (((a) < (b)) ? (a) : (b)) #endif +// This structure allows sketches to retrieve the user setup parameters at runtime +// by calling getSetup(), zero impact on code size unless used, mainly for diagnostics +typedef struct +{ +int16_t esp; +uint8_t trans; +uint8_t serial; + +uint16_t tft_driver; // Hexadecimal code +uint16_t tft_width; // Rotation 0 width and height +uint16_t tft_height; + +uint8_t r0_x_offset; // Offsets, not all used yet +uint8_t r0_y_offset; +uint8_t r1_x_offset; +uint8_t r1_y_offset; +uint8_t r2_x_offset; +uint8_t r2_y_offset; +uint8_t r3_x_offset; +uint8_t r3_y_offset; + +int8_t pin_tft_mosi; +int8_t pin_tft_miso; +int8_t pin_tft_clk; +int8_t pin_tft_cs; + +int8_t pin_tft_dc; +int8_t pin_tft_rd; +int8_t pin_tft_wr; +int8_t pin_tft_rst; + +int8_t pin_tft_d0; +int8_t pin_tft_d1; +int8_t pin_tft_d2; +int8_t pin_tft_d3; +int8_t pin_tft_d4; +int8_t pin_tft_d5; +int8_t pin_tft_d6; +int8_t pin_tft_d7; + +int8_t pin_tch_cs; + +int16_t tft_spi_freq; +int16_t tch_spi_freq; +} setup_t; + // This is a structure to conveniently hold information on the default fonts // Stores pointer to font character image address table, width table and height @@ -444,6 +490,7 @@ class TFT_eSPI : public Print { fillTriangle(int32_t x0, int32_t y0, int32_t x1, int32_t y1, int32_t x2, int32_t y2, uint32_t color), drawBitmap(int16_t x, int16_t y, const uint8_t *bitmap, int16_t w, int16_t h, uint16_t color), + setBitmapColor(uint16_t c, uint16_t b), // For 1bpp sprites setCursor(int16_t x, int16_t y), setCursor(int16_t x, int16_t y, uint8_t font), @@ -489,9 +536,9 @@ class TFT_eSPI : public Print { void pushImage(int32_t x0, int32_t y0, uint32_t w, uint32_t h, const uint16_t *data, uint16_t transparent); void pushImage(int32_t x0, int32_t y0, uint32_t w, uint32_t h, const uint16_t *data); - // These are used by pushSprite for 8 bit colours - void pushImage(int32_t x0, int32_t y0, uint32_t w, uint32_t h, uint8_t *data); - void pushImage(int32_t x0, int32_t y0, uint32_t w, uint32_t h, uint8_t *data, uint8_t transparent); + // These are used by pushSprite for 1 and 8 bit colours + void pushImage(int32_t x0, int32_t y0, uint32_t w, uint32_t h, uint8_t *data, bool bpp8 = true); + void pushImage(int32_t x0, int32_t y0, uint32_t w, uint32_t h, uint8_t *data, uint8_t transparent, bool bpp8 = true); // Swap the byte order for pushImage() - corrects endianness void setSwapBytes(bool swap); @@ -543,8 +590,11 @@ class TFT_eSPI : public Print { size_t write(uint8_t); + void getSetup(setup_t& tft_settings); // Sketch provides the instance to populate + int32_t cursor_x, cursor_y; uint32_t textcolor, textbgcolor; + uint32_t bitmap_fg, bitmap_bg; private: diff --git a/Tools/Create_Smooth_Font/Create_font/FontFiles/Final-Frontier28.vlw b/Tools/Create_Smooth_Font/Create_font/FontFiles/Final-Frontier28.vlw new file mode 100644 index 0000000000000000000000000000000000000000..2872fd554bbf34f5e1268770dd8ea3cf55cecdfd GIT binary patch literal 25287 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 literal 0 HcmV?d00001 diff --git a/User_Setup_Select.h b/User_Setup_Select.h index c4e720b..b8e7628 100644 --- a/User_Setup_Select.h +++ b/User_Setup_Select.h @@ -40,7 +40,7 @@ //#include -//#include // Setup file template for copying/editting +// ePaper #include // Setup file template for copying/editting #endif // USER_SETUP_LOADED @@ -58,39 +58,51 @@ // Load the right driver definition - do not tinker here ! #if defined (ILI9341_DRIVER) #include + #define TFT_DRIVER 0x9341 #elif defined (ST7735_DRIVER) #include + #define TFT_DRIVER 0x7735 #elif defined (ILI9163_DRIVER) #include + #define TFT_DRIVER 0x9163 #elif defined (S6D02A1_DRIVER) #include + #define TFT_DRIVER 0x6D02 #elif defined (RPI_ILI9486_DRIVER) #include + #define TFT_DRIVER 0x9481 #elif defined (ILI9481_DRIVER) #include + #define TFT_DRIVER 0x9481 #elif defined (ILI9488_DRIVER) #include + #define TFT_DRIVER 0x9488 #elif defined (HX8357D_DRIVER) #include "TFT_Drivers/HX8357D_Defines.h" + #define TFT_DRIVER 0x8357 +#elif defined (EPD_DRIVER) + #include "TFT_Drivers/EPD_Defines.h" + #define TFT_DRIVER 0xE9D #endif // These are the pins for all ESP8266 boards -#define PIN_D0 16 -#define PIN_D1 5 -#define PIN_D2 4 -#define PIN_D3 0 -#define PIN_D4 2 -#define PIN_D5 14 -#define PIN_D6 12 -#define PIN_D7 13 -#define PIN_D8 15 -#define PIN_D9 3 -#define PIN_D10 1 +// Name GPIO Function +#define PIN_D0 16 // WAKE +#define PIN_D1 5 // User purpose +#define PIN_D2 4 // User purpose +#define PIN_D3 0 // FLASH mode at boot time +#define PIN_D4 2 // TXD1 (Note: low on boot means go to FLASH mode) +#define PIN_D5 14 // HSCLK +#define PIN_D6 12 // HMISO +#define PIN_D7 13 // HMOSI RXD2 +#define PIN_D8 15 // HCS TXD0 +#define PIN_D9 3 // RXD0 +#define PIN_D10 1 // TXD0 -#define PIN_MOSI 8 -#define PIN_MISO 7 -#define PIN_SCLK 6 -#define PIN_HWCS 0 +#define PIN_MOSI 8 // SD1 +#define PIN_MISO 7 // SD0 +#define PIN_SCLK 6 // CLK +#define PIN_HWCS 0 // CMD -#define PIN_D11 9 -#define PIN_D12 10 +#define PIN_D11 9 // SD2 +#define PIN_D12 10 // SD4 diff --git a/User_Setups/SetupX_Template.h b/User_Setups/SetupX_Template.h index 6214165..8b22cbc 100644 --- a/User_Setups/SetupX_Template.h +++ b/User_Setups/SetupX_Template.h @@ -121,6 +121,10 @@ //#define TFT_RST 4 // Reset pin (could connect to RST pin) //#define TFT_RST -1 // Set TFT_RST to -1 if display RESET is connected to ESP32 board RST +//#define TOUCH_CS 22 // Chip select pin (T_CS) of touch screen + +//#define TFT_WR 21 // Write strobe for modified Raspberry Pi TFT only + // For the M5Stack module use these #define lines //#define TFT_MISO 19 //#define TFT_MOSI 23 @@ -130,9 +134,6 @@ //#define TFT_RST 33 // Reset pin (could connect to Arduino RESET pin) //#define TFT_BL 32 // LED back-light -//#define TOUCH_CS 21 // Chip select pin (T_CS) of touch screen - -//#define TFT_WR 22 // Write strobe for modified Raspberry Pi TFT only // ################################################################################## // @@ -199,6 +200,7 @@ // #define SPI_FREQUENCY 20000000 #define SPI_FREQUENCY 27000000 // Actually sets it to 26.67MHz = 80/3 // #define SPI_FREQUENCY 40000000 // Maximum to use SPIFFS +// #define SPI_FREQUENCY 53400000 // #define SPI_FREQUENCY 80000000 // The XPT2046 requires a lower SPI clock rate of 2.5MHz so we define that here: diff --git a/User_Setups/ePaper_Template.h b/User_Setups/ePaper_Template.h new file mode 100644 index 0000000..e704621 --- /dev/null +++ b/User_Setups/ePaper_Template.h @@ -0,0 +1,76 @@ +// USER DEFINED SETTINGS +// Set driver type, fonts to be loaded, pins used and SPI control method etc +// +// See the User_Setup_Select.h file if you wish to be able to define multiple +// setups and then easily select which setup file is used by the compiler. +// +// If this file is edited correctly then all the library example sketches should +// run without the need to make any more changes for a particular hardware setup! + +// ################################################################################## +// +// Section 0. Call up the right driver file and any options for it +// +// ################################################################################## + +// Only define one driver, the other ones must be commented out +#define EPD_DRIVER + + +// ################################################################################## +// +// Section 1. Define the pins that are used to interface with the display here +// +// ################################################################################## + +// ePaper pins are not defined here - dummy set + +//#define TFT_CS +//#define TFT_DC +//#define TFT_RST + + +// ################################################################################## +// +// Section 2. Not used +// +// ################################################################################## + + +// ################################################################################## +// +// Section 3. Define the fonts that are to be used here +// +// ################################################################################## + +// Comment out the #defines below with // to stop that font being loaded +// The ESP8366 and ESP32 have plenty of memory so commenting out fonts is not +// normally necessary. If all fonts are loaded the extra FLASH space required is +// about 17Kbytes. To save FLASH space only enable the fonts you need! + +#define LOAD_GLCD // Font 1. Original Adafruit 8 pixel font needs ~1820 bytes in FLASH +#define LOAD_FONT2 // Font 2. Small 16 pixel high font, needs ~3534 bytes in FLASH, 96 characters +#define LOAD_FONT4 // Font 4. Medium 26 pixel high font, needs ~5848 bytes in FLASH, 96 characters +#define LOAD_FONT6 // Font 6. Large 48 pixel font, needs ~2666 bytes in FLASH, only characters 1234567890:-.apm +//#define LOAD_FONT7 // Font 7. 7 segment 48 pixel font, needs ~2438 bytes in FLASH, only characters 1234567890:. +//#define LOAD_FONT8N // Font 8. Alternative to Font 8 above, slightly narrower, so 3 digits fit a 160 pixel TFT +//#define LOAD_FONT8 // Font 8. Large 75 pixel font needs ~3256 bytes in FLASH, only characters 1234567890:-. +#define LOAD_GFXFF // FreeFonts. Include access to the 48 Adafruit_GFX free fonts FF1 to FF48 and custom fonts + +// Comment out the #define below to stop the SPIFFS filing system and smooth font code being loaded +// this will save ~20kbytes of FLASH +#define SMOOTH_FONT + +// ################################################################################## +// +// Section 4. Not used +// +// ################################################################################## + + +// ################################################################################## +// +// Section 5. Not used +// +// ################################################################################## + diff --git a/keywords.txt b/keywords.txt index 5d4f945..33da608 100644 --- a/keywords.txt +++ b/keywords.txt @@ -75,7 +75,6 @@ calibrateTouch KEYWORD2 setTouch KEYWORD2 validTouch KEYWORD2 - TFT_eSPI_Button KEYWORD1 initButton KEYWORD2 @@ -99,8 +98,14 @@ pushBitmap KEYWORD2 pushSprite KEYWORD2 setScrollRect KEYWORD2 scroll KEYWORD2 +printToSprite KEYWORD2 +frameBuffer KEYWORD2 +setBitmapColor KEYWORD2 alphaBlend KEYWORD2 showFont KEYWORD2 loadFont KEYWORD2 unloadFont KEYWORD2 +getUnicodeIndex KEYWORD2 +decodeUTF8 KEYWORD2 +drawGlyph KEYWORD2 diff --git a/library.json b/library.json index 27cc221..74448e6 100644 --- a/library.json +++ b/library.json @@ -14,10 +14,6 @@ "name": "Bodmer", "email": "bodmer@anola.net", "maintainer": true - }, - { - "name": "Adafruit", - "url": "https://www.adafruit.com/" } ], "frameworks": "arduino", From 1f531523dd37b72a78ecbfc55f9b8e46e3d5f1e8 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Mon, 26 Mar 2018 01:16:00 +0100 Subject: [PATCH 106/150] Update ReadMe and raise version --- README.md | 5 +++-- library.json | 2 +- library.properties | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 3c1dc58..c8f4733 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,4 @@ +Update 26th March 2018: Added support for 1 bit per pixel Sprites. Example to follow with ePaper (Waveshare) support. Update 10th March 2018: Added support for 8 bit parallel interface TFTs when used with ESP32 (Touch not supported yet on parallel displays) @@ -7,13 +8,13 @@ Update 24th February 2018: Added new smooth (antialiased) fonts. See Smooth Font # TFT_eSPI -An Arduino IDE compatible graphics and fonts library for ESP8266 and ESP32 processors with a driver for ILI9341, ILI9163, ST7735, S6D02A1, ILI9481, ILI9488, ILI9486 and HX8357 based TFT displays that support SPI. 8 bit parallel interface TFTs (e.g. UNO format mcufriend shilds) can used with ESP32 +An Arduino IDE compatible graphics and fonts library for ESP8266 and ESP32 processors with a driver for ILI9341, ILI9163, ST7735, S6D02A1, ILI9481, ILI9488, ILI9486 and HX8357 based TFT displays that support SPI. 8 bit parallel interface TFTs (e.g. UNO format mcufriend shilds) can used with an ESP32 The library also supports TFT displays designed for the Raspberry Pi that are based on a ILI9486 driver chip with a 480 x 320 pixel screen. This display must be of the Waveshare design and use a 16 bit serial interface based on the 74HC04, 74HC4040 and 2 x 74HC4094 logic chips. A modification to these displays is possible (see mod image in Tools folder) to make many graphics functions much faster (e.g. 23ms to clear the screen, 1.2ms to draw a 72 pixel high numeral). A new "Sprite" class has been added, this enables flicker free updates of complex graphics. Direct writes to the TFT with graphics functions are still available, so existing sketches do not need to be changed. -A Sprite is notionally an invisible graphics screen that is kept in the processors RAM. Graphics can be drawn into the Sprite just as they can be drawn directly to the screen. Once the Sprite is completed it can be plotted onto the screen in any position. If there is sufficient RAM then the Sprite can be the same size as the screen and used as a frame buffer. Sprites by default use 16 bit colours, the bit depth can be set to 8 bits to reduce the RAM needed. On an ESP8266 the largest 16 bit colour Sprite that can be created is about 160x128 pixels, this consumes 40Kbytes of RAM. On an ESP32 the workspace RAM is more limited than the datsheet implies so a 16 bit colour Sprite is limited to about 200x200 pixels (~80Kbytes) and an 8 bit sprite to 320x240 pixels (~76kbytes). +A Sprite is notionally an invisible graphics screen that is kept in the processors RAM. Graphics can be drawn into the Sprite just as they can be drawn directly to the screen. Once the Sprite is completed it can be plotted onto the screen in any position. If there is sufficient RAM then the Sprite can be the same size as the screen and used as a frame buffer. Sprites by default use 16 bit colours, the bit depth can be set to 8 bits (256 colours) , or 1 bit (any 2 colours) to reduce the RAM needed. On an ESP8266 the largest 16 bit colour Sprite that can be created is about 160x128 pixels, this consumes 40Kbytes of RAM. On an ESP32 the workspace RAM is more limited than the datsheet implies so a 16 bit colour Sprite is limited to about 200x200 pixels (~80Kbytes), an 8 bit sprite to 320x240 pixels (~76kbytes). A 1 bit per pixel Spriee required only 9600 bytes for a full 320 x 240 screen buffer, this is ideal for supporting usie with 2 colour bitmap fonts. One or more sprites can be created, a sprite can be any width and height, limited only by available RAM. The RAM needed for a 16 bit colour depth Sprite is (2 x width x height) bytes, for a Sprite with 8 bit colour depth the RAM needed is (width x height) bytes. Sprites can be created and deleted dynamically as needed in the sketch, this means RAM can be freed up after the sprite has been plotted on the screen, more RAM intensive WiFi based code can then be run and normal graphics operations still work. diff --git a/library.json b/library.json index 74448e6..4965527 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "TFT_eSPI", - "version": "0.19.11", + "version": "0.20.10", "keywords": "tft, display, ESP8266, NodeMCU, ESP32, M5Stack, ILI9341, ST7735, ILI9163, S6D02A1, ILI9486", "description": "A TFT SPI graphics library for ESP8266 and ESP32", "repository": diff --git a/library.properties b/library.properties index 6c14dcb..dbbc4d4 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=TFT_eSPI -version=0.19.11 +version=0.20.10 author=Bodmer maintainer=Bodmer sentence=A fast TFT library for ESP8266 processors and the Arduino IDE From d9d5625e48dcc01ffbc3b5e6014b8f3ff27cc732 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Wed, 28 Mar 2018 01:59:06 +0100 Subject: [PATCH 107/150] Add 1 bpp examples --- .../1_bit_Sprite_Demo/1_bit_Sprite_Demo.ino | 106 ++++++++++++++++++ .../Sprite/1_bit_Yin_Yang/1_bit_Yin_Yang.ino | 97 ++++++++++++++++ 2 files changed, 203 insertions(+) create mode 100644 examples/Sprite/1_bit_Sprite_Demo/1_bit_Sprite_Demo.ino create mode 100644 examples/Sprite/1_bit_Yin_Yang/1_bit_Yin_Yang.ino diff --git a/examples/Sprite/1_bit_Sprite_Demo/1_bit_Sprite_Demo.ino b/examples/Sprite/1_bit_Sprite_Demo/1_bit_Sprite_Demo.ino new file mode 100644 index 0000000..dacf64f --- /dev/null +++ b/examples/Sprite/1_bit_Sprite_Demo/1_bit_Sprite_Demo.ino @@ -0,0 +1,106 @@ +/* + Sketch to show creation of a 1bpp sprite with a transparent + background, then plot it on the TFT. + + Example for library: + https://github.com/Bodmer/TFT_eSPI + + A Sprite is notionally an invisible graphics screen that is + kept in the processors RAM. Graphics can be drawn into the + Sprite just as it can be drawn directly to the screen. Once + the Sprite is completed it can be plotted onto the screen in + any position. If there is sufficient RAM then the Sprite can + be the same size as the screen and used as a frame buffer. + + A 1 bit Sprite occupies (width * height)/8 bytes in RAM. So, + for example, a 320 x 240 pixel Sprite occupies 9600 bytes. +*/ +// A new setBitmapColor(fg_color, bg_color) function allows +// any 2 colours to be used for the 1 bit sprite. +// One colour can also be defined as transparent when +// rendering to the screen. + + +#include // Include the graphics library (this includes the sprite functions) + +TFT_eSPI tft = TFT_eSPI(); // Create object "tft" + +TFT_eSprite img = TFT_eSprite(&tft); // Create Sprite object "img" with pointer to "tft" object + // the pointer is used by pushSprite() to push it onto the TFT + +#define BITS_PER_PIXEL 1 // How many bits per pixel in Sprite + +// ========================================================================= +void setup(void) { + Serial.begin(250000); + + tft.init(); + + tft.setRotation(0); +} +// ========================================================================= +void loop() { + + tft.fillScreen(TFT_NAVY); + + // Draw 10 sprites containing a "transparent" colour + for (int i = 0; i < 10; i++) + { + int x = random(240 - 70); + int y = random(320 - 80); + int c = random(0x10000); // Random colour 0 - 0xFFFF + drawStar(x, y, c); + } + + delay(2000); + + // Now go bananas and draw 500 more + for (int i = 0; i < 500; i++) + { + int x = random(240 - 70); + int y = random(320 - 80); + int c = random(0x10000); // Random colour + drawStar(x, y, c); + yield(); // Stop watchdog reset + } + + delay(2000); +} +// ========================================================================= +// Create sprite, plot graphics in it, plot to screen, then delete sprite +// ========================================================================= +void drawStar(int x, int y, int star_color) +{ + // 1 bpp colour values can only be 1 or 0 (one or zero) + uint16_t transparent = 0; // The transparent colour, can only be 1 or 0 + + // Create an 1 bit (2 colour) sprite 70x80 pixels (uses 70*80/8 = 700 bytes of RAM) + // Colour depths of 8 bits per pixel and 16 bits are also supported. + img.setColorDepth(BITS_PER_PIXEL); // Set colour depth first + img.createSprite(70, 80); // then create the sprite + + // Fill Sprite with the colour that will be defined later as "transparent" + // We could also fill with any colour as transparent, and later specify that + // same colour when we push the Sprite onto the display screen. + img.fillSprite(transparent); + + // Draw 2 triangles to create a filled in star + img.fillTriangle(35, 0, 0, 59, 69, 59, star_color); + img.fillTriangle(35, 79, 0, 20, 69, 20, star_color); + + // Punch a star shaped hole in the middle with a smaller "transparent" star + img.fillTriangle(35, 7, 6, 56, 63, 56, transparent); + img.fillTriangle(35, 73, 6, 24, 63, 24, transparent); + + // Set the 2 pixel colours that 1 and 0 represent on the display screen + img.setBitmapColor(star_color, transparent); + + // Push sprite to TFT screen CGRAM at coordinate x,y (top left corner) + // Specify what colour is to be treated as transparent (black in this example) + img.pushSprite(x, y, transparent); + + // Delete Sprite to free memory, creating and deleting takes very little time. + img.deleteSprite(); +} +// ========================================================================= + diff --git a/examples/Sprite/1_bit_Yin_Yang/1_bit_Yin_Yang.ino b/examples/Sprite/1_bit_Yin_Yang/1_bit_Yin_Yang.ino new file mode 100644 index 0000000..40fccb4 --- /dev/null +++ b/examples/Sprite/1_bit_Yin_Yang/1_bit_Yin_Yang.ino @@ -0,0 +1,97 @@ +// This sketch draws a rotating Yin and Yang symbol. It illustrates +// the drawimg and rendering of simple animated graphics using +// a 1 bit per pixel (1 bpp) Sprite. + +// Note: TFT_BLACK sets the pixel value to 0 +// Any other colour sets the pixel value to 1 + +// A square sprite of side = 2 x RADIUS will be created +// (80 * 80)/8 = 800 bytes needed for 1 bpp sprite +// 6400 bytes for 8 bpp +// 12800 bytes for 16 bpp +#define RADIUS 40 // Radius of completed symbol = 40 + +#define WAIT 0 // Loop delay + +// 1bpp Sprites are economical on memory but slower to render +#define COLOR_DEPTH 1 // Colour depth (1, 8 or 16 bits per pixel) + +// Rotation angle increment and start angle +#define ANGLE_INC 3 +int angle = 0; + +#include // Hardware-specific library + +TFT_eSPI tft = TFT_eSPI(); // Invoke library + +TFT_eSprite img = TFT_eSprite(&tft); // Sprite class + + +// ------------------------------------------------------------------------- +void setup(void) +{ + tft.begin(); + tft.setRotation(0); + tft.fillScreen(TFT_BLUE); + + img.setColorDepth(COLOR_DEPTH); + img.createSprite(RADIUS*2+1, RADIUS*2+1); + img.fillSprite(TFT_BLACK); +} +// ------------------------------------------------------------------------- +// ------------------------------------------------------------------------- +void loop() { + // Draw Yin and Yang symbol circles into Sprite + yinyang(RADIUS, RADIUS, angle, RADIUS); + + // Set the 2 pixel palette colours that 1 and 0 represent on the display screen + img.setBitmapColor(TFT_WHITE, TFT_BLACK); + + // Push Sprite image to the TFT screen at x, y + img.pushSprite(tft.width()/2 - RADIUS, 0); // Plot sprite + + angle+=3; //Increment angle to rotate circle positions + if (angle > 359) angle = 0; // Limit angle range + + // Slow things down + delay(WAIT); +} +// ------------------------------------------------------------------------- + + +// ========================================================================= +// Draw circles for Yin and Yang - rotate positions to create symbol +// ========================================================================= +// x,y == coordinate center within Sprite +// start_angle = 0 - 359 +// r = radius + +int yinyang(int x, int y, int start_angle, int r) +{ + int x1 = 0; // getCoord() will update these + int y1 = 0; + + getCoord(x, y, &x1, &y1, r/2, start_angle); // Get x1 ,y1 + img.fillCircle( x1, y1, r/2, TFT_WHITE); + img.fillCircle( x1, y1, r/8, TFT_BLACK); + + getCoord(x, y, &x1, &y1, r/2, start_angle + 180); + img.fillCircle( x1, y1, r/2, TFT_BLACK); + img.fillCircle( x1, y1, r/8, TFT_WHITE); + + img.drawCircle(x, y, r, TFT_WHITE); +} + +// ========================================================================= +// Get coordinates of end of a vector, pivot at x,y, length r, angle a +// ========================================================================= +// Coordinates are returned to caller via the xp and yp pointers +#define RAD2DEG 0.0174532925 +void getCoord(int x, int y, int *xp, int *yp, int r, int a) +{ + float sx1 = cos( (a-90) * RAD2DEG ); + float sy1 = sin( (a-90) * RAD2DEG ); + *xp = sx1 * r + x; + *yp = sy1 * r + y; +} + From 7f98aeb63f43523f51b25bd0a3d46aa8e2e0624e Mon Sep 17 00:00:00 2001 From: Bodmer Date: Sun, 1 Apr 2018 15:27:10 +0100 Subject: [PATCH 108/150] Bug fix for Issue #114 The ESP32 now works OK with the RPi display. --- TFT_eSPI.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TFT_eSPI.h b/TFT_eSPI.h index 49571eb..0dc49b3 100644 --- a/TFT_eSPI.h +++ b/TFT_eSPI.h @@ -193,7 +193,7 @@ //#define RD_H digitalWrite(TFT_WR, HIGH) #endif -#elif defined (SEND_16_BITS) +#elif defined (RPI_ILI9486_DRIVER) #define tft_Write_8(C) SPI.transfer(0); SPI.transfer(C) #define tft_Write_16(C) SPI.write16(C) #define tft_Write_32(C) SPI.write32(C) From d5f0812bbbb505ed29d732f8e4c585620166bc1b Mon Sep 17 00:00:00 2001 From: Bodmer Date: Sun, 1 Apr 2018 16:37:53 +0100 Subject: [PATCH 109/150] Fix touch issue #114 Also had to mirror the coordinate transform. --- Extensions/Touch.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Extensions/Touch.cpp b/Extensions/Touch.cpp index d077e13..e848f19 100644 --- a/Extensions/Touch.cpp +++ b/Extensions/Touch.cpp @@ -53,7 +53,7 @@ uint16_t TFT_eSPI::getTouchRawZ(void){ T_CS_L; // Z sample request - uint16_t tz = 0xFFF; + int16_t tz = 0xFFF; SPI.transfer(0xb1); tz += SPI.transfer16(0xc1) >> 3; tz -= SPI.transfer16(0x91) >> 3; @@ -62,7 +62,7 @@ uint16_t TFT_eSPI::getTouchRawZ(void){ spi_end_touch(); - return tz; + return (uint16_t)tz; } /*************************************************************************************** @@ -203,7 +203,7 @@ void TFT_eSPI::calibrateTouch(uint16_t *parameters, uint32_t color_fg, uint32_t for(uint8_t j= 0; j<8; j++){ // Use a lower detect threshold as corners tend to be less sensitive - while(!validTouch(&x_tmp, &y_tmp, Z_THRESHOLD/4)); + while(!validTouch(&x_tmp, &y_tmp, Z_THRESHOLD/2)); values[i*2 ] += x_tmp; values[i*2+1] += y_tmp; } @@ -230,20 +230,20 @@ void TFT_eSPI::calibrateTouch(uint16_t *parameters, uint32_t color_fg, uint32_t touchCalibration_y1 = (values[3] + values[7])/2; // calc max y } - // in addition, the touch screen axis could be in the opposit direction of the TFT axis - touchCalibration_invert_x = false; + // in addition, the touch screen axis could be in the opposite direction of the TFT axis + touchCalibration_invert_x = true; if(touchCalibration_x0 > touchCalibration_x1){ values[0]=touchCalibration_x0; touchCalibration_x0 = touchCalibration_x1; touchCalibration_x1 = values[0]; - touchCalibration_invert_x = true; + touchCalibration_invert_x = false; } - touchCalibration_invert_y = false; + touchCalibration_invert_y = true; if(touchCalibration_y0 > touchCalibration_y1){ values[0]=touchCalibration_y0; touchCalibration_y0 = touchCalibration_y1; touchCalibration_y1 = values[0]; - touchCalibration_invert_y = true; + touchCalibration_invert_y = false; } // pre calculate From ada678d76e71207c0b10be1c65c2bb92ebfb1cfe Mon Sep 17 00:00:00 2001 From: Bodmer Date: Mon, 2 Apr 2018 02:53:42 +0100 Subject: [PATCH 110/150] Added Waveshare ePaper support Also added ability to push 1bpp bitmaps to a Sprite to support rendering images on an EPaper screen. Floyd-Steinberg dithering and basic graphics example added as first ePaper demo. --- Extensions/Sprite.cpp | 75 ++++++- TFT_eSPI.cpp | 1 - User_Setup_Select.h | 18 +- User_Setups/Setup17_ePaper.h | 94 +++++++++ examples/ePaper/Floyd_Steinberg/EPD_Support.h | 105 ++++++++++ .../Floyd_Steinberg/Floyd_Steinberg.ino | 187 +++++++++++++++++ .../Floyd_Steinberg/Floyd_Steinberg_BMP.ino | 198 ++++++++++++++++++ examples/ePaper/Floyd_Steinberg/SPIFFS.ino | 92 ++++++++ .../ePaper/Floyd_Steinberg/data/TestCard.bmp | Bin 0 -> 47542 bytes .../ePaper/Floyd_Steinberg/data/Tiger.bmp | Bin 0 -> 42262 bytes 10 files changed, 760 insertions(+), 10 deletions(-) create mode 100644 User_Setups/Setup17_ePaper.h create mode 100644 examples/ePaper/Floyd_Steinberg/EPD_Support.h create mode 100644 examples/ePaper/Floyd_Steinberg/Floyd_Steinberg.ino create mode 100644 examples/ePaper/Floyd_Steinberg/Floyd_Steinberg_BMP.ino create mode 100644 examples/ePaper/Floyd_Steinberg/SPIFFS.ino create mode 100644 examples/ePaper/Floyd_Steinberg/data/TestCard.bmp create mode 100644 examples/ePaper/Floyd_Steinberg/data/Tiger.bmp diff --git a/Extensions/Sprite.cpp b/Extensions/Sprite.cpp index 90fadae..0ebca60 100644 --- a/Extensions/Sprite.cpp +++ b/Extensions/Sprite.cpp @@ -304,7 +304,42 @@ void TFT_eSprite::pushImage(int32_t x, int32_t y, uint32_t w, uint32_t h, uint1 } } } - // TODO Currently does nothing for 1 bpp + + else // 1bpp + { + // Move coordinate rotation to support fn + if (_rotation == 1) + { + int32_t tx = x; + x = _dwidth - y - 1; + y = tx; + } + else if (_rotation == 2) + { + x = _dwidth - x - 1; + y = _dheight - y - 1; + } + else if (_rotation == 3) + { + int32_t tx = x; + x = y; + y = _dheight - tx - 1; + } + + uint8_t* pdata = (uint8_t*) data; + uint32_t ww = (w+7) & 0xFFF8; + for (int32_t yp = 0; yp // Setup file configured for my ST7735 128x128 display //#include // Setup file configured for my ILI9163 128x128 display //#include // Setup file configured for my ST7735 -//#include // Setup file configured for my stock RPi TFT with touch -//#include // Setup file configured for my stock RPi TFT with touch +//#include // Setup file configured for ESP8266 and RPi TFT with touch +//#include // Setup file configured for ESP32 and RPi TFT with touch //#include // Setup file for the ESP32 based M5Stack //#include // Setup file for the ESP32 with parallel bus TFT //#include // Setup file for the ESP32 with parallel bus TFT //#include // Setup file configured for HX8357D (untested) //#include // Setup file for the ESP32 with parallel bus TFT +//#include // Setup file for any Waveshare ePaper display -//#include - -// ePaper #include // Setup file template for copying/editting +//#include #endif // USER_SETUP_LOADED @@ -70,7 +69,7 @@ #define TFT_DRIVER 0x6D02 #elif defined (RPI_ILI9486_DRIVER) #include - #define TFT_DRIVER 0x9481 + #define TFT_DRIVER 0x9486 #elif defined (ILI9481_DRIVER) #include #define TFT_DRIVER 0x9481 @@ -83,6 +82,11 @@ #elif defined (EPD_DRIVER) #include "TFT_Drivers/EPD_Defines.h" #define TFT_DRIVER 0xE9D +#elif defined (XYZZY_DRIVER) // <<<<<<<<<<<<<<<<<<<<<<<< ADD NEW DRIVER HERE + #include "TFT_Drivers/XYZZY_Defines.h" + #define TFT_DRIVER 0x0000 +#else + #define TFT_DRIVER 0x0000 #endif // These are the pins for all ESP8266 boards diff --git a/User_Setups/Setup17_ePaper.h b/User_Setups/Setup17_ePaper.h new file mode 100644 index 0000000..3704cc5 --- /dev/null +++ b/User_Setups/Setup17_ePaper.h @@ -0,0 +1,94 @@ +// USER DEFINED SETTINGS +// Set driver type, fonts to be loaded etc +// +// See the User_Setup_Select.h file if you wish to be able to define multiple +// setups and then easily select which setup file is used by the compiler. +// +// If this file is edited correctly then all the library example sketches should +// run without the need to make any more changes for a particular hardware setup! + +// ################################################################################## +// +// Section 0. Call up the right driver file and any options for it +// +// ################################################################################## + +#define EPD_DRIVER // ePaper driver + +// ################################################################################## +// +// Section 1. Define the pins that are used to interface with the display here +// +// ################################################################################## + + +// READ THIS READ THIS READ THIS READ THIS READ THIS READ THIS +// Install the ePaper library for your own display size and type +// from here: +// https://github.com/Bodmer/EPD_Libraries + +// Note: Pin allocations for the ePaper signals are defined in +// the ePaper library's epdif.h file. There follows the default +// pins already included in epdif.h file for the ESP8266: + +/////////////////////////////////////////////////////////////////// +// For ESP8266 connect as follows: // +// Display 3.3V to NodeMCU 3V3 // +// Display GND to NodeMCU GND // +// // +// Display GPIO NodeMCU pin // +// BUSY 5 D1 // +// RESET 4 D2 // +// DC 0 D3 // +// CS 2 D4 // +// CLK 14 D5 // +// D6 (MISO not connected to display) // +// DIN 13 D7 // +// // +/////////////////////////////////////////////////////////////////// + + +// ################################################################################## +// +// Section 2. Not used +// +// ################################################################################## + + +// ################################################################################## +// +// Section 3. Define the fonts that are to be used here +// +// ################################################################################## + +// Comment out the #defines below with // to stop that font being loaded +// The ESP8366 and ESP32 have plenty of memory so commenting out fonts is not +// normally necessary. If all fonts are loaded the extra FLASH space required is +// about 17Kbytes. To save FLASH space only enable the fonts you need! + +#define LOAD_GLCD // Font 1. Original Adafruit 8 pixel font needs ~1820 bytes in FLASH +#define LOAD_FONT2 // Font 2. Small 16 pixel high font, needs ~3534 bytes in FLASH, 96 characters +#define LOAD_FONT4 // Font 4. Medium 26 pixel high font, needs ~5848 bytes in FLASH, 96 characters +#define LOAD_FONT6 // Font 6. Large 48 pixel font, needs ~2666 bytes in FLASH, only characters 1234567890:-.apm +#define LOAD_FONT7 // Font 7. 7 segment 48 pixel font, needs ~2438 bytes in FLASH, only characters 1234567890:-. +//#define LOAD_FONT8N // Font 8. Alternative to Font 8 below, slightly narrower, so 3 digits fit a 160 pixel TFT +#define LOAD_FONT8 // Font 8. Large 75 pixel font needs ~3256 bytes in FLASH, only characters 1234567890:-. +#define LOAD_GFXFF // FreeFonts. Include access to the 48 Adafruit_GFX free fonts FF1 to FF48 and custom fonts + +// Comment out the #define below to stop the SPIFFS filing system and smooth font code being loaded +// this will save ~20kbytes of FLASH +#define SMOOTH_FONT + +// ################################################################################## +// +// Section 4. Not used +// +// ################################################################################## + + +// ################################################################################## +// +// Section 5. Not used +// +// ################################################################################## + diff --git a/examples/ePaper/Floyd_Steinberg/EPD_Support.h b/examples/ePaper/Floyd_Steinberg/EPD_Support.h new file mode 100644 index 0000000..ce3ce1d --- /dev/null +++ b/examples/ePaper/Floyd_Steinberg/EPD_Support.h @@ -0,0 +1,105 @@ +/* Support definitions and functions for ePaper examples + * These tailor the library and screen settings + * Must be a header file to ensure #defines are established first + * + * Created by Bodmer 30/3/18 for TFT_eSPI library: + * https://github.com/Bodmer/TFT_eSPI + */ + +/* + EPD_WIDTH and EPD_HEIGHT are automatically defined here based on the library selected + + For 2 colour ePaper displays create one frame pointer in sketch: + uint8_t* framePtr; + + For 3 colour ePaper displays create two frame pointers in sketch: + uint8_t* blackFramePtr; + uint8_t* redFramePtr; + + Call this function to update whole display: + updateDisplay(); +*/ +// Install the ePaper library for your own display size and type +// from here: +// https://github.com/Bodmer/EPD_Libraries + + +//------------------------------------------------------------------------------------ +// Define which colour values are paper and ink +//------------------------------------------------------------------------------------ +#if defined (EPD2IN7B_H) + #define COLORED 1 // EPD2IN7B is opposite to all others! + #define UNCOLORED 0 +#else + #define COLORED 0 + #define UNCOLORED 1 +#endif + + +//------------------------------------------------------------------------------------ +// Define the width and height of the different displays +//------------------------------------------------------------------------------------ +#if defined (EPD1IN54_H) || defined (EPD1IN54B_H) + #define EPD_WIDTH 200 // Frame buffer is 5000 bytes + #define EPD_HEIGHT 200 + +#elif defined (EPD1IN54C_H) + #define EPD_WIDTH 152 // 2 frame buffers of 2888 bytes each + #define EPD_HEIGHT 152 + +#elif defined (EPD2IN7_H) || defined (EPD2IN7B_H) + #define EPD_WIDTH 176 // Frame buffer is 5808 bytes + #define EPD_HEIGHT 264 + +#elif defined (EPD2IN9_H) || defined (EPD2IN9B_H) + #define EPD_WIDTH 128 // Frame buffer is 4736 bytes + #define EPD_HEIGHT 296 + +#elif defined (EPD2IN13_H) + #define EPD_WIDTH 122 // Frame buffer is 4000 bytes + #define EPD_HEIGHT 250 + +#elif defined (EPD2IN13B_H) + #define EPD_WIDTH 104 // 2 frame buffers of 2756 bytes each + #define EPD_HEIGHT 212 + +#elif defined (EPD4IN2_H) + #define EPD_WIDTH 400 // Frame buffer is 15000 bytes + #define EPD_HEIGHT 300 + +// ESP8266 has just enough RAM for a 2 color 7.5" display full screen buffer +// ESP32 has just enough RAM for 2 or 3 color 7.5" display +// (Without using partial screen updates) +#elif defined (EPD7IN5_H) || defined (EPD7IN5B_H) + #define EPD_WIDTH 640 // 2 colour frame buffer is 30720 bytes + #define EPD_HEIGHT 384 // 2 colour frame buffer is 61440 bytes + +#else + # error "Selected ePaper library is not supported" + +#endif + + +//------------------------------------------------------------------------------------ +// Update display - different displays have different function names in the default +// Waveshare libraries :-( +//------------------------------------------------------------------------------------ +#if defined (EPD1IN54B_H) || defined(EPD1IN54C_H) || defined(EPD2IN13B_H) || defined(EPD2IN7B_H) || defined(EPD2IN9B_H) || defined(EPD4IN2_H) + void updateDisplay(uint8_t* blackFrame = blackFramePtr, uint8_t* redFrame = redFramePtr) + { + ePaper.DisplayFrame(blackFrame, redFrame); // Update 3 colour display +#else + void updateDisplay(uint8_t* blackFrame = framePtr) + { + #if defined (EPD2IN7_H) || defined(EPD4IN2_H) + ePaper.DisplayFrame(blackFrame); // Update 2 color display + + #elif defined (EPD1IN54_H) || defined(EPD2IN13_H) || defined(EPD2IN9_H) + ePaper.SetFrameMemory(blackFrame); // Update 2 colour display + + #else + # error "Selected ePaper library is not supported" + #endif +#endif +} + diff --git a/examples/ePaper/Floyd_Steinberg/Floyd_Steinberg.ino b/examples/ePaper/Floyd_Steinberg/Floyd_Steinberg.ino new file mode 100644 index 0000000..1245a8d --- /dev/null +++ b/examples/ePaper/Floyd_Steinberg/Floyd_Steinberg.ino @@ -0,0 +1,187 @@ +// Display grey-scale images on a Monchrome ePaper display using +// Floyd-Steinberg dithering +// https://en.wikipedia.org/wiki/Floyd%E2%80%93Steinberg_dithering + +// Example created by Bodmer 31/3/18 for TFT_eSPI library: +// https://github.com/Bodmer/TFT_eSPI +// Select the ePaper setup in library's "User_Setup_Select.h" file + +// This sketch supports Waveshare 2 colour ePaper displays +// https://www.waveshare.com/product/modules/oleds-lcds/e-paper.htm + +// Test images are in the Data folder with sketch (press Ctrl+k) +// Upload using the Tools menu "ESP8266 Sketch Data Upload" option + +/////////////////////////////////////////////////////////////////// +// For ESP8266 connect as follows: // +// Display 3.3V to NodeMCU 3V3 // +// Display GND to NodeMCU GND // +// // +// Display GPIO NodeMCU pin // +// BUSY 5 D1 // +// RESET 4 D2 // +// DC 0 D3 // +// CS 2 D4 // +// CLK 14 D5 // +// D6 (MISO not connected to display) // +// DIN 13 D7 // +// // +// Note: Pin allocations for the ePaper signals are defined in // +// ePaper library's epdif.h file, above are the default pins // +/////////////////////////////////////////////////////////////////// + +// READ THIS READ THIS READ THIS READ THIS READ THIS READ THIS +// Install the ePaper library for your own display size and type +// from here: +// https://github.com/Bodmer/EPD_Libraries + +// The following is for the Waveshare 2.7" colour ePaper display +// include where ?.?? is screen size in inches + +#include // Screen specific library +Epd ePaper; // Create an instance ePaper + +#include // Graphics library and Sprite class + +TFT_eSPI glc = TFT_eSPI(); // Invoke the graphics library class +TFT_eSprite frame = TFT_eSprite(&glc); // Invoke the Sprite class for the image frame buffer + +#define INK COLORED // Black ink +#define PAPER UNCOLORED // 'paper' background colour + +uint16_t epd_width = EPD_WIDTH; // Set the initial values, these are swapped +uint16_t epd_height = EPD_HEIGHT; // in different landscape/portrait rotations + // so call frame.width() or frame.height() to get new values + +#define EPD_BUFFER 1 // Label for the black frame buffer 1 + +uint8_t* framePtr = NULL; // Pointer for the black frame buffer + +#include "EPD_Support.h" // Include sketch EPD support functions last! + +int8_t limit = 5; // Limit the number of loops before halting +//------------------------------------------------------------------------------------ +// Setup +//------------------------------------------------------------------------------------ +void setup() { + + Serial.begin(250000); // Used for messages + + // Initialise the ePaper library + if (ePaper.Init() != 0) { + Serial.print("ePaper init failed"); + while (1) yield(); // Wait here until re-boot + } + + Serial.println("\r\n ePaper initialisation OK"); + + // Initialise the SPIFFS filing system + if (!SPIFFS.begin()) { + Serial.println("SPIFFS initialisation failed!"); + while (1) yield(); // Stay here twiddling thumbs + } + + Serial.println(" SPIFFS initialisation OK"); + + frame.setColorDepth(1); // Must set the bits per pixel to 1 for ePaper displays + // Set bit depth BEFORE creating Sprite, default is 16! + + // Create a frame buffer in RAM of defined size and save the pointer to it + // RAM needed is about (EPD_WIDTH * EPD_HEIGHT)/8 , ~5000 bytes for 200 x 200 pixels + // Note: always create the Sprite before setting the Sprite rotation + framePtr = (uint8_t*) frame.createSprite(EPD_WIDTH, EPD_HEIGHT); + + Serial.println("\r\nInitialisation done."); + + listFiles(); // List all the files in the SPIFFS +} + +//------------------------------------------------------------------------------------ +// Loop +//------------------------------------------------------------------------------------ +void loop() { + + frame.setRotation(random(4)); // Set the rotation to 0, 1, 2 or 3 ( 1 & 3 = landscape) + + frame.fillSprite(PAPER); + + // Draw 8 bit grey-scale bitmap using Floyd-Steinberg dithering at x,y + // /File name x y + //drawFSBmp("/TestCard.bmp", 0, 0); // 176 x 264 pixels + + drawFSBmp("/Tiger.bmp", (frame.width()-176)/2, (frame.height()-234)/2); // 176 x 234 pixels + + updateDisplay(); // Send image to display and refresh + + delay(5000); + + frame.fillSprite(PAPER); // Fill frame with white + + // Draw circle in frame buffer (x, y, r, color) in center of screen + frame.drawCircle(frame.width()/2, frame.height()/2, frame.width()/6, INK); + + // Draw diagonal lines + frame.drawLine(0 , 0, frame.width()-1, frame.height()-1, INK); + frame.drawLine(0 , frame.height()-1, frame.width()-1, 0, INK); + + updateDisplay(); // Send image to display and refresh + + delay(3000); + + // Run a rotation test + rotateTest(); + + // Put screen to sleep to save power (if wanted) + ePaper.Sleep(); + + if (--limit <= 0) while(1) yield(); // Wait here + + delay(20000); // Wait here for 20s + + // Wake up ePaper display so we can talk to it + Serial.println("Waking up!"); + ePaper.Init(); + +} // end of loop() + + +//------------------------------------------------------------------------------------ +// setRotation() actually rotates the drawing coordinates, not the whole display frame +// buffer so we can use this to draw text at right angles or upside down +//------------------------------------------------------------------------------------ +void rotateTest(void) +{ + //frame.fillSprite(PAPER); // Fill buffer with white to clear old graphics + + // Draw some text in frame buffer + frame.setTextFont(4); // Select font 4 + frame.setTextColor(INK); // Set colour to ink + frame.setTextDatum(TC_DATUM); // Middle centre text datum + + frame.setRotation(0); // Set the display rotation to 0, 1, 2 or 3 ( 1 & 3 = landscape) + epd_width = frame.width(); // Get the values for the current rotation + epd_height = frame.height(); // epd_height is not used in this sketch + + frame.drawString("Rotation 0", epd_width / 2, 10); + + frame.setRotation(1); // Set the display rotation to 1 + epd_width = frame.width(); // Get the values for the current rotation + epd_height = frame.height(); // epd_height is not used in this sketch + + frame.drawString("Rotation 1", epd_width / 2, 10); + + frame.setRotation(2); // Set the display rotation to 2 + epd_width = frame.width(); // Get the values for the current rotation + epd_height = frame.height(); // epd_height is not used in this sketch + + frame.drawString("Rotation 2", epd_width / 2, 10); + + frame.setRotation(3); // Set the display rotation to 3 + epd_width = frame.width(); // Get the values for the current rotation + epd_height = frame.height(); // epd_height is not used in this sketch + + frame.drawString("Rotation 3", epd_width / 2, 10); + + Serial.println("Updating display"); + updateDisplay(); // Update display +} diff --git a/examples/ePaper/Floyd_Steinberg/Floyd_Steinberg_BMP.ino b/examples/ePaper/Floyd_Steinberg/Floyd_Steinberg_BMP.ino new file mode 100644 index 0000000..e78c741 --- /dev/null +++ b/examples/ePaper/Floyd_Steinberg/Floyd_Steinberg_BMP.ino @@ -0,0 +1,198 @@ +/* + Support function for Floyd-Steinberg dithering of an 8bit grey-scale BMP image + on a Monochrome display: + https://en.wikipedia.org/wiki/Floyd%E2%80%93Steinberg_dithering + + Bitmap format: + https://en.wikipedia.org/wiki/BMP_file_format + + Example for https://github.com/Bodmer/TFT_eSPI + + The MIT License (MIT) + Copyright (c) 2015 by Bodmer + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYBR_DATUM HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + + Note: drawFSBmp() is a simplified function and does not handle all possible + BMP file header variants. It works OK with 8 bit per pixel grey-scale images + generated by MS Paint and IrfanView. +*/ + +// https://github.com/Bodmer/TFT_eSPI + +//==================================================================================== +// Draw an 8 bit grey-scale bitmap (*.BMP) on a Monochrome display using dithering +//==================================================================================== +// Uses RAM for buffers (3 * width + 4) ( 532 bytes for 176 pixels) + +// Image must be stored in ESP8266 or ESP32 SPIFFS + +// Quantisation error distribution for pixel X +// (This is for bottum up drawing of the BMP) +// |-------|-------|-------| +// | +3/16 | +5/16 | +1/16 | +// |-------|-------|-------| +// | | X | +7/16 | +// |-------|-------|-------| +// + +void drawFSBmp(const char *filename, int16_t x, int16_t y) { + + if ((x >= frame.width()) || (y >= frame.height())) return; + + fs::File bmpFS; + + // Open requested file + bmpFS = SPIFFS.open( filename, "r"); + + if (!bmpFS) + { + Serial.print("File not found"); + return; + } + + uint32_t seekOffset, dib_size; + uint16_t w, h, row, col, num_colors; + uint8_t r, g, b; + + if (read16(bmpFS) == 0x4D42) // Check it is a valid bitmap header + { + read32(bmpFS); + read32(bmpFS); + seekOffset = read32(bmpFS); // Pointer to image start + dib_size = read32(bmpFS); // DIB header size, typically 40 bytes + + w = read32(bmpFS); // Get width and height of image + h = read32(bmpFS); + + // Check it is 1 plane and 8 bits per pixel and no compression + if ((read16(bmpFS) == 1) && (read16(bmpFS) == 8) && (read32(bmpFS) == 0)) + { + read32(bmpFS); // Throw away image size + read32(bmpFS); // Throw away x pixels per meter + read32(bmpFS); // Throw away y pixels per meter + + num_colors = read32(bmpFS); // Number of colours in colour table (usually 256) + + uint8_t pixel_color[num_colors]; // Lookup table for grey-scale + + bmpFS.seek(14 + dib_size); // Seek to start of colour table + + // Capture the colour lookup table + for (uint16_t i = 0; i < num_colors; i++) + { + uint32_t abgr = read32(bmpFS); // Assume 4 byte, RGB colours in LS 3 bytes + pixel_color[i] = (uint8_t) abgr; // For grey-scale R, G, B are same value + } + + bmpFS.seek(seekOffset); // Seek to start of image + + uint16_t padding = (4 - (w & 3)) & 3; // Calculate the BMP line padding + + // Create an zero an 8 bit pixel line buffer + uint8_t* lineBuffer = ( uint8_t*) calloc(w , sizeof(uint8_t)); + + // Create a 16 bit signed line buffer for the quantisation error + // Diffusion spreads to x-1 and x+1 so w + 2 avoids a bounds check + int16_t* qerrBuffer = ( int16_t*) calloc((w + 2)<<1, sizeof(uint8_t)); + + y += h - 1; // Start from bottom (assumes bottum up!) + + // Draw row by row from bottom up + for (row = 0; row < h; row++) { + + // Read a row of pixels + bmpFS.read(lineBuffer, w); + + // Prep variables + uint16_t dx = 0; + uint8_t* bptr = lineBuffer; + int16_t* qptr = qerrBuffer + 1; // + 1 because diffusion spreads to x-1 + + // Lookup color, add quantisation error, clip and clear error buffer + while(dx < w) + { + int16_t depixel = pixel_color[(uint8_t)*bptr] + *qptr; + if (depixel >255) depixel = 255; // Clip pixel to 0-255 + else if (depixel < 0) depixel = 0; + *bptr++ = (uint8_t) depixel; // Save new value, inc pointer + *qptr++ = 0; // Zero error, inc pointer + dx++; // Next pixel + } + + dx = 0; // Reset varaibles to start of line + bptr = lineBuffer; + qptr = qerrBuffer + 1; + int32_t qerr = 0; + int32_t qerr16 = 0; + + // Push the pixel row to screen + while(dx < w) + { + // Add 7/16 of error (error = 0 on first entry) + int16_t pixel = *bptr + (qerr>>1) - qerr16; + + // Do not clip here so quantisation error accumulates correctly? + // Draw pixel (black or white) and determine new error + if (pixel < 128) { frame.drawPixel(x + dx, y, INK); qerr = pixel; } + else qerr = pixel - 255; + + // Diffuse into error buffer for next pixel line + qerr16 = qerr>>4; // 1/16 of error + *(qptr - 1) += (qerr>>2) - qerr16; // Add 3/16 of error + *(qptr ) += (qerr>>2) + qerr16; // Add 5/16 of error + *(qptr + 1) += qerr16; // Add 1/16 of error + + *bptr++; // Move along pixel and error buffers + *qptr++; + dx++; // Move coordinate along + } + y--; + + // Read any line padding (saves a slow seek) + if (padding) bmpFS.read(lineBuffer, padding); + } + } + else Serial.println("BMP format not recognized."); + } + bmpFS.close(); +} + +//==================================================================================== +// Read a 16 bit value from the filing system +//==================================================================================== +uint16_t read16(fs::File &f) { + uint16_t result; + ((uint8_t *)&result)[0] = f.read(); // LSB + ((uint8_t *)&result)[1] = f.read(); // MSB + return result; +} + +//==================================================================================== +// Read a 32 bit value from the filing system +//==================================================================================== +uint32_t read32(fs::File &f) { + uint32_t result; + ((uint8_t *)&result)[0] = f.read(); // LSB + ((uint8_t *)&result)[1] = f.read(); + ((uint8_t *)&result)[2] = f.read(); + ((uint8_t *)&result)[3] = f.read(); // MSB + return result; +} + +// TODO: Add support for colour images by converting RGB to grey-scale +// grey = (R+G+B)/3 + diff --git a/examples/ePaper/Floyd_Steinberg/SPIFFS.ino b/examples/ePaper/Floyd_Steinberg/SPIFFS.ino new file mode 100644 index 0000000..ff35f61 --- /dev/null +++ b/examples/ePaper/Floyd_Steinberg/SPIFFS.ino @@ -0,0 +1,92 @@ + + // Call up the SPIFFS FLASH filing system + #define FS_NO_GLOBALS + #include + + #ifdef ESP32 + #include "SPIFFS.h" + #endif + + /*==================================================================================== + This sketch supports the ESP6266 and ESP32 SPIFFS filing system + + Created by Bodmer 15th Jan 2017 + ==================================================================================*/ + +//==================================================================================== +// Print a SPIFFS directory list (root directory) +//==================================================================================== + +void listFiles(void) { + Serial.println(); + Serial.println("SPIFFS files found:"); + +#ifdef ESP32 + listDir(SPIFFS, "/", true); +#else + fs::Dir dir = SPIFFS.openDir("/"); // Root directory + String line = "====================================="; + + Serial.println(line); + Serial.println(" File name Size"); + Serial.println(line); + + while (dir.next()) { + String fileName = dir.fileName(); + Serial.print(fileName); + int spaces = 25 - fileName.length(); // Tabulate nicely + if (spaces < 0) spaces = 1; + while (spaces--) Serial.print(" "); + fs::File f = dir.openFile("r"); + Serial.print(f.size()); Serial.println(" bytes"); + yield(); + } + + Serial.println(line); +#endif + Serial.println(); + delay(1000); +} +//==================================================================================== + +#ifdef ESP32 +void listDir(fs::FS &fs, const char * dirname, uint8_t levels) { + Serial.printf("Listing directory: %s\n", dirname); + + fs::File root = fs.open(dirname); + if (!root) { + Serial.println("Failed to open directory"); + return; + } + if (!root.isDirectory()) { + Serial.println("Not a directory"); + return; + } + + fs::File file = root.openNextFile(); + while (file) { + + if (file.isDirectory()) { + Serial.print("DIR : "); + String fileName = file.name(); + Serial.print(fileName); + if (levels) { + listDir(fs, file.name(), levels - 1); + } + } else { + String fileName = file.name(); + Serial.print(" " + fileName); + int spaces = 32 - fileName.length(); // Tabulate nicely + if (spaces < 1) spaces = 1; + while (spaces--) Serial.print(" "); + String fileSize = (String) file.size(); + spaces = 8 - fileSize.length(); // Tabulate nicely + if (spaces < 1) spaces = 1; + while (spaces--) Serial.print(" "); + Serial.println(fileSize + " bytes"); + } + + file = root.openNextFile(); + } +} +#endif diff --git a/examples/ePaper/Floyd_Steinberg/data/TestCard.bmp b/examples/ePaper/Floyd_Steinberg/data/TestCard.bmp new file mode 100644 index 0000000000000000000000000000000000000000..3a18f0cd57fc8ec887f26f63b177f6beeb7cc9db GIT binary patch literal 47542 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 Szyz286JP>NfC>Dz1pWmKz)6n) literal 0 HcmV?d00001 diff --git a/examples/ePaper/Floyd_Steinberg/data/Tiger.bmp b/examples/ePaper/Floyd_Steinberg/data/Tiger.bmp new file mode 100644 index 0000000000000000000000000000000000000000..ef26eb18d6cf4b49bef1ccba605cd79b092eba06 GIT binary patch literal 42262 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 Nqcsss%~LyInCvLq}C)Z*o;=JLUqq0K)CD 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 Date: Mon, 2 Apr 2018 02:55:25 +0100 Subject: [PATCH 111/150] Raise issue --- library.json | 6 +++--- library.properties | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/library.json b/library.json index 4965527..e61cde7 100644 --- a/library.json +++ b/library.json @@ -1,8 +1,8 @@ { "name": "TFT_eSPI", - "version": "0.20.10", - "keywords": "tft, display, ESP8266, NodeMCU, ESP32, M5Stack, ILI9341, ST7735, ILI9163, S6D02A1, ILI9486", - "description": "A TFT SPI graphics library for ESP8266 and ESP32", + "version": "0.20.11", + "keywords": "tft, ePaper, display, ESP8266, NodeMCU, ESP32, M5Stack, ILI9341, ST7735, ILI9163, S6D02A1, ILI9486", + "description": "A TFT and ePaper SPI graphics library for ESP8266 and ESP32", "repository": { "type": "git", diff --git a/library.properties b/library.properties index dbbc4d4..59d35e0 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=TFT_eSPI -version=0.20.10 +version=0.20.11 author=Bodmer maintainer=Bodmer sentence=A fast TFT library for ESP8266 processors and the Arduino IDE From 1dd28f94fbc02907723dc8aede22ed62b1559c93 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Mon, 2 Apr 2018 03:12:34 +0100 Subject: [PATCH 112/150] Update ReadMe --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index c8f4733..ad04387 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +Update 2nd April 2018: Added final stage support ePaper (Waveshare) displays plus an example (others to follow). + Update 26th March 2018: Added support for 1 bit per pixel Sprites. Example to follow with ePaper (Waveshare) support. Update 10th March 2018: Added support for 8 bit parallel interface TFTs when used with ESP32 (Touch not supported yet on parallel displays) @@ -78,7 +80,7 @@ IO32 wired to IO36 Adding touch support for UNO style displays with resistive screens may be possible - to be investigated. -**4. Add support for ePaper displays** +**4. Add support for ePaper displays - done 2/4/18** The library was intended to support only TFT displays but using a Sprite as a 1 bit per pixel screen buffer permits support for the Waveshare 2 and 3 colour SPI ePaper displays. So far this is just an experiment and no code has been released for this: From f2c92fb75f7e6b13f3c0fb93602d0a9c7c16a5d5 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Tue, 3 Apr 2018 01:09:10 +0100 Subject: [PATCH 113/150] Free the memory in example! --- User_Setups/ePaper_Template.h | 76 ------------------- .../Floyd_Steinberg/Floyd_Steinberg_BMP.ino | 2 + 2 files changed, 2 insertions(+), 76 deletions(-) delete mode 100644 User_Setups/ePaper_Template.h diff --git a/User_Setups/ePaper_Template.h b/User_Setups/ePaper_Template.h deleted file mode 100644 index e704621..0000000 --- a/User_Setups/ePaper_Template.h +++ /dev/null @@ -1,76 +0,0 @@ -// USER DEFINED SETTINGS -// Set driver type, fonts to be loaded, pins used and SPI control method etc -// -// See the User_Setup_Select.h file if you wish to be able to define multiple -// setups and then easily select which setup file is used by the compiler. -// -// If this file is edited correctly then all the library example sketches should -// run without the need to make any more changes for a particular hardware setup! - -// ################################################################################## -// -// Section 0. Call up the right driver file and any options for it -// -// ################################################################################## - -// Only define one driver, the other ones must be commented out -#define EPD_DRIVER - - -// ################################################################################## -// -// Section 1. Define the pins that are used to interface with the display here -// -// ################################################################################## - -// ePaper pins are not defined here - dummy set - -//#define TFT_CS -//#define TFT_DC -//#define TFT_RST - - -// ################################################################################## -// -// Section 2. Not used -// -// ################################################################################## - - -// ################################################################################## -// -// Section 3. Define the fonts that are to be used here -// -// ################################################################################## - -// Comment out the #defines below with // to stop that font being loaded -// The ESP8366 and ESP32 have plenty of memory so commenting out fonts is not -// normally necessary. If all fonts are loaded the extra FLASH space required is -// about 17Kbytes. To save FLASH space only enable the fonts you need! - -#define LOAD_GLCD // Font 1. Original Adafruit 8 pixel font needs ~1820 bytes in FLASH -#define LOAD_FONT2 // Font 2. Small 16 pixel high font, needs ~3534 bytes in FLASH, 96 characters -#define LOAD_FONT4 // Font 4. Medium 26 pixel high font, needs ~5848 bytes in FLASH, 96 characters -#define LOAD_FONT6 // Font 6. Large 48 pixel font, needs ~2666 bytes in FLASH, only characters 1234567890:-.apm -//#define LOAD_FONT7 // Font 7. 7 segment 48 pixel font, needs ~2438 bytes in FLASH, only characters 1234567890:. -//#define LOAD_FONT8N // Font 8. Alternative to Font 8 above, slightly narrower, so 3 digits fit a 160 pixel TFT -//#define LOAD_FONT8 // Font 8. Large 75 pixel font needs ~3256 bytes in FLASH, only characters 1234567890:-. -#define LOAD_GFXFF // FreeFonts. Include access to the 48 Adafruit_GFX free fonts FF1 to FF48 and custom fonts - -// Comment out the #define below to stop the SPIFFS filing system and smooth font code being loaded -// this will save ~20kbytes of FLASH -#define SMOOTH_FONT - -// ################################################################################## -// -// Section 4. Not used -// -// ################################################################################## - - -// ################################################################################## -// -// Section 5. Not used -// -// ################################################################################## - diff --git a/examples/ePaper/Floyd_Steinberg/Floyd_Steinberg_BMP.ino b/examples/ePaper/Floyd_Steinberg/Floyd_Steinberg_BMP.ino index e78c741..a5cc6cb 100644 --- a/examples/ePaper/Floyd_Steinberg/Floyd_Steinberg_BMP.ino +++ b/examples/ePaper/Floyd_Steinberg/Floyd_Steinberg_BMP.ino @@ -165,6 +165,8 @@ void drawFSBmp(const char *filename, int16_t x, int16_t y) { // Read any line padding (saves a slow seek) if (padding) bmpFS.read(lineBuffer, padding); } + free(lineBuffer); + free(qerrBuffer); } else Serial.println("BMP format not recognized."); } From aa81a4eab85956f0bcb3ff824588ed49ce2563ec Mon Sep 17 00:00:00 2001 From: Bodmer Date: Fri, 6 Apr 2018 22:44:31 +0100 Subject: [PATCH 114/150] Correct issue #115 --- Extensions/Touch.cpp | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/Extensions/Touch.cpp b/Extensions/Touch.cpp index e848f19..f40ebf4 100644 --- a/Extensions/Touch.cpp +++ b/Extensions/Touch.cpp @@ -140,8 +140,8 @@ uint8_t TFT_eSPI::getTouch(uint16_t *x, uint16_t *y, uint16_t threshold){ if(touchCalibration_invert_y) yy = _height - yy; } else { - yy=(x_tmp-touchCalibration_x0)*_height/touchCalibration_x1; - xx=(y_tmp-touchCalibration_y0)*_width/touchCalibration_y1; + xx=(y_tmp-touchCalibration_x0)*_width/touchCalibration_x1; + yy=(x_tmp-touchCalibration_y0)*_height/touchCalibration_y1; if(touchCalibration_invert_x) xx = _width - xx; if(touchCalibration_invert_y) @@ -212,17 +212,15 @@ void TFT_eSPI::calibrateTouch(uint16_t *parameters, uint32_t color_fg, uint32_t } - - // check orientation // from case 0 to case 1, the y value changed. // If the measured delta of the touch x axis is bigger than the delta of the y axis, the touch and TFT axes are switched. touchCalibration_rotate = false; if(abs(values[0]-values[2]) > abs(values[1]-values[3])){ touchCalibration_rotate = true; - touchCalibration_x0 = (values[0] + values[4])/2; // calc min x - touchCalibration_x1 = (values[2] + values[6])/2; // calc max x - touchCalibration_y0 = (values[1] + values[3])/2; // calc min y - touchCalibration_y1 = (values[5] + values[7])/2; // calc max y + touchCalibration_x0 = (values[1] + values[3])/2; // calc min x + touchCalibration_x1 = (values[5] + values[7])/2; // calc max x + touchCalibration_y0 = (values[0] + values[4])/2; // calc min y + touchCalibration_y1 = (values[2] + values[6])/2; // calc max y } else { touchCalibration_x0 = (values[0] + values[2])/2; // calc min x touchCalibration_x1 = (values[4] + values[6])/2; // calc max x @@ -231,19 +229,19 @@ void TFT_eSPI::calibrateTouch(uint16_t *parameters, uint32_t color_fg, uint32_t } // in addition, the touch screen axis could be in the opposite direction of the TFT axis - touchCalibration_invert_x = true; + touchCalibration_invert_x = false; if(touchCalibration_x0 > touchCalibration_x1){ values[0]=touchCalibration_x0; touchCalibration_x0 = touchCalibration_x1; touchCalibration_x1 = values[0]; - touchCalibration_invert_x = false; + touchCalibration_invert_x = true; } - touchCalibration_invert_y = true; + touchCalibration_invert_y = false; if(touchCalibration_y0 > touchCalibration_y1){ values[0]=touchCalibration_y0; touchCalibration_y0 = touchCalibration_y1; touchCalibration_y1 = values[0]; - touchCalibration_invert_y = false; + touchCalibration_invert_y = true; } // pre calculate From f56184e9cdb85524da2a0e9a2784fb7a2e7fcd43 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Wed, 11 Apr 2018 01:03:26 +0100 Subject: [PATCH 115/150] Add diagnostic sketches Do not load button class is touch screen chip select not defined --- Extensions/Touch.cpp | 40 +---- TFT_eSPI.cpp | 16 +- TFT_eSPI.h | 39 +++-- User_Setup_Select.h | 2 +- .../Read_User_Setup/Read_User_Setup.ino | 151 ++++++++++++++++++ .../Test_Touch_Controller.ino | 49 ++++++ 6 files changed, 246 insertions(+), 51 deletions(-) create mode 100644 examples/Test and diagnostics/Read_User_Setup/Read_User_Setup.ino create mode 100644 examples/Test and diagnostics/Test_Touch_Controller/Test_Touch_Controller.ino diff --git a/Extensions/Touch.cpp b/Extensions/Touch.cpp index f40ebf4..8a3efa8 100644 --- a/Extensions/Touch.cpp +++ b/Extensions/Touch.cpp @@ -1,11 +1,15 @@ // The following touch screen support code by maxpautsch was merged 1/10/17 // https://github.com/maxpautsch + // Define TOUCH_CS is the user setup file to enable this code + // A demo is provided in examples Generic folder -// Additions by Bodmer to double sample and use Z value to improve detection reliability + +// Additions by Bodmer to double sample, use Z value to improve detection reliability +// and to correct rotation handling + // See license in root directory. -#ifdef TOUCH_CS // If a pin has been allocated to the Touch screen load functions /*************************************************************************************** ** Function name: getTouchRaw ** Description: read raw touch position. Return false if not pressed. @@ -283,35 +287,3 @@ void TFT_eSPI::setTouch(uint16_t *parameters){ touchCalibration_invert_x = parameters[4] & 0x02; touchCalibration_invert_y = parameters[4] & 0x04; } - - -#else // TOUCH CS is not defined so generate dummy functions that do nothing - -/*************************************************************************************** -** Function name: Dummy functions for case where chip select pin is undefined -** Description: -***************************************************************************************/ - -uint8_t TFT_eSPI::getTouchRaw(uint16_t *x, uint16_t *y){ - return true; -} - -uint16_t TFT_eSPI::getTouchRawZ(void){ - return true; -} - -uint8_t TFT_eSPI::validTouch(uint16_t *x, uint16_t *y, uint16_t threshold){ - return true; -} - -uint8_t TFT_eSPI::getTouch(uint16_t *x, uint16_t *y, uint16_t threshold){ - return true; -} - -void TFT_eSPI::calibrateTouch(uint16_t *parameters, uint32_t color_bg, uint32_t color_fg, uint8_t size){ -} - -void TFT_eSPI::setTouch(uint16_t *parameters){ -} - -#endif // TOUCH_CS diff --git a/TFT_eSPI.cpp b/TFT_eSPI.cpp index f0572ef..bf415e4 100644 --- a/TFT_eSPI.cpp +++ b/TFT_eSPI.cpp @@ -195,6 +195,13 @@ TFT_eSPI::TFT_eSPI(int16_t w, int16_t h) fontsloaded |= 0x0100; // Bit 8 set #endif +#ifdef LOAD_FONT8N + fontsloaded |= 0x0200; // Bit 9 set +#endif + +#ifdef SMOOTH_FONT + fontsloaded |= 0x8000; // Bit 15 set +#endif } @@ -4573,6 +4580,12 @@ void TFT_eSPI::getSetup(setup_t &tft_settings) tft_settings.tft_spi_freq = SPI_FREQUENCY/100000; #endif +#if defined(TFT_SPI_OVERLAP) + tft_settings.overlap = true; +#else + tft_settings.overlap = false; +#endif + tft_settings.tft_driver = TFT_DRIVER; tft_settings.tft_width = _init_width; tft_settings.tft_height = _init_height; @@ -4677,10 +4690,9 @@ void TFT_eSPI::getSetup(setup_t &tft_settings) //////////////////////////////////////////////////////////////////////////////////////// #ifdef TOUCH_CS #include "Extensions/Touch.cpp" + #include "Extensions/Button.cpp" #endif -#include "Extensions/Button.cpp" - #include "Extensions/Sprite.cpp" #ifdef SMOOTH_FONT diff --git a/TFT_eSPI.h b/TFT_eSPI.h index 0dc49b3..7fc242a 100644 --- a/TFT_eSPI.h +++ b/TFT_eSPI.h @@ -90,21 +90,27 @@ #endif #endif - -#if defined (ESP8266) && defined (D0_USED_FOR_DC) - #define DC_C digitalWrite(TFT_DC, LOW) - #define DC_D digitalWrite(TFT_DC, HIGH) -#elif defined (ESP32) - #if defined (ESP32_PARALLEL) - #define DC_C GPIO.out_w1tc = (1 << TFT_DC) // Too fast for ST7735 - #define DC_D GPIO.out_w1ts = (1 << TFT_DC) - #else - #define DC_C GPIO.out_w1ts = (1 << TFT_DC); GPIO.out_w1tc = (1 << TFT_DC) - #define DC_D GPIO.out_w1tc = (1 << TFT_DC); GPIO.out_w1ts = (1 << TFT_DC) - #endif +#ifndef TFT_DC + #define DC_C // No macro allocated so it generates no code + #define DC_D // No macro allocated so it generates no code #else - #define DC_C GPOC=dcpinmask - #define DC_D GPOS=dcpinmask + #if defined (ESP8266) && defined (D0_USED_FOR_DC) + #define DC_C digitalWrite(TFT_DC, LOW) + #define DC_D digitalWrite(TFT_DC, HIGH) + #elif defined (ESP32) + #if defined (ESP32_PARALLEL) + #define DC_C GPIO.out_w1tc = (1 << TFT_DC) // Too fast for ST7735 + #define DC_D GPIO.out_w1ts = (1 << TFT_DC) + //#define DC_C digitalWrite(TFT_DC, LOW) + //#define DC_D digitalWrite(TFT_DC, HIGH) + #else + #define DC_C GPIO.out_w1ts = (1 << TFT_DC); GPIO.out_w1tc = (1 << TFT_DC) + #define DC_D GPIO.out_w1tc = (1 << TFT_DC); GPIO.out_w1ts = (1 << TFT_DC) + #endif + #else + #define DC_C GPOC=dcpinmask + #define DC_D GPOS=dcpinmask + #endif #endif #if defined (TFT_SPI_OVERLAP) @@ -173,8 +179,12 @@ #define tft_Write_8(C) GPIO.out_w1tc = clr_mask; GPIO.out_w1ts = set_mask((uint8_t)C); WR_H // Write 16 bits to TFT +#ifdef PSEUDO_8_BIT + #define tft_Write_16(C) WR_L;GPIO.out_w1tc = clr_mask; GPIO.out_w1ts = set_mask((uint8_t)(C >> 0)); WR_H +#else #define tft_Write_16(C) GPIO.out_w1tc = clr_mask; GPIO.out_w1ts = set_mask((uint8_t)(C >> 8)); WR_H; \ GPIO.out_w1tc = clr_mask; GPIO.out_w1ts = set_mask((uint8_t)(C >> 0)); WR_H +#endif // 16 bit write with swapped bytes #define tft_Write_16S(C) GPIO.out_w1tc = clr_mask; GPIO.out_w1ts = set_mask((uint8_t) (C >> 0)); WR_H; \ @@ -340,6 +350,7 @@ typedef struct int16_t esp; uint8_t trans; uint8_t serial; +uint8_t overlap; uint16_t tft_driver; // Hexadecimal code uint16_t tft_width; // Rotation 0 width and height diff --git a/User_Setup_Select.h b/User_Setup_Select.h index 6617d5c..97db56e 100644 --- a/User_Setup_Select.h +++ b/User_Setup_Select.h @@ -106,7 +106,7 @@ #define PIN_MOSI 8 // SD1 #define PIN_MISO 7 // SD0 #define PIN_SCLK 6 // CLK -#define PIN_HWCS 0 // CMD +#define PIN_HWCS 0 // D3 #define PIN_D11 9 // SD2 #define PIN_D12 10 // SD4 diff --git a/examples/Test and diagnostics/Read_User_Setup/Read_User_Setup.ino b/examples/Test and diagnostics/Read_User_Setup/Read_User_Setup.ino new file mode 100644 index 0000000..f326cad --- /dev/null +++ b/examples/Test and diagnostics/Read_User_Setup/Read_User_Setup.ino @@ -0,0 +1,151 @@ +/* + This sketch reads the user setup information from the processor via the Serial Port + + It is a support and diagnostic sketch for the TFT_eSPI library: + https://github.com/Bodmer/TFT_eSPI + + The output is essentially a copy of the User_Setep configuration so can be used to + verify the correct settings are being picked up by the compiler. + + If support is needed the output can be cut and pasted into an Arduino Forum post and + already inlcudes the formatting [code]...[/code] markups. + + Written by Bodmer 9/4/18 +*/ + +#include +#include // Graphics library + +TFT_eSPI tft = TFT_eSPI(); // Invoke library + +#ifdef ESP8266 + ADC_MODE(ADC_VCC); // Read the supply voltage +#endif + +setup_t user; // The library defines the type "setup_t" as a struct + // Calling tft.getSetup(user) populates it with the settings +//------------------------------------------------------------------------------------------ + +void setup() { + // Use serial port + Serial.begin(115200); + + // Initialise the TFT screen + tft.init(); +} + +//------------------------------------------------------------------------------------------ + +void loop(void) { + +tft.getSetup(user); // + +Serial.printf("\n[code]\n"); +Serial.printf("Processor = ESP%i\n", user.esp, HEX); +Serial.printf("Frequency = %i MHz\n", ESP.getCpuFreqMHz()); +#ifdef ESP8266 +Serial.printf("Voltage = %2.2f V\n", ESP.getVcc() / 918.0); // 918 empirically determined +#endif +Serial.printf("Transactions = %s \n", (user.trans == 1) ? "Yes" : "No"); +Serial.printf("Interface = %s \n", (user.serial == 1) ? "SPI" : "Parallel"); +#ifdef ESP8266 +if (user.serial == 1) +Serial.printf("SPI overlap = %s \n\n", (user.overlap == 1) ? "Yes" : "No"); +#endif +if (user.tft_driver != 0xE9D) // For ePaper displays the size is defined in the sketch +{ + Serial.printf("Display driver = "); Serial.println(user.tft_driver, HEX); // Hexadecimal code + Serial.printf("Display width = %i \n", user.tft_width); // Rotation 0 width and height + Serial.printf("Display height = %i \n\n", user.tft_height); +} +else if (user.tft_driver == 0xE9D) Serial.printf("Display driver = ePaper\n\n"); + +if (user.r0_x_offset != 0) Serial.printf("R0 x offset = %i \n", user.r0_x_offset); // Offsets, not all used yet +if (user.r0_y_offset != 0) Serial.printf("R0 y offset = %i \n", user.r0_y_offset); +if (user.r1_x_offset != 0) Serial.printf("R1 x offset = %i \n", user.r1_x_offset); +if (user.r1_y_offset != 0) Serial.printf("R1 y offset = %i \n", user.r1_y_offset); +if (user.r2_x_offset != 0) Serial.printf("R2 x offset = %i \n", user.r2_x_offset); +if (user.r2_y_offset != 0) Serial.printf("R2 y offset = %i \n", user.r2_y_offset); +if (user.r3_x_offset != 0) Serial.printf("R3 x offset = %i \n", user.r3_x_offset); +if (user.r3_y_offset != 0) Serial.printf("R3 y offset = %i \n\n", user.r3_y_offset); + +if (user.pin_tft_mosi != -1) Serial.printf("MOSI = D%i (GPIO %i)\n", getPinName(user.pin_tft_mosi), user.pin_tft_mosi); +if (user.pin_tft_miso != -1) Serial.printf("MISO = D%i (GPIO %i)\n", getPinName(user.pin_tft_miso), user.pin_tft_miso); +if (user.pin_tft_clk != -1) Serial.printf("SCK = D%i (GPIO %i)\n", getPinName(user.pin_tft_clk), user.pin_tft_clk); + +#ifdef ESP8266 +if (user.overlap == true) +{ + Serial.printf("Overlap selected, following pins MUST be used:\n"); + + Serial.printf("MOSI = SD1 (GPIO 8)\n"); + Serial.printf("MISO = SD0 (GPIO 7)\n"); + Serial.printf("SCK = CLK (GPIO 6)\n"); + Serial.printf("TFT_CS = D3 (GPIO 0)\n\n"); + + Serial.printf("TFT_DC and TFT_RST pins can be user defined\n"); +} +#endif +if (user.pin_tft_cs != -1) Serial.printf("TFT_CS = D%i (GPIO %i)\n", getPinName(user.pin_tft_cs), user.pin_tft_cs); +if (user.pin_tft_dc != -1) Serial.printf("TFT_DC = D%i (GPIO %i)\n", getPinName(user.pin_tft_dc), user.pin_tft_dc); +if (user.pin_tft_rst != -1) Serial.printf("TFT_RST = D%i (GPIO %i)\n\n", getPinName(user.pin_tft_rst), user.pin_tft_rst); + +if (user.pin_tch_cs != -1) Serial.printf("TOUCH_CS = D%i (GPIO %i)\n\n", getPinName(user.pin_tch_cs), user.pin_tch_cs); + +if (user.pin_tft_wr != -1) Serial.printf("TFT_WR = D%i (GPIO %i)\n", getPinName(user.pin_tft_wr), user.pin_tft_wr); +if (user.pin_tft_rd != -1) Serial.printf("TFT_RD = D%i (GPIO %i)\n\n", getPinName(user.pin_tft_rd), user.pin_tft_rd); + +if (user.pin_tft_d0 != -1) Serial.printf("TFT_D0 = D%i (GPIO %i)\n", getPinName(user.pin_tft_d0), user.pin_tft_d0); +if (user.pin_tft_d1 != -1) Serial.printf("TFT_D1 = D%i (GPIO %i)\n", getPinName(user.pin_tft_d1), user.pin_tft_d1); +if (user.pin_tft_d2 != -1) Serial.printf("TFT_D2 = D%i (GPIO %i)\n", getPinName(user.pin_tft_d2), user.pin_tft_d2); +if (user.pin_tft_d3 != -1) Serial.printf("TFT_D3 = D%i (GPIO %i)\n", getPinName(user.pin_tft_d3), user.pin_tft_d3); +if (user.pin_tft_d4 != -1) Serial.printf("TFT_D4 = D%i (GPIO %i)\n", getPinName(user.pin_tft_d4), user.pin_tft_d4); +if (user.pin_tft_d5 != -1) Serial.printf("TFT_D5 = D%i (GPIO %i)\n", getPinName(user.pin_tft_d5), user.pin_tft_d5); +if (user.pin_tft_d6 != -1) Serial.printf("TFT_D6 = D%i (GPIO %i)\n", getPinName(user.pin_tft_d6), user.pin_tft_d6); +if (user.pin_tft_d7 != -1) Serial.printf("TFT_D7 = D%i (GPIO %i)\n\n", getPinName(user.pin_tft_d7), user.pin_tft_d7); + +uint16_t fonts = tft.fontsLoaded(); +if (fonts & (1 << 1)) Serial.printf("Font GLCD loaded\n"); +if (fonts & (1 << 2)) Serial.printf("Font 2 loaded\n"); +if (fonts & (1 << 4)) Serial.printf("Font 4 loaded\n"); +if (fonts & (1 << 6)) Serial.printf("Font 6 loaded\n"); +if (fonts & (1 << 7)) Serial.printf("Font 7 loaded\n"); +if (fonts & (1 << 9)) Serial.printf("Font 8N loaded\n"); +else +if (fonts & (1 << 8)) Serial.printf("Font 8 loaded\n"); +if (fonts & (1 << 15)) Serial.printf("Smooth font enabled\n"); +Serial.printf("\n"); + +if (user.serial==1) Serial.printf("Display SPI frequency = %2.1f MHz \n", user.tft_spi_freq/10.0); +if (user.pin_tch_cs != -1) Serial.printf("Touch SPI frequency = %2.1f MHz \n", user.tch_spi_freq/10.0); + +Serial.printf("[/code]\n"); + +while(1) yield(); + +} + +// Get pin name for ESP8266 +int8_t getPinName(int8_t pin) +{ + // For ESP32 pin labels on boards use the GPIO number + if (user.esp == 32) return pin; + + // For ESP8266 the pin labels are not the same as the GPIO number + // These are for the NodeMCU pin definitions: + // GPIO Dxx + if (pin == 16) return 0; + if (pin == 5) return 1; + if (pin == 4) return 2; + if (pin == 0) return 3; + if (pin == 2) return 4; + if (pin == 14) return 5; + if (pin == 12) return 6; + if (pin == 13) return 7; + if (pin == 15) return 8; + if (pin == 3) return 9; + if (pin == 1) return 10; + if (pin == 9) return 11; + if (pin == 10) return 12; +} + diff --git a/examples/Test and diagnostics/Test_Touch_Controller/Test_Touch_Controller.ino b/examples/Test and diagnostics/Test_Touch_Controller/Test_Touch_Controller.ino new file mode 100644 index 0000000..6c1e887 --- /dev/null +++ b/examples/Test and diagnostics/Test_Touch_Controller/Test_Touch_Controller.ino @@ -0,0 +1,49 @@ +// This sketch is to test the touch controller, nothing is displayed +// on the TFT. The TFT_eSPI library must be configured to suit your +// pins used. Make sure both the touch chip select and the TFT chip +// select are correctly defined to avoid SPI bus contention. + +// Make sure you have defined a pin for the touch controller chip +// select line in the user setup file or you will see "no member" +// compile errors for the touch functions! + +// It is a support and diagnostic sketch for the TFT_eSPI library: +// https://github.com/Bodmer/TFT_eSPI + +// The "raw" (unprocessed) touch sensor outputs are sent to the +// serial port. Touching the screen should show changes to the x, y +// and z values. x and y are raw ADC readings, not pixel coordinates. + +#include +#include +TFT_eSPI tft = TFT_eSPI(); + +//==================================================================== + +void setup(void) { + Serial.begin(115200); + Serial.println("\n\nStarting..."); + + tft.init(); +} + +//==================================================================== + +void loop() { + + uint16_t x, y; + + tft.getTouchRaw(&x, &y); + + Serial.printf("x: %i ", x); + + Serial.printf("y: %i ", y); + + Serial.printf("z: %i \n", tft.getTouchRawZ()); + + delay(250); + +} + +//==================================================================== + From 1163817887888f855398f1b6c8debc81b02f56cc Mon Sep 17 00:00:00 2001 From: Bodmer Date: Wed, 11 Apr 2018 01:04:19 +0100 Subject: [PATCH 116/150] Update version --- library.json | 2 +- library.properties | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/library.json b/library.json index e61cde7..673a526 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "TFT_eSPI", - "version": "0.20.11", + "version": "0.20.12", "keywords": "tft, ePaper, display, ESP8266, NodeMCU, ESP32, M5Stack, ILI9341, ST7735, ILI9163, S6D02A1, ILI9486", "description": "A TFT and ePaper SPI graphics library for ESP8266 and ESP32", "repository": diff --git a/library.properties b/library.properties index 59d35e0..49ddbcc 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=TFT_eSPI -version=0.20.11 +version=0.20.12 author=Bodmer maintainer=Bodmer sentence=A fast TFT library for ESP8266 processors and the Arduino IDE From 82b749a93a6ace8856d3e16436470a9cc0f5eb07 Mon Sep 17 00:00:00 2001 From: rdts <28197082+rdts@users.noreply.github.com> Date: Wed, 11 Apr 2018 19:12:33 +0200 Subject: [PATCH 117/150] Update Sprite.cpp Hi, I found a bug. _width and _height need to be initialized properly. That is because they are used in drawString(). Lines 4118 and 4120 in TFT_eSPI.cpp. --- Extensions/Sprite.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Extensions/Sprite.cpp b/Extensions/Sprite.cpp index 0ebca60..276fa32 100644 --- a/Extensions/Sprite.cpp +++ b/Extensions/Sprite.cpp @@ -49,8 +49,8 @@ void* TFT_eSprite::createSprite(int16_t w, int16_t h, uint8_t frames) if ( w < 1 || h < 1 ) return NULL; - _iwidth = _dwidth = w; - _iheight = _dheight = h; + _width = _iwidth = _dwidth = w; + _height = _iheight = _dheight = h; this->cursor_x = 0; this->cursor_y = 0; From bdc9821b7d23c561ea9dc51f6fa8b731fbd72fc2 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Thu, 12 Apr 2018 01:55:38 +0100 Subject: [PATCH 118/150] Add this-> --- Extensions/Sprite.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Extensions/Sprite.cpp b/Extensions/Sprite.cpp index 276fa32..290a0d8 100644 --- a/Extensions/Sprite.cpp +++ b/Extensions/Sprite.cpp @@ -49,8 +49,8 @@ void* TFT_eSprite::createSprite(int16_t w, int16_t h, uint8_t frames) if ( w < 1 || h < 1 ) return NULL; - _width = _iwidth = _dwidth = w; - _height = _iheight = _dheight = h; + this->_width = _iwidth = _dwidth = w; + this->_height = _iheight = _dheight = h; this->cursor_x = 0; this->cursor_y = 0; From 71c06273c09413822c57980ab38c91f21073c97f Mon Sep 17 00:00:00 2001 From: Bodmer Date: Thu, 12 Apr 2018 02:27:22 +0100 Subject: [PATCH 119/150] Revert "Update Sprite.cpp" --- Extensions/Sprite.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Extensions/Sprite.cpp b/Extensions/Sprite.cpp index 290a0d8..0ebca60 100644 --- a/Extensions/Sprite.cpp +++ b/Extensions/Sprite.cpp @@ -49,8 +49,8 @@ void* TFT_eSprite::createSprite(int16_t w, int16_t h, uint8_t frames) if ( w < 1 || h < 1 ) return NULL; - this->_width = _iwidth = _dwidth = w; - this->_height = _iheight = _dheight = h; + _iwidth = _dwidth = w; + _iheight = _dheight = h; this->cursor_x = 0; this->cursor_y = 0; From 4d181d7a194e07d81cc6b8b95dea6ee57d019892 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Thu, 12 Apr 2018 18:48:01 +0100 Subject: [PATCH 120/150] Fix bug #122 by different approach --- TFT_eSPI.cpp | 4 ++-- TFT_eSPI.h | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/TFT_eSPI.cpp b/TFT_eSPI.cpp index bf415e4..b1a5a6b 100644 --- a/TFT_eSPI.cpp +++ b/TFT_eSPI.cpp @@ -4115,9 +4115,9 @@ int16_t TFT_eSPI::drawString(const char *string, int poX, int poY, int font) } // Check coordinates are OK, adjust if not if (poX < 0) poX = 0; - if (poX+cwidth>_width) poX = _width - cwidth; + if (poX+cwidth > width()) poX = width() - cwidth; if (poY < 0) poY = 0; - if (poY+cheight-baseline>_height) poY = _height - cheight; + if (poY+cheight-baseline> height()) poY = height() - cheight; } diff --git a/TFT_eSPI.h b/TFT_eSPI.h index 7fc242a..e4d98e5 100644 --- a/TFT_eSPI.h +++ b/TFT_eSPI.h @@ -471,7 +471,9 @@ class TFT_eSPI : public Print { fillRect(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color); virtual int16_t drawChar(unsigned int uniCode, int x, int y, int font), - drawChar(unsigned int uniCode, int x, int y); + drawChar(unsigned int uniCode, int x, int y), + height(void), + width(void); // The TFT_eSprite class inherits the following functions void setWindow(int16_t x0, int16_t y0, int16_t x1, int16_t y1), @@ -588,9 +590,7 @@ class TFT_eSPI : public Print { drawCentreString(const String& string, int dX, int poY, int font), // Deprecated, use setTextDatum() and drawString() drawRightString(const String& string, int dX, int poY, int font); // Deprecated, use setTextDatum() and drawString() - int16_t height(void), - width(void), - textWidth(const char *string, int font), + int16_t textWidth(const char *string, int font), textWidth(const char *string), textWidth(const String& string, int font), textWidth(const String& string), From 7cbf6fe0481f1dba3417cc58f17e5611f557036a Mon Sep 17 00:00:00 2001 From: Bodmer Date: Sat, 21 Apr 2018 09:45:52 +0100 Subject: [PATCH 121/150] Make some variables public To allow access to variables that can be set via functions anyway. --- TFT_eSPI.h | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/TFT_eSPI.h b/TFT_eSPI.h index e4d98e5..e29655e 100644 --- a/TFT_eSPI.h +++ b/TFT_eSPI.h @@ -603,10 +603,15 @@ class TFT_eSPI : public Print { void getSetup(setup_t& tft_settings); // Sketch provides the instance to populate - int32_t cursor_x, cursor_y; + int32_t cursor_x, cursor_y, padX; uint32_t textcolor, textbgcolor; + uint32_t bitmap_fg, bitmap_bg; + uint8_t textfont, // Current selected font + textsize, // Current font size multiplier + textdatum, // Text reference datum + rotation; // Display rotation (0-3) private: @@ -631,20 +636,16 @@ class TFT_eSPI : public Print { protected: - int32_t win_xe, win_ye, padX; + int32_t win_xe, win_ye; uint32_t _init_width, _init_height; // Display w/h as input, used by setRotation() - uint32_t _width, _height; // Display w/h as modified by current rotation + uint32_t _width, _height; // Display w/h as modified by current rotation uint32_t addr_row, addr_col; uint32_t fontsloaded; uint8_t glyph_ab, // glyph height above baseline - glyph_bb, // glyph height below baseline - textfont, // Current selected font - textsize, // Current font size multiplier - textdatum, // Text reference datum - rotation; // Display rotation (0-3) + glyph_bb; // glyph height below baseline bool textwrapX, textwrapY; // If set, 'wrap' text at right and optionally bottom edge of display bool _swapBytes; // Swap the byte order for TFT pushImage() From eded971f9f795922b42994bb0ef2f7843db3e82c Mon Sep 17 00:00:00 2001 From: Bodmer Date: Sat, 21 Apr 2018 09:50:34 +0100 Subject: [PATCH 122/150] Raise minor issue --- library.json | 2 +- library.properties | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/library.json b/library.json index 673a526..0476d29 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "TFT_eSPI", - "version": "0.20.12", + "version": "0.20.13", "keywords": "tft, ePaper, display, ESP8266, NodeMCU, ESP32, M5Stack, ILI9341, ST7735, ILI9163, S6D02A1, ILI9486", "description": "A TFT and ePaper SPI graphics library for ESP8266 and ESP32", "repository": diff --git a/library.properties b/library.properties index 49ddbcc..1eb43e2 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=TFT_eSPI -version=0.20.12 +version=0.20.13 author=Bodmer maintainer=Bodmer sentence=A fast TFT library for ESP8266 processors and the Arduino IDE From 235f89e75568dcf2253eba9bbc451394c6883bf6 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Sat, 21 Apr 2018 22:49:17 +0100 Subject: [PATCH 123/150] Allow ESP32 pin numbers greater than 31 for CS and DC --- TFT_eSPI.h | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/TFT_eSPI.h b/TFT_eSPI.h index e29655e..f66e810 100644 --- a/TFT_eSPI.h +++ b/TFT_eSPI.h @@ -99,13 +99,18 @@ #define DC_D digitalWrite(TFT_DC, HIGH) #elif defined (ESP32) #if defined (ESP32_PARALLEL) - #define DC_C GPIO.out_w1tc = (1 << TFT_DC) // Too fast for ST7735 + #define DC_C GPIO.out_w1tc = (1 << TFT_DC) #define DC_D GPIO.out_w1ts = (1 << TFT_DC) //#define DC_C digitalWrite(TFT_DC, LOW) //#define DC_D digitalWrite(TFT_DC, HIGH) #else - #define DC_C GPIO.out_w1ts = (1 << TFT_DC); GPIO.out_w1tc = (1 << TFT_DC) - #define DC_D GPIO.out_w1tc = (1 << TFT_DC); GPIO.out_w1ts = (1 << TFT_DC) + #if TFT_DC >= 32 || TFT_DC <= -1 + #define DC_C digitalWrite(TFT_DC, LOW) + #define DC_D digitalWrite(TFT_DC, HIGH) + #else + #define DC_C GPIO.out_w1ts = (1 << TFT_DC); GPIO.out_w1tc = (1 << TFT_DC) + #define DC_D GPIO.out_w1tc = (1 << TFT_DC); GPIO.out_w1ts = (1 << TFT_DC) + #endif #endif #else #define DC_C GPOC=dcpinmask @@ -129,8 +134,13 @@ #define CS_L // The TFT CS is set permanently low during init() #define CS_H #else - #define CS_L GPIO.out_w1ts = (1 << TFT_CS);GPIO.out_w1tc = (1 << TFT_CS) - #define CS_H GPIO.out_w1tc = (1 << TFT_CS);GPIO.out_w1ts = (1 << TFT_CS) + #if TFT_CS >= 32 || TFT_CS <= -1 + #define CS_L digitalWrite(TFT_DC, LOW) + #define CS_H digitalWrite(TFT_DC, HIGH) + #else + #define CS_L GPIO.out_w1ts = (1 << TFT_CS);GPIO.out_w1tc = (1 << TFT_CS) + #define CS_H GPIO.out_w1tc = (1 << TFT_CS);GPIO.out_w1ts = (1 << TFT_CS) + #endif #endif #else #define CS_L GPOC=cspinmask From 6213de3ab9e731e0d7a9e2c789f936e3412b5604 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Sun, 22 Apr 2018 20:38:55 +0100 Subject: [PATCH 124/150] Use direct write, fix bug for TFT_CS pin >31 --- TFT_eSPI.h | 41 +++++++++++++++++++++++++++-------------- 1 file changed, 27 insertions(+), 14 deletions(-) diff --git a/TFT_eSPI.h b/TFT_eSPI.h index f66e810..3c4d57c 100644 --- a/TFT_eSPI.h +++ b/TFT_eSPI.h @@ -101,15 +101,23 @@ #if defined (ESP32_PARALLEL) #define DC_C GPIO.out_w1tc = (1 << TFT_DC) #define DC_D GPIO.out_w1ts = (1 << TFT_DC) - //#define DC_C digitalWrite(TFT_DC, LOW) - //#define DC_D digitalWrite(TFT_DC, HIGH) + #else - #if TFT_DC >= 32 || TFT_DC <= -1 - #define DC_C digitalWrite(TFT_DC, LOW) - #define DC_D digitalWrite(TFT_DC, HIGH) + #if TFT_DC >= 32 + #define DC_C GPIO.out1_w1ts.val = (1 << (TFT_DC - 32)); \ + GPIO.out1_w1tc.val = (1 << (TFT_DC - 32)) + #define DC_D GPIO.out1_w1tc.val = (1 << (TFT_DC - 32)); \ + GPIO.out1_w1ts.val = (1 << (TFT_DC - 32)) #else - #define DC_C GPIO.out_w1ts = (1 << TFT_DC); GPIO.out_w1tc = (1 << TFT_DC) - #define DC_D GPIO.out_w1tc = (1 << TFT_DC); GPIO.out_w1ts = (1 << TFT_DC) + #if TFT_DC >= 0 + #define DC_C GPIO.out_w1ts = (1 << TFT_DC); \ + GPIO.out_w1tc = (1 << TFT_DC) + #define DC_D GPIO.out_w1tc = (1 << TFT_DC); \ + GPIO.out_w1ts = (1 << TFT_DC) + #else + #define DC_C + #define DC_D + #endif #endif #endif #else @@ -134,12 +142,19 @@ #define CS_L // The TFT CS is set permanently low during init() #define CS_H #else - #if TFT_CS >= 32 || TFT_CS <= -1 - #define CS_L digitalWrite(TFT_DC, LOW) - #define CS_H digitalWrite(TFT_DC, HIGH) + #if TFT_CS >= 32 + #define CS_L GPIO.out1_w1ts.val = (1 << (TFT_CS - 32)); \ + GPIO.out1_w1tc.val = (1 << (TFT_CS - 32)) + #define CS_H GPIO.out1_w1tc.val = (1 << (TFT_CS - 32)); \ + GPIO.out1_w1ts.val = (1 << (TFT_CS - 32)) #else - #define CS_L GPIO.out_w1ts = (1 << TFT_CS);GPIO.out_w1tc = (1 << TFT_CS) - #define CS_H GPIO.out_w1tc = (1 << TFT_CS);GPIO.out_w1ts = (1 << TFT_CS) + #if TFT_CS >= 0 + #define CS_L GPIO.out_w1ts = (1 << TFT_CS);GPIO.out_w1tc = (1 << TFT_CS) + #define CS_H GPIO.out_w1tc = (1 << TFT_CS);GPIO.out_w1ts = (1 << TFT_CS) + #else + #define CS_L + #define CS_H + #endif #endif #endif #else @@ -161,9 +176,7 @@ #ifdef TFT_WR #if defined (ESP32) #define WR_L GPIO.out_w1tc = (1 << TFT_WR) - //#define WR_L digitalWrite(TFT_WR, LOW) #define WR_H GPIO.out_w1ts = (1 << TFT_WR) - //#define WR_H digitalWrite(TFT_WR, HIGH) #else #define WR_L GPOC=wrpinmask #define WR_H GPOS=wrpinmask From 54b3f0f63d524df6becbe91e4ac45943f219eb8a Mon Sep 17 00:00:00 2001 From: Bodmer Date: Mon, 23 Apr 2018 17:16:18 +0100 Subject: [PATCH 125/150] Add drawXBitmap() function and example Example is in Generic folder, draw the Espressif logo on the screen. --- TFT_eSPI.cpp | 52 ++++++++++++++++- TFT_eSPI.h | 2 + examples/Generic/drawXBitmap/drawXBitmap.ino | 61 ++++++++++++++++++++ examples/Generic/drawXBitmap/xbm.h | 52 +++++++++++++++++ keywords.txt | 1 + library.json | 2 +- library.properties | 2 +- 7 files changed, 168 insertions(+), 4 deletions(-) create mode 100644 examples/Generic/drawXBitmap/drawXBitmap.ino create mode 100644 examples/Generic/drawXBitmap/xbm.h diff --git a/TFT_eSPI.cpp b/TFT_eSPI.cpp index b1a5a6b..b5e2164 100644 --- a/TFT_eSPI.cpp +++ b/TFT_eSPI.cpp @@ -1764,8 +1764,8 @@ void TFT_eSPI::fillTriangle ( int32_t x0, int32_t y0, int32_t x1, int32_t y1, in ** Function name: drawBitmap ** Description: Draw an image stored in an array on the TFT ***************************************************************************************/ -void TFT_eSPI::drawBitmap(int16_t x, int16_t y, const uint8_t *bitmap, int16_t w, int16_t h, uint16_t color) { - +void TFT_eSPI::drawBitmap(int16_t x, int16_t y, const uint8_t *bitmap, int16_t w, int16_t h, uint16_t color) +{ spi_begin(); inTransaction = true; @@ -1784,6 +1784,54 @@ void TFT_eSPI::drawBitmap(int16_t x, int16_t y, const uint8_t *bitmap, int16_t w } +/*************************************************************************************** +** Function name: drawXBitmap +** Description: Draw an image stored in an XBM array onto the TFT +***************************************************************************************/ +void TFT_eSPI::drawXBitmap(int16_t x, int16_t y, const uint8_t *bitmap, int16_t w, int16_t h, uint16_t color) +{ + spi_begin(); + inTransaction = true; + + int32_t i, j, byteWidth = (w + 7) / 8; + + for (j = 0; j < h; j++) { + for (i = 0; i < w; i++ ) { + if (pgm_read_byte(bitmap + j * byteWidth + i / 8) & (1 << (i & 7))) { + drawPixel(x + i, y + j, color); + } + } + } + + inTransaction = false; + spi_end(); +} + + +/*************************************************************************************** +** Function name: drawXBitmap +** Description: Draw an XBM image with foreground and background colors +***************************************************************************************/ +void TFT_eSPI::drawXBitmap(int16_t x, int16_t y, const uint8_t *bitmap, int16_t w, int16_t h, uint16_t color, uint16_t bgcolor) +{ + spi_begin(); + inTransaction = true; + + int32_t i, j, byteWidth = (w + 7) / 8; + + for (j = 0; j < h; j++) { + for (i = 0; i < w; i++ ) { + if (pgm_read_byte(bitmap + j * byteWidth + i / 8) & (1 << (i & 7))) + drawPixel(x + i, y + j, color); + else drawPixel(x + i, y + j, bgcolor); + } + } + + inTransaction = false; + spi_end(); +} + + /*************************************************************************************** ** Function name: setCursor ** Description: Set the text cursor x,y position diff --git a/TFT_eSPI.h b/TFT_eSPI.h index 3c4d57c..44e4ea8 100644 --- a/TFT_eSPI.h +++ b/TFT_eSPI.h @@ -526,6 +526,8 @@ class TFT_eSPI : public Print { fillTriangle(int32_t x0, int32_t y0, int32_t x1, int32_t y1, int32_t x2, int32_t y2, uint32_t color), drawBitmap(int16_t x, int16_t y, const uint8_t *bitmap, int16_t w, int16_t h, uint16_t color), + drawXBitmap(int16_t x, int16_t y, const uint8_t *bitmap, int16_t w, int16_t h, uint16_t color), + drawXBitmap(int16_t x, int16_t y, const uint8_t *bitmap, int16_t w, int16_t h, uint16_t fgcolor, uint16_t bgcolor), setBitmapColor(uint16_t c, uint16_t b), // For 1bpp sprites setCursor(int16_t x, int16_t y), diff --git a/examples/Generic/drawXBitmap/drawXBitmap.ino b/examples/Generic/drawXBitmap/drawXBitmap.ino new file mode 100644 index 0000000..8678c31 --- /dev/null +++ b/examples/Generic/drawXBitmap/drawXBitmap.ino @@ -0,0 +1,61 @@ +// Example sketch to demonstrate the drawing of X BitMap (XBM) +// format image onto the display. + +// Information on the X BitMap (XBM) format can be found here: +// https://en.wikipedia.org/wiki/X_BitMap + +// This example is part of the TFT_eSPI library: +// https://github.com/Bodmer/TFT_eSPI + +// Created by Bodmer 23/14/18 + +#include "xbm.h" // Sketch tab header for xbm images + +#include // Hardware-specific library + +TFT_eSPI tft = TFT_eSPI(); // Invoke library + + +void setup() +{ + tft.begin(); // Initialise the display + tft.fillScreen(TFT_BLACK); // Black screen fill +} + +void loop() +{ + + // Example 1 + // ========= + // Random x and y coordinates + int x = random(tft.width() - logoWidth); + int y = random(tft.height() - logoHeight); + + // Draw bitmap with top left corner at x,y with foreground only color + // Bits set to 1 plot as the defined color, bits set to 0 are not plotted + // x y xbm xbm width xbm height color + tft.drawXBitmap(x, y, logo, logoWidth, logoHeight, TFT_WHITE); + + delay(500); + + // Erase old one by drawing over with background colour + tft.drawXBitmap(x, y, logo, logoWidth, logoHeight, TFT_BLACK); + + + // Example 2 + // ========= + // New random x and y coordinates + x = random(tft.width() - logoWidth); + y = random(tft.height() - logoHeight); + + // Draw bitmap with top left corner at x,y with foreground and background colors + // Bits set to 1 plot as the defined fg color, bits set to 0 are plotted as bg color + // x y xbm xbm width xbm height fg color bg color + tft.drawXBitmap(x, y, logo, logoWidth, logoHeight, TFT_WHITE, TFT_RED); + + delay(500); + + // Erase old one by drawing over with background colour + tft.drawXBitmap(x, y, logo, logoWidth, logoHeight, TFT_BLACK, TFT_BLACK); + +} diff --git a/examples/Generic/drawXBitmap/xbm.h b/examples/Generic/drawXBitmap/xbm.h new file mode 100644 index 0000000..675dc1f --- /dev/null +++ b/examples/Generic/drawXBitmap/xbm.h @@ -0,0 +1,52 @@ +// Images can be converted to XBM format by using the online converter here: +// https://www.online-utility.org/image/convert/to/XBM + +// The output must be pasted in a header file, renamed and adjusted to appear +// as as a const unsigned char array in PROGMEM (FLASH program memory). + +// The xbm format adds padding to pixel rows so they are a whole number of bytes +// In this example 50 pixel width means 56 bits = 7 bytes +// the 50 height then means array uses 50 x 7 = 350 bytes of FLASH +// The library ignores the padding bits when drawing the image on the display. + +// Example of the correct format is shown below + +#include // PROGMEM support header + +// Espressif logo 50 x 50 pixel array in XBM format +#define logoWidth 50 // logo width +#define logoHeight 50 // logo height + +// Image is stored in this array +PROGMEM const unsigned char logo[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x07, 0xFC, 0x07, 0x00, 0x00, 0x00, 0x82, 0x7F, 0xF0, + 0x1F, 0x00, 0x00, 0x00, 0xC6, 0xFF, 0xC3, 0x3F, 0x00, 0x00, 0x00, 0xE7, + 0xFF, 0x8F, 0x7F, 0x00, 0x00, 0x80, 0xE3, 0xFF, 0x1F, 0xFE, 0x00, 0x00, + 0x80, 0xE1, 0xFF, 0x7F, 0xFC, 0x01, 0x00, 0xC0, 0x00, 0xFF, 0xFF, 0xF8, + 0x03, 0x00, 0xE0, 0x00, 0xE0, 0xFF, 0xF1, 0x03, 0x00, 0x60, 0xF0, 0x81, + 0xFF, 0xE3, 0x07, 0x00, 0x60, 0xFC, 0x1F, 0xFE, 0xC7, 0x07, 0x00, 0x30, + 0xFE, 0x7F, 0xF8, 0x8F, 0x0F, 0x00, 0x30, 0xFF, 0xFF, 0xF1, 0x9F, 0x0F, + 0x00, 0xB0, 0xFF, 0xFF, 0xE3, 0x3F, 0x0F, 0x00, 0xB0, 0xFF, 0xFF, 0xC7, + 0x3F, 0x1E, 0x00, 0xB8, 0xFF, 0xFF, 0x8F, 0x7F, 0x1E, 0x00, 0x98, 0x1F, + 0xFC, 0x3F, 0xFF, 0x1C, 0x00, 0xB8, 0x3F, 0xE0, 0x3F, 0xFE, 0x1C, 0x00, + 0x98, 0xFF, 0xC3, 0x7F, 0xFE, 0x19, 0x00, 0x98, 0xFF, 0x0F, 0xFF, 0xFC, + 0x19, 0x00, 0x38, 0xFF, 0x3F, 0xFF, 0xFC, 0x01, 0x00, 0x30, 0xFE, 0x7F, + 0xFE, 0xF9, 0x03, 0x00, 0x30, 0xFC, 0xFF, 0xFC, 0xF9, 0x03, 0x00, 0x30, + 0xF8, 0xFF, 0xF8, 0xF3, 0x03, 0x00, 0x30, 0x00, 0xFF, 0xF9, 0xF3, 0x03, + 0x00, 0x70, 0x00, 0xFC, 0xF9, 0xF3, 0x07, 0x00, 0x60, 0x00, 0xF8, 0xF3, + 0xF3, 0x07, 0x00, 0xE0, 0xF8, 0xF8, 0xF3, 0xF7, 0x03, 0x00, 0xC0, 0xF8, + 0xF1, 0xF3, 0xE3, 0x03, 0x00, 0xC0, 0xFD, 0xF1, 0xF3, 0xF7, 0x01, 0x00, + 0x80, 0xFD, 0xF1, 0xF3, 0xE7, 0x00, 0x00, 0x00, 0xFF, 0xF1, 0xF3, 0x07, + 0x00, 0x00, 0x00, 0xFF, 0xF8, 0xF3, 0x07, 0x00, 0x00, 0x00, 0x7E, 0xF8, + 0xF3, 0x83, 0x03, 0x00, 0x00, 0x3C, 0xF8, 0xF3, 0xC3, 0x01, 0x00, 0x00, + 0x70, 0xF8, 0xF9, 0xE3, 0x00, 0x00, 0x00, 0xE0, 0xE1, 0x41, 0x78, 0x00, + 0x00, 0x00, 0xC0, 0x0F, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFD, + 0x07, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, }; + diff --git a/keywords.txt b/keywords.txt index 33da608..53b0825 100644 --- a/keywords.txt +++ b/keywords.txt @@ -29,6 +29,7 @@ fillEllipse KEYWORD2 drawTriangle KEYWORD2 fillTriangle KEYWORD2 drawBitmap KEYWORD2 +drawXBitmap KEYWORD2 setCursor KEYWORD2 getCursorX KEYWORD2 getCursorY KEYWORD2 diff --git a/library.json b/library.json index 0476d29..4c6257c 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "TFT_eSPI", - "version": "0.20.13", + "version": "0.20.14", "keywords": "tft, ePaper, display, ESP8266, NodeMCU, ESP32, M5Stack, ILI9341, ST7735, ILI9163, S6D02A1, ILI9486", "description": "A TFT and ePaper SPI graphics library for ESP8266 and ESP32", "repository": diff --git a/library.properties b/library.properties index 1eb43e2..b3049e5 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=TFT_eSPI -version=0.20.13 +version=0.20.14 author=Bodmer maintainer=Bodmer sentence=A fast TFT library for ESP8266 processors and the Arduino IDE From 235887d86055c1a48e682d2a5dc745898257e6f9 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Mon, 23 Apr 2018 20:05:50 +0100 Subject: [PATCH 126/150] Update weather station example Corrected jpeg render fn to handle any width image Minor tweaks Remove version number from sketch name, put in settings.h --- .../ArialRoundedMTBold_36.h | 2 +- .../ArialRoundedMtBold_14.h | 0 .../GfxUi.cpp | 37 +++++++++++-------- .../GfxUi.h | 0 .../SPIFFS_Support.ino | 0 .../WebResource.cpp | 0 .../WebResource.h | 0 .../settings.h | 22 +++++++---- .../weather-station.ino} | 1 + 9 files changed, 38 insertions(+), 24 deletions(-) rename examples/320 x 240/{weather-station-v8 => weather-station}/ArialRoundedMTBold_36.h (99%) rename examples/320 x 240/{weather-station-v8 => weather-station}/ArialRoundedMtBold_14.h (100%) rename examples/320 x 240/{weather-station-v8 => weather-station}/GfxUi.cpp (94%) rename examples/320 x 240/{weather-station-v8 => weather-station}/GfxUi.h (100%) rename examples/320 x 240/{weather-station-v8 => weather-station}/SPIFFS_Support.ino (100%) rename examples/320 x 240/{weather-station-v8 => weather-station}/WebResource.cpp (100%) rename examples/320 x 240/{weather-station-v8 => weather-station}/WebResource.h (100%) rename examples/320 x 240/{weather-station-v8 => weather-station}/settings.h (78%) rename examples/320 x 240/{weather-station-v8/weather-station-v8.ino => weather-station/weather-station.ino} (99%) diff --git a/examples/320 x 240/weather-station-v8/ArialRoundedMTBold_36.h b/examples/320 x 240/weather-station/ArialRoundedMTBold_36.h similarity index 99% rename from examples/320 x 240/weather-station-v8/ArialRoundedMTBold_36.h rename to examples/320 x 240/weather-station/ArialRoundedMTBold_36.h index 767147c..a171dbe 100644 --- a/examples/320 x 240/weather-station-v8/ArialRoundedMTBold_36.h +++ b/examples/320 x 240/weather-station/ArialRoundedMTBold_36.h @@ -22,7 +22,7 @@ See more at http://blog.squix.ch // In case of problems make sure that you are using the font file with the correct version! // Bodmer fix: End character is 0x7D not 0x7E, so bug in last line of the file corrected -// this avoids screen corruption if ~ is printer +// this avoids screen corruption if ~ is printed // Bodmer change: '`' changed to tiny degree symbol (typically this character is on top left key of a QWERTY keyboard) diff --git a/examples/320 x 240/weather-station-v8/ArialRoundedMtBold_14.h b/examples/320 x 240/weather-station/ArialRoundedMtBold_14.h similarity index 100% rename from examples/320 x 240/weather-station-v8/ArialRoundedMtBold_14.h rename to examples/320 x 240/weather-station/ArialRoundedMtBold_14.h diff --git a/examples/320 x 240/weather-station-v8/GfxUi.cpp b/examples/320 x 240/weather-station/GfxUi.cpp similarity index 94% rename from examples/320 x 240/weather-station-v8/GfxUi.cpp rename to examples/320 x 240/weather-station/GfxUi.cpp index e222adf..c425abb 100644 --- a/examples/320 x 240/weather-station-v8/GfxUi.cpp +++ b/examples/320 x 240/weather-station/GfxUi.cpp @@ -275,30 +275,35 @@ void GfxUi::jpegRender(int xpos, int ypos) { int mcu_x = JpegDec.MCUx * mcu_w + xpos; int mcu_y = JpegDec.MCUy * mcu_h + ypos; - // check if the image block size needs to be changed for the right and bottom edges + // check if the image block size needs to be changed for the right edge if (mcu_x + mcu_w <= max_x) win_w = mcu_w; else win_w = min_w; + + // check if the image block size needs to be changed for the bottom edge if (mcu_y + mcu_h <= max_y) win_h = mcu_h; else win_h = min_h; - // calculate how many pixels must be drawn - uint32_t mcu_pixels = win_w * win_h; + // copy pixels into a contiguous block + if (win_w != mcu_w) + { + uint16_t *cImg; + int p = 0; + cImg = pImg + win_w; + for (int h = 1; h < win_h; h++) + { + p += mcu_w; + for (int w = 0; w < win_w; w++) + { + *cImg = *(pImg + w + p); + cImg++; + } + } + } // draw image MCU block only if it will fit on the screen if ( ( mcu_x + win_w) <= _tft->width() && ( mcu_y + win_h) <= _tft->height()) - { -#ifdef USE_SPI_BUFFER - // Now set a MCU bounding window on the TFT to push pixels into (x, y, x + width - 1, y + height - 1) - _tft->setWindow(mcu_x, mcu_y, mcu_x + win_w - 1, mcu_y + win_h - 1); - // Write all MCU pixels to the TFT window - uint8_t *pImg8 = (uint8_t*)pImg; // Convert 16 bit pointer to an 8 bit pointer - _tft->pushColors(pImg8, mcu_pixels*2); // Send bytes via 64 byte SPI port buffer -#else - // Now set a MCU bounding window on the TFT to push pixels into (x, y, x + width - 1, y + height - 1) - _tft->setAddrWindow(mcu_x, mcu_y, mcu_x + win_w - 1, mcu_y + win_h - 1); - // Write all MCU pixels to the TFT window - while (mcu_pixels--) _tft->pushColor(*pImg++); -#endif + { + _tft->pushImage(mcu_x, mcu_y, win_w, win_h, pImg); } else if ( ( mcu_y + win_h) >= _tft->height()) JpegDec.abort(); diff --git a/examples/320 x 240/weather-station-v8/GfxUi.h b/examples/320 x 240/weather-station/GfxUi.h similarity index 100% rename from examples/320 x 240/weather-station-v8/GfxUi.h rename to examples/320 x 240/weather-station/GfxUi.h diff --git a/examples/320 x 240/weather-station-v8/SPIFFS_Support.ino b/examples/320 x 240/weather-station/SPIFFS_Support.ino similarity index 100% rename from examples/320 x 240/weather-station-v8/SPIFFS_Support.ino rename to examples/320 x 240/weather-station/SPIFFS_Support.ino diff --git a/examples/320 x 240/weather-station-v8/WebResource.cpp b/examples/320 x 240/weather-station/WebResource.cpp similarity index 100% rename from examples/320 x 240/weather-station-v8/WebResource.cpp rename to examples/320 x 240/weather-station/WebResource.cpp diff --git a/examples/320 x 240/weather-station-v8/WebResource.h b/examples/320 x 240/weather-station/WebResource.h similarity index 100% rename from examples/320 x 240/weather-station-v8/WebResource.h rename to examples/320 x 240/weather-station/WebResource.h diff --git a/examples/320 x 240/weather-station-v8/settings.h b/examples/320 x 240/weather-station/settings.h similarity index 78% rename from examples/320 x 240/weather-station-v8/settings.h rename to examples/320 x 240/weather-station/settings.h index 4a4618c..d4d97c2 100644 --- a/examples/320 x 240/weather-station-v8/settings.h +++ b/examples/320 x 240/weather-station/settings.h @@ -18,22 +18,30 @@ SOFTWARE. See more at http://blog.squix.ch Adapted by Bodmer to use the faster TFT_ILI9341_ESP library: -https://github.com/Bodmer/TFT_ILI9341_ESP +https://github.com/Bodmer/TFT_eSPI +Version 9 */ +// *************************************************************************************** +// WARNING - READ THIS +// +// 3M Flash Size MUST be allocated to SPIFFS using the IDE Tools menu option or else the +// ESP8266 may crash or do strange things (due to lack of error checks in SPIFFS library?) +// *************************************************************************************** +// // Setup const int UPDATE_INTERVAL_SECS = 10 * 60; // Update every 10 minutes -// Pins for the TFT interface are defined in the User_Config.h file inside the TFT_ILI9341_ESP library +// Pins for the TFT interface are defined in the User_Config.h file inside the TFT_eSPI library // TimeClient settings const float UTC_OFFSET = 1; -// Wunderground Settings, EDIT TO SUIT YOUR LOCATION +// Wunderground Settings, EDIT to suit your Wunderground key and location const boolean IS_METRIC = true; // Temperature only? Wind speed units appear to stay in mph. To do: investigate <<<<<<<<<<<<<<<<<<<<<<<<< -const String WUNDERGRROUND_API_KEY = ""; -//const String WUNDERGRROUND_API_KEY = "1c265fajf48s0a82"; // Random key example showing how the above line should look +//const String WUNDERGRROUND_API_KEY = "WUNDERGROUND KEY HERE"; + const String WUNDERGRROUND_API_KEY = "1c265fajf48s0a82"; // Random key example showing how the above line should look // For language codes see https://www.wunderground.com/weather/api/d/docs?d=language-support&_ga=1.55148395.1951311424.1484425551 const String WUNDERGRROUND_LANGUAGE = "EN"; // Language EN = English @@ -53,8 +61,8 @@ const String WUNDERGROUND_CITY = "Base_Naval"; // City, "London", "FL/Boca_Raton #define WIND_SPEED_UNITS " kph" //Thingspeak Settings - not used, no need to populate this at the moment -const String THINGSPEAK_CHANNEL_ID = ""; -const String THINGSPEAK_API_READ_KEY = ""; +const String THINGSPEAK_CHANNEL_ID = "CHANNEL_ID_HERE"; +const String THINGSPEAK_API_READ_KEY = "API_READ_KEY_HERE"; // List, so that the downloader knows what to fetch String wundergroundIcons [] = {"chanceflurries","chancerain","chancesleet","chancesnow","clear","cloudy","flurries","fog","hazy","mostlycloudy","mostlysunny","partlycloudy","partlysunny","rain","sleet","snow","sunny","tstorms","unknown"}; diff --git a/examples/320 x 240/weather-station-v8/weather-station-v8.ino b/examples/320 x 240/weather-station/weather-station.ino similarity index 99% rename from examples/320 x 240/weather-station-v8/weather-station-v8.ino rename to examples/320 x 240/weather-station/weather-station.ino index 4a84d28..ab3cfad 100644 --- a/examples/320 x 240/weather-station-v8/weather-station-v8.ino +++ b/examples/320 x 240/weather-station/weather-station.ino @@ -30,6 +30,7 @@ New smart WU splash startup screen and updated progress messages Display does not need to be blanked between updates Icons nudged about slightly to add wind direction + speed + Barometric pressure added */ #define SERIAL_MESSAGES From bdcbca43ec1be9da89537096d283b1d4897ccd62 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Mon, 23 Apr 2018 20:13:33 +0100 Subject: [PATCH 127/150] Fix ePaper 1bpp width bug --- Extensions/Sprite.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Extensions/Sprite.cpp b/Extensions/Sprite.cpp index 0ebca60..a7443e9 100644 --- a/Extensions/Sprite.cpp +++ b/Extensions/Sprite.cpp @@ -33,7 +33,7 @@ TFT_eSprite::TFT_eSprite(TFT_eSPI *tft) _xptr = 0; // pushColor coordinate _yptr = 0; - this->cursor_y = this->cursor_x = 0; // Text cursor position + this->cursor_y = this->cursor_x = 0; // Text cursor position } @@ -49,7 +49,7 @@ void* TFT_eSprite::createSprite(int16_t w, int16_t h, uint8_t frames) if ( w < 1 || h < 1 ) return NULL; - _iwidth = _dwidth = w; + _iwidth = _dwidth = _bitwidth = w; _iheight = _dheight = h; this->cursor_x = 0; @@ -703,7 +703,7 @@ int16_t TFT_eSprite::width(void) if (_rotation == 1 || _rotation == 3) return _dheight; - return _dwidth; + return _bitwidth; } @@ -1568,6 +1568,7 @@ void TFT_eSprite::printToSprite(String string) char cbuffer[len + 1]; // Add 1 for the null string.toCharArray(cbuffer, len + 1); // Add 1 for the null, otherwise characters get dropped printToSprite(cbuffer, len); + //printToSprite((char*)string.c_str(), string.length()); } From 2a9f7030d88c3eff90f45a0672650c16bbec0474 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Mon, 23 Apr 2018 20:29:52 +0100 Subject: [PATCH 128/150] Fix #111 --- TFT_eSPI.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/TFT_eSPI.h b/TFT_eSPI.h index 44e4ea8..e17774f 100644 --- a/TFT_eSPI.h +++ b/TFT_eSPI.h @@ -31,6 +31,14 @@ #define SPI_TOUCH_FREQUENCY 2500000 #endif +// Use GLCD font in error case where user requests a smooth font file +// that does not exist (this is a temporary fix to stop ESP32 reboot) +#ifdef SMOOTH_FONT + #ifndef LOAD_GLCD + #define LOAD_GLCD + #endif +#endif + // Only load the fonts defined in User_Setup.h (to save space) // Set flag so RLE rendering code is optionally compiled #ifdef LOAD_GLCD From 151dd60913a6788c9cefee1fb978f37582bbbe71 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Mon, 23 Apr 2018 21:02:50 +0100 Subject: [PATCH 129/150] Add ESP32 SDcard jpeg file rendering sketch --- .../ESP32_SDcard_jpeg/ESP32_SDcard_jpeg.ino | 268 ++++++++++++++++++ 1 file changed, 268 insertions(+) create mode 100644 examples/Generic/ESP32_SDcard_jpeg/ESP32_SDcard_jpeg.ino diff --git a/examples/Generic/ESP32_SDcard_jpeg/ESP32_SDcard_jpeg.ino b/examples/Generic/ESP32_SDcard_jpeg/ESP32_SDcard_jpeg.ino new file mode 100644 index 0000000..9cc8b02 --- /dev/null +++ b/examples/Generic/ESP32_SDcard_jpeg/ESP32_SDcard_jpeg.ino @@ -0,0 +1,268 @@ +// This sketch if for an ESP32, it draws Jpeg images pulled from an SD Card +// onto the TFT. + +// As well as the TFT_eSPI library you will need the JPEG Decoder library. +// A copy can be downloaded here, it is based on the library by Makoto Kurauchi. +// https://github.com/Bodmer/JPEGDecoder + +// Images on SD Card must be put in the root folder (top level) to be found +// Use the SD library examples to verify your SD Card interface works! + +// The example images used to test this sketch can be found in the library +// JPEGDecoder/extras folder +//---------------------------------------------------------------------------------------------------- + +#include + +#include +#include + +#include +TFT_eSPI tft = TFT_eSPI(); + +// JPEG decoder library +#include + +//#################################################################################################### +// Setup +//#################################################################################################### +void setup() { + Serial.begin(115200); + + // Set all chip selects high to avoid bus contention during initialisation of each peripheral + digitalWrite(22, HIGH); // Touch controller chip select (if used) + digitalWrite(15, HIGH); // TFT screen chip select + digitalWrite( 5, HIGH); // SD card chips select, must use GPIO 5 (ESP32 SS) + + tft.begin(); + + if (!SD.begin()) { + Serial.println("Card Mount Failed"); + return; + } + uint8_t cardType = SD.cardType(); + + if (cardType == CARD_NONE) { + Serial.println("No SD card attached"); + return; + } + + Serial.print("SD Card Type: "); + if (cardType == CARD_MMC) { + Serial.println("MMC"); + } else if (cardType == CARD_SD) { + Serial.println("SDSC"); + } else if (cardType == CARD_SDHC) { + Serial.println("SDHC"); + } else { + Serial.println("UNKNOWN"); + } + + uint64_t cardSize = SD.cardSize() / (1024 * 1024); + Serial.printf("SD Card Size: %lluMB\n", cardSize); + + Serial.println("initialisation done."); +} + +//#################################################################################################### +// Main loop +//#################################################################################################### +void loop() { + + tft.setRotation(2); // portrait + tft.fillScreen(random(0xFFFF)); + + // The image is 300 x 300 pixels so we do some sums to position image in the middle of the screen! + // Doing this by reading the image width and height from the jpeg info is left as an exercise! + int x = (tft.width() - 300) / 2 - 1; + int y = (tft.height() - 300) / 2 - 1; + + drawSdJpeg("/EagleEye.jpg", x, y); // This draws a jpeg pulled off the SD Card + delay(2000); + + tft.setRotation(2); // portrait + tft.fillScreen(random(0xFFFF)); + drawSdJpeg("/Baboon40.jpg", 0, 0); // This draws a jpeg pulled off the SD Card + delay(2000); + + tft.setRotation(2); // portrait + tft.fillScreen(random(0xFFFF)); + drawSdJpeg("/lena20k.jpg", 0, 0); // This draws a jpeg pulled off the SD Card + delay(2000); + + tft.setRotation(1); // landscape + tft.fillScreen(random(0xFFFF)); + drawSdJpeg("/Mouse480.jpg", 0, 0); // This draws a jpeg pulled off the SD Card + + delay(2000); + + while(1); // Wait here +} + +//#################################################################################################### +// Draw a JPEG on the TFT pulled from SD Card +//#################################################################################################### +// xpos, ypos is top left corner of plotted image +void drawSdJpeg(const char *filename, int xpos, int ypos) { + + // Open the named file (the Jpeg decoder library will close it) + File jpegFile = SD.open( filename, FILE_READ); // or, file handle reference for SD library + + if ( !jpegFile ) { + Serial.print("ERROR: File \""); Serial.print(filename); Serial.println ("\" not found!"); + return; + } + + Serial.println("==========================="); + Serial.print("Drawing file: "); Serial.println(filename); + Serial.println("==========================="); + + // Use one of the following methods to initialise the decoder: + boolean decoded = JpegDec.decodeSdFile(jpegFile); // Pass the SD file handle to the decoder, + //boolean decoded = JpegDec.decodeSdFile(filename); // or pass the filename (String or character array) + + if (decoded) { + // print information about the image to the serial port + jpegInfo(); + // render the image onto the screen at given coordinates + jpegRender(xpos, ypos); + } + else { + Serial.println("Jpeg file format not supported!"); + } +} + +//#################################################################################################### +// Draw a JPEG on the TFT, images will be cropped on the right/bottom sides if they do not fit +//#################################################################################################### +// This function assumes xpos,ypos is a valid screen coordinate. For convenience images that do not +// fit totally on the screen are cropped to the nearest MCU size and may leave right/bottom borders. +void jpegRender(int xpos, int ypos) { + + //jpegInfo(); // Print information from the JPEG file (could comment this line out) + + uint16_t *pImg; + uint16_t mcu_w = JpegDec.MCUWidth; + uint16_t mcu_h = JpegDec.MCUHeight; + uint32_t max_x = JpegDec.width; + uint32_t max_y = JpegDec.height; + + bool swapBytes = tft.getSwapBytes(); + tft.setSwapBytes(true); + + // Jpeg images are draw as a set of image block (tiles) called Minimum Coding Units (MCUs) + // Typically these MCUs are 16x16 pixel blocks + // Determine the width and height of the right and bottom edge image blocks + uint32_t min_w = min(mcu_w, max_x % mcu_w); + uint32_t min_h = min(mcu_h, max_y % mcu_h); + + // save the current image block size + uint32_t win_w = mcu_w; + uint32_t win_h = mcu_h; + + // record the current time so we can measure how long it takes to draw an image + uint32_t drawTime = millis(); + + // save the coordinate of the right and bottom edges to assist image cropping + // to the screen size + max_x += xpos; + max_y += ypos; + + // Fetch data from the file, decode and display + while (JpegDec.read()) { // While there is more data in the file + pImg = JpegDec.pImage ; // Decode a MCU (Minimum Coding Unit, typically a 8x8 or 16x16 pixel block) + + // Calculate coordinates of top left corner of current MCU + int mcu_x = JpegDec.MCUx * mcu_w + xpos; + int mcu_y = JpegDec.MCUy * mcu_h + ypos; + + // check if the image block size needs to be changed for the right edge + if (mcu_x + mcu_w <= max_x) win_w = mcu_w; + else win_w = min_w; + + // check if the image block size needs to be changed for the bottom edge + if (mcu_y + mcu_h <= max_y) win_h = mcu_h; + else win_h = min_h; + + // copy pixels into a contiguous block + if (win_w != mcu_w) + { + uint16_t *cImg; + int p = 0; + cImg = pImg + win_w; + for (int h = 1; h < win_h; h++) + { + p += mcu_w; + for (int w = 0; w < win_w; w++) + { + *cImg = *(pImg + w + p); + cImg++; + } + } + } + + // calculate how many pixels must be drawn + uint32_t mcu_pixels = win_w * win_h; + + // draw image MCU block only if it will fit on the screen + if (( mcu_x + win_w ) <= tft.width() && ( mcu_y + win_h ) <= tft.height()) + tft.pushImage(mcu_x, mcu_y, win_w, win_h, pImg); + else if ( (mcu_y + win_h) >= tft.height()) + JpegDec.abort(); // Image has run off bottom of screen so abort decoding + } + + tft.setSwapBytes(swapBytes); + + showTime(millis() - drawTime); // These lines are for sketch testing only +} + +//#################################################################################################### +// Print image information to the serial port (optional) +//#################################################################################################### +// JpegDec.decodeFile(...) or JpegDec.decodeArray(...) must be called before this info is available! +void jpegInfo() { + + // Print information extracted from the JPEG file + Serial.println("JPEG image info"); + Serial.println("==============="); + Serial.print("Width :"); + Serial.println(JpegDec.width); + Serial.print("Height :"); + Serial.println(JpegDec.height); + Serial.print("Components :"); + Serial.println(JpegDec.comps); + Serial.print("MCU / row :"); + Serial.println(JpegDec.MCUSPerRow); + Serial.print("MCU / col :"); + Serial.println(JpegDec.MCUSPerCol); + Serial.print("Scan type :"); + Serial.println(JpegDec.scanType); + Serial.print("MCU width :"); + Serial.println(JpegDec.MCUWidth); + Serial.print("MCU height :"); + Serial.println(JpegDec.MCUHeight); + Serial.println("==============="); + Serial.println(""); +} + +//#################################################################################################### +// Show the execution time (optional) +//#################################################################################################### +// WARNING: for UNO/AVR legacy reasons printing text to the screen with the Mega might not work for +// sketch sizes greater than ~70KBytes because 16 bit address pointers are used in some libraries. + +// The Due will work fine with the HX8357_Due library. + +void showTime(uint32_t msTime) { + //tft.setCursor(0, 0); + //tft.setTextFont(1); + //tft.setTextSize(2); + //tft.setTextColor(TFT_WHITE, TFT_BLACK); + //tft.print(F(" JPEG drawn in ")); + //tft.print(msTime); + //tft.println(F(" ms ")); + Serial.print(F(" JPEG drawn in ")); + Serial.print(msTime); + Serial.println(F(" ms ")); +} + From 2acacb71a9c04cd6a7bb38a3211767593a381438 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Fri, 27 Apr 2018 18:56:31 +0100 Subject: [PATCH 130/150] Update readme --- README.md | 47 ++++++++++++++++------------------------------- 1 file changed, 16 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index ad04387..13ad91a 100644 --- a/README.md +++ b/README.md @@ -10,15 +10,17 @@ Update 24th February 2018: Added new smooth (antialiased) fonts. See Smooth Font # TFT_eSPI -An Arduino IDE compatible graphics and fonts library for ESP8266 and ESP32 processors with a driver for ILI9341, ILI9163, ST7735, S6D02A1, ILI9481, ILI9488, ILI9486 and HX8357 based TFT displays that support SPI. 8 bit parallel interface TFTs (e.g. UNO format mcufriend shilds) can used with an ESP32 +An Arduino IDE compatible graphics and fonts library for ESP8266 and ESP32 processors with a driver for ILI9341, ILI9163, ST7735, S6D02A1, ILI9481, ILI9488, ILI9486 and HX8357 based TFT displays that support SPI. 8 bit parallel interface TFTs (e.g. UNO format mcufriend shilds) can used with an ESP32. -The library also supports TFT displays designed for the Raspberry Pi that are based on a ILI9486 driver chip with a 480 x 320 pixel screen. This display must be of the Waveshare design and use a 16 bit serial interface based on the 74HC04, 74HC4040 and 2 x 74HC4094 logic chips. A modification to these displays is possible (see mod image in Tools folder) to make many graphics functions much faster (e.g. 23ms to clear the screen, 1.2ms to draw a 72 pixel high numeral). +The library supports TFT displays designed for the Raspberry Pi that are based on a ILI9486 driver chip with a 480 x 320 pixel screen. This display must be of the Waveshare design and use a 16 bit serial interface based on the 74HC04, 74HC4040 and 2 x 74HC4094 logic chips. A modification to these displays is possible (see mod image in Tools folder) to make many graphics functions much faster (e.g. 23ms to clear the screen, 1.2ms to draw a 72 pixel high numeral). -A new "Sprite" class has been added, this enables flicker free updates of complex graphics. Direct writes to the TFT with graphics functions are still available, so existing sketches do not need to be changed. +Support has been added recently for Waveshare 2 and 3 colour ePaper displays using full frame buffers. This addition is currently relatively immature and thus only one example has been provided. Further examples will be added soon. -A Sprite is notionally an invisible graphics screen that is kept in the processors RAM. Graphics can be drawn into the Sprite just as they can be drawn directly to the screen. Once the Sprite is completed it can be plotted onto the screen in any position. If there is sufficient RAM then the Sprite can be the same size as the screen and used as a frame buffer. Sprites by default use 16 bit colours, the bit depth can be set to 8 bits (256 colours) , or 1 bit (any 2 colours) to reduce the RAM needed. On an ESP8266 the largest 16 bit colour Sprite that can be created is about 160x128 pixels, this consumes 40Kbytes of RAM. On an ESP32 the workspace RAM is more limited than the datsheet implies so a 16 bit colour Sprite is limited to about 200x200 pixels (~80Kbytes), an 8 bit sprite to 320x240 pixels (~76kbytes). A 1 bit per pixel Spriee required only 9600 bytes for a full 320 x 240 screen buffer, this is ideal for supporting usie with 2 colour bitmap fonts. +The library includes a "Sprite" class, this enables flicker free updates of complex graphics. Direct writes to the TFT with graphics functions are still available, so existing sketches do not need to be changed. -One or more sprites can be created, a sprite can be any width and height, limited only by available RAM. The RAM needed for a 16 bit colour depth Sprite is (2 x width x height) bytes, for a Sprite with 8 bit colour depth the RAM needed is (width x height) bytes. Sprites can be created and deleted dynamically as needed in the sketch, this means RAM can be freed up after the sprite has been plotted on the screen, more RAM intensive WiFi based code can then be run and normal graphics operations still work. +A Sprite is notionally an invisible graphics screen that is kept in the processors RAM. Graphics can be drawn into the Sprite just as they can be drawn directly to the screen. Once the Sprite is completed it can be plotted onto the screen in any position. If there is sufficient RAM then the Sprite can be the same size as the screen and used as a frame buffer. Sprites by default use 16 bit colours, the bit depth can be set to 8 bits (256 colours) , or 1 bit (any 2 colours) to reduce the RAM needed. On an ESP8266 the largest 16 bit colour Sprite that can be created is about 160x128 pixels, this consumes 40Kbytes of RAM. On an ESP32 the workspace RAM is more limited than the datsheet implies so a 16 bit colour Sprite is limited to about 200x200 pixels (~80Kbytes), an 8 bit sprite to 320x240 pixels (~76kbytes). A 1 bit per pixel Sprite requires only 9600 bytes for a full 320 x 240 screen buffer, this is ideal for supporting use with 2 colour bitmap fonts. + +One or more sprites can be created, a sprite can be any pixel width and height, limited only by available RAM. The RAM needed for a 16 bit colour depth Sprite is (2 x width x height) bytes, for a Sprite with 8 bit colour depth the RAM needed is (width x height) bytes. Sprites can be created and deleted dynamically as needed in the sketch, this means RAM can be freed up after the Sprite has been plotted on the screen, more RAM intensive WiFi based code can then be run and normal graphics operations still work. Drawing graphics into a sprite is very fast, for those familiar with the Adafruit "graphicstest" example, this whole test completes in 18ms in a 160x128 sprite. Examples of sprite use can be found in the "examples/Sprite" folder. @@ -28,37 +30,24 @@ The XPT2046 touch screen controller is supported. The SPI bus for the touch cont The Button class from Adafruit_GFX is incorporated, with the enhancement that the button labels can be in any font. -The library supports SPI overlap on the ESP8266 so the TFT screen can share MOSI, MISO and SCLK pins with the program FLASH. +The library supports SPI overlap on the ESP8266 so the TFT screen can share MOSI, MISO and SCLK pins with the program FLASH, this frees up GPIO pins for other uses. -The library contains proportional fonts, different sizes can be enabled/disabled at compile time to optimise the use of FLASH memory. The library has been tested with the NodeMCU (ESP8266 based) and an ESP32 demo board. +The library contains proportional fonts, different sizes can be enabled/disabled at compile time to optimise the use of FLASH memory. Anti-alased (smooth) font files in vlw format stored in SPIFFS are supported and in the case any 16 bit Unicode character can be included and rendered, this means many language specific characters can be rendered to the screen. -The library is based on the Adafruit GFX and Adafruit driver libraries and the aim is to retain compatibility. Significant additions have been made to the library to boost the speed for ESP8266/ESP32 processors (it is typically 3 to 10 times faster) and to add new features. The new graphics functions include different size proportional fonts and formatting features. There are lots of example sketches to demonstrate the different features. +The library is based on the Adafruit GFX and Adafruit driver libraries and the aim is to retain compatibility. Significant additions have been made to the library to boost the speed for ESP8266/ESP32 processors (it is typically 3 to 10 times faster) and to add new features. The new graphics functions include different size proportional fonts and formatting features. There are lots of example sketches to demonstrate the different features and included functions. -Configuration of the library font selections, pins used to interface with the TFT and other features is made by editting the User_Setup.h file in the library folder. Fonts and features can easily be disabled by commenting out lines. +Configuration of the library font selections, pins used to interface with the TFT and other features is made by editting the User_Setup.h file in the library folder, or by selecting a configuration in the library "User_Setup_Selet,h" file. Fonts and features can easily be disabled by commenting out lines. -## The future... ## - -**1. Performance improvements - done 8/1/18** - -I have made some changes that will be uploaded soon that improves sprite and image rendering performance by up to 3x faster on the ESP8266. These updates are currently being tested/debugged. - -**2. Anti-aliased fonts - done 24/2/18** - -I have been experimenting with anti-aliased font files in "vlw" format generated by the free [Processing IDE](https://processing.org/). This IDE can be used to generate font files from your computer's font set and include **any** 16 bit Unicode characters. This means Greek, Japanese and any other UCS-2 glyphs can be used. Character arrays and Strings in UTF-8 format are supported. +Smooth font files in "vlw" format are generated by the free [Processing IDE](https://processing.org/) using a sketch included in the library Tools folder. This sketch with the Processing IDE can be used to generate font files from your computer's font set or any TrueType (.ttf) font, the font file can include **any** combination of 16 bit Unicode characters. This means Greek, Japanese and any other UCS-2 glyphs can be used. Character arrays and Strings in UTF-8 format are supported. It would be possible to compress the vlw font files but the rendering performance to a TFT is still good when storing the font file(s) in SPIFFS. -Here are some screen grabs (from an ILI9341 240x320 pixel TFT) showing 32pt characters. Can you spot the difference between anti-aliased and 2 colour "bitmap" fonts? - -![Anti-aliased fonts](https://i.imgur.com/HjGbUhG.png) - -Here is another screenshot, showing the anti-aliased Hiragana character Unicode block (0x3041 to 0x309F) in 24pt from the Microsoft Yahei font: +Here is an example screenshot showing the anti-aliased Hiragana character Unicode block (0x3041 to 0x309F) in 24pt from the Microsoft Yahei font: ![Hiragana glyphs](https://i.imgur.com/jeXf2st.png) -Currently these are generated from a sketch, but when I have the Alpha blending sorted for colour backgrounds the plan is to build the rendering code into the TFT_eSPI library. Watch this space " " for updates! -**3. Add support for 8 bit "UNO" style TFTs with ESP32 - done 10/3/18** +**ESP32 with 8 bit Mcufriend UNO shields** The ESP32 board I have been using for testing has the following pinout: @@ -76,13 +65,9 @@ IO32 wired to IO36 ![Example](https://i.imgur.com/pUZn6lF.jpg) -**4. Add support for Touch support 8 bit "UNO" style TFTs with ESP32** +**ePaper displays** -Adding touch support for UNO style displays with resistive screens may be possible - to be investigated. - -**4. Add support for ePaper displays - done 2/4/18** - -The library was intended to support only TFT displays but using a Sprite as a 1 bit per pixel screen buffer permits support for the Waveshare 2 and 3 colour SPI ePaper displays. So far this is just an experiment and no code has been released for this: +The library was intended to support only TFT displays but using a Sprite as a 1 bit per pixel screen buffer permits support for the Waveshare 2 and 3 colour SPI ePaper displays. This addition to the library is experimental and only one example is provided. Further examples will be added. ![Example](https://i.imgur.com/L2tV129.jpg?1) From 4704d844d26291c48e079ec2f5802c52d469204e Mon Sep 17 00:00:00 2001 From: Bodmer Date: Fri, 27 Apr 2018 18:59:20 +0100 Subject: [PATCH 131/150] Update README.md --- README.md | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 13ad91a..f55d37c 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,3 @@ -Update 2nd April 2018: Added final stage support ePaper (Waveshare) displays plus an example (others to follow). - -Update 26th March 2018: Added support for 1 bit per pixel Sprites. Example to follow with ePaper (Waveshare) support. - -Update 10th March 2018: Added support for 8 bit parallel interface TFTs when used with ESP32 (Touch not supported yet on parallel displays) - -Update 24th February 2018: Added new smooth (antialiased) fonts. See Smooth Font examples and Tools folder for the font generator. Fonts can include characters with 16 bit Unicodes (Basic Multilingual Plane). - -![Example](https://i.imgur.com/xJF0Oz7.png) - # TFT_eSPI An Arduino IDE compatible graphics and fonts library for ESP8266 and ESP32 processors with a driver for ILI9341, ILI9163, ST7735, S6D02A1, ILI9481, ILI9488, ILI9486 and HX8357 based TFT displays that support SPI. 8 bit parallel interface TFTs (e.g. UNO format mcufriend shilds) can used with an ESP32. @@ -38,7 +28,13 @@ The library is based on the Adafruit GFX and Adafruit driver libraries and the a Configuration of the library font selections, pins used to interface with the TFT and other features is made by editting the User_Setup.h file in the library folder, or by selecting a configuration in the library "User_Setup_Selet,h" file. Fonts and features can easily be disabled by commenting out lines. -Smooth font files in "vlw" format are generated by the free [Processing IDE](https://processing.org/) using a sketch included in the library Tools folder. This sketch with the Processing IDE can be used to generate font files from your computer's font set or any TrueType (.ttf) font, the font file can include **any** combination of 16 bit Unicode characters. This means Greek, Japanese and any other UCS-2 glyphs can be used. Character arrays and Strings in UTF-8 format are supported. +**Anti-aliased Fonts** + +Anti-aliased (smooth) font files in "vlw" format are generated by the free [Processing IDE](https://processing.org/) using a sketch included in the library Tools folder. This sketch with the Processing IDE can be used to generate font files from your computer's font set or any TrueType (.ttf) font, the font file can include **any** combination of 16 bit Unicode characters. This means Greek, Japanese and any other UCS-2 glyphs can be used. Character arrays and Strings in UTF-8 format are supported. + +The smooth font example dispays the following screen: + +![Example](https://i.imgur.com/xJF0Oz7.png) It would be possible to compress the vlw font files but the rendering performance to a TFT is still good when storing the font file(s) in SPIFFS. From a5952ead3e9a3d0ef34a59612329f0e05e465721 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Fri, 27 Apr 2018 19:05:19 +0100 Subject: [PATCH 132/150] Update README.md --- README.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f55d37c..f2a9024 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,7 @@ The library is based on the Adafruit GFX and Adafruit driver libraries and the a Configuration of the library font selections, pins used to interface with the TFT and other features is made by editting the User_Setup.h file in the library folder, or by selecting a configuration in the library "User_Setup_Selet,h" file. Fonts and features can easily be disabled by commenting out lines. + **Anti-aliased Fonts** Anti-aliased (smooth) font files in "vlw" format are generated by the free [Processing IDE](https://processing.org/) using a sketch included in the library Tools folder. This sketch with the Processing IDE can be used to generate font files from your computer's font set or any TrueType (.ttf) font, the font file can include **any** combination of 16 bit Unicode characters. This means Greek, Japanese and any other UCS-2 glyphs can be used. Character arrays and Strings in UTF-8 format are supported. @@ -43,7 +44,8 @@ Here is an example screenshot showing the anti-aliased Hiragana character Unicod ![Hiragana glyphs](https://i.imgur.com/jeXf2st.png) -**ESP32 with 8 bit Mcufriend UNO shields** + +**ESP32 with 8 bit Mcufriend UNO shields** The ESP32 board I have been using for testing has the following pinout: @@ -51,7 +53,7 @@ The ESP32 board I have been using for testing has the following pinout: UNO style boards with a Wemos R32(ESP32) label are also available at low cost with the same pin-out. -Unfortunately the typical UNO/mcufriend TFT display board maps LCD_RD, LCD_CS and LCD_RST signals to the ESP32 pins 35, 34 and 36 which are input only. To solve this I linked in the 3 spare pins IO15, IO33 and IO32 by adding wires to the bottom of the board as follows: +Unfortunately the typical UNO/mcufriend TFT display board maps LCD_RD, LCD_CS and LCD_RST signals to the ESP32 analogue pins 35, 34 and 36 which are input only. To solve this I linked in the 3 spare pins IO15, IO33 and IO32 by adding wires to the bottom of the board as follows: IO15 wired to IO35 @@ -61,6 +63,7 @@ IO32 wired to IO36 ![Example](https://i.imgur.com/pUZn6lF.jpg) + **ePaper displays** The library was intended to support only TFT displays but using a Sprite as a 1 bit per pixel screen buffer permits support for the Waveshare 2 and 3 colour SPI ePaper displays. This addition to the library is experimental and only one example is provided. Further examples will be added. From dcd547b062a067cde3eed08f4e43aacd444c4bd0 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Fri, 27 Apr 2018 19:06:55 +0100 Subject: [PATCH 133/150] Update README.md --- README.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index f2a9024..76dfa85 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ # TFT_eSPI -An Arduino IDE compatible graphics and fonts library for ESP8266 and ESP32 processors with a driver for ILI9341, ILI9163, ST7735, S6D02A1, ILI9481, ILI9488, ILI9486 and HX8357 based TFT displays that support SPI. 8 bit parallel interface TFTs (e.g. UNO format mcufriend shilds) can used with an ESP32. +An Arduino IDE compatible graphics and fonts library for ESP8266 and ESP32 processors with a driver for ILI9341, ILI9163, ST7735, S6D02A1, ILI9481, ILI9488, ILI9486 and HX8357 based TFT displays that support SPI. + +8 bit parallel interface TFTs (e.g. UNO format mcufriend shields) can used with an ESP32. The library supports TFT displays designed for the Raspberry Pi that are based on a ILI9486 driver chip with a 480 x 320 pixel screen. This display must be of the Waveshare design and use a 16 bit serial interface based on the 74HC04, 74HC4040 and 2 x 74HC4094 logic chips. A modification to these displays is possible (see mod image in Tools folder) to make many graphics functions much faster (e.g. 23ms to clear the screen, 1.2ms to draw a 72 pixel high numeral). @@ -29,7 +31,7 @@ The library is based on the Adafruit GFX and Adafruit driver libraries and the a Configuration of the library font selections, pins used to interface with the TFT and other features is made by editting the User_Setup.h file in the library folder, or by selecting a configuration in the library "User_Setup_Selet,h" file. Fonts and features can easily be disabled by commenting out lines. -**Anti-aliased Fonts** +#Anti-aliased Fonts Anti-aliased (smooth) font files in "vlw" format are generated by the free [Processing IDE](https://processing.org/) using a sketch included in the library Tools folder. This sketch with the Processing IDE can be used to generate font files from your computer's font set or any TrueType (.ttf) font, the font file can include **any** combination of 16 bit Unicode characters. This means Greek, Japanese and any other UCS-2 glyphs can be used. Character arrays and Strings in UTF-8 format are supported. @@ -45,7 +47,7 @@ Here is an example screenshot showing the anti-aliased Hiragana character Unicod -**ESP32 with 8 bit Mcufriend UNO shields** +#ESP32 with 8 bit Mcufriend UNO shields The ESP32 board I have been using for testing has the following pinout: From 9ffe3581d0d1a9e3b4cc9a97fed18a662e86cbd6 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Fri, 27 Apr 2018 19:07:53 +0100 Subject: [PATCH 134/150] Update README.md --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 76dfa85..ac36a7d 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ The library is based on the Adafruit GFX and Adafruit driver libraries and the a Configuration of the library font selections, pins used to interface with the TFT and other features is made by editting the User_Setup.h file in the library folder, or by selecting a configuration in the library "User_Setup_Selet,h" file. Fonts and features can easily be disabled by commenting out lines. -#Anti-aliased Fonts +# Anti-aliased Fonts Anti-aliased (smooth) font files in "vlw" format are generated by the free [Processing IDE](https://processing.org/) using a sketch included in the library Tools folder. This sketch with the Processing IDE can be used to generate font files from your computer's font set or any TrueType (.ttf) font, the font file can include **any** combination of 16 bit Unicode characters. This means Greek, Japanese and any other UCS-2 glyphs can be used. Character arrays and Strings in UTF-8 format are supported. @@ -47,7 +47,7 @@ Here is an example screenshot showing the anti-aliased Hiragana character Unicod -#ESP32 with 8 bit Mcufriend UNO shields +# ESP32 with 8 bit Mcufriend UNO shields The ESP32 board I have been using for testing has the following pinout: @@ -66,7 +66,7 @@ IO32 wired to IO36 ![Example](https://i.imgur.com/pUZn6lF.jpg) -**ePaper displays** +# ePaper displays The library was intended to support only TFT displays but using a Sprite as a 1 bit per pixel screen buffer permits support for the Waveshare 2 and 3 colour SPI ePaper displays. This addition to the library is experimental and only one example is provided. Further examples will be added. From 92a21ac94bf1cb4a07b3fe832567a94b733cffea Mon Sep 17 00:00:00 2001 From: Bodmer Date: Fri, 27 Apr 2018 19:24:15 +0100 Subject: [PATCH 135/150] Update README.md --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index ac36a7d..87e5a82 100644 --- a/README.md +++ b/README.md @@ -35,6 +35,10 @@ Configuration of the library font selections, pins used to interface with the TF Anti-aliased (smooth) font files in "vlw" format are generated by the free [Processing IDE](https://processing.org/) using a sketch included in the library Tools folder. This sketch with the Processing IDE can be used to generate font files from your computer's font set or any TrueType (.ttf) font, the font file can include **any** combination of 16 bit Unicode characters. This means Greek, Japanese and any other UCS-2 glyphs can be used. Character arrays and Strings in UTF-8 format are supported. +The next image shows the differentce between a bitmap and anti-aliased font: + +![Smooth_font](https://i.imgur.com/gAeDPFY.png) + The smooth font example dispays the following screen: ![Example](https://i.imgur.com/xJF0Oz7.png) From c9708aa155e57978ae4d036b7c7c273d9c3e9c87 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Fri, 27 Apr 2018 19:28:42 +0100 Subject: [PATCH 136/150] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 87e5a82..bd3c40e 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ Configuration of the library font selections, pins used to interface with the TF Anti-aliased (smooth) font files in "vlw" format are generated by the free [Processing IDE](https://processing.org/) using a sketch included in the library Tools folder. This sketch with the Processing IDE can be used to generate font files from your computer's font set or any TrueType (.ttf) font, the font file can include **any** combination of 16 bit Unicode characters. This means Greek, Japanese and any other UCS-2 glyphs can be used. Character arrays and Strings in UTF-8 format are supported. -The next image shows the differentce between a bitmap and anti-aliased font: +Here is the Adafruit_GFX "FreeSans12pt" bitmap font compared to the same font in drawn as anti-aliased: ![Smooth_font](https://i.imgur.com/gAeDPFY.png) From 92c5d41fa63c5f6465def2ac75f7ef32f95efbc8 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Fri, 27 Apr 2018 19:29:20 +0100 Subject: [PATCH 137/150] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bd3c40e..86342cc 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ Configuration of the library font selections, pins used to interface with the TF Anti-aliased (smooth) font files in "vlw" format are generated by the free [Processing IDE](https://processing.org/) using a sketch included in the library Tools folder. This sketch with the Processing IDE can be used to generate font files from your computer's font set or any TrueType (.ttf) font, the font file can include **any** combination of 16 bit Unicode characters. This means Greek, Japanese and any other UCS-2 glyphs can be used. Character arrays and Strings in UTF-8 format are supported. -Here is the Adafruit_GFX "FreeSans12pt" bitmap font compared to the same font in drawn as anti-aliased: +Here is the Adafruit_GFX "FreeSans12pt" bitmap font compared to the same font drawn as anti-aliased: ![Smooth_font](https://i.imgur.com/gAeDPFY.png) From 47f890f2125c6418952e1b250e8535406ecf4a00 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Fri, 27 Apr 2018 20:07:06 +0100 Subject: [PATCH 138/150] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 86342cc..54157c2 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,7 @@ Here is the Adafruit_GFX "FreeSans12pt" bitmap font compared to the same font dr ![Smooth_font](https://i.imgur.com/gAeDPFY.png) -The smooth font example dispays the following screen: +The smooth font example displays the following screen: ![Example](https://i.imgur.com/xJF0Oz7.png) From 96916c2bceffb0e8d2baa2be993ee7bed790879d Mon Sep 17 00:00:00 2001 From: Bodmer Date: Tue, 1 May 2018 16:44:34 +0100 Subject: [PATCH 139/150] Add SPIFFS BMP (bitmap) image file rendering example --- .../Generic/TFT_SPIFFS_BMP/BMP_functions.ino | 88 ++++++++++++++++++ .../Generic/TFT_SPIFFS_BMP/TFT_SPIFFS_BMP.ino | 64 +++++++++++++ .../Generic/TFT_SPIFFS_BMP/data/parrot.bmp | Bin 0 -> 61496 bytes 3 files changed, 152 insertions(+) create mode 100644 examples/Generic/TFT_SPIFFS_BMP/BMP_functions.ino create mode 100644 examples/Generic/TFT_SPIFFS_BMP/TFT_SPIFFS_BMP.ino create mode 100644 examples/Generic/TFT_SPIFFS_BMP/data/parrot.bmp diff --git a/examples/Generic/TFT_SPIFFS_BMP/BMP_functions.ino b/examples/Generic/TFT_SPIFFS_BMP/BMP_functions.ino new file mode 100644 index 0000000..515f13a --- /dev/null +++ b/examples/Generic/TFT_SPIFFS_BMP/BMP_functions.ino @@ -0,0 +1,88 @@ +// Bodmers BMP image rendering function + +void drawBmp(const char *filename, int16_t x, int16_t y) { + + if ((x >= tft.width()) || (y >= tft.height())) return; + + fs::File bmpFS; + + // Open requested file on SD card + bmpFS = SPIFFS.open(filename, "r"); + + if (!bmpFS) + { + Serial.print("File not found"); + return; + } + + uint32_t seekOffset; + uint16_t w, h, row, col; + uint8_t r, g, b; + + uint32_t startTime = millis(); + + if (read16(bmpFS) == 0x4D42) + { + read32(bmpFS); + read32(bmpFS); + seekOffset = read32(bmpFS); + read32(bmpFS); + w = read32(bmpFS); + h = read32(bmpFS); + + if ((read16(bmpFS) == 1) && (read16(bmpFS) == 24) && (read32(bmpFS) == 0)) + { + y += h - 1; + + tft.setSwapBytes(true); + bmpFS.seek(seekOffset); + + uint16_t padding = (4 - ((w * 3) & 3)) & 3; + uint8_t lineBuffer[w * 3 + padding]; + + for (row = 0; row < h; row++) { + + bmpFS.read(lineBuffer, sizeof(lineBuffer)); + uint8_t* bptr = lineBuffer; + uint16_t* tptr = (uint16_t*)lineBuffer; + // Convert 24 to 16 bit colours + for (uint16_t col = 0; col < w; col++) + { + b = *bptr++; + g = *bptr++; + r = *bptr++; + *tptr++ = ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3); + } + + // Push the pixel row to screen, pushImage will crop the line if needed + // y is decremented as the BMP image is drawn bottom up + tft.pushImage(x, y--, w, 1, (uint16_t*)lineBuffer); + } + Serial.print("Loaded in "); Serial.print(millis() - startTime); + Serial.println(" ms"); + } + else Serial.println("BMP format not recognized."); + } + bmpFS.close(); +} + +// These read 16- and 32-bit types from the SD card file. +// BMP data is stored little-endian, Arduino is little-endian too. +// May need to reverse subscript order if porting elsewhere. + +uint16_t read16(fs::File &f) { + uint16_t result; + ((uint8_t *)&result)[0] = f.read(); // LSB + ((uint8_t *)&result)[1] = f.read(); // MSB + return result; +} + +uint32_t read32(fs::File &f) { + uint32_t result; + ((uint8_t *)&result)[0] = f.read(); // LSB + ((uint8_t *)&result)[1] = f.read(); + ((uint8_t *)&result)[2] = f.read(); + ((uint8_t *)&result)[3] = f.read(); // MSB + return result; +} + diff --git a/examples/Generic/TFT_SPIFFS_BMP/TFT_SPIFFS_BMP.ino b/examples/Generic/TFT_SPIFFS_BMP/TFT_SPIFFS_BMP.ino new file mode 100644 index 0000000..a40ce4c --- /dev/null +++ b/examples/Generic/TFT_SPIFFS_BMP/TFT_SPIFFS_BMP.ino @@ -0,0 +1,64 @@ +// This sketch draws BMP images pulled from SPIFFS onto the TFT. It is an +// an example from this library: https://github.com/Bodmer/TFT_eSPI + +// Images in SPIFFS must be put in the root folder (top level) to be found +// Use the SPIFFS library example to verify SPIFFS works! + +// The example image used to test this sketch can be found in the sketch +// Data folder, press Ctrl+K to see this folder. Use the IDE "Tools" menu +// option to upload the sketches data folder to the SPIFFS + +// This sketch ahs been tested on the ESP32 and ESP8266 + +//---------------------------------------------------------------------------------------------------- + +//==================================================================================== +// Libraries +//==================================================================================== +// Call up the SPIFFS FLASH filing system this is part of the ESP Core +#define FS_NO_GLOBALS +#include + +#ifdef ESP32 + #include "SPIFFS.h" // For ESP32 only +#endif + +// Call up the TFT library +#include // Hardware-specific library for ESP8266 + +// Invoke TFT library +TFT_eSPI tft = TFT_eSPI(); + +//==================================================================================== +// Setup +//==================================================================================== +void setup() +{ + Serial.begin(115200); + + if (!SPIFFS.begin()) { + Serial.println("SPIFFS initialisation failed!"); + while (1) yield(); // Stay here twiddling thumbs waiting + } + Serial.println("\r\nSPIFFS initialised."); + + // Now initialise the TFT + tft.begin(); + tft.setRotation(0); // 0 & 2 Portrait. 1 & 3 landscape + tft.fillScreen(TFT_BLACK); +} + +//==================================================================================== +// Loop +//==================================================================================== +void loop() +{ + int x = random(tft.width() - 128); + int y = random(tft.height() - 160); + + drawBmp("/parrot.bmp", x, y); + + delay(1000); +} +//==================================================================================== + diff --git a/examples/Generic/TFT_SPIFFS_BMP/data/parrot.bmp b/examples/Generic/TFT_SPIFFS_BMP/data/parrot.bmp new file mode 100644 index 0000000000000000000000000000000000000000..a98c4eb2542eb4374c02719d281f96069747df12 GIT binary patch literal 61496 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 literal 0 HcmV?d00001 From 1c48bf6f7e5c8b5a827da4c7e3faac82107ef1dd Mon Sep 17 00:00:00 2001 From: Bodmer Date: Sat, 19 May 2018 19:51:20 +0100 Subject: [PATCH 140/150] Correct mirror reported in #143 --- TFT_Drivers/ST7735_Init.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TFT_Drivers/ST7735_Init.h b/TFT_Drivers/ST7735_Init.h index 27b3253..7cda7f6 100644 --- a/TFT_Drivers/ST7735_Init.h +++ b/TFT_Drivers/ST7735_Init.h @@ -24,7 +24,7 @@ 0x03, // 3 lines back porch 10, // 10 ms delay ST7735_MADCTL , 1 , // 5: Memory access ctrl (directions), 1 arg: - 0x08, // Row addr/col addr, bottom to top refresh + 0x48, // Row addr/col addr, bottom to top refresh ST7735_DISSET5, 2 , // 6: Display settings #5, 2 args, no delay: 0x15, // 1 clk cycle nonoverlap, 2 cycle gate // rise, 3 cycle osc equalize From 002da5be9744a54f1d7236c76e5301bc0b352be7 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Sat, 19 May 2018 20:01:11 +0100 Subject: [PATCH 141/150] Correct mirroring reported in #143 --- TFT_Drivers/ST7735_Rotation.h | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/TFT_Drivers/ST7735_Rotation.h b/TFT_Drivers/ST7735_Rotation.h index 4a8bfdc..9f30bac 100644 --- a/TFT_Drivers/ST7735_Rotation.h +++ b/TFT_Drivers/ST7735_Rotation.h @@ -20,7 +20,9 @@ writedata(TFT_MAD_MX | TFT_MAD_MY | TFT_MAD_MH | TFT_MAD_BGR); colstart = 0; rowstart = 32; - } else { + } else if(tabcolor == INITB) { + writedata(TFT_MAD_MX | TFT_MAD_BGR); + } else { writedata(TFT_MAD_MX | TFT_MAD_MY | TFT_MAD_BGR); } _width = _init_width; @@ -41,7 +43,9 @@ writedata(TFT_MAD_MV | TFT_MAD_MY | TFT_MAD_BGR); colstart = 32; rowstart = 0; - } else { + } else if(tabcolor == INITB) { + writedata(TFT_MAD_MV | TFT_MAD_MX | TFT_MAD_MY | TFT_MAD_BGR); + } else { writedata(TFT_MAD_MY | TFT_MAD_MV | TFT_MAD_BGR); } _width = _init_height; @@ -62,7 +66,9 @@ writedata(TFT_MAD_BGR); colstart = 0; rowstart = 0; - } else { + } else if(tabcolor == INITB) { + writedata(TFT_MAD_MY | TFT_MAD_RGB); + } else { writedata(TFT_MAD_BGR); } _width = _init_width; @@ -83,7 +89,9 @@ writedata(TFT_MAD_MX | TFT_MAD_MV | TFT_MAD_BGR); colstart = 0; rowstart = 0; - } else { + } else if(tabcolor == INITB) { + writedata(TFT_MAD_MV | TFT_MAD_RGB); + } else { writedata(TFT_MAD_MX | TFT_MAD_MV | TFT_MAD_BGR); } _width = _init_height; From a3107d7ed28196b1117b6804771695bc1e9a1fe6 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Sun, 20 May 2018 22:31:54 +0100 Subject: [PATCH 142/150] RGB instead of BRG as updated in #143 --- TFT_Drivers/ST7735_Init.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TFT_Drivers/ST7735_Init.h b/TFT_Drivers/ST7735_Init.h index 7cda7f6..41a9c9d 100644 --- a/TFT_Drivers/ST7735_Init.h +++ b/TFT_Drivers/ST7735_Init.h @@ -24,7 +24,7 @@ 0x03, // 3 lines back porch 10, // 10 ms delay ST7735_MADCTL , 1 , // 5: Memory access ctrl (directions), 1 arg: - 0x48, // Row addr/col addr, bottom to top refresh + 0x40, // Row addr/col addr, bottom to top refresh ST7735_DISSET5, 2 , // 6: Display settings #5, 2 args, no delay: 0x15, // 1 clk cycle nonoverlap, 2 cycle gate // rise, 3 cycle osc equalize From af5a193badbbce4862edd02fdb8a8207954293e8 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Sun, 20 May 2018 22:33:43 +0100 Subject: [PATCH 143/150] Change to RGB as updated in #143 --- TFT_Drivers/ST7735_Rotation.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/TFT_Drivers/ST7735_Rotation.h b/TFT_Drivers/ST7735_Rotation.h index 9f30bac..b9476a1 100644 --- a/TFT_Drivers/ST7735_Rotation.h +++ b/TFT_Drivers/ST7735_Rotation.h @@ -21,7 +21,7 @@ colstart = 0; rowstart = 32; } else if(tabcolor == INITB) { - writedata(TFT_MAD_MX | TFT_MAD_BGR); + writedata(TFT_MAD_MX | TFT_MAD_RGB); } else { writedata(TFT_MAD_MX | TFT_MAD_MY | TFT_MAD_BGR); } @@ -44,7 +44,7 @@ colstart = 32; rowstart = 0; } else if(tabcolor == INITB) { - writedata(TFT_MAD_MV | TFT_MAD_MX | TFT_MAD_MY | TFT_MAD_BGR); + writedata(TFT_MAD_MV | TFT_MAD_MX | TFT_MAD_MY | TFT_MAD_RGB); } else { writedata(TFT_MAD_MY | TFT_MAD_MV | TFT_MAD_BGR); } From 4a52f5e6851875ddf64a4378e3404a058330e907 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Fri, 25 May 2018 22:48:15 +0100 Subject: [PATCH 144/150] Add ability to set ST7735 tab color from sketch Use line is form: tft.init(1); or: tft.init(INITR_REDTAB); // Enumerated the configurations in library are: #define INITR_GREENTAB 0x0 #define INITR_REDTAB 0x1 #define INITR_BLACKTAB 0x2 #define INITR_GREENTAB2 0x3 // Use if you get random pixels on two edges of green tab display #define INITR_GREENTAB3 0x4 // Use if you get random pixels on edge(s) of 128x128 screen #define INITR_GREENTAB128 0x5 // Use if you only get part of 128x128 screen in rotation 0 & 1 #define INITB 0xB --- TFT_Drivers/ST7735_Init.h | 2 -- TFT_eSPI.cpp | 13 ++++++++++--- TFT_eSPI.h | 6 +++++- examples/Generic/TFT_SPIFFS_BMP/TFT_SPIFFS_BMP.ino | 2 +- examples/Generic/drawXBitmap/drawXBitmap.ino | 2 +- .../Read_User_Setup/Read_User_Setup.ino | 2 +- 6 files changed, 18 insertions(+), 9 deletions(-) diff --git a/TFT_Drivers/ST7735_Init.h b/TFT_Drivers/ST7735_Init.h index 41a9c9d..0df152c 100644 --- a/TFT_Drivers/ST7735_Init.h +++ b/TFT_Drivers/ST7735_Init.h @@ -140,8 +140,6 @@ ST7735_DISPON , TFT_INIT_DELAY, // 4: Main screen turn on, no args w/delay 100 }; // 100 ms delay - tabcolor = TAB_COLOUR; - if (tabcolor == INITB) { commandList(Bcmd); diff --git a/TFT_eSPI.cpp b/TFT_eSPI.cpp index b5e2164..c13b596 100644 --- a/TFT_eSPI.cpp +++ b/TFT_eSPI.cpp @@ -209,9 +209,9 @@ TFT_eSPI::TFT_eSPI(int16_t w, int16_t h) ** Function name: begin ** Description: Included for backwards compatibility ***************************************************************************************/ -void TFT_eSPI::begin(void) +void TFT_eSPI::begin(uint8_t tc) { - init(); + init(tc); } @@ -219,7 +219,7 @@ void TFT_eSPI::begin(void) ** Function name: init ** Description: Reset, then initialise the TFT display registers ***************************************************************************************/ -void TFT_eSPI::init(void) +void TFT_eSPI::init(uint8_t tc) { #if !defined (ESP32) #ifdef TFT_CS @@ -307,6 +307,7 @@ void TFT_eSPI::init(void) #include "TFT_Drivers/ILI9341_Init.h" #elif defined (ST7735_DRIVER) + tabcolor = tc; #include "TFT_Drivers/ST7735_Init.h" #elif defined (ILI9163_DRIVER) @@ -318,6 +319,9 @@ void TFT_eSPI::init(void) #elif defined (RPI_ILI9486_DRIVER) #include "TFT_Drivers/RPI_ILI9486_Init.h" +#elif defined (ILI9486_DRIVER) + #include "TFT_Drivers/ILI9486_Init.h" + #elif defined (ILI9481_DRIVER) #include "TFT_Drivers/ILI9481_Init.h" @@ -359,6 +363,9 @@ void TFT_eSPI::setRotation(uint8_t m) #elif defined (RPI_ILI9486_DRIVER) #include "TFT_Drivers/RPI_ILI9486_Rotation.h" +#elif defined (ILI9486_DRIVER) + #include "TFT_Drivers/ILI9486_Rotation.h" + #elif defined (ILI9481_DRIVER) #include "TFT_Drivers/ILI9481_Rotation.h" diff --git a/TFT_eSPI.h b/TFT_eSPI.h index e17774f..86e7cf8 100644 --- a/TFT_eSPI.h +++ b/TFT_eSPI.h @@ -21,6 +21,10 @@ // available and the pins to be used #include +#ifndef TAB_COLOUR + TAB_COLOUR 0 +#endif + // If the frequency is not defined, set a default #ifndef SPI_FREQUENCY #define SPI_FREQUENCY 20000000 @@ -491,7 +495,7 @@ class TFT_eSPI : public Print { TFT_eSPI(int16_t _W = TFT_WIDTH, int16_t _H = TFT_HEIGHT); - void init(void), begin(void); // Same - begin included for backwards compatibility + void init(uint8_t tc = TAB_COLOUR), begin(uint8_t tc = TAB_COLOUR); // Same - begin included for backwards compatibility // These are virtual so the TFT_eSprite class can override them with sprite specific functions virtual void drawPixel(uint32_t x, uint32_t y, uint32_t color), diff --git a/examples/Generic/TFT_SPIFFS_BMP/TFT_SPIFFS_BMP.ino b/examples/Generic/TFT_SPIFFS_BMP/TFT_SPIFFS_BMP.ino index a40ce4c..df43a4d 100644 --- a/examples/Generic/TFT_SPIFFS_BMP/TFT_SPIFFS_BMP.ino +++ b/examples/Generic/TFT_SPIFFS_BMP/TFT_SPIFFS_BMP.ino @@ -8,7 +8,7 @@ // Data folder, press Ctrl+K to see this folder. Use the IDE "Tools" menu // option to upload the sketches data folder to the SPIFFS -// This sketch ahs been tested on the ESP32 and ESP8266 +// This sketch has been tested on the ESP32 and ESP8266 //---------------------------------------------------------------------------------------------------- diff --git a/examples/Generic/drawXBitmap/drawXBitmap.ino b/examples/Generic/drawXBitmap/drawXBitmap.ino index 8678c31..d43138e 100644 --- a/examples/Generic/drawXBitmap/drawXBitmap.ino +++ b/examples/Generic/drawXBitmap/drawXBitmap.ino @@ -7,7 +7,7 @@ // This example is part of the TFT_eSPI library: // https://github.com/Bodmer/TFT_eSPI -// Created by Bodmer 23/14/18 +// Created by Bodmer 23/04/18 #include "xbm.h" // Sketch tab header for xbm images diff --git a/examples/Test and diagnostics/Read_User_Setup/Read_User_Setup.ino b/examples/Test and diagnostics/Read_User_Setup/Read_User_Setup.ino index f326cad..45d48bf 100644 --- a/examples/Test and diagnostics/Read_User_Setup/Read_User_Setup.ino +++ b/examples/Test and diagnostics/Read_User_Setup/Read_User_Setup.ino @@ -8,7 +8,7 @@ verify the correct settings are being picked up by the compiler. If support is needed the output can be cut and pasted into an Arduino Forum post and - already inlcudes the formatting [code]...[/code] markups. + already includes the formatting [code]...[/code] markups. Written by Bodmer 9/4/18 */ From 96a4df721429569e8a4ed24c15edb7a89e40eb35 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Tue, 29 May 2018 00:24:20 +0100 Subject: [PATCH 145/150] Missing #define --- TFT_eSPI.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TFT_eSPI.h b/TFT_eSPI.h index 86e7cf8..75362a9 100644 --- a/TFT_eSPI.h +++ b/TFT_eSPI.h @@ -22,7 +22,7 @@ #include #ifndef TAB_COLOUR - TAB_COLOUR 0 + #define TAB_COLOUR 0 #endif // If the frequency is not defined, set a default From f2686a9024bd0d19c34f8ebff18b7913efaac10a Mon Sep 17 00:00:00 2001 From: Bodmer Date: Tue, 29 May 2018 22:06:14 +0100 Subject: [PATCH 146/150] Try alternative drawLine for HX8357D display as noted in issue #149 --- TFT_eSPI.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TFT_eSPI.cpp b/TFT_eSPI.cpp index c13b596..ac43339 100644 --- a/TFT_eSPI.cpp +++ b/TFT_eSPI.cpp @@ -3231,7 +3231,7 @@ void TFT_eSPI::pushColors(uint16_t *data, uint32_t len, bool swap) // Bresenham's algorithm - thx wikipedia - speed enhanced by Bodmer to use // an efficient FastH/V Line draw routine for line segments of 2 pixels or more -#if defined (RPI_ILI9486_DRIVER) || defined (ESP32) || defined (RPI_WRITE_STROBE) +#if defined (RPI_ILI9486_DRIVER) || defined (ESP32) || defined (RPI_WRITE_STROBE) || defined (HX8357D_DRIVER) void TFT_eSPI::drawLine(int32_t x0, int32_t y0, int32_t x1, int32_t y1, uint32_t color) { From 43abc3b9756079c44c36399a4164e4af9bc96284 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Fri, 1 Jun 2018 01:10:46 +0100 Subject: [PATCH 147/150] Update to clarify ILI9488 is supported in parallel mode In 4 wire SPI mode the ILI9488 is not supported as it needs 18 bit colours spread across 3 bytes. --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 54157c2..410f47d 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ # TFT_eSPI -An Arduino IDE compatible graphics and fonts library for ESP8266 and ESP32 processors with a driver for ILI9341, ILI9163, ST7735, S6D02A1, ILI9481, ILI9488, ILI9486 and HX8357 based TFT displays that support SPI. +An Arduino IDE compatible graphics and fonts library for ESP8266 and ESP32 processors with a driver for ILI9341, ILI9163, ST7735, S6D02A1, ILI9481, ILI9486 and HX8357 based TFT displays that support SPI. -8 bit parallel interface TFTs (e.g. UNO format mcufriend shields) can used with an ESP32. +8 bit parallel interface TFTs (e.g. UNO format mcufriend shields) can used with an ESP32. The ILI9488 is supported in 8 bit parallel mode. The library supports TFT displays designed for the Raspberry Pi that are based on a ILI9486 driver chip with a 480 x 320 pixel screen. This display must be of the Waveshare design and use a 16 bit serial interface based on the 74HC04, 74HC4040 and 2 x 74HC4094 logic chips. A modification to these displays is possible (see mod image in Tools folder) to make many graphics functions much faster (e.g. 23ms to clear the screen, 1.2ms to draw a 72 pixel high numeral). From 53119823f9849fd5796fa342912ba5b77e8ce6fa Mon Sep 17 00:00:00 2001 From: Bodmer Date: Fri, 1 Jun 2018 18:26:09 +0100 Subject: [PATCH 148/150] Add support for ST7789 display (240 x 240) --- TFT_Drivers/ST7789_Defines.h | 48 +++++++ TFT_Drivers/ST7789_Init.h | 25 ++++ TFT_Drivers/ST7789_Rotation.h | 26 ++++ TFT_eSPI.cpp | 6 + User_Setup.h | 1 + User_Setup_Select.h | 6 +- User_Setups/Setup18_ST7789.h | 252 ++++++++++++++++++++++++++++++++++ 7 files changed, 363 insertions(+), 1 deletion(-) create mode 100644 TFT_Drivers/ST7789_Defines.h create mode 100644 TFT_Drivers/ST7789_Init.h create mode 100644 TFT_Drivers/ST7789_Rotation.h create mode 100644 User_Setups/Setup18_ST7789.h diff --git a/TFT_Drivers/ST7789_Defines.h b/TFT_Drivers/ST7789_Defines.h new file mode 100644 index 0000000..b900b79 --- /dev/null +++ b/TFT_Drivers/ST7789_Defines.h @@ -0,0 +1,48 @@ +// Change the width and height if required (defined in portrait mode) +// or use the constructor to over-ride defaults +#define TFT_WIDTH 240 +#define TFT_HEIGHT 240 + + +// Delay between some initialisation commands +#define TFT_INIT_DELAY 0x80 // Not used unless commandlist invoked + + +// Generic commands used by TFT_eSPI.cpp +#define TFT_NOP 0x00 +#define TFT_SWRST 0x01 + +#define TFT_SLPIN 0x10 +#define TFT_SLPOUT 0x11 + +#define TFT_NORON 0x13 + +#define TFT_INVOFF 0x20 +#define TFT_INVON 0x21 + +#define TFT_DISPOFF 0x28 +#define TFT_DISPON 0x29 + +#define TFT_CASET 0x2A +#define TFT_PASET 0x2B +#define TFT_RAMWR 0x2C + +#define TFT_RAMRD 0x2E + +#define TFT_MADCTL 0x36 + +#define TFT_COLMOD 0x3A + +#define TFT_MAD_MY 0x80 +#define TFT_MAD_MX 0x40 +#define TFT_MAD_MV 0x20 +#define TFT_MAD_ML 0x10 +#define TFT_MAD_RGB 0x00 +#define TFT_MAD_BGR 0x08 +#define TFT_MAD_MH 0x04 +#define TFT_MAD_SS 0x02 +#define TFT_MAD_GS 0x01 + +#define TFT_IDXRD 0x00 // ILI9341 only, indexed control register read + + diff --git a/TFT_Drivers/ST7789_Init.h b/TFT_Drivers/ST7789_Init.h new file mode 100644 index 0000000..57b526b --- /dev/null +++ b/TFT_Drivers/ST7789_Init.h @@ -0,0 +1,25 @@ + +// This is the command sequence that initialises the ST7789 driver + +// Configure ST7789 display +{ +static const uint8_t PROGMEM + st7789[] = { + 9, + TFT_SWRST, TFT_INIT_DELAY, 150, + TFT_SLPOUT, TFT_INIT_DELAY, 255, + TFT_COLMOD, 1+TFT_INIT_DELAY, 0x55, 10, + TFT_MADCTL, 1, 0x00, + TFT_CASET, 4, 0x00, 0x00, 0x00, 0xF0, + TFT_PASET, 4, 0x00, 0x00, 0x00, 0xF0, + TFT_INVON, TFT_INIT_DELAY, 10, + TFT_NORON, TFT_INIT_DELAY, 10, + TFT_DISPON, TFT_INIT_DELAY, 255 + }; + + commandList(st7789); +} +// End of ST7789 display configuration + + + diff --git a/TFT_Drivers/ST7789_Rotation.h b/TFT_Drivers/ST7789_Rotation.h new file mode 100644 index 0000000..3a75ec6 --- /dev/null +++ b/TFT_Drivers/ST7789_Rotation.h @@ -0,0 +1,26 @@ + // This is the command sequence that rotates the ST7789 driver coordinate frame + + writecommand(TFT_MADCTL); + rotation = m % 4; + switch (rotation) { + case 0: // Portrait + writedata(TFT_MAD_MX | TFT_MAD_MY | TFT_MAD_RGB); + _width = TFT_WIDTH; + _height = TFT_HEIGHT; + break; + case 1: // Landscape (Portrait + 90) + writedata(TFT_MAD_MV | TFT_MAD_MY | TFT_MAD_RGB); + _width = TFT_HEIGHT; + _height = TFT_WIDTH; + break; + case 2: // Inverter portrait + writedata(TFT_MAD_RGB); + _width = TFT_WIDTH; + _height = TFT_HEIGHT; + break; + case 3: // Inverted landscape + writedata(TFT_MAD_MX | TFT_MAD_MV | TFT_MAD_RGB); + _width = TFT_HEIGHT; + _height = TFT_WIDTH; + break; + } diff --git a/TFT_eSPI.cpp b/TFT_eSPI.cpp index ac43339..b63a1bc 100644 --- a/TFT_eSPI.cpp +++ b/TFT_eSPI.cpp @@ -331,6 +331,9 @@ void TFT_eSPI::init(uint8_t tc) #elif defined (HX8357D_DRIVER) #include "TFT_Drivers/HX8357D_Init.h" +#elif defined (ST7789_DRIVER) + #include "TFT_Drivers/ST7789_Init.h" + #endif spi_end(); @@ -375,6 +378,9 @@ void TFT_eSPI::setRotation(uint8_t m) #elif defined (HX8357D_DRIVER) #include "TFT_Drivers/HX8357D_Rotation.h" +#elif defined (ST7789_DRIVER) + #include "TFT_Drivers/ST7789_Rotation.h" + #endif delayMicroseconds(10); diff --git a/User_Setup.h b/User_Setup.h index fc2bb2b..782b71d 100644 --- a/User_Setup.h +++ b/User_Setup.h @@ -23,6 +23,7 @@ //#define HX8357D_DRIVER //#define ILI9481_DRIVER //#define ILI9488_DRIVER +//#define ST7789_DRIVER // For M5Stack ESP32 module with integrated display ONLY, remove // in line below //#define M5STACK diff --git a/User_Setup_Select.h b/User_Setup_Select.h index 97db56e..fe14441 100644 --- a/User_Setup_Select.h +++ b/User_Setup_Select.h @@ -37,7 +37,8 @@ //#include // Setup file for the ESP32 with parallel bus TFT //#include // Setup file configured for HX8357D (untested) //#include // Setup file for the ESP32 with parallel bus TFT -//#include // Setup file for any Waveshare ePaper display +//#include // Setup file for any Waveshare ePaper display +//#include // Setup file configured for HX8357D (untested) //#include @@ -82,6 +83,9 @@ #elif defined (EPD_DRIVER) #include "TFT_Drivers/EPD_Defines.h" #define TFT_DRIVER 0xE9D +#elif defined (ST7789_DRIVER) + #include "TFT_Drivers/ST7789_Defines.h" + #define TFT_DRIVER 0x7789 #elif defined (XYZZY_DRIVER) // <<<<<<<<<<<<<<<<<<<<<<<< ADD NEW DRIVER HERE #include "TFT_Drivers/XYZZY_Defines.h" #define TFT_DRIVER 0x0000 diff --git a/User_Setups/Setup18_ST7789.h b/User_Setups/Setup18_ST7789.h new file mode 100644 index 0000000..d532d0a --- /dev/null +++ b/User_Setups/Setup18_ST7789.h @@ -0,0 +1,252 @@ +// USER DEFINED SETTINGS +// Set driver type, fonts to be loaded, pins used and SPI control method etc +// +// See the User_Setup_Select.h file if you wish to be able to define multiple +// setups and then easily select which setup file is used by the compiler. +// +// If this file is edited correctly then all the library example sketches should +// run without the need to make any more changes for a particular hardware setup! +// Note that some sketches are designed for a particular TFT pixel width/height + +// ################################################################################## +// +// Section 0. Call up the right driver file and any options for it +// +// ################################################################################## + +// Only define one driver, the other ones must be commented out +//#define ILI9341_DRIVER +//#define ST7735_DRIVER +//#define ILI9163_DRIVER +//#define S6D02A1_DRIVER +//#define RPI_ILI9486_DRIVER // 20MHz maximum SPI +//#define HX8357D_DRIVER +//#define ILI9481_DRIVER +//#define ILI9488_DRIVER +#define ST7789_DRIVER + +// For M5Stack ESP32 module with integrated display ONLY, remove // in line below +//#define M5STACK + +// For ST7735 and ILI9163 ONLY, define the pixel width and height in portrait orientation +//#define TFT_WIDTH 128 +//#define TFT_HEIGHT 160 +//#define TFT_HEIGHT 128 + +// For ST7735 ONLY, define the type of display, originally this was based on the +// colour of the tab on the screen protector film but this is not always true, so try +// out the different options below if the screen does not display graphics correctly, +// e.g. colours wrong, mirror images, or tray pixels at the edges. +// Comment out ALL BUT ONE of these options for a ST7735 display driver, save this +// this User_Setup file, then rebuild and upload the sketch to the board again: + +//#define ST7735_INITB +//#define ST7735_GREENTAB +//#define ST7735_GREENTAB2 +//#define ST7735_GREENTAB3 +//#define ST7735_GREENTAB128 // For 128 x 128 display +//#define ST7735_REDTAB +//#define ST7735_BLACKTAB + +// ################################################################################## +// +// Section 1. Define the pins that are used to interface with the display here +// +// ################################################################################## + +// We must use hardware SPI, a minimum of 3 GPIO pins is needed. +// Typical setup for ESP8266 NodeMCU ESP-12 is : +// +// Display SDO/MISO to NodeMCU pin D6 (or leave disconnected if not reading TFT) +// Display LED to NodeMCU pin VIN (or 5V, see below) +// Display SCK to NodeMCU pin D5 +// Display SDI/MOSI to NodeMCU pin D7 +// Display DC (RS/AO)to NodeMCU pin D3 +// Display RESET to NodeMCU pin D4 (or RST, see below) +// Display CS to NodeMCU pin D8 (or GND, see below) +// Display GND to NodeMCU pin GND (0V) +// Display VCC to NodeMCU 5V or 3.3V +// +// The TFT RESET pin can be connected to the NodeMCU RST pin or 3.3V to free up a control pin +// +// The DC (Data Command) pin may be labeled AO or RS (Register Select) +// +// With some displays such as the ILI9341 the TFT CS pin can be connected to GND if no more +// SPI devices (e.g. an SD Card) are connected, in this case comment out the #define TFT_CS +// line below so it is NOT defined. Other displays such at the ST7735 require the TFT CS pin +// to be toggled during setup, so in these cases the TFT_CS line must be defined and connected. +// +// The NodeMCU D0 pin can be used for RST +// +// See Section 2. below if DC or CS is connected to D0 +// +// Note: only some versions of the NodeMCU provide the USB 5V on the VIN pin +// If 5V is not available at a pin you can use 3.3V but backlight brightness +// will be lower. + + +// ###### EDIT THE PIN NUMBERS IN THE LINES FOLLOWING TO SUIT YOUR ESP8266 SETUP ###### + +// For NodeMCU - use pin numbers in the form PIN_Dx where Dx is the NodeMCU pin designation +#define TFT_CS PIN_D8 // Chip select control pin D8 +#define TFT_DC PIN_D3 // Data Command control pin +#define TFT_RST PIN_D4 // Reset pin (could connect to NodeMCU RST, see next line) +//#define TFT_RST -1 // Set TFT_RST to -1 if the display RESET is connected to NodeMCU RST or 3.3V + +//#define TOUCH_CS PIN_D2 // Chip select pin (T_CS) of touch screen + +//#define TFT_WR PIN_D2 // Write strobe for modified Raspberry Pi TFT only + + +// ###### FOR ESP8266 OVERLAP MODE EDIT THE PIN NUMBERS IN THE FOLLOWING LINES ###### + +// Overlap mode shares the ESP8266 FLASH SPI bus with the TFT so has a performance impact +// but saves pins for other functions. +// Use NodeMCU SD0=MISO, SD1=MOSI, CLK=SCLK to connect to TFT in overlap mode + +// In ESP8266 overlap mode the TFT chip select MUST connect to pin D3 +//#define TFT_CS PIN_D3 +//#define TFT_DC PIN_D5 // Data Command control pin +//#define TFT_RST PIN_D4 // Reset pin (could connect to NodeMCU RST, see next line) +//#define TFT_RST -1 // Set TFT_RST to -1 if the display RESET is connected to NodeMCU RST or 3.3V + +// In ESP8266 overlap mode the following must be defined +//#define TFT_SPI_OVERLAP + +// ###### EDIT THE PIN NUMBERS IN THE LINES FOLLOWING TO SUIT YOUR ESP32 SETUP ###### + +// For ESP32 Dev board (only tested with ILI9341 display) +// The hardware SPI can be mapped to any pins + +//#define TFT_MISO 19 +//#define TFT_MOSI 23 +//#define TFT_SCLK 18 +//#define TFT_CS 15 // Chip select control pin +//#define TFT_DC 2 // Data Command control pin +//#define TFT_RST 4 // Reset pin (could connect to RST pin) +//#define TFT_RST -1 // Set TFT_RST to -1 if display RESET is connected to ESP32 board RST + +// For the M5Stack module use these #define lines +//#define TFT_MISO 19 +//#define TFT_MOSI 23 +//#define TFT_SCLK 18 +//#define TFT_CS 14 // Chip select control pin +//#define TFT_DC 27 // Data Command control pin +//#define TFT_RST 33 // Reset pin (could connect to Arduino RESET pin) +//#define TFT_BL 32 // LED back-light + +//#define TOUCH_CS 21 // Chip select pin (T_CS) of touch screen + +//#define TFT_WR 22 // Write strobe for modified Raspberry Pi TFT only + +// ###### EDIT THE PINs BELOW TO SUIT YOUR ESP32 PARALLEL TFT SETUP ###### + +// The library supports 8 bit parallel TFTs with the ESP32, the pin +// selection below is compatible with ESP32 boards in UNO format. +// Wemos D32 boards need to be modified, see diagram in Tools folder. +// Only ILI9481 and ILI9341 based displays have been tested! + +// Parallel bus is only supported on ESP32 +// Uncomment line below to use ESP32 Parallel interface instead of SPI + +//#define ESP32_PARALLEL + +// The ESP32 and TFT the pins used for testing are: +//#define TFT_CS 33 // Chip select control pin (library pulls permanently low +//#define TFT_DC 15 // Data Command control pin - use a pin in the range 0-31 +//#define TFT_RST 32 // Reset pin, toggles on startup + +//#define TFT_WR 4 // Write strobe control pin - use a pin in the range 0-31 +//#define TFT_RD 2 // Read strobe control pin - use a pin in the range 0-31 + +//#define TFT_D0 12 // Must use pins in the range 0-31 for the data bus +//#define TFT_D1 13 // so a single register write sets/clears all bits. +//#define TFT_D2 26 // Pins can be randomly assigned, this does not affect +//#define TFT_D3 25 // TFT screen update performance. +//#define TFT_D4 17 +//#define TFT_D5 16 +//#define TFT_D6 27 +//#define TFT_D7 14 + +// ################################################################################## +// +// Section 2. Define the way the DC and/or CS lines are driven (ESP8266 only) +// +// ################################################################################## + +// Normally the library uses direct register access for the DC and CS lines for speed +// If D0 (GPIO16) is used for CS or DC then a different slower method must be used +// Uncomment one line if D0 is used for DC or CS +// DC on D0 = 6% performance penalty at 40MHz SPI running graphics test +// CS on D0 = 2% performance penalty at 40MHz SPI running graphics test + +// #define D0_USED_FOR_DC +// #define D0_USED_FOR_CS + +// ################################################################################## +// +// Section 3. Define the fonts that are to be used here +// +// ################################################################################## + +// Comment out the #defines below with // to stop that font being loaded +// The ESP8366 and ESP32 have plenty of memory so commenting out fonts is not +// normally necessary. If all fonts are loaded the extra FLASH space required is +// about 17Kbytes. To save FLASH space only enable the fonts you need! + +#define LOAD_GLCD // Font 1. Original Adafruit 8 pixel font needs ~1820 bytes in FLASH +#define LOAD_FONT2 // Font 2. Small 16 pixel high font, needs ~3534 bytes in FLASH, 96 characters +#define LOAD_FONT4 // Font 4. Medium 26 pixel high font, needs ~5848 bytes in FLASH, 96 characters +#define LOAD_FONT6 // Font 6. Large 48 pixel font, needs ~2666 bytes in FLASH, only characters 1234567890:-.apm +#define LOAD_FONT7 // Font 7. 7 segment 48 pixel font, needs ~2438 bytes in FLASH, only characters 1234567890:-. +#define LOAD_FONT8 // Font 8. Large 75 pixel font needs ~3256 bytes in FLASH, only characters 1234567890:-. +//#define LOAD_FONT8N // Font 8. Alternative to Font 8 above, slightly narrower, so 3 digits fit a 160 pixel TFT +#define LOAD_GFXFF // FreeFonts. Include access to the 48 Adafruit_GFX free fonts FF1 to FF48 and custom fonts + +// Comment out the #define below to stop the SPIFFS filing system and smooth font code being loaded +// this will save ~20kbytes of FLASH +#define SMOOTH_FONT + +// ################################################################################## +// +// Section 4. Not used +// +// ################################################################################## + + +// ################################################################################## +// +// Section 5. Other options +// +// ################################################################################## + +// Define the SPI clock frequency, this affects the graphics rendering speed. Too +// fast and the TFT driver will not keep up and display corruption appears. +// With an ILI9341 display 40MHz works OK, 80MHz sometimes fails +// With a ST7735 display more than 27MHz may not work (spurious pixels and lines) +// With an ILI9163 display 27 MHz works OK. +// The RPi typically only works at 20MHz maximum. + +// #define SPI_FREQUENCY 1000000 +// #define SPI_FREQUENCY 5000000 +// #define SPI_FREQUENCY 10000000 +// #define SPI_FREQUENCY 20000000 +#define SPI_FREQUENCY 27000000 // Actually sets it to 26.67MHz = 80/3 +// #define SPI_FREQUENCY 40000000 // Maximum to use SPIFFS +// #define SPI_FREQUENCY 80000000 + +// The XPT2046 requires a lower SPI clock rate of 2.5MHz so we define that here: +#define SPI_TOUCH_FREQUENCY 2500000 + + +// Comment out the following #define if "SPI Transactions" do not need to be +// supported. When commented out the code size will be smaller and sketches will +// run slightly faster, so leave it commented out unless you need it! + +// Transaction support is needed to work with SD library but not needed with TFT_SdFat +// Transaction support is required if other SPI devices are connected. + +// Transactions are automatically enabled by the library for an ESP32 (to use HAL mutex) +// so changing it here has no effect + +//#define SUPPORT_TRANSACTIONS From 98c51c57a67b6ad62ac749cfdcd8f7a6652dccec Mon Sep 17 00:00:00 2001 From: Bodmer Date: Fri, 1 Jun 2018 18:28:48 +0100 Subject: [PATCH 149/150] Raise version and update ReadMe --- README.md | 2 +- library.json | 4 ++-- library.properties | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 410f47d..00e3023 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # TFT_eSPI -An Arduino IDE compatible graphics and fonts library for ESP8266 and ESP32 processors with a driver for ILI9341, ILI9163, ST7735, S6D02A1, ILI9481, ILI9486 and HX8357 based TFT displays that support SPI. +An Arduino IDE compatible graphics and fonts library for ESP8266 and ESP32 processors with a driver for ILI9341, ILI9163, ST7735, S6D02A1, ILI9481, ILI9486, HX8357D and ST7789 based TFT displays that support SPI. 8 bit parallel interface TFTs (e.g. UNO format mcufriend shields) can used with an ESP32. The ILI9488 is supported in 8 bit parallel mode. diff --git a/library.json b/library.json index 4c6257c..a8f6f7f 100644 --- a/library.json +++ b/library.json @@ -1,7 +1,7 @@ { "name": "TFT_eSPI", - "version": "0.20.14", - "keywords": "tft, ePaper, display, ESP8266, NodeMCU, ESP32, M5Stack, ILI9341, ST7735, ILI9163, S6D02A1, ILI9486", + "version": "0.20.15", + "keywords": "tft, ePaper, display, ESP8266, NodeMCU, ESP32, M5Stack, ILI9341, ST7735, ILI9163, S6D02A1, ILI9486, ST7789", "description": "A TFT and ePaper SPI graphics library for ESP8266 and ESP32", "repository": { diff --git a/library.properties b/library.properties index b3049e5..a2a4eb1 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=TFT_eSPI -version=0.20.14 +version=0.20.15 author=Bodmer maintainer=Bodmer sentence=A fast TFT library for ESP8266 processors and the Arduino IDE From 30c01c94ef88c21e8abce0e29b3d4c4c135d5324 Mon Sep 17 00:00:00 2001 From: per1234 Date: Sat, 2 Jun 2018 05:41:08 -0700 Subject: [PATCH 150/150] Rename file to match #include directive Mismatch of case between filename and #include directive causes compilation to fail in case sensitive operating systems such as Linux. Rather than correct the filename in the #include directive I have chosen to change the filename to make it consistent with the other file of the similar name. A search indicates that this is also consistent with the official spelling of the font. --- .../{ArialRoundedMtBold_14.h => ArialRoundedMTBold_14.h} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename examples/320 x 240/weather-station/{ArialRoundedMtBold_14.h => ArialRoundedMTBold_14.h} (100%) diff --git a/examples/320 x 240/weather-station/ArialRoundedMtBold_14.h b/examples/320 x 240/weather-station/ArialRoundedMTBold_14.h similarity index 100% rename from examples/320 x 240/weather-station/ArialRoundedMtBold_14.h rename to examples/320 x 240/weather-station/ArialRoundedMTBold_14.h

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 z zg_vYlgqJpa07W=3u=uwF(u;m8C@H@4{O!o?$9JA0N9Z_f%+cI}TUhqEjsG*&x6fZ$ zk)cT`p>`eA$j-``fl{|?)xnP%qDQhEZZlk-_665RDVMqJb<^K=?7#s#b!9sP10xg1 z$B&-(J$ijEr0{ZVxp4;5v1%ZU*%#H?u}fLy^ubdGng#~ihN@ax+FJU0x<=Ua^^7$& zbak{1v^4c_6`P@fse!(!u8t9yhPsZfjv)-g7s6l|04*?vs6v6d(idnN@6Pp6iz*g zer~L`rg~tosO-`ZzaaRg-G2Ygo6_k>?so&OP};p{JwTBOt7vB%i~-(2BR>2Hz=O#& z6-DeuY&aGWNQUu-fk%{4FN{P~BlZG?JQz`s;MwF_P%tP!3ur$42*{_(LHM+2%_k79 z3VAqvsgtWiagZNv!rKXBe6dkGPA_{Y>mtujg9TCOf9u^O*C~(^>p+N^^Gp6>8olOT3OlHTRE5$hw+-4SzB2< z*x9?-**aU8+Nfxn>KR&`I(264+TWJ1S-)`A@A8Y*NY7m^uDDoOW}c9=666As=sZF} zv_MpmM@n&)h$OGLthk(#q@0qNv?AnkbCv)Xp)4vpM^ZvTKvas4G>2Ds>9nQW0EBX< zjP{(=6cbv&v3L*1ULB527ia#Z!nId__HMmdyL6`Q)}C?1c=~b6X-6!l?=@Jd?PH!% z99>%f?%P;%d)vIFo8;y#tD=^C`SASf>qqaN-+le^)<8qK!SP*lr6rf|Jb3s0$L}mw zY5l}L;I@_26m2zb{x>s(7mjxsj8_1%mcOo)zmI)K>-?- zFJ6WmTEGBukOSv1;lN0$fhe5g@9sp{@%wl981c5*A(l z+aHk$$%BvH1eG;87Bv9~or`Pj^6TjXcZTmgwe?Ks%nf+h9@a}ODGAiSUg=O5r5mET zy*0!2%Rtn%Jacm3p_4lnPveo=wC7krU|4uu`rVs1K=r0~-na1%DvWf!f3^C_NcH^z z#`XT1p^mchO#hPyH!NSVC_E?(UW&Iqeg_yhl>@Gjrw&E06FkGH*0cw z2rWIhDkr=o)t{E+;dFM}A`RnY*E2RX11@P zgToKsc&0HrC~-acW+N4;uUnE{H-$W+``xb${?MEAx;tzz*OC%?;>>~HW=pQvq3h(A zQsNL0e|>o9^Y_t&$}S^wtD<0=&dQXg{K$r!h{nPzLyRm&M&Pk+>(;GW9TyY-mBkwV zFbcxR9i7I|i9*?Eh;0~Dh(k1Vj31&P4S6HMpdFY5AcS5R1}#uv8z73!NIV!CA;;Q)J~)&0Oi%sku( zwDTbrd}8=P8Cuv~U|2!oSU}K)XdMlsAMWW-MqbT{B`uSyV2}};eB$7D95kI}R2yut zt#N`Bf;$8&!QC~uySo&pXp1|+i&NaaIK{O%6n7|4C=@O34kzC^_x{X3Ru+?YX3yTw z?4uGRhH=>m6Qf+>L|9xzRwx{^AR_zYfk9!|YSr(NftC-%^3L$U2tBXH^Z~dNxERnA zjeTz6j;=Z7^S2#6V=DDkKsN_U8zAm=SEX?GWAtP$oTO;}!Y3}?k|G9PF#?GUGXuRu z-_in($ano_#LxEsjX0D4tFT=bg?bDsc+d^*$;XT(d0Wf0Nrw;de-y58IkmL2#9%iL zpdH89XPZjF#oOM(!P_RnBLtycghiuaBtenFACNt%L>#RWT1K8vS~p?a8CKz-%=Lbq z7^NiyESV4ch!6FI(t`Esl5Q;(EOi-O!O*VY3MBUY#DaqQ3JwK*b?H<(Hrg6cv4EPS zv95%&tg%}rwBNUO^A-WHJDqhbSge9;AYirIXODU#$j2N;v$NtIxcA5{ax~27 zl>2ES>a9(1ggRDbZ(arI8jvA?J zrMVai&#}5L-B$;{8#U6@#CV18#y}5JElxC#ePT7g#rsk}vLsR*5&zbbWKJ;&~hD=P~dR#9$uZ3n42kjicQC2#bzw><}hKAd4dEaZ%5~Z!15+lZ4gfj#;k3D}$d{uR=L< zWe2m9$Bl^cuZ)v-LNr zT`y5UDobip$nMq#nxWvvX19kl(PAwOb;4NnNCT%cAWK$oy{qNRKAph}li>^(p(Tcs zxzWr{%WAJkFR^C`4Lca;Jl+tX#)t_w6+-ys8^xDel3>S|aiicMVsG;qUL840Q(WO% z6{l_#(BPEXC_Hrb+L;3wKHF4#t{_2!uDJ%EI1@!c9XwO!FTE^`%-r9^Z2y}Awwo!s z9zmrpi= zi2)8h!3$%VRbHxVt_rs$TRm64T$;=R%M{sxqrq0=^zk?Qw$)-&-A+FaD*rmFtz=0j z!j2{L6F+%4Et}ZlZ)p#@j#nrbwAjpNtK}^l(6MJz{*#?ngF{cVg)ZuT;3XI_%>7j% zy-k#4Mg=6f`-8z;*25|)Zc!pKwfh`D9u(6ucwkaj?+`u9=q0+5S+bd3rgAI%!Q1T! za}GVEyE1EbsMhC3FJI)$?|OwV`&4sjTlTbmjO8qg~~QY41eu6nDCBh8AG_CGK{c5EGFD(9UYp)NrGJRGUW!b1oG%DlKg75GYW?SleoqIQn4V?_(XcQv zA<|6u_Wv3j99|lpU79k0S;$U`e7149;FR3l+&DQk+}x7uE23cTm5h@0$4S)dxN1lz zc+v>O#K_F)m6WlKklgQoj58Diq)p7H!yaa38E2(q1v4?zF#Q~-9sT)pa&i&_e`o72 zGH7a#XcsSa2S05OKbPCc?&A8ZaQT~?!ehn8Mx)Sn$q=_L#r8_M@W)23ku2`6Dz#{q z-|PZjsGD6c3||oD4n(schmD>G6M8ppY_+$eHJ=sh&{+!dwYX7%p)}Qu%4OClA#5c? zWjsVO!lvusp&@EPm&QV?Pj6xS4LWfof*0KnQx|-;R|qODx~Sscu({!sN|rc#|_Cn>9tb&A4BD-r+!s42xqsR_wo`oy#AZ&&52c>^2T#9?Ka zItz;n%^{%$fOnXax2M8LG3v&K5mtNEc(zlw>;A-=bAJ+zj&x>^HRk?ku4vEAdAyvC z2NzAL`riIlF5Upg7LuntS)b~8O!``C`{Fg{FAD#ztFJ;6E%7||!n#;=oC0^+B)?o% z%2D<>bUpoKiY_cKgj{e_IeT|CZoTE zA_u^Qq`pc#0{8G>|7P!qfRXQ!$;qtn_`Yof`+L)AZ!rOVynnyX14uUpOT+s~_Z7rY zK_SEw#Bluc2!QwV-edo=f%-U|AYg}< zff@sRk}L%3aO0Z@W5yk4!|A?}Kv%la^krtDU*Tl%!$KD6b}~XiuW)`_i`{lMNgHv5 z;hI8lX`G8Mb|`M{wK$BDO+fs*YDlbj7_%Xq3&0PvfF<#>jqK?qvlpB^ zi=wwwTZy(ypC~EW?yZ8(W-*3!*&BCpka!K@vN)3*y zlaC61kz36Wl&jr{DBX#1rWT6l*lJUFuWzrLNpg+1x8`ZS8*`nmCF^)=I+{Z4Bu5b{ zh+I0!QuD%uUBCY6!Ox%Yo2)gby8?kuIPeT^XxhU#hbc@7=OU{ zN}R!S+lg9IBwfG!&g@-%mdyhoN(b#9a!rsug&%R_o*G#rlVK_O>r-xWO3hrder#qc zV_ucpJDDf74r?7*9Uilw>o0*Xr7~OtV-sCGy)AXrRfPiI%Q!0IbNp+Hn|gR0&W2^U zlKU%#_FD85O-2LGAmQNi41ObwuLb$c+Wh31=uZo?WUGUs9ex@$iOYLWP6l1KiN zuMMgK!R-Hbj|5ZcC{}t0>;`oXkfrjW99`)6C1k|-D-kb6`s|@wXp(T0f8LG35|=CG z=!jhm(*>w#07)RafIbioE!}(>+>7Z70DYLo>8~+|ivV|=d{7qs^A5zn1sN)zm=}VF z!ilhuG^i1T0jV3S31r3)Co17;0e%{TUqHdYv6VM85L9*t2U%IS$#U6F!rsc<0Pi2N ztmY~NM^>}5o@U`jRe_E5j@3yeUIhiXmzYTmjeExntAmvP(0ti=I4@yoeilH_#of36 za?4?{*udta83S%+Ju6Y#>tWuO;PkV(@YoW%68a%}=zUwet$VQBWKQ3}00Z0mfcvJ7 z?ndmcnf-}e1~4@}n01<=6a=mTv>vuJ5EAD=1@D+IUy|FP zEI$&7^Fh&O(m5&l`Eu&`xyE1?ekJ|*6rOx7yN3x_hF>>VN5rD(tf>XAZu^=5)rN8= zzu|&rWK643FE`!L&+yY>5{V)zE3G0^r%R=`^z=$u<4=**@}e>3{KG_}eLc-`i-UTL zUtLI4N82SCIpFDI;{2k8w$*35P9-wB2Z0Z1$Fpcr?Y9ReG~I$i{51Tlp#EFS@8zmG zT|*KbGDb!Aaz%Y{npL$Tzhi=5;z>K(=!m9Py|+WnI$RW<7Drq4W8-jB6%J5 z8h5EU=<>a}XzblC!&3am-QlQKsbyAsarKz^)D%2L9M^~ccb`?Pb)3Rc61#%pDNf4r zBx~Rwo1pSr^1mchFH#q=<``HemuKv}@zlBGj-La?FdMa_!`CQVksCMubc2z-bEbEY zFJ_(c*CjA~iGV9$JUc`g6xJ2}^0VGYMQ{zf5NC3NdO%ysx3&lo9PSg5geKS%qoSg$ zPK+klk@(zN0KGVlmc3Phb0_w@kI0Bp@1;VdM4da&heTC@Q!VFIVytP(C{p`4o)cHI zjRg<|jiV4b3eXWHh(bN3wIWja3P65~;2DhR{c!HbhJr!CQI(wl88%&>w@8p&K1Ul+ zpIGV)aR5taG@1m$gUKv{PpdIcWW*0N69$eP$Ci4Ha6Q%2e4!a@SnTG&R716t0k9xx z01yjE!@8PtId!^W(pRnMSX{5>6uP+{p4Ve%LwuT#d_f^NVv!yxeXJvvX!m^16zO zvfA3I`T6N79!tl+3IGry^r+aMC!*gRoOY31{?W&#p~uJrlxWN5$oG!;MSY z$;WS9t3^w_jf37uK8n2x&Zkl2v>Tx^_KQzV8@ogOewSu4Pk+?%Qmt{iRb^hcrxXZ? z-10qgJxkVI`$D=|B=k0+$lW=U`@$m#6g>FLM!``P4cMbgRo?FK{K z<+mI28r}-wyVBg~tc~0zmSHsx7&XqzZ42QC>E3xKY>fx(T&2jTjSKfyE$fQ$ABnUr z-Mhxf5^1`g2AL8p%$ELWEZOI(UR)NER9!eWj5_xx!^mjux5w3PtMqVOS=lq{u8o%_OLmtU7CmYF zjv+6yu@h#O_m8;;v7t&nW0|8tPpsK?W>quv;zi`t7So`|)4o=A=gi&nwyE_BwZGLa zcq2jsW?IFFqTO=0lm;SR3c=}{D+jH8{AMPKYu$>%h)N-x0X^F019Ytb0r-$!S9tg8 z;dEdH(Itz6C~Q3VUqf7wt=rJ8 zFn66A1io$b&U}Ofa{6lABWWPNS7E!Cb`+bfwIB4fTbVJYaPu?=W76&+zume@wpS8~ zA;zTFN$()4QxD0W1S9y@C6c9Nz*yw{`ZsVLNHg**gffo_!Is81aCuyVyTnv8;7JLX{yB= zNuxtSuurPddeSg;7Zo4YmEqtg;UsG(Cngc4W{flUK+XJLCw89%mU49k%3AkJP}uXg zuXiJDPG)m^6f&&_kf%1k3Z~`n{iwU|++Cez;S+Pje% zYc-^U2%P5|x0OD+#1QL*{rN-YbxGEeAJx^|3d{RK9>3j?*jgM5`WvtOu77P0GeUL4 znRDB6R82n%EP+CT0;j8uMk+FD1S_TX-U$LbidhCsl)3Ipm&DKC9 zL$Ou9AIjq$rSPOwGIl~tq|4%D!of9K{-x@VORS&*(oi6}y#xnsMkFj$3`elWfzYsJ zVNPu2O1%_H427fP10?Z(ox_8hrtvS#(1Ad+aYVh`WiZiE2T`dl;&EG%Sr2rU5I}<_ zpx^bc1Nca$p%#R=2~ydXR;cf9jd0;HgQibpI377Ma467B`{m)NcSfreUi`i>M9_G~ z(2$Hsy93!E-GlwA3rZqHAE@F1nE4{=L{Wh5-g;shAf-Sg;SadCD6VssP}fLFl3^&0 zaD@dbV-w;Q2V)`JiMjk6r*8psNfEMpHU8H7kkU8{f)o3=-+R_lzRuCnyQRcX(f>rO z>+81AW6bM(?1=xiAl3gVS;%NTgQHS0FhI4{niUGOnc)dQ5ckpbQjQ zW*3`6tEI_pk(Xy~mY0XOK|^a%JBwKk7YC0)p_f?00tYs^<4|)##5B;7CqnQl<7)$n z*eh!F8ypT}nmy90(=-iblg+@A((`|BxK`kx#+G))8tTyg?YS#LwiF#hCUEU2Ox}Xw zE^)phfs0bWgY<1OAKJo+&Qta#4I^NG8&Coas^GC2vVf!3gpz%Qamcb86mtEl_(mQ* z$dA-6PZ#k8?L|MfUf`Ngb+6qq--IEoKzr^;JNzJ8m6u)y^i?2Mk4{zd9)}i#B z-e>h+bWCg`wC$eLZgKS`+o;&4O`E=UnOZ06OUHVmJBd6O$%`| z3;B7!9z2-fB&}#MM%hiDfapKyI=zLLGQ1K6Eot!AmgspRZnq0RJwWfM z#RJf1alW{w{4>^_N0Djs+N4HPu(u}>4CGWeKn2RC7_G;U!a-s1>)P3S=?d9qTy<2}I-8ksQPzm|CNrZNg*P-5p_RuRT#Gx%|(sCu}su{1M3y+MEA z?X_cAf*qQJ5Ml8HEDO3-vs_TI6ASfm%3ejL+oRs2EWwVTKTD z;ArGrJiFXReVE>Y#Ov10Ji(a< z3yl^|)Y2&97UF})5PYLa`hw9vRpVZI;LZW3aU@CFqEPU3O6G!Xp{7Xmr0@Lc`_cRA z!BD0@c-V-D4hZ`N=mJPmRVKlni`^jLg`v6#u3cm%sv$612Ep4r`bG)_gb(T4owP{h zvy+RJClT!(l6TPa)k?7c8JwnNZ0uGTq~YiwsiBi!bK@aF1cYsxqU3GxjHHI7Bt%(RRaqG#X*tzHOH_Sg%xNZXu z1BfqZ={q7^e>=mwyWAyQ91{V@#aWWhCP6;aKlewtBc7A?MvJfl3h^HnlgdkJX~Z^~ zSa%eVThqHzRQOD~yNk@S>1W=)@SKY9JTK+aJ-5_?UEh8uI%^%FFb9HaM}L|Kc1Lz(h&#bPpeME} z2Uu8VH3S{$1$7n6uz-k%#)fXk97GF2rWE!?$CLnwKR4R4Nshx-jL-z_v*ehFvNSQU zDf?kW@+@UiqAj z;0740uM<XNpt<7&@Y4*fPS2W1w32C#ERY5)`^FiEGR7(e&i757z* zqwQJox4Gp22XQJVg`02()HO4-Fj}ez6Qo>024x~kA%=oT;Gy<$CN`yELTqlF;SU== zal}xch1H3sw&&}G;W9&SpXdKqCTD8i@j&MOyhqD{@vK!LUt!Pu)$`>LalX^ddbhWw zlaZDakp}Ch2G%t-r}gi5>oO`*c#c+9_;PjyE=6T!FtEv%${LpQZ=VluTUU_@W~+YZ z$30T}1#qhu6UZMiFi?pbZ6--eWyW_1Sx|v7I3WBqUCFI5Xai(aMpN1^CPqfyOl^z^QZ5r^x?u^Y>dO46rE5_D7 zS*@LlqOuux`M(xTcPK7m7wGaX zwer3aUe2oA&alo)>v$>i<73y-74=XLgU*qCO@PG#wY%w`-y>98)1{}UN2FQ4tyE(E zPbVHW%gy6ICu+(U*-e+UdrB&O9+Hz<6s4p1V{cv#J6jD17S^+Fu{RB*62wuH{ALuD z49mR;3W}(#FmYlj8suiM#uCFU62P4pa1nlP!osEq=!2h3#A{LJfJMEMc3|ue*Qk25 z0wLj?sQpI~lEjF3h{OSfi_)~Sv46oUASihlP(4!45(eon%C(;Hh~~n1FJMv7dvI=t zBsuCvYmTkgS2l(7eYw$`P}NS%G88C5th+AFEW>2-P8(mlG1~31(<=2 za4dO-7jD(6lc>Q_$TS?-9p;#%XwWdFiW6^5;C*CwnVl3#5%QfRZ&Md*X%C`=-Jhfk zZ4DHV@)wl@=eluf?s+XsIOrtn&HtCYp72n%h#m1?mre9a0wBl|2gej!dwACSxaLl$ zPImw7oKKvskE|^Att?I~9V{HpoviKbVzEdUmgZ(>=a-vjxaw#5ns{42wYs6hd3?hD zd=*{qcFE20y|KP&UVB-;)tMsLbt-xBJBF?Tm<1kaxm;1OF!+Jm6-z=uNjoo{jzK<| zFg+6yL`TEC;~hR|8(TZWx^yLk#qEY99H|Fi~XD9jwP;~UG1nyxP&juiSectm6>hfz;_BT8PtEiImA_4oP6 zCG0jy@pqCF@QcB&#Gf-lGkl1>+c54dvwNxKyBT)|_;V_+LYdJD5LDs#^qWGyRJ~>w z^a|SbRU=5lb8We~2AORml}h+x@2JZ$n>qtOj&w&p zB9R!TKD43n7UQ}3T^IaE1=1kQ1C62>+|Yon{Ro34?`9zK-tipJ^_bS3_+Ur3;K9^h zNE`aFc{A!?&Xf0mu>+LYYS|K~>;b|QwHX**k|#NXU0OFf?F#}eydT6`#T;fku|wrS zfTaqP7O>QzvfU>GIaGMFcrU4C$xh`E0% z3t`e@>>sJ&o+n$pvuRojmQY7T7R)#QIQ$<}tc-8iMgoEXOPgrnF=%P|u~Mikj{kG0 zJRnB@{1J}=(UF^(UzkChD8Za4LBfNK0S^yV2}AfD@6G3VYmDaqsk_bSI;Jdc-hZMva9c;eVA@)d+M$hUY}Ca90?mryP!@9M}fa{=4r2smi)e7RGV(5kf?rF&V8S@GCPAogbaS)_Z{86^U4$6;pror zNuNcHnVA=V9iI1US>hw(e<>|>MiOuFu+?dp#)FFx&4&{QhJsLW=03&gu-Qvs5YOOX z56G$gdFH~g450-_#kJ4U71dG-k$srn2y?%T>Fv+-hYi7nL!Z4HzKFMz;8WM^+w8}! z*_cNEvi})CZy^JTpoh0T*kWqU2kh;wYKwD2p&4rd}c3V7RRDFbFBw;u3Zk!}^%Q+6%ra(YFfa7e6(*RTSc9T04p6 z)3&U>!H6ooe-RLAge!^L5DSwi*e8@?hbJ0G-{OqX0t!YMS!@Ig3R1LI;`mjgmSu^DE^W zHi+)ML11ZqBCJe)fZ!2FU;%ar&OZ~)&gwD?=R+IEv$!ez1@7f#&ZT+2*?CS>0sv@% zmuGgClau>%bJM4#rA5u25?_NwX%!PPXjF7`Tyt|(N=ia(Y(jBL_SfcaSjUz5tvTzT zHQZJ*@6jHc88d&-;=0U{yayr1I6E*k7gazS+hD2JXV z4vw)<9ycG3x^=}r-ug*!DL2n+)%yvD08;2v0GNe`lCdkVmGpNjdF|;=8VFBuJpvT!b!?d!ZHs{qmDxqNhwuQ?dYj1%+{gncZANy?NPCkGA%$6h6_JVC| z^rUEbw_iuKA>xfsnCyCXxYj|YcJiIHODXHO-O17+KK73`qiOM#wbhr0Wm&kwJeW$= zBRDg)GK!#%+Flq!A}^(d$4o2bff(@VIyt(5p7>f4ONSVx&bymH5S{)e?Z#M1py4lO+|NECQ zj(=c&+-02{sWhq!?HqyJHh{{u`-SK6cHH}uyKJCy-@!p^?f#j`%tNT%yI3kv94F3m zFfpGX4j|lo7;coyoRWBB2l-ovLIRi)#`UfS%u-ZzLwI&7piIQ_{5vPrB$zdlo$gjZ z%ik#N;)ZY0*Wh7dBdpM^FrS7{OpDvdCCp$5{K5x;4aT_hc-SrpQa|$#QqkW?lNrW= zVug(?1N5u3IEG}Jgj;CvN~wq2_IVSNDCDFgO$?l>gRM*eIujyx@QUz}z!-oYlRk^p zwm&@woQ39JUULg&R#LbX#4rRnY zrp^B;Jem@91(8q^ixxf;ua5v6UoTwTK2l$gA(5d_hsk(0Ca-Yf{OxD)(w+_dcSzKK zE0zD1y!N1JvAE1VQA1qSd^UNf^t>u2hfjbz9@XFkGfeXgk@!A zS+YyM@3F9!ZabJNge4lXv5ET;)6%!>|JUI+zFEU{lGE^6aRtYUKqIay^-U((M z6WOmNS^0FV{9Z|4x^Od6tCQO%$lVxy z+}wPq85NQ%@Dmb6h!m2tn}V$l1LK>^(~O%oJTz0(Tu;B~$iYI5(p9qjRjy7t`|CC1 z?NWZjSRd_nglvWb2L1O;2a2cAR1Z^;6vu@Lt17;$Zh|@JYXGkpr$5|oHvaBYjTrPw zdjD;d{gF7%7|3aVXBy!5d}}1(>4RT$NDGT&-M)+yrKV4%Y<#yqxsdY1I{W>@=kM)2 z5!*ajR#<;O>=JEW*DI#@#8lzh^4U7f6FJIPK3rV=D}C-HRf->5(f2BI_w1xmUSVrA zNi;IySlkzO*R=R4AxEU^?O;o51U8O*I-l9deY$hn{1I@}le^Y?d%Du$cvMDl{zWCo z`N?r0O+0Axa=&AeY{~ie{aXHyn&+d0ui1RntM^YatYphh2JL%y%~eZpEeRD2noXi6UrIc?-&uH>1ta&cW&+< ziYtL5fE4Gwh4~2kjoD&o(EPNm8?12Mw}SD(oY;?XyX%D=r`_Y{S$Od)b zSpU&QA2k z!4^r21vtX6xTFEkyU8t!Sy}T00CR~~KZiH@9vB}ka`wQ4|QwQqH}Z(~DadSPQ_b#it}M^7hU|5mhImC;CFK%iYJebm!syye={JIrmDy4{9ldV4gaZddego7j4D{H$#g`S$!e#kvKD7~f&?A6 z?w@1q$Z`LB`50S6!W-T@zVjPWB1%*=c8W+Vwq` zCWcLF8CLUnQ46I?8A6N|r8OnB_2ng$jWwl>W9X=vsYT`FwS615SG9<)Swpv9JPr%# zS^Gd!OhTgLMwO8fzEnMcZ+7F2P@{V0P<0r^_sS->Q${b zsn9v#BDT1UVJXLqkUoaY3S7SsN%&1dkX+9Dgl!s*l;BvqNe633(7U)uHDuaZ^xS_N ziIrF|53&3SP6%)m;J;1Jl;P<;`(C>+_$3X7$n1_v>*aXbVw_#HqXC*>u!0C7Snn6! zNnl*lT;lX&{f3Vun+qvBNovA^vmnRQ`2|9niAZaOYOJ1z1&3SQbG%oya_~rSyNF6t z`lRV2qpo2ltEuQE7r<7!2Q%cWmBMDFyYdT-o!Q>n*^M$yb?8A(7l!S0zS!pvn>}Hj zca*=p{`A(GJb9kLz}F*7lWS9EORa8bO$~0`{LSB+>b10%H=g$+CbRjRR_kBu-WBKt zu3avlkuBA2oR6%&3#t_kR2*1Xc)dPsR7z(zYIARX-roI=kv=wMMpIi42**{NME-AZ zn6w7%<4fQ{X)NakO^XrwY3MYP+bY_=z^pbU+r|nCGVl!#pra%B6C*{hU(oq%~>0GqvVb@e+eV`0l@`9FNAKGD;be*Y(YgeP(N zm@9{}7ylGrLB1aJZ>LAfmuUK1zBmpQ!Rw#X6hAN1_`o;c09OtXwwI;i@(IVglS{Jh zF+&l>taZC?%5(Ix){i{}!X^?~YVx`gUj%$KEEpa_`y{mt{d%$EpRHUoFD}eR|!E#+hQlxtTQq4SJz{abG1q1{sPo$dA z@dR}jJpc3garSzZTKPQeL5{%us@)piTB2rAVOGB-DZ8Iqu(=vrHLrQP2A)$#R+*XMMPb^NYKUY;ryLdarN|BPg?psRZrm4_Fw+G zb7k&n9;3|)0&Z zLLiiqJ)}=Ri&%XzginLE49$YhPVru*z?y~kW%hFz)CKP&Nm#WFsKpsvXahG$wWIJ^ zD!MsLFXJT`%#$Lgyh=T~h;3p|9`8aN>bM1gliZQ}NUC7hL8vE=sR+xX^TZAEf014W z@!M+-a2QAF%OA-9q0;wPq3XrOr|v{g2(}&4E-dzK@WKb6-(%9Z%Idb*@Z6CSymRav zrb`)rxe}ho#Xaxqda&1njb3l^k1ojlM)`Md-A7}aE#j2I6Xj*I8O3h`6XZ||e; zQU0rF3}ft#MxmciBz@IR#U~;nOr$|eLGtO7zL&Y95VCdNxBakZXKDHO z&++GnOKSrSF;i#CKxay7u|N_|P8kD1HyjN}AT_SS^EoFkDr)}KKHH>Oz1?ZDwIcJ< zgi$-81h}w`SWaCTNkcg_(M;LxeI2oaB1B)B96UNc&I|(ePENAIE_pC9g?Ta)ByAIF zY~$t+z&c-#$&1V&QzV(TmnbF$OSJtCJUG~K-1!#MydD#eJ*=8cBVG;w1m}{X6#?kn z{N)pS1p+YeL>VN22E6D!*yiNPUjE+%dq;4egqziJHzNN%jYXH7Deb`SO0RpRa$W z@H_Wh#P=j&pKe+PJc;lP|2ypbsy+A#Ls46of3xw7rT1Ia8SI+Lj;?jU+v@VtHVN?N zaa{jok|9m7Ij=NM%vyzwz^-g2a>Fs@=M2{9deuH-!$oTEBQ2M3>Q^i~mQ@dk`&8D0 zBD0~P@Ma)Iz{BlYWS!NSSWwW@A=%0kdvU&;fm!tA#Gk60kL0`Nn`n+O?*9+(U&i6c z{#}}LKTgEzpDjP6ms#D6#SzxRLXn@=|zemWC5?DG6k*>Lni90+#L=3j31y?+q(ecMKK z?DDyX1pemu`|oc@l3b0~hVk3zKi|vdm#xTGTQi3Us{TnTabK*>@{JLy=W)N8zAgy_ z!ywns*a(DTRx9$LtoCPEz6>fMMh8Y?%+ybau2>wg9xRVvZy0X)nh{FCpDT{uFEKMb4*Q^S3Z$0GL*BjvQbK8 zNZIU_4%^z4H$bAS!2byzuP$)Z-;48IRAKizBD(9ch%bUTwge^bQPiMnJosxQ+1ZAx z)%w-axO>^0<$!d1>-JD`9?3=!g4ULB= z$ws5~|3c85QyeFR!3AN6je!BV+2`#e|VU7zzudeT!?+Un>&A+W{26?t>z%YL+QYs(n*B0+|9&Q&g@~!{|4l&Vkj;G0iz@>YlrGpV$_OrhI zx7T;C1a|Gf9TzjPr^i#0qbrZ4eLo!@2XF1Uzj{=EHe+9ZLK}wV8sZWl?@rYD#cq)U zvCeO5OQ?02b~>D& zIG+3TWui11_t)H(R3iX(ede#>FMe5aaIT*iaJJ?T}NV$#6Kkg&Tw>g9r%-S@3{_`#alzw-ErB zXaE2jTI|I|k_|fG0$tG~W+Gc9i`z)we)aN&+);Ima_Plt+1^<0`R_;Pg6sg~~&5Gd?^BFZ=qQ`*d`>Sk9=Ju^vk0~+?}Iyu#o&@F+huzj(afz_tEs`igS-HU1}GZnyj=ld6W+Jy4=34P5vY+UzP=cZ9v-+_ z0p!MACMIE0c(i{#PJ13j?w^%3OxT+X%exwiOa6K~e`IeylC?i_)%|D&8DH!g4w*~q zD=I6j&Khp3#4>aL!C#Y|QF;W?3fBGvLM+@*WWcLOJrDD&`%cKwTj;0c8@N7q>plhfuqo^#vnk7}WgqeDbKfNon z!ItMw^lec7!@TN+zhI#_FK;-%NIc__lI=WF$T;l$$ISAty5jgE^9>>(hBqKsxq#w& zxajwX4o<)<8i9nCjTyS#%xZ|8q1p#)N6)ojUES776?w`O939TYOz+aF?)Z28xA8X5 zqz}mb`Dw4&VTnT>ByXMw?)vy#8*19+|HS>N)z6m>Hv8RZw))KRZt@cWGEv_(bSda3 z6*pJrmMkVOhhQ^4*Icp$gpKmlp1W*1Pn2wsL|ox^5SbN~D2U8aj#~-Uk%5ZB;7uQmvFOyrqW@6{h*4FkeUSg_-NPxoT#xFS;9jll8Y{Hsn73VFc2N}m5qH4la zqh5AyY_hrqV@lW#kK8#iLQ_H@`q$vB66^$`WdC+30v)9@BoH2lk}@f?01$3%6Fren z&xg&}Pk`D$1EaCGY|k{A2+89(cMLY@P=vWYda_O-%EC#C$hGzHOhm5kWE<_KYvP+p zVo$Tiv5hLRd_!ZcMjJ00B3Ojtb4w~YN?leKE0G~yR=HPpM%Ia6qDNh!t0hD!3J1Wp zIgIawg@y0sc2nmy$C&)BwgU!=0ucKVLyKYj?6CfAirjUnZ7#E}H$!gsX|Bja?cQs! z<87hmfYC-(30v^`j+XKrTP1pR#SLrG6TS{2&Wk%^Uf)H?E45UZD5zX4$opY;AVuwr zGQj@{cs!7edS?0?(eLf?^@(52Of=|vZ#N}fjjG%G@_eze99Ft~OUp(LZn@>LtuESS zTDqmSK3b0_k#5~4L8rn885x8R*WY|}vb{8_ode>HS{4ZNDX@+xlsYVyrG zuDx`k>w8(~_tmEDF=a&$>c-BtT81ATw7KhvYG>fe(}Zcu57zwZm2&^qP&OL%clFE! z_FDY#`8v~;BXQT@R$r^qTWTG=c8;&~c2%zXXr-z9vmhTtEm zBV?LTc-6#J*p{#Vo~d&-zF-h5KUhNDs#o$4UOy#%J$|HWD@FE0IG5&8B!(u1DxN? z*Hzzl!-&EVUP%I9JO?0_g#G@#8VlNKq^7nZY0C8c4| zgi_q})a1c|Q(s*YOhU0g{2>Ym0&ON3Kbz6{Z;7u--vwz{PU8gHHtX&q0OBTVT3nu0 zOcgPusi~-@VHZc(Qf)juCin1C#Qh(?*Mya~fBV|r-kn?ZBckjSLC{U%e*j8BwZ3r~ zl86`|7dOxBS=_T`bI#)7;uMsVS#a{AuA{5pnX?z==FAfilM#|uBFQfilAMqE$0w}> z1_Q_S5&0QHN;5=}Yjo!F z0~~8lDI7ESedj42e#IH9j<7+*y$>ix4fZ_=AkY#%_# zm)`AutO40)WQ7$Jh%+)h(JUOj06_KV5nleK^LGXqIL)jt4YYA7x8KGct5DZ zFnBIzB-|vjdH@Utpam>7Lnzu0bQK}gqU#3XCSdp}2z$gB$l)eXg>qc|)##cb^hrP$ zR`p=)g%TTQ1EK(j*jb9!e*zS%2auGcp%phE{-QuQ62ygJi49ldY6u5uRX0dU!j0L5 z*Q!8{w|7BRV$}@Z2o32xw09c1Dw;l_2(j-JawPR?kP{UcOkCI(Q`HrXZi40x6B-61 z>78MfZNNN0BNB`7ICNA!niK>aMpE0s;z1O`Cn5G%i>G&Hwf3v0wre^?WYcMWIZgTL zXw_(TMRk7Km7KiH&aPJWYvfn@IqCmi5dKSEP<-KkU=!p1eQ6v`@SwtXVq&U|k!z`| ztD&X7df6g5DRB^aJbb*IynHh_xn^^60}KVk6b_$KF|%?uGP1I?uyt{AR#iSbUr}0I zOj?X2#mUJ%dp6hXSv<35^6>JAD99~3cgf7b$?x#-6C`PAUU6A|nFV~3^Z3NkF5CD> zLIGm0QT8<9Y?1=MxSWWDjJS+~APO9uD=eu9P~;bv0Wk^~78Mm05Ru^(UBE4-G=0^6 zjy0#0jvIj@c|zVa}DaOM`3X`58g1R*HGo!VS`_4y8)2%WMLIBq$8x9(A|A3x{?a@=_z}2pidyRe9sl7w>H|{o!+^!qCThcxf zUDfJa-r!Ac@~3x(Qo1}#o4skEhnhnw+k?s){V5F|)OzoVM!&j#uiP4Uzl7YJqR01c zH83c3_0=71)$Of}=7uV;_7-Mq6QiM)3RYiVSyxAGYp#YeG}36LW#xs&rUnNO?R9bT zcZn@SW+8aPcvF+ZJPRMbL#DpdzX6As1pbjAi&H=OlCx8kl;9J4? zKzIb&9gUb5MB)-}mezv;6)Jmz=>1@5!6y-tAIcm;yM_|(N*DkT3Lz%&Few4LzQn;7 zc!?=ojthPa5S>U+0>%i1N6c0dpA&mQ11iuaXQ6~M6Mh2d=s{X%2%`u7jX-+z10lDC zRCZ!KW3h<%O>8s+hUW_bav|h)Fm!fAWp@O%3vP&l{Fs9=C?2sJlpioGj?oJP(ApU$ z3Wo=E2r$EYyMS zAkpmLM2vh_LUnf{vj;REkP;pNGzAt$Rdz&E+hfs%C~_l=+8$ckm_Y4JZyUUr(WGq~ zm`|ts<#psH#p5`_+^odn%#5ziR^(G6-Y37(&q@D}D#CxZi3|P>Y2qu4vQbt8CEZL* zM_p56k&=Rp1PQrD1q66_`H8tY9v&`kQR#Vyk1Ly6x|o?cSeV<|+d5o4dupzNG$4~C zCd4nm&CAEh!#f+~BU~aUr*Q6~v5Q;K>GKyP<>di{yi)UcB$fCj=I}`&%F zArDE8TVgJ^h?Ib+gqSqiVhza0Co01$B+e@?jdxrF5b}yhvq3oHx3lw37=t3jafBQ@ zwb2~mM&;>5_yn3M+@-^N$e91QIqwlO0O2mjDBto%pM08oc9nBB-7AmbUC4ATtc@sd z^<}g~bPh$ejRaS;1?1QIQkp$0TD?o_0hvze6!&bJTUMoKF3ls4=9ypRSjcqBtFg?e zv?^rU71cUr*ErXiT zT}v8)2OcF&@JYY`3|Xw80_xDg^ix2{-cyEKR2CE=r;voG zq^!KsLJ=txRsp(DKvasGBq<;!2UTHF5guVF9+CMx;`0E6v;Vj-|D?%=-Dmg&=gwYx z926n3SmqLdkaM^0v~3z73IT)yCoF_cT5}#S+F+;wA3^6(^Fd7nj0FM8=G3%+B>^@`g(c?d;5k42Cohc zUmv+PeD(Us&~V@V2e%(QxOe;Roey8WCZwb)D9rKoj&zAC@GNUXXZ4k%wI@EM?Ov1? zcsu)*HTzQAoD1uG%Ub=)T7f|BlulOwb47;_wHr9+TGr+T1xlAwX`5|cgMCiDLw2KE zQHM9V&y~{aRXvQ+gK|Au1PHwysS3exzFoG7a;Pa0_TGb9j)(uAxafd{B!yxi=G6&)st|c_x zKofq<0nD?Q`l0B$L9j@|fQ87FN*tdA!(a@W^Q#@a(s&h~f-gyEx&~b^070?3A2^rX zbUmr@8r%ecm)vkIu?{{=FOV;xeh7dB1LA9k@Ejou8LS&jtRG5kyqem09jbA)XoX8! z!*E8^)#SzzcnVaL8itY^hk<|C`I**oExq+Ro>PPiGg_{vHjX6K4Z^ULrjewEVL&Db zzNDJow1&Zyxbnb&dcC^&CP9tE&No zd8r9~-Aw?(oXmuxjP#!Fc4Eo-FBJZ~^xqwXf3FdpTs8Xl8c}w9VU^WH%Rkcbhr}N9 zuiM0x|Jg+B5QyUlyJ`zfHFeLPQYi9?0*YjtU7335}#T7)v>M?wJ%5Qw!P7!)|g6hRS^q~zr0F2Eqf$1rh;O0Yr5Ed*5rLXMUDX0AU6 zApCRBS$-iU48rM~l&5c2ov~e$YmXiqgra9{r7yYiAG6qIAMR096Odo!n^WzXUG0@a zcgvx<6xCKd{~VE&HeYhi1_w8lTC}dxySUD;u*JQQ38D~0BnUX~@@9CmgNSi2ZL-R( zv5YPY%4qaVt#M3cxMbJ46*jw(mOzWU_k_ z#W{iEm_|d94CJoB(hkq!`oP8^w=9}Za7t5S8MX>BHx-@8hH2StX^Er7PEj1SdH}SfMn4P+JhW zJFK)dg4*L>hLlhcrQN@%F}NIr5eP)kcI|+)U^+S)35PBK!)&ua&Q|*uGzI52`efGl z!yp8y=aS!Q998C#$-F}C@=2$8WHAEDI|HaF@Ej8kG!vR2 zq@gGR6g*yoR*?X%FtGqs6IzW3lLtc|o|y0pWe$NN1hI&!lLsn=)grb5Qv(5n#BMJD zLZsV>0oZ_KF}1_7b%39tiAEIXhgPzHK>@G?9|i*~ir$ZA3Tp=8iGagsxR5y%Lq}VQ zLK*IWSF)fbEyP+-Jc$StjHn$5Vf2Jlp-Dl+JhbN-nwf(F5DVlTRMDgz5%wNT?Z9Cb z7{l;$Kof+Z2;rlG#H6-|RJOxlP=4?@5ROswZekV?#T0@vyTa^G0c6$>#V}DI1mG$e zIXvNqIRF>~fd~d{i>7rVsn<9HhW8GQubzkwL=%C`UVtThditLy|80uduBTEfY#apd;Qj^PXGjruO(W%cy{!pwx2{+{MsN=98pd)&D5{aMizJ6IcEV30}DPD@-hw83RSAdr|$8 zipIXfCwJ~Sx_AHS{Rht;I&$&wab*yKhmM^;c=)`AmYJ=syOWcbv$MCYjg#7C?L!BT z?>~J0z+vUR2hJZls&eYA_Sp-17gdbTUDVgmGIwzBICbW3Fi*kg@-`}7_KRE&Qc zCt@d9<734|1&WK7gvVw%#1uM~GzC-vd)oXe(a}F4dTtsPff1S2;ROxB<;}qWGh$;;04Js<^7mGB zc$T(0B-eOl)Hz0$=sU-F$Crg<*Mt(tlorI6K)91g90>$(}7-ym*_$m#sooz z{y0Ft`pR}FfI&-Sbq`<JHsm{K0L59nAVBhq1C-$ z@MA%dd^lc$I0z|}(grL<03(hP1cOW9TKMPV$O_;h42Iu3+JJ_b!voZz6O^jZikL(~ zQ=&f_h$EAr1-OMkSUZqZhs0cLO)r2DD3#tgl3Wk4M~gxMd&pDD?2Dx%uPO3^Hjcnm zaAzRs#HvvuKmu3@Ps9Nd$qfUUZP#EhkP^9Gt2)!_22$$>h`GZ)P>M;_eetv|z+?)e zFQ@z3`LqTD*NC#l#(<*k?4&qQgcyXm$w>%=#9IUfCI3o4CH>z3!hdOJnSW`@@Q3t| zcleiV;>!Ox5#L@MN7ziu(mJPVU|`|u6Xxa{?(80HZR-W*=o)D67GQ4Y1_-pYbhfs3 zc5?P`bN8{acGTB1Gc>R;HnTOga5OZwH?wfDcl32}5AY3$baM64)wi~_b3T3cjF_|> zBA?{^*%EVjq~t*hLKO^DAX^cF%_gA}>M;oUNwPpb3_>0;DF7jmQcze36d{0+M{+&_ z;p&qZggAVHW4k6kp&k?=vK?BW2xsrr6FzNCI%_|3ul@<|q+lRieib0lIjzz&yUM?a z>5{{6r8Q>Uyx~V@nAWx!(mI^77+&QK9wiMvh4o(fweaUWykUVWo<+^xMJ=v5bzUju zL2;#X7H*rnVs~IFp@F6#Omp1xWv=cFH-QGp5Hu02@bo&1D=87x! z+WMx(mbV5L*9VZByh>^TKLNG13>2bSGM}o+Q5PsodL8q&$9Z^vTl>0 zTn?W35(~FQX4a%v4@Oq@BH!i&1yOJwfC@7V(B+w48=Bv`;gAl;Y(+c2w4}0*$dcx0 z3fk2SFb2Z_8)&wS5K@pMo)KCQ%Q(0Ra-vqEgZiKs3=jzS2g6=dG#dxkqGg%H9f<8z z&@R;k_CU9>{}S zt~4MJp%=IXgW;0sy1|(GAs7G#H^c@X2?hY(5}K|-0iFmWv0rrJ{?(|8ouzPRFt{B3 zW9bNWDGejZMC8Mj#$n{rY`z8~;c>uG_8Cbi1fpRW6k%HHjpXK$#QMRkmg{*Px6@m1 zz+e2-E9_Ga5!fO=i{&=l9*bkl6$vEUc*sEbhroxq^3=0T5;< zCj0{7&rkoYK=^+w{Xn~a(Izhb=LBD3^c6;~(e~;B?epq}hSnyQ_LerzMn*Q8n#TIN z78VvxW|j_yCYHv=*82M9CZ<-_HV!tnjz-27>gxI$8b&$>#ya{Yx(4R@MwT$r(#Fx& z(Z#^TTwB-D!qOH%C?TuBB__`)G4BTuPIZlf%@UR86O$E_mX}sg0`l=fqp%c!kWWHJ zKms8XgOEpTA%O7z$KGFn$8lu)|FGiN7BiC>V~&|Q;KZC5Vu+bplEq{(GqXk-4T~8@ zJPeX%B#jts$6*Ge{^xYdPB!=M@9y2Zn|t5qbveYPHl4!wYTGYN+Ia~#j_$f<@bO*!gLk#|+&&df19=zBs_fq2Mk0g$7xiFFlP!d`xj;L(& zs}+J4OlVMLs_s)%y?!)SVv{&tH3FZ-Ar&IfG*R{KVB>&+7cQ0s)&tIMj96}63GJ|n_= zVSMVcUK!oc5zfV6n?OM@zz+yD03L46tdfy{Aq^iyPOH}8BqZP`wXq99(%co_)Ctc8 z$ibVyPyi|GEw^ar23EfDB0R>a)+CkfaE=;Y*C~HYg9jQ$U;3XoH z*enAg5AX(b#2ul;#+jFO+$;klke-5I`h3zx|A*l$}&2XAj!_*e+kBSg07BfK+VhV;iZ<*T6GP0g!WI5f?%tpt` z7C;Dn5QIjSe+1#Qy;qlS*$;{klTrLF2!VnVww>4f;JV&Hd!2oD#)n;Qr&XXD<5Y!| zw)p1PhZNLDaO$H9QNy{hnq25D_n5puPD@BhYe-R3bXhCl9~pdti7v$U}SjY;n&~%_}gQgFl}yb3=2sNNGl2~7a@2mr2wI@ zQdx8*KeR~nVu-dlZTHHi~GnqtOY~Ua6=qVMFONoF7IKop%?!MeRLoC7( zUgtm~oR+MT#*||kBizP`)Wj)DID(!-`AE1cK!4off{Tj;!$1@g!jWX~s|r{+j8Ko> zf#KyMxHq|_JF&Ghk}HcKa}y$xag8&qN`iwoD#)o(oD_w2@`cG+Q;?NJrY9IYp#gql zn1B$Bd=n6Pz(rD?!ut>ts;CG7RLO9Atu&>v1GQZ<`SoI)VQ?v-3e!!1#n*togzr@t zP>p0;Eb%5}h_$5pHe5cem7-5!Jx1b&TVN2J^Cad)!GMlnS@p^^!eu_Ku|1^`9)!z> zF=fK6`u3cb&J?Z;{#?;Fu|}L+Elq^qNtqz6MxG_;IGWRZ!zUg<7?LjoMJN?D<`z{` zDarJVbVY~kH3 z`qjIaF5S9z&EC$=_0}z?Ti32%LxJda9qgP=eR{%r<{WhsTV0TYz&wlTf1DRZ0}4XBvCU-jDU(dB z^{u8ESxz;#nPF}<-O6^B?X>x}Hq&jaXPQp2H8Ho*Hl40zJzLjmf!d-yYO6n-MYb%urjpPi@OtoEqJ78fQdzUYfiOMd9RK*EJ8=>3u{f!f1L-esct?#+SkMWi*5o zHv1LUMHch?3+e+38-fcPLjaS77ziz}xEc4z6t#ww@I#Smf}j!}SZJvLJ~Wdmgb5|B zQLNe+4mYz}3ZEmO@WP5){VOr)PjHDas91oJ)r#?UTm+(VQwmxV+4b>k=r6{Pyjlf% zAe3TObbm%gXkt!k3gw$`zkmGv8S%{o{gY9tznX3$TF7O>ar;BPwxH74C*pP@-MdN} z#O(?wl?0cFL(8O5W%9(Tj$p1N_p9#``R&WM9k!39Bs}^Y@B>mTl-Qy|>RmX6&;8T} z1#Y0P5@pwjn5|toE!}{DU`*PF3uyr*xZ{-cRS2oVtnRqm0??b^9L;QiBc-vED(Z)C z4}PT}prw%&=#3CmCV@IsB1&*^5nTX@iPpeUu|Mu{1x1KUhU8))zzLv2p29Jh@Hhl~ zqQb6`1E1g&r)mkB0zvU=jU4yUaFIPgEHHo~Fp?B=Q8kjJW~4YmlmIQM3xPLar1~VG z3l$KTU{C>-3=)=9Z*UiAFNAJAeAgkeDOE}ns!^yVf?R88i>sG|vP4Ff14U#YumC~; zKY$`s0C?njA-SOjm+nBJHYl(su#kus1D4T@DJJ5%m?MG^ix?>_s~Lm8z%Lmz92mMc z5ZVrqhHih=lI%KhR)ajVSpkjdEuC=8Zc0cSS`>Nw-rSZRTpg^JQCixdC6(ChLv8BF6LcI)lU;BNpmS>iSORuN zIy%%BROeBWso5Dag%tk=i9grzztz{}-&|gU@V_p9qTN4Y2tCKa4E1}_4%=i+#rL|7k=M?e5V#ZU_i5z8=K$& zhD`i}aXBT99)Yf@Ouq_IP<4A0w+lcR0ursR4QS_E&i8B-Wk32Z=l*xeLtohoglRWb_s|CN_4%5kLZc$Qwbt z;shoaGryyZCO5Ox5nD+KgU+bKh&jwe5O&0R9_q_F8GtYezIilaQe0?HCf4vs9Kusj z?7p0^B_ECBaM4o``jZQUI3wE99pBW6sn2wavzyxU1WJae2fnT)!M|)dkJ2JLnj$&l z^{AJ}e2>*NK)hFyfZntg##qMlazxM5AOO`HK z1h#DH;)M&}0-Ha7?$TwePM)~x=;Cw3&JjT9;_7wg^ohmu7tNYwGiS!sx8}@VICs{9 zIWy+ZnL2B_4UBN+qDz2Z@X;fO3~<-zR8WNamNPJ#nknv`c?CjpY7`7qqo%Qqu9>a2 zfr+-hk(Rc;wvK_GzOj+9xs}xn)2Y*pOl>qwW`H6zuwAUN`r|j&9hrOR&id`27?>?o zUvmVoH*v!WwausB*m~}jBGle{$N0Fr(Giz7-@UTIJ|qTI9jp2tvo?U)gXu6)?79PL(ocJ^iCsidAZakoUE z5VUlP;K+kxuUy*KChL(&R8lb|0txqLN^^w$ zx%|GYhR*E9F8DN$tCL69W1KVO3VMqpMBx!ILTaPxmBs=rCAV|~V8~@N01VWj1=#cI z5^}3SnOv_-u2ZCNF=-LBpfdwNu?8nRNudVB0^mU%*ha48k>fxMcHqhr8gZ)&c}lM8 zy;6iE4uN*0L7kkO1WidI6HZ@Ug_n#6O<5%YR%N!LM<8$l#>DNW)kNalN-;17h((tV z10bP4APs&eCjRmBywqe+gsc)S zJvS9VC|BatDCvduy8JceH3OH(6D3lkG#V3D4ofsUbx^}$0I zo!kQKo!uRs{X9K1a>ZP}kJb)7LlGGBMTEGnr^G zZL--+O|$vvFR}9A>;pH~Zat!Jx?u9^!*6cC@aFpCY8y{M1w*W1(CzaYAKo-N=4O1< zb;3KB*WC>V2!-d@`Lnn|ti~V~H>9W`jMWgp;es3tDMGggFq|4K1Uc9kT7+|)vNuFjNkU5a@H57gOTr5AcLnzb zmJ32EM8uX5X;5+*tz9v- zlBgO?Q@=0xSd&W;LQ@OstSZmI%|UMFq-%PbrG4p20mi)*PXqB9o@X_@7{BEbUAV4z)V1; znW>qnm5H&1u_4BzF*i5Y)iInsd&%hw&OY}74j$O2V`eeg#8%s4x(=Yu0t2eOLLj+o zhBPe@i$>Oj%Y@0B>Kf`AS{hoK>Y5spwRAOgFdw0&t{J9bGMlY#G8g?NR(>>V|BaO! zKhn^frS|rIwXJ6-tv~U`ma|C2ZRaO!I-|Db?Bre74UW2iB18~6MPck|W|cRs@?LIb zAPe(91Tz|Z>D7LW8h>_O0K1;FKu!Z#Z~?hY_)-zB+tH7e?bS&wfhlfey?-2sCuxPP z{>8Dcw4*dY5M0U+DQpb~LIl2=1^tv&+!FprvK~qGq*dJrN_7dz@Q%#|3yhKoLLCt1%#FV5tz7If*4ez-nYwdvw)! zsB7%=5`>{}Z>b1GBs7wzVP#@aiXi?#4&p`<^oK*(V4a9D4eSA2k?HiH0%|g%LI{+? z-8Vekp9$z9bsh;VU}t;_%0xgHoB~jB>5hv;gb_eIB{hT2YXb^`=1XouIR%5@gNVnW z;Z7Ee+XC{A1TD%XiA{6Sm!hV1g z7=_;pr({pK3PWW>XI@XPu#YP0Cym0#DNstQ3Rnxe6Z!xJ;YN4_a1YFJ0J=D}}9y zOlSwnCe^f4n>twSy+_le*F1vDnwvw};@p%ZP=uVa22g|m!jA6t*C70h%WDw+%gXqN z?0?6!G_=jEr=K`=8+{6$-0U2@eY}0oocwsYxtXyAK+r^2Ur$qOlCGYHj-Dpyxf!$H zI)2*zp11#zBZmyEY$h660|+(ErT_>@uL-a}QV>j?(1kjtHo8V;8anz~+WMdcb@hy( zuAyx*$-qR%z*^mC&P0=$la1#PityuE`>rqBaA30D%s+vU%uo*%P=v(LaFjw3NW! zz>UICa)oc5Yu(4>@8=W?keTH^K=!oRUY8-jL=zyGiUq-Gt^_r~cGRT{!YV}hhso1F z+UDZ0GKZWBr|c>RN_E=qm+ku_?Wwg6xixolYFru3&a_5HYP~~F-Q8R+)LrOJaCB!j zIb?HvisZpHU7@@|8AU2W!esHdma4pBV#}=FwMC@V4S_Bxb z+kxC6R_4F}GbD=OD&Pd_Y5ZOge*iEr9DxXChAquF8r0!IVRf>|hW1zhUNd@A03cIZ zRY|QX=zuLP-GDAQ!o8>?F()7TGXNdadDsqvzhBdfk0#3H;8U@x63 z2mP1F@5*iM0%-{2P@1GPeiyYxMS;Ov6xpqcjHZsH8hKo)IIT)Z5q0c|ZMo>^Tg~Ie zmv`o6q>2UgjG|gn*Q;Ti}p_5 zx9_?*IQsbd`ky>{aEh6Ug%ySy)zZSOypz@6fTO0C&g|KXKKb;Hhx@&K`}P0`K@k$! zcc*AtVQ^8Bg})=v3~iE$wYrIo@swGX)8^_+(e4H9Z$BB~Gh*Cbzj}a4_U?AXLg3zOYocTPIT0rve zP(tA$@|Sa_uv{FTwxu(!xih9w0Y-ZMK?gtrG{O-YBSG=-2BPYF{7b~)FnB`;;3u{Qr|%N$ zl(7}^m~uo~P?ac>D~qijmw>T#iiCzP&@|*m6*!X0FT6q?U84YeVx8FA^2(rvTy!Ks z2!O%)Mq-{5xPp8lwxZzRRc#5?9T?$-*yobi*qzka4V)v@9%$2uDx6t`7L;pbTu=hZ zP$5sj(2arEN&_#9LX!wL3hnp~M3m822B-KV)Z;=R7>o&&4J#9Z0DYwt5nXVVm=`4( z4DSpEmH$wOw?f5MBN5o9cnQm3~Eqf6)>(!45vsk zBBokO9mWVnUkHq&hPRWdG?|O;6BtUSUXsFa$DroR*WVC-7*{G8BJ|4cnnDVLP2`9h}zZ-AiUxd&>#{=7q?SV5&?v?g6f>i z1O}C&>`}Z1;a^-{gYaKmUSNl?6C_=}(uA4V+)HF8LBy!LL^8kco-lbO{ z1o%xhMVn-1`-XwJ)$9dx7A@B^wlpxd0`wXf+ZdWz>Ka;UnpkKX+D%G$dzfvn~* zc1-{a6k#={#w`K;*Bs2Q56y4<<4+n1EG)y>MNCFfn(z{Y?im5(B4FkoP=@O(;MHY{g3E=$rTow$UR0XLOF-`AW=P-z8k8a&qw^(U<*Bb`Q%RGBptAw% z6($d-l0pp{g9&d3oUweFmKN$@VO$Il1E&EcywGY~sFB+=z<(G|GL2tPtd)yqurWGqlp zBSL>n)Gj5Z75tWf_lb2KF_lvIrNKB+<a8 zhOZ--L>FgDK{3NG1hGdrMd0ojLJpF;6HcKKgSoVzZbM&-+BP`NZ0f=mkej%Tlkj;b zcEM!wx^eGPYxgTfNEP(a#e+af;9m~EnjK~(Mo`3Uy{KFPV>JeKZN)2r3Yu$d>dmL_N?>;dL>DN;swQdeQr$xg)CuW0qz# zcBj`U@|qO@!Y%&g7wo+o1fnpOC_61lDry7}=4K_b>D10%B?-dU?h}7mc@4sUae=>F z{PV^C8+-aq#@V?s0kJVP6 zTJ`?zIkR`FnJrXXvqx>`xi>bRp0xf`wH=q<*mg;6%Q>}e=O?^(MSb_pS(id|_uhHW zH6FeFsHOh&TJPNI5El1OKj0rNtV|3f2r3qYl>V*=)5?~8 zaEX1H>h_Rw zIgreUD-0~>p>K74yML9;zd{yJEDtSJ0%PE9Lrc+x5#O1NgG0DR09`O|Q7I5zOvXwL zuM`97pg&L$z!q4Fp>WVWuBII)aw~uw<2zZPCw8vJP?_YtP=R)!1&I@~4p0F@_1SONC)!xDk`s= z#z&9993rO@@GM`}$CeJlExG(YmVBU~ZHO)y$Z1wl`3kzIiy<0FB^)a9c%2yEfZqdG z8Ioa+Y$RVg0;jpnolH>=kPkXgTe_ISK8~=vP&~-y_tTqIxsC0VdU1X;{wGwVI)Afop`oLLJK) zV1!1T0!Oj}EpO@=1MMtqF~p3fu`MQPGPX80w$(7Q0pW;A3N7ZS&)z&?*C6>7Oe_~poV#w)_T!VcTu@v4soIv~q$1SVc2Rvh2A45BXt(@MjKxQHx4VS@@hAF! zTX>Bqx>gciDTt^_l#`7cymGi@4I5teS?``%B`h3wqBvbpjs3IiUin3wDXNvkR*R!6 zf8QE2=uj3}CyAzcy%dPE+9Ko$4Q=r>@0Ts_G_w&eGz(M%8@adn86G_{k2pLn9IAjCARP zI+3&ip+XwDs0N+E;6^Y&3mgFwa23WOg%q5U3u$l(j&PcS>4dr{g1#JKe`c!+U%MVx z0sOFY^Y~~0Azc3>K=|rLXw2rr_`T2{$w#aylJ88u z2^zsm7!5#3z5}_)2d@X?lf(iL!YMopK$y{j!9PKekt5)oE%D<+J-rzh|6l|#=nw3~ z+RiM^?Rp1R+DH%50DmbD{0o&GPJ~_LLe~Qk5vJL6+6hme$Y~ zQznS6XvwJ*=17J&MO0m}_hK~(0%$yHRtkWS&7tSyX42?6gG0UJA0)3q_*a$JAp92> z@=upcN%-VBg3#5`>HMdkPM>CjxpvJ=P0cJUt!zw8&Giip^!4>kEoYrRf9sxKxTlw& zi?fG^hu7hc4;dNi>l>J&_k_N&iLr&bnT?5wjfM4$xo<5$a^~tyJNr#rHfx*NOg2YJ zr)gn3*%Ec0uKBcA`FP<-Rwf!+Em*OB!O|5*=GMAqw$MS#*ap<3zM-wUt|hcg*0-2! zy?D~X_tlo}vE6-k-u4d-EZ>@Fxp>mXLlgJjoV@eagbnA^cAR?S!)p^hxTd!AA{-~} zzdcTr|E6V&`r$h`PE|E$Dapo7fd%|tj?=s2eU4?p$7sz7vj4?O-22y^Bd8N>B3u50 zZ4N5LNXK2E42oP|4)P~Ek(DOuN#k`@jt`41DT3~Ffhtpo-{P|cJ?Wx>lopU6gCM-Z zt9Wq@V!$34$h$~lUAs9(4$l+^{PXR+VliOan=vjzsa>SUJ*u9?ECfb0y4SqjhfWYJ? zBo?XR;0P_aS(FI!LayP(RElx4C>MjmKs(goMx0Zv#&BrR83!RYltMcRKRANS1w+!4 zcU4PaG;)426$5o*nq08NT3LLx1b_$F12jSN+viT5 ze(SA;);80oPMbA*-lFNV=1-k5ch>B;rq7r&XWoj_=dOF+3-a>zcXjb}bMrWM?8K}Y zGi|2Mn>Bawym?D!&t3xjn=}9InX{L@wRr9E^Vgi5Tz2lxveH3&)#6 zjYAfzHp=Tb*{A11YAh{BnRT#jm zMjlv>kx9J%8Ys>H^V zD8kVmXJv^r?qDQ@Vw;pv_3h!rJHSxLS}sm_0wCcogd#@6X$RSt*3=D(EwKrJ4Lk-~ zBl*Y)On^sPD_ljLN;duxgybnX+X;OzUJRk_;6^YQ3fkdD7=#RvO?Jav5QNx;8=)KS z+ayQ{6wDR&1I9?C!Hv)vy1^sJ0471%zH!LY44_dXdIyr}<7b1T8xOTqDzOt zP^~s8K?O3!{h$}gxmk=6!^5P5FiuuOC%_McU`|VSZmWtVRu#zm3uL1V=>S{OQ`SCE z*)>w4yw7SIrpx;15*1s1pDyeNbqE78M17QIgd&yKQzTZEw~rQz2N^BhEPiK^w691q zz-;XWUC3yZGP*z88e4bG!J|SVjV<@)$8UByR&C5o<4o`{P~;bFWQ|xd-K!N*Uy~0b>Wiz)$7h@&fjoz zbbWv4JKFjdlk_dMjcjy`al#XGN*GyTu6aXC%qBq`HT2CjOsBl5Wu$EY!qNshs2f@V z9o6+r)b$N@3{A9k^fdL1G;J1Gt^IiB#zQ)$vnHA>(6e1(V6|M={Owmb`JY%^LQZ^Q zZhR6AEG{uGCJ8Mrg&v>Eh)t$J9V!WF%!CZqD@)8|$EP!6Q|WPOjKmaHd@5Q(8XJz$ zN%W{h8XRMi=y54bXoO=_LSAG%H8z=%l#!p5%7$B#lF<^Am|)2%?9{Y;sKmzQ#U{{F zGYZl&3!zV3JS`!S5t~{Zo5Y4navCczHpe@elhM+b&^i=aEei%#aWRZb1d*8`rWSyN zQ={aL%5e!u#C-<#VAk0NBF#`k7xp9q&0rb_F8c5Ra-bV2!bl>*EnrXw0|G%E8lgf? zNJ51?C1*ZgT_u;<;1ud42+7Gtz$b_~#2B{&`oLXj{9d>O?ZC72W-@6J+}@eQ?~ZR) zL~-GLWMr^!U0S9$0ELmI-rv41cz z?_W~j@Aw&ju$Gx|;oRlhH|=lVbh&xc6<~Jdn)6LNPX|XIM`u5G_W&1{dybBtPEHtH z4Or;x;(7ayqrHQRv*W!RH=J(Va)X|C?t0&K@^$kJw72)Tc=5K2z0-S}w`pmaXy{q# z8CmNZT4@+qV@|<87$Q*LTwTxXO7=;^XSjuBxrI`kf-~<1rrQOkfjNd|Ifql6 z!m{s%WI`Q|&Jj5-k-2bu)ove@j_qN&jv*8!jbk_`vTsM{g4xH>oRV0MiOf4OR5*pBU39*EG{Yf2 z*EyMz%&82Gqk1MWl0~X?(NKI-_wNgSfV7vfI)QLdCzlCI0Xqo;#G(OsNJDLh zOOG8mxB1dYiH4Jq1fNhBVnPNa5S+p#XaPE+dTSovSp!r{Z&2c1P(o#bL50+j(34yd zgb{#bz%Wp9Fkli!O~Y{5DKB#*L7iL_OmA$@;iHpZu0WMqF9*wR?SvM1F7~Wz%LHnI zrmV#&P}Gji=p#YQns(&zyO!)Ro%5!-bfGw>? znp-c(kPj{ks5*buxwy4Hx?wr4L`}!^CI}UDl?3@62 zPLA%*P99)R&K?+g+urTY9hd9Z9USc4Zd`M`dez~Mz1OWf9=Gh=0GT&$Ib6AZ*U`>? z`^L>$x+c1&(+td~8W5T2b#eRXls_67bX(Vax~74dv6;2a)LAChGchH-CER6eWM;0R zYpkwisHguo`*;31bImQjlT~~tx5AlL;YcgLL$7trt9GW>y0GiPoY-7PX1x=u!G+Ug z&*XwR@@Zl<+LOan&nrjKqrMT6(s0EI0 zrF{1?fk(N}vqA*@!90t{&8wJyuTJi0IYh8D03iswJW+2}i!!Me zJ^4rwW})wd68Zzfa9yym1GtsPSFt5MAmad{nav8ipu0c<5)k9gWj9oW6*R}kXC}r(WThuD^0IStGDr|Y zAwEi8m%pO??||@kV*M`u$Q1LnKaQW?#e4x!`cuCDQwv#sZ~td<)BpR*^YO27I5qlY zY^*dl@xuA5moHpDf9~@2Yqu|4yn629waeFT+c~;8+;zHR=iqqP4Jqj84ssAcc;m+1 z+qa!B0XVPTvAg2};JkX>+1}C1-VsT8>!$ssYYw+=+}yBky}F)>w#9T^Gg~cV3oY|; z5R&lwBM3E&tU>=-*i4-^bDqu2`4-dPGM_g0ezz1XiiNiy*xOtG=fzVTi6i6sE%aTM6>H+I9#ZQ(JILn zIE6;2N6@Pxp(U>}Jhwc8S`o>qjAB*$QJO@n_0O*I=e7q|$pa$U z+1XXzvDt3anhe2UY@Gs-2ILE^mPOTLsIv$R^*QdMhf|{jEyAlYgFNsOXHdr-<1mg) zc-`-Fp+GeZ=2C@u_{i8UB(9(xq}VHi7HB8?lk6lbq}oeq?k1HVxuGVaLVS;15BMZ6 zfu18lNU{)M25d{H1>|6Ov!rHtq72xBQtah?CP*|m0=>xT&@3L#7D9iNn4ma8hk@i0 z^isuxVA=d$xRF$Qq?81E&EzBE3LnF<7I zRpI0(vBeaQFgP5Mj!m6#3tN7lAsMC$aCHzs2skABfac@0kH8Ti2jUNx?%IZF;y$Q@ z0SKu=Oos~;ggSI%i24Xtsh|QJ0>uV!Dr_4nX&WqTABKDB!rlUTe^vKramNT#Hb@ut z1F<;LUWTBbA?VGQ^^_iRyb>Anx`g`%Faw!UiR{ZiRbzMvb|3p)lXm7^uH5eBb|BWO<$4b5~eJ9XZfSyL3k zX-G)UN{)-lPD^4la)E_CDh2TYf-uC-lGo+mU;giakmTGe5Mr(zqQ~DU|1bz&j{o-) z%Ku9XF|$AJL-BO%c?C7~{Mj3~Zep(8ix+QSxnh6&w(HHCj<;_)xj1`+xjK2;x%fIc zySuu&U%TUR^Ny>t!`&Nq9B$vb3xIUE<9hpsBbckpJy%zsTet6?yJYX^aChgnjT*WJ z>iSkX2G&}7=32(M`9)jb;+2tWgp+m6C+ZpjG7XF^Ui}{aX|pz8qiwy?Xv!KRo0SIB zR@=Us4JqJ~1ybg%OvSmz2tkPol-d!4s*G33RZiczS#i3k)jJ zNz9b&k}OJ5dgZwd`tk*xbdjP+HkdE! z%9T8%wU4m79vsIPU zI1pQ-Oy|l$_s{`jt(}R@{TTuk6=YK@uC4)@as<73(m`M<;3r4epDP*w+XmRuVbEkWK8iV(Xpq{9@jn?-bQ}bHqBBM-FcLQp0}B(#EWQ&wV^4@&WFip_I7Ip&&OXPh(QxK6Hk*Zj#f?=U&ZnFyR0-!Pk1KACo zdA!a-QFpO?0O(7~&0>J7s26?hn>&h{+oOeDb3NG?uDR4oB+)|e|5v6)U!n2$q;bF7e}=k$41|9>4%+yIe`k6A{3&L5 zB|uo5m2lzQ%`2DBpFV~5>B+OFK0SB**r}sOPJH_5=`&}}ojZH=mXnW*`#l#Iw;T3e zx9#1pU%qtY=&_GJI&|d7Cx<>d`tiXdU`GxgKYZjkw4c4~;9zIJY29iqePd#e3SEo$?3YR}%KH+#>U)892)xZh~jdm8h1 zOYqeQXI29gJreV-1f|~y%Lcm@k#i@CdOI@rb{GZhW;kIXS+_#7?}X=o!722y z3#VL6p}7{;-pVPw9FcuDG{-I?_i9wm)mX-zh^)9&PFiN6OMFgvqb#&q;+IyQ%BcuS zWCpS7BY53m7~7f4nKm9iL7Hi&(}ZxGB)F^c`#hkBX);Ee-wWx z8F8sE8MF7{${)r_Y3{@gBQ2e&O^T%YHe9G9oZq1XZuorZ=?ImCT8s$}LxHM;(>Nj& zKt?NkV*qj44);POwH3!92cdwVBo+cw8#>6~pD-pFFD9!QV`OK*$QX$Yw}}FMp_1N& zK7{~6=#W|~2mI6cNG6;+ZRyNyRRWyo{JspX0zE5uN?gF>Ulf`fDn2D zP~k3mO9w~T313+<8_+i+zd@GX`Cx&6@u%nQszsvA3Pp5cVsdg~R(cYfnn}${8|d$S z`EB|&2>+t;e*uJlJLmbIv+zF%#D5lquWY=>zq)`Td;u6F!Xp6)ODRd$t~ea~d*jmq-x8m$|XP>}u4^JOQ_aKmcAMgIa(h^GyZdCId8L$ z*)rWl@2GD%rnc$Cq%CJAZ#_Nf!z*g*j=iz=q}uMQYCF!^?sqmm==kRQSGU{^cg`-e zPhs6oq2Gw1-b&}($l%;bVBAV9yq#5kms;aM=Q`%rI#KKGn2pY~MyI@Lw|ucTx6>|* z6P3tFjAw<%(}SYY!y|LUB61?5^P*$vP!A2KL_|@c9+}AW3CX?`n~@^#EPMDx2&*)U zQsEVnbu+itvsB{5X>}_Sx)%yOiiBWL@hU>QS0wN$MDs4>dlc|I3;BMP(xjn>)ZWpU z?m@TKjv%(oi!FAkZu2Q?yH_c3&n^N{n32VCP09(WZ+9t{CsGR|l39)ktcd2$P$IVx zCQ&MvkRidsE2QI&_C)$wXbG;8VsM#q3CKsBIIR>%)VIYpDIvS+xceGoKaL0b=q#j=j!;`aM+ z%4k(UPdEjEnBCM~F6hb|{AOW5+3~Y>r7bO~CGuP*okF2xrY13S(qDlPjr>CWy8Qdg z|Kil>e>n^KhYp0j+B=Hne+)v>#{2#2%B!i-=g*%%8GDwW7IXdT-NT0tSeYAHm>QTG z>RFl^O|iAKwXrZYF)+~Aww|)+{B7@mkXR>Y_dCu$_q+p+?A>Lkt7T+lU}&gsW@>D1 zW@2G(YGPudZ)h<8t%av9I$pbaYx(kJTE-R{W;3--{{{Oz-!zz}He;p1=A$!qoH8+4 zs&2e!^1|(twjY1vy>kh$~ps%-}g;VMGwm2eEJ?|}D-ZSIb5QAIZ>Lu=#_+>TgYPx;Sdz9kiF zmv6O?OHO+9by!VzM2#xEMU~Ld71+}4Q`_dAT9}+s6rIkwlgjdGRYfG_CZrd-C34{2 zs5*=vjY*5Dam#2zvkD9TMD(AaQGSbbZ3Q}?T$1phFe zH4f5Kofa399vm3s;}b&7V~vbHdNTI3tmnH# z-Y^gd@D@{xF-zm?yK$mYFqp;z2J|C@8ai`DL)jq6S}+z&8sG=uM64Z>%YM+3!SBIH z2!w9~g_!)z!5BA#7=xx6uJ%#HLphS+Y<_=wGms3uCV(hly75g+nktun^}q2@oP^TNQLcCtaw*!9~6KvH<`X^vCg~ zgM}Tqe8>7^9zY#OG7PE|MgR~NwGRUYL59NB0!eRSTOUi*UmzbW>l!Fg;;bo+ zr(%h_iX`17ZG$i}#&T}}@F7B!*NX5jD*u~ngn!%uIu5y4ONPHYRQ!&^e*%P1#s~bbY`pisqL7LZ z%Hy%8SrL90&)+)r>9MKS7E`Rur&?QKoHZjuBSU>deFHGtX-iIBbMp#_aC7%|aP#-M z7x2mc4=v3M&CM-MP0dY>O$_w(!SwYFb##s9zqRDVISjh}r?2dSl~ZwXG-BHl3ch<(%5OlWJ>@&phds+AMc)c3He( z^P+%AUDtTc5APZrxN|f#FSeK;$>s(ZHYQYy*vfut|9y{YNl;01Y;j9)Nn=DMFQKG0 zs)QF>B?J``&+CpT;Ks&KXld;8Zs8}A3VoV;d@FhGRWj!?5g1g!oJxha*o{~6YHqL^ zvPQoze*DUlS$mS$a zLZk~h7u^Q9FUIUr)3wwl?Yn$E=Z7EsN=0F%Qs@vFZP;@4 z*0nZ*T5O zZommofD@`QQ9l3%4AmjJ38DmqI`9vWHr~!t0Rq85#$~lA^Z31BS@0BYI~;M7Cy*5s zYAbfe-8Ud}>)I&IN~Rbt^<*~pfa1(*>Bw!xkZH+PvW)t+td=%vD|$`f%&GvCVjr<< zvY{wM7N*3?snr5Og67PEM#Y`JMDu1+}q@$;YiU41Py$ zqav%eJ)0}x2o!8#H;j-~C(Ub;v3UyUnN{DO!4-odj1l$OIx|jPcHz|5C$jm)6(vko zL26189TXuY^>+}y`AG8pj<=WC`+LCqhe1dda?JnI#(V!y3#Oic z@^tJuEk5+}#XBcY9Gzxk4HSfVJsn;3Nt2=TpWgGIGaUnCFnwb)J!3OtU2}asGj&5-HH*dCi+9+pe$RN? zvdP-h-<+{kebq;5YmQD>b!_tb6K`%hK4IetwPlAEUJt41e|T`mR&_PCl@XCsQrK#n zFIw%tbs{J`wy-I_fScGT&y}k@GP4c`hrOFgyUwakEE2_(@FL4w;|rS;Dnz)0vA#2` zkn0zk&deyi?VoZag&Ek;9*|Swn?a4rE{e!32+zoeBUuSeV+W@&eUtONDJ;t1pbsPe zx_?M+BR?)R-`*wmZf-e{3CJ2%E{?5`##hNf@kCb>n>lORV{1F2D`ZjSQuJ^j;)R9@ z6!b5?a?Yj&2?Y1rl>Wth5N#1Pve+s~aETI(((#plG*O5 z1(8h(ba<$dM$}@GBis@CGSved$$3#2Ke$qmUW>bCqFOrA8@f{AC#{jjRY)VNrMQ8! z9+w!&Ev9gq*wmTS)C;_<5h~%suBu5Kl0eC&(#BpqfzwJJ|I;(K_rJ4a?8VRh_rKV4 z;N$%V4jnr7>B$S1j-0%9?BvA{-{0ruZVx?63Y+bn?SA<6$1lG7Zt(u2r(&z}FrkUsQbH7ZB%|MvXX@18yTc5H0;=dsjUX=0r+s;H%)Nih7=)0T&iqsrtN zL?9T@jsPuk8k6K%Dls)0Ph{f7nN>_hgjplt7+WchsT8C81hIlht}Mb`n6v?S1E+Pk zB#CQ#-1hitDLFx!$dxD5NR#U106Zid86%Vk3X|SI-kHklVM&IV;z58`O1&Ioa1&_-;VM82@Rl#@LxISZQJOo5?0{Wa z^$Msk#GQpQxEJ@D0$*_}4ZoB0EQBL6lGmBr+>JYK#2tk)RlaBl-T~)4CB6CL9&#Hh zoaT!FjlE1>KMRDZs52i|69@8|RC&#cvbMfTsO6T`b>ZMgeRjTsg z6c^^MgXhu<3Q`J0WfjFVW*^S2r(EguZ^khxWd2WT<0lX>DZmKlSj_H=CkkJwwCP*1&R_sm%-nQ)@kAOGAq( z29{HF^sM!bEH%xhOtxKMv1pU|)VI}ir%j%=T5ZLL6W1P{@b-~OE03vfIHtDYklMN< zYAcSazkk`YfEz%`TYvTD#$1lt6(6mA_A`&WoQR-?mo!8%E7BX=i`)9vFW;o5rnk;F z>PS&_YPlk@L>yAu62@tWE*GM&TWe1^qcl6K&@YnuaSYA9Rq2;T&q`*bWS2xIQe%?x z;204{35&^sV^l&ew8SP*y+V_(#ir7`2e`ctA((L87D_`7d*OE(f|DUm{@r~Dvccx9RAI4FZ|`S zss`WR`{}9uAC5iy?z8WHb@cLc^A5Oi*NHE0k4nf34vUSCO-fG*g`PRt1$Ngy`RVKa zpTD`^R8tlk5fb9><9E+}c%TcuhOxxMk%i5Ve)w_li?53K9q_E5pFD3J{XV8lm|Y_n z{rvOCV`HtOKZcjd(&{llXeN=Iot)LoMxtO=BanmS3**jD+`}?%S-5=^^BHA>km6yY zB7hd^Btd@;ub0$@Zdta85P^1^~m! z)#ffBP=3c~f#N1Y=i1Sdc-Jij0 z6o|WvVN6*+i~zc@QqY|sA2zk4?mcpuQB#v%A}+5grqT-%5)x>+nYr01L&L-xA^vat zQ{{E}cbETV5dN|8_vz5TA6e%2^3Q^hEPovQ|6$|3|J!9;5k4M!T1rW>yX|)R)bXh{ z=2NXL%uEfnG(ikb*3{ITIC+w$w${uUOV3?*^Y96B_qgYJFVsIE^rO8Wm|GZISp1U? z588wip{|*&p~W;~s~Or>(*cAgwzI6J&owZ&)iSoywV0x9F%1x?VLCc9JR4+l!u?_lC$gk5zNjv!q`7$D!9DMQtp|>8&o6qfxH6@zBc)grQOSc2(Uts| zT1f)GE3vqWnp^A`mVLKK>__KjXE0JSm}diXuZPmWt_D-VZiLZqN3w2)Gq4_&doLRI z%eVz4c?Tv2`lp<947*0JjuZAo)MFAmKwWGV@+`hVfdMn?r7^XlqyCH?T`MV0Qw`_TbtUr$ z0UqS0lC+n8d!*$`x~QUFVebh1&9^AOat)tzbl}8RAh>wr;_Y3y+3cPmBY2Sg90YPz( zzWfOwS0Mi^wOTg#&Ckt)-^JFdQj1%@_~HxsS5JUCgFlZw1DW{4*PY*uB{%jZRAD;Y zT)+T2MIZtJW_iK^x_AhFQjodu!{&+xsNzBBL#`3h#e-DzYQSA8AmH-E1E9tTZPy96 z;~+2HFi3C$DWMy=@(0Mw74|XZL+~5O=AlbFP>mxS0;HjXpr|KPh&g+6`I!72SjcW0 zX33FfbRl#bC~P0bh-jejc(~5T2Gu3%$>DdR7lgE*Egk^gfcz?w_f{x|0E9F_H!j-A zdrR9#(A6F5-SDLiI@82`ED0_U0^UGD0tlIWyj0pg#1Rg%c-_VF?$WkCjsPIt%Mx^z z$opVS03k?55Rrg4fFEc?sIx@!Lb;Nx7s-37I|j=;27$XkK2~c-sl2zmV;JrNMF=2_ zl?<8O$vu0+t4b`6rqvgh<>%7!k^flCV)F*3Co}{Ivp*wZj+h@+( zd-{aCc=~}T3=Rr7^3i@PBYg`Cvp>DY|HHH_W@?*ahI&n78vr4Y4^XFWVuQ)+&87hV zrc9qRbIt-i)2Zr))|jEm*hb6PLfc|G++~a|`8H~ni{D)G;l!06Pg?%bq*aF|t@~v1 z#^aMV9amd_Ol{pKZ*DsI=C-qHn@+2(J~?5_`H4F&njdkR_Nj;IM-G=_a?+~!=@l*E zoTht)EosX8k(~o)%j)i=Go#CS2^FHaVo7|7G_qI_&XtCUx}r;Jvnl0vVX02V)tTiy zYEI?#z?8cjUSL&+XR+vBr8JasYZNi% zveeeTJo!i@t2`)`5tC97pT`tgvq&BMRpTF3D_TpqU?S1!x(!Tp8utJwXErfZ@(D&=4nQ4R~cXN{n%Jd*Q2J+KKL8`{)bWzfI85` zhdZkDhQJt*qow{AD+0*7d|3}mtfKOIFx(nC%XhK_y(}IQt`MLo zRS^{I<_NnBL>OF#$?svbbaF%;CGy?^$snx-1I?fcln>-KcV;)Xu?5N!X)l~&sM2Ot ztymVq@0oTd$I;QVwxcbU*2Ln_>71gNm>4=W3s^Wf*gsCd*C70>%Ku-3@KyN}SN@)j z_x>*md<2oZy`GaGw6k+Kas2Rf8#8MQLo-tYLnB=SBYi_-1ATp6T|G^kDT^*#a_|X= zcJ~VK@bn1@2>}pV7#o7mF!JKB_oXQXuH-N(pC~Ni2Cj_u{k!7t~_X_OiC< zB3@jjAchR!QYH*5=S5YE;_KycTp3g%tAua_i!B#LRP$r&C2_!JA`yI+sP`VFC@4Id zUQi#MSrthsVX|vdQVZ`Uu>xy&FfOCl>pxGSfzJ-kK? z25*RYg(}7HR;(X>N^a#Hyz1av&-d@>537-fEhUM|O`r{i;PM@%Fq|R8$fY7{P6+ zj!Q|Q=P_ae!x%iJ(MGg@`6gnV=n{k04j{b5`dKK zA@+g}0$hQDa9YwfTr3?fl=hW%4wrY0aHQBRU!d?r-(8SWzYKpd*4W_L( zo1$eoO~=#HgWIe@#&it14hOYZ{vd+fVHdd1;r$yQ-?iR{A@&uyXj-fnpUmkqBZ|{RI@3q~d zy#7Z`JtIwhqm8|I6pwr@8vH^s{8jtI?;E;@xSazWp)$Hu5LqUPsSw6h2?2y4&VV!t zHPXad8JVvxwo*iH%|M@Vt}M1%m{==;K8ZE$pgE$8xIQtNg{2h^DLFav-irI5Q7f90 zQVN{Y=z(QTX?X=PiA?X*{A7U&n3`NC1w{fl1m%>}tcV55)`(;4r05CWfO03crqj8m zh5Ff-rCC;d zuKV$o5=H;R@4o$#FOrJ{ih!^b7rU!t&%Uc~;_uzP@92>eXHQ%`dGgfBlNU~Xa_-SXg-~LqJ z*8BaFu?OG$nBFj)&QrnnfW&e^-?zVXeg1P+ZF`ouFQ-|RCmdkOMv3j304h9X2nT^c zB+LL*xSj_ULd@lf2gwCRID$kYSqQKPw$a6dG!f2}g25>m$v={O2tVQybmO$$XUK-( z5hM-)mVj6|!lgpRXE>`V?qkXZF?@`Clr9~jiUt5zEXe@;0-+mR<+KmsCGil(1r_!d zv<*RLd=S5vE$uIA?#5Pdvb+Z2e^UO4pTB40UH_9pEDz#eFoLj_nFSy`dgRdDISXgb zTrh9JiiJzoELgH`*@}%|ix7~dMA_0VM?)Q9s0#BUyblH-nZ?E3Ca?RG2 ztGBONvu(x7bqklQSh#fkx{V)Rx$Wxa=JC!u@9A4jn{1901R`po3dF4R>L#{en> zMcs578t6hJb0c$W3)|_?6F@l8)JEOX79H?SZ1n(>24?D}GbYa23=~vb^1+)+_X7*n z-aa^C^KIIi{pKwzMv$xIVF@ zKCQYnrLHZdOb}Djl2Fm0`uZEtBELKzdraSrg_S^Hv-JgBq zlT+?TE%(e|g;3a|-~9$1o)P_Dj6Hw){26>RCQqS4HV%FNID;!qtm{Z^YER@!6F|qb z^iUc*lWW^j>e@jrWHxoB)hocD4h9%Y=C)FG>{kf&N3L#Bp^!8BO@PpWA?YFX7#Qf>iNn!SHu!f=;{ejBI*!RCY;iO*8yBwZmV7 z-0A<~hsR@MUEO^y4zAm`?>=|zBVm0>pnt&8y?c&)^zpv^2Y2m#f8WRZ4<0;tVE3+j zZr48lyr02h7Ure@`t8VzUw*7EtAbxoQ**1co6q2b2k<**OGYRS;?75pD!WEN1lP&? z9)0&iY0u9otz8KXs`#q*=8><&4}Z+4>&V6w?Y%V7AYD3~Lu4b$LIKwc211Z@V<0IA zrz98Q6f__FO5sSJ5{j)87zPGEIB32sA_@!`R7jNxb+R2;MfN1e1cMg5MC5;9i-&-b z$W9`fC>V)SU^83N1AIaW*wPJ(5O3q31%oeZH8v?FdJZE43F zfw)aw82N?Yl`jGQ4FCm!g@8$p6hpwkRc4E#P>L!rUp!DK9V%CXlItrJ^`L`EEHl5Zs+`U#N=r-QFsZcM%#jgvs(B5;^N#n`J8m8xUb}Yf z0uZWO%m76Q6Dn2#R#XFiRThFCF~HrWK#p}K)N$U*CAvouYtCIbb{Y&FbnpsrzL zt!rYXV`x6vV$PfMwyQ1LGhykjiOUXvB79@{M{le;^ya#wAP2$TTz`DRwo8D(3F}Tw z+&)osb8{BU+-R3SgMpe3IW2>y0-&V~PR@DjEC3SEV%T-^0|K#%@pHa9H+^bSAkE;>Ib1`UHay4!f zC7s*ST6+@e6>thisFPbalk1g9waWPV?qq&lpuI)&ulEaUT3Dbf1F9V5TpwT)^!xUEC z9#ae2s3)nh6NF=YC1&W2Ywk{JRps}5nbOjBD>N~kQIVKeb;>iQyh#RM28!gp!OW`a zW{JF`n_9_py%)V^$0wFE=1-Y3Z|0KqZ!KOsZP7;S*^6!GEu6V{>6|62<}6&eV(rQm z>sRbKbY$%ZM{jxsFe;kd{G+2&(*r^yqheDYef1@Lgciz2;P;culV`VLytLG68Lg!Y zv|4IC{uIeI${e9GPozo(CC!zA6a!_3!9PX4+2TGpX7O<&4C0?yG6c>65urkI5!&Ia z02oxrxlm{ao&n^*(i;@1+;;qy(1bC}Ue!Sel3au%fDMl1;3OA8kOAIkyk3rE7*3IA zElL0zMiwRR0x$#HKv#j_!{qO+ofICR4p(r2XFwft$q)vCtH?!BXHGNp1Sq0cA4|}S zP7%$WaH$YvU)wOW054f$Rlcl;E`Ud?Sgp!3Vy9@447D8)v9!IXM2c}_(0yKpdvFRR zy#PE6E7Q?m);UV&_W=v@MV&>mE@($aO1jF39XGhYl;6qbDN5zNrEUH2th`1^jkwQW zGHT*L+p+g*U@kK?zm{871|UpNPiHf7UxV;}rMw2=e^LH~y}xJUy|KJfgb2do%%ltF zuY0(8y14s0IC(p{1-N^M-t&(N3QG(LPY4Z-_YDa5ycgu@82~JFa`JL@^?`5n_KqI6 zoje>}{heI{J-kBQy@H%v@7=OBMYmkGpEg-k16cUr{S(qGb@}XHL?H-f~i|Ac;l^i z)D|NM-&nFwZ5b-UH`W}UupU7Oig41#lW%Oh^ycQXleeCoxZ~pFy?4w{cux7)Zq|qA zPrD@~aO$IIWx<@f;KIfbT3H;cCWc-Y$gT~ol*UzvK>5k#-QIzTUZKfX?e2Pp$9V=u z-40FIb=Py{p<|ZQXK87fOnqy^wvF4bT)yGt>h`CHvrHlJ0ZNs_Jp)^r%hNM4@--xAU5s!{gbG zF|@!sVH&3*BAMfooF6afiyopRX$Mi;K$77cK67POnOxYwd`C-a!8d}^61Cko{#l^ z^Gl7ir?!?S6DT?St~y~)xnMYkQs5V#?w6S76OnexIbzT8yC0o$*|GoJyZf)$dxZx^ zWjlI@q~sRT>O>Wip?bw2uY0Ib-Y-%P_J8*I*T0PY^xNZd#r>GZ&J2DJa0^svQe$^) zy(*(Y!j^V1g#$Uf{wzU9aG6gE`W^ED3T8l^iiO3P%{~ zmHvCB6v<1Zup_5E;TEuQyb;r+04;QHAm%yglKwnVFHPJBQV+%l0|f~Zu%P21OE$t3 z4}#I9Log;#kW3;0?H~#%tvz%R$i9A1gj8M+uJ!>GMW|fq{GI~&U`gjlQO5}BTnMM7 z%Ao@J1GaQ9U)ERBHb}TnU_>-fhgIF9m7VuN_5rsFQF4}ujwAG?R`+x zHp(JESfLoKP>cZ53go>d?S0iuG=2$K5^1$0x+g+y9oG%WXSP zXmqf<`|i$nrp#V6YuUPaOV-X`v}*REHH%iQU%GbF;?75V>W!V3H1^${^v;DVv5cW##yY?Fx##n*8kC(8 z?9xznSwqiY-#0(p|M5wQtlOW(1!eHX<1v9$;TM(V8<{?O|6y;N-09}6ci;JN^`>{K zYwCSnoHj0Bv2)w*RZHKV39tCp!ddecy!-Cn>(?EVGHK^-IBwm!&%w!)Ti5i%ci(^W z)z`6cNx>0u-#;1q?5F4HwX&$X4$QP(D@&|t&ump?wsu69HOE!*l4?a@P>HQ*g(Fyc zr8u!h7+=FnuHna*wWQRD{Ib|7?DA|z>5Zh!^ar2ke*P#RGdILP%{7V|QO`?fmqaBq zJ(JnMQIIS-81V$fO{TDitr&$*^QfYRNOo-mt17XyDYm2`v8VxtjVo%5%dbzVXpJeT ziz%p2s1`*OwZxb5602HcDjEWkX*7CWa&G1MpoA1zA4NP;+t&BXGn@y;zn2#{nf`d} z>Dbd>o;`u{r!SuWhJSg_9uIx_eGOm1SKO}`wXw?TzkiMwejR%bH;#?{HumEAi>JR} z1wTDsJbUr@>9etK#>UE(L)ij!PRFQblF|uA{7R`*7K8ip=69Y&_neC(H z{a@sF3Q##C$4BdfkA%0t*93s&1oOW?-<2wN0NRxvgAW>JKP2LLT6UnXr6e0L@fGa zU>G*c5rI8rBbZjAsXJEyARMF%21pRHq&>I;h2Mu$my*8xwtm8!VgL+4Sgsr_XuHqC zP@<|b1#VXuV*;r>r)FCY_BRp4~!K$&8oxMP4N8iZjn4O4p`E(#(( zyNla;tGh-3ghkRm`1Mw+hLO9XK^Uh{(p}j(P|Iy_Fm*C70>%4-n* z_sXBJ_xB9`f}g+m?aA1SI%dv=b2qPFy>;!HJ;)(fj{sNCU@zZ@pwPIWpeTR;aNocX zPcL6*7f%;wZzo4jM;9M^C+`~$o_0=dcU^p(T>}7wKu0HMk4u+tT)XLX+uq&&rrn-h zA1qk1X6d>e3zx57ylma#6#6fBY-Zco&M=!f-*U!$OPlFHN4?prCobDJan)h<jVOSnIcjY-=({34QQtnwjc$N3}smMomPWbwlNyAF&! zc}}4u9Y6iaCuhzB;N5+L&s@1~@9egF_wMrIqOliGLPGshb2$hFC@-Eu3#TAII6CgL zpPmhV{aZo>;XzT?7E>ljZ|tVaMj|Vky`nQi(^>JfvY6arsDx#(W2q&HjEdCkiioUy z|8!auB|nQ(mq;s5Wt7v4Ygtv*e$m-}wARpaVSZgxad~5WeoJz5dr^IpK-SA@l;6*u z-#&j%)W;sb7#sQGtD(=o!MW3xj&s-TZ{791VR!e^H3wds;LGoR>FMwM?9tFSzm5Go zHU{4eet0_e`Hw&SOk}Zu`e#qaxPxEN<-_cbhsBD|07wO04-2|Sio3pMbU(;ZJfbPT zDo_knc0Mfa{)W~u3f~(d^6SEBmC<=sAn_S3oyA=bXzfTTRD`V<4iw-7EF?YXNzDf! z1X2=OjS!B|O^7lM#2>kU2fzc8K^7WYiCluETyrH zBE*$Qz+sL6cf{ab{NA*>wyXxswF~S-xBZsxJbo9zi6vGs1i){M{6ZIW=Ct6FEsck= z54c4a_U7>v43VO=qaVEkfycrwz#CjDkn}QmT>yTLxT~3e`@e&T*agUz?|%E0m<$<-*fW_2?$P#jL8m-OpS<23ynzh_X~FQ4s&z?3cBBQ z3$%Ci@OJmWIZ`)opL@Y>E`Dxsm#de9z4OJ3H!odrymZac{*J@ety`zgTQu|SwX+tj znDf^18S_?{+RmFkf5n_7Yi7Rnw(Xon#vIG+IuXp=UE&W9AC2w?EKYdl)FFx$cPWrsH~BPw8wvsj>0+q;;Q6T>pv2 z=96!}dqsW6X{~ooPTGBb!ogcwyDyb?enw*#uRZDHM=x(27~ypfrss3BYn1nLtMBEN zfA-_|{0vO|Nze8ZwM zb7?%W%-b)pxTMU>%d4xi9VGST3!jDu2lRDy6tZY?5g$GmvZ?fl#EdV09vl4nNkWw* zs+df|E=j0tPv!MQ=QmL380y- zUcLb*&z#+N=+MW8Gc!UAuPf+_|GikM7&E*TKOdE-uc|(edEH zgP(lz$&n*R_U+qu7|Q7*CoY^lcJ};LpO!o1t<=z08X?BNe%^6t^v z+S*p3V6g9N7Ka{|n4Az76dE1r?Ck91;dW&I(fRWh!B?Ca^OwE7Z1tJb7lNG5g@%Wu zB&F9>F&pJ=?R^6sGPzROUsc=O+S>Zl<7dA;drIdG`>^UG^P6G|>LTf!cs4gQzbT#F z3{o|+pe2gMjbc}ZbDBaJ+_=J`n6xs#Na|Jp1h+6|Vq{uOdO?Or0pKL(Jb{07DXKT% z5MT@pv_6mz5D1N=sv}uQ%Ewn+gd35aJQe6jkbQI_Qv`Y|HsaDCZg?RQ$K%FMaer=W zS7t*yoRZODs65;bLrO{D8Se0G1__HAk?{HJVql(!p~CiIbfN&^C_=}1rbLDQ5(3=D z08$xnz-G){WM(@PXFkm^L%oi%rIDGXzP_QRrlz*G zu9mjGhPHvGff*3dz|>OP#9G~amb%Rn&8aKEG^VZ5oViYG)_Tp^>$T@@)||UpYu+}U zw{~dG+o```yV2qudW&{xytPl~tq(a3?R9+Nl;!Vi{p3dP_hSR!{u;_C^v!$ijj*^x zzu@qcbjrmmH&aqGLqo&cWb$Xf{p{=QcJcD1prD|$XHEtB`h5P`qZ}GDG9m5jU-5CV zm4c*NWh_@7RV_%a@5pW&jxKJD&dv`@$jYG=re)?)Xq@vuDhoF?G)TS@UeIOjo@Sao)8%DFUw!dqS7&c7l~z<( zR$J8~7RhVmT|vbS?#xDac2g3+FS3;H6qQ$AE6B>PsBLWm!B<)*^YV^kQ<%T~@~c$b z`9Op!0=+k>T7NMpI?} zmtTGJ#kW5W4-dm1WF8~`^qC7^eE!YYv!^8uikM7#7NsCNpPQVQpOIadN-xZ$7G=_k z6Dj4%StWETo5d(+vKy&+mHuIQmz`@rH6bz7xXhw3T0Y1fz!;PHLKhE$ zo&y6o5l#yjTn6yQkf09(F7HW)Nr4DB1P}rd;S#HDlpz}i9SQw8vJq$@Cs#pYvLr*m zJb)ZqI#|?kKVOcC9l(HIB^?jol*ArW1~;NJL!o>GSjBEb^06d+IOiz?Sq4`J*g{Zp zy_ij2IZSpg?ii}-9w}@e#kIpWoEI%u4q?DENiVb%OE7RKt^&3X!zExOG{WHJ9Rn4L zA_BsZ~9wP~g;PiM+2$IRtpaT=qopno7lBb?+AdSAZW8bOxiz6wAi`(HCxwjd1uwyEz4G{U9=1|;v$rK#+DlTrjvBcwDm2FO)T~Gj4`sC zx|Wuvp0?1886i`U8ZfhLdR;g zw$*AK>ot1T8;xz&8(FL|u-u|$y3KaZ?&1m&d};mh>t8!Nhf2A8*M#geURMCSzP0a* zZ+?EBS=ba^*qYra0}u-3@@!Uxmw)_%*-L;lHq&NW&zybk(&e$|Ps03sVq>CGv$NTp z;>s$npMMCqt|`DTu&YDyrl>|I-Qo1o{*TDL1jJq{N@7>+|_8#86Wyjhz8`rGfuzK}cuyyM;fURG(_7Guwm1tOTQ7mv3Iba`UzgTNcfmwSL80t#!qH9YRlMpLaKJNQ((>EHADr zD=Eom4s6 z=j79ifB5eEufF`TmM1@a>_VKM!{g^K8tMeM?%a0u@^yFjEUDoJMJ9%YM%z2M6l6vM zK_kPH;_rEbu!avzS)06xThl6(B&O!}boW1dK9*an42({tWpmOPRVnn6=$h+c`;GWU!8JnM&&52H|3XRC56qcm4c9MR6U^K}f zPLmRQUg(m35`-x7#Dh@hwA}{^!YQc;!N^%t5R1?VD8(>rfJ`EwC=MtY0Ex+#4&&72 z%c!A%LpTM4(|jp15>vVpndqSo#ug8-BpBzDE>w|<5M!!I`Y?_*uM6iwI|d5cQ9jaf z$*`wL-h<+j-^Jv25_>+;1%f5Oyu0YnAi+Gk9BDsb5)>iyhX)nQdO#5Z$pEV$F#(*w z#UdgTJ;*+csV2w#g}E&XT1y8I4iq6sLlBE#p#1P&8BV&E$hu2A1~5WRYX^(pLBtwG z7ycrCXT7TbYVD}niMUlCo{ga~)7kZeh>J7k4o9T0n zET`(4*k~Kt>KWOZm`^bo7wpzpxJP}_ey~Z4_Ngy9sJ8m(n`@6vSbccH#!uBYf2yVj>^42U$er!yNzfW*P zXiQ{ea!^>Jy_3%;C(g~7GZ&;4FmC?*`RmrLTeD`(@)fH#ZrZkK^Y*1nR;*gN4j{LB z)p~#*vT)7%wQJWy1&+YK&6~D^tzWlg?V3%i)@_7;g=H(&uUWeZt}a`)%s|iZO*Qq8 zPkb627K;Htuim*qFC>*x~oz%coGXv#3`tU3R{8`O9y=D{Yl3`-grU8;jzqf^&)=KK!Bo z+o$5e2OXn7`j+sWa%wpx^-|?v?>EmoKl?SMN_;!F`c8V~J$h9jr(MHh?aB@BaEeu- zAt~&#>X1r4Xu(&rqrf)65>?m(5{+1>L*G7V{G*X1%i(w9`dC+5ntVd6;w_Lyr=U z4SBpydMkQCUF`G+M028Db0h57pj?%uJ{+(_Tb#?sKx zP*+!9TT@3{TU!f?w!zZn8vul^Zr=O$9RMvjciFlnE7q)9ziHv}wNvLWpSf`5yk%?V zEm=Kn?h?xx^G(o|Vumh;kFhbdn4)iDK}bL&Z9_v%eSK{MGsL9%O!X;CC(qliv23sQ ziUazq4ytWEI$_7jH{Ush_Ws#7-aj*8*V&05T-4foOLy;0&3!i}?7yjT^seE-%j}M! zyt3LgUJ)4$!k?dyMIZJ^CiAr6ZxT<%h8`ahozEE{no08ykE2 z=;3F3_wJuFcmDP3S9^PucDF9CU%ld;9Xt2z{@|UR+u^u%HN8rzeq6=7xkIIb+Ep);rZVN8`EDzh{y zmE#kf>=&Nq5t4c@D#txE# z4HR6tV(s$fE7z=92U>9B`b|K@6)RT3DIjpu#%(~sjT^QB2$!y1@%H+4%T}#>d-eKd zE7vT3d!>ct6l?3*E}`)mteP}xc{;5mCL`ZJns&u8@TQBGTTp~&R7zMnCoGN;n_B1_ zmm8SEf~$Aqif;R-@FntJ$Hoe~zKEz0%LayDj6MGKw`cJ=g^L%hPxihJLa%^Par62O zSHFOW@X)%ZRyW^(=%|FuEGj2G;uw-yJ6KDMp}YW)l{%ovy@QZJLeEjW;F;4&2QNjnA z^~$W8p4?&Qsq#Q0v`b*7k3PmcJxE%eAxi7s$4Nr*);<2gF3WS zDu*gn4{?1_HUQF%@NF1?8;jd|D>`wqv;-$SG1o-3>VCBf2LZy>bdA*aJ*reaDs01X zDmn&gRG&j1PO`;Vrf zG5M{<>u$S;1x6(K`G>f82LTB8?A&B(sAp|!WoT%urKvZ0qUPku8ep25hRauOy<+cW zZ|D5(&iCdlSTD7+6>vSXk+rnrj+a>zPk8w4SA7Gk@~5W$H86XwTiCGk1;NjP?36H^5PM#zx)Q zn{{Vz)tR$ZckVXB`8)L&y*qiqI~q&&Xf52YKmWts$6Z%$KBE2pN%tB_QiFs}D-+cU z1EUf%D{4l5e%jL0o7E)AZj^oZe2gz@2M~rQX0`Ad2Z!$a2ZXL%vw8OXh3|d18|+>9 z-|oYpYq#!v4~`o)Zhe2(p1{E1goLpBz2i z-!rW2=ziy&_vX%fYuUN?Aa4TWDdkSTzWI0*VANs_R?$ zKR$XmI{IZ9xAoAWQ<1J$;LnJiR~Q~1UR~dmm6odNA4p8eWYRg&QStd15ug`?17nVS zvfJyP``NSS_w0Nx#LJ^aAXa_zqi|^8$+KVCyrGuv!NJdeOswoqtl+hX+aLY(6HETE zT&BvXYl<(fma7K7_-%|X8I7!J56mr(&Z$W+5~UV4p1k7a8Ic~vuFh>$aXKF0>Y#K8 z4LBzq06B<8OoYOlGb9)V=GBrRZd4&yh|-YokszfW5Dvr~@CoNUr2};7K)!sqsBPSp z0wBj}A3>o>fD>9U5H>OGiAZa|5B!4%5wocnToiZ5U?|Zc5NAd02pAHCWu3!?9hhq# z?kexPU)zgLgm4ug2W$gv2f7fX9t?}?d0jB6=^_4$j|+;9mF3{CLz_ILIQ3=R%G@WD<)109fypa`|J zbkrwlsH>|_nxw9+W4Lnl_M49P9PFGnu3cwlISqiPXKn+K)3lhXZ#e_NrfX)aXKJl) zVg+hX$Jkce$VS`1L`&b;&=N(VjG%~&^i$}0W2TlHscw_LPGbJ}L@ z*;^)Ct(?05Qc#mDJdcx}&T#ihI&jJTZggf?I?X$s6JJ#K{nO_>p)4`0AU3nW-7CP; zKQcADAUcWu$+=rIm#&?$bnSu_I~T9mHFe>JDQ~TtzG$QE{IyUqv0XS(&q~{L>YAgM zqL@{M4RR0v7<(t*fUqPAyZp1C$9li`HiTUnl2;R%-w?^JPAcKWRtkc03uDu1;W0`5 z{k;!I2S$ff!WJ%zlB(p_*A%l(e6+ivs;IHH98}b-IWym0vu5eK^($7b1f;$F_S>5_ zZ90DZ_|c7xGdjSanF7RiRPiLPwcRIj74FRrWmi?D5!GW^o6*rs=!K zFCyuD&t$e(+4IxWu~@dqBO)V}R`S_5zd;{~>RZ3q!uYfTzZ9B#c=qjku@2q|acKo% zc@;kFN?O}zq?!6h3ZyA@|fkH*&WN;4rtfi^{`2pV*lI zVIE(_;HhvsC@K3S11u3bR6qxkuh5y)kzfoF=Ko^|RnUiwsRoY&BPmN>0-ga2fk|*m zZk8#K_cA3acr-i()L5AU*9+llA+c)|BpOms*bQhb?;xB7C4IzFVsBOFFag5;e7Op^ z2o%g0sX!3|2yyvPN-Qf9tAOMhA)LaPC9)n&wA9kUQsGN*r|3hcJoJUcQbV z{sDo(AMbh3%+%P-5<=FV?nMqh-BTcgh;$S({AeZ8x5~O@G!_&FLG}r>@tUu}Ne47T}-Zj4k>z zwiqsa*JAZSvt@gX=D#;*#ZjZTK3sg_eq!c zj@rF{%FI%~G z`Dy^+s#U81gdhZge4s)1@87$3&xc#KZut1)gZuXFIdI^>o;~|OHLhK=al`s8pk?1) zxoO3UEvr^-TDENYisj1;^$p%ooAA-;i(zq@ZeAhRZ@XNxb9eWRz2o9{$HmRZJKR4y zJ&aNiLn(?$%S)oLQnDE-Nz{l;Mqn~k*4_o5c1;3R1|`3#sp%>FuH*wDq5cOB9d*2N zdZ@2ADJJH5|(g^lp?d^3UIv;AP4;cpU5@=sIh(k8&DAELL~#}pC{@CgBCF8L#`JB6bstX zBOZuVAQ>b<2%x11d*LpQj0j!!l7&zQ14T%JkX$2#r-0NeX~%SfB(wm8Ksa)Z5c*ej zkCZ5I>nVV+P}Tz=1j&d@6)S;!rEP=7lD;zeU`_YKYSn$9BRmUzB#6~S=!RlcJ}|yU z2t<_j7E5}7i)ElM0l@7;-0^ABLG(D3sH)`s)$K!t;{GDpV2QlrLYYeQY{K@BF6CC# zWEG1-W0F$iBBAa4XuC9j8%mr)goc$tWGrWC6TwOea z0{!=V@Q%5Owz;W=k%6I(mbR9L=H$tfH8r($bqtrUSbP1pySv9d_|ygv8kkQvu$%%? z&(N6I7h_}v1~>%MHnPw%GzZhxGuJgRHMX?Ww=mZguTLnW&r0 z*08`mqIy>$-1p;nchrD-K z+BG0j^vYEuZ9Ss{kG}8w{CiGqb7(ZfCpy<9GCd@XDd`w}aQ~;TK^uMjbJzVZl%o&9 zx*vYtHaH~i=^gyytDnZkKL70J$oQPV#Jqq+Mo0oXBAEk3^o`H)h{^E|N_TRP0`z)@ zB}XOF;!-)gKe=Kyd(Eot2X}mUc-pK*t5(*~sy#c32SFPRv(s0F! zRm+wvoj!HO>Xqx_Giaby10s{cq7#FoQrvBP=?6 z^z#Qlj*X?3^VwDP&t5zp{_fXb$Hsns@jR4S8<$rq9{n&;qn$OF=UMLi(W& zJOhlu>8nd*H{cdf4fs*mKFq-k@0cB;n26DXtdijR95j+q({SQZdA~$4iv5ZCPb8nP z4?QRdq!AHYdWm%N=mm)GdTlT`G5<+~eL-giV4D&z0kME>Ro$atAPQjw97NGq*);-K z0)bd4Q`Pj`M-K`ZP}UC^D+g+}^@CM)j=KipUK?3&jbflqb-$|X0Xz#%D+pf;+$keb z!4Vx0+XqT82k$^_&q%H6K?%T8+5;_RZ9SC=glm!UYEb5{PiIGLrVI;wO6S7511UcKjgLlKU`i- zjY4_;0zhbY{rboI_gGsPo0}P$TNs;}nV6UunVXw}85-!!S-9r5qmO@ZoR?3qr-#oy zFYotuY&AB}hAt*1W=2LPrl#hg+YAg%^$ko{u3CTjy7OH}*Y)ey8yZ^bn@s}{>YLf< z8QJO?S?d^Bg6SGs>Anmi3Z|!Lp`&kNWM-~!Vy>lcs;zGUGEqz4SWDMTOWQz8TNgW9 z%u_d8q-nZH-)gb8^-5ivwK}%IzqQ)aHfl}XpfPQO*7Pmfv$tr^+NMA69XOgT**$IT zVK@Q^HRgPH!zH@7lDBH#r%O-SC&~vs3aafhnT}cIu9?Lt6|E0{8-s5-&)};q*b_X! z=@a<6fM;0$>BX45r}tKBsS~r#o>m%P*ZSx=wm%+w`k0s+f^QV4vI%?k_xBjWK_G-E1amc>BY@bX{V4c5(uTWRZXGyLipU~jLe*U`}TeP z)uWMt{>|$)&7HFVD7a`DNWm4WSFa`42sdoNJu)Evwrtt9X~V{?ShhpUj_vQQU$+^^ zw_)S9Rcp7bUAukl+MOFW?3g=u?$#~qM@RbMM=2_&B>Zq*gi6xUTdv@4Onx;OK8JUR1X9GfE_>=}ce$w3rMw zy-YwW5!>BM+J4~thsUp8u=722-DC6CL;H`MynNg3?KKBJ*mv&48HfD`uY7XihO=K> zdR|3ZPML2|s*is{VhV#@STi{K%@5DV=&jvNs{3C)8>7_Aiv^0Wo;(+S{y4X(clful zZ=R0zeg4DfuP^uyeon6Kpox3pxXOe^WtMP&A|9X!`m*?_@Nk<9;p&cCP6#KzSI`2y zfkB0=LnB}h7YK<-PB=yQ5l#_cPyy)%AVldV!wE@X9_a;$>JS7Rq4P)(!V$$O9{qVSpI)Oc&y`B!Cc5hbw<_kcJpg6!*g5tR}`(1A|jsr2`ZZry%~&|6SZu z+@``L^+Gb2eNVY^5Kdu00-^x30nmS705$+25E0l3a0L)@dmaD?aZ9Sa57-6|;s`t8 zSl%&M-8BkaEXIsWKx@p84_GY_cSB2+asa5wYE_o=XY8CmI> zP6ZI+qM-26&Qj z8t9qYnoM72GHboA?FtR^w=^vfgt}AKX-{3JGaV(M*37M1v$tu_+^92alir*yP%&Hl z-t3J>r?36kZ1Ju)XYX3O=WIbqJ&jtu>-e36?jeahSsYxl z%F|1@N$iTu;)c}x+SL3yuq0M>azR~sX;UnxI=oI1P$TuOYz-=JjxOTH(9058<XMn&fvk$q;>NIQVMv82sDu|%*c?-Z33&ZV8*-`}dV5tv zL!JFYs=C^0Z#QRNeN{q2!mjsswl>u?)KqNQuzty+#fz3K-n?VW-o3lGZQJthyF1sf zU%P76iru?E*s=w1xc1=w1DiH&+P)n@NHX%Box67Ie1GRVA8gq04rs~^>$fjhuwcXb zRZK>1OJnWTYqvMAUYVU3-@vV}sH`o{FK8Ewd%LB6F)^>2>~ztiU8@cseIAHamsF8TtIj@1H-< z&VRmgW&qtr?qq=@xvB@ckevJk$bmr}#3F+5rKse8jIS|y&d|~fr}!X$z%Z|c`6WvKs!)la7r?gn5BeM6+;X?yMcKC zITpVY7zSX&eVgdX(9IEd0Sm#P0;ePh0g4!ClmKCwVt`;|Z-E#D9D)!427|o98`4zT zLn=aI5*76#iaZ&v9l{ZK2|b}d8HNpWR>=E^wZ<-Jsp%T3QevRKa#3f~z=PveLvJ2W zeEYpqA!%9E8bMl0YDPu^B`cf7q|#`apMUlReu%tyjK|mIFDZX92wy?&k5iqbMg+(W zJcIAnf9Uh|om#TT-z($YUfOu;|861vb_ozZAA9kg~e$}c?E7xpY zx8c2wTR+^q?SoBQcWvMP!QO+%-R}hkhD7@XhWYpgy1RKFJ9=`x!<^Xd)2B{`ugVDMmBopQ*=zN3{0khA_PM@NEm2A zLiw3$=~;s2GqticF}DE(YU)}*ixhXwgJp=#ulA98};UG)|$0m@2zcyi*`=i@X_L3XH4GSWjKHDS(niG ztUPK?VLpo+9F-LsmlGAw@Q=+3PtA+TVudB;2FGWIB;LE$gfCOq_GA%icnwZ6i&Y(r5(Zl0%qZ1f$X$29f`Jrj-h|GepbWUhGD?FVI7LmaT zO=U(VQ^S(-LX+r`nFZk~tf*8@LS|u9GCew%6&jmy&p*mNFx)vX#3wY$HzMxcqbH`k zwQAn-wZ<0KGiJ`-ym8Cog-e#dz4XZ80~arz|LCIwCr%vu^wSd`ez@zTOXvu6*%!u5Oi?*HhcLwolfIC<*Whr9Q#S-WZLmYpl!UI{>*sP@L2Y7>v0xfT?Y z=HwZ4<+}ZKdk^P;*t=e#p7#RWd_rR4bHkI^q4a86+h{sZmD1Re+S-}j(wo8Ul=nXX zW!(GV8%g&-M{j?tqQCwABSFt_b4QPCU|2jfC>S1X9sM%BTHruwajg(~H>tw7J$0Q! z%KpLb;fM0k&w56`P~HEceei+m;a6P`9w`O}2S@Keeg5qGu`!@ae6=*SQJE?G9J378 z36i;OxdK&Aiz1sV$*vRAn&m*6JjoD6ieB@e|LD>oT>RsA1ML8QAlIm(UUYQ;kq4*J z0rK?!WA88Eqq^3hbvBq0U_307)QG!WdS6ex9XDQziGT8a~hySvM{yU!#O*YUV} za467H9KyWU+AB;soO8?j{Cl_`VSj#Sowe6qd+kX`_ji8RG7e$mu&_bII0W+tr;vei zpdh3`K@b3pgVqH_+_J?*(a2ArT|Mv*c}g&y(gwsr5L8n%oyNmKfr4NM;a=3mXhzsm zHlvZEP!83=EUH?dW-ovc4ST6YmQhNCF~R@YmXZMkLLLNkl-Kour_66fSHS|pRb^wh zw*9oS5rtdv02y^-KePbkD`-IxPJkSEO(0)MYkz6yIZZQqA`}+34gmbXH3I+Ol(2x% zLK~W!(Sm}a075W}Py&un+tOdqbXsF-E$ce}k+Re7i_F*7eS0J-K7%iluo%_4DjrwF z;jrL))es255f1efK0E&H$I}JjU*2xVZN$8PeLUtGZTc7W|EbQu&`{Nve zDb`3A56}qb@u1;j++T7W2dUFI567_{!yU&CcZBi@Bi*3pq|t8UM!Jo)8{=U&Y5vFs z?~U;O`1ytJI?moS>h*QQr>}l_DjFa&+;bIZ)U>sZQ#XJ{PF~|MZG*!rn>^-zG;{g4 zqhER3ec`9y`6ix-P4kJ#@Q=@n$>fBkF+74KkzJF-0&#B~qi0sHzMq&mRoYv|c zJ14)fbn%*Z-roME-<&mVvggO|eZt@? zv#5fg$mFz4a%fE2&pr`{{i1Smsbq8KbI&UfBo~1f;Qbt$8)w0#mD?{xr+f&$sjo^=O4mARNhb z1`km|SgP+A)S<260b{@?U>*n%C~O$u*YziHn9u7f|PWeMV*xOsCN*ttz`g%5KbWj*prxA3(&L*RG%0u132u+Z$gs})%AS@ z2+w{3ApAV_^^LnvCS|0sgmM-|Uu$M@C2TemOyQ-o7cmI&d*s>i6psHX5dIfI&VR+i zCk^70SO0q&y7}LC485cV(NG!dpiCtG?%VHuPWS}`h8;f{bjm+0Br+b@79Nuj6Q2?q z850~9?;jK$79JBC78MW}>gyMLdaLZ`- zNl*d=9pU0O!qxqGcTc;?UeC?i`n=bN!&ZFu(uS{JSo68v%1@qO`T5JMzI=Jb7cZ~) za@6Xbc5m+nJ@?KYNQZxP&}FCZ$j^?y_`$(9_J{wPBMQtD9?9YTl*2ti6P=_=j(Iz+g?{69vb!Dw(ZvY_b;D4yJ6M3H)qbC_2#@~ z%U6H&(MRjouYd2o_g1f7y>jJB;Ng}nTi$*5-OoS$V%xTNKmYv8ci!0s2>ky0-8*)? zzy00!KKl5J_3O7T^UT1K$1`(`}*S#i{#R~H*YmJwF@}h7PIBS zox7JWU5krNj?H7ls7q;$XK2-Z%!+POC9trIV(g-pStQl<@S;>0TGcfzBCt*RW_X$M zYZ`^RMrA_>wM3t()~Cu0wEVVIQBj7lGDB=+msw=S2Bo31z}T(S*Qw0S5?zC!x>2A< zYl%S$Z*6XM6PmzK-OMbn1FHlgSAY}kVHw)Eram+U-T;MYRWp{UomzuVk^g%%y&ZuQ zj}9f4y#@gqL3q?CQ~-1V>agvD45T<%3U&}75PYEltqubS1mRtC1*T3w7Z^USp&f@t zAp;HKfGTkpikd}6juf&4(pMraVUSJ9dq_qLw!0I8NXXyf@X~8=Z zD|d@5XroCPZE&=vHM7~`MPO+^I7zsdy65L0qhQ}E@w3cq;nTz{sF23|l&iozw zlX&7hu~tgUDXXbr^Q7PiIULGw7cLTCf}Vv&pWgAmqa%Fmcq~jx=<%_mkC*>{(NJ@L z*FpU4-XFa85PdHnR7z#veY^X}(UZZ!(V)PP=#Yp+0AXlULP&UwUr=aZV64A?WI#ZK ze?SNbKzQOrFo5s~*hjxeFoLK2BLae>`~o9JbLuF*TVUZj$<9%JRQb+j`5f} zX6!4Y-8~(~PH`NExQJ#Gj-8A%BV0X3xwyMeoH*HY`sB$oCr^E2;?!3?roG}iY37)5 zlgCb+;xJ*xu!-|ter3%IudRJ?_By-yTkRIT``nWEUt00;%d3EepATF8#fS~xzPM@E z%Wr=_eEXgkxBlSx@gdi*eH=bL?7H*hmWb>qNl83U6T(o2@U&4}O}wZmL{J>W5eKrw zVM2ARygW)$7AY)^5|k&)^l_rHL}_J;q6U;8sfZDkB`9i=RQhyPS)!yUQC6I&C{0jT z#H-3tDzAuAmd2>dBW0yA%8JASU7WfmT2UD#uLxJ`B4kw&!qNyyd8E1~Kmy<_ijY+V z3i281;?|aimPSKsTZ6Vh6YO`A$0YxH;K-KEZwt9Bg-En+<=WYA&RyiSbp9f=rVM02 zix(~PMq@$Ix}#9I477CBhV^f+TDfk;id7poZeF!&&61_d(ZJEgtClTUzs!5ptT$g@ zxonX}DUwULJHPp1)nc!V*l2}B%4BdU*|`-3+UEKOlfEu4EHR754;3gVwf*Gc4tf#V zs9jLgMyf&^I8e%($Y2l+oq0tKndNQd>Qz(PF21&9dV4i6kf{et>7%tA;JXm#z-2+j;+Q9Z5E=o)SJaGV&0|Lh2rOtuE)bse zI17>gYwjy-?=S8+hucxscYz}$W*K&Bj9qmX&mA{k9l0}S?z`V5a)lYZd@(Jn!ldU4 zq#Q1T&Ca`g9==))4nDm5Y}DveJN|d*BgFpi5gKh1C${7~ije>7yWkkA`FCrm_5Zbl z7$q}keE=YQICvjki~IKc?Bf&Ub0Rz{GCedrEi5u4JSsCPF3TSf7!)1g9~%;q;O7_S zbJG8mZ{P_Ze;*(4d;!FUJ~799f_;2L0H4Q?2mJc$$wP;H;p&MKCl)T4=iuNDAROgB z5jj8SDfTXt5EtD%iFx#B9>vHp<3~7+gLI^$i?fTHn~S@P(^xm>3GVKb$9j0WjDN** z+Uu{qKHF{F)ZtFk?I$i6;kod|32(ZLU*hh$a_qF#&Qn*8p0aYJ=kj4wmydu`&oyq- z*1Jqw=R9rQm>CSWO8m?Ha|3$oRCC`O{IoqvJ=udap~-+ zbXIgWJ2;gRnazmFWyI$(qB4+TGMEWD+}KPGREWyrgc1`WP={ZhFgA-9PZGuDiW76h z@#*~JTqc?$pGk{NrzB)B;?ijG>2!EvY$glR)O2Q28Z|x#HJ(lrrQ~p8a|Ln9tjH)* zR3a@rnG&5$P9q7|eXw)vEA!{B*kJEE_RTr-*RI)!WSHfQ4QQmtZ;~Em^U2`QqivQ64J1HEXu_!UZos|H6xQFYMZT zIGZMnh|dfOi3tu*3XIJ@;vXFm8Fx57gU(Xs(dE$s4Yi@4T-C;`YU0&4gBM}f_fqto z04ItC@kwePKx=iH(XQ_Ny3<@s9~d%9ky%vJoj@-hJ7v>X`%dQ9H1UdCB=rMqOE=5d z!?ScTbUpOyE>3k1ud-WE*~NqQ%NtnLjbaqr?8zx>qZv-KO=un-G!ES5nA+GiJqTLH zc4kc$|JFTfbE`+PxR&_5QA8zBrko0T3GN%`Bdf!=>~2)Qe}& zSRbMn$g?iOr*=GL5I#0W)W*5TrVJv^{pA`TVeh}^Bk?W&CJjB~zjF{11FR433=(7h z)ndV(9}k3_3g2Ka6LPL^5Lt_IXl8>JZ@be8ibRzul(V&2n z{y|}p@IDO-iI0v<2?>lM_9HwAHu1Pmws`btx6w|Xqg*DBcJ;7#8js=U z;5@<6W#Z^D9wQyb4s&ovoq`S?qa4RMIJ!DGI5{{t*gK3K;V@>HqcmM(SmgcJ&rP;9 z*|piWjm@@g+ik|?+S<0QHruvs+txGp|GJ*{^YVA*d(MYGuva%x6`la^WISDm4gGbttgGj7+t<17Ngt>trG z%bdRPi7_Gm^jwJ}nLBI0@0|Kc3K=ZV5)8&@x_aFh2X|oucE)IOe|CrOD#$oAiVm&j z^{iQ5O98zWjqo*A~OEUHO5I-Id8mvolKf z&->GyStQxKHlr5 zI>D+86U$}X;QtzQ+D*)wgZ|}aDJwM@4B3vVz9SrVJq(^zcXk+?B4?!}NyN&J3=fZu z71u%(t0lq?nC<_twhJK6%9lqQjE&`q|5d?3WHkRwRqsO$h{MU`1VdSM-QX0y~gc1ic?A%1l)9GFx zOvT2XATqNRw%$}73F!@La^|;gxyHnBF5a|5Ah0|XE({Nb_Hq;QE;1e{!VN{8RE~|5 zk2YTjJv$v^m3Syxz$5^~X6gG`DHYoYR=SP89u?Uq3{zw28V0Z)M80;~eRWXTFw8?I zMxynD8Q1ZG;*}V)A?JcYMr?YaVUKy-#>$!QT?wM8D3vp^ks#;E0U<5IJSM(p!yG2O;!hGA_~AGQKE1!pzrq03PTjgLy577Rk=hHv~~M3LQI{o8&;vbyn& z{iMM&AO?6>aZ83qzNT&1w}W$yM29^j!R_-)1uK8;gFa zqwb>aqbv9Is2|XIzu=AGxMewa%u+RlUk>FDCXg1B^`(a zr}_PZK59l17Vq$nR1W01#rW(-kNG%vMaRyI>(jvu0WBu~FKKyrYN|9m^c);?B{(IM zNJ=NsUF)5j`ED~+FMMWJ4b)`eSZSj$@w(pK7qpSwX?00?!D{`Btj(e`zv`N6eyK zxmELkLWzu1Ddi)2c=~aVbgbXU85tR|T|?#n#2}-zIUm?1H+o&6v{i+oyteVR<1lA( z*e&_Id-Qk2Tto<`ToBY|<=(x3W(Q$v#wDoyfS-t%vG4gIIMwC)Vkck>caaFxefwxb`McE# z>Vn*LZE-FlleA}c30t^;a3#@63140&l3;fKsVrS>2Mwp-FDx#rVXNp(4vg1WP#meI z96@C51N+s?6J0u?b{52LM2M%MKx1SDWyJxx(CvP6|8xFU5_64$cN5H_K87lY1fN#c zt~^%LtHKT(``>S!WSfY`0SV6&R8X#5u*)zQFxi>HgAW2C*zRXkQV%i;d~IYX=5<8d zeUL}e5E}#wbRo16#VRr!X$=Nz5Q`BHAX3bkTp2^DTNhm7OS6FM{KcBC> zH1j;GyQ_t1Hf3oyKo|-!GF^DP{?=!+f1icaoAG0@frlIZ$?4%S=iC zpZ(&lp`zp~v=jtD3A#TlqTf!ZVRry0pTs)RBiG5rg_vt`Vqszg?mTxd^Tl8+Pyq9K zEI1UQgzK6q0M0Ry)V}BD&Xa(|KrsMuix6|$QCpR=6U6ia{89&Nu%Ty@|SyH^y%pE z?VC9)&u%fpgG$W15zL+y@0&F1l{DR#750-h{*zhywKo0~m)2go5=WoCvDj7(}tGUya) z%~^1N-&@YprvRr9oq%FD~?nCE`|Iv`sAqED1Wh6jA^hoYRA)vrv%9LODw zXVn?)keQfgQRZ-V)_Koq765VkH7XauAV3bEI-{PDgE3GkwUFoAW7QT2_Gxt|D1dkt zbqb5cW*!3Oa-79$&}Ud!nqPCTUwM4i+kceJFIyqU<|JpQ5NG^Bm$mtg=%QdOF7HDn zmj|TnNkFxNolMkRh!_AqUO?smcG228_Hd~x{=nJX-f#T5UDRu!l`B-j(o{xZfZu=Q z)h_dxFSR_i$l>EoSImQUZwEJ`DQ)P+(nMOlBO*s~MNn;LM#pa|l3IJ{(a?hpi&&Vi z2Y;dH-}j`6hT-c#f|e@DfSMq{4_8o`B)*#n`#OGKi=6DN{|pU}&uBV?Syx;asG z&?e?##8Wwo%on~96{5B%vE3$kF_aV9ryv-#Xw8I44jHwBPk}iCQY;Ew2y!9P=M^l0 zkg6OHHaf4$A3pWMobXwkNW$ib14WN_&38&BBAERIYEcSC*nCmOMUYN#@&+Qd%t6Wu z<7Hqq;1IEVli&+Z!1|haZS%55F?brHRg)r=V}wL1UO+N!m`xIR&t5m0=-{&NPUQRK zQKx)P{cR4U@konkX@pWA5cAu}7g(?5&oHI4=X~&;FTo*^hcF28eZ9Qoaj$kx&sl9hPy4{(1Os-c{og-A_jXNxH2u}(vACDU;j*l! zuWoBQ+Kpz(0YqaPEK3Vphs}x;>+hm{?;Uw>^uEZ9+DD zJh@KGM=`uPs97-VXg|!!SPXdEZPoANylUKPcYH1rAm4hizHfsS#^^%MZD+&17el&9td}xY9`Kr-j{_ws^U+}S?_IbG6wy%6h zgw>?s_xu9yI%p5f5`*YXP54XVOQ$^t(G`ygQmQY_M54oXFv_ z11a(dNEv`qa4QDt^*DxMTx}6r$T==qePCM_=cUwN6JZ(qk=K;&E z{3&$N@{>pjQL!;&_i&0l=L8spc~0l*V6neSqUvv~Uiw+I$i;KH%R*8`*oq3i{sTNs`H?24cBe0JaMVE#5XgYb&resvtqfK3K@2x-z0}}$G8o|M{-~@*#;{_)ztC4l#NpOR!k4n~WK!MN| zV=Yb@MnMMCaBQQ2@HY=Iwr2QW0C%Ke%@9w}9#8-G;WvJ3Ph62)KbieC zCZRZVut*qh#9vq6`VEhKs;)-osp#c6y}Wi|VXnm`Cukkg*jvP~FY=+UGvNEt9$e-} zhl?lC+37i_MO^Hj)i0KWNyTD9OJ;3X8ek7I&FU8O@{$lQKfBG|zk$gF@{OT*cTW;7 zpZdKh<32;5lW-Q(ZjsjN?@yPtDLX?-xq7XBwSAe+$d_{$;9jQS?ik5LZcqINsM>%m zG#Ue5<7eEbodgF(hrOZZ1W)|E!*kZf74zK6oJh9TG1HQ>eHBwfq5~G#iDGJtX+EXz zr~LL=eVA8hDcJG3y8>|zPxTY zDiXrUpvaYlQ|wEWv66vA02Nl*Mpoz(LNW5@2U}DYVn4gwKuIlYOPBB7C#w&^cNt7y z-gl!Dd|a?I^G!lmLVxTpjBdtga({%`p-JiZyu1>5A?S4l9yRfCO>Q*ib$A>l5~qQ{C*&oz$MBf{wGG z{_(x!5fZ<*vGoJp_9X|yw?|*l#_hFon`ZPW9EN(FzR@9tJ{Ke3-x2F8e~)pgQETSw6l)J2DzJ?vG7%fjvVH zl!Q~2NDNY1?9cA99tY2y&}OxmxPxnz%M{CXqy5WPRQuf%Ov)D}0yw;?KBk#59Im)9 z&Y8Z=v$Bh_wnB%1N5`MOmZK55kvB=DZSn}x+r+M99jHLG)P;`M4#FNcV%Cv@^q%qwaz@5DK8RID(i zq(qb<$bgX^%Dow20a3|`i^;+KtzkArlS193z$8;)V^y%i2_guzf%so&5i^8-?2X|y zK~QeCGrO#!Y`3XFO!6Y^sP)M1l<3Q1M8W-T!uwMNsEQ&^aI4_(A*l}?(2SNrN#4Lqlw`p$dNN z;Q_wR{u?EI`~*&IAGqLYp-my~!_>sD=#-u09hPmtP%Ce1Ya0)50^Z|oIrSx|LF1n1 zq|NSSm*p=8q24bf&phr~^W2UxC|@p2hyikQpgR~8x_=ls(S{^NGSARUlJ~o)iR~jH;^zJ3-wD1>-H%Bvk^onyr>OwG&h@U3*pD-i z2<;7|j^^HWon}7O_R>j}CFz-YrJPQLriLA~q)hfkRO!*LZ1~%8B@DwcI_)0zTXuLm z*{XM$-@~K~_1s97>ES+{4TmZB?2JH}ZXvl)1ZT1y;ibsGEwEmYu-)zQ{N3BtAn@aX z#}$VO_zK;F>9AaLQd!-(((3+px=(_g&6b+1Z zU>8o%lqo*=U47W2To%6=;GOW~EdFIshO0Jcn_pko)65i_-f(`pymGPkAsDV?(waj)zbF8|(ttH*-)>oVzFywdZOEQ@F&YU(^a?RmvkNlNsP{hZ*Kl zF6`A~fG9@*2sYyvD`*zp=7FkfQVg!{ov|#cgxI^TB+rR6sF! zLn43#<@q4Va#3?a2ni8&h`&zo-e9B#%19ClXVaZ+xM9GGQYjNrxmMImdoY%f5KT3) zPoaY6RDx~Aqm&^-+S=t0Kz$d%~uw}QJuE{~u#0-KHcj%wK>f=*vrDCdM=W~Yh5 zC)!P*EnqJv1PDdVgAfjv5{`JL_(d9OW#cgCng3GJeAP0Qa&*Csnl(0Q6k`_0;95)^ zs!X&0<$%bOo^=WFx^D)%!38t7;HIoj9B5==VNNe%f9X$l^eqsfG4FJ$)~ z=KT2hs5JkTA=<_a{=QD#3i4F$A=iU|okog!YL9#{5}wtO346j;C~?A_ZT{Jrfgb45 zjlt%4S6n{l%(s#Z0Rub%)6S3a0cIa2!h@E1>%reFY)d?o+H5YTr)X-LC^`RZp63@H z=eUA$x0ZQd7bYI7y99dP?r7z@T>Q^E61(_*^rfdP7xCjqPNsir8MTW6;&mSVqu{Zl z)c@1L`CQZ5$-|RFlp#Gei7{-BkDvB2GGxf4+v@T1&;7r!EzXPhXbekk9}t6=!+Fxc zHK-J5K0A-6`=9h967<8ClMa8_v{s$0#@q=s7oMWbWr{rQmm`6*lPxA-AtoX z0h#o{64xKMVa6wa5VOhR`_n7t@#D+t|IjgKcmxn&jra=jql#%`Y{rUc5ZI%pOb@d+-=1>t- zX3iS5KTLf5NhG$SJsv6@EY-m`b+W>6`bAWXaH}FRwWPvWRNPXJp`kEqf9nKm02Ha_ z-G^8`=G=ZJhm3;RfQxO&|G=xG)R1`>RWyf8g4}Sx#lM4qfD+|Xj*^NO4V^|#h=Kss z@dgm43iIUQfMf4~7mVzb4C&tGPs3eFcST6OARFh6H&^#lP)346-=R3O=e0Yk9^9>_4+q; zRR><(KRd}iXfDstV4}6!UJp-WV4|2Z-h(~A+(F~9<;oBK>#HmRg`}n1mNDoX4g260 z7tJ?rL2eq8kC=hKOvsmedW1w$LMmTfpz8#xcV==gmvDoSP#-)@(P0}6^hUIxxVpi_ zdpHpbj#}dufS~ZGS~kfzU)PeMcC@{tPz2CKc+JmKBD19@X0;3k44xY2jTvOu?+@H; zl9&B)@o@)X+h_Vmb`RIDM!mf#WlSIA)ST7jjcR(@GCOi}x7;UYD)<_?BR`H<=PO!N zv&*khDi`J#)06v#<$vkGR)K<_);o@1sV#@*|J48O+^qKWZ}ZrTU|egmU#a)M$Zq1Y z3vitIu>dMDQLa#zO*{N$h~nwy*0Z*KI3d(C{+)y$m`sDmzS!LXYsr*n9y1?F<`PIs zyK4gyIKrt(qr+>Z3@lLWU+>MX7~kxdBf`k2=JqXJKA$d~xN0iocl7s6xc2U!$ff^< zIs`uo3iR3q=5Yj<%`)p>UUayPrB{*o-;aRi;e7zdaHUL1xPfw$9*4o86_oM*^dk+d zcObUIAaF13D4+Tj5XdWmh5ZvzNe~b7fPj!Bc^nWofdo)OOi0*^zGdSX#dSt&deAd) zC$J#aU4mmUTW~7~5YaO^1@AQmDVv*xds+pJu)0y=@7RNSPKD1rvRl0yKD@E)Z&<|? zRo4FIlL@`Qa_g&-R@y1&@QFuQ&kstc@nmdT= zRXP!7%t#8Lup%54vBvKbmQ))q$pWY)7cZQ7WpV;p@tdL9A*hT|A-W5oXeg`fhy26D z)%;Hn9j+6=B$`z;mkO;H!cfljSe`@*KUl>1}VP<%RXYduDli9GFLXM(ntJ=^T^ceX4_q7Kg z_JV&sL1lfG5)z%`Fl@MtbG?Q+W(F#I85tRATV1bW$!+AGe+h+L=O64xa9ZOZa8Cxr ztpTK@hQR<*D}3M#Qmsn9*6U}?KH~(O)`cJ9)y5bg*uTbXS^Wxa4yRS|+Q;~O7H9Dy z@BRYrt!a~($NQ$7c5JZoW-_d4)89GfDY3^o6vZLG#i#1)H2zM@(Om5XRPOva#&t@d z^8-B-4epP8%1X{hqKBNJu7}NO*bVWX0M(BQ%xC?JmAbdKtEcDZ$ji@{ z8b{^+$*af6h{4Zh-#{tdU!A{QwotY_+#1gA(>OGE&$jz|BAZ<9%GJy6(;q~ZPI6lO zvXoVhG16U<$mA-7JZYp(38Vzl2Opxx;oucyCV-`8X1_`-UZEsAn{4x+Myuo&JL>|@ z+BWI6nC=bEL1d2O;yUGB27^wsSI;}>!X8pIf&k46e9{VERT-}d`=dK)b)K8t%-_D6xCPv8yueGtDj!N z-D9#XTF@?PgMhGR93BwnS!ow~Vq9bMlk8-NDr3{~aI)Va0jzPqvCZ%v0JTw3y!D<; zyUO|`faIyT^K8Z%IK;QH+MSi)#IQ5qE#6v?sxWhMDaO;j2)rFk3HaBwG+tiePavTV z#~`+CxY|BBT(&3&>iiIpA|kFxV**%Bp@}*Gu7t`O0(CkK)6%dZ%%SdZnxv}?#8j{d z@{J}+MFr%%rUhwioBLOnVSg8s(f?IX9L~hi-za;%*5}oS3n>EQ5GHz^`iv<|1Nl#9 z72I0F{uiiE9@&0bcdOL?1QxrO_7v^U)rjMHD7EMqaJajocb2xwmfZuSLc3q~&)I8s znW|2n(fb}ykz{Yhdg~4Chx~o~=sn;9Js=GY`wkkJc6KEB!lsX~Qd25M7@Aduo4l5%X-m8llJL^;kI#KqM|;=o zryblY#|Xx+&C4nxJeR$!v!}oq8KJbXs}|n!g(eCFjxOD*^wmL<;#t91X(iOOEoiCb zl{)(LWD2H&5{rPFkzZ_c1~4LemB{ch&JiV07$xudjWP6Bm%t6mu^H9-l*pv zB|-*cURS^ECpzY%-ang>A}puYUf+d~@J)l*^vRz`(0^xqZ{XW65$C_x+8w+@$AU+0 z6Ise<)tlZ$8(76c=Ai#tS)YH2+MNRU9@8B`@K) z-#=NkYIXSWH?h!`REx_}i}PTyUN!!LT@5zM;c{|;a}`u}t=>mFSuj%qb>YO}2k+es^{bH|z7;(lb)nUsTtZ~~!ece?x! zc2#wi6|flqGZPn@GAt;{lZ1Eh2q0qE;lP+L~LcfsgAlcM5G+E`tnj zm%{4o3Iz=Nvu;Q}#9aA9R5DQOmO#JPAvV-c-2_+^;sST`KZ~=wKMWr)HkvIcYjFxI z>&CdBYB50an*;(O@AGDvNz?TsW*yggYHlF+=n^vx0|Qw%vs3t!%TO|M0@I~3_;2sH ze17BE{@OkM!RCZO2-7dB_*=c#-TG2yU#1$@Y%YtBrcS?;e|Q-AfyU1{!N!jP&g4Qu z=ePdda=yI=KAwSuzj~Mb0|My|B9{rjYp5L^_=1}mEOdvLJzhe3x095=&ljy$dR}TY zdaB@0;Z?8m-fk%Etq*na*j`pTc9yDJ2a!-GkeGkNg-v{>I5M;RAst;|O|A6e|FEFd z3MImlq}q!SJs?`BO7t`7(3kET56j6?=n%*meiqi;uO>)6V-Z$&H2$Q+yJWkv zz7pGx%>>POFRIh%#sTIyLN<>Es2A(jZS+|gb&pN|14Dj%&|$OFD}jK`l-1Tv;PkwB z&c#)wkA=QXKujF)9Cp>z)~O^P_e*sIkEf+jrPd6-xS$}ZiY7{CG{wg4hCzl#7k3m> zf-M!zdsG{pI*%@)c%*nZ=Gza3h_a|cY4TgInU!;>2v!Y)zJ~i~9hl=+n1n(VGaIa| zY>GpRXt)Woqotw^3RaFoOlPdR5+qF7C2^;EKRKbHb!r=asp^wMb&eu9+Z^B@rghUdyg;~B{4IMzR!lKXnz1%v5fDCjtJTPC}Fei7K!21gbu6H?}IgfI0JR0Go%gavDY>0~y)SxPn;9%P7+mgWCYvMcLib(kuY*U?7Lr zoKHVLs+n+9tb&+`dJY2AGdKb%3Z<$k0c!p4tM{eTIgk7SMct>5p5teJV-dkfb+zg} z7j`hwtf;^blxG_W!YSeB`UFehdk#wdUB79hWa(y;<<(?$*Gi4?+S^rN=UyoD*4uR@ zbLZzxj=qrp%fS;U#(m;1NdPGH^9BJC64cfj5&Q^RRz5t1K9A=dJ3l?PK-@BDA>Q%_ zRT&xjcbttmdR)B?P}6ZTvIEV=UgPTy8GPI?e^^&fd4om-*}u`aWr*Sl1(IyYeru2W z>qEvMzrEGCI_GH!E#$}8(%-UwFE{L$r39ho>5J6~FlnB-EDatciKBd|IXUCZZN?4Q zSvHA-Z2eee-P1K1^qPtIC*1C<@>%j8Uyt#9UFT}j{k63>s#&dtwT#>ps-#QLdG~F8 zpQ1`qM}MokR-2w8?RB}j>YT?u{e?Iz@1VhN`{QyxT!3Gxp}QHZhfw@8YJ38OJ`g;IlNZ&GCz$pfk_3Cwh+cegzJSdmrg z!}R4vKlsymBk3qr%+({ZCua_4DO%Xkex~JTk_%>aOYh^{xfA97)>JfyZ7Rz*^i8)1 z%Xdq6JT^~lbQ)a?*Vy>Jn+=u6nP&(!Nid~QlFH$1vwDJslzXcqX->|<0xDuNI@3ue zXGg3mi-?>RCVnT`nvajj5riPkEqWoan5U&_DDVyg5&{N#l*IYKGz40q!ktE7L{q8L zaiQhlMYTDK1QqAc$)Ca3h^OulFT4cs5ry;sfc`SnTTxe7pt9tj)QAjS$T$IHQ9pPt z(cdZHtMh2tk@@ zsb(lFAW8*#RYAmgSsB_@L)ahE#?T~ic+uzy*GgPJA__dV4x!hAp3fY z=g3TEG1Nw^@$OUITue+n%*=|a4QuiPrInq$q^-=Poy^3&_-VNLNg3!E1zDJRvM}+m zNbpi{^HbrtBB)DXVPG6q-XC*ZaXM;^UUEU9^bKB&-}wS>X1R`-a(P|ThxWDdyO+CU zCLRAgD4_G#Yg!sR&MZnR(@4e=>Kyw_05Il&U&3Y`!@!Wpq*}yuQ6c!-QE_><7HOXbJZiK7P0z>{6P>Gm+9A z?sTlFqG|PGZMM1Jtv1}b!9+4IZq{sGopdGtn2?*onwBUHsQ6ctYFGR8?p=q&m?tzf=3BUA zQaupcp1}lD*W~uXS}n2J-EJk}ZRmSdI_iq zEl5kch2#atr3s)#oMLt&e+h}&?2!SHw8M!=8>5q8jcG(B7lq}ssGR_q{dKo+OrOxA zEVTjx`=sIgfTQS7X;2nz9A`GJOPEy$t{HZ4Y#=m$PL#6E-hYNY2n5n%d#ZFuN}p%I z{0dA)9!%kn!NLvC=t?KJ6@_k4+akW!yayO&?5g74G|f8TpqcfMjHMG3iEyI&Y^L5& zXUfXwaxTf7FF+Q9cF*b-!XTk=ga#86-dnvv&*kj!3iM#4CjRfic%Fks?Pc-$0?)<7 zO8>b25Zum9bX*2e6Q4cK-M!t@VFgKHQOB6|yN!Y6`HoIj(wHC&iks{k4L=aLyFW5* z8eklm0Q~|xIM8qY1}j0rb4h|4=W&LVAa}##?y}L!F=y~oz(0M7094hUgV5Rkw(#Bc zR~M)4?fue_QufIu3z1SO?ghU?SVwF}%2-LoTpnm*#N?yt_%eK7@6&?EGHBRUu=Tsu zG`4X43g&J0)T~FWS3;knm|0j|9ixaXDy%(kxydY_+Pt zf8J1ERjR`yN4oF!vo!Du(b4^`DS&clcfQln^NUf|aU0`ZUiYLT&5$%1N$lUhn7|rJ zWgnuM2p;_nW?fj;_|V%DYuVQN=KCftDj1(@-SGKWeHcRVXFW19=l4^KuGiOGF3X8f z)1LS6JCJUx$>~$q^>8BZ0J>}R1>tZa>#CvKcVKj(pj0k{8RB-r5HK$Ga5Qn3{gnP> zQqALf$Z)Ikts+zXj-p)2;QLp~lhtT$;$QhE!>dH^p#kNqCD}LcN zY(2tySw?+zeZIM6@^T_!F(ss*ZbUJ$tAwPNRQ=-nsQY4<6LFeNwLGHZJaN4JEmEH- zYSS>H44BN6t&reyAgDQ1ftac1I#i9|9YC4~-WOzv>z+h*j)uWDO>V$)rnRAr~A@L`;yw6@YRO+tL=zDO%6FInkABK`;BOXxzF38K@+yRnJ} zkNj|N$F&S9Ws2#2S&P<+0S0tzaKK&KKfb1F6cD{Wdib9u@4taaghh!KGUylEEv;r+ zZ?MfbG@4J|3yw^L2GOPwl4ZH~i4ctv5|q%;5wNe}u&@#mVMDKZ@V7j8+c>z`sD#k- z_i%_prv#UKI9T|3E~7%b8KKmQx$sB;82}(rz-xOcO)f(|`*C$&>qyc2V4{L;laTMF zL1xZEG50kl@`&D)TG5q2Xu^EmjJgS~_~!=Epw}{AYioS|G3#QV|9h%4Mw!CTo7a8o zIq($aJGdVod{9?6ti`lhy(kGX6y`M=FQqIaE0tRTfu{s_{AC@i2Wd+<42E33C#}wT z)qghY8ENS~_a2GZwzB!W56>`ubzO2g<9C0q33Q1MSyGoCKEuL>i&Kv^v=S^v#(#X= zw2b%N%ztPzMu>bVtr=j9x;G`yJnY$-E?z1H_DKjkQhm~~}or0e4saY77?YzdX@}M(F z;XapT)AY7qMolCyFNL4VH{s_u@815AO9!UviOg2VClnSIXOh613lL+6{Bi55E!<5@ zvPCYHI5ap^T0(h5VJ=mcB37}RMCh&FNXD)}m`xi$t{fzVs95TTk_+j^A~A}|nm?XC z(EqEH6*GI)Kv6<1q9BTtVLvwJ*iS*6u1|>lsX*zSl;l}W#&RDHtCy@{5IBcV*ETs~ zds2kBnX^D=tej4c~qAT;%dSuZbD|Xs{{>zvq*t<+(+~#=7 zFyn+JMzAyK_lcs#xbB>&qA_UD-Z>)uAud2drvxV#F*L>8ucPU5q_E5o;P75yA&pWJ zmH>kGY?P)6BpE0)F_4su5?qvfK$#+M0Wv|ErJi0OCYg?)?Z91N84UvZhEx8S)?FXE zAK%WUCK2j=`Wv=fKmEZBMQHYNa;3t~LTxLcYgn18)g^6ds0L>Pjf?x%Zzkt!>H zkGzI+zt&Ic)d$k_BmI`I65fK&O7qAUZ*Oo`(uU!Ptkz&&BK;S79j;h4d%5BJjh&2zf}e?-LGT;{-8*91`Ua)Ml{nHZbX1!H<{~U2byOQ9>@q(QZQia- zk|6&?j8T|0zFr~2lA^(h(qQ*g()mo$O;3Nx4l^+I93;M<1G_`UI@5=CgG)_7u-(zV0T*Zz`T&2s8m@AL&zloADi5O#4D{bU_X;^|b-Eofquw%pwBv)Uc?v=x z(Nf1hJSa>|GQ8(lnGWSptY`2@kmI&h81-eayW0{zsyXW!rx*7Ax>Dj!azLC)nQo5*;VA) zTC7YdueQP-)?otz+8nrPBOl`dkz6IXtQ zVWCryUZlrRDfd>1JAiI?h~#lR(6%vUjN4ahip9ao5)St1MpzIPh`N7#y0gtnY?~Qc z89Juz6jpprWI)VmvV5KVSWHBuuO~pQ=;`W)x3hn<&d!`MaMjY$`8dGBCaI9cW%FaH zJUUiV>5=&0M8E{SM2@Je$}7Yyu)5ydLZMbf$0TO5B=BLb7Gno4hSk6JZ|Cf~y1j#@ z=4m+eK}>IUh^kybmV|-2LI!QMZdK`Fp1}8WC|518@5!knkmxlM_*u>BWK0;ebOWpS zA^|PR8f0VDs5qK2qc-ZL5%2JI?fo?4)jzOL5Nu9kY)%U)=pylS&8*v8I*xjg#;|O^ z76=v&7ulUTN?Y{{tLWI~mpnb14BORwCv*Z2Z&&KHy#DRAD2ZiuHz2 zPm1Wyt?+Y{UJf&L=yr4u7c2SCE`zsu=U`HGrA7ws^DMe`K^S|4pny~ zC3WSk4LIc`P0k-9=S8XySDub1@r=FhU0KF+laJn+ru}PxD7?{N~ zFw*-7aDw1RU|wLS+dAo?1yFM6S^G6w}t>b9CK+?rQv{S|IouiAR=sg{Cx z4-r)Y-SANTx!lesbB7CcTbYrIA3E(;i|0IbpoTah%b9i_S5Uv=WSOTR&klk2b$4O7 z>k)Trf~!;hDHMYnk(Kc)^cR!Huf*+ZYX^(x;c$}Nd6Xi`su`ePX7mkqd;&}V` zwmoI;u6PL1GtY|4;N?i{ThwgZlbfX@6%Ik7Dg|20EMY4hS^QuU7R$3vYkd+eGmLEQLK=exw3-3k;yhU7 zvMQsL=5QL0vj!!hIg45@%I+vau`7?Utbc+w7GXZo!~)g63Rw!PnmE=(DI%^R}~PJ&a7PY&BEcPLZy*B$bOIeg2n5qxtC? z#CO$faXBnZ*gDGk>{a#e#th$6Q<%Hit! zBBnI0Y@v?Jtzh8uB;A9|6h4CRig0Q$#!0A1zM%*MLex<4IpAUbHtK>C&xG(1%>;yT zqJqpR{SdbxRMwF%9J4lknFB%%)eMr9GirIN(1Un@pHw1Bn^J$|22Z2kWc8nIfTO6 zYS!=8SDzg8IEjQ0|58#yK@Fg7ZEw#vd!Rdl+adQY3axK5w`q4e+6h!9SWNvM61o=( z0nyQ65%1-^Uyeq8K_uDS%+uDAKqBO^uI*{*8#!Yjw-`!(&l50fyQ#OGySHo&CJH2} zy?pelKQ6C?#om`nz>9r6cXw)P@^pvgP4FP*@tZ$ZQ7pa~io-v7E=uW>+oS3_=C{u5 zGviyz>T7T8c)i@Dty}D3_n34_e0fw$A}8eWuKk!l@M^J-%(4|RHQ3s|9bB8Nb|mI< z9LnU{%zJyxt3mVuiN#a;VkfuxgPXoO!%k<}TWNlh%9q~Ah)O8Pd8&PRtJ!+mZmmqZ zW@P&)H6iC(pI$V%Jv16N7df7BJms1xA`zi3tWHJr^!ctE$)A^w+xjTmU zL$tLZYLL0)4!**!L0gkbJp)d?lwX1+s+s`?>R&&;vM?)$s%aq|f*86Bt2(ds_biI8 zOx+FazZJk*%7%D>Rat*BL$rzt#eMjN(OLdh_RL?hOpBIvnBJJqKnWTv0p>=;8u<}BiaTp%JcXMV?w z@5z%KN>%to#+dH(b?&2F;55B%@<`=HxY8sivI1&c+p<_NPAo~Cwpuo)hEpj-E!42^D6BaZvLw-e9mKHShE?F@juVRT>Zher zj+WA?)&Z-z38hxClCiX`VhU*QaAP38M~f?WV`1L(YhYRkMpI6e!2LVk<&TINr3*r* zqt74Jf;7BqmMxP<;KsBa4f~D$SAHygKQv@c8(dZFuO0zrMoKBmyK#d-n{R%#?bsZb zezV8hZd#j`qY$_MRJPVi<~WA+yoP!Nt7-`}ss*bVSTFEL(D$V&I>&?5MwMw%#_Lsq z;`8?G-+!oPH28nV`CeR#Bskpvw2RPJ?(sGqur@u!6Z3Q&xe8145 zWXzb-5BsCR01~wu-!-BLC31Ug>uBA@=X5Pug{O)BQ<3?tVy-1!HyXa81;enNequKw zMdE)Aa!87YLBT*l{p5DJKV9i;L#aytM6mp8aMCI`TzPrgb`Qdl$Kqx!0=)HA)=qZI zf_qmlE1NFsbGsxfh0hXPJBf+D?z{$Eb<2$x$si* z+AqpRc?a$E+j~%ml>ldr?S#KFK+73gdI!A(gce8vXj6mw2Q|5&Gp(bEPMt{s@WHN0 zqNE80y|iA+{8gyhG%SUA5_a@EKmIhPoF5^L$$>@J$GXV=9CrGiVB}UrbGrJq}(dOx>-Cz_~JSUzA_jfXov7XZ=?BiGk zxemvE+4I|Rxnqr?Q9o_IQ+}~jI%6Kk;g$(ZU>$K^l%9-~J%7r^e^zh89ClOyB|>@XV&W~sLKaqgW(n_EQ8@3iQTYv?mv z`DSuBm|+;`+yAHP(XoTeX43c3b&G`KsyM^)!t3BCq#<$QCoQ|Hq19^e^?1IR+Ee5& z&>zYtCGVZG*(P)0_sC*tSaKKA@HtZ!%;_ENn2X3>-t=C62wAh>gwh*iD%MA-Ln1|^ zTxnyG12LBQZnr`u{spe+8&|i>PH~^IT1z3mKu2u@^|Ej5$(X4k2*ZbOgS8(RJdq}M z$W&~Jl(h-{tO?Z431zN{B`m$eA$Gx3k(EVJw=bjU)*|83`lFZ?6@Jiis;HUcw^$4N zqcLQloQ++OtD&^x&I_jUqU@o`)>FI4uTa+aq)}tdMKFz4*~W&(4Lays)p)8%1_Xc{ z7aoih7^$%$!QsxLV8w!CycT{>hM}M*S~^zWL<|+%PyDzic4t+TI!d5tC4;` z%c&aeu>F2=7P`nlq$7BLA3nC^_&&1{SW#Qa1_JZ$uayGt8}!=U%G715laml#EE+;e zv{=TTy#sUYBe=|nHJ(ADVMxH!d(GWhwdZHMr;?3)b77;1jy3-mC}^&!svh0-YCx{R zALp2(YIktX&7Wk|yy$n;M{mRH9ap)A)e=HFxInhbM$j981;CQEjTts#DF(z9Ifn*U z7tg#W*)0be5nB}6yCDLe&~{&*oe=heJ<7?GjEEheLQ%OOu(UO*^!|^hvy5u9i?%gR z@IY}5?k>gM-Q8V^dvSLyPK&!1in|1d;_ejpBE{k6JNKUZD`RBjUy}WwB9QZnhfnffTjD1GzSugdTw5mT0$`A16>X)o{gHJF z;V50`RN6iRXHYstYGou<--n#2u8{FIVb&oebM| zVp>I(AoEk4j-$}=r@&EKD1iYB84Ona$DGb@XFfVfv<&M49V`ZK{VAUcU;dpNwV5VT z_Bo&5vH!6NZumtef-^UJ`Rykt33-zy(d`G@9Gaun@zdIQL*Be}djdQhfBXaS1-sS0 z?sNkr@Zsmv(bF;dmf*=k8W-6<)tuY1SE{rv-23j(>1op4zB1*+{RujL_V*WAKYdpLc`PetE zj=`cm*sf5@y;O@i4%tE~RR`4M(^snw({y@Gi@!RAFpFi$1NfGZE?d zVNKl7r{khZTDl=DnvMYNGg$8DK7ZJaJT-6r)+cjavw-jx7IC#tb#?#LGV{lmrR>i_ z3s+$*R0E9)bjcWI5l20HhRTvT6hJyedF0eF3iz?eJssjD^svC2{RmO9vZhhi>`TVk zl=RXVs=)6R=Qlg1?d@BvItu~wYcn(HBN<0*k3ZgYssw@*3SHCnDYGk4KONd=E*!XN zLJi>uLRSX{>#hEt^Zs^!!DNm{1&P29&7?5RxLM&ee96A?5b3N*vtWyiP4C-07F45( zAyQHwvSk1L50d?TVUyD&OjfM@weTKzapN4cCFH(q?&4us@-ZuriOAonKYl>fEm&?m zVNYjafUJi=eX(YDD{;2jqeNzca^-k0pdI?wAnCC1M2 zjgJ?l+iW6B3EI}We9hG{4jy&}{eJW%ldf!ot?>oV8>LMTBO;k)z_}PcrD(KXL__!+ znKk*Nc$W;^e!o&EgogMV3dU6udpadp5I_Ky=?>PN;Z;T34yBJSjJ`F+^?g=0R;r$v ziIgY~=67Xw#bd|9x$)t1_D7~)8=67gmtd^Gq>gqxeeM+`GsRFh#R6ov({R;b4`P@^ z*e4^Fm<>Qsyhjwc*rCBTkb#hWlhi;OtiQ*=0=|&=#bB;OBWnd7=T;r9tvb^BJjy(k z)AH-g7Ps?3g0^<+A~P$6RvkB*?$A0+Ru_Dre#>3;zoOa2NC4Ll(l-*3;E&IXx_LXR ze+c>J0#`doM&8T=g-&nhS)*WIivm=hn2cVRcr*Wg8d3Hs;#gvhdi{6)?)uxnxgfF6 zm6Lcx#hXyXF>ORsmShAPz1%dx>!HazS8&`DYZo9yqa3v9+jZn3)0)CU`aeiDrNG%$ z$CBa+fL|-#_cBG@wx@I)vV=Qbwl&YXA4l>!^%$F({v5++l;uNYNqWyuZQR?+8+U&e zm|x1AHborQLzYE^h0ol#`Apy^-}z0q>XQG^zjC>HGI5GTR>BOq-+X$CXfSkS>ICIf z#uU(#f0EOQP}^s(dG&EJL)F0)#{vJ09elqFn;!?9bSRF>)$cNgU(tQ;})%wIppLmoGzEQA{Jb>hZ@r})@nkaHi zh=-X&o*VXV0BW=JEfnfnKMdwE>1SmQ<=o&R3_Yo{#38XR`rqYo&0KP7s{Ai$Z45Gn zN)#xmmRh|+O&PgFNo8(1?``YL-XY|pHUOM}%y%X`kxU3k6d9E&lEP3kS>HH!gt5T= z_K%cQd?fT&zLiW;eLc?VGBoBA^K*-3G%#}9d^RD!7LEiSN1Qzt!JcNUEJal^BbuTa zpOT^SkpqkI)5WkvbyJ%#W0J$H&Y+2snBLT1i*g?4H_K(6{N*&<3M~#-Buz zi^WZL!_ytU5qWKIFE=OS(MpPt_F29+BH6+*D#q1Thx-v}#Mn_xNvqQ)|Lu_wl-x_{ zSwiMzGK2mKPBR2&gq-6?2haTjkCoe`OPRr8?-;XsCDO^s^V?L0SnRDR%^r5UC^2CK z_7QZrs45VIW^W$6+x1^QM4Z$ea|NMROv#WyotLB|0z{U_8ZvR9RBqM$CN2>rx{Yiq zK|Y1RH5+P6W=JC+5(@H1&V&lL?I|FLC+vu&k>V=A5yk;WYCFNE77CWN+iB6*ayYGcg?Ap1?ny{_p>3b7T+_^iqL3 zH?d!+P`kyg7k~)IZO%J|#k7M*ddIyKjXXAsoqBP~gx(#yjem*0{DyX{tjsDLLdttU%c;Fzhm1i z7?UM$u*XIi2N8ll?;YK@HSB*5*E=U&Z?4_)nk?_aA(x&Ee#L)Y*`80Pn{ZV1)Cu3@}GZ~ z!pfFNAKU2+<<24;h63>ZoDjFm=SzGWqMJ}3XVNo73)6vGK>*&McBh?sLBX!CBxYE# z|KlK>PIc&{tQ^;Eq>HFg98HO&;*`)J_)BHRrH3W0C1vgU$4!?xlf|%6AKtz@xLlLI z6o;Gt<7b<5&#T$cskhB!R=Z1omuIzh&|Av`?U{{}(c_v-wav%%WlpVeecca{x3`Nc zr?u<%(}Ri8>=Z{KA+y~Stx~CS6_elO4MYC@o9o_Ruk=?tZVy}X&K|BjOrC$eybeKP z^1hqFOLHF6&pQ~^TC*O6@q{wV3^f9<;63P8(st2NQF~bOHqqpS2>kFz?9p)eR#P)z zkuF^W7*!l(hU_}QBlZXXKRYEd2<{zIMG_SOIBq{{2%vIt^MNPW=bdGeNTY4>rO1jZ zfF!&a9Ml3JSTtxAH2p{fC*mAXh!~&-22H8%bE{a+cI@F=ah|OCXC&fRFwHcA`2KA) zyu$7aTuGL{>@@)8CPIRVYIvt$#=(g8k`fLmuAyuDFQT%v>1vITKbURc_J_#>;)vB> zODXfEKOit8GMZN4l6v_sr6J8)ry}8@uwB}>GyJ0l*3PeMN_uy%-)rltd#`ohQ{EQR2g_=R zc`Pojbv~o#wwpbk<;wgHv{|WXjp}{+eZ1T%F!kIQcp1ey`C{9){26^q;NmFg^|b6q zucSKcH{TxireMj)Ac{fOkjs2gk?cO+6jAhX^Uwo+f@x$$Jou7E*l%*>SSKNN#J!d@ zC1wP#1=wCL42YLEBGlUZ2$v}L)ij4?-oB)c=d{s70C_JrpD)3 zh#9V)87T8gP-!8p>F9ZZHG#{WKptPa?&dyZ6$mib&Z{KUg09RjwzW46KYlvo@Mg`M*(WJH$F|cWnEek((?J%y}QR8rk=H)jzeKT_^so z{!(WzGVgKXb?f|qy#5>M?t;Rib%mpEz8K=`T7PVo4d!1Uu|4^O^}=puZni+~L7r7O zu8M|2BsuekoBcZ2@ey0bu%Y0$suD~_8zZKH9QlSUuSlqk#Qa6Wg~Vj_MH<)L&eOfW zMXnNzh5&$JCx^m9fFs&d2n0n1uz|7;=2GMxh02~u-2fXufi%gOg<_AX*_(@8S(-1v zq*0DgYN9l58E}+h$W76xu_Ljl6)&prIQGyPbQp%@nm)Er)B8WqW4F^x5h7#S-QP?Z`BJA|SMyfv+i&a5|M@{H(3i9giLxN^zEHYvT#oef3 z9C{FBAC#E~jgYurDOuaf%1llLUA{b%21*n!Aja~W}|8F5`5^IzP_N*31K zCv%2>d6VY7+&U?uBlZ9#tpV!prSz*^`+~AKe^(ndTo*KUSIKAN`!xG!HOHo1YhaO> zdQsIRGahX|_Pcvr|Dt9$-MZz7iOl*7{E@=K}!Qqvul|HLLvL|di z_^PZ$*?ZYk(2=5{H&XXy2VWmbR@9*{Yi5p>i~I(9s{k$#uCDMCFCqwzZtZ)1f*C+3 z=^5QovMB@@?f}7^I3offP8pAisUSFV(6vb@@TQMXvaN%~RY;!@;ifGY2nPaZfvqw7 zAj6Uw+yH$^mb)l@A6Bd*mOCvKE?A)g;$Qg#Wlh**j7mWvJ3A8rpZ;HbWCJAuXaT`j zUWiNNswU}t7ju{bdPpK*(naH%hGD4a%Ee3U)D5rnIq-7|6i+(I^g#xB|2+o&VEjE(Qwe>~{0*1i5lZ#o<~T7J9RSPK~BIS=v`viz5{5%_p;F>>BQ z()z(+yN@>_9q4JlF@0iM?pB@K7&}>V!|$eK;bkQO`38BDy7u&aP&BGL=nsC_2Ag}* zwpM-9T#~Q*VnyJG|IAWWNF=vB#NKES8Tg&-c6ZZgT_Q#1A4dB5;?lpF)2}Grdm5VR zrk6#1adSbxYT-^*nXI_0BTw%T7n2QqrzCVz_y;HHPc|qmmA@)%tHlb^7$|;nkR>L_ z)xgi{M^Dj8O+RQ@JvVjrEh}LYh$HS_w$^O9EtRT*;bNo=EsbH~kU3&#IzVs<|2uwa zDB+hV3J<{;{5pYAP1aw0gF3QW*A$N;~d{z z9Q`(I_x#s-W>f#V<^3n{aC;Dw3w?@Z5>TibW4(Niu07Gx>k%c7_QLa-58dr^IBH;*OauPo4#GG6Ga$Mx*E) z#FAnCahvWw!Cj=~&RSzLxFq)-%AC#iF$YhJD-iLDJ68=|ZuZ$-y<#VeQ@CPVx#0#U zPcxcw{*p&tgc3JzBKa>~lJ=iD(nRFT)#``3U!dUiqK?Sx3WgZYfeja z68`p}j}*vTnE3c4*w_bG5TZUV3C<6fxsi{Veha?`(r|mf2ePfF&AjQ}_kZg~BEqv} zN|b_S=#ZyD#ou}9#9ZWoY|P8NK|@+J#ByGz#^Mp|B!XWdiL9Ny=bp|flRt*OIX}xEhZdqA%3Y-k)efJ;_)yhHLYFEh_1f0@57Kp zdFekx;ZbH;^HHfr^PYkgRvcn8G$jp^lyN|cmW^!On59?jFi=BFr6{K4H0MuyQGAAR ze{=3C{MSfTDQo#pn%qzWUwTr($&!C#+5Thg1Ph(R^rMHep+kp*T98a(cdm-gAdyT* zP@qz4Th;6ayUWSp#k%$xS~X5-*u#61zB9W1)h1b&bAU@ezK`cyh}P=b2wT(5S*N7u zg_e*_{f30Q=B|{l&GqG!pX#R{TaCiuxK4Ux8@`V_)9bBOU+Wt^e{flR-rSgK@VJ?} z=#xsU%PqXxU-;*G_6)IeDSWPP!4n1K6V$=LN8HP!Y#v$oSW^1wMpWz^ zCd9XDI50SW3SE5b5P-R#Q>fR5$EUmiXy}B`iMMq^oQ{f$6jxfPm6f&q0v!@286iRG z^ffqQG$2Pc8plA6E<~mv+SV~jcAILcRWz(mgejzsetV@x$j=?8eATIXdtXW9nfl-m zQZ@#|5u7}w&Te~>6n4EU@hE?L?nC(BU{p`sl6f9f*ZFXzslE8xbhTU&y`LPtKQo%P z0{&jnHu<>UHEHN&Pb0(}hnbm$Ly}3Fn~a2eaL2)@}c6MQ@uHE`l~DCM7VcxJ5MwSK2j zZ1&FHITp5BS;=PYZM=KpGsri%J5iYEX*RfT-n?NoHhk#bd?q{Rxom-8Ki%Vc@qg{T zRG#&c2^|eSpi`3+GZ+f9zqT8uO z(?9w{2KmzY_2sl^V(_TN0>W2`eMyaor&z694b2yuAYmjTbaoWSgXI3L6!lffId6!8 z=GnTCAmUyUx5+(ua$}I4>yU^a`OZk)BDZgbilbSaubjAVd#suSDi}H&T!vm*CuOuw z&)tlhCBU7b9Md56Y!=YO)3O z2?~0WB!J*c7XR&uPl`zL4q%!`u9`z?>YwO{V`;I4bClTk30yu`|)dIW!(Plt4tO5MTUo$Ti^-9=rsk zgU6THRWfgPe4UV?qvj{^E#panek0t)1N2H)Fjb#{i$!7o z{U>xn1|N!xPI4cxr^k~(oMerd=#noue!;pbpyWaPF&-arW^B@Na4(f>{o6#q;&Qyw zJwrL4d-}H7_xCp&w1@9Ur`>~&^DH9?BB%zCKD)&lW7FM!lb1ifxj#^s;cBWe+V?XbVCT5YW5UlPquM<^ zcb%fUUY$%4ZRb8drzYLTshkQtMlkDH;Vj&ImDeMwEecUvgri5Ufczu4Oa!0)fCBPl z7#xajB*;Qsyu~1>DmzMSa}z)yW}Kn%HCeFBaC-f5Q^q@;o|P_XE2x)-KaXUTQKjPQ z-b#TN#uHnPn4x|=1Dkpuc>ue^CoxqVkE-hK#0nu#HC-07npF3w=VkZSi&VIkbo?T^%VYp*0G?&vH9O;ab_YvG zB_f#3gVRHZc*3sC3MvaO$r`9Z^n~^{`b5AFKkOuTVFwwDqGVF0I|7?N)i)T}iUulc z#(jkoi%!JlB1RYzJaf6eB~v67ML}jqA#WB-vE4Vx5;q`4na5PSW(O9zFlthk?S3-! zZ%hJG0^z9f!;ev*%^um%`~Ma098t*D!A{3u$()|IW}Yh!NfL6sId6KqA4SJH>xFE}+C8?L%=1n^Dn(;Ny@S zVNg&MnISv{A)aj~nVFJ=P7E2Ud3b$l0iv*Ead{R7ReiE8zyAj+2|Dw+ft;$g6)lTj zgMdmD;qj2r?Z1B+(;}~Tzf^jkUw_s2Zaxhrbq1~7UtB^0s`pdN+hSjLG6#D}d2gmZ zZwKBleir%g-zm=zeBbTdEFgOz5%k|*DU^uvx_Ex?yb9d9A51ZNNxWPOx;qn+5q^4I zr_FPi2V}+!{-BNb$hN)WS?!;i-?Bnp$ieEqcJ>#EayV5inLhRN$@$$^OP z*8r!_7e69Q5cYAnYxuCN9{OE7AKo(ZsMO@A?&dnal(NV;jxR1*J1FrHlJIRO7dUGh zJ-v03Ve~ebpUFs-j(9L{q%NH^P(G5E*t@t*?T^#Ad2&`((U*7S8Lj~+(o;yU)?vA6 zx!K`(fKCgyv+rUceJK=!<*}-h0-Y9);0PRj1KYd&rZLb(iN5j(9V5j_QK)~h2_1b6 z_`A7XA^ZNUrCdn|f++sCF{BLbQh8)MMYWn5hT4woDV>r77he)QAMV4qa^(^nyyT&w z9T?x<`7^FDGg*`2nE4XLy7n!sloxk^txo$o{VdRq{^OCrtgm({0)_I zs;dV!w$W$85|@IROEWpzjtHl04t}N5Tq?@C7-B#Pl9+yDxm0{fOXaYi;o6Px9+Ps>tGitGcbX;dKah7T8pqJ^uXsYF2m-`&Xy z2`|RuF4GI0#BG0}hyv(4G`EXuB>q^bA}mT)r&18vpUmVKz)v0ZWA;3nt%Toie_*AF znpZDu7`lJU9@L(vtAtEGdSw5XIRcS|PST&r?2i%le6FFcz?LLkA3M*B4ocGBdYWoF znl^N`iW%!`=&NTK79$uE3L86gHB329r@sD>r@{H#X2>tZG;=jG{i00#e&3BD;`cF# z(ev~?@!;+M(`@q}TCT|I`_tt^@A|{oba|lQ>Bs%FOx`0DkCJb|WeLGA8bv>g*OKhI zKV=>5D)cl9e>rt>=>+St`N(wW1 zr}F8KX}SwN{Vr}1DWCbnS$Q6xvq{=5CPvFRQ`dgX{)(#A8zJpX`IHa~yLm3PuBl32 zUk|0bE8Tcjn%M!G-JbtsEU8082M&dS5ITYD&7?b0X$(r?#-iM%8BMa%j{X%{o@Wt| zQ_-QF({t=9vy!?Z$@p?s$H*v7y=dg?cr<`LWZ4owIh}-+4&(g`zlyGrOT zLFBlDI(W^HgVwlKrS_QSmP*ibw!x5P(2(`QK3<2Rf(t%^I!;5gG;`6$eZQSc?Rx>X zXf#hdw+ECU8^9I$RpR|>_6subI#q2kG%T6eh_bsZyY6bd+SY>TS$A>suF)&xgh_7s zXnvuGl0V?~Gor;EzrAx(N!bsZV|bsRiWfBiB^457apUDPxZ4wO%{ zZaCXYu+}jaCT0Y-pV%l+oLxmx82H@{%`|j<1&n~#Mr>2StvM3i^JyiJ7*)!gLpav| z2DR45TjC2ec@jN<7#bXw2 zz6?msTKkC?uF%JP4zDJe7M(IcAB^^ex)43PltegqB|36YcFNJ*B5rgB_4D`?i7BF( zDH^+Kp=yD{p4Ml&s|~+m_+i=8wuE^6ZH{jR5}6*#)yGd8n(3 zeMH825x>2!Q00broh_t)>M)(>5PMky**6DNb2U5SBo9-*BEP%Jv_nJE3r$>d{77R6 zolH|F?kbyumcK)&u{M^zR&^jz{ze;1z)g}Ci`(Cj(8B*l8<&;AxvDi~XNZ(eo|K|R zrMY0mDT&18TTw-)qKB194@k+R9$Nr7yBX=1hKy^)pu|e$gr*sOwWUd?VZg4e{x!PC zW~HaC1)ESkMHN@<`}F$E*_2bbxfOyaDqH}NSdN4(3`Xvw0@R6<%&7JhW$SG!SK3<~ z3-l`?$8X-<;Cd?9;k|mgnc5uc|HB6d zO@v>9Kp12y70%K|VacDH zV(%e5Y5(hjNFHM=q3=a<51vM^gj}vtPPV4_oz^4iE8Q0&z84SgV|>0}Jhc zqwPG@?NSgNS>;TDLHU7Rf)W2E1b2USfBsNF+r0KB#vl_WCME_KYWf#wob3qh^@gM9 zyaRS+y<{{3t;gM(7RQPPs0w+swA0FKf2-;2SB&R9n)2|~eXKYx$xrJ|T;Mqdn9U-3 z-L|YTZ&-i6GV0Vb$TMjSxSLan^bLC5n`6DmxPlXEb$W;+dRVF5dK##@GFc0paXKUK zZ1ywRa+`5FX#IB<^vI%yymTR%cbGmMV6qXoa2aRXndG0p9%ycn1=n*PMyZG>j(yB| ziw0WI)3yE0SU1Ju0O`MwueP0p-bv?~g$k$6*qGxN#m+uHg~bh(wNmOT4#GSmyW`8& zUj(dBX5wjz>}`BH6)_gA`$DdTt+_6pfCYjBt259g!RrUn)d@Mknw9*d>R8Dd#2!(% zQhUN_Z%ln8R?`e&kO9ZbY%qJ~vM2b_hc zy4`4%9eC4HNo;ry`W(8e;w)^bzF5)L*X0_l4{U z^3LD|8XF>_toO^fU!?p2=Wj1zk1u$Ig6}I}F=BQI8oz?!Ai-!$M+mwZMPD1?esMRV zJ%+th)kJR_B@x(%G{CsfcVYStjDfbE!B&I8)?6w=wa?cbLWoj?_5-2nKv+2qlyFs; z0pYev+@qpMAYk~Ui6fq>s@cf`&t2td2YVNExGJI#k|$L7Kn4oRT$RZRA(z#Bp2pZJ zS4d;6^yTkCClcpR4owpuE4k z!qc_e37a=VXIEF*9dA+eSC>Ej&DQ6Uk=*%wlGPmaxp2y`CO3i-tXgcP>lXh5qt00c)d+j(B$Ou0dDpez$9~H zG757^YUFw9bIsn@KGI)}32L*G6<;P9CCZ^hfIrzVvVlC1aX@7;IW?+fp7cN@OMyPp6 zg=))Wqz&(1OQhpu-iUQLPUTDJU9sidKO9GT%ocU5hi<8Ax&~#rSlXM7nHE8KX-B2w?+4a zpuf_grQE-Yg0{MGQZaESE%JqNze{%_2*n=l&;33g>HxhH}G$iSecot`dQP!A3;L z4T~ijW^0P>&xS1=q0myDl~RE*H}_FN7+Yh`g0;I;7$~_zoo7zo4BB1A9#sl`vls1T zbxo*+V|@`*C6FLAWn!lc86%>wW!^znYC`n7nUa8YEC50wjfSHd4*b{XLGM)edq=_-Y|B?RQO%QiYoX0-oB|g7yj{K1ZqVq<; zUL4#(`X>p9n!N6fqcQO=l(0#0bgXa}4{(lU^@U1Dvr4NbwbPF(8ONR$_2!Cbg)jc_ zXJlno(y5rorb1dt6DcGr63iyl!{ISKT-lU}X&GSPRl#b8I6B*stWJVy{X)q@${Jb+ zhs*o8L={^1a*d5#cI2JlQ5FuGzuYmYMU&Xwr;r@vw+q1w;Q6|4)9 zzL0g7#29WQYw6}1X(H|yhoCUkB8rim1K3f-T;v%fDvC9mLz~4le#ca+CPs#7b0;$p zvR_0$VNE4ZGgpsFtrliT>o@&Su zL;1%S@#of~DcD5NGrFave<>5B%OqJ?7`hbBui>Q9(z1+Q#PV&|)f?8tu~MUgYY`3X*}U7*PBPo!?-MhfHCfj?oQF= zr5F{qr+DsM?i~Tjo<>ZcPKzXzXG4D?TBCp|X?RSp;C(&Dg7G8KzN6_w`aAu&ftEXW zuDe(p@PHa>Wz1XAm)Rk7cOR=b`98gtV|aC#A|ppoxE>sFsLcQftOFem2Tw!> zsT&uiOkybD!*Rg_aO`wI9JlqC{`zu`SYav_qtM~5RvAr#%5V~x)GX6`IFUAu=E$ge z3_oPmu`;CMY@Off1%q?u zHrz@w^a_--sPGOM5K{s(49K3;N41k|e=bv3S0?twHhj5aH1xTmbc>_67<-xdI z^m);VMKjc-b1=MO#hOf;J{S0-D+A>fkS=NyL>zvr=5A4v1!oylqy7_B3^FlFSus_# z;D^x*v;chIjTsnK3q?uL=kb!{AcrtcDN zrx+s6LZzdb#?7;cj_P69)kg6(SlhMxmW|G3pTv$?~={0 zoD!k`Wt?ud9Pf1ODe`;h+NA#{H$SU;)zl*Cvr)^c`@XI_7+8QP0EQKS4@;S4Y&pg# z^T)7&s(9tQ!6r;1ZD$F2^-5?6qTnDDY^>=3^zd~RW4O#07$5~>CF}sV1o^ICfi;R+ zE&PiVs`z@hOYB%wXwio)@-A-RTg2o|q-9yKV!9-<0v6-hFS`MkSYB=IbXxkjaY(bG z9u^KlVv}GdZRU1Ii{o;V7i+xy_;NIJg1B*vsP3?ire*5TZcZ#}AE&`LxpXixNtCv@gneg9l=zY*5yU9ss${Vo@v@Qj9?&K4o)V;N=ZmUdS%2?O3#2)F>;KPZ*+DZl5wSj26}>+8YDJ zv?nJ+BHuyQ z#IZ|oAS#FZ1quu0Kx+ElG%brU)r0b6Ta{PIBt{htX*Dg&YGgN*Dvq>LQ%1GdPjV`A zIquEYsY?JSRs)A3+~otDr4UTGJge%Ehz>&~&x{tDa@qW{Iw)zFuwB^ww53vq1iL=* zTFed79ki7UJw&$8G zM`=sco>}Ga6AYtgw4+q@JLgt8l%ELT53|@p!zqQ3$V}ETPEREVTM>WB`S+&k_F z#HFmtET88Kf*omvDnu~F_$@AV@(_x+&P;pO1WYqNyw4{`;c&!Hv*L^(M`tXO5CnFPR9Y`tN zM_yhILpW!BLGQ-@fg``rKsm5)-}!2T_t zx_b4b=Zro-9Q@l@#ddoVP%=c2t*wLc2Iv;0FPD$Ux@FD9D;fjjUco9hpNxv98KE?E zGsgD^RMbuBR zOP3yE!aq#>8NPXjvyOkmP35zPf@I-1OdzFg<}!dQD+62X_XOKT<`sAt>=XVzcSdXS zHg$^0xU}LcSlYYw0Eesg>TE7d8MSOFoP$)p6S+y$I{w-!c6D$X&Ra#HR> z_AQihAysD?V{1oT%@y^@SGOwuu}G(!_9U3v6ID`4?t5{hrtigPaAF8KMFwAYIp_5DTF^3G5XE-%bV{LC zFjBUN;p8NEw^Yk%d5+0nplPh)P%%h=_W}}*EodItdV+pN?u&n_Ey*y}M?{59%F?kV z$6HR3kA$!R{qhxNh8T2_W7fWlXbF5ZML{K^q)>&*mX~NKnwKSX*^R_NIq$sc613 z-po@Dudkt5Dhpg9SkeC1Uo#R&!NN0Hzvs&-mpGGEkMD<5>aX5XI z#)N~r9tXclN)bDhLPcXJF$Zh5$#9*31Ut`e7Hu+yH}u+#>3eX5PZsKTjw*dy9Hku+ zyJsFTsXvP^ONU!1F(*vyDg&ifc$JHqiA?F_n*rCymv1kL2yl*D9gY{L-0pbw*=Gmc zrhnI-ZZjtT-E4I^EglJML8`IOhT`z*^}D|uJ%~W|#^bS#q2saf#F5fDA2VO;FRHjf zt!&Ygf6~)3vN{xU5IMN0Y2A@w=*45!R5#aHXPPS}6Fj<4ur-LXX_3aY5RdG{WuN-f zLP7Q+BGhDz;W=cA6Bs2&FY{bI*o*Y31DQ2<E``0Ha8n5pZ$=ZYC{|YluE}BOl_Zn$Z+PRbu;;S1Mh!BUABK zdCg0mCQ4F4+_wPNfFjPj2GR}$X?&Vw308!XAQ{z$Z{T{@l%j*(^yE+>9i@xL(kVOZ zY)dHjjnppqQRF3RA96)j8ym^uDb&D~V`ck+ywGe1bM6TZ10CDuJhx;5@D;Sd1XNM9 z}aV;m)g(!@AP%KBwB4NNkBj49zwY8$NZKRrP=BdpKQFp93tv9$5 zc|Vt`j>1IfQdQ7RIXkg=d$t&MyI~UbkM!dFkJtPo0_49}`jKyY9QmP?)GL}rM+XIb zuDH8KSJEj?H|qx~>j4S|*!;fm zqBc53d!m0 zrRp{UNO5kVWcHCOk5Cjpzg}hfV92f644%KSfQ};Rlfm{)z?bL4q#o;>Q(qcR3El7C zX@%3J71Xt3p6elj)Y1bNIYF**F2orYx^dHSbE<=`rS0R6-X+a2Y&yW?APCZ-M9d^b zos^+vk?q%}m9R{z>&Nnn`EBqBS?lQQdTm(PQT^7_U!DBc&(0+&oPN_L@p6i*ZDyOP zrHqg+&40#S_Ct;7D0r+NmlH{ygv_Xgrz;JRZJt$t`+RoD{8Cg{>m?e(NXfXCl=tPP zlfVOQn3}GZuG{V5P$xzWhEriLwN!idQ07*L>+L_Kv-Y@UR+FdNqjQ|t60Wc^^Pkib zd$M|he7W_e*9RVX@r=owzF5cxvgSAL_#1}&csDbEbo#R3vEZ6uJD%RY8T z=&)u8NTRGzgF!;>`}imqJ%B?qk~o~xk_Ib=e44dwJm4NO%9#}Mph zhqVD_K*ygoUkt@6%!>PWrgL2PTs+5PzuL> zl!23)1wpiroFS8W#h*XXkcX@ei50FG#nNg&GGJ_Eb^5!5TCJ$y)(t-z3WiG+gMei0% zg9vgo)O3}qo#aw+%fIPab^t{2%b5yid|l<*%kisd17Udz{h}Uipgy7~>ZE9X=FSM0 zqMBjQMK?s$eZ{_X5HaL-Hnr0KZFAg0J)8TUD!a2<~mL(zPlzNz<$4RZp?vrn0fksH#^) zO!6f)z)+;l+eR-Z-dYW)b$)$ zG^EU{_avnRCkRpsG1!xWA>L=no|%;~&_D^4xaQHUruag!_Poo%bo#9Tk@Kg&eUQ5O&Yt(vQP=HFlYq5F)v1he z;itQ>+|PkgBH!}z@m{aVU1F6dg@jFwtlpTv21A z%`M8;msIZ%g4)`F6mC%hA|^S||#?+37QK50^p%cc>`py z7sOnI6psxiJ`(ahDkbY*s~GK{6BxAieA+hk2wpneit#-Pg+6qWGfQ#rD1wlGmrghpl>{$U`0ML5*48&WhzbV+MWQP(7^ zs#7((^a2BB<`THbOR^Ze3=$`hqsXi@!6c7k+c_19A98KT@ z1cFco$3h9gRSZH@r>x1=HHbU>Fn$0_m~8;3A?6X$PQV*x*yF&%mJSP34KirC7GpQg zLp7+6Tfi0YEzkn!@j_cf6pK;Q0b=TqEfcm~&MY!#vBY$~CP8CJ0n5vhr&HBo>hg4f zI$v9JDw&c~s*|;}q>43pG|lk{N&pzF}YVEvN=N=7DG@QSDbI{s(@fNe?eC7Eo7cSpuJ9|ezaK7Wp#r~UC zNz-sOBduWvo{3$+jiQoI1`1J4cGuLGUze{_sN_wuyx{;(^zsY;9 zDo$?W0AjQBRAjm)oE>xVk4kb_r8kBTi zR`gsg=)Ycc=BDP%AF6?C%Kod0zAN(H-xNK+srs(wpZ-I0`cHVeunbSgmIn&8L|yXT-giC0!Tg-52wFe`6WiGQ?V8zMjP|2}))G z;MpzbSam(XEk%95*o3Cui))7_X`ne0#JX#?enCj_<`uv~NcCt_Lv1VC z-2w`Mjxu6mzLHpgjp!Y0157I6UPBvDP@qGxQvhLs-jaXu=2~_q2H~-Y42np~;ZvUV z5&q8}|3`xGiTC(_q@mjXryhUx5#rFOk4n#|YC$s`ifRFbfI}R{#5N8xD8b%#P8f2gZEl7>_8vF-jLIz_M z>f_Ib@4`47@_&RLXCY7u9AT2GI)%aoM;Ncx#}}J2sKO+UDp*|-$CIcEsuY^~j67um zS(HU7_$7f9z|+!O&yvku97{i~{;W@)kW3P!&qkNQkJP#U}*a7&Yk|NH%?^mQ`yS1r_bHIdHcqn zcdp*FGFd!*W!Z&`muWOwT3Q-cUCLG!TlVc2-y|i*1_lOx z_St7Aj~t5(iKYluDt+zk!9nA1_fPSP#iqJj);j}N{`DlkUp8za^FNTn`ZULB<|#1&fNOX^|@&2TkB zqlc21{Q5X;V{Ab~l)5%TX$CnaPT;Xh*{06zM*ituw8L z^Pr$1-GDJQrM1yw!?O6!g|fz{ZrO3d)lcmll&YK_ty!5%M`k9+079Y&k98J3>f2uu z2tQVE{|B~`;4gby0U;D2K-lao0}9eUobq^`QV=+lM9Ruo5s&}~Wd@qjL}eyGMY%00Yk`6k!NdFkkHqPWY}5ONZ@$HS`%Oi%YEcB**sA_u&N=oj5J*7?7uchI zc<9&(x-dQIILdUE0SHw&GPdkjhYwG`_D;*NiZ3@c>a`#K@ZJ3f-pCXh9qp|%;}sN# zBAIgM_8l+2viYg!UfkE-_u`8$Z`s-MGtqcyEuxyJaxNBPmHf^<7qmD23Whuuuq`d%h$FFamdgiHtyx>S~%K0 zYz+*4Y6xlN+1mK_4xzJ4?1qb78bV#_*~hiQ_HryOY%@HFhH!Z3Q3SSet-DyJy+|u? z?BSSq4I5wxCrP*#guva)v+ib_b`0uZIJ!+}+9@z?8_{nb)NE%Nw(?Bv#*n#j$PT5L zpBU1+-SIJrF}RndlZ418qc%OI&(2t&2pb0+4N^f$h6z?lrx@r(Gc+7iI|{;4Eofne zVZdX|PUuo~ppmvCS_-q)A~Wu=G;7LE=u>r=F*B}9R`jVFZ6nmgtLj)q6ECY{MRTTs z&yIiEX+Q4fEnmI%sWz_KHX3oetl3of7zqCdx4#S!eyqIyQ??TOuViCD_`%`&{zE9N zpJiLNdGkcH#T%*wk#3Y&WIcyiRTL^}k`--n0Uou*D^XYgLTPRsAnX~D?KFh6r6qt6 z>_l=&qNoU!KlbRFMstR%PLq00P>pgqF2dPmU(ylQy6%AKF7BdsJAvYMZ!i|$b)(i`q@S`=Nq*Am%CLjfq znjqBKLhNvLAX4JUOdLbJmprtH%NEfRF)gAQbVd%_8E=(xp%1qgi#hYMWLy-3`N)kq?ws=(^_5DSLQs0 zxu3p&xUu{Lf!yVG1>gDM8^8Yd-+4UIO`CVzciWnD*!dnboJ@1kIp5vqxa^vX zu3B^Hl^0xa!E=w@y*yP@SiKvzzFKcoB8jAc$N%Y1e)RUg{ru(iJCdOk=(zOGuhi;L zD4MDtSP~lJLb-Q(X5r1ZexWu;OJl|N-uvw@e*NArK0F-CEy_({t0U%cCCt{a(Hw+p zbHu3GopHM@ZnGz>j)cV?H#-w{R|+(n3fhf%B3tK%wn8iJ6b_HzSh&z~cdZSin5H8H_4!iwvVBYSF+q^N0JA@F2 zCt`Dj@ID8^8Td^$KhEg%L|`~O&TDn}y`HcaezPwaOzWM&R=#A|V)xIy>0N$TGd9n4 zguJ1G-Vofw)kBFo$LLDJ%+%iFWCVqyO2izgXMI1J)Vr|^76ODo zAk~X>zBj~lF$Nq$0>V6FWcE?)QklqjW?j`;$HZ&fe5L=j!}d38cb#_Yi+w_!%{>}) z*;Db+sqyOa((I8{qqHD6wjG7-F93ueE2;metwjAR+E#u9P=xyr^l3d$k65<*%f{*w zkN`G7UMMq@gp0{>;7uH^fL`kIlF*yrYV-OY&G*7Heoy z)6zsm;w=b+e^M91panmwBL#$Dj6kH})Mzsy)q$-76ilS@Cg{g0X&iwGmj|p!KuMWngk%EM@i?D^1Px%3>i8Av)r7D zwAC$I=HBXdCA$S$mEF%K)pre^+%RbJb%l$3)2PPkYu9>=6K|TQUnBFaR1YXh3kGJA zYtzKRev?;Y3mRP^-_L(^`z?2!_St{!Q*wigvtosZua3y(UZ*o+8%m^u45b~6Of-WK z2F8@i*rjU+8cGLHGXCuF;h95ku>?AgKMz_sU7k(rTVH%-_l1wGd*FqAn|JKK{Ps0x zfBSpqp7*^AFTU}-i?03dIhUUK-E+>n%nPMz!l5I=D?KOw{ERlX|bifuHw8vraV3%cx!8oP_&ys?%%!y%R zY{(GhSrC%oTa(lk+S713rWnT<<(lL0!?-qLY)gE^L;?l26bYnpOfjK3A+e?TmKaQE zOTgusqcFB1#L@>v&J@oYW1GSpb3|Z`(+FHsWY`*maqMx9-lwpH!h`|xin+1 zEx51N#E37;4^~M3Os3Nc>9-jh0}EBD2_oalivmxat~=(TYZI z8{(_>*%JD(*OaLm5{kn$vZj2XLe-^U(l)0)f!a)>5d1tB$r(=Ic#;*?fVQWhQw zMd|?pDIKwktUx$=6x@P~gzz-uz^Df?q2yGBIp{wa#UzQ4ngR=vN))S8MTl?;LYSt2 zXfvYpgeo8gLKniXh*~- zGjJY-;jY!yONuci9b>Ty^^+Pi@$-x1TS! z$h6)Ofq^YH2<7H@uJRjvKfUuCV`0WM{ib932bTJ4*2+OsiBQ?zcpZ$VYc0|1v5p;b zP5!_#{ibR1b$#QYrnU?=7{(77nPu+Dn)S@2sW93Jh%Q^Gtu1S+ONgwiA3!R&@Z*C+ zS(#H+=bMIy30F~`l^3R!rCD`lB^)8*!jv#KAuCR+%5wTji0D+V^IN8r3}}FCYL>&Ur^k^{pZ>_y@v16TQAW#( zNJ*Qj8gmVMrm;6r`&^sjZ|>OrwKdPS@}z33-)_>;&N4@W@Yr@Vx4%>nt{nU8-G2K% zGvDrqhbImm8Z`JHA22+p3w2q-q?R()5Qb~~P>J+Jk5-`wfi#dwRap{vQ!I@^Bs1-{ zh6MOko7_V}b*nR_tG^~s)ddmK6`mK(t73H^kb+Q~o&MOfZ5ja{(;bF_kU%j#0VN6- z6r}BlwBnT3aDr7-+fo0KY=gm%K+?06@gSrLPI0_J;{a?h>PI+`2}MYzp~j{(W6Q|URNs#r z;M&IzIS_whzix8by7aoazM%HyT%MB7=GehCbUKrov3Yatb$xD0Xpi`#d5L9oJ6FjG z=W*Hc#mb;e7TLSFgQXMizb^2 z1BA;^ehVKSt}QOns?pt{F%%Fe{b_F$(t%~QOAD25 z0HI_w$1_+&Qp+A|1Ryk6yn`a$UPoG1S`tJ{f@ocmAm7)T)Fc&@Bo#9$X!8mGq2Y8W zzG-72ph)19o~AXT@W99+rpF3U)YNv^g;D>JM%(t9N09`m6U4@(X=c)yRRu{|NIXo7 zAQ^+tOteNbA?GTQA%?`mP_PL?B?y#-yqG7;`;`_9)dTjzektHoT6WC;aLAJ#mYKEo z05?`+#TtBtaoFVV4i|-%kikCM3SC&Q_FAv z=J2l$AAaYTzxvU?|LTXo`qkUN_|?xoJp7|y|K_cq|Ki8L`t`rP_a2fre*A$hHv`&P z#$Pj651N_!So7El=g2J84Ez8^d^%v)TwJo2_uDE5?4|v-;y$=CeTZ+IpS z(Z#1oZ_@gbdwf3~XaNtPD2)`Ai7F_FT{klm8|_L;T}OG&$&5aPpY}e5pEfAk%X5LT z3G4jN&K4GryLs0M*T1l{ho?9D9ZoXWCCU#TXIkI5^G0HM%q7sxETtYLPn3`IC7(eJV+ zh1uDW(F~U)oR(xsUl?JeLTXm&c=^@lz>guYX@DOP2;k9bN$k?IQz(+lSRKOvmq@YS zuOp@Of*;Xj=y0Ve_au-cj|rpV%MP1yIbt;^=2WPY(!lV2IB(AHLUg> zg^{MC%CfP#A1?C5?NDsvI%V-$b9T0}{KMrR{o>Hies$pA{{8iT|Ly*t|LU!Od++tv zfBKW(9DetM!$17ld++}8gQcJS+A%q=7=O()^QL`#IX?f|fnWaSM~4sp;J3ed z=YzwCe)%5K{@^zsy!OlY-ucb@@BZffgYW*>H}yKX0J_>kaS0#VeD>M}|VK8eJCxIZ*J^N@@BPLT|QdxJ`PqrWzqA(jJ)` z%?Hn_`O!eeS=N@RhdDLk?5FEdz>lW~*9z%bUQm&hxmIR2r`D{C21;#(1p?riA1La} z981<8O*r$9kiU)~Q8J~eFK z<}2|+CC+GxQim{x8CL+F!32?-I6u$#rv+xaNMYM!kB&r&8ne4ksM%qS^D?tT{?tgY zEK8C06$L}kCITr<;Zf)QWHl*eAzWI^2Pjgd*i?iRcyQsTyA*^7Twxy5F9y3n*Gja^ z6m%YV#*0+pQs#k(R5cETZViz0%9VLC_N!gj#D~Pz#viuK0J-LP?H)AR+xn|!Ubf!n7W`)r;5-I_N0;P#%8HFMgnL{RLs8i0ERG`rxKe@9m&)5OpVfH592<2E&v)RY8-(99Lm zm(Q%H6J$;wGMm8!u23DD<^>XBTbUc1;iPA@kx64}%wC!Flost{2h1gCpgCJ{ z#*i5|bOV8eVRGIKT^O$l z3;}1L#8Z2=@-+jaX{03|dB`?B4%0KE3~5a%S?s>Hpomw<6o!EeCp6 z_OV%!Dw*_mlT%_3u)0w$w&Lb0BIl`N>b{l!vv8QG2xO7b2FOK0?JuQ zc1ncW0mb{WdjP&|YSFjNm|OZ#=@1DfKp zD!T-Lfr4siFQ#h@E|n;-`hHSJS|S3GBG6hrKv&kK-De;TX%#L=2}}Z(5L*;}M0M4c zX?r1V4U!AW8~Kp5$Q&^lBcBcoYXMgnJlI7bc@(&$?6f8`rO(fr3iIaT0$f^O3Xd)` zO-ES5MKo=CN|TyIAoYOpWL`7~O~^{THmM19ntrK}4-sTVKDqL<_(B3H!D*y&R+lVL zz>{RU1A#f{PNcQ&_->YV*csQ>mmEt!!ka&+aan^!w${Cyuj7rD@c`*{8XyNN3lMxn z^VOC-^#i)>xNtNJ3To1I8V*M4vXj~z8EbFOPtgT_p#niZ2uDkC(ps9L6SGliXvj>$ z%$bQ@-m$;isy*(O?PuJ+u1BIaxx*eS0pUkgqsKsa)VE_G{Df{z5Ef@<`?Q{CSk_&^ z+K4aP=SU5@v%}sTFH{l7nO-x&3TZD)O$zgKgSHS$YZj^OEsj{9FDuvC287z}mYB4- zEQpp^p^6|fCQQ|!2*Et60^xxR3gWAuG&eHSq`^Lhz^2ivQGk%PQz9l|;SoBZX$S=H znuc8(LWKeq5bR)Sz>^Wi>qNK|pu-qN2u7|Z2jOsQ2n60>Kp;O+2L&O~pjRMYkOa}d z8^i>YuuJOw((}SM-r493f8(_49%|)sUVqD&nYWF<-ffN!OH6Wu6N+#kSYe4Zg9dN+ zXdWOm*hf3G9x`@tjEryqy2#S9WKzmSO?8fN3&r%uxUUPj>3+tyrSF!!-$?^sw=VEgc&?wCoZ0Ft1yQ+%*XpYCsgiq#6{@| z%0E$}0Vu-LJjKc`Fv%?BM5@~882>c!BLp#NX-m!mO@QJiLi)8PKm4G>V*dNT`RoHv zZzI14}#@okLDhBV1(`sZlWxf6M^F8+^WsDVKTInf{X-yx!4;{8q&1Up znYHq#2~L>g$PJ%0RQJ;md=mhTO*+zRXr7T8A`_#Z0)+)yNkDUe2RB9B^aQP3#l64< zZ1CYFP1LD;qz?)S@l@&C0>P)sP9X`cw15YO5d6jQw!_H84s?2l)oJ!{o7knZG{ETl zqo~cQ@{`ud1Jc68h&41CtXb^IP5r!nXWBUNntuOJ`$C05sVbc=^HsjR9J?S^73b## z*-2$#99q+qe@z;h7;*;tEFqphtHE28#|2NpAX_0!RmmDOqBb+3PC*Egl{-}lvVIve zmwg4&5GIY16|EJCF;%KzNX@LX=l}N}<8e1{I&IDK?OeIa;`2E4*=%%XdUF5%sbBo` zCkzOG3+EURkJ|R%2*TfN`n}Tg=c4^92maXhCnE3v_06k+n_^7J%J#nK&Xw?+rGKsS z5z#in?95EN()rYgZBMMOE6>vEP+A0_H~h}jB?$Gyv+|Jtz-YMxmu5nfAcRUf z3PPF*0%=TICQ?koz&yZAQzET;PO$x-Ct?8qEN zFg+cjNwYJ$^nxxsZ<#sddHt)I4Ac zwgrkTtxs+7cA5Q((gK8_Ay1?XK^=n=l+m)jnr-y~O@1&p2dGjOe2PAtVv0kYswbzz~_5I!mgl z;lYWTXQ2^*Kwu9>K}a7axoij~W~3vGVu^+zA0$UYxYR@aaGQBjRuNn=k*VRlD@H4a znIyO#7`-DHJTxti(kBKFLP#p)#ykZ2okXvbJfhWygX9QD+U9FU4Su`;&9S8P_UYxp zkdV5H{DeA3DpAe#B@nUS;L*8LVBv;AO^+jCnK@vXI>Zf)2@HW~vcl1Lw(*qgKuKL* zlBCB#JE6bMcIStjAz`opl0hcIfIwsjBh|dQ2}6Do0&#_f@rFD)CJ9yKu`&<{Mj}ui ztIAeEsERbU`0D@M>H5?yJHCGHbDeyp!9D76Xma`Z?DWj?^5m)_Bq03V2gl}GJ4)Ms z7YIKFHfn#0h5u3H$hOsxZNFh6Guuai{6%CX=&y78@NjN+7K-r6Ve76)o#`4tSOp3S zqhq{C6@m%I4OjSyIzZU(ig5L2f!MUyn?e}@5bhn8ZZbr8sTo1I$cK(hP6CAdaE(#~ zMv2%gU*Ja(gR{^go9?du2|jKpVR0oi4$_hWm(G8-D(P^D7H!0+IAFpN4ROFc(vDS} zCsbnqNh?U<(o`r|;RoI*2w{||^qOhr6ok~6LVwOsI-pH0nkom_xe3i%KjKa8AC6C` z3Nx;$A9YwFg9432>l;oqxOjjP(}*eB6)X*@96CEQvrOX$%JPD?%#`V?&3jEGkn;VA zN=_8$kV=$!vU`rx510%5={ z{6`uIVYo#NIp9WU=12PJRna<9>^AS0`kP#AhQR2>kn3TN(nAP@kuMKMZLg4$`ToFT zN8rk1XBu4b4C!B=2IBh7wPidd+#0Rhq_8)Y=E&HO%969Z?3sB(T4)Ryyca%`!23g9Bt?-3(Cwm%bDrW1n}Dd?U;ds3$(MSV{Dlk zSV1-W}hK#hfv-*G7YzlEYCCtMv}_X!VGO$bXNE8j?DaXyZdi%-E-1a&u;JI z>YUzG#5yrqnI>E9owt98U;PIkzIRL!9?k8)2ZYPgpVP8(6C>lAF#DtG?LUbe`vu)S zl6lP}zemTv#_hwy*_pX6jRzpy9;`|db z$OsTZDXNU_mLbVY3hzjC9Ewmt`qVTyanjwU2|`+xMBv1bj~e9=Jcs}n#m&ke?4&>}%|m2p((|(PtgN&o&dw^*D2$fXm3imnyL$|QUXDVjazhdFVs*Aq&#|Z5 ze7O;|U1xH)X}p^18_L=eKu9$oGNeUtvrujZl5?%LbwQr!9$P2nmNElLKq$za;ATMc<}J)E)Ag`b#9tquSy2~ zf-7g*CY`wjPjPv4@rTM>1G;cDl2_WI8%DGP&V;eHpcwx_pSRR2R#^h+ z5rumj+ko##bzzF-N(@?K{BV_Y4J}U7KBAzSxin{5?Ndr!3OwjS7$IMF3TDVmYLg9h zd<=nzpwG@YY6mvBDxcb{|C^gyPQK=aja|bAx3^IWG$yNl|ES5NnrMtOon?OeA?YT- zti^V0`|sb5N*iI5Z4`C?@%9Iin|iS+C_hq=|CJ2?VmDGV`nwNf_3;js>lwCVcNFLv z2L(ByG7J<13!w$Mu^LzikD{`QTVRu12=%b8#Y$OPP7LhWW_RFIjIr|Oa<$T7i7 z)&wYj8PWh$03n4UEl{X6c|*ggf909~f#FayKB=5!nhA-2wcd3lb7sT^&P!uL(SqV& z9z`Hv27`wt$0Z>Nn1L&#G=(7$$?3UBS(%(b4%8@mQHH9DQ@Y#~nJb_;uPH7YQwyf( zv@SL&%TL+If4J8a9N?=(YEN&Z%8ArC5+jPaeVzG>J zdQlwW)bLQG;dHbdryg?l)d80%?y-wvofhKsmJx@R^)xb~3Bxr(s47cLU>7iet53}; z)8`TrkQK;OfZSXeRU&O%IOZUhoIPUxauPE31)EKi$P24rrl&Ry+#C zL4WkKZAn7a}B`>pLb?s<5Ge-n=rHMK( zSX72D5DtD>v?2+YV5AB(UPFj7T2;kr zFv)0v>rEpQd6cvu;_{w1rjJ{v{L~G*PrLEeUBg0y%aaKjvW28vp<;9T3%NX_1b_R% z`@rC_s?npg{da@#h(^BuyfN=jME;W+@y=f_GZFrJw-4Vx935+PX?#zO*tYmfBR(>k zo*OLF4l>YEz#-Lz@IwO)xf4Q@lf~5mgopzW_DPK`?!3HwfPzq-o))C*qDY;VBeXz4 z@dOWwCxWY05K?RUsb}R6@yCq7iP3qKWZ0#pKLRNTX&FaDV6+G$b)HP+C#eoSVx%CF zP~_0!6-GS(CsmCkH{_F@ri0!{AmV3dRR!pPaa|E{rlt8=DU=5!U84uCOcBm?m<-9#b05E-_5 zYLbd_4(>`0j5RaI8L>;#;w*@UaC$}@g26+KsYq-Zm4l>$l#!S;4$T=M$c=hn$dKZn zem7v3Ms8jJHTo9OzbSoycpylET?8UR^Qvful%qf_{ktPAJTScWcyr*Pj}*yiCgc>U zDXP96GMCWl%?hQsR;v*u z_~CnSk14{VwjD(f!u}Hh_opKNKKcIRiq^lf?Zd-~@yQ-t;PFB0EA~{IA%HJs$Oy_p z3NT6(S!zm}n(MPiga$hl;ci!wrMBxLUlyu;3j<68nC<>u+bmapm7S*{)S#nH2c2HVg5~Iw@ zPNA4{OuY*b_VQE!;Xu4T94hl<=Dh;daAleAuPUvfL5*`=Cr=Qm7-xT?F3!_)kt)#D zVF1XP67df~2)D@wN&;G@BPO{XW)KB-QAShtAcUZi$%j&5~a@vXF+*ZZh<5hd%`T)NmBh;8GaFnmNF*OVX|y;xrS|tHtfpJ~%W! z4Clp_5MwHhDa_~*Q&j%qWl)qBbQQuvXXB7(>1{<}xkF&IyQ9Mj z$A%F#Cy=#Hzur6k^ZPgTNJUzq*2xbQjAP68u><aw}KNV?ucDtNs&c&h)r+wwOzZ9nD47h42!mD%Oh zNK*Nv-e6IvC6!8z!QXEQ2#+mHc(k^oQZ@R2*0<%4L~iyG{kdrWAJ&xqRc;^BhVlEdEM$4}yikSeIp#CDp~x;$5DLVW9rjp{Ev_`Vd!&ZF{*tV6 zK$5JXXaFy{iK;S9_|*Ke29_uYX%Rz<8yGEXXaPhI(Ec@X-8M zYRMP`B13p^ z)%h8Daz>L}P^XxYh}j9-#1GqT(H^c+pz%Nv_Ik4;LR~vw_PE+@n)$KZ88iCRe6{o8 zJp)5IGT9iI1Q2RUgaJ)%;WA*@B_XSIgA{KR3Gh>WLp}5{(u>C~3}~lCJblh^;ivbD zXyVMcJiGGIfDjlWs}iK568Tfw!6ky2G@*S|F^dowJufIi|8V5S8R?HxAcWG9-V2Q8 zk6pOrPqSK|Ni&B_E||G;m5uKq z+$r9NNxZHwKnpJ+QXwskPtvBRl}u#LXqjou#v6`Xz;|1N$4n&w!d`uM^_eYWKYw7ykX)e9n7zVa0pD0eT*f9u(JGAIFMUcxttKYK!Ll@5h3=%SgHVC0 zWKEqStAt9U6?wb{)hJ~aEJJdBs5BBNZ1h&X-0AuBjhl|Y>T!TjXB!O~G|5~_V=yZ< z(ps&~{PK^1@ThJ_2ZSHB>it(bdH#{e&E7FL-?`dN=C8b7lyHz~A_$Mw_o!SCux%}& z@jgd#z*&Hv!>1^3w7l1w*;kkuoPK*(u+H(1n@b0~t#Q8Az*d@e*b_tExIyFYlo;D1 zby;aY`9a1}*i8vZYsw7NA<7OZ3MPDdlVxwoH#LeIly@j8NCD08hW2M6{?#f?9HONm z{TqGEXssq9lN@L{&H#^=q1XlR04)FpIRj&qf3)u_($ZAOp?M;j19nkfBNe?WZ&ACl;#cdKkBfAcyfbSY3~b{T0Civ*yeG?)^`eD(T7xv@A9o7 zmm|uRSzl`7auaiw*>|M5Nr)9qdft#NewgK7;Qh(73U0vIa*Oh`_sdZ5NgK! zoFO+$>ozI9R!9bXqR>xIK&K)J(n2fH49W05W)}tz0%?mKTyUKXIWIs8PtZfkEZ7Dl zlT?Zf9y@S?2Z%olfD_5%bvkaH zoQoM{K{6q7oQQtXl*{z>#iPcb27eUtr;L@QsWEaT%+y7M9C7lxf*j3OK=6b15<;Lj zHYtfuiJ}wmkk>LcDF`=&k#T9XCI|y^RX~fMApN50Fd_&C@fxBb z98jcj2p5@Z5(bt<7(ZN=#K&-V>hvT`1Fpx(($m_xA3P?BZ|fK|dq%mIsCjH=U+%5D zwy`uKp~NyO4rKAE0SYRaHC|+~F>$y;*Z#y4fQyTi;l~wg;Eo6sqjj=MTBwRZUa&|e ztcZ;vjwDhQ28sfI0j|`a-r~r9vCDJZP1{br;pG;tLS^x~46<}FtJ64@a@NFHjhSVb z`TZZ8V&-UV|2-i5Pg?c<Fbv8bMC+CMUxs7V;hRpchNKH~$7^u_gBcbC-E79Nw7mQgC< zV;mw$l$n$!rolge5IB_J^Bhuu3~WGoIzy{l0hOlqLPnf{RWB5 zXq{$Ek`*IIphd9TEH~jnxk7Kt;73B@XGT`Q$Q(4Q@FRIL{b(p| z>39r!3V4tTMye(22dw*l-f0bUq-4T@!B};lFU#Va&EA;7nS8ZXw8tLjOza=9#eAU} zSoq9_1y3#K))REzUi!gZfq*8HNUnFgH^agPvQhB&qM+$>f0tIQU zAgvF?QBr5P8j~`TV5zi#i?`KZqvIbC)5uI+O;eFmSD0=)vh zw(RW%2*s|Xb84ZZ^yB+?^Yt>h+!;|sO86`5vhyb9!vaR<^#%n2Q9MS3&=y?st&^L> zWk@1p!e9kPzdYzS4ZcC?Kzn&~%$#X-XXZ}m3jl=Qxbc;3gF>0X={Cv%LcP|dR&tJk z@PB0cZ&HMRz%6Pk^!ga;{^Q6i+eahl8T+p&%fIN2f)GE1V7a3W3k>d7qPcg_os zi?ZX&NJCOsa{Tbe4?XwXamRh;Nu!th`cH)Q{R6IyLSbTyNxNXLFJ?6Opa}N`tHS(( zED6GquWx>`f$weTp`{iDQUExlBH+m86+nt!i~cy2;sq_fSxTo#@Z+|4RSpr*7JL@^vq4=p0d+oB^8(AT$}gS~U+KT>1Iq z_kWCoM{E0UtQu|Ba8f&>N$-znzCRs#b^B=SAndPw_#nWKe+&pimD+BZ{QUG{IQ_AxbRDLV$`IkOP;7!zF=Cr4XSP!yXdC zlt*w;B%zqYq2?~E2{Xh5`CudolXH`c6og1cvSrjEje|op8ay;DO+o{qDPf34uMI{) zNXu4fZrU*O&K^TxfUo39t-Yb*E=RO)M5#7}#1^;47nCSHFYXe&Y>5i+BptD!JH?UO zUhbEP%kzr*GT^FB&+0RaRKB46g-f1TikLDz0}#R}mYURI9w~VUv{DphF=+`~K|3kL zK|zu>gegg0p*fyg8LMYzEV%TN05^TMQkcO_05B|e@BBr+&Vu7nywsH{gg;!qF5 zoAIf7qfZPEhyG>hgChRyETXNbF-DnTmqNO^+w_pj$j?c$GZH*li2nQ>4AM=UCjB;H zwEYi;SB@HKrHj^^ZEWx$L=+*T9q3fIu%?GB5Jqr9a-a=|}!T8}SXOt|@LhDGeD~gfDsGCOXW9b{K%uH%B6WZA~9^#K~+TCe#dHJ@e zzB;}&e(0uG`@~$Xz~UEb`<9&e=N~OWkqRasLKn1_-7AUNpp7MYB?x^9>jr~SrQ1QDUf9)$PCxaq|%gvRD}>B!r+kTA!h0ms4=94 zR-kQuq$zH-yA6yAniWmMgcF)Fq6*5!$aGJoaD~yfH+T?-gphft$)(~%Fxs3+)XZx2 zG9C_9&-B@nzC~&L-6zN`tlpQQOln8z^}vOZo+FG>*OnKXX{n1p0X{^4FQ>3F-a*7%lLpoljNd==8FA_vv0xF91$pjR^NYc=VZ#AZt0&*XsAAe$Q ztOnyJYQlI;oUD@xxKpId7>x$Q3yfU=34XlK^p#!-!K=;~E=--9)K0zjC_lKneb5mI z2_13E_{^UC4{zB#1Q7B}UamhSPE7Gbby28{Bu#|VM@>U;Nyt_nZDZ~Z}dwsQGUrMHP4k&B@p&-A2A_E`nvh=Ko6fOjWw5JTd){g`s#T80A0ESjS zHu(e>e##yiPDlg&LE3c)ekcf$ma+%sHCzHehKQ7HhyX(w3PTQ5puj_6Mo$Ez`H&<> zXcxFZAYFh7NoY)3{?bc@Aw$G$Ug}5JP?*t9zO`ND>E&tgde>rH{zz+bU$xN9F^LntEsh6gP2aa95-b7(D7f-() z=zB=$^$Nr63KotSMYymQm$K>>%&G5PtU8A)aq-;NM-6oI%| z`ZWL+QXG;DbNIqoFyhnOZNxEfgP=Qcbj=_@jVi6K%rl5c;| z)Y@^7rfS;d?G8wsi63lLxVkyRsqmwWT0|f<aZ;0!XGO+foLGaKoMa`&`@_V~Pfqd@6C>do zD^w$|acYW;i)Fs*u#5K@et7tiF+p@?N^ZQ;&36*Uz;yz|(oDYgFe|uYcfZvcXxdbxC9l9+)Up zf%`0sP_Pbb5oP#r%w=g41`B-I|jl} z==MAOd!PIcN>g(k3ircoI}{-qZ%5ks%B0soazDPT<;k=(mKTL*c^*a2uE5eD$PxkfrCtOX5Px=d3lFrwMIAm$V!qwup6q;AMMeP_6yh_f$*!}m>W*o zeQ}E`sc(C9CGe81POadJR`OI`j7tkYNS z^ps#--f~B<+z}{s`%At4G7KKxD?~6+dUhx^GnkwnNzbyg z^X$~@V023}okJ#Y;itPKbK=hSC8zpg6FBKee1ek#Q0I_;Z*&|skRXTP!R4{C z^TVkb*kED`Mm_yV$oN^w8C*F#F*A~w9!kvMIdsP+x}ptSZFg+EGg3!B1M$gW<`lpl zj6GWBhRDPbc%|Wi;jNZ4ZL5%*@KYm=m$c2YytKC4v2MdYgT>1+#k7^lb)m(pUh3d+ zxJ?l9BMot^Mk3>radr7m5D|X7{F>DC%8=H`7;m(Uqxe*nhR5K7f&yGmpv3a#IsPJk z5da}OSb9(y{JR~-Pu;NNn>W6?mn~A7t)os^sa`SY+&T?^etHVO`ePtG%G)szenL00 z3d;wOgdgN47TOiQ2Zjx;5%MDxrierXY-oK8sU~7-jlc%w*lQ!+j7aAil2}?J6}CIC zF?-t;wtb^xn$m(GJH?Mr2{U6rpeT;-_Z3mNQac*2L;Qgy#9ghWq}ZeUQ)FjJg&xye z5`LPN1k!e+BpH~mW%>bvd2oqpOirMrfL~sKsi#&b@?03Ov~{kEw&%m7$DnF*W%nD? z+|_n0d*`8*z-LC8>Qj;@^{h;*rlNUHb4kk?o{4G8^)wS;FK?eAEBEF;%K67nyL%i7 zwvclq2tl>7EUtIQwC#_qgj~GE>rL>)D$Q9daSptwc6w4QImdVIO307TeL=u8bnpy< zM=zthY>sv>SJ%eV!&PlV;O^pEcJR#GI7Ya8q|TkpE&})Pt%$r=>}U}?NCc?^ zwq4@bDza@CGA6LXc1XP2#U9vZfo&__wnOCH%C{hVtJt|s;@U2CQ-goA(7usx-6VBx zkvO+XUE5^t?Q+i!g_qi9g>#eKu~BA+XPeryL+gXPMd{il^Y4}ic1nHQMDDFZ7hIeX zft#f6tqLD(v&;j-E^Le3yG`let_ti>kHX;Dru1%C`F5yD;7(1DhVRmbV2DZLYhqiq zkqxTAX6@(}wQsA++vZ4(MC$@b*!UD)Y*MR{9+M@jpq(tW2(d^TCSj`Nv^q7T&CJ1w zg3LA~jkoU@aJ{;9pU&XnT7uT;LmNW}ZrTyL49jhR`Xp_@AYV}ys=dDCIxF^k5 zM@+hyMznpE%~zLrA+0&2;cliO(|ZCMJu$6eRHrAV(fZXIpWf)x8N86@8iPlzcPsU7 znZ}{expX?0!Qe5Od?^0l(d*qSX7(d8iKcVH6b3gU%e79O)(z8YTuQYArqa2P8(f9j zB~v!)5E$r+HT|^G7oXJoh((K*xbuE^>fS}Bl`8dvW)^mZ?q)N&dL*WstkT34RNNf zEiVc~6Flg!1nDM5U3~x1k3X2II3*qb;oT#?4cpr^1~=OpRM+MJ!rQhD8Z`=;GY+1q zi{t~#kYA*&p`aSRKFQ}DGqDa4g0UJ5k`W*T?O^hFT^T0}6EdAeNxL9os?(DyX60UY zb-FA2CP4UiH*EjPnipT(Cy*#LezUmTsNyv^2EzZT?HC9@p&J3=`|p4F;fJxx6h7GR z?o)Rr$Y&|3lZg`k@#14--3k2I5@h}kl>3U>p&ItJ{ z%dE5x{`iV63qA`H;}T=rDy7!s%qy*lBr+I}LNGm{9e;a=+B?Gky{b`ahh8;mx|)h` ztKAir+ho;?SB@@i9u-wz>-CrWM@3_+N$PziPN>ZC^XoL^UIEKrq{a@Eh{szP@#MLl zEZdcaJK{?9JK_V*M87jJ;7aznBLm*ppf3&=eps(73}d+xBd)}do7k`?34>?Q9%b8N z)JAMkwj<7UCE4!eup`EDCr6xdmOTbTIM1EpdeaCTu!e`Nk(F>;jAM_(xONh=SMMJ* zkFx9$wmrgg#o-PZ{Ty4QX>4nF#2jQ4Z;SG*5x63ITwslIj6tp`1drGj6PUw% z_!*aH48kL^$HmqVOlTezm;!vGpJ(uK^fM2BYDhwgHfqWlGH6FQcRBZ?w9Vw}CbkCsl zIgXMYuFBH00;sp#tSC*^W058&>2QmYU>U{=Rlr5kW|^GihikHQT~K`kity!4dn9V> zuq7ZWjBg0;ziCsiS|$=&LW0qvFg4Ccwh1z!8hD0ZAE}jL8VliJdGl<4ju$Kn!e!F4 zG*T6ZD|8YWzArBfmPm_!sLBnLINmJJpBDuS(rD#Lef58BkbL^ujbFX~*^QlimB||O zX^UfJfKVnMnVMYbEOQKmM{_#{!cXW%8VNr506&FDb!xZ7dC#D}BgOzP(hx+(IFT9) zAWV&m39+UXangP!tsDRdWfnh6Zr|=FAXMm`dqjr4ff~Sr$db&wv@ikei4!v@4w?;w zKpG+dHnd5PQCFmzwAsQ(R9$8U#RvJ0&dOwab_7yFCLJ=uBWvoAUhgQqq*UG7lDKM1z2x-rYkYoo0=ZVFANtJ2Xph? zndz=Hu4D=x>WAB#n;pnZ!TQsaT`}BLxiwgV+a9j$3zs|MW8JCou4IE6c3UHru3)Jr zTJJt5eb~<*h^btL@Bhoy2XnO=qr643)lTn|Lzbe~Db*(r`uAv$7)f<3PB1!)c zgj(P55sGkXpEtDW+9M(`cFESjXu{&vH$iylB~?qmq(@|F8QEJ|jaGd{-Nn+h4-JLR zp&@IHY;`+V+riVdaWrjgHLRVZ=@`+s59?t2hPAr~RV~AsR*nI-n{C*|((f47ZW~m? z;MqB%gSBvsIJASUC&neshp-l|X%kz!VMGmgyTG(VXx_>*z`vQJThGyN;2Jjbj9d7o ztpYRLjXWc4lfbZ9Xxt(~APyO*ZRY8>2`w!O_imXJo~G@PI(JANTZ9&pkZ*!*7MQn* zZMzXue0<|?(4ESbI_IfY$)WzM+xi@S!sFK*hcws_gjuzGxPPxeRm>|$%=623JAkw+#C zSm|X$`Xu9Jrj;f5bfd#pbh$aY)-hc8wb@xZX*j7PWhNYlDy8EW6=b5B307?CvccG= z*9rgknoVcjwQf+Nmz!*(E=9gt1PGNX&M^@Fk8H<4_zB&}PvAo`tlpEW?v!~b2=Qgg z4OVzzvX&mci&+t}x-6;N#D{8j3W~7X8sVs{gJScJK(5Cal^dL`G7IzsI00%364S!W z7^%-p&Vr!i7rtsDW?B}gKwyT}LjpgvUXcnuln7)PVs4gBAR@_3!^o^6t0Vs?2th;` zAs;gkjZ`S5NpqdT*|kEc%RB9iB3x|(T$4AGF&iXFvmNpBH6J0(LI@4RiEv@GCKl9` znO5~5(}IYj@~pDiMmWJ*nRJG@$!6Q(I)&0jKzPAQw8?#bXVgA+>B{bkUA)ETk6Xir zk3jgU*%`{&Tv4Zd`)e!Ft{GFN?4v1*$FN$1`e??VbOch)(KO8NPg-12qdjDD2hE-k z%;F8hPj-D_lQ(2{M;)FR%<2rA?Llf5N62LhJ8U7FEoirgU=BwZ=5j>Lrhvsf+BBOr zWVHkls5ko!Ccob3Gw8iA(jIB>8Y}^g&ZX76Ven8>>6}WfLuc@7^*)Wx3sY*`Dy>JO z56F}*nZ_kmJ7E%)LnybBMnt7uu64_eUXjMZQ&{*)t59j<%Pl;a8LmKP6Uyy8iG?jR zazrM0;12V2BOE=OXXNlrFs{H1g9jl4EbSm$$3mdU3KxEakU+j}SZEj#8A*E~SJ%(c z4{!~zJ`QPCg!QsDokNO_K{<6hM&#XGZOf3njceRIsCsEg-4iHsD+_FuUt$dJ?w4<2 zYlng*=t5qMwABex6Ti@+@n@0Kxj02Rch|c^vduF0z#(q=&|i6Y{x+O3Ejv~ z;6pNq!jrFUm%1O|+4}PHqDXz%R{#iU4;LVi_8P}7H$EmTEOuGKY^9|~XxQvdb{N7+ zlY6hk)T|nXB9!E*5M(CDS{*)ttc3A88I~Sz09v%02~3bA6aUkYCJ5o7h8(aOOo444^OZyZ=F6uy*(46?U6)8#)?i2E8@Aeg2DT<|A%TdzGQ@(|t17j7fHTqrVPl8gPO#f?47p%^+qo zGCb~39+BbkMk~a^Wp}U$bNF-iK+Y8^A~$cO=#P~>;R2%Be86@FIQInezGx{BuZ$+D zuF-p^Q72c8#WB)MNL> zU7@rym~xCJVer6g{4lBEv=-lpbyQKC-55G_`{p6J zh|e{J2+2|th&<>ijQH}fVQ-EdC{i5KS=`vZ!iXnF8Vh{|9EGv`1!7DHK3j1O1Fp=F zC(8-sN4%L%U-3KL!Q);S`ScCjzH!^St`V`$5lnd0g<8pE@*M-=|I~I2grCrj`~p7s z5Fm8ts+$D1`#6@t(vmPdh95H*UzczHmjEDYB+S0Sv;k6=;zLKgc`qk914P&ybEMnQ4+_m4AxD zJp5!PXgX3pHAy7k>ZB(WikbqM>x)#eGHCp0bo>!^Xj=$5ASTYDDJ{5m|6ZJF4jI}H zp5fY%Co%`~h>Vk@0xF7I?Ur&cM(nnMr&n7yjjdbNhpyE4Z1q1f%ydwaCqH5$0Kzt0@#HTnCE{yu{bE^NRQ7%&Bg zEa5(#zh561HibqEL6$KD8`gWN4e2~^M+`m~JY2n(t@DiNJuHJ4Hmq~QSXwvF7#PvG zht)2&&I>h*|};5{9>a|sC5f8E|^H?kr;e3Q$T4N zg$cAim`ERx7=uz%2rf))7)2avB&Ib4lN$ZV1iLV~c@!=^s;~s%>g_?3D`Ip;wbr20 z5KtO|Dq{$yHU+g7@G=h5m_sm~HA3b+a3>Xxh}sj*rWxq#;?6`bFclCt$P7Nu05=+FKh@N zylvB)f(P05dM#B$3XZA-I$eF z$kZ}+C_=vTE}pH6^oXkuMaN*QSc8+8;3X!w(FTl6%%5zC3iDmgnA+?cP}o}h*?k(n z&g9-FbL?O>qd`5RGO9kIKGUgG%gKW{4p% zqy;RI6)u3*)?#;cg#L38=3KN^mRu71_vWu*?AdT`S~vZJ?RwA1a9?V5TtuxSwKGuM z6|J_WCT-2p8ke>5qmA}NF#1QCdReEC>A*s*L*Kr{gjYvqx)ReJu}K8N)0>`y!2_c) zNnmb)c2OanR>I@m@#((QTz_@})}18Dk)$&|)0>>_j*a)m>iw}sPo&Wq8z-4W>fMRS zzVu9Mc&t6z=*!G@B_;=Q^Zl8b-qcigVxlWP4$nY#7S@-Z?#oW~q$j#l%wTsY+8B;B1|s#rXk#QX2^+wvBV&Eh`ap7m8?Fooi_Bc0bygUvtHOy^2LpLd zs0`0gus9INv!hjBf|)WjT;YU@f>>1;ukj*fcCY|L2wW&CIu3os zn3>v*d@M4CU7`r1)!|SHcZz#qjS>XnzHp}sX1XQZH4r%LObbTKq@zu|(HEJxXux~i z3*1lLy!FHz)^BU=muan|Zf&+!Ht4+o;nKn)e)S9ikF^mVz3tBi;mX=czleN%fuxTa zvyQ}{SZHZw0mA=$`-4;b!RiyuMGaT&qjj3-p})ZO3qSnecOQK40YJDz;JTeiAlz%XK8Pt!Ml$wY`qOG2n5)po!mn6*2NR#pj=5Ia8UdfGUS!V1b zsY+#1!Ei^oJ2E9r=s?*xi(JgkYXQ8)5bM+b4;xxhTR<9ZkBf6 zpmHD2*vdD-ZQ&XA2~4m(T>UP#b~oPu+aoZ-cJcJXkI(`2FS12ugd+rQi_o-JWQM`B zQ*7EUG{E-Atb3J?7KMG6)Us1z-Xk`*NGxp%N2|)Y8zEv7+!nd5L*{9fx%bFjyA|$j zQu{WsZI9B^LPoN?cPc#_CAMu!_b#m;?oN#lo;`->PHkwLDgc9LuPMIIoPg~%rg!KP z+cYt_y9}v4rVO1Mx{Eh*&3FbeZwO6^S#1-k=$T%j3x8Pl&+mD#d@xJC^t!zEHR+GSq1agWmA0tn@W$rrqfx2^A23HZaNQGgJ@hWI1>MUxXEX0cxa zKWM%{ksB;S4i5VXKnr=)n?-4FuBGIPe2> zA)QlJmsJAZNHnHT4Y<_=6^>HWHKP%Q2kl4&B3%MvbtY~Slj(y);fD-y4J*5=aVW-U zhBOI`A`?c>yVCymQGXetR_R?J0!;!BcBMGW)Z4G9>|C*tBR04D^SjkSj@;>SCsZcC z!Wb}GVtPwVVF{|tL9Jm_t@E30F`F}Kv_#a#QI#Q}HHY-ph;G%imaxjGDl*ND)2(4< zcT{O0({{t)hrt7NqSE@1o6Z=7VHbY5dR;)L^&3rLlR2W+_>?M-Ru_Ortg$LI4wKQZ zP&tK4o6O*m$Zc||S)s7#^=^&ELA$^xR5qpBE|HnU3b#<^}<>q-^8pI@!7omKNbSia4+?jvgWHBN}S(cOyfg1=c4p)7g1;4=Y<(>UO@N zLtuo9kewpSreX0qwtS~8F%+9*Tcc`oz~q1%XLBtt_bc{9%6mPDF1cB(_qO!P)^RNm zuTZoj{<0ucBO*0c6voP2;4NAaRNuI+)AZ_w7QNXwWG5he+C6vkx;`aeFs%1O$a3R# z(2F-(0R=%gmcKxkc>)e$Bfh*KR24{ad~VX!E6Oie=_FBwetd!#8;2a^h4FdoOF9rrfr6xS$|~yU zsKHf+ArOZsb()Za2Ncwh^#@j#p*RwRG#YXMv1nHCP!dxR(mcVrkLb%JAz)seSf?3h z^g;5so@B46Oj?6V*-X!fUD9VKJ*^yn`xUtjU|`8CZGr4AxmzZ8a?PWy8b^!P)v9*w zQ#$sj9BoElkI~ns^$q9)eL8=)+S8?S_h`JmS|4_YbUtc>T5tc(2grMMTQ~ei8jhkE z9Wex0#?gMAm)d}y1UAEmwZ1{M2QGFIA1=onLimu*#xr>M8jsi#6?@YhN0?_Bm0Q9x zV@#rtNDWb*D!^BdO0jE*!en;8*y0fwoqU5sXmY_XwfSHY%c$5KfWg!B!+3f-*I<_# zy+XZ9Y7cpWMV&Fcen{9JNU4ew>S#gfjG6-pqc18n`vtB9`7E$SVDM11ZK_M5Gv@Ke`h2`II3y)BMw*wd?OQ3hp={R7U3 z-jOhSa5TZ=d)E!CJMz$unIVPCZi%#VEPG^bUTR8^nG>dF0X9)$Oqn4QbF1+pO}=}5 zmv!CxJ$kc$&=yjbW}o)VuX(XcJi@}?aU@g{WymiA3KBR3|48T081ch{_X=+pc}tnO zw&`*cWZqG_&SIQ2E3$(n+FmG)5;aK#qObI%CVBjB%crk-^|aex?G!4s27Az}%8gYl z=23ufZe|w0`ePtG%G;jEn)1|Fk`z=HI7Q(#op+K4NFE$Q?93)fYM;hY9lpr-BE-tm(!V-^rF;$Wq_O=^;K6HkyyN69z}MiGKKRA3QS zF|;G>VFVck7!Ch8b*O+_ts2E48rU=%ZAIA8D$Uh;(Id%9bEcUiut`Chibf++Z*|~5 zjZboBCIle57KaimM&_BU{b0SsI>=G6m9~AMT&sRmEH!l~y!h~!k1dl0AJQ|j(voUy zzbrS4qLUSC3`fS;@o`~xR#IA&mY2lE1*Tuplq5F;i3t;Dr)lqe8iHMZa-7JBL<7c& zjnVG=2!x;ZynwO8RY{Wc?;i=4hXO^o2!V}+D+9rLZ+N^XIkh)9w%MH8Wy^Gj>%FNd zeEkggili|wSRqy7(fZ(Mc{o%Z2;^Zy!Qx1`%!*WC!=cjPXra%OM|`|3Bf;wMXl2k} zf?;=nQyLL%z(QH{I_}_PG*p5dwF}ofvS&`s@+7Bi( z=!y?}l5BqlhlZVTgwU7>r`GR?_S(YTmf(mb!m-EMwiwTuga^A^M*<#}H7W3BfOfUc zyM0i;#TFBcEf3kF2D3x!hq#Z8uq`hSDqBP4UQe2@_o)qG$j$8n3nw)>lAh*7#)aXA zAdFARhCE&u*50~upZc09zW#o-dn8aG!!4Nc543Ac6NE4T z67Lv4Q3vWsH&I0J5xs~*oKP8}kSIuy&rcdSAsoR)ylFl5^m=RN>+PPuz53PD?^w5+ zBT*;}QNO0ps97yRwT9OO;fEi-e+-02ZTmAoxJtD@6j=Y)72$s^+Q(M6|6}o2Vfh!< zQV3c2fgzFK%(mXaHg_b+BD%C_0|f;Ml%zxy0*4TYC~)P~gI&&;P-*KD8C(4My=uQo z=iDi>KoQC_b3_qlh;Rf5N!2Jbp$sx4lhoj+8>!J*89_mAxB>%pC<4KrCeOey$}JcI zo7{p+bsIc1S`&m#lC6X@q7eRO4%CQtL~elVYKD}TfI75?qA9=-dJbr}s-0tvX8<)HM{yC~WFN zM&Q~sxjBSunKgU4g=J1<|6X@?GuN`O-_X`?+|9M_)cGaZhPE~@&5g?old3Y#IH9dC zsB3e|+=42Kli&)&1Ji5=>uSHdOZOD`XdcFWB#$ zrL>6K6vifKT|F)Xr@-}SDhCvWMQLJMk)6kdkrk;*%cS2{VoIKxMIc!PJ~czvDHKG< zIl&ralO#@#Dl_w{>;lpXBje)OBm(i?DvJw!)={p(#@9Mt?N#nJ#fCEVev?aNc8S$C zu{_W-Y+XOB0peAO!EpSPN#_yk?J3NG&b zeqR<09(JaOz2xfwf!*flfFnixo-{zX!<+khN8sp(bU*5u!N+p`8S5q7xgCYb7 z7v|>i%Ri9o=Hn9$&6a7X%1*2+iF< zMSg*kZUCIz_!uWzWQFR2Xq}9sj{~Ju{MsQ?pkTn*k>MiD*=Yza0HQ&ZpBCnzAg4)(BZivvHk) z`B)~OsyZtxPD=8V$X1@7pljBvaH(WD_%T@q>U^B8Xs=}!>sO>Fq)gWmvS>T=_o5#z zq*riZw%Oz?Oi%Ec@u~Q;lG|q{Z9x2{2_e+tb;P7YcVHqUp}kCFHEm+bQa)gxeXGwN z8j;z|mc)j3(QaF~H{ECzXf-CE&Ye=26J0#>x?$x=w9X1<`Oc)&lGxIx*ebI21xmw_ z20Ju?ACV|p9WEZctVR3$%iCodM~`(BAiUQy4iE~5hRGvK-u}-)*o4#n{~-KN_1S+-5gxG}QBBI&YF%ja z0{#=Ae!?~aLTk3NQ|P`=JtCK{d^jFw6N($qLFULGE0=A&Uo5R$oR5@gO&T7$=nQ~?+iPjKj#PLsnkVrv`5-0?(LiNf z5?P2bNtQe$(HNq@PdSbJ;mQ(Z$tMabk~uR$IYl68AjC&Qx~dp|ZL8AB;b~b4Ye%fq zW(a82jvj%1#G6spmvQ@K@~t!>G;$_0@8EWFvv{y%by<8C47Fj`jLrto3bJF+(9nD4-g@+zW%}V1_+|imSI-xBr8_Eaw zSPM_?5b~t{L6f(?QZWA8kNX1&w%GO3fP(GG>GMdnvgkRzL7W{Yg5gS`&C83&BWGoHzD^ ztGg^OuHP-yIzU!=W$pp{)DH{xH>F9tg=#fcY_)()FEU$31*^ zdt#hDS{8=M>Z6cxq%t#HK=KDE}hm1k}V=0$O` z>^PBtX|hf!id~fNNCF9nQl1PMW>`pSN0U$$llTCK3m)LxoPtW0nC6oS`=`lAeTo^4 zK;|Z$QN$;ei78cby6M8SnK?~vUYQ1yz(^>hk5+@zKnNzs6^Vulf`FNKhW6QM_S{iq zff=&81zhZ+WPz8Y|07MDa+Y6=W`pHNYD(fI2yY zqFE805!7DWp>#;4dXCi6<}dFwj;f``POf2SG$$x6NW$al5RoP$u^KPf;6-PE3Vvu( z7@81<8`1H@ilxXF6jeJQ&Dzaz^XGeK0PR{{vDl%mOSrV3f zMnG++px~>7j*%&Gv<_j!PSyE|nlO}uI^kue1&tp(HK^-r;R~(4XYz&Zsp`W${hWGX z#2)PI7xgKepeZjkg+IM8Ttl8HrdiPf{(P+9I4eBCMuv$hH&($*LL^jZY#>w`NL7Yn z6=`Bt6rCBy!@)ODn4Fgzn;cAziAoEb-Q^A4JekVMa;G;q3OjqHLcOEg9OdUGx$*Jg zM1z$e19k8fMP~AglQuKFbz}ffdSW149*Q=G0u$`eBranlk{t=`sfd)SQ27GzEfxO&gpED{l1?76r_EveDD?6MS?~vQXawshHU!z(Xu78$%f_5iOqeY>V`?NfW@LLo;V zfb8py;9SM7V2K?laf2271aR;80gm{K_-?>u;M)|~M&OV)&kB?f-sepBI#S)X1VV7j z{oY)!JJaXMA~JSg(I@XTB#v)0efpXmU%Bpyt?dI^vpM26W{U-b$uCo}7Z(@F51)eY z*mktGqg^%ne+a@O`iM3;cf^oE!nu!5x{AF&av7@^@E24yil4%Vhu?qyxB5hNhsb`P z(A%Av#wRl=DKfFC6+qz3pTjgP}as}{A43IRk34w+eIU<8C@s+tu&2Pi_K z(YjD-z!gHEu4wp5rDz5Mk>MgvP5QG;(B?M+LWXm+f)O47k0zmh9BmqnCNE4&B4dUe zSrZEK0+L8oS{4=NMY-1$<=2IvDVhFzR#TV-FAcc`b#Y!g`PvSZMXs~(G}dl^Zm-^_ zlfsKbrguFTDUi6adj-nK`H^P#jR?rO6~dWVUo>DdUlFh16-sCY8}KMBt?7 zI^0E8prTAdNR5fojaNA;iNMIR1_qW6e&(!;|DWUjc1W$XmL^Apx;~{HVh9;RA_KZ) z?#gIQ5HB+GTaE+O(%hsVUE{^8yx^ zF?74z#2(T?mO+)w+aTcF~3KS-1KwO|m9GjKK zApY>e)Q5v(gMn&)sMa}J>TjmQQD7LGpj!}j_?Rn|7m!Ck~ao~x_D)~ z(00Gb*PA2D;R={9T(I!JxoMsU2&8Mu-DKz+{;T zc^0Icq8^wm*-&IAm056W3LfImOhOLQYD0Qz7(->!7nIh8HsMD-6noS@hC^Apj@-ah zVSbL6U6AGXi;@fNu40QV+8r(q=O;PUIoM#T-s#Ww1-wdiZ&SJYWu_6GpBtO* zw`UCI@POP4aco%pv7zy{x%RrIaZpo#&DMCwT3@t`E!ihtGmRax&b_Ojepfm9LvHC1 zE3wD}`D$~@0yCvR?Z7}{YA8896rSa$7DTxver5sJ!_Uq^Tgy{5K>-wLX!8pyQh7Kf zOi$yGxVXg6EwS?ZS;YgLk;XuHOqg35$t`oo-`S$FwhzlXBqq`9!6AR_iDzHXc{~bB zP@r>aI`QGDh@_S&sa_{#|h_2 zwP$Q3o|8)Scb}D>uEJrPJxdQj2>?Vr}cy z?9vCG@+F>a<#VK#4v9tJFL9zowsuw*FW+U@vT>K%5*l%a@e6v!Gk@o+{aU4hXN|BT zl_7j~1W7YIz7a=)<>Ap1HE27iSTgZ_pad2Y)Hzavo*bYE12u;s5dFm=PY&S#Btl3< z&S-g)C38-f@o!%cf9mRu=RdwxtoQ0I_LyH&sFVRhrIxpU|NdhjJo?+80m3GD{-B!i zzXgP3`vcT{EdT#-W*Ka&7xsIBpZtvhp&?n@DRw*{_V?xIKr-55K{~dquA>AH4uwh8 zoVvb$pD8L(*#N@r_S`-#S?+u{*V-N?AViS_&hay(e<@@E@&QGl&!8z`D6C1pfW$aI zToXjb5Kcj*E-c9Nb3|@t8{izRr398x3WKS#GPBSUvtTz2ei+r1DErh*?-eDoLP%ml z8J|$Z#_36=xN>H;4uZPOq&l~<>J#N3T-pT@5olK(9Hsn&AI7xS5X~yh&+-a$;?i=b zF}8j{yMd)|*%wpZG&y7z@!A9!xfeb3$f za>xD8x88f-3%5M*^4bmU4|VV#>kvHOD}AO{^kh5txxIpyxC_vaUH||=|Gv9<_?cnj23xj0ci;t8WF5!3i|^XRGHu~mhrDHF_4Se1 z6qx`oJ|>KnQQU)DkU@gzzbYG zNly&K$N7nAR%VVn@kYC5RBUka^!5#1k{*9`Z=l}7wrJcwl_Me++B+1^S7Kw&?UM`n z+5xo#=p}!P$T$N+BS5G!j*i%an%e%S?9;cr*d-GQ2MvDk7$Ah&hBD^G>fj$yb>3{h zKR*yCP!NJ}03i?vm(q?CByj11cT=z<~pVfCPeMJ`GwD9NYf8w?7AjO@Z~7!j%<{{^it*Vk5mq zKOi79C2G4R&Icud{=z&{g-8?zX%S45)-`ZNkRWY2IHam9?lnea22ZclvfEv1)p;}q z&px5EHw|er&j)_8GeW>DP1K<*HAUL4m=)@q9TosWRdQM$o1jg7@+1g1OP6p1cw~tQ zO?p?Be7akbp7C{%&xs>#%%*-GMMPf{yBWMKSz%#l? zn=D=iS)ngUr%eDXAG+gCnw;SDc;;pEFMzv}K+U)}Y&FP!j&&wb_8ubz6^*G~EJmrwZO@h6}7)vtf$ z3tu|%^T&Vbgs*+!J^1(~x7~gBBhTLcz%zF|^2#lbt-tK}kR`Fmg9 z`RInW`(N4j(8iAEcJ@EFz31VL`|jV^{^*`z*ljQE`KObw`|P)Fy5X_CH$B~Z*#_a= z+WaH>{7qXWFSqjB#O`f;<12&uR$p2?{@Qw1_0oti@>ex12e6u#ZNgv*7j&HE1cUe*`%HU3}Wh+a&nP=Q8biFcUd!gU? z@{nOG-?mjS`gD)=*?!&A-IC|Klp6;$+u5e=T=S-3-4>Q%C*Qi2ZG5Fyv59SHm3anq zVXiSDw&w)ST%SII7k-z-y+h!5anSVGfNjkd{%u>hFAbVDaXrr{e6Kk2Timsm6_NG0 z5rKWPz}D)`i?YjYD!owe5XrskIwT#V_09VD7LHt^vCFhxj^6u%HTU>V37@MNP+GeD zss7O_D^S^+pT20TX8kMM#Ts{~#*XjHhfMYBp4uzmuzS?5K3}#kTI2XDoM4R=sRDs* zwphD8-tA5|t2$|QD7B^{L|{`9_Sh0IfqqMefz&wjk%&n|H9-THU@;ocy*`9{-D@D zSYVchPfZc&mLk1fC>KeCUzQmcSeR`yMpc#o=`7>O?N-{wa&wE&0T4=4b6l{80U;6! z(_}Tlrb(G5Ktg;A`lprr=W#t7$bx8pX=jIiKWoec)8YvkH>T=9XG>m8i zpH{URF>$g^jOooUN;iaYf&`c*JxlJ1GRH77%XF>Gkwq;K$efETe272_ zKNyva)X#Xx`oOBO{q3IQa~*7xD<%=E*Kgi(=bAgtJL~*QFSz`yZ=L;_|Md^|-ExO` zU|{!)FP?wyd26n^=I)zsyLrtGx7~63jkn%%%Wb#bvG&dfZolK+JMO;cj{9!8@s3+= zy7h+Z*C4{Zx30bIwmWXT@#d?py5_8JfA`GOzH!FMC!BNU$5JExy< z=IP%!?Sk)|ck-FvJ>#@*o_YFNr=ND#si%JXjBkJUJ7=AJ`sv^O`q#g6@@d~blY2rl>pO))g=xf_+2}5B?G=w`ZJqWMt1{misqYcEw~v^2 z4;uCj8QX^qp!Htd0!z7XK;1i{9_A~>TKj;)wq;0r?+e}Ez5KDWzQ6XI3$8i$g3EX8 z9)9?VZ6}`ct?z#C>~qdK?>pyQan`w4p7Fg)PC4_uZ+z#%Gta*4w6iWc>GZQt{?>Wl zI_L85U2xUe=U;W+Mc1Bp>Gj{g=-TgIbj|m_yXK7ZZut6Tw|(~Xi;w%euYB&y=bV4_ zgWtaD-cxViaQ1G|`P&4SKG^c`%U%0Mlzjr@)^5=@RX|)GZsEh1%3FxAg~e13}W6sI#(s*>2R@nf_wwM{7A4&XF?eC7ifhiBp~=l+Y90LIn#sgELaG) z*O9_5&@k)53$ir0HX<$-W@FjDJ|*9Wq*o$-mj%q)NGfUr0#%#6u%b!%bTRb4Qo z8}L~2Q>N@ha_ODa^1G!sewu&n$FS^cKT0jX9bb4ewe(i;;E!BWZ&>Pw9CJT0&;CR` z{S{_qFCsE=3o?A`FhgICTaO}|_$j9TSV884I!Esoc7)d{{k0l6R~ zg9N7WXA;FHg{g7;u~>;oRpSR+)PZL@_)>GAr8Kcief$1NYu~{~h<-xAy6K9=_-92k*OU?LBv{z3bLH@4o%6yYIU5 zuC;eOaQFR>Kk&rE4?c3|?Lhd=x8HWxl~>+y>d9xE^!b1N=JB5!X?aki;NSb;Bj=v~ zt>>S)_oWwJeBj}yHf`Iw?&Vi^ZP{x!IMbQg-C|w4E4jtj*fJnjnA`y2W@l+zk9bID z>+$3`nW>%9z{s#lB6sMl5eROc#>rPZ6~=(Z9FkhYGG?V;vCRJHGutn}`nDUcy5#A# zm)?8Tl~1p|T+p+@rcyn8@8joPaQ-#dU3v9Y*I#wz<=0(%_4U_XbL}-(U2*xP7hQD0 z#TQ?A<&~FTbisv}UUCsaF1z%?i!MC(g7X2bKD%%~A;ldja_2>X+p33%0 zpnhY!X?Q@u*7&#<&nvFN)4f`a-qEM@4M$6y=#)4%-8=r;6)omhUfCwp`MNCuQEBc` z_smT%bjpQ7u3?lLEVJ=r36ZINNjF!Hg45=EX=MLm*a~gp!+5<;f%J3$2ql zUlQ6jtNfhAl)QYPDKV)rT9AlBd3;=%p3&6~p?n$8_yj_;R&85);|HUey2+iaj=wQA z`_7il9V(>}#fm|rJokIwzxVEY?z!v!XC8au@rRy#>d_~kef;U?AARA0C!cuqk*A(` z=(&gPf8z1So__rCCm(+JQSjrD`yamVo(J!{_u+HSzVIu@pY*>!{kH=hZHGU2&lifW zd-|y@Pd@zqPk#W!{_IEZCV~mFfb!uF=HB`d%a_$nz0JpZxG=S6_JL-FL5jbnV*v z?!0I115ey{=OcICanEgc-+jyNci(pFJ!|i}|F*Svfrbw}_}Jr*zVOh4k3IO%V-G(1 z1fYK9wRfEOmD5lB?7yCS^6`h?@E`v0H=XUQ$8WecB<}&jfBma}H_9Z1LIRh1@P|L> z6>A0gGQYm~yuGllL&R1Y2?(wEZC#RKy?uk&xlQKYGw9$c-NQQ1fHTq`&i2LdRa74K zm4+RKPETpr>Me{F4!`$rb7Qrr$5qeR55GTs_;RQhc<-+S(P z7yRp2zx|be{^uvo`KSF6Zh5Nm_=X*~Tyw3u_oa+MAm;RL*u73A7#a_a=5je}F3++i zhb+-oOZ%_gCGOrm*k!P2?HprP;epwX2`rE9P=JT0a*$FII;l>>n+IWW%E?s3Sc{$y_r97-$F7 z_1KdjAJv7l%P2y?KfIjX&J?W2m8K&ocAGQz=^~%qF8|cE+fTgZ*?mJ2rOp_&o0=db zZG;R6$rNkHwxhKj1K}rhTlpFM?zgINX`{^fgfh;_FAha2_$`tCqUjmZ^D{lo1s9W( zJO+f)%KScUNUE@Rs%*Q0^*t(=NM+qAbay8jlERWCGt0|N%kv9@>=ZX%mqq5bCUe8{ z2RI8qdm=bCl$dj-X2s>YJh#79?P=wxlp3#G7Z_2w9(`%o%{M%7?m5>Sf703KoWJIY zho1T7sb`*e;%R4m>wDk(?svX>*16w27GipAZaP2GjwOkfOO^9eo=8s zVvl>=nYPy9E3Ufr{EII;<@9fTOlz@3T3Xm@+R=$&b9Y~f4JtGZoK`Lo9_ZVKo0nUkhOQ-cjq1VLMFl{zoso{Fbkt{GHE!?u%D_>vQk_YVq*8sr-Z*D`ZoRd(u2UcvIR^ZxM@+%(9r9t7ZTGN@!;;EOfgzElGnn6%p4;fi zY_)_p*ovF%^-Y@43zqawjW09#`rB{*=+KXUQ+@5HZ@&3Dt{+%mnyY{K;Tw2;JNuLu zUU0=7H{N;kb!%=|a|`VH>u!R3^G&y}x&G#Bu3m%PH8qhaNop@Zn$7-Z=dD%iEv2{>rMo?eKda{OsMsdZS?xC;IKLe|`9{ zEwQ}MndfJgUy3c?xS8MA#^S5pPdW0}?Bes-5{@R+XRtk_OWn6^h|gDcsvJH3Tz{|* zp6yCapS_;<>MJ|>8f%X=Bq+?>sVuH}W)GV$8c;X~16fXZa@e2k^pV!SLE0AR%lG^9 zpc-u>Yq;aV~h7Wv;DsE!`jlBZRSsJ zQvTgF8^3+W^IdGYUTcU4w7E*jYzpWNqBmZ9{X;Txiabm5-?8neY{x+O3Ecp~4?q0y zcfbC3fN+!C^_V<5oSy9)Ewo!>EHWcngRI09A0Lj8MS98P`iXIId9F<#k*l0-a`RRw zLWNVRwr}Oy+apz|D_(3|kRwx~u;OH#^1kSt=)jx1MA|#n^}LXr+LoMGN0t<|L-EQ=1`p$PQIQNpX&N~0xb1wY; z_s;*`ch5WboQvSO_@XN=x%kQpFSzWY3ok$ayvxox`{MK9o^uJ@3opF#{PQnA_0(^F z>4a0h@wHR$zUjtiAAacmdmlLWqRWmy`Sc5}xcb61H(he|-D_@s`pM_F3Z)XAPSY_U zdUh9IULaj&)Wwyh@Bu>Fj|Sk^6c(UvwPOd!XborF;D~gOh+f>%d-oF?&%W~3&wu^f zUpnPm$NlZ!AAiEvo_gY0l)v}h^WgpWJ^avvkAs3RsJ*-Idf*{|2Up?uRub+O>sb_xXpTF`q|8o5Q{+Hwb-_LyMGyn9p<39a` zfBx!sE?aZg(_;LY$iyzQWcW&Xj^m68O~IFT z^__dkZKr?x#&3W3$`eld)<1sf`^SCeE2n<@yU%afxocPF6<4ge=iY|^!rN|L3l`pS z`#n%jAO!4|o9_eyaoIp1$On7qfk*Fo`00o4yZ507pS<|eo4@+C)4y>1SHE?_-w&`y zdU%qn);|2Tub+LzjhA0@-~CtJdFK<)ZMx>>+fMt=IgdZGPA;)O+b`=L&Fu}$ztF-G zOD%)GKMeIqi5LE&M|N8;_Mp`@9Z_U_KCK% z53S$c{@4?*TyV{u=UjTt&1umvWKVjkh9%1m18vy~jJi{#)06>#PgD_VqLW>7PIU*ySh9&Q}82{f|8J{B74< zH8ilzVAV_XR<2C_{3|aEvARqiZzs?CDq9B*ZO-h!t4-eD!{O_F&$>%D>=N@iQocUQ zvU{Jj79ZNh1_*n!Zt_7HBGXf}WarM`EZVq!k3ee!2>GSidv&!NpKIj`B||FrfIkNi zjs$YufigfiVKz-Qm&7 zfWPvPEOE+K$#Ktf0K#+cU*FABYc!ef(eAHD4u2tTnK z=`%(_xL#s^P&7J}ngPS;I0sgU%s5Kt9i{8^4u{Ia>H2m#S%_}0#IV&}YE`=BYWpk0 z#Zx8?G?ce?Ut-~MuQrh4C{jY!R%1f@g=%Q<`xbzyp53qy_ zyZo|iuD#~QHP_#AL_}u4iy#3baUS9vmQ=6W6Ve6~gcJ_04W_w6u@sDU+Lyi}sF2t|0;+WV;@eBkbfZoT)R`|rQ^ z{`;T4{L-6FIpx$7PCohcuN}Yk)~7%7xo;fzx5xeKiD!J~vnT$|r@!>iUp)DH-@Ej& zr`B~2@Ht!~PiSe?2l@G_b*A$AHoib<-~{qd+H;#aWd@yFs}J`pU7eY!U7@8%_6pfj zy~m%G>*4}~pKr9PEfKBKAyU~{DpyOd?v-`zYae*wj7x6%-0`P;_JmXZ@$dijw3EO3 z;tQ|d_ux~v+;r>hH{OFAx$%ZufjxRh)DJGgz4p2r!9|4Ma^oFfJh|E1?*<6Jf9^#m zo^xg2&7THnSe{Y|%t9OL0b-(BxTf0xq8WM~s0|O=pK=^12PbgG(sh$17;$V1! z6RhklEL^lhwsmu>P-pM8g@u**N9=RAz1$@f$%d8gUTFWWsQ^h)kZ6w?)*c-z!}>m$Gyn< z`x~}h^4PXcj#4U7hCHf#wXD~9)EXWXA%6VFKzNk5V<7y*ZcKCeZ{AZ!%CCxT03jl#*a@3)6+0PYKohj6lAA(<(U?3P^NJ~5$^I;yS09`*0qUe?<>qmO3Ng2 zc2*K66LiT^Q=Ht4`^`gAtLgbYZF`HQwrtIiUz8W;`>cMB$ix$98VgJN54ZRBjIW${!q-kYncUxpzjJ8ikKc9kjaObW zF;+SJ{x8!Jui2)3|L|`=eD6Knef0;wP%OMjhNUxgp=A2d6f@kA;i9ZaW{i_3=AqIpZWtaKMTrQw&mOi%8Xt?yL!2#xnX_43tMU4Qpo_cJVf6wISlqtpOD zfDi`HQ%^n*3PLR2{{Ug+U3We7-S3@u{0S$We9Ebx|MFKp{lEV4Z$Ep&J-uFrtuJd1R}G`%R&R12N3~08Q&pD8ENHO?KsXpKbC{*F2FZl3)BW*D-qgW;3Rkz# zsPP4q2HUXG*d?4C;P(StD zean3}uD$R6M;^HMo~N$5?B-KWJmFuC|MF?4e0}Y`_kI3L-}?49PU7-;-6O;M_6-b) z6mq52XdN|q;$lm}IW?5tNPwR89b_hfoE8mxY)|PJL7_&ClpyYW_13wr&{R1{Jz2pl5hy6bO(2Ozxd=CuGk1tF;#J^Qk+obcrn zkN=nRzjx}UmVKW)`TEcO>pwm4)RPZCwdc+UpSbVgCtrAZSI>ZOfNSa(c?adrL0g0y zEN{%r-`*~1?;0Fb*k16|?r2f=_po|J&MuAVF;ni5JzOZlc7?6SpX&{b0aUv)^A~KB zZQ8JxtG0AnMxh8Fvd`Z5LK~MS>X+L0*`poqQm-f7=`9R;a->q!o9ppp=o0s{TL)JoldD4>WpS)(H28FS;Sb(;ll(Y{9jh8WdfPD&equKQ!uQ|%H9)vg=6qBZ z;p7+j88UcIkQf&xSDFdQ{Ay%eIbo!rI=4?BmTTR6M7oXEOq;@`P}#SM+`ai}L2jNK zA4dVLNY9b+5=fYw6;CXi4!xs4__My^LSMK-)+iW%gBLEyY*DGsyJvuN`E|FRdgl56 zaME}F?u2iD>9p^E^P(FrzhUhSH{5jXl~-JL`PG+Pe$AzqUV6d#mjHXPOD?|R!V4~i zd)1ZK!LGh)%{5ow0QagZuZLZ6<#lJD14Ve|DW{%%{D~*^4zX2geMejSn(MAV<6GbR z|FQSpZ*83U;x_5sj=K%GAiO(B!PNINJt=|E`;d4cQDO8b+emf zH`(2kvzua@_cLfuzUTY_Z%%&jbA3E{G#a>OCd_ls{nW7dyE$3;p>#|l)M4{AwU;^oECc!AcYV>J}no4e34P{QPBw@p;1Ah zQK6x+-rm7iE*t9W9RkHMIDFu39UYxl-h1u66T9@y_Z&6TJ!N5KACXha!gG3gLQRWu z@GhE`Lzi%-A0u;(4lYxMm)Tldb@8syNB_cwLZ3==y1-J!*5&8uM zdizG%SU4Zi+rL-$;OS#0oI<1D-gEBa*;C@q5ykY!oX&@&j%iWPbl1R)tb3H(HIYx{ z70CLV9{wpozK~c+A~#8?>d9>NWI2{im-p8UZr1m$WMEo&%`#BH(q>Vka=dftv3luY z=fPFFd!mWey4LyFY>{skKBcH7Ik!Hig5VPt@8aPJ_t4SR z1!xBdgwV1O0`@>SyC8eCAk>oH)5{+~c=od8;loFD59~I&e7d*_z3+&_+0$o;Tz)-g z9M_^|%ln$;qfPQ5LhE1=MOnaT!}QFP$M2_%J#{N(W9uoz7FAN$l0y*_izZ^(YK%w@ zMhGCJP@2IA8wM6Iz4sb>=1L|XYe7ijiM1f~YFoC8Eg=zU2ndx!)t&Pw^?0p%4kQl( z-8o&?F?I^@Uh+mXl7vASyEtp$bl8AfS#q6*C@+Ca@FG2V_r&^FcUU}!Oz54GRJo3u> zJ9OTDZ_mCHr!Uxeg{J0KHZ;-%1+}!y`W9p%p6%5zv>=3FZ_jIrY)3cv>tB>8N3&7f zuI>d4j+s|jkr*9obmiKa<0mbQjEpay&xwy%8R|r5#amh0`uT!31q1(pdD@mjZCxk? zv{BYE9acpbwGXMg$MWiE z-oa_-uG{WCa(2gFgZ+n&UB7m*JSVNZwBpuHhoFE^U?F&+mJOg&fWX@hu38X!dLRvl zfI~PSz$4f@D8kFf(=RZ}%F6k$J|f)b_4Zyne&YT2ckI>Qb^X=1`%HG|p0;%M zEUc<$G>L@DVY;9*rH)M+T1r+fC)d(w963=vm8+h~DQ%?mPF5~_5su>Ei9(h@QzFzf z_Rb2&A4}%`teSi}F#l2S)9*7{*CX;sY>q^v8YA%g<4apkTE_3wHPJhAN`K#BlPedq zGV^=`!hL*vyuE^)-I1n24|fn*q=8Vo1-c_RbVe#jwIGBmh3ob44RUhzx_ZTA|NbL; z4;{6+ax6D1MgOqbJMX-A`l{Kni$3Sf-5i4x6S6QwrUEeEB=5<`Fw;04nBJAl$#Q_@O1j@O^lQDrP@6E9B;6RnS`H zDA^!={)s(j$|kaaMqv_KI%~TpQGNF?y)*UQOMtz4%?ze%R=WlMX%Pr{c(RCH3#4vj z`g>g8B1$t;Asr@Y=OFEzL-;2dtW%9*yXWeA7gIT!0M5XkBEjpoa<7Ht6*qF&O}wF= z7BE7oq*p9y0ucVc&NADJP`pgPLwX6qKWO@&AWUUQLzuk)LZrjz_T=}-lwBk1)ZsNE z(mJ?E8Ce6*qs?pF#7wLYPM^RuVHfR$M;b8pmJpdtb1evgr)HlzY zT;q(bQ2SPz``6kAm)Qf;_k7FJ6enVh}q|4_|H7s8$D&KBC*5MS+W25P}QB77z##79JfQ9v=`8b?LI*u-T2fXotTgmYfz79KI__4nM3RA#iRn&-q_(`et~ggEWKE3@0aHi2Ia@Ej>FU(j z-*WJ9^Y-@e2jO$PyXLCC~gnfEpzJQYSEl;7O@b+YVMn_?*Ggo3tx_-NIKPo zCRTV%g7H;zZwE76MNn>HSVca#O<>lt??-AhT0k((qh=G91`Ar8Qf7=nM zBSpS)MlKDh?DY$Fare1=>ALRW(;w{B_rAP?jxPufOEoxm5h!nT?!vj#mo8qlxM6DN zaLb*5B`Vc@6mCaSEvt&(lQZ_jy@XL$*NAUX#;X^d%bEd%V1(#4X^d_5*`L_bn%q^X7S=rlI-!?P7X<=jr6oi1XEWkemFauj) zp}n1xyQ_z@ql>GnySuxmw!P5E$mGNcgHxxE=^i<%XK?9_oq7k3T{E`zcaJEDDP$y8 zH0RZE_=U+`%+IdGG|&a82BxL83z5S7W$RZ0wGqr!dwTwUA=+j_Kp=RbuTM~50Maie zH0(bt42z75j7f}$KME|hcABm>B%Rb zQs_-n6ZiTir@egwQ79sYBa0;n&|{nAi3iOiYn*{i(%>R(c%HOyKY=VP!wKuiO`~&@ zfB$()teO7m%Wt-R{ptCyTg7=rj}}I^e*Wp>ryp5aSi3rVc)0p_xcdVe{#&6uArJ5a zA|k`@JpHvS^!EyMb`Jt1din-jH+MLwXR!PI_aiKiZ$1AR-lTF$YeF3@d%1aV=3BpP zwF{*^J$+lhyja?Jnvz?NYgVJ$dlH3HcZzXT1{c*a6E2uaEvI8teRtVi>19-=P*JZL zP|Yp3&VGD5xlu>wL&K{Vs^xuWGWi}M8O>~=T&ei^`QN|(`pYlwJ=yx~@z&OlThF#W z-n_T<{L5coY~_^_%x&y^{eoOPUEva(o!nsyP=~*k+YT<;>vDBNOcB}XfHZ}|HF|jm zxO(`Wxp?)U-iaMMK1BN&{Q6&;GwYuvWM(^?820nC{`T>vOd;or7=qTO1{?`Z;U(8H zs&SkedV5y?J(p5aeItn?>4{a&Ip(t)v6Kdm20$1loCwTsB$1k`nq}zjX;jZLu4}Gf z^!}A}>fMw=5>Hy)-ix2yuw;zbMwQSh>_%>9W!FSy?^2C=4An6QAVexf)l(pRfI96k z3AAFOqGNo!JIp9R5ss-!6C{3dPmKE7`S)3ewxw2B|!q|V(hYSuI`B-wOPzGkMJEpBd6 z<ZQ}@CDnz~oe1B=Kq0%P}Ki|t<~jI2{89uUVi38Nd>fwgp8Q&XEl z*s83kBnO6PpSoL!+O&fc#Ln!0G z3!%sh4hahgM7o@SzF1k_0x2}QVRrK5sgq|6PaZq-?klgp0&IC}$1A&z?K3dhefZj* zy;sfbW74bmB;jyN=NL`i69vYqM8yxRo@9gF27Zey4 z78<4PI|>0r1QxXy^l zizc^SBki3d&S8-9pYrK_5l!*nn=k%cs#_U>^ zNQEW{nz^0DXktu4rj3Q0k+Dl~YF=Ps+2xDoZ5+avpMT9@v|3v`ffa$j0w=sZ{XE@$ z0E!?_|1-2i0fYp)BQ0ufK0fY25mx55r}Yg!+_nGqjUzw&`SB0Ww(`+cj`oHkM&XyA z|7C9DaU+MHmy+Ju+0{F`SyD%0QKgOQ;XAUiv;uMiQ(WIQoGF<}DkI{$$75QDQ!9ux zz8bCQppHMFb$?V`FEhAm8Jv(tkj@&Vux`iZ)-VL(HsR*OuX(C@sY5_^{dt9e9fGnLhYmUcu-=A#NI6gm$;h}2T{Es+LRizl93%OK}v zRZv@$HR3_i%%&@E&LgRW$!f0WYH+<{*uH!9$ZTr!_|7>bwilBk_br1>B7-G5rm8w7 zDv`?1;Tq*=oo2GCV@%tXrn+OSQZ))6tK`F#qJETo6s4XhlMkiK$8E{dSJPRq+ofId zNk`M0+qmM1uJ*aLm9|zjfbj3%dYwZ_?} zpnbTxrXowp;06?C_;E#nwf+56&tschBx<}zPv;4_$5LLwqdk-Df(RoMbjrZQ&cj*0N$KO11?ET{g z$F5!th>owp(bF3EbZ$38(AC(Ylqko`>zLsc38*|GD@gZi)S-QgJ=l9g3vXXg&50cY#&g^a29@$yIJLEWw( zg#dnN5uz0-UpHSjPd{gOH#h$f3p4BEy2p3#KYHoNd*vmyDOoj_%#2Q-H@aqY^T;`i zLnkkqn%KCx`W9E$Hn8M%j8-&7f*qKPmyM?slZY%Swr4a;Itw6d=$VLX9Z4-CkelSy z%3j>qYHrIQS~yDW8mDPS@|(J&n}@>CR5XrCr7NQER-Ha?c=Xt*lP51+GIuq&Vtebh zCk|gj=X81nC&Re`$eo=K1a6Os^o0x%5ia3BePrCc5#+mg0tKCKBfWuK!4920P8(c1 zdi3z_4|m1fKEAN9(AKr$8xrGaZ&QQIr_-7UR5r0i;p`qzR-8v;2~sn18<=f$#bEyU zBd-!Bs-D^)(%k7?@vGqA2uw1o8$cM@J{z7(1Q1qp<(Qs%cy}ZZt^o+IXHs%A%K?P7 zk|6-0hhWhosf@wo00*3Ga6P?5_7RnqRR%oI%&erltx9SbRthNliNO#-7#CBnk$ser1i|_ zbS)J0trYg(%j;Rn?pnyw%;)wjm5ywtV7P4g0Fl=8!*@Tz2|W81-cFxyJ^S}(kCu@q z^QRxNh3STuE?M5RGc>wxVhI35+6dtVUu!T>wB53Ww|!4vzW|`1oxRg7TSsdvI{=~K zbrWQi+d;i^=TE)Z`q$RhPa2sdDj@Ktjm5;=$kwxe&Wv{VXp~E{li&aK%O)DDp^;aI zm!%0us0)9l%zsV-1zh-oHUC-j@}HX*zhKXQL7IMwn|Xv^{fxBoSxv_zrb#Vo>+cyD zlxq62s|k@gsN`}=OlD0=PPK1fES#aAZ_xJ4GN64CTI@jp_JBG_8{xJN!X9l4VrW1} zuwST;Tfo^<7Z2+n*FSuG*S;ffzJE~X$Z40{+B8vbRI@ls-dR5|O`EvSn0kyJ`M9ok zwW?QumSNOYyc#j+#wI}(}EB(TBvxr`vn9?1Ox^81cq9g z+Z;J`Y>)n_GkPCXRF<5%XruF{&cWkXkDs+LxL|nU+7-A1m1qW<&T3@K&;nU0PeWY% zIIMLjshUD$NU+^Q86u>b6yGzJ*gBeCfq<|?)LTDxuShyrAn48)_7;f-^LX9WgDbET z&lfX=eKk~ZMm5Phu}uGx?T#Zxy2mab*n9ZQ$pZ;-nSsIaUY`Cwo&he~t%HDe?G?cm zvB3WxUk?}J<>Bk;<_o+>YJlCmZ#%kOIA;PPzH{F`+slVjGIEYya@?bLWUt=IH{gxo ztv&DTKdN{3sZ9Rd_t>Y{3n#sF^58Mh_Rh4)&Um4vo>yk@FVJQT9XPsCX z+BzAW*+8UlkgCzHS!9fS_hQlbV*p`xdMT+{R^2|(II-%=U2uvmAX6B1O&w^>NS$V( zu4|@VJ&jBsqyEptF+e_~cC>REsR&gg10<^2hpJ_xfWRu%Xq^(7WgCu!1FBUM)rv9r z5r`_~c%^y*-q?ydmkla9bW9VCf{RhC7A~)CP{W;AoO=nv|HJeWgn#G+AZ+fP10#&! z3<3y=gG-FzRb&Rsp(Ui9ZxmVdoH@Bkoj^uVG%en*Z0%`o>#E|)3p;1Zc}i}_NFzm& zgW+b>v2#(JEPP8LzO|&WHJj2_gyWYJMWy(*0t~MZ+fqglLcmu{ad)ethrwx)C_Aam zZDfwP0!^)`A>>xqbc`)Oxc_m2f9Q2k3$xp%X4WQ_HrnBX+Ct6lmZO6`_@6BlZQee9 zu5MoL9#GzSfDr;LuU|7hd-nX1L;EhDI{*BKXIsB)bxPX2T&yk4u1-%+0&bOx&XJM1 zAHRS8>|a|IrFDg+_05vrm_|_!zdw`Plf~=FZt2Zz?kW}x7IM1_1p|3)JvrjOoVMUj1RfB_hRXOH1kvi|ZYJAfrG&V0Gqo{_+QccW@yXV-l zP7+61!tE&4d1Q`IDCy=3WL2b=nCx2r#PXOs1u4nt#bwp*9#KAC z-u|AU&aN)M@lTt9GlvU7uEiapJM8iH@ON|F9zo&eZENp*_Okh*1G@VT>~}UhpHxu% zj=q)7o9~^uY<%6?@ zNvCYG7KF9!iV(p>PzDZ9ZmQ%+0fg0^Gg!?$fY30DnweTm41)&(Ll#M_@UfW~{d*BTW7+EJnXa_qhl_NFskviE3T7gWx2Bbue zkt0ziA1RU#quY8adRH%C`gP2cFZmY~;~C9d!Dw&G^va^PY7{^S5B^IK{?6$o2>;OO zhwr}ER*gnB4^d{Gf)UaNkv^Ze{&~W{B4ua=xQNu3j<1oX?lV^Im$mnDS~ca&_B_dW zA+4Rr>y)&2v-olrPu9$na|B9Zdl&5C3)MWKn%}NzZbc^MZsIEh;w}hoTSvQOM6Mhb ziMue3bZ4)iUAo70-rA}2%3E*j{7~nEeFycAy4l+zJ!LGMER1X*EG=$o+y8)b;C%2A z=`w12+vesiQ!^`b3pl_QjL`O`0~~YWgu%h%M~)htkB!gD$tjDDjy-+$)Y*$yoZJGh zTiF=fJDNJU+I#w!qUup33bCc7rMrj7Xm6woanx2EL)buT!;twn62BhLt!rdsC_EIo zrMi(vY*I-(r_qf~A<>DqedEp>+V9wR^z6CIm(HC(f9ga;fNykg06-c*7#I*56P*wj zdk3%w76`nFiB1IgffQ=%MYTKOTc3d7u&}s*fQU1vFX$dRU~u%%kpsK*_Izkypud0r zu|50FA31u>;P`bDBbUg8f?^!4ypfD%wc`njsPx8IQGdhzFR6-2Sz8yDEiFWIitCyx z(42B?OFoKMO=xMCcZf9i>&3lEXpN0W;qj{uN3Z+rIck;^lU!C>ZF}>!ww==751Bd* zI059-ZnYGIJb*+Spj_U5!GZn(?q2>T#uj?|$9Eh!cID{q+k%?W=kEwdGAoGy zLcDa8GyRygam??B5cE)!-{F7r_XTkrmtH z4grJ};vqx|yJpJO6WJ_rqIl$Rl}g7d<*aXR9)`^0whni4=GIn3LJffMo4@`I9{m4+ z@MRm}@0DJH@DH87`^P_7nyKGF$e7q*jv%WE!b>_bU)snzX#nZ8O&;Iij6LEmJS^j? zn_D%d3~@>CJ#60`PBmJs97ps{7u1t!u4)o*wSl=3i#OYCug|lY+D;6P}>#vXc+PTL6?5*QGlK0g@?4mi#DnStAu8s5A`f$O-u@Ho1V+!TNF?5uR-X7V-1qJoP3Ij z#Kjq$&?U*VgE16NfGn(u(ihg-uV0m%J7(hkKK_!|`=A|^M;r3mxDfQZ^1uA~`C{0C zY~e+>NUolV^}A-bBecH5l1l8_!b08I z^#^3LZsf1WUFoGCkf?T?Pv8A;RX-3xV#57N7}}bM9;;Rj(N4?L!BQ)qL?Lp`pQ|mu z_nkg-SkH?#Th9`@9tsu~45hhsdN7sCePS@{PvOWK==_#75kxUBZqiE#EYDU$k#)}O>WeNRls@dAH-NllE= z72axk%$e@p!vxPTQDlk~<@3P7&2IkHLtFBqoG%4m0rNxC!-0UST%3Q%c9W5`8NpIh zL?F{pHq{t)@f+>U`Tk;)ZUZI)*ORZuo-%fggpG2^n0CIQAh?^85o>qAg&4jt*;omB zzt4xtT4Sx)&(HtwsYkl~nVp{lhW5E+pUnG-cFMrlDMhkrp(rGULqKFe9lwnm8#KrE zmItVY;!5To8<$I(u`JPhr0 zTU%R~-%i~W{J=lfp`4W#o09Lqd81WUhze0szlD{aEuMR@8eGAqPy*fIxUB({+J%Qn z@ozCCdKJWag9;7_rKElGV4{tih0Ye`VrB0Knb%*pX|eg=5g!ww6eN7XmWD0sRg0u^ZmTLn^pNKuSt$0in*FHN{qekR={JitF&kf zh;eA6cnnqOj6OcnF!jysX|!wcK{()bXZoC;(zWlXOUZlJM}YfrXulqJI;T=ITd6f$ z)F_}6vKjJBunQE}4XT9u2x4lk4h!7MfkWvM=-m9Es3FK8nOpq28Q3IAL-pqx2GZ~{!nJ5b{8+cna88+532)-?zS{Hsgd02Ka=UTZIa5>GF7r%=~gpC#QEz|#+w ztc(atHv|)tOKRxU3?ne8Ix(ub_+5RP4qIJ*Y9x2{`&OykY(9e{LzPb%e~0wS)>0Gu zbV-uQMB|YCuUd{0ycUgBe`uk=#zt7En860*4%OH3%U4Ob%wies+zLFIMDO+Cb%P^- zcG_TZahbnKn|aQ)Cs^^PJbkt#P+m(E2tt$C4lmUM$VcVYY*)*Y_yZ zsdI%Z!&` z*+z1^-zs|dN3J)EVj$pTEntGts`1OOenY}bdA*lPs_1`1xP#3SKa)Q+Pl5Zgs<7Bk zX-Gsk)M+zo5);z`)z*>$5-w2TK%Xgu6gR)T7O>=*@HQ?vx7^VCIM z>WO$wg6@{gl;mc3I9%XGW_W!BJ;I*+R<(Y2ddV@=%hBsD7f zmyUhe#E+j)yzpGfDW^NfsAfIjtM>~mFwX$xaq7`iX^o@^Cv}M|&gy7stwNOtM#TGK zB@$CwIaknhXaA_h{EM7;v(eiB`gJ)HLiLWPpgqsBOted9@ci4(lQoBz^^I=>^oLLX zr`WDYx=WD)OY-#PWx2hJh#g)+i{as)3pJ2Pr12Ye)i|5 z`?R?NdJyi)%1XK!`amGrWtz}d9~jIIpcPfEwv3w8qKOZh2`&SB=P5U*cxuZuykto_ zjwI;#l6&5;#>w&HWGH1}*UJ`LnUw_+=bjf0~TOFkI_F z8V*(+kq)Y*v)i6Sm&LO*d0~a0xQ`f&lj=vZ<~zyzft2PnQHgarN1fb>9(f$-ZX;pL zUt0#+hQkU_5H^r0`5~yOt>t0wEw?q$=hpvxEDYKk_;$LgW#>7d$p2U53ymzRrC-GF zQ;$vzhSzOd#F^B>_lGtv^B{2m1UR7OaY~xmNZ9>mG-TNm&TX>Q=-tumqjt6)-)QMr zJK00{y{8zs&agY1q5qxm_o59Q}69t2Q*m7#J`0TPa|9W+XU1J=0uSY{{)TNgh?ht^QYJJ>%QkCa=q09rID@adI{wGV*32ZvJci9$um73tRN2_S*< zn<@)buSd-GPpdHh1s#{QT4+F&1d=3o9N{ zr2E^8Xo@&mU*FPLC({0_pZ-gc5SCuPXz^wZBu0hJODh@taX%|69rE~SHC}x=^6PlY z*(mkez4Suu`aJ8S$LWqUP4{Y0JsGy5kdHBsw1b<}P$L8mTmM)mC^sOAlrC{_oE3xJ2 z^7H_|MxscHLz;$#PJ);f9jO!*8yg=J6BnrzoV{y0OHOXOyPIfgYAU*`+DDF#;tAr0 z`F|)}@8pNQ)e@%j`vDDD8w6dC?()@_l?;EZ`mMYdl+XK!|D~sSOh1+6QPOAZst#TG zrR-2+*}6|x(7@*@t7-13>*h(#UrCg$t~60ua=83U>>BHLdIzzfkL1(A>OZMCDei$7 zhb$MKiNeEDY-}4pKW>W$_-&D*!pOAT%#QeoQqnSVp824rF-RdXm}FKwveTo>>)qqy zOKb}pv(D?$T|<5po(-PcW##LjhsmH~3$6A|Pdl@n2rDa1adr1pGJZ+t1P|_g*9JB0n-Sc-q`c4 z3o|_#jKs<{y9+bxbvzKj?+8eLMa;jAU-eBsAiwk$Nfp8@eIAZf&x}&|;o9eU=UL-r zwRysT!GXqATrNz7$E@^S1eVyR;>p zFd8lu4Z29Vb*Z1U+#%>@ezJ`U$pwy)nnZupA$+$ao`BNBX_I2;(c`5CMI(GQ=-s9v;#*@KsC z`DO^{p6b>TS@nFpVOeQAU_eU0k-NOIqdl`6n+E-bShU|cz_8ihqpOZ^eanzpDzuo$ z`XA?{<6U2UNOzpp_m=m*;kl*8>T;pm)}S}<^Wz>XsaIn@zty!Rc%EbTbzCeoceKu# zWm+_&8__lpE!U?lg0(~855U-17#ts;!$INqe?AH9q72ubjvCDp>*e)SKK!_5v#zNpJH=S8;I{JiBBSJy%^xwGs-m!YceQ|~JBs|m!A?EF z`$IyBwo+3R)7=bTO)Lp%;Frb1l4MERZyp|E-XPD`&r?*?9N+7K5A?(zZM@X2#0KDV zJIZ;AH#xie#BgvS>xjee=W5y*7sz126Z- z;?mO!wf%?r@gu2Vb>qLAK6!3s!Zkwy=j2~T8>2QMZowj`Q_|5RzYqyoB}CI*>6$$# zDl=kEv*9hDEYi3Wc!*87ng{3?4WmJP=Sj$6dkwF{wVdR&9K0fc=5G83A{THUy~O=Q z%DGbO_nf_t`*_7~?(w1Jp^J_}dw&ymd-wS8fOz8~e&!t#lowF9IYkzIOJQ^p;xB8uPAvVk-hO#$b>HQze*`k#1l`T_Wsd!9=$aAa=CV;0T($`&0u1poMO@l0 zMV!P*nuhbwHLLHZ%(d1tjp~T?3_h0n)|UtEjOP71N(qG{rK%{0;!+4orWB&Btu26d%QgoI!f#nlq*lvuSz5%= zgM_9&%`sL^Zs&O=mj0$%z1op$y37&E@3}=FlDv?{8_$T0tf*XC9ZwC6-0@R=4$Z_F zc~qLfpb&*}AMQ4Q9)#u;CB7}(!YaBc%kv-zWao)!E$ey*&pxh}`Og0a?yIJURolo3BsC-yRl`5YT-IMIx7i+S#rK0iu#o8OaT&q)k|l^FUr z)O88R>qL|ZZh7BZMmdfi$dOYrHf;BhU$kdCx7ZH4wE89Fa2!;Du!eM{aElX5MhJ#t zhh6#X0aPy_|3J;BbG^08>Pkk};u|Uw^AB zd{PmVkL`M5feapK3(vux7z}&OSL`(TJ(g=@QGRx^_pDGgshB$-o=Q87e|koMXDqC( zq2jf^xh+ga*UP*zhOH7|LioFt5hxRc|A&buh}Qba5$(0D!`H{#yU)_s_hq}ujofH}k@ct+1E)#~<)g$gN)2{6NJB2l--d0-z$mjX^6;FF(r|Dlb)o&CQA&v7{RK zlGRPA>##DL3}w3@N*5>RNNX$#ZbCSeD0Y=X?3Wvo0Qz@|#Wg`dtT&NWok^r-mZipO z$)Znb(v9@yN3Z!-CLGd8B|z?YpMuVkz!fO)Wo^%4P7ywnCDoeyjL;PnrS@UptV?%5 zssBaPqkJD}4cZR8AOku5|D$o@GmIPrgW@z|L(ysGsb}ARoNdq!_F*T(=pGYncPJ3( z*k0(CX-}JR{Cn>6m|LL%# zcGhntk=lgZ;}PEWT?i!*70lzA#PTfg?^-Th6go{$lEiLGVI;X%!(}I>g2U#dQ~NVu zm=o=f0nNzd?B_eSxUq9j@=945fQPQCxl1YpD8e6H;3)!3Nr?rvAzyHFH-b|FvO%#X z2sn?Y%T818m%p`Ncc7k;d0h*3LP~n+Oq@2IP5$&0?cWK%!pVPNS0n;f4iK==2QF&~ zb8WWYNdms~=W;)P%?>JRc`gUMD4%VNRR2}hUlua230%7y+eR!38`8+H@@k2iXnC4K zk{#CdIW}mD7Pcn#y+KlQKHbGcV1mYq3{_W>0hZN!PpVkL^LgYi*aE%i?KjP@9KJxwwX|t0uc4N7YF# z?ziwrf6lmSvX-*nb2X`+&2?RfY&KecwvZ7sRHhJE{1KxNoQ0B$RHtU+oCV#~i}y(g zTBUw$WAx2^oIbCPxaNcffEPVnsoBUh4dU_;reMCf%nP3>c`+K_={5$jxCe^|_ ziiuQ&l?opV-}5!_<0yA;aQ+*%1)IFqGP_5sBSmvny%PJb^yMFY00d>#FJv_%xQ6L(8JQQ4xG;t+dH*cXfWnyl-@ZJ zHp%*iY0wZVpdf(Q z(XO-P%vNvCSY4X}X4IT!#Mv?;HgjfqtaphwYZlf?+XG~{@fW?*{fp|FVMWR~`@ibY zxul>o9_8Ce>;r;+5SDo{~Ke)aLTq{ zkDQI|E2Y2z$*b|@h36&!6{-*_a&{-wT26Onu6qXba4sA5cpG(8S6d1;X^oYlDYZ#W zS8A*hhD171WkE6&XE9xvvi%rmw&7(0m?L;``}=y>i7{9tvXjTNa7FfbeH`J$SfQd8 zkLITGQ79`Hi4At=B`&h=L#cFyYTYr`uyUqAw^m*<09jjHslj5 zEISp@cRbNjN`j1n0#rlgm|Yvc3eU&yy?f;}2ko37L{8A{tRhK&2Ah&!oIOHZn)!m) z|LOw-U2O?@k1fnCOzlP$iMQ=Xy6k5`@1 zNu#>@oP0D(d%0=OQ6sk^wDA25`zfY@PZPQNY%jcKMPUtJz12{f5%om(dJWWC&-b0b za;GJX-VD%}pBL;RkA4Xm=nX;T-)jsW0VV+k14Z#uC@_%3bZaYz38WnL7rw$yWeJq{ z-BUHV?^V`b2?a<_ue?+Q{E=5lwYSldk#cNokY3@BrD>&J;GpIDHX~08&HBbxVMEnY zrN^GrOB^*_S*d{nJHxLYJsayF2`o$FX^uP9@AqI{@|@1|JF;7Jv+Vl23VA+jA*oB5 zNv+0)O?i?!n;@3xXcDPz0&G)vaCeYWB_AK7C*i@}!$%>3)6VZgyIh)_gsefS8FqUk zNVL7|M6Ve*^!9$$U2i86HZSrz8NJ%<5M-z=!O`q#d3t?&^MnOZ0dRJI{gus8wUj)h zy6r_$;?a^$!fm834#50f|29iZ$h3MeF6ZEr#Gt}wo)$8alnDl60}xom!19;i&iP8| zIo)D2+LHT5N-+-8GK%(V5PBA&; z-M00kb9(ukwg5}fnj3rJ{+~ZmX8VT!yl&h>K_3AINbFRdyH^JzXHn+=OXqh-4rS>b z#?z@p5+ITrIFS`#Fqo>^wzp@cm}6BQMe9g~sBUYwu`zN`PYc+6LoUpW$%l=(jrb{~ zBqV}UmFxSnYJ|UKEIn{V;OV#EM{w7D0$;1>d6rEYN|Z!)tb&ubUWmR$xSvkDvRR0+ zN>g!u{VwNU{kR$w6i*ck`w!Gn*fO`yON0YRj;)3+N9zbU3Koo*nZXr|6F4dF_vbN6 zX=Q>eK5J8-e-GSSv$H! zvO@;>vS_EIZG5v`_5E~Hlss*&lmdx7k&&`Hp$TM=SV#z<_y-ayA|gB@BII+Qbg-p` z1xNB|a8{X(MR=XLG_w2C_3B3)N;};{T2CMc?_63A8p(-GpmW|wp9O6i=yzkckP)|g zs*901Vw?D5O@j#35zeKvy`d^35N2#FFICtENeQSaXZuwYLQM-tRYarYDH@kn**fbJ zb`OVZr|B6OLtD)8e(UY#ql>)3TI6MK!oowk7bF5lN|qa+mWI2df(VMN{2_Drcn=Hc z3JvJo-0Xs=-KyjD*SQ}3>{?bLm3QgfakTb-cug^VN>KaM-e>9TNP`qq!E8RGdMqQTtD z4W&fze0+I`nt#Y0j8=qGqqI(rgMxUEdi(F6Tg1Vx`Spp%*#f6vm$={4*;{(9yu7mS z$H%^NSI7C_soSfzQ%<{snLmFoH4D0QJ|d^M!6H=#lY9xh9sjX;J6HCoEbKPtj#h`F)&l070NPan!wG8!rm7|{*V1UHw%*3(3^$|OkKHF zIZpjmR!F8YD~h7-Hlzq zw5#E05DU@=#1f{gGP;2;(x_io?xZ~g@u8y=z%tkrZ$j* z4WcK;Bgi$T!P)yy;1M^C&)wMmUl;-YNu2-iW`63w5V1e;gsn+AsUl6($oX1@T`hD_ zCl091*lK1K-J2!-4PUNJ_j!Kt@#$!9?+8YOxKxN?1oIFPTF*pTleJ)As4@}007W@! zh*g4K<{s2br-=AGKPOJ3wMtU2ds>gX3`ZOd3fQ8*tpcB2HPo%(!@l8Zc__p!omQ6Qa--V_*nv#wO%Kj#Kg@7j^{(lp;}G`3L_0#Q=*kpE?I)tWiWl_(sS*G{Lh?s&iRCo7{`FW`67KDhj3cr%y zl#M|juN|a8TfR3Y|E`S%L3zvhC1w5XkB8`5p`Q4vWU60b;*`G*aM7@^Aa0~~Li_=M z3n*_exDbkUSG+%6-&p|sS!dYh=V1}?mwv}xXy=Xk!v1&5%)-;-<98j7`9~(*Z~qE! zZ-00;=A{qhX6XI;-tHPj=rXtj5kjLYl&K}x$3BRiQBU=0qXys7Vh>XBe`|{ma(}C& zhKx10_s9UgP5~>JOm-atw!7S4EG)F3ON%wjPE;NrH8eFhk>Sx6OM_Z!@Rnr4hmwuI zSUH6SvGLHrtx=|qTdKp>YjHna;DcSPt+ji}4Pt6Z*=9(Lc;5YkuX1W`xDR*D?XP`b zHy#G0Q7*UJgjYXpr1>$YmWGF4h7U1sySa9alx}BPWo^?F(M|o0uDnc3BNH|5&_w;&gY1lw{j$@W-J+)rV3-qiK0CbGc8`J_rk7$4@DUS4f=~ zL6)6HXsZ~a)#G$6(?r&V4Ab%7Ra@g z_I)yD02rHv;k8%7pY2=J+1W71)7yV+9qk>Qteua~4)=bnZ{jkjoQJ9qMLFF$fpS{sH=abIIGkrd1(ybp$FI54*+LWiN z69lgBeVg+1IseejgN~y>vm$q9Tw%|hK<=Fopk?rWpnEfd(wTk^VL~naXZlugHK9Fo zekHmNnjP*u&2Z)phk++2#gNiWx!qRQ7NG8`V|ceExS=y+sh)OEjNstYmsuoP=1J`~Cu3Tyi|GOzI@)$Fviv5X&eQ+W+DI z-X$1da(;Doc({v53j+t0;O!Ga@M;4q(J$!r@A2OqBvtzWYns#VYaxICYr<5JcK_mY2D(40lnMQk_(57Im z`!sp!2N=1h2~&n}mO8K$zMX#k?PmY{<`sBmP<5$abFS}%=YvO&H!)GjQ3sr38Vl<3 z{=DQC7JB=HrE6`?`9ffZ4O%r=TYEi9tFA7)!GIlE&*=ttVyandSNVeg7n~gyR+yPd zShopzfyOk0$+R>*FaTNp`GDBae(L=^q1h$=>g(K9Uf!_o`zZdkQA&2whmzKvK{;SZ za7kF~wgRD>rcgD;Cx8vBTFmp2F{NZg*ODoeT7R&;=XyX;3pTSCAp+p^Vo^7l9v2(BxEw50)v zy#D-*ub7p4fa;eV;*sNo?`bJ^>N?xf0Z26%!HAhks$wp)AVj}+=#nyxTN^iB-B%L z)L{RI0Qt2~#6MS(2HR|b7TH`6KRe!SQ}QwyRg(X8xbfqh>0w`3Su&Nv3V&h2NfT}6 zO$Z#s2<9P?W5|+hn=NFoQQn-e36-xMr{>Sbo*&_GadEZKaA%9Y^?nHm`sW*C~o zki`p1EM*J$oc-=Ajvjf~K#G6WGj^%tTAUYly-E&Qm4p=k_~5Qt`}1Ory{n@UB2s=b z%n^lQukSLD85?%K5W+b%L*Q)D<#9&tW)sQVxgpThe`&lM<5Y)3k`#fX<0$yfrT8r} zCtEvceg66+g`Ph?dplq+V~UWU*?7+u<%GVR8|Aspr;<{RmznZ-~jDz-P z($$w0C!P6yq_Oj7F?_uIM#`$prRWG9X74fdi_<;a&n~3<CH+VK1LnjZ3Lqd5@uw`3qayPVqF z%uTwyth6PlyT!gSx{zd4SBwJiKWWklUmSm4$mCJ1|-_-#O$ znVJ*J63bja`EakhqW*z!Wotbjp9-P_V`fEAy)dWpN~cV1TB|Z4AMmwld?YL=lUQws zah2Kff>xk~%#bx3*C1V?$zL*#yO?sWSh#BD7W)BcF2e zaS20{C68wWJzx2GKexjFXHx!8-Ej~j3A^rj2`^5UDQb>zfh9x>ul@zI3Y5 zdeH_X{f%|Ax{B-Oo@4|EHO8dkaEG{Am1uD-d4e#XC9T$8xWv{#KWmfxV&G+S<*-X{ z%3qKjSo(DNF|)Yke|}sW^l>}_fwDC`%)+p8emYR>Op20dCw~L=ON=I{lZ74|UKYX@ zJRFvJzbP_e;`Xg7U(@Of!_PZ~y}K`Gttf`6eq?#wI_Fm@DO!$y_-u^&DOpH;sPBt(%`BItQpdP6srE&t}jG_^%mEAy6{o7*F-mi_a- z$^8j}2NwDj=J_og3g~2CU`>a%AxuX{wz9eYca?MIq~eym>*EcDgp1FOin;LM8Cp5J z>-8WR zh@@8%a0#O4ahBp>teFD6FHUN*{R*>d>xUYOAWF)s(=Hj`>HrM-+Fvsryt0SC2|bze z(L*R?7fzqzJdV&QdZyOL#u>pe}7MdTtP#CKm-r8oSSuB#dG*d zQjJL)8AYUN9a9VH zsn^q!2B`u-TX19QBsb&Gn=h*Wl8{{F1E<{|ck&gkJUJ9dhp{HoT#1Jw9k{BS|4z0f zEmE7AEa;%|$enITY%uGpe^#3`S0Jnqdr@OzPhMJN@Bk~y2z15!2m_XhDuP1h{A%-I z(^W{ zbQ|?T(7~ssvhE>+^~2>E*x8vWx`aA_e2I?v+2MAKVne}ZK=B=h0mlTs;de$x1`#PH zY3|9{uHW87F=V^+Lwxpzo61*;mshvv(gJ5~_m6kchiBhZhh&Zybj$Z2PWE1U^DYW* zURG}^cGDJ@YwB74zlW~(Wc;A#R9zc0HmGRlB%!SQ1oZ*tk$4XH+9l|yNW1#J7*T|j zjeo^3qSYGY2xJ!q0Zo})C@xMSqQ=JCy}csdi@{)f(6BE+s5eW?5}bp>0YM~GWO{nW zIHdU7r@Jt55RY18*jBIY^_3GQJu#I_wXjG)(0^9{{deLG*#qKUJsm9)TZUv=0 zC#jd0KYy0h&Ct=!I96T8#xexQr7;`AQVMi_)kQ{TYX~x3;dN$ac$^*BW@e%Qu}?x> z{ZFzvdT#!V78&eF>oRtS%&1LYNjy!=(s0W_$}fai%6(yUdnF3AqtM1*{m#3u?FH_H zr?Q{%U`17F&czpSl`e9;Q0FVB1BrRAi-NXxy9|$O3>x5W#BgL$QKJ61(ei=}M+OE4 zCMHwA$JD%?o!-$=nO;)|2LU>H6%{lhG<0+vQeaG#m+OxhPIAe;ZB`lVy~$|JxT3$nuGwezq44nbJ<9=(odI198~1Q z7XFFpyAV4)fjOu6V~RvlzP=@uh-%xvL6R&E!R6`RF7D3d8M^mF)ND6p1gJs4&SMr# zzSywys_5U9ANPg$b@I%=`u+~fqVG`(1^75J5vmNfo=NTbLD1}PiQH}foZ#_A;DCWt zrTo0D-FockV(Qem`{00~>dili%^)++ww8B$a5X z46EMA*_|@=Tanh#0cWb2%<7P3%j2g|pz79+Z%qxzt|1H6qmkBvGkn6ks71^(!;Hzg7VY|!`?N!wy_`UZ%pD&mjJtM-AtTmbIuf4BT zC5H^X-`vhV{kP|W7&9zs1hT;i!8Td^rx^Mob)Q26$zm&na;!_8U~MQcQ$t!ADmDHJ zkqcu96SLd z#coVTqV}&OHL*=9<@^VI|hgfxyeBps&;|-c*?+3AkZGP;J0`g(I_|DBdOs z507u|Oc1K@9|W=@1)&lJAqEFEjMY(|et}6f-qNlRG8t3Pem^E#tPmp0Bi5it%%l6H zaMgqg&fx{~*L!oQGV{!sblR*JWipT)dXh~kP#-1hJU+?<#+JU`Va(FN>;HLn-toYc z)#LhiZf#~|czkVY8Ik7%iELpB7)sDzDM**+xd4Sn?hpd%1e4-UP<;ep135wgkIx{7 zkI4WK#8=z-u(c)5k4m!jL*?n=$?K%EqE-63sjrP*6nzYz+J?p$!A^fT)h$iPY*NTV z!C_`Zt9;-oLH20)8WIjaj8O4|uoD=suJen5NWTQ%-WD6z`mm#8MNiMLzM<`C*V5X- zm4}On^=T(qzFW!UT7If;`EF^uV3fO0A`1#I)OXq>GCX8;g#HCzbrcB&Q!=t^>w%_ zlL)w<080HQ3`MG`D^RdnQb~tC?6*RxL2n3W>m7J}>V`gh_190Q_kBN~P#jA<;L(R_g zinGUSzOgB%0QMydbUDSJ#8w5p?pPnNOxs+)7r@(6LV-c49LP$>g7yDu$!YH%Qakm| zHiMD#9CSTUr~as%%|?t|!C^>WW+2|VS4iXVXrq~%BvPx+DvWg$D8T%6_YYLpPPzLD z`b@7ruTDwRsOk8<#N{ZPHQywCyZV*D@-q?D7|*V*5Xjs;|G`i$2nTrNUKw>|`+}2L znF3d|w>n(+`gNCQFwSSzb{NF4RTU3o=hk3mCk=-zY&|I(Ragy{doUP`Je8`4@@=sp z>T7NKL6Q)S%UQXj31F>a)>oi3fjS(qN`><8_aRSb1DQdb65K^j+3}%(=A@_Uo1f@v zGFiW9Y-LAu(#;waxD1)rt#bT34Y(dNQN&0_OC3=b&d4r8z>ct^(22yP);Zun4$5fS zC=D3ky0p%?UI_+mX{Q__(ZbIP9}1vH34r~gh6_6WuP$JwC;+be;T1(fHBMN zx3?IPj^l-)R7L4)YA<5FAtK(Sz+KInU_7x7wBUG2Wc@;_Kna^Xr?$*7cf#7$41Wa; z7YMm08LU@aI)U7w^WE8c9CRRrvMA@Zu)-gNrX>l*;$r%YM z2<9lJ$F6nmU8)c?rG=8y-0)IaVI>G?FafxXCGR4G@mfF1B7_)EI8;+Kk_6m;+h~c* zsaL)dUh%ypx-!v}JRCJy235@cNO?YARuIV#4HXLxU1x{^#OJAxjg5f`-)MuGf@nLj z{_KSXjU5m(;9_GDQ{8nwOG*3ibhY)6s#N;3>*1X=>?0}$P$#3D=`57#d;4#mh9+EM zFjhJ4Ax3$dv1YuMmivaE{>QIg1K(eE1e|ZIS{y&oA9jiZ%9gIa@0w`7f#qzI?~Jql zj_ORlxUU0a{brw~T#8-9=+61M->0VNA-F-0CYlGuW&)8lMmsZ&Yf5ecX~4z7A;3XJ z)5|W{x6!g=Y$03lSL}p+3Qi5&3H0~n4q{3 z&DBUu6@^?;1|((G#c#2P>O)V)h}1h4I+z|ssX|wbIXh;3ppFs8kCs1n?vELbw<)iC z92%NkndWqC3Ny?2JFo{~m9tC@e*_s(Mw#Zq76u@7<3Y&r2GKI2S1=qG>DujaM7){0g}A9}qsSTT zuc6Th3jLfStk7;5cBq5Vj8Mjl=PlKstiZS!C%~Nb`ulP)R_S<1gVFNf+@aI~wQ`4W zC?P;J;AL13Iy!|i&%xjGAntT%DeqNoWRVq`L{u+IG6#Fj9tXG_+MOW2buz8VrRO+4+` zodLTMNYzdSoHt}`F)vgSx3h`TC*ieXYSSiibB58{di>erIyABJ-cns%*%({6ugGk&1#;j6yG5sz zWUle(nnj`dYzqp^Oc4328Js`3^0s<+nywPrK~AAdlZW@ehivB{@3R6)6<2a(olGO8 zXlANrq?#ySD=gyuZz)Y2yJ#<;UZ7m9!!<;Jf3`rU7=aE2DpMF122AR$xIAF2xx|kY z@-4rUW(Hux#@<#oL(am9JEVyuY;&FQ03UmPdb#cUDDdP&BeJ%<3_3tAvTo18#f2k- zOM*j;i3u4TQfpR#Sx~qqE|!q^&`ZdT`n1k;#nnoroZFt)dfm%kdlGa1BoOqdj#$gc z$<{zaZ+@Oe+S(Fmn2~;}^*EvAT$f%@mORnf@T%92C4Wt}1V1o$tGcoKUUR51WYbXb z<@6*W_objVf@W{G%l!3x8(|C*&^vNlzO;i{A1+qCg1+u5opE+>5uOVOxnm$fuY~+C zArYHDt)q4BUn)BhpjUJ>;f1Kn-XUrPG*FTPbWq)hV(1_bV)oJC*BYOVlL5Lu6$Lf;FdDq8>$m4mKzn0#dR%+0 z>HB%(=}Tfr{IG@obdRo{-^pGKlJt)!H`JRj{tIMoe36s=*bT>^paar8^b53@nPvW7 zP=;t=06Au6a{js`dp&($+x-EBH1OC^taE7GL0ge}t#55*@ra<9he4Q8fRetxi33+t zpLQ83nj`7KLf4bHxS^Rg|1Hsvyu3Ru4#yKJvLsvWtU24#Zpbi%F-dgwL<*rNs}z7( zHOH{?ii7z^8j5~IbqrF?zEgdiq)SVcIh7_LQ6w5}P=Oc;Ew8D?GMtL!c|Agy9CF4` zV)ex{TexSPmQ7p@{%a&1N(&R=P9`#A$9)##6;z&uPpk_C(@Mb=L((EwFu=07D*@hQ zXUk(CrUKf03@_*h8T9MFp1F*s_+F4OpF&0)Gl&S03EKy^s;+!ujtZuCvNaUm z=cgwMl>-Wm-%@5Kk3|ekp82wIhf#bx(W`byM7Q0mS|XpAEB*7?+&<-VBXicPHvq<# zNAf#@Zs$P!Mh0|?5!t=Y_R3uB*j*1YZCR+_{%nlFfrG=X+vRbs9&_Z0l;?hT3zCb5 z+rUu~85!AmtD(M*rOBfSl9Hp-$;|oq;B*%engfpp0mTG zt2f`-Dy2C1p2$l4D{Ayyz2@1GE15W;~{qBJx#D4yW}=rEhj z1~)$6!JVDJuBYuyeEhU3{2bAwaukv)Zel@ogt|x|GOtYo=)z;4j~l}O0dGN&zJbW* zNOEz_#Uy?ug^y-RYZ^r%g$*I4?64Y6a5*KsoE=fo7FpevPZsCY5IqADocv-UQmZI! zeQl~m9KQLBPdB%I+N!Oo2bl&E0|=4D2K@u!Bt1Ql>FWS{z>nbIKsa+`)Pc9Re}Ff9 z4i66U^Y!%cj554xdr<%I&Np?cBQ0LMc=5MywhD4H61?n|`&zfQe)|4jKeMS^F0ZXk zrU?#4iK z6r3$n&(x1R@@H!zi||!g)|jgI+1A$2TU-D7uV0Yzd+W!aww`~!I`aJacYk@lxv4@z!bxi*~bnkLq_abbydDs$0k(p%(6C2fB4TCMoY*MDH7b`i^28t-1 z+l^iPxN&$BUh3=mSIC2Fq_Iuh$Qo+k9sxi&^@KQbAE`JUT&Ijbf{*Eq5-dY1k|^au zUd`P*dC_5OQ$r7?M+C&er}N!o9W3h`=dWM2@(WA|jf;&=PIPo~yKZP=YKBa!4K4_w zU6|L{#KOeX^17j^7KHHarAt=;gu1%=w~Q{bYcq^H%zMs!gvb)@+kKDGJu@kyk<>gCn#ilB^9yQNwqA*r&fd|{ zN!eA*sB|2SRQLIxzY%mS+_p!S-t`R&jtGnd7l#)SI9VV%WZ>l8+}ymqJ-5e4x_h`I z6U{g|IeGcIc_QnUc%D3FwDaJBgYW8ytHb`i^^17)UT9cEO}y{obnh1*eKE6kAIlV` zCgpS3O$~IOcVrA2g|AT!1u7@JQ>seJ$mJ}#Cx6l#8xXN#2pxtXx;)#H&h0Kz(EdxdnMVrtX0zS|`-2T$bWH_3`RCrgx*6^iL<`E~=L zV!TW?Q7j#UH->Wgc5|r=sXHxGOcqH-3%C2oKp^#{(vc#`FyIij5b#w&`yhOTZz}}d zQR2bfg?t^8Sd*A4G*iH5@CLh^=N6`0Tlq?b?7zPIC&IrM2nJtr@OMiuLHGwv&yn}+ zt#AJNH>zq3Kp5QAS3R(X>Ol}#-?sw%L*7+8=YAt1cpi3mrIMp&H_HKpccfE=R6#vK zkVsclPu@rOuGDrd)b=eC`c^Cs_*L0h0Tc?XruG-l zkXg#U+xqp_tzUoH`uYCi#MZNaZT;{ks~e}TUAS`jlA(*MI}}_%J8K&|!yBgHg;2(U z0o=5)hpo3yfR*(vJ7mpzXHyd^5VFgcuO2^s^8L5pN{ER?+VFn;FAAo@-sEypM99{! zTmSy~Z#Zn7s!cjKIjb3;D6c28TRKYd>|E&tX$A ztBlb{HDeq2#g9?#130FvMLF2fG1SvPmsds(PpZr)q2!j(%BzU(o`{=*vjSa!BA|eX z2nL3Q1V=4N~u8CvUvQSCh0i2NsgwrSCd58CIzlpMs8KG#Turt8!zc>Xp!I< zuwT~5RAR}JG76tSYb9_sxaRh_JdBM`w3&Ui4~fCnd|#UA(* zoF{+;_CRR&z!qSM3F#4+)LIOval3(o|8h_5x{T1EII&Xfc z^V+ckJ3sL83`okTc5w!ZdwB&SBOoCG1p$Eo_3b)dFApTSZO4Sy zu94V^>K+bIKC(!!Eo*GVHEY}jlXmeHS`Zcqy0jo{AhhH+Da$&hOO;67X1M0w$>_SG zjA9H&RxTebo8B};bvlJ-;qmN3mb6GcQLLCKlOfCl=L4$)5CZccN~H)FfsXKs@EZsL zb=ua!;%)fBN7!m92musC=-8r(Y9HQJAkZ;SxEfYiNn-KY{DGe4>Dj4viAb$h|NFoG z1r&Vo>^Y>DApG6ZOA!7+6Br?Ezy9*CMA=9ju{Df4SThKqTEP!(Kp@;2SOb6|Eq}c$ zV0EBP4dWXnY$cr~&f=(2RrBR+F&JSotD|=MAr>$>u-Y(opFZ+%yR*zPc6hyEbQ9o* z?L#1n%w#t906(&c<}1-GA&Jr=689Dn*cn(_RvE6Ss-Ya;w6yt^M5?-c>4NbMGe<{f zGw?t|TO(t0FhZM~_HM37uLmf^0|P=`T|E5*Lfk!kT;04K>|FqaR+hF}BYc17+jo-V zfBp66Uw;0tnwk<5<7+X|k#Mzt{p#DS+_Dx?&&Ja)TjjEp$Q)fI_03j~%rOR5DE+ILxyR{}{;Wm;P0%eLU2dOPuH*N+Cu7``8huhI z0e2eHiW{B%LhNoif&v2MA|jC0cOiqU@DLOp8yg)P8w=!1x|13a9s?`~O$1(shekz) zL^(J(>YqAx;K%`k6DRlVIsEFo2VUQQ&?h!OpQH+_Y%Xo-;dIY8DTfGR6`QXiFr>sL z1%a<($hxo`5t%C&$wr%nopiQ{$Zo4|l{dDkiF|c+lYl_sQdug%c3ao|JY1`PdRcrP zp{$lpX0?WfMBTEztz{lyLkm1OrEQQPAGIKaZ-MCB!w-<2Cf)(TZeF3+uH4W)c5Lr~ zBR36CRMe4quf0v*VQM;(mnjTk$FT!L~a4<_HAS~I3Mp|a8@{fI9mW7I3f@cGH^Kv zCcS;(p1Znu0TF@g0fE6zu3n~=4j0d!x^7{vr+*~C#o8~b;8mUdCr%!|Xl{P;s_|uG zvm<9No7vyaDJ(BTlXB}C(NtlLpbIti*qOiRn3#vg;p_M+AHk@7I;oDp1PccvbY%4g zq?Y6GJTStluG#8NWH>}r&-&Q}Y-x5GiX$zS_LWR+Tt%sGhh-A+O~ou(S;r*OYg7Tw zH(slrK^g^B)3B#hJ^@?cB2pu&o&_n~rsFUiR<4{V1@qh1LHGz;tq4K@BayA3KUy}i zBbWb*Mbbt8%t``X$ZH+!<<2fIi=;A{T>0aVKWa;Wm+ALRFG2W+O~~+xPd@pqUf7#J z6-5gtP$P)g0kj~1eAJ1@K(G3qWl%ccB6;Fbk`0c7u^xP91 zB77UfsmJVzr-01Hv5m$tWW)vhl$NaIv4^zjr?k0`Dg6^PQ3snZOH9eJyy><_@2t*S zd*9aiQ0L&$H};;pcEirn!tRz0GE0ktgPnt&hozORxrL3JJ5s;_1)Ut-04EOi&W=uQ zo?d?5K7n9_#zx4L+yFuYgVRU#bWKff)#JHYIhB4sfk*Z94UMgCxrSUfa`N#B^YHP% zaNU$dWwr7}6qc|MOl4@DHvcJQ1ZfwfPOMQz?lFc}X#>lop=I9a1J3*>Me4EG8ahYN z*Vf)sQrqYsS#S4e{kktd-3s!p&^lx5wVGh ziC~A}VbO81NzqXWzCOqto^i2v;$!1N146(f4;(wX=iuH`hjsV9`_AjHzV^yn`(EF9 z>fi}0{o^L5FWNf?(TB}%^(y`@u z`LsuRV|g`>!4hHl8aL6jZG3$do>9{%Di-!QvHJqkD$rO~0Y`~6b}Ep`VxoID&L!f& z2*GX(#XV&+_phMUj^SBkVsiyYS=~7U!dI@GE`e*;_6|k3h=34bA$;841NlNRVtYV7 zi2n&fL=d-G2pI@Crb;#$r<(sDyY*G`q)YzkwG;$|Jzczs*#*8(B=1oF^z*MTUc7kr z>=|s~zuL0kW%~a+y#(POG$GHxi>;?mKCNr(No0rtgmuH4b=^pxPaq#?95VZM_YyK? zjAj9%wr9C+c%=~OKiQhjlBajhmoeH2`k^&a|2nRJ6+Lhdj1c?} z2nr~LUxhegw_RYrpMQwsZ8yW~rrI__0O66NC$C;H zx_H4*N9Uba-rv3F)VX&LAAk4o$(`rU->|oh&PdJ25E{8+p{%!EJbV_;rqPgrQ<-|ctYf+o)Nu4kgkU@u z%V=OXGgYGm#e7V;*wm%q@I}{?hPU*OUJdjQ$jmHu^9TXd`FRKV1tDE!w3nkbLcqD3 zn;SB4&?iufNl&-HApbB|cPBR=FH>u`!v+_3@6$cF>%H&*%ZMz@E3co_KY7k6I^Qv- zAtIkvgqCAjN>1k}wzapaNt}gii^aD`R5ezjS!{~1j@wx>v|>je2#moSm0p7IkD8t%(@T8($)8Z|eepC=m|z5q5L^(w z-BSjc?5b}C>HqTIdF;>zYUExXO-iJ5c0)#wJLQ7;77Q0N!l}S2I&<@V{HOjyG)`OiV3pZ#mjn+iRC$ zI)DD+$&;t{ojiH<+Bp-$Gp~R6{+rjWY^xZNP0AQ@ONK~+8(1KZKgA6_X&Agm9-d*S z2IbPR>@rp`uBBvRvvKqhxeJ+U6*Gb~ZPpJW!#~Kg_Y;X+3|-X7Zdv{4$=0vG&dsfU z_06|GKY#Z9zkdyhh+J46+S>XDsKCX9o@2ffs1B$J!Z#tBkoTT{NJ3(8|zukKAYoEGb z)-IY~9->mYQK@;AwOD8K5z3Z~$ zGvZ^*ax>C0QfUnYP47gjY7oP2E9@B0AA6j^Qk7$PIF_iXU4tVum2}U0(p71N4Yf_O z5=jq$uu?OJ(#%z87Aw0J>IRmwG|N7DG;BRhq3pyl#qJ>~S1w(SvcH)W*FU zWi3NoUBz!`B;%T74cyM`$xm*J2c2Sy>+6X)RvU^eEfWvgF}gxBtMQ~30AZPOv`jTq z*EJW_zj-Pa=`~tKZ7mdamriXMV!E6ob0{Qkd9w;ZzG@mk2yUnC0*ZW8&%oON(nn_7 z2!VnK6lI9~l`AJ8U}w2v9LT2?L7FPA~41M(@ zwz6N?))QMs495!z6Caa?9}xPMaQ*Yd@zut$HDsdEg-6BWZY*0$;mPtbjnNVDS1eqe z{E}1isIhmk=dam|Btw7t>|fF0Y1US^0DHhcfLv^BJV+tH5B31|AmU$s|wMdi^;Xwr%u|NFqH+Xe#VvExIb}6-!q_D<)upyWfAGksq}6%dd|<`ywedBg)6CD5>xY$<2ed}) z(!}In{_@4l;+kS=y>fIVZ)i1--Ccna)Kl6?TqTwvt!Y-~46OL#M8#!zG*enA(p2^? zLFo?|DD7OR>Rv<-t_CnQ{uz}xX3OIA^f#Y<*{@K~v{Qb4IJouf`!61R{O!i*)~`QK zO|IRxa{)rT`+B-NyKJ97($mMo-3MMifQZ|Bd|ln$ygUN~eS-YGoqRlQxx2b~dWV}> z*dIMe}4vE}6~JT>)hB8}0ou)N8T^pbe} z)h*(BPH(}`k}132A~L_CtR62=G_ZwrBuS}c$eOJ3zgtEi^Yfb&rHYXP04OUM-Fvaz{Mdy*p+!BtWL^D;QoGh0k%!A8EAO||ALUwM~jv^og1ZwL` z;oA!BG28o*Z?~HawIGDNwy`j?t>3F<5J32vNrH7u0g2zz#+Ns9nG7<%wWX;;CHwbJ zKRn+;>P7!k2)s?Kl!Yg{48yeT41b04aoH2))SCz5qh-NZ=oYcI-ss z@J7SvCUNR<4qshG6)M$ZR4NBharx>cKR*}WTc-9`&imiAy5sL?XLQl{vW4jlI{=}n zu?ZNVshKr^5NKy=Vg+Gz!`#CBCVu6fA7PSJKx%W^yEzo=eVeR6um`)!7-z%Y1!(@=C!Z*QdPTf@NO|Hk}Ah8 zeMBE$=M1k9Mi<#*8^ZC2l&KB+?0pu$kJqdz#xh(2V(dK~Kh!(^+Kv;i@78#JbA;y%sJ|cW29AZ+-0kqH?8bTarI~>4{lvq zV|yJ_-2cmS2S-1|Ttfmv zJ^bPPf#?8uxb2{W+am6Z%&X?>=H(XdYUh=b;Pd6@i|A^Mm8FNd%`LrC27C7%y7u0? zKlY%vw!Uo?4f*&-Wc!)C_{Stby?1bk(vAwQQxs8OTT$UtXHC7-UEs>0ui;=lQ0b|9|u_F061Z3bN(j2%wnqoWt(>q2x zo6PpXh!o}t`yqgZh1)$ybLGRHf}z)wI0y)1^BS8tZ7oU?g;-sbEp2V?Qp^AK@9($Z z(f^MWYRiI`>Hqii5`=%$^a6PpHb4HTvTZ1uEQ@R#sTGTiE1R5>TXiV#q&pELp_AlT6A%4T5?KZK}CHvYGGm&PQa(MF}ZUCzy23m zyGGd39-rS3gzLo2{E0ZVMjTv156+W@mWaKZ#PLtDqxbNNQ6f{p;>$@bO=(3KbBAa% zr*I3$z%!RD&YuTrnR|I4lYGTQN5;lPL`H-}MMMS#ga-N{I0+7l@be7@wD|iWErnsB z(MYci@8H0|NIQFHef=Xx^>j}hJHGeOQJvR5(0OOis|U`$f7-o%~1y0{gA%<0j=YyKmkR{cmw!(T^Vp@S$VB*>9 z)^G1M(0StsY>%BcypvuTAD8Un>;bd`3W7put46g$4uFmjT9CunNVge(|A62qZ;wFV zpa^Hr$g5`!j~_a)XUASU^9!t2d3NEN$)lB24Z4oq|(6?&O}jdgGSU?P?sc z9K*{lrIq5D*cJtjs-ZRsOF5E~k+p*Uja;G_M{UJ4i;?-^hBr!)y1}U`^<0T!s=RZq zymv8AH5r2;RW}IB@|mGgIajV4UA<=L5EvYhlH(Sd)=*cnu&{}5k~=$jAX97y2ZRP7 z<0stQkV;W^WIcLhW;OVTG@Y3S8-{`?c*@A-B! z;V)Y+JInlD=_LsNpy|ak5W=lTpMM4*j3i1TT8A+c8{lpDA!I2DOh3}S1p*le*t?9( zuQsxQ8rlR95~%Iz%#L*R6o3#z;N79AswW@T53JP>uGWvOkp?&5M=HDKu>fr>w{`-$Vzu5ZptH0e_e+Xyw@uSU)XHH(e zcFhAppSO6u6EJc1~(ngK*a(WLcMX%#_IK@e4vS}`mUS**eGI*J;aeG^ihgF@}x z159jgWff)u2$7W~wuec$yL$rvKn;=Jp?<+|Rscm1M8vDMw@BX;FCRZoU;mJBC(p>s z7jEe5>V5d(K_`3Dq>QS!cj)W9{=o-(_21d4w`1pVef_gHZ5&D~>Z{1?YFZnXE~hkg zP@9!xu9V1^)i4A_!>idtkMjvibUg>pRg?;Qt41~|M(!1K&E$1X!8TVtnb$R4FtAWO zyj(vj=A1J8pIEoCV@bYr^K;~|TD}}JIym{Nv&D|^5)Y9SDvGX758tmV*BhuY06Gb)m zia&P##>LB*&KTMrIA?SDR*1Pv$hk|VF0Sqs80_5&R8BRcpo)`U%Ppt1Rxnzt7;S`B zWdl!$R`o^!BYV~4O=OnyS2K78RA5~Y7SpY9Cxsv|s=?B;A zGyuYSB4UJx#IB;X%FSfxV73;tF$ihxi)QK6VBQB5H|Xr$xivI!vNc9rQw1yFOl z%cyp%l|=}^Ax1Il$s2w%f%b}dyh&JLEt4;6=Qgl-jn$Q{-1=^H$4~!$hJX-p!R@um zUZ&qAy#(POHN8MM^x)G^$^`>aBx$s03_H1r^z8(sZ4amio?!>JyN&cAU0nc#=;8ZC zYzcrct4WjIIbBAV*3>uMWob~ePtpCWpnY{itJD!>ydZjTwE-D5u?f*Qum(XMd&r#m zm^t?eM?OeuYOljF@`~zQd}FRyIGZ{8-0}?c4Y^xVQJ&K3t{P6u(vU1XYeEw|x(WA8V zmEiDniCZo)bNnOv(x2PJ8UarlT}X~( z_2L%4qzrB%-D*Z2Ru3=7HYtmVLa}T}&_B=Z*eqy}+zQLR;+%ZhG5w5H%#rKvS8oOa zUBIrO+kK-dH>|T)AnXQnT~f*fw}a!0uHHdir2qN7j$4)Ak%UjSB2-P8k!Xq zJ;_$we?OH6O>^Y*wk`rP6ewP(;5;S zX?M#}D}R7GE$x8Q{{f+I5K^`1;R`s?e&y%q6X4|)7#`{#7<>Mb@j?B=J9g_iI+$kH z(B3_G{C(ZSK?&*c1@)PAbQG1x;t*PwW!oWZs1u9Z}r z7*)@!Kq?ZXwbPIDC+`>atQT~x7HIAjcC8k6ujOb~a(hz#2@%0PJ&CO5CExqaB>Jt#`;UDVc?cwI;2Jj2;4?%R$-3P7$5bomQ z;^yw@;pOS-id6nOySjM$__=!e7@1q2KYC{G0sVuAcEyLe`zIFbys~@e?nB4UTseH& z==f!m6IVKjSy(ppwENnC^BS713PLQ6TGTg?>V+0r_iph~H^Gq`c9yg4ze zj!ti>=XWDB#tqMwNN37bV>OxuZ}s}w@Y?*W!U}48c6)Eh==%9GnP+fLJ&B#imTEy* zET1e?jFu~>0DgtCF@%E3NhBj1M|J`iK@i1=6hfw0u|2W^p!na7g0O|qLJ=~xlBuA! zv7O2ES1gjwdZ(8X=n4@B!)e7;RR~%z-D>5(|Me??aGQWHr&#;_(n}EjQ4_K-{EPLc zPfGZG(PUXv+jz~;y((lxGg22?qnWSmT7WI&D>ZY~-3v(ctroJRjbvdqrz=f8Q%q^E zLa~x)ot5MFt27H0nz@R;MO-(ckF|X(mAy+f1NTq^tF?X0HN8uyfqR&-P2AK&_QVQB zH9+79Gjq$_{UT0WH2UD+kq`Fk>AbmHN9U~*$4=Vca=2k=YGi74)zI|Xb>r)Xrdlb4 z04RbN0`{(5HNJH2?B(+pu3t4YH$`S&Gd8g}cm6Wi)lvOZH?CW`xdjJpEaEYj-N9QKr;z=xymq*;aPp=dM|vy=J`s@R@@Lj@p_Td)+eevNY6c zR%~p1cz8IJxj>O^%?b$!2#Ai3j*E+f4ESPHR8(MKU_wGdQfw@Ia&++o{^{%Mo<4cx z?bmePfBTJN2B$vMJ-zGjg&373=8l_oE6R-0$2iQ^!E>p3JD8~4tMpBK6~MY?vbNAcJA@8yH-#^e`~M7 z8~ab+^iFY(sSM1crsE_HE#pGfI>=~Kn?xY)VMq}QqPfb#;pN1E2gztY4#UOM+N+t0 zOq%!(Rgy|q-JvKG$%=TQ3^K_Kbs|+6PnO@Icce5AB{3v9C>oX9&Xe_$MYI0Je7A(k zz}$w|Ok5?3d^fAg-Z3CJFeJb`2xzTUSnX+hd-)@m;ESv_3yw(6sC#9J!f~W3<*$W!03{ zTB?FT7dKMdFiaV|Kar@db@JgmlaKr`z3G`16fzUf?X76-FCCmJ=$xu-pDB`$R(H<3 zD^^d1ROO`Sm6F=BM19308>frf-2&1vdAmW+wRNkf{urzxQOuG)#a&$;)0Jehp(`aM~B_o}U3C0W}4{VaG_DjEIjU;mDQ z5Y3ml48pIfTn6FSwNya3`sQ0CNB)@Hf5==QrKhRUNnjy7$+A*&DBo3lnjD=(`92ae zwB$4;HQ%fDa7EUNBb+2^{2ZaBK^-R#pQa~gsmW<_W`-R_D5WN6DCrsCABdKkr6i_F z$!Su0hLW9QO}(j|I4iM7)kd$u79w!9JCAp5t?JlQ-FM(b`*SaCyZfF8@45exhaP?E z!G|7u^wBRshzY~tJ$F9{rf~PxttXCDZF_z51NS}n*rO=#=xw*(ef`b1ZP;+vU3WdU zVZ$Bk)_w2W-@odHyB@jk$>;BU_))N)FK^yWqR}-5qtswyt6ef{imMFrjeaIev!hSe zGI>!ldsc7s$u<7tot%^QGJpC_@%V)rx<(+g7+wD9g_WgzF`yIpP5g4km54YuZG3!W z#J~30ubNLDe)5TD;a&_Ntg1R%Q&ZPa-vUGd`vv5ys;WA6>=;%93xSkSA3Jiiskv?A z=55zpyMDv^pI-6(|HbHQnaqX#W@*)~*LJ`9Xi!UW$f@L>F0E2Am*-T%C_+ z3^9~Wj>gT?`#JgmO%r5jeH6Ksp>wl!UX3Sh@x%jxgqY8J^_guC-*xZqP0t+K{MzOx z?jkq8o;1>{Pd03NZ7bFV3Z4n@aGUwlYaO9BUOh` zrzm_oTX%t-g&KSzfUxpC9@t&6=FkXM65`jl?s@o$=dRvx%TG4k@}nR9p#I>FgVls@ z{M%1{bk$7{ZawhAq5jv8Kia~z1dER7klK|M+q_b1L~DyHZ85zqsnUd*;WBPy z<*+6_(63RbY(lGt>&dZm3w`OS0nZ>kI7W<&z+cdo;NlGj$epd-1gWDtP^65WzncP% zu#>Ma4w%Et#3UgyP6$m9WB*(@0Ui;-FNoYBFt0C+1`g3aO$dHyDh)D-KD1Z@_=iyt zn>d51&z*f*pI%oj`{t7;U#p`@EJ3qMDiq1ZJhsaz0TBM}Z~wrOX