// This is the PDQ re-mixed version of Adafruit's library // here is the original copyright notice: /*************************************************** This is an Arduino Library for the Adafruit 2.2" SPI display. This library works with the Adafruit 2.2" TFT Breakout w/SD card ----> http://www.adafruit.com/products/1480 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 ****************************************************/ //=============================================================== // This PDQ optimized version is by Xark // // Inspiration from Paul Stoffregen and the Teensy 3.1 community. // // GOALS: // 1) Maintain "sketch" compatibility with original Adafruit libraries. // 2) Be as much faster as is reasonably possible honoring goal 1. :-) // 3) Be at least as small as Adafruit libraries. // // I believe all three of these have largely been achieved: // 1) Near full compatibility. Only minor initialization changes in original sketch. // 2) Between ~2.5 and ~12 times faster (fillRect ~2.5x, drawLine ~12x). // An average of ~4x faster over entire "graphictest.ino" benchmark. // // Even if this library is faster, it was based on the Adafruit original. // Adafruit deserves your support for making their library open-source (and // for having some nice LCD modules and all kinds of other great parts too). // Consider giving them your support if possible! #if !defined(_PDQ_ILI9341H_) #define _PDQ_ILI9341H_ #include "Arduino.h" #include "Print.h" #include "PDQ_GFX.h" #include #if !defined(ILI9341_CS_PIN) || !defined(ILI9341_DC_PIN) #error Oops! You need to #include "PDQ_ILI9341_config.h" (modified with your pin configuration and options) from your sketch before #include "PDQ_ILI9341.h". #endif #include "PDQ_FastPin.h" #if !defined(__AVR_ATtiny85__) && !defined(__AVR_ATtiny45__) #define INLINE inline #define INLINE_OPT __attribute__((always_inline)) #else #define INLINE #define INLINE_OPT #endif // Color definitions enum { ILI9341_BLACK = 0x0000, ILI9341_BLUE = 0x001F, ILI9341_RED = 0xF800, ILI9341_GREEN = 0x07E0, ILI9341_CYAN = 0x07FF, ILI9341_MAGENTA = 0xF81F, ILI9341_YELLOW = 0xFFE0, ILI9341_WHITE = 0xFFFF, }; class PDQ_ILI9341 : public PDQ_GFX { public: // ILI9341 commands // For datasheet see https://www.adafruit.com/products/1480 enum { ILI9341_NOP = 0x00, ILI9341_SWRESET = 0x01, ILI9341_RDDID = 0x04, ILI9341_RDDST = 0x09, ILI9341_SLPIN = 0x10, ILI9341_SLPOUT = 0x11, ILI9341_PTLON = 0x12, ILI9341_NORON = 0x13, ILI9341_RDMODE = 0x0A, ILI9341_RDMADCTL = 0x0B, ILI9341_RDPIXFMT = 0x0C, ILI9341_RDIMGFMT = 0x0A, ILI9341_RDSELFDIAG = 0x0F, ILI9341_INVOFF = 0x20, ILI9341_INVON = 0x21, ILI9341_GAMMASET = 0x26, ILI9341_DISPOFF = 0x28, ILI9341_DISPON = 0x29, ILI9341_CASET = 0x2A, ILI9341_PASET = 0x2B, ILI9341_RAMWR = 0x2C, ILI9341_RAMRD = 0x2E, ILI9341_PTLAR = 0x30, ILI9341_MADCTL = 0x36, ILI9341_PIXFMT = 0x3A, ILI9341_FRMCTR1 = 0xB1, ILI9341_FRMCTR2 = 0xB2, ILI9341_FRMCTR3 = 0xB3, ILI9341_INVCTR = 0xB4, ILI9341_DFUNCTR = 0xB6, ILI9341_PWCTR1 = 0xC0, ILI9341_PWCTR2 = 0xC1, ILI9341_PWCTR3 = 0xC2, ILI9341_PWCTR4 = 0xC3, ILI9341_PWCTR5 = 0xC4, ILI9341_VMCTR1 = 0xC5, ILI9341_VMCTR2 = 0xC7, ILI9341_RDID1 = 0xDA, ILI9341_RDID2 = 0xDB, ILI9341_RDID3 = 0xDC, ILI9341_RDID4 = 0xDD, ILI9341_GMCTRP1 = 0xE0, ILI9341_GMCTRN1 = 0xE1, // ILI9341_PWCTR6 = 0xFC, }; // some other misc. constants enum { // screen dimensions ILI9341_TFTWIDTH = 240, ILI9341_TFTHEIGHT = 320, // MADCTL bits ILI9341_MADCTL_MH = 0x04, // bit 2 = 0 for refresh left -> right, 1 for refresh right -> left ILI9341_MADCTL_RGB = 0x00, // bit 3 = 0 for RGB color order ILI9341_MADCTL_BGR = 0x08, // bit 3 = 1 for BGR color order ILI9341_MADCTL_ML = 0x10, // bit 4 = 0 for refresh top -> bottom, 1 for bottom -> top ILI9341_MADCTL_MV = 0x20, // bit 5 = 0 for column, row order (portrait), 1 for row, column order (landscape) ILI9341_MADCTL_MX = 0x40, // bit 6 = 0 for left -> right, 1 for right -> left order ILI9341_MADCTL_MY = 0x80, // bit 7 = 0 for top -> bottom, 1 for bottom -> top // delay indicator bit for commandList() DELAY = 0x80 }; // higher-level routines PDQ_ILI9341(); static void inline begin(void); static void setAddrWindow(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1); static void pushColor(uint16_t color); static void pushColor(uint16_t color, int cnt); // Pass 8-bit (each) R,G,B, get back 16-bit packed color static INLINE uint16_t color565(uint8_t r, uint8_t g, uint8_t b) { return ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3); } static INLINE uint16_t Color565(uint8_t r, uint8_t g, uint8_t b) // older inconsistent name for compatibility { return ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3); } // required driver primitive methods (all except drawPixel can call generic version in PDQ_GFX with "_" postfix). static void drawPixel(int x, int y, uint16_t color); static void drawFastVLine(int x, int y, int h, uint16_t color); static void drawFastHLine(int x, int y, int w, uint16_t color); static void setRotation(uint8_t r); static void invertDisplay(boolean i); static inline void fillScreen(uint16_t color) __attribute__((always_inline)) { fillScreen_(color); // call generic version } static void drawLine(int x0, int y0, int x1, int y1, uint16_t color); static void fillRect(int x, int y, int w, int h, uint16_t color); // === lower-level internal routines ========= static void commandList(const uint8_t *addr); // NOTE: Make sure each spi_begin() is matched with a single spi_end() (and don't call either twice) // set CS back to low (LCD selected) static inline void spi_begin() __attribute__((always_inline)) { #if ILI9341_SAVE_SPCR && defined(AVR_HARDWARE_SPI) swapValue(save_SPCR, SPCR); // swap initial/current SPCR settings #endif FastPin::lo(); // CS <= LOW (selected) } // NOTE: Make sure each spi_begin() is matched with a single spi_end() (and don't call either twice) // reset CS back to high (LCD unselected) static inline void spi_end() __attribute__((always_inline)) { FastPin::hi(); // CS <= HIGH (deselected) #if ILI9341_SAVE_SPCR && defined(AVR_HARDWARE_SPI) swapValue(SPCR, save_SPCR); // swap current/initial SPCR settings #endif } #if defined(AVR_HARDWARE_SPI) // 10 cycle delay (including "call") static void delay10() __attribute__((noinline)) __attribute__((naked)) { __asm__ __volatile__ ( // +4 (call to get here) #if !defined(__AVR_HAVE_RAMPD__) " adiw r24,0\n" // +2 (2-cycle NOP) #else " nop\n" // +1 (1-cycle NOP) #endif " ret\n" // +4 (or +5 on >64KB AVR with RAMPD reg) // = 10 cycles : : : ); } // 13 cycle delay (including "call") static void delay13() __attribute__((noinline)) __attribute__((naked)) { __asm__ __volatile__ ( // +4 (call to get here) " adiw r24,0\n" // +2 (2-cycle NOP) " adiw r24,0\n" // +2 (2-cycle NOP) #if !defined(__AVR_HAVE_RAMPD__) " nop\n" // +1 (1-cycle NOP) #endif " ret\n" // +4 (or +5 on >64KB AVR with RAMPD reg) // = 13 cycles : : : ); } // 15 cycle delay (including "call") static void delay15() __attribute__((noinline)) __attribute__((naked)) { __asm__ __volatile__ ( // +4 (call to get here) " adiw r24,0\n" // +2 (2-cycle NOP) " adiw r24,0\n" // +2 (2-cycle NOP) " adiw r24,0\n" // +2 (2-cycle NOP) #if !defined(__AVR_HAVE_RAMPD__) " nop\n" // +1 (1-cycle NOP) #endif " ret\n" // +4 (or +5 on >64KB AVR with RAMPD reg) // = 15 cycles : : : ); } // 17 cycle delay (including "call") static void delay17() __attribute__((noinline)) __attribute__((naked)) { __asm__ __volatile__ ( // +4 (call to get here) " adiw r24,0\n" // +2 (2-cycle NOP) " adiw r24,0\n" // +2 (2-cycle NOP) " adiw r24,0\n" // +2 (2-cycle NOP) " adiw r24,0\n" // +2 (2-cycle NOP) #if !defined(__AVR_HAVE_RAMPD__) " nop\n" // +1 (2-cycle NOP) #endif " ret\n" // +4 (or +5 on >64KB AVR with RAMPD reg) // = 17 cycles : : : ); } // normal SPI write with minimal hand-tuned delay (assuming max DIV2 SPI rate) static INLINE void spiWrite(uint8_t data) INLINE_OPT { SPDR = data; __asm__ __volatile__ ( " call _ZN11PDQ_ILI93417delay17Ev\n" // call mangled delay17 (compiler would needlessly save/restore regs) : : : ); } // special SPI write with minimal hand-tuned delay (assuming max DIV2 SPI rate) - minus 2 cycles for RS (etc.) change static INLINE void spiWrite_preCmd(uint8_t data) INLINE_OPT { SPDR = data; __asm__ __volatile__ ( " call _ZN11PDQ_ILI93417delay15Ev\n" // call mangled delay15 (compiler would needlessly save/restore regs) : : : ); } // SPI 16-bit write with minimal hand-tuned delay (assuming max DIV2 SPI rate) static INLINE void spiWrite16(uint16_t data) INLINE_OPT { uint8_t temp; __asm__ __volatile__ ( " out %[spi],%[hi]\n" // write SPI data (18 cycles until next write) " call _ZN11PDQ_ILI93417delay17Ev\n" // call mangled delay17 (compiler would needlessly save/restore regs) " out %[spi],%[lo]\n" // write SPI data (18 cycles until next write) " call _ZN11PDQ_ILI93417delay17Ev\n" // call mangled delay17 (compiler would needlessly save/restore regs) : [temp] "=d" (temp) : [spi] "i" (_SFR_IO_ADDR(SPDR)), [lo] "r" ((uint8_t)data), [hi] "r" ((uint8_t)(data>>8)) : ); } // SPI 16-bit write with minimal hand-tuned delay (assuming max DIV2 SPI rate) minus 2 cycles static INLINE void spiWrite16_preCmd(uint16_t data) INLINE_OPT { uint8_t temp; __asm__ __volatile__ ( " out %[spi],%[hi]\n" // write SPI data (18 cycles until next write) " call _ZN11PDQ_ILI93417delay17Ev\n" // call mangled delay17 (compiler would needlessly save/restore regs) " out %[spi],%[lo]\n" // write SPI data (18 cycles until next write) " call _ZN11PDQ_ILI93417delay15Ev\n" // call mangled delay15 (compiler would needlessly save/restore regs) : [temp] "=d" (temp) : [spi] "i" (_SFR_IO_ADDR(SPDR)), [lo] "r" ((uint8_t)data), [hi] "r" ((uint8_t)(data>>8)) : ); } // SPI 16-bit write with minimal hand-tuned delay (assuming max DIV2 SPI rate) minus 4 cycles static INLINE void spiWrite16_lineDraw(uint16_t data) INLINE_OPT { uint8_t temp; __asm__ __volatile__ ( " out %[spi],%[hi]\n" // write SPI data (18 cycles until next write) " call _ZN11PDQ_ILI93417delay17Ev\n" // call mangled delay17 (compiler would needlessly save/restore regs) " out %[spi],%[lo]\n" // write SPI data (18 cycles until next write) : [temp] "=d" (temp) : [spi] "i" (_SFR_IO_ADDR(SPDR)), [lo] "r" ((uint8_t)data), [hi] "r" ((uint8_t)(data>>8)) : ); } // normal SPI write with minimal hand-tuned delay (assuming max DIV2 SPI rate) static INLINE void spiWrite16(uint16_t data, int count) INLINE_OPT { uint8_t temp; __asm__ __volatile__ ( " sbiw %[count],0\n" // test count " brmi 4f\n" // if < 0 then done " breq 4f\n" // if == 0 then done "1: out %[spi],%[hi]\n" // write SPI data (18 cycles until next write) " call _ZN11PDQ_ILI93417delay17Ev\n" // call mangled delay17 (compiler would needlessly save/restore regs) " out %[spi],%[lo]\n" // write SPI data (18 cycles until next write) " call _ZN11PDQ_ILI93417delay13Ev\n" // call mangled delay13 (compiler would needlessly save/restore regs) " sbiw %[count],1\n" // +2 decrement count " brne 1b\n" // +2/1 if != 0 then loop // = 13 + 2 + 2 (17 cycles) "4:\n" : [temp] "=d" (temp), [count] "+w" (count) : [spi] "i" (_SFR_IO_ADDR(SPDR)), [lo] "r" ((uint8_t)data), [hi] "r" ((uint8_t)(data>>8)) : ); } #else // bit-bang #if defined(__AVR_ATtiny85__) || defined(__AVR_ATtiny45__) // USI hardware assisted static void spiWrite(uint8_t data) __attribute__((noinline)) { USIDR = data; __asm__ __volatile__ ( " out %[spi],%[clkp0]\n" // MSB " out %[spi],%[clkp1]\n" " out %[spi],%[clkp0]\n" " out %[spi],%[clkp1]\n" " out %[spi],%[clkp0]\n" " out %[spi],%[clkp1]\n" " out %[spi],%[clkp0]\n" " out %[spi],%[clkp1]\n" " out %[spi],%[clkp0]\n" " out %[spi],%[clkp1]\n" " out %[spi],%[clkp0]\n" " out %[spi],%[clkp1]\n" " out %[spi],%[clkp0]\n" " out %[spi],%[clkp1]\n" " out %[spi],%[clkp0]\n" // LSB " out %[spi],%[clkp1]\n" : : [spi] "i" (_SFR_IO_ADDR(USICR)), [clkp0] "a" ((uint8_t)((1<>8; __asm__ __volatile__ ( " out %[spi],%[clkp0]\n" // MSB " out %[spi],%[clkp1]\n" " out %[spi],%[clkp0]\n" " out %[spi],%[clkp1]\n" " out %[spi],%[clkp0]\n" " out %[spi],%[clkp1]\n" " out %[spi],%[clkp0]\n" " out %[spi],%[clkp1]\n" " out %[spi],%[clkp0]\n" " out %[spi],%[clkp1]\n" " out %[spi],%[clkp0]\n" " out %[spi],%[clkp1]\n" " out %[spi],%[clkp0]\n" " out %[spi],%[clkp1]\n" " out %[spi],%[clkp0]\n" // LSB " out %[spi],%[clkp1]\n" : : [spi] "i" (_SFR_IO_ADDR(USICR)), [clkp0] "a" ((uint8_t)((1<>= 1) { if (data & bit) FastPin::hi(); else FastPin::lo(); FastPin::hi(); FastPin::lo(); } } static void spiWrite16(uint16_t data) __attribute__((noinline)) { spiWrite(data >> 8); spiWrite(data & 0xff); } #endif static INLINE void spiWrite_preCmd(uint8_t data) INLINE_OPT { spiWrite(data); } static INLINE void spiWrite16_preCmd(uint16_t data) INLINE_OPT { spiWrite16(data); } static INLINE void spiWrite16_lineDraw(uint16_t data) INLINE_OPT { spiWrite16(data); } static INLINE void spiWrite16(uint16_t data, int count) INLINE_OPT { while (count-- > 0) spiWrite16(data); } static inline void delay10() { } static inline void delay13() { } static inline void delay15() { } static inline void delay17() { } #endif // write SPI byte with RS (aka D/C) pin set low to indicate a command byte (and then reset back to high when done) static INLINE void writeCommand(uint8_t data) INLINE_OPT { FastPin::lo(); // RS <= LOW indicate command byte spiWrite_preCmd(data); FastPin::hi(); // RS <= HIGH indicate data byte (always assumed left in data mode) } // write SPI byte with RS assumed low indicating a data byte static inline void writeData(uint8_t data) __attribute__((always_inline)) { spiWrite(data); } // internal version that does not spi_begin()/spi_end() static INLINE void setAddrWindow_(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1) INLINE_OPT { writeCommand(ILI9341_CASET); // column address set spiWrite16(x0); // XSTART spiWrite16_preCmd(x1); // XEND writeCommand(ILI9341_PASET); // row address set spiWrite16(y0); // YSTART spiWrite16_preCmd(y1); // YEND writeCommand(ILI9341_RAMWR); // write to RAM } #if ILI9341_SAVE_SPCR && defined(AVR_HARDWARE_SPI) static volatile uint8_t save_SPCR; // initial SPCR value/saved SPCR value (swapped in spi_begin/spi_end) #endif }; typedef PDQ_GFX_Button_ PDQ_GFX_Button; /*************************************************** This is an Arduino Library for the Adafruit 2.2" SPI display. This library works with the Adafruit 2.2" TFT Breakout w/SD card ----> http://www.adafruit.com/products/1480 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 ****************************************************/ #if ILI9341_SAVE_SPCR && defined(AVR_HARDWARE_SPI) // static data needed by base class volatile uint8_t PDQ_ILI9341::save_SPCR; #endif // Constructor when using hardware SPI. PDQ_ILI9341::PDQ_ILI9341() : PDQ_GFX(ILI9341_TFTWIDTH, ILI9341_TFTHEIGHT) { #if defined(AVR_HARDWARE_SPI) // must reference these functions from C++ or they will be stripped by linker (called from inline asm) delay10(); delay13(); delay15(); delay17(); #endif } // Companion code to the above tables. Reads and issues // a series of LCD commands stored in PROGMEM byte array. void PDQ_ILI9341::commandList(const uint8_t *addr) { uint8_t numCommands, numArgs; uint16_t ms; numCommands = pgm_read_byte(addr++); // Number of commands to follow while (numCommands--) // For each command... { writeCommand(pgm_read_byte(addr++)); // Read, issue command numArgs = pgm_read_byte(addr++); // Number of args to follow ms = numArgs & DELAY; // If hibit set, delay follows args numArgs &= ~DELAY; // Mask out delay bit while (numArgs--) // For each argument... { writeData(pgm_read_byte(addr++)); // Read, issue argument } if (ms) { ms = pgm_read_byte(addr++); // Read post-command delay time (ms) if(ms == 255) ms = 500; // If 255, delay for 500 ms delay(ms); } } } void PDQ_ILI9341::begin(void) { // set CS and RS pin directions to output FastPin::setOutput(); FastPin::setOutput(); #if !defined(AVR_HARDWARE_SPI) #if defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) USICR = (0<::setInput(); #endif FastPin::setOutput(); FastPin::setOutput(); FastPin::lo(); FastPin::lo(); #endif FastPin::hi(); // CS <= HIGH (deselected, so no spurious data) FastPin::hi(); // RS <= HIGH (default data byte) #if defined(AVR_HARDWARE_SPI) #if ILI9341_SAVE_SPCR uint8_t oldSPCR = SPCR; // save initial SPCR settings #endif SPI.begin(); SPI.setBitOrder(MSBFIRST); SPI.setDataMode(SPI_MODE0); SPI.setClockDivider(SPI_CLOCK_DIV2); // 8 MHz (full! speed!) [1 byte every 18 cycles] #endif #if ILI9341_SAVE_SPCR && defined(AVR_HARDWARE_SPI) save_SPCR = SPCR; // save SPCR settings SPCR = oldSPCR; // restore previous SPCR settings (spi_begin/spi_end will switch between the two) #endif spi_begin(); // Initialization commands for ILI9341 screens static const uint8_t ILI9341_cmds[] PROGMEM = { 22, ILI9341_SWRESET, DELAY, // 1 5, 0xEF, 3, // 2 0x03, 0x80, 0x02, 0xCF, 3, // 3 0x00, 0xC1, 0x30, 0xED, 4, // 4 0x64, 0x03, 0x12, 0x81, 0xE8, 3, // 5 0x85, 0x00, 0x78, 0xCB, 5, // 6 0x39, 0x2C, 0x00, 0x34, 0x02, 0xF7, 1, // 7 0x20, 0xEA, 2, // 8 0x00, 0x00, ILI9341_PWCTR1, 1, // 9 power control 0x23, // VRH[5:0] ILI9341_PWCTR2, 1, // 10 power control 0x10, // SAP[2:0];BT[3:0] ILI9341_VMCTR1, 2, // 11 VCM control 0x3e, 0x28, ILI9341_VMCTR2, 1, // 12 VCM control2 0x86, // -- ILI9341_MADCTL, 1, // 13 (ILI9341_MADCTL_MX | ILI9341_MADCTL_BGR), ILI9341_PIXFMT, 1, // 14 0x55, ILI9341_FRMCTR1, 2, // 15 0x00, 0x18, ILI9341_DFUNCTR, 3, // 16 0x08, 0x82, 0x27, 0xF2, 1, // 17 3Gamma Function Disable 0x00, ILI9341_GAMMASET, 1, // 18 Gamma curve selected 0x01, ILI9341_GMCTRP1, 15, // 19 Set Gamma 0x0F, 0x31, 0x2B, 0x0C, 0x0E, 0x08, 0x4E, 0xF1, 0x37, 0x07, 0x10, 0x03, 0x0E, 0x09, 0x00, ILI9341_GMCTRN1, 15, // 20 0x00, 0x0E, 0x14, 0x03, 0x11, 0x07, 0x31, 0xC1, 0x48, 0x08, 0x0F, 0x0C, 0x31, 0x36, 0x0F, ILI9341_SLPOUT, DELAY, // 21 120, ILI9341_DISPON, 0, // 22 }; commandList(ILI9341_cmds); spi_end(); } void PDQ_ILI9341::setAddrWindow(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1) { spi_begin(); setAddrWindow_(x0, y0, x1, y1); spi_end(); } void PDQ_ILI9341::pushColor(uint16_t color) { spi_begin(); spiWrite16_preCmd(color); spi_end(); } void PDQ_ILI9341::pushColor(uint16_t color, int count) { spi_begin(); spiWrite16(color, count); spi_end(); } void PDQ_ILI9341::drawPixel(int x, int y, uint16_t color) { if ((x < 0) ||(x >= _width) || (y < 0) || (y >= _height)) return; spi_begin(); setAddrWindow_(x, y, x, y); spiWrite16_preCmd(color); spi_end(); } void PDQ_ILI9341::drawFastVLine(int x, int y, int h, uint16_t color) { // clipping if ((x < 0) || (x >= _width) || (y >= _height)) return; if (y < 0) { h += y; y = 0; } int y1 = y+h; if (y1 < 0) return; if (y1 > _height) h = _height-y; spi_begin(); setAddrWindow_(x, y, x, _height); spiWrite16(color, h); spi_end(); } void PDQ_ILI9341::drawFastHLine(int x, int y, int w, uint16_t color) { // clipping if ((x >= _width) || (y < 0) || (y >= _height)) return; if (x < 0) { w += x; x = 0; } int x1 = x+w; if (x1 < 0) return; if (x1 > _width) w = _width-w; spi_begin(); setAddrWindow_(x, y, _width, y); spiWrite16(color, w); spi_end(); } void PDQ_ILI9341::fillRect(int x, int y, int w, int h, uint16_t color) { // rudimentary clipping (drawChar w/big text requires this) 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; spi_begin(); setAddrWindow_(x, y, x+w-1, _height); for (; h > 0; h--) { spiWrite16(color, w); } spi_end(); } // Bresenham's algorithm - thx Wikipedia void PDQ_ILI9341::drawLine(int x0, int y0, int x1, int y1, uint16_t color) { #if 0 && defined(__AVR_ATtiny85__) || defined(__AVR_ATtiny45__) drawLine_(x0, y0, x1, y1, color); #else int8_t steep = abs(y1 - y0) > abs(x1 - x0); if (steep) { swapValue(x0, y0); swapValue(x1, y1); } if (x0 > x1) { swapValue(x0, x1); swapValue(y0, y1); } if (x1 < 0) return; int dx, dy; dx = x1 - x0; dy = abs(y1 - y0); int err = dx / 2; int8_t ystep; if (y0 < y1) { ystep = 1; } else { ystep = -1; } uint8_t setaddr = 1; #if 0 && defined(__AVR_ATtiny85__) && !defined(__AVR_ATtiny45__) int end = steep ? _height-1 : _width-1; if (x1 > end) x1 = end; for (; x0 <= x1; x0++) { if ((x0 >= 0) && (y0 >= 0) && (y0 <= end)) break; err -= dy; if (err < 0) { err += dx; y0 += ystep; } } if (x0 > x1) return; spi_begin(); for (; x0 <= x1; x0++) { if (setaddr) { if (steep) setAddrWindow_(y0, x0, y0, end+1); else setAddrWindow_(x0, y0, end+1, y0); setaddr = 0; } spiWrite16_lineDraw(color); err -= dy; if (err < 0) { y0 += ystep; if ((y0 < 0) || (y0 > end)) break; err += dx; setaddr = 1; } } #else if (steep) // y increments every iteration (y0 is x-axis, and x0 is y-axis) { if (x1 >= _height) x1 = _height - 1; for (; x0 <= x1; x0++) { if ((x0 >= 0) && (y0 >= 0) && (y0 < _width)) break; err -= dy; if (err < 0) { err += dx; y0 += ystep; } } if (x0 > x1) return; spi_begin(); for (; x0 <= x1; x0++) { if (setaddr) { setAddrWindow_(y0, x0, y0, _height); setaddr = 0; } spiWrite16_lineDraw(color); err -= dy; if (err < 0) { y0 += ystep; if ((y0 < 0) || (y0 >= _width)) break; err += dx; setaddr = 1; } #if defined(AVR_HARDWARE_SPI) else { __asm__ __volatile__ ( " call _ZN11PDQ_ILI93417delay10Ev\n" : : : ); } #endif } } else // x increments every iteration (x0 is x-axis, and y0 is y-axis) { if (x1 >= _width) x1 = _width - 1; for (; x0 <= x1; x0++) { if ((x0 >= 0) && (y0 >= 0) && (y0 < _height)) break; err -= dy; if (err < 0) { err += dx; y0 += ystep; } } if (x0 > x1) return; spi_begin(); for (; x0 <= x1; x0++) { if (setaddr) { setAddrWindow_(x0, y0, _width, y0); setaddr = 0; } spiWrite16_lineDraw(color); err -= dy; if (err < 0) { y0 += ystep; if ((y0 < 0) || (y0 >= _height)) break; err += dx; setaddr = 1; } #if defined(AVR_HARDWARE_SPI) else { __asm__ __volatile__ ( " call _ZN11PDQ_ILI93417delay10Ev\n" : : : ); } #endif } } #endif spi_end(); #endif } void PDQ_ILI9341::setRotation(uint8_t m) { rotation = (m & 3); // can't be higher than 3 spi_begin(); writeCommand(ILI9341_MADCTL); switch (rotation) { default: case 0: writeData(ILI9341_MADCTL_MX | ILI9341_MADCTL_BGR); _width = ILI9341_TFTWIDTH; _height = ILI9341_TFTHEIGHT; break; case 1: writeData(ILI9341_MADCTL_MV | ILI9341_MADCTL_BGR); _width = ILI9341_TFTHEIGHT; _height = ILI9341_TFTWIDTH; break; case 2: writeData(ILI9341_MADCTL_MY | ILI9341_MADCTL_BGR); _width = ILI9341_TFTWIDTH; _height = ILI9341_TFTHEIGHT; break; case 3: writeData(ILI9341_MADCTL_MV | ILI9341_MADCTL_MY | ILI9341_MADCTL_MX | ILI9341_MADCTL_BGR); _width = ILI9341_TFTHEIGHT; _height = ILI9341_TFTWIDTH; break; } spi_end(); } void PDQ_ILI9341::invertDisplay(boolean i) { spi_begin(); writeCommand(i ? ILI9341_INVON : ILI9341_INVOFF); spi_end(); } #endif // !defined(_PDQ_ILI9341H_)