// SPDX-License-Identifier: GPL-3.0 // original source: https://github.com/Nkawu/TFT22_ILI9225 #include "TFT22_ILI9225.h" //#define DEBUG #ifdef DEBUG //#define DB_PRINT( x, ... ) { char dbgbuf[60]; sprintf_P( dbgbuf, (const char*) F( x ), __VA_ARGS__ ) ; Serial.print( dbgbuf ); } #define DB_PRINT( ... ) { char dbgbuf[60]; sprintf( dbgbuf, __VA_ARGS__ ) ; Serial.println( dbgbuf ); } #else #define DB_PRINT( ... ) ; #endif #ifndef ARDUINO_STM32_FEATHER #include "pins_arduino.h" #ifndef RASPI #include "wiring_private.h" #endif #endif #include #ifdef __AVR__ #include #elif defined(ESP8266) || defined(ESP32) #include #endif // 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 // 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(_rs, HIGH) #define SPI_DC_LOW() digitalWrite(_rs, 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 #else #define SSPI_MOSI_HIGH() digitalWrite(_sdi, HIGH) #define SSPI_MOSI_LOW() digitalWrite(_sdi, LOW) #define SSPI_SCK_HIGH() digitalWrite(_clk, HIGH) #define SSPI_SCK_LOW() digitalWrite(_clk, LOW) #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 #ifndef ESP32 #ifdef SPI_CHANNEL extern SPIClass SPI_CHANNEL; #define SPI_OBJECT SPI_CHANNEL #else #define SPI_OBJECT SPI #endif #else #define SPI_OBJECT _spi #endif #if defined (__AVR__) || defined(TEENSYDUINO) || defined(ARDUINO_ARCH_STM32F1) #define HSPI_SET_CLOCK() SPI_OBJECT.setClockDivider(SPI_CLOCK_DIV2); #elif defined (__arm__) #define HSPI_SET_CLOCK() SPI_OBJECT.setClockDivider(11); #elif defined(ESP8266) || defined(ESP32) #define HSPI_SET_CLOCK() SPI_OBJECT.setFrequency(SPI_DEFAULT_FREQ); #elif defined(RASPI) #define HSPI_SET_CLOCK() SPI_OBJECT.setClock(SPI_DEFAULT_FREQ); #elif defined(ARDUINO_ARCH_STM32F1) #define HSPI_SET_CLOCK() SPI_OBJECT.setClock(SPI_DEFAULT_FREQ); #else #define HSPI_SET_CLOCK() #endif #ifdef SPI_HAS_TRANSACTION #define HSPI_BEGIN_TRANSACTION() SPI_OBJECT.beginTransaction(SPISettings(SPI_DEFAULT_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 #elif defined ( __STM32F1__ ) #define HSPI_WRITE(b) SPI_OBJECT.write(b) #define HSPI_WRITE16(s) SPI_OBJECT.write16(s) #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 #if defined (ARDUINO_ARCH_ARC32) #define SPI_DEFAULT_FREQ 16000000 #elif defined (__AVR__) || defined(TEENSYDUINO) #define SPI_DEFAULT_FREQ 8000000 #elif defined(ESP8266) || defined(ESP32) #define SPI_DEFAULT_FREQ 40000000 #elif defined(RASPI) #define SPI_DEFAULT_FREQ 80000000 #elif defined(ARDUINO_ARCH_STM32F1) #define SPI_DEFAULT_FREQ 18000000 //#define SPI_DEFAULT_FREQ 36000000 #else #define SPI_DEFAULT_FREQ 24000000 #endif #define SPI_BEGIN() if(_clk < 0){SPI_OBJECT.begin();} #define SPI_BEGIN_TRANSACTION() if(_clk < 0){HSPI_BEGIN_TRANSACTION();} #define SPI_END_TRANSACTION() if(_clk < 0){HSPI_END_TRANSACTION();} // #define SPI_WRITE16(s) if(_clk < 0){HSPI_WRITE16(s);}else{SSPI_WRITE16(s);} // #define SPI_WRITE32(l) if(_clk < 0){HSPI_WRITE32(l);}else{SSPI_WRITE32(l);} // #define SPI_WRITE_PIXELS(c,l) if(_clk < 0){HSPI_WRITE_PIXELS(c,l);}else{SSPI_WRITE_PIXELS(c,l);} // Constructor when using software SPI. All output pins are configurable. TFT22_ILI9225::TFT22_ILI9225(int8_t rst, int8_t rs, int8_t cs, int8_t sdi, int8_t clk, int8_t led) { _rst = rst; _rs = rs; _cs = cs; _sdi = sdi; _clk = clk; _led = led; _brightness = 255; // Set to maximum brightness hwSPI = false; writeFunctionLevel = 0; gfxFont = NULL; } // Constructor when using software SPI. All output pins are configurable. Adds backlight brightness 0-255 TFT22_ILI9225::TFT22_ILI9225(int8_t rst, int8_t rs, int8_t cs, int8_t sdi, int8_t clk, int8_t led, uint8_t brightness) { _rst = rst; _rs = rs; _cs = cs; _sdi = sdi; _clk = clk; _led = led; _brightness = brightness; hwSPI = false; writeFunctionLevel = 0; gfxFont = NULL; } // Constructor when using hardware SPI. Faster, but must use SPI pins // specific to each board type (e.g. 11,13 for Uno, 51,52 for Mega, etc.) TFT22_ILI9225::TFT22_ILI9225(int8_t rst, int8_t rs, int8_t cs, int8_t led) { _rst = rst; _rs = rs; _cs = cs; _sdi = _clk = -1; _led = led; _brightness = 255; // Set to maximum brightness hwSPI = true; writeFunctionLevel = 0; gfxFont = NULL; } // Constructor when using hardware SPI. Faster, but must use SPI pins // specific to each board type (e.g. 11,13 for Uno, 51,52 for Mega, etc.) // Adds backlight brightness 0-255 TFT22_ILI9225::TFT22_ILI9225(int8_t rst, int8_t rs, int8_t cs, int8_t led, uint8_t brightness) { _rst = rst; _rs = rs; _cs = cs; _sdi = _clk = -1; _led = led; _brightness = brightness; hwSPI = true; writeFunctionLevel = 0; gfxFont = NULL; } #ifdef ESP32 void TFT22_ILI9225::begin(SPIClass &spi) #else void TFT22_ILI9225::begin() #endif { #ifdef ESP32 _spi = spi; #endif // Set up reset pin if (_rst > 0) { pinMode(_rst, OUTPUT); digitalWrite(_rst, LOW); } // Set up backlight pin, turn off initially if (_led > 0) { pinMode(_led, OUTPUT); setBacklight(false); } // Control pins pinMode(_rs, OUTPUT); digitalWrite(_rs, LOW); pinMode(_cs, OUTPUT); digitalWrite(_cs, HIGH); #ifdef USE_FAST_PINIO csport = portOutputRegister(digitalPinToPort(_cs)); cspinmask = digitalPinToBitMask(_cs); dcport = portOutputRegister(digitalPinToPort(_rs)); dcpinmask = digitalPinToBitMask(_rs); #endif // Software SPI if(_clk >= 0){ pinMode(_sdi, OUTPUT); digitalWrite(_sdi, LOW); pinMode(_clk, OUTPUT); digitalWrite(_clk, HIGH); #ifdef USE_FAST_PINIO clkport = portOutputRegister(digitalPinToPort(_clk)); clkpinmask = digitalPinToBitMask(_clk); mosiport = portOutputRegister(digitalPinToPort(_sdi)); mosipinmask = digitalPinToBitMask(_sdi); SSPI_SCK_LOW(); SSPI_MOSI_LOW(); } else { clkport = 0; clkpinmask = 0; mosiport = 0; mosipinmask = 0; #endif } // Hardware SPI if(_clk < 0){ SPI_BEGIN(); } else { SPI_OBJECT.begin(_clk, -1, _sdi, _cs); _clk = -1; // force use of hardware SPI } // Initialization Code if (_rst > 0) { digitalWrite(_rst, HIGH); // Pull the reset pin high to release the ILI9225C from the reset status delay(1); digitalWrite(_rst, LOW); // Pull the reset pin low to reset ILI9225 delay(10); digitalWrite(_rst, HIGH); // Pull the reset pin high to release the ILI9225C from the reset status delay(50); } /* Start Initial Sequence */ /* Set SS bit and direction output from S528 to S1 */ startWrite(); _writeRegister(ILI9225_POWER_CTRL1, 0x0000); // Set SAP,DSTB,STB _writeRegister(ILI9225_POWER_CTRL2, 0x0000); // Set APON,PON,AON,VCI1EN,VC _writeRegister(ILI9225_POWER_CTRL3, 0x0000); // Set BT,DC1,DC2,DC3 _writeRegister(ILI9225_POWER_CTRL4, 0x0000); // Set GVDD _writeRegister(ILI9225_POWER_CTRL5, 0x0000); // Set VCOMH/VCOML voltage endWrite(); delay(40); // Power-on sequence startWrite(); _writeRegister(ILI9225_POWER_CTRL2, 0x0018); // Set APON,PON,AON,VCI1EN,VC _writeRegister(ILI9225_POWER_CTRL3, 0x6121); // Set BT,DC1,DC2,DC3 _writeRegister(ILI9225_POWER_CTRL4, 0x006F); // Set GVDD /*007F 0088 */ _writeRegister(ILI9225_POWER_CTRL5, 0x495F); // Set VCOMH/VCOML voltage _writeRegister(ILI9225_POWER_CTRL1, 0x0800); // Set SAP,DSTB,STB endWrite(); delay(10); startWrite(); _writeRegister(ILI9225_POWER_CTRL2, 0x103B); // Set APON,PON,AON,VCI1EN,VC endWrite(); delay(50); startWrite(); _writeRegister(ILI9225_DRIVER_OUTPUT_CTRL, 0x011C); // set the display line number and display direction _writeRegister(ILI9225_LCD_AC_DRIVING_CTRL, 0x0100); // set 1 line inversion _writeRegister(ILI9225_ENTRY_MODE, 0x1038); // set GRAM write direction and BGR=1. _writeRegister(ILI9225_DISP_CTRL1, 0x0000); // Display off _writeRegister(ILI9225_BLANK_PERIOD_CTRL1, 0x0808); // set the back porch and front porch _writeRegister(ILI9225_FRAME_CYCLE_CTRL, 0x1100); // set the clocks number per line _writeRegister(ILI9225_INTERFACE_CTRL, 0x0000); // CPU interface _writeRegister(ILI9225_OSC_CTRL, 0x0D01); // Set Osc /*0e01*/ _writeRegister(ILI9225_VCI_RECYCLING, 0x0020); // Set VCI recycling _writeRegister(ILI9225_RAM_ADDR_SET1, 0x0000); // RAM Address _writeRegister(ILI9225_RAM_ADDR_SET2, 0x0000); // RAM Address /* Set GRAM area */ _writeRegister(ILI9225_GATE_SCAN_CTRL, 0x0000); _writeRegister(ILI9225_VERTICAL_SCROLL_CTRL1, 0x00DB); _writeRegister(ILI9225_VERTICAL_SCROLL_CTRL2, 0x0000); _writeRegister(ILI9225_VERTICAL_SCROLL_CTRL3, 0x0000); _writeRegister(ILI9225_PARTIAL_DRIVING_POS1, 0x00DB); _writeRegister(ILI9225_PARTIAL_DRIVING_POS2, 0x0000); _writeRegister(ILI9225_HORIZONTAL_WINDOW_ADDR1, 0x00AF); _writeRegister(ILI9225_HORIZONTAL_WINDOW_ADDR2, 0x0000); _writeRegister(ILI9225_VERTICAL_WINDOW_ADDR1, 0x00DB); _writeRegister(ILI9225_VERTICAL_WINDOW_ADDR2, 0x0000); /* Set GAMMA curve */ _writeRegister(ILI9225_GAMMA_CTRL1, 0x0000); _writeRegister(ILI9225_GAMMA_CTRL2, 0x0808); _writeRegister(ILI9225_GAMMA_CTRL3, 0x080A); _writeRegister(ILI9225_GAMMA_CTRL4, 0x000A); _writeRegister(ILI9225_GAMMA_CTRL5, 0x0A08); _writeRegister(ILI9225_GAMMA_CTRL6, 0x0808); _writeRegister(ILI9225_GAMMA_CTRL7, 0x0000); _writeRegister(ILI9225_GAMMA_CTRL8, 0x0A00); _writeRegister(ILI9225_GAMMA_CTRL9, 0x0710); _writeRegister(ILI9225_GAMMA_CTRL10, 0x0710); _writeRegister(ILI9225_DISP_CTRL1, 0x0012); endWrite(); delay(50); startWrite(); _writeRegister(ILI9225_DISP_CTRL1, 0x1017); endWrite(); // Turn on backlight setBacklight(true); setOrientation(0); // Initialize variables setBackgroundColor( COLOR_BLACK ); clear(); } void TFT22_ILI9225::_spiWrite(uint8_t b) { if(_clk < 0){ HSPI_WRITE(b); return; } // Fast SPI bitbang swiped from LPD8806 library for(uint8_t bit = 0x80; bit; bit >>= 1){ if((b) & bit){ SSPI_MOSI_HIGH(); } else { SSPI_MOSI_LOW(); } SSPI_SCK_HIGH(); SSPI_SCK_LOW(); } } void TFT22_ILI9225::_spiWrite16(uint16_t s) { // Attempt to use HSPI_WRITE16 if available #ifdef HSPI_WRITE16 if(_clk < 0){ HSPI_WRITE16(s); return; } #endif // Fallback to SSPI_WRITE16 if HSPI_WRITE16 not available SSPI_WRITE16(s); } void TFT22_ILI9225::_spiWriteCommand(uint8_t c) { SPI_DC_LOW(); SPI_CS_LOW(); _spiWrite(c); SPI_CS_HIGH(); } void TFT22_ILI9225::_spiWriteData(uint8_t c) { SPI_DC_HIGH(); SPI_CS_LOW(); _spiWrite(c); SPI_CS_HIGH(); } void TFT22_ILI9225::_orientCoordinates(uint16_t &x1, uint16_t &y1) { switch (_orientation) { case 0: // ok break; case 1: // ok y1 = _maxY - y1 - 1; _swap(x1, y1); break; case 2: // ok x1 = _maxX - x1 - 1; y1 = _maxY - y1 - 1; break; case 3: // ok x1 = _maxX - x1 - 1; _swap(x1, y1); break; } } void TFT22_ILI9225::_setWindow(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1) { _setWindow( x0, y0, x1, y1, TopDown_L2R ); // default for drawing characters } void TFT22_ILI9225::_setWindow(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, autoIncMode_t mode) { DB_PRINT( "setWindows( x0=%d, y0=%d, x1=%d, y1=%d, mode=%d", x0,y0,x1,y1,mode ); // clip to TFT-Dimensions x0 = min( x0, (uint16_t) (_maxX-1) ); x1 = min( x1, (uint16_t) (_maxX-1) ); y0 = min( y0, (uint16_t) (_maxY-1) ); y1 = min( y1, (uint16_t) (_maxY-1) ); _orientCoordinates(x0, y0); _orientCoordinates(x1, y1); if (x1 0 ) mode = modeTab[_orientation-1][mode]; _writeRegister(ILI9225_ENTRY_MODE, 0x1000 | ( mode<<3) ); _writeRegister(ILI9225_HORIZONTAL_WINDOW_ADDR1,x1); _writeRegister(ILI9225_HORIZONTAL_WINDOW_ADDR2,x0); _writeRegister(ILI9225_VERTICAL_WINDOW_ADDR1,y1); _writeRegister(ILI9225_VERTICAL_WINDOW_ADDR2,y0); DB_PRINT( "gedreht: x0=%d, y0=%d, x1=%d, y1=%d, mode=%d", x0,y0,x1,y1,mode ); // starting position within window and increment/decrement direction switch ( mode>>1 ) { case 0: _writeRegister(ILI9225_RAM_ADDR_SET1,x1); _writeRegister(ILI9225_RAM_ADDR_SET2,y1); break; case 1: _writeRegister(ILI9225_RAM_ADDR_SET1,x0); _writeRegister(ILI9225_RAM_ADDR_SET2,y1); break; case 2: _writeRegister(ILI9225_RAM_ADDR_SET1,x1); _writeRegister(ILI9225_RAM_ADDR_SET2,y0); break; case 3: _writeRegister(ILI9225_RAM_ADDR_SET1,x0); _writeRegister(ILI9225_RAM_ADDR_SET2,y0); break; } _writeCommand16( ILI9225_GRAM_DATA_REG ); //_writeRegister(ILI9225_RAM_ADDR_SET1,x0); //_writeRegister(ILI9225_RAM_ADDR_SET2,y0); //_writeCommand(0x00, 0x22); endWrite(); } void TFT22_ILI9225::_resetWindow() { _writeRegister(ILI9225_HORIZONTAL_WINDOW_ADDR1, 0x00AF); _writeRegister(ILI9225_HORIZONTAL_WINDOW_ADDR2, 0x0000); _writeRegister(ILI9225_VERTICAL_WINDOW_ADDR1, 0x00DB); _writeRegister(ILI9225_VERTICAL_WINDOW_ADDR2, 0x0000); } void TFT22_ILI9225::clear() { uint8_t old = _orientation; setOrientation(0); fillRectangle(0, 0, _maxX - 1, _maxY - 1, COLOR_BLACK); setOrientation(old); delay(10); } void TFT22_ILI9225::invert(boolean flag) { startWrite(); _writeCommand16(flag ? ILI9225C_INVON : ILI9225C_INVOFF); //_writeCommand(0x00, flag ? ILI9225C_INVON : ILI9225C_INVOFF); endWrite(); } void TFT22_ILI9225::setBacklight(boolean flag) { blState = flag; #ifndef ESP32 if (_led) analogWrite(_led, blState ? _brightness : 0); #endif } void TFT22_ILI9225::setBacklightBrightness(uint8_t brightness) { _brightness = brightness; setBacklight(blState); } void TFT22_ILI9225::setDisplay(boolean flag) { if (flag) { startWrite(); _writeRegister(0x00ff, 0x0000); _writeRegister(ILI9225_POWER_CTRL1, 0x0000); endWrite(); delay(50); startWrite(); _writeRegister(ILI9225_DISP_CTRL1, 0x1017); endWrite(); delay(200); } else { startWrite(); _writeRegister(0x00ff, 0x0000); _writeRegister(ILI9225_DISP_CTRL1, 0x0000); endWrite(); delay(50); startWrite(); _writeRegister(ILI9225_POWER_CTRL1, 0x0003); endWrite(); delay(200); } } void TFT22_ILI9225::setOrientation(uint8_t orientation) { _orientation = orientation % 4; switch (_orientation) { case 0: _maxX = ILI9225_LCD_WIDTH; _maxY = ILI9225_LCD_HEIGHT; break; case 1: _maxX = ILI9225_LCD_HEIGHT; _maxY = ILI9225_LCD_WIDTH; break; case 2: _maxX = ILI9225_LCD_WIDTH; _maxY = ILI9225_LCD_HEIGHT; break; case 3: _maxX = ILI9225_LCD_HEIGHT; _maxY = ILI9225_LCD_WIDTH; break; } } uint8_t TFT22_ILI9225::getOrientation() { return _orientation; } void TFT22_ILI9225::drawRectangle(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t color) { startWrite(); drawLine(x1, y1, x1, y2, color); drawLine(x1, y1, x2, y1, color); drawLine(x1, y2, x2, y2, color); drawLine(x2, y1, x2, y2, color); endWrite(); } void TFT22_ILI9225::fillRectangle(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t color) { _setWindow(x1, y1, x2, y2); startWrite(); for(uint16_t t=(y2 - y1 + 1) * (x2 - x1 + 1); t > 0; t--) _writeData16(color); endWrite(); _resetWindow(); } void TFT22_ILI9225::drawCircle(uint16_t x0, uint16_t y0, uint16_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(); drawPixel(x0, y0 + r, color); drawPixel(x0, y0- r, color); drawPixel(x0 + r, y0, color); drawPixel(x0 - r, y0, color); while (x= 0) { y--; ddF_y += 2; f += ddF_y; } x++; ddF_x += 2; f += ddF_x; 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); } endWrite(); } void TFT22_ILI9225::fillCircle(uint8_t x0, uint8_t y0, uint8_t radius, uint16_t color) { int16_t f = 1 - radius; int16_t ddF_x = 1; int16_t ddF_y = -2 * radius; int16_t x = 0; int16_t y = radius; startWrite(); while (x= 0) { y--; ddF_y += 2; f += ddF_y; } x++; ddF_x += 2; f += ddF_x; drawLine(x0 + x, y0 + y, x0 - x, y0 + y, color); // bottom drawLine(x0 + x, y0 - y, x0 - x, y0 - y, color); // top drawLine(x0 + y, y0 - x, x0 + y, y0 + x, color); // right drawLine(x0 - y, y0 - x, x0 - y, y0 + x, color); // left } endWrite(); fillRectangle(x0-x, y0-y, x0+x, y0+y, color); } void TFT22_ILI9225::drawLine(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t color) { // Classic Bresenham algorithm int16_t steep = abs((int16_t)(y2 - y1)) > abs((int16_t)(x2 - x1)); int16_t dx, dy; if (steep) { _swap(x1, y1); _swap(x2, y2); } if (x1 > x2) { _swap(x1, x2); _swap(y1, y2); } dx = x2 - x1; dy = abs((int16_t)(y2 - y1)); int16_t err = dx / 2; int16_t ystep; if (y1 < y2) ystep = 1; else ystep = -1; startWrite(); for (; x1<=x2; x1++) { if (steep) drawPixel(y1, x1, color); else drawPixel(x1, y1, color); err -= dy; if (err < 0) { y1 += ystep; err += dx; } } endWrite(); } void TFT22_ILI9225::drawPixel(uint16_t x1, uint16_t y1, uint16_t color) { if((x1 >= _maxX) || (y1 >= _maxY)) return; /* _setWindow(x1, y1, x1+1, y1+1); _orientCoordinates(x1, y1); startWrite(); //_writeData(color >> 8, color); _writeData16(color); endWrite(); */ _orientCoordinates(x1, y1); startWrite(); _writeRegister(ILI9225_RAM_ADDR_SET1,x1); _writeRegister(ILI9225_RAM_ADDR_SET2,y1); _writeRegister(ILI9225_GRAM_DATA_REG,color); endWrite(); } uint16_t TFT22_ILI9225::maxX() { return _maxX; } uint16_t TFT22_ILI9225::maxY() { return _maxY; } uint16_t TFT22_ILI9225::setColor(uint8_t red8, uint8_t green8, uint8_t blue8) { // rgb16 = red5 green6 blue5 return (red8 >> 3) << 11 | (green8 >> 2) << 5 | (blue8 >> 3); } void TFT22_ILI9225::splitColor(uint16_t rgb, uint8_t &red, uint8_t &green, uint8_t &blue) { // rgb16 = red5 green6 blue5 red = (rgb & 0b1111100000000000) >> 11 << 3; green = (rgb & 0b0000011111100000) >> 5 << 2; blue = (rgb & 0b0000000000011111) << 3; } void TFT22_ILI9225::_swap(uint16_t &a, uint16_t &b) { uint16_t w = a; a = b; b = w; } // Utilities /*void TFT22_ILI9225::_writeCommand16(uint16_t command) { # ifdef HSPI_WRITE16 SPI_DC_LOW(); SPI_CS_LOW(); HSPI_WRITE16(command); SPI_CS_HIGH(); #else _spiWriteCommand(command >> 8); _spiWriteCommand(0x00ff & command); #endif } void TFT22_ILI9225::_writeData16(uint16_t data) { # ifdef HSPI_WRITE16 SPI_DC_HIGH(); SPI_CS_LOW(); HSPI_WRITE16(data); SPI_CS_HIGH(); #else _spiWriteData(data >> 8); _spiWriteData(0x00ff & data); #endif } */ void TFT22_ILI9225::_writeCommand16(uint16_t command) { SPI_DC_LOW(); SPI_CS_LOW(); if ( _clk < 0 ) { # ifdef HSPI_WRITE16 HSPI_WRITE16(command); #else HSPI_WRITE(command >> 8); HSPI_WRITE(0x00ff & command); #endif } else { // Fast SPI bitbang swiped from LPD8806 library for(uint16_t bit = 0x8000; bit; bit >>= 1){ if((command) & bit){ SSPI_MOSI_HIGH(); } else { SSPI_MOSI_LOW(); } SSPI_SCK_HIGH(); SSPI_SCK_LOW(); } } SPI_CS_HIGH(); } void TFT22_ILI9225::_writeData16(uint16_t data) { SPI_DC_HIGH(); SPI_CS_LOW(); if ( _clk < 0 ) { # ifdef HSPI_WRITE16 HSPI_WRITE16(data); #else HSPI_WRITE(data >> 8); HSPI_WRITE(0x00ff & data); #endif } else { // Fast SPI bitbang swiped from LPD8806 library for(uint16_t bit = 0x8000; bit; bit >>= 1){ if((data) & bit){ SSPI_MOSI_HIGH(); } else { SSPI_MOSI_LOW(); } SSPI_SCK_HIGH(); SSPI_SCK_LOW(); } } SPI_CS_HIGH(); } /*void TFT22_ILI9225::_writeData(uint8_t HI, uint8_t LO) { _spiWriteData(HI); _spiWriteData(LO); } void TFT22_ILI9225::_writeCommand(uint8_t HI, uint8_t LO) { _spiWriteCommand(HI); _spiWriteCommand(LO); }*/ void TFT22_ILI9225::_writeRegister(uint16_t reg, uint16_t data) { _writeCommand16(reg); _writeData16(data); } void TFT22_ILI9225::drawTriangle(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t x3, uint16_t y3, uint16_t color) { startWrite(); drawLine(x1, y1, x2, y2, color); drawLine(x2, y2, x3, y3, color); drawLine(x3, y3, x1, y1, color); endWrite(); } void TFT22_ILI9225::fillTriangle(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t x3, uint16_t y3, uint16_t color) { uint16_t a, b, y, last; // Sort coordinates by Y order (y3 >= y2 >= y1) if (y1 > y2) { _swap(y1, y2); _swap(x1, x2); } if (y2 > y3) { _swap(y3, y2); _swap(x3, x2); } if (y1 > y2) { _swap(y1, y2); _swap(x1, x2); } startWrite(); if (y1 == y3) { // Handle awkward all-on-same-line case as its own thing a = b = x1; if (x2 < a) a = x2; else if (x2 > b) b = x2; if (x3 < a) a = x3; else if (x3 > b) b = x3; drawLine(a, y1, b, y1, color); return; } int16_t dx11 = x2 - x1, dy11 = y2 - y1, dx12 = x3 - x1, dy12 = y3 - y1, dx22 = x3 - x2, dy22 = y3 - y2; int32_t sa = 0, sb = 0; // For upper part of triangle, find scanline crossings for segments // 0-1 and 0-2. If y2=y3 (flat-bottomed triangle), the scanline y2 // is included here (and second loop will be skipped, avoiding a /0 // error there), otherwise scanline y2 is skipped here and handled // in the second loop...which also avoids a /0 error here if y1=y2 // (flat-topped triangle). if (y2 == y3) last = y2; // Include y2 scanline else last = y2 - 1; // Skip it for (y = y1; y <= last; y++) { a = x1 + sa / dy11; b = x1 + sb / dy12; sa += dx11; sb += dx12; /* longhand: a = x1 + (x2 - x1) * (y - y1) / (y2 - y1); b = x1 + (x3 - x1) * (y - y1) / (y3 - y1); */ if (a > b) _swap(a,b); drawLine(a, y, b, y, color); } // For lower part of triangle, find scanline crossings for segments // 0-2 and 1-2. This loop is skipped if y2=y3. sa = dx22 * (y - y2); sb = dx12 * (y - y1); for (; y<=y3; y++) { a = x2 + sa / dy22; b = x1 + sb / dy12; sa += dx22; sb += dx12; /* longhand: a = x2 + (x3 - x2) * (y - y2) / (y3 - y2); b = x1 + (x3 - x1) * (y - y1) / (y3 - y1); */ if (a > b) _swap(a,b); drawLine(a, y, b, y, color); } endWrite(); } void TFT22_ILI9225::setBackgroundColor(uint16_t color) { _bgColor = color; } void TFT22_ILI9225::setFont(uint8_t* font, bool monoSp) { cfont.font = font; cfont.width = readFontByte(0); cfont.height = readFontByte(1); cfont.offset = readFontByte(2); cfont.numchars = readFontByte(3); cfont.nbrows = cfont.height / 8; cfont.monoSp = monoSp; if (cfont.height % 8) cfont.nbrows++; // Set number of bytes used by height of font in multiples of 8 } _currentFont TFT22_ILI9225::getFont() { return cfont; } uint16_t TFT22_ILI9225::drawText(uint16_t x, uint16_t y, STRING s, uint16_t color) { uint16_t currx = x; // Print every character in string #ifdef USE_STRING_CLASS for (uint8_t k = 0; k < s.length(); k++) { currx += drawChar(currx, y, s.charAt(k), color) + 1; } #else for (uint8_t k = 0; k < strlen(s); k++) { currx += drawChar(currx, y, s[k], color) + 1; } #endif return currx; } uint16_t TFT22_ILI9225::getTextWidth( STRING s ) { uint16_t width = 0; // Count every character in string ( +1 for spacing ) #ifdef USE_STRING_CLASS for (uint8_t k = 0; k < s.length(); k++) { width += getCharWidth(s.charAt(k) ) + 1; } #else for (uint8_t k = 0; k < strlen(s); k++) { width += getCharWidth(s[k]) + 1; } #endif return width; } uint16_t TFT22_ILI9225::drawChar(uint16_t x, uint16_t y, uint16_t ch, uint16_t color) { uint8_t charData, charWidth; uint8_t h, i, j; uint16_t charOffset; bool fastMode; charOffset = (cfont.width * cfont.nbrows) + 1; // bytes used by each character charOffset = (charOffset * (ch - cfont.offset)) + FONT_HEADER_SIZE; // char offset (add 4 for font header) if ( cfont.monoSp ) charWidth = cfont.width; // monospaced: get char width from font else charWidth = readFontByte(charOffset); // get chracter width from 1st byte charOffset++; // increment pointer to first character data byte startWrite(); // use autoincrement/decrement feature, if character fits completely on screen fastMode = ( (x+charWidth+1) < _maxX && (y+cfont.height-1) < _maxY ) ; if ( fastMode )_setWindow( x,y,x+charWidth+1, y+cfont.height-1 ); // set character Window for (i = 0; i <= charWidth; i++) { // each font "column" (+1 blank column for spacing) h = 0; // keep track of char height for (j = 0; j < cfont.nbrows; j++) { // each column byte if (i == charWidth) charData = (uint8_t)0x0; // Insert blank column else charData = readFontByte(charOffset); charOffset++; // Process every row in font character for (uint8_t k = 0; k < 8; k++) { if (h >= cfont.height ) break; // No need to process excess bits if (fastMode ) _writeData16( bitRead(charData, k)?color:_bgColor ); else drawPixel( x + i, y + (j * 8) + k, bitRead(charData, k)?color:_bgColor ); h++; } } } endWrite(); _resetWindow(); return charWidth; } uint16_t TFT22_ILI9225::getCharWidth(uint16_t ch) { uint16_t charOffset; charOffset = (cfont.width * cfont.nbrows) + 1; // bytes used by each character charOffset = (charOffset * (ch - cfont.offset)) + FONT_HEADER_SIZE; // char offset (add 4 for font header) return readFontByte(charOffset); // get font width from 1st byte } // Draw a 1-bit image (bitmap) at the specified (x,y) position from the // provided bitmap buffer (must be PROGMEM memory) using the specified // foreground color (unset bits are transparent). void TFT22_ILI9225::drawBitmap(int16_t x, int16_t y, const uint8_t *bitmap, int16_t w, int16_t h, uint16_t color) { _drawBitmap( x, y, bitmap, w, h, color, 0, true, true, false ); } // Draw a 1-bit image (bitmap) at the specified (x,y) position from the // provided bitmap buffer (must be PROGMEM memory) using the specified // foreground (for set bits) and background (for clear bits) colors. void TFT22_ILI9225::drawBitmap(int16_t x, int16_t y, const uint8_t *bitmap, int16_t w, int16_t h, uint16_t color, uint16_t bg) { _drawBitmap( x, y, bitmap, w, h, color, bg, false, true, false ); } // drawBitmap() variant for RAM-resident (not PROGMEM) bitmaps. void TFT22_ILI9225::drawBitmap(int16_t x, int16_t y, uint8_t *bitmap, int16_t w, int16_t h, uint16_t color) { _drawBitmap( x, y, bitmap, w, h, color, 0, true, false, false ); } // drawBitmap() variant w/background for RAM-resident (not PROGMEM) bitmaps. void TFT22_ILI9225::drawBitmap(int16_t x, int16_t y, uint8_t *bitmap, int16_t w, int16_t h, uint16_t color, uint16_t bg) { _drawBitmap( x, y, bitmap, w, h, color, bg, false, false, false ); } //Draw XBitMap Files (*.xbm), exported from GIMP, //Usage: Export from GIMP to *.xbm, rename *.xbm to *.c and open in editor. //C Array can be directly used with this function void TFT22_ILI9225::drawXBitmap(int16_t x, int16_t y, const uint8_t *bitmap, int16_t w, int16_t h, uint16_t color) { _drawBitmap( x, y, bitmap, w, h, color, 0, true, true, true ); } void TFT22_ILI9225::drawXBitmap(int16_t x, int16_t y, const uint8_t *bitmap, int16_t w, int16_t h, uint16_t color, uint16_t bg) { _drawBitmap( x, y, bitmap, w, h, color, bg, false, true, true ); } // internal function for drawing bitmaps with/without transparent bg, or from ram or progmem void TFT22_ILI9225::_drawBitmap(int16_t x, int16_t y, const uint8_t *bitmap, int16_t w, int16_t h, uint16_t color, uint16_t bg, bool transparent, bool progmem,bool Xbit) { bool noAutoInc = false; // Flag set when transparent pixel was 'written' int16_t i, j, byteWidth = (w + 7) / 8; int16_t wx0,wy0,wx1,wy1,wh;//,ww; // Window-position and size uint8_t byte=0, maskBit; maskBit = Xbit? 0x01:0x80; // adjust window hight/width to displaydimensions DB_PRINT( "DrawBitmap.. maxX=%d, maxY=%d", _maxX,_maxY ); wx0 = x<0?0:x; wy0 = y<0?0:y; wx1 = (x+w>_maxX?_maxX:x+w)-1; wy1 = (y+h>_maxY?_maxY:y+h)-1; wh = wy1-wy0 +1; //ww = wx1-wx0 +1; _setWindow( wx0,wy0,wx1,wy1,L2R_TopDown); startWrite(); for (j = y>=0?0:-y; j < (y>=0?0:-y)+wh; j++) { for (i = 0; i < w; i++ ) { if (i & 7) { if ( Xbit ) byte >>=1; else byte <<= 1; } else { if ( progmem ) byte = pgm_read_byte(bitmap + j * byteWidth + i / 8); else byte = bitmap[j * byteWidth + i / 8]; } if ( x+i >= wx0 && x+i <= wx1 ) { // write only if pixel is within window if (byte & maskBit) { if (noAutoInc) { //there was a transparent area, set pixelkoordinates again drawPixel(x + i, y + j, color); noAutoInc = false; } else { _writeData16(color); } } else { if (transparent) noAutoInc = true; // no autoincrement in transparent area! else _writeData16( bg); } } } } endWrite(); } //Draw XBitMap Files (*.xbm), exported from GIMP, //Usage: Export from GIMP to *.xbm, rename *.xbm to *.c and open in editor. //C Array can be directly used with this function /*void TFT22_ILI9225::drawXBitmap(int16_t x, int16_t y, const uint8_t *bitmap, int16_t w, int16_t h, uint16_t color) { int16_t i, j, byteWidth = (w + 7) / 8; uint8_t byte; startWrite(); for (j = 0; j < h; j++) { for (i = 0; i < w; i++ ) { if (i & 7) byte >>= 1; else byte = pgm_read_byte(bitmap + j * byteWidth + i / 8); if (byte & 0x01) drawPixel(x + i, y + j, color); } } endWrite(); } */ //High speed color bitmap void TFT22_ILI9225::drawBitmap(uint16_t x1, uint16_t y1, const uint16_t** bitmap, int16_t w, int16_t h) { _setWindow(x1, y1, x1+w-1, y1+h-1,L2R_TopDown); startWrite(); SPI_DC_HIGH(); SPI_CS_LOW(); for (uint16_t y = 0; y < h; y++) { #ifdef HSPI_WRITE_PIXELS if (_clk < 0) { HSPI_WRITE_PIXELS(bitmap[y], w * sizeof(uint16_t)); continue; } #endif for (uint16_t x = 0; x < w; x++) { _spiWrite16(bitmap[y][x]); } } SPI_CS_HIGH(); endWrite(); } //High speed color bitmap void TFT22_ILI9225::drawBitmap(uint16_t x1, uint16_t y1, uint16_t** bitmap, int16_t w, int16_t h) { _setWindow(x1, y1, x1+w-1, y1+h-1,L2R_TopDown); startWrite(); SPI_DC_HIGH(); SPI_CS_LOW(); for (uint16_t y = 0; y < h; y++) { #ifdef HSPI_WRITE_PIXELS if (_clk < 0) { HSPI_WRITE_PIXELS(bitmap[y], w * sizeof(uint16_t)); continue; } #endif for (uint16_t x = 0; x < w; x++) { _spiWrite16(bitmap[y][x]); } } SPI_CS_HIGH(); endWrite(); } //1-D array High speed color bitmap void TFT22_ILI9225::drawBitmap(uint16_t x1, uint16_t y1, const uint16_t* bitmap, int16_t w, int16_t h) { _setWindow(x1, y1, x1+w-1, y1+h-1,L2R_TopDown); startWrite(); SPI_DC_HIGH(); SPI_CS_LOW(); #ifdef HSPI_WRITE_PIXELS if (_clk < 0) { HSPI_WRITE_PIXELS(bitmap, w * h * sizeof(uint16_t)); } else #endif for (uint16_t i = 0; i < h * w; ++i) { _spiWrite16(bitmap[i]); } SPI_CS_HIGH(); endWrite(); } //1-D array High speed color bitmap void TFT22_ILI9225::drawBitmap(uint16_t x1, uint16_t y1, uint16_t* bitmap, int16_t w, int16_t h) { _setWindow(x1, y1, x1+w-1, y1+h-1,L2R_TopDown); startWrite(); SPI_DC_HIGH(); SPI_CS_LOW(); #ifdef HSPI_WRITE_PIXELS if (_clk < 0) { HSPI_WRITE_PIXELS(bitmap, w * h * sizeof(uint16_t)); } else #endif for (uint16_t i = 0; i < h * w; ++i) { _spiWrite16(bitmap[i]); } SPI_CS_HIGH(); endWrite(); } void TFT22_ILI9225::startWrite(void){ if (writeFunctionLevel++ == 0) { SPI_BEGIN_TRANSACTION(); SPI_CS_LOW(); } } void TFT22_ILI9225::endWrite(void){ if (--writeFunctionLevel == 0) { SPI_CS_HIGH(); SPI_END_TRANSACTION(); } } // TEXT- AND CHARACTER-HANDLING FUNCTIONS ---------------------------------- void TFT22_ILI9225::setGFXFont(const GFXfont *f) { gfxFont = (GFXfont *)f; } // Draw a string void TFT22_ILI9225::drawGFXText(int16_t x, int16_t y, STRING s, uint16_t color) { int16_t currx = x; if(gfxFont) { // Print every character in string #ifdef USE_STRING_CLASS for (uint8_t k = 0; k < s.length(); k++) { currx += drawGFXChar(currx, y, s.charAt(k), color) + 1; } #else for (uint8_t k = 0; k < strlen(s); k++) { currx += drawGFXChar(currx, y, s[k], color) + 1; } #endif } } // Draw a character uint16_t TFT22_ILI9225::drawGFXChar(int16_t x, int16_t y, unsigned char c, uint16_t color) { 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), 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 = 0, bit = 0; // Add character clipping here one day startWrite(); for(yy=0; yyfirst); 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), 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 = 0, bit = 0; for(yy=0; yy=bmwd) continue; if(y+yo+yy<0) continue; // yo can be negative for(xx=0; xx=bmwd) continue; if(!(bit++ & 7)) { bits = pgm_read_byte(&bitmap[bo++]); } if(bits & 0x80) { bm[x+xo+xx + bmwd*(y+yo+yy)] = color; } bits <<= 1; } } return (uint16_t)xa; } void TFT22_ILI9225::getGFXCharExtent(uint8_t c, int16_t *gw, int16_t *gh, int16_t *xa) { uint8_t first = pgm_read_byte(&gfxFont->first), last = pgm_read_byte(&gfxFont->last); // Char present in this font? if((c >= first) && (c <= last)) { GFXglyph *glyph = &(((GFXglyph *)pgm_read_pointer(&gfxFont->glyph))[c - first]); *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); } } void TFT22_ILI9225::getGFXTextExtent(STRING str, int16_t x, int16_t y, int16_t *w, int16_t *h) { *w = *h = 0; #ifdef USE_STRING_CLASS for (uint8_t k = 0; k < str.length(); k++) { uint8_t c = str.charAt(k); #else for (uint8_t k = 0; k < strlen(str); k++) { uint8_t c = str[k]; #endif int16_t gw, gh, xa; getGFXCharExtent(c, &gw, &gh, &xa); if(gh > *h) { *h = gh; } *w += xa + 1; } if(*w>0) (*w)--; }