/*************************************************** 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 ****************************************************/ #include "Display.h" #include #include #define MADCTL_MY 0x80 #define MADCTL_MX 0x40 #define MADCTL_MV 0x20 #define MADCTL_ML 0x10 #define MADCTL_RGB 0x00 #define MADCTL_BGR 0x08 #define MADCTL_MH 0x04 // Many (but maybe not all) non-AVR board installs define macros // for compatibility with existing PROGMEM-reading AVR code. // Do our own checks and defines here for good measure... #ifndef pgm_read_byte #define pgm_read_byte(addr) (*(const unsigned char *)(addr)) #endif #ifndef pgm_read_word #define pgm_read_word(addr) (*(const unsigned short *)(addr)) #endif #ifndef pgm_read_dword #define pgm_read_dword(addr) (*(const unsigned long *)(addr)) #endif // Pointers are a peculiar case...typically 16-bit on AVR boards, // 32 bits elsewhere. Try to accommodate both... #if !defined(__INT_MAX__) || (__INT_MAX__ > 0xFFFF) #define pgm_read_pointer(addr) ((void *)pgm_read_dword(addr)) #else #define pgm_read_pointer(addr) ((void *)pgm_read_word(addr)) #endif #ifndef min #define min(a,b) (((a) < (b)) ? (a) : (b)) #endif #ifndef _swap_int16_t #define _swap_int16_t(a, b) { int16_t t = a; a = b; b = t; } #endif /* * Control Pins * */ #ifdef USE_FAST_PINIO #define SPI_DC_HIGH() *dcport |= dcpinmask #define SPI_DC_LOW() *dcport &= ~dcpinmask #define SPI_CS_HIGH() *csport |= cspinmask #define SPI_CS_LOW() *csport &= ~cspinmask #else #define SPI_DC_HIGH() digitalWrite(_dc, HIGH) #define SPI_DC_LOW() digitalWrite(_dc, LOW) #define SPI_CS_HIGH() digitalWrite(_cs, HIGH) #define SPI_CS_LOW() digitalWrite(_cs, LOW) #endif /* * Software SPI Macros * */ #ifdef USE_FAST_PINIO #define SSPI_MOSI_HIGH() *mosiport |= mosipinmask #define SSPI_MOSI_LOW() *mosiport &= ~mosipinmask #define SSPI_SCK_HIGH() *clkport |= clkpinmask #define SSPI_SCK_LOW() *clkport &= ~clkpinmask #define SSPI_MISO_READ() ((*misoport & misopinmask) != 0) #else #define SSPI_MOSI_HIGH() digitalWrite(_mosi, HIGH) #define SSPI_MOSI_LOW() digitalWrite(_mosi, LOW) #define SSPI_SCK_HIGH() digitalWrite(_sclk, HIGH) #define SSPI_SCK_LOW() digitalWrite(_sclk, LOW) #define SSPI_MISO_READ() digitalRead(_miso) #endif #define SSPI_BEGIN_TRANSACTION() #define SSPI_END_TRANSACTION() #define SSPI_WRITE(v) spiWrite(v) #define SSPI_WRITE16(s) SSPI_WRITE((s) >> 8); SSPI_WRITE(s) #define SSPI_WRITE32(l) SSPI_WRITE((l) >> 24); SSPI_WRITE((l) >> 16); SSPI_WRITE((l) >> 8); SSPI_WRITE(l) #define SSPI_WRITE_PIXELS(c,l) for(uint32_t i=0; i<(l); i+=2){ SSPI_WRITE(((uint8_t*)(c))[i+1]); SSPI_WRITE(((uint8_t*)(c))[i]); } /* * Hardware SPI Macros * */ #define SPI_OBJECT _spi #define HSPI_SET_CLOCK() SPI_OBJECT.setFrequency(_freq); #ifdef SPI_HAS_TRANSACTION #define HSPI_BEGIN_TRANSACTION() SPI_OBJECT.beginTransaction(SPISettings(_freq, MSBFIRST, SPI_MODE0)) #define HSPI_END_TRANSACTION() SPI_OBJECT.endTransaction() #else #define HSPI_BEGIN_TRANSACTION() HSPI_SET_CLOCK(); SPI_OBJECT.setBitOrder(MSBFIRST); SPI_OBJECT.setDataMode(SPI_MODE0) #define HSPI_END_TRANSACTION() #endif #ifdef ESP32 #define SPI_HAS_WRITE_PIXELS #endif #if defined(ESP8266) || defined(ESP32) // Optimized SPI (ESP8266 and ESP32) #define HSPI_READ() SPI_OBJECT.transfer(0) #define HSPI_WRITE(b) SPI_OBJECT.write(b) #define HSPI_WRITE16(s) SPI_OBJECT.write16(s) #define HSPI_WRITE32(l) SPI_OBJECT.write32(l) #ifdef SPI_HAS_WRITE_PIXELS #define SPI_MAX_PIXELS_AT_ONCE 32 #define HSPI_WRITE_PIXELS(c,l) SPI_OBJECT.writePixels(c,l) #else #define HSPI_WRITE_PIXELS(c,l) for(uint32_t i=0; i<((l)/2); i++){ SPI_WRITE16(((uint16_t*)(c))[i]); } #endif #else // Standard Byte-by-Byte SPI #if defined (__AVR__) || defined(TEENSYDUINO) static inline uint8_t _avr_spi_read(void) __attribute__((always_inline)); static inline uint8_t _avr_spi_read(void) { uint8_t r = 0; SPDR = r; while(!(SPSR & _BV(SPIF))); r = SPDR; return r; } #define HSPI_WRITE(b) {SPDR = (b); while(!(SPSR & _BV(SPIF)));} #define HSPI_READ() _avr_spi_read() #else #define HSPI_WRITE(b) SPI_OBJECT.transfer((uint8_t)(b)) #define HSPI_READ() HSPI_WRITE(0) #endif #define HSPI_WRITE16(s) HSPI_WRITE((s) >> 8); HSPI_WRITE(s) #define HSPI_WRITE32(l) HSPI_WRITE((l) >> 24); HSPI_WRITE((l) >> 16); HSPI_WRITE((l) >> 8); HSPI_WRITE(l) #define HSPI_WRITE_PIXELS(c,l) for(uint32_t i=0; i<(l); i+=2){ HSPI_WRITE(((uint8_t*)(c))[i+1]); HSPI_WRITE(((uint8_t*)(c))[i]); } #endif /* * Final SPI Macros * */ #define SPI_DEFAULT_FREQ 78000000 #define SPI_BEGIN() if(_sclk < 0){SPI_OBJECT.begin();} #define SPI_BEGIN_TRANSACTION() if(_sclk < 0){HSPI_BEGIN_TRANSACTION();} #define SPI_END_TRANSACTION() if(_sclk < 0){HSPI_END_TRANSACTION();} #define SPI_WRITE16(s) if(_sclk < 0){HSPI_WRITE16(s);}else{SSPI_WRITE16(s);} #define SPI_WRITE32(l) if(_sclk < 0){HSPI_WRITE32(l);}else{SSPI_WRITE32(l);} #define SPI_WRITE_PIXELS(c,l) if(_sclk < 0){HSPI_WRITE_PIXELS(c,l);}else{SSPI_WRITE_PIXELS(c,l);} // Standard ASCII 5x7 font static const unsigned char font[] PROGMEM = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x5B, 0x4F, 0x5B, 0x3E, 0x3E, 0x6B, 0x4F, 0x6B, 0x3E, 0x1C, 0x3E, 0x7C, 0x3E, 0x1C, 0x18, 0x3C, 0x7E, 0x3C, 0x18, 0x1C, 0x57, 0x7D, 0x57, 0x1C, 0x1C, 0x5E, 0x7F, 0x5E, 0x1C, 0x00, 0x18, 0x3C, 0x18, 0x00, 0xFF, 0xE7, 0xC3, 0xE7, 0xFF, 0x00, 0x18, 0x24, 0x18, 0x00, 0xFF, 0xE7, 0xDB, 0xE7, 0xFF, 0x30, 0x48, 0x3A, 0x06, 0x0E, 0x26, 0x29, 0x79, 0x29, 0x26, 0x40, 0x7F, 0x05, 0x05, 0x07, 0x40, 0x7F, 0x05, 0x25, 0x3F, 0x5A, 0x3C, 0xE7, 0x3C, 0x5A, 0x7F, 0x3E, 0x1C, 0x1C, 0x08, 0x08, 0x1C, 0x1C, 0x3E, 0x7F, 0x14, 0x22, 0x7F, 0x22, 0x14, 0x5F, 0x5F, 0x00, 0x5F, 0x5F, 0x06, 0x09, 0x7F, 0x01, 0x7F, 0x00, 0x66, 0x89, 0x95, 0x6A, 0x60, 0x60, 0x60, 0x60, 0x60, 0x94, 0xA2, 0xFF, 0xA2, 0x94, 0x08, 0x04, 0x7E, 0x04, 0x08, 0x10, 0x20, 0x7E, 0x20, 0x10, 0x08, 0x08, 0x2A, 0x1C, 0x08, 0x08, 0x1C, 0x2A, 0x08, 0x08, 0x1E, 0x10, 0x10, 0x10, 0x10, 0x0C, 0x1E, 0x0C, 0x1E, 0x0C, 0x30, 0x38, 0x3E, 0x38, 0x30, 0x06, 0x0E, 0x3E, 0x0E, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x00, 0x00, 0x07, 0x00, 0x07, 0x00, 0x14, 0x7F, 0x14, 0x7F, 0x14, 0x24, 0x2A, 0x7F, 0x2A, 0x12, 0x23, 0x13, 0x08, 0x64, 0x62, 0x36, 0x49, 0x56, 0x20, 0x50, 0x00, 0x08, 0x07, 0x03, 0x00, 0x00, 0x1C, 0x22, 0x41, 0x00, 0x00, 0x41, 0x22, 0x1C, 0x00, 0x2A, 0x1C, 0x7F, 0x1C, 0x2A, 0x08, 0x08, 0x3E, 0x08, 0x08, 0x00, 0x80, 0x70, 0x30, 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, 0x60, 0x60, 0x00, 0x20, 0x10, 0x08, 0x04, 0x02, 0x3E, 0x51, 0x49, 0x45, 0x3E, 0x00, 0x42, 0x7F, 0x40, 0x00, 0x72, 0x49, 0x49, 0x49, 0x46, 0x21, 0x41, 0x49, 0x4D, 0x33, 0x18, 0x14, 0x12, 0x7F, 0x10, 0x27, 0x45, 0x45, 0x45, 0x39, 0x3C, 0x4A, 0x49, 0x49, 0x31, 0x41, 0x21, 0x11, 0x09, 0x07, 0x36, 0x49, 0x49, 0x49, 0x36, 0x46, 0x49, 0x49, 0x29, 0x1E, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x40, 0x34, 0x00, 0x00, 0x00, 0x08, 0x14, 0x22, 0x41, 0x14, 0x14, 0x14, 0x14, 0x14, 0x00, 0x41, 0x22, 0x14, 0x08, 0x02, 0x01, 0x59, 0x09, 0x06, 0x3E, 0x41, 0x5D, 0x59, 0x4E, 0x7C, 0x12, 0x11, 0x12, 0x7C, 0x7F, 0x49, 0x49, 0x49, 0x36, 0x3E, 0x41, 0x41, 0x41, 0x22, 0x7F, 0x41, 0x41, 0x41, 0x3E, 0x7F, 0x49, 0x49, 0x49, 0x41, 0x7F, 0x09, 0x09, 0x09, 0x01, 0x3E, 0x41, 0x41, 0x51, 0x73, 0x7F, 0x08, 0x08, 0x08, 0x7F, 0x00, 0x41, 0x7F, 0x41, 0x00, 0x20, 0x40, 0x41, 0x3F, 0x01, 0x7F, 0x08, 0x14, 0x22, 0x41, 0x7F, 0x40, 0x40, 0x40, 0x40, 0x7F, 0x02, 0x1C, 0x02, 0x7F, 0x7F, 0x04, 0x08, 0x10, 0x7F, 0x3E, 0x41, 0x41, 0x41, 0x3E, 0x7F, 0x09, 0x09, 0x09, 0x06, 0x3E, 0x41, 0x51, 0x21, 0x5E, 0x7F, 0x09, 0x19, 0x29, 0x46, 0x26, 0x49, 0x49, 0x49, 0x32, 0x03, 0x01, 0x7F, 0x01, 0x03, 0x3F, 0x40, 0x40, 0x40, 0x3F, 0x1F, 0x20, 0x40, 0x20, 0x1F, 0x3F, 0x40, 0x38, 0x40, 0x3F, 0x63, 0x14, 0x08, 0x14, 0x63, 0x03, 0x04, 0x78, 0x04, 0x03, 0x61, 0x59, 0x49, 0x4D, 0x43, 0x00, 0x7F, 0x41, 0x41, 0x41, 0x02, 0x04, 0x08, 0x10, 0x20, 0x00, 0x41, 0x41, 0x41, 0x7F, 0x04, 0x02, 0x01, 0x02, 0x04, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x03, 0x07, 0x08, 0x00, 0x20, 0x54, 0x54, 0x78, 0x40, 0x7F, 0x28, 0x44, 0x44, 0x38, 0x38, 0x44, 0x44, 0x44, 0x28, 0x38, 0x44, 0x44, 0x28, 0x7F, 0x38, 0x54, 0x54, 0x54, 0x18, 0x00, 0x08, 0x7E, 0x09, 0x02, 0x18, 0xA4, 0xA4, 0x9C, 0x78, 0x7F, 0x08, 0x04, 0x04, 0x78, 0x00, 0x44, 0x7D, 0x40, 0x00, 0x20, 0x40, 0x40, 0x3D, 0x00, 0x7F, 0x10, 0x28, 0x44, 0x00, 0x00, 0x41, 0x7F, 0x40, 0x00, 0x7C, 0x04, 0x78, 0x04, 0x78, 0x7C, 0x08, 0x04, 0x04, 0x78, 0x38, 0x44, 0x44, 0x44, 0x38, 0xFC, 0x18, 0x24, 0x24, 0x18, 0x18, 0x24, 0x24, 0x18, 0xFC, 0x7C, 0x08, 0x04, 0x04, 0x08, 0x48, 0x54, 0x54, 0x54, 0x24, 0x04, 0x04, 0x3F, 0x44, 0x24, 0x3C, 0x40, 0x40, 0x20, 0x7C, 0x1C, 0x20, 0x40, 0x20, 0x1C, 0x3C, 0x40, 0x30, 0x40, 0x3C, 0x44, 0x28, 0x10, 0x28, 0x44, 0x4C, 0x90, 0x90, 0x90, 0x7C, 0x44, 0x64, 0x54, 0x4C, 0x44, 0x00, 0x08, 0x36, 0x41, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, 0x41, 0x36, 0x08, 0x00, 0x02, 0x01, 0x02, 0x04, 0x02, 0x3C, 0x26, 0x23, 0x26, 0x3C, 0x1E, 0xA1, 0xA1, 0x61, 0x12, 0x3A, 0x40, 0x40, 0x20, 0x7A, 0x38, 0x54, 0x54, 0x55, 0x59, 0x21, 0x55, 0x55, 0x79, 0x41, 0x22, 0x54, 0x54, 0x78, 0x42, // a-umlaut 0x21, 0x55, 0x54, 0x78, 0x40, 0x20, 0x54, 0x55, 0x79, 0x40, 0x0C, 0x1E, 0x52, 0x72, 0x12, 0x39, 0x55, 0x55, 0x55, 0x59, 0x39, 0x54, 0x54, 0x54, 0x59, 0x39, 0x55, 0x54, 0x54, 0x58, 0x00, 0x00, 0x45, 0x7C, 0x41, 0x00, 0x02, 0x45, 0x7D, 0x42, 0x00, 0x01, 0x45, 0x7C, 0x40, 0x7D, 0x12, 0x11, 0x12, 0x7D, // A-umlaut 0xF0, 0x28, 0x25, 0x28, 0xF0, 0x7C, 0x54, 0x55, 0x45, 0x00, 0x20, 0x54, 0x54, 0x7C, 0x54, 0x7C, 0x0A, 0x09, 0x7F, 0x49, 0x32, 0x49, 0x49, 0x49, 0x32, 0x3A, 0x44, 0x44, 0x44, 0x3A, // o-umlaut 0x32, 0x4A, 0x48, 0x48, 0x30, 0x3A, 0x41, 0x41, 0x21, 0x7A, 0x3A, 0x42, 0x40, 0x20, 0x78, 0x00, 0x9D, 0xA0, 0xA0, 0x7D, 0x3D, 0x42, 0x42, 0x42, 0x3D, // O-umlaut 0x3D, 0x40, 0x40, 0x40, 0x3D, 0x3C, 0x24, 0xFF, 0x24, 0x24, 0x48, 0x7E, 0x49, 0x43, 0x66, 0x2B, 0x2F, 0xFC, 0x2F, 0x2B, 0xFF, 0x09, 0x29, 0xF6, 0x20, 0xC0, 0x88, 0x7E, 0x09, 0x03, 0x20, 0x54, 0x54, 0x79, 0x41, 0x00, 0x00, 0x44, 0x7D, 0x41, 0x30, 0x48, 0x48, 0x4A, 0x32, 0x38, 0x40, 0x40, 0x22, 0x7A, 0x00, 0x7A, 0x0A, 0x0A, 0x72, 0x7D, 0x0D, 0x19, 0x31, 0x7D, 0x26, 0x29, 0x29, 0x2F, 0x28, 0x26, 0x29, 0x29, 0x29, 0x26, 0x30, 0x48, 0x4D, 0x40, 0x20, 0x38, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x38, 0x2F, 0x10, 0xC8, 0xAC, 0xBA, 0x2F, 0x10, 0x28, 0x34, 0xFA, 0x00, 0x00, 0x7B, 0x00, 0x00, 0x08, 0x14, 0x2A, 0x14, 0x22, 0x22, 0x14, 0x2A, 0x14, 0x08, 0x55, 0x00, 0x55, 0x00, 0x55, // #176 (25% block) missing in old code 0xAA, 0x55, 0xAA, 0x55, 0xAA, // 50% block 0xFF, 0x55, 0xFF, 0x55, 0xFF, // 75% block 0x00, 0x00, 0x00, 0xFF, 0x00, 0x10, 0x10, 0x10, 0xFF, 0x00, 0x14, 0x14, 0x14, 0xFF, 0x00, 0x10, 0x10, 0xFF, 0x00, 0xFF, 0x10, 0x10, 0xF0, 0x10, 0xF0, 0x14, 0x14, 0x14, 0xFC, 0x00, 0x14, 0x14, 0xF7, 0x00, 0xFF, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x14, 0x14, 0xF4, 0x04, 0xFC, 0x14, 0x14, 0x17, 0x10, 0x1F, 0x10, 0x10, 0x1F, 0x10, 0x1F, 0x14, 0x14, 0x14, 0x1F, 0x00, 0x10, 0x10, 0x10, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x10, 0x10, 0x10, 0x10, 0x1F, 0x10, 0x10, 0x10, 0x10, 0xF0, 0x10, 0x00, 0x00, 0x00, 0xFF, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0xFF, 0x10, 0x00, 0x00, 0x00, 0xFF, 0x14, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x1F, 0x10, 0x17, 0x00, 0x00, 0xFC, 0x04, 0xF4, 0x14, 0x14, 0x17, 0x10, 0x17, 0x14, 0x14, 0xF4, 0x04, 0xF4, 0x00, 0x00, 0xFF, 0x00, 0xF7, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0xF7, 0x00, 0xF7, 0x14, 0x14, 0x14, 0x17, 0x14, 0x10, 0x10, 0x1F, 0x10, 0x1F, 0x14, 0x14, 0x14, 0xF4, 0x14, 0x10, 0x10, 0xF0, 0x10, 0xF0, 0x00, 0x00, 0x1F, 0x10, 0x1F, 0x00, 0x00, 0x00, 0x1F, 0x14, 0x00, 0x00, 0x00, 0xFC, 0x14, 0x00, 0x00, 0xF0, 0x10, 0xF0, 0x10, 0x10, 0xFF, 0x10, 0xFF, 0x14, 0x14, 0x14, 0xFF, 0x14, 0x10, 0x10, 0x10, 0x1F, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x38, 0x44, 0x44, 0x38, 0x44, 0xFC, 0x4A, 0x4A, 0x4A, 0x34, // sharp-s or beta 0x7E, 0x02, 0x02, 0x06, 0x06, 0x02, 0x7E, 0x02, 0x7E, 0x02, 0x63, 0x55, 0x49, 0x41, 0x63, 0x38, 0x44, 0x44, 0x3C, 0x04, 0x40, 0x7E, 0x20, 0x1E, 0x20, 0x06, 0x02, 0x7E, 0x02, 0x02, 0x99, 0xA5, 0xE7, 0xA5, 0x99, 0x1C, 0x2A, 0x49, 0x2A, 0x1C, 0x4C, 0x72, 0x01, 0x72, 0x4C, 0x30, 0x4A, 0x4D, 0x4D, 0x30, 0x30, 0x48, 0x78, 0x48, 0x30, 0xBC, 0x62, 0x5A, 0x46, 0x3D, 0x3E, 0x49, 0x49, 0x49, 0x00, 0x7E, 0x01, 0x01, 0x01, 0x7E, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x44, 0x44, 0x5F, 0x44, 0x44, 0x40, 0x51, 0x4A, 0x44, 0x40, 0x40, 0x44, 0x4A, 0x51, 0x40, 0x00, 0x00, 0xFF, 0x01, 0x03, 0xE0, 0x80, 0xFF, 0x00, 0x00, 0x08, 0x08, 0x6B, 0x6B, 0x08, 0x36, 0x12, 0x36, 0x24, 0x36, 0x06, 0x0F, 0x09, 0x0F, 0x06, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x10, 0x10, 0x00, 0x30, 0x40, 0xFF, 0x01, 0x01, 0x00, 0x1F, 0x01, 0x01, 0x1E, 0x00, 0x19, 0x1D, 0x17, 0x12, 0x00, 0x3C, 0x3C, 0x3C, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00 // #255 NBSP }; // Pass 8-bit (each) R,G,B, get back 16-bit packed color uint16_t ILI9341::color565(uint8_t r, uint8_t g, uint8_t b) { return ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | ((b & 0xF8) >> 3); } ILI9341::ILI9341(int8_t cs, int8_t dc, int8_t rst) : WIDTH(ILI9341_TFTWIDTH), HEIGHT(ILI9341_TFTHEIGHT) { _width = WIDTH; _height = HEIGHT; rotation = 0; cursor_y = cursor_x = 0; textsize = 1; textcolor = textbgcolor = 0xFFFF; wrap = true; _cp437 = false; gfxFont = NULL; // _cs = cs; _dc = dc; _rst = rst; _sclk = -1; _mosi = -1; _miso = -1; _freq = 0; #ifdef USE_FAST_PINIO csport = portOutputRegister(digitalPinToPort(_cs)); cspinmask = digitalPinToBitMask(_cs); dcport = portOutputRegister(digitalPinToPort(_dc)); dcpinmask = digitalPinToBitMask(_dc); clkport = 0; clkpinmask = 0; mosiport = 0; mosipinmask = 0; misoport = 0; misopinmask = 0; #endif } #ifdef ESP32 void ILI9341::begin(uint32_t freq, SPIClass &spi) #else void ILI9341::begin(uint32_t freq) #endif { #ifdef ESP32 _spi = spi; #endif if(!freq){ freq = SPI_DEFAULT_FREQ; } _freq = freq; // LED Control setBrightness(0); // Control Pins pinMode(_dc, OUTPUT); digitalWrite(_dc, LOW); pinMode(_cs, OUTPUT); digitalWrite(_cs, HIGH); // Software SPI if(_sclk >= 0){ pinMode(_mosi, OUTPUT); digitalWrite(_mosi, LOW); pinMode(_sclk, OUTPUT); digitalWrite(_sclk, HIGH); if(_miso >= 0){ pinMode(_miso, INPUT); } } // Hardware SPI SPI_BEGIN(); // toggle RST low to reset if (_rst >= 0) { pinMode(_rst, OUTPUT); digitalWrite(_rst, HIGH); delay(100); digitalWrite(_rst, LOW); delay(100); digitalWrite(_rst, HIGH); delay(200); } startWrite(); writeCommand(0xEF); spiWrite(0x03); spiWrite(0x80); spiWrite(0x02); writeCommand(0xCF); spiWrite(0x00); spiWrite(0XC1); spiWrite(0X30); writeCommand(0xED); spiWrite(0x64); spiWrite(0x03); spiWrite(0X12); spiWrite(0X81); writeCommand(0xE8); spiWrite(0x85); spiWrite(0x00); spiWrite(0x78); writeCommand(0xCB); spiWrite(0x39); spiWrite(0x2C); spiWrite(0x00); spiWrite(0x34); spiWrite(0x02); writeCommand(0xF7); spiWrite(0x20); writeCommand(0xEA); spiWrite(0x00); spiWrite(0x00); writeCommand(ILI9341_PWCTR1); //Power control spiWrite(0x23); //VRH[5:0] writeCommand(ILI9341_PWCTR2); //Power control spiWrite(0x10); //SAP[2:0];BT[3:0] writeCommand(ILI9341_VMCTR1); //VCM control spiWrite(0x3e); spiWrite(0x28); writeCommand(ILI9341_VMCTR2); //VCM control2 spiWrite(0x86); //-- writeCommand(ILI9341_MADCTL); // Memory Access Control // spiWrite(0x48); spiWrite(0x48); writeCommand(ILI9341_VSCRSADD); // Vertical scroll SPI_WRITE16(0); // Zero writeCommand(ILI9341_PIXFMT); spiWrite(0x55); writeCommand(ILI9341_FRMCTR1); spiWrite(0x00); spiWrite(0x18); writeCommand(ILI9341_DFUNCTR); // Display Function Control spiWrite(0x08); spiWrite(0x82); spiWrite(0x27); writeCommand(0xF2); // 3Gamma Function Disable spiWrite(0x00); writeCommand(ILI9341_GAMMASET); //Gamma curve selected spiWrite(0x01); writeCommand(ILI9341_GMCTRP1); //Set Gamma spiWrite(0x0F); spiWrite(0x31); spiWrite(0x2B); spiWrite(0x0C); spiWrite(0x0E); spiWrite(0x08); spiWrite(0x4E); spiWrite(0xF1); spiWrite(0x37); spiWrite(0x07); spiWrite(0x10); spiWrite(0x03); spiWrite(0x0E); spiWrite(0x09); spiWrite(0x00); writeCommand(ILI9341_GMCTRN1); //Set Gamma spiWrite(0x00); spiWrite(0x0E); spiWrite(0x14); spiWrite(0x03); spiWrite(0x11); spiWrite(0x07); spiWrite(0x31); spiWrite(0xC1); spiWrite(0x48); spiWrite(0x08); spiWrite(0x0F); spiWrite(0x0C); spiWrite(0x31); spiWrite(0x36); spiWrite(0x0F); writeCommand(ILI9341_SLPOUT); //Exit Sleep delay(120); writeCommand(ILI9341_DISPON); //Display on delay(120); endWrite(); _width = ILI9341_TFTWIDTH; _height = ILI9341_TFTHEIGHT; setRotation(0); } void ILI9341::setRotation(uint8_t m) { rotation = m % 4; // can't be higher than 3 switch (rotation) { case 0: // m = (MADCTL_MX | MADCTL_BGR); m = ( MADCTL_BGR); _width = ILI9341_TFTWIDTH; _height = ILI9341_TFTHEIGHT; break; case 1: m = (MADCTL_MX | MADCTL_MV | MADCTL_BGR); _width = ILI9341_TFTHEIGHT; _height = ILI9341_TFTWIDTH; break; case 2: m = (MADCTL_MX | MADCTL_MY | MADCTL_BGR); _width = ILI9341_TFTWIDTH; _height = ILI9341_TFTHEIGHT; break; case 3: // m = (MADCTL_MX | MADCTL_MY | MADCTL_MV | MADCTL_BGR); m = (MADCTL_MY | MADCTL_MV | MADCTL_BGR); _width = ILI9341_TFTHEIGHT; _height = ILI9341_TFTWIDTH; break; } startWrite(); writeCommand(ILI9341_MADCTL); spiWrite(m); endWrite(); } void ILI9341::invertDisplay(boolean i) { startWrite(); writeCommand(i ? ILI9341_INVON : ILI9341_INVOFF); endWrite(); } void ILI9341::scrollTo(uint16_t y) { startWrite(); writeCommand(ILI9341_VSCRSADD); SPI_WRITE16(y); endWrite(); } uint8_t ILI9341::spiRead() { if(_sclk < 0){ return HSPI_READ(); } if(_miso < 0){ return 0; } uint8_t r = 0; for (uint8_t i=0; i<8; i++) { SSPI_SCK_LOW(); SSPI_SCK_HIGH(); r <<= 1; if (SSPI_MISO_READ()){ r |= 0x1; } } return r; } void ILI9341::spiWrite(uint8_t b) { if(_sclk < 0){ HSPI_WRITE(b); return; } for(uint8_t bit = 0x80; bit; bit >>= 1){ if((b) & bit){ SSPI_MOSI_HIGH(); } else { SSPI_MOSI_LOW(); } SSPI_SCK_LOW(); SSPI_SCK_HIGH(); } } /* * Transaction API * */ void ILI9341::startWrite(void){ SPI_BEGIN_TRANSACTION(); SPI_CS_LOW(); } void ILI9341::endWrite(void){ SPI_CS_HIGH(); SPI_END_TRANSACTION(); } void ILI9341::writeCommand(uint8_t cmd){ SPI_DC_LOW(); spiWrite(cmd); SPI_DC_HIGH(); } void ILI9341::setAddrWindow(uint16_t x, uint16_t y, uint16_t w, uint16_t h) { // x = ILI9341_TFTWIDTH - x; uint32_t xa = ((uint32_t)x << 16) | (x+w-1); uint32_t ya = ((uint32_t)y << 16) | (y+h-1); writeCommand(ILI9341_CASET); // Column addr set SPI_WRITE32(xa); writeCommand(ILI9341_PASET); // Row addr set SPI_WRITE32(ya); writeCommand(ILI9341_RAMWR); // write to RAM } void ILI9341::pushColor(uint16_t color) { startWrite(); SPI_WRITE16(color); endWrite(); } void ILI9341::writePixel(uint16_t color){ SPI_WRITE16(color); } void ILI9341::writePixels(uint16_t * colors, uint32_t len){ SPI_WRITE_PIXELS((uint8_t*)colors , len * 2); } void ILI9341::writeColor(uint16_t color, uint32_t len){ #ifdef SPI_HAS_WRITE_PIXELS if(_sclk >= 0){ for (uint32_t t=0; t SPI_MAX_PIXELS_AT_ONCE)?SPI_MAX_PIXELS_AT_ONCE:len; uint16_t tlen = 0; for (uint32_t t=0; tblen)?blen:len; writePixels(temp, tlen); len -= tlen; } #else uint8_t hi = color >> 8, lo = color; if(_sclk < 0){ //AVR Optimization for (uint32_t t=len; t; t--){ HSPI_WRITE(hi); HSPI_WRITE(lo); } return; } for (uint32_t t=len; t; t--){ spiWrite(hi); spiWrite(lo); } #endif } void ILI9341::writePixel(int16_t x, int16_t y, uint16_t color) { if((x < 0) ||(x >= _width) || (y < 0) || (y >= _height)) return; setAddrWindow(x,y,1,1); writePixel(color); } void ILI9341::writeFillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color){ if((x >= _width) || (y >= _height)) return; int16_t x2 = x + w - 1, y2 = y + h - 1; if((x2 < 0) || (y2 < 0)) return; // Clip left/top if(x < 0) { x = 0; w = x2 + 1; } if(y < 0) { y = 0; h = y2 + 1; } // Clip right/bottom if(x2 >= _width) w = _width - x; if(y2 >= _height) h = _height - y; int32_t len = (int32_t)w * h; setAddrWindow(x, y, w, h); writeColor(color, len); } uint8_t ILI9341::readcommand8(uint8_t c, uint8_t index) { uint32_t freq = _freq; if(_freq > 24000000){ _freq = 24000000; } startWrite(); writeCommand(0xD9); // woo sekret command? spiWrite(0x10 + index); writeCommand(c); uint8_t r = spiRead(); endWrite(); _freq = freq; return r; } void ILI9341::drawPixel(int16_t x, int16_t y, uint16_t color){ startWrite(); writePixel(x, y, color); endWrite(); } void ILI9341::drawFastVLine( int16_t x, int16_t y, int16_t h, uint16_t color) { startWrite(); writeFastVLine(x, y, h, color); endWrite(); } void ILI9341::drawFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color) { startWrite(); writeFastHLine(x, y, w, color); endWrite(); } void ILI9341::writeFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color){ writeFillRect(x, y, 1, h, color); } void ILI9341::writeFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color){ writeFillRect(x, y, w, 1, color); } void ILI9341::fillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color) { startWrite(); writeFillRect(x,y,w,h,color); endWrite(); } // This code was ported/adapted from https://github.com/PaulStoffregen/ILI9341_t3 // by Marc MERLIN. See examples/pictureEmbed to use this. void ILI9341::drawBitmap(int16_t x, int16_t y, int16_t w, int16_t h, const uint16_t *pcolors) { int16_t x2, y2; // Lower-right coord if(( x >= _width ) || // Off-edge right ( y >= _height) || // " top ((x2 = (x+w-1)) < 0 ) || // " left ((y2 = (y+h-1)) < 0) ) return; // " bottom int16_t bx1=0, by1=0, // Clipped top-left within bitmap saveW=w; // Save original bitmap width value if(x < 0) { // Clip left w += x; bx1 = -x; x = 0; } if(y < 0) { // Clip top h += y; by1 = -y; y = 0; } if(x2 >= _width ) w = _width - x; // Clip right if(y2 >= _height) h = _height - y; // Clip bottom pcolors += by1 * saveW + bx1; // Offset bitmap ptr to clipped top-left startWrite(); setAddrWindow(x, y, w, h); // Clipped area while(h--) { // For each (clipped) scanline... writePixels((uint16_t*)pcolors, w); // Push one (clipped) row pcolors += saveW; // Advance pointer by one full (unclipped) line } endWrite(); } void ILI9341::drawBitmap(int16_t x, int16_t y, int16_t w, int16_t h, const uint8_t *pcolors) { drawBitmap(x, y, w, h, (uint16_t*)pcolors); } void ILI9341::progressBar(int x, int y, int w, int h, uint8_t val) { drawRect(x, y, w, h, 0x09F1); fillRect(x+1, y+1, w*(((float)val)/100.0), h-1, 0x09F1); } void ILI9341::setBrightness(uint8_t brightness) { ledcSetup(2, 10000, 8); ledcAttachPin(TFT_LED_PIN, 2); ledcWrite(2, brightness); // pinMode(TFT_LED_PIN, OUPUT); // digitalWrite(TFT_LED_PIN, 1); } void ILI9341::putChar(int x, int y, char ch) { setCursor(x, y); print(ch); } void ILI9341::putStr(int x, int y, String str) { setCursor(x, y); print(str); } //----------------------------- // Bresenham's algorithm - thx wikpedia void ILI9341::writeLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1, uint16_t color) { int16_t steep = abs(y1 - y0) > abs(x1 - x0); if (steep) { _swap_int16_t(x0, y0); _swap_int16_t(x1, y1); } if (x0 > x1) { _swap_int16_t(x0, x1); _swap_int16_t(y0, y1); } int16_t dx, dy; dx = x1 - x0; dy = abs(y1 - y0); int16_t err = dx / 2; int16_t ystep; if (y0 < y1) { ystep = 1; } else { ystep = -1; } for (; x0<=x1; x0++) { if (steep) { writePixel(y0, x0, color); } else { writePixel(x0, y0, color); } err -= dy; if (err < 0) { y0 += ystep; err += dx; } } } void ILI9341::drawLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1, uint16_t color) { // Update in subclasses if desired! if(x0 == x1){ if(y0 > y1) _swap_int16_t(y0, y1); drawFastVLine(x0, y0, y1 - y0 + 1, color); } else if(y0 == y1){ if(x0 > x1) _swap_int16_t(x0, x1); drawFastHLine(x0, y0, x1 - x0 + 1, color); } else { startWrite(); writeLine(x0, y0, x1, y1, color); endWrite(); } } // Draw a circle outline void ILI9341::drawCircle(int16_t x0, int16_t y0, int16_t r, uint16_t color) { int16_t f = 1 - r; int16_t ddF_x = 1; int16_t ddF_y = -2 * r; int16_t x = 0; int16_t y = r; startWrite(); writePixel(x0 , y0+r, color); writePixel(x0 , y0-r, color); writePixel(x0+r, y0 , color); writePixel(x0-r, y0 , color); while (x= 0) { y--; ddF_y += 2; f += ddF_y; } x++; ddF_x += 2; f += ddF_x; writePixel(x0 + x, y0 + y, color); writePixel(x0 - x, y0 + y, color); writePixel(x0 + x, y0 - y, color); writePixel(x0 - x, y0 - y, color); writePixel(x0 + y, y0 + x, color); writePixel(x0 - y, y0 + x, color); writePixel(x0 + y, y0 - x, color); writePixel(x0 - y, y0 - x, color); } endWrite(); } void ILI9341::drawCircleHelper( int16_t x0, int16_t y0, int16_t r, uint8_t cornername, uint16_t color) { int16_t f = 1 - r; int16_t ddF_x = 1; int16_t ddF_y = -2 * r; int16_t x = 0; int16_t y = r; while (x= 0) { y--; ddF_y += 2; f += ddF_y; } x++; ddF_x += 2; f += ddF_x; if (cornername & 0x4) { writePixel(x0 + x, y0 + y, color); writePixel(x0 + y, y0 + x, color); } if (cornername & 0x2) { writePixel(x0 + x, y0 - y, color); writePixel(x0 + y, y0 - x, color); } if (cornername & 0x8) { writePixel(x0 - y, y0 + x, color); writePixel(x0 - x, y0 + y, color); } if (cornername & 0x1) { writePixel(x0 - y, y0 - x, color); writePixel(x0 - x, y0 - y, color); } } } void ILI9341::fillCircle(int16_t x0, int16_t y0, int16_t r, uint16_t color) { startWrite(); writeFastVLine(x0, y0-r, 2*r+1, color); fillCircleHelper(x0, y0, r, 3, 0, color); endWrite(); } // Used to do circles and roundrects void ILI9341::fillCircleHelper(int16_t x0, int16_t y0, int16_t r, uint8_t cornername, int16_t delta, uint16_t color) { int16_t f = 1 - r; int16_t ddF_x = 1; int16_t ddF_y = -2 * r; int16_t x = 0; int16_t y = r; while (x= 0) { y--; ddF_y += 2; f += ddF_y; } x++; ddF_x += 2; f += ddF_x; if (cornername & 0x1) { writeFastVLine(x0+x, y0-y, 2*y+1+delta, color); writeFastVLine(x0+y, y0-x, 2*x+1+delta, color); } if (cornername & 0x2) { writeFastVLine(x0-x, y0-y, 2*y+1+delta, color); writeFastVLine(x0-y, y0-x, 2*x+1+delta, color); } } } // Draw a rectangle void ILI9341::drawRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color) { startWrite(); writeFastHLine(x, y, w, color); writeFastHLine(x, y+h-1, w, color); writeFastVLine(x, y, h, color); writeFastVLine(x+w-1, y, h, color); endWrite(); } // Draw a rounded rectangle void ILI9341::drawRoundRect(int16_t x, int16_t y, int16_t w, int16_t h, int16_t r, uint16_t color) { // smarter version startWrite(); writeFastHLine(x+r , y , w-2*r, color); // Top writeFastHLine(x+r , y+h-1, w-2*r, color); // Bottom writeFastVLine(x , y+r , h-2*r, color); // Left writeFastVLine(x+w-1, y+r , h-2*r, color); // Right // draw four corners drawCircleHelper(x+r , y+r , r, 1, color); drawCircleHelper(x+w-r-1, y+r , r, 2, color); drawCircleHelper(x+w-r-1, y+h-r-1, r, 4, color); drawCircleHelper(x+r , y+h-r-1, r, 8, color); endWrite(); } // Fill a rounded rectangle void ILI9341::fillRoundRect(int16_t x, int16_t y, int16_t w, int16_t h, int16_t r, uint16_t color) { // smarter version startWrite(); writeFillRect(x+r, y, w-2*r, h, color); // draw four corners fillCircleHelper(x+w-r-1, y+r, r, 1, h-2*r-1, color); fillCircleHelper(x+r , y+r, r, 2, h-2*r-1, color); endWrite(); } // Draw a triangle void ILI9341::drawTriangle(int16_t x0, int16_t y0, int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint16_t color) { drawLine(x0, y0, x1, y1, color); drawLine(x1, y1, x2, y2, color); drawLine(x2, y2, x0, y0, color); } // Fill a triangle void ILI9341::fillTriangle(int16_t x0, int16_t y0, int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint16_t color) { int16_t a, b, y, last; // Sort coordinates by Y order (y2 >= y1 >= y0) if (y0 > y1) { _swap_int16_t(y0, y1); _swap_int16_t(x0, x1); } if (y1 > y2) { _swap_int16_t(y2, y1); _swap_int16_t(x2, x1); } if (y0 > y1) { _swap_int16_t(y0, y1); _swap_int16_t(x0, x1); } startWrite(); if(y0 == y2) { // Handle awkward all-on-same-line case as its own thing a = b = x0; if(x1 < a) a = x1; else if(x1 > b) b = x1; if(x2 < a) a = x2; else if(x2 > b) b = x2; writeFastHLine(a, y0, b-a+1, color); endWrite(); return; } int16_t dx01 = x1 - x0, dy01 = y1 - y0, dx02 = x2 - x0, dy02 = y2 - y0, dx12 = x2 - x1, dy12 = y2 - y1; int32_t sa = 0, sb = 0; // For upper part of triangle, find scanline crossings for segments // 0-1 and 0-2. If y1=y2 (flat-bottomed triangle), the scanline y1 // is included here (and second loop will be skipped, avoiding a /0 // error there), otherwise scanline y1 is skipped here and handled // in the second loop...which also avoids a /0 error here if y0=y1 // (flat-topped triangle). if(y1 == y2) last = y1; // Include y1 scanline else last = y1-1; // Skip it for(y=y0; y<=last; y++) { a = x0 + sa / dy01; b = x0 + sb / dy02; sa += dx01; sb += dx02; /* longhand: a = x0 + (x1 - x0) * (y - y0) / (y1 - y0); b = x0 + (x2 - x0) * (y - y0) / (y2 - y0); */ if(a > b) _swap_int16_t(a,b); writeFastHLine(a, y, b-a+1, color); } // For lower part of triangle, find scanline crossings for segments // 0-2 and 1-2. This loop is skipped if y1=y2. sa = dx12 * (y - y1); sb = dx02 * (y - y0); for(; y<=y2; y++) { a = x1 + sa / dy12; b = x0 + sb / dy02; sa += dx12; sb += dx02; /* longhand: a = x1 + (x2 - x1) * (y - y1) / (y2 - y1); b = x0 + (x2 - x0) * (y - y0) / (y2 - y0); */ if(a > b) _swap_int16_t(a,b); writeFastHLine(a, y, b-a+1, color); } endWrite(); } // BITMAP / XBITMAP / GRAYSCALE / RGB BITMAP FUNCTIONS --------------------- // Draw a PROGMEM-resident 1-bit image at the specified (x,y) position, // using the specified foreground color (unset bits are transparent). void ILI9341::drawBitmap(int16_t x, int16_t y, const uint8_t bitmap[], int16_t w, int16_t h, uint16_t color) { int16_t byteWidth = (w + 7) / 8; // Bitmap scanline pad = whole byte uint8_t byte = 0; startWrite(); for(int16_t j=0; j>= 1; else byte = pgm_read_byte(&bitmap[j * byteWidth + i / 8]); // Nearly identical to drawBitmap(), only the bit order // is reversed here (left-to-right = LSB to MSB): if(byte & 0x01) writePixel(x+i, y, color); } } endWrite(); } // Draw a PROGMEM-resident 8-bit image (grayscale) at the specified (x,y) // pos. Specifically for 8-bit display devices such as IS31FL3731; // no color reduction/expansion is performed. void ILI9341::drawGrayscaleBitmap(int16_t x, int16_t y, const uint8_t bitmap[], int16_t w, int16_t h) { startWrite(); for(int16_t j=0; j= _width) || // Clip right (y >= _height) || // Clip bottom ((x + 6 * size - 1) < 0) || // Clip left ((y + 8 * size - 1) < 0)) // Clip top return; if(!_cp437 && (c >= 176)) c++; // Handle 'classic' charset behavior startWrite(); for(int8_t i=0; i<5; i++ ) { // Char bitmap = 5 columns uint8_t line = pgm_read_byte(&font[c * 5 + i]); for(int8_t j=0; j<8; j++, line >>= 1) { if(line & 1) { if(size == 1) writePixel(x+i, y+j, color); else writeFillRect(x+i*size, y+j*size, size, size, color); } else if(bg != color) { if(size == 1) writePixel(x+i, y+j, bg); else writeFillRect(x+i*size, y+j*size, size, size, bg); } } } if(bg != color) { // If opaque, draw vertical line for last column if(size == 1) writeFastVLine(x+5, y, 8, bg); else writeFillRect(x+5*size, y, size, 8*size, bg); } endWrite(); } else { // Custom font // 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! c -= (uint8_t)pgm_read_byte(&gfxFont->first); GFXglyph *glyph = &(((GFXglyph *)pgm_read_pointer(&gfxFont->glyph))[c]); uint8_t *bitmap = (uint8_t *)pgm_read_pointer(&gfxFont->bitmap); uint16_t bo = pgm_read_word(&glyph->bitmapOffset); uint8_t w = pgm_read_byte(&glyph->width), h = pgm_read_byte(&glyph->height); int8_t xo = pgm_read_byte(&glyph->xOffset), yo = pgm_read_byte(&glyph->yOffset); uint8_t xx, yy, bits = 0, bit = 0; int16_t xo16 = 0, yo16 = 0; if(size > 1) { xo16 = xo; yo16 = yo; } // Todo: Add character clipping here // NOTE: THERE IS NO 'BACKGROUND' COLOR OPTION ON CUSTOM FONTS. // THIS IS ON PURPOSE AND BY DESIGN. The background color feature // has typically been used with the 'classic' font to overwrite old // screen contents with new data. This ONLY works because the // characters are a uniform size; it's not a sensible thing to do with // proportionally-spaced fonts with glyphs of varying sizes (and that // may overlap). To replace previously-drawn text when using a custom // font, use the getTextBounds() function to determine the smallest // rectangle encompassing a string, erase the area with fillRect(), // then draw new text. This WILL infortunately 'blink' the text, but // is unavoidable. Drawing 'background' pixels will NOT fix this, // only creates a new set of problems. Have an idea to work around // this (a canvas object type for MCUs that can afford the RAM and // displays supporting setAddrWindow() and pushColors()), but haven't // implemented this yet. startWrite(); for(yy=0; yy= 100 size_t ILI9341::write(uint8_t c) { #else void ILI9341::write(uint8_t c) { #endif if(!gfxFont) { // 'Classic' built-in font if(c == '\n') { // Newline? cursor_x = 0; // Reset x to zero, cursor_y += textsize * 8; // advance y one line } else if(c != '\r') { // Ignore carriage returns if(wrap && ((cursor_x + textsize * 6) > _width)) { // Off right? cursor_x = 0; // Reset x to zero, cursor_y += textsize * 8; // advance y one line } drawChar(cursor_x, cursor_y, c, textcolor, textbgcolor, textsize); cursor_x += textsize * 6; // Advance x one char } } else { // Custom font if(c == '\n') { cursor_x = 0; cursor_y += (int16_t)textsize * (uint8_t)pgm_read_byte(&gfxFont->yAdvance); } else if(c != '\r') { uint8_t first = pgm_read_byte(&gfxFont->first); if((c >= first) && (c <= (uint8_t)pgm_read_byte(&gfxFont->last))) { GFXglyph *glyph = &(((GFXglyph *)pgm_read_pointer( &gfxFont->glyph))[c - first]); 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); // sic if(wrap && ((cursor_x + textsize * (xo + w)) > _width)) { cursor_x = 0; cursor_y += (int16_t)textsize * (uint8_t)pgm_read_byte(&gfxFont->yAdvance); } drawChar(cursor_x, cursor_y, c, textcolor, textbgcolor, textsize); } cursor_x += (uint8_t)pgm_read_byte(&glyph->xAdvance) * (int16_t)textsize; } } } #if ARDUINO >= 100 return 1; #endif } void ILI9341::setCursor(int16_t x, int16_t y) { cursor_x = x; cursor_y = y; } int16_t ILI9341::getCursorX(void) const { return cursor_x; } int16_t ILI9341::getCursorY(void) const { return cursor_y; } void ILI9341::setTextSize(uint8_t s) { textsize = (s > 0) ? s : 1; } void ILI9341::setTextColor(uint16_t c) { // For 'transparent' background, we'll set the bg // to the same as fg instead of using a flag textcolor = textbgcolor = c; } void ILI9341::setTextColor(uint16_t c, uint16_t b) { textcolor = c; textbgcolor = b; } void ILI9341::setTextWrap(boolean w) { wrap = w; } uint8_t ILI9341::getRotation(void) const { return rotation; } void ILI9341::fillScreen(uint16_t color) { // Update in subclasses if desired! fillRect(0, 0, _width, _height, color); } // void ILI9341::setRotation(uint8_t x) { // rotation = (x & 3); // switch(rotation) { // case 0: // case 2: // _width = WIDTH; // _height = HEIGHT; // break; // case 1: // case 3: // _width = HEIGHT; // _height = WIDTH; // break; // } // } // Enable (or disable) Code Page 437-compatible charset. // There was an error in glcdfont.c for the longest time -- one character // (#176, the 'light shade' block) was missing -- this threw off the index // of every character that followed it. But a TON of code has been written // with the erroneous character indices. By default, the library uses the // original 'wrong' behavior and old sketches will still work. Pass 'true' // to this function to use correct CP437 character values in your code. void ILI9341::cp437(boolean x) { _cp437 = x; } void ILI9341::setFont(const GFXfont *f) { if(f) { // Font struct pointer passed in? if(!gfxFont) { // And no current font struct? // Switching from classic to new font behavior. // Move cursor pos down 6 pixels so it's on baseline. cursor_y += 6; } } else if(gfxFont) { // NULL passed. Current font struct defined? // Switching from new to classic font behavior. // Move cursor pos up 6 pixels so it's at top-left of char. cursor_y -= 6; } gfxFont = (GFXfont *)f; } // Broke this out as it's used by both the PROGMEM- and RAM-resident // getTextBounds() functions. void ILI9341::charBounds(char c, int16_t *x, int16_t *y, int16_t *minx, int16_t *miny, int16_t *maxx, int16_t *maxy) { if(gfxFont) { if(c == '\n') { // Newline? *x = 0; // Reset x to zero, advance y by one line *y += textsize * (uint8_t)pgm_read_byte(&gfxFont->yAdvance); } else if(c != '\r') { // Not a carriage return; is normal char uint8_t first = pgm_read_byte(&gfxFont->first), last = pgm_read_byte(&gfxFont->last); if((c >= first) && (c <= last)) { // Char present in this font? GFXglyph *glyph = &(((GFXglyph *)pgm_read_pointer( &gfxFont->glyph))[c - first]); uint8_t gw = pgm_read_byte(&glyph->width), gh = 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); if(wrap && ((*x+(((int16_t)xo+gw)*textsize)) > _width)) { *x = 0; // Reset x to zero, advance y by one line *y += textsize * (uint8_t)pgm_read_byte(&gfxFont->yAdvance); } int16_t ts = (int16_t)textsize, x1 = *x + xo * ts, y1 = *y + yo * ts, x2 = x1 + gw * ts - 1, y2 = y1 + gh * ts - 1; if(x1 < *minx) *minx = x1; if(y1 < *miny) *miny = y1; if(x2 > *maxx) *maxx = x2; if(y2 > *maxy) *maxy = y2; *x += xa * ts; } } } else { // Default font if(c == '\n') { // Newline? *x = 0; // Reset x to zero, *y += textsize * 8; // advance y one line // min/max x/y unchaged -- that waits for next 'normal' character } else if(c != '\r') { // Normal char; ignore carriage returns if(wrap && ((*x + textsize * 6) > _width)) { // Off right? *x = 0; // Reset x to zero, *y += textsize * 8; // advance y one line } int x2 = *x + textsize * 6 - 1, // Lower-right pixel of char y2 = *y + textsize * 8 - 1; if(x2 > *maxx) *maxx = x2; // Track max x, y if(y2 > *maxy) *maxy = y2; if(*x < *minx) *minx = *x; // Track min x, y if(*y < *miny) *miny = *y; *x += textsize * 6; // Advance x one char } } } // Pass string and a cursor position, returns UL corner and W,H. void ILI9341::getTextBounds(char *str, int16_t x, int16_t y, int16_t *x1, int16_t *y1, uint16_t *w, uint16_t *h) { uint8_t c; // Current character *x1 = x; *y1 = y; *w = *h = 0; int16_t minx = _width, miny = _height, maxx = -1, maxy = -1; while((c = *str++)) charBounds(c, &x, &y, &minx, &miny, &maxx, &maxy); if(maxx >= minx) { *x1 = minx; *w = maxx - minx + 1; } if(maxy >= miny) { *y1 = miny; *h = maxy - miny + 1; } } // Same as above, but for PROGMEM strings void ILI9341::getTextBounds(const __FlashStringHelper *str, int16_t x, int16_t y, int16_t *x1, int16_t *y1, uint16_t *w, uint16_t *h) { uint8_t *s = (uint8_t *)str, c; *x1 = x; *y1 = y; *w = *h = 0; int16_t minx = _width, miny = _height, maxx = -1, maxy = -1; while((c = pgm_read_byte(s++))) charBounds(c, &x, &y, &minx, &miny, &maxx, &maxy); if(maxx >= minx) { *x1 = minx; *w = maxx - minx + 1; } if(maxy >= miny) { *y1 = miny; *h = maxy - miny + 1; } } // Return the size of the display (per current rotation) int16_t ILI9341::width(void) const { return _width; } int16_t ILI9341::height(void) const { return _height; }