From 6b794e1eb3440b211194c28b85db67ca0bcaa141 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Sat, 15 Jul 2017 18:44:49 +0100 Subject: [PATCH 001/287] Add SPI overlap support Issue #26 Note, must use latest ESP board package with support for the overlap mode --- Keywords.txt | 1 + TFT_eSPI.cpp | 16 ++- TFT_eSPI.h | 4 + User_Setup.h | 4 +- User_Setup_Select.h | 9 ++ User_Setups/Setup9_ST7735_Overlap.h | 163 ++++++++++++++++++++++++++++ 6 files changed, 192 insertions(+), 5 deletions(-) create mode 100644 User_Setups/Setup9_ST7735_Overlap.h diff --git a/Keywords.txt b/Keywords.txt index 784ec75..47a42a8 100644 --- a/Keywords.txt +++ b/Keywords.txt @@ -48,6 +48,7 @@ readcommand16 KEYWORD2 readcommand32 KEYWORD2 readPixel KEYWORD2 readRect KEYWORD2 +pushRect KEYWORD2 readRectRGB KEYWORD2 getRotation KEYWORD2 fontsLoaded KEYWORD2 diff --git a/TFT_eSPI.cpp b/TFT_eSPI.cpp index d805ade..0189854 100644 --- a/TFT_eSPI.cpp +++ b/TFT_eSPI.cpp @@ -164,10 +164,17 @@ void TFT_eSPI::init(void) #ifdef TFT_WR wrpinmask = (uint32_t) digitalPinToBitMask(TFT_WR); #endif + + #ifdef TFT_SPI_OVERLAP + // Overlap mode SD0=MISO, SD1=MOSI, CLK=SCLK must use D3 as CS + // pins(int8_t sck, int8_t miso, int8_t mosi, int8_t ss); + //SPI.pins( 6, 7, 8, 0); + SPI.pins(6, 7, 8, 0); + #endif - SPI.begin(); // This will set MISO to input + SPI.begin(); // This will set HMISO to input #else - #ifdef TFT_MOSI + #if defined (TFT_MOSI) && !defined (TFT_SPI_OVERLAP) SPI.begin(TFT_SCLK, TFT_MISO, TFT_MOSI, -1); #else SPI.begin(); @@ -183,8 +190,7 @@ void TFT_eSPI::init(void) SPI.setBitOrder(MSBFIRST); SPI.setDataMode(SPI_MODE0); SPI.setFrequency(SPI_FREQUENCY); - //SPI.setHwCs(1); // Use hardware SS toggling on GPIO15 (D8) - not supported - benefit is only ~0.8% performance boost - + #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 @@ -197,6 +203,8 @@ void TFT_eSPI::init(void) #ifdef TFT_CS digitalWrite(TFT_CS, HIGH); // Chip select high (inactive) pinMode(TFT_CS, OUTPUT); +#else + SPI.setHwCs(1); // Use hardware SS toggling #endif // Set to output once again in case D6 (MISO) is used for DC diff --git a/TFT_eSPI.h b/TFT_eSPI.h index d85df05..25c86b6 100644 --- a/TFT_eSPI.h +++ b/TFT_eSPI.h @@ -86,6 +86,10 @@ #define DC_D GPOS=dcpinmask #endif +#if defined (TFT_SPI_OVERLAP) + #undef TFT_CS +#endif + #ifndef TFT_CS #define CS_L // No macro allocated so it generates no code #define CS_H // No macro allocated so it generates no code diff --git a/User_Setup.h b/User_Setup.h index 8154475..a106fd9 100644 --- a/User_Setup.h +++ b/User_Setup.h @@ -95,11 +95,13 @@ //#define TFT_MISO 19 //#define TFT_MOSI 23 //#define TFT_SCLK 18 -//#define TFT_CS 5 // Chip select control pin +//#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 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) diff --git a/User_Setup_Select.h b/User_Setup_Select.h index e83f73b..3d8c600 100644 --- a/User_Setup_Select.h +++ b/User_Setup_Select.h @@ -26,6 +26,7 @@ //#include // Setup file configured for my modified RPi TFT //#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 template for copying/editting @@ -64,3 +65,11 @@ #define PIN_D8 15 #define PIN_D9 3 #define PIN_D10 1 + +#define PIN_MOSI 8 +#define PIN_MISO 7 +#define PIN_SCLK 6 +#define PIN_HWCS 0 + +#define PIN_D11 9 +#define PIN_D12 10 \ No newline at end of file diff --git a/User_Setups/Setup9_ST7735_Overlap.h b/User_Setups/Setup9_ST7735_Overlap.h new file mode 100644 index 0000000..cfca69a --- /dev/null +++ b/User_Setups/Setup9_ST7735_Overlap.h @@ -0,0 +1,163 @@ +// 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 +// 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 +// +// ################################################################################## + +// Only define one driver, the other ones must be commented out +//#define ILI9341_DRIVER +#define ST7735_DRIVER + +// 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_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 NodeMCU ESP-12 with SPI overlap is : +// +// Display SDO/MISO to NodeMCU SD0 (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 D8 +// Display RESET to NodeMCU pin D4 (or RST, see below) +// Display CS to NodeMCU pin D3 +// 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 NodeMCU D0 pin can be used for RST +// +// See Section 2. below if DC 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 +#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) +//#define TFT_RST -1 // Set TFT_RST to -1 if display RESET is connected to ESP32 board RST + +// ################################################################################## +// +// 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) + +// #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 40000000 // Maximum to use SPIFFS +// #define SPI_FREQUENCY 80000000 + + +// 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 + +// 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 \ No newline at end of file From 8595a434c3416a27718bc021cb899f5541e0745b Mon Sep 17 00:00:00 2001 From: Bodmer Date: Sat, 15 Jul 2017 19:29:13 +0100 Subject: [PATCH 002/287] Raise version number, update ReadMe --- README.md | 8 +++++--- README.txt | 2 +- library.json | 2 +- library.properties | 2 +- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 54051fa..606516d 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,14 @@ # TFT_eSPI -An Arduino IDE compatible graphics and fonts library for ESP8266 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 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 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) +The library supports SPI overlap so the TFT screen can share MOSI, MISO and SCLK pins with the program FLASH. -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 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 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. 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. diff --git a/README.txt b/README.txt index c7211cf..db56bbe 100644 --- a/README.txt +++ b/README.txt @@ -5,7 +5,7 @@ This library has been derived from the Adafruit_GFX and driver library with further code from other authors. It is not compatible with legacy versions of the IDE (e.g. 1.0.6 and -older. Use the latest 1.6.x version. +older. Use the latest version. New functions have been added in particular it contains proportional fonts in addition to the original Adafruit font. diff --git a/library.json b/library.json index 6696ff4..4cfc6a5 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "TFT_eSPI", - "version": "0.16.14", + "version": "0.16.15", "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 6150dea..b35285f 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=TFT_eSPI -version=0.16.14 +version=0.16.15 author=Bodmer maintainer=Bodmer sentence=A fast TFT library for ESP8266 processors and the Arduino IDE From a05db0b2d2489f6f7a21e2ff44952b79683bf16b Mon Sep 17 00:00:00 2001 From: Bodmer Date: Sat, 15 Jul 2017 19:45:42 +0100 Subject: [PATCH 003/287] Correct comment --- User_Setups/Setup9_ST7735_Overlap.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/User_Setups/Setup9_ST7735_Overlap.h b/User_Setups/Setup9_ST7735_Overlap.h index cfca69a..c1fb0c0 100644 --- a/User_Setups/Setup9_ST7735_Overlap.h +++ b/User_Setups/Setup9_ST7735_Overlap.h @@ -53,7 +53,7 @@ // 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 D8 +// Display DC (or AO)to NodeMCU pin D5 // Display RESET to NodeMCU pin D4 (or RST, see below) // Display CS to NodeMCU pin D3 // Display GND to NodeMCU pin GND (0V) @@ -160,4 +160,4 @@ // 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 \ No newline at end of file +#define TFT_SPI_OVERLAP From 15dcc779099c94f46465f439668306badaac3938 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Sat, 15 Jul 2017 23:39:57 +0100 Subject: [PATCH 004/287] Add jpeg examples for 160x128 display --- .../TFT_FLASH_Jpeg/TFT_FLASH_Jpeg.ino | 193 +++++++++++ examples/160 x 128/TFT_FLASH_Jpeg/jpeg1.h | 212 +++++++++++++ examples/160 x 128/TFT_FLASH_Jpeg/jpeg2.h | 300 ++++++++++++++++++ examples/160 x 128/TFT_FLASH_Jpeg/jpeg3.h | 284 +++++++++++++++++ examples/160 x 128/TFT_FLASH_Jpeg/jpeg4.h | 168 ++++++++++ .../TFT_SPIFFS_Jpeg/JPEG_functions.ino | 172 ++++++++++ .../TFT_SPIFFS_Jpeg/SPIFFS_functions.ino | 36 +++ .../TFT_SPIFFS_Jpeg/TFT_SPIFFS_Jpeg.ino | 129 ++++++++ 8 files changed, 1494 insertions(+) create mode 100644 examples/160 x 128/TFT_FLASH_Jpeg/TFT_FLASH_Jpeg.ino create mode 100644 examples/160 x 128/TFT_FLASH_Jpeg/jpeg1.h create mode 100644 examples/160 x 128/TFT_FLASH_Jpeg/jpeg2.h create mode 100644 examples/160 x 128/TFT_FLASH_Jpeg/jpeg3.h create mode 100644 examples/160 x 128/TFT_FLASH_Jpeg/jpeg4.h create mode 100644 examples/160 x 128/TFT_SPIFFS_Jpeg/JPEG_functions.ino create mode 100644 examples/160 x 128/TFT_SPIFFS_Jpeg/SPIFFS_functions.ino create mode 100644 examples/160 x 128/TFT_SPIFFS_Jpeg/TFT_SPIFFS_Jpeg.ino diff --git a/examples/160 x 128/TFT_FLASH_Jpeg/TFT_FLASH_Jpeg.ino b/examples/160 x 128/TFT_FLASH_Jpeg/TFT_FLASH_Jpeg.ino new file mode 100644 index 0000000..7d0f662 --- /dev/null +++ b/examples/160 x 128/TFT_FLASH_Jpeg/TFT_FLASH_Jpeg.ino @@ -0,0 +1,193 @@ +// Sketch to display images on a 160 x 128 TFT + +// Renders images stored in an array in program (FLASH) +// The JPEG images are stored in header files (see jpeg1.h etc) + +// As well as the TFT_eSPI library: +// https://github.com/Bodmer/TFT_eSPI +// the sketch needs the JPEG Decoder library. This can be loaded via the Library Manager. +// or can be downloaded here: +// https://github.com/Bodmer/JPEGDecoder + +//---------------------------------------------------------------------------------------------------- + +#include +#include + +TFT_eSPI tft = TFT_eSPI(); + + +// JPEG decoder library +#include + +// Return the minimum of two values a and b +#define minimum(a,b) (((a) < (b)) ? (a) : (b)) + +// Include the sketch header file that contains the image stored as an array of bytes +// More than one image array could be stored in each header file. +#include "jpeg1.h" +#include "jpeg2.h" +#include "jpeg3.h" +#include "jpeg4.h" + +// Count how many times the image is drawn for test purposes +uint32_t icount = 0; +//---------------------------------------------------------------------------------------------------- + + +//#################################################################################################### +// Setup +//#################################################################################################### +void setup() { + Serial.begin(115200); + tft.begin(); +} + +//#################################################################################################### +// Main loop +//#################################################################################################### +void loop() { + + tft.setRotation(0); // portrait + tft.fillScreen(TFT_BLACK); + + drawArrayJpeg(EagleEye, sizeof(EagleEye), 0, 16); // Draw a jpeg image stored in memory at x,y + delay(2000); + + tft.setRotation(0); // portrait + tft.fillScreen(TFT_BLACK); + drawArrayJpeg(Tiger, sizeof(Tiger), 4, 0); // Draw a jpeg image stored in memory + delay(2000); + + tft.setRotation(1); // landscape + tft.fillScreen(TFT_BLACK); + drawArrayJpeg(Baboon, sizeof(Baboon), 0, 4); // Draw a jpeg image stored in memory + delay(2000); + + tft.setRotation(1); // landscape + tft.fillScreen(TFT_BLACK); + drawArrayJpeg(Mouse160, sizeof(Mouse160), 0, 11); // Draw a jpeg image stored in memory + + delay(2000); +} + +//#################################################################################################### +// Draw a JPEG on the TFT pulled from a program memory array +//#################################################################################################### +void drawArrayJpeg(const uint8_t arrayname[], uint32_t array_size, int xpos, int ypos) { + + int x = xpos; + int y = ypos; + + JpegDec.decodeArray(arrayname, array_size); + + jpegInfo(); // Print information from the JPEG file (could comment this line out) + + renderJPEG(x, y); + + Serial.println("#########################"); +} + +//#################################################################################################### +// 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 renderJPEG(int xpos, int ypos) { + + // retrieve infomration about the image + 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; + + // 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 = minimum(mcu_w, max_x % mcu_w); + uint32_t min_h = minimum(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; + + // read each MCU block until there are no more + while (JpegDec.readSwappedBytes()) { + + // save a pointer to the image block + pImg = JpegDec.pImage ; + + // calculate where the image block should be drawn on the screen + 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 + if (mcu_x + mcu_w <= max_x) win_w = mcu_w; + else win_w = min_w; + if (mcu_y + mcu_h <= max_y) win_h = mcu_h; + else win_h = min_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.pushRect(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 + } + + // calculate how long it took to draw the image + drawTime = millis() - drawTime; + + // print the results to the serial port + Serial.print(F( "Total render time was : ")); Serial.print(drawTime); Serial.println(F(" ms")); + Serial.println(F("")); +} + +//#################################################################################################### +// Print image information to the serial port (optional) +//#################################################################################################### +void jpegInfo() { + Serial.println(F("===============")); + Serial.println(F("JPEG image info")); + Serial.println(F("===============")); + Serial.print(F( "Width :")); Serial.println(JpegDec.width); + Serial.print(F( "Height :")); Serial.println(JpegDec.height); + Serial.print(F( "Components :")); Serial.println(JpegDec.comps); + Serial.print(F( "MCU / row :")); Serial.println(JpegDec.MCUSPerRow); + Serial.print(F( "MCU / col :")); Serial.println(JpegDec.MCUSPerCol); + Serial.print(F( "Scan type :")); Serial.println(JpegDec.scanType); + Serial.print(F( "MCU width :")); Serial.println(JpegDec.MCUWidth); + Serial.print(F( "MCU height :")); Serial.println(JpegDec.MCUHeight); + Serial.println(F("===============")); +} + +//#################################################################################################### +// 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 ")); +} + diff --git a/examples/160 x 128/TFT_FLASH_Jpeg/jpeg1.h b/examples/160 x 128/TFT_FLASH_Jpeg/jpeg1.h new file mode 100644 index 0000000..66920f9 --- /dev/null +++ b/examples/160 x 128/TFT_FLASH_Jpeg/jpeg1.h @@ -0,0 +1,212 @@ +// We need this header file to use FLASH as storage with PROGMEM directive +#include + +const uint8_t EagleEye[] PROGMEM = { +0xFF,0xD8,0xFF,0xE0,0x00,0x10,0x4A,0x46,0x49,0x46,0x00,0x01,0x01,0x01,0x00,0xB4,0x00,0xB4,0x00,0x00,0xFF,0xDB,0x00,0x43,0x00,0x04,0x03,0x03,0x03,0x03,0x02,0x04, +0x03,0x03,0x03,0x04,0x04,0x04,0x05,0x06,0x0A,0x06,0x06,0x05,0x05,0x06,0x0C,0x08,0x09,0x07,0x0A,0x0E,0x0C,0x0F,0x0E,0x0E,0x0C,0x0D,0x0D,0x0F,0x11,0x16,0x13,0x0F, +0x10,0x15,0x11,0x0D,0x0D,0x13,0x1A,0x13,0x15,0x17,0x18,0x19,0x19,0x19,0x0F,0x12,0x1B,0x1D,0x1B,0x18,0x1D,0x16,0x18,0x19,0x18,0xFF,0xDB,0x00,0x43,0x01,0x04,0x04, +0x04,0x06,0x05,0x06,0x0B,0x06,0x06,0x0B,0x18,0x10,0x0D,0x10,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, +0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0xFF,0xC0, +0x00,0x11,0x08,0x00,0x80,0x00,0x80,0x03,0x01,0x22,0x00,0x02,0x11,0x01,0x03,0x11,0x01,0xFF,0xC4,0x00,0x1D,0x00,0x00,0x03,0x01,0x00,0x03,0x01,0x01,0x01,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x06,0x07,0x04,0x02,0x03,0x08,0x01,0x09,0x00,0xFF,0xC4,0x00,0x3F,0x10,0x00,0x02,0x01,0x02,0x04,0x03,0x06,0x05,0x01,0x05,0x07, +0x03,0x05,0x00,0x00,0x00,0x01,0x02,0x03,0x04,0x11,0x00,0x05,0x12,0x21,0x06,0x31,0x41,0x13,0x14,0x22,0x51,0x61,0x71,0x07,0x32,0x81,0x91,0xA1,0xB1,0x23,0x42,0xC1, +0xD1,0xF0,0x15,0x24,0x25,0x33,0x52,0x62,0x72,0x08,0x34,0x92,0x16,0x43,0x53,0xA2,0xE1,0xFF,0xC4,0x00,0x1A,0x01,0x00,0x02,0x03,0x01,0x01,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x03,0x00,0x01,0x04,0x05,0x06,0xFF,0xC4,0x00,0x28,0x11,0x00,0x02,0x02,0x01,0x04,0x01,0x04,0x01,0x05,0x01,0x00,0x00,0x00,0x00, +0x00,0x00,0x01,0x02,0x00,0x11,0x03,0x04,0x12,0x21,0x31,0x13,0x05,0x22,0x32,0x41,0x51,0x14,0x15,0x61,0x71,0x81,0x91,0xFF,0xDA,0x00,0x0C,0x03,0x01,0x00,0x02,0x11, +0x03,0x11,0x00,0x3F,0x00,0x6C,0x4C,0xFE,0x4C,0xAB,0x2B,0x4A,0x65,0xB5,0x2B,0x01,0x74,0x58,0x90,0x24,0x30,0x13,0x6D,0xEC,0x3F,0x7B,0xD7,0x9E,0x34,0x53,0xE7,0x51, +0x8D,0x15,0x11,0x42,0xD3,0x4A,0xA7,0x50,0x91,0x9F,0x48,0x66,0xF3,0x37,0xDC,0x9F,0xA7,0xDB,0x13,0xFC,0xE3,0x3D,0xA0,0xAC,0xCC,0x84,0x39,0x74,0x46,0x34,0x43,0x76, +0x9A,0x49,0x0B,0x28,0x3E,0xE4,0x0B,0xFD,0x00,0xF7,0x38,0x6B,0xE1,0x9C,0xF3,0x86,0x29,0x20,0x66,0xAB,0xA4,0x7A,0xD9,0x2C,0x2F,0x34,0xB2,0x68,0x43,0x6F,0xF7,0x58, +0x9B,0x7A,0x2E,0x38,0xAE,0x8C,0x00,0x27,0xB9,0xE8,0x43,0x83,0xF1,0x8C,0x73,0x71,0xB5,0x5E,0x59,0x46,0x8F,0x57,0x2B,0xB9,0x67,0x2B,0xD8,0xC7,0xE1,0xD4,0x4E,0xFC, +0xB9,0x91,0xEA,0xD8,0x16,0x2B,0xF3,0xEE,0x28,0xAA,0x4E,0xF3,0x53,0xDD,0x29,0x57,0x60,0x01,0x0A,0xAA,0x3F,0x4C,0x0B,0xAA,0xCD,0x78,0x4E,0x59,0x5E,0x70,0x89,0x08, +0x0D,0x71,0x69,0x1D,0x8F,0x3E,0x40,0xB3,0x1D,0xBD,0x80,0xC6,0xCA,0x0C,0xDE,0x3A,0x97,0x58,0xF2,0xAC,0xBA,0x5A,0x89,0x01,0xBA,0xEA,0x21,0x45,0xFA,0x1F,0x15,0xB0, +0x46,0xEB,0x88,0x3C,0x5D,0xCA,0x77,0x08,0xC1,0x96,0xE5,0x71,0xDB,0x2D,0x53,0x99,0x56,0x1E,0x73,0xB8,0x2B,0x1A,0x1E,0x5F,0x31,0xDD,0xBA,0xF2,0xBF,0x2E,0x78,0x25, +0x9B,0xE7,0xD9,0x95,0x04,0xD2,0x45,0x51,0x53,0x1B,0x80,0x36,0x85,0x06,0x94,0x3E,0x44,0x8F,0xE7,0x7C,0x22,0xC3,0x98,0x71,0x7D,0x2C,0x5A,0xCF,0x78,0x90,0x91,0xA7, +0xB1,0xA0,0x84,0x39,0xFF,0x00,0xC8,0x9E,0x9E,0x98,0x5C,0xAE,0xE2,0x5A,0xE3,0x9C,0x32,0x54,0x43,0x1D,0x35,0x41,0xF9,0x9A,0xA9,0x8C,0x92,0xDB,0xD5,0x4D,0xBF,0x8E, +0x23,0x29,0x65,0xAB,0x90,0x28,0xBB,0x8F,0xA2,0xBA,0xA3,0x36,0x74,0x7A,0xFE,0xED,0x28,0x53,0xE0,0x1F,0x30,0x51,0xE9,0x7B,0x5B,0xEF,0x86,0x38,0x33,0x7C,0xAF,0x26, +0xA4,0x12,0x54,0xB8,0x43,0x7D,0xA3,0x45,0x63,0xF8,0x17,0xFD,0x31,0x32,0xCB,0x12,0x7A,0xA9,0xE3,0xED,0xE6,0xCD,0x24,0x57,0x5D,0x43,0xB0,0x8C,0x42,0xBF,0xC3,0x0D, +0xB1,0x65,0x19,0x3C,0x22,0x36,0x58,0xC0,0x95,0xB9,0x34,0xB5,0x57,0x72,0x3D,0xB4,0x9B,0xF3,0xF3,0xC6,0x73,0x8C,0x77,0x77,0x1F,0xB8,0x9E,0x2A,0x30,0xBF,0x1B,0x65, +0x95,0xF2,0x68,0x8B,0x2E,0xA9,0xA9,0x07,0xE5,0x09,0x12,0xED,0xEA,0x4B,0x72,0xFB,0x1C,0x74,0xD7,0xAA,0x66,0x8C,0xAD,0x0F,0x0E,0x18,0x85,0xB7,0x33,0x4C,0x01,0xF5, +0xE5,0xB7,0xF5,0xB6,0x08,0x65,0xD4,0x70,0x50,0x46,0xB2,0x99,0x69,0x5A,0xE2,0xDD,0x98,0x4B,0x11,0xEB,0x72,0x6D,0xF8,0xC0,0x3E,0x2E,0xCF,0xF8,0x96,0x29,0x0C,0x79, +0x5E,0x4F,0x2C,0xF1,0xA7,0x36,0xA7,0xF1,0x93,0xCB,0xA5,0xBF,0x43,0xE7,0x82,0xC6,0x87,0x20,0xA6,0x13,0x3E,0x43,0xB5,0xB8,0x87,0x68,0xF8,0x46,0x86,0x37,0xED,0x99, +0xE1,0xA6,0x52,0x05,0x83,0x48,0xD2,0x8F,0xD4,0x5B,0x04,0x64,0xAF,0xCA,0x28,0xA1,0xEC,0x19,0xA9,0xE7,0xD3,0x71,0xFB,0x32,0x14,0x7B,0xD9,0xAE,0x3F,0x18,0x90,0xD3, +0xF1,0x07,0x12,0x55,0x32,0xC3,0x36,0x4B,0x99,0x87,0xE4,0x5A,0x65,0x65,0x00,0x7E,0x76,0xC1,0x24,0xCB,0xEB,0xEA,0xC7,0x69,0xA2,0x9A,0x26,0x03,0x70,0xDA,0x98,0xAE, +0xDB,0x78,0x74,0x8C,0x3B,0xC7,0x8D,0x20,0x16,0x66,0xEC,0xC7,0xF7,0xCC,0x32,0xAA,0x98,0x13,0xB2,0x9F,0xBB,0x9B,0xEC,0x4C,0xAB,0x6F,0xB0,0x00,0x63,0x1B,0x45,0x54, +0x6A,0xC2,0xD1,0x66,0x54,0x73,0x5B,0x61,0x78,0xF5,0x7E,0x16,0xC4,0xE1,0x6B,0x2E,0xA7,0xAA,0xE4,0x66,0x85,0x82,0x90,0x35,0x0A,0x57,0x52,0x3E,0xAC,0x45,0xF1,0x40, +0xE1,0xBC,0xB2,0xA9,0xA4,0xED,0x66,0xAB,0x6E,0x7F,0x2C,0x8D,0x1A,0xED,0xE9,0xCC,0x81,0x84,0xDA,0xB9,0xDA,0x3B,0x8C,0x2A,0x71,0x8B,0x33,0xEE,0x55,0x3F,0x11,0x51, +0x80,0xC6,0x92,0x9A,0x7B,0xF5,0x55,0x74,0xFB,0x82,0x76,0xC3,0x1B,0x4F,0x57,0x55,0x02,0x3E,0x61,0x45,0xDD,0x5C,0x7F,0xF1,0xB9,0x03,0xF4,0xC1,0x25,0x80,0x0A,0x62, +0xAC,0xE2,0xE3,0x90,0x49,0x2E,0x48,0xFC,0x60,0x64,0xF4,0xF4,0x82,0x76,0x92,0xA6,0xB0,0xA5,0x8E,0xA1,0xAD,0x98,0xFE,0x3F,0x96,0x36,0x10,0x55,0x6A,0x63,0x2E,0x1D, +0xAE,0x7E,0x7D,0x51,0xD3,0xB4,0xD6,0x73,0x3A,0xA2,0xAA,0x8B,0xEA,0x60,0xA1,0x47,0x4F,0x7C,0x74,0xD5,0xF6,0xB3,0x59,0x1E,0xAE,0x48,0x69,0xD7,0x90,0x06,0xDA,0xCD, +0xF9,0x91,0x84,0x61,0x98,0x66,0x51,0x47,0xFE,0x61,0x13,0x11,0xB1,0x3F,0x2C,0x6B,0xE6,0x3D,0x7D,0x71,0xB3,0x2D,0xCC,0xEA,0xAA,0x67,0x58,0xA9,0xEA,0x43,0x48,0xA7, +0x79,0x0B,0x02,0x47,0xFC,0x6F,0xD7,0x0C,0x08,0x3B,0x26,0x5A,0xBD,0xFB,0x40,0x8D,0x75,0x93,0xCB,0x96,0x4B,0x1B,0xB4,0x33,0x3A,0xBF,0xCA,0xEE,0xA4,0x29,0xFA,0x9C, +0x14,0xCB,0xF8,0xD4,0xE5,0xCD,0x18,0x92,0x46,0x0D,0x20,0xF9,0x23,0x3A,0x40,0xFE,0x38,0x51,0xAF,0x6C,0xC2,0xB2,0xAF,0xFC,0x63,0x32,0x96,0x8E,0x25,0x5F,0xD9,0x40, +0xCD,0xDA,0xC8,0xC7,0xD8,0x72,0xBE,0x30,0x35,0x05,0x5C,0x68,0xD3,0x35,0x35,0x5C,0x3B,0x17,0x43,0xD9,0x30,0x24,0x0F,0x32,0x05,0xC7,0xBE,0x2F,0xD8,0xDC,0x42,0xDC, +0x54,0xCB,0x1D,0x07,0x19,0x33,0x09,0x0A,0xD3,0x42,0x74,0x8D,0x5A,0xAA,0x5D,0xA4,0x22,0xDB,0xEC,0x09,0xFE,0xAF,0x8E,0xEA,0x4E,0x31,0xCB,0x2B,0xA6,0x96,0xB3,0x30, +0xA6,0x7A,0x7D,0x3E,0x18,0xE4,0x89,0x82,0xDC,0xF2,0x1E,0x1D,0x0D,0xF6,0xBE,0x22,0x19,0x6D,0x5D,0x29,0xAB,0x59,0x59,0x65,0xED,0x6E,0x3C,0x08,0xAD,0xEF,0xBD,0xAE, +0x6F,0xF7,0xC3,0x6D,0x33,0xD0,0xD4,0x65,0x8D,0x98,0x4B,0x9B,0xA2,0x44,0xAD,0x74,0x11,0xC7,0xA9,0xD1,0xB9,0xE9,0x3A,0xD6,0xE0,0xFA,0x83,0x84,0x38,0x17,0x40,0x43, +0x5C,0x97,0xF7,0x1E,0x33,0x5F,0x88,0xD9,0x9E,0x52,0xE2,0x28,0x29,0xE6,0x70,0xC4,0x30,0xED,0xA5,0xB1,0xD2,0x7F,0xDA,0x1B,0x9F,0xD0,0x60,0x52,0xF1,0xF5,0x45,0x4D, +0x72,0xB4,0x94,0x72,0x76,0x8D,0xF3,0x33,0x35,0xC1,0xFF,0x00,0xEB,0x8E,0xBC,0xA3,0x84,0x32,0x1C,0xFF,0x00,0x34,0x12,0xE6,0x39,0xAC,0xBA,0x19,0x75,0x42,0xF3,0x96, +0x0D,0x25,0xC1,0xF0,0x8E,0x45,0x8D,0xF6,0xD9,0x85,0xB0,0x53,0x87,0xFE,0x12,0x66,0xF2,0xD0,0x87,0xAF,0xA8,0x31,0x33,0xC8,0x58,0xA3,0xF6,0xEA,0xF1,0xA0,0xF9,0x5E, +0xF7,0xD3,0xE2,0x07,0xCC,0xE2,0x0C,0xB8,0xD5,0x68,0xC0,0x39,0x0D,0xF7,0x0B,0x1E,0x3B,0xA3,0xA4,0xA6,0x88,0xD4,0x45,0x40,0x8D,0x6B,0x18,0x96,0x7B,0xCB,0x6F,0x3D, +0x27,0x97,0xDB,0x02,0xF8,0x83,0x8E,0xA4,0xAE,0x30,0xC2,0xCE,0xBD,0xD9,0xFC,0x49,0xFD,0xDB,0x5E,0xA1,0xD0,0x8F,0x5E,0x9B,0x61,0xB3,0x3A,0xF8,0x6D,0xC3,0x51,0xF0, +0xAF,0x7C,0xCF,0xA5,0xA7,0xA4,0x68,0x63,0x11,0xB1,0x39,0x99,0x9C,0x97,0x0D,0xE2,0x6B,0x6C,0xA3,0x62,0x06,0xDB,0x6D,0x89,0xFD,0x63,0x7C,0x2B,0xA3,0xA2,0x96,0x0A, +0x4C,0xF5,0x83,0xB4,0x80,0x04,0x17,0x7F,0x08,0xBE,0xE0,0xA9,0x21,0x6D,0xB7,0x53,0x7C,0x2C,0xE7,0x50,0x7E,0x26,0x4D,0xD7,0x0B,0xE5,0xFC,0x44,0xD0,0xD2,0xAD,0x5D, +0x3C,0xB7,0x2A,0x6C,0xC8,0xF9,0x7C,0x8D,0xA4,0x79,0x9E,0x60,0x72,0xC1,0x78,0x3E,0x24,0x66,0x46,0x99,0x52,0x9A,0x90,0x37,0x8F,0x79,0x2C,0xD1,0xDF,0xA5,0xAE,0x0F, +0xF0,0xC2,0x36,0x7B,0x59,0x92,0x54,0xE4,0xD0,0x8E,0x1C,0xCE,0x24,0x16,0x90,0xDA,0x15,0x0F,0x21,0x03,0x50,0x08,0xB7,0x64,0xE7,0xB9,0xE5,0xCF,0x6C,0x64,0xA6,0xE2, +0x3E,0x23,0xC8,0x62,0x9E,0x91,0xE7,0xCC,0xDD,0x67,0x43,0x1C,0xF1,0xC9,0x01,0x4D,0x26,0xD7,0x17,0x0E,0x01,0x3B,0x5A,0xDC,0xBD,0x0E,0x2D,0x33,0x06,0xEC,0x57,0xF7, +0x08,0x19,0x6B,0xE1,0xDF,0x89,0x52,0x41,0x28,0x6A,0xBA,0x07,0x7B,0x9B,0xB3,0x77,0x99,0x0A,0xFD,0x80,0xC5,0x4B,0x27,0xF8,0x90,0xAE,0x8B,0x28,0xA7,0xAC,0x89,0x58, +0xD8,0x2B,0x90,0x76,0xF3,0x1A,0x80,0xDB,0x1E,0x38,0xA3,0xE3,0xE9,0xA9,0xEA,0xDE,0x9E,0xAB,0x30,0x92,0x32,0x48,0x67,0x8E,0xA0,0x58,0xF2,0xD8,0x5A,0xDF,0xCF,0x0F, +0xDC,0x3F,0xC6,0x94,0x91,0xCC,0xB3,0xC9,0x11,0x64,0xD8,0x93,0x4E,0x74,0x10,0x3A,0x9D,0x8D,0xFF,0x00,0xAF,0xBD,0x32,0x6D,0xF7,0x28,0x8D,0x52,0x1B,0xE5,0x3D,0x79, +0x45,0xC4,0xCF,0x57,0x0A,0x34,0xBD,0xA6,0x96,0xB7,0xCC,0xA8,0x30,0x41,0xA2,0xEF,0xA8,0xAD,0x0D,0x9D,0x41,0xB9,0x54,0x09,0xAA,0xFE,0x63,0x7C,0x79,0xEB,0x23,0xE3, +0x5C,0xBE,0x4A,0xB5,0xEE,0x55,0xE5,0x37,0x04,0xAF,0x6A,0x41,0x07,0x6B,0x8D,0xD4,0x79,0xF3,0xC3,0xC5,0x2F,0x14,0xF1,0x0C,0x35,0x62,0x65,0x68,0x2A,0x69,0x24,0x6B, +0x45,0x22,0x11,0xA8,0xFF,0x00,0xCB,0xC5,0xB7,0x5E,0x57,0xC2,0xEC,0xB7,0xCA,0x51,0xC4,0x3B,0x49,0xF9,0xED,0x3E,0x70,0xD9,0x92,0xA5,0x2C,0xBA,0xBB,0x15,0x16,0x62, +0x16,0xC5,0x8F,0x3F,0x61,0xF9,0xC6,0xAC,0x86,0xA7,0x23,0x6A,0xBE,0xC2,0xBA,0x29,0x52,0x21,0x20,0x01,0x52,0x75,0x82,0xEB,0xFE,0xE6,0x20,0xD8,0x7B,0x60,0xD4,0x7C, +0x1B,0x05,0x54,0x81,0x23,0x65,0x65,0x06,0xC8,0x81,0x77,0xBF,0x9B,0x1E,0x40,0x13,0x7E,0xBC,0x94,0xE3,0x54,0xDF,0x0E,0x65,0x34,0xAE,0x91,0xEA,0xD6,0x00,0xDA,0x35, +0xDD,0xCF,0x41,0xA8,0xF3,0x3B,0xF2,0xFE,0x44,0xE3,0x5F,0x95,0x07,0x13,0x10,0xF2,0x5D,0xCE,0x72,0xF1,0x05,0x3A,0x67,0xE8,0x94,0xEB,0x96,0x42,0xB4,0xC8,0x4D,0x3A, +0x51,0xCA,0xAA,0xC6,0xE3,0xAB,0xE9,0x21,0x8F,0x2F,0x5F,0x2C,0x2F,0x56,0x71,0x44,0x94,0xAE,0xD3,0x0E,0xCE,0xAD,0xA4,0x3E,0x28,0x1A,0x47,0x5E,0xCC,0xF5,0xBD,0xAD, +0xCF,0xD3,0x03,0x6B,0x78,0x52,0xA2,0x87,0x3D,0x8B,0x2E,0x94,0x3A,0x55,0x1D,0xCA,0x75,0xB9,0x36,0x03,0xFA,0xF2,0xC7,0xDA,0x8E,0x1C,0xAA,0xA7,0x6E,0xCE,0x4A,0x49, +0xA5,0x62,0xDA,0x2E,0xA2,0xE0,0x7B,0x7A,0xEC,0x70,0x40,0xE3,0xAE,0x24,0x3B,0xCC,0x21,0x9A,0x7C,0x42,0x97,0x30,0xA2,0x8A,0x08,0xF2,0xB4,0x85,0xE3,0x42,0x9A,0xE3, +0x95,0xEC,0x56,0xE3,0xC2,0x7D,0x2C,0x2C,0x05,0xFE,0xF8,0x15,0xFF,0x00,0xAB,0xB3,0xB9,0x68,0xFB,0x80,0x82,0x9C,0xD3,0x10,0x08,0x8B,0xB2,0xBE,0x9B,0x0B,0x6D,0xD7, +0x05,0xE9,0x38,0x58,0xC3,0x34,0x5D,0xF2,0x07,0x90,0x3E,0xA3,0xDD,0xB5,0x69,0xD1,0xE1,0xD8,0x9D,0xB9,0x83,0x63,0x6F,0xA6,0x0F,0x49,0x43,0x94,0xF0,0xCD,0x1D,0x25, +0x7C,0xD1,0xC1,0x52,0xEC,0xCC,0x86,0x89,0xD7,0xC6,0xC2,0xC7,0xC6,0xDF,0xE9,0x1B,0xFD,0xC0,0xB6,0x33,0xE6,0xD4,0xE3,0x43,0xB0,0x0B,0x63,0x2D,0x70,0xB1,0x16,0x62, +0xF5,0x1E,0x69,0xC6,0x89,0x45,0x04,0x89,0x53,0xDC,0xA9,0x20,0xBA,0xAC,0x85,0x40,0x45,0xBD,0xCD,0x8D,0xF9,0xF5,0xDB,0xD4,0xE3,0x7C,0x5F,0x15,0x38,0xBF,0x2B,0x83, +0xB1,0xA7,0xE3,0x0C,0xDC,0xFE,0xD0,0x3B,0xA5,0x34,0xA6,0x34,0x20,0x0B,0x0D,0xC1,0x1B,0x5B,0x60,0x2D,0x6C,0x2F,0xE6,0x95,0x99,0x8E,0x65,0x53,0x23,0x54,0x3B,0x08, +0x8D,0x99,0x63,0x4F,0x0A,0x2E,0xDB,0x58,0x60,0x34,0xB4,0xF5,0x0B,0x24,0x61,0xE0,0x91,0x9E,0x4E,0x5B,0x6C,0x70,0x78,0x6D,0xBE,0x47,0xFE,0x4A,0x6D,0xA0,0xD0,0x11, +0xC3,0x31,0xF8,0x85,0x26,0x7B,0x57,0x5B,0x98,0x67,0x09,0x2E,0x61,0x23,0x69,0x58,0xCD,0x75,0x4B,0xBB,0xA7,0x4D,0xBC,0xEC,0x07,0xE4,0x63,0x66,0x53,0xC7,0x19,0x04, +0x15,0x90,0xCF,0x2E,0x40,0x22,0x2A,0x82,0x30,0xD1,0xA8,0x22,0xDA,0xAF,0xA8,0xFF,0x00,0xA8,0xDB,0x6C,0x29,0x47,0x96,0x39,0x81,0x5E,0x4A,0x29,0x2E,0x08,0x36,0x55, +0xBE,0xDE,0xB8,0x68,0xCA,0x78,0x7F,0xBF,0x00,0x05,0x2B,0x20,0x41,0x73,0x6B,0x5F,0x7C,0x2B,0x2A,0xE1,0xA3,0x66,0xBF,0xD9,0x6A,0xEC,0x7E,0xA3,0xFE,0x51,0xC5,0x9C, +0x15,0x57,0x9B,0xCB,0x3C,0x35,0x42,0x3B,0x21,0x65,0x86,0xA5,0x01,0xD6,0xED,0xD0,0x93,0xBD,0xAE,0x39,0xF3,0x00,0xFD,0x70,0xF8,0xB5,0x14,0xD5,0x73,0x45,0x4D,0x31, +0x8E,0xA6,0x1A,0xB5,0xBA,0x4A,0x8C,0x16,0xD2,0x03,0x76,0xD8,0xFE,0xF0,0x06,0xD7,0xE4,0x6F,0xCF,0x12,0x84,0xF8,0x6B,0x0D,0x7E,0x46,0x64,0x89,0x94,0x55,0xA8,0xED, +0x07,0x69,0xE1,0xE9,0xC8,0x1F,0x3E,0x7D,0x77,0xBE,0x30,0x49,0xC4,0x12,0x77,0xEA,0x2C,0x9A,0xB2,0x8A,0x3C,0xAA,0xA2,0x8A,0x11,0x02,0x4F,0x48,0x8D,0x79,0x8F,0xFA, +0xA4,0x04,0x95,0x24,0x8F,0x20,0x09,0xF3,0xC0,0x63,0xC0,0x1C,0x56,0x33,0x7F,0xDC,0x22,0xF5,0xF2,0xE2,0x51,0x73,0x0E,0x07,0xCB,0x33,0xAA,0x9B,0x3D,0x22,0x39,0x3A, +0x9D,0x2A,0x15,0x4B,0x12,0x3C,0x88,0xD8,0x9E,0x77,0xB0,0xF2,0xB5,0xB1,0x3A,0xE2,0x2C,0x8B,0x33,0xE1,0xBC,0xDB,0xBA,0x52,0x4F,0xDB,0x22,0x3E,0xAE,0xCA,0x32,0x43, +0x80,0x3A,0x8E,0xA0,0x60,0xEF,0x0E,0x7C,0x4B,0x8A,0x8F,0x88,0x57,0x27,0xCC,0x2A,0x7B,0x78,0x89,0x55,0xEF,0x40,0x59,0x5C,0x5B,0x90,0x07,0xE5,0xDC,0xEF,0xFF,0x00, +0xEE,0x2B,0x39,0x9F,0x09,0xE5,0x3C,0x57,0x90,0xD2,0xE6,0x80,0xB3,0xA8,0x1E,0x3B,0xD8,0x9D,0x82,0x8B,0x5C,0x1B,0x81,0xB8,0xE5,0xBF,0xA1,0xC5,0x2E,0x53,0x89,0xB6, +0x34,0x7E,0xC2,0x46,0xE1,0xCC,0x85,0xE4,0x7C,0x4D,0x9E,0x51,0xA0,0x44,0xA9,0x66,0x76,0xBD,0x8C,0xBE,0x19,0x06,0xE3,0x91,0x3F,0x5E,0x58,0xA3,0x70,0xF7,0x12,0x55, +0x55,0x65,0xEA,0xF0,0xD6,0x97,0x2B,0xE2,0x54,0x9A,0xEC,0x09,0xB5,0x8A,0xDF,0x9A,0x9E,0x83,0x6F,0x2C,0x04,0xAE,0xE0,0x81,0x4D,0x9A,0x3A,0xC3,0x0C,0xDA,0x54,0xD9, +0x22,0xAA,0x6B,0xA8,0x17,0xBF,0x86,0x51,0xB5,0xEF,0xEC,0x7D,0xF0,0x22,0x6A,0x15,0xA0,0x83,0x55,0x33,0x3E,0xAD,0x64,0xDD,0x0F,0x8E,0x3E,0x5B,0x6F,0xD3,0x0E,0x39, +0x15,0x87,0x12,0xD7,0xDA,0x79,0x94,0x8A,0x5E,0x19,0x96,0x8E,0x7E,0xDD,0xE0,0xED,0x5A,0xA1,0x89,0xD2,0xAB,0x7B,0xE9,0xB8,0xD2,0xDE,0xB7,0x00,0xDB,0x90,0xD5,0xE7, +0xC8,0xCE,0x4F,0x92,0x35,0x5C,0xD1,0xCF,0x1A,0x1E,0xEF,0x1C,0xA1,0x74,0x84,0xB9,0xD4,0x4D,0xF4,0xAF,0xBD,0x94,0x7B,0x73,0xD8,0x93,0x87,0x3E,0x1C,0x92,0x2C,0xDD, +0x33,0x1A,0x69,0xA3,0x92,0x9D,0x0C,0x77,0x89,0xF4,0x9B,0xE9,0x0E,0xA5,0x8E,0xAB,0x7C,0xCC,0x4D,0xCF,0xB8,0xC1,0x7A,0xD3,0x1D,0x3D,0x6D,0x33,0x51,0x46,0xB2,0x2D, +0x42,0x9D,0x30,0xD2,0x8E,0x48,0x00,0x17,0x1E,0x40,0xF2,0xBF,0x97,0x95,0xF1,0x97,0x69,0x1C,0x19,0x5C,0x4F,0x34,0x71,0x75,0x32,0x27,0xC6,0x7A,0x79,0xD2,0x68,0x92, +0x7E,0xC3,0x5B,0x3E,0x9B,0x84,0x62,0x2E,0x2D,0xF4,0x22,0xDF,0x4E,0xB8,0xC5,0x9B,0xCE,0xD4,0x79,0xAC,0x6D,0x1D,0xFB,0x72,0xE9,0x24,0x64,0xA9,0x0A,0x35,0x21,0x23, +0x9F,0x36,0xDC,0x7B,0x58,0xE1,0xC7,0xE2,0x2E,0x5A,0x68,0xF3,0x24,0xE2,0x59,0xF4,0x24,0x93,0xAB,0x34,0x6A,0xA9,0x63,0x7E,0x87,0x91,0xE6,0x2F,0xCF,0x90,0x23,0x08, +0x39,0x1C,0x15,0x99,0xA5,0x7C,0x99,0xC6,0x6D,0x22,0x53,0xD2,0x52,0x21,0x98,0x3C,0xD7,0x20,0x8B,0xDC,0x80,0x47,0x52,0x6F,0x6C,0x4C,0xDA,0x81,0x8F,0x1D,0xCB,0x4C, +0x65,0x8F,0xF1,0x1A,0xB2,0x9C,0xA7,0x32,0x8A,0x7A,0x3C,0xA6,0x3C,0xAE,0x2A,0x9C,0xEE,0xBA,0x33,0xDD,0x69,0x65,0x1E,0x08,0x10,0x27,0xFD,0xC1,0xF2,0x00,0x0B,0xDB, +0xA9,0x03,0xCF,0x05,0x26,0xF8,0x57,0x96,0x65,0xB4,0xD2,0x55,0xE6,0x7D,0xBE,0x65,0x3B,0x5D,0xA4,0x92,0x5F,0x08,0x04,0x1D,0xC1,0x1F,0xC0,0x7B,0x60,0xBF,0xC1,0x3C, +0xF4,0x71,0x46,0x73,0xC5,0xFC,0x49,0x2C,0x4E,0xD5,0xAF,0x0C,0x74,0xB4,0x20,0x12,0x5A,0x28,0x94,0x92,0x40,0xEB,0xB9,0x09,0xFF,0x00,0x88,0xC1,0xAC,0xE7,0x33,0xA8, +0x87,0x87,0x4A,0xCF,0x33,0x09,0x5C,0x98,0x44,0x3C,0xEF,0xE2,0x1B,0xB0,0xE6,0x76,0x23,0x1E,0x43,0x55,0xA9,0xC8,0x99,0xBC,0x4A,0x69,0xB8,0xBF,0xF6,0x74,0xF1,0xE0, +0x39,0x17,0x75,0x71,0x24,0xF9,0xDA,0x65,0xB9,0x6E,0x56,0x2B,0xA5,0xCA,0x29,0xC4,0x20,0xE8,0x58,0x54,0x69,0x69,0x18,0x8E,0x77,0xC4,0xBB,0x3C,0x97,0x32,0xCC,0xE2, +0xED,0x66,0xA8,0x65,0x48,0xC1,0xD1,0x0C,0x7B,0x58,0x5A,0xD6,0x1F,0x4C,0x1A,0xF8,0xCD,0xC5,0xB9,0xA4,0x5C,0x4F,0x4D,0x97,0x2D,0xD5,0x60,0x80,0x7C,0xF1,0x90,0x6E, +0x7D,0x3C,0xB1,0x32,0x87,0x88,0x26,0x9A,0xA1,0x0E,0x63,0xAE,0x68,0xD4,0xDF,0x48,0x00,0x03,0xF4,0xC7,0xBD,0xF4,0x7D,0x26,0x35,0xC2,0x32,0x37,0x24,0xCF,0x3F,0xAF, +0xCA,0xDE,0x42,0x8B,0xC0,0x13,0xE4,0x19,0xDE,0x67,0x94,0xD5,0xBB,0x41,0x5A,0xE4,0x93,0xBA,0x86,0xB8,0xF6,0xFC,0x62,0x99,0xC2,0x5C,0x6B,0x2E,0x6F,0x54,0x0C,0x91, +0xA2,0xCA,0x14,0x24,0xAB,0xD1,0x87,0x2D,0x40,0x74,0xF5,0xC4,0xEF,0x31,0xCD,0xF2,0xCC,0xC0,0xB2,0xBD,0x03,0xA2,0x86,0x06,0x3D,0x04,0x2E,0xD6,0x00,0x83,0xF6,0xC7, +0x1E,0x1C,0xAC,0x86,0x93,0x8A,0xE1,0x92,0x35,0x31,0xC4,0xEC,0x14,0xAE,0xAB,0xEC,0x7D,0x71,0xB3,0x57,0xA3,0xC7,0x95,0x0D,0x88,0x9D,0x36,0x62,0xAC,0x01,0x9E,0xB7, +0xE1,0xCE,0x10,0xAB,0xE2,0x30,0x3B,0x8C,0x35,0x75,0x52,0x18,0xF5,0x84,0xA6,0x89,0xAC,0x17,0x6B,0xF2,0xDA,0xDB,0xDA,0xF8,0x42,0xF8,0xA5,0xC2,0xF2,0xD1,0x7F,0x89, +0xF7,0x5E,0xCE,0x78,0x16,0xD2,0x12,0x77,0x2A,0x7E,0x52,0x6D,0xE4,0x71,0x56,0xF8,0x4B,0xF1,0x73,0x32,0xE0,0x9C,0xB2,0x4A,0x1A,0x58,0xA2,0xAA,0xA6,0x60,0x74,0xA4, +0x8B,0xB8,0x3B,0x72,0x6E,0x76,0xDB,0xDB,0x00,0xB8,0xEF,0x34,0x19,0xD2,0x54,0x09,0x8A,0xCD,0x55,0x59,0x1B,0x19,0x43,0xED,0x6D,0xB5,0x7D,0xBF,0x96,0x3C,0xB8,0x38, +0xF4,0xB9,0x11,0xB1,0xB5,0xB7,0xE3,0xF1,0x3B,0x7E,0x26,0xCA,0x18,0x15,0xE2,0x41,0x66,0x97,0x2D,0xA8,0xCA,0xA8,0xA4,0x92,0x9A,0x51,0x37,0x6A,0x0B,0xCF,0x19,0xB0, +0x0A,0x41,0x00,0x5B,0xCE,0xF6,0xDF,0x17,0x4F,0x86,0x79,0xAC,0x93,0xF0,0x05,0x45,0x2C,0xE1,0xC4,0x88,0x7B,0x30,0x74,0x73,0x00,0xEE,0x43,0x5F,0xC3,0xCB,0xAF,0x4C, +0x79,0xF6,0xA2,0xAE,0x46,0xA4,0x97,0x4C,0x51,0xC6,0x91,0xB9,0x40,0xA9,0x7B,0x0B,0x1E,0x78,0xF5,0xAF,0xC3,0x3C,0x9E,0x66,0xC8,0x06,0x53,0x4E,0x8C,0x28,0xDB,0x2F, +0x82,0xA1,0x83,0x1B,0x89,0x25,0x72,0x43,0x13,0xD0,0x12,0x9D,0x3A,0x80,0x3C,0xF1,0xDC,0xF5,0x24,0x1B,0x46,0x40,0x3A,0x98,0xB4,0xEC,0x28,0xA9,0x98,0xB2,0xB8,0xA1, +0xAB,0x9E,0x48,0x5A,0xA9,0x1C,0x4A,0x40,0x91,0x1D,0x44,0xAA,0x4E,0xFB,0x11,0xBD,0xF9,0xF5,0x04,0xFB,0x8B,0x1C,0x00,0xCD,0xB8,0x42,0x96,0xB3,0x36,0x91,0x29,0xA3, +0x14,0xE8,0x8C,0x74,0x46,0xA8,0xF6,0xE5,0xC9,0x4F,0x3E,0xB7,0xF2,0xF4,0x18,0x66,0xCB,0x72,0x6A,0xD8,0x69,0x5F,0x30,0xA7,0x99,0xF4,0x8A,0xAD,0x01,0x96,0xEC,0x57, +0x6B,0x05,0xD3,0xD6,0xE0,0x1F,0xAE,0x36,0xD1,0x57,0x36,0x65,0x4B,0x2D,0x14,0xF4,0xA5,0xC2,0xF8,0x16,0x40,0x81,0x9A,0xFB,0xDF,0x90,0xE7,0xEE,0x6C,0x3A,0xE3,0x20, +0x7E,0x23,0x8C,0xA5,0x43,0x1C,0x74,0xBC,0x2E,0xD4,0x40,0x87,0xA8,0x91,0xD6,0x08,0xF4,0x2F,0x31,0x6F,0x11,0xB7,0xB9,0x2B,0xFC,0xF1,0xF2,0x82,0x33,0x43,0x95,0xCB, +0x2C,0xC4,0x9A,0xB9,0x27,0x11,0x07,0xEA,0x88,0x3C,0x2B,0xE9,0x60,0x01,0xD2,0x39,0x5F,0xA6,0xD8,0x16,0xBC,0x4F,0x57,0x49,0x3D,0x3D,0x0C,0xD4,0xE9,0x2C,0x85,0x5A, +0x67,0xA9,0x85,0x6E,0xB1,0xBC,0x9B,0x85,0xE5,0xB5,0x81,0x27,0x7E,0xB8,0x29,0x5B,0x58,0xD4,0xEC,0xAD,0xA8,0x46,0x18,0xEB,0x40,0xCC,0x41,0x27,0x9E,0xC4,0xAF,0xEE, +0x8B,0x01,0x60,0x79,0x1E,0x58,0x1C,0x8C,0x20,0xAA,0xF3,0xCC,0x93,0x7C,0x59,0xA2,0x4C,0xD2,0xBA,0x82,0x09,0xE9,0x41,0x33,0x54,0x22,0x2B,0xB5,0xF7,0x53,0x70,0x76, +0xBD,0x99,0x8E,0xDB,0xDA,0xDD,0x31,0x3C,0xF8,0xAD,0x95,0x66,0x33,0x70,0x45,0x3E,0x5D,0xC1,0xF0,0x43,0x57,0x96,0xD1,0x6A,0xA9,0xAE,0xEE,0xF7,0x57,0x45,0x06,0xC0, +0x30,0x3C,0xD4,0x73,0xF4,0xBF,0xA6,0x2B,0x5C,0x6D,0x0C,0x79,0x96,0x53,0x98,0x3C,0xD5,0x22,0x3A,0xBE,0xC4,0x84,0x60,0x4C,0x8E,0xDE,0x20,0xC1,0x6F,0xA4,0x01,0x6D, +0x2B,0x6B,0x7F,0x1C,0x48,0xF8,0x3F,0x89,0xB3,0x3E,0x1F,0x15,0xD2,0x66,0xD9,0x79,0xAD,0xA1,0x8E,0x36,0xA7,0x78,0x82,0x02,0x41,0xD8,0x80,0xDD,0x6D,0x6B,0x83,0xEF, +0x8E,0x6B,0x62,0x7D,0xC3,0x20,0x17,0x46,0xEA,0x3F,0x80,0x2A,0x75,0xFF,0x00,0xD3,0x63,0x14,0xE2,0xFA,0xEA,0x68,0x49,0x49,0x65,0xCB,0xD9,0x6C,0x5A,0xCB,0x71,0x26, +0xAF,0xA9,0xDC,0x7E,0x7D,0x71,0x43,0xE2,0x68,0xE6,0x5C,0xB2,0xA6,0x96,0x85,0xCB,0x4D,0x1B,0xAC,0xF2,0x31,0x37,0xB1,0x04,0x91,0xBF,0x9E,0xDC,0xBD,0x31,0x28,0xCB, +0xF2,0xF8,0x6A,0x38,0xA6,0x2C,0xF7,0x80,0xEA,0x4D,0x05,0x7C,0x5F,0xB6,0x34,0x73,0x38,0x0A,0x4F,0x50,0x8C,0x7D,0x3C,0xFD,0x71,0x40,0xFE,0xDD,0x4C,0xFA,0x81,0x2A, +0x69,0x61,0x78,0x65,0x47,0x06,0xBA,0x19,0x76,0x65,0x90,0x13,0x75,0x3D,0x6C,0x7A,0x63,0x83,0xEA,0xB8,0x9F,0xF5,0x5F,0xAA,0x51,0xC1,0xEF,0xF8,0xA9,0xD6,0xD1,0x95, +0x38,0xFC,0x77,0xCC,0x82,0x7C,0x68,0xCA,0x25,0x19,0xAC,0x19,0xC4,0x72,0x34,0xC8,0x62,0x09,0x23,0xDE,0xE6,0xE7,0x71,0x7F,0x4D,0xF1,0x26,0x4B,0x16,0x37,0xC7,0xAD, +0x73,0xDC,0x9E,0x9B,0x33,0xA8,0x9E,0x82,0x7A,0x78,0xE5,0xA7,0x98,0x7E,0xD1,0x98,0x05,0x29,0x7E,0x57,0x1D,0x31,0x14,0xCF,0xBE,0x11,0xE6,0x34,0xF9,0x84,0x83,0x24, +0x9E,0x2A,0x88,0xB5,0x00,0x15,0xCE,0x96,0x17,0xDB,0xF5,0x23,0xEF,0x8F,0x61,0xE8,0xFE,0xA9,0x8F,0xC6,0x31,0xB9,0xAA,0x9C,0x3D,0x7E,0x81,0xF7,0x97,0x5E,0x4C,0x9C, +0x47,0xA4,0x4A,0x58,0x80,0x6D,0xCA,0xF8,0x3B,0xC2,0x59,0x4C,0xD9,0x87,0x13,0x45,0x32,0x53,0xF6,0xB0,0xC2,0x7B,0x47,0x1A,0x49,0x16,0xF2,0x38,0x29,0x07,0xC3,0x3C, +0xFF,0x00,0xBF,0x76,0x15,0xFD,0x8D,0x20,0x06,0xCC,0x5D,0xAE,0x7C,0xF6,0xC5,0x0B,0x87,0xF2,0x28,0x38,0x69,0x85,0x25,0x3D,0x41,0x09,0x20,0x3A,0xAA,0x19,0x6D,0x71, +0xE7,0xFA,0x63,0xA1,0xAA,0xF5,0x3C,0x4A,0x84,0x21,0xB3,0x31,0xE9,0xB4,0x0E,0xD9,0x2D,0x85,0x46,0x6C,0xB5,0xE8,0xF4,0x30,0x96,0x27,0x84,0x32,0x85,0x5D,0x27,0x68, +0xFD,0x06,0x08,0x66,0x50,0x11,0x93,0xD7,0x66,0x1D,0xF1,0x5D,0x69,0xE9,0xD9,0x35,0x9B,0x16,0xB1,0x5B,0x01,0xCB,0xD7,0x1D,0x32,0xE5,0xB0,0x0A,0x76,0x73,0x5E,0x67, +0x95,0x80,0xB4,0x69,0xE1,0x5F,0x3C,0x2E,0xF1,0x55,0x44,0x54,0x1C,0x3E,0x32,0x85,0x98,0x3D,0x7D,0x59,0x0A,0xF0,0xC6,0x49,0x28,0x97,0xB8,0xD8,0x1E,0xBF,0xC7,0x1E, +0x47,0x0A,0x0D,0x46,0x71,0xB4,0xF2,0x4C,0xF4,0x2E,0xC3,0x16,0x33,0x27,0xF4,0x99,0x6D,0x55,0x5C,0x51,0x52,0x0B,0x2B,0xD4,0x4C,0x54,0x13,0xC8,0x5C,0xDA,0xFF,0x00, +0x4C,0x7B,0x3F,0xE1,0xCE,0x53,0x3D,0x27,0x07,0xD7,0xB5,0x2C,0xAE,0x29,0xAB,0xA5,0x44,0x81,0x25,0xF9,0xFB,0x34,0x04,0x2B,0x11,0xEC,0x47,0x2E,0x46,0xF8,0x8A,0xFC, +0x35,0xF8,0x47,0x99,0x66,0xF5,0x50,0xE7,0x59,0xCC,0x12,0x25,0x0D,0x37,0xED,0x16,0x94,0xA9,0xB9,0x00,0x03,0xA9,0xBD,0x37,0xC7,0xAA,0x28,0x8E,0x49,0x41,0x1D,0x2D, +0x6C,0x0D,0x2C,0x14,0xF4,0xE0,0xC4,0x90,0x25,0xEE,0xC4,0x80,0x00,0xB2,0xDC,0xF5,0x3B,0x75,0xC7,0xA4,0xD6,0x65,0xF2,0x91,0x89,0x3A,0xFC,0xCE,0x26,0x15,0xA0,0x49, +0x93,0x4C,0xDE,0x3A,0xCE,0x1D,0x99,0xA8,0xA0,0x80,0xC0,0x63,0x73,0x7A,0xC5,0xF0,0x6E,0xD7,0xE4,0x3C,0xEC,0x6C,0x39,0x90,0x05,0xCF,0x30,0x30,0x67,0x84,0x29,0xE8, +0x20,0x71,0x53,0x2C,0x80,0x36,0x9D,0x28,0xAA,0xC3,0xDC,0xDC,0xF4,0xDF,0x7B,0x7D,0xF0,0x03,0xE2,0x23,0xCF,0x9D,0xE6,0xF1,0x8A,0x09,0xDE,0x52,0x80,0x2B,0x6F,0xBA, +0x28,0x1F,0xBD,0x6F,0x97,0xF5,0xF7,0xC6,0x1C,0xB6,0x56,0xC9,0x72,0xC1,0x13,0x78,0xC8,0xBE,0x94,0xBD,0xD9,0xF7,0xDA,0xCB,0xFA,0x0F,0x3D,0xF6,0xC2,0x7E,0x3D,0xCD, +0x82,0x99,0x6A,0xA5,0x23,0x35,0x82,0x3A,0x2C,0xD4,0x53,0xD2,0x19,0xA9,0x63,0xAB,0x91,0x95,0xA4,0xA7,0x84,0xA2,0xBF,0x5F,0x10,0xB5,0xD9,0xBD,0x79,0x60,0x17,0x14, +0x54,0xD4,0x51,0xC1,0xDD,0x29,0x65,0xA8,0x47,0x56,0xBC,0x33,0x9D,0x76,0x3C,0x80,0xF9,0xCE,0xC7,0xCC,0x8F,0xBE,0x19,0x6A,0x26,0x8F,0x3D,0xC9,0xA1,0xA4,0xA4,0x31, +0xCB,0x16,0x90,0xD3,0x52,0xD7,0x06,0x32,0x3D,0xC0,0x27,0x44,0x83,0x7E,0xB7,0xFB,0x72,0xC0,0x2E,0x2F,0xA7,0xA6,0x5E,0x18,0x78,0x60,0x5A,0x82,0xD0,0x85,0x71,0x11, +0x87,0xB4,0x74,0xEB,0x70,0xD7,0x0C,0x45,0xFD,0xF7,0x1C,0xF0,0xC3,0x8A,0x8D,0x4C,0xC1,0xEE,0x2C,0x4F,0x5D,0x59,0x5D,0x42,0x90,0xC9,0x58,0x1B,0x30,0x58,0xFC,0x41, +0xE3,0xB1,0x3C,0xEE,0x6C,0x1A,0xDC,0x8F,0x3E,0x78,0x8F,0xE6,0x39,0xBD,0x56,0x4F,0xC5,0x52,0xC7,0x57,0x97,0x45,0x51,0x34,0xDA,0x92,0x59,0x35,0x04,0x2E,0x87,0x63, +0x65,0x22,0xEA,0x4D,0xED,0x71,0xB5,0xAF,0x8A,0x64,0x39,0xBE,0x57,0x34,0xA2,0x19,0x19,0x2A,0xD6,0x44,0x04,0x80,0xD2,0x24,0x88,0x77,0xE4,0x5C,0x9B,0xFB,0x7E,0x70, +0xA5,0xC7,0x3C,0x2B,0x4F,0x9D,0x70,0xFC,0xB9,0x9E,0x47,0x32,0x19,0xE9,0xED,0x74,0x98,0x08,0xE4,0x5E,0x9B,0x10,0x6C,0x45,0xF6,0xC1,0xA2,0x10,0xDD,0x4B,0x2D,0x62, +0x61,0xEE,0x19,0x7E,0x67,0x51,0x45,0x2F,0x0D,0xC4,0xF4,0xEB,0x2B,0x76,0xAC,0x93,0x5F,0xFB,0xBE,0xE3,0x52,0xD9,0x77,0x61,0x61,0xF3,0x00,0x36,0x1C,0x86,0x3E,0xE5, +0xF5,0xB2,0x55,0xE7,0x81,0x73,0xF9,0x1B,0x2E,0xA9,0x70,0xCE,0x73,0x73,0x73,0xAB,0x48,0x3E,0x16,0x03,0x67,0x5D,0x40,0x03,0x7B,0xE2,0x69,0x95,0x71,0x6D,0x66,0x45, +0x9D,0x32,0xD7,0x42,0xE5,0xD6,0xE1,0x40,0x60,0xAC,0xBE,0x7C,0xBD,0x07,0xE7,0x15,0x3A,0x7E,0x22,0xA2,0xE3,0x3C,0xB1,0xD6,0xAA,0x49,0xE2,0x99,0xAD,0xA7,0x53,0x76, +0x8B,0x38,0x08,0xAA,0xEE,0xE2,0xE4,0x86,0xD4,0xA0,0x92,0x3E,0xBD,0x4E,0x26,0x7D,0x0A,0xE4,0xEC,0x71,0x2B,0x1E,0xA4,0xA3,0x7B,0x7B,0x9D,0xF5,0x52,0xCF,0x3C,0xB1, +0xBB,0xAB,0xCC,0x84,0xBC,0xBD,0xA4,0x2C,0x35,0x3A,0xEB,0x24,0xB9,0x53,0x66,0x0A,0x4E,0xFB,0x8E,0xBE,0xB8,0x01,0x9C,0x54,0x2B,0x4D,0x3B,0x42,0x2A,0x23,0x37,0x52, +0xB7,0x8D,0xC0,0x7B,0x6F,0x60,0x6D,0x6E,0x7B,0xE3,0x56,0x5F,0x42,0x33,0x3C,0x9B,0x30,0xAB,0xA0,0x47,0x2E,0xA3,0xB1,0xDF,0x99,0x51,0x72,0x59,0xD5,0x6D,0x61,0xE1, +0x3C,0xED,0x72,0x56,0xD7,0xDF,0x07,0xB2,0x6F,0xED,0x15,0x4C,0xB5,0xD8,0x42,0xF0,0xD2,0xA3,0xB4,0x7A,0xA3,0x2C,0x4A,0x92,0x08,0xBA,0x9E,0xAC,0x58,0x7D,0xC6,0x39, +0x69,0xE9,0x43,0x09,0x3B,0x09,0x13,0x6F,0xEE,0x0C,0xC2,0x9B,0x98,0x82,0xF9,0x8C,0x0F,0x52,0xD1,0xB8,0x91,0xDC,0x90,0x75,0x68,0x2D,0x7F,0xEB,0x9E,0x09,0x55,0xD5, +0xCB,0x53,0x0C,0x71,0xE5,0x59,0x5D,0x4D,0x4C,0x97,0x11,0xBB,0x18,0xCA,0x2B,0x82,0x2D,0x6F,0x7E,0x78,0x7B,0x5C,0xB6,0xA2,0x09,0xE7,0x8A,0x5A,0x79,0x5A,0x79,0x81, +0x53,0x32,0xC2,0x40,0x0A,0x4F,0x8B,0x98,0xF0,0xDA,0xC7,0xED,0x86,0x3E,0x17,0xCA,0xEB,0x9F,0x2F,0x86,0xBE,0x85,0x69,0xA0,0x68,0x19,0x66,0x44,0x91,0xFF,0x00,0xEE, +0x1B,0x51,0x50,0xC0,0xB7,0x87,0xF7,0xC7,0xD3,0xA6,0x21,0xD0,0x86,0x23,0x71,0xBA,0x82,0x7D,0x41,0x87,0x0A,0x24,0xA3,0x22,0xF8,0x75,0xC6,0xB5,0x75,0xD3,0x15,0x8B, +0xBA,0x3A,0x80,0xC4,0x37,0x89,0x88,0x6E,0x41,0x57,0xAE,0xC7,0xCF,0x0F,0xD9,0x77,0xC3,0x8C,0x83,0x87,0x44,0x79,0xC5,0x6D,0x4B,0x55,0x67,0x32,0x3A,0x87,0x79,0x40, +0x62,0xC4,0xDE,0xDA,0x7E,0xC3,0x90,0xFE,0x18,0x76,0xA9,0x99,0x24,0xA2,0xA8,0xCC,0xAA,0xEB,0x6A,0x04,0xF4,0xC1,0xBB,0x57,0x41,0xA5,0x09,0x20,0x35,0xF5,0x2D,0xFA, +0x93,0xB6,0xC2,0xD6,0xDB,0x1D,0xD4,0xF9,0x8C,0x8B,0xC3,0x54,0xA6,0xA6,0x6A,0x10,0x95,0x23,0xF6,0x4A,0xF2,0xAA,0xBA,0x2B,0xDB,0x53,0x6D,0xAC,0xAF,0x87,0xC8,0x83, +0xE7,0x73,0x8D,0x2B,0xA7,0xDA,0x6C,0x71,0x10,0xD9,0x99,0xC7,0xBA,0x6F,0xA6,0x35,0xF0,0x2C,0x11,0xCD,0x1A,0x52,0xC2,0xA9,0xAD,0x04,0x7F,0x33,0x06,0x1F,0xB8,0x09, +0x0C,0xDD,0x7D,0x2C,0xDB,0x9B,0x8B,0x60,0x7E,0x6D,0xC4,0x91,0x25,0x0C,0xF4,0xB9,0x42,0x14,0x2C,0x09,0x24,0x4C,0x42,0x81,0xE6,0xC3,0x99,0x3E,0x96,0x00,0x60,0x5E, +0x7F,0xC4,0xC2,0x83,0x3A,0x7A,0x4C,0x9B,0x30,0x54,0x82,0xA2,0x25,0x13,0x2C,0x26,0xE3,0x61,0xD5,0xED,0xA8,0x9F,0xAE,0x16,0x17,0x31,0xD5,0x52,0x4D,0x10,0x88,0x20, +0x17,0xED,0x9C,0x8D,0xDA,0xDB,0xB9,0xBE,0xE4,0x5E,0xF6,0x1B,0x1F,0xAE,0x1B,0xB3,0x67,0x52,0xD4,0x5C,0x21,0x02,0xC7,0x4F,0x04,0xC9,0x51,0x38,0x31,0x9B,0x33,0x2A, +0xDC,0x6A,0x24,0xD8,0xF9,0x9F,0xB9,0xBF,0xD7,0x19,0x86,0x69,0x0D,0x2C,0xCF,0x23,0x0F,0xDA,0xDB,0xC0,0x84,0xEE,0x83,0xCC,0xFA,0x9F,0x7B,0xFA,0xE1,0x66,0xB7,0x3B, +0x8A,0x92,0xA1,0xCD,0x24,0x9D,0xE2,0xA1,0xB6,0xEF,0x0F,0x63,0xA7,0xFE,0x23,0x96,0x38,0x51,0xD5,0xA8,0xA9,0x5A,0x9A,0xE9,0xF5,0x03,0xE2,0x52,0x4D,0xB7,0xF3,0x1E, +0x67,0xD7,0x02,0x79,0x8D,0x59,0x68,0x92,0xA2,0x18,0x57,0xB7,0xCD,0x62,0x6A,0x4A,0x13,0x3B,0x43,0x4D,0x99,0x45,0xE2,0x60,0x57,0x93,0x3D,0xB7,0x23,0x6D,0x26,0xDC, +0xB9,0x8F,0xDE,0xC6,0x2C,0xEB,0x3B,0x32,0xF0,0xDD,0x65,0x2C,0xA1,0x6B,0x3B,0x44,0x2C,0x84,0x44,0x19,0x18,0x81,0xE2,0x3A,0x4F,0x89,0x48,0xEA,0x01,0x1E,0xDB,0xE0, +0x4E,0x5D,0x9C,0x66,0x0A,0x29,0xF2,0x2A,0x9A,0x85,0x7A,0x2A,0x44,0x4A,0x86,0x8E,0x51,0xE1,0x98,0x31,0x2A,0xCC,0x08,0xEA,0x00,0x04,0x7F,0xC0,0xF9,0xE0,0x2E,0x6F, +0x9A,0x3D,0x4C,0x83,0x25,0x4A,0x18,0xE4,0xA4,0xA7,0x70,0x3B,0x5D,0x5A,0x1D,0xF5,0x5C,0xAB,0x01,0xB5,0x87,0xFB,0xBC,0xFA,0xEF,0x8D,0xFD,0x09,0xCF,0x1D,0xC5,0x6C, +0xB2,0x3C,0xCA,0x5C,0xAE,0x44,0x30,0x56,0x2C,0x01,0x18,0x89,0x23,0x8C,0x4C,0x85,0x4F,0x3E,0x6D,0xB7,0xDB,0x0A,0xD9,0x86,0x99,0x29,0x62,0x85,0x6A,0xA3,0x9A,0x38, +0xC9,0xD4,0x12,0x37,0x1B,0xF9,0x78,0x6E,0x7A,0x61,0xCB,0x3A,0xA1,0xAD,0xAA,0x96,0x3C,0xC7,0x26,0x8D,0xEA,0x2A,0x14,0x69,0x78,0xBB,0x34,0x69,0x13,0x6E,0x64,0x82, +0xA6,0xE3,0xD5,0x4E,0xDB,0x82,0x70,0x0E,0xA8,0x56,0xD4,0x2F,0x61,0x9A,0xF7,0xE8,0x65,0xD2,0x6C,0x92,0xEA,0x9C,0x0B,0x1E,0x56,0x04,0x39,0x3F,0x8C,0x0E,0xEF,0xA8, +0xC2,0x2E,0x4B,0xB3,0xAC,0xB6,0x87,0x32,0x98,0xF7,0x38,0x3B,0x09,0xA2,0x07,0x50,0x88,0xBB,0xDF,0x7E,0x7E,0x2D,0xF0,0xBB,0xA7,0x3A,0xCB,0x2A,0x4B,0xD2,0xCD,0x2B, +0x0B,0x1F,0x93,0x66,0x02,0xF6,0xE9,0xBE,0x29,0x71,0x54,0xD1,0xD0,0xE6,0xB5,0x54,0x53,0xB4,0x8A,0x8D,0xCE,0x23,0x0C,0x94,0xFA,0x47,0xB1,0x61,0xF9,0xC7,0x1A,0xE9, +0xA8,0x25,0x8A,0x38,0xA9,0x2B,0xEA,0x26,0x57,0xBD,0xBF,0xBB,0x06,0x60,0x2D,0xCB,0xC6,0x3D,0x70,0xEF,0x31,0x15,0x71,0x47,0x0E,0xE3,0x01,0x64,0xDC,0x77,0x98,0xD3, +0x4B,0x09,0xAA,0xCB,0x44,0xE2,0x30,0x2D,0xAC,0x14,0xBD,0xBE,0x50,0x4F,0x22,0x01,0x00,0xE1,0x9F,0x2C,0xF8,0xA9,0x48,0xB2,0xA3,0xE6,0x14,0xF5,0x92,0x4A,0xCE,0xD2, +0xC8,0x16,0x72,0x2F,0x2D,0xAC,0x1A,0xC7,0x6B,0x01,0x60,0x07,0xA5,0xAF,0x80,0x74,0xD4,0x12,0x35,0x48,0x2B,0x96,0x8E,0xC8,0x1B,0x0E,0xF0,0x48,0x00,0xFD,0x2D,0xF7, +0xBD,0xB1,0xDB,0x36,0x65,0x95,0x3C,0x81,0xA4,0xA2,0x8A,0x32,0xA7,0x41,0x7D,0x23,0x9D,0xBF,0xD4,0x18,0x5F,0xEF,0x88,0x76,0x9E,0x84,0xA0,0xBB,0x7B,0x8F,0xB0,0xFC, +0x42,0xE1,0xB3,0x95,0xE4,0x94,0xD5,0x27,0x30,0xD1,0x10,0xFE,0xFC,0xA1,0xB5,0x89,0x85,0xF5,0x69,0xB1,0x3C,0xB5,0x74,0x04,0x5F,0x1A,0x5F,0xE2,0x2C,0xF9,0xCA,0x9C, +0xB7,0x28,0xA2,0xAC,0x14,0x33,0xCA,0x21,0x14,0xA0,0x84,0x0C,0x6E,0x34,0x80,0x4D,0xFA,0xDB,0x13,0xCE,0xF3,0x49,0x57,0x10,0x8A,0x92,0x92,0x79,0x99,0x98,0x03,0x2E, +0x8D,0x4C,0x3D,0xB9,0x00,0x3D,0xCE,0x0E,0xD2,0xD2,0xCD,0x47,0x1A,0xCD,0x53,0x0D,0x34,0x60,0x8D,0x51,0x76,0xB2,0x89,0x98,0x8B,0x73,0x08,0x80,0xFE,0x6F,0x80,0xDA, +0x39,0xE2,0x31,0x47,0xDC,0xA0,0xE4,0xB0,0xE6,0x79,0xAD,0x3B,0x97,0x86,0xA2,0x1A,0x75,0x62,0xD2,0x35,0x88,0x50,0x6F,0x7B,0x12,0x45,0xAF,0xE9,0x61,0x8C,0x99,0xA6, +0x61,0x54,0xD2,0x2A,0xAD,0x78,0x48,0x21,0x27,0x93,0x03,0x61,0xEB,0x6B,0x01,0xFA,0xFA,0x61,0x66,0x0E,0x2E,0xAC,0xA9,0x59,0xA9,0xCF,0x7B,0xA9,0x00,0x85,0x3D,0xAB, +0x5A,0x31,0xB7,0x45,0xF0,0xFE,0x77,0xC7,0x19,0xD6,0x59,0xA9,0x43,0x54,0xD6,0x53,0xC7,0x1F,0x35,0x45,0x1A,0xD8,0xFB,0x03,0x60,0x3E,0x97,0xC2,0x88,0x11,0x80,0x42, +0x75,0x3C,0x43,0x0C,0x59,0x7B,0x88,0x64,0x65,0x47,0x05,0x43,0x85,0xB9,0x90,0x7A,0x7A,0x7E,0x3D,0xF0,0x01,0x73,0x1A,0xEA,0xF9,0xFB,0x08,0x63,0x74,0x8E,0x4D,0xEE, +0x4D,0xC9,0xF7,0x38,0xEE,0x58,0x29,0x60,0x46,0x7E,0xCD,0xA5,0x98,0x8D,0xDE,0x56,0xE6,0x3C,0xB7,0xE5,0xED,0xB6,0x38,0x45,0x5A,0x61,0x04,0x2C,0xD1,0x53,0x8B,0xEE, +0xA9,0xD7,0xEA,0x71,0x2E,0xFA,0x11,0xAA,0x3F,0x30,0x95,0x3D,0x1D,0x3D,0x02,0x8A,0xAA,0x99,0x10,0x37,0x21,0xA9,0xB9,0xFD,0x3A,0x9F,0xEA,0xD8,0xD1,0x4F,0xDA,0x55, +0x95,0x92,0x9A,0x9C,0xEA,0x5F,0xFD,0xC7,0xD8,0x0D,0xFA,0x75,0xC6,0x6A,0x69,0xA8,0x98,0x87,0xD3,0x34,0xCC,0x4D,0xB5,0x10,0x58,0x03,0xEE,0x76,0xFC,0xE3,0x62,0xCD, +0x5C,0x11,0x60,0xCB,0xE8,0xDD,0xB9,0x8D,0x4E,0x40,0x3C,0xC7,0x41,0x8C,0xE5,0x4C,0x68,0x68,0xC1,0x9A,0xB2,0xC5,0xC4,0x8B,0x06,0x57,0x5D,0x25,0x53,0xBC,0x46,0x36, +0xA8,0xEC,0xC2,0x26,0xA6,0x91,0xAD,0xA5,0x40,0x00,0x28,0x0C,0x76,0x1E,0xB8,0x2B,0x25,0x3D,0x1C,0xE7,0x2E,0x96,0xA2,0x50,0xD5,0x54,0xF4,0xC9,0x10,0x96,0xDF,0xE4, +0x85,0x07,0xB2,0x2E,0x79,0x59,0x80,0x65,0x3C,0xF9,0x82,0x70,0x7F,0x30,0xE0,0x89,0xE8,0xB2,0xD8,0xE7,0xA4,0xA7,0xEC,0x91,0xE5,0xD1,0x3D,0x4B,0x9B,0xBC,0x84,0x92, +0xA4,0xA5,0xB9,0x2A,0xAB,0x11,0x7E,0xA7,0x09,0x59,0xC4,0x99,0x86,0x5A,0xAB,0x49,0x5B,0x1B,0xA4,0x15,0xDA,0x9C,0x4B,0x26,0xC1,0xA3,0x56,0xD3,0x7F,0xD0,0x60,0xD3, +0x52,0xAF,0xF7,0x31,0x04,0xFB,0x31,0x6B,0x3A,0x8D,0x2B,0x6A,0x6A,0x73,0x0A,0x56,0x7A,0x3A,0xD8,0x34,0x87,0x8E,0xFE,0x19,0x58,0x91,0x61,0x7B,0x75,0xB0,0x3C,0xED, +0xB1,0xC7,0x7D,0x35,0x6E,0x67,0x5F,0x45,0x35,0x2C,0x95,0x22,0x99,0xE3,0x5D,0x6F,0x4B,0x3A,0x07,0xB0,0xB8,0x07,0x49,0x66,0x17,0xBD,0xF9,0x2D,0xC7,0xA6,0x39,0x56, +0x9A,0x5A,0xFA,0x98,0x9A,0x9E,0x36,0x95,0x51,0x23,0x80,0xBE,0x9B,0x76,0x97,0x0C,0xC1,0x58,0x79,0xF8,0x5B,0x7F,0x41,0xE7,0x81,0x0E,0xD5,0x49,0x45,0x24,0xAE,0x7B, +0x46,0x25,0x81,0x8A,0x5D,0xCB,0x28,0x36,0xBA,0x8F,0x3B,0x9E,0x5E,0x98,0x33,0x94,0x54,0x30,0x20,0x9C,0xCA,0x79,0x29,0x33,0x2B,0xBB,0xC7,0x32,0xF2,0xD2,0x90,0xBA, +0x5B,0xCB,0x96,0x9F,0xE3,0x8C,0x35,0x72,0xD6,0x30,0x3D,0x95,0x0E,0x99,0x6D,0x74,0x77,0x2E,0x58,0xDF,0xCB,0x51,0xDB,0x6B,0xF4,0xC1,0x07,0x9E,0x89,0x32,0xE1,0x5B, +0x3E,0x5C,0xF2,0x40,0xCE,0x02,0xCF,0x4F,0x21,0x4E,0x5D,0x2F,0xD4,0xFA,0x36,0x3F,0xA7,0xAE,0xCB,0xAA,0x4F,0xF8,0x42,0x6A,0x2A,0x06,0x9E,0xD5,0xB4,0x35,0xFD,0x45, +0xED,0x7F,0xA6,0x2C,0x71,0xDC,0x65,0x54,0x06,0x32,0x2A,0xE9,0x22,0x2D,0x2C,0x0F,0xAF,0x4E,0xA2,0x65,0x60,0xBB,0x75,0xDA,0xE4,0xFE,0x98,0xEF,0x5E,0x1E,0x9A,0xA1, +0x3B,0xC4,0xF4,0xE9,0x23,0x8D,0xC4,0x93,0xC8,0xCF,0x61,0xE4,0x03,0x5B,0xF5,0xC3,0x06,0x55,0x21,0xCD,0x40,0xA5,0xA9,0x8E,0xBE,0x09,0x94,0x58,0xBD,0x29,0x8C,0xEA, +0x1E,0x5F,0x28,0x37,0xFA,0xE3,0xAA,0xBA,0x3C,0x9A,0x15,0x68,0xA7,0x8F,0x38,0x91,0xF7,0x00,0xCE,0x25,0x22,0xDF,0x7C,0x2D,0x9E,0x8F,0xF3,0x08,0x28,0x23,0x88,0xAF, +0x55,0x55,0x55,0x1C,0xB1,0xB4,0xDA,0x54,0xC6,0x6C,0x0A,0x13,0x1A,0x8F,0x60,0x08,0xB7,0xDF,0x06,0x67,0xCF,0xE9,0xEB,0xBB,0x29,0x62,0xA8,0x95,0x24,0x8A,0xD7,0x42, +0xC5,0xD7,0x63,0xD3,0x51,0xB9,0xFA,0x9C,0x0A,0xAC,0xA6,0xA4,0x8E,0x56,0x78,0x69,0xA3,0x50,0x0D,0xEE,0xF4,0xAE,0x6D,0xB7,0x99,0xC7,0x5C,0x33,0x53,0x4A,0x5A,0xF5, +0xA5,0x1C,0x30,0x50,0xBD,0x9B,0x28,0x3E,0xA3,0xD3,0x1A,0x15,0xB7,0x08,0xB2,0x00,0xEE,0x1A,0x9B,0x38,0xAE,0xAC,0x84,0x53,0xB0,0x22,0x3B,0xDF,0x4D,0xED,0xAB,0xDC, +0x01,0x8D,0x54,0xB2,0xB0,0x87,0xB3,0x88,0x41,0x4E,0xD6,0xDF,0xB2,0x8C,0xB3,0x13,0xEA,0x4E,0x07,0xC5,0x4E,0xA2,0xC5,0xE6,0x4B,0x83,0xB9,0xD0,0x58,0x9F,0xB9,0xC6, +0xF8,0x3F,0xB5,0x10,0x83,0x4C,0x91,0xB2,0xF4,0x3B,0x0C,0x43,0xD4,0x80,0xCC,0xA6,0x0E,0xD6,0x72,0xD5,0x95,0x15,0x6F,0xA6,0xD7,0x2C,0xDA,0x2F,0xEA,0x06,0x3B,0xA3, +0x8E,0x38,0x27,0x2F,0x05,0x33,0x05,0x26,0xF7,0x23,0x57,0xD6,0xFC,0xB1,0xDA,0xD5,0x39,0xB4,0x4C,0x75,0x44,0xA1,0x81,0xF9,0x8A,0x5A,0xFE,0xD6,0x1B,0xE3,0xA6,0x4E, +0xF8,0xD2,0x86,0x9A,0xA3,0xB2,0xB8,0xE4,0x11,0x71,0x70,0xB7,0xAF,0xE2,0x16,0xA3,0xAD,0x2F,0xA4,0x24,0x41,0xD8,0x1E,0x67,0x95,0xB0,0x6E,0x0A,0xC1,0x1D,0x38,0x59, +0x6B,0x61,0x80,0x11,0xC8,0xA6,0xDF,0xD7,0xAE,0x14,0xE0,0xA8,0x58,0x9C,0xC7,0xDB,0xC9,0x7B,0xF3,0x00,0x6F,0xF6,0xC7,0xC7,0xAC,0x70,0x42,0x69,0x91,0xF5,0x7C,0xA1, +0x93,0x9E,0xFC,0xEC,0x70,0xA6,0xC5,0x70,0xD5,0xC7,0xD0,0x9F,0xFF,0xD9,}; + diff --git a/examples/160 x 128/TFT_FLASH_Jpeg/jpeg2.h b/examples/160 x 128/TFT_FLASH_Jpeg/jpeg2.h new file mode 100644 index 0000000..1240af7 --- /dev/null +++ b/examples/160 x 128/TFT_FLASH_Jpeg/jpeg2.h @@ -0,0 +1,300 @@ +// We need this header file to use FLASH as storage with PROGMEM directive +#include + +const uint8_t Tiger[] PROGMEM = { +0xFF,0xD8,0xFF,0xE0,0x00,0x10,0x4A,0x46,0x49,0x46,0x00,0x01,0x01,0x01,0x00,0xB4,0x00,0xB4,0x00,0x00,0xFF,0xDB,0x00,0x43,0x00,0x04,0x03,0x03,0x03,0x03,0x02,0x04, +0x03,0x03,0x03,0x04,0x04,0x04,0x05,0x06,0x0A,0x06,0x06,0x05,0x05,0x06,0x0C,0x08,0x09,0x07,0x0A,0x0E,0x0C,0x0F,0x0E,0x0E,0x0C,0x0D,0x0D,0x0F,0x11,0x16,0x13,0x0F, +0x10,0x15,0x11,0x0D,0x0D,0x13,0x1A,0x13,0x15,0x17,0x18,0x19,0x19,0x19,0x0F,0x12,0x1B,0x1D,0x1B,0x18,0x1D,0x16,0x18,0x19,0x18,0xFF,0xDB,0x00,0x43,0x01,0x04,0x04, +0x04,0x06,0x05,0x06,0x0B,0x06,0x06,0x0B,0x18,0x10,0x0D,0x10,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, +0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0xFF,0xC0, +0x00,0x11,0x08,0x00,0xA0,0x00,0x78,0x03,0x01,0x22,0x00,0x02,0x11,0x01,0x03,0x11,0x01,0xFF,0xC4,0x00,0x1D,0x00,0x00,0x02,0x02,0x03,0x01,0x01,0x01,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x06,0x04,0x07,0x00,0x03,0x08,0x02,0x01,0x09,0xFF,0xC4,0x00,0x40,0x10,0x00,0x01,0x03,0x02,0x05,0x02,0x04,0x03,0x06,0x04,0x05, +0x02,0x07,0x01,0x00,0x00,0x01,0x02,0x03,0x04,0x05,0x11,0x00,0x06,0x12,0x21,0x31,0x07,0x41,0x13,0x22,0x51,0x61,0x14,0x71,0x81,0x08,0x15,0x23,0x32,0x91,0xA1,0x42, +0x62,0xB1,0xD1,0x33,0x52,0xC1,0xE1,0xF0,0x63,0x72,0x16,0x17,0x24,0x34,0x43,0x82,0x92,0xC2,0xFF,0xC4,0x00,0x1B,0x01,0x00,0x03,0x01,0x01,0x01,0x01,0x01,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x04,0x05,0x02,0x06,0x01,0x00,0x07,0xFF,0xC4,0x00,0x30,0x11,0x00,0x02,0x02,0x01,0x03,0x03,0x03,0x01,0x07,0x04,0x03,0x00, +0x00,0x00,0x00,0x00,0x01,0x02,0x00,0x03,0x11,0x04,0x12,0x21,0x31,0x41,0x51,0x05,0x13,0x22,0x61,0x14,0x23,0x42,0x71,0x81,0x91,0xB1,0x32,0xC1,0xD1,0xF0,0x33,0xA1, +0xE1,0xFF,0xDA,0x00,0x0C,0x03,0x01,0x00,0x02,0x11,0x03,0x11,0x00,0x3F,0x00,0x75,0x87,0x3D,0xB4,0x20,0xC7,0x6D,0xB0,0xDE,0x9E,0x34,0xA7,0xB7,0x7B,0x83,0x81,0xE3, +0x30,0xAD,0xDA,0xBA,0x63,0xB2,0xC8,0x75,0x29,0x56,0x92,0xA4,0x10,0x40,0xFA,0x76,0x3F,0xF3,0xDF,0x00,0xB3,0x5C,0xBA,0xB8,0xF0,0x98,0xA4,0x18,0x6D,0x21,0xC3,0xA1, +0x6E,0xBE,0x6C,0x52,0x3B,0x9B,0x5F,0x73,0xFF,0x00,0x36,0xC0,0x6A,0x3B,0x49,0xCA,0xF4,0x79,0x2F,0x2A,0x58,0x99,0x29,0xD7,0x37,0x56,0x91,0xE7,0x59,0xE1,0x29,0x1D, +0xAF,0xFD,0xFB,0x63,0xF3,0x21,0x4F,0xC7,0x3D,0xCF,0x69,0xD5,0x6F,0x00,0xFD,0x21,0xFC,0xC7,0xD4,0x98,0x54,0x19,0xEC,0xD3,0x13,0x19,0xD9,0x95,0x17,0x9C,0xB1,0x89, +0x19,0x3B,0xB6,0xDD,0xFF,0x00,0xC4,0x51,0x3B,0x01,0xED,0x84,0xB6,0xBA,0xB9,0x9F,0xA0,0x4D,0x0D,0x55,0x72,0xF5,0x32,0x74,0x17,0x56,0x50,0x92,0xA6,0x14,0x82,0xA1, +0xB9,0xDF,0x73,0xF2,0xE3,0x0B,0xB5,0xCA,0xEC,0x7A,0x45,0x6C,0x29,0xE7,0xDB,0x45,0x42,0x5A,0x8A,0xE5,0x49,0xD9,0x76,0x3C,0x0B,0x7B,0x01,0x60,0x07,0xA2,0x46,0x2C, +0xFC,0x85,0x12,0x4E,0x61,0x8C,0xC3,0xF1,0xD9,0x4D,0x46,0x9E,0xE9,0x50,0xD6,0x0A,0x75,0x80,0x8B,0xEA,0x3A,0x49,0x1D,0xC5,0xBE,0xA9,0xFA,0x75,0x1A,0x3F,0x49,0x55, +0x4C,0x91,0x96,0x32,0x16,0xAB,0xD4,0x18,0xB6,0x41,0xE2,0x15,0xCB,0x13,0xBA,0x69,0x9C,0x5D,0x6A,0x32,0xE9,0x8A,0xA3,0xD5,0x1A,0x6C,0x15,0xC2,0x5E,0xA4,0xDC,0x1B, +0x59,0x48,0x23,0xCA,0x41,0x24,0x7A,0x1D,0xF7,0x18,0x25,0x5F,0x89,0x91,0xEB,0x5E,0x3C,0x14,0x57,0xA2,0xB5,0x3D,0x94,0x84,0x25,0x0B,0x74,0x58,0x11,0x6B,0x02,0x0F, +0xA6,0x90,0x30,0xBD,0xD4,0x2C,0x91,0x4B,0x8E,0xDC,0x7A,0xBC,0x15,0xBF,0x1D,0xB6,0x80,0x71,0x7A,0x54,0x10,0xAD,0x3C,0x94,0x83,0xDA,0xE3,0x8F,0x98,0xC1,0x0A,0xB6, +0x4F,0xE9,0x9C,0x5A,0x53,0xB5,0x0A,0x9C,0x78,0xE9,0x6D,0x60,0x15,0x4B,0x93,0x21,0x45,0x4B,0xF4,0x3A,0x8A,0xBB,0xE1,0x6D,0x4E,0xC4,0x3B,0x2E,0x66,0x38,0xED,0xD7, +0xF9,0x9A,0x4A,0xD7,0x50,0xB9,0x0A,0x20,0xB8,0x09,0xA7,0x39,0x11,0x9F,0x83,0x0D,0xD6,0xA7,0xD3,0x02,0x92,0xCC,0x86,0x0E,0xB5,0xB7,0xA4,0x9F,0x2F,0x37,0x0A,0xB9, +0x20,0x28,0x8B,0x27,0x91,0x73,0xA4,0xA6,0x0C,0x49,0xB3,0xE3,0x46,0x9E,0xFE,0x66,0x64,0xC3,0x8E,0x12,0x97,0x96,0x5D,0x51,0x2A,0x2B,0x04,0x13,0xCF,0x73,0xE8,0x3F, +0xCB,0x8A,0xB6,0x26,0x7D,0xC8,0xF9,0x5F,0xAA,0x6B,0xAB,0xD2,0x22,0x85,0x41,0x42,0x94,0xDB,0x6A,0x69,0x4B,0x04,0x00,0x2D,0xA8,0x02,0x48,0x50,0x3B,0xFA,0x60,0xBE, +0x78,0xFB,0x40,0x26,0x72,0xD0,0x8C,0xAE,0xDD,0x2A,0xA3,0x1C,0xA3,0xF1,0x5A,0x94,0xCA,0xD0,0xE8,0x20,0xEF,0x6B,0xEC,0xA0,0x47,0x6D,0xFB,0xE3,0x2D,0xA2,0x66,0x75, +0x5A,0xD4,0xED,0x3C,0xF8,0xFF,0x00,0x31,0x53,0xA5,0xA8,0x8D,0xC5,0xB9,0x12,0x7D,0x3F,0xAD,0xA8,0xA5,0xE7,0xC8,0xB5,0x4A,0x75,0x2D,0x87,0x63,0x44,0x74,0xA9,0x0C, +0xBC,0x2F,0xAC,0x10,0x42,0x8D,0xBB,0x13,0x72,0x71,0xD7,0xF9,0x6B,0xED,0x31,0xD3,0xFC,0xC5,0x45,0x4B,0xAA,0x9E,0xD4,0x19,0x1E,0x16,0xA7,0x23,0xC8,0x55,0x8A,0x0F, +0xA7,0xBE,0x3F,0x3F,0xE2,0x57,0x32,0x6E,0x6C,0x79,0x65,0x70,0xC5,0x1A,0x63,0x96,0x51,0x46,0xBB,0x24,0xAB,0x7B,0xE9,0x36,0x02,0xC7,0x01,0x66,0xD1,0xEA,0x31,0xA5, +0xAD,0x98,0xCB,0x2E,0x84,0x1B,0x82,0x0F,0x3F,0xF0,0x62,0xE5,0x06,0x8A,0xC6,0xC5,0x1B,0x0C,0x0F,0xD9,0xD9,0x3F,0xA3,0x99,0x6E,0xFD,0xAC,0x73,0x65,0x03,0x35,0xE7, +0x48,0x4E,0xE5,0xC7,0xD8,0x74,0xA1,0x8B,0x3C,0xB6,0xB7,0x17,0xBE,0xC0,0xE2,0x47,0x43,0xE9,0x2E,0xFF,0x00,0xE5,0x33,0xF2,0xDE,0xF0,0x23,0xC6,0x12,0x12,0x94,0xAD, +0xB5,0x94,0xBC,0xFA,0xAF,0x75,0x1B,0xF7,0xB0,0x20,0x0B,0x0F,0x5C,0x24,0xE5,0x1E,0x8D,0xE6,0x4C,0xC3,0xF0,0xD5,0x1A,0xC3,0x6B,0x87,0x0D,0xF5,0x0F,0x05,0x0E,0x9B, +0x39,0x20,0x7A,0x81,0xC8,0x4F,0xAA,0x8F,0xD2,0xF8,0xE9,0x39,0xD4,0x88,0xD9,0x73,0x24,0xC7,0xCA,0xD4,0xE8,0xCD,0xC5,0x6A,0x1B,0x05,0xC2,0xE5,0xB7,0x70,0xF2,0x79, +0x1D,0xCF,0xF6,0xC6,0x75,0x56,0xD3,0x65,0x66,0xB0,0x73,0x88,0xBE,0xA0,0x32,0x2E,0x0F,0x53,0x16,0xDF,0x71,0xF6,0x96,0xCB,0xED,0xB4,0xA7,0x63,0xF8,0x28,0x01,0xED, +0x7A,0x82,0x95,0xE2,0x58,0x8B,0xF7,0x36,0xEF,0xDF,0x12,0x96,0xF8,0x87,0x1A,0x4A,0x0C,0x92,0x12,0xE3,0x61,0x6B,0x0A,0x55,0xFF,0x00,0x89,0x23,0xFA,0x6F,0xF5,0x38, +0x1F,0x22,0x43,0x8A,0xF0,0xD8,0x75,0x44,0xC6,0x08,0x5B,0x84,0xB6,0x78,0x52,0x5C,0x1A,0x7F,0xDF,0x1A,0x66,0xBE,0x17,0x4F,0x9A,0xA9,0x0D,0x23,0x49,0x53,0x68,0x04, +0x8E,0x52,0x53,0xB8,0x3F,0xA0,0xFD,0x31,0xC4,0x32,0xE0,0x32,0x8F,0x3F,0xDE,0x0D,0xB2,0xB8,0xCF,0x89,0x19,0xB9,0x0C,0x54,0x50,0xB9,0xD1,0x9D,0x57,0x92,0x56,0x95, +0xDF,0xF8,0x95,0xED,0x7F,0x61,0x8C,0xC4,0x26,0x69,0x71,0xE3,0x20,0xB8,0xE3,0xEA,0x67,0xC0,0xB8,0x6D,0x90,0x7F,0x3A,0x94,0x90,0x47,0xE8,0x06,0x33,0x1A,0xB7,0x2A, +0x40,0x10,0x37,0xD5,0xB9,0xB3,0x02,0x66,0x1C,0xB5,0x93,0xE9,0x4B,0x76,0x6D,0x66,0xB3,0x25,0x60,0x6E,0xB0,0xB9,0x04,0xEA,0x26,0xFB,0x5B,0xBD,0xCE,0x11,0xE3,0x66, +0xAF,0xB9,0x25,0x47,0x97,0x0D,0x95,0x39,0x42,0x2F,0x16,0x23,0xDC,0x5C,0xF8,0xAA,0x49,0x24,0xEF,0x63,0x7B,0x5F,0xD7,0x9C,0x3B,0x64,0x4E,0x9D,0xC5,0x7A,0xB3,0x36, +0xA7,0x56,0x4B,0xAF,0x2E,0x38,0x4A,0x90,0xCB,0xAE,0xEB,0x0D,0x95,0x02,0x4A,0x97,0xEA,0x6D,0x6D,0xBD,0xF0,0xA7,0xD4,0x16,0x60,0x66,0x3E,0xB1,0xB3,0x94,0x40,0x54, +0x78,0x10,0x46,0x94,0x86,0x5A,0xD9,0x6E,0xDB,0x51,0xBD,0xAC,0x05,0xED,0x6D,0x47,0x80,0x0E,0x3A,0x1D,0x1D,0x60,0xDC,0x54,0x92,0xD8,0xE7,0x3D,0xBF,0x41,0x3A,0x5D, +0x43,0xFD,0xDE,0x40,0xC6,0x62,0x8E,0x7A,0x99,0x1E,0x66,0x66,0x63,0xC3,0x91,0xE1,0xBC,0xF9,0x4D,0xED,0x73,0xA5,0x26,0xDC,0x0F,0x6D,0xF1,0xD2,0x9D,0x0C,0x79,0xD9, +0x9D,0x3D,0xAB,0x40,0xA6,0xCC,0x63,0xE2,0x92,0xB0,0xEC,0x55,0x11,0x70,0xCA,0x37,0x49,0x6D,0x29,0xE4,0xA8,0x6B,0x49,0x3B,0x0B,0x6C,0x38,0xB0,0xC7,0x21,0x57,0x0A, +0x65,0xF5,0x21,0xD8,0xF2,0x9A,0x28,0x4B,0x72,0x92,0xDE,0x94,0x02,0xAB,0x36,0x9B,0x00,0x3C,0xBB,0x93,0xA4,0x6F,0x6E,0xF8,0xE9,0x4E,0x87,0xD7,0x65,0xD0,0xA3,0x4B, +0xA6,0x57,0x7C,0x66,0xDF,0x75,0xEF,0x04,0xBC,0xD8,0x43,0x2A,0x69,0x49,0x56,0x9B,0x8B,0x0B,0x84,0x5C,0x94,0xF7,0xE2,0xFB,0x76,0xE8,0xD3,0xE1,0x83,0x23,0xDA,0xB9, +0x40,0x25,0xE7,0x54,0x8B,0x0A,0x96,0xA9,0x54,0xA9,0xAE,0x22,0x73,0xA6,0x10,0x71,0x48,0x5A,0x75,0x16,0xCF,0x60,0x05,0xB6,0x01,0x49,0x27,0xDE,0xF8,0xE6,0x5C,0xC5, +0xD3,0xFA,0xA6,0x6F,0xCE,0x82,0x35,0x7B,0x36,0x8A,0x7D,0x21,0xB6,0x93,0xF0,0xCD,0x3A,0x8F,0xF0,0xC0,0xE4,0x14,0xDC,0x0E,0xC7,0x7B,0xFA,0x62,0xE6,0xAC,0xE6,0x39, +0x15,0xDA,0xFC,0xA2,0xD3,0x05,0xBD,0x36,0x41,0x52,0x8A,0x81,0x70,0x24,0xF9,0x82,0x81,0x36,0xE7,0x7E,0xFC,0xED,0xEE,0xC9,0x96,0xB2,0x9D,0x16,0xB7,0x25,0x2A,0xAD, +0x86,0xDD,0x6A,0x4B,0x44,0x16,0x08,0x0B,0x41,0x41,0xE7,0x51,0x3B,0x0E,0xC0,0x7C,0xFF,0x00,0x5E,0x7F,0x5D,0xA8,0x65,0xBF,0x75,0x7D,0x7C,0xCA,0x1A,0x34,0x51,0x5E, +0x1E,0x73,0x9C,0x18,0xFD,0x10,0xC8,0x66,0xA1,0x47,0xAA,0x56,0xD3,0x98,0x1F,0x71,0x00,0x19,0x08,0x8D,0xE2,0x23,0xFE,0xD0,0x37,0x00,0x8B,0x73,0x7E,0xF8,0x89,0x96, +0x22,0x74,0xD5,0xA9,0xE7,0x30,0xC1,0x88,0xD2,0x19,0x7D,0x61,0x29,0xF8,0xB4,0x58,0x32,0xE6,0xF6,0x09,0xD5,0xB0,0xBD,0xBB,0x7F,0x6C,0x5C,0x3D,0x42,0xC9,0x7F,0x66, +0x5C,0xB7,0x19,0xAA,0xB4,0x98,0x6C,0xC1,0x44,0x55,0xA9,0x83,0xE1,0xA9,0xC5,0x25,0xE7,0x45,0xD5,0xA7,0x48,0xFC,0xD6,0xB1,0x1F,0xB6,0x29,0x4E,0xAC,0xF5,0x13,0x25, +0xE6,0x3E,0x98,0x42,0xA1,0xD2,0x32,0xE4,0x9A,0x63,0xC9,0x7D,0x2F,0xC2,0x2A,0x68,0x34,0x85,0x35,0xE6,0x05,0x76,0x1D,0x8D,0xAC,0x07,0xCF,0x02,0x6A,0x5B,0x50,0xC0, +0x21,0x6F,0x97,0x53,0x9E,0x3F,0x68,0xD5,0x77,0x04,0x19,0x20,0x71,0xD0,0x62,0x1F,0xCD,0xB9,0xEE,0x3A,0x59,0x4B,0x31,0xFA,0x66,0xBA,0xDA,0x54,0xAF,0x08,0x3E,0xEB, +0x09,0x53,0x64,0xDB,0x6D,0x3A,0x42,0xAF,0xBF,0xCB,0x11,0x32,0x6D,0x4D,0xE7,0xFA,0xAD,0x4F,0xA7,0x56,0x7A,0x61,0x2A,0x96,0xCC,0xA1,0xA0,0xFE,0x77,0x1B,0x41,0x00, +0xE9,0x55,0x88,0xB5,0xAF,0xB1,0xFA,0x62,0xB6,0xCA,0x15,0xA9,0xB9,0x1E,0x84,0xFD,0x49,0x8A,0xE3,0xB1,0xEA,0x05,0xC4,0x29,0x98,0x12,0x0A,0x83,0x2F,0x20,0x8B,0xEF, +0xD8,0x1B,0x5E,0xD8,0x7A,0xCB,0x5D,0x68,0xAF,0xD7,0xAB,0xE8,0x5E,0x5E,0xA8,0xA2,0x89,0x5A,0xD3,0xF8,0x30,0xE6,0x7E,0x3C,0x39,0x6B,0xB7,0xE5,0x04,0xEE,0xDA,0x8F, +0x03,0x7B,0x1D,0xB1,0x9F,0xB2,0xB5,0x4A,0x55,0x17,0x2A,0x3F,0x16,0x4F,0xF1,0x3D,0x6B,0x77,0xF2,0x4F,0x3E,0x38,0x9D,0x75,0x0F,0x2C,0x57,0xC2,0x5F,0xAD,0x30,0xCA, +0x65,0x06,0xDB,0x01,0x90,0xED,0x90,0x00,0xB5,0xEC,0x2E,0x7D,0x79,0x3C,0x61,0x12,0xA9,0x9E,0x8D,0x46,0x94,0xBA,0x35,0x65,0xA6,0x90,0x56,0xAD,0x08,0x73,0x48,0xF2, +0x1E,0xC2,0xE7,0xDC,0x0E,0xFC,0xE1,0x7B,0xA7,0xDF,0x68,0x6A,0xAE,0x69,0x96,0xAA,0x2E,0x76,0xA6,0xA2,0x95,0x55,0xA7,0x3A,0x1B,0x01,0x96,0xCA,0x10,0x55,0xBE,0xD6, +0xBF,0x3F,0xAE,0xD8,0x3D,0xD4,0xBA,0x1A,0x5E,0x44,0x2A,0xB4,0x64,0xB4,0xDC,0x67,0x8A,0x6D,0xA4,0xEC,0x95,0x5C,0x92,0x6D,0xEF,0xA8,0xF7,0xE7,0x18,0xB6,0xC6,0xD3, +0xAE,0xC4,0x3C,0x8E,0xB2,0x36,0xBA,0x97,0xB3,0xEF,0x3A,0x48,0x31,0x18,0x65,0xAC,0xA7,0x50,0x17,0x05,0xC5,0x90,0x52,0xA3,0xB9,0x48,0x59,0xD5,0xDF,0xE7,0x85,0xA9, +0xF3,0x0C,0xC8,0x8E,0x42,0x48,0xB9,0x6F,0x4D,0xEF,0xDC,0xD8,0xF6,0xFA,0x60,0x8B,0x8D,0xBE,0xE6,0x51,0x42,0x22,0x5A,0xCF,0xA1,0x41,0x66,0xF6,0x26,0xC0,0x8F,0xD6, +0xF6,0xC0,0xB7,0x99,0x42,0x29,0x88,0x96,0xD8,0x5F,0x8C,0xB5,0x36,0xA7,0x4A,0x8F,0x60,0x39,0xFD,0xF1,0x0D,0x57,0x36,0x9F,0xA9,0x8B,0x5B,0x66,0xED,0x8A,0x7B,0x08, +0x42,0x04,0x26,0x6A,0x6E,0x36,0xFF,0x00,0x8C,0x13,0xE1,0xA5,0x2D,0xB8,0x87,0x2F,0xAC,0xDA,0xC0,0x8B,0xFA,0xEF,0xFB,0x63,0x31,0x1E,0x85,0x35,0x2E,0xD7,0x42,0x12, +0xE1,0x40,0x0A,0x25,0x41,0x63,0x75,0xA8,0x9B,0x03,0xFB,0x1C,0x66,0x1B,0x64,0x50,0x79,0x12,0x86,0x8A,0xCA,0x99,0x4E,0xF1,0xDE,0x58,0x79,0x7B,0x2B,0xC1,0xA0,0xD2, +0x19,0xA7,0xC5,0x05,0x65,0x43,0x5B,0xEF,0x2B,0x72,0xEA,0xB9,0xBA,0x8F,0xF7,0xEC,0x46,0x39,0xE6,0x5B,0xCE,0xC1,0xFB,0x41,0x56,0x61,0x3D,0x1D,0xA4,0xB7,0x25,0x4A, +0x4F,0xC4,0xB8,0x2C,0x50,0x9D,0xC6,0xDD,0xCD,0xEF,0xFB,0x0D,0xB1,0xD7,0x74,0xFA,0x42,0xE5,0x28,0xA9,0xC4,0x94,0x21,0x80,0x9B,0x94,0x9E,0x4D,0xEF,0x6F,0xD2,0xC3, +0xE4,0x4E,0x2A,0x7E,0xA2,0xE5,0x9A,0x68,0xCC,0xAF,0x54,0x92,0x94,0xB4,0xB7,0xD0,0xA6,0xDC,0xB2,0x6E,0xA5,0x0D,0x8D,0xB6,0xDC,0x76,0x3F,0xEC,0x71,0x47,0x45,0x78, +0x1B,0xB7,0x7E,0x29,0x43,0x52,0x32,0x40,0x1D,0xA2,0x9F,0x4D,0x7A,0x57,0x95,0x69,0xB5,0x23,0x98,0xAA,0x95,0x26,0xAB,0x15,0x47,0x9A,0x2E,0xB6,0xC2,0x12,0xA5,0x25, +0x2A,0x36,0x51,0x29,0x57,0x2A,0x36,0xB7,0xA6,0xD7,0x17,0xC3,0xCD,0x2B,0x2D,0xCA,0x2F,0x38,0xFF,0x00,0xDD,0x32,0xC7,0x88,0xE2,0x92,0xDC,0xA6,0xDB,0xBA,0xDA,0x40, +0x36,0x4D,0xF6,0xDA,0xE3,0x6B,0x9B,0xF1,0xDB,0x9C,0x0E,0xA4,0x52,0x9E,0xA3,0xD2,0x91,0x51,0x2D,0x29,0x96,0xD2,0x3C,0x46,0xC7,0xF1,0x04,0x80,0x2C,0x4D,0xC9,0xFF, +0x00,0x9F,0x2C,0x33,0xD4,0x73,0xCD,0x17,0x28,0xD2,0x9F,0xA8,0x57,0xAB,0x21,0x86,0x14,0x82,0xB4,0x37,0x20,0x0D,0x6A,0x07,0x8B,0x27,0x9D,0xD3,0xB5,0xB6,0xB6,0xDF, +0x2C,0x52,0x3A,0xED,0xC3,0x62,0xF5,0x88,0x7B,0x04,0x1D,0xC6,0x4B,0x81,0x95,0x1B,0x43,0xAA,0x93,0x55,0x78,0x43,0x56,0xAF,0xC3,0x74,0xEE,0xA5,0xDA,0xDB,0x6E,0x6C, +0x4E,0xC7,0xB7,0x6E,0xF8,0x54,0xEA,0x27,0x5B,0xF2,0xDE,0x55,0x84,0xF5,0x36,0x34,0xAB,0xBE,0x84,0x5B,0xC4,0x49,0x1A,0xB5,0x0B,0x82,0x13,0xB5,0xEF,0x84,0xD8,0xFD, +0x6D,0xAF,0x67,0xEA,0x84,0xC8,0xD9,0x66,0x1A,0x11,0x19,0x92,0x03,0x73,0x1D,0x1A,0x4B,0x60,0x03,0xBE,0xDC,0xEC,0x47,0xF4,0xB7,0x7C,0x53,0xD5,0x7A,0x8E,0x43,0x99, +0xD5,0x38,0x70,0x2B,0xDE,0x2C,0x84,0xB5,0x20,0xB7,0x22,0x42,0x4D,0x9A,0x17,0xDB,0x71,0xDC,0x5F,0xF6,0xC2,0x25,0x1A,0xDB,0x0A,0x30,0x3C,0x72,0x71,0xD6,0x34,0xAA, +0x10,0x06,0x32,0x5D,0x5F,0x2E,0xD7,0xBA,0x8B,0x5A,0xA5,0x66,0x1A,0xE4,0xE4,0x44,0xCB,0xCF,0x38,0x1B,0x8B,0x1F,0x7D,0x7A,0x54,0x6E,0x12,0x84,0xF7,0x5A,0xBD,0x7F, +0xB6,0x2D,0x7C,0xC1,0xD1,0xDA,0x2D,0x4D,0xC7,0x2B,0x55,0x48,0xAF,0xCF,0x91,0x11,0x80,0xD4,0x58,0x2C,0xB8,0x1A,0x6C,0x36,0x81,0x74,0x37,0xFB,0x1B,0xF6,0xF3,0x1C, +0x5E,0x19,0x47,0x27,0x53,0xE4,0xC9,0x82,0xCA,0x62,0x47,0x31,0x98,0x48,0x0C,0x10,0x2E,0x10,0x34,0xEF,0xA7,0xD3,0x6C,0x72,0xBF,0xDA,0x8F,0x3C,0xE6,0xAA,0x3F,0x5B, +0x2A,0xB9,0x3A,0x93,0x54,0x91,0x0A,0x99,0x09,0xB6,0x9B,0x0D,0xB3,0x74,0x15,0x15,0x36,0x95,0x92,0x4F,0x3F,0xC5,0xFF,0x00,0x37,0xC7,0xDA,0x74,0xD4,0x6A,0xC8,0xF6, +0xDB,0x68,0x10,0x8E,0xF5,0xD5,0x90,0x46,0x49,0x95,0x66,0x67,0x7E,0x75,0x57,0x30,0x56,0x9C,0xA9,0xD2,0xBE,0x0D,0x41,0xF4,0xB0,0xF4,0x76,0x88,0x52,0x5B,0xD0,0x0A, +0x40,0x49,0x00,0x0B,0x80,0x8D,0x8F,0x7F,0xAE,0x1E,0x7A,0x4B,0x95,0x61,0xD2,0xE0,0xCB,0xCC,0x15,0x3A,0x8D,0x1E,0x4D,0x1D,0xC3,0xA1,0x3F,0x12,0x01,0x0A,0x4F,0x73, +0x73,0xBA,0x14,0x01,0xFD,0xF0,0x81,0x42,0xA8,0x55,0xA7,0x74,0xF2,0xB7,0x0D,0xE7,0x75,0x47,0x61,0x6D,0x48,0x53,0xAE,0xEE,0x75,0x15,0xDE,0xE0,0xF7,0x36,0xD5,0xF3, +0xBE,0x23,0xC1,0xCB,0x0A,0xA9,0xCA,0x34,0xD6,0xAB,0xEC,0xD3,0xD9,0x94,0xE5,0xC3,0x12,0x4A,0x92,0x85,0x1E,0x76,0xED,0x7B,0x14,0x9B,0x7F,0x30,0xC3,0xDA,0x85,0xF8, +0x35,0x45,0xB6,0x81,0x8C,0xF7,0xE3,0x03,0xF6,0x98,0xAF,0x39,0x0C,0x06,0x4C,0xE9,0x6C,0xCD,0x97,0x20,0x56,0x9B,0x8D,0x5C,0xCB,0x35,0x38,0x8F,0x68,0x92,0xDA,0x5D, +0x53,0x4E,0x82,0x00,0xD3,0xA6,0xCA,0x20,0xFC,0xAD,0x7F,0x7C,0x38,0x54,0x2A,0x52,0x2A,0xDD,0x32,0x34,0x61,0x4F,0x7F,0xE2,0x12,0x7F,0x0D,0xD7,0x05,0xBC,0x42,0x9B, +0x1B,0xA7,0x7D,0xC1,0xB0,0xDF,0xDF,0xDF,0x1C,0xF9,0xD2,0x4C,0x89,0x9E,0xA9,0xF9,0xE2,0xB3,0x47,0x11,0x1C,0x5C,0x15,0xD3,0x1D,0x7E,0x43,0xA8,0x57,0xE1,0xA8,0x22, +0xCA,0x6D,0x69,0x3D,0xC8,0x58,0x48,0xF5,0xB6,0xAC,0x5D,0x51,0xAA,0x6B,0x7B,0x2B,0xD3,0xDB,0x64,0xA8,0xA9,0xB4,0xAA,0xCA,0x52,0xCD,0xD2,0x54,0x9D,0x5B,0x7A,0xFE, +0x41,0xDF,0xB9,0xC4,0x8D,0x4D,0x2B,0x52,0x80,0x8D,0xB8,0x1E,0x86,0x4F,0xF5,0x2B,0xAC,0x2A,0x50,0x8C,0x4D,0xB0,0x50,0xA5,0x64,0xFA,0x21,0x37,0x0E,0xA9,0x6E,0x38, +0xA4,0xFB,0x05,0x10,0x71,0x01,0xC2,0x85,0xA9,0x4C,0xB5,0xAC,0xA5,0x6C,0xA9,0xCD,0x40,0xEC,0x90,0x9F,0x5F,0x9E,0x08,0x49,0x7D,0xD6,0x3E,0xEC,0xA7,0xEA,0x68,0x3B, +0xA1,0x0B,0xD2,0x8D,0xED,0x72,0x54,0xAF,0xD9,0x62,0xD8,0x1B,0x70,0xC4,0x47,0x14,0x38,0xF8,0x32,0x00,0xF9,0x9F,0xF7,0xC4,0xAB,0x49,0xA8,0x9F,0x3C,0x49,0xAA,0xC3, +0xDD,0x50,0xC3,0xA4,0x87,0x4F,0x85,0x21,0xB9,0x05,0xE4,0x4C,0x52,0x00,0x7D,0x22,0xC1,0x00,0x95,0x24,0x1D,0x40,0x5F,0xB5,0xAE,0x77,0xC6,0x63,0xC3,0xF3,0xD6,0xD3, +0x01,0xD2,0xB0,0x3C,0x45,0xF7,0xD8,0x1D,0xBB,0xE3,0x30,0xDB,0x56,0xED,0xCA,0xC2,0xE8,0xED,0x01,0x4E,0xE9,0xD6,0x93,0x62,0xB9,0x4F,0xA5,0x2D,0x0D,0x28,0x21,0xA0, +0x92,0x91,0x60,0x77,0xF5,0x27,0x14,0xC6,0x6F,0x69,0x32,0xAB,0x69,0x8F,0x09,0xB2,0x64,0x48,0x5F,0x95,0x29,0x49,0x04,0x20,0x7E,0x72,0x4D,0x8D,0x86,0xD7,0xF7,0xB6, +0xD7,0xC5,0xDB,0x59,0x21,0xD6,0x86,0xB4,0xAF,0xC9,0x77,0x0A,0x54,0x40,0x00,0x03,0xB0,0xDF,0x6B,0xFF,0x00,0x41,0xBE,0x28,0x47,0x2A,0xD5,0x0C,0xC9,0x9B,0x6A,0xB4, +0xF8,0x35,0x05,0x31,0x25,0xA5,0x84,0xB4,0xB8,0xA9,0x00,0x58,0x9D,0x85,0xB7,0xD4,0x93,0x6B,0xDC,0xD8,0x6D,0x70,0x3D,0x5E,0xAA,0x83,0xBB,0x22,0x5C,0x7B,0x40,0x19, +0x31,0x8E,0x5C,0xEA,0x7F,0x89,0x1E,0x87,0x2A,0x9A,0xB7,0x12,0x84,0xFF,0x00,0x86,0x8B,0x12,0x0F,0xF3,0x13,0x72,0x3E,0x43,0x7E,0x7E,0x58,0xE5,0x8E,0xAE,0xA6,0xB3, +0xD4,0x7E,0xB8,0x47,0xCB,0x70,0x19,0x08,0xF0,0x6C,0xD2,0x11,0x6B,0x04,0x0F,0x7B,0x5F,0x80,0x06,0x2F,0x3C,0xCB,0x5F,0xA1,0xF4,0xA2,0x8C,0xFC,0xEA,0xAC,0xCF,0xBC, +0xB3,0x03,0xA9,0x21,0x11,0xD0,0x3C,0xA9,0x51,0x17,0x37,0xE0,0xDB,0x8D,0xB6,0x1C,0x73,0x81,0x1D,0x14,0xE9,0xD5,0x5A,0xA0,0xF4,0xAE,0xA4,0x66,0x62,0xDA,0xA6,0x54, +0x54,0xA7,0x9B,0x2E,0x91,0x74,0x36,0x77,0xD4,0x7D,0x2E,0x3F,0x61,0x87,0x51,0x4E,0x9D,0x4D,0xC4,0x73,0xDB,0xF3,0x81,0x46,0xDE,0xDB,0x73,0xC4,0x64,0xC8,0x5D,0x37, +0xA6,0x65,0x1C,0xB4,0xDD,0x19,0x96,0x87,0x8B,0xA7,0xF1,0x5D,0xB7,0xF8,0x8A,0x3C,0x9B,0x9F,0xF9,0xB6,0x38,0xE7,0xAA,0x39,0x22,0xA5,0x92,0xBA,0x93,0x3E,0x0C,0x96, +0x9C,0x31,0xD6,0xE2,0x9D,0x8C,0xFD,0xB6,0x71,0xB2,0x49,0x1B,0xFA,0x8E,0x0E,0x3A,0x17,0xA9,0x3F,0x68,0xA6,0x28,0x19,0x91,0x34,0x9C,0xAB,0x0E,0x2D,0x41,0x96,0x54, +0x03,0xEF,0xAD,0x77,0x4A,0xAD,0xC8,0x41,0x1F,0xD7,0xF6,0xC2,0xE5,0x5F,0xAE,0xF9,0x55,0xFC,0xC4,0xC0,0x99,0x4D,0x4D,0x5E,0x99,0x29,0x84,0x29,0x6D,0x3A,0xD0,0x2B, +0x88,0xE9,0x27,0x5A,0x49,0x50,0xF3,0x0E,0x38,0xC6,0x34,0x6B,0xAC,0xD3,0xB9,0xB4,0xA6,0xED,0xD1,0x8B,0x1E,0xAB,0x06,0x33,0x8C,0x4E,0x82,0xFB,0x29,0x67,0xCF,0xFC, +0x5B,0xD3,0xAF,0x84,0x79,0x0A,0xF8,0xEA,0x3F,0x86,0xC2,0xC2,0xB7,0x52,0xD3,0xA0,0x58,0xFE,0xCA,0xC5,0x9B,0x9E,0x3A,0x41,0xD1,0x79,0xF2,0xEA,0x3D,0x46,0xEA,0x15, +0x26,0x13,0x6E,0x2D,0xA4,0x19,0x32,0x67,0x3A,0x50,0x84,0x84,0xA0,0x25,0x3B,0x5E,0xD7,0xB0,0x1F,0xA6,0x39,0x1B,0xA4,0x35,0x5E,0xA4,0xE4,0xBC,0xC5,0x9B,0x6A,0xBD, +0x3E,0xCA,0xEE,0xD5,0xCC,0x8A,0x9B,0x6D,0x86,0x10,0xC2,0x9C,0x4A,0x1A,0x21,0x6B,0x49,0xB0,0xE2,0xE9,0x29,0x00,0x9D,0xB1,0x62,0x66,0x3E,0xBD,0x65,0x7E,0xAA,0x65, +0x7A,0xEE,0x48,0xEA,0xF6,0x57,0x99,0x43,0x99,0x4D,0x2D,0x48,0x65,0x88,0xEA,0x5F,0x88,0x4A,0x5C,0x48,0x71,0x3A,0x4E,0xE1,0x61,0x2A,0x57,0x23,0x8B,0xFA,0x62,0x9E, +0x96,0x94,0xE4,0x8E,0x9E,0x24,0xFB,0x98,0x83,0x2A,0x39,0xF5,0x4C,0x99,0x52,0xA3,0xE7,0x8A,0x8E,0x54,0xCA,0xCA,0x67,0x28,0xD3,0x1D,0x65,0xB8,0x6E,0x06,0xCD,0xDF, +0x71,0x4A,0x29,0x41,0x70,0x5E,0xE0,0x12,0x36,0xF6,0x1E,0xB8,0x45,0x25,0xC9,0xA2,0x4B,0xE9,0x86,0xD3,0x29,0x65,0x0C,0x3A,0xB0,0xEB,0x21,0x5E,0x52,0x8B,0x15,0x11, +0x6B,0xEA,0x4E,0xF7,0xF6,0x4E,0x3A,0xF3,0xA8,0x39,0x07,0xA5,0xD9,0x2E,0x83,0x91,0x9F,0xC8,0xD4,0x64,0x8C,0xB7,0x99,0xEA,0x6C,0xB3,0x50,0x71,0x32,0x1C,0x5B,0x72, +0x19,0x5B,0x77,0x6C,0x28,0x28,0x91,0xC9,0xD4,0x0F,0x20,0x8D,0xB9,0x38,0xA8,0xFA,0xCF,0x43,0x6F,0x23,0x75,0x72,0xA7,0x47,0xA7,0x53,0xD6,0xAF,0x05,0x2D,0x29,0x0B, +0x29,0xD9,0xD8,0x8E,0x28,0xAD,0xBF,0xFF,0x00,0x2A,0x0B,0x6A,0xFD,0xEC,0x3D,0x70,0x9E,0xAF,0x47,0xEC,0xEE,0x74,0x1D,0x7E,0xBD,0x3F,0xDC,0x47,0x34,0xDA,0x93,0x66, +0x11,0x8C,0x49,0xCA,0xB9,0x87,0xA8,0x03,0x3A,0xC1,0xCB,0xF0,0x66,0xB3,0x49,0xA8,0x68,0x4C,0x78,0x73,0x02,0x35,0x25,0x5E,0x22,0x4E,0x94,0x38,0x91,0x74,0xA9,0xA5, +0x11,0xA4,0xEC,0x74,0xDC,0x1E,0xD8,0xB8,0xB2,0xDD,0x29,0xE8,0x59,0x58,0xC0,0xAF,0x40,0x4C,0x7A,0x8B,0x48,0x4B,0xAB,0x49,0xDB,0xCA,0x11,0xC2,0x6C,0x7B,0x05,0x14, +0x9F,0xAE,0xFB,0x1C,0x50,0x6F,0xE7,0x55,0xD0,0xB3,0x7C,0x69,0x54,0xC8,0xEA,0x2D,0xC7,0x29,0x91,0x16,0x42,0x01,0x2E,0x44,0x78,0x6F,0x61,0xEA,0x80,0xA4,0x6E,0x9F, +0x4C,0x75,0x20,0xA6,0xC0,0xCC,0xA4,0xF5,0x0A,0x0B,0xEB,0x5D,0x16,0xB3,0x4B,0x45,0x55,0x0D,0x07,0x42,0x04,0x67,0x6E,0x02,0xDA,0x04,0xFA,0x2F,0x51,0x23,0xE7,0x80, +0x0D,0x39,0xBB,0x4E,0x70,0xB8,0x23,0x90,0x3F,0x98,0x9F,0xA9,0x81,0xBB,0x24,0xC4,0x09,0x76,0x7A,0xB0,0x8A,0xAB,0x88,0x5F,0x82,0x87,0x83,0x60,0xDC,0x02,0xA4,0x70, +0x3F,0xA7,0xF4,0xC7,0xAA,0xD3,0x80,0x31,0x20,0x30,0x9D,0x04,0xA5,0x28,0x4A,0x51,0x6F,0x28,0xBF,0x6B,0xFB,0x61,0x87,0x32,0x65,0x78,0x02,0x20,0xA9,0x51,0x6A,0x3E, +0x3D,0x39,0xC4,0x83,0xAD,0xF0,0x75,0x25,0x42,0xC1,0x43,0x6B,0xF0,0x7E,0x5F,0xD3,0x0A,0xF5,0x36,0xD9,0x90,0x8F,0x19,0xA9,0x61,0x69,0x53,0x9A,0x94,0x94,0x20,0x92, +0x8B,0x1B,0x77,0xF9,0xFE,0xD8,0x85,0x7D,0x2D,0xB8,0x6E,0xFA,0x48,0xEC,0x4E,0x49,0x30,0x1C,0xD6,0x9F,0x90,0xD4,0x76,0x16,0x52,0xDA,0x00,0x2A,0xD6,0x6E,0x48,0x37, +0xF4,0xF9,0x63,0x30,0xC1,0x0A,0x0D,0x16,0x6C,0xC4,0x25,0xD4,0x4B,0x9C,0xA6,0xAC,0x52,0xD2,0x12,0x10,0x8B,0x8D,0xC6,0xA2,0x4F,0x00,0xDC,0x9E,0x05,0x87,0x38,0xCC, +0x38,0xAA,0xDD,0x01,0x1F,0xBC,0x1D,0x76,0x15,0x18,0xC4,0xBB,0xFA,0x95,0x9B,0xCC,0x5A,0xBD,0x3F,0x28,0x50,0x58,0x71,0xF9,0x12,0x55,0xE1,0xAF,0xC2,0x4D,0x94,0x96, +0xFB,0xAB,0x7E,0x00,0xE7,0xDF,0xDF,0x8C,0x49,0xA5,0xE4,0xBC,0x9B,0xD3,0xF8,0x6A,0xAE,0x19,0x28,0xA6,0xBA,0xB4,0xF8,0x92,0xA6,0xC8,0x78,0x92,0x4D,0xB7,0xDD,0x47, +0x8F,0xA0,0xC1,0xFC,0xA5,0x94,0x8A,0x24,0x3B,0x9A,0x6B,0x2D,0x87,0xA7,0xBC,0x48,0x61,0x97,0x2D,0xF8,0x48,0xDB,0x61,0xE8,0x4F,0x72,0x3B,0x7B,0x13,0x7E,0x6C,0xFB, +0x60,0x67,0x56,0xD1,0x06,0x36,0x5D,0x62,0xA8,0x95,0x49,0xF1,0xC1,0x91,0x12,0x3B,0xDF,0x95,0x2A,0x41,0xFC,0xE0,0x6F,0xBF,0xEF,0xBE,0x3A,0xCA,0x74,0x8A,0x06,0xE3, +0x29,0x35,0xC5,0x8E,0xD1,0x23,0xD4,0xF2,0x7B,0xDD,0x6B,0xFB,0x41,0xC0,0x83,0x40,0x4B,0xB3,0x28,0x31,0x02,0x57,0x22,0x68,0x68,0xF8,0x7A,0x0F,0x9A,0xFF,0x00,0x5F, +0xF5,0xC5,0xB9,0xF6,0x9E,0xAC,0x3D,0xD3,0x1E,0x82,0xC5,0xA2,0x51,0xDB,0x53,0x2B,0xAA,0x2B,0xE1,0x0B,0xE8,0x1A,0x74,0x21,0x29,0xB9,0x17,0x16,0xDC,0x8D,0xBE,0x57, +0xC1,0x5F,0xB3,0x64,0xE8,0x10,0x3A,0x37,0x4A,0x76,0x95,0x4E,0xF8,0x30,0xB6,0xFC,0xC1,0x6A,0x2A,0x2A,0xDF,0x72,0x2F,0xBD,0xAF,0x7C,0x22,0x7D,0xAA,0x2A,0xF4,0x6E, +0xAB,0x66,0x9C,0x97,0xD3,0x6A,0x3D,0x65,0x87,0x6A,0xAA,0x9E,0xAF,0x89,0x6A,0x3A,0x83,0x81,0x84,0x11,0x62,0x55,0x6E,0x08,0xB1,0x36,0xE7,0x0C,0x0A,0x14,0xFD,0xE3, +0x0E,0x07,0x49,0x95,0xB7,0x9D,0x99,0x9C,0x8D,0x90,0x3A,0x71,0x59,0xEA,0x26,0x64,0x51,0x5F,0x89,0x1E,0x98,0xD1,0xBC,0x89,0xA4,0x6C,0x3F,0x94,0x13,0xC9,0xC3,0xD7, +0x50,0xFA,0x45,0x45,0x4D,0x2A,0x2C,0xBA,0x14,0xB8,0xB0,0x60,0xC4,0x49,0x41,0x70,0xB7,0x77,0x1E,0x55,0xEC,0x75,0x28,0x90,0x0E,0xE3,0x6F,0x9E,0x2F,0xB6,0xB2,0x35, +0x3A,0x91,0x4A,0xA9,0xE4,0x8C,0xBF,0x29,0x0D,0x33,0x47,0xF0,0x94,0xEA,0xE2,0xAC,0x07,0x1D,0xD8,0x2C,0x85,0x7A,0x15,0x10,0x41,0xC5,0x55,0xF6,0x92,0x08,0x39,0x7E, +0x87,0x96,0xE2,0x2D,0xF8,0xB3,0xDF,0x79,0xB7,0x57,0x11,0xC2,0x6C,0x42,0xC1,0xD3,0xB9,0xE2,0xCA,0x49,0xDB,0xB6,0x21,0x59,0x6E,0xA2,0xDD,0x48,0xDA,0x76,0x81,0xFF, +0x00,0x52,0xC5,0x6D,0x52,0xA6,0x31,0x9C,0xCF,0x9D,0x13,0xCE,0xD9,0xC3,0xA3,0xF3,0xEB,0x39,0xA9,0x88,0x13,0xF3,0x0D,0x11,0xA6,0xD2,0xC5,0x40,0x8B,0xA7,0xE1,0xD2, +0x93,0x64,0xA8,0x85,0x5B,0xD7,0x63,0xDF,0x0E,0xDD,0x41,0xEB,0x97,0x47,0xF3,0xAB,0xAD,0x55,0xAB,0xF9,0x0A,0x4C,0x2A,0xD3,0xA8,0x09,0x8F,0x51,0x54,0x64,0x87,0x00, +0x20,0x79,0x94,0xA6,0xD6,0x14,0xA0,0x3D,0x2E,0x6E,0x2E,0x3B,0x9C,0x55,0xD9,0x7F,0x34,0x42,0xCA,0x32,0x27,0xD2,0xEB,0xCF,0xCF,0x93,0x97,0xAA,0xF4,0xB3,0x41,0xAB, +0xAD,0x9B,0x95,0xC6,0x70,0x5C,0x07,0xB4,0x9D,0x95,0xA4,0x93,0xC7,0x65,0x1E,0x70,0x12,0xAF,0x07,0x33,0xBF,0x40,0x42,0x58,0xCA,0xE2,0xB1,0x1E,0x08,0x2D,0xB1,0x98, +0xE2,0x85,0x39,0x13,0xC3,0x03,0x67,0x3C,0xA9,0x36,0x36,0xB5,0xC1,0x22,0xC7,0x9F,0x4C,0x52,0xD3,0x5C,0xCD,0x52,0x90,0xA0,0xF9,0xED,0x11,0xD4,0x56,0x05,0x87,0x92, +0x27,0x53,0xC6,0x81,0x0B,0x37,0xE4,0xAE,0x9F,0x35,0x45,0xAA,0x42,0x9D,0x43,0xA3,0xD5,0x5A,0xAB,0x48,0x59,0x25,0xB6,0x92,0xDA,0x50,0xAB,0xA1,0x08,0xDE,0xDE,0x73, +0x7D,0x1F,0xC2,0x76,0xD8,0x00,0x31,0x54,0x75,0x9F,0x38,0x2F,0x35,0x7D,0xA9,0x04,0xCA,0x43,0x8D,0x3B,0x05,0xCA,0x52,0x62,0x2D,0x0B,0x37,0x28,0x4B,0x6B,0x2B,0x0A, +0x23,0x84,0x92,0xBE,0x37,0xED,0xF2,0xC3,0x3F,0xD9,0xA9,0x74,0xF8,0x39,0x1F,0x30,0x64,0x9A,0xAC,0xD6,0x1D,0x93,0x0D,0x83,0x3E,0x3B,0x8D,0x2C,0x9F,0x19,0xA5,0x02, +0x54,0x9B,0x7A,0x83,0x62,0x7F,0xEF,0xC2,0x6C,0x7A,0x2D,0x26,0x6D,0x41,0xE7,0x56,0x14,0x1D,0x71,0xC0,0xF0,0x74,0x91,0x65,0x2C,0x70,0x0E,0xF7,0x21,0x22,0xD6,0x16, +0xFF,0x00,0x65,0x7D,0x56,0xE6,0xAD,0x3D,0xBF,0x23,0x10,0x9A,0x0A,0xF2,0xC5,0xBC,0x4A,0xAE,0xA9,0x4F,0x10,0x6A,0xCE,0xBA,0x1C,0x69,0x87,0xD9,0x74,0x1B,0x1E,0x09, +0x07,0x5E,0xDF,0x3B,0x94,0xDB,0x16,0x66,0x4D,0xA9,0xBB,0x99,0x32,0x95,0x33,0x2F,0xBE,0x99,0x0B,0xA5,0xD2,0x96,0xE2,0x20,0xC3,0x65,0xCD,0x28,0x52,0x8A,0x8A,0xCA, +0x96,0x6C,0x4A,0x80,0x0A,0x36,0x1F,0xCA,0xAD,0xEF,0x7C,0x06,0xCC,0xB4,0x18,0xCD,0x57,0x98,0x88,0x86,0x8A,0x03,0x81,0x20,0xBA,0xE0,0xB1,0x51,0x2A,0xDC,0x9E,0x6C, +0x0D,0xCE,0xFB,0x77,0xDB,0x12,0x7A,0x7C,0xC5,0x46,0x95,0x94,0x1A,0x9F,0x1A,0xA0,0x22,0xC8,0x92,0xE9,0x4B,0x4D,0xA1,0x24,0xA5,0x09,0x4D,0xC0,0x02,0xC0,0xD8,0x5D, +0x4A,0xF9,0xDC,0x9E,0x4E,0xF2,0xE8,0xB4,0xAD,0x4C,0x43,0x60,0x9E,0x23,0x1E,0xA3,0x8F,0x88,0x96,0xBC,0x60,0x22,0x52,0xEA,0x54,0xB7,0x69,0x12,0x51,0x19,0xF4,0x38, +0xB6,0x9E,0x54,0xA4,0xA9,0x01,0x7A,0x4E,0x93,0xA7,0xC3,0xB8,0x07,0xE7,0xD8,0x7C,0xF0,0xB8,0x88,0x54,0xF9,0xAE,0xB7,0x10,0x29,0xF6,0x02,0x98,0x0F,0x3A,0xEE,0xB4, +0x59,0x3B,0xA6,0xE7,0x7B,0x58,0x73,0x7D,0xEF,0xE9,0x83,0x15,0x70,0xEC,0xAC,0xA2,0xD2,0xA5,0xCF,0x31,0x63,0x34,0xB0,0x5D,0x71,0x57,0x25,0x5B,0x6E,0x12,0x3B,0x9E, +0x2C,0x3F,0x5C,0x00,0xA4,0x56,0xA3,0x07,0xD2,0x8A,0xAC,0x06,0x64,0xD3,0xD0,0x90,0xC2,0xDA,0xD3,0x6D,0x41,0x5A,0x41,0x51,0x3C,0xEA,0xEE,0x3D,0x3B,0x77,0xC4,0xB7, +0x56,0x20,0x09,0x15,0x90,0x06,0x35,0x99,0xB2,0x44,0xEA,0x2C,0x5A,0x67,0xDD,0xF1,0xA6,0x4E,0x2C,0xAD,0x41,0x2F,0x3E,0xE4,0x70,0x16,0xF8,0xBF,0xA8,0x5D,0xC0,0xE3, +0xCB,0x63,0x7E,0x4F,0x6B,0x66,0x02,0x66,0x3A,0x64,0x48,0x33,0x24,0x53,0xD8,0x4A,0x92,0x95,0x9F,0x1A,0x33,0x88,0x37,0x4A,0xD0,0x77,0xDE,0xFB,0x83,0x6D,0xAD,0xEA, +0x0E,0x33,0x1E,0x65,0x81,0xE9,0x14,0xDA,0x80,0x90,0xE3,0x91,0x3B,0x46,0xB9,0x51,0x79,0x8C,0xBB,0x26,0x5B,0x12,0x9B,0x43,0x85,0x3A,0x12,0xE1,0xB0,0x4B,0x63,0xB9, +0xBF,0xA0,0xDC,0xE3,0xF3,0x9B,0xAB,0xD9,0x6E,0x2B,0xBD,0x4B,0x12,0x3E,0x2D,0xE9,0x8F,0xCF,0x96,0x02,0xD6,0x91,0xA8,0xA9,0x4A,0x36,0xB6,0xF8,0xED,0xFC,0xFB,0x55, +0x62,0x7C,0x75,0xD2,0x59,0x70,0x29,0x9D,0x3A,0x74,0xA9,0x41,0x29,0x57,0x3C,0x93,0xB0,0x1B,0x9D,0x8F,0x38,0xA4,0xF3,0x76,0x50,0xA5,0xBB,0x49,0x76,0x4D,0x42,0x32, +0x94,0xF3,0x88,0x5A,0x52,0x9D,0xDB,0x5B,0xF7,0x16,0x28,0x4A,0xAD,0x74,0xA0,0x8D,0x94,0xAE,0x48,0x3A,0x47,0x73,0x8E,0xFE,0xC3,0xD8,0x46,0xAA,0x6D,0xBC,0xC8,0xD4, +0xDC,0xFF,0x00,0x44,0x8B,0xF6,0x62,0xA9,0xC5,0x5C,0x9A,0x84,0x58,0x54,0xFB,0x40,0x6A,0x5C,0x20,0xA6,0xDC,0x16,0xB2,0x50,0xEA,0x7D,0x2F,0xB1,0x23,0xDC,0xE2,0x88, +0xE9,0x9F,0x4F,0x33,0x9D,0x67,0xAE,0x51,0x69,0xF4,0xAA,0xBF,0xDD,0x75,0x16,0x90,0x67,0xC7,0xA8,0x12,0x16,0x16,0x9B,0x79,0x5C,0x45,0xFF,0x00,0x38,0x37,0xF7,0xE4, +0xE0,0xAC,0x8A,0x76,0x73,0x9F,0x0D,0xCA,0x46,0x64,0x90,0xCB,0x99,0x78,0xB8,0x08,0xA7,0xD3,0xCA,0x5A,0x4B,0x76,0xFC,0xBA,0x6D,0xC8,0x16,0xE0,0x93,0x7E,0x4D,0xCE, +0xF8,0xB1,0x1B,0x6E,0xAC,0xFE,0x44,0xA6,0xA7,0x23,0xD3,0x9C,0x6E,0xB5,0x42,0x5A,0x57,0x4E,0x98,0xE3,0x89,0x53,0xC9,0x4F,0x0A,0x42,0x92,0xBE,0x52,0x41,0x3B,0x6E, +0x36,0xF6,0xC0,0xC5,0xAB,0x8D,0x80,0xC3,0x0F,0x8E,0x71,0xDE,0x22,0x51,0xB2,0x97,0xDA,0x09,0x1D,0x43,0xCE,0x74,0xB8,0x12,0xDF,0x72,0xA8,0x82,0x17,0x55,0x2E,0x2D, +0x03,0xE2,0xC1,0x55,0xD2,0x50,0x54,0x2D,0xE6,0x1B,0x8B,0x5B,0x6D,0xB1,0x60,0xE7,0x3C,0x9B,0x51,0xEA,0xB6,0x72,0xA1,0x75,0x07,0x2A,0xC4,0x6E,0x4B,0x8C,0x46,0x4C, +0x2A,0xED,0x19,0xC7,0xD2,0xD4,0x88,0x52,0x59,0xBE,0xC5,0x27,0x71,0x7B,0x6C,0x6D,0xDB,0x0E,0x94,0x1C,0xC5,0x51,0xCB,0xB9,0x9D,0x39,0xFB,0xA9,0xF5,0x98,0x14,0xB1, +0x22,0x02,0x20,0xCA,0x8A,0xC2,0x6E,0xA7,0xDE,0x42,0x89,0x49,0x40,0x4F,0xE6,0x36,0x3C,0x76,0xF9,0x60,0x63,0x59,0x23,0x2C,0xF5,0xAF,0xA8,0x93,0x33,0xA7,0x4A,0x33, +0x45,0x5B,0x2C,0xD6,0x98,0x5A,0x53,0x52,0x84,0xB0,0xB8,0xEA,0x94,0x8B,0x5B,0x58,0x09,0x52,0x4A,0x6E,0x36,0x3D,0x8D,0xBB,0x6F,0x7F,0x0A,0x8B,0x15,0x8F,0x99,0xA0, +0xE5,0x58,0x1F,0x12,0x80,0x7D,0x99,0x12,0xAB,0xF5,0x86,0x23,0x26,0xA5,0x19,0xC5,0xC8,0x2E,0x3F,0x4E,0x92,0x94,0x87,0x19,0x5E,0xE1,0x49,0x20,0xF6,0xBF,0xF1,0x0F, +0x4C,0x58,0xDD,0x23,0xAA,0xC9,0xC8,0x52,0x27,0x2C,0x3C,0xF2,0xE9,0x73,0xD8,0x5B,0x53,0xA2,0x36,0xAD,0x9C,0xF2,0x90,0x08,0x1F,0xE6,0xDF,0xB7,0xFA,0xE2,0x46,0x73, +0xE9,0x5E,0x7C,0xA7,0xE6,0x05,0x37,0x57,0xA1,0xD7,0x2B,0x69,0x8D,0x71,0x1A,0x7C,0x95,0x47,0x6D,0xDD,0x27,0xFE,0xA2,0x5C,0x25,0x47,0xD2,0xE3,0x12,0x68,0xD4,0xB7, +0x69,0xF4,0x35,0xB3,0x55,0x86,0xC4,0x39,0x4A,0x46,0xA1,0x1D,0x4F,0xA5,0xE5,0x81,0x7D,0xB5,0xAA,0xC0,0x1F,0x40,0x00,0xF5,0x37,0xDB,0x10,0xCD,0xED,0x43,0xF0,0x65, +0x7F,0x6D,0x6D,0x4C,0x19,0x58,0xD0,0xE5,0xD6,0x59,0xCC,0xAD,0x4E,0xA6,0xFC,0x55,0x39,0x5A,0x94,0xD2,0xF4,0x9F,0x32,0x9B,0x37,0xD9,0x56,0xE7,0x6B,0x7E,0x98,0xBE, +0x29,0x74,0xB2,0xD5,0x2D,0xB5,0x85,0x29,0x0B,0x4A,0x00,0x50,0x52,0x74,0xDE,0xFF,0x00,0xBE,0x06,0x51,0x32,0xB4,0x65,0xC6,0x12,0x1F,0x69,0x3A,0xDC,0x37,0x52,0x49, +0x17,0xFD,0x70,0xEA,0xC4,0x76,0x9A,0xCB,0xF2,0x16,0x1B,0x51,0x17,0xB6,0xFB,0x58,0x58,0x0E,0x3E,0x98,0xC6,0xBB,0x54,0xB7,0xB0,0x20,0x4F,0x74,0xF5,0x1A,0xC1,0x11, +0x07,0x31,0x53,0xBE,0xF1,0x53,0x08,0x16,0x70,0x92,0xA0,0xA5,0x0B,0xD9,0x00,0x8B,0xDF,0xD8,0xED,0xDF,0x13,0x28,0x89,0xA7,0x52,0xB2,0x84,0x67,0x2A,0x75,0x14,0x33, +0x19,0x08,0xF0,0xEC,0x82,0x52,0xB7,0x56,0x9D,0xEC,0x90,0x05,0xEC,0x38,0x2A,0xE3,0x6C,0x19,0x44,0x45,0xB3,0x4C,0x5C,0xC5,0x36,0x15,0xBE,0xFC,0xD9,0x3F,0xF3,0xFD, +0x30,0x9F,0x59,0xA8,0xA9,0xF8,0x0D,0x45,0x95,0x20,0x3E,0x12,0xA5,0x29,0x8B,0xA0,0x79,0x00,0xF2,0xE8,0x4F,0x1A,0x46,0x94,0xA0,0xDB,0x7B,0x9B,0x9E,0xF8,0x9C,0xF5, +0x82,0xB9,0x3D,0xB9,0x8B,0x7A,0x88,0x2C,0xA2,0x4D,0xAD,0xE6,0x09,0x23,0x2F,0xA1,0x6C,0x4A,0x09,0x43,0x8F,0x2D,0xBF,0x05,0x5A,0x55,0xB2,0x8E,0xC0,0x5D,0x37,0xB8, +0xE6,0xE0,0xE0,0x32,0x2A,0xB2,0x58,0x60,0x30,0xCA,0xD6,0x94,0x36,0x91,0xA0,0x92,0x7B,0xFA,0xDA,0xD8,0xD5,0x34,0xC4,0x35,0x38,0xEC,0x49,0x48,0x6D,0x94,0xD9,0xFF, +0x00,0x1F,0xC3,0xD7,0x65,0x29,0x09,0x00,0x5B,0xD8,0xDC,0xDB,0x1E,0x65,0xC7,0x96,0xF8,0x4B,0x07,0xE1,0xDA,0x5E,0xE9,0xBA,0x97,0x64,0x27,0x7B,0xDE,0xFB,0xED,0xDF, +0x01,0x73,0xBB,0x91,0x23,0xDE,0x80,0x39,0x71,0xD3,0xFF,0x00,0x24,0xF8,0xD5,0x37,0x64,0xD3,0x97,0xE2,0x3C,0x7E,0x30,0xA9,0xB2,0x16,0xB5,0x2A,0xC9,0x4E,0xB4,0x83, +0xEF,0x6E,0x6E,0x3D,0xF1,0x98,0x80,0x90,0x93,0x19,0x2D,0xC4,0x42,0x1F,0x75,0x04,0x94,0x3C,0x7F,0x29,0x1A,0xBB,0x26,0xD7,0xDF,0xDC,0xFD,0x31,0x98,0xC7,0x20,0x0C, +0x98,0x11,0xED,0xE0,0x16,0x59,0x6D,0x66,0x4C,0xE3,0x4B,0x6E,0x49,0x94,0xFB,0xE8,0x6D,0xB4,0x90,0x95,0x2A,0xD7,0xB0,0xBE,0xDA,0x41,0xEE,0x77,0x23,0xB9,0x02,0xFE, +0x51,0x8A,0xE6,0x5F,0x50,0x9F,0x9F,0x50,0x65,0xD9,0x91,0x93,0x36,0x9A,0xFB,0x8A,0x40,0x65,0xE2,0x49,0x0D,0xF1,0x64,0xAC,0x58,0x83,0x62,0x4F,0xA5,0xC9,0x36,0xBE, +0x14,0x6B,0xEE,0x4A,0x7D,0x96,0x5B,0xD6,0xE2,0xDB,0x0A,0xF2,0xA0,0xEF,0xA0,0x90,0x55,0xFB,0x83,0x7C,0x6A,0x7A,0x32,0x86,0x57,0xA3,0x16,0x54,0x35,0xAC,0x38,0xE6, +0x9F,0x6B,0x81,0x7B,0xFB,0xD8,0x8F,0xA6,0x2C,0x37,0xA8,0x58,0xE7,0x8E,0x23,0x0C,0x98,0xFD,0x31,0x1A,0xE6,0x8C,0xB0,0xEA,0x0A,0x22,0xCF,0x9B,0x4E,0x71,0x46,0xC1, +0x12,0x5A,0x12,0x19,0x48,0x3C,0x10,0xEA,0x2C,0xBB,0x1E,0x47,0xE1,0x93,0x63,0xCE,0x1F,0x59,0xA1,0x54,0xE0,0x9A,0x63,0x94,0x39,0x34,0xD9,0xAA,0xD0,0x90,0x14,0xD4, +0x94,0xA1,0xD7,0xEC,0xDA,0x75,0x69,0x6D,0xCD,0x2B,0x3B,0xEF,0xB2,0x4F,0xD3,0x15,0x33,0x24,0xCB,0xCB,0x30,0x64,0x90,0x4A,0xA3,0xA8,0xA1,0x76,0x06,0xE4,0x5A,0xE8, +0xBF,0xE8,0x47,0xC8,0x0C,0x3F,0x0C,0xAD,0x5B,0xAC,0x66,0xB6,0xD0,0xD4,0x09,0x2B,0x8D,0x77,0x07,0x8D,0xE0,0x9F,0x0D,0xB4,0x96,0xD2,0x8B,0x95,0x71,0xC2,0x3D,0x70, +0x2A,0xEE,0x6D,0xFB,0xB1,0x93,0xF4,0x99,0x4C,0xE0,0xF8,0xE2,0x2D,0xF5,0x75,0xDF,0xBC,0x9F,0x6E,0x89,0x58,0x6A,0x5D,0x1E,0x5B,0x2E,0xA5,0xF6,0xD4,0xE2,0x0A,0x55, +0xA9,0x26,0xC9,0x58,0x06,0xDB,0xEE,0x76,0xFF,0x00,0x6C,0x35,0x57,0x0B,0x79,0xCD,0x54,0xAC,0xC3,0x44,0xCD,0x93,0xB2,0xB6,0x62,0x83,0x1F,0xE1,0x9E,0xA8,0xC5,0x8C, +0xB0,0x99,0x0D,0x94,0x6A,0x1A,0x80,0xB5,0xC5,0xC5,0xC7,0x3C,0x91,0xCE,0x37,0x66,0x08,0xBD,0x55,0x93,0x16,0x7C,0x6A,0x7D,0x31,0x99,0x30,0x22,0x2C,0x06,0xA1,0xD4, +0x43,0x12,0xD8,0x91,0xA9,0x44,0xA8,0x94,0x3A,0x48,0x4F,0x6B,0x14,0xD8,0x8B,0x73,0x8D,0xF4,0x68,0xD5,0x95,0xE5,0x77,0x4D,0x67,0x23,0xCD,0xCB,0xB3,0x59,0x05,0x7E, +0x2D,0x32,0x42,0x64,0x30,0xAB,0x6F,0xE5,0x65,0xC7,0x0A,0xB9,0xFF,0x00,0xA9,0x61,0xD8,0x76,0xC7,0x41,0x56,0xE6,0x50,0xE8,0x79,0x8C,0x23,0x02,0x30,0xC2,0x29,0x1A, +0xF7,0x5C,0x92,0x1C,0xA2,0xBB,0x9D,0xB2,0xCE,0x68,0x8A,0xD5,0xCF,0xC5,0xC8,0x76,0xCE,0xB5,0xDB,0x4A,0xB4,0xD8,0x8F,0xAD,0xFE,0x78,0xD3,0x42,0xCB,0xF2,0x61,0x32, +0x3E,0xF1,0x99,0x19,0xE7,0x96,0x4A,0x9C,0x6A,0x0A,0x74,0xB6,0x95,0xFF,0x00,0x98,0x93,0x75,0x2C,0xFB,0xA8,0xDB,0xDB,0x07,0x73,0xFF,0x00,0x50,0xA1,0xD0,0x19,0x82, +0xAA,0x96,0x56,0x53,0xD5,0x15,0xC5,0x6C,0xBD,0x25,0xB8,0x4A,0x65,0xB9,0x0A,0x20,0x92,0x02,0x96,0x91,0x75,0x00,0x6F,0x6D,0xC6,0xFC,0xE1,0x6F,0x29,0xE6,0xCA,0x7E, +0x6A,0xA8,0x38,0x88,0x90,0x5F,0xA7,0x3E,0x92,0x35,0x47,0x90,0xB1,0x73,0xE8,0x6C,0x3E,0x98,0x8B,0xAD,0xAF,0x51,0xB8,0xBB,0x2E,0x07,0x99,0x6B,0x4B,0x65,0x61,0x40, +0x07,0x99,0x65,0xC6,0x2C,0x22,0x21,0xFE,0x3B,0x8D,0x3A,0x81,0xB1,0x3B,0x1E,0x3D,0xB1,0xB9,0x88,0xB2,0x27,0x31,0xF0,0xAA,0x59,0x42,0x4A,0xAD,0x6D,0xF8,0xE0,0x6F, +0xE9,0x81,0x15,0x55,0x39,0x4C,0xCB,0xCE,0x2F,0xC5,0x42,0x1C,0x2D,0x92,0xCB,0x80,0xD8,0x8B,0x83,0xBE,0x26,0x74,0xFA,0x7C,0xDA,0xA6,0x52,0x61,0xD9,0xEE,0xA5,0xC7, +0x12,0x0A,0x0B,0xA9,0x57,0xF8,0x96,0xDA,0xFB,0xF7,0x3E,0xD8,0x51,0x17,0x3C,0xC2,0xBB,0x62,0x12,0x9F,0x4E,0x6E,0x24,0x45,0x46,0x8E,0xB5,0x2D,0x2A,0x1A,0x48,0x26, +0xF6,0xFF,0x00,0x7C,0x21,0x54,0xE2,0x8A,0x28,0x9B,0x21,0xE8,0x82,0x44,0x14,0x38,0x96,0x65,0x25,0x57,0x0B,0x6D,0xA5,0xDB,0xF1,0x13,0xE8,0x42,0x90,0x9B,0x1F,0x51, +0x63,0xCE,0x2C,0x99,0xAE,0x47,0x4B,0x7A,0x16,0xE2,0x11,0xA8,0xEA,0xDC,0xEE,0x7D,0xF7,0xF7,0xC2,0x85,0x5E,0x5B,0xCC,0x66,0x08,0xF2,0x5A,0x63,0xC7,0x0A,0x6D,0x68, +0x71,0x95,0x10,0x52,0xFB,0x76,0x1A,0x92,0xAB,0xF6,0x3C,0x0F,0x72,0x31,0xE3,0xE1,0x46,0x4F,0x4E,0xF1,0x1D,0x5B,0x1F,0x6F,0x22,0x26,0x47,0x31,0x64,0x53,0x93,0x67, +0x0F,0x9B,0x74,0xAC,0x8D,0x94,0x42,0x88,0x1F,0x22,0x40,0xFD,0xBD,0xF1,0x09,0x4E,0x87,0x62,0x21,0x2B,0x49,0x36,0x25,0x44,0x93,0x72,0x4E,0xD8,0x74,0x6A,0x8B,0x49, +0x72,0xA3,0x2A,0x2C,0x74,0x91,0x15,0xD4,0x09,0x91,0x17,0x7D,0x20,0xB6,0xBB,0x02,0x3D,0x8A,0x56,0x94,0xED,0xFD,0xF0,0x12,0x15,0x39,0xA9,0x15,0x69,0x14,0x87,0x02, +0x1C,0x5A,0xA4,0x06,0xCB,0xA8,0x5F,0x99,0x07,0x70,0x6D,0x7B,0x02,0x2E,0x7B,0xE1,0x67,0xA9,0x81,0x18,0x9C,0xFB,0xBA,0xE3,0xAC,0x11,0x4D,0x6D,0xC7,0xAA,0x4C,0xBA, +0xD4,0x85,0x30,0xD2,0x5B,0x70,0x14,0x5A,0xD7,0x57,0x3C,0xFD,0x07,0xE9,0x8C,0xC1,0x88,0x94,0x79,0x32,0x1B,0xA7,0x88,0xF2,0x59,0x5A,0x08,0xD7,0xE0,0x95,0x04,0x29, +0xC2,0x76,0x36,0xD5,0x6B,0xDA,0xC7,0x8B,0xE3,0x31,0xA5,0xA5,0x8C,0x2D,0x1F,0x31,0xC4,0xD3,0x53,0xCD,0xD1,0x63,0xB9,0x4C,0x11,0x28,0x71,0xE5,0x38,0x23,0x87,0x2C, +0xEC,0x68,0xC5,0x2B,0x21,0x24,0x5C,0xA0,0x33,0x71,0xF2,0x4A,0xC7,0x7C,0x1B,0xA9,0xC8,0x95,0xF7,0xF3,0x31,0xD7,0x47,0xA2,0xD3,0x12,0xCA,0x92,0xDC,0x78,0x71,0xE9, +0x49,0x95,0x2D,0xE0,0x94,0x0B,0x94,0xB0,0xAB,0x84,0x82,0x6E,0xA0,0x16,0x51,0x70,0x76,0xBD,0xB0,0x32,0x26,0x60,0xCB,0xF0,0xEA,0x54,0x58,0xD4,0x2A,0x7B,0xCA,0x75, +0xD6,0x94,0xDF,0xC6,0xBC,0xAD,0x2B,0x1B,0x9B,0x80,0xA1,0x65,0x8E,0x76,0x52,0x0B,0x46,0xC6,0xCA,0x07,0x0A,0x92,0xF3,0x35,0x6A,0xB1,0x4D,0x30,0x1B,0x3E,0x12,0x5E, +0x5E,0x85,0xC5,0x86,0x8F,0x05,0xB7,0x75,0x90,0x40,0x58,0x4D,0x82,0xCD,0xC9,0x17,0x55,0xCF,0xBE,0x28,0x7B,0xAA,0xA0,0xEE,0x39,0x3F,0x41,0x0C,0x0E,0x54,0xE3,0xAC, +0xB6,0xA9,0xB9,0xD6,0xA9,0x10,0xB7,0x4C,0x34,0xFC,0xBF,0x45,0x69,0xD1,0xA5,0x4E,0x3A,0x86,0x1B,0x94,0x14,0x2E,0x47,0xE0,0xC5,0x42,0x54,0x8D,0xC0,0xBA,0x56,0x95, +0x6C,0x39,0xC4,0x85,0xF5,0x72,0x64,0xAA,0xC9,0xA6,0x8C,0xCE,0xF9,0x7A,0x43,0xAA,0xD2,0xBA,0x74,0x12,0x12,0x12,0x92,0x36,0x51,0x2E,0x37,0x7F,0x4B,0xE9,0x38,0xA7, +0xE9,0x14,0x9C,0xC0,0x9A,0xDC,0x47,0xD5,0x47,0x53,0x4D,0x23,0x50,0x4B,0xD2,0x2E,0xCB,0x43,0x63,0x73,0xAD,0x44,0x27,0xEB,0x7C,0x3C,0x50,0xE8,0x99,0x39,0x99,0xB0, +0xBE,0xF7,0xCC,0x0C,0x2E,0x4A,0x92,0x24,0x18,0xB4,0xD7,0xCA,0x90,0x4E,0xAB,0x9D,0x6E,0x24,0x28,0x0E,0xC3,0x7D,0xBD,0xF0,0x4A,0x35,0x37,0x31,0x3B,0x07,0x18,0xFC, +0xBF,0xC4,0x18,0x25,0x80,0x3F,0x58,0xCE,0x9C,0xD5,0x0A,0xB4,0x99,0xF1,0x6A,0x01,0x53,0x54,0x8D,0xD3,0xAD,0xC6,0xD4,0xA3,0xC1,0xB1,0x48,0x6D,0xCD,0x3B,0xF1,0x72, +0x4E,0x03,0x25,0xAC,0xD0,0xE5,0x55,0xE8,0x99,0x5B,0xA7,0xF5,0xB7,0x18,0x4A,0x35,0x2A,0x5B,0x8A,0x6D,0x86,0x13,0xEA,0x7C,0x40,0x00,0x36,0xF4,0x02,0xFE,0xD8,0x78, +0xA3,0x56,0x32,0x7D,0x29,0x3E,0x3E,0x53,0xCA,0x71,0xDB,0x7D,0x63,0xFF,0x00,0x7D,0x21,0x03,0x59,0x03,0xB9,0x04,0x92,0x7D,0x2E,0x48,0xD8,0x76,0xC0,0x9A,0xD5,0x2F, +0x30,0x67,0x8A,0x7A,0xE6,0x4F,0x97,0x53,0x79,0x95,0x2F,0x5C,0x3A,0x74,0x0D,0x29,0x2E,0xA4,0x1B,0x15,0xA1,0x6E,0x03,0xE1,0xA6,0xE0,0x02,0xB1,0x64,0xEF,0x74,0x95, +0x11,0x6C,0x5A,0xD1,0xBB,0x2B,0x61,0x9B,0xAF,0xFB,0xD6,0x0C,0x31,0xDE,0x44,0x47,0x39,0x8A,0x89,0x2D,0x0E,0x65,0xFC,0xFC,0xC4,0x4A,0xCC,0x66,0xD2,0xA6,0xDC,0x62, +0x9C,0x1B,0x7A,0x65,0x3C,0x71,0xA9,0xA7,0x42,0x82,0x8A,0x41,0xFE,0x12,0x92,0x7E,0x63,0x04,0x27,0xE5,0x64,0xD1,0xDC,0x46,0x65,0xE9,0x71,0x83,0x9A,0x60,0x30,0xC0, +0x43,0x90,0x65,0x14,0x89,0xCC,0xA8,0x5B,0x60,0xAB,0x5C,0x5A,0xFB,0x83,0xB8,0xC2,0xAD,0x57,0x2A,0x57,0x29,0x75,0x70,0xBA,0x1A,0x93,0x4D,0xD6,0xEA,0x59,0xD5,0x45, +0x51,0x52,0xD0,0xE2,0xCD,0xB4,0xAA,0x6A,0xCF,0x88,0xFA,0x8D,0x8E,0xA5,0x6A,0x0D,0x27,0x72,0x80,0xA0,0x14,0x94,0x91,0xA1,0xD2,0xA0,0xBA,0xEA,0x6A,0x52,0xA5,0x39, +0x97,0xAA,0x21,0x45,0xCA,0x6D,0x46,0x94,0x0A,0x97,0x21,0xBB,0x59,0x2E,0xCA,0x42,0x80,0x57,0x9B,0xC8,0xAB,0x10,0x35,0x03,0x70,0x91,0x7C,0x12,0xFD,0x30,0x7F,0xAC, +0x7D,0x2D,0x29,0xCE,0x61,0xEC,0xB9,0x2A,0x9B,0x9E,0xB3,0x5A,0x69,0x75,0x15,0x49,0xA5,0xCE,0x7B,0xCA,0xF5,0x3E,0x48,0x28,0x00,0x01,0xFC,0x09,0xFA,0xF2,0x30,0x17, +0x24,0x56,0x2A,0x34,0xDE,0xA6,0xE6,0x5C,0x9F,0x9A,0x5E,0x66,0x99,0x2A,0x2C,0xAB,0xC3,0x68,0x82,0x94,0x3C,0xC1,0x2A,0x01,0x49,0x3F,0xC5,0x6B,0x27,0x7F,0xE6,0x18, +0x6F,0x15,0x0A,0xCC,0x87,0x23,0xBF,0x5F,0xA6,0xE5,0xDC,0xC2,0xEC,0x5B,0x2D,0xB9,0xE5,0x2E,0x42,0x70,0xAB,0xF8,0x4A,0xAE,0x9B,0x13,0xB7,0x00,0x8C,0x29,0xF5,0xB7, +0xEE,0x9E,0xA6,0xB5,0x4C,0x10,0xA9,0x7F,0x07,0x56,0x8A,0xAF,0x34,0xB4,0xAB,0x60,0x2D,0xF9,0x45,0xAF,0x71,0x7F,0x7C,0x24,0x9A,0x4A,0xD1,0x58,0x58,0x71,0x08,0x75, +0x79,0x65,0x03,0xA4,0x35,0x5C,0xCC,0x49,0x84,0xFA,0xA3,0xD3,0x9C,0x62,0x54,0xAB,0xE9,0x42,0x14,0xF0,0x64,0x73,0xCE,0xA5,0xD8,0x7E,0xF8,0x54,0x7A,0x74,0x9A,0xC4, +0xB8,0xF5,0x2F,0x87,0x4C,0x59,0x6C,0x38,0xE2,0x7C,0x22,0xE3,0x65,0x3B,0x58,0xEC,0xA0,0xAB,0x10,0x42,0x4E,0x3C,0x51,0xBA,0x6E,0xF3,0x2C,0xC2,0x97,0x5F,0x75,0xD9, +0x31,0x18,0xD3,0xE2,0x32,0xE2,0x89,0xBE,0xF6,0x25,0x6A,0x04,0x6E,0x2E,0x3C,0xB7,0x06,0xD8,0x3B,0x53,0x85,0x4D,0xA4,0xD7,0xCB,0xD2,0x63,0x35,0x1A,0x94,0xE8,0x52, +0x92,0x86,0x92,0x14,0xA5,0x92,0x09,0x01,0xB4,0x8B,0x69,0xE6,0xDB,0x90,0x08,0x22,0xDB,0x8D,0xA6,0x6A,0xC2,0x6C,0x2A,0x07,0xEB,0x05,0xAC,0xBC,0x05,0xDA,0xA7,0x3F, +0xDA,0x4A,0xA7,0xD4,0xD5,0x50,0x05,0x82,0xB7,0x19,0x75,0x09,0x5F,0x84,0x37,0x0B,0x4D,0xB6,0x71,0xAF,0xD9,0x2A,0x1F,0x2F,0x7C,0x25,0x57,0x49,0xF8,0xE7,0xDC,0x61, +0x4A,0xF1,0x1E,0x3A,0xB6,0xF5,0xB7,0xF7,0xBE,0x37,0xFD,0xE5,0xF0,0x53,0x19,0x79,0xB9,0xB2,0x1E,0x09,0x75,0x2B,0x69,0x4E,0xAB,0xCC,0x52,0x41,0x1E,0x55,0x77,0x4D, +0x80,0x04,0x1D,0xC1,0xDA,0xFC,0xE2,0x14,0xDB,0x2D,0xE5,0x3A,0x0A,0x0B,0x48,0x25,0x45,0x27,0xD0,0xF1,0xBE,0x25,0x5E,0xE0,0x00,0x33,0x24,0xB1,0xF9,0x0C,0xC3,0x39, +0x66,0xBA,0xFD,0x1E,0x42,0x22,0xBE,0xDA,0x9F,0x84,0xE4,0x84,0xA1,0x48,0x5A,0x42,0x92,0x93,0x71,0x62,0x2F,0xB5,0xC1,0x24,0x8F,0xDB,0x19,0x81,0x71,0x4C,0x87,0x58, +0x2A,0x69,0x2C,0x29,0x1A,0x89,0xD2,0xA4,0x02,0x9B,0x03,0xFB,0x7C,0xC6,0x33,0x07,0xAA,0xC2,0xAA,0x00,0x30,0xD5,0x07,0x03,0x28,0xBC,0x41,0xF1,0x7C,0x33,0x2A,0x14, +0xD3,0x29,0xA6,0xDB,0x62,0x2B,0xF2,0x4A,0xF5,0x6A,0x20,0x86,0x8A,0xC8,0x09,0x1B,0x9B,0x14,0x9D,0xBD,0xB0,0x31,0xBA,0xEA,0x60,0x26,0x64,0x4A,0x43,0x25,0xA5,0xB8, +0x4A,0x44,0xB5,0x1F,0xC6,0x25,0x24,0x90,0x52,0x46,0xCD,0xEC,0x0E,0xC9,0xDC,0x5E,0xDA,0x8E,0x23,0xC2,0x5A,0x15,0x40,0xAB,0x38,0xA1,0xA4,0xB7,0x19,0x41,0x00,0x76, +0x2B,0x21,0x1F,0xBE,0xB3,0x80,0xAC,0x2B,0xC2,0x8E,0x5C,0x00,0xDF,0x92,0x46,0x0B,0x5E,0x14,0x12,0x23,0x7B,0x87,0x5F,0x31,0xF9,0xD8,0x94,0xF9,0x68,0xA2,0x66,0xBA, +0x23,0x73,0x03,0x73,0x12,0xE3,0x32,0xE3,0x4D,0x7B,0xE2,0x14,0xCC,0x84,0x22,0xE7,0xCE,0x40,0xBA,0x56,0x95,0x25,0x42,0xE2,0xF7,0xD4,0x37,0xB6,0x23,0x51,0x61,0x49, +0x67,0x3C,0xB5,0x4E,0x92,0xA4,0xB6,0xEA,0x54,0x83,0xE0,0x6E,0xA5,0x2C,0xAF,0x52,0x92,0x3C,0xA3,0x91,0x7D,0xEF,0x6B,0x58,0xF7,0xC6,0xDC,0x82,0xE2,0x54,0x2A,0x14, +0x77,0x89,0x52,0x5F,0x52,0x5E,0x8C,0x00,0xB9,0xF1,0x90,0x85,0x80,0x00,0xF7,0x42,0x96,0x3F,0x4C,0x31,0x50,0x21,0x31,0x95,0x33,0x4B,0x2C,0xC5,0x51,0x95,0x99,0xE6, +0x21,0x05,0xB5,0x20,0x6A,0xFB,0xB9,0x05,0xBF,0x2E,0x93,0xDD,0xD5,0x5E,0xE0,0x8F,0xC8,0x0A,0x48,0x25,0x4A,0x05,0x26,0xA9,0x77,0xB3,0x37,0x69,0xEA,0x15,0x50,0x07, +0x93,0x2C,0x25,0x45,0x34,0x1A,0x2C,0x5A,0x42,0x62,0xAA,0x65,0x76,0x5A,0x94,0xD0,0x84,0x95,0x24,0x88,0xC8,0xDE,0xE5,0xC2,0x41,0x48,0x50,0x1C,0xA4,0x83,0x6E,0xF6, +0xFC,0xC8,0x25,0x5D,0xCD,0x11,0x32,0xE4,0x74,0x51,0xDC,0x71,0xE7,0xDF,0x4A,0x42,0x65,0xF8,0x0E,0x29,0x4A,0x75,0x64,0x6E,0x84,0xA9,0x64,0x90,0x8D,0xEC,0x5C,0x57, +0x99,0x5C,0x0B,0x80,0x40,0x4C,0x8D,0x99,0xA9,0xF9,0x5E,0x8F,0x29,0x30,0x58,0x32,0xE6,0xC1,0x49,0x42,0x9E,0x69,0xCD,0x69,0x6D,0x60,0x85,0x69,0xF5,0x21,0x37,0x49, +0x2A,0xE0,0xAA,0xD6,0xB8,0x49,0x25,0x46,0x81,0x99,0xA2,0xC9,0xCC,0x6D,0x89,0x0C,0x2D,0xE5,0xA9,0x2A,0x97,0x27,0x54,0x8F,0xC8,0x94,0xA4,0xAD,0x4A,0xB9,0x1B,0xB8, +0xAB,0x1B,0x5F,0xB9,0x04,0xF6,0xBB,0xFE,0xF1,0xAC,0x8A,0xEB,0x1C,0xFF,0x00,0x02,0x2E,0xA9,0x96,0x33,0xA1,0x9A,0xAA,0xD1,0x93,0x4B,0x65,0xAA,0xDB,0x51,0x98,0x7D, +0xD6,0x0A,0xFC,0x0D,0x23,0xF0,0x10,0xE7,0x94,0xDD,0x3C,0xDD,0x56,0xB1,0xBF,0x20,0x5A,0xF6,0x2A,0xD3,0x55,0x67,0xDA,0x6C,0x6A,0xD5,0x71,0x4B,0xA7,0xBF,0x1E,0x2B, +0x4D,0xF9,0x96,0x9D,0x00,0xA9,0x56,0x26,0xC4,0xDF,0xD0,0x92,0x7E,0x6A,0x27,0xBE,0x17,0xE5,0xD5,0x25,0x55,0xEB,0xCC,0x3B,0x21,0x87,0xD2,0xBF,0x1F,0xC7,0x90,0xAF, +0x88,0x48,0x42,0x02,0x49,0xB0,0xB6,0x9B,0x04,0x04,0xA4,0x80,0x38,0x48,0xF9,0xE3,0x4A,0xEA,0x54,0xA6,0xAB,0x2E,0xA6,0x43,0xCA,0x6C,0xC8,0x29,0x0E,0x2C,0x9F,0x15, +0x01,0x36,0xD8,0xDD,0x23,0x63,0xB0,0x57,0x7F,0x9E,0xD8,0x29,0xD7,0xB1,0x04,0x01,0xC7,0x99,0xE5,0x6A,0xC4,0x9E,0x78,0x13,0xE3,0xD1,0x17,0x12,0x10,0xF8,0xBA,0xAB, +0xA9,0x6F,0xF2,0x0D,0x3E,0x5D,0x7E,0xC0,0x0F,0x7C,0x65,0x29,0xB7,0xAA,0xF5,0x46,0xE2,0xC1,0x29,0x61,0x94,0x20,0xEB,0x79,0xD1,0xAB,0xC2,0x48,0xE5,0x77,0xE0,0x1F, +0x73,0xB0,0xE6,0xE3,0x91,0xB6,0x15,0x26,0x27,0xC6,0x97,0xD7,0x54,0x69,0x60,0x59,0xDF,0x11,0xE4,0xDA,0x3C,0x64,0x12,0x2C,0xB7,0x37,0xB5,0xCF,0x60,0x01,0x27,0x8B, +0x73,0x87,0xB8,0xCB,0xC8,0x28,0xA3,0xBB,0x0A,0x55,0x6A,0x3A,0x21,0x92,0x95,0xBA,0xD3,0x7A,0xCA,0x9E,0x50,0x02,0xCA,0x25,0x1F,0x98,0xFF,0x00,0x22,0x4D,0x93,0xEE, +0x45,0xF0,0x91,0x67,0xBC,0xED,0x06,0x17,0x39,0x1C,0x4D,0x71,0xAB,0xD4,0xF8,0xB0,0xA4,0x53,0xE8,0xCB,0xF8,0xC7,0x9B,0x57,0x83,0x22,0x59,0x46,0xBD,0x17,0x3B,0x2B, +0x41,0xFC,0xE9,0xF2,0x80,0x4E,0xF6,0xB6,0xC4,0x03,0x7C,0x29,0x66,0x56,0x0D,0x52,0x19,0x71,0x9F,0x19,0xC9,0x31,0xC1,0x71,0xC4,0xA5,0xC2,0xE1,0xB6,0xDA,0xB4,0xA9, +0x5B,0x91,0xB2,0x4D,0x8E,0xE0,0x13,0xCE,0x37,0x54,0xDC,0x8B,0x1E,0xB1,0x35,0xDC,0xBD,0x12,0x1A,0x9A,0x69,0xD4,0xB8,0x1C,0x4A,0xD6,0x54,0xA1,0x65,0x1B,0x28,0x6A, +0xDB,0x93,0xF5,0xC4,0xFA,0xC4,0xD4,0xC3,0xA5,0xC3,0xAB,0xB0,0xFD,0x39,0xA0,0xE2,0x9B,0x75,0x51,0xD9,0x8E,0xA2,0xA4,0x05,0x5C,0x1B,0xAA,0xF6,0x23,0xCC,0xAD,0xAD, +0xB1,0xB8,0xED,0x73,0x9B,0x14,0x10,0x54,0xF6,0x98,0xF6,0xF2,0xBC,0xF5,0x95,0x84,0xB9,0x2B,0x01,0x28,0xE1,0xA4,0xAF,0x5B,0x69,0x26,0xE4,0x5F,0x9F,0xA9,0xE7,0xB7, +0x38,0x2B,0x09,0xA5,0xD4,0xA7,0xA1,0x98,0xAE,0x34,0xB6,0xAC,0x94,0xB8,0x85,0x0B,0x85,0x6A,0xB0,0x1B,0xFC,0xCD,0x8F,0xCF,0x03,0xAA,0xC2,0x23,0x35,0xA7,0x23,0x36, +0x95,0x25,0x17,0xD6,0x84,0xFB,0x5E,0xE3,0xE9,0x6C,0x6A,0x81,0x29,0x48,0xCC,0x6B,0x69,0x95,0xAD,0xA4,0xBC,0x12,0x14,0xA6,0x94,0x53,0xB1,0x00,0x93,0x88,0x2C,0x06, +0x70,0xD1,0x4E,0xBC,0x18,0x69,0x55,0x07,0x29,0xB4,0xB6,0x60,0xC5,0x28,0x53,0x81,0x47,0xC6,0x55,0xEE,0x11,0xE6,0xB8,0x40,0x1F,0x5D,0xF1,0x98,0xFA,0xED,0x2A,0x2B, +0x0D,0x19,0x0C,0xA2,0x4C,0x98,0xE8,0x58,0xD2,0xEC,0x75,0x27,0x4E,0x90,0xBB,0x2B,0x5A,0x4E,0xE7,0xB7,0x1C,0x1F,0x98,0xC6,0x61,0xB0,0x84,0x46,0x12,0xD2,0x46,0x3A, +0x62,0x24,0x16,0xDC,0x46,0x57,0xAB,0xBC,0x91,0xA4,0x6B,0x69,0xB4,0x94,0x9B,0xED,0xA8,0x2B,0xFF,0x00,0xE3,0x10,0x58,0x4A,0x5F,0x86,0xDA,0x50,0xB6,0xD2,0x54,0x52, +0x9F,0x3A,0x82,0x47,0x04,0xF7,0xF9,0x60,0xB0,0xA5,0xCA,0x14,0xDA,0x8C,0x14,0xB6,0xE0,0x53,0xEE,0x38,0x96,0xD0,0xAF,0xE2,0x2D,0x8E,0x47,0xFF,0x00,0x65,0xDB,0xE9, +0x85,0xF6,0x5B,0xD3,0x97,0xD0,0x5C,0xD2,0x80,0xA7,0xC8,0x4B,0x8B,0xE1,0x21,0x3D,0xFE,0x97,0x38,0x68,0xA4,0x3D,0xA8,0x54,0x02,0x65,0x97,0xD3,0xA6,0x4C,0x1A,0x9B, +0x15,0x5A,0x84,0x74,0x69,0x6C,0x29,0xB4,0xB6,0xE0,0xF3,0x3E,0xEA,0x50,0x6C,0xD8,0x04,0xDE,0xDA,0x74,0xA9,0x44,0x76,0xF2,0xDC,0x15,0x0C,0x1A,0x8E,0xB6,0xA9,0xD9, +0x97,0x32,0xCA,0x6E,0x43,0x8E,0x56,0x6E,0xE1,0x71,0xC7,0x2C,0x4B,0x17,0x41,0xF2,0x92,0x3F,0xF9,0x4F,0x2A,0xFF,0x00,0x2F,0x17,0x2A,0x27,0x4A,0xED,0x1E,0xA8,0xB6, +0xAB,0x31,0x60,0xD3,0xF4,0x26,0x2C,0x56,0x97,0xF0,0xEA,0x58,0x1A,0xC5,0xD0,0xAD,0x4E,0x5C,0xEE,0x16,0xA2,0x01,0x24,0x6E,0x05,0x80,0x36,0x48,0xB6,0xF8,0x25,0x4D, +0xE6,0xAA,0xE5,0x92,0x4A,0x5C,0x94,0xF1,0xD0,0x08,0x48,0x3A,0x46,0x9B,0x5C,0xFB,0x9B,0x7D,0x3D,0xF0,0x55,0x60,0x89,0x85,0x9B,0x65,0xCA,0x29,0x82,0x6B,0xAD,0x4B, +0xFC,0x1A,0x43,0x0E,0xF8,0x3F,0x87,0xE3,0x2E,0xE9,0xDD,0xD5,0x28,0x70,0x0F,0x04,0x0B,0x11,0x71,0x89,0x54,0x7A,0x62,0x20,0xC4,0xA8,0xD3,0x9A,0x42,0x15,0x39,0xC6, +0x52,0xDA,0x94,0xB2,0x14,0x45,0x96,0x1C,0x2A,0x59,0xBD,0x80,0x05,0xBB,0x11,0xB7,0x27,0xDF,0x11,0x63,0x54,0xE7,0xAB,0x33,0x06,0x1D,0x6D,0x01,0x89,0x6E,0x05,0x38, +0xD4,0x96,0xFC,0x46,0x92,0x85,0x24,0x12,0xA4,0x8B,0xFF,0x00,0x2D,0xEE,0x0D,0xC6,0xFC,0x58,0xE1,0x89,0x11,0x22,0x54,0x21,0x98,0x34,0xDA,0x8A,0x69,0xEF,0x38,0x4A, +0xFC,0x27,0x14,0x4B,0x64,0x9B,0x6D,0xAB,0x73,0xC5,0xCD,0xCD,0xF6,0xE4,0x80,0x2F,0x8D,0x72,0x3E,0x50,0xAD,0x4E,0x17,0x89,0xF6,0x89,0x32,0x25,0x44,0xA9,0x11,0x13, +0xA4,0x32,0x09,0x71,0x6B,0xD9,0x2F,0x1F,0xC8,0x02,0x6F,0xD8,0xFB,0x9E,0xF7,0xEF,0xB4,0x28,0x2D,0xB1,0x26,0xB6,0x5E,0xA9,0x4D,0x54,0x68,0x2C,0x05,0x87,0x56,0x10, +0x15,0xA9,0x64,0x8E,0xC4,0x8B,0xF2,0x05,0x87,0x1C,0x8E,0x70,0x6A,0x15,0x36,0x99,0x4C,0x2D,0x53,0xE6,0x78,0x81,0xD5,0x24,0x10,0xE2,0x9B,0xD0,0x97,0x49,0x48,0x3E, +0x51,0x62,0x95,0xDB,0x8D,0xC8,0x1A,0x89,0x16,0x55,0xF6,0x92,0x9A,0x1F,0xDE,0xB2,0x54,0xF2,0xD0,0xEC,0xF6,0x14,0x0F,0xFE,0xA0,0x28,0x32,0x84,0x04,0xED,0x72,0x4D, +0xF4,0x01,0xBD,0x81,0x16,0x1B,0xDB,0x9C,0x6B,0xFE,0x50,0x14,0xF0,0x20,0xB6,0xA6,0xEC,0x31,0xE2,0x79,0x99,0x0A,0x8D,0x54,0xA6,0x44,0xA6,0x46,0xAB,0xB6,0xDC,0x6D, +0xDC,0x6D,0x0A,0xD4,0x94,0xE9,0xDA,0xEA,0x71,0x44,0x72,0x37,0xDF,0xB0,0x26,0xDB,0x62,0x0B,0x79,0x4E,0x11,0x75,0xB6,0xA1,0x09,0x4E,0x5D,0x25,0x2A,0x7E,0x3A,0x02, +0x93,0xB1,0x3C,0x90,0x4E,0xE0,0x5A,0xD6,0xD8,0xDF,0x13,0xE3,0xD4,0x29,0x10,0xDD,0x7D,0x34,0x48,0x69,0x9F,0x2D,0x95,0x80,0x5E,0x7E,0xFE,0x12,0x0F,0x1F,0x86,0x08, +0xF3,0xF2,0x45,0xD5,0x6F,0xFB,0x7B,0xE2,0x36,0x61,0x7D,0xF9,0x90,0x8C,0xD6,0xE4,0xAC,0xA4,0xD8,0x84,0x2D,0xC5,0x5D,0xAF,0x60,0x9B,0xEC,0x01,0xB5,0xB6,0xB6,0x3D, +0xC6,0xC2,0x14,0x4F,0x05,0x98,0x6F,0x6F,0xA0,0x91,0xD2,0xF3,0x59,0x7F,0x31,0x25,0xDF,0xBA,0x2A,0xB3,0xDB,0x2D,0x24,0xA4,0xAD,0x6D,0xB0,0xCA,0x94,0x94,0x94,0x85, +0x10,0x55,0xB9,0xBA,0x78,0xBF,0x7C,0x46,0x45,0x66,0xB5,0x29,0xA9,0x46,0x42,0x8B,0x6C,0xC8,0x0E,0x27,0xC1,0x00,0x69,0x21,0x44,0x9B,0x03,0x7B,0x5C,0x7B,0x5F,0x7F, +0x9E,0x31,0xDA,0x80,0xAD,0xD0,0x51,0x4D,0xAF,0xB5,0x05,0x94,0x02,0x52,0xDB,0x8A,0x6D,0x2B,0xF1,0x3D,0x48,0x57,0x20,0x82,0x06,0xC4,0x9E,0x4E,0xDC,0x61,0x7D,0x14, +0xFA,0x44,0x15,0xAC,0xC2,0x93,0x29,0xC7,0x52,0xA2,0x3F,0x00,0x15,0x21,0x5B,0x6D,0xB0,0xD8,0x63,0x76,0xA8,0x61,0x91,0xC1,0x84,0x75,0x5D,0xB8,0x1D,0x67,0x9A,0xF4, +0x4A,0x9C,0x79,0x11,0xA6,0xCF,0x78,0x3D,0x70,0x94,0x21,0xC1,0xDD,0x25,0x20,0xA4,0x7D,0x06,0x23,0x87,0x63,0xB5,0x50,0x89,0xE1,0xEA,0x0F,0x12,0x54,0xAB,0xFE,0x83, +0x1A,0x6A,0x0E,0x3E,0x56,0xEA,0x14,0xE4,0x85,0x2B,0x52,0x17,0xE1,0x29,0x67,0x48,0x23,0xFC,0xA3,0xF7,0xF6,0xBE,0x35,0x3A,0x7C,0x45,0xA5,0xF4,0x1B,0x29,0x2A,0xB7, +0xA7,0x06,0xDF,0xE9,0x88,0x3A,0xA5,0xC3,0x49,0xB7,0x28,0xDD,0xC4,0x77,0xCB,0x89,0x96,0xB8,0xCE,0x53,0xD4,0xC9,0x6D,0x32,0xA4,0x8F,0x02,0x51,0x48,0x3E,0x02,0xCA, +0x93,0xE7,0x49,0x3C,0x70,0x7E,0x78,0xCC,0x08,0xA7,0x38,0x2E,0xB5,0x29,0x4E,0x5B,0x48,0x70,0x29,0x27,0x70,0xA1,0x6F,0xDF,0x19,0x86,0xAA,0xBF,0x6A,0xED,0x30,0x4E, +0x76,0x31,0x13,0xFF,0xD9,}; + diff --git a/examples/160 x 128/TFT_FLASH_Jpeg/jpeg3.h b/examples/160 x 128/TFT_FLASH_Jpeg/jpeg3.h new file mode 100644 index 0000000..3bae8c4 --- /dev/null +++ b/examples/160 x 128/TFT_FLASH_Jpeg/jpeg3.h @@ -0,0 +1,284 @@ +// We need this header file to use FLASH as storage with PROGMEM directive +#include + +const uint8_t Baboon[] PROGMEM = { +0xFF,0xD8,0xFF,0xE0,0x00,0x10,0x4A,0x46,0x49,0x46,0x00,0x01,0x01,0x01,0x00,0x48,0x00,0x48,0x00,0x00,0xFF,0xDB,0x00,0x43,0x00,0x04,0x03,0x03,0x03,0x03,0x02,0x04, +0x03,0x03,0x03,0x04,0x04,0x04,0x05,0x06,0x0A,0x06,0x06,0x05,0x05,0x06,0x0C,0x08,0x09,0x07,0x0A,0x0E,0x0C,0x0F,0x0E,0x0E,0x0C,0x0D,0x0D,0x0F,0x11,0x16,0x13,0x0F, +0x10,0x15,0x11,0x0D,0x0D,0x13,0x1A,0x13,0x15,0x17,0x18,0x19,0x19,0x19,0x0F,0x12,0x1B,0x1D,0x1B,0x18,0x1D,0x16,0x18,0x19,0x18,0xFF,0xDB,0x00,0x43,0x01,0x04,0x04, +0x04,0x06,0x05,0x06,0x0B,0x06,0x06,0x0B,0x18,0x10,0x0D,0x10,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, +0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0xFF,0xC0, +0x00,0x11,0x08,0x00,0x78,0x00,0xA0,0x03,0x01,0x22,0x00,0x02,0x11,0x01,0x03,0x11,0x01,0xFF,0xC4,0x00,0x1C,0x00,0x00,0x03,0x00,0x03,0x01,0x01,0x01,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x06,0x07,0x00,0x03,0x04,0x01,0x02,0x08,0xFF,0xC4,0x00,0x38,0x10,0x00,0x02,0x01,0x03,0x03,0x02,0x04,0x05,0x03,0x03,0x03,0x04, +0x03,0x01,0x00,0x00,0x01,0x02,0x03,0x04,0x05,0x11,0x00,0x12,0x21,0x06,0x31,0x13,0x22,0x41,0x51,0x07,0x14,0x32,0x61,0x81,0x71,0x91,0xA1,0x15,0x23,0x42,0x52,0xD1, +0xF0,0x24,0xB1,0xC1,0xE1,0x33,0x43,0x72,0xF1,0xFF,0xC4,0x00,0x1B,0x01,0x00,0x02,0x03,0x01,0x01,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x06, +0x03,0x04,0x07,0x02,0x08,0x00,0xFF,0xC4,0x00,0x36,0x11,0x00,0x01,0x03,0x02,0x04,0x04,0x03,0x05,0x07,0x05,0x01,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x02,0x03,0x04, +0x11,0x05,0x12,0x21,0x41,0x06,0x22,0x31,0x61,0x13,0x51,0xB1,0x14,0x32,0x71,0x81,0xC1,0x15,0x23,0x33,0x34,0x62,0xA1,0xF0,0x07,0x35,0x42,0x52,0xB2,0xF1,0xFF,0xDA, +0x00,0x0C,0x03,0x01,0x00,0x02,0x11,0x03,0x11,0x00,0x3F,0x00,0xD7,0x7B,0xA8,0xB1,0xD3,0xDC,0xE6,0x80,0xDD,0x67,0xAC,0x86,0x80,0x4A,0xAA,0xE6,0x47,0x99,0x61,0x24, +0x83,0x20,0xD8,0xE3,0x95,0x8F,0x7B,0x71,0xCE,0xDD,0xA0,0x83,0xE5,0xE3,0xCA,0x6A,0xEA,0xCB,0x47,0x47,0xD4,0xC9,0x35,0x40,0xB9,0xAF,0xCD,0x63,0xC6,0x83,0xCF,0x2F, +0x8A,0x63,0x8C,0x15,0x55,0x2B,0xB8,0x12,0xF8,0x61,0x93,0x86,0xDF,0xF5,0x03,0x80,0x12,0x6D,0x77,0x39,0x2A,0x52,0x78,0x69,0x92,0x9A,0x69,0xAA,0xA4,0x66,0xA9,0xA6, +0xBA,0x4E,0x23,0x77,0x56,0x4F,0x33,0x00,0xBC,0xE7,0x70,0x00,0xAA,0xA8,0x20,0x92,0x1B,0x2A,0x48,0xD0,0xE8,0x6E,0x36,0x8B,0x55,0xDA,0x9E,0xB6,0xA6,0x2F,0x1B,0x60, +0xDB,0x07,0x89,0x29,0x2A,0xBC,0x28,0xDA,0x63,0xDC,0x5F,0x6E,0x0A,0x92,0x0E,0x33,0x86,0xC6,0x70,0x72,0x06,0x9E,0x30,0x1D,0xCE,0xAB,0xBE,0x23,0x97,0x45,0x4E,0xBA, +0x3C,0x30,0xD8,0x27,0xB6,0x49,0xD4,0x34,0x75,0x34,0xEB,0x20,0xF0,0xE0,0x60,0xB1,0x3C,0x65,0x70,0x42,0x63,0xB9,0x3B,0xD8,0x8C,0x2F,0xEC,0x07,0x60,0xB5,0x53,0x52, +0x4D,0x53,0x55,0x4B,0x05,0xBF,0xE6,0xEA,0xA4,0x95,0xAA,0x43,0x2D,0x3A,0xC7,0x4F,0xB8,0xAB,0x80,0x14,0x12,0x4F,0xF8,0x76,0x03,0x20,0xAA,0xFB,0x1D,0x26,0x4D,0xD5, +0xF5,0x52,0x4B,0x3B,0x3D,0x57,0xCD,0x06,0x98,0xD4,0x23,0x95,0x08,0x44,0x83,0x3E,0x51,0x8E,0x70,0x49,0x27,0x27,0x3E,0x98,0xEC,0x74,0x45,0xFA,0xF7,0xCB,0xE2,0x8F, +0x16,0x1A,0x69,0x0F,0xF7,0x0C,0x0A,0x21,0x04,0xA8,0x62,0x46,0x46,0x08,0x25,0xDB,0x76,0x70,0x39,0x23,0x1E,0xBA,0x32,0xC7,0xC6,0x05,0xEF,0x65,0x4C,0xD3,0xCA,0x08, +0x24,0x5D,0x77,0x5C,0x2E,0xB7,0x89,0x20,0x4B,0x33,0x40,0x90,0x9A,0xDD,0xF1,0x55,0x6D,0xA9,0x59,0xC0,0x50,0x08,0x58,0x9D,0x58,0xA8,0x49,0x54,0x01,0x83,0xD8,0x64, +0x60,0x83,0x9D,0x0F,0xB2,0x75,0x4D,0xCA,0xD9,0x4B,0x5B,0x0D,0xCE,0xB1,0x24,0x59,0x0A,0x24,0x8A,0xB7,0x05,0xA9,0xDD,0xB4,0x02,0x46,0x40,0x2C,0x0F,0x1D,0xF2,0x47, +0x38,0x39,0xD0,0xA1,0xD7,0x72,0xD4,0xDE,0x29,0xAB,0x2B,0x2A,0x4C,0x72,0xC6,0xC8,0xB2,0x41,0x32,0x89,0xE1,0x2A,0xA0,0xE1,0xB1,0xEB,0xB4,0x93,0x80,0xDB,0xBD,0xCF, +0xBE,0xB5,0x37,0x50,0xD1,0xCF,0x05,0x67,0xF4,0xD8,0xA8,0xAA,0x25,0x24,0x3C,0x53,0x55,0xC6,0x5A,0x58,0x8F,0x00,0x8C,0x9E,0x0E,0x42,0xE3,0x91,0x8E,0xFA,0xE2,0xEC, +0x6E,0xB7,0x53,0x06,0x3C,0xB7,0x28,0x1A,0x22,0x35,0xA3,0xA9,0xFA,0x9E,0x96,0x34,0x6A,0x34,0x7A,0x70,0xDB,0xD9,0xE6,0x02,0x41,0x1B,0xF8,0x71,0xA9,0xC3,0x46,0x32, +0x54,0xE1,0x78,0x19,0xEF,0xCF,0x6D,0x79,0x59,0x6D,0x5B,0x15,0x19,0x6A,0xD5,0x69,0xA7,0x9E,0x75,0x6A,0x86,0x88,0x33,0xD3,0x37,0x94,0x91,0xB3,0x00,0x15,0xC0,0x2A, +0xDB,0x70,0x38,0x03,0x1C,0x63,0x4B,0xB6,0xAE,0xA8,0xAD,0xB6,0x53,0xB4,0x54,0x75,0x12,0xC7,0x02,0x82,0x66,0x05,0x42,0x82,0x4E,0x46,0x71,0xDF,0x38,0xC7,0x9B,0x82, +0x31,0xC1,0xD3,0x75,0x83,0xAC,0xE1,0x9D,0x60,0xA0,0x5B,0x75,0x3C,0x71,0xC8,0x36,0x3C,0xAD,0x89,0x3C,0x68,0xF3,0xC2,0x36,0x76,0x90,0x3B,0x00,0x46,0x48,0x04,0x8F, +0x30,0x1A,0x92,0x33,0x1B,0xC7,0xBD,0x62,0xBB,0x96,0x39,0x19,0xFE,0x3C,0xA8,0x7B,0xDC,0xBA,0x95,0x7A,0x85,0x24,0xFE,0xDD,0x22,0xD7,0x2A,0x4E,0x4A,0x40,0x8A,0xA5, +0xB2,0xD8,0xDD,0x9C,0x9C,0xE4,0x95,0xC9,0xE7,0x1F,0xC0,0xF1,0x43,0x7B,0x85,0x4A,0x5D,0x2D,0x9E,0x34,0xF5,0x21,0xE1,0x81,0xD0,0x7F,0x6C,0xB2,0xB7,0x86,0x4A,0xB6, +0xE0,0x30,0x01,0x62,0x4E,0x3B,0x83,0xEC,0x4E,0xAB,0xD0,0xDD,0xBA,0x7A,0x82,0xCB,0x4D,0xFD,0x4A,0xD1,0x09,0xA7,0x55,0xD9,0x99,0xC2,0xC9,0xE2,0x12,0xAD,0x89,0x16, +0x56,0xC0,0x3D,0xF2,0x40,0x53,0xC9,0xE7,0xB3,0x69,0x76,0xF9,0x35,0xAE,0xE5,0x6E,0xA3,0x9A,0xC7,0x7D,0x8C,0xC1,0x0C,0x6E,0x22,0xB2,0xAC,0x67,0xC6,0x63,0x92,0xAF, +0x9C,0x36,0xEF,0x32,0x8D,0xC4,0x91,0xFF,0x00,0xD8,0x40,0xD4,0xB2,0x53,0x46,0x05,0xF3,0x2A,0xBE,0x3B,0x9C,0x40,0x0D,0x16,0x49,0x33,0xA7,0x55,0x86,0xA9,0xA6,0xBB, +0x5B,0x5F,0xC3,0x08,0x57,0xE5,0xDC,0x6D,0xDE,0xA1,0x83,0xF2,0x46,0x01,0x21,0xC0,0x3C,0x1F,0xFD,0xBB,0x45,0xD0,0xF7,0x7A,0x9A,0x48,0x20,0xA7,0xB5,0xCD,0x04,0xCF, +0x39,0x32,0xCF,0x51,0x29,0x94,0x49,0x8C,0x86,0x0C,0x30,0x30,0x17,0x2D,0xEA,0x32,0x40,0xE7,0x9C,0xEB,0xBE,0xDD,0xD7,0xD4,0x17,0x4A,0xCC,0xDB,0xA9,0x67,0x6F,0x95, +0x88,0xA5,0x42,0x08,0x84,0x8E,0xC7,0x39,0xC1,0xC8,0x52,0xCA,0x09,0x6E,0x40,0x27,0xEE,0x3D,0x4D,0x4B,0xF1,0x2E,0x8E,0xDB,0x3A,0x42,0x92,0xC5,0x02,0xC7,0x0F,0x85, +0xE1,0x4B,0x0B,0xAC,0x8A,0xCC,0xC1,0x89,0x71,0xC8,0x1F,0x48,0x5C,0x7B,0x93,0x9E,0x35,0x6A,0x18,0xE9,0xD9,0xAB,0xCD,0xD4,0x2F,0x7C,0xAE,0xF7,0x00,0x5A,0x9B,0xE1, +0xDD,0xDD,0xAC,0x12,0x6E,0xAF,0x9A,0xA6,0xF6,0xE0,0x42,0xAC,0x26,0x55,0xDC,0x99,0x05,0xB8,0x6C,0x79,0x41,0x2A,0x7B,0xFA,0x0C,0x73,0xA5,0xDB,0xA5,0xB2,0x83,0xA7, +0x20,0xA3,0x34,0x77,0xA9,0xA9,0x46,0x04,0x13,0xC9,0x31,0x3E,0x1C,0xA7,0x04,0x3E,0xCE,0xC6,0x46,0x19,0x1C,0x67,0xD8,0xF7,0x3C,0x83,0xEA,0x3F,0x88,0xD7,0x6B,0xC5, +0xD1,0x80,0xAB,0x48,0xD7,0x77,0x87,0x23,0x53,0xC8,0xE3,0xC7,0x50,0x77,0x15,0xC9,0x39,0x61,0xCF,0xAE,0x7D,0x7B,0x67,0x4B,0xEB,0x59,0x3D,0xD2,0xF1,0x05,0x4D,0x75, +0x75,0x58,0xA3,0x81,0x8C,0x94,0xF4,0xCF,0x33,0x4A,0x14,0x83,0x9E,0xDC,0x0C,0x7E,0x87,0xF1,0xAA,0x92,0xD5,0x41,0x7B,0x35,0x4B,0x1D,0x2C,0xA0,0x5D,0xFD,0x0A,0x75, +0xB5,0xF4,0xCC,0xB2,0xA5,0x25,0x55,0xBA,0x86,0xBC,0x3C,0x85,0xA4,0x46,0x48,0x96,0x9C,0x91,0xBB,0x24,0x3B,0x3B,0x26,0x7B,0x67,0x2A,0x4E,0x08,0xF2,0x8E,0xFA,0x35, +0x17,0x43,0xED,0xF9,0x8A,0xCC,0xD2,0x08,0xAB,0x1C,0x24,0x66,0xA2,0x41,0x52,0xCE,0x32,0xAC,0x57,0x0C,0x47,0x9C,0x6D,0x38,0x2B,0x9C,0xE3,0x19,0x00,0x1D,0x22,0xDC, +0x7A,0xD6,0xE1,0x4F,0x71,0x92,0xB1,0x2E,0x93,0xD3,0x54,0x30,0x0B,0xF3,0x30,0xA0,0xDE,0x17,0x77,0x76,0xF5,0x27,0x81,0xDC,0x9E,0xDE,0xDC,0x68,0x74,0xFD,0x5D,0x7F, +0xAC,0xB7,0x2C,0x30,0x5F,0x9D,0xE2,0x72,0x37,0xC7,0x24,0xAC,0xAD,0x27,0xF9,0x12,0x40,0x38,0xC6,0x0B,0x0C,0x72,0x39,0x38,0xC0,0xC6,0xBB,0x74,0xD0,0xAE,0x84,0x12, +0x95,0x5F,0xA7,0xE9,0xFA,0x7B,0x25,0xC3,0xC2,0x91,0xA9,0x8D,0x30,0x6D,0xF3,0x78,0x91,0x28,0x07,0xCC,0x02,0x90,0xE5,0x40,0xCE,0x19,0x8F,0x97,0x19,0xC8,0x39,0xEC, +0x4F,0x6C,0x75,0xF6,0x9A,0x69,0xEA,0x68,0xAD,0xB5,0xD2,0x4B,0x23,0xC4,0x76,0x4F,0xB4,0xCE,0x5C,0x32,0xA6,0x4A,0x6D,0x19,0x66,0x5E,0x49,0x1C,0x8F,0x38,0x27,0xDD, +0x62,0x50,0xF5,0x3D,0xE2,0xDB,0x50,0xB5,0x30,0xD6,0x05,0x66,0x52,0x9E,0x1C,0x2E,0xE5,0x37,0x6D,0x20,0x36,0xDC,0xB0,0xFF,0x00,0x23,0x8E,0x3F,0xF3,0xAE,0x79,0xBA, +0x82,0xE1,0x52,0xB4,0xE0,0xDC,0x6B,0x64,0x76,0x2D,0xB6,0x49,0xE7,0x07,0x1E,0x65,0x23,0x0F,0x95,0xC8,0xF2,0x8E,0x71,0xAE,0x4D,0x63,0x18,0x6C,0x54,0x9E,0xC8,0xFD, +0xD5,0xA2,0xE7,0x57,0x41,0x54,0x91,0xCB,0x43,0x3C,0x0F,0x56,0x68,0xBC,0x0F,0x12,0x58,0xBC,0x47,0xCA,0x96,0x61,0x84,0x20,0xED,0x66,0xDC,0x18,0xB0,0x53,0xCA,0x8C, +0xE8,0x05,0x3D,0xE6,0xA1,0x77,0xD9,0xE1,0xB7,0xD6,0x4C,0xB1,0x13,0x59,0x3C,0x15,0x05,0x25,0x97,0x69,0x05,0xB7,0x8F,0x44,0x63,0x80,0x3C,0xD9,0x65,0x51,0xE5,0x51, +0xE9,0x32,0xA8,0xBF,0x5E,0x63,0x94,0x4C,0xB1,0xCA,0x33,0x08,0x8C,0x24,0xF2,0x31,0x38,0x3C,0x0D,0x85,0xB9,0x0B,0x80,0x46,0x41,0xCF,0x38,0xCE,0x38,0xD7,0x64,0xF7, +0x68,0xA5,0x8A,0xAD,0x96,0xE9,0x4F,0x42,0x2B,0x8F,0x87,0x3D,0x34,0x28,0x1B,0x2A,0x1B,0x80,0x66,0x18,0x07,0x24,0x64,0xF6,0x04,0x85,0xCE,0x48,0x07,0x55,0xA5,0x78, +0x90,0xDC,0x15,0x24,0x51,0x16,0xF2,0x93,0x75,0xF1,0x5F,0x5F,0x2D,0x1F,0xCB,0xBC,0xF2,0x55,0xC6,0xCE,0x3C,0x1A,0xEA,0x7A,0x0F,0x0E,0x28,0xE3,0x52,0x1B,0x6A,0xC8, +0x81,0x08,0x04,0x12,0x30,0x49,0x24,0x80,0x41,0xD0,0x09,0x18,0xC2,0x8C,0x94,0x75,0x95,0x32,0xC0,0xD2,0x28,0x8D,0xA3,0x99,0x82,0x36,0xD0,0x46,0xC0,0x76,0x1C,0x38, +0x1F,0xB0,0xFD,0xB5,0x45,0x6E,0x83,0x5A,0x09,0xDE,0xB0,0xC3,0x3C,0x0A,0x5F,0xC6,0x4A,0xA6,0x8C,0x98,0xE1,0x44,0xC3,0x00,0xA5,0x94,0x97,0x72,0xCC,0x00,0xC1,0x23, +0xD4,0x92,0x17,0x05,0x4A,0x6E,0x96,0xAE,0x32,0xC0,0x4B,0xD6,0x3D,0x39,0x91,0xB6,0xC5,0x28,0xCB,0x46,0x53,0x20,0x92,0x3B,0xE4,0x88,0xC6,0x49,0x00,0xE3,0x1C,0x10, +0x0E,0xA2,0xF0,0xDA,0xAE,0x78,0x8D,0x69,0xB2,0x0C,0xCA,0xF1,0x45,0x12,0x4A,0x24,0x85,0x59,0xD8,0x89,0x10,0x3F,0x87,0xB0,0x13,0x9E,0x30,0x1B,0x19,0x04,0xF1,0xEF, +0xDB,0x41,0xE2,0xAE,0x86,0x1A,0x80,0x23,0xDF,0x3C,0x95,0x19,0x10,0xE0,0x64,0xB3,0x13,0x81,0xC1,0xCF,0x39,0xCF,0xA7,0x3D,0xFD,0x8E,0xAC,0x14,0x5D,0x05,0x51,0x51, +0x4C,0x95,0x14,0xB5,0x1E,0x03,0x10,0xB1,0xA4,0x81,0x77,0x3B,0x10,0x0E,0x33,0xB3,0x24,0x70,0x40,0xCF,0x7F,0xB0,0xCE,0xA7,0x37,0x39,0x2C,0x9D,0x35,0xF1,0x0E,0xD1, +0x3C,0xD0,0x09,0x2A,0xAD,0xB5,0xE9,0x2D,0x74,0x91,0xCE,0x65,0x49,0x55,0x65,0xDD,0x8D,0xAC,0xA3,0x04,0x05,0x00,0xE0,0xE3,0xD3,0xBF,0x3A,0xF8,0x31,0x97,0xB1,0x51, +0x32,0xA3,0xCD,0x74,0xD3,0x74,0x7F,0x59,0x31,0x3F,0x2F,0x6F,0x2C,0x44,0xAD,0x11,0xDD,0x50,0x89,0xB4,0x8E,0xE4,0x1D,0xD8,0x23,0x9E,0xFA,0xD0,0x3A,0x53,0xAA,0xA8, +0xE4,0x09,0x1D,0xBE,0x8E,0xA6,0x45,0x07,0x7A,0xC5,0x59,0x19,0x61,0x83,0xEA,0x32,0x7F,0xE7,0xBE,0xAD,0x76,0x55,0xA4,0xB8,0x40,0xD4,0x6B,0x54,0x4A,0x22,0x1A,0x76, +0x68,0x64,0x21,0x80,0x39,0x21,0xC1,0xF5,0x04,0x1C,0x82,0x3D,0x31,0xA5,0x7B,0x37,0xC3,0xCA,0xAB,0x34,0xC6,0x64,0xBA,0xC5,0x25,0x24,0x13,0xBC,0xE8,0x52,0x11,0xF3, +0x32,0x97,0x00,0x62,0x49,0x79,0x66,0x00,0x00,0x02,0xE4,0x00,0x46,0x40,0x19,0xD2,0x51,0xE2,0x57,0x47,0x2C,0xB0,0xCA,0x43,0x0B,0x4E,0x80,0xB4,0x9B,0xA7,0x9A,0x7C, +0x0D,0x92,0xC7,0x13,0xA3,0xBB,0xB3,0x0B,0x93,0x7E,0x8A,0x5D,0x5B,0x6F,0xEA,0x1A,0x3A,0x37,0x9A,0xBE,0xDF,0x24,0x11,0xF8,0x8A,0xA1,0x84,0x88,0xC0,0x16,0xC8,0x00, +0x90,0x71,0xDF,0x8E,0x75,0xF5,0x59,0x4F,0x5F,0x60,0x96,0x1F,0xEA,0x56,0x9A,0xBA,0x59,0xB7,0x17,0x1E,0x2A,0x1F,0x0E,0x45,0x18,0x3C,0x30,0x3C,0x8E,0x79,0xC7,0xDB, +0x54,0x1F,0x8A,0x17,0x5A,0x6B,0x4F,0xC2,0xEA,0x8B,0x5C,0x90,0x87,0xAC,0xAC,0x95,0x22,0x85,0x78,0x66,0x01,0x64,0x59,0x1D,0xBD,0x71,0x80,0x02,0xF3,0xEA,0xFF,0x00, +0xAE,0x97,0xBE,0x1D,0xDF,0xBF,0xAB,0x59,0xE2,0xB1,0x75,0x12,0x2B,0xC3,0x2C,0x6E,0xF4,0x92,0x4B,0x30,0xDB,0x24,0x60,0x3A,0xB2,0xE0,0x1D,0xD9,0x07,0x24,0x00,0x46, +0x46,0x78,0x3C,0x1D,0x36,0xD0,0xCE,0x67,0xA6,0x6C,0xD2,0x36,0xCE,0x29,0x67,0x13,0x8F,0xD9,0xAA,0x1F,0x14,0x66,0xED,0x04,0x04,0x0A,0x97,0xA9,0xEA,0x68,0x6A,0xD6, +0xE3,0x44,0xA1,0x9B,0x77,0xD0,0x39,0x52,0x08,0xE5,0x4F,0x39,0xC1,0xCF,0xBF,0x3A,0x21,0x6D,0x65,0xAB,0x62,0xB2,0xBF,0xC9,0x2D,0x4C,0x81,0xBC,0x9B,0x56,0x25,0xC9, +0x04,0xE7,0x9C,0x01,0xF8,0xF7,0xD0,0x2E,0xA8,0xB4,0x41,0x66,0xBC,0xCA,0x69,0x65,0x44,0xA6,0x8D,0xC9,0x10,0x6E,0x12,0x6E,0xC1,0x00,0x85,0x75,0xF2,0x92,0xB9,0x20, +0xFA,0x1C,0x64,0x1C,0x92,0x35,0x96,0xB9,0xE3,0xAA,0x70,0xAA,0xDB,0x3B,0x12,0x76,0x96,0xDC,0x7D,0x39,0xE3,0xD3,0xDB,0xFD,0xB5,0x24,0x92,0x00,0x2E,0x35,0x55,0x1E, +0xDC,0xCC,0xBA,0x71,0x4A,0x4B,0xAD,0x15,0x05,0x45,0x3D,0x0F,0x50,0xD1,0x2D,0x34,0x93,0x6C,0x30,0x43,0x21,0x22,0x43,0x9E,0x0E,0xD5,0x51,0xC7,0x23,0xFE,0x67,0x40, +0x2B,0x8B,0xC1,0xBE,0x9A,0x4D,0x95,0x55,0xD2,0x38,0x01,0x95,0xF0,0x72,0x71,0xEA,0x4F,0x7F,0x4E,0xFA,0xFA,0x68,0xA2,0x85,0xCC,0xAB,0x57,0x53,0x3F,0x98,0x23,0x4A, +0x06,0x10,0x9E,0x38,0xDA,0x7B,0x1F,0x4C,0x73,0xFA,0x9E,0x75,0xDB,0xD3,0x4A,0xE6,0xB2,0xA6,0x76,0xA5,0x9E,0x49,0x19,0x82,0x46,0x60,0x52,0x5D,0x58,0xE7,0x00,0x36, +0x3C,0xA4,0xE3,0xBF,0x07,0xEF,0xDF,0x51,0x19,0x5A,0x74,0xBE,0x8A,0x0A,0x76,0x3B,0x3E,0x8B,0xAA,0xC1,0xD0,0xD7,0xBA,0x9F,0x06,0xA2,0xEB,0x71,0x14,0x01,0xC9,0x6F, +0x0F,0xC3,0x26,0x40,0x80,0x67,0x71,0xC7,0x0A,0x7D,0x3F,0x23,0x5C,0xB7,0x7B,0x5D,0x34,0x3D,0x53,0x6F,0xB0,0x59,0x2E,0x15,0x97,0x1A,0xEA,0xA9,0x22,0x89,0xA6,0x66, +0xF2,0x44,0xCC,0x42,0x95,0xC0,0x00,0x60,0x13,0x8F,0x5C,0x01,0xA7,0x7E,0xA6,0xBA,0xD4,0xD9,0xAC,0x49,0x6C,0xA5,0x48,0x05,0xE9,0xD5,0xDA,0x48,0xC0,0x45,0x58,0x40, +0x50,0xC1,0x02,0xB1,0xE0,0x8D,0x87,0xB8,0xC9,0x24,0x77,0x27,0x51,0xC3,0x27,0x53,0x74,0xE7,0x50,0xDA,0x6F,0xB5,0x14,0xD2,0xC6,0x77,0xC5,0x72,0xA6,0x33,0xF1,0xE2, +0xA0,0x7E,0xFE,0x99,0x04,0x82,0xBF,0x8D,0x77,0x03,0x9B,0xA5,0xD7,0x6F,0x71,0x79,0x39,0x3A,0x0F,0x55,0x68,0x83,0xE1,0x75,0xA9,0x6D,0xE6,0xB2,0xB6,0xAA,0xAD,0xE3, +0xF3,0x48,0xCD,0xBC,0xA9,0xD8,0x3D,0x76,0x8F,0x53,0x9E,0xDE,0x9A,0xF6,0x9B,0xA3,0x3E,0x1F,0x5E,0x6B,0x96,0xD3,0x4D,0x67,0xBB,0x5B,0xEB,0x8C,0x02,0xA6,0x24,0xA9, +0x79,0x61,0xF1,0xA3,0xDC,0xC9,0xBC,0x67,0x07,0x19,0x56,0x19,0x1E,0xDD,0xF4,0xD7,0xD3,0x1D,0x49,0x65,0xBF,0x5A,0xD3,0x15,0xB1,0x4F,0x14,0x88,0x41,0x86,0x56,0x50, +0xC1,0x4F,0x74,0x6F,0x76,0x18,0xC1,0xFD,0x33,0xEA,0x35,0xBA,0x9F,0xA5,0x7A,0x77,0xA6,0x2B,0xDA,0xE9,0x41,0x43,0x2D,0x3C,0x8E,0x85,0x0C,0xB3,0x4C,0x4A,0xC7,0x1E, +0xED,0xD8,0x00,0x9E,0x06,0x49,0x20,0x01,0x8E,0x75,0x9B,0x56,0x62,0xD5,0x6C,0x9E,0x58,0xAA,0x5C,0xF1,0x25,0xF9,0x72,0xF4,0x4E,0x54,0x74,0x50,0x18,0xE3,0x92,0x30, +0xDC,0x84,0x6B,0x7E,0xAA,0x5F,0xD5,0x7F,0x0E,0xA2,0xB2,0x74,0xE5,0xCE,0xED,0x68,0xAE,0xAA,0x91,0x6D,0xF1,0xA4,0xE6,0x19,0x18,0x3E,0x22,0x2D,0xB1,0xBC,0xC4,0x67, +0x21,0x99,0x4F,0xDC,0x13,0xED,0xA0,0xDD,0x3D,0x64,0xBB,0xDD,0xEC,0x62,0xBB,0xC1,0xDF,0x33,0x12,0x40,0xA9,0x6F,0x3C,0xF1,0x95,0xC7,0x95,0x48,0x20,0x9D,0xA0,0xB0, +0x3D,0xB8,0xC6,0x4E,0x34,0xDB,0xF1,0x0B,0xAB,0x1A,0x2E,0x94,0xA8,0xE9,0x9A,0x43,0xF3,0x17,0x6B,0x9A,0xA4,0x0C,0xA1,0x40,0x68,0xA0,0x0E,0x1D,0x9D,0xF9,0xC2,0x96, +0x2A,0xB8,0x04,0xFD,0x2A,0x49,0xC6,0x46,0x9D,0xFE,0x15,0xD9,0xA4,0x83,0xA4,0xA1,0xF9,0xAD,0x95,0x71,0x02,0x4A,0x54,0x92,0xDC,0x36,0x02,0xE7,0x6B,0x73,0x82,0x59, +0x88,0x3F,0xE9,0x20,0xF1,0xC8,0xD6,0x85,0x86,0x36,0x63,0x49,0x1F,0xB4,0xEA,0xFB,0x6B,0xE7,0xFC,0xB2,0x53,0xAF,0x91,0x82,0x79,0x04,0x06,0xED,0xBE,0x9F,0x0D,0xFF, +0x00,0x7B,0xA8,0x8F,0xF4,0xFA,0xB8,0xA5,0x79,0xA2,0xA7,0x64,0x96,0x9B,0x1C,0x08,0xD6,0x4D,0x84,0x1E,0xE7,0xCB,0xB4,0x1E,0x06,0xBA,0x20,0xB1,0x4D,0x3D,0x1C,0x93, +0x7C,0xB4,0xA6,0x3C,0x17,0xDA,0x40,0xCE,0xCC,0x8E,0x49,0xC0,0x18,0xCE,0x07,0x7E,0x72,0x35,0x49,0xEA,0x8A,0x3A,0x39,0x6F,0x48,0xA9,0x54,0x0B,0x07,0x0A,0x11,0x51, +0x64,0x60,0x01,0x3E,0x5C,0x16,0xC6,0x7F,0xF2,0xC4,0xE4,0x7A,0xEA,0xB6,0xD0,0x1A,0xAA,0xC8,0x66,0x89,0xE9,0xD9,0x0A,0xB6,0xFA,0x88,0x14,0x87,0x47,0x78,0xF3,0xB5, +0x99,0x4E,0xE4,0x6C,0xAB,0xB6,0x76,0x9C,0x81,0xC0,0x6C,0x6D,0xD5,0xE6,0xC2,0x45,0x9C,0x55,0x13,0x31,0x20,0x10,0x9A,0x51,0x8D,0x4A,0x25,0x2C,0x31,0x5B,0xA6,0xA8, +0xA7,0x26,0x20,0x66,0xF1,0x1A,0x55,0x50,0xA4,0x91,0x04,0x20,0xB3,0x67,0x73,0x63,0x7B,0xB0,0xCF,0x72,0x3E,0x93,0xA0,0x75,0x94,0xB0,0xBD,0xED,0xE2,0x4F,0x0D,0x64, +0x0E,0xEB,0x3D,0x51,0x75,0x72,0x19,0x81,0xCA,0x84,0xC6,0xC7,0xF2,0xEF,0x00,0xA7,0x19,0x7F,0x5C,0xE7,0x4C,0x0F,0x4B,0x73,0xAA,0xA3,0x48,0x64,0xA3,0x7A,0x59,0x48, +0x67,0xA7,0x94,0x53,0xF8,0xA6,0x22,0xB8,0x21,0x50,0xC6,0xA1,0x37,0x9F,0x2E,0x79,0x20,0x67,0x1B,0xB8,0x20,0xF0,0x5D,0x45,0x33,0x55,0xC1,0x4C,0xF1,0x2A,0xD4,0x46, +0xA1,0xC5,0x34,0x48,0xEA,0x91,0x77,0x1B,0xA5,0x7C,0x20,0xC7,0x00,0xB1,0x2F,0x8C,0x9E,0xCB,0xBB,0x9E,0x29,0xE4,0x26,0xD7,0x5F,0x48,0x33,0x2F,0x6C,0x97,0x68,0xA0, +0x81,0x29,0xA4,0x2B,0xF3,0x32,0xC4,0x24,0x5F,0x96,0x5D,0xA2,0x30,0x41,0x6F,0xAB,0x23,0x70,0x07,0x8F,0x5C,0x71,0xC8,0xC6,0x90,0x7A,0xB3,0xE1,0xFD,0x15,0x7D,0xD6, +0x20,0xCF,0x54,0x0B,0x40,0x92,0x48,0xFC,0x3C,0xA4,0x61,0x95,0x0A,0x93,0xF5,0x60,0x2E,0x0A,0x82,0x73,0xB7,0xD0,0xF3,0xA6,0xE1,0x43,0xFD,0x4B,0xC5,0x9C,0x50,0xAA, +0xBC,0x6E,0xC8,0x23,0xE7,0x62,0xA2,0x16,0x50,0xB8,0x2A,0x36,0x85,0xE3,0x92,0x32,0x49,0x07,0xD3,0x95,0xBB,0xD5,0xF2,0xA6,0xCF,0x2A,0xFF,0x00,0x53,0xB5,0xC9,0x2C, +0x2A,0x87,0xC0,0x90,0x9C,0xE1,0xBB,0x9D,0xA4,0x92,0xDB,0x4A,0xEF,0x1C,0x76,0x19,0xE3,0x18,0xC4,0xD2,0x35,0xF7,0xCC,0x15,0x68,0x89,0x6B,0x8B,0x42,0x9C,0xD3,0x1E, +0xB5,0xE9,0x7A,0x97,0x82,0xCB,0x51,0x2D,0x74,0x50,0x86,0xDA,0x69,0xFC,0xF2,0x2A,0x8E,0x4A,0x94,0xFA,0xC0,0x19,0xFA,0x58,0x63,0xBE,0x3B,0x92,0x4B,0x4F,0xF1,0x2B, +0xE2,0xBC,0xB4,0xE9,0x6D,0x4B,0x4C,0xB1,0x4A,0xE3,0x00,0xAD,0x0B,0x46,0xC0,0x0E,0xE4,0x93,0x81,0xEB,0xEB,0xAA,0x35,0xA2,0x9F,0xA7,0xFA,0x9A,0x58,0x8D,0x65,0xE6, +0x96,0xE9,0x38,0x6C,0x29,0x7A,0x64,0x7A,0x90,0x30,0x48,0x05,0x93,0x6C,0x98,0x1C,0x02,0x48,0xE3,0xD4,0xF6,0x23,0xB2,0x8A,0x3A,0x18,0xE9,0x84,0x12,0x5D,0x69,0xC2, +0x86,0x4C,0x2C,0x35,0xB2,0xB9,0xE5,0xBE,0x9F,0x0D,0x08,0x60,0x41,0xF5,0x3C,0x65,0xBB,0xE7,0x00,0xD3,0x96,0x86,0x8E,0x67,0xF8,0x92,0xC6,0x0B,0xBC,0xEC,0x8A,0x41, +0x5D,0x5F,0x13,0x32,0x46,0xE3,0x95,0x41,0xA9,0xBA,0x77,0xAF,0x7A,0x9B,0xA9,0x23,0x6B,0xC5,0x9E,0xF0,0x16,0x67,0x08,0xF5,0x13,0xC1,0x96,0x8C,0x77,0xC2,0x23,0x15, +0xC9,0xC6,0x7C,0xB9,0x1D,0xF3,0xEE,0x75,0x5B,0x87,0xE1,0x77,0x52,0xD0,0x74,0xA4,0xBF,0x23,0x6C,0x92,0x48,0xDA,0x7F,0x11,0xA5,0x96,0xBA,0x96,0x04,0x0F,0x10,0x05, +0x37,0x21,0x95,0xB6,0xF0,0x64,0x04,0x6E,0x0D,0xE7,0x27,0x3E,0x5C,0x13,0x2D,0x5F,0x65,0xA0,0xAE,0x13,0x41,0x57,0x57,0x25,0x4A,0x12,0x04,0x10,0x49,0x3C,0xE7,0xCC, +0x00,0x60,0xF1,0x31,0x59,0x01,0xF4,0x38,0x66,0xCE,0x48,0xC1,0x1A,0x19,0x76,0xB9,0xAD,0xF1,0x26,0xA4,0x66,0x95,0x9A,0x38,0xB6,0xAB,0xCD,0x51,0x18,0xF0,0x13,0x0B, +0x80,0xC8,0x8C,0x3C,0x25,0x04,0x7D,0x20,0xEE,0x39,0x39,0xCF,0x3A,0xBE,0xEA,0x78,0xDE,0xD6,0xDB,0x65,0x0C,0x93,0xCC,0x09,0x24,0x75,0x4A,0x7F,0x11,0x22,0xE9,0x96, +0xA1,0x9E,0x18,0xEA,0x62,0x6B,0xC5,0x1D,0x52,0x53,0xCC,0x23,0x50,0x9E,0x2A,0x98,0xF3,0xBD,0x40,0x66,0xC9,0x04,0x05,0x3C,0x9C,0x76,0xEC,0x06,0x57,0x12,0xC9,0x57, +0x6D,0x7A,0x06,0x55,0x8D,0x65,0x75,0x0D,0xB1,0x09,0x20,0xE3,0xBF,0x88,0xAD,0xF4,0xE3,0x20,0xF0,0x4A,0xF2,0x08,0xF5,0xCF,0x54,0xB6,0x48,0x2E,0x7D,0x65,0x4B,0x69, +0xB3,0xC9,0x05,0x79,0x11,0xB3,0xCF,0x3D,0x4A,0x36,0xD3,0x20,0x52,0x4B,0x21,0x03,0x81,0x82,0x36,0xF1,0xFA,0xE9,0x9A,0xFF,0x00,0x43,0x9E,0xA2,0xA4,0x96,0x82,0xB9, +0xA9,0x27,0x86,0x94,0xA8,0x56,0x90,0xBA,0xAA,0x8E,0xF9,0xC9,0x3E,0x50,0x06,0x38,0x0B,0xD8,0x70,0x34,0x2E,0xB8,0x39,0x9E,0xE8,0xEA,0x8A,0x61,0x6C,0x64,0xCD,0xCA, +0xFD,0x57,0x1C,0x74,0x15,0xB5,0x5D,0x42,0x7F,0xAE,0xC3,0x0A,0x47,0x0C,0xCA,0xC2,0x3A,0x3C,0x78,0x58,0x6E,0x01,0xCC,0x5C,0x01,0x9F,0x6C,0xB1,0xF4,0xD1,0xDA,0x19, +0x07,0x4C,0xD7,0x4B,0x7D,0xA5,0xC4,0x54,0x33,0x23,0x49,0x0B,0x2C,0x58,0x0D,0x2F,0x87,0x85,0x00,0x16,0xCA,0xE1,0xD8,0x90,0x58,0x93,0xC2,0x96,0x00,0x91,0x83,0xD7, +0x6B,0x49,0xA3,0xB9,0xA7,0xCC,0x16,0x85,0x80,0x8A,0x45,0x10,0x41,0xB6,0x13,0x84,0x42,0x5B,0x1C,0x2E,0xEC,0x31,0xED,0xF9,0xD7,0x44,0x51,0x43,0x75,0x92,0xE5,0x61, +0xAD,0xA3,0xA6,0x35,0x54,0xA1,0x6A,0xE8,0xB2,0xA0,0x9D,0xA1,0x86,0x5B,0x01,0x40,0x00,0x67,0xBF,0x3F,0x50,0xC0,0x3A,0x8E,0x90,0x09,0x25,0x73,0x6F,0x65,0x26,0x25, +0x4D,0xEC,0xB4,0xC2,0x46,0x82,0x12,0x9F,0x4D,0x57,0xD7,0x5C,0x7E,0x20,0x47,0x78,0xF0,0xED,0x74,0x4F,0x4F,0x33,0x89,0x21,0xAF,0x55,0x98,0xD5,0x39,0x46,0x23,0x29, +0xC6,0x36,0xE3,0x82,0x30,0x77,0x1C,0x8E,0x70,0x47,0x37,0x56,0xFC,0x3D,0xB8,0x55,0xDD,0x67,0xA8,0x9A,0xEE,0x0C,0xA8,0xC4,0x9A,0x69,0x29,0x64,0x59,0x22,0x0E,0xCC, +0xC4,0xEC,0x76,0x67,0xF0,0xC3,0x1F,0xA8,0x06,0xE1,0xB3,0xEF,0xAD,0xD7,0xB9,0xA8,0xAD,0x1D,0x56,0x67,0x9A,0xEE,0x29,0x6E,0xD1,0x82,0x85,0xD1,0x15,0xA2,0xA9,0x50, +0x36,0x11,0x22,0xC6,0x09,0x0E,0x78,0xC9,0xDA,0x73,0x9C,0x80,0x4F,0x21,0xB2,0xD1,0x3F,0x57,0x5C,0x20,0x11,0x4B,0x64,0x9E,0x6A,0x2C,0xED,0x87,0xC7,0xA3,0x01,0x07, +0x38,0xF2,0x1C,0x73,0xC0,0xC8,0x2D,0x1E,0x7F,0x5F,0x43,0x31,0x51,0xB8,0xBB,0x30,0x69,0x3F,0x2B,0xA5,0x77,0x55,0x06,0x34,0x73,0x80,0x3B,0xA9,0x05,0x4F,0xC3,0xCB, +0xD5,0x1D,0x63,0x49,0x49,0xD5,0x7D,0x3D,0x4D,0xB4,0x0F,0xEF,0xA5,0xC9,0x50,0x9E,0x70,0x17,0x6F,0xD5,0xF7,0xE4,0x70,0x3D,0x74,0x4E,0x86,0xC9,0x79,0x6A,0x51,0x42, +0x7A,0xE2,0xAE,0xEF,0x5C,0xDE,0x58,0xA0,0xB4,0x45,0x34,0xEE,0x8F,0xEB,0x82,0xC8,0xAB,0x9F,0x62,0xAE,0x31,0xE8,0x0E,0x78,0xB3,0x55,0x5A,0x6B,0xCC,0x7E,0x35,0x5F, +0x49,0x48,0xF3,0x48,0x72,0x2A,0xA1,0x96,0x57,0x61,0x8E,0xE4,0x23,0x95,0x0A,0x7B,0x72,0x0E,0x7E,0xFF,0x00,0xE3,0xAE,0x5A,0xAE,0xBC,0xB3,0x74,0x85,0x03,0x96,0xB0, +0xD7,0xD2,0x4C,0x63,0x3E,0x2B,0xCD,0x28,0x3E,0x31,0x1C,0xF2,0xA4,0xBE,0xD1,0xBB,0x9E,0x09,0x27,0x3D,0xC6,0x06,0x64,0x92,0x8E,0xD6,0xCF,0x17,0x4E,0x86,0xDA,0xE9, +0xF1,0x0A,0xC4,0x15,0x46,0x56,0xE4,0x64,0xC3,0xE4,0x50,0xEB,0x07,0xC3,0xB8,0x6D,0x34,0x66,0x86,0xA6,0xDF,0x14,0x15,0x12,0xAE,0x6A,0x17,0xE6,0x1A,0xAA,0xA6,0xB3, +0x38,0x2C,0x66,0x75,0x3E,0x51,0x9C,0x1F,0x0C,0x00,0x09,0x3E,0x62,0x73,0xCB,0x07,0x52,0xF5,0x5D,0xBA,0xC5,0x68,0x6B,0x41,0xB8,0xAC,0x95,0x86,0x21,0x1B,0x0A,0x74, +0x6F,0xEC,0xB8,0xCB,0x18,0xF3,0xE6,0x04,0x79,0x93,0xEC,0x0E,0x73,0x8E,0xDA,0x4A,0x9B,0xE2,0x6D,0xE3,0xA9,0xE4,0xD9,0x43,0x6E,0xA3,0xB4,0x40,0xC0,0xA0,0x64,0x4F, +0x0D,0x8E,0x40,0xCE,0x64,0x3E,0x66,0x20,0x60,0xE7,0xDF,0x07,0xD4,0x63,0x75,0xBA,0xD9,0x4C,0x16,0x6A,0x4A,0x77,0x6A,0xA0,0x72,0x65,0xA8,0x01,0x67,0x62,0x70,0x77, +0x17,0x2F,0x91,0x8F,0xCF,0xFA,0x89,0xC6,0x30,0x23,0xC9,0x98,0x67,0x72,0xE4,0xE5,0x8B,0x96,0xF7,0x28,0x54,0x6B,0x70,0xB9,0x57,0x45,0x4D,0x00,0x67,0x3B,0xE3,0x64, +0x91,0x8C,0x52,0xB8,0x45,0xC7,0x31,0x0D,0xC0,0x6F,0xCE,0x01,0x50,0xC4,0x6D,0x2C,0x30,0x31,0xCB,0x5C,0x35,0x0F,0x3D,0xC9,0x96,0x96,0x19,0x69,0xE3,0x91,0x3C,0xF5, +0x54,0xC0,0x41,0x18,0x0A,0xCE,0xC0,0x07,0x74,0x07,0x63,0x13,0xB8,0xA0,0x7C,0x92,0x0F,0xD5,0xE5,0x24,0x76,0xE4,0x83,0xA6,0x5A,0x29,0x6A,0x48,0x9E,0x6D,0x8A,0xBF, +0x35,0x13,0x4B,0x13,0xB2,0x30,0x6C,0xA1,0x51,0x94,0x3E,0x62,0x43,0x73,0x82,0xFC,0x13,0xE6,0x1A,0x27,0xE0,0xDA,0x52,0x24,0xB7,0x46,0xD7,0x0A,0xAA,0xC9,0xB1,0x51, +0xF2,0x55,0xCA,0x8F,0x14,0x92,0x72,0xCC,0x81,0xCB,0x85,0xCE,0x1B,0x38,0x0A,0xF9,0xF4,0xDD,0x9D,0x46,0x0D,0x97,0x44,0x5D,0x37,0x5D,0xE1,0x6B,0x85,0x55,0x4A,0xDA, +0xEE,0x7B,0x9A,0x8A,0x2D,0xE9,0x49,0x1C,0xE7,0xCA,0x72,0x08,0x67,0x4C,0x09,0x17,0xB8,0xC1,0x25,0x4F,0xD5,0x85,0x39,0xC3,0x21,0xB5,0x75,0xCA,0x4B,0x94,0x90,0xDE, +0x10,0x4C,0xD4,0xE4,0x84,0x96,0xB6,0x1C,0x64,0xE4,0x05,0x27,0x01,0x91,0x1B,0x24,0xE0,0x30,0xDE,0x02,0xB0,0xC0,0x66,0xC3,0x1F,0xA1,0x58,0x7A,0x56,0xC9,0x24,0xD1, +0x4F,0x53,0x53,0x2A,0x92,0x0D,0xCE,0xA1,0x1C,0xB3,0x36,0x32,0x63,0xC0,0x38,0x43,0xE4,0xEE,0x14,0x8C,0x0E,0x5B,0x1A,0x58,0xEA,0x7A,0xC3,0x53,0xD0,0x68,0xF4,0x86, +0xDD,0xF3,0xC9,0x28,0xCA,0xC1,0x2B,0x1D,0xA5,0x41,0x7F,0x2A,0x83,0x92,0xC0,0x1E,0x58,0x81,0xB7,0x83,0x92,0x58,0x60,0x7D,0x29,0xC8,0x46,0xEA,0xDC,0xB1,0x5D,0xBC, +0xAB,0x4D,0xEA,0xB2,0x29,0x42,0x54,0xC1,0x50,0x95,0xD5,0x61,0x3C,0x08,0xA0,0x2E,0x55,0xA7,0x04,0x85,0x2F,0x92,0x00,0x5C,0x36,0x14,0x67,0x0A,0x7C,0xC4,0x0E,0x41, +0xD2,0xCF,0x51,0x5E,0xEE,0x66,0xDF,0x12,0x4F,0x14,0x4D,0x18,0xDC,0xAB,0x2C,0xB2,0x17,0x69,0x7B,0x8F,0x10,0x60,0x95,0xC6,0x39,0x07,0xFC,0xB9,0xF5,0xCE,0x7C,0x35, +0x93,0xC9,0xE0,0x3C,0xF1,0xD7,0x55,0xC4,0x19,0x59,0x69,0xD9,0x96,0x11,0x93,0xE6,0x01,0x37,0x29,0x39,0xC6,0x32,0x54,0xA9,0xC8,0x38,0xE3,0xBF,0xC3,0x2D,0xC2,0xFD, +0x78,0x79,0x6A,0x6D,0x29,0x25,0x1A,0x90,0x48,0x76,0x2C,0x11,0x43,0x6E,0x20,0x12,0x0A,0xB0,0x04,0x73,0x8C,0x96,0xFB,0xE3,0x45,0xA0,0x63,0xE5,0x20,0x37,0xE7,0xD9, +0x53,0x7B,0x9A,0xD1,0x77,0x21,0x76,0x3B,0x35,0x3F,0x50,0xDD,0xC5,0x1C,0x97,0x7A,0x6A,0x06,0x69,0x44,0x91,0x04,0x83,0x3B,0x89,0x25,0x47,0x9F,0x20,0xFA,0x0F,0x53, +0xC9,0xF4,0xD5,0x8E,0x93,0xE0,0x9D,0x34,0xB4,0x0B,0x15,0x4D,0xEE,0xB6,0x51,0x23,0x6F,0x78,0xC6,0xD6,0x5D,0xC4,0x92,0x72,0x08,0x3C,0x73,0xDB,0x4C,0x96,0x4E,0x8E, +0xE9,0xDA,0xBB,0x04,0x46,0x3A,0x08,0x5A,0x19,0x23,0x52,0x1D,0x00,0x05,0xB2,0x33,0xCE,0x3F,0x8F,0xC6,0x88,0xDB,0xED,0xB7,0x4E,0x99,0xB8,0xAB,0x2C,0xB2,0xD5,0xDA, +0xC9,0xC3,0xA4,0x8D,0x99,0x22,0x3E,0xE0,0xFA,0x8E,0xDC,0x1E,0x74,0xED,0x0E,0x19,0x05,0x3B,0x43,0x64,0x60,0x75,0xF7,0x09,0x5A,0xA3,0x17,0x92,0x77,0x16,0x53,0xB8, +0xB4,0x8D,0x8A,0x19,0x6B,0xF8,0x4B,0x64,0xB6,0x4B,0xB5,0xE9,0x28,0x6E,0x14,0xEC,0x30,0x60,0xAA,0xA6,0x07,0x9F,0x7E,0x30,0x07,0xED,0xA5,0xFF,0x00,0x88,0xBF,0x07, +0x3A,0x46,0x5E,0x91,0xAC,0xBA,0xD8,0xE0,0x96,0xCD,0x76,0xA5,0x0D,0x3C,0x69,0x11,0xDD,0x1B,0xFA,0xED,0xDA,0x72,0x00,0xFD,0x3F,0x9D,0x5D,0xA9,0x61,0x49,0xA9,0x96, +0x65,0xC1,0xE3,0xF7,0xFB,0xE9,0x6B,0xE2,0x0B,0x44,0x9D,0x22,0xF1,0x89,0x0F,0x8B,0x29,0x0A,0x84,0x80,0x40,0x6F,0x41,0xF6,0xF6,0xCE,0x7D,0x74,0x3F,0x11,0x74,0x11, +0x53,0xC8,0x72,0x8B,0x00,0xAE,0x61,0x71,0xD5,0x54,0x54,0xC7,0x19,0x71,0x25,0xC4,0x05,0x01,0xF8,0x57,0x4C,0x24,0xB6,0x9B,0xA3,0x15,0x96,0x72,0x05,0x2E,0xD5,0x8C, +0xA8,0xC9,0x55,0x50,0xC4,0x92,0x72,0x72,0xB2,0x03,0x8C,0x8C,0x77,0xC7,0x1A,0x5C,0xEB,0x49,0xCC,0x5F,0x11,0xA1,0x8E,0x2A,0x53,0x35,0x0D,0x0E,0x21,0x2B,0x36,0xEC, +0x92,0x14,0x83,0xB9,0xB3,0x9C,0x11,0xE7,0x38,0xE0,0xF2,0x39,0xCE,0x74,0xF5,0xF0,0x9E,0xDD,0x53,0x43,0x3D,0xCE,0x79,0x44,0x50,0x45,0x1E,0xE2,0x23,0x50,0xA0,0xF8, +0xC4,0xE0,0x13,0xCE,0x3B,0x16,0x00,0x63,0xBF,0xEB,0xA0,0x77,0x6A,0x38,0x2A,0x7A,0xEA,0xAD,0xB7,0x46,0xD1,0xB3,0x8C,0xAA,0x8C,0xAA,0x9C,0x72,0x31,0xDC,0x8F,0x4E, +0x3F,0x6D,0x67,0x75,0xB8,0x93,0x1C,0xF6,0x2D,0x87,0x0E,0xC0,0xA4,0x8E,0x27,0x17,0x6E,0x6C,0x9E,0x6F,0x73,0xC6,0xD7,0x68,0x61,0x8A,0x92,0x99,0x92,0x2A,0x38,0xA6, +0x77,0x11,0xE5,0x40,0x78,0xD7,0x03,0x38,0xE4,0x01,0xB8,0x69,0x7F,0xAC,0x64,0xAD,0xA1,0xB5,0xD0,0x75,0xAD,0xBA,0x74,0x8C,0xD3,0xCC,0x21,0x9D,0x90,0x67,0x6A,0x1C, +0x28,0x24,0xF3,0xDB,0x00,0xFE,0xAB,0xFB,0xB4,0xDE,0xA4,0x0C,0xB0,0xBC,0x64,0x7F,0x72,0x96,0x20,0x07,0x75,0x20,0x20,0xFE,0x38,0xE3,0x5C,0xB4,0x30,0x0B,0x8F,0x4D, +0xD5,0x51,0x54,0x2D,0x3C,0x90,0xC8,0x8D,0x1C,0xBC,0x15,0xCA,0x30,0x23,0x04,0x8E,0xFC,0x13,0xFF,0x00,0x3B,0x0A,0xA4,0xAD,0xCB,0x5B,0x25,0xF4,0x03,0xA7,0xF3,0xE6, +0x8E,0x62,0x78,0x01,0x76,0x1A,0xC0,0xDD,0x49,0x17,0x53,0x9F,0x83,0x54,0x35,0xF7,0x6F,0x8D,0x4F,0x5F,0x3D,0x1F,0xCC,0x42,0xC2,0x42,0xAD,0x34,0x40,0x84,0x50,0x78, +0xDA,0x4F,0x7E,0x71,0xDB,0xDC,0xEB,0xF5,0xDC,0x76,0x6A,0x68,0x69,0x44,0x95,0x92,0x6C,0xFF,0x00,0x47,0x23,0xF8,0x1F,0xEF,0xA8,0xFF,0x00,0xC0,0xDB,0x5C,0x54,0xB5, +0x97,0x1B,0x8C,0xE5,0xD4,0xC3,0x88,0x22,0x8C,0x67,0x08,0x32,0x4F,0xEB,0x92,0x78,0xD5,0x7A,0x7F,0x99,0xB8,0x56,0x78,0x95,0x38,0x58,0x97,0x88,0xE2,0x19,0xC6,0x39, +0xFB,0x73,0xDF,0x5A,0x8E,0x13,0x3C,0xBE,0xCE,0xD7,0x0D,0x33,0x6A,0xB0,0x9C,0x62,0x9D,0x82,0x72,0xC9,0x46,0xAD,0x00,0x5B,0x6B,0xA1,0x57,0x0A,0xD8,0x20,0x45,0x8A, +0x86,0x96,0x6A,0xC6,0x5E,0x32,0xB8,0x20,0x7D,0xF2,0x78,0xFD,0xB4,0x97,0x78,0x9F,0xA9,0xEA,0x62,0x96,0x3A,0x7B,0x45,0x10,0x89,0x97,0x85,0xA8,0x93,0x82,0x3D,0x38, +0xC7,0x3D,0xF1,0xCE,0xA8,0xD2,0x53,0xA8,0x52,0x15,0x30,0xA0,0x67,0xCA,0x00,0xC6,0x94,0x2E,0x57,0x58,0x45,0x70,0xA4,0xA0,0x8C,0xCD,0x29,0xFA,0xE5,0x3F,0x4A,0x77, +0x03,0xF3,0x9F,0x41,0xA3,0xA0,0x07,0x32,0xF2,0xC8,0x40,0xEC,0x97,0x64,0x25,0xB2,0xFD,0xDB,0x02,0x82,0x75,0x05,0xB6,0xF1,0x6C,0xA9,0x0B,0x17,0x48,0x51,0x5A,0xF0, +0xC7,0x15,0x74,0xAC,0x32,0x30,0x32,0xBC,0x71,0xEA,0x3B,0x9D,0x6F,0xA2,0xB8,0x2C,0x76,0xCF,0x0A,0x18,0x2A,0x55,0xE0,0x63,0x2C,0xD3,0xBC,0xA1,0xE4,0x52,0x41,0xF3, +0xE0,0x70,0x06,0x73,0x9C,0x10,0x71,0xB8,0x63,0x04,0x6A,0x8F,0x73,0xE9,0x5E,0xA2,0xBE,0x93,0x25,0x5D,0x44,0x68,0x4F,0xD0,0xC3,0x92,0xAA,0x0F,0x38,0x07,0xBF,0x03, +0xFF,0x00,0x7A,0x9E,0xDE,0xAC,0x17,0x7B,0x1D,0xFE,0x35,0x37,0x69,0x91,0x00,0xF2,0xB9,0x1E,0x64,0x3D,0xB1,0x95,0x00,0xE0,0x86,0x23,0x1F,0x7C,0xE9,0x72,0xBE,0x90, +0x86,0x99,0x43,0x4E,0x5E,0xFF,0x00,0xF8,0x98,0xA8,0xEB,0x23,0x90,0x06,0x12,0x2F,0xD9,0x6B,0x13,0xBA,0x74,0xDD,0x2B,0xA5,0x7C,0x55,0x4A,0xEC,0x29,0x61,0x59,0x55, +0x64,0x4D,0xC7,0x92,0x5C,0xE5,0x5B,0x73,0x01,0x81,0xC1,0x56,0xE3,0xB9,0x1A,0x6D,0xB7,0x25,0x65,0xD6,0xB4,0xC7,0x73,0x78,0xA9,0xEA,0xD9,0x97,0x14,0x70,0x39,0xFE, +0xE1,0x03,0x20,0x9D,0xC7,0x20,0x80,0x73,0xE6,0x56,0xC8,0x24,0x1D,0xE0,0x69,0x55,0x62,0xA5,0xA3,0xAE,0xA4,0xAC,0xA8,0xF0,0x1A,0x04,0x56,0x86,0xA2,0x36,0x94,0x46, +0x08,0xC1,0x0A,0xCD,0xB8,0xE4,0x90,0x31,0xEA,0x72,0x40,0xE4,0x1D,0x77,0x59,0x6B,0x6E,0x75,0x57,0x89,0x23,0xB8,0xA7,0xCB,0x96,0x57,0x4A,0x1A,0xF6,0xA7,0x99,0x43, +0x80,0x3D,0x06,0x38,0x21,0x72,0x33,0xC8,0xC8,0x20,0x83,0x8C,0x95,0xC9,0x1C,0x58,0xFB,0x0D,0x91,0x86,0x92,0xE1,0x70,0x17,0x7D,0xCD,0xA7,0xBE,0xD4,0xAD,0x04,0xB0, +0xD4,0x41,0x4F,0x2F,0x9B,0xC2,0x69,0x5D,0xEA,0x64,0x0C,0xA0,0x06,0x0B,0xE1,0x94,0x0B,0xCB,0x70,0xE4,0x70,0x7B,0x1E,0x32,0x2E,0xD5,0xD1,0xB4,0x36,0x35,0x92,0x4B, +0x97,0xC9,0x4E,0x69,0x89,0x8A,0x2A,0x55,0x8E,0x22,0x22,0x0C,0x46,0x09,0x63,0xB8,0xB7,0x00,0x7D,0x58,0x3D,0xB0,0x08,0xC6,0xA8,0x14,0x8F,0x5B,0x25,0xAC,0xD2,0xC8, +0x7E,0x7E,0xE9,0x29,0x76,0x1F,0x27,0xB2,0x18,0xC8,0x2D,0x92,0x4B,0x32,0x20,0x62,0x33,0xDC,0x79,0xBD,0xBD,0xB4,0x09,0xF7,0x52,0xD1,0xD4,0x57,0x67,0x36,0xEA,0x79, +0x9A,0x19,0x26,0x92,0xA1,0x5A,0x39,0x1B,0x1F,0x51,0x2A,0x9F,0xDC,0x39,0x3D,0x9B,0x76,0x33,0xD8,0x63,0x1A,0xA9,0x47,0x1B,0x45,0xDA,0xA7,0x96,0x52,0x5B,0xA2,0x9C, +0x5C,0xAE,0x54,0x31,0xD0,0xCD,0x1D,0x9A,0xDD,0x04,0x95,0x01,0x18,0x3E,0xC8,0x43,0x6C,0x00,0x64,0x33,0x15,0x5E,0x14,0x1D,0xC0,0xFA,0x8D,0xA3,0x81,0xC1,0xD5,0xCB, +0xA1,0xEC,0xB4,0x63,0xA2,0x28,0xA4,0x58,0xD6,0x48,0xA5,0x89,0x64,0x65,0x0B,0x8D,0xC5,0x86,0x4F,0x6E,0xFD,0xCF,0x3A,0x97,0x5C,0x28,0xA9,0x7A,0x86,0xB1,0x69,0x60, +0xA3,0xCC,0x6B,0x30,0xF9,0x6A,0x91,0x4E,0x62,0x8C,0xE3,0xEB,0x21,0x5F,0x07,0x3E,0x99,0x0A,0xBD,0xC6,0x3D,0xF5,0x5B,0xE8,0xB8,0xE6,0xA4,0xE9,0xD8,0x28,0x67,0x5D, +0xBF,0x2A,0x5A,0x9C,0xE1,0xB8,0x1B,0x58,0x80,0x7B,0xF3,0xC1,0x1C,0xFD,0xB4,0xF9,0x80,0x44,0x6C,0xFD,0x34,0x23,0x43,0xF0,0x4A,0xB8,0xEB,0xB9,0x1A,0x6F,0xAD,0xF5, +0x5D,0x16,0xDA,0x53,0x63,0xBC,0x49,0x45,0xE1,0x2A,0xD1,0xD4,0x83,0x2C,0x08,0x4F,0xD2,0xC0,0x0D,0xCB,0xCF,0x20,0xF1,0x91,0xFA,0x9D,0x34,0x88,0x96,0xA2,0x3D,0xB2, +0x00,0x08,0xEF,0xBC,0x1E,0x75,0xCD,0x72,0xA0,0x15,0x76,0xC5,0xA8,0x84,0xA8,0x9A,0x22,0x19,0x19,0x8E,0x36,0xB0,0xE7,0xFD,0xFF,0x00,0x7D,0x74,0xD0,0x54,0x3C,0xD4, +0x71,0x4B,0x14,0x67,0x90,0x32,0x33,0x9C,0x7B,0xFF,0x00,0x39,0xD1,0x82,0xE3,0xE1,0xE5,0x1B,0x20,0xF1,0xC5,0xF7,0x99,0x8E,0xFA,0xA2,0x16,0x84,0x48,0xE6,0x34,0x92, +0x1E,0x08,0x3B,0x7F,0xE7,0xDB,0x40,0x3E,0x26,0xA9,0x8F,0xA4,0x6A,0x30,0x4A,0x1D,0xCA,0xA7,0x6F,0xA7,0x3E,0x9F,0x7E,0x34,0x69,0x1C,0x09,0x44,0x91,0x90,0x08,0x20, +0x9C,0x9E,0x0F,0xBE,0x80,0x7C,0x59,0x6D,0xDD,0x1E,0xAC,0x5F,0x61,0x79,0x11,0x38,0x38,0xDD,0xDF,0x83,0xEF,0xFF,0x00,0xF7,0x4A,0xB8,0xE9,0x73,0x28,0xA7,0xEE,0xD2, +0x9E,0x78,0x59,0x82,0x5C,0x42,0x99,0xA3,0xFD,0xC2,0x9A,0x58,0xC8,0xA4,0xE8,0xA9,0x6A,0x0A,0x10,0xD5,0x12,0xBB,0x2B,0xA9,0xF3,0x36,0xD3,0x8F,0x7C,0xE3,0xCC,0x7B, +0xFE,0xBA,0x55,0x8E,0x9E,0x49,0x6F,0xA2,0x51,0xB5,0x18,0xF3,0x84,0x62,0x77,0x64,0xFA,0xFB,0x7E,0x34,0xE7,0x39,0x96,0x9E,0xC7,0x4D,0x09,0x70,0x57,0x69,0xDC,0x5B, +0x20,0x9C,0xE7,0x9F,0xF6,0xE4,0x77,0xD0,0xBB,0x64,0x54,0xD5,0x35,0x89,0x21,0x55,0x58,0x90,0x0C,0x2B,0x64,0xE7,0xD3,0x24,0x9F,0x5F,0xF6,0xD6,0x47,0x2C,0xAE,0xF1, +0x63,0x1D,0x82,0xF4,0x9B,0x29,0x1A,0x62,0x21,0xCD,0xD6,0xF7,0xD3,0xB2,0xEE,0xB9,0x3F,0x87,0x4B,0x09,0x97,0x71,0x65,0x8D,0x06,0xEC,0x9C,0x0C,0x0F,0xA7,0x9D,0x6E, +0xB6,0xCF,0x23,0xC6,0xF1,0xC4,0xAC,0x43,0x29,0x00,0x00,0x70,0x38,0xFE,0x47,0xBF,0xE3,0x5F,0x15,0x8F,0x0A,0xCE,0xCA,0x64,0x57,0x24,0x71,0x84,0x00,0x63,0xDB,0x8F, +0xD3,0x5E,0xDA,0x32,0xCE,0xE8,0xB2,0x76,0xF3,0x02,0x07,0xAE,0x38,0xE0,0xEA,0x20,0xE7,0x3A,0xBC,0xEB,0xD5,0x59,0x74,0x2C,0xF6,0x21,0xCB,0x6B,0x26,0x5F,0x86,0xD4, +0xA9,0x49,0x2D,0xE2,0x98,0xB8,0x2C,0xD5,0x42,0x42,0xA1,0x89,0xE4,0xAE,0xEE,0xDE,0x83,0x2C,0x75,0x4E,0x45,0x2E,0xA8,0x13,0xCC,0x7B,0x13,0xA4,0x0E,0x88,0x81,0x7F, +0xA9,0xDC,0xE6,0x52,0xA1,0xA5,0x92,0x35,0xF2,0xAE,0x30,0x04,0x63,0x8E,0x47,0xDF,0x54,0x70,0x9F,0x29,0x42,0xC1,0x80,0x1C,0x70,0xD9,0xF7,0xED,0xAD,0x97,0x08,0x70, +0x6D,0x04,0x36,0x1B,0x7D,0x4A,0xF3,0x0F,0x13,0x46,0x3E,0xD6,0xA8,0x03,0xA0,0x3F,0x40,0x80,0xDF,0x1E,0x5A,0x99,0xDA,0x8A,0x90,0x98,0xE3,0xC6,0x1E,0x50,0x39,0xFD, +0x01,0xF4,0x3F,0x7D,0x0A,0x16,0xD8,0x29,0x99,0x7C,0x30,0x02,0x72,0xCD,0xC0,0xE7,0xF5,0xD1,0xC5,0x81,0x15,0x0C,0xA1,0x42,0x96,0xF4,0x27,0xB6,0x87,0xD6,0x48,0xD1, +0x53,0x33,0xC6,0x3C,0xF2,0x70,0xAA,0x07,0xAF,0xDF,0x4D,0x14,0x61,0xA0,0x5D,0xC9,0x1E,0xBD,0x85,0xE6,0xE1,0x2F,0xDC,0xAF,0x20,0x5C,0xD6,0xD7,0x40,0x86,0x4A,0x92, +0x32,0x5C,0xF0,0xA9,0xFB,0x1E,0xDA,0x99,0x75,0x55,0xA6,0xE5,0x49,0x71,0x82,0xAE,0xB9,0xEA,0xE7,0x59,0x64,0x29,0x2B,0x41,0x85,0x2B,0xFF,0x00,0xE4,0xFA,0xFE,0x7B, +0xEA,0xB7,0x68,0xB3,0xC7,0x45,0xBA,0x66,0x07,0xC5,0x72,0x1D,0x8B,0x71,0xB9,0xB1,0xDC,0x11,0xF8,0xD0,0x5E,0xBA,0x31,0x7F,0x4E,0x12,0xCC,0x8B,0x23,0x6E,0x5D,0x9C, +0x9C,0xA9,0xCF,0xDB,0xF4,0xD5,0x7C,0x42,0x95,0xD3,0xB0,0xB9,0xEE,0xE8,0xA6,0xA3,0xA9,0x6D,0x3B,0xD8,0xC6,0xB6,0xE4,0xF5,0x48,0x49,0xF2,0x37,0x47,0x82,0x39,0xC2, +0x57,0xC9,0x00,0xC8,0x92,0x7D,0xE6,0x44,0x6C,0x71,0x95,0x3D,0xCF,0x39,0x27,0x96,0x1F,0xF7,0xE9,0x5B,0x2F,0xF5,0x0A,0x19,0x2D,0x94,0x15,0x94,0x53,0x52,0x89,0x9B, +0x73,0x54,0xBB,0x81,0x14,0xA3,0x03,0x6E,0x58,0x13,0x81,0xDC,0x0F,0x30,0x1C,0x67,0x00,0xE3,0x5D,0x14,0xF4,0xD4,0x94,0xF6,0xF3,0x3D,0x2C,0x93,0x46,0xC5,0xD9,0xDA, +0xA6,0x9A,0x65,0x2E,0xB9,0xCE,0x42,0xED,0x1B,0x87,0x1C,0x9C,0x06,0x3D,0xF9,0x1D,0xC1,0x7B,0x65,0x05,0xBD,0x28,0xCD,0x65,0x25,0x43,0x90,0x63,0xCB,0xFF,0x00,0xD3, +0xF9,0xA4,0x00,0x0C,0xE7,0x07,0x7E,0xEE,0x46,0x49,0xEC,0x39,0xE4,0x69,0x46,0xA2,0x22,0x4D,0xC8,0xE8,0x99,0xE3,0x90,0xE8,0xD0,0x50,0x4A,0x98,0xA1,0x8E,0xDD,0x34, +0x54,0x7B,0x29,0x18,0x29,0x77,0x79,0x5E,0x27,0x92,0x50,0xBF,0x57,0x91,0x77,0x7A,0xE7,0x2D,0x81,0x8C,0xFA,0x71,0x94,0x6B,0xB5,0xD8,0xCF,0x47,0x34,0x76,0x19,0x17, +0xC6,0x64,0x06,0xA1,0x26,0x8B,0x6D,0x4C,0x98,0x23,0x84,0xF1,0x15,0xB6,0x8F,0xCF,0x1C,0x67,0x19,0x18,0x63,0xA8,0xB2,0xC1,0x5F,0x70,0x85,0x2B,0xAD,0x77,0x18,0xA1, +0x9E,0x47,0x8A,0x57,0xA6,0x11,0xCF,0x51,0x54,0xC9,0xE6,0xF3,0x6D,0x2A,0x23,0x89,0x77,0x73,0x92,0x46,0x00,0xED,0xDF,0x42,0xFA,0xCE,0xF4,0x6C,0x14,0xD3,0x52,0xD3, +0xD0,0xD4,0x3C,0x0E,0x86,0x24,0x96,0xA2,0x36,0x10,0xC5,0xC7,0x28,0x36,0x93,0x96,0x5F,0x31,0x19,0x00,0x0C,0xF6,0x23,0x07,0x40,0xA9,0xDA,0x1A,0x2E,0x3D,0xE4,0x62, +0x40,0x36,0x2B,0xEF,0xE1,0x6D,0x1B,0x1E,0xB5,0x28,0xEF,0x51,0x52,0x5E,0x2C,0x48,0x2A,0x18,0x9F,0x0D,0x81,0xCF,0x97,0x24,0x8C,0x70,0x73,0x9C,0x83,0x9F,0xB6,0x35, +0x64,0xA0,0x89,0xA8,0x7A,0xA2,0xE1,0x4B,0x2B,0xB1,0xF1,0x02,0xD4,0xC6,0x87,0xB7,0x6D,0xAC,0x3F,0x18,0x1F,0xB8,0xD4,0xBF,0xE0,0x55,0x04,0x69,0x9A,0xE4,0xA8,0x57, +0xF9,0x96,0x96,0x5F,0x11,0x54,0x2A,0xED,0xC8,0x03,0xB2,0xA9,0x3C,0x1E,0xF8,0xD5,0x76,0xF3,0x28,0xA7,0xBB,0xDB,0x6A,0xCA,0xB1,0x52,0x5A,0x12,0x71,0xC7,0x99,0x4F, +0x7F,0xB6,0x40,0xD6,0x9D,0x86,0xC5,0xE1,0xD3,0xC6,0x0F,0x95,0xD2,0x2E,0x31,0x28,0x92,0xA1,0xE0,0x74,0x16,0x47,0x20,0x0A,0xD1,0x18,0x5D,0x01,0x1E,0xFD,0xB5,0xA6, +0xDB,0x1A,0xD3,0x55,0xCD,0x12,0xB3,0x3A,0xE4,0xB1,0xDC,0x31,0xCE,0xBC,0xA5,0x3B,0x95,0x77,0x72,0xA3,0xD4,0x76,0xD7,0x44,0x71,0x47,0x1D,0x53,0x18,0x43,0x79,0xBC, +0xCA,0x71,0xFB,0x8D,0x71,0x33,0x72,0xB9,0x7D,0x4A,0xEC,0xCD,0x0B,0xEB,0x63,0xF8,0x9B,0x19,0x8F,0x6E,0xC3,0xD7,0x9C,0x69,0x6B,0xE2,0x10,0x92,0xA3,0xA4,0x69,0x15, +0x8B,0x1F,0xFA,0x95,0x04,0x1F,0x5C,0x06,0xED,0xF8,0xC6,0x9B,0xAA,0x95,0x96,0x54,0x77,0x88,0x47,0x91,0x92,0x01,0xC9,0xD2,0x9F,0x5C,0xA7,0x8F,0x61,0xA6,0x88,0x1D, +0xA4,0xD5,0x2E,0x31,0xCF,0xF8,0x9E,0xDA,0x5E,0xE2,0x53,0x7C,0x36,0x57,0x76,0x4E,0x1C,0x10,0x2D,0x8D,0x53,0xB4,0xEC,0xEF,0x44,0x81,0x73,0x96,0x46,0xB4,0x87,0x20, +0x79,0x70,0x80,0x16,0xE4,0x64,0x1C,0x7A,0x76,0xD7,0x15,0x95,0x96,0x69,0xF6,0xCC,0xE0,0x20,0x53,0xE6,0x0B,0xFF,0x00,0x6F,0xCE,0xBC,0xBC,0x02,0x19,0x57,0x79,0xE7, +0x92,0xA7,0xB0,0x3D,0xB5,0xED,0x92,0x97,0xC6,0x60,0x84,0xAE,0xD7,0x39,0x3B,0x71,0xC0,0x3F,0x9F,0xE3,0x58,0xC0,0x61,0x9A,0xAC,0x58,0x74,0xB2,0xF5,0x4B,0xC0,0x8A, +0x02,0x49,0xF9,0xAE,0xCB,0x9C,0x26,0x16,0x8D,0xD8,0x1D,0xC4,0x03,0x8C,0x93,0xFF,0x00,0x7F,0xCE,0xB9,0xED,0x72,0x95,0xAB,0x2B,0x18,0x0A,0x5C,0x63,0x03,0xD7,0x44, +0x2E,0x54,0xF4,0xFF,0x00,0x2E,0xAC,0x67,0xDA,0xE0,0x7D,0x27,0x07,0x70,0x3E,0xFC,0xF0,0x3F,0x9D,0x09,0xA2,0x8C,0x7C,0xDA,0x28,0xDC,0x39,0xDB,0x95,0xD7,0x15,0x99, +0xA2,0xAD,0x6D,0x97,0x14,0xE4,0x4F,0x4E,0x43,0x95,0x2B,0xE1,0xA3,0x3D,0x54,0xF7,0x49,0x66,0x39,0xC4,0xC0,0x0F,0x36,0x4F,0xD0,0xA3,0x27,0x3F,0xF8,0xD3,0xF5,0xC5, +0xC4,0xD5,0x42,0x91,0x58,0x08,0xE1,0xC1,0x7C,0x0E,0xED,0xED,0xA4,0x8F,0x86,0xC4,0x41,0x43,0x77,0x9D,0x94,0x85,0x5A,0xA2,0x0E,0x46,0x3B,0x46,0x9F,0xED,0xA6,0xB5, +0x2F,0x2C,0x01,0xD9,0x00,0x79,0x5B,0x7B,0x7A,0x91,0x93,0x9E,0x3F,0x7D,0x6E,0x18,0x53,0x2F,0x04,0x39,0xB6,0x68,0x5E,0x5B,0xE2,0xA7,0xB4,0x62,0x95,0x39,0x77,0x71, +0xFA,0x2C,0x63,0xE2,0xB1,0x76,0xC9,0x89,0x73,0xC6,0x7B,0xE8,0x5A,0xC4,0xF5,0x55,0xC6,0xA1,0xC1,0x08,0xBC,0x2F,0xE3,0xBF,0xE4,0xF1,0xA2,0x77,0x19,0x63,0x8A,0x95, +0x62,0xDC,0x79,0x1E,0x61,0x9C,0xF0,0x75,0xAA,0x9E,0x9C,0xC7,0x49,0x81,0xB0,0x67,0x3C,0x8F,0x41,0xED,0x8D,0x1C,0x8E,0x4C,0xA2,0xC9,0x49,0xF1,0x03,0xCC,0x57,0x1D, +0x49,0x0D,0xE6,0x75,0x18,0x51,0xC6,0x32,0x02,0xE7,0xF4,0xD2,0xBD,0xCE,0x81,0x6E,0xB3,0x96,0x96,0x32,0xE9,0x1E,0x55,0x19,0x8F,0x73,0x8E,0x49,0xC7,0x38,0xE7,0x1D, +0xF4,0xC1,0x74,0xA8,0x5A,0x6B,0x64,0xD3,0xC8,0xDB,0xD5,0x0B,0x65,0x40,0x27,0x9F,0x4F,0xE7,0x5C,0x74,0xAB,0x22,0x5B,0xD5,0xD8,0x06,0x0E,0xBB,0x98,0xFF,0x00,0x3C, +0x0E,0x71,0xDF,0x44,0x0B,0x03,0xDA,0x1A,0x50,0x27,0x12,0x1E,0x5E,0x3A,0xED,0xF1,0x52,0x19,0x25,0x9A,0xD9,0x7C,0x68,0x24,0x66,0x90,0x06,0x28,0x16,0x1C,0x09,0x00, +0x3D,0x8E,0x0E,0x07,0xD4,0x78,0xFD,0x7D,0x08,0x18,0x61,0xA3,0xBD,0xCD,0x47,0x57,0x15,0xBE,0xE7,0x2B,0x56,0x53,0xCA,0x18,0x94,0x96,0x95,0xA3,0x90,0xF6,0x5F,0x3A, +0x96,0x0A,0x70,0x72,0x32,0x41,0xED,0xEF,0x81,0xA5,0x8E,0xA1,0xAC,0xE9,0xAB,0x77,0x57,0x48,0x97,0x09,0x04,0x6F,0xBC,0xB9,0x99,0x23,0xDD,0xB5,0x87,0xA9,0x2D,0x80, +0x47,0x6E,0xD9,0x3F,0x6D,0x33,0xD0,0xCD,0x25,0xCE,0xD3,0x0D,0x63,0xD4,0xAB,0xC5,0x22,0x95,0x86,0x59,0x24,0x68,0xD6,0x4C,0x0E,0xC5,0x54,0x82,0xAD,0x8E,0x38,0x3C, +0xE3,0xB7,0x03,0x48,0x15,0x27,0x2C,0xAE,0x6B,0x4D,0xC5,0xD3,0x7D,0x3B,0xB3,0x35,0xAF,0x78,0xB6,0x89,0x7A,0xE5,0xD4,0x54,0xD6,0xDB,0x2D,0x5D,0x53,0x57,0x01,0x1D, +0x12,0x17,0x96,0x1F,0x1D,0x2A,0x65,0x92,0x42,0x40,0x51,0x29,0x50,0xB8,0xC9,0x65,0xE3,0x9C,0xE3,0xE9,0xF6,0x9B,0xF5,0x9D,0x75,0xE6,0xEB,0x5C,0x6A,0xFF,0x00,0xA9, +0x9B,0xA5,0x32,0xC6,0xBB,0x80,0xDE,0x63,0x88,0x6D,0xC9,0x19,0xE0,0x77,0xE4,0x0C,0xE7,0x8C,0xF3,0xC6,0x8D,0xF5,0x45,0xDA,0x93,0xA9,0x23,0xA3,0x92,0x19,0x63,0xA4, +0xAA,0x89,0x43,0xCB,0x4E,0xD1,0x96,0xF0,0xC9,0x52,0xAA,0x41,0x5C,0x16,0x38,0xCE,0x09,0xCB,0x65,0xF8,0x2A,0x09,0x25,0x6A,0xFF,0x00,0x7C,0x78,0xAD,0x72,0xD3,0x55, +0x3C,0xF0,0xE6,0x36,0x8D,0x25,0x45,0xDA,0x95,0x60,0x0C,0x32,0x82,0x32,0xC3,0x27,0xD0,0x1C,0x70,0x7D,0xCE,0x83,0x52,0x30,0x17,0x37,0x64,0xCC,0xE1,0x61,0x75,0x70, +0xF8,0x7D,0x6E,0x8E,0xC9,0xD3,0x1D,0x3C,0x62,0x05,0x16,0xA6,0x9C,0x97,0xE7,0x38,0x67,0xC3,0x72,0x77,0x73,0xC6,0xA8,0xB7,0x6B,0x78,0xA8,0xB0,0x49,0x93,0xB0,0xA7, +0x99,0x46,0x48,0x0A,0x47,0x20,0xE7,0xDB,0x3A,0x9C,0xD9,0x6B,0x76,0xFC,0x22,0xB0,0x5D,0xE0,0x8C,0x81,0x07,0xCB,0xC9,0x22,0x28,0x24,0xAA,0x61,0x41,0xCF,0x3C,0x7D, +0x47,0xF6,0xD5,0x42,0xDF,0x30,0xAC,0xB6,0x18,0xD8,0xE4,0xBC,0x7D,0x87,0xA6,0x47,0x7D,0x6A,0xD2,0xE9,0x0B,0x7C,0x3D,0x80,0xF4,0x59,0xB1,0x17,0x9C,0x99,0x37,0x27, +0xD5,0x69,0xB1,0xD4,0x47,0x5D,0x6E,0x82,0x7F,0x36,0x5D,0x01,0x23,0x69,0xE1,0xBD,0x47,0xE0,0xF1,0xA3,0x53,0x9F,0x24,0x45,0x53,0xCE,0x87,0xCC,0x73,0xDC,0x7E,0x9A, +0x54,0xE9,0xC9,0x3F,0xA7,0xDC,0xE5,0xA0,0x98,0x3E,0xD6,0x93,0x7C,0x7B,0xD7,0xB1,0xE3,0x70,0xE7,0xF2,0x7F,0x3A,0x77,0xA9,0x8D,0x3E,0x48,0x93,0xBB,0x24,0x76,0xCF, +0xF3,0xA1,0xB5,0x6F,0xE6,0x08,0x95,0x33,0x32,0x82,0x7C,0x97,0xD5,0xC2,0x3C,0x51,0xC6,0xCC,0xA3,0x2B,0x9C,0x30,0xF5,0xD4,0xFB,0xAD,0xA7,0x73,0x4D,0x49,0xB5,0x14, +0x84,0x9B,0xC4,0xC7,0x3E,0x8B,0x8C,0xFF,0x00,0x3A,0xA0,0xD3,0xC8,0x2B,0x3A,0x70,0x47,0x82,0x24,0x0B,0x80,0x4F,0xBE,0x90,0x3A,0xB0,0xAE,0xDA,0x58,0x25,0x69,0x1E, +0x49,0x18,0x84,0x29,0xC0,0xE0,0xAE,0x73,0x9E,0xFF,0x00,0xA6,0x97,0x31,0xA7,0x13,0x86,0x48,0x0F,0x6F,0x50,0x9E,0xB8,0x35,0x83,0xED,0xC8,0x1E,0x07,0x5B,0x9F,0xD9, +0x4D,0x2E,0x7F,0xFC,0xC4,0x12,0x09,0x19,0x04,0x83,0x9C,0xE8,0x9F,0x4D,0x3C,0x89,0x53,0x1C,0xE2,0x15,0x67,0x42,0x08,0xED,0xCF,0x39,0xE4,0x7A,0x8C,0x03,0xA1,0xB7, +0x50,0xAF,0x5E,0x15,0x19,0x4E,0x32,0x30,0x39,0xED,0xEF,0xC0,0xD1,0xBE,0x90,0x3E,0x0D,0x71,0x95,0xA4,0xDB,0x1A,0x9F,0x33,0x86,0xDA,0x14,0x7D,0xF1,0x8C,0x9C,0xE7, +0x8D,0x65,0x18,0x3C,0x62,0x4C,0x40,0xB4,0xF9,0xAF,0x4A,0x62,0xCF,0xF0,0xB0,0xF7,0x3B,0xC8,0x22,0x97,0xA9,0x1A,0x56,0x91,0x2A,0xD5,0x92,0x45,0x62,0x42,0xB2,0xE7, +0x19,0xC1,0xCE,0x73,0xEF,0x9D,0x2C,0x81,0x4D,0xE3,0xF2,0xE3,0x9C,0xF1,0xB4,0x9E,0x78,0xE4,0x01,0xEB,0xA6,0x6E,0xAF,0x8E,0x98,0xDC,0x1D,0x92,0x55,0x0D,0x22,0xE7, +0x2A,0x18,0x81,0xC9,0x03,0xD7,0xED,0xFC,0xE9,0x34,0xA9,0x8B,0xC2,0x75,0x40,0x4F,0xD9,0xBB,0x73,0xED,0xAE,0x71,0xE6,0x78,0x75,0x84,0x37,0x65,0x43,0x00,0x22,0x5A, +0x46,0xBC,0x1E,0xAA,0xA3,0xD1,0x4D,0xE1,0x74,0x95,0x42,0x00,0xED,0xE3,0x56,0x10,0x47,0x6F,0x44,0xCE,0x7F,0x6D,0x3A,0x51,0xC0,0x27,0xAC,0x0E,0x1C,0xED,0x4F,0x31, +0x04,0xFF,0x00,0x1A,0x48,0xE8,0x94,0x90,0x74,0x84,0x61,0x9C,0x96,0x92,0xB1,0xD8,0xE7,0xB7,0x08,0x34,0xF1,0x47,0x8A,0x7B,0x34,0xD3,0x86,0x04,0x82,0x00,0xCF,0x1F, +0xAE,0xB6,0x6C,0x39,0xC4,0x61,0xF1,0x38,0x75,0x2D,0x0B,0xCD,0x7C,0x46,0xC0,0xEC,0x6A,0xA1,0xA7,0xA0,0x71,0x5C,0xB5,0x8A,0xD3,0x56,0x05,0xC3,0x0C,0xB6,0xE6,0xF2, +0xF7,0xE7,0x80,0x4F,0xE9,0xAF,0x59,0xCA,0xA8,0x03,0x85,0x5C,0x9F,0x37,0x05,0xB5,0xF5,0x4B,0x1B,0x4D,0x23,0x48,0x06,0x77,0x72,0x57,0x9F,0xE7,0x5A,0x2E,0x38,0xA6, +0x82,0x59,0xA4,0x38,0x8D,0x01,0x62,0x5B,0x8E,0xDA,0x29,0x4E,0xD0,0x4D,0x8E,0xC9,0x5A,0xA6,0xE0,0x5C,0x6E,0x81,0x57,0xD4,0xC5,0x57,0x75,0x8E,0xD4,0xCA,0xC7,0x3E, +0x79,0x00,0xCF,0x00,0x1E,0x06,0x47,0xE4,0xFE,0x35,0xDF,0x35,0x2A,0xC3,0x11,0xA7,0xC3,0x00,0x47,0x0D,0xD8,0x76,0xCF,0xFC,0xCE,0xB9,0x6C,0xB4,0x1E,0x3B,0xCB,0x73, +0x92,0x46,0x77,0x75,0x24,0x81,0x8F,0x2E,0x47,0x00,0x73,0xCE,0x06,0x3F,0x7D,0x6B,0xBE,0x55,0xFC,0xB5,0x2D,0x45,0x4C,0x99,0x06,0x9D,0x09,0x24,0x61,0x77,0x71,0xF8, +0xF4,0xC6,0x89,0xB2,0x41,0x67,0x1F,0x20,0x84,0xD4,0xC4,0x1A,0x00,0x23,0x52,0xA0,0xDD,0x43,0x59,0x14,0xDD,0x69,0x30,0x68,0xE1,0x9E,0x25,0x72,0x1D,0x24,0x59,0x48, +0xDB,0xBB,0x6E,0x32,0xA0,0x00,0x7E,0xD9,0xCF,0xEF,0xAE,0x8B,0x45,0xCA,0x9E,0x6A,0x88,0xED,0xF0,0xD2,0x9A,0xA8,0x99,0xB7,0x46,0xBE,0x21,0x93,0x6B,0x2E,0x38,0xDA, +0xD1,0x85,0xC8,0x1F,0xEA,0x6C,0x63,0xD7,0x3A,0x58,0x7B,0x8C,0x93,0x75,0x65,0x4D,0x5D,0x3A,0xC8,0xB2,0xCB,0x92,0x6A,0xE9,0xCA,0xF9,0x94,0x1E,0x41,0xC0,0x20,0x2F, +0xA6,0x7F,0x5E,0x74,0xF1,0x64,0xB7,0x9A,0x4A,0x96,0xB8,0xDA,0xA8,0xD1,0x6E,0x32,0x45,0xB5,0xA1,0x96,0x76,0x8E,0x36,0xC0,0x05,0x9C,0x31,0xF3,0x30,0xFB,0xB0,0x55, +0x1F,0x70,0x72,0x73,0x69,0x8D,0xE5,0x2F,0x4D,0xF0,0xC6,0x5B,0x1B,0x5A,0x7C,0x94,0x5A,0xBA,0x92,0x01,0x05,0x43,0x08,0x96,0x27,0x76,0x51,0x37,0x84,0xF2,0xF8,0x71, +0xE4,0xFA,0x2E,0xD6,0xC8,0x39,0xCF,0x3D,0xB0,0x70,0x3D,0x74,0x3F,0xAB,0xAE,0x9F,0x2F,0x4B,0x0C,0x74,0xF5,0x42,0xAE,0x28,0x95,0x57,0x06,0x06,0x8E,0x32,0x71,0xE8, +0x18,0x64,0xFA,0xFB,0x67,0xD8,0x6B,0x35,0x9A,0x82,0x9B,0xF1,0x02,0x61,0x5F,0xA5,0x7E,0x0C,0xD5,0x3F,0x51,0x7C,0x04,0xA1,0x5F,0x19,0x1B,0x74,0x6F,0x0E,0x38,0x1D, +0x98,0x8C,0x60,0x0E,0x38,0xD3,0x77,0x46,0xD7,0x49,0xF2,0x29,0x42,0xEE,0xC2,0x5A,0x67,0x34,0xEE,0x59,0xF2,0x0E,0xCE,0x33,0x8C,0x7B,0x60,0xFE,0x75,0x9A,0xCD,0x6A, +0x91,0x6B,0x18,0x1F,0xA4,0x7A,0x05,0x99,0x56,0x69,0x33,0xED,0xB3,0xFE,0xA5,0x1F,0xB8,0xD2,0xB2,0xD6,0x7C,0xCC,0x58,0x12,0x29,0x0C,0xB9,0xC7,0xD5,0xEA,0x3B,0x67, +0x91,0xA6,0x44,0x7F,0x9D,0xB6,0x24,0x8A,0x70,0x42,0xE4,0xEE,0xF5,0xD6,0x6B,0x34,0x12,0xA3,0x40,0x1D,0xDD,0x1C,0x85,0xA0,0x12,0xDF,0x30,0xB4,0xD9,0xE4,0xF2,0x55, +0xD3,0x87,0x01,0xD4,0x96,0x50,0x17,0xBE,0x40,0xD4,0xCF,0xE2,0x14,0x81,0x6E,0x34,0x2D,0x27,0x23,0x0E,0xC0,0x0C,0x8E,0xDE,0xD8,0xF4,0x3F,0xF3,0x8D,0x66,0xB3,0x4B, +0xBC,0x49,0xFD,0xBA,0x6F,0x97,0xAA,0x78,0xFE,0x9E,0x6B,0x8E,0x53,0x37,0xF4,0xBB,0xFE,0x54,0xFD,0x99,0xDE,0x47,0xA8,0x0D,0xC9,0x24,0xF6,0xE3,0xBF,0xFE,0xF4,0xCD, +0xD1,0xF4,0x82,0xA6,0x79,0x09,0x8D,0xC2,0x8C,0x0D,0xDB,0xB3,0x82,0x48,0xED,0xDF,0x9E,0x73,0xAC,0xD6,0x6B,0x34,0xE1,0x5F,0xBC,0xAA,0x0E,0x72,0xF4,0x3F,0x12,0x48, +0xE8,0xE8,0x1C,0xE6,0xA2,0x9D,0x58,0x76,0xCE,0x23,0x0E,0xF2,0x28,0x5D,0x83,0x2E,0x1F,0x91,0xDD,0xB3,0xEC,0x4F,0xBE,0x93,0xA6,0x39,0x89,0x5C,0xB6,0x4F,0x3C,0x7B, +0x7E,0x75,0x9A,0xCD,0x7D,0xC4,0x5F,0x9D,0x72,0xA9,0xC3,0x3F,0x90,0x8D,0x54,0xFA,0x33,0xCD,0xD1,0xF4,0x64,0x63,0x7B,0x4B,0x33,0x0C,0x0E,0xF8,0x3B,0x73,0x9F,0x5E, +0x74,0xE1,0x73,0x94,0x53,0xD0,0x52,0x51,0xC2,0x09,0x32,0xB7,0x20,0x6B,0x35,0x9A,0xD7,0xA8,0x35,0xA3,0x81,0xBD,0x87,0xA0,0x5E,0x6B,0xE2,0x1D,0x31,0x7A,0xC7,0x79, +0x38,0xFA,0xAE,0xFA,0x62,0x90,0x51,0x16,0x23,0x73,0x37,0xE3,0x4A,0x3D,0x4F,0x54,0x93,0x4F,0x1D,0xB2,0x31,0xB4,0x2B,0x6F,0x75,0xF6,0xE7,0xCA,0x33,0xF7,0x23,0x59, +0xAC,0xD5,0xDA,0x5F,0xC4,0x77,0xC5,0x05,0xAA,0xFC,0x36,0xA2,0x70,0x2B,0xD0,0xDA,0x62,0x50,0x77,0x21,0x5D,0xAE,0xED,0x9C,0x93,0xDC,0xE9,0x3B,0xE2,0x35,0x54,0x90, +0x74,0xAD,0x45,0x3C,0x51,0xEF,0x79,0x10,0xB3,0xED,0xC2,0x90,0xB8,0xEF,0xCE,0xB3,0x59,0xAB,0xA3,0x58,0x9E,0x7B,0x14,0x1E,0x4E,0x69,0xDA,0x0E,0xC4,0x2F,0xCE,0xB6, +0xBB,0x6C,0xF5,0x95,0x33,0xC1,0xE1,0xD5,0x24,0xE1,0x0B,0x47,0x3B,0xAB,0x91,0x8D,0xD9,0x23,0x85,0xCB,0x0E,0x4F,0xE9,0xA6,0x68,0x23,0xAF,0xB4,0xA8,0x6A,0x38,0x05, +0xC1,0x2A,0x93,0xFB,0xCC,0xEA,0xED,0x26,0xE0,0x48,0x04,0x8C,0x92,0xA7,0xEC,0x76,0x93,0xEC,0x35,0x9A,0xCD,0x67,0x73,0xEC,0x9A,0xDA,0xBF,0xFF,0xD9,}; + diff --git a/examples/160 x 128/TFT_FLASH_Jpeg/jpeg4.h b/examples/160 x 128/TFT_FLASH_Jpeg/jpeg4.h new file mode 100644 index 0000000..ef73296 --- /dev/null +++ b/examples/160 x 128/TFT_FLASH_Jpeg/jpeg4.h @@ -0,0 +1,168 @@ +// We need this header file to use FLASH as storage with PROGMEM directive +#include + +const uint8_t Mouse160[] PROGMEM = { +0xFF,0xD8,0xFF,0xE0,0x00,0x10,0x4A,0x46,0x49,0x46,0x00,0x01,0x01,0x01,0x00,0x48,0x00,0x48,0x00,0x00,0xFF,0xDB,0x00,0x43,0x00,0x04,0x03,0x03,0x03,0x03,0x02,0x04, +0x03,0x03,0x03,0x04,0x04,0x04,0x05,0x06,0x0A,0x06,0x06,0x05,0x05,0x06,0x0C,0x08,0x09,0x07,0x0A,0x0E,0x0C,0x0F,0x0E,0x0E,0x0C,0x0D,0x0D,0x0F,0x11,0x16,0x13,0x0F, +0x10,0x15,0x11,0x0D,0x0D,0x13,0x1A,0x13,0x15,0x17,0x18,0x19,0x19,0x19,0x0F,0x12,0x1B,0x1D,0x1B,0x18,0x1D,0x16,0x18,0x19,0x18,0xFF,0xDB,0x00,0x43,0x01,0x04,0x04, +0x04,0x06,0x05,0x06,0x0B,0x06,0x06,0x0B,0x18,0x10,0x0D,0x10,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, +0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0xFF,0xC0, +0x00,0x11,0x08,0x00,0x6B,0x00,0xA0,0x03,0x01,0x22,0x00,0x02,0x11,0x01,0x03,0x11,0x01,0xFF,0xC4,0x00,0x1D,0x00,0x00,0x02,0x03,0x00,0x03,0x01,0x01,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x05,0x03,0x06,0x07,0x00,0x02,0x08,0x01,0x09,0xFF,0xC4,0x00,0x3B,0x10,0x00,0x02,0x01,0x02,0x04,0x04,0x04,0x04,0x04,0x05,0x03, +0x04,0x03,0x00,0x00,0x00,0x01,0x02,0x03,0x04,0x11,0x00,0x05,0x12,0x21,0x06,0x13,0x31,0x41,0x22,0x51,0x61,0x71,0x07,0x14,0x81,0x91,0x23,0x32,0xA1,0xB1,0x08,0x15, +0x33,0x42,0xD1,0x52,0x62,0xC1,0x16,0x24,0x43,0xF1,0x82,0xB2,0xE1,0xFF,0xC4,0x00,0x19,0x01,0x00,0x03,0x01,0x01,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x02,0x03,0x01,0x04,0x05,0xFF,0xC4,0x00,0x22,0x11,0x00,0x02,0x02,0x02,0x02,0x02,0x02,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x02, +0x11,0x03,0x21,0x31,0x41,0x12,0x13,0x22,0x51,0x04,0x14,0x32,0xFF,0xDA,0x00,0x0C,0x03,0x01,0x00,0x02,0x11,0x03,0x11,0x00,0x3F,0x00,0xF5,0x73,0xE6,0xCF,0x96,0xC7, +0x24,0xF9,0xDE,0x4F,0x48,0xF0,0xAD,0xAF,0x51,0x08,0x04,0x93,0x7F,0xF4,0x9D,0xFF,0x00,0x5C,0x34,0xC8,0xF8,0x9F,0x85,0xB3,0x88,0xD9,0x32,0xF9,0xA0,0x59,0x10,0xF8, +0xE0,0x91,0x02,0x3A,0xF9,0x1B,0x10,0x36,0x3E,0x78,0x97,0xE4,0xA0,0x9C,0xAB,0xC9,0x1A,0x4C,0xAA,0x06,0x9B,0x81,0x6B,0x7A,0x8F,0x2C,0x52,0x33,0x9E,0x08,0xA7,0x35, +0x72,0xD5,0x65,0xB2,0xF2,0x27,0x6B,0xAF,0x2F,0x70,0x42,0xF9,0x7B,0x6E,0x77,0xC6,0xA9,0xC9,0x6B,0xA2,0x6F,0x1F,0xD1,0xA7,0x2D,0x2D,0x23,0x0B,0xAC,0x31,0x91,0xE6, +0x14,0x60,0x5A,0x8C,0xBB,0x2F,0xA8,0x3F,0x89,0x49,0x13,0x8B,0x5B,0xC4,0x83,0x1E,0x3B,0x3F,0x1E,0x38,0xEB,0xE1,0x5F,0xC4,0x9A,0xEE,0x1C,0xE2,0x06,0xFE,0x79,0x95, +0xC3,0x51,0xA2,0xCC,0x2D,0x2A,0xA1,0xB5,0xAC,0xC4,0xDC,0xD8,0x1E,0xF8,0xF4,0xAE,0x49,0xC4,0xF5,0x5C,0x65,0xC2,0x74,0x39,0xED,0x04,0x13,0x53,0xD1,0xD6,0x46,0x25, +0x55,0x94,0x69,0x76,0x53,0xD2,0xF6,0xE8,0x3B,0xE2,0x9E,0xC8,0xC5,0x09,0xEB,0x61,0x3C,0x49,0x94,0x64,0x52,0xE5,0x52,0x51,0x24,0x14,0x8B,0x23,0x0B,0x10,0xAA,0x09, +0x02,0xFB,0xF4,0xC7,0x29,0x68,0xF8,0x6A,0x99,0x56,0x9E,0x08,0xA9,0x14,0x84,0x1F,0xF8,0xC2,0x93,0xFA,0x62,0x1A,0xA8,0xF9,0x04,0x34,0x8A,0xF1,0x93,0x72,0x34,0xAD, +0xC8,0x18,0xAF,0x57,0x51,0x4F,0x23,0xBB,0xBA,0x94,0x36,0xDA,0xF7,0x18,0x45,0x99,0xCB,0xA2,0xFF,0x00,0xAF,0x65,0xD2,0x3C,0xB7,0x2D,0xD0,0x0C,0x74,0xD0,0x95,0x3B, +0x82,0xAA,0x2D,0x88,0xBE,0x46,0x93,0x5E,0xA1,0x02,0x0F,0x65,0x03,0x19,0xF5,0x1E,0x71,0x98,0x64,0xAE,0xB0,0xAC,0xAC,0xE8,0x5A,0xDA,0x5B,0x7D,0xB1,0x7B,0xCB,0xB3, +0x4A,0x6C,0xC6,0x8C,0x4F,0x0B,0x82,0x41,0xB3,0x28,0xEA,0xA7,0x16,0x52,0x4F,0x82,0x52,0x83,0xC6,0xCE,0x35,0x1D,0x31,0x8D,0x64,0xD0,0x81,0x8D,0xC5,0x80,0xB7,0x4B, +0x7F,0x9C,0x0D,0xC9,0x8B,0xFD,0x03,0x06,0x49,0x24,0x4A,0xE5,0x55,0xAF,0x6C,0x08,0xD2,0x2A,0xB1,0x56,0x36,0xB6,0x18,0x90,0x2B,0x41,0x1A,0x9B,0x14,0x5C,0x0E,0xF4, +0xD1,0x2E,0xE1,0x05,0xB0,0x4B,0xCA,0xBA,0xFC,0x64,0x0C,0x40,0xEC,0x19,0xEE,0x31,0xCE,0xE6,0xD8,0x01,0xCB,0x0C,0x5C,0xCF,0xC8,0x3A,0x61,0x4E,0x69,0x55,0x0D,0x13, +0x47,0x1A,0x21,0x92,0xA2,0x53,0xA6,0x28,0x10,0xEE,0xC7,0xCF,0xD0,0x79,0x9C,0x45,0xC5,0x9C,0x53,0x45,0xC3,0x79,0x6F,0xCC,0xC9,0xF8,0xD3,0xBF,0x86,0x18,0x14,0xEE, +0xE7,0xFC,0x0B,0x8B,0x9C,0x22,0xF8,0x59,0x3E,0x65,0x9C,0xF1,0x15,0x7E,0x73,0x9A,0x48,0xF2,0xAA,0x26,0xC4,0xC6,0x36,0x37,0x1B,0x2F,0xA0,0xDA,0xD8,0xCB,0xD5,0x16, +0x82,0xB8,0xD1,0x63,0xA8,0xA1,0x92,0x91,0xA2,0xA7,0xA8,0xD0,0xD5,0x06,0x21,0x24,0x96,0x16,0x00,0x92,0x76,0x18,0x11,0xA0,0x8D,0x4D,0x8A,0x2E,0x0F,0xCD,0x26,0x41, +0xC4,0x05,0x1E,0x6D,0x72,0xB4,0x60,0x15,0xF2,0x23,0xB0,0x1F,0x5C,0x41,0x2B,0x0B,0x69,0xEF,0x8C,0x28,0x2E,0x9E,0x08,0x99,0xAC,0x50,0x60,0x39,0x69,0x20,0xE6,0x7E, +0x4E,0xD8,0x3D,0xD8,0x33,0xDC,0x60,0x69,0x7F,0xA9,0xF4,0xC0,0x02,0x59,0xB2,0x7A,0x03,0xA8,0x9A,0x38,0x4B,0x11,0x72,0x4A,0x83,0x85,0x15,0x7C,0x23,0xC3,0xF5,0x12, +0x6B,0x9B,0x29,0xA5,0x79,0x3B,0xB1,0x41,0x8B,0x2B,0x31,0x63,0x73,0x85,0xF2,0xB0,0xB6,0x9E,0xF8,0x00,0xF4,0x36,0x5F,0x50,0xE8,0x81,0xCA,0x24,0xC1,0x01,0xB9,0x09, +0x73,0xD3,0xC8,0x62,0x6A,0x99,0xE3,0xA9,0xA5,0x13,0x72,0x82,0xB2,0xEE,0xB2,0x46,0x01,0x23,0xCF,0x63,0x88,0xEB,0x32,0xFA,0x2C,0xB2,0x49,0x2A,0x29,0x2B,0x4C,0x2D, +0xD6,0xCC,0xB7,0x1F,0x7C,0x05,0xCD,0x8F,0x30,0xA4,0x47,0xA9,0x9A,0x0D,0x40,0x9B,0x33,0x0D,0x21,0x87,0x90,0x7D,0x8F,0xDF,0x1C,0xB0,0x9F,0x68,0xD7,0x5D,0x1E,0x3C, +0xFE,0x26,0x32,0x6C,0xBE,0x87,0xE2,0x1A,0xE7,0x70,0x42,0xA1,0x2B,0x51,0x4B,0xE8,0x16,0xFC,0x40,0x2C,0xC7,0x6E,0xFD,0x0F,0xD7,0x1E,0xC3,0xF8,0x67,0x49,0x47,0x55, +0xF0,0x67,0x85,0xEA,0x50,0x16,0x07,0x29,0xA6,0x37,0x3D,0x7F,0xA4,0xB8,0xCD,0x7E,0x32,0x7C,0x3E,0x4E,0x32,0xE0,0x99,0x69,0x21,0x51,0x35,0x65,0x10,0x35,0x11,0x0D, +0x0A,0x5A,0x4D,0xBA,0x29,0xEF,0xF4,0xC3,0x1F,0xE1,0xE3,0x8D,0xA9,0x33,0x4F,0x84,0x34,0x99,0x24,0x8A,0x20,0xAE,0xC9,0x89,0xA2,0x95,0x19,0x89,0xB8,0x04,0x95,0x6D, +0xF7,0xE8,0x6D,0xEE,0x0E,0x1F,0x1F,0x3B,0x0A,0xD5,0x9A,0x7D,0x7C,0x54,0xD0,0x07,0x76,0x08,0x58,0x6F,0xBF,0x6C,0x57,0x04,0x30,0x55,0x54,0x30,0x75,0x32,0xB1,0xD8, +0xFA,0x60,0xAC,0xFA,0xAE,0x46,0x52,0xE5,0xC3,0x17,0xD8,0x79,0x5B,0x1D,0xF2,0x5A,0x46,0x8A,0x01,0xA1,0x35,0x4C,0xFD,0x5F,0xC8,0xE2,0xD7,0xBA,0x2D,0x8F,0x81,0x36, +0x77,0xC1,0xCB,0x2D,0x01,0x9E,0xC1,0x5C,0x02,0x42,0x8E,0x87,0x19,0xA6,0x65,0x5B,0x9D,0xF0,0xFC,0xEF,0x36,0x59,0xF8,0x73,0xAB,0x04,0x92,0x19,0x45,0xD5,0x81,0x36, +0x1D,0x7A,0x78,0xAD,0xBF,0xFB,0xB1,0xBF,0x56,0x1C,0xBA,0x92,0x91,0x4D,0x6A,0x33,0xB3,0x75,0x20,0xFF,0x00,0x9C,0x65,0x1C,0x65,0x43,0x1D,0x59,0x9A,0x4A,0x37,0x2C, +0x18,0x00,0x88,0xBD,0x19,0xAE,0x0F,0x43,0xDC,0x5B,0x1A,0x4E,0xDC,0xB4,0x63,0x75,0xBF,0xC4,0x26,0x6F,0x4F,0x9A,0xCB,0x97,0x55,0xE4,0x4D,0x4D,0x57,0x16,0xA2,0xCA, +0x58,0xD8,0x15,0x62,0xA7,0xF6,0x38,0xEB,0x0F,0xF1,0x1B,0xF3,0x4E,0xF0,0xAE,0x49,0x31,0x91,0x53,0x58,0x41,0x70,0x5E,0xC3,0xC5,0x6B,0xDB,0xDC,0x7D,0xB1,0x75,0xF8, +0x83,0xC0,0x39,0x35,0x45,0x74,0x99,0xC5,0x14,0x5F,0x89,0x14,0x12,0x3C,0xAC,0x77,0x2F,0xE1,0xF0,0x83,0xE9,0xB0,0x1F,0x53,0x8C,0x2D,0x32,0xD6,0xCB,0x6A,0x09,0x9E, +0x9D,0x5E,0x04,0x45,0x95,0x4B,0x2E,0xEC,0x0D,0xC3,0x0B,0xFD,0x09,0xFA,0x0C,0x43,0x27,0xE4,0x6C,0x4C,0x78,0xD5,0x9A,0x2A,0x7C,0x7C,0x35,0x21,0xA3,0xFE,0x4B,0x56, +0x25,0x54,0x2D,0xA5,0x86,0xFA,0x86,0xC4,0x7B,0xDA,0xC4,0x7A,0xE1,0xB8,0xF8,0xA6,0x2B,0x3E,0x4E,0x3D,0x33,0xC4,0x92,0xA2,0x16,0x3B,0x5F,0x73,0x72,0x3A,0xEC,0x40, +0x07,0x19,0x64,0x75,0x14,0x79,0x65,0x6D,0x0C,0xF3,0x13,0xCF,0x99,0x96,0x49,0x13,0x4D,0xED,0xE2,0xD3,0x60,0x7D,0x40,0xFB,0xE2,0xC0,0xB1,0xD0,0xFC,0x85,0x43,0x23, +0x46,0xAB,0x0C,0x9A,0x5C,0x1D,0xB4,0x5F,0x50,0xF0,0xFD,0xEF,0x85,0x8E,0x7F,0x20,0x78,0xD4,0x5E,0x8B,0x4E,0x69,0x92,0xD5,0x67,0x39,0xE4,0x19,0xAC,0xF5,0x32,0xCA, +0x8C,0x0A,0xC7,0x16,0x92,0x79,0x6A,0x2F,0xB7,0xED,0xFA,0xE2,0xED,0xC1,0x6D,0xFC,0xAF,0x2F,0x68,0x0A,0xD9,0xEA,0x49,0x63,0xA6,0xF7,0xD9,0x85,0xEC,0x3E,0xA0,0xE0, +0x0E,0x06,0xE2,0x6C,0xA3,0x36,0xCC,0xA3,0xA1,0x58,0x16,0x6F,0x12,0x84,0x88,0x36,0x93,0x60,0xAC,0x4F,0xBE,0xEA,0x6F,0xEF,0x8B,0x8E,0x65,0x05,0x2D,0x5E,0x68,0x33, +0x3C,0xBA,0x45,0x02,0x10,0x62,0x90,0x2A,0xDB,0xC7,0xE1,0xBF,0xEA,0x3E,0xC3,0x17,0x83,0xB5,0xB3,0x6F,0x54,0x29,0xCF,0xF9,0x31,0xD6,0xAE,0x61,0x10,0x70,0xE2,0x40, +0x0B,0xD8,0x9F,0x05,0xB7,0xFF,0x00,0xDE,0x3A,0xF3,0xD2,0x58,0x96,0x60,0xC1,0x94,0x8E,0xAB,0xB8,0xC3,0x7C,0xF5,0x29,0xD7,0x26,0xF9,0xAA,0x52,0xB2,0x8D,0x1A,0xE4, +0x42,0x7C,0x24,0xE9,0xF1,0x0F,0xDF,0x15,0x6C,0xB2,0x90,0xD2,0x65,0x08,0x35,0x12,0x1B,0xF1,0x2C,0x7F,0xB6,0xFB,0xD8,0x61,0x8C,0x08,0x9A,0x53,0x76,0x1F,0xDC,0x36, +0xC0,0xAE,0xC5,0x56,0xE3,0xCF,0x1D,0xDD,0x99,0xDA,0xE5,0xB6,0x1D,0x06,0x06,0x76,0x0C,0xD7,0x18,0x0C,0x6A,0xC8,0xA7,0x93,0x51,0x2A,0x46,0xE0,0xF5,0xC0,0xB2,0xFF, +0x00,0x4F,0xEB,0x89,0xA4,0x7D,0x56,0x03,0xA1,0xC0,0x8E,0xC1,0x9A,0xE3,0x18,0xEF,0xA0,0x77,0xD1,0xBE,0x66,0x35,0x55,0xB5,0x3A,0x9E,0x0A,0x6D,0x2A,0x0E,0xDA,0xD8, +0x23,0x7B,0xF5,0xC4,0x55,0x10,0x4B,0x4F,0x43,0xCA,0x04,0x3B,0x3E,0xE6,0x2D,0x5A,0x3F,0x4D,0xC3,0x7D,0xB0,0xCD,0xE5,0x14,0x5C,0xA2,0x94,0x4D,0x25,0xBA,0xB1,0x2A, +0x00,0xF4,0xF3,0xC0,0x55,0xF2,0xA9,0x03,0x9D,0x96,0x4A,0x8B,0x21,0xB0,0x65,0x7B,0x0F,0xB7,0xF9,0xC7,0x94,0x75,0x82,0x64,0x74,0x51,0xC7,0x3B,0xA2,0xE9,0x8B,0x59, +0x37,0x49,0x08,0x2B,0x7F,0x20,0xBE,0x5E,0xD8,0xA9,0x66,0x3C,0x3B,0x47,0xC0,0x9F,0x15,0xA2,0xE2,0x8A,0x18,0xB9,0x39,0x4E,0x6F,0xF8,0x39,0x8C,0x51,0xAF,0x86,0x19, +0x3A,0xA4,0x96,0xDF,0x6D,0x5D,0x4F,0xA9,0xC6,0x93,0x95,0xE5,0xCA,0xD0,0x7C,0xCA,0xC2,0x0C,0x7D,0x46,0xB3,0xAC,0xAF,0xD3,0x0C,0x2B,0xE8,0x56,0x7A,0x27,0x35,0x10, +0x89,0x14,0xAE,0xEA,0x46,0xA5,0x23,0xD4,0x77,0xC5,0x7C,0xE5,0xFD,0x0A,0xE6,0x96,0x8A,0x26,0x75,0x5D,0x04,0x87,0x95,0x0B,0x6A,0x8D,0x0D,0xF5,0x03,0xFE,0x31,0x65, +0xE1,0x49,0x61,0x6C,0xAC,0x19,0xE4,0x1A,0xBD,0x70,0x83,0x32,0xC8,0x53,0x27,0xAD,0x67,0x48,0x1D,0xB2,0xF9,0x53,0x5C,0x52,0x2E,0xEB,0x1B,0x77,0x4F,0x31,0xE9,0x8E, +0xBC,0x3D,0x53,0x1C,0xB5,0x75,0x10,0xCA,0x5B,0x42,0x0D,0xB4,0xEC,0x17,0xD4,0xE3,0xB2,0x16,0xDD,0x98,0x92,0x6A,0x91,0x66,0xE2,0x41,0x0C,0xB9,0x74,0xA6,0x33,0x76, +0xEA,0x18,0x6F,0x6D,0xB1,0x96,0x66,0xD5,0x13,0xD6,0xE5,0xD4,0xF4,0xF4,0x09,0x6A,0xA5,0x71,0x0C,0xB1,0xDE,0xE1,0xD1,0xB7,0xD4,0x3D,0x41,0x43,0xF4,0xC5,0xC7,0x8B, +0x4C,0xD0,0x65,0x6B,0x55,0x4B,0x32,0x08,0xE6,0x65,0x89,0x64,0x2F,0xB5,0xC9,0xB0,0xDA,0xDE,0xBD,0x70,0x3D,0x16,0x47,0x4B,0x03,0xC7,0x2C,0xAC,0xC5,0x96,0xCF,0x1B, +0x92,0x7A,0x9E,0xB7,0xFB,0x91,0x89,0x64,0x9D,0xBA,0x89,0xBA,0x8A,0x14,0xD6,0x52,0x5B,0x84,0xDF,0x99,0x09,0x25,0xD5,0xA1,0x57,0x6F,0xCC,0x6C,0xDB,0x13,0xF4,0x1F, +0xAE,0x30,0xFE,0x29,0xA3,0xA6,0xA7,0x8D,0xB2,0xA8,0x60,0x2F,0xAC,0xB4,0x6A,0xF1,0x81,0xF8,0x46,0xFE,0x11,0xD3,0xFD,0xC4,0x63,0xD0,0x19,0xC0,0xAB,0xA9,0xAA,0x9E, +0x38,0xD9,0x63,0xA0,0x44,0x11,0x4C,0xC1,0x6F,0xC9,0x7B,0xDC,0x1F,0xD7,0xAF,0x98,0x18,0xA9,0x45,0xC2,0x0F,0x2E,0x65,0x34,0x55,0x50,0xA9,0x59,0xE2,0x05,0x25,0x53, +0x72,0xEE,0x84,0x91,0xF5,0xB5,0xB7,0xEF,0x71,0x8C,0x94,0x53,0xD3,0x23,0x07,0x4F,0x66,0x03,0x99,0xE5,0x70,0xD0,0xE4,0x51,0xAD,0x5A,0xF3,0x2B,0x29,0x69,0xD2,0x37, +0x41,0xDD,0xB5,0xDC,0x11,0xEA,0x14,0xDF,0xDC,0x60,0x4C,0xDA,0x82,0x9A,0x1C,0xB0,0xE7,0x10,0x57,0x12,0xD2,0x40,0xD3,0x3C,0x57,0xFF,0x00,0x68,0xD3,0xF7,0x04,0x7D, +0xF1,0xA8,0x71,0x47,0x0B,0x1F,0x94,0xAB,0xA8,0x11,0x82,0xC0,0xCF,0x7B,0xDC,0xB1,0x01,0x14,0x10,0x3B,0x6D,0x73,0xFA,0x63,0x31,0xCC,0xB8,0x5E,0x5A,0x5B,0x72,0xE6, +0x60,0xAA,0x51,0x80,0x63,0x70,0xC8,0x5A,0xCD,0x7F,0x40,0x05,0xF6,0xC2,0x7A,0x90,0xD7,0x72,0xD0,0x07,0xC3,0xCE,0x26,0x97,0x2A,0xE2,0xEA,0x1A,0xC9,0x98,0x88,0x63, +0x9C,0xC6,0xEA,0xA3,0x49,0x11,0xDC,0xB3,0x9F,0xB1,0x3F,0x7C,0x7A,0x43,0x28,0xAB,0x9A,0xA7,0x27,0x4F,0x97,0x78,0xE2,0x89,0x97,0xE6,0x27,0x91,0x89,0x66,0xD6,0xC4, +0xED,0x61,0xE8,0x3A,0x63,0x0E,0xCB,0xF8,0x52,0x92,0xB7,0x26,0x86,0xBE,0x14,0x92,0x19,0x65,0x56,0x46,0xDA,0xEA,0xAC,0xA1,0x77,0xB7,0x6B,0xDF,0xFE,0x71,0x60,0xE1, +0x1C,0xE2,0xAF,0x2D,0x57,0xC9,0xEB,0x25,0xE6,0x4F,0x15,0x4B,0x69,0x7B,0x0D,0x3C,0xA2,0x00,0x02,0xF7,0xDC,0x8D,0xC7,0xD3,0x14,0x26,0x6A,0x95,0xAB,0x3B,0xE5,0x53, +0x53,0x99,0xDC,0x29,0x5E,0x61,0x05,0x74,0xEE,0xC6,0xE0,0x7D,0xF7,0xC0,0x25,0xC0,0x8A,0x38,0x94,0x12,0x15,0x40,0x2C,0x7B,0xDB,0x1C,0x69,0x11,0xE3,0xB4,0x3A,0xF9, +0x6E,0x06,0xEC,0x6E,0x4F,0xAE,0x21,0x79,0x02,0x80,0x01,0x1A,0xBB,0x8C,0x75,0x18,0xD5,0x9C,0xA9,0x7B,0xCB,0xA5,0x7A,0x0E,0xB8,0x12,0x59,0x44,0x63,0xD7,0x12,0xC9, +0x11,0x2A,0x35,0x74,0x1E,0x58,0x0A,0x70,0x55,0x2C,0xC4,0x03,0xDB,0x00,0x6E,0xCE,0x34,0xEA,0xC6,0xE7,0xF6,0xC0,0x72,0xB2,0xDB,0x48,0x60,0x4F,0xA6,0x24,0x55,0x7E, +0x58,0x76,0x16,0xBF,0x4C,0x40,0xE9,0xA7,0x71,0xD3,0x09,0x36,0xB8,0x66,0x9E,0x96,0xCC,0xA9,0x20,0x50,0x19,0xCC,0x71,0xE9,0x17,0xD4,0xC5,0xB4,0xDF,0xDA,0xF8,0x12, +0x6A,0xC8,0x92,0x8B,0x97,0x33,0x5A,0x39,0x3C,0x37,0x8D,0x43,0x5E,0xFE,0x87,0x06,0x67,0xB1,0x7C,0xC4,0xD4,0xF1,0x84,0xD0,0x41,0x27,0x5B,0x90,0x74,0xFD,0x2F,0x84, +0xB5,0x79,0x41,0x68,0xFF,0x00,0x02,0xA5,0xE7,0xA8,0xB6,0xAD,0x52,0x48,0x42,0xA7,0xB0,0xED,0x8F,0x2E,0xF7,0x47,0x50,0xFF,0x00,0x87,0x79,0xEB,0x01,0x50,0x96,0x8F, +0xB1,0x7E,0xA7,0xF4,0xC1,0xF5,0xCB,0x2C,0xF1,0x78,0x26,0x9A,0x9C,0x9D,0x98,0x0F,0x5F,0x7C,0x20,0xCA,0x6B,0xFE,0x56,0xB5,0x61,0x9A,0xAD,0xE4,0x20,0x6E,0x11,0x18, +0xDB,0xEB,0xBE,0x1A,0xD7,0xE6,0x17,0x48,0xC8,0x76,0xB3,0x11,0x7B,0xA8,0xDC,0x7D,0x71,0xD5,0x85,0xD2,0x21,0x93,0x92,0x0A,0x8E,0x1E,0xCE,0x9E,0x0F,0x96,0x15,0x74, +0x55,0x31,0x01,0xB3,0x4E,0xAE,0xAC,0x7D,0xEC,0x6D,0xFA,0x61,0x74,0x1C,0x39,0x57,0x96,0xD4,0x55,0xC5,0x23,0xC2,0xCB,0x56,0x8A,0xA8,0xAB,0xD1,0x2C,0xB6,0x3E,0x5D, +0xF7,0xC5,0x91,0xF3,0x65,0x48,0x06,0x88,0x9D,0x89,0xDF,0x63,0x6F,0xDF,0x15,0x3E,0x20,0xCD,0xB3,0x00,0x5A,0x5E,0x6D,0x3A,0x12,0x6D,0x1A,0xF3,0x40,0x3E,0xD8,0xB2, +0x9D,0x21,0x09,0x66,0x9A,0x90,0x81,0x97,0x3C,0x49,0x24,0xC9,0x74,0x61,0xB5,0xAF,0xEA,0x3B,0xE2,0x37,0xA0,0x0B,0x44,0x94,0x70,0xA1,0x2C,0x1B,0x71,0x19,0x20,0x2D, +0xFC,0xAE,0x7C,0xF1,0x5C,0x85,0x5D,0xEA,0x35,0xDE,0x78,0xDE,0x57,0xBB,0x19,0xAE,0x00,0x3E,0x63,0xD3,0x0D,0x93,0x30,0x87,0x20,0xAB,0x59,0xAB,0xA5,0x92,0x71,0x3B, +0x00,0x14,0x38,0xBA,0x93,0xE4,0xDD,0x2D,0xE8,0x6D,0x84,0x35,0xBB,0x33,0xAF,0x8D,0x1F,0x17,0x72,0xCF,0x86,0x19,0x71,0xA7,0xA6,0x8A,0x1A,0xCC,0xE6,0x45,0x59,0x19, +0x25,0xB0,0x10,0x9D,0xB6,0x20,0x1F,0x11,0xB7,0x6C,0x65,0x7F,0x0E,0x7F,0x88,0x8C,0xC7,0x8B,0x38,0xB6,0x9B,0x21,0xCC,0xA9,0x29,0xB2,0xE9,0xE7,0x2E,0x90,0x54,0xC3, +0xF9,0x75,0x15,0x36,0x52,0xA7,0xBF,0x50,0x3E,0x98,0xCA,0xBF,0x88,0x5A,0xB9,0x73,0x0F,0x8E,0xB5,0xB5,0x75,0xF1,0x4C,0xB1,0xAD,0x5B,0xE9,0x57,0x36,0xD4,0x9A,0x86, +0x9F,0xD3,0x1A,0x27,0x0F,0x7C,0x3B,0xE1,0xEE,0x22,0x9E,0x2F,0x88,0xFC,0x23,0x40,0xD9,0x42,0x41,0x99,0x51,0xC5,0x4F,0x93,0xCC,0x4B,0x3B,0x84,0x50,0x65,0x97,0x51, +0xFE,0xD2,0x45,0xF6,0xB8,0xDC,0x8B,0xE3,0xA6,0x2A,0x91,0x87,0xA1,0xB3,0x3A,0x58,0x17,0x28,0x86,0x24,0x85,0xA7,0xD5,0x19,0x46,0x04,0xF9,0xA5,0x9C,0x93,0xEB,0xFF, +0x00,0x18,0xCF,0x38,0x8F,0x84,0x05,0x3A,0xB2,0x56,0xA3,0x37,0x2A,0xF0,0x2A,0xAD,0xFC,0x31,0xEE,0x2C,0x4F,0xA5,0xBA,0xE2,0xFD,0x59,0x58,0x29,0x16,0x3D,0x48,0x35, +0xCD,0x20,0x57,0xE6,0x0B,0x69,0xDF,0x62,0x0F,0x99,0x16,0xC1,0x0F,0x24,0x39,0xDD,0x5D,0x4C,0x33,0x05,0xE6,0x68,0x0B,0xBF,0x52,0xE4,0xFF,0x00,0x82,0x70,0xAF,0xE4, +0xB4,0x6A,0x74,0x64,0x3C,0x3F,0x56,0xE9,0x4F,0x15,0x37,0x2A,0x34,0x49,0x23,0xE6,0xA0,0xD8,0x0F,0x10,0x0A,0x6F,0x7E,0xD7,0xBF,0xDC,0x63,0x1B,0xCE,0xF8,0xD6,0x9F, +0x25,0xF8,0xB8,0xFC,0x3B,0x99,0xB1,0x89,0x20,0x64,0x8A,0x09,0xC1,0x01,0x0E,0xB5,0x56,0xF1,0x9E,0xBD,0x4F,0x5D,0xF1,0xE8,0x57,0xC9,0x10,0xD7,0xCB,0x24,0x69,0x64, +0x85,0xCA,0x24,0x2C,0x9A,0x40,0xD4,0xEA,0x16,0xD7,0xED,0x72,0x7E,0xB8,0xF3,0x67,0xC6,0xAE,0x05,0x9A,0xAF,0xE2,0x0E,0x53,0x55,0x4D,0x4E,0x0C,0x2F,0x12,0xA5,0x4C, +0x8A,0x46,0xFA,0x58,0x90,0x4F,0xFF,0x00,0x12,0x07,0xA5,0xB1,0x8A,0x1A,0xA6,0x61,0xE8,0xFC,0xB9,0x9E,0x5A,0x38,0xE7,0x9A,0x4D,0x6C,0x40,0xFC,0xBD,0x3A,0x6C,0x06, +0x19,0x94,0x28,0x6C,0x48,0x24,0x8D,0xF1,0x5C,0xE1,0x69,0x0D,0x4E,0x47,0x05,0x41,0xA9,0x25,0x51,0x79,0x60,0x29,0xEE,0x36,0xB9,0xEF,0xD3,0x0D,0x9E,0xB2,0x39,0x91, +0xB9,0x12,0x8D,0x20,0xD9,0x9F,0xF7,0xB6,0x28,0x04,0xB5,0x33,0x05,0xBC,0x69,0xE3,0x7B,0xF4,0x07,0xA7,0xBE,0x00,0x92,0x22,0xF3,0x97,0x9C,0xEB,0x7E,0x9B,0x1D,0x86, +0x3A,0x4B,0x3C,0x68,0x49,0x4B,0x2C,0x60,0x7E,0x66,0x6B,0xFD,0xF0,0x0C,0xD9,0x94,0x3C,0xD2,0xBC,0xCD,0x4C,0x3B,0xF6,0xC0,0x01,0xB2,0x3A,0xB2,0x58,0x60,0x39,0x18, +0x34,0x84,0x8E,0x9D,0xB1,0xF1,0xA7,0x0E,0x97,0x2C,0xAA,0x2F,0xEF,0x88,0x65,0xD2,0x1B,0x79,0x35,0x1F,0x3E,0xD8,0x84,0xDB,0xE1,0x81,0xE9,0xFC,0xD6,0x2A,0x63,0x5A, +0xCB,0x20,0xD6,0x41,0xBE,0x90,0x6D,0x6F,0xB6,0x12,0x66,0xC6,0xAA,0x2B,0x06,0x95,0x29,0xE3,0x6E,0xA9,0x1C,0x65,0xDB,0xEA,0x4D,0xED,0x8B,0x6C,0xB4,0xF0,0xA3,0x4B, +0x2E,0x98,0xCB,0xF4,0x2C,0x45,0xDA,0xDF,0xAE,0x2B,0x35,0xD4,0xF5,0x55,0x55,0x84,0x3C,0xBA,0xD7,0xB4,0x43,0xC2,0x00,0xFD,0xCF,0xB9,0xC7,0x9D,0x5F,0x2B,0x3A,0x85, +0xF9,0x32,0xE6,0x26,0x53,0x2F,0xF3,0x39,0x8A,0xDE,0xC2,0x24,0x83,0x55,0xBD,0xFB,0x0C,0x77,0xCC,0xE9,0xB3,0x29,0xEA,0x23,0x98,0x66,0x93,0x23,0x46,0x40,0x29,0x50, +0x42,0xDF,0x7F,0x2E,0xDD,0x70,0x6E,0x5B,0x44,0xB4,0x92,0xEA,0x69,0x0B,0x6F,0xF9,0x75,0x78,0x40,0xF3,0xDC,0xE0,0xBA,0xF9,0xD1,0x23,0x3A,0x62,0x66,0x24,0x58,0x00, +0x7F,0xF7,0x8B,0x62,0xE4,0x4C,0x9C,0x04,0xC6,0xF9,0x92,0x65,0x8A,0x86,0xAA,0x29,0x64,0x23,0xF2,0x93,0x75,0xFA,0x77,0xC5,0x37,0x35,0xCF,0xAB,0xE8,0x39,0xE2,0xB7, +0x24,0x13,0x8B,0xF8,0x5A,0x21,0x72,0x77,0xEC,0xB6,0xC5,0x89,0x26,0x5A,0x58,0x62,0x67,0x82,0x58,0xD9,0x85,0xC5,0xD9,0x74,0xB7,0xDC,0xDF,0x0B,0xB3,0x29,0x11,0xE1, +0x96,0xA4,0x47,0x0C,0x41,0x96,0xC4,0xB3,0x81,0xA8,0x7D,0xF1,0xD2,0xDD,0x90,0x10,0xFF,0x00,0xD4,0xA9,0x51,0x96,0xBB,0x4B,0x46,0x69,0xC2,0x25,0xD5,0x24,0x42,0xB6, +0xC2,0xFC,0xB5,0x2A,0x2A,0x5C,0x66,0x55,0x0A,0xB2,0x95,0x3E,0x0E,0x54,0xBE,0x18,0xFA,0xEF,0xA4,0x92,0x3A,0x1F,0x4C,0x7D,0xAB,0xCC,0xE0,0xE5,0x07,0x9B,0x25,0x57, +0x8D,0x4D,0x96,0x56,0x2A,0x2F,0xEA,0x0D,0xF1,0x0B,0xC8,0x15,0x05,0x4C,0xA6,0x68,0x65,0x2D,0x65,0x57,0xA9,0xD5,0x1B,0x7A,0x5E,0xE5,0xB0,0xB2,0x8D,0xE8,0x65,0x6B, +0x67,0x4C,0xFB,0x82,0x72,0xCE,0x27,0x9B,0x9F,0x53,0x95,0xE5,0xEB,0x58,0xDB,0xBC,0xD3,0x21,0x91,0x0A,0x5B,0xCA,0xC2,0xC7,0x07,0x45,0x1D,0x16,0x55,0x41,0xFC,0xAA, +0x38,0xBE,0x42,0x64,0x50,0xE2,0x61,0xE2,0x49,0xB4,0xDF,0xD2,0xC3,0xED,0xE5,0x89,0x20,0x7C,0xC9,0xE0,0x57,0xAA,0x58,0x4C,0x1A,0xBF,0xF1,0x31,0x72,0x41,0xF3,0x24, +0x5C,0xFE,0xB8,0x1F,0x3C,0xAF,0xA7,0xA3,0xC9,0x24,0xF9,0xAA,0x1A,0x8A,0xDA,0x52,0xA4,0x37,0x2F,0x4B,0x32,0x93,0xEF,0xDB,0xD4,0x6F,0x8A,0xE3,0x5D,0x98,0xD5,0x72, +0x03,0xC4,0x4E,0xE6,0x92,0x96,0x8D,0x67,0x15,0x06,0xAA,0x30,0xCD,0x2A,0x1B,0x84,0x20,0xEA,0x17,0x03,0xD4,0x7E,0xA7,0x10,0xE5,0x19,0xD0,0x8A,0x54,0xAB,0xAE,0xA6, +0xE4,0x43,0x19,0x40,0x00,0x37,0x77,0x00,0x90,0x48,0xF6,0xB6,0x2B,0xD4,0x39,0x57,0x31,0x20,0xAC,0xCB,0xE5,0x9A,0xA2,0x12,0x54,0x82,0xED,0xAA,0x48,0xC0,0xD8,0x86, +0x3B,0x5F,0x6B,0x58,0xFA,0x77,0xC3,0xCF,0xE5,0xE5,0x53,0x4C,0xE4,0x48,0xB7,0x36,0x94,0xAE,0xEA,0x3A,0xFF,0x00,0xFB,0x8A,0xD6,0xEC,0xD5,0x06,0xCE,0xD9,0xE5,0x4C, +0x74,0x1C,0x8C,0xEA,0x34,0x32,0x34,0x87,0x98,0xF0,0x90,0x00,0x74,0xBA,0x28,0x5F,0x70,0x6C,0x41,0xF4,0xC6,0x47,0xC7,0x39,0xA5,0x3E,0x65,0x41,0x9A,0x31,0xA2,0xE5, +0xC2,0x8A,0x64,0x86,0x4B,0x58,0xDD,0xC9,0xB9,0x3D,0x2C,0x00,0x16,0xB7,0xA8,0xC5,0xC3,0x88,0xEA,0x9E,0xAE,0x9D,0xE9,0x91,0x4B,0x98,0x86,0x94,0x70,0x4F,0x84,0x03, +0xB7,0xD6,0xF6,0xDB,0x00,0x0C,0xA6,0x3C,0xDB,0x85,0x6A,0x72,0xDA,0x70,0x08,0x93,0x4F,0x34,0x3E,0xED,0x7B,0x11,0xF5,0x16,0xBD,0x81,0xC6,0x2A,0x7B,0x40,0xE0,0xD1, +0x41,0xE1,0x6A,0xF9,0xD2,0x9D,0xE3,0xAB,0x9F,0x5D,0x21,0x3A,0x95,0xD9,0x74,0x93,0x7E,0xB6,0x23,0xFB,0x6F,0xE5,0x8B,0x22,0xF1,0x6D,0x1C,0x6E,0x29,0xE9,0xAC,0x54, +0x0E,0xBB,0xF8,0x7F,0xCE,0x33,0xBE,0x2D,0xE7,0x65,0xFC,0x59,0x16,0x5B,0x02,0x4D,0x1D,0x3D,0x24,0x62,0x23,0x1B,0x58,0x82,0xD6,0x1B,0xD8,0x77,0xF7,0xC4,0x50,0xD5, +0x4A,0xAB,0x66,0x70,0x96,0xED,0x7B,0x93,0xEF,0x86,0x14,0xD0,0x64,0xCF,0x01,0x66,0xE6,0x39,0x62,0x77,0x00,0xE1,0x44,0xB9,0xE3,0xF3,0x4E,0xC6,0xDD,0xB0,0x91,0x56, +0x46,0xB0,0x56,0x69,0x2E,0x2F,0xE1,0xBF,0xFC,0x63,0xAD,0x4C,0x52,0xC7,0x10,0x0D,0xB6,0xAE,0xF6,0xC0,0x05,0x98,0xE7,0xCC,0xB4,0x88,0x58,0xD8,0x61,0x7D,0x6F,0x16, +0x18,0xA9,0xCB,0x23,0x11,0x7D,0x86,0xF8,0x49,0x30,0x78,0xF2,0xC0,0xFA,0xAC,0xC7,0xB6,0x11,0xCD,0xAE,0x55,0x92,0x02,0xD7,0xBE,0xE0,0x9E,0xC7,0xBE,0x25,0x38,0x36, +0xC0,0xFD,0x28,0x97,0x5F,0x38,0x8F,0xED,0x2B,0xF9,0xAD,0xDF,0x0B,0x6A,0xBC,0x11,0x4B,0x15,0x19,0x1A,0xC0,0x21,0xE5,0xBD,0xC7,0x9E,0x1D,0x4F,0x0C,0x73,0xDA,0x4D, +0x82,0x76,0x4B,0xEE,0x70,0xA6,0xB7,0x91,0x0C,0x9A,0x52,0x3D,0x89,0xB9,0xBF,0xFC,0x63,0xCF,0x71,0x69,0xD3,0x2C,0xB2,0x7D,0x95,0x69,0xE9,0x2A,0x90,0x13,0xF3,0xAC, +0x0D,0xB5,0x36,0xDB,0x9F,0xF6,0x9C,0x4A,0x89,0xA6,0x99,0x5A,0xA1,0x99,0xD9,0x8E,0xA0,0x19,0xAE,0xD6,0xF6,0x1B,0xFD,0xF0,0xF1,0x12,0x3A,0xAD,0x5A,0x84,0x42,0xDD, +0xD4,0xDC,0x74,0xB5,0xB0,0x0C,0x32,0xAC,0x4E,0xC5,0xC8,0x2D,0x7B,0x97,0x3B,0x0D,0x87,0x9F,0x96,0x3A,0x21,0x1A,0x5B,0x07,0x28,0xB1,0x7C,0x9F,0xF6,0xF5,0x0A,0x6A, +0xA9,0xC1,0x88,0x6E,0xA4,0xA0,0x3B,0xF6,0xB9,0x3B,0xE2,0x63,0x4F,0x95,0xCD,0x2D,0xCC,0x43,0x4D,0xB7,0x58,0xF6,0x5F,0xB6,0x22,0xAD,0x26,0xBB,0x4C,0xDC,0xC4,0x64, +0x53,0x73,0xB6,0xC6,0xDD,0xFF,0x00,0x4C,0x47,0x15,0x40,0x91,0x04,0x46,0x55,0x72,0x6F,0xAB,0xB0,0x5C,0x32,0x69,0xED,0x13,0x71,0x6B,0x90,0x6A,0x88,0x60,0xE6,0xBC, +0x73,0xBA,0x53,0x53,0xA9,0xDB,0xC0,0x18,0x91,0xF5,0x07,0x0B,0xA5,0x9B,0x28,0xA1,0xAF,0x88,0x98,0xA6,0xA8,0x49,0x18,0x04,0x24,0x0B,0x13,0xED,0xD4,0xF4,0xC3,0xA6, +0xAA,0xA2,0x4B,0x00,0x8B,0x52,0xCD,0xB1,0xF0,0xDC,0x03,0xF5,0xC2,0xEA,0xA4,0x9E,0x35,0xE6,0x5F,0x94,0x4F,0x78,0xD4,0x0B,0x0F,0x7B,0x62,0x94,0x9F,0x02,0x96,0x3A, +0x1A,0xAC,0xBE,0xAE,0x36,0x32,0x51,0xB2,0xB5,0xB6,0x56,0x0B,0xFB,0x12,0x6D,0x8A,0x5F,0x1B,0x41,0x53,0x96,0x66,0x34,0xB9,0xDE,0x5B,0xA9,0x45,0xF9,0x6F,0x4E,0x84, +0xE9,0x75,0xEE,0x18,0x1D,0xB1,0xF2,0x96,0x8B,0x31,0xA7,0x9C,0x81,0x53,0x35,0x4C,0x6C,0x2E,0xAA,0x64,0xB1,0x5D,0xF7,0xF2,0xC4,0x35,0xF5,0x10,0x54,0x29,0xA4,0xAD, +0xA3,0x55,0x98,0x8D,0x3A,0x95,0xB5,0xB0,0xF6,0xDB,0x15,0x82,0xA5,0xB0,0x07,0xA6,0xA4,0xA4,0x9C,0x35,0x76,0x55,0x4C,0xF4,0xF0,0xB0,0x3C,0xC8,0x82,0xD9,0x45,0xCF, +0xF6,0xEC,0x3E,0xD8,0x9E,0x47,0x35,0x91,0xB5,0x25,0x2F,0x81,0x5E,0xCA,0xDA,0xFB,0x1F,0xA7,0x7C,0x73,0x2D,0x8A,0x9A,0x9A,0x54,0x82,0x47,0x92,0x35,0x40,0x03,0x2D, +0x3A,0xDF,0x7F,0x56,0x37,0xFB,0x01,0x87,0x07,0x86,0x24,0xA8,0x98,0x4D,0x0C,0xC4,0x03,0xE2,0x21,0x45,0x81,0x3B,0x6F,0x8D,0x77,0xD1,0x7C,0x2E,0x3C,0x48,0xA9,0x65, +0xDC,0x37,0x24,0xF4,0xF3,0xCE,0xE0,0x5D,0x58,0xC6,0xF7,0x1B,0x06,0xDE,0xDF,0x70,0x31,0xD3,0x27,0xC8,0xCE,0x57,0x9D,0xCE,0x2A,0x2C,0x91,0x3F,0x8D,0x8B,0x0B,0x5B, +0x4A,0xED,0xED,0xD7,0x17,0xAA,0xFC,0x96,0xB7,0xF0,0xA1,0x13,0x14,0x76,0xB0,0x3A,0x16,0xDA,0xED,0xD0,0x9F,0x5D,0xF1,0x58,0xE3,0x19,0xE1,0xE1,0xDC,0xBA,0xA2,0xB6, +0xB2,0xAE,0x29,0x6A,0x99,0x08,0x10,0x96,0xD3,0xCC,0xDA,0xC6,0xDE,0x76,0xC6,0x28,0x24,0x5B,0x34,0xE0,0xE3,0x56,0x79,0xF7,0x8F,0x32,0xEA,0x4A,0xAE,0x26,0xAC,0xAD, +0x8B,0x30,0xA4,0x97,0xC5,0xAA,0xCB,0x2E,0xAD,0x8F,0xB8,0xEC,0x71,0x43,0x99,0x9E,0x24,0xB0,0xB5,0x89,0xD8,0x83,0x7C,0x5B,0xDA,0xBD,0x9E,0xAA,0xAA,0x5A,0xC2,0x65, +0x0C,0x8C,0x46,0xAE,0xA3,0x7B,0x81,0xEB,0x8A,0xCD,0x7C,0x34,0xED,0x7D,0x00,0x46,0x42,0x8B,0x1F,0x71,0x7C,0x3B,0x76,0x71,0x1D,0x28,0x2A,0xDE,0x79,0xD4,0x73,0x08, +0xB1,0xE9,0x87,0xF4,0x39,0x85,0x88,0xA6,0xAB,0x84,0x4D,0x01,0xF3,0x1B,0x8F,0x63,0x84,0x79,0x64,0x5F,0x2B,0x3B,0x54,0x4D,0xA0,0x82,0x2C,0xB7,0xE8,0x70,0x49,0x96, +0xB1,0xE6,0x78,0xC2,0xDD,0x62,0x25,0x98,0xA8,0xE8,0x3C,0xFD,0xB1,0x80,0x32,0xCD,0x62,0x81,0x20,0x67,0xA6,0x94,0x10,0xA3,0xFA,0x67,0xAD,0xB0,0x86,0x9C,0xAF,0xCF, +0xA4,0x8E,0x40,0x1D,0x0E,0x0E,0x66,0x54,0x93,0x4C,0xAC,0x41,0x6D,0xD6,0xC2,0xFA,0xBD,0xB0,0x25,0x55,0x25,0x40,0x88,0xCD,0x4E,0xCB,0x23,0x7F,0xA3,0xB8,0xC0,0x07, +0xE8,0xB2,0xC8,0xA8,0x9E,0x19,0x8D,0xC8,0x37,0x24,0xF7,0xED,0x85,0xD5,0xD3,0xC5,0x55,0x30,0x8D,0xC1,0x5B,0x8F,0xCA,0x08,0x3F,0x5C,0x57,0x56,0xAE,0xA5,0xA1,0xB1, +0x99,0x88,0xBE,0x39,0x3C,0xB2,0x43,0x4B,0xCE,0x8D,0xAC,0xFB,0x1B,0xF5,0xFE,0xEB,0x63,0x8D,0x45,0x2D,0x00,0xEA,0xC9,0x4A,0xC0,0x23,0x85,0x53,0xDA,0xFD,0xBA,0x58, +0x63,0xBC,0xAD,0x4F,0x2C,0x3F,0x2E,0xFA,0x34,0x80,0x59,0xD4,0x7E,0xD8,0xAB,0x45,0x53,0x3C,0x92,0x21,0x79,0x19,0x88,0x7D,0xAF,0xEE,0x71,0xD3,0x32,0xA8,0x9D,0x21, +0x25,0x25,0x65,0x24,0x6E,0x41,0xB7,0x7C,0x52,0x0A,0xD8,0x0F,0x1E,0x11,0x2A,0x15,0x79,0x34,0xC2,0x17,0x7D,0x27,0xAF,0xA6,0x17,0x0A,0x1A,0x61,0xCC,0x85,0x26,0xD0, +0xA5,0x6E,0x58,0x01,0xA9,0x8F,0x90,0x38,0x55,0xF3,0xB5,0x5C,0x8F,0xEB,0xB7,0xE4,0xC7,0xC6,0x25,0x68,0xA9,0xE6,0x04,0x87,0x29,0xBB,0x61,0x3C,0x77,0x66,0xDE,0xA8, +0x32,0x48,0x45,0x39,0x91,0x69,0x8D,0x90,0x30,0x55,0x24,0x76,0x06,0xE7,0x03,0x55,0xB4,0x8E,0x9A,0x35,0xB0,0x50,0x77,0x8C,0x9E,0xBE,0x67,0x10,0xD4,0xBB,0xA4,0x57, +0x56,0x6E,0xA3,0xBD,0xFB,0xE0,0x79,0xDD,0x9D,0x81,0x66,0x24,0xB3,0x6F,0xEB,0x8A,0xC2,0x29,0xAB,0x30,0x9E,0x58,0xF5,0xD1,0x98,0x92,0x59,0x43,0xB7,0x64,0x6B,0x5C, +0xFB,0xE2,0xAD,0x98,0x65,0xF9,0xAC,0x39,0x80,0xA8,0x5A,0x89,0x23,0xBB,0x5D,0xFB,0x83,0xE9,0x7E,0xD7,0xC3,0xF8,0x27,0x95,0x46,0x81,0x21,0x0B,0xCB,0x26,0xD8,0x44, +0x95,0x13,0xD5,0xF3,0x63,0xA9,0x95,0x9D,0x4C,0xA1,0x48,0x26,0xDB,0x11,0xE9,0x8E,0x88,0x2B,0x74,0x2C,0x9D,0x2B,0x03,0x8B,0x89,0x6B,0x72,0xEC,0xEE,0x9E,0x19,0xD1, +0x54,0xC8,0x4F,0x31,0x95,0xAF,0xB0,0xD8,0x0B,0x1D,0x86,0xE7,0x76,0xEB,0x6C,0x5A,0xB2,0x8F,0x88,0x1C,0xDA,0x3D,0x4C,0xC2,0x15,0xD6,0xCA,0x14,0x8D,0xF6,0xB5,0xC0, +0x3D,0xF1,0x88,0xD4,0xD7,0x56,0x0E,0x20,0x96,0x4F,0x98,0x72,0xC9,0x50,0x55,0x6E,0x6F,0x60,0x51,0x81,0x1F,0x5B,0x0C,0x76,0xCA,0x2A,0x67,0x93,0x3A,0x91,0x5E,0x4B, +0x85,0x52,0x46,0xC3,0x63,0xAB,0x17,0x51,0x48,0x59,0xCD,0xA7,0x46,0x9F,0xC4,0xDF,0x14,0xF3,0x6A,0x03,0x34,0xD4,0xC8,0xB5,0x11,0x00,0xB6,0x00,0x5C,0xAF,0x51,0xEE, +0x31,0x93,0x71,0x47,0x16,0x55,0x71,0x31,0x77,0xCC,0x6A,0x57,0xE6,0x95,0x43,0x0D,0x1F,0xDB,0x71,0xFB,0xE1,0xC7,0x10,0xED,0x48,0x95,0x0B,0xE1,0x94,0x38,0xF1,0x2E, +0xDD,0x4A,0x83,0xD3,0xAE,0x29,0xD5,0x27,0xE6,0x83,0xD4,0x4E,0x15,0xE5,0xD6,0x46,0xAB,0x00,0x6D,0x73,0xE5,0x89,0x64,0x7D,0x0D,0x07,0x6A,0xCA,0x8C,0xF5,0x55,0x14, +0xEE,0x46,0xED,0x66,0xB8,0x0C,0x2F,0xDF,0x10,0x57,0x54,0xA5,0x4C,0xFC,0xD4,0x6F,0x0C,0xAC,0x5D,0x93,0xFD,0x3B,0xEE,0x31,0x62,0x6A,0x4A,0x7A,0x8A,0x8A,0x81,0x34, +0x41,0x82,0x53,0x4A,0xCB,0xB9,0x16,0x21,0x49,0x07,0x14,0xB4,0x45,0x5A,0xBB,0xA8,0xB7,0x80,0xE2,0x63,0x0F,0xE4,0x8D,0x1E,0xBE,0xE8,0xC1,0x63,0x30,0xC6,0xDA,0x6E, +0x36,0x62,0xA0,0x9F,0xD4,0xE1,0xFE,0x55,0x1C,0x2E,0xDF,0x3E,0xF4,0xEA,0x64,0x0A,0xCA,0xAA,0x3A,0x3B,0x11,0x6D,0xF1,0x50,0xAA,0x76,0x4C,0xC1,0x4A,0x9B,0x6C,0x3F, +0xFA,0x0C,0x59,0x32,0x09,0x1C,0xD0,0x50,0x5D,0xCF,0x8F,0x9F,0x23,0x6F,0xD5,0x95,0x45,0x8F,0xD3,0x14,0x84,0x13,0x40,0x11,0x9D,0x23,0xC9,0x47,0x1B,0xD3,0x46,0x87, +0x96,0x0A,0xDA,0x20,0x00,0x2C,0xB6,0x36,0x07,0xAD,0xB7,0xB6,0x2B,0xF9,0x14,0x95,0x34,0x39,0x84,0xD5,0x35,0xD0,0x13,0x1C,0x06,0xCE,0x87,0x61,0xE2,0x52,0x05,0xCF, +0x98,0xB8,0x38,0xF9,0xC3,0x95,0x13,0xD4,0x56,0x3C,0x33,0xCA,0xF2,0x20,0x46,0x60,0xAC,0x76,0x06,0xE7,0x7F,0xD3,0x0E,0x73,0xF5,0x55,0xCB,0xA1,0x89,0x54,0x04,0x96, +0xEC,0xE2,0xDF,0x98,0x80,0x37,0xC2,0xC9,0x53,0xA0,0x3F,0xFF,0xD9,}; + diff --git a/examples/160 x 128/TFT_SPIFFS_Jpeg/JPEG_functions.ino b/examples/160 x 128/TFT_SPIFFS_Jpeg/JPEG_functions.ino new file mode 100644 index 0000000..ce4dd80 --- /dev/null +++ b/examples/160 x 128/TFT_SPIFFS_Jpeg/JPEG_functions.ino @@ -0,0 +1,172 @@ +/*==================================================================================== + This sketch contains support functions to render the Jpeg images. + + Created by Bodmer 15th Jan 2017 + ==================================================================================*/ + +// Return the minimum of two values a and b +#define minimum(a,b) (((a) < (b)) ? (a) : (b)) + +//==================================================================================== +// Opens the image file and prime the Jpeg decoder +//==================================================================================== +void drawJpeg(const char *filename, int xpos, int ypos) { + + Serial.println("==========================="); + Serial.print("Drawing file: "); Serial.println(filename); + Serial.println("==========================="); + + // Open the named file (the Jpeg decoder library will close it after rendering image) + fs::File jpegFile = SPIFFS.open( filename, "r"); // File handle reference for SPIFFS + // 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; + } + + // Use one of the three following methods to initialise the decoder: + //boolean decoded = JpegDec.decodeFsFile(jpegFile); // Pass a SPIFFS file handle to the decoder, + //boolean decoded = JpegDec.decodeSdFile(jpegFile); // or pass the SD file handle to the decoder, + boolean decoded = JpegDec.decodeFsFile(filename); // or pass the filename (leading / distinguishes SPIFFS files) + // Note: the filename can be a String or character array type + 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!"); + } +} + +//==================================================================================== +// Decode and render the Jpeg image onto the TFT screen +//==================================================================================== +void jpegRender(int xpos, int ypos) { + + // retrieve infomration about the image + 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; + + // 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 = minimum(mcu_w, max_x % mcu_w); + uint32_t min_h = minimum(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; + + // read each MCU block until there are no more + while ( JpegDec.readSwappedBytes()) { // Swap byte order so the SPI buffer can be used + + // save a pointer to the image block + pImg = JpegDec.pImage; + + // calculate where the image block should be drawn on the screen + 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 + if (mcu_x + mcu_w <= max_x) win_w = mcu_w; + else win_w = min_w; + if (mcu_y + mcu_h <= max_y) win_h = mcu_h; + else win_h = min_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.pushRect(mcu_x, mcu_y, win_w, win_h, pImg); + } + + else if ( ( mcu_y + win_h) >= tft.height()) JpegDec.abort(); + + } + + // calculate how long it took to draw the image + drawTime = millis() - drawTime; // Calculate the time it took + + // print the results to the serial port + Serial.print ("Total render time was : "); Serial.print(drawTime); Serial.println(" ms"); + Serial.println("====================================="); + +} + +//==================================================================================== +// Print information decoded from the Jpeg image +//==================================================================================== +void jpegInfo() { + + Serial.println("==============="); + 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(""); +} + +//==================================================================================== +// Open a Jpeg file and send it to the Serial port in a C array compatible format +//==================================================================================== +void createArray(const char *filename) { + + // Open the named file + fs::File jpgFile = SPIFFS.open( filename, "r"); // File handle reference for SPIFFS + // File jpgFile = SD.open( filename, FILE_READ); // or, file handle reference for SD library + + if ( !jpgFile ) { + Serial.print("ERROR: File \""); Serial.print(filename); Serial.println ("\" not found!"); + return; + } + + uint8_t data; + byte line_len = 0; + Serial.println(""); + Serial.println("// Generated by a JPEGDecoder library example sketch:"); + Serial.println("// https://github.com/Bodmer/JPEGDecoder"); + Serial.println(""); + Serial.println("#include "); + Serial.println("// Remove leading / from array name!"); + Serial.print ("const uint8_t "); + while (*filename != '.') Serial.print(*filename++); + Serial.println("[] PROGMEM = {"); // PROGMEM added for AVR processors, it is ignored by Due + + while ( jpgFile.available()) { + + data = jpgFile.read(); + Serial.print("0x"); if (abs(data) < 16) Serial.print("0"); + Serial.print(data, HEX); Serial.print(",");// Add value and comma + line_len++; + if ( line_len >= 32) { + line_len = 0; + Serial.println(); + } + + } + + Serial.println("};\r\n"); + jpgFile.close(); +} +//==================================================================================== diff --git a/examples/160 x 128/TFT_SPIFFS_Jpeg/SPIFFS_functions.ino b/examples/160 x 128/TFT_SPIFFS_Jpeg/SPIFFS_functions.ino new file mode 100644 index 0000000..a0f8225 --- /dev/null +++ b/examples/160 x 128/TFT_SPIFFS_Jpeg/SPIFFS_functions.ino @@ -0,0 +1,36 @@ +/*==================================================================================== + This sketch contains support functions for the ESP6266 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:"); + + 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 + while (spaces--) Serial.print(" "); + fs::File f = dir.openFile("r"); + Serial.print(f.size()); Serial.println(" bytes"); + } + + Serial.println(line); + Serial.println(); + delay(1000); +} +//==================================================================================== + diff --git a/examples/160 x 128/TFT_SPIFFS_Jpeg/TFT_SPIFFS_Jpeg.ino b/examples/160 x 128/TFT_SPIFFS_Jpeg/TFT_SPIFFS_Jpeg.ino new file mode 100644 index 0000000..f500a47 --- /dev/null +++ b/examples/160 x 128/TFT_SPIFFS_Jpeg/TFT_SPIFFS_Jpeg.ino @@ -0,0 +1,129 @@ +/*==================================================================================== + + This sketch demonstrates loading images which have been stored as files in the + built-in FLASH memory on a NodeMCU 1.0 (ESP8266 based, ESP-12E Module) rendering the + images onto a 160 x 128 pixel TFT screen. + + The images are stored in the SPI FLASH Filing System (SPIFFS), which effectively + functions like a tiny "hard drive". This filing system is built into the ESP8266 + Core that can be loaded from the IDE "Boards manager" menu option. This is at + version 2.3.0 at the time of sketch creation. + + The size of the SPIFFS partition can be set in the IDE as 1Mbyte or 3Mbytes. Either + will work with this sketch. Typically most sketches easily fit within 1 Mbyte so a + 3 Mbyte SPIFS partition can be used, in which case it can contain 100's of Jpeg + full screem images. + + The Jpeg library can be found here: + https://github.com/Bodmer/JPEGDecoder + + Images in the Jpeg format can be created using Paint or IrfanView or other picture + editting software. + + Place the images inside the sketch folder, in a folder called "Data". Then upload + all the files in the folder using the Arduino IDE "ESP8266 Sketch Data Upload" option + in the "Tools" menu: + http://www.esp8266.com/viewtopic.php?f=32&t=10081 + https://github.com/esp8266/arduino-esp8266fs-plugin/releases + + This takes some time, but the SPIFFS content is not altered when a new sketch is + uploaded, so there is no need to upload the same files again! + Note: If open, you must close the "Serial Monitor" window to upload data to SPIFFS! + + The IDE will not copy the "data" folder with the sketch if you save the sketch under + another name. It is necessary to manually make a copy and place it in the sketch + folder. + + This sketch includes example images in the Data folder. + + Saving images, uploading and rendering on the TFT screen couldn't be much easier! + + Created by Bodmer 24th Jan 2017 - Tested in Arduino IDE 1.8.0 esp8266 Core 2.3.0 + ==================================================================================*/ + +//==================================================================================== +// Libraries +//==================================================================================== +// Call up the SPIFFS FLASH filing system this is part of the ESP Core +#define FS_NO_GLOBALS +#include + +// JPEG decoder library +#include + +// SPI library, built into IDE +#include + +// Call up the TFT library +#include // Hardware-specific library for ESP8266 +// The TFT control pins are set in the User_Setup.h file <<<<<<<<<<<<<<<<< NOTE! +// that can be found in the "src" folder of the library + +// Invoke TFT library +TFT_eSPI tft = TFT_eSPI(); + +//==================================================================================== +// Setup +//==================================================================================== +void setup() +{ + Serial.begin(250000); // Used for messages and the C array generator + + delay(10); + Serial.println("NodeMCU decoder test!"); + + tft.begin(); + tft.setRotation(0); // 0 & 2 Portrait. 1 & 3 landscape + tft.fillScreen(TFT_BLACK); + + 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 + +} + +//==================================================================================== +// Loop +//==================================================================================== +void loop() +{ + // Note the / before the SPIFFS file name must be present, this means the file is in + // the root directory of the SPIFFS, e.g. "/Tiger.jpg" for a file called "Tiger.jpg" + + tft.setRotation(0); // portrait + tft.fillScreen(random(0xFFFF)); + + drawJpeg("/EagleEye160.jpg", 0, 16); + delay(2000); + + tft.fillScreen(random(0xFFFF)); + drawJpeg("/tiger160.jpg", 4, 0); + delay(2000); + + tft.setRotation(1); // landscape + //tft.fillScreen(random(0xFFFF)); + drawJpeg("/arduino160.jpg", 0, 0); + delay(2000); + + tft.fillScreen(TFT_BLACK); + drawJpeg("/Baboon160.jpg", 0, 4); + delay(2000); + + tft.fillScreen(random(0xFFFF)); + drawJpeg("/Mouse160.jpg", 0, 11); + delay(2000); + + // Create arrays from the jpeg images and send them to the serial port for copy and + // pasting into a sketch (used to make arrays fot the TFT_FLASH_Jpeg sketch) + + //createArray("/EagleEye160.jpg"); + //createArray("/tiger160.jpg"); + //createArray("/Baboon160.jpg"); + //createArray("/Mouse160.jpg"); + //while(1) yield(); // Stay here +} +//==================================================================================== + From c735d18a41e5ccb705fed221eafe13460f9a2237 Mon Sep 17 00:00:00 2001 From: Mark Date: Mon, 11 Sep 2017 03:32:34 -0400 Subject: [PATCH 005/287] fix for ST7735_REDTAB --- TFT_Drivers/ST7735_Defines.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TFT_Drivers/ST7735_Defines.h b/TFT_Drivers/ST7735_Defines.h index e182cf4..5fe2662 100644 --- a/TFT_Drivers/ST7735_Defines.h +++ b/TFT_Drivers/ST7735_Defines.h @@ -38,7 +38,7 @@ #define TAB_COLOUR INITR_GREENTAB128 #define CGRAM_OFFSET -#elif defined (ST6635_REDTAB) +#elif defined (ST7735_REDTAB) #define TAB_COLOUR INITR_REDTAB #elif defined (ST7735_BLACKTAB) From ca54325fbc989263a39f538ee2791b2485c1b641 Mon Sep 17 00:00:00 2001 From: Max Date: Sun, 1 Oct 2017 00:26:33 +0200 Subject: [PATCH 006/287] Add XPT2046 touch controller supports esp8266 + RPi displays OO --- User_Setups/Setup10_RPi_touch_ILI9486.h | 163 ++++++++++++++++++ .../Touch_Controller_Demo.ino | 76 ++++++++ 2 files changed, 239 insertions(+) create mode 100644 User_Setups/Setup10_RPi_touch_ILI9486.h create mode 100644 examples/480 x 320/Touch_Controller_Demo/Touch_Controller_Demo.ino diff --git a/User_Setups/Setup10_RPi_touch_ILI9486.h b/User_Setups/Setup10_RPi_touch_ILI9486.h new file mode 100644 index 0000000..b572e0f --- /dev/null +++ b/User_Setups/Setup10_RPi_touch_ILI9486.h @@ -0,0 +1,163 @@ +// 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 ###### + +// ModeMCU +#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_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 + +// ################################################################################## +// +// 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 diff --git a/examples/480 x 320/Touch_Controller_Demo/Touch_Controller_Demo.ino b/examples/480 x 320/Touch_Controller_Demo/Touch_Controller_Demo.ino new file mode 100644 index 0000000..6eda034 --- /dev/null +++ b/examples/480 x 320/Touch_Controller_Demo/Touch_Controller_Demo.ino @@ -0,0 +1,76 @@ +#include "FS.h" +#include +#include +TFT_eSPI tft = TFT_eSPI(); + +#define CALIBRATION_FILE "/calibrationData" + +void setup(void) { + uint16_t calibrationData[4]; + uint8_t calDataOK = 0; + + Serial.begin(115200); + Serial.println("starting"); + + tft.init(); + + tft.setRotation(2); + tft.fillScreen((0xFFFF)); + + tft.setCursor(20, 0, 2); + tft.setTextColor(TFT_BLACK, TFT_WHITE); tft.setTextSize(1); + tft.println("calibration run"); + + // check file system + if (!SPIFFS.begin()) { + Serial.println("formating file system"); + + SPIFFS.format(); + SPIFFS.begin(); + } + + // check if calibration file exists + if (SPIFFS.exists(CALIBRATION_FILE)) { + File f = SPIFFS.open(CALIBRATION_FILE, "r"); + if (f) { + if (f.readBytes((char *)calibrationData, 12) == 12) + calDataOK = 1; + f.close(); + } + } + if (calDataOK) { + // calibration data valid + tft.setTouch(calibrationData); + } else { + // data not valid. recalibrate + tft.calibrateTouch(calibrationData, TFT_WHITE, TFT_RED, 15); + // store data + File f = SPIFFS.open(CALIBRATION_FILE, "w"); + if (f) { + f.write((const unsigned char *)calibrationData, 12); + f.close(); + } + } + + tft.fillScreen((0xFFFF)); + +} + +void loop() { + uint16_t x, y; + static uint16_t color; + + if (tft.getTouch(&x, &y) { + + tft.setCursor(5, 5, 2); + tft.printf("x: %i ", x); + tft.setCursor(5, 20, 2); + tft.printf("y: %i ", y); + + tft.drawPixel(x, y, color); + color += 155; + } +} + + + From 944e505f3677cfb051d6c7aa0d873850fa261c78 Mon Sep 17 00:00:00 2001 From: Max Date: Sun, 1 Oct 2017 00:32:30 +0200 Subject: [PATCH 007/287] Add XPT2046 touch controller support supports esp8266 + RPi displays --- README.md | 3 ++ TFT_eSPI.cpp | 149 +++++++++++++++++++++++++++++++++++++++++++++++++++ TFT_eSPI.h | 21 ++++++++ User_Setup.h | 54 ++++++++----------- 4 files changed, 195 insertions(+), 32 deletions(-) diff --git a/README.md b/README.md index 606516d..d059391 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,8 @@ # 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). diff --git a/TFT_eSPI.cpp b/TFT_eSPI.cpp index 0189854..5d22513 100644 --- a/TFT_eSPI.cpp +++ b/TFT_eSPI.cpp @@ -73,6 +73,12 @@ TFT_eSPI::TFT_eSPI(int16_t w, int16_t h) pinMode(TFT_CS, OUTPUT); #endif +// Configure chip select for touchscreen controller if present +#ifdef TOUCH_CS + digitalWrite(TOUCH_CS, HIGH); // Chip select high (inactive) + pinMode(TOUCH_CS, OUTPUT); +#endif + #ifdef TFT_WR digitalWrite(TFT_WR, HIGH); // Set write strobe high (inactive) pinMode(TFT_WR, OUTPUT); @@ -3641,6 +3647,149 @@ void spiWriteBlock(uint16_t color, uint32_t repeat) } #endif + +/*************************************************************************************** +** 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; + T_CS_L; + + SPI.setFrequency(SPI_TOUCH_FREQUENCY); + + // 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); + + if(tmp == 0 || tmp == 0x3ff){ + T_CS_H; + SPI.setFrequency(SPI_FREQUENCY); + return false; + } + + *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.setFrequency(SPI_FREQUENCY); + + 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. +***************************************************************************************/ +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; + + *x=(x_tmp-touchCalibration_x0)*_width/(touchCalibration_x1-touchCalibration_x0); + *y=(y_tmp-touchCalibration_y0)*_height/(touchCalibration_y1-touchCalibration_y0); + + return retVal; +} + +/*************************************************************************************** +** Function name: calibrateTouch +** Description: generates calibration data for touchscreen. +***************************************************************************************/ +uint8_t TFT_eSPI::calibrateTouch(uint16_t *data, uint32_t color_bg, uint32_t color_fg, uint8_t size){ + uint16_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++){ + while(!getTouchRaw(&x_tmp, &y_tmp)) delay(100); + values[i*2 ] += x_tmp; + values[i*2+1] += y_tmp; + } + values[i*2 ] /= 8; + values[i*2+1] /= 8; + } + + // TODO: build in orientation check! + 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 + + if(data != NULL){ + data[0] = touchCalibration_x0; + data[1] = touchCalibration_x1; + data[2] = touchCalibration_y0; + data[3] = touchCalibration_y1; + } +} + + +/*************************************************************************************** +** Function name: setTouch +** Description: imports calibration data 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]; +} + +#endif + /*************************************************** 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 25c86b6..eff876f 100644 --- a/TFT_eSPI.h +++ b/TFT_eSPI.h @@ -108,6 +108,16 @@ #endif #endif +// chip select signal for touchscreen +#ifndef TOUCH_CS + #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) +#endif + + #ifdef TFT_WR #if defined (ESP32) #define WR_L GPIO.out_w1tc = (1 << TFT_WR) @@ -417,6 +427,13 @@ class TFT_eSPI : public Print { 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 + virtual size_t write(uint8_t); private: @@ -435,6 +452,10 @@ inline void spi_end() __attribute__((always_inline)); uint32_t lastColor = 0xFFFF; +#ifdef TOUCH_CS + uint16_t touchCalibration_x0, touchCalibration_x1, touchCalibration_y0, touchCalibration_y1; +#endif + protected: int32_t cursor_x, cursor_y, win_xe, win_ye, padX; diff --git a/User_Setup.h b/User_Setup.h index a106fd9..70f798a 100644 --- a/User_Setup.h +++ b/User_Setup.h @@ -14,16 +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 - -// 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 +#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 @@ -36,10 +31,14 @@ //#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 @@ -53,7 +52,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 (RS/AO)to NodeMCU pin D3 +// 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) @@ -61,8 +60,6 @@ // // 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 @@ -76,35 +73,26 @@ // 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 ModeMCU - use pin numbers in the form PIN_Dx where Dx is the NodeMCU pin designation +// ###### EDIT THE PIN NUMBERS IN THE LINES FOLLOWING TO SUIT YOUR SETUP ###### +// ModeMCU #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_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_WR PIN_D2 // Write strobe for modified Raspberry Pi TFT only -// ###### 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) +// 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 -//#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) +// Section 2. Define the way the DC and/or CS lines are driven // // ################################################################################## @@ -156,12 +144,14 @@ // #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 +#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. From 8813ed35b150429012ea00966d7ab0989dc3c26d Mon Sep 17 00:00:00 2001 From: Max Date: Sun, 1 Oct 2017 00:56:05 +0200 Subject: [PATCH 008/287] should add esp32 support. have no hardware to test. --- TFT_eSPI.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/TFT_eSPI.cpp b/TFT_eSPI.cpp index 5d22513..732b83e 100644 --- a/TFT_eSPI.cpp +++ b/TFT_eSPI.cpp @@ -3659,6 +3659,12 @@ uint8_t TFT_eSPI::getTouchRaw(uint16_t *x, uint16_t *y){ 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); // Start bit + YP sample request for x position @@ -3670,6 +3676,7 @@ uint8_t TFT_eSPI::getTouchRaw(uint16_t *x, uint16_t *y){ if(tmp == 0 || tmp == 0x3ff){ T_CS_H; SPI.setFrequency(SPI_FREQUENCY); + spi_end(); return false; } @@ -3685,6 +3692,7 @@ uint8_t TFT_eSPI::getTouchRaw(uint16_t *x, uint16_t *y){ T_CS_H; SPI.setFrequency(SPI_FREQUENCY); + spi_end(); if(tmp == 0 || tmp == 0x3ff){ return false; From b6d234af709dc608116e69172b377043334ed3bd Mon Sep 17 00:00:00 2001 From: Max Date: Sun, 1 Oct 2017 00:58:33 +0200 Subject: [PATCH 009/287] should add ESP32 support. do not have hardware to test. --- .../480 x 320/Touch_Controller_Demo/Touch_Controller_Demo.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/480 x 320/Touch_Controller_Demo/Touch_Controller_Demo.ino b/examples/480 x 320/Touch_Controller_Demo/Touch_Controller_Demo.ino index 6eda034..4e03770 100644 --- a/examples/480 x 320/Touch_Controller_Demo/Touch_Controller_Demo.ino +++ b/examples/480 x 320/Touch_Controller_Demo/Touch_Controller_Demo.ino @@ -60,7 +60,7 @@ void loop() { uint16_t x, y; static uint16_t color; - if (tft.getTouch(&x, &y) { + if (tft.getTouch(&x, &y)) { tft.setCursor(5, 5, 2); tft.printf("x: %i ", x); From 548fcca258c55d780d0bfa6273ed479ab1268499 Mon Sep 17 00:00:00 2001 From: Max Date: Sun, 1 Oct 2017 21:34:11 +0200 Subject: [PATCH 010/287] now calibration works with all orientations. --- TFT_eSPI.cpp | 69 ++++++++++++++++--- TFT_eSPI.h | 1 + .../Touch_Controller_Demo.ino | 8 +-- 3 files changed, 66 insertions(+), 12 deletions(-) diff --git a/TFT_eSPI.cpp b/TFT_eSPI.cpp index 732b83e..a335193 100644 --- a/TFT_eSPI.cpp +++ b/TFT_eSPI.cpp @@ -3712,8 +3712,21 @@ uint8_t TFT_eSPI::getTouch(uint16_t *x, uint16_t *y){ if(!retVal) return retVal; - *x=(x_tmp-touchCalibration_x0)*_width/(touchCalibration_x1-touchCalibration_x0); - *y=(y_tmp-touchCalibration_y0)*_height/(touchCalibration_y1-touchCalibration_y0); + if(!touchCalibration_rotate){ + *x=(x_tmp-touchCalibration_x0)*_width/touchCalibration_x1; + *y=(y_tmp-touchCalibration_y0)*_height/touchCalibration_y1; + if(touchCalibration_invert_x) + *x = _width - *x; + if(touchCalibration_invert_y) + *y = _height - *y; + } else { + *y=(x_tmp-touchCalibration_x0)*_height/touchCalibration_x1; + *x=(y_tmp-touchCalibration_y0)*_width/touchCalibration_y1; + if(touchCalibration_invert_x) + *x = _width - *x; + if(touchCalibration_invert_y) + *y = _height - *y; + } return retVal; } @@ -3723,10 +3736,11 @@ uint8_t TFT_eSPI::getTouch(uint16_t *x, uint16_t *y){ ** Description: generates calibration data for touchscreen. ***************************************************************************************/ uint8_t TFT_eSPI::calibrateTouch(uint16_t *data, uint32_t color_bg, uint32_t color_fg, uint8_t size){ - uint16_t values[] = {0,0,0,0,0,0,0,0}; + 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); @@ -3770,17 +3784,52 @@ uint8_t TFT_eSPI::calibrateTouch(uint16_t *data, uint32_t color_bg, uint32_t col values[i*2+1] /= 8; } - // TODO: build in orientation check! - 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 + + // 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. + 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 + } 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; + + // 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); } } @@ -3794,6 +3843,10 @@ void TFT_eSPI::setTouch(uint16_t *data){ touchCalibration_x1 = data[1]; touchCalibration_y0 = data[2]; touchCalibration_y1 = data[3]; + + touchCalibration_rotate = data[4] & 0x01; + touchCalibration_invert_x = data[4] & 0x02; + touchCalibration_invert_y = data[4] & 0x04; } #endif diff --git a/TFT_eSPI.h b/TFT_eSPI.h index eff876f..d3a37da 100644 --- a/TFT_eSPI.h +++ b/TFT_eSPI.h @@ -454,6 +454,7 @@ inline void spi_end() __attribute__((always_inline)); #ifdef TOUCH_CS uint16_t touchCalibration_x0, touchCalibration_x1, touchCalibration_y0, touchCalibration_y1; + uint8_t touchCalibration_rotate, touchCalibration_invert_x, touchCalibration_invert_y; #endif protected: diff --git a/examples/480 x 320/Touch_Controller_Demo/Touch_Controller_Demo.ino b/examples/480 x 320/Touch_Controller_Demo/Touch_Controller_Demo.ino index 4e03770..f0b14d8 100644 --- a/examples/480 x 320/Touch_Controller_Demo/Touch_Controller_Demo.ino +++ b/examples/480 x 320/Touch_Controller_Demo/Touch_Controller_Demo.ino @@ -6,7 +6,7 @@ TFT_eSPI tft = TFT_eSPI(); #define CALIBRATION_FILE "/calibrationData" void setup(void) { - uint16_t calibrationData[4]; + uint16_t calibrationData[5]; uint8_t calDataOK = 0; Serial.begin(115200); @@ -14,7 +14,7 @@ void setup(void) { tft.init(); - tft.setRotation(2); + tft.setRotation(3); tft.fillScreen((0xFFFF)); tft.setCursor(20, 0, 2); @@ -33,7 +33,7 @@ void setup(void) { if (SPIFFS.exists(CALIBRATION_FILE)) { File f = SPIFFS.open(CALIBRATION_FILE, "r"); if (f) { - if (f.readBytes((char *)calibrationData, 12) == 12) + if (f.readBytes((char *)calibrationData, 14) == 14) calDataOK = 1; f.close(); } @@ -47,7 +47,7 @@ void setup(void) { // store data File f = SPIFFS.open(CALIBRATION_FILE, "w"); if (f) { - f.write((const unsigned char *)calibrationData, 12); + f.write((const unsigned char *)calibrationData, 14); f.close(); } } From 80b5d91ba4bb96d107ee7a4066b8141b40e76be2 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Wed, 11 Oct 2017 00:59:14 +0100 Subject: [PATCH 011/287] 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 012/287] 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 013/287] 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 014/287] 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 015/287] 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 016/287] 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 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 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 017/287] 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 018/287] 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 019/287] 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 020/287] 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 021/287] 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 022/287] 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 023/287] 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 024/287] 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 025/287] 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 026/287] 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 027/287] 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 028/287] 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 029/287] 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 030/287] 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 031/287] 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 032/287] 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 033/287] 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 034/287] 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 035/287] 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 036/287] 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 037/287] 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 038/287] 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 039/287] 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 040/287] 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 041/287] 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 042/287] 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 043/287] 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 044/287] 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 045/287] 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 046/287] 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 047/287] 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 048/287] 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 049/287] 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 050/287] 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 051/287] 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 052/287] 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 053/287] 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 054/287] 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 055/287] 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 056/287] 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 057/287] 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 058/287] 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 059/287] 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 060/287] 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 061/287] 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 062/287] 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 063/287] 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 064/287] 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 065/287] 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 066/287] 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 067/287] 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 068/287] 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 069/287] 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 070/287] 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 071/287] 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 072/287] 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 073/287] 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 074/287] 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 075/287] 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 076/287] 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 077/287] 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 078/287] 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 079/287] 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 080/287] 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 081/287] 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 082/287] 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 083/287] 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 084/287] 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 085/287] 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 086/287] 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 087/287] 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 088/287] 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 089/287] 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 090/287] 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 091/287] 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 092/287] 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 093/287] 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 094/287] 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 095/287] 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 096/287] 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 097/287] 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 098/287] 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 zcmdUXd3==Bx&JxuI`8cJlF2fYNit;LX9%DfCLsg}5FvzBWDBAMP!^GoB3PuTNC8De zii)UFDaA;sT5EAduh&{j5h+z`tqYgyQfn>OLh|PKJ!d8%*!FjOf4{$O;C<%2+d0p9 z_UAcghEPI?jjSgkxn|Quh;kah|tm z$;#`~=da-X>lb${Uz$2kjvz$7x^vn5^z)-1!~GkV%)LI1&zL;CA6hzhNe4dhH>Qyg z;m_ABTd^{A-ZB)Qf4XLQNBa5kz=z}#BECnr5gC2$rq>dpx1>I$17xA#6p3I;z5Vxa za?_X)Nsv&%V!2BPZIl)CFnfv-ldLDgA(1t@(DUS#bCHbbv~Jk&5tk4$?b7t&NBw@qoEI)ludK6teM&-Qnb{};QH$RF^egnx{*H}&-ahB#zbUjVfv@J}Hs zF%XRyiHVqrg;*iccH$sT;sQlI#7lg{PXZ)^1W6{zBH3gX$sw1M5PAzk4k9FvSdiq>cfvu1xs=p{i6cppG>}Fz ziZqeYq?wE%W63zuLdKH`WFl!Lmyt2Ym|KBODH*1Z)m!?G zfvbDOL-H26iyov)Xd8K%yo8xPPmYjZkmtzH$+P5VnsykFcU92OoG*3zHS2AZTM z@-8_=PLdPk82L4Mi~N$jPF}^xFJfdi>NDhNjChzFN{@Pg?8lgU$ZoO<;{`!mBjy2# zO?W+Sm(yXlSuJLhQ8TEDEQtaoC5NCF4WZlT-5yCUY%eW2R76Na+x6X~c$%+nI{z5K zKe~-Y)4OSd{X3URON#NoTPP|mNdY>qn?#aNQZYXnY6>lyyP#Xl$7iTpYKY7YEqF4K zxV>#!7=KTaaOU(@G@m3=eyC|tx0*j{Za2AXS~%D(Hw>K~oV#H9W-M zvcGAqjbGL}G&WJ1Ve@M?Q%|edZL(>8jMy&D5z6H)9QW*mTM;BPG0&zVk*|=|0i^5e z%gQRKBf|d)<$E65EpO>PE|ju^+2156aG3X!L)sE!vNR!R5(*S*R07oJ7DSqFv6;}f zS959xvt-W6&dLmC1bl;a;(|(KR~!A9Z8$aC`uh6f3K|x}F8r?+!z!9MA})T!!>%w6 z@reHM9GdCaNdxzE-Ayy%`X_T}`EdQjj>mTDr|<6CuK!^;&6%ygSw|~(iEa96agsib zKBiCGuAjL_pFz9WaS!!vr*NP`-$#8ni+05ay)7Y)iAy3xu!Xe}YNSQf=2Fcb1EtwC zlZG8Rng>jdh6Jb4AdC-4R4$={lNLpUIhom3MJU4@@CVvmN^5HhfO!NT#WYv(xV<^( z1Z-42ky^~b5iP?bv3PAYJ={E@c>--)XLQ=SMz=;nw?4J>sjgM}!_%m=U}SR5(pAD< zz0kI_y>YEr@fWYfen~9jlFaM&9$B&bMB%1&FAKwGjku+`^PbS1OfXyePAd!KF36M* z_PiX{xGgbuWJ2)dh-PZgvTZO>Ingjt;xQqZRjN@{k{Zl)S_94YYcq7{O{}_KD&uZ%byW-#jpQnHP+J}Jj0~A&B=CeO zCrvr64Y#~<#~rV}w)u`%4$f|Dm_56pakjXIL3Y;tDz3kB$L3e{2US&51Nvved#Y+s zGpJK)XLs&+_0>Drqd0mVNWDF~F?Hj$309Y++9a1%uFzZt)haoyus19EK9^U>t?*gX z6PJ~RR7E0?Nx^Lqv^aIt5=qISej29E47XRYxQ0NXMJa^SfZykm?V?r}7ix2biTQR} zR%scXAXYk*@y24Jh?l1LUR06dw9msCo+3&VRI$-$JX{s4r#>~pgn&eH31dDNfoe!e zoKa7>;j$}6HLbtzz>Y0llC;U~wdPsY(Niw3BV@T@n<(fXe)_Kd>4HVU%s20%V>g-w zmtES@Had1?mFtmPw>|pfZQfF^!4y`$QY?Puv^~GzL;X`~KfdAUn8K+Qw?1>$BbgZ; zW{AtkH}W3X0C&PH5c3sG{i&Q%zp@G!wEmmj@*b}2lx&28ya-*l5r4vA)Tmh#1ZTT! z!7C0n>-u_Vt49$$Zl})^E)Z&KoV1%dUii0HUeW(_=e@8T)|>Q)j(w!xzutNeU2^nA z+6f#2=(VlCS5+fsn*qIwrop{>ogTNKhHGQa+8QBRON+Pe-g%e)<;$=9+w=04yY;)D z`=!3)Uh8@~|BuJ$x*Yqh?pE`d#QJNgIVT!V1Kr!GxOtKc6PJ(buKa0CwBb9f9280Y!-m?^-wM6D%HE zseLo;w)(8X3hfKcnrryNXkqEVQ++3Z+lQDlWLVU+283Lx3Bo*;Rm2Bbo~I_pVyzS7 z@d>ly6DG#sD)D|_S6@)DFMXmZI;tNex0E~^d|mPR16`%X)UTiJ|BB#|iw17>0{`kg#P7;6Kn z&8ipPHmiT`+;n;zDck9Cr0j#EhE!J(@yL(%-1nm&-FM&qxAgb_@{Rs3&Hv^vG$QWV zd+5mr_4Ws!c;Z2NK%ex5{xQw^oF4p~X6hd^3T+4YKt6)7Cy;Q!@T*iK!gy+R%hbh( z#ROdxLo%CO%cR-?EVk3AYVhly`wePNxipW?I`;#!Qw>`1wH<{Ifvqy`TQs;xR={>v|p%I+|+lUvb@L?Y7ZxtsVWn_}pun#=UGX!omqB+g4t>{JuMG zI^B6~K|%e$brr5pq2mrMX@Sn1Q^U{%&~eW=nv`nNW=JYL2W3 z1t18+&6UE0p^-wyN+WR9+_&LY%ENv2{juFke*F8w;>Alhtgd?SCVlfXYW(d_x82^_ zdhN7XtEIrHnbY6c)3~Ih@}=8v{iq=``101Sn;r`nEpMK>Y#GC`4k_Cb$X*tyNfZM; z8lXX%A)A8G2{Fs;(t<`%F3Txcs2leti8NcLISfU_Wh^#w#2v6U+?k|(D&yZJG#sVX zG>0B~?q-w27PYLKs~R%&Zvv{?`{5d4&HMBgdl)a*7P!{r3!96_W}f&&5shZuC2jZ}7e-H3a}0yVTWDTL@i3kx;05| z7BhfyA;S!+Tm-81+jY*wh@!>^`{4oo4%53T%OAge{eus0bs3F@b#H#6fBc7c^xqGQ z)7heEGC62c_-Ne5#I27Uxb3$4DSayI>0t`u$g6vs3Qyt?5Glq0tc{yrv@uG;>i_3QR; zT~*&aZ|vB4&GqMo40-#9KRi|ty>{NX(ak@dKmVB@jO!GM_0j8ZdE$v%RzKpn%Q~}R z{DKAJ8)jN7i>^%Ef8xabBd;u+`Li459~w1!T+8M|^KX2E@f;NbX#!@J;zuEVs%q{>>Q(V8D3IZNHf3gCe!@}1h;E-v5!pr;#JLH`6 zlca;^C(+Z7JVNh(jAdG6S>I_fOP&V`MaZy3c_b`kd$nBZv1Ch%i`u;&iY^hjh&02c zO|WMWIid+uTwX~L@-ZRqmzfh8L^Cm!hajzDRuSsW(k%qsv@JG~Q*Y>wm;U(NlTU4# zckP;$_gmc7sPRIAy!6k<4!oVQm$t24vUY=J&W2{N%9HXYJZC1UNaQ=H!)1{`U5_AX zaC51uj@e|8iMd>)2p6(b;Q~_T;)+75(Qs|bUkoU;nAR#v^kd@9;tl$nZl5gbztLNA z+)|ct9St7s-7HByw|wMW(rUM8Hi>>E%6`MUbX1R~*bEjsD7Ony!&q)l3Q2t;=JOg= z$w{>#QNihQ!AR%8@n$3X6C@)-KMfI+myXJiWVtX$XeN0eU~!6oX&;<&W@^gvMZ%z{ zPv%Z9G7t^Gg43<^>+7%Id2j8aD!L`hfAuUqMBjb(*Twn^hoAoJ_q{&9swxKm%h#>G zZKI;lc%U#)KlX(?A2{Rw{cG=h%!3B$Ta(QYV9`0N5Bo5KV#KN80tDZAu!v4-fnR3v z!jOVIr{ru@z;jy4P=z@{aKN>c#n(J;s78=j5M^0L3@#7<2a+c-x$d!Hvu4klHS&SY z!Xk^?9CduZ_hoF8U?padb6xz!pRMrk2hBrBbYcl>auy% z_4tNQ=QLbh5YFUY2lU&s z+jztE3;u9adh`6*;_BwNZ@=l?vDFpls|V9_)Yz-^-~9A3{mXfmjRLm)wAE0btxl9y zP@}61)Rm}1&?F_3`XC#2c<-u;vP?x1RAdE>_B^{x<7rCA`+fEQgywWcyLfy9%R}{L z|0?x`{V$(&OJK(^X!VD^vDgvFuQY3;$!_l*cNP6;BZ@Hk0u}@R z5whKqmfkO{c3bZUFP`;VtiAgNLZ_5W%iDop4eCb2-PC5$$|2aLAU!a2c#V=Fm?2mk zSr8aeg6$Bkik3+$%S**EUaLcHR3fmUu>r-rnD~xxIwVM=`!7g7ZPn|U?3-^Eu0eeD zT?O!>XmGIt&_6*13}5z~XwI>tmLqlk>u=fv8rxxLAm6b#28%77uVHCUhyus>F z()mo9xlk*I@x0*zTfx`82tJNr#y(b;b!n>EX+X{@G$SM*aO3p@^7UObUxWOhoMuo| zmC2ET$x#{wgUvK}D0mxq#Mzv-$lk4Pkw>*^5FSPmotcQ!9JD#)_Q*7sQIID9yc8Ub zr_$4u&tu6Olk`+=IR3?J51uZ1)}t!7&0V@yPz(W@11#u)U;lOA4>q{WUQ=`DjY}lA zEbijQnNuL`W2W(0SCg_tqOi~frKH&3CP-lHjIOTQ+5 zms#@o-jhDPU4OETTgOsFJ|iUQD1FOhQZ$RoVo2B!k=c*{DM;ivsh3(E9@q>jsb&|# za_a5pE33B;HXEtA^yjO!@szRP7z?V&Wht=S+?zkhN~CaG2bnW`$*{rLS$)UUa>Nn= z&@(qc5fAz`tI?vFjiToBIN9QmLDI?uRsc_z`M?Bf{n#pM}IU(~v0~l@UJ0`C| zGl(oI8nglr8fIVx$_1zk5uXeqW_6m#3a^2%+BK(tEG|~dM}FGVKIi=#@7F(Hn5ODpM(y>t z%>N`72kBF6n09~jxBBtVm&D>J!ahnfJEzQK1poMv-RGJv-MNmQK?TAnX7XBWXNS90~`aX85B8*F!KfHX==JSh8z zv_HJH7y&eLX;Knim!vqy~j zN36k2B8Vi7m`NTuQ)8vsss^j)@*+l19SRq6Vnp_63TqjD1nr|`lPt5kSyR~AUz=hT z0tgYx23ZsZsZ?;8fAQ)8EUU*Ydxd8D6eZMrF3>JLOX*LZ?mhg&UB7%BxM5cgf1)ZaUC_GQ)SyzBYOyN5h-^Ics$@~s=uIU0Ku*kWZWS`Q*x4;f{_Mk%Td z3Y@-IGbn=7Lw&T_PC$2VAO=@3_@dXvWES@HzKX~Sj$7XGeHLP=vdWyaL6zP#ShAHb z*t*vFb;zaDzL`lt-)XG=Zz1`lG7*iq?c6hFm{qexX^2~@G0aj05&1QTTu`@Yzoi1H z1{o^V;g-Uoyo+2UEEgOnH(4o9P4##1@1%aB-zRmw`@y4ht@v8M^|G7J|84()1N$Yl z|BA!dQ+NFpvsuOno64dE57vYI1U6kWnqk~E6=j|ZqZu~UmrfESv8z0d33at!WMoMxj_>$yw(rYB3Bskw}1r{V)*Sp5b@A0R`W2PzapV+*__ zVh+Kv7|q2)@^ZsDSs7kX1Un%>6K9n<1guI2utNNBF;#fd1lOlsD25HD)&10Mhu?G| zTCfW3`ok_+MU`fgzT0M26}Ns+NcQ&7SuTTOwTRp4&E9~;;6#nScUiAwMX_GB1l{y& z-RjFQX*R`W6?%m2R;R(LT5_@O!Sa8(h3jiHk!=ZSezR!y`JHwWxKbfX+N-Ub{U(2YWyA{eP=rADU%M&&{U zp6x!-WTMtUYx}GI>Fl2skHZ5M3VK*v&9VmLy7T$ss@^P%$6R1qCyiybcEgv_8=4Un zM~r$B?ixFCcug}$q=qoXcy?+GJ9!50KxKU@)`?P8O^oD9Oy^u6s|Ui!EF0DyDf28Y)OQN))NIt8 z)Ep+5iiD{Msl^K{ayqc$Els5+l(;<1vM>wm2*;~c)I*13cSNp|Md2&ySv|tjsx@ou zF2TG}-g0imDOS3cu!}?ToWh9ph(KG#J*1HN=4^{52HaYfDg?qIWKlz?JCE>!LXIYQ z>{=mA|I7$Xd3Kg?xm`p->_XuN#Sc_IhSQXV?%g{& z%s%hvDO0EaXGMd4!t4QySJRAp)~jmZzMb11c}cR`T9f0((lhmCjK{}rf28*VE@q5Q ztC4$0uwEWTO}Q#S-4^&QcQ^=?Bh(Vk4MW}D-aD)PC3dfAb@Clb_0RsI&&rTYvSPBMki>#) z)A_wW+_h<^-R3qN`>$WV*J}-knQSE#`%=U<&^bs-69qoR*hJO5SQPRAXA@P!R?b_G znJqPAJ5b7VF}9{DnO;<3M9xEDDaxt_!te*W9@zP!HN!l_y!w^*&r$msYUwcB%);4w z?%cXd)56N}uTK27?}T8ru!x`G@Fl2LhD^DxfM&p}hs#hd&8J|rLkNdmnhSB58qJqP znuWDsp}?((0XoA%z4DYS>?RN@QhtmPFYU+D)Z?5@$vYE2E{FX%BO1=DMn8zb85P0d z9%NR>_8olS&K90BuO9hp{lBR1FXA40HI~~sC6Ny|rOk@LVHWo9-tvPVvXuEaB}aeP zo1RTKDC%bvRj7+#c zJz^|o^qA)l5g!gLENYyyw9SyIT>7Y5fJ96o4uzY!X!Bo88g zEXEX_3J9~6$_2}_04_j8^>h7&$b%t36D4Ydk6i8wuAL6DkC9(k!^@YW^QMY#8iNG!H#WJOsr zSyrA5^dsAkoV2!VN$4-A?zrdfj!h~fsqI6sfZ$+VF!3mrJR6j zbWmreK|s-ADvMrItEW6^@m-9IQcOY0m^f%F!^6;O_%Bs`{>ZxamZ_HolI3L$$@21~ zB2Mc^1ySjm=U?c$Nw%c%OwmUQgU>-yO)g1P1&Ls_W^h(V$~9Rl3HaPZHWg}!6zNo0 zTDdIGE>`3U(+gP^%ga1zUQ@3#E9G%7p-gkqff}y~Fh|52tY$x;ZU}sCd;ZiZ*rF#N z-1e@DI-!HM_k8(XvI^Tn`q|g-=S7^BeTVnIgs?|4iAI~P=lQ|naK3pEow<*qYcMk( z+sC20m^lT;kWo-Jz1sHAYf}S)*w5RG1n=)hQVOBr4WvCW&ZG)MDPk*0Vmf1j&eUsV zP?};IijKey;ey_jqF_=(^K8QOC<5q9X&p@{f=Z`rG)oaj!CV&c+RA_$ReQJw`Jmzd zK#j0MGeFr?<>2pB>x9&BNAuOi8~km%ex7V9n$77f*mH#gMCNY%#$m=TMmKccxNQ^JsZSA*gArmNeZObjt;Z zo`(#~Uf`=dZ1xvV{vXUy7?^a*d|-~7kG|eNp`}+z|7b?S+rPRP`5V7C7>B(zIlS$^{-C6qklzhg7@I|I}bjwefvRpi`9CIgdIHaX+sm`SkuT1 ztI$R}7EMzTN<_6=1%H+{#pow;u1&~Ogc(`%LRbZY^&2i0L6Kr?FT@hraGGBKOi>Gg zLfY6q{AcRZzkMsY+LY}m2$pVrX#c|?Sb#Y?eV6nsHyypdJK9H%D}I}K_r{$+Jb%$D zN56NAGA;+G#jWuO5IFOQkVjRDicrvO*AN0C5S&5GZh4BGmF@aBVX$cCzYSp01?<;2 zcBP_Zn&MCOiu&XOba_u!$NWn~ogSC_^^=nvptvGyqfK zWTqmUg)JAvkS)}GH*83ga$Vh`$Rz8i*wz7?kyfEt`F_EcTh=>!05jmwcMj3u>%buo zxyF!?DiDj7gMc&}0jVWy=5m&Y(z?T=70L@XN;?9wXuhTKGkCZZl}*KC47sNW&L8yLOVypr0d#_GTS zB%H!lkvN_4)EukbR0`JhF(0+H_1>}4s#B@R>4LjPIz4EGCX_IarEEt!w2Rb6RklU`J6?$AM=7_2 z(#8R>?S$00`L$R39Z<)0%y#g2M4XiN;z_CL*l~KW-=*43`lQ=d2?zSm9Zb3utlK;W z?ER=&2Z$XteUs7eaTpDVU{5D@95 zdL>nuWKuedeoe7(t63E%9N-MG%Vu*bPED4<7dEnd3L^*Enu1LL$6Q9cX2qHUH3t1| zlcr!7h*oKF0);f^`qvav+dw!qG;t6TCs9Mo{u&v*A&6ZHmHHp`GeAayeZXe^I$?hA zLxRnqimFBB_z3O2`%^es41#8Woa1|3QYmnXLeBHtBxKCe%$jD5=7n>zGyQIt-2$H= z5qr#(g*={x=bSKX<%q4~GtuVdu+*NTs3}$V!G>a1_DO945CYGss>MR<^tJS$e$;AJ z)vrELRjXA$iu1L!4p%f`&Zh>4h9Z?sZ`IvaG)yj)I`uCNP7`)!S?LG#7dFkM8SF-2 zeFjncCSV=G%1|Z=8hxtK7zt(ieO?=@Q<;XFGJq{xO&(z5K*7wzP|T)KPmX1pm^hUy24qf{eT8Lz>rQwn~abtwNjd1vsr* z4T^z%{|%L6@ig#rVAY}2WQ_HidLC*ZB2;pG)_bi!^=)cf zDAwvD3ai$w-mrOSzOYr_+w0cXocl(**CKQ6bwyn%KqSgzK zJ1BbqTVPGoYymS0gcLJe{hQZo*|JXomKBhyV~-zV8yOpHs^Ek8C6pWJ@SA4OylL&M z**6J`N4$2&qwiE4rgJ6^nXUik=_nO$Sb(A-k#5Se{))9eaL9*5*4LEn9Hc< zWTYzyW%)g3MKoY1dwE%DVT6Ap$q(tZ#%&%*FUx3|^nzxC#dna<=3viBG~Q3FR5`=v z$rqV0-}BJhv1jPzt;JXCFTQ;~f5l_*N&52VO3%N2w{i1uv#;16DI8gooOSPg`jIS% zH4$X$J>7eq-g<(X7fY)CXs=UW`R!T#KK(3qkw$Kq_P)PU%FU@R9WiF|hzaX!TpjFd zu=q|--(B)u3foKXL#?3#a&jfUdANaWCU@Z*iu)4}s#GwvYL7p(ciXKC+-esQo}k4= z2A9%WVnVcWXn3Y56&=td)FF1MINUR)_1MQitd~^2Fo!?AX3- z>m9e|$}@ESnx9i;!O)))MSclHF2X;Urj&ri`BGy6z8Qtx3~bGj zx)dq3*EEhYdcNqDAck?=$ye0=&0w*-cxpl1hp>aymC}77ID#zWB6BBbm}o4P4;#&P zd#0}Dr(8+udpB{H*U6r7x{MSNK!G6QobAu*?~kiP3PHd$RxJF&3bT{8D2?~EPubn1 zH0+o#YY+YEvcEo9x_e5!{_QyZ#a9mN=NmOc(?pYZz7T(ZD)kOu@MxLxfd04MeTCQf z&6CDhtz)R;x#{=)Oja5@!e=s@zJE!>-kCEW7@6EU@l3Si5=Hsh4_&1h?|+b0Mk{vy z7{wA*{jom!u_KCtJ=ec@V1s8t%hA`ycX)!GttU@RTpY9vFF$k=Uxe5+468K7KUw0g z$Qk$4l~+D9cHGrj9`Eqs2E$v=RE$`tAdak6Q2nC6TmJ)dJdsf7MEz;w##O;m_q@BC zG-vA^eTy`5eB-X|qpon9#_XDxmk4awA_>ozf3Yj8)LpeOrmxKm4j(amm?uM_n?^2< z`fCEazAPEJZiK#-UfXcfNF{dWu3$}u9Ra}Y*RyYaf5$MY5R`) zKs@vlDpviNqIUJa)zY)bF?HbM3fqXk648Lx#eb}NWT=LgBlkp>%HH7b;5+t12{Bmv zX7qI#s?*o`O%7(W5;*(A`fci1O?Ct*hRccZ6R6m zS+WUHM+<)spTKvQ(oP~u7~jduC%fb=SnIU*osg4#AIL$xyN{Fu_mjZ$13rhBNxig? ztQPl@BtDbkH<(8&v9h+*Ojgsc*wHsb+LdZ6d&p{bUE0W>U(KEqPUAek@0?If0_>jn z9L&`z?2Rw()5T?dXT%RlL~0_%gAe&(;$!C^HwZ5;1pHVe_!{sueCs~k_Z}c3OTZ|; z18+y3!(cgwl^oXSx8h2ykHL7~Ho&2MjevFfSAfI#l?L8J3x^W{!}uoNI6w!7SsZ3_ z7{Q3BHgZ_bVI_yv{H+@F9458=N(?>Ydmg-9*S8q$NxVIo!#2QNh6P}Zx9b2SxKfTh z)=owOhWnldtOG0{HhnR}u1^GX@OD<;2(+^~3}f5^aw&&N4x7+h0e1bc>k~100Y+!A z9au&AC{a>_D^c|S9)5|y#-RhSh*PzgKT`~>3qX4|>te#@Ta3510kU}&bKHvIAIG7c z-G!)0YES%iA%GUcxz2f_~U_MxPR3_%vu>1Ha0FLmi-l zw;46cIjzdULDsGVCKaH}TtIstgAU%#>U$XNY!1Wdxq`%jA^bRhKFJ}YPX!r=ekwqv z#efreyOqO99I{zd@%dI^+^5lIBeJ-WU1#&H!k%CT8Rk`d*44nP06SF~RyD-VA;YSM zWa+yBvpEdw$I*^p+#2-%9^j?=1i%D`Nxcy5CX84^T6lY+{vz6~`YOOl{7M^tvK_tE za(=}y;*aso6no!vKnHJU^-VxKo5L_Lk8uvhK!?R>Cplzvh;a(UxP-(&|JOk;oBkf4 z9h8rwH`dMqh2!XrLB@+X?3@ z`yM!K(_aU)_gMfPyq(ooh;}xIVf0zY--Rq-+&YY#1DNEHQK=5&vMZA?(_!f6J=BkF z7`*}QrvV+joyFVP9JYZ+s4+t-n$g>PfDDsn^ur+IelzIs9_}-}7>6Dh%;GQ`u!Uc5 zfqX*8_}vyV1#eBnbqk<_!z_$D5wl=0fwv}-ksPwICXz<}{3zZY%i%Z<84eT41P+;8 zO~h|`K!@>r9^(KR&J)R0T%U}wJb(@k+3Y4`tf%pu4b*7kbZEm})@FCx_}wWSlPP?@ zQ}}$_@q8irWpr!D^VnvR{u_(W!N+qj!Ek%@H}HJ-BRqHp^@ta6dVFgx}z? z{ecksb%Z$Jj~$;9;@m-q3(vW+!N84YJRcL{oq*$ag!sA%@t5Gh_R9did>Z(MkPJK@ zTu4YJaLL5`S&InCcH;OxAvt~==syG;L#Q8yXW@7OKhsl%1NcWUm%MBopA(Y5l#l{9 zj(;a4dM6=6@^Bm?r0@fA*GKIQU+Ygenm)mGa(fUj`f68qK`^E zS9LWG;9d<{)U**&y9+;0bR!{kz-!p0%*&D4_$`#w?*ZYf9&aPKrTwRC48G{W4bNlovrD%f?TJ+t>St1y>%)vhIQfh zUs7#KGU*lRwm_`(hICsb!{}}4wnW_Y<#ZeU)0648LNW&0DxUxI$fe6xF6vkwTDbhG zj-?Aa*Mt@>Te=cw%a(*jc6N3w4NWQwO`Y4hpkrxSXv&1vNfX;fHMEUt43&mjJ3HpC z=m;&iYQ@UsSIt|svIB2*babvMYr1Ob+|E$ba&&Z6$MQ)XSFY-uyZqvdp^EC#s+!We z>QKedVKuewk6fvV|*G&t2Yuz8G>VN#PD2>F*5P#D=7xScjW;$mDIkN&UYy // 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 zcmeIb4@hm#w;wh+Jw3JN)YMvQt%#|$)>>G3FVcF+Ovj ziMh4b-q_pTT5GMfg0)seL=X`X5kbU?hzKGgA|i;0h&>1gIUEj~&7rHGHGlTr=jiwQ z`@Q_WzxNu*LeHLA^LNjhS+i!%n%P2#|62(0C-MI;Np|EaKl?q|aOv?uJJf!mwszY71qfPX*!Q?%QMCqq8|xlWvh%jS3{>|cZXpT~b1 z{{IyIIm|!-99j71G#vgf;@_nE*Wu>4{Jxpm&F{oei2t7o`!^8Ab>VvQljE6mMR5O1 z8lU_Q|K>YkKuIXZFSj@UGcCM%{#)<|^*`geeg7%^GnC+;>%e~VGc>%;Bot>?e1{hY>xnezTU9melw!c09s{7kxk zUx)EKn{;qW_=sOF|Go1510D8WJ_60D@71&7XTts?gc-VUd$ZfT-zT`af10-T zhW*FDa~%!M`5ieP!)N?+f9G(1PmW{4{u7P&rsMv|P>Fwz&-6BF4ITbd;Fz}jpN)Tx zXTqwqKOIm0=P(0*1`bmPGsaB1KdZyI?);t{&%ArJCyep@Z_@oa?PvONn|Q-&w4cXA za@e2OVWuA5@@jP$*M-NmH_!h}`M*8za$Wusa9j?*cXGNW;J6Ha2SX!M zm%psT_#L?2ymk32+RyK1!c1M7b(o=#H{D;=VO*9e4^9a!_~p1f-|#=X4g57Y*w61~ z-p_=!>M*W{sf!8w&plzu>Dmy+Jo%r-zv0Pd9Q_x-@mSz@Po{f2aLMU_lJH;RmuX;V z&UH3jqm1)mTxVbI49{l8g6ZSXY=D1uQzaP5| z{kq^b={Ve*?r-Y2-gG9<-_>zBjN8+ju3M+$_TqnUJ^z;Wn>@YgdUTlSpWb@@o(|)7 z;xXtA>xG~B5x0p9^R(OF)^RycF3X#)&*M+d^Y7^ROdF;nr!{TXul=T;-mu@-Veh@) z0K&L@ZDX<;{vO<1@Aux}@9QuQ zH~(--7{V{(_&xZa-3I;v9PH=!<9~11SNKhQ#(Ts5EA8jHB&Qn&j@zHC797re%e2b~+&`7>zd;y}Bd!C}m&c45f1_}7{`}AF=8-YWs^DjS#QmLl4?lS< z0?PgjewY{VJ8~F5b)IE^7H%%fq~rJG`U1*$j$^u*bnG_i{v6y~9>+K3nRGS4ad{?B zPH*b+=i%mbJl0J*6IKh{Pt}v>I8O6kx;mYX+c^3C{(=tUd`+09QCYnXV_GDK{f-V} z8kjKi?hU|k8dDxWP1s+Ao6F+9V$zxSYt&)<{>fo~2{_Kvw6&K;O*+h!=MDSI2;=-r zp8Vwg11S3|_~A4rPj;KIW*uhUkDn&&ufpxEi#Hw5aqKsA;Jgjp|Ju)l@f_!EGft<+ zLmAI;Jii+nasDQZ=QxhbX}Qg~o*K${j^ntT*0iSy<2jDQ_}`>6bmuva%QAU#{w9p) zI1V%Ey!HI+KNHre!%Ta6)BOz{X5P;m)`c)0Lxz6rHgx!#aC1AGy0F{S^LOFqeBR5m z8+ab8CLQN(+VgMyOc>8`oW?tDOHo~}`{BP)B#!4S>oacM* z@OL!c(3azwckhRr>tXWr@}=Jgp5yYr=@X{B0pPs#E7#ufB$E~hJGgOA0UkTk13DoXxfbDIPO2DJboV&#&aCU zHFWU4AJ1`IZ<8mdGw;B29H%kqIF1S9Igb4%o%h{&j$=IIl4-G)~* z;C~M{*U?)SQ{MjoJeSAiB&YiU9GA!M%wha~Cf)xCH;29V4&yq^JLj0X{7(qud`zBP zC-Z(2aC_U+`+onkPG{Q1n{EwP z)?1fp9mcriJpVV~I6spo$1#0k25!#No6gkpe+QmvVCctgGf&SZg|U2O+H+3FWBH4L z%jI?el>Hz0;XHIcGK|{^P&SVr#&bS~wk#I|%KlIMm~}x zDr~aFnEgXE5v>w6A-h?kI^_0)s455lMES@gohSv#WiCjWLSg;G`P9*d{$XD)p)<-K z_AOJh{$bxKmFORFjn$KZTLe|oj{f0@ZG_g6KkS>KPW{8a4T6*?e-bhw%y}hrrhhnM z3oYp%_Kj1Y{$Zad6Zl>zRsV39YN$ll;1yAHyIhDJRcTA~)AdIpf9-|1z*}+C`q7#u ztwmW9C&U(N!J+6KHti~xs0kyR0a--W5}ao_(oJ+rPu$OuB6v#NdpT_}z3@Qbv}CWq z30Dc-S{KbAZRIVEw&<4J^^1xc^WSfdFwIjsM27%X;Ng%JnS-s z%LqRO#Vl>6rLNMZLd_Ed)@RDc;+-oEsGxoPs#>G_1AJF@0UlApOXpFDQD2DXA+e6U zg6qWmCns{3!l>dH*E+=F%qG<^Mu?9GR%|twU1_5iG5cXUo81bs?&MoiNHsPim*~36 z=PN&@106?*7}t>NsK~=^i)8br(lb1&9zHAH9>D^ebn2c#SObBww# zsS7AwE4W(mUKe-*rTG~5cDs;K$280jz!$`lJqMk13VkOR`OqTKSkRX(MpV-fJyy$z z%t^}T?`->Zy^DEV^SNzJf;TZ^ss~r90ZS?XKVoLYnRhT{ieguYdqm|z)YCSwhxD20 zcn@c{B;6`C!F?vXZj?A&o*1j}rTm zGwHIXXQ=}Y|Fy-V;uD?V7j4jh-_LZ!o_jeGW@HC%rWIg{B}D=h2+#H`>@l0X_tT}T40O}EvC6^W&KA+gE>^5Ix==m&jU2@OgA~5 zt5J%scA|r4K0Zqx<2wrHJssga=1{pd!gN^2jkj4RbJPv|hjtr+CO>6HifDi6JOdP| zrOkGlpQH9|s(G=%_FVX3f5$BL1CyA7Vx9yfOd7W03)yK3)gVd0?k%cTsLc4Ia)=@H zdj(f&G%~L^(5N|{gc$tMnC|W!ibDgpPZcr@y-xNSc)rn~LWzes^9n$f?49gdk{uF` zpe`7L;&>MWxMMy~DH0ptOO!V$?j@IVYk-~m?7uInEap5EuFms`W% zvP)Gu@fX79w<|k#Ism5pSxsED=M%A)FF7KmI}B>?t7)x+u8e%lwr+qesKDf zm^VFC)XZa#m5__r|j6}dY zo^8qIm8VC|1j?i9t7@I_%V{Mz)nVVS@R%Gpq5^LcH}S7>S@Fnbtt?LFG6U_iPxwAA z5R2myCTLH)vb4|uuS4zvdeGEarJ09;JmLGaL$A52%TTyV6fK70vGXy_*eaHv?AVs% zT39|XupF{Pp@D3X(gBXxMt9k=xD?@gh@$SSyMSfX8n&W^M2q2QY<#QH;WVwU(G zDO#y0S7tQx;^#?o03o)O$i#69DH>F9g_x%A#u<#-y7757m@{bK134q6$#hf%9!Via z#q#x^#O%8XaH~`dDTaq^@q?ZkeZJ4Pm>-{sWgN&UF)b#cF=FCU%^^I8b<;W8yW%+t ziX+|NF5QDrJdz<|G7HIABYXXsuFFLFH7#d~zz>QPPU_imoR*(S3YsF5B5@_;GOKye zGgb>-jZkbLQ>6Dtso#$^lY~WN+>4rr*STkl;-wcmEE!p&>K~A=3u82yB??y|AAHaoq?W9ya9T9_O} zL{DeeJ&I$9qdJ`s4=nW}YsGSjMHDHCE#!;hbK0Kb7(8SoXhvGB8NeQOGg|Z9A9ThA zbke0P=T-}6ruocMX5{%{bqxcnY#7GuZ}Q1MA0`f?wiVtOG>rnjao{vvd`1?8bzzLc z6IGyfM&q1C53)HM(3CnD?XXKM5ESqV&;uJhFYd8AMPy9gTQ@bed;KE#MAfDAiVjmG z#UX2(-*SkSzq5o8xTXDKk+mjgoML*|WA;8k7u=#yP@zG6WJP%4xUV@QNft%69HTM0 zN)u3Fq35B8OG#Ca-e%cC4YdA3sB4&YO2UV?MP3`!xlV07nVc1h!tF_m`g}u>tonG# zuhb2ptW|E9P_aml5=KG+R0?|qB5ODPy1*TKm_mhkB*7kqw1|a(pbkvkB0B9U^9x^A z(`>{^(aXvm1z`s%QrcJVVpi;Wr9+P;8B|J&**fv2$XTICod4zeCC>FS&`J|a&QgJq z`&3p<5jRs_g7}_|yod;6G$O>5XVQ7()eV$a4LZ*nwQjvrD^?&ksGoyIP31Deh0>p^b<6QoCRP}R&JiCO!)=Y@qHrIa#l*LRJz5a;^}i>y$z=%SL7;D zF_B^^_VqV3zbWe`6Hdekv^`;V(IcmVUX7F2*BYCKGPHBZ$c1@7=sF0jgfsMHlkanr zit8)+X0r}dRicXyjT7R$aYB+c&VCc2Xe>?`SQ-XZzVT3*=(CjymYAyFKmDI*f3Vs0@libOS7WVo>8EF&0qLj`US*oDc0k!Au^5u#+e zGW!o*5e2?)tdMme*G>o=rC5szLlzrE`^|Wl)cVO6phvKnR3Q6*&hTEuwqMr!Fq#3@ z(|QMns@vF*JfsglzMM8R@i2#R3_w!^qPOG268aUoXcdItWkOGMeD#igl~Ldto-jy* z9v^s*$CpD79$y=+cznkMy_G4Un{JH?T!^>zMlC^qi{9f@qFMZbiiW+84 zSc1dk?3VHCyL3iwsj4OmF%oXl-JB~G6B(ucVrP|Tb`kX<`|}!5n5z3EfF2IVFn0%hPD)045I65 zIOiNQzwSih_Urcdk8Z01%~3d5!Eei_EAsBcl>J9v7cP-Y|0RFDuPEP4H}5a>kTRWL zXXN(6^1w=Fzl6pfAaxUX)u28hpv_zL) z{&TbsmYz=FNxwG=8hAG8Hch@Ca@#Wk0B7AEGqJcF2w`f0B@0E*?2(B$Yj9;2taNq! zN05?@M`Yg96DA574Kiw&&Qgs>RK=mb)FF`Mi6$M7i~j$a%iAj)Hx?8IAh$3$S_57% z17de^M^g7a+s~L4r}ZKfDQjp4%SEe*UJSEZrB)vG*r(}LIkVoFGr~5*jly(8D3llo z3bOUF2UDmqt#Zdp6sSHTTlHS*3C383s8V6|K8u_l!jRB4Ch%fsV#Z}AsWA`Blo{p61wa~%u@`?Ye%)`Q8%QpuU~Ij#_Ey8k$-pcmzSqEZ<}v+;(|p%rj^au?rl zTf00soKQRWmBQ#_3>=lm$(F~<-4kzZLPhX_UL`SPWL6U>)tL4|5Rx;2@ro0Ql?j^}9_W2Vg$SL$XBd4?fT#h-*E>Dle=WjI)65jB}Y#Dk22x_UH*qSOs@QlGtP&4Ww|vI)`7v$yw!$Kz5E@ zI&{bSG>P)Oz)GaOfNeq|^&#ve)1PfL_MOVIvKB4*rVj}5rWf#buO}T|6jPVGktBmy z%b(mAAdGD#^#&+SyOa6@^qVQq2|YqC8ygb(#DOfN-_t8z3ssdGo(x?=puK`X*ZM2B z*>^|Fq41*~E@I+h-8paE=J^%!SEbMA9SowChp;mps+c(ww^mH4zvGd8IURB&35QPK z$o>*x%L3C;ns}Gmc;t4A@dVk%#(2InZ&sd?S-N#ss@4?0l`16mNmhiTWiL~#oP8?I z7Tq%URi2!vTxcr~Mw?w^LPW|Ffs^YHo`HKD5A?s`EE57_@x)!71N0=%4%Kt$dfnL-@1^A~3T4ZxLIADx-_k>8Pz0K9xepdT zYT8_ICrBGPiNzy3z33t~bCsg_Cm$aY)cBIvZI+ zi%7ARK@mqdhdkKsd5>Xw!9In{jZs2voYj~H&S{6j9yJcC7 zKC!^`h1BH#g<885kx?v6AWC(6Z&PNjV)iJO1-)mvndP%Dg}9X@g65SoUSuO6C|6{kOi5E+c4Y65ml>97rw z);ZiwCSdQ#G=%+8Gu~G1{y>!8g5vli@Q6OWg#KTk#Mxlvt0?m?m3w zopR}7nixFP)gyo+TvZ0wxyfoI37>dE%D0rk7TyR8KdFP2ED?0#8mBzaJr{qal@(A< zec>G_v(b4-;WYn)>CvJ%PCxp{*xRudIC|sJ6{f#HfvpPXU#R`PI*=2Bep@yZ2Y#j3 ztaVu>_Latw4dmK2_K3i>%mzyvIRqw2Zt(_TcZ9lD8dWe9W+N>B-eWRkxI}JCPPfED zgEk?_>%|kcS9j5MbdN-|5{#&L9;eHS^LnZvt_%@IDP92NjNB&0Hmh8fLZ^bR*G;i3 zP5=$~gAZn!n0Fb{!_@v6?WBBou|t62ru^l9P=d6Gk!HxUl0A+F z&^5$zjk7!kTdoloB&H$#g1pnB!7%{CSe(;1*H8=sQm00iyT(6`kr_7db=MTGF`8?j zCS0R6*N_dHiCPsPiY;wr#Vv?peoH@+KC=DzRX!N51^239S=h{gt%MTcgqWm(_?_aF z>DCQ+4>%QI1QpMS$l>P?g~1EHN(S5`e*+H|`p>n}j*KObnBjq;EB3le(>M>H3avl|%{EZN)`w?P zB#T}hk)(^(Nw<6YJ+(n!wN@&8byu*ggziKYdz;PgQhR9G7sIj{#HUqQ0Ub05KblJd zICl6;w(wVPR#Lb~Rb^FeEy5FWlm+hUF~*y?5>A|MG)T8n?!q(!#b#%k2-I(j+$&Rt z2=pJqC}B-cqMZ0QY-5o!bQZHNdDl*4U8-_06JkQXfr_PQ1(;U4Q!>UZ5%!!_l`{!V zXl4}~y+fgS!K_S-RoyO)oCw}KL1$7p^W25@{GA3-CRNh@zO`mV)5wyh$K^;Klw#w&oj$d7^4!-vmgh3JC9l6^-fepwW!SDAKjTV7q(wuRzn1HQ-%ryUv@Aq>>|q4abjh6JH**eSA)s=AORH_* z!d%SQVY0DoV&|~75Qcn6uN7P_uo1JA#f%Zt@(beD*ve#&bHMt8H81RB75BJy@-Dbz zWQz)5Bk0!y@nJV+twRvS5f3&Ug+-@idCwWBH6V;FgXhtpuQo*6G@T|sKVnlsRi{HQ z3tCt=E2ouIy=({&CT)HtQ#*eCv14*(UhJ1yJ`Yxc^%meBCJeVbWiUUB(HQC7;32v& zi=XV@-nhaFatNj;4>S2_qg-$%hDaq&u+TY0rg-Ba$4yVAGher$JXo;?sy%N@#EUIE=Euy2&Kdu zEWuszhOIxOcvHGgR)Hhlpi_9n8?YpYA-g=uYx9u>UT!o;iQ*098LxQLrs{Sr#ha=F zE#8<;#IOH{b?Xy%oD`7q^(@>~axq{snq);9z;}o@kI{ZPgbO&G$%vqwP?J$)&3oeF5C4YV7Qx{W64mDx zgoH}flvm5yFqc)+hqV0T0;JfZ)BXAnku?q_`+mJBlOhHZp3+~6KM zvGwEKC_j&ktCo16Q6D5qXizx!l<8RfUKhj3e^F;ls|8CimgG4?R_uXs+x@AcXcqZD<3IUe&2zy~_&CbRn7q}rgZcS4FV*@;E1Z=HkP7F23d*_s=F zWRl#m{uRq5xegrWV~y!i92f%R#<9&aYF5F!G#m&S^Y$h>>{Wap@q91#mTTQM31N*> zOTg<$)g4r0%vL~jt!&Go%Q3CY}laYERgQ_uXLBI%Jw>n`_xX`@{}k6za%% zse#<0(lASTpINb1&l6#0{0xiwNmeGJTluY9Hg4?~m28%GY;iWL5WO#L?9@C`PFj48 z%V?vXkFvB0>TXf`-L&}D3>(9uQP&r>-tr*w{LDFso5-D#xmdY6&KR_jUPa>wu2%UL z;YcJxmT_2{V|;>m0mYECWjdk9G_0UGNrMVrVrAf!QXV7LYzJK{9$iQAr?}R=LB|XI zr-EXPXRA=UeU~(N&c|o~Kj_|H5|! zYIr{9E4R3_LNgG}HJ}U)au7@Rs193ptiZbH5#5BLG!|DPnMk`ebjH$dTZLjhC$DVZ;=h@I<<{vlwfO*U=>Ba+s)jMDdq|UlwBXSRtRW$tqQh60B@t z=81J)`nnVmb75u+ASv!9qy+~vCUKY2h@gWOxtFz`U~c}yf@Q@{+mJX-9)9Ihh@88! zf&Zye<#BH)Iycc=!A*{pxzE3|v;ig?{`og&ORQ`zCAiFXBC(SeF0k&xdY;LHBZXq< z@c?@=&o@Im(6jinajJp=!ZUY@+^MJd1=s@MC8~Rg^d;<^`{LBxH@|TYCW0y%03#9m zakSIc@>olaN(}KfQ22DC*c&f7pE&k2G~B$^uf5Dz*z>iCwsRm=4&XrO0WE&#LDWlE zv$PIIxXXuLp#i!Bw*!|02gXiBh|l1(vK-GU1=!5zl|O<})_i}4q`JUGtsiiS2U#M0 zId<2Jy`I~}N-kW&hlp0v8w77NOt>E9x-+^vCjh;xroMG>e*IFV4_h9sgxX`;j>Dvc z4%U}vIvdN~hJ1CjL{4TgP&X~&4q~N=gqUOK&Y(tk_?&9VzFQSYu|P~~*CEqVAL2osSb6as~k z%D{H@_zbep%)AqGCk!7L25I-2x_ng|h;$+`4Kb?gg%3jA*ijjX(+XCM;n#a>-Ht;s z$_MMw^S;W+=BF*PImkRGa`TQIaAf8eX>&oa+4jP!*suz^$2V)0d zBL)f+s;wgEeI*kGPj&NwLX2FXX@ox>S5p@w=Os(3@xJp1-u;Dg+1nM*dH!GZ?n+>6 zK&^7D@2ybzL!c>y{fe7A(94*#Tn&(nSMQ!EQ0XwVB82N9;0x{*8r&)k-?JKN)NO(m z-g0Ub`7!rYUe?8zZaPjsRb9%Y4LqRb_KZ(Gi_h#0yjTJZEq*qBtRi1ld-#x#D4mj; zI>|7xVcWNIGi+kRH6H&g$99OhZ-o8~gxfg%Z-6 z#y0MzjPEC(d7VoqFqS$6gDM)k(eJ~1s+ZXJS?@vaDFfiSBWv}lI;T`IMY&Ys$glF99Hrr27}JFHsH7vxzGpHTulpaTU+G0j8}ovgH$> z4*^8R=~lj5$(?0ILR(Fzo+3=<>gl{Pn2?5u6GICTw&f+B6Z$J1C9R8hm<1VpIkhbq z19I{{S17F#q0kMzep0A~*MrsB_PUgcNLWw73XfwvuP@ArJW%u!PQqkAMUqmbWAi>7 zMK5&#Hqs1`%^ZL@Kc1JO1RRC@N@1RKEtfAfP*f^nUyYWZiBTtGpYZA{rEY8^{a)^? z0V!v4loHv|np|*zwO7k}-T78he3+{WN&NK|kC1o0W9QtfQwkuHuO6?856`rgjY(Sq zXqIzF+dHvWY}d5cac|Dtl@nxQRzawo5ADcayDYv1T#L1&kby8zC-@l8X}L)03c-A! zo9+taNk6_#;{hwmV$`bmsDNu{LkEymPVrNED94Gv8+BLHaXyxKfM$`o9($e55ZQCD z)_IdG>CG!iY+~J1#Gk{B9_8!YWPW)qiw7e2Br0VLUt!}21}VyC;(nu*Aa92-*r)R? z*_^KO`L;+rpRV*dIxHYF_eMA@xyd!IZ1Oy2zitg6GX-ffOZoGnr}maG(Q!|)Ph>Qs zcgfy1$q`JFuM{t3x!@U^B7+l)FaRi~NP)N9BCG-ySyAAL$6FUK1WGcK{Hpp6-y^^h zPCBm|&MzgIN5EijkamXUjH1XxyWiJgW3uSvn#6ecrk4xHV%(Mm31MfUWQbvNQL zft)*}PKNH}d66S*X@!P2a=>ga0J5u+}l>!~-k}@LLeF&KmLg;lrZ)dM@ zEZBX;?_`~M>y;0|GBz}lIYWm&kv7BCYE{c`^!G>E@&UP2I@Zg_j}(_nL^-RLQACzC zsiz{foLW*!_$Mtwsi`Q}VnehlvZP>wV~1QYY+LquX{%Z&U%yt`@n>8uqu;y9n7<=7 zPT-1GmI9_&d7SxQ(Dxazo7-5&2NQLCVmLwXYh8GpIUrx}na` zd3XsoqhxM`c`4T&H`Q=SH{T(uBg--q5A9m97xd|y#CnNW6Pr`TxzrOKr@=~A9VCzs zo6#d2Gm(Cxh{Ce^i3Ef?Xow>xQLv1j)kK{@Ld8xBkB;vX5@YQxCEq9I`?UNyVnT3cYmEdr*VB!QTz)eGTcE!v?AiV* z;{<}A>sx-1&HViW7?y7pL^r4S2&q_!h4(gB=DKISf|N{~8ls9oWy|c*xbOwiD{GrN zrp#=84$7l(x#*Yfm9QRzm9Z}-)`n7D&CLZH93@t(+T1I+x-I#_gUz3k_PI6| zUs&A<4+qzNcxk|uVX9!c6AK?~t;`Sh(oHbcVZ<_sb*0=?pXZR)fg(EYTE+GjtKod9 zA8VWY=Qk8lZpXp3T>4;@-=X9_lySJZG}d010c+S%Cnv}8&0}KZYg)d>iRqh_kaLY* z)xFbp6#5`jRmSP0s}Mp%YE|3Ptt1sz@)J&u#$hfUF@AnN$(QnCSkAYm8*=4p!Jk^8 z!zSEfwJ9tD3P(0>qA);;hOZTm(vgdgOH7uTcS_R?f8dDEy(!qBd+zoOa};7hhGCo> zsqq^{bz#{y9k(=!PN zehSl;GvYsn$z}9Vho8uv%+atHv&l~h6}!4uVr6gy!n=_9=)6B8gJ*OW6i$Ek;Sw1v zk}G3=^2BTpwCyd7SfUp+WR2Y+dZ(mUiP= z#mR`$7tch^Njz=IUO?@unl1w|XHu@$Hk&n$RF7>6MUQrOnVP1R599770)9SV0&tW; zaBG=jORi{BifS6$e?T_7dx`GevM^!fdN~3j5!MuKzFustEl!bDS5uVgb1shKywpN9 zL28t`;zU7`)vXB!h>VKCxu}kHuBxh!?vb<#tv=r|);E07-IB~})$CdGi0YK6QN4Wv z?ibSAUr>|EAilQfK)JH+7nLI|q>oVQ70ne_5>R^e>G_(nQGC~*W{Zf&3;9Vt$w)&kl#3psz|af®~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 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 // 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 099/287] 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 100/287] 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 101/287] 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 102/287] 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 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%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 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 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 103/287] 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 104/287] 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 105/287] 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 106/287] 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 107/287] 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 108/287] 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 109/287] 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 110/287] 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 111/287] 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 112/287] 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 113/287] 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 114/287] 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 115/287] 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 116/287] 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 117/287] 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 118/287] 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 119/287] 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 120/287] 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 zcmeI51DG637lyyswr$(CZQHi(+}O5l+qP}nyfgEss!vyTwt9BwZjwKzpJ%4N(^X&9 z>32@`&g>?;xr?pa2*;7$FObltR@fc^eF44@&~d)!T3}D`|2=Fc<>AYhFZlND8-D!w zfnUFV;m@Bx`1kK00RjX-z<>b}C{Q2-4jdRkf&@X(pg|EVSTF<+9vmS;gh0rUArUH6 zD1;6j8ezhOLD;Zi5iVRfgbyDc5h6rD#E20QDN-avjvN_LqC`Q|s8JCuS~NtD9vv}a z#6ZlLF%c_PEX0l-8*$>qLEN};5iedm#E%~z2@)hg!h{KtC{ZFLPMjD?k|aUWq)Cx1 zSu!L~o*XGsq(I7)DUm8wDx^-G8fns`LE5xwkuF_2q)(q788T!*#*7(}DN`n7&YT%p zvSdNltXYvQTQ+3Ro*g-Ic<8fD6qLD{lpQLbD$lrLW% z6)IFf#flYCsZu3Wu3Q;as#HPMs#Q^~S~XO!UL7@R)IiOeHBqZpE!3`E8+Gc`LEXA_ zQLkP-)URJ34H`5+!-fsfs8J&{Zrm76nlwSvrcKeTSu-?m-W)Ajv_Q+2Ezzn~E3|Ii z8g1INLEE-%(XL%Pv~S-Y9XfPC$BrG*sZ%F(?%Ww&x^zL;u3gcsTQ_v?-W@%9^gz#^ zJ<+RIFZAx+8-4opLEpZ8(XU@W^zYvv0|pGhz<~oXXwV=G9y}OBh77^bp+oV{KmTCZ zuwfWJd^kpo7=e)^M`F~dQ5ZdXG{%e>gRx`BV%)fK7(aeICQO)si4!Mc(xge4Jb5yv zOqqhIQ>S9uv}u?=eL7~$n1PuyXJXc@S(rV0Hs;KkgSm6(V&1%Ym_L6$7A#nRg$oyA z(V|6Iym&E|ELno3OP6BVvSnDld^uLESb>!*S7OzwRam`xHP)ym>RWY}taXTeo7{wr$wHeLHsS*nyopcVgGBUD&;QH}>q=gS~tA zV&A@f*uQ^24jedug9i`d(4j*(eE2Yq965rcM~~vzv12%X{5VdWIDwNVPvX?6Q#gJ4 zG|rqkgR^JP;@r7&IDh^;E?l^Pix)5A(xpqdeEBl2T)Bd)SFhsQwQIP3{W@;kxPhBD zZ{pUiTeyAuHtyWHgS&U{;@-V`xPSjX9z1w}hYug((W6Ir{P;1RJb8krPoLu1vuAkz z{5f8{c!8HMU*grPS9tyUHQu~=gST(r;@!Jn%AK79Cqj~_qc)2C1P{P{D!eEEW} zU%%qpw{Q6V{k#3a2NPfdOn?b60Vco%m;e)SBLUkd8J75A}jMUzG@rU4&~JPTh9Rd{rVeX1=;lKR@?7DvCn% zL^I!?`RUFuUs^S4aq<03B|>9L&YGrfyJo&B5gJ=#N=H$*T{B;m2#qN@Ynr<4n)#|k zXl#us9Yx)C&3siNG^XUNY3jCX=BpB+u{EZ26m{D*^Hqt^n3A)msoSoZuS$f*)|k>! z)NR+yS0zGYO3s?5ZoB>?`8tyJ){~0CNoj>tt<_*I<1qKW@1gN6B$c-OR3X3 ziLK>GN2wEeD@((yPU|G5nOM{6M8=ZdQtGr$Vrx0lQBEfEq0=-IYubkr<7Jot6JP>N z;4dd&`}J>{dwzmvr0xE|8P6Y z(l}3-QMB{iJnErleSYc)&etQR^xR*`mtM5Xn$pv2I=xz*`k1=Cs`WY25p)%Oe(DJ7 z_Nu1Y=(Sf@ai340BOT$sb$KQxzyz286JP>NfC(^xUrWIF@M}r^+GK63TguWH^#72r z?a!N9Kg0#w{x+uzhj6)tuk~bYBgM)tBx@TgT%4~CwjjlPjhg~d0uOC3%Ib?RPeZ#q+-|-`y@8#>H^H^u_l;OH?l4-7QVunXd;-=_!ed`TkTZl8quLKb@9V z=F5C-#lrRQQyIt;l8qvCdyMu=EAKbSRS3yO5tN@!ODppgSY<2OC_=}S_Dd`Cm0X38 zY!pHH>9n*mUx8J&l8qvCOliNgGGED62+2kfl%GyZEAtgtWh>bzLdTT$ODpr0T!oNq z6hZmvw6ro`fmODWjUseRX}`2GU&&Pn$wm>BpH53F^A%WSE7>SQ$CUOsPAgVdLy?N5*44@XgYlN~6odYQ^?6Dr 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 zcmX`U4`?LmR`=gE-IJ-Lce9oB_Egfdw{NnUzUjNO{ibHRZt6~FZ+BIvb~CxTv#Fh~ zP2K5Kbx);lB|X{Bbhf*yQ{x|aK?G3*QA9-)K?G6c75{)Bf+C_If}nyRf*^wYL=Z&~ zMEHH4UhmEB%;sNrojT8XzUO){nyMd{K79}Uc7jb`TM{B`^+Ew!5?Hy)68saY-AKg$^75{`+qb4^FRNS z`H>&_k@@TY?9cuz^Q*u5tKWP5KmYSTXMXl)e|G-*zx~_4&HUc){oeOpFJAj!|Mg#) zpZJNN$b9umwqV|MN#G-{^1`ofB1)g znECL-4>OfYWp2B;{?XA<=D~vpna@7^EOUB#n%Udi%iOznFZ2E1|NWUlp^$m=%{S+^ zeb=t<+VfpozI**nr}Mq**X#AnbUOXs^#_B&_pZOczn|IK+RFUU5B*T)$A0X`GJpQ( zf1dgEU;p*Yzx>O;WPbkVe?IeffA@Ep-~avJ&;0j)|99r6e(I;@KL4w~`m4-u|MqXs zeJ=d{AOGvJFc z&hPwAW_NdY{@!A3&(F^@rBW&L@ZrPx>p%YZvw72o*elTR}5zWeUnpWaWj94?_uOIku5Na_R%6nfIL%3tcpiTYLz^^td-2W5tjoRfWuw_}9L>=z zmdZ-fW}|7CZA(iGXKt_6Xt(Qo-G;-j^0L_XbiX~b3`>hsMRF3GH)_lh$F&m6OROZ( zELTa*Q1K=j?`o!I5=&ALr1EfNCrvJCnRe`%W^B5y?)$E%X%?or*oNh1!Ubo-NM3Du zlV%fFO}gAiyWun)4CY(>A}=x$oMtu}^=904G|y<)r=9LZ>$dClw#9|GZwDh|J8ieQ zJnJ8?Wu^HN1O+*k_q*ocE%F0ip_^iRC+tr#$YU+NYt|hoy_izNx=yvNJD|Hhu)??4p!(6D_1VgNZQjo@h zV(YqT8Lnxo1=VX{mmM34XS!1A+h5!zZ{Ec1rf$2jiQf{U0be=}OWK%lVc~J%ut~Ek z;z6w3L^Dks)SXQ`xi7U1Pp65SzHmoam)%f8}$y0qiOX{ecGzC1&H6eB@=kWRM$u?E11-q zhNroTE^9Q2j3(w<>^FYdz~Qrck`U2T(#N#jAb4=_ zOqZIInXuRQ45?Y~I)=tJkHLdk(`nS(vk6AJX%Ks00IO&vz8*z>DoOKDNc6y0A~{Ii zbd(Qm%+VDoHBHyFCYCc}yXdxUIUa7%G>^R~-~q33HzF>;289n z8ewgc;4L`)d}=y)K?XL3n#Wat!bNu1%53qEsSgnBZYrSSpqhQ5-l-_0bz9SE zhb%prK-1c6;O6MSbei#S*^nmnnMEkDxUSupEO=&EPT0p2$I|WC^(@dsv-o1sv$aH* z6vEsT5hs8agvP77yt|u=(uJgIo7|-zMY%}#z#<@Yx;JHev7?CK%}ruL>()i~z}~Jv z2eAqHv1j0e6sRMS=$6*yh1g4Uc3Pbos6Dar3+-7F1F|!l@0Ch`19rBm4*SH58oBZnYDQm6+BAE;j3}`?t7BXA-HihUwd*kv46U zh;8gOb-ir0!A3Ty!2a0>QFu-^UV2k87ukL>voeOK}jGtYhNwO;g*v zUNnKNi5J+HN)VGNUA83TLb4+HnT-sFuN z6Wy0mf|aFV4Hx2y>)TRonIxeH!$*>92dOvVPJp|KP3X=hYsop=jJ4N5I5Js?jFnn;|Li)z)J$LASbD$q_(26`~kPIE9t2p2--?kOe;prZs|g-IvoLG!d_=q7n`u&jiJ5jk!1Ul zGf|}lg3fdtAXIC2Cs&r(x46KOSsNz-Y>7_@LmG2s!o@Lm`UO?ISPqpy@?+E2rPPX- zW1~XSR&+tdu8jq4voU+anFp_DnODNAgaOfrOASxMa^YW626q!1b|A=By9=uYSlTRV zx9fE45XL$%%kL(GFqQqJk@Ppx*f(GbSoL6jM{HBpQqzx3tIoFIWX54kR9YDj}>zCemQjoQ4+z*t<~8t zBc*G;_N6P5E?hft%#E1v^jKX@_Xu&VJW}Og;o4eU z+%)0^xC|LhFn9bb_8{!1(Qw#?#15oC2vZU@CUQ?{vjb-kfg@ZzYtIA`ZpL6unDwpd z1Ugn8Dt2N43UO>bn6WC;ded36oDl_^Wk9femsNLDS1>66j$H_-UaAmfR+ROH5@V`^ z8y&;Ovdw0q?2cq#uf{r?P%RJdT5YzUX3aVnySP1zbg?h;v|_f@X9zOOYlXS>h{_-!@I>7BLKmT2|bJ$hU(?^$Af)^5DKW z6_l78E8+o|XxwroJFX^XG{c#~zgAs9?u=N+UMz12)S_JVM$vNK$2b-;?dQQNLdlJb zRl=N|$;Pu(XHwEC@HRQH%uM+Oli-V)aEPtrC%#EW&}MZnr0-OTsZ*oLLphdbUm`;?pt zNQ3)UikHvRbR*>pPf=}CvWYRlnJpm~h;YDOmU?b4-(2whIEXH_1n*;^7Q5_ub{Pqq z-@;zPh$5o7sgOpu`-crtcf`Jwm)9EZMocNsd!c)@>kTW~pil;m*mQ{pH;DbJMmiCh zX~C|!`kGVeAC+sYxMA9U7#VBcj$4GgAQeyu)xs#)kW9mI2oFM4ypY|=n`0WGM*JB@ zn#MIrCrAJZJ-j6#sdOuK|3>@;8Z@J8w?BP-hfa?Qbql!kH%&0Ev9Yz2V8c{$SxF^lO@`A!;6YTJpSSCa)Zxr`#j3rV4Fy9y46a=1Gv4fwZI zdY0QJPB0caZv<+tkngLhWk_%yEColXeyAzMQjhj_u>`(Xnk`kwf%eF!M!opnZUgKcImapmylTB^YUbc&# z>lSOv@l`6pS&Ltk_?NkY5{w=da{a>Nadrqb)fR|%R$H(1L*+OqTEz|D<&C&?{sK2O ztZi;OhO1mcHa+tag=U!of1X%<1DywiAR%Hd|Kac1#Ln3yVZ&%RCu1G9YUpdGJn#=l z9YyvB-l_WDwNZ-AqJ60w3q}v&!eDTY@D6Dip=El5yF*lz;R=JtwM zo}BD@QatgzMtf2i=t4__GUmo%Kf8_A;HUbfuB%5o!)Jjc@Aw;+=GA4O2d-mDtO{k1 z5GS-NNOQ$7%3H(jTC|Ips;VPJT1M@uE_!iU9$f(&x>b!8Ul*%>?C3!W|!j&bt zfFXckRUVZhgG}m6dSBM_Iln3@K+{<8!?d6Cs;(51{Va(c$7O52O#q1ih1tiHDZUE> z$e#rEG7KoPQ+;_OfMhCt2)!I`OQv)X!7VlH)j$VIHV0tSs)@A;7oZ*m9*95!RqV&M zu2T+y57;YqC4tMA>1qzs&oUVpNvwT2K z^SB+IcTWP=&yA@z*z%B2yC%WpmEj--6g;+DqUlI0m(sSI)(CgoRphdu!op%$5n2Qr z8qv$Gt`WrifS~0+tUc1HQ@^UUr|3{3AcS}&xX;WZ5o0fufr7q4UR(Xd_H-#&)@#q@ z{0X?&5z^)LZZA3f}oj^}QoK75XIf(7GIk^-ep&_SIjycW)EqGABbq~_hZFJx|4XDqx zG`N~ru<(khbwK0?-u7zmXj$SCM%X(U40ALBiZ+`iJ~?^!sqe+Pv?$fW1Cnc8k$k;! z1zofKN}Oz>;cq%?MEk+0ASFqeRn}Ok(LpVxt~UbQyL&Kquc$u31sKvTJ~JerAL3h4 z4<(R^ZlkJm|0`P5oH`Bw(QTli2@=#?dzNz=mTgF?>=DejCP^TTFNWch_q5Ur<(xZ^0HYq zmQ~b4fePqCB9RFsb66JY0P?n_Es7s#n_3sPWx!FcB5gWpTtmbg?FPha3iy^%a+r>$ zr+5T2py>M>gWmI8;CilkaPg#P*G3W<6@!DVi(y}m3ZorWrOJ`87Whqd{ENXAfSG^< zo5+=ADc6sxz|^stVL4E_?r4tie#C!%g# z9>udoWM~0sgl`>1XOtJ^o~VIhZou*l4>3F)^m=_iAxzZX^SrC8s_TJ}c9>u459Lwm zQU6d4BL!@tiuZ9a7N@CRfxL2K7{tC()PAVPfvkI;q?8rEa!DmBtoYyX75fbk6y_rw z$9_y)8LSBY*1CcYQ)O5ivJq1mi_C@$*PeEz>=Ctz7PA3JDs4w!Fv$>ZIv9@!Qsdlo zcY99;Qo2)+DuCSRs8{Nb3&UPEUy#Ef-4M|r;(-!Li2md=(M-b4W^_0j9rky9$OZa* z;G+KV3vN~06aj&Ig}sP^SZH`_o=Z7nrpxGYP)w`gVL7#gMuTjJXeNE!f&{&BFD_+W z>Q&9u7KH|cEZ2-#TGv%dn6*PXiVF|3eZ^M_TV-^S+;DX`=<#nUeRi-HDQVl@w=eqnY{NcOyag-nLo)qk2P0shmIZ2W8gYZbr z^&g)-8w-OU^-m6nBCZCmA>mWH8{|SbVT^CE#Jt>*M)^W6ly?Fz-Wi4Ebc4`dqm==K z;bMWmkZDY7PduaQY4jw4Z+xG8(OQH<#A(|>jnpZw;LMl9l8T6ce+?s{+H@X}21WZx zw~wN*(2Fj^oUDfV)hAnau$|8qM*YLWC+mZ3Hdo4?eLu~ z;Y(^RH+(ws?S<$h@+${m6uiu=Shq+QLXN<{qLVb+^k_B0cQ0Y=i_W64iPwz;aW9ro zN}5hF@|SVqL@=HsF z@zay>FbdIHHz(TW%DJ{E){lQRgS$1VS7}O9s;2wA_Gx}sqSdIoDa=cAHbsC#&QRcq z3J>;3yJArUk+Vr3H%(2eTEY9CVa;Yv5}-K}0Eryx-nCHD1i75i7qm91Bql4Ux-P_p z#0o!=2Zf3k4GL;FeqNC9c0g;nstOh~G=Y0U?|&83~?6NE}2;6v?Vuh|*~IJg1IVhe4Ph^;e(lD!J8BS{Uj5 zcu^7k@GAmoKL^xHjGmWK~Y> zVSd=}561oRYVLTXO1W`fO8ZY^bLgAFwGmhihuvnAHIP%6b@V29uuCTZ0)G_fm-0!! zuLi!F+m@8dS``A67!z17k4X+W9TluR0EqFJcLMJ<5Od zbGD#q6ip!#BH|b>d_hY#DnLNDu?faqn<0BLiIJSuLQaP5#TtcQ4dPJLYZ3D`3Le%y zY0&G-M}@(=tNjX0pnRY=j6!4P55qKZ?ea1!2<`FZFglhG(&Y-^2E4gwWl^p#mm`%; zBq`fKgibkRP+_I;AkiIWlj2zb0#fFPJc4vF7mu1N2pQOm1g8T54;nP$8#8B7l+{9Z z1A5Z%`S@vAc3nLRUDMw{l%Y#3hbNRrg#eppQw!H28EER%n6qrzwN#dgjBmk`l2LXc%BJL?K(4EaIvGokQ z0;|Tt3Bdv3Y)lL+6*ZDj=*Sb3HTGC;IKqpCkV59bui30I1}hYRfz*GJm)0gpZIDg7 zi|NYst@C~&8X1ava3nxEw|j&dB6bR`dzhMMt$8>u*2{siv!UDDqacXrs|95e0<$O9 zITHcrh-Lr_&|AdCU>l(gOTl0>peN>Q*ImcH^dBHrXa>ZS5_!_Rh0=mFMC>l4(JrRS z>a)QB7PfKBKpAckHGw+Hido*}<5<=2Q1@ubR%#>DE zRQ5y$TO{vQ=xJTL{z!%QBHtccx4I>g>DMh_Yk$EYG@B?`iEDc8?uS9_lfR4AAnDVL^chr>|G=PERvoz%Uq zulO*8X{&3GO8LTZx-E_KCt!KGrXV1H)m?Hf_ly zuR(GuL;|rZ3L}b560yk6=Mya&5n9{S*S0A}d7K!ov5C%2@Lr=B6WvAnAhumhk9Pad zAC3#}tu9^k#={&TNgZRm@Z5Bml@~j$w;KkXf-BubX$w>qz(cBSEXMx9XqZ1a8D_Jq z<1I;Rw@%+~oCEgkVCo??JibKIhG)_%r&>7&uSC1UCDiGF0=;Yn?Dag@5T4e@h z`{D6|P?f%5bWnNtyyKk1acB_}e$=v&I?RrqDK5K($6VEgru+#T!OUuq2fn2!LH$6@C7}zIiV{bcdaWS zzES<9JG=Sed)d{8XBXoVyi)d&&YInq^>cX`XtS5~d-dro-l5)i+MSoQDR%Rv@#8+S zPX67c)$HR!l-%s!+}yP4qM1$26)QPa2=k`DOkoWVU#a>Qw1)d*i1 zx_(J&_OCpV9K51`yfuuVBk*$yRDc?Y@O0OmYuxasiASS4-H23m@NRGEtJy&TBAZ?o zy`)RX9*iVoaRuhTU|(Iu*Z136IhW6tp62_qU38;CZ}r`w?ABkue!ainxwknl0%$o% zJ4w;>Yj*kcdLe6;%*0A<&qFOv%7zf-E$&V36~5k}^?`9J+b!IMg3( zJPQIK5)6qkVYtii%;p2vS00tJ?-Clld~}JTMIj;($2{fZJji^a&cvuuq=JHPmMj$eRroi)k;SirD}{m^*#YA+-d7JRFZ7JwE&P ztL$NFIV+9U>CN73QakJi*2`(vTafz0LB6mR`B$`7JgJY6+LvZGZ|-%N=5Sq;;1uO!@;00OUtO_ z?XKnbpRbns(ye!1x2!9hDK!F%mJHiWkG=Cn!!0Hz(xGJ1R%cQo2o*S;Ys}pXOwbX7 zw=GZ~r;fIay=Xki#9Kyw32kEqdf}yzQ@L!%3x2J5+A$XJYO(HB_t+@4?O~ZVx{wVAemXd>saA?p>CWDL1;~9 zOh^q?Ijo32$u;dmm-#H8JnV1a0LYrvn4_*P9c?(=F)P}bY0_xCnq~17w4>P@^|t5N zls+oJaOIuXA2wjl{YU-L4zRuK##e@$EY4O8+tq9dcRE)zNsWMTAY@!a1O)X_0s|t! zL2437OKQl84Iw(LDD14njz((P*aWC2D~xa{hr?1|BnLe8LU-G&WGs>4%De9xtXt!GRTTJl<$ZLoIa_jYn`(7CCZsl^js*DKan&^s% zqLbJ)G@Dm#aA~~6oN|T8mOH8FnK}U`=kcLEX znB}8D9)*Fvfa!_lAF=~QL(r18=qV8Ing&)e17yXq8&g;2^Uw0Ta=)J|^hYvsI6N#s z=q0`&)=tC7^_aE(q*o|6nQSEKz@VXvk`GB9B{v^_*i9*=LNO5$FnnBtrkgN$*FK*8t9_*4@bwTzGE{; zXju9sh@Mob65xipTz@G3^mC988KPiAXeh$Z(?_J~{%}jN-eBU=XWRj^f&-tBI8x)* z&8fq#lI0_~5*(W0BLpj<{h%0Y29sbyKY}reQ53Wx!Ii;Zie@Q0TPBm4> z*{6(~=rn7o6b@wgau_i*OQpE%%eBe9F1#R6LLsmbzue6MGm8f-P(@^gE94`_pwuIL zOS-cOfCSLf&^18@Gp<2mRo7Rgkn)PfJ_+*|+3a|Dv`a&5$POtz&VF@uSh~1a?a?&@ zPy5674j(-l&SRNPa{^3W?5q=&5y_&^fnnmXM0TG{{L){cHW6jYu^b)~6)yV*bZ4AOQDxJnR;nF{4C{#! zjwa5cw_rz97Llq^K*tIAwFsIvl^SA|=y}1BJ(~Q(!EpF&TpHl*rL*<3PcC}o*b@rd zAO(4JcbQt9>$0Mwd_U3~-CLddjF&Prj;1I$y-Y0<&^k)0v}fEPI#MC~BDh2h^J7_| z1hI>tckD|xP3BQb3PM3X1{By$9u)^1Ep??_sKlfIHm+uCb8 zSceY@7|~2wAOx#snUGdT1RfzwnIJq2Oen!YN^f6JYjG^+WW!KYZjv!3ijJ$;^Eb@o zDop(iH;&WL(ig-O4g)ePli97S0970RlA#^SCI+HejzBq()17{9Sb}-);+NdBt^SeN zXD1_d1p4Pfm7fK!_O_TIW^A;z2lDDEm9#t>g=AlJnMG=CN#^U3SRV#=cq3u(7iBB* zm@id4R~^|}tf**o0odR%RA_`bTXSW!H#-H9~;`2p;A8&^*UF=W}+Av_Q4# z_zII}lvOsR<$zU*K;OQ&9vkL(J2yHy21X@m(%Iwi(cbCJ{yTe9x*~46GuN$HawY^>tQbBZGd-BS(SAv5Moeg(Td_RS8zk51 z?VJ7A`*21>UoJ}u6KLViPNa|+*4R;Hl0`1Jl@mTzMfP z;Y^ETuTf|5ndo8y2YvM@H_XxdF__<&wwONVR{;E!ln|i?kpq|KJ#13AaBXe`7>4wb zLm480`vZH0Z`dwo zB7&>=7PS!wY-CE)-iRW$CntE~b1@XnX$@V8`uXQW$x4R-JrMOJ)}K1_6^fm>lG`q$=nTk>yi*xQ1seTp6K^_QU!T9*C+& zET|g(Y+R#`ti?KfoXIE(I470_oiiA$ceP5qM#J4-j^NajN#%+mVGhG22Zd}e`z(sJ z&0svFZ2|I^fq&n(mt7bODG=TWUSluwJ~8_Zf#qb2{Wdxhq>q2d9WwD^+6(rGwviV{ zM*(yDg3yCyWP>P0L4{lb!-lJ0>99 Nqcsss%~LyInCvLq}C)Z*o;=JLUqq0K)CD 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 Date: Mon, 2 Apr 2018 02:55:25 +0100 Subject: [PATCH 121/287] 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 122/287] 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 123/287] 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 124/287] 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 125/287] 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 126/287] 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 127/287] 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 128/287] 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 129/287] 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 130/287] 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 131/287] 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 132/287] 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 133/287] 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 134/287] 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 135/287] 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 136/287] 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 137/287] 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 138/287] 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 139/287] 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 140/287] 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 141/287] 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 142/287] 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 143/287] 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 144/287] 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 145/287] 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 146/287] 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 147/287] 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 148/287] 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 149/287] 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 zcmXWCXLzIMbuF56Z=M{>(^yc5-h1!80|X0LK@tFo-g_@3L686e_By?fMx&Wg8};5L z%a$csa*5^Gj^)I%6DM(s9or}NNfh(Gqg*^6z8@@q)_(WiYpuOsfA-}^3f^?^ zZG!Jj@cmzZ)(75y_GkYO_ut^`rlO=}%vL{KX$1`{Qpu`u%S|`NhBNzxmI8% z+0|=m-Rkhn+Ytliyc{Pel}BRi$_xOqQ2v*y!W%uR(YhP4s- zj!W^}g7D0ocGb){809pFL=#H2P92enM~3_Jv+grvGtGdza;}+kq}`&KDe;h9G%#s& z`cswFcxzpik8|=t&O(AepQlBG*fX$TJBhLJhGmd-+SwaKfd%2zkK40|9ti6=;5QI8^8aL;;V1? z?%s>N{64Lm62*g|jnnvCLbb3c8nkzz@egq5eYHUB)3;td`tRrd^rx4<_xWeHLt;8ePp%vm3!gm~dZgniP4cVGp`|JQt%A+3 zQP-4|rj36$LpZSWD>6(+g=+P)EG#OAYU0q0dP8z@W?OGAPL4V+)nhiF4L4vR>#Ss@ zg<~C?i4?+%EoCLeSxEC+3&cVcIX8lE3?oMSk%PL!#~%Us|MpK`>^yacJ|IDI2^c;R z#l`ip34JUaoPonK@N5N}sAD3eIH;Ha6XPI4Y%d=R;bSmD5}ZvwfkvJ{^!1>Ty#y?t zP8-t61zI*<$bbszC+PTI9=TgWhiU|5s|v4Tb<+_i@bHs3_(?p}=UfUomuBn>!^0V^ zW@g;#8y*Tz%q^IP5~h*V&}3!Ozv@r#%%*p|$<6Zh8;Q4HuYLS+`CH%GdHL0ahwrMZ z)&BO5=gQ;wc9DF0jrYnG-V0Y~hbs^C%l=<3iFeq(^QS-j4LDjyM@v8YDSpa}pozNh zbfQ!l$*!uDNwMy>wEtBMX&Ftt4(+Kxdg`Z+uOucCpS=)#c-@ts2YY61`~?j&jLV6hp=x92sv}2I^X4cqx~9yqTGa*qNh~tbr+# zx}RYl9PuZTt*wD(omNd#i!q#cVk|KouO&qO3H;31%=+4I{?8vk2;TebNBzlJgph>d zkw6rXY+@f9*T=%37#Ny>%FuF9axwrPE+RmLgdRQ-;ExvIF+$=A8t#8%k;l-mUJM#V zp}`nb2%Ci95WAQ-2!{sa(;-YMj7i7IMI;pu#vwu|m>vSA3x^EMtpr@l0oTgh)WW!} zppiMug8^T7%@^v7ITv~AsMS%Aya5BBzUpuMXnPcGTixu3oeez0S&PH`I^TW)~!P`=-xQB~CR zl>xznQS3E0H!eVR^r&?!&BZ4Y;V7ke(mu04=h=w)*3T_0BqCGtQL}8yDW7zcwEZ-r z)jAtY*VjfCs+iCuE;vbc+apoWrBZq*;bW!}nr8FGzx)*7|IdH?L+R2EQOm+`zYGBn zk6`1#gJfdy93of4#;Iv=F#+HY7vZ4-987>g3JDZB183y-YPhHPlr91WNx;)s3?he4 z;L+eB29!^QF-X05>?s1On@^_bc^DZT%EZBGxE=yJ8t8ato8H-m&)Jxss0~>&BcoZH zz2phD6NUX@M_oH!_5@p@+)f~WkVrPMJZ_iYhFu7vI}z!P9jx2_=L$OecB}BmUoQRr zSLOfn>%*g?yI)qP`X7EZ_rW)Y&fO-t!^f4HV? zm>KcO5^?VAlyc6&iMYA#66Z{7?$PUi_^&TO2%h-h>(W^(noq#-C}d_)y7uq?BHL;}jQTDPvrEA37IIHf@Lhe#lW^225>+wolS{l(#Z7_f zEh4Q0N8W@(wz|7kM=i;BZU(QnCkxYpc5ozY;N8w@#uSQ#nAR|J_uTjwGNKRzyVln5TUgxDt{B18m|lp@!OnFB0#45Ev1&6GwPk$PWiTZ_3nSC0O4^j}9umtKB`Kc+$PNoXDk#V7W0i3koJ#lm4Y zIFXXg(6iBU5?V$?0Q@ERJ`n*gCQH=LC-AG;llI{g0(KXf*iFK96EP4n5ke$(p|DUald7~jqYDC>U!Zu5DY=6s z9zb9>j(>H%IFtOk zq!;26!KL6t5>>(wV15$v3;E@nqJd%q;ad1Q)Nvstx^(>s6 zgq4#qG7?%sLW{{%DV3*Y5cmudi%DiMp%m&@QOILZ%!xk2Nff%5Kmg76D~PUcWH*kE zB}r*AlaQij!5C<-VYpY|r3 z42>Bh5T2ysx;SLGLD6S1of7leHciIw)(wu4`v);fB{Yj(ohi>&sjq9H@JwdmbZp_xWYI#jn>qKdbl&QTLd zd@@HwlgjyW4MnHsj*N4yBUIy{*giX#U$k#*Y9G9Ad*+#ehb}9ZtNeVFQ}9XK72}yL z^NmZhkKg;J-~R^)!FT@dXJbV_T1dk1NhmJBpNIr0z`|qLM1g`UH1mmS3QkGE$*E{5 z83i&$LS`zcG$|9oq@bxZ9F-2mlTIKAU44X8aBNQ>5=et%C|Eb955vIVg;cRYC^8F> zeB9JTd3v%Oa4*L~?XbTU_BTU;cF@zx#X5y(XLhcQ@5{P|MkCL{`>!!edy61tqh@) zknFZTy_zT+LJ9i26*{rkW1FvgmNv!0tVsJZg@2Ps-014LspOac?)~{QOXFpS{B(NO zuMyu)%h!VT84+{E$XlNxzPrTU99P#QsI7j&s*y1%WG?%pFYT%Cw9qCU7DJNKIZ6`O zfTt_S0)=eCp%`&ej8^Wb-JYtrnjPgqyZ`3pp^Im<)tsalqo)!e=J z-vjvn;ZJ|sdi)02#KrIlXg(SBKaH48Kr`_)0bOYl(G6_Uef%kCDG4bdA;l!JluQFe zluJQSNpJ!Yi6`~oNL@(6DJb?h3~>Tb4m7F@kA_h(P#&3X5UXu!l8owcEsl@RPfX0a z97_QIkiY5kE(bv&3bX>Vja(d5r&hq<@P*cGz8Yg-=s1IVl1V+mrk-RFyT}C4RlB7E z6zH&OIYci~4r#}xj9eqXhk-vvr|`m#%$1AdwK~cmlQ;*Y9;YxESGdyDu^4j5WAr5# z8rN!@k6Q;yY|#aQ@tb5?6Nb1;#VmgB#r$DoBx{$qLUy-G_;^u%n4ED5*qejWEeGT6 zCE8i1wk$yI57L)(l&C?p8xuddF8=5t&E*X)hsB_=gRH@^ zfl%5zzpQIkHGAuZowmMHm#mbqfie1=!?>^}UtWXEjJVJ3{1F7;=;#|i{c(SC1}!9_ z1ymHDfaDU<90HDwCvr(joq%T+5|uQff&xkqR!jtnfFLH)Wn?Uu+(W|kQV38A9!@6q zVz6DH3PI7w;fRw+Bov3nGl>{E8=)792NWE=AmCkcJL-<9rLobPb7mv@Mu$ln@}Y)>=ZTjAV_jOT^OT`PUj zNGlHUx5NC$o5G(w9r^gV8T$l{KvPrrMl?}OF^$M-rSZ~&VBAi#3>oLbfnwd1%Me2j zdM2iBHYLq6W^Mp&9%8wYB&Q$Jucw7v-}qY~*^hqnpZ__3?U177;sq3>fP~-?F&qL= z1mG=}v*i{MNkb#bX*f9<9Dcxp&>|vJMg=xSACp9t@n||3j6*twft`XOd;5sT5!kQ7 zVK5Aq!DiAFBB+AL9a77N)yYUF;#-*lKEvd^-9GPeFXs|F)!be=vzv}>#R47wTq7P@ zjRyfiZHze*ShWnR6JceNV`MCf%Y?F6eM~-1Afp)+JfmWG*7Edn_Jw*b=Nj~v2fzQ( z^`o!dzSS-dOiZO>xfy>>VE00_mg7XpNhG%i&F>+~Pcj`eT}Q-8Nc7Khl~0n$jb6yL zXD*h{t&Y`Z6}xfAj8u3fBD!6iwsV=AR@wG6@9h=V-H5d$CvKRD%R0vLq~u(h_h6Ov z>BF&~y%gGA74ales>n*_8tImi;pZOrK6uj*j?&EoifNBCQR@#x`gCHbNrg{%B`alF zIfu9P!}$WJT-2iz(}(1@@7??|1CCz){zq!hAW=lc34sq;f@|)% zifUpCDFVHNM^?zX!1<>#pi~N0#3GBiNG?UL6^&XoIj{8xA6!5Bub=ZZ5I0SlG>{Hj|Mx&+NQm$llAM zb@A!FBEfMs>8n!uaV6&gHst{d;sHYM12~A?Dfqi@-#Yq_pa1B+$8rmyo#x84+mGN& zA$%49VPo@=tB-!IbMqeHgiyM*X&NCZ1` z!q-=Qqa04VU$QyFf4#wav*FGtX&p1QGsLb~xrYh<#S-g6iTB#R;=|j6KX`hil(uvs z#Wb$Qdbl@otg5e>3 z|Dcg9VxPcadx>}?okU_0`zVBNGzmfAbQ7s35$Fe?h~o$(l0-r=*;KK3v2?IfJw0FC zU921~Rd&PPro+()g*u5SIQbhOD23E^F0qwMY~~Z2^I1@oR>Q$&6buWdT(k)*#$xIl z8|m{p`+U>rN&ReQB9|H&_e&moX60Z1`rdzi{qc`(oO$Bx&O00HJ1{a- zTs-*pH-GhqzdyS1*w-n#aR_ayo8qTa+)DKwiRA+ltuZ~kUk;htLCwut)FBqEPjcR9 z`yC?5ibb+M#s%m9o7>T%mbWp;TDNgZ7U52qf2qhhU*J667QKB@_vurU@7|jxu>^Rb zQJ2q;J$OS=&XXl_hD0Sbj}CZ~6WQ!=Aufn|5F>^zIS(f0!-3ml)3M_r&SI8Xj*qn) z|N8sigAn}wi!bVzw?%rsK+a`J85{{iAmxgsLY9C%(ytktHfV>0N{d)$QJAe7%b=C3 z6QcxpD2s%mQ&ChRgpBGY;Crz|6p@Q#3wvqz4T@thL>CeT!xOM{Y9+g0Ol}raorS{o zQguI5#aVGwi1!eXm~x~U-iy4=Uk0QV6B+iPJ~;z#47lgbhtB`uJ^YWT-!_P zOt{ZA-k))NaDDT$^X)gDZ6E#mheyBq^!W!5OVz436^O)V$^8<#lI|b0s#S`UWC}2U zU%L0>3kPor2RvxD3Bs^aSVJOC%Q*HaOSr37ZVszdTQU8U3n9Nu*sw94Y7qfjH^kZ=Hg=9Z-1a4GYA!Yk%B9d3WQ=Im(Rzu$SRdEHZwkFAGQq|3?{Wqrx0sp6geFw#scpG zN+AF*^s6M)NfHKvCm{)322Vnhis1~#F(mQ?41N+0>qa8evCV9Jy_DK4<+kd@{c2&a zk_RR7M>BOR)Sa>bu>qPvWbZjjYZ0C~e5r1pGe)<PfFa-z2WkVRu2T*-qfy3w$tJE+{ z5*UeG6Hz{(G0xHPQ+msbBHgn>L5WGw?Iq36)gykXpgziYc+u-rscS~@)&&3MI^}O4 zER`(s?S9IZjg!%^OLqQ^GUIfabE(35Zd3N!1@$}E^jFV}VYPPA$ehIK*4x9Tv8Z~` zVVa7#lgsn9MmX-1PufX?gD|ZIp-|H0VvdXrlqt`obA+?Qf#}waYyX4)YoENQnb1fS zQi)V75b^0e29-_5GcYg)MxqvJjZ%@4D^xI;G6qt3-;_p3NpK;)n~d%SmJ6u=6kHDu z2g6YDG!C0DB(d0C80@J&L^lF)0tStSn%QJKn`jr)Tk+s(DzRS7Z7)=Ii>ZwukO2Nx zz}E@~+S%l0$PZZPs?W2UPi@WT4=TC6LV6?OU4E!~{_kFV^6cp(YfOtB()0~$Q8uG- zdT!7@S4fA?WTLLv)IvVFSjhG%MW@IVs?9aLdY%^zoep^OJm>LM;ky@=FP;^@cFQK5nqUq4Btw%x0;`7I(gCM_GB_JwbS7)`krAv~ zjgU%C(g@vTJYLA9%h*^Ui6Z6*4FK8j>uca@{TS%-Yg01=dertZOy|5eiuhdn7{ zHp3M{&5%(rJa|gL?qzZM^J}{={d4BguhBDpe!f6*&USGGa`mJD47KDn8qTiP-xC|2 ze&7MrVP^Q{CAU)~XpFJGw(c&C*wSkG+8F=kRsQFv?HM(9%|uxrq}I$rr%rOE$T>*U z&y+d$I)b;)NM1TCee*#>qCSH*dkjwSxU^$)qJb&(n8y*F4;7ly83#-*f%4hibaEeq zf)fY`ay~*pg0Kh>D&Z83c$`jlG}lG}6gsw-&2snhR&vn-`0jt!NZMp7y?#EM+KRdwW3CW$&hAam$7-$qg(5K@gv{BGDI_Q8v{Mw~DJ~l! zRbcrFoW(lS+D>1&Q@?g+y0eXS*a;f7Rukn*7W5-m1cEq^UNen;(A}drlOBGl?r{kD z%Qp5~OJggOBQd$CHO76d!~fZSe@eq@8mJpKcHJr%mWVc^f~zIkPM&?I#d>XD@a`q~ z8&^$NFOH-7r-$a!xqRK~4oe26RJOTse>PTFF?zjly`onk=@IatJRVXh#Ta#H6|a|r z?V_Mhl5uc8_42vW(eHl+z<=?3?<;5fdzmOW8{5amf;W_d1v4xN6GPWxgC%f$=IgH-Sl}{b91eLw-pbp zZ?9b5Z(YtOcIKQ5zVOm)IB$xFO{EmI5QmMAo#F{jl7Uk}?4eSSJQ0#7rw&-BGlg5% zUU~G^yMv_##LTdA$fZ}G%2?^hUb#e+_Ej5A_Qh|jxm z*v{Bfz;=VfzY2r)_F?+abC+3oCK2IeTU&$vy6`G%&Ed%_+ zp;b_nGKr0a{PtpTCzI$TVw>ssdNkNBWcQ=t&Dr}(ZDr2ga=TZ;{*{B}vpXvn0Y~*X zo5S`je#nBb4?zYECxratIQ%IB{sftHf&_-t6nQuD4bEkki{2Ijaaf$ zO;bH|Q8j)`uM9{zlTzVM*9pa8X5g)sds@U<8e=_|H&%wtJ^^PF=-+M0Pu4|CdS+`t zurtbCu_^RSaePd0HBY=)q+h8spI&3Xx+i>gU-`;|Ll@S@42}rjZD&tgBxBQ(VTWnj zAI{ai%PnywCrAb2nfzj0EI*n$DRO_v}(4(gxfq0YWg>F^@$1H1cjf zqnk`Tjs*mac$~)k-!#et7NM;bPVdC~{d)1}C|NUrkqgHr-3EO{Zrm|V-%(okb&4t5 zNSQ$nJn$FPz2v|POLJ3veruZha$Z#((1gU??HR$lJF35Gvl>R~qLF=Q7qy0^CboEX zKyfz9yphLTnWx=tvR>a4Jh3f#>QMXjM@Gt(z+fP~RE&nJ49v(4YUW z?w#1c>ka3WfVVLx{#r@4G$;!QxjPQtTf6)pu2XhwoJ9lo%(Qf6NMz+pOd`=jn0q!u zxL(FT*kHY|$$NN1^5~ZK?K>l{-E;uXbMN-eSZ7H%F$3t(cqp5ywnwsQj%NnpoI(!w zL*>G50lizsgy;lVH6JCU_cF0?8dh%*kHq&_kYa$k$#kz{f&z@J3}PCi5? zgs`dc$VxoA8jY+1^gs)aL_qQHo|~VY z5%CTJ!q@Yng?_P5#66r7e2G7Ke~htY79UIqTQRiS}EnWffmaoJE>&5oZG5ZcMFB}d~UOl+X2`v&7ZE6c76WF?Cg@?*Yf+C zq0o9LwBw#_&w@P`UlX_!OBKLfcOs!RrD+DvrrU#oS08=j=imJNlXw66!B;-WJ^xnl z#zTXJEMP=jiDqP^Z5+DP@4jcST^uy}+>uMZ#0!)58z;VkUGi$5s?B+n{EIQ}$2IQO zsCZ7qzu=d?vBUkr8uf6JRWb4o?Be#Q+`<=0n6k*IXd_A9E72}hhp z@%vAWzHxKTbN2=BJD*tg54Zy(hG}m&-w2nNMbi!loz#Uxb%DhIA+O7<=ru_CI8+Ri zfZ`G$EKC<31!oaT<=kI=`-iW5@}W630TrM?^93mY;otyS>Sd$57)XqO#4&QwS_;4) zrXWIP^hP?>Ddjf-=Hz}Mo_TSK=aGod`6O@c zaz94;jsljb%P^n(if$|CETb>T)we|<;)#?7&Rd1+$% zkqhz1U!GWK2!_TKqch%QHC|nn&-!6(4p2kgczidH)oU_Ch6cJsEC>mUVG-dR{7DMf zLS#>mj$XNN_4!xd^{wV`8XA<11|fiOF;Le16zFDQKrdivxI`ldp(OVyaBw*RBE$7c z2^bj}FXzJht=x^MJLJS1ck^slkv}%{O+1>w&rnHL!m}I*^bB8 zWAXJ&W;>JK%w;!<`Rzh>qg>pbFCAnvo7wF4LV2%J+DfN(!r_f->Gay-`B-EX#3CG8 zyYEy)R__ndg3VxHHRN8j^gI3n(f1W3;;UH90|fK~Wb6Y%#tAtO%BBwu#YgN{Y`*7> z&L>6&^KN%5ntjIMd1P|(+OgxbhEM-y-I=$FZWTCB#yOiKT(4Ag#w~ewSMc2q>DC-I zqv4#HQ8q^egA%ElC)M-SjR1cqOKa!3*H*aCZ1Fc*nw>4vz4ODre0KEXm$HtHy@7C& zKRm`8p0J0riBePUaGoUNyTH;87VKuw5OOU-Yv|!HPvOvTDp+_RK-nZ1myednRgRh6 zb61wm?&-X?J~8ggE`Wt`FrWnkQrg2rQ>1jJi4RIppB$tB=)d@02_7LKkhFYaD9G7d zMdUqSrC_^z`w&Q~2Z#zf+UWw+y!q%`cs{{_eGzQZr$2hs0yk%;5>EHxkR$wXWG?Z1PDG z9-My&kpeaTN0xAG%%KUuD^35H_en0RQ_G z2#``B%}_i7&%h??$@lS>-G2c75HX&rVlrJ5qP11V=E_%ef@59Xy=V-aPVW=ad)Q>t za9%SIch2Vh?yPGjGv}(z&Q<)ug(T>|navb1JTsf=>~^`hS1Ip+j(i{g+)lN2eqsK? z^1_9M%GqdS-S1uY`5Lj^1)-WZuWV;@N;v$uk=$DPir(=%6&pI|P|YTjLTE(}Q@tFhlG zFmBHB9b(bpob>H2-p5Vy?KxUh#W|c3u1%^eBGrJ*U=dr!Os2yk@36$)UX(qyCAiQM zCzlM@FIcZ`nLm4YNy+r7ne)!2}pL+XsS3Qi9h3mIMTJ}4H~OC`fNOhC~m+|5zf(sW>X+FSE@ za*oOPHF&@5^1wh9u1F%?2>Vx5D2kJ$6T`H&J^*Z#$!^>NiMaELO4ancTw3AAR-G)0+NP~lo9)6 z1ek=-BPJkZM3$0VCDnRW5Fq3On_Mv-QQZrSd_wdb(OU0}`Z=-V6nr z9^Z;Tu?|0V{K>UdUN3{@Tay41ZgBVbCv%fT{NVqNvuu91H-*hs9%OZ$`1PK=*qW7BACmi;` z*~4G{!>_;l^S}14#$n(h00#%-VPWk1?EuO`1K}*x@>nJgMo9q+av%h~5&~3$Cn?Dc zHLHsUST3oXN9_XTjzs8TfaN-Fb@A+C=knU#oz~VP^|hPP!d|qr77rC2lc|8e4hl{p zz8s6y6Uo&~W<8r-FM{bne!EgW03leMKU1$A*2<@I+1+YkJD2Q4Lu;TMfye7z4u?BV z=kk=jHa?mKW_3AtIt$XoziKZu%@3b9-+9#6I@j!cZ#MT$yZd_3UCre$r!sdV!RsOK z74O`Q6DK(N8O@uGnH{I<-U8=?8uM8nW0cF;bIab`=6|$Gdoe_dso58t^45fMOluub zSpoi5<>2&i|9(}mQ{i{!#rO8a%S-C`is|N!k*kk{zWaRo(>IcUd}qlQZCB7owLhcj46ju6G;V89BZfDF?qyC)rtRPz15{L%0K_{F7{ zZ?kOTFYP!0J`NBeKvp446jMSMTljeG|BpXJO6V2iF)}<`!$OED5Ve>%HHNgAyCs}c z97-RXUM%iZm(FKuH=>m*snVJG<+JM>SBGbu$&e=z$oqY@WPGJqSg#f~^4WI$elDyO z^PAQCxp2@}Jh!rRraphVnBU7~cFNgJFeXTYmH}{%ZG!Xc_pi>)EqmwY!IU8$UIQLQ zYyND#xRX8i_}Ja&<`14&?tJRXz31^B_`TIk?q(u+D;B;U2|eg@T|0S#nVr$U-*TRI z>z=9$KB+Ta@N-Al>^+a_^=K$STaOvd&FeRh{u3<7{bZuz?Gs?&931q%7Xjoj za70lkE?#2bGYo8uoPv~rSpXggVX&sl(J&cWc9)WiniwUyrr^VdZVkJOO-&T`yoq&t zc-^1buQ$%r>$?)mM7Mww%}>3u-e{(4(ZGB*)vV?>fDu|Rucs1?NUUC~+-F2}aQ+uT z6izQLp2_F;ft9yV*sT-}bGiLW`^H`L4TX&h?&RMMmWBw;dOK`e*9GZ#S4PN4SG* z?)I$WwGI9UEzYYkRz%I&otCc7sBJ2A6}_KzCJqK=uRjQ;Gr5a7SDth~Ibn=2U8 zYh&l0PA_dIk3KHE^i)A<_X)?Rr4ufDG&_;ZlPAV{q_S?Yq>DxCWia3h1JX2z6pMQV ztZuywYE>devY z2UMqAYCE0S&ZR(f4r--y<>Fxhkk#BlC4X4X?SP%GbbK|RXs>`ilInQD6@%2n-qZ_V z>8@6}Tqxa1M6O1I*MgqQk-2?%FLTZ!{HX1`9x^;t7kyl3ycy#SvRK>VZvo|GMwJU6D)1Yc_R6jB?I2D=>7K4`kn!Hn$ROb~}clFsNv$H<5`)Is!ss8@0 z%s+f9GT`wFC#HqtQ^SG8Od?Mm9Xur!_6m4#E)U8P!p+lgySGQHI3?kt#>QpI^U z@81+4P*e=du+}u;G>;@mO4BJO89OnKb-T`-xn3>T-NBg9GJq9}C<+NxBgZPFeG*Y0 zmwSSYfr)v86TW-`h=QeLas`B7bNR4Q1=4t>R0Prt(4oCX{R|*Qh5OWWvyk5}<_`<*cV8?Q;2gF@HM~zZwf& z3A)cFJv(SPZ$iWWV0Gro3rUqzOv4HYl-`IRuEHjx5fo+hh|u2 z?3Y=GwWAX@hj%KR2~Dn)H2aH^bV0eZu31{POw8NrSN+TPTCH0Aiyu}J z`E)ZJn$Ks~ma5y0%1$=5oXf7(%IlTlT6=M?Q3W2w#(V`>wg(H9!(3)75N_AXhpUaN zrQ(@f{xFr=$s{+IihIqNr8cq!`HUq~E~@lvyZ z{84M_(TwTYhWz~n{@ZcBmCab4RJ^h-c&|6jb(6)7B#7X8w3yqr$?`7UA+G7Z~d@&X@@-^ zKytAdJ|4lt!S4?Rz)dBLOwBV5%UOB`QAGx8x-=D)s-&unLi@Otsgj%`5r|A0O-P4I z8Q**V?@ynJK}d&SszvyaTUF76Ru1f`eo*R)zFy5K4J4XC;f}b!G)r_y=Yi0 zYYR2)-kNDTKQK7&TzPHXy)`3TdG(ZAHa?}9a?M1u!~QVNpoPoCCnYSXLcksz zR!@bc_Bq0k3E>!_7E{` z%G6SpTtL)_L0*vc8md->Q^|2^673VY!EZ>*f( zU%j~3z6N%U;$W>PwpmJEj(E?-+*|AO7d3JlxK;6XWBln{{|hVH_p0o7lEOhQb7eyL z{Hplf6`%+>X)SZrCTva{2UX?)^`Ol%JvQpA#HKbX#(Yj!EE!r$nsiCu?pT5qn=bFH z-mO+|)Y7@qovX2dIhS;7N;*C@(pI^%-8?o?wF|(RYG!_b-`4aH!+LioH zWByDo)v4q*8`Z;7ey=$XFmK1AfW)3D<@fK~6p4*oW^=i+x3+i&{PBF{5Eysy@NzP| zl?fk!!+)@P%b;^m(Xh9cC!Q->pKU6?R^xsnEgt2um&Y_OwZ&gsrGKZ!N~>tgR?hN_ zZa`@U#b?~=n03TfGnRHqT`C$9Ib*Y7Ojk_R6;rW3A&EKtXVdLx>cOb@y{Fv1>=fTJ zDi|D9jX5U+g^k@OEevj^c`y?};-Ae#pIsztZn_$r;o7`9^?e4X&7jg%&$a>IQk9%v)!tQSKn#nXv z=tI1{Jn?*a;F)FZJ5~Ps8PSA@yEv|Uu_Jw_MgQIsE2X6^_H!FkhEeU{pl))~>X@GL z?-UJt743Xp7ftDl3;nhFP)Cp4DfHnhp7vQcO`_u}bOP;we0Zw7 zdimzfkG$)L!e|URVeQfhNntOeRD!xkPI4%xSk!JIAHwByvB=$QN)MCT#p9v|2Ryk_ zAQ5F5xC8;7C?w#(U2HxM$Hf!);F1^BI;0;Tx0?DjYNJ%3;}PypWUvq^9x5jGut+^* zd@q~PO(mlQtjTa}=kWIV3y;MYYI?^I-)10MGlZ^~okHp1;^MjN^Us71@AxG~8(U=*8*TlQ0gE>|m8kQbo)d%m9fCUSPg4lc4Y3exR%tbyc|8*B9^uUlgjX=>TT|iiO&m-rDNU z_Lg-$9b-M6+d4ZZdONo_`PbEgUMOe{l|~x}62nIJ+ zyW3luH%N6g8HuTv`b-zX(i2^xvq8pN^=u2B)}Rrd8RT5)!QE-Y1N(<$_#V4d!_yeV zmLj9i@9}MHWv^@DwS|b#%-m8;|12K(X#O=&zAb`-K{&PYIdD(P#2d{ zWLj8XXsu{f7S-f{5X@!8!cZI=;2h8^Fo@R}_;@xcmVyDs&(J87l)_jZ8MM(7P>6U8 zBA%F!fEmTEl3IU_x=>9Ok#GI4>zTBOxO-J{=%P6Y|L` z+)xv@GWL95!R0pbk#_NkCg@x(+bg994D1sF@a1med+oS31-3arnbHD9VWF|9 zx7M|#oxMIJ2#1tItIAsj>?QlAdgewO4sVklI4a(F$TA%0o${UDSulRQSzcX95e2OzWlPc75=@E^6U)z!|6hPy zIv1THp`}O}VCcCLmA^7S7Xfp4CS!Q*<|Bu1?bvtH(cs~jWCV>kSH=Ue00Hx|m{1~v z8b?H>AafEjQ{&T8<5H61k%<*%)8)V;yLMo_PH1UGaB*ZFD&0n5YZ>1g#0g&POUoc`p* zjJX!$)i&i^NO-Xo-tA#*aO1Xk8Hc;smwE^{Lxc_^u|B8wl%K5ikC$5`MzRf@U7H;&sq`P}~rs>XE*Ti(Ww4s_HHB&T&=8BfG zx(L~7O5o7qDL9~&5r@uO3G9zWy#^CwHN5Q7!rY3&>`EuH!Jk-YSV_XXB6*<@iBvX5 zXWhH~;5(P!?OWXo>JTOm$Knzo9-YT$ve+~tgd>5Dw2A=ipAOJ*0%SZlKY^W>3UnmI z?pWI zG~g5i1Zhf4deX|A1gK11x^6fyJQfKK7d!l((w6G-5D3amzDNTI%5}ZX{^7yy$yNP3 z*A4F(?A{RyY;0--RF1U(ci{yFuN`czUtJHzouCa~JyciK-&hst3JmqNuGYvL8_UHv zBZh@G;mKxrx*3{mz)ds|_J;+>Tc|f$2pg)zAsHs1g=COiCa`%63%54HyE?_2SKBJK zuP-`%SAF~w>9PB&(+}lmJ{6q!2tD;4)VlN#Uasxri7xn318|50n(u19uc^?5X#5Hv784TWAZ8^hU3l-0XSd`4B0hV!6c4l09 za%@^6;IzgISn&n=*AzmY)(~#$t#SuF6)oMNk;b|XpSL~W>uPW8iv&kj_l>XZ9betM zZKP*&Z^wp6a7|0Ya5%6A{1Ei_*LgbXJ&{0NKj^jgwXf?64Z2-TRWjWhC!F8l$X`;~wGTf*T-=yj)*ZLLEKt5+TG)T(ttvlpwj@NFJXL$|fYpDyMm;BkpW zTpX2OMV@O3;CJqN+a1G+$2w47W9AOaH5DpH0Rn;`%I-Mlz90BGgr!02oyDm62EWfq_k zpeS5AMZnGyL+N^DwpwLxZMy!>!|%UaKKJGwTfLj52e05&QkpB`r*WWU5+)ts7_(Ah zfap_dGSC`|;X*MG9dLe;FsHntsmjv|Iz?Sz_SH6AQ`OQ`-__PU1R6xW;q@ckV}p?m zt9v)B?Hlb2uj>x22{#RQ2S>UBgTdMk;Q6~ltA{!^4z#cJlm?3h)T?d!Q$4y}jiTLQ z)?_nnI>ed@lSV608*1qL!@|Q&ta345E6{8BhCY{Iu+v;J)~7phM|$>&?9%V5%l~LD z{VTitoOA9k{>9&z#~$Tvev1=`RE(}3m4^dvMY z9t|oBR0@}!Cs$-bf>atIxOvSFe=Gs?@2R(Lnd(X)ogAwafpRRJ#Y&}9Q}Ou;h|JiG zWMKc;+!TOT1tYu_sNDYt1xC+QM{x~!0xnmeJvcJZ0j84?5DtR%y&b`!p0?Fpt;3*S zx2ktbSKGRQj*Xpw`V?3-)Crhqt9o08hQe!xI=~UEZL00IiJ?xwGULW7e#h2M^`Q%r3*XAl{UE>gTzv6`^3rqZ zr5C&l&v+OA=A8Oou<@{NV8pq8-Ih6TMW{fachd_>NyZ{aRh!$_g4e2J=!7^XDNaa@ z7tzur{4BXDkqf6-)bvOvwyh!7B#2c)@n9k#=HS>6kwqG*}bqZtPjxyLlkIrnPaPP$oWHr=6(Qjx`w$^$QPmQV&L$ z`#Oj-KHT;?=8+!uL_O6ALq!U)$EX^q;(GQk2(J7JU-?UX^>4}57x4L)(u*(TmtP1k zEyCyj;hp)Ve0)?hvCF<@Q`d!c?u9OyzkzOYyDC&yxp0jwMWGpnpH+ii_k za}vbtc$k);6<}&TS>@IkA!VhEjq$iiRo-lc5W%A(G{S6~JkKH2hij*gojh{kbZwuH zD97YOKok4DW}UG)*&q;rp>qtA zl*s4B1869P7(>F94z>LJ5-8%l`ka5~mcH7>(92OuQ98iM^O#vo5t;|e` zLu9>%N35V=SCVl_5D~$ou#D<*U&vb#Y-{XW(=*ywURPh?^H;ZmGC9&R9Pk6^X$JsW z^tP{C+q<b7TEVL@UHyHy7C;p@`8VPk#k{@ckZR~(o4~WCHUMk z{`p_Z&%R|ieOouOq2kmz`KhC#t`4TA6s@($OT4uW5mj+X0-cg9fpOj%VnbbuT@|Bb z$4ZzP5*P^r0Ypoo5L1K#ba^GAsy4|WNY-#Nol2xfi?1~^LM1(WC$HVOwf)ePrc#ZB z^N`GZAPof6X;Nu9c~Cw>NyjJ&;QnXwQK>v+5(kwdK*!>N93N^05(qQ_TskTiq9%Y& zIFlC3q{c8=sRH`m*U$c+{lWJ=r%%dD%ru=GAs1zec}DiXf^1dR+Tmklsg-Z#$m7Na2Nk*m^0l*pN~-I8>v%1 z#<4E?&U&UtP1(_=Xf4y2H;*e%eImN_Gxg$2=;|Wp;v)aj66f5aP_0-#^V<*o`2&Cfb(OexI9)~ z9uueobaV^V9k0WEhpjLkcudA`ZP0g@{EX04_R#gpDKPSF))w zT1lKl_#YxcSZ)36*I)kc2>$qIasBiji4y>hB`6*@4`w46q-0cfTxM!adMbdb#xiiP z30ScVYAhCn0~En&Y(IG9$cK*)y>ZXsto1uh60kq}%#!HhEBmW1E$T1)t-AV~ z{PiEpXQum4%rTCBf#0}*5}WhY7N)h-Q{P$UYs^+jRzUQ4HYJHmNfJ>({R{paP$DH! zNQrnt0)?9htK-Fj1QncW=H`2Jw1A5iDj_!75mjnTnWlGq?D89LuiH1qv2$}7xgZ3m z(-EoE>~u;d2FB9Ww0s4K0_ZFOI*p6Ugpo6i9xQ_h3JstkkcdJiqk%+Neyp6ETx^Ti zDdNebL@K6p&z8Rd3F?18KmPoDaLc4nr=qdhXf7KegfgjEK=E6dl^UC$ox{RGT7E8< z8k3JqBw~b?;@08Gk?n_@x9+A;L}hBj>0Ku`_iYLJUilwTAXHTZYCSETfnk@?X%H#{ zwQb!Y5THR&*w_=^JkUAns|+<+g!OjwhGy5{9?{Vb?tw6X15swYr1>yst{s}G7i_NJ z&elUmhfIzmZ*Z^tPCEMxcl00Tr6tzoDC^u3bb3j6Au0w>;QV6Qo6lXZ|0TQpi~9IG zoo5cg$Dfd=-pp1S^W<8Jso3dl^)|MV3iL5NW({D0hB~Q0e(RoHjgGDR~4xEa$yVx z2y>C?1y)^MgErJ62sDZr{9=`6@4BfieVYQFfVaFU5wADvi z{C&e9JMAA^+rMqo@D5PE1#Pt6O6B^7qQl+d6CK=xoxFt>*1V50-NHT^Vb0X?_WOCK zyP&hHl_d+8nV0{hU0lpR@)CXemHlaFql`0A)|n;#g+m$qv!yf>a1FaOFp|Hyvo ztnlbV+JTSqJv9iU38%9bR5dj;b@J^6Nf0BJLII<8;7(UE@Nq11JcE+JpeN92Nla!E zmy;-@WtxPjGE-KOGFi#Zl5n%F8e*W1+}e=mw)==X>R$=NYEnTdKSB^b&xni3EQASefD zG8Mz2pPs+UMel$A_uDt0o<4i0xvxvu zB&iO$J&|^GS7&`7)LB*C+t9wYZE#hn-&ayqW_JgDk-m;K^>rNr2JBF(#`-o6wX6nD zVDrfM+JUVj{S%Gej^SGNI=^tW*L)}f=oq}Y4(@!2d7_bZpdDK1pdD=D?({*#C`X%7rNESQLLIN*B(#ut;Hc4Qx`}nETBRe*5?7R#h4MfWW3QozCtW;Vy zOG@JEnP?e43q)Za=se()N$6yQ7E$U>l!)T;vSYHdlBl>0wG=d0SAypN=*cuj1|2pA zypT*tN_-_*4^T;EyhsH=8(BEGQCRX^6VwcXcU zTiMzGCOeJ&E_>xzyLe9{XRKZ`->Ep-BAV+I&b9E)1)#$noI`EUu@=ru1M7GT=R!Ag zc86bf^$*(BWya+w_UJO|M3jCuN;(#$o`{lQh<7;CQZ#rrer5k zvhf^@LBFfU)DUvJ&$U8Tr^0 z3U2-BeZcvX#UY<@C5Kjql6>C{!7G#7np;K+0*~#?f$`X`WM@U2dcRbd56AY zHxH3y3Y=O;GZ&WC27QeomR6ZgA*6xa10?6@Jdjtc#3O-(Y_?QEb61l5)u^hHJV!yY zMwldkV%hXWqk=OysGZnFjx=YMX>ovYTvc-L=*h=VzMVNYCoC3a(y}suMi3Qnia{-w zCsOlZJ)AGb1vho3OanD&icr3`?^SQvw)$fE^w5X9QEZ*O=XRJf&Q`n zu{Dvk!J1B|sc?5hG*QW3U#FQ4i;lK(K@^y4k(>>34t2b8M3=+xLKEq1E9+8-y=kml z{N@Y#r6}RjGUa>}b0ms579|{uV&<0!N0!iYi-bMD=579O(vBb4)8Chzer!GRF>n4U zF|;v{$;KR%O2k;`PxvAjm6-+3L zDWWUPQlgTO$Il0;OO^m)h+s68m_@+n(iljI3?)@&fTsskJF+v8sC+~|DkVQJ0Vp5R zaE;pro`VAbivZ4CtR86xNAUa8pSruNhXT!e9 z;mMk!(p{a>(Q@wEI>Y{U@zEfEu19e&C_NX1=fk|Y0QW*W`%p9aLM!`R6EwVOQ1Hfc z_Qgf~*=6#DSC&T{0*)w(nOnloFJTY9r0o2HH1loV_vuq!2X#SHb!q! z2uy602%XA>iG?hRAVzi{->weFGCg>@yGG84B*pKns9i#W+tT)G5i78f)8)$Bl% zMp-JC?9(dWRmzW4m+x$ATDxxW(5kLO!H$J2WPz3^-90?GZD6!F(B0Y8-B#Dp>5FXa z-qsuFn(EfBc8U7kmi-~oLKA1cOT4$4f2Nr`*C{v{;GS(`91c(~ggD3j?7l4;=_TO6uzb%smN-*#Wa#AQjtSm>P zlekI@kcO5n;1HdKOoIl;XQ4#me31k%mM{czidaGy3kWnSf=U9&zUGF`j<&T@muDqJ z(RWpS{m(0r(!;Ocmb;w>Z)u>nH_|`S)YIu|tW=eoxkWmRNd>|&O3u?3l<|Rbr(Q4R z1q{ZkPWu-c`A25edoA@Rx2``q)W6Uiz5uZgPzV*1Lu*D_20Q(oO>SSUv!cXURAMUg z7MD%;>Q^}db=$lTW{a{=#%@w^&KcNHl3OY3SsaB63FRn zDHN(Ica<7)SYVq5FM|uEK~$uWnam@_v2jSH*jf>&@k9g)Lpq6=BVZcJ-HygE!(ACK z;wFk{>rWqevAq1#U(xW`grLwM1F3wdY5nNr==R;7aJ{O`$SE}B8`WR~1>jfb^4KDY zS|A+JsPE_`Un#i{Et;=N3P1GMyk70STxfqsY<)8qy-F*p-PkqM=IyO1?6Rx2Xm~qJ z@+O-~H`8tCD%5qC+xN7H4%BlFb_(`|M8|`ig@|N-v+zVKXTF7Yxrugbt6});0{ij{ z+U4iaB~SvqO8#jlqlDv8#5@QA(Y%98$V1WW{U8v$$lUdF=FT65;~zoW-fh`=W%;c$ zw`V5w?h1mz=BN(V)wj{jrg#=99^{`YPFA^|*zJcm^wD}k#FlDCZxA1-ML7$I202zQ z%r4TUmzdHEHEC*YmYA9+qGa=e@sr&H6@C?Es?eTl4G z2F7mJyUQ%;OnfevlES6n)e?VOTkHBY>Oifr*5~xKmshq^MT%H3j3(ogVS1WXmufdA zYh){#1ZKJM`h$l{OUqY2cv#@C5!g)1%A!@oa$cXHEq+XdM-U4io)kvN9S*(`@W{f zl~8{`Jh=wnTbEs=#u{Y!A|t|WNw>%_S`mQMvJ4u2X~q8OuZXMzjl_0rrG8i-im=J@K$f6`l zIq4Q%vRO6?w-@u5!o)TDh>Qu>6?eN4rC3Q<32Ko42$t90TFF>e-|c^aSdx`_0F zn0~)p`@WoYjFRt|@7GtE?crkGOqf4g&zcRxpy#>ZW6Xj4Gax+GDVPm0FLd({?{98? z>l4A{m#o*L?CVSLC6IY8;SNPfN0*Uv(VYEJ%0e`IIvT(0S^o6DIlF(!n0U+_`@uf) zL^QC#DKF&KRFlm%AQR2f7s{>WA)s^ARYxvT#SPuQr5wX*ZnpB-CnZr!PV~`@|%p2GKe!l$fgP$C2Edr~7XERn0ciemY4G0a-7GF%A z+OMoAU>H?+g;1>q_~ZSB=0~o=r|z<^b*68aq|X_+`yAQ>0q08r{T7>g4v(2ZXPtv^ zcetdx0_x`q#uX9e1QS=ize7`HGB-QSvmL_yerTp0+R?x`?B^Wl zd14NKYaTcW)s~ann)56Lxl}d|k~5X&R&V{Gk%-)DAo!~-6Fbd2CfLJ4?ZUL}jZ^AV z+cn4cc>a5%_m7`OUjE(x*H7g)&r3J;DF%8ZYldYb{qptz$5)9g(#4BO|DmD~YDEX| ztKA)tk(&-0(G<{#$VmiXbz&w%M1{N_z=3C)wES`hn4S9~t+2})C!zyXLjs?Yz@a3A zWCEj0BB1K_khQi}HD*I6PL@Qu*<>zrI{r-go|*wA3lEnpqZY-=0m+UoA0s zrP+1h;t@@8fj}eC>w^mUVV(Rli{Yz=`iFAaXDq_!5akOR@ga-)rHpw~#ydj5>>}j6 z$sv8jBY(&yekOx1$>FOEjDLTtrd*@<7n%=rN%#4o{cYUw2F4*DYc3+14)Ra;iKYX* zlVgVMhkL7UJQiR62ju^(YfZ#OM3KIEDEZ6U6mXs(CWDwzr9KCCo_0I88N3v6tIEe+--3K?sT&4NJu`?IBi9lIoj7NN<+HyQVyp9l5_ zpO?|)^;457v%J*Ru25by$iFhk9yqL5#k}`9)Q3#+7ZltBA@sFg__{;4fr{NNB;OX0 z-iL|zxa3dt{A)_V8ys@|LPX~^=>2xbOqX`1ku%%Qoe01OYgtD+g?n3ghkHfS9lWCx zh7AiFs&0Oxc>OQhr6_c9k#lO1e0&)R!tPuYuiqjEX2LI+wM z1}h^fmHFb5=b#7T_Sv8zsx2#|?;;B%QM4qeCr;7!# z9CD151%^TCS}{f~rdjkPi#=1Nh~a^yIAx#y{5>%Bqwjt%548zY8jeWAE6|>P@AChi z!15o@{uw{Gt){j|r@CoYe{0i!u2bBSaz2xC99NN6B+h6nFyrqsU$V<_!Iqz3x|{|DQDH zFR9w^Invv+8#e}8>j>5|va^dRC5;Fn>>nXl92aeEe|9 zjUP4F{}P^k#yYhGGEdftsN!5ydv1w$c#-hx@<;QgqL@`L@HPLW_x*wP{)s63Gq2!l zGV@Mj>vltJZJJJ-rPh;mPMNEzyuQE4AHdkP8EPTfVZ}R5nHEWQsRCJHrWQJh8bcnP z0%UvuhXTWa`2~eiM-fgh!8CX@vwNJ^E?ci$67Jra?{lw|&|>JAWI7oB3L=5lwX4^7 znrdMsmBGW&VIl)2vHJQlZI)yO$J$wOXl_^6*3o!q6*3{pSH^5@%rAANt0bu! zF4p5>HPmj|I`F~H&F=nShSwA4ELah5p7$&;!R%7+8WJtn__1ZJR1^Dw(|Bi@OLzE$D85Z?YxN&(e80q+o>~- zw|A4V}2soDoMcm>V6wvgh;gi_Z>@qujpj)(SCvW>Ubf7j?!CgTi09tDz0asJqaDMOE z4Vy+~1$?fECX}%F0*Kq!hx3%I&_Skf+5V|*ZR<8BneDM^MMjyO)f49Q_vL#%={hCY zUXfywV5*BpH+8x>>*M#b*)xJcNCA-K3hnl!uoz&K*bf+m% z#7>~%S7P&CgNU3$VN-3Pz1F4iRYRWQIKA>Uy)3)bGJW&>|6TRV@831nR;ip0VWFL@ z)eB0^Z{EN0&AofxqU$Eb(*pVTCh@n0x~DwG7i82!IrNxM{V$htP9;8|5?m2dUS&Rf z`h6wzfr5QP%y}D&*x}O%$LrKP+H|`@@`+ado+iPrcHu-1v^hfB+CkY8f_DsQ#>cCh zPF%BJ{Z4!F2Qcd5oqhpEUGRyh@N`smCdxh%#6mO3 zc|-tLre&x^xps3_p)pa+i-j1e7I~J(m|zyh@oDieGmXc{(JB#T&TO|m#i>rSD}qBq z4{qQ8<^HdmC$}*qqPBR&#a2XtJ_#I9k%JZReE8jca0FmTxprp1yx1XgI@mS~ zL9P;)Tc@Wd{LU+8#Sa?Ew<`YQ0_9^C;S)0Ufq?ahOTEJ)oG{843Y2egNOyU(2O`#e zA?=n?d|n2>O(#zm%T(*i)mxi1djjfht)d+b!l@47?m_yd2w`i4F%}YT-Dn&iFKs$> zqVU?c#`8}_=btmKEHh8Pq#s$L9f}H1MD3TNg4rcXU@@n6DS7P@zV9EXC>Kl1`dxH6#Fl-)}Xv)*)CnYmS=9P zzxBqQyZ1f;a#p!41Yjze4ou^_TM~-&@n%(q&4MU%lj^I&+jh?GIOXbJo8T$RZmh|w z^~6bdKtMGfm7hvvq%xp59xFvFL0Gh?LQ9fOA7@s-rW96;b^W#csyTh{$#;%Glf1Z4 zSY)S}E%{=pM8|EbK3;74)uR1bBmYXLd_X7OBjWFKnUDC?cVN;fwcwZ(y2Pd4YzZp2HYz4TAn2!$brHt<=wt2dZR<_jMjZ`% zcRQ|rtv~lzbp8+8#bwf2(EM4XFGOi`QRSJa<@6HAy@;ueW~_-)+<$Szzi>srK+w-D z;g2xqp;2?|@W!!scCG`;`q*G4!Rh1|RslFxZJ?E3?N$S4QR{iubNOHeZ7#mEHL0vH#wg7w*5N!wNRNve4wzRB`ZkX$NBVN=%5v)~k-q8_ zy*h3vLxEG#IJivELp<&ml)!|S@8D){*N&H1CRf$ znDeI_tWhq6?JXFP^YNeK{aW`wj!2JOW;8$76AoOaXdEo z$^+YeIZS;)vD{XaCSk^dL9m3DVLt0&0>>2%wBBz}k!Kjj1+WhQpAe z5~p(*DGEW7MzfL!{RhJTN6KDl5XU<7X-;dhwIH|1nQbb-7HeOB_#rrg$3OqoFgT(p zwn>~es@cTSOI87LjQ63p>Y=mfiz4e~F6AafyU$@hViB)v#d{5s<6_PmeENGZ>0J)) z9Xj%+1iEUJ+=NMQQ?NUY`UhU~_HiF|Lrv~*J$j&lJw3u6?au$R!BDrV$OgKgAhAhdlap!qbUN6+Lq##@ z7!DO9VWE|f#G;evjhQedo=MCV^RuK-x|~5N)Xa+U6Kh0U2}S4(<={;`d&U8CmXhjg>kroEGOvnLFjCv(b+a*E9< zMpc5CMK;N)He(D-{U0emq1=&NRhVAyNv|wVb($#d3aG3mNvkmQ)_wW=Z{P?ny?+nL zILVxLzN3I-koSb_k+vJ5rqjOKs}|!0KK%nO;|mVuF(L1~Ub#&MAArekiRm}Fgqs}v zdo29>LfWMQ$t@A(CY5lo#QynaU(wbU;-(hjh9*p(585*X?;0RY^pU6f=>w~cV`H|F z4OO+fCQZjb6dioTI`l7yywLGw{)r{Yt|dzEqH%o5(!T`xm$>3TNrWG8)URRwQ#%xbhMId{prP~wP)sl{hvpdhxY7~ z6UKrOb@tun>?{5a>2Mq0N66=wW_a)5w zLd5=p3x66Kd($(y4Plc4Xr;Qq!Lyfei@_YGtJWW+Xyj>ZS_Y4i#Rt?#@(Mm_g@{U2 z02%2Tnb(IdF9Rd66h1B8q01|^;!Qd*JIiGd&|t|Oi@@WMv4HAG!Gc{~G3b1td=-g7o}Adu}31(&Fk>3b(qGAqRHO-|GoFkyC0iI!g))S`*2<|=t zfw)mxTmajxo?6AkKu@V{NX>N?8v>%tV(i>MZCnozAdKT;?e&L z;op?AE{PfMaVVd1s8e13ExUT9yZRUt5yn&>YiF1?GsM_8Lfzg^p6ny7U9Id~Td-zR zMcew0;*AsV;ok@gOZWqe)cHk_|1cY0vP+iOmPMiIAHMK+CgWEj`zI>%TPE+ZO!P>~ z{~Y*#A^h3E;EAsF8wdqbu3qN%cubX*6uYaquC=MT9kh1Sx%4CsB@rSjE95(-`x`bj zN~@d=Rqf?1tJsa*$f|N=rIQg3qeJzHMsXUOl)=E}Qjs_knnXdNX=ntKmO}?(S@<{v zP&Rv|LIxQX96k^6qspk)*pQj5(05i*n1l2k`wVu3l$s)54w%8?ODeQi#uGs|D&+PnVC z-~ame@8vBAPHIbw6vg%ew{`-sPVH~Y_}ARl$yWcATKW#3_LU62q?XO;WJg(qOAzS> zpYkRJeTPT6E`{EN86OFtM-=oyPuZc#zRA?P(=x*Woonk4y?ku> z`R`A^db@C>j_tAu?N*}8g>jc6+^&3Yd3vcCBti*b?KKq*-EM7>!w0N4!E2BZ2UB6`W98y3HJ`lz_Dh z>DA^{CuV;5e)-+^f2avZ)FoC`sdaP2SnED(vwy5o->$2;Y}DKpGC$%_&+3%NV8$$) zc!5K{!6UsvMc(35-V(Ck zd>Ty20{tR@=|aEKH%kCo(*zttCbqi@i2f#NU5%%ujb2eK^{b!UKf1j9-SS_bE&X)o z#rM~j|9m8=3B^9J4)!M}JK~LloPHZcma} zw1R?*S4*->9Lh-J!7JC_d++D<6Z5)q7bph;eno$9Z=m55XW<>S?5>#mA(wHFj=ySD z&8xY4d9+JH##;i~4LbTgHt~j-b5qFuNG^ORU{2H)o*fHR92{a#b<-xg855n99fQ2R z{jAA;%8miT#2}}9_5^eGHg)$^c>gWl^f%-g&~RHMOh;*>&$!m-X!Rnt{5e0bVHFKe=kI^IFv z-ou<(!?OKqnLqgGL8l!IsoiHK%D5rluzwt9R5l@b=kCTO1}Gjc<*z<{$;}6m+bjvNy{@a{|}AfnNsn) zl>3cb_^np{qe}FRod3iqd#ngRJ5+-$uPY^1`fQSks{3^1QSJu;EY1UoEJ=tKas2eJT(WE`MT$5DvjZYQD9$rzyRj7kOk zIBZT#UPfX*8U)`I0xAiMNW!7xDFC%gh-HzoglyY}O+{O_=+~|1cC}&ZYUK^iBU?hh z+}RJ1&aX`T{MTonoO|;2T*uZSUPlMF!KRN{` zbtS2UOt1jaQ=M5;iD~k(YwKW-N8|Ps*VNjo-SRSrr&`R6>UIle01C8Syt_+0+Ye3kFeZT` z>KDw8u%`wn+j~%LqpiBJqqg3NX4^r@fv2op&oR@>$f=j4-Oq(BFJb66zUoh<w7bgCl7tR zmR=boh8?!@K(a6P90pWvibBfpI1>)k!eumYK7`Z#8_R8Nmbyg;{?Ay zS1c)F)69WjRa1vG9#i=(4Uy82`)YOKN+ht)Vb~~P++gC4$v6`_?qLD`C=+{(OS&PZ z+>uak$!Pb~+8cjQICBG>%`-|!D|@vq#{- zC+1S{SUE3W%F2|n(|P1{F1^@oEA~6mWXvo8P9&m>iC6@Mn1e;-qR>zh1;U^d(8&dC z7K|$d9|)HRWpQh_9+|h}^YY~zV@p=Iesr{T`Bg zXKcUD*l?1$@jUO7cdU_LkW2qTt)Ia!{)=P$iN|`!;=Pe_o+(+6q>RT>#zPrT0u=^^q=n!^GhE6$GEKEZn zvq}(oBnr)AZ5&!UvV2ErOS8o77V%CgMbBh{YeME_5$h5Se}#(MEn^?#k`FWRhXkav zBFZ%tcr$XQm7FOy{wfJoJ6J9rt&|7#iZw0jE!~`T9n`h0^l`v8pD)_jfvX(vW$u5) z-1&$yagnp@1$X>sV%ML{n!iM4f6JslsQE9|f|pAE3nh12%6hEeJkg3CtHqD>>c>Xy zW2gQKQ)y&{3^gm~*KQgmw+3dJc(d5}YzDbRC_}1EdVfW%ypCtmfg^Aho0`pIq0}mb zTw)AVb+oKVwG0mr>>OLTM^#=%jaLMEJ680yhNJ!xju1*03dCx)QcV;J3Yb(7@s>v- zf{jo$J zg$Qrd7Fc!ss?xT>Wv%^7G-V!%VuM=xSSorX;l7dZz87-tQ*Z}NilYkFAu9GWA?>`D zdrQZjl+$jCh_@w_DLQ&n#N=*tseNi=Z@Fq?mt=D%bIm-~suu3D9>wM!LfevB_TCrF zt#>HvE)gcCxNCl4#Q)&A{w-1dt&{(x1dGpmso+1+i5~03k5z&vV%D@yJgrwgvZ$X# z9LKS8VW7@nt-Xo zNz`+UKH0i~_AT@KY8pz%nq%c1w|5C4fu9UykslJ!+ztt)Zh}oY~@yGd; zGjir_A!SlTy2-^|Qjo9HU`Nd2M8at=xBKh-j#cwin|nkXI@oJlILq6Fn-*}}7B+Ks zPSbZxQMW!Mu6`uy_<^p6%k#bx1OS)Iq^TGx=n`lCQRlv<{iUAvIVT8VyOU z!pS8ViI}8RaP(${#V5A~RQ`xNRT*fn>*z}A0&3tJ1>x*D1R&{uFQ1;nzyK3j8VwK5 zYhb39K|*~9j>#mHX5$uYLp3a9gIBm2HRb-1Ma>)c)NS4uUA9?PS)Zj4W@xzal>hK> z!@+G`OGay&np1VP)l8d`?@%&*rhK=#NUKT1<31=Tm{VAkUJL~gQ4Eu13u*2iS~7F* z*k4zUPVHT9?rkWjt;96d+FDyG;iRd}y;zmX|$O$iGp^K9%y03K?I^ z7$?M(3rx%{G3Bn1a$A7E!oZ%8aeBfQU5(3DVfQR+lx^%2Z|Y*NZRLRZ`*@$uKQ=Dd ze3S9@gv`p6r)?-jis(MjuxA^18766oT1F747&5ETSrv-Z8NFei*-p}H5E@ms zkTr{ro6RMs3n?(E5TjDDot8i}?5yw;ozg5NX$~EgO~U3=@L5DeIu$!hMEj6OpG`(* zVj;lr1Fd-{08O-h?n{@<432R-Px|fe7-s1t44uk%z%y#8RqR-?FImpFiGP zR%hNlJpcK6X=9Asl0-JtlBz0Pk<#+ApiI@GkUS|3|K@fax9In&r8`9I-9pA^0{U?Q z^{j|`N6Y@&m^u@6*!#`=2lNQc9}`M7fJ zdG_8PX&=3#?fipV@m3^%BXIpDv%FJso=Z8;CH!Y9{v$c(iAgeT65Y25Z+mo;<)+E8 z@{=nUG>LV5B$a`o)4?XE2-JCnoLNY zsZgsea5{4x#w?pE%d3Or8M{rw{s z&jaVztAGFe;N+h1GoO5Nao6YCNckKrZZ;A*3l2>$fn}o*xg2_)ke4sx!XmcOy}j=@ zsNn1GpVsVOOG!nQwWXHEB+R5D>E!-U$l>xR#QWvKpR|&vJo+UHem9S{TfsaA`fmc# zX#wr5fObMgy(l7IRxl>{gbPmd=?dRsn^>?YqG<~n);6)0w}`j&@z*p`n%ZKak1rcm zpW=S>miF<_lpXJ+RX@okuSKSxl(u&U$xAK&nNIjb#d`!k2JwA^=#E8l%cHwhZoad2 z-mXtZTFfR1fh7D71QLA*el$z_R{Kk!iW>+R${z5q{~9E zIxtoTI#`M*EyJX0@SSa#resmTTx3TCsm z4hxn}1_6ceTo#xnpob28ZrZd-wr&+;`BF;fLWn;IjXD^$RjlS(MNhA5(bB~79TkhV zZTMvUzn`A@^Y+)1+m;>wc+19Bo0uBotU_oe9tT&*u>yV;9g7Ki>Ct35a5mMrD<@iS zJ^unM|1UE$9T!fr7Y+D_=GV7&ldbw38OIWeRKyboPfE$#@`Pf7V;Kr`n}yaVe`U>SguPB{h+l2>Lh^k+VjF{ibHCtChp73Zc!d?TQ} zRqwkz-qyalIS~t*xdIl7LBUHoIHMw40osh@Gzq)dtRvc70vk9yww6}6Ys&(nkXctA zjLd5bwY8Fc=3=d;#A1O*!{p{BT4Oyd=*=)_|2GJfp`&L5whRlGEvDrQnE6a{h6Jc1 zIGJ{Sx|W3~R=;>@+_u#;JZfv{EYa)caA^#uAySzvt*Ws5`V7kNRs0#H=zAd_%*9UV zlouJq6C%nfF?b6wPs&)A6r3w64iE)>U0=CxG-dZ_)IOQGvsARQPP(jF(%d(Xzhq~4 z{Ut~JMZN2$Q9JDjO<8SEZOYdU{TsdHrAhqECVQe}JydfaY57xn{-l9-(x zw{IVR_2aW;=T3nB2B{(jF`4Gl>9dJgh@5YVgc7L+Pdw-h4`@X{acMI$P#Fy8&8FK@ z?inHFgqV6xzy!mBlWP7&HUAQibV?7jecjcSMz(<~0l#i)QLb*5HP7qf4IQ2>UYVyci!+5n+Y70 z&T|N-`HZh6qWvoIw;JXJHRmFaete*HVqcp!VN}=z-0@n`ih50FUA?^d8@1_*O*iee z-wHb)hwU#yrWaQEQlZTlBiEqG0dRY(Mjm#v0s(kc6y4q`>1=X0 zSE<{YEVWf-fElINg8M%WhfAl?b9p?rSr@6O3MQ*;F=sSBVphMZt^Rdv`J3vxpH<=; zZ1NWz>bFA9r)uHna^@)o=Q|GZkll24QEJ`Jl(56D7^@bqNGW3@uUzWq9`$p({Fz1m)Tw#u&^>qSo>-({{qOsIQ_qpI2_bSYUz6;#JfSCi)ov4HcL#wVr;fr<4!zIzU$ zcn%K@o`ShLq9P#I4kb+|4|V+VcF9jaF8u17SYuN@oeCzEbBiG9P(&_)1mm(1azVaP znQoE;b(UqM=jdnKX1+LlVAl>!`+R;?BeT4!$YDhqrBt5{S5aEzx8*q1kVKGKE_S(_jK2!QY@D(OAOdq!fdH5TdFLPN$jPi&7A{f&5e%IR-NXxO7hd<(Z>tspO%U) z@R=uAlw&;RDHZRWl6z9Y`Id^^?X=%&sJnNlGx%}JFc_1sZs7GtwI16Bi{^Hj>t4t? z<+n|{w9|g=3$N~lSN*~)d8U&-acZB~wND(X$42o(o8oS%>Bgd(3vcdRoOyfo;upjI zYJ)Hw@FnIMeIce&h!oOFcoe!>eD=hSnI9fsKDEzYUMh9OeDT&qvW2DArJ*oT8lSGT zqMQye1Pr%Un_J3@+|oHp7RG1j9BGe_G_a#;ib2A2$OvXmo`{gnBjqW{1tM%Z1MvYJ zHk$~`1v6p^C0k9yipfNs7++r|+q=&fF$gKB}3lb8xm^=UEMf4y(T*wsplsMSB+;G?%L5 z?!NxE^*gKf9W$+3$4|vetlBKl+wobI3kD;rcl$>-xrbJ(dlm?)%TW$Pj$X=Wt@3T( z?%1(iwsf4(+6+t8AS)x|%UXW_>hOUbYZwkA&ZMax9NM+_#OUh%q3E4>Vy3R)_xASN z(a^_A=}`{#8yewD9_gx#aZyD7TERNbCw*eIUP_j2sq?j$JC-$-jyH$qJ2mSa`jZ~h zJ-gHx@K~dEzuU(Y#kn z{`T%_yft77R7k8=l8nogaA9mRNIwhSealhg=nn$+{kAuoMQ^gE({{ z74)C6S-=*jW)=DsMNu`ry+S>)UO%y@y}zzJ=5DVqJvZ97bT9=6XJG;~4+lk2u~0EB zOG}?6K+i!JWuV}|d|JXr;U#2_oQI&0vk=HUG6l(F6E$+E+YS%8dRL6yJA4V4@}i9k zsO6=S#){O~is-5dX?HI=5YCZHvLvDp0V6tS;nh|uo7>Gji%oqafxba?T{FaIu&iF* zd*)8pm4~JKj&XW>Geb5+sdMY5p_y~1t{vTzO8AU!x2>Um{YRgDdhkNs{L#A3t*h3( zt*n0J@t;@8KW37?r4vswDVIg;t0E@wh@F=6PsrFOHL_zQ+=NDWMJ<}th_C777hJ|$ zF5`Wp@{vRL*rAzrsiqu?#}4@ux8{XM^W3GF3K*w6>bn8W?P}|l;p9!P|I4!O@;A@F z1NZ;=3ll7tpoGuJV^Rx60*YKj08uz>d~aju%-WHW-nhmIf*pJyJU3q36^T`$c#JG0 zJPV7;WYaRloDATRvCFfae5g-Lt}7G9tjP$7PqPckwC$ZyYlR_)gU>+2@~{Xv0ar}L zWbyF1U|4{K0^lBKK@>9JFfN)QV{k-l2#*KRC?ZYshP&2ie6b86MH*5KtQ@Hs9?(_R z!Yqb7otPW-t1HTR(O99`IGafXY_7R7QMOrKV$;D48mLK2DD_$U7g{<;;Wm>nUWAF8!2E z^U$T6GRy8*We*(6X|U(CDyAK(hYr5PChUS28#FS*?P#_Eb zJ?g4BzmSP2Vxcg65>v$@tArSfs-mrX<=|GkH&H^wGMQwglvE_6YOz{=LMsAgXUb=}?=e2`f=57n*C@Q})IJCr zANlo<+`7k3?L)WzVX1AZ)O;spzcG-ws*{}d*min>v{)_8*U30;Cod3&dL4Oo3(9Dg zT1-ZhoGhf~5plU>LIIsbk#f0OlgJr$$D69F=ZOruY*a}O1)r%_WSP_ukA+$lRkYR6 zs{AOw4VQ@Fs)FLApA~ZyDH%mXEE11H0$45p>p>y!hwp_&Ad7r%UTzvl9EKEu`@fih zfN{_SFBaUmE1I_Zd=vo^x~^vlBnU`Hi_@sRqro{U;ogjd*m`q z2dz_Oj(c&(t&Yg8s^BHL=v12p-*#`!?9~)hfGaQ(QGl?wF;w?TSf{>aI_F*Q0;n)=vd2Q-0%QspC$K z`+861ZME{cL3c48TW2Y4z=LsWWn_4CO~?8j(gh3fu?Wg;bC;L**VTEvCOC~yh{gi9 zMX||9sfcqzWh#HNs=6~AiQ)Jxv`_*j^?4Sxs;)LTHs3ll51;U4IMn&2cHlo1gbgKD zMYcve^O-3}>WL&*; z4iydPJs^o-4(LJ*MiLNR6 zSCx$0Ow?9joUy5Hy0n)a>Z?ZiZKw8*Lw?JtoD5l}0@jCq!@Yq1PKEtWbLdvN>$*g6 zQ?I#N<~^TCbOSvGE95h`^jPPQX21qRU?mA z3SF_%%6aj43Y^ryE~Qw`XNGJ;V?$#r7BlPIbJT(~jUd-0M+CI_79p?z=NRRAc15;< zpReMAkisk|G#`%0f+<#mI(ua!VnYY8fiMOO9t!9j^7{sO8u&@Vc3OQ!qQ9$g;X?7eMp(#;1n=wR%gXi~;w@+k_IDgQ zcxB`E!?k^TM_11bFZp-C`_^HamU7O?1z%{T!y3_QHRrO13q(8D73|YG_H7aIw2ZdL zp*iVPpLJ+&I&^m&%BxoCO|Nz`V7cozOoGLaStcu-cVq6`ddXFj;-*o3Jy~`(kW9$S zyd;;F8!~I+5qW7T!|Q;$ZK~Fuz`&>~>}7CiXbKKOLD4NrzzT0@Y~VU0?pRZ*s##>z zf)))>>Q=`~dj{I$-PQPjE?+AHm~^N|%LR{sQh|I*7 zWME2iNbhBM=_GUp8k$K!kY&ug`@!@lvpJ59YJWqfH^e84t&B7k6Ym}1G+xK>u(n|rEqF_5s5epUI@}d3Sm(2 zI+&H8{m0uGPh|syjzF_91ObjMp=pg$yVXn=i4Zg#f`-ola|tOenQ)EwCOuv)_+#dO zXL1UH`3= z^O>Cajfi?eD?F#>UzO6YshBsFj5})1O&Q~wf^l8PxufP?HVD534}x2Bxy*Jg;ksF2 zzugeH(^CE*6`Holr@+izB77=f-F6%9+Ecp&3);Teu>I1WZMQcqIl6j4H?IOx5kLn` ztz*k}?fRx~(F&tC@-s|WL8nZti0^M zU(Kvta{wz~l6Y8}ki-^KWh$ORA;EK~2s#eI0IW+Q!y+1AH1EeFA75WND%6OwNrWs4 zHjRqUrVt7!1O$@;XHyD!%sf6nlMP6f*nHs4rvq^pHB-dQRPxaYuYPoqVQ>KCid3|9 zrJ9mul@X*?Mlf0A!I(T0)z{ZsmJDi_4r#jQc|b(-#tj|YwpH}+u{&N8;JeuPy?n|c z5$(8=|CNOMot$%7&AF!G+}3dJs=2qMj616L&%$jT_l}x*Q^h=Q7GLqGZkO36%PjZ2 z+WUIpq=fZY&VM0fJyQsuYGjW*#_6ht$?8q}wr<(CdG*?!p=6*XENH2ecQ#YvUW82~ zjXK$WGg>F6s-+ky3npX`6nvJ^Le)7rmT)NERMyxgN`?yr_gvKw|SuN?vzkaP!B>9lKVo-@j~h7jQgL zj2gPpO7y$6bxpRm9_RRQ!*^d)pE*pC%joxX%zH8_kgPm3@*WxaPt}}}+L0Zh9Wg-!P_76DcnPfsbs6V+m;;Qhxp21Ks zB$bIpVm_PCBr$=%3|Y*8!+Au6nq4+Ob@tfy8z**sx~@ACH>pfwlS5IUWuP4jp3@GM z@-sL@giS|{L<@A9G$t+w(At@lY$g$iOAEBZVy~H!Dih3amGm|y7LFvlMv-bIMWa)P zE9kXJ)!;(=_|lF8dk^1xvgP5!)MxucTUPGBe)P{@emp*PH9j<@5S^!^KIIZX=jRIn z>9~;ag@AZbM4gl{Zp#?A)%<%}-js;^KuN!^U_Q|Do*H;hb)07!_7gq-rB3i#!+mNL zKG#X!sHH#1g)f!DmuBs>&pn;0eH-ptCn$}=G|GnN(647doq09+$D{MZYe!hX`4TN< zS5=cEeymwfQ;85Di;z#Js^uu56vmKqlum6hCa6yp8O0D1#ue1p&94-+hWRyR@>G~w z8A62JnP$~o1tVQTpQ~Y~ODLIiY$h3>iNa(R15qxbP%J8ti87ESvj7&fu9g>%s{?+n zL_}bbQA|`Z6`oHmDP$lJJPb^$Gf{Tf4}a?8kP{Vnw6}{B+UdU-rmFyqn%pVn; z*9O@ujr6Tn@m4N=rIkMM*{8z67flVnRd((oTkKM=?(UJbGe6y)nfd9@@6U~{n#Ze6 za69`%y@RAwHP+*1=`{o)y@*YM^EfaX7tfF}6%Mn%QdgcVHYy-EJP>)NOG#;F5!Rz( z1dNoRv&d`8wW;%sD!^?>m$S1t*eojM{XP(eC_tc!NgxrAlf@*0xQY)7^A~*d1+TT2 zSzk|gI}7P(0L3ZKmPE~-!K1pZ*Bi$=AWOyyzGZJ561Sa zPpn!c>+L{Az1Wb80MaVmKD0MPjzq{|4-i8Z>r_R0CDd)=B$C?dR!&(NG2jB$9;V5F z&?rfu?XYHv>+>%xn>I@N8$8SUTJ}tQeD31D>$jpChT`M%eDkZ_b?x!g4?_BvY}7FU z?z9kpf`>gyM|?s?o(E204LAj`?kmA1pg)#UpX!89fwoP`ekNr+moZ`m2b7`A7s+k7Sl_Q>s83}rnUR6*k6az=vw<5XC7Xf`Rg56|Cyh!9^XG$HBc>` z-^uUqlTcb@&e?D!?a z=vbhwoum}ykV^{a@H{30dm{~NY?^QOA9n)MF(}f@(9M~%5~})tsUy&<;>RB9FMNpB8M7PMH(@_!tXkM zWZu&^LylWr)%^qJUa=Z6#S@+k?vrnF#e*DAbzkYoHs?fs=AA?*_Mop=t zu1Z~aeYBU-}GLm@~EG6@{DRP1~O4bJ1XXj|F%0gE#*MDwz7gPP^Ap z%qHg3VW9ZrF%bFS60ng?E4u#AqW|&lKi~fL`oAy4`@i`8-~aR}ZWHS@7*wS66I3gslkidfLOAcL+uo=2W<|{T7Jd&MggF>YJiR z_cuJ8syKg|JJ`wY?=Y@hUbT1U(1B0Z9oVt?@F(^~1FU4Cs&msvTW5UcuVvI-T-;{_ z_?HaaehC{Kf$xaOHzl+?a_&<(^QD~iJ(u!A#d$4bz7{jyNO^CSvR{;7KX=v(cG}Sjq%99|e94(MyWm2lnE_79R5*^9vc}$HMN+jsyN|)D;G|QnHDG`wL zH2fS1DPKY<0Ihx&aTXE!AqJWTQd3dTY$ObhhUXv(XBFqCgM0+wBgR9)N;G1@&CFbUa@#P} zCTOW;wWjDj&HSET`%qW+y7g_Fca)Eht~&DR+XsK&I`>jkSFLVJj&Az6ann|6PZPE- zNvbaA#v<;b}(w&L7o|%4f@Z^{5h8lTw;`8J8 zCx#BK?%uSlZEux(d&s=qqdTnPe9ZzCiGEGSeZZ$Y=Q4lf(ZJ3BTFQANV!o0JepJYQ zlPi8!Y5!2`ezy64aD-l#C4Z_+y^SZnkC*?|JMt!!niq^mJGvW3`sZ~Hc3;1AeCEy6 zPtVVvI<~cXL4Byb{J?O>mc?C)`ck6TjZv#1DoL@5i&m*w=2BZ(OHHa%X|qCkT(J6< zU=WDn3Y83)K>)Ex3k||t1rsKq6;sf&kVPNB3O;}qd{~m71usCLOLEZ#vw=H|2-t?O zS=d6b{$L2S{>a7H!sSD2c7*1S!qwtD3NoLD$fLn?7|22vlI;{#Eo@0F9&rry)l77M z^XSy{>qi|2wxHtnt_|zAe|l2g*9T1oK@xXUTe57ZWnj_5^;_3%{_Mz-kM~QPTI8uj z-;z~bt0!c=tAX*Rt3DrX`fhdijke??xAF0N zogwF?nEzU-_(`SuMQ!{Od`zBSow1+26+cI-e~nkYt%(29xo{@9V1p{^F@-&9zb#c2 z8y@ORE^S@Ad+f;e#rrlciZ35@tr+ZE)>|{bfnDmPngD9mSY%e_>iJNqgs%5%A`MkF z-L7yc&f%bx#wbn;Ldt`Rn1BF}wknEsk}MvDOvh6xSm1*E7c~C^Sl%31UOplp0_>1@ zK=X!uNQBHH04PBbaCrf6(zQE3EZ=buFgH}4?R38j1@bkh&|E4s8(ac9s)&su7}>&_ z_YQYLs!X+{wdKf8`NB?2h3U|#Grvs!zHIO3$f|I7uzl6|$Vg9PYfnqZyf$?rq;E;q z3@*_%G)iJ2OJf~57R)d!Gfb)?p8-|oBSuT}HHx`1E<9+ZHC8%?JNlL_PtBiS*;Y0( z-e_(K(ESFQ+c3Cd)t5UCq{0b8F_VE1a4|A9OP6qlSM+sXK79Db(Q~(sU4QWPy>p*T z^|wAU>7S~!FO~Wq_0HebmOo9dzg!i6c&dN#R{R=_{TPkCOSS&J?!=GAp%F@Xgk{r6 z9A*qdIbI)DD$5u1@|2QX8LNoPq^O({ zUv(_m6|JnMMtlW+SGJOi60j(8kWVKDiOE@PN+yj$U=l?f9EJpXVg(r}U`GWJzXgRT zNER8D^FBWBz5X@}RrF|P=G*Vz-hTGW_cwoTShAc}9o5Cl#0FIf3!C@81i^CYup%ZB zAtF*-a#|{i0XUkl9a`xDd8|mEe$mG}9zXs2tIOXHA3k;V{GCaOt1L2$5GzqqH8P@_ixh}(B0f=67mMBc=BxYPU70>P z`RMG_qtlOet(i)s9{XZH8vH*Se7`vpzuGF_nj`Nl!5@PaKZHwv8QwOt`ry~W?$Hvb zB?laAwF&_^464OcgYfvlO*20{eKGl2e0|U0M-vy0>{zpISRHUsltQ^tij(n+xpXL< zj8hl|&O|WLUJXd~UV9ekEd$`QT7eNuKtyj2j#$9s7aR0wtyn9fOSl9)m|@WgnW%yc zNNyGal0ih|lHm|MBnMucg(wE~XWz}+>rb9LeeF@(#Dt}#83ceE13tQhlTU}|&>)}? z6*0kC07cU>`EfU=sTx}5Pj~AeVJEsQpzf?2IrQ0%QxEoBdG+|$e}g!HeV?C2St*6bxI)#C6&Wje2f4AWv2y%e>8F^Umzsx$D60rQw@j ze)Ig|-PhNqA6=M!adGO?9n-ZPFI>sDp6Z{x4R3Uj9}J$Kyk&2~iMLhte{DSdLrv$1 zvaBqP{@zlZMZ>`)TnwK@S8>i9Uk~;_cdmTWK2r77=Al2&ADMY@dh?prq1N)xhC3=# zKD3aHV$m=PEyq#j21)G=9r|#fNGnFD<;45xTetUYLu1|Ngyk_0fV|AGdw7rDpw#=;&fiLmSWMlYS=m*PpRc86%478 zCH#EH_8)FPesSgD^!bO6&P*NMdw=n&hphw8s(N1eYM(pG-Uj18`paKCf^WOm%mf#$ z&g4)&q+tIG3H=wO2$)dFb=CMps6fn6dSyMs<@G(0q1MW6OI!b%{BGvAsY^!|A6nIO zZR7ZY`ALkNi{i3CQUlW*F-Pj^>pQ)*NtDMtSIC=##-+nB1q^1fOvrWE)V=^K8e*i9 zl(K-iDrv8*#Y%aZ$U?C48HAEq;5YC>JOPcRqrZ6eWcjCud$w&{c3|(q@4l?uyPw(6 zUZU4>%^IOr24SFz!E}s;Ea4zA0=z)aaMnd5T?4eTD5|wmv2iVHd>EPtBHS8U(4?p= z)25Q5$|_TBV@Fr_g07aj_V$*pm9d6}ESnPtWGY=Ps`fTo!plm!y(6_(u73W16d-4> zf5j^gLiJ*p*ADf%5DL(TWfc-=`4leLczm+sK&7`VptJkbPOsW&mb)~3otVax(u7)$ zRLK_SS{W%$NA$E?=J8E=A#vT zV@>n=DvcFJl!A-lF(5)QLFZBWs;X-{B8^ROk7X8)kV#`eyl!mBit*~%WkF#)rtI&r zY}_Cm>J|fZLn4~1;ARp_=HQBFV@p1S6@ZzDh{q#|X!DMKqg=eeGv2>&|K8s7C#sHr z%^O&dp%>9Y+N%1n#v&^MqX8BY%0eUgSelfi@hD^SI*nCzgqomw(}ZgMI(RagYf^%L zA7&7ujY_CqMfcd6+Ea&?47Mfh9;JYO14^BMVlVq{gtxSLKU##%mMaaz2?S zp$Wtko?0MLvBj(U27a7;^z_;ju>O;$?oC~ozH)5x$ez1{%O1OGUb^D1g7K%W$cyIr zGs6e3&cajw-=h5g2`T)K;==#R&-)h{O-eYqRc^dViQux~1R8@U6|0oCkagpVfzJZaa1@j!S9-9 z8k=AL*`e~UzL2jP;?7IKD!|4=ks%>u(vfp0*jWTX1cjs3oLo7{y>+X6=`zE_xOdB1 z+QVg?GyMj^RqyoeywF+K4zf3k^D>Sp(}h!)K! zv{V=QtZ7m~mVj2M;DD)Ik%X^{7$z1EY+th6nJi_wJ#t_n?;n(dE_7d~wyx6IR5^KZ z@B1VFuT!6Fn~%1L(`d9bC?*Sn$)zwzS`&bf)6MGsj*cc*s470ss<0(IvF3_~+E97a z;Zw3D5-MB9;ORwjoj}o3Tlf6>{b$#n+&%r^?%9V^mmW=?ox1qtYn=I zj|1i3_bmMpV|M(HyzKudF8Cis1^_Duyu}C~vMwwlG0Duu)Zg^Ukt=&s3b+ zFI~{CTDhd`(``-XzBYWkg4LDaG{#UB_M(uk$RRF}Gl27Y4i}duBV_0qb9g9f%qd?s z68`!t-;txD(fOoQ1R1rWtAdHm{ZD>+AGLGh_!lHO56MDcxhOmz^q)v-o6HvT^L#e6 zTSu*na=SYL3p7J1&EnIG6@0u(jSz!v7+W22dg^O=VV9yj5gk|?Sh-s=v=kRBEjCCg z4#mW()~6SCeE!LNl|`6^M(3l5MOa1=n*-I!*#SR05GrwLjj3>ZMZC-x4(Xf~j&OTr zLt`{v>IzF)LMeqKrEzpZg;}g=2uCJQU%P+);jL3s4=z4_aB=$1xrf(J-aB{f=8{#n zV)Ld;YoEuGe`+Ip{;$H^|0v4+&%)e)m4MW5$Xqb9gBL0tzQt?yRj=HDi`eig8N;9e z3Iv`>LGkKv2_LN7l5G~GYxp1V$z+wdvod6}$gucKDQz~8R9kcR_Ts;1 zg5T|PEE`yGV14KD&FbBwh*np)Be8Hffja>^_`1ji$D z+Y`ZWjz`a)SFT#ZN|h7heoRA5wRPmsyJuki|NiBVikfOD6OG_vP&^DlNa83NI)_5% zbSu%Zqe%+Ie$acb)2$u4PR z9$sUosiaDW93y9Dau^Vai~w9?QW;XCgz2Sdvl4DsWw;fba(7>!| zNWQqsvTcoae4(yaj|GVh9V(IHC&5J zD|ed-4jsF?Oj%n^a@%v1!W{odJU_ zXbVd~mXo_~B7bVklSy?u82+IN#Dk6!EQ zyA`haBT#edKT+`igckp=;(`xK3V@I!6I(JD0hnyLX(feFGKD0N^1MDpb6t5?lQHhb z+vQM;tUyhJ3&HS3$hHNTCbzFN*wvPFc%4Fpm8dYY%)wZBBOnCPohGzJ$&GpiEmbE^ z?3(%M&ecm>8I@{G%(!L!{8M|DRS#6;dnD;f8j>#}h~(*H#J}i>*#HxSfaH;2;J@I8 zt>*3~@4`O!ib3z@HSXP;v>V3_d)L*RJl3**tG>#|QS&ieBuG$2aZzAQ%$1SMHmS#F zr#Q6GG8?l#feL$a)Vy3!B$YgvR#l(}E-Ds4CT3tk_m2n{X&^e?9HX$rETfqX+&~~O zu(Wag(a_>8u0<>AKR(*C|J!iiFf-zzHB_-{EAbvnrb`iTsGk^JwsrNYo|=Y)-JJk- z{Ai_KqE_=2VhUSI=V&=XIg5Y%qXVyQJ)6An=N%jvh`24sqKG4^ZpAC`QMPj|6L67?!nm;k_IcBTU?R{!xiJmP!1h9++(Tacz0KI zZHR4=2~29HUc!`eSxTAA;T1bve2X1tGos}RszimB=m{F9Etn{ar96@7n*Pp-@orUf zWol)|!DHj)iz-nGo3_SpZ1ySZ%mlAA+sH|05yVQD!W7QrkY*_)E4`hqzM($$IsrKx9xzbzh5@EKsvlY+|dd|)XT;e9on_$_|Y%6uH9H0 zsw%a3BPP3EEOqOx8oru|q3{Xxgf~3--BnQbZ=HQSdH(VLSJHV#H+7}&{{QXX|4hKP zWXqP-d+)t>SzVUpCb`H}a<8~|V{pU94P1bL!Ss&l)dhknF@z8xlq8UZBuu6xLuMv3 z3A&rPT4(u#zIZL2bN1fv`##U_d2#RCXLrAT^wEpeYmZkiKfF5sD7X4g#qF=*0@YeN z9V!$=(C863R2&tLViQmd8j8llu_YL>5I_#))&y^QdTm91QGT*H$so3<*e0pKsLayaqD`6JAtR-$PGf1Rqb@>tyYa#!$<3dFsmGvAd3M} z=`t3KPe(~POoM`^0fEsRB$I)a$Z$42CCjZT%uCA;IkK|#g}zW@b!B6XsWK3l+S?p`6VBi@%JkzQ+ z1zi4OUubgU=+hfdpMk~a);Etd(7Nf7*54nUetz%hz)a83%vk5;bNx# zbXDgRp5Ak2>DbEWH@|%J(br$DzJ7Y=<&)cAe!2SP)SN$}kWdm0%aRaz0=`adN>0S8Bp_oKLLd;hr22ACTXTjPoE0@3paX=- z`9zaO;dSbLpzY%pdu>#M3NI4iq)L+BuJY!FiW*AGLbVM(e|c)4B3L##GV%2F-tXS7 zzk1TyT1fJH5IPx6t0k(GK=2Rp8shO3DnlR;>R2KbNhnDxE{|f9W4J_~OP5_2F8Wl#$!%2aBT4^o_l+?6&ii>0cued0il9MVc@FZ3El)YPLpRIiPWaZVP>#x8o@Z#P#uRaBZ*cU4omUr$xc=_o0=`F7Vo1U;k z6|CX{T$U@^WBmh<9z~#`nQ{VCh@=xlMtO5dPC}9?l0k-$36UgRw3reN>~dmOERb;M z6lA**?zp1M_ zX*rp=Kr&z^N3j@jG#-*6M{q=N3K@n*pt+P>pCd8N1}JJsF?e&SFufqoqC#oJWU~pR zhP1b}wYAm)=@ias0W=bh!_2YhX*wAhz@NoZrqZCbCp$bDX1mAg@ak;I+NAV^oQ$f9 z+&%p*9oAK12O zcD{f5SZVvXt)N1lo2jfWW|sx9={8I%Ks~5Ipoz|x43fpD8a75o#wjTTC6%hB&@?o< zRzTJ$2?j00Zj+@1h3;IqQijn9s@pmz`#ucR)Vqo*aw?m%f~B5@k7*3+FI`ma;4-BW0k! zX>;%To_e9r5~r3RRT4&m*^-hWv6&ePAxEa7J)%}*Oep{7HEB0S!K-`^<5b~ zok{JDY+&n_$xspz9B^*U;#iF!R>6gvzRM5;BTp>4k8UG2SFgYin;D*y?X>&TrpP+|lbSEm8Q>8Njyhw4(hkYKbqU zXJc?^i?Oh%!8o1X8ho^0E9VcWL3JsUS29Nm6(diKW1_T^18OCviMCw4DwnOhp! zcFvztWR~lMEb*501E1e`^~trDk8XVZ{PwG-H=n&&d9*aYeE0ml9sB-7(IrDLXgHf4 zFIFQtG7Lo=jROhMSRxasvN2IO6oi06@CZT$hwqT6fe9>_H`ZBKS&`<;cI+M>tlYU( z-CHdUB@!(fkb;U7a9mkMMa8XpZwg%|5NZudTSj75b$Mx%!Jh;qh)ADg!eoiD8+et$n#of= z)0aGTEsPW}?eeS2N~FyoMXt7LX0p3y@96Z!E!(e+ZeJeRv9x9P^3>j?iMgfe{Wm(h zw*%~shHp4LzH8U`i}_t&K3)0t{=!#xFMj^n!e^`J9-KS(yR&rbn)vu=0v^fcGE^3t z$Ph=Ot&NAr60ig|DUOZ?bMQz48cIWA_;{*A$<9v8t1tZZ@b({$PaT=wu(We)i)L_V?$~s^tA9JgtRwL`BE3oB&bDS%l$JI-($ZjZBF@2A*X8Tm>QP>8 z1Ren;;r^STN{*qBO9*aTrlvr#M*RtZb8IW#JMeI-kd$nn+f05i$x=K?N6HLt)v( zd{-jRX`CCc4?np1*OiYpZ5d@32RZqfnqWZRP-X62pU~N5PCl{7mI|j4Ln;ScJIcu6a z&m2CqeBaU41v9Fm^P!=ZHVG34s_DpXa+*Z&# z0xy9+ZrMW}b zckN#|d|>&LE6=M6ZZk1UX7&B^2fn&`^7HBOPeY+!yp_-J+9W7}L|_OcDmzDEgEM$h zBvJ$c7ePQrk& z#4%DXoJk>xffn6C*Xd&zBp8Q?lJTi#k1RQu8Eo_w6=PBq5C@mzH#b)0CTAqXFmTZ< za*To(qh!Uhi6Fol0{$Zk5sN1P=S4Ig0~oFG0y;n4(=okmb@lVR*FKxvd-dGqpN6(B zRQ5M59-XaOUr$bTP&_7aPJym;ovbhysH&t@!NmG?{H!#fFOq|bSi?oj$QUIB%;>2a zCeO%~m}G`bbMO)0H(m9Ez1secNGn9&fd^1Z>eS1RjZqtLStK-_8#n9 zys_bnZwl|QT_2{>3r zu-Ooy7;qRO7+47K36ekkWG1d2Z)uoBiy8#B;~hV;k7u zVW^}CzREmlj#p8bmsr$h&#F*oCRL61H=jHin%xr`++-;!6((6l78zbiN69D{Ii09x z(2aDtRU~t$ctN+KEhy~>(Mwb4U|84KFtB-IXzzic`3w064tZvFICt(yp4*jw`CRXt z@2>%lYdGwFcmp+DOY16UG?x>)YFL9Eyph(x@aE}>xeq@)dF06AfrE?3k1ieBe`Rv| z=z-m5FCBWy#$UikoMa-e%9xJ>X)lvpFFoF$Y=JtUnTNCaD2|lC*P-}Q*#9@90sOmw z2jP;V`ILA8GmcG-z@yio5hxlFDDB~LYNUb*0z_gh0)k)90m%K4R_8?L@SfSe^Jmr5 zW3iboz-@@7k>c1if=ozMNTNApj99^SWV!-1B^7nbqNLb#71AXJ{hSyA0*XYyP{?R3 z3I?WZcpQ>UjUx~sNO1T;#u9+}6a_FdvaZW-SNa_-oAz!asT6cmht zPFB-6Mz+eW&{z0mtwqc#KPJmg3+B1nd#k1=FWvd%pRdE0p8Ux^x81O3yy4u5gP*P( z`tj9#I1Fw<_iw)luFZWg82X?mKdPq))mKFwtTt{Nu5I6Y_TcFo7Z&bZx_R@`%F$zs z6I)N6KX7e!%MD`8E>h%adi;Wd3t*LRa`S$5Rqrw9B(u$iIF5kCS715(C}8uUkRc>e z6!3mhiJ1dK@e*d!rTgcRgQ3Di+{L#_ROh) zYv*}ARZ%J)6gcMSKp7<NK2pShS(wa!%>%o!c%y8H3nx4*r48(zBgRoB=Ypv`cN zG9c4>ZzWccNlH53Dliu(8S8`8f)s3?mtIvYYHv#&-+cbwgCGC$?)sa*;)$;HvY_9Wz@zS+pW%1*cxZ2~>!HI#hA75NP zb97;F3k_r|R3RW&AY7MahZUhkr<#LF6mpY{=R8;TH_OYxomD^}^nb>+c znn#0?u~0NJ5*`mhqhj&I7#snDLPjFv*S6WSUNoR?X#Wl!T^T>f;_KL3ZoN+JkM-{Vj+Ct-K9mY|?Qm?tocw zL?}3KH(myjZc^F>3gjds<`frm-5`GD^WT@H7Nf*6lvGPMThunY#Hd3H1aKNX3Im$_ z#279YK**s?TnvvGC#J^=DRDd!km3N(>{`I;*K(U1`!@INP}fwT3$l=TY21c7N`1*6 z68L}6^ATP3}>8W7B}D5CT%zR2Fe!UT$HXHzR--av2JM2(>xA zE{WZUmkEF&5$y61BsvBM_Fp`Z-9k_ZC<1(gilz`zLhAGHpFF(&K$+@?X#|de?gNj% zx%ly4Mo)Y*dGXDr^LGy3zx?N)KmW(eyR!$!gr!Nwpw}Dn`zs4mJDW>prbf?S>$GKFeMG{cne<2=DaIx#YVNA(=oePy$=g~in}-BljmUg2 zApFE7TH|b57?>gnIXFHS1NhHuVib=W!K199p`#civ`oiwW~Jp-=LSL)u@E?1XmY8+ zZgqMS)roeJRtOGDKy4n0K}TZIP#gw=dcWyMVUS<|8p|eB9^3Wc`H#cnyJIwBWm|pi zjpaknf4TcIe0}vFRsH*|RsOpV4~Ktxb@9XuEjK|FO!U@fZ11n1nx5`Gd1>cIj|Q)= zB+c*6-9K60)-1Nj334JyL8U4gVm-%_s03npMP049y_Zq54x8nbmjn|4)^E5yV|LQF zZNxma!EtQ1^3mPB-#&Zs_SN^{e}LE|Aae&FpvwLPBq)W$m%`!Ma5&?|eMQBbQM;^W z-<4DD2yxe?BoI^e$f)^3<3Gx)uAmmC;ha{o*+vo@kbF5-rQ{fOM3ERuq(q=`Ytd+s zeFKw-VKUzPsRt7q16~9+i7XYdWB!chup17MMff-87uJYnEP@1$C+s8JV!m zG>C+al*_RiE%@+1mLoQb|2V}kYYjh;loo8pPie(ZpqA(*4G8+Pi?$& z=l0h>KYjjN-Pj?q&wTOnSop7>ojA3bR_vB{S7nU%k4*JV&I~snIJx26^{Qh>4HNCA z@us4_dT)l4sbZ7m@5K@v9Y&4%GeGm@6eFwY-?||9k`|!KF;c(+We~|4O&@}ezGv~}n z*X?O{64M{3yt_$;^!OAzr8t9^1$r7zn%f?)Qi7cq$rWPw0xXjQBT^!@}D| zh|#HH#DGlBLdZpUht`mmtZ*vuNfO@rM()Pe_!JA$D3eqKIQ4m8BaYyaqx_zTP#(nV z66CiDst2%%32+WRl1=)fobiE-_5q6+$D$KuN|HIr>?0~-QU zgEQyOoW69^R8|Ss3e#$n=Js_K^yIK>e9FOk*S6uw`7LM9Zf_dfls7SzH$CI-?-Dl@ z$(jnX%X6JBBU8y@D485B8weC!ZhfZTZS;A`i8ex73IJ4+v%Lh6my{P!mxZ#*i=8F; zv}{kqRKxS{Z$J9^i@8^iX8-hh(?8$L|NhHg0dF)MUJC#I;k$q4zr3ZH9+0%uBXaDx zl1$6wmi#?OG);|Ag*aNTCU}#9U5;HCkbsS*xFA+7i(|47bS8?*j3?6}L?W2B0Jky- z4a948aL^K$OVN5Y+$N>F)dU?YRz-?W)55dND31nZmcY#bl&XLTSYTGN)@k^lAS)u( zPRc9i)OC{snIMPavkBIf z1$OSOIDW}DwVe`3#~Mw{q)b{y77)OTH?~OIYEd2&2ug^d&|p*`YvIJw=+O)^QN$-J z-QTU2jv$4SXd)c_knXRj#ALX1M6xTB8Sx=vV>qbw~D zRuzE^bfhntzp>eNW`D=aJC}d|>q0m@9R7Fxf4=X1_jc27zn1;@QgM7+beay4uJewJ zHqTus-hRM6FrX?c6eK1x(^7z#IXc-K?=#@j&2dH(1Mvb zF~dpGijnWzt&~^}GloHhGikt)0VQFuQUTZ(hPKQ+fAsR|XU`X&e0k*TmHevOI60SO zl4iH{96tD7IH`I=Lw@F_x|ZH`&W6m=T{RDG9eaD_eCPB~%Gf~W=3Z@8 zAl`3Ars&i1Gt2V}t!|USEYlek5*3f5p;e4kJ^kwQuU*|%4l-P=jq*stlqrvz!MBbi|9BFkKxr@s(dY->k696`1RQvj*i{EePv?LwzQu5U|(Z-Z80|| zF*enxC`@Z?Xss$L*ITp#6^AaTQ4};)p0VS|)Xu9H8n9@7y}1q5%C~N#00kBP-RJtA=eY_7C`M1sFVW*TL6uS z;xWM}YORO|;6DtpjAC_R%@(*u%2Z19_3abe@LD-s%mhGDV^wzVuHzkBj|;1dC2gUMsiE4jEuo4$U7lA7*y(;} ze5wVXZOyDLYp83`xb!$F5f8?cN;=iR6s2m-Wf_T$)j6YsGYeOrKYsqvCr^e>U2HgZ zq-*7F-Hn@B$M@w9KMxv!I+-S_&0WfnEqA zVy-dCBTb^UVp;?h9Zki@fJF(I=_qK7NPt&^Dl9uEzf$jYM=^10F!8`!4Ios2MulX6 hH-)^0h>m4ZQ3ee;BZE;KLT9I-okqOb@IU|a{{Y&>*{}cr 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 150/287] 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 151/287] 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 152/287] 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 153/287] 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 154/287] 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 155/287] 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 156/287] 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 157/287] 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 158/287] 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 159/287] 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 160/287] 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 From 51ed23af0f6aba27d11cb2edb191b44a6e55aa9c Mon Sep 17 00:00:00 2001 From: Bodmer Date: Fri, 6 Jul 2018 18:48:24 +0100 Subject: [PATCH 161/287] Support ST7789 rotation offset and boards with no chip select Other minor changes to tidy code up Put back in ability to call init() multiple times in sketch --- ...PI_ILI9486_Defines.h => ILI9486_Defines.h} | 2 +- .../{RPI_ILI9486_Init.h => ILI9486_Init.h} | 0 ..._ILI9486_Rotation.h => ILI9486_Rotation.h} | 0 TFT_Drivers/ILI9488_Init.h | 2 +- TFT_Drivers/ST7789_Defines.h | 1 + TFT_Drivers/ST7789_Init.h | 5 +- TFT_Drivers/ST7789_Rotation.h | 32 ++++++---- TFT_eSPI.cpp | 59 ++++++++++--------- TFT_eSPI.h | 25 ++++++-- User_Setup.h | 1 + User_Setup_Select.h | 5 +- User_Setups/Setup18_ST7789.h | 2 +- 12 files changed, 82 insertions(+), 52 deletions(-) rename TFT_Drivers/{RPI_ILI9486_Defines.h => ILI9486_Defines.h} (97%) rename TFT_Drivers/{RPI_ILI9486_Init.h => ILI9486_Init.h} (100%) rename TFT_Drivers/{RPI_ILI9486_Rotation.h => ILI9486_Rotation.h} (100%) diff --git a/TFT_Drivers/RPI_ILI9486_Defines.h b/TFT_Drivers/ILI9486_Defines.h similarity index 97% rename from TFT_Drivers/RPI_ILI9486_Defines.h rename to TFT_Drivers/ILI9486_Defines.h index 5ba1007..31996e4 100644 --- a/TFT_Drivers/RPI_ILI9486_Defines.h +++ b/TFT_Drivers/ILI9486_Defines.h @@ -4,7 +4,7 @@ #define TFT_HEIGHT 480 // For Raspberry Pi ILI9486 only with a modified board to add a write strobe: -#ifdef TFT_WR +#if defined (TFT_WR) && defined (RPI_ILI9486_DRIVER) #define RPI_WRITE_STROBE #endif diff --git a/TFT_Drivers/RPI_ILI9486_Init.h b/TFT_Drivers/ILI9486_Init.h similarity index 100% rename from TFT_Drivers/RPI_ILI9486_Init.h rename to TFT_Drivers/ILI9486_Init.h diff --git a/TFT_Drivers/RPI_ILI9486_Rotation.h b/TFT_Drivers/ILI9486_Rotation.h similarity index 100% rename from TFT_Drivers/RPI_ILI9486_Rotation.h rename to TFT_Drivers/ILI9486_Rotation.h diff --git a/TFT_Drivers/ILI9488_Init.h b/TFT_Drivers/ILI9488_Init.h index feaffdd..917b32b 100644 --- a/TFT_Drivers/ILI9488_Init.h +++ b/TFT_Drivers/ILI9488_Init.h @@ -42,7 +42,7 @@ writedata(0x80); writedata(0x40); - writecommand(0xE0); // Positive Gamma Control + writecommand(0xE0); // Positive Gamma Control writedata(0x00); writedata(0x03); writedata(0x09); diff --git a/TFT_Drivers/ST7789_Defines.h b/TFT_Drivers/ST7789_Defines.h index b900b79..99d35db 100644 --- a/TFT_Drivers/ST7789_Defines.h +++ b/TFT_Drivers/ST7789_Defines.h @@ -3,6 +3,7 @@ #define TFT_WIDTH 240 #define TFT_HEIGHT 240 +#define CGRAM_OFFSET // Delay between some initialisation commands #define TFT_INIT_DELAY 0x80 // Not used unless commandlist invoked diff --git a/TFT_Drivers/ST7789_Init.h b/TFT_Drivers/ST7789_Init.h index 57b526b..d5ba408 100644 --- a/TFT_Drivers/ST7789_Init.h +++ b/TFT_Drivers/ST7789_Init.h @@ -2,6 +2,7 @@ // This is the command sequence that initialises the ST7789 driver // Configure ST7789 display + { static const uint8_t PROGMEM st7789[] = { @@ -9,7 +10,7 @@ static const uint8_t PROGMEM 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_MADCTL, 1, TFT_MAD_RGB, TFT_CASET, 4, 0x00, 0x00, 0x00, 0xF0, TFT_PASET, 4, 0x00, 0x00, 0x00, 0xF0, TFT_INVON, TFT_INIT_DELAY, 10, @@ -21,5 +22,3 @@ static const uint8_t PROGMEM } // End of ST7789 display configuration - - diff --git a/TFT_Drivers/ST7789_Rotation.h b/TFT_Drivers/ST7789_Rotation.h index 3a75ec6..34ba394 100644 --- a/TFT_Drivers/ST7789_Rotation.h +++ b/TFT_Drivers/ST7789_Rotation.h @@ -4,23 +4,31 @@ rotation = m % 4; switch (rotation) { case 0: // Portrait - writedata(TFT_MAD_MX | TFT_MAD_MY | TFT_MAD_RGB); - _width = TFT_WIDTH; - _height = TFT_HEIGHT; + writedata(TFT_MAD_RGB); + _width = _init_width; + _height = _init_height; + colstart = 0; + rowstart = 0; break; case 1: // Landscape (Portrait + 90) - writedata(TFT_MAD_MV | TFT_MAD_MY | TFT_MAD_RGB); - _width = TFT_HEIGHT; - _height = TFT_WIDTH; + writedata(TFT_MAD_MX | TFT_MAD_MV | TFT_MAD_RGB); + _width = _init_height; + _height = _init_width; + colstart = 0; + rowstart = 0; break; case 2: // Inverter portrait - writedata(TFT_MAD_RGB); - _width = TFT_WIDTH; - _height = TFT_HEIGHT; + writedata(TFT_MAD_MX | TFT_MAD_MY | TFT_MAD_RGB); + _width = _init_width; + _height = _init_height; + colstart = 0; + rowstart = 80; break; case 3: // Inverted landscape - writedata(TFT_MAD_MX | TFT_MAD_MV | TFT_MAD_RGB); - _width = TFT_HEIGHT; - _height = TFT_WIDTH; + writedata(TFT_MAD_MV | TFT_MAD_MY | TFT_MAD_RGB); + _width = _init_height; + _height = _init_width; + colstart = 80; + rowstart = 0; break; } diff --git a/TFT_eSPI.cpp b/TFT_eSPI.cpp index b63a1bc..0f80d68 100644 --- a/TFT_eSPI.cpp +++ b/TFT_eSPI.cpp @@ -48,7 +48,7 @@ void busDir(uint32_t mask, uint8_t mode); inline void TFT_eSPI::spi_begin(void){ #if defined (SPI_HAS_TRANSACTION) && defined (SUPPORT_TRANSACTIONS) && !defined(ESP32_PARALLEL) - if (locked) {locked = false; SPI.beginTransaction(SPISettings(SPI_FREQUENCY, MSBFIRST, SPI_MODE0));} + if (locked) {locked = false; SPI.beginTransaction(SPISettings(SPI_FREQUENCY, MSBFIRST, TFT_SPI_MODE));} #endif } @@ -117,12 +117,6 @@ 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++) @@ -168,6 +162,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; @@ -216,11 +212,13 @@ void TFT_eSPI::begin(uint8_t tc) /*************************************************************************************** -** Function name: init +** Function name: init (tc is tab colour for ST7735 displays only) ** Description: Reset, then initialise the TFT display registers ***************************************************************************************/ void TFT_eSPI::init(uint8_t tc) { + if (_booted) + { #if !defined (ESP32) #ifdef TFT_CS cspinmask = (uint32_t) digitalPinToBitMask(TFT_CS); @@ -241,7 +239,7 @@ void TFT_eSPI::init(uint8_t tc) 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(ESP32_PARALLEL) #if defined (TFT_MOSI) && !defined (TFT_SPI_OVERLAP) @@ -252,15 +250,15 @@ void TFT_eSPI::init(uint8_t tc) #endif #endif - inTransaction = false; - locked = true; + inTransaction = false; + locked = true; // 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); - SPI.setDataMode(SPI_MODE0); - SPI.setFrequency(SPI_FREQUENCY); + SPI.setBitOrder(MSBFIRST); + SPI.setDataMode(TFT_SPI_MODE); + SPI.setFrequency(SPI_FREQUENCY); #endif #if defined(ESP32_PARALLEL) @@ -278,11 +276,16 @@ void TFT_eSPI::init(uint8_t tc) // 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 + spi_begin(); + #ifdef TFT_RST if (TFT_RST >= 0) { digitalWrite(TFT_RST, HIGH); @@ -290,15 +293,14 @@ void TFT_eSPI::init(uint8_t tc) digitalWrite(TFT_RST, LOW); delay(20); digitalWrite(TFT_RST, HIGH); - delay(150); } + else writecommand(TFT_SWRST); // Software reset +#else + writecommand(TFT_SWRST); // Software reset #endif - spi_begin(); - writecommand(TFT_SWRST); // Software reset spi_end(); - - delay(5); // Wait for software reset to complete + delay(150); // Wait for reset to complete spi_begin(); @@ -317,7 +319,7 @@ void TFT_eSPI::init(uint8_t tc) #include "TFT_Drivers/S6D02A1_Init.h" #elif defined (RPI_ILI9486_DRIVER) - #include "TFT_Drivers/RPI_ILI9486_Init.h" + #include "TFT_Drivers/ILI9486_Init.h" #elif defined (ILI9486_DRIVER) #include "TFT_Drivers/ILI9486_Init.h" @@ -338,6 +340,7 @@ void TFT_eSPI::init(uint8_t tc) spi_end(); + setRotation(rotation); } @@ -364,7 +367,7 @@ void TFT_eSPI::setRotation(uint8_t m) #include "TFT_Drivers/S6D02A1_Rotation.h" #elif defined (RPI_ILI9486_DRIVER) - #include "TFT_Drivers/RPI_ILI9486_Rotation.h" + #include "TFT_Drivers/ILI9486_Rotation.h" #elif defined (ILI9486_DRIVER) #include "TFT_Drivers/ILI9486_Rotation.h" @@ -566,7 +569,7 @@ uint16_t TFT_eSPI::readPixel(int32_t x0, int32_t y0) // Fetch the 16 bit BRG pixel //uint16_t rgb = (readByte() << 8) | readByte(); -#if defined (ILI9341_DRIVER) | defined (ILI9488_DRIVER) // Read 3 bytes + #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); @@ -578,7 +581,7 @@ uint16_t TFT_eSPI::readPixel(int32_t x0, int32_t y0) return rgb; -#else // ILI9481 16 bit read + #else // ILI9481 16 bit read // Fetch the 16 bit BRG pixel uint16_t bgr = (readByte() << 8) | readByte(); @@ -590,7 +593,7 @@ uint16_t TFT_eSPI::readPixel(int32_t x0, int32_t y0) // Swap Red and Blue (could check MADCTL setting to see if this is needed) return (bgr>>11) | (bgr<<11) | (bgr & 0x7E0); -#endif + #endif #else // Not ESP32_PARALLEL @@ -2174,7 +2177,7 @@ void TFT_eSPI::drawChar(int32_t x, int32_t y, unsigned char c, uint32_t color, u for (int8_t i = 0; i < 5; i++ ) column[i] = pgm_read_byte(font + (c * 5) + i); column[5] = 0; -#if defined (ESP8266) +#if defined (ESP8266) && !defined (ILI9488_DRIVER) color = (color >> 8) | (color << 8); bg = (bg >> 8) | (bg << 8); uint32_t spimask = ~((SPIMMOSI << SPILMOSI) | (SPIMMISO << SPILMISO)); @@ -2197,7 +2200,7 @@ void TFT_eSPI::drawChar(int32_t x, int32_t y, unsigned char c, uint32_t color, u SPI1CMD |= SPIBUSY; while(SPI1CMD & SPIBUSY) {} } -#else // for ESP32 +#else // for ESP32 or ILI9488 for (int8_t j = 0; j < 8; j++) { for (int8_t k = 0; k < 5; k++ ) { diff --git a/TFT_eSPI.h b/TFT_eSPI.h index 75362a9..5816b04 100644 --- a/TFT_eSPI.h +++ b/TFT_eSPI.h @@ -30,6 +30,12 @@ #define SPI_FREQUENCY 20000000 #endif +#ifdef ST7789_DRIVER + #define TFT_SPI_MODE SPI_MODE3 +#else + #define TFT_SPI_MODE SPI_MODE0 +#endif + // If the frequency is not defined, set a default #ifndef SPI_TOUCH_FREQUENCY #define SPI_TOUCH_FREQUENCY 2500000 @@ -214,12 +220,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; \ + #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 + #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; \ @@ -238,6 +244,13 @@ //#define RD_H digitalWrite(TFT_WR, HIGH) #endif +#elif defined (ILI9488_DRIVER) // 16 bit colour converted to 3 bytes for 24 bit RGB + #define tft_Write_8(C) SPI.transfer(C) + #define tft_Write_16(C) SPI.transfer(((C & 0xF800)>>8) | ((C & 0xF800)>>13)); \ + SPI.transfer(((C & 0x07E0)>>3) | ((C & 0x07E0)>> 9)); \ + SPI.transfer(((C & 0x001F)<<3) | ((C & 0x001F)>> 2)) + #define tft_Write_32(C) SPI.write32(C) + #elif defined (RPI_ILI9486_DRIVER) #define tft_Write_8(C) SPI.transfer(0); SPI.transfer(C) #define tft_Write_16(C) SPI.write16(C) @@ -688,6 +701,8 @@ class TFT_eSPI : public Print { 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 diff --git a/User_Setup.h b/User_Setup.h index 782b71d..2548f39 100644 --- a/User_Setup.h +++ b/User_Setup.h @@ -22,6 +22,7 @@ //#define RPI_ILI9486_DRIVER // 20MHz maximum SPI //#define HX8357D_DRIVER //#define ILI9481_DRIVER +//#define ILI9486_DRIVER //#define ILI9488_DRIVER //#define ST7789_DRIVER diff --git a/User_Setup_Select.h b/User_Setup_Select.h index fe14441..bacecb9 100644 --- a/User_Setup_Select.h +++ b/User_Setup_Select.h @@ -69,7 +69,10 @@ #include #define TFT_DRIVER 0x6D02 #elif defined (RPI_ILI9486_DRIVER) - #include + #include + #define TFT_DRIVER 0x9486 +#elif defined (ILI9486_DRIVER) + #include #define TFT_DRIVER 0x9486 #elif defined (ILI9481_DRIVER) #include diff --git a/User_Setups/Setup18_ST7789.h b/User_Setups/Setup18_ST7789.h index d532d0a..1f2030a 100644 --- a/User_Setups/Setup18_ST7789.h +++ b/User_Setups/Setup18_ST7789.h @@ -88,7 +88,7 @@ // ###### 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_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 From a76d0d60dc24375135fb47b5025a74e8d4819742 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Fri, 6 Jul 2018 18:49:23 +0100 Subject: [PATCH 162/287] Raise version --- library.json | 2 +- library.properties | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/library.json b/library.json index a8f6f7f..418a1b5 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "TFT_eSPI", - "version": "0.20.15", + "version": "0.20.16", "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 a2a4eb1..a771190 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=TFT_eSPI -version=0.20.15 +version=0.20.16 author=Bodmer maintainer=Bodmer sentence=A fast TFT library for ESP8266 processors and the Arduino IDE From 5b0d96d64df6da32fb55fe9f7173685f01b22f75 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Sat, 7 Jul 2018 22:41:53 +0100 Subject: [PATCH 163/287] Completed support for SPI ILI9488 Setup20 added for ESP8266 and Setup21 addwd for ESP32 --- TFT_Drivers/ILI9488_Init.h | 78 ++++++------ TFT_eSPI.cpp | 147 +++++++++++++++++++++-- TFT_eSPI.h | 36 ++++-- User_Setup_Select.h | 3 + User_Setups/Setup20_ILI9488.h | 219 ++++++++++++++++++++++++++++++++++ User_Setups/Setup21_ILI9488.h | 219 ++++++++++++++++++++++++++++++++++ library.json | 2 +- library.properties | 2 +- 8 files changed, 646 insertions(+), 60 deletions(-) create mode 100644 User_Setups/Setup20_ILI9488.h create mode 100644 User_Setups/Setup21_ILI9488.h diff --git a/TFT_Drivers/ILI9488_Init.h b/TFT_Drivers/ILI9488_Init.h index 917b32b..2297069 100644 --- a/TFT_Drivers/ILI9488_Init.h +++ b/TFT_Drivers/ILI9488_Init.h @@ -8,40 +8,6 @@ // 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); @@ -76,14 +42,50 @@ writedata(0x37); writedata(0x0F); + writecommand(0XC0); // Power Control 1 + writedata(0x17); + writedata(0x15); + + writecommand(0xC1); // Power Control 2 + writedata(0x41); + + writecommand(0xC5); // VCOM Control + writedata(0x00); + writedata(0x12); + writedata(0x80); + + writecommand(TFT_MADCTL); // Memory Access Control + writedata(0x48); // MX, BGR + + writecommand(0x3A); // Pixel Interface Format +#if defined (ESP32_PARALLEL) + writedata(0x55); // 16 bit colour for parallel +#else + writedata(0x66); // 18 bit colour for SPI +#endif + + writecommand(0xB0); // Interface Mode Control + writedata(0x00); + + writecommand(0xB1); // Frame Rate Control + writedata(0xA0); + + 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(0xF7); // Adjust Control 3 writedata(0xA9); writedata(0x51); writedata(0x2C); - writedata(0x02); - - writecommand(TFT_MADCTL); // Memory Access Control - writedata(0x48); // MX, BGR + writedata(0x82); writecommand(TFT_SLPOUT); //Exit Sleep delay(120); diff --git a/TFT_eSPI.cpp b/TFT_eSPI.cpp index 0f80d68..b2fcb15 100644 --- a/TFT_eSPI.cpp +++ b/TFT_eSPI.cpp @@ -2881,11 +2881,15 @@ void TFT_eSPI::drawPixel(uint32_t x, uint32_t y, uint32_t color) DC_D; +#if defined (ILI9488_DRIVER) + tft_Write_16(color); +#else SPI1U1 = mask | (15 << SPILMOSI) | (15 << SPILMISO); SPI1W0 = (color >> 8) | (color << 8); SPI1CMD |= SPIBUSY; while(SPI1CMD & SPIBUSY) {} +#endif CS_H; @@ -3086,7 +3090,7 @@ 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 - #ifdef ESP32_PARALLEL + #if defined (ESP32_PARALLEL) while (len--) {tft_Write_16(color);} #else writeBlock(color, len); @@ -3116,6 +3120,9 @@ void TFT_eSPI::pushColors(uint8_t *data, uint32_t len) #else #ifdef ESP32_PARALLEL while (len--) {tft_Write_8(*data); data++;} + #elif defined (ILI9488_DRIVER) + uint16_t color; + while (len>1) {color = (*data++) | ((*data++)<<8); tft_Write_16(color); len-=2;} #else #if (SPI_FREQUENCY == 80000000) while ( len >=64 ) {SPI.writePattern(data, 64, 1); data += 64; len -= 64; } @@ -3142,8 +3149,8 @@ void TFT_eSPI::pushColors(uint16_t *data, uint32_t len, bool swap) CS_L; -#if defined (ESP32) - #ifdef ESP32_PARALLEL +#if defined (ESP32) || defined (ILI9488_DRIVER) + #if defined (ESP32_PARALLEL) || defined (ILI9488_DRIVER) if (swap) while ( len-- ) {tft_Write_16(*data); data++;} else while ( len-- ) {tft_Write_16S(*data); data++;} #else @@ -3240,7 +3247,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) || defined (HX8357D_DRIVER) +#if defined (RPI_ILI9486_DRIVER) || defined (ESP32) || defined (RPI_WRITE_STROBE) || defined (HX8357D_DRIVER) || defined (ILI9488_DRIVER) void TFT_eSPI::drawLine(int32_t x0, int32_t y0, int32_t x1, int32_t y1, uint32_t color) { @@ -4518,7 +4525,7 @@ 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) +#if defined (ESP8266) && !defined (ILI9488_DRIVER) void writeBlock(uint16_t color, uint32_t repeat) { uint16_t color16 = (color >> 8) | (color << 8); @@ -4578,7 +4585,131 @@ void writeBlock(uint16_t color, uint32_t repeat) SPI1U = SPIUMOSI | SPIUDUPLEX | SPIUSSE; } -#else // Low level register based ESP32 code +#elif defined (ILI9488_DRIVER) + +#ifdef ESP8266 +void writeBlock(uint16_t color, uint32_t repeat) +{ + + uint32_t mask = ~(SPIMMOSI << SPILMOSI); + mask = SPI1U1 & mask; + SPI1U = SPIUMOSI | SPIUSSE; + + // Split out the colours + uint8_t r = (color & 0xF800)>>8; + uint8_t g = (color & 0x07E0)>>3; + uint8_t b = (color & 0x001F)<<3; + // Concatenate 4 pixels into three 32 bit blocks + uint32_t r0 = r<<24 | b<<16 | g<<8 | r; + uint32_t r1 = g<<24 | r<<16 | b<<8 | g; + uint32_t r2 = b<<24 | g<<16 | r<<8 | b; + + SPI1W0 = r0; + SPI1W1 = r1; + SPI1W2 = r2; + + if (repeat > 4) + { + SPI1W3 = r0; + SPI1W4 = r1; + SPI1W5 = r2; + } + if (repeat > 8) + { + SPI1W6 = r0; + SPI1W7 = r1; + SPI1W8 = r2; + } + if (repeat > 12) + { + SPI1W9 = r0; + SPI1W10 = r1; + SPI1W11 = r2; + SPI1W12 = r0; + SPI1W13 = r1; + SPI1W14 = r2; + SPI1W15 = r0; + } + + if (repeat > 20) + { + SPI1U1 = mask | (503 << SPILMOSI); + while(repeat>20) + { + while(SPI1CMD & SPIBUSY) {} + SPI1CMD |= SPIBUSY; + repeat -= 21; + } + while(SPI1CMD & SPIBUSY) {} + } + + if (repeat) + { + repeat = (repeat * 24) - 1; + SPI1U1 = mask | (repeat << SPILMOSI); + SPI1CMD |= SPIBUSY; + while(SPI1CMD & SPIBUSY) {} + } + + SPI1U = SPIUMOSI | SPIUDUPLEX | SPIUSSE; +} +#else // Now the code for ESP32 and ILI9488 + +#include "soc/spi_reg.h" +#define SPI_NUM 0x3 + +void writeBlock(uint16_t color, uint32_t repeat) +{ + // Split out the colours + uint8_t r = (color & 0xF800)>>8; + uint8_t g = (color & 0x07E0)>>3; + uint8_t b = (color & 0x001F)<<3; + // Concatenate 4 pixels into three 32 bit blocks + uint32_t r0 = r<<24 | b<<16 | g<<8 | r; + uint32_t r1 = g<<24 | r<<16 | b<<8 | g; + uint32_t r2 = b<<24 | g<<16 | r<<8 | b; + + if (repeat > 9) + { + SET_PERI_REG_BITS(SPI_MOSI_DLEN_REG(SPI_NUM), SPI_USR_MOSI_DBITLEN, 239, SPI_USR_MOSI_DBITLEN_S); + + while(repeat>9) + { + while (READ_PERI_REG(SPI_CMD_REG(SPI_NUM))&SPI_USR); + WRITE_PERI_REG((SPI_W0_REG(SPI_NUM) + 0), r0); + WRITE_PERI_REG((SPI_W0_REG(SPI_NUM) + 4), r1); + WRITE_PERI_REG((SPI_W0_REG(SPI_NUM) + 8), r2); + WRITE_PERI_REG((SPI_W0_REG(SPI_NUM) + 12), r0); + WRITE_PERI_REG((SPI_W0_REG(SPI_NUM) + 16), r1); + WRITE_PERI_REG((SPI_W0_REG(SPI_NUM) + 20), r2); + WRITE_PERI_REG((SPI_W0_REG(SPI_NUM) + 24), r0); + WRITE_PERI_REG((SPI_W0_REG(SPI_NUM) + 28), r1); + SET_PERI_REG_MASK(SPI_CMD_REG(SPI_NUM), SPI_USR); + repeat -= 10; + } + while (READ_PERI_REG(SPI_CMD_REG(SPI_NUM))&SPI_USR); + } + + if (repeat) + { + repeat = (repeat * 24) - 1; + SET_PERI_REG_BITS(SPI_MOSI_DLEN_REG(SPI_NUM), SPI_USR_MOSI_DBITLEN, repeat, SPI_USR_MOSI_DBITLEN_S); + WRITE_PERI_REG((SPI_W0_REG(SPI_NUM) + 0), r0); + WRITE_PERI_REG((SPI_W0_REG(SPI_NUM) + 4), r1); + WRITE_PERI_REG((SPI_W0_REG(SPI_NUM) + 8), r2); + WRITE_PERI_REG((SPI_W0_REG(SPI_NUM) + 12), r0); + WRITE_PERI_REG((SPI_W0_REG(SPI_NUM) + 16), r1); + WRITE_PERI_REG((SPI_W0_REG(SPI_NUM) + 20), r2); + WRITE_PERI_REG((SPI_W0_REG(SPI_NUM) + 24), r0); + WRITE_PERI_REG((SPI_W0_REG(SPI_NUM) + 28), r1); + SET_PERI_REG_MASK(SPI_CMD_REG(SPI_NUM), SPI_USR); + while (READ_PERI_REG(SPI_CMD_REG(SPI_NUM))&SPI_USR); + } + +} +#endif + +#else // Low level register based ESP32 code for 16 bit colour SPI TFTs #include "soc/spi_reg.h" #define SPI_NUM 0x3 @@ -4595,7 +4726,7 @@ void writeBlock(uint16_t color, uint32_t repeat) while(repeat>15) { while (READ_PERI_REG(SPI_CMD_REG(SPI_NUM))&SPI_USR); - for (uint32_t i=0; i<16; i++) WRITE_PERI_REG((SPI_W0_REG(SPI_NUM) + (i << 2)), color32); + for (uint32_t i=0; i<8; i++) WRITE_PERI_REG((SPI_W0_REG(SPI_NUM) + (i << 2)), color32); SET_PERI_REG_MASK(SPI_CMD_REG(SPI_NUM), SPI_USR); repeat -= 16; } @@ -4606,7 +4737,7 @@ void writeBlock(uint16_t color, uint32_t repeat) { repeat = (repeat << 4) - 1; SET_PERI_REG_BITS(SPI_MOSI_DLEN_REG(SPI_NUM), SPI_USR_MOSI_DBITLEN, repeat, SPI_USR_MOSI_DBITLEN_S); - for (uint32_t i=0; i<16; i++) WRITE_PERI_REG((SPI_W0_REG(SPI_NUM) + (i << 2)), color32); + for (uint32_t i=0; i<8; i++) WRITE_PERI_REG((SPI_W0_REG(SPI_NUM) + (i << 2)), color32); SET_PERI_REG_MASK(SPI_CMD_REG(SPI_NUM), SPI_USR); while (READ_PERI_REG(SPI_CMD_REG(SPI_NUM))&SPI_USR); } diff --git a/TFT_eSPI.h b/TFT_eSPI.h index 5816b04..8dfe0bd 100644 --- a/TFT_eSPI.h +++ b/TFT_eSPI.h @@ -244,22 +244,34 @@ //#define RD_H digitalWrite(TFT_WR, HIGH) #endif -#elif defined (ILI9488_DRIVER) // 16 bit colour converted to 3 bytes for 24 bit RGB - #define tft_Write_8(C) SPI.transfer(C) - #define tft_Write_16(C) SPI.transfer(((C & 0xF800)>>8) | ((C & 0xF800)>>13)); \ - SPI.transfer(((C & 0x07E0)>>3) | ((C & 0x07E0)>> 9)); \ - SPI.transfer(((C & 0x001F)<<3) | ((C & 0x001F)>> 2)) - #define tft_Write_32(C) SPI.write32(C) +#elif defined (ILI9488_DRIVER) // 16 bit colour converted to 3 bytes for 18 bit RGB + + // Write 8 bits to TFT + #define tft_Write_8(C) SPI.transfer(C) + + // Convert 16 bit colour to 18 bit and write in 3 bytes + #define tft_Write_16(C) SPI.transfer((C & 0xF800)>>8); \ + SPI.transfer((C & 0x07E0)>>3); \ + SPI.transfer((C & 0x001F)<<3) + + // Convert swapped byte 16 bit colour to 18 bit and write in 3 bytes + #define tft_Write_16S(C) SPI.transfer(C & 0xF8); \ + SPI.transfer((C & 0xE0)>>11 | (C & 0x07)<<5); \ + SPI.transfer((C & 0x1F00)>>5) + // Write 32 bits to TFT + #define tft_Write_32(C) SPI.write32(C) #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) + + #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 tft_Write_8(C) SPI.transfer(C) - #define tft_Write_16(C) SPI.write16(C) - #define tft_Write_32(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 diff --git a/User_Setup_Select.h b/User_Setup_Select.h index bacecb9..7b2c23d 100644 --- a/User_Setup_Select.h +++ b/User_Setup_Select.h @@ -40,6 +40,9 @@ //#include // Setup file for any Waveshare ePaper display //#include // Setup file configured for HX8357D (untested) +//#include // Setup file for ESP8266 and ILI9488 SPI bus TFT +//#include // Setup file for ESP32 and ILI9488 SPI bus TFT + //#include diff --git a/User_Setups/Setup20_ILI9488.h b/User_Setups/Setup20_ILI9488.h new file mode 100644 index 0000000..1a20ffd --- /dev/null +++ b/User_Setups/Setup20_ILI9488.h @@ -0,0 +1,219 @@ +// 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 ILI9488_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 + +//#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 (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 diff --git a/User_Setups/Setup21_ILI9488.h b/User_Setups/Setup21_ILI9488.h new file mode 100644 index 0000000..9b74048 --- /dev/null +++ b/User_Setups/Setup21_ILI9488.h @@ -0,0 +1,219 @@ +// 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 ILI9488_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 + +//#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 (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 diff --git a/library.json b/library.json index 418a1b5..b53afb6 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "TFT_eSPI", - "version": "0.20.16", + "version": "0.20.17", "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 a771190..f5fb525 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=TFT_eSPI -version=0.20.16 +version=0.20.17 author=Bodmer maintainer=Bodmer sentence=A fast TFT library for ESP8266 processors and the Arduino IDE From 2773d6a148b1299ca8edfc79a51495eaf77c4f37 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Sun, 8 Jul 2018 15:04:33 +0100 Subject: [PATCH 164/287] Update text re. ILI9488 SPI support ILI9488 is now supported in SPI mode as well as 8 bit parallel. --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 00e3023..b4d3d2e 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, ILI9486, HX8357D and ST7789 based TFT displays that support SPI. +An Arduino IDE compatible graphics and fonts library for ESP8266 and ESP32 processors with drivers for ILI9341, ILI9163, ST7735, S6D02A1, ILI9481, ILI9486, ILI9488, 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. +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). From 27d80339d947f746519b64f4b49a32829359c289 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Mon, 9 Jul 2018 10:54:05 +0100 Subject: [PATCH 165/287] Update notes on ESP8266 pins --- User_Setup_Select.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/User_Setup_Select.h b/User_Setup_Select.h index 7b2c23d..00b6013 100644 --- a/User_Setup_Select.h +++ b/User_Setup_Select.h @@ -104,12 +104,12 @@ #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_D3 0 // Low on boot means enter FLASH mode +#define PIN_D4 2 // TXD1 (must be high on boot to go to UART0 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_D8 15 // HCS TXD0 (must be low on boot to enter UART0 FLASH mode) #define PIN_D9 3 // RXD0 #define PIN_D10 1 // TXD0 From 40e2c8dc745080342bf287c4e4716dac740f7fcb Mon Sep 17 00:00:00 2001 From: Bodmer Date: Wed, 11 Jul 2018 20:30:34 +0100 Subject: [PATCH 166/287] Update so read TFT works for ILI9488 SPI display Small performance improvement for ILI9488 --- TFT_eSPI.cpp | 120 ++++++++++++++++++++++++++++++++++++--------------- TFT_eSPI.h | 9 ++-- 2 files changed, 91 insertions(+), 38 deletions(-) diff --git a/TFT_eSPI.cpp b/TFT_eSPI.cpp index b2fcb15..9770163 100644 --- a/TFT_eSPI.cpp +++ b/TFT_eSPI.cpp @@ -603,11 +603,20 @@ uint16_t TFT_eSPI::readPixel(int32_t x0, int32_t y0) // Dummy read to throw away don't care value tft_Write_8(0); - - // Read window pixel 24 bit RGB values - uint8_t r = tft_Write_8(0); - uint8_t g = tft_Write_8(0); - uint8_t b = tft_Write_8(0); + + // 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 + #if !defined (ILI9488_DRIVER) + uint8_t r = tft_Write_8(0); + uint8_t g = tft_Write_8(0); + uint8_t b = tft_Write_8(0); + #else + // The 6 colour bits are in MS 6 bits of each byte, but the ILI9488 needs an extra clock pulse + // so bits appear shifted right 1 bit, so mask the middle 6 bits then shift 1 place left + uint8_t r = (tft_Write_8(0)&0x7E)<<1; + uint8_t g = (tft_Write_8(0)&0x7E)<<1; + uint8_t b = (tft_Write_8(0)&0x7E)<<1; + #endif CS_H; @@ -734,11 +743,20 @@ void TFT_eSPI::readRect(uint32_t x, uint32_t y, uint32_t w, uint32_t h, uint16_t // 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 + #if !defined (ILI9488_DRIVER) uint8_t r = tft_Write_8(0); uint8_t g = tft_Write_8(0); uint8_t b = tft_Write_8(0); + #else + // The 6 colour bits are in LS 6 bits of each byte but we do not include the extra clock pulse + // so we use a trick and mask the middle 6 bits of the byte, then only shift 1 place left + uint8_t r = (tft_Write_8(0)&0x7E)<<1; + uint8_t g = (tft_Write_8(0)&0x7E)<<1; + uint8_t b = (tft_Write_8(0)&0x7E)<<1; + #endif // Swapped colour byte order for compatibility with pushRect() *data++ = (r & 0xF8) | (g & 0xE0) >> 5 | (b & 0xF8) << 5 | (g & 0x1C) << 11; @@ -1311,9 +1329,21 @@ void TFT_eSPI::readRectRGB(int32_t x0, int32_t y0, int32_t w, int32_t h, uint8_ 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 + + // 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 + #if !defined (ILI9488_DRIVER) *data++ = tft_Write_8(0); *data++ = tft_Write_8(0); *data++ = tft_Write_8(0); + #else + // The 6 colour bits are in MS 6 bits of each byte, but the ILI9488 needs an extra clock pulse + // so bits appear shifted right 1 bit, so mask the middle 6 bits then shift 1 place left + *data++ = (tft_Write_8(0)&0x7E)<<1; + *data++ = (tft_Write_8(0)&0x7E)<<1; + *data++ = (tft_Write_8(0)&0x7E)<<1; + #endif + } CS_H; @@ -2144,6 +2174,10 @@ int16_t TFT_eSPI::fontHeight(int16_t font) return pgm_read_byte( &fontdata[font].height ) * textsize; } +int16_t TFT_eSPI::fontHeight(void) +{ + return fontHeight(textfont); +} /*************************************************************************************** ** Function name: drawChar @@ -3079,7 +3113,7 @@ void TFT_eSPI::pushColor(uint16_t color) ** Function name: pushColor ** Description: push a single colour to "len" pixels ***************************************************************************************/ -void TFT_eSPI::pushColor(uint16_t color, uint16_t len) +void TFT_eSPI::pushColor(uint16_t color, uint32_t len) { spi_begin(); @@ -4661,47 +4695,64 @@ void writeBlock(uint16_t color, uint32_t repeat) void writeBlock(uint16_t color, uint32_t repeat) { // Split out the colours - uint8_t r = (color & 0xF800)>>8; - uint8_t g = (color & 0x07E0)>>3; - uint8_t b = (color & 0x001F)<<3; + uint32_t r = (color & 0xF800)>>8; + uint32_t g = (color & 0x07E0)<<5; + uint32_t b = (color & 0x001F)<<19; // Concatenate 4 pixels into three 32 bit blocks - uint32_t r0 = r<<24 | b<<16 | g<<8 | r; - uint32_t r1 = g<<24 | r<<16 | b<<8 | g; - uint32_t r2 = b<<24 | g<<16 | r<<8 | b; + uint32_t r0 = r<<24 | b | g | r; + uint32_t r1 = r0>>8 | g<<16; + uint32_t r2 = r1>>8 | b<<8; - if (repeat > 9) + if (repeat > 19) { - SET_PERI_REG_BITS(SPI_MOSI_DLEN_REG(SPI_NUM), SPI_USR_MOSI_DBITLEN, 239, SPI_USR_MOSI_DBITLEN_S); + SET_PERI_REG_BITS(SPI_MOSI_DLEN_REG(SPI_NUM), SPI_USR_MOSI_DBITLEN, 479, SPI_USR_MOSI_DBITLEN_S); - while(repeat>9) + while(repeat>19) { while (READ_PERI_REG(SPI_CMD_REG(SPI_NUM))&SPI_USR); - WRITE_PERI_REG((SPI_W0_REG(SPI_NUM) + 0), r0); - WRITE_PERI_REG((SPI_W0_REG(SPI_NUM) + 4), r1); - WRITE_PERI_REG((SPI_W0_REG(SPI_NUM) + 8), r2); - WRITE_PERI_REG((SPI_W0_REG(SPI_NUM) + 12), r0); - WRITE_PERI_REG((SPI_W0_REG(SPI_NUM) + 16), r1); - WRITE_PERI_REG((SPI_W0_REG(SPI_NUM) + 20), r2); - WRITE_PERI_REG((SPI_W0_REG(SPI_NUM) + 24), r0); - WRITE_PERI_REG((SPI_W0_REG(SPI_NUM) + 28), r1); + WRITE_PERI_REG(SPI_W0_REG(SPI_NUM), r0); + WRITE_PERI_REG(SPI_W1_REG(SPI_NUM), r1); + WRITE_PERI_REG(SPI_W2_REG(SPI_NUM), r2); + WRITE_PERI_REG(SPI_W3_REG(SPI_NUM), r0); + WRITE_PERI_REG(SPI_W4_REG(SPI_NUM), r1); + WRITE_PERI_REG(SPI_W5_REG(SPI_NUM), r2); + WRITE_PERI_REG(SPI_W6_REG(SPI_NUM), r0); + WRITE_PERI_REG(SPI_W7_REG(SPI_NUM), r1); + WRITE_PERI_REG(SPI_W8_REG(SPI_NUM), r2); + WRITE_PERI_REG(SPI_W9_REG(SPI_NUM), r0); + WRITE_PERI_REG(SPI_W10_REG(SPI_NUM), r1); + WRITE_PERI_REG(SPI_W11_REG(SPI_NUM), r2); + WRITE_PERI_REG(SPI_W12_REG(SPI_NUM), r0); + WRITE_PERI_REG(SPI_W13_REG(SPI_NUM), r1); + WRITE_PERI_REG(SPI_W14_REG(SPI_NUM), r2); SET_PERI_REG_MASK(SPI_CMD_REG(SPI_NUM), SPI_USR); - repeat -= 10; + repeat -= 20; } while (READ_PERI_REG(SPI_CMD_REG(SPI_NUM))&SPI_USR); } if (repeat) { - repeat = (repeat * 24) - 1; - SET_PERI_REG_BITS(SPI_MOSI_DLEN_REG(SPI_NUM), SPI_USR_MOSI_DBITLEN, repeat, SPI_USR_MOSI_DBITLEN_S); - WRITE_PERI_REG((SPI_W0_REG(SPI_NUM) + 0), r0); - WRITE_PERI_REG((SPI_W0_REG(SPI_NUM) + 4), r1); - WRITE_PERI_REG((SPI_W0_REG(SPI_NUM) + 8), r2); - WRITE_PERI_REG((SPI_W0_REG(SPI_NUM) + 12), r0); - WRITE_PERI_REG((SPI_W0_REG(SPI_NUM) + 16), r1); - WRITE_PERI_REG((SPI_W0_REG(SPI_NUM) + 20), r2); - WRITE_PERI_REG((SPI_W0_REG(SPI_NUM) + 24), r0); - WRITE_PERI_REG((SPI_W0_REG(SPI_NUM) + 28), r1); + SET_PERI_REG_BITS(SPI_MOSI_DLEN_REG(SPI_NUM), SPI_USR_MOSI_DBITLEN, (repeat * 24) - 1, SPI_USR_MOSI_DBITLEN_S); + WRITE_PERI_REG(SPI_W0_REG(SPI_NUM), r0); + WRITE_PERI_REG(SPI_W1_REG(SPI_NUM), r1); + WRITE_PERI_REG(SPI_W2_REG(SPI_NUM), r2); + WRITE_PERI_REG(SPI_W3_REG(SPI_NUM), r0); + WRITE_PERI_REG(SPI_W4_REG(SPI_NUM), r1); + WRITE_PERI_REG(SPI_W5_REG(SPI_NUM), r2); + if (repeat > 8 ) + { + WRITE_PERI_REG(SPI_W6_REG(SPI_NUM), r0); + WRITE_PERI_REG(SPI_W7_REG(SPI_NUM), r1); + WRITE_PERI_REG(SPI_W8_REG(SPI_NUM), r2); + WRITE_PERI_REG(SPI_W9_REG(SPI_NUM), r0); + WRITE_PERI_REG(SPI_W10_REG(SPI_NUM), r1); + WRITE_PERI_REG(SPI_W11_REG(SPI_NUM), r2); + WRITE_PERI_REG(SPI_W12_REG(SPI_NUM), r0); + WRITE_PERI_REG(SPI_W13_REG(SPI_NUM), r1); + WRITE_PERI_REG(SPI_W14_REG(SPI_NUM), r2); + } + SET_PERI_REG_MASK(SPI_CMD_REG(SPI_NUM), SPI_USR); while (READ_PERI_REG(SPI_CMD_REG(SPI_NUM))&SPI_USR); } @@ -4714,6 +4765,7 @@ void writeBlock(uint16_t color, uint32_t repeat) #include "soc/spi_reg.h" #define SPI_NUM 0x3 + void writeBlock(uint16_t color, uint32_t repeat) { uint16_t color16 = (color >> 8) | (color << 8); diff --git a/TFT_eSPI.h b/TFT_eSPI.h index 8dfe0bd..fdb39e7 100644 --- a/TFT_eSPI.h +++ b/TFT_eSPI.h @@ -538,7 +538,7 @@ class TFT_eSPI : public Print { // The TFT_eSprite class inherits the following functions 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), + pushColor(uint16_t color, uint32_t len), pushColors(uint16_t *data, uint32_t len, bool swap = true), // With byte swap option pushColors(uint8_t *data, uint32_t len), @@ -565,7 +565,7 @@ class TFT_eSPI : public Print { 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 + setBitmapColor(uint16_t fgcolor, uint16_t bgcolor), // For 1bpp sprites setCursor(int16_t x, int16_t y), setCursor(int16_t x, int16_t y, uint8_t font), @@ -632,7 +632,7 @@ class TFT_eSPI : public Print { getCursorY(void); uint16_t fontsLoaded(void), - color565(uint8_t r, uint8_t g, uint8_t b), + color565(uint8_t red, uint8_t green, uint8_t blue), // Convert 8 bit red, green and blue to 16 bits color8to16(uint8_t color332); // Convert 8 bit colour to 16 bits int16_t drawNumber(long long_num,int poX, int poY, int font), @@ -656,7 +656,8 @@ class TFT_eSPI : public Print { textWidth(const char *string), textWidth(const String& string, int font), textWidth(const String& string), - fontHeight(int16_t font); + fontHeight(int16_t font), + fontHeight(void); void setAddrWindow(int32_t xs, int32_t ys, int32_t xe, int32_t ye); From 73c1831e7b6dc9394129c9bf1f7f7b51b8cf41af Mon Sep 17 00:00:00 2001 From: Bodmer Date: Sun, 29 Jul 2018 15:00:09 +0100 Subject: [PATCH 167/287] Add option for a different TFT read SPI frequency #define SPI_READ_FREQUENCY 20000000 // Optional reduced SPI frequency for reading TFT Also weeded out some compiler warnings --- Extensions/Smooth_font.cpp | 5 +++- Extensions/Sprite.cpp | 12 +++++----- TFT_eSPI.cpp | 48 +++++++++++++++++++++++++++----------- TFT_eSPI.h | 13 +++++++++-- User_Setup.h | 2 ++ library.json | 2 +- library.properties | 2 +- 7 files changed, 60 insertions(+), 24 deletions(-) diff --git a/Extensions/Smooth_font.cpp b/Extensions/Smooth_font.cpp index 14ed796..7ec2340 100644 --- a/Extensions/Smooth_font.cpp +++ b/Extensions/Smooth_font.cpp @@ -256,7 +256,10 @@ uint16_t TFT_eSPI::decodeUTF8(uint8_t *buf, uint16_t *index, uint16_t remaining) // 16 bit Unicode if (((c & 0xF0) == 0xE0) && (remaining > 2)) - return ((c & 0x0F)<<12) | ((buf[(*index)++]&0x3F)<<6) | ((buf[(*index)++]&0x3F)); + { + c = ((c & 0x0F)<<12) | ((buf[(*index)++]&0x3F)<<6); + return c | ((buf[(*index)++]&0x3F)); + } // 21 bit Unicode not supported so fall-back to extended ASCII // if ((c & 0xF8) == 0xF0) return c; diff --git a/Extensions/Sprite.cpp b/Extensions/Sprite.cpp index a7443e9..fb94747 100644 --- a/Extensions/Sprite.cpp +++ b/Extensions/Sprite.cpp @@ -517,13 +517,14 @@ void TFT_eSprite::pushColor(uint32_t color, uint16_t len) if (!_created ) return; uint16_t pixelColor; + if (_bpp == 16) pixelColor = (uint16_t) (color >> 8) | (color << 8); else if (_bpp == 8) pixelColor = (color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3; - // else Nothing to do for 1bpp + else pixelColor = (uint16_t) color; // for 1bpp while(len--) writeColor(pixelColor); } @@ -1213,11 +1214,11 @@ void TFT_eSprite::drawChar(int32_t x, int32_t y, unsigned char c, uint32_t color 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); + 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; + uint8_t xx, yy, bits=0, bit=0; int16_t xo16 = 0, yo16 = 0; if(size > 1) { @@ -1609,7 +1610,6 @@ void TFT_eSprite::printToSprite(char *cbuffer, int len) //String string) } createSprite(sWidth, this->gFont.yAdvance); - uint16_t transparent = TFT_BLACK; if (this->textbgcolor != TFT_BLACK) fillSprite(this->textbgcolor); } @@ -1646,7 +1646,7 @@ int16_t TFT_eSprite::printToSprite(int16_t x, int16_t y, uint16_t index) if (newSprite) { createSprite(sWidth, this->gFont.yAdvance); - uint16_t transparent = TFT_BLACK; + if (this->textbgcolor != TFT_BLACK) fillSprite(this->textbgcolor); drawGlyph(this->gUnicode[index]); diff --git a/TFT_eSPI.cpp b/TFT_eSPI.cpp index 9770163..d19d0c6 100644 --- a/TFT_eSPI.cpp +++ b/TFT_eSPI.cpp @@ -58,6 +58,26 @@ inline void TFT_eSPI::spi_end(void){ #endif } +inline void TFT_eSPI::spi_begin_read(void){ +#if defined (SPI_HAS_TRANSACTION) && defined (SUPPORT_TRANSACTIONS) && !defined(ESP32_PARALLEL) + if (locked) {locked = false; SPI.beginTransaction(SPISettings(SPI_READ_FREQUENCY, MSBFIRST, TFT_SPI_MODE));} +#else + #if !defined(ESP32_PARALLEL) + SPI.setFrequency(SPI_READ_FREQUENCY); + #endif +#endif +} + +inline void TFT_eSPI::spi_end_read(void){ +#if defined (SPI_HAS_TRANSACTION) && defined (SUPPORT_TRANSACTIONS) && !defined(ESP32_PARALLEL) + if(!inTransaction) {if (!locked) {locked = true; SPI.endTransaction();}} +#else + #if !defined(ESP32_PARALLEL) + SPI.setFrequency(SPI_FREQUENCY); + #endif +#endif +} + #if defined (TOUCH_CS) && defined (SPI_TOUCH_FREQUENCY) // && !defined(ESP32_PARALLEL) inline void TFT_eSPI::spi_begin_touch(void){ @@ -495,7 +515,7 @@ uint8_t TFT_eSPI::readcommand8(uint8_t cmd_function, uint8_t index) #else // for ILI9341 Interface II i.e. IM [3:0] = "1101" - spi_begin(); + spi_begin_read(); index = 0x10 + (index & 0x0F); DC_C; @@ -512,7 +532,7 @@ uint8_t TFT_eSPI::readcommand8(uint8_t cmd_function, uint8_t index) reg = tft_Write_8(0); CS_H; - spi_end(); + spi_end_read(); #endif return reg; } @@ -526,7 +546,7 @@ uint16_t TFT_eSPI::readcommand16(uint8_t cmd_function, uint8_t index) { uint32_t reg; - reg |= (readcommand8(cmd_function, index + 0) << 8); + reg = (readcommand8(cmd_function, index + 0) << 8); reg |= (readcommand8(cmd_function, index + 1) << 0); return reg; @@ -597,7 +617,7 @@ uint16_t TFT_eSPI::readPixel(int32_t x0, int32_t y0) #else // Not ESP32_PARALLEL - spi_begin(); + spi_begin_read(); readAddrWindow(x0, y0, x0, y0); // Sets CS low @@ -620,7 +640,7 @@ uint16_t TFT_eSPI::readPixel(int32_t x0, int32_t y0) CS_H; - spi_end(); + spi_end_read(); return color565(r, g, b); @@ -733,7 +753,7 @@ void TFT_eSPI::readRect(uint32_t x, uint32_t y, uint32_t w, uint32_t h, uint16_t #else // Not ESP32_PARALLEL - spi_begin(); + spi_begin_read(); readAddrWindow(x, y, x + w - 1, y + h - 1); // Sets CS low @@ -764,7 +784,7 @@ void TFT_eSPI::readRect(uint32_t x, uint32_t y, uint32_t w, uint32_t h, uint16_t CS_H; - spi_end(); + spi_end_read(); #endif } @@ -1183,7 +1203,7 @@ void TFT_eSPI::pushImage(int32_t x, int32_t y, uint32_t w, uint32_t h, uint8_t * uint8_t msbColor = 0; uint8_t lsbColor = 0; - int32_t spx = x, spy = y; + //int32_t spx = x, spy = y; while (dh--) { @@ -1317,7 +1337,7 @@ bool TFT_eSPI::getSwapBytes(void) 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(); + spi_begin_read(); readAddrWindow(x0, y0, x0 + w - 1, y0 + h - 1); // Sets CS low @@ -1347,7 +1367,7 @@ void TFT_eSPI::readRectRGB(int32_t x0, int32_t y0, int32_t w, int32_t h, uint8_ } CS_H; - spi_end(); + spi_end_read(); #endif } @@ -2301,11 +2321,11 @@ void TFT_eSPI::drawChar(int32_t x, int32_t y, unsigned char c, uint32_t color, u 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); + 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; + uint8_t xx, yy, bits=0, bit=0; int16_t xo16 = 0, yo16 = 0; if(size > 1) { @@ -4054,8 +4074,10 @@ int16_t TFT_eSPI::drawChar(unsigned int uniCode, int x, int y, int font) //spi_begin(); setAddrWindow(x, y, x + width - 1, y + height - 1); +#ifdef RPI_WRITE_STROBE uint8_t textcolorBin[] = { (uint8_t) (textcolor >> 8), (uint8_t) textcolor }; uint8_t textbgcolorBin[] = { (uint8_t) (textbgcolor >> 8), (uint8_t) textbgcolor }; +#endif // Maximum font size is equivalent to 180x180 pixels in area while (w > 0) diff --git a/TFT_eSPI.h b/TFT_eSPI.h index fdb39e7..5729a37 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_READ_FREQUENCY + #define SPI_READ_FREQUENCY SPI_FREQUENCY +#endif + #ifdef ST7789_DRIVER #define TFT_SPI_MODE SPI_MODE3 #else @@ -213,8 +218,9 @@ #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)<>7)<>6)<>5)<>4)<>3)<>2)<>1)<>0)< Date: Mon, 30 Jul 2018 12:08:02 -0500 Subject: [PATCH 168/287] adds support for 160x80 ebay module unknown module 160x80 BGR inverted, offset 26 differs from adafruit of same size --- TFT_Drivers/ST7735_Defines.h | 5 +++++ TFT_Drivers/ST7735_Init.h | 7 +++++++ TFT_Drivers/ST7735_Rotation.h | 16 ++++++++++++++++ User_Setups/SetupX_Template.h | 2 ++ 4 files changed, 30 insertions(+) diff --git a/TFT_Drivers/ST7735_Defines.h b/TFT_Drivers/ST7735_Defines.h index 5fe2662..35e9659 100644 --- a/TFT_Drivers/ST7735_Defines.h +++ b/TFT_Drivers/ST7735_Defines.h @@ -15,6 +15,7 @@ #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 INITR_GREENTAB160x80 0x6 // Use if you only get part of 128x128 screen in rotation 0 & 1 #define INITB 0xB @@ -38,6 +39,10 @@ #define TAB_COLOUR INITR_GREENTAB128 #define CGRAM_OFFSET +#elif defined (ST7735_GREENTAB160x80) + #define TAB_COLOUR INITR_GREENTAB160x80 + #define CGRAM_OFFSET + #elif defined (ST7735_REDTAB) #define TAB_COLOUR INITR_REDTAB diff --git a/TFT_Drivers/ST7735_Init.h b/TFT_Drivers/ST7735_Init.h index 0df152c..3eef1d9 100644 --- a/TFT_Drivers/ST7735_Init.h +++ b/TFT_Drivers/ST7735_Init.h @@ -173,6 +173,13 @@ colstart = 0; rowstart = 32; } + else if (tabcolor == INITR_GREENTAB160x80) + { + commandList(Rcmd2green); + writecommand(TFT_INVON); + colstart = 26; + rowstart = 1; + } else if (tabcolor == INITR_REDTAB) { commandList(Rcmd2red); diff --git a/TFT_Drivers/ST7735_Rotation.h b/TFT_Drivers/ST7735_Rotation.h index b9476a1..89701f7 100644 --- a/TFT_Drivers/ST7735_Rotation.h +++ b/TFT_Drivers/ST7735_Rotation.h @@ -20,6 +20,10 @@ writedata(TFT_MAD_MX | TFT_MAD_MY | TFT_MAD_MH | TFT_MAD_BGR); colstart = 0; rowstart = 32; + } else if(tabcolor == INITR_GREENTAB160x80) { + writedata(TFT_MAD_MX | TFT_MAD_MY | TFT_MAD_MH | TFT_MAD_BGR); + colstart = 26; + rowstart = 1; } else if(tabcolor == INITB) { writedata(TFT_MAD_MX | TFT_MAD_RGB); } else { @@ -43,6 +47,10 @@ writedata(TFT_MAD_MV | TFT_MAD_MY | TFT_MAD_BGR); colstart = 32; rowstart = 0; + } else if(tabcolor == INITR_GREENTAB160x80) { + writedata(TFT_MAD_MV | TFT_MAD_MY | TFT_MAD_BGR); + colstart = 1; + rowstart = 26; } else if(tabcolor == INITB) { writedata(TFT_MAD_MV | TFT_MAD_MX | TFT_MAD_MY | TFT_MAD_RGB); } else { @@ -66,6 +74,10 @@ writedata(TFT_MAD_BGR); colstart = 0; rowstart = 0; + } else if(tabcolor == INITR_GREENTAB160x80) { + writedata(TFT_MAD_BGR); + colstart = 0; + rowstart = 0; } else if(tabcolor == INITB) { writedata(TFT_MAD_MY | TFT_MAD_RGB); } else { @@ -89,6 +101,10 @@ writedata(TFT_MAD_MX | TFT_MAD_MV | TFT_MAD_BGR); colstart = 0; rowstart = 0; + } else if(tabcolor == INITR_GREENTAB160x80) { + writedata(TFT_MAD_MX | TFT_MAD_MV | TFT_MAD_BGR); + colstart = 1; + rowstart = 26; } else if(tabcolor == INITB) { writedata(TFT_MAD_MV | TFT_MAD_RGB); } else { diff --git a/User_Setups/SetupX_Template.h b/User_Setups/SetupX_Template.h index 8b22cbc..f321517 100644 --- a/User_Setups/SetupX_Template.h +++ b/User_Setups/SetupX_Template.h @@ -24,6 +24,7 @@ //#define M5STACK // For ST7735 and ILI9163 ONLY, define the pixel width and height in portrait orientation +//#define TFT_WIDTH 80 //#define TFT_WIDTH 128 //#define TFT_HEIGHT 160 //#define TFT_HEIGHT 128 @@ -40,6 +41,7 @@ //#define ST7735_GREENTAB2 //#define ST7735_GREENTAB3 //#define ST7735_GREENTAB128 // For 128 x 128 display +//#define ST7735_GREENTAB160x80 // For 160 x 80 display (BGR, inverted, 26 offset) //#define ST7735_REDTAB //#define ST7735_BLACKTAB From d8684840aa5014bd2fd909633c74e9da59a6cc12 Mon Sep 17 00:00:00 2001 From: Shawn A Date: Mon, 30 Jul 2018 12:10:42 -0500 Subject: [PATCH 169/287] update user_setup --- User_Setup.h | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/User_Setup.h b/User_Setup.h index 139f0e9..dca9207 100644 --- a/User_Setup.h +++ b/User_Setup.h @@ -30,8 +30,9 @@ //#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_WIDTH 80 +// #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 @@ -41,12 +42,13 @@ // 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_INITB +// #define ST7735_GREENTAB +// #define ST7735_GREENTAB2 +// #define ST7735_GREENTAB3 +// #define ST7735_GREENTAB128 // For 128 x 128 display +// #define ST7735_GREENTAB160x80 // For 160 x 80 display (BGR, inverted, 26 offset) +// #define ST7735_REDTAB //#define ST7735_BLACKTAB // ################################################################################## From 8eefc6f26e77710973a3a414bf963749b97cf346 Mon Sep 17 00:00:00 2001 From: Andriy Makukha Date: Wed, 1 Aug 2018 21:36:15 +0800 Subject: [PATCH 170/287] Added ST7789 initialization --- TFT_Drivers/ST7789_Defines.h | 90 +++++++++++++++++++++--- TFT_Drivers/ST7789_Init.h | 132 ++++++++++++++++++++++++++++++----- TFT_eSPI.h | 6 +- 3 files changed, 197 insertions(+), 31 deletions(-) diff --git a/TFT_Drivers/ST7789_Defines.h b/TFT_Drivers/ST7789_Defines.h index 99d35db..5a0d1be 100644 --- a/TFT_Drivers/ST7789_Defines.h +++ b/TFT_Drivers/ST7789_Defines.h @@ -1,7 +1,11 @@ // 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 +#ifndef TFT_WIDTH + #define TFT_WIDTH 240 +#endif +#ifndef TFT_HEIGHT + #define TFT_HEIGHT 320 +#endif #define CGRAM_OFFSET @@ -15,25 +19,20 @@ #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 +// Flags for TFT_MADCTL #define TFT_MAD_MY 0x80 #define TFT_MAD_MX 0x40 #define TFT_MAD_MV 0x20 @@ -46,4 +45,79 @@ #define TFT_IDXRD 0x00 // ILI9341 only, indexed control register read +// ST7789 specific commands used in init +#define ST7789_NOP 0x00 +#define ST7789_SWRESET 0x01 +#define ST7789_RDDID 0x04 +#define ST7789_RDDST 0x09 + +#define ST7789_RDDPM 0x0A // Read display power mode +#define ST7789_RDD_MADCTL 0x0B // Read display MADCTL +#define ST7789_RDD_COLMOD 0x0C // Read display pixel format +#define ST7789_RDDIM 0x0D // Read display image mode +#define ST7789_RDDSM 0x0E // Read display signal mode +#define ST7789_RDDSR 0x0F // Read display self-diagnostic result (ST7789V) + +#define ST7789_SLPIN 0x10 +#define ST7789_SLPOUT 0x11 +#define ST7789_PTLON 0x12 +#define ST7789_NORON 0x13 + +#define ST7789_INVOFF 0x20 +#define ST7789_INVON 0x21 +#define ST7789_GAMSET 0x26 // Gamma set +#define ST7789_DISPOFF 0x28 +#define ST7789_DISPON 0x29 +#define ST7789_CASET 0x2A +#define ST7789_RASET 0x2B +#define ST7789_RAMWR 0x2C +#define ST7789_RGBSET 0x2D // Color setting for 4096, 64K and 262K colors +#define ST7789_RAMRD 0x2E + +#define ST7789_PTLAR 0x30 +#define ST7789_VSCRDEF 0x33 // Vertical scrolling definition (ST7789V) +#define ST7789_TEOFF 0x34 // Tearing effect line off +#define ST7789_TEON 0x35 // Tearing effect line on +#define ST7789_MADCTL 0x36 // Memory data access control +#define ST7789_IDMOFF 0x38 // Idle mode off +#define ST7789_IDMON 0x39 // Idle mode on +#define ST7789_RAMWRC 0x3C // Memory write continue (ST7789V) +#define ST7789_RAMRDC 0x3E // Memory read continue (ST7789V) +#define ST7789_COLMOD 0x3A + +#define ST7789_RAMCTRL 0xB0 // RAM control +#define ST7789_RGBCTRL 0xB1 // RGB control +#define ST7789_PORCTRL 0xB2 // Porch control +#define ST7789_FRCTRL1 0xB3 // Frame rate control +#define ST7789_PARCTRL 0xB5 // Partial mode control +#define ST7789_GCTRL 0xB7 // Gate control +#define ST7789_GTADJ 0xB8 // Gate on timing adjustment +#define ST7789_DGMEN 0xBA // Digital gamma enable +#define ST7789_VCOMS 0xBB // VCOMS setting +#define ST7789_LCMCTRL 0xC0 // LCM control +#define ST7789_IDSET 0xC1 // ID setting +#define ST7789_VDVVRHEN 0xC2 // VDV and VRH command enable +#define ST7789_VRHS 0xC3 // VRH set +#define ST7789_VDVSET 0xC4 // VDV setting +#define ST7789_VCMOFSET 0xC5 // VCOMS offset set +#define ST7789_FRCTR2 0xC6 // FR Control 2 +#define ST7789_CABCCTRL 0xC7 // CABC control +#define ST7789_REGSEL1 0xC8 // Register value section 1 +#define ST7789_REGSEL2 0xCA // Register value section 2 +#define ST7789_PWMFRSEL 0xCC // PWM frequency selection +#define ST7789_PWCTRL1 0xD0 // Power control 1 +#define ST7789_VAPVANEN 0xD2 // Enable VAP/VAN signal output +#define ST7789_CMD2EN 0xDF // Command 2 enable +#define ST7789_PVGAMCTRL 0xE0 // Positive voltage gamma control +#define ST7789_NVGAMCTRL 0xE1 // Negative voltage gamma control +#define ST7789_DGMLUTR 0xE2 // Digital gamma look-up table for red +#define ST7789_DGMLUTB 0xE3 // Digital gamma look-up table for blue +#define ST7789_GATECTRL 0xE4 // Gate control +#define ST7789_SPI2EN 0xE7 // SPI2 enable +#define ST7789_PWCTRL2 0xE8 // Power control 2 +#define ST7789_EQCTRL 0xE9 // Equalize time control +#define ST7789_PROMCTRL 0xEC // Program control +#define ST7789_PROMEN 0xFA // Program mode enable +#define ST7789_NVMSET 0xFC // NVM setting +#define ST7789_PROMACT 0xFE // Program action diff --git a/TFT_Drivers/ST7789_Init.h b/TFT_Drivers/ST7789_Init.h index d5ba408..9b69cb4 100644 --- a/TFT_Drivers/ST7789_Init.h +++ b/TFT_Drivers/ST7789_Init.h @@ -1,24 +1,120 @@ // This is the command sequence that initialises the ST7789 driver - -// Configure ST7789 display +// +// This setup information uses simple 8 bit SPI writecommand() and writedata() functions +// +// See ST7735_Setup.h file for an alternative format { -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, TFT_MAD_RGB, - 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 - }; + writecommand(ST7789_SLPOUT); // Sleep out + delay(120); - commandList(st7789); + writecommand(ST7789_NORON); // Normal display mode on + + //------------------------------display and color format setting--------------------------------// + writecommand(ST7789_MADCTL); + writedata(0x00); + writedata(0x48); + + // JLX240 display datasheet + writecommand(0xB6); + writedata(0x0A); + writedata(0x82); + + writecommand(ST7789_COLMOD); + writedata(0x55); + + //--------------------------------ST7789V Frame rate setting----------------------------------// + writecommand(ST7789_PORCTRL); + writedata(0x0c); + writedata(0x0c); + writedata(0x00); + writedata(0x33); + writedata(0x33); + + writecommand(ST7789_GCTRL); // Voltages: VGH / VGL + writedata(0x35); + + //---------------------------------ST7789V Power setting--------------------------------------// + writecommand(ST7789_VCOMS); + writedata(0x28); // JLX240 display datasheet + + writecommand(ST7789_LCMCTRL); + writedata(0x0C); + + writecommand(ST7789_VDVVRHEN); + writedata(0x01); + writedata(0xFF); + + writecommand(ST7789_VRHS); // voltage VRHS + writedata(0x10); + + writecommand(ST7789_VDVSET); + writedata(0x20); + + writecommand(ST7789_FRCTR2); + writedata(0x0f); + + writecommand(ST7789_PWCTRL1); + writedata(0xa4); + writedata(0xa1); + + //--------------------------------ST7789V gamma setting---------------------------------------// + writecommand(ST7789_PVGAMCTRL); + writedata(0xd0); + writedata(0x00); + writedata(0x02); + writedata(0x07); + writedata(0x0a); + writedata(0x28); + writedata(0x32); + writedata(0x44); + writedata(0x42); + writedata(0x06); + writedata(0x0e); + writedata(0x12); + writedata(0x14); + writedata(0x17); + + writecommand(ST7789_NVGAMCTRL); + writedata(0xd0); + writedata(0x00); + writedata(0x02); + writedata(0x07); + writedata(0x0a); + writedata(0x28); + writedata(0x31); + writedata(0x54); + writedata(0x47); + writedata(0x0e); + writedata(0x1c); + writedata(0x17); + writedata(0x1b); + writedata(0x1e); + + writecommand(ST7789_INVOFF); + + writecommand(ST7789_CASET); // Column address set + writedata(0x00); + writedata(0x00); + writedata(0x00); + writedata(0xE5); // 239 + + writecommand(ST7789_RASET); // Row address set + writedata(0x00); + writedata(0x00); + writedata(0x01); + writedata(0x3F); // 319 + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + spi_end(); + delay(120); + spi_begin(); + + writecommand(ST7789_DISPON); //Display on + + // Turn on the back-light LED + digitalWrite(TFT_BL, HIGH); + pinMode(TFT_BL, OUTPUT); } -// End of ST7789 display configuration - diff --git a/TFT_eSPI.h b/TFT_eSPI.h index 5729a37..3be13f5 100644 --- a/TFT_eSPI.h +++ b/TFT_eSPI.h @@ -35,11 +35,7 @@ #define SPI_READ_FREQUENCY SPI_FREQUENCY #endif -#ifdef ST7789_DRIVER - #define TFT_SPI_MODE SPI_MODE3 -#else - #define TFT_SPI_MODE SPI_MODE0 -#endif +#define TFT_SPI_MODE SPI_MODE0 // If the frequency is not defined, set a default #ifndef SPI_TOUCH_FREQUENCY From 273aed5c85fe03c3a29a8cfc32612f1c0eb1641e Mon Sep 17 00:00:00 2001 From: Bodmer Date: Wed, 1 Aug 2018 23:14:59 +0100 Subject: [PATCH 171/287] Raise to version 1.0.0 --- library.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library.properties b/library.properties index bd3e5b9..e789025 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=TFT_eSPI -version=0.20.18 +version=1.0.0 author=Bodmer maintainer=Bodmer sentence=A fast TFT library for ESP8266 processors and the Arduino IDE From 06a72ad9c37b87ff3e724139efe2f653dff4ec11 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Wed, 1 Aug 2018 23:15:52 +0100 Subject: [PATCH 172/287] Raise to 1.0.0 --- library.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library.json b/library.json index 0e37ef4..5265998 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "TFT_eSPI", - "version": "0.20.18", + "version": "1.0.0", "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": From 7d48498f455dfb892ebcb8fb1e75357cea8bcfcc Mon Sep 17 00:00:00 2001 From: Andriy Makukha Date: Thu, 2 Aug 2018 16:32:20 +0800 Subject: [PATCH 173/287] Returned SPI_MODE3 for the ST7789 driver --- TFT_eSPI.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/TFT_eSPI.h b/TFT_eSPI.h index 3be13f5..5729a37 100644 --- a/TFT_eSPI.h +++ b/TFT_eSPI.h @@ -35,7 +35,11 @@ #define SPI_READ_FREQUENCY SPI_FREQUENCY #endif -#define TFT_SPI_MODE SPI_MODE0 +#ifdef ST7789_DRIVER + #define TFT_SPI_MODE SPI_MODE3 +#else + #define TFT_SPI_MODE SPI_MODE0 +#endif // If the frequency is not defined, set a default #ifndef SPI_TOUCH_FREQUENCY From 59f4b17ed36551db6dd80740efd9f3a14bf6bfeb Mon Sep 17 00:00:00 2001 From: Bodmer Date: Thu, 2 Aug 2018 22:49:30 +0100 Subject: [PATCH 174/287] Add note about SDO line (leave TFT SDO disconnected if other SPI devices share MISO) --- User_Setups/Setup20_ILI9488.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/User_Setups/Setup20_ILI9488.h b/User_Setups/Setup20_ILI9488.h index 1a20ffd..ada2eee 100644 --- a/User_Setups/Setup20_ILI9488.h +++ b/User_Setups/Setup20_ILI9488.h @@ -53,7 +53,7 @@ // 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 SDO/MISO to NodeMCU pin D6 (leave TFT SDO disconnected if other SPI devices share MISO) // Display LED to NodeMCU pin VIN (or 5V, see below) // Display SCK to NodeMCU pin D5 // Display SDI/MOSI to NodeMCU pin D7 @@ -114,7 +114,7 @@ // For ESP32 Dev board (only tested with ILI9341 display) // The hardware SPI can be mapped to any pins -//#define TFT_MISO 19 +//#define TFT_MISO 19 // (leave TFT SDO disconnected if other SPI devices share MISO) //#define TFT_MOSI 23 //#define TFT_SCLK 18 //#define TFT_CS 15 // Chip select control pin From 2f4c48ee72cf1b919b7b020fcad58e7b696af8d0 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Thu, 2 Aug 2018 22:51:25 +0100 Subject: [PATCH 175/287] Add note about SDO (leave TFT SDO disconnected if other SPI devices share MISO) --- User_Setups/Setup21_ILI9488.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/User_Setups/Setup21_ILI9488.h b/User_Setups/Setup21_ILI9488.h index 9b74048..07e1f8f 100644 --- a/User_Setups/Setup21_ILI9488.h +++ b/User_Setups/Setup21_ILI9488.h @@ -53,7 +53,7 @@ // 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 SDO/MISO to NodeMCU pin D6 (leave TFT SDO disconnected if other SPI devices share MISO) // Display LED to NodeMCU pin VIN (or 5V, see below) // Display SCK to NodeMCU pin D5 // Display SDI/MOSI to NodeMCU pin D7 @@ -114,7 +114,7 @@ // For ESP32 Dev board (only tested with ILI9341 display) // The hardware SPI can be mapped to any pins -#define TFT_MISO 19 +#define TFT_MISO 19 // (leave TFT SDO disconnected if other SPI devices share MISO) #define TFT_MOSI 23 #define TFT_SCLK 18 #define TFT_CS 15 // Chip select control pin From a9c4351c64c9f3ffccb76838da8379cc480d5e6a Mon Sep 17 00:00:00 2001 From: Bodmer Date: Sun, 26 Aug 2018 23:17:58 +0100 Subject: [PATCH 176/287] Allow more settle time for raw values read from touch controller --- Extensions/Touch.cpp | 7 ++++++- library.json | 2 +- library.properties | 2 +- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/Extensions/Touch.cpp b/Extensions/Touch.cpp index 8a3efa8..87fa182 100644 --- a/Extensions/Touch.cpp +++ b/Extensions/Touch.cpp @@ -22,8 +22,13 @@ uint8_t TFT_eSPI::getTouchRaw(uint16_t *x, uint16_t *y){ T_CS_L; + // Dummy transfer + SPI.transfer(0xd0); + SPI.transfer(0); + SPI.transfer(0); + // Start bit + YP sample request for x position - tmp = SPI.transfer(0xd0); + SPI.transfer(0xd0); tmp = SPI.transfer(0); tmp = tmp <<5; tmp |= 0x1f & (SPI.transfer(0)>>3); diff --git a/library.json b/library.json index 5265998..8e644b2 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "TFT_eSPI", - "version": "1.0.0", + "version": "1.0.1", "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 e789025..c9eba93 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=TFT_eSPI -version=1.0.0 +version=1.0.1 author=Bodmer maintainer=Bodmer sentence=A fast TFT library for ESP8266 processors and the Arduino IDE From 641ac9f51eb2860b483fa4d7e083d00eb4e97e9c Mon Sep 17 00:00:00 2001 From: Bodmer Date: Mon, 17 Sep 2018 23:37:14 +0100 Subject: [PATCH 177/287] Load via library manager now available. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b4d3d2e..bb2dfd5 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 drivers for ILI9341, ILI9163, ST7735, S6D02A1, ILI9481, ILI9486, ILI9488, HX8357D and ST7789 based TFT displays that support SPI. +An Arduino IDE compatible graphics and fonts library for ESP8266 and ESP32 processors with drivers for ILI9341, ILI9163, ST7735, S6D02A1, ILI9481, ILI9486, ILI9488, HX8357D and ST7789 based TFT displays that support SPI. The library can be loaded using the Arduino IDE's Library Manager. 8 bit parallel interface TFTs (e.g. UNO format mcufriend shields) can used with an ESP32. From a38efa53300d7767db414e7bef070b6d2817a8d4 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Wed, 10 Oct 2018 22:52:21 +0100 Subject: [PATCH 178/287] Add missing image files --- .../Generic/ESP32_SDcard_jpeg/Data/Baboon40.jpg | Bin 0 -> 24384 bytes .../Generic/ESP32_SDcard_jpeg/Data/EagleEye.jpg | Bin 0 -> 20838 bytes .../Generic/ESP32_SDcard_jpeg/Data/Mouse480.jpg | Bin 0 -> 6609 bytes .../Generic/ESP32_SDcard_jpeg/Data/lena20k.jpg | Bin 0 -> 19414 bytes 4 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 examples/Generic/ESP32_SDcard_jpeg/Data/Baboon40.jpg create mode 100644 examples/Generic/ESP32_SDcard_jpeg/Data/EagleEye.jpg create mode 100644 examples/Generic/ESP32_SDcard_jpeg/Data/Mouse480.jpg create mode 100644 examples/Generic/ESP32_SDcard_jpeg/Data/lena20k.jpg diff --git a/examples/Generic/ESP32_SDcard_jpeg/Data/Baboon40.jpg b/examples/Generic/ESP32_SDcard_jpeg/Data/Baboon40.jpg new file mode 100644 index 0000000000000000000000000000000000000000..a53afef096cf4bf6f52bf4d61b7dbc01e75f3a74 GIT binary patch literal 24384 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 YW~f|Ki9z^Vev_5~>SWvk+wuJW0_vjI>Hq)$ literal 0 HcmV?d00001 diff --git a/examples/Generic/ESP32_SDcard_jpeg/Data/lena20k.jpg b/examples/Generic/ESP32_SDcard_jpeg/Data/lena20k.jpg new file mode 100644 index 0000000000000000000000000000000000000000..183b98f58d5c787ecf37aa1da0f5240f32de61a9 GIT binary patch literal 19414 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 *a1q*4*KSYhyn^A!GwaWgA@QrM;-$|$nj86D*d zi*j_1%|!}h)%trhM(=l-HG)y(jf9OtslzEGs;c734|}a#F@`iWbX3UHBf%4bbC)I* z3n{Zj5vfwOlnW9LUTCu7kkJDXJ189yIw+BHfKD|LruRb~S4S$su9LCKbraX zhi?2%DBtBJeR4KO6(=CstBdlI#?5q(kkql!vO8?za{h`{6!xNPgKkBMUc??-PfEjU zfphgFt4c_tmXvkuJuJOi(F-W4ENX*(7bCtl(^L}2W{UcRDdFH%@K{K2e=X7RNQ`_s zyu6YNIDa=)s5W{y#1$@V+|iBwThOQQm>VTi80joSuN#L@f(Wgni0USUXh+8%^$55< zob#OXM%>LliIA_MhgX_nX{)1i&CG3m^$R~z(-hBRopXK4(&G_BTW7&$)ka4Vz1CiQ z&T}v315}JK_C?-AGDDxLBb#kr(gNTkU^69dhg%(@hc`C;v{vx&>Dih`G+T02JWi@` z%Fa+xqr;?^__8_I?78NzwsgU@(jBeG7~986(FXuqRbJcS|R#Xf2piCzw0Vl;3z7FY`=+o$$W4Y%}D1wp$iv=C`^Ey{p8?4%8@fr)CmGzu-= z>OktTj)9>ImQx?}uN8q{ve|U0SwwEJjLKTUM3qIKo>B- zQ|MM&W8!rqD;w@LT##M22oS$ZCL7Gu^mN#~k%O%Ufq?JA5{5SBw0ph4A(uvHB5B)y(1nuDgnq1w0F&B1*THv4}NV5a*mbG zKZGb!(U9k6K2v3FPRyhQ%aV1Zc4EdFo|p?=IM~3{1qi_EQQ9mQBW;~YBz1!5g4(FG zSgCc@Erjj<%b-|`t_>8eq6Q|?yXsV1qLJBS626LCp|#psPj!q*r$VJ=7OvjNigw(~ zg}ar}vXIm)0y%YE14Z7tuK<7<6&?lsZG$(R=^N~_FvUB=b%7 literal 0 HcmV?d00001 From 1fd8d4cd6d8562ff1cf2fecf3c92ef96d44fa151 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Thu, 11 Oct 2018 23:46:19 +0100 Subject: [PATCH 179/287] Fix issue #216 --- TFT_eSPI.cpp | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/TFT_eSPI.cpp b/TFT_eSPI.cpp index d19d0c6..b50d4ee 100644 --- a/TFT_eSPI.cpp +++ b/TFT_eSPI.cpp @@ -4156,17 +4156,17 @@ int16_t TFT_eSPI::drawString(const char *string, int poX, int poY, int font) uint16_t cheight = 8 * textsize; #ifdef LOAD_GFXFF - if (font == 1) { - if(gfxFont) { - cheight = glyph_ab * textsize; - poY += cheight; // Adjust for baseline datum of free fonts - baseline = cheight; - padding =101; // Different padding method used for Free Fonts + bool freeFont = (font == 1 && gfxFont && !fontLoaded); - // 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; - } + if (freeFont) { + cheight = glyph_ab * textsize; + poY += cheight; // Adjust for baseline datum of free fonts + baseline = cheight; + padding =101; // Different padding method used for Free Fonts + + // 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; } } #endif @@ -4250,7 +4250,7 @@ int16_t TFT_eSPI::drawString(const char *string, int poX, int poY, int font) int8_t xo = 0; #ifdef LOAD_GFXFF - if ((font == 1) && (gfxFont) && (textcolor!=textbgcolor)) + if (freeFont && (textcolor!=textbgcolor)) { cheight = (glyph_ab + glyph_bb) * textsize; // Get the offset for the first character only to allow for negative offsets @@ -4306,7 +4306,7 @@ int16_t TFT_eSPI::drawString(const char *string, int poX, int poY, int font) { int16_t padXc = poX+cwidth+xo; #ifdef LOAD_GFXFF - if ((font == 1) && (gfxFont)) + if (freeFont) { poX +=xo; // Adjust for negative offset start character poY -= glyph_ab * textsize; From 22177cf29a84235e5fb02996e7954d5f21841fce Mon Sep 17 00:00:00 2001 From: Bodmer Date: Thu, 11 Oct 2018 23:48:25 +0100 Subject: [PATCH 180/287] Raise version --- library.json | 2 +- library.properties | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/library.json b/library.json index 8e644b2..15bfb8b 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "TFT_eSPI", - "version": "1.0.1", + "version": "1.0.2", "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 c9eba93..dd9055a 100644 --- a/library.properties +++ b/library.properties @@ -1,10 +1,10 @@ name=TFT_eSPI -version=1.0.1 +version=1.0.2 author=Bodmer maintainer=Bodmer -sentence=A fast TFT library for ESP8266 processors and the Arduino IDE +sentence=A fast TFT library for ESP8266 and ESP32 processors for the Arduino IDE paragraph=Supports TFT displays using drivers (ILI9341 etc) that operate with hardware SPI. category=Display -url=http://www.instructables.com/id/Arduino-TFT-display-and-font-library/ +url=https://github.com/Bodmer/TFT_eSPI architectures=esp8266,esp32 includes=TFT_eSPI.h From 1a0b37097a58d29f09955e0d7a5a0eb472152f2b Mon Sep 17 00:00:00 2001 From: Bodmer Date: Sun, 21 Oct 2018 17:53:27 +0100 Subject: [PATCH 181/287] Add new anti-aliased (smooth) font examples + bug fixes Four new examples have been added to show different methods of rendering the anta-aliased fonts. A few minor bugs have been corrected associated with the font rendering. drawNumber and drawFloat handle fixed width mono fonts with no digit movement. --- Extensions/Smooth_font.cpp | 2 +- Extensions/Smooth_font.h | 3 +- Extensions/Sprite.cpp | 13 +- TFT_eSPI.cpp | 61 +++-- TFT_eSPI.h | 10 +- User_Setup.h | 3 +- User_Setup_Select.h | 2 + User_Setups/Setup1_ILI9341.h | 6 +- User_Setups/Setup21_ILI9488.h | 3 + .../Smooth Fonts/Font_Demo_1/Font_Demo_1.ino | 170 ++++++++++++++ examples/Smooth Fonts/Font_Demo_1/Notes.ino | 56 +++++ .../Font_Demo_1/data/NotoSansBold15.vlw | Bin 0 -> 10766 bytes .../Font_Demo_1/data/NotoSansBold36.vlw | Bin 0 -> 44169 bytes .../Smooth Fonts/Font_Demo_2/Font_Demo_2.ino | 219 ++++++++++++++++++ examples/Smooth Fonts/Font_Demo_2/Notes.ino | 56 +++++ .../Font_Demo_2/data/NotoSansBold15.vlw | Bin 0 -> 10766 bytes .../Font_Demo_2/data/NotoSansBold36.vlw | Bin 0 -> 44169 bytes .../Smooth Fonts/Font_Demo_3/Font_Demo_3.ino | 218 +++++++++++++++++ examples/Smooth Fonts/Font_Demo_3/Notes.ino | 61 +++++ .../Font_Demo_3/data/NotoSansBold15.vlw | Bin 0 -> 10766 bytes .../Font_Demo_3/data/NotoSansBold36.vlw | Bin 0 -> 44169 bytes .../Font_Demo_3/data/NotoSansMonoSCB20.vlw | Bin 0 -> 15382 bytes .../Smooth Fonts/Font_Demo_4/Font_Demo_4.ino | 128 ++++++++++ examples/Smooth Fonts/Font_Demo_4/Notes.ino | 56 +++++ .../Font_Demo_4/data/NotoSansBold15.vlw | Bin 0 -> 10766 bytes .../Font_Demo_4/data/NotoSansBold36.vlw | Bin 0 -> 44169 bytes keywords.txt | 1 + library.json | 2 +- library.properties | 6 +- 29 files changed, 1034 insertions(+), 42 deletions(-) create mode 100644 examples/Smooth Fonts/Font_Demo_1/Font_Demo_1.ino create mode 100644 examples/Smooth Fonts/Font_Demo_1/Notes.ino create mode 100644 examples/Smooth Fonts/Font_Demo_1/data/NotoSansBold15.vlw create mode 100644 examples/Smooth Fonts/Font_Demo_1/data/NotoSansBold36.vlw create mode 100644 examples/Smooth Fonts/Font_Demo_2/Font_Demo_2.ino create mode 100644 examples/Smooth Fonts/Font_Demo_2/Notes.ino create mode 100644 examples/Smooth Fonts/Font_Demo_2/data/NotoSansBold15.vlw create mode 100644 examples/Smooth Fonts/Font_Demo_2/data/NotoSansBold36.vlw create mode 100644 examples/Smooth Fonts/Font_Demo_3/Font_Demo_3.ino create mode 100644 examples/Smooth Fonts/Font_Demo_3/Notes.ino create mode 100644 examples/Smooth Fonts/Font_Demo_3/data/NotoSansBold15.vlw create mode 100644 examples/Smooth Fonts/Font_Demo_3/data/NotoSansBold36.vlw create mode 100644 examples/Smooth Fonts/Font_Demo_3/data/NotoSansMonoSCB20.vlw create mode 100644 examples/Smooth Fonts/Font_Demo_4/Font_Demo_4.ino create mode 100644 examples/Smooth Fonts/Font_Demo_4/Notes.ino create mode 100644 examples/Smooth Fonts/Font_Demo_4/data/NotoSansBold15.vlw create mode 100644 examples/Smooth Fonts/Font_Demo_4/data/NotoSansBold36.vlw diff --git a/Extensions/Smooth_font.cpp b/Extensions/Smooth_font.cpp index 7ec2340..7d573be 100644 --- a/Extensions/Smooth_font.cpp +++ b/Extensions/Smooth_font.cpp @@ -424,7 +424,7 @@ void TFT_eSPI::drawGlyph(uint16_t code) uint8_t pbuffer[gWidth[gNum]]; - uint16_t xs = 0; + int16_t xs = 0; uint32_t dl = 0; int16_t cy = cursor_y + gFont.maxAscent - gdY[gNum]; diff --git a/Extensions/Smooth_font.h b/Extensions/Smooth_font.h index 6ab09fc..2c7faa2 100644 --- a/Extensions/Smooth_font.h +++ b/Extensions/Smooth_font.h @@ -13,7 +13,8 @@ uint16_t alphaBlend(uint8_t alpha, uint16_t fgc, uint16_t bgc); - void drawGlyph(uint16_t code); + virtual void drawGlyph(uint16_t code); + void showFont(uint32_t td); fs::File fontFile; diff --git a/Extensions/Sprite.cpp b/Extensions/Sprite.cpp index fb94747..acfc315 100644 --- a/Extensions/Sprite.cpp +++ b/Extensions/Sprite.cpp @@ -1473,10 +1473,10 @@ void TFT_eSprite::drawGlyph(uint16_t code) if (code == '\n') { if (_created) { - this->cursor_x = 0; - this->cursor_y += this->gFont.yAdvance; - if (this->cursor_y >= _height) this->cursor_y = 0; - return; + this->cursor_x = 0; + this->cursor_y += this->gFont.yAdvance; + if (this->cursor_y >= _height) this->cursor_y = 0; + return; } else { @@ -1511,7 +1511,7 @@ void TFT_eSprite::drawGlyph(uint16_t code) uint8_t pbuffer[this->gWidth[gNum]]; - uint16_t xs = 0; + int16_t xs = 0; uint16_t dl = 0; for (int y = 0; y < this->gHeight[gNum]; y++) @@ -1525,7 +1525,8 @@ void TFT_eSprite::drawGlyph(uint16_t code) if (pixel != 0xFF) { 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)); + if (_bpp != 1) drawPixel(x + this->cursor_x + this->gdX[gNum], y + this->cursor_y + this->gFont.maxAscent - this->gdY[gNum], alphaBlend(pixel, fg, bg)); + else if (pixel>127) drawPixel(x + this->cursor_x + this->gdX[gNum], y + this->cursor_y + this->gFont.maxAscent - this->gdY[gNum], fg); } else { diff --git a/TFT_eSPI.cpp b/TFT_eSPI.cpp index b50d4ee..aeb0766 100644 --- a/TFT_eSPI.cpp +++ b/TFT_eSPI.cpp @@ -172,6 +172,7 @@ TFT_eSPI::TFT_eSPI(int16_t w, int16_t h) textcolor = bitmap_fg = 0xFFFF; // White textbgcolor = bitmap_bg = 0x0000; // Black padX = 0; // No padding + isDigits = false; // No bounding box adjustment 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 @@ -1548,7 +1549,7 @@ void TFT_eSPI::fillCircleHelper(int32_t x0, int32_t y0, int32_t r, uint8_t corne ** Function name: drawEllipse ** Description: Draw a ellipse outline ***************************************************************************************/ -void TFT_eSPI::drawEllipse(int16_t x0, int16_t y0, int16_t rx, int16_t ry, uint16_t color) +void TFT_eSPI::drawEllipse(int16_t x0, int16_t y0, int32_t rx, int32_t ry, uint16_t color) { if (rx<2) return; if (ry<2) return; @@ -1603,7 +1604,7 @@ void TFT_eSPI::drawEllipse(int16_t x0, int16_t y0, int16_t rx, int16_t ry, uint1 ** Function name: fillEllipse ** Description: draw a filled ellipse ***************************************************************************************/ -void TFT_eSPI::fillEllipse(int16_t x0, int16_t y0, int16_t rx, int16_t ry, uint16_t color) +void TFT_eSPI::fillEllipse(int16_t x0, int16_t y0, int32_t rx, int32_t ry, uint16_t color) { if (rx<2) return; if (ry<2) return; @@ -2102,13 +2103,14 @@ int16_t TFT_eSPI::textWidth(const char *string, int font) if (found) { if(str_width == 0 && gdX[gNum] < 0) str_width -= gdX[gNum]; - if (*string) str_width += gxAdvance[gNum]; + if (*string || isDigits) str_width += gxAdvance[gNum]; else str_width += (gdX[gNum] + gWidth[gNum]); } else str_width += gFont.spaceWidth + 1; } } } + isDigits = false; return str_width; } #endif @@ -2141,8 +2143,8 @@ int16_t TFT_eSPI::textWidth(const char *string, int font) { 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); + // If this is not the last character or is a digit then use xAdvance + if (*string || isDigits) 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)); } @@ -2156,6 +2158,7 @@ int16_t TFT_eSPI::textWidth(const char *string, int font) #endif } } + isDigits = false; return str_width * textsize; } @@ -3715,11 +3718,11 @@ size_t TFT_eSPI::write(uint8_t utf8) //fontFile = SPIFFS.open( _gFontFilename, "r" ); - if(!fontFile) - { - fontLoaded = false; - return 0; - } + //if(!fontFile) + //{ + // fontLoaded = false; + // return 0; + //} drawGlyph(unicode); @@ -4147,6 +4150,7 @@ int16_t TFT_eSPI::drawString(const char *string, int poX, int poY) { return drawString(string, poX, poY, textfont); } + // With font number int16_t TFT_eSPI::drawString(const char *string, int poX, int poY, int font) { @@ -4171,23 +4175,23 @@ int16_t TFT_eSPI::drawString(const char *string, int poX, int poY, int font) } #endif + + // 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(); + } + else +#endif + if (font!=1) { + baseline = pgm_read_byte( &fontdata[font].baseline ) * textsize; + cheight = fontHeight(font); + } + if (textdatum || padX) { - // 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); - } - switch(textdatum) { case TC_DATUM: poX -= cwidth/2; @@ -4418,6 +4422,7 @@ int16_t TFT_eSPI::drawRightString(const char *string, int dX, int poY, int font) ***************************************************************************************/ int16_t TFT_eSPI::drawNumber(long long_num, int poX, int poY) { + isDigits = true; // Eliminate jiggle in monospaced fonts char str[12]; ltoa(long_num, str, 10); return drawString(str, poX, poY, textfont); @@ -4425,6 +4430,7 @@ int16_t TFT_eSPI::drawNumber(long long_num, int poX, int poY) int16_t TFT_eSPI::drawNumber(long long_num, int poX, int poY, int font) { + isDigits = true; // Eliminate jiggle in monospaced fonts char str[12]; ltoa(long_num, str, 10); return drawString(str, poX, poY, font); @@ -4444,6 +4450,7 @@ int16_t TFT_eSPI::drawFloat(float floatNumber, int dp, int poX, int poY) int16_t TFT_eSPI::drawFloat(float floatNumber, int dp, int poX, int poY, int font) { + isDigits = true; char str[14]; // Array to contain decimal string uint8_t ptr = 0; // Initialise pointer for array int8_t digits = 1; // Count the digits to avoid array overflow @@ -4572,7 +4579,7 @@ void TFT_eSPI::setTextFont(uint8_t f) /*************************************************************************************** -** Function name: spiBlockWrite +** Function name: writeBlock ** Description: Write a block of pixels of the same colour ***************************************************************************************/ //Clear screen test 76.8ms theoretical. 81.5ms TFT_eSPI, 967ms Adafruit_ILI9341 @@ -4964,6 +4971,10 @@ void TFT_eSPI::getSetup(setup_t &tft_settings) #include "Extensions/Sprite.cpp" +// #ifdef ESP32 +// #include "Extensions/pSprite.cpp" +// #endif + #ifdef SMOOTH_FONT #include "Extensions/Smooth_font.cpp" #endif diff --git a/TFT_eSPI.h b/TFT_eSPI.h index 5729a37..2c93392 100644 --- a/TFT_eSPI.h +++ b/TFT_eSPI.h @@ -562,8 +562,8 @@ class TFT_eSPI : public Print { fillCircle(int32_t x0, int32_t y0, int32_t r, uint32_t color), fillCircleHelper(int32_t x0, int32_t y0, int32_t r, uint8_t cornername, int32_t delta, uint32_t color), - drawEllipse(int16_t x0, int16_t y0, int16_t rx, int16_t ry, uint16_t color), - fillEllipse(int16_t x0, int16_t y0, int16_t rx, int16_t ry, uint16_t color), + drawEllipse(int16_t x0, int16_t y0, int32_t rx, int32_t ry, uint16_t color), + fillEllipse(int16_t x0, int16_t y0, int32_t rx, int32_t ry, uint16_t color), drawTriangle(int32_t x0, int32_t y0, int32_t x1, int32_t y1, int32_t x2, int32_t y2, uint32_t color), fillTriangle(int32_t x0, int32_t y0, int32_t x1, int32_t y1, int32_t x2, int32_t y2, uint32_t color), @@ -719,6 +719,7 @@ class TFT_eSPI : public Print { uint8_t glyph_ab, // glyph height above baseline glyph_bb; // glyph height below baseline + bool isDigits; // adjust bounding box for numbers to reduce visual jiggling 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 @@ -749,4 +750,9 @@ class TFT_eSPI : public Print { // Load the Sprite Class #include "Extensions/Sprite.h" +// #ifdef ESP32 +// // Load the Sprite Class +// #include "Extensions/pSprite.h" +// #endif + #endif diff --git a/User_Setup.h b/User_Setup.h index 139f0e9..24e2873 100644 --- a/User_Setup.h +++ b/User_Setup.h @@ -236,7 +236,8 @@ // #define SPI_FREQUENCY 40000000 // Maximum to use SPIFFS // #define SPI_FREQUENCY 80000000 -#define SPI_READ_FREQUENCY 20000000 // Optional reduced SPI frequency for reading TFT +// Optional reduced SPI frequency for reading TFT +#define SPI_READ_FREQUENCY 20000000 // The XPT2046 requires a lower SPI clock rate of 2.5MHz so we define that here: #define SPI_TOUCH_FREQUENCY 2500000 diff --git a/User_Setup_Select.h b/User_Setup_Select.h index 00b6013..dee02cf 100644 --- a/User_Setup_Select.h +++ b/User_Setup_Select.h @@ -43,6 +43,8 @@ //#include // Setup file for ESP8266 and ILI9488 SPI bus TFT //#include // Setup file for ESP32 and ILI9488 SPI bus TFT +//#include // Setup file configured for my ST7735S 80x160 + //#include diff --git a/User_Setups/Setup1_ILI9341.h b/User_Setups/Setup1_ILI9341.h index 7eb6fba..1f02b38 100644 --- a/User_Setups/Setup1_ILI9341.h +++ b/User_Setups/Setup1_ILI9341.h @@ -197,10 +197,12 @@ // #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 +#define SPI_READ_FREQUENCY 20000000 + // The XPT2046 requires a lower SPI clock rate of 2.5MHz so we define that here: #define SPI_TOUCH_FREQUENCY 2500000 diff --git a/User_Setups/Setup21_ILI9488.h b/User_Setups/Setup21_ILI9488.h index 07e1f8f..b00d02a 100644 --- a/User_Setups/Setup21_ILI9488.h +++ b/User_Setups/Setup21_ILI9488.h @@ -19,6 +19,9 @@ //#define ILI9163_DRIVER //#define S6D02A1_DRIVER //#define RPI_ILI9486_DRIVER // 20MHz maximum SPI +//#define HX8357D_DRIVER +//#define ILI9481_DRIVER +//#define ILI9486_DRIVER #define ILI9488_DRIVER // For M5Stack ESP32 module with integrated display ONLY, remove // in line below diff --git a/examples/Smooth Fonts/Font_Demo_1/Font_Demo_1.ino b/examples/Smooth Fonts/Font_Demo_1/Font_Demo_1.ino new file mode 100644 index 0000000..0d24af9 --- /dev/null +++ b/examples/Smooth Fonts/Font_Demo_1/Font_Demo_1.ino @@ -0,0 +1,170 @@ +/* + There are four different methods of plotting anti-aliased fonts to the screen. + + This sketch uses method 1, using tft.print() and tft.println() calls. + + In some cases the sketch shows what can go wrong too, so read the comments! + + The font is rendered WITHOUT a background, but a background colour needs to be + set so the anti-aliasing of the character is performed correctly. This is because + characters are drawn one by one. + + This method is good for static text that does not change often because changing + values may flicker. The text appears at the tft cursor coordinates. + + It is also possible to "print" text directly into a created sprite, for example using + spr.println("Hello"); and then push the sprite to the screen. That method is not + demonstrated in this sketch. + +*/ +// The fonts used are in the sketch data folder, press Ctrl+K to view. + +// Upload the fonts and icons to SPIFFS (must set at least 1M for SPIFFS) using the +// "Tools" "ESP8266 (or ESP32) Sketch Data Upload" menu option in the IDE. +// To add this option follow instructions here for the ESP8266: +// https://github.com/esp8266/arduino-esp8266fs-plugin +// or for the ESP32: +// https://github.com/me-no-dev/arduino-esp32fs-plugin + +// Close the IDE and open again to see the new menu option. + +// A processing sketch to create new fonts can be found in the Tools folder of TFT_eSPI +// https://github.com/Bodmer/TFT_eSPI/tree/master/Tools/Create_Smooth_Font/Create_font + +// This sketch uses font files created from the Noto family of fonts: +// https://www.google.com/get/noto/ + +#define AA_FONT_SMALL "NotoSansBold15" +#define AA_FONT_LARGE "NotoSansBold36" + +// Font files are stored in SPIFFS, so load the linbrary +#include + +#include +#include // Hardware-specific library + +TFT_eSPI tft = TFT_eSPI(); + + +void setup(void) { + + Serial.begin(250000); + + tft.begin(); + + tft.setRotation(0); + + if (!SPIFFS.begin()) { + Serial.println("SPIFFS initialisation failed!"); + while (1) yield(); // Stay here twiddling thumbs waiting + } + Serial.println("\r\nSPIFFS available!"); +} + + +void loop() { + + tft.fillScreen(TFT_BLACK); + + tft.setTextColor(TFT_WHITE, TFT_BLACK); // Set the font colour AND the background colour + // so the anti-aliasing works + + tft.setCursor(0, 0); // Set cursor at top left of screen + + + // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + // Small font + // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + tft.loadFont(AA_FONT_SMALL); // Must load the font first + + tft.println("Small 15pt font"); // println moves cursor down for a new line + + tft.println(); // New line + + tft.print("ABC"); // print leaves cursor at end of line + + tft.setTextColor(TFT_CYAN, TFT_BLACK); + tft.println("1234"); // Added to line after ABC + + tft.setTextColor(TFT_YELLOW, TFT_BLACK); + // print stream formatting can be used,see: + // https://www.arduino.cc/en/Serial/Print + int ivalue = 1234; + tft.println(ivalue); // print as an ASCII-encoded decimal + tft.println(ivalue, DEC); // print as an ASCII-encoded decimal + tft.println(ivalue, HEX); // print as an ASCII-encoded hexadecimal + tft.println(ivalue, OCT); // print as an ASCII-encoded octal + tft.println(ivalue, BIN); // print as an ASCII-encoded binary + + tft.println(); // New line + tft.setTextColor(TFT_MAGENTA, TFT_BLACK); + float fvalue = 1.23456; + tft.println(fvalue, 0); // no decimal places + tft.println(fvalue, 1); // 1 decimal place + tft.println(fvalue, 2); // 2 decimal places + tft.println(fvalue, 5); // 5 decimal places + + delay(5000); + + // Get ready for the next demo while we have this font loaded + tft.fillScreen(TFT_BLACK); + tft.setCursor(0, 0); // Set cursor at top left of screen + tft.setTextColor(TFT_WHITE, TFT_BLACK); + tft.println("Wrong and right ways to"); + tft.println("print changing values..."); + + tft.unloadFont(); // Remove the font to recover memory used + + + // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + // Large font + // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + tft.loadFont(AA_FONT_LARGE); // Load another different font + + //tft.fillScreen(TFT_BLACK); + + // Draw changing numbers - does not work unless a filled rectangle is drawn over the old text + for (int i = 0; i <= 20; i++) + { + tft.setCursor(50, 50); + tft.print(" "); // Overprinting old number with spaces DOES NOT WORK! + tft.setTextColor(TFT_GREEN, TFT_BLACK); + tft.setCursor(50, 50); + tft.print(i / 10.0, 1); + + tft.fillRect (50, 90, 60, 40, TFT_BLACK); // Overprint with a filled rectangle + tft.setTextColor(TFT_GREEN, TFT_BLACK); + tft.setCursor(50, 90); + tft.print(i / 10.0, 1); + + delay (200); + } + + delay(5000); + + // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + // Large font text wrapping + // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + tft.fillScreen(TFT_BLACK); + + tft.setTextColor(TFT_YELLOW, TFT_BLACK); // Change the font colour and the background colour + + tft.setCursor(0, 0); // Set cursor at top left of screen + + tft.println("Large font!"); + + tft.setTextWrap(true); // Wrap on width + tft.setTextColor(TFT_CYAN, TFT_BLACK); + tft.println("Long lines wrap to the next line"); + + tft.setTextWrap(false, false); // Wrap on width and height switched off + tft.setTextColor(TFT_MAGENTA, TFT_BLACK); + tft.println("Unless text wrap is switched off"); + + tft.unloadFont(); // Remove the font to recover memory used + + delay(8000); +} diff --git a/examples/Smooth Fonts/Font_Demo_1/Notes.ino b/examples/Smooth Fonts/Font_Demo_1/Notes.ino new file mode 100644 index 0000000..f04f2b7 --- /dev/null +++ b/examples/Smooth Fonts/Font_Demo_1/Notes.ino @@ -0,0 +1,56 @@ +/* + +Information notes only: +====================== + +//These are the text plotting alignment (reference datum point) + +TL_DATUM = Top left (default) +TC_DATUM = Top centre +TR_DATUM = Top right + +ML_DATUM = Middle left +MC_DATUM = Middle centre +MR_DATUM = Middle right + +BL_DATUM = Bottom left +BC_DATUM = Bottom centre +BR_DATUM = Bottom right + +L_BASELINE = Left character baseline (Line the 'A' character would sit on) +C_BASELINE = Centre character baseline +R_BASELINE = Right character baseline + +// Basic colours already defined: + +TFT_BLACK 0x0000 +TFT_NAVY 0x000F +TFT_DARKGREEN 0x03E0 +TFT_DARKCYAN 0x03EF +TFT_MAROON 0x7800 +TFT_PURPLE 0x780F +TFT_OLIVE 0x7BE0 +TFT_LIGHTGREY 0xC618 +TFT_DARKGREY 0x7BEF +TFT_BLUE 0x001F +TFT_GREEN 0x07E0 +TFT_CYAN 0x07FF +TFT_RED 0xF800 +TFT_MAGENTA 0xF81F +TFT_YELLOW 0xFFE0 +TFT_WHITE 0xFFFF +TFT_ORANGE 0xFDA0 +TFT_GREENYELLOW 0xB7E0 +TFT_PINK 0xFC9F + + + + + + + + + + + + */ diff --git a/examples/Smooth Fonts/Font_Demo_1/data/NotoSansBold15.vlw b/examples/Smooth Fonts/Font_Demo_1/data/NotoSansBold15.vlw new file mode 100644 index 0000000000000000000000000000000000000000..803a1bd9748c922e455e4952f08368ce634f96f4 GIT binary patch literal 10766 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 literal 0 HcmV?d00001 diff --git a/examples/Smooth Fonts/Font_Demo_1/data/NotoSansBold36.vlw b/examples/Smooth Fonts/Font_Demo_1/data/NotoSansBold36.vlw new file mode 100644 index 0000000000000000000000000000000000000000..66003f6e1c9915e71f571ade912b9eb899323db6 GIT binary patch literal 44169 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 K_&W^#+W!ZD7G}`^ literal 0 HcmV?d00001 diff --git a/examples/Smooth Fonts/Font_Demo_2/Font_Demo_2.ino b/examples/Smooth Fonts/Font_Demo_2/Font_Demo_2.ino new file mode 100644 index 0000000..7eca0be --- /dev/null +++ b/examples/Smooth Fonts/Font_Demo_2/Font_Demo_2.ino @@ -0,0 +1,219 @@ +/* + There are four different methods of plotting anti-aliased fonts to the screen. + + This sketch uses method 2, using graphics calls plotting direct to the TFT: + tft.drawString(string, x, y); + tft.drawNumber(integer, x, y); + tft.drawFloat(float, dp, x, y); // dp = number of decimal places + + setTextDatum() and setTextPadding() functions work with those draw functions. + + This method is good for static text that does not change often because changing + values may flicker. + +*/ +// The fonts used are in the sketch data folder, press Ctrl+K to view. + +// Upload the fonts and icons to SPIFFS (must set at least 1M for SPIFFS) using the +// "Tools" "ESP8266 (or ESP32) Sketch Data Upload" menu option in the IDE. +// To add this option follow instructions here for the ESP8266: +// https://github.com/esp8266/arduino-esp8266fs-plugin +// or for the ESP32: +// https://github.com/me-no-dev/arduino-esp32fs-plugin + +// Close the IDE and open again to see the new menu option. + +// A processing sketch to create new fonts can be found in the Tools folder of TFT_eSPI +// https://github.com/Bodmer/TFT_eSPI/tree/master/Tools/Create_Smooth_Font/Create_font + +// This sketch uses font files created from the Noto family of fonts: +// https://www.google.com/get/noto/ + +#define AA_FONT_SMALL "NotoSansBold15" +#define AA_FONT_LARGE "NotoSansBold36" + +// Font files are stored in SPIFFS, so load the linbrary +#include + +#include +#include // Hardware-specific library + +TFT_eSPI tft = TFT_eSPI(); + +void setup(void) { + + Serial.begin(250000); + + tft.begin(); + + tft.setRotation(1); + + if (!SPIFFS.begin()) { + Serial.println("SPIFFS initialisation failed!"); + while (1) yield(); // Stay here twiddling thumbs waiting + } + Serial.println("\r\nSPIFFS available!"); +} + +void loop() { + + tft.fillScreen(TFT_BLACK); + + tft.setTextColor(TFT_WHITE, TFT_BLACK); // Set the font colour and the background colour + + tft.setTextDatum(TC_DATUM); // Top Centre datum + + int xpos = tft.width() / 2; // Half the screen width + int ypos = 10; + + + // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + // Small font + // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + tft.loadFont(AA_FONT_SMALL); // Must load the font first + + tft.drawString("Small 15pt font", xpos, ypos); + + ypos += tft.fontHeight(); // Get the font height and move ypos down + + tft.setTextColor(TFT_GREEN, TFT_BLACK); + + // If the string does not fit the screen width, then the next character will wrap to a new line + tft.drawString("Ode To A Small Lump Of Green Putty I Found In My Armpit One Midsummer Morning", xpos, ypos); + + tft.setTextColor(TFT_GREEN, TFT_BLUE); // Background colour does not match the screen background! + tft.drawString("Anti-aliasing causes odd looking shadow effects if the text and screen background colours are not the same!", xpos, ypos + 60); + + tft.unloadFont(); // Remove the font to recover memory used + + delay(5000); + + // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + // Large font + // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + tft.loadFont(AA_FONT_LARGE); // Load another different font + + tft.fillScreen(TFT_BLACK); + + tft.setTextColor(TFT_GREEN, TFT_BLUE); // Change the font colour and the background colour + + tft.drawString("36pt font", xpos, ypos); + + ypos += tft.fontHeight(); // Get the font height and move ypos down + + // Set text padding to 100 pixels wide area to over-write old values on screen + tft.setTextPadding(100); + + // Draw changing numbers - likely to flicker using this plot method! + for (int i = 0; i <= 20; i++) { + tft.drawFloat(i / 10.0, 1, xpos, ypos); + delay (200); + } + + tft.unloadFont(); // Remove the font to recover memory used + + delay(5000); + + // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + // Setting the 12 datum positions works with free fonts + // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + // Integer numbers, floats and strings can be drawn relative to a x,y datum, e.g.: + // tft.drawNumber( 123, x, y); + // tft.drawFloat( 1.23, dp, x, y); // Where dp is number of decimal places to show + // tft.drawString( "Abc", x, y); + + tft.fillScreen(TFT_BLACK); + + tft.setTextColor(TFT_DARKGREY, TFT_BLACK); + + // Use middle of screen as datum + xpos = tft.width() /2; + ypos = tft.height()/2; + + tft.loadFont(AA_FONT_SMALL); + tft.setTextDatum(TL_DATUM); + tft.drawString("[Top left]", xpos, ypos); + drawDatumMarker(160,120); + delay(1000); + + tft.fillScreen(TFT_BLACK); + tft.setTextDatum(TC_DATUM); + tft.drawString("[Top centre]", xpos, ypos); + drawDatumMarker(160,120); + delay(1000); + + tft.fillScreen(TFT_BLACK); + tft.setTextDatum(TR_DATUM); + tft.drawString("[Top right]", xpos, ypos); + drawDatumMarker(160,120); + delay(1000); + + tft.fillScreen(TFT_BLACK); + tft.setTextDatum(ML_DATUM); + tft.drawString("[Middle left]", xpos, ypos); + drawDatumMarker(160,120); + delay(1000); + + tft.fillScreen(TFT_BLACK); + tft.setTextDatum(MC_DATUM); + tft.drawString("[Middle centre]", xpos, ypos); + drawDatumMarker(160,120); + delay(1000); + + tft.fillScreen(TFT_BLACK); + tft.setTextDatum(MR_DATUM); + tft.drawString("[Middle right]", xpos, ypos); + drawDatumMarker(160,120); + delay(1000); + + tft.fillScreen(TFT_BLACK); + tft.setTextDatum(BL_DATUM); + tft.drawString("[Bottom left]", xpos, ypos); + drawDatumMarker(160,120); + delay(1000); + + tft.fillScreen(TFT_BLACK); + tft.setTextDatum(BC_DATUM); + tft.drawString("[Bottom centre]", xpos, ypos); + drawDatumMarker(160,120); + delay(1000); + + tft.fillScreen(TFT_BLACK); + tft.setTextDatum(BR_DATUM); + tft.drawString("[Bottom right]", xpos, ypos); + drawDatumMarker(160,120); + delay(1000); + + tft.fillScreen(TFT_BLACK); + tft.setTextDatum(L_BASELINE); + tft.drawString("[Left baseline]", xpos, ypos); + drawDatumMarker(160,120); + delay(1000); + + tft.fillScreen(TFT_BLACK); + tft.setTextDatum(C_BASELINE); + tft.drawString("[Centre baseline]", xpos, ypos); + drawDatumMarker(160,120); + delay(1000); + + tft.fillScreen(TFT_BLACK); + tft.setTextDatum(R_BASELINE); + tft.drawString("[Right baseline]", xpos, ypos); + drawDatumMarker(160,120); + delay(1000); + + tft.unloadFont(); // Remove the font to recover memory used + + delay(4000); + +} + +// Draw a + mark centred on x,y +void drawDatumMarker(int x, int y) +{ + tft.drawLine(x - 5, y, x + 5, y, TFT_GREEN); + tft.drawLine(x, y - 5, x, y + 5, TFT_GREEN); +} diff --git a/examples/Smooth Fonts/Font_Demo_2/Notes.ino b/examples/Smooth Fonts/Font_Demo_2/Notes.ino new file mode 100644 index 0000000..f04f2b7 --- /dev/null +++ b/examples/Smooth Fonts/Font_Demo_2/Notes.ino @@ -0,0 +1,56 @@ +/* + +Information notes only: +====================== + +//These are the text plotting alignment (reference datum point) + +TL_DATUM = Top left (default) +TC_DATUM = Top centre +TR_DATUM = Top right + +ML_DATUM = Middle left +MC_DATUM = Middle centre +MR_DATUM = Middle right + +BL_DATUM = Bottom left +BC_DATUM = Bottom centre +BR_DATUM = Bottom right + +L_BASELINE = Left character baseline (Line the 'A' character would sit on) +C_BASELINE = Centre character baseline +R_BASELINE = Right character baseline + +// Basic colours already defined: + +TFT_BLACK 0x0000 +TFT_NAVY 0x000F +TFT_DARKGREEN 0x03E0 +TFT_DARKCYAN 0x03EF +TFT_MAROON 0x7800 +TFT_PURPLE 0x780F +TFT_OLIVE 0x7BE0 +TFT_LIGHTGREY 0xC618 +TFT_DARKGREY 0x7BEF +TFT_BLUE 0x001F +TFT_GREEN 0x07E0 +TFT_CYAN 0x07FF +TFT_RED 0xF800 +TFT_MAGENTA 0xF81F +TFT_YELLOW 0xFFE0 +TFT_WHITE 0xFFFF +TFT_ORANGE 0xFDA0 +TFT_GREENYELLOW 0xB7E0 +TFT_PINK 0xFC9F + + + + + + + + + + + + */ diff --git a/examples/Smooth Fonts/Font_Demo_2/data/NotoSansBold15.vlw b/examples/Smooth Fonts/Font_Demo_2/data/NotoSansBold15.vlw new file mode 100644 index 0000000000000000000000000000000000000000..803a1bd9748c922e455e4952f08368ce634f96f4 GIT binary patch literal 10766 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 literal 0 HcmV?d00001 diff --git a/examples/Smooth Fonts/Font_Demo_2/data/NotoSansBold36.vlw b/examples/Smooth Fonts/Font_Demo_2/data/NotoSansBold36.vlw new file mode 100644 index 0000000000000000000000000000000000000000..66003f6e1c9915e71f571ade912b9eb899323db6 GIT binary patch literal 44169 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 K_&W^#+W!ZD7G}`^ literal 0 HcmV?d00001 diff --git a/examples/Smooth Fonts/Font_Demo_3/Font_Demo_3.ino b/examples/Smooth Fonts/Font_Demo_3/Font_Demo_3.ino new file mode 100644 index 0000000..5214264 --- /dev/null +++ b/examples/Smooth Fonts/Font_Demo_3/Font_Demo_3.ino @@ -0,0 +1,218 @@ +/* + There are four different methods of plotting anti-aliased fonts to the screen. + + This sketch uses method 3, the font characters are first plotted in a Sprite, then the + Sprite is pushed to the screen. This method is very flexible and the Sprite can be + created, deleted, resized as needed. To render anit-aliased fonts well the Sprite + needs to be 16 bit. The fonts will render in 1 bit per pixel sprites but there + will then be no anti-aliasing. Using 1 bit per pixel Sprites is however useful + to use the extended Unicode range in fonts on mono displays like ePaper. + + A single Sprite can be re-used for plotting different values and graphics to + different positions on the screen. This makes this method a very powerful display tool, + for example round buttons can be created, making use of transparent colour plotting. + +*/ +// The fonts used are in the sketch data folder, press Ctrl+K to view. + +// Upload the fonts and icons to SPIFFS (must set at least 1M for SPIFFS) using the +// "Tools" "ESP8266 (or ESP32) Sketch Data Upload" menu option in the IDE. +// To add this option follow instructions here for the ESP8266: +// https://github.com/esp8266/arduino-esp8266fs-plugin +// or for the ESP32: +// https://github.com/me-no-dev/arduino-esp32fs-plugin + +// Close the IDE and open again to see the new menu option. + +// A processing sketch to create new fonts can be found in the Tools folder of TFT_eSPI +// https://github.com/Bodmer/TFT_eSPI/tree/master/Tools/Create_Smooth_Font/Create_font + +// This sketch uses font files created from the Noto family of fonts: +// https://www.google.com/get/noto/ + +#define AA_FONT_SMALL "NotoSansBold15" +#define AA_FONT_LARGE "NotoSansBold36" +#define AA_FONT_MONO "NotoSansMonoSCB20" // NotoSansMono-SemiCondensedBold 20pt +// Font files are stored in SPIFFS, so load the linbrary +#include + +#include +#include // Hardware-specific library + +TFT_eSPI tft = TFT_eSPI(); +TFT_eSprite spr = TFT_eSprite(&tft); // Sprite class needs to be invoked + +void setup(void) { + + Serial.begin(250000); + + tft.begin(); + + tft.setRotation(1); + + spr.setColorDepth(16); // 16 bit colour needed to show antialiased fonts + + if (!SPIFFS.begin()) { + Serial.println("SPIFFS initialisation failed!"); + while (1) yield(); // Stay here twiddling thumbs waiting + } + Serial.println("\r\nSPIFFS available!"); +} + +void loop() { + + tft.fillScreen(TFT_DARKGREY); + + int xpos = tft.width() / 2; // Half the screen width + int ypos = 50; + + + // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + // Small font + // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + spr.loadFont(AA_FONT_SMALL); // Must load the font first into the sprite class + + spr.createSprite(100, 50); // Create a sprite 100 pixels wide and 50 high + + spr.fillSprite(TFT_BLUE); + + spr.drawRect(0, 0, 100, 50, TFT_WHITE); // Draw sprite border outline (so we see extent) + + spr.setTextColor(TFT_YELLOW, TFT_DARKGREY); // Set the sprite font colour and the background colour + + spr.setTextDatum(MC_DATUM); // Middle Centre datum + + spr.drawString("15pt font", 50, 25 ); // Coords of middle of 100 x 50 Sprite + + spr.pushSprite(10, 10); // Push to TFT screen coord 10, 10 + + spr.pushSprite(10, 70, TFT_BLUE); // Push to TFT screen, TFT_BLUE is transparent + + spr.unloadFont(); // Remove the font from sprite class to recover memory used + + delay(4000); + + // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + // Large font + // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + tft.fillScreen(TFT_BLACK); + + // Beware: Sprites are a differerent "class" to TFT, so different fonts can be loaded + // in the tft and sprite instances, so load the font in the class instance you use! + // In this example this means the spr. instance. + + spr.loadFont(AA_FONT_LARGE); // Load another different font into the sprite instance + + // 100 x 50 sprite was created above and still exists... + + spr.fillSprite(TFT_GREEN); + + spr.setTextColor(TFT_BLACK, TFT_GREEN); // Set the font colour and the background colour + + spr.setTextDatum(MC_DATUM); // Middle Centre datum + + spr.drawString("Fits", 50, 25); // Make sure text fits in the Sprite! + spr.pushSprite(10, 10); // Push to TFT screen coord 10, 10 + + spr.fillSprite(TFT_RED); + spr.setTextColor(TFT_WHITE, TFT_RED); // Set the font colour and the background colour + + spr.drawString("Too big", 50, 25); // Text is too big to all fit in the Sprite! + spr.pushSprite(10, 70); // Push to TFT screen coord 10, 70 + + // Draw changing numbers - no flicker using this plot method! + + // >>>> Note: it is best to use drawNumber() and drawFloat() for numeric values <<<< + // >>>> this reduces digit position movement when the value changes <<<< + // >>>> drawNumber() and drawFloat() functions behave like drawString() and are <<<< + // >>>> supported by setTextDatum() and setTextPadding() <<<< + + spr.setTextDatum(TC_DATUM); // Top Centre datum + + spr.setTextColor(TFT_WHITE, TFT_BLUE); // Set the font colour and the background colour + + for (int i = 0; i <= 200; i++) { + spr.fillSprite(TFT_BLUE); + spr.drawFloat(i / 100.0, 2, 50, 10); // draw with 2 decimal places at 50,10 in sprite + spr.pushSprite(10, 130); // Push to TFT screen coord 10, 130 + delay (20); + } + + spr.unloadFont(); // Remove the font to recover memory used + + spr.deleteSprite(); // Recover memory + + delay(1000); + + // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + // Mono spaced font + // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + spr.loadFont(AA_FONT_MONO); // Mono spaced fonts have fixed intercharacter gaps to + // aid formatting + int bnum = 1; + + // Example of drawing buttons + for (int j = 0; j < 4; j++) + { + for (int k = 0; k < 4; k++) + { + int x = 120 + k * 45; + int y = 40 + j * 30; + button(x, y, bnum++); + } + } + + for (int i = 0; i < 100; i++) + { + button(120, 160, i); + delay(50); + } + + spr.unloadFont(); + + delay(8000); +} + +// ######################################################################### +// Draw a number in a rounded rectangle with some transparent pixels +// Load the font before calling +// ######################################################################### +void button(int x, int y, int num ) +{ + + // Size of sprite + #define IWIDTH 40 + #define IHEIGHT 25 + + // Create a 16 bit sprite 40 pixels wide, 25 high (2000 bytes of RAM needed) + spr.setColorDepth(16); + spr.createSprite(IWIDTH, IHEIGHT); + + // Fill it with black (this will be the transparent colour this time) + spr.fillSprite(TFT_BLACK); + + // Draw a background for the numbers + spr.fillRoundRect( 0, 0, IWIDTH, IHEIGHT, 8, TFT_RED); + spr.drawRoundRect( 0, 0, IWIDTH, IHEIGHT, 8, TFT_WHITE); + + // Set the font parameters + + // Set text coordinate datum to middle centre + spr.setTextDatum(MC_DATUM); + + // Set the font colour and the background colour + spr.setTextColor(TFT_WHITE, TFT_RED); + + // Draw the number + spr.drawNumber(num, IWIDTH/2, 1 + IHEIGHT/2); + + // 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" + spr.pushSprite(x, y, TFT_BLACK); + + // Delete sprite to free up the RAM + spr.deleteSprite(); +} diff --git a/examples/Smooth Fonts/Font_Demo_3/Notes.ino b/examples/Smooth Fonts/Font_Demo_3/Notes.ino new file mode 100644 index 0000000..bdab3d0 --- /dev/null +++ b/examples/Smooth Fonts/Font_Demo_3/Notes.ino @@ -0,0 +1,61 @@ +/* + +Information notes only: +====================== + +Note: it is best to use drawNumber() and drawFloat() for numeric values + this reduces digit position movement when the value changes + drawNumber() and drawFloat() functions behave like drawString() and are + supported by setTextDatum() and setTextPadding() + +//These are the text plotting alignment (reference datum point) + +TL_DATUM = Top left (default) +TC_DATUM = Top centre +TR_DATUM = Top right + +ML_DATUM = Middle left +MC_DATUM = Middle centre +MR_DATUM = Middle right + +BL_DATUM = Bottom left +BC_DATUM = Bottom centre +BR_DATUM = Bottom right + +L_BASELINE = Left character baseline (Line the 'A' character would sit on) +C_BASELINE = Centre character baseline +R_BASELINE = Right character baseline + +// Basic colours already defined: + +TFT_BLACK 0x0000 +TFT_NAVY 0x000F +TFT_DARKGREEN 0x03E0 +TFT_DARKCYAN 0x03EF +TFT_MAROON 0x7800 +TFT_PURPLE 0x780F +TFT_OLIVE 0x7BE0 +TFT_LIGHTGREY 0xC618 +TFT_DARKGREY 0x7BEF +TFT_BLUE 0x001F +TFT_GREEN 0x07E0 +TFT_CYAN 0x07FF +TFT_RED 0xF800 +TFT_MAGENTA 0xF81F +TFT_YELLOW 0xFFE0 +TFT_WHITE 0xFFFF +TFT_ORANGE 0xFDA0 +TFT_GREENYELLOW 0xB7E0 +TFT_PINK 0xFC9F + + + + + + + + + + + + */ diff --git a/examples/Smooth Fonts/Font_Demo_3/data/NotoSansBold15.vlw b/examples/Smooth Fonts/Font_Demo_3/data/NotoSansBold15.vlw new file mode 100644 index 0000000000000000000000000000000000000000..803a1bd9748c922e455e4952f08368ce634f96f4 GIT binary patch literal 10766 zcmcgx4@hHOcE6jZsh!Q*iin7aTWhUrT|2F{*4os%wXU_UO|!MBwRSqq)=urzZ|d4w zYo{^{f($aqAi^Lrh)6)fLnMe01`!bv1{p*|1d$*?LO>*h5W=IUzu$d%vsu@1zpsTJ zeea%k&;4`mx#!=#QA#~hN_`#AFW@1#@i_7PQXbB4wy~iK$j{<&;rTf{q?^lhwZ{R7w^YeH(mkqxHgx{nW;>qtG#_?+FkQC-!ta# z+%sm&J+$8ijrwQHG=Gm;QP1_>Mf*L}v#l*pj$wlb2=~mUv9#g$fqbrC8)%d#?Zmbf zpc?)FZ|b}KiNxweB{%njeL@xzxF@P)v?~D0oCBen{92K$nW=OsOLO)_4DQW-L;1A=lXC= zdmn&m_;bANwX-jA8~T87uDfLY7pP}j(rh|V4Su{yqrCa#kNKCm@4Nc-=h1HS@>e+; z>u=KrP)E7Y{`B6WwD;k!fph-5bYc+oxBH<=!{6X-@4@Z)7~kl3sS_^fk)X8UZ-LO) zk`F$VpAC=uE6$roY;^p5^&pGlRd}svYKD)1q8y>2a~%S*k)IwQ4w1H|oSxXJ;y$QsE61 zx>9a8{{j6$M^}(jsI_agk|Lrm40W&x#tcI)3^Qu97DPVE;h`Kx5SNFfazN^oY7gvY z13PA5Pz9;zfCOYXVzUFdHQ$aX{u8KZ>MhDWBmRkq`vj5gw3?_i*~n|lMlghWhKe%94$I=DqD>ki@IVF24qJ82lnFU&EEe znd0TZO&&d=Xyl1i(U6rJQ-<_MK!WO-tZ2MX_TAe;dxJ!eB_$hrajNb_kQ61Hv{dRmGJ+GgQg7q}Goiq7cSu`T%6dAC+~+5SJU=5kjHV z&DmLagUZljbgJUSnM1u0FnDRYRh3{VURj2k$lKhj8;q(VIYZ|UQbozVAet&ws@z}O zXmHODGa&e60qmz!;N1}H`vRpt1b(s3$4V^;_Gm}yRgoE0YD^}S`|7+vRr=8t;Ds&| zHj{Xjw>g4`s=i@BQqfpk7V91_i$nrYsz~DW46F~!ivf9!mflh|%mu31q_x6%LFsV0 z7R2px`Kd4>$;SElV_BG=MLSK;D;vDvSG_eF9cI?mf~7x7wqUSi(l?VxB)qrn3vHdD zHN5*|74I$Sg#MZ_pkp$F_YrmPp&S66qf{M`sEVxVF}y79(-?p?0dDD_gHZt+rQ zR=kc~ndz1^CH`?hj+N??Oh8~89U_C!J=H0uS0EXmTosy91K9@EJGl%CtiI5|3NaOT znQKvHD@L7ai;H+hOjw*J9Bsh6s04u>+wvverPJHdP`gsK7y|&V!*45H5$Y8u7!|rJ zz#%q~@LJ9aSWZc-g5{)K=yF_+O8DIkvxY>Iu0cJXV2gT#M<=0f)nJMVtM;m)PKRq> zi&Uv)c?GlSf0aEf#>^s7u!8UBajkjW{8SnFcrEe4v=Q7DdEq(7$XAo7bh#hkO{h00 zbg+DsOJ}Ar6DyizRwD(c7ZnU)=rn>e#elzb@gObd=H{~6yGgS_!kWmA& zjnD*9*GaYldIRMONo*}8Faq~7#J-^I#i3PZSTDseoQs-Q5ofgruq0cWTL-+OMh4_m zm)QAXjdqUYKC$gdXqKGB$D8vVP4H$oOu1sMY z0P(VJxv4GKl(!S)qerfXDUUvpW|0MGlF-N<=GO`~_rW zD^x?fa%H908Aoh$+B$|#w*s*GTsuy1%gwL~P_5s!L3+O0proc%?V7|V3*iN1g1c0? zW@Z<4T&bK*$>C$Re=vH^_JmHjH^Qv#?%@73v;;N0^U?T{$yk}*azyCMQH@paO3cEV z*5%5=YWL*=iyk_Z%klEpNjV&3kTKwT!H2cAs%B;@7JdZhXJ$l(8wZ_g+!Ek~4n1R% zY@ic3uScJ2Q@elxDmIqKixY$yogb8^P5tTCEl}SuwHlo7U<>75mDu{)sk{fxv6~!Z zoiByxG%DRC3$g3>!y`zDF1H=W$>)z`#c94Pm$5#?R+lWPWUx#80qMX*3o5hRB1ko! zf}y1*8E|HU@V3cJM6!DhwPdz9dIqYMuMYw#IZwanC~WpEX_YQ|lcpveTKcUV54)Md zXz+x?lR0!5mZ{^9a;CFu3~_*d+3;HeQS0)=Z88MFXr&w?8v;5c@jxI%g;d*|15mEu zipBzgz!~y@F^PhtX5~J6P7taLL@685ncdFFCI?o#^@ zEWu$)uSVcpti3`+>C`pqCYQv(pf6#Hj%rmNm+ax>Tjy=kvw;A zBde|lcM0f4f&0_2^TvoCb6PG-hJmKyTtHws_jbmENwabh{Bb=grkF(X~V!4NJH zI}LZnV~sdqFFnL@YEmjLFNx1F-=3B0T4an#d5$dLLd@-5Q%GqqxH?DOq@z}( zQgd=HiBZPpx^rayj7Y9$F)Oc7?gLq_XKV_|vRAIx;YB(dkYfwBz~01hIwU|VEJuUC z&g0pW8IOlD2=mxV_`e|I27`SA(rN<-3+?J$UmXkSxPl{;7>WgZ zsAIl+Mni$+Tc`X5qm5tiIdACtmAVq2N7C^5t%79bk~T2QtB2RLm@|g+7Ky8RNur60 zif0NI3z(IcyS!K6iD}2YCQ&I!TSqTCIDINuHkwiWPS5$uXbZ^{PM|^$=*UXLFi)|_ zOD}o4N~&6(Lsn&9s*&V?w}dpQtJE;Eq7dB`JXMW^T0u$i$_aNQ0eR*~U)S=kN!7^Z ziZsI2P1wsi#AqYJI}yMtVg=9M!Xr3spCckAxx#zEz)G2a_ZP>Rqddkr8f@K^x*;17 zHe~e`ImKxpDLg_E-UxLRP=UHDoJD!wpL?%Qse=-8S7qR3N)q%^Q`*_{wB$O+PzK{{ zone&qNgC=D;@Ra8nMu8v;LRhMGD8OwjJG9eWJ>To;2TCsSe68x1YnS1MZ-NFM&0;pNd~lvS&dyr`l(OWQw@7x?DPAZUKlkP_?bQ%mgrj&NV*eMJ}pGYb(RZ#qm$xhB%6R^ z+-@i}p(S5tUXB>X>UhLVV&0$n)OfN-7_8 z-{}#Wp5nfyA8D7DaYUV-s#*`Vy~^XZPEfVTb^s-%fdEuvRQ8ebw?}7?KXiM$f%hRu zFtI7w3V1iIgw4tOn(kc^k~Jc)x^dR@Q~RGt63Oi1ToQC>SiJiX3v)fdbgE;piUqD3 z$wq$JU)I~g??qfDtfNd%VUq$8?qo>TbbG}32s4#&2}gMN*pror$o3wx&`7p7CfRX4 zrW@=68ofR6C1rGqX0bYuvynGTFfdvjudA{wK9$u>GZj|8E2&TzYbf17612l6IhRAU zf3SjuQb{ZL=8(eP4j7?Vd2Vo=I06!tvr#xLZzE^J-LTp#GFjU8~90d?z z^d;SwDpV2VQ0*zU~)sVvs zYqT72^REUw3Yr4LM6YR&f)4koHMaO{7vfsjL!SO1ctZRoO}c=h&`C%`tID~oXFut(R&Kehv7F0#Z@DZO zMQcQ6;Ig=b1Bxz7OG$5=wda=1p*elBE3|=sX;Kf;wq4P` zRN+)O8@27~$6Xlb`Bd{}@gc-t*jp9JHpS&pL{h0R*!=({kLX0iV)ZBANML$9^)S0A(B9Gmrv{(nso3H#G7qxi@$|hpIVw14swk`T;HlMI2JvHnBQ1#1K^gbsvI?mxyn9j_3Qf~J zF5}{DVv4sX-f*H=UXRnvr(`-6RA*Zu?q@U3z$0oUd69Ktj${_0<*`^S;6&nJ#yAEq z<$-TNC=2rHxX%B94%w1N|^b1>a2@kgZIK&eafAZ4~Ud)a)TWx zzS8IFb)CmQ5!p11E(O-{3btzs=9D{SPR~G)({i literal 0 HcmV?d00001 diff --git a/examples/Smooth Fonts/Font_Demo_3/data/NotoSansBold36.vlw b/examples/Smooth Fonts/Font_Demo_3/data/NotoSansBold36.vlw new file mode 100644 index 0000000000000000000000000000000000000000..66003f6e1c9915e71f571ade912b9eb899323db6 GIT binary patch literal 44169 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 K_&W^#+W!ZD7G}`^ literal 0 HcmV?d00001 diff --git a/examples/Smooth Fonts/Font_Demo_3/data/NotoSansMonoSCB20.vlw b/examples/Smooth Fonts/Font_Demo_3/data/NotoSansMonoSCB20.vlw new file mode 100644 index 0000000000000000000000000000000000000000..4045c621230f1d184991a5a74bf14d0785e2b913 GIT binary patch literal 15382 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 + +#include +#include // Hardware-specific library + +TFT_eSPI tft = TFT_eSPI(); +TFT_eSprite spr = TFT_eSprite(&tft); // Sprite class needs to be invoked + +void setup(void) { + + Serial.begin(250000); + + tft.begin(); + + tft.setRotation(1); + + spr.setColorDepth(16); // 16 bit colour needed to show antialiased fonts + + if (!SPIFFS.begin()) { + Serial.println("SPIFFS initialisation failed!"); + while (1) yield(); // Stay here twiddling thumbs waiting + } + Serial.println("\r\nSPIFFS available!"); +} + +void loop() { + + tft.fillScreen(TFT_BLACK); + + tft.setTextColor(TFT_WHITE, TFT_BLACK); // Set the font colour and the background colour + + tft.setTextDatum(TC_DATUM); // Top Centre datum + + int xpos = tft.width() / 2; // Half the screen width + int ypos = 50; + + + // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + // Small font + // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + spr.loadFont(AA_FONT_SMALL); // Must load the font first into the sprite class + + spr.setTextColor(TFT_YELLOW, TFT_BLACK); // Set the sprite font colour and the background colour + + tft.setCursor(xpos - 50, ypos); // Set the tft cursor position, yes tft position! + spr.printToSprite("Small 15pt font"); // Prints to tft cursor position, tft cursor NOT moved + + ypos += spr.fontHeight(); // Get the font height and move ypos down + + spr.unloadFont(); // Remove the font from sprite class to recover memory used + + delay(4000); + + // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + // Large font + // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + tft.fillScreen(TFT_BLACK); + + spr.loadFont(AA_FONT_LARGE); // Load another different font + + spr.setTextColor(TFT_WHITE, TFT_BLUE); // Set the font colour and the background colour + + tft.setCursor(xpos - 90, ypos); // Set the tft cursor position + spr.printToSprite("36pt font"); // Text is rendered via a minimally sized sprite + + ypos += spr.fontHeight(); // Get the font height and move ypos down + + // Draw changing numbers - no flicker using this plot method! + for (int i = 0; i <= 200; i++) { + tft.setCursor(10, 10); + // Number is converted to String type by (String) (number) + spr.printToSprite(" " + (String) (i / 100.0) + " "); // Space padding helps over-write old numbers + delay (20); + } + + spr.unloadFont(); // Remove the font to recover memory used + + delay(8000); +} diff --git a/examples/Smooth Fonts/Font_Demo_4/Notes.ino b/examples/Smooth Fonts/Font_Demo_4/Notes.ino new file mode 100644 index 0000000..f04f2b7 --- /dev/null +++ b/examples/Smooth Fonts/Font_Demo_4/Notes.ino @@ -0,0 +1,56 @@ +/* + +Information notes only: +====================== + +//These are the text plotting alignment (reference datum point) + +TL_DATUM = Top left (default) +TC_DATUM = Top centre +TR_DATUM = Top right + +ML_DATUM = Middle left +MC_DATUM = Middle centre +MR_DATUM = Middle right + +BL_DATUM = Bottom left +BC_DATUM = Bottom centre +BR_DATUM = Bottom right + +L_BASELINE = Left character baseline (Line the 'A' character would sit on) +C_BASELINE = Centre character baseline +R_BASELINE = Right character baseline + +// Basic colours already defined: + +TFT_BLACK 0x0000 +TFT_NAVY 0x000F +TFT_DARKGREEN 0x03E0 +TFT_DARKCYAN 0x03EF +TFT_MAROON 0x7800 +TFT_PURPLE 0x780F +TFT_OLIVE 0x7BE0 +TFT_LIGHTGREY 0xC618 +TFT_DARKGREY 0x7BEF +TFT_BLUE 0x001F +TFT_GREEN 0x07E0 +TFT_CYAN 0x07FF +TFT_RED 0xF800 +TFT_MAGENTA 0xF81F +TFT_YELLOW 0xFFE0 +TFT_WHITE 0xFFFF +TFT_ORANGE 0xFDA0 +TFT_GREENYELLOW 0xB7E0 +TFT_PINK 0xFC9F + + + + + + + + + + + + */ diff --git a/examples/Smooth Fonts/Font_Demo_4/data/NotoSansBold15.vlw b/examples/Smooth Fonts/Font_Demo_4/data/NotoSansBold15.vlw new file mode 100644 index 0000000000000000000000000000000000000000..803a1bd9748c922e455e4952f08368ce634f96f4 GIT binary patch literal 10766 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 literal 0 HcmV?d00001 diff --git a/examples/Smooth Fonts/Font_Demo_4/data/NotoSansBold36.vlw b/examples/Smooth Fonts/Font_Demo_4/data/NotoSansBold36.vlw new file mode 100644 index 0000000000000000000000000000000000000000..66003f6e1c9915e71f571ade912b9eb899323db6 GIT binary patch literal 44169 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 K_&W^#+W!ZD7G}`^ literal 0 HcmV?d00001 diff --git a/keywords.txt b/keywords.txt index 53b0825..b197c5a 100644 --- a/keywords.txt +++ b/keywords.txt @@ -36,6 +36,7 @@ getCursorY KEYWORD2 setTextColor KEYWORD2 setTextSize KEYWORD2 setTextFont KEYWORD2 +setFreeFont KEYWORD2 setTextWrap KEYWORD2 setTextDatum KEYWORD2 setTextPadding KEYWORD2 diff --git a/library.json b/library.json index 15bfb8b..ceaa20f 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "TFT_eSPI", - "version": "1.0.2", + "version": "1.1.0", "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 dd9055a..8e9b396 100644 --- a/library.properties +++ b/library.properties @@ -1,9 +1,9 @@ name=TFT_eSPI -version=1.0.2 +version=1.1.0 author=Bodmer maintainer=Bodmer -sentence=A fast TFT library for ESP8266 and ESP32 processors for the Arduino IDE -paragraph=Supports TFT displays using drivers (ILI9341 etc) that operate with hardware SPI. +sentence=A fast TFT graphics library for ESP8266 and ESP32 processors for the Arduino IDE +paragraph=Supports TFT displays using drivers (ILI9341 etc) that operate with hardware SPI or 8 bit parallel. category=Display url=https://github.com/Bodmer/TFT_eSPI architectures=esp8266,esp32 From 9666314eb6bf4831070405d89aee0611b85fec29 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Sun, 21 Oct 2018 21:22:20 +0100 Subject: [PATCH 182/287] Support for ESP32 PSRAM added If PSRAM is fitted and enabled the Sprites are now created in PSRAM. This makes multiple full screen buffers possible! --- Extensions/Sprite.cpp | 16 +++++++++++++++- README.md | 2 ++ library.json | 2 +- library.properties | 2 +- 4 files changed, 19 insertions(+), 3 deletions(-) diff --git a/Extensions/Sprite.cpp b/Extensions/Sprite.cpp index acfc315..73285da 100644 --- a/Extensions/Sprite.cpp +++ b/Extensions/Sprite.cpp @@ -67,7 +67,13 @@ void* TFT_eSprite::createSprite(int16_t w, int16_t h, uint8_t frames) // hence will run faster in normal circumstances. if (_bpp == 16) { + +#if defined (ESP32) && defined (CONFIG_SPIRAM_SUPPORT) + if ( psramFound() ) _img8_1 = ( uint8_t*) ps_calloc(w * h + 1, sizeof(uint16_t)); + else +#endif _img8_1 = ( uint8_t*) calloc(w * h + 1, sizeof(uint16_t)); + _img8_2 = _img8_1; _img = (uint16_t*) _img8_1; @@ -80,6 +86,10 @@ void* TFT_eSprite::createSprite(int16_t w, int16_t h, uint8_t frames) else if (_bpp == 8) { +#if defined (ESP32) && defined (CONFIG_SPIRAM_SUPPORT) + if ( psramFound() ) _img8_1 = ( uint8_t*) ps_calloc(w * h + 1, sizeof(uint8_t)); + else +#endif _img8_1 = ( uint8_t*) calloc(w * h + 1, sizeof(uint8_t)); if (_img8_1) @@ -103,7 +113,11 @@ void* TFT_eSprite::createSprite(int16_t w, int16_t h, uint8_t frames) 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 defined (ESP32) && defined (CONFIG_SPIRAM_SUPPORT) + if ( psramFound() ) _img8 = ( uint8_t*) ps_calloc(frames * (w>>3) * h + frames, sizeof(uint8_t)); + else +#endif + _img8 = ( uint8_t*) calloc(frames * (w>>3) * h + frames, sizeof(uint8_t)); if (_img8) { diff --git a/README.md b/README.md index bb2dfd5..b61fc76 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,8 @@ Drawing graphics into a sprite is very fast, for those familiar with the Adafrui Sprites can be plotted to the TFT with one colour being specified as "transparent", see Transparent_Sprite_Demo example. +IF an ESP32 board with SPIRAM (i.e. PSRAM) fitted then Sprites will use the PSRAM memory and large full screen buffer Sprites can be created. Full screen Sprites take longer to render (~45ms for a 320 x 240 16 bit Sprite), so bear that in mind. + 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. diff --git a/library.json b/library.json index ceaa20f..81f4046 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "TFT_eSPI", - "version": "1.1.0", + "version": "1.1.1", "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 8e9b396..6b18d6b 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=TFT_eSPI -version=1.1.0 +version=1.1.1 author=Bodmer maintainer=Bodmer sentence=A fast TFT graphics library for ESP8266 and ESP32 processors for the Arduino IDE From 2ba492c1d30bc66f9acd53fa34907c7e0bf72735 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Sun, 21 Oct 2018 21:23:40 +0100 Subject: [PATCH 183/287] Correct typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b61fc76..4f91bca 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ Drawing graphics into a sprite is very fast, for those familiar with the Adafrui Sprites can be plotted to the TFT with one colour being specified as "transparent", see Transparent_Sprite_Demo example. -IF an ESP32 board with SPIRAM (i.e. PSRAM) fitted then Sprites will use the PSRAM memory and large full screen buffer Sprites can be created. Full screen Sprites take longer to render (~45ms for a 320 x 240 16 bit Sprite), so bear that in mind. +If an ESP32 board has SPIRAM (i.e. PSRAM) fitted then Sprites will use the PSRAM memory and large full screen buffer Sprites can be created. Full screen Sprites take longer to render (~45ms for a 320 x 240 16 bit Sprite), so bear that in mind. 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 d12a68a49dcff9baf75ed23636f374c69a6c10c4 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Sun, 21 Oct 2018 21:57:30 +0100 Subject: [PATCH 184/287] Add checks in examples for missing font files --- examples/Smooth Fonts/Font_Demo_1/Font_Demo_1.ino | 12 ++++++++++++ examples/Smooth Fonts/Font_Demo_2/Font_Demo_2.ino | 12 ++++++++++++ examples/Smooth Fonts/Font_Demo_3/Font_Demo_3.ino | 13 +++++++++++++ examples/Smooth Fonts/Font_Demo_4/Font_Demo_4.ino | 12 ++++++++++++ 4 files changed, 49 insertions(+) diff --git a/examples/Smooth Fonts/Font_Demo_1/Font_Demo_1.ino b/examples/Smooth Fonts/Font_Demo_1/Font_Demo_1.ino index 0d24af9..e38a5ee 100644 --- a/examples/Smooth Fonts/Font_Demo_1/Font_Demo_1.ino +++ b/examples/Smooth Fonts/Font_Demo_1/Font_Demo_1.ino @@ -59,6 +59,18 @@ void setup(void) { while (1) yield(); // Stay here twiddling thumbs waiting } Serial.println("\r\nSPIFFS available!"); + + // ESP32 will crash if any of the fonts are missing + bool font_missing = false; + if (SPIFFS.exists("/NotoSansBold15.vlw") == false) font_missing = true; + if (SPIFFS.exists("/NotoSansBold36.vlw") == false) font_missing = true; + + if (font_missing) + { + Serial.println("\r\nFont missing in SPIFFS, did you upload it?"); + while(1) yield(); + } + else Serial.println("\r\nFonts found OK."); } diff --git a/examples/Smooth Fonts/Font_Demo_2/Font_Demo_2.ino b/examples/Smooth Fonts/Font_Demo_2/Font_Demo_2.ino index 7eca0be..a2ef436 100644 --- a/examples/Smooth Fonts/Font_Demo_2/Font_Demo_2.ino +++ b/examples/Smooth Fonts/Font_Demo_2/Font_Demo_2.ino @@ -53,6 +53,18 @@ void setup(void) { while (1) yield(); // Stay here twiddling thumbs waiting } Serial.println("\r\nSPIFFS available!"); + + // ESP32 will crash if any of the fonts are missing + bool font_missing = false; + if (SPIFFS.exists("/NotoSansBold15.vlw") == false) font_missing = true; + if (SPIFFS.exists("/NotoSansBold36.vlw") == false) font_missing = true; + + if (font_missing) + { + Serial.println("\r\nFont missing in SPIFFS, did you upload it?"); + while(1) yield(); + } + else Serial.println("\r\nFonts found OK."); } void loop() { diff --git a/examples/Smooth Fonts/Font_Demo_3/Font_Demo_3.ino b/examples/Smooth Fonts/Font_Demo_3/Font_Demo_3.ino index 5214264..ebc2055 100644 --- a/examples/Smooth Fonts/Font_Demo_3/Font_Demo_3.ino +++ b/examples/Smooth Fonts/Font_Demo_3/Font_Demo_3.ino @@ -57,6 +57,19 @@ void setup(void) { while (1) yield(); // Stay here twiddling thumbs waiting } Serial.println("\r\nSPIFFS available!"); + + // ESP32 will crash if any of the fonts are missing + bool font_missing = false; + if (SPIFFS.exists("/NotoSansBold15.vlw") == false) font_missing = true; + if (SPIFFS.exists("/NotoSansBold36.vlw") == false) font_missing = true; + if (SPIFFS.exists("/NotoSansMonoSCB20.vlw") == false) font_missing = true; + + if (font_missing) + { + Serial.println("\r\nFont missing in SPIFFS, did you upload it?"); + while(1) yield(); + } + else Serial.println("\r\nFonts found OK."); } void loop() { diff --git a/examples/Smooth Fonts/Font_Demo_4/Font_Demo_4.ino b/examples/Smooth Fonts/Font_Demo_4/Font_Demo_4.ino index f8ce4c1..edad83c 100644 --- a/examples/Smooth Fonts/Font_Demo_4/Font_Demo_4.ino +++ b/examples/Smooth Fonts/Font_Demo_4/Font_Demo_4.ino @@ -68,6 +68,18 @@ void setup(void) { while (1) yield(); // Stay here twiddling thumbs waiting } Serial.println("\r\nSPIFFS available!"); + + // ESP32 will crash if any of the fonts are missing + bool font_missing = false; + if (SPIFFS.exists("/NotoSansBold15.vlw") == false) font_missing = true; + if (SPIFFS.exists("/NotoSansBold36.vlw") == false) font_missing = true; + + if (font_missing) + { + Serial.println("\r\nFont missing in SPIFFS, did you upload it?"); + while(1) yield(); + } + else Serial.println("\r\nFonts found OK."); } void loop() { From b93a40a54fd8a530cc31abf60b6f8696ba1d1fb5 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Tue, 23 Oct 2018 14:20:45 +0100 Subject: [PATCH 185/287] Correct datum marker for different screen sizes datum marker was not in correct position on screens that are not 320x240 pixels --- .../Smooth Fonts/Font_Demo_2/Font_Demo_2.ino | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/examples/Smooth Fonts/Font_Demo_2/Font_Demo_2.ino b/examples/Smooth Fonts/Font_Demo_2/Font_Demo_2.ino index a2ef436..6dbf769 100644 --- a/examples/Smooth Fonts/Font_Demo_2/Font_Demo_2.ino +++ b/examples/Smooth Fonts/Font_Demo_2/Font_Demo_2.ino @@ -148,73 +148,73 @@ void loop() { tft.loadFont(AA_FONT_SMALL); tft.setTextDatum(TL_DATUM); tft.drawString("[Top left]", xpos, ypos); - drawDatumMarker(160,120); + drawDatumMarker(xpos, ypos); delay(1000); tft.fillScreen(TFT_BLACK); tft.setTextDatum(TC_DATUM); tft.drawString("[Top centre]", xpos, ypos); - drawDatumMarker(160,120); + drawDatumMarker(xpos, ypos); delay(1000); tft.fillScreen(TFT_BLACK); tft.setTextDatum(TR_DATUM); tft.drawString("[Top right]", xpos, ypos); - drawDatumMarker(160,120); + drawDatumMarker(xpos, ypos); delay(1000); tft.fillScreen(TFT_BLACK); tft.setTextDatum(ML_DATUM); tft.drawString("[Middle left]", xpos, ypos); - drawDatumMarker(160,120); + drawDatumMarker(xpos, ypos); delay(1000); tft.fillScreen(TFT_BLACK); tft.setTextDatum(MC_DATUM); tft.drawString("[Middle centre]", xpos, ypos); - drawDatumMarker(160,120); + drawDatumMarker(xpos, ypos); delay(1000); tft.fillScreen(TFT_BLACK); tft.setTextDatum(MR_DATUM); tft.drawString("[Middle right]", xpos, ypos); - drawDatumMarker(160,120); + drawDatumMarker(xpos, ypos); delay(1000); tft.fillScreen(TFT_BLACK); tft.setTextDatum(BL_DATUM); tft.drawString("[Bottom left]", xpos, ypos); - drawDatumMarker(160,120); + drawDatumMarker(xpos, ypos); delay(1000); tft.fillScreen(TFT_BLACK); tft.setTextDatum(BC_DATUM); tft.drawString("[Bottom centre]", xpos, ypos); - drawDatumMarker(160,120); + drawDatumMarker(xpos, ypos); delay(1000); tft.fillScreen(TFT_BLACK); tft.setTextDatum(BR_DATUM); tft.drawString("[Bottom right]", xpos, ypos); - drawDatumMarker(160,120); + drawDatumMarker(xpos, ypos); delay(1000); tft.fillScreen(TFT_BLACK); tft.setTextDatum(L_BASELINE); tft.drawString("[Left baseline]", xpos, ypos); - drawDatumMarker(160,120); + drawDatumMarker(xpos, ypos); delay(1000); tft.fillScreen(TFT_BLACK); tft.setTextDatum(C_BASELINE); tft.drawString("[Centre baseline]", xpos, ypos); - drawDatumMarker(160,120); + drawDatumMarker(xpos, ypos); delay(1000); tft.fillScreen(TFT_BLACK); tft.setTextDatum(R_BASELINE); tft.drawString("[Right baseline]", xpos, ypos); - drawDatumMarker(160,120); + drawDatumMarker(xpos, ypos); delay(1000); tft.unloadFont(); // Remove the font to recover memory used From 1e1525010e4469cabd92e93272253be8ae4cc140 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Wed, 24 Oct 2018 09:58:20 +0100 Subject: [PATCH 186/287] Fix issue #225 --- TFT_eSPI.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/TFT_eSPI.cpp b/TFT_eSPI.cpp index aeb0766..afaedd1 100644 --- a/TFT_eSPI.cpp +++ b/TFT_eSPI.cpp @@ -4160,7 +4160,11 @@ int16_t TFT_eSPI::drawString(const char *string, int poX, int poY, int font) uint16_t cheight = 8 * textsize; #ifdef LOAD_GFXFF - bool freeFont = (font == 1 && gfxFont && !fontLoaded); + #ifdef SMOOTH_FONT + bool freeFont = (font == 1 && gfxFont && !fontLoaded); + #else + bool freeFont = (font == 1 && gfxFont); + #endif if (freeFont) { cheight = glyph_ab * textsize; From d18a5a0c8fb6d9bbcf44d9080f81d47bf9d14c4b Mon Sep 17 00:00:00 2001 From: Bodmer Date: Wed, 24 Oct 2018 09:59:26 +0100 Subject: [PATCH 187/287] Revert "Fix issue #225" This reverts commit 1e1525010e4469cabd92e93272253be8ae4cc140. --- TFT_eSPI.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/TFT_eSPI.cpp b/TFT_eSPI.cpp index afaedd1..aeb0766 100644 --- a/TFT_eSPI.cpp +++ b/TFT_eSPI.cpp @@ -4160,11 +4160,7 @@ int16_t TFT_eSPI::drawString(const char *string, int poX, int poY, int font) uint16_t cheight = 8 * textsize; #ifdef LOAD_GFXFF - #ifdef SMOOTH_FONT - bool freeFont = (font == 1 && gfxFont && !fontLoaded); - #else - bool freeFont = (font == 1 && gfxFont); - #endif + bool freeFont = (font == 1 && gfxFont && !fontLoaded); if (freeFont) { cheight = glyph_ab * textsize; From 3d5ff73690edb48f29c1a46cc88bd4a41e0b4e33 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Wed, 24 Oct 2018 10:00:33 +0100 Subject: [PATCH 188/287] Fix issue #224 --- TFT_eSPI.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/TFT_eSPI.cpp b/TFT_eSPI.cpp index aeb0766..afaedd1 100644 --- a/TFT_eSPI.cpp +++ b/TFT_eSPI.cpp @@ -4160,7 +4160,11 @@ int16_t TFT_eSPI::drawString(const char *string, int poX, int poY, int font) uint16_t cheight = 8 * textsize; #ifdef LOAD_GFXFF - bool freeFont = (font == 1 && gfxFont && !fontLoaded); + #ifdef SMOOTH_FONT + bool freeFont = (font == 1 && gfxFont && !fontLoaded); + #else + bool freeFont = (font == 1 && gfxFont); + #endif if (freeFont) { cheight = glyph_ab * textsize; From 38d99a53dd96c8892cd7ce30ebee000e2f5abe94 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Sun, 4 Nov 2018 20:41:10 +0000 Subject: [PATCH 189/287] Issue #232 --- TFT_Drivers/ST7789_Rotation.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/TFT_Drivers/ST7789_Rotation.h b/TFT_Drivers/ST7789_Rotation.h index 34ba394..94be726 100644 --- a/TFT_Drivers/ST7789_Rotation.h +++ b/TFT_Drivers/ST7789_Rotation.h @@ -22,13 +22,13 @@ _width = _init_width; _height = _init_height; colstart = 0; - rowstart = 80; + rowstart = 0; break; case 3: // Inverted landscape writedata(TFT_MAD_MV | TFT_MAD_MY | TFT_MAD_RGB); _width = _init_height; _height = _init_width; - colstart = 80; + colstart = 0; rowstart = 0; break; } From ffef37a22d43fee55791cd1a3295e59f7130cbe6 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Sun, 4 Nov 2018 20:46:39 +0000 Subject: [PATCH 190/287] Revert "Issue #232" This reverts commit 38d99a53dd96c8892cd7ce30ebee000e2f5abe94. --- TFT_Drivers/ST7789_Rotation.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/TFT_Drivers/ST7789_Rotation.h b/TFT_Drivers/ST7789_Rotation.h index 94be726..34ba394 100644 --- a/TFT_Drivers/ST7789_Rotation.h +++ b/TFT_Drivers/ST7789_Rotation.h @@ -22,13 +22,13 @@ _width = _init_width; _height = _init_height; colstart = 0; - rowstart = 0; + rowstart = 80; break; case 3: // Inverted landscape writedata(TFT_MAD_MV | TFT_MAD_MY | TFT_MAD_RGB); _width = _init_height; _height = _init_width; - colstart = 0; + colstart = 80; rowstart = 0; break; } From d5f582cbbb46ed446e62cb09d3de1c557ac0ef61 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Mon, 5 Nov 2018 22:34:15 +0000 Subject: [PATCH 191/287] Version not raised for #224 change --- library.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library.properties b/library.properties index 6b18d6b..b8383c8 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=TFT_eSPI -version=1.1.1 +version=1.1.2 author=Bodmer maintainer=Bodmer sentence=A fast TFT graphics library for ESP8266 and ESP32 processors for the Arduino IDE From 8d29c396e3568e95a9c961489c8b233853ef9924 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Mon, 5 Nov 2018 22:34:45 +0000 Subject: [PATCH 192/287] Version not raised for #224 change --- library.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library.json b/library.json index 81f4046..350069f 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "TFT_eSPI", - "version": "1.1.1", + "version": "1.1.2", "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": From cbc2e66dfd43207267909945be13de33bbe6fe81 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Tue, 6 Nov 2018 00:41:14 +0000 Subject: [PATCH 193/287] Update for WROVER to address #232 The library supported 240x240 ST7789 displays only which require an 80 pixel offset in rotation 2 and 3 The library now also supports the 240x320 display which has a different RGB colour order. --- TFT_Drivers/ST7789_Defines.h | 4 +- TFT_Drivers/ST7789_Init.h | 9 +++- TFT_Drivers/ST7789_Rotation.h | 26 ++++++---- User_Setup.h | 23 ++++++--- User_Setups/SetupX_Template.h | 91 ++++++++++++++++++++++++++--------- library.json | 2 +- library.properties | 2 +- 7 files changed, 112 insertions(+), 45 deletions(-) diff --git a/TFT_Drivers/ST7789_Defines.h b/TFT_Drivers/ST7789_Defines.h index 5a0d1be..06f2a39 100644 --- a/TFT_Drivers/ST7789_Defines.h +++ b/TFT_Drivers/ST7789_Defines.h @@ -7,7 +7,9 @@ #define TFT_HEIGHT 320 #endif -#define CGRAM_OFFSET +#if (TFT_HEIGHT == 240) && (TFT_WIDTH == 240) + #define CGRAM_OFFSET +#endif // Delay between some initialisation commands #define TFT_INIT_DELAY 0x80 // Not used unless commandlist invoked diff --git a/TFT_Drivers/ST7789_Init.h b/TFT_Drivers/ST7789_Init.h index 9b69cb4..4afd2cc 100644 --- a/TFT_Drivers/ST7789_Init.h +++ b/TFT_Drivers/ST7789_Init.h @@ -14,7 +14,12 @@ //------------------------------display and color format setting--------------------------------// writecommand(ST7789_MADCTL); writedata(0x00); - writedata(0x48); +#ifdef CGRAM_OFFSET + writedata(0x48); // BGR colour order for 240 x 240 TFT +#else + writedata(0x40); // RGB colour order for 240 x 320 TFT (Issue #232) +#endif + // JLX240 display datasheet writecommand(0xB6); @@ -114,7 +119,9 @@ writecommand(ST7789_DISPON); //Display on +#ifdef TFT_BL // Turn on the back-light LED digitalWrite(TFT_BL, HIGH); pinMode(TFT_BL, OUTPUT); +#endif } diff --git a/TFT_Drivers/ST7789_Rotation.h b/TFT_Drivers/ST7789_Rotation.h index 34ba394..8c526ad 100644 --- a/TFT_Drivers/ST7789_Rotation.h +++ b/TFT_Drivers/ST7789_Rotation.h @@ -7,28 +7,36 @@ writedata(TFT_MAD_RGB); _width = _init_width; _height = _init_height; +#ifdef CGRAM_OFFSET colstart = 0; rowstart = 0; +#endif break; case 1: // Landscape (Portrait + 90) writedata(TFT_MAD_MX | TFT_MAD_MV | TFT_MAD_RGB); _width = _init_height; _height = _init_width; - colstart = 0; - rowstart = 0; - break; +#ifdef CGRAM_OFFSET + colstart = 0; + rowstart = 0; +#endif + break; case 2: // Inverter portrait writedata(TFT_MAD_MX | TFT_MAD_MY | TFT_MAD_RGB); _width = _init_width; _height = _init_height; - colstart = 0; - rowstart = 80; - break; +#ifdef CGRAM_OFFSET + colstart = 0; + rowstart = 80; +#endif + break; case 3: // Inverted landscape writedata(TFT_MAD_MV | TFT_MAD_MY | TFT_MAD_RGB); _width = _init_height; _height = _init_width; - colstart = 80; - rowstart = 0; - break; +#ifdef CGRAM_OFFSET + colstart = 80; + rowstart = 0; +#endif + break; } diff --git a/User_Setup.h b/User_Setup.h index a91a719..dd3d42a 100644 --- a/User_Setup.h +++ b/User_Setup.h @@ -24,16 +24,19 @@ //#define ILI9481_DRIVER //#define ILI9486_DRIVER //#define ILI9488_DRIVER -//#define ST7789_DRIVER +//#define ST7789_DRIVER // Define the screen size below for this display // 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 +// For ST7789, ST7735 and ILI9163 ONLY, define the pixel width and height in portrait orientation // #define TFT_WIDTH 80 // #define TFT_WIDTH 128 +// #define TFT_WIDTH 240 // ST7789 240 x 240 and 240 x 320 // #define TFT_HEIGHT 160 -//#define TFT_HEIGHT 128 +// #define TFT_HEIGHT 128 +// #define TFT_HEIGHT 240 // ST7789 240 x 240 +// #define TFT_HEIGHT 320 // ST7789 240 x 320 // 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 @@ -46,10 +49,10 @@ // #define ST7735_GREENTAB // #define ST7735_GREENTAB2 // #define ST7735_GREENTAB3 -// #define ST7735_GREENTAB128 // For 128 x 128 display +// #define ST7735_GREENTAB128 // For 128 x 128 display // #define ST7735_GREENTAB160x80 // For 160 x 80 display (BGR, inverted, 26 offset) // #define ST7735_REDTAB -//#define ST7735_BLACKTAB +// #define ST7735_BLACKTAB // ################################################################################## // @@ -94,7 +97,9 @@ #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_RST -1 // Set TFT_RST to -1 if the display RESET is connected to NodeMCU RST or 3.3V + +//#define TFT_BL PIN_D1 // LED back-light (only for ST7789 with backlight control pin) //#define TOUCH_CS PIN_D2 // Chip select pin (T_CS) of touch screen @@ -124,10 +129,12 @@ //#define TFT_MISO 19 //#define TFT_MOSI 23 //#define TFT_SCLK 18 -//#define TFT_CS 15 // Chip select control pin +//#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 TFT_BL 32 // LED back-light (only for ST7789 with backlight control pin) + // For the M5Stack module use these #define lines //#define TFT_MISO 19 @@ -136,7 +143,7 @@ //#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 TFT_BL 32 // LED back-light (required for M5Stack) //#define TOUCH_CS 21 // Chip select pin (T_CS) of touch screen diff --git a/User_Setups/SetupX_Template.h b/User_Setups/SetupX_Template.h index f321517..dd3d42a 100644 --- a/User_Setups/SetupX_Template.h +++ b/User_Setups/SetupX_Template.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,15 +20,23 @@ //#define ILI9163_DRIVER //#define S6D02A1_DRIVER //#define RPI_ILI9486_DRIVER // 20MHz maximum SPI +//#define HX8357D_DRIVER +//#define ILI9481_DRIVER +//#define ILI9486_DRIVER +//#define ILI9488_DRIVER +//#define ST7789_DRIVER // Define the screen size below for this display // 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 80 -//#define TFT_WIDTH 128 -//#define TFT_HEIGHT 160 -//#define TFT_HEIGHT 128 +// For ST7789, ST7735 and ILI9163 ONLY, define the pixel width and height in portrait orientation +// #define TFT_WIDTH 80 +// #define TFT_WIDTH 128 +// #define TFT_WIDTH 240 // ST7789 240 x 240 and 240 x 320 +// #define TFT_HEIGHT 160 +// #define TFT_HEIGHT 128 +// #define TFT_HEIGHT 240 // ST7789 240 x 240 +// #define TFT_HEIGHT 320 // ST7789 240 x 320 // 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 @@ -36,14 +45,14 @@ // 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_GREENTAB160x80 // For 160 x 80 display (BGR, inverted, 26 offset) -//#define ST7735_REDTAB -//#define ST7735_BLACKTAB +// #define ST7735_INITB +// #define ST7735_GREENTAB +// #define ST7735_GREENTAB2 +// #define ST7735_GREENTAB3 +// #define ST7735_GREENTAB128 // For 128 x 128 display +// #define ST7735_GREENTAB160x80 // For 160 x 80 display (BGR, inverted, 26 offset) +// #define ST7735_REDTAB +// #define ST7735_BLACKTAB // ################################################################################## // @@ -88,9 +97,11 @@ #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_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_BL PIN_D1 // LED back-light (only for ST7789 with backlight control pin) + +//#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 @@ -118,14 +129,12 @@ //#define TFT_MISO 19 //#define TFT_MOSI 23 //#define TFT_SCLK 18 -//#define TFT_CS 15 // Chip select control pin +//#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 TFT_BL 32 // LED back-light (only for ST7789 with backlight control pin) -//#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 @@ -134,8 +143,40 @@ //#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 TFT_BL 32 // LED back-light (required for M5Stack) +//#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 // ################################################################################## // @@ -167,9 +208,9 @@ #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_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 @@ -202,9 +243,11 @@ // #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 +// Optional reduced SPI frequency for reading TFT +#define SPI_READ_FREQUENCY 20000000 + // The XPT2046 requires a lower SPI clock rate of 2.5MHz so we define that here: #define SPI_TOUCH_FREQUENCY 2500000 @@ -219,4 +262,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 350069f..70c37a9 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "TFT_eSPI", - "version": "1.1.2", + "version": "1.1.3", "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 b8383c8..ac3cd85 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=TFT_eSPI -version=1.1.2 +version=1.1.3 author=Bodmer maintainer=Bodmer sentence=A fast TFT graphics library for ESP8266 and ESP32 processors for the Arduino IDE From d4ff3c8dce57e2269c6c5784d4ea7be73ba850d2 Mon Sep 17 00:00:00 2001 From: Gavin Smalley Date: Wed, 7 Nov 2018 14:56:27 +0000 Subject: [PATCH 194/287] Add support for red-tabbed 80x160 screen. --- TFT_Drivers/ST7735_Defines.h | 5 +++++ TFT_Drivers/ST7735_Init.h | 6 ++++++ TFT_Drivers/ST7735_Rotation.h | 16 ++++++++++++++++ User_Setup.h | 1 + 4 files changed, 28 insertions(+) diff --git a/TFT_Drivers/ST7735_Defines.h b/TFT_Drivers/ST7735_Defines.h index 35e9659..82a3c30 100644 --- a/TFT_Drivers/ST7735_Defines.h +++ b/TFT_Drivers/ST7735_Defines.h @@ -16,6 +16,7 @@ #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 INITR_GREENTAB160x80 0x6 // Use if you only get part of 128x128 screen in rotation 0 & 1 +#define INITR_REDTAB160x80 0x7 // Added for https://www.aliexpress.com/item/ShengYang-1pcs-IPS-0-96-inch-7P-SPI-HD-65K-Full-Color-OLED-Module-ST7735-Drive/32918394604.html #define INITB 0xB @@ -42,6 +43,10 @@ #elif defined (ST7735_GREENTAB160x80) #define TAB_COLOUR INITR_GREENTAB160x80 #define CGRAM_OFFSET + +#elif defined (ST7735_REDTAB160x80) + #define TAB_COLOUR INITR_REDTAB160x80 + #define CGRAM_OFFSET #elif defined (ST7735_REDTAB) #define TAB_COLOUR INITR_REDTAB diff --git a/TFT_Drivers/ST7735_Init.h b/TFT_Drivers/ST7735_Init.h index 3eef1d9..a528785 100644 --- a/TFT_Drivers/ST7735_Init.h +++ b/TFT_Drivers/ST7735_Init.h @@ -180,6 +180,12 @@ colstart = 26; rowstart = 1; } + else if (tabcolor == INITR_REDTAB160x80) + { + commandList(Rcmd2green); + colstart = 24; + rowstart = 0; + } else if (tabcolor == INITR_REDTAB) { commandList(Rcmd2red); diff --git a/TFT_Drivers/ST7735_Rotation.h b/TFT_Drivers/ST7735_Rotation.h index 89701f7..fba6419 100644 --- a/TFT_Drivers/ST7735_Rotation.h +++ b/TFT_Drivers/ST7735_Rotation.h @@ -24,6 +24,10 @@ writedata(TFT_MAD_MX | TFT_MAD_MY | TFT_MAD_MH | TFT_MAD_BGR); colstart = 26; rowstart = 1; + } else if(tabcolor == INITR_REDTAB160x80) { + writedata(TFT_MAD_MX | TFT_MAD_MY | TFT_MAD_MH | TFT_MAD_BGR); + colstart = 24; + rowstart = 0; } else if(tabcolor == INITB) { writedata(TFT_MAD_MX | TFT_MAD_RGB); } else { @@ -51,6 +55,10 @@ writedata(TFT_MAD_MV | TFT_MAD_MY | TFT_MAD_BGR); colstart = 1; rowstart = 26; + } else if(tabcolor == INITR_REDTAB160x80) { + writedata(TFT_MAD_MV | TFT_MAD_MY | TFT_MAD_BGR); + colstart = 0; + rowstart = 24; } else if(tabcolor == INITB) { writedata(TFT_MAD_MV | TFT_MAD_MX | TFT_MAD_MY | TFT_MAD_RGB); } else { @@ -78,6 +86,10 @@ writedata(TFT_MAD_BGR); colstart = 0; rowstart = 0; + } else if(tabcolor == INITR_REDTAB160x80) { + writedata(TFT_MAD_BGR); + colstart = 0; + rowstart = 0; } else if(tabcolor == INITB) { writedata(TFT_MAD_MY | TFT_MAD_RGB); } else { @@ -105,6 +117,10 @@ writedata(TFT_MAD_MX | TFT_MAD_MV | TFT_MAD_BGR); colstart = 1; rowstart = 26; + } else if(tabcolor == INITR_REDTAB160x80) { + writedata(TFT_MAD_MX | TFT_MAD_MV | TFT_MAD_BGR); + colstart = 0; + rowstart = 24; } else if(tabcolor == INITB) { writedata(TFT_MAD_MV | TFT_MAD_RGB); } else { diff --git a/User_Setup.h b/User_Setup.h index dd3d42a..9b2359c 100644 --- a/User_Setup.h +++ b/User_Setup.h @@ -53,6 +53,7 @@ // #define ST7735_GREENTAB160x80 // For 160 x 80 display (BGR, inverted, 26 offset) // #define ST7735_REDTAB // #define ST7735_BLACKTAB +// #define ST7735_REDTAB160x80 // For 160 x 80 display (24 offset) (https://www.aliexpress.com/item/ShengYang-1pcs-IPS-0-96-inch-7P-SPI-HD-65K-Full-Color-OLED-Module-ST7735-Drive/32918394604.html) // ################################################################################## // From 8d3a4d09bd77313f5f191726b6feef6f23cc658a Mon Sep 17 00:00:00 2001 From: Gavin Smalley Date: Wed, 7 Nov 2018 15:29:29 +0000 Subject: [PATCH 195/287] Correct rotation 2 for red-tabbed 80x160 screen. --- TFT_Drivers/ST7735_Rotation.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TFT_Drivers/ST7735_Rotation.h b/TFT_Drivers/ST7735_Rotation.h index fba6419..6163abb 100644 --- a/TFT_Drivers/ST7735_Rotation.h +++ b/TFT_Drivers/ST7735_Rotation.h @@ -88,7 +88,7 @@ rowstart = 0; } else if(tabcolor == INITR_REDTAB160x80) { writedata(TFT_MAD_BGR); - colstart = 0; + colstart = 24; rowstart = 0; } else if(tabcolor == INITB) { writedata(TFT_MAD_MY | TFT_MAD_RGB); From 2aeb09db38d0ff6ddd9e56c6b87d9de96130dde3 Mon Sep 17 00:00:00 2001 From: Gavin Smalley Date: Thu, 8 Nov 2018 10:32:40 +0000 Subject: [PATCH 196/287] Bump version and add option for red-tabbed 160x80 screen support to SetupX_Template.h --- User_Setups/SetupX_Template.h | 1 + library.json | 2 +- library.properties | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/User_Setups/SetupX_Template.h b/User_Setups/SetupX_Template.h index dd3d42a..9b2359c 100644 --- a/User_Setups/SetupX_Template.h +++ b/User_Setups/SetupX_Template.h @@ -53,6 +53,7 @@ // #define ST7735_GREENTAB160x80 // For 160 x 80 display (BGR, inverted, 26 offset) // #define ST7735_REDTAB // #define ST7735_BLACKTAB +// #define ST7735_REDTAB160x80 // For 160 x 80 display (24 offset) (https://www.aliexpress.com/item/ShengYang-1pcs-IPS-0-96-inch-7P-SPI-HD-65K-Full-Color-OLED-Module-ST7735-Drive/32918394604.html) // ################################################################################## // diff --git a/library.json b/library.json index 70c37a9..496bd2e 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "TFT_eSPI", - "version": "1.1.3", + "version": "1.1.4", "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 ac3cd85..fd62f0e 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=TFT_eSPI -version=1.1.3 +version=1.1.4 author=Bodmer maintainer=Bodmer sentence=A fast TFT graphics library for ESP8266 and ESP32 processors for the Arduino IDE From beb9bb3360ccd1d8ae5b4ac0429f1d11b12495c0 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Fri, 9 Nov 2018 09:17:13 +0000 Subject: [PATCH 197/287] Add link for UNO style boards with touch screen display --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 4f91bca..a010d0c 100644 --- a/README.md +++ b/README.md @@ -71,6 +71,8 @@ IO32 wired to IO36 ![Example](https://i.imgur.com/pUZn6lF.jpg) +If the display board is fitted with a resistance based touch screen then this can be used by performing the modifications described here and the fork of the Adafruit library: +https://github.com/s60sc/Adafruit_TouchScreen # ePaper displays From 21811b1751f0c6d0edc1a42564bc9a923b2aecfa Mon Sep 17 00:00:00 2001 From: Bodmer Date: Sun, 11 Nov 2018 10:44:12 +0000 Subject: [PATCH 198/287] Correct colour oder for ST7789 240x320 TFT --- TFT_Drivers/ST7789_Rotation.h | 52 +++++++++++++++++++++-------------- 1 file changed, 31 insertions(+), 21 deletions(-) diff --git a/TFT_Drivers/ST7789_Rotation.h b/TFT_Drivers/ST7789_Rotation.h index 8c526ad..74a5b9f 100644 --- a/TFT_Drivers/ST7789_Rotation.h +++ b/TFT_Drivers/ST7789_Rotation.h @@ -3,40 +3,50 @@ writecommand(TFT_MADCTL); rotation = m % 4; switch (rotation) { - case 0: // Portrait - writedata(TFT_MAD_RGB); - _width = _init_width; - _height = _init_height; -#ifdef CGRAM_OFFSET - colstart = 0; - rowstart = 0; -#endif - break; - case 1: // Landscape (Portrait + 90) - writedata(TFT_MAD_MX | TFT_MAD_MV | TFT_MAD_RGB); - _width = _init_height; - _height = _init_width; + case 0: // Portrait #ifdef CGRAM_OFFSET + writedata(TFT_MAD_BGR); colstart = 0; rowstart = 0; +#else + writedata(TFT_MAD_RGB); #endif - break; - case 2: // Inverter portrait - writedata(TFT_MAD_MX | TFT_MAD_MY | TFT_MAD_RGB); _width = _init_width; _height = _init_height; + break; + + case 1: // Landscape (Portrait + 90) #ifdef CGRAM_OFFSET + writedata(TFT_MAD_MX | TFT_MAD_MV | TFT_MAD_BGR); colstart = 0; - rowstart = 80; + rowstart = 0; +#else + writedata(TFT_MAD_MX | TFT_MAD_MV | TFT_MAD_RGB); #endif - break; - case 3: // Inverted landscape - writedata(TFT_MAD_MV | TFT_MAD_MY | TFT_MAD_RGB); _width = _init_height; _height = _init_width; + break; + + case 2: // Inverter portrait #ifdef CGRAM_OFFSET + writedata(TFT_MAD_MX | TFT_MAD_MY | TFT_MAD_BGR); + colstart = 0; + rowstart = 80; +#else + writedata(TFT_MAD_MX | TFT_MAD_MY | TFT_MAD_RGB); +#endif + _width = _init_width; + _height = _init_height; + break; + case 3: // Inverted landscape +#ifdef CGRAM_OFFSET + writedata(TFT_MAD_MV | TFT_MAD_MY | TFT_MAD_BGR); colstart = 80; rowstart = 0; +#else + writedata(TFT_MAD_MV | TFT_MAD_MY | TFT_MAD_RGB); #endif - break; + _width = _init_height; + _height = _init_width; + break; } From 7a3d6bcab9c1ef906e17a9b80ab4da19425d4eb1 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Wed, 14 Nov 2018 01:05:16 +0000 Subject: [PATCH 199/287] Minor performance improvements for ESP32 Remove legacy changes for double buffered SPI Eliminate unecessary ESP32 SPI read/modify write Eliminate two stage control bit changes (may impact some diplays with setup/hold timing and show ESP32 hardware register bug?) Add single register write for CS and DC command Make setAddrWindow smarter (as used in previous AVR library) Improve compatibility with GFX sketches Re-arrange comments slightly to improve positioning --- Extensions/Smooth_font.cpp | 7 +- TFT_Drivers/ST7735_Defines.h | 16 +-- TFT_eSPI.cpp | 238 ++++++++++++++++++++-------------- TFT_eSPI.h | 130 ++++++++++++++----- User_Setup.h | 12 +- User_Setups/SetupX_Template.h | 23 ++-- keywords.txt | 3 + library.json | 2 +- library.properties | 2 +- 9 files changed, 275 insertions(+), 158 deletions(-) diff --git a/Extensions/Smooth_font.cpp b/Extensions/Smooth_font.cpp index 7d573be..fe77885 100644 --- a/Extensions/Smooth_font.cpp +++ b/Extensions/Smooth_font.cpp @@ -78,6 +78,12 @@ void TFT_eSPI::loadFont(String fontName) _gFontFilename = "/" + fontName + ".vlw"; + // Avoid a crash on the ESP32 if the file does not exist + if (SPIFFS.exists(_gFontFilename) == false) { + Serial.println("Font file " + fontName + " not found!"); + return; + } + fontFile = SPIFFS.open( _gFontFilename, "r"); if(!fontFile) return; @@ -479,7 +485,6 @@ void TFT_eSPI::drawGlyph(uint16_t code) void TFT_eSPI::showFont(uint32_t td) { if(!fontLoaded) return; -// fontFile = SPIFFS.open( _gFontFilename, "r" ); if(!fontFile) { diff --git a/TFT_Drivers/ST7735_Defines.h b/TFT_Drivers/ST7735_Defines.h index 82a3c30..494c673 100644 --- a/TFT_Drivers/ST7735_Defines.h +++ b/TFT_Drivers/ST7735_Defines.h @@ -9,15 +9,15 @@ // Enumerate the different configurations -#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 INITR_GREENTAB 0x0 +#define INITR_REDTAB 0x1 +#define INITR_BLACKTAB 0x2 // Display with no offsets +#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 INITR_GREENTAB160x80 0x6 // Use if you only get part of 128x128 screen in rotation 0 & 1 -#define INITR_REDTAB160x80 0x7 // Added for https://www.aliexpress.com/item/ShengYang-1pcs-IPS-0-96-inch-7P-SPI-HD-65K-Full-Color-OLED-Module-ST7735-Drive/32918394604.html -#define INITB 0xB +#define INITR_REDTAB160x80 0x7 // Added for https://www.aliexpress.com/item/ShengYang-1pcs-IPS-0-96-inch-7P-SPI-HD-65K-Full-Color-OLED-Module-ST7735-Drive/32918394604.html +#define INITB 0xB // Setup the tab color that will be used by the library setRotation() and setup command list diff --git a/TFT_eSPI.cpp b/TFT_eSPI.cpp index afaedd1..10f9bb3 100644 --- a/TFT_eSPI.cpp +++ b/TFT_eSPI.cpp @@ -185,8 +185,8 @@ TFT_eSPI::TFT_eSPI(int16_t w, int16_t h) _booted = true; - addr_row = 0xFFFF; - addr_col = 0xFFFF; + ys_row = ye_row = addr_row = 0xFFFF; + xs_col = xe_col = addr_col = 0xFFFF; #ifdef LOAD_GLCD fontsloaded = 0x0002; // Bit 1 set @@ -530,7 +530,7 @@ uint8_t TFT_eSPI::readcommand8(uint8_t cmd_function, uint8_t index) CS_L; tft_Write_8(cmd_function); DC_D; - reg = tft_Write_8(0); + reg = tft_Read_8(0); CS_H; spi_end_read(); @@ -628,15 +628,15 @@ uint16_t TFT_eSPI::readPixel(int32_t x0, int32_t y0) // 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 #if !defined (ILI9488_DRIVER) - uint8_t r = tft_Write_8(0); - uint8_t g = tft_Write_8(0); - uint8_t b = tft_Write_8(0); + uint8_t r = tft_Read_8(0); + uint8_t g = tft_Read_8(0); + uint8_t b = tft_Read_8(0); #else // The 6 colour bits are in MS 6 bits of each byte, but the ILI9488 needs an extra clock pulse // so bits appear shifted right 1 bit, so mask the middle 6 bits then shift 1 place left - uint8_t r = (tft_Write_8(0)&0x7E)<<1; - uint8_t g = (tft_Write_8(0)&0x7E)<<1; - uint8_t b = (tft_Write_8(0)&0x7E)<<1; + uint8_t r = (tft_Read_8(0)&0x7E)<<1; + uint8_t g = (tft_Read_8(0)&0x7E)<<1; + uint8_t b = (tft_Read_8(0)&0x7E)<<1; #endif CS_H; @@ -768,15 +768,15 @@ void TFT_eSPI::readRect(uint32_t x, uint32_t y, uint32_t w, uint32_t h, uint16_t // 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 #if !defined (ILI9488_DRIVER) - uint8_t r = tft_Write_8(0); - uint8_t g = tft_Write_8(0); - uint8_t b = tft_Write_8(0); + uint8_t r = tft_Read_8(0); + uint8_t g = tft_Read_8(0); + uint8_t b = tft_Read_8(0); #else // The 6 colour bits are in LS 6 bits of each byte but we do not include the extra clock pulse // so we use a trick and mask the middle 6 bits of the byte, then only shift 1 place left - uint8_t r = (tft_Write_8(0)&0x7E)<<1; - uint8_t g = (tft_Write_8(0)&0x7E)<<1; - uint8_t b = (tft_Write_8(0)&0x7E)<<1; + uint8_t r = (tft_Read_8(0)&0x7E)<<1; + uint8_t g = (tft_Read_8(0)&0x7E)<<1; + uint8_t b = (tft_Read_8(0)&0x7E)<<1; #endif // Swapped colour byte order for compatibility with pushRect() @@ -1354,15 +1354,15 @@ void TFT_eSPI::readRectRGB(int32_t x0, int32_t y0, int32_t w, int32_t h, uint8_ // 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 #if !defined (ILI9488_DRIVER) - *data++ = tft_Write_8(0); - *data++ = tft_Write_8(0); - *data++ = tft_Write_8(0); + *data++ = tft_Read_8(0); + *data++ = tft_Read_8(0); + *data++ = tft_Read_8(0); #else // The 6 colour bits are in MS 6 bits of each byte, but the ILI9488 needs an extra clock pulse // so bits appear shifted right 1 bit, so mask the middle 6 bits then shift 1 place left - *data++ = (tft_Write_8(0)&0x7E)<<1; - *data++ = (tft_Write_8(0)&0x7E)<<1; - *data++ = (tft_Write_8(0)&0x7E)<<1; + *data++ = (tft_Read_8(0)&0x7E)<<1; + *data++ = (tft_Read_8(0)&0x7E)<<1; + *data++ = (tft_Read_8(0)&0x7E)<<1; #endif } @@ -2473,7 +2473,7 @@ void TFT_eSPI::setWindow(int16_t x0, int16_t y0, int16_t x1, int16_t y1) // Chip select stays low, use setWindow() from sketches #if defined (ESP8266) && !defined (RPI_WRITE_STROBE) && !defined (RPI_ILI9486_DRIVER) -inline void TFT_eSPI::setAddrWindow(int32_t xs, int32_t ys, int32_t xe, int32_t ye) +void TFT_eSPI::setAddrWindow(int32_t xs, int32_t ys, int32_t xe, int32_t ye) { //spi_begin(); @@ -2563,8 +2563,8 @@ void TFT_eSPI::setAddrWindow(int32_t xs, int32_t ys, int32_t xe, int32_t ye) DC_D; - uint8_t xBin[] = { 0, (uint8_t) (xs>>8), 0, (uint8_t) (xs>>0), 0, (uint8_t) (xe>>8), 0, (uint8_t) (xe>>0), }; - SPI.writePattern(&xBin[0], 8, 1); + uint8_t xb[] = { 0, (uint8_t) (xs>>8), 0, (uint8_t) (xs>>0), 0, (uint8_t) (xe>>8), 0, (uint8_t) (xe>>0), }; + SPI.writePattern(&xb[0], 8, 1); // Row addr set DC_C; @@ -2577,8 +2577,8 @@ void TFT_eSPI::setAddrWindow(int32_t xs, int32_t ys, int32_t xe, int32_t ye) DC_D; - uint8_t yBin[] = { 0, (uint8_t) (ys>>8), 0, (uint8_t) (ys>>0), 0, (uint8_t) (ye>>8), 0, (uint8_t) (ye>>0), }; - SPI.writePattern(&yBin[0], 8, 1); + uint8_t yb[] = { 0, (uint8_t) (ys>>8), 0, (uint8_t) (ys>>0), 0, (uint8_t) (ye>>8), 0, (uint8_t) (ye>>0), }; + SPI.writePattern(&yb[0], 8, 1); // write to RAM DC_C; @@ -2596,7 +2596,7 @@ void TFT_eSPI::setAddrWindow(int32_t xs, int32_t ys, int32_t xe, int32_t ye) #else #if defined (ESP8266) && defined (RPI_ILI9486_DRIVER) // This is for the RPi display that needs 16 bits -inline void TFT_eSPI::setAddrWindow(int32_t x0, int32_t y0, int32_t x1, int32_t y1) +void TFT_eSPI::setAddrWindow(int32_t x0, int32_t y0, int32_t x1, int32_t y1) { //spi_begin(); @@ -2673,7 +2673,7 @@ inline void TFT_eSPI::setAddrWindow(int32_t x0, int32_t y0, int32_t x1, int32_t #else // This is for the ESP32 -inline void TFT_eSPI::setAddrWindow(int32_t x0, int32_t y0, int32_t x1, int32_t y1) +void TFT_eSPI::setAddrWindow(int32_t x0, int32_t y0, int32_t x1, int32_t y1) { //spi_begin(); @@ -2687,42 +2687,48 @@ inline void TFT_eSPI::setAddrWindow(int32_t x0, int32_t y0, int32_t x1, int32_t y1+=rowstart; #endif -#if !defined (RPI_ILI9486_DRIVER) - uint32_t xaw = ((uint32_t)x0 << 16) | x1; - uint32_t yaw = ((uint32_t)y0 << 16) | y1; -#endif + CS_L_DC_C; - // Column addr set - DC_C; - CS_L; + // No need to send x if it has not changed (small speed-up of complex transparent Sprites and bitmaps) + // Nb: caller must fill the set address area so that setting same area again will fill from xs, ys + if (xs_col != x0 || xe_col != x1) { + // Column addr set - tft_Write_8(TFT_CASET); + tft_Write_8(TFT_CASET); - DC_D; + 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); + uint8_t xb[] = { 0, (uint8_t) (x0>>8), 0, (uint8_t) (x0>>0), 0, (uint8_t) (x1>>8), 0, (uint8_t) (x1>>0), }; + SPI.writePattern(&xb[0], 8, 1); #else - tft_Write_32(xaw); + tft_Write_32(SPI_32(x0, x1)); #endif - // Row addr set - DC_C; - tft_Write_8(TFT_PASET); + DC_C; + xs_col = x0; xe_col = x1; + } - DC_D; + // No need to send y if it has not changed + if (ys_row != y0 || ye_row != y1) { + + // Row addr set + tft_Write_8(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); + uint8_t yb[] = { 0, (uint8_t) (y0>>8), 0, (uint8_t) (y0>>0), 0, (uint8_t) (y1>>8), 0, (uint8_t) (y1>>0), }; + SPI.writePattern(&yb[0], 8, 1); #else - tft_Write_32(yaw); + tft_Write_32(SPI_32(y0, y1)); #endif + DC_C; + ys_row = y0; ye_row = y1; + } + // write to RAM - DC_C; - tft_Write_8(TFT_RAMWR); DC_D; @@ -2819,19 +2825,15 @@ void TFT_eSPI::readAddrWindow(int32_t x0, int32_t y0, int32_t x1, int32_t y1) y1+=rowstart; #endif - uint32_t xaw = ((uint32_t)x0 << 16) | x1; - uint32_t yaw = ((uint32_t)y0 << 16) | y1; - // Column addr set - DC_C; - CS_L; + CS_L_DC_C; tft_Write_8(TFT_CASET); DC_D; - tft_Write_32(xaw); + tft_Write_32(SPI_32(x0, x1)); // Row addr set DC_C; @@ -2840,7 +2842,7 @@ void TFT_eSPI::readAddrWindow(int32_t x0, int32_t y0, int32_t x1, int32_t y1) DC_D; - tft_Write_32(yaw); + tft_Write_32(SPI_32(y0, y1)); DC_C; tft_Write_8(TFT_RAMRD); // Read CGRAM command @@ -3053,52 +3055,49 @@ void TFT_eSPI::drawPixel(uint32_t x, uint32_t y, uint32_t color) y+=rowstart; #endif -#if !defined (RPI_ILI9486_DRIVER) - uint32_t xaw = ((uint32_t)x << 16) | x; - uint32_t yaw = ((uint32_t)y << 16) | y; -#endif - - CS_L; + CS_L_DC_C; // No need to send x if it has not changed (speeds things up) if (addr_col != x) { - DC_C; - tft_Write_8(TFT_CASET); DC_D; #if defined (RPI_ILI9486_DRIVER) - 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); + uint8_t xb[] = { 0, (uint8_t) (x>>8), 0, (uint8_t) (x>>0), 0, (uint8_t) (x>>8), 0, (uint8_t) (x>>0), }; + SPI.writePattern(&xb[0], 8, 1); #else - tft_Write_32(xaw); + tft_Write_32(SPI_32(x, x)); #endif - + + DC_C; + addr_col = x; + xs_col = xe_col = x; + } // No need to send y if it has not changed (speeds things up) if (addr_row != y) { - DC_C; - tft_Write_8(TFT_PASET); DC_D; #if defined (RPI_ILI9486_DRIVER) - 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); + uint8_t yb[] = { 0, (uint8_t) (y>>8), 0, (uint8_t) (y>>0), 0, (uint8_t) (y>>8), 0, (uint8_t) (y>>0), }; + SPI.writePattern(&yb[0], 8, 1); #else - tft_Write_32(yaw); + tft_Write_32(SPI_32(y, y)); #endif + DC_C; + addr_row = y; + ys_row = ye_row = y; } - DC_C; tft_Write_8(TFT_RAMWR); @@ -3159,6 +3158,48 @@ void TFT_eSPI::pushColor(uint16_t color, uint32_t len) spi_end(); } +/*************************************************************************************** +** Function name: startWrite +** Description: begin transaction with CS low, MUST later call endWrite +***************************************************************************************/ +void TFT_eSPI::startWrite(void) +{ + spi_begin(); + + CS_L; +} + +/*************************************************************************************** +** Function name: endWrite +** Description: end transaction with CS high +***************************************************************************************/ +void TFT_eSPI::endWrite(void) +{ + + CS_H; + + spi_end(); +} + +/*************************************************************************************** +** Function name: writeColor (use startWrite() and endWrite() before & after) +** Description: raw write of "len" pixels avoiding transaction check +***************************************************************************************/ +void TFT_eSPI::writeColor(uint16_t color, uint32_t len) +{ +#ifdef RPI_WRITE_STROBE + uint8_t colorBin[] = { (uint8_t) (color >> 8), (uint8_t) color }; + if(len) SPI.writePattern(&colorBin[0], 2, 1); len--; + while(len--) {WR_L; WR_H;} +#else + #if defined (ESP32_PARALLEL) + while (len--) {tft_Write_16(color);} + #else + writeBlock(color, len); + #endif +#endif +} + /*************************************************************************************** ** Function name: pushColors ** Description: push an array of pixels for 16 bit raw image drawing @@ -4655,7 +4696,7 @@ void writeBlock(uint16_t color, uint32_t repeat) #elif defined (ILI9488_DRIVER) #ifdef ESP8266 -void writeBlock(uint16_t color, uint32_t repeat) +void TFT_eSPI::writeBlock(uint16_t color, uint32_t repeat) { uint32_t mask = ~(SPIMMOSI << SPILMOSI); @@ -4722,9 +4763,6 @@ void writeBlock(uint16_t color, uint32_t repeat) } #else // Now the code for ESP32 and ILI9488 -#include "soc/spi_reg.h" -#define SPI_NUM 0x3 - void writeBlock(uint16_t color, uint32_t repeat) { // Split out the colours @@ -4795,38 +4833,46 @@ void writeBlock(uint16_t color, uint32_t repeat) #else // Low level register based ESP32 code for 16 bit colour SPI TFTs -#include "soc/spi_reg.h" -#define SPI_NUM 0x3 - - void writeBlock(uint16_t color, uint32_t repeat) { - uint16_t color16 = (color >> 8) | (color << 8); - uint32_t color32 = color16 | color16 << 16; + uint32_t color32 = SPI_32(color, color); - if (repeat > 15) + if (repeat > 31) // Revert legacy toggle buffer change { - SET_PERI_REG_BITS(SPI_MOSI_DLEN_REG(SPI_NUM), SPI_USR_MOSI_DBITLEN, 255, SPI_USR_MOSI_DBITLEN_S); - - while(repeat>15) + WRITE_PERI_REG(SPI_MOSI_DLEN_REG(SPI_NUM), 511); + while(repeat>31) { while (READ_PERI_REG(SPI_CMD_REG(SPI_NUM))&SPI_USR); - for (uint32_t i=0; i<8; i++) WRITE_PERI_REG((SPI_W0_REG(SPI_NUM) + (i << 2)), color32); + WRITE_PERI_REG(SPI_W0_REG(SPI_NUM), color32); + WRITE_PERI_REG(SPI_W1_REG(SPI_NUM), color32); + WRITE_PERI_REG(SPI_W2_REG(SPI_NUM), color32); + WRITE_PERI_REG(SPI_W3_REG(SPI_NUM), color32); + WRITE_PERI_REG(SPI_W4_REG(SPI_NUM), color32); + WRITE_PERI_REG(SPI_W5_REG(SPI_NUM), color32); + WRITE_PERI_REG(SPI_W6_REG(SPI_NUM), color32); + WRITE_PERI_REG(SPI_W7_REG(SPI_NUM), color32); + WRITE_PERI_REG(SPI_W8_REG(SPI_NUM), color32); + WRITE_PERI_REG(SPI_W9_REG(SPI_NUM), color32); + WRITE_PERI_REG(SPI_W10_REG(SPI_NUM), color32); + WRITE_PERI_REG(SPI_W11_REG(SPI_NUM), color32); + WRITE_PERI_REG(SPI_W12_REG(SPI_NUM), color32); + WRITE_PERI_REG(SPI_W13_REG(SPI_NUM), color32); + WRITE_PERI_REG(SPI_W14_REG(SPI_NUM), color32); + WRITE_PERI_REG(SPI_W15_REG(SPI_NUM), color32); SET_PERI_REG_MASK(SPI_CMD_REG(SPI_NUM), SPI_USR); - repeat -= 16; + repeat -= 32; } while (READ_PERI_REG(SPI_CMD_REG(SPI_NUM))&SPI_USR); } if (repeat) { - repeat = (repeat << 4) - 1; - SET_PERI_REG_BITS(SPI_MOSI_DLEN_REG(SPI_NUM), SPI_USR_MOSI_DBITLEN, repeat, SPI_USR_MOSI_DBITLEN_S); - for (uint32_t i=0; i<8; i++) WRITE_PERI_REG((SPI_W0_REG(SPI_NUM) + (i << 2)), color32); + // Revert toggle buffer change + WRITE_PERI_REG(SPI_MOSI_DLEN_REG(SPI_NUM), (repeat << 4) - 1); + for (uint32_t i=0; i <= (repeat>>1); i++) WRITE_PERI_REG((SPI_W0_REG(SPI_NUM) + (i << 2)), color32); SET_PERI_REG_MASK(SPI_CMD_REG(SPI_NUM), SPI_USR); while (READ_PERI_REG(SPI_CMD_REG(SPI_NUM))&SPI_USR); } - } #endif @@ -4975,10 +5021,6 @@ void TFT_eSPI::getSetup(setup_t &tft_settings) #include "Extensions/Sprite.cpp" -// #ifdef ESP32 -// #include "Extensions/pSprite.cpp" -// #endif - #ifdef SMOOTH_FONT #include "Extensions/Smooth_font.cpp" #endif diff --git a/TFT_eSPI.h b/TFT_eSPI.h index 2c93392..456b066 100644 --- a/TFT_eSPI.h +++ b/TFT_eSPI.h @@ -103,6 +103,11 @@ #include +#ifdef ESP32 + #include "soc/spi_reg.h" + #define SPI_NUM 0x3 +#endif + #ifdef SMOOTH_FONT // Call up the SPIFFS FLASH filing system for the anti-aliased fonts #define FS_NO_GLOBALS @@ -127,16 +132,16 @@ #else #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)) + #define DC_C GPIO.out1_w1tc.val = (1 << (TFT_DC - 32)); //\ + //GPIO.out1_w1tc.val = (1 << (TFT_DC - 32)) + #define DC_D GPIO.out1_w1ts.val = (1 << (TFT_DC - 32)); //\ + //GPIO.out1_w1ts.val = (1 << (TFT_DC - 32)) #else #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) + #define DC_C GPIO.out_w1tc = (1 << TFT_DC); //\ + //GPIO.out_w1tc = (1 << TFT_DC) + #define DC_D GPIO.out_w1ts = (1 << TFT_DC); //\ + //GPIO.out_w1ts = (1 << TFT_DC) #else #define DC_C #define DC_D @@ -166,14 +171,14 @@ #define CS_H #else #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)) + #define CS_L GPIO.out1_w1tc.val = (1 << (TFT_CS - 32)); //\ + //GPIO.out1_w1tc.val = (1 << (TFT_CS - 32)) + #define CS_H GPIO.out1_w1ts.val = (1 << (TFT_CS - 32)); //\ + //GPIO.out1_w1ts.val = (1 << (TFT_CS - 32)) #else #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) + #define CS_L GPIO.out_w1tc = (1 << TFT_CS); //GPIO.out_w1tc = (1 << TFT_CS) + #define CS_H GPIO.out_w1ts = (1 << TFT_CS); //GPIO.out_w1ts = (1 << TFT_CS) #else #define CS_L #define CS_H @@ -186,6 +191,18 @@ #endif #endif +// Use single register write for CS_L and DC_C if pins are both in range 0-31 +#ifdef ESP32 + #if (TFT_CS >= 0) && (TFT_CS < 32) && (TFT_DC >= 0) && (TFT_DC < 32) + #define CS_L_DC_C GPIO.out_w1tc = ((1 << TFT_CS) | (1 << TFT_DC)); //\ + //GPIO.out_w1tc = ((1 << TFT_CS) | (1 << TFT_DC)) + #else + #define CS_L_DC_C CS_L; DC_C + #endif +#else // ESP8266 + #define CS_L_DC_C CS_L; DC_C +#endif + // chip select signal for touchscreen #ifndef TOUCH_CS #define T_CS_L // No macro allocated so it generates no code @@ -206,6 +223,14 @@ #endif #endif +#ifdef ESP8266 + // Concatenate two 16 bit values for the SPI 32 bit register write + #define SPI_32(H,L) ( (H)<<16 | (L) ) +#else + // Swap byte order for concatenated 16 bit window address or colors + // AB CD -> DCBA for SPI 32 bit register write + #define SPI_32(H,L) ( ((H)<<8 | (H)>>8) | (((L)<<8 | (L)>>8)<<16 ) ) +#endif #if defined (ESP32) && defined (ESP32_PARALLEL) // Mask for the 8 data bits to set pin directions @@ -253,34 +278,75 @@ #elif defined (ILI9488_DRIVER) // 16 bit colour converted to 3 bytes for 18 bit RGB // Write 8 bits to TFT - #define tft_Write_8(C) SPI.transfer(C) + #define tft_Write_8(C) SPI.transfer(C) // Convert 16 bit colour to 18 bit and write in 3 bytes - #define tft_Write_16(C) SPI.transfer((C & 0xF800)>>8); \ - SPI.transfer((C & 0x07E0)>>3); \ - SPI.transfer((C & 0x001F)<<3) + #define tft_Write_16(C) SPI.transfer((C & 0xF800)>>8); \ + SPI.transfer((C & 0x07E0)>>3); \ + SPI.transfer((C & 0x001F)<<3) // Convert swapped byte 16 bit colour to 18 bit and write in 3 bytes #define tft_Write_16S(C) SPI.transfer(C & 0xF8); \ SPI.transfer((C & 0xE0)>>11 | (C & 0x07)<<5); \ SPI.transfer((C & 0x1F00)>>5) // Write 32 bits to TFT - #define tft_Write_32(C) SPI.write32(C) + #define tft_Write_32(C) SPI.write32(C) #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) + #define tft_Write_8(C) SPI.transfer(0); SPI.transfer(C) + #define tft_Write_16(C) SPI.write16(C) + #define tft_Write_16S(C) SPI.write16(C<<8 | C>>8) + #define tft_Write_32(C) SPI.write32(C) -#else +#elif defined ESP8266 - #define tft_Write_8(C) SPI.transfer(C) - #define tft_Write_16(C) SPI.write16(C) - #define tft_Write_32(C) SPI.write32(C) + #define tft_Write_8(C) SPI.write(C) + #define tft_Write_16(C) SPI.write16(C) + #define tft_Write_32(C) SPI.write32(C) + +#else // ESP32 using SPI with 16 bit color display + + // ESP32 low level SPI writes for 8, 16 and 32 bit values + // to avoid the function call overhead + + // Write 8 bits + #define tft_Write_8(C) \ + WRITE_PERI_REG(SPI_MOSI_DLEN_REG(SPI_NUM), 8-1); \ + WRITE_PERI_REG(SPI_W0_REG(SPI_NUM), C); \ + SET_PERI_REG_MASK(SPI_CMD_REG(SPI_NUM), SPI_USR); \ + while (READ_PERI_REG(SPI_CMD_REG(SPI_NUM))&SPI_USR); + + // Write 16 bits with corrected endianess for 16 bit colours + #define tft_Write_16(C) \ + WRITE_PERI_REG(SPI_MOSI_DLEN_REG(SPI_NUM), 16-1); \ + WRITE_PERI_REG(SPI_W0_REG(SPI_NUM), C<<8 | C>>8); \ + SET_PERI_REG_MASK(SPI_CMD_REG(SPI_NUM), SPI_USR); \ + while (READ_PERI_REG(SPI_CMD_REG(SPI_NUM))&SPI_USR); + + // Write 16 bits (used for ESP32_PARALLEL or ILI9488_DRIVER) + #define tft_Write_16S(C) \ + WRITE_PERI_REG(SPI_MOSI_DLEN_REG(SPI_NUM), 16-1); \ + WRITE_PERI_REG(SPI_W0_REG(SPI_NUM), C); \ + SET_PERI_REG_MASK(SPI_CMD_REG(SPI_NUM), SPI_USR); \ + while (READ_PERI_REG(SPI_CMD_REG(SPI_NUM))&SPI_USR); + + // Write 32 bits + #define tft_Write_32(C) \ + WRITE_PERI_REG(SPI_MOSI_DLEN_REG(SPI_NUM), 32-1); \ + WRITE_PERI_REG(SPI_W0_REG(SPI_NUM), C); \ + SET_PERI_REG_MASK(SPI_CMD_REG(SPI_NUM), SPI_USR); \ + while (READ_PERI_REG(SPI_CMD_REG(SPI_NUM))&SPI_USR); #endif + +#ifndef ESP32_PARALLEL + // Support SPI TFT reads (not all displays support this) + #define tft_Read_8(C) SPI.transfer(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 @@ -406,6 +472,7 @@ template static inline void swap_coord(T& a, T& b) { T t = a; a = b; b = t; } #ifndef min + // Return minimum of two numbers, may already be defined #define min(a,b) (((a) < (b)) ? (a) : (b)) #endif @@ -667,6 +734,10 @@ class TFT_eSPI : public Print { void setAddrWindow(int32_t xs, int32_t ys, int32_t xe, int32_t ye); + // These 3 functions are used together, for every startWrite() there must be an endWrite() + void startWrite(void); // Begin SPI transaction + void writeColor(uint16_t color, uint32_t len); // Write a colour without transaction overhead + void endWrite(void); // End SPI transaction size_t write(uint8_t); @@ -713,6 +784,8 @@ class TFT_eSPI : public Print { 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 addr_row, addr_col; + uint32_t xs_col, xe_col; + uint32_t ys_row, ye_row; uint32_t fontsloaded; @@ -750,9 +823,4 @@ class TFT_eSPI : public Print { // Load the Sprite Class #include "Extensions/Sprite.h" -// #ifdef ESP32 -// // Load the Sprite Class -// #include "Extensions/pSprite.h" -// #endif - #endif diff --git a/User_Setup.h b/User_Setup.h index 9b2359c..e8a5d79 100644 --- a/User_Setup.h +++ b/User_Setup.h @@ -113,14 +113,15 @@ // 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 following must be defined +//#define TFT_SPI_OVERLAP + // 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 ###### @@ -136,6 +137,9 @@ //#define TFT_RST -1 // Set TFT_RST to -1 if display RESET is connected to ESP32 board RST //#define TFT_BL 32 // LED back-light (only for ST7789 with backlight control pin) +//#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 @@ -146,10 +150,6 @@ //#define TFT_RST 33 // Reset pin (could connect to Arduino RESET pin) //#define TFT_BL 32 // LED back-light (required for M5Stack) -//#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 diff --git a/User_Setups/SetupX_Template.h b/User_Setups/SetupX_Template.h index 9b2359c..900da04 100644 --- a/User_Setups/SetupX_Template.h +++ b/User_Setups/SetupX_Template.h @@ -102,7 +102,7 @@ //#define TFT_BL PIN_D1 // LED back-light (only for ST7789 with backlight control pin) -//#define TOUCH_CS PIN_D2 // Chip select pin (T_CS) of touch screen +//#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 @@ -113,14 +113,15 @@ // 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 following must be defined +//#define TFT_SPI_OVERLAP + // 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 ###### @@ -134,8 +135,10 @@ //#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 TFT_BL 32 // LED back-light (only for ST7789 with backlight control pin) +//#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 @@ -144,11 +147,7 @@ //#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 (required for M5Stack) - -//#define TOUCH_CS 21 // Chip select pin (T_CS) of touch screen - -//#define TFT_WR 22 // Write strobe for modified Raspberry Pi TFT only +//#define TFT_BL 32 // LED back-light (if needed) // ###### EDIT THE PINs BELOW TO SUIT YOUR ESP32 PARALLEL TFT SETUP ###### @@ -209,9 +208,9 @@ #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_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 @@ -263,4 +262,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/keywords.txt b/keywords.txt index b197c5a..9fb1758 100644 --- a/keywords.txt +++ b/keywords.txt @@ -6,6 +6,9 @@ drawChar KEYWORD2 setAddrWindow KEYWORD2 setWindow KEYWORD2 readAddrWindow KEYWORD2 +startWrite KEYWORD2 +writeColor KEYWORD2 +endWrite KEYWORD2 pushColor KEYWORD2 pushColors KEYWORD2 fillScreen KEYWORD2 diff --git a/library.json b/library.json index 496bd2e..0867346 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "TFT_eSPI", - "version": "1.1.4", + "version": "1.2.0", "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 fd62f0e..eb7a239 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=TFT_eSPI -version=1.1.4 +version=1.2.0 author=Bodmer maintainer=Bodmer sentence=A fast TFT graphics library for ESP8266 and ESP32 processors for the Arduino IDE From ac7a2293016a6f4140b18fb7d6fc51db399f50af Mon Sep 17 00:00:00 2001 From: Bodmer Date: Fri, 16 Nov 2018 00:54:51 +0000 Subject: [PATCH 200/287] Remove setAddressWindow change for ESP32 Corner case caused missed graphics. --- TFT_eSPI.cpp | 45 +++++++++++++++------------------------------ TFT_eSPI.h | 2 -- 2 files changed, 15 insertions(+), 32 deletions(-) diff --git a/TFT_eSPI.cpp b/TFT_eSPI.cpp index 10f9bb3..45e8219 100644 --- a/TFT_eSPI.cpp +++ b/TFT_eSPI.cpp @@ -185,8 +185,8 @@ TFT_eSPI::TFT_eSPI(int16_t w, int16_t h) _booted = true; - ys_row = ye_row = addr_row = 0xFFFF; - xs_col = xe_col = addr_col = 0xFFFF; + addr_row = 0xFFFF; + addr_col = 0xFFFF; #ifdef LOAD_GLCD fontsloaded = 0x0002; // Bit 1 set @@ -2689,44 +2689,32 @@ void TFT_eSPI::setAddrWindow(int32_t x0, int32_t y0, int32_t x1, int32_t y1) CS_L_DC_C; - // No need to send x if it has not changed (small speed-up of complex transparent Sprites and bitmaps) - // Nb: caller must fill the set address area so that setting same area again will fill from xs, ys - if (xs_col != x0 || xe_col != x1) { - // Column addr set + tft_Write_8(TFT_CASET); - tft_Write_8(TFT_CASET); - - DC_D; + DC_D; #if defined (RPI_ILI9486_DRIVER) - uint8_t xb[] = { 0, (uint8_t) (x0>>8), 0, (uint8_t) (x0>>0), 0, (uint8_t) (x1>>8), 0, (uint8_t) (x1>>0), }; - SPI.writePattern(&xb[0], 8, 1); + uint8_t xb[] = { 0, (uint8_t) (x0>>8), 0, (uint8_t) (x0>>0), 0, (uint8_t) (x1>>8), 0, (uint8_t) (x1>>0), }; + SPI.writePattern(&xb[0], 8, 1); #else - tft_Write_32(SPI_32(x0, x1)); + tft_Write_32(SPI_32(x0, x1)); #endif - DC_C; - xs_col = x0; xe_col = x1; - } + DC_C; - // No need to send y if it has not changed - if (ys_row != y0 || ye_row != y1) { + // Row addr set + tft_Write_8(TFT_PASET); - // Row addr set - tft_Write_8(TFT_PASET); - - DC_D; + DC_D; #if defined (RPI_ILI9486_DRIVER) - uint8_t yb[] = { 0, (uint8_t) (y0>>8), 0, (uint8_t) (y0>>0), 0, (uint8_t) (y1>>8), 0, (uint8_t) (y1>>0), }; - SPI.writePattern(&yb[0], 8, 1); + uint8_t yb[] = { 0, (uint8_t) (y0>>8), 0, (uint8_t) (y0>>0), 0, (uint8_t) (y1>>8), 0, (uint8_t) (y1>>0), }; + SPI.writePattern(&yb[0], 8, 1); #else - tft_Write_32(SPI_32(y0, y1)); + tft_Write_32(SPI_32(y0, y1)); #endif - DC_C; - ys_row = y0; ye_row = y1; - } + DC_C; // write to RAM tft_Write_8(TFT_RAMWR); @@ -3074,8 +3062,6 @@ void TFT_eSPI::drawPixel(uint32_t x, uint32_t y, uint32_t color) DC_C; addr_col = x; - xs_col = xe_col = x; - } // No need to send y if it has not changed (speeds things up) @@ -3095,7 +3081,6 @@ void TFT_eSPI::drawPixel(uint32_t x, uint32_t y, uint32_t color) DC_C; addr_row = y; - ys_row = ye_row = y; } diff --git a/TFT_eSPI.h b/TFT_eSPI.h index 456b066..187dc23 100644 --- a/TFT_eSPI.h +++ b/TFT_eSPI.h @@ -784,8 +784,6 @@ class TFT_eSPI : public Print { 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 addr_row, addr_col; - uint32_t xs_col, xe_col; - uint32_t ys_row, ye_row; uint32_t fontsloaded; From c51ec3751c2b98eb17a4020e8f35bfb1157c228f Mon Sep 17 00:00:00 2001 From: Bodmer Date: Fri, 16 Nov 2018 00:55:58 +0000 Subject: [PATCH 201/287] Raise version --- library.json | 2 +- library.properties | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/library.json b/library.json index 0867346..941dd07 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "TFT_eSPI", - "version": "1.2.0", + "version": "1.2.1", "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 eb7a239..483c231 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=TFT_eSPI -version=1.2.0 +version=1.2.1 author=Bodmer maintainer=Bodmer sentence=A fast TFT graphics library for ESP8266 and ESP32 processors for the Arduino IDE From 419a7ef8de7c3f2710fdfe579da84983ae420e53 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Mon, 19 Nov 2018 23:49:32 +0000 Subject: [PATCH 202/287] Add option for different RGB colour order with ST7789 // For ST7789 ONLY, define the colour order IF the blue and red are swapped on your display //#define TFT_RGB_ORDER TFT_RGB // Colour order Red-Green-Blue //#define TFT_RGB_ORDER TFT_BGR // Colour order Blue-Green-Red --- TFT_Drivers/ST7789_Defines.h | 14 ++++++++++++++ TFT_Drivers/ST7789_Init.h | 7 +------ TFT_Drivers/ST7789_Rotation.h | 20 ++++++++------------ User_Setup.h | 4 ++++ User_Setup_Select.h | 6 ++++++ User_Setups/SetupX_Template.h | 5 +++++ library.json | 2 +- library.properties | 2 +- 8 files changed, 40 insertions(+), 20 deletions(-) diff --git a/TFT_Drivers/ST7789_Defines.h b/TFT_Drivers/ST7789_Defines.h index 06f2a39..2695336 100644 --- a/TFT_Drivers/ST7789_Defines.h +++ b/TFT_Drivers/ST7789_Defines.h @@ -45,6 +45,20 @@ #define TFT_MAD_SS 0x02 #define TFT_MAD_GS 0x01 +#ifdef TFT_RGB_ORDER + #if (TFT_RGB_ORDER == 1) + #define TFT_MAD_COLOR_ORDER TFT_MAD_RGB + #else + #define TFT_MAD_COLOR_ORDER TFT_MAD_BGR + #endif +#else + #ifdef CGRAM_OFFSET + #define TFT_MAD_COLOR_ORDER TFT_MAD_BGR + #else + #define TFT_MAD_COLOR_ORDER TFT_MAD_RGB + #endif +#endif + #define TFT_IDXRD 0x00 // ILI9341 only, indexed control register read // ST7789 specific commands used in init diff --git a/TFT_Drivers/ST7789_Init.h b/TFT_Drivers/ST7789_Init.h index 4afd2cc..c72b51d 100644 --- a/TFT_Drivers/ST7789_Init.h +++ b/TFT_Drivers/ST7789_Init.h @@ -14,12 +14,7 @@ //------------------------------display and color format setting--------------------------------// writecommand(ST7789_MADCTL); writedata(0x00); -#ifdef CGRAM_OFFSET - writedata(0x48); // BGR colour order for 240 x 240 TFT -#else - writedata(0x40); // RGB colour order for 240 x 320 TFT (Issue #232) -#endif - + writedata(TFT_MAD_COLOR_ORDER); // JLX240 display datasheet writecommand(0xB6); diff --git a/TFT_Drivers/ST7789_Rotation.h b/TFT_Drivers/ST7789_Rotation.h index 74a5b9f..d490279 100644 --- a/TFT_Drivers/ST7789_Rotation.h +++ b/TFT_Drivers/ST7789_Rotation.h @@ -5,47 +5,43 @@ switch (rotation) { case 0: // Portrait #ifdef CGRAM_OFFSET - writedata(TFT_MAD_BGR); colstart = 0; rowstart = 0; -#else - writedata(TFT_MAD_RGB); #endif + writedata(TFT_MAD_COLOR_ORDER); + _width = _init_width; _height = _init_height; break; case 1: // Landscape (Portrait + 90) #ifdef CGRAM_OFFSET - writedata(TFT_MAD_MX | TFT_MAD_MV | TFT_MAD_BGR); colstart = 0; rowstart = 0; -#else - writedata(TFT_MAD_MX | TFT_MAD_MV | TFT_MAD_RGB); #endif + writedata(TFT_MAD_MX | TFT_MAD_MV | TFT_MAD_COLOR_ORDER); + _width = _init_height; _height = _init_width; break; case 2: // Inverter portrait #ifdef CGRAM_OFFSET - writedata(TFT_MAD_MX | TFT_MAD_MY | TFT_MAD_BGR); colstart = 0; rowstart = 80; -#else - writedata(TFT_MAD_MX | TFT_MAD_MY | TFT_MAD_RGB); #endif + writedata(TFT_MAD_MX | TFT_MAD_MY | TFT_MAD_COLOR_ORDER); + _width = _init_width; _height = _init_height; break; case 3: // Inverted landscape #ifdef CGRAM_OFFSET - writedata(TFT_MAD_MV | TFT_MAD_MY | TFT_MAD_BGR); colstart = 80; rowstart = 0; -#else - writedata(TFT_MAD_MV | TFT_MAD_MY | TFT_MAD_RGB); #endif + writedata(TFT_MAD_MV | TFT_MAD_MY | TFT_MAD_COLOR_ORDER); + _width = _init_height; _height = _init_width; break; diff --git a/User_Setup.h b/User_Setup.h index e8a5d79..53bbd60 100644 --- a/User_Setup.h +++ b/User_Setup.h @@ -29,6 +29,10 @@ // For M5Stack ESP32 module with integrated display ONLY, remove // in line below //#define M5STACK +// For ST7789 ONLY, define the colour order IF the blue and red are swapped on your display +//#define TFT_RGB_ORDER TFT_RGB // Colour order Red-Green-Blue +//#define TFT_RGB_ORDER TFT_BGR // Colour order Blue-Green-Red + // For ST7789, ST7735 and ILI9163 ONLY, define the pixel width and height in portrait orientation // #define TFT_WIDTH 80 // #define TFT_WIDTH 128 diff --git a/User_Setup_Select.h b/User_Setup_Select.h index dee02cf..be58b13 100644 --- a/User_Setup_Select.h +++ b/User_Setup_Select.h @@ -60,6 +60,11 @@ ///////////////////////////////////////////////////////////////////////////////////// +// Identical looking TFT displays may have a different colour ordering in the 16 bit colour +#define TFT_BGR 0 // Colour order Blue-Green-Red +#define TFT_RGB 1 // Colour order Red-Green-Blue + + // Load the right driver definition - do not tinker here ! #if defined (ILI9341_DRIVER) #include @@ -101,6 +106,7 @@ #define TFT_DRIVER 0x0000 #endif + // These are the pins for all ESP8266 boards // Name GPIO Function #define PIN_D0 16 // WAKE diff --git a/User_Setups/SetupX_Template.h b/User_Setups/SetupX_Template.h index 900da04..26ccf8a 100644 --- a/User_Setups/SetupX_Template.h +++ b/User_Setups/SetupX_Template.h @@ -26,6 +26,11 @@ //#define ILI9488_DRIVER //#define ST7789_DRIVER // Define the screen size below for this display +// For ST7789 ONLY, define the colour order if the blue and red are swapped on your display +// Try ONE option at a time to find the correct colour order for your display +//#define TFT_RGB_ORDER TFT_RGB // Colour order Red-Green-Blue +//#define TFT_RGB_ORDER TFT_BGR // Colour order Blue-Green-Red + // For M5Stack ESP32 module with integrated display ONLY, remove // in line below //#define M5STACK diff --git a/library.json b/library.json index 941dd07..1bba1a1 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "TFT_eSPI", - "version": "1.2.1", + "version": "1.2.2", "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 483c231..a2a1260 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=TFT_eSPI -version=1.2.1 +version=1.2.2 author=Bodmer maintainer=Bodmer sentence=A fast TFT graphics library for ESP8266 and ESP32 processors for the Arduino IDE From d14a09e947caafb55f902daf6054537392196023 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Wed, 21 Nov 2018 02:30:13 +0000 Subject: [PATCH 203/287] Update notes --- Extensions/Touch.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Extensions/Touch.cpp b/Extensions/Touch.cpp index 87fa182..36a183a 100644 --- a/Extensions/Touch.cpp +++ b/Extensions/Touch.cpp @@ -12,7 +12,7 @@ /*************************************************************************************** ** Function name: getTouchRaw -** Description: read raw touch position. Return false if not pressed. +** Description: read raw touch position. Always returns true. ***************************************************************************************/ uint8_t TFT_eSPI::getTouchRaw(uint16_t *x, uint16_t *y){ uint16_t tmp; From 1cf2305e4d6d20292796ce43fb9ccc7531bd4f5e Mon Sep 17 00:00:00 2001 From: Bodmer Date: Fri, 23 Nov 2018 03:09:07 +0000 Subject: [PATCH 204/287] Add capability to read TFT SDA bi-directional pin Tested on an ESP32 with ST7789V display SDA read is very slow on an ESP8266 (to be investigated) Added new setup #define TFT_SDA_READ Add new ESP32 and ESP8266 compatible in example/Generic folder to test capture of TFT screen --- Extensions/Touch.cpp | 2 +- TFT_eSPI.cpp | 164 +++++- TFT_eSPI.h | 14 +- User_Setup.h | 5 + User_Setups/Setup18_ST7789.h | 6 + .../TFT_Screen_Capture/TFT_Screen_Capture.ino | 207 +++++++ .../TFT_Screen_Capture/processing_sketch.ino | 535 ++++++++++++++++++ .../TFT_Screen_Capture/screenServer.ino | 196 +++++++ 8 files changed, 1117 insertions(+), 12 deletions(-) create mode 100644 examples/Generic/TFT_Screen_Capture/TFT_Screen_Capture.ino create mode 100644 examples/Generic/TFT_Screen_Capture/processing_sketch.ino create mode 100644 examples/Generic/TFT_Screen_Capture/screenServer.ino diff --git a/Extensions/Touch.cpp b/Extensions/Touch.cpp index 87fa182..1b0192d 100644 --- a/Extensions/Touch.cpp +++ b/Extensions/Touch.cpp @@ -12,7 +12,7 @@ /*************************************************************************************** ** Function name: getTouchRaw -** Description: read raw touch position. Return false if not pressed. +** Description: read raw touch position. Always returns true. ***************************************************************************************/ uint8_t TFT_eSPI::getTouchRaw(uint16_t *x, uint16_t *y){ uint16_t tmp; diff --git a/TFT_eSPI.cpp b/TFT_eSPI.cpp index 45e8219..86029f9 100644 --- a/TFT_eSPI.cpp +++ b/TFT_eSPI.cpp @@ -253,6 +253,10 @@ void TFT_eSPI::init(uint8_t tc) wrpinmask = (uint32_t) digitalPinToBitMask(TFT_WR); #endif + #ifdef TFT_SCLK + sclkpinmask = (uint32_t) digitalPinToBitMask(TFT_SCLK); + #endif + #ifdef TFT_SPI_OVERLAP // Overlap mode SD0=MISO, SD1=MOSI, CLK=SCLK must use D3 as CS // pins(int8_t sck, int8_t miso, int8_t mosi, int8_t ss); @@ -620,35 +624,68 @@ uint16_t TFT_eSPI::readPixel(int32_t x0, int32_t y0) spi_begin_read(); + #ifdef ST7789_DRIVER + writecommand(ST7789_COLMOD); // Switch from 16 bit colour to 18 bit (required) + writedata(0x66); + #endif + readAddrWindow(x0, y0, x0, y0); // Sets CS low + #ifdef TFT_SDA_READ + // To be investigated: spi_end_read() is VERY slow on the ESP8266 here + spi_end_read(); // Releasing the HAL mutex is mandatory for the ESP32 + SPI.end(); // Disconnect the SPI peripheral to release the pins + digitalWrite(TFT_SCLK, HIGH); // Set clock pin high + pinMode(TFT_SCLK, OUTPUT); // Set the SCLK pin to output + pinMode(TFT_MOSI, INPUT_PULLUP); // Set the MOSI pin to input + #endif + // Dummy read to throw away don't care value - tft_Write_8(0); + tft_Read_8(0); // 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 #if !defined (ILI9488_DRIVER) + uint8_t r = tft_Read_8(0); uint8_t g = tft_Read_8(0); uint8_t b = tft_Read_8(0); + #else + // The 6 colour bits are in MS 6 bits of each byte, but the ILI9488 needs an extra clock pulse // so bits appear shifted right 1 bit, so mask the middle 6 bits then shift 1 place left uint8_t r = (tft_Read_8(0)&0x7E)<<1; uint8_t g = (tft_Read_8(0)&0x7E)<<1; uint8_t b = (tft_Read_8(0)&0x7E)<<1; + #endif CS_H; +#ifdef TFT_SDA_READ + #ifdef ESP32 + SPI.begin(TFT_SCLK, TFT_MISO, TFT_MOSI, -1); + #else + #ifdef TFT_SPI_OVERLAP + SPI.pins(6, 7, 8, 0); + #else + SPI.begin(); + #endif + #endif + #ifdef ST7789_DRIVER + writecommand(ST7789_COLMOD); + writedata(0x55); + #endif +#else spi_end_read(); - +#endif + return color565(r, g, b); #endif } - /*************************************************************************************** ** Function name: read byte - supports class functions ** Description: Read a byte from ESP32 8 bit data port @@ -756,10 +793,24 @@ void TFT_eSPI::readRect(uint32_t x, uint32_t y, uint32_t w, uint32_t h, uint16_t spi_begin_read(); + #ifdef ST7789_DRIVER + writecommand(ST7789_COLMOD); // Switch from 16 bit colour to 18 bit (required) + writedata(0x66); + #endif + readAddrWindow(x, y, x + w - 1, y + h - 1); // Sets CS low + #ifdef TFT_SDA_READ + // To be investigated: spi_end_read() is VERY slow on the ESP8266 here + spi_end_read(); // Releasing the HAL mutex is mandatory for the ESP32 + SPI.end(); // Disconnect the SPI peripheral to release the pins + digitalWrite(TFT_SCLK, HIGH); // Set clock pin high + pinMode(TFT_SCLK, OUTPUT); // Set the SCLK pin to output + pinMode(TFT_MOSI, INPUT_PULLUP); // Set the MOSI pin to input + #endif + // Dummy read to throw away don't care value - tft_Write_8(0); + tft_Read_8(0); // Read window pixel 24 bit RGB values uint32_t len = w * h; @@ -768,15 +819,19 @@ void TFT_eSPI::readRect(uint32_t x, uint32_t y, uint32_t w, uint32_t h, uint16_t // 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 #if !defined (ILI9488_DRIVER) + uint8_t r = tft_Read_8(0); uint8_t g = tft_Read_8(0); uint8_t b = tft_Read_8(0); + #else + // The 6 colour bits are in LS 6 bits of each byte but we do not include the extra clock pulse // so we use a trick and mask the middle 6 bits of the byte, then only shift 1 place left uint8_t r = (tft_Read_8(0)&0x7E)<<1; uint8_t g = (tft_Read_8(0)&0x7E)<<1; uint8_t b = (tft_Read_8(0)&0x7E)<<1; + #endif // Swapped colour byte order for compatibility with pushRect() @@ -785,10 +840,64 @@ void TFT_eSPI::readRect(uint32_t x, uint32_t y, uint32_t w, uint32_t h, uint16_t CS_H; +#ifdef TFT_SDA_READ + #ifdef ESP32 + SPI.begin(TFT_SCLK, TFT_MISO, TFT_MOSI, -1); + #else + #ifdef TFT_SPI_OVERLAP + SPI.pins(6, 7, 8, 0); + #else + SPI.begin(); + #endif + #endif + #ifdef ST7789_DRIVER + writecommand(ST7789_COLMOD); + writedata(0x55); + #endif +#else spi_end_read(); #endif + +#endif } +/*************************************************************************************** +** Function name: tft_Read_8 +** Description: Software SPI to read bidirectional SDA line +***************************************************************************************/ +// For the ST7789 the tft_Read_8() macro is replaced by a function +#ifdef TFT_SDA_READ + #ifdef ESP32 + #define SCLK_L GPIO.out_w1tc = (1 << TFT_SCLK) + #define SCLK_H GPIO.out_w1ts = (1 << TFT_SCLK) + #else // ESP8266 + #define SCLK_L GPOC=sclkpinmask + #define SCLK_H GPOS=sclkpinmask + #endif +uint8_t TFT_eSPI::tft_Read_8(uint8_t dummy) +{ + uint8_t ret = 0; + uint32_t reg = 0; + + for (uint8_t i = 0; i < 8; i++) { // read results + ret <<= 1; + #ifdef ESP32 // bit bangs at around 1MHz + SCLK_L; + //SCLK_L; + reg = gpio_input_get(); // SDA must connect to ESP32 pin in range 0-31 + SCLK_H; + if (reg&(1<>>> NOTE: NOT ALL TFTs SUPPORT READING THE CGRAM (pixel) MEMORY <<<< + + ######################################################################### + ###### DON'T FORGET TO UPDATE THE User_Setup.h FILE IN THE LIBRARY ###### + ######################################################################### +*/ + +// Created by: Bodmer 5/3/17 +// Updated by: Bodmer 10/3/17 +// Updated by: Bodmer 23/11/18 to support SDA reads and the ESP32 +// Version: 0.07 + +// MIT licence applies, all text above must be included in derivative works + +#include // Hardware-specific library +#include + +TFT_eSPI tft = TFT_eSPI(); // Invoke custom library + +unsigned long targetTime = 0; +byte red = 0x1F; +byte green = 0; +byte blue = 0; +byte state = 0; +unsigned int colour = red << 11; // Colour order is RGB 5+6+5 bits each + +void setup(void) { + Serial.begin(921600); // Set to a high rate for fast image transfer to a PC + + tft.init(); + tft.setRotation(0); + tft.fillScreen(TFT_BLACK); + + randomSeed(analogRead(A0)); + + targetTime = millis() + 1000; +} + +#define RGB_TEST false // true produces a simple RGB color test screen + +void loop() { + + if (targetTime < millis()) { + if (!RGB_TEST) + { + targetTime = millis() + 1500; // Wait a minimum of 1.5s + + tft.setRotation(random(4)); + rainbow_fill(); // Fill the screen with rainbow colours + + tft.setTextColor(TFT_BLACK); // Text background is not defined so it is transparent + tft.setTextDatum(TC_DATUM); // Top Centre datum + int xpos = tft.width() / 2; // Centre of screen + + tft.setTextFont(0); // Select font 0 which is the Adafruit font + tft.drawString("Original Adafruit font!", xpos, 5); + + // The new larger fonts do not need to use the .setCursor call, coords are embedded + tft.setTextColor(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!) + tft.drawString("Font size 2", xpos, 14, 2); // Draw text centre at position xpos, 14 using font 2 + tft.drawString("Font size 4", xpos, 30, 4); // Draw text centre at position xpos, 30 using font 4 + tft.drawString("12.34", xpos, 54, 6); // Draw text centre at position xpos, 54 using font 6 + + tft.drawString("12.34 is in font size 6", xpos, 92, 2); // Draw text centre at position xpos, 92 using font 2 + // Note the x position is the top of the font! + + // draw a floating point number + float pi = 3.1415926; // Value to print + int precision = 3; // Number of digits after decimal point + + int ypos = 110; // y position + + tft.setTextDatum(TR_DATUM); // Top Right datum so text butts neatly to xpos (right justified) + + tft.drawFloat(pi, precision, xpos, ypos, 2); // Draw rounded number and return new xpos delta for next print position + + tft.setTextDatum(TL_DATUM); // Top Left datum so text butts neatly to xpos (left justified) + + tft.drawString(" is pi", xpos, ypos, 2); + + tft.setTextSize(1); // We are using a font size multiplier of 1 + tft.setTextDatum(TC_DATUM); // Top Centre datum + tft.setTextColor(TFT_BLACK); // Set text colour to black, no background (so transparent) + + tft.drawString("Transparent...", xpos, 125, 4); // Font 4 + + tft.setTextColor(TFT_WHITE, TFT_BLACK); // Set text colour to white and background to black + tft.drawString("White on black", xpos, 150, 4); // Font 4 + + tft.setTextColor(TFT_GREEN, TFT_BLACK); // This time we will use green text on a black background + + tft.setTextFont(2); // Select font 2, now we do not need to specify the font in drawString() + + // An easier way to position text and blank old text is to set the datum and use width padding + tft.setTextDatum(BC_DATUM); // Bottom centre for text datum + tft.setTextPadding(tft.width() + 1); // Pad text to full screen width + 1 spare for +/-1 position rounding + + tft.drawString("Ode to a Small Lump of Green Putty", xpos, 230 - 32); + tft.drawString("I Found in My Armpit One Midsummer", xpos, 230 - 16); + tft.drawString("Morning", xpos, 230); + + tft.setTextDatum(TL_DATUM); // Reset to top left for text datum + tft.setTextPadding(0); // Reset text padding to 0 pixels + + // Now call the screen server to send a copy of the TFT screen to the PC running the Processing client sketch + screenServer(); + } + else + { + tft.fillScreen(TFT_BLACK); + tft.fillRect( 0, 0, 16, 16, TFT_RED); + tft.fillRect(16, 0, 16, 16, TFT_GREEN); + tft.fillRect(32, 0, 16, 16, TFT_BLUE); + screenServer(); + } + } +} + +// Fill screen with a rainbow pattern +void rainbow_fill() +{ + // The colours and state are not initialised so the start colour changes each time the funtion is called + int rotation = tft.getRotation(); + tft.setRotation(random(4)); + for (int i = tft.height() - 1; i >= 0; i--) { + // This is a "state machine" that ramps up/down the colour brightnesses in sequence + switch (state) { + case 0: + green ++; + 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 --; + 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; + // Draw a line 1 pixel wide in the selected colour + tft.drawFastHLine(0, i, tft.width(), colour); // tft.width() returns the pixel width of the display + } + tft.setRotation(rotation); +} diff --git a/examples/Generic/TFT_Screen_Capture/processing_sketch.ino b/examples/Generic/TFT_Screen_Capture/processing_sketch.ino new file mode 100644 index 0000000..3e71f41 --- /dev/null +++ b/examples/Generic/TFT_Screen_Capture/processing_sketch.ino @@ -0,0 +1,535 @@ +// This is a copy of the processing sketch that can be used to capture the images +// Copy the sketch below and remove the /* and */ at the beginning and end. + +// The sketch runs in Processing version 3.3 on a PC, it can be downloaded here: +// https://processing.org/download/ + +/* + +// This is a Processing sketch, see https://processing.org/ to download the IDE + +// The sketch is a client that requests TFT screenshots from an Arduino board. +// The Arduino must call a screenshot server function to respond with pixels. + +// It has been created to work with the TFT_eSPI library here: +// https://github.com/Bodmer/TFT_eSPI + +// The sketch must only be run when the designated serial port is available and enumerated +// otherwise the screenshot window may freeze and that process will need to be terminated +// This is a limitation of the Processing environment and not the sketch. +// If anyone knows how to determine if a serial port is available at start up the PM me +// on (Bodmer) the Arduino forum. + +// The block below contains variables that the user may need to change for a particular setup +// As a minimum set the serial port and baud rate must be defined. The capture window is +// automatically resized for landscape, portrait and different TFT resolutions. + +// Captured images are stored in the sketch folder, use the Processing IDE "Sketch" menu +// option "Show Sketch Folder" or press Ctrl+K + +// Created by: Bodmer 5/3/17 +// Updated by: Bodmer 12/3/17 +// Version: 0.07 + +// MIT licence applies, all text above must be included in derivative works + + +// ########################################################################################### +// # These are the values to change for a particular setup # +// # +int serial_port = 0; // Use enumerated value from list provided when sketch is run # +// # +// On an Arduino Due Programming Port use a baud rate of:115200) # +// On an Arduino Due Native USB Port use a baud rate of any value # +int serial_baud_rate = 921600; // # +// # +// Change the image file type saved here, comment out all but one # +//String image_type = ".jpg"; // # +String image_type = ".png"; // Lossless compression # +//String image_type = ".bmp"; // # +//String image_type = ".tif"; // # +// # +boolean save_border = true; // Save the image with a border # +int border = 5; // Border pixel width # +boolean fade = false; // Fade out image after saving # +// # +int max_images = 100; // Maximum of numbered file images before over-writing files # +// # +int max_allowed = 1000; // Maximum number of save images allowed before a restart # +// # +// # End of the values to change for a particular setup # +// ########################################################################################### + +// These are default values, this sketch obtains the actual values from the Arduino board +int tft_width = 480; // default TFT width (automatic - sent by Arduino) +int tft_height = 480; // default TFT height (automatic - sent by Arduino) +int color_bytes = 2; // 2 for 16 bit, 3 for three RGB bytes (automatic - sent by Arduino) + +import processing.serial.*; + +Serial serial; // Create an instance called serial + +int serialCount = 0; // Count of colour bytes arriving + +// Stage window graded background colours +color bgcolor1 = color(0, 100, 104); // Arduino IDE style background color 1 +color bgcolor2 = color(77, 183, 187); // Arduino IDE style background color 2 +//color bgcolor2 = color(255, 255, 255); // White + +// TFT image frame greyscale value (dark grey) +color frameColor = 42; + +color buttonStopped = color(255, 0, 0); +color buttonRunning = color(128, 204, 206); +color buttonDimmed = color(180, 0, 0); +boolean dimmed = false; +boolean running = true; +boolean mouseClick = false; + +int[] rgb = new int[3]; // Buffer for the colour bytes +int indexRed = 0; // Colour byte index in the array +int indexGreen = 1; +int indexBlue = 2; + +int n = 0; + +int x_offset = (500 - tft_width) /2; // Image offsets in the window +int y_offset = 20; + +int xpos = 0, ypos = 0; // Current pixel position + +int beginTime = 0; +int pixelWaitTime = 1000; // Maximum 1000ms wait for image pixels to arrive +int lastPixelTime = 0; // Time that "image send" command was sent + +int requestTime = 0; +int requestCount = 0; + +int state = 0; // State machine current state + +int progress_bar = 0; // Console progress bar dot count +int pixel_count = 0; // Number of pixels read for 1 screen +float percentage = 0; // Percentage of pixels received + +int saved_image_count = 0; // Stats - number of images processed +int bad_image_count = 0; // Stats - number of images that had lost pixels +String filename = ""; + +int drawLoopCount = 0; // Used for the fade out + +void setup() { + + size(500, 540); // Stage size, can handle 480 pixels wide screen + noStroke(); // No border on the next thing drawn + noSmooth(); // No anti-aliasing to avoid adjacent pixel colour merging + + // Graded background and title + drawWindow(); + + frameRate(2000); // High frame rate so draw() loops fast + + // Print a list of the available serial ports + println("-----------------------"); + println("Available Serial Ports:"); + println("-----------------------"); + printArray(Serial.list()); + println("-----------------------"); + + print("Port currently used: ["); + print(serial_port); + println("]"); + + String portName = Serial.list()[serial_port]; + + serial = new Serial(this, portName, serial_baud_rate); + + state = 99; +} + +void draw() { + + if (mouseClick) buttonClicked(); + + switch(state) { + + case 0: // Init varaibles, send start request + if (running) { + tint(0, 0, 0, 255); + flushBuffer(); + println(""); + print("Ready: "); + + xpos = 0; + ypos = 0; + serialCount = 0; + progress_bar = 0; + pixel_count = 0; + percentage = 0; + drawLoopCount = frameCount; + lastPixelTime = millis() + 1000; + + state = 1; + } else { + if (millis() > beginTime) { + beginTime = millis() + 500; + dimmed = !dimmed; + if (dimmed) drawButton(buttonDimmed); + else drawButton(buttonStopped); + } + } + break; + + case 1: // Console message, give server some time + print("requesting image "); + serial.write("S"); + delay(10); + beginTime = millis(); + requestTime = millis() + 1000; + requestCount = 1; + state = 2; + break; + + case 2: // Get size and set start time for rendering duration report + if (millis() > requestTime) { + requestCount++; + print("*"); + serial.clear(); + serial.write("S"); + if (requestCount > 32) { + requestCount = 0; + System.err.println(" - no response!"); + state = 0; + } + requestTime = millis() + 1000; + } + if ( getSize() == true ) { // Go to next state when we have the size and bits per pixel + getFilename(); + flushBuffer(); // Precaution in case image header size increases in later versions + lastPixelTime = millis() + 1000; + beginTime = millis(); + state = 3; + } + break; + + case 3: // Request pixels and render returned RGB values + state = renderPixels(); // State will change when all pixels are rendered + + // Request more pixels, changing the number requested allows the average transfer rate to be controlled + // The pixel transfer rate is dependant on four things: + // 1. The frame rate defined in this Processing sketch in setup() + // 2. The baud rate of the serial link (~10 bit periods per byte) + // 3. The number of request bytes 'R' sent in the lines below + // 4. The number of pixels sent in a burst by the server sketch (defined via NPIXELS) + + //serial.write("RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR"); // 32 x NPIXELS more + serial.write("RRRRRRRRRRRRRRRR"); // 16 x NPIXELS more + //serial.write("RRRRRRRR"); // 8 x NPIXELS more + //serial.write("RRRR"); // 4 x NPIXELS more + //serial.write("RR"); // 2 x NPIXELS more + //serial.write("R"); // 1 x NPIXELS more + if (!running) state = 4; + break; + + case 4: // Pixel receive time-out, flush serial buffer + flushBuffer(); + state = 6; + break; + + case 5: // Save the image to the sketch folder (Ctrl+K to access) + saveScreenshot(); + saved_image_count++; + println("Saved image count = " + saved_image_count); + if (bad_image_count > 0) System.err.println(" Bad image count = " + bad_image_count); + drawLoopCount = frameCount; // Reset value ready for counting in step 6 + state = 6; + break; + + case 6: // Fade the old image if enabled + if ( fadedImage() == true ) state = 0; // Go to next state when image has faded + break; + + case 99: // Draw image viewer window + drawWindow(); + delay(50); // Delay here seems to be required for the IDE console to get ready + state = 0; + break; + + default: + println(""); + System.err.println("Error state reached - check sketch!"); + break; + } +} + +void drawWindow() +{ + // Graded background in Arduino colours + for (int i = 0; i < height - 25; i++) { + float inter = map(i, 0, height - 25, 0, 1); + color c = lerpColor(bgcolor1, bgcolor2, inter); + stroke(c); + line(0, i, 500, i); + } + fill(bgcolor2); + rect( 0, height-25, width-1, 24); + textAlign(CENTER); + textSize(20); + fill(0); + text("Bodmer's TFT image viewer", width/2, height-6); + + if (running) drawButton(buttonRunning); + else drawButton(buttonStopped); +} + +void flushBuffer() +{ + //println("Clearing serial pipe after a time-out"); + int clearTime = millis() + 50; + while ( millis() < clearTime ) serial.clear(); +} + +boolean getSize() +{ + if ( serial.available() > 6 ) { + println(); + char code = (char)serial.read(); + if (code == 'W') { + tft_width = serial.read()<<8 | serial.read(); + } + code = (char)serial.read(); + if (code == 'H') { + tft_height = serial.read()<<8 | serial.read(); + } + code = (char)serial.read(); + if (code == 'Y') { + int bits_per_pixel = (char)serial.read(); + if (bits_per_pixel == 24) color_bytes = 3; + else color_bytes = 2; + } + code = (char)serial.read(); + if (code == '?') { + drawWindow(); + + x_offset = (500 - tft_width) /2; + tint(0, 0, 0, 255); + noStroke(); + fill(frameColor); + rect((width - tft_width)/2 - border, y_offset - border, tft_width + 2 * border, tft_height + 2 * border); + return true; + } + } + return false; +} + +void saveScreenshot() +{ + println(); + if (saved_image_count < max_allowed) + { + if (filename == "") filename = "tft_screen_" + (n++); + filename = filename + image_type; + println("Saving image as \"" + filename + "\""); + if (save_border) + { + PImage partialSave = get(x_offset - border, y_offset - border, tft_width + 2*border, tft_height + 2*border); + partialSave.save(filename); + } else { + PImage partialSave = get(x_offset, y_offset, tft_width, tft_height); + partialSave.save(filename); + } + + if (n>=max_images) n = 0; + } + else + { + System.err.println(max_allowed + " saved image count exceeded, restart the sketch"); + } +} + +void getFilename() +{ + int readTime = millis() + 20; + int inByte = 0; + filename = ""; + while ( serial.available() > 0 && millis() < readTime && inByte != '.') + { + inByte = serial.read(); + if (inByte == ' ') inByte = '_'; + if ( unicodeCheck(inByte) ) filename += (char)inByte; + } + + inByte = serial.read(); + if (inByte == '@') filename += "_" + timeCode(); + else if (inByte == '#') filename += "_" + saved_image_count%100; + else if (inByte == '%') filename += "_" + millis(); + else if (inByte != '*') filename = ""; + + inByte = serial.read(); + if (inByte == 'j') image_type =".jpg"; + else if (inByte == 'b') image_type =".bmp"; + else if (inByte == 'p') image_type =".png"; + else if (inByte == 't') image_type =".tif"; +} + +boolean unicodeCheck(int unicode) +{ + if ( unicode >= '0' && unicode <= '9' ) return true; + if ( (unicode >= 'A' && unicode <= 'Z' ) || (unicode >= 'a' && unicode <= 'z')) return true; + if ( unicode == '_' || unicode == '/' ) return true; + return false; +} + +String timeCode() +{ + String timeCode = (int)year() + "_" + (int)month() + "_" + (int)day() + "_"; + timeCode += (int)hour() + "_" + (int)minute() + "_" + (int)second(); + return timeCode; +} + +int renderPixels() +{ + if ( serial.available() > 0 ) { + + // Add the latest byte from the serial port to array: + while (serial.available()>0) + { + rgb[serialCount++] = serial.read(); + + // If we have 3 colour bytes: + if ( serialCount >= color_bytes ) { + serialCount = 0; + pixel_count++; + if (color_bytes == 3) + { + stroke(rgb[indexRed], rgb[indexGreen], rgb[indexBlue], 1000); + } else + { // Can cater for various byte orders + //stroke( (rgb[0] & 0x1F)<<3, (rgb[0] & 0xE0)>>3 | (rgb[1] & 0x07)<<5, (rgb[1] & 0xF8)); + //stroke( (rgb[1] & 0x1F)<<3, (rgb[1] & 0xE0)>>3 | (rgb[0] & 0x07)<<5, (rgb[0] & 0xF8)); + stroke( (rgb[0] & 0xF8), (rgb[1] & 0xE0)>>3 | (rgb[0] & 0x07)<<5, (rgb[1] & 0x1F)<<3); + //stroke( (rgb[1] & 0xF8), (rgb[0] & 0xE0)>>3 | (rgb[1] & 0x07)<<5, (rgb[0] & 0x1F)<<3); + } + // We get some pixel merge aliasing if smooth() is defined, so draw pixel twice + point(xpos + x_offset, ypos + y_offset); + //point(xpos + x_offset, ypos + y_offset); + + lastPixelTime = millis(); + xpos++; + if (xpos >= tft_width) { + xpos = 0; + progressBar(); + ypos++; + if (ypos>=tft_height) { + ypos = 0; + if ((int)percentage <100) { + while (progress_bar++ < 64) print(" "); + percent(100); + } + println("Image fetch time = " + (millis()-beginTime)/1000.0 + " s"); + return 5; + } + } + } + } + } else + { + if (millis() > (lastPixelTime + pixelWaitTime)) + { + println(""); + System.err.println(pixelWaitTime + "ms time-out for pixels exceeded..."); + if (pixel_count > 0) { + bad_image_count++; + System.err.print("Pixels missing = " + (tft_width * tft_height - pixel_count)); + System.err.println(", corrupted image not saved"); + System.err.println("Good image count = " + saved_image_count); + System.err.println(" Bad image count = " + bad_image_count); + } + return 4; + } + } + return 3; +} + +void progressBar() +{ + progress_bar++; + print("."); + if (progress_bar >63) + { + progress_bar = 0; + percentage = 0.5 + 100 * pixel_count/(0.001 + tft_width * tft_height); + percent(percentage); + } +} + +void percent(float percentage) +{ + if (percentage > 100) percentage = 100; + println(" [ " + (int)percentage + "% ]"); + textAlign(LEFT); + textSize(16); + noStroke(); + fill(bgcolor2); + rect(10, height - 25, 70, 20); + fill(0); + text(" [ " + (int)percentage + "% ]", 10, height-8); +} + +boolean fadedImage() +{ + int opacity = frameCount - drawLoopCount; // So we get increasing fade + if (fade) + { + tint(255, opacity); + //image(tft_img, x_offset, y_offset); + noStroke(); + fill(50, 50, 50, opacity); + rect( (width - tft_width)/2, y_offset, tft_width, tft_height); + delay(10); + } + if (opacity > 50) // End fade after 50 cycles + { + return true; + } + return false; +} + +void drawButton(color buttonColor) +{ + stroke(0); + fill(buttonColor); + rect(500 - 100, 540 - 26, 80, 24); + textAlign(CENTER); + textSize(20); + fill(0); + if (running) text(" Pause ", 500 - 60, height-7); + else text(" Run ", 500 - 60, height-7); +} + +void buttonClicked() +{ + mouseClick = false; + if (running) { + running = false; + drawButton(buttonStopped); + System.err.println(""); + System.err.println("Stopped - click 'Run' button: "); + //noStroke(); + //fill(50); + //rect( (width - tft_width)/2, y_offset, tft_width, tft_height); + beginTime = millis() + 500; + dimmed = false; + state = 4; + } else { + running = true; + drawButton(buttonRunning); + } +} + +void mousePressed() { + if (mouseX > (500 - 100) && mouseX < (500 - 20) && mouseY > (540 - 26) && mouseY < (540 - 2)) { + mouseClick = true; + } +} + +*/ diff --git a/examples/Generic/TFT_Screen_Capture/screenServer.ino b/examples/Generic/TFT_Screen_Capture/screenServer.ino new file mode 100644 index 0000000..2924947 --- /dev/null +++ b/examples/Generic/TFT_Screen_Capture/screenServer.ino @@ -0,0 +1,196 @@ +// Reads a screen image off the TFT and send it to a processing client sketch +// over the serial port. Use a high baud rate, e.g. for an ESP8266: +// Serial.begin(921600); + +// At 921600 baud a 320 x 240 image with 16 bit colour transfers can be sent to the +// PC client in ~1.67s and 24 bit colour in ~2.5s which is close to the theoretical +// minimum transfer time. + +// This sketch has been created to work with the TFT_eSPI library here: +// https://github.com/Bodmer/TFT_eSPI + +// Created by: Bodmer 27/1/17 +// Updated by: Bodmer 10/3/17 +// Updated by: Bodmer 23/11/18 to support SDA reads and the ESP32 +// Version: 0.08 + +// MIT licence applies, all text above must be included in derivative works + +//==================================================================================== +// Definitions +//==================================================================================== +#define PIXEL_TIMEOUT 100 // 100ms Time-out between pixel requests +#define START_TIMEOUT 10000 // 10s Maximum time to wait at start transfer + +#define BITS_PER_PIXEL 16 // 24 for RGB colour format, 16 for 565 colour format + +// File names must be alpha-numeric characters (0-9, a-z, A-Z) or "/" underscore "_" +// other ascii characters are stripped out by client, including / generates +// sub-directories +#define DEFAULT_FILENAME "tft_screenshots/screenshot" // In case none is specified +#define FILE_TYPE "png" // jpg, bmp, png, tif are valid + +// Filename extension +// '#' = add incrementing number, '@' = add timestamp, '%' add millis() timestamp, +// '*' = add nothing +// '@' and '%' will generate new unique filenames, so beware of cluttering up your +// hard drive with lots of images! The PC client sketch is set to limit the number of +// saved images to 1000 and will then prompt for a restart. +#define FILE_EXT '@' + +// Number of pixels to send in a burst (minimum of 1), no benefit above 8 +// NPIXELS values and render times: +// NPIXELS 1 = use readPixel() = >5s and 16 bit pixels only +// NPIXELS >1 using rectRead() 2 = 1.75s, 4 = 1.68s, 8 = 1.67s +#define NPIXELS 8 // Must be integer division of both TFT width and TFT height + +//==================================================================================== +// Screen server call with no filename +//==================================================================================== +// Start a screen dump server (serial or network) - no filename specified +boolean screenServer(void) +{ + // With no filename the screenshot will be saved with a default name e.g. tft_screen_#.xxx + // where # is a number 0-9 and xxx is a file type specified below + return screenServer(DEFAULT_FILENAME); +} + +//==================================================================================== +// Screen server call with filename +//==================================================================================== +// Start a screen dump server (serial or network) - filename specified +boolean screenServer(String filename) +{ + delay(0); // Equivalent to yield() for ESP8266; + + boolean result = serialScreenServer(filename); // Screenshot serial port server + //boolean result = wifiScreenServer(filename); // Screenshot WiFi UDP port server (WIP) + + delay(0); // Equivalent to yield() + + //Serial.println(); + //if (result) Serial.println(F("Screen dump passed :-)")); + //else Serial.println(F("Screen dump failed :-(")); + + return result; +} + +//==================================================================================== +// Serial server function that sends the data to the client +//==================================================================================== +boolean serialScreenServer(String filename) +{ + // Precautionary receive buffer garbage flush for 50ms + uint32_t clearTime = millis() + 50; + while ( millis() < clearTime && Serial.read() >= 0) delay(0); // Equivalent to yield() for ESP8266; + + boolean wait = true; + uint32_t lastCmdTime = millis(); // Initialise start of command time-out + + // Wait for the starting flag with a start time-out + while (wait) + { + delay(0); // Equivalent to yield() for ESP8266; + // Check serial buffer + if (Serial.available() > 0) { + // Read the command byte + uint8_t cmd = Serial.read(); + // If it is 'S' (start command) then clear the serial buffer for 100ms and stop waiting + if ( cmd == 'S' ) { + // Precautionary receive buffer garbage flush for 50ms + clearTime = millis() + 50; + while ( millis() < clearTime && Serial.read() >= 0) delay(0); // Equivalent to yield() for ESP8266; + + wait = false; // No need to wait anymore + lastCmdTime = millis(); // Set last received command time + + // Send screen size etc using a simple header with delimiters for client checks + sendParameters(filename); + } + } + else + { + // Check for time-out + if ( millis() > lastCmdTime + START_TIMEOUT) return false; + } + } + + uint8_t color[3 * NPIXELS]; // RGB and 565 format color buffer for N pixels + + // Send all the pixels on the whole screen + for ( uint32_t y = 0; y < tft.height(); y++) + { + // Increment x by NPIXELS as we send NPIXELS for every byte received + for ( uint32_t x = 0; x < tft.width(); x += NPIXELS) + { + delay(0); // Equivalent to yield() for ESP8266; + + // Wait here for serial data to arrive or a time-out elapses + while ( Serial.available() == 0 ) + { + if ( millis() > lastCmdTime + PIXEL_TIMEOUT) return false; + delay(0); // Equivalent to yield() for ESP8266; + } + + // Serial data must be available to get here, read 1 byte and + // respond with N pixels, i.e. N x 3 RGB bytes or N x 2 565 format bytes + if ( Serial.read() == 'X' ) { + // X command byte means abort, so clear the buffer and return + clearTime = millis() + 50; + while ( millis() < clearTime && Serial.read() >= 0) delay(0); // Equivalent to yield() for ESP8266; + return false; + } + // Save arrival time of the read command (for later time-out check) + lastCmdTime = millis(); + +#if defined BITS_PER_PIXEL && BITS_PER_PIXEL >= 24 && NPIXELS > 1 + // Fetch N RGB pixels from x,y and put in buffer + tft.readRectRGB(x, y, NPIXELS, 1, color); + // Send buffer to client + Serial.write(color, 3 * NPIXELS); // Write all pixels in the buffer +#else + // Fetch N 565 format pixels from x,y and put in buffer + if (NPIXELS > 1) tft.readRect(x, y, NPIXELS, 1, (uint16_t *)color); + else + { + uint16_t c = tft.readPixel(x, y); + color[0] = c>>8; + color[1] = c & 0xFF; // Swap bytes + } + // Send buffer to client + Serial.write(color, 2 * NPIXELS); // Write all pixels in the buffer +#endif + } + } + + Serial.flush(); // Make sure all pixel bytes have been despatched + + return true; +} + +//==================================================================================== +// Send screen size etc using a simple header with delimiters for client checks +//==================================================================================== +void sendParameters(String filename) +{ + Serial.write('W'); // Width + Serial.write(tft.width() >> 8); + Serial.write(tft.width() & 0xFF); + + Serial.write('H'); // Height + Serial.write(tft.height() >> 8); + Serial.write(tft.height() & 0xFF); + + Serial.write('Y'); // Bits per pixel (16 or 24) + if (NPIXELS > 1) Serial.write(BITS_PER_PIXEL); + else Serial.write(16); // readPixel() only provides 16 bit values + + Serial.write('?'); // Filename next + Serial.print(filename); + + Serial.write('.'); // End of filename marker + + Serial.write(FILE_EXT); // Filename extension identifier + + Serial.write(*FILE_TYPE); // First character defines file type j,b,p,t +} From d13f84e4a6b05dc5326495072cc29a9aa852f16f Mon Sep 17 00:00:00 2001 From: Bodmer Date: Sat, 24 Nov 2018 01:30:41 +0000 Subject: [PATCH 205/287] Re-work the support for bi-directional SDA line Tested on ESP32 and ST7789V display only. It may not work with other displays! Use: #define TFT_SDA_READ in setup file to use bi-directional SDA pin support. --- README.md | 4 +- TFT_eSPI.cpp | 220 ++++++++++++++-------------------- TFT_eSPI.h | 23 ++-- User_Setups/SetupX_Template.h | 5 + 4 files changed, 112 insertions(+), 140 deletions(-) diff --git a/README.md b/README.md index a010d0c..beffc09 100644 --- a/README.md +++ b/README.md @@ -26,11 +26,11 @@ The Button class from Adafruit_GFX is incorporated, with the enhancement that th 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. 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 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. 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 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, or by selecting a configuration in the library "User_Setup_Selet,h" file. 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 your own configuration in the "User_Setup_Selet,h" file. Fonts and features can easily be enabled/disabled by commenting out lines. # Anti-aliased Fonts diff --git a/TFT_eSPI.cpp b/TFT_eSPI.cpp index 86029f9..260e324 100644 --- a/TFT_eSPI.cpp +++ b/TFT_eSPI.cpp @@ -534,7 +534,7 @@ uint8_t TFT_eSPI::readcommand8(uint8_t cmd_function, uint8_t index) CS_L; tft_Write_8(cmd_function); DC_D; - reg = tft_Read_8(0); + reg = tft_Read_8(); CS_H; spi_end_read(); @@ -624,62 +624,40 @@ uint16_t TFT_eSPI::readPixel(int32_t x0, int32_t y0) spi_begin_read(); - #ifdef ST7789_DRIVER - writecommand(ST7789_COLMOD); // Switch from 16 bit colour to 18 bit (required) - writedata(0x66); - #endif - readAddrWindow(x0, y0, x0, y0); // Sets CS low #ifdef TFT_SDA_READ - // To be investigated: spi_end_read() is VERY slow on the ESP8266 here - spi_end_read(); // Releasing the HAL mutex is mandatory for the ESP32 - SPI.end(); // Disconnect the SPI peripheral to release the pins - digitalWrite(TFT_SCLK, HIGH); // Set clock pin high - pinMode(TFT_SCLK, OUTPUT); // Set the SCLK pin to output - pinMode(TFT_MOSI, INPUT_PULLUP); // Set the MOSI pin to input + begin_SDA_Read(); #endif // Dummy read to throw away don't care value - tft_Read_8(0); + tft_Read_8(); + + #if !defined (ILI9488_DRIVER) // 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 - #if !defined (ILI9488_DRIVER) - - uint8_t r = tft_Read_8(0); - uint8_t g = tft_Read_8(0); - uint8_t b = tft_Read_8(0); + uint8_t r = tft_Read_8(); + uint8_t g = tft_Read_8(); + uint8_t b = tft_Read_8(); #else // The 6 colour bits are in MS 6 bits of each byte, but the ILI9488 needs an extra clock pulse // so bits appear shifted right 1 bit, so mask the middle 6 bits then shift 1 place left - uint8_t r = (tft_Read_8(0)&0x7E)<<1; - uint8_t g = (tft_Read_8(0)&0x7E)<<1; - uint8_t b = (tft_Read_8(0)&0x7E)<<1; + uint8_t r = (tft_Read_8()&0x7E)<<1; + uint8_t g = (tft_Read_8()&0x7E)<<1; + uint8_t b = (tft_Read_8()&0x7E)<<1; #endif CS_H; -#ifdef TFT_SDA_READ - #ifdef ESP32 - SPI.begin(TFT_SCLK, TFT_MISO, TFT_MOSI, -1); - #else - #ifdef TFT_SPI_OVERLAP - SPI.pins(6, 7, 8, 0); - #else - SPI.begin(); - #endif + #ifdef TFT_SDA_READ + end_SDA_Read(); #endif - #ifdef ST7789_DRIVER - writecommand(ST7789_COLMOD); - writedata(0x55); - #endif -#else + spi_end_read(); -#endif return color565(r, g, b); @@ -793,44 +771,34 @@ void TFT_eSPI::readRect(uint32_t x, uint32_t y, uint32_t w, uint32_t h, uint16_t spi_begin_read(); - #ifdef ST7789_DRIVER - writecommand(ST7789_COLMOD); // Switch from 16 bit colour to 18 bit (required) - writedata(0x66); - #endif - readAddrWindow(x, y, x + w - 1, y + h - 1); // Sets CS low #ifdef TFT_SDA_READ - // To be investigated: spi_end_read() is VERY slow on the ESP8266 here - spi_end_read(); // Releasing the HAL mutex is mandatory for the ESP32 - SPI.end(); // Disconnect the SPI peripheral to release the pins - digitalWrite(TFT_SCLK, HIGH); // Set clock pin high - pinMode(TFT_SCLK, OUTPUT); // Set the SCLK pin to output - pinMode(TFT_MOSI, INPUT_PULLUP); // Set the MOSI pin to input + begin_SDA_Read(); #endif // Dummy read to throw away don't care value - tft_Read_8(0); + tft_Read_8(); // 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 #if !defined (ILI9488_DRIVER) - uint8_t r = tft_Read_8(0); - uint8_t g = tft_Read_8(0); - uint8_t b = tft_Read_8(0); + // 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 = tft_Read_8(); + uint8_t g = tft_Read_8(); + uint8_t b = tft_Read_8(); #else // The 6 colour bits are in LS 6 bits of each byte but we do not include the extra clock pulse // so we use a trick and mask the middle 6 bits of the byte, then only shift 1 place left - uint8_t r = (tft_Read_8(0)&0x7E)<<1; - uint8_t g = (tft_Read_8(0)&0x7E)<<1; - uint8_t b = (tft_Read_8(0)&0x7E)<<1; + uint8_t r = (tft_Read_8()&0x7E)<<1; + uint8_t g = (tft_Read_8()&0x7E)<<1; + uint8_t b = (tft_Read_8()&0x7E)<<1; #endif @@ -840,23 +808,11 @@ void TFT_eSPI::readRect(uint32_t x, uint32_t y, uint32_t w, uint32_t h, uint16_t CS_H; -#ifdef TFT_SDA_READ - #ifdef ESP32 - SPI.begin(TFT_SCLK, TFT_MISO, TFT_MOSI, -1); - #else - #ifdef TFT_SPI_OVERLAP - SPI.pins(6, 7, 8, 0); - #else - SPI.begin(); - #endif + #ifdef TFT_SDA_READ + end_SDA_Read(); #endif - #ifdef ST7789_DRIVER - writecommand(ST7789_COLMOD); - writedata(0x55); - #endif -#else + spi_end_read(); -#endif #endif } @@ -865,40 +821,66 @@ void TFT_eSPI::readRect(uint32_t x, uint32_t y, uint32_t w, uint32_t h, uint16_t ** Function name: tft_Read_8 ** Description: Software SPI to read bidirectional SDA line ***************************************************************************************/ -// For the ST7789 the tft_Read_8() macro is replaced by a function -#ifdef TFT_SDA_READ - #ifdef ESP32 - #define SCLK_L GPIO.out_w1tc = (1 << TFT_SCLK) - #define SCLK_H GPIO.out_w1ts = (1 << TFT_SCLK) - #else // ESP8266 - #define SCLK_L GPOC=sclkpinmask - #define SCLK_H GPOS=sclkpinmask - #endif -uint8_t TFT_eSPI::tft_Read_8(uint8_t dummy) +#if defined (ESP8266) && defined (TFT_SDA_READ) +uint8_t TFT_eSPI::tft_Read_8(void) { uint8_t ret = 0; uint32_t reg = 0; for (uint8_t i = 0; i < 8; i++) { // read results ret <<= 1; - #ifdef ESP32 // bit bangs at around 1MHz - SCLK_L; - //SCLK_L; - reg = gpio_input_get(); // SDA must connect to ESP32 pin in range 0-31 - SCLK_H; - if (reg&(1< Date: Sat, 24 Nov 2018 02:38:49 +0000 Subject: [PATCH 206/287] Fix issue #250 --- Extensions/Sprite.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Extensions/Sprite.cpp b/Extensions/Sprite.cpp index 73285da..6463b59 100644 --- a/Extensions/Sprite.cpp +++ b/Extensions/Sprite.cpp @@ -946,11 +946,14 @@ void TFT_eSprite::fillRect(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t { if (!_created ) return; + if ((x >= _iwidth) || (y >= _iheight)) return; + if (x < 0) { w += x; x = 0; } + if (y < 0) { h += y; y = 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; From 329b794950ce974b4374f1c95f26c5e8afb761e5 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Sat, 24 Nov 2018 02:49:13 +0000 Subject: [PATCH 207/287] Update version and ReadMe Added notes about reading from the display. --- README.md | 2 ++ library.json | 2 +- library.properties | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index beffc09..bb3dadb 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,8 @@ An Arduino IDE compatible graphics and fonts library for ESP8266 and ESP32 proce 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). +Some displays permit the internal TFT screen RAM to be read. The library supports reading from ILI9341, ST7789 and ILI9488 SPI displays for the ESP32 and ESP8266. The 8 bit parallel displays used with the ESP32 can usually can be read too. The TFT_Screen_Capture example allows full screens to be captured and sent to a PC, this is handy to create program documentation. + 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. 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. diff --git a/library.json b/library.json index 1bba1a1..7c1f70b 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "TFT_eSPI", - "version": "1.2.2", + "version": "1.3.0", "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 a2a1260..a4d6b9b 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=TFT_eSPI -version=1.2.2 +version=1.3.0 author=Bodmer maintainer=Bodmer sentence=A fast TFT graphics library for ESP8266 and ESP32 processors for the Arduino IDE From b0d56ce39483e4fef972d2f6c28d197e38d2b4a5 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Sat, 24 Nov 2018 23:30:40 +0000 Subject: [PATCH 208/287] Add ability to scroll 1bpp Sprites New example added: Sprite_scroll_1bit --- Extensions/Sprite.cpp | 16 +- .../Sprite_scroll_1bit/Sprite_scroll_1bit.ino | 147 ++++++++++++++++++ 2 files changed, 162 insertions(+), 1 deletion(-) create mode 100644 examples/Sprite/Sprite_scroll_1bit/Sprite_scroll_1bit.ino diff --git a/Extensions/Sprite.cpp b/Extensions/Sprite.cpp index 6463b59..aef6010 100644 --- a/Extensions/Sprite.cpp +++ b/Extensions/Sprite.cpp @@ -657,7 +657,21 @@ void TFT_eSprite::scroll(int16_t dx, int16_t dy) fyp += iw; } } - else return; // TODO add scroll for 1 bpp + else if (_bpp == 1) + { + if (dx > 0) { tx += w; fx += w; } // Start from right edge + while (h--) + { // move pixels one by one + for (uint16_t xp = 0; xp < w; xp++) + { + if (dx <= 0) drawPixel(tx + xp, ty, readPixel(fx + xp, fy)); + if (dx > 0) drawPixel(tx - xp, ty, readPixel(fx - xp, fy)); + } + if (dy <= 0) { ty++; fy++; } + else { ty--; fy--; } + } + } + else return; // Not 1, 8 or 16 bpp // Fill the gap left by the scrolling if (dx > 0) fillRect(_sx, _sy, dx, _sh, _scolor); diff --git a/examples/Sprite/Sprite_scroll_1bit/Sprite_scroll_1bit.ino b/examples/Sprite/Sprite_scroll_1bit/Sprite_scroll_1bit.ino new file mode 100644 index 0000000..671d7b6 --- /dev/null +++ b/examples/Sprite/Sprite_scroll_1bit/Sprite_scroll_1bit.ino @@ -0,0 +1,147 @@ +/* + Sketch to show scrolling of the graphics in sprites. + + This sketch scrolls a 1 bit per pixel (1 bpp) Sprite. + + In a 1 bit Sprite any colour except TFT_BLACK turns a pixel "ON" + TFT_BLACK turns a pixel "OFF". + + The 1 bpp Sprite has a unique property that other bit depth Sprites + do not have, your can set the rotation of the coordinate frame e.g.: + spr.setRotation(1); + + This is similar to screen rotations, so for example text can + be drawn rotated: + Rotation 0: Normal orientation + Rotation 1: Coordinate frame rotated clockwise 90 degrees + Rotation 2: Coordinate frame rotated clockwise 180 degrees (upside down) + Rotation 3: Coordinate frame rotated clockwise 270 degrees + + When pushSprite is used the sprite is drawn with the width and height + staying as created, so the created Sprite itself is not rotated during + rendering. See stext2 sprite example below at line 83. + + ON and OFF pixels can be set to any two colours before + rendering to the screen with pushSprite, for example: + tft.setBitmapColor(ON_COLOR, OFF_COLOR); + + Scrolling 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 1 bit Sprite occupies (width * height)/8 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(1); + graph1.createSprite(128, 61); + graph1.fillSprite(TFT_BLACK); // 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_BLACK); // Try this line to change the graph scroll area + + // Create a sprite for the scrolling numbers + stext1.setColorDepth(1); + stext1.createSprite(32, 64); + stext1.fillSprite(TFT_BLACK); // Fill sprite with blue + stext1.setScrollRect(0, 0, 32, 64, TFT_BLACK); // 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(1); + stext2.createSprite(16, 80); // Narrow and tall + stext2.setRotation(1); // Plot with 90 deg. clockwise rotation + stext2.fillSprite(TFT_BLACK); + stext2.setScrollRect(0, 0, 40, 16, TFT_BLACK); // 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_WHITE); // 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 + tft.setBitmapColor(TFT_WHITE, TFT_BLUE); // Specify the colours of the ON and OFF pixels + graph1.pushSprite(0, 0); + + tft.setBitmapColor(TFT_GREEN, TFT_BLACK); + stext1.pushSprite(0, 64); + + tft.setBitmapColor(TFT_BLACK, TFT_YELLOW); + stext2.pushSprite(60, 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_WHITE); // 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_WHITE); + } + + 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 +//========================================================================================== From ee7d18f7f975d4cab7bed6957e0547699afe8292 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Sat, 24 Nov 2018 23:32:00 +0000 Subject: [PATCH 209/287] Raise version --- library.json | 2 +- library.properties | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/library.json b/library.json index 7c1f70b..65e6a5a 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "TFT_eSPI", - "version": "1.3.0", + "version": "1.3.1", "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 a4d6b9b..16f3333 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=TFT_eSPI -version=1.3.0 +version=1.3.1 author=Bodmer maintainer=Bodmer sentence=A fast TFT graphics library for ESP8266 and ESP32 processors for the Arduino IDE From a0e2bc97d60fa9c71684d0fdcffad4bf32ada1fb Mon Sep 17 00:00:00 2001 From: Bodmer Date: Sat, 24 Nov 2018 23:45:31 +0000 Subject: [PATCH 210/287] Comment typo --- examples/Sprite/Sprite_scroll_1bit/Sprite_scroll_1bit.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/Sprite/Sprite_scroll_1bit/Sprite_scroll_1bit.ino b/examples/Sprite/Sprite_scroll_1bit/Sprite_scroll_1bit.ino index 671d7b6..ab4a53e 100644 --- a/examples/Sprite/Sprite_scroll_1bit/Sprite_scroll_1bit.ino +++ b/examples/Sprite/Sprite_scroll_1bit/Sprite_scroll_1bit.ino @@ -7,7 +7,7 @@ TFT_BLACK turns a pixel "OFF". The 1 bpp Sprite has a unique property that other bit depth Sprites - do not have, your can set the rotation of the coordinate frame e.g.: + do not have, you can set the rotation of the coordinate frame e.g.: spr.setRotation(1); This is similar to screen rotations, so for example text can From 5e62875ff548a2214500ffc05f06a93010548fd0 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Sun, 25 Nov 2018 03:15:16 +0000 Subject: [PATCH 211/287] Fix issue #256 Parallel compatibility lost during recent updates. Now working again. --- TFT_eSPI.cpp | 2 +- TFT_eSPI.h | 8 +++++--- library.json | 2 +- library.properties | 2 +- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/TFT_eSPI.cpp b/TFT_eSPI.cpp index 260e324..92873da 100644 --- a/TFT_eSPI.cpp +++ b/TFT_eSPI.cpp @@ -4928,7 +4928,7 @@ void writeBlock(uint16_t color, uint32_t repeat) void writeBlock(uint16_t color, uint32_t repeat) { - uint32_t color32 = SPI_32(color, color); + uint32_t color32 = COL_32(color, color); if (repeat > 31) // Revert legacy toggle buffer change { diff --git a/TFT_eSPI.h b/TFT_eSPI.h index fae8068..2af6786 100644 --- a/TFT_eSPI.h +++ b/TFT_eSPI.h @@ -226,10 +226,12 @@ #ifdef ESP8266 // Concatenate two 16 bit values for the SPI 32 bit register write #define SPI_32(H,L) ( (H)<<16 | (L) ) + #define COL_32(H,L) ( (H)<<16 | (L) ) #else - // Swap byte order for concatenated 16 bit window address or colors - // AB CD -> DCBA for SPI 32 bit register write - #define SPI_32(H,L) ( ((H)<<8 | (H)>>8) | (((L)<<8 | (L)>>8)<<16 ) ) + #define SPI_32(H,L) ( (H)<<16 | (L) ) + // Swap byte order for concatenated 16 bit colors + // AB CD -> DCBA for 32 bit register write + #define COL_32(H,L) ( ((H)<<8 | (H)>>8) | (((L)<<8 | (L)>>8)<<16 ) ) #endif #if defined (ESP32) && defined (ESP32_PARALLEL) diff --git a/library.json b/library.json index 65e6a5a..7540093 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "TFT_eSPI", - "version": "1.3.1", + "version": "1.3.2", "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 16f3333..fd3be61 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=TFT_eSPI -version=1.3.1 +version=1.3.2 author=Bodmer maintainer=Bodmer sentence=A fast TFT graphics library for ESP8266 and ESP32 processors for the Arduino IDE From e01ea43baf5c75bfe9e4217909c2d1d94b811b4c Mon Sep 17 00:00:00 2001 From: Bodmer Date: Sun, 25 Nov 2018 03:47:54 +0000 Subject: [PATCH 212/287] Fix SPI incompatibility I fixed the parallel and broke the SPI, now both work! --- TFT_eSPI.h | 6 +++++- library.json | 2 +- library.properties | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/TFT_eSPI.h b/TFT_eSPI.h index 2af6786..8f31f29 100644 --- a/TFT_eSPI.h +++ b/TFT_eSPI.h @@ -228,7 +228,11 @@ #define SPI_32(H,L) ( (H)<<16 | (L) ) #define COL_32(H,L) ( (H)<<16 | (L) ) #else - #define SPI_32(H,L) ( (H)<<16 | (L) ) + #ifdef ESP32_PARALLEL + #define SPI_32(H,L) ( (H)<<16 | (L) ) + #else + #define SPI_32(H,L) ( ((H)<<8 | (H)>>8) | (((L)<<8 | (L)>>8)<<16 ) ) + #endif // Swap byte order for concatenated 16 bit colors // AB CD -> DCBA for 32 bit register write #define COL_32(H,L) ( ((H)<<8 | (H)>>8) | (((L)<<8 | (L)>>8)<<16 ) ) diff --git a/library.json b/library.json index 7540093..786075d 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "TFT_eSPI", - "version": "1.3.2", + "version": "1.3.3", "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 fd3be61..a9e0071 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=TFT_eSPI -version=1.3.2 +version=1.3.3 author=Bodmer maintainer=Bodmer sentence=A fast TFT graphics library for ESP8266 and ESP32 processors for the Arduino IDE From eb08c17b24b665f86335f779d4ac4935c9575a7c Mon Sep 17 00:00:00 2001 From: Bodmer Date: Sun, 25 Nov 2018 16:54:31 +0000 Subject: [PATCH 213/287] Raise version Arduino library manager picked up the wrong release. This version raise is to forece a new release to be recognised. Releases 1.3.2 and 1.3.3 have been deleted. --- library.json | 2 +- library.properties | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/library.json b/library.json index 786075d..94d5340 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "TFT_eSPI", - "version": "1.3.3", + "version": "1.3.4", "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 a9e0071..b8b5f01 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=TFT_eSPI -version=1.3.3 +version=1.3.4 author=Bodmer maintainer=Bodmer sentence=A fast TFT graphics library for ESP8266 and ESP32 processors for the Arduino IDE From 0f630b375bf93edb88a4ee438a47499df53c3fe5 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Sun, 25 Nov 2018 17:20:40 +0000 Subject: [PATCH 214/287] Add TTGO boards --- User_Setup_Select.h | 3 +++ User_Setups/Setup22_TTGO_T4.h | 28 ++++++++++++++++++++++++++++ User_Setups/Setup23_TTGO_TM.h | 34 ++++++++++++++++++++++++++++++++++ 3 files changed, 65 insertions(+) create mode 100644 User_Setups/Setup22_TTGO_T4.h create mode 100644 User_Setups/Setup23_TTGO_TM.h diff --git a/User_Setup_Select.h b/User_Setup_Select.h index be58b13..bd31358 100644 --- a/User_Setup_Select.h +++ b/User_Setup_Select.h @@ -43,6 +43,9 @@ //#include // Setup file for ESP8266 and ILI9488 SPI bus TFT //#include // Setup file for ESP32 and ILI9488 SPI bus TFT +//#include // Setup file for ESP32 and TTGO T4 (BTC) ILI9341 SPI bus TFT +//#include // Setup file for ESP32 and TTGO TM ST7789 SPI bus TFT + //#include // Setup file configured for my ST7735S 80x160 //#include diff --git a/User_Setups/Setup22_TTGO_T4.h b/User_Setups/Setup22_TTGO_T4.h new file mode 100644 index 0000000..fa04323 --- /dev/null +++ b/User_Setups/Setup22_TTGO_T4.h @@ -0,0 +1,28 @@ +// Setup for the TTGO T4 ("Bitcoin Tracker") ESP32 board with 2.2" ILI9341 display + +#define ILI9341_DRIVER + +#define TFT_MISO 12 +#define TFT_MOSI 23 +#define TFT_SCLK 18 + +#define TFT_CS 27 +#define TFT_DC 26 +#define TFT_RST 5 + +#define LOAD_GLCD +#define LOAD_FONT2 +#define LOAD_FONT4 +#define LOAD_FONT6 +#define LOAD_FONT7 +#define LOAD_FONT8 +#define LOAD_GFXFF + +#define SMOOTH_FONT + +//#define SPI_FREQUENCY 27000000 // Actually sets it to 26.67MHz = 80/3 + #define SPI_FREQUENCY 40000000 // Maximum for ILI9341 + +#define SPI_TOUCH_FREQUENCY 2500000 + +#define SPI_READ_FREQUENCY 10000000 diff --git a/User_Setups/Setup23_TTGO_TM.h b/User_Setups/Setup23_TTGO_TM.h new file mode 100644 index 0000000..74c9a05 --- /dev/null +++ b/User_Setups/Setup23_TTGO_TM.h @@ -0,0 +1,34 @@ +// Setup for the TTGO TM (Music) ESP32 board with 2.4" ST7789V display + +#define ST7789_DRIVER + +#define TFT_SDA_READ // Read from display, it only provides an SDA pin + +#define TFT_MISO 19 // Must be defined even though it is not used +#define TFT_MOSI 23 // Connected to display SDA line +#define TFT_SCLK 18 + +#define TFT_CS 05 +#define TFT_DC 16 +#define TFT_RST 17 + +#define TFT_WIDTH 240 +#define TFT_HEIGHT 320 + +//#define TFT_RGB_ORDER TFT_RGB // Colour order Red-Green-Blue +#define TFT_RGB_ORDER TFT_BGR // Colour order Blue-Green-Red + +#define LOAD_GLCD +#define LOAD_FONT2 +#define LOAD_FONT4 +#define LOAD_FONT6 +#define LOAD_FONT7 +#define LOAD_FONT8 +#define LOAD_GFXFF + +#define SMOOTH_FONT + +#define SPI_FREQUENCY 40000000 // The display also seems to work reliably at 80MHz + +#define SPI_READ_FREQUENCY 6000000 // 6 MHz is the maximum SPI read speed for the ST7789V + From 046e48f8c33a3f6412f01475a8a510b61a5c3d33 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Sun, 25 Nov 2018 18:08:29 +0000 Subject: [PATCH 215/287] Delete legacy example --- .../TFT_Screen_Capture/TFT_Screen_Capture.ino | 188 ------ .../TFT_Screen_Capture/processing_sketch.ino | 535 ------------------ .../TFT_Screen_Capture/screenServer.ino | 194 ------- 3 files changed, 917 deletions(-) delete mode 100644 examples/320 x 240/TFT_Screen_Capture/TFT_Screen_Capture.ino delete mode 100644 examples/320 x 240/TFT_Screen_Capture/processing_sketch.ino delete mode 100644 examples/320 x 240/TFT_Screen_Capture/screenServer.ino diff --git a/examples/320 x 240/TFT_Screen_Capture/TFT_Screen_Capture.ino b/examples/320 x 240/TFT_Screen_Capture/TFT_Screen_Capture.ino deleted file mode 100644 index d791b0f..0000000 --- a/examples/320 x 240/TFT_Screen_Capture/TFT_Screen_Capture.ino +++ /dev/null @@ -1,188 +0,0 @@ -/* - This sketch has been written to test the Processing screenshot client. - - It has been created to work with the TFT_eSPI library here: - https://github.com/Bodmer/TFT_eSPI - - It sends screenshots to a PC running a Processing client sketch. - - The Processing IDE that will run the client sketch can be downloaded - here: https://processing.org/ - - The Processing sketch needed is contained within a tab attached to this - Arduino sketch. Cut and copy that tab into the Processing IDE and run. - - This sketch uses the GLCD, 2, 4, 6 fonts only. - - Make sure all the display driver and pin comnenctions are correct by - editting the User_Setup.h file in the TFT_eSPI library folder. - - Maximum recommended SPI clock rate is 27MHz when reading pixels, 40MHz - seems to be OK with ILI9341 displays but this is above the manufacturers - specifed maximum clock rate. - - ######################################################################### - ###### DON'T FORGET TO UPDATE THE User_Setup.h FILE IN THE LIBRARY ###### - ######################################################################### -*/ - -// Created by: Bodmer 5/3/17 -// Updated by: Bodmer 10/3/17 -// Version: 0.06 - -// MIT licence applies, all text above must be included in derivative works - -#include // Hardware-specific library -#include - -TFT_eSPI tft = TFT_eSPI(); // Invoke custom library with default width and height - -unsigned long targetTime = 0; -byte red = 31; -byte green = 0; -byte blue = 0; -byte state = 0; -unsigned int colour = red << 11; // Colour order is RGB 5+6+5 bits each - -void setup(void) { - Serial.begin(921600); - - tft.init(); - tft.setRotation(0); - tft.fillScreen(TFT_BLACK); - - randomSeed(analogRead(A0)); - - targetTime = millis() + 1000; -} - -void loop() { - - if (targetTime < millis()) { - targetTime = millis() + 1500; // Wait a minimum of 1.5s - - tft.setRotation(random(4)); - rainbow_fill(); // Fill the screen with rainbow colours - - tft.setTextColor(TFT_BLACK); // Text background is not defined so it is transparent - tft.setTextDatum(TC_DATUM); // Top Centre datum - int xpos = tft.width()/2; // Centre of screen - - tft.setTextFont(0); // Select font 0 which is the Adafruit font - tft.drawString("Original Adafruit font!", xpos, 5); - - // The new larger fonts do not need to use the .setCursor call, coords are embedded - tft.setTextColor(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!) - tft.drawString("Font size 2", xpos, 14, 2); // Draw text centre at position xpos, 14 using font 2 - tft.drawString("Font size 4", xpos, 30, 4); // Draw text centre at position xpos, 30 using font 4 - tft.drawString("12.34", xpos, 54, 6); // Draw text centre at position xpos, 54 using font 6 - - tft.drawString("12.34 is in font size 6", xpos, 92, 2); // Draw text centre at position xpos, 92 using font 2 - // Note the x position is the top of the font! - - // draw a floating point number - float pi = 3.1415926; // Value to print - int precision = 3; // Number of digits after decimal point - - int ypos = 110; // y position - - tft.setTextDatum(TR_DATUM); // Top Right datum so text butts neatly to xpos (right justified) - - tft.drawFloat(pi, precision, xpos, ypos, 2); // Draw rounded number and return new xpos delta for next print position - - tft.setTextDatum(TL_DATUM); // Top Left datum so text butts neatly to xpos (left justified) - - tft.drawString(" is pi", xpos, ypos, 2); - - tft.setTextSize(1); // We are using a font size multiplier of 1 - tft.setTextDatum(TC_DATUM); // Top Centre datum - tft.setTextColor(TFT_BLACK); // Set text colour to black, no background (so transparent) - - tft.drawString("Transparent...", xpos, 125, 4); // Font 4 - - tft.setTextColor(TFT_WHITE, TFT_BLACK); // Set text colour to white and background to black - tft.drawString("White on black", xpos, 150, 4); // Font 4 - - tft.setTextColor(TFT_GREEN, TFT_BLACK); // This time we will use green text on a black background - - tft.setTextFont(2); // Select font 2, now we do not need to specify the font in drawString() - - // An easier way to position text and blank old text is to set the datum and use width padding - tft.setTextDatum(BC_DATUM); // Bottom centre for text datum - tft.setTextPadding(tft.width() + 1); // Pad text to full screen width + 1 spare for +/-1 position rounding - - tft.drawString("Ode to a Small Lump of Green Putty", xpos, 230 - 32); - tft.drawString("I Found in My Armpit One Midsummer", xpos, 230 - 16); - tft.drawString("Morning", xpos, 230); - - tft.setTextDatum(TL_DATUM); // Reset to top left for text datum - tft.setTextPadding(0); // Reset text padding to 0 pixels - - // Now call the screen server to send a copy of the TFT screen to the PC running the Processing client sketch - screenServer(); - } -} - -// Fill screen with a rainbow pattern -void rainbow_fill() -{ - // The colours and state are not initialised so the start colour changes each time the funtion is called - int rotation = tft.getRotation(); - tft.setRotation(random(4)); - for (int i = tft.height() - 1; i >= 0; i--) { - // This is a "state machine" that ramps up/down the colour brightnesses in sequence - switch (state) { - case 0: - green ++; - 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 --; - 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; - // Draw a line 1 pixel wide in the selected colour - tft.drawFastHLine(0, i, tft.width(), colour); // in this example tft.width() returns the pixel width of the display - } - tft.setRotation(rotation); -} - - - diff --git a/examples/320 x 240/TFT_Screen_Capture/processing_sketch.ino b/examples/320 x 240/TFT_Screen_Capture/processing_sketch.ino deleted file mode 100644 index 3e71f41..0000000 --- a/examples/320 x 240/TFT_Screen_Capture/processing_sketch.ino +++ /dev/null @@ -1,535 +0,0 @@ -// This is a copy of the processing sketch that can be used to capture the images -// Copy the sketch below and remove the /* and */ at the beginning and end. - -// The sketch runs in Processing version 3.3 on a PC, it can be downloaded here: -// https://processing.org/download/ - -/* - -// This is a Processing sketch, see https://processing.org/ to download the IDE - -// The sketch is a client that requests TFT screenshots from an Arduino board. -// The Arduino must call a screenshot server function to respond with pixels. - -// It has been created to work with the TFT_eSPI library here: -// https://github.com/Bodmer/TFT_eSPI - -// The sketch must only be run when the designated serial port is available and enumerated -// otherwise the screenshot window may freeze and that process will need to be terminated -// This is a limitation of the Processing environment and not the sketch. -// If anyone knows how to determine if a serial port is available at start up the PM me -// on (Bodmer) the Arduino forum. - -// The block below contains variables that the user may need to change for a particular setup -// As a minimum set the serial port and baud rate must be defined. The capture window is -// automatically resized for landscape, portrait and different TFT resolutions. - -// Captured images are stored in the sketch folder, use the Processing IDE "Sketch" menu -// option "Show Sketch Folder" or press Ctrl+K - -// Created by: Bodmer 5/3/17 -// Updated by: Bodmer 12/3/17 -// Version: 0.07 - -// MIT licence applies, all text above must be included in derivative works - - -// ########################################################################################### -// # These are the values to change for a particular setup # -// # -int serial_port = 0; // Use enumerated value from list provided when sketch is run # -// # -// On an Arduino Due Programming Port use a baud rate of:115200) # -// On an Arduino Due Native USB Port use a baud rate of any value # -int serial_baud_rate = 921600; // # -// # -// Change the image file type saved here, comment out all but one # -//String image_type = ".jpg"; // # -String image_type = ".png"; // Lossless compression # -//String image_type = ".bmp"; // # -//String image_type = ".tif"; // # -// # -boolean save_border = true; // Save the image with a border # -int border = 5; // Border pixel width # -boolean fade = false; // Fade out image after saving # -// # -int max_images = 100; // Maximum of numbered file images before over-writing files # -// # -int max_allowed = 1000; // Maximum number of save images allowed before a restart # -// # -// # End of the values to change for a particular setup # -// ########################################################################################### - -// These are default values, this sketch obtains the actual values from the Arduino board -int tft_width = 480; // default TFT width (automatic - sent by Arduino) -int tft_height = 480; // default TFT height (automatic - sent by Arduino) -int color_bytes = 2; // 2 for 16 bit, 3 for three RGB bytes (automatic - sent by Arduino) - -import processing.serial.*; - -Serial serial; // Create an instance called serial - -int serialCount = 0; // Count of colour bytes arriving - -// Stage window graded background colours -color bgcolor1 = color(0, 100, 104); // Arduino IDE style background color 1 -color bgcolor2 = color(77, 183, 187); // Arduino IDE style background color 2 -//color bgcolor2 = color(255, 255, 255); // White - -// TFT image frame greyscale value (dark grey) -color frameColor = 42; - -color buttonStopped = color(255, 0, 0); -color buttonRunning = color(128, 204, 206); -color buttonDimmed = color(180, 0, 0); -boolean dimmed = false; -boolean running = true; -boolean mouseClick = false; - -int[] rgb = new int[3]; // Buffer for the colour bytes -int indexRed = 0; // Colour byte index in the array -int indexGreen = 1; -int indexBlue = 2; - -int n = 0; - -int x_offset = (500 - tft_width) /2; // Image offsets in the window -int y_offset = 20; - -int xpos = 0, ypos = 0; // Current pixel position - -int beginTime = 0; -int pixelWaitTime = 1000; // Maximum 1000ms wait for image pixels to arrive -int lastPixelTime = 0; // Time that "image send" command was sent - -int requestTime = 0; -int requestCount = 0; - -int state = 0; // State machine current state - -int progress_bar = 0; // Console progress bar dot count -int pixel_count = 0; // Number of pixels read for 1 screen -float percentage = 0; // Percentage of pixels received - -int saved_image_count = 0; // Stats - number of images processed -int bad_image_count = 0; // Stats - number of images that had lost pixels -String filename = ""; - -int drawLoopCount = 0; // Used for the fade out - -void setup() { - - size(500, 540); // Stage size, can handle 480 pixels wide screen - noStroke(); // No border on the next thing drawn - noSmooth(); // No anti-aliasing to avoid adjacent pixel colour merging - - // Graded background and title - drawWindow(); - - frameRate(2000); // High frame rate so draw() loops fast - - // Print a list of the available serial ports - println("-----------------------"); - println("Available Serial Ports:"); - println("-----------------------"); - printArray(Serial.list()); - println("-----------------------"); - - print("Port currently used: ["); - print(serial_port); - println("]"); - - String portName = Serial.list()[serial_port]; - - serial = new Serial(this, portName, serial_baud_rate); - - state = 99; -} - -void draw() { - - if (mouseClick) buttonClicked(); - - switch(state) { - - case 0: // Init varaibles, send start request - if (running) { - tint(0, 0, 0, 255); - flushBuffer(); - println(""); - print("Ready: "); - - xpos = 0; - ypos = 0; - serialCount = 0; - progress_bar = 0; - pixel_count = 0; - percentage = 0; - drawLoopCount = frameCount; - lastPixelTime = millis() + 1000; - - state = 1; - } else { - if (millis() > beginTime) { - beginTime = millis() + 500; - dimmed = !dimmed; - if (dimmed) drawButton(buttonDimmed); - else drawButton(buttonStopped); - } - } - break; - - case 1: // Console message, give server some time - print("requesting image "); - serial.write("S"); - delay(10); - beginTime = millis(); - requestTime = millis() + 1000; - requestCount = 1; - state = 2; - break; - - case 2: // Get size and set start time for rendering duration report - if (millis() > requestTime) { - requestCount++; - print("*"); - serial.clear(); - serial.write("S"); - if (requestCount > 32) { - requestCount = 0; - System.err.println(" - no response!"); - state = 0; - } - requestTime = millis() + 1000; - } - if ( getSize() == true ) { // Go to next state when we have the size and bits per pixel - getFilename(); - flushBuffer(); // Precaution in case image header size increases in later versions - lastPixelTime = millis() + 1000; - beginTime = millis(); - state = 3; - } - break; - - case 3: // Request pixels and render returned RGB values - state = renderPixels(); // State will change when all pixels are rendered - - // Request more pixels, changing the number requested allows the average transfer rate to be controlled - // The pixel transfer rate is dependant on four things: - // 1. The frame rate defined in this Processing sketch in setup() - // 2. The baud rate of the serial link (~10 bit periods per byte) - // 3. The number of request bytes 'R' sent in the lines below - // 4. The number of pixels sent in a burst by the server sketch (defined via NPIXELS) - - //serial.write("RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR"); // 32 x NPIXELS more - serial.write("RRRRRRRRRRRRRRRR"); // 16 x NPIXELS more - //serial.write("RRRRRRRR"); // 8 x NPIXELS more - //serial.write("RRRR"); // 4 x NPIXELS more - //serial.write("RR"); // 2 x NPIXELS more - //serial.write("R"); // 1 x NPIXELS more - if (!running) state = 4; - break; - - case 4: // Pixel receive time-out, flush serial buffer - flushBuffer(); - state = 6; - break; - - case 5: // Save the image to the sketch folder (Ctrl+K to access) - saveScreenshot(); - saved_image_count++; - println("Saved image count = " + saved_image_count); - if (bad_image_count > 0) System.err.println(" Bad image count = " + bad_image_count); - drawLoopCount = frameCount; // Reset value ready for counting in step 6 - state = 6; - break; - - case 6: // Fade the old image if enabled - if ( fadedImage() == true ) state = 0; // Go to next state when image has faded - break; - - case 99: // Draw image viewer window - drawWindow(); - delay(50); // Delay here seems to be required for the IDE console to get ready - state = 0; - break; - - default: - println(""); - System.err.println("Error state reached - check sketch!"); - break; - } -} - -void drawWindow() -{ - // Graded background in Arduino colours - for (int i = 0; i < height - 25; i++) { - float inter = map(i, 0, height - 25, 0, 1); - color c = lerpColor(bgcolor1, bgcolor2, inter); - stroke(c); - line(0, i, 500, i); - } - fill(bgcolor2); - rect( 0, height-25, width-1, 24); - textAlign(CENTER); - textSize(20); - fill(0); - text("Bodmer's TFT image viewer", width/2, height-6); - - if (running) drawButton(buttonRunning); - else drawButton(buttonStopped); -} - -void flushBuffer() -{ - //println("Clearing serial pipe after a time-out"); - int clearTime = millis() + 50; - while ( millis() < clearTime ) serial.clear(); -} - -boolean getSize() -{ - if ( serial.available() > 6 ) { - println(); - char code = (char)serial.read(); - if (code == 'W') { - tft_width = serial.read()<<8 | serial.read(); - } - code = (char)serial.read(); - if (code == 'H') { - tft_height = serial.read()<<8 | serial.read(); - } - code = (char)serial.read(); - if (code == 'Y') { - int bits_per_pixel = (char)serial.read(); - if (bits_per_pixel == 24) color_bytes = 3; - else color_bytes = 2; - } - code = (char)serial.read(); - if (code == '?') { - drawWindow(); - - x_offset = (500 - tft_width) /2; - tint(0, 0, 0, 255); - noStroke(); - fill(frameColor); - rect((width - tft_width)/2 - border, y_offset - border, tft_width + 2 * border, tft_height + 2 * border); - return true; - } - } - return false; -} - -void saveScreenshot() -{ - println(); - if (saved_image_count < max_allowed) - { - if (filename == "") filename = "tft_screen_" + (n++); - filename = filename + image_type; - println("Saving image as \"" + filename + "\""); - if (save_border) - { - PImage partialSave = get(x_offset - border, y_offset - border, tft_width + 2*border, tft_height + 2*border); - partialSave.save(filename); - } else { - PImage partialSave = get(x_offset, y_offset, tft_width, tft_height); - partialSave.save(filename); - } - - if (n>=max_images) n = 0; - } - else - { - System.err.println(max_allowed + " saved image count exceeded, restart the sketch"); - } -} - -void getFilename() -{ - int readTime = millis() + 20; - int inByte = 0; - filename = ""; - while ( serial.available() > 0 && millis() < readTime && inByte != '.') - { - inByte = serial.read(); - if (inByte == ' ') inByte = '_'; - if ( unicodeCheck(inByte) ) filename += (char)inByte; - } - - inByte = serial.read(); - if (inByte == '@') filename += "_" + timeCode(); - else if (inByte == '#') filename += "_" + saved_image_count%100; - else if (inByte == '%') filename += "_" + millis(); - else if (inByte != '*') filename = ""; - - inByte = serial.read(); - if (inByte == 'j') image_type =".jpg"; - else if (inByte == 'b') image_type =".bmp"; - else if (inByte == 'p') image_type =".png"; - else if (inByte == 't') image_type =".tif"; -} - -boolean unicodeCheck(int unicode) -{ - if ( unicode >= '0' && unicode <= '9' ) return true; - if ( (unicode >= 'A' && unicode <= 'Z' ) || (unicode >= 'a' && unicode <= 'z')) return true; - if ( unicode == '_' || unicode == '/' ) return true; - return false; -} - -String timeCode() -{ - String timeCode = (int)year() + "_" + (int)month() + "_" + (int)day() + "_"; - timeCode += (int)hour() + "_" + (int)minute() + "_" + (int)second(); - return timeCode; -} - -int renderPixels() -{ - if ( serial.available() > 0 ) { - - // Add the latest byte from the serial port to array: - while (serial.available()>0) - { - rgb[serialCount++] = serial.read(); - - // If we have 3 colour bytes: - if ( serialCount >= color_bytes ) { - serialCount = 0; - pixel_count++; - if (color_bytes == 3) - { - stroke(rgb[indexRed], rgb[indexGreen], rgb[indexBlue], 1000); - } else - { // Can cater for various byte orders - //stroke( (rgb[0] & 0x1F)<<3, (rgb[0] & 0xE0)>>3 | (rgb[1] & 0x07)<<5, (rgb[1] & 0xF8)); - //stroke( (rgb[1] & 0x1F)<<3, (rgb[1] & 0xE0)>>3 | (rgb[0] & 0x07)<<5, (rgb[0] & 0xF8)); - stroke( (rgb[0] & 0xF8), (rgb[1] & 0xE0)>>3 | (rgb[0] & 0x07)<<5, (rgb[1] & 0x1F)<<3); - //stroke( (rgb[1] & 0xF8), (rgb[0] & 0xE0)>>3 | (rgb[1] & 0x07)<<5, (rgb[0] & 0x1F)<<3); - } - // We get some pixel merge aliasing if smooth() is defined, so draw pixel twice - point(xpos + x_offset, ypos + y_offset); - //point(xpos + x_offset, ypos + y_offset); - - lastPixelTime = millis(); - xpos++; - if (xpos >= tft_width) { - xpos = 0; - progressBar(); - ypos++; - if (ypos>=tft_height) { - ypos = 0; - if ((int)percentage <100) { - while (progress_bar++ < 64) print(" "); - percent(100); - } - println("Image fetch time = " + (millis()-beginTime)/1000.0 + " s"); - return 5; - } - } - } - } - } else - { - if (millis() > (lastPixelTime + pixelWaitTime)) - { - println(""); - System.err.println(pixelWaitTime + "ms time-out for pixels exceeded..."); - if (pixel_count > 0) { - bad_image_count++; - System.err.print("Pixels missing = " + (tft_width * tft_height - pixel_count)); - System.err.println(", corrupted image not saved"); - System.err.println("Good image count = " + saved_image_count); - System.err.println(" Bad image count = " + bad_image_count); - } - return 4; - } - } - return 3; -} - -void progressBar() -{ - progress_bar++; - print("."); - if (progress_bar >63) - { - progress_bar = 0; - percentage = 0.5 + 100 * pixel_count/(0.001 + tft_width * tft_height); - percent(percentage); - } -} - -void percent(float percentage) -{ - if (percentage > 100) percentage = 100; - println(" [ " + (int)percentage + "% ]"); - textAlign(LEFT); - textSize(16); - noStroke(); - fill(bgcolor2); - rect(10, height - 25, 70, 20); - fill(0); - text(" [ " + (int)percentage + "% ]", 10, height-8); -} - -boolean fadedImage() -{ - int opacity = frameCount - drawLoopCount; // So we get increasing fade - if (fade) - { - tint(255, opacity); - //image(tft_img, x_offset, y_offset); - noStroke(); - fill(50, 50, 50, opacity); - rect( (width - tft_width)/2, y_offset, tft_width, tft_height); - delay(10); - } - if (opacity > 50) // End fade after 50 cycles - { - return true; - } - return false; -} - -void drawButton(color buttonColor) -{ - stroke(0); - fill(buttonColor); - rect(500 - 100, 540 - 26, 80, 24); - textAlign(CENTER); - textSize(20); - fill(0); - if (running) text(" Pause ", 500 - 60, height-7); - else text(" Run ", 500 - 60, height-7); -} - -void buttonClicked() -{ - mouseClick = false; - if (running) { - running = false; - drawButton(buttonStopped); - System.err.println(""); - System.err.println("Stopped - click 'Run' button: "); - //noStroke(); - //fill(50); - //rect( (width - tft_width)/2, y_offset, tft_width, tft_height); - beginTime = millis() + 500; - dimmed = false; - state = 4; - } else { - running = true; - drawButton(buttonRunning); - } -} - -void mousePressed() { - if (mouseX > (500 - 100) && mouseX < (500 - 20) && mouseY > (540 - 26) && mouseY < (540 - 2)) { - mouseClick = true; - } -} - -*/ diff --git a/examples/320 x 240/TFT_Screen_Capture/screenServer.ino b/examples/320 x 240/TFT_Screen_Capture/screenServer.ino deleted file mode 100644 index 962d561..0000000 --- a/examples/320 x 240/TFT_Screen_Capture/screenServer.ino +++ /dev/null @@ -1,194 +0,0 @@ -// Reads a screen image off the TFT and send it to a processing client sketch -// over the serial port. Use a high baud rate, e.g. for an ESP8266: -// Serial.begin(921600); - -// At 921600 baud a 320 x 240 image with 16 bit colour transfers can be sent to the -// PC client in ~1.67s and 24 bit colour in ~2.5s which is close to the theoretical -// minimum transfer time. - -// This sketch has been created to work with the TFT_eSPI library here: -// https://github.com/Bodmer/TFT_eSPI - -// Created by: Bodmer 27/1/17 -// Updated by: Bodmer 10/3/17 -// Version: 0.07 - -// MIT licence applies, all text above must be included in derivative works - -//==================================================================================== -// Definitions -//==================================================================================== -#define BAUD_RATE 250000 // Maximum Serial Monitor rate for other messages -#define DUMP_BAUD_RATE 921600 // Rate used for screen dumps - -#define PIXEL_TIMEOUT 100 // 100ms Time-out between pixel requests -#define START_TIMEOUT 10000 // 10s Maximum time to wait at start transfer - -#define BITS_PER_PIXEL 16 // 24 for RGB colour format, 16 for 565 colour format - -// File names must be alpha-numeric characters (0-9, a-z, A-Z) or "/" underscore "_" -// other ascii characters are stripped out by client, including / generates -// sub-directories -#define DEFAULT_FILENAME "tft_screenshots/screenshot" // In case none is specified -#define FILE_TYPE "png" // jpg, bmp, png, tif are valid - -// Filename extension -// '#' = add 0-9, '@' = add timestamp, '%' add millis() timestamp, '*' = add nothing -// '@' and '%' will generate new unique filenames, so beware of cluttering up your -// hard drive with lots of images! The PC client sketch is set to limit the number of -// saved images to 1000 and will then prompt for a restart. -#define FILE_EXT '%' - -// Number of pixels to send in a burst (minimum of 1), no benefit above 8 -// NPIXELS values and render times: 1 = 5.0s, 2 = 1.75s, 4 = 1.68s, 8 = 1.67s -#define NPIXELS 8 // Must be integer division of both TFT width and TFT height - -//==================================================================================== -// Screen server call with no filename -//==================================================================================== -// Start a screen dump server (serial or network) - no filename specified -boolean screenServer(void) -{ - // With no filename the screenshot will be saved with a default name e.g. tft_screen_#.xxx - // where # is a number 0-9 and xxx is a file type specified below - return screenServer(DEFAULT_FILENAME); -} - -//==================================================================================== -// Screen server call with filename -//==================================================================================== -// Start a screen dump server (serial or network) - filename specified -boolean screenServer(String filename) -{ - Serial.end(); // Stop the serial port (clears buffers too) - Serial.begin(DUMP_BAUD_RATE); // Force baud rate to be high - delay(0); // Equivalent to yield() for ESP8266; - - boolean result = serialScreenServer(filename); // Screenshot serial port server - //boolean result = wifiScreenServer(filename); // Screenshot WiFi UDP port server (WIP) - - Serial.end(); // Stop the serial port (clears buffers too) - Serial.begin(BAUD_RATE); // Return baud rate to normal - delay(0); // Equivalent to yield() for ESP8266; - - //Serial.println(); - //if (result) Serial.println(F("Screen dump passed :-)")); - //else Serial.println(F("Screen dump failed :-(")); - - return result; -} - -//==================================================================================== -// Serial server function that sends the data to the client -//==================================================================================== -boolean serialScreenServer(String filename) -{ - // Precautionary receive buffer garbage flush for 50ms - uint32_t clearTime = millis() + 50; - while ( millis() < clearTime && Serial.read() >= 0) delay(0); // Equivalent to yield() for ESP8266; - - boolean wait = true; - uint32_t lastCmdTime = millis(); // Initialise start of command time-out - - // Wait for the starting flag with a start time-out - while (wait) - { - delay(0); // Equivalent to yield() for ESP8266; - // Check serial buffer - if (Serial.available() > 0) { - // Read the command byte - uint8_t cmd = Serial.read(); - // If it is 'S' (start command) then clear the serial buffer for 100ms and stop waiting - if ( cmd == 'S' ) { - // Precautionary receive buffer garbage flush for 50ms - clearTime = millis() + 50; - while ( millis() < clearTime && Serial.read() >= 0) delay(0); // Equivalent to yield() for ESP8266; - - wait = false; // No need to wait anymore - lastCmdTime = millis(); // Set last received command time - - // Send screen size etc using a simple header with delimiters for client checks - sendParameters(filename); - } - } - else - { - // Check for time-out - if ( millis() > lastCmdTime + START_TIMEOUT) return false; - } - } - - uint8_t color[3 * NPIXELS]; // RGB and 565 format color buffer for N pixels - - // Send all the pixels on the whole screen - for ( uint32_t y = 0; y < tft.height(); y++) - { - // Increment x by NPIXELS as we send NPIXELS for every byte received - for ( uint32_t x = 0; x < tft.width(); x += NPIXELS) - { - delay(0); // Equivalent to yield() for ESP8266; - - // Wait here for serial data to arrive or a time-out elapses - while ( Serial.available() == 0 ) - { - if ( millis() > lastCmdTime + PIXEL_TIMEOUT) return false; - delay(0); // Equivalent to yield() for ESP8266; - } - - // Serial data must be available to get here, read 1 byte and - // respond with N pixels, i.e. N x 3 RGB bytes or N x 2 565 format bytes - if ( Serial.read() == 'X' ) { - // X command byte means abort, so clear the buffer and return - clearTime = millis() + 50; - while ( millis() < clearTime && Serial.read() >= 0) delay(0); // Equivalent to yield() for ESP8266; - return false; - } - // Save arrival time of the read command (for later time-out check) - lastCmdTime = millis(); - -#if defined BITS_PER_PIXEL && BITS_PER_PIXEL >= 24 - // Fetch N RGB pixels from x,y and put in buffer - tft.readRectRGB(x, y, NPIXELS, 1, color); - // Send buffer to client - Serial.write(color, 3 * NPIXELS); // Write all pixels in the buffer -#else - // Fetch N 565 format pixels from x,y and put in buffer - tft.readRect(x, y, NPIXELS, 1, (uint16_t *)color); - // Send buffer to client - Serial.write(color, 2 * NPIXELS); // Write all pixels in the buffer -#endif - } - } - - Serial.flush(); // Make sure all pixel bytes have been despatched - - return true; -} - -//==================================================================================== -// Send screen size etc using a simple header with delimiters for client checks -//==================================================================================== -void sendParameters(String filename) -{ - Serial.write('W'); // Width - Serial.write(tft.width() >> 8); - Serial.write(tft.width() & 0xFF); - - Serial.write('H'); // Height - Serial.write(tft.height() >> 8); - Serial.write(tft.height() & 0xFF); - - Serial.write('Y'); // Bits per pixel (16 or 24) - Serial.write(BITS_PER_PIXEL); - - Serial.write('?'); // Filename next - Serial.print(filename); - - Serial.write('.'); // End of filename marker - - Serial.write(FILE_EXT); // Filename extension identifier - - Serial.write(*FILE_TYPE); // First character defines file type j,b,p,t -} - - From beda811a3aed4bcaa6a53a98716fd28c80725179 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Sun, 25 Nov 2018 19:41:29 +0000 Subject: [PATCH 216/287] Add R61581 driver as detailed in #238 Not tested by Bodmer, tested by s60sc as in #238 --- TFT_Drivers/R61581_Defines.h | 42 ++++++++++++++++++ TFT_Drivers/R61581_Init.h | 80 +++++++++++++++++++++++++++++++++++ TFT_Drivers/R61581_Rotation.h | 27 ++++++++++++ TFT_eSPI.cpp | 6 +++ TFT_eSPI.h | 2 +- User_Setup.h | 1 + User_Setup_Select.h | 18 +++++--- User_Setups/SetupX_Template.h | 1 + library.json | 2 +- library.properties | 2 +- 10 files changed, 171 insertions(+), 10 deletions(-) create mode 100644 TFT_Drivers/R61581_Defines.h create mode 100644 TFT_Drivers/R61581_Init.h create mode 100644 TFT_Drivers/R61581_Rotation.h diff --git a/TFT_Drivers/R61581_Defines.h b/TFT_Drivers/R61581_Defines.h new file mode 100644 index 0000000..bd5fb88 --- /dev/null +++ b/TFT_Drivers/R61581_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/R61581_Init.h b/TFT_Drivers/R61581_Init.h new file mode 100644 index 0000000..929d680 --- /dev/null +++ b/TFT_Drivers/R61581_Init.h @@ -0,0 +1,80 @@ + +// This is the command sequence that initialises the R61581 driver +// +// This setup information uses simple 8 bit SPI writecommand() and writedata() functions +// +// See ST7735_Setup.h file for an alternative format + + +// Configure R61581 display + + writecommand(TFT_SLPOUT); + delay(20); + + writecommand(0xB0); + writedata(0x00); + + 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(0x12); + 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 R61581 display configuration + + + diff --git a/TFT_Drivers/R61581_Rotation.h b/TFT_Drivers/R61581_Rotation.h new file mode 100644 index 0000000..4d7dc61 --- /dev/null +++ b/TFT_Drivers/R61581_Rotation.h @@ -0,0 +1,27 @@ + // This is the command sequence that rotates the R61581 driver coordinate frame + + writecommand(TFT_MADCTL); + rotation = m % 4; + switch (rotation) { + case 0: // Portrait + writedata(TFT_MAD_BGR | TFT_MAD_MX); + _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_MX | TFT_MAD_GS); + _width = TFT_HEIGHT; + _height = TFT_WIDTH; + break; + } + \ No newline at end of file diff --git a/TFT_eSPI.cpp b/TFT_eSPI.cpp index 92873da..0b9fc21 100644 --- a/TFT_eSPI.cpp +++ b/TFT_eSPI.cpp @@ -361,6 +361,9 @@ void TFT_eSPI::init(uint8_t tc) #elif defined (ST7789_DRIVER) #include "TFT_Drivers/ST7789_Init.h" +#elif defined (R61581_DRIVER) + #include "TFT_Drivers/R61581_Init.h" + #endif spi_end(); @@ -409,6 +412,9 @@ void TFT_eSPI::setRotation(uint8_t m) #elif defined (ST7789_DRIVER) #include "TFT_Drivers/ST7789_Rotation.h" +#elif defined (R61581_DRIVER) + #include "TFT_Drivers/R61581_Rotation.h" + #endif delayMicroseconds(10); diff --git a/TFT_eSPI.h b/TFT_eSPI.h index 8f31f29..1dcb28c 100644 --- a/TFT_eSPI.h +++ b/TFT_eSPI.h @@ -330,7 +330,7 @@ SET_PERI_REG_MASK(SPI_CMD_REG(SPI_NUM), SPI_USR); \ while (READ_PERI_REG(SPI_CMD_REG(SPI_NUM))&SPI_USR); - // Write 16 bits (used for ESP32_PARALLEL or ILI9488_DRIVER) + // Write 16 bits #define tft_Write_16S(C) \ WRITE_PERI_REG(SPI_MOSI_DLEN_REG(SPI_NUM), 16-1); \ WRITE_PERI_REG(SPI_W0_REG(SPI_NUM), C); \ diff --git a/User_Setup.h b/User_Setup.h index 49107c0..5c48d70 100644 --- a/User_Setup.h +++ b/User_Setup.h @@ -25,6 +25,7 @@ //#define ILI9486_DRIVER //#define ILI9488_DRIVER //#define ST7789_DRIVER // Define the screen size below for this display +//#define R61581_DRIVER // Some displays support SPI reads via the MISO pin, if the display has a single // bi-directional SDA pin the library will try to use bit banging to read the line diff --git a/User_Setup_Select.h b/User_Setup_Select.h index bd31358..69d27a0 100644 --- a/User_Setup_Select.h +++ b/User_Setup_Select.h @@ -25,13 +25,14 @@ //#include // Setup file configured for my ST7735 //#include // Setup file configured for my ILI9163 //#include // Setup file configured for my S6D02A1 -//#include // Setup file configured for my stock RPi TFT -//#include // Setup file configured for my modified RPi TFT -//#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 ESP8266 and RPi TFT with touch -//#include // Setup file configured for ESP32 and RPi TFT with touch +//#include // Setup file configured for my stock RPi TFT +//#include // Setup file configured for my modified RPi TFT +//#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 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 @@ -102,6 +103,9 @@ #elif defined (ST7789_DRIVER) #include "TFT_Drivers/ST7789_Defines.h" #define TFT_DRIVER 0x7789 +#elif defined (R61581_DRIVER) + #include "TFT_Drivers/R61581_Defines.h" + #define TFT_DRIVER 0x6158 #elif defined (XYZZY_DRIVER) // <<<<<<<<<<<<<<<<<<<<<<<< ADD NEW DRIVER HERE #include "TFT_Drivers/XYZZY_Defines.h" #define TFT_DRIVER 0x0000 diff --git a/User_Setups/SetupX_Template.h b/User_Setups/SetupX_Template.h index 3052b69..11cf2ab 100644 --- a/User_Setups/SetupX_Template.h +++ b/User_Setups/SetupX_Template.h @@ -25,6 +25,7 @@ //#define ILI9486_DRIVER //#define ILI9488_DRIVER //#define ST7789_DRIVER // Define the screen size below for this display +//#define R61581_DRIVER // Some displays support SPI reads via the MISO pin, if the display has a single // bi-directional SDA pin the library will try to use bit banging to read the line diff --git a/library.json b/library.json index 94d5340..83de892 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "TFT_eSPI", - "version": "1.3.4", + "version": "1.3.5", "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 b8b5f01..9f33bfb 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=TFT_eSPI -version=1.3.4 +version=1.3.5 author=Bodmer maintainer=Bodmer sentence=A fast TFT graphics library for ESP8266 and ESP32 processors for the Arduino IDE From 467d1dc3c51f4ede6ac2cdf82a2c493d5eb6c243 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Mon, 26 Nov 2018 19:50:52 +0000 Subject: [PATCH 217/287] Fix issue #260 --- TFT_eSPI.h | 2 +- library.json | 2 +- library.properties | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/TFT_eSPI.h b/TFT_eSPI.h index 1dcb28c..78c1816 100644 --- a/TFT_eSPI.h +++ b/TFT_eSPI.h @@ -228,7 +228,7 @@ #define SPI_32(H,L) ( (H)<<16 | (L) ) #define COL_32(H,L) ( (H)<<16 | (L) ) #else - #ifdef ESP32_PARALLEL + #ifdef ESP32_PARALLEL || defined (ILI9488_DRIVER) #define SPI_32(H,L) ( (H)<<16 | (L) ) #else #define SPI_32(H,L) ( ((H)<<8 | (H)>>8) | (((L)<<8 | (L)>>8)<<16 ) ) diff --git a/library.json b/library.json index 83de892..6eaff8a 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "TFT_eSPI", - "version": "1.3.5", + "version": "1.3.6", "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 9f33bfb..b3ab068 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=TFT_eSPI -version=1.3.5 +version=1.3.6 author=Bodmer maintainer=Bodmer sentence=A fast TFT graphics library for ESP8266 and ESP32 processors for the Arduino IDE From ff691f82030b77a6f322d0af3c5c74671e641ece Mon Sep 17 00:00:00 2001 From: Bodmer Date: Mon, 26 Nov 2018 21:39:42 +0000 Subject: [PATCH 218/287] Add News --- README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/README.md b/README.md index bb3dadb..523f307 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,12 @@ + +# News + +1. androdlang has published a really nice companion library to extend the graphics capabilities of TFT_eSPI, you can find this here: +https://github.com/androdlang/TFTShape + +2. I have created a user updateable graphics extension library template that can be used to create your own graphics extensions. The Library contains examples and is commented so it should be clear what you need to do to add functions. You can find it here: +https://github.com/Bodmer/TFT_eFX + # TFT_eSPI An Arduino IDE compatible graphics and fonts library for ESP8266 and ESP32 processors with drivers for ILI9341, ILI9163, ST7735, S6D02A1, ILI9481, ILI9486, ILI9488, HX8357D and ST7789 based TFT displays that support SPI. The library can be loaded using the Arduino IDE's Library Manager. From 47e5a15604809e5bd6d25adffad4d0c136e2319c Mon Sep 17 00:00:00 2001 From: Bodmer Date: Tue, 27 Nov 2018 21:18:01 +0000 Subject: [PATCH 219/287] Add news --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 523f307..220212e 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,12 @@ https://github.com/androdlang/TFTShape 2. I have created a user updateable graphics extension library template that can be used to create your own graphics extensions. The Library contains examples and is commented so it should be clear what you need to do to add functions. You can find it here: https://github.com/Bodmer/TFT_eFX +3. The capability to read from an ST7789V TFT with a single bidirectional SDA pin has been added. This is enabled with a #define TFT_SDA_READ in the setup file. + +4. ST7789V displays are manufactured in two variants that have the red and green pixels swapped, this is catered for by a new option in the setup file: + //#define TFT_RGB_ORDER TFT_RGB // Colour order Red-Green-Blue + //#define TFT_RGB_ORDER TFT_BGR // Colour order Blue-Green-Red + # TFT_eSPI An Arduino IDE compatible graphics and fonts library for ESP8266 and ESP32 processors with drivers for ILI9341, ILI9163, ST7735, S6D02A1, ILI9481, ILI9486, ILI9488, HX8357D and ST7789 based TFT displays that support SPI. The library can be loaded using the Arduino IDE's Library Manager. From 5d496ca7ee493afa100d70edf6e3b3eec5c36efb Mon Sep 17 00:00:00 2001 From: Bodmer Date: Tue, 27 Nov 2018 21:19:40 +0000 Subject: [PATCH 220/287] State SDA read constraint is only for ESP32 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 220212e..2d557b2 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ https://github.com/androdlang/TFTShape 2. I have created a user updateable graphics extension library template that can be used to create your own graphics extensions. The Library contains examples and is commented so it should be clear what you need to do to add functions. You can find it here: https://github.com/Bodmer/TFT_eFX -3. The capability to read from an ST7789V TFT with a single bidirectional SDA pin has been added. This is enabled with a #define TFT_SDA_READ in the setup file. +3. The capability to read from an ST7789V TFT with a single bidirectional SDA pin has been added. At the moment this **ONLY** works with an ESP32. It is enabled with a #define TFT_SDA_READ in the setup file. 4. ST7789V displays are manufactured in two variants that have the red and green pixels swapped, this is catered for by a new option in the setup file: //#define TFT_RGB_ORDER TFT_RGB // Colour order Red-Green-Blue From d23f0b00c0efcafcfe9d919a6995012b89a0ea94 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Wed, 28 Nov 2018 01:02:15 +0000 Subject: [PATCH 221/287] Correct typo causing error message --- TFT_eSPI.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TFT_eSPI.h b/TFT_eSPI.h index 78c1816..26169f9 100644 --- a/TFT_eSPI.h +++ b/TFT_eSPI.h @@ -228,7 +228,7 @@ #define SPI_32(H,L) ( (H)<<16 | (L) ) #define COL_32(H,L) ( (H)<<16 | (L) ) #else - #ifdef ESP32_PARALLEL || defined (ILI9488_DRIVER) + #if defined (ESP32_PARALLEL) || defined (ILI9488_DRIVER) #define SPI_32(H,L) ( (H)<<16 | (L) ) #else #define SPI_32(H,L) ( ((H)<<8 | (H)>>8) | (((L)<<8 | (L)>>8)<<16 ) ) From 436476e9bd24e2dc75fe6a747317afc226e23217 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Wed, 28 Nov 2018 01:02:54 +0000 Subject: [PATCH 222/287] Update --- library.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library.properties b/library.properties index b3ab068..6585c47 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=TFT_eSPI -version=1.3.6 +version=1.3.7 author=Bodmer maintainer=Bodmer sentence=A fast TFT graphics library for ESP8266 and ESP32 processors for the Arduino IDE From fa808ed8110f543196768f0fad3f0a18d736f80f Mon Sep 17 00:00:00 2001 From: Bodmer Date: Wed, 28 Nov 2018 01:03:16 +0000 Subject: [PATCH 223/287] Update --- library.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library.json b/library.json index 6eaff8a..cee476f 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "TFT_eSPI", - "version": "1.3.6", + "version": "1.3.7", "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": From e0aa312dfa65ba65d7869c573366575beb252f5e Mon Sep 17 00:00:00 2001 From: Bodmer Date: Wed, 28 Nov 2018 22:23:45 +0000 Subject: [PATCH 224/287] Slow down the RPi strobes but keep fast for other displays --- TFT_eSPI.h | 58 ++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 41 insertions(+), 17 deletions(-) diff --git a/TFT_eSPI.h b/TFT_eSPI.h index 26169f9..68bec74 100644 --- a/TFT_eSPI.h +++ b/TFT_eSPI.h @@ -132,16 +132,26 @@ #else #if TFT_DC >= 32 - #define DC_C GPIO.out1_w1tc.val = (1 << (TFT_DC - 32)); //\ - //GPIO.out1_w1tc.val = (1 << (TFT_DC - 32)) - #define DC_D GPIO.out1_w1ts.val = (1 << (TFT_DC - 32)); //\ - //GPIO.out1_w1ts.val = (1 << (TFT_DC - 32)) + #ifdef RPI_ILI9486_DRIVER // RPi display needs a slower DC change + #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.out1_w1tc.val = (1 << (TFT_DC - 32)) + #define DC_D GPIO.out1_w1ts.val = (1 << (TFT_DC - 32)) + #endif #else #if TFT_DC >= 0 - #define DC_C GPIO.out_w1tc = (1 << TFT_DC); //\ - //GPIO.out_w1tc = (1 << TFT_DC) - #define DC_D GPIO.out_w1ts = (1 << TFT_DC); //\ - //GPIO.out_w1ts = (1 << TFT_DC) + #ifdef RPI_ILI9486_DRIVER // RPi display needs a slower DC change + #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 GPIO.out_w1tc = (1 << TFT_DC) + #define DC_D GPIO.out_w1ts = (1 << TFT_DC) + #endif #else #define DC_C #define DC_D @@ -171,14 +181,24 @@ #define CS_H #else #if TFT_CS >= 32 - #define CS_L GPIO.out1_w1tc.val = (1 << (TFT_CS - 32)); //\ - //GPIO.out1_w1tc.val = (1 << (TFT_CS - 32)) - #define CS_H GPIO.out1_w1ts.val = (1 << (TFT_CS - 32)); //\ - //GPIO.out1_w1ts.val = (1 << (TFT_CS - 32)) + #ifdef RPI_ILI9486_DRIVER // RPi display needs a slower CS change + #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.out1_w1tc.val = (1 << (TFT_CS - 32)) + #define CS_H GPIO.out1_w1ts.val = (1 << (TFT_CS - 32)) + #endif #else #if TFT_CS >= 0 - #define CS_L GPIO.out_w1tc = (1 << TFT_CS); //GPIO.out_w1tc = (1 << TFT_CS) - #define CS_H GPIO.out_w1ts = (1 << TFT_CS); //GPIO.out_w1ts = (1 << TFT_CS) + #ifdef RPI_ILI9486_DRIVER // RPi display needs a slower CS change + #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 GPIO.out_w1tc = (1 << TFT_CS) + #define CS_H GPIO.out_w1ts = (1 << TFT_CS) + #endif #else #define CS_L #define CS_H @@ -194,8 +214,12 @@ // Use single register write for CS_L and DC_C if pins are both in range 0-31 #ifdef ESP32 #if (TFT_CS >= 0) && (TFT_CS < 32) && (TFT_DC >= 0) && (TFT_DC < 32) - #define CS_L_DC_C GPIO.out_w1tc = ((1 << TFT_CS) | (1 << TFT_DC)); //\ - //GPIO.out_w1tc = ((1 << TFT_CS) | (1 << TFT_DC)) + #ifdef RPI_ILI9486_DRIVER // RPi display needs a slower CD and DC change + #define CS_L_DC_C GPIO.out_w1tc = ((1 << TFT_CS) | (1 << TFT_DC)); \ + GPIO.out_w1tc = ((1 << TFT_CS) | (1 << TFT_DC)) + #else + #define CS_L_DC_C GPIO.out_w1tc = ((1 << TFT_CS) | (1 << TFT_DC)) + #endif #else #define CS_L_DC_C CS_L; DC_C #endif @@ -261,7 +285,7 @@ #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 + GPIO.out_w1tc = clr_mask; GPIO.out_w1ts = set_mask((uint8_t)(C >> 0)); WR_H #endif // 16 bit write with swapped bytes From 057fceb7147546f1755c88d41161d6b9950d0342 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Wed, 28 Nov 2018 22:31:47 +0000 Subject: [PATCH 225/287] Raise version --- library.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library.properties b/library.properties index 6585c47..664863a 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=TFT_eSPI -version=1.3.7 +version=1.3.8 author=Bodmer maintainer=Bodmer sentence=A fast TFT graphics library for ESP8266 and ESP32 processors for the Arduino IDE From b3d16984b099d2099e795b335641f99f59bd6a43 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Wed, 28 Nov 2018 22:32:14 +0000 Subject: [PATCH 226/287] Raise version --- library.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library.json b/library.json index cee476f..af139eb 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "TFT_eSPI", - "version": "1.3.7", + "version": "1.3.8", "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": From 1a5e34b6bbe08d0e63bc0ae3bc09552ba953913b Mon Sep 17 00:00:00 2001 From: Bodmer Date: Thu, 29 Nov 2018 20:29:20 +0000 Subject: [PATCH 227/287] Correct typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2d557b2..dad0286 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ https://github.com/Bodmer/TFT_eFX 3. The capability to read from an ST7789V TFT with a single bidirectional SDA pin has been added. At the moment this **ONLY** works with an ESP32. It is enabled with a #define TFT_SDA_READ in the setup file. -4. ST7789V displays are manufactured in two variants that have the red and green pixels swapped, this is catered for by a new option in the setup file: +4. ST7789V displays are manufactured in two variants that have the red and blue pixels swapped, this is catered for by a new option in the setup file: //#define TFT_RGB_ORDER TFT_RGB // Colour order Red-Green-Blue //#define TFT_RGB_ORDER TFT_BGR // Colour order Blue-Green-Red From 41dea61bfdf78233ef024c122c662b053b6c6d56 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Fri, 30 Nov 2018 23:27:10 +0000 Subject: [PATCH 228/287] Correct #263, add setup file for 160x80 TFT, add invert option Fixed bug where wrong wodth was returned for 1bpp Sprite Improved speed for copying a 1bpp Sprite into another 1bpp Sprite ( affects Sprite pushImage() function only) Added option to setup for colour inversion --- Extensions/Sprite.cpp | 15 ++- TFT_eSPI.cpp | 8 ++ User_Setup.h | 5 + User_Setups/Setup43_ST7735.h | 228 +++++++++++++++++++++++++++++++++++ library.json | 2 +- library.properties | 2 +- 6 files changed, 250 insertions(+), 10 deletions(-) create mode 100644 User_Setups/Setup43_ST7735.h diff --git a/Extensions/Sprite.cpp b/Extensions/Sprite.cpp index aef6010..322cbeb 100644 --- a/Extensions/Sprite.cpp +++ b/Extensions/Sprite.cpp @@ -340,17 +340,16 @@ void TFT_eSprite::pushImage(int32_t x, int32_t y, uint32_t w, uint32_t h, uint1 y = _dheight - tx - 1; } - uint8_t* pdata = (uint8_t*) data; + uint8_t* pdata = (uint8_t* ) data; uint32_t ww = (w+7) & 0xFFF8; for (int32_t yp = 0; yp>3; + uint32_t yyp = y + yp; + for (int32_t xp = 0; xp>3) + yw] & (0x80 >> (xp & 0x7)) ); + drawPixel(x+xp, yyp, readPixel); } } } @@ -732,7 +731,7 @@ int16_t TFT_eSprite::width(void) if (_rotation == 1 || _rotation == 3) return _dheight; - return _bitwidth; + return _dwidth; } diff --git a/TFT_eSPI.cpp b/TFT_eSPI.cpp index 0b9fc21..4f47950 100644 --- a/TFT_eSPI.cpp +++ b/TFT_eSPI.cpp @@ -366,6 +366,14 @@ void TFT_eSPI::init(uint8_t tc) #endif +#ifdef TFT_INVERSION_ON + writecommand(TFT_INVON); +#endif + +#ifdef TFT_INVERSION_OFF + writecommand(TFT_INVOFF); +#endif + spi_end(); setRotation(rotation); diff --git a/User_Setup.h b/User_Setup.h index 5c48d70..b27e1a7 100644 --- a/User_Setup.h +++ b/User_Setup.h @@ -65,6 +65,11 @@ // #define ST7735_BLACKTAB // #define ST7735_REDTAB160x80 // For 160 x 80 display (24 offset) (https://www.aliexpress.com/item/ShengYang-1pcs-IPS-0-96-inch-7P-SPI-HD-65K-Full-Color-OLED-Module-ST7735-Drive/32918394604.html) +// If colours are inverted (white shows as black) then uncomment one of the next +// 2 lines try both options, one of the options should correct the inversion. +//#define TFT_INVERSION_ON +//#define TFT_INVERSION_OFF + // ################################################################################## // // Section 1. Define the pins that are used to interface with the display here diff --git a/User_Setups/Setup43_ST7735.h b/User_Setups/Setup43_ST7735.h new file mode 100644 index 0000000..efe20f3 --- /dev/null +++ b/User_Setups/Setup43_ST7735.h @@ -0,0 +1,228 @@ +// 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 + + +// 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 80 +//#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 + +#define ST7735_GREENTAB160x80 +//#define ST7735_REDTAB160x80 + +// If colours are inverted (white shows as black) then uncomment one of these +// Try both options, one of the options should correct the inversion. +//#define TFT_INVERSION_ON +//#define TFT_INVERSION_OFF + +// ################################################################################## +// +// 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 + +//#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 (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 diff --git a/library.json b/library.json index af139eb..6684cb1 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "TFT_eSPI", - "version": "1.3.8", + "version": "1.3.9", "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 664863a..c3dbd0f 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=TFT_eSPI -version=1.3.8 +version=1.3.9 author=Bodmer maintainer=Bodmer sentence=A fast TFT graphics library for ESP8266 and ESP32 processors for the Arduino IDE From 2a02c81c692e015534ca3579b31b25418431e759 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Mon, 3 Dec 2018 21:09:54 +0000 Subject: [PATCH 229/287] Fix #267 --- TFT_eSPI.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TFT_eSPI.cpp b/TFT_eSPI.cpp index 4f47950..6daac3a 100644 --- a/TFT_eSPI.cpp +++ b/TFT_eSPI.cpp @@ -4803,7 +4803,7 @@ void writeBlock(uint16_t color, uint32_t repeat) #elif defined (ILI9488_DRIVER) #ifdef ESP8266 -void TFT_eSPI::writeBlock(uint16_t color, uint32_t repeat) +void writeBlock(uint16_t color, uint32_t repeat) { uint32_t mask = ~(SPIMMOSI << SPILMOSI); From 71a222f704768f450c07c5a9251d64b300c0c22f Mon Sep 17 00:00:00 2001 From: Bodmer Date: Wed, 26 Dec 2018 16:46:54 +0000 Subject: [PATCH 230/287] Fix #276 Fixes warning/error that is only reported with certain compiler warning flag settings ("All" in Arduino IDE). --- TFT_eSPI.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/TFT_eSPI.cpp b/TFT_eSPI.cpp index 6daac3a..2513076 100644 --- a/TFT_eSPI.cpp +++ b/TFT_eSPI.cpp @@ -1251,7 +1251,7 @@ void TFT_eSPI::pushImage(int32_t x, int32_t y, uint32_t w, uint32_t h, uint8_t * //else drawPixel((dw-len)+xp,h-dh,bitmap_bg); xp++; } - *ptr++; + ptr++; len -= 8; } @@ -1400,7 +1400,7 @@ void TFT_eSPI::pushImage(int32_t x, int32_t y, uint32_t w, uint32_t h, uint8_t * px++; xp++; } - *ptr++; + ptr++; len -= 8; } if (np) pushColor(bitmap_fg, np); From e8c201db5e7be8bf7c14cdc90c20151e6ae1f268 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Tue, 1 Jan 2019 23:56:08 +0000 Subject: [PATCH 231/287] Update touch handler Added new function that sketch can call to convert raw ADC x and y values to calibrated screen coordinates: tft.convertRawXY(&x, &y); Redunced required number of SPI transfers needed to read raw values. Changed mode so PENIRQ pin is enabled so users sketch can read this signal (if needed). See issue #279. --- Extensions/Touch.cpp | 65 ++++++++++++++++++++++++-------------------- Extensions/Touch.h | 19 +++++++++---- library.json | 2 +- library.properties | 2 +- 4 files changed, 52 insertions(+), 36 deletions(-) diff --git a/Extensions/Touch.cpp b/Extensions/Touch.cpp index 1b0192d..ab76771 100644 --- a/Extensions/Touch.cpp +++ b/Extensions/Touch.cpp @@ -22,24 +22,18 @@ uint8_t TFT_eSPI::getTouchRaw(uint16_t *x, uint16_t *y){ T_CS_L; - // Dummy transfer - SPI.transfer(0xd0); - SPI.transfer(0); - SPI.transfer(0); - - // Start bit + YP sample request for x position - SPI.transfer(0xd0); - tmp = SPI.transfer(0); + // Start YP sample request for x position + SPI.transfer(0xd0); // Start new YP conversion + tmp = SPI.transfer(0); // Read first 8 bits tmp = tmp <<5; - tmp |= 0x1f & (SPI.transfer(0)>>3); + tmp |= 0x1f & (SPI.transfer(0x90)>>3); // Read last 8 bits and start new XP conversion *x = tmp; - // Start bit + XP sample request for y position - SPI.transfer(0x90); - tmp = SPI.transfer(0); + // Start XP sample request for y position + tmp = SPI.transfer(0); // Read first 8 bits tmp = tmp <<5; - tmp |= 0x1f & (SPI.transfer(0)>>3); + tmp |= 0x1f & (SPI.transfer(0)>>3); // Read last 8 bits *y = tmp; @@ -61,12 +55,12 @@ uint16_t TFT_eSPI::getTouchRawZ(void){ T_CS_L; - // Z sample request + // Calculate Z int16_t tz = 0xFFF; - SPI.transfer(0xb1); - tz += SPI.transfer16(0xc1) >> 3; - tz -= SPI.transfer16(0x91) >> 3; - + SPI.transfer(0xb0); // Start new Z1 conversion + tz += SPI.transfer16(0xc0) >> 3; // Read Z1 and start Z2 conversion + tz -= SPI.transfer16(0x00) >> 3; // Read Z2 + T_CS_H; spi_end_touch(); @@ -78,11 +72,11 @@ uint16_t TFT_eSPI::getTouchRawZ(void){ ** Function name: validTouch ** Description: read validated position. Return false if not pressed. ***************************************************************************************/ -#define _RAWERR 10 // Deadband in position samples +#define _RAWERR 10 // Deadband error allowed in successive 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 + // Wait until pressure stops increasing to debounce pressure uint16_t z1 = 1; uint16_t z2 = 0; while (z1 > z2) @@ -125,7 +119,7 @@ uint8_t TFT_eSPI::validTouch(uint16_t *x, uint16_t *y, uint16_t threshold){ ***************************************************************************************/ #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; + uint16_t x_tmp, y_tmp; if (threshold<20) threshold = 20; if (_pressTime > millis()) threshold=20; @@ -141,6 +135,25 @@ uint8_t TFT_eSPI::getTouch(uint16_t *x, uint16_t *y, uint16_t threshold){ _pressTime = millis() + 50; + convertRawXY(&x_tmp, &y_tmp); + + if (x_tmp >= _width || y_tmp >= _height) return valid; + + _pressX = x_tmp; + _pressY = y_tmp; + *x = _pressX; + *y = _pressY; + return valid; +} + +/*************************************************************************************** +** Function name: convertRawXY +** Description: convert raw touch x,y values to screen coordinates +***************************************************************************************/ +void TFT_eSPI::convertRawXY(uint16_t *x, uint16_t *y) +{ + uint16_t x_tmp = *x, y_tmp = *y, xx, yy; + if(!touchCalibration_rotate){ xx=(x_tmp-touchCalibration_x0)*_width/touchCalibration_x1; yy=(y_tmp-touchCalibration_y0)*_height/touchCalibration_y1; @@ -156,14 +169,8 @@ uint8_t TFT_eSPI::getTouch(uint16_t *x, uint16_t *y, uint16_t threshold){ if(touchCalibration_invert_y) yy = _height - yy; } - - if (xx >= _width || yy >= _height) return valid; - - _pressX = xx; - _pressY = yy; - *x = _pressX; - *y = _pressY; - return valid; + *x = xx; + *y = yy; } /*************************************************************************************** diff --git a/Extensions/Touch.h b/Extensions/Touch.h index 7f3b22a..de4ade7 100644 --- a/Extensions/Touch.h +++ b/Extensions/Touch.h @@ -2,23 +2,32 @@ // This is part of the TFT_eSPI class and is associated with the Touch Screen handlers public: - + // Get raw x,y ADC values from touch controller uint8_t getTouchRaw(uint16_t *x, uint16_t *y); + // Get raw z (i.e. pressure) ADC value from touch controller uint16_t getTouchRawZ(void); + // Convert raw x,y values to calibrated and correctly rotated screen coordinates + void convertRawXY(uint16_t *x, uint16_t *y); + // Get the screen touch coordinates, returns true if screen has been touched + // if the touch cordinates are off screen then x and y are not updated uint8_t getTouch(uint16_t *x, uint16_t *y, uint16_t threshold = 600); + // Run screen calibration and test, report calibration values to the serial port void calibrateTouch(uint16_t *data, uint32_t color_fg, uint32_t color_bg, uint8_t size); + // Set the screen calibration values void setTouch(uint16_t *data); private: - + // Handlers for the SPI settings and clock speed change inline void spi_begin_touch() __attribute__((always_inline)); inline void spi_end_touch() __attribute__((always_inline)); - // These are associated with the Touch Screen handlers + // Private function to validate a touch, allow settle time and reduce spurious coordinates 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; + + uint32_t _pressTime; // Press and hold time-out + uint16_t _pressX, _pressY; // For future use (last sampled calibrated coordinates) diff --git a/library.json b/library.json index 6684cb1..5d937f3 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "TFT_eSPI", - "version": "1.3.9", + "version": "1.3.10", "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 c3dbd0f..8517311 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=TFT_eSPI -version=1.3.9 +version=1.3.10 author=Bodmer maintainer=Bodmer sentence=A fast TFT graphics library for ESP8266 and ESP32 processors for the Arduino IDE From 96228cbf449b1a6c63cd6c8cbb7974afc31f95d7 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Sun, 6 Jan 2019 01:14:10 +0000 Subject: [PATCH 232/287] Add Sprite rotation and examples Sprites can be rendered rotated with defined pivot points, 3 new examples have been added "Rotated_Sprite_1/2/3". The FLASH bitmap example has been moved to the Generic examples folder. Boundary checks have been added to pushImage() A new scrolling and wrapping in a Sprite example has been added. --- Extensions/Sprite.cpp | 398 +++++++++++++++--- Extensions/Sprite.h | 29 +- README.md | 13 +- TFT_eSPI.cpp | 49 ++- TFT_eSPI.h | 22 +- User_Setups/SetupX_Template.h | 5 + examples/160 x 128/Flash_Bitmap2/Alert.h | 42 -- examples/160 x 128/Flash_Bitmap2/Close.h | 41 -- .../160 x 128/Flash_Bitmap2/Flash_Bitmap2.ino | 110 ----- examples/160 x 128/Flash_Bitmap2/Info.h | 41 -- .../TFT_Flash_Bitmap/TFT_Flash_Bitmap.ino | 106 ----- examples/480 x 320/Flash_Bitmap/Alert.h | 42 -- examples/480 x 320/Flash_Bitmap/Close.h | 41 -- .../480 x 320/Flash_Bitmap/Flash_Bitmap.ino | 113 ----- examples/480 x 320/Flash_Bitmap/Info.h | 41 -- .../TFT_Flash_Bitmap/Alert.h | 1 - .../TFT_Flash_Bitmap/Close.h | 0 .../TFT_Flash_Bitmap/Info.h | 0 .../TFT_Flash_Bitmap/TFT_Flash_Bitmap.ino | 72 ++++ .../One_bit_Sprite_Demo.ino} | 0 .../One_bit_Yin_Yang.ino} | 0 .../Rotated_Sprite_1/Rotated_Sprite_1.ino | 182 ++++++++ .../Rotated_Sprite_2/Rotated_Sprite_2.ino | 181 ++++++++ .../Rotated_Sprite_3/Rotated_Sprite_3.ino | 140 ++++++ .../Sprite/Rotated_Sprite_3/data/EagleEye.jpg | Bin 0 -> 20838 bytes .../Rotated_Sprite_3/data/Eye_80x64.jpg | Bin 0 -> 9978 bytes .../Sprite_scroll_wrap_1bit.ino | 139 ++++++ keywords.txt | 8 +- library.json | 2 +- library.properties | 2 +- 30 files changed, 1164 insertions(+), 656 deletions(-) delete mode 100644 examples/160 x 128/Flash_Bitmap2/Alert.h delete mode 100644 examples/160 x 128/Flash_Bitmap2/Close.h delete mode 100644 examples/160 x 128/Flash_Bitmap2/Flash_Bitmap2.ino delete mode 100644 examples/160 x 128/Flash_Bitmap2/Info.h delete mode 100644 examples/320 x 240/TFT_Flash_Bitmap/TFT_Flash_Bitmap.ino delete mode 100644 examples/480 x 320/Flash_Bitmap/Alert.h delete mode 100644 examples/480 x 320/Flash_Bitmap/Close.h delete mode 100644 examples/480 x 320/Flash_Bitmap/Flash_Bitmap.ino delete mode 100644 examples/480 x 320/Flash_Bitmap/Info.h rename examples/{320 x 240 => Generic}/TFT_Flash_Bitmap/Alert.h (99%) rename examples/{320 x 240 => Generic}/TFT_Flash_Bitmap/Close.h (100%) rename examples/{320 x 240 => Generic}/TFT_Flash_Bitmap/Info.h (100%) create mode 100644 examples/Generic/TFT_Flash_Bitmap/TFT_Flash_Bitmap.ino rename examples/Sprite/{1_bit_Sprite_Demo/1_bit_Sprite_Demo.ino => One_bit_Sprite_Demo/One_bit_Sprite_Demo.ino} (100%) rename examples/Sprite/{1_bit_Yin_Yang/1_bit_Yin_Yang.ino => One_bit_Yin_Yang/One_bit_Yin_Yang.ino} (100%) create mode 100644 examples/Sprite/Rotated_Sprite_1/Rotated_Sprite_1.ino create mode 100644 examples/Sprite/Rotated_Sprite_2/Rotated_Sprite_2.ino create mode 100644 examples/Sprite/Rotated_Sprite_3/Rotated_Sprite_3.ino create mode 100644 examples/Sprite/Rotated_Sprite_3/data/EagleEye.jpg create mode 100644 examples/Sprite/Rotated_Sprite_3/data/Eye_80x64.jpg create mode 100644 examples/Sprite/Sprite_scroll_wrap_1bit/Sprite_scroll_wrap_1bit.ino diff --git a/Extensions/Sprite.cpp b/Extensions/Sprite.cpp index 322cbeb..e74864b 100644 --- a/Extensions/Sprite.cpp +++ b/Extensions/Sprite.cpp @@ -33,6 +33,9 @@ TFT_eSprite::TFT_eSprite(TFT_eSPI *tft) _xptr = 0; // pushColor coordinate _yptr = 0; + _xpivot = 0; + _ypivot = 0; + this->cursor_y = this->cursor_x = 0; // Text cursor position } @@ -62,43 +65,61 @@ void* TFT_eSprite::createSprite(int16_t w, int16_t h, uint8_t frames) _sh = h; _scolor = TFT_BLACK; + _xpivot = w/2; + _ypivot = h/2; + + _img8 = (uint8_t*) callocSprite(w, h, frames); + _img8_1 = _img8; + _img8_2 = _img8; + _img = (uint16_t*) _img8; + + // This is to make it clear what pointer size is expected to be used + // but casting in the user sketch is needed due to the use of void* + if (_bpp == 1) + { + w = (w+7) & 0xFFF8; + _img8_2 = _img8 + ( (w>>3) * h + 1 ); + } + + if (_img8) + { + _created = true; + return _img8; + } + + return NULL; +} + + +/*************************************************************************************** +** Function name: callocSprite +** Description: Allocate a memory area for the Sprite and return pointer +*************************************************************************************x*/ + +void* TFT_eSprite::callocSprite(int16_t w, int16_t h, uint8_t frames) +{ // 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. + uint8_t* ptr8 = NULL; + if (_bpp == 16) { #if defined (ESP32) && defined (CONFIG_SPIRAM_SUPPORT) - if ( psramFound() ) _img8_1 = ( uint8_t*) ps_calloc(w * h + 1, sizeof(uint16_t)); + if ( psramFound() ) ptr8 = ( uint8_t*) ps_calloc(w * h + 1, sizeof(uint16_t)); else #endif - _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; - } + ptr8 = ( uint8_t*) calloc(w * h + 1, sizeof(uint16_t)); } else if (_bpp == 8) { #if defined (ESP32) && defined (CONFIG_SPIRAM_SUPPORT) - if ( psramFound() ) _img8_1 = ( uint8_t*) ps_calloc(w * h + 1, sizeof(uint8_t)); + if ( psramFound() ) ptr8 = ( uint8_t*) ps_calloc(w * h + 1, sizeof(uint8_t)); else #endif - _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; - } + ptr8 = ( uint8_t*) calloc(w * h + 1, sizeof(uint8_t)); } else // Must be 1 bpp @@ -114,21 +135,13 @@ void* TFT_eSprite::createSprite(int16_t w, int16_t h, uint8_t frames) if (frames > 2) frames = 2; // Currently restricted to 2 frame buffers if (frames < 1) frames = 1; #if defined (ESP32) && defined (CONFIG_SPIRAM_SUPPORT) - if ( psramFound() ) _img8 = ( uint8_t*) ps_calloc(frames * (w>>3) * h + frames, sizeof(uint8_t)); + if ( psramFound() ) ptr8 = ( uint8_t*) ps_calloc(frames * (w>>3) * h + frames, sizeof(uint8_t)); else #endif - _img8 = ( uint8_t*) calloc(frames * (w>>3) * h + frames, sizeof(uint8_t)); - - if (_img8) - { - _created = true; - _img8_1 = _img8; - _img8_2 = _img8 + ( (w>>3) * h + 1 ); - return _img8_1; - } + ptr8 = ( uint8_t*) calloc(frames * (w>>3) * h + frames, sizeof(uint8_t)); } - - return NULL; + + return ptr8; } /*************************************************************************************** @@ -151,8 +164,8 @@ void* TFT_eSprite::frameBuffer(int8_t f) } /*************************************************************************************** -** Function name: setDepth -** Description: Set bits per pixel for colour (8 or 16) +** Function name: setColorDepth +** Description: Set bits per pixel for colour (1, 8 or 16) *************************************************************************************x*/ void* TFT_eSprite::setColorDepth(int8_t b) @@ -175,6 +188,19 @@ void* TFT_eSprite::setColorDepth(int8_t b) return NULL; } + +/*************************************************************************************** +** Function name: getColorDepth +** Description: Get bits per pixel for colour (1, 8 or 16) +*************************************************************************************x*/ + +int8_t TFT_eSprite::getColorDepth(void) +{ + if (_created) return _bpp; + else return 0; +} + + /*************************************************************************************** ** Function name: setBitmapColor ** Description: Set the foreground foreground and background colour @@ -201,6 +227,232 @@ void TFT_eSprite::deleteSprite(void) } +/*************************************************************************************** +** Function name: setPivot +** Description: Set the pivot point in this Sprite +*************************************************************************************x*/ +void TFT_eSprite::setPivot(int16_t x, int16_t y) +{ + _xpivot = x; + _ypivot = y; +} + + +/*************************************************************************************** +** Function name: getPivotX +** Description: Get the x pivot position +***************************************************************************************/ +int16_t TFT_eSprite::getPivotX(void) +{ + return _xpivot; +} + + +/*************************************************************************************** +** Function name: getPivotY +** Description: Get the y pivot position +***************************************************************************************/ +int16_t TFT_eSprite::getPivotY(void) +{ + return _ypivot; +} + + +/*************************************************************************************** +** Function name: pushRotated +** Description: Push a rotated copy of the Sprite to TFT screen +*************************************************************************************x*/ +bool TFT_eSprite::pushRotated(int16_t angle, int32_t transp) +{ + if ( !_created ) return false; + + // Trig values for the rotation + float radAngle = -angle * 0.0174532925; // Convert degrees to radians + float sinra = sin(radAngle); + float cosra = cos(radAngle); + + // Bounding box parameters + int16_t min_x; + int16_t min_y; + int16_t max_x; + int16_t max_y; + + // Get the bounding box of this rotated source Sprite relative to Sprite pivot + getRotatedBounds(sinra, cosra, width(), height(), _xpivot, _ypivot, &min_x, &min_y, &max_x, &max_y); + + // Move bounding box so source Sprite pivot coincides with TFT pivot + min_x += _tft->_xpivot; + max_x += _tft->_xpivot; + min_y += _tft->_ypivot; + max_y += _tft->_ypivot; + + // Test only to show bounding box on TFT + // _tft->drawRect(min_x, min_y, max_x - min_x + 1, max_y - min_y + 1, TFT_GREEN); + + // Return if bounding box is outside of TFT area + if (min_x > _tft->width()) return true; + if (min_y > _tft->height()) return true; + if (max_x < 0) return true; + if (max_y < 0) return true; + + // Clip bounding box to be within TFT area + if (min_x < 0) min_x = 0; + if (min_y < 0) min_y = 0; + if (max_x > _tft->width()) max_x = _tft->width(); + if (max_y > _tft->height()) max_y = _tft->height(); + + + // Scan destination bounding box and fetch transformed pixels from source Sprite + for (int32_t x = min_x; x <= max_x; x++) { + int32_t xt = x - _tft->_xpivot; + float cxt = cosra * xt + _xpivot; + float sxt = sinra * xt + _ypivot; + bool column_drawn = false; + for (int32_t y = min_y; y <= max_y; y++) { + int32_t yt = y - _tft->_ypivot; + int32_t xs = (int32_t)round(cxt - sinra * yt); + // Do not calculate ys unless xs is in bounds + if (xs >= 0 && xs < width()) + { + int32_t ys = (int32_t)round(sxt + cosra * yt); + // Check if ys is in bounds + if (ys >= 0 && ys < height()) { + int32_t rp = readPixel(xs, ys); + if (rp != transp) _tft->drawPixel(x, y, rp); + column_drawn = true; + } + } + else if (column_drawn) y = max_y; // Skip remaining column pixels + } + } + + return true; +} + + +/*************************************************************************************** +** Function name: pushRotated +** Description: Push a rotated copy of the Sprite to another Sprite +*************************************************************************************x*/ +bool TFT_eSprite::pushRotated(TFT_eSprite *spr, int16_t angle, int32_t transp) +{ + if ( !_created ) return false; // Check this Sprite is created + if ( !spr->_created ) return false; // Ckeck destination Sprite is created + + // Trig values for the rotation + float radAngle = -angle * 0.0174532925; // Convert degrees to radians + float sinra = sin(radAngle); + float cosra = cos(radAngle); + + // Bounding box parameters + int16_t min_x; + int16_t min_y; + int16_t max_x; + int16_t max_y; + + // Get the bounding box of this rotated source Sprite + getRotatedBounds(sinra, cosra, width(), height(), _xpivot, _ypivot, &min_x, &min_y, &max_x, &max_y); + + // Move bounding box so source Sprite pivot coincides with destination Sprite pivot + min_x += spr->_xpivot; + max_x += spr->_xpivot; + min_y += spr->_ypivot; + max_y += spr->_ypivot; + + // Test only to show bounding box + // spr->fillSprite(TFT_BLACK); + // spr->drawRect(min_x, min_y, max_x - min_x + 1, max_y - min_y + 1, TFT_GREEN); + + // Return if bounding box is completely outside of destination Sprite + if (min_x > spr->width()) return true; + if (min_y > spr->height()) return true; + if (max_x < 0) return true; + if (max_y < 0) return true; + + // Clip bounding box if it is partially within destination Sprite + if (min_x < 0) min_x = 0; + if (min_y < 0) min_y = 0; + if (max_x > spr->width()) max_x = spr->width(); + if (max_y > spr->height()) max_y = spr->height(); + + // Scan destination bounding box and fetch transformed pixels from source Sprite + for (int32_t x = min_x; x <= max_x; x++) + { + int32_t xt = x - spr->_xpivot; + float cxt = cosra * xt + _xpivot; + float sxt = sinra * xt + _ypivot; + bool column_drawn = false; + for (int32_t y = min_y; y <= max_y; y++) + { + int32_t yt = y - spr->_ypivot; + int32_t xs = (int32_t)round(cxt - sinra * yt); + // Do not calculate ys unless xs is in bounds + if (xs >= 0 && xs < width()) + { + int32_t ys = (int32_t)round(sxt + cosra * yt); + // Check if ys is in bounds + if (ys >= 0 && ys < height()) + { + int32_t rp = readPixel(xs, ys); + if (rp != transp) spr->drawPixel(x, y, rp); + column_drawn = true; + } + } + else if (column_drawn) y = max_y; // Skip the remaining pixels below the Sprite + } + } + + return true; +} + + +/*************************************************************************************** +** Function name: rotatedBounds +** Description: Get bounding box of a rotated Sprite wrt pivot +*************************************************************************************x*/ +void TFT_eSprite::getRotatedBounds(float sina, float cosa, int16_t w, int16_t h, int16_t xp, int16_t yp, + int16_t *min_x, int16_t *min_y, int16_t *max_x, int16_t *max_y) +{ + w -= xp; // w is now right edge coordinate relative to xp + h -= yp; // h is now bottom edge coordinate relative to yp + + // Calculate new corner coordinates + int16_t x0 = -xp * cosa - yp * sina; + int16_t y0 = xp * sina - yp * cosa; + + int16_t x1 = w * cosa - yp * sina; + int16_t y1 = -w * sina - yp * cosa; + + int16_t x2 = h * sina + w * cosa; + int16_t y2 = h * cosa - w * sina; + + int16_t x3 = h * sina - xp * cosa; + int16_t y3 = h * cosa + xp * sina; + + // Find bounding box extremes, enlarge box to accomodate rounding errors + *min_x = x0-2; + if (x1 < *min_x) *min_x = x1-2; + if (x2 < *min_x) *min_x = x2-2; + if (x3 < *min_x) *min_x = x3-2; + + *max_x = x0+2; + if (x1 > *max_x) *max_x = x1+2; + if (x2 > *max_x) *max_x = x2+2; + if (x3 > *max_x) *max_x = x3+2; + + *min_y = y0-2; + if (y1 < *min_y) *min_y = y1-2; + if (y2 < *min_y) *min_y = y2-2; + if (y3 < *min_y) *min_y = y3-2; + + *max_y = y0+2; + if (y1 > *max_y) *max_y = y1+2; + if (y2 > *max_y) *max_y = y2+2; + if (y3 > *max_y) *max_y = y3+2; + +} + + /*************************************************************************************** ** Function name: pushSprite ** Description: Push the sprite to the TFT at x, y @@ -289,33 +541,53 @@ 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 + (int32_t)w < 0) || (y + (int32_t)h < 0)) return; + + int32_t xo = 0; + int32_t yo = 0; + + int32_t xs = x; + int32_t ys = y; + + uint32_t ws = w; + uint32_t hs = h; + + if (x < 0) { xo = -x; xs = 0; } + if (y < 0) { yo = -y; ys = 0; } + + if (xs + w >= _iwidth) ws = _iwidth - xs; + if (ys + h >= _iheight) hs = _iheight - ys; if (_bpp == 16) { - for (uint32_t yp = y; yp < y + h; yp++) + for (uint32_t yp = yo; yp < yo + hs; yp++) { - for (uint32_t xp = x; xp < x + w; xp++) + x = xs; + for (uint32_t xp = xo; xp < xo + ws; xp++) { - uint16_t color = *data++; + uint16_t color = data[xp + yp * w]; if(!_iswapBytes) color = color<<8 | color>>8; - _img[xp + yp * _iwidth] = color; + _img[x + ys * _iwidth] = color; + x++; } + ys++; } } else if (_bpp == 8) { - for (uint32_t yp = y; yp < y + h; yp++) + for (uint32_t yp = yo; yp < yo + hs; yp++) { - for (uint32_t xp = x; xp < x + w; xp++) + x = xs; + for (uint32_t xp = xo; xp < xo + ws; xp++) { - uint16_t color = *data++; + uint16_t color = data[xp + yp * w]; if(_iswapBytes) color = color<<8 | color>>8; - _img8[xp + yp * _iwidth] = (uint8_t)((color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3); + _img8[x + ys * _iwidth] = (uint8_t)((color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3); } + ys++; } } @@ -360,34 +632,54 @@ 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 + (int32_t)w < 0) || (y + (int32_t)h < 0)) return; + + int32_t xo = 0; + int32_t yo = 0; + + int32_t xs = x; + int32_t ys = y; + + uint32_t ws = w; + uint32_t hs = h; + + if (x < 0) { xo = -x; xs = 0; } + if (y < 0) { yo = -y; ys = 0; } + + if (xs + w >= _iwidth) ws = _iwidth - xs; + if (ys + h >= _iheight) hs = _iheight - ys; if (_bpp == 16) { - for (uint32_t yp = y; yp < y + h; yp++) + for (uint32_t yp = yo; yp < yo + hs; yp++) { - for (uint32_t xp = x; xp < x + w; xp++) + x = xs; + for (uint32_t xp = xo; xp < xo + ws; xp++) { - uint16_t color = pgm_read_word(data++); + uint16_t color = pgm_read_word(data + xp + yp * w); if(!_iswapBytes) color = color<<8 | color>>8; - _img[xp + yp * _iwidth] = color; + _img[x + ys * _iwidth] = color; + x++; } + ys++; } } else if (_bpp == 8) { - for (uint32_t yp = y; yp < y + h; yp++) + for (uint32_t yp = yo; yp < yo + hs; yp++) { - for (uint32_t xp = x; xp < x + w; xp++) + x = xs; + for (uint32_t xp = xo; xp < xo + ws; xp++) { - uint16_t color = pgm_read_word(data++); + uint16_t color = pgm_read_word(data + xp + yp * w); if(_iswapBytes) color = color<<8 | color>>8; - _img8[xp + yp * _iwidth] = (uint8_t)((color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3); + _img8[x + ys * _iwidth] = (uint8_t)((color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3); } + ys++; } } diff --git a/Extensions/Sprite.h b/Extensions/Sprite.h index 1ea7645..57db847 100644 --- a/Extensions/Sprite.h +++ b/Extensions/Sprite.h @@ -22,9 +22,10 @@ class TFT_eSprite : public TFT_eSPI { // 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 + // Set or get 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); + int8_t getColorDepth(void); void setBitmapColor(uint16_t c, uint16_t b); @@ -34,7 +35,7 @@ class TFT_eSprite : public TFT_eSPI { fillSprite(uint32_t color), - // Define a window to push 16 bit colour pixels into is a raster order + // Define a window to push 16 bit colour pixels into in 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), @@ -59,9 +60,23 @@ class TFT_eSprite : public TFT_eSPI { // Set the sprite text cursor position for print class (does not change the TFT screen cursor) //setCursor(int16_t x, int16_t y); + // Set the rotation of the Sprite (for 1bpp Sprites only) void setRotation(uint8_t rotation); uint8_t getRotation(void); + // Push a rotated copy of Sprite to TFT with optional transparent colour + bool pushRotated(int16_t angle, int32_t transp = -1); + // Push a rotated copy of Sprite to another different Sprite with optional transparent colour + bool pushRotated(TFT_eSprite *spr, int16_t angle, int32_t transp = -1); + // Set and get the pivot point for this Sprite + void setPivot(int16_t x, int16_t y); + int16_t getPivotX(void), + getPivotY(void); + + // Get the bounding box for a rotated copy of this Sprite + void getRotatedBounds(float sina, float cosa, int16_t w, int16_t h, int16_t xp, int16_t yp, + int16_t *min_x, int16_t *min_y, int16_t *max_x, int16_t *max_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); @@ -98,15 +113,21 @@ class TFT_eSprite : public TFT_eSPI { TFT_eSPI *_tft; + // Reserve memory for the Sprite and return a pointer + void* callocSprite(int16_t width, int16_t height, uint8_t frames = 1); + protected: - uint8_t _bpp; + uint8_t _bpp; // bits per pixel (1, 8 or 16) 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 + int16_t _xpivot; // x pivot point coordinate + int16_t _ypivot; // y pivot point coordinate + + bool _created; // A Sprite has been created and memory reserved bool _gFont = false; // int32_t _icursor_x, _icursor_y; diff --git a/README.md b/README.md index dad0286..2befb58 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,20 @@ # News -1. androdlang has published a really nice companion library to extend the graphics capabilities of TFT_eSPI, you can find this here: +1. Sprites can now by pushed to the screen (or another Sprite) with a rotation angle. The new function is pushRotated(). Three new examples (Rotate_Sprite_1/2/3) have been added to show how the functions can be used to rotate text, images and to draw animated dials with moving needles. + +2. A new TFT_eFEX support library has been created which includes extra functions such as drawing a BMP or Jpeg to the screen. This library will simplify the examples. It will be expanded at a future date to include meters, dials and GUI elements like progress bars, graphs and animated buttons: +https://github.com/Bodmer/TFT_eFEX + +3. androdlang has published a really nice companion library to extend the graphics capabilities of TFT_eSPI, you can find this here: https://github.com/androdlang/TFTShape -2. I have created a user updateable graphics extension library template that can be used to create your own graphics extensions. The Library contains examples and is commented so it should be clear what you need to do to add functions. You can find it here: +4. I have created a user updateable graphics extension library template that can be used to create your own graphics extensions. The Library contains examples and is commented so it should be clear what you need to do to add functions. You can find it here: https://github.com/Bodmer/TFT_eFX -3. The capability to read from an ST7789V TFT with a single bidirectional SDA pin has been added. At the moment this **ONLY** works with an ESP32. It is enabled with a #define TFT_SDA_READ in the setup file. +5. The capability to read from an ST7789V TFT with a single bidirectional SDA pin has been added. At the moment this **ONLY** works with an ESP32. It is enabled with a #define TFT_SDA_READ in the setup file. -4. ST7789V displays are manufactured in two variants that have the red and blue pixels swapped, this is catered for by a new option in the setup file: +6. ST7789V displays are manufactured in two variants that have the red and blue pixels swapped, this is catered for by a new option in the setup file: //#define TFT_RGB_ORDER TFT_RGB // Colour order Red-Green-Blue //#define TFT_RGB_ORDER TFT_BGR // Colour order Blue-Green-Red diff --git a/TFT_eSPI.cpp b/TFT_eSPI.cpp index 2513076..a20cee4 100644 --- a/TFT_eSPI.cpp +++ b/TFT_eSPI.cpp @@ -188,6 +188,9 @@ TFT_eSPI::TFT_eSPI(int16_t w, int16_t h) addr_row = 0xFFFF; addr_col = 0xFFFF; + _xpivot = 0; + _ypivot = 0; + #ifdef LOAD_GLCD fontsloaded = 0x0002; // Bit 1 set #endif @@ -1062,7 +1065,7 @@ void TFT_eSPI::pushImage(int32_t x, int32_t y, uint32_t w, uint32_t h, const uin for (int j = 0; j < 64; j++) { pix_buffer[j] = pgm_read_word(&data[i * 64 + j]); } - pushColors(pix_buffer, 64, !_swapBytes); + pushColors(pix_buffer, 64, _swapBytes); } // Work out number of pixels not yet sent @@ -1074,7 +1077,7 @@ void TFT_eSPI::pushImage(int32_t x, int32_t y, uint32_t w, uint32_t h, const uin { pix_buffer[i] = pgm_read_word(&data[nb * 64 + i]); } - pushColors(pix_buffer, np, !_swapBytes); + pushColors(pix_buffer, np, _swapBytes); } CS_H; @@ -1115,7 +1118,7 @@ void TFT_eSPI::pushImage(int32_t x, int32_t y, uint32_t w, uint32_t h, const uin uint16_t lineBuf[dw]; - if (_swapBytes) transp = transp >> 8 | transp << 8; + if (!_swapBytes) transp = transp >> 8 | transp << 8; while (dh--) { @@ -1140,14 +1143,14 @@ void TFT_eSPI::pushImage(int32_t x, int32_t y, uint32_t w, uint32_t h, const uin move = true; if (np) { - pushColors(lineBuf, np, !_swapBytes); + pushColors(lineBuf, np, _swapBytes); np = 0; } } px++; ptr++; } - if (np) pushColors(lineBuf, np, !_swapBytes); + if (np) pushColors(lineBuf, np, _swapBytes); y++; data += w; @@ -2097,6 +2100,37 @@ void TFT_eSPI::setTextColor(uint16_t c, uint16_t b) } +/*************************************************************************************** +** Function name: setPivot +** Description: Set the pivot point on the TFT +*************************************************************************************x*/ +void TFT_eSPI::setPivot(int16_t x, int16_t y) +{ + _xpivot = x; + _ypivot = y; +} + + +/*************************************************************************************** +** Function name: getPivotX +** Description: Get the x pivot position +***************************************************************************************/ +int16_t TFT_eSPI::getPivotX(void) +{ + return _xpivot; +} + + +/*************************************************************************************** +** Function name: getPivotY +** Description: Get the y pivot position +***************************************************************************************/ +int16_t TFT_eSPI::getPivotY(void) +{ + return _ypivot; +} + + /*************************************************************************************** ** Function name: setBitmapColor ** Description: Set the foreground foreground and background colour @@ -3272,7 +3306,7 @@ void TFT_eSPI::pushColor(uint16_t color, uint32_t len) void TFT_eSPI::startWrite(void) { spi_begin(); - + inTransaction = true; CS_L; } @@ -3282,9 +3316,8 @@ void TFT_eSPI::startWrite(void) ***************************************************************************************/ void TFT_eSPI::endWrite(void) { - CS_H; - + inTransaction = false; spi_end(); } diff --git a/TFT_eSPI.h b/TFT_eSPI.h index 68bec74..48b9f48 100644 --- a/TFT_eSPI.h +++ b/TFT_eSPI.h @@ -213,12 +213,16 @@ // Use single register write for CS_L and DC_C if pins are both in range 0-31 #ifdef ESP32 - #if (TFT_CS >= 0) && (TFT_CS < 32) && (TFT_DC >= 0) && (TFT_DC < 32) - #ifdef RPI_ILI9486_DRIVER // RPi display needs a slower CD and DC change - #define CS_L_DC_C GPIO.out_w1tc = ((1 << TFT_CS) | (1 << TFT_DC)); \ - GPIO.out_w1tc = ((1 << TFT_CS) | (1 << TFT_DC)) + #ifdef TFT_CS + #if (TFT_CS >= 0) && (TFT_CS < 32) && (TFT_DC >= 0) && (TFT_DC < 32) + #ifdef RPI_ILI9486_DRIVER // RPi display needs a slower CD and DC change + #define CS_L_DC_C GPIO.out_w1tc = ((1 << TFT_CS) | (1 << TFT_DC)); \ + GPIO.out_w1tc = ((1 << TFT_CS) | (1 << TFT_DC)) + #else + #define CS_L_DC_C GPIO.out_w1tc = ((1 << TFT_CS) | (1 << TFT_DC)) + #endif #else - #define CS_L_DC_C GPIO.out_w1tc = ((1 << TFT_CS) | (1 << TFT_DC)) + #define CS_L_DC_C CS_L; DC_C #endif #else #define CS_L_DC_C CS_L; DC_C @@ -676,7 +680,7 @@ class TFT_eSPI : public Print { 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 fgcolor, uint16_t bgcolor), // For 1bpp sprites - + setPivot(int16_t x, int16_t y), setCursor(int16_t x, int16_t y), setCursor(int16_t x, int16_t y, uint8_t font), setTextColor(uint16_t color), @@ -741,6 +745,9 @@ class TFT_eSPI : public Print { int16_t getCursorX(void), getCursorY(void); + int16_t getPivotX(void), + getPivotY(void); + uint16_t fontsLoaded(void), color565(uint8_t red, uint8_t green, uint8_t blue), // Convert 8 bit red, green and blue to 16 bits color8to16(uint8_t color332); // Convert 8 bit colour to 16 bits @@ -798,6 +805,9 @@ class TFT_eSPI : public Print { textdatum, // Text reference datum rotation; // Display rotation (0-3) + int16_t _xpivot; // x pivot point coordinate + int16_t _ypivot; // x pivot point coordinate + private: inline void spi_begin() __attribute__((always_inline)); diff --git a/User_Setups/SetupX_Template.h b/User_Setups/SetupX_Template.h index 11cf2ab..7d6ef8b 100644 --- a/User_Setups/SetupX_Template.h +++ b/User_Setups/SetupX_Template.h @@ -66,6 +66,11 @@ // #define ST7735_BLACKTAB // #define ST7735_REDTAB160x80 // For 160 x 80 display (24 offset) (https://www.aliexpress.com/item/ShengYang-1pcs-IPS-0-96-inch-7P-SPI-HD-65K-Full-Color-OLED-Module-ST7735-Drive/32918394604.html) +// If colours are inverted (white shows as black) then uncomment one of the next +// 2 lines try both options, one of the options should correct the inversion. +//#define TFT_INVERSION_ON +//#define TFT_INVERSION_OFF + // ################################################################################## // // Section 1. Define the pins that are used to interface with the display here diff --git a/examples/160 x 128/Flash_Bitmap2/Alert.h b/examples/160 x 128/Flash_Bitmap2/Alert.h deleted file mode 100644 index 54d013d..0000000 --- a/examples/160 x 128/Flash_Bitmap2/Alert.h +++ /dev/null @@ -1,42 +0,0 @@ -// We need this header file to use FLASH as storage with PROGMEM directive: -#include - -// Icon width and height -const uint16_t alertWidth = 32; -const uint16_t alertHeight = 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 alert[1024] PROGMEM={ -0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0840,0x0000,0x0000,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,0x0000,0x0000,0x0000,0x1080,0xAC66,0xEDE8,0xFE69,0xC4C6,0x2901,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 1, 64 pixels -0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0xBCC6,0xFE68,0xFE68,0xFE6A,0xFE68,0xEDE8,0x18A1,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 2, 96 pixels -0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x8344,0xFE48,0xFE8C,0xFFDD,0xFFFF,0xFEF0,0xFE48,0xB466,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 3, 128 pixels -0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x1880,0xEDC7,0xFE48,0xFF99,0xFFBC,0xFF9B,0xFFBD,0xFE6A,0xFE48,0x5A23,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 4, 160 pixels -0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x9BE5,0xFE28,0xFED0,0xFFBC,0xFF7A,0xFF9A,0xFF9B,0xFF35,0xFE28,0xBCA6,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 5, 192 pixels -0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x3962,0xFE28,0xFE28,0xFF9A,0xFF79,0xFF9A,0xFF9B,0xFF9A,0xFFBD,0xFE6B,0xFE28,0x72E3,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 6, 224 pixels -0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0xB465,0xFE28,0xFEF2,0xFF7A,0xFF79,0xFF7A,0xFF9A,0xFF7A,0xFF7A,0xFF78,0xFE28,0xDD67,0x0860,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 7, 256 pixels -0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x5A22,0xFE07,0xFE29,0xFF9B,0xFF37,0xFF58,0xFF79,0xFF79,0xFF79,0xFF58,0xFF9B,0xFEAE,0xFE07,0x93A4,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 8, 288 pixels -0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0xC4A5,0xFE07,0xFF15,0xFF37,0xFF36,0xAD11,0x2965,0x2965,0xCDF4,0xFF37,0xFF37,0xFF79,0xFE07,0xFE07,0x2901,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 9, 320 pixels -0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x7B03,0xFDE7,0xFE4B,0xFF79,0xFEF4,0xFF15,0xB552,0x2945,0x2945,0xDE55,0xFF16,0xFF15,0xFF58,0xFED1,0xFDE7,0xAC25,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 10, 352 pixels -0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0840,0xDD26,0xFDE7,0xFF57,0xFED3,0xFED2,0xFEF4,0xBD93,0x2124,0x2124,0xDE75,0xFF14,0xFED3,0xFED3,0xFF7A,0xFE08,0xFDE7,0x49A2,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 11, 384 pixels -0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x9BA4,0xFDC6,0xFE6E,0xFF36,0xFE90,0xFEB1,0xFED3,0xC592,0x2124,0x2124,0xE675,0xFED3,0xFEB2,0xFEB1,0xFEF3,0xFEF3,0xFDC6,0xBC45,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 12, 416 pixels -0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x3141,0xF5C6,0xF5C7,0xFF58,0xFE90,0xFE6F,0xFE8F,0xFEB1,0xCDB2,0x2104,0x2104,0xF6B4,0xFEB1,0xFE90,0xFE8F,0xFE90,0xFF58,0xFE0A,0xF5C6,0x72A3,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 13, 448 pixels -0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0xABE4,0xF5A6,0xFEB1,0xFED3,0xFE4E,0xFE6E,0xFE6F,0xFE90,0xD5F2,0x18E3,0x18E3,0xFED4,0xFE90,0xFE6F,0xFE6F,0xFE6E,0xFE91,0xFF36,0xF5A6,0xCCA5,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 14, 480 pixels -0x0000,0x0000,0x0000,0x0000,0x0000,0x5202,0xF5A6,0xF5C7,0xFF58,0xFE4D,0xFE4D,0xFE4D,0xFE4E,0xFE6F,0xDE11,0x18C3,0x18C3,0xFED3,0xFE6F,0xFE6E,0xFE4E,0xFE4D,0xFE4D,0xFF16,0xFE2C,0xF5A6,0x9363,0x0000,0x0000,0x0000,0x0000,0x0000, // row 15, 512 pixels -0x0000,0x0000,0x0000,0x0000,0x0000,0xBC44,0xF585,0xFED3,0xFE6F,0xFE2C,0xFE2C,0xFE2D,0xFE4D,0xFE4E,0xE630,0x10A2,0x2104,0xFED1,0xFE4E,0xFE4D,0xFE4D,0xFE2D,0xFE2C,0xFE4D,0xFF37,0xF586,0xF585,0x28E1,0x0000,0x0000,0x0000,0x0000, // row 16, 544 pixels -0x0000,0x0000,0x0000,0x0000,0x7282,0xF565,0xF5EA,0xFF16,0xFE0B,0xFE0B,0xFE0B,0xFE2C,0xFE2C,0xFE4D,0xF670,0x1082,0x2924,0xFEB0,0xFE2D,0xFE2C,0xFE2C,0xFE2C,0xFE0B,0xFE0B,0xFEB2,0xFE6F,0xF565,0xA383,0x0000,0x0000,0x0000,0x0000, // row 17, 576 pixels -0x0000,0x0000,0x0000,0x0840,0xD4C4,0xF565,0xFEF5,0xFE0C,0xFDE9,0xFDEA,0xFE0A,0xFE0B,0xFE0B,0xFE2C,0xFE8F,0x0861,0x2964,0xFE8F,0xFE2C,0xFE0B,0xFE0B,0xFE0B,0xFE0A,0xFDEA,0xFE0B,0xFF37,0xF586,0xF565,0x4181,0x0000,0x0000,0x0000, // row 18, 608 pixels -0x0000,0x0000,0x0000,0x9343,0xF545,0xF60C,0xFED3,0xFDC8,0xFDC8,0xFDC9,0xFDE9,0xFDEA,0xFDEA,0xFE0B,0xFE8E,0x0861,0x3184,0xFE6D,0xFE0B,0xFE0A,0xFDEA,0xFDEA,0xFDE9,0xFDC9,0xFDC9,0xFE4E,0xFEB2,0xF545,0xB3E3,0x0000,0x0000,0x0000, // row 19, 640 pixels -0x0000,0x0000,0x28E0,0xF544,0xF545,0xFF17,0xFDC8,0xFDA7,0xFDA7,0xFDC8,0xFDC8,0xFDC9,0xFDC9,0xFDE9,0xFE6C,0x10A2,0x39C4,0xFE4C,0xFDEA,0xFDE9,0xFDC9,0xFDC9,0xFDC8,0xFDC8,0xFDA7,0xFDA8,0xFF16,0xF588,0xF544,0x6222,0x0000,0x0000, // row 20, 672 pixels -0x0000,0x0000,0xA383,0xF524,0xF64E,0xFE4E,0xFD86,0xFD86,0xFD87,0xFDA7,0xFDA7,0xFDA8,0xFDC8,0xFDC8,0xFE2A,0xA469,0xB4EA,0xFE2A,0xFDC9,0xFDC8,0xFDC8,0xFDA8,0xFDA7,0xFDA7,0xFD87,0xFD86,0xFDEA,0xFED3,0xF524,0xC443,0x0000,0x0000, // row 21, 704 pixels -0x0000,0x51C1,0xF504,0xF546,0xFF16,0xF565,0xFD65,0xFD65,0xFD86,0xFD86,0xFD86,0xFDA7,0xFDA7,0xFDA7,0xFDE8,0xFE6A,0xFE4A,0xFDE8,0xFDA7,0xFDA7,0xFDA7,0xFDA7,0xFD86,0xFD86,0xFD86,0xFD65,0xFD65,0xFEB2,0xF5CA,0xF504,0x8AE2,0x0000, // row 22, 736 pixels -0x0000,0xB3A2,0xED03,0xFE92,0xFDC9,0xF543,0xF544,0xFD44,0xFD65,0xFD65,0xFD65,0xFD86,0xFD86,0xFD86,0xFDA7,0xFDC7,0xFDC7,0xFDA7,0xFD86,0xFD86,0xFD86,0xFD86,0xFD65,0xFD65,0xFD65,0xFD44,0xF544,0xFD86,0xFEF5,0xED03,0xE4C3,0x1880, // row 23, 768 pixels -0x7241,0xECE3,0xF567,0xFED3,0xF523,0xF523,0xF523,0xF543,0xF544,0xF544,0xFD65,0xFD65,0xFD65,0xFD65,0xD4E6,0x39C5,0x39A5,0xD4E6,0xFD86,0xFD65,0xFD65,0xFD65,0xFD65,0xF544,0xF544,0xF543,0xF523,0xF523,0xFE2E,0xF5EC,0xECE3,0x9B42, // row 24, 800 pixels -0xD443,0xECE3,0xFED4,0xF565,0xF502,0xF502,0xF522,0xF523,0xF523,0xF543,0xF544,0xF544,0xF544,0xFD65,0x8B64,0x18C3,0x18C3,0x8344,0xFD85,0xFD44,0xF544,0xF544,0xF544,0xF543,0xF523,0xF523,0xF522,0xF502,0xF523,0xFEF5,0xED04,0xECE3, // row 25, 832 pixels -0xECC3,0xF5AB,0xFE6F,0xF501,0xF4E1,0xF501,0xF502,0xF502,0xF522,0xF522,0xF523,0xF523,0xF523,0xFD84,0xC504,0x20E1,0x18E1,0xC4E4,0xFD84,0xF543,0xF523,0xF523,0xF523,0xF522,0xF522,0xF502,0xF502,0xF501,0xF501,0xFDC9,0xF62F,0xECC3, // row 26, 864 pixels -0xECC2,0xFE92,0xF523,0xF4E0,0xF4E0,0xF4E1,0xF4E1,0xF501,0xF501,0xF502,0xF502,0xF522,0xF522,0xF543,0xFDE3,0xFEA5,0xF6A4,0xFE04,0xF543,0xF522,0xF522,0xF522,0xF502,0xF502,0xF501,0xF501,0xF4E1,0xF4E1,0xF4E0,0xF4E1,0xFED4,0xECC2, // row 27, 896 pixels -0xECA2,0xF5EC,0xF4E0,0xF4C0,0xF4E0,0xF4E0,0xF4E0,0xF4E1,0xF4E1,0xF501,0xF501,0xF501,0xF502,0xF502,0xF542,0xFDA2,0xFDA2,0xF542,0xF502,0xF502,0xF502,0xF501,0xF501,0xF501,0xF4E1,0xF4E1,0xF4E0,0xF4E0,0xF4E0,0xF4C0,0xF5A9,0xECA2, // row 28, 928 pixels -0xECA2,0xECA2,0xECC2,0xF4C1,0xF4C1,0xF4C1,0xF4C1,0xF4C1,0xF4C1,0xF4C1,0xF4C1,0xF4E1,0xF4E2,0xF4E2,0xF4E2,0xF4E2,0xF4E2,0xF4E2,0xF4E2,0xF4E2,0xF4E2,0xF4E1,0xF4C1,0xF4C1,0xF4C1,0xF4C1,0xF4C1,0xF4C1,0xF4C1,0xECC2,0xECC3,0xECA2, // row 29, 960 pixels -0x8AC1,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0x9B01, // row 30, 992 pixels -0x0000,0x1880,0x51A0,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x61E0,0x28E0,0x0000}; // row 31, 1024 pixels - diff --git a/examples/160 x 128/Flash_Bitmap2/Close.h b/examples/160 x 128/Flash_Bitmap2/Close.h deleted file mode 100644 index ce676c9..0000000 --- a/examples/160 x 128/Flash_Bitmap2/Close.h +++ /dev/null @@ -1,41 +0,0 @@ -// We need this header file to use FLASH as storage with PROGMEM directive: -#include - -// Icon width and height -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 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 -0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x2041,0x8945,0xF34D,0xFD34,0xFDB6,0xFD75,0xFD55,0xFD55,0xFD96,0xFDD7,0xFDF7,0xFDF7,0xFDB6,0xFDB6,0xFDD7,0xFDF7,0xFD75,0xF38E,0x8965,0x2041,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 3, 128 pixels -0x0000,0x0000,0x0000,0x0000,0x0000,0x4082,0xE208,0xF410,0xFD34,0xFC92,0xFBEF,0xFBAE,0xFBEF,0xFC71,0xFD14,0xFD75,0xFDB6,0xFD75,0xFD14,0xFC92,0xFC51,0xFC71,0xFCF3,0xFD75,0xFC30,0xEA28,0x40A2,0x0000,0x0000,0x0000,0x0000,0x0000, // row 4, 160 pixels -0x0000,0x0000,0x0000,0x0000,0x3861,0xE1E7,0xF451,0xFC92,0xFB4D,0xFA49,0xFA49,0xFAEB,0xFBAE,0xFC71,0xFD34,0xFDB6,0xFE18,0xFDB6,0xFD34,0xFC71,0xFBAE,0xFB0C,0xFAEB,0xFBAE,0xFCD3,0xFC71,0xE208,0x4082,0x0000,0x0000,0x0000,0x0000, // row 5, 192 pixels -0x0000,0x0000,0x0000,0x1020,0xD986,0xF430,0xFC30,0xFA28,0xF924,0xF965,0xFA8A,0xFB0C,0xFBAE,0xFC51,0xFD14,0xFD75,0xFDB6,0xFD75,0xFD14,0xFC51,0xFC71,0xFBEF,0xFA28,0xF9C7,0xFA8A,0xFC51,0xF430,0xD9A6,0x1020,0x0000,0x0000,0x0000, // row 6, 224 pixels -0x0000,0x0000,0x0000,0x78A2,0xEB6D,0xFC30,0xF9C7,0xF861,0xF8A2,0xFA08,0xFEDB,0xFD55,0xFB4D,0xFC10,0xFC92,0xFD14,0xFD34,0xFD14,0xFC92,0xFCB2,0xFF7D,0xFF7D,0xFB2C,0xF945,0xF8E3,0xF9E7,0xFC30,0xEB8E,0x78C3,0x0000,0x0000,0x0000, // row 7, 256 pixels -0x0000,0x0000,0x3841,0xD9E7,0xF492,0xF208,0xF041,0xF800,0xF945,0xFE9A,0xFFFF,0xFFFF,0xFD75,0xFB8E,0xFC10,0xFC51,0xFC71,0xFC51,0xFCB2,0xFF7D,0xFFFF,0xFFFF,0xFF3C,0xFA8A,0xF882,0xF841,0xFA08,0xFC92,0xDA08,0x3841,0x0000,0x0000, // row 8, 288 pixels -0x0000,0x0000,0x88A2,0xEBCF,0xF2EB,0xF061,0xF000,0xF8E3,0xFE79,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFD75,0xFB4D,0xFBAE,0xFBAE,0xFC71,0xFF7D,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFEFB,0xFA28,0xF800,0xF061,0xF2EB,0xEBEF,0x90C3,0x0000,0x0000, // row 9, 320 pixels -0x0000,0x2820,0xD1C7,0xF410,0xE945,0xE800,0xF000,0xFE9A,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFD34,0xFAEB,0xFBCF,0xFF5D,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFF1C,0xF986,0xF000,0xF145,0xF410,0xD1E7,0x2820,0x0000, // row 10, 352 pixels -0x0000,0x6841,0xDB2C,0xEACB,0xE041,0xE800,0xF000,0xFEFB,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFD14,0xFF1C,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFBCF,0xF082,0xF000,0xE841,0xEACB,0xE34D,0x7061,0x0000, // row 11, 384 pixels -0x0000,0x9861,0xE3CF,0xE186,0xE000,0xE800,0xE800,0xF145,0xFEDB,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFB8E,0xF000,0xF000,0xE800,0xE800,0xE986,0xEBCF,0xA082,0x0000, // row 12, 416 pixels -0x0800,0xB8A2,0xE3AE,0xD8A2,0xD800,0xE000,0xE800,0xE800,0xF145,0xFEFB,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFB8E,0xF000,0xF000,0xE800,0xE800,0xE000,0xE0A2,0xEBAE,0xC0C3,0x0800, // row 13, 448 pixels -0x1800,0xC124,0xE30C,0xD020,0xD800,0xE000,0xE000,0xE800,0xE800,0xF145,0xFEDB,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFB8E,0xF000,0xF000,0xE800,0xE800,0xE000,0xE000,0xD820,0xE30C,0xC124,0x1800, // row 14, 480 pixels -0x2800,0xC165,0xDAAA,0xC800,0xD000,0xD800,0xE000,0xE000,0xE800,0xE800,0xF124,0xFE79,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFB6D,0xF000,0xF000,0xE800,0xE800,0xE000,0xE000,0xD800,0xD000,0xDAAA,0xC165,0x2800, // row 15, 512 pixels -0x2000,0xB924,0xD269,0xC800,0xD000,0xD000,0xD800,0xE000,0xE000,0xE800,0xE924,0xFEFB,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xF36D,0xE800,0xE800,0xE800,0xE000,0xE000,0xD800,0xD000,0xD000,0xDA69,0xC145,0x2800, // row 16, 544 pixels -0x1000,0xB0A2,0xD28A,0xC000,0xC800,0xD000,0xD000,0xD800,0xD800,0xE165,0xFEFB,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xF3AE,0xE000,0xE000,0xD800,0xD800,0xD000,0xD000,0xC800,0xD28A,0xB8C3,0x1000, // row 17, 576 pixels -0x0000,0xA800,0xD2AA,0xB800,0xC000,0xC800,0xC800,0xD000,0xD965,0xFEFB,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xEBAE,0xD800,0xD800,0xD000,0xC800,0xC800,0xC000,0xD2AA,0xB020,0x0000, // row 18, 608 pixels -0x0000,0x8000,0xCA69,0xB841,0xB800,0xC000,0xC800,0xD186,0xFEFB,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xEBCF,0xD000,0xC800,0xC800,0xC000,0xC041,0xCA69,0x8000,0x0000, // row 19, 640 pixels -0x0000,0x4800,0xC1C7,0xB8E3,0xB800,0xB800,0xC000,0xF69A,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xEBEF,0xFE79,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xE410,0xC841,0xC000,0xB800,0xC0E3,0xC1C7,0x4800,0x0000, // row 20, 672 pixels -0x0000,0x1000,0xB061,0xC1E7,0xB000,0xB000,0xB800,0xD269,0xFFBE,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xE38E,0xD000,0xD965,0xF69A,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xDB0C,0xC020,0xB800,0xB000,0xC1E7,0xB061,0x1000,0x0000, // row 21, 704 pixels -0x0000,0x0000,0x6000,0xB9C7,0xB061,0xB000,0xB000,0xB800,0xCA49,0xFF9E,0xFFFF,0xFFFF,0xFFFF,0xE38E,0xC800,0xC800,0xC800,0xD186,0xF69A,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xDB0C,0xB800,0xB800,0xB000,0xB061,0xC1C7,0x6000,0x0000,0x0000, // row 22, 736 pixels -0x0000,0x0000,0x1800,0xB041,0xB986,0xA800,0xA800,0xB000,0xB000,0xCA49,0xFF7D,0xFFFF,0xDB8E,0xC000,0xC000,0xC000,0xC000,0xC000,0xC986,0xF6DB,0xFFFF,0xFFFF,0xD30C,0xB800,0xB000,0xB000,0xA800,0xB986,0xB041,0x1800,0x0000,0x0000, // row 23, 768 pixels -0x0000,0x0000,0x0000,0x5800,0xB0E3,0xA8C3,0xA800,0xA800,0xA800,0xB000,0xCACB,0xD38E,0xB000,0xB800,0xB800,0xB800,0xB800,0xB800,0xB800,0xC145,0xF6DB,0xD34D,0xB000,0xB000,0xA800,0xA800,0xB0C3,0xB0E3,0x5800,0x0000,0x0000,0x0000, // row 24, 800 pixels -0x0000,0x0000,0x0000,0x0000,0x6000,0xB124,0xA882,0xA000,0xA800,0xA800,0xA800,0xA800,0xB000,0xB000,0xB000,0xB000,0xB000,0xB000,0xB000,0xB000,0xB000,0xA800,0xA800,0xA800,0xA800,0xA882,0xB124,0x6000,0x0000,0x0000,0x0000,0x0000, // row 25, 832 pixels -0x0000,0x0000,0x0000,0x0000,0x0000,0x6000,0xB104,0xA882,0xA000,0xA000,0xA000,0xA800,0xA800,0xA800,0xA800,0xA800,0xA800,0xA800,0xA800,0xA800,0xA800,0xA800,0xA000,0xA000,0xA882,0xB104,0x6000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 26, 864 pixels -0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x6000,0xB0A2,0xA8C3,0xA020,0xA000,0xA000,0xA000,0xA000,0xA000,0xA000,0xA000,0xA000,0xA000,0xA000,0xA000,0xA000,0xA020,0xA8C3,0xB0A2,0x6000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 27, 896 pixels -0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x4800,0xA800,0xB0C3,0xA0A2,0x9800,0x9800,0x9800,0x9800,0xA000,0xA000,0xA000,0x9800,0x9800,0x9800,0xA082,0xB0E3,0xA800,0x4800,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 28, 928 pixels -0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x5800,0xA800,0xB0A2,0xA8E3,0xA0A2,0xA041,0x9800,0x9800,0xA041,0xA0A2,0xA8E3,0xB0A2,0xA800,0x5800,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 29, 960 pixels -0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x3000,0x6000,0x8800,0xA000,0xA800,0xA800,0xA000,0x8800,0x6000,0x3000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 30, 992 pixels -0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000}; // row 31, 1024 pixels diff --git a/examples/160 x 128/Flash_Bitmap2/Flash_Bitmap2.ino b/examples/160 x 128/Flash_Bitmap2/Flash_Bitmap2.ino deleted file mode 100644 index 42099e9..0000000 --- a/examples/160 x 128/Flash_Bitmap2/Flash_Bitmap2.ino +++ /dev/null @@ -1,110 +0,0 @@ -// Code partly derived from ILI9341_Due library example - -// Draws the 3 icons across the middle of the screen and pauses. -// Then draws 300 icons at random locations, clears screen and repeats -// -// This demonstrates drawing icons from FLASH - -// Icons are stored in tabs, e.g. Alert.h etc -// more than one icon can be in a header file. - -// Original sketch header follow: -/* - This sketch demonstrates loading images from arrays stored in program (FLASH) memory. - - This sketch does not use/need any fonts at all... - - Arrays containing FLASH images can be created with UTFT library tool: - (libraries\UTFT\Tools\ImageConverter565.exe) - Convert to .c format then copy into a new tab - - The number and size of icons is limited by available FLASH memory. The icon array will - use width x height x 2 bytes of FLASH, i.e. 32 x 32 icon uses ~2048 bytes - -*/ -#include // Graphics and font library for ST7735 driver chip -#include - -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 "Close.h" -#include "Info.h" - -long count = 0; // Loop count - -void setup() -{ - Serial.begin(115200); - tft.init(); - tft.setRotation(1); // landscape - - tft.fillScreen(TFT_BLACK); - - // 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(closeX, (tft.width() - closeWidth)/2 + 50, (tft.height() - closeHeight)/2, closeWidth, closeHeight); - - // Pause here to admire the icons! - delay(4000); - -} - -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(closeX, random(tft.width() - closeWidth), random(tft.height() - closeHeight), alertWidth, closeHeight); - - // Clear screen after 100 x 3 = 300 icons drawn - if (100 == count++) { - count = 1; - tft.setRotation(2 * random(2)); // Rotate randomly to clear display left>right or right>left to reduce monotony! - tft.fillScreen(TFT_BLACK); - tft.setRotation(1); - Serial.println(millis()); - } -} - - -//==================================================================================== -// This is the function to draw the icon stored as an array in program memory (FLASH) -//==================================================================================== - -// To speed up rendering we use a 64 pixel buffer -#define BUFF_SIZE 64 - -// 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, uint16_t width, uint16_t height) { - - uint16_t pix_buffer[BUFF_SIZE]; // Pixel buffer (16 bits per pixel) - - // Set up a window the right size to stream pixels into - tft.setAddrWindow(x, y, x + width - 1, y + height - 1); - - // Work out the number whole buffers to send - uint16_t nb = ((uint16_t)height * width) / BUFF_SIZE; - - // Fill and send "nb" buffers to TFT - for (int i = 0; i < nb; i++) { - for (int j = 0; j < BUFF_SIZE; j++) { - pix_buffer[j] = pgm_read_word(&icon[i * BUFF_SIZE + j]); - } - tft.pushColors(pix_buffer, BUFF_SIZE); - } - - // Work out number of pixels not yet sent - uint16_t np = ((uint16_t)height * width) % BUFF_SIZE; - - // Send any partial buffer left over - if (np) { - for (int i = 0; i < np; i++) pix_buffer[i] = pgm_read_word(&icon[nb * BUFF_SIZE + i]); - tft.pushColors(pix_buffer, np); - } -} - diff --git a/examples/160 x 128/Flash_Bitmap2/Info.h b/examples/160 x 128/Flash_Bitmap2/Info.h deleted file mode 100644 index c4ee633..0000000 --- a/examples/160 x 128/Flash_Bitmap2/Info.h +++ /dev/null @@ -1,41 +0,0 @@ -// We need this header file to use FLASH as storage with PROGMEM directive: -#include - -// Icon width and height -const uint16_t infoWidth = 32; -const uint16_t infoHeight = 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 info[1024] PROGMEM={ -0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,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,0x0000,0x0861,0x4A69,0x8C71,0xA514,0xBDF7,0xBDF7,0xA514,0x8C71,0x4A69,0x0861,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 1, 64 pixels -0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x39E7,0x9CF3,0xEF7D,0xF79E,0xFFDF,0xFFDF,0xFFDF,0xFFDF,0xFFDF,0xFFDF,0xF79E,0xEF7D,0x9CF3,0x39E7,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 2, 96 pixels -0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x2965,0x9492,0xF79E,0xFFDF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFDF,0xF79E,0x9492,0x2965,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 3, 128 pixels -0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x630C,0xEF7D,0xFFDF,0xFFFF,0xFFFF,0xFFFF,0xD75F,0xB6BF,0x9E5F,0x963F,0x963F,0x9E5F,0xB6BF,0xD75F,0xFFFF,0xFFFF,0xFFFF,0xFFDF,0xEF7D,0x630C,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 4, 160 pixels -0x0000,0x0000,0x0000,0x0000,0x0000,0x73AE,0xEF7D,0xFFDF,0xFFFF,0xFFDF,0xBEDF,0x7DBF,0x7DBF,0x7DDF,0x7DDF,0x7DDF,0x7DDF,0x7DDF,0x7DBF,0x759F,0x7DBE,0xBEBF,0xFFDF,0xFFFF,0xFFDF,0xEF7D,0x73AE,0x0000,0x0000,0x0000,0x0000,0x0000, // row 5, 192 pixels -0x0000,0x0000,0x0000,0x0000,0x630C,0xEF7D,0xFFFF,0xFFFF,0xE77F,0x7DBE,0x759E,0x759F,0x7DBF,0x7DDF,0x7DDF,0x85FF,0x7DDF,0x7DDF,0x7DBF,0x759F,0x759E,0x6D7E,0x7DBE,0xDF7F,0xFFFF,0xFFFF,0xEF7D,0x630C,0x0000,0x0000,0x0000,0x0000, // row 6, 224 pixels -0x0000,0x0000,0x0000,0x31A6,0xEF5D,0xFFDF,0xFFFF,0xCF1E,0x6D7E,0x6D7E,0x759E,0x759F,0x7DBF,0x7DDF,0x8E1F,0xBEDF,0xC6FF,0x8DFF,0x75BF,0x759F,0x759E,0x6D7E,0x655E,0x655D,0xCF1E,0xFFFF,0xFFDF,0xEF5D,0x31A6,0x0000,0x0000,0x0000, // row 7, 256 pixels -0x0000,0x0000,0x0000,0x94B2,0xF7BE,0xFFFF,0xDF5E,0x655D,0x655D,0x6D7E,0x6D7E,0x759E,0x75BF,0x759F,0xEFBF,0xFFFF,0xFFFF,0xEFBF,0x759F,0x759E,0x6D7E,0x6D7E,0x655D,0x653D,0x653D,0xDF5E,0xFFFF,0xF7BE,0x94B2,0x0000,0x0000,0x0000, // row 8, 288 pixels -0x0000,0x0000,0x4228,0xEF7D,0xFFFF,0xF7BF,0x6D5D,0x653D,0x655D,0x6D5E,0x6D7E,0x759E,0x759E,0x85DF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0x8DFE,0x6D7E,0x6D7E,0x6D5E,0x655D,0x653D,0x5D1D,0x6D5D,0xF7BF,0xFFFF,0xEF7D,0x4228,0x0000,0x0000, // row 9, 320 pixels -0x0000,0x0000,0xA534,0xFFDF,0xFFDF,0xA65D,0x5D1D,0x5D1D,0x653D,0x655E,0x6D7E,0x6D7E,0x6D7E,0x651E,0xE77F,0xFFFF,0xFFFF,0xF7BF,0x5CFE,0x6D7E,0x6D7E,0x655E,0x653D,0x5D1D,0x5D1D,0x54FC,0xA65D,0xFFDF,0xFFDF,0xA534,0x0000,0x0000, // row 10, 352 pixels -0x0000,0x18E3,0xEF5D,0xFFFF,0xEF9E,0x5CFC,0x54FC,0x5D1D,0x5D3D,0x653D,0x655E,0x6D7E,0x6D7E,0x653E,0x6D3E,0xB67E,0xBEBE,0x755E,0x5D1E,0x6D5E,0x655E,0x653D,0x5D3D,0x5D1D,0x54FC,0x54DC,0x54FC,0xEF9E,0xFFFF,0xEF5D,0x18E3,0x0000, // row 11, 384 pixels -0x0000,0x630C,0xEF7D,0xFFDF,0xB69D,0x54DC,0x54FC,0x5CFC,0x5D1D,0x653D,0x653D,0x655E,0x6D5E,0x655E,0x5CFE,0x4C9D,0x4C7D,0x54DD,0x653E,0x655E,0x653D,0x653D,0x5D1D,0x5CFC,0x54FC,0x54DC,0x4CBC,0xB69D,0xFFDF,0xEF7D,0x630C,0x0000, // row 12, 416 pixels -0x0000,0x94B2,0xF7BE,0xFFDF,0x85BC,0x4CBC,0x54DC,0x54FC,0x5CFD,0x5D1D,0x5D3D,0x653D,0x655D,0x653D,0x85DE,0xC6FE,0xC6FE,0x85BE,0x653D,0x653D,0x5D3D,0x5D1D,0x5CFD,0x54FC,0x54DC,0x4CBC,0x4CBB,0x85BC,0xFFDF,0xF7BE,0x94B2,0x0000, // row 13, 448 pixels -0x0000,0xB5B6,0xFFDF,0xF7BE,0x651C,0x4CBB,0x4CBC,0x54DC,0x54FC,0x5CFC,0x5D1D,0x5D1D,0x653D,0x5D1D,0xE77E,0xFFDF,0xFFDF,0xEF9E,0x5CFD,0x5D1D,0x5D1D,0x5CFC,0x54FC,0x54DC,0x4CBC,0x4CBB,0x449B,0x651B,0xF7BE,0xFFDF,0xB5B6,0x0000, // row 14, 480 pixels -0x0000,0xC638,0xFFDF,0xF7BE,0x54DB,0x449B,0x4CBB,0x4CBC,0x54DC,0x54FC,0x54FC,0x5D1D,0x5D1D,0x7D7D,0xF7BE,0xF7BE,0xF7BE,0xF7BE,0x7D7D,0x5CFD,0x54FC,0x54FC,0x54DC,0x4CBC,0x4CBB,0x449B,0x447B,0x54BB,0xF7BE,0xFFDF,0xC638,0x0000, // row 15, 512 pixels -0x0000,0xC638,0xFFDF,0xF79E,0x4CBB,0x449B,0x449B,0x4CBB,0x4CBC,0x54DC,0x54DC,0x54FC,0x54DC,0x753C,0xF7BE,0xF7BE,0xF7BE,0xF7BE,0x753C,0x54DC,0x54DC,0x54DC,0x4CBC,0x4CBB,0x449B,0x449B,0x3C7B,0x4C9B,0xF79E,0xFFDF,0xC638,0x0000, // row 16, 544 pixels -0x0000,0xB5B6,0xFFDF,0xF7BE,0x5CFB,0x3C7B,0x447B,0x449B,0x4CBB,0x4CBC,0x4CBC,0x4CDC,0x4CBC,0x6D1C,0xF7BE,0xF7BE,0xF7BE,0xF7BE,0x6CFC,0x4CBC,0x4CBC,0x4CBC,0x4CBB,0x449B,0x447B,0x3C7B,0x3C5A,0x54DB,0xF7BE,0xFFDF,0xB5B6,0x0000, // row 17, 576 pixels -0x0000,0x94B2,0xF7BE,0xF7BE,0x755B,0x3C5A,0x3C7B,0x447B,0x449B,0x449B,0x4CBB,0x4CBB,0x4C9B,0x6CFB,0xF79E,0xF79E,0xF79E,0xF79E,0x64FB,0x449B,0x4CBB,0x449B,0x449B,0x447B,0x3C7B,0x3C5A,0x3C5A,0x753B,0xF7BE,0xF7BE,0x9CD3,0x0000, // row 18, 608 pixels -0x0000,0x6B4D,0xEF7D,0xF7BE,0xA61C,0x3C5A,0x3C5A,0x3C7B,0x447B,0x447B,0x449B,0x449B,0x447B,0x64DB,0xF79E,0xF79E,0xF79E,0xF79E,0x64DB,0x447B,0x449B,0x447B,0x447B,0x3C7B,0x3C5A,0x3C5A,0x343A,0xA61C,0xF7BE,0xEF7D,0x6B4D,0x0000, // row 19, 640 pixels -0x0000,0x2124,0xE71C,0xFFDF,0xDF3D,0x3C5A,0x343A,0x3C5A,0x3C5A,0x3C7B,0x3C7B,0x447B,0x3C5B,0x64BA,0xF79E,0xF79E,0xF79E,0xF79E,0x64BA,0x3C5B,0x3C7B,0x3C7B,0x3C5A,0x3C5A,0x343A,0x343A,0x343A,0xDF3D,0xFFDF,0xE71C,0x2124,0x0000, // row 20, 672 pixels -0x0000,0x0000,0xAD75,0xF7BE,0xF79E,0x859B,0x343A,0x343A,0x345A,0x3C5A,0x3C5A,0x3C5A,0x3C5A,0x5C9A,0xEF7D,0xEF7D,0xEF7D,0xEF7D,0x5C9A,0x3C3A,0x3C5A,0x3C5A,0x345A,0x343A,0x343A,0x341A,0x859B,0xF79E,0xF7BE,0xAD75,0x0000,0x0000, // row 21, 704 pixels -0x0000,0x0000,0x528A,0xE71C,0xFFDF,0xDF3D,0x3C5A,0x343A,0x343A,0x343A,0x343A,0x3C5A,0x343A,0x4C5A,0xEF7D,0xEF7D,0xEF7D,0xEF7D,0x4C59,0x343A,0x343A,0x343A,0x343A,0x343A,0x341A,0x3C5A,0xDF3D,0xFFDF,0xE71C,0x528A,0x0000,0x0000, // row 22, 736 pixels -0x0000,0x0000,0x0000,0x9CD3,0xF79E,0xF7BE,0xBE7C,0x3419,0x341A,0x341A,0x343A,0x343A,0x341A,0x2B99,0xC69C,0xEF7D,0xEF7D,0xD6DC,0x2398,0x341A,0x343A,0x341A,0x341A,0x2C19,0x2C19,0xBE7C,0xF7BE,0xF79E,0x9CD3,0x0000,0x0000,0x0000, // row 23, 768 pixels -0x0000,0x0000,0x0000,0x39E7,0xDEDB,0xFFDF,0xF79E,0x9DFB,0x2C19,0x2C19,0x2C1A,0x341A,0x341A,0x2BB9,0x2B57,0x6459,0x74B9,0x2337,0x2BB9,0x341A,0x2C1A,0x2C19,0x2C19,0x2C19,0x9DFB,0xF79E,0xFFDF,0xDEDB,0x39E7,0x0000,0x0000,0x0000, // row 24, 800 pixels -0x0000,0x0000,0x0000,0x0000,0x632C,0xDEFB,0xFFDF,0xEF7D,0xB65C,0x3C39,0x2BF9,0x2C19,0x2C19,0x2BF9,0x2398,0x1B58,0x1B37,0x2398,0x2BF9,0x2C19,0x2BF9,0x2BF9,0x3439,0xB65C,0xEF7D,0xFFDF,0xDEFB,0x632C,0x0000,0x0000,0x0000,0x0000, // row 25, 832 pixels -0x0000,0x0000,0x0000,0x0000,0x0000,0x73AE,0xDEFB,0xF7BE,0xF79E,0xDF1C,0x7D5A,0x2BF9,0x2BF9,0x2BF9,0x2BF9,0x23D9,0x23D9,0x2BF9,0x2BF9,0x2BF9,0x2BF9,0x7D5A,0xDF1C,0xF79E,0xF7BE,0xDEFB,0x73AE,0x0000,0x0000,0x0000,0x0000,0x0000, // row 26, 864 pixels -0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x632C,0xDEDB,0xF79E,0xFFDF,0xEF7D,0xD6FC,0x9DFB,0x5CDA,0x4C9A,0x3419,0x3419,0x4C9A,0x5CDA,0x9DFB,0xD6FC,0xEF7D,0xFFDF,0xF79E,0xDEDB,0x632C,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 27, 896 pixels -0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x4208,0x94B2,0xDEFB,0xF7BE,0xFFDF,0xF7BE,0xF79E,0xEF7D,0xEF5D,0xEF5D,0xEF7D,0xF79E,0xF7BE,0xFFDF,0xF7BE,0xDEFB,0x94B2,0x4208,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 28, 928 pixels -0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x528A,0xA534,0xDEDB,0xE73C,0xF79E,0xF7BE,0xF7BE,0xF7BE,0xF7BE,0xF79E,0xE73C,0xDEDB,0xA534,0x528A,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 29, 960 pixels -0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x18C3,0x5AEB,0x8C71,0xAD55,0xBDD7,0xBDD7,0xAD55,0x8C71,0x5AEB,0x18C3,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 30, 992 pixels -0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000}; // row 31, 1024 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 deleted file mode 100644 index 82e09eb..0000000 --- a/examples/320 x 240/TFT_Flash_Bitmap/TFT_Flash_Bitmap.ino +++ /dev/null @@ -1,106 +0,0 @@ -// Icons are stored in tabs ^ e.g. Alert.h etc above this line -// more than one icon can be in a header file - -/* - This sketch demonstrates loading images from arrays stored in program (FLASH) memory. - - Works with TFT_eSPI library here: - https://github.com/Bodmer/TFT_eSPI - - This sketch does not use/need any fonts at all... - - Code derived from ILI9341_due example - - Make sure all the display driver and pin comnenctions 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 ###### - ######################################################################### -*/ - -#include // Hardware-specific library - -TFT_eSPI tft = TFT_eSPI(); // Invoke custom library - -// Include the header files that contain the icons -#include "Alert.h" -#include "Close.h" -#include "Info.h" - -long count = 0; // Loop count - -void setup() -{ - Serial.begin(115200); - tft.begin(); - tft.setRotation(1); // landscape - - tft.fillScreen(TFT_BLACK); - - // Draw the icons - drawIcon(info, 100, 100, infoWidth, infoHeight); - drawIcon(alert, 140, 100, alertWidth, alertHeight); - drawIcon(closeX, 180, 100, closeWidth, closeHeight); - - // Pause here to admire the icons! - delay(2000); - -} - -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(closeX, random(tft.width() - closeWidth), random(tft.height() - closeHeight), alertWidth, closeHeight); - - // Clear screen after 100 x 3 = 300 icons drawn - if (100 == count++) { - count = 1; - tft.setRotation(2 * random(2)); // Rotate randomly to clear display left>right or right>left to reduce monotony! - tft.fillScreen(TFT_BLACK); - tft.setRotation(1); - //Serial.println(millis()); - } -} - - -//==================================================================================== -// This is the function to draw the icon stored as an array in program memory (FLASH) -//==================================================================================== - -// To speed up rendering we use a 64 pixel buffer -#define BUFF_SIZE 64 - -// 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, uint16_t width, uint16_t height) { - - uint16_t pix_buffer[BUFF_SIZE]; // Pixel buffer (16 bits per pixel) - - // Set up a window the right size to stream pixels into - tft.setWindow(x, y, x + width - 1, y + height - 1); - - // Work out the number whole buffers to send - uint16_t nb = ((uint16_t)height * width) / BUFF_SIZE; - - // Fill and send "nb" buffers to TFT - for (int i = 0; i < nb; i++) { - for (int j = 0; j < BUFF_SIZE; j++) { - pix_buffer[j] = pgm_read_word(&icon[i * BUFF_SIZE + j]); - } - tft.pushColors(pix_buffer, BUFF_SIZE); - } - - // Work out number of pixels not yet sent - uint16_t np = ((uint16_t)height * width) % BUFF_SIZE; - - // Send any partial buffer left over - if (np) { - for (int i = 0; i < np; i++) pix_buffer[i] = pgm_read_word(&icon[nb * BUFF_SIZE + i]); - tft.pushColors(pix_buffer, np); - } -} - diff --git a/examples/480 x 320/Flash_Bitmap/Alert.h b/examples/480 x 320/Flash_Bitmap/Alert.h deleted file mode 100644 index 54d013d..0000000 --- a/examples/480 x 320/Flash_Bitmap/Alert.h +++ /dev/null @@ -1,42 +0,0 @@ -// We need this header file to use FLASH as storage with PROGMEM directive: -#include - -// Icon width and height -const uint16_t alertWidth = 32; -const uint16_t alertHeight = 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 alert[1024] PROGMEM={ -0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0840,0x0000,0x0000,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,0x0000,0x0000,0x0000,0x1080,0xAC66,0xEDE8,0xFE69,0xC4C6,0x2901,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 1, 64 pixels -0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0xBCC6,0xFE68,0xFE68,0xFE6A,0xFE68,0xEDE8,0x18A1,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 2, 96 pixels -0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x8344,0xFE48,0xFE8C,0xFFDD,0xFFFF,0xFEF0,0xFE48,0xB466,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 3, 128 pixels -0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x1880,0xEDC7,0xFE48,0xFF99,0xFFBC,0xFF9B,0xFFBD,0xFE6A,0xFE48,0x5A23,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 4, 160 pixels -0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x9BE5,0xFE28,0xFED0,0xFFBC,0xFF7A,0xFF9A,0xFF9B,0xFF35,0xFE28,0xBCA6,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 5, 192 pixels -0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x3962,0xFE28,0xFE28,0xFF9A,0xFF79,0xFF9A,0xFF9B,0xFF9A,0xFFBD,0xFE6B,0xFE28,0x72E3,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 6, 224 pixels -0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0xB465,0xFE28,0xFEF2,0xFF7A,0xFF79,0xFF7A,0xFF9A,0xFF7A,0xFF7A,0xFF78,0xFE28,0xDD67,0x0860,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 7, 256 pixels -0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x5A22,0xFE07,0xFE29,0xFF9B,0xFF37,0xFF58,0xFF79,0xFF79,0xFF79,0xFF58,0xFF9B,0xFEAE,0xFE07,0x93A4,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 8, 288 pixels -0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0xC4A5,0xFE07,0xFF15,0xFF37,0xFF36,0xAD11,0x2965,0x2965,0xCDF4,0xFF37,0xFF37,0xFF79,0xFE07,0xFE07,0x2901,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 9, 320 pixels -0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x7B03,0xFDE7,0xFE4B,0xFF79,0xFEF4,0xFF15,0xB552,0x2945,0x2945,0xDE55,0xFF16,0xFF15,0xFF58,0xFED1,0xFDE7,0xAC25,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 10, 352 pixels -0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0840,0xDD26,0xFDE7,0xFF57,0xFED3,0xFED2,0xFEF4,0xBD93,0x2124,0x2124,0xDE75,0xFF14,0xFED3,0xFED3,0xFF7A,0xFE08,0xFDE7,0x49A2,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 11, 384 pixels -0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x9BA4,0xFDC6,0xFE6E,0xFF36,0xFE90,0xFEB1,0xFED3,0xC592,0x2124,0x2124,0xE675,0xFED3,0xFEB2,0xFEB1,0xFEF3,0xFEF3,0xFDC6,0xBC45,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 12, 416 pixels -0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x3141,0xF5C6,0xF5C7,0xFF58,0xFE90,0xFE6F,0xFE8F,0xFEB1,0xCDB2,0x2104,0x2104,0xF6B4,0xFEB1,0xFE90,0xFE8F,0xFE90,0xFF58,0xFE0A,0xF5C6,0x72A3,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 13, 448 pixels -0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0xABE4,0xF5A6,0xFEB1,0xFED3,0xFE4E,0xFE6E,0xFE6F,0xFE90,0xD5F2,0x18E3,0x18E3,0xFED4,0xFE90,0xFE6F,0xFE6F,0xFE6E,0xFE91,0xFF36,0xF5A6,0xCCA5,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 14, 480 pixels -0x0000,0x0000,0x0000,0x0000,0x0000,0x5202,0xF5A6,0xF5C7,0xFF58,0xFE4D,0xFE4D,0xFE4D,0xFE4E,0xFE6F,0xDE11,0x18C3,0x18C3,0xFED3,0xFE6F,0xFE6E,0xFE4E,0xFE4D,0xFE4D,0xFF16,0xFE2C,0xF5A6,0x9363,0x0000,0x0000,0x0000,0x0000,0x0000, // row 15, 512 pixels -0x0000,0x0000,0x0000,0x0000,0x0000,0xBC44,0xF585,0xFED3,0xFE6F,0xFE2C,0xFE2C,0xFE2D,0xFE4D,0xFE4E,0xE630,0x10A2,0x2104,0xFED1,0xFE4E,0xFE4D,0xFE4D,0xFE2D,0xFE2C,0xFE4D,0xFF37,0xF586,0xF585,0x28E1,0x0000,0x0000,0x0000,0x0000, // row 16, 544 pixels -0x0000,0x0000,0x0000,0x0000,0x7282,0xF565,0xF5EA,0xFF16,0xFE0B,0xFE0B,0xFE0B,0xFE2C,0xFE2C,0xFE4D,0xF670,0x1082,0x2924,0xFEB0,0xFE2D,0xFE2C,0xFE2C,0xFE2C,0xFE0B,0xFE0B,0xFEB2,0xFE6F,0xF565,0xA383,0x0000,0x0000,0x0000,0x0000, // row 17, 576 pixels -0x0000,0x0000,0x0000,0x0840,0xD4C4,0xF565,0xFEF5,0xFE0C,0xFDE9,0xFDEA,0xFE0A,0xFE0B,0xFE0B,0xFE2C,0xFE8F,0x0861,0x2964,0xFE8F,0xFE2C,0xFE0B,0xFE0B,0xFE0B,0xFE0A,0xFDEA,0xFE0B,0xFF37,0xF586,0xF565,0x4181,0x0000,0x0000,0x0000, // row 18, 608 pixels -0x0000,0x0000,0x0000,0x9343,0xF545,0xF60C,0xFED3,0xFDC8,0xFDC8,0xFDC9,0xFDE9,0xFDEA,0xFDEA,0xFE0B,0xFE8E,0x0861,0x3184,0xFE6D,0xFE0B,0xFE0A,0xFDEA,0xFDEA,0xFDE9,0xFDC9,0xFDC9,0xFE4E,0xFEB2,0xF545,0xB3E3,0x0000,0x0000,0x0000, // row 19, 640 pixels -0x0000,0x0000,0x28E0,0xF544,0xF545,0xFF17,0xFDC8,0xFDA7,0xFDA7,0xFDC8,0xFDC8,0xFDC9,0xFDC9,0xFDE9,0xFE6C,0x10A2,0x39C4,0xFE4C,0xFDEA,0xFDE9,0xFDC9,0xFDC9,0xFDC8,0xFDC8,0xFDA7,0xFDA8,0xFF16,0xF588,0xF544,0x6222,0x0000,0x0000, // row 20, 672 pixels -0x0000,0x0000,0xA383,0xF524,0xF64E,0xFE4E,0xFD86,0xFD86,0xFD87,0xFDA7,0xFDA7,0xFDA8,0xFDC8,0xFDC8,0xFE2A,0xA469,0xB4EA,0xFE2A,0xFDC9,0xFDC8,0xFDC8,0xFDA8,0xFDA7,0xFDA7,0xFD87,0xFD86,0xFDEA,0xFED3,0xF524,0xC443,0x0000,0x0000, // row 21, 704 pixels -0x0000,0x51C1,0xF504,0xF546,0xFF16,0xF565,0xFD65,0xFD65,0xFD86,0xFD86,0xFD86,0xFDA7,0xFDA7,0xFDA7,0xFDE8,0xFE6A,0xFE4A,0xFDE8,0xFDA7,0xFDA7,0xFDA7,0xFDA7,0xFD86,0xFD86,0xFD86,0xFD65,0xFD65,0xFEB2,0xF5CA,0xF504,0x8AE2,0x0000, // row 22, 736 pixels -0x0000,0xB3A2,0xED03,0xFE92,0xFDC9,0xF543,0xF544,0xFD44,0xFD65,0xFD65,0xFD65,0xFD86,0xFD86,0xFD86,0xFDA7,0xFDC7,0xFDC7,0xFDA7,0xFD86,0xFD86,0xFD86,0xFD86,0xFD65,0xFD65,0xFD65,0xFD44,0xF544,0xFD86,0xFEF5,0xED03,0xE4C3,0x1880, // row 23, 768 pixels -0x7241,0xECE3,0xF567,0xFED3,0xF523,0xF523,0xF523,0xF543,0xF544,0xF544,0xFD65,0xFD65,0xFD65,0xFD65,0xD4E6,0x39C5,0x39A5,0xD4E6,0xFD86,0xFD65,0xFD65,0xFD65,0xFD65,0xF544,0xF544,0xF543,0xF523,0xF523,0xFE2E,0xF5EC,0xECE3,0x9B42, // row 24, 800 pixels -0xD443,0xECE3,0xFED4,0xF565,0xF502,0xF502,0xF522,0xF523,0xF523,0xF543,0xF544,0xF544,0xF544,0xFD65,0x8B64,0x18C3,0x18C3,0x8344,0xFD85,0xFD44,0xF544,0xF544,0xF544,0xF543,0xF523,0xF523,0xF522,0xF502,0xF523,0xFEF5,0xED04,0xECE3, // row 25, 832 pixels -0xECC3,0xF5AB,0xFE6F,0xF501,0xF4E1,0xF501,0xF502,0xF502,0xF522,0xF522,0xF523,0xF523,0xF523,0xFD84,0xC504,0x20E1,0x18E1,0xC4E4,0xFD84,0xF543,0xF523,0xF523,0xF523,0xF522,0xF522,0xF502,0xF502,0xF501,0xF501,0xFDC9,0xF62F,0xECC3, // row 26, 864 pixels -0xECC2,0xFE92,0xF523,0xF4E0,0xF4E0,0xF4E1,0xF4E1,0xF501,0xF501,0xF502,0xF502,0xF522,0xF522,0xF543,0xFDE3,0xFEA5,0xF6A4,0xFE04,0xF543,0xF522,0xF522,0xF522,0xF502,0xF502,0xF501,0xF501,0xF4E1,0xF4E1,0xF4E0,0xF4E1,0xFED4,0xECC2, // row 27, 896 pixels -0xECA2,0xF5EC,0xF4E0,0xF4C0,0xF4E0,0xF4E0,0xF4E0,0xF4E1,0xF4E1,0xF501,0xF501,0xF501,0xF502,0xF502,0xF542,0xFDA2,0xFDA2,0xF542,0xF502,0xF502,0xF502,0xF501,0xF501,0xF501,0xF4E1,0xF4E1,0xF4E0,0xF4E0,0xF4E0,0xF4C0,0xF5A9,0xECA2, // row 28, 928 pixels -0xECA2,0xECA2,0xECC2,0xF4C1,0xF4C1,0xF4C1,0xF4C1,0xF4C1,0xF4C1,0xF4C1,0xF4C1,0xF4E1,0xF4E2,0xF4E2,0xF4E2,0xF4E2,0xF4E2,0xF4E2,0xF4E2,0xF4E2,0xF4E2,0xF4E1,0xF4C1,0xF4C1,0xF4C1,0xF4C1,0xF4C1,0xF4C1,0xF4C1,0xECC2,0xECC3,0xECA2, // row 29, 960 pixels -0x8AC1,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0x9B01, // row 30, 992 pixels -0x0000,0x1880,0x51A0,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x61E0,0x28E0,0x0000}; // row 31, 1024 pixels - diff --git a/examples/480 x 320/Flash_Bitmap/Close.h b/examples/480 x 320/Flash_Bitmap/Close.h deleted file mode 100644 index ce676c9..0000000 --- a/examples/480 x 320/Flash_Bitmap/Close.h +++ /dev/null @@ -1,41 +0,0 @@ -// We need this header file to use FLASH as storage with PROGMEM directive: -#include - -// Icon width and height -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 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 -0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x2041,0x8945,0xF34D,0xFD34,0xFDB6,0xFD75,0xFD55,0xFD55,0xFD96,0xFDD7,0xFDF7,0xFDF7,0xFDB6,0xFDB6,0xFDD7,0xFDF7,0xFD75,0xF38E,0x8965,0x2041,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 3, 128 pixels -0x0000,0x0000,0x0000,0x0000,0x0000,0x4082,0xE208,0xF410,0xFD34,0xFC92,0xFBEF,0xFBAE,0xFBEF,0xFC71,0xFD14,0xFD75,0xFDB6,0xFD75,0xFD14,0xFC92,0xFC51,0xFC71,0xFCF3,0xFD75,0xFC30,0xEA28,0x40A2,0x0000,0x0000,0x0000,0x0000,0x0000, // row 4, 160 pixels -0x0000,0x0000,0x0000,0x0000,0x3861,0xE1E7,0xF451,0xFC92,0xFB4D,0xFA49,0xFA49,0xFAEB,0xFBAE,0xFC71,0xFD34,0xFDB6,0xFE18,0xFDB6,0xFD34,0xFC71,0xFBAE,0xFB0C,0xFAEB,0xFBAE,0xFCD3,0xFC71,0xE208,0x4082,0x0000,0x0000,0x0000,0x0000, // row 5, 192 pixels -0x0000,0x0000,0x0000,0x1020,0xD986,0xF430,0xFC30,0xFA28,0xF924,0xF965,0xFA8A,0xFB0C,0xFBAE,0xFC51,0xFD14,0xFD75,0xFDB6,0xFD75,0xFD14,0xFC51,0xFC71,0xFBEF,0xFA28,0xF9C7,0xFA8A,0xFC51,0xF430,0xD9A6,0x1020,0x0000,0x0000,0x0000, // row 6, 224 pixels -0x0000,0x0000,0x0000,0x78A2,0xEB6D,0xFC30,0xF9C7,0xF861,0xF8A2,0xFA08,0xFEDB,0xFD55,0xFB4D,0xFC10,0xFC92,0xFD14,0xFD34,0xFD14,0xFC92,0xFCB2,0xFF7D,0xFF7D,0xFB2C,0xF945,0xF8E3,0xF9E7,0xFC30,0xEB8E,0x78C3,0x0000,0x0000,0x0000, // row 7, 256 pixels -0x0000,0x0000,0x3841,0xD9E7,0xF492,0xF208,0xF041,0xF800,0xF945,0xFE9A,0xFFFF,0xFFFF,0xFD75,0xFB8E,0xFC10,0xFC51,0xFC71,0xFC51,0xFCB2,0xFF7D,0xFFFF,0xFFFF,0xFF3C,0xFA8A,0xF882,0xF841,0xFA08,0xFC92,0xDA08,0x3841,0x0000,0x0000, // row 8, 288 pixels -0x0000,0x0000,0x88A2,0xEBCF,0xF2EB,0xF061,0xF000,0xF8E3,0xFE79,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFD75,0xFB4D,0xFBAE,0xFBAE,0xFC71,0xFF7D,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFEFB,0xFA28,0xF800,0xF061,0xF2EB,0xEBEF,0x90C3,0x0000,0x0000, // row 9, 320 pixels -0x0000,0x2820,0xD1C7,0xF410,0xE945,0xE800,0xF000,0xFE9A,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFD34,0xFAEB,0xFBCF,0xFF5D,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFF1C,0xF986,0xF000,0xF145,0xF410,0xD1E7,0x2820,0x0000, // row 10, 352 pixels -0x0000,0x6841,0xDB2C,0xEACB,0xE041,0xE800,0xF000,0xFEFB,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFD14,0xFF1C,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFBCF,0xF082,0xF000,0xE841,0xEACB,0xE34D,0x7061,0x0000, // row 11, 384 pixels -0x0000,0x9861,0xE3CF,0xE186,0xE000,0xE800,0xE800,0xF145,0xFEDB,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFB8E,0xF000,0xF000,0xE800,0xE800,0xE986,0xEBCF,0xA082,0x0000, // row 12, 416 pixels -0x0800,0xB8A2,0xE3AE,0xD8A2,0xD800,0xE000,0xE800,0xE800,0xF145,0xFEFB,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFB8E,0xF000,0xF000,0xE800,0xE800,0xE000,0xE0A2,0xEBAE,0xC0C3,0x0800, // row 13, 448 pixels -0x1800,0xC124,0xE30C,0xD020,0xD800,0xE000,0xE000,0xE800,0xE800,0xF145,0xFEDB,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFB8E,0xF000,0xF000,0xE800,0xE800,0xE000,0xE000,0xD820,0xE30C,0xC124,0x1800, // row 14, 480 pixels -0x2800,0xC165,0xDAAA,0xC800,0xD000,0xD800,0xE000,0xE000,0xE800,0xE800,0xF124,0xFE79,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFB6D,0xF000,0xF000,0xE800,0xE800,0xE000,0xE000,0xD800,0xD000,0xDAAA,0xC165,0x2800, // row 15, 512 pixels -0x2000,0xB924,0xD269,0xC800,0xD000,0xD000,0xD800,0xE000,0xE000,0xE800,0xE924,0xFEFB,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xF36D,0xE800,0xE800,0xE800,0xE000,0xE000,0xD800,0xD000,0xD000,0xDA69,0xC145,0x2800, // row 16, 544 pixels -0x1000,0xB0A2,0xD28A,0xC000,0xC800,0xD000,0xD000,0xD800,0xD800,0xE165,0xFEFB,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xF3AE,0xE000,0xE000,0xD800,0xD800,0xD000,0xD000,0xC800,0xD28A,0xB8C3,0x1000, // row 17, 576 pixels -0x0000,0xA800,0xD2AA,0xB800,0xC000,0xC800,0xC800,0xD000,0xD965,0xFEFB,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xEBAE,0xD800,0xD800,0xD000,0xC800,0xC800,0xC000,0xD2AA,0xB020,0x0000, // row 18, 608 pixels -0x0000,0x8000,0xCA69,0xB841,0xB800,0xC000,0xC800,0xD186,0xFEFB,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xEBCF,0xD000,0xC800,0xC800,0xC000,0xC041,0xCA69,0x8000,0x0000, // row 19, 640 pixels -0x0000,0x4800,0xC1C7,0xB8E3,0xB800,0xB800,0xC000,0xF69A,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xEBEF,0xFE79,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xE410,0xC841,0xC000,0xB800,0xC0E3,0xC1C7,0x4800,0x0000, // row 20, 672 pixels -0x0000,0x1000,0xB061,0xC1E7,0xB000,0xB000,0xB800,0xD269,0xFFBE,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xE38E,0xD000,0xD965,0xF69A,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xDB0C,0xC020,0xB800,0xB000,0xC1E7,0xB061,0x1000,0x0000, // row 21, 704 pixels -0x0000,0x0000,0x6000,0xB9C7,0xB061,0xB000,0xB000,0xB800,0xCA49,0xFF9E,0xFFFF,0xFFFF,0xFFFF,0xE38E,0xC800,0xC800,0xC800,0xD186,0xF69A,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xDB0C,0xB800,0xB800,0xB000,0xB061,0xC1C7,0x6000,0x0000,0x0000, // row 22, 736 pixels -0x0000,0x0000,0x1800,0xB041,0xB986,0xA800,0xA800,0xB000,0xB000,0xCA49,0xFF7D,0xFFFF,0xDB8E,0xC000,0xC000,0xC000,0xC000,0xC000,0xC986,0xF6DB,0xFFFF,0xFFFF,0xD30C,0xB800,0xB000,0xB000,0xA800,0xB986,0xB041,0x1800,0x0000,0x0000, // row 23, 768 pixels -0x0000,0x0000,0x0000,0x5800,0xB0E3,0xA8C3,0xA800,0xA800,0xA800,0xB000,0xCACB,0xD38E,0xB000,0xB800,0xB800,0xB800,0xB800,0xB800,0xB800,0xC145,0xF6DB,0xD34D,0xB000,0xB000,0xA800,0xA800,0xB0C3,0xB0E3,0x5800,0x0000,0x0000,0x0000, // row 24, 800 pixels -0x0000,0x0000,0x0000,0x0000,0x6000,0xB124,0xA882,0xA000,0xA800,0xA800,0xA800,0xA800,0xB000,0xB000,0xB000,0xB000,0xB000,0xB000,0xB000,0xB000,0xB000,0xA800,0xA800,0xA800,0xA800,0xA882,0xB124,0x6000,0x0000,0x0000,0x0000,0x0000, // row 25, 832 pixels -0x0000,0x0000,0x0000,0x0000,0x0000,0x6000,0xB104,0xA882,0xA000,0xA000,0xA000,0xA800,0xA800,0xA800,0xA800,0xA800,0xA800,0xA800,0xA800,0xA800,0xA800,0xA800,0xA000,0xA000,0xA882,0xB104,0x6000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 26, 864 pixels -0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x6000,0xB0A2,0xA8C3,0xA020,0xA000,0xA000,0xA000,0xA000,0xA000,0xA000,0xA000,0xA000,0xA000,0xA000,0xA000,0xA000,0xA020,0xA8C3,0xB0A2,0x6000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 27, 896 pixels -0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x4800,0xA800,0xB0C3,0xA0A2,0x9800,0x9800,0x9800,0x9800,0xA000,0xA000,0xA000,0x9800,0x9800,0x9800,0xA082,0xB0E3,0xA800,0x4800,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 28, 928 pixels -0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x5800,0xA800,0xB0A2,0xA8E3,0xA0A2,0xA041,0x9800,0x9800,0xA041,0xA0A2,0xA8E3,0xB0A2,0xA800,0x5800,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 29, 960 pixels -0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x3000,0x6000,0x8800,0xA000,0xA800,0xA800,0xA000,0x8800,0x6000,0x3000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 30, 992 pixels -0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000}; // row 31, 1024 pixels diff --git a/examples/480 x 320/Flash_Bitmap/Flash_Bitmap.ino b/examples/480 x 320/Flash_Bitmap/Flash_Bitmap.ino deleted file mode 100644 index d3ca697..0000000 --- a/examples/480 x 320/Flash_Bitmap/Flash_Bitmap.ino +++ /dev/null @@ -1,113 +0,0 @@ -// Code partly derived from ILI9341_due library example - -// Draws the 3 icons across the middle of the screen and pauses. -// Then draws 300 icons at random locations, clears screen and repeats -// -// This demonstrates drawing icons from FLASH - -// Icons are stored in tabs, e.g. Alert.h etc -// more than one icon can be in a header file. - -/* - This sketch demonstrates loading images from arrays stored in program (FLASH) memory. - - Works with TFT_eSPI library here: - https://github.com/Bodmer/TFT_eSPI - - This sketch does not use/need any fonts at all... - - Arrays containing FLASH images can be created with UTFT library tool: - (libraries\UTFT\Tools\ImageConverter565.exe) - Convert to .c format then copy into a new tab - - The number and size of icons is limited by available FLASH memory. The icon array will - use width x height x 2 bytes of FLASH, i.e. 32 x 32 icon uses ~2048 bytes - -*/ - -#include // Hardware-specific library -#include - -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 "Close.h" -#include "Info.h" - -long count = 0; // Loop count - -void setup() -{ - Serial.begin(115200); - tft.begin(); - tft.setRotation(1); // landscape - - tft.fillScreen(TFT_BLACK); - - // 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(closeX, (tft.width() - closeWidth)/2 + 50, (tft.height() - closeHeight)/2, closeWidth, closeHeight); - - // Pause here to admire the icons! - delay(4000); - -} - -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(closeX, random(tft.width() - closeWidth), random(tft.height() - closeHeight), alertWidth, closeHeight); - - // Clear screen after 100 x 3 = 300 icons drawn - if (100 == count++) { - count = 1; - tft.setRotation(2 * random(2)); // Rotate randomly to clear display left>right or right>left to reduce monotony! - tft.fillScreen(TFT_BLACK); - tft.setRotation(1); - Serial.println(millis()); - } -} - - -//==================================================================================== -// This is the function to draw the icon stored as an array in program memory (FLASH) -//==================================================================================== - -// To speed up rendering we use a 64 pixel buffer -#define BUFF_SIZE 64 - -// 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, uint16_t width, uint16_t height) { - - uint16_t pix_buffer[BUFF_SIZE]; // Pixel buffer (16 bits per pixel) - - // Set up a window the right size to stream pixels into - tft.setWindow(x, y, x + width - 1, y + height - 1); - - // Work out the number whole buffers to send - uint16_t nb = ((uint16_t)height * width) / BUFF_SIZE; - - // Fill and send "nb" buffers to TFT - for (int i = 0; i < nb; i++) { - for (int j = 0; j < BUFF_SIZE; j++) { - pix_buffer[j] = pgm_read_word(&icon[i * BUFF_SIZE + j]); - } - tft.pushColors(pix_buffer, BUFF_SIZE); - } - - // Work out number of pixels not yet sent - uint16_t np = ((uint16_t)height * width) % BUFF_SIZE; - - // Send any partial buffer left over - if (np) { - for (int i = 0; i < np; i++) pix_buffer[i] = pgm_read_word(&icon[nb * BUFF_SIZE + i]); - tft.pushColors(pix_buffer, np); - } -} - diff --git a/examples/480 x 320/Flash_Bitmap/Info.h b/examples/480 x 320/Flash_Bitmap/Info.h deleted file mode 100644 index c4ee633..0000000 --- a/examples/480 x 320/Flash_Bitmap/Info.h +++ /dev/null @@ -1,41 +0,0 @@ -// We need this header file to use FLASH as storage with PROGMEM directive: -#include - -// Icon width and height -const uint16_t infoWidth = 32; -const uint16_t infoHeight = 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 info[1024] PROGMEM={ -0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,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,0x0000,0x0861,0x4A69,0x8C71,0xA514,0xBDF7,0xBDF7,0xA514,0x8C71,0x4A69,0x0861,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 1, 64 pixels -0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x39E7,0x9CF3,0xEF7D,0xF79E,0xFFDF,0xFFDF,0xFFDF,0xFFDF,0xFFDF,0xFFDF,0xF79E,0xEF7D,0x9CF3,0x39E7,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 2, 96 pixels -0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x2965,0x9492,0xF79E,0xFFDF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFDF,0xF79E,0x9492,0x2965,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 3, 128 pixels -0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x630C,0xEF7D,0xFFDF,0xFFFF,0xFFFF,0xFFFF,0xD75F,0xB6BF,0x9E5F,0x963F,0x963F,0x9E5F,0xB6BF,0xD75F,0xFFFF,0xFFFF,0xFFFF,0xFFDF,0xEF7D,0x630C,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 4, 160 pixels -0x0000,0x0000,0x0000,0x0000,0x0000,0x73AE,0xEF7D,0xFFDF,0xFFFF,0xFFDF,0xBEDF,0x7DBF,0x7DBF,0x7DDF,0x7DDF,0x7DDF,0x7DDF,0x7DDF,0x7DBF,0x759F,0x7DBE,0xBEBF,0xFFDF,0xFFFF,0xFFDF,0xEF7D,0x73AE,0x0000,0x0000,0x0000,0x0000,0x0000, // row 5, 192 pixels -0x0000,0x0000,0x0000,0x0000,0x630C,0xEF7D,0xFFFF,0xFFFF,0xE77F,0x7DBE,0x759E,0x759F,0x7DBF,0x7DDF,0x7DDF,0x85FF,0x7DDF,0x7DDF,0x7DBF,0x759F,0x759E,0x6D7E,0x7DBE,0xDF7F,0xFFFF,0xFFFF,0xEF7D,0x630C,0x0000,0x0000,0x0000,0x0000, // row 6, 224 pixels -0x0000,0x0000,0x0000,0x31A6,0xEF5D,0xFFDF,0xFFFF,0xCF1E,0x6D7E,0x6D7E,0x759E,0x759F,0x7DBF,0x7DDF,0x8E1F,0xBEDF,0xC6FF,0x8DFF,0x75BF,0x759F,0x759E,0x6D7E,0x655E,0x655D,0xCF1E,0xFFFF,0xFFDF,0xEF5D,0x31A6,0x0000,0x0000,0x0000, // row 7, 256 pixels -0x0000,0x0000,0x0000,0x94B2,0xF7BE,0xFFFF,0xDF5E,0x655D,0x655D,0x6D7E,0x6D7E,0x759E,0x75BF,0x759F,0xEFBF,0xFFFF,0xFFFF,0xEFBF,0x759F,0x759E,0x6D7E,0x6D7E,0x655D,0x653D,0x653D,0xDF5E,0xFFFF,0xF7BE,0x94B2,0x0000,0x0000,0x0000, // row 8, 288 pixels -0x0000,0x0000,0x4228,0xEF7D,0xFFFF,0xF7BF,0x6D5D,0x653D,0x655D,0x6D5E,0x6D7E,0x759E,0x759E,0x85DF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0x8DFE,0x6D7E,0x6D7E,0x6D5E,0x655D,0x653D,0x5D1D,0x6D5D,0xF7BF,0xFFFF,0xEF7D,0x4228,0x0000,0x0000, // row 9, 320 pixels -0x0000,0x0000,0xA534,0xFFDF,0xFFDF,0xA65D,0x5D1D,0x5D1D,0x653D,0x655E,0x6D7E,0x6D7E,0x6D7E,0x651E,0xE77F,0xFFFF,0xFFFF,0xF7BF,0x5CFE,0x6D7E,0x6D7E,0x655E,0x653D,0x5D1D,0x5D1D,0x54FC,0xA65D,0xFFDF,0xFFDF,0xA534,0x0000,0x0000, // row 10, 352 pixels -0x0000,0x18E3,0xEF5D,0xFFFF,0xEF9E,0x5CFC,0x54FC,0x5D1D,0x5D3D,0x653D,0x655E,0x6D7E,0x6D7E,0x653E,0x6D3E,0xB67E,0xBEBE,0x755E,0x5D1E,0x6D5E,0x655E,0x653D,0x5D3D,0x5D1D,0x54FC,0x54DC,0x54FC,0xEF9E,0xFFFF,0xEF5D,0x18E3,0x0000, // row 11, 384 pixels -0x0000,0x630C,0xEF7D,0xFFDF,0xB69D,0x54DC,0x54FC,0x5CFC,0x5D1D,0x653D,0x653D,0x655E,0x6D5E,0x655E,0x5CFE,0x4C9D,0x4C7D,0x54DD,0x653E,0x655E,0x653D,0x653D,0x5D1D,0x5CFC,0x54FC,0x54DC,0x4CBC,0xB69D,0xFFDF,0xEF7D,0x630C,0x0000, // row 12, 416 pixels -0x0000,0x94B2,0xF7BE,0xFFDF,0x85BC,0x4CBC,0x54DC,0x54FC,0x5CFD,0x5D1D,0x5D3D,0x653D,0x655D,0x653D,0x85DE,0xC6FE,0xC6FE,0x85BE,0x653D,0x653D,0x5D3D,0x5D1D,0x5CFD,0x54FC,0x54DC,0x4CBC,0x4CBB,0x85BC,0xFFDF,0xF7BE,0x94B2,0x0000, // row 13, 448 pixels -0x0000,0xB5B6,0xFFDF,0xF7BE,0x651C,0x4CBB,0x4CBC,0x54DC,0x54FC,0x5CFC,0x5D1D,0x5D1D,0x653D,0x5D1D,0xE77E,0xFFDF,0xFFDF,0xEF9E,0x5CFD,0x5D1D,0x5D1D,0x5CFC,0x54FC,0x54DC,0x4CBC,0x4CBB,0x449B,0x651B,0xF7BE,0xFFDF,0xB5B6,0x0000, // row 14, 480 pixels -0x0000,0xC638,0xFFDF,0xF7BE,0x54DB,0x449B,0x4CBB,0x4CBC,0x54DC,0x54FC,0x54FC,0x5D1D,0x5D1D,0x7D7D,0xF7BE,0xF7BE,0xF7BE,0xF7BE,0x7D7D,0x5CFD,0x54FC,0x54FC,0x54DC,0x4CBC,0x4CBB,0x449B,0x447B,0x54BB,0xF7BE,0xFFDF,0xC638,0x0000, // row 15, 512 pixels -0x0000,0xC638,0xFFDF,0xF79E,0x4CBB,0x449B,0x449B,0x4CBB,0x4CBC,0x54DC,0x54DC,0x54FC,0x54DC,0x753C,0xF7BE,0xF7BE,0xF7BE,0xF7BE,0x753C,0x54DC,0x54DC,0x54DC,0x4CBC,0x4CBB,0x449B,0x449B,0x3C7B,0x4C9B,0xF79E,0xFFDF,0xC638,0x0000, // row 16, 544 pixels -0x0000,0xB5B6,0xFFDF,0xF7BE,0x5CFB,0x3C7B,0x447B,0x449B,0x4CBB,0x4CBC,0x4CBC,0x4CDC,0x4CBC,0x6D1C,0xF7BE,0xF7BE,0xF7BE,0xF7BE,0x6CFC,0x4CBC,0x4CBC,0x4CBC,0x4CBB,0x449B,0x447B,0x3C7B,0x3C5A,0x54DB,0xF7BE,0xFFDF,0xB5B6,0x0000, // row 17, 576 pixels -0x0000,0x94B2,0xF7BE,0xF7BE,0x755B,0x3C5A,0x3C7B,0x447B,0x449B,0x449B,0x4CBB,0x4CBB,0x4C9B,0x6CFB,0xF79E,0xF79E,0xF79E,0xF79E,0x64FB,0x449B,0x4CBB,0x449B,0x449B,0x447B,0x3C7B,0x3C5A,0x3C5A,0x753B,0xF7BE,0xF7BE,0x9CD3,0x0000, // row 18, 608 pixels -0x0000,0x6B4D,0xEF7D,0xF7BE,0xA61C,0x3C5A,0x3C5A,0x3C7B,0x447B,0x447B,0x449B,0x449B,0x447B,0x64DB,0xF79E,0xF79E,0xF79E,0xF79E,0x64DB,0x447B,0x449B,0x447B,0x447B,0x3C7B,0x3C5A,0x3C5A,0x343A,0xA61C,0xF7BE,0xEF7D,0x6B4D,0x0000, // row 19, 640 pixels -0x0000,0x2124,0xE71C,0xFFDF,0xDF3D,0x3C5A,0x343A,0x3C5A,0x3C5A,0x3C7B,0x3C7B,0x447B,0x3C5B,0x64BA,0xF79E,0xF79E,0xF79E,0xF79E,0x64BA,0x3C5B,0x3C7B,0x3C7B,0x3C5A,0x3C5A,0x343A,0x343A,0x343A,0xDF3D,0xFFDF,0xE71C,0x2124,0x0000, // row 20, 672 pixels -0x0000,0x0000,0xAD75,0xF7BE,0xF79E,0x859B,0x343A,0x343A,0x345A,0x3C5A,0x3C5A,0x3C5A,0x3C5A,0x5C9A,0xEF7D,0xEF7D,0xEF7D,0xEF7D,0x5C9A,0x3C3A,0x3C5A,0x3C5A,0x345A,0x343A,0x343A,0x341A,0x859B,0xF79E,0xF7BE,0xAD75,0x0000,0x0000, // row 21, 704 pixels -0x0000,0x0000,0x528A,0xE71C,0xFFDF,0xDF3D,0x3C5A,0x343A,0x343A,0x343A,0x343A,0x3C5A,0x343A,0x4C5A,0xEF7D,0xEF7D,0xEF7D,0xEF7D,0x4C59,0x343A,0x343A,0x343A,0x343A,0x343A,0x341A,0x3C5A,0xDF3D,0xFFDF,0xE71C,0x528A,0x0000,0x0000, // row 22, 736 pixels -0x0000,0x0000,0x0000,0x9CD3,0xF79E,0xF7BE,0xBE7C,0x3419,0x341A,0x341A,0x343A,0x343A,0x341A,0x2B99,0xC69C,0xEF7D,0xEF7D,0xD6DC,0x2398,0x341A,0x343A,0x341A,0x341A,0x2C19,0x2C19,0xBE7C,0xF7BE,0xF79E,0x9CD3,0x0000,0x0000,0x0000, // row 23, 768 pixels -0x0000,0x0000,0x0000,0x39E7,0xDEDB,0xFFDF,0xF79E,0x9DFB,0x2C19,0x2C19,0x2C1A,0x341A,0x341A,0x2BB9,0x2B57,0x6459,0x74B9,0x2337,0x2BB9,0x341A,0x2C1A,0x2C19,0x2C19,0x2C19,0x9DFB,0xF79E,0xFFDF,0xDEDB,0x39E7,0x0000,0x0000,0x0000, // row 24, 800 pixels -0x0000,0x0000,0x0000,0x0000,0x632C,0xDEFB,0xFFDF,0xEF7D,0xB65C,0x3C39,0x2BF9,0x2C19,0x2C19,0x2BF9,0x2398,0x1B58,0x1B37,0x2398,0x2BF9,0x2C19,0x2BF9,0x2BF9,0x3439,0xB65C,0xEF7D,0xFFDF,0xDEFB,0x632C,0x0000,0x0000,0x0000,0x0000, // row 25, 832 pixels -0x0000,0x0000,0x0000,0x0000,0x0000,0x73AE,0xDEFB,0xF7BE,0xF79E,0xDF1C,0x7D5A,0x2BF9,0x2BF9,0x2BF9,0x2BF9,0x23D9,0x23D9,0x2BF9,0x2BF9,0x2BF9,0x2BF9,0x7D5A,0xDF1C,0xF79E,0xF7BE,0xDEFB,0x73AE,0x0000,0x0000,0x0000,0x0000,0x0000, // row 26, 864 pixels -0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x632C,0xDEDB,0xF79E,0xFFDF,0xEF7D,0xD6FC,0x9DFB,0x5CDA,0x4C9A,0x3419,0x3419,0x4C9A,0x5CDA,0x9DFB,0xD6FC,0xEF7D,0xFFDF,0xF79E,0xDEDB,0x632C,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 27, 896 pixels -0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x4208,0x94B2,0xDEFB,0xF7BE,0xFFDF,0xF7BE,0xF79E,0xEF7D,0xEF5D,0xEF5D,0xEF7D,0xF79E,0xF7BE,0xFFDF,0xF7BE,0xDEFB,0x94B2,0x4208,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 28, 928 pixels -0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x528A,0xA534,0xDEDB,0xE73C,0xF79E,0xF7BE,0xF7BE,0xF7BE,0xF7BE,0xF79E,0xE73C,0xDEDB,0xA534,0x528A,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 29, 960 pixels -0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x18C3,0x5AEB,0x8C71,0xAD55,0xBDD7,0xBDD7,0xAD55,0x8C71,0x5AEB,0x18C3,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 30, 992 pixels -0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000}; // row 31, 1024 pixels diff --git a/examples/320 x 240/TFT_Flash_Bitmap/Alert.h b/examples/Generic/TFT_Flash_Bitmap/Alert.h similarity index 99% rename from examples/320 x 240/TFT_Flash_Bitmap/Alert.h rename to examples/Generic/TFT_Flash_Bitmap/Alert.h index 44196a9..0e5a895 100644 --- a/examples/320 x 240/TFT_Flash_Bitmap/Alert.h +++ b/examples/Generic/TFT_Flash_Bitmap/Alert.h @@ -38,4 +38,3 @@ const unsigned short alert[1024] PROGMEM={ 0xECA2,0xECA2,0xECC2,0xF4C1,0xF4C1,0xF4C1,0xF4C1,0xF4C1,0xF4C1,0xF4C1,0xF4C1,0xF4E1,0xF4E2,0xF4E2,0xF4E2,0xF4E2,0xF4E2,0xF4E2,0xF4E2,0xF4E2,0xF4E2,0xF4E1,0xF4C1,0xF4C1,0xF4C1,0xF4C1,0xF4C1,0xF4C1,0xF4C1,0xECC2,0xECC3,0xECA2, // row 29, 960 pixels 0x8AC1,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0x9B01, // row 30, 992 pixels 0x0000,0x1880,0x51A0,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x61E0,0x28E0,0x0000}; // row 31, 1024 pixels - diff --git a/examples/320 x 240/TFT_Flash_Bitmap/Close.h b/examples/Generic/TFT_Flash_Bitmap/Close.h similarity index 100% rename from examples/320 x 240/TFT_Flash_Bitmap/Close.h rename to examples/Generic/TFT_Flash_Bitmap/Close.h diff --git a/examples/320 x 240/TFT_Flash_Bitmap/Info.h b/examples/Generic/TFT_Flash_Bitmap/Info.h similarity index 100% rename from examples/320 x 240/TFT_Flash_Bitmap/Info.h rename to examples/Generic/TFT_Flash_Bitmap/Info.h diff --git a/examples/Generic/TFT_Flash_Bitmap/TFT_Flash_Bitmap.ino b/examples/Generic/TFT_Flash_Bitmap/TFT_Flash_Bitmap.ino new file mode 100644 index 0000000..54ac4e7 --- /dev/null +++ b/examples/Generic/TFT_Flash_Bitmap/TFT_Flash_Bitmap.ino @@ -0,0 +1,72 @@ +// Icon images are stored in tabs ^ e.g. Alert.h etc above this line +// more than one icon can be in a header file + +// Arrays containing FLASH images can be created with UTFT library tool: +// (libraries\UTFT\Tools\ImageConverter565.exe) +// Convert to .c format then copy into a new tab + +/* + This sketch demonstrates loading images from arrays stored in program (FLASH) memory. + + Works with TFT_eSPI library here: + https://github.com/Bodmer/TFT_eSPI + + This sketch does not use/need any fonts at all... + + Code derived from ILI9341_due library example + + Make sure all the display driver and pin comnenctions 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 ###### + ######################################################################### +*/ + +#include // Hardware-specific library + +TFT_eSPI tft = TFT_eSPI(); // Invoke custom library + +// Include the header files that contain the icons +#include "Alert.h" +#include "Close.h" +#include "Info.h" + +long count = 0; // Loop count + +void setup() +{ + Serial.begin(115200); + tft.begin(); + tft.setRotation(1); // landscape + + tft.fillScreen(TFT_BLACK); + + // Swap the colour byte order when rendering + tft.setSwapBytes(true); + + // Draw the icons + tft.pushImage(100, 100, infoWidth, infoHeight, info); + tft.pushImage(140, 100, alertWidth, alertHeight, alert); + tft.pushImage(180, 100, closeWidth, closeHeight, closeX); + + // Pause here to admire the icons! + delay(2000); + +} + +void loop() +{ + // Loop filling and clearing screen + tft.pushImage(random(tft.width() - infoWidth), random(tft.height() - infoHeight), infoWidth, infoHeight, info); + tft.pushImage(random(tft.width() - alertWidth), random(tft.height() - alertHeight), alertWidth, alertHeight, alert); + tft.pushImage(random(tft.width() - closeWidth), random(tft.height() - closeHeight), alertWidth, closeHeight, closeX); + + // Clear screen after 100 x 3 = 300 icons drawn + if (1000 == count++) { + count = 1; + tft.setRotation(2 * random(2)); // Rotate randomly to clear display left>right or right>left to reduce monotony! + tft.fillScreen(TFT_BLACK); + tft.setRotation(1); + } +} diff --git a/examples/Sprite/1_bit_Sprite_Demo/1_bit_Sprite_Demo.ino b/examples/Sprite/One_bit_Sprite_Demo/One_bit_Sprite_Demo.ino similarity index 100% rename from examples/Sprite/1_bit_Sprite_Demo/1_bit_Sprite_Demo.ino rename to examples/Sprite/One_bit_Sprite_Demo/One_bit_Sprite_Demo.ino diff --git a/examples/Sprite/1_bit_Yin_Yang/1_bit_Yin_Yang.ino b/examples/Sprite/One_bit_Yin_Yang/One_bit_Yin_Yang.ino similarity index 100% rename from examples/Sprite/1_bit_Yin_Yang/1_bit_Yin_Yang.ino rename to examples/Sprite/One_bit_Yin_Yang/One_bit_Yin_Yang.ino diff --git a/examples/Sprite/Rotated_Sprite_1/Rotated_Sprite_1.ino b/examples/Sprite/Rotated_Sprite_1/Rotated_Sprite_1.ino new file mode 100644 index 0000000..b789b50 --- /dev/null +++ b/examples/Sprite/Rotated_Sprite_1/Rotated_Sprite_1.ino @@ -0,0 +1,182 @@ +// This example plots a rotated Sprite to the screen using the pushRotated() +// function. It is written for a 240 x 320 TFT screen. + +// Two rotation pivot points must be set, one for the Sprite and one for the TFT +// using setPivot(). These pivot points do not need to be within the visible screen +// or Sprite boundary. + +// When the Sprite is rotated and pushed to the TFT with pushRotated(angle) it will be +// drawn so that the two pivot points coincide. This makes rotation about a point on the +// screen very simple. The rotation is clockwise with increasing angle. The angle is in +// degrees, an angle of 0 means no Sprite rotation. + +// The pushRotated() function works with 1, 8 and 16 bit per pixel (bpp) Sprites. + +// The original Sprite is unchanged so can be plotted again at a different angle. + +// Optionally a transparent colour can be defined, pixels of this colour will +// not be plotted to the TFT. + +// For 1 bpp Sprites the foreground and background colours are defined with the +// function spr.setBitmapColor(foregroundColor, backgroundColor). + +// Created by Bodmer 6/1/19 as an example to the TFT_eSPI library: +// https://github.com/Bodmer/TFT_eSPI + +#include + +TFT_eSPI tft = TFT_eSPI(); // TFT object + +TFT_eSprite spr = TFT_eSprite(&tft); // Sprite object + +// ======================================================================================= +// Setup +// ======================================================================================= + +void setup() { + Serial.begin(250000); // Debug only + + tft.begin(); // initialize + tft.setRotation(0); +} + +// ======================================================================================= +// Loop +// ======================================================================================= + +void loop() { + + int xw = tft.width()/2; // xw, yh is midle of screen + int yh = tft.height()/2; + + + showMessage("90 degree angles"); + tft.setPivot(xw, yh); // Set pivot to middle of TFT screen + drawX(xw, yh); // Show where screen pivot is + + // Create the Sprite + spr.setColorDepth(8); // Create an 8bpp Sprite of 60x30 pixels + spr.createSprite(64, 30); // 8bpp requires 64 * 30 = 1920 bytes + spr.setPivot(32, 55); // Set pivot relative to top left corner of Sprite + spr.fillSprite(TFT_BLACK); // Fill the Sprite with black + + spr.setTextColor(TFT_GREEN); // Green text + spr.setTextDatum(MC_DATUM); // Middle centre datum + spr.drawString("Hello", 32, 15, 4); // Plot text, font 4, in Sprite at 30, 15 + + spr.pushRotated(0); + spr.pushRotated(90); + spr.pushRotated(180); + spr.pushRotated(270); + + delay(2000); + + + showMessage("45 degree angles"); + drawX(xw, yh); // Show where screen pivot is + + spr.pushRotated(45); + spr.pushRotated(135); + spr.pushRotated(225); + spr.pushRotated(315); + + delay(2000); // Pause so we see it + + + showMessage("Moved Sprite pivot point"); + drawX(xw, yh); // Show where screen pivot is + + spr.setPivot(-20, 15); // Change just the Sprite pivot point + + spr.pushRotated(45); + spr.pushRotated(135); + spr.pushRotated(225); + spr.pushRotated(315); + + delay(2000); // Pause so we see it + + + showMessage("Moved TFT pivot point"); + tft.setPivot(100, 100); // Change just the TFT pivot point + drawX(100, 100); // Show where pivot is + + spr.pushRotated(45); + spr.pushRotated(135); + spr.pushRotated(225); + spr.pushRotated(315); + + delay(2000); // Pause so we see it + + + showMessage("Transparent rotations"); + tft.fillCircle(xw, yh, 70, TFT_DARKGREY); // Draw a filled circle + + tft.setPivot(xw, yh); // Set pivot to middle of screen + drawX(xw, yh); // Show where pivot is + + spr.deleteSprite(); + + spr.setColorDepth(8); // Create a 8bpp Sprite + spr.createSprite(40, 30); // Create a new Sprite 40x30 + spr.setPivot(20, 70); // Set Sprite pivot at 20,80 + + spr.setTextColor(TFT_RED); // Red text in Sprite + spr.setTextDatum(MC_DATUM); // Middle centre datum + + int num = 1; + + for (int16_t angle = 30; angle <= 360; angle += 30) + { + spr.fillSprite(TFT_BLACK); // Clear the Sprite + spr.drawNumber(num, 20, 15, 4); // Plot number, in Sprite at 15,15 and with font 4 + spr.pushRotated(angle, TFT_BLACK); // Plot rotated Sprite, black being transparent + num++; + } + + spr.setTextColor(TFT_WHITE); // White text in Sprite + spr.setPivot(-75, 15); // Set Sprite pivot at -75,15 + + for (int16_t angle = -90; angle < 270; angle += 30) + { + spr.fillSprite(TFT_BLACK); // Clear the Sprite + spr.drawNumber(angle+90, 15, 15, 4); // Plot number, in Sprite at 15,15 and with font 4 + spr.pushRotated(angle, TFT_BLACK); // Plot rotated Sprite, black being transparent + num++; + } + + delay(8000); // Pause so we see it + + spr.deleteSprite(); + +} + +// ======================================================================================= +// Draw an X centered on x,y +// ======================================================================================= + +void drawX(int x, int y) +{ + tft.drawLine(x-5, y-5, x+5, y+5, TFT_WHITE); + tft.drawLine(x-5, y+5, x+5, y-5, TFT_WHITE); +} + +// ======================================================================================= +// Show a message at the top of the screen +// ======================================================================================= + +void showMessage(String msg) +{ + // Clear the screen areas + tft.fillRect(0, 0, tft.width(), 20, TFT_BLACK); + tft.fillRect(0, 20, tft.width(), tft.height()-20, TFT_BLUE); + + uint8_t td = tft.getTextDatum(); // Get current datum + + tft.setTextDatum(TC_DATUM); // Set new datum + + tft.drawString(msg, tft.width()/2, 2, 2); // Message in font 2 + + tft.setTextDatum(td); // Restore old datum +} + +// ======================================================================================= diff --git a/examples/Sprite/Rotated_Sprite_2/Rotated_Sprite_2.ino b/examples/Sprite/Rotated_Sprite_2/Rotated_Sprite_2.ino new file mode 100644 index 0000000..1dc5da7 --- /dev/null +++ b/examples/Sprite/Rotated_Sprite_2/Rotated_Sprite_2.ino @@ -0,0 +1,181 @@ +// This example plots a rotated Sprite into another Sprite and then the resultant composited +// Sprite is pushed to the TFT screen. This example is for a 240 x 320 screen. + +// The motivation for developing this capability is that animated dials can be drawn easily +// and the complex calculations involved are handled by the TFT_eSPI library. To create a dial +// with a moving needle a graphic of a meter needle is plotted at a specified angle into another +// Sprite that contains the dial face. When the needle Sprite is pushed to the dial Sprite the +// plotting ensures two pivot points for each Sprite coincide with pixel level accuracy. + +// Two rotation pivot points must be set, one for the first Sprite and one for the second +// Sprite using setPivot(). These pivot points do not need to be within the Sprite boundaries. + +// In this example a needle graphic is also be plotted direct to a defined TFT pivot point. + +// The rotation angle is in degrees, an angle of 0 means no Sprite rotation. + +// The pushRotated() function works with 1, 8 and 16 bit per pixel (bpp) Sprites. + +// For 1 bpp Sprites the foreground and background colours are defined with the +// member function setBitmapColor(foregroundColor, backgroundColor). + +// Created by Bodmer 6/1/19 as an example to the TFT_eSPI library: +// https://github.com/Bodmer/TFT_eSPI + +#include + +TFT_eSPI tft = TFT_eSPI(); + +TFT_eSprite dial = TFT_eSprite(&tft); // Sprite object for dial +TFT_eSprite needle = TFT_eSprite(&tft); // Sprite object for needle + +uint32_t startMillis; + +int16_t angle = 0; + +// ======================================================================================= +// Setup +// ======================================================================================= + +void setup() { + Serial.begin(250000); // Debug only + + tft.begin(); + tft.setRotation(1); + + // Clear TFT screen + tft.fillScreen(TFT_NAVY); + + // Create the dial Sprite and dial (this temporarily hijacks the use of the needle Sprite) + createDialScale(-120, 120, 30); // create scale (start angle, end angle, increment angle) + drawEmptyDial("Label", 12345); // draw the centre of dial in the Sprite + + dial.pushSprite(110, 0); // push a copy of the dial to the screen so we can see it + + // Create the needle Sprite + createNeedle(); // draw the needle graphic + needle.pushSprite(95, 7); // push a copy of the needle to the screen so we can see it +} + +// ======================================================================================= +// Loop +// ======================================================================================= + +void loop() { + + // Push the needle sprite to the dial Sprite at different angles and then push the dial to the screen + // Use angle increments in range 1 to 6 for smoother or faster movement + for (int16_t angle = -120; angle <= 120; angle += 2) { + plotDial(0, 0, angle, "RPM", angle + 120); + delay(25); + yield(); // Avoid a watchdog time-out + } + + delay(1000); // Pause + + // Update the dial Sprite with decreasing angle and plot to screen at 0,0, no delay + for (int16_t angle = 120; angle >= -120; angle -= 2) { + plotDial(0, 0, angle, "RPM", angle + 120); + yield(); // Avoid a watchdog time-out + } + + // Now show plotting of the rotated needle direct to the TFT + tft.setPivot(45, 150); // Set the TFT pivot point that the needle will rotate around + + // The needle graphic has a black border so if the angle increment is small + // (6 degrees or less in this case) it wipes the last needle position when + // it is rotated and hence it clears the swept area to black + for (int16_t angle = 0; angle <= 360; angle += 5) + { + needle.pushRotated(angle); // Plot direct to TFT at specifed angle + yield(); // Avoid a watchdog time-out + } +} + +// ======================================================================================= +// Create the dial sprite, the dial outer and place scale markers +// ======================================================================================= + +void createDialScale(int16_t start_angle, int16_t end_angle, int16_t increment) +{ + // Create the dial Sprite + dial.setColorDepth(8); // Size is odd (i.e. 91) so there is a centre pixel at 45,45 + dial.createSprite(91, 91); // 8bpp requires 91 * 91 = 8281 bytes + dial.setPivot(45, 45); // set pivot in middle of dial Sprite + + // Draw dial outline + dial.fillSprite(TFT_TRANSPARENT); // Fill with transparent colour + dial.fillCircle(45, 45, 43, TFT_DARKGREY); // Draw dial outer + + // Hijack the use of the needle Sprite since that has not been used yet! + needle.createSprite(3, 3); // 3 pixels wide, 3 high + needle.fillSprite(TFT_WHITE); // Fill with white + needle.setPivot(1, 43); // Set pivot point x to the Sprite centre and y to marker radius + + for (int16_t angle = start_angle; angle <= end_angle; angle += increment) { + needle.pushRotated(&dial, angle); // Sprite is used to make scale markers + yield(); // Avoid a watchdog time-out + } + + needle.deleteSprite(); // Delete the hijacked Sprite +} + + +// ======================================================================================= +// Add the empty dial face with label and value +// ======================================================================================= + +void drawEmptyDial(String label, int16_t val) +{ + // Draw black face + dial.fillCircle(45, 45, 40, TFT_BLACK); + dial.drawPixel(45, 45, TFT_WHITE); // For demo only, mark pivot point with a while pixel + + dial.setTextDatum(TC_DATUM); // Draw dial text + dial.drawString(label, 45, 15, 2); + dial.drawNumber(val, 45, 60, 2); +} + +// ======================================================================================= +// Update the dial and plot to screen with needle at defined angle +// ======================================================================================= + +void plotDial(int16_t x, int16_t y, int16_t angle, String label, uint16_t val) +{ + // Draw the blank dial in the Sprite, add label and number + drawEmptyDial(label, val); + + // Push a rotated needle Sprite to the dial Sprite, with black as transparent colour + needle.pushRotated(&dial, angle, TFT_BLACK); // dial is the destination Sprite + + // Push the resultant dial Sprite to the screen, with transparent colour + dial.pushSprite(x, y, TFT_TRANSPARENT); +} + +// ======================================================================================= +// Create the needle Sprite and the image of the needle +// ======================================================================================= + +void createNeedle(void) +{ + needle.setColorDepth(8); + needle.createSprite(11, 49); // create the needle Sprite 11 pixels wide by 49 high + + needle.fillSprite(TFT_BLACK); // Fill with black + + // Define needle pivot point + uint16_t piv_x = needle.width() / 2; // x pivot of Sprite (middle) + uint16_t piv_y = needle.height() - 10; // y pivot of Sprite (10 pixels from bottom) + needle.setPivot(piv_x, piv_y); // Set pivot point in this Sprite + + // Draw the red needle with a yellow tip + // Keep needle tip 1 pixel inside dial circle to avoid leaving stray pixels + needle.fillRect(piv_x - 1, 2, 3, piv_y + 8, TFT_RED); + needle.fillRect(piv_x - 1, 2, 3, 5, TFT_YELLOW); + + // Draw needle centre boss + needle.fillCircle(piv_x, piv_y, 5, TFT_MAROON); + needle.drawPixel( piv_x, piv_y, TFT_WHITE); // Mark needle pivot point with a white pixel +} + +// ======================================================================================= diff --git a/examples/Sprite/Rotated_Sprite_3/Rotated_Sprite_3.ino b/examples/Sprite/Rotated_Sprite_3/Rotated_Sprite_3.ino new file mode 100644 index 0000000..835b1e2 --- /dev/null +++ b/examples/Sprite/Rotated_Sprite_3/Rotated_Sprite_3.ino @@ -0,0 +1,140 @@ +/*==================================================================================== + + This example draws a jpeg image in a Sprite then plots a rotated copy of the Sprite + to the TFT. + + The jpeg used is in the sketch Data folder (presss Ctrl+K to see folder) + + The jpeg must be uploaded to the ESP8266 or ESP32 SPIFFS by using the Tools menu + sketch data upload option of the Arduino IDE. If you do not have that option it can + be added. Close the Serial Monitor window before uploading to avoid an error message! + + To add the upload option for the ESP8266 see: + http://www.esp8266.com/viewtopic.php?f=32&t=10081 + https://github.com/esp8266/arduino-esp8266fs-plugin/releases + + To add the upload option for the ESP32 see: + https://github.com/me-no-dev/arduino-esp32fs-plugin + + Created by Bodmer 6/1/19 as an example to the TFT_eSPI library: + https://github.com/Bodmer/TFT_eSPI + + Extension funtions in the TFT_eFEX library are used to list SPIFFS files and render + the jpeg to the TFT and to the Sprite: + https://github.com/Bodmer/TFT_eFEX + + To render the Jpeg image the JPEGDecoder library is needed, this can be obtained + with the IDE library manager, or downloaded from here: + https://github.com/Bodmer/JPEGDecoder + + ==================================================================================*/ + +//==================================================================================== +// 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" // Needed for ESP32 only +#endif + +// https://github.com/Bodmer/TFT_eSPI +#include // Hardware-specific library +TFT_eSPI tft = TFT_eSPI(); // Invoke custom library +TFT_eSprite spr = TFT_eSprite(&tft); // Create Sprite object "spr" with pointer to "tft" object + +// https://github.com/Bodmer/TFT_eFEX +#include // Include the function extension library +TFT_eFEX fex = TFT_eFEX(&tft); // Create TFT_eFX object "fex" with pointer to "tft" object + + +//==================================================================================== +// Setup +//==================================================================================== +void setup() +{ + Serial.begin(250000); // Used for messages + + tft.begin(); + tft.setRotation(0); // 0 & 2 Portrait. 1 & 3 landscape + tft.fillScreen(TFT_BLACK); + + // Create a sprite to hold the jpeg (or part of it) + spr.createSprite(80, 64); + + // Initialise SPIFFS + if (!SPIFFS.begin()) { + Serial.println("SPIFFS initialisation failed!"); + while (1) yield(); // Stay here twiddling thumbs waiting + } + Serial.println("\r\nInitialisation done.\r\n"); + + // Lists the files so you can see what is in the SPIFFS + fex.listSPIFFS(); + + // Note the / before the SPIFFS file name must be present, this means the file is in + // the root directory of the SPIFFS, e.g. "/tiger.jpg" for a file called "tiger.jpg" + + // Send jpeg info to serial port + fex.jpegInfo("/Eye_80x64.jpg"); + + // Draw jpeg iamge in Sprite spr at 0,0 + fex.drawJpeg("/Eye_80x64.jpg", 0 , 0, &spr); +} + +//==================================================================================== +// Loop +//==================================================================================== +void loop() +{ + + tft.fillScreen(random(0xFFFF)); + + + // Set the TFT pivot point to the centre of the screen + tft.setPivot(tft.width() / 2, tft.height() / 2); + + // Set Sprite pivot point to centre of Sprite + spr.setPivot(spr.width() / 2, spr.height() / 2); + + // Push Sprite to the TFT at 0,0 (not rotated) + spr.pushSprite(0, 0); + + delay(1000); + + // Push copies of Sprite rotated through increasing angles 0-360 degrees + // with 45 fegree increments + for (int16_t angle = 0; angle <= 360; angle += 45) { + spr.pushRotated(angle); + delay(500); + } + + delay(2000); + + // Move Sprite pivot to a point above the image at 40,-60 + // (Note: Top left corner is Sprite coordinate 0,0) + // The TFT pivot point has already been set to middle of screen. + /* .Pivot point at 40,-60 + ^ + | + -60 + < 40 >| + ______V______ + | | + | Sprite | + |_____________| + */ + spr.setPivot(40, -60); + + // Push Sprite to screen rotated about the new pivot points + // negative angle rotates Sprite anticlockwise + for (int16_t angle = 330; angle >= 0; angle -= 30) { + spr.pushRotated(angle); + yield(); // Stop watchdog triggering + } + + delay(5000); +} +//==================================================================================== diff --git a/examples/Sprite/Rotated_Sprite_3/data/EagleEye.jpg b/examples/Sprite/Rotated_Sprite_3/data/EagleEye.jpg new file mode 100644 index 0000000000000000000000000000000000000000..240b7ef29a6c51eb18625778679b6bdac47be74f GIT binary patch literal 20838 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 literal 0 HcmV?d00001 diff --git a/examples/Sprite/Sprite_scroll_wrap_1bit/Sprite_scroll_wrap_1bit.ino b/examples/Sprite/Sprite_scroll_wrap_1bit/Sprite_scroll_wrap_1bit.ino new file mode 100644 index 0000000..b3bc967 --- /dev/null +++ b/examples/Sprite/Sprite_scroll_wrap_1bit/Sprite_scroll_wrap_1bit.ino @@ -0,0 +1,139 @@ +// This **ONLY** works for 1 and 16 bpp Sprites due to lack of bounds checking in the +// Sprite pushImage() function for 8 bit Sprites (it is on the TO DO list) + +// Wrapping scroll example by Bodmer for the TFT_eSPI library + +//========================================================================================== + +#include + +TFT_eSPI tft = TFT_eSPI(); + +TFT_eSprite gfx = TFT_eSprite(&tft); // Sprite object for graphics write + +TFT_eSprite fb = TFT_eSprite(&tft); // Sprite object for frame buffer + +// Width and height of the Sprite +#define WIDTH 60 +#define HEIGHT 60 + +// Define scroll increment in x and y directions +// positive numbers = right, down +// negative numbers = left, up +#define XDELTA 1 +#define YDELTA 1 + +int16_t scroll_x = 0; // Keep track of the scrolled position, this is where the origin +int16_t scroll_y = 0; // (top left) of the gfx Sprite will be + +int16_t radius = 5; // radius of circle + +bool grow = true; // grow or shrink circle + +uint16_t *gfxPtr; // Pointer to start of graphics sprite memory area + +//========================================================================================== +//========================================================================================== + +void setup() { + Serial.begin(115200); + tft.init(); + tft.fillScreen(TFT_BLACK); + + tft.setRotation(1); + + //tft.invertDisplay(true); + + // Create a 1bpp sprite for the graphics + gfx.setColorDepth(1); + gfxPtr = (uint16_t*) gfx.createSprite(WIDTH, HEIGHT); // 480 bytes needed + gfx.fillSprite(TFT_BLACK); // Note: Sprite is filled with black when created + + // Create a 1bpp sprite for the frame buffer + fb.setColorDepth(1); + fb.createSprite(WIDTH, HEIGHT); // 480 bytes needed + fb.fillSprite(TFT_BLACK); // Note: Sprite is filled with black when created + + // Text colour and alignment in graphics Sprite + gfx.setTextColor(TFT_WHITE, TFT_BLACK); + gfx.setTextDatum(MC_DATUM); + + // Text colour and alignment in frame buffer + fb.setTextColor(TFT_WHITE, TFT_BLACK); + fb.setTextDatum(MC_DATUM); + + // Next 3 lines are for test only to see what we have drawn + drawGraphics(); // draw the graphics in the gfx sprite and copy to buffer + gfx.pushSprite(0, 0); // Plot to screen so we see what it looks like + delay(2000); +} + +//========================================================================================== +//========================================================================================== + +void loop() { +uint32_t tnow = millis(); + drawGraphics(); // Not needed if scrolling graphics are static + + wrappingScroll(XDELTA, YDELTA); + + // Plot two copies without "Hello", then one in the middle with "Hello" + //fb.setBitmapColor(TFT_WHITE, TFT_RED ); + fb.pushSprite(0, 0); // Plot frame buffer onto the TFT screen + //fb.setBitmapColor(TFT_WHITE, TFT_BLUE ); + fb.pushSprite(120, 0); // Plot frame buffer onto the TFT screen + + fb.drawString("Hello", 30, 20, 2); // Plot hello in frame buffer + //fb.setBitmapColor(TFT_BLACK, TFT_GREEN); + fb.pushSprite(60, 0); // Plot frame buffer in middle of other two + + //Serial.println(millis() - tnow); + delay(20); +} + +//========================================================================================== +// Draw graphics in the master Sprite +//========================================================================================== + +void drawGraphics(void) +{ + gfx.fillSprite(TFT_BLACK); // Clear sprite each time and completely redraw + + //gfx.drawRect(0,0,60,60,TFT_WHITE); // Test to check alignment + + gfx.drawCircle( 30 , 30, radius, TFT_WHITE); + if (grow) radius++; + else radius--; + + if ( radius > 25 ) grow = false; + if ( radius < 1 ) grow = true; + + gfx.drawString("World", 30, 40, 2); // Plot hello in frame buffer +} + +//========================================================================================== +// This function scrolls and wraps the graphic in a 1 bit per pixel Sprite, dy and dy +// control the scroll direction. Pixels that scroll off one side appear on the other. +// Scrolling is achieved by plotting one Sprite inside another with an offset. This has +// to be done by plotting 4 times into a second frame buffer Sprite. +//========================================================================================== +void wrappingScroll(int16_t dx, int16_t dy) +{ + // Position the quadrants so they overlap all areas of the buffer + scroll_x += dx; + if (scroll_x < -WIDTH) scroll_x += WIDTH; + if (scroll_x > 0) scroll_x -= WIDTH; + + scroll_y += dy; + if (scroll_y < -HEIGHT) scroll_y += HEIGHT; + if (scroll_y > 0) scroll_y -= HEIGHT; + + // push the 4 quadrants of gfx. sprite into the fb. sprite + // pushImage will do the cropping + fb.pushImage(scroll_x, scroll_y, WIDTH, HEIGHT, gfxPtr); + fb.pushImage(scroll_x + WIDTH, scroll_y, WIDTH, HEIGHT, gfxPtr); + fb.pushImage(scroll_x, scroll_y + HEIGHT, WIDTH, HEIGHT, gfxPtr); + fb.pushImage(scroll_x + WIDTH, scroll_y + HEIGHT, WIDTH, HEIGHT, gfxPtr); +} + +//========================================================================================== diff --git a/keywords.txt b/keywords.txt index 9fb1758..f89d981 100644 --- a/keywords.txt +++ b/keywords.txt @@ -74,11 +74,11 @@ textWidth KEYWORD2 fontHeight KEYWORD2 getTouchRaw KEYWORD2 +convertRawXY KEYWORD2 getTouchRawZ KEYWORD2 getTouch KEYWORD2 calibrateTouch KEYWORD2 setTouch KEYWORD2 -validTouch KEYWORD2 TFT_eSPI_Button KEYWORD1 @@ -97,7 +97,13 @@ TFT_eSprite KEYWORD1 createSprite KEYWORD2 setColorDepth KEYWORD2 +getColorDepth KEYWORD2 deleteSprite KEYWORD2 +pushRotated KEYWORD2 +rotatedBounds KEYWORD2 +setPivot KEYWORD2 +getPivotX KEYWORD2 +getPivotY KEYWORD2 fillSprite KEYWORD2 pushBitmap KEYWORD2 pushSprite KEYWORD2 diff --git a/library.json b/library.json index 5d937f3..705fcb9 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "TFT_eSPI", - "version": "1.3.10", + "version": "1.3.11", "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 8517311..82d9492 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=TFT_eSPI -version=1.3.10 +version=1.3.11 author=Bodmer maintainer=Bodmer sentence=A fast TFT graphics library for ESP8266 and ESP32 processors for the Arduino IDE From 05359a4dffbe3999b1fd6a800479119e34eede03 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Sun, 6 Jan 2019 17:45:06 +0000 Subject: [PATCH 233/287] Misc updates Speed up Sprite rotation on an ESP32 Update utf8 serial decoder to handle illegal format strings Remove need for D0_USED_FOR_xx (issue #271) Clean up setup files Correct various comment typos --- Extensions/Smooth_font.cpp | 11 +- Extensions/Sprite.cpp | 3 +- TFT_eSPI.h | 4 +- User_Setup.h | 44 ++-- User_Setups/Setup10_RPi_touch_ILI9486.h | 189 +------------- User_Setups/Setup11_RPi_touch_ILI9486.h | 191 +------------- User_Setups/Setup12_M5Stack.h | 195 +-------------- User_Setups/Setup13_ILI9481_Parallel.h | 66 +---- User_Setups/Setup14_ILI9341_Parallel.h | 64 +---- User_Setups/Setup15_HX8357D.h | 194 +-------------- User_Setups/Setup16_ILI9488_Parallel.h | 64 +---- User_Setups/Setup17_ePaper.h | 56 +---- User_Setups/Setup18_ST7789.h | 233 +----------------- User_Setups/Setup1_ILI9341.h | 197 +-------------- User_Setups/Setup20_ILI9488.h | 195 +-------------- User_Setups/Setup21_ILI9488.h | 198 +-------------- User_Setups/Setup22_TTGO_T4.h | 7 +- User_Setups/Setup23_TTGO_TM.h | 6 +- User_Setups/Setup2_ST7735.h | 187 +------------- User_Setups/Setup3_ILI9163.h | 188 +------------- User_Setups/Setup43_ST7735.h | 199 +-------------- User_Setups/Setup4_S6D02A1.h | 192 +-------------- User_Setups/Setup5_RPi_ILI9486.h | 193 +-------------- User_Setups/Setup6_RPi_Wr_ILI9486.h | 189 +------------- User_Setups/Setup7_ST7735_128x128.h | 185 +------------- User_Setups/Setup8_ILI9163_128x128.h | 185 +------------- User_Setups/Setup9_ST7735_Overlap.h | 181 +------------- User_Setups/SetupX_Template.h | 44 ++-- User_Setups/User_Custom_Fonts.h | 5 +- .../Rotated_Sprite_3/Rotated_Sprite_3.ino | 4 +- .../Sprite_scroll_wrap_1bit.ino | 12 +- library.json | 2 +- library.properties | 2 +- 33 files changed, 123 insertions(+), 3562 deletions(-) diff --git a/Extensions/Smooth_font.cpp b/Extensions/Smooth_font.cpp index fe77885..3dc2cb0 100644 --- a/Extensions/Smooth_font.cpp +++ b/Extensions/Smooth_font.cpp @@ -282,11 +282,15 @@ uint16_t TFT_eSPI::decodeUTF8(uint8_t c) { #ifdef DECODE_UTF8 + + // 7 bit Unicode + if ((c & 0x80) == 0x00) { + decoderState = 0; + return (uint16_t)c; + } + if (decoderState == 0) { - // 7 bit Unicode - if ((c & 0x80) == 0x00) return (uint16_t)c; - // 11 bit Unicode if ((c & 0xE0) == 0xC0) { @@ -322,6 +326,7 @@ uint16_t TFT_eSPI::decodeUTF8(uint8_t c) } #endif + decoderState = 0; return (uint16_t)c; // fall-back to extended ASCII } diff --git a/Extensions/Sprite.cpp b/Extensions/Sprite.cpp index e74864b..0102320 100644 --- a/Extensions/Sprite.cpp +++ b/Extensions/Sprite.cpp @@ -301,7 +301,7 @@ bool TFT_eSprite::pushRotated(int16_t angle, int32_t transp) if (max_x > _tft->width()) max_x = _tft->width(); if (max_y > _tft->height()) max_y = _tft->height(); - + _tft->startWrite(); // Scan destination bounding box and fetch transformed pixels from source Sprite for (int32_t x = min_x; x <= max_x; x++) { int32_t xt = x - _tft->_xpivot; @@ -325,6 +325,7 @@ bool TFT_eSprite::pushRotated(int16_t angle, int32_t transp) else if (column_drawn) y = max_y; // Skip remaining column pixels } } + _tft->endWrite(); return true; } diff --git a/TFT_eSPI.h b/TFT_eSPI.h index 48b9f48..1422fb4 100644 --- a/TFT_eSPI.h +++ b/TFT_eSPI.h @@ -122,7 +122,7 @@ #define DC_C // No macro allocated so it generates no code #define DC_D // No macro allocated so it generates no code #else - #if defined (ESP8266) && defined (D0_USED_FOR_DC) + #if defined (ESP8266) && (TFT_DC == 16) #define DC_C digitalWrite(TFT_DC, LOW) #define DC_D digitalWrite(TFT_DC, HIGH) #elif defined (ESP32) @@ -172,7 +172,7 @@ #define CS_L // No macro allocated so it generates no code #define CS_H // No macro allocated so it generates no code #else - #if defined (ESP8266) && defined (D0_USED_FOR_CS) + #if defined (ESP8266) && (TFT_CS == 16) #define CS_L digitalWrite(TFT_CS, LOW) #define CS_H digitalWrite(TFT_CS, HIGH) #elif defined (ESP32) diff --git a/User_Setup.h b/User_Setup.h index b27e1a7..e43b749 100644 --- a/User_Setup.h +++ b/User_Setup.h @@ -8,9 +8,10 @@ // 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 +// Section 1. Call up the right driver file and any options for it // // ################################################################################## @@ -32,13 +33,14 @@ // To use the SDA line for reading data from the TFT uncomment the following line: // #define TFT_SDA_READ -// For M5Stack ESP32 module with integrated display ONLY, remove // in line below -//#define M5STACK - // For ST7789 ONLY, define the colour order IF the blue and red are swapped on your display +// Try ONE option at a time to find the correct colour order for your display //#define TFT_RGB_ORDER TFT_RGB // Colour order Red-Green-Blue //#define TFT_RGB_ORDER TFT_BGR // Colour order Blue-Green-Red +// For M5Stack ESP32 module with integrated display ONLY, remove // in line below +//#define M5STACK + // For ST7789, ST7735 and ILI9163 ONLY, define the pixel width and height in portrait orientation // #define TFT_WIDTH 80 // #define TFT_WIDTH 128 @@ -70,9 +72,10 @@ //#define TFT_INVERSION_ON //#define TFT_INVERSION_OFF + // ################################################################################## // -// Section 1. Define the pins that are used to interface with the display here +// Section 2. Define the pins that are used to interface with the display here // // ################################################################################## @@ -150,6 +153,7 @@ //#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 TFT_BL 32 // LED back-light (only for ST7789 with backlight control pin) //#define TOUCH_CS 21 // Chip select pin (T_CS) of touch screen @@ -179,11 +183,11 @@ // 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_DC 15 // Data Command control pin - must 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_WR 4 // Write strobe control pin - must use a pin in the range 0-31 +//#define TFT_RD 2 // Read strobe control pin //#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. @@ -194,20 +198,6 @@ //#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 // ################################################################################## // @@ -233,16 +223,10 @@ // this will save ~20kbytes of FLASH #define SMOOTH_FONT -// ################################################################################## -// -// Section 4. Not used -// -// ################################################################################## - // ################################################################################## // -// Section 5. Other options +// Section 4. Other options // // ################################################################################## @@ -278,4 +262,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/User_Setups/Setup10_RPi_touch_ILI9486.h b/User_Setups/Setup10_RPi_touch_ILI9486.h index bbfe895..8e59a6b 100644 --- a/User_Setups/Setup10_RPi_touch_ILI9486.h +++ b/User_Setups/Setup10_RPi_touch_ILI9486.h @@ -1,86 +1,7 @@ -// 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! +// See SetupX_Template.h for all options available -// ################################################################################## -// -// 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 -// 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 @@ -90,75 +11,6 @@ #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 - -//#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 (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 @@ -166,53 +18,14 @@ #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 diff --git a/User_Setups/Setup11_RPi_touch_ILI9486.h b/User_Setups/Setup11_RPi_touch_ILI9486.h index cbb489b..e2edbb3 100644 --- a/User_Setups/Setup11_RPi_touch_ILI9486.h +++ b/User_Setups/Setup11_RPi_touch_ILI9486.h @@ -1,117 +1,7 @@ -// 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! +// See SetupX_Template.h for all options available -// ################################################################################## -// -// 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 -// 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 @@ -123,43 +13,6 @@ #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 (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 @@ -167,53 +20,11 @@ #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 diff --git a/User_Setups/Setup12_M5Stack.h b/User_Setups/Setup12_M5Stack.h index 3439512..d4b50b8 100644 --- a/User_Setups/Setup12_M5Stack.h +++ b/User_Setups/Setup12_M5Stack.h @@ -1,131 +1,11 @@ -// 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! +// See SetupX_Template.h for all options available -// ################################################################################## -// -// 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 -// 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 - -//#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 @@ -134,31 +14,6 @@ #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 (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 @@ -166,53 +21,9 @@ #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 +#define SPI_FREQUENCY 27000000 diff --git a/User_Setups/Setup13_ILI9481_Parallel.h b/User_Setups/Setup13_ILI9481_Parallel.h index 0de1ebd..1758420 100644 --- a/User_Setups/Setup13_ILI9481_Parallel.h +++ b/User_Setups/Setup13_ILI9481_Parallel.h @@ -1,37 +1,17 @@ -// 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! +// See SetupX_Template.h for all options available -// ################################################################################## -// -// 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 +// ESP32 pins used for UNO format board #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_DC 15 // Data Command control pin - must 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_WR 4 // Write strobe control pin - must 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 @@ -44,48 +24,12 @@ #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/Setup14_ILI9341_Parallel.h b/User_Setups/Setup14_ILI9341_Parallel.h index d777264..d19a827 100644 --- a/User_Setups/Setup14_ILI9341_Parallel.h +++ b/User_Setups/Setup14_ILI9341_Parallel.h @@ -1,37 +1,17 @@ -// 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! +// See SetupX_Template.h for all options available -// ################################################################################## -// -// 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_DC 15 // Data Command control pin - must 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_WR 4 // Write strobe control pin - must 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 @@ -44,48 +24,12 @@ #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 index 99ae1d6..f4ee614 100644 --- a/User_Setups/Setup15_HX8357D.h +++ b/User_Setups/Setup15_HX8357D.h @@ -1,87 +1,7 @@ -// 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! +// See SetupX_Template.h for all options available -// ################################################################################## -// -// 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 @@ -89,77 +9,6 @@ #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 @@ -167,53 +16,16 @@ #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 +#define SPI_FREQUENCY 27000000 +// #define SPI_FREQUENCY 40000000 -// 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 index bcaca48..5d3cb58 100644 --- a/User_Setups/Setup16_ILI9488_Parallel.h +++ b/User_Setups/Setup16_ILI9488_Parallel.h @@ -1,37 +1,17 @@ -// 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! +// See SetupX_Template.h for all options available -// ################################################################################## -// -// 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_DC 15 // Data Command control pin - must 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_WR 4 // Write strobe control pin - must 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 @@ -44,48 +24,12 @@ #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/Setup17_ePaper.h b/User_Setups/Setup17_ePaper.h index 3704cc5..86d0e65 100644 --- a/User_Setups/Setup17_ePaper.h +++ b/User_Setups/Setup17_ePaper.h @@ -1,26 +1,7 @@ -// 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 -// -// ################################################################################## +// See SetupX_Template.h for all options available #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 @@ -48,47 +29,12 @@ /////////////////////////////////////////////////////////////////// -// ################################################################################## -// -// 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/User_Setups/Setup18_ST7789.h b/User_Setups/Setup18_ST7789.h index 6a6f5ac..25d57a3 100644 --- a/User_Setups/Setup18_ST7789.h +++ b/User_Setups/Setup18_ST7789.h @@ -1,204 +1,17 @@ -// 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 +// See SetupX_Template.h for all options available -// ################################################################################## -// -// 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 -// Some displays support SPI reads via the MISO pin, if the display has a single -// bi-directional SDA pin the library will try to use bit banging to read the line -// To use the SDA line for reading data from the TFT uncomment the following line: + #define TFT_SDA_READ -// 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 ###### +// My ST7789 display has TCT_CS wired permananently low so the pin is not defined here // 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 @@ -206,53 +19,17 @@ #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 27000000 +// #define SPI_FREQUENCY 40000000 // #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 1f02b38..82d69c5 100644 --- a/User_Setups/Setup1_ILI9341.h +++ b/User_Setups/Setup1_ILI9341.h @@ -1,164 +1,13 @@ -// 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! +// See SetupX_Template.h for all options available -// ################################################################################## -// -// 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 - -// 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 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 - -//#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 (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 @@ -166,55 +15,19 @@ #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 27000000 +#define SPI_FREQUENCY 40000000 // #define SPI_FREQUENCY 80000000 #define SPI_READ_FREQUENCY 20000000 -// 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/Setup20_ILI9488.h b/User_Setups/Setup20_ILI9488.h index ada2eee..7d1eaf1 100644 --- a/User_Setups/Setup20_ILI9488.h +++ b/User_Setups/Setup20_ILI9488.h @@ -1,87 +1,7 @@ -// 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! +// See SetupX_Template.h for all options available -// ################################################################################## -// -// 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 ILI9488_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 (leave TFT SDO disconnected if other SPI devices share MISO) -// 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 @@ -89,77 +9,6 @@ #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 // (leave TFT SDO disconnected if other SPI devices share MISO) -//#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 (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 @@ -167,53 +16,17 @@ #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 +#define SPI_FREQUENCY 27000000 +// #define SPI_FREQUENCY 40000000 + -// 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/Setup21_ILI9488.h b/User_Setups/Setup21_ILI9488.h index b00d02a..1a39a02 100644 --- a/User_Setups/Setup21_ILI9488.h +++ b/User_Setups/Setup21_ILI9488.h @@ -1,121 +1,7 @@ -// 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! +// See SetupX_Template.h for all options available -// ################################################################################## -// -// 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 ILI9486_DRIVER #define ILI9488_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 (leave TFT SDO disconnected if other SPI devices share MISO) -// 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 // (leave TFT SDO disconnected if other SPI devices share MISO) #define TFT_MOSI 23 @@ -123,46 +9,7 @@ #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 (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 @@ -170,53 +17,14 @@ #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 27000000 +// #define SPI_FREQUENCY 40000000 // #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/Setup22_TTGO_T4.h b/User_Setups/Setup22_TTGO_T4.h index fa04323..cfed5b5 100644 --- a/User_Setups/Setup22_TTGO_T4.h +++ b/User_Setups/Setup22_TTGO_T4.h @@ -1,5 +1,7 @@ // Setup for the TTGO T4 ("Bitcoin Tracker") ESP32 board with 2.2" ILI9341 display +// See SetupX_Template.h for all options available + #define ILI9341_DRIVER #define TFT_MISO 12 @@ -20,9 +22,8 @@ #define SMOOTH_FONT -//#define SPI_FREQUENCY 27000000 // Actually sets it to 26.67MHz = 80/3 +//#define SPI_FREQUENCY 27000000 #define SPI_FREQUENCY 40000000 // Maximum for ILI9341 -#define SPI_TOUCH_FREQUENCY 2500000 -#define SPI_READ_FREQUENCY 10000000 +#define SPI_READ_FREQUENCY 6000000 // 6 MHz is the maximum SPI read speed for the ST7789V diff --git a/User_Setups/Setup23_TTGO_TM.h b/User_Setups/Setup23_TTGO_TM.h index 74c9a05..58468d5 100644 --- a/User_Setups/Setup23_TTGO_TM.h +++ b/User_Setups/Setup23_TTGO_TM.h @@ -1,5 +1,7 @@ // Setup for the TTGO TM (Music) ESP32 board with 2.4" ST7789V display +// See SetupX_Template.h for all options available + #define ST7789_DRIVER #define TFT_SDA_READ // Read from display, it only provides an SDA pin @@ -28,7 +30,7 @@ #define SMOOTH_FONT -#define SPI_FREQUENCY 40000000 // The display also seems to work reliably at 80MHz +#define SPI_FREQUENCY 40000000 // This display also seems to work reliably at 80MHz +#define SPI_FREQUENCY 80000000 #define SPI_READ_FREQUENCY 6000000 // 6 MHz is the maximum SPI read speed for the ST7789V - diff --git a/User_Setups/Setup2_ST7735.h b/User_Setups/Setup2_ST7735.h index 7f79ca8..b9e618f 100644 --- a/User_Setups/Setup2_ST7735.h +++ b/User_Setups/Setup2_ST7735.h @@ -1,164 +1,21 @@ -// 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! +// See SetupX_Template.h for all options available -// ################################################################################## -// -// 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 -// 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 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 - -//#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 (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 @@ -169,50 +26,14 @@ //#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 +#define SPI_FREQUENCY 27000000 +// #define SPI_FREQUENCY 40000000 -// 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/Setup3_ILI9163.h b/User_Setups/Setup3_ILI9163.h index e8bd1b2..f9b2656 100644 --- a/User_Setups/Setup3_ILI9163.h +++ b/User_Setups/Setup3_ILI9163.h @@ -1,86 +1,11 @@ -// 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! +// See SetupX_Template.h for all options available -// ################################################################################## -// -// 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 -// 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 @@ -88,77 +13,6 @@ #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 - -//#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 (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 @@ -169,50 +23,14 @@ //#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 +#define SPI_FREQUENCY 27000000 +// #define SPI_FREQUENCY 40000000 -// 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/Setup43_ST7735.h b/User_Setups/Setup43_ST7735.h index efe20f3..8865f54 100644 --- a/User_Setups/Setup43_ST7735.h +++ b/User_Setups/Setup43_ST7735.h @@ -1,127 +1,16 @@ -// 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! +// Setup for ESP32 and ST7735 80 x 160 TFT -// ################################################################################## -// -// Section 0. Call up the right driver file and any options for it -// -// ################################################################################## +// See SetupX_Template.h for all options available -// 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 80 -//#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 #define ST7735_GREENTAB160x80 -//#define ST7735_REDTAB160x80 -// If colours are inverted (white shows as black) then uncomment one of these -// Try both options, one of the options should correct the inversion. -//#define TFT_INVERSION_ON -//#define TFT_INVERSION_OFF - -// ################################################################################## -// -// 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 @@ -131,44 +20,6 @@ #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 (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 @@ -179,50 +30,8 @@ //#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 +//#define SPI_FREQUENCY 20000000 + #define SPI_FREQUENCY 27000000 // Actually sets it to 26.67MHz = 80/3 diff --git a/User_Setups/Setup4_S6D02A1.h b/User_Setups/Setup4_S6D02A1.h index 0a1530b..233117c 100644 --- a/User_Setups/Setup4_S6D02A1.h +++ b/User_Setups/Setup4_S6D02A1.h @@ -1,86 +1,7 @@ -// 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! +// See SetupX_Template.h for all options available -// ################################################################################## -// -// 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 -// 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 @@ -88,77 +9,6 @@ #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 - -//#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 (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 @@ -169,50 +19,14 @@ //#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 +#define SPI_FREQUENCY 27000000 +// #define SPI_FREQUENCY 40000000 -// 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/Setup5_RPi_ILI9486.h b/User_Setups/Setup5_RPi_ILI9486.h index c21e181..ba21c31 100644 --- a/User_Setups/Setup5_RPi_ILI9486.h +++ b/User_Setups/Setup5_RPi_ILI9486.h @@ -1,164 +1,14 @@ -// 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! +// See SetupX_Template.h for all options available -// ################################################################################## -// -// 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 -// 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 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 - -//#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 (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 @@ -166,53 +16,14 @@ #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 diff --git a/User_Setups/Setup6_RPi_Wr_ILI9486.h b/User_Setups/Setup6_RPi_Wr_ILI9486.h index e5c9303..abeaddb 100644 --- a/User_Setups/Setup6_RPi_Wr_ILI9486.h +++ b/User_Setups/Setup6_RPi_Wr_ILI9486.h @@ -1,86 +1,7 @@ -// 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! +// See SetupX_Template.h for all options available -// ################################################################################## -// -// 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 -// 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 @@ -88,131 +9,23 @@ #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 - -//#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 (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 diff --git a/User_Setups/Setup7_ST7735_128x128.h b/User_Setups/Setup7_ST7735_128x128.h index 3bb32cf..6821f47 100644 --- a/User_Setups/Setup7_ST7735_128x128.h +++ b/User_Setups/Setup7_ST7735_128x128.h @@ -1,86 +1,14 @@ -// 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! +// See SetupX_Template.h for all options available -// ################################################################################## -// -// 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 -// 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 @@ -88,77 +16,6 @@ #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 - -//#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 (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 @@ -169,50 +26,14 @@ //#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 +#define SPI_FREQUENCY 27000000 -// 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/Setup8_ILI9163_128x128.h b/User_Setups/Setup8_ILI9163_128x128.h index 05b7cb6..da42ec3 100644 --- a/User_Setups/Setup8_ILI9163_128x128.h +++ b/User_Setups/Setup8_ILI9163_128x128.h @@ -1,86 +1,11 @@ -// 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! +// See SetupX_Template.h for all options available -// ################################################################################## -// -// 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 -// 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 @@ -88,77 +13,6 @@ #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 - -//#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 (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 @@ -169,50 +23,13 @@ //#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 diff --git a/User_Setups/Setup9_ST7735_Overlap.h b/User_Setups/Setup9_ST7735_Overlap.h index 57f9019..2237893 100644 --- a/User_Setups/Setup9_ST7735_Overlap.h +++ b/User_Setups/Setup9_ST7735_Overlap.h @@ -1,103 +1,14 @@ -// 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! +// See SetupX_Template.h for all options available -// ################################################################################## -// -// 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 -// 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 @@ -108,57 +19,6 @@ // 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 (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 @@ -169,50 +29,13 @@ //#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 +#define SPI_FREQUENCY 27000000 -// 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/SetupX_Template.h b/User_Setups/SetupX_Template.h index 7d6ef8b..59eef66 100644 --- a/User_Setups/SetupX_Template.h +++ b/User_Setups/SetupX_Template.h @@ -8,9 +8,10 @@ // 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 +// Section 1. Call up the right driver file and any options for it // // ################################################################################## @@ -71,9 +72,10 @@ //#define TFT_INVERSION_ON //#define TFT_INVERSION_OFF + // ################################################################################## // -// Section 1. Define the pins that are used to interface with the display here +// Section 2. Define the pins that are used to interface with the display here // // ################################################################################## @@ -152,9 +154,11 @@ //#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_BL 32 // LED back-light (only for ST7789 with backlight control pin) -//#define TFT_WR 21 // Write strobe for modified Raspberry Pi TFT only +//#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 @@ -163,7 +167,7 @@ //#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 (if needed) +//#define TFT_BL 32 // LED back-light (required for M5Stack) // ###### EDIT THE PINs BELOW TO SUIT YOUR ESP32 PARALLEL TFT SETUP ###### @@ -178,12 +182,12 @@ //#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_CS 33 // Chip select control pin (library pulls permanently low) +//#define TFT_DC 15 // Data Command control pin - must 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_WR 4 // Write strobe control pin - must use a pin in the range 0-31 +//#define TFT_RD 2 // Read strobe control pin //#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. @@ -194,20 +198,6 @@ //#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 // ################################################################################## // @@ -225,20 +215,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_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/User_Custom_Fonts.h b/User_Setups/User_Custom_Fonts.h index ef9e0ae..f2dc3d3 100644 --- a/User_Setups/User_Custom_Fonts.h +++ b/User_Setups/User_Custom_Fonts.h @@ -13,9 +13,10 @@ ^^^^ */ -// When font files are placed in the Custom folder then they must also be #included here: +// When font files are placed in the Custom folder (TFT_eSPI\Fonts\Custom) then they must +// also be #included here: -// The comment added is a shorthand reference but this is not essential +// The CF_OL24 etc are a shorthand reference, but this is not essential to use the fonts #ifdef LOAD_GFXFF diff --git a/examples/Sprite/Rotated_Sprite_3/Rotated_Sprite_3.ino b/examples/Sprite/Rotated_Sprite_3/Rotated_Sprite_3.ino index 835b1e2..8e8e72b 100644 --- a/examples/Sprite/Rotated_Sprite_3/Rotated_Sprite_3.ino +++ b/examples/Sprite/Rotated_Sprite_3/Rotated_Sprite_3.ino @@ -1,9 +1,9 @@ /*==================================================================================== - This example draws a jpeg image in a Sprite then plots a rotated copy of the Sprite + This example draws a jpeg image in a Sprite then plot a rotated copy of the Sprite to the TFT. - The jpeg used is in the sketch Data folder (presss Ctrl+K to see folder) + The jpeg used in in the sketch Data folder (presss Ctrl+K to see folder) The jpeg must be uploaded to the ESP8266 or ESP32 SPIFFS by using the Tools menu sketch data upload option of the Arduino IDE. If you do not have that option it can diff --git a/examples/Sprite/Sprite_scroll_wrap_1bit/Sprite_scroll_wrap_1bit.ino b/examples/Sprite/Sprite_scroll_wrap_1bit/Sprite_scroll_wrap_1bit.ino index b3bc967..76fcfc6 100644 --- a/examples/Sprite/Sprite_scroll_wrap_1bit/Sprite_scroll_wrap_1bit.ino +++ b/examples/Sprite/Sprite_scroll_wrap_1bit/Sprite_scroll_wrap_1bit.ino @@ -1,5 +1,5 @@ -// This **ONLY** works for 1 and 16 bpp Sprites due to lack of bounds checking in the -// Sprite pushImage() function for 8 bit Sprites (it is on the TO DO list) +// This **ONLY** works for 1 bpp Sprites due to lack of bounds checking in the +// Sprite pushImage() function for 8 and 16 bit Sprites (it is on the TO DO list) // Wrapping scroll example by Bodmer for the TFT_eSPI library @@ -44,14 +44,14 @@ void setup() { //tft.invertDisplay(true); - // Create a 1bpp sprite for the graphics + // Create a sprite for the graphics gfx.setColorDepth(1); - gfxPtr = (uint16_t*) gfx.createSprite(WIDTH, HEIGHT); // 480 bytes needed + gfxPtr = (uint16_t*) gfx.createSprite(WIDTH, HEIGHT); // 450 bytes needed gfx.fillSprite(TFT_BLACK); // Note: Sprite is filled with black when created - // Create a 1bpp sprite for the frame buffer + // Create a sprite for the frame buffer fb.setColorDepth(1); - fb.createSprite(WIDTH, HEIGHT); // 480 bytes needed + fb.createSprite(WIDTH, HEIGHT); // 450 bytes needed fb.fillSprite(TFT_BLACK); // Note: Sprite is filled with black when created // Text colour and alignment in graphics Sprite diff --git a/library.json b/library.json index 705fcb9..fec1a93 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "TFT_eSPI", - "version": "1.3.11", + "version": "1.3.12", "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 82d9492..000edb4 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=TFT_eSPI -version=1.3.11 +version=1.3.12 author=Bodmer maintainer=Bodmer sentence=A fast TFT graphics library for ESP8266 and ESP32 processors for the Arduino IDE From 4e6736e5965d06865d3cab6631f9ed78f4690965 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Sun, 6 Jan 2019 19:03:31 +0000 Subject: [PATCH 234/287] Add backlight support The backlight control pin and ON state can be defined in the setup.h file. Addresses #229, #226, #221 Comments updated and ILI9488 warning added. --- TFT_Drivers/ILI9341_Init.h | 6 ----- TFT_eSPI.cpp | 11 ++++++++ User_Setup.h | 39 ++++++++++++++++++---------- User_Setups/SetupX_Template.h | 49 +++++++++++++++++++++-------------- library.json | 2 +- library.properties | 2 +- 6 files changed, 68 insertions(+), 41 deletions(-) diff --git a/TFT_Drivers/ILI9341_Init.h b/TFT_Drivers/ILI9341_Init.h index 124c849..404c3b9 100644 --- a/TFT_Drivers/ILI9341_Init.h +++ b/TFT_Drivers/ILI9341_Init.h @@ -121,10 +121,4 @@ 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_eSPI.cpp b/TFT_eSPI.cpp index a20cee4..fae609a 100644 --- a/TFT_eSPI.cpp +++ b/TFT_eSPI.cpp @@ -327,6 +327,17 @@ void TFT_eSPI::init(uint8_t tc) writecommand(TFT_SWRST); // Software reset #endif +#if defined (TFT_BL) && defined (TFT_BACKLIGHT_ON) + digitalWrite(TFT_BL, TFT_BACKLIGHT_ON); + pinMode(TFT_BL, OUTPUT); +#else + #if defined (TFT_BL) && defined (M5STACK) + // Turn on the back-light LED + digitalWrite(TFT_BL, HIGH); + pinMode(TFT_BL, OUTPUT); + #endif +#endif + spi_end(); delay(150); // Wait for reset to complete diff --git a/User_Setup.h b/User_Setup.h index e43b749..6a5531e 100644 --- a/User_Setup.h +++ b/User_Setup.h @@ -17,29 +17,32 @@ // Only define one driver, the other ones must be commented out #define ILI9341_DRIVER -//#define ST7735_DRIVER -//#define ILI9163_DRIVER +//#define ST7735_DRIVER // Define additional parameters below for this display +//#define ILI9163_DRIVER // Define additional parameters below for this display //#define S6D02A1_DRIVER //#define RPI_ILI9486_DRIVER // 20MHz maximum SPI //#define HX8357D_DRIVER //#define ILI9481_DRIVER //#define ILI9486_DRIVER -//#define ILI9488_DRIVER -//#define ST7789_DRIVER // Define the screen size below for this display +//#define ILI9488_DRIVER // WARNING: Do not connect ILI9488 display SDO to MISO if other devices share the SPI bus (TFT SDO does NOT tristate when CS is high) +//#define ST7789_DRIVER // Define additional parameters below for this display //#define R61581_DRIVER -// Some displays support SPI reads via the MISO pin, if the display has a single -// bi-directional SDA pin the library will try to use bit banging to read the line +// Some displays support SPI reads via the MISO pin, other displays have a single +// bi-directional SDA pin and the library will try to read this via the MOSI line. // To use the SDA line for reading data from the TFT uncomment the following line: -// #define TFT_SDA_READ + +// #define TFT_SDA_READ // This option if for ESP32 ONLY, tested with ST7789 display only // For ST7789 ONLY, define the colour order IF the blue and red are swapped on your display // Try ONE option at a time to find the correct colour order for your display -//#define TFT_RGB_ORDER TFT_RGB // Colour order Red-Green-Blue -//#define TFT_RGB_ORDER TFT_BGR // Colour order Blue-Green-Red -// For M5Stack ESP32 module with integrated display ONLY, remove // in line below -//#define M5STACK +// #define TFT_RGB_ORDER TFT_RGB // Colour order Red-Green-Blue +// #define TFT_RGB_ORDER TFT_BGR // Colour order Blue-Green-Red + +// For M5Stack ESP32 module with integrated ILI9341 display ONLY, remove // in line below + +// #define M5STACK // For ST7789, ST7735 and ILI9163 ONLY, define the pixel width and height in portrait orientation // #define TFT_WIDTH 80 @@ -65,13 +68,21 @@ // #define ST7735_GREENTAB160x80 // For 160 x 80 display (BGR, inverted, 26 offset) // #define ST7735_REDTAB // #define ST7735_BLACKTAB -// #define ST7735_REDTAB160x80 // For 160 x 80 display (24 offset) (https://www.aliexpress.com/item/ShengYang-1pcs-IPS-0-96-inch-7P-SPI-HD-65K-Full-Color-OLED-Module-ST7735-Drive/32918394604.html) +// #define ST7735_REDTAB160x80 // For 160 x 80 display with 24 pixel offset // If colours are inverted (white shows as black) then uncomment one of the next // 2 lines try both options, one of the options should correct the inversion. -//#define TFT_INVERSION_ON -//#define TFT_INVERSION_OFF +// #define TFT_INVERSION_ON +// #define TFT_INVERSION_OFF + +// If a backlight control signal is available then define the TFT_BL pin in Section 2 +// below. The backlight will be turned ON when tft.begin() is called, but the library +// needs to know if the LEDs are ON with the pin HIGH or LOW. If the LEDs are to be +// driven with a PWM signal or turned OFF/ON then this must be handled by the user +// sketch. e.g. with digitalWrite(TFT_BL, LOW); + +// #define TFT_BACKLIGHT_ON HIGH // HIGH or LOW are options // ################################################################################## // diff --git a/User_Setups/SetupX_Template.h b/User_Setups/SetupX_Template.h index 59eef66..6a5531e 100644 --- a/User_Setups/SetupX_Template.h +++ b/User_Setups/SetupX_Template.h @@ -17,29 +17,32 @@ // Only define one driver, the other ones must be commented out #define ILI9341_DRIVER -//#define ST7735_DRIVER -//#define ILI9163_DRIVER +//#define ST7735_DRIVER // Define additional parameters below for this display +//#define ILI9163_DRIVER // Define additional parameters below for this display //#define S6D02A1_DRIVER //#define RPI_ILI9486_DRIVER // 20MHz maximum SPI //#define HX8357D_DRIVER //#define ILI9481_DRIVER //#define ILI9486_DRIVER -//#define ILI9488_DRIVER -//#define ST7789_DRIVER // Define the screen size below for this display +//#define ILI9488_DRIVER // WARNING: Do not connect ILI9488 display SDO to MISO if other devices share the SPI bus (TFT SDO does NOT tristate when CS is high) +//#define ST7789_DRIVER // Define additional parameters below for this display //#define R61581_DRIVER -// Some displays support SPI reads via the MISO pin, if the display has a single -// bi-directional SDA pin the library will try to use bit banging to read the line +// Some displays support SPI reads via the MISO pin, other displays have a single +// bi-directional SDA pin and the library will try to read this via the MOSI line. // To use the SDA line for reading data from the TFT uncomment the following line: -// #define TFT_SDA_READ -// For ST7789 ONLY, define the colour order if the blue and red are swapped on your display +// #define TFT_SDA_READ // This option if for ESP32 ONLY, tested with ST7789 display only + +// For ST7789 ONLY, define the colour order IF the blue and red are swapped on your display // Try ONE option at a time to find the correct colour order for your display -//#define TFT_RGB_ORDER TFT_RGB // Colour order Red-Green-Blue -//#define TFT_RGB_ORDER TFT_BGR // Colour order Blue-Green-Red -// For M5Stack ESP32 module with integrated display ONLY, remove // in line below -//#define M5STACK +// #define TFT_RGB_ORDER TFT_RGB // Colour order Red-Green-Blue +// #define TFT_RGB_ORDER TFT_BGR // Colour order Blue-Green-Red + +// For M5Stack ESP32 module with integrated ILI9341 display ONLY, remove // in line below + +// #define M5STACK // For ST7789, ST7735 and ILI9163 ONLY, define the pixel width and height in portrait orientation // #define TFT_WIDTH 80 @@ -65,13 +68,21 @@ // #define ST7735_GREENTAB160x80 // For 160 x 80 display (BGR, inverted, 26 offset) // #define ST7735_REDTAB // #define ST7735_BLACKTAB -// #define ST7735_REDTAB160x80 // For 160 x 80 display (24 offset) (https://www.aliexpress.com/item/ShengYang-1pcs-IPS-0-96-inch-7P-SPI-HD-65K-Full-Color-OLED-Module-ST7735-Drive/32918394604.html) +// #define ST7735_REDTAB160x80 // For 160 x 80 display with 24 pixel offset // If colours are inverted (white shows as black) then uncomment one of the next // 2 lines try both options, one of the options should correct the inversion. -//#define TFT_INVERSION_ON -//#define TFT_INVERSION_OFF +// #define TFT_INVERSION_ON +// #define TFT_INVERSION_OFF + +// If a backlight control signal is available then define the TFT_BL pin in Section 2 +// below. The backlight will be turned ON when tft.begin() is called, but the library +// needs to know if the LEDs are ON with the pin HIGH or LOW. If the LEDs are to be +// driven with a PWM signal or turned OFF/ON then this must be handled by the user +// sketch. e.g. with digitalWrite(TFT_BL, LOW); + +// #define TFT_BACKLIGHT_ON HIGH // HIGH or LOW are options // ################################################################################## // @@ -120,7 +131,7 @@ //#define TFT_BL PIN_D1 // LED back-light (only for ST7789 with backlight control pin) -//#define TOUCH_CS PIN_D1 // 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 @@ -182,7 +193,7 @@ //#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_CS 33 // Chip select control pin (library pulls permanently low //#define TFT_DC 15 // Data Command control pin - must use a pin in the range 0-31 //#define TFT_RST 32 // Reset pin, toggles on startup @@ -214,7 +225,7 @@ #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_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 @@ -226,7 +237,7 @@ // ################################################################################## // -// Section 5. Other options +// Section 4. Other options // // ################################################################################## diff --git a/library.json b/library.json index fec1a93..640cef0 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "TFT_eSPI", - "version": "1.3.12", + "version": "1.3.13", "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 000edb4..7f4f52c 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=TFT_eSPI -version=1.3.12 +version=1.3.13 author=Bodmer maintainer=Bodmer sentence=A fast TFT graphics library for ESP8266 and ESP32 processors for the Arduino IDE From b5db54dc79cd5ee7b0ceb1d9f4be7042b3163ac9 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Tue, 22 Jan 2019 11:46:04 +0000 Subject: [PATCH 235/287] Fix #293 for ESP8266 core 2.4.2 and later UTFT8 encoded Unicode values were not being drawn on screen with versions after 2.4.1 of the ESP8266 core. --- TFT_eSPI.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/TFT_eSPI.cpp b/TFT_eSPI.cpp index fae609a..f1fb2c4 100644 --- a/TFT_eSPI.cpp +++ b/TFT_eSPI.cpp @@ -3906,14 +3906,14 @@ size_t TFT_eSPI::write(uint8_t utf8) if(fontLoaded) { uint16_t unicode = decodeUTF8(utf8); - if (!unicode) return 0; + if (!unicode) return 1; //fontFile = SPIFFS.open( _gFontFilename, "r" ); //if(!fontFile) //{ // fontLoaded = false; - // return 0; + // return 1; //} drawGlyph(unicode); @@ -3925,7 +3925,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; + else if (utf8 < 32) return 1; uint16_t width = 0; uint16_t height = 0; @@ -3945,7 +3945,7 @@ size_t TFT_eSPI::write(uint8_t utf8) #ifdef LOAD_FONT2 if (textfont == 2) { - if (utf8 > 127) return 0; + if (utf8 > 127) return 1; // 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; @@ -3962,7 +3962,7 @@ size_t TFT_eSPI::write(uint8_t utf8) { if ((textfont>2) && (textfont<9)) { - if (utf8 > 127) return 0; + if (utf8 > 127) return 1; // 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 ); @@ -3978,7 +3978,7 @@ size_t TFT_eSPI::write(uint8_t utf8) height = 8; } #else - if (textfont==1) return 0; + if (textfont==1) return 1; #endif height = height * textsize; @@ -4009,8 +4009,8 @@ size_t TFT_eSPI::write(uint8_t utf8) 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; + if (uniCode > (uint8_t)pgm_read_byte(&gfxFont->last )) return 1; + if (uniCode < (uint8_t)pgm_read_byte(&gfxFont->first)) return 1; uint8_t c2 = uniCode - pgm_read_byte(&gfxFont->first); GFXglyph *glyph = &(((GFXglyph *)pgm_read_dword(&gfxFont->glyph))[c2]); From 8cf85c89d33390107c1a879fbe6d83efab692390 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Tue, 22 Jan 2019 12:10:13 +0000 Subject: [PATCH 236/287] Fix #293 for Sprites --- Extensions/Sprite.cpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Extensions/Sprite.cpp b/Extensions/Sprite.cpp index 0102320..615f2ab 100644 --- a/Extensions/Sprite.cpp +++ b/Extensions/Sprite.cpp @@ -1311,7 +1311,7 @@ size_t TFT_eSprite::write(uint8_t utf8) if(this->fontLoaded) { uint16_t unicode = decodeUTF8(utf8); - if (unicode < 32 && utf8 != '\n') return 0; + if (unicode < 32 && utf8 != '\n') return 1; //fontFile = SPIFFS.open( _gFontFilename, "r" ); //fontFile = SPIFFS.open( this->_gFontFilename, "r" ); @@ -1319,22 +1319,22 @@ size_t TFT_eSprite::write(uint8_t utf8) //if(!fontFile) //{ // fontLoaded = false; - // return 0; + // return 1; //} //Serial.print("Decoded Unicode = 0x");Serial.println(unicode,HEX); drawGlyph(unicode); //fontFile.close(); - return 0; + return 1; } #endif - if (!_created ) return 0; + if (!_created ) 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; + else if (utf8 < 32) return 1; uint16_t width = 0; uint16_t height = 0; @@ -1354,7 +1354,7 @@ size_t TFT_eSprite::write(uint8_t utf8) #ifdef LOAD_FONT2 if (textfont == 2) { - if (utf8 > 127) return 0; + if (utf8 > 127) return 1; // 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; @@ -1371,7 +1371,7 @@ size_t TFT_eSprite::write(uint8_t utf8) { if ((textfont>2) && (textfont<9)) { - if (utf8 > 127) return 0; + if (utf8 > 127) return 1; // 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 ); @@ -1387,7 +1387,7 @@ size_t TFT_eSprite::write(uint8_t utf8) height = 8; } #else - if (textfont==1) return 0; + if (textfont==1) return 1; #endif height = height * textsize; @@ -1418,8 +1418,8 @@ size_t TFT_eSprite::write(uint8_t utf8) 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; + if (uniCode > (uint8_t)pgm_read_byte(&gfxFont->last )) return 1; + if (uniCode < (uint8_t)pgm_read_byte(&gfxFont->first)) return 1; uint8_t c2 = uniCode - pgm_read_byte(&gfxFont->first); GFXglyph *glyph = &(((GFXglyph *)pgm_read_dword(&gfxFont->glyph))[c2]); From 0fdd25186ae65a132904fbac1d3cd9387572d20c Mon Sep 17 00:00:00 2001 From: Bodmer Date: Tue, 22 Jan 2019 17:43:31 +0000 Subject: [PATCH 237/287] Fix #289 A 16 bit color image can now be plotted into an 8 bit Sprite. --- Extensions/Sprite.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Extensions/Sprite.cpp b/Extensions/Sprite.cpp index 615f2ab..abf0163 100644 --- a/Extensions/Sprite.cpp +++ b/Extensions/Sprite.cpp @@ -587,6 +587,7 @@ void TFT_eSprite::pushImage(int32_t x, int32_t y, uint32_t w, uint32_t h, uint1 uint16_t color = data[xp + yp * w]; if(_iswapBytes) color = color<<8 | color>>8; _img8[x + ys * _iwidth] = (uint8_t)((color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3); + x++; } ys++; } @@ -679,6 +680,7 @@ void TFT_eSprite::pushImage(int32_t x, int32_t y, uint32_t w, uint32_t h, const uint16_t color = pgm_read_word(data + xp + yp * w); if(_iswapBytes) color = color<<8 | color>>8; _img8[x + ys * _iwidth] = (uint8_t)((color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3); + x++; } ys++; } From 054a824eb824771a5836dcd56499002eadaee6b2 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Tue, 22 Jan 2019 18:41:31 +0000 Subject: [PATCH 238/287] Add ESP32 VSPI or HSPI port option + others If the VSPI port is in use and pins are not accessible (e.g. TTGO T-Beam) then add or uncomment the following line in the setup header file: //#define USE_HSPI_PORT Minor performance tweaks for ESP32 to minimise the occurence of the slow transaction overhead. setAddrWindow now takes xstart, ystart, width and height as inputs Multi-sample raw touch x and y for noisy displays Example update for setAddrWindow change compatibility --- Extensions/Smooth_font.cpp | 5 +- Extensions/Sprite.cpp | 24 +- Extensions/Touch.cpp | 34 +- TFT_eSPI.cpp | 386 ++++++++++-------- TFT_eSPI.h | 84 ++-- User_Setup.h | 4 + User_Setups/SetupX_Template.h | 4 + examples/160 x 128/Pong_v3/Pong_v3.ino | 6 +- .../TFT_graphicstest_PDQ3.ino | 10 +- .../TFT_Mandlebrot/TFT_Mandlebrot.ino | 11 +- examples/320 x 240/TFT_Pong/TFT_Pong.ino | 6 +- .../TFT_graphicstest_PDQ.ino | 6 +- .../480 x 320/TFT_flash_jpg/TFT_flash_jpg.ino | 7 +- .../TFT_ring_meter/TFT_ring_meter.ino | 6 +- library.json | 2 +- library.properties | 2 +- 16 files changed, 355 insertions(+), 242 deletions(-) diff --git a/Extensions/Smooth_font.cpp b/Extensions/Smooth_font.cpp index 3dc2cb0..1952add 100644 --- a/Extensions/Smooth_font.cpp +++ b/Extensions/Smooth_font.cpp @@ -441,6 +441,8 @@ void TFT_eSPI::drawGlyph(uint16_t code) int16_t cy = cursor_y + gFont.maxAscent - gdY[gNum]; int16_t cx = cursor_x + gdX[gNum]; + startWrite(); // Avoid slow ESP32 transaction overhead for every pixel + for (int y = 0; y < gHeight[gNum]; y++) { fontFile.read(pbuffer, gWidth[gNum]); // _tft->width()) max_x = _tft->width(); if (max_y > _tft->height()) max_y = _tft->height(); - _tft->startWrite(); + _tft->startWrite(); // ESP32: avoid transaction overhead for every tft pixel + // Scan destination bounding box and fetch transformed pixels from source Sprite for (int32_t x = min_x; x <= max_x; x++) { int32_t xt = x - _tft->_xpivot; @@ -325,7 +326,8 @@ bool TFT_eSprite::pushRotated(int16_t angle, int32_t transp) else if (column_drawn) y = max_y; // Skip remaining column pixels } } - _tft->endWrite(); + + _tft->endWrite(); // ESP32: end transaction return true; } @@ -465,7 +467,6 @@ void TFT_eSprite::pushSprite(int32_t x, int32_t y) if (_bpp == 16) _tft->pushImage(x, y, _iwidth, _iheight, _img ); else _tft->pushImage(x, y, _dwidth, _dheight, _img8, (bool)(_bpp == 8)); - } @@ -562,7 +563,7 @@ void TFT_eSprite::pushImage(int32_t x, int32_t y, uint32_t w, uint32_t h, uint1 if (xs + w >= _iwidth) ws = _iwidth - xs; if (ys + h >= _iheight) hs = _iheight - ys; - if (_bpp == 16) + if (_bpp == 16) // Plot a 16 bpp image into a 16 bpp Sprite { for (uint32_t yp = yo; yp < yo + hs; yp++) { @@ -577,7 +578,7 @@ void TFT_eSprite::pushImage(int32_t x, int32_t y, uint32_t w, uint32_t h, uint1 ys++; } } - else if (_bpp == 8) + else if (_bpp == 8) // Plot a 16 bpp image into a 8 bpp Sprite { for (uint32_t yp = yo; yp < yo + hs; yp++) { @@ -613,7 +614,7 @@ void TFT_eSprite::pushImage(int32_t x, int32_t y, uint32_t w, uint32_t h, uint1 x = y; y = _dheight - tx - 1; } - + // Plot a 1bpp image into a 1bpp Sprite uint8_t* pdata = (uint8_t* ) data; uint32_t ww = (w+7) & 0xFFF8; for (int32_t yp = 0; yp= _iwidth) || (y >= _iheight) || (w == 0) || (h == 0) || !_created) return; if ((x + (int32_t)w < 0) || (y + (int32_t)h < 0)) return; @@ -654,7 +659,7 @@ void TFT_eSprite::pushImage(int32_t x, int32_t y, uint32_t w, uint32_t h, const if (xs + w >= _iwidth) ws = _iwidth - xs; if (ys + h >= _iheight) hs = _iheight - ys; - if (_bpp == 16) + if (_bpp == 16) // Plot a 16 bpp image into a 16 bpp Sprite { for (uint32_t yp = yo; yp < yo + hs; yp++) { @@ -670,7 +675,7 @@ void TFT_eSprite::pushImage(int32_t x, int32_t y, uint32_t w, uint32_t h, const } } - else if (_bpp == 8) + else if (_bpp == 8) // Plot a 16 bpp image into a 8 bpp Sprite { for (uint32_t yp = yo; yp < yo + hs; yp++) { @@ -706,7 +711,7 @@ void TFT_eSprite::pushImage(int32_t x, int32_t y, uint32_t w, uint32_t h, const x = y; y = _dheight - tx - 1; } - + // Plot a 1bpp image into a 1bpp Sprite const uint8_t* pdata = (const uint8_t* ) data; uint32_t ww = (w+7) & 0xFFF8; for (int32_t yp = 0; yp>3); // Read last 8 bits and start new XP conversion + tmp |= 0x1f & (spi.transfer(0x90)>>3); // Read last 8 bits and start new XP conversion *x = tmp; - // Start XP sample request for y position - tmp = SPI.transfer(0); // Read first 8 bits + // Start XP sample request for y position, read 4 times and keep last sample + spi.transfer(0); // Read first 8 bits + spi.transfer(0x90); // Read last 8 bits and start new XP conversion + spi.transfer(0); // Read first 8 bits + spi.transfer(0x90); // Read last 8 bits and start new XP conversion + spi.transfer(0); // Read first 8 bits + spi.transfer(0x90); // Read last 8 bits and start new XP conversion + + tmp = spi.transfer(0); // Read first 8 bits tmp = tmp <<5; - tmp |= 0x1f & (SPI.transfer(0)>>3); // Read last 8 bits + tmp |= 0x1f & (spi.transfer(0)>>3); // Read last 8 bits *y = tmp; @@ -57,9 +71,9 @@ uint16_t TFT_eSPI::getTouchRawZ(void){ // Calculate Z int16_t tz = 0xFFF; - SPI.transfer(0xb0); // Start new Z1 conversion - tz += SPI.transfer16(0xc0) >> 3; // Read Z1 and start Z2 conversion - tz -= SPI.transfer16(0x00) >> 3; // Read Z2 + spi.transfer(0xb0); // Start new Z1 conversion + tz += spi.transfer16(0xc0) >> 3; // Read Z1 and start Z2 conversion + tz -= spi.transfer16(0x00) >> 3; // Read Z2 T_CS_H; diff --git a/TFT_eSPI.cpp b/TFT_eSPI.cpp index f1fb2c4..4df354b 100644 --- a/TFT_eSPI.cpp +++ b/TFT_eSPI.cpp @@ -15,10 +15,16 @@ #include "TFT_eSPI.h" -#include - -#ifndef ESP32_PARALLEL - #include +#if defined (ESP32) + #if !defined (ESP32_PARALLEL) + #ifdef USE_HSPI_PORT + SPIClass spi = SPIClass(HSPI); + #else // use default VSPI port + SPIClass spi = SPIClass(VSPI); + #endif + #endif +#else // ESP8266 + SPIClass spi = SPIClass(); #endif // SUPPORT_TRANSACTIONS is mandatory for ESP32 so the hal mutex is toggled @@ -48,32 +54,32 @@ void busDir(uint32_t mask, uint8_t mode); inline void TFT_eSPI::spi_begin(void){ #if defined (SPI_HAS_TRANSACTION) && defined (SUPPORT_TRANSACTIONS) && !defined(ESP32_PARALLEL) - if (locked) {locked = false; SPI.beginTransaction(SPISettings(SPI_FREQUENCY, MSBFIRST, TFT_SPI_MODE));} + if (locked) {locked = false; spi.beginTransaction(SPISettings(SPI_FREQUENCY, MSBFIRST, TFT_SPI_MODE));} #endif } inline void TFT_eSPI::spi_end(void){ #if defined (SPI_HAS_TRANSACTION) && defined (SUPPORT_TRANSACTIONS) && !defined(ESP32_PARALLEL) - if(!inTransaction) {if (!locked) {locked = true; SPI.endTransaction();}} + if(!inTransaction) {if (!locked) {locked = true; spi.endTransaction();}} #endif } inline void TFT_eSPI::spi_begin_read(void){ #if defined (SPI_HAS_TRANSACTION) && defined (SUPPORT_TRANSACTIONS) && !defined(ESP32_PARALLEL) - if (locked) {locked = false; SPI.beginTransaction(SPISettings(SPI_READ_FREQUENCY, MSBFIRST, TFT_SPI_MODE));} + if (locked) {locked = false; spi.beginTransaction(SPISettings(SPI_READ_FREQUENCY, MSBFIRST, TFT_SPI_MODE));} #else #if !defined(ESP32_PARALLEL) - SPI.setFrequency(SPI_READ_FREQUENCY); + spi.setFrequency(SPI_READ_FREQUENCY); #endif #endif } inline void TFT_eSPI::spi_end_read(void){ #if defined (SPI_HAS_TRANSACTION) && defined (SUPPORT_TRANSACTIONS) && !defined(ESP32_PARALLEL) - if(!inTransaction) {if (!locked) {locked = true; SPI.endTransaction();}} + if(!inTransaction) {if (!locked) {locked = true; spi.endTransaction();}} #else #if !defined(ESP32_PARALLEL) - SPI.setFrequency(SPI_FREQUENCY); + spi.setFrequency(SPI_FREQUENCY); #endif #endif } @@ -82,17 +88,17 @@ inline void TFT_eSPI::spi_end_read(void){ 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));} + if (locked) {locked = false; spi.beginTransaction(SPISettings(SPI_TOUCH_FREQUENCY, MSBFIRST, SPI_MODE0));} #else - SPI.setFrequency(SPI_TOUCH_FREQUENCY); + 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();}} + if(!inTransaction) {if (!locked) {locked = true; spi.endTransaction();}} #else - SPI.setFrequency(SPI_FREQUENCY); + spi.setFrequency(SPI_FREQUENCY); #endif } @@ -263,17 +269,17 @@ void TFT_eSPI::init(uint8_t tc) #ifdef TFT_SPI_OVERLAP // Overlap mode SD0=MISO, SD1=MOSI, CLK=SCLK must use D3 as CS // pins(int8_t sck, int8_t miso, int8_t mosi, int8_t ss); - //SPI.pins( 6, 7, 8, 0); - SPI.pins(6, 7, 8, 0); + //spi.pins( 6, 7, 8, 0); + 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(ESP32_PARALLEL) #if defined (TFT_MOSI) && !defined (TFT_SPI_OVERLAP) - SPI.begin(TFT_SCLK, TFT_MISO, TFT_MOSI, -1); + spi.begin(TFT_SCLK, TFT_MISO, TFT_MOSI, -1); #else - SPI.begin(); + spi.begin(); #endif #endif #endif @@ -284,9 +290,9 @@ void TFT_eSPI::init(uint8_t tc) // 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); - SPI.setDataMode(TFT_SPI_MODE); - SPI.setFrequency(SPI_FREQUENCY); + spi.setBitOrder(MSBFIRST); + spi.setDataMode(TFT_SPI_MODE); + spi.setFrequency(SPI_FREQUENCY); #endif #if defined(ESP32_PARALLEL) @@ -298,7 +304,7 @@ void TFT_eSPI::init(uint8_t tc) 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 #endif @@ -611,7 +617,7 @@ uint16_t TFT_eSPI::readPixel(int32_t x0, int32_t y0) { #if defined(ESP32_PARALLEL) - readAddrWindow(x0, y0, x0, y0); // Sets CS low + readAddrWindow(x0, y0, 1, 1); // Sets CS low // Set masked pins D0- D7 to input busDir(dir_mask, INPUT); @@ -652,7 +658,7 @@ uint16_t TFT_eSPI::readPixel(int32_t x0, int32_t y0) spi_begin_read(); - readAddrWindow(x0, y0, x0, y0); // Sets CS low + readAddrWindow(x0, y0, 1, 1); // Sets CS low #ifdef TFT_SDA_READ begin_SDA_Read(); @@ -757,7 +763,7 @@ void TFT_eSPI::readRect(uint32_t x, uint32_t y, uint32_t w, uint32_t h, uint16_t #if defined(ESP32_PARALLEL) - readAddrWindow(x, y, x + w - 1, y + h - 1); // Sets CS low + readAddrWindow(x, y, w, h); // Sets CS low // Set masked pins D0- D7 to input busDir(dir_mask, INPUT); @@ -799,7 +805,7 @@ void TFT_eSPI::readRect(uint32_t x, uint32_t y, uint32_t w, uint32_t h, uint16_t spi_begin_read(); - readAddrWindow(x, y, x + w - 1, y + h - 1); // Sets CS low + readAddrWindow(x, y, w, h); // Sets CS low #ifdef TFT_SDA_READ begin_SDA_Read(); @@ -881,7 +887,7 @@ void TFT_eSPI::begin_SDA_Read(void) #ifdef TFT_SPI_OVERLAP // Reads in overlap mode not supported #else - SPI.end(); + spi.end(); #endif #endif } @@ -901,9 +907,9 @@ void TFT_eSPI::end_SDA_Read(void) pinMatrixInAttach(TFT_MISO, VSPIQ_IN_IDX, false); #else #ifdef TFT_SPI_OVERLAP - SPI.pins(6, 7, 8, 0); + spi.pins(6, 7, 8, 0); #else - SPI.begin(); + spi.begin(); #endif #endif } @@ -946,7 +952,7 @@ void TFT_eSPI::pushImage(int32_t x, int32_t y, uint32_t w, uint32_t h, uint16_t spi_begin(); inTransaction = true; - setAddrWindow(x, y, x + dw - 1, y + dh - 1); // Sets CS low and sent RAMWR + setWindow(x, y, x + dw - 1, y + dh - 1); // Sets CS low and sent RAMWR data += dx + dy * w; @@ -1007,7 +1013,7 @@ void TFT_eSPI::pushImage(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); } + if (move) { move = false; setWindow(px, y, xe, ye); } lineBuf[np] = *ptr; np++; } @@ -1042,7 +1048,10 @@ void TFT_eSPI::pushImage(int32_t x, int32_t y, uint32_t w, uint32_t h, uint16_t ***************************************************************************************/ void TFT_eSPI::pushImage(int32_t x, int32_t y, uint32_t w, uint32_t h, const uint16_t *data) { - +#ifdef ESP32 + pushImage(x, y, w, h, (uint16_t*)data); +#else + // Partitioned memory FLASH processor if ((x >= (int32_t)_width) || (y >= (int32_t)_height)) return; int32_t dx = 0; @@ -1066,7 +1075,7 @@ void TFT_eSPI::pushImage(int32_t x, int32_t y, uint32_t w, uint32_t h, const uin uint16_t buffer[64]; uint16_t* pix_buffer = buffer; - setAddrWindow(x, y, x + dw - 1, y + dh - 1); + setWindow(x, y, x + dw - 1, y + dh - 1); // Work out the number whole buffers to send uint16_t nb = (dw * dh) / 64; @@ -1095,6 +1104,7 @@ void TFT_eSPI::pushImage(int32_t x, int32_t y, uint32_t w, uint32_t h, const uin inTransaction = false; spi_end(); +#endif // if ESP32 else ESP8266 check } @@ -1104,7 +1114,10 @@ void TFT_eSPI::pushImage(int32_t x, int32_t y, uint32_t w, uint32_t h, const uin ***************************************************************************************/ void TFT_eSPI::pushImage(int32_t x, int32_t y, uint32_t w, uint32_t h, const uint16_t *data, uint16_t transp) { - +#ifdef ESP32 + pushImage(x, y, w, h, (uint16_t*) data, transp); +#else + // Partitioned memory FLASH processor if ((x >= (int32_t)_width) || (y >= (int32_t)_height)) return; int32_t dx = 0; @@ -1145,7 +1158,7 @@ void TFT_eSPI::pushImage(int32_t x, int32_t y, uint32_t w, uint32_t h, const uin uint16_t color = pgm_read_word(ptr); if (transp != color) { - if (move) { move = false; setAddrWindow(px, y, xe, ye); } + if (move) { move = false; setWindow(px, y, xe, ye); } lineBuf[np] = color; np++; } @@ -1171,6 +1184,7 @@ void TFT_eSPI::pushImage(int32_t x, int32_t y, uint32_t w, uint32_t h, const uin inTransaction = false; spi_end(); +#endif // if ESP32 else ESP8266 check } @@ -1198,7 +1212,7 @@ void TFT_eSPI::pushImage(int32_t x, int32_t y, uint32_t w, uint32_t h, uint8_t * spi_begin(); inTransaction = true; - setAddrWindow(x, y, x + dw - 1, y + dh - 1); // Sets CS low and sent RAMWR + setWindow(x, y, x + dw - 1, y + dh - 1); // Sets CS low and sent RAMWR // Line buffer makes plotting faster uint16_t lineBuf[dw]; @@ -1339,7 +1353,7 @@ void TFT_eSPI::pushImage(int32_t x, int32_t y, uint32_t w, uint32_t h, uint8_t * { if (transp != *ptr) { - if (move) { move = false; setAddrWindow(px, y, xe, ye);} + if (move) { move = false; setWindow(px, y, xe, ye);} uint8_t color = *ptr; // Shifts are slow so check if colour has changed first @@ -1398,7 +1412,7 @@ void TFT_eSPI::pushImage(int32_t x, int32_t y, uint32_t w, uint32_t h, uint8_t * if (move) { move = false; - setAddrWindow(px, y, xe, ye); + setWindow(px, y, xe, ye); } np++; } @@ -1464,7 +1478,7 @@ void TFT_eSPI::readRectRGB(int32_t x0, int32_t y0, int32_t w, int32_t h, uint8_ spi_begin_read(); - readAddrWindow(x0, y0, x0 + w - 1, y0 + h - 1); // Sets CS low + readAddrWindow(x0, y0, w, h); // Sets CS low #ifdef TFT_SDA_READ begin_SDA_Read(); @@ -2397,7 +2411,7 @@ void TFT_eSPI::drawChar(int32_t x, int32_t y, unsigned char c, uint32_t color, u uint8_t mask = 0x1; spi_begin(); //inTransaction = true; - setAddrWindow(x, y, x+5, y+8); + setWindow(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; @@ -2620,27 +2634,26 @@ void TFT_eSPI::drawChar(int32_t x, int32_t y, unsigned char c, uint32_t color, u /*************************************************************************************** -** Function name: setWindow +** Function name: setAddrWindow ** 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) +void TFT_eSPI::setAddrWindow(int32_t x0, int32_t y0, int32_t w, int32_t h) { spi_begin(); - setAddrWindow(x0, y0, x1, y1); + setWindow(x0, y0, x0 + w - 1, y0 + h - 1); CS_H; spi_end(); } /*************************************************************************************** -** Function name: setAddrWindow +** Function name: setWindow ** Description: define an area to receive a stream of pixels ***************************************************************************************/ -// Chip select stays low, use setWindow() from sketches - +// Chip select stays low, call spi_begin first. Use setAddrWindow() from sketches #if defined (ESP8266) && !defined (RPI_WRITE_STROBE) && !defined (RPI_ILI9486_DRIVER) -void TFT_eSPI::setAddrWindow(int32_t xs, int32_t ys, int32_t xe, int32_t ye) +void TFT_eSPI::setWindow(int32_t xs, int32_t ys, int32_t xe, int32_t ye) { //spi_begin(); @@ -2708,7 +2721,7 @@ void TFT_eSPI::setAddrWindow(int32_t xs, int32_t ys, int32_t xe, int32_t ye) #elif defined (ESP8266) && !defined (RPI_WRITE_STROBE) && defined (RPI_ILI9486_DRIVER) // This is for the RPi display that needs 16 bits -void TFT_eSPI::setAddrWindow(int32_t xs, int32_t ys, int32_t xe, int32_t ye) +void TFT_eSPI::setWindow(int32_t xs, int32_t ys, int32_t xe, int32_t ye) { //spi_begin(); @@ -2731,7 +2744,7 @@ void TFT_eSPI::setAddrWindow(int32_t xs, int32_t ys, int32_t xe, int32_t ye) DC_D; uint8_t xb[] = { 0, (uint8_t) (xs>>8), 0, (uint8_t) (xs>>0), 0, (uint8_t) (xe>>8), 0, (uint8_t) (xe>>0), }; - SPI.writePattern(&xb[0], 8, 1); + spi.writePattern(&xb[0], 8, 1); // Row addr set DC_C; @@ -2745,7 +2758,7 @@ void TFT_eSPI::setAddrWindow(int32_t xs, int32_t ys, int32_t xe, int32_t ye) DC_D; uint8_t yb[] = { 0, (uint8_t) (ys>>8), 0, (uint8_t) (ys>>0), 0, (uint8_t) (ye>>8), 0, (uint8_t) (ye>>0), }; - SPI.writePattern(&yb[0], 8, 1); + spi.writePattern(&yb[0], 8, 1); // write to RAM DC_C; @@ -2763,7 +2776,7 @@ void TFT_eSPI::setAddrWindow(int32_t xs, int32_t ys, int32_t xe, int32_t ye) #else #if defined (ESP8266) && defined (RPI_ILI9486_DRIVER) // This is for the RPi display that needs 16 bits -void TFT_eSPI::setAddrWindow(int32_t x0, int32_t y0, int32_t x1, int32_t y1) +void TFT_eSPI::setWindow(int32_t x0, int32_t y0, int32_t x1, int32_t y1) { //spi_begin(); @@ -2840,7 +2853,7 @@ void TFT_eSPI::setAddrWindow(int32_t x0, int32_t y0, int32_t x1, int32_t y1) #else // This is for the ESP32 -void TFT_eSPI::setAddrWindow(int32_t x0, int32_t y0, int32_t x1, int32_t y1) +void TFT_eSPI::setWindow(int32_t x0, int32_t y0, int32_t x1, int32_t y1) { //spi_begin(); @@ -2862,7 +2875,7 @@ void TFT_eSPI::setAddrWindow(int32_t x0, int32_t y0, int32_t x1, int32_t y1) #if defined (RPI_ILI9486_DRIVER) uint8_t xb[] = { 0, (uint8_t) (x0>>8), 0, (uint8_t) (x0>>0), 0, (uint8_t) (x1>>8), 0, (uint8_t) (x1>>0), }; - SPI.writePattern(&xb[0], 8, 1); + spi.writePattern(&xb[0], 8, 1); #else tft_Write_32(SPI_32(x0, x1)); #endif @@ -2876,7 +2889,7 @@ void TFT_eSPI::setAddrWindow(int32_t x0, int32_t y0, int32_t x1, int32_t y1) #if defined (RPI_ILI9486_DRIVER) uint8_t yb[] = { 0, (uint8_t) (y0>>8), 0, (uint8_t) (y0>>0), 0, (uint8_t) (y1>>8), 0, (uint8_t) (y1>>0), }; - SPI.writePattern(&yb[0], 8, 1); + spi.writePattern(&yb[0], 8, 1); #else tft_Write_32(SPI_32(y0, y1)); #endif @@ -2900,10 +2913,13 @@ void TFT_eSPI::setAddrWindow(int32_t x0, int32_t y0, int32_t x1, int32_t y1) ***************************************************************************************/ // Chip select stays low #if defined (ESP8266) && !defined (RPI_WRITE_STROBE) -void TFT_eSPI::readAddrWindow(int32_t xs, int32_t ys, int32_t xe, int32_t ye) +void TFT_eSPI::readAddrWindow(int32_t xs, int32_t ys, int32_t w, int32_t h) { //spi_begin(); + int32_t xe = xs + w - 1; + int32_t ye = ys + h - 1; + addr_col = 0xFFFF; addr_row = 0xFFFF; @@ -2966,10 +2982,13 @@ void TFT_eSPI::readAddrWindow(int32_t xs, int32_t ys, int32_t xe, int32_t ye) #else //ESP32 -void TFT_eSPI::readAddrWindow(int32_t x0, int32_t y0, int32_t x1, int32_t y1) +void TFT_eSPI::readAddrWindow(int32_t xs, int32_t ys, int32_t w, int32_t h) { //spi_begin(); + int32_t xe = xs + w - 1; + int32_t ye = ys + h - 1; + addr_col = 0xFFFF; addr_row = 0xFFFF; @@ -2987,7 +3006,7 @@ void TFT_eSPI::readAddrWindow(int32_t x0, int32_t y0, int32_t x1, int32_t y1) DC_D; - tft_Write_32(SPI_32(x0, x1)); + tft_Write_32(SPI_32(xs, xe)); // Row addr set DC_C; @@ -2996,7 +3015,7 @@ void TFT_eSPI::readAddrWindow(int32_t x0, int32_t y0, int32_t x1, int32_t y1) DC_D; - tft_Write_32(SPI_32(y0, y1)); + tft_Write_32(SPI_32(ys, ye)); DC_C; @@ -3044,7 +3063,7 @@ void TFT_eSPI::drawPixel(uint32_t x, uint32_t y, uint32_t color) #if defined (RPI_ILI9486_DRIVER) // This is for the RPi display that needs 16 bits per byte uint8_t cBin[] = { 0, (uint8_t) (x>>8), 0, (uint8_t) (x>>0)}; - SPI.writePattern(&cBin[0], 4, 2); + spi.writePattern(&cBin[0], 4, 2); #else SPI1U1 = mask | (31 << SPILMOSI) | (31 << SPILMISO); // Load the two coords as a 32 bit value and shift in one go @@ -3072,7 +3091,7 @@ void TFT_eSPI::drawPixel(uint32_t x, uint32_t y, uint32_t color) #if defined (RPI_ILI9486_DRIVER) // This is for the RPi display that needs 16 bits per byte uint8_t cBin[] = { 0, (uint8_t) (y>>8), 0, (uint8_t) (y>>0)}; - SPI.writePattern(&cBin[0], 4, 2); + spi.writePattern(&cBin[0], 4, 2); #else SPI1U1 = mask | (31 << SPILMOSI) | (31 << SPILMISO); // Load the two coords as a 32 bit value and shift in one go @@ -3221,7 +3240,7 @@ void TFT_eSPI::drawPixel(uint32_t x, uint32_t y, uint32_t color) #if defined (RPI_ILI9486_DRIVER) uint8_t xb[] = { 0, (uint8_t) (x>>8), 0, (uint8_t) (x>>0), 0, (uint8_t) (x>>8), 0, (uint8_t) (x>>0), }; - SPI.writePattern(&xb[0], 8, 1); + spi.writePattern(&xb[0], 8, 1); #else tft_Write_32(SPI_32(x, x)); #endif @@ -3240,7 +3259,7 @@ void TFT_eSPI::drawPixel(uint32_t x, uint32_t y, uint32_t color) #if defined (RPI_ILI9486_DRIVER) uint8_t yb[] = { 0, (uint8_t) (y>>8), 0, (uint8_t) (y>>0), 0, (uint8_t) (y>>8), 0, (uint8_t) (y>>0), }; - SPI.writePattern(&yb[0], 8, 1); + spi.writePattern(&yb[0], 8, 1); #else tft_Write_32(SPI_32(y, y)); #endif @@ -3295,7 +3314,7 @@ void TFT_eSPI::pushColor(uint16_t color, uint32_t len) #ifdef RPI_WRITE_STROBE uint8_t colorBin[] = { (uint8_t) (color >> 8), (uint8_t) color }; - if(len) SPI.writePattern(&colorBin[0], 2, 1); len--; + if(len) spi.writePattern(&colorBin[0], 2, 1); len--; while(len--) {WR_L; WR_H;} #else #if defined (ESP32_PARALLEL) @@ -3340,7 +3359,7 @@ void TFT_eSPI::writeColor(uint16_t color, uint32_t len) { #ifdef RPI_WRITE_STROBE uint8_t colorBin[] = { (uint8_t) (color >> 8), (uint8_t) color }; - if(len) SPI.writePattern(&colorBin[0], 2, 1); len--; + if(len) spi.writePattern(&colorBin[0], 2, 1); len--; while(len--) {WR_L; WR_H;} #else #if defined (ESP32_PARALLEL) @@ -3355,7 +3374,7 @@ void TFT_eSPI::writeColor(uint16_t color, uint32_t len) ** Function name: pushColors ** Description: push an array of pixels for 16 bit raw image drawing ***************************************************************************************/ -// Assumed that setWindow() has previously been called +// Assumed that setAddrWindow() has previously been called void TFT_eSPI::pushColors(uint8_t *data, uint32_t len) { @@ -3364,8 +3383,8 @@ void TFT_eSPI::pushColors(uint8_t *data, uint32_t len) 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); + while ( len >=64 ) {spi.writePattern(data, 64, 1); data += 64; len -= 64; } + if (len) spi.writePattern(data, len, 1); #else #ifdef ESP32_PARALLEL while (len--) {tft_Write_8(*data); data++;} @@ -3374,10 +3393,10 @@ void TFT_eSPI::pushColors(uint8_t *data, uint32_t len) while (len>1) {color = (*data++) | ((*data++)<<8); tft_Write_16(color); len-=2;} #else #if (SPI_FREQUENCY == 80000000) - while ( len >=64 ) {SPI.writePattern(data, 64, 1); data += 64; len -= 64; } - if (len) SPI.writePattern(data, len, 1); + while ( len >=64 ) {spi.writePattern(data, 64, 1); data += 64; len -= 64; } + if (len) spi.writePattern(data, len, 1); #else - SPI.writeBytes(data, len); + spi.writeBytes(data, len); #endif #endif #endif @@ -3403,8 +3422,8 @@ void TFT_eSPI::pushColors(uint16_t *data, uint32_t len, bool swap) if (swap) while ( len-- ) {tft_Write_16(*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); + if (swap) spi.writePixels(data,len<<1); + else spi.writeBytes((uint8_t*)data,len<<1); #endif #else @@ -3600,7 +3619,7 @@ void TFT_eSPI::drawLine(int32_t x0, int32_t y0, int32_t x1, int32_t y1, uint32_t if (x0 > x1) {spi_end(); return;} - setAddrWindow(y0, x0, y0, _height); + setWindow(y0, x0, y0, _height); SPI1U1 = mask; SPI1W0 = swapped_color; for (; x0 <= x1; x0++) { @@ -3613,7 +3632,7 @@ void TFT_eSPI::drawLine(int32_t x0, int32_t y0, int32_t x1, int32_t y1, uint32_t if ((y0 < 0) || (y0 >= _width)) break; err += dx; while(SPI1CMD & SPIBUSY) {} - setAddrWindow(y0, x0+1, y0, _height); + setWindow(y0, x0+1, y0, _height); SPI1U1 = mask; SPI1W0 = swapped_color; } @@ -3634,7 +3653,7 @@ void TFT_eSPI::drawLine(int32_t x0, int32_t y0, int32_t x1, int32_t y1, uint32_t if (x0 > x1) {spi_end(); return;} - setAddrWindow(x0, y0, _width, y0); + setWindow(x0, y0, _width, y0); SPI1U1 = mask; SPI1W0 = swapped_color; for (; x0 <= x1; x0++) { @@ -3647,7 +3666,7 @@ void TFT_eSPI::drawLine(int32_t x0, int32_t y0, int32_t x1, int32_t y1, uint32_t if ((y0 < 0) || (y0 >= _height)) break; err += dx; while(SPI1CMD & SPIBUSY) {} - setAddrWindow(x0+1, y0, _width, y0); + setWindow(x0+1, y0, _width, y0); SPI1U1 = mask; SPI1W0 = swapped_color; } @@ -3670,13 +3689,18 @@ void TFT_eSPI::drawLine(int32_t x0, int32_t y0, int32_t x1, int32_t y1, uint32_t #if defined (ESP8266) && !defined (RPI_WRITE_STROBE) void TFT_eSPI::drawFastVLine(int32_t x, int32_t y, int32_t h, uint32_t color) { - // Rudimentary clipping - if ((x >= _width) || (y >= _height) || (h < 1)) return; - if ((y + h - 1) >= _height) h = _height - y; + // Clipping + if ((x < 0) || (x >= _width) || (y >= _height)) return; + + if (y < 0) { h += y; y = 0; } + + if ((y + h) > _height) h = _height - y; + + if (h < 1) return; spi_begin(); - setAddrWindow(x, y, x, y + h - 1); + setWindow(x, y, x, y + h - 1); writeBlock(color, h); @@ -3689,17 +3713,22 @@ void TFT_eSPI::drawFastVLine(int32_t x, int32_t y, int32_t h, uint32_t color) void TFT_eSPI::drawFastVLine(int32_t x, int32_t y, int32_t h, uint32_t color) { - // Rudimentary clipping - if ((x >= _width) || (y >= _height) || (h < 1)) return; - if ((y + h - 1) >= _height) h = _height - y; + // Clipping + if ((x < 0) || (x >= _width) || (y >= _height)) return; + + if (y < 0) { h += y; y = 0; } + + if ((y + h) > _height) h = _height - y; + + if (h < 1) return; spi_begin(); - setAddrWindow(x, y, x, y + h - 1); + setWindow(x, y, x, y + h - 1); #ifdef RPI_WRITE_STROBE #if defined (ESP8266) - // SPI1U1 will already be set to transfer 16 bits by setAddrWindow() + // SPI1U1 will already be set to transfer 16 bits by setWindow() SPI1W0 = (color >> 8) | (color << 8); SPI1CMD |= SPIBUSY; while(SPI1CMD & SPIBUSY) {} @@ -3729,13 +3758,18 @@ void TFT_eSPI::drawFastVLine(int32_t x, int32_t y, int32_t h, uint32_t color) #if defined (ESP8266) && !defined (RPI_WRITE_STROBE) void TFT_eSPI::drawFastHLine(int32_t x, int32_t y, int32_t w, uint32_t color) { - // Rudimentary clipping - if ((x >= _width) || (y >= _height) || (w < 1)) return; - if ((x + w - 1) >= _width) w = _width - x; + // Clipping + if ((y < 0) || (x >= _width) || (y >= _height)) return; + + if (x < 0) { w += x; x = 0; } + + if ((x + w) > _width) w = _width - x; + + if (w < 1) return; spi_begin(); - setAddrWindow(x, y, x + w - 1, y); + setWindow(x, y, x + w - 1, y); writeBlock(color, w); @@ -3753,11 +3787,11 @@ void TFT_eSPI::drawFastHLine(int32_t x, int32_t y, int32_t w, uint32_t color) if ((x + w - 1) >= _width) w = _width - x; spi_begin(); - setAddrWindow(x, y, x + w - 1, y); + setWindow(x, y, x + w - 1, y); #ifdef RPI_WRITE_STROBE #if defined (ESP8266) - // SPI1U1 will already be set to transfer 16 bits by setAddrWindow() + // SPI1U1 will already be set to transfer 16 bits by setWindow() SPI1W0 = (color >> 8) | (color << 8); SPI1CMD |= SPIBUSY; while(SPI1CMD & SPIBUSY) {} @@ -3787,13 +3821,19 @@ void TFT_eSPI::drawFastHLine(int32_t x, int32_t y, int32_t w, uint32_t color) #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) { - // rudimentary clipping (drawChar w/big text requires this) - if ((x > _width) || (y > _height) || (w < 1) || (h < 1)) return; - if ((x + w - 1) > _width) w = _width - x; - if ((y + h - 1) > _height) h = _height - y; + // Clipping + if ((x >= _width) || (y >= _height)) return; + + if (x < 0) { w += x; x = 0; } + if (y < 0) { h += y; y = 0; } + + if ((x + w) > _width) w = _width - x; + if ((y + h) > _height) h = _height - y; + + if ((w < 1) || (h < 1)) return; spi_begin(); - setAddrWindow(x, y, x + w - 1, y + h - 1); + setWindow(x, y, x + w - 1, y + h - 1); writeBlock(color, w * h); @@ -3806,13 +3846,19 @@ void TFT_eSPI::fillRect(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t col 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; - if ((x + w - 1) > _width) w = _width - x; - if ((y + h - 1) > _height) h = _height - y; + // Clipping + if ((x >= _width) || (y >= _height)) return; + + if (x < 0) { w += x; x = 0; } + if (y < 0) { h += y; y = 0; } + + if ((x + w) > _width) w = _width - x; + if ((y + h) > _height) h = _height - y; + + if ((w < 1) || (h < 1)) return; spi_begin(); - setAddrWindow(x, y, x + w - 1, y + h - 1); + setWindow(x, y, x + w - 1, y + h - 1); uint32_t n = (uint32_t)w * (uint32_t)h; @@ -3906,6 +3952,10 @@ size_t TFT_eSPI::write(uint8_t utf8) if(fontLoaded) { uint16_t unicode = decodeUTF8(utf8); + + //Serial.print("UniCode="); Serial.println(unicode); + //Serial.print("UTF8 ="); Serial.println(utf8); + if (!unicode) return 1; //fontFile = SPIFFS.open( _gFontFilename, "r" ); @@ -4176,7 +4226,7 @@ int16_t TFT_eSPI::drawChar(unsigned int uniCode, int x, int y, int font) // Faster drawing of characters and background using block write { spi_begin(); - setAddrWindow(x, y, (x + w * 8) - 1, y + height - 1); + setWindow(x, y, (x + w * 8) - 1, y + height - 1); uint8_t mask; for (int i = 0; i < height; i++) @@ -4238,7 +4288,7 @@ int16_t TFT_eSPI::drawChar(unsigned int uniCode, int x, int y, int font) } while (line--) { // In this case the while(line--) is faster pc++; // This is faster than putting pc+=line before while()? - setAddrWindow(px, py, px + ts, py + ts); + setWindow(px, py, px + ts, py + ts); if (ts) { tnp = np; @@ -4267,7 +4317,7 @@ int16_t TFT_eSPI::drawChar(unsigned int uniCode, int x, int y, int font) // so use faster drawing of characters and background using block write { //spi_begin(); - setAddrWindow(x, y, x + width - 1, y + height - 1); + setWindow(x, y, x + width - 1, y + height - 1); #ifdef RPI_WRITE_STROBE uint8_t textcolorBin[] = { (uint8_t) (textcolor >> 8), (uint8_t) textcolor }; @@ -4282,7 +4332,7 @@ int16_t TFT_eSPI::drawChar(unsigned int uniCode, int x, int y, int font) line &= 0x7F; line++; w -= line; #ifdef RPI_WRITE_STROBE - SPI.writePattern(&textcolorBin[0], 2, 1); line--; + spi.writePattern(&textcolorBin[0], 2, 1); line--; while(line--) {WR_L; WR_H;} #else #ifdef ESP32_PARALLEL @@ -4295,7 +4345,7 @@ int16_t TFT_eSPI::drawChar(unsigned int uniCode, int x, int y, int font) else { line++; w -= line; #ifdef RPI_WRITE_STROBE - SPI.writePattern(&textbgcolorBin[0], 2, 1); line--; + spi.writePattern(&textbgcolorBin[0], 2, 1); line--; while(line--) {WR_L; WR_H;} #else #ifdef ESP32_PARALLEL @@ -4927,56 +4977,56 @@ void writeBlock(uint16_t color, uint32_t repeat) if (repeat > 19) { - SET_PERI_REG_BITS(SPI_MOSI_DLEN_REG(SPI_NUM), SPI_USR_MOSI_DBITLEN, 479, SPI_USR_MOSI_DBITLEN_S); + SET_PERI_REG_BITS(SPI_MOSI_DLEN_REG(SPI_PORT), SPI_USR_MOSI_DBITLEN, 479, SPI_USR_MOSI_DBITLEN_S); while(repeat>19) { - while (READ_PERI_REG(SPI_CMD_REG(SPI_NUM))&SPI_USR); - WRITE_PERI_REG(SPI_W0_REG(SPI_NUM), r0); - WRITE_PERI_REG(SPI_W1_REG(SPI_NUM), r1); - WRITE_PERI_REG(SPI_W2_REG(SPI_NUM), r2); - WRITE_PERI_REG(SPI_W3_REG(SPI_NUM), r0); - WRITE_PERI_REG(SPI_W4_REG(SPI_NUM), r1); - WRITE_PERI_REG(SPI_W5_REG(SPI_NUM), r2); - WRITE_PERI_REG(SPI_W6_REG(SPI_NUM), r0); - WRITE_PERI_REG(SPI_W7_REG(SPI_NUM), r1); - WRITE_PERI_REG(SPI_W8_REG(SPI_NUM), r2); - WRITE_PERI_REG(SPI_W9_REG(SPI_NUM), r0); - WRITE_PERI_REG(SPI_W10_REG(SPI_NUM), r1); - WRITE_PERI_REG(SPI_W11_REG(SPI_NUM), r2); - WRITE_PERI_REG(SPI_W12_REG(SPI_NUM), r0); - WRITE_PERI_REG(SPI_W13_REG(SPI_NUM), r1); - WRITE_PERI_REG(SPI_W14_REG(SPI_NUM), r2); - SET_PERI_REG_MASK(SPI_CMD_REG(SPI_NUM), SPI_USR); + while (READ_PERI_REG(SPI_CMD_REG(SPI_PORT))&SPI_USR); + WRITE_PERI_REG(SPI_W0_REG(SPI_PORT), r0); + WRITE_PERI_REG(SPI_W1_REG(SPI_PORT), r1); + WRITE_PERI_REG(SPI_W2_REG(SPI_PORT), r2); + WRITE_PERI_REG(SPI_W3_REG(SPI_PORT), r0); + WRITE_PERI_REG(SPI_W4_REG(SPI_PORT), r1); + WRITE_PERI_REG(SPI_W5_REG(SPI_PORT), r2); + WRITE_PERI_REG(SPI_W6_REG(SPI_PORT), r0); + WRITE_PERI_REG(SPI_W7_REG(SPI_PORT), r1); + WRITE_PERI_REG(SPI_W8_REG(SPI_PORT), r2); + WRITE_PERI_REG(SPI_W9_REG(SPI_PORT), r0); + WRITE_PERI_REG(SPI_W10_REG(SPI_PORT), r1); + WRITE_PERI_REG(SPI_W11_REG(SPI_PORT), r2); + WRITE_PERI_REG(SPI_W12_REG(SPI_PORT), r0); + WRITE_PERI_REG(SPI_W13_REG(SPI_PORT), r1); + WRITE_PERI_REG(SPI_W14_REG(SPI_PORT), r2); + SET_PERI_REG_MASK(SPI_CMD_REG(SPI_PORT), SPI_USR); repeat -= 20; } - while (READ_PERI_REG(SPI_CMD_REG(SPI_NUM))&SPI_USR); + while (READ_PERI_REG(SPI_CMD_REG(SPI_PORT))&SPI_USR); } if (repeat) { - SET_PERI_REG_BITS(SPI_MOSI_DLEN_REG(SPI_NUM), SPI_USR_MOSI_DBITLEN, (repeat * 24) - 1, SPI_USR_MOSI_DBITLEN_S); - WRITE_PERI_REG(SPI_W0_REG(SPI_NUM), r0); - WRITE_PERI_REG(SPI_W1_REG(SPI_NUM), r1); - WRITE_PERI_REG(SPI_W2_REG(SPI_NUM), r2); - WRITE_PERI_REG(SPI_W3_REG(SPI_NUM), r0); - WRITE_PERI_REG(SPI_W4_REG(SPI_NUM), r1); - WRITE_PERI_REG(SPI_W5_REG(SPI_NUM), r2); + SET_PERI_REG_BITS(SPI_MOSI_DLEN_REG(SPI_PORT), SPI_USR_MOSI_DBITLEN, (repeat * 24) - 1, SPI_USR_MOSI_DBITLEN_S); + WRITE_PERI_REG(SPI_W0_REG(SPI_PORT), r0); + WRITE_PERI_REG(SPI_W1_REG(SPI_PORT), r1); + WRITE_PERI_REG(SPI_W2_REG(SPI_PORT), r2); + WRITE_PERI_REG(SPI_W3_REG(SPI_PORT), r0); + WRITE_PERI_REG(SPI_W4_REG(SPI_PORT), r1); + WRITE_PERI_REG(SPI_W5_REG(SPI_PORT), r2); if (repeat > 8 ) { - WRITE_PERI_REG(SPI_W6_REG(SPI_NUM), r0); - WRITE_PERI_REG(SPI_W7_REG(SPI_NUM), r1); - WRITE_PERI_REG(SPI_W8_REG(SPI_NUM), r2); - WRITE_PERI_REG(SPI_W9_REG(SPI_NUM), r0); - WRITE_PERI_REG(SPI_W10_REG(SPI_NUM), r1); - WRITE_PERI_REG(SPI_W11_REG(SPI_NUM), r2); - WRITE_PERI_REG(SPI_W12_REG(SPI_NUM), r0); - WRITE_PERI_REG(SPI_W13_REG(SPI_NUM), r1); - WRITE_PERI_REG(SPI_W14_REG(SPI_NUM), r2); + WRITE_PERI_REG(SPI_W6_REG(SPI_PORT), r0); + WRITE_PERI_REG(SPI_W7_REG(SPI_PORT), r1); + WRITE_PERI_REG(SPI_W8_REG(SPI_PORT), r2); + WRITE_PERI_REG(SPI_W9_REG(SPI_PORT), r0); + WRITE_PERI_REG(SPI_W10_REG(SPI_PORT), r1); + WRITE_PERI_REG(SPI_W11_REG(SPI_PORT), r2); + WRITE_PERI_REG(SPI_W12_REG(SPI_PORT), r0); + WRITE_PERI_REG(SPI_W13_REG(SPI_PORT), r1); + WRITE_PERI_REG(SPI_W14_REG(SPI_PORT), r2); } - SET_PERI_REG_MASK(SPI_CMD_REG(SPI_NUM), SPI_USR); - while (READ_PERI_REG(SPI_CMD_REG(SPI_NUM))&SPI_USR); + SET_PERI_REG_MASK(SPI_CMD_REG(SPI_PORT), SPI_USR); + while (READ_PERI_REG(SPI_CMD_REG(SPI_PORT))&SPI_USR); } } @@ -4990,39 +5040,39 @@ void writeBlock(uint16_t color, uint32_t repeat) if (repeat > 31) // Revert legacy toggle buffer change { - WRITE_PERI_REG(SPI_MOSI_DLEN_REG(SPI_NUM), 511); + WRITE_PERI_REG(SPI_MOSI_DLEN_REG(SPI_PORT), 511); while(repeat>31) { - while (READ_PERI_REG(SPI_CMD_REG(SPI_NUM))&SPI_USR); - WRITE_PERI_REG(SPI_W0_REG(SPI_NUM), color32); - WRITE_PERI_REG(SPI_W1_REG(SPI_NUM), color32); - WRITE_PERI_REG(SPI_W2_REG(SPI_NUM), color32); - WRITE_PERI_REG(SPI_W3_REG(SPI_NUM), color32); - WRITE_PERI_REG(SPI_W4_REG(SPI_NUM), color32); - WRITE_PERI_REG(SPI_W5_REG(SPI_NUM), color32); - WRITE_PERI_REG(SPI_W6_REG(SPI_NUM), color32); - WRITE_PERI_REG(SPI_W7_REG(SPI_NUM), color32); - WRITE_PERI_REG(SPI_W8_REG(SPI_NUM), color32); - WRITE_PERI_REG(SPI_W9_REG(SPI_NUM), color32); - WRITE_PERI_REG(SPI_W10_REG(SPI_NUM), color32); - WRITE_PERI_REG(SPI_W11_REG(SPI_NUM), color32); - WRITE_PERI_REG(SPI_W12_REG(SPI_NUM), color32); - WRITE_PERI_REG(SPI_W13_REG(SPI_NUM), color32); - WRITE_PERI_REG(SPI_W14_REG(SPI_NUM), color32); - WRITE_PERI_REG(SPI_W15_REG(SPI_NUM), color32); - SET_PERI_REG_MASK(SPI_CMD_REG(SPI_NUM), SPI_USR); + while (READ_PERI_REG(SPI_CMD_REG(SPI_PORT))&SPI_USR); + WRITE_PERI_REG(SPI_W0_REG(SPI_PORT), color32); + WRITE_PERI_REG(SPI_W1_REG(SPI_PORT), color32); + WRITE_PERI_REG(SPI_W2_REG(SPI_PORT), color32); + WRITE_PERI_REG(SPI_W3_REG(SPI_PORT), color32); + WRITE_PERI_REG(SPI_W4_REG(SPI_PORT), color32); + WRITE_PERI_REG(SPI_W5_REG(SPI_PORT), color32); + WRITE_PERI_REG(SPI_W6_REG(SPI_PORT), color32); + WRITE_PERI_REG(SPI_W7_REG(SPI_PORT), color32); + WRITE_PERI_REG(SPI_W8_REG(SPI_PORT), color32); + WRITE_PERI_REG(SPI_W9_REG(SPI_PORT), color32); + WRITE_PERI_REG(SPI_W10_REG(SPI_PORT), color32); + WRITE_PERI_REG(SPI_W11_REG(SPI_PORT), color32); + WRITE_PERI_REG(SPI_W12_REG(SPI_PORT), color32); + WRITE_PERI_REG(SPI_W13_REG(SPI_PORT), color32); + WRITE_PERI_REG(SPI_W14_REG(SPI_PORT), color32); + WRITE_PERI_REG(SPI_W15_REG(SPI_PORT), color32); + SET_PERI_REG_MASK(SPI_CMD_REG(SPI_PORT), SPI_USR); repeat -= 32; } - while (READ_PERI_REG(SPI_CMD_REG(SPI_NUM))&SPI_USR); + while (READ_PERI_REG(SPI_CMD_REG(SPI_PORT))&SPI_USR); } if (repeat) { // Revert toggle buffer change - WRITE_PERI_REG(SPI_MOSI_DLEN_REG(SPI_NUM), (repeat << 4) - 1); - for (uint32_t i=0; i <= (repeat>>1); i++) WRITE_PERI_REG((SPI_W0_REG(SPI_NUM) + (i << 2)), color32); - SET_PERI_REG_MASK(SPI_CMD_REG(SPI_NUM), SPI_USR); - while (READ_PERI_REG(SPI_CMD_REG(SPI_NUM))&SPI_USR); + WRITE_PERI_REG(SPI_MOSI_DLEN_REG(SPI_PORT), (repeat << 4) - 1); + for (uint32_t i=0; i <= (repeat>>1); i++) WRITE_PERI_REG((SPI_W0_REG(SPI_PORT) + (i << 2)), color32); + SET_PERI_REG_MASK(SPI_CMD_REG(SPI_PORT), SPI_USR); + while (READ_PERI_REG(SPI_CMD_REG(SPI_PORT))&SPI_USR); } } #endif diff --git a/TFT_eSPI.h b/TFT_eSPI.h index 1422fb4..5e471e9 100644 --- a/TFT_eSPI.h +++ b/TFT_eSPI.h @@ -105,7 +105,11 @@ #ifdef ESP32 #include "soc/spi_reg.h" - #define SPI_NUM 0x3 + #ifdef USE_HSPI_PORT + #define SPI_PORT HSPI + #else + #define SPI_PORT VSPI + #endif #endif #ifdef SMOOTH_FONT @@ -312,32 +316,32 @@ #elif defined (ILI9488_DRIVER) // 16 bit colour converted to 3 bytes for 18 bit RGB // Write 8 bits to TFT - #define tft_Write_8(C) SPI.transfer(C) + #define tft_Write_8(C) spi.transfer(C) // Convert 16 bit colour to 18 bit and write in 3 bytes - #define tft_Write_16(C) SPI.transfer((C & 0xF800)>>8); \ - SPI.transfer((C & 0x07E0)>>3); \ - SPI.transfer((C & 0x001F)<<3) + #define tft_Write_16(C) spi.transfer((C & 0xF800)>>8); \ + spi.transfer((C & 0x07E0)>>3); \ + spi.transfer((C & 0x001F)<<3) // Convert swapped byte 16 bit colour to 18 bit and write in 3 bytes - #define tft_Write_16S(C) SPI.transfer(C & 0xF8); \ - SPI.transfer((C & 0xE0)>>11 | (C & 0x07)<<5); \ - SPI.transfer((C & 0x1F00)>>5) + #define tft_Write_16S(C) spi.transfer(C & 0xF8); \ + spi.transfer((C & 0xE0)>>11 | (C & 0x07)<<5); \ + spi.transfer((C & 0x1F00)>>5) // Write 32 bits to TFT - #define tft_Write_32(C) SPI.write32(C) + #define tft_Write_32(C) spi.write32(C) #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_16S(C) SPI.write16(C<<8 | C>>8) - #define tft_Write_32(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_16S(C) spi.write16(C<<8 | C>>8) + #define tft_Write_32(C) spi.write32(C) #elif defined ESP8266 - #define tft_Write_8(C) SPI.write(C) - #define tft_Write_16(C) SPI.write16(C) - #define tft_Write_32(C) SPI.write32(C) + #define tft_Write_8(C) spi.write(C) + #define tft_Write_16(C) spi.write16(C) + #define tft_Write_32(C) spi.write32(C) #else // ESP32 using SPI with 16 bit color display @@ -346,31 +350,31 @@ // Write 8 bits #define tft_Write_8(C) \ - WRITE_PERI_REG(SPI_MOSI_DLEN_REG(SPI_NUM), 8-1); \ - WRITE_PERI_REG(SPI_W0_REG(SPI_NUM), C); \ - SET_PERI_REG_MASK(SPI_CMD_REG(SPI_NUM), SPI_USR); \ - while (READ_PERI_REG(SPI_CMD_REG(SPI_NUM))&SPI_USR); + WRITE_PERI_REG(SPI_MOSI_DLEN_REG(SPI_PORT), 8-1); \ + WRITE_PERI_REG(SPI_W0_REG(SPI_PORT), C); \ + SET_PERI_REG_MASK(SPI_CMD_REG(SPI_PORT), SPI_USR); \ + while (READ_PERI_REG(SPI_CMD_REG(SPI_PORT))&SPI_USR); // Write 16 bits with corrected endianess for 16 bit colours #define tft_Write_16(C) \ - WRITE_PERI_REG(SPI_MOSI_DLEN_REG(SPI_NUM), 16-1); \ - WRITE_PERI_REG(SPI_W0_REG(SPI_NUM), C<<8 | C>>8); \ - SET_PERI_REG_MASK(SPI_CMD_REG(SPI_NUM), SPI_USR); \ - while (READ_PERI_REG(SPI_CMD_REG(SPI_NUM))&SPI_USR); + WRITE_PERI_REG(SPI_MOSI_DLEN_REG(SPI_PORT), 16-1); \ + WRITE_PERI_REG(SPI_W0_REG(SPI_PORT), C<<8 | C>>8); \ + SET_PERI_REG_MASK(SPI_CMD_REG(SPI_PORT), SPI_USR); \ + while (READ_PERI_REG(SPI_CMD_REG(SPI_PORT))&SPI_USR); // Write 16 bits #define tft_Write_16S(C) \ - WRITE_PERI_REG(SPI_MOSI_DLEN_REG(SPI_NUM), 16-1); \ - WRITE_PERI_REG(SPI_W0_REG(SPI_NUM), C); \ - SET_PERI_REG_MASK(SPI_CMD_REG(SPI_NUM), SPI_USR); \ - while (READ_PERI_REG(SPI_CMD_REG(SPI_NUM))&SPI_USR); + WRITE_PERI_REG(SPI_MOSI_DLEN_REG(SPI_PORT), 16-1); \ + WRITE_PERI_REG(SPI_W0_REG(SPI_PORT), C); \ + SET_PERI_REG_MASK(SPI_CMD_REG(SPI_PORT), SPI_USR); \ + while (READ_PERI_REG(SPI_CMD_REG(SPI_PORT))&SPI_USR); // Write 32 bits #define tft_Write_32(C) \ - WRITE_PERI_REG(SPI_MOSI_DLEN_REG(SPI_NUM), 32-1); \ - WRITE_PERI_REG(SPI_W0_REG(SPI_NUM), C); \ - SET_PERI_REG_MASK(SPI_CMD_REG(SPI_NUM), SPI_USR); \ - while (READ_PERI_REG(SPI_CMD_REG(SPI_NUM))&SPI_USR); + WRITE_PERI_REG(SPI_MOSI_DLEN_REG(SPI_PORT), 32-1); \ + WRITE_PERI_REG(SPI_W0_REG(SPI_PORT), C); \ + SET_PERI_REG_MASK(SPI_CMD_REG(SPI_PORT), SPI_USR); \ + while (READ_PERI_REG(SPI_CMD_REG(SPI_PORT))&SPI_USR); #endif @@ -383,7 +387,7 @@ #define SCLK_H GPOS=sclkpinmask #else // Use a SPI read transfer - #define tft_Read_8() SPI.transfer(0) + #define tft_Read_8() spi.transfer(0) #endif #endif @@ -650,7 +654,7 @@ class TFT_eSPI : public Print { width(void); // The TFT_eSprite class inherits the following functions - void setWindow(int16_t x0, int16_t y0, int16_t x1, int16_t y1), + void setWindow(int32_t xs, int32_t ys, int32_t xe, int32_t ye), pushColor(uint16_t color), pushColor(uint16_t color, uint32_t len), pushColors(uint16_t *data, uint32_t len, bool swap = true), // With byte swap option @@ -776,12 +780,12 @@ class TFT_eSPI : public Print { fontHeight(int16_t font), fontHeight(void); - void setAddrWindow(int32_t xs, int32_t ys, int32_t xe, int32_t ye); + void setAddrWindow(int32_t xs, int32_t ys, int32_t w, int32_t h); - // These 3 functions are used together, for every startWrite() there must be an endWrite() - void startWrite(void); // Begin SPI transaction - void writeColor(uint16_t color, uint32_t len); // Write a colour without transaction overhead - void endWrite(void); // End SPI transaction + // Compatibility additions (non-essential) + void startWrite(void); // Begin SPI transaction (not normally needed) + void writeColor(uint16_t color, uint32_t len); // Write colours without transaction overhead + void endWrite(void); // End SPI transaction size_t write(uint8_t); @@ -816,7 +820,7 @@ class TFT_eSPI : public Print { inline void spi_begin_read() __attribute__((always_inline)); inline void spi_end_read() __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 w, int32_t h); uint8_t tabcolor, colstart = 0, rowstart = 0; // some ST7735 displays need this changed diff --git a/User_Setup.h b/User_Setup.h index 6a5531e..b581333 100644 --- a/User_Setup.h +++ b/User_Setup.h @@ -262,6 +262,10 @@ // The XPT2046 requires a lower SPI clock rate of 2.5MHz so we define that here: #define SPI_TOUCH_FREQUENCY 2500000 +// The ESP32 has 2 free SPI ports i.e. VSPI and HSPI, the VSPI is the default. +// If the VSPI port is in use and pins are not accessible (e.g. TTGO T-Beam) +// then uncomment the following line: +//#define USE_HSPI_PORT // 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 diff --git a/User_Setups/SetupX_Template.h b/User_Setups/SetupX_Template.h index 6a5531e..36a2372 100644 --- a/User_Setups/SetupX_Template.h +++ b/User_Setups/SetupX_Template.h @@ -262,6 +262,10 @@ // The XPT2046 requires a lower SPI clock rate of 2.5MHz so we define that here: #define SPI_TOUCH_FREQUENCY 2500000 +// The ESP32 has 2 free SPI ports i.e. VSPI and HSPI, the VSPI is the default. +// If the VSPI port is in use and pins are not accessible (e.g. TTGO T-Beam) +// then uncomment the following line to use the HSPI port: +//#define USE_HSPI_PORT // 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 diff --git a/examples/160 x 128/Pong_v3/Pong_v3.ino b/examples/160 x 128/Pong_v3/Pong_v3.ino index 7c51299..318c851 100644 --- a/examples/160 x 128/Pong_v3/Pong_v3.ino +++ b/examples/160 x 128/Pong_v3/Pong_v3.ino @@ -107,13 +107,17 @@ void midline() { // If the ball is not on the line then don't redraw the line if ((ball_x dashline_x+dashline_w)) return; + tft.startWrite(); + // Quick way to draw a dashed line - tft.setAddrWindow(dashline_x,0,dashline_x+dashline_w-1,h); + tft.setAddrWindow(dashline_x, 0, dashline_w, h); for(int16_t i = 0; i < dashline_n; i+=2) { tft.pushColor(WHITE, dashline_w*dashline_h); // push dash pixels tft.pushColor(BLACK, dashline_w*dashline_h); // push gap pixels } + + tft.endWrite(); } void lpaddle() { diff --git a/examples/160 x 128/TFT_graphicstest_PDQ3/TFT_graphicstest_PDQ3.ino b/examples/160 x 128/TFT_graphicstest_PDQ3/TFT_graphicstest_PDQ3.ino index 325b9a4..148499e 100644 --- a/examples/160 x 128/TFT_graphicstest_PDQ3/TFT_graphicstest_PDQ3.ino +++ b/examples/160 x 128/TFT_graphicstest_PDQ3/TFT_graphicstest_PDQ3.ino @@ -308,14 +308,16 @@ uint32_t testHaD() 0x0a, 0x2b, 0x0b, 0x41, 0x0a, 0x29, 0x0b, 0x43, 0x0a, 0x27, 0x0a, 0x46, 0x0a, 0x25, 0x0a, 0x49, 0x09, 0x23, 0x08, 0x4e, 0x08, 0x96, 0x12 }; - + tft.fillScreen(TFT_BLACK); uint32_t start = micros_start(); - + + tft.startWrite(); + for (int i = 0; i < 0x10; i++) { - tft.setAddrWindow(0, 0, tft.width()-1, tft.height()-1); + tft.setAddrWindow(0, 0, tft.width(), tft.height()); uint16_t cnt = 0; uint16_t color = tft.color565((i << 4) | i, (i << 4) | i, (i << 4) | i); @@ -335,6 +337,8 @@ uint32_t testHaD() } } + tft.endWrite(); + uint32_t t = micros() - start; tft.setTextColor(TFT_YELLOW); diff --git a/examples/320 x 240/TFT_Mandlebrot/TFT_Mandlebrot.ino b/examples/320 x 240/TFT_Mandlebrot/TFT_Mandlebrot.ino index 73e0afc..2a04556 100644 --- a/examples/320 x 240/TFT_Mandlebrot/TFT_Mandlebrot.ino +++ b/examples/320 x 240/TFT_Mandlebrot/TFT_Mandlebrot.ino @@ -7,7 +7,7 @@ TFT_eSPI tft = TFT_eSPI(); // Invoke custom library -#define ILI9341_GREY 0x7BEF +#define TFT_GREY 0x7BEF unsigned long runTime = 0; @@ -16,8 +16,9 @@ uint16_t x0 = 0, x1 = 0, yy0 = 0, yy1 = 0; void setup() { + Serial.begin(250000); //randomSeed(analogRead(A0)); - + Serial.println(); // Setup the LCD tft.init(); tft.setRotation(3); @@ -27,7 +28,8 @@ void loop() { runTime = millis(); - tft.fillScreen(ILI9341_BLACK); + tft.fillScreen(TFT_BLACK); + tft.startWrite(); for (int px = 1; px < 320; px++) { for (int py = 0; py < 240; py++) @@ -49,6 +51,9 @@ void loop() yield();tft.drawPixel(px, py, color); } } + tft.endWrite(); + + Serial.println(millis()-runTime); while(1) yield(); } diff --git a/examples/320 x 240/TFT_Pong/TFT_Pong.ino b/examples/320 x 240/TFT_Pong/TFT_Pong.ino index 6cf91f2..5bcd880 100644 --- a/examples/320 x 240/TFT_Pong/TFT_Pong.ino +++ b/examples/320 x 240/TFT_Pong/TFT_Pong.ino @@ -108,13 +108,17 @@ void midline() { // If the ball is not on the line then don't redraw the line if ((ball_x dashline_x+dashline_w)) return; + tft.startWrite(); + // Quick way to draw a dashed line - tft.setWindow(dashline_x,0,dashline_x+dashline_w-1,h); + tft.setAddrWindow(dashline_x, 0, dashline_w, h); for(int16_t i = 0; i < dashline_n; i+=2) { tft.pushColor(WHITE, dashline_w*dashline_h); // push dash pixels tft.pushColor(BLACK, dashline_w*dashline_h); // push gap pixels } + + tft.endWrite(); } void lpaddle() { diff --git a/examples/320 x 240/TFT_graphicstest_PDQ/TFT_graphicstest_PDQ.ino b/examples/320 x 240/TFT_graphicstest_PDQ/TFT_graphicstest_PDQ.ino index 7db002e..8e03ef5 100644 --- a/examples/320 x 240/TFT_graphicstest_PDQ/TFT_graphicstest_PDQ.ino +++ b/examples/320 x 240/TFT_graphicstest_PDQ/TFT_graphicstest_PDQ.ino @@ -336,10 +336,10 @@ uint32_t testHaD() tft.fillScreen(TFT_BLACK); uint32_t start = micros_start(); - + for (int i = 0; i < 0x10; i++) { - tft.setWindow(0, 0, 240-1, 320-1); + tft.setAddrWindow(0, 0, 240, 320); uint16_t cnt = 0; uint16_t color = tft.color565((i << 4) | i, (i << 4) | i, (i << 4) | i); @@ -347,6 +347,7 @@ uint32_t testHaD() const uint8_t *cmp = &HaD_240x320[0]; + tft.startWrite(); while (cmp < &HaD_240x320[sizeof(HaD_240x320)]) { cnt = pgm_read_byte(cmp++); @@ -354,6 +355,7 @@ uint32_t testHaD() tft.pushColor(curcolor, cnt); // PDQ_GFX has count curcolor ^= color; } + tft.endWrite(); } uint32_t t = micros() - start; 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 5af0f13..f8ebcb1 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 @@ -171,11 +171,14 @@ void renderJPEG(int xpos, int ypos) { // calculate how many pixels must be drawn uint32_t mcu_pixels = win_w * win_h; + tft.startWrite(); + // 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()) { + // 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); + tft.setAddrWindow(mcu_x, mcu_y, win_w, win_h); // Write all MCU pixels to the TFT window while (mcu_pixels--) { @@ -185,6 +188,8 @@ void renderJPEG(int xpos, int ypos) { } else if ( (mcu_y + win_h) >= tft.height()) JpegDec.abort(); // Image has run off bottom of screen so abort decoding + + tft.endWrite(); } // calculate how long it took to draw the image diff --git a/examples/480 x 320/TFT_ring_meter/TFT_ring_meter.ino b/examples/480 x 320/TFT_ring_meter/TFT_ring_meter.ino index ebed42b..d4d8c2f 100644 --- a/examples/480 x 320/TFT_ring_meter/TFT_ring_meter.ino +++ b/examples/480 x 320/TFT_ring_meter/TFT_ring_meter.ino @@ -251,8 +251,10 @@ void drawIcon(const unsigned short* icon, int16_t x, int16_t y, int8_t width, in uint16_t pix_buffer[BUFF_SIZE]; // Pixel buffer (16 bits per pixel) + tft.startWrite(); + // Set up a window the right size to stream pixels into - tft.setWindow(x, y, x + width - 1, y + height - 1); + tft.setAddrWindow(x, y, width, height); // Work out the number whole buffers to send uint16_t nb = ((uint16_t)height * width) / BUFF_SIZE; @@ -273,5 +275,7 @@ void drawIcon(const unsigned short* icon, int16_t x, int16_t y, int8_t width, in for (int i = 0; i < np; i++) pix_buffer[i] = pgm_read_word(&icon[nb * BUFF_SIZE + i]); tft.pushColors(pix_buffer, np); } + + tft.endWrite(); } diff --git a/library.json b/library.json index 640cef0..d32cf0c 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "TFT_eSPI", - "version": "1.3.13", + "version": "1.4.0", "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 7f4f52c..834703f 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=TFT_eSPI -version=1.3.13 +version=1.4.0 author=Bodmer maintainer=Bodmer sentence=A fast TFT graphics library for ESP8266 and ESP32 processors for the Arduino IDE From 0460ab64d271c06ed8fbfcc548793d23b71b34fd Mon Sep 17 00:00:00 2001 From: Bodmer Date: Tue, 22 Jan 2019 19:09:18 +0000 Subject: [PATCH 239/287] Delete weather-station example Because Weather Underground will no longer support free API's --- .../weather-station/ArialRoundedMTBold_14.h | 224 ------- .../weather-station/ArialRoundedMTBold_36.h | 226 ------- examples/320 x 240/weather-station/GfxUi.cpp | 342 ---------- examples/320 x 240/weather-station/GfxUi.h | 60 -- .../weather-station/SPIFFS_Support.ino | 42 -- .../320 x 240/weather-station/WebResource.cpp | 105 ---- .../320 x 240/weather-station/WebResource.h | 46 -- examples/320 x 240/weather-station/settings.h | 72 --- .../weather-station/weather-station.ino | 595 ------------------ 9 files changed, 1712 deletions(-) delete mode 100644 examples/320 x 240/weather-station/ArialRoundedMTBold_14.h delete mode 100644 examples/320 x 240/weather-station/ArialRoundedMTBold_36.h delete mode 100644 examples/320 x 240/weather-station/GfxUi.cpp delete mode 100644 examples/320 x 240/weather-station/GfxUi.h delete mode 100644 examples/320 x 240/weather-station/SPIFFS_Support.ino delete mode 100644 examples/320 x 240/weather-station/WebResource.cpp delete mode 100644 examples/320 x 240/weather-station/WebResource.h delete mode 100644 examples/320 x 240/weather-station/settings.h delete mode 100644 examples/320 x 240/weather-station/weather-station.ino diff --git a/examples/320 x 240/weather-station/ArialRoundedMTBold_14.h b/examples/320 x 240/weather-station/ArialRoundedMTBold_14.h deleted file mode 100644 index b08d84f..0000000 --- a/examples/320 x 240/weather-station/ArialRoundedMTBold_14.h +++ /dev/null @@ -1,224 +0,0 @@ -/**The MIT License (MIT) -Copyright (c) 2015 by Daniel Eichhorn -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 COPYRIGHT 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. -See more at http://blog.squix.ch -*/ - -// Created by http://oleddisplay.squix.ch/ Consider a donation -// 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 - -const uint8_t ArialRoundedMTBold_14Bitmaps[] PROGMEM = { - - // Bitmap Data: - 0x00, // ' ' - 0xFF,0xF8,0xF0, // '!' - 0xDE,0xF6, // '"' - 0x12,0x32,0x36,0xFF,0xFF,0x24,0xFF,0xFF,0x4C,0x48, // '#' - 0x10,0x61,0xF6,0xAD,0x7A,0x1E,0x0E,0xD7,0xAF,0x5B,0xE1,0x02,0x04,0x00, // '$' - 0x60,0x92,0x22,0x44,0x49,0x07,0x60,0x0B,0x82,0x48,0xC9,0x11,0x24,0x18, // '%' - 0x3C,0x19,0x82,0x60,0xF0,0x39,0x33,0x6C,0x73,0x1C,0xFF,0x8E,0x30, // '&' - 0xFC, // ''' - 0x32,0x64,0xCC,0xCC,0xC4,0x62,0x30, // '(' - 0xC4,0x62,0x33,0x33,0x32,0x64,0xC0, // ')' - 0x21,0x2A,0xE5,0x28, // '*' - 0x18,0x18,0x18,0xFF,0xFF,0x18,0x18, // '+' - 0xF6, // ',' - 0xFF, // '-' - 0xF0, // '.' - 0x33,0x32,0x66,0x4C,0xCC, // '/' - 0x38,0xFB,0x9E,0x3C,0x78,0xF1,0xF6,0x7C,0x70, // '0' - 0x19,0xDF,0xB1,0x8C,0x63,0x18,0xC0, // '1' - 0x38,0xFF,0x1E,0x30,0xC3,0x0C,0x30,0xFF,0xFC, // '2' - 0x79,0x9B,0x10,0x63,0xC7,0x81,0xC3,0xC6,0xF0, // '3' - 0x06,0x0E,0x1E,0x16,0x26,0x46,0xFF,0xFF,0x06,0x06, // '4' - 0x7E,0xFD,0x06,0x0F,0xD8,0xC1,0xC3,0xCC,0xF0, // '5' - 0x38,0xDB,0x1E,0x0F,0xD8,0xF1,0xE3,0x66,0x78, // '6' - 0xFF,0xFC,0x30,0x41,0x82,0x0C,0x18,0x30,0xC0, // '7' - 0x38,0xDB,0x1F,0x63,0x98,0xF1,0xE3,0xC6,0xF8, // '8' - 0x3C,0x66,0xC3,0xC3,0xE7,0x3F,0x03,0xC3,0x66,0x3C, // '9' - 0xF0,0x3C, // ':' - 0xF0,0x3D,0x80, // ';' - 0x02,0x1D,0xF7,0x0E,0x0F,0x83,0x81, // '<' - 0xFF,0xFC,0x07,0xFF,0xE0, // '=' - 0x81,0xC1,0xF0,0x70,0xEF,0xB8,0x40, // '>' - 0x3C,0xFF,0x1E,0x30,0xC7,0x0C,0x00,0x30,0x60, // '?' - 0x0F,0x83,0x06,0x60,0x24,0xED,0x99,0x9B,0x19,0xB1,0xBB,0x12,0xBF,0xE4,0xDC,0x40,0x13,0x06,0x0F,0xC0, // '@' - 0x1C,0x0E,0x05,0x06,0xC3,0x63,0x19,0xFC,0xFE,0xC1,0xE0,0xC0, // 'A' - 0xFC,0xFE,0xC7,0xC6,0xFE,0xFE,0xC3,0xC3,0xFF,0xFE, // 'B' - 0x3E,0x3F,0xB8,0xF8,0x3C,0x06,0x03,0x06,0xC7,0x7F,0x0F,0x00, // 'C' - 0xFE,0x7F,0xB0,0xF8,0x3C,0x1E,0x0F,0x07,0x87,0xFF,0x7F,0x00, // 'D' - 0xFE,0xFF,0xC0,0xC0,0xFE,0xFE,0xC0,0xC0,0xFF,0xFF, // 'E' - 0xFF,0xFF,0x06,0x0F,0xDF,0xB0,0x60,0xC1,0x80, // 'F' - 0x1E,0x3F,0x98,0xF8,0x2C,0x06,0x3F,0x1E,0xC3,0x7F,0x9F,0x00, // 'G' - 0xC1,0xE0,0xF0,0x78,0x3F,0xFF,0xFF,0x07,0x83,0xC1,0xE0,0xC0, // 'H' - 0xFF,0xFF,0xF0, // 'I' - 0x06,0x0C,0x18,0x30,0x60,0xF1,0xF3,0x7E,0x78, // 'J' - 0xC3,0x63,0xB3,0x9B,0x8F,0x87,0x63,0x19,0x8E,0xC3,0x60,0xC0, // 'K' - 0xC1,0x83,0x06,0x0C,0x18,0x30,0x60,0xFF,0xFC, // 'L' - 0xE1,0xFC,0xFF,0x3F,0xCF,0xD2,0xF7,0xBD,0xEF,0x7B,0xCC,0xF3,0x30, // 'M' - 0xC1,0xF0,0xFC,0x7E,0x3D,0x9E,0x6F,0x3F,0x8F,0xC3,0xE0,0xC0, // 'N' - 0x1E,0x1F,0xE6,0x1B,0x03,0xC0,0xF0,0x3C,0x0D,0x86,0x7F,0x87,0x80, // 'O' - 0xFE,0xFF,0xC3,0xC3,0xFF,0xFE,0xC0,0xC0,0xC0,0xC0, // 'P' - 0x1E,0x0F,0xF1,0x87,0x60,0x6C,0x0D,0x81,0xB1,0x33,0x7C,0x7F,0x83,0xD8,0x01,0x80, // 'Q' - 0xFE,0xFF,0xC3,0xC3,0xFE,0xFC,0xCE,0xC6,0xC3,0xC3, // 'R' - 0x7C,0xFE,0xC7,0xC2,0x7C,0x0F,0xC3,0xC3,0x7E,0x3C, // 'S' - 0xFF,0xFF,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, // 'T' - 0xC1,0xE0,0xF0,0x78,0x3C,0x1E,0x0F,0x07,0xC7,0x7F,0x1F,0x00, // 'U' - 0xC1,0xE0,0xD8,0xCC,0x66,0x31,0xB0,0xD8,0x6C,0x1C,0x0E,0x00, // 'V' - 0xC7,0x1E,0x38,0xF1,0x46,0xDB,0x66,0xDB,0x36,0xD9,0xA2,0xC7,0x1C,0x38,0xE1,0x83,0x00, // 'W' - 0xC3,0x66,0x7E,0x3C,0x18,0x3C,0x7E,0x66,0xC3,0xC3, // 'X' - 0xC3,0xC3,0x66,0x3E,0x3C,0x18,0x18,0x18,0x18,0x18, // 'Y' - 0x7F,0x3F,0x80,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xFF,0xFF,0xC0, // 'Z' - 0xFF,0xCC,0xCC,0xCC,0xCC,0xCF,0xF0, // '[' - 0xCC,0x44,0x66,0x22,0x33, // '\' - 0xFF,0x33,0x33,0x33,0x33,0x3F,0xF0, // ']' - 0x30,0xE7,0x9A,0xCF,0x30, // '^' - 0xFE, // '_' - 0xD0, // '`' - 0x7D,0x8C,0x7F,0x3C,0x79,0xDD,0x80, // 'a' - 0xC1,0x83,0x06,0xEF,0xF8,0xF1,0xE3,0xFF,0xB8, // 'b' - 0x3C,0xFF,0x1E,0x0C,0x6F,0xCF,0x00, // 'c' - 0x06,0x0C,0x1B,0xBF,0xF8,0xF1,0xE3,0xFE,0xEC, // 'd' - 0x3C,0xCF,0x1F,0xFC,0x0C,0xCF,0x00, // 'e' - 0x3B,0x19,0xF6,0x31,0x8C,0x63,0x00, // 'f' - 0x77,0xFF,0x1E,0x3C,0x7F,0xDD,0xE3,0xC6,0xF8, // 'g' - 0xC1,0x83,0x06,0xEF,0xF8,0xF1,0xE3,0xC7,0x8C, // 'h' - 0xF3,0xFF,0xF0, // 'i' - 0x33,0x03,0x33,0x33,0x33,0x3F,0xE0, // 'j' - 0xC1,0x83,0x06,0x6D,0x9E,0x3E,0x66,0xCD,0x8C, // 'k' - 0xFF,0xFF,0xF0, // 'l' - 0xD9,0xDF,0xFF,0x31,0xE6,0x3C,0xC7,0x98,0xF3,0x18, // 'm' - 0xDD,0xFF,0x1E,0x3C,0x78,0xF1,0x80, // 'n' - 0x38,0xFB,0x1E,0x3C,0x6F,0x8E,0x00, // 'o' - 0xDD,0xFF,0x1E,0x3C,0x7F,0xF7,0x60,0xC1,0x80, // 'p' - 0x77,0xFF,0x1E,0x3C,0x7F,0xDD,0x83,0x06,0x0C, // 'q' - 0xDF,0xF1,0x8C,0x63,0x00, // 'r' - 0x7B,0x3E,0x1E,0x0F,0x37,0x80, // 's' - 0x63,0x19,0xF6,0x31,0x8C,0x79,0xC0, // 't' - 0xC7,0x8F,0x1E,0x3C,0x7F,0xDD,0x80, // 'u' - 0xC7,0x8D,0x93,0x62,0x87,0x04,0x00, // 'v' - 0xC4,0x79,0xCD,0x29,0x35,0x67,0xBC,0x63,0x0C,0x60, // 'w' - 0xC6,0xD9,0xF1,0xC7,0xCD,0xB1,0x80, // 'x' - 0xC7,0x8D,0x93,0x62,0xC7,0x06,0x18,0xF1,0xC0, // 'y' - 0xFE,0x18,0x61,0x86,0x1F,0xFF,0x80, // 'z' - 0x19,0xCC,0x63,0x3B,0x8E,0x31,0x8C,0x71,0x80, // '{' - 0xFF,0xFF,0xFF,0xC0, // '|' - 0xC7,0x18,0xC6,0x38,0xEE,0x63,0x19,0xCC,0x00 // '}' -}; -const GFXglyph ArialRoundedMTBold_14Glyphs[] PROGMEM = { -// bitmapOffset, width, height, xAdvance, xOffset, yOffset - { 0, 1, 1, 5, 0, 0 }, // ' ' - { 1, 2, 10, 6, 1, -10 }, // '!' - { 4, 5, 3, 8, 1, -10 }, // '"' - { 6, 8, 10, 9, 0, -10 }, // '#' - { 16, 7, 15, 9, 1, -12 }, // '$' - { 30, 11, 10, 13, 0, -10 }, // '%' - { 44, 10, 10, 12, 1, -10 }, // '&' - { 57, 2, 3, 4, 1, -10 }, // ''' - { 58, 4, 13, 6, 1, -10 }, // '(' - { 65, 4, 13, 6, 0, -10 }, // ')' - { 72, 5, 6, 7, 1, -11 }, // '*' - { 76, 8, 7, 9, 0, -9 }, // '+' - { 83, 2, 4, 5, 1, -2 }, // ',' - { 84, 4, 2, 6, 0, -5 }, // '-' - { 85, 2, 2, 5, 1, -2 }, // '.' - { 86, 4, 10, 5, 0, -10 }, // '/' - { 91, 7, 10, 9, 1, -10 }, // '0' - { 100, 5, 10, 9, 1, -10 }, // '1' - { 107, 7, 10, 9, 1, -10 }, // '2' - { 116, 7, 10, 9, 1, -10 }, // '3' - { 125, 8, 10, 9, 0, -10 }, // '4' - { 135, 7, 10, 9, 1, -10 }, // '5' - { 144, 7, 10, 9, 1, -10 }, // '6' - { 153, 7, 10, 9, 1, -10 }, // '7' - { 162, 7, 10, 9, 1, -10 }, // '8' - { 171, 8, 10, 9, 0, -10 }, // '9' - { 181, 2, 7, 5, 1, -7 }, // ':' - { 183, 2, 9, 5, 1, -7 }, // ';' - { 186, 7, 8, 9, 1, -9 }, // '<' - { 193, 7, 5, 9, 1, -7 }, // '=' - { 198, 7, 8, 9, 1, -9 }, // '>' - { 205, 7, 10, 9, 1, -10 }, // '?' - { 214, 12, 13, 15, 1, -10 }, // '@' - { 234, 9, 10, 11, 1, -10 }, // 'A' - { 246, 8, 10, 11, 1, -10 }, // 'B' - { 256, 9, 10, 11, 1, -10 }, // 'C' - { 268, 9, 10, 11, 1, -10 }, // 'D' - { 280, 8, 10, 10, 1, -10 }, // 'E' - { 290, 7, 10, 9, 1, -10 }, // 'F' - { 299, 9, 10, 12, 1, -10 }, // 'G' - { 311, 9, 10, 12, 1, -10 }, // 'H' - { 323, 2, 10, 5, 1, -10 }, // 'I' - { 326, 7, 10, 9, 0, -10 }, // 'J' - { 335, 9, 10, 11, 1, -10 }, // 'K' - { 347, 7, 10, 9, 1, -10 }, // 'L' - { 356, 10, 10, 13, 1, -10 }, // 'M' - { 369, 9, 10, 12, 1, -10 }, // 'N' - { 381, 10, 10, 12, 1, -10 }, // 'O' - { 394, 8, 10, 10, 1, -10 }, // 'P' - { 404, 11, 11, 12, 1, -10 }, // 'Q' - { 420, 8, 10, 11, 1, -10 }, // 'R' - { 430, 8, 10, 10, 1, -10 }, // 'S' - { 440, 8, 10, 10, 0, -10 }, // 'T' - { 450, 9, 10, 12, 1, -10 }, // 'U' - { 462, 9, 10, 11, 0, -10 }, // 'V' - { 474, 13, 10, 14, 0, -10 }, // 'W' - { 491, 8, 10, 9, 0, -10 }, // 'X' - { 501, 8, 10, 10, 0, -10 }, // 'Y' - { 511, 9, 10, 10, 0, -10 }, // 'Z' - { 523, 4, 13, 6, 1, -10 }, // '[' - { 530, 4, 10, 5, 0, -10 }, // '\' - { 535, 4, 13, 6, 0, -10 }, // ']' - { 542, 6, 6, 9, 1, -10 }, // '^' - { 547, 7, 1, 8, 0, 1 }, // '_' - { 548, 2, 2, 6, 1, -10 }, // '`' - { 549, 7, 7, 9, 1, -7 }, // 'a' - { 556, 7, 10, 10, 1, -10 }, // 'b' - { 565, 7, 7, 9, 1, -7 }, // 'c' - { 572, 7, 10, 10, 1, -10 }, // 'd' - { 581, 7, 7, 9, 1, -7 }, // 'e' - { 588, 5, 10, 6, 0, -10 }, // 'f' - { 595, 7, 10, 10, 1, -7 }, // 'g' - { 604, 7, 10, 9, 1, -10 }, // 'h' - { 613, 2, 10, 5, 1, -10 }, // 'i' - { 616, 4, 13, 5, -1, -10 }, // 'j' - { 623, 7, 10, 9, 1, -10 }, // 'k' - { 632, 2, 10, 5, 1, -10 }, // 'l' - { 635, 11, 7, 13, 1, -7 }, // 'm' - { 645, 7, 7, 9, 1, -7 }, // 'n' - { 652, 7, 7, 9, 1, -7 }, // 'o' - { 659, 7, 10, 10, 1, -7 }, // 'p' - { 668, 7, 10, 10, 1, -7 }, // 'q' - { 677, 5, 7, 7, 1, -7 }, // 'r' - { 682, 6, 7, 9, 1, -7 }, // 's' - { 688, 5, 10, 6, 0, -10 }, // 't' - { 695, 7, 7, 9, 1, -7 }, // 'u' - { 702, 7, 7, 9, 0, -7 }, // 'v' - { 709, 11, 7, 12, 0, -7 }, // 'w' - { 719, 7, 7, 8, 0, -7 }, // 'x' - { 726, 7, 10, 9, 0, -7 }, // 'y' - { 735, 7, 7, 8, 0, -7 }, // 'z' - { 742, 5, 13, 6, 0, -10 }, // '{' - { 751, 2, 13, 5, 1, -10 }, // '|' - { 755, 5, 13, 6, 1, -10 } // '}' character 0x7D -}; -const GFXfont ArialRoundedMTBold_14 PROGMEM = { // Last character bug fixed 0x7E to 0x7D -(uint8_t *)ArialRoundedMTBold_14Bitmaps,(GFXglyph *)ArialRoundedMTBold_14Glyphs,0x20, 0x7D, 17}; - diff --git a/examples/320 x 240/weather-station/ArialRoundedMTBold_36.h b/examples/320 x 240/weather-station/ArialRoundedMTBold_36.h deleted file mode 100644 index a171dbe..0000000 --- a/examples/320 x 240/weather-station/ArialRoundedMTBold_36.h +++ /dev/null @@ -1,226 +0,0 @@ -/**The MIT License (MIT) -Copyright (c) 2015 by Daniel Eichhorn -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 COPYRIGHT 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. -See more at http://blog.squix.ch -*/ - -// Created by http://oleddisplay.squix.ch/ Consider a donation -// 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 printed - -// Bodmer change: '`' changed to tiny degree symbol (typically this character is on top left key of a QWERTY keyboard) - -const uint8_t ArialRoundedMTBold_36Bitmaps[] PROGMEM = { - - // Bitmap Data: - 0x00, // ' ' - 0x77,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xE7,0x39,0xCE,0x73,0x00,0x00,0x3B,0xFF,0xFB,0x80, // '!' - 0xFC,0xFF,0xF3,0xFF,0xCF,0xFF,0x3F,0xFC,0xFF,0xF3,0xFF,0xCF,0xDE,0x1E,0x78,0x78, // '"' - 0x01,0x83,0x80,0x78,0x70,0x0F,0x0E,0x01,0xC3,0xC0,0x78,0x78,0x0F,0x0F,0x01,0xE1,0xE3,0xFF,0xFE,0xFF,0xFF,0xFF,0xFF,0xFD,0xFF,0xFF,0x07,0x87,0x80,0xF0,0xE0,0x1E,0x3C,0x07,0x87,0x80,0xF0,0xF0,0x7F,0xFF,0xDF,0xFF,0xFF,0xFF,0xFF,0xBF,0xFF,0xE3,0xC3,0xC0,0x78,0x78,0x0F,0x0E,0x01,0xC3,0xC0,0x78,0x78,0x07,0x06,0x00, // '#' - 0x00,0x60,0x00,0x0C,0x00,0x01,0x80,0x00,0x30,0x00,0x7F,0xE0,0x3F,0xFE,0x0F,0xFF,0xE1,0xF3,0x7E,0x7C,0x67,0xCF,0x0C,0x79,0xE1,0x8F,0x3C,0x30,0xC7,0xC6,0x00,0xFE,0xC0,0x0F,0xF8,0x00,0xFF,0xE0,0x0F,0xFF,0x00,0x7F,0xF0,0x01,0xFF,0x00,0x37,0xE0,0x06,0x3E,0xE0,0xC3,0xFC,0x18,0x7F,0xC3,0x0F,0xF8,0x61,0xEF,0x8C,0x79,0xFD,0x9F,0x1F,0xFF,0xC1,0xFF,0xF0,0x0F,0xFC,0x00,0x38,0x00,0x03,0x00,0x00,0x60,0x00,0x0C,0x00,0x01,0x80,0x00,0x30,0x00,0x06,0x00, // '$' - 0x1F,0x00,0x06,0x03,0xFC,0x00,0x70,0x1C,0x70,0x03,0x01,0xC1,0x80,0x38,0x0E,0x0E,0x03,0x80,0x70,0x70,0x1C,0x03,0x83,0x81,0xC0,0x1C,0x1C,0x0C,0x00,0xE0,0xE0,0xE0,0x07,0x07,0x0E,0x00,0x1C,0x70,0x70,0x00,0xFF,0x87,0x00,0x03,0xF8,0x30,0x00,0x00,0x03,0x87,0xF0,0x00,0x38,0x7F,0xC0,0x01,0xC3,0x8E,0x00,0x1C,0x38,0x38,0x00,0xC1,0xC1,0xC0,0x0E,0x0E,0x0E,0x00,0xE0,0x70,0x70,0x07,0x03,0x83,0x80,0x70,0x1C,0x1C,0x07,0x00,0xE0,0xE0,0x38,0x03,0x8E,0x03,0x80,0x0F,0xE0,0x1C,0x00,0x3E,0x00,0xC0,0x00,0x00, // '%' - 0x00,0xFC,0x00,0x01,0xFF,0x80,0x01,0xFF,0xE0,0x01,0xF0,0xF0,0x00,0xF0,0x3C,0x00,0x78,0x1E,0x00,0x3C,0x0F,0x00,0x1F,0x0F,0x80,0x0F,0xDF,0x80,0x03,0xFF,0x80,0x00,0xFF,0x80,0x00,0xFF,0x00,0x00,0xFF,0xC1,0x81,0xFF,0xF0,0xE1,0xF9,0xF8,0xF8,0xF8,0x7E,0x78,0xF8,0x1F,0xFC,0x7C,0x07,0xFC,0x3E,0x01,0xFE,0x1F,0x00,0x7F,0x0F,0xC0,0x3F,0xC3,0xF0,0x7F,0xF1,0xFF,0xFF,0xFC,0x7F,0xFF,0x3F,0x0F,0xFE,0x0F,0x01,0xFC,0x03,0x80, // '&' - 0xFF,0xFF,0xFF,0xFF,0xFF,0xDE,0x78, // ''' - 0x03,0x81,0xC1,0xC1,0xE0,0xF0,0xF0,0xF8,0x78,0x3C,0x3E,0x1F,0x0F,0x0F,0x87,0xC3,0xE1,0xF0,0xF8,0x7C,0x3E,0x1F,0x0F,0x83,0xC1,0xF0,0xF8,0x3C,0x1E,0x0F,0x83,0xC0,0xF0,0x78,0x1C,0x07,0x01,0x80, // '(' - 0xE0,0x70,0x1C,0x0F,0x03,0x81,0xE0,0xF8,0x3C,0x1E,0x0F,0x87,0xC1,0xE0,0xF8,0x7C,0x3E,0x1F,0x0F,0x87,0xC3,0xE1,0xF0,0xF8,0x78,0x7C,0x3E,0x1E,0x0F,0x0F,0x87,0x83,0x83,0xC1,0xC1,0xC0,0xE0,0x00, // ')' - 0x03,0x80,0x07,0x00,0x0E,0x00,0x1C,0x0E,0x38,0xFF,0x77,0xDF,0xFF,0x07,0xF0,0x07,0xC0,0x1D,0xC0,0x3B,0xC0,0xE3,0x83,0xC7,0x83,0x06,0x00, // '*' - 0x01,0xF0,0x00,0x3E,0x00,0x07,0xC0,0x00,0xF8,0x00,0x1F,0x00,0x03,0xE0,0x00,0x7C,0x07,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF0,0x1F,0x00,0x03,0xE0,0x00,0x7C,0x00,0x0F,0x80,0x01,0xF0,0x00,0x3E,0x00, // '+' - 0x7B,0xEF,0xFF,0x7C,0x71,0xCE,0x7B,0xCE,0x00, // ',' - 0x7F,0xDF,0xFF,0xFF,0xBF,0xE0, // '-' - 0x77,0xFF,0xF7,0x00, // '.' - 0x03,0x81,0xC1,0xE0,0xF0,0x70,0x78,0x3C,0x1E,0x0E,0x07,0x07,0x83,0xC1,0xC0,0xE0,0xF0,0x78,0x38,0x1C,0x1E,0x0F,0x07,0x03,0x83,0xC1,0xE0,0xE0,0x70,0x00, // '/' - 0x03,0xF0,0x03,0xFF,0x01,0xFF,0xE0,0xFF,0xFC,0x3E,0x1F,0x1F,0x03,0xE7,0xC0,0xF9,0xE0,0x1E,0xF8,0x07,0xFE,0x01,0xFF,0x80,0x7F,0xE0,0x1F,0xF8,0x07,0xFE,0x01,0xFF,0x80,0x7F,0xE0,0x1F,0xF8,0x07,0xFE,0x01,0xF7,0x80,0x79,0xF0,0x3E,0x7C,0x0F,0x8F,0x87,0xC3,0xFF,0xF0,0x7F,0xF8,0x0F,0xFC,0x00,0xFC,0x00, // '0' - 0x00,0x30,0x03,0xC0,0x3E,0x03,0xF0,0x3F,0x83,0xFC,0x7F,0xEF,0xDF,0xFC,0xFF,0xC7,0xD8,0x3E,0x01,0xF0,0x0F,0x80,0x7C,0x03,0xE0,0x1F,0x00,0xF8,0x07,0xC0,0x3E,0x01,0xF0,0x0F,0x80,0x7C,0x03,0xE0,0x1F,0x00,0xF8,0x03,0x80, // '1' - 0x03,0xF8,0x03,0xFF,0x83,0xFF,0xF0,0xFF,0xFE,0x7E,0x1F,0x9F,0x03,0xFF,0x80,0x7F,0xE0,0x1F,0xF0,0x07,0xD8,0x01,0xF0,0x00,0xF8,0x00,0x7E,0x00,0x1F,0x00,0x1F,0x80,0x0F,0xC0,0x07,0xE0,0x03,0xF0,0x01,0xF8,0x00,0xFC,0x00,0x7E,0x00,0x3F,0x00,0x1F,0x80,0x07,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xDF,0xFF,0xF0, // '2' - 0x03,0xF0,0x07,0xFF,0x03,0xFF,0xE1,0xF0,0xFC,0x78,0x1F,0x1E,0x03,0xE7,0x00,0xF8,0x80,0x3E,0x00,0x1F,0x00,0x0F,0xC0,0x7F,0xE0,0x1F,0xF0,0x07,0xFE,0x01,0xFF,0xC0,0x01,0xF8,0x00,0x3F,0x00,0x07,0xD8,0x01,0xFF,0x00,0x7F,0xE0,0x1F,0xF8,0x0F,0x9F,0x07,0xE7,0xFF,0xF0,0xFF,0xF8,0x1F,0xFC,0x01,0xFC,0x00, // '3' - 0x00,0x0F,0x00,0x00,0xFC,0x00,0x07,0xE0,0x00,0x7F,0x00,0x07,0xF8,0x00,0x7F,0xC0,0x03,0xFE,0x00,0x3D,0xF0,0x03,0xCF,0x80,0x3C,0x7C,0x03,0xE3,0xE0,0x1E,0x1F,0x01,0xE0,0xF8,0x1E,0x07,0xC1,0xF0,0x3E,0x0F,0x01,0xF0,0xFF,0xFF,0xF7,0xFF,0xFF,0xFF,0xFF,0xFE,0xFF,0xFF,0xE0,0x00,0xF8,0x00,0x07,0xC0,0x00,0x3E,0x00,0x01,0xF0,0x00,0x0F,0x80,0x00,0x38,0x00, // '4' - 0x1F,0xFF,0x8F,0xFF,0xE3,0xFF,0xF8,0xFF,0xFC,0x3C,0x00,0x0F,0x00,0x07,0xC0,0x01,0xF0,0x00,0x78,0x00,0x1E,0x7E,0x07,0xFF,0xE1,0xFF,0xFC,0xFF,0xFF,0xBF,0x07,0xE7,0x80,0xFC,0x80,0x1F,0x00,0x07,0xC0,0x01,0xF6,0x00,0x7F,0xC0,0x1F,0xF0,0x0F,0xBE,0x03,0xE7,0xC1,0xF1,0xFF,0xF8,0x1F,0xFC,0x01,0xFC,0x00, // '5' - 0x03,0xF8,0x01,0xFF,0x81,0xFF,0xF0,0x7C,0x7C,0x3C,0x0F,0x9F,0x03,0xE7,0x80,0x71,0xE0,0x00,0xF8,0x00,0x3E,0x3E,0x0F,0xBF,0xE3,0xFF,0xFC,0xFE,0x1F,0xBF,0x03,0xEF,0x80,0x7F,0xE0,0x1F,0xF8,0x07,0xFE,0x01,0xF7,0x80,0x7D,0xF0,0x1F,0x7C,0x0F,0x8F,0x87,0xE3,0xFF,0xF0,0x7F,0xFC,0x0F,0xFC,0x00,0xFC,0x00, // '6' - 0x7F,0xFF,0xBF,0xFF,0xFF,0xFF,0xFD,0xFF,0xFF,0x00,0x0F,0x80,0x07,0xC0,0x01,0xE0,0x00,0xF8,0x00,0x7C,0x00,0x1E,0x00,0x0F,0x80,0x03,0xC0,0x01,0xF0,0x00,0x78,0x00,0x3E,0x00,0x0F,0x80,0x07,0xC0,0x01,0xF0,0x00,0x7C,0x00,0x3F,0x00,0x0F,0x80,0x03,0xE0,0x00,0xF8,0x00,0x3E,0x00,0x0F,0x80,0x01,0xC0,0x00, // '7' - 0x03,0xF8,0x01,0xFF,0xC0,0x7F,0xFC,0x1F,0x0F,0xC7,0xC0,0xFC,0xF8,0x0F,0x9F,0x01,0xF3,0xE0,0x3E,0x7C,0x07,0xCF,0x80,0xF0,0xF8,0x7E,0x0F,0xFF,0x80,0xFF,0xE0,0x7F,0xFE,0x1F,0x83,0xF3,0xE0,0x3E,0xF8,0x03,0xFF,0x00,0x7F,0xE0,0x0F,0xFC,0x01,0xFF,0x80,0x3E,0xF8,0x0F,0x9F,0x83,0xF1,0xFF,0xFC,0x1F,0xFF,0x00,0x7F,0x00, // '8' - 0x03,0xF0,0x03,0xFF,0x03,0xFF,0xE0,0xFF,0xFC,0x7E,0x1F,0x1F,0x03,0xEF,0x80,0xFB,0xE0,0x1E,0xF8,0x07,0xFE,0x01,0xFF,0x80,0x7F,0xE0,0x1F,0x7C,0x0F,0xDF,0x87,0xF3,0xFF,0xFC,0x7F,0xDF,0x07,0xC7,0xC0,0x01,0xF0,0x00,0x78,0xE0,0x1E,0x78,0x0F,0x9F,0x03,0xC3,0xE3,0xE0,0xFF,0xF8,0x1F,0xF8,0x01,0xF8,0x00, // '9' - 0x77,0xFF,0xF7,0x00,0x00,0x00,0x00,0x00,0x01,0xDF,0xFF,0xDC, // ':' - 0x77,0xFF,0xF7,0x00,0x00,0x00,0x00,0x00,0x01,0xDE,0xFF,0xFE,0x73,0x9B,0xDC,0xC0, // ';' - 0x00,0x00,0x40,0x00,0x70,0x00,0xFC,0x00,0xFF,0x00,0xFF,0xC1,0xFF,0xE1,0xFF,0xC1,0xFF,0xC0,0xFF,0x80,0x3F,0x80,0x0F,0xE0,0x03,0xFE,0x00,0x7F,0xF0,0x07,0xFF,0x00,0x7F,0xF8,0x03,0xFF,0x00,0x3F,0xC0,0x03,0xF0,0x00,0x1C,0x00,0x01, // '<' - 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xC0, // '=' - 0x80,0x00,0x38,0x00,0x0F,0xC0,0x03,0xFC,0x00,0xFF,0xC0,0x1F,0xFE,0x00,0xFF,0xE0,0x0F,0xFE,0x00,0x7F,0xC0,0x07,0xF0,0x01,0xFC,0x01,0xFF,0x03,0xFF,0x83,0xFF,0x87,0xFF,0x83,0xFF,0x00,0xFF,0x00,0x3F,0x00,0x0E,0x00,0x02,0x00,0x00, // '>' - 0x03,0xF8,0x03,0xFF,0x83,0xFF,0xF0,0xFF,0xFE,0x7E,0x1F,0xBF,0x03,0xFF,0x80,0x7F,0xE0,0x1F,0x70,0x07,0xC8,0x03,0xE0,0x01,0xF8,0x00,0xFC,0x00,0x7E,0x00,0x3F,0x00,0x0F,0x80,0x03,0xC0,0x01,0xF0,0x00,0x78,0x00,0x0E,0x00,0x00,0x00,0x00,0x00,0x00,0x38,0x00,0x1F,0x00,0x07,0xC0,0x01,0xF0,0x00,0x38,0x00, // '?' - 0x00,0x07,0xFE,0x00,0x00,0x0F,0xFF,0xF0,0x00,0x0F,0xFF,0xFE,0x00,0x07,0xE0,0x0F,0xE0,0x07,0xC0,0x00,0x7C,0x03,0xE0,0x00,0x0F,0x00,0xE0,0x00,0x01,0xE0,0x70,0x1F,0x9F,0x3C,0x3C,0x1F,0xF7,0xC7,0x0E,0x0F,0xFF,0xE1,0xC7,0x07,0xE3,0xF8,0x39,0xC3,0xE0,0x7E,0x0E,0x70,0xF8,0x0F,0x83,0xB8,0x7C,0x03,0xE0,0xEE,0x1F,0x00,0xF0,0x3B,0x8F,0x80,0x3C,0x0E,0xE3,0xE0,0x1F,0x07,0xB8,0xF8,0x07,0xC1,0xCE,0x3E,0x01,0xE0,0xF3,0x8F,0x80,0xF8,0x78,0xE3,0xE0,0x3E,0x1E,0x1C,0x7C,0x1F,0x9F,0x07,0x1F,0xFF,0xFF,0x81,0xC3,0xFF,0xFF,0xC0,0x38,0x7F,0xBF,0xE0,0x0F,0x07,0x87,0xE0,0x01,0xE0,0x00,0x00,0x3C,0x3C,0x00,0x00,0x3E,0x07,0xC0,0x00,0x1F,0x00,0xFE,0x00,0x3F,0x80,0x1F,0xFF,0xFF,0x80,0x00,0xFF,0xFF,0x80,0x00,0x07,0xFF,0x00,0x00, // '@' - 0x00,0x3C,0x00,0x00,0x7E,0x00,0x00,0xFF,0x00,0x00,0xFF,0x00,0x01,0xFF,0x00,0x01,0xFF,0x80,0x01,0xEF,0x80,0x03,0xE7,0xC0,0x03,0xE7,0xC0,0x07,0xC7,0xC0,0x07,0xC3,0xE0,0x07,0xC3,0xE0,0x0F,0x81,0xF0,0x0F,0x81,0xF0,0x1F,0x81,0xF8,0x1F,0x00,0xF8,0x1F,0xFF,0xF8,0x3F,0xFF,0xFC,0x3F,0xFF,0xFC,0x7F,0xFF,0xFE,0x7C,0x00,0x7E,0x7C,0x00,0x3E,0xFC,0x00,0x3F,0xF8,0x00,0x1F,0xF8,0x00,0x1F,0x70,0x00,0x0E, // 'A' - 0x7F,0xFF,0x07,0xFF,0xFC,0x3F,0xFF,0xF1,0xFF,0xFF,0xCF,0x80,0x7F,0x7C,0x01,0xFB,0xE0,0x07,0xDF,0x00,0x3E,0xF8,0x01,0xF7,0xC0,0x1F,0x3E,0x01,0xF9,0xFF,0xFF,0x8F,0xFF,0xF0,0x7F,0xFF,0xE3,0xFF,0xFF,0x9F,0x00,0x7E,0xF8,0x01,0xFF,0xC0,0x07,0xFE,0x00,0x3F,0xF0,0x01,0xFF,0x80,0x1F,0xFC,0x01,0xFB,0xFF,0xFF,0xDF,0xFF,0xFC,0xFF,0xFF,0xC3,0xFF,0xF8,0x00, // 'B' - 0x00,0xFF,0x00,0x07,0xFF,0x80,0x3F,0xFF,0xC0,0xFF,0xFF,0xC3,0xF8,0x1F,0xC7,0xE0,0x1F,0x9F,0x80,0x1F,0x3E,0x00,0x1F,0x7C,0x00,0x3D,0xF0,0x00,0x33,0xE0,0x00,0x07,0xC0,0x00,0x0F,0x80,0x00,0x1F,0x00,0x00,0x3E,0x00,0x00,0x7C,0x00,0x04,0xF8,0x00,0x1C,0xF8,0x00,0x7D,0xF0,0x00,0xFB,0xF0,0x03,0xE3,0xF0,0x0F,0xC7,0xF0,0x3F,0x07,0xFF,0xFE,0x07,0xFF,0xF8,0x03,0xFF,0xC0,0x01,0xFE,0x00, // 'C' - 0x7F,0xFE,0x03,0xFF,0xFE,0x0F,0xFF,0xFC,0x3F,0xFF,0xF8,0xF8,0x07,0xF3,0xE0,0x07,0xEF,0x80,0x1F,0xBE,0x00,0x3E,0xF8,0x00,0xFF,0xE0,0x01,0xFF,0x80,0x07,0xFE,0x00,0x1F,0xF8,0x00,0x7F,0xE0,0x01,0xFF,0x80,0x07,0xFE,0x00,0x1F,0xF8,0x00,0x7F,0xE0,0x03,0xFF,0x80,0x0F,0xBE,0x00,0x7E,0xF8,0x01,0xFB,0xE0,0x1F,0xCF,0xFF,0xFE,0x3F,0xFF,0xF0,0xFF,0xFF,0x81,0xFF,0xF8,0x00, // 'D' - 0x7F,0xFF,0xEF,0xFF,0xFE,0xFF,0xFF,0xEF,0xFF,0xFE,0xF8,0x00,0x0F,0x80,0x00,0xF8,0x00,0x0F,0x80,0x00,0xF8,0x00,0x0F,0x80,0x00,0xFF,0xFF,0xCF,0xFF,0xFC,0xFF,0xFF,0xCF,0xFF,0xFC,0xF8,0x00,0x0F,0x80,0x00,0xF8,0x00,0x0F,0x80,0x00,0xF8,0x00,0x0F,0x80,0x00,0xF8,0x00,0x0F,0x80,0x00,0xFF,0xFF,0xEF,0xFF,0xFF,0xFF,0xFF,0xF7,0xFF,0xFE, // 'E' - 0x7F,0xFF,0xBF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFE,0xF8,0x00,0x3E,0x00,0x0F,0x80,0x03,0xE0,0x00,0xF8,0x00,0x3E,0x00,0x0F,0x80,0x03,0xFF,0xF8,0xFF,0xFF,0x3F,0xFF,0xCF,0xFF,0xE3,0xE0,0x00,0xF8,0x00,0x3E,0x00,0x0F,0x80,0x03,0xE0,0x00,0xF8,0x00,0x3E,0x00,0x0F,0x80,0x03,0xE0,0x00,0xF8,0x00,0x1C,0x00,0x00, // 'F' - 0x00,0x7F,0x80,0x03,0xFF,0xF0,0x07,0xFF,0xF8,0x1F,0xFF,0xFC,0x1F,0xC1,0xFE,0x3F,0x00,0x7E,0x7E,0x00,0x3E,0x7C,0x00,0x3E,0x7C,0x00,0x00,0xF8,0x00,0x00,0xF8,0x00,0x00,0xF8,0x00,0x00,0xF8,0x07,0xFE,0xF8,0x0F,0xFF,0xF8,0x0F,0xFF,0xF8,0x07,0xFF,0xF8,0x00,0x1F,0x7C,0x00,0x1F,0x7C,0x00,0x1F,0x7E,0x00,0x1F,0x3F,0x00,0x3F,0x1F,0xC0,0xFF,0x1F,0xFF,0xFE,0x07,0xFF,0xFC,0x03,0xFF,0xF0,0x00,0x7F,0x80, // 'G' - 0x70,0x00,0x77,0xC0,0x07,0xFE,0x00,0x3F,0xF0,0x01,0xFF,0x80,0x0F,0xFC,0x00,0x7F,0xE0,0x03,0xFF,0x00,0x1F,0xF8,0x00,0xFF,0xC0,0x07,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xE0,0x03,0xFF,0x00,0x1F,0xF8,0x00,0xFF,0xC0,0x07,0xFE,0x00,0x3F,0xF0,0x01,0xFF,0x80,0x0F,0xFC,0x00,0x7F,0xE0,0x03,0xFF,0x00,0x1F,0xF8,0x00,0xFB,0x80,0x03,0x80, // 'H' - 0x77,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFB,0x80, // 'I' - 0x00,0x07,0x00,0x07,0xC0,0x03,0xE0,0x01,0xF0,0x00,0xF8,0x00,0x7C,0x00,0x3E,0x00,0x1F,0x00,0x0F,0x80,0x07,0xC0,0x03,0xE0,0x01,0xF0,0x00,0xF8,0x00,0x7C,0x00,0x3E,0x00,0x1F,0x70,0x0F,0xFC,0x07,0xFE,0x03,0xFF,0x01,0xFF,0xC1,0xFB,0xF1,0xF9,0xFF,0xFC,0x7F,0xFC,0x1F,0xFC,0x03,0xF8,0x00, // 'J' - 0x70,0x00,0xE3,0xE0,0x07,0xCF,0x80,0x3F,0x3E,0x01,0xFC,0xF8,0x0F,0xE3,0xE0,0x7F,0x0F,0x83,0xF8,0x3E,0x1F,0xC0,0xF8,0xFE,0x03,0xE7,0xF0,0x0F,0xBF,0x80,0x3F,0xFF,0x00,0xFF,0xFC,0x03,0xFF,0xF8,0x0F,0xFB,0xF0,0x3F,0xCF,0xE0,0xFE,0x1F,0x83,0xE0,0x3F,0x0F,0x80,0xFE,0x3E,0x01,0xF8,0xF8,0x03,0xF3,0xE0,0x0F,0xEF,0x80,0x1F,0xBE,0x00,0x3F,0xF8,0x00,0xFD,0xC0,0x01,0xE0, // 'K' - 0x70,0x00,0x3E,0x00,0x0F,0x80,0x03,0xE0,0x00,0xF8,0x00,0x3E,0x00,0x0F,0x80,0x03,0xE0,0x00,0xF8,0x00,0x3E,0x00,0x0F,0x80,0x03,0xE0,0x00,0xF8,0x00,0x3E,0x00,0x0F,0x80,0x03,0xE0,0x00,0xF8,0x00,0x3E,0x00,0x0F,0x80,0x03,0xE0,0x00,0xF8,0x00,0x3E,0x00,0x0F,0xFF,0xFB,0xFF,0xFF,0xFF,0xFF,0xDF,0xFF,0xE0, // 'L' - 0x7E,0x00,0x3F,0x7F,0x80,0x3F,0xFF,0xE0,0x1F,0xFF,0xF0,0x1F,0xFF,0xF8,0x0F,0xFF,0xFC,0x07,0xFF,0xFF,0x07,0xFF,0xFF,0x83,0xFF,0xFB,0xC1,0xEF,0xFD,0xE0,0xF7,0xFE,0xF8,0xFB,0xFF,0x7C,0x7D,0xFF,0x9E,0x3C,0xFF,0xCF,0x1E,0x7F,0xE7,0xDF,0x3F,0xF3,0xEF,0x9F,0xF8,0xF7,0x8F,0xFC,0x7B,0xC7,0xFE,0x3F,0xE3,0xFF,0x1F,0xF1,0xFF,0x87,0xF0,0xFF,0xC3,0xF8,0x7F,0xE1,0xFC,0x3F,0xF0,0x7C,0x1F,0xF8,0x3E,0x0F,0xB8,0x0E,0x03,0x80, // 'M' - 0x78,0x00,0x3B,0xF0,0x01,0xFF,0xC0,0x07,0xFF,0x80,0x1F,0xFF,0x00,0x7F,0xFE,0x01,0xFF,0xF8,0x07,0xFF,0xF0,0x1F,0xFF,0xE0,0x7F,0xEF,0x81,0xFF,0x9F,0x07,0xFE,0x7E,0x1F,0xF8,0xF8,0x7F,0xE1,0xF1,0xFF,0x87,0xE7,0xFE,0x0F,0x9F,0xF8,0x1F,0x7F,0xE0,0x7F,0xFF,0x80,0xFF,0xFE,0x01,0xFF,0xF8,0x07,0xFF,0xE0,0x0F,0xFF,0x80,0x1F,0xFE,0x00,0x7F,0xF8,0x00,0xFD,0xC0,0x01,0xE0, // 'N' - 0x00,0xFF,0x00,0x01,0xFF,0xF0,0x03,0xFF,0xFE,0x03,0xFF,0xFF,0x83,0xFC,0x1F,0xE1,0xF8,0x03,0xF1,0xF8,0x00,0xFC,0xF8,0x00,0x3E,0x7C,0x00,0x1F,0x7C,0x00,0x07,0xFE,0x00,0x03,0xFF,0x00,0x01,0xFF,0x80,0x00,0xFF,0xC0,0x00,0x7F,0xE0,0x00,0x3F,0xF0,0x00,0x1F,0xFC,0x00,0x0F,0xBE,0x00,0x0F,0x9F,0x00,0x07,0xCF,0xC0,0x07,0xE3,0xF0,0x07,0xE0,0xFE,0x0F,0xF0,0x7F,0xFF,0xF0,0x1F,0xFF,0xF0,0x03,0xFF,0xE0,0x00,0x3F,0xC0,0x00, // 'O' - 0x7F,0xFC,0x1F,0xFF,0xE3,0xFF,0xFE,0x7F,0xFF,0xEF,0x80,0xFD,0xF0,0x0F,0xFE,0x00,0xFF,0xC0,0x1F,0xF8,0x03,0xFF,0x00,0x7F,0xE0,0x1F,0xFC,0x07,0xEF,0xFF,0xFD,0xFF,0xFF,0x3F,0xFF,0xC7,0xFF,0xE0,0xF8,0x00,0x1F,0x00,0x03,0xE0,0x00,0x7C,0x00,0x0F,0x80,0x01,0xF0,0x00,0x3E,0x00,0x07,0xC0,0x00,0xF8,0x00,0x0E,0x00,0x00, // 'P' - 0x00,0xFF,0x00,0x00,0xFF,0xF8,0x00,0xFF,0xFF,0x80,0x7F,0xFF,0xF0,0x3F,0xC1,0xFE,0x0F,0xC0,0x1F,0x87,0xE0,0x03,0xF1,0xF0,0x00,0x7C,0x7C,0x00,0x1F,0x3E,0x00,0x03,0xEF,0x80,0x00,0xFB,0xE0,0x00,0x3E,0xF8,0x00,0x0F,0xBE,0x00,0x03,0xEF,0x80,0x00,0xFB,0xE0,0x00,0x3E,0xF8,0x00,0x0F,0x9F,0x07,0x07,0xC7,0xC1,0xF1,0xF1,0xF8,0x3F,0xFC,0x3F,0x03,0xFE,0x07,0xF0,0x7F,0x81,0xFF,0xFF,0xC0,0x1F,0xFF,0xF8,0x03,0xFF,0xFF,0x80,0x1F,0xE3,0xF0,0x00,0x00,0x7C,0x00,0x00,0x07, // 'Q' - 0x7F,0xFF,0x07,0xFF,0xFE,0x3F,0xFF,0xF9,0xFF,0xFF,0xEF,0x80,0x3F,0xFC,0x00,0xFF,0xE0,0x03,0xFF,0x00,0x1F,0xF8,0x00,0xFF,0xC0,0x0F,0xFE,0x00,0xFD,0xFF,0xFF,0xEF,0xFF,0xFE,0x7F,0xFF,0xC3,0xFF,0xF8,0x1F,0x07,0xE0,0xF8,0x1F,0x87,0xC0,0x7E,0x3E,0x01,0xF9,0xF0,0x0F,0xCF,0x80,0x3F,0x7C,0x00,0xFF,0xE0,0x07,0xFF,0x00,0x1F,0xF8,0x00,0xFB,0x80,0x03,0xC0, // 'R' - 0x03,0xF8,0x01,0xFF,0xF0,0x3F,0xFF,0x87,0xFF,0xFC,0x7E,0x0F,0xCF,0xC0,0x7E,0xF8,0x03,0xEF,0x80,0x1E,0xFC,0x00,0xCF,0xF0,0x00,0x7F,0xF0,0x03,0xFF,0xE0,0x1F,0xFF,0x80,0x7F,0xFC,0x00,0x7F,0xE0,0x00,0x7F,0x60,0x03,0xFF,0x00,0x1F,0xF0,0x01,0xFF,0x80,0x1F,0xFC,0x03,0xFF,0xE0,0x7E,0x7F,0xFF,0xC3,0xFF,0xFC,0x0F,0xFF,0x00,0x3F,0xC0, // 'S' - 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF0,0x0F,0x80,0x00,0x7C,0x00,0x03,0xE0,0x00,0x1F,0x00,0x00,0xF8,0x00,0x07,0xC0,0x00,0x3E,0x00,0x01,0xF0,0x00,0x0F,0x80,0x00,0x7C,0x00,0x03,0xE0,0x00,0x1F,0x00,0x00,0xF8,0x00,0x07,0xC0,0x00,0x3E,0x00,0x01,0xF0,0x00,0x0F,0x80,0x00,0x7C,0x00,0x03,0xE0,0x00,0x1F,0x00,0x00,0xF8,0x00,0x03,0x80,0x00, // 'T' - 0x70,0x00,0x77,0xC0,0x07,0xFE,0x00,0x3F,0xF0,0x01,0xFF,0x80,0x0F,0xFC,0x00,0x7F,0xE0,0x03,0xFF,0x00,0x1F,0xF8,0x00,0xFF,0xC0,0x07,0xFE,0x00,0x3F,0xF0,0x01,0xFF,0x80,0x0F,0xFC,0x00,0x7F,0xE0,0x03,0xFF,0x00,0x1F,0xF8,0x00,0xFF,0xC0,0x07,0xFE,0x00,0x3F,0xF8,0x03,0xF7,0xC0,0x1F,0x3F,0x83,0xF8,0xFF,0xFF,0x83,0xFF,0xF8,0x0F,0xFF,0x80,0x1F,0xF0,0x00, // 'U' - 0x70,0x00,0x1D,0xF0,0x00,0x7F,0xE0,0x00,0xFF,0xE0,0x03,0xF7,0xC0,0x07,0xCF,0x80,0x0F,0x9F,0x80,0x3F,0x1F,0x00,0x7C,0x3F,0x00,0xF8,0x7E,0x03,0xE0,0x7C,0x07,0xC0,0xFC,0x0F,0x80,0xF8,0x3E,0x01,0xF0,0x7C,0x03,0xF0,0xF8,0x03,0xE3,0xE0,0x07,0xC7,0xC0,0x07,0xDF,0x00,0x0F,0xBE,0x00,0x1F,0x7C,0x00,0x1F,0xF0,0x00,0x3F,0xE0,0x00,0x7F,0x80,0x00,0x7F,0x00,0x00,0xFE,0x00,0x00,0x70,0x00, // 'V' - 0x70,0x03,0xC0,0x0E,0xF8,0x07,0xE0,0x1F,0xF8,0x07,0xE0,0x1F,0xF8,0x0F,0xF0,0x1F,0xF8,0x0F,0xF0,0x1F,0x7C,0x0F,0xF0,0x3E,0x7C,0x0F,0xF0,0x3E,0x7C,0x1F,0xF8,0x3E,0x7C,0x1E,0x78,0x3E,0x3E,0x1E,0x78,0x7C,0x3E,0x1E,0x78,0x7C,0x3E,0x3E,0x7C,0x7C,0x3E,0x3C,0x3C,0x7C,0x1E,0x3C,0x3C,0x78,0x1F,0x7C,0x3E,0xF8,0x1F,0x78,0x1E,0xF8,0x1F,0x78,0x1E,0xF8,0x0F,0x78,0x1E,0xF0,0x0F,0xF8,0x1F,0xF0,0x0F,0xF0,0x0F,0xF0,0x0F,0xF0,0x0F,0xF0,0x07,0xF0,0x0F,0xE0,0x07,0xF0,0x0F,0xE0,0x07,0xE0,0x07,0xE0,0x03,0xE0,0x07,0xC0,0x03,0xC0,0x03,0xC0, // 'W' - 0x38,0x00,0x73,0xE0,0x07,0x9F,0x00,0x7E,0xFC,0x07,0xE3,0xF0,0x3F,0x1F,0x83,0xF0,0x7E,0x3F,0x01,0xF9,0xF8,0x0F,0xDF,0x80,0x3F,0xF8,0x00,0xFF,0xC0,0x07,0xFC,0x00,0x1F,0xC0,0x00,0xFE,0x00,0x0F,0xF8,0x00,0xFF,0xE0,0x07,0xFF,0x80,0x7E,0xFC,0x07,0xE3,0xF0,0x7F,0x1F,0xC3,0xF0,0x7E,0x3F,0x01,0xFB,0xF0,0x07,0xFF,0x80,0x3F,0xF8,0x00,0xFB,0x80,0x03,0x80, // 'X' - 0x70,0x00,0x77,0xC0,0x07,0xFF,0x00,0x3E,0xF8,0x03,0xF7,0xE0,0x3F,0x1F,0x01,0xF0,0xFC,0x1F,0x83,0xF0,0xF8,0x0F,0x8F,0x80,0x7E,0xFC,0x01,0xF7,0xC0,0x07,0xFC,0x00,0x3F,0xE0,0x00,0xFE,0x00,0x03,0xE0,0x00,0x1F,0x00,0x00,0xF8,0x00,0x07,0xC0,0x00,0x3E,0x00,0x01,0xF0,0x00,0x0F,0x80,0x00,0x7C,0x00,0x03,0xE0,0x00,0x1F,0x00,0x00,0xF8,0x00,0x03,0x80,0x00, // 'Y' - 0x3F,0xFF,0xF8,0x7F,0xFF,0xF9,0xFF,0xFF,0xF1,0xFF,0xFF,0xE0,0x00,0x1F,0x80,0x00,0x7F,0x00,0x01,0xFC,0x00,0x07,0xF0,0x00,0x0F,0xC0,0x00,0x3F,0x00,0x00,0xFC,0x00,0x03,0xF0,0x00,0x0F,0xE0,0x00,0x3F,0x80,0x00,0x7E,0x00,0x01,0xF8,0x00,0x07,0xE0,0x00,0x1F,0x80,0x00,0x7F,0x00,0x01,0xFC,0x00,0x07,0xF0,0x00,0x0F,0xC0,0x00,0x3F,0xFF,0xFF,0x7F,0xFF,0xFF,0xFF,0xFF,0xFE,0xFF,0xFF,0xF8, // 'Z' - 0x7F,0xFF,0xFF,0xFF,0xFF,0xF8,0x3E,0x0F,0x83,0xE0,0xF8,0x3E,0x0F,0x83,0xE0,0xF8,0x3E,0x0F,0x83,0xE0,0xF8,0x3E,0x0F,0x83,0xE0,0xF8,0x3E,0x0F,0x83,0xE0,0xF8,0x3E,0x0F,0x83,0xE0,0xF8,0x3F,0xFF,0xFF,0xFF,0x7F,0xC0, // '[' - 0x70,0x3C,0x07,0x01,0xE0,0x78,0x1E,0x03,0x80,0xF0,0x3C,0x0F,0x01,0xC0,0x78,0x1E,0x07,0x80,0xE0,0x3C,0x0F,0x03,0xC0,0x70,0x1E,0x07,0x81,0xE0,0x38,0x0F,0x03,0xC0,0x60, // '\' - 0xFF,0xBF,0xFF,0xFF,0xFF,0x07,0xC1,0xF0,0x7C,0x1F,0x07,0xC1,0xF0,0x7C,0x1F,0x07,0xC1,0xF0,0x7C,0x1F,0x07,0xC1,0xF0,0x7C,0x1F,0x07,0xC1,0xF0,0x7C,0x1F,0x07,0xC1,0xF0,0x7C,0x1F,0x07,0xFF,0xFF,0xFF,0xFF,0xFF,0x80, // ']' - 0x03,0xE0,0x01,0xF0,0x01,0xFC,0x00,0xFE,0x00,0x7F,0x80,0x7B,0xC0,0x3D,0xF0,0x3E,0xF8,0x1E,0x3C,0x1F,0x1F,0x0F,0x07,0x8F,0x83,0xE7,0xC1,0xF7,0xC0,0x7C, // '^' - 0xFF,0xFF,0xFF,0xFF,0xF0, // '_' - 0x0E,0x1B,0x11,0x1B,0x0E, // '`' Changed into a degree symbol - 0x03,0xFC,0x03,0xFF,0xE0,0xFF,0xFE,0x3F,0x07,0xC7,0xC0,0x7C,0xF0,0x0F,0x9C,0x01,0xF0,0x00,0xFE,0x01,0xFF,0xC3,0xFF,0xF8,0xFF,0x9F,0x3F,0x03,0xEF,0x80,0x7D,0xF0,0x0F,0xBE,0x03,0xF7,0xE1,0xFE,0x7F,0xFF,0xE7,0xFE,0x7C,0x3E,0x07,0x00, // 'a' - 0x70,0x00,0x1F,0x00,0x03,0xE0,0x00,0x7C,0x00,0x0F,0x80,0x01,0xF0,0x00,0x3E,0x00,0x07,0xC7,0xE0,0xF9,0xFF,0x1F,0x7F,0xF3,0xFF,0xFF,0x7F,0x87,0xEF,0xE0,0x7D,0xF8,0x0F,0xFF,0x00,0xFF,0xC0,0x1F,0xF8,0x03,0xFF,0x00,0x7F,0xE0,0x0F,0xFE,0x03,0xFF,0xC0,0x7D,0xFE,0x1F,0xBF,0xFF,0xE7,0xDF,0xFC,0xF9,0xFF,0x0E,0x1F,0x80, // 'b' - 0x03,0xF8,0x03,0xFF,0x81,0xFF,0xF0,0xFF,0xFE,0x7F,0x0F,0xDF,0x01,0xFF,0xC0,0x3F,0xE0,0x04,0xF8,0x00,0x3E,0x00,0x0F,0x80,0x03,0xE0,0x07,0xFC,0x03,0xDF,0x01,0xF7,0xF0,0xFC,0xFF,0xFE,0x1F,0xFF,0x83,0xFF,0x80,0x3F,0x80, // 'c' - 0x00,0x01,0xC0,0x00,0x7C,0x00,0x0F,0x80,0x01,0xF0,0x00,0x3E,0x00,0x07,0xC0,0x00,0xF8,0x3F,0x1F,0x1F,0xF3,0xE7,0xFF,0x7D,0xFF,0xFF,0xBF,0x0F,0xF7,0xC0,0xFF,0xF8,0x0F,0xFE,0x01,0xFF,0xC0,0x1F,0xF8,0x03,0xFF,0x00,0x7F,0xE0,0x1F,0xFE,0x03,0xF7,0xC0,0x7E,0xFC,0x3F,0xCF,0xFF,0xF9,0xFF,0xDF,0x1F,0xF3,0xE0,0xF8,0x38, // 'd' - 0x03,0xF8,0x03,0xFF,0x81,0xFF,0xF0,0xF8,0x7E,0x7C,0x07,0x9F,0x01,0xFF,0x80,0x7F,0xE0,0x0F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFB,0xE0,0x00,0xF8,0x00,0x1F,0x00,0x77,0xC0,0x3C,0xFC,0x3F,0x1F,0xFF,0x83,0xFF,0xC0,0x3F,0x80, // 'e' - 0x01,0xF8,0x0F,0xF8,0x1F,0xF8,0x7F,0xE0,0xF8,0x01,0xF0,0x03,0xE0,0x3F,0xF8,0x7F,0xF9,0xFF,0xF1,0xFF,0xC0,0x7C,0x00,0xF8,0x01,0xF0,0x03,0xE0,0x07,0xC0,0x0F,0x80,0x1F,0x00,0x3E,0x00,0x7C,0x00,0xF8,0x01,0xF0,0x03,0xE0,0x07,0xC0,0x0F,0x80,0x0E,0x00, // 'f' - 0x07,0xE1,0xC3,0xFE,0x7C,0xFF,0xEF,0xBF,0xFF,0xF7,0xE1,0xFE,0xF8,0x0F,0xFF,0x01,0xFF,0xC0,0x1F,0xF8,0x03,0xFF,0x00,0x7F,0xE0,0x0F,0xFC,0x01,0xFF,0xC0,0x7E,0xF8,0x0F,0xDF,0x87,0xF9,0xFF,0xFF,0x3F,0xFB,0xE3,0xFE,0x7C,0x1F,0x8F,0x80,0x01,0xF7,0x00,0x7E,0xF0,0x0F,0x9F,0x83,0xF3,0xFF,0xFC,0x1F,0xFF,0x00,0xFF,0x80, // 'g' - 0x70,0x00,0x3E,0x00,0x0F,0x80,0x03,0xE0,0x00,0xF8,0x00,0x3E,0x00,0x0F,0x80,0x03,0xE3,0xF0,0xF9,0xFF,0x3E,0xFF,0xEF,0xFF,0xFB,0xF8,0x3F,0xFC,0x0F,0xFE,0x01,0xFF,0x80,0x7F,0xE0,0x1F,0xF8,0x07,0xFE,0x01,0xFF,0x80,0x7F,0xE0,0x1F,0xF8,0x07,0xFE,0x01,0xFF,0x80,0x7F,0xE0,0x1F,0xF8,0x07,0xDC,0x00,0xE0, // 'h' - 0x77,0xFF,0xF7,0x00,0x0E,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFB,0x80, // 'i' - 0x03,0x81,0xF0,0x7C,0x1F,0x03,0x80,0x00,0x00,0x0E,0x07,0xC1,0xF0,0x7C,0x1F,0x07,0xC1,0xF0,0x7C,0x1F,0x07,0xC1,0xF0,0x7C,0x1F,0x07,0xC1,0xF0,0x7C,0x1F,0x07,0xC1,0xF0,0x7C,0x1F,0x07,0xFF,0xFF,0xFB,0xFE,0x7E,0x00, // 'j' - 0x70,0x00,0x7C,0x00,0x3E,0x00,0x1F,0x00,0x0F,0x80,0x07,0xC0,0x03,0xE0,0x01,0xF0,0x1C,0xF8,0x1E,0x7C,0x1F,0x3E,0x1F,0x9F,0x1F,0x8F,0x9F,0x07,0xDF,0x03,0xFF,0x81,0xFF,0xE0,0xFF,0xF0,0x7F,0x7C,0x3F,0x1F,0x1F,0x0F,0x8F,0x83,0xE7,0xC1,0xFB,0xE0,0x7D,0xF0,0x1F,0xF8,0x0F,0xB8,0x03,0x80, // 'k' - 0x77,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFB,0x80, // 'l' - 0x70,0xFC,0x0F,0x8F,0x3F,0xE3,0xFC,0xFF,0xFF,0x7F,0xEF,0xFF,0xFF,0xFE,0xFE,0x1F,0xE3,0xFF,0xC0,0xFC,0x1F,0xF8,0x0F,0x81,0xFF,0x80,0xF8,0x1F,0xF8,0x0F,0x81,0xFF,0x80,0xF8,0x1F,0xF8,0x0F,0x81,0xFF,0x80,0xF8,0x1F,0xF8,0x0F,0x81,0xFF,0x80,0xF8,0x1F,0xF8,0x0F,0x81,0xFF,0x80,0xF8,0x1F,0xF8,0x0F,0x81,0xFF,0x80,0xF8,0x1F,0x70,0x07,0x00,0xE0, // 'm' - 0x70,0xFC,0x3C,0x7F,0xCF,0xBF,0xFB,0xFF,0xFE,0xFE,0x0F,0xFF,0x03,0xFF,0xC0,0x7F,0xE0,0x1F,0xF8,0x07,0xFE,0x01,0xFF,0x80,0x7F,0xE0,0x1F,0xF8,0x07,0xFE,0x01,0xFF,0x80,0x7F,0xE0,0x1F,0xF8,0x07,0xFE,0x01,0xF7,0x00,0x38, // 'n' - 0x01,0xF8,0x00,0xFF,0xF0,0x1F,0xFF,0x83,0xFF,0xFC,0x7F,0x0F,0xE7,0xC0,0x3E,0xFC,0x03,0xFF,0x80,0x1F,0xF8,0x01,0xFF,0x80,0x1F,0xF8,0x01,0xFF,0x80,0x1F,0xFC,0x03,0xF7,0xC0,0x3E,0x7F,0x0F,0xE3,0xFF,0xFC,0x1F,0xFF,0x80,0xFF,0xF0,0x01,0xF8,0x00, // 'o' - 0x70,0xFC,0x1F,0x3F,0xE3,0xEF,0xFE,0x7F,0xFF,0xCF,0xF0,0xFD,0xF8,0x0F,0xBF,0x01,0xFF,0xC0,0x1F,0xF8,0x03,0xFF,0x00,0x7F,0xE0,0x0F,0xFC,0x01,0xFF,0xC0,0x7F,0xF8,0x0F,0xBF,0xC3,0xF7,0xFF,0xFC,0xFB,0xFF,0x9F,0x3F,0xE3,0xE3,0xF0,0x7C,0x00,0x0F,0x80,0x01,0xF0,0x00,0x3E,0x00,0x07,0xC0,0x00,0xF8,0x00,0x0E,0x00,0x00, // 'p' - 0x07,0xE1,0xC3,0xFE,0x7C,0xFF,0xEF,0x9F,0xFF,0xF7,0xE1,0xFE,0xF8,0x0F,0xFF,0x01,0xFF,0xC0,0x1F,0xF8,0x03,0xFF,0x00,0x7F,0xE0,0x0F,0xFC,0x01,0xFF,0xC0,0x7E,0xF8,0x0F,0xDF,0x87,0xF9,0xFF,0xFF,0x3F,0xFB,0xE3,0xFE,0x7C,0x1F,0x0F,0x80,0x01,0xF0,0x00,0x3E,0x00,0x07,0xC0,0x00,0xF8,0x00,0x1F,0x00,0x03,0xE0,0x00,0x38, // 'q' - 0x71,0xE7,0xDF,0xFF,0xFF,0xFF,0xFF,0xE1,0x7E,0x03,0xF0,0x1F,0x00,0xF8,0x07,0xC0,0x3E,0x01,0xF0,0x0F,0x80,0x7C,0x03,0xE0,0x1F,0x00,0xF8,0x07,0xC0,0x1C,0x00, // 'r' - 0x0F,0xF0,0x1F,0xFE,0x1F,0xFF,0x8F,0x87,0xEF,0x81,0xF7,0xC0,0x7B,0xF0,0x00,0xFF,0x80,0x3F,0xF8,0x0F,0xFF,0x00,0xFF,0xC0,0x0F,0xF6,0x01,0xFF,0x80,0x7F,0xE0,0x3F,0xF8,0x3E,0x7F,0xFF,0x1F,0xFF,0x03,0xFC,0x00, // 's' - 0x0E,0x00,0xF8,0x07,0xC0,0x3E,0x01,0xF0,0x0F,0x80,0x7C,0x0F,0xFC,0xFF,0xF7,0xFF,0x9F,0xF8,0x3E,0x01,0xF0,0x0F,0x80,0x7C,0x03,0xE0,0x1F,0x00,0xF8,0x07,0xC0,0x3E,0x01,0xF0,0x0F,0x80,0x7F,0xE3,0xFF,0x0F,0xF8,0x3F,0x80, // 't' - 0x70,0x03,0xBE,0x01,0xFF,0x80,0x7F,0xE0,0x1F,0xF8,0x07,0xFE,0x01,0xFF,0x80,0x7F,0xE0,0x1F,0xF8,0x07,0xFE,0x01,0xFF,0x80,0x7F,0xE0,0x1F,0xF8,0x07,0xFF,0x03,0xFF,0xE3,0xFD,0xFF,0xFF,0x7F,0xF7,0xCF,0xF8,0xF0,0xF8,0x38, // 'u' - 0x70,0x03,0xBC,0x00,0xFF,0x80,0x7F,0xE0,0x1F,0x7C,0x07,0x9F,0x03,0xE3,0xC0,0xF0,0xF8,0x7C,0x3E,0x1E,0x07,0x87,0x81,0xF3,0xE0,0x3C,0xF0,0x0F,0x3C,0x01,0xFE,0x00,0x7F,0x80,0x1F,0xE0,0x03,0xF0,0x00,0xFC,0x00,0x1E,0x00, // 'v' - 0x70,0x0F,0x00,0xEF,0x00,0xF0,0x0F,0xF8,0x1F,0x81,0xFF,0x81,0xF8,0x1F,0x78,0x1F,0x81,0xE7,0x83,0xFC,0x1E,0x7C,0x3F,0xC3,0xE3,0xC3,0xFC,0x3C,0x3C,0x79,0xE3,0xC3,0xE7,0x9E,0x7C,0x1E,0x79,0xE7,0x81,0xE7,0x0E,0x78,0x0E,0xF0,0xF7,0x00,0xFF,0x0F,0xF0,0x0F,0xE0,0x7F,0x00,0x7E,0x07,0xE0,0x07,0xE0,0x7E,0x00,0x7C,0x03,0xE0,0x03,0xC0,0x3C,0x00, // 'w' - 0x70,0x07,0x3C,0x07,0xBF,0x07,0xEF,0xC7,0xE3,0xE3,0xE0,0xFB,0xE0,0x7F,0xE0,0x1F,0xF0,0x07,0xF0,0x03,0xF8,0x03,0xFE,0x01,0xFF,0x01,0xF7,0xC1,0xF1,0xF1,0xF8,0xFC,0xF8,0x3E,0xF8,0x0F,0xFC,0x07,0xDC,0x01,0xC0, // 'x' - 0x38,0x01,0xCF,0x80,0x3D,0xF0,0x0F,0xBE,0x01,0xF3,0xE0,0x3C,0x7C,0x0F,0x87,0x81,0xE0,0xF8,0x7C,0x1F,0x0F,0x81,0xE1,0xE0,0x3E,0x7C,0x03,0xCF,0x00,0x7D,0xE0,0x07,0xFC,0x00,0xFF,0x00,0x1F,0xE0,0x01,0xF8,0x00,0x3F,0x00,0x07,0xE0,0x00,0xF8,0x00,0x1F,0x00,0x07,0xC0,0x1F,0xF8,0x07,0xFE,0x00,0x7F,0xC0,0x07,0xE0,0x00, // 'y' - 0x7F,0xFF,0x1F,0xFF,0xE7,0xFF,0xF9,0xFF,0xFE,0x00,0x3F,0x00,0x1F,0x80,0x07,0xC0,0x03,0xE0,0x01,0xF0,0x00,0xF8,0x00,0x7E,0x00,0x3F,0x00,0x1F,0x80,0x0F,0xC0,0x07,0xE0,0x03,0xFF,0xFE,0xFF,0xFF,0xFF,0xFF,0xF7,0xFF,0xF8, // 'z' - 0x01,0xF0,0x1F,0xC1,0xFE,0x1F,0xE0,0xFC,0x07,0xC0,0x3E,0x01,0xF0,0x0F,0x80,0x7C,0x03,0xE0,0x1F,0x01,0xF8,0x0F,0x81,0xFC,0x1F,0xC0,0xF8,0x07,0xF0,0x1F,0xC0,0x3E,0x01,0xF8,0x07,0xC0,0x3E,0x01,0xF0,0x0F,0x80,0x7C,0x03,0xE0,0x1F,0x00,0xFC,0x07,0xF8,0x1F,0xE0,0x7F,0x01,0xF0, // '{' - 0x6F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x60, // '|' - 0x7C,0x07,0xF0,0x3F,0xC0,0xFF,0x01,0xF8,0x07,0xC0,0x3E,0x01,0xF0,0x0F,0x80,0x7C,0x03,0xE0,0x1F,0x00,0xFC,0x03,0xE0,0x1F,0xC0,0x7F,0x00,0xF8,0x1F,0xC1,0xFC,0x0F,0x80,0xFC,0x07,0xC0,0x3E,0x01,0xF0,0x0F,0x80,0x7C,0x03,0xE0,0x1F,0x01,0xF8,0x3F,0xC3,0xFC,0x1F,0xC0,0x7C,0x00 // '}' -}; -const GFXglyph ArialRoundedMTBold_36Glyphs[] PROGMEM = { -// bitmapOffset, width, height, xAdvance, xOffset, yOffset - { 0, 1, 1, 10, 0, 0 }, // ' ' - { 1, 5, 26, 13, 4, -26 }, // '!' - { 18, 14, 9, 18, 2, -26 }, // '"' - { 34, 19, 26, 21, 0, -26 }, // '#' - { 96, 19, 37, 22, 1, -30 }, // '$' - { 184, 29, 27, 32, 1, -26 }, // '%' - { 282, 25, 26, 28, 2, -26 }, // '&' - { 364, 6, 9, 10, 1, -26 }, // ''' - { 371, 9, 33, 14, 2, -26 }, // '(' - { 409, 9, 33, 14, 2, -26 }, // ')' - { 447, 15, 14, 17, 0, -28 }, // '*' - { 474, 19, 18, 22, 1, -22 }, // '+' - { 517, 6, 11, 12, 3, -5 }, // ',' - { 526, 11, 4, 13, 1, -11 }, // '-' - { 532, 5, 5, 12, 3, -5 }, // '.' - { 536, 9, 26, 11, 1, -26 }, // '/' - { 566, 18, 26, 22, 2, -26 }, // '0' - { 625, 13, 26, 22, 2, -26 }, // '1' - { 668, 18, 26, 22, 2, -26 }, // '2' - { 727, 18, 26, 22, 2, -26 }, // '3' - { 786, 21, 26, 22, 0, -26 }, // '4' - { 855, 18, 26, 22, 2, -26 }, // '5' - { 914, 18, 26, 22, 2, -26 }, // '6' - { 973, 18, 26, 22, 3, -26 }, // '7' - { 1032, 19, 26, 22, 1, -26 }, // '8' - { 1094, 18, 26, 22, 1, -26 }, // '9' - { 1153, 5, 19, 12, 3, -19 }, // ':' - { 1165, 5, 25, 12, 3, -19 }, // ';' - { 1181, 18, 20, 22, 1, -23 }, // '<' - { 1226, 18, 13, 22, 2, -19 }, // '=' - { 1256, 18, 20, 22, 1, -23 }, // '>' - { 1301, 18, 26, 22, 1, -26 }, // '?' - { 1360, 34, 33, 36, 1, -26 }, // '@' - { 1501, 24, 26, 27, 1, -26 }, // 'A' - { 1579, 21, 26, 27, 3, -26 }, // 'B' - { 1648, 23, 26, 28, 2, -26 }, // 'C' - { 1723, 22, 26, 28, 3, -26 }, // 'D' - { 1795, 20, 26, 25, 3, -26 }, // 'E' - { 1860, 18, 26, 23, 3, -26 }, // 'F' - { 1919, 24, 26, 30, 2, -26 }, // 'G' - { 1997, 21, 26, 28, 3, -26 }, // 'H' - { 2066, 5, 26, 12, 3, -26 }, // 'I' - { 2083, 17, 26, 22, 1, -26 }, // 'J' - { 2139, 22, 26, 28, 3, -26 }, // 'K' - { 2211, 18, 26, 23, 3, -26 }, // 'L' - { 2270, 25, 26, 31, 3, -26 }, // 'M' - { 2352, 22, 26, 28, 3, -26 }, // 'N' - { 2424, 25, 26, 30, 2, -26 }, // 'O' - { 2506, 19, 26, 25, 3, -26 }, // 'P' - { 2568, 26, 28, 30, 2, -26 }, // 'Q' - { 2659, 21, 26, 27, 3, -26 }, // 'R' - { 2728, 20, 26, 25, 2, -26 }, // 'S' - { 2793, 21, 26, 24, 1, -26 }, // 'T' - { 2862, 21, 26, 28, 3, -26 }, // 'U' - { 2931, 23, 26, 26, 1, -26 }, // 'V' - { 3006, 32, 26, 35, 1, -26 }, // 'W' - { 3110, 21, 26, 23, 0, -26 }, // 'X' - { 3179, 21, 26, 24, 1, -26 }, // 'Y' - { 3248, 23, 26, 24, 0, -26 }, // 'Z' - { 3323, 10, 33, 14, 2, -26 }, // '[' - { 3365, 10, 26, 11, 0, -26 }, // '\' - { 3398, 10, 33, 14, 0, -26 }, // ']' - { 3440, 17, 14, 22, 2, -26 }, // '^' - { 3470, 18, 2, 19, 0, 3 }, // '_' - { 3475, 8, 5, 10, 0, -26 }, // '`' Changed to degree symbol - { 3480, 19, 19, 22, 1, -19 }, // 'a' - { 3526, 19, 26, 24, 2, -26 }, // 'b' - { 3588, 18, 19, 22, 1, -19 }, // 'c' - { 3631, 19, 26, 24, 1, -26 }, // 'd' - { 3693, 18, 19, 22, 2, -19 }, // 'e' - { 3736, 15, 26, 13, -1, -26 }, // 'f' - { 3785, 19, 26, 24, 1, -19 }, // 'g' - { 3847, 18, 26, 23, 2, -26 }, // 'h' - { 3906, 5, 26, 11, 2, -26 }, // 'i' - { 3923, 10, 33, 11, -3, -26 }, // 'j' - { 3965, 17, 26, 22, 3, -26 }, // 'k' - { 4021, 5, 26, 11, 2, -26 }, // 'l' - { 4038, 28, 19, 33, 2, -19 }, // 'm' - { 4105, 18, 19, 23, 2, -19 }, // 'n' - { 4148, 20, 19, 23, 1, -19 }, // 'o' - { 4196, 19, 26, 24, 2, -19 }, // 'p' - { 4258, 19, 26, 24, 1, -19 }, // 'q' - { 4320, 13, 19, 17, 2, -19 }, // 'r' - { 4351, 17, 19, 21, 1, -19 }, // 's' - { 4392, 13, 26, 14, 0, -26 }, // 't' - { 4435, 18, 19, 23, 2, -19 }, // 'u' - { 4478, 18, 19, 21, 1, -19 }, // 'v' - { 4521, 28, 19, 30, 1, -19 }, // 'w' - { 4588, 17, 19, 20, 1, -19 }, // 'x' - { 4629, 19, 26, 21, 0, -19 }, // 'y' - { 4691, 18, 19, 20, 1, -19 }, // 'z' - { 4734, 13, 33, 15, 1, -26 }, // '{' - { 4788, 4, 33, 11, 3, -26 }, // '|' - { 4805, 13, 33, 15, 0, -26 } // '}' character 0x7D -}; -const GFXfont ArialRoundedMTBold_36 PROGMEM = { // Last character bug fixed 0x7E to 0x7D -(uint8_t *)ArialRoundedMTBold_36Bitmaps,(GFXglyph *)ArialRoundedMTBold_36Glyphs,0x20, 0x7D, 43}; - diff --git a/examples/320 x 240/weather-station/GfxUi.cpp b/examples/320 x 240/weather-station/GfxUi.cpp deleted file mode 100644 index c425abb..0000000 --- a/examples/320 x 240/weather-station/GfxUi.cpp +++ /dev/null @@ -1,342 +0,0 @@ -/**The MIT License (MIT) -Copyright (c) 2015 by Daniel Eichhorn -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 COPYRIGHT 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. -See more at http://blog.squix.ch - -Adapted by Bodmer to use the faster TFT_eSPI library: -https://github.com/Bodmer/TFT_eSPI - -Bodmer: Functions no longer needed weeded out, Jpeg decoder functions added -Bodmer: drawBMP() updated to buffer in and out pixels and use screen CGRAM rotation for faster bottom up drawing (now ~2x faster) -*/ - -#include "GfxUi.h" - -GfxUi::GfxUi(TFT_eSPI *tft) { - _tft = tft; -} - -void GfxUi::drawProgressBar(uint16_t x0, uint16_t y0, uint16_t w, uint16_t h, uint8_t percentage, uint16_t frameColor, uint16_t barColor) { - if (percentage == 0) { - _tft->fillRoundRect(x0, y0, w, h, 3, TFT_BLACK); - } - uint8_t margin = 2; - uint16_t barHeight = h - 2 * margin; - uint16_t barWidth = w - 2 * margin; - _tft->drawRoundRect(x0, y0, w, h, 3, frameColor); - _tft->fillRect(x0 + margin, y0 + margin, barWidth * percentage / 100.0, barHeight, barColor); -} - -// This drawBMP function contains code from: -// https://github.com/adafruit/Adafruit_ILI9341/blob/master/examples/spitftbitmap/spitftbitmap.ino -// Here is Bodmer's version: this uses the ILI9341 CGRAM coordinate rotation features inside the display and -// buffers both file and TFT pixel blocks, it typically runs about 2x faster for bottom up encoded BMP images - -//void GfxUi::drawBMP(String filename, uint8_t x, uint16_t y, boolean flip) { // Alernative for caller control of flip -void GfxUi::drawBmp(String filename, uint8_t x, uint16_t y) { - // Flips the TFT internal SGRAM coords to draw bottom up BMP images faster, in this application it can be fixed - boolean flip = 1; - - if ((x >= _tft->width()) || (y >= _tft->height())) return; - - fs::File bmpFile; - int16_t bmpWidth, bmpHeight; // Image W+H in pixels - uint32_t bmpImageoffset; // Start address of image data in file - uint32_t rowSize; // Not always = bmpWidth; may have padding - uint8_t sdbuffer[3 * BUFFPIXEL]; // file read pixel buffer (8 bits each R+G+B per pixel) - uint16_t tftbuffer[BUFFPIXEL]; // TFT pixel out buffer (16-bit per pixel) - uint8_t rgb_ptr = sizeof(sdbuffer); // read 24 bit RGB pixel data buffer pointer (8 bit so BUFF_SIZE must be less than 86) - boolean goodBmp = false; // Flag set to true on valid header parse - int16_t w, h, row, col; // to store width, height, row and column - uint8_t rotation; // to restore rotation - uint8_t tft_ptr = 0; // TFT 16 bit 565 format pixel data buffer pointer - - // Check file exists and open it - Serial.println(filename); - if ( !(bmpFile = SPIFFS.open(filename, "r")) ) { - Serial.println(F(" File not found")); // Can comment out if not needed - return; - } - - // Parse BMP header to get the information we need - if (read16(bmpFile) == 0x4D42) { // BMP file start signature check - read32(bmpFile); // Dummy read to throw away and move on - read32(bmpFile); // Read & ignore creator bytes - bmpImageoffset = read32(bmpFile); // Start of image data - read32(bmpFile); // Dummy read to throw away and move on - bmpWidth = read32(bmpFile); // Image width - bmpHeight = read32(bmpFile); // Image height - - // Only proceed if we pass a bitmap file check - // Number of image planes -- must be '1', depth 24 and 0 (uncompressed format) - if ((read16(bmpFile) == 1) && (read16(bmpFile) == 24) && (read32(bmpFile) == 0)) { - goodBmp = true; // Supported BMP format - // BMP rows are padded (if needed) to 4-byte boundary - rowSize = (bmpWidth * 3 + 3) & ~3; - // Crop area to be loaded - w = bmpWidth; - h = bmpHeight; - - // We might need to alter rotation to avoid tedious file pointer manipulation - // Save the current value so we can restore it later - rotation = _tft->getRotation(); - // Use TFT SGRAM coord rotation if flip is set for 25% faster rendering (new rotations 4-7 supported by library) - if (flip) _tft->setRotation((rotation + (flip<<2)) % 8); // Value 0-3 mapped to 4-7 - - // Calculate new y plot coordinate if we are flipping - switch (rotation) { - case 0: - if (flip) y = _tft->height() - y - h; break; - case 1: - y = _tft->height() - y - h; break; - break; - case 2: - if (flip) y = _tft->height() - y - h; break; - break; - case 3: - y = _tft->height() - y - h; break; - break; - } - - // Set TFT address window to image bounds - // Currently, image will not draw or will be corrputed if it does not fit - // TODO -> efficient clipping, but I don't need it to be idiot proof ;-) - _tft->setAddrWindow(x, y, x + w - 1, y + h - 1); - - // Finally we are ready to send rows of pixels, writing like this avoids slow 32 bit multiply in 8 bit processors - for (uint32_t pos = bmpImageoffset; pos < bmpImageoffset + h * rowSize ; pos += rowSize) { - // Seek if we need to on boundaries and arrange to dump buffer and start again - if (bmpFile.position() != pos) { - bmpFile.seek(pos, fs::SeekSet); - rgb_ptr = sizeof(sdbuffer); - //Serial.println("Seeking in file >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"); - } - - // Fill the pixel buffer and plot - for (col = 0; col < w; col++) { // For each column... - // Time to read more pixel data? - if (rgb_ptr >= sizeof(sdbuffer)) { - // Push tft buffer to the display - if (tft_ptr) { - // Here we are sending a uint16_t array to the function - _tft->pushColors(tftbuffer, tft_ptr); - tft_ptr = 0; // tft_ptr and rgb_ptr are not always in sync... - } - // Finally reading bytes from SD Card - bmpFile.read(sdbuffer, sizeof(sdbuffer)); - rgb_ptr = 0; // Set buffer index to start - } - // Convert pixel from BMP 8+8+8 format to TFT compatible 16 bit word - // Blue 5 bits, green 6 bits and red 5 bits (16 bits total) - // Is is a long line but it is faster than calling a library fn for this - tftbuffer[tft_ptr] = (sdbuffer[rgb_ptr++] >> 3) ; - tftbuffer[tft_ptr] |= ((sdbuffer[rgb_ptr++] & 0xFC) << 3); - tftbuffer[tft_ptr] |= ((sdbuffer[rgb_ptr++] & 0xF8) << 8); - tft_ptr++; - } // Next row - } // All rows done - - // Write any partially full buffer to TFT - if (tft_ptr) _tft->pushColors(tftbuffer, tft_ptr); - - } // End of bitmap access - } // End of bitmap file check - - bmpFile.close(); - - 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. -// BMP data is stored little-endian, Arduino is little-endian too. -// May need to reverse subscript order if porting elsewhere. - -uint16_t GfxUi::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 GfxUi::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; -} - -/*==================================================================================== - This sketch support functions to render the Jpeg images. - - Created by Bodmer 15th Jan 2017 - ==================================================================================*/ - -// Return the minimum of two values a and b -#define minimum(a,b) (((a) < (b)) ? (a) : (b)) - -#define USE_SPI_BUFFER // Comment out to use slower 16 bit pushColor() - -//==================================================================================== -// Opens the image file and prime the Jpeg decoder -//==================================================================================== -void GfxUi::drawJpeg(const char *filename, int xpos, int ypos) { - - Serial.println("==========================="); - Serial.print("Drawing file: "); Serial.println(filename); - Serial.println("==========================="); - - // Open the named file (the Jpeg decoder library will close it after rendering image) - fs::File jpegFile = SPIFFS.open( filename, "r"); // File handle reference for SPIFFS - // 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; - } - - // Use one of the three following methods to initialise the decoder: - //boolean decoded = JpegDec.decodeFsFile(jpegFile); // Pass a SPIFFS file handle to the decoder, - //boolean decoded = JpegDec.decodeSdFile(jpegFile); // or pass the SD file handle to the decoder, - boolean decoded = JpegDec.decodeFsFile(filename); // or pass the filename (leading / distinguishes SPIFFS files) - // Note: the filename can be a String or character array type - 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!"); - } -} - -//==================================================================================== -// Decode and render the Jpeg image onto the TFT screen -//==================================================================================== -void GfxUi::jpegRender(int xpos, int ypos) { - - // retrieve infomration about the image - 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; - - // 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 = minimum(mcu_w, max_x % mcu_w); - uint32_t min_h = minimum(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; - - // read each MCU block until there are no more -#ifdef USE_SPI_BUFFER - while( JpegDec.readSwappedBytes()){ // Swap byte order so the SPI buffer can be used -#else - while ( JpegDec.read()) { // Normal byte order read -#endif - // save a pointer to the image block - pImg = JpegDec.pImage; - - // calculate where the image block should be drawn on the screen - 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++; - } - } - } - - // 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(); - - } - - // calculate how long it took to draw the image - drawTime = millis() - drawTime; // Calculate the time it took - - // print the results to the serial port - Serial.print ("Total render time was : "); Serial.print(drawTime); Serial.println(" ms"); - Serial.println("====================================="); - -} - -//==================================================================================== -// Print information decoded from the Jpeg image -//==================================================================================== -void GfxUi::jpegInfo() { - - Serial.println("==============="); - 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(""); - -} -//==================================================================================== diff --git a/examples/320 x 240/weather-station/GfxUi.h b/examples/320 x 240/weather-station/GfxUi.h deleted file mode 100644 index f381129..0000000 --- a/examples/320 x 240/weather-station/GfxUi.h +++ /dev/null @@ -1,60 +0,0 @@ -/**The MIT License (MIT) -Copyright (c) 2015 by Daniel Eichhorn -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 COPYRIGHT 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. - -See more at http://blog.squix.ch - -Adapted by Bodmer to use the faster TFT_eSPI library: -https://github.com/Bodmer/TFT_eSPI - -*/ - - -#include // Hardware-specific library - -#define FS_NO_GLOBALS // Avoid conflict with SD library File type definition -#include - -// JPEG decoder library -#include - -#ifndef _GFX_UI_H -#define _GFX_UI_H - -// Maximum of 85 for BUFFPIXEL as 3 x this value is stored in an 8 bit variable! -// 32 is an efficient size for SPIFFS due to SPI hardware pipeline buffer size -// A larger value of 80 is better for SD cards -#define BUFFPIXEL 32 - -class GfxUi { - public: - GfxUi(TFT_eSPI * tft); - void drawBmp(String filename, uint8_t x, uint16_t y); - void drawProgressBar(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint8_t percentage, uint16_t frameColor, uint16_t barColor); - void jpegInfo(); - void drawJpeg(const char *filename, int xpos, int ypos); - void jpegRender(int xpos, int ypos); - - private: - TFT_eSPI * _tft; - uint16_t read16(fs::File &f); - uint32_t read32(fs::File &f); - -}; - -#endif - diff --git a/examples/320 x 240/weather-station/SPIFFS_Support.ino b/examples/320 x 240/weather-station/SPIFFS_Support.ino deleted file mode 100644 index 37eefe1..0000000 --- a/examples/320 x 240/weather-station/SPIFFS_Support.ino +++ /dev/null @@ -1,42 +0,0 @@ -/*==================================================================================== - This sketch contains support functions for the ESP6266 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:"); - - fs::Dir dir = SPIFFS.openDir("/"); // Root directory - String line = "====================================="; - uint32_t totalBytes = 0; - - 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 - while (spaces--) Serial.print(" "); - fs::File f = dir.openFile("r"); - Serial.print(f.size()); Serial.println(" bytes"); - totalBytes += f.size(); - } - Serial.println(); Serial.print("Total = "); - int spaces = 25 - 8; // Tabulate nicely - while (spaces--) Serial.print(" "); - Serial.print(totalBytes); Serial.println(" bytes"); - - Serial.println(line); - Serial.println(); - delay(1000); -} -//==================================================================================== - diff --git a/examples/320 x 240/weather-station/WebResource.cpp b/examples/320 x 240/weather-station/WebResource.cpp deleted file mode 100644 index 2020fd1..0000000 --- a/examples/320 x 240/weather-station/WebResource.cpp +++ /dev/null @@ -1,105 +0,0 @@ -/**The MIT License (MIT) -Copyright (c) 2015 by Daniel Eichhorn -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 COPYRIGHT 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. -See more at http://blog.squix.ch -*/ - -#include "WebResource.h" - -WebResource::WebResource(){ - -} - -void WebResource::downloadFile(String url, String filename) { - downloadFile(url, filename, nullptr); -} - -void WebResource::downloadFile(String url, String filename, ProgressCallback progressCallback) { - - if (SPIFFS.exists(filename) == true) { - Serial.println("Found " + filename); - return; - } - else Serial.println("Downloading " + filename + " from " + url); - - // wait for WiFi connection - if((_wifiMulti.run() == WL_CONNECTED)) { - HTTPClient http; - - Serial.print("[HTTP] begin...\n"); - - // configure server and url - http.begin(url); - - Serial.print("[HTTP] GET...\n"); - // start connection and send HTTP header - int httpCode = http.GET(); - if(httpCode > 0) { - //SPIFFS.remove(filename); - fs::File f = SPIFFS.open(filename, "w+"); - if (!f) { - Serial.println("file open failed"); - return; - } - // HTTP header has been send and Server response header has been handled - Serial.printf("[HTTP] GET... code: %d\n", httpCode); - - // file found at server - if(httpCode == HTTP_CODE_OK) { - - // get length of document (is -1 when Server sends no Content-Length header) - int total = http.getSize(); - int len = total; - progressCallback(filename, 0,total); - // create buffer for read - uint8_t buff[128] = { 0 }; - - // get tcp stream - WiFiClient * stream = http.getStreamPtr(); - - // read all data from server - while(http.connected() && (len > 0 || len == -1)) { - // get available data size - size_t size = stream->available(); - - if(size) { - // read up to 128 byte - int c = stream->readBytes(buff, ((size > sizeof(buff)) ? sizeof(buff) : size)); - - // write it to Serial - f.write(buff, c); - - if(len > 0) { - len -= c; - } - progressCallback(filename, total - len,total); - } - delay(1); - } - - Serial.println(); - Serial.print("[HTTP] connection closed or file end.\n"); - - } - f.close(); - } else { - Serial.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str()); - } - - http.end(); - } -} diff --git a/examples/320 x 240/weather-station/WebResource.h b/examples/320 x 240/weather-station/WebResource.h deleted file mode 100644 index acff084..0000000 --- a/examples/320 x 240/weather-station/WebResource.h +++ /dev/null @@ -1,46 +0,0 @@ -/**The MIT License (MIT) -Copyright (c) 2015 by Daniel Eichhorn -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 COPYRIGHT 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. -See more at http://blog.squix.ch -*/ - -#define FS_NO_GLOBALS // Avoid conflict with SD library File type definition -#include -#include -#include -#include -#include - -#ifndef _WEBRESOURCE_H -#define _WEBRESOURCE_H - -typedef void (*ProgressCallback)(String fileName, int16_t bytesDownloaded, int16_t bytesTotal); - -class WebResource { - public: - WebResource(); - void downloadFile(String url, String filename, ProgressCallback progressCallback); - void downloadFile(String url, String filename); - - - private: - ESP8266WiFiMulti _wifiMulti; - -}; - -#endif - diff --git a/examples/320 x 240/weather-station/settings.h b/examples/320 x 240/weather-station/settings.h deleted file mode 100644 index d4d97c2..0000000 --- a/examples/320 x 240/weather-station/settings.h +++ /dev/null @@ -1,72 +0,0 @@ -/**The MIT License (MIT) -Copyright (c) 2015 by Daniel Eichhorn -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 COPYRIGHT 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. -See more at http://blog.squix.ch - -Adapted by Bodmer to use the faster TFT_ILI9341_ESP library: -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_eSPI library - -// TimeClient settings -const float UTC_OFFSET = 1; - -// 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 = "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 - -// For a list of countries, states and cities see https://www.wunderground.com/about/faq/international_cities.asp -const String WUNDERGROUND_COUNTRY = "Peru"; // UK, US etc -const String WUNDERGROUND_CITY = "Base_Naval"; // City, "London", "FL/Boca_Raton" for Boca Raton in Florida (State/City) etc. Use underscore_for spaces) - -// Windspeed conversion, use 1 pair of #defines. To do: investigate a more convenient method <<<<<<<<<<<<<<<<<<<<< -//#define WIND_SPEED_SCALING 1.0 // mph -//#define WIND_SPEED_UNITS " mph" - -//#define WIND_SPEED_SCALING 0.868976 // mph to knots -//#define WIND_SPEED_UNITS " kn" - -#define WIND_SPEED_SCALING 1.60934 // mph to kph -#define WIND_SPEED_UNITS " kph" - -//Thingspeak Settings - not used, no need to populate this at the moment -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"}; - -/*************************** - * End Settings - **************************/ diff --git a/examples/320 x 240/weather-station/weather-station.ino b/examples/320 x 240/weather-station/weather-station.ino deleted file mode 100644 index ab3cfad..0000000 --- a/examples/320 x 240/weather-station/weather-station.ino +++ /dev/null @@ -1,595 +0,0 @@ -/**The MIT License (MIT) - Copyright (c) 2015 by Daniel Eichhorn - 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. - See more at http://blog.squix.ch - - Adapted by Bodmer to use the faster TFT_eSPI library: - https://github.com/Bodmer/TFT_eSPI - - Plus: - Minor changes to text placement and auto-blanking out old text with background colour padding - Moon phase text added - Forecast text lines are automatically split onto two lines at a central space (some are long!) - Time is printed with colons aligned to tidy display - Min and max forecast temperatures spaced out - The ` character has been changed to a degree symbol in the 36 point font - 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 - -#include - -#include -#include // Hardware-specific library - -// Additional UI functions -#include "GfxUi.h" - -// Fonts created by http://oleddisplay.squix.ch/ -#include "ArialRoundedMTBold_14.h" -#include "ArialRoundedMTBold_36.h" - -// Download helper -#include "WebResource.h" - -#include -#include -#include -#include -#include - -// Helps with connecting to internet -#include - -// check settings.h for adapting to your needs -#include "settings.h" -#include -#include -#include "TimeClient.h" - -// HOSTNAME for OTA update -#define HOSTNAME "ESP8266-OTA-" - -/***************************** - Important: see settings.h to configure your settings!!! - * ***************************/ - -TFT_eSPI tft = TFT_eSPI(); // Invoke custom library - -boolean booted = true; - -GfxUi ui = GfxUi(&tft); - -WebResource webResource; -TimeClient timeClient(UTC_OFFSET); - -// Set to false, if you prefere imperial/inches, Fahrenheit -WundergroundClient wunderground(IS_METRIC); - -//declaring prototypes -void configModeCallback (WiFiManager *myWiFiManager); -void downloadCallback(String filename, int16_t bytesDownloaded, int16_t bytesTotal); -ProgressCallback _downloadCallback = downloadCallback; -void downloadResources(); -void updateData(); -void drawProgress(uint8_t percentage, String text); -void drawTime(); -void drawCurrentWeather(); -void drawForecast(); -void drawForecastDetail(uint16_t x, uint16_t y, uint8_t dayIndex); -String getMeteoconIcon(String iconText); -void drawAstronomy(); -void drawSeparator(uint16_t y); - -long lastDownloadUpdate = millis(); - -void setup() { -#ifdef SERIAL_MESSAGES - Serial.begin(250000); -#endif - tft.begin(); - tft.fillScreen(TFT_BLACK); - - tft.setFreeFont(&ArialRoundedMTBold_14); - tft.setTextDatum(BC_DATUM); - tft.setTextColor(TFT_DARKGREY, TFT_BLACK); - tft.drawString("Original by: blog.squix.org", 120, 240); - tft.drawString("Adapted by: Bodmer", 120, 260); - tft.setTextColor(TFT_ORANGE, TFT_BLACK); - - SPIFFS.begin(); - //listFiles(); - //Uncomment if you want to erase SPIFFS and update all internet resources, this takes some time! - //tft.drawString("Formatting SPIFFS, so wait!", 120, 200); SPIFFS.format(); - - if (SPIFFS.exists("/WU.jpg") == true) ui.drawJpeg("/WU.jpg", 0, 10); - if (SPIFFS.exists("/Earth.jpg") == true) ui.drawJpeg("/Earth.jpg", 0, 320-56); // Image is 56 pixels high - delay(1000); - tft.drawString("Connecting to WiFi", 120, 200); - tft.setTextPadding(240); // Pad next drawString() text to full width to over-write old text - - //WiFiManager - //Local intialization. Once its business is done, there is no need to keep it around - WiFiManager wifiManager; - // Uncomment for testing wifi manager - //wifiManager.resetSettings(); - wifiManager.setAPCallback(configModeCallback); - - //or use this for auto generated name ESP + ChipID - wifiManager.autoConnect(); - - //Manual Wifi - //WiFi.begin(WIFI_SSID, WIFI_PWD); - - // OTA Setup - String hostname(HOSTNAME); - hostname += String(ESP.getChipId(), HEX); - WiFi.hostname(hostname); - ArduinoOTA.setHostname((const char *)hostname.c_str()); - ArduinoOTA.begin(); - - // download images from the net. If images already exist don't download - tft.drawString("Downloading to SPIFFS...", 120, 200); - tft.drawString(" ", 120, 240); // Clear line - tft.drawString(" ", 120, 260); // Clear line - downloadResources(); - //listFiles(); - tft.setTextDatum(BC_DATUM); - tft.setTextPadding(240); // Pad next drawString() text to full width to over-write old text - tft.drawString(" ", 120, 200); // Clear line above using set padding width - tft.drawString("Fetching weather data...", 120, 200); - //delay(500); - - // load the weather information - updateData(); -} - -long lastDrew = 0; -void loop() { - // Handle OTA update requests - ArduinoOTA.handle(); - - // Check if we should update the clock - if (millis() - lastDrew > 30000 && wunderground.getSeconds() == "00") { - drawTime(); - lastDrew = millis(); - } - - // Check if we should update weather information - if (millis() - lastDownloadUpdate > 1000 * UPDATE_INTERVAL_SECS) { - updateData(); - lastDownloadUpdate = millis(); - } -} - -// Called if WiFi has not been configured yet -void configModeCallback (WiFiManager *myWiFiManager) { - tft.setTextDatum(BC_DATUM); - tft.setFreeFont(&ArialRoundedMTBold_14); - tft.setTextColor(TFT_ORANGE); - tft.drawString("Wifi Manager", 120, 28); - tft.drawString("Please connect to AP", 120, 42); - tft.setTextColor(TFT_WHITE); - tft.drawString(myWiFiManager->getConfigPortalSSID(), 120, 56); - tft.setTextColor(TFT_ORANGE); - tft.drawString("To setup Wifi Configuration", 120, 70); -} - -// callback called during download of files. Updates progress bar -void downloadCallback(String filename, int16_t bytesDownloaded, int16_t bytesTotal) { - Serial.println(String(bytesDownloaded) + " / " + String(bytesTotal)); - - tft.setTextDatum(BC_DATUM); - tft.setTextColor(TFT_ORANGE, TFT_BLACK); - tft.setTextPadding(240); - - int percentage = 100 * bytesDownloaded / bytesTotal; - if (percentage == 0) { - tft.drawString(filename, 120, 220); - } - if (percentage % 5 == 0) { - tft.setTextDatum(TC_DATUM); - tft.setTextPadding(tft.textWidth(" 888% ")); - tft.drawString(String(percentage) + "%", 120, 245); - ui.drawProgressBar(10, 225, 240 - 20, 15, percentage, TFT_WHITE, TFT_BLUE); - } - -} - -// Download the bitmaps -void downloadResources() { - // tft.fillScreen(TFT_BLACK); - tft.setFreeFont(&ArialRoundedMTBold_14); - char id[5]; - - // Download WU graphic jpeg first and display it, then the Earth view - webResource.downloadFile((String)"http://i.imgur.com/njl1pMj.jpg", (String)"/WU.jpg", _downloadCallback); - if (SPIFFS.exists("/WU.jpg") == true) ui.drawJpeg("/WU.jpg", 0, 10); - - webResource.downloadFile((String)"http://i.imgur.com/v4eTLCC.jpg", (String)"/Earth.jpg", _downloadCallback); - if (SPIFFS.exists("/Earth.jpg") == true) ui.drawJpeg("/Earth.jpg", 0, 320-56); - - //webResource.downloadFile((String)"http://i.imgur.com/IY57GSv.jpg", (String)"/Horizon.jpg", _downloadCallback); - //if (SPIFFS.exists("/Horizon.jpg") == true) ui.drawJpeg("/Horizon.jpg", 0, 320-160); - - //webResource.downloadFile((String)"http://i.imgur.com/jZptbtY.jpg", (String)"/Rainbow.jpg", _downloadCallback); - //if (SPIFFS.exists("/Rainbow.jpg") == true) ui.drawJpeg("/Rainbow.jpg", 0, 0); - - for (int i = 0; i < 19; i++) { - sprintf(id, "%02d", i); - webResource.downloadFile("http://www.squix.org/blog/wunderground/" + wundergroundIcons[i] + ".bmp", wundergroundIcons[i] + ".bmp", _downloadCallback); - } - for (int i = 0; i < 19; i++) { - sprintf(id, "%02d", i); - webResource.downloadFile("http://www.squix.org/blog/wunderground/mini/" + wundergroundIcons[i] + ".bmp", "/mini/" + wundergroundIcons[i] + ".bmp", _downloadCallback); - } - for (int i = 0; i < 24; i++) { - webResource.downloadFile("http://www.squix.org/blog/moonphase_L" + String(i) + ".bmp", "/moon" + String(i) + ".bmp", _downloadCallback); - } -} - -// Update the internet based information and update screen -void updateData() { - // booted = true; // Test only - // booted = false; // Test only - - if (booted) ui.drawJpeg("/WU.jpg", 0, 10); // May have already drawn this but it does not take long - else tft.drawCircle(22, 22, 18, TFT_DARKGREY); // Outer ring - optional - - if (booted) drawProgress(20, "Updating time..."); - else fillSegment(22, 22, 0, (int) (20 * 3.6), 16, TFT_NAVY); - - timeClient.updateTime(); - if (booted) drawProgress(50, "Updating conditions..."); - else fillSegment(22, 22, 0, (int) (50 * 3.6), 16, TFT_NAVY); - - wunderground.updateConditions(WUNDERGRROUND_API_KEY, WUNDERGRROUND_LANGUAGE, WUNDERGROUND_COUNTRY, WUNDERGROUND_CITY); - if (booted) drawProgress(70, "Updating forecasts..."); - else fillSegment(22, 22, 0, (int) (70 * 3.6), 16, TFT_NAVY); - - wunderground.updateForecast(WUNDERGRROUND_API_KEY, WUNDERGRROUND_LANGUAGE, WUNDERGROUND_COUNTRY, WUNDERGROUND_CITY); - if (booted) drawProgress(90, "Updating astronomy..."); - else fillSegment(22, 22, 0, (int) (90 * 3.6), 16, TFT_NAVY); - - wunderground.updateAstronomy(WUNDERGRROUND_API_KEY, WUNDERGRROUND_LANGUAGE, WUNDERGROUND_COUNTRY, WUNDERGROUND_CITY); - // lastUpdate = timeClient.getFormattedTime(); - // readyForWeatherUpdate = false; - if (booted) drawProgress(100, "Done..."); - else fillSegment(22, 22, 0, 360, 16, TFT_NAVY); - - if (booted) delay(2000); - - if (booted) tft.fillScreen(TFT_BLACK); - else fillSegment(22, 22, 0, 360, 22, TFT_BLACK); - - //tft.fillScreen(TFT_CYAN); // For text padding and update graphics over-write checking only - drawTime(); - drawCurrentWeather(); - drawForecast(); - drawAstronomy(); - booted = false; -} - -// Progress bar helper -void drawProgress(uint8_t percentage, String text) { - tft.setFreeFont(&ArialRoundedMTBold_14); - - tft.setTextDatum(BC_DATUM); - tft.setTextColor(TFT_ORANGE, TFT_BLACK); - tft.setTextPadding(240); - tft.drawString(text, 120, 220); - - ui.drawProgressBar(10, 225, 240 - 20, 15, percentage, TFT_WHITE, TFT_BLUE); - - tft.setTextPadding(0); -} - -// draws the clock -void drawTime() { - - tft.setFreeFont(&ArialRoundedMTBold_36); - - String timeNow = timeClient.getHours() + ":" + timeClient.getMinutes(); - - tft.setTextDatum(BC_DATUM); - tft.setTextColor(TFT_YELLOW, TFT_BLACK); - tft.setTextPadding(tft.textWidth(" 44:44 ")); // String width + margin - tft.drawString(timeNow, 120, 53); - - tft.setFreeFont(&ArialRoundedMTBold_14); - - String date = wunderground.getDate(); - - tft.setTextDatum(BC_DATUM); - tft.setTextColor(TFT_WHITE, TFT_BLACK); - tft.setTextPadding(tft.textWidth(" Ddd, 44 Mmm 4444 ")); // String width + margin - tft.drawString(date, 120, 16); - - drawSeparator(54); - - tft.setTextPadding(0); -} - -// draws current weather information -void drawCurrentWeather() { - // Weather Icon - String weatherIcon = getMeteoconIcon(wunderground.getTodayIcon()); - //uint32_t dt = millis(); - ui.drawBmp(weatherIcon + ".bmp", 0, 59); - //Serial.print("Icon draw time = "); Serial.println(millis()-dt); - - // Weather Text - - String weatherText = wunderground.getWeatherText(); - //weatherText = "Heavy Thunderstorms with Small Hail"; // Test line splitting with longest(?) string - - tft.setFreeFont(&ArialRoundedMTBold_14); - - tft.setTextDatum(BR_DATUM); - tft.setTextColor(TFT_ORANGE, TFT_BLACK); - - int splitPoint = 0; - int xpos = 230; - splitPoint = splitIndex(weatherText); - if (splitPoint > 16) xpos = 235; - - tft.setTextPadding(tft.textWidth("Heavy Thunderstorms")); // Max anticipated string width - if (splitPoint) tft.drawString(weatherText.substring(0, splitPoint), xpos, 72); - tft.setTextPadding(tft.textWidth(" with Small Hail")); // Max anticipated string width + margin - tft.drawString(weatherText.substring(splitPoint), xpos, 87); - - tft.setFreeFont(&ArialRoundedMTBold_36); - - tft.setTextDatum(TR_DATUM); - tft.setTextColor(TFT_ORANGE, TFT_BLACK); - - // Font ASCII code 96 (0x60) modified to make "`" a degree symbol - tft.setTextPadding(tft.textWidth("-88`")); // Max width of vales - - weatherText = wunderground.getCurrentTemp(); - if (weatherText.indexOf(".")) weatherText = weatherText.substring(0, weatherText.indexOf(".")); // Make it integer temperature - if (weatherText == "") weatherText = "?"; // Handle null return - tft.drawString(weatherText + "`", 221, 100); - - tft.setFreeFont(&ArialRoundedMTBold_14); - - tft.setTextDatum(TL_DATUM); - tft.setTextPadding(0); - if (IS_METRIC) tft.drawString("C ", 221, 100); - else tft.drawString("F ", 221, 100); - - //tft.drawString(wunderground.getPressure(), 180, 30); - - weatherText = ""; //wunderground.getWindDir() + " "; - weatherText += String((int)(wunderground.getWindSpeed().toInt() * WIND_SPEED_SCALING)) + WIND_SPEED_UNITS; - - tft.setTextDatum(TC_DATUM); - tft.setTextPadding(tft.textWidth(" 888 mph")); // Max string length? - tft.drawString(weatherText, 128, 136); - - weatherText = wunderground.getPressure(); - - tft.setTextDatum(TR_DATUM); - tft.setTextPadding(tft.textWidth(" 8888mb")); // Max string length? - tft.drawString(weatherText, 230, 136); - - weatherText = wunderground.getWindDir(); - - int windAngle = 0; - String compassCardinal = ""; - switch (weatherText.length()) { - case 1: - compassCardinal = "N E S W "; // Not used, see default below - windAngle = 90 * compassCardinal.indexOf(weatherText) / 2; - break; - case 2: - compassCardinal = "NE SE SW NW"; - windAngle = 45 + 90 * compassCardinal.indexOf(weatherText) / 3; - break; - case 3: - compassCardinal = "NNE ENE ESE SSE SSW WSW WNW NNW"; - windAngle = 22 + 45 * compassCardinal.indexOf(weatherText) / 4; // 22 should be 22.5 but accuracy is not needed! - break; - default: - if (weatherText == "Variable") windAngle = -1; - else { - // v23456v23456v23456v23456 character ruler - compassCardinal = "North East South West"; // Possible strings - windAngle = 90 * compassCardinal.indexOf(weatherText) / 6; - } - break; - } - - tft.fillCircle(128, 110, 23, TFT_BLACK); // Erase old plot, radius + 1 to delete stray pixels - tft.drawCircle(128, 110, 22, TFT_DARKGREY); // Outer ring - optional - if ( windAngle >= 0 ) fillSegment(128, 110, windAngle - 15, 30, 22, TFT_GREEN); // Might replace this with a bigger rotating arrow - tft.drawCircle(128, 110, 6, TFT_RED); - - drawSeparator(153); - - tft.setTextDatum(TL_DATUM); // Reset datum to normal - tft.setTextPadding(0); // Reset padding width to none -} - -// draws the three forecast columns -void drawForecast() { - drawForecastDetail(10, 171, 0); - drawForecastDetail(95, 171, 2); - drawForecastDetail(180, 171, 4); - drawSeparator(171 + 69); -} - -// helper for the forecast columns -void drawForecastDetail(uint16_t x, uint16_t y, uint8_t dayIndex) { - tft.setFreeFont(&ArialRoundedMTBold_14); - - String day = wunderground.getForecastTitle(dayIndex).substring(0, 3); - day.toUpperCase(); - - tft.setTextDatum(BC_DATUM); - - tft.setTextColor(TFT_ORANGE, TFT_BLACK); - tft.setTextPadding(tft.textWidth("WWW")); - tft.drawString(day, x + 25, y); - - tft.setTextColor(TFT_WHITE, TFT_BLACK); - tft.setTextPadding(tft.textWidth("-88 -88")); - tft.drawString(wunderground.getForecastHighTemp(dayIndex) + " " + wunderground.getForecastLowTemp(dayIndex), x + 25, y + 14); - - String weatherIcon = getMeteoconIcon(wunderground.getForecastIcon(dayIndex)); - ui.drawBmp("/mini/" + weatherIcon + ".bmp", x, y + 15); - - tft.setTextPadding(0); // Reset padding width to none -} - -// draw moonphase and sunrise/set and moonrise/set -void drawAstronomy() { - tft.setFreeFont(&ArialRoundedMTBold_14); - - tft.setTextDatum(BC_DATUM); - tft.setTextColor(TFT_ORANGE, TFT_BLACK); - tft.setTextPadding(tft.textWidth(" Waxing Crescent ")); - tft.drawString(wunderground.getMoonPhase(), 120, 260 - 2); - - int moonAgeImage = 24 * wunderground.getMoonAge().toInt() / 30.0; - ui.drawBmp("/moon" + String(moonAgeImage) + ".bmp", 120 - 30, 260); - - tft.setTextDatum(BC_DATUM); - tft.setTextColor(TFT_ORANGE, TFT_BLACK); - tft.setTextPadding(0); // Reset padding width to none - tft.drawString("Sun", 40, 280); - - tft.setTextDatum(BR_DATUM); - tft.setTextColor(TFT_WHITE, TFT_BLACK); - tft.setTextPadding(tft.textWidth(" 88:88 ")); - int dt = rightOffset(wunderground.getSunriseTime(), ":"); // Draw relative to colon to them aligned - tft.drawString(wunderground.getSunriseTime(), 40 + dt, 300); - - dt = rightOffset(wunderground.getSunsetTime(), ":"); - tft.drawString(wunderground.getSunsetTime(), 40 + dt, 315); - - tft.setTextDatum(BC_DATUM); - tft.setTextColor(TFT_ORANGE, TFT_BLACK); - tft.setTextPadding(0); // Reset padding width to none - tft.drawString("Moon", 200, 280); - - tft.setTextDatum(BR_DATUM); - tft.setTextColor(TFT_WHITE, TFT_BLACK); - tft.setTextPadding(tft.textWidth(" 88:88 ")); - dt = rightOffset(wunderground.getMoonriseTime(), ":"); // Draw relative to colon to them aligned - tft.drawString(wunderground.getMoonriseTime(), 200 + dt, 300); - - dt = rightOffset(wunderground.getMoonsetTime(), ":"); - tft.drawString(wunderground.getMoonsetTime(), 200 + dt, 315); - - tft.setTextPadding(0); // Reset padding width to none -} - -// Helper function, should be part of the weather station library and should disappear soon -String getMeteoconIcon(String iconText) { - if (iconText == "F") return "chanceflurries"; - if (iconText == "Q") return "chancerain"; - if (iconText == "W") return "chancesleet"; - if (iconText == "V") return "chancesnow"; - if (iconText == "S") return "chancetstorms"; - if (iconText == "B") return "clear"; - if (iconText == "Y") return "cloudy"; - if (iconText == "F") return "flurries"; - if (iconText == "M") return "fog"; - if (iconText == "E") return "hazy"; - if (iconText == "Y") return "mostlycloudy"; - if (iconText == "H") return "mostlysunny"; - if (iconText == "H") return "partlycloudy"; - if (iconText == "J") return "partlysunny"; - if (iconText == "W") return "sleet"; - if (iconText == "R") return "rain"; - if (iconText == "W") return "snow"; - if (iconText == "B") return "sunny"; - if (iconText == "0") return "tstorms"; - - - return "unknown"; -} - -// if you want separators, uncomment the tft-line -void drawSeparator(uint16_t y) { - tft.drawFastHLine(10, y, 240 - 2 * 10, 0x4228); -} - -// determine the "space" split point in a long string -int splitIndex(String text) -{ - int index = 0; - while ( (text.indexOf(' ', index) >= 0) && ( index <= text.length() / 2 ) ) { - index = text.indexOf(' ', index) + 1; - } - if (index) index--; - return index; -} - -// Calculate coord delta from start of text String to start of sub String contained within that text -// Can be used to vertically right align text so for example a colon ":" in the time value is always -// plotted at same point on the screen irrespective of different proportional character widths, -// could also be used to align decimal points for neat formatting -int rightOffset(String text, String sub) -{ - int index = text.indexOf(sub); - return tft.textWidth(text.substring(index)); -} - -// Calculate coord delta from start of text String to start of sub String contained within that text -// Can be used to vertically left align text so for example a colon ":" in the time value is always -// plotted at same point on the screen irrespective of different proportional character widths, -// could also be used to align decimal points for neat formatting -int leftOffset(String text, String sub) -{ - int index = text.indexOf(sub); - return tft.textWidth(text.substring(0, index)); -} - -// Draw a segment of a circle, centred on x,y with defined start_angle and subtended sub_angle -// Angles are defined in a clockwise direction with 0 at top -// Segment has radius r and it is plotted in defined colour -// Can be used for pie charts etc, in this sketch it is used for wind direction -#define DEG2RAD 0.0174532925 // Degrees to Radians conversion factor -#define INC 2 // Minimum segment subtended angle and plotting angle increment (in degrees) -void fillSegment(int x, int y, int start_angle, int sub_angle, int r, unsigned int colour) -{ - // Calculate first pair of coordinates for segment start - float sx = cos((start_angle - 90) * DEG2RAD); - float sy = sin((start_angle - 90) * DEG2RAD); - uint16_t x1 = sx * r + x; - uint16_t y1 = sy * r + y; - - // Draw colour blocks every INC degrees - for (int i = start_angle; i < start_angle + sub_angle; i += INC) { - - // Calculate pair of coordinates for segment end - int x2 = cos((i + 1 - 90) * DEG2RAD) * r + x; - int y2 = sin((i + 1 - 90) * DEG2RAD) * r + y; - - tft.fillTriangle(x1, y1, x2, y2, x, y, colour); - - // Copy segment end to sgement start for next segment - x1 = x2; - y1 = y2; - } -} From 11ef56d48c3c51e1b724684d2880ee5b62a06b20 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Wed, 23 Jan 2019 20:55:04 +0000 Subject: [PATCH 240/287] Test slower DC and CS lines for a user --- TFT_eSPI.cpp | 27 +++++++++++++++++---------- TFT_eSPI.h | 30 +++++++++++++++++++++--------- 2 files changed, 38 insertions(+), 19 deletions(-) diff --git a/TFT_eSPI.cpp b/TFT_eSPI.cpp index 4df354b..e4cf7ad 100644 --- a/TFT_eSPI.cpp +++ b/TFT_eSPI.cpp @@ -345,6 +345,7 @@ void TFT_eSPI::init(uint8_t tc) #endif spi_end(); + delay(150); // Wait for reset to complete spi_begin(); @@ -506,13 +507,15 @@ void TFT_eSPI::spiwrite(uint8_t c) ***************************************************************************************/ void TFT_eSPI::writecommand(uint8_t c) { - DC_C; - CS_L; - + //DC_C; + //CS_L; + digitalWrite(TFT_DC, LOW); + digitalWrite(TFT_CS, LOW); tft_Write_8(c); - - CS_H; - DC_D; + digitalWrite(TFT_CS, HIGH); + digitalWrite(TFT_DC, HIGH); + //CS_H; + //DC_D; } @@ -522,11 +525,11 @@ void TFT_eSPI::writecommand(uint8_t c) ***************************************************************************************/ void TFT_eSPI::writedata(uint8_t d) { - CS_L; - + //CS_L; + digitalWrite(TFT_CS, LOW); tft_Write_8(d); - - CS_H; + digitalWrite(TFT_CS, HIGH); + //CS_H; } @@ -5084,6 +5087,7 @@ void writeBlock(uint16_t color, uint32_t repeat) ***************************************************************************************/ void TFT_eSPI::getSetup(setup_t &tft_settings) { +// tft_settings.version is set in header file #if defined (ESP8266) tft_settings.esp = 8266; @@ -5105,6 +5109,9 @@ void TFT_eSPI::getSetup(setup_t &tft_settings) #else tft_settings.serial = true; tft_settings.tft_spi_freq = SPI_FREQUENCY/100000; + #ifdef SPI_READ_FREQUENCY + tft_settings.tft_rd_freq = SPI_READ_FREQUENCY/100000; + #endif #endif #if defined(TFT_SPI_OVERLAP) diff --git a/TFT_eSPI.h b/TFT_eSPI.h index 5e471e9..01a1351 100644 --- a/TFT_eSPI.h +++ b/TFT_eSPI.h @@ -15,6 +15,8 @@ #ifndef _TFT_eSPIH_ #define _TFT_eSPIH_ +#define TFT_ESPI_VERSION "1.4.0" + //#define ESP32 //Just used to test ESP32 options // Include header file that defines the fonts loaded, the TFT drivers @@ -142,8 +144,8 @@ #define DC_D GPIO.out1_w1tc.val = (1 << (TFT_DC - 32)); \ GPIO.out1_w1ts.val = (1 << (TFT_DC - 32)) #else - #define DC_C GPIO.out1_w1tc.val = (1 << (TFT_DC - 32)) - #define DC_D GPIO.out1_w1ts.val = (1 << (TFT_DC - 32)) + #define DC_C GPIO.out1_w1tc.val = (1 << (TFT_DC - 32));GPIO.out1_w1tc.val = (1 << (TFT_DC - 32)) + #define DC_D GPIO.out1_w1ts.val = (1 << (TFT_DC - 32));GPIO.out1_w1ts.val = (1 << (TFT_DC - 32)) #endif #else #if TFT_DC >= 0 @@ -153,8 +155,8 @@ #define DC_D GPIO.out_w1tc = (1 << TFT_DC); \ GPIO.out_w1ts = (1 << TFT_DC) #else - #define DC_C GPIO.out_w1tc = (1 << TFT_DC) - #define DC_D GPIO.out_w1ts = (1 << TFT_DC) + #define DC_C GPIO.out_w1tc = (1 << TFT_DC);GPIO.out_w1tc = (1 << TFT_DC) + #define DC_D GPIO.out_w1ts = (1 << TFT_DC);GPIO.out_w1ts = (1 << TFT_DC) #endif #else #define DC_C @@ -191,8 +193,8 @@ #define CS_H GPIO.out1_w1tc.val = (1 << (TFT_CS - 32)); \ GPIO.out1_w1ts.val = (1 << (TFT_CS - 32)) #else - #define CS_L GPIO.out1_w1tc.val = (1 << (TFT_CS - 32)) - #define CS_H GPIO.out1_w1ts.val = (1 << (TFT_CS - 32)) + #define CS_L GPIO.out1_w1tc.val = (1 << (TFT_CS - 32));GPIO.out1_w1tc.val = (1 << (TFT_CS - 32)) + #define CS_H GPIO.out1_w1ts.val = (1 << (TFT_CS - 32));GPIO.out1_w1ts.val = (1 << (TFT_CS - 32)) #endif #else #if TFT_CS >= 0 @@ -200,8 +202,8 @@ #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 GPIO.out_w1tc = (1 << TFT_CS) - #define CS_H GPIO.out_w1ts = (1 << TFT_CS) + #define CS_L GPIO.out_w1tc = (1 << TFT_CS);GPIO.out_w1tc = (1 << TFT_CS) + #define CS_H GPIO.out_w1ts = (1 << TFT_CS);GPIO.out_w1ts = (1 << TFT_CS) #endif #else #define CS_L @@ -223,7 +225,7 @@ #define CS_L_DC_C GPIO.out_w1tc = ((1 << TFT_CS) | (1 << TFT_DC)); \ GPIO.out_w1tc = ((1 << TFT_CS) | (1 << TFT_DC)) #else - #define CS_L_DC_C GPIO.out_w1tc = ((1 << TFT_CS) | (1 << TFT_DC)) + #define CS_L_DC_C GPIO.out_w1tc = ((1 << TFT_CS) | (1 << TFT_DC)); GPIO.out_w1tc = ((1 << TFT_CS) | (1 << TFT_DC)) #endif #else #define CS_L_DC_C CS_L; DC_C @@ -525,11 +527,20 @@ swap_coord(T& a, T& b) { T t = a; a = b; b = t; } // by calling getSetup(), zero impact on code size unless used, mainly for diagnostics typedef struct { +String version = TFT_ESPI_VERSION; int16_t esp; uint8_t trans; uint8_t serial; uint8_t overlap; +#if defined (ESP32) + #if defined (USE_HSPI_PORT) + uint8_t port = HSPI; + #else + uint8_t port = VSPI; + #endif +#endif + uint16_t tft_driver; // Hexadecimal code uint16_t tft_width; // Rotation 0 width and height uint16_t tft_height; @@ -565,6 +576,7 @@ int8_t pin_tft_d7; int8_t pin_tch_cs; int16_t tft_spi_freq; +int16_t tft_rd_freq; int16_t tch_spi_freq; } setup_t; From e8a0024054b2e0a0c00264790da5dfaf0c260599 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Fri, 25 Jan 2019 13:03:28 +0000 Subject: [PATCH 241/287] Fix issue #295 --- TFT_eSPI.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/TFT_eSPI.cpp b/TFT_eSPI.cpp index e4cf7ad..d72d835 100644 --- a/TFT_eSPI.cpp +++ b/TFT_eSPI.cpp @@ -2927,10 +2927,10 @@ void TFT_eSPI::readAddrWindow(int32_t xs, int32_t ys, int32_t w, int32_t h) addr_row = 0xFFFF; #ifdef CGRAM_OFFSET - xs+=colstart; - xe+=colstart; - ys+=rowstart; - ye+=rowstart; + xs += colstart; + xe += colstart; + ys += rowstart; + ye += rowstart; #endif // Column addr set @@ -2996,10 +2996,10 @@ void TFT_eSPI::readAddrWindow(int32_t xs, int32_t ys, int32_t w, int32_t h) addr_row = 0xFFFF; #ifdef CGRAM_OFFSET - x0+=colstart; - x1+=colstart; - y0+=rowstart; - y1+=rowstart; +xs += colstart; +xe += colstart; +ys += rowstart; +ye += rowstart; #endif // Column addr set From caef4519f343441f15bd0b03c64054663bdf97dd Mon Sep 17 00:00:00 2001 From: Bodmer Date: Fri, 25 Jan 2019 14:25:40 +0000 Subject: [PATCH 242/287] Improve bounds checking update CS management Add bounds checking to graphics functions Rationalise variable types and style used to minimse type casting needs (this change ended up being a more extensive refactoring than anticipated - but once started...) Add version reporting to diagnostic sketch Boost PDQ graphicstest performance --- Extensions/Sprite.cpp | 47 ++- Extensions/Sprite.h | 14 +- TFT_eSPI.cpp | 287 ++++++++---------- TFT_eSPI.h | 78 ++--- .../TFT_graphicstest_PDQ3.ino | 7 +- .../TFT_graphicstest_PDQ.ino | 4 +- .../Read_User_Setup/Read_User_Setup.ino | 2 + library.json | 2 +- library.properties | 2 +- 9 files changed, 206 insertions(+), 237 deletions(-) diff --git a/Extensions/Sprite.cpp b/Extensions/Sprite.cpp index 1ab5bb3..6bc58b2 100644 --- a/Extensions/Sprite.cpp +++ b/Extensions/Sprite.cpp @@ -543,10 +543,10 @@ 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*/ -void TFT_eSprite::pushImage(int32_t x, int32_t y, uint32_t w, uint32_t h, uint16_t *data) +void TFT_eSprite::pushImage(int32_t x, int32_t y, int32_t w, int32_t h, uint16_t *data) { if ((x >= _iwidth) || (y >= _iheight) || (w == 0) || (h == 0) || !_created) return; - if ((x + (int32_t)w < 0) || (y + (int32_t)h < 0)) return; + if ((x + w < 0) || (y + h < 0)) return; int32_t xo = 0; int32_t yo = 0; @@ -635,14 +635,14 @@ 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*/ -void TFT_eSprite::pushImage(int32_t x, int32_t y, uint32_t w, uint32_t h, const uint16_t *data) +void TFT_eSprite::pushImage(int32_t x, int32_t y, int32_t w, int32_t h, const uint16_t *data) { #ifdef ESP32 pushImage(x, y, w, h, (uint16_t*) data); #else // Partitioned memory FLASH processor if ((x >= _iwidth) || (y >= _iheight) || (w == 0) || (h == 0) || !_created) return; - if ((x + (int32_t)w < 0) || (y + (int32_t)h < 0)) return; + if ((x + w < 0) || (y + h < 0)) return; int32_t xo = 0; int32_t yo = 0; @@ -877,7 +877,7 @@ 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) +void TFT_eSprite::setScrollRect(int32_t x, int32_t y, int32_t w, int32_t h, uint16_t color) { if ((x >= _iwidth) || (y >= _iheight) || !_created ) return; @@ -1082,11 +1082,10 @@ uint8_t TFT_eSprite::getRotation(void) ** 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) +void TFT_eSprite::drawPixel(int32_t x, int32_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; + // Range checking + if ((x < 0) || (y < 0) ||(x >= _width) || (y >= _height) || !_created) return; if (_bpp == 16) { @@ -1598,12 +1597,12 @@ void TFT_eSprite::drawChar(int32_t x, int32_t y, unsigned char c, uint32_t color ** Function name: drawChar ** Description: draw a unicode onto the screen *************************************************************************************x*/ -int16_t TFT_eSprite::drawChar(unsigned int uniCode, int x, int y) +int16_t TFT_eSprite::drawChar(uint16_t uniCode, int32_t x, int32_t y) { return drawChar(uniCode, x, y, textfont); } -int16_t TFT_eSprite::drawChar(unsigned int uniCode, int x, int y, int font) +int16_t TFT_eSprite::drawChar(uint16_t uniCode, int32_t x, int32_t y, uint8_t font) { if (!_created ) return 0; @@ -1647,8 +1646,8 @@ int16_t TFT_eSprite::drawChar(unsigned int uniCode, int x, int y, int font) if ((font>1) && (font<9) && ((uniCode < 32) || (uniCode > 127))) return 0; - int width = 0; - int height = 0; + int32_t width = 0; + int32_t height = 0; uint32_t flash_address = 0; uniCode -= 32; @@ -1677,9 +1676,9 @@ int16_t TFT_eSprite::drawChar(unsigned int uniCode, int x, int y, int font) } #endif - int w = width; - int pX = 0; - int pY = y; + int32_t w = width; + int32_t pX = 0; + int32_t pY = y; uint8_t line = 0; #ifdef LOAD_FONT2 // chop out code if we do not need it @@ -1688,11 +1687,11 @@ int16_t TFT_eSprite::drawChar(unsigned int uniCode, int x, int y, int font) w = w / 8; if (x + width * textsize >= _iwidth) return width * textsize ; - for (int i = 0; i < height; i++) + for (int32_t i = 0; i < height; i++) { if (textcolor != textbgcolor) fillRect(x, pY, width * textsize, textsize, textbgcolor); - for (int k = 0; k < w; k++) + for (int32_t k = 0; k < w; k++) { line = pgm_read_byte((uint8_t *)flash_address + w * i + k); if (line) { @@ -1738,8 +1737,8 @@ int16_t TFT_eSprite::drawChar(unsigned int uniCode, int x, int y, int font) 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 + int32_t px = 0, py = pY; // To hold character block start and end column and row values + int32_t 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 @@ -1845,10 +1844,10 @@ void TFT_eSprite::drawGlyph(uint16_t code) int16_t xs = 0; uint16_t dl = 0; - for (int y = 0; y < this->gHeight[gNum]; y++) + for (int32_t y = 0; y < this->gHeight[gNum]; y++) { this->fontFile.read(pbuffer, this->gWidth[gNum]); - for (int x = 0; x < this->gWidth[gNum]; x++) + for (int32_t x = 0; x < this->gWidth[gNum]; x++) { uint8_t pixel = pbuffer[x]; if (pixel) @@ -1897,7 +1896,7 @@ void TFT_eSprite::drawGlyph(uint16_t code) void TFT_eSprite::printToSprite(String string) { if(!this->fontLoaded) return; - int16_t len = string.length(); + uint16_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); @@ -1909,7 +1908,7 @@ void TFT_eSprite::printToSprite(String string) ** Function name: printToSprite ** Description: Write a string to the sprite cursor position *************************************************************************************x*/ -void TFT_eSprite::printToSprite(char *cbuffer, int len) //String string) +void TFT_eSprite::printToSprite(char *cbuffer, uint16_t len) //String string) { if(!this->fontLoaded) return; diff --git a/Extensions/Sprite.h b/Extensions/Sprite.h index 57db847..2bf800c 100644 --- a/Extensions/Sprite.h +++ b/Extensions/Sprite.h @@ -29,7 +29,7 @@ class TFT_eSprite : public TFT_eSPI { void setBitmapColor(uint16_t c, uint16_t b); - void drawPixel(uint32_t x, uint32_t y, uint32_t color); + void drawPixel(int32_t x, int32_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), @@ -45,7 +45,7 @@ class TFT_eSprite : public TFT_eSPI { // 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), + setScrollRect(int32_t x, int32_t y, int32_t w, int32_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 @@ -81,8 +81,8 @@ class TFT_eSprite : public TFT_eSPI { 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); + void pushImage(int32_t x0, int32_t y0, int32_t w, int32_t h, uint16_t *data); + void pushImage(int32_t x0, int32_t y0, int32_t w, int32_t h, const uint16_t *data); // Swap the byte order for pushImage() - corrects different image endianness void setSwapBytes(bool swap); @@ -93,8 +93,8 @@ class TFT_eSprite : public TFT_eSPI { 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 drawChar(uint16_t uniCode, int32_t x, int32_t y, uint8_t font), + drawChar(uint16_t uniCode, int32_t x, int32_t y); // Return the width and height of the sprite int16_t width(void), @@ -106,7 +106,7 @@ class TFT_eSprite : public TFT_eSPI { // Functions associated with anti-aliased fonts void drawGlyph(uint16_t code); void printToSprite(String string); - void printToSprite(char *cbuffer, int len); + void printToSprite(char *cbuffer, uint16_t len); int16_t printToSprite(int16_t x, int16_t y, uint16_t index); private: diff --git a/TFT_eSPI.cpp b/TFT_eSPI.cpp index d72d835..20ae7df 100644 --- a/TFT_eSPI.cpp +++ b/TFT_eSPI.cpp @@ -54,33 +54,39 @@ void busDir(uint32_t mask, uint8_t mode); inline void TFT_eSPI::spi_begin(void){ #if defined (SPI_HAS_TRANSACTION) && defined (SUPPORT_TRANSACTIONS) && !defined(ESP32_PARALLEL) - if (locked) {locked = false; spi.beginTransaction(SPISettings(SPI_FREQUENCY, MSBFIRST, TFT_SPI_MODE));} + if (locked) {locked = false; spi.beginTransaction(SPISettings(SPI_FREQUENCY, MSBFIRST, TFT_SPI_MODE)); CS_L;} +#else + CS_L; #endif } inline void TFT_eSPI::spi_end(void){ #if defined (SPI_HAS_TRANSACTION) && defined (SUPPORT_TRANSACTIONS) && !defined(ESP32_PARALLEL) - if(!inTransaction) {if (!locked) {locked = true; spi.endTransaction();}} + if(!inTransaction) {if (!locked) {locked = true; CS_H; spi.endTransaction();}} +#else + CS_H; #endif } inline void TFT_eSPI::spi_begin_read(void){ #if defined (SPI_HAS_TRANSACTION) && defined (SUPPORT_TRANSACTIONS) && !defined(ESP32_PARALLEL) - if (locked) {locked = false; spi.beginTransaction(SPISettings(SPI_READ_FREQUENCY, MSBFIRST, TFT_SPI_MODE));} + if (locked) {locked = false; spi.beginTransaction(SPISettings(SPI_READ_FREQUENCY, MSBFIRST, TFT_SPI_MODE)); CS_L;} #else #if !defined(ESP32_PARALLEL) spi.setFrequency(SPI_READ_FREQUENCY); #endif + CS_L; #endif } inline void TFT_eSPI::spi_end_read(void){ #if defined (SPI_HAS_TRANSACTION) && defined (SUPPORT_TRANSACTIONS) && !defined(ESP32_PARALLEL) - if(!inTransaction) {if (!locked) {locked = true; spi.endTransaction();}} + if(!inTransaction) {if (!locked) {locked = true; CS_H; spi.endTransaction();}} #else #if !defined(ESP32_PARALLEL) spi.setFrequency(SPI_FREQUENCY); #endif + CS_H; #endif } @@ -145,7 +151,7 @@ TFT_eSPI::TFT_eSPI(int16_t w, int16_t h) #ifdef ESP32_PARALLEL // 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++) + for (int32_t c = 0; c<256; c++) { xset_mask[c] = 0; if ( c & 0x01 ) xset_mask[c] |= (1 << TFT_D0); @@ -507,15 +513,16 @@ void TFT_eSPI::spiwrite(uint8_t c) ***************************************************************************************/ void TFT_eSPI::writecommand(uint8_t c) { - //DC_C; - //CS_L; - digitalWrite(TFT_DC, LOW); - digitalWrite(TFT_CS, LOW); + spi_begin(); // CS_L; + + DC_C; + tft_Write_8(c); - digitalWrite(TFT_CS, HIGH); - digitalWrite(TFT_DC, HIGH); - //CS_H; - //DC_D; + + DC_D; + + spi_end(); // CS_H; + } @@ -525,11 +532,15 @@ void TFT_eSPI::writecommand(uint8_t c) ***************************************************************************************/ void TFT_eSPI::writedata(uint8_t d) { - //CS_L; - digitalWrite(TFT_CS, LOW); + spi_begin(); // CS_L; + + DC_D; // Play safe, but should already be in data mode + tft_Write_8(d); - digitalWrite(TFT_CS, HIGH); - //CS_H; + + CS_L; // Allow more hold time for low VDI rail + + spi_end(); // CS_H; } @@ -561,18 +572,17 @@ uint8_t TFT_eSPI::readcommand8(uint8_t cmd_function, uint8_t index) index = 0x10 + (index & 0x0F); DC_C; - CS_L; tft_Write_8(0xD9); DC_D; tft_Write_8(index); - CS_H; + + CS_H; // Some displays seem to need CS to be pulsed here, or is just a delay needed? + CS_L; DC_C; - CS_L; tft_Write_8(cmd_function); DC_D; reg = tft_Read_8(); - CS_H; spi_end_read(); #endif @@ -760,7 +770,7 @@ void busDir(uint32_t mask, uint8_t mode) ** 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(int32_t x, int32_t y, int32_t w, int32_t h, uint16_t *data) { if ((x > _width) || (y > _height) || (w == 0) || (h == 0)) return; @@ -922,7 +932,7 @@ void TFT_eSPI::end_SDA_Read(void) ** 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(int32_t x, int32_t y, int32_t w, int32_t h, uint16_t *data) { // Function deprecated, remains for backwards compatibility // pushImage() is better as it will crop partly off-screen image blocks @@ -934,10 +944,10 @@ void TFT_eSPI::pushRect(uint32_t x, uint32_t y, uint32_t w, uint32_t h, uint16_t ** Function name: pushImage ** Description: plot 16 bit colour sprite or image onto TFT ***************************************************************************************/ -void TFT_eSPI::pushImage(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, int32_t w, int32_t h, uint16_t *data) { - if ((x >= (int32_t)_width) || (y >= (int32_t)_height)) return; + if ((x >= _width) || (y >= _height)) return; int32_t dx = 0; int32_t dy = 0; @@ -955,7 +965,7 @@ void TFT_eSPI::pushImage(int32_t x, int32_t y, uint32_t w, uint32_t h, uint16_t spi_begin(); inTransaction = true; - setWindow(x, y, x + dw - 1, y + dh - 1); // Sets CS low and sent RAMWR + setWindow(x, y, x + dw - 1, y + dh - 1); data += dx + dy * w; @@ -965,8 +975,6 @@ void TFT_eSPI::pushImage(int32_t x, int32_t y, uint32_t w, uint32_t h, uint16_t data += w; } - CS_H; - inTransaction = false; spi_end(); } @@ -975,10 +983,10 @@ void TFT_eSPI::pushImage(int32_t x, int32_t y, uint32_t w, uint32_t h, uint16_t ** Function name: pushImage ** 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, uint16_t *data, uint16_t transp) +void TFT_eSPI::pushImage(int32_t x, int32_t y, int32_t w, int32_t h, uint16_t *data, uint16_t transp) { - if ((x >= (int32_t)_width) || (y >= (int32_t)_height)) return; + if ((x >= _width) || (y >= _height)) return; int32_t dx = 0; int32_t dy = 0; @@ -1038,8 +1046,6 @@ void TFT_eSPI::pushImage(int32_t x, int32_t y, uint32_t w, uint32_t h, uint16_t data += w; } - CS_H; - inTransaction = false; spi_end(); } @@ -1049,13 +1055,13 @@ void TFT_eSPI::pushImage(int32_t x, int32_t y, uint32_t w, uint32_t h, uint16_t ** Function name: pushImage - for FLASH (PROGMEM) stored images ** 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) +void TFT_eSPI::pushImage(int32_t x, int32_t y, int32_t w, int32_t h, const uint16_t *data) { #ifdef ESP32 pushImage(x, y, w, h, (uint16_t*)data); #else // Partitioned memory FLASH processor - if ((x >= (int32_t)_width) || (y >= (int32_t)_height)) return; + if ((x >= _width) || (y >= _height)) return; int32_t dx = 0; int32_t dy = 0; @@ -1084,8 +1090,8 @@ void TFT_eSPI::pushImage(int32_t x, int32_t y, uint32_t w, uint32_t h, const uin 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++) { + for (int32_t i = 0; i < nb; i++) { + for (int32_t j = 0; j < 64; j++) { pix_buffer[j] = pgm_read_word(&data[i * 64 + j]); } pushColors(pix_buffer, 64, _swapBytes); @@ -1096,15 +1102,13 @@ void TFT_eSPI::pushImage(int32_t x, int32_t y, uint32_t w, uint32_t h, const uin // Send any partial buffer left over if (np) { - for (int i = 0; i < np; i++) + for (int32_t 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(); #endif // if ESP32 else ESP8266 check @@ -1115,13 +1119,13 @@ void TFT_eSPI::pushImage(int32_t x, int32_t y, uint32_t w, uint32_t h, const uin ** 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) +void TFT_eSPI::pushImage(int32_t x, int32_t y, int32_t w, int32_t h, const uint16_t *data, uint16_t transp) { #ifdef ESP32 pushImage(x, y, w, h, (uint16_t*) data, transp); #else // Partitioned memory FLASH processor - if ((x >= (int32_t)_width) || (y >= (int32_t)_height)) return; + if ((x >= _width) || (y >= (int32_t)_height)) return; int32_t dx = 0; int32_t dy = 0; @@ -1183,8 +1187,6 @@ void TFT_eSPI::pushImage(int32_t x, int32_t y, uint32_t w, uint32_t h, const uin data += w; } - CS_H; - inTransaction = false; spi_end(); #endif // if ESP32 else ESP8266 check @@ -1195,9 +1197,9 @@ 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, bool bpp8) +void TFT_eSPI::pushImage(int32_t x, int32_t y, int32_t w, int32_t h, uint8_t *data, bool bpp8) { - if ((x >= (int32_t)_width) || (y >= (int32_t)_height)) return; + if ((x >= _width) || (y >= (int32_t)_height)) return; int32_t dx = 0; int32_t dy = 0; @@ -1292,8 +1294,6 @@ void TFT_eSPI::pushImage(int32_t x, int32_t y, uint32_t w, uint32_t h, uint8_t * } } - CS_H; - inTransaction = false; spi_end(); } @@ -1303,9 +1303,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 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, bool bpp8) +void TFT_eSPI::pushImage(int32_t x, int32_t y, int32_t w, int32_t h, uint8_t *data, uint8_t transp, bool bpp8) { - if ((x >= (int32_t)_width) || (y >= (int32_t)_height)) return; + if ((x >= _width) || (y >= _height)) return; int32_t dx = 0; int32_t dy = 0; @@ -1440,8 +1440,6 @@ void TFT_eSPI::pushImage(int32_t x, int32_t y, uint32_t w, uint32_t h, uint8_t * } } - CS_H; - inTransaction = false; spi_end(); } @@ -2254,7 +2252,7 @@ int16_t TFT_eSPI::textWidth(const String& string) return textWidth(buffer, textfont); } -int16_t TFT_eSPI::textWidth(const String& string, int font) +int16_t TFT_eSPI::textWidth(const String& string, uint8_t font) { int16_t len = string.length() + 2; char buffer[len]; @@ -2267,9 +2265,9 @@ int16_t TFT_eSPI::textWidth(const char *string) return textWidth(string, textfont); } -int16_t TFT_eSPI::textWidth(const char *string, int font) +int16_t TFT_eSPI::textWidth(const char *string, uint8_t font) { - int str_width = 0; + int32_t str_width = 0; #ifdef SMOOTH_FONT if(fontLoaded) @@ -2392,8 +2390,8 @@ int16_t TFT_eSPI::fontHeight(void) ***************************************************************************************/ void TFT_eSPI::drawChar(int32_t x, int32_t y, unsigned char c, uint32_t color, uint32_t bg, uint8_t size) { - if ((x >= (int16_t)_width) || // Clip right - (y >= (int16_t)_height) || // Clip bottom + if ((x >= _width) || // Clip right + (y >= _height) || // Clip bottom ((x + 6 * size - 1) < 0) || // Clip left ((y + 8 * size - 1) < 0)) // Clip top return; @@ -2413,8 +2411,9 @@ void TFT_eSPI::drawChar(int32_t x, int32_t y, unsigned char c, uint32_t color, u uint8_t column[6]; uint8_t mask = 0x1; spi_begin(); - //inTransaction = true; + setWindow(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; @@ -2453,8 +2452,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 @@ -2644,8 +2642,9 @@ void TFT_eSPI::drawChar(int32_t x, int32_t y, unsigned char c, uint32_t color, u void TFT_eSPI::setAddrWindow(int32_t x0, int32_t y0, int32_t w, int32_t h) { spi_begin(); + setWindow(x0, y0, x0 + w - 1, y0 + h - 1); - CS_H; + spi_end(); } @@ -2658,7 +2657,7 @@ void TFT_eSPI::setAddrWindow(int32_t x0, int32_t y0, int32_t w, int32_t h) #if defined (ESP8266) && !defined (RPI_WRITE_STROBE) && !defined (RPI_ILI9486_DRIVER) void TFT_eSPI::setWindow(int32_t xs, int32_t ys, int32_t xe, int32_t ye) { - //spi_begin(); + //spi_begin(); // Must be called before setWimdow #ifdef CGRAM_OFFSET xs+=colstart; @@ -2669,7 +2668,6 @@ void TFT_eSPI::setWindow(int32_t xs, int32_t ys, int32_t xe, int32_t ye) // Column addr set DC_C; - CS_L; uint32_t mask = ~((SPIMMOSI << SPILMOSI) | (SPIMMISO << SPILMISO)); mask = SPI1U1 & mask; @@ -2726,14 +2724,13 @@ void TFT_eSPI::setWindow(int32_t xs, int32_t ys, int32_t xe, int32_t ye) void TFT_eSPI::setWindow(int32_t xs, int32_t ys, int32_t xe, int32_t ye) { - //spi_begin(); + //spi_begin(); // Must be called before setWimdow addr_col = 0xFFFF; addr_row = 0xFFFF; // Column addr set DC_C; - CS_L; uint32_t mask = ~((SPIMMOSI << SPILMOSI) | (SPIMMISO << SPILMISO)); mask = SPI1U1 & mask; @@ -2781,9 +2778,8 @@ void TFT_eSPI::setWindow(int32_t xs, int32_t ys, int32_t xe, int32_t ye) #if defined (ESP8266) && defined (RPI_ILI9486_DRIVER) // This is for the RPi display that needs 16 bits void TFT_eSPI::setWindow(int32_t x0, int32_t y0, int32_t x1, int32_t y1) { - //spi_begin(); + //spi_begin(); // Must be called before setWimdow - CS_L; uint32_t mask = ~((SPIMMOSI << SPILMOSI) | (SPIMMISO << SPILMISO)); mask = SPI1U1 & mask; SPI1U1 = mask | (CMD_BITS << SPILMOSI) | (CMD_BITS << SPILMISO); @@ -2858,7 +2854,7 @@ void TFT_eSPI::setWindow(int32_t x0, int32_t y0, int32_t x1, int32_t y1) void TFT_eSPI::setWindow(int32_t x0, int32_t y0, int32_t x1, int32_t y1) { - //spi_begin(); + //spi_begin(); // Must be called before setWimdow addr_col = 0xFFFF; addr_row = 0xFFFF; @@ -2870,7 +2866,7 @@ void TFT_eSPI::setWindow(int32_t x0, int32_t y0, int32_t x1, int32_t y1) y1+=rowstart; #endif - CS_L_DC_C; + DC_C; tft_Write_8(TFT_CASET); @@ -2918,7 +2914,7 @@ void TFT_eSPI::setWindow(int32_t x0, int32_t y0, int32_t x1, int32_t y1) #if defined (ESP8266) && !defined (RPI_WRITE_STROBE) void TFT_eSPI::readAddrWindow(int32_t xs, int32_t ys, int32_t w, int32_t h) { - //spi_begin(); + spi_begin(); int32_t xe = xs + w - 1; int32_t ye = ys + h - 1; @@ -2935,7 +2931,6 @@ void TFT_eSPI::readAddrWindow(int32_t xs, int32_t ys, int32_t w, int32_t h) // Column addr set DC_C; - CS_L; uint32_t mask = ~((SPIMMOSI << SPILMOSI) | (SPIMMISO << SPILMISO)); mask = SPI1U1 & mask; @@ -2987,7 +2982,7 @@ void TFT_eSPI::readAddrWindow(int32_t xs, int32_t ys, int32_t w, int32_t h) void TFT_eSPI::readAddrWindow(int32_t xs, int32_t ys, int32_t w, int32_t h) { - //spi_begin(); + spi_begin(); int32_t xe = xs + w - 1; int32_t ye = ys + h - 1; @@ -3003,7 +2998,7 @@ ye += rowstart; #endif // Column addr set - CS_L_DC_C; + DC_C; tft_Write_8(TFT_CASET); @@ -3036,11 +3031,11 @@ ye += rowstart; ** Description: push a single pixel at an arbitrary position ***************************************************************************************/ #if defined (ESP8266) && !defined (RPI_WRITE_STROBE) -void TFT_eSPI::drawPixel(uint32_t x, uint32_t y, uint32_t color) +void TFT_eSPI::drawPixel(int32_t x, int32_t y, uint32_t color) { - // Faster range checking, possible because x and y are unsigned - if ((x >= _width) || (y >= _height)) return; - + // Range checking + if ((x < 0) || (y < 0) ||(x >= _width) || (y >= _height)) return; + #ifdef CGRAM_OFFSET x+=colstart; y+=rowstart; @@ -3048,8 +3043,6 @@ void TFT_eSPI::drawPixel(uint32_t x, uint32_t y, uint32_t color) spi_begin(); - CS_L; - uint32_t mask = ~((SPIMMOSI << SPILMOSI) | (SPIMMISO << SPILMISO)); mask = SPI1U1 & mask; // No need to send x if it has not changed (speeds things up) @@ -3127,8 +3120,6 @@ void TFT_eSPI::drawPixel(uint32_t x, uint32_t y, uint32_t color) while(SPI1CMD & SPIBUSY) {} #endif - CS_H; - spi_end(); } @@ -3136,13 +3127,13 @@ void TFT_eSPI::drawPixel(uint32_t x, uint32_t y, uint32_t color) #if defined (ESP8266) && defined (RPI_ILI9486_DRIVER) // This is for the RPi display that needs 16 bits -void TFT_eSPI::drawPixel(uint32_t x, uint32_t y, uint32_t color) +void TFT_eSPI::drawPixel(int32_t x, int32_t y, uint32_t color) { - // Faster range checking, possible because x and y are unsigned - if ((x >= _width) || (y >= _height)) return; + // Range checking + if ((x < 0) || (y < 0) ||(x >= _width) || (y >= _height)) return; + spi_begin(); - CS_L; uint32_t mask = ~((SPIMMOSI << SPILMOSI) | (SPIMMISO << SPILMISO)); mask = SPI1U1 & mask; SPI1U1 = mask | (CMD_BITS << SPILMOSI) | (CMD_BITS << SPILMISO); @@ -3214,17 +3205,16 @@ void TFT_eSPI::drawPixel(uint32_t x, uint32_t y, uint32_t color) SPI1CMD |= SPIBUSY; while(SPI1CMD & SPIBUSY) {} - CS_H; - spi_end(); } #else // ESP32 -void TFT_eSPI::drawPixel(uint32_t x, uint32_t y, uint32_t color) +void TFT_eSPI::drawPixel(int32_t x, int32_t y, uint32_t color) { - // Faster range checking, possible because x and y are unsigned - if ((x >= _width) || (y >= _height)) return; + // Range checking + if ((x < 0) || (y < 0) ||(x >= _width) || (y >= _height)) return; + spi_begin(); #ifdef CGRAM_OFFSET @@ -3232,7 +3222,7 @@ void TFT_eSPI::drawPixel(uint32_t x, uint32_t y, uint32_t color) y+=rowstart; #endif - CS_L_DC_C; + DC_C; // No need to send x if it has not changed (speeds things up) if (addr_col != x) { @@ -3279,8 +3269,6 @@ void TFT_eSPI::drawPixel(uint32_t x, uint32_t y, uint32_t color) tft_Write_16(color); - CS_H; - spi_end(); } #endif @@ -3295,12 +3283,8 @@ void TFT_eSPI::pushColor(uint16_t color) { spi_begin(); - CS_L; - tft_Write_16(color); - CS_H; - spi_end(); } @@ -3313,8 +3297,6 @@ void TFT_eSPI::pushColor(uint16_t color, uint32_t len) { spi_begin(); - CS_L; - #ifdef RPI_WRITE_STROBE uint8_t colorBin[] = { (uint8_t) (color >> 8), (uint8_t) color }; if(len) spi.writePattern(&colorBin[0], 2, 1); len--; @@ -3327,8 +3309,6 @@ void TFT_eSPI::pushColor(uint16_t color, uint32_t len) #endif #endif - CS_H; - spi_end(); } @@ -3340,7 +3320,6 @@ void TFT_eSPI::startWrite(void) { spi_begin(); inTransaction = true; - CS_L; } /*************************************************************************************** @@ -3349,7 +3328,6 @@ void TFT_eSPI::startWrite(void) ***************************************************************************************/ void TFT_eSPI::endWrite(void) { - CS_H; inTransaction = false; spi_end(); } @@ -3383,8 +3361,6 @@ 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); @@ -3404,8 +3380,6 @@ void TFT_eSPI::pushColors(uint8_t *data, uint32_t len) #endif #endif - CS_H; - spi_end(); } @@ -3418,8 +3392,6 @@ void TFT_eSPI::pushColors(uint16_t *data, uint32_t len, bool swap) { spi_begin(); - CS_L; - #if defined (ESP32) || defined (ILI9488_DRIVER) #if defined (ESP32_PARALLEL) || defined (ILI9488_DRIVER) if (swap) while ( len-- ) {tft_Write_16(*data); data++;} @@ -3505,8 +3477,6 @@ void TFT_eSPI::pushColors(uint16_t *data, uint32_t len, bool swap) #endif - CS_H; - spi_end(); } @@ -3609,7 +3579,7 @@ void TFT_eSPI::drawLine(int32_t x0, int32_t y0, int32_t x1, int32_t y1, uint32_t if (steep) // y increments every iteration (y0 is x-axis, and x0 is y-axis) { - if (x1 >= _height) x1 = _height - 1; + if (x1 >= (int32_t)_height) x1 = _height - 1; for (; x0 <= x1; x0++) { if ((x0 >= 0) && (y0 >= 0) && (y0 < _width)) break; @@ -3646,7 +3616,7 @@ void TFT_eSPI::drawLine(int32_t x0, int32_t y0, int32_t x1, int32_t y1, uint32_t if (x1 >= _width) x1 = _width - 1; for (; x0 <= x1; x0++) { - if ((x0 >= 0) && (y0 >= 0) && (y0 < _height)) break; + if ((x0 >= 0) && (y0 >= 0) && (y0 < (int32_t)_height)) break; err -= dy; if (err < 0) { err += dx; @@ -3666,7 +3636,7 @@ void TFT_eSPI::drawLine(int32_t x0, int32_t y0, int32_t x1, int32_t y1, uint32_t err -= dy; if (err < 0) { y0 += ystep; - if ((y0 < 0) || (y0 >= _height)) break; + if ((y0 < 0) || (y0 >= (int32_t)_height)) break; err += dx; while(SPI1CMD & SPIBUSY) {} setWindow(x0+1, y0, _width, y0); @@ -3678,7 +3648,7 @@ void TFT_eSPI::drawLine(int32_t x0, int32_t y0, int32_t x1, int32_t y1, uint32_t while(SPI1CMD & SPIBUSY) {} SPI1U = SPIUMOSI | SPIUDUPLEX | SPIUSSE; - CS_H; + spi_end(); } @@ -3707,8 +3677,6 @@ void TFT_eSPI::drawFastVLine(int32_t x, int32_t y, int32_t h, uint32_t color) writeBlock(color, h); - CS_H; - spi_end(); } @@ -3748,8 +3716,6 @@ void TFT_eSPI::drawFastVLine(int32_t x, int32_t y, int32_t h, uint32_t color) #endif #endif - CS_H; - spi_end(); } #endif @@ -3776,8 +3742,6 @@ void TFT_eSPI::drawFastHLine(int32_t x, int32_t y, int32_t w, uint32_t color) writeBlock(color, w); - CS_H; - spi_end(); } @@ -3785,11 +3749,17 @@ void TFT_eSPI::drawFastHLine(int32_t x, int32_t y, int32_t w, uint32_t color) void TFT_eSPI::drawFastHLine(int32_t x, int32_t y, int32_t w, uint32_t color) { - // Rudimentary clipping - if ((x >= _width) || (y >= _height) || (w < 1)) return; - if ((x + w - 1) >= _width) w = _width - x; + // Clipping + if ((y < 0) || (x >= _width) || (y >= _height)) return; + + if (x < 0) { w += x; x = 0; } + + if ((x + w) > _width) w = _width - x; + + if (w < 1) return; spi_begin(); + setWindow(x, y, x + w - 1, y); #ifdef RPI_WRITE_STROBE @@ -3811,8 +3781,6 @@ void TFT_eSPI::drawFastHLine(int32_t x, int32_t y, int32_t w, uint32_t color) #endif #endif - CS_H; - spi_end(); } #endif @@ -3836,12 +3804,11 @@ void TFT_eSPI::fillRect(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t col if ((w < 1) || (h < 1)) return; spi_begin(); + setWindow(x, y, x + w - 1, y + h - 1); writeBlock(color, w * h); - CS_H; - spi_end(); } @@ -3849,6 +3816,7 @@ void TFT_eSPI::fillRect(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t col void TFT_eSPI::fillRect(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color) { + // Clipping if ((x >= _width) || (y >= _height)) return; @@ -3861,6 +3829,7 @@ void TFT_eSPI::fillRect(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t col if ((w < 1) || (h < 1)) return; spi_begin(); + setWindow(x, y, x + w - 1, y + h - 1); uint32_t n = (uint32_t)w * (uint32_t)h; @@ -3885,8 +3854,6 @@ void TFT_eSPI::fillRect(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t col #endif #endif - CS_H; - spi_end(); } #endif @@ -4047,7 +4014,7 @@ size_t TFT_eSPI::write(uint8_t utf8) cursor_y += height; cursor_x = 0; } - if (textwrapY && (cursor_y >= _height)) cursor_y = 0; + if (textwrapY && (cursor_y >= (int32_t)_height)) cursor_y = 0; cursor_x += drawChar(uniCode, cursor_x, cursor_y, textfont); } @@ -4077,7 +4044,7 @@ size_t TFT_eSPI::write(uint8_t utf8) cursor_y += (int16_t)textsize * (uint8_t)pgm_read_byte(&gfxFont->yAdvance); } - if (textwrapY && (cursor_y >= _height)) cursor_y = 0; + if (textwrapY && (cursor_y >= (int32_t)_height)) cursor_y = 0; drawChar(cursor_x, cursor_y, uniCode, textcolor, textbgcolor, textsize); } cursor_x += pgm_read_byte(&glyph->xAdvance) * (int16_t)textsize; @@ -4094,12 +4061,12 @@ size_t TFT_eSPI::write(uint8_t utf8) ** Function name: drawChar ** Description: draw a Unicode onto the screen ***************************************************************************************/ -int16_t TFT_eSPI::drawChar(unsigned int uniCode, int x, int y) +int16_t TFT_eSPI::drawChar(uint16_t uniCode, int32_t x, int32_t y) { return drawChar(uniCode, x, y, textfont); } -int16_t TFT_eSPI::drawChar(unsigned int uniCode, int x, int y, int font) +int16_t TFT_eSPI::drawChar(uint16_t uniCode, int32_t x, int32_t y, uint8_t font) { if (font==1) @@ -4142,8 +4109,8 @@ int16_t TFT_eSPI::drawChar(unsigned int uniCode, int x, int y, int font) if ((font>1) && (font<9) && ((uniCode < 32) || (uniCode > 127))) return 0; - int width = 0; - int height = 0; + int32_t width = 0; + int32_t height = 0; uint32_t flash_address = 0; uniCode -= 32; @@ -4172,9 +4139,9 @@ int16_t TFT_eSPI::drawChar(unsigned int uniCode, int x, int y, int font) } #endif - int w = width; - int pX = 0; - int pY = y; + int32_t w = width; + int32_t pX = 0; + int32_t pY = y; uint8_t line = 0; #ifdef LOAD_FONT2 // chop out code if we do not need it @@ -4187,11 +4154,11 @@ int16_t TFT_eSPI::drawChar(unsigned int uniCode, int x, int y, int font) spi_begin(); inTransaction = true; - for (int i = 0; i < height; i++) + for (int32_t i = 0; i < height; i++) { if (textcolor != textbgcolor) fillRect(x, pY, width * textsize, textsize, textbgcolor); - for (int k = 0; k < w; k++) + for (int32_t k = 0; k < w; k++) { line = pgm_read_byte((uint8_t *)flash_address + w * i + k); if (line) { @@ -4229,12 +4196,13 @@ int16_t TFT_eSPI::drawChar(unsigned int uniCode, int x, int y, int font) // Faster drawing of characters and background using block write { spi_begin(); + setWindow(x, y, (x + w * 8) - 1, y + height - 1); uint8_t mask; - for (int i = 0; i < height; i++) + for (int32_t i = 0; i < height; i++) { - for (int k = 0; k < w; k++) + for (int32_t k = 0; k < w; k++) { line = pgm_read_byte((uint8_t *)flash_address + w * i + k); pX = x + k * 8; @@ -4248,7 +4216,6 @@ int16_t TFT_eSPI::drawChar(unsigned int uniCode, int x, int y, int font) pY += textsize; } - CS_H; spi_end(); } } @@ -4266,8 +4233,8 @@ int16_t TFT_eSPI::drawChar(unsigned int uniCode, int x, int y, int font) w *= height; // Now w is total number of pixels in the character if ((textsize != 1) || (textcolor == textbgcolor)) { 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 + int32_t px = 0, py = pY; // To hold character block start and end column and row values + int32_t 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 @@ -4313,7 +4280,6 @@ int16_t TFT_eSPI::drawChar(unsigned int uniCode, int x, int y, int font) } } - CS_H; spi_end(); } else // Text colour != background && textsize = 1 @@ -4359,7 +4325,7 @@ int16_t TFT_eSPI::drawChar(unsigned int uniCode, int x, int y, int font) #endif } } - CS_H; + spi_end(); } } @@ -4374,7 +4340,7 @@ int16_t TFT_eSPI::drawChar(unsigned int uniCode, int x, int y, int font) ** Description : draw string with padding if it is defined ***************************************************************************************/ // Without font number, uses font set by setTextFont() -int16_t TFT_eSPI::drawString(const String& string, int poX, int poY) +int16_t TFT_eSPI::drawString(const String& string, int32_t poX, int32_t poY) { int16_t len = string.length() + 2; char buffer[len]; @@ -4382,7 +4348,7 @@ int16_t TFT_eSPI::drawString(const String& string, int poX, int poY) return drawString(buffer, poX, poY, textfont); } // With font number -int16_t TFT_eSPI::drawString(const String& string, int poX, int poY, int font) +int16_t TFT_eSPI::drawString(const String& string, int32_t poX, int32_t poY, uint8_t font) { int16_t len = string.length() + 2; char buffer[len]; @@ -4391,13 +4357,13 @@ int16_t TFT_eSPI::drawString(const String& string, int poX, int poY, int font) } // Without font number, uses font set by setTextFont() -int16_t TFT_eSPI::drawString(const char *string, int poX, int poY) +int16_t TFT_eSPI::drawString(const char *string, int32_t poX, int32_t poY) { return drawString(string, poX, poY, textfont); } // With font number -int16_t TFT_eSPI::drawString(const char *string, int poX, int poY, int font) +int16_t TFT_eSPI::drawString(const char *string, int32_t poX, int32_t poY, uint8_t font) { int16_t sumX = 0; uint8_t padding = 1, baseline = 0; @@ -4536,6 +4502,7 @@ int16_t TFT_eSPI::drawString(const char *string, int poX, int poY, int font) 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); @@ -4623,7 +4590,7 @@ return sumX; ** Function name: drawCentreString (deprecated, use setTextDatum()) ** Descriptions: draw string centred on dX ***************************************************************************************/ -int16_t TFT_eSPI::drawCentreString(const String& string, int dX, int poY, int font) +int16_t TFT_eSPI::drawCentreString(const String& string, int32_t dX, int32_t poY, uint8_t font) { int16_t len = string.length() + 2; char buffer[len]; @@ -4631,10 +4598,10 @@ int16_t TFT_eSPI::drawCentreString(const String& string, int dX, int poY, int fo return drawCentreString(buffer, dX, poY, font); } -int16_t TFT_eSPI::drawCentreString(const char *string, int dX, int poY, int font) +int16_t TFT_eSPI::drawCentreString(const char *string, int32_t dX, int32_t poY, uint8_t font) { uint8_t tempdatum = textdatum; - int sumX = 0; + int32_t sumX = 0; textdatum = TC_DATUM; sumX = drawString(string, dX, poY, font); textdatum = tempdatum; @@ -4646,7 +4613,7 @@ int16_t TFT_eSPI::drawCentreString(const char *string, int dX, int poY, int font ** Function name: drawRightString (deprecated, use setTextDatum()) ** Descriptions: draw string right justified to dX ***************************************************************************************/ -int16_t TFT_eSPI::drawRightString(const String& string, int dX, int poY, int font) +int16_t TFT_eSPI::drawRightString(const String& string, int32_t dX, int32_t poY, uint8_t font) { int16_t len = string.length() + 2; char buffer[len]; @@ -4654,7 +4621,7 @@ int16_t TFT_eSPI::drawRightString(const String& string, int dX, int poY, int fon return drawRightString(buffer, dX, poY, font); } -int16_t TFT_eSPI::drawRightString(const char *string, int dX, int poY, int font) +int16_t TFT_eSPI::drawRightString(const char *string, int32_t dX, int32_t poY, uint8_t font) { uint8_t tempdatum = textdatum; int16_t sumX = 0; @@ -4669,7 +4636,7 @@ int16_t TFT_eSPI::drawRightString(const char *string, int dX, int poY, int font) ** Function name: drawNumber ** Description: draw a long integer ***************************************************************************************/ -int16_t TFT_eSPI::drawNumber(long long_num, int poX, int poY) +int16_t TFT_eSPI::drawNumber(long long_num, int32_t poX, int32_t poY) { isDigits = true; // Eliminate jiggle in monospaced fonts char str[12]; @@ -4677,7 +4644,7 @@ int16_t TFT_eSPI::drawNumber(long long_num, int poX, int poY) return drawString(str, poX, poY, textfont); } -int16_t TFT_eSPI::drawNumber(long long_num, int poX, int poY, int font) +int16_t TFT_eSPI::drawNumber(long long_num, int32_t poX, int32_t poY, uint8_t font) { isDigits = true; // Eliminate jiggle in monospaced fonts char str[12]; @@ -4692,12 +4659,12 @@ int16_t TFT_eSPI::drawNumber(long long_num, int poX, int poY, int font) ***************************************************************************************/ // Assemble and print a string, this permits alignment relative to a datum // looks complicated but much more compact and actually faster than using print class -int16_t TFT_eSPI::drawFloat(float floatNumber, int dp, int poX, int poY) +int16_t TFT_eSPI::drawFloat(float floatNumber, uint8_t dp, int32_t poX, int32_t poY) { return drawFloat(floatNumber, dp, poX, poY, textfont); } -int16_t TFT_eSPI::drawFloat(float floatNumber, int dp, int poX, int poY, int font) +int16_t TFT_eSPI::drawFloat(float floatNumber, uint8_t dp, int32_t poX, int32_t poY, uint8_t font) { isDigits = true; char str[14]; // Array to contain decimal string diff --git a/TFT_eSPI.h b/TFT_eSPI.h index 01a1351..66546c7 100644 --- a/TFT_eSPI.h +++ b/TFT_eSPI.h @@ -15,7 +15,7 @@ #ifndef _TFT_eSPIH_ #define _TFT_eSPIH_ -#define TFT_ESPI_VERSION "1.4.0" +#define TFT_ESPI_VERSION "1.4.1" //#define ESP32 //Just used to test ESP32 options @@ -144,8 +144,8 @@ #define DC_D GPIO.out1_w1tc.val = (1 << (TFT_DC - 32)); \ GPIO.out1_w1ts.val = (1 << (TFT_DC - 32)) #else - #define DC_C GPIO.out1_w1tc.val = (1 << (TFT_DC - 32));GPIO.out1_w1tc.val = (1 << (TFT_DC - 32)) - #define DC_D GPIO.out1_w1ts.val = (1 << (TFT_DC - 32));GPIO.out1_w1ts.val = (1 << (TFT_DC - 32)) + #define DC_C GPIO.out1_w1tc.val = (1 << (TFT_DC - 32))//;GPIO.out1_w1tc.val = (1 << (TFT_DC - 32)) + #define DC_D GPIO.out1_w1ts.val = (1 << (TFT_DC - 32))//;GPIO.out1_w1ts.val = (1 << (TFT_DC - 32)) #endif #else #if TFT_DC >= 0 @@ -155,8 +155,8 @@ #define DC_D GPIO.out_w1tc = (1 << TFT_DC); \ GPIO.out_w1ts = (1 << TFT_DC) #else - #define DC_C GPIO.out_w1tc = (1 << TFT_DC);GPIO.out_w1tc = (1 << TFT_DC) - #define DC_D GPIO.out_w1ts = (1 << TFT_DC);GPIO.out_w1ts = (1 << TFT_DC) + #define DC_C GPIO.out_w1tc = (1 << TFT_DC)//;GPIO.out_w1tc = (1 << TFT_DC) + #define DC_D GPIO.out_w1ts = (1 << TFT_DC)//;GPIO.out_w1ts = (1 << TFT_DC) #endif #else #define DC_C @@ -193,8 +193,8 @@ #define CS_H GPIO.out1_w1tc.val = (1 << (TFT_CS - 32)); \ GPIO.out1_w1ts.val = (1 << (TFT_CS - 32)) #else - #define CS_L GPIO.out1_w1tc.val = (1 << (TFT_CS - 32));GPIO.out1_w1tc.val = (1 << (TFT_CS - 32)) - #define CS_H GPIO.out1_w1ts.val = (1 << (TFT_CS - 32));GPIO.out1_w1ts.val = (1 << (TFT_CS - 32)) + #define CS_L GPIO.out1_w1tc.val = (1 << (TFT_CS - 32)); GPIO.out1_w1tc.val = (1 << (TFT_CS - 32)) + #define CS_H GPIO.out1_w1ts.val = (1 << (TFT_CS - 32))//;GPIO.out1_w1ts.val = (1 << (TFT_CS - 32)) #endif #else #if TFT_CS >= 0 @@ -203,7 +203,7 @@ #define CS_H GPIO.out_w1tc = (1 << TFT_CS); GPIO.out_w1ts = (1 << TFT_CS) #else #define CS_L GPIO.out_w1tc = (1 << TFT_CS);GPIO.out_w1tc = (1 << TFT_CS) - #define CS_H GPIO.out_w1ts = (1 << TFT_CS);GPIO.out_w1ts = (1 << TFT_CS) + #define CS_H GPIO.out_w1ts = (1 << TFT_CS)//;GPIO.out_w1ts = (1 << TFT_CS) #endif #else #define CS_L @@ -653,15 +653,15 @@ class TFT_eSPI : public Print { 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), + virtual void drawPixel(int32_t x, int32_t y, uint32_t color), 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); - virtual int16_t drawChar(unsigned int uniCode, int x, int y, int font), - drawChar(unsigned int uniCode, int x, int y), + virtual int16_t drawChar(uint16_t uniCode, int32_t x, int32_t y, uint8_t font), + drawChar(uint16_t uniCode, int32_t x, int32_t y), height(void), width(void); @@ -720,30 +720,30 @@ class TFT_eSPI : public Print { commandList(const uint8_t *addr); - uint8_t readcommand8(uint8_t cmd_function, uint8_t index); - uint16_t readcommand16(uint8_t cmd_function, uint8_t index); - uint32_t readcommand32(uint8_t cmd_function, uint8_t index); + uint8_t readcommand8(uint8_t cmd_function, uint8_t index = 0); + uint16_t readcommand16(uint8_t cmd_function, uint8_t index = 0); + uint32_t readcommand32(uint8_t cmd_function, uint8_t index = 0); // Read the colour of a pixel at x,y and return value in 565 format uint16_t readPixel(int32_t x0, int32_t y0); // The next functions can be used as a pair to copy screen blocks (or horizontal/vertical lines) to another location // Read a block of pixels to a data buffer, buffer is 16 bit and the array size must be at least w * h - void readRect(uint32_t x0, uint32_t y0, uint32_t w, uint32_t h, uint16_t *data); + void readRect(int32_t x0, int32_t y0, int32_t w, int32_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 pushRect(int32_t x0, int32_t y0, int32_t w, int32_t h, uint16_t *data); // 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, uint16_t *data, uint16_t transparent); + void pushImage(int32_t x0, int32_t y0, int32_t w, int32_t h, uint16_t *data); + void pushImage(int32_t x0, int32_t y0, int32_t w, int32_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); + void pushImage(int32_t x0, int32_t y0, int32_t w, int32_t h, const uint16_t *data, uint16_t transparent); + void pushImage(int32_t x0, int32_t y0, int32_t w, int32_t h, const uint16_t *data); // 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); + void pushImage(int32_t x0, int32_t y0, int32_t w, int32_t h, uint8_t *data, bool bpp8 = true); + void pushImage(int32_t x0, int32_t y0, int32_t w, int32_t h, uint8_t *data, uint8_t transparent, bool bpp8 = true); // Swap the byte order for pushImage() - corrects endianness void setSwapBytes(bool swap); @@ -768,26 +768,26 @@ class TFT_eSPI : public Print { color565(uint8_t red, uint8_t green, uint8_t blue), // Convert 8 bit red, green and blue to 16 bits 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), + int16_t drawNumber(long long_num, int32_t poX, int32_t poY, uint8_t font), + drawNumber(long long_num, int32_t poX, int32_t poY), + drawFloat(float floatNumber, uint8_t decimal, int32_t poX, int32_t poY, uint8_t font), + drawFloat(float floatNumber, uint8_t decimal, int32_t poX, int32_t poY), // Handle char arrays - drawString(const char *string, int poX, int poY, int font), - drawString(const char *string, int poX, int poY), - drawCentreString(const char *string, int dX, int poY, int font), // Deprecated, use setTextDatum() and drawString() - drawRightString(const char *string, int dX, int poY, int font), // Deprecated, use setTextDatum() and drawString() + drawString(const char *string, int32_t poX, int32_t poY, uint8_t font), + drawString(const char *string, int32_t poX, int32_t poY), + drawCentreString(const char *string, int32_t dX, int32_t poY, uint8_t font), // Deprecated, use setTextDatum() and drawString() + drawRightString(const char *string, int32_t dX, int32_t poY, uint8_t font), // Deprecated, use setTextDatum() and drawString() // Handle String type - drawString(const String& string, int poX, int poY, int font), - 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() + drawString(const String& string, int32_t poX, int32_t poY, uint8_t font), + drawString(const String& string, int32_t poX, int32_t poY), + drawCentreString(const String& string, int32_t dX, int32_t poY, uint8_t font), // Deprecated, use setTextDatum() and drawString() + drawRightString(const String& string, int32_t dX, int32_t poY, uint8_t font); // Deprecated, use setTextDatum() and drawString() - int16_t textWidth(const char *string, int font), + int16_t textWidth(const char *string, uint8_t font), textWidth(const char *string), - textWidth(const String& string, int font), + textWidth(const String& string, uint8_t font), textWidth(const String& string), fontHeight(int16_t font), fontHeight(void); @@ -852,9 +852,9 @@ class TFT_eSPI : public Print { 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 addr_row, addr_col; + int32_t _init_width, _init_height; // Display w/h as input, used by setRotation() + int32_t _width, _height; // Display w/h as modified by current rotation + int32_t addr_row, addr_col; uint32_t fontsloaded; diff --git a/examples/160 x 128/TFT_graphicstest_PDQ3/TFT_graphicstest_PDQ3.ino b/examples/160 x 128/TFT_graphicstest_PDQ3/TFT_graphicstest_PDQ3.ino index 148499e..1cb0515 100644 --- a/examples/160 x 128/TFT_graphicstest_PDQ3/TFT_graphicstest_PDQ3.ino +++ b/examples/160 x 128/TFT_graphicstest_PDQ3/TFT_graphicstest_PDQ3.ino @@ -324,7 +324,7 @@ uint32_t testHaD() uint16_t curcolor = 0; const uint8_t *cmp = &HaD_128x160[0]; - + tft.startWrite(); while (cmp < &HaD_128x160[sizeof(HaD_128x160)]) { cnt = pgm_read_byte(cmp++); @@ -335,6 +335,7 @@ uint32_t testHaD() curcolor ^= color; } + tft.endWrite(); } tft.endWrite(); @@ -409,7 +410,7 @@ uint32_t testPixels() int32_t h = tft.height(); uint32_t start = micros_start(); - + tft.startWrite(); for (uint16_t y = 0; y < h; y++) { for (uint16_t x = 0; x < w; x++) @@ -417,7 +418,7 @@ uint32_t testPixels() tft.drawPixel(x, y, tft.color565(x<<3, y<<3, x*y)); } } - + tft.endWrite(); return micros() - start; } diff --git a/examples/320 x 240/TFT_graphicstest_PDQ/TFT_graphicstest_PDQ.ino b/examples/320 x 240/TFT_graphicstest_PDQ/TFT_graphicstest_PDQ.ino index 8e03ef5..ee76936 100644 --- a/examples/320 x 240/TFT_graphicstest_PDQ/TFT_graphicstest_PDQ.ino +++ b/examples/320 x 240/TFT_graphicstest_PDQ/TFT_graphicstest_PDQ.ino @@ -434,7 +434,7 @@ uint32_t testPixels() int32_t h = tft.height(); uint32_t start = micros_start(); - + tft.startWrite(); for (uint16_t y = 0; y < h; y++) { for (uint16_t x = 0; x < w; x++) @@ -442,7 +442,7 @@ uint32_t testPixels() tft.drawPixel(x, y, tft.color565(x<<3, y<<3, x*y)); } } - + tft.endWrite(); return micros() - start; } 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 45d48bf..d0a2f1f 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 @@ -41,6 +41,8 @@ void loop(void) { tft.getSetup(user); // Serial.printf("\n[code]\n"); + +Serial.printf("TFT_eSPI ver = " + user.version) +"\n"); Serial.printf("Processor = ESP%i\n", user.esp, HEX); Serial.printf("Frequency = %i MHz\n", ESP.getCpuFreqMHz()); #ifdef ESP8266 diff --git a/library.json b/library.json index d32cf0c..4ac48a5 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "TFT_eSPI", - "version": "1.4.0", + "version": "1.4.1", "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 834703f..a73d64e 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=TFT_eSPI -version=1.4.0 +version=1.4.1 author=Bodmer maintainer=Bodmer sentence=A fast TFT graphics library for ESP8266 and ESP32 processors for the Arduino IDE From 33d93131134e7b642ddba9711fa6eedc2af3306f Mon Sep 17 00:00:00 2001 From: Bodmer Date: Thu, 31 Jan 2019 01:17:19 +0000 Subject: [PATCH 243/287] Update for ESP8266 ESP32 printf() can handle String type but ESP8266 cannot. --- .../Test and diagnostics/Read_User_Setup/Read_User_Setup.ino | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 d0a2f1f..1cf8f3f 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 @@ -41,8 +41,8 @@ void loop(void) { tft.getSetup(user); // Serial.printf("\n[code]\n"); - -Serial.printf("TFT_eSPI ver = " + user.version) +"\n"); +String ver = user.version; +Serial.println("TFT_eSPI ver = " + ver +"\n"); Serial.printf("Processor = ESP%i\n", user.esp, HEX); Serial.printf("Frequency = %i MHz\n", ESP.getCpuFreqMHz()); #ifdef ESP8266 From 091d8cd2317bce14baa0a8ed784f7d542a5396a5 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Sun, 3 Feb 2019 21:53:52 +0000 Subject: [PATCH 244/287] Fix issue #297 There is a bug in the ESP8266 Arduino core that averwrites the overlap mode setup and hold bits for the hardware driven SPI SS line. The TFT_eSPI library has been patched as a work-around. In overlap mode to maintain the required setup and hold times at 80MHz ESP8266 CPU clock the SPI rate must be 27MHz or less. At 160MHz CPU clock the SPI rate can be 40MHz. --- TFT_eSPI.cpp | 122 +++++++++++++++++++-------------------------- TFT_eSPI.h | 9 +++- library.json | 2 +- library.properties | 2 +- 4 files changed, 61 insertions(+), 74 deletions(-) diff --git a/TFT_eSPI.cpp b/TFT_eSPI.cpp index 20ae7df..0681144 100644 --- a/TFT_eSPI.cpp +++ b/TFT_eSPI.cpp @@ -48,10 +48,6 @@ 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) && !defined(ESP32_PARALLEL) if (locked) {locked = false; spi.beginTransaction(SPISettings(SPI_FREQUENCY, MSBFIRST, TFT_SPI_MODE)); CS_L;} @@ -77,6 +73,9 @@ inline void TFT_eSPI::spi_begin_read(void){ #endif CS_L; #endif +#ifdef ESP8266 + SPI1U = SPI1U_READ; +#endif } inline void TFT_eSPI::spi_end_read(void){ @@ -88,6 +87,9 @@ inline void TFT_eSPI::spi_end_read(void){ #endif CS_H; #endif +#ifdef ESP8266 + SPI1U = SPI1U_WRITE; +#endif } #if defined (TOUCH_CS) && defined (SPI_TOUCH_FREQUENCY) // && !defined(ESP32_PARALLEL) @@ -279,7 +281,8 @@ void TFT_eSPI::init(uint8_t tc) 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(ESP32_PARALLEL) #if defined (TFT_MOSI) && !defined (TFT_SPI_OVERLAP) @@ -313,7 +316,7 @@ void TFT_eSPI::init(uint8_t tc) 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 @@ -1822,8 +1825,9 @@ void TFT_eSPI::drawRect(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t col drawFastHLine(x, y, w, color); drawFastHLine(x, y + h - 1, w, color); - drawFastVLine(x, y, h, color); - drawFastVLine(x + w - 1, y, h, color); + // Avoid drawing corner pixels twice + drawFastVLine(x, y+1, h-2, color); + drawFastVLine(x + w - 1, y+1, h-2, color); inTransaction = false; spi_end(); @@ -2420,8 +2424,7 @@ void TFT_eSPI::drawChar(int32_t x, int32_t y, unsigned char c, uint32_t color, u #if defined (ESP8266) && !defined (ILI9488_DRIVER) color = (color >> 8) | (color << 8); bg = (bg >> 8) | (bg << 8); - uint32_t spimask = ~((SPIMMOSI << SPILMOSI) | (SPIMMISO << SPILMISO)); - SPI1U1 = (SPI1U1 & spimask) | (15 << SPILMOSI) | (15 << SPILMISO); + for (int8_t j = 0; j < 8; j++) { for (int8_t k = 0; k < 5; k++ ) { if (column[k] & mask) { @@ -2669,10 +2672,8 @@ void TFT_eSPI::setWindow(int32_t xs, int32_t ys, int32_t xe, int32_t ye) // Column addr set DC_C; - uint32_t mask = ~((SPIMMOSI << SPILMOSI) | (SPIMMISO << SPILMISO)); - mask = SPI1U1 & mask; - - SPI1U1 = mask | (CMD_BITS << SPILMOSI) | (CMD_BITS << SPILMISO); + SPI1U = SPI1U_WRITE; + SPI1U1 = (CMD_BITS << SPILMOSI) | (CMD_BITS << SPILMISO); SPI1W0 = TFT_CASET; SPI1CMD |= SPIBUSY; @@ -2684,7 +2685,7 @@ void TFT_eSPI::setWindow(int32_t xs, int32_t ys, int32_t xe, int32_t ye) DC_D; - SPI1U1 = mask | (31 << SPILMOSI) | (31 << SPILMISO); + SPI1U1 = (31 << SPILMOSI) | (31 << SPILMISO); // Load the two coords as a 32 bit value and shift in one go SPI1W0 = (xs >> 8) | (uint16_t)(xs << 8) | ((uint8_t)(xe >> 8)<<16 | (xe << 24)); SPI1CMD |= SPIBUSY; @@ -2693,7 +2694,7 @@ void TFT_eSPI::setWindow(int32_t xs, int32_t ys, int32_t xe, int32_t ye) // Row addr set DC_C; - SPI1U1 = mask | (CMD_BITS << SPILMOSI) | (CMD_BITS << SPILMISO); + SPI1U1 = (CMD_BITS << SPILMOSI) | (CMD_BITS << SPILMISO); SPI1W0 = TFT_PASET; SPI1CMD |= SPIBUSY; @@ -2701,7 +2702,7 @@ void TFT_eSPI::setWindow(int32_t xs, int32_t ys, int32_t xe, int32_t ye) DC_D; - SPI1U1 = mask | (31 << SPILMOSI) | (31 << SPILMISO); + SPI1U1 = (31 << SPILMOSI) | (31 << SPILMISO); // Load the two coords as a 32 bit value and shift in one go SPI1W0 = (ys >> 8) | (uint16_t)(ys << 8) | ((uint8_t)(ye >> 8)<<16 | (ye << 24)); SPI1CMD |= SPIBUSY; @@ -2710,13 +2711,14 @@ void TFT_eSPI::setWindow(int32_t xs, int32_t ys, int32_t xe, int32_t ye) // write to RAM DC_C; - SPI1U1 = mask | (CMD_BITS << SPILMOSI) | (CMD_BITS << SPILMISO); + SPI1U1 = (CMD_BITS << SPILMOSI) | (CMD_BITS << SPILMISO); SPI1W0 = TFT_RAMWR; SPI1CMD |= SPIBUSY; while(SPI1CMD & SPIBUSY) {} DC_D; + SPI1U1 = (15 << SPILMOSI) | (15 << SPILMISO); //spi_end(); } @@ -2732,10 +2734,8 @@ void TFT_eSPI::setWindow(int32_t xs, int32_t ys, int32_t xe, int32_t ye) // Column addr set DC_C; - uint32_t mask = ~((SPIMMOSI << SPILMOSI) | (SPIMMISO << SPILMISO)); - mask = SPI1U1 & mask; - - SPI1U1 = mask | (CMD_BITS << SPILMOSI) | (CMD_BITS << SPILMISO); + SPI1U = SPI1U_WRITE; + SPI1U1 = (CMD_BITS << SPILMOSI) | (CMD_BITS << SPILMISO); SPI1W0 = TFT_CASET<<8; SPI1CMD |= SPIBUSY; @@ -2749,7 +2749,7 @@ void TFT_eSPI::setWindow(int32_t xs, int32_t ys, int32_t xe, int32_t ye) // Row addr set DC_C; - SPI1U1 = mask | (CMD_BITS << SPILMOSI) | (CMD_BITS << SPILMISO); + SPI1U1 = (CMD_BITS << SPILMOSI) | (CMD_BITS << SPILMISO); SPI1W0 = TFT_PASET<<8; SPI1CMD |= SPIBUSY; @@ -2763,7 +2763,7 @@ void TFT_eSPI::setWindow(int32_t xs, int32_t ys, int32_t xe, int32_t ye) // write to RAM DC_C; - SPI1U1 = mask | (CMD_BITS << SPILMOSI) | (CMD_BITS << SPILMISO); + SPI1U1 = (CMD_BITS << SPILMOSI) | (CMD_BITS << SPILMISO); SPI1W0 = TFT_RAMWR<<8; SPI1CMD |= SPIBUSY; while(SPI1CMD & SPIBUSY) {} @@ -2780,9 +2780,8 @@ void TFT_eSPI::setWindow(int32_t x0, int32_t y0, int32_t x1, int32_t y1) { //spi_begin(); // Must be called before setWimdow - uint32_t mask = ~((SPIMMOSI << SPILMOSI) | (SPIMMISO << SPILMISO)); - mask = SPI1U1 & mask; - SPI1U1 = mask | (CMD_BITS << SPILMOSI) | (CMD_BITS << SPILMISO); + SPI1U = SPI1U_WRITE; + SPI1U1 = (CMD_BITS << SPILMOSI) | (CMD_BITS << SPILMISO); // Column addr set DC_C; @@ -2932,10 +2931,8 @@ void TFT_eSPI::readAddrWindow(int32_t xs, int32_t ys, int32_t w, int32_t h) // Column addr set DC_C; - uint32_t mask = ~((SPIMMOSI << SPILMOSI) | (SPIMMISO << SPILMISO)); - mask = SPI1U1 & mask; - - SPI1U1 = mask | (CMD_BITS << SPILMOSI) | (CMD_BITS << SPILMISO); + SPI1U = SPI1U_WRITE; + SPI1U1 = (CMD_BITS << SPILMOSI) | (CMD_BITS << SPILMISO); SPI1W0 = TFT_CASET; SPI1CMD |= SPIBUSY; @@ -2943,7 +2940,7 @@ void TFT_eSPI::readAddrWindow(int32_t xs, int32_t ys, int32_t w, int32_t h) DC_D; - SPI1U1 = mask | (31 << SPILMOSI) | (31 << SPILMISO); + SPI1U1 = (31 << SPILMOSI) | (31 << SPILMISO); // Load the two coords as a 32 bit value and shift in one go SPI1W0 = (xs >> 8) | (uint16_t)(xs << 8) | ((uint8_t)(xe >> 8)<<16 | (xe << 24)); SPI1CMD |= SPIBUSY; @@ -2952,7 +2949,7 @@ void TFT_eSPI::readAddrWindow(int32_t xs, int32_t ys, int32_t w, int32_t h) // Row addr set DC_C; - SPI1U1 = mask | (CMD_BITS << SPILMOSI) | (CMD_BITS << SPILMISO); + SPI1U1 = (CMD_BITS << SPILMOSI) | (CMD_BITS << SPILMISO); SPI1W0 = TFT_PASET; SPI1CMD |= SPIBUSY; @@ -2960,7 +2957,7 @@ void TFT_eSPI::readAddrWindow(int32_t xs, int32_t ys, int32_t w, int32_t h) DC_D; - SPI1U1 = mask | (31 << SPILMOSI) | (31 << SPILMISO); + SPI1U1 = (31 << SPILMOSI) | (31 << SPILMISO); // Load the two coords as a 32 bit value and shift in one go SPI1W0 = (ys >> 8) | (uint16_t)(ys << 8) | ((uint8_t)(ye >> 8)<<16 | (ye << 24)); SPI1CMD |= SPIBUSY; @@ -2969,7 +2966,7 @@ void TFT_eSPI::readAddrWindow(int32_t xs, int32_t ys, int32_t w, int32_t h) // read from RAM DC_C; - SPI1U1 = mask | (CMD_BITS << SPILMOSI) | (CMD_BITS << SPILMISO); + SPI1U1 = (CMD_BITS << SPILMOSI) | (CMD_BITS << SPILMISO); SPI1W0 = TFT_RAMRD; SPI1CMD |= SPIBUSY; while(SPI1CMD & SPIBUSY) {} @@ -3043,14 +3040,13 @@ void TFT_eSPI::drawPixel(int32_t x, int32_t y, uint32_t color) spi_begin(); - uint32_t mask = ~((SPIMMOSI << SPILMOSI) | (SPIMMISO << SPILMISO)); - mask = SPI1U1 & mask; + SPI1U = SPI1U_WRITE; // No need to send x if it has not changed (speeds things up) if (addr_col != x) { DC_C; - SPI1U1 = mask | (CMD_BITS << SPILMOSI) | (CMD_BITS << SPILMISO); + SPI1U1 = (CMD_BITS << SPILMOSI) | (CMD_BITS << SPILMISO); SPI1W0 = TFT_CASET<<(CMD_BITS + 1 - 8); SPI1CMD |= SPIBUSY; while(SPI1CMD & SPIBUSY) {} @@ -3061,7 +3057,7 @@ void TFT_eSPI::drawPixel(int32_t x, int32_t y, uint32_t color) uint8_t cBin[] = { 0, (uint8_t) (x>>8), 0, (uint8_t) (x>>0)}; spi.writePattern(&cBin[0], 4, 2); #else - SPI1U1 = mask | (31 << SPILMOSI) | (31 << SPILMISO); + SPI1U1 = (31 << SPILMOSI) | (31 << SPILMISO); // Load the two coords as a 32 bit value and shift in one go uint32_t xswap = (x >> 8) | (uint16_t)(x << 8); SPI1W0 = xswap | (xswap << 16); @@ -3077,7 +3073,7 @@ void TFT_eSPI::drawPixel(int32_t x, int32_t y, uint32_t color) DC_C; - SPI1U1 = mask | (CMD_BITS << SPILMOSI) | (CMD_BITS << SPILMISO); + SPI1U1 = (CMD_BITS << SPILMOSI) | (CMD_BITS << SPILMISO); SPI1W0 = TFT_PASET<<(CMD_BITS + 1 - 8); SPI1CMD |= SPIBUSY; @@ -3089,7 +3085,7 @@ void TFT_eSPI::drawPixel(int32_t x, int32_t y, uint32_t color) uint8_t cBin[] = { 0, (uint8_t) (y>>8), 0, (uint8_t) (y>>0)}; spi.writePattern(&cBin[0], 4, 2); #else - SPI1U1 = mask | (31 << SPILMOSI) | (31 << SPILMISO); + SPI1U1 = (31 << SPILMOSI) | (31 << SPILMISO); // Load the two coords as a 32 bit value and shift in one go uint32_t yswap = (y >> 8) | (uint16_t)(y << 8); SPI1W0 = yswap | (yswap << 16); @@ -3102,7 +3098,7 @@ void TFT_eSPI::drawPixel(int32_t x, int32_t y, uint32_t color) DC_C; - SPI1U1 = mask | (CMD_BITS << SPILMOSI) | (CMD_BITS << SPILMISO); + SPI1U1 = (CMD_BITS << SPILMOSI) | (CMD_BITS << SPILMISO); SPI1W0 = TFT_RAMWR<<(CMD_BITS + 1 - 8); SPI1CMD |= SPIBUSY; @@ -3113,7 +3109,7 @@ void TFT_eSPI::drawPixel(int32_t x, int32_t y, uint32_t color) #if defined (ILI9488_DRIVER) tft_Write_16(color); #else - SPI1U1 = mask | (15 << SPILMOSI) | (15 << SPILMISO); + SPI1U1 = (15 << SPILMOSI) | (15 << SPILMISO); SPI1W0 = (color >> 8) | (color << 8); SPI1CMD |= SPIBUSY; @@ -3134,9 +3130,8 @@ void TFT_eSPI::drawPixel(int32_t x, int32_t y, uint32_t color) spi_begin(); - uint32_t mask = ~((SPIMMOSI << SPILMOSI) | (SPIMMISO << SPILMISO)); - mask = SPI1U1 & mask; - SPI1U1 = mask | (CMD_BITS << SPILMOSI) | (CMD_BITS << SPILMISO); + SPI1U = SPI1U_WRITE; + SPI1U1 = (CMD_BITS << SPILMOSI) | (CMD_BITS << SPILMISO); // No need to send x if it has not changed (speeds things up) if (addr_col != x) { DC_C; @@ -3404,9 +3399,9 @@ void TFT_eSPI::pushColors(uint16_t *data, uint32_t len, bool swap) uint32_t color[8]; - uint32_t mask = ~((SPIMMOSI << SPILMOSI) | (SPIMMISO << SPILMISO)); + SPI1U = SPI1U_WRITE; + SPI1U1 = (255 << SPILMOSI) | (255 << SPILMISO); - SPI1U1 = (SPI1U1 & mask) | (255 << SPILMOSI) | (255 << SPILMISO); while(len>15) { @@ -3461,7 +3456,7 @@ void TFT_eSPI::pushColors(uint16_t *data, uint32_t len, bool swap) memcpy(color,data,len<<1); } while(SPI1CMD & SPIBUSY) {} - SPI1U1 = (SPI1U1 & mask) | (bits << SPILMOSI) | (bits << SPILMISO); + SPI1U1 = (bits << SPILMOSI) | (bits << SPILMISO); SPI1W0 = color[0]; SPI1W1 = color[1]; SPI1W2 = color[2]; @@ -3572,9 +3567,6 @@ void TFT_eSPI::drawLine(int32_t x0, int32_t y0, int32_t x1, int32_t y1, uint32_t spi_begin(); - uint32_t mask = ~((SPIMMOSI << SPILMOSI) | (SPIMMISO << SPILMISO)); - mask = (SPI1U1 & mask) | (15 << SPILMOSI) | (15 << SPILMISO); - SPI1U = SPIUMOSI | SPIUSSE; int16_t swapped_color = (color >> 8) | (color << 8); if (steep) // y increments every iteration (y0 is x-axis, and x0 is y-axis) @@ -3593,7 +3585,6 @@ void TFT_eSPI::drawLine(int32_t x0, int32_t y0, int32_t x1, int32_t y1, uint32_t if (x0 > x1) {spi_end(); return;} setWindow(y0, x0, y0, _height); - SPI1U1 = mask; SPI1W0 = swapped_color; for (; x0 <= x1; x0++) { while(SPI1CMD & SPIBUSY) {} @@ -3606,7 +3597,6 @@ void TFT_eSPI::drawLine(int32_t x0, int32_t y0, int32_t x1, int32_t y1, uint32_t err += dx; while(SPI1CMD & SPIBUSY) {} setWindow(y0, x0+1, y0, _height); - SPI1U1 = mask; SPI1W0 = swapped_color; } } @@ -3627,7 +3617,6 @@ void TFT_eSPI::drawLine(int32_t x0, int32_t y0, int32_t x1, int32_t y1, uint32_t if (x0 > x1) {spi_end(); return;} setWindow(x0, y0, _width, y0); - SPI1U1 = mask; SPI1W0 = swapped_color; for (; x0 <= x1; x0++) { while(SPI1CMD & SPIBUSY) {} @@ -3640,14 +3629,12 @@ void TFT_eSPI::drawLine(int32_t x0, int32_t y0, int32_t x1, int32_t y1, uint32_t err += dx; while(SPI1CMD & SPIBUSY) {} setWindow(x0+1, y0, _width, y0); - SPI1U1 = mask; SPI1W0 = swapped_color; } } } while(SPI1CMD & SPIBUSY) {} - SPI1U = SPIUMOSI | SPIUDUPLEX | SPIUSSE; spi_end(); } @@ -3699,7 +3686,6 @@ 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 setWindow() SPI1W0 = (color >> 8) | (color << 8); SPI1CMD |= SPIBUSY; while(SPI1CMD & SPIBUSY) {} @@ -3764,7 +3750,6 @@ 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 setWindow() SPI1W0 = (color >> 8) | (color << 8); SPI1CMD |= SPIBUSY; while(SPI1CMD & SPIBUSY) {} @@ -4809,9 +4794,8 @@ void writeBlock(uint16_t color, uint32_t repeat) { uint16_t color16 = (color >> 8) | (color << 8); uint32_t color32 = color16 | color16 << 16; - uint32_t mask = ~(SPIMMOSI << SPILMOSI); - mask = SPI1U1 & mask; - SPI1U = SPIUMOSI | SPIUSSE; + + SPI1U = SPI1U_WRITE; SPI1W0 = color32; SPI1W1 = color32; @@ -4840,7 +4824,7 @@ void writeBlock(uint16_t color, uint32_t repeat) } if (repeat > 31) { - SPI1U1 = mask | (511 << SPILMOSI); + SPI1U1 = (511 << SPILMOSI); while(repeat>31) { #if defined SPI_FREQUENCY && (SPI_FREQUENCY == 80000000) @@ -4856,12 +4840,11 @@ void writeBlock(uint16_t color, uint32_t repeat) if (repeat) { repeat = (repeat << 4) - 1; - SPI1U1 = mask | (repeat << SPILMOSI); + SPI1U1 = (repeat << SPILMOSI); SPI1CMD |= SPIBUSY; while(SPI1CMD & SPIBUSY) {} } - SPI1U = SPIUMOSI | SPIUDUPLEX | SPIUSSE; } #elif defined (ILI9488_DRIVER) @@ -4870,9 +4853,7 @@ void writeBlock(uint16_t color, uint32_t repeat) void writeBlock(uint16_t color, uint32_t repeat) { - uint32_t mask = ~(SPIMMOSI << SPILMOSI); - mask = SPI1U1 & mask; - SPI1U = SPIUMOSI | SPIUSSE; + SPI1U = SPI1U_WRITE; // Split out the colours uint8_t r = (color & 0xF800)>>8; @@ -4912,7 +4893,7 @@ void writeBlock(uint16_t color, uint32_t repeat) if (repeat > 20) { - SPI1U1 = mask | (503 << SPILMOSI); + SPI1U1 = (503 << SPILMOSI); while(repeat>20) { while(SPI1CMD & SPIBUSY) {} @@ -4925,12 +4906,11 @@ void writeBlock(uint16_t color, uint32_t repeat) if (repeat) { repeat = (repeat * 24) - 1; - SPI1U1 = mask | (repeat << SPILMOSI); + SPI1U1 = (repeat << SPILMOSI); SPI1CMD |= SPIBUSY; while(SPI1CMD & SPIBUSY) {} } - SPI1U = SPIUMOSI | SPIUDUPLEX | SPIUSSE; } #else // Now the code for ESP32 and ILI9488 diff --git a/TFT_eSPI.h b/TFT_eSPI.h index 66546c7..512bedc 100644 --- a/TFT_eSPI.h +++ b/TFT_eSPI.h @@ -15,7 +15,7 @@ #ifndef _TFT_eSPIH_ #define _TFT_eSPIH_ -#define TFT_ESPI_VERSION "1.4.1" +#define TFT_ESPI_VERSION "1.4.2" //#define ESP32 //Just used to test ESP32 options @@ -172,6 +172,13 @@ #if defined (TFT_SPI_OVERLAP) #undef TFT_CS + #define SPI1U_WRITE (SPIUMOSI | SPIUSSE | SPIUCSSETUP | SPIUCSHOLD) + #define SPI1U_READ (SPIUMOSI | SPIUSSE | SPIUCSSETUP | SPIUCSHOLD | SPIUDUPLEX) +#else + #ifdef ESP8266 + #define SPI1U_WRITE (SPIUMOSI | SPIUSSE) + #define SPI1U_READ (SPIUMOSI | SPIUSSE | SPIUDUPLEX) + #endif #endif #ifndef TFT_CS diff --git a/library.json b/library.json index 4ac48a5..ac3228d 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "TFT_eSPI", - "version": "1.4.1", + "version": "1.4.2", "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 a73d64e..9de9fc2 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=TFT_eSPI -version=1.4.1 +version=1.4.2 author=Bodmer maintainer=Bodmer sentence=A fast TFT graphics library for ESP8266 and ESP32 processors for the Arduino IDE From 7479f7e680547ca449c26a9839ef191b40c19f9b Mon Sep 17 00:00:00 2001 From: Bodmer Date: Mon, 4 Feb 2019 01:48:53 +0000 Subject: [PATCH 245/287] Fix transparent Sprite rendering --- TFT_eSPI.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TFT_eSPI.cpp b/TFT_eSPI.cpp index 0681144..0902360 100644 --- a/TFT_eSPI.cpp +++ b/TFT_eSPI.cpp @@ -60,7 +60,7 @@ inline void TFT_eSPI::spi_end(void){ #if defined (SPI_HAS_TRANSACTION) && defined (SUPPORT_TRANSACTIONS) && !defined(ESP32_PARALLEL) if(!inTransaction) {if (!locked) {locked = true; CS_H; spi.endTransaction();}} #else - CS_H; + if(!inTransaction) CS_H; #endif } From 61dfb2b4acb2ffbf3a397d809317d6f7d5710807 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Fri, 8 Feb 2019 18:25:55 +0000 Subject: [PATCH 246/287] Extended character set suport for Adafruit_GFX format fonts Supports Adafruit_GFX compatible font format with characters in the range 32-255. Note that the font rendering functions expect UTF-8 encoded characters/strings. --- Extensions/Smooth_font.cpp | 4 + TFT_eSPI.cpp | 142 +- .../Local_Custom_Fonts/Local_Custom_Fonts.ino | 123 + examples/Generic/Local_Custom_Fonts/MyFont.h | 3366 +++++++++++++++++ library.json | 2 +- library.properties | 2 +- 6 files changed, 3618 insertions(+), 21 deletions(-) create mode 100644 examples/Generic/Local_Custom_Fonts/Local_Custom_Fonts.ino create mode 100644 examples/Generic/Local_Custom_Fonts/MyFont.h diff --git a/Extensions/Smooth_font.cpp b/Extensions/Smooth_font.cpp index 1952add..855aa62 100644 --- a/Extensions/Smooth_font.cpp +++ b/Extensions/Smooth_font.cpp @@ -246,6 +246,7 @@ void TFT_eSPI::unloadFont( void ) ** Function name: decodeUTF8 ** Description: Line buffer UTF-8 decoder with fall-back to extended ASCII *************************************************************************************x*/ +/* Function moved to TFT_eSPI.cpp #define DECODE_UTF8 uint16_t TFT_eSPI::decodeUTF8(uint8_t *buf, uint16_t *index, uint16_t remaining) { @@ -273,11 +274,13 @@ uint16_t TFT_eSPI::decodeUTF8(uint8_t *buf, uint16_t *index, uint16_t remaining) return c; // fall-back to extended ASCII } +*/ /*************************************************************************************** ** Function name: decodeUTF8 ** Description: Serial UTF-8 decoder with fall-back to extended ASCII *************************************************************************************x*/ +/* Function moved to TFT_eSPI.cpp uint16_t TFT_eSPI::decodeUTF8(uint8_t c) { @@ -329,6 +332,7 @@ uint16_t TFT_eSPI::decodeUTF8(uint8_t c) decoderState = 0; return (uint16_t)c; // fall-back to extended ASCII } +*/ diff --git a/TFT_eSPI.cpp b/TFT_eSPI.cpp index 0902360..5e24cd8 100644 --- a/TFT_eSPI.cpp +++ b/TFT_eSPI.cpp @@ -13,6 +13,7 @@ Bodmer: Added RPi 16 bit display support ****************************************************/ + #include "TFT_eSPI.h" #if defined (ESP32) @@ -85,7 +86,7 @@ inline void TFT_eSPI::spi_end_read(void){ #if !defined(ESP32_PARALLEL) spi.setFrequency(SPI_FREQUENCY); #endif - CS_H; + if(!inTransaction) CS_H; #endif #ifdef ESP8266 SPI1U = SPI1U_WRITE; @@ -342,17 +343,6 @@ void TFT_eSPI::init(uint8_t tc) writecommand(TFT_SWRST); // Software reset #endif -#if defined (TFT_BL) && defined (TFT_BACKLIGHT_ON) - digitalWrite(TFT_BL, TFT_BACKLIGHT_ON); - pinMode(TFT_BL, OUTPUT); -#else - #if defined (TFT_BL) && defined (M5STACK) - // Turn on the back-light LED - digitalWrite(TFT_BL, HIGH); - pinMode(TFT_BL, OUTPUT); - #endif -#endif - spi_end(); delay(150); // Wait for reset to complete @@ -407,6 +397,17 @@ void TFT_eSPI::init(uint8_t tc) spi_end(); setRotation(rotation); + +#if defined (TFT_BL) && defined (TFT_BACKLIGHT_ON) + digitalWrite(TFT_BL, TFT_BACKLIGHT_ON); + pinMode(TFT_BL, OUTPUT); +#else + #if defined (TFT_BL) && defined (M5STACK) + // Turn on the back-light LED + digitalWrite(TFT_BL, HIGH); + pinMode(TFT_BL, OUTPUT); + #endif +#endif } @@ -2324,7 +2325,7 @@ int16_t TFT_eSPI::textWidth(const char *string, uint8_t font) { while (*string) { - uniCode = *(string++); + uniCode = decodeUTF8(*string++); if ((uniCode >= (uint8_t)pgm_read_byte(&gfxFont->first)) && (uniCode <= (uint8_t)pgm_read_byte(&gfxFont->last ))) { uniCode -= pgm_read_byte(&gfxFont->first); @@ -3895,6 +3896,95 @@ void TFT_eSPI::invertDisplay(boolean i) } +/*************************************************************************************** +** Function name: decodeUTF8 +** Description: Serial UTF-8 decoder with fall-back to extended ASCII +*************************************************************************************x*/ +#define DECODE_UTF8 // Test only, comment out to stop decoding +uint16_t TFT_eSPI::decodeUTF8(uint8_t c) +{ +#ifdef DECODE_UTF8 + // 7 bit Unicode Code Point + if ((c & 0x80) == 0x00) { + decoderState = 0; + return (uint16_t)c; + } + + if (decoderState == 0) + { + // 11 bit Unicode Code Point + if ((c & 0xE0) == 0xC0) + { + decoderBuffer = ((c & 0x1F)<<6); + decoderState = 1; + return 0; + } + + // 16 bit Unicode Code Point + if ((c & 0xF0) == 0xE0) + { + decoderBuffer = ((c & 0x0F)<<12); + decoderState = 2; + return 0; + } + // 21 bit Unicode Code Point 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; + } + } + + decoderState = 0; +#endif + + return (uint16_t)c; // fall-back to extended ASCII +} + + +/*************************************************************************************** +** Function name: decodeUTF8 +** Description: Line buffer UTF-8 decoder with fall-back to extended ASCII +*************************************************************************************x*/ +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)) + { + c = ((c & 0x0F)<<12) | ((buf[(*index)++]&0x3F)<<6); + return c | ((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: write ** Description: draw characters piped through serial stream @@ -4008,6 +4098,8 @@ size_t TFT_eSPI::write(uint8_t utf8) } // Custom GFX font else { + uniCode = (uint8_t)decodeUTF8(utf8); + if (!uniCode) return 1; if(utf8 == '\n') { cursor_x = 0; @@ -4046,14 +4138,17 @@ size_t TFT_eSPI::write(uint8_t utf8) ** Function name: drawChar ** Description: draw a Unicode onto the screen ***************************************************************************************/ -int16_t TFT_eSPI::drawChar(uint16_t uniCode, int32_t x, int32_t y) +int16_t TFT_eSPI::drawChar(uint16_t utf8, int32_t x, int32_t y) { - return drawChar(uniCode, x, y, textfont); + return drawChar(utf8, x, y, textfont); } -int16_t TFT_eSPI::drawChar(uint16_t uniCode, int32_t x, int32_t y, uint8_t font) +int16_t TFT_eSPI::drawChar(uint16_t utf8, int32_t x, int32_t y, uint8_t font) { + uint16_t uniCode = decodeUTF8(utf8); + if (!uniCode) return 0; + if (font==1) { #ifdef LOAD_GLCD @@ -4458,7 +4553,12 @@ int16_t TFT_eSPI::drawString(const char *string, int32_t poX, int32_t poY, uint8 { cheight = (glyph_ab + glyph_bb) * textsize; // Get the offset for the first character only to allow for negative offsets - uint8_t c2 = *string; + uint8_t c2 = 0; + uint16_t len = strlen(string); + uint16_t n = 0; + + while (n < len && c2 == 0) c2 = decodeUTF8((uint8_t*)string, &n, len - n); + if((c2 >= pgm_read_byte(&gfxFont->first)) && (c2 <= pgm_read_byte(&gfxFont->last) )) { c2 -= pgm_read_byte(&gfxFont->first); @@ -4498,8 +4598,12 @@ int16_t TFT_eSPI::drawString(const char *string, int32_t poX, int32_t poY, uint8 } else #endif - while (*string) sumX += drawChar(*(string++), poX+sumX, poY, font); - +Serial.print("sumX="); + while (*string) { + sumX += drawChar(*(string++), poX+sumX, poY, font); + Serial.print(sumX); + } +Serial.println(); //vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv DEBUG vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv // Switch on debugging for the padding areas //#define PADDING_DEBUG diff --git a/examples/Generic/Local_Custom_Fonts/Local_Custom_Fonts.ino b/examples/Generic/Local_Custom_Fonts/Local_Custom_Fonts.ino new file mode 100644 index 0000000..b26f11e --- /dev/null +++ b/examples/Generic/Local_Custom_Fonts/Local_Custom_Fonts.ino @@ -0,0 +1,123 @@ +/* + Example for TFT_eSPI library + + This example shows the use of a Adafruit_GFX custom font with a + character code range of 32 - 255, this means accented characters + (amongst others) are available. + + The custom font file is attached to this sketch as a header file. The + font data has been created following the instructions here: + https://www.youtube.com/watch?v=L8MmTISmwZ8 + + Note that online converters for Adafruit_GFX compatible fonts are + available but these typically only use characters in the range 32-127, + and thus do not include the accented characters. These online converters + can however still be used with this sketch but the example characters + used must be changed. + + The Arduino IDE uses UTF8 encoding for these characters. The TFT_eSPI + library also expects characters in the range 128 to 255 to be UTF-8 + encoded. See link here for details: + + https://playground.arduino.cc/Code/UTF-8 + + To sumarise, UTF-8 characters are encoded as mor than 1 byte so care must + be taken: + + char c = 'µ'; // Wrong + char bad[4] = "5µA"; // Wrong + char good[] = "5µA"; // Good + String okay = "5µA"; // Good + + Created by Bodmer 08/02/19 + + Make sure LOAD_GFXFF is defined in the used User_Setup file + within the library folder. + + ######################################################################### + ###### DON'T FORGET TO UPDATE THE User_Setup.h FILE IN THE LIBRARY ###### + ###### TO SELECT YOUR DISPLAY TYPE, PINS USED AND ENABLE FONTS ###### + ######################################################################### +*/ + +#define TEST_TEXT "ßäöü ñâàå" // Text that will be printed on screen in the font +//#define TEST_TEXT "Hello" // Text that will be printed on screen in the font + +#include "SPI.h" +#include "TFT_eSPI.h" +#include "TFT_eFEX.h" + +// The custom font file attached to this sketch must be included +#include "MyFont.h" + +// Stock font and GFXFF reference handle +#define GFXFF 1 + +// Easily remembered name for the font +#define MYFONT32 &myFont32pt8b + +// Use hardware SPI +TFT_eSPI tft = TFT_eSPI(); +TFT_eFEX fex = TFT_eFEX(&tft); + +void setup(void) { + + Serial.begin(250000); + + tft.begin(); + + tft.setRotation(1); + +} + +void loop() { + + // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + // Show custom fonts + // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + // Where font sizes increase the screen is not cleared as the larger fonts overwrite + // the smaller one with the background colour. + + // We can set the text datum to be Top, Middle, Bottom vertically and Left, Centre + // and Right horizontally. These are the text datums that can be used: + // TL_DATUM = Top left (default) + // TC_DATUM = Top centre + // TR_DATUM = Top right + // ML_DATUM = Middle left + // MC_DATUM = Middle centre <<< This is used below + // MR_DATUM = Middle right + // BL_DATUM = Bottom left + // BC_DATUM = Bottom centre + // BR_DATUM = Bottom right + // L_BASELINE = Left character baseline (Line the 'A' character would sit on) + // C_BASELINE = Centre character baseline + // R_BASELINE = Right character baseline + + //Serial.println(); + + // Set text datum to middle centre (MC_DATUM) + tft.setTextDatum(MC_DATUM); + + // Set text colour to white with black background + // Unlike the stock Adafruit_GFX library, the TFT_eSPI library DOES optionally draw + // the background colour for the custom and Free Fonts when using drawString() + tft.setTextColor(TFT_WHITE, TFT_BLACK); // White characters on black background + //tft.setTextColor(TFT_WHITE); // or white characters, no background + + tft.fillScreen(TFT_BLUE); // Clear screen + tft.setFreeFont(MYFONT32); // Select the font + tft.drawString("MyFont 32", 160, 60, GFXFF); // Print the name of the font + tft.setFreeFont(MYFONT32); // Select the font + tft.drawString(TEST_TEXT, 160, 140, GFXFF); // Print the test text in the custom font + delay(2000); +fex.screenServer(); + // Setting textDatum does nothing when using tft.print + tft.fillScreen(TFT_BLUE); // Clear screen + tft.setCursor(0,60); // To be compatible with Adafruit_GFX the cursor datum is always bottom left + tft.print("âäàå"); // Using tft.print means text background is NEVER rendered + delay(2000); + + // Reset text padding to zero (default) + tft.setTextPadding(0); +} diff --git a/examples/Generic/Local_Custom_Fonts/MyFont.h b/examples/Generic/Local_Custom_Fonts/MyFont.h new file mode 100644 index 0000000..da5ee55 --- /dev/null +++ b/examples/Generic/Local_Custom_Fonts/MyFont.h @@ -0,0 +1,3366 @@ +// If you follow the tutorial here: +// https://www.youtube.com/watch?v=L8MmTISmwZ8 +// do not forget the names must be edited to remove the Windows file path. + +// I had to edit lines 8, 3135, 3361-3363 in this file + + +const uint8_t myFont32pt8bBitmaps[] PROGMEM = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0xBF, 0xCF, 0xE7, 0xF3, + 0xF9, 0xFC, 0xFE, 0x7F, 0x3F, 0x9F, 0x8F, 0xC7, 0xE1, 0xF0, 0xF8, 0x7C, + 0x3E, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xF8, 0xFF, 0xC3, 0xFF, 0xFF, 0xC3, 0xFF, 0xFF, 0xC3, 0xFF, + 0xFF, 0xC3, 0xFF, 0xFF, 0xC3, 0xFF, 0xFF, 0xC3, 0xFF, 0xFF, 0xC3, 0xFF, + 0xFF, 0xC3, 0xFF, 0x7F, 0xC3, 0xFF, 0x7F, 0xC1, 0xFE, 0x7F, 0x81, 0xFE, + 0x7F, 0x81, 0xFE, 0x7F, 0x81, 0xFE, 0x3F, 0x81, 0xFE, 0x3F, 0x80, 0xFE, + 0x3F, 0x80, 0xFC, 0x00, 0x1F, 0xC0, 0xFE, 0x00, 0x1F, 0xC0, 0x7F, 0x00, + 0x0F, 0xE0, 0x3F, 0x80, 0x07, 0xF0, 0x1F, 0xC0, 0x03, 0xF8, 0x1F, 0xE0, + 0x01, 0xFC, 0x0F, 0xE0, 0x01, 0xFC, 0x07, 0xF0, 0x00, 0xFE, 0x03, 0xF8, + 0x00, 0x7F, 0x01, 0xFC, 0x00, 0x3F, 0x81, 0xFE, 0x00, 0x1F, 0x80, 0xFE, + 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x7F, 0x01, 0xFC, 0x00, 0x3F, 0x81, + 0xFC, 0x00, 0x3F, 0x80, 0xFE, 0x00, 0x1F, 0xC0, 0x7F, 0x00, 0x0F, 0xE0, + 0x3F, 0x80, 0x07, 0xF0, 0x1F, 0xC0, 0x03, 0xF8, 0x1F, 0xC0, 0x03, 0xF8, + 0x0F, 0xE0, 0x01, 0xFC, 0x07, 0xF0, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC3, + 0xF0, 0x1F, 0xC0, 0x03, 0xF8, 0x0F, 0xE0, 0x01, 0xFC, 0x07, 0xF0, 0x00, + 0xFE, 0x03, 0xF8, 0x00, 0x7F, 0x03, 0xF8, 0x00, 0x7F, 0x01, 0xFC, 0x00, + 0x3F, 0x80, 0xFE, 0x00, 0x1F, 0xC0, 0x7F, 0x00, 0x0F, 0xE0, 0x3F, 0x80, + 0x07, 0xF0, 0x3F, 0x80, 0x07, 0xF0, 0x1F, 0xC0, 0x00, 0x00, 0x07, 0xC0, + 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x0F, 0xFC, 0x00, + 0x00, 0xFF, 0xFE, 0x00, 0x0F, 0xFF, 0xFC, 0x00, 0x7F, 0xFF, 0xF8, 0x03, + 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, 0xFF, 0xE0, 0x7F, 0xFF, 0xFF, 0xC3, 0xFF, + 0x7F, 0xFF, 0x0F, 0xF9, 0xF3, 0xFC, 0x3F, 0xC7, 0xCF, 0xF9, 0xFE, 0x1F, + 0x1F, 0xE7, 0xF8, 0x7C, 0x78, 0x1F, 0xE1, 0xF0, 0x00, 0x7F, 0x87, 0xC0, + 0x00, 0xFF, 0x1F, 0x00, 0x03, 0xFC, 0x7C, 0x00, 0x0F, 0xFD, 0xF0, 0x00, + 0x3F, 0xFF, 0xC0, 0x00, 0x7F, 0xFF, 0x00, 0x01, 0xFF, 0xFF, 0x80, 0x03, + 0xFF, 0xFF, 0x80, 0x07, 0xFF, 0xFF, 0x80, 0x0F, 0xFF, 0xFF, 0x00, 0x0F, + 0xFF, 0xFE, 0x00, 0x0F, 0xFF, 0xFC, 0x00, 0x07, 0xFF, 0xF8, 0x00, 0x1F, + 0xFF, 0xE0, 0x00, 0x7D, 0xFF, 0xC0, 0x01, 0xF1, 0xFF, 0x00, 0x07, 0xC3, + 0xFC, 0x00, 0x1F, 0x0F, 0xF0, 0xF0, 0x7C, 0x1F, 0xFF, 0xE1, 0xF0, 0x7F, + 0xFF, 0x87, 0xC1, 0xFD, 0xFE, 0x1F, 0x0F, 0xF7, 0xFC, 0x7C, 0x3F, 0xDF, + 0xF9, 0xF1, 0xFF, 0x3F, 0xF7, 0xCF, 0xF8, 0xFF, 0xFF, 0xFF, 0xE1, 0xFF, + 0xFF, 0xFF, 0x07, 0xFF, 0xFF, 0xF8, 0x0F, 0xFF, 0xFF, 0xC0, 0x1F, 0xFF, + 0xFE, 0x00, 0x3F, 0xFF, 0xF0, 0x00, 0x3F, 0xFF, 0x00, 0x00, 0x1F, 0xF0, + 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x01, 0xF0, 0x00, + 0x00, 0x07, 0xC0, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x00, + 0x03, 0xF8, 0x00, 0x00, 0x3F, 0x00, 0x03, 0xFF, 0x80, 0x00, 0x1F, 0x80, + 0x01, 0xFF, 0xF8, 0x00, 0x07, 0xE0, 0x00, 0xFF, 0xFE, 0x00, 0x03, 0xF0, + 0x00, 0x7F, 0xFF, 0xC0, 0x00, 0xFC, 0x00, 0x1F, 0xC7, 0xF8, 0x00, 0x7E, + 0x00, 0x0F, 0xE0, 0xFE, 0x00, 0x1F, 0x80, 0x03, 0xF8, 0x3F, 0x80, 0x0F, + 0xC0, 0x00, 0xFE, 0x07, 0xE0, 0x03, 0xF0, 0x00, 0x3F, 0x81, 0xFC, 0x01, + 0xF8, 0x00, 0x0F, 0xE0, 0x7F, 0x00, 0x7E, 0x00, 0x03, 0xF0, 0x1F, 0xC0, + 0x3F, 0x00, 0x00, 0xFC, 0x07, 0xF0, 0x0F, 0xC0, 0x00, 0x3F, 0x01, 0xFC, + 0x07, 0xE0, 0x00, 0x0F, 0xE0, 0x7F, 0x01, 0xF8, 0x00, 0x03, 0xF8, 0x1F, + 0x80, 0xFC, 0x00, 0x00, 0xFE, 0x0F, 0xE0, 0x3F, 0x00, 0x00, 0x3F, 0x83, + 0xF8, 0x1F, 0x80, 0x00, 0x07, 0xF1, 0xFE, 0x07, 0xE0, 0x00, 0x01, 0xFF, + 0xFF, 0x03, 0xF0, 0x00, 0x00, 0x3F, 0xFF, 0x80, 0xFC, 0x00, 0x00, 0x07, + 0xFF, 0xE0, 0x7E, 0x00, 0x00, 0x00, 0xFF, 0xE0, 0x1F, 0x80, 0x00, 0x00, + 0x0F, 0xE0, 0x0F, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x03, 0xF0, 0x07, 0xF0, + 0x00, 0x00, 0x01, 0xF8, 0x07, 0xFF, 0x00, 0x00, 0x00, 0x7E, 0x07, 0xFF, + 0xE0, 0x00, 0x00, 0x3F, 0x03, 0xFF, 0xFC, 0x00, 0x00, 0x0F, 0xC0, 0xFF, + 0xFF, 0x00, 0x00, 0x07, 0xE0, 0x7F, 0x8F, 0xE0, 0x00, 0x01, 0xF8, 0x1F, + 0xC3, 0xF8, 0x00, 0x00, 0xFC, 0x07, 0xE0, 0x7F, 0x00, 0x00, 0x3F, 0x03, + 0xF8, 0x1F, 0xC0, 0x00, 0x1F, 0x80, 0xFE, 0x07, 0xF0, 0x00, 0x07, 0xE0, + 0x3F, 0x81, 0xFC, 0x00, 0x03, 0xF0, 0x0F, 0xE0, 0x7F, 0x00, 0x00, 0xFC, + 0x03, 0xF8, 0x1F, 0xC0, 0x00, 0x7E, 0x00, 0xFE, 0x07, 0xF0, 0x00, 0x1F, + 0x80, 0x3F, 0x81, 0xFC, 0x00, 0x0F, 0xC0, 0x0F, 0xE0, 0x7F, 0x00, 0x03, + 0xF0, 0x01, 0xF8, 0x1F, 0xC0, 0x01, 0xF8, 0x00, 0x7F, 0x0F, 0xE0, 0x00, + 0x7E, 0x00, 0x1F, 0xC3, 0xF8, 0x00, 0x3F, 0x00, 0x03, 0xFF, 0xFE, 0x00, + 0x0F, 0xC0, 0x00, 0xFF, 0xFF, 0x00, 0x07, 0xE0, 0x00, 0x1F, 0xFF, 0x80, + 0x01, 0xF8, 0x00, 0x03, 0xFF, 0xC0, 0x00, 0xFC, 0x00, 0x00, 0x1F, 0xC0, + 0x00, 0x03, 0xFC, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xC0, 0x00, 0x00, 0x1F, + 0xFF, 0xF8, 0x00, 0x00, 0x1F, 0xFF, 0xFE, 0x00, 0x00, 0x1F, 0xFF, 0xFF, + 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xC0, 0x00, 0x0F, 0xFF, 0xFF, 0xF0, 0x00, + 0x07, 0xFE, 0x0F, 0xF8, 0x00, 0x07, 0xFE, 0x03, 0xFC, 0x00, 0x03, 0xFE, + 0x00, 0xFE, 0x00, 0x01, 0xFF, 0x00, 0x7F, 0x00, 0x00, 0x7F, 0x80, 0x3F, + 0x80, 0x00, 0x3F, 0xE0, 0x3F, 0xC0, 0x00, 0x1F, 0xF0, 0x1F, 0xE0, 0x00, + 0x0F, 0xFC, 0x3F, 0xE0, 0x00, 0x03, 0xFF, 0x3F, 0xF0, 0x00, 0x00, 0xFF, + 0xFF, 0xF0, 0x00, 0x00, 0x7F, 0xFF, 0xF0, 0x00, 0x00, 0x1F, 0xFF, 0xF0, + 0x00, 0x00, 0x07, 0xFF, 0xF0, 0x00, 0x00, 0x07, 0xFF, 0xE0, 0x00, 0x00, + 0x0F, 0xFF, 0xE0, 0x00, 0x00, 0x0F, 0xFF, 0xF8, 0x00, 0x00, 0x0F, 0xFF, + 0xFE, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0x82, 0x00, 0x0F, 0xFE, 0xFF, 0xE1, + 0xF8, 0x0F, 0xFE, 0x3F, 0xF0, 0xFF, 0x0F, 0xFC, 0x1F, 0xFC, 0xFF, 0x87, + 0xFC, 0x07, 0xFF, 0x7F, 0x87, 0xFE, 0x01, 0xFF, 0xFF, 0xC3, 0xFE, 0x00, + 0x7F, 0xFF, 0xE1, 0xFF, 0x00, 0x1F, 0xFF, 0xE0, 0xFF, 0x80, 0x07, 0xFF, + 0xF0, 0x7F, 0xC0, 0x03, 0xFF, 0xF0, 0x3F, 0xE0, 0x00, 0xFF, 0xF8, 0x1F, + 0xF0, 0x00, 0x3F, 0xFC, 0x0F, 0xFC, 0x00, 0x3F, 0xFF, 0x07, 0xFF, 0x00, + 0x3F, 0xFF, 0xE1, 0xFF, 0xC0, 0x7F, 0xFF, 0xF8, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0x9F, 0xFF, 0xFF, 0xFF, 0xFF, 0x87, + 0xFF, 0xFF, 0xFC, 0xFF, 0xC1, 0xFF, 0xFF, 0xFC, 0x3F, 0xC0, 0x3F, 0xFF, + 0xF8, 0x0F, 0xC0, 0x0F, 0xFF, 0xF0, 0x01, 0xC0, 0x00, 0x7F, 0xC0, 0x00, + 0x60, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xEF, 0xF7, 0xF3, 0xF9, 0xFC, 0xFE, 0x00, 0x3F, 0x00, 0x7E, 0x00, + 0xFE, 0x00, 0xFC, 0x01, 0xFC, 0x01, 0xFC, 0x03, 0xF8, 0x03, 0xF8, 0x07, + 0xF0, 0x07, 0xF0, 0x0F, 0xF0, 0x0F, 0xE0, 0x1F, 0xE0, 0x1F, 0xE0, 0x1F, + 0xC0, 0x3F, 0xC0, 0x3F, 0xC0, 0x3F, 0xC0, 0x7F, 0xC0, 0x7F, 0x80, 0x7F, + 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0xFF, 0x80, 0xFF, 0x80, 0xFF, + 0x80, 0xFF, 0x80, 0xFF, 0x80, 0xFF, 0x80, 0xFF, 0x80, 0xFF, 0x80, 0xFF, + 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, + 0x80, 0x3F, 0xC0, 0x3F, 0xC0, 0x3F, 0xC0, 0x3F, 0xC0, 0x1F, 0xC0, 0x1F, + 0xE0, 0x1F, 0xE0, 0x0F, 0xE0, 0x0F, 0xE0, 0x07, 0xF0, 0x07, 0xF0, 0x03, + 0xF8, 0x03, 0xF8, 0x01, 0xF8, 0x01, 0xFC, 0x00, 0xFC, 0x00, 0xFE, 0x00, + 0x7E, 0x00, 0x3F, 0xFC, 0x00, 0x7E, 0x00, 0x7F, 0x00, 0x3F, 0x00, 0x3F, + 0x80, 0x3F, 0x80, 0x1F, 0xC0, 0x1F, 0xC0, 0x0F, 0xE0, 0x0F, 0xE0, 0x0F, + 0xF0, 0x07, 0xF0, 0x07, 0xF8, 0x07, 0xF8, 0x03, 0xF8, 0x03, 0xFC, 0x03, + 0xFC, 0x03, 0xFC, 0x03, 0xFC, 0x01, 0xFE, 0x01, 0xFE, 0x01, 0xFE, 0x01, + 0xFE, 0x01, 0xFE, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, + 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFE, 0x01, + 0xFE, 0x01, 0xFE, 0x01, 0xFE, 0x01, 0xFE, 0x01, 0xFE, 0x03, 0xFC, 0x03, + 0xFC, 0x03, 0xFC, 0x03, 0xFC, 0x03, 0xF8, 0x07, 0xF8, 0x07, 0xF8, 0x07, + 0xF0, 0x0F, 0xF0, 0x0F, 0xE0, 0x0F, 0xE0, 0x1F, 0xC0, 0x1F, 0xC0, 0x1F, + 0x80, 0x3F, 0x80, 0x3F, 0x00, 0x7F, 0x00, 0x7E, 0x00, 0xFC, 0x00, 0x00, + 0xFC, 0x00, 0x03, 0xF0, 0x00, 0x0F, 0x80, 0x00, 0x3E, 0x00, 0x00, 0xF8, + 0x01, 0x81, 0xE0, 0x67, 0xC7, 0x87, 0x9F, 0xDE, 0x7E, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xF3, 0xFF, 0xFF, 0x00, 0x3F, 0x00, 0x01, 0xFE, 0x00, 0x0F, + 0xFC, 0x00, 0x7F, 0xF8, 0x03, 0xF3, 0xF0, 0x1F, 0x8F, 0xE0, 0xFE, 0x1F, + 0xC1, 0xF0, 0x3C, 0x01, 0xC0, 0xE0, 0x02, 0x01, 0x00, 0x00, 0x1F, 0xE0, + 0x00, 0x00, 0x3F, 0xC0, 0x00, 0x00, 0x7F, 0x80, 0x00, 0x00, 0xFF, 0x00, + 0x00, 0x01, 0xFE, 0x00, 0x00, 0x03, 0xFC, 0x00, 0x00, 0x07, 0xF8, 0x00, + 0x00, 0x0F, 0xF0, 0x00, 0x00, 0x1F, 0xE0, 0x00, 0x00, 0x3F, 0xC0, 0x00, + 0x00, 0x7F, 0x80, 0x00, 0x00, 0xFF, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xF0, 0x01, 0xFE, 0x00, 0x00, 0x03, 0xFC, 0x00, 0x00, 0x07, + 0xF8, 0x00, 0x00, 0x0F, 0xF0, 0x00, 0x00, 0x1F, 0xE0, 0x00, 0x00, 0x3F, + 0xC0, 0x00, 0x00, 0x7F, 0x80, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x01, 0xFE, + 0x00, 0x00, 0x03, 0xFC, 0x00, 0x00, 0x07, 0xF8, 0x00, 0x00, 0x0F, 0xF0, + 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x87, + 0xC3, 0xE1, 0xF0, 0xF0, 0xF9, 0xF9, 0xFC, 0xFC, 0x38, 0x18, 0x00, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0x00, 0x1F, 0x80, 0x07, 0xE0, 0x03, 0xF8, 0x00, 0xFE, 0x00, 0x3F, + 0x00, 0x0F, 0xC0, 0x07, 0xF0, 0x01, 0xFC, 0x00, 0x7E, 0x00, 0x1F, 0x80, + 0x0F, 0xE0, 0x03, 0xF8, 0x00, 0xFC, 0x00, 0x3F, 0x00, 0x1F, 0xC0, 0x07, + 0xF0, 0x01, 0xF8, 0x00, 0x7E, 0x00, 0x3F, 0x80, 0x0F, 0xE0, 0x03, 0xF0, + 0x00, 0xFC, 0x00, 0x7F, 0x00, 0x1F, 0xC0, 0x07, 0xE0, 0x01, 0xF8, 0x00, + 0xFE, 0x00, 0x3F, 0x80, 0x0F, 0xC0, 0x03, 0xF0, 0x01, 0xFC, 0x00, 0x7F, + 0x00, 0x1F, 0x80, 0x07, 0xE0, 0x03, 0xF8, 0x00, 0xFE, 0x00, 0x3F, 0x00, + 0x0F, 0xC0, 0x03, 0xF0, 0x01, 0xFC, 0x00, 0x7E, 0x00, 0x1F, 0x80, 0x07, + 0xE0, 0x03, 0xF8, 0x00, 0xFC, 0x00, 0x00, 0x00, 0x3F, 0xC0, 0x00, 0x07, + 0xFF, 0xC0, 0x00, 0xFF, 0xFF, 0x00, 0x0F, 0xFF, 0xFC, 0x00, 0xFF, 0xFF, + 0xF0, 0x0F, 0xFF, 0xFF, 0xC0, 0x7F, 0xFF, 0xFF, 0x07, 0xFF, 0xFF, 0xF8, + 0x3F, 0xF0, 0xFF, 0xE3, 0xFF, 0x01, 0xFF, 0x1F, 0xF0, 0x0F, 0xF8, 0xFF, + 0x80, 0x3F, 0xE7, 0xFC, 0x01, 0xFF, 0x7F, 0xC0, 0x0F, 0xFB, 0xFE, 0x00, + 0x7F, 0xDF, 0xF0, 0x01, 0xFE, 0xFF, 0x80, 0x0F, 0xFF, 0xFC, 0x00, 0x7F, + 0xFF, 0xE0, 0x03, 0xFF, 0xFF, 0x00, 0x1F, 0xFF, 0xF8, 0x00, 0xFF, 0xFF, + 0xC0, 0x07, 0xFF, 0xFE, 0x00, 0x3F, 0xFF, 0xF0, 0x01, 0xFF, 0xFF, 0x80, + 0x0F, 0xFF, 0xFC, 0x00, 0x7F, 0xFF, 0xE0, 0x03, 0xFF, 0xFF, 0x00, 0x1F, + 0xFF, 0xF8, 0x00, 0xFF, 0xFF, 0xC0, 0x07, 0xFF, 0xFE, 0x00, 0x3F, 0xDF, + 0xF0, 0x03, 0xFE, 0xFF, 0x80, 0x1F, 0xF3, 0xFE, 0x00, 0xFF, 0x9F, 0xF0, + 0x07, 0xFC, 0xFF, 0x80, 0x7F, 0xC7, 0xFE, 0x03, 0xFE, 0x1F, 0xF8, 0x7F, + 0xF0, 0xFF, 0xFF, 0xFF, 0x03, 0xFF, 0xFF, 0xF8, 0x1F, 0xFF, 0xFF, 0x80, + 0x7F, 0xFF, 0xF8, 0x01, 0xFF, 0xFF, 0xC0, 0x07, 0xFF, 0xF8, 0x00, 0x0F, + 0xFF, 0x80, 0x00, 0x1F, 0xE0, 0x00, 0x00, 0x07, 0xF0, 0x00, 0xFF, 0x00, + 0x0F, 0xF0, 0x01, 0xFF, 0x00, 0x3F, 0xF0, 0x07, 0xFF, 0x00, 0xFF, 0xF0, + 0x1F, 0xFF, 0x07, 0xFF, 0xF1, 0xFF, 0xFF, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFD, 0xFF, 0xFF, 0x9F, 0xFF, 0xF1, 0xFF, 0xFE, 0x1F, + 0xFF, 0x81, 0xFF, 0xE0, 0x1F, 0xF8, 0x01, 0xFF, 0x00, 0x1F, 0xF0, 0x01, + 0xFF, 0x00, 0x1F, 0xF0, 0x01, 0xFF, 0x00, 0x1F, 0xF0, 0x01, 0xFF, 0x00, + 0x1F, 0xF0, 0x01, 0xFF, 0x00, 0x1F, 0xF0, 0x01, 0xFF, 0x00, 0x1F, 0xF0, + 0x01, 0xFF, 0x00, 0x1F, 0xF0, 0x01, 0xFF, 0x00, 0x1F, 0xF0, 0x01, 0xFF, + 0x00, 0x1F, 0xF0, 0x01, 0xFF, 0x00, 0x1F, 0xF0, 0x01, 0xFF, 0x00, 0x1F, + 0xF0, 0x01, 0xFF, 0x00, 0x1F, 0xF0, 0x01, 0xFF, 0x00, 0x1F, 0xF0, 0x00, + 0x1F, 0xF0, 0x00, 0x03, 0xFF, 0xF8, 0x00, 0x3F, 0xFF, 0xF8, 0x01, 0xFF, + 0xFF, 0xF0, 0x0F, 0xFF, 0xFF, 0xE0, 0x7F, 0xFF, 0xFF, 0xC1, 0xFF, 0xFF, + 0xFF, 0x0F, 0xFF, 0xFF, 0xFE, 0x3F, 0xF8, 0x3F, 0xF9, 0xFF, 0x80, 0x3F, + 0xF7, 0xFC, 0x00, 0xFF, 0xDF, 0xF0, 0x01, 0xFF, 0x7F, 0xC0, 0x07, 0xFD, + 0xFE, 0x00, 0x1F, 0xF0, 0xF8, 0x00, 0x7F, 0xC0, 0x00, 0x01, 0xFE, 0x00, + 0x00, 0x07, 0xF8, 0x00, 0x00, 0x3F, 0xE0, 0x00, 0x00, 0xFF, 0x00, 0x00, + 0x07, 0xFC, 0x00, 0x00, 0x3F, 0xE0, 0x00, 0x01, 0xFF, 0x80, 0x00, 0x0F, + 0xFC, 0x00, 0x00, 0x7F, 0xE0, 0x00, 0x03, 0xFF, 0x00, 0x00, 0x1F, 0xF8, + 0x00, 0x00, 0xFF, 0xC0, 0x00, 0x07, 0xFE, 0x00, 0x00, 0x3F, 0xF0, 0x00, + 0x01, 0xFF, 0x80, 0x00, 0x0F, 0xFC, 0x00, 0x00, 0x7F, 0xE0, 0x00, 0x03, + 0xFF, 0x00, 0x00, 0x1F, 0xF8, 0x00, 0x00, 0xFF, 0xC0, 0x00, 0x07, 0xFE, + 0x00, 0x00, 0x1F, 0xFF, 0xFF, 0xFC, 0xFF, 0xFF, 0xFF, 0xF7, 0xFF, 0xFF, + 0xFF, 0xDF, 0xFF, 0xFF, 0xFF, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, + 0x00, 0x1F, 0xE0, 0x00, 0x03, 0xFF, 0xF0, 0x00, 0x1F, 0xFF, 0xE0, 0x01, + 0xFF, 0xFF, 0xC0, 0x0F, 0xFF, 0xFF, 0x80, 0x3F, 0xFF, 0xFF, 0x01, 0xFF, + 0xFF, 0xFE, 0x07, 0xFF, 0xFF, 0xF8, 0x3F, 0xF0, 0x7F, 0xF0, 0xFF, 0x80, + 0xFF, 0xC7, 0xFC, 0x01, 0xFF, 0x1F, 0xF0, 0x03, 0xFC, 0x7F, 0x80, 0x0F, + 0xF0, 0x0E, 0x00, 0x3F, 0xC0, 0x00, 0x01, 0xFF, 0x00, 0x00, 0x07, 0xFC, + 0x00, 0x00, 0x3F, 0xE0, 0x00, 0x03, 0xFF, 0x80, 0x00, 0xFF, 0xFC, 0x00, + 0x03, 0xFF, 0xE0, 0x00, 0x0F, 0xFE, 0x00, 0x00, 0x3F, 0xF0, 0x00, 0x00, + 0xFF, 0xF8, 0x00, 0x03, 0xFF, 0xF0, 0x00, 0x0F, 0xFF, 0xE0, 0x00, 0x40, + 0xFF, 0xC0, 0x00, 0x01, 0xFF, 0x80, 0x00, 0x03, 0xFE, 0x00, 0x00, 0x07, + 0xFC, 0x00, 0x00, 0x1F, 0xF0, 0x00, 0x00, 0x7F, 0xC1, 0xE0, 0x01, 0xFF, + 0xFF, 0x80, 0x07, 0xFD, 0xFE, 0x00, 0x1F, 0xF7, 0xFC, 0x00, 0x7F, 0xDF, + 0xF0, 0x03, 0xFF, 0x7F, 0xE0, 0x1F, 0xFC, 0xFF, 0xE0, 0xFF, 0xE3, 0xFF, + 0xFF, 0xFF, 0x87, 0xFF, 0xFF, 0xFC, 0x0F, 0xFF, 0xFF, 0xE0, 0x3F, 0xFF, + 0xFF, 0x00, 0x7F, 0xFF, 0xF8, 0x00, 0x7F, 0xFF, 0xC0, 0x00, 0xFF, 0xFC, + 0x00, 0x00, 0x7F, 0x80, 0x00, 0x00, 0x00, 0x1F, 0xE0, 0x00, 0x00, 0x0F, + 0xF0, 0x00, 0x00, 0x0F, 0xF8, 0x00, 0x00, 0x0F, 0xFC, 0x00, 0x00, 0x0F, + 0xFE, 0x00, 0x00, 0x07, 0xFF, 0x00, 0x00, 0x07, 0xFF, 0x80, 0x00, 0x07, + 0xFF, 0xC0, 0x00, 0x03, 0xFF, 0xE0, 0x00, 0x03, 0xFF, 0xF0, 0x00, 0x03, + 0xFF, 0xF8, 0x00, 0x03, 0xFF, 0xFC, 0x00, 0x01, 0xFF, 0xFE, 0x00, 0x01, + 0xFE, 0xFF, 0x00, 0x01, 0xFE, 0x7F, 0x80, 0x00, 0xFF, 0x3F, 0xC0, 0x00, + 0xFF, 0x1F, 0xE0, 0x00, 0xFF, 0x0F, 0xF0, 0x00, 0xFF, 0x07, 0xF8, 0x00, + 0x7F, 0x83, 0xFC, 0x00, 0x7F, 0x81, 0xFE, 0x00, 0x7F, 0x80, 0xFF, 0x00, + 0x7F, 0xC0, 0x7F, 0x80, 0x3F, 0xC0, 0x3F, 0xC0, 0x3F, 0xC0, 0x1F, 0xE0, + 0x3F, 0xC0, 0x0F, 0xF0, 0x1F, 0xE0, 0x07, 0xF8, 0x1F, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x00, 0x03, 0xFC, 0x00, 0x00, 0x01, + 0xFE, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x7F, 0x80, 0x00, 0x00, + 0x3F, 0xC0, 0x00, 0x00, 0x1F, 0xE0, 0x00, 0x00, 0x0F, 0xF0, 0x00, 0x00, + 0x07, 0xF8, 0x00, 0x00, 0x03, 0xFC, 0x00, 0x07, 0xFF, 0xFF, 0xF0, 0x1F, + 0xFF, 0xFF, 0xC0, 0x7F, 0xFF, 0xFF, 0x01, 0xFF, 0xFF, 0xFC, 0x0F, 0xFF, + 0xFF, 0xF0, 0x3F, 0xFF, 0xFF, 0xC0, 0xFF, 0xFF, 0xFF, 0x03, 0xFF, 0xFF, + 0xFC, 0x0F, 0xFF, 0xFF, 0xF0, 0x3F, 0xC0, 0x00, 0x01, 0xFF, 0x00, 0x00, + 0x07, 0xF8, 0x00, 0x00, 0x1F, 0xE0, 0x00, 0x00, 0x7F, 0x80, 0x00, 0x01, + 0xFE, 0x00, 0x00, 0x07, 0xF9, 0xFC, 0x00, 0x3F, 0xFF, 0xFE, 0x00, 0xFF, + 0xFF, 0xFC, 0x03, 0xFF, 0xFF, 0xFC, 0x0F, 0xFF, 0xFF, 0xF8, 0x3F, 0xFF, + 0xFF, 0xE1, 0xFF, 0xFF, 0xFF, 0xC7, 0xFF, 0xFF, 0xFF, 0x9F, 0xFC, 0x1F, + 0xFE, 0x7F, 0xC0, 0x1F, 0xF8, 0x3C, 0x00, 0x3F, 0xF0, 0x00, 0x00, 0x7F, + 0xC0, 0x00, 0x01, 0xFF, 0x00, 0x00, 0x07, 0xFC, 0x00, 0x00, 0x1F, 0xF0, + 0xF0, 0x00, 0x7F, 0xFF, 0xE0, 0x01, 0xFF, 0xFF, 0x80, 0x07, 0xFF, 0xFE, + 0x00, 0x1F, 0xF7, 0xFC, 0x00, 0xFF, 0x9F, 0xF8, 0x07, 0xFE, 0x7F, 0xF0, + 0x7F, 0xF8, 0xFF, 0xFF, 0xFF, 0xC3, 0xFF, 0xFF, 0xFE, 0x07, 0xFF, 0xFF, + 0xF8, 0x0F, 0xFF, 0xFF, 0xC0, 0x1F, 0xFF, 0xFE, 0x00, 0x3F, 0xFF, 0xF0, + 0x00, 0x3F, 0xFF, 0x00, 0x00, 0x3F, 0xE0, 0x00, 0x00, 0x0F, 0xF0, 0x00, + 0x01, 0xFF, 0xF0, 0x00, 0x0F, 0xFF, 0xF0, 0x00, 0xFF, 0xFF, 0xE0, 0x07, + 0xFF, 0xFF, 0xC0, 0x3F, 0xFF, 0xFF, 0x81, 0xFF, 0xFF, 0xFE, 0x07, 0xFF, + 0xFF, 0xFC, 0x3F, 0xF8, 0x3F, 0xF0, 0xFF, 0xC0, 0x7F, 0xC7, 0xFE, 0x00, + 0xFF, 0x9F, 0xF0, 0x03, 0xFE, 0x7F, 0xC0, 0x07, 0x81, 0xFF, 0x00, 0x00, + 0x0F, 0xF8, 0x00, 0x00, 0x3F, 0xE0, 0x00, 0x00, 0xFF, 0x80, 0x00, 0x03, + 0xFE, 0x1F, 0xC0, 0x0F, 0xF9, 0xFF, 0xE0, 0x3F, 0xEF, 0xFF, 0xC0, 0xFF, + 0xFF, 0xFF, 0x83, 0xFF, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0xFE, 0x3F, 0xFF, + 0xFF, 0xFC, 0xFF, 0xFF, 0xFF, 0xF3, 0xFF, 0xE0, 0xFF, 0xEF, 0xFE, 0x01, + 0xFF, 0xBF, 0xF0, 0x03, 0xFE, 0xFF, 0xC0, 0x07, 0xFF, 0xFE, 0x00, 0x1F, + 0xFF, 0xF8, 0x00, 0x7F, 0xFF, 0xE0, 0x01, 0xFF, 0x7F, 0x80, 0x07, 0xFD, + 0xFF, 0x00, 0x1F, 0xF7, 0xFC, 0x00, 0x7F, 0xDF, 0xF8, 0x01, 0xFE, 0x3F, + 0xF0, 0x0F, 0xF8, 0xFF, 0xE0, 0xFF, 0xE1, 0xFF, 0xFF, 0xFF, 0x07, 0xFF, + 0xFF, 0xFC, 0x0F, 0xFF, 0xFF, 0xE0, 0x1F, 0xFF, 0xFF, 0x00, 0x3F, 0xFF, + 0xF8, 0x00, 0x7F, 0xFF, 0xC0, 0x00, 0x7F, 0xFC, 0x00, 0x00, 0x7F, 0x80, + 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x00, + 0xFF, 0x00, 0x00, 0x0F, 0xF0, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x07, 0xF8, + 0x00, 0x00, 0x7F, 0x80, 0x00, 0x07, 0xF8, 0x00, 0x00, 0x3F, 0xC0, 0x00, + 0x03, 0xFC, 0x00, 0x00, 0x3F, 0xE0, 0x00, 0x01, 0xFE, 0x00, 0x00, 0x1F, + 0xE0, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x0F, 0xF0, 0x00, 0x00, 0x7F, 0x80, + 0x00, 0x07, 0xFC, 0x00, 0x00, 0x3F, 0xC0, 0x00, 0x01, 0xFE, 0x00, 0x00, + 0x1F, 0xE0, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x07, 0xF8, 0x00, 0x00, 0x7F, + 0x80, 0x00, 0x03, 0xFC, 0x00, 0x00, 0x1F, 0xE0, 0x00, 0x01, 0xFF, 0x00, + 0x00, 0x0F, 0xF0, 0x00, 0x00, 0x7F, 0x80, 0x00, 0x03, 0xFC, 0x00, 0x00, + 0x3F, 0xE0, 0x00, 0x01, 0xFF, 0x00, 0x00, 0x0F, 0xF0, 0x00, 0x00, 0x7F, + 0x80, 0x00, 0x03, 0xFC, 0x00, 0x00, 0x1F, 0xE0, 0x00, 0x00, 0xFF, 0x00, + 0x00, 0x07, 0xF8, 0x00, 0x00, 0x00, 0x3F, 0xE0, 0x00, 0x0F, 0xFF, 0xC0, + 0x01, 0xFF, 0xFF, 0x80, 0x1F, 0xFF, 0xFE, 0x01, 0xFF, 0xFF, 0xF8, 0x1F, + 0xFF, 0xFF, 0xE0, 0xFF, 0xFF, 0xFF, 0x8F, 0xFE, 0x0F, 0xFC, 0x7F, 0xC0, + 0x3F, 0xE3, 0xFE, 0x00, 0xFF, 0x9F, 0xE0, 0x07, 0xFC, 0xFF, 0x00, 0x1F, + 0xE7, 0xF8, 0x00, 0xFF, 0x3F, 0xC0, 0x07, 0xF9, 0xFE, 0x00, 0x7F, 0x8F, + 0xF8, 0x03, 0xFC, 0x3F, 0xC0, 0x3F, 0xE1, 0xFF, 0x83, 0xFE, 0x07, 0xFF, + 0xFF, 0xE0, 0x1F, 0xFF, 0xFE, 0x00, 0x3F, 0xFF, 0xE0, 0x00, 0xFF, 0xFE, + 0x00, 0x1F, 0xFF, 0xFC, 0x03, 0xFF, 0xFF, 0xF0, 0x3F, 0xFF, 0xFF, 0xC1, + 0xFF, 0x83, 0xFF, 0x1F, 0xF0, 0x0F, 0xFC, 0xFF, 0x00, 0x3F, 0xEF, 0xF8, + 0x00, 0xFF, 0xFF, 0x80, 0x07, 0xFF, 0xFC, 0x00, 0x1F, 0xFF, 0xE0, 0x00, + 0xFF, 0xFF, 0x00, 0x07, 0xFF, 0xF8, 0x00, 0x3F, 0xFF, 0xC0, 0x03, 0xFF, + 0xFF, 0x00, 0x1F, 0xFF, 0xF8, 0x01, 0xFF, 0xFF, 0xE0, 0x0F, 0xF9, 0xFF, + 0xC1, 0xFF, 0xCF, 0xFF, 0xFF, 0xFC, 0x3F, 0xFF, 0xFF, 0xE0, 0xFF, 0xFF, + 0xFE, 0x03, 0xFF, 0xFF, 0xE0, 0x0F, 0xFF, 0xFE, 0x00, 0x1F, 0xFF, 0xC0, + 0x00, 0x1F, 0xF0, 0x00, 0x00, 0x3F, 0xC0, 0x00, 0x03, 0xFF, 0xE0, 0x00, + 0x3F, 0xFF, 0xE0, 0x01, 0xFF, 0xFF, 0xC0, 0x0F, 0xFF, 0xFF, 0x80, 0x7F, + 0xFF, 0xFF, 0x03, 0xFF, 0xFF, 0xFE, 0x0F, 0xFF, 0xFF, 0xF8, 0x7F, 0xF0, + 0x7F, 0xF1, 0xFF, 0x00, 0xFF, 0xCF, 0xF8, 0x01, 0xFF, 0x3F, 0xE0, 0x03, + 0xFE, 0xFF, 0x80, 0x0F, 0xFB, 0xFE, 0x00, 0x3F, 0xEF, 0xF0, 0x00, 0xFF, + 0xBF, 0xE0, 0x03, 0xFF, 0xFF, 0x80, 0x0F, 0xFF, 0xFE, 0x00, 0x3F, 0xFF, + 0xF8, 0x01, 0xFF, 0xDF, 0xF0, 0x07, 0xFF, 0x7F, 0xF0, 0x7F, 0xFD, 0xFF, + 0xFF, 0xFF, 0xF3, 0xFF, 0xFF, 0xFF, 0xC7, 0xFF, 0xFF, 0xFF, 0x1F, 0xFF, + 0xFF, 0xFC, 0x3F, 0xFF, 0xFF, 0xF0, 0x7F, 0xFF, 0x7F, 0xC0, 0x7F, 0xF9, + 0xFF, 0x00, 0x7F, 0x07, 0xFC, 0x00, 0x00, 0x1F, 0xF0, 0x00, 0x00, 0x7F, + 0xC0, 0x00, 0x03, 0xFE, 0x00, 0x00, 0x0F, 0xF8, 0x1E, 0x00, 0x3F, 0xE7, + 0xFC, 0x01, 0xFF, 0x9F, 0xF0, 0x07, 0xFC, 0x7F, 0xE0, 0x3F, 0xF0, 0xFF, + 0xC3, 0xFF, 0x83, 0xFF, 0xFF, 0xFE, 0x0F, 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, + 0xFF, 0xC0, 0x3F, 0xFF, 0xFE, 0x00, 0x7F, 0xFF, 0xF0, 0x00, 0xFF, 0xFF, + 0x00, 0x01, 0xFF, 0xF8, 0x00, 0x00, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x7F, + 0xDF, 0xF7, 0xFD, 0xFF, 0x7F, 0xDF, 0xF7, 0xFD, 0xFF, 0x7F, 0xC0, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xDF, 0xF7, 0xFD, 0xFF, 0x7F, 0xDF, + 0xF7, 0xFD, 0xFF, 0x7F, 0xC1, 0xE0, 0x78, 0x1E, 0x0F, 0x87, 0xC3, 0xF3, + 0xF8, 0x7C, 0x1E, 0x02, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x3C, 0x00, 0x00, 0x01, 0xF8, 0x00, 0x00, 0x0F, 0xF0, 0x00, 0x00, 0xFF, + 0xE0, 0x00, 0x07, 0xFF, 0xC0, 0x00, 0x3F, 0xFF, 0x80, 0x03, 0xFF, 0xFF, + 0x00, 0x1F, 0xFF, 0xFC, 0x00, 0xFF, 0xFF, 0xC0, 0x0F, 0xFF, 0xFE, 0x00, + 0x7F, 0xFF, 0xE0, 0x03, 0xFF, 0xFF, 0x00, 0x1F, 0xFF, 0xF0, 0x00, 0x3F, + 0xFF, 0x80, 0x00, 0x7F, 0xFC, 0x00, 0x00, 0xFF, 0xC0, 0x00, 0x01, 0xFF, + 0xC0, 0x00, 0x03, 0xFF, 0xE0, 0x00, 0x07, 0xFF, 0xF8, 0x00, 0x0F, 0xFF, + 0xFC, 0x00, 0x07, 0xFF, 0xFE, 0x00, 0x03, 0xFF, 0xFF, 0x80, 0x00, 0xFF, + 0xFF, 0xC0, 0x00, 0x7F, 0xFF, 0xE0, 0x00, 0x3F, 0xFF, 0xF8, 0x00, 0x1F, + 0xFF, 0xF8, 0x00, 0x07, 0xFF, 0xF0, 0x00, 0x03, 0xFF, 0xE0, 0x00, 0x01, + 0xFF, 0xC0, 0x00, 0x00, 0x7F, 0x80, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, + 0x1E, 0x00, 0x00, 0x00, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x80, 0x00, 0x00, 0x01, 0xC0, + 0x00, 0x00, 0x03, 0xF0, 0x00, 0x00, 0x07, 0xF8, 0x00, 0x00, 0x0F, 0xFC, + 0x00, 0x00, 0x1F, 0xFF, 0x00, 0x00, 0x3F, 0xFF, 0x80, 0x00, 0x7F, 0xFF, + 0xC0, 0x00, 0x7F, 0xFF, 0xF0, 0x00, 0x1F, 0xFF, 0xF8, 0x00, 0x0F, 0xFF, + 0xFC, 0x00, 0x07, 0xFF, 0xFF, 0x00, 0x01, 0xFF, 0xFF, 0x80, 0x00, 0xFF, + 0xFF, 0xC0, 0x00, 0x3F, 0xFF, 0x80, 0x00, 0x1F, 0xFF, 0x00, 0x00, 0x0F, + 0xFE, 0x00, 0x00, 0x1F, 0xFC, 0x00, 0x00, 0xFF, 0xF8, 0x00, 0x0F, 0xFF, + 0xF0, 0x00, 0x7F, 0xFF, 0xE0, 0x03, 0xFF, 0xFF, 0x00, 0x3F, 0xFF, 0xF8, + 0x01, 0xFF, 0xFF, 0x80, 0x1F, 0xFF, 0xFC, 0x00, 0xFF, 0xFF, 0xE0, 0x03, + 0xFF, 0xFE, 0x00, 0x07, 0xFF, 0xF0, 0x00, 0x0F, 0xFF, 0x80, 0x00, 0x1F, + 0xF8, 0x00, 0x00, 0x3F, 0xC0, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, 0xE0, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xF8, 0x00, 0x00, + 0x3F, 0xFF, 0x80, 0x00, 0x7F, 0xFF, 0xF0, 0x00, 0xFF, 0xFF, 0xFE, 0x00, + 0xFF, 0xFF, 0xFF, 0x80, 0xFF, 0xFF, 0xFF, 0xE0, 0xFF, 0xFF, 0xFF, 0xF8, + 0x7F, 0xF8, 0x1F, 0xFC, 0x7F, 0xF0, 0x03, 0xFF, 0x3F, 0xE0, 0x00, 0xFF, + 0x9F, 0xF0, 0x00, 0x3F, 0xDF, 0xF0, 0x00, 0x1F, 0xFF, 0xF8, 0x00, 0x0F, + 0xF8, 0x78, 0x00, 0x07, 0xFC, 0x00, 0x00, 0x03, 0xFE, 0x00, 0x00, 0x03, + 0xFE, 0x00, 0x00, 0x01, 0xFF, 0x00, 0x00, 0x01, 0xFF, 0x80, 0x00, 0x03, + 0xFF, 0x80, 0x00, 0x03, 0xFF, 0x80, 0x00, 0x03, 0xFF, 0x80, 0x00, 0x03, + 0xFF, 0x80, 0x00, 0x03, 0xFF, 0x80, 0x00, 0x03, 0xFF, 0x80, 0x00, 0x03, + 0xFF, 0x80, 0x00, 0x01, 0xFF, 0x80, 0x00, 0x01, 0xFF, 0x80, 0x00, 0x00, + 0xFF, 0x80, 0x00, 0x00, 0x7F, 0x80, 0x00, 0x00, 0x7F, 0xC0, 0x00, 0x00, + 0x3F, 0xC0, 0x00, 0x00, 0x1F, 0xE0, 0x00, 0x00, 0x0F, 0xF0, 0x00, 0x00, + 0x07, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xC0, 0x00, 0x00, 0x3F, 0xE0, 0x00, + 0x00, 0x1F, 0xF0, 0x00, 0x00, 0x0F, 0xF8, 0x00, 0x00, 0x07, 0xFC, 0x00, + 0x00, 0x03, 0xFE, 0x00, 0x00, 0x01, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x80, + 0x00, 0x00, 0x7F, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xF8, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, + 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFE, 0x00, 0x00, 0x00, + 0x3F, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, + 0x00, 0x00, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x01, 0xFF, 0xFC, + 0x00, 0x3F, 0xFF, 0x00, 0x00, 0x7F, 0xF8, 0x00, 0x00, 0x7F, 0xF0, 0x00, + 0x1F, 0xFC, 0x00, 0x00, 0x03, 0xFF, 0x00, 0x07, 0xFE, 0x00, 0x00, 0x00, + 0x1F, 0xF0, 0x01, 0xFF, 0x00, 0x00, 0x00, 0x01, 0xFE, 0x00, 0x3F, 0xC0, + 0x0F, 0xE0, 0x00, 0x1F, 0xE0, 0x0F, 0xF0, 0x07, 0xFF, 0x0F, 0xF1, 0xFC, + 0x03, 0xFC, 0x03, 0xFF, 0xF1, 0xFE, 0x1F, 0xC0, 0x7F, 0x00, 0xFF, 0xFF, + 0x3F, 0xC1, 0xF8, 0x1F, 0xC0, 0x3F, 0xFF, 0xEF, 0xF0, 0x3F, 0x03, 0xF8, + 0x0F, 0xFF, 0xFF, 0xFE, 0x03, 0xF0, 0xFE, 0x03, 0xFF, 0xFF, 0xFF, 0xC0, + 0x7E, 0x1F, 0xC0, 0xFF, 0xFF, 0xFF, 0xF8, 0x0F, 0xC3, 0xF0, 0x1F, 0xFC, + 0x1F, 0xFF, 0x00, 0xF8, 0xFE, 0x07, 0xFE, 0x00, 0xFF, 0xC0, 0x1F, 0x1F, + 0x80, 0xFF, 0x80, 0x0F, 0xF8, 0x03, 0xF3, 0xF0, 0x3F, 0xE0, 0x01, 0xFF, + 0x00, 0x7E, 0x7E, 0x07, 0xF8, 0x00, 0x3F, 0xE0, 0x0F, 0xDF, 0x80, 0xFF, + 0x00, 0x07, 0xF8, 0x01, 0xFB, 0xF0, 0x3F, 0xE0, 0x00, 0xFF, 0x00, 0x3E, + 0x7E, 0x07, 0xF8, 0x00, 0x1F, 0xE0, 0x07, 0xCF, 0xC0, 0xFF, 0x00, 0x03, + 0xFC, 0x01, 0xF9, 0xF8, 0x1F, 0xE0, 0x00, 0x7F, 0x80, 0x3F, 0x3F, 0x03, + 0xF8, 0x00, 0x0F, 0xE0, 0x07, 0xE7, 0xC0, 0xFF, 0x00, 0x03, 0xFC, 0x01, + 0xF8, 0xF8, 0x1F, 0xE0, 0x00, 0x7F, 0x80, 0x7F, 0x1F, 0x03, 0xFE, 0x00, + 0x1F, 0xF0, 0x0F, 0xC3, 0xF0, 0x7F, 0xC0, 0x03, 0xFE, 0x03, 0xF8, 0x7E, + 0x07, 0xF8, 0x00, 0xFF, 0x80, 0xFE, 0x0F, 0xC0, 0xFF, 0x80, 0x3F, 0xF0, + 0x3F, 0xC1, 0xF8, 0x1F, 0xFC, 0x1F, 0xFE, 0x0F, 0xF0, 0x3F, 0x03, 0xFF, + 0xFF, 0xFF, 0xC7, 0xFC, 0x03, 0xF0, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, + 0x7E, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x0F, 0xC0, 0x7F, 0xFF, 0xFF, + 0xFF, 0xF8, 0x00, 0xFC, 0x07, 0xFF, 0xFB, 0xFF, 0xFC, 0x00, 0x1F, 0x80, + 0x7F, 0xFE, 0x7F, 0xFF, 0x00, 0x03, 0xF8, 0x07, 0xFF, 0x07, 0xFF, 0x80, + 0x00, 0x3F, 0x80, 0x1F, 0x80, 0x7F, 0x80, 0x00, 0x07, 0xF8, 0x00, 0x00, + 0x00, 0x00, 0x03, 0xF8, 0x7F, 0x80, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x0F, + 0xF8, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xC0, 0xFF, 0x80, 0x00, 0x00, 0x00, + 0x0F, 0xF0, 0x0F, 0xFC, 0x00, 0x00, 0x00, 0x07, 0xFE, 0x01, 0xFF, 0xE0, + 0x00, 0x00, 0x03, 0xFF, 0x80, 0x1F, 0xFF, 0x00, 0x00, 0x01, 0xFF, 0xE0, + 0x01, 0xFF, 0xFF, 0x00, 0x07, 0xFF, 0xF8, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFE, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x07, + 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x3F, 0xFF, 0xFF, 0xFF, 0xE0, + 0x00, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x07, 0xFF, + 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF, 0xE0, 0x00, 0x00, 0x00, + 0x00, 0x7F, 0xE0, 0x00, 0x00, 0x00, 0x03, 0xFF, 0x80, 0x00, 0x00, 0x00, + 0x1F, 0xFC, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x0F, + 0xFF, 0x80, 0x00, 0x00, 0x00, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x07, 0xFF, + 0xF0, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0x80, 0x00, 0x00, 0x03, 0xFF, 0xFE, + 0x00, 0x00, 0x00, 0x1F, 0xFF, 0xF0, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0x80, + 0x00, 0x00, 0x0F, 0xFF, 0xFE, 0x00, 0x00, 0x00, 0x7F, 0xDF, 0xF0, 0x00, + 0x00, 0x07, 0xFC, 0xFF, 0xC0, 0x00, 0x00, 0x3F, 0xE3, 0xFE, 0x00, 0x00, + 0x03, 0xFF, 0x1F, 0xF0, 0x00, 0x00, 0x1F, 0xF0, 0xFF, 0xC0, 0x00, 0x00, + 0xFF, 0x83, 0xFE, 0x00, 0x00, 0x0F, 0xF8, 0x1F, 0xF8, 0x00, 0x00, 0x7F, + 0xC0, 0x7F, 0xC0, 0x00, 0x03, 0xFE, 0x03, 0xFE, 0x00, 0x00, 0x3F, 0xE0, + 0x1F, 0xF8, 0x00, 0x01, 0xFF, 0x00, 0x7F, 0xC0, 0x00, 0x1F, 0xF8, 0x03, + 0xFF, 0x00, 0x00, 0xFF, 0x80, 0x0F, 0xF8, 0x00, 0x07, 0xFC, 0x00, 0x7F, + 0xC0, 0x00, 0x7F, 0xC0, 0x03, 0xFF, 0x00, 0x03, 0xFF, 0xFF, 0xFF, 0xF8, + 0x00, 0x3F, 0xFF, 0xFF, 0xFF, 0xE0, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, + 0x0F, 0xFF, 0xFF, 0xFF, 0xF8, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x07, + 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xFC, 0x03, 0xFF, + 0xFF, 0xFF, 0xFF, 0xE0, 0x1F, 0xF0, 0x00, 0x01, 0xFF, 0x01, 0xFF, 0x80, + 0x00, 0x0F, 0xFC, 0x0F, 0xF8, 0x00, 0x00, 0x7F, 0xE0, 0x7F, 0xC0, 0x00, + 0x01, 0xFF, 0x87, 0xFE, 0x00, 0x00, 0x0F, 0xFC, 0x3F, 0xE0, 0x00, 0x00, + 0x3F, 0xE3, 0xFF, 0x00, 0x00, 0x01, 0xFF, 0x9F, 0xF8, 0x00, 0x00, 0x0F, + 0xFC, 0xFF, 0x80, 0x00, 0x00, 0x3F, 0xFF, 0xFC, 0x00, 0x00, 0x01, 0xFF, + 0x80, 0xFF, 0xFF, 0xFF, 0xC0, 0x07, 0xFF, 0xFF, 0xFF, 0xC0, 0x3F, 0xFF, + 0xFF, 0xFF, 0x81, 0xFF, 0xFF, 0xFF, 0xFE, 0x0F, 0xFF, 0xFF, 0xFF, 0xF8, + 0x7F, 0xFF, 0xFF, 0xFF, 0xC3, 0xFF, 0xFF, 0xFF, 0xFF, 0x1F, 0xFF, 0xFF, + 0xFF, 0xF8, 0xFF, 0x80, 0x01, 0xFF, 0xE7, 0xFC, 0x00, 0x03, 0xFF, 0x3F, + 0xE0, 0x00, 0x0F, 0xF9, 0xFF, 0x00, 0x00, 0x7F, 0xCF, 0xF8, 0x00, 0x03, + 0xFE, 0x7F, 0xC0, 0x00, 0x1F, 0xF3, 0xFE, 0x00, 0x00, 0xFF, 0x9F, 0xF0, + 0x00, 0x07, 0xF8, 0xFF, 0x80, 0x00, 0x7F, 0xC7, 0xFC, 0x00, 0x0F, 0xFC, + 0x3F, 0xFF, 0xFF, 0xFF, 0xE1, 0xFF, 0xFF, 0xFF, 0xFE, 0x0F, 0xFF, 0xFF, + 0xFF, 0xC0, 0x7F, 0xFF, 0xFF, 0xFC, 0x03, 0xFF, 0xFF, 0xFF, 0xFC, 0x1F, + 0xFF, 0xFF, 0xFF, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xC7, 0xFF, 0xFF, 0xFF, + 0xFF, 0x3F, 0xE0, 0x00, 0x3F, 0xFD, 0xFF, 0x00, 0x00, 0x7F, 0xEF, 0xF8, + 0x00, 0x01, 0xFF, 0xFF, 0xC0, 0x00, 0x07, 0xFF, 0xFE, 0x00, 0x00, 0x3F, + 0xFF, 0xF0, 0x00, 0x01, 0xFF, 0xFF, 0x80, 0x00, 0x0F, 0xFF, 0xFC, 0x00, + 0x00, 0x7F, 0xFF, 0xE0, 0x00, 0x07, 0xFF, 0xFF, 0x00, 0x00, 0x7F, 0xFF, + 0xF8, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFB, 0xFF, 0xFF, 0xFF, + 0xFF, 0xDF, 0xFF, 0xFF, 0xFF, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xC7, 0xFF, + 0xFF, 0xFF, 0xFC, 0x3F, 0xFF, 0xFF, 0xFF, 0xC1, 0xFF, 0xFF, 0xFF, 0xF8, + 0x0F, 0xFF, 0xFF, 0xFE, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xC0, 0x00, 0x00, + 0x0F, 0xFF, 0xF0, 0x00, 0x00, 0xFF, 0xFF, 0xF8, 0x00, 0x03, 0xFF, 0xFF, + 0xFC, 0x00, 0x0F, 0xFF, 0xFF, 0xFC, 0x00, 0x7F, 0xFF, 0xFF, 0xFC, 0x01, + 0xFF, 0xFF, 0xFF, 0xFC, 0x03, 0xFF, 0xFF, 0xFF, 0xF8, 0x0F, 0xFF, 0x80, + 0xFF, 0xF8, 0x3F, 0xFC, 0x00, 0x7F, 0xF0, 0x7F, 0xF0, 0x00, 0x7F, 0xF1, + 0xFF, 0x80, 0x00, 0x7F, 0xE3, 0xFF, 0x00, 0x00, 0x7F, 0xEF, 0xFC, 0x00, + 0x00, 0xFF, 0xDF, 0xF8, 0x00, 0x00, 0xFE, 0x3F, 0xE0, 0x00, 0x01, 0x80, + 0x7F, 0xC0, 0x00, 0x00, 0x01, 0xFF, 0x80, 0x00, 0x00, 0x03, 0xFF, 0x00, + 0x00, 0x00, 0x07, 0xFE, 0x00, 0x00, 0x00, 0x0F, 0xF8, 0x00, 0x00, 0x00, + 0x1F, 0xF0, 0x00, 0x00, 0x00, 0x3F, 0xE0, 0x00, 0x00, 0x00, 0x7F, 0xC0, + 0x00, 0x00, 0x00, 0xFF, 0x80, 0x00, 0x00, 0x01, 0xFF, 0x00, 0x00, 0x00, + 0x03, 0xFF, 0x00, 0x00, 0x00, 0x07, 0xFE, 0x00, 0x00, 0x00, 0x0F, 0xFC, + 0x00, 0x00, 0x30, 0x0F, 0xF8, 0x00, 0x00, 0x7C, 0x1F, 0xF0, 0x00, 0x00, + 0xFF, 0x3F, 0xF0, 0x00, 0x03, 0xFF, 0x7F, 0xE0, 0x00, 0x07, 0xFE, 0x7F, + 0xE0, 0x00, 0x1F, 0xF8, 0xFF, 0xC0, 0x00, 0x3F, 0xF0, 0xFF, 0xC0, 0x00, + 0xFF, 0xE1, 0xFF, 0xE0, 0x03, 0xFF, 0x81, 0xFF, 0xF0, 0x3F, 0xFF, 0x03, + 0xFF, 0xFF, 0xFF, 0xFC, 0x03, 0xFF, 0xFF, 0xFF, 0xF0, 0x03, 0xFF, 0xFF, + 0xFF, 0xC0, 0x03, 0xFF, 0xFF, 0xFF, 0x00, 0x01, 0xFF, 0xFF, 0xFC, 0x00, + 0x01, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x1F, + 0xF8, 0x00, 0x00, 0xFF, 0xFF, 0xFE, 0x00, 0x07, 0xFF, 0xFF, 0xFE, 0x00, + 0x3F, 0xFF, 0xFF, 0xFC, 0x01, 0xFF, 0xFF, 0xFF, 0xF8, 0x0F, 0xFF, 0xFF, + 0xFF, 0xE0, 0x7F, 0xFF, 0xFF, 0xFF, 0x83, 0xFF, 0xFF, 0xFF, 0xFE, 0x1F, + 0xFF, 0xFF, 0xFF, 0xF8, 0xFF, 0x80, 0x07, 0xFF, 0xC7, 0xFC, 0x00, 0x0F, + 0xFF, 0x3F, 0xE0, 0x00, 0x3F, 0xF9, 0xFF, 0x00, 0x00, 0xFF, 0xEF, 0xF8, + 0x00, 0x03, 0xFF, 0x7F, 0xC0, 0x00, 0x0F, 0xFB, 0xFE, 0x00, 0x00, 0x7F, + 0xDF, 0xF0, 0x00, 0x03, 0xFF, 0xFF, 0x80, 0x00, 0x1F, 0xFF, 0xFC, 0x00, + 0x00, 0x7F, 0xFF, 0xE0, 0x00, 0x03, 0xFF, 0xFF, 0x00, 0x00, 0x1F, 0xFF, + 0xF8, 0x00, 0x00, 0xFF, 0xFF, 0xC0, 0x00, 0x07, 0xFF, 0xFE, 0x00, 0x00, + 0x3F, 0xFF, 0xF0, 0x00, 0x01, 0xFF, 0xFF, 0x80, 0x00, 0x0F, 0xFF, 0xFC, + 0x00, 0x00, 0x7F, 0xFF, 0xE0, 0x00, 0x03, 0xFF, 0xFF, 0x00, 0x00, 0x1F, + 0xFF, 0xF8, 0x00, 0x01, 0xFF, 0xFF, 0xC0, 0x00, 0x0F, 0xFF, 0xFE, 0x00, + 0x00, 0x7F, 0xFF, 0xF0, 0x00, 0x03, 0xFE, 0xFF, 0x80, 0x00, 0x3F, 0xF7, + 0xFC, 0x00, 0x01, 0xFF, 0xBF, 0xE0, 0x00, 0x1F, 0xF9, 0xFF, 0x00, 0x01, + 0xFF, 0xCF, 0xF8, 0x00, 0x7F, 0xFC, 0x7F, 0xFF, 0xFF, 0xFF, 0xE3, 0xFF, + 0xFF, 0xFF, 0xFE, 0x1F, 0xFF, 0xFF, 0xFF, 0xE0, 0xFF, 0xFF, 0xFF, 0xFE, + 0x07, 0xFF, 0xFF, 0xFF, 0xE0, 0x3F, 0xFF, 0xFF, 0xFC, 0x01, 0xFF, 0xFF, + 0xFF, 0x80, 0x0F, 0xFF, 0xFF, 0xE0, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, + 0xBF, 0xFF, 0xFF, 0xFF, 0xEF, 0xFF, 0xFF, 0xFF, 0xFB, 0xFF, 0xFF, 0xFF, + 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xBF, 0xFF, 0xFF, 0xFF, 0xEF, 0xFF, 0xFF, + 0xFF, 0xFB, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0x80, 0x00, 0x00, 0x3F, 0xE0, + 0x00, 0x00, 0x0F, 0xF8, 0x00, 0x00, 0x03, 0xFE, 0x00, 0x00, 0x00, 0xFF, + 0x80, 0x00, 0x00, 0x3F, 0xE0, 0x00, 0x00, 0x0F, 0xF8, 0x00, 0x00, 0x03, + 0xFE, 0x00, 0x00, 0x00, 0xFF, 0x80, 0x00, 0x00, 0x3F, 0xE0, 0x00, 0x00, + 0x0F, 0xFF, 0xFF, 0xFF, 0xE3, 0xFF, 0xFF, 0xFF, 0xF8, 0xFF, 0xFF, 0xFF, + 0xFE, 0x3F, 0xFF, 0xFF, 0xFF, 0x8F, 0xFF, 0xFF, 0xFF, 0xE3, 0xFF, 0xFF, + 0xFF, 0xF8, 0xFF, 0xFF, 0xFF, 0xFE, 0x3F, 0xFF, 0xFF, 0xFF, 0x8F, 0xF8, + 0x00, 0x00, 0x03, 0xFE, 0x00, 0x00, 0x00, 0xFF, 0x80, 0x00, 0x00, 0x3F, + 0xE0, 0x00, 0x00, 0x0F, 0xF8, 0x00, 0x00, 0x03, 0xFE, 0x00, 0x00, 0x00, + 0xFF, 0x80, 0x00, 0x00, 0x3F, 0xE0, 0x00, 0x00, 0x0F, 0xF8, 0x00, 0x00, + 0x03, 0xFE, 0x00, 0x00, 0x00, 0xFF, 0x80, 0x00, 0x00, 0x3F, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x01, 0xFF, 0x00, 0x00, 0x03, 0xFE, + 0x00, 0x00, 0x07, 0xFC, 0x00, 0x00, 0x0F, 0xF8, 0x00, 0x00, 0x1F, 0xF0, + 0x00, 0x00, 0x3F, 0xE0, 0x00, 0x00, 0x7F, 0xC0, 0x00, 0x00, 0xFF, 0x80, + 0x00, 0x01, 0xFF, 0x00, 0x00, 0x03, 0xFF, 0xFF, 0xFF, 0xC7, 0xFF, 0xFF, + 0xFF, 0x8F, 0xFF, 0xFF, 0xFF, 0x1F, 0xFF, 0xFF, 0xFE, 0x3F, 0xFF, 0xFF, + 0xFC, 0x7F, 0xFF, 0xFF, 0xF8, 0xFF, 0xFF, 0xFF, 0xF1, 0xFF, 0xFF, 0xFF, + 0xE3, 0xFE, 0x00, 0x00, 0x07, 0xFC, 0x00, 0x00, 0x0F, 0xF8, 0x00, 0x00, + 0x1F, 0xF0, 0x00, 0x00, 0x3F, 0xE0, 0x00, 0x00, 0x7F, 0xC0, 0x00, 0x00, + 0xFF, 0x80, 0x00, 0x01, 0xFF, 0x00, 0x00, 0x03, 0xFE, 0x00, 0x00, 0x07, + 0xFC, 0x00, 0x00, 0x0F, 0xF8, 0x00, 0x00, 0x1F, 0xF0, 0x00, 0x00, 0x3F, + 0xE0, 0x00, 0x00, 0x7F, 0xC0, 0x00, 0x00, 0xFF, 0x80, 0x00, 0x01, 0xFF, + 0x00, 0x00, 0x03, 0xFE, 0x00, 0x00, 0x07, 0xFC, 0x00, 0x00, 0x0F, 0xF8, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xF0, 0x00, 0x00, 0x01, 0xFF, 0xFF, + 0xC0, 0x00, 0x01, 0xFF, 0xFF, 0xFC, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0x80, + 0x00, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x7F, 0xFF, 0xFF, 0xFE, 0x00, 0x3F, + 0xFF, 0xFF, 0xFF, 0xC0, 0x1F, 0xFF, 0xFF, 0xFF, 0xF8, 0x0F, 0xFF, 0xC0, + 0x3F, 0xFF, 0x03, 0xFF, 0xC0, 0x03, 0xFF, 0xC1, 0xFF, 0xC0, 0x00, 0x7F, + 0xF8, 0x7F, 0xE0, 0x00, 0x0F, 0xFE, 0x3F, 0xF0, 0x00, 0x01, 0xFF, 0x8F, + 0xFC, 0x00, 0x00, 0x3F, 0xE7, 0xFE, 0x00, 0x00, 0x0F, 0xE1, 0xFF, 0x80, + 0x00, 0x03, 0x80, 0x7F, 0xC0, 0x00, 0x00, 0x00, 0x1F, 0xF0, 0x00, 0x00, + 0x00, 0x0F, 0xFC, 0x00, 0x00, 0x00, 0x03, 0xFF, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x3F, 0xE0, 0x00, 0x00, 0x00, 0x0F, 0xF8, + 0x00, 0x1F, 0xFF, 0xFF, 0xFE, 0x00, 0x07, 0xFF, 0xFF, 0xFF, 0x80, 0x01, + 0xFF, 0xFF, 0xFF, 0xE0, 0x00, 0x7F, 0xFF, 0xFF, 0xFC, 0x00, 0x1F, 0xFF, + 0xFF, 0xFF, 0x00, 0x07, 0xFF, 0xFF, 0xFF, 0xC0, 0x01, 0xFF, 0xFF, 0xDF, + 0xF0, 0x00, 0x7F, 0xFF, 0xF7, 0xFC, 0x00, 0x00, 0x07, 0xFD, 0xFF, 0x80, + 0x00, 0x01, 0xFF, 0x7F, 0xE0, 0x00, 0x00, 0x7F, 0xCF, 0xFC, 0x00, 0x00, + 0x1F, 0xF3, 0xFF, 0x80, 0x00, 0x07, 0xFC, 0x7F, 0xE0, 0x00, 0x01, 0xFF, + 0x1F, 0xFE, 0x00, 0x01, 0xFF, 0xC3, 0xFF, 0xC0, 0x01, 0xFF, 0xF0, 0xFF, + 0xFE, 0x03, 0xFF, 0xFC, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0xFF, 0xFF, + 0xFF, 0xFF, 0x80, 0x7F, 0xFF, 0xFF, 0xFF, 0xC0, 0x0F, 0xFF, 0xFF, 0xFF, + 0xE0, 0x00, 0xFF, 0xFF, 0xFF, 0xE0, 0x00, 0x1F, 0xFF, 0xFF, 0xE0, 0x00, + 0x00, 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x07, 0xFF, 0x00, 0x00, 0xFF, 0x80, + 0x00, 0x1F, 0xFF, 0xF8, 0x00, 0x01, 0xFF, 0xFF, 0x80, 0x00, 0x1F, 0xFF, + 0xF8, 0x00, 0x01, 0xFF, 0xFF, 0x80, 0x00, 0x1F, 0xFF, 0xF8, 0x00, 0x01, + 0xFF, 0xFF, 0x80, 0x00, 0x1F, 0xFF, 0xF8, 0x00, 0x01, 0xFF, 0xFF, 0x80, + 0x00, 0x1F, 0xFF, 0xF8, 0x00, 0x01, 0xFF, 0xFF, 0x80, 0x00, 0x1F, 0xFF, + 0xF8, 0x00, 0x01, 0xFF, 0xFF, 0x80, 0x00, 0x1F, 0xFF, 0xF8, 0x00, 0x01, + 0xFF, 0xFF, 0x80, 0x00, 0x1F, 0xFF, 0xF8, 0x00, 0x01, 0xFF, 0xFF, 0x80, + 0x00, 0x1F, 0xFF, 0xF8, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x1F, 0xFF, + 0xF8, 0x00, 0x01, 0xFF, 0xFF, 0x80, 0x00, 0x1F, 0xFF, 0xF8, 0x00, 0x01, + 0xFF, 0xFF, 0x80, 0x00, 0x1F, 0xFF, 0xF8, 0x00, 0x01, 0xFF, 0xFF, 0x80, + 0x00, 0x1F, 0xFF, 0xF8, 0x00, 0x01, 0xFF, 0xFF, 0x80, 0x00, 0x1F, 0xFF, + 0xF8, 0x00, 0x01, 0xFF, 0xFF, 0x80, 0x00, 0x1F, 0xFF, 0xF8, 0x00, 0x01, + 0xFF, 0xFF, 0x80, 0x00, 0x1F, 0xFF, 0xF8, 0x00, 0x01, 0xFF, 0xFF, 0x80, + 0x00, 0x1F, 0xFF, 0xF8, 0x00, 0x01, 0xFF, 0xFF, 0x80, 0x00, 0x1F, 0xFF, + 0xF8, 0x00, 0x01, 0xFF, 0xFF, 0x80, 0x00, 0x1F, 0xF0, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, + 0x00, 0x00, 0x0F, 0xF8, 0x00, 0x00, 0x7F, 0xC0, 0x00, 0x03, 0xFE, 0x00, + 0x00, 0x1F, 0xF0, 0x00, 0x00, 0xFF, 0x80, 0x00, 0x07, 0xFC, 0x00, 0x00, + 0x3F, 0xE0, 0x00, 0x01, 0xFF, 0x00, 0x00, 0x0F, 0xF8, 0x00, 0x00, 0x7F, + 0xC0, 0x00, 0x03, 0xFE, 0x00, 0x00, 0x1F, 0xF0, 0x00, 0x00, 0xFF, 0x80, + 0x00, 0x07, 0xFC, 0x00, 0x00, 0x3F, 0xE0, 0x00, 0x01, 0xFF, 0x00, 0x00, + 0x0F, 0xF8, 0x00, 0x00, 0x7F, 0xC0, 0x00, 0x03, 0xFE, 0x00, 0x00, 0x1F, + 0xF0, 0x00, 0x00, 0xFF, 0x80, 0x00, 0x07, 0xFC, 0x00, 0x00, 0x3F, 0xE0, + 0x00, 0x01, 0xFF, 0x00, 0x00, 0x0F, 0xF8, 0x00, 0x00, 0x7F, 0xC0, 0x00, + 0x03, 0xFE, 0x00, 0x00, 0x1F, 0xF0, 0x00, 0x00, 0xFF, 0x80, 0x00, 0x07, + 0xFC, 0x3E, 0x00, 0x3F, 0xFF, 0xF0, 0x01, 0xFF, 0xFF, 0x80, 0x0F, 0xFF, + 0xFC, 0x00, 0x7F, 0xFF, 0xE0, 0x07, 0xFF, 0xFF, 0x80, 0x3F, 0xE7, 0xFC, + 0x03, 0xFF, 0x3F, 0xF8, 0x3F, 0xF9, 0xFF, 0xFF, 0xFF, 0x87, 0xFF, 0xFF, + 0xFC, 0x3F, 0xFF, 0xFF, 0xC0, 0xFF, 0xFF, 0xFE, 0x03, 0xFF, 0xFF, 0xE0, + 0x0F, 0xFF, 0xFC, 0x00, 0x1F, 0xFF, 0xC0, 0x00, 0x1F, 0xF0, 0x00, 0xFF, + 0x80, 0x00, 0x1F, 0xFE, 0xFF, 0x80, 0x00, 0x3F, 0xFC, 0xFF, 0x80, 0x00, + 0x7F, 0xF8, 0xFF, 0x80, 0x00, 0xFF, 0xF0, 0xFF, 0x80, 0x01, 0xFF, 0xE0, + 0xFF, 0x80, 0x03, 0xFF, 0xC0, 0xFF, 0x80, 0x07, 0xFF, 0x80, 0xFF, 0x80, + 0x0F, 0xFF, 0x00, 0xFF, 0x80, 0x1F, 0xFE, 0x00, 0xFF, 0x80, 0x3F, 0xFC, + 0x00, 0xFF, 0x80, 0x7F, 0xF8, 0x00, 0xFF, 0x80, 0xFF, 0xF0, 0x00, 0xFF, + 0x81, 0xFF, 0xE0, 0x00, 0xFF, 0x83, 0xFF, 0xC0, 0x00, 0xFF, 0x87, 0xFF, + 0x80, 0x00, 0xFF, 0x8F, 0xFF, 0x00, 0x00, 0xFF, 0x9F, 0xFE, 0x00, 0x00, + 0xFF, 0xBF, 0xFE, 0x00, 0x00, 0xFF, 0xFF, 0xFE, 0x00, 0x00, 0xFF, 0xFF, + 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x80, + 0x00, 0xFF, 0xFF, 0xFF, 0xC0, 0x00, 0xFF, 0xFF, 0xFF, 0xC0, 0x00, 0xFF, + 0xFF, 0x7F, 0xE0, 0x00, 0xFF, 0xFE, 0x7F, 0xF0, 0x00, 0xFF, 0xFC, 0x3F, + 0xF0, 0x00, 0xFF, 0xF8, 0x3F, 0xF8, 0x00, 0xFF, 0xF0, 0x1F, 0xFC, 0x00, + 0xFF, 0xE0, 0x1F, 0xFC, 0x00, 0xFF, 0xC0, 0x0F, 0xFE, 0x00, 0xFF, 0x80, + 0x07, 0xFF, 0x00, 0xFF, 0x80, 0x07, 0xFF, 0x00, 0xFF, 0x80, 0x03, 0xFF, + 0x80, 0xFF, 0x80, 0x03, 0xFF, 0xC0, 0xFF, 0x80, 0x01, 0xFF, 0xC0, 0xFF, + 0x80, 0x00, 0xFF, 0xE0, 0xFF, 0x80, 0x00, 0xFF, 0xF0, 0xFF, 0x80, 0x00, + 0x7F, 0xF0, 0xFF, 0x80, 0x00, 0x7F, 0xF8, 0xFF, 0x80, 0x00, 0x3F, 0xF8, + 0xFF, 0x80, 0x00, 0x1F, 0xFC, 0xFF, 0x80, 0x00, 0x1F, 0xFE, 0xFF, 0x80, + 0x00, 0x0F, 0xFE, 0xFF, 0x80, 0x00, 0x0F, 0xFF, 0xFF, 0x80, 0x00, 0x00, + 0xFF, 0x80, 0x00, 0x00, 0xFF, 0x80, 0x00, 0x00, 0xFF, 0x80, 0x00, 0x00, + 0xFF, 0x80, 0x00, 0x00, 0xFF, 0x80, 0x00, 0x00, 0xFF, 0x80, 0x00, 0x00, + 0xFF, 0x80, 0x00, 0x00, 0xFF, 0x80, 0x00, 0x00, 0xFF, 0x80, 0x00, 0x00, + 0xFF, 0x80, 0x00, 0x00, 0xFF, 0x80, 0x00, 0x00, 0xFF, 0x80, 0x00, 0x00, + 0xFF, 0x80, 0x00, 0x00, 0xFF, 0x80, 0x00, 0x00, 0xFF, 0x80, 0x00, 0x00, + 0xFF, 0x80, 0x00, 0x00, 0xFF, 0x80, 0x00, 0x00, 0xFF, 0x80, 0x00, 0x00, + 0xFF, 0x80, 0x00, 0x00, 0xFF, 0x80, 0x00, 0x00, 0xFF, 0x80, 0x00, 0x00, + 0xFF, 0x80, 0x00, 0x00, 0xFF, 0x80, 0x00, 0x00, 0xFF, 0x80, 0x00, 0x00, + 0xFF, 0x80, 0x00, 0x00, 0xFF, 0x80, 0x00, 0x00, 0xFF, 0x80, 0x00, 0x00, + 0xFF, 0x80, 0x00, 0x00, 0xFF, 0x80, 0x00, 0x00, 0xFF, 0x80, 0x00, 0x00, + 0xFF, 0x80, 0x00, 0x00, 0xFF, 0x80, 0x00, 0x00, 0xFF, 0x80, 0x00, 0x00, + 0xFF, 0x80, 0x00, 0x00, 0xFF, 0x80, 0x00, 0x00, 0xFF, 0x80, 0x00, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x00, 0x03, + 0xFF, 0xFF, 0xFF, 0xE0, 0x00, 0x3F, 0xFF, 0xFF, 0xFE, 0x00, 0x03, 0xFF, + 0xFF, 0xFF, 0xE0, 0x00, 0x7F, 0xFF, 0xFF, 0xFE, 0x00, 0x07, 0xFF, 0xFF, + 0xFF, 0xF0, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0x00, 0x07, 0xFF, 0xFF, 0xFF, + 0xF0, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xF8, + 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x0F, 0xEF, 0xFF, 0xFF, 0xF8, 0x01, + 0xFE, 0xFF, 0xFF, 0xBF, 0x80, 0x1F, 0xEF, 0xFF, 0xFB, 0xFC, 0x01, 0xFE, + 0xFF, 0xFF, 0xBF, 0xC0, 0x1F, 0xCF, 0xFF, 0xFB, 0xFC, 0x03, 0xFC, 0xFF, + 0xFF, 0x9F, 0xC0, 0x3F, 0xCF, 0xFF, 0xF9, 0xFE, 0x03, 0xFC, 0xFF, 0xFF, + 0x9F, 0xE0, 0x7F, 0x8F, 0xFF, 0xF9, 0xFE, 0x07, 0xF8, 0xFF, 0xFF, 0x8F, + 0xF0, 0x7F, 0x8F, 0xFF, 0xF8, 0xFF, 0x07, 0xF8, 0xFF, 0xFF, 0x8F, 0xF0, + 0xFF, 0x0F, 0xFF, 0xF8, 0xFF, 0x0F, 0xF0, 0xFF, 0xFF, 0x87, 0xF8, 0xFF, + 0x0F, 0xFF, 0xF8, 0x7F, 0x8F, 0xF0, 0xFF, 0xFF, 0x87, 0xF9, 0xFE, 0x0F, + 0xFF, 0xF8, 0x7F, 0x9F, 0xE0, 0xFF, 0xFF, 0x83, 0xFD, 0xFE, 0x0F, 0xFF, + 0xF8, 0x3F, 0xDF, 0xE0, 0xFF, 0xFF, 0x83, 0xFF, 0xFC, 0x0F, 0xFF, 0xF8, + 0x3F, 0xFF, 0xC0, 0xFF, 0xFF, 0x81, 0xFF, 0xFC, 0x0F, 0xFF, 0xF8, 0x1F, + 0xFF, 0xC0, 0xFF, 0xFF, 0x81, 0xFF, 0xF8, 0x0F, 0xFF, 0xF8, 0x1F, 0xFF, + 0x80, 0xFF, 0xFF, 0x80, 0xFF, 0xF8, 0x0F, 0xFF, 0xF8, 0x0F, 0xFF, 0x80, + 0xFF, 0xFF, 0x80, 0xFF, 0xF0, 0x0F, 0xFF, 0xF8, 0x0F, 0xFF, 0x00, 0xFF, + 0xFF, 0x80, 0x7F, 0xF0, 0x0F, 0xFF, 0xF8, 0x07, 0xFF, 0x00, 0xFF, 0xFF, + 0x80, 0x7F, 0xE0, 0x0F, 0xFF, 0xF8, 0x07, 0xFE, 0x00, 0xFF, 0xFF, 0x80, + 0x3F, 0xE0, 0x0F, 0xF0, 0xFF, 0x80, 0x00, 0x1F, 0xFF, 0xF0, 0x00, 0x03, + 0xFF, 0xFF, 0x00, 0x00, 0x7F, 0xFF, 0xF0, 0x00, 0x0F, 0xFF, 0xFE, 0x00, + 0x01, 0xFF, 0xFF, 0xE0, 0x00, 0x3F, 0xFF, 0xFE, 0x00, 0x07, 0xFF, 0xFF, + 0xC0, 0x00, 0xFF, 0xFF, 0xFC, 0x00, 0x1F, 0xFF, 0xFF, 0xC0, 0x03, 0xFF, + 0xFF, 0xF8, 0x00, 0x7F, 0xFF, 0xFF, 0x80, 0x0F, 0xFF, 0xFF, 0xF0, 0x01, + 0xFF, 0xFF, 0xFF, 0x00, 0x3F, 0xFF, 0xFF, 0xF0, 0x07, 0xFF, 0xFF, 0xFE, + 0x00, 0xFF, 0xFF, 0xFF, 0xE0, 0x1F, 0xFF, 0xEF, 0xFE, 0x03, 0xFF, 0xFC, + 0xFF, 0xC0, 0x7F, 0xFF, 0x9F, 0xFC, 0x0F, 0xFF, 0xF1, 0xFF, 0x81, 0xFF, + 0xFE, 0x1F, 0xF8, 0x3F, 0xFF, 0xC3, 0xFF, 0x87, 0xFF, 0xF8, 0x3F, 0xF0, + 0xFF, 0xFF, 0x07, 0xFF, 0x1F, 0xFF, 0xE0, 0x7F, 0xF3, 0xFF, 0xFC, 0x07, + 0xFE, 0x7F, 0xFF, 0x80, 0xFF, 0xEF, 0xFF, 0xF0, 0x0F, 0xFD, 0xFF, 0xFE, + 0x00, 0xFF, 0xFF, 0xFF, 0xC0, 0x1F, 0xFF, 0xFF, 0xF8, 0x01, 0xFF, 0xFF, + 0xFF, 0x00, 0x3F, 0xFF, 0xFF, 0xE0, 0x03, 0xFF, 0xFF, 0xFC, 0x00, 0x3F, + 0xFF, 0xFF, 0x80, 0x07, 0xFF, 0xFF, 0xF0, 0x00, 0x7F, 0xFF, 0xFE, 0x00, + 0x07, 0xFF, 0xFF, 0xC0, 0x00, 0xFF, 0xFF, 0xF8, 0x00, 0x0F, 0xFF, 0xFF, + 0x00, 0x01, 0xFF, 0xFF, 0xE0, 0x00, 0x1F, 0xFF, 0xFC, 0x00, 0x01, 0xFF, + 0xFF, 0x80, 0x00, 0x3F, 0xFF, 0xF0, 0x00, 0x03, 0xFE, 0x00, 0x00, 0xFF, + 0xE0, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0xFF, 0xFF, 0xFE, + 0x00, 0x00, 0x3F, 0xFF, 0xFF, 0xE0, 0x00, 0x1F, 0xFF, 0xFF, 0xFF, 0x00, + 0x07, 0xFF, 0xFF, 0xFF, 0xF0, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x3F, + 0xFF, 0xFF, 0xFF, 0xF0, 0x0F, 0xFF, 0xC0, 0x7F, 0xFE, 0x03, 0xFF, 0xE0, + 0x01, 0xFF, 0xE0, 0x7F, 0xF0, 0x00, 0x1F, 0xFE, 0x1F, 0xFC, 0x00, 0x01, + 0xFF, 0xC3, 0xFF, 0x00, 0x00, 0x1F, 0xFC, 0xFF, 0xC0, 0x00, 0x01, 0xFF, + 0x9F, 0xF8, 0x00, 0x00, 0x3F, 0xF3, 0xFE, 0x00, 0x00, 0x03, 0xFF, 0xFF, + 0xC0, 0x00, 0x00, 0x7F, 0xFF, 0xF8, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0x00, + 0x00, 0x00, 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x1F, 0xFF, 0xF8, 0x00, 0x00, + 0x03, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xE0, 0x00, 0x00, 0x0F, + 0xFF, 0xFC, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x3F, 0xFF, + 0xF0, 0x00, 0x00, 0x07, 0xFF, 0xFE, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xC0, + 0x00, 0x00, 0x1F, 0xFF, 0xFC, 0x00, 0x00, 0x03, 0xFF, 0xFF, 0x80, 0x00, + 0x00, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x1F, 0xFB, 0xFE, 0x00, 0x00, 0x03, + 0xFF, 0x7F, 0xE0, 0x00, 0x00, 0xFF, 0xCF, 0xFC, 0x00, 0x00, 0x1F, 0xF8, + 0xFF, 0xC0, 0x00, 0x07, 0xFF, 0x1F, 0xFC, 0x00, 0x01, 0xFF, 0xC1, 0xFF, + 0xC0, 0x00, 0x7F, 0xF8, 0x3F, 0xFE, 0x00, 0x1F, 0xFE, 0x03, 0xFF, 0xF0, + 0x1F, 0xFF, 0x80, 0x7F, 0xFF, 0xFF, 0xFF, 0xF0, 0x07, 0xFF, 0xFF, 0xFF, + 0xFC, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0x00, 0x07, 0xFF, 0xFF, 0xFF, 0xC0, + 0x00, 0x3F, 0xFF, 0xFF, 0xE0, 0x00, 0x01, 0xFF, 0xFF, 0xF8, 0x00, 0x00, + 0x0F, 0xFF, 0xF8, 0x00, 0x00, 0x00, 0x3F, 0xF8, 0x00, 0x00, 0xFF, 0xFF, + 0xFF, 0x00, 0x3F, 0xFF, 0xFF, 0xF8, 0x0F, 0xFF, 0xFF, 0xFF, 0x83, 0xFF, + 0xFF, 0xFF, 0xF0, 0xFF, 0xFF, 0xFF, 0xFE, 0x3F, 0xFF, 0xFF, 0xFF, 0xCF, + 0xFF, 0xFF, 0xFF, 0xF3, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0x80, 0x07, 0xFF, + 0xBF, 0xE0, 0x00, 0x7F, 0xFF, 0xF8, 0x00, 0x0F, 0xFF, 0xFE, 0x00, 0x03, + 0xFF, 0xFF, 0x80, 0x00, 0x7F, 0xFF, 0xE0, 0x00, 0x1F, 0xFF, 0xF8, 0x00, + 0x07, 0xFF, 0xFE, 0x00, 0x01, 0xFF, 0xFF, 0x80, 0x00, 0xFF, 0xFF, 0xE0, + 0x00, 0x3F, 0xFF, 0xF8, 0x00, 0x3F, 0xFF, 0xFE, 0x00, 0x3F, 0xFE, 0xFF, + 0xFF, 0xFF, 0xFF, 0xBF, 0xFF, 0xFF, 0xFF, 0xCF, 0xFF, 0xFF, 0xFF, 0xF3, + 0xFF, 0xFF, 0xFF, 0xF8, 0xFF, 0xFF, 0xFF, 0xFC, 0x3F, 0xFF, 0xFF, 0xFC, + 0x0F, 0xFF, 0xFF, 0xFE, 0x03, 0xFF, 0xFF, 0xF8, 0x00, 0xFF, 0x80, 0x00, + 0x00, 0x3F, 0xE0, 0x00, 0x00, 0x0F, 0xF8, 0x00, 0x00, 0x03, 0xFE, 0x00, + 0x00, 0x00, 0xFF, 0x80, 0x00, 0x00, 0x3F, 0xE0, 0x00, 0x00, 0x0F, 0xF8, + 0x00, 0x00, 0x03, 0xFE, 0x00, 0x00, 0x00, 0xFF, 0x80, 0x00, 0x00, 0x3F, + 0xE0, 0x00, 0x00, 0x0F, 0xF8, 0x00, 0x00, 0x03, 0xFE, 0x00, 0x00, 0x00, + 0xFF, 0x80, 0x00, 0x00, 0x3F, 0xE0, 0x00, 0x00, 0x0F, 0xF8, 0x00, 0x00, + 0x03, 0xFE, 0x00, 0x00, 0x00, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0xE0, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xF0, 0x00, 0x00, 0x07, 0xFF, + 0xFF, 0xE0, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x1F, 0xFF, 0xFF, + 0xFF, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFC, 0x00, 0x1F, 0xFF, 0xFF, 0xFF, + 0xF0, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x0F, 0xFF, 0xC0, 0x7F, 0xFE, + 0x00, 0xFF, 0xF8, 0x00, 0x7F, 0xF8, 0x07, 0xFF, 0x00, 0x01, 0xFF, 0xE0, + 0x7F, 0xF0, 0x00, 0x07, 0xFF, 0x03, 0xFF, 0x00, 0x00, 0x1F, 0xFC, 0x3F, + 0xF0, 0x00, 0x00, 0x7F, 0xE1, 0xFF, 0x80, 0x00, 0x03, 0xFF, 0x0F, 0xF8, + 0x00, 0x00, 0x0F, 0xFC, 0xFF, 0xC0, 0x00, 0x00, 0x7F, 0xE7, 0xFE, 0x00, + 0x00, 0x03, 0xFF, 0x3F, 0xF0, 0x00, 0x00, 0x0F, 0xF9, 0xFF, 0x00, 0x00, + 0x00, 0x7F, 0xCF, 0xF8, 0x00, 0x00, 0x03, 0xFE, 0x7F, 0xC0, 0x00, 0x00, + 0x1F, 0xF3, 0xFE, 0x00, 0x00, 0x00, 0xFF, 0x9F, 0xF0, 0x00, 0x00, 0x07, + 0xFC, 0xFF, 0x80, 0x00, 0x00, 0x3F, 0xE7, 0xFC, 0x00, 0x00, 0x01, 0xFF, + 0x3F, 0xE0, 0x00, 0x00, 0x0F, 0xF9, 0xFF, 0x00, 0x00, 0x00, 0x7F, 0xCF, + 0xFC, 0x00, 0x00, 0x03, 0xFE, 0x7F, 0xE0, 0x00, 0x00, 0x3F, 0xF3, 0xFF, + 0x00, 0x00, 0x01, 0xFF, 0x8F, 0xF8, 0x00, 0x40, 0x0F, 0xF8, 0x7F, 0xE0, + 0x07, 0xC0, 0xFF, 0xC3, 0xFF, 0x00, 0x3F, 0x87, 0xFE, 0x0F, 0xFC, 0x03, + 0xFE, 0x7F, 0xE0, 0x7F, 0xF0, 0x1F, 0xFF, 0xFF, 0x03, 0xFF, 0xC0, 0x7F, + 0xFF, 0xF0, 0x0F, 0xFF, 0x00, 0xFF, 0xFF, 0x80, 0x3F, 0xFF, 0x01, 0xFF, + 0xF8, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x07, 0xFF, 0xFF, 0xFF, 0xFC, + 0x00, 0x1F, 0xFF, 0xFF, 0xFF, 0xF8, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xF0, + 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, + 0x03, 0xFF, 0xFF, 0x7F, 0xF8, 0x00, 0x03, 0xFF, 0x80, 0xFF, 0xC0, 0x00, + 0x00, 0x00, 0x03, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x07, 0xE0, 0x00, 0x00, + 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0xFF, 0xFF, 0xFF, + 0xC0, 0x00, 0xFF, 0xFF, 0xFF, 0xFC, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0xFF, 0xFF, + 0xFF, 0xFF, 0xE0, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0xFF, 0xFF, 0xFF, 0xFF, + 0xF0, 0xFF, 0x80, 0x00, 0xFF, 0xF0, 0xFF, 0x80, 0x00, 0x3F, 0xF0, 0xFF, + 0x80, 0x00, 0x3F, 0xF0, 0xFF, 0x80, 0x00, 0x1F, 0xF0, 0xFF, 0x80, 0x00, + 0x1F, 0xF8, 0xFF, 0x80, 0x00, 0x1F, 0xF0, 0xFF, 0x80, 0x00, 0x1F, 0xF0, + 0xFF, 0x80, 0x00, 0x3F, 0xF0, 0xFF, 0x80, 0x00, 0x3F, 0xF0, 0xFF, 0x80, + 0x00, 0xFF, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0xFF, 0xFF, 0xFF, 0xFF, + 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0xFF, + 0xFF, 0xFF, 0xFE, 0x00, 0xFF, 0xFF, 0xFF, 0xF8, 0x00, 0xFF, 0xFF, 0xFF, + 0xC0, 0x00, 0xFF, 0xFF, 0xFF, 0xC0, 0x00, 0xFF, 0x81, 0xFF, 0xE0, 0x00, + 0xFF, 0x80, 0x7F, 0xF0, 0x00, 0xFF, 0x80, 0x3F, 0xF8, 0x00, 0xFF, 0x80, + 0x1F, 0xFC, 0x00, 0xFF, 0x80, 0x0F, 0xFE, 0x00, 0xFF, 0x80, 0x0F, 0xFF, + 0x00, 0xFF, 0x80, 0x07, 0xFF, 0x00, 0xFF, 0x80, 0x03, 0xFF, 0x80, 0xFF, + 0x80, 0x03, 0xFF, 0xC0, 0xFF, 0x80, 0x01, 0xFF, 0xC0, 0xFF, 0x80, 0x00, + 0xFF, 0xE0, 0xFF, 0x80, 0x00, 0xFF, 0xF0, 0xFF, 0x80, 0x00, 0x7F, 0xF0, + 0xFF, 0x80, 0x00, 0x3F, 0xF8, 0xFF, 0x80, 0x00, 0x3F, 0xF8, 0xFF, 0x80, + 0x00, 0x1F, 0xFC, 0xFF, 0x80, 0x00, 0x0F, 0xFE, 0xFF, 0x80, 0x00, 0x0F, + 0xFE, 0xFF, 0x80, 0x00, 0x07, 0xFF, 0x00, 0x07, 0xFF, 0x00, 0x00, 0x01, + 0xFF, 0xFF, 0x00, 0x00, 0x3F, 0xFF, 0xFE, 0x00, 0x07, 0xFF, 0xFF, 0xF8, + 0x00, 0x7F, 0xFF, 0xFF, 0xF0, 0x07, 0xFF, 0xFF, 0xFF, 0x80, 0x3F, 0xFF, + 0xFF, 0xFE, 0x03, 0xFF, 0xFF, 0xFF, 0xF8, 0x1F, 0xFE, 0x03, 0xFF, 0xC1, + 0xFF, 0x80, 0x07, 0xFF, 0x0F, 0xF8, 0x00, 0x1F, 0xF8, 0x7F, 0xC0, 0x00, + 0xFF, 0xC3, 0xFE, 0x00, 0x03, 0xFE, 0x1F, 0xF0, 0x00, 0x1F, 0xF8, 0xFF, + 0x80, 0x00, 0xFF, 0xC7, 0xFE, 0x00, 0x00, 0x00, 0x3F, 0xF8, 0x00, 0x00, + 0x00, 0xFF, 0xF8, 0x00, 0x00, 0x07, 0xFF, 0xFC, 0x00, 0x00, 0x1F, 0xFF, + 0xFE, 0x00, 0x00, 0xFF, 0xFF, 0xFE, 0x00, 0x03, 0xFF, 0xFF, 0xFE, 0x00, + 0x0F, 0xFF, 0xFF, 0xF8, 0x00, 0x1F, 0xFF, 0xFF, 0xF0, 0x00, 0x7F, 0xFF, + 0xFF, 0xC0, 0x00, 0x7F, 0xFF, 0xFF, 0x00, 0x00, 0x7F, 0xFF, 0xF8, 0x00, + 0x00, 0x3F, 0xFF, 0xE0, 0x00, 0x00, 0x1F, 0xFF, 0x00, 0x00, 0x00, 0x3F, + 0xF8, 0x00, 0x00, 0x00, 0x7F, 0xE0, 0xF0, 0x00, 0x03, 0xFF, 0xFF, 0x80, + 0x00, 0x0F, 0xFF, 0xFE, 0x00, 0x00, 0x7F, 0xDF, 0xF0, 0x00, 0x03, 0xFE, + 0xFF, 0xC0, 0x00, 0x3F, 0xF7, 0xFF, 0x00, 0x03, 0xFF, 0x9F, 0xFC, 0x00, + 0x3F, 0xF8, 0xFF, 0xF8, 0x07, 0xFF, 0xC3, 0xFF, 0xFF, 0xFF, 0xFC, 0x1F, + 0xFF, 0xFF, 0xFF, 0xE0, 0x7F, 0xFF, 0xFF, 0xFE, 0x01, 0xFF, 0xFF, 0xFF, + 0xE0, 0x07, 0xFF, 0xFF, 0xFE, 0x00, 0x0F, 0xFF, 0xFF, 0xE0, 0x00, 0x1F, + 0xFF, 0xF8, 0x00, 0x00, 0x1F, 0xFE, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x03, 0xFE, 0x00, + 0x00, 0x00, 0x3F, 0xE0, 0x00, 0x00, 0x03, 0xFE, 0x00, 0x00, 0x00, 0x3F, + 0xE0, 0x00, 0x00, 0x03, 0xFE, 0x00, 0x00, 0x00, 0x3F, 0xE0, 0x00, 0x00, + 0x03, 0xFE, 0x00, 0x00, 0x00, 0x3F, 0xE0, 0x00, 0x00, 0x03, 0xFE, 0x00, + 0x00, 0x00, 0x3F, 0xE0, 0x00, 0x00, 0x03, 0xFE, 0x00, 0x00, 0x00, 0x3F, + 0xE0, 0x00, 0x00, 0x03, 0xFE, 0x00, 0x00, 0x00, 0x3F, 0xE0, 0x00, 0x00, + 0x03, 0xFE, 0x00, 0x00, 0x00, 0x3F, 0xE0, 0x00, 0x00, 0x03, 0xFE, 0x00, + 0x00, 0x00, 0x3F, 0xE0, 0x00, 0x00, 0x03, 0xFE, 0x00, 0x00, 0x00, 0x3F, + 0xE0, 0x00, 0x00, 0x03, 0xFE, 0x00, 0x00, 0x00, 0x3F, 0xE0, 0x00, 0x00, + 0x03, 0xFE, 0x00, 0x00, 0x00, 0x3F, 0xE0, 0x00, 0x00, 0x03, 0xFE, 0x00, + 0x00, 0x00, 0x3F, 0xE0, 0x00, 0x00, 0x03, 0xFE, 0x00, 0x00, 0x00, 0x3F, + 0xE0, 0x00, 0x00, 0x03, 0xFE, 0x00, 0x00, 0x00, 0x3F, 0xE0, 0x00, 0x00, + 0x03, 0xFE, 0x00, 0x00, 0x00, 0x3F, 0xE0, 0x00, 0x00, 0x03, 0xFE, 0x00, + 0x00, 0x00, 0x3F, 0xE0, 0x00, 0x00, 0x03, 0xFE, 0x00, 0x00, 0x00, 0x3F, + 0xE0, 0x00, 0x00, 0x03, 0xFE, 0x00, 0x00, 0xFF, 0x80, 0x00, 0x3F, 0xFF, + 0xF0, 0x00, 0x07, 0xFF, 0xFE, 0x00, 0x00, 0xFF, 0xFF, 0xC0, 0x00, 0x1F, + 0xFF, 0xF8, 0x00, 0x03, 0xFF, 0xFF, 0x00, 0x00, 0x7F, 0xFF, 0xE0, 0x00, + 0x0F, 0xFF, 0xFC, 0x00, 0x01, 0xFF, 0xFF, 0x80, 0x00, 0x3F, 0xFF, 0xF0, + 0x00, 0x07, 0xFF, 0xFE, 0x00, 0x00, 0xFF, 0xFF, 0xC0, 0x00, 0x1F, 0xFF, + 0xF8, 0x00, 0x03, 0xFF, 0xFF, 0x00, 0x00, 0x7F, 0xFF, 0xE0, 0x00, 0x0F, + 0xFF, 0xFC, 0x00, 0x01, 0xFF, 0xFF, 0x80, 0x00, 0x3F, 0xFF, 0xF0, 0x00, + 0x07, 0xFF, 0xFE, 0x00, 0x00, 0xFF, 0xFF, 0xC0, 0x00, 0x1F, 0xFF, 0xF8, + 0x00, 0x03, 0xFF, 0xFF, 0x00, 0x00, 0x7F, 0xFF, 0xE0, 0x00, 0x0F, 0xFF, + 0xFC, 0x00, 0x01, 0xFF, 0xFF, 0x80, 0x00, 0x3F, 0xFF, 0xF0, 0x00, 0x07, + 0xFF, 0xFE, 0x00, 0x00, 0xFF, 0xFF, 0xC0, 0x00, 0x1F, 0xFF, 0xF8, 0x00, + 0x03, 0xFF, 0xFF, 0x00, 0x00, 0x7F, 0xFF, 0xE0, 0x00, 0x0F, 0xFF, 0xFC, + 0x00, 0x01, 0xFF, 0xFF, 0x80, 0x00, 0x3F, 0xFF, 0xF0, 0x00, 0x07, 0xFF, + 0xFF, 0x00, 0x01, 0xFF, 0xFF, 0xF0, 0x00, 0x3F, 0xF7, 0xFF, 0x00, 0x1F, + 0xFC, 0xFF, 0xF8, 0x0F, 0xFF, 0x8F, 0xFF, 0xFF, 0xFF, 0xF1, 0xFF, 0xFF, + 0xFF, 0xFC, 0x1F, 0xFF, 0xFF, 0xFF, 0x01, 0xFF, 0xFF, 0xFF, 0xE0, 0x1F, + 0xFF, 0xFF, 0xF0, 0x01, 0xFF, 0xFF, 0xFC, 0x00, 0x0F, 0xFF, 0xFE, 0x00, + 0x00, 0x1F, 0xFE, 0x00, 0x00, 0xFF, 0xC0, 0x00, 0x00, 0xFF, 0xDF, 0xF0, + 0x00, 0x00, 0x3F, 0xE7, 0xFE, 0x00, 0x00, 0x1F, 0xF9, 0xFF, 0x80, 0x00, + 0x07, 0xFE, 0x3F, 0xE0, 0x00, 0x01, 0xFF, 0x0F, 0xFC, 0x00, 0x00, 0xFF, + 0xC3, 0xFF, 0x00, 0x00, 0x3F, 0xF0, 0x7F, 0xC0, 0x00, 0x0F, 0xF8, 0x1F, + 0xF8, 0x00, 0x07, 0xFE, 0x07, 0xFE, 0x00, 0x01, 0xFF, 0x80, 0xFF, 0x80, + 0x00, 0x7F, 0xC0, 0x3F, 0xF0, 0x00, 0x3F, 0xF0, 0x0F, 0xFC, 0x00, 0x0F, + 0xF8, 0x01, 0xFF, 0x80, 0x03, 0xFE, 0x00, 0x7F, 0xE0, 0x01, 0xFF, 0x80, + 0x0F, 0xF8, 0x00, 0x7F, 0xC0, 0x03, 0xFF, 0x00, 0x1F, 0xF0, 0x00, 0xFF, + 0xC0, 0x0F, 0xFC, 0x00, 0x1F, 0xF0, 0x03, 0xFE, 0x00, 0x07, 0xFE, 0x00, + 0xFF, 0x80, 0x01, 0xFF, 0x80, 0x7F, 0xE0, 0x00, 0x3F, 0xE0, 0x1F, 0xF0, + 0x00, 0x0F, 0xFC, 0x07, 0xFC, 0x00, 0x03, 0xFF, 0x03, 0xFF, 0x00, 0x00, + 0x7F, 0xC0, 0xFF, 0x80, 0x00, 0x1F, 0xF8, 0x3F, 0xE0, 0x00, 0x07, 0xFE, + 0x1F, 0xF0, 0x00, 0x00, 0xFF, 0x87, 0xFC, 0x00, 0x00, 0x3F, 0xF1, 0xFF, + 0x00, 0x00, 0x07, 0xFC, 0xFF, 0x80, 0x00, 0x01, 0xFF, 0x3F, 0xE0, 0x00, + 0x00, 0x7F, 0xEF, 0xF8, 0x00, 0x00, 0x0F, 0xFF, 0xFC, 0x00, 0x00, 0x03, + 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x1F, 0xFF, + 0xE0, 0x00, 0x00, 0x07, 0xFF, 0xF8, 0x00, 0x00, 0x01, 0xFF, 0xFE, 0x00, + 0x00, 0x00, 0x3F, 0xFF, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xC0, 0x00, 0x00, + 0x03, 0xFF, 0xE0, 0x00, 0x00, 0x00, 0x7F, 0xF8, 0x00, 0x00, 0x00, 0x1F, + 0xFE, 0x00, 0x00, 0x00, 0x03, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xC0, + 0x00, 0x00, 0xFF, 0xC0, 0x00, 0xFF, 0xF0, 0x00, 0x3F, 0xEF, 0xF8, 0x00, + 0x1F, 0xFE, 0x00, 0x07, 0xFD, 0xFF, 0x00, 0x03, 0xFF, 0xC0, 0x00, 0xFF, + 0xBF, 0xE0, 0x00, 0x7F, 0xF8, 0x00, 0x3F, 0xF7, 0xFE, 0x00, 0x1F, 0xFF, + 0x80, 0x07, 0xFC, 0x7F, 0xC0, 0x03, 0xFF, 0xF0, 0x00, 0xFF, 0x8F, 0xF8, + 0x00, 0x7F, 0xFE, 0x00, 0x1F, 0xF1, 0xFF, 0x00, 0x0F, 0xFF, 0xC0, 0x03, + 0xFE, 0x3F, 0xE0, 0x03, 0xFF, 0xFC, 0x00, 0xFF, 0x87, 0xFE, 0x00, 0x7F, + 0xFF, 0x80, 0x1F, 0xF0, 0x7F, 0xC0, 0x0F, 0xFF, 0xF0, 0x03, 0xFE, 0x0F, + 0xF8, 0x03, 0xFF, 0xFE, 0x00, 0x7F, 0xC1, 0xFF, 0x00, 0x7F, 0xFF, 0xE0, + 0x1F, 0xF0, 0x3F, 0xE0, 0x0F, 0xF7, 0xFC, 0x03, 0xFE, 0x03, 0xFE, 0x01, + 0xFE, 0x7F, 0x80, 0x7F, 0xC0, 0x7F, 0xC0, 0x7F, 0xCF, 0xF0, 0x0F, 0xF8, + 0x0F, 0xF8, 0x0F, 0xF1, 0xFF, 0x01, 0xFE, 0x01, 0xFF, 0x01, 0xFE, 0x3F, + 0xE0, 0x7F, 0xC0, 0x1F, 0xF0, 0x3F, 0xC3, 0xFC, 0x0F, 0xF8, 0x03, 0xFE, + 0x0F, 0xF8, 0x7F, 0x81, 0xFF, 0x00, 0x7F, 0xC1, 0xFE, 0x0F, 0xF8, 0x3F, + 0xC0, 0x0F, 0xF8, 0x3F, 0xC0, 0xFF, 0x0F, 0xF8, 0x00, 0xFF, 0x07, 0xF8, + 0x1F, 0xE1, 0xFF, 0x00, 0x1F, 0xF1, 0xFF, 0x03, 0xFC, 0x3F, 0xE0, 0x03, + 0xFE, 0x3F, 0xC0, 0x7F, 0xC7, 0xF8, 0x00, 0x7F, 0xC7, 0xF8, 0x07, 0xF8, + 0xFF, 0x00, 0x07, 0xF9, 0xFF, 0x00, 0xFF, 0x3F, 0xE0, 0x00, 0xFF, 0xBF, + 0xE0, 0x1F, 0xE7, 0xFC, 0x00, 0x1F, 0xF7, 0xF8, 0x03, 0xFE, 0xFF, 0x00, + 0x03, 0xFE, 0xFF, 0x00, 0x3F, 0xDF, 0xE0, 0x00, 0x3F, 0xFF, 0xE0, 0x07, + 0xFB, 0xFC, 0x00, 0x07, 0xFF, 0xF8, 0x00, 0xFF, 0xFF, 0x80, 0x00, 0xFF, + 0xFF, 0x00, 0x1F, 0xFF, 0xF0, 0x00, 0x1F, 0xFF, 0xE0, 0x01, 0xFF, 0xFC, + 0x00, 0x03, 0xFF, 0xFC, 0x00, 0x3F, 0xFF, 0x80, 0x00, 0x3F, 0xFF, 0x00, + 0x07, 0xFF, 0xF0, 0x00, 0x07, 0xFF, 0xE0, 0x00, 0x7F, 0xFE, 0x00, 0x00, + 0xFF, 0xFC, 0x00, 0x0F, 0xFF, 0x80, 0x00, 0x1F, 0xFF, 0x80, 0x01, 0xFF, + 0xF0, 0x00, 0x01, 0xFF, 0xE0, 0x00, 0x3F, 0xFE, 0x00, 0x00, 0x3F, 0xFC, + 0x00, 0x03, 0xFF, 0xC0, 0x00, 0x07, 0xFF, 0x80, 0x00, 0x7F, 0xF0, 0x00, + 0x00, 0xFF, 0xF0, 0x00, 0x0F, 0xFE, 0x00, 0x00, 0x0F, 0xFC, 0x00, 0x01, + 0xFF, 0xC0, 0x00, 0x01, 0xFF, 0x80, 0x00, 0x1F, 0xF8, 0x00, 0x3F, 0xF0, + 0x00, 0x03, 0xFF, 0x0F, 0xFE, 0x00, 0x01, 0xFF, 0xC1, 0xFF, 0xC0, 0x00, + 0xFF, 0xE0, 0x3F, 0xF0, 0x00, 0x3F, 0xF0, 0x0F, 0xFE, 0x00, 0x1F, 0xFC, + 0x01, 0xFF, 0xC0, 0x0F, 0xFE, 0x00, 0x3F, 0xF0, 0x03, 0xFF, 0x00, 0x0F, + 0xFE, 0x01, 0xFF, 0xC0, 0x01, 0xFF, 0xC0, 0xFF, 0xE0, 0x00, 0x3F, 0xF0, + 0x3F, 0xF0, 0x00, 0x0F, 0xFE, 0x1F, 0xFC, 0x00, 0x01, 0xFF, 0xCF, 0xFE, + 0x00, 0x00, 0x3F, 0xF3, 0xFF, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xC0, 0x00, + 0x01, 0xFF, 0xFF, 0xE0, 0x00, 0x00, 0x3F, 0xFF, 0xF0, 0x00, 0x00, 0x0F, + 0xFF, 0xFC, 0x00, 0x00, 0x01, 0xFF, 0xFE, 0x00, 0x00, 0x00, 0x3F, 0xFF, + 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xC0, 0x00, 0x00, 0x01, 0xFF, 0xE0, 0x00, + 0x00, 0x00, 0x3F, 0xF0, 0x00, 0x00, 0x00, 0x1F, 0xFE, 0x00, 0x00, 0x00, + 0x0F, 0xFF, 0xC0, 0x00, 0x00, 0x03, 0xFF, 0xF0, 0x00, 0x00, 0x01, 0xFF, + 0xFE, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x3F, 0xFF, 0xF0, + 0x00, 0x00, 0x1F, 0xFF, 0xFE, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xC0, 0x00, + 0x03, 0xFF, 0x7F, 0xF0, 0x00, 0x01, 0xFF, 0xCF, 0xFE, 0x00, 0x00, 0xFF, + 0xE1, 0xFF, 0xC0, 0x00, 0x3F, 0xF0, 0x7F, 0xF0, 0x00, 0x1F, 0xFC, 0x0F, + 0xFE, 0x00, 0x0F, 0xFE, 0x01, 0xFF, 0xC0, 0x03, 0xFF, 0x80, 0x7F, 0xF0, + 0x01, 0xFF, 0xC0, 0x0F, 0xFE, 0x00, 0xFF, 0xE0, 0x01, 0xFF, 0xC0, 0x3F, + 0xF8, 0x00, 0x7F, 0xF0, 0x1F, 0xFC, 0x00, 0x0F, 0xFE, 0x0F, 0xFE, 0x00, + 0x01, 0xFF, 0xC3, 0xFF, 0x80, 0x00, 0x7F, 0xF1, 0xFF, 0xC0, 0x00, 0x0F, + 0xFE, 0xFF, 0xE0, 0x00, 0x01, 0xFF, 0xC0, 0xFF, 0xE0, 0x00, 0x01, 0xFF, + 0xDF, 0xF8, 0x00, 0x00, 0x7F, 0xE7, 0xFF, 0x00, 0x00, 0x3F, 0xF8, 0xFF, + 0xE0, 0x00, 0x0F, 0xFC, 0x1F, 0xF8, 0x00, 0x07, 0xFE, 0x07, 0xFF, 0x00, + 0x03, 0xFF, 0x80, 0xFF, 0xC0, 0x00, 0xFF, 0xC0, 0x1F, 0xF8, 0x00, 0x7F, + 0xE0, 0x07, 0xFF, 0x00, 0x1F, 0xF8, 0x00, 0xFF, 0xC0, 0x0F, 0xFC, 0x00, + 0x1F, 0xF8, 0x07, 0xFE, 0x00, 0x07, 0xFE, 0x01, 0xFF, 0x80, 0x00, 0xFF, + 0xC0, 0xFF, 0xC0, 0x00, 0x3F, 0xF8, 0x3F, 0xF0, 0x00, 0x07, 0xFE, 0x1F, + 0xF8, 0x00, 0x00, 0xFF, 0xC7, 0xFC, 0x00, 0x00, 0x3F, 0xF3, 0xFF, 0x00, + 0x00, 0x07, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0xFF, 0xFF, 0xC0, 0x00, 0x00, + 0x3F, 0xFF, 0xF0, 0x00, 0x00, 0x07, 0xFF, 0xF8, 0x00, 0x00, 0x00, 0xFF, + 0xFC, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0x00, 0x00, 0x00, 0x07, 0xFF, 0x80, + 0x00, 0x00, 0x01, 0xFF, 0xE0, 0x00, 0x00, 0x00, 0x3F, 0xF0, 0x00, 0x00, + 0x00, 0x0F, 0xFC, 0x00, 0x00, 0x00, 0x03, 0xFF, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x3F, 0xF0, 0x00, 0x00, 0x00, 0x0F, 0xFC, + 0x00, 0x00, 0x00, 0x03, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xC0, 0x00, + 0x00, 0x00, 0x3F, 0xF0, 0x00, 0x00, 0x00, 0x0F, 0xFC, 0x00, 0x00, 0x00, + 0x03, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x3F, + 0xF0, 0x00, 0x00, 0x00, 0x0F, 0xFC, 0x00, 0x00, 0x00, 0x03, 0xFF, 0x00, + 0x00, 0x00, 0x00, 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x3F, 0xF0, 0x00, 0x00, + 0x00, 0x0F, 0xFC, 0x00, 0x00, 0x00, 0x03, 0xFF, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0xC0, 0x00, 0x00, 0x3F, 0xFF, 0xFF, 0xFF, 0xE3, 0xFF, 0xFF, 0xFF, + 0xFE, 0x3F, 0xFF, 0xFF, 0xFF, 0xE3, 0xFF, 0xFF, 0xFF, 0xFE, 0x3F, 0xFF, + 0xFF, 0xFF, 0xE3, 0xFF, 0xFF, 0xFF, 0xFE, 0x3F, 0xFF, 0xFF, 0xFF, 0xE3, + 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0x00, 0x01, 0xFF, 0xE0, 0x00, 0x00, 0x3F, + 0xFC, 0x00, 0x00, 0x07, 0xFF, 0x80, 0x00, 0x00, 0xFF, 0xF0, 0x00, 0x00, + 0x0F, 0xFF, 0x00, 0x00, 0x01, 0xFF, 0xE0, 0x00, 0x00, 0x3F, 0xFC, 0x00, + 0x00, 0x07, 0xFF, 0x80, 0x00, 0x00, 0xFF, 0xF0, 0x00, 0x00, 0x0F, 0xFE, + 0x00, 0x00, 0x01, 0xFF, 0xC0, 0x00, 0x00, 0x3F, 0xFC, 0x00, 0x00, 0x07, + 0xFF, 0x80, 0x00, 0x00, 0xFF, 0xF0, 0x00, 0x00, 0x1F, 0xFE, 0x00, 0x00, + 0x01, 0xFF, 0xC0, 0x00, 0x00, 0x3F, 0xF8, 0x00, 0x00, 0x07, 0xFF, 0x00, + 0x00, 0x00, 0xFF, 0xF0, 0x00, 0x00, 0x1F, 0xFE, 0x00, 0x00, 0x01, 0xFF, + 0xC0, 0x00, 0x00, 0x3F, 0xF8, 0x00, 0x00, 0x07, 0xFF, 0x00, 0x00, 0x00, + 0xFF, 0xE0, 0x00, 0x00, 0x1F, 0xFE, 0x00, 0x00, 0x01, 0xFF, 0xC0, 0x00, + 0x00, 0x3F, 0xF8, 0x00, 0x00, 0x07, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xE0, + 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0xFF, 0x80, 0xFF, + 0x80, 0xFF, 0x80, 0xFF, 0x80, 0xFF, 0x80, 0xFF, 0x80, 0xFF, 0x80, 0xFF, + 0x80, 0xFF, 0x80, 0xFF, 0x80, 0xFF, 0x80, 0xFF, 0x80, 0xFF, 0x80, 0xFF, + 0x80, 0xFF, 0x80, 0xFF, 0x80, 0xFF, 0x80, 0xFF, 0x80, 0xFF, 0x80, 0xFF, + 0x80, 0xFF, 0x80, 0xFF, 0x80, 0xFF, 0x80, 0xFF, 0x80, 0xFF, 0x80, 0xFF, + 0x80, 0xFF, 0x80, 0xFF, 0x80, 0xFF, 0x80, 0xFF, 0x80, 0xFF, 0x80, 0xFF, + 0x80, 0xFF, 0x80, 0xFF, 0x80, 0xFF, 0x80, 0xFF, 0x80, 0xFF, 0x80, 0xFF, + 0x80, 0xFF, 0x80, 0xFF, 0x80, 0xFF, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, + 0x00, 0x3F, 0x80, 0x07, 0xE0, 0x01, 0xF8, 0x00, 0x7E, 0x00, 0x1F, 0xC0, + 0x03, 0xF0, 0x00, 0xFC, 0x00, 0x3F, 0x00, 0x0F, 0xE0, 0x01, 0xF8, 0x00, + 0x7E, 0x00, 0x1F, 0x80, 0x07, 0xF0, 0x00, 0xFC, 0x00, 0x3F, 0x00, 0x0F, + 0xC0, 0x03, 0xF8, 0x00, 0xFE, 0x00, 0x1F, 0x80, 0x07, 0xE0, 0x01, 0xFC, + 0x00, 0x7F, 0x00, 0x0F, 0xC0, 0x03, 0xF0, 0x00, 0xFE, 0x00, 0x3F, 0x80, + 0x07, 0xE0, 0x01, 0xF8, 0x00, 0x7F, 0x00, 0x1F, 0xC0, 0x03, 0xF0, 0x00, + 0xFC, 0x00, 0x3F, 0x80, 0x0F, 0xE0, 0x01, 0xF8, 0x00, 0x7E, 0x00, 0x1F, + 0xC0, 0x07, 0xF0, 0x00, 0xFC, 0x00, 0x3F, 0x00, 0x0F, 0xE0, 0x03, 0xF8, + 0x00, 0x7E, 0x00, 0x1F, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0xFE, 0x03, 0xFC, + 0x07, 0xF8, 0x0F, 0xF0, 0x1F, 0xE0, 0x3F, 0xC0, 0x7F, 0x80, 0xFF, 0x01, + 0xFE, 0x03, 0xFC, 0x07, 0xF8, 0x0F, 0xF0, 0x1F, 0xE0, 0x3F, 0xC0, 0x7F, + 0x80, 0xFF, 0x01, 0xFE, 0x03, 0xFC, 0x07, 0xF8, 0x0F, 0xF0, 0x1F, 0xE0, + 0x3F, 0xC0, 0x7F, 0x80, 0xFF, 0x01, 0xFE, 0x03, 0xFC, 0x07, 0xF8, 0x0F, + 0xF0, 0x1F, 0xE0, 0x3F, 0xC0, 0x7F, 0x80, 0xFF, 0x01, 0xFE, 0x03, 0xFC, + 0x07, 0xF8, 0x0F, 0xF0, 0x1F, 0xE0, 0x3F, 0xC0, 0x7F, 0x80, 0xFF, 0x01, + 0xFE, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x00, 0x1F, 0xC0, 0x00, 0x01, 0xFF, + 0x00, 0x00, 0x0F, 0xF8, 0x00, 0x00, 0xFF, 0xE0, 0x00, 0x07, 0xFF, 0x00, + 0x00, 0x7F, 0xFC, 0x00, 0x03, 0xFF, 0xE0, 0x00, 0x3F, 0xFF, 0x00, 0x01, + 0xFF, 0xFC, 0x00, 0x1F, 0xFF, 0xE0, 0x00, 0xFF, 0x7F, 0x80, 0x07, 0xFB, + 0xFC, 0x00, 0x7F, 0x8F, 0xF0, 0x03, 0xFC, 0x7F, 0x80, 0x3F, 0xC1, 0xFE, + 0x01, 0xFE, 0x0F, 0xF0, 0x1F, 0xE0, 0x7F, 0xC0, 0xFF, 0x01, 0xFE, 0x0F, + 0xF8, 0x0F, 0xF8, 0x7F, 0x80, 0x3F, 0xC7, 0xFC, 0x01, 0xFF, 0x3F, 0xC0, + 0x0F, 0xFB, 0xFE, 0x00, 0x3F, 0xFF, 0xF0, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x7F, 0xC0, 0xFF, 0x81, 0xFE, 0x03, 0xFC, + 0x07, 0xF0, 0x1F, 0xE0, 0x3F, 0x80, 0x7F, 0x00, 0xFC, 0x00, 0x3F, 0xF8, + 0x00, 0x03, 0xFF, 0xFE, 0x00, 0x0F, 0xFF, 0xFF, 0x00, 0x3F, 0xFF, 0xFF, + 0x00, 0xFF, 0xFF, 0xFF, 0x03, 0xFF, 0xFF, 0xFE, 0x0F, 0xFF, 0xFF, 0xFC, + 0x1F, 0xF8, 0x1F, 0xFC, 0x3F, 0xC0, 0x0F, 0xF8, 0xFF, 0x80, 0x1F, 0xF0, + 0x1E, 0x00, 0x3F, 0xE0, 0x00, 0x00, 0x3F, 0xC0, 0x00, 0x01, 0xFF, 0x80, + 0x00, 0x1F, 0xFF, 0x00, 0x07, 0xFF, 0xFE, 0x00, 0xFF, 0xFF, 0xFC, 0x07, + 0xFF, 0xFF, 0xF8, 0x3F, 0xFF, 0xFF, 0xF0, 0xFF, 0xFF, 0xFF, 0xE1, 0xFF, + 0xFF, 0x3F, 0xC7, 0xFF, 0xC0, 0x7F, 0x8F, 0xFC, 0x00, 0xFF, 0x3F, 0xE0, + 0x03, 0xFE, 0x7F, 0xC0, 0x07, 0xFC, 0xFF, 0x80, 0x0F, 0xF9, 0xFF, 0x00, + 0x3F, 0xF3, 0xFF, 0x00, 0xFF, 0xE3, 0xFF, 0x07, 0xFF, 0xC7, 0xFF, 0xFF, + 0xFF, 0x8F, 0xFF, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0xFF, 0x0F, 0xFF, 0xF9, + 0xFE, 0x1F, 0xFF, 0xE3, 0xFC, 0x0F, 0xFF, 0x07, 0xFC, 0x07, 0xF8, 0x00, + 0x00, 0xFF, 0x80, 0x00, 0x00, 0xFF, 0x80, 0x00, 0x00, 0xFF, 0x80, 0x00, + 0x00, 0xFF, 0x80, 0x00, 0x00, 0xFF, 0x80, 0x00, 0x00, 0xFF, 0x80, 0x00, + 0x00, 0xFF, 0x80, 0x00, 0x00, 0xFF, 0x80, 0x00, 0x00, 0xFF, 0x80, 0x00, + 0x00, 0xFF, 0x80, 0x00, 0x00, 0xFF, 0x80, 0x00, 0x00, 0xFF, 0x81, 0xFC, + 0x00, 0xFF, 0x8F, 0xFF, 0x00, 0xFF, 0x9F, 0xFF, 0xC0, 0xFF, 0xBF, 0xFF, + 0xE0, 0xFF, 0xFF, 0xFF, 0xF0, 0xFF, 0xFF, 0xFF, 0xF8, 0xFF, 0xFF, 0xFF, + 0xFC, 0xFF, 0xF8, 0x3F, 0xFC, 0xFF, 0xF0, 0x0F, 0xFE, 0xFF, 0xE0, 0x07, + 0xFE, 0xFF, 0xC0, 0x03, 0xFE, 0xFF, 0xC0, 0x03, 0xFE, 0xFF, 0x80, 0x01, + 0xFF, 0xFF, 0x80, 0x01, 0xFF, 0xFF, 0x80, 0x01, 0xFF, 0xFF, 0x80, 0x01, + 0xFF, 0xFF, 0x80, 0x01, 0xFF, 0xFF, 0x80, 0x01, 0xFF, 0xFF, 0x80, 0x01, + 0xFF, 0xFF, 0x80, 0x01, 0xFF, 0xFF, 0x80, 0x01, 0xFF, 0xFF, 0x80, 0x01, + 0xFF, 0xFF, 0x80, 0x01, 0xFF, 0xFF, 0xC0, 0x03, 0xFE, 0xFF, 0xC0, 0x03, + 0xFE, 0xFF, 0xE0, 0x07, 0xFE, 0xFF, 0xF0, 0x0F, 0xFC, 0xFF, 0xFC, 0x1F, + 0xFC, 0xFF, 0xFF, 0xFF, 0xF8, 0xFF, 0xFF, 0xFF, 0xF8, 0xFF, 0x7F, 0xFF, + 0xF0, 0xFF, 0x3F, 0xFF, 0xE0, 0xFF, 0x1F, 0xFF, 0xC0, 0xFF, 0x07, 0xFF, + 0x00, 0x00, 0x01, 0xFC, 0x00, 0x00, 0x1F, 0xF0, 0x00, 0x03, 0xFF, 0xF8, + 0x00, 0x3F, 0xFF, 0xF8, 0x01, 0xFF, 0xFF, 0xF0, 0x0F, 0xFF, 0xFF, 0xE0, + 0x7F, 0xFF, 0xFF, 0xC3, 0xFF, 0xFF, 0xFF, 0x0F, 0xFE, 0x0F, 0xFE, 0x7F, + 0xE0, 0x0F, 0xF9, 0xFF, 0x00, 0x1F, 0xE7, 0xFC, 0x00, 0x7F, 0xFF, 0xE0, + 0x01, 0xF0, 0xFF, 0x80, 0x00, 0x03, 0xFE, 0x00, 0x00, 0x0F, 0xF8, 0x00, + 0x00, 0x3F, 0xE0, 0x00, 0x00, 0xFF, 0x80, 0x00, 0x03, 0xFE, 0x00, 0x00, + 0x0F, 0xF8, 0x00, 0x00, 0x3F, 0xE0, 0x00, 0x00, 0xFF, 0x80, 0x00, 0x03, + 0xFE, 0x00, 0x00, 0x0F, 0xF8, 0x00, 0x3C, 0x3F, 0xE0, 0x00, 0xFF, 0x7F, + 0xC0, 0x07, 0xFD, 0xFF, 0x00, 0x1F, 0xF7, 0xFE, 0x00, 0xFF, 0x8F, 0xFE, + 0x0F, 0xFE, 0x3F, 0xFF, 0xFF, 0xF0, 0x7F, 0xFF, 0xFF, 0xC0, 0xFF, 0xFF, + 0xFE, 0x01, 0xFF, 0xFF, 0xF0, 0x03, 0xFF, 0xFF, 0x80, 0x03, 0xFF, 0xF8, + 0x00, 0x01, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFE, 0x00, 0x00, 0x03, + 0xFC, 0x00, 0x00, 0x07, 0xF8, 0x00, 0x00, 0x0F, 0xF0, 0x00, 0x00, 0x1F, + 0xE0, 0x00, 0x00, 0x3F, 0xC0, 0x00, 0x00, 0x7F, 0x80, 0x00, 0x00, 0xFF, + 0x00, 0x00, 0x01, 0xFE, 0x00, 0x00, 0x03, 0xFC, 0x00, 0x00, 0x07, 0xF8, + 0x01, 0xFC, 0x0F, 0xF0, 0x1F, 0xFE, 0x1F, 0xE0, 0x7F, 0xFF, 0x3F, 0xC3, + 0xFF, 0xFF, 0x7F, 0x8F, 0xFF, 0xFF, 0xFF, 0x1F, 0xFF, 0xFF, 0xFE, 0x7F, + 0xFF, 0xFF, 0xFD, 0xFF, 0xE0, 0xFF, 0xFB, 0xFF, 0x00, 0x7F, 0xF7, 0xFC, + 0x00, 0x7F, 0xFF, 0xF8, 0x00, 0xFF, 0xFF, 0xE0, 0x00, 0xFF, 0xFF, 0xC0, + 0x01, 0xFF, 0xFF, 0x80, 0x03, 0xFF, 0xFF, 0x00, 0x03, 0xFF, 0xFC, 0x00, + 0x07, 0xFF, 0xF8, 0x00, 0x0F, 0xFF, 0xF0, 0x00, 0x1F, 0xFF, 0xE0, 0x00, + 0x3F, 0xFF, 0xC0, 0x00, 0x7F, 0xFF, 0xC0, 0x00, 0xFF, 0xFF, 0x80, 0x03, + 0xFF, 0xFF, 0x00, 0x07, 0xFF, 0xFE, 0x00, 0x0F, 0xFF, 0xFE, 0x00, 0x3F, + 0xF7, 0xFC, 0x00, 0x7F, 0xEF, 0xFC, 0x01, 0xFF, 0xCF, 0xFE, 0x0F, 0xFF, + 0x9F, 0xFF, 0xFF, 0xFF, 0x1F, 0xFF, 0xFF, 0xFE, 0x1F, 0xFF, 0xFB, 0xFC, + 0x1F, 0xFF, 0xE7, 0xF8, 0x1F, 0xFF, 0x8F, 0xF0, 0x1F, 0xFE, 0x1F, 0xE0, + 0x07, 0xE0, 0x00, 0x00, 0x00, 0x1F, 0xE0, 0x00, 0x01, 0xFF, 0xF8, 0x00, + 0x07, 0xFF, 0xFC, 0x00, 0x3F, 0xFF, 0xFC, 0x00, 0xFF, 0xFF, 0xFC, 0x01, + 0xFF, 0xFF, 0xFC, 0x07, 0xFF, 0xFF, 0xFC, 0x1F, 0xFC, 0x1F, 0xF8, 0x3F, + 0xE0, 0x1F, 0xF8, 0xFF, 0xC0, 0x1F, 0xF1, 0xFF, 0x00, 0x1F, 0xE3, 0xFE, + 0x00, 0x3F, 0xEF, 0xF8, 0x00, 0x3F, 0xDF, 0xF0, 0x00, 0x7F, 0xBF, 0xE0, + 0x00, 0xFF, 0x7F, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFD, 0xFF, 0xFF, + 0xFF, 0xFB, 0xFF, 0xFF, 0xFF, 0xF7, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x00, + 0x00, 0x1F, 0xF0, 0x00, 0x00, 0x3F, 0xE0, 0x00, 0x00, 0x3F, 0xC0, 0x03, + 0xE0, 0x7F, 0xC0, 0x07, 0xFC, 0xFF, 0xC0, 0x1F, 0xF8, 0xFF, 0xC0, 0x3F, + 0xE1, 0xFF, 0xC1, 0xFF, 0xC1, 0xFF, 0xFF, 0xFF, 0x03, 0xFF, 0xFF, 0xFC, + 0x03, 0xFF, 0xFF, 0xF8, 0x03, 0xFF, 0xFF, 0xE0, 0x03, 0xFF, 0xFF, 0x00, + 0x01, 0xFF, 0xFC, 0x00, 0x00, 0x7F, 0xC0, 0x00, 0x00, 0x1F, 0xE0, 0x03, + 0xFF, 0xF0, 0x1F, 0xFF, 0xC0, 0xFF, 0xFE, 0x07, 0xFF, 0xF8, 0x1F, 0xFF, + 0xE0, 0x7F, 0xFF, 0x81, 0xFF, 0x00, 0x07, 0xFC, 0x00, 0x1F, 0xE0, 0x00, + 0x7F, 0x80, 0x01, 0xFE, 0x00, 0x07, 0xF8, 0x03, 0xFF, 0xFF, 0xCF, 0xFF, + 0xFF, 0x3F, 0xFF, 0xFC, 0xFF, 0xFF, 0xF3, 0xFF, 0xFF, 0xCF, 0xFF, 0xFF, + 0x3F, 0xFF, 0xFC, 0x07, 0xF8, 0x00, 0x1F, 0xE0, 0x00, 0x7F, 0x80, 0x01, + 0xFE, 0x00, 0x07, 0xF8, 0x00, 0x1F, 0xE0, 0x00, 0x7F, 0x80, 0x01, 0xFE, + 0x00, 0x07, 0xF8, 0x00, 0x1F, 0xE0, 0x00, 0x7F, 0x80, 0x01, 0xFE, 0x00, + 0x07, 0xF8, 0x00, 0x1F, 0xE0, 0x00, 0x7F, 0x80, 0x01, 0xFE, 0x00, 0x07, + 0xF8, 0x00, 0x1F, 0xE0, 0x00, 0x7F, 0x80, 0x01, 0xFE, 0x00, 0x07, 0xF8, + 0x00, 0x1F, 0xE0, 0x00, 0x7F, 0x80, 0x01, 0xFE, 0x00, 0x07, 0xF8, 0x00, + 0x1F, 0xE0, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x03, 0xFF, 0xC3, 0xFC, 0x0F, + 0xFF, 0xC7, 0xF8, 0x7F, 0xFF, 0xEF, 0xF1, 0xFF, 0xFF, 0xDF, 0xE3, 0xFF, + 0xFF, 0xFF, 0xCF, 0xFF, 0xFF, 0xFF, 0xBF, 0xFC, 0x1F, 0xFF, 0x7F, 0xE0, + 0x0F, 0xFE, 0xFF, 0x80, 0x0F, 0xFF, 0xFF, 0x00, 0x0F, 0xFF, 0xFC, 0x00, + 0x1F, 0xFF, 0xF8, 0x00, 0x3F, 0xFF, 0xF0, 0x00, 0x3F, 0xFF, 0xE0, 0x00, + 0x7F, 0xFF, 0x80, 0x00, 0xFF, 0xFF, 0x00, 0x01, 0xFF, 0xFE, 0x00, 0x03, + 0xFF, 0xFC, 0x00, 0x07, 0xFF, 0xFC, 0x00, 0x0F, 0xFF, 0xF8, 0x00, 0x1F, + 0xFF, 0xF0, 0x00, 0x7F, 0xFF, 0xE0, 0x00, 0xFF, 0xFF, 0xE0, 0x01, 0xFF, + 0x7F, 0xC0, 0x07, 0xFE, 0xFF, 0xC0, 0x1F, 0xFD, 0xFF, 0xE0, 0xFF, 0xF9, + 0xFF, 0xFF, 0xFF, 0xF1, 0xFF, 0xFF, 0xFF, 0xE3, 0xFF, 0xFF, 0xBF, 0xC3, + 0xFF, 0xFE, 0x7F, 0x83, 0xFF, 0xF8, 0xFF, 0x01, 0xFF, 0xE1, 0xFE, 0x00, + 0xFE, 0x03, 0xFC, 0x00, 0x00, 0x07, 0xF8, 0x00, 0x00, 0x0F, 0xF7, 0xC0, + 0x00, 0x1F, 0xEF, 0xFC, 0x00, 0x7F, 0xDF, 0xF8, 0x00, 0xFF, 0xBF, 0xF0, + 0x03, 0xFF, 0x7F, 0xF8, 0x1F, 0xFC, 0x7F, 0xFF, 0xFF, 0xF8, 0xFF, 0xFF, + 0xFF, 0xE0, 0xFF, 0xFF, 0xFF, 0xC0, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, + 0xFC, 0x00, 0x7F, 0xFF, 0xE0, 0x00, 0x1F, 0xFE, 0x00, 0xFF, 0x80, 0x00, + 0x03, 0xFE, 0x00, 0x00, 0x0F, 0xF8, 0x00, 0x00, 0x3F, 0xE0, 0x00, 0x00, + 0xFF, 0x80, 0x00, 0x03, 0xFE, 0x00, 0x00, 0x0F, 0xF8, 0x00, 0x00, 0x3F, + 0xE0, 0x00, 0x00, 0xFF, 0x80, 0x00, 0x03, 0xFE, 0x00, 0x00, 0x0F, 0xF8, + 0x00, 0x00, 0x3F, 0xE0, 0x3F, 0x80, 0xFF, 0x87, 0xFF, 0x83, 0xFE, 0x3F, + 0xFF, 0x8F, 0xF9, 0xFF, 0xFF, 0x3F, 0xEF, 0xFF, 0xFC, 0xFF, 0xFF, 0xFF, + 0xFB, 0xFF, 0xFF, 0xFF, 0xEF, 0xFF, 0xC1, 0xFF, 0xFF, 0xFC, 0x03, 0xFF, + 0xFF, 0xE0, 0x07, 0xFF, 0xFF, 0x00, 0x1F, 0xFF, 0xFC, 0x00, 0x7F, 0xFF, + 0xF0, 0x01, 0xFF, 0xFF, 0x80, 0x03, 0xFF, 0xFE, 0x00, 0x0F, 0xFF, 0xF8, + 0x00, 0x3F, 0xFF, 0xE0, 0x00, 0xFF, 0xFF, 0x80, 0x03, 0xFF, 0xFE, 0x00, + 0x0F, 0xFF, 0xF8, 0x00, 0x3F, 0xFF, 0xE0, 0x00, 0xFF, 0xFF, 0x80, 0x03, + 0xFF, 0xFE, 0x00, 0x0F, 0xFF, 0xF8, 0x00, 0x3F, 0xFF, 0xE0, 0x00, 0xFF, + 0xFF, 0x80, 0x03, 0xFF, 0xFE, 0x00, 0x0F, 0xFF, 0xF8, 0x00, 0x3F, 0xFF, + 0xE0, 0x00, 0xFF, 0xFF, 0x80, 0x03, 0xFF, 0xFE, 0x00, 0x0F, 0xFF, 0xF8, + 0x00, 0x3F, 0xFF, 0xE0, 0x00, 0xFF, 0xFF, 0x80, 0x03, 0xFC, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, + 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, 0x01, 0xFF, 0x01, + 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, + 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, + 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, + 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, + 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, + 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x03, + 0xFF, 0x7F, 0xFF, 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFC, 0xFF, + 0xF8, 0xFF, 0xF0, 0x3F, 0xC0, 0xFF, 0x80, 0x00, 0x03, 0xFE, 0x00, 0x00, + 0x0F, 0xF8, 0x00, 0x00, 0x3F, 0xE0, 0x00, 0x00, 0xFF, 0x80, 0x00, 0x03, + 0xFE, 0x00, 0x00, 0x0F, 0xF8, 0x00, 0x00, 0x3F, 0xE0, 0x00, 0x00, 0xFF, + 0x80, 0x00, 0x03, 0xFE, 0x00, 0x00, 0x0F, 0xF8, 0x00, 0x00, 0x3F, 0xE0, + 0x00, 0x00, 0xFF, 0x80, 0x1F, 0xFB, 0xFE, 0x00, 0xFF, 0xCF, 0xF8, 0x07, + 0xFE, 0x3F, 0xE0, 0x3F, 0xF0, 0xFF, 0x81, 0xFF, 0x83, 0xFE, 0x0F, 0xFE, + 0x0F, 0xF8, 0x7F, 0xF0, 0x3F, 0xE3, 0xFF, 0x80, 0xFF, 0x8F, 0xFC, 0x03, + 0xFE, 0x7F, 0xE0, 0x0F, 0xFB, 0xFF, 0x00, 0x3F, 0xFF, 0xF8, 0x00, 0xFF, + 0xFF, 0xE0, 0x03, 0xFF, 0xFF, 0x80, 0x0F, 0xFF, 0xFF, 0x00, 0x3F, 0xFF, + 0xFC, 0x00, 0xFF, 0xFF, 0xF8, 0x03, 0xFF, 0xFF, 0xF0, 0x0F, 0xFF, 0xFF, + 0xC0, 0x3F, 0xFC, 0xFF, 0x80, 0xFF, 0xE3, 0xFE, 0x03, 0xFF, 0x07, 0xFC, + 0x0F, 0xF8, 0x1F, 0xF0, 0x3F, 0xE0, 0x3F, 0xE0, 0xFF, 0x80, 0xFF, 0xC3, + 0xFE, 0x01, 0xFF, 0x0F, 0xF8, 0x03, 0xFE, 0x3F, 0xE0, 0x0F, 0xF8, 0xFF, + 0x80, 0x1F, 0xF3, 0xFE, 0x00, 0x7F, 0xCF, 0xF8, 0x00, 0xFF, 0xBF, 0xE0, + 0x03, 0xFF, 0xFF, 0x80, 0x07, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x01, 0xF8, 0x00, 0x3F, 0x80, 0xFF, 0x0F, 0xFF, + 0x00, 0xFF, 0xE0, 0xFF, 0x1F, 0xFF, 0x83, 0xFF, 0xF8, 0xFF, 0x3F, 0xFF, + 0xC7, 0xFF, 0xFC, 0xFF, 0x7F, 0xFF, 0xEF, 0xFF, 0xFC, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xF8, 0x7F, + 0xFF, 0x07, 0xFE, 0xFF, 0xE0, 0x1F, 0xFE, 0x03, 0xFF, 0xFF, 0xC0, 0x1F, + 0xFC, 0x01, 0xFF, 0xFF, 0x80, 0x1F, 0xF8, 0x01, 0xFF, 0xFF, 0x80, 0x1F, + 0xF8, 0x01, 0xFF, 0xFF, 0x80, 0x0F, 0xF8, 0x01, 0xFF, 0xFF, 0x80, 0x0F, + 0xF8, 0x01, 0xFF, 0xFF, 0x80, 0x0F, 0xF0, 0x01, 0xFF, 0xFF, 0x80, 0x0F, + 0xF0, 0x01, 0xFF, 0xFF, 0x80, 0x0F, 0xF0, 0x01, 0xFF, 0xFF, 0x80, 0x0F, + 0xF0, 0x01, 0xFF, 0xFF, 0x80, 0x0F, 0xF0, 0x01, 0xFF, 0xFF, 0x80, 0x0F, + 0xF0, 0x01, 0xFF, 0xFF, 0x80, 0x0F, 0xF0, 0x01, 0xFF, 0xFF, 0x80, 0x0F, + 0xF0, 0x01, 0xFF, 0xFF, 0x80, 0x0F, 0xF0, 0x01, 0xFF, 0xFF, 0x80, 0x0F, + 0xF0, 0x01, 0xFF, 0xFF, 0x80, 0x0F, 0xF0, 0x01, 0xFF, 0xFF, 0x80, 0x0F, + 0xF0, 0x01, 0xFF, 0xFF, 0x80, 0x0F, 0xF0, 0x01, 0xFF, 0xFF, 0x80, 0x0F, + 0xF0, 0x01, 0xFF, 0xFF, 0x80, 0x0F, 0xF0, 0x01, 0xFF, 0xFF, 0x80, 0x0F, + 0xF0, 0x01, 0xFF, 0xFF, 0x80, 0x0F, 0xF0, 0x01, 0xFF, 0xFF, 0x80, 0x0F, + 0xF0, 0x01, 0xFF, 0xFF, 0x80, 0x0F, 0xF0, 0x01, 0xFF, 0xFF, 0x80, 0x0F, + 0xF0, 0x01, 0xFF, 0x00, 0x00, 0xFE, 0x03, 0xFC, 0x1F, 0xFE, 0x0F, 0xF0, + 0xFF, 0xFE, 0x3F, 0xC7, 0xFF, 0xFC, 0xFF, 0x3F, 0xFF, 0xFB, 0xFD, 0xFF, + 0xFF, 0xEF, 0xFF, 0xFF, 0xFF, 0xBF, 0xFF, 0x07, 0xFF, 0xFF, 0xF0, 0x0F, + 0xFF, 0xFF, 0x80, 0x1F, 0xFF, 0xFC, 0x00, 0x7F, 0xFF, 0xF0, 0x01, 0xFF, + 0xFF, 0xC0, 0x07, 0xFF, 0xFE, 0x00, 0x1F, 0xFF, 0xF8, 0x00, 0x3F, 0xFF, + 0xE0, 0x00, 0xFF, 0xFF, 0x80, 0x03, 0xFF, 0xFE, 0x00, 0x0F, 0xFF, 0xF8, + 0x00, 0x3F, 0xFF, 0xE0, 0x00, 0xFF, 0xFF, 0x80, 0x03, 0xFF, 0xFE, 0x00, + 0x0F, 0xFF, 0xF8, 0x00, 0x3F, 0xFF, 0xE0, 0x00, 0xFF, 0xFF, 0x80, 0x03, + 0xFF, 0xFE, 0x00, 0x0F, 0xFF, 0xF8, 0x00, 0x3F, 0xFF, 0xE0, 0x00, 0xFF, + 0xFF, 0x80, 0x03, 0xFF, 0xFE, 0x00, 0x0F, 0xFF, 0xF8, 0x00, 0x3F, 0xFF, + 0xE0, 0x00, 0xFF, 0xFF, 0x80, 0x03, 0xFF, 0xFE, 0x00, 0x0F, 0xF0, 0x00, + 0x0F, 0xF8, 0x00, 0x00, 0x3F, 0xFF, 0x80, 0x00, 0x7F, 0xFF, 0xE0, 0x00, + 0x7F, 0xFF, 0xFC, 0x00, 0x7F, 0xFF, 0xFF, 0x00, 0x7F, 0xFF, 0xFF, 0xC0, + 0x7F, 0xFF, 0xFF, 0xF0, 0x7F, 0xF8, 0x3F, 0xFC, 0x3F, 0xF0, 0x07, 0xFE, + 0x3F, 0xF0, 0x01, 0xFF, 0x9F, 0xF0, 0x00, 0x7F, 0xDF, 0xF8, 0x00, 0x3F, + 0xEF, 0xF8, 0x00, 0x0F, 0xFF, 0xFC, 0x00, 0x07, 0xFF, 0xFE, 0x00, 0x03, + 0xFF, 0xFE, 0x00, 0x01, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x80, 0x00, + 0x7F, 0xFF, 0xC0, 0x00, 0x3F, 0xFF, 0xE0, 0x00, 0x1F, 0xFF, 0xF8, 0x00, + 0x0F, 0xFF, 0xFC, 0x00, 0x07, 0xFF, 0xFE, 0x00, 0x03, 0xFF, 0xFF, 0x00, + 0x03, 0xFE, 0x7F, 0xC0, 0x01, 0xFF, 0x3F, 0xF0, 0x01, 0xFF, 0x9F, 0xFC, + 0x01, 0xFF, 0x87, 0xFF, 0x83, 0xFF, 0x81, 0xFF, 0xFF, 0xFF, 0xC0, 0xFF, + 0xFF, 0xFF, 0xC0, 0x3F, 0xFF, 0xFF, 0xC0, 0x0F, 0xFF, 0xFF, 0xC0, 0x01, + 0xFF, 0xFF, 0x80, 0x00, 0x3F, 0xFF, 0x80, 0x00, 0x03, 0xFE, 0x00, 0x00, + 0x00, 0x01, 0xFC, 0x00, 0xFF, 0x07, 0xFF, 0x00, 0xFF, 0x1F, 0xFF, 0xC0, + 0xFF, 0x3F, 0xFF, 0xE0, 0xFF, 0x7F, 0xFF, 0xF0, 0xFF, 0x7F, 0xFF, 0xF8, + 0xFF, 0xFF, 0xFF, 0xFC, 0xFF, 0xFC, 0x1F, 0xFC, 0xFF, 0xF0, 0x0F, 0xFE, + 0xFF, 0xE0, 0x07, 0xFE, 0xFF, 0xC0, 0x03, 0xFE, 0xFF, 0xC0, 0x03, 0xFF, + 0xFF, 0x80, 0x01, 0xFF, 0xFF, 0x80, 0x01, 0xFF, 0xFF, 0x80, 0x01, 0xFF, + 0xFF, 0x80, 0x01, 0xFF, 0xFF, 0x80, 0x01, 0xFF, 0xFF, 0x80, 0x01, 0xFF, + 0xFF, 0x80, 0x01, 0xFF, 0xFF, 0x80, 0x01, 0xFF, 0xFF, 0x80, 0x01, 0xFF, + 0xFF, 0x80, 0x01, 0xFF, 0xFF, 0xC0, 0x01, 0xFF, 0xFF, 0xC0, 0x03, 0xFE, + 0xFF, 0xC0, 0x03, 0xFE, 0xFF, 0xE0, 0x07, 0xFE, 0xFF, 0xF0, 0x0F, 0xFE, + 0xFF, 0xFC, 0x1F, 0xFC, 0xFF, 0xFF, 0xFF, 0xFC, 0xFF, 0xFF, 0xFF, 0xF8, + 0xFF, 0xFF, 0xFF, 0xF0, 0xFF, 0xBF, 0xFF, 0xE0, 0xFF, 0x9F, 0xFF, 0xC0, + 0xFF, 0x87, 0xFF, 0x00, 0xFF, 0x81, 0xFC, 0x00, 0xFF, 0x80, 0x00, 0x00, + 0xFF, 0x80, 0x00, 0x00, 0xFF, 0x80, 0x00, 0x00, 0xFF, 0x80, 0x00, 0x00, + 0xFF, 0x80, 0x00, 0x00, 0xFF, 0x80, 0x00, 0x00, 0xFF, 0x80, 0x00, 0x00, + 0xFF, 0x80, 0x00, 0x00, 0xFF, 0x80, 0x00, 0x00, 0xFF, 0x80, 0x00, 0x00, + 0xFF, 0x80, 0x00, 0x00, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x3F, 0x80, 0x00, + 0x00, 0xFF, 0xE0, 0xFF, 0x03, 0xFF, 0xF8, 0xFF, 0x07, 0xFF, 0xFC, 0xFF, + 0x0F, 0xFF, 0xFE, 0xFF, 0x1F, 0xFF, 0xFE, 0xFF, 0x3F, 0xFF, 0xFF, 0xFF, + 0x3F, 0xFC, 0x3F, 0xFF, 0x7F, 0xF0, 0x0F, 0xFF, 0x7F, 0xE0, 0x07, 0xFF, + 0x7F, 0xC0, 0x03, 0xFF, 0xFF, 0xC0, 0x03, 0xFF, 0xFF, 0x80, 0x01, 0xFF, + 0xFF, 0x80, 0x01, 0xFF, 0xFF, 0x80, 0x01, 0xFF, 0xFF, 0x80, 0x01, 0xFF, + 0xFF, 0x80, 0x01, 0xFF, 0xFF, 0x80, 0x01, 0xFF, 0xFF, 0x80, 0x01, 0xFF, + 0xFF, 0x80, 0x01, 0xFF, 0xFF, 0x80, 0x01, 0xFF, 0xFF, 0x80, 0x01, 0xFF, + 0xFF, 0x80, 0x03, 0xFF, 0xFF, 0x80, 0x03, 0xFF, 0x7F, 0xC0, 0x07, 0xFF, + 0x7F, 0xE0, 0x07, 0xFF, 0x7F, 0xF0, 0x0F, 0xFF, 0x3F, 0xF8, 0x3F, 0xFF, + 0x3F, 0xFF, 0xFF, 0xFF, 0x1F, 0xFF, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0xFF, + 0x07, 0xFF, 0xFD, 0xFF, 0x03, 0xFF, 0xF9, 0xFF, 0x01, 0xFF, 0xE1, 0xFF, + 0x00, 0x3F, 0x81, 0xFF, 0x00, 0x00, 0x01, 0xFF, 0x00, 0x00, 0x01, 0xFF, + 0x00, 0x00, 0x01, 0xFF, 0x00, 0x00, 0x01, 0xFF, 0x00, 0x00, 0x01, 0xFF, + 0x00, 0x00, 0x01, 0xFF, 0x00, 0x00, 0x01, 0xFF, 0x00, 0x00, 0x01, 0xFF, + 0x00, 0x00, 0x01, 0xFF, 0x00, 0x00, 0x01, 0xFF, 0x00, 0x00, 0x01, 0xFF, + 0x00, 0x00, 0x01, 0xFF, 0x00, 0x07, 0xE7, 0xF8, 0xFF, 0xFF, 0xCF, 0xFF, + 0xFE, 0x7F, 0xFF, 0xF7, 0xFF, 0xFF, 0xFF, 0xFB, 0xFF, 0xFF, 0xDF, 0xFF, + 0xFE, 0xFF, 0xFF, 0xE7, 0xFF, 0x83, 0x3F, 0xF8, 0x01, 0xFF, 0x80, 0x0F, + 0xFC, 0x00, 0x7F, 0xE0, 0x03, 0xFE, 0x00, 0x1F, 0xF0, 0x00, 0xFF, 0x80, + 0x07, 0xFC, 0x00, 0x3F, 0xE0, 0x01, 0xFF, 0x00, 0x0F, 0xF8, 0x00, 0x7F, + 0xC0, 0x03, 0xFE, 0x00, 0x1F, 0xF0, 0x00, 0xFF, 0x80, 0x07, 0xFC, 0x00, + 0x3F, 0xE0, 0x01, 0xFF, 0x00, 0x0F, 0xF8, 0x00, 0x7F, 0xC0, 0x03, 0xFE, + 0x00, 0x1F, 0xF0, 0x00, 0xFF, 0x80, 0x07, 0xFC, 0x00, 0x00, 0x00, 0x3F, + 0xF0, 0x00, 0x03, 0xFF, 0xFE, 0x00, 0x0F, 0xFF, 0xFE, 0x00, 0x7F, 0xFF, + 0xFE, 0x00, 0xFF, 0xFF, 0xFE, 0x03, 0xFF, 0xFF, 0xFE, 0x0F, 0xFF, 0xFF, + 0xFE, 0x1F, 0xF8, 0x0F, 0xFC, 0x3F, 0xC0, 0x0F, 0xF8, 0x7F, 0x80, 0x0F, + 0xF8, 0xFF, 0x00, 0x0F, 0x01, 0xFF, 0x00, 0x00, 0x03, 0xFF, 0xC0, 0x00, + 0x07, 0xFF, 0xFC, 0x00, 0x07, 0xFF, 0xFF, 0x00, 0x0F, 0xFF, 0xFF, 0xC0, + 0x0F, 0xFF, 0xFF, 0xE0, 0x0F, 0xFF, 0xFF, 0xE0, 0x07, 0xFF, 0xFF, 0xE0, + 0x03, 0xFF, 0xFF, 0xE0, 0x00, 0x7F, 0xFF, 0xC0, 0x00, 0x0F, 0xFF, 0xC0, + 0x00, 0x01, 0xFF, 0x83, 0xC0, 0x01, 0xFF, 0x7F, 0xC0, 0x03, 0xFE, 0xFF, + 0x80, 0x07, 0xFD, 0xFF, 0x80, 0x0F, 0xF9, 0xFF, 0xC0, 0x7F, 0xE3, 0xFF, + 0xFF, 0xFF, 0xC3, 0xFF, 0xFF, 0xFF, 0x03, 0xFF, 0xFF, 0xFE, 0x03, 0xFF, + 0xFF, 0xF8, 0x03, 0xFF, 0xFF, 0xC0, 0x01, 0xFF, 0xFE, 0x00, 0x00, 0x7F, + 0xE0, 0x00, 0x00, 0x08, 0x00, 0x07, 0x00, 0x03, 0xE0, 0x00, 0xFC, 0x00, + 0x7F, 0x80, 0x1F, 0xF0, 0x03, 0xFE, 0x00, 0x7F, 0xC0, 0x0F, 0xF8, 0x01, + 0xFF, 0x00, 0x3F, 0xE0, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x3F, 0xE0, 0x07, + 0xFC, 0x00, 0xFF, 0x80, 0x1F, 0xF0, 0x03, 0xFE, 0x00, 0x7F, 0xC0, 0x0F, + 0xF8, 0x01, 0xFF, 0x00, 0x3F, 0xE0, 0x07, 0xFC, 0x00, 0xFF, 0x80, 0x1F, + 0xF0, 0x03, 0xFE, 0x00, 0x7F, 0xC0, 0x0F, 0xF8, 0x01, 0xFF, 0x00, 0x3F, + 0xE0, 0x07, 0xFC, 0x00, 0xFF, 0x80, 0x1F, 0xF0, 0x83, 0xFF, 0xF8, 0x7F, + 0xFF, 0x07, 0xFF, 0xE0, 0xFF, 0xFC, 0x0F, 0xFF, 0x80, 0xFF, 0xF0, 0x07, + 0xF8, 0xFF, 0x80, 0x07, 0xFF, 0xFE, 0x00, 0x1F, 0xFF, 0xF8, 0x00, 0x7F, + 0xFF, 0xE0, 0x01, 0xFF, 0xFF, 0x80, 0x07, 0xFF, 0xFE, 0x00, 0x1F, 0xFF, + 0xF8, 0x00, 0x7F, 0xFF, 0xE0, 0x01, 0xFF, 0xFF, 0x80, 0x07, 0xFF, 0xFE, + 0x00, 0x1F, 0xFF, 0xF8, 0x00, 0x7F, 0xFF, 0xE0, 0x01, 0xFF, 0xFF, 0x80, + 0x07, 0xFF, 0xFE, 0x00, 0x1F, 0xFF, 0xF8, 0x00, 0x7F, 0xFF, 0xE0, 0x01, + 0xFF, 0xFF, 0x80, 0x07, 0xFF, 0xFE, 0x00, 0x1F, 0xFF, 0xF8, 0x00, 0x7F, + 0xFF, 0xE0, 0x01, 0xFF, 0xFF, 0x80, 0x07, 0xFF, 0xFE, 0x00, 0x1F, 0xFF, + 0xF8, 0x00, 0x7F, 0xFF, 0xE0, 0x03, 0xFF, 0x7F, 0xC0, 0x0F, 0xFD, 0xFF, + 0x80, 0x7F, 0xF7, 0xFF, 0x07, 0xFF, 0xDF, 0xFF, 0xFF, 0xFF, 0x3F, 0xFF, + 0xFF, 0xFC, 0xFF, 0xFF, 0xEF, 0xF1, 0xFF, 0xFF, 0x3F, 0xC3, 0xFF, 0xF8, + 0xFF, 0x07, 0xFF, 0x83, 0xFC, 0x03, 0xF8, 0x00, 0x00, 0x7F, 0xC0, 0x00, + 0x7F, 0xDF, 0xF0, 0x00, 0x1F, 0xF7, 0xFC, 0x00, 0x0F, 0xF8, 0xFF, 0x80, + 0x03, 0xFE, 0x3F, 0xE0, 0x00, 0xFF, 0x87, 0xF8, 0x00, 0x7F, 0xC1, 0xFF, + 0x00, 0x1F, 0xF0, 0x7F, 0xC0, 0x07, 0xF8, 0x0F, 0xF8, 0x03, 0xFE, 0x03, + 0xFE, 0x00, 0xFF, 0x80, 0x7F, 0x80, 0x7F, 0xC0, 0x1F, 0xF0, 0x1F, 0xF0, + 0x07, 0xFC, 0x07, 0xF8, 0x00, 0xFF, 0x03, 0xFE, 0x00, 0x3F, 0xE0, 0xFF, + 0x80, 0x07, 0xF8, 0x3F, 0xC0, 0x01, 0xFE, 0x1F, 0xF0, 0x00, 0x7F, 0xC7, + 0xF8, 0x00, 0x0F, 0xF1, 0xFE, 0x00, 0x03, 0xFC, 0xFF, 0x80, 0x00, 0x7F, + 0xBF, 0xC0, 0x00, 0x1F, 0xEF, 0xF0, 0x00, 0x07, 0xFF, 0xF8, 0x00, 0x00, + 0xFF, 0xFE, 0x00, 0x00, 0x3F, 0xFF, 0x80, 0x00, 0x07, 0xFF, 0xC0, 0x00, + 0x01, 0xFF, 0xF0, 0x00, 0x00, 0x7F, 0xF8, 0x00, 0x00, 0x0F, 0xFE, 0x00, + 0x00, 0x03, 0xFF, 0x80, 0x00, 0x00, 0xFF, 0xC0, 0x00, 0x00, 0x1F, 0xF0, + 0x00, 0x00, 0x07, 0xF8, 0x00, 0x00, 0xFF, 0x80, 0x0F, 0xF8, 0x00, 0xFF, + 0xBF, 0xC0, 0x07, 0xFC, 0x00, 0x7F, 0xDF, 0xE0, 0x03, 0xFE, 0x00, 0x3F, + 0xCF, 0xF8, 0x03, 0xFF, 0x80, 0x3F, 0xE3, 0xFC, 0x01, 0xFF, 0xC0, 0x1F, + 0xF1, 0xFE, 0x00, 0xFF, 0xE0, 0x0F, 0xF0, 0xFF, 0x80, 0x7F, 0xF0, 0x07, + 0xF8, 0x3F, 0xC0, 0x7F, 0xFC, 0x07, 0xFC, 0x1F, 0xE0, 0x3F, 0xFE, 0x03, + 0xFC, 0x0F, 0xF0, 0x1F, 0xFF, 0x01, 0xFE, 0x03, 0xFC, 0x0F, 0xFF, 0x81, + 0xFF, 0x01, 0xFE, 0x0F, 0xFF, 0xE0, 0xFF, 0x00, 0xFF, 0x07, 0xF7, 0xF0, + 0x7F, 0x80, 0x3F, 0x83, 0xFB, 0xF8, 0x3F, 0xC0, 0x1F, 0xE1, 0xFD, 0xFC, + 0x3F, 0xC0, 0x0F, 0xF1, 0xFE, 0x7F, 0x1F, 0xE0, 0x07, 0xF8, 0xFE, 0x3F, + 0x8F, 0xF0, 0x01, 0xFE, 0x7F, 0x1F, 0xCF, 0xF0, 0x00, 0xFF, 0x3F, 0x8F, + 0xE7, 0xF8, 0x00, 0x7F, 0xBF, 0xC3, 0xFB, 0xFC, 0x00, 0x1F, 0xDF, 0xC1, + 0xFD, 0xFC, 0x00, 0x0F, 0xFF, 0xE0, 0xFF, 0xFE, 0x00, 0x07, 0xFF, 0xF0, + 0x7F, 0xFF, 0x00, 0x01, 0xFF, 0xF8, 0x1F, 0xFF, 0x80, 0x00, 0xFF, 0xF8, + 0x0F, 0xFF, 0x80, 0x00, 0x7F, 0xFC, 0x07, 0xFF, 0xC0, 0x00, 0x1F, 0xFE, + 0x03, 0xFF, 0xE0, 0x00, 0x0F, 0xFE, 0x00, 0xFF, 0xE0, 0x00, 0x07, 0xFF, + 0x00, 0x7F, 0xF0, 0x00, 0x01, 0xFF, 0x80, 0x3F, 0xF8, 0x00, 0x00, 0xFF, + 0xC0, 0x0F, 0xF8, 0x00, 0x00, 0x7F, 0xC0, 0x07, 0xFC, 0x00, 0x00, 0x3F, + 0xE0, 0x03, 0xFE, 0x00, 0x00, 0x7F, 0xF0, 0x01, 0xFF, 0x8F, 0xFC, 0x00, + 0xFF, 0xC1, 0xFF, 0x80, 0x3F, 0xF0, 0x7F, 0xF0, 0x1F, 0xF8, 0x0F, 0xFC, + 0x0F, 0xFC, 0x01, 0xFF, 0x83, 0xFF, 0x00, 0x7F, 0xF1, 0xFF, 0x80, 0x0F, + 0xFC, 0xFF, 0xC0, 0x01, 0xFF, 0xBF, 0xE0, 0x00, 0x3F, 0xFF, 0xF8, 0x00, + 0x0F, 0xFF, 0xFC, 0x00, 0x01, 0xFF, 0xFE, 0x00, 0x00, 0x3F, 0xFF, 0x80, + 0x00, 0x0F, 0xFF, 0xC0, 0x00, 0x01, 0xFF, 0xE0, 0x00, 0x00, 0x3F, 0xF0, + 0x00, 0x00, 0x0F, 0xFE, 0x00, 0x00, 0x07, 0xFF, 0xC0, 0x00, 0x03, 0xFF, + 0xF0, 0x00, 0x00, 0xFF, 0xFE, 0x00, 0x00, 0x7F, 0xFF, 0xC0, 0x00, 0x3F, + 0xFF, 0xF0, 0x00, 0x0F, 0xFF, 0xFE, 0x00, 0x07, 0xFE, 0xFF, 0xC0, 0x03, + 0xFF, 0x3F, 0xF8, 0x00, 0xFF, 0x87, 0xFE, 0x00, 0x7F, 0xE0, 0xFF, 0xC0, + 0x3F, 0xF0, 0x3F, 0xF8, 0x1F, 0xF8, 0x07, 0xFE, 0x07, 0xFE, 0x00, 0xFF, + 0xC3, 0xFF, 0x00, 0x3F, 0xF9, 0xFF, 0x80, 0x07, 0xFE, 0x7F, 0xE0, 0x00, + 0xFF, 0xC0, 0x7F, 0xC0, 0x00, 0x7F, 0xDF, 0xF0, 0x00, 0x1F, 0xE7, 0xFC, + 0x00, 0x0F, 0xF8, 0xFF, 0x80, 0x03, 0xFE, 0x3F, 0xE0, 0x00, 0xFF, 0x07, + 0xF8, 0x00, 0x7F, 0xC1, 0xFF, 0x00, 0x1F, 0xF0, 0x7F, 0xC0, 0x07, 0xF8, + 0x0F, 0xF0, 0x03, 0xFE, 0x03, 0xFE, 0x00, 0xFF, 0x80, 0xFF, 0x80, 0x3F, + 0xC0, 0x1F, 0xE0, 0x1F, 0xF0, 0x07, 0xFC, 0x07, 0xF8, 0x00, 0xFF, 0x01, + 0xFE, 0x00, 0x3F, 0xC0, 0xFF, 0x80, 0x0F, 0xF8, 0x3F, 0xC0, 0x01, 0xFE, + 0x0F, 0xF0, 0x00, 0x7F, 0x87, 0xFC, 0x00, 0x1F, 0xF1, 0xFE, 0x00, 0x03, + 0xFC, 0x7F, 0x80, 0x00, 0xFF, 0x3F, 0xE0, 0x00, 0x1F, 0xEF, 0xF0, 0x00, + 0x07, 0xFB, 0xFC, 0x00, 0x01, 0xFF, 0xFF, 0x00, 0x00, 0x3F, 0xFF, 0x80, + 0x00, 0x0F, 0xFF, 0xE0, 0x00, 0x03, 0xFF, 0xF0, 0x00, 0x00, 0x7F, 0xFC, + 0x00, 0x00, 0x1F, 0xFF, 0x00, 0x00, 0x03, 0xFF, 0x80, 0x00, 0x00, 0xFF, + 0xE0, 0x00, 0x00, 0x3F, 0xF8, 0x00, 0x00, 0x07, 0xFC, 0x00, 0x00, 0x01, + 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xC0, 0x00, 0x00, 0x3F, 0xE0, 0x00, 0x00, + 0x0F, 0xF8, 0x00, 0x00, 0x07, 0xFE, 0x00, 0x00, 0x03, 0xFF, 0x00, 0x00, + 0x01, 0xFF, 0xC0, 0x00, 0x1F, 0xFF, 0xE0, 0x00, 0x07, 0xFF, 0xF8, 0x00, + 0x01, 0xFF, 0xFC, 0x00, 0x00, 0x7F, 0xFE, 0x00, 0x00, 0x1F, 0xFF, 0x00, + 0x00, 0x07, 0xFF, 0x80, 0x00, 0x01, 0xFF, 0x00, 0x00, 0x00, 0x7F, 0xFF, + 0xFF, 0xF3, 0xFF, 0xFF, 0xFF, 0x9F, 0xFF, 0xFF, 0xFC, 0xFF, 0xFF, 0xFF, + 0xE7, 0xFF, 0xFF, 0xFF, 0x3F, 0xFF, 0xFF, 0xF9, 0xFF, 0xFF, 0xFF, 0xC0, + 0x00, 0x1F, 0xFC, 0x00, 0x01, 0xFF, 0xC0, 0x00, 0x0F, 0xFC, 0x00, 0x00, + 0xFF, 0xE0, 0x00, 0x0F, 0xFE, 0x00, 0x00, 0xFF, 0xE0, 0x00, 0x0F, 0xFE, + 0x00, 0x00, 0xFF, 0xE0, 0x00, 0x07, 0xFE, 0x00, 0x00, 0x7F, 0xF0, 0x00, + 0x07, 0xFF, 0x00, 0x00, 0x7F, 0xF0, 0x00, 0x07, 0xFF, 0x00, 0x00, 0x7F, + 0xF0, 0x00, 0x03, 0xFF, 0x00, 0x00, 0x3F, 0xF0, 0x00, 0x03, 0xFF, 0x80, + 0x00, 0x3F, 0xF8, 0x00, 0x03, 0xFF, 0x80, 0x00, 0x3F, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x00, 0x03, + 0xF8, 0x00, 0xFF, 0xC0, 0x1F, 0xFE, 0x00, 0xFF, 0xF0, 0x0F, 0xFF, 0x80, + 0xFF, 0xFC, 0x07, 0xFF, 0xE0, 0x3F, 0xFF, 0x01, 0xFF, 0x00, 0x0F, 0xF0, + 0x00, 0x7F, 0x80, 0x03, 0xFC, 0x00, 0x1F, 0xE0, 0x00, 0xFF, 0x00, 0x07, + 0xF8, 0x00, 0x3F, 0xC0, 0x01, 0xFC, 0x00, 0x0F, 0xE0, 0x00, 0xFF, 0x00, + 0x07, 0xF8, 0x00, 0x3F, 0xC0, 0x01, 0xFE, 0x00, 0x0F, 0xF0, 0x00, 0xFF, + 0x80, 0x0F, 0xF8, 0x00, 0xFF, 0xC0, 0x3F, 0xFE, 0x01, 0xFF, 0xE0, 0x0F, + 0xFE, 0x00, 0x7F, 0xE0, 0x03, 0xFF, 0x00, 0x1F, 0xFC, 0x00, 0xFF, 0xF0, + 0x07, 0xFF, 0xC0, 0x07, 0xFE, 0x00, 0x1F, 0xF0, 0x00, 0x7F, 0xC0, 0x01, + 0xFE, 0x00, 0x0F, 0xF0, 0x00, 0x7F, 0x80, 0x03, 0xFC, 0x00, 0x1F, 0xE0, + 0x00, 0xFF, 0x00, 0x03, 0xF8, 0x00, 0x1F, 0xC0, 0x00, 0xFF, 0x00, 0x07, + 0xF8, 0x00, 0x3F, 0xC0, 0x01, 0xFE, 0x00, 0x0F, 0xF0, 0x00, 0x7F, 0x80, + 0x03, 0xFE, 0x00, 0x1F, 0xFF, 0x80, 0xFF, 0xFC, 0x07, 0xFF, 0xE0, 0x1F, + 0xFF, 0x00, 0x7F, 0xF8, 0x01, 0xFF, 0xC0, 0x07, 0xFE, 0x00, 0x07, 0xF0, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x07, 0xFE, 0x00, 0x3F, 0xFC, 0x01, + 0xFF, 0xF0, 0x0F, 0xFF, 0x80, 0x7F, 0xFE, 0x03, 0xFF, 0xF0, 0x1F, 0xFF, + 0x80, 0x07, 0xFC, 0x00, 0x1F, 0xF0, 0x00, 0xFF, 0x80, 0x03, 0xFC, 0x00, + 0x1F, 0xE0, 0x00, 0xFF, 0x00, 0x07, 0xF8, 0x00, 0x3F, 0xC0, 0x01, 0xFE, + 0x00, 0x0F, 0xF0, 0x00, 0x7F, 0x80, 0x03, 0xFC, 0x00, 0x1F, 0xE0, 0x00, + 0xFF, 0x00, 0x07, 0xFC, 0x00, 0x3F, 0xE0, 0x00, 0xFF, 0x80, 0x07, 0xFF, + 0x00, 0x1F, 0xFE, 0x00, 0xFF, 0xF0, 0x03, 0xFF, 0x80, 0x07, 0xFC, 0x00, + 0x3F, 0xE0, 0x07, 0xFF, 0x00, 0x7F, 0xF8, 0x03, 0xFF, 0xC0, 0x3F, 0xF8, + 0x01, 0xFF, 0x00, 0x1F, 0xF0, 0x00, 0xFF, 0x80, 0x07, 0xF8, 0x00, 0x3F, + 0xC0, 0x01, 0xFE, 0x00, 0x0F, 0xF0, 0x00, 0x7F, 0x80, 0x03, 0xFC, 0x00, + 0x1F, 0xE0, 0x00, 0xFF, 0x00, 0x07, 0xF8, 0x00, 0x3F, 0xC0, 0x01, 0xFE, + 0x00, 0x1F, 0xF0, 0x00, 0xFF, 0x80, 0x0F, 0xF8, 0x0F, 0xFF, 0xC0, 0x7F, + 0xFE, 0x03, 0xFF, 0xF0, 0x1F, 0xFF, 0x00, 0xFF, 0xF8, 0x07, 0xFF, 0x80, + 0x3F, 0xF0, 0x01, 0xFE, 0x00, 0x00, 0x03, 0xF8, 0x00, 0x00, 0x8F, 0xFF, + 0x80, 0x00, 0xCF, 0xFF, 0xF8, 0x01, 0xEF, 0xFF, 0xFF, 0x83, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xF8, 0x0F, 0xFF, 0xFF, 0x70, 0x00, 0xFF, 0xFF, 0x30, + 0x00, 0x1F, 0xFE, 0x10, 0x00, 0x01, 0xFC, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, + 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x18, + 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0xC0, + 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, + 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x30, 0x00, + 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x01, 0x80, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, + 0x00, 0x18, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, + 0x00, 0xC0, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x01, + 0x80, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0C, + 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x7F, + 0xFF, 0xFF, 0xFF, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x06, + 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x30, + 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x01, 0x80, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0C, 0x00, + 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x60, 0x00, + 0x00, 0x00, 0xC0, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x00, 0x06, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, + 0x00, 0x30, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, + 0x01, 0x80, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x0C, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, + 0x60, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x18, + 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0x80, + 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0C, + 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x60, + 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x18, 0x00, + 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0xC0, 0x00, + 0x00, 0x01, 0x80, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, + 0x00, 0x0C, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, + 0x00, 0x60, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, + 0xC0, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, + 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x30, + 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, + 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x18, + 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0xC0, + 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, + 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x30, 0x00, + 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x01, 0x80, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, + 0x00, 0x18, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, + 0x00, 0xC0, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x01, + 0x80, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0C, + 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x7F, + 0xFF, 0xFF, 0xFF, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x06, + 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x30, + 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x01, 0x80, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0C, 0x00, + 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x60, 0x00, + 0x00, 0x00, 0xC0, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x00, 0x06, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, + 0x00, 0x30, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, + 0x01, 0x80, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x0C, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, + 0x60, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x18, + 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0x80, + 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0C, + 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x60, + 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x18, 0x00, + 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0xC0, 0x00, + 0x00, 0x01, 0x80, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, + 0x00, 0x0C, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, + 0x00, 0x60, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, + 0xC0, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, + 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x30, + 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, + 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x18, + 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0xC0, + 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, + 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x30, 0x00, + 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x01, 0x80, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, + 0x00, 0x18, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, + 0x00, 0xC0, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x01, + 0x80, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0C, + 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x7F, + 0xFF, 0xFF, 0xFF, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x06, + 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x30, + 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x01, 0x80, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0C, 0x00, + 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x60, 0x00, + 0x00, 0x00, 0xC0, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x00, 0x06, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, + 0x00, 0x30, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, + 0x01, 0x80, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x0C, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, + 0x60, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x18, + 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0x80, + 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0C, + 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x60, + 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x18, 0x00, + 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0xC0, 0x00, + 0x00, 0x01, 0x80, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, + 0x00, 0x0C, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, + 0x00, 0x60, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, + 0xC0, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, + 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x30, + 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, + 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x18, + 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0xC0, + 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, + 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x30, 0x00, + 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x01, 0x80, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, + 0x00, 0x18, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, + 0x00, 0xC0, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x01, + 0x80, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0C, + 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x7F, + 0xFF, 0xFF, 0xFF, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x06, + 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x30, + 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x01, 0x80, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0C, 0x00, + 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x60, 0x00, + 0x00, 0x00, 0xC0, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x00, 0x06, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, + 0x00, 0x30, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, + 0x01, 0x80, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x0C, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, + 0x60, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x18, + 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0x80, + 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0C, + 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x60, + 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x18, 0x00, + 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0xC0, 0x00, + 0x00, 0x01, 0x80, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, + 0x00, 0x0C, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, + 0x00, 0x60, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, + 0xC0, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, + 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x30, + 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, + 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x18, + 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0xC0, + 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, + 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x30, 0x00, + 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x01, 0x80, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, + 0x00, 0x18, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, + 0x00, 0xC0, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x01, + 0x80, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0C, + 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x7F, + 0xFF, 0xFF, 0xFF, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x06, + 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x30, + 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x01, 0x80, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0C, 0x00, + 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x60, 0x00, + 0x00, 0x00, 0xC0, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x00, 0x06, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, + 0x00, 0x30, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, + 0x01, 0x80, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x0C, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, + 0x60, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x18, + 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0x80, + 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0C, + 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x60, + 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x18, 0x00, + 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0xC0, 0x00, + 0x00, 0x01, 0x80, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, + 0x00, 0x0C, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, + 0x00, 0x60, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, + 0xC0, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, + 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x30, + 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, + 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x18, + 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0xC0, + 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, + 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x30, 0x00, + 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x01, 0x80, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, + 0x00, 0x18, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, + 0x00, 0xC0, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x01, + 0x80, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0C, + 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x7F, + 0xFF, 0xFF, 0xFF, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x06, + 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x30, + 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x01, 0x80, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0C, 0x00, + 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x60, 0x00, + 0x00, 0x00, 0xC0, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x00, 0x06, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, + 0x00, 0x30, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, + 0x01, 0x80, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x0C, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, + 0x60, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x18, + 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0x80, + 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0C, + 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x60, + 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x18, 0x00, + 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0xC0, 0x00, + 0x00, 0x01, 0x80, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, + 0x00, 0x0C, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, + 0x00, 0x60, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, + 0xC0, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, + 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x30, + 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, + 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x18, + 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0xC0, + 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, + 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x30, 0x00, + 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x01, 0x80, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, + 0x00, 0x18, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, + 0x00, 0xC0, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x01, + 0x80, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0C, + 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x7F, + 0xFF, 0xFF, 0xFF, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x06, + 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x30, + 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x01, 0x80, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0C, 0x00, + 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x60, 0x00, + 0x00, 0x00, 0xC0, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x00, 0x06, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, + 0x00, 0x30, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, + 0x01, 0x80, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x0C, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, + 0x60, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x18, + 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0x80, + 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0C, + 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x60, + 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x18, 0x00, + 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0xC0, 0x00, + 0x00, 0x01, 0x80, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, + 0x00, 0x0C, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, + 0x00, 0x60, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, + 0xC0, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, + 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x30, + 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, + 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x18, + 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0xC0, + 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, + 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x30, 0x00, + 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x01, 0x80, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, + 0x00, 0x18, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, + 0x00, 0xC0, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x01, + 0x80, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0C, + 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x7F, + 0xFF, 0xFF, 0xFF, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x06, + 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x30, + 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x01, 0x80, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0C, 0x00, + 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x60, 0x00, + 0x00, 0x00, 0xC0, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x00, 0x06, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, + 0x00, 0x30, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, + 0x01, 0x80, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x0C, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, + 0x60, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x18, + 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0x80, + 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0C, + 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x60, + 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x18, 0x00, + 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0xC0, 0x00, + 0x00, 0x01, 0x80, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, + 0x00, 0x0C, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, + 0x00, 0x60, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, + 0xC0, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, + 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x30, + 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, + 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x18, + 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0xC0, + 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, + 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x30, 0x00, + 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x01, 0x80, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, + 0x00, 0x18, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, + 0x00, 0xC0, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x01, + 0x80, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0C, + 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x7F, + 0xFF, 0xFF, 0xFF, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x06, + 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x30, + 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x01, 0x80, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0C, 0x00, + 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x60, 0x00, + 0x00, 0x00, 0xC0, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x00, 0x06, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, + 0x00, 0x30, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, + 0x01, 0x80, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x0C, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, + 0x60, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x18, + 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0x80, + 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0C, + 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x60, + 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x18, 0x00, + 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0xC0, 0x00, + 0x00, 0x01, 0x80, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, + 0x00, 0x0C, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, + 0x00, 0x60, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, + 0xC0, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, + 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x30, + 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, + 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x18, + 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0xC0, + 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, + 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x30, 0x00, + 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x01, 0x80, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, + 0x00, 0x18, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, + 0x00, 0xC0, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x01, + 0x80, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0C, + 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x7F, + 0xFF, 0xFF, 0xFF, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x06, + 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x30, + 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x01, 0x80, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0C, 0x00, + 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x60, 0x00, + 0x00, 0x00, 0xC0, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x00, 0x06, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, + 0x00, 0x30, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, + 0x01, 0x80, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x0C, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, + 0x60, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x18, + 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0x80, + 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0C, + 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x60, + 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x18, 0x00, + 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0xC0, 0x00, + 0x00, 0x01, 0x80, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, + 0x00, 0x0C, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, + 0x00, 0x60, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, + 0xC0, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, + 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x30, + 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, + 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x18, + 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0xC0, + 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, + 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x30, 0x00, + 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x01, 0x80, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, + 0x00, 0x18, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, + 0x00, 0xC0, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x01, + 0x80, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0C, + 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x7F, + 0xFF, 0xFF, 0xFF, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x06, + 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x30, + 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x01, 0x80, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0C, 0x00, + 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x60, 0x00, + 0x00, 0x00, 0xC0, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x00, 0x06, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, + 0x00, 0x30, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, + 0x01, 0x80, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x0C, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, + 0x60, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x18, + 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0x80, + 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0C, + 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x60, + 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x18, 0x00, + 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0xC0, 0x00, + 0x00, 0x01, 0x80, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, + 0x00, 0x0C, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, + 0x00, 0x60, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, + 0xC0, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, + 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x30, + 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x03, 0xE1, 0xF0, + 0xF8, 0x7C, 0x3F, 0x1F, 0x8F, 0xC7, 0xE7, 0xF3, 0xF9, 0xFC, 0xFE, 0x7F, + 0x3F, 0x9F, 0xCF, 0xF7, 0xFB, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x78, 0x00, + 0x00, 0x01, 0xE0, 0x00, 0x00, 0x0F, 0x80, 0x00, 0x00, 0x3C, 0x00, 0x00, + 0x00, 0xF0, 0x00, 0x00, 0x07, 0xC0, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, + 0x78, 0x00, 0x00, 0x03, 0xE0, 0x00, 0x07, 0xFF, 0x00, 0x00, 0xFF, 0xFC, + 0x00, 0x0F, 0xFF, 0xFC, 0x00, 0x7F, 0xFF, 0xFC, 0x03, 0xFF, 0xFF, 0xF8, + 0x1F, 0xFF, 0xFF, 0xE0, 0xFF, 0xFF, 0xFF, 0xC3, 0xFF, 0x8F, 0xFF, 0x9F, + 0xF8, 0x3F, 0xFE, 0x7F, 0xC0, 0xFF, 0xFB, 0xFF, 0x07, 0x9F, 0xFF, 0xF8, + 0x1E, 0x7C, 0x3F, 0xE0, 0x78, 0x00, 0xFF, 0x83, 0xE0, 0x03, 0xFE, 0x0F, + 0x00, 0x0F, 0xF0, 0x3C, 0x00, 0x3F, 0xC1, 0xF0, 0x00, 0xFF, 0x07, 0x80, + 0x03, 0xFC, 0x1E, 0x00, 0x0F, 0xF8, 0x78, 0x00, 0x3F, 0xE3, 0xC0, 0x00, + 0xFF, 0x8F, 0x00, 0x03, 0xFE, 0x3C, 0x0F, 0x0F, 0xF9, 0xF0, 0x3F, 0xDF, + 0xF7, 0x81, 0xFF, 0x7F, 0xFE, 0x07, 0xFD, 0xFF, 0xF8, 0x3F, 0xE3, 0xFF, + 0xC3, 0xFF, 0x8F, 0xFF, 0xFF, 0xFC, 0x1F, 0xFF, 0xFF, 0xF0, 0x3F, 0xFF, + 0xFF, 0x80, 0x7F, 0xFF, 0xFC, 0x00, 0xFF, 0xFF, 0xE0, 0x00, 0xFF, 0xFE, + 0x00, 0x03, 0xFF, 0xC0, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, + 0x01, 0xE0, 0x00, 0x00, 0x07, 0x80, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, + 0xF8, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x3C, + 0x00, 0x00, 0x01, 0xE0, 0x00, 0x00, 0x07, 0x80, 0x00, 0x00, 0x06, 0x00, + 0x00, 0x00, 0x00, 0x03, 0xFE, 0x00, 0x00, 0x07, 0xFF, 0xF0, 0x00, 0x07, + 0xFF, 0xFE, 0x00, 0x03, 0xFF, 0xFF, 0xC0, 0x01, 0xFF, 0xFF, 0xF8, 0x00, + 0xFF, 0xFF, 0xFF, 0x00, 0x3F, 0xFF, 0xFF, 0xE0, 0x1F, 0xFC, 0x3F, 0xF8, + 0x07, 0xFC, 0x03, 0xFF, 0x03, 0xFE, 0x00, 0x7F, 0xC0, 0xFF, 0x80, 0x1F, + 0xF0, 0x3F, 0xC0, 0x03, 0xFC, 0x0F, 0xF0, 0x00, 0xE0, 0x03, 0xFC, 0x00, + 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x3F, 0xE0, 0x00, 0x00, 0x0F, 0xF8, + 0x00, 0x00, 0x03, 0xFE, 0x00, 0x00, 0x00, 0x7F, 0x80, 0x00, 0x00, 0x1F, + 0xF0, 0x00, 0x00, 0xFF, 0xFF, 0xFE, 0x00, 0x3F, 0xFF, 0xFF, 0x80, 0x0F, + 0xFF, 0xFF, 0xE0, 0x03, 0xFF, 0xFF, 0xF8, 0x00, 0xFF, 0xFF, 0xFE, 0x00, + 0x3F, 0xFF, 0xFF, 0x80, 0x0F, 0xFF, 0xFF, 0xE0, 0x00, 0x07, 0xF8, 0x00, + 0x00, 0x01, 0xFE, 0x00, 0x00, 0x00, 0x7F, 0x80, 0x00, 0x00, 0x1F, 0xE0, + 0x00, 0x00, 0x0F, 0xF8, 0x00, 0x00, 0x03, 0xFE, 0x00, 0x00, 0x00, 0xFF, + 0x00, 0x00, 0x00, 0x7F, 0x80, 0x00, 0x00, 0x1F, 0xE0, 0x00, 0x00, 0x0F, + 0xF0, 0x00, 0x00, 0x07, 0xFB, 0xF0, 0x00, 0x83, 0xFF, 0xFF, 0x00, 0x63, + 0xFF, 0xFF, 0xF0, 0x7C, 0x7F, 0xFF, 0xFF, 0xFF, 0x1F, 0xFF, 0xFF, 0xFF, + 0xE3, 0xFF, 0xFF, 0xFF, 0xF8, 0xFF, 0xFF, 0xFF, 0xFE, 0x1F, 0x01, 0xFF, + 0xFF, 0xC7, 0x00, 0x0F, 0xFF, 0xE1, 0x00, 0x00, 0x3F, 0xC0, 0x04, 0x00, + 0x00, 0x10, 0x0E, 0x00, 0x00, 0x38, 0x1F, 0x00, 0x00, 0x78, 0x3F, 0x07, + 0xF0, 0xFC, 0x3F, 0x9F, 0xFC, 0xFE, 0x7F, 0xFF, 0xFF, 0xFF, 0x7F, 0xFF, + 0xFF, 0xFF, 0x3F, 0xFF, 0xFF, 0xFE, 0x1F, 0xFF, 0xFF, 0xFC, 0x0F, 0xFF, + 0xFF, 0xF8, 0x0F, 0xFF, 0xFF, 0xF8, 0x0F, 0xFC, 0x1F, 0xF8, 0x1F, 0xF0, + 0x07, 0xF8, 0x1F, 0xE0, 0x03, 0xFC, 0x1F, 0xE0, 0x03, 0xFC, 0x1F, 0xC0, + 0x01, 0xFC, 0x1F, 0xC0, 0x01, 0xFC, 0x1F, 0xC0, 0x01, 0xFC, 0x1F, 0xE0, + 0x03, 0xFC, 0x1F, 0xE0, 0x03, 0xFC, 0x1F, 0xF0, 0x07, 0xF8, 0x0F, 0xFC, + 0x1F, 0xF8, 0x0F, 0xFF, 0xFF, 0xF8, 0x0F, 0xFF, 0xFF, 0xF8, 0x1F, 0xFF, + 0xFF, 0xFC, 0x3F, 0xFF, 0xFF, 0xFE, 0x7F, 0xFF, 0xFF, 0xFF, 0x7F, 0xFF, + 0xFF, 0xFF, 0x3F, 0x9F, 0xFC, 0xFE, 0x1F, 0x87, 0xF0, 0xFC, 0x0F, 0x00, + 0x00, 0x78, 0x0E, 0x00, 0x00, 0x38, 0x04, 0x00, 0x00, 0x10, 0xFF, 0xC0, + 0x00, 0x7F, 0xCF, 0xF8, 0x00, 0x0F, 0xF9, 0xFF, 0x80, 0x03, 0xFF, 0x1F, + 0xF0, 0x00, 0x7F, 0xC3, 0xFF, 0x00, 0x1F, 0xF8, 0x3F, 0xE0, 0x03, 0xFE, + 0x07, 0xFE, 0x00, 0xFF, 0xC0, 0x7F, 0xC0, 0x1F, 0xF0, 0x0F, 0xF8, 0x07, + 0xFE, 0x00, 0xFF, 0x80, 0xFF, 0x80, 0x1F, 0xF0, 0x3F, 0xF0, 0x01, 0xFF, + 0x07, 0xFC, 0x00, 0x3F, 0xE1, 0xFF, 0x80, 0x07, 0xFE, 0x3F, 0xE0, 0x00, + 0x7F, 0xCF, 0xFC, 0x00, 0x0F, 0xFD, 0xFF, 0x00, 0x00, 0xFF, 0xBF, 0xE0, + 0x00, 0x1F, 0xFF, 0xF8, 0x00, 0x01, 0xFF, 0xFF, 0x00, 0x00, 0x3F, 0xFF, + 0xC0, 0x03, 0xFF, 0xFF, 0xFF, 0xF8, 0x7F, 0xFF, 0xFF, 0xFF, 0x0F, 0xFF, + 0xFF, 0xFF, 0xE1, 0xFF, 0xFF, 0xFF, 0xFC, 0x3F, 0xFF, 0xFF, 0xFF, 0x87, + 0xFF, 0xFF, 0xFF, 0xF0, 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0x03, 0xFE, 0x00, + 0x00, 0x00, 0x7F, 0xC0, 0x00, 0x00, 0x0F, 0xF8, 0x00, 0x0F, 0xFF, 0xFF, + 0xFF, 0xE1, 0xFF, 0xFF, 0xFF, 0xFC, 0x3F, 0xFF, 0xFF, 0xFF, 0x87, 0xFF, + 0xFF, 0xFF, 0xF0, 0xFF, 0xFF, 0xFF, 0xFE, 0x1F, 0xFF, 0xFF, 0xFF, 0xC3, + 0xFF, 0xFF, 0xFF, 0xF8, 0x00, 0x0F, 0xF8, 0x00, 0x00, 0x01, 0xFF, 0x00, + 0x00, 0x00, 0x3F, 0xE0, 0x00, 0x00, 0x07, 0xFC, 0x00, 0x00, 0x00, 0xFF, + 0x80, 0x00, 0x00, 0x1F, 0xF0, 0x00, 0x00, 0x03, 0xFE, 0x00, 0x00, 0x00, + 0x7F, 0xC0, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x00, 0x3F, 0xE0, 0x00, 0x01, + 0xFF, 0xF8, 0x00, 0x0F, 0xFF, 0xFC, 0x00, 0x3F, 0xFF, 0xFC, 0x00, 0xFF, + 0xFF, 0xFC, 0x03, 0xFF, 0xFF, 0xF8, 0x07, 0xFF, 0xFF, 0xF8, 0x0F, 0xFC, + 0x3F, 0xF0, 0x3F, 0xE0, 0x1F, 0xF0, 0x7F, 0xC0, 0x3F, 0xE0, 0xFF, 0x80, + 0x3F, 0xC0, 0xFF, 0x80, 0x78, 0x01, 0xFF, 0x80, 0x00, 0x03, 0xFF, 0x80, + 0x00, 0x03, 0xFF, 0x80, 0x00, 0x07, 0xFF, 0xC0, 0x00, 0x07, 0xFF, 0xE0, + 0x00, 0x1F, 0xFF, 0xE0, 0x00, 0x7F, 0xFF, 0xF0, 0x01, 0xFF, 0xFF, 0xF0, + 0x07, 0xFF, 0xFF, 0xF0, 0x0F, 0xF3, 0xFF, 0xF8, 0x1F, 0xC1, 0xFF, 0xF8, + 0x7F, 0x81, 0xFF, 0xF8, 0xFF, 0x00, 0xFF, 0xF9, 0xFE, 0x00, 0xFF, 0xF3, + 0xFC, 0x00, 0xFF, 0xF7, 0xF8, 0x00, 0x7F, 0xEF, 0xF8, 0x00, 0x7F, 0xFF, + 0xF8, 0x00, 0x7F, 0xDF, 0xF8, 0x00, 0x7F, 0xBF, 0xFC, 0x00, 0xFF, 0x7F, + 0xFC, 0x01, 0xFE, 0x7F, 0xFE, 0x03, 0xFC, 0x7F, 0xFE, 0x07, 0xF0, 0x7F, + 0xFF, 0x1F, 0xE0, 0x7F, 0xFF, 0x7F, 0x80, 0x3F, 0xFF, 0xFF, 0x00, 0x3F, + 0xFF, 0xFC, 0x00, 0x3F, 0xFF, 0xF0, 0x00, 0x1F, 0xFF, 0x80, 0x00, 0x1F, + 0xFF, 0x80, 0x00, 0x0F, 0xFF, 0x80, 0x00, 0x07, 0xFF, 0x80, 0x00, 0x07, + 0xFF, 0x00, 0x00, 0x07, 0xFF, 0x00, 0x00, 0x03, 0xFE, 0x01, 0xE0, 0x07, + 0xFC, 0x3F, 0xC0, 0x0F, 0xF8, 0x7F, 0xC0, 0x1F, 0xF0, 0xFF, 0xC0, 0x3F, + 0xE1, 0xFF, 0xC0, 0xFF, 0xC1, 0xFF, 0xFF, 0xFF, 0x01, 0xFF, 0xFF, 0xFE, + 0x03, 0xFF, 0xFF, 0xF8, 0x03, 0xFF, 0xFF, 0xE0, 0x01, 0xFF, 0xFF, 0x80, + 0x01, 0xFF, 0xFC, 0x00, 0x00, 0x7F, 0xC0, 0x00, 0xFF, 0x07, 0xFF, 0xF8, + 0x3F, 0xFF, 0xC1, 0xFF, 0xFE, 0x0F, 0xFF, 0xF0, 0x7F, 0xFF, 0x83, 0xFF, + 0xFC, 0x1F, 0xFF, 0xE0, 0xFF, 0x00, 0x00, 0x1F, 0xF0, 0x00, 0x00, 0x00, + 0x03, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x1F, 0xFF, 0xFF, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x07, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x1F, + 0xFC, 0x01, 0xFF, 0x80, 0x00, 0x7F, 0xC0, 0x00, 0x7F, 0xC0, 0x01, 0xFE, + 0x00, 0x00, 0x3F, 0xC0, 0x07, 0xF0, 0x00, 0x00, 0x3F, 0x80, 0x1F, 0xC0, + 0x00, 0x00, 0x1F, 0x80, 0x3F, 0x00, 0x00, 0x00, 0x1F, 0x80, 0xFC, 0x00, + 0x7F, 0x00, 0x1F, 0x83, 0xF0, 0x07, 0xFF, 0xC0, 0x3F, 0x07, 0xC0, 0x1F, + 0xFF, 0xC0, 0x3F, 0x0F, 0x80, 0x7F, 0xFF, 0xC0, 0x3E, 0x3E, 0x01, 0xFC, + 0x1F, 0xC0, 0x7C, 0x7C, 0x07, 0xE0, 0x1F, 0x80, 0x7D, 0xF0, 0x0F, 0xC0, + 0x1F, 0x80, 0xFB, 0xE0, 0x3F, 0x00, 0x18, 0x01, 0xF7, 0xC0, 0x7E, 0x00, + 0x00, 0x01, 0xEF, 0x80, 0xF8, 0x00, 0x00, 0x03, 0xFE, 0x01, 0xF0, 0x00, + 0x00, 0x07, 0xFC, 0x03, 0xE0, 0x00, 0x00, 0x0F, 0xF8, 0x07, 0xC0, 0x00, + 0x00, 0x1F, 0xF0, 0x0F, 0x80, 0x00, 0x00, 0x3F, 0xE0, 0x1F, 0x00, 0x00, + 0x00, 0x7F, 0xE0, 0x3E, 0x00, 0x00, 0x00, 0xFF, 0xC0, 0x7E, 0x00, 0x00, + 0x01, 0xFF, 0x80, 0xFC, 0x00, 0x70, 0x07, 0xDF, 0x00, 0xFC, 0x01, 0xF8, + 0x0F, 0x9F, 0x01, 0xF8, 0x07, 0xE0, 0x1F, 0x3E, 0x01, 0xFC, 0x1F, 0xC0, + 0x7C, 0x3E, 0x01, 0xFF, 0xFF, 0x00, 0xF8, 0x7C, 0x01, 0xFF, 0xFC, 0x03, + 0xF0, 0xFC, 0x01, 0xFF, 0xF0, 0x07, 0xC0, 0xFC, 0x00, 0x7F, 0x00, 0x1F, + 0x80, 0xFC, 0x00, 0x00, 0x00, 0x7E, 0x01, 0xFC, 0x00, 0x00, 0x01, 0xF8, + 0x01, 0xFC, 0x00, 0x00, 0x0F, 0xF0, 0x01, 0xFE, 0x00, 0x00, 0x3F, 0xC0, + 0x01, 0xFF, 0x00, 0x01, 0xFF, 0x00, 0x01, 0xFF, 0xC0, 0x1F, 0xF8, 0x00, + 0x01, 0xFF, 0xFF, 0xFF, 0xE0, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x80, 0x00, + 0x00, 0x7F, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xE0, 0x00, 0x00, + 0x00, 0x0F, 0xFC, 0x00, 0x00, 0x00, 0x03, 0xFE, 0x00, 0x7F, 0xFE, 0x07, + 0xFF, 0xF8, 0x7F, 0xFF, 0xC3, 0xFF, 0xFF, 0x3F, 0x83, 0xF9, 0xF8, 0x1F, + 0xC1, 0xC0, 0x7E, 0x00, 0x0F, 0xF0, 0x07, 0xFF, 0x83, 0xFF, 0xFC, 0x7F, + 0xFF, 0xE7, 0xFF, 0xBF, 0x3F, 0xC1, 0xFB, 0xF8, 0x1F, 0xDF, 0xC0, 0xFE, + 0xFE, 0x07, 0xF7, 0xF8, 0xFF, 0xBF, 0xFF, 0xFC, 0xFF, 0xFF, 0xE7, 0xFF, + 0xFF, 0x0F, 0xFD, 0xF8, 0x3F, 0x87, 0xE0, 0x00, 0x7F, 0x07, 0xF0, 0x03, + 0xF8, 0x3F, 0x80, 0x3F, 0x83, 0xF8, 0x03, 0xFC, 0x3F, 0xC0, 0x1F, 0xC1, + 0xFC, 0x01, 0xFC, 0x1F, 0xC0, 0x1F, 0xE1, 0xFE, 0x00, 0xFE, 0x0F, 0xE0, + 0x0F, 0xF0, 0xFF, 0x00, 0xFF, 0x0F, 0xF0, 0x07, 0xF8, 0x7F, 0x80, 0x7F, + 0x87, 0xF8, 0x07, 0xFC, 0x7F, 0xC0, 0x3F, 0xC3, 0xFC, 0x03, 0xFE, 0x3F, + 0xE0, 0x0F, 0xF0, 0xFF, 0x00, 0x7F, 0xC7, 0xFC, 0x01, 0xFE, 0x1F, 0xE0, + 0x07, 0xF8, 0x7F, 0x80, 0x3F, 0xC3, 0xFC, 0x00, 0xFF, 0x0F, 0xF0, 0x03, + 0xF8, 0x3F, 0x80, 0x1F, 0xE1, 0xFE, 0x00, 0x7F, 0x07, 0xF8, 0x01, 0xFC, + 0x1F, 0xC0, 0x0F, 0xF0, 0xFF, 0x00, 0x3F, 0x83, 0xF8, 0x00, 0xFE, 0x0F, + 0xE0, 0x07, 0xF0, 0x7F, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x00, 0x00, 0x01, 0xFE, 0x00, 0x00, 0x03, 0xFC, 0x00, 0x00, 0x07, 0xF8, + 0x00, 0x00, 0x0F, 0xF0, 0x00, 0x00, 0x1F, 0xE0, 0x00, 0x00, 0x3F, 0xC0, + 0x00, 0x00, 0x7F, 0x80, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x01, 0xFE, 0x00, + 0x00, 0x03, 0xFC, 0x00, 0x00, 0x07, 0xF8, 0x00, 0x00, 0x0F, 0xF0, 0x00, + 0x00, 0x1F, 0xE0, 0x00, 0x00, 0x3F, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x00, 0x00, 0x1F, 0xF0, 0x00, 0x00, 0x00, 0x03, 0xFF, 0xFE, 0x00, 0x00, + 0x00, 0x1F, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x80, 0x00, + 0x07, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x1F, 0xFC, 0x01, 0xFF, 0xC0, 0x00, + 0x7F, 0xC0, 0x00, 0x7F, 0xC0, 0x01, 0xFE, 0x00, 0x00, 0x3F, 0xC0, 0x07, + 0xF0, 0x00, 0x00, 0x3F, 0x80, 0x1F, 0xC0, 0x00, 0x00, 0x1F, 0x80, 0x3F, + 0x00, 0x00, 0x00, 0x1F, 0x80, 0xFC, 0x3F, 0xFF, 0x80, 0x1F, 0x83, 0xF0, + 0x7F, 0xFF, 0xC0, 0x1F, 0x07, 0xC0, 0xFF, 0xFF, 0xC0, 0x3F, 0x0F, 0x81, + 0xFF, 0xFF, 0xC0, 0x3E, 0x3E, 0x03, 0xE0, 0x3F, 0x80, 0x7C, 0x7C, 0x07, + 0xC0, 0x1F, 0x80, 0x7D, 0xF0, 0x0F, 0x80, 0x3F, 0x00, 0xFB, 0xE0, 0x1F, + 0x00, 0x3E, 0x00, 0xF7, 0xC0, 0x3E, 0x00, 0xFC, 0x01, 0xEF, 0x80, 0x7C, + 0x01, 0xF8, 0x03, 0xFF, 0x00, 0xF8, 0x07, 0xE0, 0x07, 0xFC, 0x01, 0xFF, + 0xFF, 0x80, 0x0F, 0xF8, 0x03, 0xFF, 0xFE, 0x00, 0x1F, 0xF0, 0x07, 0xFF, + 0xF0, 0x00, 0x3F, 0xF0, 0x0F, 0xFF, 0xC0, 0x00, 0x7F, 0xE0, 0x1F, 0x0F, + 0xC0, 0x00, 0xFF, 0xC0, 0x3E, 0x0F, 0xC0, 0x01, 0xFF, 0x80, 0x7C, 0x0F, + 0xC0, 0x03, 0xDF, 0x00, 0xF8, 0x0F, 0xC0, 0x0F, 0x9F, 0x01, 0xF0, 0x1F, + 0x80, 0x1F, 0x3E, 0x03, 0xE0, 0x1F, 0x80, 0x7C, 0x3E, 0x07, 0xC0, 0x3F, + 0x00, 0xF8, 0x7C, 0x0F, 0x80, 0x3F, 0x03, 0xF0, 0xFC, 0x1F, 0x00, 0x7F, + 0x07, 0xC0, 0xFC, 0x3E, 0x00, 0x7E, 0x1F, 0x80, 0xFC, 0x00, 0x00, 0x00, + 0x7E, 0x01, 0xFC, 0x00, 0x00, 0x01, 0xF8, 0x01, 0xFC, 0x00, 0x00, 0x0F, + 0xF0, 0x01, 0xFE, 0x00, 0x00, 0x3F, 0xC0, 0x01, 0xFF, 0x00, 0x01, 0xFF, + 0x00, 0x01, 0xFF, 0xC0, 0x1F, 0xF8, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xE0, + 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x7F, 0xFF, 0xFC, 0x00, + 0x00, 0x00, 0x3F, 0xFF, 0xE0, 0x00, 0x00, 0x00, 0x0F, 0xFC, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x03, 0xF8, 0x01, + 0xFF, 0xC0, 0x7F, 0xFC, 0x1F, 0xFF, 0xC7, 0xFF, 0xFC, 0xFC, 0x1F, 0xBF, + 0x01, 0xFF, 0xC0, 0x1F, 0xF8, 0x03, 0xFF, 0x00, 0x7F, 0xE0, 0x0F, 0xFC, + 0x01, 0xFF, 0xC0, 0x7E, 0xFC, 0x1F, 0x9F, 0xFF, 0xF1, 0xFF, 0xFC, 0x1F, + 0xFF, 0x01, 0xFF, 0xC0, 0x0F, 0xE0, 0x00, 0x00, 0x1F, 0xE0, 0x00, 0x00, + 0x3F, 0xC0, 0x00, 0x00, 0x7F, 0x80, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x01, + 0xFE, 0x00, 0x00, 0x03, 0xFC, 0x00, 0x00, 0x07, 0xF8, 0x00, 0x00, 0x0F, + 0xF0, 0x00, 0x00, 0x1F, 0xE0, 0x00, 0x00, 0x3F, 0xC0, 0x00, 0x00, 0x7F, + 0x80, 0x00, 0x00, 0xFF, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xF0, 0x01, 0xFE, 0x00, 0x00, 0x03, 0xFC, 0x00, 0x00, 0x07, 0xF8, 0x00, + 0x00, 0x0F, 0xF0, 0x00, 0x00, 0x1F, 0xE0, 0x00, 0x00, 0x3F, 0xC0, 0x00, + 0x00, 0x7F, 0x80, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x01, 0xFE, 0x00, 0x00, + 0x03, 0xFC, 0x00, 0x00, 0x07, 0xF8, 0x00, 0x00, 0x0F, 0xF0, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x03, 0xF8, 0x03, 0xFF, 0xC3, 0xFF, + 0xF8, 0xFF, 0xFF, 0x7F, 0xFF, 0xDF, 0x83, 0xF7, 0xE0, 0xFF, 0xF0, 0x1F, + 0x00, 0x0F, 0xC0, 0x03, 0xF0, 0x01, 0xF8, 0x00, 0xFC, 0x00, 0xFF, 0x00, + 0x7F, 0x00, 0x3F, 0x80, 0x3F, 0xC0, 0x1F, 0xE0, 0x0F, 0xE0, 0x07, 0xFF, + 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x03, 0xF8, + 0x01, 0xFF, 0xC0, 0x7F, 0xFC, 0x1F, 0xFF, 0xC7, 0xFF, 0xF8, 0xFC, 0x3F, + 0x1F, 0x03, 0xF0, 0xE0, 0xFC, 0x00, 0x3F, 0x80, 0x3F, 0xE0, 0x07, 0xF8, + 0x00, 0xFF, 0x80, 0x1F, 0xF8, 0x00, 0x1F, 0x80, 0x01, 0xF3, 0xE0, 0x3F, + 0xFC, 0x0F, 0xEF, 0xC3, 0xFD, 0xFF, 0xFF, 0x1F, 0xFF, 0xC1, 0xFF, 0xF8, + 0x1F, 0xFC, 0x00, 0xFE, 0x00, 0x0F, 0xF8, 0x7F, 0xC1, 0xFE, 0x0F, 0xF0, + 0x3F, 0x81, 0xFE, 0x07, 0xF0, 0x3F, 0x80, 0xFC, 0x00, 0xFF, 0x80, 0x07, + 0xFF, 0xFE, 0x00, 0x1F, 0xFF, 0xF8, 0x00, 0x7F, 0xFF, 0xE0, 0x01, 0xFF, + 0xFF, 0x80, 0x07, 0xFF, 0xFE, 0x00, 0x1F, 0xFF, 0xF8, 0x00, 0x7F, 0xFF, + 0xE0, 0x01, 0xFF, 0xFF, 0x80, 0x07, 0xFF, 0xFE, 0x00, 0x1F, 0xFF, 0xF8, + 0x00, 0x7F, 0xFF, 0xE0, 0x01, 0xFF, 0xFF, 0x80, 0x07, 0xFF, 0xFE, 0x00, + 0x1F, 0xFF, 0xF8, 0x00, 0x7F, 0xFF, 0xE0, 0x01, 0xFF, 0xFF, 0x80, 0x07, + 0xFF, 0xFE, 0x00, 0x1F, 0xFF, 0xF8, 0x00, 0x7F, 0xFF, 0xE0, 0x01, 0xFF, + 0xFF, 0x80, 0x07, 0xFF, 0xFE, 0x00, 0x1F, 0xFF, 0xFC, 0x00, 0x7F, 0xFF, + 0xF0, 0x03, 0xFF, 0xFF, 0xE0, 0x0F, 0xFF, 0xFF, 0x80, 0x7F, 0xFF, 0xFF, + 0x87, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFB, 0xFF, 0xBF, 0xFF, 0xEF, 0xFE, 0xFF, 0xFF, 0x9F, 0xF3, + 0xFF, 0xFE, 0x1F, 0x00, 0x0F, 0xF8, 0x00, 0x00, 0x3F, 0xE0, 0x00, 0x00, + 0xFF, 0x80, 0x00, 0x03, 0xFE, 0x00, 0x00, 0x0F, 0xF8, 0x00, 0x00, 0x3F, + 0xE0, 0x00, 0x00, 0xFF, 0x80, 0x00, 0x03, 0xFE, 0x00, 0x00, 0x0F, 0xF8, + 0x00, 0x00, 0x3F, 0xE0, 0x00, 0x00, 0xFF, 0x80, 0x00, 0x03, 0xFE, 0x00, + 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xE0, 0x7F, 0xFF, 0xFF, 0xFC, 0x1F, + 0xFF, 0xFF, 0xFF, 0x8F, 0xFF, 0xFF, 0xFF, 0xF1, 0xFF, 0xFF, 0xFF, 0xFE, + 0x7F, 0xFF, 0xFF, 0xFF, 0xDF, 0xFF, 0xFF, 0xFF, 0xFB, 0xFF, 0xFF, 0x87, + 0xF0, 0xFF, 0xFF, 0xF0, 0xFE, 0x1F, 0xFF, 0xFE, 0x1F, 0xC3, 0xFF, 0xFF, + 0xC3, 0xF8, 0x7F, 0xFF, 0xF8, 0x7F, 0x0F, 0xFF, 0xFF, 0x0F, 0xE1, 0xFF, + 0xFF, 0xE1, 0xFC, 0x3F, 0xFF, 0xFC, 0x3F, 0x87, 0xFF, 0xFF, 0x87, 0xF0, + 0xFF, 0xFF, 0xF0, 0xFE, 0x0F, 0xFF, 0xFE, 0x1F, 0xC1, 0xFF, 0xFF, 0xC3, + 0xF8, 0x1F, 0xFF, 0xF8, 0x7F, 0x03, 0xFF, 0xFF, 0x0F, 0xE0, 0x3F, 0xFF, + 0xE1, 0xFC, 0x03, 0xFF, 0xFC, 0x3F, 0x80, 0x1F, 0xFF, 0x87, 0xF0, 0x00, + 0x7F, 0xF0, 0xFE, 0x00, 0x01, 0xFE, 0x1F, 0xC0, 0x00, 0x3F, 0xC3, 0xF8, + 0x00, 0x07, 0xF8, 0x7F, 0x00, 0x00, 0xFF, 0x0F, 0xE0, 0x00, 0x1F, 0xE1, + 0xFC, 0x00, 0x03, 0xFC, 0x3F, 0x80, 0x00, 0x7F, 0x87, 0xF0, 0x00, 0x0F, + 0xF0, 0xFE, 0x00, 0x01, 0xFE, 0x1F, 0xC0, 0x00, 0x3F, 0xC3, 0xF8, 0x00, + 0x07, 0xF8, 0x7F, 0x00, 0x00, 0xFF, 0x0F, 0xE0, 0x00, 0x1F, 0xE1, 0xFC, + 0x00, 0x03, 0xFC, 0x3F, 0x80, 0x00, 0x7F, 0x87, 0xF0, 0x00, 0x0F, 0xF0, + 0xFE, 0x00, 0x01, 0xFE, 0x1F, 0xC0, 0x00, 0x3F, 0xC3, 0xF8, 0x00, 0x07, + 0xF8, 0x7F, 0x00, 0x00, 0xFF, 0x0F, 0xE0, 0x00, 0x1F, 0xE1, 0xFC, 0x00, + 0x03, 0xFC, 0x3F, 0x80, 0x00, 0x7F, 0x87, 0xF0, 0x00, 0x0F, 0xF0, 0xFE, + 0x00, 0x01, 0xFE, 0x1F, 0xC0, 0x00, 0x3F, 0xC3, 0xF8, 0x00, 0x07, 0xF8, + 0x7F, 0x00, 0x00, 0xFF, 0x0F, 0xE0, 0x00, 0x1F, 0xE1, 0xFC, 0x00, 0x03, + 0xFC, 0x3F, 0x80, 0x00, 0x7F, 0x87, 0xF0, 0x00, 0x0F, 0xF0, 0xFE, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x07, + 0xF8, 0x07, 0xFF, 0x07, 0xFF, 0xC2, 0x07, 0xF0, 0x01, 0xF8, 0x00, 0xFC, + 0x00, 0x7E, 0x00, 0xFE, 0xFF, 0xFE, 0x7F, 0xFE, 0x0F, 0xF8, 0x00, 0x01, + 0xF0, 0x3F, 0x07, 0xF0, 0xFF, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xBF, 0xF3, + 0xF8, 0x3F, 0x03, 0xF0, 0x3F, 0x03, 0xF0, 0x3F, 0x03, 0xF0, 0x3F, 0x03, + 0xF0, 0x3F, 0x03, 0xF0, 0x3F, 0x03, 0xF0, 0x3F, 0x03, 0xF0, 0x01, 0xFC, + 0x00, 0x3F, 0xF8, 0x07, 0xFF, 0xF0, 0x7F, 0xFF, 0xC3, 0xFF, 0xFE, 0x3F, + 0xC7, 0xF9, 0xF8, 0x1F, 0xDF, 0xC0, 0x7F, 0xFE, 0x03, 0xFF, 0xE0, 0x1F, + 0xFF, 0x00, 0xFF, 0xF8, 0x07, 0xFF, 0xC0, 0x3F, 0xFE, 0x01, 0xFF, 0xF8, + 0x0F, 0xFF, 0xC0, 0x7F, 0x7E, 0x07, 0xF3, 0xFC, 0x7F, 0x8F, 0xFF, 0xF8, + 0x7F, 0xFF, 0xC1, 0xFF, 0xFC, 0x03, 0xFF, 0x80, 0x07, 0xF0, 0x00, 0x7E, + 0x07, 0xE0, 0x03, 0xF8, 0x3F, 0x80, 0x0F, 0xE0, 0xFE, 0x00, 0x7F, 0x07, + 0xF0, 0x01, 0xFC, 0x1F, 0xC0, 0x0F, 0xF0, 0xFF, 0x00, 0x3F, 0x83, 0xF8, + 0x01, 0xFE, 0x1F, 0xE0, 0x07, 0xF8, 0x7F, 0x80, 0x3F, 0xC1, 0xFC, 0x00, + 0xFF, 0x0F, 0xF0, 0x03, 0xFC, 0x3F, 0xC0, 0x1F, 0xE1, 0xFE, 0x00, 0x7F, + 0x87, 0xF8, 0x03, 0xFE, 0x3F, 0xE0, 0x1F, 0xE1, 0xFE, 0x01, 0xFF, 0x1F, + 0xF0, 0x0F, 0xF0, 0xFF, 0x00, 0xFF, 0x0F, 0xF0, 0x07, 0xF8, 0x7F, 0x80, + 0x7F, 0x87, 0xF8, 0x07, 0xF8, 0x7F, 0x80, 0x3F, 0x83, 0xFC, 0x03, 0xFC, + 0x3F, 0xC0, 0x1F, 0xC1, 0xFC, 0x01, 0xFC, 0x1F, 0xE0, 0x0F, 0xE0, 0xFE, + 0x00, 0xFE, 0x0F, 0xE0, 0x07, 0xE0, 0x7E, 0x00, 0x00, 0x01, 0xF0, 0x00, + 0x00, 0x3E, 0x00, 0x01, 0xF8, 0x00, 0x00, 0x3E, 0x00, 0x01, 0xFC, 0x00, + 0x00, 0x1F, 0x00, 0x01, 0xFE, 0x00, 0x00, 0x1F, 0x00, 0x07, 0xFF, 0x00, + 0x00, 0x0F, 0x00, 0x07, 0xFF, 0x80, 0x00, 0x0F, 0x80, 0x03, 0xFF, 0xC0, + 0x00, 0x0F, 0x80, 0x01, 0xF7, 0xE0, 0x00, 0x07, 0xC0, 0x00, 0xF3, 0xF0, + 0x00, 0x07, 0xC0, 0x00, 0x41, 0xF8, 0x00, 0x03, 0xE0, 0x00, 0x00, 0xFC, + 0x00, 0x03, 0xE0, 0x00, 0x00, 0x7E, 0x00, 0x03, 0xE0, 0x00, 0x00, 0x3F, + 0x00, 0x01, 0xF0, 0x00, 0x00, 0x1F, 0x80, 0x01, 0xF0, 0x00, 0x00, 0x0F, + 0xC0, 0x00, 0xF8, 0x00, 0x00, 0x07, 0xE0, 0x00, 0xF8, 0x00, 0x00, 0x03, + 0xF0, 0x00, 0x78, 0x00, 0x00, 0x01, 0xF8, 0x00, 0x7C, 0x00, 0x00, 0x00, + 0xFC, 0x00, 0x7C, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x3E, 0x00, 0x00, 0x00, + 0x3F, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x1F, 0x80, 0x1F, 0x00, 0x00, 0x00, + 0x0F, 0xC0, 0x1F, 0x00, 0x03, 0xF0, 0x00, 0x00, 0x0F, 0x00, 0x03, 0xF8, + 0x00, 0x00, 0x0F, 0x80, 0x01, 0xFC, 0x00, 0x00, 0x0F, 0x80, 0x01, 0xFE, + 0x00, 0x00, 0x07, 0xC0, 0x01, 0xFF, 0x00, 0x00, 0x07, 0xC0, 0x01, 0xFF, + 0x80, 0x00, 0x03, 0xE0, 0x01, 0xFF, 0xC0, 0x00, 0x03, 0xE0, 0x01, 0xF7, + 0xE0, 0x00, 0x03, 0xE0, 0x01, 0xFB, 0xF0, 0x00, 0x01, 0xF0, 0x01, 0xF9, + 0xF8, 0x00, 0x01, 0xF0, 0x01, 0xF8, 0xFC, 0x00, 0x00, 0xF8, 0x00, 0xF8, + 0x7E, 0x00, 0x00, 0xF8, 0x00, 0xF8, 0x3F, 0x00, 0x00, 0x78, 0x00, 0xFF, + 0xFF, 0xF0, 0x00, 0x7C, 0x00, 0x7F, 0xFF, 0xF8, 0x00, 0x7C, 0x00, 0x3F, + 0xFF, 0xFC, 0x00, 0x3E, 0x00, 0x1F, 0xFF, 0xFE, 0x00, 0x3E, 0x00, 0x0F, + 0xFF, 0xFF, 0x00, 0x1F, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x1F, 0x00, 0x00, + 0x00, 0x7E, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x0F, 0x80, 0x00, + 0x00, 0x1F, 0x80, 0x0F, 0x80, 0x00, 0x00, 0x0F, 0xC0, 0x07, 0xC0, 0x00, + 0x00, 0x00, 0x00, 0x01, 0xF0, 0x00, 0x00, 0x3E, 0x00, 0x03, 0xF0, 0x00, + 0x00, 0x7C, 0x00, 0x07, 0xF0, 0x00, 0x00, 0x7C, 0x00, 0x0F, 0xF0, 0x00, + 0x00, 0xF8, 0x00, 0x7F, 0xF0, 0x00, 0x00, 0xF8, 0x00, 0xFF, 0xF0, 0x00, + 0x01, 0xF0, 0x00, 0xFF, 0xF0, 0x00, 0x03, 0xE0, 0x00, 0xFB, 0xF0, 0x00, + 0x03, 0xE0, 0x00, 0xF3, 0xF0, 0x00, 0x07, 0xC0, 0x00, 0x83, 0xF0, 0x00, + 0x07, 0xC0, 0x00, 0x03, 0xF0, 0x00, 0x0F, 0x80, 0x00, 0x03, 0xF0, 0x00, + 0x0F, 0x80, 0x00, 0x03, 0xF0, 0x00, 0x1F, 0x00, 0x00, 0x03, 0xF0, 0x00, + 0x3E, 0x00, 0x00, 0x03, 0xF0, 0x00, 0x3E, 0x00, 0x00, 0x03, 0xF0, 0x00, + 0x7C, 0x00, 0x00, 0x03, 0xF0, 0x00, 0x7C, 0x00, 0x00, 0x03, 0xF0, 0x00, + 0xF8, 0x00, 0x00, 0x03, 0xF0, 0x00, 0xF8, 0x00, 0x00, 0x03, 0xF0, 0x01, + 0xF0, 0x00, 0x00, 0x03, 0xF0, 0x01, 0xE0, 0x00, 0x00, 0x03, 0xF0, 0x03, + 0xE0, 0x00, 0x00, 0x03, 0xF0, 0x07, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x07, + 0xC0, 0x1F, 0xE0, 0x00, 0x00, 0x0F, 0x80, 0x7F, 0xF8, 0x00, 0x00, 0x0F, + 0x80, 0xFF, 0xFC, 0x00, 0x00, 0x1F, 0x01, 0xFF, 0xFE, 0x00, 0x00, 0x1E, + 0x01, 0xFF, 0xFF, 0x00, 0x00, 0x3E, 0x03, 0xF8, 0x7F, 0x00, 0x00, 0x7C, + 0x03, 0xF0, 0x3F, 0x00, 0x00, 0x7C, 0x03, 0xF0, 0x3F, 0x00, 0x00, 0xF8, + 0x00, 0x00, 0x3F, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x7E, 0x00, 0x01, 0xF0, + 0x00, 0x00, 0xFE, 0x00, 0x01, 0xE0, 0x00, 0x01, 0xFC, 0x00, 0x03, 0xE0, + 0x00, 0x03, 0xF8, 0x00, 0x07, 0xC0, 0x00, 0x0F, 0xF0, 0x00, 0x07, 0xC0, + 0x00, 0x1F, 0xE0, 0x00, 0x0F, 0x80, 0x00, 0x3F, 0x80, 0x00, 0x0F, 0x80, + 0x00, 0x7F, 0x00, 0x00, 0x1F, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x1E, 0x00, + 0x01, 0xFF, 0xFF, 0x00, 0x3E, 0x00, 0x03, 0xFF, 0xFF, 0x00, 0x7C, 0x00, + 0x03, 0xFF, 0xFF, 0x00, 0x7C, 0x00, 0x03, 0xFF, 0xFF, 0x00, 0xF8, 0x00, + 0x07, 0xFF, 0xFF, 0x00, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x03, 0xF8, 0x00, + 0x00, 0x00, 0x00, 0x01, 0xFF, 0xC0, 0x00, 0x00, 0xF8, 0x00, 0x7F, 0xFC, + 0x00, 0x00, 0x3E, 0x00, 0x1F, 0xFF, 0xC0, 0x00, 0x0F, 0x80, 0x07, 0xFF, + 0xF8, 0x00, 0x01, 0xF0, 0x00, 0xFC, 0x3F, 0x00, 0x00, 0x7C, 0x00, 0x1F, + 0x03, 0xF0, 0x00, 0x0F, 0x80, 0x00, 0xE0, 0xFC, 0x00, 0x03, 0xE0, 0x00, + 0x00, 0x3F, 0x80, 0x00, 0x78, 0x00, 0x00, 0x3F, 0xE0, 0x00, 0x1F, 0x00, + 0x00, 0x07, 0xF8, 0x00, 0x07, 0xC0, 0x00, 0x00, 0xFF, 0x80, 0x00, 0xF8, + 0x00, 0x00, 0x1F, 0xF8, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x1F, 0x80, 0x07, + 0xC0, 0x00, 0x00, 0x01, 0xF0, 0x01, 0xF0, 0x00, 0x03, 0xE0, 0x3F, 0x00, + 0x7C, 0x00, 0x00, 0xFC, 0x0F, 0xE0, 0x0F, 0x80, 0x00, 0x0F, 0xC3, 0xFC, + 0x03, 0xE0, 0x00, 0x01, 0xFF, 0xFF, 0x00, 0x7C, 0x00, 0x00, 0x1F, 0xFF, + 0xC0, 0x1F, 0x00, 0x00, 0x01, 0xFF, 0xF8, 0x03, 0xC0, 0x00, 0x00, 0x1F, + 0xFC, 0x00, 0xF8, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x3E, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x07, 0xC0, 0x01, 0xF8, 0x00, 0x00, 0x01, 0xF0, 0x00, 0x7F, + 0x00, 0x00, 0x00, 0x3E, 0x00, 0x0F, 0xE0, 0x00, 0x00, 0x0F, 0x80, 0x03, + 0xFC, 0x00, 0x00, 0x01, 0xE0, 0x00, 0xFF, 0x80, 0x00, 0x00, 0x7C, 0x00, + 0x3F, 0xF0, 0x00, 0x00, 0x1F, 0x00, 0x0F, 0xFE, 0x00, 0x00, 0x03, 0xE0, + 0x03, 0xEF, 0xC0, 0x00, 0x00, 0xF8, 0x00, 0xFD, 0xF8, 0x00, 0x00, 0x1F, + 0x00, 0x3F, 0x3F, 0x00, 0x00, 0x07, 0xC0, 0x0F, 0xC7, 0xE0, 0x00, 0x01, + 0xF0, 0x01, 0xF0, 0xFC, 0x00, 0x00, 0x3E, 0x00, 0x7C, 0x1F, 0x80, 0x00, + 0x0F, 0x80, 0x1F, 0xFF, 0xFE, 0x00, 0x01, 0xF0, 0x03, 0xFF, 0xFF, 0xC0, + 0x00, 0x7C, 0x00, 0x7F, 0xFF, 0xF8, 0x00, 0x0F, 0x00, 0x0F, 0xFF, 0xFF, + 0x00, 0x03, 0xE0, 0x01, 0xFF, 0xFF, 0xE0, 0x00, 0xF8, 0x00, 0x00, 0x07, + 0xE0, 0x00, 0x1F, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x07, 0xC0, 0x00, 0x00, + 0x1F, 0x80, 0x00, 0xF8, 0x00, 0x00, 0x03, 0xF0, 0x00, 0x3E, 0x00, 0x00, + 0x00, 0x7E, 0x00, 0x0F, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xF0, + 0x00, 0x00, 0x07, 0xF8, 0x00, 0x00, 0x03, 0xFC, 0x00, 0x00, 0x01, 0xFE, + 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x7F, 0x80, 0x00, 0x00, 0x3F, + 0xC0, 0x00, 0x00, 0x1F, 0xE0, 0x00, 0x00, 0x0F, 0xF0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7F, 0x00, 0x00, 0x00, 0x3F, 0xC0, 0x00, 0x00, 0x1F, 0xE0, 0x00, 0x00, + 0x1F, 0xF0, 0x00, 0x00, 0x0F, 0xF0, 0x00, 0x00, 0x07, 0xF8, 0x00, 0x00, + 0x07, 0xFC, 0x00, 0x00, 0x07, 0xFE, 0x00, 0x00, 0x07, 0xFE, 0x00, 0x00, + 0x07, 0xFF, 0x00, 0x00, 0x07, 0xFF, 0x00, 0x00, 0x07, 0xFF, 0x00, 0x00, + 0x07, 0xFF, 0x00, 0x00, 0x07, 0xFF, 0x00, 0x00, 0x07, 0xFF, 0x00, 0x00, + 0x07, 0xFF, 0x00, 0x00, 0x07, 0xFF, 0x00, 0x00, 0x03, 0xFF, 0x00, 0x00, + 0x01, 0xFF, 0x00, 0x00, 0x01, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x80, 0x00, + 0x00, 0x7F, 0xC0, 0x00, 0x00, 0x3F, 0xE0, 0x00, 0x3E, 0x1F, 0xF0, 0x00, + 0x1F, 0xEF, 0xF8, 0x00, 0x1F, 0xF3, 0xFE, 0x00, 0x0F, 0xF9, 0xFF, 0x80, + 0x0F, 0xFC, 0x7F, 0xF0, 0x3F, 0xFC, 0x3F, 0xFF, 0xFF, 0xFE, 0x0F, 0xFF, + 0xFF, 0xFE, 0x03, 0xFF, 0xFF, 0xFE, 0x00, 0xFF, 0xFF, 0xFE, 0x00, 0x3F, + 0xFF, 0xFC, 0x00, 0x07, 0xFF, 0xF8, 0x00, 0x00, 0x3F, 0xE0, 0x00, 0x00, + 0x03, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xF8, 0x00, 0x00, 0x00, 0x00, + 0x3F, 0xC0, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x03, + 0xF8, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x7F, + 0x00, 0x00, 0x00, 0x00, 0x01, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x07, 0xE0, + 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, 0xF0, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xC0, 0x00, 0x00, + 0x00, 0x0F, 0xFE, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xF8, 0x00, 0x00, 0x00, + 0x07, 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x7F, 0xFE, 0x00, 0x00, 0x00, 0x03, + 0xFF, 0xF8, 0x00, 0x00, 0x00, 0x1F, 0xFF, 0xC0, 0x00, 0x00, 0x01, 0xFF, + 0xFF, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xF8, 0x00, 0x00, 0x00, 0xFF, 0xFF, + 0xC0, 0x00, 0x00, 0x07, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x3F, 0xEF, 0xF8, + 0x00, 0x00, 0x03, 0xFE, 0x7F, 0xE0, 0x00, 0x00, 0x1F, 0xF1, 0xFF, 0x00, + 0x00, 0x01, 0xFF, 0x8F, 0xF8, 0x00, 0x00, 0x0F, 0xF8, 0x7F, 0xE0, 0x00, + 0x00, 0x7F, 0xC1, 0xFF, 0x00, 0x00, 0x07, 0xFC, 0x0F, 0xFC, 0x00, 0x00, + 0x3F, 0xE0, 0x3F, 0xE0, 0x00, 0x01, 0xFF, 0x01, 0xFF, 0x00, 0x00, 0x1F, + 0xF0, 0x0F, 0xFC, 0x00, 0x00, 0xFF, 0x80, 0x3F, 0xE0, 0x00, 0x0F, 0xFC, + 0x01, 0xFF, 0x80, 0x00, 0x7F, 0xC0, 0x07, 0xFC, 0x00, 0x03, 0xFE, 0x00, + 0x3F, 0xE0, 0x00, 0x3F, 0xE0, 0x01, 0xFF, 0x80, 0x01, 0xFF, 0xFF, 0xFF, + 0xFC, 0x00, 0x1F, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, + 0x80, 0x07, 0xFF, 0xFF, 0xFF, 0xFC, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xF0, + 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x3F, 0xFF, 0xFF, 0xFF, 0xFE, 0x01, + 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x0F, 0xF8, 0x00, 0x00, 0xFF, 0x80, 0xFF, + 0xC0, 0x00, 0x07, 0xFE, 0x07, 0xFC, 0x00, 0x00, 0x3F, 0xF0, 0x3F, 0xE0, + 0x00, 0x00, 0xFF, 0xC3, 0xFF, 0x00, 0x00, 0x07, 0xFE, 0x1F, 0xF0, 0x00, + 0x00, 0x1F, 0xF1, 0xFF, 0x80, 0x00, 0x00, 0xFF, 0xCF, 0xFC, 0x00, 0x00, + 0x07, 0xFE, 0x7F, 0xC0, 0x00, 0x00, 0x1F, 0xFF, 0xFE, 0x00, 0x00, 0x00, + 0xFF, 0xC0, 0x00, 0x00, 0x07, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xC0, + 0x00, 0x00, 0x00, 0x03, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xC0, 0x00, + 0x00, 0x00, 0x01, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xE0, 0x00, 0x00, + 0x00, 0x00, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xE0, 0x00, 0x00, 0x00, + 0x00, 0x7E, 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, 0xF0, 0x00, 0x00, 0x00, 0x01, 0xFF, + 0xC0, 0x00, 0x00, 0x00, 0x0F, 0xFE, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xF8, + 0x00, 0x00, 0x00, 0x07, 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x7F, 0xFE, 0x00, + 0x00, 0x00, 0x03, 0xFF, 0xF8, 0x00, 0x00, 0x00, 0x1F, 0xFF, 0xC0, 0x00, + 0x00, 0x01, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xF8, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x07, 0xFF, 0xFF, 0x00, 0x00, 0x00, + 0x3F, 0xEF, 0xF8, 0x00, 0x00, 0x03, 0xFE, 0x7F, 0xE0, 0x00, 0x00, 0x1F, + 0xF1, 0xFF, 0x00, 0x00, 0x01, 0xFF, 0x8F, 0xF8, 0x00, 0x00, 0x0F, 0xF8, + 0x7F, 0xE0, 0x00, 0x00, 0x7F, 0xC1, 0xFF, 0x00, 0x00, 0x07, 0xFC, 0x0F, + 0xFC, 0x00, 0x00, 0x3F, 0xE0, 0x3F, 0xE0, 0x00, 0x01, 0xFF, 0x01, 0xFF, + 0x00, 0x00, 0x1F, 0xF0, 0x0F, 0xFC, 0x00, 0x00, 0xFF, 0x80, 0x3F, 0xE0, + 0x00, 0x0F, 0xFC, 0x01, 0xFF, 0x80, 0x00, 0x7F, 0xC0, 0x07, 0xFC, 0x00, + 0x03, 0xFE, 0x00, 0x3F, 0xE0, 0x00, 0x3F, 0xE0, 0x01, 0xFF, 0x80, 0x01, + 0xFF, 0xFF, 0xFF, 0xFC, 0x00, 0x1F, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0xFF, + 0xFF, 0xFF, 0xFF, 0x80, 0x07, 0xFF, 0xFF, 0xFF, 0xFC, 0x00, 0x7F, 0xFF, + 0xFF, 0xFF, 0xF0, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x3F, 0xFF, 0xFF, + 0xFF, 0xFE, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x0F, 0xF8, 0x00, 0x00, + 0xFF, 0x80, 0xFF, 0xC0, 0x00, 0x07, 0xFE, 0x07, 0xFC, 0x00, 0x00, 0x3F, + 0xF0, 0x3F, 0xE0, 0x00, 0x00, 0xFF, 0xC3, 0xFF, 0x00, 0x00, 0x07, 0xFE, + 0x1F, 0xF0, 0x00, 0x00, 0x1F, 0xF1, 0xFF, 0x80, 0x00, 0x00, 0xFF, 0xCF, + 0xFC, 0x00, 0x00, 0x07, 0xFE, 0x7F, 0xC0, 0x00, 0x00, 0x1F, 0xFF, 0xFE, + 0x00, 0x00, 0x00, 0xFF, 0xC0, 0x00, 0x00, 0x3F, 0xE0, 0x00, 0x00, 0x00, + 0x03, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x1F, 0xFC, 0x00, 0x00, 0x00, 0x01, + 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x1F, 0xDF, 0xC0, 0x00, 0x00, 0x00, 0xFC, + 0xFE, 0x00, 0x00, 0x00, 0x0F, 0xE3, 0xF8, 0x00, 0x00, 0x00, 0xFE, 0x0F, + 0xE0, 0x00, 0x00, 0x0F, 0xE0, 0x3F, 0x80, 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, 0xF0, 0x00, 0x00, + 0x00, 0x01, 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x0F, 0xFE, 0x00, 0x00, 0x00, + 0x00, 0xFF, 0xF8, 0x00, 0x00, 0x00, 0x07, 0xFF, 0xC0, 0x00, 0x00, 0x00, + 0x7F, 0xFE, 0x00, 0x00, 0x00, 0x03, 0xFF, 0xF8, 0x00, 0x00, 0x00, 0x1F, + 0xFF, 0xC0, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x0F, 0xFF, + 0xF8, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x07, 0xFF, 0xFF, + 0x00, 0x00, 0x00, 0x3F, 0xEF, 0xF8, 0x00, 0x00, 0x03, 0xFE, 0x7F, 0xE0, + 0x00, 0x00, 0x1F, 0xF1, 0xFF, 0x00, 0x00, 0x01, 0xFF, 0x8F, 0xF8, 0x00, + 0x00, 0x0F, 0xF8, 0x7F, 0xE0, 0x00, 0x00, 0x7F, 0xC1, 0xFF, 0x00, 0x00, + 0x07, 0xFC, 0x0F, 0xFC, 0x00, 0x00, 0x3F, 0xE0, 0x3F, 0xE0, 0x00, 0x01, + 0xFF, 0x01, 0xFF, 0x00, 0x00, 0x1F, 0xF0, 0x0F, 0xFC, 0x00, 0x00, 0xFF, + 0x80, 0x3F, 0xE0, 0x00, 0x0F, 0xFC, 0x01, 0xFF, 0x80, 0x00, 0x7F, 0xC0, + 0x07, 0xFC, 0x00, 0x03, 0xFE, 0x00, 0x3F, 0xE0, 0x00, 0x3F, 0xE0, 0x01, + 0xFF, 0x80, 0x01, 0xFF, 0xFF, 0xFF, 0xFC, 0x00, 0x1F, 0xFF, 0xFF, 0xFF, + 0xF0, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x07, 0xFF, 0xFF, 0xFF, 0xFC, + 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xF0, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, + 0x3F, 0xFF, 0xFF, 0xFF, 0xFE, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x0F, + 0xF8, 0x00, 0x00, 0xFF, 0x80, 0xFF, 0xC0, 0x00, 0x07, 0xFE, 0x07, 0xFC, + 0x00, 0x00, 0x3F, 0xF0, 0x3F, 0xE0, 0x00, 0x00, 0xFF, 0xC3, 0xFF, 0x00, + 0x00, 0x07, 0xFE, 0x1F, 0xF0, 0x00, 0x00, 0x1F, 0xF1, 0xFF, 0x80, 0x00, + 0x00, 0xFF, 0xCF, 0xFC, 0x00, 0x00, 0x07, 0xFE, 0x7F, 0xC0, 0x00, 0x00, + 0x1F, 0xFF, 0xFE, 0x00, 0x00, 0x00, 0xFF, 0xC0, 0x00, 0x01, 0xF0, 0x07, + 0x80, 0x00, 0x00, 0x3F, 0xE0, 0x3C, 0x00, 0x00, 0x01, 0xFF, 0xC3, 0xE0, + 0x00, 0x00, 0x1F, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xF0, 0x00, + 0x00, 0x07, 0x87, 0xFF, 0x80, 0x00, 0x00, 0x3C, 0x0F, 0xF8, 0x00, 0x00, + 0x01, 0xE0, 0x0F, 0x80, 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, 0x07, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x3F, + 0xF8, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x1F, 0xFF, + 0x00, 0x00, 0x00, 0x00, 0xFF, 0xF8, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xC0, + 0x00, 0x00, 0x00, 0x7F, 0xFF, 0x00, 0x00, 0x00, 0x03, 0xFF, 0xF8, 0x00, + 0x00, 0x00, 0x3F, 0xFF, 0xE0, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0x00, 0x00, + 0x00, 0x1F, 0xFF, 0xF8, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xE0, 0x00, 0x00, + 0x07, 0xFD, 0xFF, 0x00, 0x00, 0x00, 0x7F, 0xCF, 0xFC, 0x00, 0x00, 0x03, + 0xFE, 0x3F, 0xE0, 0x00, 0x00, 0x3F, 0xF1, 0xFF, 0x00, 0x00, 0x01, 0xFF, + 0x0F, 0xFC, 0x00, 0x00, 0x0F, 0xF8, 0x3F, 0xE0, 0x00, 0x00, 0xFF, 0x81, + 0xFF, 0x80, 0x00, 0x07, 0xFC, 0x07, 0xFC, 0x00, 0x00, 0x3F, 0xE0, 0x3F, + 0xE0, 0x00, 0x03, 0xFE, 0x01, 0xFF, 0x80, 0x00, 0x1F, 0xF0, 0x07, 0xFC, + 0x00, 0x01, 0xFF, 0x80, 0x3F, 0xF0, 0x00, 0x0F, 0xF8, 0x00, 0xFF, 0x80, + 0x00, 0x7F, 0xC0, 0x07, 0xFC, 0x00, 0x07, 0xFC, 0x00, 0x3F, 0xF0, 0x00, + 0x3F, 0xFF, 0xFF, 0xFF, 0x80, 0x03, 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0x1F, + 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x0F, 0xFF, + 0xFF, 0xFF, 0xFE, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xF0, 0x07, 0xFF, 0xFF, + 0xFF, 0xFF, 0xC0, 0x3F, 0xFF, 0xFF, 0xFF, 0xFE, 0x01, 0xFF, 0x00, 0x00, + 0x1F, 0xF0, 0x1F, 0xF8, 0x00, 0x00, 0xFF, 0xC0, 0xFF, 0x80, 0x00, 0x07, + 0xFE, 0x07, 0xFC, 0x00, 0x00, 0x1F, 0xF8, 0x7F, 0xE0, 0x00, 0x00, 0xFF, + 0xC3, 0xFE, 0x00, 0x00, 0x03, 0xFE, 0x3F, 0xF0, 0x00, 0x00, 0x1F, 0xF9, + 0xFF, 0x80, 0x00, 0x00, 0xFF, 0xCF, 0xF8, 0x00, 0x00, 0x03, 0xFF, 0xFF, + 0xC0, 0x00, 0x00, 0x1F, 0xF8, 0x00, 0x0F, 0xF0, 0x7F, 0x80, 0x00, 0x00, + 0x7F, 0x83, 0xFC, 0x00, 0x00, 0x03, 0xFC, 0x1F, 0xE0, 0x00, 0x00, 0x1F, + 0xE0, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x07, 0xF8, 0x00, 0x00, 0x07, 0xF8, + 0x3F, 0xC0, 0x00, 0x00, 0x3F, 0xC1, 0xFE, 0x00, 0x00, 0x01, 0xFE, 0x0F, + 0xF0, 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, 0x07, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xF8, 0x00, 0x00, + 0x00, 0x01, 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x1F, 0xFF, 0x00, 0x00, 0x00, + 0x00, 0xFF, 0xF8, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xC0, 0x00, 0x00, 0x00, + 0x7F, 0xFF, 0x00, 0x00, 0x00, 0x03, 0xFF, 0xF8, 0x00, 0x00, 0x00, 0x3F, + 0xFF, 0xE0, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x1F, 0xFF, + 0xF8, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xE0, 0x00, 0x00, 0x07, 0xFD, 0xFF, + 0x00, 0x00, 0x00, 0x7F, 0xCF, 0xFC, 0x00, 0x00, 0x03, 0xFE, 0x3F, 0xE0, + 0x00, 0x00, 0x3F, 0xF1, 0xFF, 0x00, 0x00, 0x01, 0xFF, 0x0F, 0xFC, 0x00, + 0x00, 0x0F, 0xF8, 0x3F, 0xE0, 0x00, 0x00, 0xFF, 0x81, 0xFF, 0x80, 0x00, + 0x07, 0xFC, 0x07, 0xFC, 0x00, 0x00, 0x3F, 0xE0, 0x3F, 0xE0, 0x00, 0x03, + 0xFE, 0x01, 0xFF, 0x80, 0x00, 0x1F, 0xF0, 0x07, 0xFC, 0x00, 0x01, 0xFF, + 0x80, 0x3F, 0xF0, 0x00, 0x0F, 0xF8, 0x00, 0xFF, 0x80, 0x00, 0x7F, 0xC0, + 0x07, 0xFC, 0x00, 0x07, 0xFC, 0x00, 0x3F, 0xF0, 0x00, 0x3F, 0xFF, 0xFF, + 0xFF, 0x80, 0x03, 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0x1F, 0xFF, 0xFF, 0xFF, + 0xF0, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x0F, 0xFF, 0xFF, 0xFF, 0xFE, + 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xF0, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, + 0x3F, 0xFF, 0xFF, 0xFF, 0xFE, 0x01, 0xFF, 0x00, 0x00, 0x1F, 0xF0, 0x1F, + 0xF8, 0x00, 0x00, 0xFF, 0xC0, 0xFF, 0x80, 0x00, 0x07, 0xFE, 0x07, 0xFC, + 0x00, 0x00, 0x1F, 0xF8, 0x7F, 0xE0, 0x00, 0x00, 0xFF, 0xC3, 0xFE, 0x00, + 0x00, 0x03, 0xFE, 0x3F, 0xF0, 0x00, 0x00, 0x1F, 0xF9, 0xFF, 0x80, 0x00, + 0x00, 0xFF, 0xCF, 0xF8, 0x00, 0x00, 0x03, 0xFF, 0xFF, 0xC0, 0x00, 0x00, + 0x1F, 0xF8, 0x00, 0x00, 0x0F, 0x80, 0x00, 0x00, 0x00, 0x01, 0xFE, 0x00, + 0x00, 0x00, 0x00, 0x0F, 0xF8, 0x00, 0x00, 0x00, 0x00, 0xF1, 0xE0, 0x00, + 0x00, 0x00, 0x07, 0x07, 0x00, 0x00, 0x00, 0x00, 0x78, 0x38, 0x00, 0x00, + 0x00, 0x03, 0xC1, 0xE0, 0x00, 0x00, 0x00, 0x0E, 0x0E, 0x00, 0x00, 0x00, + 0x00, 0x78, 0xF0, 0x00, 0x00, 0x00, 0x03, 0xFF, 0x00, 0x00, 0x00, 0x00, + 0x1F, 0xFC, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xE0, 0x00, 0x00, 0x00, 0x0F, + 0xFF, 0x80, 0x00, 0x00, 0x00, 0x7F, 0xFC, 0x00, 0x00, 0x00, 0x07, 0xFF, + 0xE0, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0x80, 0x00, 0x00, 0x01, 0xFF, 0xFC, + 0x00, 0x00, 0x00, 0x1F, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x80, + 0x00, 0x00, 0x0F, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xF0, 0x00, + 0x00, 0x03, 0xFE, 0xFF, 0x80, 0x00, 0x00, 0x3F, 0xE7, 0xFE, 0x00, 0x00, + 0x01, 0xFF, 0x1F, 0xF0, 0x00, 0x00, 0x1F, 0xF8, 0xFF, 0x80, 0x00, 0x00, + 0xFF, 0x87, 0xFE, 0x00, 0x00, 0x07, 0xFC, 0x1F, 0xF0, 0x00, 0x00, 0x7F, + 0xC0, 0xFF, 0xC0, 0x00, 0x03, 0xFE, 0x03, 0xFE, 0x00, 0x00, 0x1F, 0xF0, + 0x1F, 0xF0, 0x00, 0x01, 0xFF, 0x00, 0xFF, 0xC0, 0x00, 0x0F, 0xF8, 0x03, + 0xFE, 0x00, 0x00, 0xFF, 0xC0, 0x1F, 0xF8, 0x00, 0x07, 0xFC, 0x00, 0x7F, + 0xC0, 0x00, 0x3F, 0xE0, 0x03, 0xFE, 0x00, 0x03, 0xFE, 0x00, 0x1F, 0xF8, + 0x00, 0x1F, 0xFF, 0xFF, 0xFF, 0xC0, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, + 0x0F, 0xFF, 0xFF, 0xFF, 0xF8, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xC0, 0x07, + 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x3F, 0xFF, 0xFF, 0xFF, 0xF8, 0x03, 0xFF, + 0xFF, 0xFF, 0xFF, 0xE0, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0x80, + 0x00, 0x0F, 0xF8, 0x0F, 0xFC, 0x00, 0x00, 0x7F, 0xE0, 0x7F, 0xC0, 0x00, + 0x03, 0xFF, 0x03, 0xFE, 0x00, 0x00, 0x0F, 0xFC, 0x3F, 0xF0, 0x00, 0x00, + 0x7F, 0xE1, 0xFF, 0x00, 0x00, 0x01, 0xFF, 0x1F, 0xF8, 0x00, 0x00, 0x0F, + 0xFC, 0xFF, 0xC0, 0x00, 0x00, 0x7F, 0xE7, 0xFC, 0x00, 0x00, 0x01, 0xFF, + 0xFF, 0xE0, 0x00, 0x00, 0x0F, 0xFC, 0x00, 0x00, 0x07, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFC, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x00, 0x00, + 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, + 0xFF, 0xE0, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x00, 0x03, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0x00, 0x00, 0x1F, 0xF0, 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0x00, 0x7F, + 0xE1, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x83, 0xFE, 0x00, 0x00, + 0x00, 0x00, 0x03, 0xFF, 0x07, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFC, + 0x0F, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xF8, 0x1F, 0xF0, 0x00, 0x00, + 0x00, 0x00, 0x3F, 0xE0, 0x3F, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xC0, + 0x7F, 0xC0, 0x00, 0x00, 0x00, 0x01, 0xFF, 0x00, 0xFF, 0x80, 0x00, 0x00, + 0x00, 0x03, 0xFE, 0x01, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xF8, 0x03, + 0xFF, 0xFF, 0xFF, 0xE0, 0x00, 0x1F, 0xF0, 0x07, 0xFF, 0xFF, 0xFF, 0xC0, + 0x00, 0x7F, 0xC0, 0x0F, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0xFF, 0x80, 0x1F, + 0xFF, 0xFF, 0xFF, 0x00, 0x03, 0xFF, 0x00, 0x3F, 0xFF, 0xFF, 0xFE, 0x00, + 0x07, 0xFC, 0x00, 0x7F, 0xFF, 0xFF, 0xFC, 0x00, 0x1F, 0xF8, 0x00, 0xFF, + 0xFF, 0xFF, 0xF8, 0x00, 0x3F, 0xE0, 0x01, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, + 0xFF, 0xC0, 0x03, 0xFE, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFC, + 0x00, 0x00, 0x00, 0x03, 0xFF, 0xFF, 0xFF, 0xF8, 0x00, 0x00, 0x00, 0x0F, + 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x1F, 0xFF, 0xFF, 0xFF, 0xE0, + 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x00, 0xFF, + 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, + 0x00, 0x00, 0x07, 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0x00, 0x00, 0x1F, 0xF8, + 0x00, 0x07, 0xFC, 0x00, 0x00, 0x00, 0x3F, 0xE0, 0x00, 0x0F, 0xF8, 0x00, + 0x00, 0x00, 0xFF, 0xC0, 0x00, 0x1F, 0xF0, 0x00, 0x00, 0x01, 0xFF, 0x80, + 0x00, 0x3F, 0xFF, 0xFF, 0xFF, 0xC7, 0xFE, 0x00, 0x00, 0x7F, 0xFF, 0xFF, + 0xFF, 0x8F, 0xFC, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x3F, 0xF0, 0x00, + 0x01, 0xFF, 0xFF, 0xFF, 0xFE, 0x7F, 0xE0, 0x00, 0x03, 0xFF, 0xFF, 0xFF, + 0xFC, 0xFF, 0xC0, 0x00, 0x07, 0xFF, 0xFF, 0xFF, 0xFB, 0xFF, 0x00, 0x00, + 0x0F, 0xFF, 0xFF, 0xFF, 0xF7, 0xFE, 0x00, 0x00, 0x1F, 0xFF, 0xFF, 0xFF, + 0xE0, 0x00, 0x00, 0xFF, 0xC0, 0x00, 0x00, 0x0F, 0xFF, 0xF0, 0x00, 0x00, + 0xFF, 0xFF, 0xF8, 0x00, 0x03, 0xFF, 0xFF, 0xFC, 0x00, 0x0F, 0xFF, 0xFF, + 0xFC, 0x00, 0x7F, 0xFF, 0xFF, 0xFC, 0x01, 0xFF, 0xFF, 0xFF, 0xFC, 0x03, + 0xFF, 0xFF, 0xFF, 0xF8, 0x0F, 0xFF, 0x80, 0xFF, 0xF8, 0x3F, 0xFC, 0x00, + 0x7F, 0xF0, 0x7F, 0xF0, 0x00, 0x7F, 0xF1, 0xFF, 0x80, 0x00, 0x7F, 0xE3, + 0xFF, 0x00, 0x00, 0x7F, 0xEF, 0xFC, 0x00, 0x00, 0xFF, 0xDF, 0xF8, 0x00, + 0x00, 0xFE, 0x3F, 0xE0, 0x00, 0x01, 0x80, 0x7F, 0xC0, 0x00, 0x00, 0x01, + 0xFF, 0x80, 0x00, 0x00, 0x03, 0xFF, 0x00, 0x00, 0x00, 0x07, 0xFE, 0x00, + 0x00, 0x00, 0x0F, 0xF8, 0x00, 0x00, 0x00, 0x1F, 0xF0, 0x00, 0x00, 0x00, + 0x3F, 0xE0, 0x00, 0x00, 0x00, 0x7F, 0xC0, 0x00, 0x00, 0x00, 0xFF, 0x80, + 0x00, 0x00, 0x01, 0xFF, 0x00, 0x00, 0x00, 0x03, 0xFF, 0x00, 0x00, 0x00, + 0x07, 0xFE, 0x00, 0x00, 0x00, 0x0F, 0xFC, 0x00, 0x00, 0x30, 0x0F, 0xF8, + 0x00, 0x00, 0x7C, 0x1F, 0xF0, 0x00, 0x00, 0xFF, 0x3F, 0xF0, 0x00, 0x03, + 0xFF, 0x7F, 0xE0, 0x00, 0x07, 0xFE, 0x7F, 0xE0, 0x00, 0x1F, 0xF8, 0xFF, + 0xC0, 0x00, 0x3F, 0xF0, 0xFF, 0xC0, 0x00, 0xFF, 0xE1, 0xFF, 0xE0, 0x03, + 0xFF, 0x81, 0xFF, 0xF0, 0x3F, 0xFF, 0x03, 0xFF, 0xFF, 0xFF, 0xFC, 0x03, + 0xFF, 0xFF, 0xFF, 0xF0, 0x03, 0xFF, 0xFF, 0xFF, 0xC0, 0x03, 0xFF, 0xFF, + 0xFF, 0x00, 0x01, 0xFF, 0xFF, 0xFC, 0x00, 0x01, 0xFF, 0xFF, 0xF0, 0x00, + 0x00, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x1F, 0xF8, 0x00, 0x00, 0x00, 0x3F, + 0xC0, 0x00, 0x00, 0x00, 0xFF, 0xE0, 0x00, 0x00, 0x03, 0xFF, 0xE0, 0x00, + 0x00, 0x04, 0x0F, 0xE0, 0x00, 0x00, 0x00, 0x0F, 0xC0, 0x00, 0x00, 0x00, + 0x1F, 0x80, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x01, 0xFC, 0x00, + 0x00, 0x07, 0xFF, 0xF0, 0x00, 0x00, 0x0F, 0xFF, 0xC0, 0x00, 0x00, 0x07, + 0xFC, 0x00, 0x00, 0x00, 0xFF, 0x80, 0x00, 0x00, 0x1F, 0xF0, 0x00, 0x00, + 0x03, 0xFC, 0x00, 0x00, 0x00, 0x7F, 0x80, 0x00, 0x00, 0x0F, 0xE0, 0x00, + 0x00, 0x03, 0xFC, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x0F, 0xE0, + 0x00, 0x00, 0x01, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xFF, + 0xFF, 0xFF, 0xEF, 0xFF, 0xFF, 0xFF, 0xFB, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, + 0xFF, 0xFF, 0xFF, 0xBF, 0xFF, 0xFF, 0xFF, 0xEF, 0xFF, 0xFF, 0xFF, 0xFB, + 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xBF, 0xE0, 0x00, 0x00, + 0x0F, 0xF8, 0x00, 0x00, 0x03, 0xFE, 0x00, 0x00, 0x00, 0xFF, 0x80, 0x00, + 0x00, 0x3F, 0xE0, 0x00, 0x00, 0x0F, 0xF8, 0x00, 0x00, 0x03, 0xFE, 0x00, + 0x00, 0x00, 0xFF, 0x80, 0x00, 0x00, 0x3F, 0xE0, 0x00, 0x00, 0x0F, 0xF8, + 0x00, 0x00, 0x03, 0xFF, 0xFF, 0xFF, 0xF8, 0xFF, 0xFF, 0xFF, 0xFE, 0x3F, + 0xFF, 0xFF, 0xFF, 0x8F, 0xFF, 0xFF, 0xFF, 0xE3, 0xFF, 0xFF, 0xFF, 0xF8, + 0xFF, 0xFF, 0xFF, 0xFE, 0x3F, 0xFF, 0xFF, 0xFF, 0x8F, 0xFF, 0xFF, 0xFF, + 0xE3, 0xFE, 0x00, 0x00, 0x00, 0xFF, 0x80, 0x00, 0x00, 0x3F, 0xE0, 0x00, + 0x00, 0x0F, 0xF8, 0x00, 0x00, 0x03, 0xFE, 0x00, 0x00, 0x00, 0xFF, 0x80, + 0x00, 0x00, 0x3F, 0xE0, 0x00, 0x00, 0x0F, 0xF8, 0x00, 0x00, 0x03, 0xFE, + 0x00, 0x00, 0x00, 0xFF, 0x80, 0x00, 0x00, 0x3F, 0xE0, 0x00, 0x00, 0x0F, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x01, + 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x80, 0x00, 0x00, 0x3F, 0xC0, 0x00, 0x00, + 0x1F, 0xE0, 0x00, 0x00, 0x07, 0xF0, 0x00, 0x00, 0x03, 0xFC, 0x00, 0x00, + 0x00, 0xFE, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x1F, 0x80, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xFF, 0xFF, 0xEF, 0xFF, 0xFF, + 0xFF, 0xFB, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xBF, 0xFF, + 0xFF, 0xFF, 0xEF, 0xFF, 0xFF, 0xFF, 0xFB, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, + 0xFF, 0xFF, 0xFF, 0xBF, 0xE0, 0x00, 0x00, 0x0F, 0xF8, 0x00, 0x00, 0x03, + 0xFE, 0x00, 0x00, 0x00, 0xFF, 0x80, 0x00, 0x00, 0x3F, 0xE0, 0x00, 0x00, + 0x0F, 0xF8, 0x00, 0x00, 0x03, 0xFE, 0x00, 0x00, 0x00, 0xFF, 0x80, 0x00, + 0x00, 0x3F, 0xE0, 0x00, 0x00, 0x0F, 0xF8, 0x00, 0x00, 0x03, 0xFF, 0xFF, + 0xFF, 0xF8, 0xFF, 0xFF, 0xFF, 0xFE, 0x3F, 0xFF, 0xFF, 0xFF, 0x8F, 0xFF, + 0xFF, 0xFF, 0xE3, 0xFF, 0xFF, 0xFF, 0xF8, 0xFF, 0xFF, 0xFF, 0xFE, 0x3F, + 0xFF, 0xFF, 0xFF, 0x8F, 0xFF, 0xFF, 0xFF, 0xE3, 0xFE, 0x00, 0x00, 0x00, + 0xFF, 0x80, 0x00, 0x00, 0x3F, 0xE0, 0x00, 0x00, 0x0F, 0xF8, 0x00, 0x00, + 0x03, 0xFE, 0x00, 0x00, 0x00, 0xFF, 0x80, 0x00, 0x00, 0x3F, 0xE0, 0x00, + 0x00, 0x0F, 0xF8, 0x00, 0x00, 0x03, 0xFE, 0x00, 0x00, 0x00, 0xFF, 0x80, + 0x00, 0x00, 0x3F, 0xE0, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x1F, 0xF0, 0x00, 0x00, 0x0F, 0xFE, + 0x00, 0x00, 0x03, 0xFF, 0x80, 0x00, 0x01, 0xFF, 0xF0, 0x00, 0x00, 0xFE, + 0xFE, 0x00, 0x00, 0x3F, 0x3F, 0x80, 0x00, 0x1F, 0xC7, 0xF0, 0x00, 0x0F, + 0xE0, 0xFE, 0x00, 0x07, 0xF0, 0x1F, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3F, 0xFF, 0xFF, 0xFF, 0xEF, 0xFF, 0xFF, 0xFF, 0xFB, 0xFF, 0xFF, 0xFF, + 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xBF, 0xFF, 0xFF, 0xFF, 0xEF, 0xFF, 0xFF, + 0xFF, 0xFB, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xBF, 0xE0, + 0x00, 0x00, 0x0F, 0xF8, 0x00, 0x00, 0x03, 0xFE, 0x00, 0x00, 0x00, 0xFF, + 0x80, 0x00, 0x00, 0x3F, 0xE0, 0x00, 0x00, 0x0F, 0xF8, 0x00, 0x00, 0x03, + 0xFE, 0x00, 0x00, 0x00, 0xFF, 0x80, 0x00, 0x00, 0x3F, 0xE0, 0x00, 0x00, + 0x0F, 0xF8, 0x00, 0x00, 0x03, 0xFF, 0xFF, 0xFF, 0xF8, 0xFF, 0xFF, 0xFF, + 0xFE, 0x3F, 0xFF, 0xFF, 0xFF, 0x8F, 0xFF, 0xFF, 0xFF, 0xE3, 0xFF, 0xFF, + 0xFF, 0xF8, 0xFF, 0xFF, 0xFF, 0xFE, 0x3F, 0xFF, 0xFF, 0xFF, 0x8F, 0xFF, + 0xFF, 0xFF, 0xE3, 0xFE, 0x00, 0x00, 0x00, 0xFF, 0x80, 0x00, 0x00, 0x3F, + 0xE0, 0x00, 0x00, 0x0F, 0xF8, 0x00, 0x00, 0x03, 0xFE, 0x00, 0x00, 0x00, + 0xFF, 0x80, 0x00, 0x00, 0x3F, 0xE0, 0x00, 0x00, 0x0F, 0xF8, 0x00, 0x00, + 0x03, 0xFE, 0x00, 0x00, 0x00, 0xFF, 0x80, 0x00, 0x00, 0x3F, 0xE0, 0x00, + 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, + 0x03, 0xFC, 0x1F, 0xE0, 0x00, 0xFF, 0x07, 0xF8, 0x00, 0x3F, 0xC1, 0xFE, + 0x00, 0x0F, 0xF0, 0x7F, 0x80, 0x03, 0xFC, 0x1F, 0xE0, 0x00, 0xFF, 0x07, + 0xF8, 0x00, 0x3F, 0xC1, 0xFE, 0x00, 0x0F, 0xF0, 0x7F, 0x80, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xBF, 0xFF, 0xFF, 0xFF, 0xEF, + 0xFF, 0xFF, 0xFF, 0xFB, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, + 0xBF, 0xFF, 0xFF, 0xFF, 0xEF, 0xFF, 0xFF, 0xFF, 0xFB, 0xFF, 0xFF, 0xFF, + 0xFE, 0xFF, 0x80, 0x00, 0x00, 0x3F, 0xE0, 0x00, 0x00, 0x0F, 0xF8, 0x00, + 0x00, 0x03, 0xFE, 0x00, 0x00, 0x00, 0xFF, 0x80, 0x00, 0x00, 0x3F, 0xE0, + 0x00, 0x00, 0x0F, 0xF8, 0x00, 0x00, 0x03, 0xFE, 0x00, 0x00, 0x00, 0xFF, + 0x80, 0x00, 0x00, 0x3F, 0xE0, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xE3, + 0xFF, 0xFF, 0xFF, 0xF8, 0xFF, 0xFF, 0xFF, 0xFE, 0x3F, 0xFF, 0xFF, 0xFF, + 0x8F, 0xFF, 0xFF, 0xFF, 0xE3, 0xFF, 0xFF, 0xFF, 0xF8, 0xFF, 0xFF, 0xFF, + 0xFE, 0x3F, 0xFF, 0xFF, 0xFF, 0x8F, 0xF8, 0x00, 0x00, 0x03, 0xFE, 0x00, + 0x00, 0x00, 0xFF, 0x80, 0x00, 0x00, 0x3F, 0xE0, 0x00, 0x00, 0x0F, 0xF8, + 0x00, 0x00, 0x03, 0xFE, 0x00, 0x00, 0x00, 0xFF, 0x80, 0x00, 0x00, 0x3F, + 0xE0, 0x00, 0x00, 0x0F, 0xF8, 0x00, 0x00, 0x03, 0xFE, 0x00, 0x00, 0x00, + 0xFF, 0x80, 0x00, 0x00, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xC0, 0x7F, 0xC0, 0xFF, 0x81, 0xFE, 0x03, 0xFC, 0x07, 0xF0, + 0x1F, 0xE0, 0x3F, 0x80, 0x7F, 0x00, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1F, 0xF0, 0x7F, 0xC1, 0xFF, 0x07, 0xFC, 0x1F, 0xF0, 0x7F, + 0xC1, 0xFF, 0x07, 0xFC, 0x1F, 0xF0, 0x7F, 0xC1, 0xFF, 0x07, 0xFC, 0x1F, + 0xF0, 0x7F, 0xC1, 0xFF, 0x07, 0xFC, 0x1F, 0xF0, 0x7F, 0xC1, 0xFF, 0x07, + 0xFC, 0x1F, 0xF0, 0x7F, 0xC1, 0xFF, 0x07, 0xFC, 0x1F, 0xF0, 0x7F, 0xC1, + 0xFF, 0x07, 0xFC, 0x1F, 0xF0, 0x7F, 0xC1, 0xFF, 0x07, 0xFC, 0x1F, 0xF0, + 0x7F, 0xC1, 0xFF, 0x07, 0xFC, 0x1F, 0xF0, 0x7F, 0xC1, 0xFF, 0x07, 0xFC, + 0x1F, 0xF0, 0x7F, 0xC1, 0xFF, 0x07, 0xFC, 0x1F, 0xF0, 0x0F, 0xF8, 0x7F, + 0xC1, 0xFE, 0x0F, 0xF0, 0x3F, 0x81, 0xFE, 0x07, 0xF0, 0x3F, 0x80, 0xFC, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xFE, 0x0F, 0xF8, 0x3F, + 0xE0, 0xFF, 0x83, 0xFE, 0x0F, 0xF8, 0x3F, 0xE0, 0xFF, 0x83, 0xFE, 0x0F, + 0xF8, 0x3F, 0xE0, 0xFF, 0x83, 0xFE, 0x0F, 0xF8, 0x3F, 0xE0, 0xFF, 0x83, + 0xFE, 0x0F, 0xF8, 0x3F, 0xE0, 0xFF, 0x83, 0xFE, 0x0F, 0xF8, 0x3F, 0xE0, + 0xFF, 0x83, 0xFE, 0x0F, 0xF8, 0x3F, 0xE0, 0xFF, 0x83, 0xFE, 0x0F, 0xF8, + 0x3F, 0xE0, 0xFF, 0x83, 0xFE, 0x0F, 0xF8, 0x3F, 0xE0, 0xFF, 0x83, 0xFE, + 0x0F, 0xF8, 0x3F, 0xE0, 0xFF, 0x83, 0xFE, 0x0F, 0xF8, 0x3F, 0xE0, 0xFF, + 0x83, 0xFE, 0x00, 0x03, 0xFE, 0x00, 0x3F, 0xF8, 0x01, 0xFF, 0xC0, 0x1F, + 0xFF, 0x01, 0xFD, 0xFC, 0x0F, 0xCF, 0xE0, 0xFE, 0x3F, 0x8F, 0xE0, 0xFE, + 0xFE, 0x03, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x03, 0xFE, 0x00, 0x1F, 0xF0, 0x00, 0xFF, 0x80, 0x07, 0xFC, 0x00, + 0x3F, 0xE0, 0x01, 0xFF, 0x00, 0x0F, 0xF8, 0x00, 0x7F, 0xC0, 0x03, 0xFE, + 0x00, 0x1F, 0xF0, 0x00, 0xFF, 0x80, 0x07, 0xFC, 0x00, 0x3F, 0xE0, 0x01, + 0xFF, 0x00, 0x0F, 0xF8, 0x00, 0x7F, 0xC0, 0x03, 0xFE, 0x00, 0x1F, 0xF0, + 0x00, 0xFF, 0x80, 0x07, 0xFC, 0x00, 0x3F, 0xE0, 0x01, 0xFF, 0x00, 0x0F, + 0xF8, 0x00, 0x7F, 0xC0, 0x03, 0xFE, 0x00, 0x1F, 0xF0, 0x00, 0xFF, 0x80, + 0x07, 0xFC, 0x00, 0x3F, 0xE0, 0x01, 0xFF, 0x00, 0x0F, 0xF8, 0x00, 0x7F, + 0xC0, 0x03, 0xFE, 0x00, 0x1F, 0xF0, 0x00, 0xFF, 0x80, 0x07, 0xFC, 0x00, + 0x3F, 0xE0, 0x01, 0xFF, 0x00, 0x0F, 0xF8, 0x00, 0x7F, 0xC0, 0x03, 0xFE, + 0x00, 0x1F, 0xF0, 0x00, 0xFF, 0x80, 0x07, 0xFC, 0x00, 0x3F, 0xE0, 0x00, + 0xFF, 0x07, 0xFF, 0xF8, 0x3F, 0xFF, 0xC1, 0xFF, 0xFE, 0x0F, 0xFF, 0xF0, + 0x7F, 0xFF, 0x83, 0xFF, 0xFC, 0x1F, 0xFF, 0xE0, 0xFF, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xE0, 0x01, 0xFF, + 0x00, 0x0F, 0xF8, 0x00, 0x7F, 0xC0, 0x03, 0xFE, 0x00, 0x1F, 0xF0, 0x00, + 0xFF, 0x80, 0x07, 0xFC, 0x00, 0x3F, 0xE0, 0x01, 0xFF, 0x00, 0x0F, 0xF8, + 0x00, 0x7F, 0xC0, 0x03, 0xFE, 0x00, 0x1F, 0xF0, 0x00, 0xFF, 0x80, 0x07, + 0xFC, 0x00, 0x3F, 0xE0, 0x01, 0xFF, 0x00, 0x0F, 0xF8, 0x00, 0x7F, 0xC0, + 0x03, 0xFE, 0x00, 0x1F, 0xF0, 0x00, 0xFF, 0x80, 0x07, 0xFC, 0x00, 0x3F, + 0xE0, 0x01, 0xFF, 0x00, 0x0F, 0xF8, 0x00, 0x7F, 0xC0, 0x03, 0xFE, 0x00, + 0x1F, 0xF0, 0x00, 0xFF, 0x80, 0x07, 0xFC, 0x00, 0x3F, 0xE0, 0x01, 0xFF, + 0x00, 0x0F, 0xF8, 0x00, 0x7F, 0xC0, 0x03, 0xFE, 0x00, 0x1F, 0xF0, 0x00, + 0xFF, 0x80, 0x07, 0xFC, 0x00, 0x3F, 0xE0, 0x01, 0xFF, 0x00, 0x0F, 0xF8, + 0x00, 0x7F, 0xC0, 0x03, 0xFE, 0x00, 0x07, 0xFF, 0xFF, 0xF0, 0x00, 0x01, + 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x7F, 0xFF, 0xFF, 0xF8, 0x00, 0x1F, 0xFF, + 0xFF, 0xFF, 0x80, 0x07, 0xFF, 0xFF, 0xFF, 0xF0, 0x01, 0xFF, 0xFF, 0xFF, + 0xFE, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xC0, 0x1F, 0xFF, 0xFF, 0xFF, 0xF8, + 0x07, 0xFC, 0x00, 0x3F, 0xFE, 0x01, 0xFF, 0x00, 0x03, 0xFF, 0xC0, 0x7F, + 0xC0, 0x00, 0x7F, 0xF0, 0x1F, 0xF0, 0x00, 0x0F, 0xFE, 0x07, 0xFC, 0x00, + 0x01, 0xFF, 0x81, 0xFF, 0x00, 0x00, 0x3F, 0xE0, 0x7F, 0xC0, 0x00, 0x0F, + 0xFC, 0x1F, 0xF0, 0x00, 0x03, 0xFF, 0x07, 0xFC, 0x00, 0x00, 0xFF, 0xC1, + 0xFF, 0x00, 0x00, 0x1F, 0xF0, 0x7F, 0xC0, 0x00, 0x07, 0xFF, 0xFF, 0xFF, + 0xF8, 0x01, 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0x80, + 0x1F, 0xFF, 0xFF, 0xFF, 0xE0, 0x07, 0xFF, 0xFF, 0xFF, 0xF8, 0x01, 0xFF, + 0xFF, 0xFF, 0xFE, 0x00, 0x7F, 0xC1, 0xFF, 0x00, 0x00, 0x1F, 0xF0, 0x7F, + 0xC0, 0x00, 0x07, 0xFC, 0x1F, 0xF0, 0x00, 0x01, 0xFF, 0x07, 0xFC, 0x00, + 0x00, 0x7F, 0xC1, 0xFF, 0x00, 0x00, 0x3F, 0xF0, 0x7F, 0xC0, 0x00, 0x0F, + 0xFC, 0x1F, 0xF0, 0x00, 0x03, 0xFE, 0x07, 0xFC, 0x00, 0x01, 0xFF, 0x81, + 0xFF, 0x00, 0x00, 0x7F, 0xE0, 0x7F, 0xC0, 0x00, 0x3F, 0xF0, 0x1F, 0xF0, + 0x00, 0x1F, 0xFC, 0x07, 0xFC, 0x00, 0x3F, 0xFE, 0x01, 0xFF, 0xFF, 0xFF, + 0xFF, 0x80, 0x7F, 0xFF, 0xFF, 0xFF, 0xC0, 0x1F, 0xFF, 0xFF, 0xFF, 0xE0, + 0x07, 0xFF, 0xFF, 0xFF, 0xF0, 0x01, 0xFF, 0xFF, 0xFF, 0xF8, 0x00, 0x7F, + 0xFF, 0xFF, 0xF8, 0x00, 0x1F, 0xFF, 0xFF, 0xF8, 0x00, 0x07, 0xFF, 0xFF, + 0xF0, 0x00, 0x00, 0x00, 0x3E, 0x00, 0xF0, 0x00, 0x1F, 0xF0, 0x1E, 0x00, + 0x03, 0xFF, 0x87, 0xC0, 0x00, 0xFF, 0xFF, 0xF8, 0x00, 0x1F, 0xFF, 0xFE, + 0x00, 0x03, 0xC3, 0xFF, 0xC0, 0x00, 0x78, 0x1F, 0xF0, 0x00, 0x0F, 0x00, + 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xF8, 0x00, 0x01, 0xFF, + 0xFF, 0x00, 0x00, 0x3F, 0xFF, 0xF0, 0x00, 0x07, 0xFF, 0xFF, 0x00, 0x00, + 0xFF, 0xFF, 0xE0, 0x00, 0x1F, 0xFF, 0xFE, 0x00, 0x03, 0xFF, 0xFF, 0xE0, + 0x00, 0x7F, 0xFF, 0xFC, 0x00, 0x0F, 0xFF, 0xFF, 0xC0, 0x01, 0xFF, 0xFF, + 0xFC, 0x00, 0x3F, 0xFF, 0xFF, 0x80, 0x07, 0xFF, 0xFF, 0xF8, 0x00, 0xFF, + 0xFF, 0xFF, 0x00, 0x1F, 0xFF, 0xFF, 0xF0, 0x03, 0xFF, 0xFF, 0xFF, 0x00, + 0x7F, 0xFF, 0xFF, 0xE0, 0x0F, 0xFF, 0xFF, 0xFE, 0x01, 0xFF, 0xFE, 0xFF, + 0xE0, 0x3F, 0xFF, 0xCF, 0xFC, 0x07, 0xFF, 0xF9, 0xFF, 0xC0, 0xFF, 0xFF, + 0x1F, 0xF8, 0x1F, 0xFF, 0xE1, 0xFF, 0x83, 0xFF, 0xFC, 0x3F, 0xF8, 0x7F, + 0xFF, 0x83, 0xFF, 0x0F, 0xFF, 0xF0, 0x7F, 0xF1, 0xFF, 0xFE, 0x07, 0xFF, + 0x3F, 0xFF, 0xC0, 0x7F, 0xE7, 0xFF, 0xF8, 0x0F, 0xFE, 0xFF, 0xFF, 0x00, + 0xFF, 0xDF, 0xFF, 0xE0, 0x0F, 0xFF, 0xFF, 0xFC, 0x01, 0xFF, 0xFF, 0xFF, + 0x80, 0x1F, 0xFF, 0xFF, 0xF0, 0x03, 0xFF, 0xFF, 0xFE, 0x00, 0x3F, 0xFF, + 0xFF, 0xC0, 0x03, 0xFF, 0xFF, 0xF8, 0x00, 0x7F, 0xFF, 0xFF, 0x00, 0x07, + 0xFF, 0xFF, 0xE0, 0x00, 0x7F, 0xFF, 0xFC, 0x00, 0x0F, 0xFF, 0xFF, 0x80, + 0x00, 0xFF, 0xFF, 0xF0, 0x00, 0x1F, 0xFF, 0xFE, 0x00, 0x01, 0xFF, 0xFF, + 0xC0, 0x00, 0x1F, 0xFF, 0xF8, 0x00, 0x03, 0xFF, 0xFF, 0x00, 0x00, 0x3F, + 0xE0, 0x00, 0x07, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xC0, 0x00, 0x00, + 0x00, 0x07, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x80, 0x00, 0x00, 0x00, + 0x07, 0xF0, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x0F, + 0xE0, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xC0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFE, 0x00, 0x00, 0x00, + 0x0F, 0xFF, 0xF8, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xE0, 0x00, 0x03, 0xFF, + 0xFF, 0xFE, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x7F, 0xFF, 0xFF, + 0xFF, 0x00, 0x1F, 0xFF, 0xFF, 0xFF, 0xF0, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, + 0x00, 0xFF, 0xFC, 0x07, 0xFF, 0xE0, 0x3F, 0xFE, 0x00, 0x1F, 0xFE, 0x07, + 0xFF, 0x00, 0x01, 0xFF, 0xE1, 0xFF, 0xC0, 0x00, 0x1F, 0xFC, 0x3F, 0xF0, + 0x00, 0x01, 0xFF, 0xCF, 0xFC, 0x00, 0x00, 0x1F, 0xF9, 0xFF, 0x80, 0x00, + 0x03, 0xFF, 0x3F, 0xE0, 0x00, 0x00, 0x3F, 0xFF, 0xFC, 0x00, 0x00, 0x07, + 0xFF, 0xFF, 0x80, 0x00, 0x00, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x0F, 0xFF, + 0xFC, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x3F, 0xFF, 0xF0, + 0x00, 0x00, 0x07, 0xFF, 0xFE, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xC0, 0x00, + 0x00, 0x1F, 0xFF, 0xF8, 0x00, 0x00, 0x03, 0xFF, 0xFF, 0x00, 0x00, 0x00, + 0x7F, 0xFF, 0xE0, 0x00, 0x00, 0x0F, 0xFF, 0xFC, 0x00, 0x00, 0x01, 0xFF, + 0xFF, 0xC0, 0x00, 0x00, 0x3F, 0xFF, 0xF8, 0x00, 0x00, 0x0F, 0xFF, 0xFF, + 0x00, 0x00, 0x01, 0xFF, 0xBF, 0xE0, 0x00, 0x00, 0x3F, 0xF7, 0xFE, 0x00, + 0x00, 0x0F, 0xFC, 0xFF, 0xC0, 0x00, 0x01, 0xFF, 0x8F, 0xFC, 0x00, 0x00, + 0x7F, 0xF1, 0xFF, 0xC0, 0x00, 0x1F, 0xFC, 0x1F, 0xFC, 0x00, 0x07, 0xFF, + 0x83, 0xFF, 0xE0, 0x01, 0xFF, 0xE0, 0x3F, 0xFF, 0x01, 0xFF, 0xF8, 0x07, + 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xC0, 0x07, 0xFF, + 0xFF, 0xFF, 0xF0, 0x00, 0x7F, 0xFF, 0xFF, 0xFC, 0x00, 0x03, 0xFF, 0xFF, + 0xFE, 0x00, 0x00, 0x1F, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0xFF, 0xFF, 0x80, + 0x00, 0x00, 0x03, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xF8, 0x00, + 0x00, 0x00, 0x03, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x80, 0x00, 0x00, + 0x00, 0x1F, 0xE0, 0x00, 0x00, 0x00, 0x03, 0xF8, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xC0, 0x00, 0x00, 0x00, 0x07, 0xF0, + 0x00, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0F, 0xFE, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xF8, 0x00, 0x00, 0x0F, + 0xFF, 0xFF, 0xE0, 0x00, 0x03, 0xFF, 0xFF, 0xFE, 0x00, 0x01, 0xFF, 0xFF, + 0xFF, 0xF0, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0x00, 0x1F, 0xFF, 0xFF, 0xFF, + 0xF0, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFC, 0x07, 0xFF, 0xE0, + 0x3F, 0xFE, 0x00, 0x1F, 0xFE, 0x07, 0xFF, 0x00, 0x01, 0xFF, 0xE1, 0xFF, + 0xC0, 0x00, 0x1F, 0xFC, 0x3F, 0xF0, 0x00, 0x01, 0xFF, 0xCF, 0xFC, 0x00, + 0x00, 0x1F, 0xF9, 0xFF, 0x80, 0x00, 0x03, 0xFF, 0x3F, 0xE0, 0x00, 0x00, + 0x3F, 0xFF, 0xFC, 0x00, 0x00, 0x07, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0xFF, + 0xFF, 0xF0, 0x00, 0x00, 0x0F, 0xFF, 0xFC, 0x00, 0x00, 0x01, 0xFF, 0xFF, + 0x80, 0x00, 0x00, 0x3F, 0xFF, 0xF0, 0x00, 0x00, 0x07, 0xFF, 0xFE, 0x00, + 0x00, 0x00, 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x1F, 0xFF, 0xF8, 0x00, 0x00, + 0x03, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xE0, 0x00, 0x00, 0x0F, + 0xFF, 0xFC, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x3F, 0xFF, + 0xF8, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0x00, 0x00, 0x01, 0xFF, 0xBF, 0xE0, + 0x00, 0x00, 0x3F, 0xF7, 0xFE, 0x00, 0x00, 0x0F, 0xFC, 0xFF, 0xC0, 0x00, + 0x01, 0xFF, 0x8F, 0xFC, 0x00, 0x00, 0x7F, 0xF1, 0xFF, 0xC0, 0x00, 0x1F, + 0xFC, 0x1F, 0xFC, 0x00, 0x07, 0xFF, 0x83, 0xFF, 0xE0, 0x01, 0xFF, 0xE0, + 0x3F, 0xFF, 0x01, 0xFF, 0xF8, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x7F, + 0xFF, 0xFF, 0xFF, 0xC0, 0x07, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x7F, 0xFF, + 0xFF, 0xFC, 0x00, 0x03, 0xFF, 0xFF, 0xFE, 0x00, 0x00, 0x1F, 0xFF, 0xFF, + 0x80, 0x00, 0x00, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x03, 0xFF, 0x80, 0x00, + 0x00, 0x00, 0x00, 0x7F, 0xC0, 0x00, 0x00, 0x00, 0x1F, 0xFC, 0x00, 0x00, + 0x00, 0x03, 0xFF, 0x80, 0x00, 0x00, 0x00, 0xFF, 0xF8, 0x00, 0x00, 0x00, + 0x3F, 0xBF, 0x80, 0x00, 0x00, 0x07, 0xE7, 0xF0, 0x00, 0x00, 0x01, 0xFC, + 0x7F, 0x00, 0x00, 0x00, 0x7F, 0x07, 0xF0, 0x00, 0x00, 0x1F, 0xC0, 0x7F, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFE, 0x00, 0x00, 0x00, + 0x0F, 0xFF, 0xF8, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xE0, 0x00, 0x03, 0xFF, + 0xFF, 0xFE, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x7F, 0xFF, 0xFF, + 0xFF, 0x00, 0x1F, 0xFF, 0xFF, 0xFF, 0xF0, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, + 0x00, 0xFF, 0xFC, 0x07, 0xFF, 0xE0, 0x3F, 0xFE, 0x00, 0x1F, 0xFE, 0x07, + 0xFF, 0x00, 0x01, 0xFF, 0xE1, 0xFF, 0xC0, 0x00, 0x1F, 0xFC, 0x3F, 0xF0, + 0x00, 0x01, 0xFF, 0xCF, 0xFC, 0x00, 0x00, 0x1F, 0xF9, 0xFF, 0x80, 0x00, + 0x03, 0xFF, 0x3F, 0xE0, 0x00, 0x00, 0x3F, 0xFF, 0xFC, 0x00, 0x00, 0x07, + 0xFF, 0xFF, 0x80, 0x00, 0x00, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x0F, 0xFF, + 0xFC, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x3F, 0xFF, 0xF0, + 0x00, 0x00, 0x07, 0xFF, 0xFE, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xC0, 0x00, + 0x00, 0x1F, 0xFF, 0xF8, 0x00, 0x00, 0x03, 0xFF, 0xFF, 0x00, 0x00, 0x00, + 0x7F, 0xFF, 0xE0, 0x00, 0x00, 0x0F, 0xFF, 0xFC, 0x00, 0x00, 0x01, 0xFF, + 0xFF, 0xC0, 0x00, 0x00, 0x3F, 0xFF, 0xF8, 0x00, 0x00, 0x0F, 0xFF, 0xFF, + 0x00, 0x00, 0x01, 0xFF, 0xBF, 0xE0, 0x00, 0x00, 0x3F, 0xF7, 0xFE, 0x00, + 0x00, 0x0F, 0xFC, 0xFF, 0xC0, 0x00, 0x01, 0xFF, 0x8F, 0xFC, 0x00, 0x00, + 0x7F, 0xF1, 0xFF, 0xC0, 0x00, 0x1F, 0xFC, 0x1F, 0xFC, 0x00, 0x07, 0xFF, + 0x83, 0xFF, 0xE0, 0x01, 0xFF, 0xE0, 0x3F, 0xFF, 0x01, 0xFF, 0xF8, 0x07, + 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xC0, 0x07, 0xFF, + 0xFF, 0xFF, 0xF0, 0x00, 0x7F, 0xFF, 0xFF, 0xFC, 0x00, 0x03, 0xFF, 0xFF, + 0xFE, 0x00, 0x00, 0x1F, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0xFF, 0xFF, 0x80, + 0x00, 0x00, 0x03, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x03, 0xE0, 0x0F, 0x00, + 0x00, 0x01, 0xFF, 0x01, 0xE0, 0x00, 0x00, 0x3F, 0xF8, 0x7C, 0x00, 0x00, + 0x0F, 0xFF, 0xFF, 0x80, 0x00, 0x01, 0xFF, 0xFF, 0xE0, 0x00, 0x00, 0x3C, + 0x3F, 0xFC, 0x00, 0x00, 0x07, 0x81, 0xFF, 0x00, 0x00, 0x00, 0xF0, 0x07, + 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xF0, 0x00, 0x00, + 0x00, 0x7F, 0xFF, 0xC0, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0x00, 0x00, 0x1F, + 0xFF, 0xFF, 0xF0, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0x80, 0x03, 0xFF, 0xFF, + 0xFF, 0xF8, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x1F, 0xFF, 0xFF, 0xFF, + 0xF8, 0x07, 0xFF, 0xE0, 0x3F, 0xFF, 0x01, 0xFF, 0xF0, 0x00, 0xFF, 0xF0, + 0x3F, 0xF8, 0x00, 0x0F, 0xFF, 0x0F, 0xFE, 0x00, 0x00, 0xFF, 0xE1, 0xFF, + 0x80, 0x00, 0x0F, 0xFE, 0x7F, 0xE0, 0x00, 0x00, 0xFF, 0xCF, 0xFC, 0x00, + 0x00, 0x1F, 0xF9, 0xFF, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0xE0, 0x00, 0x00, + 0x3F, 0xFF, 0xFC, 0x00, 0x00, 0x07, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x7F, + 0xFF, 0xE0, 0x00, 0x00, 0x0F, 0xFF, 0xFC, 0x00, 0x00, 0x01, 0xFF, 0xFF, + 0x80, 0x00, 0x00, 0x3F, 0xFF, 0xF0, 0x00, 0x00, 0x07, 0xFF, 0xFE, 0x00, + 0x00, 0x00, 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x1F, 0xFF, 0xF8, 0x00, 0x00, + 0x03, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xE0, 0x00, 0x00, 0x0F, + 0xFF, 0xFE, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x7F, 0xFF, + 0xF8, 0x00, 0x00, 0x0F, 0xFD, 0xFF, 0x00, 0x00, 0x01, 0xFF, 0xBF, 0xF0, + 0x00, 0x00, 0x7F, 0xE7, 0xFE, 0x00, 0x00, 0x0F, 0xFC, 0x7F, 0xE0, 0x00, + 0x03, 0xFF, 0x8F, 0xFE, 0x00, 0x00, 0xFF, 0xE0, 0xFF, 0xE0, 0x00, 0x3F, + 0xFC, 0x1F, 0xFF, 0x00, 0x0F, 0xFF, 0x01, 0xFF, 0xF8, 0x0F, 0xFF, 0xC0, + 0x3F, 0xFF, 0xFF, 0xFF, 0xF8, 0x03, 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0x3F, + 0xFF, 0xFF, 0xFF, 0x80, 0x03, 0xFF, 0xFF, 0xFF, 0xE0, 0x00, 0x1F, 0xFF, + 0xFF, 0xF0, 0x00, 0x00, 0xFF, 0xFF, 0xFC, 0x00, 0x00, 0x07, 0xFF, 0xFC, + 0x00, 0x00, 0x00, 0x1F, 0xFC, 0x00, 0x00, 0x00, 0x1F, 0xE0, 0xFF, 0x00, + 0x00, 0x03, 0xFC, 0x1F, 0xE0, 0x00, 0x00, 0x7F, 0x83, 0xFC, 0x00, 0x00, + 0x0F, 0xF0, 0x7F, 0x80, 0x00, 0x01, 0xFE, 0x0F, 0xF0, 0x00, 0x00, 0x3F, + 0xC1, 0xFE, 0x00, 0x00, 0x07, 0xF8, 0x3F, 0xC0, 0x00, 0x00, 0xFF, 0x07, + 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xF0, 0x00, 0x00, + 0x00, 0x7F, 0xFF, 0xC0, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0x00, 0x00, 0x1F, + 0xFF, 0xFF, 0xF0, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0x80, 0x03, 0xFF, 0xFF, + 0xFF, 0xF8, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x1F, 0xFF, 0xFF, 0xFF, + 0xF8, 0x07, 0xFF, 0xE0, 0x3F, 0xFF, 0x01, 0xFF, 0xF0, 0x00, 0xFF, 0xF0, + 0x3F, 0xF8, 0x00, 0x0F, 0xFF, 0x0F, 0xFE, 0x00, 0x00, 0xFF, 0xE1, 0xFF, + 0x80, 0x00, 0x0F, 0xFE, 0x7F, 0xE0, 0x00, 0x00, 0xFF, 0xCF, 0xFC, 0x00, + 0x00, 0x1F, 0xF9, 0xFF, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0xE0, 0x00, 0x00, + 0x3F, 0xFF, 0xFC, 0x00, 0x00, 0x07, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x7F, + 0xFF, 0xE0, 0x00, 0x00, 0x0F, 0xFF, 0xFC, 0x00, 0x00, 0x01, 0xFF, 0xFF, + 0x80, 0x00, 0x00, 0x3F, 0xFF, 0xF0, 0x00, 0x00, 0x07, 0xFF, 0xFE, 0x00, + 0x00, 0x00, 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x1F, 0xFF, 0xF8, 0x00, 0x00, + 0x03, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xE0, 0x00, 0x00, 0x0F, + 0xFF, 0xFE, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x7F, 0xFF, + 0xF8, 0x00, 0x00, 0x0F, 0xFD, 0xFF, 0x00, 0x00, 0x01, 0xFF, 0xBF, 0xF0, + 0x00, 0x00, 0x7F, 0xE7, 0xFE, 0x00, 0x00, 0x0F, 0xFC, 0x7F, 0xE0, 0x00, + 0x03, 0xFF, 0x8F, 0xFE, 0x00, 0x00, 0xFF, 0xE0, 0xFF, 0xE0, 0x00, 0x3F, + 0xFC, 0x1F, 0xFF, 0x00, 0x0F, 0xFF, 0x01, 0xFF, 0xF8, 0x0F, 0xFF, 0xC0, + 0x3F, 0xFF, 0xFF, 0xFF, 0xF8, 0x03, 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0x3F, + 0xFF, 0xFF, 0xFF, 0x80, 0x03, 0xFF, 0xFF, 0xFF, 0xE0, 0x00, 0x1F, 0xFF, + 0xFF, 0xF0, 0x00, 0x00, 0xFF, 0xFF, 0xFC, 0x00, 0x00, 0x07, 0xFF, 0xFC, + 0x00, 0x00, 0x00, 0x1F, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x18, + 0x00, 0x07, 0x00, 0xF0, 0x00, 0x3E, 0x0F, 0xE0, 0x01, 0xFC, 0x7F, 0xC0, + 0x0F, 0xFB, 0xFF, 0x80, 0x7F, 0xF7, 0xFF, 0x03, 0xFF, 0x8F, 0xFE, 0x1F, + 0xFC, 0x1F, 0xFC, 0xFF, 0xE0, 0x3F, 0xFF, 0xFF, 0x00, 0x7F, 0xFF, 0xF8, + 0x00, 0xFF, 0xFF, 0xC0, 0x01, 0xFF, 0xFE, 0x00, 0x03, 0xFF, 0xF0, 0x00, + 0x07, 0xFF, 0x80, 0x00, 0x1F, 0xFE, 0x00, 0x00, 0xFF, 0xFC, 0x00, 0x07, + 0xFF, 0xF8, 0x00, 0x3F, 0xFF, 0xF0, 0x01, 0xFF, 0xFF, 0xE0, 0x0F, 0xFF, + 0xFF, 0xC0, 0x7F, 0xF3, 0xFF, 0x83, 0xFF, 0x87, 0xFF, 0x0F, 0xFC, 0x0F, + 0xFE, 0x7F, 0xE0, 0x1F, 0xFD, 0xFF, 0x00, 0x3F, 0xF3, 0xF8, 0x00, 0x7F, + 0x07, 0xC0, 0x00, 0xF8, 0x0E, 0x00, 0x01, 0xC0, 0x10, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, + 0x00, 0x1F, 0xFC, 0x00, 0x78, 0x00, 0x07, 0xFF, 0xFC, 0x07, 0xE0, 0x01, + 0xFF, 0xFF, 0xFC, 0x7F, 0x80, 0x1F, 0xFF, 0xFF, 0xF3, 0xF8, 0x03, 0xFF, + 0xFF, 0xFF, 0xFF, 0x80, 0x3F, 0xFF, 0xFF, 0xFF, 0xFC, 0x03, 0xFF, 0xFF, + 0xFF, 0xFF, 0xC0, 0x3F, 0xFF, 0xFF, 0xFF, 0xFC, 0x01, 0xFF, 0xF8, 0x0F, + 0xFF, 0xC0, 0x1F, 0xFE, 0x00, 0x0F, 0xFF, 0x01, 0xFF, 0xE0, 0x00, 0x3F, + 0xF8, 0x0F, 0xFE, 0x00, 0x01, 0xFF, 0xE0, 0x7F, 0xE0, 0x00, 0x1F, 0xFF, + 0x07, 0xFE, 0x00, 0x01, 0xFF, 0xFC, 0x3F, 0xF0, 0x00, 0x1F, 0xFF, 0xE1, + 0xFF, 0x00, 0x01, 0xFF, 0xFF, 0x1F, 0xF8, 0x00, 0x1F, 0xEF, 0xFC, 0xFF, + 0xC0, 0x01, 0xFE, 0x7F, 0xE7, 0xFC, 0x00, 0x1F, 0xE1, 0xFF, 0x3F, 0xE0, + 0x01, 0xFE, 0x0F, 0xF9, 0xFF, 0x00, 0x1F, 0xE0, 0x7F, 0xCF, 0xF8, 0x00, + 0xFE, 0x03, 0xFE, 0x7F, 0xC0, 0x0F, 0xE0, 0x1F, 0xF3, 0xFE, 0x00, 0xFF, + 0x00, 0xFF, 0x9F, 0xF0, 0x0F, 0xF0, 0x07, 0xFC, 0xFF, 0x80, 0xFF, 0x00, + 0x3F, 0xE7, 0xFC, 0x0F, 0xF0, 0x01, 0xFF, 0x3F, 0xE0, 0xFF, 0x00, 0x0F, + 0xF9, 0xFF, 0x8F, 0xF0, 0x00, 0x7F, 0xCF, 0xFC, 0xFF, 0x00, 0x07, 0xFE, + 0x7F, 0xEF, 0xF0, 0x00, 0x3F, 0xF1, 0xFF, 0x7F, 0x00, 0x01, 0xFF, 0x8F, + 0xFF, 0xF0, 0x00, 0x1F, 0xF8, 0x7F, 0xFF, 0x80, 0x00, 0xFF, 0xC1, 0xFF, + 0xF8, 0x00, 0x0F, 0xFE, 0x0F, 0xFF, 0x80, 0x00, 0xFF, 0xE0, 0x3F, 0xF8, + 0x00, 0x0F, 0xFF, 0x01, 0xFF, 0xE0, 0x00, 0xFF, 0xF0, 0x07, 0xFF, 0xE0, + 0x3F, 0xFF, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xF8, 0x03, 0xFF, 0xFF, 0xFF, + 0xFF, 0x80, 0x3F, 0xFF, 0xFF, 0xFF, 0xF8, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, + 0x80, 0x3F, 0xDF, 0xFF, 0xFF, 0xF0, 0x03, 0xFC, 0x3F, 0xFF, 0xFF, 0x00, + 0x0F, 0xC0, 0x7F, 0xFF, 0xE0, 0x00, 0x3C, 0x00, 0x7F, 0xF0, 0x00, 0x00, + 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xC0, 0x00, 0x00, 0x07, + 0xFC, 0x00, 0x00, 0x00, 0x7F, 0x80, 0x00, 0x00, 0x07, 0xF8, 0x00, 0x00, + 0x00, 0x7F, 0x00, 0x00, 0x00, 0x0F, 0xF0, 0x00, 0x00, 0x00, 0xFE, 0x00, + 0x00, 0x00, 0x0F, 0xE0, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0xFF, 0x00, 0x00, 0x7F, 0xFF, 0xE0, 0x00, 0x0F, 0xFF, + 0xFC, 0x00, 0x01, 0xFF, 0xFF, 0x80, 0x00, 0x3F, 0xFF, 0xF0, 0x00, 0x07, + 0xFF, 0xFE, 0x00, 0x00, 0xFF, 0xFF, 0xC0, 0x00, 0x1F, 0xFF, 0xF8, 0x00, + 0x03, 0xFF, 0xFF, 0x00, 0x00, 0x7F, 0xFF, 0xE0, 0x00, 0x0F, 0xFF, 0xFC, + 0x00, 0x01, 0xFF, 0xFF, 0x80, 0x00, 0x3F, 0xFF, 0xF0, 0x00, 0x07, 0xFF, + 0xFE, 0x00, 0x00, 0xFF, 0xFF, 0xC0, 0x00, 0x1F, 0xFF, 0xF8, 0x00, 0x03, + 0xFF, 0xFF, 0x00, 0x00, 0x7F, 0xFF, 0xE0, 0x00, 0x0F, 0xFF, 0xFC, 0x00, + 0x01, 0xFF, 0xFF, 0x80, 0x00, 0x3F, 0xFF, 0xF0, 0x00, 0x07, 0xFF, 0xFE, + 0x00, 0x00, 0xFF, 0xFF, 0xC0, 0x00, 0x1F, 0xFF, 0xF8, 0x00, 0x03, 0xFF, + 0xFF, 0x00, 0x00, 0x7F, 0xFF, 0xE0, 0x00, 0x0F, 0xFF, 0xFC, 0x00, 0x01, + 0xFF, 0xFF, 0x80, 0x00, 0x3F, 0xFF, 0xF0, 0x00, 0x07, 0xFF, 0xFE, 0x00, + 0x00, 0xFF, 0xFF, 0xC0, 0x00, 0x1F, 0xFF, 0xF8, 0x00, 0x03, 0xFF, 0xFF, + 0x00, 0x00, 0x7F, 0xFF, 0xE0, 0x00, 0x0F, 0xFF, 0xFE, 0x00, 0x03, 0xFF, + 0xFF, 0xE0, 0x00, 0x7F, 0xEF, 0xFE, 0x00, 0x3F, 0xF9, 0xFF, 0xF0, 0x1F, + 0xFF, 0x1F, 0xFF, 0xFF, 0xFF, 0xE3, 0xFF, 0xFF, 0xFF, 0xF8, 0x3F, 0xFF, + 0xFF, 0xFE, 0x03, 0xFF, 0xFF, 0xFF, 0xC0, 0x3F, 0xFF, 0xFF, 0xE0, 0x03, + 0xFF, 0xFF, 0xF8, 0x00, 0x1F, 0xFF, 0xFC, 0x00, 0x00, 0x3F, 0xFC, 0x00, + 0x00, 0x00, 0x00, 0xFF, 0x80, 0x00, 0x00, 0x3F, 0xE0, 0x00, 0x00, 0x07, + 0xF8, 0x00, 0x00, 0x01, 0xFE, 0x00, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, + 0x0F, 0xF0, 0x00, 0x00, 0x01, 0xFC, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, + 0x00, 0x0F, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, 0x00, + 0x00, 0x7F, 0xFF, 0xE0, 0x00, 0x0F, 0xFF, 0xFC, 0x00, 0x01, 0xFF, 0xFF, + 0x80, 0x00, 0x3F, 0xFF, 0xF0, 0x00, 0x07, 0xFF, 0xFE, 0x00, 0x00, 0xFF, + 0xFF, 0xC0, 0x00, 0x1F, 0xFF, 0xF8, 0x00, 0x03, 0xFF, 0xFF, 0x00, 0x00, + 0x7F, 0xFF, 0xE0, 0x00, 0x0F, 0xFF, 0xFC, 0x00, 0x01, 0xFF, 0xFF, 0x80, + 0x00, 0x3F, 0xFF, 0xF0, 0x00, 0x07, 0xFF, 0xFE, 0x00, 0x00, 0xFF, 0xFF, + 0xC0, 0x00, 0x1F, 0xFF, 0xF8, 0x00, 0x03, 0xFF, 0xFF, 0x00, 0x00, 0x7F, + 0xFF, 0xE0, 0x00, 0x0F, 0xFF, 0xFC, 0x00, 0x01, 0xFF, 0xFF, 0x80, 0x00, + 0x3F, 0xFF, 0xF0, 0x00, 0x07, 0xFF, 0xFE, 0x00, 0x00, 0xFF, 0xFF, 0xC0, + 0x00, 0x1F, 0xFF, 0xF8, 0x00, 0x03, 0xFF, 0xFF, 0x00, 0x00, 0x7F, 0xFF, + 0xE0, 0x00, 0x0F, 0xFF, 0xFC, 0x00, 0x01, 0xFF, 0xFF, 0x80, 0x00, 0x3F, + 0xFF, 0xF0, 0x00, 0x07, 0xFF, 0xFE, 0x00, 0x00, 0xFF, 0xFF, 0xC0, 0x00, + 0x1F, 0xFF, 0xF8, 0x00, 0x03, 0xFF, 0xFF, 0x00, 0x00, 0x7F, 0xFF, 0xE0, + 0x00, 0x0F, 0xFF, 0xFE, 0x00, 0x03, 0xFF, 0xFF, 0xE0, 0x00, 0x7F, 0xEF, + 0xFE, 0x00, 0x3F, 0xF9, 0xFF, 0xF0, 0x1F, 0xFF, 0x1F, 0xFF, 0xFF, 0xFF, + 0xE3, 0xFF, 0xFF, 0xFF, 0xF8, 0x3F, 0xFF, 0xFF, 0xFE, 0x03, 0xFF, 0xFF, + 0xFF, 0xC0, 0x3F, 0xFF, 0xFF, 0xE0, 0x03, 0xFF, 0xFF, 0xF8, 0x00, 0x1F, + 0xFF, 0xFC, 0x00, 0x00, 0x3F, 0xFC, 0x00, 0x00, 0x00, 0x07, 0xFC, 0x00, + 0x00, 0x01, 0xFF, 0xC0, 0x00, 0x00, 0x3F, 0xF8, 0x00, 0x00, 0x0F, 0xFF, + 0x80, 0x00, 0x03, 0xFB, 0xF8, 0x00, 0x00, 0x7E, 0x7F, 0x00, 0x00, 0x1F, + 0xC7, 0xF0, 0x00, 0x07, 0xF0, 0x7F, 0x00, 0x01, 0xFC, 0x07, 0xF0, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, 0x00, 0x00, 0x7F, 0xFF, 0xE0, 0x00, + 0x0F, 0xFF, 0xFC, 0x00, 0x01, 0xFF, 0xFF, 0x80, 0x00, 0x3F, 0xFF, 0xF0, + 0x00, 0x07, 0xFF, 0xFE, 0x00, 0x00, 0xFF, 0xFF, 0xC0, 0x00, 0x1F, 0xFF, + 0xF8, 0x00, 0x03, 0xFF, 0xFF, 0x00, 0x00, 0x7F, 0xFF, 0xE0, 0x00, 0x0F, + 0xFF, 0xFC, 0x00, 0x01, 0xFF, 0xFF, 0x80, 0x00, 0x3F, 0xFF, 0xF0, 0x00, + 0x07, 0xFF, 0xFE, 0x00, 0x00, 0xFF, 0xFF, 0xC0, 0x00, 0x1F, 0xFF, 0xF8, + 0x00, 0x03, 0xFF, 0xFF, 0x00, 0x00, 0x7F, 0xFF, 0xE0, 0x00, 0x0F, 0xFF, + 0xFC, 0x00, 0x01, 0xFF, 0xFF, 0x80, 0x00, 0x3F, 0xFF, 0xF0, 0x00, 0x07, + 0xFF, 0xFE, 0x00, 0x00, 0xFF, 0xFF, 0xC0, 0x00, 0x1F, 0xFF, 0xF8, 0x00, + 0x03, 0xFF, 0xFF, 0x00, 0x00, 0x7F, 0xFF, 0xE0, 0x00, 0x0F, 0xFF, 0xFC, + 0x00, 0x01, 0xFF, 0xFF, 0x80, 0x00, 0x3F, 0xFF, 0xF0, 0x00, 0x07, 0xFF, + 0xFE, 0x00, 0x00, 0xFF, 0xFF, 0xC0, 0x00, 0x1F, 0xFF, 0xF8, 0x00, 0x03, + 0xFF, 0xFF, 0x00, 0x00, 0x7F, 0xFF, 0xE0, 0x00, 0x0F, 0xFF, 0xFE, 0x00, + 0x03, 0xFF, 0xFF, 0xE0, 0x00, 0x7F, 0xEF, 0xFE, 0x00, 0x3F, 0xF9, 0xFF, + 0xF0, 0x1F, 0xFF, 0x1F, 0xFF, 0xFF, 0xFF, 0xE3, 0xFF, 0xFF, 0xFF, 0xF8, + 0x3F, 0xFF, 0xFF, 0xFE, 0x03, 0xFF, 0xFF, 0xFF, 0xC0, 0x3F, 0xFF, 0xFF, + 0xE0, 0x03, 0xFF, 0xFF, 0xF8, 0x00, 0x1F, 0xFF, 0xFC, 0x00, 0x00, 0x3F, + 0xFC, 0x00, 0x00, 0x01, 0xFE, 0x0F, 0xF0, 0x00, 0x3F, 0xC1, 0xFE, 0x00, + 0x07, 0xF8, 0x3F, 0xC0, 0x00, 0xFF, 0x07, 0xF8, 0x00, 0x1F, 0xE0, 0xFF, + 0x00, 0x03, 0xFC, 0x1F, 0xE0, 0x00, 0x7F, 0x83, 0xFC, 0x00, 0x0F, 0xF0, + 0x7F, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xF8, 0x00, 0x03, 0xFF, + 0xFF, 0x00, 0x00, 0x7F, 0xFF, 0xE0, 0x00, 0x0F, 0xFF, 0xFC, 0x00, 0x01, + 0xFF, 0xFF, 0x80, 0x00, 0x3F, 0xFF, 0xF0, 0x00, 0x07, 0xFF, 0xFE, 0x00, + 0x00, 0xFF, 0xFF, 0xC0, 0x00, 0x1F, 0xFF, 0xF8, 0x00, 0x03, 0xFF, 0xFF, + 0x00, 0x00, 0x7F, 0xFF, 0xE0, 0x00, 0x0F, 0xFF, 0xFC, 0x00, 0x01, 0xFF, + 0xFF, 0x80, 0x00, 0x3F, 0xFF, 0xF0, 0x00, 0x07, 0xFF, 0xFE, 0x00, 0x00, + 0xFF, 0xFF, 0xC0, 0x00, 0x1F, 0xFF, 0xF8, 0x00, 0x03, 0xFF, 0xFF, 0x00, + 0x00, 0x7F, 0xFF, 0xE0, 0x00, 0x0F, 0xFF, 0xFC, 0x00, 0x01, 0xFF, 0xFF, + 0x80, 0x00, 0x3F, 0xFF, 0xF0, 0x00, 0x07, 0xFF, 0xFE, 0x00, 0x00, 0xFF, + 0xFF, 0xC0, 0x00, 0x1F, 0xFF, 0xF8, 0x00, 0x03, 0xFF, 0xFF, 0x00, 0x00, + 0x7F, 0xFF, 0xE0, 0x00, 0x0F, 0xFF, 0xFC, 0x00, 0x01, 0xFF, 0xFF, 0x80, + 0x00, 0x3F, 0xFF, 0xF0, 0x00, 0x07, 0xFF, 0xFE, 0x00, 0x00, 0xFF, 0xFF, + 0xC0, 0x00, 0x1F, 0xFF, 0xF8, 0x00, 0x03, 0xFF, 0xFF, 0x00, 0x00, 0x7F, + 0xFF, 0xF0, 0x00, 0x1F, 0xFF, 0xFF, 0x00, 0x03, 0xFF, 0x7F, 0xF0, 0x01, + 0xFF, 0xCF, 0xFF, 0x80, 0xFF, 0xF8, 0xFF, 0xFF, 0xFF, 0xFF, 0x1F, 0xFF, + 0xFF, 0xFF, 0xC1, 0xFF, 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, 0xFF, 0xFE, 0x01, + 0xFF, 0xFF, 0xFF, 0x00, 0x1F, 0xFF, 0xFF, 0xC0, 0x00, 0xFF, 0xFF, 0xE0, + 0x00, 0x01, 0xFF, 0xE0, 0x00, 0x00, 0x00, 0x1F, 0xF0, 0x00, 0x00, 0x00, + 0x0F, 0xF8, 0x00, 0x00, 0x00, 0x03, 0xFC, 0x00, 0x00, 0x00, 0x01, 0xFE, + 0x00, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xC0, 0x00, + 0x00, 0x00, 0x0F, 0xE0, 0x00, 0x00, 0x00, 0x07, 0xF0, 0x00, 0x00, 0x00, + 0x01, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3F, 0xF8, 0x00, 0x00, 0x7F, 0xF7, 0xFE, 0x00, 0x00, 0x1F, 0xF9, + 0xFF, 0xC0, 0x00, 0x0F, 0xFE, 0x3F, 0xF8, 0x00, 0x03, 0xFF, 0x07, 0xFE, + 0x00, 0x01, 0xFF, 0x81, 0xFF, 0xC0, 0x00, 0xFF, 0xE0, 0x3F, 0xF0, 0x00, + 0x3F, 0xF0, 0x07, 0xFE, 0x00, 0x1F, 0xF8, 0x01, 0xFF, 0xC0, 0x07, 0xFE, + 0x00, 0x3F, 0xF0, 0x03, 0xFF, 0x00, 0x07, 0xFE, 0x01, 0xFF, 0x80, 0x01, + 0xFF, 0x80, 0x7F, 0xE0, 0x00, 0x3F, 0xF0, 0x3F, 0xF0, 0x00, 0x0F, 0xFE, + 0x0F, 0xFC, 0x00, 0x01, 0xFF, 0x87, 0xFE, 0x00, 0x00, 0x3F, 0xF1, 0xFF, + 0x00, 0x00, 0x0F, 0xFC, 0xFF, 0xC0, 0x00, 0x01, 0xFF, 0xFF, 0xE0, 0x00, + 0x00, 0x3F, 0xFF, 0xF0, 0x00, 0x00, 0x0F, 0xFF, 0xFC, 0x00, 0x00, 0x01, + 0xFF, 0xFE, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0x00, 0x00, 0x00, 0x0F, 0xFF, + 0xC0, 0x00, 0x00, 0x01, 0xFF, 0xE0, 0x00, 0x00, 0x00, 0x7F, 0xF8, 0x00, + 0x00, 0x00, 0x0F, 0xFC, 0x00, 0x00, 0x00, 0x03, 0xFF, 0x00, 0x00, 0x00, + 0x00, 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x3F, 0xF0, 0x00, 0x00, 0x00, 0x0F, + 0xFC, 0x00, 0x00, 0x00, 0x03, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xC0, + 0x00, 0x00, 0x00, 0x3F, 0xF0, 0x00, 0x00, 0x00, 0x0F, 0xFC, 0x00, 0x00, + 0x00, 0x03, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xC0, 0x00, 0x00, 0x00, + 0x3F, 0xF0, 0x00, 0x00, 0x00, 0x0F, 0xFC, 0x00, 0x00, 0x00, 0x03, 0xFF, + 0x00, 0x00, 0x00, 0x00, 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x3F, 0xF0, 0x00, + 0x00, 0x00, 0x0F, 0xFC, 0x00, 0x00, 0x00, 0x03, 0xFF, 0x00, 0x00, 0x00, + 0x00, 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x3F, 0xF0, 0x00, 0x00, 0xFF, 0x80, + 0x00, 0x00, 0x3F, 0xE0, 0x00, 0x00, 0x0F, 0xF8, 0x00, 0x00, 0x03, 0xFE, + 0x00, 0x00, 0x00, 0xFF, 0x80, 0x00, 0x00, 0x3F, 0xE0, 0x00, 0x00, 0x0F, + 0xF8, 0x00, 0x00, 0x03, 0xFE, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x00, + 0x3F, 0xFF, 0xFF, 0xF8, 0x0F, 0xFF, 0xFF, 0xFF, 0x83, 0xFF, 0xFF, 0xFF, + 0xF0, 0xFF, 0xFF, 0xFF, 0xFE, 0x3F, 0xFF, 0xFF, 0xFF, 0xCF, 0xFF, 0xFF, + 0xFF, 0xF3, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0x80, 0x07, 0xFF, 0xBF, 0xE0, + 0x00, 0x7F, 0xFF, 0xF8, 0x00, 0x0F, 0xFF, 0xFE, 0x00, 0x03, 0xFF, 0xFF, + 0x80, 0x00, 0x7F, 0xFF, 0xE0, 0x00, 0x1F, 0xFF, 0xF8, 0x00, 0x07, 0xFF, + 0xFE, 0x00, 0x01, 0xFF, 0xFF, 0x80, 0x00, 0xFF, 0xFF, 0xE0, 0x00, 0x3F, + 0xFF, 0xF8, 0x00, 0x1F, 0xFF, 0xFE, 0x00, 0x0F, 0xFE, 0xFF, 0x80, 0x07, + 0xFF, 0xBF, 0xFF, 0xFF, 0xFF, 0xEF, 0xFF, 0xFF, 0xFF, 0xF3, 0xFF, 0xFF, + 0xFF, 0xF8, 0xFF, 0xFF, 0xFF, 0xFE, 0x3F, 0xFF, 0xFF, 0xFF, 0x0F, 0xFF, + 0xFF, 0xFF, 0x03, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFE, 0x00, 0x3F, + 0xE0, 0x00, 0x00, 0x0F, 0xF8, 0x00, 0x00, 0x03, 0xFE, 0x00, 0x00, 0x00, + 0xFF, 0x80, 0x00, 0x00, 0x3F, 0xE0, 0x00, 0x00, 0x0F, 0xF8, 0x00, 0x00, + 0x03, 0xFE, 0x00, 0x00, 0x00, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x7F, + 0xC0, 0x00, 0x01, 0xFF, 0xF8, 0x00, 0x07, 0xFF, 0xFE, 0x00, 0x0F, 0xFF, + 0xFF, 0x00, 0x1F, 0xFF, 0xFF, 0x80, 0x3F, 0xFF, 0xFF, 0x80, 0x3F, 0xFF, + 0xFF, 0xC0, 0x7F, 0xF0, 0xFF, 0xC0, 0x7F, 0xC0, 0x7F, 0xC0, 0x7F, 0xC0, + 0x7F, 0xC0, 0x7F, 0x80, 0x7F, 0xC0, 0x7F, 0x80, 0x7F, 0xC0, 0xFF, 0x80, + 0x7F, 0xC0, 0xFF, 0x80, 0xFF, 0x80, 0xFF, 0x80, 0xFF, 0x80, 0xFF, 0x80, + 0xFF, 0x00, 0xFF, 0x81, 0xFF, 0x00, 0xFF, 0x81, 0xFE, 0x00, 0xFF, 0x83, + 0xFE, 0x00, 0xFF, 0x83, 0xFC, 0x00, 0xFF, 0x87, 0xFC, 0x00, 0xFF, 0x87, + 0xFE, 0x00, 0xFF, 0x87, 0xFE, 0x00, 0xFF, 0x87, 0xFF, 0x00, 0xFF, 0x83, + 0xFF, 0x80, 0xFF, 0x81, 0xFF, 0xC0, 0xFF, 0x81, 0xFF, 0xE0, 0xFF, 0x80, + 0xFF, 0xF0, 0xFF, 0x80, 0x7F, 0xF8, 0xFF, 0x80, 0x3F, 0xFC, 0xFF, 0x80, + 0x1F, 0xFC, 0xFF, 0x80, 0x0F, 0xFE, 0xFF, 0x80, 0x07, 0xFE, 0xFF, 0x80, + 0x03, 0xFF, 0xFF, 0x80, 0x01, 0xFF, 0xFF, 0x80, 0x01, 0xFF, 0xFF, 0x80, + 0x00, 0xFF, 0xFF, 0x80, 0x00, 0xFF, 0xFF, 0x80, 0xC1, 0xFF, 0xFF, 0x83, + 0xE3, 0xFF, 0xFF, 0x8F, 0xFF, 0xFF, 0xFF, 0x8F, 0xFF, 0xFE, 0xFF, 0x8F, + 0xFF, 0xFC, 0xFF, 0x87, 0xFF, 0xFC, 0xFF, 0x83, 0xFF, 0xF8, 0xFF, 0x80, + 0xFF, 0xE0, 0x00, 0x00, 0x3F, 0x80, 0x01, 0xFF, 0x00, 0x00, 0x01, 0xFF, + 0x00, 0x00, 0x01, 0xFE, 0x00, 0x00, 0x01, 0xFE, 0x00, 0x00, 0x01, 0xFC, + 0x00, 0x00, 0x03, 0xFC, 0x00, 0x00, 0x03, 0xF8, 0x00, 0x00, 0x03, 0xF8, + 0x00, 0x00, 0x03, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xFF, 0x80, 0x00, 0x3F, 0xFF, 0xE0, + 0x00, 0xFF, 0xFF, 0xF0, 0x03, 0xFF, 0xFF, 0xF0, 0x0F, 0xFF, 0xFF, 0xF0, + 0x3F, 0xFF, 0xFF, 0xE0, 0xFF, 0xFF, 0xFF, 0xC1, 0xFF, 0x81, 0xFF, 0xC3, + 0xFC, 0x00, 0xFF, 0x8F, 0xF8, 0x01, 0xFF, 0x01, 0xE0, 0x03, 0xFE, 0x00, + 0x00, 0x03, 0xFC, 0x00, 0x00, 0x1F, 0xF8, 0x00, 0x01, 0xFF, 0xF0, 0x00, + 0x7F, 0xFF, 0xE0, 0x0F, 0xFF, 0xFF, 0xC0, 0x7F, 0xFF, 0xFF, 0x83, 0xFF, + 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0xFE, 0x1F, 0xFF, 0xF3, 0xFC, 0x7F, 0xFC, + 0x07, 0xF8, 0xFF, 0xC0, 0x0F, 0xF3, 0xFE, 0x00, 0x3F, 0xE7, 0xFC, 0x00, + 0x7F, 0xCF, 0xF8, 0x00, 0xFF, 0x9F, 0xF0, 0x03, 0xFF, 0x3F, 0xF0, 0x0F, + 0xFE, 0x3F, 0xF0, 0x7F, 0xFC, 0x7F, 0xFF, 0xFF, 0xF8, 0xFF, 0xFF, 0xFF, + 0xF0, 0xFF, 0xFF, 0xFF, 0xF0, 0xFF, 0xFF, 0x9F, 0xE1, 0xFF, 0xFE, 0x3F, + 0xC0, 0xFF, 0xF0, 0x7F, 0xC0, 0x7F, 0x80, 0x00, 0x00, 0x00, 0x01, 0xFF, + 0x00, 0x00, 0x07, 0xFC, 0x00, 0x00, 0x0F, 0xF0, 0x00, 0x00, 0x3F, 0xC0, + 0x00, 0x00, 0x7F, 0x00, 0x00, 0x01, 0xFE, 0x00, 0x00, 0x03, 0xF8, 0x00, + 0x00, 0x0F, 0xE0, 0x00, 0x00, 0x1F, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xFF, 0x80, 0x00, + 0x3F, 0xFF, 0xE0, 0x00, 0xFF, 0xFF, 0xF0, 0x03, 0xFF, 0xFF, 0xF0, 0x0F, + 0xFF, 0xFF, 0xF0, 0x3F, 0xFF, 0xFF, 0xE0, 0xFF, 0xFF, 0xFF, 0xC1, 0xFF, + 0x81, 0xFF, 0xC3, 0xFC, 0x00, 0xFF, 0x8F, 0xF8, 0x01, 0xFF, 0x01, 0xE0, + 0x03, 0xFE, 0x00, 0x00, 0x03, 0xFC, 0x00, 0x00, 0x1F, 0xF8, 0x00, 0x01, + 0xFF, 0xF0, 0x00, 0x7F, 0xFF, 0xE0, 0x0F, 0xFF, 0xFF, 0xC0, 0x7F, 0xFF, + 0xFF, 0x83, 0xFF, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0xFE, 0x1F, 0xFF, 0xF3, + 0xFC, 0x7F, 0xFC, 0x07, 0xF8, 0xFF, 0xC0, 0x0F, 0xF3, 0xFE, 0x00, 0x3F, + 0xE7, 0xFC, 0x00, 0x7F, 0xCF, 0xF8, 0x00, 0xFF, 0x9F, 0xF0, 0x03, 0xFF, + 0x3F, 0xF0, 0x0F, 0xFE, 0x3F, 0xF0, 0x7F, 0xFC, 0x7F, 0xFF, 0xFF, 0xF8, + 0xFF, 0xFF, 0xFF, 0xF0, 0xFF, 0xFF, 0xFF, 0xF0, 0xFF, 0xFF, 0x9F, 0xE1, + 0xFF, 0xFE, 0x3F, 0xC0, 0xFF, 0xF0, 0x7F, 0xC0, 0x7F, 0x80, 0x00, 0x00, + 0x00, 0x1F, 0xF0, 0x00, 0x00, 0x7F, 0xF0, 0x00, 0x00, 0xFF, 0xE0, 0x00, + 0x03, 0xFF, 0xE0, 0x00, 0x0F, 0xEF, 0xE0, 0x00, 0x1F, 0x9F, 0xC0, 0x00, + 0x7F, 0x1F, 0xC0, 0x01, 0xFC, 0x1F, 0xC0, 0x07, 0xF0, 0x1F, 0xC0, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, + 0xFF, 0x80, 0x00, 0x3F, 0xFF, 0xE0, 0x00, 0xFF, 0xFF, 0xF0, 0x03, 0xFF, + 0xFF, 0xF0, 0x0F, 0xFF, 0xFF, 0xF0, 0x3F, 0xFF, 0xFF, 0xE0, 0xFF, 0xFF, + 0xFF, 0xC1, 0xFF, 0x81, 0xFF, 0xC3, 0xFC, 0x00, 0xFF, 0x8F, 0xF8, 0x01, + 0xFF, 0x01, 0xE0, 0x03, 0xFE, 0x00, 0x00, 0x03, 0xFC, 0x00, 0x00, 0x1F, + 0xF8, 0x00, 0x01, 0xFF, 0xF0, 0x00, 0x7F, 0xFF, 0xE0, 0x0F, 0xFF, 0xFF, + 0xC0, 0x7F, 0xFF, 0xFF, 0x83, 0xFF, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0xFE, + 0x1F, 0xFF, 0xF3, 0xFC, 0x7F, 0xFC, 0x07, 0xF8, 0xFF, 0xC0, 0x0F, 0xF3, + 0xFE, 0x00, 0x3F, 0xE7, 0xFC, 0x00, 0x7F, 0xCF, 0xF8, 0x00, 0xFF, 0x9F, + 0xF0, 0x03, 0xFF, 0x3F, 0xF0, 0x0F, 0xFE, 0x3F, 0xF0, 0x7F, 0xFC, 0x7F, + 0xFF, 0xFF, 0xF8, 0xFF, 0xFF, 0xFF, 0xF0, 0xFF, 0xFF, 0xFF, 0xF0, 0xFF, + 0xFF, 0x9F, 0xE1, 0xFF, 0xFE, 0x3F, 0xC0, 0xFF, 0xF0, 0x7F, 0xC0, 0x7F, + 0x80, 0x00, 0x00, 0x00, 0xF8, 0x03, 0xC0, 0x07, 0xFC, 0x07, 0x80, 0x0F, + 0xFE, 0x1F, 0x00, 0x3F, 0xFF, 0xFE, 0x00, 0x7F, 0xFF, 0xF8, 0x00, 0xF0, + 0xFF, 0xF0, 0x01, 0xE0, 0x7F, 0xC0, 0x03, 0xC0, 0x1F, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, + 0xC0, 0x00, 0x1F, 0xFF, 0xF0, 0x00, 0x7F, 0xFF, 0xF8, 0x01, 0xFF, 0xFF, + 0xF8, 0x07, 0xFF, 0xFF, 0xF8, 0x1F, 0xFF, 0xFF, 0xF0, 0x7F, 0xFF, 0xFF, + 0xE0, 0xFF, 0xC0, 0xFF, 0xE1, 0xFE, 0x00, 0x7F, 0xC7, 0xFC, 0x00, 0xFF, + 0x80, 0xF0, 0x01, 0xFF, 0x00, 0x00, 0x01, 0xFE, 0x00, 0x00, 0x0F, 0xFC, + 0x00, 0x00, 0xFF, 0xF8, 0x00, 0x3F, 0xFF, 0xF0, 0x07, 0xFF, 0xFF, 0xE0, + 0x3F, 0xFF, 0xFF, 0xC1, 0xFF, 0xFF, 0xFF, 0x87, 0xFF, 0xFF, 0xFF, 0x0F, + 0xFF, 0xF9, 0xFE, 0x3F, 0xFE, 0x03, 0xFC, 0x7F, 0xE0, 0x07, 0xF9, 0xFF, + 0x00, 0x1F, 0xF3, 0xFE, 0x00, 0x3F, 0xE7, 0xFC, 0x00, 0x7F, 0xCF, 0xF8, + 0x01, 0xFF, 0x9F, 0xF8, 0x07, 0xFF, 0x1F, 0xF8, 0x3F, 0xFE, 0x3F, 0xFF, + 0xFF, 0xFC, 0x7F, 0xFF, 0xFF, 0xF8, 0x7F, 0xFF, 0xFF, 0xF8, 0x7F, 0xFF, + 0xCF, 0xF0, 0xFF, 0xFF, 0x1F, 0xE0, 0x7F, 0xF8, 0x3F, 0xE0, 0x3F, 0xC0, + 0x00, 0x00, 0x07, 0xF8, 0x3F, 0xC0, 0x0F, 0xF0, 0x7F, 0x80, 0x1F, 0xE0, + 0xFF, 0x00, 0x3F, 0xC1, 0xFE, 0x00, 0x7F, 0x83, 0xFC, 0x00, 0xFF, 0x07, + 0xF8, 0x01, 0xFE, 0x0F, 0xF0, 0x03, 0xFC, 0x1F, 0xE0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xC0, + 0x00, 0x1F, 0xFF, 0xF0, 0x00, 0x7F, 0xFF, 0xF8, 0x01, 0xFF, 0xFF, 0xF8, + 0x07, 0xFF, 0xFF, 0xF8, 0x1F, 0xFF, 0xFF, 0xF0, 0x7F, 0xFF, 0xFF, 0xE0, + 0xFF, 0xC0, 0xFF, 0xE1, 0xFE, 0x00, 0x7F, 0xC7, 0xFC, 0x00, 0xFF, 0x80, + 0xF0, 0x01, 0xFF, 0x00, 0x00, 0x01, 0xFE, 0x00, 0x00, 0x0F, 0xFC, 0x00, + 0x00, 0xFF, 0xF8, 0x00, 0x3F, 0xFF, 0xF0, 0x07, 0xFF, 0xFF, 0xE0, 0x3F, + 0xFF, 0xFF, 0xC1, 0xFF, 0xFF, 0xFF, 0x87, 0xFF, 0xFF, 0xFF, 0x0F, 0xFF, + 0xF9, 0xFE, 0x3F, 0xFE, 0x03, 0xFC, 0x7F, 0xE0, 0x07, 0xF9, 0xFF, 0x00, + 0x1F, 0xF3, 0xFE, 0x00, 0x3F, 0xE7, 0xFC, 0x00, 0x7F, 0xCF, 0xF8, 0x01, + 0xFF, 0x9F, 0xF8, 0x07, 0xFF, 0x1F, 0xF8, 0x3F, 0xFE, 0x3F, 0xFF, 0xFF, + 0xFC, 0x7F, 0xFF, 0xFF, 0xF8, 0x7F, 0xFF, 0xFF, 0xF8, 0x7F, 0xFF, 0xCF, + 0xF0, 0xFF, 0xFF, 0x1F, 0xE0, 0x7F, 0xF8, 0x3F, 0xE0, 0x3F, 0xC0, 0x00, + 0x00, 0x00, 0x07, 0xC0, 0x00, 0x00, 0x3F, 0xC0, 0x00, 0x00, 0x7F, 0xC0, + 0x00, 0x01, 0xE3, 0xC0, 0x00, 0x03, 0x83, 0x80, 0x00, 0x0F, 0x07, 0x00, + 0x00, 0x1E, 0x0F, 0x00, 0x00, 0x1C, 0x1C, 0x00, 0x00, 0x3C, 0x78, 0x00, + 0x00, 0x3F, 0xE0, 0x00, 0x00, 0x7F, 0x80, 0x00, 0x00, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1F, 0xFC, 0x00, 0x01, 0xFF, 0xFF, 0x00, 0x07, 0xFF, 0xFF, 0x80, 0x1F, + 0xFF, 0xFF, 0x80, 0x7F, 0xFF, 0xFF, 0x81, 0xFF, 0xFF, 0xFF, 0x07, 0xFF, + 0xFF, 0xFE, 0x0F, 0xFC, 0x0F, 0xFE, 0x1F, 0xE0, 0x07, 0xFC, 0x7F, 0xC0, + 0x0F, 0xF8, 0x0F, 0x00, 0x1F, 0xF0, 0x00, 0x00, 0x1F, 0xE0, 0x00, 0x00, + 0xFF, 0xC0, 0x00, 0x0F, 0xFF, 0x80, 0x03, 0xFF, 0xFF, 0x00, 0x7F, 0xFF, + 0xFE, 0x03, 0xFF, 0xFF, 0xFC, 0x1F, 0xFF, 0xFF, 0xF8, 0x7F, 0xFF, 0xFF, + 0xF0, 0xFF, 0xFF, 0x9F, 0xE3, 0xFF, 0xE0, 0x3F, 0xC7, 0xFE, 0x00, 0x7F, + 0x9F, 0xF0, 0x01, 0xFF, 0x3F, 0xE0, 0x03, 0xFE, 0x7F, 0xC0, 0x07, 0xFC, + 0xFF, 0x80, 0x1F, 0xF9, 0xFF, 0x80, 0x7F, 0xF1, 0xFF, 0x83, 0xFF, 0xE3, + 0xFF, 0xFF, 0xFF, 0xC7, 0xFF, 0xFF, 0xFF, 0x87, 0xFF, 0xFF, 0xFF, 0x87, + 0xFF, 0xFC, 0xFF, 0x0F, 0xFF, 0xF1, 0xFE, 0x07, 0xFF, 0x83, 0xFE, 0x03, + 0xFC, 0x00, 0x00, 0x00, 0x3F, 0xF0, 0x01, 0xFE, 0x00, 0x00, 0x7F, 0xFF, + 0x83, 0xFF, 0xF0, 0x00, 0x7F, 0xFF, 0xFB, 0xFF, 0xFE, 0x00, 0x3F, 0xFF, + 0xFF, 0xFF, 0xFF, 0xE0, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x07, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0xFF, + 0xC1, 0xFF, 0xFC, 0x1F, 0xFC, 0x7F, 0xC0, 0x1F, 0xFE, 0x01, 0xFF, 0x1F, + 0xE0, 0x03, 0xFF, 0x00, 0x3F, 0xE0, 0x78, 0x00, 0xFF, 0x80, 0x07, 0xF8, + 0x00, 0x00, 0x3F, 0xE0, 0x01, 0xFE, 0x00, 0x00, 0x3F, 0xF8, 0x00, 0x7F, + 0x80, 0x00, 0xFF, 0xFE, 0x00, 0x1F, 0xF0, 0x03, 0xFF, 0xFF, 0x00, 0x07, + 0xFC, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xC7, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF3, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFD, 0xFF, 0xFF, 0x3F, 0xFF, 0xFF, 0xFF, 0x7F, 0xF8, 0x0F, 0xF0, + 0x00, 0x00, 0x3F, 0xF0, 0x03, 0xFE, 0x00, 0x00, 0x0F, 0xF8, 0x00, 0xFF, + 0x80, 0x00, 0x03, 0xFC, 0x00, 0x3F, 0xE0, 0x01, 0xE0, 0xFF, 0x00, 0x1F, + 0xFC, 0x00, 0x7F, 0xBF, 0xE0, 0x07, 0xFF, 0x00, 0x3F, 0xEF, 0xF8, 0x03, + 0xFF, 0xE0, 0x1F, 0xFB, 0xFF, 0x83, 0xFF, 0xFE, 0x0F, 0xFC, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x87, 0xFF, + 0xFF, 0xEF, 0xFF, 0xFF, 0xC0, 0xFF, 0xFF, 0xF1, 0xFF, 0xFF, 0xE0, 0x1F, + 0xFF, 0xF0, 0x3F, 0xFF, 0xF0, 0x03, 0xFF, 0xF0, 0x03, 0xFF, 0xF0, 0x00, + 0x1F, 0xE0, 0x00, 0x1F, 0xF0, 0x00, 0x00, 0x1F, 0xF0, 0x00, 0x03, 0xFF, + 0xF8, 0x00, 0x3F, 0xFF, 0xF8, 0x01, 0xFF, 0xFF, 0xF0, 0x0F, 0xFF, 0xFF, + 0xE0, 0x7F, 0xFF, 0xFF, 0xC3, 0xFF, 0xFF, 0xFF, 0x0F, 0xFE, 0x0F, 0xFE, + 0x7F, 0xE0, 0x0F, 0xF9, 0xFF, 0x00, 0x1F, 0xE7, 0xFC, 0x00, 0x7F, 0xFF, + 0xE0, 0x01, 0xF0, 0xFF, 0x80, 0x00, 0x03, 0xFE, 0x00, 0x00, 0x0F, 0xF8, + 0x00, 0x00, 0x3F, 0xE0, 0x00, 0x00, 0xFF, 0x80, 0x00, 0x03, 0xFE, 0x00, + 0x00, 0x0F, 0xF8, 0x00, 0x00, 0x3F, 0xE0, 0x00, 0x00, 0xFF, 0x80, 0x00, + 0x03, 0xFE, 0x00, 0x00, 0x0F, 0xF8, 0x00, 0x3C, 0x3F, 0xE0, 0x00, 0xFF, + 0x7F, 0xC0, 0x07, 0xFD, 0xFF, 0x00, 0x1F, 0xF7, 0xFE, 0x00, 0xFF, 0x8F, + 0xFE, 0x0F, 0xFE, 0x3F, 0xFF, 0xFF, 0xF0, 0x7F, 0xFF, 0xFF, 0xC0, 0xFF, + 0xFF, 0xFE, 0x01, 0xFF, 0xFF, 0xF0, 0x03, 0xFF, 0xFF, 0x80, 0x03, 0xFF, + 0xF8, 0x00, 0x01, 0xFF, 0x00, 0x00, 0x07, 0xFF, 0x00, 0x00, 0x3F, 0xFE, + 0x00, 0x00, 0x81, 0xFC, 0x00, 0x00, 0x03, 0xF0, 0x00, 0x00, 0x0F, 0xC0, + 0x00, 0x00, 0x3F, 0x00, 0x00, 0x03, 0xF8, 0x00, 0x1F, 0xFF, 0xC0, 0x00, + 0x7F, 0xFE, 0x00, 0x00, 0x7F, 0xC0, 0x00, 0x03, 0xFE, 0x00, 0x00, 0x03, + 0xFE, 0x00, 0x00, 0x03, 0xFC, 0x00, 0x00, 0x03, 0xFC, 0x00, 0x00, 0x03, + 0xF8, 0x00, 0x00, 0x07, 0xF8, 0x00, 0x00, 0x07, 0xF0, 0x00, 0x00, 0x07, + 0xF0, 0x00, 0x00, 0x07, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFE, 0x00, 0x00, 0x1F, 0xFF, + 0x80, 0x00, 0x7F, 0xFF, 0xC0, 0x03, 0xFF, 0xFF, 0xC0, 0x0F, 0xFF, 0xFF, + 0xC0, 0x1F, 0xFF, 0xFF, 0xC0, 0x7F, 0xFF, 0xFF, 0xC1, 0xFF, 0xC1, 0xFF, + 0x83, 0xFE, 0x01, 0xFF, 0x8F, 0xFC, 0x01, 0xFF, 0x1F, 0xF0, 0x01, 0xFE, + 0x3F, 0xE0, 0x03, 0xFE, 0xFF, 0x80, 0x03, 0xFD, 0xFF, 0x00, 0x07, 0xFB, + 0xFE, 0x00, 0x0F, 0xF7, 0xFF, 0xFF, 0xFF, 0xEF, 0xFF, 0xFF, 0xFF, 0xDF, + 0xFF, 0xFF, 0xFF, 0xBF, 0xFF, 0xFF, 0xFF, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, + 0x80, 0x00, 0x01, 0xFF, 0x00, 0x00, 0x03, 0xFE, 0x00, 0x00, 0x03, 0xFC, + 0x00, 0x3E, 0x07, 0xFC, 0x00, 0x7F, 0xCF, 0xFC, 0x01, 0xFF, 0x8F, 0xFC, + 0x03, 0xFE, 0x1F, 0xFC, 0x1F, 0xFC, 0x1F, 0xFF, 0xFF, 0xF0, 0x3F, 0xFF, + 0xFF, 0xC0, 0x3F, 0xFF, 0xFF, 0x80, 0x3F, 0xFF, 0xFE, 0x00, 0x3F, 0xFF, + 0xF0, 0x00, 0x1F, 0xFF, 0xC0, 0x00, 0x07, 0xFC, 0x00, 0x00, 0x00, 0x01, + 0xFF, 0x00, 0x00, 0x07, 0xFC, 0x00, 0x00, 0x0F, 0xF0, 0x00, 0x00, 0x3F, + 0xC0, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x01, 0xFE, 0x00, 0x00, 0x03, 0xF8, + 0x00, 0x00, 0x0F, 0xE0, 0x00, 0x00, 0x1F, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFE, 0x00, + 0x00, 0x1F, 0xFF, 0x80, 0x00, 0x7F, 0xFF, 0xC0, 0x03, 0xFF, 0xFF, 0xC0, + 0x0F, 0xFF, 0xFF, 0xC0, 0x1F, 0xFF, 0xFF, 0xC0, 0x7F, 0xFF, 0xFF, 0xC1, + 0xFF, 0xC1, 0xFF, 0x83, 0xFE, 0x01, 0xFF, 0x8F, 0xFC, 0x01, 0xFF, 0x1F, + 0xF0, 0x01, 0xFE, 0x3F, 0xE0, 0x03, 0xFE, 0xFF, 0x80, 0x03, 0xFD, 0xFF, + 0x00, 0x07, 0xFB, 0xFE, 0x00, 0x0F, 0xF7, 0xFF, 0xFF, 0xFF, 0xEF, 0xFF, + 0xFF, 0xFF, 0xDF, 0xFF, 0xFF, 0xFF, 0xBF, 0xFF, 0xFF, 0xFF, 0x7F, 0xFF, + 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x01, 0xFF, 0x00, 0x00, 0x03, 0xFE, 0x00, + 0x00, 0x03, 0xFC, 0x00, 0x3E, 0x07, 0xFC, 0x00, 0x7F, 0xCF, 0xFC, 0x01, + 0xFF, 0x8F, 0xFC, 0x03, 0xFE, 0x1F, 0xFC, 0x1F, 0xFC, 0x1F, 0xFF, 0xFF, + 0xF0, 0x3F, 0xFF, 0xFF, 0xC0, 0x3F, 0xFF, 0xFF, 0x80, 0x3F, 0xFF, 0xFE, + 0x00, 0x3F, 0xFF, 0xF0, 0x00, 0x1F, 0xFF, 0xC0, 0x00, 0x07, 0xFC, 0x00, + 0x00, 0x00, 0x1F, 0xF0, 0x00, 0x00, 0x7F, 0xF0, 0x00, 0x00, 0xFF, 0xE0, + 0x00, 0x03, 0xFF, 0xE0, 0x00, 0x0F, 0xEF, 0xE0, 0x00, 0x1F, 0x9F, 0xC0, + 0x00, 0x7F, 0x1F, 0xC0, 0x01, 0xFC, 0x1F, 0xC0, 0x07, 0xF0, 0x1F, 0xC0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0xFE, 0x00, 0x00, 0x1F, 0xFF, 0x80, 0x00, 0x7F, 0xFF, 0xC0, 0x03, + 0xFF, 0xFF, 0xC0, 0x0F, 0xFF, 0xFF, 0xC0, 0x1F, 0xFF, 0xFF, 0xC0, 0x7F, + 0xFF, 0xFF, 0xC1, 0xFF, 0xC1, 0xFF, 0x83, 0xFE, 0x01, 0xFF, 0x8F, 0xFC, + 0x01, 0xFF, 0x1F, 0xF0, 0x01, 0xFE, 0x3F, 0xE0, 0x03, 0xFE, 0xFF, 0x80, + 0x03, 0xFD, 0xFF, 0x00, 0x07, 0xFB, 0xFE, 0x00, 0x0F, 0xF7, 0xFF, 0xFF, + 0xFF, 0xEF, 0xFF, 0xFF, 0xFF, 0xDF, 0xFF, 0xFF, 0xFF, 0xBF, 0xFF, 0xFF, + 0xFF, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x01, 0xFF, 0x00, 0x00, + 0x03, 0xFE, 0x00, 0x00, 0x03, 0xFC, 0x00, 0x3E, 0x07, 0xFC, 0x00, 0x7F, + 0xCF, 0xFC, 0x01, 0xFF, 0x8F, 0xFC, 0x03, 0xFE, 0x1F, 0xFC, 0x1F, 0xFC, + 0x1F, 0xFF, 0xFF, 0xF0, 0x3F, 0xFF, 0xFF, 0xC0, 0x3F, 0xFF, 0xFF, 0x80, + 0x3F, 0xFF, 0xFE, 0x00, 0x3F, 0xFF, 0xF0, 0x00, 0x1F, 0xFF, 0xC0, 0x00, + 0x07, 0xFC, 0x00, 0x00, 0x07, 0xF8, 0x3F, 0xC0, 0x0F, 0xF0, 0x7F, 0x80, + 0x1F, 0xE0, 0xFF, 0x00, 0x3F, 0xC1, 0xFE, 0x00, 0x7F, 0x83, 0xFC, 0x00, + 0xFF, 0x07, 0xF8, 0x01, 0xFE, 0x0F, 0xF0, 0x03, 0xFC, 0x1F, 0xE0, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0x00, 0x00, 0x0F, 0xFF, 0xC0, 0x00, 0x3F, 0xFF, 0xE0, 0x01, 0xFF, + 0xFF, 0xE0, 0x07, 0xFF, 0xFF, 0xE0, 0x0F, 0xFF, 0xFF, 0xE0, 0x3F, 0xFF, + 0xFF, 0xE0, 0xFF, 0xE0, 0xFF, 0xC1, 0xFF, 0x00, 0xFF, 0xC7, 0xFE, 0x00, + 0xFF, 0x8F, 0xF8, 0x00, 0xFF, 0x1F, 0xF0, 0x01, 0xFF, 0x7F, 0xC0, 0x01, + 0xFE, 0xFF, 0x80, 0x03, 0xFD, 0xFF, 0x00, 0x07, 0xFB, 0xFF, 0xFF, 0xFF, + 0xF7, 0xFF, 0xFF, 0xFF, 0xEF, 0xFF, 0xFF, 0xFF, 0xDF, 0xFF, 0xFF, 0xFF, + 0xBF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0xFF, 0x80, 0x00, 0x01, + 0xFF, 0x00, 0x00, 0x01, 0xFE, 0x00, 0x1F, 0x03, 0xFE, 0x00, 0x3F, 0xE7, + 0xFE, 0x00, 0xFF, 0xC7, 0xFE, 0x01, 0xFF, 0x0F, 0xFE, 0x0F, 0xFE, 0x0F, + 0xFF, 0xFF, 0xF8, 0x1F, 0xFF, 0xFF, 0xE0, 0x1F, 0xFF, 0xFF, 0xC0, 0x1F, + 0xFF, 0xFF, 0x00, 0x1F, 0xFF, 0xF8, 0x00, 0x0F, 0xFF, 0xE0, 0x00, 0x03, + 0xFE, 0x00, 0x00, 0x7F, 0xC0, 0xFF, 0x81, 0xFE, 0x03, 0xFC, 0x07, 0xF0, + 0x1F, 0xE0, 0x3F, 0x80, 0x7F, 0x00, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0F, 0xF0, 0x3F, 0xC0, 0xFF, 0x03, 0xFC, 0x0F, 0xF0, 0x3F, + 0xC0, 0xFF, 0x03, 0xFC, 0x0F, 0xF0, 0x3F, 0xC0, 0xFF, 0x03, 0xFC, 0x0F, + 0xF0, 0x3F, 0xC0, 0xFF, 0x03, 0xFC, 0x0F, 0xF0, 0x3F, 0xC0, 0xFF, 0x03, + 0xFC, 0x0F, 0xF0, 0x3F, 0xC0, 0xFF, 0x03, 0xFC, 0x0F, 0xF0, 0x3F, 0xC0, + 0xFF, 0x03, 0xFC, 0x0F, 0xF0, 0x3F, 0xC0, 0xFF, 0x03, 0xFC, 0x0F, 0xF0, + 0x0F, 0xF8, 0x7F, 0xC1, 0xFE, 0x0F, 0xF0, 0x3F, 0x81, 0xFE, 0x07, 0xF0, + 0x3F, 0x80, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFE, + 0x07, 0xF8, 0x1F, 0xE0, 0x7F, 0x81, 0xFE, 0x07, 0xF8, 0x1F, 0xE0, 0x7F, + 0x81, 0xFE, 0x07, 0xF8, 0x1F, 0xE0, 0x7F, 0x81, 0xFE, 0x07, 0xF8, 0x1F, + 0xE0, 0x7F, 0x81, 0xFE, 0x07, 0xF8, 0x1F, 0xE0, 0x7F, 0x81, 0xFE, 0x07, + 0xF8, 0x1F, 0xE0, 0x7F, 0x81, 0xFE, 0x07, 0xF8, 0x1F, 0xE0, 0x7F, 0x81, + 0xFE, 0x07, 0xF8, 0x1F, 0xE0, 0x7F, 0x81, 0xFE, 0x00, 0x03, 0xFE, 0x00, + 0x3F, 0xF8, 0x01, 0xFF, 0xC0, 0x1F, 0xFF, 0x01, 0xFD, 0xFC, 0x0F, 0xCF, + 0xE0, 0xFE, 0x3F, 0x8F, 0xE0, 0xFE, 0xFE, 0x03, 0xF8, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x07, 0xF8, + 0x00, 0x3F, 0xC0, 0x01, 0xFE, 0x00, 0x0F, 0xF0, 0x00, 0x7F, 0x80, 0x03, + 0xFC, 0x00, 0x1F, 0xE0, 0x00, 0xFF, 0x00, 0x07, 0xF8, 0x00, 0x3F, 0xC0, + 0x01, 0xFE, 0x00, 0x0F, 0xF0, 0x00, 0x7F, 0x80, 0x03, 0xFC, 0x00, 0x1F, + 0xE0, 0x00, 0xFF, 0x00, 0x07, 0xF8, 0x00, 0x3F, 0xC0, 0x01, 0xFE, 0x00, + 0x0F, 0xF0, 0x00, 0x7F, 0x80, 0x03, 0xFC, 0x00, 0x1F, 0xE0, 0x00, 0xFF, + 0x00, 0x07, 0xF8, 0x00, 0x3F, 0xC0, 0x01, 0xFE, 0x00, 0x0F, 0xF0, 0x00, + 0x7F, 0x80, 0x03, 0xFC, 0x00, 0x1F, 0xE0, 0x00, 0xFF, 0x00, 0xFF, 0x07, + 0xFF, 0xF8, 0x3F, 0xFF, 0xC1, 0xFF, 0xFE, 0x0F, 0xFF, 0xF0, 0x7F, 0xFF, + 0x83, 0xFF, 0xFC, 0x1F, 0xFF, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xE0, 0x00, 0xFF, 0x00, 0x07, + 0xF8, 0x00, 0x3F, 0xC0, 0x01, 0xFE, 0x00, 0x0F, 0xF0, 0x00, 0x7F, 0x80, + 0x03, 0xFC, 0x00, 0x1F, 0xE0, 0x00, 0xFF, 0x00, 0x07, 0xF8, 0x00, 0x3F, + 0xC0, 0x01, 0xFE, 0x00, 0x0F, 0xF0, 0x00, 0x7F, 0x80, 0x03, 0xFC, 0x00, + 0x1F, 0xE0, 0x00, 0xFF, 0x00, 0x07, 0xF8, 0x00, 0x3F, 0xC0, 0x01, 0xFE, + 0x00, 0x0F, 0xF0, 0x00, 0x7F, 0x80, 0x03, 0xFC, 0x00, 0x1F, 0xE0, 0x00, + 0xFF, 0x00, 0x07, 0xF8, 0x00, 0x3F, 0xC0, 0x01, 0xFE, 0x00, 0x0F, 0xF0, + 0x00, 0x7F, 0x80, 0x03, 0xFC, 0x00, 0x1F, 0xE0, 0x00, 0x00, 0xFF, 0x80, + 0x80, 0x00, 0x3F, 0xE1, 0xE0, 0x00, 0x07, 0xFB, 0xF0, 0x00, 0x01, 0xFF, + 0xF0, 0x00, 0x00, 0x7F, 0xE0, 0x00, 0x00, 0x3F, 0xF0, 0x00, 0x00, 0x7F, + 0xFC, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x01, 0xFC, 0x7F, 0x80, 0x00, 0x78, + 0x1F, 0xE0, 0x00, 0x30, 0x07, 0xF8, 0x00, 0x00, 0x03, 0xFE, 0x00, 0x03, + 0xF8, 0xFF, 0x00, 0x07, 0xFF, 0xFF, 0xC0, 0x0F, 0xFF, 0xFF, 0xE0, 0x0F, + 0xFF, 0xFF, 0xF8, 0x0F, 0xFF, 0xFF, 0xFC, 0x0F, 0xFF, 0xFF, 0xFF, 0x0F, + 0xFF, 0xFF, 0xFF, 0x87, 0xFF, 0x83, 0xFF, 0xE7, 0xFF, 0x00, 0xFF, 0xF3, + 0xFF, 0x00, 0x1F, 0xFB, 0xFF, 0x00, 0x0F, 0xFD, 0xFF, 0x80, 0x03, 0xFF, + 0xFF, 0x80, 0x01, 0xFF, 0xFF, 0xC0, 0x00, 0x7F, 0xFF, 0xE0, 0x00, 0x3F, + 0xFF, 0xF0, 0x00, 0x1F, 0xFF, 0xF8, 0x00, 0x0F, 0xFF, 0xFC, 0x00, 0x07, + 0xFF, 0xFE, 0x00, 0x03, 0xFF, 0xFF, 0x00, 0x01, 0xFF, 0xFF, 0x80, 0x00, + 0xFF, 0xFF, 0xC0, 0x00, 0xFF, 0xFF, 0xF0, 0x00, 0x7F, 0xCF, 0xF8, 0x00, + 0x7F, 0xE7, 0xFE, 0x00, 0x3F, 0xF1, 0xFF, 0x80, 0x3F, 0xF0, 0xFF, 0xF0, + 0x7F, 0xF0, 0x3F, 0xFF, 0xFF, 0xF8, 0x0F, 0xFF, 0xFF, 0xF8, 0x03, 0xFF, + 0xFF, 0xF8, 0x00, 0xFF, 0xFF, 0xF8, 0x00, 0x3F, 0xFF, 0xF8, 0x00, 0x07, + 0xFF, 0xF0, 0x00, 0x00, 0x7F, 0xC0, 0x00, 0x00, 0xF8, 0x03, 0xC0, 0x0F, + 0xF8, 0x0F, 0x00, 0x3F, 0xF8, 0x7C, 0x01, 0xFF, 0xFF, 0xF0, 0x07, 0xFF, + 0xFF, 0x80, 0x1E, 0x1F, 0xFE, 0x00, 0x78, 0x1F, 0xF0, 0x01, 0xE0, 0x0F, + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3F, 0x80, 0xFF, 0x07, 0xFF, 0x83, 0xFC, 0x3F, 0xFF, 0x8F, + 0xF1, 0xFF, 0xFF, 0x3F, 0xCF, 0xFF, 0xFE, 0xFF, 0x7F, 0xFF, 0xFB, 0xFF, + 0xFF, 0xFF, 0xEF, 0xFF, 0xC1, 0xFF, 0xFF, 0xFC, 0x03, 0xFF, 0xFF, 0xE0, + 0x07, 0xFF, 0xFF, 0x00, 0x1F, 0xFF, 0xFC, 0x00, 0x7F, 0xFF, 0xF0, 0x01, + 0xFF, 0xFF, 0x80, 0x07, 0xFF, 0xFE, 0x00, 0x0F, 0xFF, 0xF8, 0x00, 0x3F, + 0xFF, 0xE0, 0x00, 0xFF, 0xFF, 0x80, 0x03, 0xFF, 0xFE, 0x00, 0x0F, 0xFF, + 0xF8, 0x00, 0x3F, 0xFF, 0xE0, 0x00, 0xFF, 0xFF, 0x80, 0x03, 0xFF, 0xFE, + 0x00, 0x0F, 0xFF, 0xF8, 0x00, 0x3F, 0xFF, 0xE0, 0x00, 0xFF, 0xFF, 0x80, + 0x03, 0xFF, 0xFE, 0x00, 0x0F, 0xFF, 0xF8, 0x00, 0x3F, 0xFF, 0xE0, 0x00, + 0xFF, 0xFF, 0x80, 0x03, 0xFF, 0xFE, 0x00, 0x0F, 0xFF, 0xF8, 0x00, 0x3F, + 0xFF, 0xE0, 0x00, 0xFF, 0xFF, 0x80, 0x03, 0xFC, 0x01, 0xFF, 0x00, 0x00, + 0x00, 0x7F, 0xC0, 0x00, 0x00, 0x1F, 0xE0, 0x00, 0x00, 0x07, 0xF8, 0x00, + 0x00, 0x01, 0xFC, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x3F, 0x80, + 0x00, 0x00, 0x0F, 0xE0, 0x00, 0x00, 0x03, 0xF0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, + 0x80, 0x00, 0x03, 0xFF, 0xF8, 0x00, 0x07, 0xFF, 0xFE, 0x00, 0x07, 0xFF, + 0xFF, 0xC0, 0x07, 0xFF, 0xFF, 0xF0, 0x07, 0xFF, 0xFF, 0xFC, 0x07, 0xFF, + 0xFF, 0xFF, 0x07, 0xFF, 0x83, 0xFF, 0xC3, 0xFF, 0x00, 0x7F, 0xE3, 0xFF, + 0x00, 0x1F, 0xF9, 0xFF, 0x00, 0x07, 0xFD, 0xFF, 0x80, 0x03, 0xFE, 0xFF, + 0x80, 0x00, 0xFF, 0xFF, 0xC0, 0x00, 0x7F, 0xFF, 0xE0, 0x00, 0x3F, 0xFF, + 0xE0, 0x00, 0x1F, 0xFF, 0xF0, 0x00, 0x0F, 0xFF, 0xF8, 0x00, 0x07, 0xFF, + 0xFC, 0x00, 0x03, 0xFF, 0xFE, 0x00, 0x01, 0xFF, 0xFF, 0x80, 0x00, 0xFF, + 0xFF, 0xC0, 0x00, 0x7F, 0xFF, 0xE0, 0x00, 0x3F, 0xFF, 0xF0, 0x00, 0x3F, + 0xE7, 0xFC, 0x00, 0x1F, 0xF3, 0xFF, 0x00, 0x1F, 0xF9, 0xFF, 0xC0, 0x1F, + 0xF8, 0x7F, 0xF8, 0x3F, 0xF8, 0x1F, 0xFF, 0xFF, 0xFC, 0x0F, 0xFF, 0xFF, + 0xFC, 0x03, 0xFF, 0xFF, 0xFC, 0x00, 0xFF, 0xFF, 0xFC, 0x00, 0x1F, 0xFF, + 0xF8, 0x00, 0x03, 0xFF, 0xF8, 0x00, 0x00, 0x3F, 0xE0, 0x00, 0x00, 0x00, + 0xFF, 0x80, 0x00, 0x00, 0xFF, 0x80, 0x00, 0x00, 0x7F, 0x80, 0x00, 0x00, + 0x7F, 0x80, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x3F, 0xC0, 0x00, 0x00, + 0x1F, 0xC0, 0x00, 0x00, 0x1F, 0xC0, 0x00, 0x00, 0x0F, 0xC0, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xFF, 0x80, 0x00, 0x03, 0xFF, 0xF8, 0x00, 0x07, 0xFF, 0xFE, 0x00, + 0x07, 0xFF, 0xFF, 0xC0, 0x07, 0xFF, 0xFF, 0xF0, 0x07, 0xFF, 0xFF, 0xFC, + 0x07, 0xFF, 0xFF, 0xFF, 0x07, 0xFF, 0x83, 0xFF, 0xC3, 0xFF, 0x00, 0x7F, + 0xE3, 0xFF, 0x00, 0x1F, 0xF9, 0xFF, 0x00, 0x07, 0xFD, 0xFF, 0x80, 0x03, + 0xFE, 0xFF, 0x80, 0x00, 0xFF, 0xFF, 0xC0, 0x00, 0x7F, 0xFF, 0xE0, 0x00, + 0x3F, 0xFF, 0xE0, 0x00, 0x1F, 0xFF, 0xF0, 0x00, 0x0F, 0xFF, 0xF8, 0x00, + 0x07, 0xFF, 0xFC, 0x00, 0x03, 0xFF, 0xFE, 0x00, 0x01, 0xFF, 0xFF, 0x80, + 0x00, 0xFF, 0xFF, 0xC0, 0x00, 0x7F, 0xFF, 0xE0, 0x00, 0x3F, 0xFF, 0xF0, + 0x00, 0x3F, 0xE7, 0xFC, 0x00, 0x1F, 0xF3, 0xFF, 0x00, 0x1F, 0xF9, 0xFF, + 0xC0, 0x1F, 0xF8, 0x7F, 0xF8, 0x3F, 0xF8, 0x1F, 0xFF, 0xFF, 0xFC, 0x0F, + 0xFF, 0xFF, 0xFC, 0x03, 0xFF, 0xFF, 0xFC, 0x00, 0xFF, 0xFF, 0xFC, 0x00, + 0x1F, 0xFF, 0xF8, 0x00, 0x03, 0xFF, 0xF8, 0x00, 0x00, 0x3F, 0xE0, 0x00, + 0x00, 0x0F, 0xF8, 0x00, 0x00, 0x0F, 0xFE, 0x00, 0x00, 0x07, 0xFF, 0x00, + 0x00, 0x07, 0xFF, 0xC0, 0x00, 0x07, 0xF7, 0xF0, 0x00, 0x03, 0xF3, 0xF8, + 0x00, 0x03, 0xF8, 0xFE, 0x00, 0x03, 0xF8, 0x3F, 0x80, 0x03, 0xF8, 0x0F, + 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xFF, 0x80, 0x00, 0x03, 0xFF, 0xF8, 0x00, 0x07, 0xFF, + 0xFE, 0x00, 0x07, 0xFF, 0xFF, 0xC0, 0x07, 0xFF, 0xFF, 0xF0, 0x07, 0xFF, + 0xFF, 0xFC, 0x07, 0xFF, 0xFF, 0xFF, 0x07, 0xFF, 0x83, 0xFF, 0xC3, 0xFF, + 0x00, 0x7F, 0xE3, 0xFF, 0x00, 0x1F, 0xF9, 0xFF, 0x00, 0x07, 0xFD, 0xFF, + 0x80, 0x03, 0xFE, 0xFF, 0x80, 0x00, 0xFF, 0xFF, 0xC0, 0x00, 0x7F, 0xFF, + 0xE0, 0x00, 0x3F, 0xFF, 0xE0, 0x00, 0x1F, 0xFF, 0xF0, 0x00, 0x0F, 0xFF, + 0xF8, 0x00, 0x07, 0xFF, 0xFC, 0x00, 0x03, 0xFF, 0xFE, 0x00, 0x01, 0xFF, + 0xFF, 0x80, 0x00, 0xFF, 0xFF, 0xC0, 0x00, 0x7F, 0xFF, 0xE0, 0x00, 0x3F, + 0xFF, 0xF0, 0x00, 0x3F, 0xE7, 0xFC, 0x00, 0x1F, 0xF3, 0xFF, 0x00, 0x1F, + 0xF9, 0xFF, 0xC0, 0x1F, 0xF8, 0x7F, 0xF8, 0x3F, 0xF8, 0x1F, 0xFF, 0xFF, + 0xFC, 0x0F, 0xFF, 0xFF, 0xFC, 0x03, 0xFF, 0xFF, 0xFC, 0x00, 0xFF, 0xFF, + 0xFC, 0x00, 0x1F, 0xFF, 0xF8, 0x00, 0x03, 0xFF, 0xF8, 0x00, 0x00, 0x3F, + 0xE0, 0x00, 0x00, 0x7C, 0x01, 0xE0, 0x00, 0xFF, 0x80, 0xF0, 0x00, 0x7F, + 0xF0, 0xF8, 0x00, 0x7F, 0xFF, 0xFC, 0x00, 0x3F, 0xFF, 0xFC, 0x00, 0x1E, + 0x1F, 0xFE, 0x00, 0x0F, 0x03, 0xFE, 0x00, 0x07, 0x80, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0xFF, 0x00, 0x00, 0x07, 0xFF, 0xF0, 0x00, 0x0F, 0xFF, 0xFC, 0x00, + 0x0F, 0xFF, 0xFF, 0x80, 0x0F, 0xFF, 0xFF, 0xE0, 0x0F, 0xFF, 0xFF, 0xF8, + 0x0F, 0xFF, 0xFF, 0xFE, 0x0F, 0xFF, 0x07, 0xFF, 0x87, 0xFE, 0x00, 0xFF, + 0xC7, 0xFE, 0x00, 0x3F, 0xF3, 0xFE, 0x00, 0x0F, 0xFB, 0xFF, 0x00, 0x07, + 0xFD, 0xFF, 0x00, 0x01, 0xFF, 0xFF, 0x80, 0x00, 0xFF, 0xFF, 0xC0, 0x00, + 0x7F, 0xFF, 0xC0, 0x00, 0x3F, 0xFF, 0xE0, 0x00, 0x1F, 0xFF, 0xF0, 0x00, + 0x0F, 0xFF, 0xF8, 0x00, 0x07, 0xFF, 0xFC, 0x00, 0x03, 0xFF, 0xFF, 0x00, + 0x01, 0xFF, 0xFF, 0x80, 0x00, 0xFF, 0xFF, 0xC0, 0x00, 0x7F, 0xFF, 0xE0, + 0x00, 0x7F, 0xCF, 0xF8, 0x00, 0x3F, 0xE7, 0xFE, 0x00, 0x3F, 0xF3, 0xFF, + 0x80, 0x3F, 0xF0, 0xFF, 0xF0, 0x7F, 0xF0, 0x3F, 0xFF, 0xFF, 0xF8, 0x1F, + 0xFF, 0xFF, 0xF8, 0x07, 0xFF, 0xFF, 0xF8, 0x01, 0xFF, 0xFF, 0xF8, 0x00, + 0x3F, 0xFF, 0xF0, 0x00, 0x07, 0xFF, 0xF0, 0x00, 0x00, 0x7F, 0xC0, 0x00, + 0x03, 0xFC, 0x1F, 0xE0, 0x01, 0xFE, 0x0F, 0xF0, 0x00, 0xFF, 0x07, 0xF8, + 0x00, 0x7F, 0x83, 0xFC, 0x00, 0x3F, 0xC1, 0xFE, 0x00, 0x1F, 0xE0, 0xFF, + 0x00, 0x0F, 0xF0, 0x7F, 0x80, 0x07, 0xF8, 0x3F, 0xC0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, + 0x00, 0x00, 0x07, 0xFF, 0xF0, 0x00, 0x0F, 0xFF, 0xFC, 0x00, 0x0F, 0xFF, + 0xFF, 0x80, 0x0F, 0xFF, 0xFF, 0xE0, 0x0F, 0xFF, 0xFF, 0xF8, 0x0F, 0xFF, + 0xFF, 0xFE, 0x0F, 0xFF, 0x07, 0xFF, 0x87, 0xFE, 0x00, 0xFF, 0xC7, 0xFE, + 0x00, 0x3F, 0xF3, 0xFE, 0x00, 0x0F, 0xFB, 0xFF, 0x00, 0x07, 0xFD, 0xFF, + 0x00, 0x01, 0xFF, 0xFF, 0x80, 0x00, 0xFF, 0xFF, 0xC0, 0x00, 0x7F, 0xFF, + 0xC0, 0x00, 0x3F, 0xFF, 0xE0, 0x00, 0x1F, 0xFF, 0xF0, 0x00, 0x0F, 0xFF, + 0xF8, 0x00, 0x07, 0xFF, 0xFC, 0x00, 0x03, 0xFF, 0xFF, 0x00, 0x01, 0xFF, + 0xFF, 0x80, 0x00, 0xFF, 0xFF, 0xC0, 0x00, 0x7F, 0xFF, 0xE0, 0x00, 0x7F, + 0xCF, 0xF8, 0x00, 0x3F, 0xE7, 0xFE, 0x00, 0x3F, 0xF3, 0xFF, 0x80, 0x3F, + 0xF0, 0xFF, 0xF0, 0x7F, 0xF0, 0x3F, 0xFF, 0xFF, 0xF8, 0x1F, 0xFF, 0xFF, + 0xF8, 0x07, 0xFF, 0xFF, 0xF8, 0x01, 0xFF, 0xFF, 0xF8, 0x00, 0x3F, 0xFF, + 0xF0, 0x00, 0x07, 0xFF, 0xF0, 0x00, 0x00, 0x7F, 0xC0, 0x00, 0x00, 0x1F, + 0xF0, 0x00, 0x00, 0x3F, 0xE0, 0x00, 0x00, 0x7F, 0xC0, 0x00, 0x00, 0xFF, + 0x80, 0x00, 0x01, 0xFF, 0x00, 0x00, 0x03, 0xFE, 0x00, 0x00, 0x07, 0xFC, + 0x00, 0x00, 0x0F, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xF0, 0x00, 0x00, + 0x3F, 0xE0, 0x00, 0x00, 0x7F, 0xC0, 0x00, 0x00, 0xFF, 0x80, 0x00, 0x01, + 0xFF, 0x00, 0x00, 0x03, 0xFE, 0x00, 0x00, 0x07, 0xFC, 0x00, 0x00, 0x0F, + 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x03, + 0xFE, 0x03, 0xC0, 0x0F, 0xFF, 0xE3, 0xF0, 0x1F, 0xFF, 0xFB, 0xF0, 0x1F, + 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, 0xFF, 0xF8, 0x1F, 0xFF, 0xFF, 0xF8, 0x1F, + 0xFF, 0xFF, 0xFC, 0x1F, 0xFE, 0x0F, 0xFF, 0x0F, 0xFC, 0x01, 0xFF, 0x8F, + 0xFC, 0x01, 0xFF, 0xE7, 0xFC, 0x01, 0xFF, 0xF7, 0xFC, 0x00, 0xFF, 0xFB, + 0xFE, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0xFD, 0xFF, 0xFF, 0x00, 0xFC, 0xFF, + 0xFF, 0x80, 0xFC, 0x3F, 0xFF, 0xC0, 0xFC, 0x1F, 0xFF, 0xE0, 0x7C, 0x0F, + 0xFF, 0xF0, 0x7E, 0x07, 0xFF, 0xF8, 0x7E, 0x03, 0xFF, 0xFE, 0x7E, 0x01, + 0xFF, 0xFF, 0x7E, 0x01, 0xFF, 0xFF, 0xFE, 0x00, 0xFF, 0xBF, 0xFE, 0x00, + 0x7F, 0xDF, 0xFF, 0x00, 0x7F, 0xCF, 0xFF, 0x00, 0x7F, 0xE3, 0xFF, 0x00, + 0x7F, 0xE1, 0xFF, 0xE0, 0xFF, 0xF0, 0x7F, 0xFF, 0xFF, 0xF0, 0x3F, 0xFF, + 0xFF, 0xF0, 0x1F, 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, + 0xFF, 0xF0, 0x1F, 0x8F, 0xFF, 0xE0, 0x07, 0x80, 0xFF, 0x80, 0x01, 0x80, + 0x00, 0x00, 0x00, 0x03, 0xFE, 0x00, 0x00, 0x07, 0xFC, 0x00, 0x00, 0x0F, + 0xF0, 0x00, 0x00, 0x1F, 0xE0, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0xFF, + 0x00, 0x00, 0x01, 0xFC, 0x00, 0x00, 0x03, 0xF8, 0x00, 0x00, 0x07, 0xE0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x03, 0xFE, 0x00, 0x1F, 0xFF, 0xF8, 0x00, 0x7F, 0xFF, + 0xE0, 0x01, 0xFF, 0xFF, 0x80, 0x07, 0xFF, 0xFE, 0x00, 0x1F, 0xFF, 0xF8, + 0x00, 0x7F, 0xFF, 0xE0, 0x01, 0xFF, 0xFF, 0x80, 0x07, 0xFF, 0xFE, 0x00, + 0x1F, 0xFF, 0xF8, 0x00, 0x7F, 0xFF, 0xE0, 0x01, 0xFF, 0xFF, 0x80, 0x07, + 0xFF, 0xFE, 0x00, 0x1F, 0xFF, 0xF8, 0x00, 0x7F, 0xFF, 0xE0, 0x01, 0xFF, + 0xFF, 0x80, 0x07, 0xFF, 0xFE, 0x00, 0x1F, 0xFF, 0xF8, 0x00, 0x7F, 0xFF, + 0xE0, 0x01, 0xFF, 0xFF, 0x80, 0x07, 0xFF, 0xFE, 0x00, 0x1F, 0xFF, 0xF8, + 0x00, 0x7F, 0xFF, 0xE0, 0x01, 0xFF, 0xFF, 0x80, 0x0F, 0xFD, 0xFF, 0x00, + 0x3F, 0xF7, 0xFE, 0x01, 0xFF, 0xDF, 0xFC, 0x1F, 0xFF, 0x7F, 0xFF, 0xFF, + 0xFC, 0xFF, 0xFF, 0xFF, 0xF3, 0xFF, 0xFF, 0xBF, 0xC7, 0xFF, 0xFC, 0xFF, + 0x0F, 0xFF, 0xE3, 0xFC, 0x1F, 0xFE, 0x0F, 0xF0, 0x0F, 0xE0, 0x00, 0x00, + 0x00, 0x01, 0xFF, 0x00, 0x00, 0x0F, 0xF8, 0x00, 0x00, 0x3F, 0xC0, 0x00, + 0x01, 0xFE, 0x00, 0x00, 0x07, 0xF0, 0x00, 0x00, 0x3F, 0xC0, 0x00, 0x00, + 0xFE, 0x00, 0x00, 0x07, 0xF0, 0x00, 0x00, 0x1F, 0x80, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0xFE, 0x00, 0x1F, 0xFF, 0xF8, 0x00, 0x7F, 0xFF, 0xE0, 0x01, 0xFF, + 0xFF, 0x80, 0x07, 0xFF, 0xFE, 0x00, 0x1F, 0xFF, 0xF8, 0x00, 0x7F, 0xFF, + 0xE0, 0x01, 0xFF, 0xFF, 0x80, 0x07, 0xFF, 0xFE, 0x00, 0x1F, 0xFF, 0xF8, + 0x00, 0x7F, 0xFF, 0xE0, 0x01, 0xFF, 0xFF, 0x80, 0x07, 0xFF, 0xFE, 0x00, + 0x1F, 0xFF, 0xF8, 0x00, 0x7F, 0xFF, 0xE0, 0x01, 0xFF, 0xFF, 0x80, 0x07, + 0xFF, 0xFE, 0x00, 0x1F, 0xFF, 0xF8, 0x00, 0x7F, 0xFF, 0xE0, 0x01, 0xFF, + 0xFF, 0x80, 0x07, 0xFF, 0xFE, 0x00, 0x1F, 0xFF, 0xF8, 0x00, 0x7F, 0xFF, + 0xE0, 0x01, 0xFF, 0xFF, 0x80, 0x0F, 0xFD, 0xFF, 0x00, 0x3F, 0xF7, 0xFE, + 0x01, 0xFF, 0xDF, 0xFC, 0x1F, 0xFF, 0x7F, 0xFF, 0xFF, 0xFC, 0xFF, 0xFF, + 0xFF, 0xF3, 0xFF, 0xFF, 0xBF, 0xC7, 0xFF, 0xFC, 0xFF, 0x0F, 0xFF, 0xE3, + 0xFC, 0x1F, 0xFE, 0x0F, 0xF0, 0x0F, 0xE0, 0x00, 0x00, 0x00, 0x1F, 0xF0, + 0x00, 0x00, 0xFF, 0xE0, 0x00, 0x03, 0xFF, 0x80, 0x00, 0x1F, 0xFF, 0x00, + 0x00, 0xFE, 0xFE, 0x00, 0x03, 0xF3, 0xF8, 0x00, 0x1F, 0xC7, 0xF0, 0x00, + 0xFE, 0x0F, 0xE0, 0x07, 0xF0, 0x1F, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xFE, 0x00, + 0x1F, 0xFF, 0xF8, 0x00, 0x7F, 0xFF, 0xE0, 0x01, 0xFF, 0xFF, 0x80, 0x07, + 0xFF, 0xFE, 0x00, 0x1F, 0xFF, 0xF8, 0x00, 0x7F, 0xFF, 0xE0, 0x01, 0xFF, + 0xFF, 0x80, 0x07, 0xFF, 0xFE, 0x00, 0x1F, 0xFF, 0xF8, 0x00, 0x7F, 0xFF, + 0xE0, 0x01, 0xFF, 0xFF, 0x80, 0x07, 0xFF, 0xFE, 0x00, 0x1F, 0xFF, 0xF8, + 0x00, 0x7F, 0xFF, 0xE0, 0x01, 0xFF, 0xFF, 0x80, 0x07, 0xFF, 0xFE, 0x00, + 0x1F, 0xFF, 0xF8, 0x00, 0x7F, 0xFF, 0xE0, 0x01, 0xFF, 0xFF, 0x80, 0x07, + 0xFF, 0xFE, 0x00, 0x1F, 0xFF, 0xF8, 0x00, 0x7F, 0xFF, 0xE0, 0x01, 0xFF, + 0xFF, 0x80, 0x0F, 0xFD, 0xFF, 0x00, 0x3F, 0xF7, 0xFE, 0x01, 0xFF, 0xDF, + 0xFC, 0x1F, 0xFF, 0x7F, 0xFF, 0xFF, 0xFC, 0xFF, 0xFF, 0xFF, 0xF3, 0xFF, + 0xFF, 0xBF, 0xC7, 0xFF, 0xFC, 0xFF, 0x0F, 0xFF, 0xE3, 0xFC, 0x1F, 0xFE, + 0x0F, 0xF0, 0x0F, 0xE0, 0x00, 0x00, 0x07, 0xF8, 0x3F, 0xC0, 0x1F, 0xE0, + 0xFF, 0x00, 0x7F, 0x83, 0xFC, 0x01, 0xFE, 0x0F, 0xF0, 0x07, 0xF8, 0x3F, + 0xC0, 0x1F, 0xE0, 0xFF, 0x00, 0x7F, 0x83, 0xFC, 0x01, 0xFE, 0x0F, 0xF0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xFF, 0x80, 0x07, 0xFF, 0xFE, 0x00, 0x1F, 0xFF, 0xF8, + 0x00, 0x7F, 0xFF, 0xE0, 0x01, 0xFF, 0xFF, 0x80, 0x07, 0xFF, 0xFE, 0x00, + 0x1F, 0xFF, 0xF8, 0x00, 0x7F, 0xFF, 0xE0, 0x01, 0xFF, 0xFF, 0x80, 0x07, + 0xFF, 0xFE, 0x00, 0x1F, 0xFF, 0xF8, 0x00, 0x7F, 0xFF, 0xE0, 0x01, 0xFF, + 0xFF, 0x80, 0x07, 0xFF, 0xFE, 0x00, 0x1F, 0xFF, 0xF8, 0x00, 0x7F, 0xFF, + 0xE0, 0x01, 0xFF, 0xFF, 0x80, 0x07, 0xFF, 0xFE, 0x00, 0x1F, 0xFF, 0xF8, + 0x00, 0x7F, 0xFF, 0xE0, 0x01, 0xFF, 0xFF, 0x80, 0x07, 0xFF, 0xFE, 0x00, + 0x1F, 0xFF, 0xF8, 0x00, 0x7F, 0xFF, 0xE0, 0x03, 0xFF, 0x7F, 0xC0, 0x0F, + 0xFD, 0xFF, 0x80, 0x7F, 0xF7, 0xFF, 0x07, 0xFF, 0xDF, 0xFF, 0xFF, 0xFF, + 0x3F, 0xFF, 0xFF, 0xFC, 0xFF, 0xFF, 0xEF, 0xF1, 0xFF, 0xFF, 0x3F, 0xC3, + 0xFF, 0xF8, 0xFF, 0x07, 0xFF, 0x83, 0xFC, 0x03, 0xF8, 0x00, 0x00, 0x00, + 0x00, 0x7F, 0xC0, 0x00, 0x00, 0x3F, 0xE0, 0x00, 0x00, 0x0F, 0xF0, 0x00, + 0x00, 0x07, 0xF8, 0x00, 0x00, 0x01, 0xFC, 0x00, 0x00, 0x00, 0xFF, 0x00, + 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x1F, 0xC0, 0x00, 0x00, 0x07, 0xE0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xF0, 0x00, 0x1F, 0xF7, 0xFC, + 0x00, 0x07, 0xF9, 0xFF, 0x00, 0x03, 0xFE, 0x3F, 0xE0, 0x00, 0xFF, 0x8F, + 0xF8, 0x00, 0x3F, 0xC1, 0xFE, 0x00, 0x1F, 0xF0, 0x7F, 0xC0, 0x07, 0xFC, + 0x1F, 0xF0, 0x01, 0xFE, 0x03, 0xFC, 0x00, 0xFF, 0x80, 0xFF, 0x80, 0x3F, + 0xE0, 0x3F, 0xE0, 0x0F, 0xF0, 0x07, 0xF8, 0x07, 0xFC, 0x01, 0xFF, 0x01, + 0xFE, 0x00, 0x3F, 0xC0, 0x7F, 0x80, 0x0F, 0xF0, 0x3F, 0xE0, 0x03, 0xFE, + 0x0F, 0xF0, 0x00, 0x7F, 0x83, 0xFC, 0x00, 0x1F, 0xE1, 0xFF, 0x00, 0x07, + 0xFC, 0x7F, 0x80, 0x00, 0xFF, 0x1F, 0xE0, 0x00, 0x3F, 0xCF, 0xF8, 0x00, + 0x07, 0xFB, 0xFC, 0x00, 0x01, 0xFE, 0xFF, 0x00, 0x00, 0x7F, 0xFF, 0xC0, + 0x00, 0x0F, 0xFF, 0xE0, 0x00, 0x03, 0xFF, 0xF8, 0x00, 0x00, 0xFF, 0xFC, + 0x00, 0x00, 0x1F, 0xFF, 0x00, 0x00, 0x07, 0xFF, 0xC0, 0x00, 0x00, 0xFF, + 0xE0, 0x00, 0x00, 0x3F, 0xF8, 0x00, 0x00, 0x0F, 0xFE, 0x00, 0x00, 0x01, + 0xFF, 0x00, 0x00, 0x00, 0x7F, 0xC0, 0x00, 0x00, 0x3F, 0xF0, 0x00, 0x00, + 0x0F, 0xF8, 0x00, 0x00, 0x03, 0xFE, 0x00, 0x00, 0x01, 0xFF, 0x80, 0x00, + 0x00, 0xFF, 0xC0, 0x00, 0x00, 0x7F, 0xF0, 0x00, 0x07, 0xFF, 0xF8, 0x00, + 0x01, 0xFF, 0xFE, 0x00, 0x00, 0x7F, 0xFF, 0x00, 0x00, 0x1F, 0xFF, 0x80, + 0x00, 0x07, 0xFF, 0xC0, 0x00, 0x01, 0xFF, 0xE0, 0x00, 0x00, 0x7F, 0xC0, + 0x00, 0x00, 0xFF, 0x80, 0x00, 0x00, 0xFF, 0x80, 0x00, 0x00, 0xFF, 0x80, + 0x00, 0x00, 0xFF, 0x80, 0x00, 0x00, 0xFF, 0x80, 0x00, 0x00, 0xFF, 0x80, + 0x00, 0x00, 0xFF, 0x80, 0x00, 0x00, 0xFF, 0x80, 0x00, 0x00, 0xFF, 0x80, + 0x00, 0x00, 0xFF, 0x80, 0x00, 0x00, 0xFF, 0x80, 0x00, 0x00, 0xFF, 0x81, + 0xFC, 0x00, 0xFF, 0x87, 0xFF, 0x00, 0xFF, 0x9F, 0xFF, 0xC0, 0xFF, 0xBF, + 0xFF, 0xE0, 0xFF, 0xFF, 0xFF, 0xF0, 0xFF, 0xFF, 0xFF, 0xF8, 0xFF, 0xFF, + 0xFF, 0xFC, 0xFF, 0xFC, 0x1F, 0xFC, 0xFF, 0xF0, 0x0F, 0xFE, 0xFF, 0xE0, + 0x07, 0xFE, 0xFF, 0xC0, 0x03, 0xFE, 0xFF, 0xC0, 0x03, 0xFF, 0xFF, 0x80, + 0x01, 0xFF, 0xFF, 0x80, 0x01, 0xFF, 0xFF, 0x80, 0x01, 0xFF, 0xFF, 0x80, + 0x01, 0xFF, 0xFF, 0x80, 0x01, 0xFF, 0xFF, 0x80, 0x01, 0xFF, 0xFF, 0x80, + 0x01, 0xFF, 0xFF, 0x80, 0x01, 0xFF, 0xFF, 0x80, 0x01, 0xFF, 0xFF, 0x80, + 0x01, 0xFF, 0xFF, 0xC0, 0x01, 0xFF, 0xFF, 0xC0, 0x03, 0xFE, 0xFF, 0xC0, + 0x03, 0xFE, 0xFF, 0xE0, 0x07, 0xFE, 0xFF, 0xF0, 0x0F, 0xFE, 0xFF, 0xFC, + 0x1F, 0xFC, 0xFF, 0xFF, 0xFF, 0xFC, 0xFF, 0xFF, 0xFF, 0xF8, 0xFF, 0xFF, + 0xFF, 0xF0, 0xFF, 0xBF, 0xFF, 0xE0, 0xFF, 0x9F, 0xFF, 0xC0, 0xFF, 0x87, + 0xFF, 0x00, 0xFF, 0x81, 0xFC, 0x00, 0xFF, 0x80, 0x00, 0x00, 0xFF, 0x80, + 0x00, 0x00, 0xFF, 0x80, 0x00, 0x00, 0xFF, 0x80, 0x00, 0x00, 0xFF, 0x80, + 0x00, 0x00, 0xFF, 0x80, 0x00, 0x00, 0xFF, 0x80, 0x00, 0x00, 0xFF, 0x80, + 0x00, 0x00, 0xFF, 0x80, 0x00, 0x00, 0xFF, 0x80, 0x00, 0x00, 0xFF, 0x80, + 0x00, 0x00, 0xFF, 0x80, 0x00, 0x00, 0x01, 0xFE, 0x0F, 0xF0, 0x00, 0x7F, + 0x83, 0xFC, 0x00, 0x1F, 0xE0, 0xFF, 0x00, 0x07, 0xF8, 0x3F, 0xC0, 0x01, + 0xFE, 0x0F, 0xF0, 0x00, 0x7F, 0x83, 0xFC, 0x00, 0x1F, 0xE0, 0xFF, 0x00, + 0x07, 0xF8, 0x3F, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xC0, 0x00, + 0x7F, 0xDF, 0xF0, 0x00, 0x1F, 0xE7, 0xFC, 0x00, 0x0F, 0xF8, 0xFF, 0x80, + 0x03, 0xFE, 0x3F, 0xE0, 0x00, 0xFF, 0x07, 0xF8, 0x00, 0x7F, 0xC1, 0xFF, + 0x00, 0x1F, 0xF0, 0x7F, 0xC0, 0x07, 0xF8, 0x0F, 0xF0, 0x03, 0xFE, 0x03, + 0xFE, 0x00, 0xFF, 0x80, 0xFF, 0x80, 0x3F, 0xC0, 0x1F, 0xE0, 0x1F, 0xF0, + 0x07, 0xFC, 0x07, 0xF8, 0x00, 0xFF, 0x01, 0xFE, 0x00, 0x3F, 0xC0, 0xFF, + 0x80, 0x0F, 0xF8, 0x3F, 0xC0, 0x01, 0xFE, 0x0F, 0xF0, 0x00, 0x7F, 0x87, + 0xFC, 0x00, 0x1F, 0xF1, 0xFE, 0x00, 0x03, 0xFC, 0x7F, 0x80, 0x00, 0xFF, + 0x3F, 0xE0, 0x00, 0x1F, 0xEF, 0xF0, 0x00, 0x07, 0xFB, 0xFC, 0x00, 0x01, + 0xFF, 0xFF, 0x00, 0x00, 0x3F, 0xFF, 0x80, 0x00, 0x0F, 0xFF, 0xE0, 0x00, + 0x03, 0xFF, 0xF0, 0x00, 0x00, 0x7F, 0xFC, 0x00, 0x00, 0x1F, 0xFF, 0x00, + 0x00, 0x03, 0xFF, 0x80, 0x00, 0x00, 0xFF, 0xE0, 0x00, 0x00, 0x3F, 0xF8, + 0x00, 0x00, 0x07, 0xFC, 0x00, 0x00, 0x01, 0xFF, 0x00, 0x00, 0x00, 0xFF, + 0xC0, 0x00, 0x00, 0x3F, 0xE0, 0x00, 0x00, 0x0F, 0xF8, 0x00, 0x00, 0x07, + 0xFE, 0x00, 0x00, 0x03, 0xFF, 0x00, 0x00, 0x01, 0xFF, 0xC0, 0x00, 0x1F, + 0xFF, 0xE0, 0x00, 0x07, 0xFF, 0xF8, 0x00, 0x01, 0xFF, 0xFC, 0x00, 0x00, + 0x7F, 0xFE, 0x00, 0x00, 0x1F, 0xFF, 0x00, 0x00, 0x07, 0xFF, 0x80, 0x00, + 0x01, 0xFF, 0x00, 0x00, 0x00 }; + +const GFXglyph myFont32pt8bGlyphs[] PROGMEM = { + { 0, 0, 0, 18, 0, 1 }, // 0x20 ' ' + { 0, 9, 45, 21, 6, -44 }, // 0x21 '!' + { 51, 24, 16, 30, 3, -44 }, // 0x22 '"' + { 99, 33, 45, 35, 1, -44 }, // 0x23 '#' + { 285, 30, 55, 35, 2, -47 }, // 0x24 '$' + { 492, 50, 48, 56, 3, -45 }, // 0x25 '%' + { 792, 41, 47, 46, 3, -45 }, // 0x26 '&' + { 1033, 9, 16, 15, 3, -44 }, // 0x27 ''' + { 1051, 16, 58, 21, 3, -44 }, // 0x28 '(' + { 1167, 16, 58, 21, 2, -44 }, // 0x29 ')' + { 1283, 22, 21, 25, 1, -44 }, // 0x2A '*' + { 1341, 31, 32, 37, 3, -37 }, // 0x2B '+' + { 1465, 9, 19, 18, 4, -8 }, // 0x2C ',' + { 1487, 17, 8, 21, 4, -19 }, // 0x2D '-' + { 1504, 8, 9, 18, 5, -8 }, // 0x2E '.' + { 1513, 18, 45, 18, 0, -44 }, // 0x2F '/' + { 1615, 29, 46, 35, 3, -44 }, // 0x30 '0' + { 1782, 20, 45, 35, 5, -44 }, // 0x31 '1' + { 1895, 30, 45, 35, 2, -44 }, // 0x32 '2' + { 2064, 30, 46, 35, 2, -44 }, // 0x33 '3' + { 2237, 33, 44, 35, 1, -43 }, // 0x34 '4' + { 2419, 30, 45, 35, 3, -43 }, // 0x35 '5' + { 2588, 30, 46, 35, 3, -44 }, // 0x36 '6' + { 2761, 29, 44, 35, 3, -43 }, // 0x37 '7' + { 2921, 29, 46, 35, 3, -44 }, // 0x38 '8' + { 3088, 30, 46, 35, 2, -44 }, // 0x39 '9' + { 3261, 9, 33, 21, 6, -32 }, // 0x3A ':' + { 3299, 10, 43, 21, 5, -32 }, // 0x3B ';' + { 3353, 31, 34, 37, 3, -38 }, // 0x3C '<' + { 3485, 31, 22, 37, 3, -32 }, // 0x3D '=' + { 3571, 31, 34, 37, 3, -38 }, // 0x3E '>' + { 3703, 33, 46, 38, 3, -45 }, // 0x3F '?' + { 3893, 59, 61, 61, 2, -45 }, // 0x40 '@' + { 4343, 45, 45, 46, 0, -44 }, // 0x41 'A' + { 4597, 37, 45, 46, 5, -44 }, // 0x42 'B' + { 4806, 39, 46, 46, 3, -45 }, // 0x43 'C' + { 5031, 37, 45, 46, 5, -44 }, // 0x44 'D' + { 5240, 34, 45, 42, 5, -44 }, // 0x45 'E' + { 5432, 31, 45, 38, 5, -44 }, // 0x46 'F' + { 5607, 42, 47, 49, 3, -45 }, // 0x47 'G' + { 5854, 36, 45, 46, 5, -44 }, // 0x48 'H' + { 6057, 9, 45, 18, 4, -44 }, // 0x49 'I' + { 6108, 29, 46, 35, 1, -44 }, // 0x4A 'J' + { 6275, 40, 45, 46, 5, -44 }, // 0x4B 'K' + { 6500, 32, 45, 38, 5, -44 }, // 0x4C 'L' + { 6680, 44, 45, 52, 4, -44 }, // 0x4D 'M' + { 6928, 35, 45, 46, 5, -44 }, // 0x4E 'N' + { 7125, 43, 47, 49, 3, -45 }, // 0x4F 'O' + { 7378, 34, 45, 42, 5, -44 }, // 0x50 'P' + { 7570, 45, 51, 49, 3, -45 }, // 0x51 'Q' + { 7857, 40, 45, 46, 5, -44 }, // 0x52 'R' + { 8082, 37, 47, 42, 2, -45 }, // 0x53 'S' + { 8300, 36, 45, 38, 1, -44 }, // 0x54 'T' + { 8503, 35, 46, 46, 5, -44 }, // 0x55 'U' + { 8705, 42, 45, 42, 0, -44 }, // 0x56 'V' + { 8942, 59, 45, 59, 0, -44 }, // 0x57 'W' + { 9274, 42, 45, 42, 0, -44 }, // 0x58 'X' + { 9511, 42, 45, 42, 0, -44 }, // 0x59 'Y' + { 9748, 36, 45, 38, 1, -44 }, // 0x5A 'Z' + { 9951, 16, 58, 21, 4, -44 }, // 0x5B '[' + { 10067, 18, 45, 18, 0, -44 }, // 0x5C '\' + { 10169, 15, 58, 21, 1, -44 }, // 0x5D ']' + { 10278, 29, 24, 37, 4, -45 }, // 0x5E '^' + { 10365, 36, 7, 35, -1, 6 }, // 0x5F '_' + { 10397, 14, 9, 21, 1, -44 }, // 0x60 '`' + { 10413, 31, 35, 35, 2, -33 }, // 0x61 'a' + { 10549, 32, 46, 38, 4, -44 }, // 0x62 'b' + { 10733, 30, 35, 35, 3, -33 }, // 0x63 'c' + { 10865, 31, 46, 38, 3, -44 }, // 0x64 'd' + { 11044, 31, 35, 35, 2, -33 }, // 0x65 'e' + { 11180, 22, 46, 21, 1, -45 }, // 0x66 'f' + { 11307, 31, 48, 38, 3, -33 }, // 0x67 'g' + { 11493, 30, 45, 38, 4, -44 }, // 0x68 'h' + { 11662, 8, 45, 18, 5, -44 }, // 0x69 'i' + { 11707, 16, 59, 18, -3, -44 }, // 0x6A 'j' + { 11825, 30, 45, 35, 4, -44 }, // 0x6B 'k' + { 11994, 8, 45, 18, 5, -44 }, // 0x6C 'l' + { 12039, 48, 34, 56, 4, -33 }, // 0x6D 'm' + { 12243, 30, 34, 38, 4, -33 }, // 0x6E 'n' + { 12371, 33, 35, 38, 3, -33 }, // 0x6F 'o' + { 12516, 32, 47, 38, 4, -33 }, // 0x70 'p' + { 12704, 32, 47, 38, 3, -33 }, // 0x71 'q' + { 12892, 21, 34, 25, 4, -33 }, // 0x72 'r' + { 12982, 31, 35, 35, 1, -33 }, // 0x73 's' + { 13118, 19, 45, 21, 1, -43 }, // 0x74 't' + { 13225, 30, 34, 38, 4, -32 }, // 0x75 'u' + { 13353, 34, 33, 35, 0, -32 }, // 0x76 'v' + { 13494, 49, 33, 49, 0, -32 }, // 0x77 'w' + { 13697, 34, 33, 35, 0, -32 }, // 0x78 'x' + { 13838, 34, 47, 35, 0, -32 }, // 0x79 'y' + { 14038, 29, 33, 32, 1, -32 }, // 0x7A 'z' + { 14158, 21, 60, 25, 2, -45 }, // 0x7B '{' + { 14316, 7, 59, 18, 5, -44 }, // 0x7C '|' + { 14368, 21, 60, 25, 1, -45 }, // 0x7D '}' + { 14526, 33, 12, 37, 2, -27 }, // 0x7E '~' + { 14576, 31, 39, 47, 8, -38 }, // 0x7F + { 14728, 31, 39, 47, 8, -38 }, // 0x80 + { 14880, 31, 39, 47, 8, -38 }, // 0x81 + { 15032, 31, 39, 47, 8, -38 }, // 0x82 + { 15184, 31, 39, 47, 8, -38 }, // 0x83 + { 15336, 31, 39, 47, 8, -38 }, // 0x84 + { 15488, 31, 39, 47, 8, -38 }, // 0x85 + { 15640, 31, 39, 47, 8, -38 }, // 0x86 + { 15792, 31, 39, 47, 8, -38 }, // 0x87 + { 15944, 31, 39, 47, 8, -38 }, // 0x88 + { 16096, 31, 39, 47, 8, -38 }, // 0x89 + { 16248, 31, 39, 47, 8, -38 }, // 0x8A + { 16400, 31, 39, 47, 8, -38 }, // 0x8B + { 16552, 31, 39, 47, 8, -38 }, // 0x8C + { 16704, 31, 39, 47, 8, -38 }, // 0x8D + { 16856, 31, 39, 47, 8, -38 }, // 0x8E + { 17008, 31, 39, 47, 8, -38 }, // 0x8F + { 17160, 31, 39, 47, 8, -38 }, // 0x90 + { 17312, 31, 39, 47, 8, -38 }, // 0x91 + { 17464, 31, 39, 47, 8, -38 }, // 0x92 + { 17616, 31, 39, 47, 8, -38 }, // 0x93 + { 17768, 31, 39, 47, 8, -38 }, // 0x94 + { 17920, 31, 39, 47, 8, -38 }, // 0x95 + { 18072, 31, 39, 47, 8, -38 }, // 0x96 + { 18224, 31, 39, 47, 8, -38 }, // 0x97 + { 18376, 31, 39, 47, 8, -38 }, // 0x98 + { 18528, 31, 39, 47, 8, -38 }, // 0x99 + { 18680, 31, 39, 47, 8, -38 }, // 0x9A + { 18832, 31, 39, 47, 8, -38 }, // 0x9B + { 18984, 31, 39, 47, 8, -38 }, // 0x9C + { 19136, 31, 39, 47, 8, -38 }, // 0x9D + { 19288, 31, 39, 47, 8, -38 }, // 0x9E + { 19440, 31, 39, 47, 8, -38 }, // 0x9F + { 19592, 0, 0, 18, 0, 1 }, // 0xA0 + { 19592, 9, 46, 21, 6, -32 }, // 0xA1 + { 19644, 30, 58, 35, 3, -44 }, // 0xA2 + { 19862, 34, 47, 35, 0, -45 }, // 0xA3 + { 20062, 32, 33, 35, 1, -38 }, // 0xA4 + { 20194, 35, 45, 35, 0, -44 }, // 0xA5 + { 20391, 7, 59, 18, 5, -44 }, // 0xA6 + { 20443, 31, 59, 35, 2, -45 }, // 0xA7 + { 20672, 21, 8, 21, 0, -44 }, // 0xA8 + { 20693, 47, 47, 46, 0, -45 }, // 0xA9 + { 20970, 21, 23, 23, 1, -45 }, // 0xAA + { 21031, 29, 29, 35, 3, -30 }, // 0xAB + { 21137, 31, 22, 37, 3, -32 }, // 0xAC + { 21223, 17, 8, 21, 4, -19 }, // 0xAD + { 21240, 47, 47, 46, 0, -45 }, // 0xAE + { 21517, 36, 7, 35, -1, -54 }, // 0xAF + { 21549, 19, 19, 25, 3, -45 }, // 0xB0 + { 21595, 31, 43, 35, 2, -42 }, // 0xB1 + { 21762, 18, 23, 21, 1, -45 }, // 0xB2 + { 21814, 19, 23, 21, 1, -45 }, // 0xB3 + { 21869, 14, 9, 21, 6, -44 }, // 0xB4 + { 21885, 30, 46, 36, 3, -32 }, // 0xB5 + { 22058, 35, 57, 35, 0, -44 }, // 0xB6 + { 22308, 9, 9, 21, 6, -26 }, // 0xB7 + { 22319, 17, 11, 21, 1, 1 }, // 0xB8 + { 22343, 12, 23, 21, 3, -45 }, // 0xB9 + { 22378, 21, 23, 23, 1, -45 }, // 0xBA + { 22439, 29, 29, 35, 3, -30 }, // 0xBB + { 22545, 49, 46, 53, 3, -44 }, // 0xBC + { 22827, 48, 47, 53, 3, -45 }, // 0xBD + { 23109, 51, 47, 53, 1, -45 }, // 0xBE + { 23409, 33, 47, 38, 3, -32 }, // 0xBF + { 23603, 45, 58, 46, 0, -57 }, // 0xC0 + { 23930, 45, 58, 46, 0, -57 }, // 0xC1 + { 24257, 45, 58, 46, 0, -57 }, // 0xC2 + { 24584, 45, 57, 46, 0, -56 }, // 0xC3 + { 24905, 45, 57, 46, 0, -56 }, // 0xC4 + { 25226, 45, 54, 46, 0, -53 }, // 0xC5 + { 25530, 63, 45, 63, -3, -44 }, // 0xC6 + { 25885, 39, 57, 46, 3, -45 }, // 0xC7 + { 26163, 34, 58, 42, 5, -57 }, // 0xC8 + { 26410, 34, 58, 42, 5, -57 }, // 0xC9 + { 26657, 34, 58, 42, 5, -57 }, // 0xCA + { 26904, 34, 57, 42, 5, -56 }, // 0xCB + { 27147, 14, 58, 18, -1, -57 }, // 0xCC + { 27249, 14, 58, 18, 4, -57 }, // 0xCD + { 27351, 21, 58, 18, -1, -57 }, // 0xCE + { 27504, 21, 57, 18, -2, -56 }, // 0xCF + { 27654, 42, 45, 46, 0, -44 }, // 0xD0 + { 27891, 35, 57, 46, 5, -56 }, // 0xD1 + { 28141, 43, 59, 49, 3, -57 }, // 0xD2 + { 28459, 43, 59, 49, 3, -57 }, // 0xD3 + { 28777, 43, 59, 49, 3, -57 }, // 0xD4 + { 29095, 43, 58, 49, 3, -56 }, // 0xD5 + { 29407, 43, 58, 49, 3, -56 }, // 0xD6 + { 29719, 30, 30, 37, 3, -36 }, // 0xD7 + { 29832, 45, 50, 49, 2, -47 }, // 0xD8 + { 30114, 35, 59, 46, 5, -57 }, // 0xD9 + { 30373, 35, 59, 46, 5, -57 }, // 0xDA + { 30632, 35, 59, 46, 5, -57 }, // 0xDB + { 30891, 35, 58, 46, 5, -56 }, // 0xDC + { 31145, 42, 58, 42, 0, -57 }, // 0xDD + { 31450, 34, 45, 42, 5, -44 }, // 0xDE + { 31642, 32, 47, 38, 4, -45 }, // 0xDF + { 31830, 31, 47, 35, 2, -45 }, // 0xE0 + { 32013, 31, 47, 35, 2, -45 }, // 0xE1 + { 32196, 31, 47, 35, 2, -45 }, // 0xE2 + { 32379, 31, 46, 35, 2, -44 }, // 0xE3 + { 32558, 31, 46, 35, 2, -44 }, // 0xE4 + { 32737, 31, 50, 35, 2, -48 }, // 0xE5 + { 32931, 50, 35, 56, 3, -33 }, // 0xE6 + { 33150, 30, 45, 35, 3, -33 }, // 0xE7 + { 33319, 31, 47, 35, 2, -45 }, // 0xE8 + { 33502, 31, 47, 35, 2, -45 }, // 0xE9 + { 33685, 31, 47, 35, 2, -45 }, // 0xEA + { 33868, 31, 46, 35, 2, -44 }, // 0xEB + { 34047, 14, 46, 18, -1, -45 }, // 0xEC + { 34128, 14, 46, 18, 4, -45 }, // 0xED + { 34209, 21, 46, 18, -2, -45 }, // 0xEE + { 34330, 21, 45, 18, -2, -44 }, // 0xEF + { 34449, 33, 46, 38, 3, -44 }, // 0xF0 + { 34639, 30, 45, 38, 4, -44 }, // 0xF1 + { 34808, 33, 47, 38, 3, -45 }, // 0xF2 + { 35002, 33, 47, 38, 3, -45 }, // 0xF3 + { 35196, 33, 47, 38, 3, -45 }, // 0xF4 + { 35390, 33, 46, 38, 3, -44 }, // 0xF5 + { 35580, 33, 46, 38, 3, -44 }, // 0xF6 + { 35770, 31, 32, 35, 2, -37 }, // 0xF7 + { 35894, 33, 38, 38, 3, -35 }, // 0xF8 + { 36051, 30, 47, 38, 4, -45 }, // 0xF9 + { 36228, 30, 47, 38, 4, -45 }, // 0xFA + { 36405, 30, 47, 38, 4, -45 }, // 0xFB + { 36582, 30, 46, 38, 4, -44 }, // 0xFC + { 36755, 34, 60, 35, 0, -45 }, // 0xFD + { 37010, 32, 58, 38, 4, -44 }, // 0xFE + { 37242, 34, 59, 35, 0, -44 } }; // 0xFF + +const GFXfont myFont32pt8b PROGMEM = { + (uint8_t *)myFont32pt8bBitmaps, + (GFXglyph *)myFont32pt8bGlyphs, + 0x20, 0xFF, 72 }; + +// Approx. 39068 bytes diff --git a/library.json b/library.json index ac3228d..7b1724d 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "TFT_eSPI", - "version": "1.4.2", + "version": "1.4.3", "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 9de9fc2..707afde 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=TFT_eSPI -version=1.4.2 +version=1.4.3 author=Bodmer maintainer=Bodmer sentence=A fast TFT graphics library for ESP8266 and ESP32 processors for the Arduino IDE From 0b8e9572034d4527dd23b442a6fa814430bdc2be Mon Sep 17 00:00:00 2001 From: Bodmer Date: Fri, 8 Feb 2019 18:50:03 +0000 Subject: [PATCH 247/287] Remove screen server code --- examples/Generic/Local_Custom_Fonts/Local_Custom_Fonts.ino | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/examples/Generic/Local_Custom_Fonts/Local_Custom_Fonts.ino b/examples/Generic/Local_Custom_Fonts/Local_Custom_Fonts.ino index b26f11e..67deb9a 100644 --- a/examples/Generic/Local_Custom_Fonts/Local_Custom_Fonts.ino +++ b/examples/Generic/Local_Custom_Fonts/Local_Custom_Fonts.ino @@ -45,7 +45,6 @@ #include "SPI.h" #include "TFT_eSPI.h" -#include "TFT_eFEX.h" // The custom font file attached to this sketch must be included #include "MyFont.h" @@ -58,7 +57,6 @@ // Use hardware SPI TFT_eSPI tft = TFT_eSPI(); -TFT_eFEX fex = TFT_eFEX(&tft); void setup(void) { @@ -111,7 +109,7 @@ void loop() { tft.setFreeFont(MYFONT32); // Select the font tft.drawString(TEST_TEXT, 160, 140, GFXFF); // Print the test text in the custom font delay(2000); -fex.screenServer(); + // Setting textDatum does nothing when using tft.print tft.fillScreen(TFT_BLUE); // Clear screen tft.setCursor(0,60); // To be compatible with Adafruit_GFX the cursor datum is always bottom left From b5a74a016ae7c04f759e6a8ed44ef9bbe5adf26f Mon Sep 17 00:00:00 2001 From: Bodmer Date: Sun, 10 Feb 2019 01:25:34 +0000 Subject: [PATCH 248/287] Fix #301 --- TFT_eSPI.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/TFT_eSPI.h b/TFT_eSPI.h index 512bedc..b1b0cb0 100644 --- a/TFT_eSPI.h +++ b/TFT_eSPI.h @@ -389,6 +389,7 @@ #if !defined (ESP32_PARALLEL) + // Read from display using SPI or software SPI #if defined (ESP8266) && defined (TFT_SDA_READ) // Use a bit banged function call for ESP8266 and bi-directional SDA pin @@ -398,6 +399,12 @@ // Use a SPI read transfer #define tft_Read_8() spi.transfer(0) #endif + + // Make sure TFT_MISO is defined if not used to avoid an error message + #ifndef TFT_MISO + #define TFT_MISO -1 + #endif + #endif From 6a42e4b16d3f44e2665c85e5f010b3fca74956e7 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Sun, 10 Feb 2019 11:06:40 +0000 Subject: [PATCH 249/287] Remove debug lines to fix rendering part of #303 --- TFT_eSPI.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/TFT_eSPI.cpp b/TFT_eSPI.cpp index 5e24cd8..4dc8679 100644 --- a/TFT_eSPI.cpp +++ b/TFT_eSPI.cpp @@ -4598,12 +4598,10 @@ int16_t TFT_eSPI::drawString(const char *string, int32_t poX, int32_t poY, uint8 } else #endif -Serial.print("sumX="); - while (*string) { - sumX += drawChar(*(string++), poX+sumX, poY, font); - Serial.print(sumX); - } -Serial.println(); + { + while (*string) sumX += drawChar(*(string++), poX+sumX, poY, font); + } + //vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv DEBUG vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv // Switch on debugging for the padding areas //#define PADDING_DEBUG From 1e1e888fa0767e3e72c93950921bb5ed281fba2c Mon Sep 17 00:00:00 2001 From: Bodmer Date: Sun, 10 Feb 2019 21:36:40 +0000 Subject: [PATCH 250/287] Support extended font formats Adafruit_GFX font support extended to Unidode Basic Multilingual Plane Print stream deocdes UTF-8 Smooth font ascent and descent (affects line spacing) changed to rely on metrics provided by Processing IDE (issue #303) Bug fix for font rendering with no background on RLE native fonts --- Extensions/Smooth_font.cpp | 17 ++- Extensions/Smooth_font.h | 6 - Extensions/Sprite.cpp | 43 +++--- Extensions/Sprite.h | 2 +- Fonts/GFXFF/gfxfont.h | 4 +- TFT_eSPI.cpp | 113 ++++++++------- TFT_eSPI.h | 7 +- .../Create_font/Create_font.pde | 130 +++++++++++------- .../Read_User_Setup/Read_User_Setup.ino | 4 +- library.json | 2 +- library.properties | 2 +- 11 files changed, 188 insertions(+), 142 deletions(-) diff --git a/Extensions/Smooth_font.cpp b/Extensions/Smooth_font.cpp index 855aa62..8d7628f 100644 --- a/Extensions/Smooth_font.cpp +++ b/Extensions/Smooth_font.cpp @@ -97,7 +97,7 @@ void TFT_eSPI::loadFont(String fontName) 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 + // These next gFont values might 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; @@ -147,11 +147,19 @@ void TFT_eSPI::loadMetrics(uint16_t gCount) 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 + //Serial.print("Unicode = 0x"); Serial.print(gUnicode[gNum], HEX); Serial.print(", gHeight = "); Serial.println(gHeight[gNum]); + //Serial.print("Unicode = 0x"); Serial.print(gUnicode[gNum], HEX); Serial.print(", gWidth = "); Serial.println(gWidth[gNum]); + //Serial.print("Unicode = 0x"); Serial.print(gUnicode[gNum], HEX); Serial.print(", gxAdvance = "); Serial.println(gxAdvance[gNum]); + //Serial.print("Unicode = 0x"); Serial.print(gUnicode[gNum], HEX); Serial.print(", gdY = "); Serial.println(gdY[gNum]); + + // Different glyph sets have different ascent values not always based on "d", so we could get + // the maximum glyph ascent by checking all characters. BUT this method can generate bad values + // for non-existant glyphs, so we will reply on processing for the value and disable this code for now... + /* 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)) + // Try to avoid UTF coding values and characters that tend to give duff values + if (((gUnicode[gNum] > 0x20) && (gUnicode[gNum] < 0x7F)) || (gUnicode[gNum] > 0xA0)) { gFont.maxAscent = gdY[gNum]; #ifdef SHOW_ASCENT_DESCENT @@ -159,6 +167,7 @@ void TFT_eSPI::loadMetrics(uint16_t gCount) #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) diff --git a/Extensions/Smooth_font.h b/Extensions/Smooth_font.h index 2c7faa2..1bc9fd2 100644 --- a/Extensions/Smooth_font.h +++ b/Extensions/Smooth_font.h @@ -8,9 +8,6 @@ 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); virtual void drawGlyph(uint16_t code); @@ -44,9 +41,6 @@ fontMetrics gFont = { 0, 0, 0, 0, 0, 0, 0 }; 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: diff --git a/Extensions/Sprite.cpp b/Extensions/Sprite.cpp index 6bc58b2..3782775 100644 --- a/Extensions/Sprite.cpp +++ b/Extensions/Sprite.cpp @@ -1312,13 +1312,16 @@ 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) { + uint16_t uniCode = decodeUTF8(utf8); + + if (!uniCode) return 1; + if (utf8 == '\r') return 1; #ifdef SMOOTH_FONT if(this->fontLoaded) { - uint16_t unicode = decodeUTF8(utf8); - if (unicode < 32 && utf8 != '\n') return 1; + if (uniCode < 32 && utf8 != '\n') return 1; //fontFile = SPIFFS.open( _gFontFilename, "r" ); //fontFile = SPIFFS.open( this->_gFontFilename, "r" ); @@ -1330,7 +1333,8 @@ size_t TFT_eSprite::write(uint8_t utf8) //} //Serial.print("Decoded Unicode = 0x");Serial.println(unicode,HEX); - drawGlyph(unicode); + drawGlyph(uniCode); + //fontFile.close(); return 1; } @@ -1338,10 +1342,8 @@ size_t TFT_eSprite::write(uint8_t utf8) if (!_created ) 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 1; + if (uniCode == '\n') uniCode+=22; // Make it a valid space character to stop errors + else if (uniCode < 32) return 1; uint16_t width = 0; uint16_t height = 0; @@ -1362,7 +1364,7 @@ size_t TFT_eSprite::write(uint8_t utf8) if (textfont == 2) { if (utf8 > 127) return 1; - // 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 @@ -1380,7 +1382,6 @@ size_t TFT_eSprite::write(uint8_t utf8) { if (utf8 > 127) return 1; // 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 ); } @@ -1420,15 +1421,14 @@ size_t TFT_eSprite::write(uint8_t utf8) } // Custom GFX font else { - if(utf8 == '\n') { 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 1; - if (uniCode < (uint8_t)pgm_read_byte(&gfxFont->first)) return 1; + if (uniCode > pgm_read_word(&gfxFont->last )) return 1; + if (uniCode < pgm_read_word(&gfxFont->first)) return 1; - uint8_t c2 = uniCode - pgm_read_byte(&gfxFont->first); + uint8_t c2 = uniCode - pgm_read_word(&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); @@ -1456,7 +1456,7 @@ size_t TFT_eSprite::write(uint8_t utf8) ** 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) +void TFT_eSprite::drawChar(int32_t x, int32_t y, uint16_t c, uint32_t color, uint32_t bg, uint8_t size) { if (!_created ) return; @@ -1466,6 +1466,7 @@ void TFT_eSprite::drawChar(int32_t x, int32_t y, unsigned char c, uint32_t color ((y + 8 * size - 1) < 0)) // Clip top return; + if (c < 32) return; #ifdef LOAD_GLCD //>>>>>>>>>>>>>>>>>> #ifdef LOAD_GFXFF @@ -1534,15 +1535,15 @@ void TFT_eSprite::drawChar(int32_t x, int32_t y, unsigned char c, uint32_t color #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 ))) + if ((c >= pgm_read_word(&gfxFont->first)) && (c <= pgm_read_word(&gfxFont->last ))) { //>>>>>>>>>>>>>>>>>>>>>>>>>>> - c -= pgm_read_byte(&gfxFont->first); + c -= pgm_read_word(&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); + uint32_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); @@ -1597,15 +1598,19 @@ void TFT_eSprite::drawChar(int32_t x, int32_t y, unsigned char c, uint32_t color ** Function name: drawChar ** Description: draw a unicode onto the screen *************************************************************************************x*/ + // Any UTF-8 decoding must be done before calling drawChar() int16_t TFT_eSprite::drawChar(uint16_t uniCode, int32_t x, int32_t y) { return drawChar(uniCode, x, y, textfont); } + // Any UTF-8 decoding must be done before calling drawChar() int16_t TFT_eSprite::drawChar(uint16_t uniCode, int32_t x, int32_t y, uint8_t font) { if (!_created ) return 0; + if (!uniCode) return 0; + if (font==1) { #ifdef LOAD_GLCD @@ -1630,9 +1635,9 @@ int16_t TFT_eSprite::drawChar(uint16_t uniCode, int32_t x, int32_t y, uint8_t fo } else { - if((uniCode >= pgm_read_byte(&gfxFont->first)) && (uniCode <= pgm_read_byte(&gfxFont->last) )) + if((uniCode >= pgm_read_word(&gfxFont->first)) && (uniCode <= pgm_read_word(&gfxFont->last) )) { - uint8_t c2 = uniCode - pgm_read_byte(&gfxFont->first); + uint16_t c2 = uniCode - pgm_read_word(&gfxFont->first); GFXglyph *glyph = &(((GFXglyph *)pgm_read_dword(&gfxFont->glyph))[c2]); return pgm_read_byte(&glyph->xAdvance) * textsize; } diff --git a/Extensions/Sprite.h b/Extensions/Sprite.h index 2bf800c..5dea24e 100644 --- a/Extensions/Sprite.h +++ b/Extensions/Sprite.h @@ -31,7 +31,7 @@ class TFT_eSprite : public TFT_eSPI { void drawPixel(int32_t x, int32_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), + void drawChar(int32_t x, int32_t y, uint16_t c, uint32_t color, uint32_t bg, uint8_t size), fillSprite(uint32_t color), diff --git a/Fonts/GFXFF/gfxfont.h b/Fonts/GFXFF/gfxfont.h index eae3997..cd2bccc 100644 --- a/Fonts/GFXFF/gfxfont.h +++ b/Fonts/GFXFF/gfxfont.h @@ -12,7 +12,7 @@ #ifdef LOAD_GFXFF typedef struct { // Data stored PER GLYPH - uint16_t bitmapOffset; // Pointer into GFXfont->bitmap + uint32_t bitmapOffset; // Pointer into GFXfont->bitmap uint8_t width, height; // Bitmap dimensions in pixels uint8_t xAdvance; // Distance to advance cursor (x axis) int8_t xOffset, yOffset; // Dist from cursor pos to UL corner @@ -21,7 +21,7 @@ typedef struct { // Data stored PER GLYPH typedef struct { // Data stored for FONT AS A WHOLE: uint8_t *bitmap; // Glyph bitmaps, concatenated GFXglyph *glyph; // Glyph array - uint8_t first, last; // ASCII extents + uint16_t first, last; // ASCII extents uint8_t yAdvance; // Newline distance (y axis) } GFXfont; diff --git a/TFT_eSPI.cpp b/TFT_eSPI.cpp index 4dc8679..9c4fc66 100644 --- a/TFT_eSPI.cpp +++ b/TFT_eSPI.cpp @@ -2272,21 +2272,22 @@ int16_t TFT_eSPI::textWidth(const char *string) int16_t TFT_eSPI::textWidth(const char *string, uint8_t font) { - int32_t str_width = 0; + int32_t str_width = 0; + uint16_t uniCode = 0; #ifdef SMOOTH_FONT if(fontLoaded) { while (*string) { - uint16_t unicode = decodeUTF8(*string++); - if (unicode) + uniCode = decodeUTF8(*string++); + if (uniCode) { - if (unicode == 0x20) str_width += gFont.spaceWidth; + if (uniCode == 0x20) str_width += gFont.spaceWidth; else { uint16_t gNum = 0; - bool found = getUnicodeIndex(unicode, &gNum); + bool found = getUnicodeIndex(uniCode, &gNum); if (found) { if(str_width == 0 && gdX[gNum] < 0) str_width -= gdX[gNum]; @@ -2302,18 +2303,15 @@ int16_t TFT_eSPI::textWidth(const char *string, uint8_t font) } #endif - unsigned char uniCode; - char *widthtable; - if (font>1 && font<9) { - widthtable = (char *)pgm_read_dword( &(fontdata[font].widthtbl ) ) - 32; //subtract the 32 outside the loop + char *widthtable = (char *)pgm_read_dword( &(fontdata[font].widthtbl ) ) - 32; //subtract the 32 outside the loop while (*string) { uniCode = *(string++); if (uniCode > 31 && uniCode < 128) - str_width += pgm_read_byte( widthtable + uniCode); // Normally we need to subract 32 from uniCode + str_width += pgm_read_byte( widthtable + uniCode); // Normally we need to subtract 32 from uniCode else str_width += pgm_read_byte( widthtable + 32); // Set illegal character = space width } } @@ -2326,9 +2324,9 @@ int16_t TFT_eSPI::textWidth(const char *string, uint8_t font) while (*string) { uniCode = decodeUTF8(*string++); - if ((uniCode >= (uint8_t)pgm_read_byte(&gfxFont->first)) && (uniCode <= (uint8_t)pgm_read_byte(&gfxFont->last ))) + if ((uniCode >= pgm_read_word(&gfxFont->first)) && (uniCode <= pgm_read_word(&gfxFont->last ))) { - uniCode -= pgm_read_byte(&gfxFont->first); + uniCode -= pgm_read_word(&gfxFont->first); GFXglyph *glyph = &(((GFXglyph *)pgm_read_dword(&gfxFont->glyph))[uniCode]); // If this is not the last character or is a digit then use xAdvance if (*string || isDigits) str_width += pgm_read_byte(&glyph->xAdvance); @@ -2393,7 +2391,7 @@ int16_t TFT_eSPI::fontHeight(void) ** Function name: drawChar ** Description: draw a single character in the Adafruit GLCD font ***************************************************************************************/ -void TFT_eSPI::drawChar(int32_t x, int32_t y, unsigned char c, uint32_t color, uint32_t bg, uint8_t size) +void TFT_eSPI::drawChar(int32_t x, int32_t y, uint16_t c, uint32_t color, uint32_t bg, uint8_t size) { if ((x >= _width) || // Clip right (y >= _height) || // Clip bottom @@ -2498,17 +2496,17 @@ void TFT_eSPI::drawChar(int32_t x, int32_t y, unsigned char c, uint32_t color, u #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 ))) + if ((c >= pgm_read_word(&gfxFont->first)) && (c <= pgm_read_word(&gfxFont->last ))) { spi_begin(); inTransaction = true; //>>>>>>>>>>>>>>>>>>>>>>>>>>> - c -= pgm_read_byte(&gfxFont->first); + c -= pgm_read_word(&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); + uint32_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); @@ -3991,18 +3989,18 @@ uint16_t TFT_eSPI::decodeUTF8(uint8_t *buf, uint16_t *index, uint16_t remaining) ***************************************************************************************/ size_t TFT_eSPI::write(uint8_t utf8) { + uint16_t uniCode = decodeUTF8(utf8); + + if (!uniCode) return 1; + if (utf8 == '\r') return 1; #ifdef SMOOTH_FONT if(fontLoaded) { - uint16_t unicode = decodeUTF8(utf8); - - //Serial.print("UniCode="); Serial.println(unicode); + //Serial.print("UniCode="); Serial.println(uniCode); //Serial.print("UTF8 ="); Serial.println(utf8); - if (!unicode) return 1; - //fontFile = SPIFFS.open( _gFontFilename, "r" ); //if(!fontFile) @@ -4011,16 +4009,15 @@ size_t TFT_eSPI::write(uint8_t utf8) // return 1; //} - drawGlyph(unicode); + 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 1; + if (uniCode == '\n') uniCode+=22; // Make it a valid space character to stop errors + else if (uniCode < 32) return 1; uint16_t width = 0; uint16_t height = 0; @@ -4040,8 +4037,8 @@ size_t TFT_eSPI::write(uint8_t utf8) #ifdef LOAD_FONT2 if (textfont == 2) { - if (utf8 > 127) return 1; - // This is 20us faster than using the fontdata structure (0.443ms per character instead of 0.465ms) + if (uniCode > 127) return 1; + 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 @@ -4057,9 +4054,8 @@ size_t TFT_eSPI::write(uint8_t utf8) { if ((textfont>2) && (textfont<9)) { - if (utf8 > 127) return 1; + if (uniCode > 127) return 1; // 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 ); } @@ -4098,18 +4094,15 @@ size_t TFT_eSPI::write(uint8_t utf8) } // Custom GFX font else { - uniCode = (uint8_t)decodeUTF8(utf8); - if (!uniCode) return 1; - if(utf8 == '\n') { cursor_x = 0; cursor_y += (int16_t)textsize * (uint8_t)pgm_read_byte(&gfxFont->yAdvance); } else { - if (uniCode > (uint8_t)pgm_read_byte(&gfxFont->last )) return 1; - if (uniCode < (uint8_t)pgm_read_byte(&gfxFont->first)) return 1; + if (uniCode > pgm_read_word(&gfxFont->last )) return 1; + if (uniCode < pgm_read_word(&gfxFont->first)) return 1; - uint8_t c2 = uniCode - pgm_read_byte(&gfxFont->first); + uint16_t c2 = uniCode - pgm_read_word(&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); @@ -4136,17 +4129,17 @@ size_t TFT_eSPI::write(uint8_t utf8) /*************************************************************************************** ** Function name: drawChar -** Description: draw a Unicode onto the screen +** Description: draw a Unicode glyph onto the screen ***************************************************************************************/ -int16_t TFT_eSPI::drawChar(uint16_t utf8, int32_t x, int32_t y) + // Any UTF-8 decoding must be done before calling drawChar() +int16_t TFT_eSPI::drawChar(uint16_t uniCode, int32_t x, int32_t y) { - return drawChar(utf8, x, y, textfont); + return drawChar(uniCode, x, y, textfont); } -int16_t TFT_eSPI::drawChar(uint16_t utf8, int32_t x, int32_t y, uint8_t font) + // Any UTF-8 decoding must be done before calling drawChar() +int16_t TFT_eSPI::drawChar(uint16_t uniCode, int32_t x, int32_t y, uint8_t font) { - - uint16_t uniCode = decodeUTF8(utf8); if (!uniCode) return 0; if (font==1) @@ -4173,9 +4166,9 @@ int16_t TFT_eSPI::drawChar(uint16_t utf8, int32_t x, int32_t y, uint8_t font) } else { - if((uniCode >= pgm_read_byte(&gfxFont->first)) && (uniCode <= pgm_read_byte(&gfxFont->last) )) + if((uniCode >= pgm_read_word(&gfxFont->first)) && (uniCode <= pgm_read_word(&gfxFont->last) )) { - uint8_t c2 = uniCode - pgm_read_byte(&gfxFont->first); + uint16_t c2 = uniCode - pgm_read_word(&gfxFont->first); GFXglyph *glyph = &(((GFXglyph *)pgm_read_dword(&gfxFont->glyph))[c2]); return pgm_read_byte(&glyph->xAdvance) * textsize; } @@ -4197,7 +4190,6 @@ int16_t TFT_eSPI::drawChar(uint16_t utf8, int32_t x, int32_t y, uint8_t font) #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; @@ -4211,7 +4203,6 @@ int16_t TFT_eSPI::drawChar(uint16_t utf8, int32_t x, int32_t y, uint8_t font) { 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 ); @@ -4309,6 +4300,7 @@ int16_t TFT_eSPI::drawChar(uint16_t utf8, int32_t x, int32_t y, uint8_t font) // Font is not 2 and hence is RLE encoded { spi_begin(); + inTransaction = true; w *= height; // Now w is total number of pixels in the character if ((textsize != 1) || (textcolor == textbgcolor)) { @@ -4360,6 +4352,7 @@ int16_t TFT_eSPI::drawChar(uint16_t utf8, int32_t x, int32_t y, uint8_t font) } } + inTransaction = false; spi_end(); } else // Text colour != background && textsize = 1 @@ -4442,7 +4435,7 @@ int16_t TFT_eSPI::drawString(const char *string, int32_t poX, int32_t poY) return drawString(string, poX, poY, textfont); } -// With font number +// With font number. Note: font number is over-ridden if a smooth font is loaded int16_t TFT_eSPI::drawString(const char *string, int32_t poX, int32_t poY, uint8_t font) { int16_t sumX = 0; @@ -4553,15 +4546,15 @@ int16_t TFT_eSPI::drawString(const char *string, int32_t poX, int32_t poY, uint8 { cheight = (glyph_ab + glyph_bb) * textsize; // Get the offset for the first character only to allow for negative offsets - uint8_t c2 = 0; + uint16_t c2 = 0; uint16_t len = strlen(string); uint16_t n = 0; while (n < len && c2 == 0) c2 = decodeUTF8((uint8_t*)string, &n, len - n); - if((c2 >= pgm_read_byte(&gfxFont->first)) && (c2 <= pgm_read_byte(&gfxFont->last) )) + if((c2 >= pgm_read_word(&gfxFont->first)) && (c2 <= pgm_read_word(&gfxFont->last) )) { - c2 -= pgm_read_byte(&gfxFont->first); + c2 -= pgm_read_word(&gfxFont->first); GFXglyph *glyph = &(((GFXglyph *)pgm_read_dword(&gfxFont->glyph))[c2]); xo = pgm_read_byte(&glyph->xOffset) * textsize; // Adjust for negative xOffset @@ -4576,6 +4569,9 @@ int16_t TFT_eSPI::drawString(const char *string, int32_t poX, int32_t poY, uint8 } #endif + uint16_t len = strlen(string); + uint16_t n = 0; + #ifdef SMOOTH_FONT if(fontLoaded) { @@ -4584,14 +4580,13 @@ int16_t TFT_eSPI::drawString(const char *string, int32_t poX, int32_t poY, uint8 //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); + uint16_t uniCode = decodeUTF8((uint8_t*)string, &n, len - n); + drawGlyph(uniCode); } sumX += cwidth; //fontFile.close(); @@ -4599,7 +4594,11 @@ int16_t TFT_eSPI::drawString(const char *string, int32_t poX, int32_t poY, uint8 else #endif { - while (*string) sumX += drawChar(*(string++), poX+sumX, poY, font); + while (n < len) + { + uint16_t uniCode = decodeUTF8((uint8_t*)string, &n, len - n); + sumX += drawChar(uniCode, poX+sumX, poY, font); + } } //vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv DEBUG vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv @@ -4669,7 +4668,7 @@ int16_t TFT_eSPI::drawString(const char *string, int32_t poX, int32_t poY, uint8 #endif //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ DEBUG ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -return sumX; +return sumX + poX; } @@ -4782,7 +4781,7 @@ int16_t TFT_eSPI::drawFloat(float floatNumber, uint8_t dp, int32_t poX, int32_t // No chance of overflow from here on // Get integer part - unsigned long temp = (unsigned long)floatNumber; + uint32_t temp = (uint32_t)floatNumber; // Put integer part into array ltoa(temp, str + ptr, 10); @@ -4830,7 +4829,7 @@ void TFT_eSPI::setFreeFont(const GFXfont *f) glyph_ab = 0; glyph_bb = 0; - uint8_t numChars = pgm_read_byte(&gfxFont->last) - pgm_read_byte(&gfxFont->first); + uint16_t numChars = pgm_read_word(&gfxFont->last) - pgm_read_word(&gfxFont->first); // Find the biggest above and below baseline offsets for (uint8_t c = 0; c < numChars; c++) diff --git a/TFT_eSPI.h b/TFT_eSPI.h index b1b0cb0..55110f0 100644 --- a/TFT_eSPI.h +++ b/TFT_eSPI.h @@ -668,7 +668,7 @@ class TFT_eSPI : public Print { // These are virtual so the TFT_eSprite class can override them with sprite specific functions virtual void drawPixel(int32_t x, int32_t y, uint32_t color), - drawChar(int32_t x, int32_t y, unsigned char c, uint32_t color, uint32_t bg, uint8_t size), + drawChar(int32_t x, int32_t y, uint16_t 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), @@ -813,6 +813,8 @@ class TFT_eSPI : public Print { void writeColor(uint16_t color, uint32_t len); // Write colours without transaction overhead void endWrite(void); // End SPI transaction + uint16_t decodeUTF8(uint8_t *buf, uint16_t *index, uint16_t remaining); + uint16_t decodeUTF8(uint8_t c); size_t write(uint8_t); #ifdef TFT_SDA_READ @@ -838,6 +840,9 @@ class TFT_eSPI : public Print { int16_t _xpivot; // x pivot point coordinate int16_t _ypivot; // x pivot point coordinate + uint8_t decoderState = 0; // UTF8 decoder state + uint16_t decoderBuffer; // Unicode code-point buffer + private: inline void spi_begin() __attribute__((always_inline)); diff --git a/Tools/Create_Smooth_Font/Create_font/Create_font.pde b/Tools/Create_Smooth_Font/Create_font/Create_font.pde index 1363b6b..6832248 100644 --- a/Tools/Create_Smooth_Font/Create_font/Create_font.pde +++ b/Tools/Create_Smooth_Font/Create_font/Create_font.pde @@ -1,6 +1,8 @@ // 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 +// Select the font, size and character ranges in the user configuration section +// of this sketch, which starts at line 120. Instructions start at line 50. + /* Software License Agreement (FreeBSD License) @@ -36,47 +38,61 @@ Software License Agreement (FreeBSD License) //////////////////////////////////////////////////////////////////////////////////////////////// - // 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 +// Coded by Bodmer January 2018, updated 10/2/19 +// Version 0.8 -// 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/ +// >>>>>>>>>>>>>>>>>>>> INSTRUCTIONS <<<<<<<<<<<<<<<<<<<< + +// See comments below in code for specifying the font parameters (point size, +// unicode blocks to include etc). Ranges of characters (glyphs) and specific +// individual glyphs can be included in the created "*.vlw" font file. // Created fonts are saved in the sketches "FontFiles" folder. Press Ctrl+K to -// see that folder. +// see that folder location. -// 16 bit unicodes in the range 0x0000 - 0xFFFF are supported. +// 16 bit Unicode point codes in the range 0x0000 - 0xFFFF are supported. +// Codes 0-31 are control codes such as "tab" and "carraige return" etc. +// and 32 is a "space", these should NOT be included. // 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. +// 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. +// To maximise rendering performance and the memory consumed only include the characters +// you will use. Characters at the start of the file will render faster than those at +// the end due to the buffering and file seeking overhead. + +// The inclusion of "non-existant" characters in a font may give unpredicatable results +// when rendering with the TFT_eSPI library. The Processing sketch window that pops up +// to show the font characters will print "boxes" (also known as Tofu!) for non existant +// characters. // 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. +// When the sketch is run it will generate a file called "System_Font_List.txt" in the +// sketch "FontFiles" folder, press Ctrl+K to see it. Open the file in a text editor to +// view it. This list provides the font reference number needed below to locate that +// font on your system. + +// The sketch also lists all the available system fonts to the console, you can increase +// the 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: +// 3000 lines, then save, then run Processing again: + +// console.length=3000; // Line 4 in file +// console.scrollback.lines=3000; // Line 7 in file - /* - console.length=1000 // Line 4 in file - console.scrollback.lines=1000 // Line 7 in file - */ // Useful links: - /* +/* https://en.wikipedia.org/wiki/Unicode_font @@ -86,33 +102,39 @@ Software License Agreement (FreeBSD License) 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 - */ -//////////////////////////////////////////////////////////////////////////////////////////////// + >>>>>>>>>>>>>>>>>>>> END OF INSTRUCTIONS <<<<<<<<<<<<<<<<<<<< */ -import java.awt.Desktop; + +import java.awt.Desktop; // Required to allow sketch to open file windows + + +//////////////////////////////////////////////////////////////////////////////////////////////// // >>>>>>>>>> USER CONFIGURED PARAMETERS START HERE <<<<<<<<<< +// Use font number or name, -1 for fontNumber means use fontName below, a value >=0 means use system font number from list. +// When the sketch is run it will generate a file called "systemFontList.txt" in the sketch folder, press Ctrl+K to see it. +// Open the "systemFontList.txt" in a text editor to view the font files and reference numbers for your system. -// Use font name for ttf files placed in the "Data" folder or the font number seen in IDE Console for system fonts +int fontNumber = -1; // << Use [Number] in brackets from the fonts listed. + +// OR 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 / +// | 1 2 | Maximum filename size for SPIFFS is 31 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 fontName = "Final-Frontier"; // Manually crop the filename length later after creation if needed + // Note: SPIFFS does NOT accept underscore in a filename! +String fontType = ".ttf"; //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; +// Define the font size in points for the TFT_eSPI font file +int fontSize = 20; // Font size to use in the Processing sketch display window that pops up (can be different to above) int displayFontSize = 28; @@ -125,7 +147,7 @@ int displayFontSize = 28; 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 + // 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 @@ -298,20 +320,20 @@ static final int[] unicodeBlocks = { //0x0061, 0x007A, //Example custom range (Lower case a-z) }; -// Here we specify specific individual Unicodes to be included (appended at end of selected range) +// Here we specify particular 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 + // More characters for TFT_eSPI test sketches, change next line to //* to use /* 0x0102, 0x0103, 0x0104, 0x0105, 0x0106, 0x0107, 0x010C, 0x010D, 0x010E, 0x010F, 0x0110, 0x0111, 0x0118, 0x0119, 0x011A, 0x011B, @@ -337,8 +359,8 @@ static final int[] specificUnicodes = { //*/ }; - // >>>>>>>>>> USER CONFIGURED PARAMETERS END HERE <<<<<<<<<< + //////////////////////////////////////////////////////////////////////////////////////////////// // Variable to hold the inclusive Unicode range (16 bit values only for this sketch) @@ -347,7 +369,10 @@ int lastUnicode = 0; PFont myFont; +PrintWriter logOutput; + void setup() { + logOutput = createWriter("FontFiles/System_Font_List.txt"); size(1000, 800); @@ -355,6 +380,15 @@ void setup() { String[] fontList = PFont.list(); printArray(fontList); + // Save font list to file + for (int x = 0; x < fontList.length; x++) + { + logOutput.print("[" + x + "] "); + logOutput.println(fontList[x]); + } + logOutput.flush(); // Writes the remaining data to the file + logOutput.close(); // Finishes the file + // Set the fontName from the array number or the defined fontName if (fontNumber >= 0) { @@ -410,19 +444,19 @@ void setup() { } } - // loading the range specified + // loading the specific point codes for (int i = 0; i < specificUnicodes.length; i++) { charset[index] = Character.toChars(specificUnicodes[i])[0]; index++; } - - // Make font smooth + + // Make font smooth (anti-aliased) boolean smooth = true; // Create the font in memory myFont = createFont(fontName+fontType, displayFontSize, smooth, charset); - // Print a few characters to the sketch window + // Print characters to the sketch window fill(0, 0, 0); textFont(myFont); @@ -444,10 +478,10 @@ void setup() { int unicode = charset[index]; float cwidth = textWidth((char)unicode) + 2; if ( (x + cwidth) > (width - gapx) ) break; - - // Draw the letter to the screen + + // Draw the glyph to the screen text(new String(Character.toChars(unicode)), x, y); - + // Move cursor x += cwidth; // Increment the counter @@ -458,12 +492,12 @@ void setup() { } - // creating font + // creating font to save as a file PFont font; font = createFont(fontName+fontType, fontSize, smooth, charset); - println("Created font " + fontName + str(fontSize) + ".vlw"); + println("Created font " + fontName + str(fontSize) + ".vlw"); // creating file try { @@ -486,4 +520,4 @@ void setup() { catch(IOException e) { println("Doh! Failed to create the file"); } -} \ No newline at end of file +} 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 1cf8f3f..d0a2f1f 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 @@ -41,8 +41,8 @@ void loop(void) { tft.getSetup(user); // Serial.printf("\n[code]\n"); -String ver = user.version; -Serial.println("TFT_eSPI ver = " + ver +"\n"); + +Serial.printf("TFT_eSPI ver = " + user.version) +"\n"); Serial.printf("Processor = ESP%i\n", user.esp, HEX); Serial.printf("Frequency = %i MHz\n", ESP.getCpuFreqMHz()); #ifdef ESP8266 diff --git a/library.json b/library.json index 7b1724d..f3f1f04 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "TFT_eSPI", - "version": "1.4.3", + "version": "1.4.4", "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 707afde..d65fa12 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=TFT_eSPI -version=1.4.3 +version=1.4.4 author=Bodmer maintainer=Bodmer sentence=A fast TFT graphics library for ESP8266 and ESP32 processors for the Arduino IDE From 35a402f6a8ad2b4830cb9825aadd4aee30af6bcc Mon Sep 17 00:00:00 2001 From: Bodmer Date: Sun, 10 Feb 2019 21:38:34 +0000 Subject: [PATCH 251/287] Raise issue in header --- TFT_eSPI.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TFT_eSPI.h b/TFT_eSPI.h index 55110f0..93de46b 100644 --- a/TFT_eSPI.h +++ b/TFT_eSPI.h @@ -15,7 +15,7 @@ #ifndef _TFT_eSPIH_ #define _TFT_eSPIH_ -#define TFT_ESPI_VERSION "1.4.2" +#define TFT_ESPI_VERSION "1.4.4" //#define ESP32 //Just used to test ESP32 options From e25da37fc5e0be90adba8f24757c14af55d03dd0 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Mon, 11 Feb 2019 21:09:38 +0000 Subject: [PATCH 252/287] Correct diagnostic example --- .../Test and diagnostics/Read_User_Setup/Read_User_Setup.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 d0a2f1f..fb49cf3 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 @@ -42,7 +42,7 @@ tft.getSetup(user); // Serial.printf("\n[code]\n"); -Serial.printf("TFT_eSPI ver = " + user.version) +"\n"); +Serial.print ("TFT_eSPI ver = " + user.version +"\n"); Serial.printf("Processor = ESP%i\n", user.esp, HEX); Serial.printf("Frequency = %i MHz\n", ESP.getCpuFreqMHz()); #ifdef ESP8266 From dd4e184b53dd6bdfef1bdf0fde361caba987a2b0 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Wed, 13 Feb 2019 09:30:44 +0000 Subject: [PATCH 253/287] Remove obsolete comment --- User_Setup.h | 1 - 1 file changed, 1 deletion(-) diff --git a/User_Setup.h b/User_Setup.h index b581333..a963e3b 100644 --- a/User_Setup.h +++ b/User_Setup.h @@ -114,7 +114,6 @@ // // 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 From d6e07078f7c8a5486a783462c4a8d2955e6f9150 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Wed, 13 Feb 2019 09:32:13 +0000 Subject: [PATCH 254/287] Remove obsolete comment --- User_Setups/SetupX_Template.h | 1 - 1 file changed, 1 deletion(-) diff --git a/User_Setups/SetupX_Template.h b/User_Setups/SetupX_Template.h index 36a2372..aa5037a 100644 --- a/User_Setups/SetupX_Template.h +++ b/User_Setups/SetupX_Template.h @@ -114,7 +114,6 @@ // // 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 From 26d7e4038aaac3f1bf343d7ee3767fecb095a249 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Thu, 14 Feb 2019 00:32:10 +0000 Subject: [PATCH 255/287] Fix touch screen handler The touch screen handler was broken for the ESP8266 only. The library uses SPI write only configuration for ESP8266 so the SPI buffer can be recycled and it was left in write only mode. The library now switches back to read & write mode at the end of a transaction. --- Extensions/Touch.cpp | 14 +---- TFT_eSPI.cpp | 52 ++++++++++--------- TFT_eSPI.h | 2 +- .../Read_User_Setup/Read_User_Setup.ino | 2 +- library.json | 2 +- library.properties | 2 +- 6 files changed, 33 insertions(+), 41 deletions(-) diff --git a/Extensions/Touch.cpp b/Extensions/Touch.cpp index 943d2e0..3f45102 100644 --- a/Extensions/Touch.cpp +++ b/Extensions/Touch.cpp @@ -16,11 +16,8 @@ ***************************************************************************************/ uint8_t TFT_eSPI::getTouchRaw(uint16_t *x, uint16_t *y){ uint16_t tmp; - CS_H; spi_begin_touch(); - - T_CS_L; // Start YP sample request for x position, read 4 times and keep last sample spi.transfer(0xd0); // Start new YP conversion @@ -51,8 +48,6 @@ uint8_t TFT_eSPI::getTouchRaw(uint16_t *x, uint16_t *y){ *y = tmp; - T_CS_H; - spi_end_touch(); return true; @@ -63,20 +58,15 @@ uint8_t TFT_eSPI::getTouchRaw(uint16_t *x, uint16_t *y){ ** Description: read raw pressure on touchpad and return Z value. ***************************************************************************************/ uint16_t TFT_eSPI::getTouchRawZ(void){ - CS_H; spi_begin_touch(); - T_CS_L; - - // Calculate Z + // Z sample request int16_t tz = 0xFFF; spi.transfer(0xb0); // Start new Z1 conversion tz += spi.transfer16(0xc0) >> 3; // Read Z1 and start Z2 conversion tz -= spi.transfer16(0x00) >> 3; // Read Z2 - T_CS_H; - spi_end_touch(); return (uint16_t)tz; @@ -86,7 +76,7 @@ uint16_t TFT_eSPI::getTouchRawZ(void){ ** Function name: validTouch ** Description: read validated position. Return false if not pressed. ***************************************************************************************/ -#define _RAWERR 10 // Deadband error allowed in successive position samples +#define _RAWERR 20 // Deadband error allowed in successive 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; diff --git a/TFT_eSPI.cpp b/TFT_eSPI.cpp index 9c4fc66..36760b3 100644 --- a/TFT_eSPI.cpp +++ b/TFT_eSPI.cpp @@ -55,11 +55,17 @@ inline void TFT_eSPI::spi_begin(void){ #else CS_L; #endif +#ifdef ESP8266 + SPI1U = SPI1U_WRITE; +#endif } inline void TFT_eSPI::spi_end(void){ #if defined (SPI_HAS_TRANSACTION) && defined (SUPPORT_TRANSACTIONS) && !defined(ESP32_PARALLEL) if(!inTransaction) {if (!locked) {locked = true; CS_H; spi.endTransaction();}} + #ifdef ESP8266 + SPI1U = SPI1U_READ; + #endif #else if(!inTransaction) CS_H; #endif @@ -96,19 +102,33 @@ inline void TFT_eSPI::spi_end_read(void){ #if defined (TOUCH_CS) && defined (SPI_TOUCH_FREQUENCY) // && !defined(ESP32_PARALLEL) inline void TFT_eSPI::spi_begin_touch(void){ + CS_H; // Just in case it has been left low + #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 + + #ifdef ESP8266 + SPI1U = SPI1U_READ; + #endif + + T_CS_L; } inline void TFT_eSPI::spi_end_touch(void){ + T_CS_H; + #if defined (SPI_HAS_TRANSACTION) && defined (SUPPORT_TRANSACTIONS) if(!inTransaction) {if (!locked) {locked = true; spi.endTransaction();}} #else spi.setFrequency(SPI_FREQUENCY); #endif + + #ifdef ESP8266 + SPI1U = SPI1U_WRITE; + #endif } #endif @@ -325,6 +345,7 @@ void TFT_eSPI::init(uint8_t tc) #endif _booted = false; + spi_end(); } // end of: if just _booted // Toggle RST low to reset @@ -475,8 +496,6 @@ void TFT_eSPI::commandList (const uint8_t *addr) uint8_t numArgs; uint8_t ms; - spi_begin(); - numCommands = pgm_read_byte(addr++); // Number of commands to follow while (numCommands--) // For each command... @@ -497,7 +516,7 @@ void TFT_eSPI::commandList (const uint8_t *addr) delay( (ms==255 ? 500 : ms) ); } } - spi_end(); + } @@ -2671,7 +2690,6 @@ void TFT_eSPI::setWindow(int32_t xs, int32_t ys, int32_t xe, int32_t ye) // Column addr set DC_C; - SPI1U = SPI1U_WRITE; SPI1U1 = (CMD_BITS << SPILMOSI) | (CMD_BITS << SPILMISO); SPI1W0 = TFT_CASET; @@ -2733,7 +2751,6 @@ void TFT_eSPI::setWindow(int32_t xs, int32_t ys, int32_t xe, int32_t ye) // Column addr set DC_C; - SPI1U = SPI1U_WRITE; SPI1U1 = (CMD_BITS << SPILMOSI) | (CMD_BITS << SPILMISO); SPI1W0 = TFT_CASET<<8; @@ -2779,7 +2796,6 @@ void TFT_eSPI::setWindow(int32_t x0, int32_t y0, int32_t x1, int32_t y1) { //spi_begin(); // Must be called before setWimdow - SPI1U = SPI1U_WRITE; SPI1U1 = (CMD_BITS << SPILMOSI) | (CMD_BITS << SPILMISO); // Column addr set @@ -2912,7 +2928,6 @@ void TFT_eSPI::setWindow(int32_t x0, int32_t y0, int32_t x1, int32_t y1) #if defined (ESP8266) && !defined (RPI_WRITE_STROBE) void TFT_eSPI::readAddrWindow(int32_t xs, int32_t ys, int32_t w, int32_t h) { - spi_begin(); int32_t xe = xs + w - 1; int32_t ye = ys + h - 1; @@ -2930,7 +2945,6 @@ void TFT_eSPI::readAddrWindow(int32_t xs, int32_t ys, int32_t w, int32_t h) // Column addr set DC_C; - SPI1U = SPI1U_WRITE; SPI1U1 = (CMD_BITS << SPILMOSI) | (CMD_BITS << SPILMISO); SPI1W0 = TFT_CASET; @@ -2971,14 +2985,13 @@ void TFT_eSPI::readAddrWindow(int32_t xs, int32_t ys, int32_t w, int32_t h) while(SPI1CMD & SPIBUSY) {} DC_D; - //spi_end(); + } #else //ESP32 void TFT_eSPI::readAddrWindow(int32_t xs, int32_t ys, int32_t w, int32_t h) { - spi_begin(); int32_t xe = xs + w - 1; int32_t ye = ys + h - 1; @@ -3017,7 +3030,6 @@ ye += rowstart; DC_D; - //spi_end(); } #endif @@ -3039,7 +3051,6 @@ void TFT_eSPI::drawPixel(int32_t x, int32_t y, uint32_t color) spi_begin(); - SPI1U = SPI1U_WRITE; // No need to send x if it has not changed (speeds things up) if (addr_col != x) { @@ -3129,7 +3140,6 @@ void TFT_eSPI::drawPixel(int32_t x, int32_t y, uint32_t color) spi_begin(); - SPI1U = SPI1U_WRITE; SPI1U1 = (CMD_BITS << SPILMOSI) | (CMD_BITS << SPILMISO); // No need to send x if it has not changed (speeds things up) if (addr_col != x) { @@ -3398,7 +3408,6 @@ void TFT_eSPI::pushColors(uint16_t *data, uint32_t len, bool swap) uint32_t color[8]; - SPI1U = SPI1U_WRITE; SPI1U1 = (255 << SPILMOSI) | (255 << SPILMISO); @@ -4351,14 +4360,10 @@ int16_t TFT_eSPI::drawChar(uint16_t uniCode, int32_t x, int32_t y, uint8_t font) pc += line; } } - - inTransaction = false; - spi_end(); } else // Text colour != background && textsize = 1 // so use faster drawing of characters and background using block write { - //spi_begin(); setWindow(x, y, x + width - 1, y + height - 1); #ifdef RPI_WRITE_STROBE @@ -4398,9 +4403,9 @@ int16_t TFT_eSPI::drawChar(uint16_t uniCode, int32_t x, int32_t y, uint8_t font) #endif } } - - spi_end(); } + inTransaction = false; + spi_end(); } // End of RLE font rendering #endif @@ -4616,6 +4621,7 @@ int16_t TFT_eSPI::drawString(const char *string, int32_t poX, int32_t poY, uint8 { poX +=xo; // Adjust for negative offset start character poY -= glyph_ab * textsize; + sumX += poX; } #endif switch(padding) { @@ -4668,7 +4674,7 @@ int16_t TFT_eSPI::drawString(const char *string, int32_t poX, int32_t poY, uint8 #endif //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ DEBUG ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -return sumX + poX; +return sumX; } @@ -4896,8 +4902,6 @@ void writeBlock(uint16_t color, uint32_t repeat) uint16_t color16 = (color >> 8) | (color << 8); uint32_t color32 = color16 | color16 << 16; - SPI1U = SPI1U_WRITE; - SPI1W0 = color32; SPI1W1 = color32; SPI1W2 = color32; @@ -4954,8 +4958,6 @@ void writeBlock(uint16_t color, uint32_t repeat) void writeBlock(uint16_t color, uint32_t repeat) { - SPI1U = SPI1U_WRITE; - // Split out the colours uint8_t r = (color & 0xF800)>>8; uint8_t g = (color & 0x07E0)>>3; diff --git a/TFT_eSPI.h b/TFT_eSPI.h index 93de46b..2d52ac9 100644 --- a/TFT_eSPI.h +++ b/TFT_eSPI.h @@ -15,7 +15,7 @@ #ifndef _TFT_eSPIH_ #define _TFT_eSPIH_ -#define TFT_ESPI_VERSION "1.4.4" +#define TFT_ESPI_VERSION "1.4.5" //#define ESP32 //Just used to test ESP32 options 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 fb49cf3..f13cb16 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 @@ -42,7 +42,7 @@ tft.getSetup(user); // Serial.printf("\n[code]\n"); -Serial.print ("TFT_eSPI ver = " + user.version +"\n"); +Serial.print ("TFT_eSPI ver = " + user.version + "\n"); Serial.printf("Processor = ESP%i\n", user.esp, HEX); Serial.printf("Frequency = %i MHz\n", ESP.getCpuFreqMHz()); #ifdef ESP8266 diff --git a/library.json b/library.json index f3f1f04..3e84942 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "TFT_eSPI", - "version": "1.4.4", + "version": "1.4.5", "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 d65fa12..43ac980 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=TFT_eSPI -version=1.4.4 +version=1.4.5 author=Bodmer maintainer=Bodmer sentence=A fast TFT graphics library for ESP8266 and ESP32 processors for the Arduino IDE From e17ba463c967ccf780a38843e0e56467cb619805 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Sun, 17 Feb 2019 22:35:49 +0000 Subject: [PATCH 256/287] Add UTF-8 on/off control via new setAttribute function No change to legacy sketches meeded, UTF-8 decoding is on by default. Add colour test example. --- TFT_eSPI.cpp | 60 ++++++-- TFT_eSPI.h | 20 ++- .../Colour_Test/Colour_Test.ino | 132 ++++++++++++++++++ library.json | 2 +- library.properties | 2 +- 5 files changed, 199 insertions(+), 17 deletions(-) create mode 100644 examples/Test and diagnostics/Colour_Test/Colour_Test.ino diff --git a/TFT_eSPI.cpp b/TFT_eSPI.cpp index 36760b3..53d498c 100644 --- a/TFT_eSPI.cpp +++ b/TFT_eSPI.cpp @@ -218,7 +218,9 @@ TFT_eSPI::TFT_eSPI(int16_t w, int16_t h) locked = true; // ESP32 transaction mutex lock flags inTransaction = false; - _booted = true; + _booted = true; + _cp437 = true; + _utf8 = true; addr_row = 0xFFFF; addr_col = 0xFFFF; @@ -703,14 +705,14 @@ uint16_t TFT_eSPI::readPixel(int32_t x0, int32_t y0) // Dummy read to throw away don't care value tft_Read_8(); - #if !defined (ILI9488_DRIVER) + //#if !defined (ILI9488_DRIVER) // 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 = tft_Read_8(); uint8_t g = tft_Read_8(); uint8_t b = tft_Read_8(); - +/* #else // The 6 colour bits are in MS 6 bits of each byte, but the ILI9488 needs an extra clock pulse @@ -720,7 +722,7 @@ uint16_t TFT_eSPI::readPixel(int32_t x0, int32_t y0) uint8_t b = (tft_Read_8()&0x7E)<<1; #endif - +*/ CS_H; #ifdef TFT_SDA_READ @@ -3903,6 +3905,46 @@ void TFT_eSPI::invertDisplay(boolean i) } +/************************************************************************** +** Function name: setAttribute +** Description: Sets a control parameter of an attribute +**************************************************************************/ +void TFT_eSPI::setAttribute(uint8_t attr_id, uint8_t param) { + switch (attr_id) { + break; + case 1: + _cp437 = param; + break; + case 2: + _utf8 = param; + break; + //case 3: // TBD future feature control + // _tbd = param; + // break; + } +} + + +/************************************************************************** +** Function name: getAttribute +** Description: Get value of an attribute (control parameter) +**************************************************************************/ +uint8_t TFT_eSPI::getAttribute(uint8_t attr_id) { + switch (attr_id) { + case 1: // ON/OFF control of full CP437 character set + return _cp437; + break; + case 2: // ON/OFF control of UTF-8 decoding + return _utf8; + break; + //case 3: // TBD future feature control + // return _tbd; + // break; + } + + return false; +} + /*************************************************************************************** ** Function name: decodeUTF8 ** Description: Serial UTF-8 decoder with fall-back to extended ASCII @@ -3998,12 +4040,14 @@ uint16_t TFT_eSPI::decodeUTF8(uint8_t *buf, uint16_t *index, uint16_t remaining) ***************************************************************************************/ size_t TFT_eSPI::write(uint8_t utf8) { - uint16_t uniCode = decodeUTF8(utf8); - - if (!uniCode) return 1; - if (utf8 == '\r') return 1; + uint16_t uniCode = utf8; + + if (_utf8) uniCode = decodeUTF8(utf8); + + if (uniCode == 0) return 1; + #ifdef SMOOTH_FONT if(fontLoaded) { diff --git a/TFT_eSPI.h b/TFT_eSPI.h index 2d52ac9..cc937d7 100644 --- a/TFT_eSPI.h +++ b/TFT_eSPI.h @@ -808,8 +808,8 @@ class TFT_eSPI : public Print { void setAddrWindow(int32_t xs, int32_t ys, int32_t w, int32_t h); - // Compatibility additions (non-essential) - void startWrite(void); // Begin SPI transaction (not normally needed) + // Compatibility additions + void startWrite(void); // Begin SPI transaction void writeColor(uint16_t color, uint32_t len); // Write colours without transaction overhead void endWrite(void); // End SPI transaction @@ -825,6 +825,10 @@ class TFT_eSPI : public Print { void end_SDA_Read(void); #endif + // Set or get an arbitrary library attribute or configuration option + void setAttribute(uint8_t id = 0, uint8_t a = 0); + uint8_t getAttribute(uint8_t id = 0); + void getSetup(setup_t& tft_settings); // Sketch provides the instance to populate int32_t cursor_x, cursor_y, padX; @@ -877,17 +881,19 @@ class TFT_eSPI : public Print { uint32_t fontsloaded; - uint8_t glyph_ab, // glyph height above baseline - glyph_bb; // glyph height below baseline + uint8_t glyph_ab, // glyph delta Y (height) above baseline + glyph_bb; // glyph delta Y (height) below baseline - bool isDigits; // adjust bounding box for numbers to reduce visual jiggling + bool isDigits; // adjust bounding box for numbers to reduce visual jiggling 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; + bool _booted; // init() or begin() has already run once + bool _cp437; // If set, use correct CP437 charset (default is ON) + bool _utf8; // If set, use UTF-8 decoder in print stream 'write()' function (default ON) - uint32_t _lastColor; + uint32_t _lastColor; // Buffered value of last colour used #ifdef LOAD_GFXFF GFXfont *gfxFont; diff --git a/examples/Test and diagnostics/Colour_Test/Colour_Test.ino b/examples/Test and diagnostics/Colour_Test/Colour_Test.ino new file mode 100644 index 0000000..fb65bb4 --- /dev/null +++ b/examples/Test and diagnostics/Colour_Test/Colour_Test.ino @@ -0,0 +1,132 @@ + +// Diagnostic test for the displayed colour order +// +// Writen by Bodmer 17/2/19 for the TFT_eSPI library: +// https://github.com/Bodmer/TFT_eSPI + +/* + Different hardware manufacturers use different colour order + configurations at the hardware level. This may result in + incorrect colours being displayed. + + Incorrectly displayed colours could also be the result of + using the wrong display driver in the library setup file. + + Typically displays have a control register (MADCTL) that can + be used to set the Red Green Blue (RGB) colour order to RGB + or BRG so that red and blue are swapped on the display. + + This control register is also used to manage the display + rotation and coordinate mirroring. The control register + typically has 8 bits, for the ILI9341 these are: + + Bit Function + 7 Mirror Y coordinate (row address order) + 6 Mirror X coordinate (column address order) + 5 Row/column exchange (for rotation) + 4 Refresh direction (top to bottom or bottom to top in portrait orientation) + 3 RGB order (swaps red and blue) + 2 Refresh direction (top to bottom or bottom to top in landscape orientation) + 1 Not used + 0 Not used + + The control register bits can be written with this example command sequence: + + tft.writecommand(TFT_MADCTL); + tft.writedata(0x48); // Bits 6 and 3 set + + 0x48 is the default value for ILI9341 (0xA8 for ESP32 M5STACK) + in rotation 0 orientation. + + Another control register can be used to "invert" colours, + this swaps black and white as well as other colours (e.g. + green to magenta, red to cyan, blue to yellow). + + To invert colours insert this line after tft.init() or tft.begin(): + + tft.invertDisplay( invert ); // Where invert is true or false + +*/ + +#include + +#include // Hardware-specific library + +TFT_eSPI tft = TFT_eSPI(); // Invoke custom library + +void setup(void) { + tft.init(); + + tft.fillScreen(TFT_BLACK); + + // Set "cursor" at top left corner of display (0,0) and select font 4 + tft.setCursor(0, 0, 4); + + // Set the font colour to be white with a black background + tft.setTextColor(TFT_WHITE, TFT_BLACK); + + // We can now plot text on screen using the "print" class + tft.println("Intialised default\n"); + tft.println("White text"); + + tft.setTextColor(TFT_RED, TFT_BLACK); + tft.println("Red text"); + + tft.setTextColor(TFT_GREEN, TFT_BLACK); + tft.println("Green text"); + + tft.setTextColor(TFT_BLUE, TFT_BLACK); + tft.println("Blue text"); + + delay(10000); + +} + +void loop() { + + tft.invertDisplay( false ); // Where i is true or false + + tft.fillScreen(TFT_BLACK); + + tft.setCursor(0, 0, 4); + + tft.setTextColor(TFT_WHITE, TFT_BLACK); + tft.println("Invert OFF\n"); + + tft.println("White text"); + + tft.setTextColor(TFT_RED, TFT_BLACK); + tft.println("Red text"); + + tft.setTextColor(TFT_GREEN, TFT_BLACK); + tft.println("Green text"); + + tft.setTextColor(TFT_BLUE, TFT_BLACK); + tft.println("Blue text"); + + delay(10000); + + + // Binary inversion of colours + tft.invertDisplay( true ); // Where i is true or false + + tft.fillScreen(TFT_BLACK); + + tft.setCursor(0, 0, 4); + + tft.setTextColor(TFT_WHITE, TFT_BLACK); + tft.println("Invert ON\n"); + + tft.println("White text"); + + tft.setTextColor(TFT_RED, TFT_BLACK); + tft.println("Red text"); + + tft.setTextColor(TFT_GREEN, TFT_BLACK); + tft.println("Green text"); + + tft.setTextColor(TFT_BLUE, TFT_BLACK); + tft.println("Blue text"); + + delay(10000); +} diff --git a/library.json b/library.json index 3e84942..09ec816 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "TFT_eSPI", - "version": "1.4.5", + "version": "1.4.6", "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 43ac980..09b3308 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=TFT_eSPI -version=1.4.5 +version=1.4.6 author=Bodmer maintainer=Bodmer sentence=A fast TFT graphics library for ESP8266 and ESP32 processors for the Arduino IDE From 13f0589aa6d68289b0479d8419904f44fc55d0e5 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Mon, 18 Feb 2019 01:21:26 +0000 Subject: [PATCH 257/287] Adapt for new setAddrWindow parameters --- examples/Generic/ESP8266_uncannyEyes/ESP8266_uncannyEyes.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/Generic/ESP8266_uncannyEyes/ESP8266_uncannyEyes.ino b/examples/Generic/ESP8266_uncannyEyes/ESP8266_uncannyEyes.ino index 0e3654e..4d5d4e7 100644 --- a/examples/Generic/ESP8266_uncannyEyes/ESP8266_uncannyEyes.ino +++ b/examples/Generic/ESP8266_uncannyEyes/ESP8266_uncannyEyes.ino @@ -141,7 +141,7 @@ void drawEye( // Renders one eye. Inputs must be pre-clipped & valid. // reset on each frame here in case of an SPI glitch. //eye[e].tft.setAddrWindow(319-127, 0, 319, 127); - eye[e].tft.setAddrWindow(0, 0, 127, 127); + eye[e].tft.setAddrWindow(0, 0, 128, 128); //digitalWrite(eye[e].cs, LOW); // Chip select From 66a21cb761e00c936253092b825e6b9ba7c2583c Mon Sep 17 00:00:00 2001 From: Bodmer Date: Tue, 19 Feb 2019 00:16:29 +0000 Subject: [PATCH 258/287] Fix ESP8266 core library issue reported in #310 --- TFT_eSPI.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/TFT_eSPI.cpp b/TFT_eSPI.cpp index 53d498c..86c8971 100644 --- a/TFT_eSPI.cpp +++ b/TFT_eSPI.cpp @@ -2788,6 +2788,9 @@ void TFT_eSPI::setWindow(int32_t xs, int32_t ys, int32_t xe, int32_t ye) DC_D; + // Re-instate SPI flags settings corrupted by SPI library writePattern() call + SPI1U = SPI1U_WRITE; + //spi_end(); } From 1edfe6c680b7602e12d0fa3f1d3e633c97d84d4b Mon Sep 17 00:00:00 2001 From: Bodmer Date: Wed, 20 Feb 2019 00:45:02 +0000 Subject: [PATCH 259/287] Fic #311 Pixel function used wrong width and height for bounds check. Remove String variable in smooth font code (not used) Correct ESP8266UncannyEyes example for new setAddrWindow parameters --- Extensions/Smooth_font.cpp | 6 ++---- Extensions/Smooth_font.h | 2 -- Extensions/Sprite.cpp | 7 ++++++- .../Generic/ESP8266_uncannyEyes/ESP8266_uncannyEyes.ino | 2 +- library.json | 2 +- library.properties | 2 +- 6 files changed, 11 insertions(+), 10 deletions(-) diff --git a/Extensions/Smooth_font.cpp b/Extensions/Smooth_font.cpp index 8d7628f..f63b45c 100644 --- a/Extensions/Smooth_font.cpp +++ b/Extensions/Smooth_font.cpp @@ -76,15 +76,13 @@ void TFT_eSPI::loadFont(String fontName) unloadFont(); - _gFontFilename = "/" + fontName + ".vlw"; - // Avoid a crash on the ESP32 if the file does not exist - if (SPIFFS.exists(_gFontFilename) == false) { + if (SPIFFS.exists("/" + fontName + ".vlw") == false) { Serial.println("Font file " + fontName + " not found!"); return; } - fontFile = SPIFFS.open( _gFontFilename, "r"); + fontFile = SPIFFS.open( "/" + fontName + ".vlw", "r"); if(!fontFile) return; diff --git a/Extensions/Smooth_font.h b/Extensions/Smooth_font.h index 1bc9fd2..aa51855 100644 --- a/Extensions/Smooth_font.h +++ b/Extensions/Smooth_font.h @@ -39,8 +39,6 @@ fontMetrics gFont = { 0, 0, 0, 0, 0, 0, 0 }; int8_t* gdX = NULL; //leftExtent uint32_t* gBitmap = NULL; //file pointer to greyscale bitmap - String _gFontFilename; - bool fontLoaded = false; // Flags when a anti-aliased font is loaded private: diff --git a/Extensions/Sprite.cpp b/Extensions/Sprite.cpp index 3782775..f389141 100644 --- a/Extensions/Sprite.cpp +++ b/Extensions/Sprite.cpp @@ -1059,6 +1059,8 @@ int16_t TFT_eSprite::height(void) // Does nothing for 8 and 16 bpp sprites. TODO allow rotation of these sprites void TFT_eSprite::setRotation(uint8_t rotation) { + if (_bpp != 1) return; + _rotation = rotation; if (rotation == 0 && _iwidth > _iheight) swap_coord(_iwidth, _iheight); if (rotation == 1 && _iwidth < _iheight) swap_coord(_iwidth, _iheight); @@ -1085,19 +1087,22 @@ uint8_t TFT_eSprite::getRotation(void) void TFT_eSprite::drawPixel(int32_t x, int32_t y, uint32_t color) { // Range checking - if ((x < 0) || (y < 0) ||(x >= _width) || (y >= _height) || !_created) return; + if ((x < 0) || (y < 0) || !_created) return; if (_bpp == 16) { + if ((x >= _iwidth) || (y >= _iheight)) return; color = (color >> 8) | (color << 8); _img[x+y*_iwidth] = (uint16_t) color; } else if (_bpp == 8) { + if ((x >= _iwidth) || (y >= _iheight)) return; _img8[x+y*_iwidth] = (uint8_t)((color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3); } else // 1 bpp { + if ((x >= _dwidth) || (y >= _dheight)) return; if (_rotation == 1) { uint16_t tx = x; diff --git a/examples/Generic/ESP8266_uncannyEyes/ESP8266_uncannyEyes.ino b/examples/Generic/ESP8266_uncannyEyes/ESP8266_uncannyEyes.ino index 4d5d4e7..0e3654e 100644 --- a/examples/Generic/ESP8266_uncannyEyes/ESP8266_uncannyEyes.ino +++ b/examples/Generic/ESP8266_uncannyEyes/ESP8266_uncannyEyes.ino @@ -141,7 +141,7 @@ void drawEye( // Renders one eye. Inputs must be pre-clipped & valid. // reset on each frame here in case of an SPI glitch. //eye[e].tft.setAddrWindow(319-127, 0, 319, 127); - eye[e].tft.setAddrWindow(0, 0, 128, 128); + eye[e].tft.setAddrWindow(0, 0, 127, 127); //digitalWrite(eye[e].cs, LOW); // Chip select diff --git a/library.json b/library.json index 09ec816..6381bb3 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "TFT_eSPI", - "version": "1.4.6", + "version": "1.4.7", "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 09b3308..f4e7a65 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=TFT_eSPI -version=1.4.6 +version=1.4.7 author=Bodmer maintainer=Bodmer sentence=A fast TFT graphics library for ESP8266 and ESP32 processors for the Arduino IDE From 43862876a2c63d43aa096c012ca46d6af7e5105b Mon Sep 17 00:00:00 2001 From: Bodmer Date: Wed, 20 Feb 2019 23:33:21 +0000 Subject: [PATCH 260/287] Permit larger anti-aliased fonts Size limit for anti-aliased fonts raised to ~200 pixels --- Extensions/Smooth_font.cpp | 4 ++-- Extensions/Smooth_font.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Extensions/Smooth_font.cpp b/Extensions/Smooth_font.cpp index f63b45c..6e5f89d 100644 --- a/Extensions/Smooth_font.cpp +++ b/Extensions/Smooth_font.cpp @@ -124,7 +124,7 @@ void TFT_eSPI::loadMetrics(uint16_t gCount) 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 + gdY = (int16_t*)malloc( gCount * 2); // 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 @@ -141,7 +141,7 @@ void TFT_eSPI::loadMetrics(uint16_t gCount) 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 + gdY[gNum] = (int16_t)readInt32(); // y delta from baseline gdX[gNum] = (int8_t)readInt32(); // x delta from cursor readInt32(); // ignored diff --git a/Extensions/Smooth_font.h b/Extensions/Smooth_font.h index aa51855..c1cc483 100644 --- a/Extensions/Smooth_font.h +++ b/Extensions/Smooth_font.h @@ -35,7 +35,7 @@ fontMetrics gFont = { 0, 0, 0, 0, 0, 0, 0 }; uint8_t* gHeight = NULL; //cheight uint8_t* gWidth = NULL; //cwidth uint8_t* gxAdvance = NULL; //setWidth - int8_t* gdY = NULL; //topExtent + int16_t* gdY = NULL; //topExtent int8_t* gdX = NULL; //leftExtent uint32_t* gBitmap = NULL; //file pointer to greyscale bitmap From 46425b83bc1e3d43d8a52425f5f243b2d5f5a0b8 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Fri, 22 Feb 2019 00:13:40 +0000 Subject: [PATCH 261/287] Fix bug in UTF-8 line buffer decoder --- TFT_eSPI.cpp | 2 +- library.json | 2 +- library.properties | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/TFT_eSPI.cpp b/TFT_eSPI.cpp index 86c8971..19af227 100644 --- a/TFT_eSPI.cpp +++ b/TFT_eSPI.cpp @@ -4011,7 +4011,7 @@ uint16_t TFT_eSPI::decodeUTF8(uint8_t c) *************************************************************************************x*/ uint16_t TFT_eSPI::decodeUTF8(uint8_t *buf, uint16_t *index, uint16_t remaining) { - byte c = buf[(*index)++]; + uint16_t c = buf[(*index)++]; //Serial.print("Byte from string = 0x"); Serial.println(c, HEX); #ifdef DECODE_UTF8 diff --git a/library.json b/library.json index 6381bb3..da5bb7c 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "TFT_eSPI", - "version": "1.4.7", + "version": "1.4.8", "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 f4e7a65..a1b8621 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=TFT_eSPI -version=1.4.7 +version=1.4.8 author=Bodmer maintainer=Bodmer sentence=A fast TFT graphics library for ESP8266 and ESP32 processors for the Arduino IDE From 652383b694398d5379d4e3870e2f9d927f6a8bdc Mon Sep 17 00:00:00 2001 From: Bodmer Date: Mon, 4 Mar 2019 13:34:29 +0000 Subject: [PATCH 262/287] Fix part of Issue #322 --- TFT_Drivers/ST7789_Init.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TFT_Drivers/ST7789_Init.h b/TFT_Drivers/ST7789_Init.h index c72b51d..cad01a3 100644 --- a/TFT_Drivers/ST7789_Init.h +++ b/TFT_Drivers/ST7789_Init.h @@ -13,7 +13,7 @@ //------------------------------display and color format setting--------------------------------// writecommand(ST7789_MADCTL); - writedata(0x00); + //writedata(0x00); writedata(TFT_MAD_COLOR_ORDER); // JLX240 display datasheet From 9f179201157febae786aa9aaa9a0eed399971913 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Mon, 15 Apr 2019 12:23:16 +0100 Subject: [PATCH 263/287] Add ESP32 SD support for smooth font plus performance updates Added SD card storage for smooth fonts with ESP32 ESP32 will use PSRAM (if available and enabled) to hold smooth font metrics Improve performance of ESP32 Sprite shared functions Add basic ST7789 driver option Latent bug fixes for pin mask --- Extensions/Smooth_font.cpp | 71 +++++-- Extensions/Smooth_font.h | 13 +- TFT_Drivers/ST7789_2_Defines.h | 143 ++++++++++++++ TFT_Drivers/ST7789_2_Init.h | 22 +++ TFT_Drivers/ST7789_2_Rotation.h | 48 +++++ TFT_Drivers/ST7789_Init.h | 4 +- TFT_eSPI.cpp | 95 ++++++---- TFT_eSPI.h | 4 +- User_Setup.h | 5 +- User_Setup_Select.h | 6 +- User_Setups/Setup10_RPi_touch_ILI9486.h | 2 +- User_Setups/Setup18_ST7789.h | 11 +- User_Setups/Setup21_ILI9488.h | 4 + User_Setups/Setup24_ST7789.h | 53 ++++++ User_Setups/SetupX_Template.h | 7 +- .../ESP32_Smooth_Font_SD.ino | 174 ++++++++++++++++++ .../data/Final-Frontier-28.vlw | Bin 0 -> 25287 bytes .../Colour_Test/Colour_Test.ino | 6 +- library.json | 2 +- library.properties | 2 +- 20 files changed, 600 insertions(+), 72 deletions(-) create mode 100644 TFT_Drivers/ST7789_2_Defines.h create mode 100644 TFT_Drivers/ST7789_2_Init.h create mode 100644 TFT_Drivers/ST7789_2_Rotation.h create mode 100644 User_Setups/Setup24_ST7789.h create mode 100644 examples/Smooth Fonts/ESP32_Smooth_Font_SD/ESP32_Smooth_Font_SD.ino create mode 100644 examples/Smooth Fonts/ESP32_Smooth_Font_SD/data/Final-Frontier-28.vlw diff --git a/Extensions/Smooth_font.cpp b/Extensions/Smooth_font.cpp index 6e5f89d..ca1cb63 100644 --- a/Extensions/Smooth_font.cpp +++ b/Extensions/Smooth_font.cpp @@ -8,9 +8,18 @@ /*************************************************************************************** ** Function name: loadFont -** Description: loads parameters from a new font vlw file stored in SPIFFS +** Description: loads parameters from a new font vlw file *************************************************************************************x*/ -void TFT_eSPI::loadFont(String fontName) +void TFT_eSPI::loadFont(String fontName, fs::FS &ffs) +{ + fontFS = ffs; + loadFont(fontName, false); +} +/*************************************************************************************** +** Function name: loadFont +** Description: loads parameters from a new font vlw file +*************************************************************************************x*/ +void TFT_eSPI::loadFont(String fontName, bool flash) { /* The vlw font format does not appear to be documented anywhere, so some reverse @@ -74,15 +83,19 @@ void TFT_eSPI::loadFont(String fontName) */ - unloadFont(); - + spiffs = flash; + + if(spiffs) fontFS = SPIFFS; + + unloadFont(); + // Avoid a crash on the ESP32 if the file does not exist - if (SPIFFS.exists("/" + fontName + ".vlw") == false) { + if (fontFS.exists("/" + fontName + ".vlw") == false) { Serial.println("Font file " + fontName + " not found!"); return; } - fontFile = SPIFFS.open( "/" + fontName + ".vlw", "r"); + fontFile = fontFS.open( "/" + fontName + ".vlw", "r"); if(!fontFile) return; @@ -120,13 +133,28 @@ 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 = (int16_t*)malloc( gCount * 2); // 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 +#if defined (ESP32) && defined (CONFIG_SPIRAM_SUPPORT) + if ( psramFound() ) + { + gUnicode = (uint16_t*)ps_malloc( gCount * 2); // Unicode 16 bit Basic Multilingual Plane (0-FFFF) + gHeight = (uint8_t*)ps_malloc( gCount ); // Height of glyph + gWidth = (uint8_t*)ps_malloc( gCount ); // Width of glyph + gxAdvance = (uint8_t*)ps_malloc( gCount ); // xAdvance - to move x cursor + gdY = (int16_t*)ps_malloc( gCount * 2); // offset from bitmap top edge from lowest point in any character + gdX = (int8_t*)ps_malloc( gCount ); // offset for bitmap left edge relative to cursor X + gBitmap = (uint32_t*)ps_malloc( gCount * 4); // seek pointer to glyph bitmap in the file + } + else +#endif + { + 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 = (int16_t*)malloc( gCount * 2); // 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 the file + } #ifdef SHOW_ASCENT_DESCENT Serial.print("ascent = "); Serial.println(gFont.ascent); @@ -456,7 +484,19 @@ void TFT_eSPI::drawGlyph(uint16_t code) for (int y = 0; y < gHeight[gNum]; y++) { - fontFile.read(pbuffer, gWidth[gNum]); //= 0) cspinmask = (uint32_t) digitalPinToBitMask(TFT_CS); #endif - #ifdef TFT_DC + #if defined (TFT_DC) && (TFT_DC >= 0) dcpinmask = (uint32_t) digitalPinToBitMask(TFT_DC); #endif - #ifdef TFT_WR + #if defined (TFT_WR) && (TFT_WR >= 0) wrpinmask = (uint32_t) digitalPinToBitMask(TFT_WR); #endif - #ifdef TFT_SCLK + #if defined (TFT_SCLK) && (TFT_SCLK >= 0) sclkpinmask = (uint32_t) digitalPinToBitMask(TFT_SCLK); #endif @@ -407,6 +412,9 @@ void TFT_eSPI::init(uint8_t tc) #elif defined (R61581_DRIVER) #include "TFT_Drivers/R61581_Init.h" +#elif defined (ST7789_2_DRIVER) + #include "TFT_Drivers/ST7789_2_Init.h" + #endif #ifdef TFT_INVERSION_ON @@ -477,6 +485,9 @@ void TFT_eSPI::setRotation(uint8_t m) #elif defined (R61581_DRIVER) #include "TFT_Drivers/R61581_Rotation.h" +#elif defined (ST7789_2_DRIVER) + #include "TFT_Drivers/ST7789_2_Rotation.h" + #endif delayMicroseconds(10); @@ -1561,7 +1572,7 @@ void TFT_eSPI::drawCircle(int32_t x0, int32_t y0, int32_t r, uint32_t color) int32_t dy = r+r; int32_t p = -(r>>1); - spi_begin(); + //spi_begin(); // Sprite class can use this function, avoiding spi_begin() inTransaction = true; // These are ordered to minimise coordinate changes in x or y @@ -1598,7 +1609,7 @@ void TFT_eSPI::drawCircle(int32_t x0, int32_t y0, int32_t r, uint32_t color) } inTransaction = false; - spi_end(); + spi_end(); // Does nothing if Sprite class uses this function } @@ -1654,7 +1665,7 @@ void TFT_eSPI::fillCircle(int32_t x0, int32_t y0, int32_t r, uint32_t color) int32_t dy = r+r; int32_t p = -(r>>1); - spi_begin(); + //spi_begin(); // Sprite class can use this function, avoiding spi_begin() inTransaction = true; drawFastHLine(x0 - r, y0, dy+1, color); @@ -1680,7 +1691,7 @@ void TFT_eSPI::fillCircle(int32_t x0, int32_t y0, int32_t r, uint32_t color) } inTransaction = false; - spi_end(); + spi_end(); // Does nothing if Sprite class uses this function } @@ -1736,7 +1747,7 @@ void TFT_eSPI::drawEllipse(int16_t x0, int16_t y0, int32_t rx, int32_t ry, uint1 int32_t fy2 = 4 * ry2; int32_t s; - spi_begin(); + //spi_begin(); // Sprite class can use this function, avoiding spi_begin() inTransaction = true; for (x = 0, y = ry, s = 2*ry2+rx2*(1-2*ry); ry2*x <= rx2*y; x++) @@ -1772,7 +1783,7 @@ void TFT_eSPI::drawEllipse(int16_t x0, int16_t y0, int32_t rx, int32_t ry, uint1 } inTransaction = false; - spi_end(); + spi_end(); // Does nothing if Sprite class uses this function } @@ -1791,7 +1802,7 @@ void TFT_eSPI::fillEllipse(int16_t x0, int16_t y0, int32_t rx, int32_t ry, uint1 int32_t fy2 = 4 * ry2; int32_t s; - spi_begin(); + //spi_begin(); // Sprite class can use this function, avoiding spi_begin() inTransaction = true; for (x = 0, y = ry, s = 2*ry2+rx2*(1-2*ry); ry2*x <= rx2*y; x++) @@ -1821,7 +1832,7 @@ void TFT_eSPI::fillEllipse(int16_t x0, int16_t y0, int32_t rx, int32_t ry, uint1 } inTransaction = false; - spi_end(); + spi_end(); // Does nothing if Sprite class uses this function } @@ -1842,7 +1853,7 @@ void TFT_eSPI::fillScreen(uint32_t color) // Draw a rectangle void TFT_eSPI::drawRect(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color) { - spi_begin(); + //spi_begin(); // Sprite class can use this function, avoiding spi_begin() inTransaction = true; drawFastHLine(x, y, w, color); @@ -1852,7 +1863,7 @@ void TFT_eSPI::drawRect(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t col drawFastVLine(x + w - 1, y+1, h-2, color); inTransaction = false; - spi_end(); + spi_end(); // Does nothing if Sprite class uses this function } @@ -1863,7 +1874,7 @@ void TFT_eSPI::drawRect(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t col // Draw a rounded rectangle void TFT_eSPI::drawRoundRect(int32_t x, int32_t y, int32_t w, int32_t h, int32_t r, uint32_t color) { - spi_begin(); + //spi_begin(); // Sprite class can use this function, avoiding spi_begin() inTransaction = true; // smarter version @@ -1878,7 +1889,7 @@ void TFT_eSPI::drawRoundRect(int32_t x, int32_t y, int32_t w, int32_t h, int32_t drawCircleHelper(x + r , y + h - r - 1, r, 8, color); inTransaction = false; - spi_end(); + spi_end(); // Does nothing if Sprite class uses this function } @@ -1889,7 +1900,7 @@ void TFT_eSPI::drawRoundRect(int32_t x, int32_t y, int32_t w, int32_t h, int32_t // 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(); + //spi_begin(); // Sprite class can use this function, avoiding spi_begin() inTransaction = true; // smarter version @@ -1900,7 +1911,7 @@ void TFT_eSPI::fillRoundRect(int32_t x, int32_t y, int32_t w, int32_t h, int32_t fillCircleHelper(x + r , y + r, r, 2, w - r - r - 1, color); inTransaction = false; - spi_end(); + spi_end(); // Does nothing if Sprite class uses this function } @@ -1911,7 +1922,7 @@ void TFT_eSPI::fillRoundRect(int32_t x, int32_t y, int32_t w, int32_t h, int32_t // Draw a triangle void TFT_eSPI::drawTriangle(int32_t x0, int32_t y0, int32_t x1, int32_t y1, int32_t x2, int32_t y2, uint32_t color) { - spi_begin(); + //spi_begin(); // Sprite class can use this function, avoiding spi_begin() inTransaction = true; drawLine(x0, y0, x1, y1, color); @@ -1919,7 +1930,7 @@ void TFT_eSPI::drawTriangle(int32_t x0, int32_t y0, int32_t x1, int32_t y1, int3 drawLine(x2, y2, x0, y0, color); inTransaction = false; - spi_end(); + spi_end(); // Does nothing if Sprite class uses this function } @@ -1953,7 +1964,7 @@ void TFT_eSPI::fillTriangle ( int32_t x0, int32_t y0, int32_t x1, int32_t y1, in return; } - spi_begin(); + //spi_begin(); // Sprite class can use this function, avoiding spi_begin() inTransaction = true; int32_t @@ -2000,7 +2011,7 @@ void TFT_eSPI::fillTriangle ( int32_t x0, int32_t y0, int32_t x1, int32_t y1, in } inTransaction = false; - spi_end(); + spi_end(); // Does nothing if Sprite class uses this function } @@ -2010,7 +2021,7 @@ void TFT_eSPI::fillTriangle ( int32_t x0, int32_t y0, int32_t x1, int32_t y1, in ***************************************************************************************/ 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(); + //spi_begin(); // Sprite class can use this function, avoiding spi_begin() inTransaction = true; int32_t i, j, byteWidth = (w + 7) / 8; @@ -2024,7 +2035,7 @@ void TFT_eSPI::drawBitmap(int16_t x, int16_t y, const uint8_t *bitmap, int16_t w } inTransaction = false; - spi_end(); + spi_end(); // Does nothing if Sprite class uses this function } @@ -2034,7 +2045,7 @@ void TFT_eSPI::drawBitmap(int16_t x, int16_t y, const uint8_t *bitmap, int16_t w ***************************************************************************************/ 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(); + //spi_begin(); // Sprite class can use this function, avoiding spi_begin() inTransaction = true; int32_t i, j, byteWidth = (w + 7) / 8; @@ -2048,7 +2059,7 @@ void TFT_eSPI::drawXBitmap(int16_t x, int16_t y, const uint8_t *bitmap, int16_t } inTransaction = false; - spi_end(); + spi_end(); // Does nothing if Sprite class uses this function } @@ -2058,7 +2069,7 @@ void TFT_eSPI::drawXBitmap(int16_t x, int16_t y, const uint8_t *bitmap, int16_t ***************************************************************************************/ 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(); + //spi_begin(); // Sprite class can use this function, avoiding spi_begin() inTransaction = true; int32_t i, j, byteWidth = (w + 7) / 8; @@ -2072,7 +2083,7 @@ void TFT_eSPI::drawXBitmap(int16_t x, int16_t y, const uint8_t *bitmap, int16_t } inTransaction = false; - spi_end(); + spi_end(); // Does nothing if Sprite class uses this function } @@ -2480,7 +2491,7 @@ void TFT_eSPI::drawChar(int32_t x, int32_t y, uint16_t c, uint32_t color, uint32 } else { - spi_begin(); + //spi_begin(); // Sprite class can use this function, avoiding spi_begin() inTransaction = true; for (int8_t i = 0; i < 6; i++ ) { uint8_t line; @@ -2505,7 +2516,7 @@ void TFT_eSPI::drawChar(int32_t x, int32_t y, uint16_t c, uint32_t color, uint32 } } inTransaction = false; - spi_end(); + spi_end(); // Does nothing if Sprite class uses this function } //>>>>>>>>>>>>>>>>>>>>>>>>>>> @@ -2519,7 +2530,7 @@ void TFT_eSPI::drawChar(int32_t x, int32_t y, uint16_t c, uint32_t color, uint32 // Filter out bad characters not present in font if ((c >= pgm_read_word(&gfxFont->first)) && (c <= pgm_read_word(&gfxFont->last ))) { - spi_begin(); + //spi_begin(); // Sprite class can use this function, avoiding spi_begin() inTransaction = true; //>>>>>>>>>>>>>>>>>>>>>>>>>>> @@ -2643,7 +2654,7 @@ void TFT_eSPI::drawChar(int32_t x, int32_t y, uint16_t c, uint32_t color, uint32 } #endif inTransaction = false; - spi_end(); + spi_end(); // Does nothing if Sprite class uses this function } #endif @@ -3500,7 +3511,7 @@ void TFT_eSPI::pushColors(uint16_t *data, uint32_t len, bool swap) void TFT_eSPI::drawLine(int32_t x0, int32_t y0, int32_t x1, int32_t y1, uint32_t color) { - spi_begin(); + //spi_begin(); // Sprite class can use this function, avoiding spi_begin() inTransaction = true; boolean steep = abs(y1 - y0) > abs(x1 - x0); if (steep) { @@ -4278,7 +4289,7 @@ int16_t TFT_eSPI::drawChar(uint16_t uniCode, int32_t x, int32_t y, uint8_t font) if (x + width * textsize >= (int16_t)_width) return width * textsize ; if (textcolor == textbgcolor || textsize != 1) { - spi_begin(); + //spi_begin(); // Sprite class can use this function, avoiding spi_begin() inTransaction = true; for (int32_t i = 0; i < height; i++) @@ -5178,6 +5189,18 @@ void writeBlock(uint16_t color, uint32_t repeat) #endif +/*************************************************************************************** +** Function name: getSPIinstance +** Description: Get the instance of the SPI class (for ESP32 only) +***************************************************************************************/ +#ifndef ESP32_PARALLEL +SPIClass& TFT_eSPI::getSPIinstance(void) +{ + return spi; +} +#endif + + /*************************************************************************************** ** Function name: getSetup ** Description: Get the setup details for diagnostic and sketch access diff --git a/TFT_eSPI.h b/TFT_eSPI.h index cc937d7..0756d39 100644 --- a/TFT_eSPI.h +++ b/TFT_eSPI.h @@ -37,7 +37,7 @@ #define SPI_READ_FREQUENCY SPI_FREQUENCY #endif -#ifdef ST7789_DRIVER +#if defined(ST7789_DRIVER) || defined(ST7789_2_DRIVER) #define TFT_SPI_MODE SPI_MODE3 #else #define TFT_SPI_MODE SPI_MODE0 @@ -831,6 +831,8 @@ class TFT_eSPI : public Print { void getSetup(setup_t& tft_settings); // Sketch provides the instance to populate + static SPIClass& getSPIinstance(void); + int32_t cursor_x, cursor_y, padX; uint32_t textcolor, textbgcolor; diff --git a/User_Setup.h b/User_Setup.h index a963e3b..3f44644 100644 --- a/User_Setup.h +++ b/User_Setup.h @@ -25,14 +25,15 @@ //#define ILI9481_DRIVER //#define ILI9486_DRIVER //#define ILI9488_DRIVER // WARNING: Do not connect ILI9488 display SDO to MISO if other devices share the SPI bus (TFT SDO does NOT tristate when CS is high) -//#define ST7789_DRIVER // Define additional parameters below for this display +//#define ST7789_DRIVER // Full configuration option, define additional parameters below for this display +//#define ST7789_2_DRIVER // Minimal configuration option, define additional parameters below for this display //#define R61581_DRIVER // Some displays support SPI reads via the MISO pin, other displays have a single // bi-directional SDA pin and the library will try to read this via the MOSI line. // To use the SDA line for reading data from the TFT uncomment the following line: -// #define TFT_SDA_READ // This option if for ESP32 ONLY, tested with ST7789 display only +// #define TFT_SDA_READ // This option is for ESP32 ONLY, tested with ST7789 display only // For ST7789 ONLY, define the colour order IF the blue and red are swapped on your display // Try ONE option at a time to find the correct colour order for your display diff --git a/User_Setup_Select.h b/User_Setup_Select.h index 69d27a0..06568f1 100644 --- a/User_Setup_Select.h +++ b/User_Setup_Select.h @@ -39,13 +39,14 @@ //#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 configured for HX8357D (untested) +//#include // Setup file configured for ST7789 //#include // Setup file for ESP8266 and ILI9488 SPI bus TFT //#include // Setup file for ESP32 and ILI9488 SPI bus TFT //#include // Setup file for ESP32 and TTGO T4 (BTC) ILI9341 SPI bus TFT //#include // Setup file for ESP32 and TTGO TM ST7789 SPI bus TFT +//#include // Setup file configured for ST7789 240 x 240 //#include // Setup file configured for my ST7735S 80x160 @@ -106,6 +107,9 @@ #elif defined (R61581_DRIVER) #include "TFT_Drivers/R61581_Defines.h" #define TFT_DRIVER 0x6158 +#elif defined (ST7789_2_DRIVER) + #include "TFT_Drivers/ST7789_2_Defines.h" + #define TFT_DRIVER 0x778B #elif defined (XYZZY_DRIVER) // <<<<<<<<<<<<<<<<<<<<<<<< ADD NEW DRIVER HERE #include "TFT_Drivers/XYZZY_Defines.h" #define TFT_DRIVER 0x0000 diff --git a/User_Setups/Setup10_RPi_touch_ILI9486.h b/User_Setups/Setup10_RPi_touch_ILI9486.h index 8e59a6b..ffe2f71 100644 --- a/User_Setups/Setup10_RPi_touch_ILI9486.h +++ b/User_Setups/Setup10_RPi_touch_ILI9486.h @@ -23,7 +23,7 @@ #define SMOOTH_FONT -#define SPI_FREQUENCY 20000000 +#define SPI_FREQUENCY 16000000 #define SPI_TOUCH_FREQUENCY 2500000 diff --git a/User_Setups/Setup18_ST7789.h b/User_Setups/Setup18_ST7789.h index 25d57a3..1f9be03 100644 --- a/User_Setups/Setup18_ST7789.h +++ b/User_Setups/Setup18_ST7789.h @@ -2,8 +2,17 @@ #define ST7789_DRIVER +// #define TFT_SDA_READ // This option is for ESP32 ONLY, tested with ST7789 display only -#define TFT_SDA_READ +// If colours are inverted (white shows as black) then uncomment one of the next +// 2 lines try both options, one of the options should correct the inversion. +// #define TFT_INVERSION_ON +// #define TFT_INVERSION_OFF + +// For ST7789 ONLY, define the colour order IF the blue and red are swapped on your display +// Try ONE option at a time to find the correct colour order for your display +// #define TFT_RGB_ORDER TFT_RGB // Colour order Red-Green-Blue +// #define TFT_RGB_ORDER TFT_BGR // Colour order Blue-Green-Red // My ST7789 display has TCT_CS wired permananently low so the pin is not defined here diff --git a/User_Setups/Setup21_ILI9488.h b/User_Setups/Setup21_ILI9488.h index 1a39a02..358eef4 100644 --- a/User_Setups/Setup21_ILI9488.h +++ b/User_Setups/Setup21_ILI9488.h @@ -2,6 +2,7 @@ #define ILI9488_DRIVER +//#define TFT_INVERSION_OFF #define TFT_MISO 19 // (leave TFT SDO disconnected if other SPI devices share MISO) #define TFT_MOSI 23 @@ -27,4 +28,7 @@ // #define SPI_FREQUENCY 40000000 // #define SPI_FREQUENCY 80000000 +// Optional reduced SPI frequency for reading TFT +#define SPI_READ_FREQUENCY 16000000 + #define SPI_TOUCH_FREQUENCY 2500000 diff --git a/User_Setups/Setup24_ST7789.h b/User_Setups/Setup24_ST7789.h new file mode 100644 index 0000000..d75b743 --- /dev/null +++ b/User_Setups/Setup24_ST7789.h @@ -0,0 +1,53 @@ +// ST7789 240 x 240 display with no chip select line + +#define ST7789_DRIVER // Configure all registers + +#define TFT_WIDTH 240 +#define TFT_HEIGHT 240 + +//#define TFT_RGB_ORDER TFT_RGB // Colour order Red-Green-Blue +//#define TFT_RGB_ORDER TFT_BGR // Colour order Blue-Green-Red + +//#define TFT_INVERSION_ON +//#define TFT_INVERSION_OFF + +// DSTIKE stepup +//#define TFT_DC 23 +//#define TFT_RST 32 +//#define TFT_MOSI 26 +//#define TFT_SCLK 27 + +// Generic ESP32 setup +//#define TFT_MISO 19 +//#define TFT_MOSI 23 +//#define TFT_SCLK 18 +//#define TFT_CS -1 // Not connected +//#define TFT_DC 2 +//#define TFT_RST 4 // Connect reset to ensure display initialises + +// For NodeMCU - use pin numbers in the form PIN_Dx where Dx is the NodeMCU pin designation +#define TFT_CS -1 // Define as not used +#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 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 + +#define SMOOTH_FONT + + +// #define SPI_FREQUENCY 27000000 +#define SPI_FREQUENCY 40000000 + +#define SPI_READ_FREQUENCY 20000000 + +#define SPI_TOUCH_FREQUENCY 2500000 + +// #define SUPPORT_TRANSACTIONS \ No newline at end of file diff --git a/User_Setups/SetupX_Template.h b/User_Setups/SetupX_Template.h index aa5037a..534a634 100644 --- a/User_Setups/SetupX_Template.h +++ b/User_Setups/SetupX_Template.h @@ -25,7 +25,8 @@ //#define ILI9481_DRIVER //#define ILI9486_DRIVER //#define ILI9488_DRIVER // WARNING: Do not connect ILI9488 display SDO to MISO if other devices share the SPI bus (TFT SDO does NOT tristate when CS is high) -//#define ST7789_DRIVER // Define additional parameters below for this display +//#define ST7789_DRIVER // Full configuration option, define additional parameters below for this display +//#define ST7789_2_DRIVER // Minimal configuration option, define additional parameters below for this display //#define R61581_DRIVER // Some displays support SPI reads via the MISO pin, other displays have a single @@ -37,8 +38,8 @@ // For ST7789 ONLY, define the colour order IF the blue and red are swapped on your display // Try ONE option at a time to find the correct colour order for your display -// #define TFT_RGB_ORDER TFT_RGB // Colour order Red-Green-Blue -// #define TFT_RGB_ORDER TFT_BGR // Colour order Blue-Green-Red +// #define TFT_RGB_ORDER TFT_RGB // Colour order Red-Green-Blue +// #define TFT_RGB_ORDER TFT_BGR // Colour order Blue-Green-Red // For M5Stack ESP32 module with integrated ILI9341 display ONLY, remove // in line below diff --git a/examples/Smooth Fonts/ESP32_Smooth_Font_SD/ESP32_Smooth_Font_SD.ino b/examples/Smooth Fonts/ESP32_Smooth_Font_SD/ESP32_Smooth_Font_SD.ino new file mode 100644 index 0000000..c6dbb19 --- /dev/null +++ b/examples/Smooth Fonts/ESP32_Smooth_Font_SD/ESP32_Smooth_Font_SD.ino @@ -0,0 +1,174 @@ +/* + Sketch to demonstrate using the print class with smooth fonts + that are saved onto an SD Card accessed by the SD library. + + For ESP32 only, GPIO 5 must be used for SD chip select. + This method of storing the fonts is NOT compatible with the ESP8266. + + Sketch is written for a 240 x 320 display + + Load the font file onto the root directory of the SD Card. The font files + used by this sketch can be found in the Data folder, press Ctrl+K to see it. + + 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 on SD card +#include + +// Graphics and font library +#include +#include + +TFT_eSPI tft = TFT_eSPI(); // Invoke library + +// ------------------------------------------------------------------------- +// Setup +// ------------------------------------------------------------------------- +void setup(void) { + Serial.begin(115200); // Used for messages + + // Initialise the SD library before the TFT so the chip select is defined + 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); + + // Initialise the TFT after the SD card! + tft.init(); + tft.setRotation(1); + tft.fillScreen(TFT_BLACK); + + listDir(SD, "/", 0); + + Serial.println("SD and TFT initialisation done."); +} + +// ------------------------------------------------------------------------- +// 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, SD); // Use font stored on SD + + // Display all characters of the font + tft.showFont(2000); + + uint32_t dt = millis(); + + int count = 100; + + while (count--) + { + // 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 + } + + Serial.println(millis()-dt); + + // Unload the font to recover used RAM + tft.unloadFont(); + + delay(10000); +} + +void listDir(fs::FS &fs, const char * dirname, uint8_t levels){ + Serial.printf("Listing directory: %s\n", dirname); + + File root = fs.open(dirname); + if(!root){ + Serial.println("Failed to open directory"); + return; + } + if(!root.isDirectory()){ + Serial.println("Not a directory"); + return; + } + + File file = root.openNextFile(); + while(file){ + if(file.isDirectory()){ + Serial.print(" DIR : "); + Serial.println(file.name()); + if(levels){ + listDir(fs, file.name(), levels -1); + } + } else { + Serial.print(" FILE: "); + Serial.print(file.name()); + Serial.print(" SIZE: "); + Serial.println(file.size()); + } + file = root.openNextFile(); + } +} diff --git a/examples/Smooth Fonts/ESP32_Smooth_Font_SD/data/Final-Frontier-28.vlw b/examples/Smooth Fonts/ESP32_Smooth_Font_SD/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/Test and diagnostics/Colour_Test/Colour_Test.ino b/examples/Test and diagnostics/Colour_Test/Colour_Test.ino index fb65bb4..ed7a061 100644 --- a/examples/Test and diagnostics/Colour_Test/Colour_Test.ino +++ b/examples/Test and diagnostics/Colour_Test/Colour_Test.ino @@ -78,7 +78,7 @@ void setup(void) { tft.setTextColor(TFT_BLUE, TFT_BLACK); tft.println("Blue text"); - delay(10000); + delay(5000); } @@ -104,7 +104,7 @@ void loop() { tft.setTextColor(TFT_BLUE, TFT_BLACK); tft.println("Blue text"); - delay(10000); + delay(5000); // Binary inversion of colours @@ -128,5 +128,5 @@ void loop() { tft.setTextColor(TFT_BLUE, TFT_BLACK); tft.println("Blue text"); - delay(10000); + delay(5000); } diff --git a/library.json b/library.json index da5bb7c..1474e04 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "TFT_eSPI", - "version": "1.4.8", + "version": "1.4.9", "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 a1b8621..a8b48c7 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=TFT_eSPI -version=1.4.8 +version=1.4.9 author=Bodmer maintainer=Bodmer sentence=A fast TFT graphics library for ESP8266 and ESP32 processors for the Arduino IDE From 197c408d7d8509aa8ccb5f5325e8906cc0c124b5 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Sun, 19 May 2019 09:22:04 +0100 Subject: [PATCH 264/287] Corrrect byte swap for 18 bit colours --- TFT_eSPI.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TFT_eSPI.h b/TFT_eSPI.h index 0756d39..75c1237 100644 --- a/TFT_eSPI.h +++ b/TFT_eSPI.h @@ -334,7 +334,7 @@ // Convert swapped byte 16 bit colour to 18 bit and write in 3 bytes #define tft_Write_16S(C) spi.transfer(C & 0xF8); \ - spi.transfer((C & 0xE0)>>11 | (C & 0x07)<<5); \ + spi.transfer((C & 0xE000)>>11 | (C & 0x07)<<5); \ spi.transfer((C & 0x1F00)>>5) // Write 32 bits to TFT #define tft_Write_32(C) spi.write32(C) From e090cd8b62b059efd601de98b0d12e2b3b7db402 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Sun, 19 May 2019 09:24:00 +0100 Subject: [PATCH 265/287] Correct byte swap for18 bit colours --- library.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library.properties b/library.properties index a8b48c7..43cc01e 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=TFT_eSPI -version=1.4.9 +version=1.4.10 author=Bodmer maintainer=Bodmer sentence=A fast TFT graphics library for ESP8266 and ESP32 processors for the Arduino IDE From 0a1aadfd5a3c12024fd6a79a44358ea3ace5ab60 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Sun, 19 May 2019 09:25:16 +0100 Subject: [PATCH 266/287] Correct byte swap for 18 bit colours --- library.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library.json b/library.json index 1474e04..b3b4039 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "TFT_eSPI", - "version": "1.4.9", + "version": "1.4.10", "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": From f6dad6e4ab50ca6785f50cad242ef02dbf99e264 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Sun, 19 May 2019 10:29:21 +0100 Subject: [PATCH 267/287] ESP8266 compiler compatibility update #361 --- TFT_eSPI.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/TFT_eSPI.cpp b/TFT_eSPI.cpp index bb3bfb8..3c7e711 100644 --- a/TFT_eSPI.cpp +++ b/TFT_eSPI.cpp @@ -4270,7 +4270,8 @@ int16_t TFT_eSPI::drawChar(uint16_t uniCode, int32_t x, int32_t y, uint8_t font) { if ((font>2) && (font<9)) { - flash_address = pgm_read_dword( pgm_read_dword( &(fontdata[font].chartbl ) ) + uniCode*sizeof(void *) ); + //flash_address = pgm_read_dword( pgm_read_dword( &(fontdata[font].chartbl ) ) + uniCode*sizeof(void *) ); + flash_address = pgm_read_dword( (const void*)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 ); } From da2239f4c17125dff7c4fee7f4fa41590ae08218 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Mon, 20 May 2019 12:35:17 +0100 Subject: [PATCH 268/287] ESP8266 compiler compatibility update #361 --- Extensions/Sprite.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Extensions/Sprite.cpp b/Extensions/Sprite.cpp index f389141..cadd111 100644 --- a/Extensions/Sprite.cpp +++ b/Extensions/Sprite.cpp @@ -1679,7 +1679,8 @@ int16_t TFT_eSprite::drawChar(uint16_t uniCode, int32_t x, int32_t y, uint8_t fo 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 *) ); + // flash_address = pgm_read_dword( pgm_read_dword( &(fontdata[font].chartbl ) ) + uniCode*sizeof(void *) ); + flash_address = pgm_read_dword( (const void*)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 ); } From a627c51118786a8672568bb509b8afa8e4b1a6da Mon Sep 17 00:00:00 2001 From: Calvin Hass Date: Tue, 21 May 2019 07:13:19 -0700 Subject: [PATCH 269/287] Fix getTouch() return status upon bounds overflow - When the post-remapped coordinates exceed screen bounds, `getTouch()` was returning a "valid" status without updating the coordinates. This would result in spurious touch detection events at the screen boundaries. - The fix ensures that the return value is set to "invalid" in this boundary condition. --- Extensions/Touch.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Extensions/Touch.cpp b/Extensions/Touch.cpp index 3f45102..cb253be 100644 --- a/Extensions/Touch.cpp +++ b/Extensions/Touch.cpp @@ -141,7 +141,7 @@ uint8_t TFT_eSPI::getTouch(uint16_t *x, uint16_t *y, uint16_t threshold){ convertRawXY(&x_tmp, &y_tmp); - if (x_tmp >= _width || y_tmp >= _height) return valid; + if (x_tmp >= _width || y_tmp >= _height) return false; _pressX = x_tmp; _pressY = y_tmp; From da349a5fbd0ac017f49bd3dba0787d41d906a91a Mon Sep 17 00:00:00 2001 From: Bodmer Date: Mon, 3 Jun 2019 23:31:04 +0100 Subject: [PATCH 270/287] Minor changes --- Extensions/Sprite.cpp | 1 - TFT_eSPI.cpp | 3 +-- .../Test and diagnostics/Read_User_Setup/Read_User_Setup.ino | 2 ++ library.json | 2 +- library.properties | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Extensions/Sprite.cpp b/Extensions/Sprite.cpp index cadd111..832abb9 100644 --- a/Extensions/Sprite.cpp +++ b/Extensions/Sprite.cpp @@ -1679,7 +1679,6 @@ int16_t TFT_eSprite::drawChar(uint16_t uniCode, int32_t x, int32_t y, uint8_t fo 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 *) ); flash_address = pgm_read_dword( (const void*)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 ); diff --git a/TFT_eSPI.cpp b/TFT_eSPI.cpp index 3c7e711..ad27dcb 100644 --- a/TFT_eSPI.cpp +++ b/TFT_eSPI.cpp @@ -3389,7 +3389,7 @@ void TFT_eSPI::pushColors(uint8_t *data, uint32_t len) while (len--) {tft_Write_8(*data); data++;} #elif defined (ILI9488_DRIVER) uint16_t color; - while (len>1) {color = (*data++) | ((*data++)<<8); tft_Write_16(color); len-=2;} + while (len>1) {color = (*data++); color |= ((*data++)<<8); tft_Write_16(color); len-=2;} #else #if (SPI_FREQUENCY == 80000000) while ( len >=64 ) {spi.writePattern(data, 64, 1); data += 64; len -= 64; } @@ -4270,7 +4270,6 @@ int16_t TFT_eSPI::drawChar(uint16_t uniCode, int32_t x, int32_t y, uint8_t font) { if ((font>2) && (font<9)) { - //flash_address = pgm_read_dword( pgm_read_dword( &(fontdata[font].chartbl ) ) + uniCode*sizeof(void *) ); flash_address = pgm_read_dword( (const void*)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 ); 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 f13cb16..6485568 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 @@ -149,5 +149,7 @@ int8_t getPinName(int8_t pin) if (pin == 1) return 10; if (pin == 9) return 11; if (pin == 10) return 12; + + return -1; // Invalid pin } diff --git a/library.json b/library.json index b3b4039..c264ba9 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "TFT_eSPI", - "version": "1.4.10", + "version": "1.4.11", "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 43cc01e..be98ae8 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=TFT_eSPI -version=1.4.10 +version=1.4.11 author=Bodmer maintainer=Bodmer sentence=A fast TFT graphics library for ESP8266 and ESP32 processors for the Arduino IDE From f14ee2cd9fa33e9ea9cab0f31d69dc49a8e6f3af Mon Sep 17 00:00:00 2001 From: Ricard Bitria Ribes Date: Sat, 20 Jul 2019 20:57:22 +0200 Subject: [PATCH 271/287] Add new driver RM68140 -Added new RM68140 480x320 TFT. It works like ILI9481 but needed some changes to make setRotation() work properly. -Fixed some PlatformIO warnings --- TFT_Drivers/RM68140_Defines.h | 42 ++++++++++++++ TFT_Drivers/RM68140_Init.h | 77 ++++++++++++++++++++++++++ TFT_Drivers/RM68140_Rotation.h | 44 +++++++++++++++ TFT_eSPI.cpp | 8 ++- User_Setup.h | 1 + User_Setup_Select.h | 5 ++ User_Setups/Setup19_RM68140_Parallel.h | 35 ++++++++++++ 7 files changed, 211 insertions(+), 1 deletion(-) create mode 100644 TFT_Drivers/RM68140_Defines.h create mode 100644 TFT_Drivers/RM68140_Init.h create mode 100644 TFT_Drivers/RM68140_Rotation.h create mode 100644 User_Setups/Setup19_RM68140_Parallel.h diff --git a/TFT_Drivers/RM68140_Defines.h b/TFT_Drivers/RM68140_Defines.h new file mode 100644 index 0000000..bd5fb88 --- /dev/null +++ b/TFT_Drivers/RM68140_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/RM68140_Init.h b/TFT_Drivers/RM68140_Init.h new file mode 100644 index 0000000..e5df339 --- /dev/null +++ b/TFT_Drivers/RM68140_Init.h @@ -0,0 +1,77 @@ + +// This is the command sequence that initialises the RM68140 driver +// +// This setup information uses simple 8 bit SPI writecommand() and writedata() functions +// +// See ST7735_Setup.h file for an alternative format + + +// Configure RM68140 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 RM68140 display configuration + + + diff --git a/TFT_Drivers/RM68140_Rotation.h b/TFT_Drivers/RM68140_Rotation.h new file mode 100644 index 0000000..ba429d8 --- /dev/null +++ b/TFT_Drivers/RM68140_Rotation.h @@ -0,0 +1,44 @@ + // 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); + writecommand(0xB6); + writedata(0); + writedata(0x22); + writedata(0x3B); + _width = TFT_WIDTH; + _height = TFT_HEIGHT; + break; + case 1: // Landscape (Portrait + 90) + writedata(TFT_MAD_MV | TFT_MAD_BGR); + writecommand(0xB6); + writedata(0); + writedata(0x02); + writedata(0x3B); + _width = TFT_HEIGHT; + _height = TFT_WIDTH; + break; + case 2: // Inverter portrait + writedata(TFT_MAD_BGR); + writecommand(0xB6); + writedata(0); + writedata(0x42); + writedata(0x3B); + _width = TFT_WIDTH; + _height = TFT_HEIGHT; + break; + case 3: // Inverted landscape + writedata(TFT_MAD_MV | TFT_MAD_BGR); + writecommand(0xB6); + writedata(0); + writedata(0x62); + writedata(0x3B); + _width = TFT_HEIGHT; + _height = TFT_WIDTH; + break; + } + \ No newline at end of file diff --git a/TFT_eSPI.cpp b/TFT_eSPI.cpp index ad27dcb..a06e2fc 100644 --- a/TFT_eSPI.cpp +++ b/TFT_eSPI.cpp @@ -412,6 +412,9 @@ void TFT_eSPI::init(uint8_t tc) #elif defined (R61581_DRIVER) #include "TFT_Drivers/R61581_Init.h" +#elif defined (RM68140_DRIVER) + #include "TFT_Drivers/RM68140_Init.h" + #elif defined (ST7789_2_DRIVER) #include "TFT_Drivers/ST7789_2_Init.h" @@ -485,6 +488,9 @@ void TFT_eSPI::setRotation(uint8_t m) #elif defined (R61581_DRIVER) #include "TFT_Drivers/R61581_Rotation.h" +#elif defined (RM68140_DRIVER) + #include "TFT_Drivers/RM68140_Rotation.h" + #elif defined (ST7789_2_DRIVER) #include "TFT_Drivers/ST7789_2_Rotation.h" @@ -4270,7 +4276,7 @@ int16_t TFT_eSPI::drawChar(uint16_t uniCode, int32_t x, int32_t y, uint8_t font) { if ((font>2) && (font<9)) { - flash_address = pgm_read_dword( (const void*)pgm_read_dword( &(fontdata[font].chartbl ) ) + uniCode*sizeof(void *) ); + flash_address = pgm_read_dword( (const void*)(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 ); } diff --git a/User_Setup.h b/User_Setup.h index 3f44644..1f70a3f 100644 --- a/User_Setup.h +++ b/User_Setup.h @@ -28,6 +28,7 @@ //#define ST7789_DRIVER // Full configuration option, define additional parameters below for this display //#define ST7789_2_DRIVER // Minimal configuration option, define additional parameters below for this display //#define R61581_DRIVER +//#define RM68140_DRIVER // Some displays support SPI reads via the MISO pin, other displays have a single // bi-directional SDA pin and the library will try to read this via the MOSI line. diff --git a/User_Setup_Select.h b/User_Setup_Select.h index 06568f1..aae87cd 100644 --- a/User_Setup_Select.h +++ b/User_Setup_Select.h @@ -41,6 +41,8 @@ //#include // Setup file for any Waveshare ePaper display //#include // Setup file configured for ST7789 +//#include // Setup file configured for RM68140 with parallel bus + //#include // Setup file for ESP8266 and ILI9488 SPI bus TFT //#include // Setup file for ESP32 and ILI9488 SPI bus TFT @@ -110,6 +112,9 @@ #elif defined (ST7789_2_DRIVER) #include "TFT_Drivers/ST7789_2_Defines.h" #define TFT_DRIVER 0x778B +#elif defined (RM68140_DRIVER) + #include "TFT_Drivers/RM68140_Defines.h" + #define TFT_DRIVER 0x6814 #elif defined (XYZZY_DRIVER) // <<<<<<<<<<<<<<<<<<<<<<<< ADD NEW DRIVER HERE #include "TFT_Drivers/XYZZY_Defines.h" #define TFT_DRIVER 0x0000 diff --git a/User_Setups/Setup19_RM68140_Parallel.h b/User_Setups/Setup19_RM68140_Parallel.h new file mode 100644 index 0000000..85300ae --- /dev/null +++ b/User_Setups/Setup19_RM68140_Parallel.h @@ -0,0 +1,35 @@ +// See SetupX_Template.h for all options available + +#define ESP32_PARALLEL + + +#define RM68140_DRIVER + + +// ESP32 pins used for UNO format board +#define TFT_CS 33 // Chip select control pin +#define TFT_DC 15 // Data Command control pin - must use a pin in the range 0-31 +#define TFT_RST 32 // Reset pin + +#define TFT_WR 4 // Write strobe control pin - must 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 + + +#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 + +#define SMOOTH_FONT From b5db2cd3fa558d4a59b28781699c57db5c82f461 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Sun, 21 Jul 2019 13:27:27 +0100 Subject: [PATCH 272/287] minor edit --- TFT_Drivers/RM68140_Rotation.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/TFT_Drivers/RM68140_Rotation.h b/TFT_Drivers/RM68140_Rotation.h index ba429d8..2f83d61 100644 --- a/TFT_Drivers/RM68140_Rotation.h +++ b/TFT_Drivers/RM68140_Rotation.h @@ -1,4 +1,4 @@ - // This is the command sequence that rotates the ILI9481 driver coordinate frame + // This is the command sequence that rotates the RM68140 driver coordinate frame writecommand(TFT_MADCTL); @@ -41,4 +41,4 @@ _height = TFT_WIDTH; break; } - \ No newline at end of file + From 9bd73a33dcdbd239a725882284dfa3c69fdf4bea Mon Sep 17 00:00:00 2001 From: Bodmer Date: Sun, 21 Jul 2019 13:33:08 +0100 Subject: [PATCH 273/287] RM8140 driver added --- library.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library.properties b/library.properties index be98ae8..6738c66 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=TFT_eSPI -version=1.4.11 +version=1.4.12 author=Bodmer maintainer=Bodmer sentence=A fast TFT graphics library for ESP8266 and ESP32 processors for the Arduino IDE From d38bdac8f7771743646dc19b29a257f0f8f0ff87 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Sun, 21 Jul 2019 13:33:55 +0100 Subject: [PATCH 274/287] RM8140 driver added --- library.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library.json b/library.json index c264ba9..a9be7c6 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "TFT_eSPI", - "version": "1.4.11", + "version": "1.4.12", "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": From 3f6c4072fc0bdb1f7b0d447dddd6db2e5cdbc081 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Sun, 21 Jul 2019 13:37:51 +0100 Subject: [PATCH 275/287] RM68140 driver added --- library.properties | 1 + 1 file changed, 1 insertion(+) diff --git a/library.properties b/library.properties index 6738c66..5006ec8 100644 --- a/library.properties +++ b/library.properties @@ -8,3 +8,4 @@ category=Display url=https://github.com/Bodmer/TFT_eSPI architectures=esp8266,esp32 includes=TFT_eSPI.h + From 92f12cd45cee06005902ff22d2bae41c0385cff1 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Sun, 21 Jul 2019 13:38:58 +0100 Subject: [PATCH 276/287] RM68140 driver added --- library.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library.json b/library.json index a9be7c6..9d4c622 100644 --- a/library.json +++ b/library.json @@ -1,7 +1,7 @@ { "name": "TFT_eSPI", "version": "1.4.12", - "keywords": "tft, ePaper, display, ESP8266, NodeMCU, ESP32, M5Stack, ILI9341, ST7735, ILI9163, S6D02A1, ILI9486, ST7789", + "keywords": "tft, ePaper, display, ESP8266, NodeMCU, ESP32, M5Stack, ILI9341, ST7735, ILI9163, S6D02A1, ILI9486, ST7789, RM68140", "description": "A TFT and ePaper SPI graphics library for ESP8266 and ESP32", "repository": { From af4a96a47a6f241cec644f03b166cc7da574a4e0 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Wed, 24 Jul 2019 01:42:56 +0100 Subject: [PATCH 277/287] Fix #402 --- TFT_eSPI.cpp | 1 + library.json | 2 +- library.properties | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/TFT_eSPI.cpp b/TFT_eSPI.cpp index a06e2fc..5a98bf3 100644 --- a/TFT_eSPI.cpp +++ b/TFT_eSPI.cpp @@ -2352,6 +2352,7 @@ int16_t TFT_eSPI::textWidth(const char *string, uint8_t font) str_width += pgm_read_byte( widthtable + uniCode); // Normally we need to subtract 32 from uniCode else str_width += pgm_read_byte( widthtable + 32); // Set illegal character = space width } + if ((font == 2) && (str_width > 0)) str_width--; } else { diff --git a/library.json b/library.json index 9d4c622..4bbe7e7 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "TFT_eSPI", - "version": "1.4.12", + "version": "1.4.13", "keywords": "tft, ePaper, display, ESP8266, NodeMCU, ESP32, M5Stack, ILI9341, ST7735, ILI9163, S6D02A1, ILI9486, ST7789, RM68140", "description": "A TFT and ePaper SPI graphics library for ESP8266 and ESP32", "repository": diff --git a/library.properties b/library.properties index 5006ec8..0bb271d 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=TFT_eSPI -version=1.4.12 +version=1.4.13 author=Bodmer maintainer=Bodmer sentence=A fast TFT graphics library for ESP8266 and ESP32 processors for the Arduino IDE From 00bf69fc48f995e0f8b42d8b4e0cde763abcce6d Mon Sep 17 00:00:00 2001 From: Bodmer Date: Tue, 30 Jul 2019 15:56:55 +0100 Subject: [PATCH 278/287] Correct #400 and #402 + other tweaks Corrected font 2 rendering issue #402 Corrected NULL pointer issue #400 Changed grave ` character (key for this character is left of numeral 1 key) to degree symbol in font 2 Change setup24 so DC pin is not D3 --- Extensions/Sprite.cpp | 2 +- Fonts/Font16.c | 9 ++++++--- TFT_eSPI.cpp | 19 +++++++++++++------ User_Setups/Setup12_M5Stack.h | 3 +++ User_Setups/Setup24_ST7789.h | 5 +++-- library.json | 2 +- library.properties | 2 +- 7 files changed, 28 insertions(+), 14 deletions(-) diff --git a/Extensions/Sprite.cpp b/Extensions/Sprite.cpp index 832abb9..c2ecf13 100644 --- a/Extensions/Sprite.cpp +++ b/Extensions/Sprite.cpp @@ -1679,7 +1679,7 @@ int16_t TFT_eSprite::drawChar(uint16_t uniCode, int32_t x, int32_t y, uint8_t fo if ((font>2) && (font<9)) { // This is slower than above but is more convenient for the RLE fonts - flash_address = pgm_read_dword( (const void*)pgm_read_dword( &(fontdata[font].chartbl ) ) + uniCode*sizeof(void *) ); + flash_address = pgm_read_dword( (const void*) (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 ); } diff --git a/Fonts/Font16.c b/Fonts/Font16.c index a69c2c1..0f9167f 100644 --- a/Fonts/Font16.c +++ b/Fonts/Font16.c @@ -15,7 +15,8 @@ PROGMEM const unsigned char widtbl_f16[96] = // character width table 8, 4, 8, 8, 7, 10, 8, 8, // char 72 - 79 8, 8, 8, 8, 8, 8, 8, 10, // char 80 - 87 8, 8, 8, 4, 7, 4, 7, 9, // char 88 - 95 - 4, 7, 7, 7, 7, 7, 6, 7, // char 96 - 103 +// 4, 7, 7, 7, 7, 7, 6, 7, // char 96 - 103 grave see lines 411-414 + 5, 7, 7, 7, 7, 7, 6, 7, // char 96 - 103 celcius 7, 4, 5, 6, 4, 8, 7, 8, // char 104 - 111 7, 8, 6, 6, 5, 7, 8, 8, // char 112 - 119 6, 7, 7, 5, 3, 5, 8, 6 // char 120 - 127 @@ -407,8 +408,10 @@ PROGMEM const unsigned char chr_f16_5F[32] = // 1 unsigned chars per row PROGMEM const unsigned char chr_f16_60[16] = // 1 unsigned char per row { - 0x00, 0x00, 0x00, 0x40, 0x40, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, // row 1 - 11 - 0x00, 0x00, 0x00, 0x00, 0x00 // row 12 - 16 +// 0x00, 0x00, 0x00, 0x40, 0x40, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, // row 1 - 11 grave +// 0x00, 0x00, 0x00, 0x00, 0x00 // row 12 - 16 + 0x00, 0x00, 0x00, 0x60, 0x90, 0x90, 0x60, 0x00, 0x00, 0x00, 0x00, // row 1 - 11 Celcius + 0x00, 0x00, 0x00, 0x00, 0x00 }; PROGMEM const unsigned char chr_f16_61[16] = // 1 unsigned char per row diff --git a/TFT_eSPI.cpp b/TFT_eSPI.cpp index 5a98bf3..48d9f8e 100644 --- a/TFT_eSPI.cpp +++ b/TFT_eSPI.cpp @@ -2352,7 +2352,7 @@ int16_t TFT_eSPI::textWidth(const char *string, uint8_t font) str_width += pgm_read_byte( widthtable + uniCode); // Normally we need to subtract 32 from uniCode else str_width += pgm_read_byte( widthtable + 32); // Set illegal character = space width } - if ((font == 2) && (str_width > 0)) str_width--; + } else { @@ -4342,23 +4342,24 @@ int16_t TFT_eSPI::drawChar(uint16_t uniCode, int32_t x, int32_t y, uint8_t font) { spi_begin(); - setWindow(x, y, (x + w * 8) - 1, y + height - 1); + setWindow(x, y, x + width - 1, y + height - 1); uint8_t mask; for (int32_t i = 0; i < height; i++) { + pX = width; for (int32_t k = 0; k < w; k++) { - line = pgm_read_byte((uint8_t *)flash_address + w * i + k); - pX = x + k * 8; + line = pgm_read_byte((uint8_t *) (flash_address + w * i + k) ); mask = 0x80; - while (mask) { + while (mask && pX) { if (line & mask) {tft_Write_16(textcolor);} else {tft_Write_16(textbgcolor);} + pX--; mask = mask >> 1; } } - pY += textsize; + if (pX) {tft_Write_16(textbgcolor);} } spi_end(); @@ -4895,6 +4896,12 @@ int16_t TFT_eSPI::drawFloat(float floatNumber, uint8_t dp, int32_t poX, int32_t void TFT_eSPI::setFreeFont(const GFXfont *f) { + if (f == nullptr) // Fix issue #400 (ESP32 crash) + { + setTextFont(1); // Use GLCD font + return; + } + textfont = 1; gfxFont = (GFXfont *)f; diff --git a/User_Setups/Setup12_M5Stack.h b/User_Setups/Setup12_M5Stack.h index d4b50b8..a1b564f 100644 --- a/User_Setups/Setup12_M5Stack.h +++ b/User_Setups/Setup12_M5Stack.h @@ -27,3 +27,6 @@ #define SPI_FREQUENCY 27000000 + +// Optional reduced SPI frequency for reading TFT +#define SPI_READ_FREQUENCY 5000000 \ No newline at end of file diff --git a/User_Setups/Setup24_ST7789.h b/User_Setups/Setup24_ST7789.h index d75b743..44e4343 100644 --- a/User_Setups/Setup24_ST7789.h +++ b/User_Setups/Setup24_ST7789.h @@ -27,8 +27,9 @@ // For NodeMCU - use pin numbers in the form PIN_Dx where Dx is the NodeMCU pin designation #define TFT_CS -1 // Define as not used -#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_DC PIN_D1 // Data Command control pin +#define TFT_RST PIN_D4 // TFT reset pin (could connect to NodeMCU RST, see next line) +//#define TFT_RST -1 // TFT reset pin connect to NodeMCU RST, must also then add 10K pull down to TFT SCK #define LOAD_GLCD // Font 1. Original Adafruit 8 pixel font needs ~1820 bytes in FLASH diff --git a/library.json b/library.json index 4bbe7e7..145f754 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "TFT_eSPI", - "version": "1.4.13", + "version": "1.4.14", "keywords": "tft, ePaper, display, ESP8266, NodeMCU, ESP32, M5Stack, ILI9341, ST7735, ILI9163, S6D02A1, ILI9486, ST7789, RM68140", "description": "A TFT and ePaper SPI graphics library for ESP8266 and ESP32", "repository": diff --git a/library.properties b/library.properties index 0bb271d..1a8d51b 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=TFT_eSPI -version=1.4.13 +version=1.4.14 author=Bodmer maintainer=Bodmer sentence=A fast TFT graphics library for ESP8266 and ESP32 processors for the Arduino IDE From 4a38aa6aeca64a908f946736811b4624ffbe634f Mon Sep 17 00:00:00 2001 From: Bodmer Date: Tue, 30 Jul 2019 17:59:08 +0100 Subject: [PATCH 279/287] Correct issue #398 --- Extensions/Sprite.cpp | 4 +++- library.json | 2 +- library.properties | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Extensions/Sprite.cpp b/Extensions/Sprite.cpp index c2ecf13..dae49d1 100644 --- a/Extensions/Sprite.cpp +++ b/Extensions/Sprite.cpp @@ -1102,7 +1102,9 @@ void TFT_eSprite::drawPixel(int32_t x, int32_t y, uint32_t color) } else // 1 bpp { - if ((x >= _dwidth) || (y >= _dheight)) return; + + if ((x >= _iwidth) || (y >= _iheight)) return; + if (_rotation == 1) { uint16_t tx = x; diff --git a/library.json b/library.json index 145f754..203187d 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "TFT_eSPI", - "version": "1.4.14", + "version": "1.4.15", "keywords": "tft, ePaper, display, ESP8266, NodeMCU, ESP32, M5Stack, ILI9341, ST7735, ILI9163, S6D02A1, ILI9486, ST7789, RM68140", "description": "A TFT and ePaper SPI graphics library for ESP8266 and ESP32", "repository": diff --git a/library.properties b/library.properties index 1a8d51b..9c964bb 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=TFT_eSPI -version=1.4.14 +version=1.4.15 author=Bodmer maintainer=Bodmer sentence=A fast TFT graphics library for ESP8266 and ESP32 processors for the Arduino IDE From 90189130ec85a3cbcd5ce60170358a40a04c0107 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Wed, 31 Jul 2019 01:07:05 +0100 Subject: [PATCH 280/287] Update for #379 Added support for ST7789 135 x 240 display which needs offsets Added Setup135_ST7789.h as an example setup file for ESP8266 --- TFT_Drivers/ST7789_Rotation.h | 48 +++++++++++++++++++++++++----- User_Setup_Select.h | 4 ++- User_Setups/Setup135_ST7789.h | 56 +++++++++++++++++++++++++++++++++++ library.json | 2 +- library.properties | 2 +- 5 files changed, 101 insertions(+), 11 deletions(-) create mode 100644 User_Setups/Setup135_ST7789.h diff --git a/TFT_Drivers/ST7789_Rotation.h b/TFT_Drivers/ST7789_Rotation.h index d490279..707c775 100644 --- a/TFT_Drivers/ST7789_Rotation.h +++ b/TFT_Drivers/ST7789_Rotation.h @@ -5,8 +5,16 @@ switch (rotation) { case 0: // Portrait #ifdef CGRAM_OFFSET - colstart = 0; - rowstart = 0; + if (_init_width == 135) + { + colstart = 52; + rowstart = 40; + } + else + { + colstart = 0; + rowstart = 0; + } #endif writedata(TFT_MAD_COLOR_ORDER); @@ -16,8 +24,16 @@ case 1: // Landscape (Portrait + 90) #ifdef CGRAM_OFFSET - colstart = 0; - rowstart = 0; + if (_init_width == 135) + { + colstart = 40; + rowstart = 53; + } + else + { + colstart = 0; + rowstart = 0; + } #endif writedata(TFT_MAD_MX | TFT_MAD_MV | TFT_MAD_COLOR_ORDER); @@ -27,8 +43,16 @@ case 2: // Inverter portrait #ifdef CGRAM_OFFSET - colstart = 0; - rowstart = 80; + if (_init_width == 135) + { + colstart = 53; + rowstart = 40; + } + else + { + colstart = 0; + rowstart = 80; + } #endif writedata(TFT_MAD_MX | TFT_MAD_MY | TFT_MAD_COLOR_ORDER); @@ -37,8 +61,16 @@ break; case 3: // Inverted landscape #ifdef CGRAM_OFFSET - colstart = 80; - rowstart = 0; + if (_init_width == 135) + { + colstart = 40; + rowstart = 52; + } + else + { + colstart = 80; + rowstart = 0; + } #endif writedata(TFT_MAD_MV | TFT_MAD_MY | TFT_MAD_COLOR_ORDER); diff --git a/User_Setup_Select.h b/User_Setup_Select.h index aae87cd..413f22b 100644 --- a/User_Setup_Select.h +++ b/User_Setup_Select.h @@ -50,7 +50,9 @@ //#include // Setup file for ESP32 and TTGO TM ST7789 SPI bus TFT //#include // Setup file configured for ST7789 240 x 240 -//#include // Setup file configured for my ST7735S 80x160 +//#include // Setup file configured for my ST7735S 80x160 + +//#include // Setup file for ESP8266 and ST7789 125 x 240 TFT //#include diff --git a/User_Setups/Setup135_ST7789.h b/User_Setups/Setup135_ST7789.h new file mode 100644 index 0000000..e87abad --- /dev/null +++ b/User_Setups/Setup135_ST7789.h @@ -0,0 +1,56 @@ +// ST7789 135 x 240 display with no chip select line + +#define ST7789_DRIVER // Configure all registers + +#define TFT_WIDTH 135 +#define TFT_HEIGHT 240 + +#define CGRAM_OFFSET // Library will add offsets required + +//#define TFT_RGB_ORDER TFT_RGB // Colour order Red-Green-Blue +//#define TFT_RGB_ORDER TFT_BGR // Colour order Blue-Green-Red + +//#define TFT_INVERSION_ON +//#define TFT_INVERSION_OFF + +// DSTIKE stepup +//#define TFT_DC 23 +//#define TFT_RST 32 +//#define TFT_MOSI 26 +//#define TFT_SCLK 27 + +// Generic ESP32 setup +//#define TFT_MISO 19 +//#define TFT_MOSI 23 +//#define TFT_SCLK 18 +//#define TFT_CS -1 // Not connected +//#define TFT_DC 2 +//#define TFT_RST 4 // Connect reset to ensure display initialises + +// For NodeMCU - use pin numbers in the form PIN_Dx where Dx is the NodeMCU pin designation +#define TFT_CS -1 // Define as not used +#define TFT_DC PIN_D1 // Data Command control pin +//#define TFT_RST PIN_D4 // TFT reset pin (could connect to NodeMCU RST, see next line) +#define TFT_RST -1 // TFT reset pin connect to NodeMCU RST, must also then add 10K pull down to TFT SCK + + +#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 + +#define SMOOTH_FONT + + +// #define SPI_FREQUENCY 27000000 +#define SPI_FREQUENCY 40000000 + +#define SPI_READ_FREQUENCY 20000000 + +#define SPI_TOUCH_FREQUENCY 2500000 + +// #define SUPPORT_TRANSACTIONS \ No newline at end of file diff --git a/library.json b/library.json index 203187d..0134170 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "TFT_eSPI", - "version": "1.4.15", + "version": "1.4.16", "keywords": "tft, ePaper, display, ESP8266, NodeMCU, ESP32, M5Stack, ILI9341, ST7735, ILI9163, S6D02A1, ILI9486, ST7789, RM68140", "description": "A TFT and ePaper SPI graphics library for ESP8266 and ESP32", "repository": diff --git a/library.properties b/library.properties index 9c964bb..3be6902 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=TFT_eSPI -version=1.4.15 +version=1.4.16 author=Bodmer maintainer=Bodmer sentence=A fast TFT graphics library for ESP8266 and ESP32 processors for the Arduino IDE From 4f68cf90e863bfbfb6f7ce984abb009d4d06ef8b Mon Sep 17 00:00:00 2001 From: Bodmer Date: Thu, 8 Aug 2019 09:56:52 +0100 Subject: [PATCH 281/287] update diagnostic version number --- TFT_eSPI.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TFT_eSPI.h b/TFT_eSPI.h index 75c1237..638f131 100644 --- a/TFT_eSPI.h +++ b/TFT_eSPI.h @@ -15,7 +15,7 @@ #ifndef _TFT_eSPIH_ #define _TFT_eSPIH_ -#define TFT_ESPI_VERSION "1.4.5" +#define TFT_ESPI_VERSION "1.4.16" //#define ESP32 //Just used to test ESP32 options From 0769e497187c2bb5eb05b851dc4cfbf95b9c88af Mon Sep 17 00:00:00 2001 From: Bodmer Date: Sat, 28 Sep 2019 21:54:42 +0100 Subject: [PATCH 282/287] Update Floyd_Steinberg_BMP.ino Correct compile error. --- examples/ePaper/Floyd_Steinberg/Floyd_Steinberg_BMP.ino | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/ePaper/Floyd_Steinberg/Floyd_Steinberg_BMP.ino b/examples/ePaper/Floyd_Steinberg/Floyd_Steinberg_BMP.ino index a5cc6cb..e52dbe0 100644 --- a/examples/ePaper/Floyd_Steinberg/Floyd_Steinberg_BMP.ino +++ b/examples/ePaper/Floyd_Steinberg/Floyd_Steinberg_BMP.ino @@ -156,8 +156,8 @@ void drawFSBmp(const char *filename, int16_t x, int16_t y) { *(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++; + bptr++; // Move along pixel and error buffers + qptr++; dx++; // Move coordinate along } y--; From 9a84fb156b7c6048fa8b335f3a9bbe2cf4315489 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Thu, 3 Oct 2019 23:53:43 +0100 Subject: [PATCH 283/287] Fix #424 --- examples/Generic/ESP8266_uncannyEyes/ESP8266_uncannyEyes.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/Generic/ESP8266_uncannyEyes/ESP8266_uncannyEyes.ino b/examples/Generic/ESP8266_uncannyEyes/ESP8266_uncannyEyes.ino index 0e3654e..4d5d4e7 100644 --- a/examples/Generic/ESP8266_uncannyEyes/ESP8266_uncannyEyes.ino +++ b/examples/Generic/ESP8266_uncannyEyes/ESP8266_uncannyEyes.ino @@ -141,7 +141,7 @@ void drawEye( // Renders one eye. Inputs must be pre-clipped & valid. // reset on each frame here in case of an SPI glitch. //eye[e].tft.setAddrWindow(319-127, 0, 319, 127); - eye[e].tft.setAddrWindow(0, 0, 127, 127); + eye[e].tft.setAddrWindow(0, 0, 128, 128); //digitalWrite(eye[e].cs, LOW); // Chip select From d3210a7ee62eec0270bdcc157f6604e99f399057 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Mon, 7 Oct 2019 17:56:11 +0100 Subject: [PATCH 284/287] Fix #417 and add for 135x240 display Setup 135 is for TTGO T Display (ESP32) Plus minor tweaks to avoid some warnings --- Extensions/Sprite.cpp | 18 +++++++-- TFT_eSPI.cpp | 4 +- User_Setup_Select.h | 1 + User_Setups/Setup25_TTGO_T_Display.h | 38 +++++++++++++++++++ .../One_bit_Yin_Yang/One_bit_Yin_Yang.ino | 2 +- library.json | 2 +- library.properties | 2 +- 7 files changed, 59 insertions(+), 8 deletions(-) create mode 100644 User_Setups/Setup25_TTGO_T_Display.h diff --git a/Extensions/Sprite.cpp b/Extensions/Sprite.cpp index dae49d1..6e734bd 100644 --- a/Extensions/Sprite.cpp +++ b/Extensions/Sprite.cpp @@ -464,7 +464,13 @@ void TFT_eSprite::pushSprite(int32_t x, int32_t y) { if (!_created) return; - if (_bpp == 16) _tft->pushImage(x, y, _iwidth, _iheight, _img ); + if (_bpp == 16) + { + bool oldSwapBytes = _tft->getSwapBytes(); + _tft->setSwapBytes(false); + _tft->pushImage(x, y, _iwidth, _iheight, _img ); + _tft->setSwapBytes(oldSwapBytes); + } else _tft->pushImage(x, y, _dwidth, _dheight, _img8, (bool)(_bpp == 8)); } @@ -478,7 +484,13 @@ void TFT_eSprite::pushSprite(int32_t x, int32_t y, uint16_t transp) { if (!_created) return; - if (_bpp == 16) _tft->pushImage(x, y, _iwidth, _iheight, _img, transp ); + if (_bpp == 16) + { + bool oldSwapBytes = _tft->getSwapBytes(); + _tft->setSwapBytes(false); + _tft->pushImage(x, y, _iwidth, _iheight, _img, transp ); + _tft->setSwapBytes(oldSwapBytes); + } else if (_bpp == 8) { transp = (uint8_t)((transp & 0xE000)>>8 | (transp & 0x0700)>>6 | (transp & 0x0018)>>3); @@ -716,7 +728,7 @@ void TFT_eSprite::pushImage(int32_t x, int32_t y, int32_t w, int32_t h, const u uint32_t ww = (w+7) & 0xFFF8; for (int32_t yp = 0; yp // Setup file for ESP32 and TTGO T4 (BTC) ILI9341 SPI bus TFT //#include // Setup file for ESP32 and TTGO TM ST7789 SPI bus TFT //#include // Setup file configured for ST7789 240 x 240 +//#include // Setup file for ESP32 and TTGO T-Display ST7789V SPI bus TFT //#include // Setup file configured for my ST7735S 80x160 diff --git a/User_Setups/Setup25_TTGO_T_Display.h b/User_Setups/Setup25_TTGO_T_Display.h new file mode 100644 index 0000000..18a0c2d --- /dev/null +++ b/User_Setups/Setup25_TTGO_T_Display.h @@ -0,0 +1,38 @@ +// Setup for the TTGO T4 ("Bitcoin Tracker") ESP32 board with 2.2" ILI9341 display + +// See SetupX_Template.h for all options available + +#define ST7789_DRIVER + +#define TFT_WIDTH 135 +#define TFT_HEIGHT 240 + +#define CGRAM_OFFSET // Library will add offsets required + +//#define TFT_MISO -1 + +#define TFT_MOSI 19 +#define TFT_SCLK 18 +#define TFT_CS 5 +#define TFT_DC 16 +#define TFT_RST 23 + +#define TFT_BL 4 // Display backlight control pin + +#define TFT_BACKLIGHT_ON HIGH // HIGH or LOW are options + +#define LOAD_GLCD +#define LOAD_FONT2 +#define LOAD_FONT4 +#define LOAD_FONT6 +#define LOAD_FONT7 +#define LOAD_FONT8 +#define LOAD_GFXFF + +#define SMOOTH_FONT + +//#define SPI_FREQUENCY 27000000 + #define SPI_FREQUENCY 40000000 // Maximum for ILI9341 + + +#define SPI_READ_FREQUENCY 6000000 // 6 MHz is the maximum SPI read speed for the ST7789V diff --git a/examples/Sprite/One_bit_Yin_Yang/One_bit_Yin_Yang.ino b/examples/Sprite/One_bit_Yin_Yang/One_bit_Yin_Yang.ino index 40fccb4..7067f7a 100644 --- a/examples/Sprite/One_bit_Yin_Yang/One_bit_Yin_Yang.ino +++ b/examples/Sprite/One_bit_Yin_Yang/One_bit_Yin_Yang.ino @@ -66,7 +66,7 @@ void loop() { // start_angle = 0 - 359 // r = radius -int yinyang(int x, int y, int start_angle, int r) +void yinyang(int x, int y, int start_angle, int r) { int x1 = 0; // getCoord() will update these int y1 = 0; diff --git a/library.json b/library.json index 0134170..bb138b6 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "TFT_eSPI", - "version": "1.4.16", + "version": "1.4.17", "keywords": "tft, ePaper, display, ESP8266, NodeMCU, ESP32, M5Stack, ILI9341, ST7735, ILI9163, S6D02A1, ILI9486, ST7789, RM68140", "description": "A TFT and ePaper SPI graphics library for ESP8266 and ESP32", "repository": diff --git a/library.properties b/library.properties index 3be6902..63a12ef 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=TFT_eSPI -version=1.4.16 +version=1.4.17 author=Bodmer maintainer=Bodmer sentence=A fast TFT graphics library for ESP8266 and ESP32 processors for the Arduino IDE From e9d405ea1f8b5fcca40cc197b30a84cdf115e54b Mon Sep 17 00:00:00 2001 From: Bodmer Date: Mon, 21 Oct 2019 22:56:35 +0100 Subject: [PATCH 285/287] Fix image cropping bug plus minor update Images/sprites overlapping both sides of the display were not correctly cropped. Option added to allow RGB<>BGR colourr swap option to be used. ESP8266 Wemos D1 R1 pin numbering difference accomodated. TTGO T4 setup changed to use HSPI port. --- README.md | 2 +- TFT_Drivers/ILI9341_Defines.h | 10 ++++++++++ TFT_Drivers/ILI9341_Init.h | 4 ++-- TFT_Drivers/ILI9341_Rotation.h | 32 ++++++++++++++++---------------- TFT_eSPI.cpp | 31 ++++++++++++++++--------------- TFT_eSPI.h | 2 +- User_Setup.h | 2 +- User_Setup_Select.h | 28 ++++++++++++++-------------- User_Setups/Setup22_TTGO_T4.h | 1 + User_Setups/SetupX_Template.h | 2 +- library.json | 2 +- library.properties | 2 +- 12 files changed, 65 insertions(+), 53 deletions(-) diff --git a/README.md b/README.md index 2befb58..6852147 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ https://github.com/Bodmer/TFT_eFX 5. The capability to read from an ST7789V TFT with a single bidirectional SDA pin has been added. At the moment this **ONLY** works with an ESP32. It is enabled with a #define TFT_SDA_READ in the setup file. -6. ST7789V displays are manufactured in two variants that have the red and blue pixels swapped, this is catered for by a new option in the setup file: +6. ST7789V and ILI9341 displays are manufactured in two variants that have the red and blue pixels swapped, this is catered for by a new option in the setup file: //#define TFT_RGB_ORDER TFT_RGB // Colour order Red-Green-Blue //#define TFT_RGB_ORDER TFT_BGR // Colour order Blue-Green-Red diff --git a/TFT_Drivers/ILI9341_Defines.h b/TFT_Drivers/ILI9341_Defines.h index bc58b7f..b675bfc 100644 --- a/TFT_Drivers/ILI9341_Defines.h +++ b/TFT_Drivers/ILI9341_Defines.h @@ -51,6 +51,16 @@ #define TFT_MAD_MH 0x04 #define TFT_MAD_RGB 0x00 +#ifdef TFT_RGB_ORDER + #if (TFT_RGB_ORDER == 1) + #define TFT_MAD_COLOR_ORDER TFT_MAD_RGB + #else + #define TFT_MAD_COLOR_ORDER TFT_MAD_BGR + #endif +#else + #define TFT_MAD_COLOR_ORDER TFT_MAD_BGR +#endif + #define TFT_INVOFF 0x20 #define TFT_INVON 0x21 diff --git a/TFT_Drivers/ILI9341_Init.h b/TFT_Drivers/ILI9341_Init.h index 404c3b9..7c763e0 100644 --- a/TFT_Drivers/ILI9341_Init.h +++ b/TFT_Drivers/ILI9341_Init.h @@ -56,9 +56,9 @@ writecommand(ILI9341_MADCTL); // Memory Access Control #ifdef M5STACK - writedata(0xA8); // Rotation 0 (portrait mode) + writedata(TFT_MAD_MY | TFT_MAD_MV | TFT_MAD_COLOR_ORDER); // Rotation 0 (portrait mode) #else - writedata(0x48); // Rotation 0 (portrait mode) + writedata(TFT_MAD_MX | TFT_MAD_COLOR_ORDER); // Rotation 0 (portrait mode) #endif writecommand(ILI9341_PIXFMT); diff --git a/TFT_Drivers/ILI9341_Rotation.h b/TFT_Drivers/ILI9341_Rotation.h index f5e9b38..f31b9df 100644 --- a/TFT_Drivers/ILI9341_Rotation.h +++ b/TFT_Drivers/ILI9341_Rotation.h @@ -7,36 +7,36 @@ switch (rotation) { case 0: #ifdef M5STACK - writedata(TFT_MAD_MY | TFT_MAD_MV | TFT_MAD_BGR); + writedata(TFT_MAD_MY | TFT_MAD_MV | TFT_MAD_COLOR_ORDER); #else - writedata(TFT_MAD_MX | TFT_MAD_BGR); + writedata(TFT_MAD_MX | TFT_MAD_COLOR_ORDER); #endif _width = _init_width; _height = _init_height; break; case 1: #ifdef M5STACK - writedata(TFT_MAD_BGR); + writedata(TFT_MAD_COLOR_ORDER); #else - writedata(TFT_MAD_MV | TFT_MAD_BGR); + writedata(TFT_MAD_MV | TFT_MAD_COLOR_ORDER); #endif _width = _init_height; _height = _init_width; break; case 2: #ifdef M5STACK - writedata(TFT_MAD_MV | TFT_MAD_MX | TFT_MAD_BGR); + writedata(TFT_MAD_MV | TFT_MAD_MX | TFT_MAD_COLOR_ORDER); #else - writedata(TFT_MAD_MY | TFT_MAD_BGR); + writedata(TFT_MAD_MY | TFT_MAD_COLOR_ORDER); #endif _width = _init_width; _height = _init_height; break; case 3: #ifdef M5STACK - writedata(TFT_MAD_MX | TFT_MAD_MY | TFT_MAD_BGR); + writedata(TFT_MAD_MX | TFT_MAD_MY | TFT_MAD_COLOR_ORDER); #else - writedata(TFT_MAD_MX | TFT_MAD_MY | TFT_MAD_MV | TFT_MAD_BGR); + writedata(TFT_MAD_MX | TFT_MAD_MY | TFT_MAD_MV | TFT_MAD_COLOR_ORDER); #endif _width = _init_height; _height = _init_width; @@ -44,36 +44,36 @@ // These next rotations are for bottom up BMP drawing case 4: #ifdef M5STACK - writedata(TFT_MAD_MX | TFT_MAD_MY | TFT_MAD_MV | TFT_MAD_BGR); + writedata(TFT_MAD_MX | TFT_MAD_MY | TFT_MAD_MV | TFT_MAD_COLOR_ORDER); #else - writedata(TFT_MAD_MX | TFT_MAD_MY | TFT_MAD_BGR); + writedata(TFT_MAD_MX | TFT_MAD_MY | TFT_MAD_COLOR_ORDER); #endif _width = _init_width; _height = _init_height; break; case 5: #ifdef M5STACK - writedata(TFT_MAD_MY | TFT_MAD_BGR); + writedata(TFT_MAD_MY | TFT_MAD_COLOR_ORDER); #else - writedata(TFT_MAD_MV | TFT_MAD_MX | TFT_MAD_BGR); + writedata(TFT_MAD_MV | TFT_MAD_MX | TFT_MAD_COLOR_ORDER); #endif _width = _init_height; _height = _init_width; break; case 6: #ifdef M5STACK - writedata(TFT_MAD_MV | TFT_MAD_BGR); + writedata(TFT_MAD_MV | TFT_MAD_COLOR_ORDER); #else - writedata(TFT_MAD_BGR); + writedata(TFT_MAD_COLOR_ORDER); #endif _width = _init_width; _height = _init_height; break; case 7: #ifdef M5STACK - writedata(TFT_MAD_MX | TFT_MAD_BGR); + writedata(TFT_MAD_MX | TFT_MAD_COLOR_ORDER); #else - writedata(TFT_MAD_MY | TFT_MAD_MV | TFT_MAD_BGR); + writedata(TFT_MAD_MY | TFT_MAD_MV | TFT_MAD_COLOR_ORDER); #endif _width = _init_height; _height = _init_width; diff --git a/TFT_eSPI.cpp b/TFT_eSPI.cpp index 1c07d9f..7414516 100644 --- a/TFT_eSPI.cpp +++ b/TFT_eSPI.cpp @@ -67,7 +67,7 @@ inline void TFT_eSPI::spi_end(void){ SPI1U = SPI1U_READ; #endif #else - if(!inTransaction) CS_H; + if(!inTransaction) {CS_H;} #endif } @@ -92,7 +92,7 @@ inline void TFT_eSPI::spi_end_read(void){ #if !defined(ESP32_PARALLEL) spi.setFrequency(SPI_FREQUENCY); #endif - if(!inTransaction) CS_H; + if(!inTransaction) {CS_H;} #endif #ifdef ESP8266 SPI1U = SPI1U_WRITE; @@ -999,8 +999,8 @@ void TFT_eSPI::pushImage(int32_t x, int32_t y, int32_t w, int32_t h, uint16_t *d 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 + dw) > _width ) dw = _width - x; + if ((y + dh) > _height) dh = _height - y; if (dw < 1 || dh < 1) return; @@ -1038,8 +1038,8 @@ void TFT_eSPI::pushImage(int32_t x, int32_t y, int32_t w, int32_t h, uint16_t *d 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 + dw) > _width ) dw = _width - x; + if ((y + dh) > _height) dh = _height - y; if (dw < 1 || dh < 1) return; @@ -1113,8 +1113,8 @@ void TFT_eSPI::pushImage(int32_t x, int32_t y, int32_t w, int32_t h, const uint1 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 + dw) > _width ) dw = _width - x; + if ((y + dh) > _height) dh = _height - y; if (dw < 1 || dh < 1) return; @@ -1177,8 +1177,8 @@ void TFT_eSPI::pushImage(int32_t x, int32_t y, int32_t w, int32_t h, const uint1 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 + dw) > _width ) dw = _width - x; + if ((y + dh) > _height) dh = _height - y; if (dw < 1 || dh < 1) return; @@ -1251,8 +1251,8 @@ void TFT_eSPI::pushImage(int32_t x, int32_t y, int32_t w, int32_t h, uint8_t *da 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 + dw) > _width ) dw = _width - x; + if ((y + dh) > _height) dh = _height - y; if (dw < 1 || dh < 1) return; @@ -1357,8 +1357,8 @@ void TFT_eSPI::pushImage(int32_t x, int32_t y, int32_t w, int32_t h, uint8_t *da 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 + dw) > _width ) dw = _width - x; + if ((y + dh) > _height) dh = _height - y; if (dw < 1 || dh < 1) return; @@ -3938,6 +3938,7 @@ void TFT_eSPI::setAttribute(uint8_t attr_id, uint8_t param) { break; case 2: _utf8 = param; + decoderState = 0; break; //case 3: // TBD future feature control // _tbd = param; @@ -3998,7 +3999,7 @@ uint16_t TFT_eSPI::decodeUTF8(uint8_t c) return 0; } // 21 bit Unicode Code Point not supported so fall-back to extended ASCII - if ((c & 0xF8) == 0xF0) return (uint16_t)c; + // if ((c & 0xF8) == 0xF0) return (uint16_t)c; } else { diff --git a/TFT_eSPI.h b/TFT_eSPI.h index 638f131..117a819 100644 --- a/TFT_eSPI.h +++ b/TFT_eSPI.h @@ -15,7 +15,7 @@ #ifndef _TFT_eSPIH_ #define _TFT_eSPIH_ -#define TFT_ESPI_VERSION "1.4.16" +#define TFT_ESPI_VERSION "1.4.18" //#define ESP32 //Just used to test ESP32 options diff --git a/User_Setup.h b/User_Setup.h index 1f70a3f..95b3ef1 100644 --- a/User_Setup.h +++ b/User_Setup.h @@ -36,7 +36,7 @@ // #define TFT_SDA_READ // This option is for ESP32 ONLY, tested with ST7789 display only -// For ST7789 ONLY, define the colour order IF the blue and red are swapped on your display +// For ST7789 and ILI9341 ONLY, define the colour order IF the blue and red are swapped on your display // Try ONE option at a time to find the correct colour order for your display // #define TFT_RGB_ORDER TFT_RGB // Colour order Red-Green-Blue diff --git a/User_Setup_Select.h b/User_Setup_Select.h index e58ebae..fc620ac 100644 --- a/User_Setup_Select.h +++ b/User_Setup_Select.h @@ -126,21 +126,21 @@ #endif -// These are the pins for all ESP8266 boards -// Name GPIO Function -#define PIN_D0 16 // WAKE -#define PIN_D1 5 // User purpose -#define PIN_D2 4 // User purpose -#define PIN_D3 0 // Low on boot means enter FLASH mode -#define PIN_D4 2 // TXD1 (must be high on boot to go to UART0 FLASH mode) -#define PIN_D5 14 // HSCLK -#define PIN_D6 12 // HMISO -#define PIN_D7 13 // HMOSI RXD2 -#define PIN_D8 15 // HCS TXD0 (must be low on boot to enter UART0 FLASH mode) -#define PIN_D9 3 // RXD0 -#define PIN_D10 1 // TXD0 +// These are the pins for ESP8266 boards +// Name GPIO NodeMCU Function +#define PIN_D0 D0 // GPIO16 WAKE +#define PIN_D1 D1 // GPIO5 User purpose +#define PIN_D2 D2 // GPIO4 User purpose +#define PIN_D3 D3 // GPIO0 Low on boot means enter FLASH mode +#define PIN_D4 D4 // GPIO2 TXD1 (must be high on boot to go to UART0 FLASH mode) +#define PIN_D5 D5 // GPIO14 HSCLK +#define PIN_D6 D6 // GPIO12 HMISO +#define PIN_D7 D7 // GPIO13 HMOSI RXD2 +#define PIN_D8 D8 // GPIO15 HCS TXD0 (must be low on boot to enter UART0 FLASH mode) +#define PIN_D9 3 // RXD0 +#define PIN_D10 1 // TXD0 -#define PIN_MOSI 8 // SD1 +#define PIN_MOSI 8 // SD1 FLASH and overlap mode #define PIN_MISO 7 // SD0 #define PIN_SCLK 6 // CLK #define PIN_HWCS 0 // D3 diff --git a/User_Setups/Setup22_TTGO_T4.h b/User_Setups/Setup22_TTGO_T4.h index cfed5b5..a62af13 100644 --- a/User_Setups/Setup22_TTGO_T4.h +++ b/User_Setups/Setup22_TTGO_T4.h @@ -25,5 +25,6 @@ //#define SPI_FREQUENCY 27000000 #define SPI_FREQUENCY 40000000 // Maximum for ILI9341 +#define USE_HSPI_PORT #define SPI_READ_FREQUENCY 6000000 // 6 MHz is the maximum SPI read speed for the ST7789V diff --git a/User_Setups/SetupX_Template.h b/User_Setups/SetupX_Template.h index 534a634..a902f8b 100644 --- a/User_Setups/SetupX_Template.h +++ b/User_Setups/SetupX_Template.h @@ -35,7 +35,7 @@ // #define TFT_SDA_READ // This option if for ESP32 ONLY, tested with ST7789 display only -// For ST7789 ONLY, define the colour order IF the blue and red are swapped on your display +// For ST7789 and ILI9341 ONLY, define the colour order IF the blue and red are swapped on your display // Try ONE option at a time to find the correct colour order for your display // #define TFT_RGB_ORDER TFT_RGB // Colour order Red-Green-Blue diff --git a/library.json b/library.json index bb138b6..b03c7b4 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "TFT_eSPI", - "version": "1.4.17", + "version": "1.4.18", "keywords": "tft, ePaper, display, ESP8266, NodeMCU, ESP32, M5Stack, ILI9341, ST7735, ILI9163, S6D02A1, ILI9486, ST7789, RM68140", "description": "A TFT and ePaper SPI graphics library for ESP8266 and ESP32", "repository": diff --git a/library.properties b/library.properties index 63a12ef..b7bf13c 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=TFT_eSPI -version=1.4.17 +version=1.4.18 author=Bodmer maintainer=Bodmer sentence=A fast TFT graphics library for ESP8266 and ESP32 processors for the Arduino IDE From bd23626b53aa1c2366b818d47f34da79aaf92b70 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Tue, 22 Oct 2019 23:01:33 +0100 Subject: [PATCH 286/287] Correct coordinate cropping in Sprite --- Extensions/Sprite.cpp | 45 ++++++++++++++++--------------------------- 1 file changed, 17 insertions(+), 28 deletions(-) diff --git a/Extensions/Sprite.cpp b/Extensions/Sprite.cpp index 6e734bd..2c8573e 100644 --- a/Extensions/Sprite.cpp +++ b/Extensions/Sprite.cpp @@ -569,11 +569,11 @@ void TFT_eSprite::pushImage(int32_t x, int32_t y, int32_t w, int32_t h, uint16_ uint32_t ws = w; uint32_t hs = h; - if (x < 0) { xo = -x; xs = 0; } - if (y < 0) { yo = -y; ys = 0; } + if (x < 0) { xo = -x; ws += x; xs = 0; } + if (y < 0) { yo = -y; hs += y; ys = 0; } - if (xs + w >= _iwidth) ws = _iwidth - xs; - if (ys + h >= _iheight) hs = _iheight - ys; + if (xs + ws >= _iwidth) ws = _iwidth - xs; + if (ys + hs >= _iheight) hs = _iheight - ys; if (_bpp == 16) // Plot a 16 bpp image into a 16 bpp Sprite { @@ -665,11 +665,11 @@ void TFT_eSprite::pushImage(int32_t x, int32_t y, int32_t w, int32_t h, const u uint32_t ws = w; uint32_t hs = h; - if (x < 0) { xo = -x; xs = 0; } - if (y < 0) { yo = -y; ys = 0; } + if (x < 0) { xo = -x; ws += x; xs = 0; } + if (y < 0) { yo = -y; hs += y; ys = 0; } - if (xs + w >= _iwidth) ws = _iwidth - xs; - if (ys + h >= _iheight) hs = _iheight - ys; + if (xs + ws >= _iwidth) ws = _iwidth - xs; + if (ys + hs >= _iheight) hs = _iheight - ys; if (_bpp == 16) // Plot a 16 bpp image into a 16 bpp Sprite { @@ -768,22 +768,10 @@ bool TFT_eSprite::getSwapBytes(void) *************************************************************************************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) + if ((x0 >= _iwidth) || (x1 < 0) || (y0 >= _iheight) || (y1 < 0)) { // Point to that extra "off screen" pixel _xs = 0; _ys = _iheight; @@ -792,6 +780,11 @@ void TFT_eSprite::setWindow(int32_t x0, int32_t y0, int32_t x1, int32_t y1) } else { + if (x0 < 0) x0 = 0; + if (x1 >= _iwidth) x1 = _iwidth - 1; + if (y0 < 0) y0 = 0; + if (y1 >= _iheight) y1 = _iheight - 1; + _xs = x0; _ys = y0; _xe = x1; @@ -893,8 +886,8 @@ void TFT_eSprite::setScrollRect(int32_t x, int32_t y, int32_t w, int32_t h, uint { if ((x >= _iwidth) || (y >= _iheight) || !_created ) return; - if (x < 0) x = 0; - if (y < 0) y = 0; + if (x < 0) { w += x; x = 0; } + if (y < 0) { h += y; y = 0; } if ((x + w) > _iwidth ) w = _iwidth - x; if ((y + h) > _iheight) h = _iheight - y; @@ -1100,23 +1093,19 @@ void TFT_eSprite::drawPixel(int32_t x, int32_t y, uint32_t color) { // Range checking if ((x < 0) || (y < 0) || !_created) return; + if ((x >= _iwidth) || (y >= _iheight)) return; if (_bpp == 16) { - if ((x >= _iwidth) || (y >= _iheight)) return; color = (color >> 8) | (color << 8); _img[x+y*_iwidth] = (uint16_t) color; } else if (_bpp == 8) { - if ((x >= _iwidth) || (y >= _iheight)) return; _img8[x+y*_iwidth] = (uint8_t)((color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3); } else // 1 bpp { - - if ((x >= _iwidth) || (y >= _iheight)) return; - if (_rotation == 1) { uint16_t tx = x; From 27b6509f276ede5f3c2ccf93bfbd6e366e0781bb Mon Sep 17 00:00:00 2001 From: Bodmer Date: Tue, 22 Oct 2019 23:02:41 +0100 Subject: [PATCH 287/287] Raise version --- TFT_eSPI.h | 2 +- library.json | 2 +- library.properties | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/TFT_eSPI.h b/TFT_eSPI.h index 117a819..7f7622d 100644 --- a/TFT_eSPI.h +++ b/TFT_eSPI.h @@ -15,7 +15,7 @@ #ifndef _TFT_eSPIH_ #define _TFT_eSPIH_ -#define TFT_ESPI_VERSION "1.4.18" +#define TFT_ESPI_VERSION "1.4.20" //#define ESP32 //Just used to test ESP32 options diff --git a/library.json b/library.json index b03c7b4..ac9e8b9 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "TFT_eSPI", - "version": "1.4.18", + "version": "1.4.20", "keywords": "tft, ePaper, display, ESP8266, NodeMCU, ESP32, M5Stack, ILI9341, ST7735, ILI9163, S6D02A1, ILI9486, ST7789, RM68140", "description": "A TFT and ePaper SPI graphics library for ESP8266 and ESP32", "repository": diff --git a/library.properties b/library.properties index b7bf13c..aea4881 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=TFT_eSPI -version=1.4.18 +version=1.4.20 author=Bodmer maintainer=Bodmer sentence=A fast TFT graphics library for ESP8266 and ESP32 processors for the Arduino IDE

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 z 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