diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4b0c279..1e79ef4 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -50,6 +50,22 @@ jobs: cp ${pioenv}/firmware.bin firmware_output/${pioenv}.bin cp ${pioenv}/merged-firmware.bin firmware_output/${pioenv}_full.bin + - name: Build & Package esp32-s3-N16R8-extuart Firmware + run: | + export PLATFORMIO_BUILD_FLAGS="-D BUILD_VERSION=\"${{ github.ref_name }}\" -D SHA=$GITHUB_SHA" + pioenv=esp32-s3-N16R8-extuart + pio run --environment ${pioenv} + mkdir ${pioenv} + cp ~/.platformio/packages/framework-arduinoespressif32/tools/partitions/boot_app0.bin ${pioenv}/boot_app0.bin + cp .pio/build/${pioenv}/firmware.bin ${pioenv}/firmware.bin + cp .pio/build/${pioenv}/bootloader.bin ${pioenv}/bootloader.bin + cp .pio/build/${pioenv}/partitions.bin ${pioenv}/partitions.bin + cd ${pioenv} + esptool.py --chip esp32-s3 merge_bin -o merged-firmware.bin --flash_mode dio --flash_freq 80m --flash_size 16MB 0x0000 bootloader.bin 0x8000 partitions.bin 0xe000 boot_app0.bin 0x10000 firmware.bin + cd .. + cp ${pioenv}/firmware.bin firmware_output/${pioenv}.bin + cp ${pioenv}/merged-firmware.bin firmware_output/${pioenv}_full.bin + - name: Build & Package esp32-s3-N8R8 Firmware run: | export PLATFORMIO_BUILD_FLAGS="-D BUILD_VERSION=\"${{ github.ref_name }}\" -D SHA=$GITHUB_SHA" diff --git a/.gitignore b/.gitignore index ce78e8f..44e447d 100644 --- a/.gitignore +++ b/.gitignore @@ -77,6 +77,7 @@ dkms.conf .clang_complete .gcc-flags.json .pio +.vscode ### Other files .idea diff --git a/lib/Seeed_GFX/CMakeLists.txt b/lib/Seeed_GFX/CMakeLists.txt new file mode 100644 index 0000000..23fe222 --- /dev/null +++ b/lib/Seeed_GFX/CMakeLists.txt @@ -0,0 +1,9 @@ +idf_component_register(SRCS "TFT_eSPI.cpp" + INCLUDE_DIRS "." + "Extensions" + REQUIRES driver arduino-esp32 +) + + +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-misleading-indentation") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-misleading-indentation") \ No newline at end of file diff --git a/lib/Seeed_GFX/Extensions/Button.cpp b/lib/Seeed_GFX/Extensions/Button.cpp new file mode 100644 index 0000000..9c93098 --- /dev/null +++ b/lib/Seeed_GFX/Extensions/Button.cpp @@ -0,0 +1,107 @@ +/*************************************************************************************** +** Code for the GFX button UI element +** Grabbed from Adafruit_GFX library and enhanced to handle any label font +***************************************************************************************/ +TFT_eSPI_Button::TFT_eSPI_Button(void) { + _gfx = nullptr; + _xd = 0; + _yd = 0; + _textdatum = MC_DATUM; + _label[9] = '\0'; + currstate = false; + laststate = false; +} + +// Classic initButton() function: pass center & size +void TFT_eSPI_Button::initButton( + TFT_eSPI *gfx, int16_t x, int16_t y, uint16_t w, uint16_t h, + uint16_t outline, uint16_t fill, uint16_t textcolor, + char *label, uint8_t textsize) +{ + // Tweak arguments and pass to the newer initButtonUL() function... + initButtonUL(gfx, x - (w / 2), y - (h / 2), w, h, outline, fill, + textcolor, label, textsize); +} + +// Newer function instead accepts upper-left corner & size +void TFT_eSPI_Button::initButtonUL( + TFT_eSPI *gfx, int16_t x1, int16_t y1, uint16_t w, uint16_t h, + uint16_t outline, uint16_t fill, uint16_t textcolor, + char *label, uint8_t textsize) +{ + _x1 = x1; + _y1 = y1; + _w = w; + _h = h; + _outlinecolor = outline; + _fillcolor = fill; + _textcolor = textcolor; + _textsize = textsize; + _gfx = gfx; + strncpy(_label, label, 9); +} + +// Adjust text datum and x, y deltas +void TFT_eSPI_Button::setLabelDatum(int16_t x_delta, int16_t y_delta, uint8_t datum) +{ + _xd = x_delta; + _yd = y_delta; + _textdatum = datum; +} + +void TFT_eSPI_Button::drawButton(bool inverted, String long_name) { + uint16_t fill, outline, text; + + if(!inverted) { + fill = _fillcolor; + outline = _outlinecolor; + text = _textcolor; + } else { + fill = _textcolor; + outline = _outlinecolor; + text = _fillcolor; + } + + uint8_t r = min(_w, _h) / 4; // Corner radius + _gfx->fillRoundRect(_x1, _y1, _w, _h, r, fill); + _gfx->drawRoundRect(_x1, _y1, _w, _h, r, outline); + + if (_gfx->textfont == 255) { + _gfx->setCursor(_x1 + (_w / 8), + _y1 + (_h / 4)); + _gfx->setTextColor(text); + _gfx->setTextSize(_textsize); + _gfx->print(_label); + } + else { + _gfx->setTextColor(text, fill); + _gfx->setTextSize(_textsize); + + uint8_t tempdatum = _gfx->getTextDatum(); + _gfx->setTextDatum(_textdatum); + uint16_t tempPadding = _gfx->getTextPadding(); + _gfx->setTextPadding(0); + + if (long_name == "") + _gfx->drawString(_label, _x1 + (_w/2) + _xd, _y1 + (_h/2) - 4 + _yd); + else + _gfx->drawString(long_name, _x1 + (_w/2) + _xd, _y1 + (_h/2) - 4 + _yd); + + _gfx->setTextDatum(tempdatum); + _gfx->setTextPadding(tempPadding); + } +} + +bool TFT_eSPI_Button::contains(int16_t x, int16_t y) { + return ((x >= _x1) && (x < (_x1 + _w)) && + (y >= _y1) && (y < (_y1 + _h))); +} + +void TFT_eSPI_Button::press(bool p) { + laststate = currstate; + currstate = p; +} + +bool TFT_eSPI_Button::isPressed() { return currstate; } +bool TFT_eSPI_Button::justPressed() { return (currstate && !laststate); } +bool TFT_eSPI_Button::justReleased() { return (!currstate && laststate); } diff --git a/lib/Seeed_GFX/Extensions/Button.h b/lib/Seeed_GFX/Extensions/Button.h new file mode 100644 index 0000000..3a0e2d5 --- /dev/null +++ b/lib/Seeed_GFX/Extensions/Button.h @@ -0,0 +1,44 @@ +/*************************************************************************************** +// The following button class has been ported over from the Adafruit_GFX library so +// should be compatible. +// A slightly different implementation in this TFT_eSPI library allows the button +// legends to be in any font, allow longer labels and to adjust text positioning +// within button +***************************************************************************************/ + +class TFT_eSPI_Button +{ + public: + TFT_eSPI_Button(void); + // "Classic" initButton() uses centre & size + void initButton(TFT_eSPI *gfx, int16_t x, int16_t y, + uint16_t w, uint16_t h, uint16_t outline, uint16_t fill, + uint16_t textcolor, char *label, uint8_t textsize); + + // New/alt initButton() uses upper-left corner & size + void initButtonUL(TFT_eSPI *gfx, int16_t x1, int16_t y1, + uint16_t w, uint16_t h, uint16_t outline, uint16_t fill, + uint16_t textcolor, char *label, uint8_t textsize); + + // Adjust text datum and x, y deltas + void setLabelDatum(int16_t x_delta, int16_t y_delta, uint8_t datum = MC_DATUM); + + void drawButton(bool inverted = false, String long_name = ""); + bool contains(int16_t x, int16_t y); + + void press(bool p); + bool isPressed(); + bool justPressed(); + bool justReleased(); + + private: + TFT_eSPI *_gfx; + int16_t _x1, _y1; // Coordinates of top-left corner of button + int16_t _xd, _yd; // Button text datum offsets (wrt centre of button) + uint16_t _w, _h; // Width and height of button + uint8_t _textsize, _textdatum; // Text size multiplier and text datum for button + uint16_t _outlinecolor, _fillcolor, _textcolor; + char _label[10]; // Button text is 9 chars maximum unless long_name used + + bool currstate, laststate; // Button states +}; diff --git a/lib/Seeed_GFX/Extensions/EPaper.cpp b/lib/Seeed_GFX/Extensions/EPaper.cpp new file mode 100644 index 0000000..52ee33b --- /dev/null +++ b/lib/Seeed_GFX/Extensions/EPaper.cpp @@ -0,0 +1,266 @@ +EPaper::EPaper() : TFT_eSprite(this), _sleep(false), _entemp(true), _temp(16.00), _humi(50.00) +{ + setColorDepth(EPD_COLOR_DEPTH); + createSprite(_width, _height, 1); + //createPalette(cmap, 16); +} + +void EPaper::begin(uint8_t wake) +{ + setBitmapColor(1, 0); + setTextFont(1); + setTextColor(TFT_BLACK, TFT_WHITE, true); + if(wake) + { + initFromSleep(); + } + else + { + init(); + EPD_WAKEUP(); + } +// fillSprite(1); +// #ifdef EPD_HORIZONTAL_MIRROR +// EPD_PUSH_OLD_COLORS_FLIP(_width, _height, _img8); +// fillSprite(0); +// EPD_PUSH_NEW_COLORS_FLIP(_width, _height, _img8); +// #else +// EPD_PUSH_OLD_COLORS(_width, _height, _img8); +// fillSprite(0); +// EPD_PUSH_NEW_COLORS(_width, _height, _img8); +// #endif +// EPD_UPDATE(); + +} + + void EPaper::drawBufferPixel(int32_t x, int32_t y, uint32_t color, uint8_t bpp) + { + _img8[y * (_width / (8 / bpp)) + (x / (8 / bpp))] = color; + } + +void EPaper::update() +{ + wake(); + EPD_SET_WINDOW(0, 0, (_width - 1), (_height - 1)); + if(!_grayLevel) + { + #ifdef EPD_HORIZONTAL_MIRROR + EPD_PUSH_OLD_COLORS_FLIP(_width, _height, _img8); + EPD_PUSH_NEW_COLORS_FLIP(_width, _height, _img8); + #else + EPD_PUSH_OLD_COLORS(_width, _height, _img8); + EPD_PUSH_NEW_COLORS(_width, _height, _img8); + #endif + EPD_UPDATE(); + } + else + { + #ifdef USE_MUTIGRAY_EPAPER + #ifdef EPD_HORIZONTAL_MIRROR + EPD_PUSH_NEW_GRAY_COLORS_FLIP(_width, _height, _img8); + #else + EPD_PUSH_NEW_GRAY_COLORS(_width, _height, _img8); + #endif + EPD_UPDATE_GRAY(); + #endif + } + sleep(); +} + +#ifdef USE_PARTIAL_EPAPER +void EPaper::updataPartial(uint16_t x, uint16_t y, uint16_t w, uint16_t h) +{ + + int32_t bx = x; + int32_t by = y; + int32_t bw = w; + int32_t bh = h; + // Map the rotated coordinate space back to the backing buffer. + switch (rotation & 3) + { + case 1: + bx = (int32_t)_width - y - h; + by = x; + bw = h; + bh = w; + break; + case 2: + bx = (int32_t)_width - x - w; + by = (int32_t)_height - y - h; + break; + case 3: + bx = y; + by = (int32_t)_height - x - w; + bw = h; + bh = w; + break; + default: + break; + } + + if (bx < 0) { bw += bx; bx = 0; } + if (by < 0) { bh += by; by = 0; } + if ((bx + bw) > (int32_t)_width) bw = (int32_t)_width - bx; + if ((by + bh) > (int32_t)_height) bh = (int32_t)_height - by; + if (bw < 1 || bh < 1) return; + + uint16_t align_px = 8; +#ifdef TCON_ENABLE + align_px = 16; + wake(); + _sleep = false; +#else + if (_sleep) { EPD_WAKEUP_PARTIAL(); _sleep = false; } +#endif + + uint16_t x0 = ((uint16_t)bx) & ~(align_px - 1); + uint16_t x1 = ((uint16_t)(bx + bw + (align_px - 1))) & ~(align_px - 1); + uint16_t w_aligned = x1 - x0; + uint16_t yy = (uint16_t)by; + uint16_t hh = (uint16_t)bh; + + uint16_t stride = _width >> 3; + uint16_t win_bytes_per_row = w_aligned >> 3; + + const uint8_t* src0 = _img8 + (yy * stride) + (x0 >> 3); + + size_t win_size = (size_t)win_bytes_per_row * hh; + uint8_t* winbuf = (uint8_t*)malloc(win_size); + if (!winbuf) return; + + + for (uint16_t row = 0; row < hh; row++) { + memcpy(winbuf + row * win_bytes_per_row, + src0 + row * stride, + win_bytes_per_row); + } + + + + #ifdef EPD_HORIZONTAL_MIRROR + uint16_t x_end = x0 + w_aligned - 1; + uint16_t mx0 = (_width - 1) - x_end; + uint16_t mx1 = (_width - 1) - x0; + + EPD_SET_WINDOW(mx0, yy, mx1, yy + hh - 1); + EPD_PUSH_NEW_COLORS_FLIP(w_aligned, hh, winbuf); + #else + EPD_SET_WINDOW(x0, yy, x0 + w_aligned - 1, yy + hh - 1); + EPD_PUSH_NEW_COLORS(w_aligned, hh, winbuf); + #endif + EPD_UPDATE_PARTIAL(); + + free(winbuf); + sleep(); +} + +#endif + +void EPaper::update(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t *data) +{ + if (_sleep) + { + EPD_WAKEUP(); + _sleep = false; + } + uint8_t *p = (uint8_t *)data; + // only support x, y multiple of 8 (floor) + x = (x / 8) * 8; + y = (y / 8) * 8; + + + if(_bpp == 4) + pushImage(x, y, w, h / 2 , (uint16_t *)p); + else + pushImage(x, y, w, h , (uint16_t *)p); +#ifdef EPD_HORIZONTAL_MIRROR + EPD_PUSH_NEW_COLORS_FLIP(w, h, p); +#else + EPD_PUSH_NEW_COLORS(w, h, p); +#endif + EPD_UPDATE(); + sleep(); +} + +#ifdef USE_MUTIGRAY_EPAPER +void EPaper::initGrayMode(uint8_t grayLevel) +{ + if (grayLevel != 4 && grayLevel != 16) { + return; + } + if (grayLevel == _grayLevel) { + return; + } + else + { + _grayLevel = grayLevel; + } + if (_created) { + deleteSprite(); + } + setColorDepth(4); + createSprite(_width, _height, 1); + fillSprite(grayLevel - 1); + setTextColor(TFT_GRAY_0, grayLevel - 1, true); +} + + +void EPaper::deinitGrayMode() +{ + if (_bpp == EPD_COLOR_DEPTH) return; + + if (_created) { + deleteSprite(); + _grayLevel = 0; + } + setColorDepth(EPD_COLOR_DEPTH); + createSprite(_width, _height, 1); + fillSprite(TFT_WHITE); + setTextColor(TFT_BLACK, TFT_WHITE, true); +} +#endif + + +void EPaper::sleep() +{ + if (_sleep) + return; + EPD_SLEEP(); + _sleep = true; +} + +void EPaper::wake() +{ + if (!_sleep) + return; + if(_entemp) + EPD_SET_TEMP(_temp); + if(!_grayLevel) + EPD_WAKEUP(); + else + { + #ifdef USE_MUTIGRAY_EPAPER + EPD_WAKEUP_GRAY(); + #endif + } + _sleep = false; + +} + +void EPaper::setTemp(GetTempCallback callback) +{ + _temp = callback(); + EPD_SET_TEMP(_temp); +} +float EPaper::getTemp() +{ + return _temp; +} +void EPaper::setHumi(GetHumiCallback callback) +{ + _humi = callback(); +} +float EPaper::getHumi() +{ + return _humi; +} diff --git a/lib/Seeed_GFX/Extensions/EPaper.h b/lib/Seeed_GFX/Extensions/EPaper.h new file mode 100644 index 0000000..5f5e014 --- /dev/null +++ b/lib/Seeed_GFX/Extensions/EPaper.h @@ -0,0 +1,45 @@ +#include "TFT_eSPI.h" + +class EPaper : public TFT_eSprite +{ +public: + explicit EPaper(); + + void begin(uint8_t wake = 0); + void drawBufferPixel(int32_t x, int32_t y, uint32_t color, uint8_t bpp); + void update(); + void update(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t *data); + void updataPartial(uint16_t x, uint16_t y, uint16_t w, uint16_t h); + +#ifdef USE_MUTIGRAY_EPAPER + void initGrayMode(uint8_t grayLevel); + void deinitGrayMode(); +#endif + void sleep(); + void wake(); + + typedef float (*GetTempCallback)(); + typedef float (*GetHumiCallback)(); + void setTemp(GetTempCallback callback); + float getTemp(); + void setHumi(GetHumiCallback callback); + float getHumi(); + + +private: + uint8_t _grayLevel; + bool _sleep; + bool _entemp; + float _temp; + float _humi; + + typedef struct + { + uint16_t x1; + uint16_t x2; + uint16_t y1; + uint16_t y2; + } freshArea_t; + freshArea_t _freshArea; +}; + diff --git a/lib/Seeed_GFX/Extensions/Smooth_font.cpp b/lib/Seeed_GFX/Extensions/Smooth_font.cpp new file mode 100644 index 0000000..c67204a --- /dev/null +++ b/lib/Seeed_GFX/Extensions/Smooth_font.cpp @@ -0,0 +1,582 @@ + // Coded by Bodmer 10/2/18, see license in root directory. + // This is part of the TFT_eSPI class and is associated with anti-aliased font functions + + +//////////////////////////////////////////////////////////////////////////////////////// +// New anti-aliased (smoothed) font functions added below +//////////////////////////////////////////////////////////////////////////////////////// + +/*************************************************************************************** +** Function name: loadFont +** Description: loads parameters from a font vlw array in memory +*************************************************************************************x*/ +void TFT_eSPI::loadFont(const uint8_t array[]) +{ + if (array == nullptr) return; + fontPtr = (uint8_t*) array; + loadFont("", false); +} + +#ifdef FONT_FS_AVAILABLE +/*************************************************************************************** +** Function name: loadFont +** Description: loads parameters from a font vlw file +*************************************************************************************x*/ +void TFT_eSPI::loadFont(String fontName, fs::FS &ffs) +{ + fontFS = ffs; + loadFont(fontName, false); +} +#endif + +/*************************************************************************************** +** Function name: loadFont +** Description: loads parameters from a font vlw file +*************************************************************************************x*/ +void TFT_eSPI::loadFont(String fontName, bool flash) +{ + /* + The vlw font format does not appear to be documented anywhere, so some reverse + engineering has been applied! + + Header of vlw file comprises 6 uint32_t parameters (24 bytes total): + 1. The gCount (number of character glyphs) + 2. A version number (0xB = 11 for the one I am using) + 3. The font size (in points, not pixels) + 4. Deprecated mboxY parameter (typically set to 0) + 5. Ascent in pixels from baseline to top of "d" + 6. Descent in pixels from baseline to bottom of "p" + + Next are gCount sets of values for each glyph, each set comprises 7 int32t parameters (28 bytes): + 1. Glyph Unicode stored as a 32-bit value + 2. Height of bitmap bounding box + 3. Width of bitmap bounding box + 4. gxAdvance for cursor (setWidth in Processing) + 5. dY = distance from cursor baseline to top of glyph bitmap (signed value +ve = up) + 6. dX = distance from cursor to left side of glyph bitmap (signed value -ve = left) + 7. padding value, typically 0 + + The bitmaps start next at 24 + (28 * gCount) bytes from the start of the file. + Each pixel is 1 byte, an 8-bit Alpha value which represents the transparency from + 0xFF foreground colour, 0x00 background. The library uses a linear interpolation + between the foreground and background RGB component colours. e.g. + pixelRed = ((fgRed * alpha) + (bgRed * (255 - alpha))/255 + To gain a performance advantage fixed point arithmetic is used with rounding and + division by 256 (shift right 8 bits is faster). + + After the bitmaps is: + 1 byte for font name string length (excludes null) + a zero terminated character string giving the font name + 1 byte for Postscript name string length + a zero/one terminated character string giving the font name + last byte is 0 for non-anti-aliased and 1 for anti-aliased (smoothed) + + + Glyph bitmap example is: + // Cursor coordinate positions for this and next character are marked by 'C' + // C<------- gxAdvance ------->C gxAdvance is how far to move cursor for next glyph cursor position + // | | + // | | ascent is top of "d", descent is bottom of "p" + // +-- gdX --+ ascent + // | +-- gWidth--+ | gdX is offset to left edge of glyph bitmap + // | + x@.........@x + | gdX may be negative e.g. italic "y" tail extending to left of + // | | @@.........@@ | | cursor position, plot top left corner of bitmap at (cursorX + gdX) + // | | @@.........@@ gdY | gWidth and gHeight are glyph bitmap dimensions + // | | .@@@.....@@@@ | | + // | gHeight ....@@@@@..@@ + + <-- baseline + // | | ...........@@ | + // | | ...........@@ | gdY is the offset to the top edge of the bitmap + // | | .@@.......@@. descent plot top edge of bitmap at (cursorY + ascent - gdY) + // | + x..@@@@@@@..x | x marks the corner pixels of the bitmap + // | | + // +---------------------------+ yAdvance is y delta for the next line, font size or (ascent + descent) + // some fonts can overlay in y direction so may need a user adjust value + + */ + + if (fontLoaded) unloadFont(); + +#ifdef FONT_FS_AVAILABLE + if (fontName == "") fs_font = false; + else { fontPtr = nullptr; fs_font = true; } + + if (fs_font) { + spiffs = flash; // true if font is in SPIFFS + + if(spiffs) fontFS = SPIFFS; + + // Avoid a crash on the ESP32 if the file does not exist + if (fontFS.exists("/" + fontName + ".vlw") == false) { + Serial.println("Font file " + fontName + " not found!"); + return; + } + + fontFile = fontFS.open( "/" + fontName + ".vlw", "r"); + + if(!fontFile) return; + + fontFile.seek(0, fs::SeekSet); + } +#else + // Avoid unused varaible warning + fontName = fontName; + flash = flash; +#endif + + gFont.gArray = (const uint8_t*)fontPtr; + + gFont.gCount = (uint16_t)readInt32(); // glyph count in file + readInt32(); // vlw encoder version - discard + gFont.yAdvance = (uint16_t)readInt32(); // Font size in points, not pixels + readInt32(); // discard + gFont.ascent = (uint16_t)readInt32(); // top of "d" + gFont.descent = (uint16_t)readInt32(); // bottom of "p" + + // These next gFont values might be updated when the Metrics are fetched + gFont.maxAscent = gFont.ascent; // Determined from metrics + gFont.maxDescent = gFont.descent; // Determined from metrics + gFont.yAdvance = gFont.ascent + gFont.descent; + gFont.spaceWidth = gFont.yAdvance / 4; // Guess at space width + + fontLoaded = true; + + // Fetch the metrics for each glyph + loadMetrics(); +} + + +/*************************************************************************************** +** Function name: loadMetrics +** Description: Get the metrics for each glyph and store in RAM +*************************************************************************************x*/ +//#define SHOW_ASCENT_DESCENT +void TFT_eSPI::loadMetrics(void) +{ + uint32_t headerPtr = 24; + uint32_t bitmapPtr = headerPtr + gFont.gCount * 28; + +#if defined (ESP32) && defined (CONFIG_SPIRAM_SUPPORT) + if ( psramFound() ) + { + gUnicode = (uint16_t*)ps_malloc( gFont.gCount * 2); // Unicode 16-bit Basic Multilingual Plane (0-FFFF) + gHeight = (uint8_t*)ps_malloc( gFont.gCount ); // Height of glyph + gWidth = (uint8_t*)ps_malloc( gFont.gCount ); // Width of glyph + gxAdvance = (uint8_t*)ps_malloc( gFont.gCount ); // xAdvance - to move x cursor + gdY = (int16_t*)ps_malloc( gFont.gCount * 2); // offset from bitmap top edge from lowest point in any character + gdX = (int8_t*)ps_malloc( gFont.gCount ); // offset for bitmap left edge relative to cursor X + gBitmap = (uint32_t*)ps_malloc( gFont.gCount * 4); // seek pointer to glyph bitmap in the file + } + else +#endif + { + gUnicode = (uint16_t*)malloc( gFont.gCount * 2); // Unicode 16-bit Basic Multilingual Plane (0-FFFF) + gHeight = (uint8_t*)malloc( gFont.gCount ); // Height of glyph + gWidth = (uint8_t*)malloc( gFont.gCount ); // Width of glyph + gxAdvance = (uint8_t*)malloc( gFont.gCount ); // xAdvance - to move x cursor + gdY = (int16_t*)malloc( gFont.gCount * 2); // offset from bitmap top edge from lowest point in any character + gdX = (int8_t*)malloc( gFont.gCount ); // offset for bitmap left edge relative to cursor X + gBitmap = (uint32_t*)malloc( gFont.gCount * 4); // seek pointer to glyph bitmap in the file + } + +#ifdef SHOW_ASCENT_DESCENT + Serial.print("ascent = "); Serial.println(gFont.ascent); + Serial.print("descent = "); Serial.println(gFont.descent); +#endif + +#ifdef FONT_FS_AVAILABLE + if (fs_font) fontFile.seek(headerPtr, fs::SeekSet); +#endif + + uint16_t gNum = 0; + + while (gNum < gFont.gCount) + { + gUnicode[gNum] = (uint16_t)readInt32(); // Unicode code point value + gHeight[gNum] = (uint8_t)readInt32(); // Height of glyph + gWidth[gNum] = (uint8_t)readInt32(); // Width of glyph + gxAdvance[gNum] = (uint8_t)readInt32(); // xAdvance - to move x cursor + gdY[gNum] = (int16_t)readInt32(); // y delta from baseline + gdX[gNum] = (int8_t)readInt32(); // x delta from cursor + readInt32(); // ignored + + //Serial.print("Unicode = 0x"); Serial.print(gUnicode[gNum], HEX); Serial.print(", gHeight = "); Serial.println(gHeight[gNum]); + //Serial.print("Unicode = 0x"); Serial.print(gUnicode[gNum], HEX); Serial.print(", gWidth = "); Serial.println(gWidth[gNum]); + //Serial.print("Unicode = 0x"); Serial.print(gUnicode[gNum], HEX); Serial.print(", gxAdvance = "); Serial.println(gxAdvance[gNum]); + //Serial.print("Unicode = 0x"); Serial.print(gUnicode[gNum], HEX); Serial.print(", gdY = "); Serial.println(gdY[gNum]); + + // Different glyph sets have different ascent values not always based on "d", so we could get + // the maximum glyph ascent by checking all characters. BUT this method can generate bad values + // for non-existent glyphs, so we will reply on processing for the value and disable this code for now... + /* + if (gdY[gNum] > gFont.maxAscent) + { + // Try to avoid UTF coding values and characters that tend to give duff values + if (((gUnicode[gNum] > 0x20) && (gUnicode[gNum] < 0x7F)) || (gUnicode[gNum] > 0xA0)) + { + gFont.maxAscent = gdY[gNum]; +#ifdef SHOW_ASCENT_DESCENT + Serial.print("Unicode = 0x"); Serial.print(gUnicode[gNum], HEX); Serial.print(", maxAscent = "); Serial.println(gFont.maxAscent); +#endif + } + } + */ + + // Different glyph sets have different descent values not always based on "p", so get maximum glyph descent + if (((int16_t)gHeight[gNum] - (int16_t)gdY[gNum]) > gFont.maxDescent) + { + // Avoid UTF coding values and characters that tend to give duff values + if (((gUnicode[gNum] > 0x20) && (gUnicode[gNum] < 0xA0) && (gUnicode[gNum] != 0x7F)) || (gUnicode[gNum] > 0xFF)) + { + gFont.maxDescent = gHeight[gNum] - gdY[gNum]; +#ifdef SHOW_ASCENT_DESCENT + Serial.print("Unicode = 0x"); Serial.print(gUnicode[gNum], HEX); Serial.print(", maxDescent = "); Serial.println(gHeight[gNum] - gdY[gNum]); +#endif + } + } + + gBitmap[gNum] = bitmapPtr; + + bitmapPtr += gWidth[gNum] * gHeight[gNum]; + + gNum++; + yield(); + } + + gFont.yAdvance = gFont.maxAscent + gFont.maxDescent; + + gFont.spaceWidth = (gFont.ascent + gFont.descent) * 2/7; // Guess at space width +} + + +/*************************************************************************************** +** Function name: deleteMetrics +** Description: Delete the old glyph metrics and free up the memory +*************************************************************************************x*/ +void TFT_eSPI::unloadFont( void ) +{ + if (gUnicode) + { + free(gUnicode); + gUnicode = NULL; + } + + if (gHeight) + { + free(gHeight); + gHeight = NULL; + } + + if (gWidth) + { + free(gWidth); + gWidth = NULL; + } + + if (gxAdvance) + { + free(gxAdvance); + gxAdvance = NULL; + } + + if (gdY) + { + free(gdY); + gdY = NULL; + } + + if (gdX) + { + free(gdX); + gdX = NULL; + } + + if (gBitmap) + { + free(gBitmap); + gBitmap = NULL; + } + + gFont.gArray = nullptr; + +#ifdef FONT_FS_AVAILABLE + if (fs_font && fontFile) fontFile.close(); +#endif + + fontLoaded = false; +} + + +/*************************************************************************************** +** Function name: readInt32 +** Description: Get a 32-bit integer from the font file +*************************************************************************************x*/ +uint32_t TFT_eSPI::readInt32(void) +{ + uint32_t val = 0; + +#ifdef FONT_FS_AVAILABLE + if (fs_font) { + val = (uint32_t)fontFile.read() << 24; + val |= (uint32_t)fontFile.read() << 16; + val |= (uint32_t)fontFile.read() << 8; + val |= (uint32_t)fontFile.read(); + } + else +#endif + { + val = (uint32_t)pgm_read_byte(fontPtr++) << 24; + val |= (uint32_t)pgm_read_byte(fontPtr++) << 16; + val |= (uint32_t)pgm_read_byte(fontPtr++) << 8; + val |= (uint32_t)pgm_read_byte(fontPtr++); + } + + return val; +} + + +/*************************************************************************************** +** Function name: getUnicodeIndex +** Description: Get the font file index of a Unicode character +*************************************************************************************x*/ +bool TFT_eSPI::getUnicodeIndex(uint16_t unicode, uint16_t *index) +{ + for (uint16_t i = 0; i < gFont.gCount; i++) + { + if (gUnicode[i] == unicode) + { + *index = i; + return true; + } + } + return false; +} + + +/*************************************************************************************** +** Function name: drawGlyph +** Description: Write a character to the TFT cursor position +*************************************************************************************x*/ +// Expects file to be open +void TFT_eSPI::drawGlyph(uint16_t code) +{ + uint16_t fg = textcolor; + uint16_t bg = textbgcolor; + + // Check if cursor has moved + if (last_cursor_x != cursor_x) + { + bg_cursor_x = cursor_x; + last_cursor_x = cursor_x; + } + + if (code < 0x21) + { + if (code == 0x20) { + if (_fillbg) fillRect(bg_cursor_x, cursor_y, (cursor_x + gFont.spaceWidth) - bg_cursor_x, gFont.yAdvance, bg); + cursor_x += gFont.spaceWidth; + bg_cursor_x = cursor_x; + last_cursor_x = cursor_x; + return; + } + + if (code == '\n') { + cursor_x = 0; + bg_cursor_x = 0; + last_cursor_x = 0; + cursor_y += gFont.yAdvance; + if (textwrapY && (cursor_y >= height())) cursor_y = 0; + return; + } + } + + uint16_t gNum = 0; + bool found = getUnicodeIndex(code, &gNum); + + if (found) + { + + if (textwrapX && (cursor_x + gWidth[gNum] + gdX[gNum] > width())) + { + cursor_y += gFont.yAdvance; + cursor_x = 0; + bg_cursor_x = 0; + } + if (textwrapY && ((cursor_y + gFont.yAdvance) >= height())) cursor_y = 0; + if (cursor_x == 0) cursor_x -= gdX[gNum]; + + uint8_t* pbuffer = nullptr; + const uint8_t* gPtr = (const uint8_t*) gFont.gArray; + +#ifdef FONT_FS_AVAILABLE + if (fs_font) + { + fontFile.seek(gBitmap[gNum], fs::SeekSet); + pbuffer = (uint8_t*)malloc(gWidth[gNum]); + } +#endif + + int16_t cy = cursor_y + gFont.maxAscent - gdY[gNum]; + int16_t cx = cursor_x + gdX[gNum]; + + // if (cx > width() && bg_cursor_x > width()) return; + // if (cursor_y > height()) return; + + int16_t fxs = cx; + uint32_t fl = 0; + int16_t bxs = cx; + uint32_t bl = 0; + int16_t bx = 0; + uint8_t pixel; + + startWrite(); // Avoid slow ESP32 transaction overhead for every pixel + + int16_t fillwidth = 0; + int16_t fillheight = 0; + + // Fill area above glyph + if (_fillbg) { + fillwidth = (cursor_x + gxAdvance[gNum]) - bg_cursor_x; + if (fillwidth > 0) { + fillheight = gFont.maxAscent - gdY[gNum]; + // Could be negative + if (fillheight > 0) { + fillRect(bg_cursor_x, cursor_y, fillwidth, fillheight, textbgcolor); + } + } + else { + // Could be negative + fillwidth = 0; + } + + // Fill any area to left of glyph + if (bg_cursor_x < cx) fillRect(bg_cursor_x, cy, cx - bg_cursor_x, gHeight[gNum], textbgcolor); + // Set x position in glyph area where background starts + if (bg_cursor_x > cx) bx = bg_cursor_x - cx; + // Fill any area to right of glyph + if (cx + gWidth[gNum] < cursor_x + gxAdvance[gNum]) { + fillRect(cx + gWidth[gNum], cy, (cursor_x + gxAdvance[gNum]) - (cx + gWidth[gNum]), gHeight[gNum], textbgcolor); + } + } + + for (int32_t y = 0; y < gHeight[gNum]; y++) + { +#ifdef FONT_FS_AVAILABLE + if (fs_font) { + if (spiffs) + { + fontFile.read(pbuffer, gWidth[gNum]); + //Serial.println("SPIFFS"); + } + else + { + endWrite(); // Release SPI for SD card transaction + fontFile.read(pbuffer, gWidth[gNum]); + startWrite(); // Re-start SPI for TFT transaction + //Serial.println("Not SPIFFS"); + } + } +#endif + + for (int32_t x = 0; x < gWidth[gNum]; x++) + { +#ifdef FONT_FS_AVAILABLE + if (fs_font) pixel = pbuffer[x]; + else +#endif + pixel = pgm_read_byte(gPtr + gBitmap[gNum] + x + gWidth[gNum] * y); + + if (pixel) + { + if (bl) { drawFastHLine( bxs, y + cy, bl, bg); bl = 0; } + if (pixel != 0xFF) + { + if (fl) { + if (fl==1) drawPixel(fxs, y + cy, fg); + else drawFastHLine( fxs, y + cy, fl, fg); + fl = 0; + } + if (getColor) bg = getColor(x + cx, y + cy); + drawPixel(x + cx, y + cy, alphaBlend(pixel, fg, bg)); + } + else + { + if (fl==0) fxs = x + cx; + fl++; + } + } + else + { + if (fl) { drawFastHLine( fxs, y + cy, fl, fg); fl = 0; } + if (_fillbg) { + if (x >= bx) { + if (bl==0) bxs = x + cx; + bl++; + } + } + } + } + if (fl) { drawFastHLine( fxs, y + cy, fl, fg); fl = 0; } + if (bl) { drawFastHLine( bxs, y + cy, bl, bg); bl = 0; } + } + + // Fill area below glyph + if (fillwidth > 0) { + fillheight = (cursor_y + gFont.yAdvance) - (cy + gHeight[gNum]); + if (fillheight > 0) { + fillRect(bg_cursor_x, cy + gHeight[gNum], fillwidth, fillheight, textbgcolor); + } + } + + if (pbuffer) free(pbuffer); + cursor_x += gxAdvance[gNum]; + endWrite(); + } + else + { + // Point code not in font so draw a rectangle and move on cursor + drawRect(cursor_x, cursor_y + gFont.maxAscent - gFont.ascent, gFont.spaceWidth, gFont.ascent, fg); + cursor_x += gFont.spaceWidth + 1; + } + bg_cursor_x = cursor_x; + last_cursor_x = cursor_x; +} + +/*************************************************************************************** +** Function name: showFont +** Description: Page through all characters in font, td ms between screens +*************************************************************************************x*/ +void TFT_eSPI::showFont(uint32_t td) +{ + if(!fontLoaded) return; + + int16_t cursorX = width(); // Force start of new page to initialise cursor + int16_t cursorY = height();// for the first character + uint32_t timeDelay = 0; // No delay before first page + + fillScreen(textbgcolor); + + for (uint16_t i = 0; i < gFont.gCount; i++) + { + // Check if this will need a new screen + if (cursorX + gdX[i] + gWidth[i] >= width()) { + cursorX = -gdX[i]; + + cursorY += gFont.yAdvance; + if (cursorY + gFont.maxAscent + gFont.descent >= height()) { + cursorX = -gdX[i]; + cursorY = 0; + delay(timeDelay); + timeDelay = td; + fillScreen(textbgcolor); + } + } + + setCursor(cursorX, cursorY); + drawGlyph(gUnicode[i]); + cursorX += gxAdvance[i]; + yield(); + } + + delay(timeDelay); + fillScreen(textbgcolor); +} diff --git a/lib/Seeed_GFX/Extensions/Smooth_font.h b/lib/Seeed_GFX/Extensions/Smooth_font.h new file mode 100644 index 0000000..c31dcad --- /dev/null +++ b/lib/Seeed_GFX/Extensions/Smooth_font.h @@ -0,0 +1,61 @@ + // Coded by Bodmer 10/2/18, see license in root directory. + // This is part of the TFT_eSPI class and is associated with anti-aliased font functions + + public: + + // These are for the new anti-aliased fonts + void loadFont(const uint8_t array[]); +#ifdef FONT_FS_AVAILABLE + void loadFont(String fontName, fs::FS &ffs); +#endif + void loadFont(String fontName, bool flash = true); + void unloadFont( void ); + bool getUnicodeIndex(uint16_t unicode, uint16_t *index); + + virtual void drawGlyph(uint16_t code); + + void showFont(uint32_t td); + + // This is for the whole font + typedef struct + { + const uint8_t* gArray; //array start pointer + uint16_t gCount; // Total number of characters + uint16_t yAdvance; // Line advance + uint16_t spaceWidth; // Width of a space character + int16_t ascent; // Height of top of 'd' above baseline, other characters may be taller + int16_t descent; // Offset to bottom of 'p', other characters may have a larger descent + uint16_t maxAscent; // Maximum ascent found in font + uint16_t maxDescent; // Maximum descent found in font + } fontMetrics; + +fontMetrics gFont = { nullptr, 0, 0, 0, 0, 0, 0, 0 }; + + // These are for the metrics for each individual glyph (so we don't need to seek this in file and waste time) + uint16_t* gUnicode = NULL; //UTF-16 code, the codes are searched so do not need to be sequential + uint8_t* gHeight = NULL; //cheight + uint8_t* gWidth = NULL; //cwidth + uint8_t* gxAdvance = NULL; //setWidth + int16_t* gdY = NULL; //topExtent + int8_t* gdX = NULL; //leftExtent + uint32_t* gBitmap = NULL; //file pointer to greyscale bitmap + + bool fontLoaded = false; // Flags when a anti-aliased font is loaded + +#ifdef FONT_FS_AVAILABLE + fs::File fontFile; + fs::FS &fontFS = SPIFFS; + bool spiffs = true; + bool fs_font = false; // For ESP32/8266 use smooth font file or FLASH (PROGMEM) array + +#else + bool fontFile = true; +#endif + + private: + + void loadMetrics(void); + uint32_t readInt32(void); + + uint8_t* fontPtr = nullptr; + diff --git a/lib/Seeed_GFX/Extensions/Sprite.cpp b/lib/Seeed_GFX/Extensions/Sprite.cpp new file mode 100644 index 0000000..181388c --- /dev/null +++ b/lib/Seeed_GFX/Extensions/Sprite.cpp @@ -0,0 +1,2777 @@ +/************************************************************************************** +// The following class creates Sprites in RAM, graphics can then be drawn in the Sprite +// and rendered quickly onto the TFT screen. The class inherits the graphics functions +// from the TFT_eSPI class. Some functions are overridden by this class so that the +// graphics are written to the Sprite rather than the TFT. +// Coded by Bodmer, see license file in root folder +***************************************************************************************/ +/*************************************************************************************** +// Color bytes are swapped when writing to RAM, this introduces a small overhead but +// there is a nett performance gain by using swapped bytes. +***************************************************************************************/ + +/*************************************************************************************** +** Function name: TFT_eSprite +** Description: Class constructor +***************************************************************************************/ +TFT_eSprite::TFT_eSprite(TFT_eSPI *tft) +{ + _tft = tft; // Pointer to tft class so we can call member functions + + _iwidth = 0; // Initialise width and height to 0 (it does not exist yet) + _iheight = 0; + _bpp = 16; + _swapBytes = false; // Do not swap pushImage colour bytes by default + + _created = false; + _vpOoB = true; + + _xs = 0; // window bounds for pushColor + _ys = 0; + _xe = 0; + _ye = 0; + + _xptr = 0; // pushColor coordinate + _yptr = 0; + + _colorMap = nullptr; + + _psram_enable = true; + + // Ensure end_tft_write() does nothing in inherited functions. + lockTransaction = true; +} + + +/*************************************************************************************** +** Function name: createSprite +** Description: Create a sprite (bitmap) of defined width and height +***************************************************************************************/ +// cast returned value to (uint8_t*) for 8-bit or (uint16_t*) for 16-bit colours +void* TFT_eSprite::createSprite(int16_t w, int16_t h, uint8_t frames) +{ + + if ( _created ) return _img8_1; + + if ( w < 1 || h < 1 ) return nullptr; + + _iwidth = _dwidth = _bitwidth = w; + _iheight = _dheight = h; + + cursor_x = 0; + cursor_y = 0; + + // Default scroll rectangle and gap fill colour + _sx = 0; + _sy = 0; + _sw = w; + _sh = h; + _scolor = TFT_BLACK; + + _img8 = (uint8_t*) callocSprite(w, h, frames); + _img8_1 = _img8; + _img8_2 = _img8; + _img = (uint16_t*) _img8; + _img4 = _img8; + + if ( (_bpp == 16) && (frames > 1) ) { + _img8_2 = _img8 + (w * h * 2 + 1); + } + + // ESP32 only 16bpp check + //if (esp_ptr_dma_capable(_img8_1)) Serial.println("DMA capable Sprite pointer _img8_1"); + //else Serial.println("Not a DMA capable Sprite pointer _img8_1"); + //if (esp_ptr_dma_capable(_img8_2)) Serial.println("DMA capable Sprite pointer _img8_2"); + //else Serial.println("Not a DMA capable Sprite pointer _img8_2"); + + if ( (_bpp == 8) && (frames > 1) ) { + _img8_2 = _img8 + (w * h + 1); + } + + // This is to make it clear what pointer size is expected to be used + // but casting in the user sketch is needed due to the use of void* + if ( (_bpp == 1) && (frames > 1) ) + { + w = (w+7) & 0xFFF8; + _img8_2 = _img8 + ( (w>>3) * h + 1 ); + } + + if (_img8) + { + _created = true; + if ( (_bpp == 4) && (_colorMap == nullptr)) createPalette(default_4bit_palette); + + rotation = 0; + setViewport(0, 0, _dwidth, _dheight); + setPivot(_iwidth/2, _iheight/2); + return _img8_1; + } + + return nullptr; +} + + +/*************************************************************************************** +** Function name: getPointer +** Description: Returns pointer to start of sprite memory area +***************************************************************************************/ +void* TFT_eSprite::getPointer(void) +{ + if (!_created) return nullptr; + return _img8_1; +} + + +/*************************************************************************************** +** Function name: created +** Description: Returns true if sprite has been created +***************************************************************************************/ +bool TFT_eSprite::created(void) +{ + return _created; +} + + +/*************************************************************************************** +** Function name: ~TFT_eSprite +** Description: Class destructor +***************************************************************************************/ +TFT_eSprite::~TFT_eSprite(void) +{ + deleteSprite(); + +#ifdef SMOOTH_FONT + if(fontLoaded) unloadFont(); +#endif +} + + +/*************************************************************************************** +** Function name: callocSprite +** Description: Allocate a memory area for the Sprite and return pointer +***************************************************************************************/ +void* TFT_eSprite::callocSprite(int16_t w, int16_t h, uint8_t frames) +{ + // Add one extra "off screen" pixel to point out-of-bounds setWindow() coordinates + // this means push/writeColor functions do not need additional bounds checks and + // hence will run faster in normal circumstances. + uint8_t* ptr8 = nullptr; + + if (frames > 2) frames = 2; // Currently restricted to 2 frame buffers + if (frames < 1) frames = 1; + + if (_bpp == 16) + { +#if defined (ESP32) && defined (CONFIG_SPIRAM_SUPPORT) + if ( psramFound() && _psram_enable && !_tft->DMA_Enabled) + { + ptr8 = ( uint8_t*) ps_calloc(frames * w * h + frames, sizeof(uint16_t)); + //Serial.println("PSRAM"); + } + else +#endif + { + ptr8 = ( uint8_t*) calloc(frames * w * h + frames, sizeof(uint16_t)); + //Serial.println("Normal RAM"); + } + } + + else if (_bpp == 8) + { +#if defined (ESP32) && defined (CONFIG_SPIRAM_SUPPORT) + if ( psramFound() && _psram_enable ) ptr8 = ( uint8_t*) ps_calloc(frames * w * h + frames, sizeof(uint8_t)); + else +#endif + ptr8 = ( uint8_t*) calloc(frames * w * h + frames, sizeof(uint8_t)); + } + + else if (_bpp == 4) + { + w = (w+1) & 0xFFFE; // width needs to be multiple of 2, with an extra "off screen" pixel + _iwidth = w; +#if defined (ESP32) && defined (CONFIG_SPIRAM_SUPPORT) + if ( psramFound() && _psram_enable ) ptr8 = ( uint8_t*) ps_calloc(((frames * w * h) >> 1) + frames, sizeof(uint8_t)); + else +#endif + ptr8 = ( uint8_t*) calloc(((frames * w * h) >> 1) + frames, sizeof(uint8_t)); + } + + else // Must be 1 bpp + { + //_dwidth Display width+height in pixels always in rotation 0 orientation + //_dheight Not swapped for sprite rotations + // Note: for 1bpp _iwidth and _iheight are swapped during Sprite rotations + + w = (w+7) & 0xFFF8; // width should be the multiple of 8 bits to be compatible with epdpaint + _iwidth = w; // _iwidth is rounded up to be multiple of 8, so might not be = _dwidth + _bitwidth = w; // _bitwidth will not be rotated whereas _iwidth may be + +#if defined (ESP32) && defined (CONFIG_SPIRAM_SUPPORT) + if ( psramFound() && _psram_enable ) ptr8 = ( uint8_t*) ps_calloc(frames * (w>>3) * h + frames, sizeof(uint8_t)); + else +#endif + ptr8 = ( uint8_t*) calloc(frames * (w>>3) * h + frames, sizeof(uint8_t)); + } + + return ptr8; +} + + +/*************************************************************************************** +** Function name: createPalette (from RAM array) +** Description: Set a palette for a 4-bit per pixel sprite +***************************************************************************************/ +void TFT_eSprite::createPalette(uint16_t colorMap[], uint8_t colors) +{ + if (!_created) return; + + if (colorMap == nullptr) + { + // Create a color map using the default FLASH map + createPalette(default_4bit_palette); + return; + } + + // Allocate and clear memory for 16 color map + if (_colorMap == nullptr) _colorMap = (uint16_t *)calloc(16, sizeof(uint16_t)); + + if (colors > 16) colors = 16; + + // Copy map colors + for (uint8_t i = 0; i < colors; i++) + { + _colorMap[i] = colorMap[i]; + } +} + + +/*************************************************************************************** +** Function name: createPalette (from FLASH array) +** Description: Set a palette for a 4-bit per pixel sprite +***************************************************************************************/ +void TFT_eSprite::createPalette(const uint16_t colorMap[], uint8_t colors) +{ + if (!_created) return; + + if (colorMap == nullptr) + { + // Create a color map using the default FLASH map + colorMap = default_4bit_palette; + } + + // Allocate and clear memory for 16 color map + if (_colorMap == nullptr) _colorMap = (uint16_t *)calloc(16, sizeof(uint16_t)); + + if (colors > 16) colors = 16; + + // Copy map colors + for (uint8_t i = 0; i < colors; i++) + { + _colorMap[i] = pgm_read_word(colorMap++); + } +} + + +/*************************************************************************************** +** Function name: frameBuffer +** Description: For 1 bpp Sprites, select the frame used for graphics +***************************************************************************************/ +// Frames are numbered 1 and 2 +void* TFT_eSprite::frameBuffer(int8_t f) +{ + if (!_created) return nullptr; + + if ( f == 2 ) _img8 = _img8_2; + else _img8 = _img8_1; + + if (_bpp == 16) _img = (uint16_t*)_img8; + + //if (_bpp == 8) _img8 = _img8; + + if (_bpp == 4) _img4 = _img8; + + return _img8; +} + + +/*************************************************************************************** +** Function name: setColorDepth +** Description: Set bits per pixel for colour (1, 8 or 16) +***************************************************************************************/ +void* TFT_eSprite::setColorDepth(int8_t b) +{ + // Do not re-create the sprite if the colour depth does not change + if (_bpp == b) return _img8_1; + + // Validate the new colour depth + if ( b > 8 ) _bpp = 16; // Bytes per pixel + else if ( b > 4 ) _bpp = 8; + else if ( b > 1 ) _bpp = 4; + else _bpp = 1; + + // Can't change an existing sprite's colour depth so delete and create a new one + if (_created) { + deleteSprite(); + return createSprite(_dwidth, _dheight); + } + + return nullptr; +} + + +/*************************************************************************************** +** Function name: getColorDepth +** Description: Get bits per pixel for colour (1, 8 or 16) +***************************************************************************************/ +int8_t TFT_eSprite::getColorDepth(void) +{ + if (_created) return _bpp; + else return 0; +} + + +/*************************************************************************************** +** Function name: setBitmapColor +** Description: Set the 1bpp foreground foreground and background colour +***************************************************************************************/ +void TFT_eSprite::setBitmapColor(uint16_t c, uint16_t b) +{ + if (c == b) b = ~c; + _tft->bitmap_fg = c; + _tft->bitmap_bg = b; +} + + +/*************************************************************************************** +** Function name: setPaletteColor +** Description: Set the 4bpp palette color at the given index +***************************************************************************************/ +void TFT_eSprite::setPaletteColor(uint8_t index, uint16_t color) +{ + if (_colorMap == nullptr || index > 15) return; // out of bounds + + _colorMap[index] = color; +} + + +/*************************************************************************************** +** Function name: getPaletteColor +** Description: Return the palette color at 4bpp index, or 0 on error. +***************************************************************************************/ +uint16_t TFT_eSprite::getPaletteColor(uint8_t index) +{ + if (_colorMap == nullptr || index > 15) return 0; // out of bounds + + return _colorMap[index]; +} + + +/*************************************************************************************** +** Function name: deleteSprite +** Description: Delete the sprite to free up memory (RAM) +***************************************************************************************/ +void TFT_eSprite::deleteSprite(void) +{ + if (_colorMap != nullptr) + { + free(_colorMap); + _colorMap = nullptr; + } + + if (_created) + { + free(_img8_1); + _img8 = nullptr; + _created = false; + _vpOoB = true; // TFT_eSPI class write() uses this to check for valid sprite + } +} + + +/*************************************************************************************** +** Function name: pushRotated - Fast fixed point integer maths version +** Description: Push rotated Sprite to TFT screen +***************************************************************************************/ +#define FP_SCALE 10 +bool TFT_eSprite::pushRotated(int16_t angle, uint32_t transp) +{ + if ( !_created || _tft->_vpOoB) return false; + + // Bounding box parameters + int16_t min_x; + int16_t min_y; + int16_t max_x; + int16_t max_y; + + // Get the bounding box of this rotated source Sprite relative to Sprite pivot + if ( !getRotatedBounds(angle, &min_x, &min_y, &max_x, &max_y) ) return false; + + uint16_t sline_buffer[max_x - min_x + 1]; + + int32_t xt = min_x - _tft->_xPivot; + int32_t yt = min_y - _tft->_yPivot; + uint32_t xe = _dwidth << FP_SCALE; + uint32_t ye = _dheight << FP_SCALE; + uint16_t tpcolor = (uint16_t)transp; + + if (transp != 0x00FFFFFF) { + if (_bpp == 4) tpcolor = _colorMap[transp & 0x0F]; + tpcolor = tpcolor>>8 | tpcolor<<8; // Working with swapped color bytes + } + _tft->startWrite(); // Avoid transaction overhead for every tft pixel + + // Scan destination bounding box and fetch transformed pixels from source Sprite + for (int32_t y = min_y; y <= max_y; y++, yt++) { + int32_t x = min_x; + uint32_t xs = (_cosra * xt - (_sinra * yt - (_xPivot << FP_SCALE)) + (1 << (FP_SCALE - 1))); + uint32_t ys = (_sinra * xt + (_cosra * yt + (_yPivot << FP_SCALE)) + (1 << (FP_SCALE - 1))); + + while ((xs >= xe || ys >= ye) && x < max_x) { x++; xs += _cosra; ys += _sinra; } + if (x == max_x) continue; + + uint32_t pixel_count = 0; + do { + uint32_t rp; + int32_t xp = xs >> FP_SCALE; + int32_t yp = ys >> FP_SCALE; + if (_bpp == 16) {rp = _img[xp + yp * _iwidth]; } + else { rp = readPixel(xp, yp); rp = (uint16_t)(rp>>8 | rp<<8); } + if (transp != 0x00FFFFFF && tpcolor == rp) { + if (pixel_count) { + // TFT window is already clipped, so this is faster than pushImage() + _tft->setWindow(x - pixel_count, y, x - 1, y); + _tft->pushPixels(sline_buffer, pixel_count); + pixel_count = 0; + } + } + else { + sline_buffer[pixel_count++] = rp; + } + } while (++x < max_x && (xs += _cosra) < xe && (ys += _sinra) < ye); + if (pixel_count) { + // TFT window is already clipped, so this is faster than pushImage() + _tft->setWindow(x - pixel_count, y, x - 1, y); + _tft->pushPixels(sline_buffer, pixel_count); + } + } + + _tft->endWrite(); // End transaction + + return true; +} + + +/*************************************************************************************** +** Function name: pushRotated - Fast fixed point integer maths version +** Description: Push a rotated copy of the Sprite to another Sprite +***************************************************************************************/ +// Not compatible with 4bpp +bool TFT_eSprite::pushRotated(TFT_eSprite *spr, int16_t angle, uint32_t transp) +{ + if ( !_created || _bpp == 4) return false; // Check this Sprite is created + if ( !spr->_created || spr->_bpp == 4) return false; // Ckeck destination Sprite is created + + // Bounding box parameters + int16_t min_x; + int16_t min_y; + int16_t max_x; + int16_t max_y; + + // Get the bounding box of this rotated source Sprite + if ( !getRotatedBounds(spr, angle, &min_x, &min_y, &max_x, &max_y) ) return false; + + uint16_t sline_buffer[max_x - min_x + 1]; + + int32_t xt = min_x - spr->_xPivot; + int32_t yt = min_y - spr->_yPivot; + uint32_t xe = _dwidth << FP_SCALE; + uint32_t ye = _dheight << FP_SCALE; + uint16_t tpcolor = (uint16_t)transp; + + if (transp != 0x00FFFFFF) { + if (_bpp == 4) tpcolor = _colorMap[transp & 0x0F]; + tpcolor = tpcolor>>8 | tpcolor<<8; // Working with swapped color bytes + } + + bool oldSwapBytes = spr->getSwapBytes(); + spr->setSwapBytes(false); + + // Scan destination bounding box and fetch transformed pixels from source Sprite + for (int32_t y = min_y; y <= max_y; y++, yt++) { + int32_t x = min_x; + uint32_t xs = (_cosra * xt - (_sinra * yt - (_xPivot << FP_SCALE)) + (1 << (FP_SCALE - 1))); + uint32_t ys = (_sinra * xt + (_cosra * yt + (_yPivot << FP_SCALE)) + (1 << (FP_SCALE - 1))); + + while ((xs >= xe || ys >= ye) && x < max_x) { x++; xs += _cosra; ys += _sinra; } + if (x == max_x) continue; + + uint32_t pixel_count = 0; + do { + uint32_t rp; + int32_t xp = xs >> FP_SCALE; + int32_t yp = ys >> FP_SCALE; + if (_bpp == 16) rp = _img[xp + yp * _iwidth]; + else { rp = readPixel(xp, yp); rp = (uint16_t)(rp>>8 | rp<<8); } + if (transp != 0x00FFFFFF && tpcolor == rp) { + if (pixel_count) { + spr->pushImage(x - pixel_count, y, pixel_count, 1, sline_buffer); + pixel_count = 0; + } + } + else { + sline_buffer[pixel_count++] = rp; + } + } while (++x < max_x && (xs += _cosra) < xe && (ys += _sinra) < ye); + if (pixel_count) spr->pushImage(x - pixel_count, y, pixel_count, 1, sline_buffer); + } + spr->setSwapBytes(oldSwapBytes); + return true; +} + + +/*************************************************************************************** +** Function name: getRotatedBounds +** Description: Get TFT bounding box of a rotated Sprite wrt pivot +***************************************************************************************/ +bool TFT_eSprite::getRotatedBounds(int16_t angle, int16_t *min_x, int16_t *min_y, + int16_t *max_x, int16_t *max_y) +{ + // Get the bounding box of this rotated source Sprite relative to Sprite pivot + getRotatedBounds(angle, width(), height(), _xPivot, _yPivot, min_x, min_y, max_x, max_y); + + // Move bounding box so source Sprite pivot coincides with TFT pivot + *min_x += _tft->_xPivot; + *max_x += _tft->_xPivot; + *min_y += _tft->_yPivot; + *max_y += _tft->_yPivot; + + // Return if bounding box is outside of TFT viewport + if (*min_x > _tft->_vpW) return false; + if (*min_y > _tft->_vpH) return false; + if (*max_x < _tft->_vpX) return false; + if (*max_y < _tft->_vpY) return false; + + // Clip bounding box to be within TFT viewport + if (*min_x < _tft->_vpX) *min_x = _tft->_vpX; + if (*min_y < _tft->_vpY) *min_y = _tft->_vpY; + if (*max_x > _tft->_vpW) *max_x = _tft->_vpW; + if (*max_y > _tft->_vpH) *max_y = _tft->_vpH; + + return true; +} + + +/*************************************************************************************** +** Function name: getRotatedBounds +** Description: Get destination Sprite bounding box of a rotated Sprite wrt pivot +***************************************************************************************/ +bool TFT_eSprite::getRotatedBounds(TFT_eSprite *spr, int16_t angle, int16_t *min_x, int16_t *min_y, + int16_t *max_x, int16_t *max_y) +{ + // Get the bounding box of this rotated source Sprite relative to Sprite pivot + getRotatedBounds(angle, width(), height(), _xPivot, _yPivot, min_x, min_y, max_x, max_y); + + // Move bounding box so source Sprite pivot coincides with destination Sprite pivot + *min_x += spr->_xPivot; + *max_x += spr->_xPivot; + *min_y += spr->_yPivot; + *max_y += spr->_yPivot; + + // Test only to show bounding box + // spr->fillSprite(TFT_BLACK); + // spr->drawRect(min_x, min_y, max_x - min_x + 1, max_y - min_y + 1, TFT_GREEN); + + // Return if bounding box is completely outside of destination Sprite + if (*min_x > spr->width()) return true; + if (*min_y > spr->height()) return true; + if (*max_x < 0) return true; + if (*max_y < 0) return true; + + // Clip bounding box to Sprite boundaries + // Clipping to a viewport will be done by destination Sprite pushImage function + if (*min_x < 0) min_x = 0; + if (*min_y < 0) min_y = 0; + if (*max_x > spr->width()) *max_x = spr->width(); + if (*max_y > spr->height()) *max_y = spr->height(); + + return true; +} + + +/*************************************************************************************** +** Function name: rotatedBounds +** Description: Get bounding box of a rotated Sprite wrt pivot +***************************************************************************************/ +void TFT_eSprite::getRotatedBounds(int16_t angle, int16_t w, int16_t h, int16_t xp, int16_t yp, + int16_t *min_x, int16_t *min_y, int16_t *max_x, int16_t *max_y) +{ + // Trig values for the rotation + float radAngle = -angle * 0.0174532925; // Convert degrees to radians + float sina = sin(radAngle); + float cosa = cos(radAngle); + + w -= xp; // w is now right edge coordinate relative to xp + h -= yp; // h is now bottom edge coordinate relative to yp + + // Calculate new corner coordinates + int16_t x0 = -xp * cosa - yp * sina; + int16_t y0 = xp * sina - yp * cosa; + + int16_t x1 = w * cosa - yp * sina; + int16_t y1 = -w * sina - yp * cosa; + + int16_t x2 = h * sina + w * cosa; + int16_t y2 = h * cosa - w * sina; + + int16_t x3 = h * sina - xp * cosa; + int16_t y3 = h * cosa + xp * sina; + + // Find bounding box extremes, enlarge box to accomodate rounding errors + *min_x = x0-2; + if (x1 < *min_x) *min_x = x1-2; + if (x2 < *min_x) *min_x = x2-2; + if (x3 < *min_x) *min_x = x3-2; + + *max_x = x0+2; + if (x1 > *max_x) *max_x = x1+2; + if (x2 > *max_x) *max_x = x2+2; + if (x3 > *max_x) *max_x = x3+2; + + *min_y = y0-2; + if (y1 < *min_y) *min_y = y1-2; + if (y2 < *min_y) *min_y = y2-2; + if (y3 < *min_y) *min_y = y3-2; + + *max_y = y0+2; + if (y1 > *max_y) *max_y = y1+2; + if (y2 > *max_y) *max_y = y2+2; + if (y3 > *max_y) *max_y = y3+2; + + _sinra = round(sina * (1<getSwapBytes(); + _tft->setSwapBytes(false); + _tft->pushImage(x, y, _dwidth, _dheight, _img ); + _tft->setSwapBytes(oldSwapBytes); + } + else if (_bpp == 4) + { + _tft->pushImage(x, y, _dwidth, _dheight, _img4, false, _colorMap); + } + else _tft->pushImage(x, y, _dwidth, _dheight, _img8, (bool)(_bpp == 8)); +} + + +/*************************************************************************************** +** Function name: pushSprite +** Description: Push the sprite to the TFT at x, y with transparent colour +***************************************************************************************/ +void TFT_eSprite::pushSprite(int32_t x, int32_t y, uint16_t transp) +{ + if (!_created) return; + + if (_bpp == 16) + { + bool oldSwapBytes = _tft->getSwapBytes(); + _tft->setSwapBytes(false); + _tft->pushImage(x, y, _dwidth, _dheight, _img, transp ); + _tft->setSwapBytes(oldSwapBytes); + } + else if (_bpp == 8) + { + transp = (uint8_t)((transp & 0xE000)>>8 | (transp & 0x0700)>>6 | (transp & 0x0018)>>3); + _tft->pushImage(x, y, _dwidth, _dheight, _img8, (uint8_t)transp, (bool)true); + } + else if (_bpp == 4) + { + _tft->pushImage(x, y, _dwidth, _dheight, _img4, (uint8_t)(transp & 0x0F), false, _colorMap); + } + else _tft->pushImage(x, y, _dwidth, _dheight, _img8, 0, (bool)false); +} + + +/*************************************************************************************** +** Function name: pushToSprite +** Description: Push the sprite to another sprite at x, y +***************************************************************************************/ +// Note: The following sprite to sprite colour depths are currently supported: +// Source Destination +// 16bpp -> 16bpp +// 16bpp -> 8bpp +// 8bpp -> 8bpp +// 4bpp -> 4bpp (note: color translation depends on the 2 sprites palette colors) +// 1bpp -> 1bpp (note: color translation depends on the 2 sprites bitmap colors) + +bool TFT_eSprite::pushToSprite(TFT_eSprite *dspr, int32_t x, int32_t y) +{ + if (!_created) return false; + if (!dspr->created()) return false; + + // Check destination sprite compatibility + int8_t ds_bpp = dspr->getColorDepth(); + if (_bpp == 16 && ds_bpp != 16 && ds_bpp != 8) return false; + if (_bpp == 8 && ds_bpp != 8) return false; + if (_bpp == 4 && ds_bpp != 4) return false; + if (_bpp == 1 && ds_bpp != 1) return false; + + bool oldSwapBytes = dspr->getSwapBytes(); + dspr->setSwapBytes(false); + dspr->pushImage(x, y, _dwidth, _dheight, _img, _bpp); + dspr->setSwapBytes(oldSwapBytes); + + return true; +} + + +/*************************************************************************************** +** Function name: pushToSprite +** Description: Push the sprite to another sprite at x, y with transparent colour +***************************************************************************************/ +// Note: The following sprite to sprite colour depths are currently supported: +// Source Destination +// 16bpp -> 16bpp +// 16bpp -> 8bpp +// 8bpp -> 8bpp +// 1bpp -> 1bpp + +bool TFT_eSprite::pushToSprite(TFT_eSprite *dspr, int32_t x, int32_t y, uint16_t transp) +{ + if ( !_created || !dspr->_created) return false; // Check Sprites exist + + // Check destination sprite compatibility + int8_t ds_bpp = dspr->getColorDepth(); + if (_bpp == 16 && ds_bpp != 16 && ds_bpp != 8) return false; + if (_bpp == 8 && ds_bpp != 8) return false; + if (_bpp == 4 || ds_bpp == 4) return false; + if (_bpp == 1 && ds_bpp != 1) return false; + + bool oldSwapBytes = dspr->getSwapBytes(); + uint16_t sline_buffer[width()]; + + transp = transp>>8 | transp<<8; + + // Scan destination bounding box and fetch transformed pixels from source Sprite + for (int32_t ys = 0; ys < height(); ys++) { + int32_t ox = x; + uint32_t pixel_count = 0; + + for (int32_t xs = 0; xs < width(); xs++) { + uint16_t rp = 0; + if (_bpp == 16) rp = _img[xs + ys * width()]; + else { rp = readPixel(xs, ys); rp = rp>>8 | rp<<8; } + //dspr->drawPixel(xs, ys, rp); + + if (transp == rp) { + if (pixel_count) { + dspr->pushImage(ox, y, pixel_count, 1, sline_buffer); + ox += pixel_count; + pixel_count = 0; + } + ox++; + } + else { + sline_buffer[pixel_count++] = rp; + } + } + if (pixel_count) dspr->pushImage(ox, y, pixel_count, 1, sline_buffer); + y++; + } + dspr->setSwapBytes(oldSwapBytes); + return true; +} + + +/*************************************************************************************** +** Function name: pushSprite +** Description: Push a cropped sprite to the TFT at tx, ty +***************************************************************************************/ +bool TFT_eSprite::pushSprite(int32_t tx, int32_t ty, int32_t sx, int32_t sy, int32_t sw, int32_t sh) +{ + if (!_created) return false; + + // Perform window boundary checks and crop if needed + setWindow(sx, sy, sx + sw - 1, sy + sh - 1); + + /* These global variables are now populated for the sprite + _xs = x start coordinate + _ys = y start coordinate + _xe = x end coordinate (inclusive) + _ye = y end coordinate (inclusive) + */ + + // Calculate new sprite window bounding box width and height + sw = _xe - _xs + 1; + sh = _ye - _ys + 1; + + if (_ys >= _iheight) return false; + + if (_bpp == 16) + { + bool oldSwapBytes = _tft->getSwapBytes(); + _tft->setSwapBytes(false); + + // Check if a faster block copy to screen is possible + if ( sx == 0 && sw == _dwidth) + _tft->pushImage(tx, ty, sw, sh, _img + _iwidth * _ys ); + else // Render line by line + while (sh--) + _tft->pushImage(tx, ty++, sw, 1, _img + _xs + _iwidth * _ys++ ); + + _tft->setSwapBytes(oldSwapBytes); + } + else if (_bpp == 8) + { + // Check if a faster block copy to screen is possible + if ( sx == 0 && sw == _dwidth) + _tft->pushImage(tx, ty, sw, sh, _img8 + _iwidth * _ys, (bool)true ); + else // Render line by line + while (sh--) + _tft->pushImage(tx, ty++, sw, 1, _img8 + _xs + _iwidth * _ys++, (bool)true ); + } + else if (_bpp == 4) + { + // Check if a faster block copy to screen is possible + if ( sx == 0 && sw == _dwidth) + _tft->pushImage(tx, ty, sw, sh, _img4 + (_iwidth>>1) * _ys, false, _colorMap ); + else // Render line by line + { + int32_t ds = _xs&1; // Odd x start pixel + + int32_t de = 0; // Odd x end pixel + if ((sw > ds) && (_xe&1)) de = 1; + + uint32_t dm = 0; // Midsection pixel count + if (sw > (ds+de)) dm = sw - ds - de; + sw--; + + uint32_t yp = (_xs + ds + _iwidth * _ys)>>1; + _tft->startWrite(); + while (sh--) + { + if (ds) _tft->drawPixel(tx, ty, readPixel(_xs, _ys) ); + if (dm) _tft->pushImage(tx + ds, ty, dm, 1, _img4 + yp, false, _colorMap ); + if (de) _tft->drawPixel(tx + sw, ty, readPixel(_xe, _ys) ); + _ys++; + ty++; + yp += (_iwidth>>1); + } + _tft->endWrite(); + } + } + else // 1bpp + { + // Check if a faster block copy to screen is possible + if ( sx == 0 && sw == _dwidth) + _tft->pushImage(tx, ty, sw, sh, _img8 + (_bitwidth>>3) * _ys, (bool)false ); + else // Render line by line + { + _tft->startWrite(); + while (sh--) + { + _tft->pushImage(tx, ty++, sw, 1, _img8 + (_bitwidth>>3) * _ys++, (bool)false ); + } + _tft->endWrite(); + } + } + + return true; +} + + +/*************************************************************************************** +** Function name: readPixelValue +** Description: Read the color map index of a pixel at defined coordinates +***************************************************************************************/ +uint16_t TFT_eSprite::readPixelValue(int32_t x, int32_t y) +{ + if (_vpOoB || !_created) return 0xFF; + + x+= _xDatum; + y+= _yDatum; + + // Range checking + if ((x < _vpX) || (y < _vpY) ||(x >= _vpW) || (y >= _vpH)) return 0xFF; + + if (_bpp == 16) + { + // Return the pixel colour + return readPixel(x - _xDatum, y - _yDatum); + } + + if (_bpp == 8) + { + // Return the pixel byte value + return _img8[x + y * _iwidth]; + } + + if (_bpp == 4) + { + if (rotation == 1) + { + uint16_t tx = x; + x = _dheight - y - 1; + y = tx; + } + else if (rotation == 2) + { + x = _dwidth - x - 1; + y = _dheight - y - 1; + } + else if (rotation == 3) + { + uint16_t tx = x; + x = y; + y = _dwidth - tx - 1; + } + + if (x >= _dwidth) return 0xFF; + if ((x & 0x01) == 0) + return _img4[((x+y*_iwidth)>>1)] >> 4; // even index = bits 7 .. 4 + else + return _img4[((x+y*_iwidth)>>1)] & 0x0F; // odd index = bits 3 .. 0. + } + + if (_bpp == 1) + { + // Note: _dwidth and _dheight bounds not checked (rounded up -iwidth and _iheight used) + if (rotation == 1) + { + uint16_t tx = x; + x = _dheight - y - 1; + y = tx; + } + else if (rotation == 2) + { + x = _dwidth - x - 1; + y = _dheight - y - 1; + } + else if (rotation == 3) + { + uint16_t tx = x; + x = y; + y = _dwidth - tx - 1; + } + // Return 1 or 0 + return (_img8[(x + y * _bitwidth)>>3] >> (7-(x & 0x7))) & 0x01; + } + + return 0; +} + +/*************************************************************************************** +** Function name: readPixel +** Description: Read 565 colour of a pixel at defined coordinates +***************************************************************************************/ +uint16_t TFT_eSprite::readPixel(int32_t x, int32_t y) +{ + if (_vpOoB || !_created) return 0xFFFF; + + x+= _xDatum; + y+= _yDatum; + + // Range checking + if ((x < _vpX) || (y < _vpY) ||(x >= _vpW) || (y >= _vpH)) return 0xFFFF; + + if (_bpp == 16) + { + uint16_t color = _img[x + y * _iwidth]; + return (color >> 8) | (color << 8); + } + + if (_bpp == 8) + { + uint16_t color = _img8[x + y * _iwidth]; + if (color != 0) + { + uint8_t blue[] = {0, 11, 21, 31}; + color = (color & 0xE0)<<8 | (color & 0xC0)<<5 + | (color & 0x1C)<<6 | (color & 0x1C)<<3 + | blue[color & 0x03]; + } + return color; + } + + if (_bpp == 4) + { + if (rotation == 1) + { + uint16_t tx = x; + x = _dheight - y - 1; + y = tx; + } + else if (rotation == 2) + { + x = _dwidth - x - 1; + y = _dheight - y - 1; + } + else if (rotation == 3) + { + uint16_t tx = x; + x = y; + y = _dwidth - tx - 1; + } + + if (x >= _dwidth) return 0xFFFF; + uint16_t color; + if ((x & 0x01) == 0) + color = _colorMap[_img4[((x+y*_iwidth)>>1)] >> 4]; // even index = bits 7 .. 4 + else + color = _colorMap[_img4[((x+y*_iwidth)>>1)] & 0x0F]; // odd index = bits 3 .. 0. + return color; + } + + // Note: Must be 1bpp + // _dwidth and _dheight bounds not checked (rounded up -iwidth and _iheight used) + if (rotation == 1) + { + uint16_t tx = x; + x = _dheight - y - 1; + y = tx; + } + else if (rotation == 2) + { + x = _dwidth - x - 1; + y = _dheight - y - 1; + } + else if (rotation == 3) + { + uint16_t tx = x; + x = y; + y = _dwidth - tx - 1; + } + + uint16_t color = (_img8[(x + y * _bitwidth)>>3] << (x & 0x7)) & 0x80; + + if (color) return _tft->bitmap_fg; + else return _tft->bitmap_bg; +} + + +/*************************************************************************************** +** Function name: pushImage +** Description: push image into a defined area of a sprite +***************************************************************************************/ +void TFT_eSprite::pushImage(int32_t x, int32_t y, int32_t w, int32_t h, uint16_t *data, uint8_t sbpp) +{ + if (data == nullptr || !_created) return; + + PI_CLIP; + + if (_bpp == 16) // Plot a 16 bpp image into a 16 bpp Sprite + { + // Pointer within original image + uint8_t *ptro = (uint8_t *)data + ((dx + dy * w) << 1); + // Pointer within sprite image + uint8_t *ptrs = (uint8_t *)_img + ((x + y * _iwidth) << 1); + + if(_swapBytes) + { + while (dh--) + { + // Fast copy with a 1 byte shift + memcpy(ptrs+1, ptro, (dw<<1) - 1); + // Now correct just the even numbered bytes + for (int32_t xp = 0; xp < (dw<<1); xp+=2) + { + ptrs[xp] = ptro[xp+1];; + } + ptro += w<<1; + ptrs += _iwidth<<1; + } + } + else + { + while (dh--) + { + memcpy(ptrs, ptro, dw<<1); + ptro += w << 1; + ptrs += _iwidth << 1; + } + } + } + else if (_bpp == 8 && sbpp == 8) // Plot a 8 bpp image into a 8 bpp Sprite + { + // Pointer within original image + uint8_t *ptro = (uint8_t *)data + (dx + dy * w); + // Pointer within sprite image + uint8_t *ptrs = (uint8_t *)_img + (x + y * _iwidth); + + while (dh--) + { + memcpy(ptrs, ptro, dw); + ptro += w; + ptrs += _iwidth; + } + } + else if (_bpp == 8) // Plot a 16 bpp image into a 8 bpp Sprite + { + uint16_t lastColor = 0; + uint8_t color8 = 0; + for (int32_t yp = dy; yp < dy + dh; yp++) + { + int32_t xyw = x + y * _iwidth; + int32_t dxypw = dx + yp * w; + for (int32_t xp = dx; xp < dx + dw; xp++) + { + uint16_t color = data[dxypw++]; + if (color != lastColor) { + // When data source is a sprite, the bytes are already swapped + if(!_swapBytes) color8 = (uint8_t)((color & 0xE0) | (color & 0x07)<<2 | (color & 0x1800)>>11); + else color8 = (uint8_t)((color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3); + } + lastColor = color; + _img8[xyw++] = color8; + } + y++; + } + } + else if (_bpp == 4) + { + // The image is assumed to be 4-bit, where each byte corresponds to two pixels. + // much faster when aligned to a byte boundary, because the alternative is slower, requiring + // tedious bit operations. + + int sWidth = (_iwidth >> 1); + uint8_t *ptr = (uint8_t *)data; + + if (!rotation && (x & 0x01) == 0 && (dx & 0x01) == 0 && (dw & 0x01) == 0) + { + x = (x >> 1) + y * sWidth; + dw = (dw >> 1); + dx = (dx >> 1) + dy * (w>>1); + while (dh--) + { + memcpy(_img4 + x, ptr + dx, dw); + dx += (w >> 1); + x += sWidth; + } + } + else // not optimized + { + for (int32_t yp = dy; yp < dy + dh; yp++) + { + int32_t ox = x; + for (int32_t xp = dx; xp < dx + dw; xp++) + { + uint32_t color; + if ((xp & 0x01) == 0) + color = (ptr[((xp+yp*w)>>1)] & 0xF0) >> 4; // even index = bits 7 .. 4 + else + color = ptr[((xp-1+yp*w)>>1)] & 0x0F; // odd index = bits 3 .. 0. + drawPixel(ox, y, color); + ox++; + } + y++; + } + } + } + + else // 1bpp + { + // Plot a 1bpp image into a 1bpp Sprite + uint32_t ww = (w+7)>>3; // Width of source image line in bytes + uint8_t *ptr = (uint8_t *)data; + for (int32_t yp = dy; yp < dy + dh; yp++) + { + uint32_t yw = yp * ww; // Byte starting the line containing source pixel + int32_t ox = x; + for (int32_t xp = dx; xp < dx + dw; xp++) + { + uint16_t readPixel = (ptr[(xp>>3) + yw] & (0x80 >> (xp & 0x7)) ); + drawPixel(ox++, y, readPixel); + } + y++; + } + } +} + + +/*************************************************************************************** +** Function name: pushImage +** Description: push 565 colour FLASH (PROGMEM) image into a defined area +***************************************************************************************/ +void TFT_eSprite::pushImage(int32_t x, int32_t y, int32_t w, int32_t h, const uint16_t *data) +{ +#ifdef ESP32 + pushImage(x, y, w, h, (uint16_t*) data); +#else + // Partitioned memory FLASH processor + if (data == nullptr || !_created) return; + + PI_CLIP; + + if (_bpp == 16) // Plot a 16 bpp image into a 16 bpp Sprite + { + for (int32_t yp = dy; yp < dy + dh; yp++) + { + int32_t ox = x; + for (int32_t xp = dx; xp < dx + dw; xp++) + { + uint16_t color = pgm_read_word(data + xp + yp * w); + if(_swapBytes) color = color<<8 | color>>8; + _img[ox + y * _iwidth] = color; + ox++; + } + y++; + } + } + + else if (_bpp == 8) // Plot a 16 bpp image into a 8 bpp Sprite + { + for (int32_t yp = dy; yp < dy + dh; yp++) + { + int32_t ox = x; + for (int32_t xp = dx; xp < dx + dw; xp++) + { + uint16_t color = pgm_read_word(data + xp + yp * w); + if(_swapBytes) color = color<<8 | color>>8; + _img8[ox + y * _iwidth] = (uint8_t)((color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3); + ox++; + } + y++; + } + } + + else if (_bpp == 4) + { + #ifdef TFT_eSPI_DEBUG + Serial.println("TFT_eSprite::pushImage(int32_t x, int32_t y, int32_t w, int32_t h, const uint16_t *data) not implemented"); + #endif + return; + } + + else // Plot a 1bpp image into a 1bpp Sprite + { + x-= _xDatum; // Remove offsets, drawPixel will add + y-= _yDatum; + uint16_t bsw = (w+7) >> 3; // Width in bytes of source image line + uint8_t *ptr = ((uint8_t*)data) + dy * bsw; + + while (dh--) { + int32_t odx = dx; + int32_t ox = x; + while (odx < dx + dw) { + uint8_t pbyte = pgm_read_byte(ptr + (odx>>3)); + uint8_t mask = 0x80 >> (odx & 7); + while (mask) { + uint8_t p = pbyte & mask; + mask = mask >> 1; + drawPixel(ox++, y, p); + odx++; + } + } + ptr += bsw; + y++; + } + } +#endif // if ESP32 check +} + + +/*************************************************************************************** +** Function name: setWindow +** Description: Set the bounds of a window in the sprite +***************************************************************************************/ +// Intentionally not constrained to viewport area, does not manage 1bpp rotations +void TFT_eSprite::setWindow(int32_t x0, int32_t y0, int32_t x1, int32_t y1) +{ + if (x0 > x1) transpose(x0, x1); + if (y0 > y1) transpose(y0, y1); + + int32_t w = width(); + int32_t h = height(); + + if ((x0 >= w) || (x1 < 0) || (y0 >= h) || (y1 < 0)) + { // Point to that extra "off screen" pixel + _xs = 0; + _ys = _dheight; + _xe = 0; + _ye = _dheight; + } + else + { + if (x0 < 0) x0 = 0; + if (x1 >= w) x1 = w - 1; + if (y0 < 0) y0 = 0; + if (y1 >= h) y1 = h - 1; + + _xs = x0; + _ys = y0; + _xe = x1; + _ye = y1; + } + + _xptr = _xs; + _yptr = _ys; +} + + +/*************************************************************************************** +** Function name: pushColor +** Description: Send a new pixel to the set window +***************************************************************************************/ +void TFT_eSprite::pushColor(uint16_t color) +{ + if (!_created ) return; + + // Write the colour to RAM in set window + if (_bpp == 16) + _img [_xptr + _yptr * _iwidth] = (uint16_t) (color >> 8) | (color << 8); + + else if (_bpp == 8) + _img8[_xptr + _yptr * _iwidth] = (uint8_t )((color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3); + + else if (_bpp == 4) + { + uint8_t c = (uint8_t)color & 0x0F; + if ((_xptr & 0x01) == 0) { + _img4[(_xptr + _yptr * _iwidth)>>1] = (c << 4) | (_img4[(_xptr + _yptr * _iwidth)>>1] & 0x0F); // new color is in bits 7 .. 4 + } + else { + _img4[(_xptr + _yptr * _iwidth)>>1] = (_img4[(_xptr + _yptr * _iwidth)>>1] & 0xF0) | c; // new color is the low bits + } + } + + else drawPixel(_xptr, _yptr, color); + + // Increment x + _xptr++; + + // Wrap on x and y to start, increment y if needed + if (_xptr > _xe) + { + _xptr = _xs; + _yptr++; + if (_yptr > _ye) _yptr = _ys; + } + +} + + +/*************************************************************************************** +** Function name: pushColor +** Description: Send a "len" new pixels to the set window +***************************************************************************************/ +void TFT_eSprite::pushColor(uint16_t color, uint32_t len) +{ + if (!_created ) return; + + uint16_t pixelColor; + + if (_bpp == 16) + pixelColor = (uint16_t) (color >> 8) | (color << 8); + + else if (_bpp == 8) + pixelColor = (color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3; + + else pixelColor = (uint16_t) color; // for 1bpp or 4bpp + + while(len--) writeColor(pixelColor); +} + + +/*************************************************************************************** +** Function name: writeColor +** Description: Write a pixel with pre-formatted colour to the set window +***************************************************************************************/ +void TFT_eSprite::writeColor(uint16_t color) +{ + if (!_created ) return; + + // Write 16-bit RGB 565 encoded colour to RAM + if (_bpp == 16) _img [_xptr + _yptr * _iwidth] = color; + + // Write 8-bit RGB 332 encoded colour to RAM + else if (_bpp == 8) _img8[_xptr + _yptr * _iwidth] = (uint8_t) color; + + else if (_bpp == 4) + { + uint8_t c = (uint8_t)color & 0x0F; + if ((_xptr & 0x01) == 0) + _img4[(_xptr + _yptr * _iwidth)>>1] = (c << 4) | (_img4[(_xptr + _yptr * _iwidth)>>1] & 0x0F); // new color is in bits 7 .. 4 + else + _img4[(_xptr + _yptr * _iwidth)>>1] = (_img4[(_xptr + _yptr * _iwidth)>>1] & 0xF0) | c; // new color is the low bits (x is odd) + } + + else drawPixel(_xptr, _yptr, color); + + // Increment x + _xptr++; + + // Wrap on x and y to start, increment y if needed + if (_xptr > _xe) + { + _xptr = _xs; + _yptr++; + if (_yptr > _ye) _yptr = _ys; + } +} + + +/*************************************************************************************** +** Function name: setScrollRect +** Description: Set scroll area within the sprite and the gap fill colour +***************************************************************************************/ +// Intentionally not constrained to viewport area +void TFT_eSprite::setScrollRect(int32_t x, int32_t y, int32_t w, int32_t h, uint16_t color) +{ + if ((x >= _iwidth) || (y >= _iheight) || !_created ) return; + + if (x < 0) { w += x; x = 0; } + if (y < 0) { h += y; y = 0; } + + if ((x + w) > _iwidth ) w = _iwidth - x; + if ((y + h) > _iheight) h = _iheight - y; + + if ( w < 1 || h < 1) return; + + _sx = x; + _sy = y; + _sw = w; + _sh = h; + + _scolor = color; +} + + +/*************************************************************************************** +** Function name: scroll +** Description: Scroll dx,dy pixels, positive right,down, negative left,up +***************************************************************************************/ +void TFT_eSprite::scroll(int16_t dx, int16_t dy) +{ + if (abs(dx) >= _sw || abs(dy) >= _sh) + { + fillRect (_sx, _sy, _sw, _sh, _scolor); + return; + } + + // Fetch the scroll area width and height set by setScrollRect() + uint32_t w = _sw - abs(dx); // line width to copy + uint32_t h = _sh - abs(dy); // lines to copy + int32_t iw = _iwidth; // rounded up width of sprite + + // Fetch the x,y origin set by setScrollRect() + uint32_t tx = _sx; // to x + uint32_t fx = _sx; // from x + uint32_t ty = _sy; // to y + uint32_t fy = _sy; // from y + + // Adjust for x delta + if (dx <= 0) fx -= dx; + else tx += dx; + + // Adjust for y delta + if (dy <= 0) fy -= dy; + else + { // Scrolling down so start copy from bottom + ty = ty + _sh - 1; // "To" pointer + iw = -iw; // Pointer moves backwards + fy = ty - dy; // "From" pointer + } + + // Calculate "from y" and "to y" pointers in RAM + uint32_t fyp = fx + fy * _iwidth; + uint32_t typ = tx + ty * _iwidth; + + // Now move the pixels in RAM + if (_bpp == 16) + { + while (h--) + { // move pixel lines (to, from, byte count) + memmove( _img + typ, _img + fyp, w<<1); + typ += iw; + fyp += iw; + } + } + else if (_bpp == 8) + { + while (h--) + { // move pixel lines (to, from, byte count) + memmove( _img8 + typ, _img8 + fyp, w); + typ += iw; + fyp += iw; + } + } + else if (_bpp == 4) + { + // could optimize for scrolling by even # pixels using memove (later) + if (dx > 0) { tx += w; fx += w; } // Start from right edge + while (h--) + { // move pixels one by one + for (uint16_t xp = 0; xp < w; xp++) + { + if (dx <= 0) drawPixel(tx + xp, ty, readPixelValue(fx + xp, fy)); + if (dx > 0) drawPixel(tx - xp, ty, readPixelValue(fx - xp, fy)); + } + if (dy <= 0) { ty++; fy++; } + else { ty--; fy--; } + } + } + else if (_bpp == 1 ) + { + if (dx > 0) { tx += w; fx += w; } // Start from right edge + while (h--) + { // move pixels one by one + for (uint16_t xp = 0; xp < w; xp++) + { + if (dx <= 0) drawPixel(tx + xp, ty, readPixelValue(fx + xp, fy)); + if (dx > 0) drawPixel(tx - xp, ty, readPixelValue(fx - xp, fy)); + } + if (dy <= 0) { ty++; fy++; } + else { ty--; fy--; } + } + } + else return; // Not 1, 4, 8 or 16 bpp + + // Fill the gap left by the scrolling + if (dx > 0) fillRect(_sx, _sy, dx, _sh, _scolor); + if (dx < 0) fillRect(_sx + _sw + dx, _sy, -dx, _sh, _scolor); + if (dy > 0) fillRect(_sx, _sy, _sw, dy, _scolor); + if (dy < 0) fillRect(_sx, _sy + _sh + dy, _sw, -dy, _scolor); +} + + +/*************************************************************************************** +** Function name: fillSprite +** Description: Fill the whole sprite with defined colour +***************************************************************************************/ +void TFT_eSprite::fillSprite(uint32_t color) +{ + if (!_created || _vpOoB) return; + + // Use memset if possible as it is super fast + if(_xDatum == 0 && _yDatum == 0 && _xWidth == width()) + { + if(_bpp == 16) { + if ( (uint8_t)color == (uint8_t)(color>>8) ) { + memset(_img, (uint8_t)color, _iwidth * _yHeight * 2); + } + else fillRect(_vpX, _vpY, _xWidth, _yHeight, color); + } + else if (_bpp == 8) + { + color = (color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3; + memset(_img8, (uint8_t)color, _iwidth * _yHeight); + } + else if (_bpp == 4) + { + uint8_t c = ((color & 0x0F) | (((color & 0x0F) << 4) & 0xF0)); + memset(_img4, c, (_iwidth * _yHeight) >> 1); + } + else if (_bpp == 1) + { + if(color) memset(_img8, 0xFF, (_bitwidth>>3) * _dheight + 1); + else memset(_img8, 0x00, (_bitwidth>>3) * _dheight + 1); + } + } + else fillRect(_vpX - _xDatum, _vpY - _yDatum, _xWidth, _yHeight, color); +} + + +/*************************************************************************************** +** Function name: width +** Description: Return the width of sprite +***************************************************************************************/ +// Return the size of the sprite +int16_t TFT_eSprite::width(void) +{ + if (!_created ) return 0; + + if (_bpp > 1 && _bpp != 4) { + if (_vpDatum) return _xWidth; + return _dwidth; + } + + if (rotation & 1) { + if (_vpDatum) return _xWidth; + return _dheight; + } + + if (_vpDatum) return _xWidth; + return _dwidth; +} + + +/*************************************************************************************** +** Function name: height +** Description: Return the height of sprite +***************************************************************************************/ +int16_t TFT_eSprite::height(void) +{ + if (!_created ) return 0; + + if (_bpp > 1 && _bpp != 4) { + if (_vpDatum) return _yHeight; + return _dheight; + } + + if (rotation & 1) { + if (_vpDatum) return _yHeight; + return _dwidth; + } + + if (_vpDatum) return _yHeight; + return _dheight; +} + + +/*************************************************************************************** +** Function name: setRotation +** Description: Rotate coordinate frame for 1bpp and 4bpp sprite +***************************************************************************************/ +// Does nothing for 8 and 16 bpp sprites. +void TFT_eSprite::setRotation(uint8_t r) +{ + if (_bpp != 1 && _bpp != 4) return; + + rotation = r; + + if (rotation&1) { + resetViewport(); + } + else { + resetViewport(); + } +} + + +/*************************************************************************************** +** Function name: getRotation +** Description: Get rotation for 1bpp sprite +***************************************************************************************/ +uint8_t TFT_eSprite::getRotation(void) +{ + return rotation; +} + + +/*************************************************************************************** +** Function name: drawPixel +** Description: push a single pixel at an arbitrary position +***************************************************************************************/ +void TFT_eSprite::drawPixel(int32_t x, int32_t y, uint32_t color) +{ + if (!_created || _vpOoB) return; + + x+= _xDatum; + y+= _yDatum; + + // Range checking + if ((x < _vpX) || (y < _vpY) ||(x >= _vpW) || (y >= _vpH)) return; + + if (_bpp == 16) + { + color = (color >> 8) | (color << 8); + _img[x+y*_iwidth] = (uint16_t) color; + } + else if (_bpp == 8) + { + _img8[x+y*_iwidth] = (uint8_t)((color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3); + } + else if (_bpp == 4) + { + if (rotation == 1) + { + uint16_t tx = x; + x = _dwidth - y - 1; + y = tx; + } + else if (rotation == 2) + { + x = _dwidth - x - 1; + y = _dheight - y - 1; + } + else if (rotation == 3) + { + uint16_t tx = x; + x = y; + y = _dheight - tx - 1; + } + + uint8_t c = color & 0x0F; + int index = (x+y*_iwidth)>>1;; + if ((x & 0x01) == 0) { + _img4[index] = (uint8_t)((c << 4) | (_img4[index] & 0x0F)); + } + else { + _img4[index] = (uint8_t)(c | (_img4[index] & 0xF0)); + } + } + else // 1 bpp + { + if (rotation == 1) + { + uint16_t tx = x; + x = _dwidth - y - 1; + y = tx; + } + else if (rotation == 2) + { + x = _dwidth - x - 1; + y = _dheight - y - 1; + } + else if (rotation == 3) + { + uint16_t tx = x; + x = y; + y = _dheight - tx - 1; + } + + if (color) _img8[(x + y * _bitwidth)>>3] |= (0x80 >> (x & 0x7)); + else _img8[(x + y * _bitwidth)>>3] &= ~(0x80 >> (x & 0x7)); + } +} + + +/*************************************************************************************** +** Function name: drawLine +** Description: draw a line between 2 arbitrary points +***************************************************************************************/ +void TFT_eSprite::drawLine(int32_t x0, int32_t y0, int32_t x1, int32_t y1, uint32_t color) +{ + if (!_created || _vpOoB) return; + + //_xDatum and _yDatum Not added here, it is added by drawPixel & drawFastxLine + + bool steep = abs(y1 - y0) > abs(x1 - x0); + if (steep) { + transpose(x0, y0); + transpose(x1, y1); + } + + if (x0 > x1) { + transpose(x0, x1); + transpose(y0, y1); + } + + int32_t dx = x1 - x0, dy = abs(y1 - y0);; + + int32_t err = dx >> 1, ystep = -1, xs = x0, dlen = 0; + + if (y0 < y1) ystep = 1; + + // Split into steep and not steep for FastH/V separation + if (steep) { + for (; x0 <= x1; x0++) { + dlen++; + err -= dy; + if (err < 0) { + err += dx; + if (dlen == 1) drawPixel(y0, xs, color); + else drawFastVLine(y0, xs, dlen, color); + dlen = 0; y0 += ystep; xs = x0 + 1; + } + } + if (dlen) drawFastVLine(y0, xs, dlen, color); + } + else + { + for (; x0 <= x1; x0++) { + dlen++; + err -= dy; + if (err < 0) { + err += dx; + if (dlen == 1) drawPixel(xs, y0, color); + else drawFastHLine(xs, y0, dlen, color); + dlen = 0; y0 += ystep; xs = x0 + 1; + } + } + if (dlen) drawFastHLine(xs, y0, dlen, color); + } +} + + +/*************************************************************************************** +** Function name: drawFastVLine +** Description: draw a vertical line +***************************************************************************************/ +void TFT_eSprite::drawFastVLine(int32_t x, int32_t y, int32_t h, uint32_t color) +{ + if (!_created || _vpOoB) return; + + x+= _xDatum; + y+= _yDatum; + + // Clipping + if ((x < _vpX) || (x >= _vpW) || (y >= _vpH)) return; + + if (y < _vpY) { h += y - _vpY; y = _vpY; } + + if ((y + h) > _vpH) h = _vpH - y; + + if (h < 1) return; + + if (_bpp == 16) + { + color = (color >> 8) | (color << 8); + int32_t yp = x + _iwidth * y; + while (h--) {_img[yp] = (uint16_t) color; yp += _iwidth;} + } + else if (_bpp == 8) + { + color = (color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3; + while (h--) _img8[x + _iwidth * y++] = (uint8_t) color; + } + else if (_bpp == 4) + { + if (rotation) + { + if (h < 1) return; + while (h--) drawPixel(x, y++, color); + return; + } + + if ((x & 0x01) == 0) + { + uint8_t c = (uint8_t) (color & 0xF) << 4; + while (h--) { + _img4[(x + _iwidth * y)>>1] = (uint8_t) (c | (_img4[(x + _iwidth * y)>>1] & 0x0F)); + y++; + } + } + else { + uint8_t c = (uint8_t)color & 0xF; + while (h--) { + _img4[(x + _iwidth * y)>>1] = (uint8_t) (c | (_img4[(x + _iwidth * y)>>1] & 0xF0)); // x is odd; new color goes into the low bits. + y++; + } + } + } + else + { + x -= _xDatum; // Remove any offset as it will be added by drawPixel + y -= _yDatum; + while (h--) + { + drawPixel(x, y++, color); + } + } +} + + +/*************************************************************************************** +** Function name: drawFastHLine +** Description: draw a horizontal line +***************************************************************************************/ +void TFT_eSprite::drawFastHLine(int32_t x, int32_t y, int32_t w, uint32_t color) +{ + if (!_created || _vpOoB) return; + + x+= _xDatum; + y+= _yDatum; + + // Clipping + if ((y < _vpY) || (x >= _vpW) || (y >= _vpH)) return; + + if (x < _vpX) { w += x - _vpX; x = _vpX; } + + if ((x + w) > _vpW) w = _vpW - x; + + if (w < 1) return; + + if (_bpp == 16) + { + color = (color >> 8) | (color << 8); + while (w--) _img[_iwidth * y + x++] = (uint16_t) color; + } + else if (_bpp == 8) + { + color = (color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3; + memset(_img8+_iwidth * y + x, (uint8_t)color, w); + } + else if (_bpp == 4) + { + if(rotation) + { + if (w < 1) return; + while (w--) drawPixel(x++, y, color); + return; + } + uint8_t c = (uint8_t)color & 0x0F; + uint8_t c2 = (c | ((c << 4) & 0xF0)); + if ((x & 0x01) == 1) + { + drawPixel(x - _xDatum, y - _yDatum, color); + x++; w--; + if (w < 1) + return; + } + + if (((w + x) & 0x01) == 1) + { + // handle the extra one at the other end + drawPixel(x - _xDatum + w - 1, y - _yDatum, color); + w--; + if (w < 1) return; + } + memset(_img4 + ((_iwidth * y + x) >> 1), c2, (w >> 1)); + } + else { + x -= _xDatum; // Remove any offset as it will be added by drawPixel + y -= _yDatum; + + while (w--) + { + drawPixel(x++, y, color); + } + } +} + + +/*************************************************************************************** +** Function name: fillRect +** Description: draw a filled rectangle +***************************************************************************************/ +void TFT_eSprite::fillRect(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color) +{ + if (!_created || _vpOoB) return; + + x+= _xDatum; + y+= _yDatum; + + // Clipping + if ((x >= _vpW) || (y >= _vpH)) return; + + if (x < _vpX) { w += x - _vpX; x = _vpX; } + if (y < _vpY) { h += y - _vpY; y = _vpY; } + + if ((x + w) > _vpW) w = _vpW - x; + if ((y + h) > _vpH) h = _vpH - y; + + if ((w < 1) || (h < 1)) return; + + int32_t yp = _iwidth * y + x; + + if (_bpp == 16) + { + color = (color >> 8) | (color << 8); + uint32_t iw = w; + int32_t ys = yp; + if(h--) {while (iw--) _img[yp++] = (uint16_t) color;} + yp = ys; + while (h--) + { + yp += _iwidth; + memcpy( _img+yp, _img+ys, w<<1); + } + } + else if (_bpp == 8) + { + color = (color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3; + while (h--) + { + memset(_img8 + yp, (uint8_t)color, w); + yp += _iwidth; + } + } + else if (_bpp == 4) + { + if (rotation) + { + if (w < 1 || h < 1) return; + for (int32_t yy = 0; yy < h; yy++) + { + for (int32_t xx = 0; xx < w; xx++) + { + drawPixel(x + xx, y + yy, color); + } + } + return; + } + uint8_t c1 = (uint8_t)color & 0x0F; + uint8_t c2 = c1 | ((c1 << 4) & 0xF0); + if ((x & 0x01) == 0 && (w & 0x01) == 0) + { + yp = (yp >> 1); + while (h--) + { + memset(_img4 + yp, c2, (w>>1)); + yp += (_iwidth >> 1); + } + } + else if ((x & 0x01) == 0) + { + + // same as above but you have a hangover on the right. + yp = (yp >> 1); + while (h--) + { + if (w > 1) + memset(_img4 + yp, c2, (w-1)>>1); + // handle the rightmost pixel by calling drawPixel + drawPixel(x+w-1-_xDatum, y+h-_yDatum, c1); + yp += (_iwidth >> 1); + } + } + else if ((w & 0x01) == 1) + { + yp = (yp + 1) >> 1; + while (h--) { + drawPixel(x-_xDatum, y+h-_yDatum, color & 0x0F); + if (w > 1) + memset(_img4 + yp, c2, (w-1)>>1); + // same as above but you have a hangover on the left instead + yp += (_iwidth >> 1); + } + } + else + { + yp = (yp + 1) >> 1; + while (h--) { + drawPixel(x-_xDatum, y+h-_yDatum, color & 0x0F); + if (w > 1) drawPixel(x+w-1-_xDatum, y+h-_yDatum, color & 0x0F); + if (w > 2) + memset(_img4 + yp, c2, (w-2)>>1); + // maximal hacking, single pixels on left and right. + yp += (_iwidth >> 1); + } + } + } + else + { + x -= _xDatum; + y -= _yDatum; + while (h--) + { + int32_t ww = w; + int32_t xx = x; + while (ww--) drawPixel(xx++, y, color); + y++; + } + } +} + + +/*************************************************************************************** +** Function name: drawChar +** Description: draw a single character in the Adafruit GLCD or freefont +***************************************************************************************/ +void TFT_eSprite::drawChar(int32_t x, int32_t y, uint16_t c, uint32_t color, uint32_t bg, uint8_t size) +{ + if ( _vpOoB || !_created ) return; + + if (c < 32) return; +#ifdef LOAD_GLCD +//>>>>>>>>>>>>>>>>>> +#ifdef LOAD_GFXFF + if(!gfxFont) { // 'Classic' built-in font +#endif +//>>>>>>>>>>>>>>>>>> + + if ((x >= _vpW - _xDatum) || // Clip right + (y >= _vpH - _yDatum)) // Clip bottom + return; + + if (((x + 6 * size - 1) < (_vpX - _xDatum)) || // Clip left + ((y + 8 * size - 1) < (_vpY - _yDatum))) // Clip top + return; + + if (c > 255) return; + if (!_cp437 && c > 175) c++; + + bool fillbg = (bg != color); + + if ((size==1) && fillbg) + { + uint8_t column[6]; + uint8_t mask = 0x1; + + for (int8_t i = 0; i < 5; i++ ) column[i] = pgm_read_byte(font + (c * 5) + i); + column[5] = 0; + + int8_t j, k; + for (j = 0; j < 8; j++) { + for (k = 0; k < 5; k++ ) { + if (column[k] & mask) { + drawPixel(x + k, y + j, color); + } + else { + drawPixel(x + k, y + j, bg); + } + } + + mask <<= 1; + + drawPixel(x + k, y + j, bg); + } + } + else + { + for (int8_t i = 0; i < 6; i++ ) { + uint8_t line; + if (i == 5) + line = 0x0; + else + line = pgm_read_byte(font + (c * 5) + i); + + if (size == 1) // default size + { + for (int8_t j = 0; j < 8; j++) { + if (line & 0x1) drawPixel(x + i, y + j, color); + line >>= 1; + } + } + else { // big size + for (int8_t j = 0; j < 8; j++) { + if (line & 0x1) fillRect(x + (i * size), y + (j * size), size, size, color); + else if (fillbg) fillRect(x + i * size, y + j * size, size, size, bg); + line >>= 1; + } + } + } + } + +//>>>>>>>>>>>>>>>>>>>>>>>>>>> +#ifdef LOAD_GFXFF + } else { // Custom font +#endif +//>>>>>>>>>>>>>>>>>>>>>>>>>>> +#endif // LOAD_GLCD + +#ifdef LOAD_GFXFF + // Filter out bad characters not present in font + if ((c >= pgm_read_word(&gfxFont->first)) && (c <= pgm_read_word(&gfxFont->last ))) + { +//>>>>>>>>>>>>>>>>>>>>>>>>>>> + + c -= pgm_read_word(&gfxFont->first); + GFXglyph *glyph = &(((GFXglyph *)pgm_read_dword(&gfxFont->glyph))[c]); + + 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); + + if (((x + xo + w * size - 1) < (_vpX - _xDatum)) || // Clip left + ((y + yo + h * size - 1) < (_vpY - _yDatum))) // Clip top + return; + + uint8_t *bitmap = (uint8_t *)pgm_read_dword(&gfxFont->bitmap); + uint32_t bo = pgm_read_word(&glyph->bitmapOffset); + + uint8_t xx, yy, bits=0, bit=0; + //uint8_t xa = pgm_read_byte(&glyph->xAdvance); + int16_t xo16 = 0, yo16 = 0; + + if(size > 1) { + xo16 = xo; + yo16 = yo; + } + + uint16_t hpc = 0; // Horizontal foreground pixel count + for(yy=0; yy>= 1; + } + // Draw pixels for this line as we are about to increment yy + if (hpc) { + if(size == 1) drawFastHLine(x+xo+xx-hpc, y+yo+yy, hpc, color); + else fillRect(x+(xo16+xx-hpc)*size, y+(yo16+yy)*size, size*hpc, size, color); + hpc=0; + } + } + } +#endif + + +#ifdef LOAD_GLCD + #ifdef LOAD_GFXFF + } // End classic vs custom font + #endif +#else + #ifndef LOAD_GFXFF + color = color; + bg = bg; + size = size; + #endif +#endif + +} + + +/*************************************************************************************** +** Function name: drawChar +** Description: draw a Unicode glyph into the sprite +***************************************************************************************/ + // TODO: Rationalise with TFT_eSPI + // Any UTF-8 decoding must be done before calling drawChar() +int16_t TFT_eSprite::drawChar(uint16_t uniCode, int32_t x, int32_t y) +{ + return drawChar(uniCode, x, y, textfont); +} + + // Any UTF-8 decoding must be done before calling drawChar() +int16_t TFT_eSprite::drawChar(uint16_t uniCode, int32_t x, int32_t y, uint8_t font) +{ + if (_vpOoB || !uniCode) return 0; + + if (font==1) { +#ifdef LOAD_GLCD + #ifndef LOAD_GFXFF + drawChar(x, y, uniCode, textcolor, textbgcolor, textsize); + return 6 * textsize; + #endif +#else + #ifndef LOAD_GFXFF + return 0; + #endif +#endif + +#ifdef LOAD_GFXFF + drawChar(x, y, uniCode, textcolor, textbgcolor, textsize); + if(!gfxFont) { // 'Classic' built-in font + #ifdef LOAD_GLCD + return 6 * textsize; + #else + return 0; + #endif + } + else { + if((uniCode >= pgm_read_word(&gfxFont->first)) && (uniCode <= pgm_read_word(&gfxFont->last) )) { + uint16_t c2 = uniCode - pgm_read_word(&gfxFont->first); + GFXglyph *glyph = &(((GFXglyph *)pgm_read_dword(&gfxFont->glyph))[c2]); + return pgm_read_byte(&glyph->xAdvance) * textsize; + } + else { + return 0; + } + } +#endif + } + + if ((font>1) && (font<9) && ((uniCode < 32) || (uniCode > 127))) return 0; + + int32_t width = 0; + int32_t height = 0; + uint32_t flash_address = 0; + uniCode -= 32; + +#ifdef LOAD_FONT2 + if (font == 2) { + flash_address = pgm_read_dword(&chrtbl_f16[uniCode]); + width = pgm_read_byte(widtbl_f16 + uniCode); + height = chr_hgt_f16; + } + #ifdef LOAD_RLE + else + #endif +#endif + +#ifdef LOAD_RLE + { + if ((font>2) && (font<9)) { + flash_address = pgm_read_dword( (const void*)(pgm_read_dword( &(fontdata[font].chartbl ) ) + uniCode*sizeof(void *)) ); + width = pgm_read_byte( (uint8_t *)pgm_read_dword( &(fontdata[font].widthtbl ) ) + uniCode ); + height= pgm_read_byte( &fontdata[font].height ); + } + } +#endif + + int32_t xd = x + _xDatum; + int32_t yd = y + _yDatum; + + if ((xd + width * textsize < _vpX || xd >= _vpW) && (yd + height * textsize < _vpY || yd >= _vpH)) return width * textsize ; + + int32_t w = width; + int32_t pX = 0; + int32_t pY = y; + uint8_t line = 0; + bool clip = xd < _vpX || xd + width * textsize >= _vpW || yd < _vpY || yd + height * textsize >= _vpH; + +#ifdef LOAD_FONT2 // chop out code if we do not need it + if (font == 2) { + w = w + 6; // Should be + 7 but we need to compensate for width increment + w = w / 8; + + for (int32_t i = 0; i < height; i++) + { + if (textcolor != textbgcolor) fillRect(x, pY, width * textsize, textsize, textbgcolor); + + for (int32_t k = 0; k < w; k++) + { + line = pgm_read_byte((uint8_t *)flash_address + w * i + k); + if (line) { + if (textsize == 1) { + pX = x + k * 8; + if (line & 0x80) drawPixel(pX, pY, textcolor); + if (line & 0x40) drawPixel(pX + 1, pY, textcolor); + if (line & 0x20) drawPixel(pX + 2, pY, textcolor); + if (line & 0x10) drawPixel(pX + 3, pY, textcolor); + if (line & 0x08) drawPixel(pX + 4, pY, textcolor); + if (line & 0x04) drawPixel(pX + 5, pY, textcolor); + if (line & 0x02) drawPixel(pX + 6, pY, textcolor); + if (line & 0x01) drawPixel(pX + 7, pY, textcolor); + } + else { + pX = x + k * 8 * textsize; + if (line & 0x80) fillRect(pX, pY, textsize, textsize, textcolor); + if (line & 0x40) fillRect(pX + textsize, pY, textsize, textsize, textcolor); + if (line & 0x20) fillRect(pX + 2 * textsize, pY, textsize, textsize, textcolor); + if (line & 0x10) fillRect(pX + 3 * textsize, pY, textsize, textsize, textcolor); + if (line & 0x08) fillRect(pX + 4 * textsize, pY, textsize, textsize, textcolor); + if (line & 0x04) fillRect(pX + 5 * textsize, pY, textsize, textsize, textcolor); + if (line & 0x02) fillRect(pX + 6 * textsize, pY, textsize, textsize, textcolor); + if (line & 0x01) fillRect(pX + 7 * textsize, pY, textsize, textsize, textcolor); + } + } + } + pY += textsize; + } + } + + #ifdef LOAD_RLE + else + #endif +#endif //FONT2 + +#ifdef LOAD_RLE //674 bytes of code + // Font is not 2 and hence is RLE encoded + { + w *= height; // Now w is total number of pixels in the character + int16_t color = textcolor; + if (_bpp == 16) color = (textcolor >> 8) | (textcolor << 8); + else if (_bpp == 8) color = ((textcolor & 0xE000)>>8 | (textcolor & 0x0700)>>6 | (textcolor & 0x0018)>>3); + + int16_t bgcolor = textbgcolor; + if (_bpp == 16) bgcolor = (textbgcolor >> 8) | (textbgcolor << 8); + else if (_bpp == 8) bgcolor = ((textbgcolor & 0xE000)>>8 | (textbgcolor & 0x0700)>>6 | (textbgcolor & 0x0018)>>3); + + if (textcolor == textbgcolor && !clip && _bpp != 1) { + int32_t px = 0, py = pY; // To hold character block start and end column and row values + int32_t pc = 0; // Pixel count + uint8_t np = textsize * textsize; // Number of pixels in a drawn pixel + + uint8_t tnp = 0; // Temporary copy of np for while loop + uint8_t ts = textsize - 1; // Temporary copy of textsize + // 16-bit pixel count so maximum font size is equivalent to 180x180 pixels in area + // w is total number of pixels to plot to fill character block + while (pc < w) { + line = pgm_read_byte((uint8_t *)flash_address); + flash_address++; + if (line & 0x80) { + line &= 0x7F; + line++; + if (ts) { + px = xd + textsize * (pc % width); // Keep these px and py calculations outside the loop as they are slow + py = yd + textsize * (pc / width); + } + else { + px = xd + pc % width; // Keep these px and py calculations outside the loop as they are slow + py = yd + pc / width; + } + while (line--) { // In this case the while(line--) is faster + pc++; // This is faster than putting pc+=line before while()? + setWindow(px, py, px + ts, py + ts); + + if (ts) { + tnp = np; + while (tnp--) writeColor(color); + } + else writeColor(color); + + px += textsize; + + if (px >= (xd + width * textsize)) { + px = xd; + py += textsize; + } + } + } + else { + line++; + pc += line; + } + } + } + else { + // Text colour != background and textsize = 1 and character is within viewport area + // so use faster drawing of characters and background using block write + if (textcolor != textbgcolor && textsize == 1 && !clip && _bpp != 1) + { + setWindow(xd, yd, xd + width - 1, yd + height - 1); + + // Maximum font size is equivalent to 180x180 pixels in area + while (w > 0) { + line = pgm_read_byte((uint8_t *)flash_address++); // 8 bytes smaller when incrementing here + if (line & 0x80) { + line &= 0x7F; + line++; w -= line; + while (line--) writeColor(color); + } + else { + line++; w -= line; + while (line--) writeColor(bgcolor); + } + } + } + else + { + int32_t px = 0, py = 0; // To hold character pixel coords + int32_t tx = 0, ty = 0; // To hold character TFT pixel coords + int32_t pc = 0; // Pixel count + int32_t pl = 0; // Pixel line length + uint16_t pcol = 0; // Pixel color + bool pf = true; // Flag for plotting + while (pc < w) { + line = pgm_read_byte((uint8_t *)flash_address); + flash_address++; + if (line & 0x80) { pcol = textcolor; line &= 0x7F; pf = true;} + else { pcol = textbgcolor; if (textcolor == textbgcolor) pf = false;} + line++; + px = pc % width; + tx = x + textsize * px; + py = pc / width; + ty = y + textsize * py; + + pl = 0; + pc += line; + while (line--) { + pl++; + if ((px+pl) >= width) { + if (pf) fillRect(tx, ty, pl * textsize, textsize, pcol); + pl = 0; + px = 0; + tx = x; + py ++; + ty += textsize; + } + } + if (pl && pf) fillRect(tx, ty, pl * textsize, textsize, pcol); + } + } + } + } + // End of RLE font rendering +#endif + +#if !defined (LOAD_FONT2) && !defined (LOAD_RLE) + // Stop warnings + flash_address = flash_address; + w = w; + pX = pX; + pY = pY; + line = line; + clip = clip; +#endif + + return width * textsize; // x + +} + + +#ifdef SMOOTH_FONT +/*************************************************************************************** +** Function name: drawGlyph +** Description: Write a character to the sprite cursor position +***************************************************************************************/ +// +void TFT_eSprite::drawGlyph(uint16_t code) +{ + uint16_t fg = textcolor; + uint16_t bg = textbgcolor; + bool getBG = false; + if (fg == bg) getBG = true; + + // Check if cursor has moved + if (last_cursor_x != cursor_x) + { + bg_cursor_x = cursor_x; + last_cursor_x = cursor_x; + } + + if (code < 0x21) + { + if (code == 0x20) { + if (_fillbg) fillRect(bg_cursor_x, cursor_y, (cursor_x + gFont.spaceWidth) - bg_cursor_x, gFont.yAdvance, bg); + cursor_x += gFont.spaceWidth; + bg_cursor_x = cursor_x; + last_cursor_x = cursor_x; + return; + } + + if (code == '\n') { + cursor_x = 0; + bg_cursor_x = 0; + last_cursor_x = 0; + cursor_y += gFont.yAdvance; + if (textwrapY && (cursor_y >= height())) cursor_y = 0; + return; + } + } + + uint16_t gNum = 0; + bool found = getUnicodeIndex(code, &gNum); + + if (found) + { + + bool newSprite = !_created; + + if (newSprite) + { + createSprite(gWidth[gNum], gFont.yAdvance); + if(fg != bg) fillSprite(bg); + cursor_x = -gdX[gNum]; + bg_cursor_x = cursor_x; + last_cursor_x = cursor_x; + cursor_y = 0; + } + else + { + if( textwrapX && ((cursor_x + gWidth[gNum] + gdX[gNum]) > width())) { + cursor_y += gFont.yAdvance; + cursor_x = 0; + bg_cursor_x = 0; + last_cursor_x = 0; + } + + if( textwrapY && ((cursor_y + gFont.yAdvance) > height())) cursor_y = 0; + if ( cursor_x == 0) cursor_x -= gdX[gNum]; + } + + uint8_t* pbuffer = nullptr; + const uint8_t* gPtr = (const uint8_t*) gFont.gArray; + +#ifdef FONT_FS_AVAILABLE + if (fs_font) { + fontFile.seek(gBitmap[gNum], fs::SeekSet); // This is slow for a significant position shift! + pbuffer = (uint8_t*)malloc(gWidth[gNum]); + } +#endif + + int16_t cy = cursor_y + gFont.maxAscent - gdY[gNum]; + int16_t cx = cursor_x + gdX[gNum]; + + // if (cx > width() && bg_cursor_x > width()) return; + // if (cursor_y > height()) return; + + int16_t fxs = cx; + uint32_t fl = 0; + int16_t bxs = cx; + uint32_t bl = 0; + int16_t bx = 0; + uint8_t pixel = 0; + + int16_t fillwidth = 0; + int16_t fillheight = 0; + + // Fill area above glyph + if (_fillbg) { + fillwidth = (cursor_x + gxAdvance[gNum]) - bg_cursor_x; + if (fillwidth > 0) { + fillheight = gFont.maxAscent - gdY[gNum]; + if (fillheight > 0) { + fillRect(bg_cursor_x, cursor_y, fillwidth, fillheight, textbgcolor); + } + } + else { + // Could be negative + fillwidth = 0; + } + + // Fill any area to left of glyph + if (bg_cursor_x < cx) fillRect(bg_cursor_x, cy, cx - bg_cursor_x, gHeight[gNum], textbgcolor); + // Set x position in glyph area where background starts + if (bg_cursor_x > cx) bx = bg_cursor_x - cx; + // Fill any area to right of glyph + if (cx + gWidth[gNum] < cursor_x + gxAdvance[gNum]) { + fillRect(cx + gWidth[gNum], cy, (cursor_x + gxAdvance[gNum]) - (cx + gWidth[gNum]), gHeight[gNum], textbgcolor); + } + } + + for (int32_t y = 0; y < gHeight[gNum]; y++) + { +#ifdef FONT_FS_AVAILABLE + if (fs_font) { + fontFile.read(pbuffer, gWidth[gNum]); + } +#endif + + for (int32_t x = 0; x < gWidth[gNum]; x++) + { +#ifdef FONT_FS_AVAILABLE + if (fs_font) pixel = pbuffer[x]; + else +#endif + pixel = pgm_read_byte(gPtr + gBitmap[gNum] + x + gWidth[gNum] * y); + + if (pixel) + { + if (bl) { drawFastHLine( bxs, y + cy, bl, bg); bl = 0; } + if (pixel != 0xFF) + { + if (fl) { + if (fl==1) drawPixel(fxs, y + cy, fg); + else drawFastHLine( fxs, y + cy, fl, fg); + fl = 0; + } + if (getBG) bg = readPixel(x + cx, y + cy); + drawPixel(x + cx, y + cy, alphaBlend(pixel, fg, bg)); + } + else + { + if (fl==0) fxs = x + cx; + fl++; + } + } + else + { + if (fl) { drawFastHLine( fxs, y + cy, fl, fg); fl = 0; } + if (_fillbg) { + if (x >= bx) { + if (bl==0) bxs = x + cx; + bl++; + } + } + } + } + if (fl) { drawFastHLine( fxs, y + cy, fl, fg); fl = 0; } + if (bl) { drawFastHLine( bxs, y + cy, bl, bg); bl = 0; } + } + + // Fill area below glyph + if (fillwidth > 0) { + fillheight = (cursor_y + gFont.yAdvance) - (cy + gHeight[gNum]); + if (fillheight > 0) { + fillRect(bg_cursor_x, cy + gHeight[gNum], fillwidth, fillheight, textbgcolor); + } + } + + if (pbuffer) free(pbuffer); + cursor_x += gxAdvance[gNum]; + + if (newSprite) + { + pushSprite(cx, cursor_y); + deleteSprite(); + } + } + else + { + // Not a Unicode in font so draw a rectangle and move on cursor + drawRect(cursor_x, cursor_y + gFont.maxAscent - gFont.ascent, gFont.spaceWidth, gFont.ascent, fg); + cursor_x += gFont.spaceWidth + 1; + } + bg_cursor_x = cursor_x; + last_cursor_x = cursor_x; +} + + +/*************************************************************************************** +** Function name: printToSprite +** Description: Write a string to the sprite cursor position +***************************************************************************************/ +void TFT_eSprite::printToSprite(String string) +{ + if(!fontLoaded) return; + printToSprite((char*)string.c_str(), string.length()); +} + + +/*************************************************************************************** +** Function name: printToSprite +** Description: Write a string to the sprite cursor position +***************************************************************************************/ +void TFT_eSprite::printToSprite(char *cbuffer, uint16_t len) //String string) +{ + if(!fontLoaded) return; + + uint16_t n = 0; + bool newSprite = !_created; + int16_t cursorX = _tft->cursor_x; + + if (newSprite) + { + int16_t sWidth = 0; + uint16_t index = 0; + bool first = true; + while (n < len) + { + uint16_t unicode = decodeUTF8((uint8_t*)cbuffer, &n, len - n); + if (getUnicodeIndex(unicode, &index)) + { + if (first) { + first = false; + sWidth -= gdX[index]; + cursorX += gdX[index]; + } + if (n == len) sWidth += ( gWidth[index] + gdX[index]); + else sWidth += gxAdvance[index]; + } + else sWidth += gFont.spaceWidth + 1; + } + + createSprite(sWidth, gFont.yAdvance); + + if (textcolor != textbgcolor) fillSprite(textbgcolor); + } + + n = 0; + + while (n < len) + { + uint16_t unicode = decodeUTF8((uint8_t*)cbuffer, &n, len - n); + //Serial.print("Decoded Unicode = 0x");Serial.println(unicode,HEX); + //Serial.print("n = ");Serial.println(n); + drawGlyph(unicode); + } + + if (newSprite) + { // The sprite had to be created so place at TFT cursor + pushSprite(cursorX, _tft->cursor_y); + deleteSprite(); + } +} + + +/*************************************************************************************** +** Function name: printToSprite +** Description: Print character in a Sprite, create sprite if needed +***************************************************************************************/ +int16_t TFT_eSprite::printToSprite(int16_t x, int16_t y, uint16_t index) +{ + bool newSprite = !_created; + int16_t sWidth = gWidth[index]; + + if (newSprite) + { + createSprite(sWidth, gFont.yAdvance); + + if (textcolor != textbgcolor) fillSprite(textbgcolor); + + drawGlyph(gUnicode[index]); + + pushSprite(x + gdX[index], y, textbgcolor); + deleteSprite(); + } + + else drawGlyph(gUnicode[index]); + + return gxAdvance[index]; +} +#endif diff --git a/lib/Seeed_GFX/Extensions/Sprite.h b/lib/Seeed_GFX/Extensions/Sprite.h new file mode 100644 index 0000000..67e29de --- /dev/null +++ b/lib/Seeed_GFX/Extensions/Sprite.h @@ -0,0 +1,188 @@ +/*************************************************************************************** +// The following class creates Sprites in RAM, graphics can then be drawn in the Sprite +// and rendered quickly onto the TFT screen. The class inherits the graphics functions +// from the TFT_eSPI class. Some functions are overridden by this class so that the +// graphics are written to the Sprite rather than the TFT. +***************************************************************************************/ + +class TFT_eSprite : public TFT_eSPI { + + public: + + explicit TFT_eSprite(TFT_eSPI *tft); + ~TFT_eSprite(void); + + // Create a sprite of width x height pixels, return a pointer to the RAM area + // Sketch can cast returned value to (uint16_t*) for 16-bit depth if needed + // RAM required is: + // - 1 bit per pixel for 1 bit colour depth + // - 1 nibble per pixel for 4-bit colour (with palette table) + // - 1 byte per pixel for 8-bit colour (332 RGB format) + // - 2 bytes per pixel for 16-bit color depth (565 RGB format) + void* createSprite(int16_t width, int16_t height, uint8_t frames = 1); + + // Returns a pointer to the sprite or nullptr if not created, user must cast to pointer type + void* getPointer(void); + + // Returns true if sprite has been created + bool created(void); + + // Delete the sprite to free up the RAM + void deleteSprite(void); + + // Select the frame buffer for graphics write (for 2 colour ePaper and DMA toggle buffer) + // Returns a pointer to the Sprite frame buffer + void* frameBuffer(int8_t f); + + // Set or get the colour depth to 1, 4, 8 or 16 bits. Can be used to change depth an existing + // sprite, but clears it to black, returns a new pointer if sprite is re-created. + void* setColorDepth(int8_t b); + int8_t getColorDepth(void); + + // Set the palette for a 4-bit depth sprite. Only the first 16 colours in the map are used. + void createPalette(uint16_t *palette = nullptr, uint8_t colors = 16); // Palette in RAM + void createPalette(const uint16_t *palette = nullptr, uint8_t colors = 16); // Palette in FLASH + + // Set a single palette index to the given color + void setPaletteColor(uint8_t index, uint16_t color); + + // Get the color at the given palette index + uint16_t getPaletteColor(uint8_t index); + + // Set foreground and background colours for 1 bit per pixel Sprite + void setBitmapColor(uint16_t fg, uint16_t bg); + + // Draw a single pixel at x,y + void drawPixel(int32_t x, int32_t y, uint32_t color); + + // Draw a single character in the GLCD or GFXFF font + void drawChar(int32_t x, int32_t y, uint16_t c, uint32_t color, uint32_t bg, uint8_t size), + + // Fill Sprite with a colour + fillSprite(uint32_t color), + + // Define a window to push 16-bit colour pixels into in a raster order + // Colours are converted to the set Sprite colour bit depth + setWindow(int32_t x0, int32_t y0, int32_t x1, int32_t y1), + // Push a color (aka singe pixel) to the sprite's set window area + pushColor(uint16_t color), + // Push len colors (pixels) to the sprite's set window area + pushColor(uint16_t color, uint32_t len), + // Push a pixel pre-formatted as a 1, 4, 8 or 16-bit colour (avoids conversion overhead) + writeColor(uint16_t color), + + // Set the scroll zone, top left corner at x,y with defined width and height + // The colour (optional, black is default) is used to fill the gap after the scroll + setScrollRect(int32_t x, int32_t y, int32_t w, int32_t h, uint16_t color = TFT_BLACK), + // Scroll the defined zone dx,dy pixels. Negative values left,up, positive right,down + // dy is optional (default is 0, so no up/down scroll). + // The sprite coordinate frame does not move because pixels are moved + scroll(int16_t dx, int16_t dy = 0), + + // Draw lines + drawLine(int32_t x0, int32_t y0, int32_t x1, int32_t y1, uint32_t color), + drawFastVLine(int32_t x, int32_t y, int32_t h, uint32_t color), + drawFastHLine(int32_t x, int32_t y, int32_t w, uint32_t color), + + // Fill a rectangular area with a color (aka draw a filled rectangle) + fillRect(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color); + + // Set the coordinate rotation of the Sprite (for 1bpp Sprites only) + // Note: this uses coordinate rotation and is primarily for ePaper which does not support + // CGRAM rotation (like TFT drivers do) within the displays internal hardware + void setRotation(uint8_t rotation); + uint8_t getRotation(void); + + // Push a rotated copy of Sprite to TFT with optional transparent colour + bool pushRotated(int16_t angle, uint32_t transp = 0x00FFFFFF); + // Push a rotated copy of Sprite to another different Sprite with optional transparent colour + bool pushRotated(TFT_eSprite *spr, int16_t angle, uint32_t transp = 0x00FFFFFF); + + // Get the TFT bounding box for a rotated copy of this Sprite + bool getRotatedBounds(int16_t angle, int16_t *min_x, int16_t *min_y, int16_t *max_x, int16_t *max_y); + // Get the destination Sprite bounding box for a rotated copy of this Sprite + bool getRotatedBounds(TFT_eSprite *spr, int16_t angle, int16_t *min_x, int16_t *min_y, + int16_t *max_x, int16_t *max_y); + // Bounding box support function + void getRotatedBounds(int16_t angle, int16_t w, int16_t h, int16_t xp, int16_t yp, + int16_t *min_x, int16_t *min_y, int16_t *max_x, int16_t *max_y); + + // Read the colour of a pixel at x,y and return value in 565 format + uint16_t readPixel(int32_t x0, int32_t y0); + + // return the numerical value of the pixel at x,y (used when scrolling) + // 16bpp = colour, 8bpp = byte, 4bpp = colour index, 1bpp = 1 or 0 + uint16_t readPixelValue(int32_t x, int32_t y); + + // Write an image (colour bitmap) to the sprite. + void pushImage(int32_t x0, int32_t y0, int32_t w, int32_t h, uint16_t *data, uint8_t sbpp = 0); + void pushImage(int32_t x0, int32_t y0, int32_t w, int32_t h, const uint16_t *data); + + // Push the sprite to the TFT screen, this fn calls pushImage() in the TFT class. + // Optionally a "transparent" colour can be defined, pixels of that colour will not be rendered + void pushSprite(int32_t x, int32_t y); + void pushSprite(int32_t x, int32_t y, uint16_t transparent); + + // Push a windowed area of the sprite to the TFT at tx, ty + bool pushSprite(int32_t tx, int32_t ty, int32_t sx, int32_t sy, int32_t sw, int32_t sh); + + // Push the sprite to another sprite at x,y. This fn calls pushImage() in the destination sprite (dspr) class. + bool pushToSprite(TFT_eSprite *dspr, int32_t x, int32_t y); + bool pushToSprite(TFT_eSprite *dspr, int32_t x, int32_t y, uint16_t transparent); + + // Draw a single character in the selected font + int16_t drawChar(uint16_t uniCode, int32_t x, int32_t y, uint8_t font), + drawChar(uint16_t uniCode, int32_t x, int32_t y); + + // Return the width and height of the sprite + int16_t width(void), + height(void); + + // Functions associated with anti-aliased fonts + // Draw a single Unicode character using the loaded font + void drawGlyph(uint16_t code); + // Print string to sprite using loaded font at cursor position + void printToSprite(String string); + // Print char array to sprite using loaded font at cursor position + void printToSprite(char *cbuffer, uint16_t len); + // Print indexed glyph to sprite using loaded font at x,y + int16_t printToSprite(int16_t x, int16_t y, uint16_t index); + + private: + + TFT_eSPI *_tft; + + // Reserve memory for the Sprite and return a pointer + void* callocSprite(int16_t width, int16_t height, uint8_t frames = 1); + + // Override the non-inlined TFT_eSPI functions + void begin_nin_write(void) { ; } + void end_nin_write(void) { ; } + + protected: + + uint8_t _bpp; // bits per pixel (1, 4, 8 or 16) + uint16_t *_img; // pointer to 16-bit sprite + uint8_t *_img8; // pointer to 1 and 8-bit sprite frame 1 or frame 2 + uint8_t *_img4; // pointer to 4-bit sprite (uses color map) + uint8_t *_img8_1; // pointer to frame 1 + uint8_t *_img8_2; // pointer to frame 2 + + uint16_t *_colorMap; // color map pointer: 16 entries, used with 4-bit color map. + + int32_t _sinra; // Sine of rotation angle in fixed point + int32_t _cosra; // Cosine of rotation angle in fixed point + + bool _created; // A Sprite has been created and memory reserved + bool _gFont = false; + + int32_t _xs, _ys, _xe, _ye, _xptr, _yptr; // for setWindow + int32_t _sx, _sy; // x,y for scroll zone + uint32_t _sw, _sh; // w,h for scroll zone + uint32_t _scolor; // gap fill colour for scroll zone + + int32_t _iwidth, _iheight; // Sprite memory image bit width and height (swapped during rotations) + int32_t _dwidth, _dheight; // Real sprite width and height (for <8bpp Sprites) + int32_t _bitwidth; // Sprite image bit width for drawPixel (for <8bpp Sprites, not swapped) + +}; diff --git a/lib/Seeed_GFX/Extensions/Tcon.cpp b/lib/Seeed_GFX/Extensions/Tcon.cpp new file mode 100644 index 0000000..32c1f47 --- /dev/null +++ b/lib/Seeed_GFX/Extensions/Tcon.cpp @@ -0,0 +1,524 @@ +#if defined(OPENDISPLAY_SEEED_GFX_RUNTIME_PINS) +#include +#include "OpenDisplay/opendisplay_runtime_pins.h" +#endif + +TWord reverse_bits_16(TWord x) { + x = (x & 0xAAAA) >> 1 | (x & 0x5555) << 1; + x = (x & 0xCCCC) >> 2 | (x & 0x3333) << 2; + x = (x & 0xF0F0) >> 4 | (x & 0x0F0F) << 4; + x = (x & 0xFF00) >> 8 | (x & 0x00FF) << 8; + return x; +} + + +TByte reverse_bits_8(TByte x) { + x = (x & 0xAA) >> 1 | (x & 0x55) << 1; + x = (x & 0xCC) >> 2 | (x & 0x33) << 2; + x = (x & 0xF0) >> 4 | (x & 0x0F) << 4; + return x; +} + +void TFT_eSPI::tconWaitForReady() +{ +#if defined(OPENDISPLAY_SEEED_GFX_RUNTIME_PINS) + if (opnd_seeed_tcon_busy_timed_out) { + return; + } + TByte ulData = digitalRead(opnd_seeed_runtime_busy); + uint32_t deadline = millis() + OPND_SEEED_TCON_BUSY_TIMEOUT_MS; + while (ulData == 0) { + if ((int32_t)(millis() - deadline) >= 0) { + opnd_seeed_tcon_busy_timed_out = true; + break; + } + ulData = digitalRead(opnd_seeed_runtime_busy); + } +#else + TByte ulData = digitalRead(TFT_BUSY); + while(ulData == 0) + { + ulData = digitalRead(TFT_BUSY); + } +#endif +} + + + +inline void TFT_eSPI::tconSendWord(TWord data) +{ + spi.transfer16(data); +} + +inline TWord TFT_eSPI::tconReceiveWord() +{ + TWord rxData = 0; + rxData = spi.transfer16(0); + return rxData; +} + +void TFT_eSPI::tconWriteCmdCode(TWord usCmdCode) +{ + //Set Preamble for Write Command + TWord wPreamble = 0x6000; + + //begin_tft_write(); + spi_begin(); + + tconWaitForReady(); + + tconSendWord(wPreamble); + + tconWaitForReady(); + tconSendWord(usCmdCode); + + //end_tft_write(); + spi_end(); + //delay_nop(); + + +} + +void TFT_eSPI::tconWirteData(TWord usData) +{ + TWord wPreamble = 0x0000; + spi_begin(); + + tconWaitForReady(); + tconSendWord(wPreamble); + + + tconWaitForReady(); + tconSendWord(usData); + + spi_end(); + //delay_nop(); + + +} + +void TFT_eSPI::tconWirteNData(TWord* pwBuf, TDWord ulSizeWordCnt) +{ + TWord wPreamble = 0x0000; + TDWord i, b = 0; + TDWord chunk_size = 16384; + TWord *EPD_temp = NULL; + spi_begin(); + tconWaitForReady(); + tconSendWord(wPreamble); + tconWaitForReady(); + //Send Data + + for (i = 0; i < (ulSizeWordCnt / chunk_size); i++) + { + + + EPD_temp = &pwBuf[b]; + if (DMA_Enabled) + pushPixelsDMA(EPD_temp, chunk_size); + else + pushPixels(EPD_temp, chunk_size); + // writenBytes(EPD_temp, chunk_size, 32768); + b += chunk_size; + } + + TDWord remainder = ulSizeWordCnt % chunk_size; + if (remainder > 0) + { + EPD_temp = &pwBuf[b]; + if (DMA_Enabled) + pushPixelsDMA(EPD_temp, remainder); + else + pushPixels(EPD_temp, remainder); + //writenBytes(EPD_temp, remainder, 32768); + } + // for(i=0;iusEndianType << 8 ) + |(pstLdImgInfo->usPixelFormat << 4) + |(pstLdImgInfo->usRotate); + //Send Cmd + tconWriteCmdCode(IT8951_TCON_LD_IMG); + //Send Arg + tconWirteData(usArg); + +} + +void TFT_eSPI::tconSetImgRotation(TDWord rotation) +{ + TWord arg = {(IT8951_LDIMG_B_ENDIAN << 8)|(IT8951_8BPP << 4)| rotation}; + tconWriteCmdCode(IT8951_TCON_LD_IMG); + tconWirteData((arg)); + +} + +void TFT_eSPI::tconLoadImgAreaStart(TCONLdImgInfo* pstLdImgInfo ,TCONAreaImgInfo* pstAreaImgInfo) +{ + TWord usArg[5]; + //Setting Argument for Load image start + usArg[0] = (pstLdImgInfo->usEndianType << 8 ) + |(pstLdImgInfo->usPixelFormat << 4) + |(pstLdImgInfo->usRotate); + + usArg[1] = pstAreaImgInfo->usX; + usArg[2] = pstAreaImgInfo->usY; + usArg[3] = pstAreaImgInfo->usWidth; + usArg[4] = pstAreaImgInfo->usHeight; + //Send Cmd and Args + tconSendCmdArg(IT8951_TCON_LD_IMG_AREA , usArg , 5); +} + +void TFT_eSPI::tconLoadImgEnd(void) +{ + tconWriteCmdCode(IT8951_TCON_LD_IMG_END); +} + +void TFT_eSPI::tconSetImgBufBaseAddr(TDWord ulImgBufAddr) +{ + TWord usWordH = (TWord)((ulImgBufAddr >> 16) & 0x0000FFFF); + TWord usWordL = (TWord)( ulImgBufAddr & 0x0000FFFF); + //Write LISAR Reg + tconWriteReg(LISAR + 2 ,usWordH); + tconWriteReg(LISAR ,usWordL); +} + + +void TFT_eSPI::tconHostAreaPackedPixelWrite(TCONLdImgInfo* pstLdImgInfo,TCONAreaImgInfo* pstAreaImgInfo) +{ + + TDWord i,j; + //Source buffer address of Host + TWord* pusFrameBuf = (TWord*)pstLdImgInfo->ulStartFBAddr; + //Set Image buffer(IT8951) Base address + tconSetImgBufBaseAddr(pstLdImgInfo->ulImgBufBaseAddr); + //Send Load Image start Cmd + tconLoadImgAreaStart(pstLdImgInfo , pstAreaImgInfo); + //printf("---IT8951 Host Area Packed Pixel Write begin---\r\n"); + //Host Write Data + + + uint16_t height = pstAreaImgInfo->usHeight; + uint16_t width = pstAreaImgInfo->usWidth; + if(pstLdImgInfo->usPixelFormat == IT8951_4BPP) + width = (width + 3) / 4; + else if(pstLdImgInfo->usPixelFormat == IT8951_8BPP) + width = (width + 1) / 2; + + TWord *mirroredFrameBuf = (TWord *)malloc(width * height * sizeof(TWord)); + if(mirroredFrameBuf == NULL) { + return; + } + + for (uint16_t j = 0; j < height; j++) { + for (uint16_t i = 0; i < width; i++) { + if(pstLdImgInfo->usFilp) + mirroredFrameBuf[j * width + i] = reverse_bits_16(pusFrameBuf[j * width + i]); + else + mirroredFrameBuf[j * width + i] = pusFrameBuf[j * width + (width - 1 - i)]; + } + } + tconWirteNData(mirroredFrameBuf, height * width ); + free(mirroredFrameBuf); + // for(j=0;j< pstAreaImgInfo->usHeight;j++) + // { + // for(i=0;i< pstAreaImgInfo->usWidth/2;i++) + // { + // if(pstLdImgInfo->usFilp) + // //Write a Word(2-Bytes) for each time + // tconWirteData(reverse_bits_16(pusFrameBuf[j * (pstAreaImgInfo->usWidth/2) + (pstAreaImgInfo->usWidth/2 + i)])); + // else + // tconWirteData((pusFrameBuf[j * (pstAreaImgInfo->usWidth/2) + (pstAreaImgInfo->usWidth/2 - i - 1)])); + // } + // } + //printf("---IT8951 Host Area Packed Pixel Write end---\r\n"); + //Send Load Img End Command + tconLoadImgEnd(); + //memset(pusFrameBufTemp, 0xf, pstAreaImgInfo->usHeight * pstAreaImgInfo->usWidth); +} + + + +void TFT_eSPI::tconDisplayArea(TWord usX, TWord usY, TWord usW, TWord usH, TWord usDpyMode) +{ + //Send I80 Display Command (User defined command of IT8951) + tconWriteCmdCode(USDEF_I80_CMD_DPY_AREA); //0x0034 + //Write arguments + tconWirteData(usX); + tconWirteData(usY); + tconWirteData(usW); + tconWirteData(usH); + + tconWirteData(usDpyMode); + + +} + +void TFT_eSPI::tconDisplayArea1bpp(TWord usX, TWord usY, TWord usW, TWord usH, TWord usDpyMode, TByte ucBGGrayVal, TByte ucFGGrayVal) +{ + usX = (_gstI80DevInfo.usPanelW - 1) - usX - usW + 1; + //Set Display mode to 1 bpp mode - Set 0x18001138 Bit[18](0x1800113A Bit[2])to 1 + tconWriteReg(UP1SR+2, tconReadReg(UP1SR+2) | (1<<2)); + + //Set BitMap color table 0 and 1 , => Set Register[0x18001250]: + //Bit[7:0]: ForeGround Color(G0~G15) for 1 + //Bit[15:8]:Background Color(G0~G15) for 0 + tconWriteReg(BGVR, (ucBGGrayVal<<8) | ucFGGrayVal); + + //Display + tconDisplayArea( usX, usY, usW, usH, usDpyMode); + + tconWaitForDisplayReady(); + + //Restore to normal mode + tconWriteReg(UP1SR+2, tconReadReg(UP1SR+2) & ~(1<<2)); + +} + + + +void TFT_eSPI::tconLoad1bppImage(const TByte* p1bppImgBuf, TWord usX, TWord usY, TWord usW, TWord usH, TByte enFilp) +{ + usX = (_gstI80DevInfo.usPanelW - 1) - usX - usW + 1; + TCONLdImgInfo stLdImgInfo; + TCONAreaImgInfo stAreaImgInfo; + + //Setting Load image information + stLdImgInfo.ulStartFBAddr = (TDWord) p1bppImgBuf; + stLdImgInfo.usEndianType = IT8951_LDIMG_L_ENDIAN; + stLdImgInfo.usPixelFormat = IT8951_8BPP; //we use 8bpp because IT8951 dose not support 1bpp mode for load image�Aso we use Load 8bpp mode ,but the transfer size needs to be reduced to Size/8 + stLdImgInfo.usRotate = IT8951_ROTATE_0; + stLdImgInfo.ulImgBufBaseAddr = _gulImgBufAddr; + stLdImgInfo.usFilp = enFilp; + //Set Load Area + stAreaImgInfo.usX = (usX + 7)/8; + stAreaImgInfo.usY = usY; + stAreaImgInfo.usWidth = (usW + 7)/8;//1bpp, Chaning Transfer size setting to 1/8X of 8bpp mode + stAreaImgInfo.usHeight = usH; + //printf("IT8951HostAreaPackedPixelWrite [wait]\n\r"); + //Load Image from Host to IT8951 Image Buffer + tconHostAreaPackedPixelWrite(&stLdImgInfo, &stAreaImgInfo);//Display function 2 +} + + +void TFT_eSPI::tconLoadImage(const TByte* pImgBuf, TWord usX, TWord usY, TWord usW, TWord usH, TByte enFilp) +{ + TCONLdImgInfo stLdImgInfo; + TCONAreaImgInfo stAreaImgInfo; + + //Setting Load image information + stLdImgInfo.ulStartFBAddr = (TDWord) pImgBuf; + stLdImgInfo.usEndianType = IT8951_LDIMG_L_ENDIAN; + stLdImgInfo.usPixelFormat = IT8951_4BPP; + stLdImgInfo.usRotate = IT8951_ROTATE_0; + stLdImgInfo.ulImgBufBaseAddr = _gulImgBufAddr; + stLdImgInfo.usFilp = enFilp; + //Set Load Area + stAreaImgInfo.usX = usX ; + stAreaImgInfo.usY = usY; + stAreaImgInfo.usWidth = usW ; + stAreaImgInfo.usHeight = usH; + //printf("IT8951HostAreaPackedPixelWrite [wait]\n\r"); + //Load Image from Host to IT8951 Image Buffer + tconHostAreaPackedPixelWrite(&stLdImgInfo, &stAreaImgInfo);//Display function 2 +} + + +void TFT_eSPI::getTconInfo(void* pBuf) +{ + TWord* pusWord = (TWord*)pBuf; + I80TCONDevInfo* pstDevInfo; + + tconWriteCmdCode(USDEF_I80_CMD_GET_DEV_INFO); + + //Burst Read Request for SPI interface only + tconReadNData(pusWord, sizeof(I80TCONDevInfo) / 2);//Polling HRDY for each words(2-bytes) if possible + + //Show Device information of IT8951 + pstDevInfo = (I80TCONDevInfo*)pBuf; + //printf("Panel(W,H) = (%d,%d)\r\n", + //pstDevInfo->usPanelW, pstDevInfo->usPanelH ); + //printf("Image Buffer Address = %X\r\n", + //pstDevInfo->usImgBufAddrL | (pstDevInfo->usImgBufAddrH << 16)); + //Show Firmware and LUT Version + //printf("FW Version = %s\r\n", (TByte*)pstDevInfo->usFWVersion); + //printf("LUT Version = %s\r\n", (TByte*)pstDevInfo->usLUTVersion); +} + +void TFT_eSPI::hostTconInit() +{ + + setTconVcom(1400); //SET VCOM + //setTconTemp(14); //SET TEMP + getTconInfo(&_gstI80DevInfo); + if (_gstI80DevInfo.usPanelW == 0 || _gstI80DevInfo.usPanelH == 0) { + println("Invalid panel size! Communication with IT8951 may have failed."); + return; + } + + _gulImgBufAddr = _gstI80DevInfo.usImgBufAddrL | (_gstI80DevInfo.usImgBufAddrH << 16); + //Set to Enable I80 Packed mode + tconWriteReg(I80CPCR, 0x0001); +} + +void TFT_eSPI::hostTconInitFast() +{ + //setTconVcom(1400); + getTconInfo(&_gstI80DevInfo); + if (_gstI80DevInfo.usPanelW == 0 || _gstI80DevInfo.usPanelH == 0) { + println("Invalid panel size! Communication with IT8951 may have failed."); + return; + } + + _gulImgBufAddr = _gstI80DevInfo.usImgBufAddrL | (_gstI80DevInfo.usImgBufAddrH << 16); + //tconWriteReg(I80CPCR, 0x0001); +} + +void TFT_eSPI::setTconWindowsData(TWord x1, TWord y1, TWord x2, TWord y2) +{ + _imgAreaInfo.usX = x1; + _imgAreaInfo.usY = y1; + _imgAreaInfo.usWidth = x2 - x1 + 1; + _imgAreaInfo.usHeight = y2 - y1 + 1; +} + +TWord TFT_eSPI::getTconTemp() +{ + tconWriteCmdCode(0x0040); + tconWirteData(0x00); + return (TByte)tconReadData(); + +} + + void TFT_eSPI::setTconTemp(TWord temp) + { + tconWriteCmdCode(0x0040); + tconWirteData(0x01); + tconWirteData(temp); + + } + + +TWord TFT_eSPI::getTconVcom() + { + tconWriteCmdCode(0x0039); + tconWirteData(0x00); + return tconReadData(); + } + + void TFT_eSPI::setTconVcom(TWord vcom) + { + tconWriteCmdCode(0x0039); + tconWirteData(0x02); + tconWirteData(vcom); + } + + +void TFT_eSPI::tconSleep() +{ + tconWriteCmdCode(IT8951_TCON_SLEEP); +} + +void TFT_eSPI::tconWake() +{ + tconWriteCmdCode(IT8951_TCON_SYS_RUN); +} + +void TFT_eSPI::tconStandby() +{ + tconWriteCmdCode(IT8951_TCON_STANDBY); +} + +void TFT_eSPI::tconWaitForDisplayReady() +{ + while(tconReadReg(LUTAFSR)); +} \ No newline at end of file diff --git a/lib/Seeed_GFX/Extensions/Tcon.h b/lib/Seeed_GFX/Extensions/Tcon.h new file mode 100644 index 0000000..cb96e45 --- /dev/null +++ b/lib/Seeed_GFX/Extensions/Tcon.h @@ -0,0 +1,93 @@ +public: + + typedef struct TCONLdImgInfo + { + TWord usEndianType; //little or Big Endian + TWord usPixelFormat; //bpp + TWord usRotate; //Rotate mode + TWord usFilp; //Filp mode + TDWord ulStartFBAddr; //Start address of source Frame buffer + TDWord ulImgBufBaseAddr;//Base address of target image buffer + + }TCONLdImgInfo; + + //structure prototype 2 + typedef struct TCONAreaImgInfo + { + TWord usX; + TWord usY; + TWord usWidth; + TWord usHeight; + + }TCONAreaImgInfo; + + typedef struct I80TCON1DevInfo + { + TWord usPanelW; + TWord usPanelH; + TWord usImgBufAddrL; + TWord usImgBufAddrH; + TWord usFWVersion[8]; //16 Bytes String + TWord usLUTVersion[8]; //16 Bytes String + }I80TCONDevInfo; + + I80TCONDevInfo _gstI80DevInfo; + TDWord _gulImgBufAddr; //IT8951 Image buffer address + + TCONAreaImgInfo _imgAreaInfo; + + + + void tconWaitForReady(); + + void tconSendWord(TWord data); + TWord tconReceiveWord(void); + + void tconWriteCmdCode(TWord usCmdCode); + void tconWirteData(TWord usData); + void tconWirteNData(TWord* pwBuf, TDWord ulSizeWordCnt); + + void tconSendCmdArg(TWord usCmdCode,TWord* pArg, TWord usNumArg); + TWord tconReadData(); + void tconReadNData(TWord* pwBuf, TDWord ulSizeWordCnt); + + TWord tconReadReg(TWord usRegAddr); + void tconWriteReg(TWord usRegAddr,TWord usValue); + + void tconLoadImgStart(TCONLdImgInfo* pstLdImgInfo); + void tconLoadImgAreaStart(TCONLdImgInfo* pstLdImgInfo ,TCONAreaImgInfo* pstAreaImgInfo); + void tconLoadImgEnd(void); + + void tconSetImgBufBaseAddr(TDWord ulImgBufAddr); + void tconSetImgRotation(TDWord rotation); + + void tconHostAreaPackedPixelWrite(TCONLdImgInfo* pstLdImgInfo,TCONAreaImgInfo* pstAreaImgInfo); + + + void tconDisplayArea(TWord usX, TWord usY, TWord usW, TWord usH, TWord usDpyMode); + void tconDisplayArea1bpp(TWord usX, TWord usY, TWord usW, TWord usH, TWord usDpyMode, TByte ucBGGrayVal, TByte ucFGGrayVal); + + void tconLoad1bppImage(const TByte* p1bppImgBuf, TWord usX, TWord usY, TWord usW, TWord usH, TByte enFilp); + void tconLoadImage(const TByte* pImgBuf, TWord usX, TWord usY, TWord usW, TWord usH, TByte enFilp); + + TWord getTconTemp(); + void setTconTemp(TWord temp); + + TWord getTconVcom(); + void setTconVcom(TWord vcom); + + void getTconInfo(void* pBuf); + void setTconWindowsData(TWord x1, TWord y1, TWord x2, TWord y2); + void hostTconInit(); + void hostTconInitFast(); + + void tconSleep(); + void tconWake(); + void tconStandby(); + void tconWaitForDisplayReady(); + + + + + + diff --git a/lib/Seeed_GFX/Extensions/Touch.cpp b/lib/Seeed_GFX/Extensions/Touch.cpp new file mode 100644 index 0000000..3d3ed82 --- /dev/null +++ b/lib/Seeed_GFX/Extensions/Touch.cpp @@ -0,0 +1,349 @@ +// The following touch screen support code by maxpautsch was merged 1/10/17 +// https://github.com/maxpautsch + +// Define TOUCH_CS is the user setup file to enable this code + +// A demo is provided in examples Generic folder + +// Additions by Bodmer to double sample, use Z value to improve detection reliability +// and to correct rotation handling + +// See license in root directory. + +// Define a default pressure threshold +#ifndef Z_THRESHOLD + #define Z_THRESHOLD 350 // Touch pressure threshold for validating touches +#endif + +/*************************************************************************************** +** Function name: begin_touch_read_write - was spi_begin_touch +** Description: Start transaction and select touch controller +***************************************************************************************/ +// The touch controller has a low SPI clock rate +inline void TFT_eSPI::begin_touch_read_write(void){ + DMA_BUSY_CHECK; + CS_H; // Just in case it has been left low + #if defined (SPI_HAS_TRANSACTION) && defined (SUPPORT_TRANSACTIONS) + if (locked) {locked = false; spi.beginTransaction(SPISettings(SPI_TOUCH_FREQUENCY, MSBFIRST, SPI_MODE0));} + #else + spi.setFrequency(SPI_TOUCH_FREQUENCY); + #endif + SET_BUS_READ_MODE; + T_CS_L; +} + +/*************************************************************************************** +** Function name: end_touch_read_write - was spi_end_touch +** Description: End transaction and deselect touch controller +***************************************************************************************/ +inline void TFT_eSPI::end_touch_read_write(void){ + T_CS_H; + #if defined (SPI_HAS_TRANSACTION) && defined (SUPPORT_TRANSACTIONS) + if(!inTransaction) {if (!locked) {locked = true; spi.endTransaction();}} + #else + spi.setFrequency(SPI_FREQUENCY); + #endif + //SET_BUS_WRITE_MODE; +} + +/*************************************************************************************** +** Function name: Legacy - deprecated +** Description: Start/end transaction +***************************************************************************************/ +void TFT_eSPI::spi_begin_touch() {begin_touch_read_write();} +void TFT_eSPI::spi_end_touch() { end_touch_read_write();} + +/*************************************************************************************** +** Function name: getTouchRaw +** Description: read raw touch position. Always returns true. +***************************************************************************************/ +uint8_t TFT_eSPI::getTouchRaw(uint16_t *x, uint16_t *y){ + uint16_t tmp; + + begin_touch_read_write(); + + // Start YP sample request for x position, read 4 times and keep last sample + spi.transfer(0xd0); // Start new YP conversion + spi.transfer(0); // Read first 8 bits + spi.transfer(0xd0); // Read last 8 bits and start new YP conversion + spi.transfer(0); // Read first 8 bits + spi.transfer(0xd0); // Read last 8 bits and start new YP conversion + spi.transfer(0); // Read first 8 bits + spi.transfer(0xd0); // Read last 8 bits and start new YP conversion + + tmp = spi.transfer(0); // Read first 8 bits + tmp = tmp <<5; + tmp |= 0x1f & (spi.transfer(0x90)>>3); // Read last 8 bits and start new XP conversion + + *x = tmp; + + // Start XP sample request for y position, read 4 times and keep last sample + spi.transfer(0); // Read first 8 bits + spi.transfer(0x90); // Read last 8 bits and start new XP conversion + spi.transfer(0); // Read first 8 bits + spi.transfer(0x90); // Read last 8 bits and start new XP conversion + spi.transfer(0); // Read first 8 bits + spi.transfer(0x90); // Read last 8 bits and start new XP conversion + + tmp = spi.transfer(0); // Read first 8 bits + tmp = tmp <<5; + tmp |= 0x1f & (spi.transfer(0)>>3); // Read last 8 bits + + *y = tmp; + + end_touch_read_write(); + + return true; +} + +/*************************************************************************************** +** Function name: getTouchRawZ +** Description: read raw pressure on touchpad and return Z value. +***************************************************************************************/ +uint16_t TFT_eSPI::getTouchRawZ(void){ + + begin_touch_read_write(); + + // Z sample request + int16_t tz = 0xFFF; + spi.transfer(0xb0); // Start new Z1 conversion + tz += spi.transfer16(0xc0) >> 3; // Read Z1 and start Z2 conversion + tz -= spi.transfer16(0x00) >> 3; // Read Z2 + + end_touch_read_write(); + + if (tz == 4095) tz = 0; + + return (uint16_t)tz; +} + +/*************************************************************************************** +** Function name: validTouch +** Description: read validated position. Return false if not pressed. +***************************************************************************************/ +#define _RAWERR 20 // Deadband error allowed in successive position samples +uint8_t TFT_eSPI::validTouch(uint16_t *x, uint16_t *y, uint16_t threshold){ + uint16_t x_tmp, y_tmp, x_tmp2, y_tmp2; + + // Wait until pressure stops increasing to debounce pressure + uint16_t z1 = 1; + uint16_t z2 = 0; + while (z1 > z2) + { + z2 = z1; + z1 = getTouchRawZ(); + delay(1); + } + + // Serial.print("Z = ");Serial.println(z1); + + if (z1 <= threshold) return false; + + getTouchRaw(&x_tmp,&y_tmp); + + // Serial.print("Sample 1 x,y = "); Serial.print(x_tmp);Serial.print(",");Serial.print(y_tmp); + // Serial.print(", Z = ");Serial.println(z1); + + delay(1); // Small delay to the next sample + if (getTouchRawZ() <= threshold) return false; + + delay(2); // Small delay to the next sample + getTouchRaw(&x_tmp2,&y_tmp2); + + // Serial.print("Sample 2 x,y = "); Serial.print(x_tmp2);Serial.print(",");Serial.println(y_tmp2); + // Serial.print("Sample difference = ");Serial.print(abs(x_tmp - x_tmp2));Serial.print(",");Serial.println(abs(y_tmp - y_tmp2)); + + if (abs(x_tmp - x_tmp2) > _RAWERR) return false; + if (abs(y_tmp - y_tmp2) > _RAWERR) return false; + + *x = x_tmp; + *y = y_tmp; + + return true; +} + +/*************************************************************************************** +** Function name: getTouch +** Description: read callibrated position. Return false if not pressed. +***************************************************************************************/ +uint8_t TFT_eSPI::getTouch(uint16_t *x, uint16_t *y, uint16_t threshold){ + uint16_t x_tmp, y_tmp; + + if (threshold<20) threshold = 20; + if (_pressTime > millis()) threshold=20; + + uint8_t n = 5; + uint8_t valid = 0; + while (n--) + { + if (validTouch(&x_tmp, &y_tmp, threshold)) valid++;; + } + + if (valid<1) { _pressTime = 0; return false; } + + _pressTime = millis() + 50; + + convertRawXY(&x_tmp, &y_tmp); + + if (x_tmp >= _width || y_tmp >= _height) return false; + + _pressX = x_tmp; + _pressY = y_tmp; + *x = _pressX; + *y = _pressY; + return valid; +} + +/*************************************************************************************** +** Function name: convertRawXY +** Description: convert raw touch x,y values to screen coordinates +***************************************************************************************/ +void TFT_eSPI::convertRawXY(uint16_t *x, uint16_t *y) +{ + uint16_t x_tmp = *x, y_tmp = *y, xx, yy; + + if(!touchCalibration_rotate){ + xx=(x_tmp-touchCalibration_x0)*_width/touchCalibration_x1; + yy=(y_tmp-touchCalibration_y0)*_height/touchCalibration_y1; + if(touchCalibration_invert_x) + xx = _width - xx; + if(touchCalibration_invert_y) + yy = _height - yy; + } else { + xx=(y_tmp-touchCalibration_x0)*_width/touchCalibration_x1; + yy=(x_tmp-touchCalibration_y0)*_height/touchCalibration_y1; + if(touchCalibration_invert_x) + xx = _width - xx; + if(touchCalibration_invert_y) + yy = _height - yy; + } + *x = xx; + *y = yy; +} + +/*************************************************************************************** +** Function name: calibrateTouch +** Description: generates calibration parameters for touchscreen. +***************************************************************************************/ +void TFT_eSPI::calibrateTouch(uint16_t *parameters, uint32_t color_fg, uint32_t color_bg, uint8_t size){ + int16_t values[] = {0,0,0,0,0,0,0,0}; + uint16_t x_tmp, y_tmp; + + + + for(uint8_t i = 0; i<4; i++){ + fillRect(0, 0, size+1, size+1, color_bg); + fillRect(0, _height-size-1, size+1, size+1, color_bg); + fillRect(_width-size-1, 0, size+1, size+1, color_bg); + fillRect(_width-size-1, _height-size-1, size+1, size+1, color_bg); + + if (i == 5) break; // used to clear the arrows + + switch (i) { + case 0: // up left + drawLine(0, 0, 0, size, color_fg); + drawLine(0, 0, size, 0, color_fg); + drawLine(0, 0, size , size, color_fg); + break; + case 1: // bot left + drawLine(0, _height-size-1, 0, _height-1, color_fg); + drawLine(0, _height-1, size, _height-1, color_fg); + drawLine(size, _height-size-1, 0, _height-1 , color_fg); + break; + case 2: // up right + drawLine(_width-size-1, 0, _width-1, 0, color_fg); + drawLine(_width-size-1, size, _width-1, 0, color_fg); + drawLine(_width-1, size, _width-1, 0, color_fg); + break; + case 3: // bot right + drawLine(_width-size-1, _height-size-1, _width-1, _height-1, color_fg); + drawLine(_width-1, _height-1-size, _width-1, _height-1, color_fg); + drawLine(_width-1-size, _height-1, _width-1, _height-1, color_fg); + break; + } + + // user has to get the chance to release + if(i>0) delay(1000); + + for(uint8_t j= 0; j<8; j++){ + // Use a lower detect threshold as corners tend to be less sensitive + while(!validTouch(&x_tmp, &y_tmp, Z_THRESHOLD/2)); + values[i*2 ] += x_tmp; + values[i*2+1] += y_tmp; + } + values[i*2 ] /= 8; + values[i*2+1] /= 8; + } + + + // from case 0 to case 1, the y value changed. + // If the measured delta of the touch x axis is bigger than the delta of the y axis, the touch and TFT axes are switched. + touchCalibration_rotate = false; + if(abs(values[0]-values[2]) > abs(values[1]-values[3])){ + touchCalibration_rotate = true; + touchCalibration_x0 = (values[1] + values[3])/2; // calc min x + touchCalibration_x1 = (values[5] + values[7])/2; // calc max x + touchCalibration_y0 = (values[0] + values[4])/2; // calc min y + touchCalibration_y1 = (values[2] + values[6])/2; // calc max y + } else { + touchCalibration_x0 = (values[0] + values[2])/2; // calc min x + touchCalibration_x1 = (values[4] + values[6])/2; // calc max x + touchCalibration_y0 = (values[1] + values[5])/2; // calc min y + touchCalibration_y1 = (values[3] + values[7])/2; // calc max y + } + + // in addition, the touch screen axis could be in the opposite direction of the TFT axis + touchCalibration_invert_x = false; + if(touchCalibration_x0 > touchCalibration_x1){ + values[0]=touchCalibration_x0; + touchCalibration_x0 = touchCalibration_x1; + touchCalibration_x1 = values[0]; + touchCalibration_invert_x = true; + } + touchCalibration_invert_y = false; + if(touchCalibration_y0 > touchCalibration_y1){ + values[0]=touchCalibration_y0; + touchCalibration_y0 = touchCalibration_y1; + touchCalibration_y1 = values[0]; + touchCalibration_invert_y = true; + } + + // pre calculate + touchCalibration_x1 -= touchCalibration_x0; + touchCalibration_y1 -= touchCalibration_y0; + + if(touchCalibration_x0 == 0) touchCalibration_x0 = 1; + if(touchCalibration_x1 == 0) touchCalibration_x1 = 1; + if(touchCalibration_y0 == 0) touchCalibration_y0 = 1; + if(touchCalibration_y1 == 0) touchCalibration_y1 = 1; + + // export parameters, if pointer valid + if(parameters != NULL){ + parameters[0] = touchCalibration_x0; + parameters[1] = touchCalibration_x1; + parameters[2] = touchCalibration_y0; + parameters[3] = touchCalibration_y1; + parameters[4] = touchCalibration_rotate | (touchCalibration_invert_x <<1) | (touchCalibration_invert_y <<2); + } +} + + +/*************************************************************************************** +** Function name: setTouch +** Description: imports calibration parameters for touchscreen. +***************************************************************************************/ +void TFT_eSPI::setTouch(uint16_t *parameters){ + touchCalibration_x0 = parameters[0]; + touchCalibration_x1 = parameters[1]; + touchCalibration_y0 = parameters[2]; + touchCalibration_y1 = parameters[3]; + + if(touchCalibration_x0 == 0) touchCalibration_x0 = 1; + if(touchCalibration_x1 == 0) touchCalibration_x1 = 1; + if(touchCalibration_y0 == 0) touchCalibration_y0 = 1; + if(touchCalibration_y1 == 0) touchCalibration_y1 = 1; + + touchCalibration_rotate = parameters[4] & 0x01; + touchCalibration_invert_x = parameters[4] & 0x02; + touchCalibration_invert_y = parameters[4] & 0x04; +} diff --git a/lib/Seeed_GFX/Extensions/Touch.h b/lib/Seeed_GFX/Extensions/Touch.h new file mode 100644 index 0000000..9bb81fc --- /dev/null +++ b/lib/Seeed_GFX/Extensions/Touch.h @@ -0,0 +1,42 @@ + // Coded by Bodmer 10/2/18, see license in root directory. + // This is part of the TFT_eSPI class and is associated with the Touch Screen handlers + + public: + // Get raw x,y ADC values from touch controller + uint8_t getTouchRaw(uint16_t *x, uint16_t *y); + // Get raw z (i.e. pressure) ADC value from touch controller + uint16_t getTouchRawZ(void); + // Convert raw x,y values to calibrated and correctly rotated screen coordinates + void convertRawXY(uint16_t *x, uint16_t *y); + // Get the screen touch coordinates, returns true if screen has been touched + // if the touch coordinates are off screen then x and y are not updated + // The returned value can be treated as a bool type, false or 0 means touch not detected + // In future the function may return an 8-bit "quality" (jitter) value. + // The threshold value is optional, this must be higher than the bias level for z (pressure) + // reported by Test_Touch_Controller when the screen is NOT touched. When touched the z value + // must be higher than the threshold for a touch to be detected. + uint8_t getTouch(uint16_t *x, uint16_t *y, uint16_t threshold = 600); + + // Run screen calibration and test, report calibration values to the serial port + void calibrateTouch(uint16_t *data, uint32_t color_fg, uint32_t color_bg, uint8_t size); + // Set the screen calibration values + void setTouch(uint16_t *data); + + private: + // Legacy support only - deprecated TODO: delete + void spi_begin_touch(); + void spi_end_touch(); + + // Handlers for the touch controller bus settings + inline void begin_touch_read_write() __attribute__((always_inline)); + inline void end_touch_read_write() __attribute__((always_inline)); + + // Private function to validate a touch, allow settle time and reduce spurious coordinates + uint8_t validTouch(uint16_t *x, uint16_t *y, uint16_t threshold = 600); + + // Initialise with example calibration values so processor does not crash if setTouch() not called in setup() + uint16_t touchCalibration_x0 = 300, touchCalibration_x1 = 3600, touchCalibration_y0 = 300, touchCalibration_y1 = 3600; + uint8_t touchCalibration_rotate = 1, touchCalibration_invert_x = 2, touchCalibration_invert_y = 0; + + uint32_t _pressTime; // Press and hold time-out + uint16_t _pressX, _pressY; // For future use (last sampled calibrated coordinates) diff --git a/lib/Seeed_GFX/OpenDisplay/opendisplay_runtime_pins.h b/lib/Seeed_GFX/OpenDisplay/opendisplay_runtime_pins.h new file mode 100644 index 0000000..8688c22 --- /dev/null +++ b/lib/Seeed_GFX/OpenDisplay/opendisplay_runtime_pins.h @@ -0,0 +1,28 @@ +#pragma once + +#include +#include + +#ifndef OPND_SEEED_TCON_BUSY_TIMEOUT_MS +#define OPND_SEEED_TCON_BUSY_TIMEOUT_MS 15000u +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +extern volatile bool opnd_seeed_tcon_busy_timed_out; +void opnd_seeed_tcon_busy_timeout_reset(void); + +extern int8_t opnd_seeed_runtime_sclk; +extern int8_t opnd_seeed_runtime_miso; +extern int8_t opnd_seeed_runtime_mosi; +extern int8_t opnd_seeed_runtime_cs; +extern int8_t opnd_seeed_runtime_rst; +extern int8_t opnd_seeed_runtime_busy; +extern int8_t opnd_seeed_runtime_tft_enable; +extern int8_t opnd_seeed_runtime_ite_enable; + +#ifdef __cplusplus +} +#endif diff --git a/lib/Seeed_GFX/Processors/TFT_eSPI_ESP32.c b/lib/Seeed_GFX/Processors/TFT_eSPI_ESP32.c new file mode 100644 index 0000000..faf3034 --- /dev/null +++ b/lib/Seeed_GFX/Processors/TFT_eSPI_ESP32.c @@ -0,0 +1,854 @@ + //////////////////////////////////////////////////// + // TFT_eSPI driver functions for ESP32 processors // + //////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////////////// +// Global variables +//////////////////////////////////////////////////////////////////////////////////////// + +// Select the SPI port to use, ESP32 has 2 options +#if !defined (TFT_PARALLEL_8_BIT) + #ifdef CONFIG_IDF_TARGET_ESP32 + #ifdef USE_HSPI_PORT + SPIClass spi = SPIClass(HSPI); + #elif defined(USE_FSPI_PORT) + SPIClass spi = SPIClass(FSPI); + #else // use default VSPI port + SPIClass spi = SPIClass(VSPI); + #endif + #else + #ifdef USE_HSPI_PORT + SPIClass spi = SPIClass(HSPI); + #elif defined(USE_FSPI_PORT) + SPIClass spi = SPIClass(FSPI); + #else // use FSPI port + SPIClass& spi = SPI; + #endif + #endif +#endif + +#ifdef ESP32_DMA + // DMA SPA handle + spi_device_handle_t dmaHAL; + #ifdef CONFIG_IDF_TARGET_ESP32 + #define DMA_CHANNEL 1 + #ifdef USE_HSPI_PORT + spi_host_device_t spi_host = HSPI_HOST; + #elif defined(USE_FSPI_PORT) + spi_host_device_t spi_host = SPI_HOST; + #else // use VSPI port + spi_host_device_t spi_host = VSPI_HOST; + #endif + #else + #ifdef USE_HSPI_PORT + #define DMA_CHANNEL SPI_DMA_CH_AUTO + spi_host_device_t spi_host = (spi_host_device_t) SPI3_HOST; // Draws once then freezes + #else // use FSPI port + #define DMA_CHANNEL SPI_DMA_CH_AUTO + spi_host_device_t spi_host = (spi_host_device_t) SPI2_HOST; // Draws once then freezes + #endif + #endif +#endif + +#if !defined (TFT_PARALLEL_8_BIT) + // Volatile for register reads: + volatile uint32_t* _spi_cmd = (volatile uint32_t*)(SPI_CMD_REG(SPI_PORT)); + volatile uint32_t* _spi_user = (volatile uint32_t*)(SPI_USER_REG(SPI_PORT)); + // Register writes only: + volatile uint32_t* _spi_mosi_dlen = (volatile uint32_t*)(SPI_MOSI_DLEN_REG(SPI_PORT)); + volatile uint32_t* _spi_w = (volatile uint32_t*)(SPI_W0_REG(SPI_PORT)); +#endif + +//////////////////////////////////////////////////////////////////////////////////////// +#if defined (TFT_SDA_READ) && !defined (TFT_PARALLEL_8_BIT) +//////////////////////////////////////////////////////////////////////////////////////// + +/*************************************************************************************** +** Function name: beginSDA - VSPI port only, FPSI port only for S2 +** Description: Detach MOSI and attach MISO to SDA for reads +***************************************************************************************/ +void TFT_eSPI::begin_SDA_Read(void) +{ + gpio_set_direction((gpio_num_t)TFT_MOSI, GPIO_MODE_INPUT); + #ifdef CONFIG_IDF_TARGET_ESP32 + pinMatrixInAttach(TFT_MOSI, VSPIQ_IN_IDX, false); + #else // S2 + pinMatrixInAttach(TFT_MOSI, FSPIQ_IN_IDX, false); + #endif + SET_BUS_READ_MODE; +} + +/*************************************************************************************** +** Function name: endSDA - VSPI port only, FPSI port only for S2 +** Description: Attach MOSI to SDA and detach MISO for writes +***************************************************************************************/ +void TFT_eSPI::end_SDA_Read(void) +{ + gpio_set_direction((gpio_num_t)TFT_MOSI, GPIO_MODE_OUTPUT); + #ifdef CONFIG_IDF_TARGET_ESP32 + pinMatrixOutAttach(TFT_MOSI, VSPID_OUT_IDX, false, false); + #else // S2 + pinMatrixOutAttach(TFT_MOSI, FSPID_OUT_IDX, false, false); + #endif + SET_BUS_WRITE_MODE; +} + +//////////////////////////////////////////////////////////////////////////////////////// +#endif // #if defined (TFT_SDA_READ) +//////////////////////////////////////////////////////////////////////////////////////// + + +/*************************************************************************************** +** Function name: read byte - supports class functions +** Description: Read a byte from ESP32 8-bit data port +***************************************************************************************/ +// Parallel bus MUST be set to input before calling this function! +uint8_t TFT_eSPI::readByte(void) +{ + uint8_t b = 0xAA; + +#if defined (TFT_PARALLEL_8_BIT) + RD_L; + uint32_t reg; // Read all GPIO pins 0-31 + reg = gpio_input_get(); // Read three times to allow for bus access time + reg = gpio_input_get(); + reg = gpio_input_get(); // Data should be stable now + RD_H; + + // Check GPIO bits used and build value + b = (((reg>>TFT_D0)&1) << 0); + b |= (((reg>>TFT_D1)&1) << 1); + b |= (((reg>>TFT_D2)&1) << 2); + b |= (((reg>>TFT_D3)&1) << 3); + b |= (((reg>>TFT_D4)&1) << 4); + b |= (((reg>>TFT_D5)&1) << 5); + b |= (((reg>>TFT_D6)&1) << 6); + b |= (((reg>>TFT_D7)&1) << 7); +#endif + + return b; +} + +//////////////////////////////////////////////////////////////////////////////////////// +#ifdef TFT_PARALLEL_8_BIT +//////////////////////////////////////////////////////////////////////////////////////// + +/*************************************************************************************** +** Function name: GPIO direction control - supports class functions +** Description: Set parallel bus to INPUT or OUTPUT +***************************************************************************************/ +void TFT_eSPI::busDir(uint32_t mask, uint8_t mode) +{ + // Arduino generic native function + pinMode(TFT_D0, mode); + pinMode(TFT_D1, mode); + pinMode(TFT_D2, mode); + pinMode(TFT_D3, mode); + pinMode(TFT_D4, mode); + pinMode(TFT_D5, mode); + pinMode(TFT_D6, mode); + pinMode(TFT_D7, mode); +} + +/*************************************************************************************** +** Function name: GPIO direction control - supports class functions +** Description: Set ESP32 GPIO pin to input or output (set high) ASAP +***************************************************************************************/ +void TFT_eSPI::gpioMode(uint8_t gpio, uint8_t mode) +{ + pinMode(gpio, mode); + digitalWrite(gpio, HIGH); +} +//////////////////////////////////////////////////////////////////////////////////////// +#endif // #ifdef TFT_PARALLEL_8_BIT +//////////////////////////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////////////////////////// +#if defined (RPI_WRITE_STROBE) && !defined (TFT_PARALLEL_8_BIT) // Code for RPi TFT +//////////////////////////////////////////////////////////////////////////////////////// + +/*************************************************************************************** +** Function name: pushBlock - for ESP32 or ESP8266 RPi TFT +** Description: Write a block of pixels of the same colour +***************************************************************************************/ +void TFT_eSPI::pushBlock(uint16_t color, uint32_t len) +{ + uint8_t colorBin[] = { (uint8_t) (color >> 8), (uint8_t) color }; + if(len) spi.writePattern(&colorBin[0], 2, 1); len--; + while(len--) {WR_L; WR_H;} +} + +/*************************************************************************************** +** Function name: pushPixels - for ESP32 or ESP8266 RPi TFT +** Description: Write a sequence of pixels +***************************************************************************************/ +void TFT_eSPI::pushPixels(const void* data_in, uint32_t len) +{ + uint8_t *data = (uint8_t*)data_in; + + if(_swapBytes) { + while ( len-- ) {tft_Write_16(*data); data++;} + return; + } + + while ( len >=64 ) {spi.writePattern(data, 64, 1); data += 64; len -= 64; } + if (len) spi.writePattern(data, len, 1); +} + +//////////////////////////////////////////////////////////////////////////////////////// +#elif !defined (SPI_18BIT_DRIVER) && !defined (TFT_PARALLEL_8_BIT) // Most SPI displays +//////////////////////////////////////////////////////////////////////////////////////// + +/*************************************************************************************** +** Function name: pushBlock - for ESP32 +** Description: Write a block of pixels of the same colour +***************************************************************************************/ +/* +void TFT_eSPI::pushBlock(uint16_t color, uint32_t len){ + + uint32_t color32 = (color<<8 | color >>8)<<16 | (color<<8 | color >>8); + bool empty = true; + volatile uint32_t* spi_w = (volatile uint32_t*)_spi_w; + if (len > 31) + { + *_spi_mosi_dlen = 511; + spi_w[0] = color32; + spi_w[1] = color32; + spi_w[2] = color32; + spi_w[3] = color32; + spi_w[4] = color32; + spi_w[5] = color32; + spi_w[6] = color32; + spi_w[7] = color32; + spi_w[8] = color32; + spi_w[9] = color32; + spi_w[10] = color32; + spi_w[11] = color32; + spi_w[12] = color32; + spi_w[13] = color32; + spi_w[14] = color32; + spi_w[15] = color32; + while(len>31) + { + while ((*_spi_cmd)&SPI_USR); + *_spi_cmd = SPI_USR; + len -= 32; + } + empty = false; + } + + if (len) + { + if(empty) { + for (uint32_t i=0; i <= len; i+=2) *spi_w++ = color32; + } + len = (len << 4) - 1; + while (*_spi_cmd&SPI_USR); + *_spi_mosi_dlen = len; + *_spi_cmd = SPI_USR; + } + while ((*_spi_cmd)&SPI_USR); // Move to later in code to use transmit time usefully? +} +//*/ +//* +void TFT_eSPI::pushBlock(uint16_t color, uint32_t len){ + + volatile uint32_t* spi_w = _spi_w; + uint32_t color32 = (color<<8 | color >>8)<<16 | (color<<8 | color >>8); + uint32_t i = 0; + uint32_t rem = len & 0x1F; + len = len - rem; + + // Start with partial buffer pixels + if (rem) + { + while (*_spi_cmd&SPI_USR); + for (i=0; i < rem; i+=2) *spi_w++ = color32; + *_spi_mosi_dlen = (rem << 4) - 1; + *_spi_cmd = SPI_USR; + if (!len) return; //{while (*_spi_cmd&SPI_USR); return; } + i = i>>1; while(i++<16) *spi_w++ = color32; + } + + while (*_spi_cmd&SPI_USR); + if (!rem) while (i++<16) *spi_w++ = color32; + *_spi_mosi_dlen = 511; + + // End with full buffer to maximise useful time for downstream code + while(len) + { + while (*_spi_cmd&SPI_USR); + *_spi_cmd = SPI_USR; + len -= 32; + } + + // Do not wait here + //while (*_spi_cmd&SPI_USR); +} +//*/ +/*************************************************************************************** +** Function name: pushSwapBytePixels - for ESP32 +** Description: Write a sequence of pixels with swapped bytes +***************************************************************************************/ +void TFT_eSPI::pushSwapBytePixels(const void* data_in, uint32_t len){ + + uint8_t* data = (uint8_t*)data_in; + uint32_t color[16]; + + if (len > 31) + { + WRITE_PERI_REG(SPI_MOSI_DLEN_REG(SPI_PORT), 511); + while(len>31) + { + uint32_t i = 0; + while(i<16) + { + color[i++] = DAT8TO32(data); + data+=4; + } + while (READ_PERI_REG(SPI_CMD_REG(SPI_PORT))&SPI_USR); + WRITE_PERI_REG(SPI_W0_REG(SPI_PORT), color[0]); + WRITE_PERI_REG(SPI_W1_REG(SPI_PORT), color[1]); + WRITE_PERI_REG(SPI_W2_REG(SPI_PORT), color[2]); + WRITE_PERI_REG(SPI_W3_REG(SPI_PORT), color[3]); + WRITE_PERI_REG(SPI_W4_REG(SPI_PORT), color[4]); + WRITE_PERI_REG(SPI_W5_REG(SPI_PORT), color[5]); + WRITE_PERI_REG(SPI_W6_REG(SPI_PORT), color[6]); + WRITE_PERI_REG(SPI_W7_REG(SPI_PORT), color[7]); + WRITE_PERI_REG(SPI_W8_REG(SPI_PORT), color[8]); + WRITE_PERI_REG(SPI_W9_REG(SPI_PORT), color[9]); + WRITE_PERI_REG(SPI_W10_REG(SPI_PORT), color[10]); + WRITE_PERI_REG(SPI_W11_REG(SPI_PORT), color[11]); + WRITE_PERI_REG(SPI_W12_REG(SPI_PORT), color[12]); + WRITE_PERI_REG(SPI_W13_REG(SPI_PORT), color[13]); + WRITE_PERI_REG(SPI_W14_REG(SPI_PORT), color[14]); + WRITE_PERI_REG(SPI_W15_REG(SPI_PORT), color[15]); + SET_PERI_REG_MASK(SPI_CMD_REG(SPI_PORT), SPI_USR); + len -= 32; + } + } + + if (len > 15) + { + uint32_t i = 0; + while(i<8) + { + color[i++] = DAT8TO32(data); + data+=4; + } + while (READ_PERI_REG(SPI_CMD_REG(SPI_PORT))&SPI_USR); + WRITE_PERI_REG(SPI_MOSI_DLEN_REG(SPI_PORT), 255); + WRITE_PERI_REG(SPI_W0_REG(SPI_PORT), color[0]); + WRITE_PERI_REG(SPI_W1_REG(SPI_PORT), color[1]); + WRITE_PERI_REG(SPI_W2_REG(SPI_PORT), color[2]); + WRITE_PERI_REG(SPI_W3_REG(SPI_PORT), color[3]); + WRITE_PERI_REG(SPI_W4_REG(SPI_PORT), color[4]); + WRITE_PERI_REG(SPI_W5_REG(SPI_PORT), color[5]); + WRITE_PERI_REG(SPI_W6_REG(SPI_PORT), color[6]); + WRITE_PERI_REG(SPI_W7_REG(SPI_PORT), color[7]); + SET_PERI_REG_MASK(SPI_CMD_REG(SPI_PORT), SPI_USR); + len -= 16; + } + + if (len) + { + while (READ_PERI_REG(SPI_CMD_REG(SPI_PORT))&SPI_USR); + WRITE_PERI_REG(SPI_MOSI_DLEN_REG(SPI_PORT), (len << 4) - 1); + for (uint32_t i=0; i <= (len<<1); i+=4) { + WRITE_PERI_REG(SPI_W0_REG(SPI_PORT)+i, DAT8TO32(data)); data+=4; + } + SET_PERI_REG_MASK(SPI_CMD_REG(SPI_PORT), SPI_USR); + } + while (READ_PERI_REG(SPI_CMD_REG(SPI_PORT))&SPI_USR); + +} + +/*************************************************************************************** +** Function name: pushPixels - for ESP32 +** Description: Write a sequence of pixels +***************************************************************************************/ +void TFT_eSPI::pushPixels(const void* data_in, uint32_t len){ + + if(_swapBytes) { + pushSwapBytePixels(data_in, len); + return; + } + + uint32_t *data = (uint32_t*)data_in; + + if (len > 31) + { + WRITE_PERI_REG(SPI_MOSI_DLEN_REG(SPI_PORT), 511); + while(len>31) + { + while (READ_PERI_REG(SPI_CMD_REG(SPI_PORT))&SPI_USR); + WRITE_PERI_REG(SPI_W0_REG(SPI_PORT), *data++); + WRITE_PERI_REG(SPI_W1_REG(SPI_PORT), *data++); + WRITE_PERI_REG(SPI_W2_REG(SPI_PORT), *data++); + WRITE_PERI_REG(SPI_W3_REG(SPI_PORT), *data++); + WRITE_PERI_REG(SPI_W4_REG(SPI_PORT), *data++); + WRITE_PERI_REG(SPI_W5_REG(SPI_PORT), *data++); + WRITE_PERI_REG(SPI_W6_REG(SPI_PORT), *data++); + WRITE_PERI_REG(SPI_W7_REG(SPI_PORT), *data++); + WRITE_PERI_REG(SPI_W8_REG(SPI_PORT), *data++); + WRITE_PERI_REG(SPI_W9_REG(SPI_PORT), *data++); + WRITE_PERI_REG(SPI_W10_REG(SPI_PORT), *data++); + WRITE_PERI_REG(SPI_W11_REG(SPI_PORT), *data++); + WRITE_PERI_REG(SPI_W12_REG(SPI_PORT), *data++); + WRITE_PERI_REG(SPI_W13_REG(SPI_PORT), *data++); + WRITE_PERI_REG(SPI_W14_REG(SPI_PORT), *data++); + WRITE_PERI_REG(SPI_W15_REG(SPI_PORT), *data++); + SET_PERI_REG_MASK(SPI_CMD_REG(SPI_PORT), SPI_USR); + len -= 32; + } + } + + if (len) + { + while (READ_PERI_REG(SPI_CMD_REG(SPI_PORT))&SPI_USR); + WRITE_PERI_REG(SPI_MOSI_DLEN_REG(SPI_PORT), (len << 4) - 1); + for (uint32_t i=0; i <= (len<<1); i+=4) WRITE_PERI_REG((SPI_W0_REG(SPI_PORT) + i), *data++); + SET_PERI_REG_MASK(SPI_CMD_REG(SPI_PORT), SPI_USR); + } + while (READ_PERI_REG(SPI_CMD_REG(SPI_PORT))&SPI_USR); +} + +//////////////////////////////////////////////////////////////////////////////////////// +#elif defined (SPI_18BIT_DRIVER) // SPI 18-bit colour +//////////////////////////////////////////////////////////////////////////////////////// + +/*************************************************************************************** +** Function name: pushBlock - for ESP32 and 3 byte RGB display +** Description: Write a block of pixels of the same colour +***************************************************************************************/ +void TFT_eSPI::pushBlock(uint16_t color, uint32_t len) +{ + // Split out the colours + uint32_t r = (color & 0xF800)>>8; + uint32_t g = (color & 0x07E0)<<5; + uint32_t b = (color & 0x001F)<<19; + // Concatenate 4 pixels into three 32-bit blocks + uint32_t r0 = r<<24 | b | g | r; + uint32_t r1 = r0>>8 | g<<16; + uint32_t r2 = r1>>8 | b<<8; + + if (len > 19) + { + SET_PERI_REG_BITS(SPI_MOSI_DLEN_REG(SPI_PORT), SPI_USR_MOSI_DBITLEN, 479, SPI_USR_MOSI_DBITLEN_S); + + while(len>19) + { + while (READ_PERI_REG(SPI_CMD_REG(SPI_PORT))&SPI_USR); + WRITE_PERI_REG(SPI_W0_REG(SPI_PORT), r0); + WRITE_PERI_REG(SPI_W1_REG(SPI_PORT), r1); + WRITE_PERI_REG(SPI_W2_REG(SPI_PORT), r2); + WRITE_PERI_REG(SPI_W3_REG(SPI_PORT), r0); + WRITE_PERI_REG(SPI_W4_REG(SPI_PORT), r1); + WRITE_PERI_REG(SPI_W5_REG(SPI_PORT), r2); + WRITE_PERI_REG(SPI_W6_REG(SPI_PORT), r0); + WRITE_PERI_REG(SPI_W7_REG(SPI_PORT), r1); + WRITE_PERI_REG(SPI_W8_REG(SPI_PORT), r2); + WRITE_PERI_REG(SPI_W9_REG(SPI_PORT), r0); + WRITE_PERI_REG(SPI_W10_REG(SPI_PORT), r1); + WRITE_PERI_REG(SPI_W11_REG(SPI_PORT), r2); + WRITE_PERI_REG(SPI_W12_REG(SPI_PORT), r0); + WRITE_PERI_REG(SPI_W13_REG(SPI_PORT), r1); + WRITE_PERI_REG(SPI_W14_REG(SPI_PORT), r2); + SET_PERI_REG_MASK(SPI_CMD_REG(SPI_PORT), SPI_USR); + len -= 20; + } + while (READ_PERI_REG(SPI_CMD_REG(SPI_PORT))&SPI_USR); + } + + if (len) + { + SET_PERI_REG_BITS(SPI_MOSI_DLEN_REG(SPI_PORT), SPI_USR_MOSI_DBITLEN, (len * 24) - 1, SPI_USR_MOSI_DBITLEN_S); + WRITE_PERI_REG(SPI_W0_REG(SPI_PORT), r0); + WRITE_PERI_REG(SPI_W1_REG(SPI_PORT), r1); + WRITE_PERI_REG(SPI_W2_REG(SPI_PORT), r2); + WRITE_PERI_REG(SPI_W3_REG(SPI_PORT), r0); + WRITE_PERI_REG(SPI_W4_REG(SPI_PORT), r1); + WRITE_PERI_REG(SPI_W5_REG(SPI_PORT), r2); + if (len > 8 ) + { + WRITE_PERI_REG(SPI_W6_REG(SPI_PORT), r0); + WRITE_PERI_REG(SPI_W7_REG(SPI_PORT), r1); + WRITE_PERI_REG(SPI_W8_REG(SPI_PORT), r2); + WRITE_PERI_REG(SPI_W9_REG(SPI_PORT), r0); + WRITE_PERI_REG(SPI_W10_REG(SPI_PORT), r1); + WRITE_PERI_REG(SPI_W11_REG(SPI_PORT), r2); + WRITE_PERI_REG(SPI_W12_REG(SPI_PORT), r0); + WRITE_PERI_REG(SPI_W13_REG(SPI_PORT), r1); + WRITE_PERI_REG(SPI_W14_REG(SPI_PORT), r2); + } + + SET_PERI_REG_MASK(SPI_CMD_REG(SPI_PORT), SPI_USR); + while (READ_PERI_REG(SPI_CMD_REG(SPI_PORT))&SPI_USR); + } +} + +/*************************************************************************************** +** Function name: pushPixels - for ESP32 and 3 byte RGB display +** Description: Write a sequence of pixels +***************************************************************************************/ +void TFT_eSPI::pushPixels(const void* data_in, uint32_t len){ + + uint16_t *data = (uint16_t*)data_in; + // ILI9488 write macro is not endianess dependant, hence !_swapBytes + if(!_swapBytes) { while ( len-- ) {tft_Write_16S(*data); data++;} } + else { while ( len-- ) {tft_Write_16(*data); data++;} } +} + +/*************************************************************************************** +** Function name: pushSwapBytePixels - for ESP32 and 3 byte RGB display +** Description: Write a sequence of pixels with swapped bytes +***************************************************************************************/ +void TFT_eSPI::pushSwapBytePixels(const void* data_in, uint32_t len){ + + uint16_t *data = (uint16_t*)data_in; + // ILI9488 write macro is not endianess dependant, so swap byte macro not used here + while ( len-- ) {tft_Write_16(*data); data++;} +} + +//////////////////////////////////////////////////////////////////////////////////////// +#elif defined (TFT_PARALLEL_8_BIT) // Now the code for ESP32 8-bit parallel +//////////////////////////////////////////////////////////////////////////////////////// + +/*************************************************************************************** +** Function name: pushBlock - for ESP32 and parallel display +** Description: Write a block of pixels of the same colour +***************************************************************************************/ +void TFT_eSPI::pushBlock(uint16_t color, uint32_t len){ + #if defined (SSD1963_DRIVER) + if ( ((color & 0xF800)>> 8) == ((color & 0x07E0)>> 3) && ((color & 0xF800)>> 8)== ((color & 0x001F)<< 3) ) + #else + if ( (color >> 8) == (color & 0x00FF) ) + #endif + { if (!len) return; + tft_Write_16(color); + #if defined (SSD1963_DRIVER) + while (--len) {WR_L; WR_H; WR_L; WR_H; WR_L; WR_H;} + #else + #ifdef PSEUDO_16_BIT + while (--len) {WR_L; WR_H;} + #else + while (--len) {WR_L; WR_H; WR_L; WR_H;} + #endif + #endif + } + else while (len--) {tft_Write_16(color);} +} + +/*************************************************************************************** +** Function name: pushSwapBytePixels - for ESP32 and parallel display +** Description: Write a sequence of pixels with swapped bytes +***************************************************************************************/ +void TFT_eSPI::pushSwapBytePixels(const void* data_in, uint32_t len){ + + uint16_t *data = (uint16_t*)data_in; + while ( len-- ) {tft_Write_16(*data); data++;} +} + +/*************************************************************************************** +** Function name: pushPixels - for ESP32 and parallel display +** Description: Write a sequence of pixels +***************************************************************************************/ +void TFT_eSPI::pushPixels(const void* data_in, uint32_t len){ + + uint16_t *data = (uint16_t*)data_in; + if(_swapBytes) { while ( len-- ) {tft_Write_16(*data); data++; } } + else { while ( len-- ) {tft_Write_16S(*data); data++;} } +} + +//////////////////////////////////////////////////////////////////////////////////////// +#endif // End of display interface specific functions +//////////////////////////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////////////////////////// +#if defined (ESP32_DMA) && !defined (TFT_PARALLEL_8_BIT) // DMA FUNCTIONS +//////////////////////////////////////////////////////////////////////////////////////// + +/*************************************************************************************** +** Function name: dmaBusy +** Description: Check if DMA is busy +***************************************************************************************/ +bool TFT_eSPI::dmaBusy(void) +{ + if (!DMA_Enabled || !spiBusyCheck) return false; + + spi_transaction_t *rtrans; + esp_err_t ret; + uint8_t checks = spiBusyCheck; + for (int i = 0; i < checks; ++i) + { + ret = spi_device_get_trans_result(dmaHAL, &rtrans, 0); + if (ret == ESP_OK) spiBusyCheck--; + } + + //Serial.print("spiBusyCheck=");Serial.println(spiBusyCheck); + if (spiBusyCheck ==0) return false; + return true; +} + + +/*************************************************************************************** +** Function name: dmaWait +** Description: Wait until DMA is over (blocking!) +***************************************************************************************/ +void TFT_eSPI::dmaWait(void) +{ + if (!DMA_Enabled || !spiBusyCheck) return; + spi_transaction_t *rtrans; + esp_err_t ret; + for (int i = 0; i < spiBusyCheck; ++i) + { + ret = spi_device_get_trans_result(dmaHAL, &rtrans, portMAX_DELAY); + assert(ret == ESP_OK); + } + spiBusyCheck = 0; +} + + +/*************************************************************************************** +** Function name: pushPixelsDMA +** Description: Push pixels to TFT (len must be less than 32767) +***************************************************************************************/ +// This will byte swap the original image if setSwapBytes(true) was called by sketch. +void TFT_eSPI::pushPixelsDMA(uint16_t* image, uint32_t len) +{ + if ((len == 0) || (!DMA_Enabled)) return; + + dmaWait(); + + if(_swapBytes) { + for (uint32_t i = 0; i < len; i++) (image[i] = image[i] << 8 | image[i] >> 8); + } + + esp_err_t ret; + static spi_transaction_t trans; + + memset(&trans, 0, sizeof(spi_transaction_t)); + + trans.user = (void *)1; + trans.tx_buffer = image; //finally send the line data + trans.length = len * 16; //Data length, in bits + trans.flags = 0; //SPI_TRANS_USE_TXDATA flag + + ret = spi_device_queue_trans(dmaHAL, &trans, portMAX_DELAY); + assert(ret == ESP_OK); + + spiBusyCheck++; +} + + +/*************************************************************************************** +** Function name: pushImageDMA +** Description: Push image to a window (w*h must be less than 65536) +***************************************************************************************/ +// Fixed const data assumed, will NOT clip or swap bytes +void TFT_eSPI::pushImageDMA(int32_t x, int32_t y, int32_t w, int32_t h, uint16_t const* image) +{ + if ((w == 0) || (h == 0) || (!DMA_Enabled)) return; + + uint32_t len = w*h; + + dmaWait(); + + setAddrWindow(x, y, w, h); + + esp_err_t ret; + static spi_transaction_t trans; + + memset(&trans, 0, sizeof(spi_transaction_t)); + + trans.user = (void *)1; + trans.tx_buffer = image; //Data pointer + trans.length = len * 16; //Data length, in bits + trans.flags = 0; //SPI_TRANS_USE_TXDATA flag + + ret = spi_device_queue_trans(dmaHAL, &trans, portMAX_DELAY); + assert(ret == ESP_OK); + + spiBusyCheck++; +} + + +/*************************************************************************************** +** Function name: pushImageDMA +** Description: Push image to a window (w*h must be less than 65536) +***************************************************************************************/ +// This will clip and also swap bytes if setSwapBytes(true) was called by sketch +void TFT_eSPI::pushImageDMA(int32_t x, int32_t y, int32_t w, int32_t h, uint16_t* image, uint16_t* buffer) +{ + if ((x >= _vpW) || (y >= _vpH) || (!DMA_Enabled)) return; + + int32_t dx = 0; + int32_t dy = 0; + int32_t dw = w; + int32_t dh = h; + + if (x < _vpX) { dx = _vpX - x; dw -= dx; x = _vpX; } + if (y < _vpY) { dy = _vpY - y; dh -= dy; y = _vpY; } + + if ((x + dw) > _vpW ) dw = _vpW - x; + if ((y + dh) > _vpH ) dh = _vpH - y; + + if (dw < 1 || dh < 1) return; + + uint32_t len = dw*dh; + + if (buffer == nullptr) { + buffer = image; + dmaWait(); + } + + // If image is clipped, copy pixels into a contiguous block + if ( (dw != w) || (dh != h) ) { + if(_swapBytes) { + for (int32_t yb = 0; yb < dh; yb++) { + for (int32_t xb = 0; xb < dw; xb++) { + uint32_t src = xb + dx + w * (yb + dy); + (buffer[xb + yb * dw] = image[src] << 8 | image[src] >> 8); + } + } + } + else { + for (int32_t yb = 0; yb < dh; yb++) { + memcpy((uint8_t*) (buffer + yb * dw), (uint8_t*) (image + dx + w * (yb + dy)), dw << 1); + } + } + } + // else, if a buffer pointer has been provided copy whole image to the buffer + else if (buffer != image || _swapBytes) { + if(_swapBytes) { + for (uint32_t i = 0; i < len; i++) (buffer[i] = image[i] << 8 | image[i] >> 8); + } + else { + memcpy(buffer, image, len*2); + } + } + + if (spiBusyCheck) dmaWait(); // In case we did not wait earlier + + setAddrWindow(x, y, dw, dh); + + esp_err_t ret; + static spi_transaction_t trans; + + memset(&trans, 0, sizeof(spi_transaction_t)); + + trans.user = (void *)1; + trans.tx_buffer = buffer; //finally send the line data + trans.length = len * 16; //Data length, in bits + trans.flags = 0; //SPI_TRANS_USE_TXDATA flag + + ret = spi_device_queue_trans(dmaHAL, &trans, portMAX_DELAY); + assert(ret == ESP_OK); + + spiBusyCheck++; +} + +//////////////////////////////////////////////////////////////////////////////////////// +// Processor specific DMA initialisation +//////////////////////////////////////////////////////////////////////////////////////// + +// The DMA functions here work with SPI only (not parallel) +/*************************************************************************************** +** Function name: dc_callback +** Description: Toggles DC line during transaction +***************************************************************************************/ +extern "C" void dc_callback(); + +void IRAM_ATTR dc_callback(spi_transaction_t *spi_tx) +{ + if ((bool)spi_tx->user) {DC_D;} + else {DC_C;} +} + +/*************************************************************************************** +** Function name: dma_end_callback +** Description: Clear DMA run flag to stop retransmission loop +***************************************************************************************/ +extern "C" void dma_end_callback(); + +void IRAM_ATTR dma_end_callback(spi_transaction_t *spi_tx) +{ + WRITE_PERI_REG(SPI_DMA_CONF_REG(spi_host), 0); +} + +/*************************************************************************************** +** Function name: initDMA +** Description: Initialise the DMA engine - returns true if init OK +***************************************************************************************/ +bool TFT_eSPI::initDMA(bool ctrl_cs) +{ + if (DMA_Enabled) return false; + + esp_err_t ret; + spi_bus_config_t buscfg = { + .mosi_io_num = TFT_MOSI, + .miso_io_num = TFT_MISO, + .sclk_io_num = TFT_SCLK, + .quadwp_io_num = -1, + .quadhd_io_num = -1, + #ifdef xCONFIG_IDF_TARGET_ESP32S2 + .data4_io_num = -1, + .data5_io_num = -1, + .data6_io_num = -1, + .data7_io_num = -1, + #endif + .max_transfer_sz = TFT_WIDTH * TFT_HEIGHT * 2 + 8, // TFT screen size + .flags = 0, + .intr_flags = 0 + }; + + int8_t pin = -1; + if (ctrl_cs) pin = TFT_CS; + + spi_device_interface_config_t devcfg = { + .command_bits = 0, + .address_bits = 0, + .dummy_bits = 0, + .mode = TFT_SPI_MODE, + .duty_cycle_pos = 0, + .cs_ena_pretrans = 0, + .cs_ena_posttrans = 0, + .clock_speed_hz = SPI_FREQUENCY, + .input_delay_ns = 0, + .spics_io_num = pin, + .flags = SPI_DEVICE_NO_DUMMY, //0, + .queue_size = 1, + .pre_cb = 0, //dc_callback, //Callback to handle D/C line + #ifdef CONFIG_IDF_TARGET_ESP32 + .post_cb = 0 + #else + .post_cb = dma_end_callback + #endif + }; + ret = spi_bus_initialize(spi_host, &buscfg, DMA_CHANNEL); + ESP_ERROR_CHECK(ret); + ret = spi_bus_add_device(spi_host, &devcfg, &dmaHAL); + ESP_ERROR_CHECK(ret); + + DMA_Enabled = true; + spiBusyCheck = 0; + return true; +} + +/*************************************************************************************** +** Function name: deInitDMA +** Description: Disconnect the DMA engine from SPI +***************************************************************************************/ +void TFT_eSPI::deInitDMA(void) +{ + if (!DMA_Enabled) return; + spi_bus_remove_device(dmaHAL); + spi_bus_free(spi_host); + DMA_Enabled = false; +} + +//////////////////////////////////////////////////////////////////////////////////////// +#endif // End of DMA FUNCTIONS +//////////////////////////////////////////////////////////////////////////////////////// diff --git a/lib/Seeed_GFX/Processors/TFT_eSPI_ESP32.h b/lib/Seeed_GFX/Processors/TFT_eSPI_ESP32.h new file mode 100644 index 0000000..8fa1fbf --- /dev/null +++ b/lib/Seeed_GFX/Processors/TFT_eSPI_ESP32.h @@ -0,0 +1,591 @@ + //////////////////////////////////////////////////// + // TFT_eSPI driver functions for ESP32 processors // + //////////////////////////////////////////////////// + +#ifndef _TFT_eSPI_ESP32H_ +#define _TFT_eSPI_ESP32H_ + +// Processor ID reported by getSetup() +#define PROCESSOR_ID 0x32 + +// Include processor specific header +#include "soc/spi_reg.h" +#include "driver/spi_master.h" +#include "hal/gpio_ll.h" + +#if !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32) + #define CONFIG_IDF_TARGET_ESP32 +#endif + +// Fix IDF problems with ESP32C3 +#if CONFIG_IDF_TARGET_ESP32C3 + // Fix ESP32C3 IDF bug for missing definition + #ifndef REG_SPI_BASE + #define REG_SPI_BASE(i) (DR_REG_SPI1_BASE + (((i)>1) ? (((i)* 0x1000) + 0x20000) : (((~(i)) & 1)* 0x1000 ))) + #endif + + // Fix ESP32C3 IDF bug for name change + #ifndef SPI_MOSI_DLEN_REG + #define SPI_MOSI_DLEN_REG(x) SPI_MS_DLEN_REG(x) + #endif + + // Fix ESP32C3 specific register reference + #define out_w1tc out_w1tc.val + #define out_w1ts out_w1ts.val +#endif + +// SUPPORT_TRANSACTIONS is mandatory for ESP32 so the hal mutex is toggled +#if !defined (SUPPORT_TRANSACTIONS) + #define SUPPORT_TRANSACTIONS +#endif + +/* +ESP32: +FSPI not defined +HSPI = 2, uses SPI2 +VSPI = 3, uses SPI3 + +ESP32-S2: +FSPI = 1, uses SPI2 +HSPI = 2, uses SPI3 +VSPI not defined + +ESP32 C3: +FSPI = 0, uses SPI2 ???? To be checked +HSPI = 1, uses SPI3 ???? To be checked +VSPI not defined + +For ESP32/S2/C3: +SPI1_HOST = 0 +SPI2_HOST = 1 +SPI3_HOST = 2 +*/ + +// ESP32 specific SPI port selection +#ifdef USE_HSPI_PORT + #ifdef CONFIG_IDF_TARGET_ESP32 + #define SPI_PORT HSPI //HSPI is port 2 on ESP32 + #else + #define SPI_PORT 3 //HSPI is port 3 on ESP32 S2 + #endif +#elif defined(USE_FSPI_PORT) + #define SPI_PORT 2 //FSPI(ESP32 S2) +#else + #ifdef CONFIG_IDF_TARGET_ESP32 + #define SPI_PORT VSPI + #else + #define SPI_PORT 2 //FSPI(ESP32 S2) + #endif +#endif + +#ifdef RPI_DISPLAY_TYPE + #define CMD_BITS (16-1) +#else + #define CMD_BITS (8-1) +#endif + +// Initialise processor specific SPI functions, used by init() +#define INIT_TFT_DATA_BUS // Not used + +// Define a generic flag for 8-bit parallel +#if defined (ESP32_PARALLEL) // Specific to ESP32 for backwards compatibility + #if !defined (TFT_PARALLEL_8_BIT) + #define TFT_PARALLEL_8_BIT // Generic parallel flag + #endif +#endif + +// Ensure ESP32 specific flag is defined for 8-bit parallel +#if defined (TFT_PARALLEL_8_BIT) + #if !defined (ESP32_PARALLEL) + #define ESP32_PARALLEL + #endif +#endif + +// Processor specific code used by SPI bus transaction startWrite and endWrite functions +#if !defined (ESP32_PARALLEL) + #if (TFT_SPI_MODE == SPI_MODE1) || (TFT_SPI_MODE == SPI_MODE2) + #define SET_BUS_WRITE_MODE *_spi_user = SPI_USR_MOSI | SPI_CK_OUT_EDGE + #define SET_BUS_READ_MODE *_spi_user = SPI_USR_MOSI | SPI_USR_MISO | SPI_DOUTDIN | SPI_CK_OUT_EDGE + #else + #define SET_BUS_WRITE_MODE *_spi_user = SPI_USR_MOSI + #define SET_BUS_READ_MODE *_spi_user = SPI_USR_MOSI | SPI_USR_MISO | SPI_DOUTDIN + #endif +#else + // Not applicable to parallel bus + #define SET_BUS_WRITE_MODE + #define SET_BUS_READ_MODE +#endif + +// Code to check if DMA is busy, used by SPI bus transaction transaction and endWrite functions +#if !defined(TFT_PARALLEL_8_BIT) && !defined(SPI_18BIT_DRIVER) + #define ESP32_DMA + // Code to check if DMA is busy, used by SPI DMA + transaction + endWrite functions + #define DMA_BUSY_CHECK dmaWait() +#else + #define DMA_BUSY_CHECK +#endif + +#if defined(TFT_PARALLEL_8_BIT) + #define SPI_BUSY_CHECK +#else + #define SPI_BUSY_CHECK while (*_spi_cmd&SPI_USR) +#endif + +// If smooth font is used then it is likely SPIFFS will be needed +#ifdef SMOOTH_FONT + // Call up the SPIFFS (SPI FLASH Filing System) for the anti-aliased fonts + #define FS_NO_GLOBALS + #include + #include "SPIFFS.h" // ESP32 only + #define FONT_FS_AVAILABLE +#endif + + +//////////////////////////////////////////////////////////////////////////////////////// +// Define the DC (TFT Data/Command or Register Select (RS))pin drive code +//////////////////////////////////////////////////////////////////////////////////////// +#ifndef TFT_DC + #define DC_C // No macro allocated so it generates no code + #define DC_D // No macro allocated so it generates no code +#else + #if defined (TFT_PARALLEL_8_BIT) + // TFT_DC, by design, must be in range 0-31 for single register parallel write + #if (TFT_DC >= 0) && (TFT_DC < 32) + #define DC_C GPIO.out_w1tc = (1 << TFT_DC) + #define DC_D GPIO.out_w1ts = (1 << TFT_DC) + #elif (TFT_DC >= 32) + #define DC_C GPIO.out1_w1tc.val = (1 << (TFT_DC- 32)) + #define DC_D GPIO.out1_w1ts.val = (1 << (TFT_DC- 32)) + #else + #define DC_C + #define DC_D + #endif + #else + #if (TFT_DC >= 32) + #ifdef RPI_DISPLAY_TYPE // RPi displays need a slower DC change + #define DC_C GPIO.out1_w1ts.val = (1 << (TFT_DC - 32)); \ + GPIO.out1_w1tc.val = (1 << (TFT_DC - 32)) + #define DC_D GPIO.out1_w1tc.val = (1 << (TFT_DC - 32)); \ + GPIO.out1_w1ts.val = (1 << (TFT_DC - 32)) + #else + #define DC_C GPIO.out1_w1tc.val = (1 << (TFT_DC - 32))//;GPIO.out1_w1tc.val = (1 << (TFT_DC - 32)) + #define DC_D GPIO.out1_w1ts.val = (1 << (TFT_DC - 32))//;GPIO.out1_w1ts.val = (1 << (TFT_DC - 32)) + #endif + #elif (TFT_DC >= 0) + #if defined (RPI_DISPLAY_TYPE) + #if defined (ILI9486_DRIVER) + // RPi ILI9486 display needs a slower DC change + #define DC_C GPIO.out_w1tc = (1 << TFT_DC); \ + GPIO.out_w1tc = (1 << TFT_DC) + #define DC_D GPIO.out_w1tc = (1 << TFT_DC); \ + GPIO.out_w1ts = (1 << TFT_DC) + #else + // Other RPi displays need a slower C->D change + #define DC_C GPIO.out_w1tc = (1 << TFT_DC) + #define DC_D GPIO.out_w1tc = (1 << TFT_DC); \ + GPIO.out_w1ts = (1 << TFT_DC) + #endif + #else + #define DC_C GPIO.out_w1tc = (1 << TFT_DC)//;GPIO.out_w1tc = (1 << TFT_DC) + #define DC_D GPIO.out_w1ts = (1 << TFT_DC)//;GPIO.out_w1ts = (1 << TFT_DC) + #endif + #else + #define DC_C + #define DC_D + #endif + #endif +#endif + +//////////////////////////////////////////////////////////////////////////////////////// +// Define the CS (TFT chip select) pin drive code +//////////////////////////////////////////////////////////////////////////////////////// +#ifndef TFT_CS + #define TFT_CS -1 // Keep DMA code happy + #define CS_L // No macro allocated so it generates no code + #define CS_H // No macro allocated so it generates no code +#else + #if defined (TFT_PARALLEL_8_BIT) + #if TFT_CS >= 32 + #define CS_L GPIO.out1_w1tc.val = (1 << (TFT_CS - 32)) + #define CS_H GPIO.out1_w1ts.val = (1 << (TFT_CS - 32)) + #elif TFT_CS >= 0 + #define CS_L GPIO.out_w1tc = (1 << TFT_CS) + #define CS_H GPIO.out_w1ts = (1 << TFT_CS) + #else + #define CS_L + #define CS_H + #endif + #else + #if (TFT_CS >= 32) + #ifdef RPI_DISPLAY_TYPE // RPi display needs a slower CS change + #define CS_L GPIO.out1_w1ts.val = (1 << (TFT_CS - 32)); \ + GPIO.out1_w1tc.val = (1 << (TFT_CS - 32)) + #define CS_H GPIO.out1_w1tc.val = (1 << (TFT_CS - 32)); \ + GPIO.out1_w1ts.val = (1 << (TFT_CS - 32)) + #else + #define CS_L GPIO.out1_w1tc.val = (1 << (TFT_CS - 32)); GPIO.out1_w1tc.val = (1 << (TFT_CS - 32)) + #define CS_H GPIO.out1_w1ts.val = (1 << (TFT_CS - 32))//;GPIO.out1_w1ts.val = (1 << (TFT_CS - 32)) + #endif + #elif (TFT_CS >= 0) + #ifdef RPI_DISPLAY_TYPE // RPi display needs a slower CS change + #define CS_L GPIO.out_w1ts = (1 << TFT_CS); GPIO.out_w1tc = (1 << TFT_CS) + #define CS_H GPIO.out_w1tc = (1 << TFT_CS); GPIO.out_w1ts = (1 << TFT_CS) + #else + #define CS_L GPIO.out_w1tc = (1 << TFT_CS); GPIO.out_w1tc = (1 << TFT_CS) + #define CS_H GPIO.out_w1ts = (1 << TFT_CS)//;GPIO.out_w1ts = (1 << TFT_CS) + #endif + #else + #define CS_L + #define CS_H + #endif + #endif +#endif + +//////////////////////////////////////////////////////////////////////////////////////// +// Define the WR (TFT Write) pin drive code +//////////////////////////////////////////////////////////////////////////////////////// +#if defined (TFT_WR) + #if (TFT_WR >= 32) + // Note: it will be ~1.25x faster if the TFT_WR pin uses a GPIO pin lower than 32 + #define WR_L GPIO.out1_w1tc.val = (1 << (TFT_WR - 32)) + #define WR_H GPIO.out1_w1ts.val = (1 << (TFT_WR - 32)) + #elif (TFT_WR >= 0) + // TFT_WR, for best performance, should be in range 0-31 for single register parallel write + #define WR_L GPIO.out_w1tc = (1 << TFT_WR) + #define WR_H GPIO.out_w1ts = (1 << TFT_WR) + #else + #define WR_L + #define WR_H + #endif +#else + #define WR_L + #define WR_H +#endif + +//////////////////////////////////////////////////////////////////////////////////////// +// Define the touch screen chip select pin drive code +//////////////////////////////////////////////////////////////////////////////////////// +#ifndef TOUCH_CS + #define T_CS_L // No macro allocated so it generates no code + #define T_CS_H // No macro allocated so it generates no code +#else // XPT2046 is slow, so use slower digitalWrite here + #define T_CS_L digitalWrite(TOUCH_CS, LOW) + #define T_CS_H digitalWrite(TOUCH_CS, HIGH) +#endif + +//////////////////////////////////////////////////////////////////////////////////////// +// Make sure SPI default pins are assigned if not specified by user or set to -1 +//////////////////////////////////////////////////////////////////////////////////////// +#if !defined (TFT_PARALLEL_8_BIT) + + #ifdef USE_HSPI_PORT + + #ifndef TFT_MISO + #define TFT_MISO -1 + #endif + + #ifndef TFT_MOSI + #define TFT_MOSI 13 + #endif + #if (TFT_MOSI == -1) + #undef TFT_MOSI + #define TFT_MOSI 13 + #endif + + #ifndef TFT_SCLK + #define TFT_SCLK 14 + #endif + #if (TFT_SCLK == -1) + #undef TFT_SCLK + #define TFT_SCLK 14 + #endif + + #else // VSPI port + + #ifndef TFT_MISO + #define TFT_MISO -1 + #endif + + #ifndef TFT_MOSI + #define TFT_MOSI 23 + #endif + #if (TFT_MOSI == -1) + #undef TFT_MOSI + #define TFT_MOSI 23 + #endif + + #ifndef TFT_SCLK + #define TFT_SCLK 18 + #endif + #if (TFT_SCLK == -1) + #undef TFT_SCLK + #define TFT_SCLK 18 + #endif + + #if defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32S2) + #if (TFT_MISO == -1) + #undef TFT_MISO + #define TFT_MISO TFT_MOSI + #endif + #endif + + #endif + +#endif + +//////////////////////////////////////////////////////////////////////////////////////// +// Define the parallel bus interface chip pin drive code +//////////////////////////////////////////////////////////////////////////////////////// +#if defined (TFT_PARALLEL_8_BIT) + + // Create a bit set lookup table for data bus - wastes 1kbyte of RAM but speeds things up dramatically + // can then use e.g. GPIO.out_w1ts = set_mask(0xFF); to set data bus to 0xFF + #define PARALLEL_INIT_TFT_DATA_BUS \ + for (int32_t c = 0; c<256; c++) \ + { \ + xset_mask[c] = 0; \ + if ( c & 0x01 ) xset_mask[c] |= (1 << TFT_D0); \ + if ( c & 0x02 ) xset_mask[c] |= (1 << TFT_D1); \ + if ( c & 0x04 ) xset_mask[c] |= (1 << TFT_D2); \ + if ( c & 0x08 ) xset_mask[c] |= (1 << TFT_D3); \ + if ( c & 0x10 ) xset_mask[c] |= (1 << TFT_D4); \ + if ( c & 0x20 ) xset_mask[c] |= (1 << TFT_D5); \ + if ( c & 0x40 ) xset_mask[c] |= (1 << TFT_D6); \ + if ( c & 0x80 ) xset_mask[c] |= (1 << TFT_D7); \ + } \ + + // Mask for the 8 data bits to set pin directions + #define GPIO_DIR_MASK ((1 << TFT_D0) | (1 << TFT_D1) | (1 << TFT_D2) | (1 << TFT_D3) | (1 << TFT_D4) | (1 << TFT_D5) | (1 << TFT_D6) | (1 << TFT_D7)) + + #if (TFT_WR >= 32) + // Data bits and the write line are cleared sequentially + #define GPIO_OUT_CLR_MASK (GPIO_DIR_MASK); WR_L + #elif (TFT_WR >= 0) + // Data bits and the write line are cleared to 0 in one step (1.25x faster) + #define GPIO_OUT_CLR_MASK (GPIO_DIR_MASK | (1 << TFT_WR)) + #else + #define GPIO_OUT_CLR_MASK + #endif + + // A lookup table is used to set the different bit patterns, this uses 1kByte of RAM + #define set_mask(C) xset_mask[C] // 63fps Sprite rendering test 33% faster, graphicstest only 1.8% faster than shifting in real time + + // Real-time shifting alternative to above to save 1KByte RAM, 47 fps Sprite rendering test + /*#define set_mask(C) (((C)&0x80)>>7)<>6)<>5)<>4)<>3)<>2)<>1)<>0)<> 8)); WR_H; \ + GPIO.out_w1tc = GPIO_OUT_CLR_MASK; GPIO.out_w1ts = set_mask((uint8_t) (((C) & 0x07E0)>> 3)); WR_H; \ + GPIO.out_w1tc = GPIO_OUT_CLR_MASK; GPIO.out_w1ts = set_mask((uint8_t) (((C) & 0x001F)<< 3)); WR_H + + // 18-bit color write with swapped bytes + #define tft_Write_16S(C) Cswap = ((C) >>8 | (C) << 8); tft_Write_16(Cswap) + + #else + + #ifdef PSEUDO_16_BIT + // One write strobe for both bytes + #define tft_Write_16(C) GPIO.out_w1tc = GPIO_OUT_CLR_MASK; GPIO.out_w1ts = set_mask((uint8_t) ((C) >> 0)); WR_H + #define tft_Write_16S(C) GPIO.out_w1tc = GPIO_OUT_CLR_MASK; GPIO.out_w1ts = set_mask((uint8_t) ((C) >> 8)); WR_H + #else + // Write 16 bits to TFT + #define tft_Write_16(C) GPIO.out_w1tc = GPIO_OUT_CLR_MASK; GPIO.out_w1ts = set_mask((uint8_t) ((C) >> 8)); WR_H; \ + GPIO.out_w1tc = GPIO_OUT_CLR_MASK; GPIO.out_w1ts = set_mask((uint8_t) ((C) >> 0)); WR_H + + // 16-bit write with swapped bytes + #define tft_Write_16S(C) GPIO.out_w1tc = GPIO_OUT_CLR_MASK; GPIO.out_w1ts = set_mask((uint8_t) ((C) >> 0)); WR_H; \ + GPIO.out_w1tc = GPIO_OUT_CLR_MASK; GPIO.out_w1ts = set_mask((uint8_t) ((C) >> 8)); WR_H + #endif + + #endif + + // Write 32 bits to TFT + #define tft_Write_32(C) GPIO.out_w1tc = GPIO_OUT_CLR_MASK; GPIO.out_w1ts = set_mask((uint8_t) ((C) >> 24)); WR_H; \ + GPIO.out_w1tc = GPIO_OUT_CLR_MASK; GPIO.out_w1ts = set_mask((uint8_t) ((C) >> 16)); WR_H; \ + GPIO.out_w1tc = GPIO_OUT_CLR_MASK; GPIO.out_w1ts = set_mask((uint8_t) ((C) >> 8)); WR_H; \ + GPIO.out_w1tc = GPIO_OUT_CLR_MASK; GPIO.out_w1ts = set_mask((uint8_t) ((C) >> 0)); WR_H + + // Write two concatenated 16-bit values to TFT + #define tft_Write_32C(C,D) GPIO.out_w1tc = GPIO_OUT_CLR_MASK; GPIO.out_w1ts = set_mask((uint8_t) ((C) >> 8)); WR_H; \ + GPIO.out_w1tc = GPIO_OUT_CLR_MASK; GPIO.out_w1ts = set_mask((uint8_t) ((C) >> 0)); WR_H; \ + GPIO.out_w1tc = GPIO_OUT_CLR_MASK; GPIO.out_w1ts = set_mask((uint8_t) ((D) >> 8)); WR_H; \ + GPIO.out_w1tc = GPIO_OUT_CLR_MASK; GPIO.out_w1ts = set_mask((uint8_t) ((D) >> 0)); WR_H + + // Write 16-bit value twice to TFT - used by drawPixel() + #define tft_Write_32D(C) GPIO.out_w1tc = GPIO_OUT_CLR_MASK; GPIO.out_w1ts = set_mask((uint8_t) ((C) >> 8)); WR_H; \ + GPIO.out_w1tc = GPIO_OUT_CLR_MASK; GPIO.out_w1ts = set_mask((uint8_t) ((C) >> 0)); WR_H; \ + GPIO.out_w1tc = GPIO_OUT_CLR_MASK; GPIO.out_w1ts = set_mask((uint8_t) ((C) >> 8)); WR_H; \ + GPIO.out_w1tc = GPIO_OUT_CLR_MASK; GPIO.out_w1ts = set_mask((uint8_t) ((C) >> 0)); WR_H + + // Read pin + #ifdef TFT_RD + #if (TFT_RD >= 32) + #define RD_L GPIO.out1_w1tc.val = (1 << (TFT_RD - 32)) + #define RD_H GPIO.out1_w1ts.val = (1 << (TFT_RD - 32)) + #elif (TFT_RD >= 0) + #define RD_L GPIO.out_w1tc = (1 << TFT_RD) + //#define RD_L digitalWrite(TFT_WR, LOW) + #define RD_H GPIO.out_w1ts = (1 << TFT_RD) + //#define RD_H digitalWrite(TFT_WR, HIGH) + #else + #define RD_L + #define RD_H + #endif + #else + #define TFT_RD -1 + #define RD_L + #define RD_H + #endif + +//////////////////////////////////////////////////////////////////////////////////////// +// Macros to write commands/pixel colour data to a SPI ILI948x TFT +//////////////////////////////////////////////////////////////////////////////////////// +#elif defined (SPI_18BIT_DRIVER) // SPI 18-bit colour + + // Write 8 bits to TFT + #define tft_Write_8(C) spi.transfer(C) + + // Convert 16-bit colour to 18-bit and write in 3 bytes + #define tft_Write_16(C) spi.transfer(((C) & 0xF800)>>8); \ + spi.transfer(((C) & 0x07E0)>>3); \ + spi.transfer(((C) & 0x001F)<<3) + + // Future option for transfer without wait + #define tft_Write_16N(C) tft_Write_16(C) + + // Convert swapped byte 16-bit colour to 18-bit and write in 3 bytes + #define tft_Write_16S(C) spi.transfer((C) & 0xF8); \ + spi.transfer(((C) & 0xE000)>>11 | ((C) & 0x07)<<5); \ + spi.transfer(((C) & 0x1F00)>>5) + + // Write 32 bits to TFT + #define tft_Write_32(C) spi.write32(C) + + // Write two concatenated 16-bit values to TFT + #define tft_Write_32C(C,D) spi.write32((C)<<16 | (D)) + + // Write 16-bit value twice to TFT + #define tft_Write_32D(C) spi.write32((C)<<16 | (C)) + +//////////////////////////////////////////////////////////////////////////////////////// +// Macros to write commands/pixel colour data to an Raspberry Pi TFT +//////////////////////////////////////////////////////////////////////////////////////// +#elif defined (RPI_DISPLAY_TYPE) + + // ESP32 low level SPI writes for 8, 16 and 32-bit values + // to avoid the function call overhead + #define TFT_WRITE_BITS(D, B) \ + WRITE_PERI_REG(SPI_MOSI_DLEN_REG(SPI_PORT), B-1); \ + WRITE_PERI_REG(SPI_W0_REG(SPI_PORT), D); \ + SET_PERI_REG_MASK(SPI_CMD_REG(SPI_PORT), SPI_USR); \ + while (READ_PERI_REG(SPI_CMD_REG(SPI_PORT))&SPI_USR); + + // Write 8 bits + #define tft_Write_8(C) TFT_WRITE_BITS((C)<<8, 16) + + // Write 16 bits with corrected endianness for 16-bit colours + #define tft_Write_16(C) TFT_WRITE_BITS((C)<<8 | (C)>>8, 16) + + // Future option for transfer without wait + #define tft_Write_16N(C) tft_Write_16(C) + + // Write 16 bits + #define tft_Write_16S(C) TFT_WRITE_BITS(C, 16) + + // Write 32 bits + #define tft_Write_32(C) TFT_WRITE_BITS(C, 32) + + // Write two address coordinates + #define tft_Write_32C(C,D) TFT_WRITE_BITS((C)<<24 | (C), 32); \ + TFT_WRITE_BITS((D)<<24 | (D), 32) + + // Write same value twice + #define tft_Write_32D(C) tft_Write_32C(C,C) + +//////////////////////////////////////////////////////////////////////////////////////// +// Macros for all other SPI displays +//////////////////////////////////////////////////////////////////////////////////////// +#else +/* Old macros + // ESP32 low level SPI writes for 8, 16 and 32-bit values + // to avoid the function call overhead + #define TFT_WRITE_BITS(D, B) \ + WRITE_PERI_REG(SPI_MOSI_DLEN_REG(SPI_PORT), B-1); \ + WRITE_PERI_REG(SPI_W0_REG(SPI_PORT), D); \ + SET_PERI_REG_MASK(SPI_CMD_REG(SPI_PORT), SPI_USR); \ + while (READ_PERI_REG(SPI_CMD_REG(SPI_PORT))&SPI_USR); + + // Write 8 bits + #define tft_Write_8(C) TFT_WRITE_BITS(C, 8) + + // Write 16 bits with corrected endianness for 16-bit colours + #define tft_Write_16(C) TFT_WRITE_BITS((C)<<8 | (C)>>8, 16) + + // Write 16 bits + #define tft_Write_16S(C) TFT_WRITE_BITS(C, 16) + + // Write 32 bits + #define tft_Write_32(C) TFT_WRITE_BITS(C, 32) + + // Write two address coordinates + #define tft_Write_32C(C,D) TFT_WRITE_BITS((uint16_t)((D)<<8 | (D)>>8)<<16 | (uint16_t)((C)<<8 | (C)>>8), 32) + + // Write same value twice + #define tft_Write_32D(C) TFT_WRITE_BITS((uint16_t)((C)<<8 | (C)>>8)<<16 | (uint16_t)((C)<<8 | (C)>>8), 32) +//*/ +//* Replacement slimmer macros + #define TFT_WRITE_BITS(D, B) *_spi_mosi_dlen = B-1; \ + *_spi_w = D; \ + *_spi_cmd = SPI_USR; \ + while (*_spi_cmd & SPI_USR); + + // Write 8 bits + #define tft_Write_8(C) TFT_WRITE_BITS(C, 8) + + // Write 16 bits with corrected endianness for 16-bit colours + #define tft_Write_16(C) TFT_WRITE_BITS((C)<<8 | (C)>>8, 16) + + // Future option for transfer without wait + #define tft_Write_16N(C) *_spi_mosi_dlen = 16-1; \ + *_spi_w = ((C)<<8 | (C)>>8); \ + *_spi_cmd = SPI_USR; + + // Write 16 bits + #define tft_Write_16S(C) TFT_WRITE_BITS(C, 16) + + // Write 32 bits + #define tft_Write_32(C) TFT_WRITE_BITS(C, 32) + + // Write two address coordinates + #define tft_Write_32C(C,D) TFT_WRITE_BITS((uint16_t)((D)<<8 | (D)>>8)<<16 | (uint16_t)((C)<<8 | (C)>>8), 32) + + // Write same value twice + #define tft_Write_32D(C) TFT_WRITE_BITS((uint16_t)((C)<<8 | (C)>>8)<<16 | (uint16_t)((C)<<8 | (C)>>8), 32) + +//*/ +#endif + +#ifndef tft_Write_16N + #define tft_Write_16N tft_Write_16 +#endif + +//////////////////////////////////////////////////////////////////////////////////////// +// Macros to read from display using SPI or software SPI +//////////////////////////////////////////////////////////////////////////////////////// +#if !defined (TFT_PARALLEL_8_BIT) + // Read from display using SPI or software SPI + // Use a SPI read transfer + #define tft_Read_8() spi.transfer(0) +#endif + +// Concatenate a byte sequence A,B,C,D to CDAB, P is a uint8_t pointer +#define DAT8TO32(P) ( (uint32_t)P[0]<<8 | P[1] | P[2]<<24 | P[3]<<16 ) + +#endif // Header end diff --git a/lib/Seeed_GFX/Processors/TFT_eSPI_ESP32_S3.c b/lib/Seeed_GFX/Processors/TFT_eSPI_ESP32_S3.c new file mode 100644 index 0000000..ed3f738 --- /dev/null +++ b/lib/Seeed_GFX/Processors/TFT_eSPI_ESP32_S3.c @@ -0,0 +1,899 @@ + //////////////////////////////////////////////////// + // TFT_eSPI driver functions for ESP32 processors // + //////////////////////////////////////////////////// + +// Temporarily a separate file to TFT_eSPI_ESP32.c until board package low level API stabilises + +//////////////////////////////////////////////////////////////////////////////////////// +// Global variables +//////////////////////////////////////////////////////////////////////////////////////// + +// Select the SPI port to use, ESP32 has 2 options +#if !defined (TFT_PARALLEL_8_BIT) + #ifdef CONFIG_IDF_TARGET_ESP32 + #ifdef USE_HSPI_PORT + SPIClass spi = SPIClass(HSPI); + #elif defined(USE_FSPI_PORT) + SPIClass spi = SPIClass(FSPI); + #else // use default VSPI port + SPIClass spi = SPIClass(VSPI); + #endif + #else + #ifdef USE_HSPI_PORT + SPIClass spi = SPIClass(HSPI); + #elif defined(USE_FSPI_PORT) + SPIClass spi = SPIClass(FSPI); + #else // use FSPI port + SPIClass& spi = SPI; + #endif + #endif +#endif + +#ifdef ESP32_DMA + // DMA SPA handle + spi_device_handle_t dmaHAL; + #ifdef CONFIG_IDF_TARGET_ESP32 + #define DMA_CHANNEL 1 + #ifdef USE_HSPI_PORT + spi_host_device_t spi_host = HSPI_HOST; + #elif defined(USE_FSPI_PORT) + spi_host_device_t spi_host = SPI_HOST; + #else // use VSPI port + spi_host_device_t spi_host = VSPI_HOST; + #endif + #else + #ifdef USE_HSPI_PORT + #define DMA_CHANNEL SPI_DMA_CH_AUTO + spi_host_device_t spi_host = SPI3_HOST; + #else // use FSPI port + #define DMA_CHANNEL SPI_DMA_CH_AUTO + spi_host_device_t spi_host = SPI2_HOST; + #endif + #endif +#endif + +//////////////////////////////////////////////////////////////////////////////////////// +#if defined (TFT_SDA_READ) && !defined (TFT_PARALLEL_8_BIT) +//////////////////////////////////////////////////////////////////////////////////////// + +/*************************************************************************************** +** Function name: beginSDA - FPSI port only +** Description: Detach MOSI and attach MISO to SDA for reads +***************************************************************************************/ +void TFT_eSPI::begin_SDA_Read(void) +{ + gpio_set_direction((gpio_num_t)TFT_MOSI, GPIO_MODE_INPUT); + pinMatrixInAttach(TFT_MOSI, FSPIQ_IN_IDX, false); + SET_BUS_READ_MODE; +} + +/*************************************************************************************** +** Function name: endSDA - FPSI port only +** Description: Attach MOSI to SDA and detach MISO for writes +***************************************************************************************/ +void TFT_eSPI::end_SDA_Read(void) +{ + gpio_set_direction((gpio_num_t)TFT_MOSI, GPIO_MODE_OUTPUT); + pinMatrixOutAttach(TFT_MOSI, FSPID_OUT_IDX, false, false); + SET_BUS_WRITE_MODE; +} +//////////////////////////////////////////////////////////////////////////////////////// +#endif // #if defined (TFT_SDA_READ) +//////////////////////////////////////////////////////////////////////////////////////// + + +/*************************************************************************************** +** Function name: read byte - supports class functions +** Description: Read a byte from ESP32 8-bit data port +***************************************************************************************/ +// Parallel bus MUST be set to input before calling this function! +uint8_t TFT_eSPI::readByte(void) +{ + uint8_t b = 0xAA; + +#if defined (TFT_PARALLEL_8_BIT) + RD_L; + b = gpio_get_level((gpio_num_t)TFT_D0); // Read three times to allow for bus access time + b = gpio_get_level((gpio_num_t)TFT_D0); + b = gpio_get_level((gpio_num_t)TFT_D0); // Data should be stable now + + // Check GPIO bits used and build value + b = (gpio_get_level((gpio_num_t)TFT_D0) << 0); + b |= (gpio_get_level((gpio_num_t)TFT_D1) << 1); + b |= (gpio_get_level((gpio_num_t)TFT_D2) << 2); + b |= (gpio_get_level((gpio_num_t)TFT_D3) << 3); + b |= (gpio_get_level((gpio_num_t)TFT_D4) << 4); + b |= (gpio_get_level((gpio_num_t)TFT_D5) << 5); + b |= (gpio_get_level((gpio_num_t)TFT_D6) << 6); + b |= (gpio_get_level((gpio_num_t)TFT_D7) << 7); + RD_H; +#endif + + return b; +} + +//////////////////////////////////////////////////////////////////////////////////////// +#ifdef TFT_PARALLEL_8_BIT +//////////////////////////////////////////////////////////////////////////////////////// + +/*************************************************************************************** +** Function name: GPIO direction control - supports class functions +** Description: Set parallel bus to INPUT or OUTPUT +***************************************************************************************/ +void TFT_eSPI::busDir(uint32_t mask, uint8_t mode) +{ + // Arduino generic native function + pinMode(TFT_D0, mode); + pinMode(TFT_D1, mode); + pinMode(TFT_D2, mode); + pinMode(TFT_D3, mode); + pinMode(TFT_D4, mode); + pinMode(TFT_D5, mode); + pinMode(TFT_D6, mode); + pinMode(TFT_D7, mode); +} + +/*************************************************************************************** +** Function name: GPIO direction control - supports class functions +** Description: Set ESP32 GPIO pin to input or output (set high) ASAP +***************************************************************************************/ +void TFT_eSPI::gpioMode(uint8_t gpio, uint8_t mode) +{ + pinMode(gpio, mode); + digitalWrite(gpio, HIGH); +} +//////////////////////////////////////////////////////////////////////////////////////// +#endif // #ifdef TFT_PARALLEL_8_BIT +//////////////////////////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////////////////////////// +#if defined (RPI_WRITE_STROBE) && !defined (TFT_PARALLEL_8_BIT) // Code for RPi TFT +//////////////////////////////////////////////////////////////////////////////////////// + +/*************************************************************************************** +** Function name: pushBlock - for ESP32 or ESP8266 RPi TFT +** Description: Write a block of pixels of the same colour +***************************************************************************************/ +void TFT_eSPI::pushBlock(uint16_t color, uint32_t len) +{ + uint8_t colorBin[] = { (uint8_t) (color >> 8), (uint8_t) color }; + if(len) spi.writePattern(&colorBin[0], 2, 1); len--; + while(len--) {WR_L; WR_H;} +} + +/*************************************************************************************** +** Function name: pushPixels - for ESP32 or ESP8266 RPi TFT +** Description: Write a sequence of pixels +***************************************************************************************/ +void TFT_eSPI::pushPixels(const void* data_in, uint32_t len) +{ + uint8_t *data = (uint8_t*)data_in; + + if(_swapBytes) { + while ( len-- ) {tft_Write_16(*data); data++;} + return; + } + + while ( len >=64 ) {spi.writePattern(data, 64, 1); data += 64; len -= 64; } + if (len) spi.writePattern(data, len, 1); +} + +//////////////////////////////////////////////////////////////////////////////////////// +#elif !defined (SPI_18BIT_DRIVER) && !defined (TFT_PARALLEL_8_BIT) // Most SPI displays +//////////////////////////////////////////////////////////////////////////////////////// + +/*************************************************************************************** +** Function name: pushBlock - for ESP32 +** Description: Write a block of pixels of the same colour +***************************************************************************************/ +/* +void TFT_eSPI::pushBlock(uint16_t color, uint32_t len){ + + uint32_t color32 = (color<<8 | color >>8)<<16 | (color<<8 | color >>8); + bool empty = true; + volatile uint32_t* spi_w = (volatile uint32_t*)_spi_w; + if (len > 31) + { + *_spi_mosi_dlen = 511; + spi_w[0] = color32; + spi_w[1] = color32; + spi_w[2] = color32; + spi_w[3] = color32; + spi_w[4] = color32; + spi_w[5] = color32; + spi_w[6] = color32; + spi_w[7] = color32; + spi_w[8] = color32; + spi_w[9] = color32; + spi_w[10] = color32; + spi_w[11] = color32; + spi_w[12] = color32; + spi_w[13] = color32; + spi_w[14] = color32; + spi_w[15] = color32; + while(len>31) + { + while ((*_spi_cmd)&SPI_USR); + *_spi_cmd = SPI_USR; + len -= 32; + } + empty = false; + } + + if (len) + { + if(empty) { + for (uint32_t i=0; i <= len; i+=2) *spi_w++ = color32; + } + len = (len << 4) - 1; + while (*_spi_cmd&SPI_USR); + *_spi_mosi_dlen = len; + *_spi_cmd = SPI_USR; + } + while ((*_spi_cmd)&SPI_USR); // Move to later in code to use transmit time usefully? +} +//*/ +//* +void TFT_eSPI::pushBlock(uint16_t color, uint32_t len){ + + volatile uint32_t* spi_w = _spi_w; + uint32_t color32 = (color<<8 | color >>8)<<16 | (color<<8 | color >>8); + uint32_t i = 0; + uint32_t rem = len & 0x1F; + len = len - rem; + + // Start with partial buffer pixels + if (rem) + { + while (*_spi_cmd&SPI_USR); + for (i=0; i < rem; i+=2) *spi_w++ = color32; + *_spi_mosi_dlen = (rem << 4) - 1; +#if CONFIG_IDF_TARGET_ESP32S3 + *_spi_cmd = SPI_UPDATE; + while (*_spi_cmd & SPI_UPDATE); +#endif + *_spi_cmd = SPI_USR; + if (!len) return; //{while (*_spi_cmd&SPI_USR); return; } + i = i>>1; while(i++<16) *spi_w++ = color32; + } + + while (*_spi_cmd&SPI_USR); + if (!rem) while (i++<16) *spi_w++ = color32; + *_spi_mosi_dlen = 511; + + // End with full buffer to maximise useful time for downstream code + while(len) + { + while (*_spi_cmd&SPI_USR); +#if CONFIG_IDF_TARGET_ESP32S3 + *_spi_cmd = SPI_UPDATE; + while (*_spi_cmd & SPI_UPDATE); +#endif + *_spi_cmd = SPI_USR; + len -= 32; + } + + // Do not wait here + //while (*_spi_cmd&SPI_USR); +} +//*/ +/*************************************************************************************** +** Function name: pushSwapBytePixels - for ESP32 +** Description: Write a sequence of pixels with swapped bytes +***************************************************************************************/ +void TFT_eSPI::pushSwapBytePixels(const void* data_in, uint32_t len){ + + uint8_t* data = (uint8_t*)data_in; + uint32_t color[16]; + + if (len > 31) + { + WRITE_PERI_REG(SPI_MOSI_DLEN_REG(SPI_PORT), 511); + while(len>31) + { + uint32_t i = 0; + while(i<16) + { + color[i++] = DAT8TO32(data); + data+=4; + } + while (READ_PERI_REG(SPI_CMD_REG(SPI_PORT))&SPI_USR); + WRITE_PERI_REG(SPI_W0_REG(SPI_PORT), color[0]); + WRITE_PERI_REG(SPI_W1_REG(SPI_PORT), color[1]); + WRITE_PERI_REG(SPI_W2_REG(SPI_PORT), color[2]); + WRITE_PERI_REG(SPI_W3_REG(SPI_PORT), color[3]); + WRITE_PERI_REG(SPI_W4_REG(SPI_PORT), color[4]); + WRITE_PERI_REG(SPI_W5_REG(SPI_PORT), color[5]); + WRITE_PERI_REG(SPI_W6_REG(SPI_PORT), color[6]); + WRITE_PERI_REG(SPI_W7_REG(SPI_PORT), color[7]); + WRITE_PERI_REG(SPI_W8_REG(SPI_PORT), color[8]); + WRITE_PERI_REG(SPI_W9_REG(SPI_PORT), color[9]); + WRITE_PERI_REG(SPI_W10_REG(SPI_PORT), color[10]); + WRITE_PERI_REG(SPI_W11_REG(SPI_PORT), color[11]); + WRITE_PERI_REG(SPI_W12_REG(SPI_PORT), color[12]); + WRITE_PERI_REG(SPI_W13_REG(SPI_PORT), color[13]); + WRITE_PERI_REG(SPI_W14_REG(SPI_PORT), color[14]); + WRITE_PERI_REG(SPI_W15_REG(SPI_PORT), color[15]); +#if CONFIG_IDF_TARGET_ESP32S3 + SET_PERI_REG_MASK(SPI_CMD_REG(SPI_PORT), SPI_UPDATE); + while (READ_PERI_REG(SPI_CMD_REG(SPI_PORT))&SPI_UPDATE); +#endif + SET_PERI_REG_MASK(SPI_CMD_REG(SPI_PORT), SPI_USR); + len -= 32; + } + } + + if (len > 15) + { + uint32_t i = 0; + while(i<8) + { + color[i++] = DAT8TO32(data); + data+=4; + } + while (READ_PERI_REG(SPI_CMD_REG(SPI_PORT))&SPI_USR); + WRITE_PERI_REG(SPI_MOSI_DLEN_REG(SPI_PORT), 255); + WRITE_PERI_REG(SPI_W0_REG(SPI_PORT), color[0]); + WRITE_PERI_REG(SPI_W1_REG(SPI_PORT), color[1]); + WRITE_PERI_REG(SPI_W2_REG(SPI_PORT), color[2]); + WRITE_PERI_REG(SPI_W3_REG(SPI_PORT), color[3]); + WRITE_PERI_REG(SPI_W4_REG(SPI_PORT), color[4]); + WRITE_PERI_REG(SPI_W5_REG(SPI_PORT), color[5]); + WRITE_PERI_REG(SPI_W6_REG(SPI_PORT), color[6]); + WRITE_PERI_REG(SPI_W7_REG(SPI_PORT), color[7]); +#if CONFIG_IDF_TARGET_ESP32S3 + SET_PERI_REG_MASK(SPI_CMD_REG(SPI_PORT), SPI_UPDATE); + while (READ_PERI_REG(SPI_CMD_REG(SPI_PORT))&SPI_UPDATE); +#endif + SET_PERI_REG_MASK(SPI_CMD_REG(SPI_PORT), SPI_USR); + len -= 16; + } + + if (len) + { + while (READ_PERI_REG(SPI_CMD_REG(SPI_PORT))&SPI_USR); + WRITE_PERI_REG(SPI_MOSI_DLEN_REG(SPI_PORT), (len << 4) - 1); + for (uint32_t i=0; i <= (len<<1); i+=4) { + WRITE_PERI_REG(SPI_W0_REG(SPI_PORT)+i, DAT8TO32(data)); data+=4; + } +#if CONFIG_IDF_TARGET_ESP32S3 + SET_PERI_REG_MASK(SPI_CMD_REG(SPI_PORT), SPI_UPDATE); + while (READ_PERI_REG(SPI_CMD_REG(SPI_PORT))&SPI_UPDATE); +#endif + SET_PERI_REG_MASK(SPI_CMD_REG(SPI_PORT), SPI_USR); + } + while (READ_PERI_REG(SPI_CMD_REG(SPI_PORT))&SPI_USR); + +} + +/*************************************************************************************** +** Function name: pushPixels - for ESP32 +** Description: Write a sequence of pixels +***************************************************************************************/ +void TFT_eSPI::pushPixels(const void* data_in, uint32_t len){ + + if(_swapBytes) { + pushSwapBytePixels(data_in, len); + return; + } + + uint32_t *data = (uint32_t*)data_in; + + if (len > 31) + { + WRITE_PERI_REG(SPI_MOSI_DLEN_REG(SPI_PORT), 511); + while(len>31) + { + while (READ_PERI_REG(SPI_CMD_REG(SPI_PORT))&SPI_USR); + WRITE_PERI_REG(SPI_W0_REG(SPI_PORT), *data++); + WRITE_PERI_REG(SPI_W1_REG(SPI_PORT), *data++); + WRITE_PERI_REG(SPI_W2_REG(SPI_PORT), *data++); + WRITE_PERI_REG(SPI_W3_REG(SPI_PORT), *data++); + WRITE_PERI_REG(SPI_W4_REG(SPI_PORT), *data++); + WRITE_PERI_REG(SPI_W5_REG(SPI_PORT), *data++); + WRITE_PERI_REG(SPI_W6_REG(SPI_PORT), *data++); + WRITE_PERI_REG(SPI_W7_REG(SPI_PORT), *data++); + WRITE_PERI_REG(SPI_W8_REG(SPI_PORT), *data++); + WRITE_PERI_REG(SPI_W9_REG(SPI_PORT), *data++); + WRITE_PERI_REG(SPI_W10_REG(SPI_PORT), *data++); + WRITE_PERI_REG(SPI_W11_REG(SPI_PORT), *data++); + WRITE_PERI_REG(SPI_W12_REG(SPI_PORT), *data++); + WRITE_PERI_REG(SPI_W13_REG(SPI_PORT), *data++); + WRITE_PERI_REG(SPI_W14_REG(SPI_PORT), *data++); + WRITE_PERI_REG(SPI_W15_REG(SPI_PORT), *data++); +#if CONFIG_IDF_TARGET_ESP32S3 + SET_PERI_REG_MASK(SPI_CMD_REG(SPI_PORT), SPI_UPDATE); + while (READ_PERI_REG(SPI_CMD_REG(SPI_PORT))&SPI_UPDATE); +#endif + SET_PERI_REG_MASK(SPI_CMD_REG(SPI_PORT), SPI_USR); + len -= 32; + } + } + + if (len) + { + while (READ_PERI_REG(SPI_CMD_REG(SPI_PORT))&SPI_USR); + WRITE_PERI_REG(SPI_MOSI_DLEN_REG(SPI_PORT), (len << 4) - 1); + for (uint32_t i=0; i <= (len<<1); i+=4) WRITE_PERI_REG((SPI_W0_REG(SPI_PORT) + i), *data++); +#if CONFIG_IDF_TARGET_ESP32S3 + SET_PERI_REG_MASK(SPI_CMD_REG(SPI_PORT), SPI_UPDATE); + while (READ_PERI_REG(SPI_CMD_REG(SPI_PORT))&SPI_UPDATE); +#endif + SET_PERI_REG_MASK(SPI_CMD_REG(SPI_PORT), SPI_USR); + } + while (READ_PERI_REG(SPI_CMD_REG(SPI_PORT))&SPI_USR); +} + +//////////////////////////////////////////////////////////////////////////////////////// +#elif defined (SPI_18BIT_DRIVER) // SPI 18-bit colour +//////////////////////////////////////////////////////////////////////////////////////// + +/*************************************************************************************** +** Function name: pushBlock - for ESP32 and 3 byte RGB display +** Description: Write a block of pixels of the same colour +***************************************************************************************/ +void TFT_eSPI::pushBlock(uint16_t color, uint32_t len) +{ + // Split out the colours + uint32_t r = (color & 0xF800)>>8; + uint32_t g = (color & 0x07E0)<<5; + uint32_t b = (color & 0x001F)<<19; + // Concatenate 4 pixels into three 32-bit blocks + uint32_t r0 = r<<24 | b | g | r; + uint32_t r1 = r0>>8 | g<<16; + uint32_t r2 = r1>>8 | b<<8; + + if (len > 19) + { + WRITE_PERI_REG(SPI_MOSI_DLEN_REG(SPI_PORT), 479); + + while(len>19) + { + while (READ_PERI_REG(SPI_CMD_REG(SPI_PORT))&SPI_USR); + WRITE_PERI_REG(SPI_W0_REG(SPI_PORT), r0); + WRITE_PERI_REG(SPI_W1_REG(SPI_PORT), r1); + WRITE_PERI_REG(SPI_W2_REG(SPI_PORT), r2); + WRITE_PERI_REG(SPI_W3_REG(SPI_PORT), r0); + WRITE_PERI_REG(SPI_W4_REG(SPI_PORT), r1); + WRITE_PERI_REG(SPI_W5_REG(SPI_PORT), r2); + WRITE_PERI_REG(SPI_W6_REG(SPI_PORT), r0); + WRITE_PERI_REG(SPI_W7_REG(SPI_PORT), r1); + WRITE_PERI_REG(SPI_W8_REG(SPI_PORT), r2); + WRITE_PERI_REG(SPI_W9_REG(SPI_PORT), r0); + WRITE_PERI_REG(SPI_W10_REG(SPI_PORT), r1); + WRITE_PERI_REG(SPI_W11_REG(SPI_PORT), r2); + WRITE_PERI_REG(SPI_W12_REG(SPI_PORT), r0); + WRITE_PERI_REG(SPI_W13_REG(SPI_PORT), r1); + WRITE_PERI_REG(SPI_W14_REG(SPI_PORT), r2); +#if CONFIG_IDF_TARGET_ESP32S3 + SET_PERI_REG_MASK(SPI_CMD_REG(SPI_PORT), SPI_UPDATE); + while (READ_PERI_REG(SPI_CMD_REG(SPI_PORT))&SPI_UPDATE); +#endif + SET_PERI_REG_MASK(SPI_CMD_REG(SPI_PORT), SPI_USR); + len -= 20; + } + while (READ_PERI_REG(SPI_CMD_REG(SPI_PORT))&SPI_USR); + } + + if (len) + { + WRITE_PERI_REG(SPI_MOSI_DLEN_REG(SPI_PORT), (len * 24) - 1); + WRITE_PERI_REG(SPI_W0_REG(SPI_PORT), r0); + WRITE_PERI_REG(SPI_W1_REG(SPI_PORT), r1); + WRITE_PERI_REG(SPI_W2_REG(SPI_PORT), r2); + WRITE_PERI_REG(SPI_W3_REG(SPI_PORT), r0); + WRITE_PERI_REG(SPI_W4_REG(SPI_PORT), r1); + WRITE_PERI_REG(SPI_W5_REG(SPI_PORT), r2); + if (len > 8 ) + { + WRITE_PERI_REG(SPI_W6_REG(SPI_PORT), r0); + WRITE_PERI_REG(SPI_W7_REG(SPI_PORT), r1); + WRITE_PERI_REG(SPI_W8_REG(SPI_PORT), r2); + WRITE_PERI_REG(SPI_W9_REG(SPI_PORT), r0); + WRITE_PERI_REG(SPI_W10_REG(SPI_PORT), r1); + WRITE_PERI_REG(SPI_W11_REG(SPI_PORT), r2); + WRITE_PERI_REG(SPI_W12_REG(SPI_PORT), r0); + WRITE_PERI_REG(SPI_W13_REG(SPI_PORT), r1); + WRITE_PERI_REG(SPI_W14_REG(SPI_PORT), r2); + } +#if CONFIG_IDF_TARGET_ESP32S3 + SET_PERI_REG_MASK(SPI_CMD_REG(SPI_PORT), SPI_UPDATE); + while (READ_PERI_REG(SPI_CMD_REG(SPI_PORT))&SPI_UPDATE); +#endif + SET_PERI_REG_MASK(SPI_CMD_REG(SPI_PORT), SPI_USR); + while (READ_PERI_REG(SPI_CMD_REG(SPI_PORT))&SPI_USR); + } +} + +/*************************************************************************************** +** Function name: pushPixels - for ESP32 and 3 byte RGB display +** Description: Write a sequence of pixels +***************************************************************************************/ +void TFT_eSPI::pushPixels(const void* data_in, uint32_t len){ + + uint16_t *data = (uint16_t*)data_in; + // ILI9488 write macro is not endianess dependant, hence !_swapBytes + if(!_swapBytes) { while ( len-- ) {tft_Write_16S(*data); data++;} } + else { while ( len-- ) {tft_Write_16(*data); data++;} } +} + +/*************************************************************************************** +** Function name: pushSwapBytePixels - for ESP32 and 3 byte RGB display +** Description: Write a sequence of pixels with swapped bytes +***************************************************************************************/ +void TFT_eSPI::pushSwapBytePixels(const void* data_in, uint32_t len){ + + uint16_t *data = (uint16_t*)data_in; + // ILI9488 write macro is not endianess dependant, so swap byte macro not used here + while ( len-- ) {tft_Write_16(*data); data++;} +} + +//////////////////////////////////////////////////////////////////////////////////////// +#elif defined (TFT_PARALLEL_8_BIT) // Now the code for ESP32 8-bit parallel +//////////////////////////////////////////////////////////////////////////////////////// + +/*************************************************************************************** +** Function name: pushBlock - for ESP32 and parallel display +** Description: Write a block of pixels of the same colour +***************************************************************************************/ +void TFT_eSPI::pushBlock(uint16_t color, uint32_t len){ + if ( (color >> 8) == (color & 0x00FF) ) + { if (!len) return; + tft_Write_16(color); + #if defined (SSD1963_DRIVER) + while (--len) {WR_L; WR_H; WR_L; WR_H; WR_L; WR_H;} + #else + #ifdef PSEUDO_16_BIT + while (--len) {WR_L; WR_H;} + #else + while (--len) {WR_L; WR_H; WR_L; WR_H;} + #endif + #endif + } + else while (len--) {tft_Write_16(color);} +} + +/*************************************************************************************** +** Function name: pushSwapBytePixels - for ESP32 and parallel display +** Description: Write a sequence of pixels with swapped bytes +***************************************************************************************/ +void TFT_eSPI::pushSwapBytePixels(const void* data_in, uint32_t len){ + + uint16_t *data = (uint16_t*)data_in; + while ( len-- ) {tft_Write_16(*data); data++;} +} + +/*************************************************************************************** +** Function name: pushPixels - for ESP32 and parallel display +** Description: Write a sequence of pixels +***************************************************************************************/ +void TFT_eSPI::pushPixels(const void* data_in, uint32_t len){ + + uint16_t *data = (uint16_t*)data_in; + if(_swapBytes) { while ( len-- ) {tft_Write_16(*data); data++; } } + else { while ( len-- ) {tft_Write_16S(*data); data++;} } +} + +//////////////////////////////////////////////////////////////////////////////////////// +#endif // End of display interface specific functions +//////////////////////////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////////////////////////// +#if defined (ESP32_DMA) && !defined (TFT_PARALLEL_8_BIT) // DMA FUNCTIONS +//////////////////////////////////////////////////////////////////////////////////////// + +/*************************************************************************************** +** Function name: dmaBusy +** Description: Check if DMA is busy +***************************************************************************************/ +bool TFT_eSPI::dmaBusy(void) +{ + if (!DMA_Enabled || !spiBusyCheck) return false; + + spi_transaction_t *rtrans; + esp_err_t ret; + uint8_t checks = spiBusyCheck; + for (int i = 0; i < checks; ++i) + { + ret = spi_device_get_trans_result(dmaHAL, &rtrans, 0); + if (ret == ESP_OK) spiBusyCheck--; + } + + //Serial.print("spiBusyCheck=");Serial.println(spiBusyCheck); + if (spiBusyCheck ==0) return false; + return true; +} + + +/*************************************************************************************** +** Function name: dmaWait +** Description: Wait until DMA is over (blocking!) +***************************************************************************************/ +void TFT_eSPI::dmaWait(void) +{ + if (!DMA_Enabled || !spiBusyCheck) return; + spi_transaction_t *rtrans; + esp_err_t ret; + for (int i = 0; i < spiBusyCheck; ++i) + { + ret = spi_device_get_trans_result(dmaHAL, &rtrans, portMAX_DELAY); + assert(ret == ESP_OK); + } + spiBusyCheck = 0; +} + + +/*************************************************************************************** +** Function name: pushPixelsDMA +** Description: Push pixels to TFT (len must be less than 32767) +***************************************************************************************/ +// This will byte swap the original image if setSwapBytes(true) was called by sketch. +void TFT_eSPI::pushPixelsDMA(uint16_t* image, uint32_t len) +{ + if ((len == 0) || (!DMA_Enabled)) return; + + dmaWait(); + + if(_swapBytes) { + for (uint32_t i = 0; i < len; i++) (image[i] = image[i] << 8 | image[i] >> 8); + } + + // DMA byte count for transmit is 64Kbytes maximum, so to avoid this constraint + // small transfers are performed using a blocking call until DMA capacity is reached. + // User sketch can prevent blocking by managing pixel count and splitting into blocks + // of 32768 pixels maximum. (equivalent to an area of ~320 x 100 pixels) + bool temp = _swapBytes; + _swapBytes = false; + while(len>0x4000) { // Transfer 16-bit pixels in blocks if len*2 over 65536 bytes + pushPixels(image, 0x400); + len -= 0x400; image+= 0x400; // Arbitrarily send 1K pixel blocks (2Kbytes) + } + _swapBytes = temp; + + esp_err_t ret; + static spi_transaction_t trans; + + memset(&trans, 0, sizeof(spi_transaction_t)); + + trans.user = (void *)1; + trans.tx_buffer = image; //finally send the line data + trans.length = len * 16; //Data length, in bits + trans.flags = 0; //SPI_TRANS_USE_TXDATA flag + + ret = spi_device_queue_trans(dmaHAL, &trans, portMAX_DELAY); + assert(ret == ESP_OK); + + spiBusyCheck++; +} + + +/*************************************************************************************** +** Function name: pushImageDMA +** Description: Push image to a window (w*h must be less than 65536) +***************************************************************************************/ +// Fixed const data assumed, will NOT clip or swap bytes +void TFT_eSPI::pushImageDMA(int32_t x, int32_t y, int32_t w, int32_t h, uint16_t const* image) +{ + if ((w == 0) || (h == 0) || (!DMA_Enabled)) return; + + uint16_t *buffer = (uint16_t*)image; + uint32_t len = w*h; + + dmaWait(); + + setAddrWindow(x, y, w, h); + // DMA byte count for transmit is 64Kbytes maximum, so to avoid this constraint + // small transfers are performed using a blocking call until DMA capacity is reached. + // User sketch can prevent blocking by managing pixel count and splitting into blocks + // of 32768 pixels maximum. (equivalent to an area of ~320 x 100 pixels) + bool temp = _swapBytes; + _swapBytes = false; + while(len>0x4000) { // Transfer 16-bit pixels in blocks if len*2 over 65536 bytes + pushPixels(buffer, 0x400); + len -= 0x400; buffer+= 0x400; // Arbitrarily send 1K pixel blocks (2Kbytes) + } + _swapBytes = temp; + + esp_err_t ret; + static spi_transaction_t trans; + + memset(&trans, 0, sizeof(spi_transaction_t)); + + trans.user = (void *)1; + trans.tx_buffer = buffer; //Data pointer + trans.length = len * 16; //Data length, in bits + trans.flags = 0; //SPI_TRANS_USE_TXDATA flag + + ret = spi_device_queue_trans(dmaHAL, &trans, portMAX_DELAY); + assert(ret == ESP_OK); + + spiBusyCheck++; +} + + +/*************************************************************************************** +** Function name: pushImageDMA +** Description: Push image to a window (w*h must be less than 65536) +***************************************************************************************/ +// This will clip and also swap bytes if setSwapBytes(true) was called by sketch +void TFT_eSPI::pushImageDMA(int32_t x, int32_t y, int32_t w, int32_t h, uint16_t* image, uint16_t* buffer) +{ + if ((x >= _vpW) || (y >= _vpH) || (!DMA_Enabled)) return; + + int32_t dx = 0; + int32_t dy = 0; + int32_t dw = w; + int32_t dh = h; + + if (x < _vpX) { dx = _vpX - x; dw -= dx; x = _vpX; } + if (y < _vpY) { dy = _vpY - y; dh -= dy; y = _vpY; } + + if ((x + dw) > _vpW ) dw = _vpW - x; + if ((y + dh) > _vpH ) dh = _vpH - y; + + if (dw < 1 || dh < 1) return; + + uint32_t len = dw*dh; + + if (buffer == nullptr) { + buffer = image; + dmaWait(); + } + + // If image is clipped, copy pixels into a contiguous block + if ( (dw != w) || (dh != h) ) { + if(_swapBytes) { + for (int32_t yb = 0; yb < dh; yb++) { + for (int32_t xb = 0; xb < dw; xb++) { + uint32_t src = xb + dx + w * (yb + dy); + (buffer[xb + yb * dw] = image[src] << 8 | image[src] >> 8); + } + } + } + else { + for (int32_t yb = 0; yb < dh; yb++) { + memcpy((uint8_t*) (buffer + yb * dw), (uint8_t*) (image + dx + w * (yb + dy)), dw << 1); + } + } + } + // else, if a buffer pointer has been provided copy whole image to the buffer + else if (buffer != image || _swapBytes) { + if(_swapBytes) { + for (uint32_t i = 0; i < len; i++) (buffer[i] = image[i] << 8 | image[i] >> 8); + } + else { + memcpy(buffer, image, len*2); + } + } + + if (spiBusyCheck) dmaWait(); // In case we did not wait earlier + + setAddrWindow(x, y, dw, dh); + + // DMA byte count for transmit is 64Kbytes maximum, so to avoid this constraint + // small transfers are performed using a blocking call until DMA capacity is reached. + // User sketch can prevent blocking by managing pixel count and splitting into blocks + // of 32768 pixels maximum. (equivalent to an area of ~320 x 100 pixels) + bool temp = _swapBytes; + _swapBytes = false; + while(len>0x4000) { // Transfer 16-bit pixels in blocks if len*2 over 65536 bytes + pushPixels(buffer, 0x400); + len -= 0x400; buffer+= 0x400; // Arbitrarily send 1K pixel blocks (2Kbytes) + } + _swapBytes = temp; + + esp_err_t ret; + static spi_transaction_t trans; + + memset(&trans, 0, sizeof(spi_transaction_t)); + + trans.user = (void *)1; + trans.tx_buffer = buffer; //finally send the line data + trans.length = len * 16; //Data length, in bits + trans.flags = 0; //SPI_TRANS_USE_TXDATA flag + + ret = spi_device_queue_trans(dmaHAL, &trans, portMAX_DELAY); + assert(ret == ESP_OK); + + spiBusyCheck++; +} + +//////////////////////////////////////////////////////////////////////////////////////// +// Processor specific DMA initialisation +//////////////////////////////////////////////////////////////////////////////////////// + +// The DMA functions here work with SPI only (not parallel) +/*************************************************************************************** +** Function name: dc_callback +** Description: Toggles DC line during transaction (not used) +***************************************************************************************/ +extern "C" void dc_callback(); + +void IRAM_ATTR dc_callback(spi_transaction_t *spi_tx) +{ + if ((bool)spi_tx->user) {DC_D;} + else {DC_C;} +} + +/*************************************************************************************** +** Function name: dma_end_callback +** Description: Clear DMA run flag to stop retransmission loop +***************************************************************************************/ +extern "C" void dma_end_callback(); + +void IRAM_ATTR dma_end_callback(spi_transaction_t *spi_tx) +{ + //WRITE_PERI_REG(SPI_DMA_CONF_REG(spi_host), 0); + WRITE_PERI_REG( SPI_DMA_CONF_REG(SPI_DMA_CH_AUTO) , 0b11 ); +} + +/*************************************************************************************** +** Function name: initDMA +** Description: Initialise the DMA engine - returns true if init OK +***************************************************************************************/ +bool TFT_eSPI::initDMA(bool ctrl_cs) +{ + if (DMA_Enabled) return false; + + esp_err_t ret; + spi_bus_config_t buscfg = { + .mosi_io_num = TFT_MOSI, + .miso_io_num = TFT_MISO, + .sclk_io_num = TFT_SCLK, + .quadwp_io_num = -1, + .quadhd_io_num = -1, + .data4_io_num = -1, + .data5_io_num = -1, + .data6_io_num = -1, + .data7_io_num = -1, + .max_transfer_sz = 65536, // ESP32 S3 max size is 64Kbytes + .flags = 0, + .intr_flags = 0 + }; + + int8_t pin = -1; + if (ctrl_cs) pin = TFT_CS; + + spi_device_interface_config_t devcfg = { + .command_bits = 0, + .address_bits = 0, + .dummy_bits = 0, + .mode = TFT_SPI_MODE, + .duty_cycle_pos = 0, + .cs_ena_pretrans = 0, + .cs_ena_posttrans = 0, + .clock_speed_hz = SPI_FREQUENCY, + .input_delay_ns = 0, + .spics_io_num = pin, + .flags = SPI_DEVICE_NO_DUMMY, //0, + .queue_size = 1, // Not using queues + .pre_cb = 0, //dc_callback, //Callback to handle D/C line (not used) + .post_cb = dma_end_callback //Callback to end transmission + }; + ret = spi_bus_initialize(spi_host, &buscfg, DMA_CHANNEL); + ESP_ERROR_CHECK(ret); + ret = spi_bus_add_device(spi_host, &devcfg, &dmaHAL); + ESP_ERROR_CHECK(ret); + + DMA_Enabled = true; + spiBusyCheck = 0; + return true; +} + +/*************************************************************************************** +** Function name: deInitDMA +** Description: Disconnect the DMA engine from SPI +***************************************************************************************/ +void TFT_eSPI::deInitDMA(void) +{ + if (!DMA_Enabled) return; + spi_bus_remove_device(dmaHAL); + spi_bus_free(spi_host); + DMA_Enabled = false; +} + +//////////////////////////////////////////////////////////////////////////////////////// +#endif // End of DMA FUNCTIONS +//////////////////////////////////////////////////////////////////////////////////////// diff --git a/lib/Seeed_GFX/Processors/TFT_eSPI_ESP32_S3.h b/lib/Seeed_GFX/Processors/TFT_eSPI_ESP32_S3.h new file mode 100644 index 0000000..12cbdfb --- /dev/null +++ b/lib/Seeed_GFX/Processors/TFT_eSPI_ESP32_S3.h @@ -0,0 +1,612 @@ + //////////////////////////////////////////////////// + // TFT_eSPI driver functions for ESP32 processors // + //////////////////////////////////////////////////// + +// Temporarily a separate file to TFT_eSPI_ESP32.h until board package low level API stabilises + +#ifndef _TFT_eSPI_ESP32H_ +#define _TFT_eSPI_ESP32H_ + +// Processor ID reported by getSetup() +#define PROCESSOR_ID 0x32 + +// Include processor specific header +#include "soc/spi_reg.h" +#include "driver/spi_master.h" +#include "hal/gpio_ll.h" + +#if !defined(CONFIG_IDF_TARGET_ESP32S3) && !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32) + #define CONFIG_IDF_TARGET_ESP32 +#endif + +#ifndef VSPI + #define VSPI FSPI +#endif + +// Fix IDF problems with ESP32S3 +// Note illogical enumerations: FSPI_HOST=SPI2_HOST=1 HSPI_HOST=SPI3_HOST=2 +#if CONFIG_IDF_TARGET_ESP32S3 + // Fix ESP32C3 IDF bug for missing definition (FSPI only tested at the moment) + #ifndef REG_SPI_BASE // HSPI FSPI/VSPI + #define REG_SPI_BASE(i) (((i)>1) ? (DR_REG_SPI3_BASE) : (DR_REG_SPI2_BASE)) + #endif + + // Fix ESP32S3 IDF bug for name change + #ifndef SPI_MOSI_DLEN_REG + #define SPI_MOSI_DLEN_REG(x) SPI_MS_DLEN_REG(x) + #endif + +#endif + +// SUPPORT_TRANSACTIONS is mandatory for ESP32 so the hal mutex is toggled +#if !defined (SUPPORT_TRANSACTIONS) + #define SUPPORT_TRANSACTIONS +#endif + +/* +ESP32: +FSPI not defined +HSPI = 2, uses SPI2 +VSPI = 3, uses SPI3 + +ESP32-S2: +FSPI = 1, uses SPI2 +HSPI = 2, uses SPI3 +VSPI not defined + +ESP32 C3: +FSPI = 0, uses SPI2 ???? To be checked +HSPI = 1, uses SPI3 ???? To be checked +VSPI not defined + +For ESP32/S2/C3/S3: +SPI1_HOST = 0 +SPI2_HOST = 1 +SPI3_HOST = 2 +*/ + +// ESP32 specific SPI port selection +#ifdef USE_HSPI_PORT + #ifdef CONFIG_IDF_TARGET_ESP32 + #define SPI_PORT HSPI //HSPI is port 2 on ESP32 + #else + #define SPI_PORT 3 //HSPI is port 3 on ESP32 S2 + #endif +#elif defined(USE_FSPI_PORT) + #define SPI_PORT 2 //FSPI(ESP32 S2) +#else + #ifdef CONFIG_IDF_TARGET_ESP32 + #define SPI_PORT VSPI + #elif CONFIG_IDF_TARGET_ESP32S2 + #define SPI_PORT 2 //FSPI(ESP32 S2) + #elif CONFIG_IDF_TARGET_ESP32S3 + #define SPI_PORT 2//FSPI->2 asd1 + #endif +#endif + +#ifdef RPI_DISPLAY_TYPE + #define CMD_BITS (16-1) +#else + #define CMD_BITS (8-1) +#endif + +// Initialise processor specific SPI functions, used by init() +#define INIT_TFT_DATA_BUS // Not used + +// Define a generic flag for 8-bit parallel +#if defined (ESP32_PARALLEL) // Specific to ESP32 for backwards compatibility + #if !defined (TFT_PARALLEL_8_BIT) + #define TFT_PARALLEL_8_BIT // Generic parallel flag + #endif +#endif + +// Ensure ESP32 specific flag is defined for 8-bit parallel +#if defined (TFT_PARALLEL_8_BIT) + #if !defined (ESP32_PARALLEL) + #define ESP32_PARALLEL + #endif +#endif + +#if !defined(DISABLE_ALL_LIBRARY_WARNINGS) && defined (ESP32_PARALLEL) + #warning >>>>------>> DMA is not supported in parallel mode +#endif + +// Processor specific code used by SPI bus transaction startWrite and endWrite functions +#if !defined (ESP32_PARALLEL) + #define _spi_cmd (volatile uint32_t*)(SPI_CMD_REG(SPI_PORT)) + #define _spi_user (volatile uint32_t*)(SPI_USER_REG(SPI_PORT)) + #define _spi_mosi_dlen (volatile uint32_t*)(SPI_MOSI_DLEN_REG(SPI_PORT)) + #define _spi_w (volatile uint32_t*)(SPI_W0_REG(SPI_PORT)) + + #if (TFT_SPI_MODE == SPI_MODE1) || (TFT_SPI_MODE == SPI_MODE2) + #define SET_BUS_WRITE_MODE *_spi_user = SPI_USR_MOSI | SPI_CK_OUT_EDGE + #define SET_BUS_READ_MODE *_spi_user = SPI_USR_MOSI | SPI_USR_MISO | SPI_DOUTDIN | SPI_CK_OUT_EDGE + #else + #define SET_BUS_WRITE_MODE *_spi_user = SPI_USR_MOSI + #define SET_BUS_READ_MODE *_spi_user = SPI_USR_MOSI | SPI_USR_MISO | SPI_DOUTDIN + #endif +#else + // Not applicable to parallel bus + #define SET_BUS_WRITE_MODE + #define SET_BUS_READ_MODE +#endif + +// Code to check if DMA is busy, used by SPI bus transaction transaction and endWrite functions +#if !defined(TFT_PARALLEL_8_BIT) && !defined(SPI_18BIT_DRIVER) + #define ESP32_DMA + // Code to check if DMA is busy, used by SPI DMA + transaction + endWrite functions + #define DMA_BUSY_CHECK dmaWait() +#else + #define DMA_BUSY_CHECK +#endif + +#if defined(TFT_PARALLEL_8_BIT) + #define SPI_BUSY_CHECK +#else + #define SPI_BUSY_CHECK while (*_spi_cmd&SPI_USR) +#endif + +// If smooth font is used then it is likely SPIFFS will be needed +#ifdef SMOOTH_FONT + // Call up the SPIFFS (SPI FLASH Filing System) for the anti-aliased fonts + #define FS_NO_GLOBALS + #include + #include "SPIFFS.h" // ESP32 only + #define FONT_FS_AVAILABLE +#endif + +//////////////////////////////////////////////////////////////////////////////////////// +// Define the DC (TFT Data/Command or Register Select (RS))pin drive code +//////////////////////////////////////////////////////////////////////////////////////// +#ifndef TFT_DC + #define DC_C // No macro allocated so it generates no code + #define DC_D // No macro allocated so it generates no code +#else + #if defined (TFT_PARALLEL_8_BIT) + // TFT_DC, by design, must be in range 0-31 for single register parallel write + #if (TFT_DC >= 0) && (TFT_DC < 32) + #define DC_C GPIO.out_w1tc = (1 << TFT_DC) + #define DC_D GPIO.out_w1ts = (1 << TFT_DC) + #elif (TFT_DC >= 32) + #define DC_C GPIO.out1_w1tc.val = (1 << (TFT_DC- 32)) + #define DC_D GPIO.out1_w1ts.val = (1 << (TFT_DC- 32)) + #else + #define DC_C + #define DC_D + #endif + #else + #if (TFT_DC >= 32) + #ifdef RPI_DISPLAY_TYPE // RPi displays need a slower DC change + #define DC_C GPIO.out1_w1ts.val = (1 << (TFT_DC - 32)); \ + GPIO.out1_w1tc.val = (1 << (TFT_DC - 32)) + #define DC_D GPIO.out1_w1tc.val = (1 << (TFT_DC - 32)); \ + GPIO.out1_w1ts.val = (1 << (TFT_DC - 32)) + #else + #define DC_C GPIO.out1_w1tc.val = (1 << (TFT_DC - 32))//;GPIO.out1_w1tc.val = (1 << (TFT_DC - 32)) + #define DC_D GPIO.out1_w1ts.val = (1 << (TFT_DC - 32))//;GPIO.out1_w1ts.val = (1 << (TFT_DC - 32)) + #endif + #elif (TFT_DC >= 0) + #if defined (RPI_DISPLAY_TYPE) + #if defined (ILI9486_DRIVER) + // RPi ILI9486 display needs a slower DC change + #define DC_C GPIO.out_w1tc = (1 << TFT_DC); \ + GPIO.out_w1tc = (1 << TFT_DC) + #define DC_D GPIO.out_w1tc = (1 << TFT_DC); \ + GPIO.out_w1ts = (1 << TFT_DC) + #else + // Other RPi displays need a slower C->D change + #define DC_C GPIO.out_w1tc = (1 << TFT_DC) + #define DC_D GPIO.out_w1tc = (1 << TFT_DC); \ + GPIO.out_w1ts = (1 << TFT_DC) + #endif + #else + #define DC_C GPIO.out_w1tc = (1 << TFT_DC)//;GPIO.out_w1tc = (1 << TFT_DC) + #define DC_D GPIO.out_w1ts = (1 << TFT_DC)//;GPIO.out_w1ts = (1 << TFT_DC) + #endif + #else + #define DC_C + #define DC_D + #endif + #endif +#endif + +//////////////////////////////////////////////////////////////////////////////////////// +// Define the CS (TFT chip select) pin drive code +//////////////////////////////////////////////////////////////////////////////////////// +#ifndef TFT_CS + #define TFT_CS -1 // Keep DMA code happy + #define CS_L // No macro allocated so it generates no code + #define CS_H // No macro allocated so it generates no code +#else + #if defined (TFT_PARALLEL_8_BIT) + #if TFT_CS >= 32 + #define CS_L GPIO.out1_w1tc.val = (1 << (TFT_CS - 32)) + #define CS_H GPIO.out1_w1ts.val = (1 << (TFT_CS - 32)) + #elif TFT_CS >= 0 + #define CS_L GPIO.out_w1tc = (1 << TFT_CS) + #define CS_H GPIO.out_w1ts = (1 << TFT_CS) + #else + #define CS_L + #define CS_H + #endif + #else + #if (TFT_CS >= 32) + #ifdef RPI_DISPLAY_TYPE // RPi display needs a slower CS change + #define CS_L GPIO.out1_w1ts.val = (1 << (TFT_CS - 32)); \ + GPIO.out1_w1tc.val = (1 << (TFT_CS - 32)) + #define CS_H GPIO.out1_w1tc.val = (1 << (TFT_CS - 32)); \ + GPIO.out1_w1ts.val = (1 << (TFT_CS - 32)) + #else + #define CS_L GPIO.out1_w1tc.val = (1 << (TFT_CS - 32)); GPIO.out1_w1tc.val = (1 << (TFT_CS - 32)) + #define CS_H GPIO.out1_w1ts.val = (1 << (TFT_CS - 32))//;GPIO.out1_w1ts.val = (1 << (TFT_CS - 32)) + #endif + #elif (TFT_CS >= 0) + #ifdef RPI_DISPLAY_TYPE // RPi display needs a slower CS change + #define CS_L GPIO.out_w1ts = (1 << TFT_CS); GPIO.out_w1tc = (1 << TFT_CS) + #define CS_H GPIO.out_w1tc = (1 << TFT_CS); GPIO.out_w1ts = (1 << TFT_CS) + #else + #define CS_L GPIO.out_w1tc = (1 << TFT_CS); GPIO.out_w1tc = (1 << TFT_CS) + #define CS_H GPIO.out_w1ts = (1 << TFT_CS)//;GPIO.out_w1ts = (1 << TFT_CS) + #endif + #else + #define CS_L + #define CS_H + #endif + #endif +#endif + +//////////////////////////////////////////////////////////////////////////////////////// +// Define the WR (TFT Write) pin drive code +//////////////////////////////////////////////////////////////////////////////////////// +#if defined (TFT_WR) + #if (TFT_WR >= 32) + // Note: it will be ~1.25x faster if the TFT_WR pin uses a GPIO pin lower than 32 + #define WR_L GPIO.out1_w1tc.val = (1 << (TFT_WR - 32)) + #define WR_H GPIO.out1_w1ts.val = (1 << (TFT_WR - 32)) + #elif (TFT_WR >= 0) + // TFT_WR, for best performance, should be in range 0-31 for single register parallel write + #define WR_L GPIO.out_w1tc = (1 << TFT_WR) + #define WR_H GPIO.out_w1ts = (1 << TFT_WR) + #else + #define WR_L + #define WR_H + #endif +#else + #define WR_L + #define WR_H +#endif + +//////////////////////////////////////////////////////////////////////////////////////// +// Define the touch screen chip select pin drive code +//////////////////////////////////////////////////////////////////////////////////////// +#ifndef TOUCH_CS + #define T_CS_L // No macro allocated so it generates no code + #define T_CS_H // No macro allocated so it generates no code +#else // XPT2046 is slow, so use slower digitalWrite here + #define T_CS_L digitalWrite(TOUCH_CS, LOW) + #define T_CS_H digitalWrite(TOUCH_CS, HIGH) +#endif + +//////////////////////////////////////////////////////////////////////////////////////// +// Make sure SPI default pins are assigned if not specified by user or set to -1 +//////////////////////////////////////////////////////////////////////////////////////// +#if !defined (TFT_PARALLEL_8_BIT) + + #ifdef USE_HSPI_PORT + + #ifndef TFT_MISO + #define TFT_MISO -1 + #endif + + #ifndef TFT_MOSI + #define TFT_MOSI 13 + #endif + #if (TFT_MOSI == -1) + #undef TFT_MOSI + #define TFT_MOSI 13 + #endif + + #ifndef TFT_SCLK + #define TFT_SCLK 14 + #endif + #if (TFT_SCLK == -1) + #undef TFT_SCLK + #define TFT_SCLK 14 + #endif + + #else // VSPI port + + #ifndef TFT_MISO + #define TFT_MISO -1 + #endif + + #ifndef TFT_MOSI + #define TFT_MOSI 23 + #endif + #if (TFT_MOSI == -1) + #undef TFT_MOSI + #define TFT_MOSI 23 + #endif + + #ifndef TFT_SCLK + #define TFT_SCLK 18 + #endif + #if (TFT_SCLK == -1) + #undef TFT_SCLK + #define TFT_SCLK 18 + #endif + + #if defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32S2) + #if (TFT_MISO == -1) + #undef TFT_MISO + #define TFT_MISO TFT_MOSI + #endif + #endif + + #endif + +#endif + +//////////////////////////////////////////////////////////////////////////////////////// +// Define the parallel bus interface chip pin drive code +//////////////////////////////////////////////////////////////////////////////////////// +#if defined (TFT_PARALLEL_8_BIT) + + #if (TFT_D0 >= 32) // If D0 is a high GPIO assume all other data bits are high GPIO + #define MASK_OFFSET 32 + #define GPIO_CLR_REG GPIO.out1_w1tc.val + #define GPIO_SET_REG GPIO.out1_w1ts.val + #else + #define MASK_OFFSET 0 + #define GPIO_CLR_REG GPIO.out_w1tc + #define GPIO_SET_REG GPIO.out_w1ts + #endif + + // Create a bit set lookup table for data bus - wastes 1kbyte of RAM but speeds things up dramatically + // can then use e.g. GPIO.out_w1ts = set_mask(0xFF); to set data bus to 0xFF + #define PARALLEL_INIT_TFT_DATA_BUS \ + for (int32_t c = 0; c<256; c++) \ + { \ + xset_mask[c] = 0; \ + if ( c & 0x01 ) xset_mask[c] |= (1 << (TFT_D0-MASK_OFFSET)); \ + if ( c & 0x02 ) xset_mask[c] |= (1 << (TFT_D1-MASK_OFFSET)); \ + if ( c & 0x04 ) xset_mask[c] |= (1 << (TFT_D2-MASK_OFFSET)); \ + if ( c & 0x08 ) xset_mask[c] |= (1 << (TFT_D3-MASK_OFFSET)); \ + if ( c & 0x10 ) xset_mask[c] |= (1 << (TFT_D4-MASK_OFFSET)); \ + if ( c & 0x20 ) xset_mask[c] |= (1 << (TFT_D5-MASK_OFFSET)); \ + if ( c & 0x40 ) xset_mask[c] |= (1 << (TFT_D6-MASK_OFFSET)); \ + if ( c & 0x80 ) xset_mask[c] |= (1 << (TFT_D7-MASK_OFFSET)); \ + } \ + + // Mask for the 8 data bits to set pin directions + #define GPIO_DIR_MASK ((1 << (TFT_D0-MASK_OFFSET)) | (1 << (TFT_D1-MASK_OFFSET)) | (1 << (TFT_D2-MASK_OFFSET)) | (1 << (TFT_D3-MASK_OFFSET)) | (1 << (TFT_D4-MASK_OFFSET)) | (1 << (TFT_D5-MASK_OFFSET)) | (1 << (TFT_D6-MASK_OFFSET)) | (1 << (TFT_D7-MASK_OFFSET))) + + #if (TFT_WR >= 32) + #if (TFT_D0 >= 32) + // Data bits and the write line are cleared to 0 in one step (1.25x faster) + #define GPIO_OUT_CLR_MASK (GPIO_DIR_MASK | (1 << (TFT_WR-32))) + #elif (TFT_D0 >= 0) + // Data bits and the write line are cleared sequentially + #define GPIO_OUT_CLR_MASK (GPIO_DIR_MASK); WR_L + #endif + #elif (TFT_WR >= 0) + #if (TFT_D0 >= 32) + // Data bits and the write line are cleared sequentially + #define GPIO_OUT_CLR_MASK (GPIO_DIR_MASK); WR_L + #elif (TFT_D0 >= 0) + // Data bits and the write line are cleared to 0 in one step (1.25x faster) + #define GPIO_OUT_CLR_MASK (GPIO_DIR_MASK | (1 << TFT_WR)) + #endif + #else + #define GPIO_OUT_CLR_MASK + #endif + + // A lookup table is used to set the different bit patterns, this uses 1kByte of RAM + #define set_mask(C) xset_mask[C] // 63fps Sprite rendering test 33% faster, graphicstest only 1.8% faster than shifting in real time + + // Real-time shifting alternative to above to save 1KByte RAM, 47 fps Sprite rendering test + /*#define set_mask(C) (((C)&0x80)>>7)<>6)<>5)<>4)<>3)<>2)<>1)<>0)<> 8)); WR_H; \ + GPIO.out_w1tc = GPIO_OUT_CLR_MASK; GPIO.out_w1ts = set_mask((uint8_t) (((C) & 0x07E0)>> 3)); WR_H; \ + GPIO.out_w1tc = GPIO_OUT_CLR_MASK; GPIO.out_w1ts = set_mask((uint8_t) (((C) & 0x001F)<< 3)); WR_H + + // 18-bit color write with swapped bytes + #define tft_Write_16S(C) Cswap = ((C) >>8 | (C) << 8); tft_Write_16(Cswap) + + #else + + #ifdef PSEUDO_16_BIT + // One write strobe for both bytes + #define tft_Write_16(C) GPIO.out_w1tc = GPIO_OUT_CLR_MASK; GPIO.out_w1ts = set_mask((uint8_t) ((C) >> 0)); WR_H + #define tft_Write_16S(C) GPIO.out_w1tc = GPIO_OUT_CLR_MASK; GPIO.out_w1ts = set_mask((uint8_t) ((C) >> 8)); WR_H + #else + // Write 16 bits to TFT + #define tft_Write_16(C) GPIO_CLR_REG = GPIO_OUT_CLR_MASK; GPIO_SET_REG = set_mask((uint8_t) ((C) >> 8)); WR_H; \ + GPIO_CLR_REG = GPIO_OUT_CLR_MASK; GPIO_SET_REG = set_mask((uint8_t) ((C) >> 0)); WR_H + + // 16-bit write with swapped bytes + #define tft_Write_16S(C) GPIO_CLR_REG = GPIO_OUT_CLR_MASK; GPIO_SET_REG = set_mask((uint8_t) ((C) >> 0)); WR_H; \ + GPIO_CLR_REG = GPIO_OUT_CLR_MASK; GPIO_SET_REG = set_mask((uint8_t) ((C) >> 8)); WR_H + #endif + + #endif + + // Write 32 bits to TFT + #define tft_Write_32(C) GPIO_CLR_REG = GPIO_OUT_CLR_MASK; GPIO_SET_REG = set_mask((uint8_t) ((C) >> 24)); WR_H; \ + GPIO_CLR_REG = GPIO_OUT_CLR_MASK; GPIO_SET_REG = set_mask((uint8_t) ((C) >> 16)); WR_H; \ + GPIO_CLR_REG = GPIO_OUT_CLR_MASK; GPIO_SET_REG = set_mask((uint8_t) ((C) >> 8)); WR_H; \ + GPIO_CLR_REG = GPIO_OUT_CLR_MASK; GPIO_SET_REG = set_mask((uint8_t) ((C) >> 0)); WR_H + + // Write two concatenated 16-bit values to TFT + #define tft_Write_32C(C,D) GPIO_CLR_REG = GPIO_OUT_CLR_MASK; GPIO_SET_REG = set_mask((uint8_t) ((C) >> 8)); WR_H; \ + GPIO_CLR_REG = GPIO_OUT_CLR_MASK; GPIO_SET_REG = set_mask((uint8_t) ((C) >> 0)); WR_H; \ + GPIO_CLR_REG = GPIO_OUT_CLR_MASK; GPIO_SET_REG = set_mask((uint8_t) ((D) >> 8)); WR_H; \ + GPIO_CLR_REG = GPIO_OUT_CLR_MASK; GPIO_SET_REG = set_mask((uint8_t) ((D) >> 0)); WR_H + + // Write 16-bit value twice to TFT - used by drawPixel() + #define tft_Write_32D(C) GPIO_CLR_REG = GPIO_OUT_CLR_MASK; GPIO_SET_REG = set_mask((uint8_t) ((C) >> 8)); WR_H; \ + GPIO_CLR_REG = GPIO_OUT_CLR_MASK; GPIO_SET_REG = set_mask((uint8_t) ((C) >> 0)); WR_H; \ + GPIO_CLR_REG = GPIO_OUT_CLR_MASK; GPIO_SET_REG = set_mask((uint8_t) ((C) >> 8)); WR_H; \ + GPIO_CLR_REG = GPIO_OUT_CLR_MASK; GPIO_SET_REG = set_mask((uint8_t) ((C) >> 0)); WR_H + + // Read pin + #ifdef TFT_RD + #if (TFT_RD >= 32) + #define RD_L GPIO.out1_w1tc.val = (1 << (TFT_RD - 32)) + #define RD_H GPIO.out1_w1ts.val = (1 << (TFT_RD - 32)) + #elif (TFT_RD >= 0) + #define RD_L GPIO.out_w1tc = (1 << TFT_RD) + //#define RD_L digitalWrite(TFT_WR, LOW) + #define RD_H GPIO.out_w1ts = (1 << TFT_RD) + //#define RD_H digitalWrite(TFT_WR, HIGH) + #else + #define RD_L + #define RD_H + #endif + #else + #define TFT_RD -1 + #define RD_L + #define RD_H + #endif + +//////////////////////////////////////////////////////////////////////////////////////// +// Macros to write commands/pixel colour data to a SPI ILI948x TFT +//////////////////////////////////////////////////////////////////////////////////////// +#elif defined (SPI_18BIT_DRIVER) // SPI 18-bit colour + + // Write 8 bits to TFT + #define tft_Write_8(C) spi.transfer(C) + + // Convert 16-bit colour to 18-bit and write in 3 bytes + #define tft_Write_16(C) spi.transfer(((C) & 0xF800)>>8); \ + spi.transfer(((C) & 0x07E0)>>3); \ + spi.transfer(((C) & 0x001F)<<3) + + // Future option for transfer without wait + #define tft_Write_16N(C) tft_Write_16(C) + + // Convert swapped byte 16-bit colour to 18-bit and write in 3 bytes + #define tft_Write_16S(C) spi.transfer((C) & 0xF8); \ + spi.transfer(((C) & 0xE000)>>11 | ((C) & 0x07)<<5); \ + spi.transfer(((C) & 0x1F00)>>5) + + // Write 32 bits to TFT + #define tft_Write_32(C) spi.write32(C) + + // Write two concatenated 16-bit values to TFT + #define tft_Write_32C(C,D) spi.write32((C)<<16 | (D)) + + // Write 16-bit value twice to TFT + #define tft_Write_32D(C) spi.write32((C)<<16 | (C)) + +//////////////////////////////////////////////////////////////////////////////////////// +// Macros to write commands/pixel colour data to an Raspberry Pi TFT +//////////////////////////////////////////////////////////////////////////////////////// +#elif defined (RPI_DISPLAY_TYPE) + // ESP32-S3 low level SPI writes for 8, 16 and 32-bit values + // to avoid the function call overhead + #define TFT_WRITE_BITS(D, B) *_spi_mosi_dlen = B-1; \ + *_spi_w = D; \ + *_spi_cmd = SPI_UPDATE; \ + while (*_spi_cmd & SPI_UPDATE); \ + *_spi_cmd = SPI_USR; \ + while (*_spi_cmd & SPI_USR); + + // Write 8 bits + #define tft_Write_8(C) TFT_WRITE_BITS((C)<<8, 16) + + // Write 16 bits with corrected endianness for 16-bit colours + #define tft_Write_16(C) TFT_WRITE_BITS((C)<<8 | (C)>>8, 16) + + // Future option for transfer without wait + #define tft_Write_16N(C) tft_Write_16(C) + + // Write 16 bits + #define tft_Write_16S(C) TFT_WRITE_BITS(C, 16) + + // Write 32 bits + #define tft_Write_32(C) TFT_WRITE_BITS(C, 32) + + // Write two address coordinates + #define tft_Write_32C(C,D) TFT_WRITE_BITS((C)<<24 | (C), 32); \ + TFT_WRITE_BITS((D)<<24 | (D), 32) + + // Write same value twice + #define tft_Write_32D(C) tft_Write_32C(C,C) + +//////////////////////////////////////////////////////////////////////////////////////// +// Macros for all other SPI displays +//////////////////////////////////////////////////////////////////////////////////////// +#else + #if !defined(CONFIG_IDF_TARGET_ESP32S3) + #define TFT_WRITE_BITS(D, B) *_spi_mosi_dlen = B-1; \ + *_spi_w = D; \ + *_spi_cmd = SPI_USR; \ + while (*_spi_cmd & SPI_USR); + #else + #define TFT_WRITE_BITS(D, B) *_spi_mosi_dlen = B-1; \ + *_spi_w = D; \ + *_spi_cmd = SPI_UPDATE; \ + while (*_spi_cmd & SPI_UPDATE); \ + *_spi_cmd = SPI_USR; \ + while (*_spi_cmd & SPI_USR); + #endif + // Write 8 bits + #define tft_Write_8(C) TFT_WRITE_BITS(C, 8) + + // Write 16 bits with corrected endianness for 16-bit colours + #define tft_Write_16(C) TFT_WRITE_BITS((C)<<8 | (C)>>8, 16) + + // Future option for transfer without wait + #if !defined(CONFIG_IDF_TARGET_ESP32S3) + #define tft_Write_16N(C) *_spi_mosi_dlen = 16-1; \ + *_spi_w = ((C)<<8 | (C)>>8); \ + *_spi_cmd = SPI_USR; + #else + #define tft_Write_16N(C) *_spi_mosi_dlen = 16-1; \ + *_spi_w = ((C)<<8 | (C)>>8); \ + *_spi_cmd = SPI_UPDATE; \ + while (*_spi_cmd & SPI_UPDATE); \ + *_spi_cmd = SPI_USR; + #endif + + // Write 16 bits + #define tft_Write_16S(C) TFT_WRITE_BITS(C, 16) + + // Write 32 bits + #define tft_Write_32(C) TFT_WRITE_BITS(C, 32) + + // Write two address coordinates + #define tft_Write_32C(C,D) TFT_WRITE_BITS((uint16_t)((D)<<8 | (D)>>8)<<16 | (uint16_t)((C)<<8 | (C)>>8), 32) + + // Write same value twice + #define tft_Write_32D(C) TFT_WRITE_BITS((uint16_t)((C)<<8 | (C)>>8)<<16 | (uint16_t)((C)<<8 | (C)>>8), 32) + +#endif + +#ifndef tft_Write_16N + #define tft_Write_16N tft_Write_16 +#endif + +//////////////////////////////////////////////////////////////////////////////////////// +// Macros to read from display using SPI or software SPI +//////////////////////////////////////////////////////////////////////////////////////// +#if !defined (TFT_PARALLEL_8_BIT) + // Read from display using SPI or software SPI + // Use a SPI read transfer + #define tft_Read_8() spi.transfer(0) +#endif + +// Concatenate a byte sequence A,B,C,D to CDAB, P is a uint8_t pointer +#define DAT8TO32(P) ( (uint32_t)P[0]<<8 | P[1] | P[2]<<24 | P[3]<<16 ) + +#endif // Header end diff --git a/lib/Seeed_GFX/Processors/TFT_eSPI_Generic.c b/lib/Seeed_GFX/Processors/TFT_eSPI_Generic.c new file mode 100644 index 0000000..1c2287b --- /dev/null +++ b/lib/Seeed_GFX/Processors/TFT_eSPI_Generic.c @@ -0,0 +1,263 @@ + //////////////////////////////////////////////////// + // TFT_eSPI generic driver functions // + //////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////////////// +// Global variables +//////////////////////////////////////////////////////////////////////////////////////// + +// Select the SPI port to use +#ifdef TFT_SPI_PORT + SPIClass& spi = TFT_SPI_PORT; +#else + SPIClass& spi = SPI; +#endif + +//////////////////////////////////////////////////////////////////////////////////////// +#if defined (TFT_SDA_READ) && !defined (TFT_PARALLEL_8_BIT) +//////////////////////////////////////////////////////////////////////////////////////// + +/*************************************************************************************** +** Function name: tft_Read_8 +** Description: Bit bashed SPI to read bidirectional SDA line +***************************************************************************************/ +uint8_t TFT_eSPI::tft_Read_8(void) +{ + uint8_t ret = 0; + + for (uint8_t i = 0; i < 8; i++) { // read results + ret <<= 1; + SCLK_L; + if (digitalRead(TFT_MOSI)) ret |= 1; + SCLK_H; + } + + return ret; +} + +/*************************************************************************************** +** Function name: beginSDA +** Description: Detach SPI from pin to permit software SPI +***************************************************************************************/ +void TFT_eSPI::begin_SDA_Read(void) +{ + // Release configured SPI port for SDA read + spi.end(); +} + +/*************************************************************************************** +** Function name: endSDA +** Description: Attach SPI pins after software SPI +***************************************************************************************/ +void TFT_eSPI::end_SDA_Read(void) +{ + // Configure SPI port ready for next TFT access + spi.begin(); +} + +//////////////////////////////////////////////////////////////////////////////////////// +#endif // #if defined (TFT_SDA_READ) +//////////////////////////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////////////////////////// +#if defined (TFT_PARALLEL_8_BIT) // Code for generic (i.e. any) processor +//////////////////////////////////////////////////////////////////////////////////////// + +/*************************************************************************************** +** Function name: pushBlock - for generic processor and parallel display +** Description: Write a block of pixels of the same colour +***************************************************************************************/ +void TFT_eSPI::pushBlock(uint16_t color, uint32_t len){ + + while (len>1) {tft_Write_32D(color); len-=2;} + if (len) {tft_Write_16(color);} +} + +/*************************************************************************************** +** Function name: pushPixels - for gereric processor and parallel display +** Description: Write a sequence of pixels +***************************************************************************************/ +void TFT_eSPI::pushPixels(const void* data_in, uint32_t len){ + + uint16_t *data = (uint16_t*)data_in; + if(_swapBytes) { + while (len>1) {tft_Write_16(*data); data++; tft_Write_16(*data); data++; len -=2;} + if (len) {tft_Write_16(*data);} + return; + } + + while (len>1) {tft_Write_16S(*data); data++; tft_Write_16S(*data); data++; len -=2;} + if (len) {tft_Write_16S(*data);} +} + +/*************************************************************************************** +** Function name: GPIO direction control - supports class functions +** Description: Set parallel bus to INPUT or OUTPUT +***************************************************************************************/ +void TFT_eSPI::busDir(uint32_t mask, uint8_t mode) +{ + // mask is unused for generic processor + // Arduino native functions suited well to a generic driver + pinMode(TFT_D0, mode); + pinMode(TFT_D1, mode); + pinMode(TFT_D2, mode); + pinMode(TFT_D3, mode); + pinMode(TFT_D4, mode); + pinMode(TFT_D5, mode); + pinMode(TFT_D6, mode); + pinMode(TFT_D7, mode); + return; +} + +/*************************************************************************************** +** Function name: GPIO direction control - supports class functions +** Description: Faster GPIO pin input/output switch +***************************************************************************************/ +void TFT_eSPI::gpioMode(uint8_t gpio, uint8_t mode) +{ + // No fast port based generic approach available +} + +/*************************************************************************************** +** Function name: read byte - supports class functions +** Description: Read a byte - parallel bus only +***************************************************************************************/ +uint8_t TFT_eSPI::readByte(void) +{ + uint8_t b = 0; + + busDir(0, INPUT); + digitalWrite(TFT_RD, LOW); + + b |= digitalRead(TFT_D0) << 0; + b |= digitalRead(TFT_D1) << 1; + b |= digitalRead(TFT_D2) << 2; + b |= digitalRead(TFT_D3) << 3; + b |= digitalRead(TFT_D4) << 4; + b |= digitalRead(TFT_D5) << 5; + b |= digitalRead(TFT_D6) << 6; + b |= digitalRead(TFT_D7) << 7; + + digitalWrite(TFT_RD, HIGH); + busDir(0, OUTPUT); + + return b; +} + +//////////////////////////////////////////////////////////////////////////////////////// +#elif defined (RPI_WRITE_STROBE) // For RPi TFT with write strobe +//////////////////////////////////////////////////////////////////////////////////////// + +/*************************************************************************************** +** Function name: pushBlock - for ESP32 or STM32 RPi TFT +** Description: Write a block of pixels of the same colour +***************************************************************************************/ +void TFT_eSPI::pushBlock(uint16_t color, uint32_t len){ + + if(len) { tft_Write_16(color); len--; } + while(len--) {WR_L; WR_H;} +} + +/*************************************************************************************** +** Function name: pushPixels - for ESP32 or STM32 RPi TFT +** Description: Write a sequence of pixels +***************************************************************************************/ +void TFT_eSPI::pushPixels(const void* data_in, uint32_t len) +{ + uint16_t *data = (uint16_t*)data_in; + + if (_swapBytes) while ( len-- ) {tft_Write_16S(*data); data++;} + else while ( len-- ) {tft_Write_16(*data); data++;} +} + +//////////////////////////////////////////////////////////////////////////////////////// +#elif defined (SPI_18BIT_DRIVER) // SPI 18-bit colour +//////////////////////////////////////////////////////////////////////////////////////// + +/*************************************************************************************** +** Function name: pushBlock - for STM32 and 3 byte RGB display +** Description: Write a block of pixels of the same colour +***************************************************************************************/ +void TFT_eSPI::pushBlock(uint16_t color, uint32_t len) +{ + // Split out the colours + uint8_t r = (color & 0xF800)>>8; + uint8_t g = (color & 0x07E0)>>3; + uint8_t b = (color & 0x001F)<<3; + + while ( len-- ) {tft_Write_8(r); tft_Write_8(g); tft_Write_8(b);} +} + +/*************************************************************************************** +** Function name: pushPixels - for STM32 and 3 byte RGB display +** Description: Write a sequence of pixels +***************************************************************************************/ +void TFT_eSPI::pushPixels(const void* data_in, uint32_t len){ + + uint16_t *data = (uint16_t*)data_in; + if (_swapBytes) { + while ( len-- ) { + uint16_t color = *data >> 8 | *data << 8; + tft_Write_8((color & 0xF800)>>8); + tft_Write_8((color & 0x07E0)>>3); + tft_Write_8((color & 0x001F)<<3); + data++; + } + } + else { + while ( len-- ) { + tft_Write_8((*data & 0xF800)>>8); + tft_Write_8((*data & 0x07E0)>>3); + tft_Write_8((*data & 0x001F)<<3); + data++; + } + } +} + +//////////////////////////////////////////////////////////////////////////////////////// +#else // Standard SPI 16-bit colour TFT +//////////////////////////////////////////////////////////////////////////////////////// + +/*************************************************************************************** +** Function name: pushBlock - for STM32 +** Description: Write a block of pixels of the same colour +***************************************************************************************/ +void TFT_eSPI::pushBlock(uint16_t color, uint32_t len){ + + while ( len-- ) {tft_Write_16(color);} +} + +/*************************************************************************************** +** Function name: pushPixels - for STM32 +** Description: Write a sequence of pixels +***************************************************************************************/ +void TFT_eSPI::pushPixels(const void* data_in, uint32_t len){ + + uint16_t *data = (uint16_t*)data_in; + + if (_swapBytes) while ( len-- ) {tft_Write_16(*data); data++;} + else while ( len-- ) {tft_Write_16S(*data); data++;} +} + +//////////////////////////////////////////////////////////////////////////////////////// +#endif // End of display interface specific functions +//////////////////////////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////////////////////////// +// DMA FUNCTIONS +//////////////////////////////////////////////////////////////////////////////////////// + +// Placeholder for DMA functions + +/* +Minimal function set to support DMA: + +bool TFT_eSPI::initDMA(void) +void TFT_eSPI::deInitDMA(void) +bool TFT_eSPI::dmaBusy(void) +void TFT_eSPI::pushPixelsDMA(uint16_t* image, uint32_t len) +void TFT_eSPI::pushImageDMA(int32_t x, int32_t y, int32_t w, int32_t h, uint16_t* image) + +*/ diff --git a/lib/Seeed_GFX/Processors/TFT_eSPI_Generic.h b/lib/Seeed_GFX/Processors/TFT_eSPI_Generic.h new file mode 100644 index 0000000..baaef9c --- /dev/null +++ b/lib/Seeed_GFX/Processors/TFT_eSPI_Generic.h @@ -0,0 +1,188 @@ + //////////////////////////////////////////////////// + // TFT_eSPI generic driver functions // + //////////////////////////////////////////////////// + +// This is a generic driver for Arduino boards, it supports SPI interface displays +// 8-bit parallel interface to TFT is not supported for generic processors + +#ifndef _TFT_eSPI_GENERICH_ +#define _TFT_eSPI_GENERICH_ + +// Processor ID reported by getSetup() +#define PROCESSOR_ID 0x0000 + +// Include processor specific header +// None + +// Processor specific code used by SPI bus transaction startWrite and endWrite functions +#define SET_BUS_WRITE_MODE // Not used +#define SET_BUS_READ_MODE // Not used + +// Code to check if DMA is busy, used by SPI bus transaction startWrite and endWrite functions +#define DMA_BUSY_CHECK // Not used so leave blank + +// To be safe, SUPPORT_TRANSACTIONS is assumed mandatory +#if !defined (SUPPORT_TRANSACTIONS) + #define SUPPORT_TRANSACTIONS +#endif + +// Initialise processor specific SPI functions, used by init() +#define INIT_TFT_DATA_BUS + +// If smooth fonts are enabled the filing system may need to be loaded +#ifdef SMOOTH_FONT + // Call up the filing system for the anti-aliased fonts + #define FS_NO_GLOBALS + #include +#endif + +//////////////////////////////////////////////////////////////////////////////////////// +// Define the DC (TFT Data/Command or Register Select (RS))pin drive code +//////////////////////////////////////////////////////////////////////////////////////// +#ifndef TFT_DC + #define DC_C // No macro allocated so it generates no code + #define DC_D // No macro allocated so it generates no code +#else + #define DC_C digitalWrite(TFT_DC, LOW) + #define DC_D digitalWrite(TFT_DC, HIGH) +#endif + +//////////////////////////////////////////////////////////////////////////////////////// +// Define the CS (TFT chip select) pin drive code +//////////////////////////////////////////////////////////////////////////////////////// +#ifndef TFT_CS + #define CS_L // No macro allocated so it generates no code + #define CS_H // No macro allocated so it generates no code +#else + #define CS_L digitalWrite(TFT_CS, LOW) + #define CS_H digitalWrite(TFT_CS, HIGH) +#endif + +//////////////////////////////////////////////////////////////////////////////////////// +// Make sure TFT_RD is defined if not used to avoid an error message +//////////////////////////////////////////////////////////////////////////////////////// +#ifndef TFT_RD + #define TFT_RD -1 +#endif + +//////////////////////////////////////////////////////////////////////////////////////// +// Define the WR (TFT Write) pin drive code +//////////////////////////////////////////////////////////////////////////////////////// +#ifdef TFT_WR + #define WR_L digitalWrite(TFT_WR, LOW) + #define WR_H digitalWrite(TFT_WR, HIGH) +#endif + +//////////////////////////////////////////////////////////////////////////////////////// +// Define the touch screen chip select pin drive code +//////////////////////////////////////////////////////////////////////////////////////// +#if !defined TOUCH_CS || (TOUCH_CS < 0) + #define T_CS_L // No macro allocated so it generates no code + #define T_CS_H // No macro allocated so it generates no code +#else + #define T_CS_L digitalWrite(TOUCH_CS, LOW) + #define T_CS_H digitalWrite(TOUCH_CS, HIGH) +#endif + +//////////////////////////////////////////////////////////////////////////////////////// +// Make sure TFT_MISO is defined if not used to avoid an error message +//////////////////////////////////////////////////////////////////////////////////////// +#ifndef TFT_MISO + #define TFT_MISO -1 +#endif + +//////////////////////////////////////////////////////////////////////////////////////// +// Macros to write commands/pixel colour data to a SPI ILI948x TFT +//////////////////////////////////////////////////////////////////////////////////////// +#if defined (SPI_18BIT_DRIVER) // SPI 18-bit colour + + // Write 8 bits to TFT + #define tft_Write_8(C) spi.transfer(C) + + // Convert 16-bit colour to 18-bit and write in 3 bytes + #define tft_Write_16(C) spi.transfer(((C) & 0xF800)>>8); \ + spi.transfer(((C) & 0x07E0)>>3); \ + spi.transfer(((C) & 0x001F)<<3) + + // Convert swapped byte 16-bit colour to 18-bit and write in 3 bytes + #define tft_Write_16S(C) spi.transfer((C) & 0xF8); \ + spi.transfer(((C) & 0xE000)>>11 | ((C) & 0x07)<<5); \ + spi.transfer(((C) & 0x1F00)>>5) + // Write 32 bits to TFT + #define tft_Write_32(C) spi.transfer16((C)>>16); spi.transfer16((uint16_t)(C)) + + // Write two address coordinates + #define tft_Write_32C(C,D) spi.transfer16(C); spi.transfer16(D) + + // Write same value twice + #define tft_Write_32D(C) spi.transfer16(C); spi.transfer16(C) + +//////////////////////////////////////////////////////////////////////////////////////// +// Macros to write commands/pixel colour data to other displays +//////////////////////////////////////////////////////////////////////////////////////// +#else + #if defined (RPI_DISPLAY_TYPE) // RPi TFT type always needs 16-bit transfers + #define tft_Write_8(C) spi.transfer(C); spi.transfer(C) + #define tft_Write_16(C) spi.transfer((uint8_t)((C)>>8));spi.transfer((uint8_t)((C)>>0)) + #define tft_Write_16S(C) spi.transfer((uint8_t)((C)>>0));spi.transfer((uint8_t)((C)>>8)) + + #define tft_Write_32(C) \ + tft_Write_16((uint16_t) ((C)>>16)); \ + tft_Write_16((uint16_t) ((C)>>0)) + + #define tft_Write_32C(C,D) \ + spi.transfer(0); spi.transfer((C)>>8); \ + spi.transfer(0); spi.transfer((C)>>0); \ + spi.transfer(0); spi.transfer((D)>>8); \ + spi.transfer(0); spi.transfer((D)>>0) + + #define tft_Write_32D(C) \ + spi.transfer(0); spi.transfer((C)>>8); \ + spi.transfer(0); spi.transfer((C)>>0); \ + spi.transfer(0); spi.transfer((C)>>8); \ + spi.transfer(0); spi.transfer((C)>>0) + + #else + #ifdef __AVR__ // AVR processors do not have 16-bit transfer + #define tft_Write_8(C) {SPDR=(C); while (!(SPSR&_BV(SPIF)));} + #define tft_Write_16(C) tft_Write_8((uint8_t)((C)>>8));tft_Write_8((uint8_t)((C)>>0)) + #define tft_Write_16S(C) tft_Write_8((uint8_t)((C)>>0));tft_Write_8((uint8_t)((C)>>8)) + #else + #define tft_Write_8(C) spi.transfer(C) + #define tft_Write_16(C) spi.transfer16(C) + #define tft_Write_16S(C) spi.transfer16(((C)>>8) | ((C)<<8)) + #endif // AVR + + #define tft_Write_32(C) \ + tft_Write_16((uint16_t) ((C)>>16)); \ + tft_Write_16((uint16_t) ((C)>>0)) + + #define tft_Write_32C(C,D) \ + tft_Write_16((uint16_t) (C)); \ + tft_Write_16((uint16_t) (D)) + + #define tft_Write_32D(C) \ + tft_Write_16((uint16_t) (C)); \ + tft_Write_16((uint16_t) (C)) + #endif // RPI_DISPLAY_TYPE +#endif + +#ifndef tft_Write_16N + #define tft_Write_16N tft_Write_16 +#endif + +//////////////////////////////////////////////////////////////////////////////////////// +// Macros to read from display using SPI or software SPI +//////////////////////////////////////////////////////////////////////////////////////// +#if defined (TFT_SDA_READ) + // Use a bit banged function call for STM32 and bi-directional SDA pin + #define TFT_eSPI_ENABLE_8_BIT_READ // Enable tft_Read_8(void); + #define SCLK_L digitalWrite(TFT_SCLK, LOW) + #define SCLK_H digitalWrite(TFT_SCLK, LOW) +#else + // Use a SPI read transfer + #define tft_Read_8() spi.transfer(0) +#endif + + +#endif // Header end diff --git a/lib/Seeed_GFX/Processors/pio_16bit_parallel.pio.h b/lib/Seeed_GFX/Processors/pio_16bit_parallel.pio.h new file mode 100644 index 0000000..655c1b1 --- /dev/null +++ b/lib/Seeed_GFX/Processors/pio_16bit_parallel.pio.h @@ -0,0 +1,62 @@ +// -------------------------------------------------- // +// This file is autogenerated by pioasm; do not edit! // +// 16-bit parallel // +// -------------------------------------------------- // + +#pragma once + +#if !PICO_NO_HARDWARE +#include "hardware/pio.h" +#endif + +// ------ // +// tft_io // +// ------ // + +#define tft_io_wrap_target 7 +#define tft_io_wrap 20 +#define tft_io_offset_block_fill 0u +#define tft_io_offset_start_8 7u +#define tft_io_offset_start_tx 7u +#define tft_io_offset_set_addr_window 10u + +static const uint16_t tft_io_program_instructions[] = { + 0x98a0, // 0: pull block side 1 + 0xa027, // 1: mov x, osr + 0x80a0, // 2: pull block + 0xa047, // 3: mov y, osr + 0xb8e1, // 4: mov osr, x side 1 + 0x7100, // 5: out pins, 32 side 0 [1] + 0x1884, // 6: jmp y--, 4 side 1 + // .wrap_target + 0x98a0, // 7: pull block side 1 + 0x7100, // 8: out pins, 32 side 0 [1] + 0x1807, // 9: jmp 7 side 1 + 0xf822, // 10: set x, 2 side 1 + 0xe000, // 11: set pins, 0 + 0x80a0, // 12: pull block + 0x7000, // 13: out pins, 32 side 0 + 0x0033, // 14: jmp !x, 19 + 0x98a0, // 15: pull block side 1 + 0xe001, // 16: set pins, 1 + 0x7108, // 17: out pins, 8 side 0 [1] + 0x19f1, // 18: jmp !osre, 17 side 1 [1] + 0x184b, // 19: jmp x--, 11 side 1 + 0xe001, // 20: set pins, 1 + // .wrap +}; + +#if !PICO_NO_HARDWARE +static const struct pio_program tft_io_program = { + .instructions = tft_io_program_instructions, + .length = 21, + .origin = -1, +}; + +static inline pio_sm_config tft_io_program_get_default_config(uint offset) { + pio_sm_config c = pio_get_default_sm_config(); + sm_config_set_wrap(&c, offset + tft_io_wrap_target, offset + tft_io_wrap); + sm_config_set_sideset(&c, 2, true, false); + return c; +} +#endif diff --git a/lib/Seeed_GFX/Processors/pio_8bit_parallel.pio.h b/lib/Seeed_GFX/Processors/pio_8bit_parallel.pio.h new file mode 100644 index 0000000..b5d6a8c --- /dev/null +++ b/lib/Seeed_GFX/Processors/pio_8bit_parallel.pio.h @@ -0,0 +1,70 @@ +// -------------------------------------------------- // +// This file is autogenerated by pioasm; do not edit! // +// 8-bit parallel // +// -------------------------------------------------- // + +#pragma once + +#if !PICO_NO_HARDWARE +#include "hardware/pio.h" +#endif + +// ------ // +// tft_io // +// ------ // + +#define tft_io_wrap_target 9 +#define tft_io_wrap 27 + +#define tft_io_offset_block_fill 0u +#define tft_io_offset_start_tx 9u +#define tft_io_offset_start_8 14u +#define tft_io_offset_set_addr_window 17u + +static const uint16_t tft_io_program_instructions[] = { + 0x98a0, // 0: pull block side 1 + 0xa027, // 1: mov x, osr + 0x80a0, // 2: pull block + 0xa047, // 3: mov y, osr + 0xb8e1, // 4: mov osr, x side 1 + 0x7118, // 5: out pins, 24 side 0 [1] + 0xb942, // 6: nop side 1 [1] + 0x7108, // 7: out pins, 8 side 0 [1] + 0x1884, // 8: jmp y--, 4 side 1 + // .wrap_target + 0x98a0, // 9: pull block side 1 + 0x7118, // 10: out pins, 24 side 0 [1] + 0xb942, // 11: nop side 1 [1] + 0x7108, // 12: out pins, 8 side 0 [1] + 0x1809, // 13: jmp 9 side 1 + 0x98a0, // 14: pull block side 1 + 0x7100, // 15: out pins, 32 side 0 [1] + 0x1809, // 16: jmp 9 side 1 + 0xf822, // 17: set x, 2 side 1 + 0xe000, // 18: set pins, 0 + 0x80a0, // 19: pull block + 0x7000, // 20: out pins, 32 side 0 + 0x003a, // 21: jmp !x, 26 + 0x98a0, // 22: pull block side 1 + 0xe001, // 23: set pins, 1 + 0x7108, // 24: out pins, 8 side 0 [1] + 0x19f8, // 25: jmp !osre, 24 side 1 [1] + 0x1852, // 26: jmp x--, 18 side 1 + 0xe001, // 27: set pins, 1 + // .wrap +}; + +#if !PICO_NO_HARDWARE +static const struct pio_program tft_io_program = { + .instructions = tft_io_program_instructions, + .length = 28, + .origin = -1, +}; + +static inline pio_sm_config tft_io_program_get_default_config(uint offset) { + pio_sm_config c = pio_get_default_sm_config(); + sm_config_set_wrap(&c, offset + tft_io_wrap_target, offset + tft_io_wrap); + sm_config_set_sideset(&c, 2, true, false); + return c; +} +#endif diff --git a/lib/Seeed_GFX/Processors/pio_8bit_parallel_18bpp.pio.h b/lib/Seeed_GFX/Processors/pio_8bit_parallel_18bpp.pio.h new file mode 100644 index 0000000..b2aaff4 --- /dev/null +++ b/lib/Seeed_GFX/Processors/pio_8bit_parallel_18bpp.pio.h @@ -0,0 +1,73 @@ +// -------------------------------------------------- // +// This file is autogenerated by pioasm; do not edit! // +// -------------------------------------------------- // + +#pragma once + +#if !PICO_NO_HARDWARE +#include "hardware/pio.h" +#endif + +// ------ // +// tft_io // +// ------ // + +#define tft_io_wrap_target 11 +#define tft_io_wrap 31 + +#define tft_io_offset_block_fill 0u +#define tft_io_offset_start_tx 11u +#define tft_io_offset_start_8 18u +#define tft_io_offset_set_addr_window 21u + +static const uint16_t tft_io_program_instructions[] = { + 0x98a0, // 0: pull block side 1 + 0xa027, // 1: mov x, osr + 0x80a0, // 2: pull block + 0xa047, // 3: mov y, osr + 0xb8e1, // 4: mov osr, x side 1 + 0x7110, // 5: out pins, 16 side 0 [1] + 0xb942, // 6: nop side 1 [1] + 0x7108, // 7: out pins, 8 side 0 [1] + 0xb942, // 8: nop side 1 [1] + 0x7108, // 9: out pins, 8 side 0 [1] + 0x1884, // 10: jmp y--, 4 side 1 + // .wrap_target + 0x98a0, // 11: pull block side 1 + 0x7110, // 12: out pins, 16 side 0 [1] + 0xb942, // 13: nop side 1 [1] + 0x7108, // 14: out pins, 8 side 0 [1] + 0xb942, // 15: nop side 1 [1] + 0x7108, // 16: out pins, 8 side 0 [1] + 0x180b, // 17: jmp 11 side 1 + 0x98a0, // 18: pull block side 1 + 0x7100, // 19: out pins, 32 side 0 [1] + 0x180b, // 20: jmp 11 side 1 + 0xf822, // 21: set x, 2 side 1 + 0xe000, // 22: set pins, 0 + 0x80a0, // 23: pull block + 0x7000, // 24: out pins, 32 side 0 + 0x003e, // 25: jmp !x, 30 + 0x98a0, // 26: pull block side 1 + 0xe001, // 27: set pins, 1 + 0x7108, // 28: out pins, 8 side 0 [1] + 0x19fc, // 29: jmp !osre, 28 side 1 [1] + 0x1856, // 30: jmp x--, 22 side 1 + 0xe001, // 31: set pins, 1 + // .wrap +}; + +#if !PICO_NO_HARDWARE +static const struct pio_program tft_io_program = { + .instructions = tft_io_program_instructions, + .length = 32, + .origin = -1, +}; + +static inline pio_sm_config tft_io_program_get_default_config(uint offset) { + pio_sm_config c = pio_get_default_sm_config(); + sm_config_set_wrap(&c, offset + tft_io_wrap_target, offset + tft_io_wrap); + sm_config_set_sideset(&c, 2, true, false); + return c; +} +#endif diff --git a/lib/Seeed_GFX/Processors/pio_SPI.pio.h b/lib/Seeed_GFX/Processors/pio_SPI.pio.h new file mode 100644 index 0000000..0848529 --- /dev/null +++ b/lib/Seeed_GFX/Processors/pio_SPI.pio.h @@ -0,0 +1,74 @@ +// -------------------------------------------------- // +// This file is autogenerated by pioasm; do not edit! // +// 8 + 16-bit SPI - no auto colour conversion // +// -------------------------------------------------- // + +#pragma once + +#if !PICO_NO_HARDWARE +#include "hardware/pio.h" +#endif + +// ------ // +// tft_io // +// ------ // + +#define tft_io_wrap_target 27 +#define tft_io_wrap 31 + +#define tft_io_offset_start_8 0u +#define tft_io_offset_set_addr_window 3u +#define tft_io_offset_block_fill 17u +#define tft_io_offset_start_tx 27u + +static const uint16_t tft_io_program_instructions[] = { + 0x90a0, // 0: pull block side 0 + 0x6019, // 1: out pins, 25 + 0x181e, // 2: jmp 30 side 1 + 0xf022, // 3: set x, 2 side 0 + 0xe000, // 4: set pins, 0 + 0x90a0, // 5: pull block side 0 + 0x6019, // 6: out pins, 25 + 0xb842, // 7: nop side 1 + 0x7001, // 8: out pins, 1 side 0 + 0x18e8, // 9: jmp !osre, 8 side 1 + 0xf001, // 10: set pins, 1 side 0 + 0x003b, // 11: jmp !x, 27 + 0x80a0, // 12: pull block + 0x7001, // 13: out pins, 1 side 0 + 0x18ed, // 14: jmp !osre, 13 side 1 + 0x1044, // 15: jmp x--, 4 side 0 + 0x001b, // 16: jmp 27 + 0x90a0, // 17: pull block side 0 + 0xa027, // 18: mov x, osr + 0x80a0, // 19: pull block + 0xa047, // 20: mov y, osr + 0xb0e1, // 21: mov osr, x side 0 + 0x7011, // 22: out pins, 17 side 0 + 0xb842, // 23: nop side 1 + 0x7001, // 24: out pins, 1 side 0 + 0x18f8, // 25: jmp !osre, 24 side 1 + 0x1095, // 26: jmp y--, 21 side 0 + // .wrap_target + 0x90a0, // 27: pull block side 0 + 0x7011, // 28: out pins, 17 side 0 + 0xb842, // 29: nop side 1 + 0x7001, // 30: out pins, 1 side 0 + 0x18fe, // 31: jmp !osre, 30 side 1 + // .wrap +}; + +#if !PICO_NO_HARDWARE +static const struct pio_program tft_io_program = { + .instructions = tft_io_program_instructions, + .length = 32, + .origin = -1, +}; + +static inline pio_sm_config tft_io_program_get_default_config(uint offset) { + pio_sm_config c = pio_get_default_sm_config(); + sm_config_set_wrap(&c, offset + tft_io_wrap_target, offset + tft_io_wrap); + sm_config_set_sideset(&c, 2, true, false); + return c; +} +#endif diff --git a/lib/Seeed_GFX/Processors/pio_SPI_18bit.pio.h b/lib/Seeed_GFX/Processors/pio_SPI_18bit.pio.h new file mode 100644 index 0000000..09bbfeb --- /dev/null +++ b/lib/Seeed_GFX/Processors/pio_SPI_18bit.pio.h @@ -0,0 +1,74 @@ +// -------------------------------------------------- // +// This file is autogenerated by pioasm; do not edit! // +// 8 + 18-bit SPI - no auto colour conversion // +// -------------------------------------------------- // + +#pragma once + +#if !PICO_NO_HARDWARE +#include "hardware/pio.h" +#endif + +// ------ // +// tft_io // +// ------ // + +#define tft_io_wrap_target 27 +#define tft_io_wrap 31 + +#define tft_io_offset_start_8 0u +#define tft_io_offset_set_addr_window 3u +#define tft_io_offset_block_fill 17u +#define tft_io_offset_start_tx 27u + +static const uint16_t tft_io_program_instructions[] = { + 0x90a0, // 0: pull block side 0 + 0x6019, // 1: out pins, 25 + 0x181e, // 2: jmp 30 side 1 + 0xf022, // 3: set x, 2 side 0 + 0xe000, // 4: set pins, 0 + 0x90a0, // 5: pull block side 0 + 0x6019, // 6: out pins, 25 + 0xb842, // 7: nop side 1 + 0x7001, // 8: out pins, 1 side 0 + 0x18e8, // 9: jmp !osre, 8 side 1 + 0xf001, // 10: set pins, 1 side 0 + 0x003b, // 11: jmp !x, 27 + 0x80a0, // 12: pull block + 0x7001, // 13: out pins, 1 side 0 + 0x18ed, // 14: jmp !osre, 13 side 1 + 0x1044, // 15: jmp x--, 4 side 0 + 0x001b, // 16: jmp 27 + 0x90a0, // 17: pull block side 0 + 0xa027, // 18: mov x, osr + 0x80a0, // 19: pull block + 0xa047, // 20: mov y, osr + 0xb0e1, // 21: mov osr, x side 0 + 0x7009, // 22: out pins, 9 side 0 + 0xb842, // 23: nop side 1 + 0x7001, // 24: out pins, 1 side 0 + 0x18f8, // 25: jmp !osre, 24 side 1 + 0x1095, // 26: jmp y--, 21 side 0 + // .wrap_target + 0x90a0, // 27: pull block side 0 + 0x7009, // 28: out pins, 9 side 0 + 0xb842, // 29: nop side 1 + 0x7001, // 30: out pins, 1 side 0 + 0x18fe, // 31: jmp !osre, 30 side 1 + // .wrap +}; + +#if !PICO_NO_HARDWARE +static const struct pio_program tft_io_program = { + .instructions = tft_io_program_instructions, + .length = 32, + .origin = -1, +}; + +static inline pio_sm_config tft_io_program_get_default_config(uint offset) { + pio_sm_config c = pio_get_default_sm_config(); + sm_config_set_wrap(&c, offset + tft_io_wrap_target, offset + tft_io_wrap); + sm_config_set_sideset(&c, 2, true, false); + return c; +} +#endif diff --git a/lib/Seeed_GFX/TFT_Drivers/ED103TC2_Defines.h b/lib/Seeed_GFX/TFT_Drivers/ED103TC2_Defines.h new file mode 100644 index 0000000..0876ab4 --- /dev/null +++ b/lib/Seeed_GFX/TFT_Drivers/ED103TC2_Defines.h @@ -0,0 +1,182 @@ + +#ifndef EPD_WIDTH +#define EPD_WIDTH 1872 +#endif + +#ifndef EPD_HEIGHT +#define EPD_HEIGHT 1404 +#endif + +#ifndef TFT_WIDTH +#define TFT_WIDTH EPD_WIDTH +#endif + +#ifndef TFT_HEIGHT +#define TFT_HEIGHT EPD_HEIGHT +#endif + +#define EPD_COLOR_DEPTH 1 + +#define USE_PARTIAL_EPAPER +#define USE_MUTIGRAY_EPAPER +#define GRAY_LEVEL16 16 + +#define EPD_PSR 0x00 +#define EPD_PWRR 0x01 +#define EPD_POF 0x02 +#define EPD_POFS 0x03 +#define EPD_PON 0x04 +#define EPD_BTST1 0x05 +#define EPD_BTST2 0x06 +#define EPD_DSLP 0x07 +#define EPD_BTST3 0x08 +#define EPD_DTM 0x10 +#define EPD_DRF 0x12 +#define EPD_PLL 0x30 +#define EPD_CDI 0x50 +#define EPD_TCON 0x60 +#define EPD_TRES 0x61 +#define EPD_REV 0x70 +#define EPD_VDCS 0x82 +#define EPD_T_VDCS 0x84 +#define EPD_PWS 0xE3 + +#define TFT_SWRST 0xFF +#define TFT_CASET 0xFF +#define TFT_PASET 0xFF +#define TFT_RAMWR 0xFF +#define TFT_RAMRD 0xFF +#define TFT_INVON EPD_PON +#define TFT_INVOFF EPD_POF + +#define TFT_INIT_DELAY 0 + +#ifdef TFT_BUSY +#define CHECK_BUSY() \ + do \ + { \ + tconWaitForReady(); \ + } while (true) +#else +#define CHECK_BUSY() +#endif + +#define EPD_UPDATE_PARTIAL() \ + do \ + { \ + tconDisplayArea1bpp(_imgAreaInfo.usX,_imgAreaInfo.usY, _imgAreaInfo.usWidth, _imgAreaInfo.usHeight, 0x01, 0x00, 0xff); \ + } while (0); + +#define EPD_UPDATE() \ + do \ + { \ + \ + tconDisplayArea1bpp(_imgAreaInfo.usX,_imgAreaInfo.usY, _imgAreaInfo.usWidth, _imgAreaInfo.usHeight, 0x02, 0x00, 0xff); \ + } while (0) + +#define EPD_UPDATE_GRAY() \ + do \ + { \ + \ + tconDisplayArea(_imgAreaInfo.usX,_imgAreaInfo.usY, _imgAreaInfo.usWidth, _imgAreaInfo.usHeight, 0x02); \ + } while (0) + +#define EPD_SLEEP() \ + do \ + { \ + tconSleep(); \ + } while (0) + +#if defined(OPENDISPLAY_SEEED_GFX_RUNTIME_PINS) +#include "OpenDisplay/opendisplay_runtime_pins.h" +#define OD_EPD_RST_TOGGLE() \ + do { \ + if (opnd_seeed_runtime_rst >= 0) { \ + digitalWrite(opnd_seeed_runtime_rst, LOW); \ + delay(10); \ + digitalWrite(opnd_seeed_runtime_rst, HIGH); \ + delay(10); \ + } \ + } while (0) +#else +#define OD_EPD_RST_TOGGLE() \ + do { \ + digitalWrite(TFT_RST, LOW); \ + delay(10); \ + digitalWrite(TFT_RST, HIGH); \ + delay(10); \ + } while (0) +#endif + +#define EPD_INIT() \ + do \ + { \ + OD_EPD_RST_TOGGLE(); \ + CHECK_BUSY(); \ + } while (0) + +#define EPD_WAKEUP() \ + do \ + { \ + tconWake(); \ + if(_entemp) \ + EPD_SET_TEMP(_temp); \ + } while (0) + +#define EPD_WAKEUP_GRAY EPD_WAKEUP +#define EPD_WAKEUP_PARTIAL() \ + do \ + { \ + tconWake(); \ + } while (0) + + +#define EPD_SET_WINDOW(x1, y1, x2, y2) \ + do \ + { \ + setTconWindowsData(x1,y1, x2, y2); \ + } while (0) + +#define EPD_COLOR_INVERT() \ + do \ + { \ + } while (0) + +#define EPD_PUSH_NEW_COLORS(w, h, colors) \ + do \ + { \ + tconLoad1bppImage(colors, _imgAreaInfo.usX, _imgAreaInfo.usY , w ,h, false); \ + } while (0) + +#define EPD_PUSH_NEW_COLORS_FLIP(w, h, colors) \ + do \ + { tconLoad1bppImage(colors, _imgAreaInfo.usX, _imgAreaInfo.usY , w ,h, true); \ + } while (0) + +#define EPD_PUSH_NEW_GRAY_COLORS(w, h, colors) \ + do \ + { \ + tconLoadImage(colors, _imgAreaInfo.usX, _imgAreaInfo.usY , w ,h, false); \ + } while (0) + +#define EPD_PUSH_NEW_GRAY_COLORS_FLIP(w, h, colors) \ + do \ + { \ + tconLoadImage(colors, _imgAreaInfo.usX, _imgAreaInfo.usY , w ,h, true); \ + } while (0) + +#define EPD_PUSH_OLD_COLORS_FILP(w, h, colors)\ + do \ + { \ + } while (0) + +#define EPD_PUSH_OLD_COLORS(w, h, colors) \ + do \ + { \ + } while (0) + +#define EPD_SET_TEMP(temp) \ + do \ + { \ + setTconTemp((uint16_t)(temp)); \ + } while (0) diff --git a/lib/Seeed_GFX/TFT_Drivers/ED103TC2_Init.h b/lib/Seeed_GFX/TFT_Drivers/ED103TC2_Init.h new file mode 100644 index 0000000..49d64ad --- /dev/null +++ b/lib/Seeed_GFX/TFT_Drivers/ED103TC2_Init.h @@ -0,0 +1,47 @@ +#if defined(OPENDISPLAY_SEEED_GFX_RUNTIME_PINS) +#include "OpenDisplay/opendisplay_runtime_pins.h" +#endif +#ifdef TFT_BUSY +#if defined(OPENDISPLAY_SEEED_GFX_RUNTIME_PINS) + if (opnd_seeed_runtime_busy >= 0) pinMode(opnd_seeed_runtime_busy, INPUT); +#else + pinMode(TFT_BUSY, INPUT); +#endif +#endif +#ifdef TFT_ENABLE +#if defined(OPENDISPLAY_SEEED_GFX_RUNTIME_PINS) + if (opnd_seeed_runtime_tft_enable >= 0) { + pinMode(opnd_seeed_runtime_tft_enable, OUTPUT); + digitalWrite(opnd_seeed_runtime_tft_enable, HIGH); + } +#else + pinMode(TFT_ENABLE, OUTPUT); + digitalWrite(TFT_ENABLE, HIGH); +#endif +#endif +#ifdef ITE_ENABLE +#if defined(OPENDISPLAY_SEEED_GFX_RUNTIME_PINS) + if (opnd_seeed_runtime_ite_enable >= 0) { + pinMode(opnd_seeed_runtime_ite_enable, OUTPUT); + digitalWrite(opnd_seeed_runtime_ite_enable, HIGH); + } +#else + pinMode(ITE_ENABLE, OUTPUT); + digitalWrite(ITE_ENABLE, HIGH); +#endif +#endif +#if defined(OPENDISPLAY_SEEED_GFX_RUNTIME_PINS) + if (opnd_seeed_runtime_rst >= 0) { + digitalWrite(opnd_seeed_runtime_rst, LOW); + delay(10); + digitalWrite(opnd_seeed_runtime_rst, HIGH); + delay(10); + } +#else + digitalWrite(TFT_RST, LOW); + delay(10); + digitalWrite(TFT_RST, HIGH); + delay(10); +#endif + //hostTconInitFast(); + hostTconInit(); \ No newline at end of file diff --git a/lib/Seeed_GFX/TFT_Drivers/ED103TC2_Init_Wake.h b/lib/Seeed_GFX/TFT_Drivers/ED103TC2_Init_Wake.h new file mode 100644 index 0000000..d3697de --- /dev/null +++ b/lib/Seeed_GFX/TFT_Drivers/ED103TC2_Init_Wake.h @@ -0,0 +1,33 @@ +#if defined(OPENDISPLAY_SEEED_GFX_RUNTIME_PINS) +#include "OpenDisplay/opendisplay_runtime_pins.h" +#endif +#ifdef TFT_BUSY +#if defined(OPENDISPLAY_SEEED_GFX_RUNTIME_PINS) + if (opnd_seeed_runtime_busy >= 0) pinMode(opnd_seeed_runtime_busy, INPUT); +#else + pinMode(TFT_BUSY, INPUT); +#endif +#endif +#ifdef TFT_ENABLE +#if defined(OPENDISPLAY_SEEED_GFX_RUNTIME_PINS) + if (opnd_seeed_runtime_tft_enable >= 0) { + pinMode(opnd_seeed_runtime_tft_enable, OUTPUT); + digitalWrite(opnd_seeed_runtime_tft_enable, HIGH); + } +#else + pinMode(TFT_ENABLE, OUTPUT); + digitalWrite(TFT_ENABLE, HIGH); +#endif +#endif +#ifdef ITE_ENABLE +#if defined(OPENDISPLAY_SEEED_GFX_RUNTIME_PINS) + if (opnd_seeed_runtime_ite_enable >= 0) { + pinMode(opnd_seeed_runtime_ite_enable, OUTPUT); + digitalWrite(opnd_seeed_runtime_ite_enable, HIGH); + } +#else + pinMode(ITE_ENABLE, OUTPUT); + digitalWrite(ITE_ENABLE, HIGH); +#endif +#endif + hostTconInitFast(); diff --git a/lib/Seeed_GFX/TFT_Drivers/ED103TC2_Rotation.h b/lib/Seeed_GFX/TFT_Drivers/ED103TC2_Rotation.h new file mode 100644 index 0000000..e69de29 diff --git a/lib/Seeed_GFX/TFT_Drivers/ED2208_Defines.h b/lib/Seeed_GFX/TFT_Drivers/ED2208_Defines.h new file mode 100644 index 0000000..6c63826 --- /dev/null +++ b/lib/Seeed_GFX/TFT_Drivers/ED2208_Defines.h @@ -0,0 +1,218 @@ + +#ifndef EPD_WIDTH +#define EPD_WIDTH 800 +#endif + +#ifndef EPD_HEIGHT +#define EPD_HEIGHT 480 +#endif + +#ifndef TFT_WIDTH +#define TFT_WIDTH EPD_WIDTH +#endif + +#ifndef TFT_HEIGHT +#define TFT_HEIGHT EPD_HEIGHT +#endif + +#define EPD_COLOR_DEPTH 4 + +#define USE_COLORFULL_EPAPER + +#define EPD_PSR 0x00 +#define EPD_PWRR 0x01 +#define EPD_POF 0x02 +#define EPD_POFS 0x03 +#define EPD_PON 0x04 +#define EPD_BTST1 0x05 +#define EPD_BTST2 0x06 +#define EPD_DSLP 0x07 +#define EPD_BTST3 0x08 +#define EPD_DTM 0x10 +#define EPD_DRF 0x12 +#define EPD_PLL 0x30 +#define EPD_CDI 0x50 +#define EPD_TCON 0x60 +#define EPD_TRES 0x61 +#define EPD_REV 0x70 +#define EPD_VDCS 0x82 +#define EPD_T_VDCS 0x84 +#define EPD_PWS 0xE3 + +#define TFT_SWRST 0xFF +#define TFT_CASET 0xFF +#define TFT_PASET 0xFF +#define TFT_RAMWR 0xFF +#define TFT_RAMRD 0xFF +#define TFT_INVON EPD_PON +#define TFT_INVOFF EPD_POF + +#define TFT_INIT_DELAY 0 + +#ifdef TFT_BUSY +#define CHECK_BUSY() \ + do \ + { \ + delay(10); \ + if (digitalRead(TFT_BUSY)) \ + break; \ + } while (true) +#else +#define CHECK_BUSY() +#endif + +#define EPD_UPDATE() \ + do \ + { \ + writecommand(0x12); \ + writedata(0x00); \ + delay(1); \ + CHECK_BUSY(); \ + } while (0) + +#define EPD_SLEEP() \ + do \ + { \ + writecommand(0x02); \ + writedata(0x00); \ + delay(1); \ + CHECK_BUSY(); \ + } while (0) + + #define EPD_INIT() \ + do \ + { \ + digitalWrite(TFT_RST, LOW); \ + delay(10); \ + digitalWrite(TFT_RST, HIGH);\ + delay(10); \ + writecommand(0xAA);\ + writedata(0x49);\ + writedata(0x55);\ + writedata(0x20);\ + writedata(0x08);\ + writedata(0x09);\ + writedata(0x18);\ + writecommand(EPD_PWRR);\ + writedata(0x3F);\ + writecommand(EPD_PSR); \ + writedata(0x5F);\ + writedata(0x69);\ + writecommand(EPD_POFS);\ + writedata(0x00);\ + writedata(0x54);\ + writedata(0x00);\ + writedata(0x44);\ + writecommand(EPD_BTST1);\ + writedata(0x40);\ + writedata(0x1F);\ + writedata(0x1F);\ + writedata(0x2C);\ + writecommand(EPD_BTST2);\ + writedata(0x6F);\ + writedata(0x1F);\ + writedata(0x17);\ + writedata(0x49);\ + writecommand(EPD_BTST3);\ + writedata(0x6F);\ + writedata(0x1F);\ + writedata(0x1F);\ + writedata(0x22);\ + writecommand(EPD_PLL);\ + writedata(0x08);\ + writecommand(EPD_CDI);\ + writedata(0x3F);\ + writecommand(EPD_TCON);\ + writedata(0x02);\ + writedata(0x00);\ + writecommand(EPD_TRES);\ + writedata(0x03);\ + writedata(0x20);\ + writedata(0x01); \ + writedata(0xE0);\ + writecommand(EPD_T_VDCS);\ + writedata(0x01);\ + writecommand(EPD_PWS);\ + writedata(0x2F);\ + writecommand(0x04); \ + CHECK_BUSY(); \ + } while (0) + +#define EPD_WAKEUP() \ + do \ + { \ + writecommand(0x04); \ + delay(1); \ + CHECK_BUSY(); \ + } while (0) + +#define EPD_SET_WINDOW(x1, y1, x2, y2) \ + do \ + { \ + } while (0) + + + +#define COLOR_GET(color) ( \ + (color) == 0x0F ? 0x00 : \ + (color) == 0x00 ? 0x01 : \ + (color) == 0x0D ? 0x05 : \ + (color) == 0x02 ? 0x06 : \ + (color) == 0x0B ? 0x02 : \ + (color) == 0x06 ? 0x03 : \ + 0x00 \ +) + + +#define EPD_PUSH_NEW_COLORS(w, h, colors) \ + do \ + { \ + uint16_t bytes_per_row = (w) / 2; \ + uint8_t temp1,temp2 ; \ + writecommand(0x10); \ + for (uint16_t row = 0; row < (h) ; row++) \ + { \ + for(uint16_t col = 0; col < bytes_per_row; col++) \ + { \ + uint8_t b = (colors[bytes_per_row *row+col]) ; \ + temp1 = (b >> 4) & 0x0F;\ + temp2 = b & 0x0F;\ + writedata(((COLOR_GET(temp1) <<4)|( COLOR_GET(temp2))));\ + } \ + } \ + } while (0) + +#define EPD_PUSH_NEW_COLORS_FLIP(w, h, colors) \ + do \ + { \ + uint16_t bytes_per_row = (w) / 2; \ + uint8_t temp1,temp2 ; \ + writecommand(0x10); \ + for (uint16_t row = 0; row < (h); row++) \ + { \ + uint16_t start = row * bytes_per_row; \ + for (uint16_t col = 0; col < bytes_per_row; col++) \ + { \ + uint8_t b = (colors[bytes_per_row *row + (bytes_per_row - 1 - col)]) ;\ + temp1 = (b >> 4) & 0x0F; \ + temp2 = b & 0x0F; \ + writedata(((COLOR_GET(temp2) <<4)|( COLOR_GET(temp1))));\ + } \ + } \ + } while (0) + +#define EPD_PUSH_OLD_COLORS_FILP(w, h, colors)\ + do \ + { \ + } while (0) + +#define EPD_PUSH_OLD_COLORS(w, h, colors) \ + do \ + { \ + } while (0) + +#define EPD_SET_TEMP(temp) \ +do \ +{ \ +} while (0) + \ No newline at end of file diff --git a/lib/Seeed_GFX/TFT_Drivers/ED2208_Init.h b/lib/Seeed_GFX/TFT_Drivers/ED2208_Init.h new file mode 100644 index 0000000..0937192 --- /dev/null +++ b/lib/Seeed_GFX/TFT_Drivers/ED2208_Init.h @@ -0,0 +1,106 @@ + +// This is the command sequence that initialises the ED2208 driver + +{ + +#ifdef TFT_BUSY + pinMode(TFT_BUSY, INPUT); +#endif +#ifdef TFT_ENABLE + pinMode(TFT_ENABLE, OUTPUT); + digitalWrite(TFT_ENABLE, HIGH); +#endif + digitalWrite(TFT_RST, LOW); + delay(20); + digitalWrite(TFT_RST, HIGH); + delay(10); + writecommand(0xAA); // CMDH + writedata(0x49); + writedata(0x55); + writedata(0x20); + writedata(0x08); + writedata(0x09); + writedata(0x18); + + writecommand(EPD_PWRR); + writedata(0x3F); + writedata(0x00); + writedata(0x32); + writedata(0x2A); + writedata(0x0E); + writedata(0x2A); + + writecommand(EPD_PSR); + writedata(0x5F); + writedata(0x69); + + writecommand(EPD_POFS); + writedata(0x00); + writedata(0x54); + writedata(0x00); + writedata(0x44); + + writecommand(EPD_BTST1); + writedata(0x40); + writedata(0x1F); + writedata(0x1F); + writedata(0x2C); + + writecommand(EPD_BTST2); + writedata(0x6F); + writedata(0x1F); + writedata(0x16); + writedata(0x25); + + writecommand(EPD_BTST3); + writedata(0x6F); + writedata(0x1F); + writedata(0x1F); + writedata(0x22); + + writecommand(0x13); // IPC + writedata(0x00); + writedata(0x04); + + writecommand(EPD_PLL); + writedata(0x02); + + writecommand(0x41); // TSE + writedata(0x00); + + writecommand(EPD_CDI); + writedata(0x3F); + + writecommand(EPD_TCON); + writedata(0x02); + writedata(0x00); + + writecommand(EPD_TRES); + writedata((TFT_WIDTH >> 8) & 0xFF); + writedata(TFT_WIDTH & 0xFF); + writedata((TFT_HEIGHT >> 8) & 0xFF); + writedata(TFT_HEIGHT & 0xFF); + + + writecommand(EPD_VDCS); + writedata(0x1E); + + writecommand(EPD_T_VDCS); + writedata(0x01); + + writecommand(0x86); // AGID + writedata(0x00); + + writecommand(EPD_PWS); + writedata(0x2F); + + writecommand(0xE0); // CCSET + writedata(0x00); + + writecommand(0xE6); // TSSET + writedata(0x00); + + writecommand(0x04); //PWR on + CHECK_BUSY(); //waiting for the electronic paper IC to release the idle signal + +} \ No newline at end of file diff --git a/lib/Seeed_GFX/TFT_Drivers/ED2208_Rotation.h b/lib/Seeed_GFX/TFT_Drivers/ED2208_Rotation.h new file mode 100644 index 0000000..2b58283 --- /dev/null +++ b/lib/Seeed_GFX/TFT_Drivers/ED2208_Rotation.h @@ -0,0 +1,4 @@ + +// This is the command sequence that rotates the ED2208 driver coordinate frame + + diff --git a/lib/Seeed_GFX/TFT_Drivers/EPD_Defines.h b/lib/Seeed_GFX/TFT_Drivers/EPD_Defines.h new file mode 100644 index 0000000..6a6838c --- /dev/null +++ b/lib/Seeed_GFX/TFT_Drivers/EPD_Defines.h @@ -0,0 +1,27 @@ +// Null set for ePaper +#define TFT_WIDTH 1000 +#define TFT_HEIGHT 1000 + +#define TFT_INIT_DELAY 0 + +#define TFT_NOP 0x00 +#define TFT_SWRST 0x00 + +#define TFT_CASET 0x00 +#define TFT_PASET 0x00 +#define TFT_RAMWR 0x00 + +#define TFT_RAMRD 0x00 +#define TFT_IDXRD 0x00 + +#define TFT_MADCTL 0x00 +#define TFT_MAD_MY 0x00 +#define TFT_MAD_MX 0x00 +#define TFT_MAD_MV 0x00 +#define TFT_MAD_ML 0x00 +#define TFT_MAD_BGR 0x00 +#define TFT_MAD_MH 0x00 +#define TFT_MAD_RGB 0x00 + +#define TFT_INVOFF 0x00 +#define TFT_INVON 0x00 diff --git a/lib/Seeed_GFX/TFT_Drivers/GC9A01_Defines.h b/lib/Seeed_GFX/TFT_Drivers/GC9A01_Defines.h new file mode 100644 index 0000000..4cb1095 --- /dev/null +++ b/lib/Seeed_GFX/TFT_Drivers/GC9A01_Defines.h @@ -0,0 +1,40 @@ +// Change the width and height if required (defined in portrait mode) +// or use the constructor to over-ride defaults +#ifndef TFT_WIDTH + #define TFT_WIDTH 240 +#endif +#ifndef TFT_HEIGHT + #define TFT_HEIGHT 240 +#endif + +// Delay between some initialisation commands +#define TFT_INIT_DELAY 0x80 + +// Generic commands used by TFT_eSPI.cpp +#define TFT_NOP 0x00 +#define TFT_SWRST 0x01 + +#define TFT_SLPIN 0x10 +#define TFT_SLPOUT 0x11 + +#define TFT_INVOFF 0x20 +#define TFT_INVON 0x21 + +#define TFT_DISPOFF 0x28 +#define TFT_DISPON 0x29 + +#define TFT_CASET 0x2A +#define TFT_PASET 0x2B +#define TFT_RAMWR 0x2C + +#define TFT_RAMRD 0x2E +#define TFT_IDXRD 0x00 //0xDD // ILI9341 only, indexed control register read + +#define TFT_MADCTL 0x36 +#define TFT_MAD_MY 0x80 +#define TFT_MAD_MX 0x40 +#define TFT_MAD_MV 0x20 +#define TFT_MAD_ML 0x10 +#define TFT_MAD_BGR 0x08 +#define TFT_MAD_MH 0x04 +#define TFT_MAD_RGB 0x00 diff --git a/lib/Seeed_GFX/TFT_Drivers/GC9A01_Init.h b/lib/Seeed_GFX/TFT_Drivers/GC9A01_Init.h new file mode 100644 index 0000000..947d37d --- /dev/null +++ b/lib/Seeed_GFX/TFT_Drivers/GC9A01_Init.h @@ -0,0 +1,232 @@ + +// This is the command sequence that initialises the GC9A01 driver + +{ + writecommand(0xEF); + writecommand(0xEB); + writedata(0x14); + + writecommand(0xFE); + writecommand(0xEF); + + writecommand(0xEB); + writedata(0x14); + + writecommand(0x84); + writedata(0x40); + + writecommand(0x85); + writedata(0xFF); + + writecommand(0x86); + writedata(0xFF); + + writecommand(0x87); + writedata(0xFF); + + writecommand(0x88); + writedata(0x0A); + + writecommand(0x89); + writedata(0x21); + + writecommand(0x8A); + writedata(0x00); + + writecommand(0x8B); + writedata(0x80); + + writecommand(0x8C); + writedata(0x01); + + writecommand(0x8D); + writedata(0x01); + + writecommand(0x8E); + writedata(0xFF); + + writecommand(0x8F); + writedata(0xFF); + + writecommand(0xB6); + writedata(0x00); + writedata(0x20); + + writecommand(0x3A); + writedata(0x05); + + writecommand(0x90); + writedata(0x08); + writedata(0x08); + writedata(0x08); + writedata(0x08); + + writecommand(0xBD); + writedata(0x06); + + writecommand(0xBC); + writedata(0x00); + + writecommand(0xFF); + writedata(0x60); + writedata(0x01); + writedata(0x04); + + writecommand(0xC3); + writedata(0x13); + writecommand(0xC4); + writedata(0x13); + + writecommand(0xC9); + writedata(0x22); + + writecommand(0xBE); + writedata(0x11); + + writecommand(0xE1); + writedata(0x10); + writedata(0x0E); + + writecommand(0xDF); + writedata(0x21); + writedata(0x0c); + writedata(0x02); + + writecommand(0xF0); + writedata(0x45); + writedata(0x09); + writedata(0x08); + writedata(0x08); + writedata(0x26); + writedata(0x2A); + + writecommand(0xF1); + writedata(0x43); + writedata(0x70); + writedata(0x72); + writedata(0x36); + writedata(0x37); + writedata(0x6F); + + writecommand(0xF2); + writedata(0x45); + writedata(0x09); + writedata(0x08); + writedata(0x08); + writedata(0x26); + writedata(0x2A); + + writecommand(0xF3); + writedata(0x43); + writedata(0x70); + writedata(0x72); + writedata(0x36); + writedata(0x37); + writedata(0x6F); + + writecommand(0xED); + writedata(0x1B); + writedata(0x0B); + + writecommand(0xAE); + writedata(0x77); + + writecommand(0xCD); + writedata(0x63); + + writecommand(0x70); + writedata(0x07); + writedata(0x07); + writedata(0x04); + writedata(0x0E); + writedata(0x0F); + writedata(0x09); + writedata(0x07); + writedata(0x08); + writedata(0x03); + + writecommand(0xE8); + writedata(0x34); + + writecommand(0x62); + writedata(0x18); + writedata(0x0D); + writedata(0x71); + writedata(0xED); + writedata(0x70); + writedata(0x70); + writedata(0x18); + writedata(0x0F); + writedata(0x71); + writedata(0xEF); + writedata(0x70); + writedata(0x70); + + writecommand(0x63); + writedata(0x18); + writedata(0x11); + writedata(0x71); + writedata(0xF1); + writedata(0x70); + writedata(0x70); + writedata(0x18); + writedata(0x13); + writedata(0x71); + writedata(0xF3); + writedata(0x70); + writedata(0x70); + + writecommand(0x64); + writedata(0x28); + writedata(0x29); + writedata(0xF1); + writedata(0x01); + writedata(0xF1); + writedata(0x00); + writedata(0x07); + + writecommand(0x66); + writedata(0x3C); + writedata(0x00); + writedata(0xCD); + writedata(0x67); + writedata(0x45); + writedata(0x45); + writedata(0x10); + writedata(0x00); + writedata(0x00); + writedata(0x00); + + writecommand(0x67); + writedata(0x00); + writedata(0x3C); + writedata(0x00); + writedata(0x00); + writedata(0x00); + writedata(0x01); + writedata(0x54); + writedata(0x10); + writedata(0x32); + writedata(0x98); + + writecommand(0x74); + writedata(0x10); + writedata(0x85); + writedata(0x80); + writedata(0x00); + writedata(0x00); + writedata(0x4E); + writedata(0x00); + + writecommand(0x98); + writedata(0x3e); + writedata(0x07); + + writecommand(0x35); + writecommand(0x21); + + writecommand(0x11); + delay(120); + writecommand(0x29); + delay(20); +} diff --git a/lib/Seeed_GFX/TFT_Drivers/GC9A01_Rotation.h b/lib/Seeed_GFX/TFT_Drivers/GC9A01_Rotation.h new file mode 100644 index 0000000..48724e7 --- /dev/null +++ b/lib/Seeed_GFX/TFT_Drivers/GC9A01_Rotation.h @@ -0,0 +1,56 @@ + +// This is the command sequence that rotates the GC9A01 driver coordinate frame + + rotation = m % 4; + + writecommand(TFT_MADCTL); + switch (rotation) { + case 0: // Portrait + writedata(TFT_MAD_BGR); + _width = _init_width; + _height = _init_height; +#ifdef CGRAM_OFFSET + if (_init_width == 128) + { + colstart = 2; + rowstart = 1; + } +#endif + break; + case 1: // Landscape (Portrait + 90) + writedata(TFT_MAD_MX | TFT_MAD_MV | TFT_MAD_BGR); + _width = _init_height; + _height = _init_width; +#ifdef CGRAM_OFFSET + if (_init_width == 128) + { + colstart = 1; + rowstart = 2; + } +#endif + break; + case 2: // Inverter portrait + writedata(TFT_MAD_MX | TFT_MAD_MY | TFT_MAD_BGR); + _width = _init_width; + _height = _init_height; +#ifdef CGRAM_OFFSET + if (_init_width == 128) + { + colstart = 2; + rowstart = 1; + } +#endif + break; + case 3: // Inverted landscape + writedata(TFT_MAD_MV | TFT_MAD_MY | TFT_MAD_BGR); + _width = _init_height; + _height = _init_width; +#ifdef CGRAM_OFFSET + if (_init_width == 128) + { + colstart = 1; + rowstart = 2; + } +#endif + break; + } diff --git a/lib/Seeed_GFX/TFT_Drivers/HX8357B_Defines.h b/lib/Seeed_GFX/TFT_Drivers/HX8357B_Defines.h new file mode 100644 index 0000000..d2f3cf9 --- /dev/null +++ b/lib/Seeed_GFX/TFT_Drivers/HX8357B_Defines.h @@ -0,0 +1,52 @@ +// Change the width and height if required (defined in portrait mode) +// or use the constructor to over-ride defaults +#define TFT_WIDTH 320 +#define TFT_HEIGHT 480 + + +// Delay between some initialisation commands +#define TFT_INIT_DELAY 0x80 // Not used unless commandlist invoked + + +// Generic commands used by TFT_eSPar.cpp +#define TFT_NOP 0x00 +#define TFT_SWRST 0x01 + +#define TFT_SLPIN 0x10 +#define TFT_SLPOUT 0x11 + +#define TFT_INVOFF 0x20 +#define TFT_INVON 0x21 + +#define TFT_DISPOFF 0x28 +#define TFT_DISPON 0x29 + +#define TFT_CASET 0x2A +#define TFT_PASET 0x2B +#define TFT_RAMWR 0x2C + +#define TFT_RAMRD 0x2E + +#define TFT_MADCTL 0x36 + +#define TFT_MAD_MY 0x80 +#define TFT_MAD_MX 0x40 +#define TFT_MAD_MV 0x20 +#define TFT_MAD_ML 0x10 +#define TFT_MAD_RGB 0x00 +#define TFT_MAD_BGR 0x08 +#define TFT_MAD_MH 0x04 +#define TFT_MAD_SS 0x02 +#define TFT_MAD_GS 0x01 + +#ifdef TFT_RGB_ORDER + #if (TFT_RGB_ORDER == 1) + #define TFT_MAD_COLOR_ORDER TFT_MAD_RGB + #else + #define TFT_MAD_COLOR_ORDER TFT_MAD_BGR + #endif +#else + #define TFT_MAD_COLOR_ORDER TFT_MAD_BGR +#endif + +#define TFT_IDXRD 0x00 // ILI9341 only, indexed control register read diff --git a/lib/Seeed_GFX/TFT_Drivers/HX8357B_Init.h b/lib/Seeed_GFX/TFT_Drivers/HX8357B_Init.h new file mode 100644 index 0000000..7ca937c --- /dev/null +++ b/lib/Seeed_GFX/TFT_Drivers/HX8357B_Init.h @@ -0,0 +1,76 @@ + +// This is the command sequence that initialises the HX8357B driver +// +// This setup information uses simple 8-bit SPI writecommand() and writedata() functions +// +// See ST7735_Setup.h file for an alternative format + + +// Configure HX8357-B display + writecommand(0x11); + delay(20); + writecommand(0xD0); + writedata(0x07); + writedata(0x42); + writedata(0x18); + + writecommand(0xD1); + writedata(0x00); + writedata(0x07); + writedata(0x10); + + writecommand(0xD2); + writedata(0x01); + writedata(0x02); + + writecommand(0xC0); + writedata(0x10); + writedata(0x3B); + writedata(0x00); + writedata(0x02); + writedata(0x11); + + writecommand(0xC5); + writedata(0x08); + + writecommand(0xC8); + writedata(0x00); + writedata(0x32); + writedata(0x36); + writedata(0x45); + writedata(0x06); + writedata(0x16); + writedata(0x37); + writedata(0x75); + writedata(0x77); + writedata(0x54); + writedata(0x0C); + writedata(0x00); + + writecommand(0x36); + writedata(0x0a); + + writecommand(0x3A); + writedata(0x55); + + writecommand(0x2A); + writedata(0x00); + writedata(0x00); + writedata(0x01); + writedata(0x3F); + + writecommand(0x2B); + writedata(0x00); + writedata(0x00); + writedata(0x01); + writedata(0xDF); + + delay(120); + writecommand(0x29); + + delay(25); + +// End of HX8357B display configuration + + + diff --git a/lib/Seeed_GFX/TFT_Drivers/HX8357B_Rotation.h b/lib/Seeed_GFX/TFT_Drivers/HX8357B_Rotation.h new file mode 100644 index 0000000..d3644f8 --- /dev/null +++ b/lib/Seeed_GFX/TFT_Drivers/HX8357B_Rotation.h @@ -0,0 +1,47 @@ + // This is the command sequence that rotates the HX8357C driver coordinate frame + + writecommand(TFT_MADCTL); + rotation = m % 8; + switch (rotation) { + case 0: // Portrait + writedata(TFT_MAD_COLOR_ORDER | TFT_MAD_MX); + _width = _init_width; + _height = _init_height; + break; + case 1: // Landscape (Portrait + 90) + writedata(TFT_MAD_COLOR_ORDER | TFT_MAD_MV); + _width = _init_height; + _height = _init_width; + break; + case 2: // Inverter portrait + writedata( TFT_MAD_COLOR_ORDER | TFT_MAD_MY); + _width = _init_width; + _height = _init_height; + break; + case 3: // Inverted landscape + writedata(TFT_MAD_COLOR_ORDER | TFT_MAD_MV | TFT_MAD_MX | TFT_MAD_MY); + _width = _init_height; + _height = _init_width; + break; + case 4: // Portrait + writedata(TFT_MAD_COLOR_ORDER | TFT_MAD_MX | TFT_MAD_MY); + _width = _init_width; + _height = _init_height; + break; + case 5: // Landscape (Portrait + 90) + writedata(TFT_MAD_COLOR_ORDER | TFT_MAD_MV | TFT_MAD_MX); + _width = _init_height; + _height = _init_width; + break; + case 6: // Inverter portrait + writedata( TFT_MAD_COLOR_ORDER); + _width = _init_width; + _height = _init_height; + break; + case 7: // Inverted landscape + writedata(TFT_MAD_COLOR_ORDER | TFT_MAD_MV | TFT_MAD_MY); + _width = _init_height; + _height = _init_width; + break; + } + \ No newline at end of file diff --git a/lib/Seeed_GFX/TFT_Drivers/HX8357C_Defines.h b/lib/Seeed_GFX/TFT_Drivers/HX8357C_Defines.h new file mode 100644 index 0000000..d2f3cf9 --- /dev/null +++ b/lib/Seeed_GFX/TFT_Drivers/HX8357C_Defines.h @@ -0,0 +1,52 @@ +// Change the width and height if required (defined in portrait mode) +// or use the constructor to over-ride defaults +#define TFT_WIDTH 320 +#define TFT_HEIGHT 480 + + +// Delay between some initialisation commands +#define TFT_INIT_DELAY 0x80 // Not used unless commandlist invoked + + +// Generic commands used by TFT_eSPar.cpp +#define TFT_NOP 0x00 +#define TFT_SWRST 0x01 + +#define TFT_SLPIN 0x10 +#define TFT_SLPOUT 0x11 + +#define TFT_INVOFF 0x20 +#define TFT_INVON 0x21 + +#define TFT_DISPOFF 0x28 +#define TFT_DISPON 0x29 + +#define TFT_CASET 0x2A +#define TFT_PASET 0x2B +#define TFT_RAMWR 0x2C + +#define TFT_RAMRD 0x2E + +#define TFT_MADCTL 0x36 + +#define TFT_MAD_MY 0x80 +#define TFT_MAD_MX 0x40 +#define TFT_MAD_MV 0x20 +#define TFT_MAD_ML 0x10 +#define TFT_MAD_RGB 0x00 +#define TFT_MAD_BGR 0x08 +#define TFT_MAD_MH 0x04 +#define TFT_MAD_SS 0x02 +#define TFT_MAD_GS 0x01 + +#ifdef TFT_RGB_ORDER + #if (TFT_RGB_ORDER == 1) + #define TFT_MAD_COLOR_ORDER TFT_MAD_RGB + #else + #define TFT_MAD_COLOR_ORDER TFT_MAD_BGR + #endif +#else + #define TFT_MAD_COLOR_ORDER TFT_MAD_BGR +#endif + +#define TFT_IDXRD 0x00 // ILI9341 only, indexed control register read diff --git a/lib/Seeed_GFX/TFT_Drivers/HX8357C_Init.h b/lib/Seeed_GFX/TFT_Drivers/HX8357C_Init.h new file mode 100644 index 0000000..807cadb --- /dev/null +++ b/lib/Seeed_GFX/TFT_Drivers/HX8357C_Init.h @@ -0,0 +1,116 @@ + +// This is the command sequence that initialises the HX8357C driver +// +// This setup information uses simple 8-bit SPI writecommand() and writedata() functions +// +// See ST7735_Setup.h file for an alternative format + + +// Configure HX8357C display + + writecommand(0xB9); // Enable extension command + writedata(0xFF); + writedata(0x83); + writedata(0x57); + delay(50); + + writecommand(0xB6); //Set VCOM voltage + writedata(0x2C); //0x52 for HSD 3.0" + + writecommand(0x11); // Sleep off + delay(200); + + writecommand(0x35); // Tearing effect on + writedata(0x00); // Added parameter + + writecommand(0x3A); // Interface pixel format + writedata(0x55); // 16 bits per pixel + + //writecommand(0xCC); // Set panel characteristic + //writedata(0x09); // S960>S1, G1>G480, R-G-B, normally black + + //writecommand(0xB3); // RGB interface + //writedata(0x43); + //writedata(0x00); + //writedata(0x06); + //writedata(0x06); + + writecommand(0xB1); // Power control + writedata(0x00); + writedata(0x15); + writedata(0x0D); + writedata(0x0D); + writedata(0x83); + writedata(0x48); + + + writecommand(0xC0); // Does this do anything? + writedata(0x24); + writedata(0x24); + writedata(0x01); + writedata(0x3C); + writedata(0xC8); + writedata(0x08); + + writecommand(0xB4); // Display cycle + writedata(0x02); + writedata(0x40); + writedata(0x00); + writedata(0x2A); + writedata(0x2A); + writedata(0x0D); + writedata(0x4F); + + writecommand(0xE0); // Gamma curve + writedata(0x00); + writedata(0x15); + writedata(0x1D); + writedata(0x2A); + writedata(0x31); + writedata(0x42); + writedata(0x4C); + writedata(0x53); + writedata(0x45); + writedata(0x40); + writedata(0x3B); + writedata(0x32); + writedata(0x2E); + writedata(0x28); + + writedata(0x24); + writedata(0x03); + writedata(0x00); + writedata(0x15); + writedata(0x1D); + writedata(0x2A); + writedata(0x31); + writedata(0x42); + writedata(0x4C); + writedata(0x53); + writedata(0x45); + writedata(0x40); + writedata(0x3B); + writedata(0x32); + + writedata(0x2E); + writedata(0x28); + writedata(0x24); + writedata(0x03); + writedata(0x00); + writedata(0x01); + + writecommand(0x36); // MADCTL Memory access control + writedata(0x48); + delay(20); + + writecommand(0x21); //Display inversion on + delay(20); + + writecommand(0x29); // Display on + + delay(120); + +// End of HX8357C display configuration + + + diff --git a/lib/Seeed_GFX/TFT_Drivers/HX8357C_Rotation.h b/lib/Seeed_GFX/TFT_Drivers/HX8357C_Rotation.h new file mode 100644 index 0000000..d3644f8 --- /dev/null +++ b/lib/Seeed_GFX/TFT_Drivers/HX8357C_Rotation.h @@ -0,0 +1,47 @@ + // This is the command sequence that rotates the HX8357C driver coordinate frame + + writecommand(TFT_MADCTL); + rotation = m % 8; + switch (rotation) { + case 0: // Portrait + writedata(TFT_MAD_COLOR_ORDER | TFT_MAD_MX); + _width = _init_width; + _height = _init_height; + break; + case 1: // Landscape (Portrait + 90) + writedata(TFT_MAD_COLOR_ORDER | TFT_MAD_MV); + _width = _init_height; + _height = _init_width; + break; + case 2: // Inverter portrait + writedata( TFT_MAD_COLOR_ORDER | TFT_MAD_MY); + _width = _init_width; + _height = _init_height; + break; + case 3: // Inverted landscape + writedata(TFT_MAD_COLOR_ORDER | TFT_MAD_MV | TFT_MAD_MX | TFT_MAD_MY); + _width = _init_height; + _height = _init_width; + break; + case 4: // Portrait + writedata(TFT_MAD_COLOR_ORDER | TFT_MAD_MX | TFT_MAD_MY); + _width = _init_width; + _height = _init_height; + break; + case 5: // Landscape (Portrait + 90) + writedata(TFT_MAD_COLOR_ORDER | TFT_MAD_MV | TFT_MAD_MX); + _width = _init_height; + _height = _init_width; + break; + case 6: // Inverter portrait + writedata( TFT_MAD_COLOR_ORDER); + _width = _init_width; + _height = _init_height; + break; + case 7: // Inverted landscape + writedata(TFT_MAD_COLOR_ORDER | TFT_MAD_MV | TFT_MAD_MY); + _width = _init_height; + _height = _init_width; + break; + } + \ No newline at end of file diff --git a/lib/Seeed_GFX/TFT_Drivers/HX8357D_Defines.h b/lib/Seeed_GFX/TFT_Drivers/HX8357D_Defines.h new file mode 100644 index 0000000..b3def6c --- /dev/null +++ b/lib/Seeed_GFX/TFT_Drivers/HX8357D_Defines.h @@ -0,0 +1,96 @@ +// Change the width and height if required (defined in portrait mode) +// or use the constructor to over-ride defaults +#define TFT_WIDTH 320 +#define TFT_HEIGHT 480 + + +// Delay between some initialisation commands +#define TFT_INIT_DELAY 0x80 // Not used unless commandlist invoked + + +// Generic commands used by TFT_eSPar.cpp +#define TFT_NOP 0x00 +#define TFT_SWRST 0x01 + +#define TFT_SLPIN 0x10 +#define TFT_SLPOUT 0x11 + +#define TFT_INVOFF 0x20 +#define TFT_INVON 0x21 + +#define TFT_DISPOFF 0x28 +#define TFT_DISPON 0x29 + +#define TFT_CASET 0x2A +#define TFT_PASET 0x2B +#define TFT_RAMWR 0x2C + +#define TFT_RAMRD 0x2E + +#define TFT_MADCTL 0x36 + +#define TFT_MAD_MY 0x80 +#define TFT_MAD_MX 0x40 +#define TFT_MAD_MV 0x20 +#define TFT_MAD_ML 0x10 +#define TFT_MAD_RGB 0x00 +#define TFT_MAD_BGR 0x08 +#define TFT_MAD_MH 0x04 +#define TFT_MAD_SS 0x02 +#define TFT_MAD_GS 0x01 + +#ifdef TFT_RGB_ORDER + #if (TFT_RGB_ORDER == 1) + #define TFT_MAD_COLOR_ORDER TFT_MAD_RGB + #else + #define TFT_MAD_COLOR_ORDER TFT_MAD_BGR + #endif +#else + #define TFT_MAD_COLOR_ORDER TFT_MAD_RGB +#endif + +#define TFT_IDXRD 0x00 // ILI9341 only, indexed control register read + + +#define HX8357_NOP 0x00 +#define HX8357_SWRESET 0x01 +#define HX8357_RDDID 0x04 +#define HX8357_RDDST 0x09 + +#define HX8357_RDPOWMODE 0x0A +#define HX8357_RDMADCTL 0x0B +#define HX8357_RDCOLMOD 0x0C +#define HX8357_RDDIM 0x0D +#define HX8357_RDDSDR 0x0F + +#define HX8357_SLPIN 0x10 +#define HX8357_SLPOUT 0x11 + +#define HX8357_INVOFF 0x20 +#define HX8357_INVON 0x21 +#define HX8357_DISPOFF 0x28 +#define HX8357_DISPON 0x29 + +#define HX8357_CASET 0x2A +#define HX8357_PASET 0x2B +#define HX8357_RAMWR 0x2C +#define HX8357_RAMRD 0x2E + +#define HX8357_TEON 0x35 +#define HX8357_TEARLINE 0x44 +#define HX8357_MADCTL 0x36 +#define HX8357_COLMOD 0x3A + +#define HX8357_SETOSC 0xB0 +#define HX8357_SETPWR1 0xB1 +#define HX8357_SETRGB 0xB3 +#define HX8357D_SETCOM 0xB6 + +#define HX8357D_SETCYC 0xB4 +#define HX8357D_SETC 0xB9 + +#define HX8357D_SETSTBA 0xC0 + +#define HX8357_SETPANEL 0xCC + +#define HX8357D_SETGAMMA 0xE0 diff --git a/lib/Seeed_GFX/TFT_Drivers/HX8357D_Init.h b/lib/Seeed_GFX/TFT_Drivers/HX8357D_Init.h new file mode 100644 index 0000000..0e49d3f --- /dev/null +++ b/lib/Seeed_GFX/TFT_Drivers/HX8357D_Init.h @@ -0,0 +1,118 @@ + +// This is the command sequence that initialises the HX8357D driver +// +// This setup information uses simple 8-bit SPI writecommand() and writedata() functions +// +// See ST7735_Setup.h file for an alternative format + + +// Configure HX8357D display + + // setextc + writecommand(HX8357D_SETC); + writedata(0xFF); + writedata(0x83); + writedata(0x57); + delay(300); + + // setRGB which also enables SDO + writecommand(HX8357_SETRGB); + writedata(0x80); //enable SDO pin! +// writedata(0x00); //disable SDO pin! + writedata(0x0); + writedata(0x06); + writedata(0x06); + + writecommand(HX8357D_SETCOM); + writedata(0x25); // -1.52V + + writecommand(HX8357_SETOSC); + writedata(0x68); // Normal mode 70Hz, Idle mode 55 Hz + + writecommand(HX8357_SETPANEL); //Set Panel + writedata(0x05); // BGR, Gate direction swapped + + writecommand(HX8357_SETPWR1); + writedata(0x00); // Not deep standby + writedata(0x15); //BT + writedata(0x1C); //VSPR + writedata(0x1C); //VSNR + writedata(0x83); //AP + writedata(0xAA); //FS + + writecommand(HX8357D_SETSTBA); + writedata(0x50); //OPON normal + writedata(0x50); //OPON idle + writedata(0x01); //STBA + writedata(0x3C); //STBA + writedata(0x1E); //STBA + writedata(0x08); //GEN + + writecommand(HX8357D_SETCYC); + writedata(0x02); //NW 0x02 + writedata(0x40); //RTN + writedata(0x00); //DIV + writedata(0x2A); //DUM + writedata(0x2A); //DUM + writedata(0x0D); //GDON + writedata(0x78); //GDOFF + + writecommand(HX8357D_SETGAMMA); + writedata(0x02); + writedata(0x0A); + writedata(0x11); + writedata(0x1d); + writedata(0x23); + writedata(0x35); + writedata(0x41); + writedata(0x4b); + writedata(0x4b); + writedata(0x42); + writedata(0x3A); + writedata(0x27); + writedata(0x1B); + writedata(0x08); + writedata(0x09); + writedata(0x03); + writedata(0x02); + writedata(0x0A); + writedata(0x11); + writedata(0x1d); + writedata(0x23); + writedata(0x35); + writedata(0x41); + writedata(0x4b); + writedata(0x4b); + writedata(0x42); + writedata(0x3A); + writedata(0x27); + writedata(0x1B); + writedata(0x08); + writedata(0x09); + writedata(0x03); + writedata(0x00); + writedata(0x01); + + writecommand(HX8357_COLMOD); + writedata(0x55); // 16-bit + + writecommand(HX8357_MADCTL); + writedata(TFT_MAD_MX | TFT_MAD_MY | TFT_MAD_COLOR_ORDER); + + writecommand(HX8357_TEON); // TE off + writedata(0x00); + + writecommand(HX8357_TEARLINE); // tear line + writedata(0x00); + writedata(0x02); + + writecommand(HX8357_SLPOUT); //Exit Sleep + delay(150); + + writecommand(HX8357_DISPON); // display on + delay(50); + +// End of HX8357D display configuration + + + diff --git a/lib/Seeed_GFX/TFT_Drivers/HX8357D_Rotation.h b/lib/Seeed_GFX/TFT_Drivers/HX8357D_Rotation.h new file mode 100644 index 0000000..f54eaef --- /dev/null +++ b/lib/Seeed_GFX/TFT_Drivers/HX8357D_Rotation.h @@ -0,0 +1,26 @@ + // This is the command sequence that rotates the HX8357D driver coordinate frame + + writecommand(TFT_MADCTL); + rotation = m % 4; + switch (rotation) { + case 0: // Portrait + writedata(TFT_MAD_MX | TFT_MAD_MY | TFT_MAD_COLOR_ORDER); + _width = _init_width; + _height = _init_height; + break; + case 1: // Landscape (Portrait + 90) + writedata(TFT_MAD_MV | TFT_MAD_MY | TFT_MAD_COLOR_ORDER); + _width = _init_height; + _height = _init_width; + break; + case 2: // Inverter portrait + writedata(TFT_MAD_COLOR_ORDER); + _width = _init_width; + _height = _init_height; + break; + case 3: // Inverted landscape + writedata(TFT_MAD_MX | TFT_MAD_MV | TFT_MAD_COLOR_ORDER); + _width = _init_height; + _height = _init_width; + break; + } diff --git a/lib/Seeed_GFX/TFT_Drivers/ILI9163_Defines.h b/lib/Seeed_GFX/TFT_Drivers/ILI9163_Defines.h new file mode 100644 index 0000000..f1c6339 --- /dev/null +++ b/lib/Seeed_GFX/TFT_Drivers/ILI9163_Defines.h @@ -0,0 +1,65 @@ +// Change the width and height if required (defined in portrait mode) +// or use the constructor to over-ride defaults +#ifndef TFT_WIDTH + #define TFT_WIDTH 128 +#endif +#ifndef TFT_HEIGHT + #define TFT_HEIGHT 160 +#endif + +#if (TFT_HEIGHT == 128) && (TFT_WIDTH == 128) + #define CGRAM_OFFSET +#endif + +// Color definitions for backwards compatibility with old sketches +// use colour definitions like TFT_BLACK to make sketches more portable +#define ILI9163_BLACK 0x0000 /* 0, 0, 0 */ +#define ILI9163_NAVY 0x000F /* 0, 0, 128 */ +#define ILI9163_DARKGREEN 0x03E0 /* 0, 128, 0 */ +#define ILI9163_DARKCYAN 0x03EF /* 0, 128, 128 */ +#define ILI9163_MAROON 0x7800 /* 128, 0, 0 */ +#define ILI9163_PURPLE 0x780F /* 128, 0, 128 */ +#define ILI9163_OLIVE 0x7BE0 /* 128, 128, 0 */ +#define ILI9163_LIGHTGREY 0xC618 /* 192, 192, 192 */ +#define ILI9163_DARKGREY 0x7BEF /* 128, 128, 128 */ +#define ILI9163_BLUE 0x001F /* 0, 0, 255 */ +#define ILI9163_GREEN 0x07E0 /* 0, 255, 0 */ +#define ILI9163_CYAN 0x07FF /* 0, 255, 255 */ +#define ILI9163_RED 0xF800 /* 255, 0, 0 */ +#define ILI9163_MAGENTA 0xF81F /* 255, 0, 255 */ +#define ILI9163_YELLOW 0xFFE0 /* 255, 255, 0 */ +#define ILI9163_WHITE 0xFFFF /* 255, 255, 255 */ +#define ILI9163_ORANGE 0xFD20 /* 255, 165, 0 */ +#define ILI9163_GREENYELLOW 0xAFE5 /* 173, 255, 47 */ +#define ILI9163_PINK 0xF81F + + +// Delay between some initialisation commands +#define TFT_INIT_DELAY 0x80 + + +// Generic commands used by TFT_eSPI.cpp +#define TFT_NOP 0x00 +#define TFT_SWRST 0x01 + +#define TFT_INVOFF 0x20 +#define TFT_INVON 0x21 + +#define TFT_DISPOFF 0x28 +#define TFT_DISPON 0x29 + +#define TFT_CASET 0x2A +#define TFT_PASET 0x2B +#define TFT_RAMWR 0x2C + +#define TFT_RAMRD 0x2E +#define TFT_IDXRD 0x00 //0xDD // ILI9341 only, indexed control register read + +#define TFT_MADCTL 0x36 +#define TFT_MAD_MY 0x80 +#define TFT_MAD_MX 0x40 +#define TFT_MAD_MV 0x20 +#define TFT_MAD_ML 0x10 +#define TFT_MAD_BGR 0x08 +#define TFT_MAD_MH 0x04 +#define TFT_MAD_RGB 0x00 diff --git a/lib/Seeed_GFX/TFT_Drivers/ILI9163_Init.h b/lib/Seeed_GFX/TFT_Drivers/ILI9163_Init.h new file mode 100644 index 0000000..0f2702c --- /dev/null +++ b/lib/Seeed_GFX/TFT_Drivers/ILI9163_Init.h @@ -0,0 +1,41 @@ + +// This is the command sequence that initialises the ILI9163 driver +// +// This setup information is in a format accecpted by the commandList() function +// which reduces FLASH space, but on an ESP8266 there is plenty available! +// +// See ILI9341_Setup.h file for an alternative simpler format + +{ + // Initialization commands for ILI9163 screens + static const uint8_t ILI9163_cmds[] PROGMEM = + { + 17, // 17 commands follow + 0x01, 0 + TFT_INIT_DELAY, 120, // Software reset + 0x11, 0 + TFT_INIT_DELAY, 5, // Exit sleep mode + 0x3A, 1, 0x05, // Set pixel format + 0x26, 1, 0x04, // Set Gamma curve 3 + 0xF2, 1, 0x01, // Gamma adjustment enabled + 0xE0, 15, 0x3F, 0x25, 0x1C, 0x1E, 0x20, 0x12, 0x2A, 0x90, + 0x24, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, // Positive Gamma + 0xE1, 15, 0x20, 0x20, 0x20, 0x20, 0x05, 0x00, 0x15,0xA7, + 0x3D, 0x18, 0x25, 0x2A, 0x2B, 0x2B, 0x3A, // Negative Gamma + 0xB1, 2, 0x08, 0x08, // Frame rate control 1 + 0xB4, 1, 0x07, // Display inversion + 0xC0, 2, 0x0A, 0x02, // Power control 1 + 0xC1, 1, 0x02, // Power control 2 + 0xC5, 2, 0x50, 0x5B, // Vcom control 1 + 0xC7, 1, 0x40, // Vcom offset + 0x2A, 4, 0x00, 0x00, 0x00, 0x7F, // Set column address + 0x2B, 4 + TFT_INIT_DELAY, 0x00, 0x00, 0x00, 0x9F, 250, // Set page address + 0x36, 1, 0xC8, // Set address mode + 0x29, 0, // Set display on + }; + + commandList(ILI9163_cmds); + + #ifdef CGRAM_OFFSET + colstart = 0; + rowstart = 0; + #endif +} \ No newline at end of file diff --git a/lib/Seeed_GFX/TFT_Drivers/ILI9163_Rotation.h b/lib/Seeed_GFX/TFT_Drivers/ILI9163_Rotation.h new file mode 100644 index 0000000..3323169 --- /dev/null +++ b/lib/Seeed_GFX/TFT_Drivers/ILI9163_Rotation.h @@ -0,0 +1,44 @@ + +// This is the command sequence that rotates the ILI9163 driver coordinate frame + + rotation = m % 4; + + writecommand(TFT_MADCTL); + switch (rotation) { + case 0: + writedata(TFT_MAD_MX | TFT_MAD_MY | TFT_MAD_BGR); + _width = _init_width; + _height = _init_height; + #ifdef CGRAM_OFFSET + colstart = 0; + rowstart = 0; + #endif + break; + case 1: + writedata(TFT_MAD_MV | TFT_MAD_MY | TFT_MAD_BGR); + _width = _init_height; + _height = _init_width; + #ifdef CGRAM_OFFSET + colstart = 0; + rowstart = 0; + #endif + break; + case 2: + writedata(TFT_MAD_BGR); + _width = _init_width; + _height = _init_height; + #ifdef CGRAM_OFFSET + colstart = 0; + rowstart = 32; + #endif + break; + case 3: + writedata(TFT_MAD_MX | TFT_MAD_MV | TFT_MAD_BGR); + _width = _init_height; + _height = _init_width; + #ifdef CGRAM_OFFSET + colstart = 32; + rowstart = 0; + #endif + break; + } diff --git a/lib/Seeed_GFX/TFT_Drivers/ILI9225_Defines.h b/lib/Seeed_GFX/TFT_Drivers/ILI9225_Defines.h new file mode 100644 index 0000000..1a083e0 --- /dev/null +++ b/lib/Seeed_GFX/TFT_Drivers/ILI9225_Defines.h @@ -0,0 +1,84 @@ +// Change the width and height if required (defined in portrait mode) +// or use the constructor to over-ride defaults +#define TFT_WIDTH 176 +#define TFT_HEIGHT 220 + +// Generic commands used by TFT_eSPI.cpp +#define TFT_NOP 0x00 +#define TFT_SWRST 0x28 + +#define TFT_CASET 0 +#define TFT_PASET 0 + +#define TFT_CASET1 ILI9225_HORIZONTAL_WINDOW_ADDR2 +#define TFT_CASET2 ILI9225_HORIZONTAL_WINDOW_ADDR1 + +#define TFT_PASET1 ILI9225_VERTICAL_WINDOW_ADDR2 +#define TFT_PASET2 ILI9225_VERTICAL_WINDOW_ADDR1 + +#define TFT_RAM_ADDR1 ILI9225_RAM_ADDR_SET1 +#define TFT_RAM_ADDR2 ILI9225_RAM_ADDR_SET2 + +#define TFT_RAMWR ILI9225_GRAM_DATA_REG + +#define TFT_MAD_BGR 0x10 +#define TFT_MAD_RGB 0x00 + +#ifdef TFT_RGB_ORDER + #if (TFT_RGB_ORDER == 1) + #define TFT_MAD_COLOR_ORDER TFT_MAD_RGB + #else + #define TFT_MAD_COLOR_ORDER TFT_MAD_BGR + #endif +#else + #define TFT_MAD_COLOR_ORDER TFT_MAD_BGR +#endif + +// Not used +#define TFT_INVOFF 0x00 +#define TFT_INVON 0x00 +#define TFT_RAMRD 0x00 +#define TFT_IDXRD 0x00 + +/* ILI9225 Registers */ +#define ILI9225_DRIVER_OUTPUT_CTRL 0x01 // Driver Output Control +#define ILI9225_LCD_AC_DRIVING_CTRL 0x02 // LCD AC Driving Control +#define ILI9225_ENTRY_MODE 0x03 // Entry Mode +#define ILI9225_DISP_CTRL1 0x07 // Display Control 1 +#define ILI9225_BLANK_PERIOD_CTRL1 0x08 // Blank Period Control +#define ILI9225_FRAME_CYCLE_CTRL 0x0B // Frame Cycle Control +#define ILI9225_INTERFACE_CTRL 0x0C // Interface Control +#define ILI9225_OSC_CTRL 0x0F // Osc Control +#define ILI9225_POWER_CTRL1 0x10 // Power Control 1 +#define ILI9225_POWER_CTRL2 0x11 // Power Control 2 +#define ILI9225_POWER_CTRL3 0x12 // Power Control 3 +#define ILI9225_POWER_CTRL4 0x13 // Power Control 4 +#define ILI9225_POWER_CTRL5 0x14 // Power Control 5 +#define ILI9225_VCI_RECYCLING 0x15 // VCI Recycling +#define ILI9225_RAM_ADDR_SET1 0x20 // Horizontal GRAM Address Set +#define ILI9225_RAM_ADDR_SET2 0x21 // Vertical GRAM Address Set +#define ILI9225_GRAM_DATA_REG 0x22 // GRAM Data Register +#define ILI9225_GATE_SCAN_CTRL 0x30 // Gate Scan Control Register +#define ILI9225_VERTICAL_SCROLL_CTRL1 0x31 // Vertical Scroll Control 1 Register +#define ILI9225_VERTICAL_SCROLL_CTRL2 0x32 // Vertical Scroll Control 2 Register +#define ILI9225_VERTICAL_SCROLL_CTRL3 0x33 // Vertical Scroll Control 3 Register +#define ILI9225_PARTIAL_DRIVING_POS1 0x34 // Partial Driving Position 1 Register +#define ILI9225_PARTIAL_DRIVING_POS2 0x35 // Partial Driving Position 2 Register +#define ILI9225_HORIZONTAL_WINDOW_ADDR1 0x36 // Horizontal Address Start Position +#define ILI9225_HORIZONTAL_WINDOW_ADDR2 0x37 // Horizontal Address End Position +#define ILI9225_VERTICAL_WINDOW_ADDR1 0x38 // Vertical Address Start Position +#define ILI9225_VERTICAL_WINDOW_ADDR2 0x39 // Vertical Address End Position +#define ILI9225_GAMMA_CTRL1 0x50 // Gamma Control 1 +#define ILI9225_GAMMA_CTRL2 0x51 // Gamma Control 2 +#define ILI9225_GAMMA_CTRL3 0x52 // Gamma Control 3 +#define ILI9225_GAMMA_CTRL4 0x53 // Gamma Control 4 +#define ILI9225_GAMMA_CTRL5 0x54 // Gamma Control 5 +#define ILI9225_GAMMA_CTRL6 0x55 // Gamma Control 6 +#define ILI9225_GAMMA_CTRL7 0x56 // Gamma Control 7 +#define ILI9225_GAMMA_CTRL8 0x57 // Gamma Control 8 +#define ILI9225_GAMMA_CTRL9 0x58 // Gamma Control 9 +#define ILI9225_GAMMA_CTRL10 0x59 // Gamma Control 10 + +// Delay between some initialisation commands +#define TFT_INIT_DELAY 0x00 // Not used unless commandlist invoked + diff --git a/lib/Seeed_GFX/TFT_Drivers/ILI9225_Init.h b/lib/Seeed_GFX/TFT_Drivers/ILI9225_Init.h new file mode 100644 index 0000000..741be43 --- /dev/null +++ b/lib/Seeed_GFX/TFT_Drivers/ILI9225_Init.h @@ -0,0 +1,105 @@ +// This is the command sequence that initialises the ILI9225 driver + +{ + writecommand(ILI9225_POWER_CTRL1); + writedata(0x00);writedata(0x00); + writecommand(ILI9225_POWER_CTRL2); + writedata(0x00);writedata(0x00); + writecommand(ILI9225_POWER_CTRL3); + writedata(0x00);writedata(0x00); + writecommand(ILI9225_POWER_CTRL4); + writedata(0x00);writedata(0x00); + writecommand(ILI9225_POWER_CTRL5); + writedata(0x00);writedata(0x00); + + delay(40); + + writecommand(ILI9225_POWER_CTRL2); + writedata(0x00);writedata(0x18); + writecommand(ILI9225_POWER_CTRL3); + writedata(0x61);writedata(0x21); + writecommand(ILI9225_POWER_CTRL4); + writedata(0x00);writedata(0x6F); + writecommand(ILI9225_POWER_CTRL5); + writedata(0x49);writedata(0x5F); + writecommand(ILI9225_POWER_CTRL1); + writedata(0x08);writedata(0x00); + + delay(10); + + writecommand(ILI9225_POWER_CTRL2); + writedata(0x10);writedata(0x3B); + + delay(50); + + writecommand(ILI9225_LCD_AC_DRIVING_CTRL); + writedata(0x01);writedata(0x00); + writecommand(ILI9225_DISP_CTRL1); + writedata(0x00);writedata(0x00); + writecommand(ILI9225_BLANK_PERIOD_CTRL1); + writedata(0x08);writedata(0x08); + writecommand(ILI9225_FRAME_CYCLE_CTRL); + writedata(0x11);writedata(0x00); + writecommand(ILI9225_INTERFACE_CTRL); + writedata(0x00);writedata(0x00); + writecommand(ILI9225_OSC_CTRL); + writedata(0x0D);writedata(0x01); + writecommand(ILI9225_VCI_RECYCLING); + writedata(0x00);writedata(0x20); + writecommand(ILI9225_RAM_ADDR_SET1); + writedata(0x00);writedata(0x00); + writecommand(ILI9225_RAM_ADDR_SET2); + writedata(0x00);writedata(0x00); + + writecommand(ILI9225_GATE_SCAN_CTRL); + writedata(0x00);writedata(0x00); + writecommand(ILI9225_VERTICAL_SCROLL_CTRL1); + writedata(0x00);writedata(0xDB); + writecommand(ILI9225_VERTICAL_SCROLL_CTRL2); + writedata(0x00);writedata(0x00); + writecommand(ILI9225_VERTICAL_SCROLL_CTRL3); + writedata(0x00);writedata(0x00); + writecommand(ILI9225_PARTIAL_DRIVING_POS1); + writedata(0x00);writedata(0xDB); + writecommand(ILI9225_PARTIAL_DRIVING_POS2); + writedata(0x00);writedata(0x00); + writecommand(ILI9225_HORIZONTAL_WINDOW_ADDR1); + writedata(0x00);writedata(0xAF); + writecommand(ILI9225_HORIZONTAL_WINDOW_ADDR2); + writedata(0x00);writedata(0x00); + writecommand(ILI9225_VERTICAL_WINDOW_ADDR1); + writedata(0x00);writedata(0xDB); + writecommand(ILI9225_VERTICAL_WINDOW_ADDR2); + writedata(0x00);writedata(0x00); + + /* Set GAMMA curve */ + writecommand(ILI9225_GAMMA_CTRL1); + writedata(0x00);writedata(0x00); + writecommand(ILI9225_GAMMA_CTRL2); + writedata(0x08);writedata(0x08); + writecommand(ILI9225_GAMMA_CTRL3); + writedata(0x08);writedata(0x0A); + writecommand(ILI9225_GAMMA_CTRL4); + writedata(0x00);writedata(0x0A); + writecommand(ILI9225_GAMMA_CTRL5); + writedata(0x0A);writedata(0x08); + writecommand(ILI9225_GAMMA_CTRL6); + writedata(0x08);writedata(0x08); + writecommand(ILI9225_GAMMA_CTRL7); + writedata(0x00);writedata(0x00); + writecommand(ILI9225_GAMMA_CTRL8); + writedata(0x0A);writedata(0x00); + writecommand(ILI9225_GAMMA_CTRL9); + writedata(0x07);writedata(0x10); + writecommand(ILI9225_GAMMA_CTRL10); + writedata(0x07);writedata(0x10); + + writecommand(ILI9225_DISP_CTRL1); + writedata(0x00);writedata(0x12); + + delay(50); + + writecommand(ILI9225_DISP_CTRL1); + writedata(0x10);writedata(0x17); + +} \ No newline at end of file diff --git a/lib/Seeed_GFX/TFT_Drivers/ILI9225_Rotation.h b/lib/Seeed_GFX/TFT_Drivers/ILI9225_Rotation.h new file mode 100644 index 0000000..d7cef9a --- /dev/null +++ b/lib/Seeed_GFX/TFT_Drivers/ILI9225_Rotation.h @@ -0,0 +1,39 @@ + +// This is the command sequence that rotates the ILI9225 driver coordinate frame + + rotation = m % 4; // Limit the range of values to 0-3 + + switch (rotation) { + case 0: + writecommand(ILI9225_DRIVER_OUTPUT_CTRL); + writedata(0x01);writedata(0x1C); + writecommand(ILI9225_ENTRY_MODE); + writedata(TFT_MAD_COLOR_ORDER);writedata(0x30); + _width = _init_width; + _height = _init_height; + break; + case 1: + writecommand(ILI9225_DRIVER_OUTPUT_CTRL); + writedata(0x00);writedata(0x1C); + writecommand(ILI9225_ENTRY_MODE); + writedata(TFT_MAD_COLOR_ORDER);writedata(0x38); + _width = _init_height; + _height = _init_width; + break; + case 2: + writecommand(ILI9225_DRIVER_OUTPUT_CTRL); + writedata(0x02);writedata(0x1C); + writecommand(ILI9225_ENTRY_MODE); + writedata(TFT_MAD_COLOR_ORDER);writedata(0x30); + _width = _init_width; + _height = _init_height; + break; + case 3: + writecommand(ILI9225_DRIVER_OUTPUT_CTRL); + writedata(0x03);writedata(0x1C); + writecommand(ILI9225_ENTRY_MODE); + writedata(TFT_MAD_COLOR_ORDER);writedata(0x38); + _width = _init_height; + _height = _init_width; + break; + } diff --git a/lib/Seeed_GFX/TFT_Drivers/ILI9341_Defines.h b/lib/Seeed_GFX/TFT_Drivers/ILI9341_Defines.h new file mode 100644 index 0000000..94ce1ef --- /dev/null +++ b/lib/Seeed_GFX/TFT_Drivers/ILI9341_Defines.h @@ -0,0 +1,143 @@ +// Change the width and height if required (defined in portrait mode) +// or use the constructor to over-ride defaults + +#if defined (ILI9341_DRIVER) || defined (ILI9341_2_DRIVER) + #define TFT_WIDTH 240 + #define TFT_HEIGHT 320 +#elif defined (ILI9342_DRIVER) + #define TFT_WIDTH 320 + #define TFT_HEIGHT 240 +#endif + +// Color definitions for backwards compatibility with old sketches +// use colour definitions like TFT_BLACK to make sketches more portable +#define ILI9341_BLACK 0x0000 /* 0, 0, 0 */ +#define ILI9341_NAVY 0x000F /* 0, 0, 128 */ +#define ILI9341_DARKGREEN 0x03E0 /* 0, 128, 0 */ +#define ILI9341_DARKCYAN 0x03EF /* 0, 128, 128 */ +#define ILI9341_MAROON 0x7800 /* 128, 0, 0 */ +#define ILI9341_PURPLE 0x780F /* 128, 0, 128 */ +#define ILI9341_OLIVE 0x7BE0 /* 128, 128, 0 */ +#define ILI9341_LIGHTGREY 0xC618 /* 192, 192, 192 */ +#define ILI9341_DARKGREY 0x7BEF /* 128, 128, 128 */ +#define ILI9341_BLUE 0x001F /* 0, 0, 255 */ +#define ILI9341_GREEN 0x07E0 /* 0, 255, 0 */ +#define ILI9341_CYAN 0x07FF /* 0, 255, 255 */ +#define ILI9341_RED 0xF800 /* 255, 0, 0 */ +#define ILI9341_MAGENTA 0xF81F /* 255, 0, 255 */ +#define ILI9341_YELLOW 0xFFE0 /* 255, 255, 0 */ +#define ILI9341_WHITE 0xFFFF /* 255, 255, 255 */ +#define ILI9341_ORANGE 0xFD20 /* 255, 165, 0 */ +#define ILI9341_GREENYELLOW 0xAFE5 /* 173, 255, 47 */ +#define ILI9341_PINK 0xF81F + + +// Delay between some initialisation commands +#define TFT_INIT_DELAY 0x80 // Not used unless commandlist invoked + + +// Generic commands used by TFT_eSPI.cpp +#define TFT_NOP 0x00 +#define TFT_SWRST 0x01 + +#define TFT_INVOFF 0x20 +#define TFT_INVON 0x21 + +#define TFT_DISPOFF 0x28 +#define TFT_DISPON 0x29 + +#define TFT_CASET 0x2A +#define TFT_PASET 0x2B +#define TFT_RAMWR 0x2C + +#define TFT_RAMRD 0x2E +#define TFT_IDXRD 0xDD // ILI9341 only, indexed control register read + +#define TFT_MADCTL 0x36 +#define TFT_MAD_MY 0x80 +#define TFT_MAD_MX 0x40 +#define TFT_MAD_MV 0x20 +#define TFT_MAD_ML 0x10 +#define TFT_MAD_BGR 0x08 +#define TFT_MAD_MH 0x04 +#define TFT_MAD_RGB 0x00 + +#ifdef TFT_RGB_ORDER + #if (TFT_RGB_ORDER == 1) + #define TFT_MAD_COLOR_ORDER TFT_MAD_RGB + #else + #define TFT_MAD_COLOR_ORDER TFT_MAD_BGR + #endif +#else + #define TFT_MAD_COLOR_ORDER TFT_MAD_BGR +#endif + +// All ILI9341 specific commands some are used by init() +#define ILI9341_NOP 0x00 +#define ILI9341_SWRESET 0x01 +#define ILI9341_RDDID 0x04 +#define ILI9341_RDDST 0x09 + +#define ILI9341_SLPIN 0x10 +#define ILI9341_SLPOUT 0x11 +#define ILI9341_PTLON 0x12 +#define ILI9341_NORON 0x13 + +#define ILI9341_RDMODE 0x0A +#define ILI9341_RDMADCTL 0x0B +#define ILI9341_RDPIXFMT 0x0C +#define ILI9341_RDIMGFMT 0x0A +#define ILI9341_RDSELFDIAG 0x0F + +#define ILI9341_INVOFF 0x20 +#define ILI9341_INVON 0x21 +#define ILI9341_GAMMASET 0x26 +#define ILI9341_DISPOFF 0x28 +#define ILI9341_DISPON 0x29 + +#define ILI9341_CASET 0x2A +#define ILI9341_PASET 0x2B +#define ILI9341_RAMWR 0x2C +#define ILI9341_RAMRD 0x2E + +#define ILI9341_PTLAR 0x30 +#define ILI9341_VSCRDEF 0x33 +#define ILI9341_MADCTL 0x36 +#define ILI9341_VSCRSADD 0x37 +#define ILI9341_PIXFMT 0x3A + +#define ILI9341_WRDISBV 0x51 +#define ILI9341_RDDISBV 0x52 +#define ILI9341_WRCTRLD 0x53 + +#define ILI9341_FRMCTR1 0xB1 +#define ILI9341_FRMCTR2 0xB2 +#define ILI9341_FRMCTR3 0xB3 +#define ILI9341_INVCTR 0xB4 +#define ILI9341_DFUNCTR 0xB6 + +#define ILI9341_PWCTR1 0xC0 +#define ILI9341_PWCTR2 0xC1 +#define ILI9341_PWCTR3 0xC2 +#define ILI9341_PWCTR4 0xC3 +#define ILI9341_PWCTR5 0xC4 +#define ILI9341_VMCTR1 0xC5 +#define ILI9341_VMCTR2 0xC7 + +#define ILI9341_RDID4 0xD3 +#define ILI9341_RDINDEX 0xD9 +#define ILI9341_RDID1 0xDA +#define ILI9341_RDID2 0xDB +#define ILI9341_RDID3 0xDC +#define ILI9341_RDIDX 0xDD // TBC + +#define ILI9341_GMCTRP1 0xE0 +#define ILI9341_GMCTRN1 0xE1 + +#define ILI9341_MADCTL_MY 0x80 +#define ILI9341_MADCTL_MX 0x40 +#define ILI9341_MADCTL_MV 0x20 +#define ILI9341_MADCTL_ML 0x10 +#define ILI9341_MADCTL_RGB 0x00 +#define ILI9341_MADCTL_BGR 0x08 +#define ILI9341_MADCTL_MH 0x04 diff --git a/lib/Seeed_GFX/TFT_Drivers/ILI9341_Init.h b/lib/Seeed_GFX/TFT_Drivers/ILI9341_Init.h new file mode 100644 index 0000000..05a5dda --- /dev/null +++ b/lib/Seeed_GFX/TFT_Drivers/ILI9341_Init.h @@ -0,0 +1,248 @@ + +// This is the command sequence that initialises the ILI9341 driver +// +// This setup information uses simple 8-bit SPI writecommand() and writedata() functions +// +// See ST7735_Setup.h file for an alternative format + +#if defined (ILI9341_DRIVER) || defined (ILI9342_DRIVER) +{ + writecommand(0xEF); + writedata(0x03); + writedata(0x80); + writedata(0x02); + + writecommand(0xCF); + writedata(0x00); + writedata(0XC1); + writedata(0X30); + + writecommand(0xED); + writedata(0x64); + writedata(0x03); + writedata(0X12); + writedata(0X81); + + writecommand(0xE8); + writedata(0x85); + writedata(0x00); + writedata(0x78); + + writecommand(0xCB); + writedata(0x39); + writedata(0x2C); + writedata(0x00); + writedata(0x34); + writedata(0x02); + + writecommand(0xF7); + writedata(0x20); + + writecommand(0xEA); + writedata(0x00); + writedata(0x00); + + writecommand(ILI9341_PWCTR1); //Power control + writedata(0x23); //VRH[5:0] + + writecommand(ILI9341_PWCTR2); //Power control + writedata(0x10); //SAP[2:0];BT[3:0] + + writecommand(ILI9341_VMCTR1); //VCM control + writedata(0x3e); + writedata(0x28); + + writecommand(ILI9341_VMCTR2); //VCM control2 + writedata(0x86); //-- + + writecommand(ILI9341_MADCTL); // Memory Access Control +#ifdef M5STACK + writedata(TFT_MAD_MY | TFT_MAD_MV | TFT_MAD_COLOR_ORDER); // Rotation 0 (portrait mode) +#else + writedata(TFT_MAD_MX | TFT_MAD_COLOR_ORDER); // Rotation 0 (portrait mode) +#endif + + writecommand(ILI9341_PIXFMT); + writedata(0x55); + + writecommand(ILI9341_FRMCTR1); + writedata(0x00); + writedata(0x13); // 0x18 79Hz, 0x1B default 70Hz, 0x13 100Hz + + writecommand(ILI9341_DFUNCTR); // Display Function Control + writedata(0x08); + writedata(0x82); + writedata(0x27); + + writecommand(0xF2); // 3Gamma Function Disable + writedata(0x00); + + writecommand(ILI9341_GAMMASET); //Gamma curve selected + writedata(0x01); + + writecommand(ILI9341_GMCTRP1); //Set Gamma + writedata(0x0F); + writedata(0x31); + writedata(0x2B); + writedata(0x0C); + writedata(0x0E); + writedata(0x08); + writedata(0x4E); + writedata(0xF1); + writedata(0x37); + writedata(0x07); + writedata(0x10); + writedata(0x03); + writedata(0x0E); + writedata(0x09); + writedata(0x00); + + writecommand(ILI9341_GMCTRN1); //Set Gamma + writedata(0x00); + writedata(0x0E); + writedata(0x14); + writedata(0x03); + writedata(0x11); + writedata(0x07); + writedata(0x31); + writedata(0xC1); + writedata(0x48); + writedata(0x08); + writedata(0x0F); + writedata(0x0C); + writedata(0x31); + writedata(0x36); + writedata(0x0F); + + writecommand(ILI9341_SLPOUT); //Exit Sleep + + end_tft_write(); + delay(120); + begin_tft_write(); + + writecommand(ILI9341_DISPON); //Display on + +} + +#elif defined (ILI9341_2_DRIVER) // Alternative init sequence, see https://github.com/Bodmer/TFT_eSPI/issues/1172 + +{ +writecommand(0xCF); +writedata(0x00); +writedata(0XC1); +writedata(0X30); + +writecommand(0xED); +writedata(0x64); +writedata(0x03); +writedata(0X12); +writedata(0X81); + +writecommand(0xE8); +writedata(0x85); +writedata(0x00); +writedata(0x78); + +writecommand(0xCB); +writedata(0x39); +writedata(0x2C); +writedata(0x00); +writedata(0x34); +writedata(0x02); + +writecommand(0xF7); +writedata(0x20); + +writecommand(0xEA); +writedata(0x00); +writedata(0x00); + +writecommand(ILI9341_PWCTR1); //Power control +writedata(0x10); //VRH[5:0] + +writecommand(ILI9341_PWCTR2); //Power control +writedata(0x00); //SAP[2:0];BT[3:0] + +writecommand(ILI9341_VMCTR1); //VCM control +writedata(0x30); +writedata(0x30); + +writecommand(ILI9341_VMCTR2); //VCM control2 +writedata(0xB7); //-- + +writecommand(ILI9341_PIXFMT); +writedata(0x55); + +writecommand(0x36); // Memory Access Control +writedata(0x08); // Rotation 0 (portrait mode) + +writecommand(ILI9341_FRMCTR1); +writedata(0x00); +writedata(0x1A); + +writecommand(ILI9341_DFUNCTR); // Display Function Control +writedata(0x08); +writedata(0x82); +writedata(0x27); + +writecommand(0xF2); // 3Gamma Function Disable +writedata(0x00); + +writecommand(0x26); //Gamma curve selected +writedata(0x01); + +writecommand(0xE0); //Set Gamma +writedata(0x0F); +writedata(0x2A); +writedata(0x28); +writedata(0x08); +writedata(0x0E); +writedata(0x08); +writedata(0x54); +writedata(0xA9); +writedata(0x43); +writedata(0x0A); +writedata(0x0F); +writedata(0x00); +writedata(0x00); +writedata(0x00); +writedata(0x00); + +writecommand(0XE1); //Set Gamma +writedata(0x00); +writedata(0x15); +writedata(0x17); +writedata(0x07); +writedata(0x11); +writedata(0x06); +writedata(0x2B); +writedata(0x56); +writedata(0x3C); +writedata(0x05); +writedata(0x10); +writedata(0x0F); +writedata(0x3F); +writedata(0x3F); +writedata(0x0F); + +writecommand(0x2B); +writedata(0x00); +writedata(0x00); +writedata(0x01); +writedata(0x3f); + +writecommand(0x2A); +writedata(0x00); +writedata(0x00); +writedata(0x00); +writedata(0xef); + +writecommand(ILI9341_SLPOUT); //Exit Sleep + +end_tft_write(); +delay(120); +begin_tft_write(); + +writecommand(ILI9341_DISPON); //Display on +} +#endif \ No newline at end of file diff --git a/lib/Seeed_GFX/TFT_Drivers/ILI9341_Rotation.h b/lib/Seeed_GFX/TFT_Drivers/ILI9341_Rotation.h new file mode 100644 index 0000000..f31b9df --- /dev/null +++ b/lib/Seeed_GFX/TFT_Drivers/ILI9341_Rotation.h @@ -0,0 +1,82 @@ + +// This is the command sequence that rotates the ILI9341 driver coordinate frame + + rotation = m % 8; // Limit the range of values to 0-7 + + writecommand(TFT_MADCTL); + switch (rotation) { + case 0: +#ifdef M5STACK + writedata(TFT_MAD_MY | TFT_MAD_MV | TFT_MAD_COLOR_ORDER); +#else + writedata(TFT_MAD_MX | TFT_MAD_COLOR_ORDER); +#endif + _width = _init_width; + _height = _init_height; + break; + case 1: +#ifdef M5STACK + writedata(TFT_MAD_COLOR_ORDER); +#else + writedata(TFT_MAD_MV | TFT_MAD_COLOR_ORDER); +#endif + _width = _init_height; + _height = _init_width; + break; + case 2: +#ifdef M5STACK + writedata(TFT_MAD_MV | TFT_MAD_MX | TFT_MAD_COLOR_ORDER); +#else + writedata(TFT_MAD_MY | TFT_MAD_COLOR_ORDER); +#endif + _width = _init_width; + _height = _init_height; + break; + case 3: +#ifdef M5STACK + writedata(TFT_MAD_MX | TFT_MAD_MY | TFT_MAD_COLOR_ORDER); +#else + writedata(TFT_MAD_MX | TFT_MAD_MY | TFT_MAD_MV | TFT_MAD_COLOR_ORDER); +#endif + _width = _init_height; + _height = _init_width; + break; + // These next rotations are for bottom up BMP drawing + case 4: +#ifdef M5STACK + writedata(TFT_MAD_MX | TFT_MAD_MY | TFT_MAD_MV | TFT_MAD_COLOR_ORDER); +#else + writedata(TFT_MAD_MX | TFT_MAD_MY | TFT_MAD_COLOR_ORDER); +#endif + _width = _init_width; + _height = _init_height; + break; + case 5: +#ifdef M5STACK + writedata(TFT_MAD_MY | TFT_MAD_COLOR_ORDER); +#else + writedata(TFT_MAD_MV | TFT_MAD_MX | TFT_MAD_COLOR_ORDER); +#endif + _width = _init_height; + _height = _init_width; + break; + case 6: +#ifdef M5STACK + writedata(TFT_MAD_MV | TFT_MAD_COLOR_ORDER); +#else + writedata(TFT_MAD_COLOR_ORDER); +#endif + _width = _init_width; + _height = _init_height; + break; + case 7: +#ifdef M5STACK + writedata(TFT_MAD_MX | TFT_MAD_COLOR_ORDER); +#else + writedata(TFT_MAD_MY | TFT_MAD_MV | TFT_MAD_COLOR_ORDER); +#endif + _width = _init_height; + _height = _init_width; + break; + + } diff --git a/lib/Seeed_GFX/TFT_Drivers/ILI9481_Defines.h b/lib/Seeed_GFX/TFT_Drivers/ILI9481_Defines.h new file mode 100644 index 0000000..bd5fb88 --- /dev/null +++ b/lib/Seeed_GFX/TFT_Drivers/ILI9481_Defines.h @@ -0,0 +1,42 @@ +// Change the width and height if required (defined in portrait mode) +// or use the constructor to over-ride defaults +#define TFT_WIDTH 320 +#define TFT_HEIGHT 480 + + +// Delay between some initialisation commands +#define TFT_INIT_DELAY 0x80 // Not used unless commandlist invoked + + +// Generic commands used by TFT_eSPI.cpp +#define TFT_NOP 0x00 +#define TFT_SWRST 0x01 + +#define TFT_SLPIN 0x10 +#define TFT_SLPOUT 0x11 + +#define TFT_INVOFF 0x20 +#define TFT_INVON 0x21 + +#define TFT_DISPOFF 0x28 +#define TFT_DISPON 0x29 + +#define TFT_CASET 0x2A +#define TFT_PASET 0x2B +#define TFT_RAMWR 0x2C + +#define TFT_RAMRD 0x2E + +#define TFT_MADCTL 0x36 + +#define TFT_MAD_MY 0x80 +#define TFT_MAD_MX 0x40 +#define TFT_MAD_MV 0x20 +#define TFT_MAD_ML 0x10 +#define TFT_MAD_RGB 0x00 +#define TFT_MAD_BGR 0x08 +#define TFT_MAD_MH 0x04 +#define TFT_MAD_SS 0x02 +#define TFT_MAD_GS 0x01 + +#define TFT_IDXRD 0x00 // ILI9341 only, indexed control register read diff --git a/lib/Seeed_GFX/TFT_Drivers/ILI9481_Init.h b/lib/Seeed_GFX/TFT_Drivers/ILI9481_Init.h new file mode 100644 index 0000000..a5f19b8 --- /dev/null +++ b/lib/Seeed_GFX/TFT_Drivers/ILI9481_Init.h @@ -0,0 +1,736 @@ + +// This is the command sequence that initialises the ILI9481 driver +// +// This setup information uses simple 8-bit SPI writecommand() and writedata() functions +// +// See ST7735_Setup.h file for an alternative format + +#define ILI9481_INIT_1 // Original default +//#define ILI9481_INIT_2 // CPT29 +//#define ILI9481_INIT_3 // PVI35 +//#define ILI9481_INIT_4 // AUO317 +//#define ILI9481_INIT_5 // CMO35 ***** +//#define ILI9481_INIT_6 // RGB +//#define ILI9481_INIT_7 // From #1774 +//#define ILI9481_INIT_8 // From #1774 + +///////////////////////////////////////////////////////////////////////////////////////// +#ifdef ILI9481_INIT_1 +// Configure ILI9481 display + + writecommand(TFT_SLPOUT); + delay(20); + + writecommand(0xD0); + writedata(0x07); + writedata(0x42); + writedata(0x18); + + writecommand(0xD1); + writedata(0x00); + writedata(0x07); + writedata(0x10); + + writecommand(0xD2); + writedata(0x01); + writedata(0x02); + + writecommand(0xC0); + writedata(0x10); + writedata(0x3B); + writedata(0x00); + writedata(0x02); + writedata(0x11); + + writecommand(0xC5); + writedata(0x03); + + writecommand(0xC8); + writedata(0x00); + writedata(0x32); + writedata(0x36); + writedata(0x45); + writedata(0x06); + writedata(0x16); + writedata(0x37); + writedata(0x75); + writedata(0x77); + writedata(0x54); + writedata(0x0C); + writedata(0x00); + + writecommand(TFT_MADCTL); + writedata(0x0A); + + writecommand(0x3A); + #if defined (TFT_PARALLEL_8_BIT) || defined (TFT_PARALLEL_16_BIT) || defined (RPI_DISPLAY_TYPE) + writedata(0x55); // 16-bit colour interface + #else + writedata(0x66); // 18-bit colour interface + #endif + + #if !defined (TFT_PARALLEL_8_BIT) && !defined (TFT_PARALLEL_16_BIT) + writecommand(TFT_INVON); + #endif + + writecommand(TFT_CASET); + writedata(0x00); + writedata(0x00); + writedata(0x01); + writedata(0x3F); + + writecommand(TFT_PASET); + writedata(0x00); + writedata(0x00); + writedata(0x01); + writedata(0xDF); + + delay(120); + writecommand(TFT_DISPON); + + delay(25); +// End of ILI9481 display configuration +///////////////////////////////////////////////////////////////////////////////////////// +#elif defined (ILI9481_INIT_2) +// Configure ILI9481 display + + writecommand(TFT_SLPOUT); + delay(20); + + writecommand(0xD0); + writedata(0x07); + writedata(0x41); + writedata(0x1D); + + writecommand(0xD1); + writedata(0x00); + writedata(0x2B); + writedata(0x1F); + + writecommand(0xD2); + writedata(0x01); + writedata(0x11); + + writecommand(0xC0); + writedata(0x10); + writedata(0x3B); + writedata(0x00); + writedata(0x02); + writedata(0x11); + + writecommand(0xC5); + writedata(0x03); + + writecommand(0xC8); + writedata(0x00); + writedata(0x14); + writedata(0x33); + writedata(0x10); + writedata(0x00); + writedata(0x16); + writedata(0x44); + writedata(0x36); + writedata(0x77); + writedata(0x00); + writedata(0x0F); + writedata(0x00); + + writecommand(0xB0); + writedata(0x00); + + writecommand(0xE4); + writedata(0xA0); + + writecommand(0xF0); + writedata(0x01); + + writecommand(0xF3); + writedata(0x02); + writedata(0x1A); + + writecommand(TFT_MADCTL); + writedata(0x0A); + + writecommand(0x3A); + #if defined (TFT_PARALLEL_8_BIT) || defined (TFT_PARALLEL_16_BIT) || defined (RPI_DISPLAY_TYPE) + writedata(0x55); // 16-bit colour interface + #else + writedata(0x66); // 18-bit colour interface + #endif + + #if !defined (TFT_PARALLEL_8_BIT) && !defined (TFT_PARALLEL_16_BIT) + writecommand(TFT_INVON); + #endif + + writecommand(TFT_CASET); + writedata(0x00); + writedata(0x00); + writedata(0x01); + writedata(0x3F); + + writecommand(TFT_PASET); + writedata(0x00); + writedata(0x00); + writedata(0x01); + writedata(0xDF); + + delay(120); + writecommand(TFT_DISPON); + + delay(25); +// End of ILI9481 display configuration +///////////////////////////////////////////////////////////////////////////////////////// +#elif defined (ILI9481_INIT_3) +// Configure ILI9481 display + + writecommand(TFT_SLPOUT); + delay(20); + + writecommand(0xD0); + writedata(0x07); + writedata(0x41); + writedata(0x1D); + + writecommand(0xD1); + writedata(0x00); + writedata(0x2B); + writedata(0x1F); + + writecommand(0xD2); + writedata(0x01); + writedata(0x11); + + writecommand(0xC0); + writedata(0x10); + writedata(0x3B); + writedata(0x00); + writedata(0x02); + writedata(0x11); + + writecommand(0xC5); + writedata(0x03); + + writecommand(0xC8); + writedata(0x00); + writedata(0x14); + writedata(0x33); + writedata(0x10); + writedata(0x00); + writedata(0x16); + writedata(0x44); + writedata(0x36); + writedata(0x77); + writedata(0x00); + writedata(0x0F); + writedata(0x00); + + writecommand(0xB0); + writedata(0x00); + + writecommand(0xE4); + writedata(0xA0); + + writecommand(0xF0); + writedata(0x01); + + writecommand(0xF3); + writedata(0x40); + writedata(0x0A); + + writecommand(TFT_MADCTL); + writedata(0x0A); + + writecommand(0x3A); + #if defined (TFT_PARALLEL_8_BIT) || defined (TFT_PARALLEL_16_BIT) || defined (RPI_DISPLAY_TYPE) + writedata(0x55); // 16-bit colour interface + #else + writedata(0x66); // 18-bit colour interface + #endif + + #if !defined (TFT_PARALLEL_8_BIT) && !defined (TFT_PARALLEL_16_BIT) + writecommand(TFT_INVON); + #endif + + writecommand(TFT_CASET); + writedata(0x00); + writedata(0x00); + writedata(0x01); + writedata(0x3F); + + writecommand(TFT_PASET); + writedata(0x00); + writedata(0x00); + writedata(0x01); + writedata(0xDF); + + delay(120); + writecommand(TFT_DISPON); + + delay(25); +// End of ILI9481 display configuration +///////////////////////////////////////////////////////////////////////////////////////// +#elif defined (ILI9481_INIT_4) +// Configure ILI9481 display + + writecommand(TFT_SLPOUT); + delay(20); + + writecommand(0xD0); + writedata(0x07); + writedata(0x40); + writedata(0x1D); + + writecommand(0xD1); + writedata(0x00); + writedata(0x18); + writedata(0x13); + + writecommand(0xD2); + writedata(0x01); + writedata(0x11); + + writecommand(0xC0); + writedata(0x10); + writedata(0x3B); + writedata(0x00); + writedata(0x02); + writedata(0x11); + + writecommand(0xC5); + writedata(0x03); + + writecommand(0xC8); + writedata(0x00); + writedata(0x44); + writedata(0x06); + writedata(0x44); + writedata(0x0A); + writedata(0x08); + writedata(0x17); + writedata(0x33); + writedata(0x77); + writedata(0x44); + writedata(0x08); + writedata(0x0C); + + writecommand(0xB0); + writedata(0x00); + + writecommand(0xE4); + writedata(0xA0); + + writecommand(0xF0); + writedata(0x01); + + writecommand(TFT_MADCTL); + writedata(0x0A); + + writecommand(0x3A); + #if defined (TFT_PARALLEL_8_BIT) || defined (TFT_PARALLEL_16_BIT) || defined (RPI_DISPLAY_TYPE) + writedata(0x55); // 16-bit colour interface + #else + writedata(0x66); // 18-bit colour interface + #endif + + #if !defined (TFT_PARALLEL_8_BIT) && !defined (TFT_PARALLEL_16_BIT) + writecommand(TFT_INVON); + #endif + + writecommand(TFT_CASET); + writedata(0x00); + writedata(0x00); + writedata(0x01); + writedata(0x3F); + + writecommand(TFT_PASET); + writedata(0x00); + writedata(0x00); + writedata(0x01); + writedata(0xDF); + + delay(120); + writecommand(TFT_DISPON); + + delay(25); +// End of ILI9481 display configuration +///////////////////////////////////////////////////////////////////////////////////////// +#elif defined (ILI9481_INIT_5) +// Configure ILI9481 display + + writecommand(TFT_SLPOUT); + delay(20); + + writecommand(0xD0); + writedata(0x07); + writedata(0x41); + writedata(0x1D); + + writecommand(0xD1); + writedata(0x00); + writedata(0x1C); + writedata(0x1F); + + writecommand(0xD2); + writedata(0x01); + writedata(0x11); + + writecommand(0xC0); + writedata(0x10); + writedata(0x3B); + writedata(0x00); + writedata(0x02); + writedata(0x11); + + writecommand(0xC5); + writedata(0x03); + + writecommand(0xC6); + writedata(0x83); + + writecommand(0xC8); + writedata(0x00); + writedata(0x26); + writedata(0x21); + writedata(0x00); + writedata(0x00); + writedata(0x1F); + writedata(0x65); + writedata(0x23); + writedata(0x77); + writedata(0x00); + writedata(0x0F); + writedata(0x00); + + writecommand(0xB0); + writedata(0x00); + + writecommand(0xE4); + writedata(0xA0); + + writecommand(0xF0); + writedata(0x01); + + writecommand(TFT_MADCTL); + writedata(0x0A); + + writecommand(0x3A); + #if defined (TFT_PARALLEL_8_BIT) || defined (TFT_PARALLEL_16_BIT) || defined (RPI_DISPLAY_TYPE) + writedata(0x55); // 16-bit colour interface + #else + writedata(0x66); // 18-bit colour interface + #endif + + #if !defined (TFT_PARALLEL_8_BIT) && !defined (TFT_PARALLEL_16_BIT) + writecommand(TFT_INVON); + #endif + + writecommand(TFT_CASET); + writedata(0x00); + writedata(0x00); + writedata(0x01); + writedata(0x3F); + + writecommand(TFT_PASET); + writedata(0x00); + writedata(0x00); + writedata(0x01); + writedata(0xDF); + + delay(120); + writecommand(TFT_DISPON); + + delay(25); +// End of ILI9481 display configuration +///////////////////////////////////////////////////////////////////////////////////////// +#elif defined (ILI9481_INIT_6) +// Configure ILI9481 display + + writecommand(TFT_SLPOUT); + delay(20); + + writecommand(0xD0); + writedata(0x07); + writedata(0x41); + writedata(0x1D); + + writecommand(0xD1); + writedata(0x00); + writedata(0x2B); + writedata(0x1F); + + writecommand(0xD2); + writedata(0x01); + writedata(0x11); + + writecommand(0xC0); + writedata(0x10); + writedata(0x3B); + writedata(0x00); + writedata(0x02); + writedata(0x11); + writedata(0x00); + + writecommand(0xC5); + writedata(0x03); + + writecommand(0xC6); + writedata(0x80); + + writecommand(0xC8); + writedata(0x00); + writedata(0x14); + writedata(0x33); + writedata(0x10); + writedata(0x00); + writedata(0x16); + writedata(0x44); + writedata(0x36); + writedata(0x77); + writedata(0x00); + writedata(0x0F); + writedata(0x00); + + writecommand(0xB0); + writedata(0x00); + + writecommand(0xE4); + writedata(0xA0); + + writecommand(0xF0); + writedata(0x08); + + writecommand(0xF3); + writedata(0x40); + writedata(0x0A); + + writecommand(0xF6); + writedata(0x84); + + writecommand(0xF7); + writedata(0x80); + + writecommand(0xB3); + writedata(0x00); + writedata(0x01); + writedata(0x06); + writedata(0x30); + + writecommand(0xB4); + writedata(0x00); + + writecommand(0x0C); + writedata(0x00); + writedata(0x55); + + writecommand(TFT_MADCTL); + writedata(0x0A); + + writecommand(0x3A); + #if defined (TFT_PARALLEL_8_BIT) || defined (TFT_PARALLEL_16_BIT) || defined (RPI_DISPLAY_TYPE) + writedata(0x55); // 16-bit colour interface + #else + writedata(0x66); // 18-bit colour interface + #endif + + #if !defined (TFT_PARALLEL_8_BIT) && !defined (TFT_PARALLEL_16_BIT) + writecommand(TFT_INVON); + #endif + + writecommand(TFT_CASET); + writedata(0x00); + writedata(0x00); + writedata(0x01); + writedata(0x3F); + + writecommand(TFT_PASET); + writedata(0x00); + writedata(0x00); + writedata(0x01); + writedata(0xDF); + + delay(120); + writecommand(TFT_DISPON); + + delay(25); +// End of ILI9481 display configuration +///////////////////////////////////////////////////////////////////////////////////////// + + +// From #1774 +#elif defined (ILI9481_INIT_7) + ///ili9481+cmi3.5ips //效果不好 + //************* Start Initial Sequence **********// + writecommand(0x11); + delay(20); + writecommand(0xD0); + writedata(0x07); + writedata(0x42); + writedata(0x1B); + + writecommand(0xD1); + writedata(0x00); + writedata(0x14); + writedata(0x1B); + + writecommand(0xD2); + writedata(0x01); + writedata(0x12); + + writecommand(0xC0); + writedata(0x10); + writedata(0x3B); + writedata(0x00); + writedata(0x02); + writedata(0x01); + + writecommand(0xC5); + writedata(0x03); + + writecommand(0xC8); + writedata(0x00); + writedata(0x46); + writedata(0x44); + writedata(0x50); + writedata(0x04); + writedata(0x16); + writedata(0x33); + writedata(0x13); + writedata(0x77); + writedata(0x05); + writedata(0x0F); + writedata(0x00); + + writecommand(0x36); + writedata(0x0A); + + writecommand(0x3A); + #if defined (TFT_PARALLEL_8_BIT) || defined (TFT_PARALLEL_16_BIT) || defined (RPI_DISPLAY_TYPE) + writedata(0x55); // 16-bit colour interface + #else + writedata(0x66); // 18-bit colour interface + #endif + + #if !defined (TFT_PARALLEL_8_BIT) && !defined (TFT_PARALLEL_16_BIT) + writecommand(TFT_INVON); + #endif + + writecommand(0x22); + writedata(0x00); + writedata(0x00); + writedata(0x01); + writedata(0x3F); + + writecommand(0x2B); + writedata(0x00); + writedata(0x00); + writedata(0x01); + writedata(0xE0); + delay(120); + writecommand(0x29); + +#elif defined (ILI9481_INIT_8) + + //3.5IPS ILI9481+CMI + writecommand(0x01); //Soft_rese + delay(220); + + writecommand(0x11); + delay(280); + + writecommand(0xd0); //Power_Setting + writedata(0x07);//07 VC[2:0] Sets the ratio factor of Vci to generate the reference voltages Vci1 + writedata(0x44);//41 BT[2:0] Sets the Step up factor and output voltage level from the reference voltages Vci1 + writedata(0x1E);//1f 17 1C VRH[3:0]: Sets the factor to generate VREG1OUT from VCILVL + delay(220); + + writecommand(0xd1); //VCOM Control + writedata(0x00);//00 + writedata(0x0C);//1A VCM [6:0] is used to set factor to generate VCOMH voltage from the reference voltage VREG1OUT 15 09 + writedata(0x1A);//1F VDV[4:0] is used to set the VCOM alternating amplitude in the range of VREG1OUT x 0.70 to VREG1OUT 1F 18 + + writecommand(0xC5); //Frame Rate + writedata(0x03); // 03 02 + + writecommand(0xd2); //Power_Setting for Normal Mode + writedata(0x01); //01 + writedata(0x11); //11 + + writecommand(0xE4); //? + writedata(0xa0); + writecommand(0xf3); + writedata(0x00); + writedata(0x2a); + + //1 OK + writecommand(0xc8); + writedata(0x00); + writedata(0x26); + writedata(0x21); + writedata(0x00); + writedata(0x00); + writedata(0x1f); + writedata(0x65); + writedata(0x23); + writedata(0x77); + writedata(0x00); + writedata(0x0f); + writedata(0x00); + //GAMMA SETTING + + writecommand(0xC0); //Panel Driving Setting + writedata(0x00); //1//00 REV SM GS + writedata(0x3B); //2//NL[5:0]: Sets the number of lines to drive the LCD at an interval of 8 lines. + writedata(0x00); //3//SCN[6:0] + writedata(0x02); //4//PTV: Sets the Vcom output in non-display area drive period + writedata(0x11); //5//NDL: Sets the source output level in non-display area. PTG: Sets the scan mode in non-display area. + + writecommand(0xc6); //Interface Control + writedata(0x83); + //GAMMA SETTING + + writecommand(0xf0); //? + writedata(0x01); + + writecommand(0xE4);//? + writedata(0xa0); + + //////倒装设置 NG + writecommand(0x36); + writedata(0x0A); // 8C:出来两行 CA:出来一个点 + + writecommand(0x3a); + #if defined (TFT_PARALLEL_8_BIT) || defined (TFT_PARALLEL_16_BIT) || defined (RPI_DISPLAY_TYPE) + writedata(0x55); // 16-bit colour interface + #else + writedata(0x66); // 18-bit colour interface + #endif + + #if defined (TFT_PARALLEL_8_BIT) || defined (TFT_PARALLEL_16_BIT) + writecommand(TFT_INVON); + #endif + + writecommand(0xb4);//Display Mode and Frame Memory Write Mode Setting + writedata(0x02); + writedata(0x00); //? + writedata(0x00); + writedata(0x01); + + delay(280); + + writecommand(0x2a); + writedata(0x00); + writedata(0x00); + writedata(0x01); + writedata(0x3F); //3F + + writecommand(0x2b); + writedata(0x00); + writedata(0x00); + writedata(0x01); + writedata(0xDf); //DF + + //writecommand(0x21); + writecommand(0x29); + writecommand(0x2c); + +#endif \ No newline at end of file diff --git a/lib/Seeed_GFX/TFT_Drivers/ILI9481_Rotation.h b/lib/Seeed_GFX/TFT_Drivers/ILI9481_Rotation.h new file mode 100644 index 0000000..e80d08e --- /dev/null +++ b/lib/Seeed_GFX/TFT_Drivers/ILI9481_Rotation.h @@ -0,0 +1,27 @@ + // This is the command sequence that rotates the ILI9481 driver coordinate frame + + writecommand(TFT_MADCTL); + rotation = m % 4; + switch (rotation) { + case 0: // Portrait + writedata(TFT_MAD_BGR | TFT_MAD_SS); + _width = TFT_WIDTH; + _height = TFT_HEIGHT; + break; + case 1: // Landscape (Portrait + 90) + writedata(TFT_MAD_MV | TFT_MAD_BGR); + _width = TFT_HEIGHT; + _height = TFT_WIDTH; + break; + case 2: // Inverter portrait + writedata(TFT_MAD_BGR | TFT_MAD_GS); + _width = TFT_WIDTH; + _height = TFT_HEIGHT; + break; + case 3: // Inverted landscape + writedata(TFT_MAD_MV | TFT_MAD_BGR | TFT_MAD_SS | TFT_MAD_GS); + _width = TFT_HEIGHT; + _height = TFT_WIDTH; + break; + } + \ No newline at end of file diff --git a/lib/Seeed_GFX/TFT_Drivers/ILI9486_Defines.h b/lib/Seeed_GFX/TFT_Drivers/ILI9486_Defines.h new file mode 100644 index 0000000..fcec0b8 --- /dev/null +++ b/lib/Seeed_GFX/TFT_Drivers/ILI9486_Defines.h @@ -0,0 +1,64 @@ +// Change the width and height if required (defined in portrait mode) +// or use the constructor to over-ride defaults +#define TFT_WIDTH 320 +#define TFT_HEIGHT 480 + +// For Raspberry Pi ILI9486 only with a modified board to add a write strobe: +#if defined (TFT_WR) && defined (RPI_ILI9486_DRIVER) + #define RPI_WRITE_STROBE +#endif + +// Color definitions for backwards compatibility with old sketches +// use colour definitions like TFT_BLACK to make sketches more portable +#define ILI9486_BLACK 0x0000 /* 0, 0, 0 */ +#define ILI9486_NAVY 0x000F /* 0, 0, 128 */ +#define ILI9486_DARKGREEN 0x03E0 /* 0, 128, 0 */ +#define ILI9486_DARKCYAN 0x03EF /* 0, 128, 128 */ +#define ILI9486_MAROON 0x7800 /* 128, 0, 0 */ +#define ILI9486_PURPLE 0x780F /* 128, 0, 128 */ +#define ILI9486_OLIVE 0x7BE0 /* 128, 128, 0 */ +#define ILI9486_LIGHTGREY 0xC618 /* 192, 192, 192 */ +#define ILI9486_DARKGREY 0x7BEF /* 128, 128, 128 */ +#define ILI9486_BLUE 0x001F /* 0, 0, 255 */ +#define ILI9486_GREEN 0x07E0 /* 0, 255, 0 */ +#define ILI9486_CYAN 0x07FF /* 0, 255, 255 */ +#define ILI9486_RED 0xF800 /* 255, 0, 0 */ +#define ILI9486_MAGENTA 0xF81F /* 255, 0, 255 */ +#define ILI9486_YELLOW 0xFFE0 /* 255, 255, 0 */ +#define ILI9486_WHITE 0xFFFF /* 255, 255, 255 */ +#define ILI9486_ORANGE 0xFD20 /* 255, 165, 0 */ +#define ILI9486_GREENYELLOW 0xAFE5 /* 173, 255, 47 */ +#define ILI9486_PINK 0xF81F + + +// Delay between some initialisation commands +#define TFT_INIT_DELAY 0x80 // Not used unless commandlist invoked + + +// Generic commands used by TFT_eSPI.cpp +#define TFT_NOP 0x00 +#define TFT_SWRST 0x01 + +#define TFT_INVOFF 0x20 +#define TFT_INVON 0x21 + +#define TFT_DISPOFF 0x28 +#define TFT_DISPON 0x29 + +#define TFT_CASET 0x2A +#define TFT_PASET 0x2B +#define TFT_RAMWR 0x2C + +#define TFT_RAMRD 0x2E +#define TFT_IDXRD 0x00 // ILI9341 only, indexed control register read + +#define TFT_MADCTL 0x36 +#define TFT_MAD_MY 0x80 +#define TFT_MAD_MX 0x40 +#define TFT_MAD_MV 0x20 +#define TFT_MAD_ML 0x10 +#define TFT_MAD_BGR 0x08 +#define TFT_MAD_MH 0x04 +#define TFT_MAD_SS 0x02 +#define TFT_MAD_GS 0x01 +#define TFT_MAD_RGB 0x00 diff --git a/lib/Seeed_GFX/TFT_Drivers/ILI9486_Init.h b/lib/Seeed_GFX/TFT_Drivers/ILI9486_Init.h new file mode 100644 index 0000000..db0f7aa --- /dev/null +++ b/lib/Seeed_GFX/TFT_Drivers/ILI9486_Init.h @@ -0,0 +1,84 @@ + +// This is the command sequence that initialises the ILI9486 driver +// +// This setup information uses simple 8-bit SPI writecommand() and writedata() functions +// +// See ST7735_Setup.h file for an alternative format + +{ +// From https://github.com/notro/fbtft/blob/master/fb_ili9486.c + + writecommand(0x01); // SW reset + delay(120); + + writecommand(0x11); // Sleep out, also SW reset + delay(120); + + writecommand(0x3A); + #if defined (TFT_PARALLEL_8_BIT) || defined (TFT_PARALLEL_16_BIT) || defined (RPI_DISPLAY_TYPE) + writedata(0x55); // 16-bit colour interface + #else + writedata(0x66); // 18-bit colour interface + #endif + + writecommand(0xC0); // 1100.0000 Power Control 1 + writedata(0x0E); // 0001.0111 ... VRH1 + writedata(0x0E); // 0001.0101 ... VRH2 + writecommand(0xC1); // 1100.0001 Power Control 2 + writedata(0x41); // 0100.0001 . SAP BT + writedata(0x00); // 0000.0000 ..... VC + writecommand(0xC2); // 1100.0010 Power Control 3 + writedata(0x55); // nb. was 0x44 0101.0101 . DCA1 . DCA0 + + writecommand(0xC5); + writedata(0x00); + writedata(0x00); + writedata(0x00); + writedata(0x00); + + writecommand(0xE0); + writedata(0x0F); + writedata(0x1F); + writedata(0x1C); + writedata(0x0C); + writedata(0x0F); + writedata(0x08); + writedata(0x48); + writedata(0x98); + writedata(0x37); + writedata(0x0A); + writedata(0x13); + writedata(0x04); + writedata(0x11); + writedata(0x0D); + writedata(0x00); + + writecommand(0xE1); + writedata(0x0F); + writedata(0x32); + writedata(0x2E); + writedata(0x0B); + writedata(0x0D); + writedata(0x05); + writedata(0x47); + writedata(0x75); + writedata(0x37); + writedata(0x06); + writedata(0x10); + writedata(0x03); + writedata(0x24); + writedata(0x20); + writedata(0x00); + + #if defined (TFT_PARALLEL_8_BIT) || defined (TFT_PARALLEL_16_BIT) || defined (RPI_DISPLAY_TYPE) + writecommand(TFT_INVOFF); + #else + writecommand(TFT_INVON); + #endif + + writecommand(0x36); + writedata(0x48); + + writecommand(0x29); // display on + delay(150); +} diff --git a/lib/Seeed_GFX/TFT_Drivers/ILI9486_Rotation.h b/lib/Seeed_GFX/TFT_Drivers/ILI9486_Rotation.h new file mode 100644 index 0000000..495d675 --- /dev/null +++ b/lib/Seeed_GFX/TFT_Drivers/ILI9486_Rotation.h @@ -0,0 +1,47 @@ + // This is the command sequence that rotates the ILI9486 driver coordinate frame + + writecommand(TFT_MADCTL); + rotation = m % 8; + switch (rotation) { + case 0: // Portrait + writedata(TFT_MAD_BGR | TFT_MAD_MX); + _width = _init_width; + _height = _init_height; + break; + case 1: // Landscape (Portrait + 90) + writedata(TFT_MAD_BGR | TFT_MAD_MV); + _width = _init_height; + _height = _init_width; + break; + case 2: // Inverter portrait + writedata( TFT_MAD_BGR | TFT_MAD_MY); + _width = _init_width; + _height = _init_height; + break; + case 3: // Inverted landscape + writedata(TFT_MAD_BGR | TFT_MAD_MV | TFT_MAD_MX | TFT_MAD_MY); + _width = _init_height; + _height = _init_width; + break; + case 4: // Portrait + writedata(TFT_MAD_BGR | TFT_MAD_MX | TFT_MAD_MY); + _width = _init_width; + _height = _init_height; + break; + case 5: // Landscape (Portrait + 90) + writedata(TFT_MAD_BGR | TFT_MAD_MV | TFT_MAD_MX); + _width = _init_height; + _height = _init_width; + break; + case 6: // Inverter portrait + writedata( TFT_MAD_BGR); + _width = _init_width; + _height = _init_height; + break; + case 7: // Inverted landscape + writedata(TFT_MAD_BGR | TFT_MAD_MV | TFT_MAD_MY); + _width = _init_height; + _height = _init_width; + break; + } + diff --git a/lib/Seeed_GFX/TFT_Drivers/ILI9488_Defines.h b/lib/Seeed_GFX/TFT_Drivers/ILI9488_Defines.h new file mode 100644 index 0000000..bd5fb88 --- /dev/null +++ b/lib/Seeed_GFX/TFT_Drivers/ILI9488_Defines.h @@ -0,0 +1,42 @@ +// Change the width and height if required (defined in portrait mode) +// or use the constructor to over-ride defaults +#define TFT_WIDTH 320 +#define TFT_HEIGHT 480 + + +// Delay between some initialisation commands +#define TFT_INIT_DELAY 0x80 // Not used unless commandlist invoked + + +// Generic commands used by TFT_eSPI.cpp +#define TFT_NOP 0x00 +#define TFT_SWRST 0x01 + +#define TFT_SLPIN 0x10 +#define TFT_SLPOUT 0x11 + +#define TFT_INVOFF 0x20 +#define TFT_INVON 0x21 + +#define TFT_DISPOFF 0x28 +#define TFT_DISPON 0x29 + +#define TFT_CASET 0x2A +#define TFT_PASET 0x2B +#define TFT_RAMWR 0x2C + +#define TFT_RAMRD 0x2E + +#define TFT_MADCTL 0x36 + +#define TFT_MAD_MY 0x80 +#define TFT_MAD_MX 0x40 +#define TFT_MAD_MV 0x20 +#define TFT_MAD_ML 0x10 +#define TFT_MAD_RGB 0x00 +#define TFT_MAD_BGR 0x08 +#define TFT_MAD_MH 0x04 +#define TFT_MAD_SS 0x02 +#define TFT_MAD_GS 0x01 + +#define TFT_IDXRD 0x00 // ILI9341 only, indexed control register read diff --git a/lib/Seeed_GFX/TFT_Drivers/ILI9488_Init.h b/lib/Seeed_GFX/TFT_Drivers/ILI9488_Init.h new file mode 100644 index 0000000..c1b20b6 --- /dev/null +++ b/lib/Seeed_GFX/TFT_Drivers/ILI9488_Init.h @@ -0,0 +1,99 @@ + +// This is the command sequence that initialises the ILI9488 driver +// +// This setup information uses simple 8-bit SPI writecommand() and writedata() functions +// +// See ST7735_Setup.h file for an alternative format + + +// Configure ILI9488 display + + writecommand(0xE0); // Positive Gamma Control + writedata(0x00); + writedata(0x03); + writedata(0x09); + writedata(0x08); + writedata(0x16); + writedata(0x0A); + writedata(0x3F); + writedata(0x78); + writedata(0x4C); + writedata(0x09); + writedata(0x0A); + writedata(0x08); + writedata(0x16); + writedata(0x1A); + writedata(0x0F); + + writecommand(0XE1); // Negative Gamma Control + writedata(0x00); + writedata(0x16); + writedata(0x19); + writedata(0x03); + writedata(0x0F); + writedata(0x05); + writedata(0x32); + writedata(0x45); + writedata(0x46); + writedata(0x04); + writedata(0x0E); + writedata(0x0D); + writedata(0x35); + writedata(0x37); + writedata(0x0F); + + writecommand(0XC0); // Power Control 1 + writedata(0x17); + writedata(0x15); + + writecommand(0xC1); // Power Control 2 + writedata(0x41); + + writecommand(0xC5); // VCOM Control + writedata(0x00); + writedata(0x12); + writedata(0x80); + + writecommand(TFT_MADCTL); // Memory Access Control + writedata(0x48); // MX, BGR + + writecommand(0x3A); // Pixel Interface Format +#if defined (TFT_PARALLEL_8_BIT) || defined (TFT_PARALLEL_16_BIT) || defined (RPI_DISPLAY_TYPE) + writedata(0x55); // 16-bit colour for parallel +#else + writedata(0x66); // 18-bit colour for SPI +#endif + + writecommand(0xB0); // Interface Mode Control + writedata(0x00); + + writecommand(0xB1); // Frame Rate Control + writedata(0xA0); + + writecommand(0xB4); // Display Inversion Control + writedata(0x02); + + writecommand(0xB6); // Display Function Control + writedata(0x02); + writedata(0x02); + writedata(0x3B); + + writecommand(0xB7); // Entry Mode Set + writedata(0xC6); + + writecommand(0xF7); // Adjust Control 3 + writedata(0xA9); + writedata(0x51); + writedata(0x2C); + writedata(0x82); + + writecommand(TFT_SLPOUT); //Exit Sleep +delay(120); + + writecommand(TFT_DISPON); //Display on +delay(25); + +// End of ILI9488 display configuration + + + diff --git a/lib/Seeed_GFX/TFT_Drivers/ILI9488_Rotation.h b/lib/Seeed_GFX/TFT_Drivers/ILI9488_Rotation.h new file mode 100644 index 0000000..6ab17bd --- /dev/null +++ b/lib/Seeed_GFX/TFT_Drivers/ILI9488_Rotation.h @@ -0,0 +1,27 @@ + // This is the command sequence that rotates the ILI9488 driver coordinate frame + + writecommand(TFT_MADCTL); + rotation = m % 4; + switch (rotation) { + case 0: // Portrait + writedata(TFT_MAD_MX | TFT_MAD_BGR); + _width = TFT_WIDTH; + _height = TFT_HEIGHT; + break; + case 1: // Landscape (Portrait + 90) + writedata(TFT_MAD_MV | TFT_MAD_BGR); + _width = TFT_HEIGHT; + _height = TFT_WIDTH; + break; + case 2: // Inverter portrait + writedata(TFT_MAD_MY | TFT_MAD_BGR); + _width = TFT_WIDTH; + _height = TFT_HEIGHT; + break; + case 3: // Inverted landscape + writedata(TFT_MAD_MX | TFT_MAD_MY | TFT_MAD_MV | TFT_MAD_BGR); + _width = TFT_HEIGHT; + _height = TFT_WIDTH; + break; + } + \ No newline at end of file diff --git a/lib/Seeed_GFX/TFT_Drivers/IT8951_Defines.h b/lib/Seeed_GFX/TFT_Drivers/IT8951_Defines.h new file mode 100644 index 0000000..5cf3a43 --- /dev/null +++ b/lib/Seeed_GFX/TFT_Drivers/IT8951_Defines.h @@ -0,0 +1,90 @@ +typedef uint8_t TByte; //uint8_t +typedef uint16_t TWord; //uint16_t +typedef uint32_t TDWord;//uint32_t + +//Built in I80 Command Code +#define IT8951_TCON_SYS_RUN 0x0001 +#define IT8951_TCON_STANDBY 0x0002 +#define IT8951_TCON_SLEEP 0x0003 +#define IT8951_TCON_REG_RD 0x0010 +#define IT8951_TCON_REG_WR 0x0011 +#define IT8951_TCON_MEM_BST_RD_T 0x0012 +#define IT8951_TCON_MEM_BST_RD_S 0x0013 +#define IT8951_TCON_MEM_BST_WR 0x0014 +#define IT8951_TCON_MEM_BST_END 0x0015 +#define IT8951_TCON_LD_IMG 0x0020 +#define IT8951_TCON_LD_IMG_AREA 0x0021 +#define IT8951_TCON_LD_IMG_END 0x0022 + +//I80 User defined command code +#define USDEF_I80_CMD_DPY_AREA 0x0034 +#define USDEF_I80_CMD_GET_DEV_INFO 0x0302 +#define USDEF_I80_CMD_DPY_BUF_AREA 0x0037 +//Panel +#define IT8951_PANEL_WIDTH 1872 //it Get Device information +#define IT8951_PANEL_HEIGHT 1404 + +//Rotate mode +#define IT8951_ROTATE_0 0 +#define IT8951_ROTATE_90 1 +#define IT8951_ROTATE_180 2 +#define IT8951_ROTATE_270 3 + +//Pixel mode , BPP - Bit per Pixel +#define IT8951_2BPP 0 +#define IT8951_3BPP 1 +#define IT8951_4BPP 2 +#define IT8951_8BPP 3 + +//Waveform Mode +#define IT8951_MODE_0 0 +#define IT8951_MODE_1 1 +#define IT8951_MODE_2 2 +#define IT8951_MODE_3 3 +#define IT8951_MODE_4 4 + +//Endian Type +#define IT8951_LDIMG_L_ENDIAN 0 +#define IT8951_LDIMG_B_ENDIAN 1 + +//Auto LUT +#define IT8951_DIS_AUTO_LUT 0 +#define IT8951_EN_AUTO_LUT 1 + +//LUT Engine Status +#define IT8951_ALL_LUTE_BUSY 0xFFFF + +//----------------------------------------------------------------------- +// IT8951 TCon Registers defines +//----------------------------------------------------------------------- +//Register Base Address +#define DISPLAY_REG_BASE 0x1000 //Register RW access for I80 only + +//Base Address of Basic LUT Registers +#define LUT0EWHR (DISPLAY_REG_BASE + 0x00) //LUT0 Engine Width Height Reg +#define LUT0XYR (DISPLAY_REG_BASE + 0x40) //LUT0 XY Reg +#define LUT0BADDR (DISPLAY_REG_BASE + 0x80) //LUT0 Base Address Reg +#define LUT0MFN (DISPLAY_REG_BASE + 0xC0) //LUT0 Mode and Frame number Reg +#define LUT01AF (DISPLAY_REG_BASE + 0x114) //LUT0 and LUT1 Active Flag Reg + +//Update Parameter Setting Register +#define UP0SR (DISPLAY_REG_BASE + 0x134) //Update Parameter0 Setting Reg + +#define UP1SR (DISPLAY_REG_BASE + 0x138) //Update Parameter1 Setting Reg +#define LUT0ABFRV (DISPLAY_REG_BASE + 0x13C) //LUT0 Alpha blend and Fill rectangle Value +#define UPBBADDR (DISPLAY_REG_BASE + 0x17C) //Update Buffer Base Address +#define LUT0IMXY (DISPLAY_REG_BASE + 0x180) //LUT0 Image buffer X/Y offset Reg +#define LUTAFSR (DISPLAY_REG_BASE + 0x224) //LUT Status Reg (status of All LUT Engines) + +#define BGVR (DISPLAY_REG_BASE + 0x250) //Bitmap (1bpp) image color table + +//-------System Registers---------------- +#define SYS_REG_BASE 0x0000 + +//Address of System Registers +#define I80CPCR (SYS_REG_BASE + 0x04) + +//-------Memory Converter Registers---------------- +#define MCSR_BASE_ADDR 0x0200 +#define MCSR (MCSR_BASE_ADDR + 0x0000) +#define LISAR (MCSR_BASE_ADDR + 0x0008) \ No newline at end of file diff --git a/lib/Seeed_GFX/TFT_Drivers/JD79660_Defines.h b/lib/Seeed_GFX/TFT_Drivers/JD79660_Defines.h new file mode 100644 index 0000000..05ba770 --- /dev/null +++ b/lib/Seeed_GFX/TFT_Drivers/JD79660_Defines.h @@ -0,0 +1,190 @@ +#ifndef EPD_WIDTH +#define EPD_WIDTH 200 +#endif + +#ifndef EPD_HEIGHT +#define EPD_HEIGHT 200 +#endif + +#ifndef TFT_WIDTH +#define TFT_WIDTH EPD_WIDTH +#endif + +#ifndef TFT_HEIGHT +#define TFT_HEIGHT EPD_HEIGHT +#endif + +#define EPD_COLOR_DEPTH 4 + + + +#define EPD_NOP 0xFF // No operation command (not supported) +#define EPD_PNLSET 0x00 // Panel setting (R00H PSR) +#define EPD_DISPON 0x04 // Power on (R04H PON) +#define EPD_DISPOFF 0x02 // Power off (R02H POF) +#define EPD_SLPIN 0x07 // Enter deep sleep (R07H DSLP) +#define EPD_SLPOUT 0xFF // Exit sleep (not supported, requires wake-up) +#define EPD_PTLIN 0x91 // Partial display in (R91H PTIN) +#define EPD_PTLOUT 0x92 // Partial display out (R92H PTOUT) +#define EPD_PTLW 0x90 // Partial display window setting (R90H PTL) + +#define TFT_SWRST 0xFF // Software reset (not supported) +#define TFT_CASET 0xFF // Column address setting (not supported) +#define TFT_PASET 0xFF // Page address setting (not supported) +#define TFT_RAMWR 0x13 // Write RAM (R13H DTM2, red data) +#define TFT_RAMRD 0xFF // Read RAM (not supported) +#define TFT_INVON 0xFF // Display inversion on (not supported) +#define TFT_INVOFF 0xFF // Display inversion off (not supported) +#define TFT_INIT_DELAY 0 // Initialization delay (none) + +#ifndef JD79660_BUSY_TIMEOUT_MS +#define JD79660_BUSY_TIMEOUT_MS 21000 +#endif + +#ifdef TFT_BUSY +#define CHECK_BUSY() \ + do \ + { \ + uint32_t __start = millis(); \ + while (digitalRead(TFT_BUSY) == LOW) \ + { \ + if(millis() - __start > JD79660_BUSY_TIMEOUT_MS) \ + { \ + break; \ + } \ + delay(1); \ + } \ + } while (0) +#else +#define CHECK_BUSY() +#endif + +#if defined(TFT_ENABLE) + #define EPD_INIT() \ + do { \ + pinMode(TFT_ENABLE, OUTPUT); \ + digitalWrite(TFT_ENABLE, HIGH); \ + writecommand(0x4D); writedata(0x78); \ + writecommand(0x00); writedata(0x0F); writedata(0x29); \ + writecommand(0x06); writedata(0x0D); writedata(0x12); writedata(0x30); writedata(0x20); writedata(0x19); writedata(0x2A); writedata(0x22); \ + writecommand(0x50); writedata(0x37); \ + writecommand(0x30); writedata(0x08); \ + writecommand(0x61); writedata(200/256); writedata(200%256); writedata(200/256); writedata(200%256); \ + writecommand(0xE9); writedata(0x01); \ + writecommand(0x04); \ + delay(100); \ + } while(0) +#else + #define EPD_INIT() \ + do { \ + writecommand(0x4D); writedata(0x78); \ + writecommand(0x00); writedata(0x0F); writedata(0x29); \ + writecommand(0x06); writedata(0x0D); writedata(0x12); writedata(0x30); writedata(0x20); writedata(0x19); writedata(0x2A); writedata(0x22); \ + writecommand(0x50); writedata(0x37); \ + writecommand(0x30); writedata(0x08); \ + writecommand(0x61); writedata(200/256); writedata(200%256); writedata(200/256); writedata(200%256); \ + writecommand(0xE9); writedata(0x01); \ + writecommand(0x04); \ + delay(100); \ + } while(0) +#endif + +#define EPD_UPDATE() \ + do \ + { \ + writecommand(0x12); \ + writedata(0x00); \ + CHECK_BUSY(); \ + } while (0) + +#define EPD_SLEEP() \ + do \ + { \ + writecommand(0x02); \ + writedata(0x00); \ + delay(100); \ + writecommand(0x07); \ + writedata(0xA5); \ + } while (0) + +#define EPD_WAKEUP() \ + do \ + { \ + digitalWrite(TFT_RST, LOW); \ + delay(50); \ + digitalWrite(TFT_RST, HIGH); \ + delay(100); \ + EPD_INIT(); \ + } while (0) + +#define EPD_SET_WINDOW(x1, y1, x2, y2) \ + do \ + { \ + } while (0) + +#define COLOR_GET(color) ( \ + (color) == 0x00 ? 0x01 : \ + (color) == 0x0B ? 0x02 : \ + (color) == 0x06 ? 0x03 : \ + (color) == 0x0F ? 0x00 : \ + 0x00 \ +) + +#define EPD_PUSH_NEW_COLORS(w, h, colors) \ + do \ + { \ + uint16_t bytes_per_row = (w) / 2; \ + uint8_t temp1, temp2, temp3, temp4; \ + writecommand(0x10); \ + for (uint16_t row = 0; row < (h) ; row++) \ + { \ + for(uint16_t col = 0; col < bytes_per_row; col+=2) \ + { \ + uint8_t b = (colors[bytes_per_row *row+col ]) ; \ + uint8_t c = (colors[bytes_per_row *row+col + 1]) ; \ + temp1 = (b >> 4) & 0x0F;\ + temp2 = b & 0x0F;\ + temp3 = (c >> 4) & 0x0F;\ + temp4 = c & 0x0F;\ + writedata(((COLOR_GET(temp1) <<6)|( COLOR_GET(temp2) << 4 ) |( COLOR_GET(temp3) << 2 ) |( COLOR_GET(temp4) << 0 )));\ + } \ + } \ + } while (0) + +#define EPD_PUSH_NEW_COLORS_FLIP(w, h, colors) \ + do \ + { \ + uint16_t bytes_per_row = (w) / 2; \ + uint8_t temp1, temp2, temp3, temp4; \ + writecommand(0x10); \ + for (uint16_t row = 0; row < (h); row++) \ + { \ + uint16_t start = row * bytes_per_row; \ + for (uint16_t col = 0; col < bytes_per_row; col+=2) \ + { \ + uint8_t b = (colors[bytes_per_row *row + (bytes_per_row - 1 - col)]) ;\ + uint8_t c = (colors[bytes_per_row *row + (bytes_per_row - 1 - col) - 1]) ;\ + temp1 = (b >> 4) & 0x0F;\ + temp2 = b & 0x0F;\ + temp3 = (c >> 4) & 0x0F;\ + temp4 = c & 0x0F;\ + writedata(((COLOR_GET(temp1) <<6)|( COLOR_GET(temp2) << 4 ) |( COLOR_GET(temp3) << 2 ) |( COLOR_GET(temp4) << 0 )));\ + } \ + } \ + } while (0) + +#define EPD_PUSH_OLD_COLORS(w, h, colors) \ + do \ + { \ + } while (0) + +#define EPD_PUSH_OLD_COLORS_FLIP(w, h, colors) \ + do \ + { \ + \ + } while (0) + +#define EPD_SET_TEMP(temp) \ + do \ + { \ + } while (0) diff --git a/lib/Seeed_GFX/TFT_Drivers/JD79660_Init.h b/lib/Seeed_GFX/TFT_Drivers/JD79660_Init.h new file mode 100644 index 0000000..36fa128 --- /dev/null +++ b/lib/Seeed_GFX/TFT_Drivers/JD79660_Init.h @@ -0,0 +1,47 @@ +// JD79660_Init.h +{ +#ifdef TFT_BUSY + pinMode(TFT_BUSY, INPUT); +#endif +#ifdef TFT_ENABLE + pinMode(TFT_ENABLE, OUTPUT); + digitalWrite(TFT_ENABLE, HIGH); +#endif + digitalWrite(TFT_RST, LOW); + delay(50); + digitalWrite(TFT_RST, HIGH); + delay(100); + writecommand(0x4D); + writedata(0x78); + + writecommand(0x00); //PSR + writedata(0x0F); + writedata(0x29); + + writecommand(0X06); //BTST_P + writedata(0x0D); //47uH + writedata(0x12); + writedata(0x30); + writedata(0x20); + writedata(0x19); + writedata(0x2A); + writedata(0x22); + + writecommand(0x50); //CDI + writedata(0x37); + + writecommand(0x61); //TRES + writedata(TFT_WIDTH/256); // Source_BITS_H + writedata(TFT_WIDTH%256); // Source_BITS_L + writedata(TFT_HEIGHT/256); // Gate_BITS_H + writedata(TFT_HEIGHT%256); // Gate_BITS_L + + writecommand(0xE9); + writedata(0x01); + + writecommand(0x30); + writedata(0x08); + + writecommand(0x04); + CHECK_BUSY(); +} diff --git a/lib/Seeed_GFX/TFT_Drivers/JD79660_Rotation.h b/lib/Seeed_GFX/TFT_Drivers/JD79660_Rotation.h new file mode 100644 index 0000000..e69de29 diff --git a/lib/Seeed_GFX/TFT_Drivers/JD79667_Defines.h b/lib/Seeed_GFX/TFT_Drivers/JD79667_Defines.h new file mode 100644 index 0000000..68d7dbb --- /dev/null +++ b/lib/Seeed_GFX/TFT_Drivers/JD79667_Defines.h @@ -0,0 +1,199 @@ +#ifndef EPD_WIDTH +#define EPD_WIDTH 128 +#endif + +#ifndef EPD_HEIGHT +#define EPD_HEIGHT 296 +#endif + +#ifndef TFT_WIDTH +#define TFT_WIDTH EPD_WIDTH +#endif + +#ifndef TFT_HEIGHT +#define TFT_HEIGHT EPD_HEIGHT +#endif + +#define EPD_COLOR_DEPTH 4 + + + +#define EPD_NOP 0xFF // No operation command (not supported) +#define EPD_PNLSET 0x00 // Panel setting (R00H PSR) +#define EPD_DISPON 0x04 // Power on (R04H PON) +#define EPD_DISPOFF 0x02 // Power off (R02H POF) +#define EPD_SLPIN 0x07 // Enter deep sleep (R07H DSLP) +#define EPD_SLPOUT 0xFF // Exit sleep (not supported, requires wake-up) +#define EPD_PTLIN 0x91 // Partial display in (R91H PTIN) +#define EPD_PTLOUT 0x92 // Partial display out (R92H PTOUT) +#define EPD_PTLW 0x90 // Partial display window setting (R90H PTL) + +#define TFT_SWRST 0xFF // Software reset (not supported) +#define TFT_CASET 0xFF // Column address setting (not supported) +#define TFT_PASET 0xFF // Page address setting (not supported) +#define TFT_RAMWR 0x13 // Write RAM (R13H DTM2, red data) +#define TFT_RAMRD 0xFF // Read RAM (not supported) +#define TFT_INVON 0xFF // Display inversion on (not supported) +#define TFT_INVOFF 0xFF // Display inversion off (not supported) +#define TFT_INIT_DELAY 0 // Initialization delay (none) + +#ifdef TFT_BUSY +#define CHECK_BUSY() \ + do \ + { \ + while (!digitalRead(TFT_BUSY)) \ + ; \ + } while (0) +#else +#define CHECK_BUSY() +#endif + +#define EPD_INIT() \ + do \ + { \ + writecommand(0x4D);\ + writedata(0x78);\ + writecommand(0x00); \ + writedata(0x0F);\ + writedata(0x29);\ + writecommand(0x01); \ + writedata(0x07);\ + writedata(0x00);\ + writecommand(0x03); \ + writedata(0x10);\ + writedata(0x54);\ + writedata(0x44);\ + writecommand(0x06); \ + writedata(0x05);\ + writedata(0x00);\ + writedata(0x3F);\ + writedata(0x0A);\ + writedata(0x25);\ + writedata(0x12);\ + writedata(0x1A); \ + writecommand(0x50);\ + writedata(0x37);\ + writecommand(0x60); \ + writedata(0x02);\ + writedata(0x02);\ + writecommand(0x61); \ + writedata(128/256); \ + writedata(128%256); \ + writedata(296/256); \ + writedata(296%256); \ + writecommand(0xE7);\ + writedata(0x1C);\ + writecommand(0xE3); \ + writedata(0x22);\ + writecommand(0xB4);\ + writedata(0xD0);\ + writecommand(0xB5);\ + writedata(0x03);\ + writecommand(0xE9);\ + writedata(0x01); \ + writecommand(0x30);\ + writedata(0x08); \ + writecommand(0x04);\ + CHECK_BUSY(); \ + } while (0) + +#define EPD_UPDATE() \ + do \ + { \ + writecommand(0x12); \ + writedata(0x00); \ + CHECK_BUSY(); \ + } while (0) + +#define EPD_SLEEP() \ + do \ + { \ + writecommand(0x02); \ + CHECK_BUSY(); \ + delay(100); \ + writecommand(0x07); \ + writedata(0xA5); \ + } while (0) + +#define EPD_WAKEUP() \ + do \ + { \ + digitalWrite(TFT_RST, LOW); \ + delay(20); \ + digitalWrite(TFT_RST, HIGH); \ + delay(20); \ + CHECK_BUSY(); \ + EPD_INIT(); \ + } while (0) + +#define EPD_SET_WINDOW(x1, y1, x2, y2) \ + do \ + { \ + } while (0) + +#define COLOR_GET(color) ( \ + (color) == 0x00 ? 0x01 : \ + (color) == 0x0B ? 0x02 : \ + (color) == 0x06 ? 0x03 : \ + (color) == 0x0F ? 0x00 : \ + 0x00 \ +) + +#define EPD_PUSH_NEW_COLORS(w, h, colors) \ + do \ + { \ + uint16_t bytes_per_row = (w) / 2; \ + uint8_t temp1, temp2, temp3, temp4; \ + writecommand(0x10); \ + for (uint16_t row = 0; row < (h) ; row++) \ + { \ + for(uint16_t col = 0; col < bytes_per_row; col+=2) \ + { \ + uint8_t b = (colors[bytes_per_row *row+col ]) ; \ + uint8_t c = (colors[bytes_per_row *row+col + 1]) ; \ + temp1 = (b >> 4) & 0x0F;\ + temp2 = b & 0x0F;\ + temp3 = (c >> 4) & 0x0F;\ + temp4 = c & 0x0F;\ + writedata(((COLOR_GET(temp1) <<6)|( COLOR_GET(temp2) << 4 ) |( COLOR_GET(temp3) << 2 ) |( COLOR_GET(temp4) << 0 )));\ + } \ + } \ + } while (0) + +#define EPD_PUSH_NEW_COLORS_FLIP(w, h, colors) \ + do \ + { \ + uint16_t bytes_per_row = (w) / 2; \ + uint8_t temp1, temp2, temp3, temp4; \ + writecommand(0x10); \ + for (uint16_t row = 0; row < (h); row++) \ + { \ + uint16_t start = row * bytes_per_row; \ + for (uint16_t col = 0; col < bytes_per_row; col+=2) \ + { \ + uint8_t b = (colors[bytes_per_row *row + (bytes_per_row - 1 - col)]) ;\ + uint8_t c = (colors[bytes_per_row *row + (bytes_per_row - 1 - col) - 1]) ;\ + temp1 = (b >> 4) & 0x0F;\ + temp2 = b & 0x0F;\ + temp3 = (c >> 4) & 0x0F;\ + temp4 = c & 0x0F;\ + writedata(((COLOR_GET(temp1) <<6)|( COLOR_GET(temp2) << 4 ) |( COLOR_GET(temp3) << 2 ) |( COLOR_GET(temp4) << 0 )));\ + } \ + } \ + } while (0) + +#define EPD_PUSH_OLD_COLORS(w, h, colors) \ + do \ + { \ + } while (0) + +#define EPD_PUSH_OLD_COLORS_FLIP(w, h, colors) \ + do \ + { \ + \ + } while (0) + +#define EPD_SET_TEMP(temp) \ + do \ + { \ + } while (0) \ No newline at end of file diff --git a/lib/Seeed_GFX/TFT_Drivers/JD79667_Init.h b/lib/Seeed_GFX/TFT_Drivers/JD79667_Init.h new file mode 100644 index 0000000..192bfd8 --- /dev/null +++ b/lib/Seeed_GFX/TFT_Drivers/JD79667_Init.h @@ -0,0 +1,72 @@ +// JD79686B_Init.h +{ +#ifdef TFT_BUSY + pinMode(TFT_BUSY, INPUT); +#endif +#ifdef TFT_ENABLE + pinMode(TFT_ENABLE, OUTPUT); + digitalWrite(TFT_ENABLE, HIGH); +#endif + digitalWrite(TFT_RST, LOW); + delay(20); + digitalWrite(TFT_RST, HIGH); + delay(50); + CHECK_BUSY(); + writecommand(0x4D); + writedata(0x78); + + writecommand(0x00); //PSR + writedata(0x0F); + writedata(0x29); + + writecommand(0x01); //PWRR + writedata(0x07); + writedata(0x00); + + writecommand(0x03); //POFS + writedata(0x10); + writedata(0x54); + writedata(0x44); + + writecommand(0x06); //BTST_P + writedata(0x05); + writedata(0x00); + writedata(0x3F); + writedata(0x0A); + writedata(0x25); + writedata(0x12); + writedata(0x1A); + + writecommand(0x50); //CDI + writedata(0x37); + + writecommand(0x60); //TCON + writedata(0x02); + writedata(0x02); + + writecommand(0x61); //TRES + writedata(128/256); // Source_BITS_H + writedata(128%256); // Source_BITS_L + writedata(296/256); // Gate_BITS_H + writedata(296%256); // Gate_BITS_L + + writecommand(0xE7); + writedata(0x1C); + + writecommand(0xE3); + writedata(0x22); + + writecommand(0xB4); + writedata(0xD0); + writecommand(0xB5); + writedata(0x03); + + writecommand(0xE9); + writedata(0x01); + + writecommand(0x30); + writedata(0x08); + + writecommand(0x04); + CHECK_BUSY(); +} \ No newline at end of file diff --git a/lib/Seeed_GFX/TFT_Drivers/JD79667_Rotation.h b/lib/Seeed_GFX/TFT_Drivers/JD79667_Rotation.h new file mode 100644 index 0000000..692818a --- /dev/null +++ b/lib/Seeed_GFX/TFT_Drivers/JD79667_Rotation.h @@ -0,0 +1,25 @@ +rotation = m % 4; + +writecommand(EPD_PNLSET); +switch (rotation) { + case 0: // Portrait + writedata(0x1F); // 0b00011111 + _width = _init_width; + _height = _init_height; + break; + case 1: // Landscape (Portrait + 90) + writedata(0x1B); // 0b00011011 + _width = _init_height; + _height = _init_width; + break; + case 2: // Inverted Portrait + writedata(0x13); // 0b00010011 + _width = _init_width; + _height = _init_height; + break; + case 3: // Landscape (Portrait + 270) + writedata(0x17); // 0b00010111 + _width = _init_height; + _height = _init_width; + break; +} \ No newline at end of file diff --git a/lib/Seeed_GFX/TFT_Drivers/JD79676_Defines.h b/lib/Seeed_GFX/TFT_Drivers/JD79676_Defines.h new file mode 100644 index 0000000..09fba4b --- /dev/null +++ b/lib/Seeed_GFX/TFT_Drivers/JD79676_Defines.h @@ -0,0 +1,198 @@ +#ifndef EPD_WIDTH +#define EPD_WIDTH 128 +#endif + +#ifndef EPD_HEIGHT +#define EPD_HEIGHT 250 +#endif + +#ifndef TFT_WIDTH +#define TFT_WIDTH EPD_WIDTH +#endif + +#ifndef TFT_HEIGHT +#define TFT_HEIGHT EPD_HEIGHT +#endif + +#define EPD_COLOR_DEPTH 4 + + + +#define EPD_NOP 0xFF // No operation command (not supported) +#define EPD_PNLSET 0x00 // Panel setting (R00H PSR) +#define EPD_DISPON 0x04 // Power on (R04H PON) +#define EPD_DISPOFF 0x02 // Power off (R02H POF) +#define EPD_SLPIN 0x07 // Enter deep sleep (R07H DSLP) +#define EPD_SLPOUT 0xFF // Exit sleep (not supported, requires wake-up) +#define EPD_PTLIN 0x91 // Partial display in (R91H PTIN) +#define EPD_PTLOUT 0x92 // Partial display out (R92H PTOUT) +#define EPD_PTLW 0x90 // Partial display window setting (R90H PTL) + +#define TFT_SWRST 0xFF // Software reset (not supported) +#define TFT_CASET 0xFF // Column address setting (not supported) +#define TFT_PASET 0xFF // Page address setting (not supported) +#define TFT_RAMWR 0x13 // Write RAM (R13H DTM2, red data) +#define TFT_RAMRD 0xFF // Read RAM (not supported) +#define TFT_INVON 0xFF // Display inversion on (not supported) +#define TFT_INVOFF 0xFF // Display inversion off (not supported) +#define TFT_INIT_DELAY 0 // Initialization delay (none) + +#ifdef TFT_BUSY +#define CHECK_BUSY() \ + do \ + { \ + while (!digitalRead(TFT_BUSY)) \ + ; \ + } while (0) +#else +#define CHECK_BUSY() +#endif + +#define EPD_INIT() \ + do \ + { \ + writecommand(0x4D);\ + writedata(0x78);\ + writecommand(0x00); \ + writedata(0x0F);\ + writedata(0x29);\ + writecommand(0x01); \ + writedata(0x07);\ + writecommand(0x03); \ + writedata(0x10);\ + writedata(0x54);\ + writedata(0x44);\ + writecommand(0x06); \ + writedata(0x0F);\ + writedata(0x0A);\ + writedata(0x2F);\ + writedata(0x25);\ + writedata(0x22);\ + writedata(0x2E);\ + writedata(0x21); \ + writecommand(0x50);\ + writedata(0x37);\ + writecommand(0x60); \ + writedata(0x02);\ + writedata(0x02);\ + writecommand(0x61); \ + writedata(128/256); \ + writedata(128%256); \ + writedata(250/256); \ + writedata(250%256); \ + writecommand(0xE7);\ + writedata(0x1C);\ + writecommand(0xE3); \ + writedata(0x22);\ + writecommand(0xB4);\ + writedata(0xD0);\ + writecommand(0xB5);\ + writedata(0x03);\ + writecommand(0xE9);\ + writedata(0x01); \ + writecommand(0x30);\ + writedata(0x08); \ + writecommand(0x04);\ + CHECK_BUSY(); \ + } while (0) + +#define EPD_UPDATE() \ + do \ + { \ + writecommand(0x12); \ + writedata(0x00); \ + CHECK_BUSY(); \ + } while (0) + +#define EPD_SLEEP() \ + do \ + { \ + writecommand(0x02); \ + CHECK_BUSY(); \ + delay(100); \ + writecommand(0x07); \ + writedata(0xA5); \ + } while (0) + +#define EPD_WAKEUP() \ + do \ + { \ + digitalWrite(TFT_RST, LOW); \ + delay(20); \ + digitalWrite(TFT_RST, HIGH); \ + delay(20); \ + CHECK_BUSY(); \ + EPD_INIT(); \ + } while (0) + +#define EPD_SET_WINDOW(x1, y1, x2, y2) \ + do \ + { \ + } while (0) + +#define COLOR_GET(color) ( \ + (color) == 0x00 ? 0x01 : \ + (color) == 0x0B ? 0x02 : \ + (color) == 0x06 ? 0x03 : \ + (color) == 0x0F ? 0x00 : \ + 0x00 \ +) + +#define EPD_PUSH_NEW_COLORS(w, h, colors) \ + do \ + { \ + uint16_t bytes_per_row = (w) / 2; \ + uint8_t temp1, temp2, temp3, temp4; \ + writecommand(0x10); \ + for (uint16_t row = 0; row < (h) ; row++) \ + { \ + for(uint16_t col = 0; col < bytes_per_row; col+=2) \ + { \ + uint8_t b = (colors[bytes_per_row *row+col ]) ; \ + uint8_t c = (colors[bytes_per_row *row+col + 1]) ; \ + temp1 = (b >> 4) & 0x0F;\ + temp2 = b & 0x0F;\ + temp3 = (c >> 4) & 0x0F;\ + temp4 = c & 0x0F;\ + writedata(((COLOR_GET(temp1) <<6)|( COLOR_GET(temp2) << 4 ) |( COLOR_GET(temp3) << 2 ) |( COLOR_GET(temp4) << 0 )));\ + } \ + } \ + } while (0) + +#define EPD_PUSH_NEW_COLORS_FLIP(w, h, colors) \ + do \ + { \ + uint16_t bytes_per_row = (w) / 2; \ + uint8_t temp1, temp2, temp3, temp4; \ + writecommand(0x10); \ + for (uint16_t row = 0; row < (h); row++) \ + { \ + uint16_t start = row * bytes_per_row; \ + for (uint16_t col = 0; col < bytes_per_row; col+=2) \ + { \ + uint8_t b = (colors[bytes_per_row *row + (bytes_per_row - 1 - col)]) ;\ + uint8_t c = (colors[bytes_per_row *row + (bytes_per_row - 1 - col) - 1]) ;\ + temp1 = (b >> 4) & 0x0F;\ + temp2 = b & 0x0F;\ + temp3 = (c >> 4) & 0x0F;\ + temp4 = c & 0x0F;\ + writedata(((COLOR_GET(temp1) <<6)|( COLOR_GET(temp2) << 4 ) |( COLOR_GET(temp3) << 2 ) |( COLOR_GET(temp4) << 0 )));\ + } \ + } \ + } while (0) + +#define EPD_PUSH_OLD_COLORS(w, h, colors) \ + do \ + { \ + } while (0) + +#define EPD_PUSH_OLD_COLORS_FLIP(w, h, colors) \ + do \ + { \ + \ + } while (0) + +#define EPD_SET_TEMP(temp) \ + do \ + { \ + } while (0) \ No newline at end of file diff --git a/lib/Seeed_GFX/TFT_Drivers/JD79676_Rotation.h b/lib/Seeed_GFX/TFT_Drivers/JD79676_Rotation.h new file mode 100644 index 0000000..93dd365 --- /dev/null +++ b/lib/Seeed_GFX/TFT_Drivers/JD79676_Rotation.h @@ -0,0 +1,25 @@ +rotation = m % 4; + +writecommand(EPD_PNLSET); +switch (rotation) { + case 0: // Portrait + writedata(0x0F); // 0b00011111 + _width = _init_width; + _height = _init_height; + break; + case 1: // Landscape (Portrait + 90) + writedata(0x0B); // 0b00011011 + _width = _init_height; + _height = _init_width; + break; + case 2: // Inverted Portrait + writedata(0x03); // 0b00010011 + _width = _init_width; + _height = _init_height; + break; + case 3: // Landscape (Portrait + 270) + writedata(0x07); // 0b00010111 + _width = _init_height; + _height = _init_width; + break; +} \ No newline at end of file diff --git a/lib/Seeed_GFX/TFT_Drivers/JD79676_init.h b/lib/Seeed_GFX/TFT_Drivers/JD79676_init.h new file mode 100644 index 0000000..e4285b3 --- /dev/null +++ b/lib/Seeed_GFX/TFT_Drivers/JD79676_init.h @@ -0,0 +1,75 @@ +// JD79686B_Init.h +{ +#ifdef TFT_BUSY + pinMode(TFT_BUSY, INPUT); +#endif +#ifdef TFT_ENABLE + pinMode(TFT_ENABLE, OUTPUT); + digitalWrite(TFT_ENABLE, HIGH); +#endif + digitalWrite(TFT_RST, LOW); + delay(20); + digitalWrite(TFT_RST, HIGH); + delay(50); + CHECK_BUSY(); + writecommand(0x4D); + writedata(0x78); + + writecommand(0x00); //PSR + writedata(0x0F); + writedata(0x29); + + writecommand(0x01); //PWRR + writedata(0x07); + + + writecommand(0x03); //POFS + writedata(0x10); + writedata(0x54); + writedata(0x44); + + writecommand(0x06); //BTST_P + writedata(0x0F); + writedata(0x0A); + writedata(0x2F); + writedata(0x25); + writedata(0x22); + writedata(0x2E); + writedata(0x21); + + writecommand(0x50); //CDI + writedata(0x37); + + writecommand(0x60); //TCON + writedata(0x02); + writedata(0x02); + + writecommand(0x61); //TRES + writedata(128/256); // Source_BITS_H + writedata(128%256); // Source_BITS_L + writedata(250/256); // Gate_BITS_H + writedata(250%256); // Gate_BITS_L + + writecommand(0xE7); + writedata(0x1C); + + writecommand(0xE3); + writedata(0x22); + + writecommand(0xB4); + writedata(0xD0); + writecommand(0xB5); + writedata(0x03); + + writecommand(0xE9); + writedata(0x01); + + writecommand(0x30); + writedata(0x08); + + writecommand(0x04); + CHECK_BUSY(); + + //set ic offset + setViewport(0 ,0 ,EPD_WIDTH - COL_OFFSET ,EPD_HEIGHT - ROW_OFFSET); +} \ No newline at end of file diff --git a/lib/Seeed_GFX/TFT_Drivers/JD79686B_Defines.h b/lib/Seeed_GFX/TFT_Drivers/JD79686B_Defines.h new file mode 100644 index 0000000..89e3691 --- /dev/null +++ b/lib/Seeed_GFX/TFT_Drivers/JD79686B_Defines.h @@ -0,0 +1,165 @@ +#ifndef EPD_WIDTH +#define EPD_WIDTH 800 +#endif + +#ifndef EPD_HEIGHT +#define EPD_HEIGHT 480 +#endif + +#ifndef TFT_WIDTH +#define TFT_WIDTH EPD_WIDTH +#endif + +#ifndef TFT_HEIGHT +#define TFT_HEIGHT EPD_HEIGHT +#endif + +#define EPD_COLOR_DEPTH 1 + +#define EPD_NOP 0xFF // No operation command (not supported) +#define EPD_PNLSET 0x00 // Panel setting (R00H PSR) +#define EPD_DISPON 0x04 // Power on (R04H PON) +#define EPD_DISPOFF 0x02 // Power off (R02H POF) +#define EPD_SLPIN 0x07 // Enter deep sleep (R07H DSLP) +#define EPD_SLPOUT 0xFF // Exit sleep (not supported, requires wake-up) +#define EPD_PTLIN 0x91 // Partial display in (R91H PTIN) +#define EPD_PTLOUT 0x92 // Partial display out (R92H PTOUT) +#define EPD_PTLW 0x90 // Partial display window setting (R90H PTL) + +#define TFT_SWRST 0xFF // Software reset (not supported) +#define TFT_CASET 0xFF // Column address setting (not supported) +#define TFT_PASET 0xFF // Page address setting (not supported) +#define TFT_RAMWR 0x13 // Write RAM (R13H DTM2, red data) +#define TFT_RAMRD 0xFF // Read RAM (not supported) +#define TFT_INVON 0xFF // Display inversion on (not supported) +#define TFT_INVOFF 0xFF // Display inversion off (not supported) +#define TFT_INIT_DELAY 0 // Initialization delay (none) + +#ifdef TFT_BUSY +#define CHECK_BUSY() \ + do \ + { \ + while (!digitalRead(TFT_BUSY)) \ + ; \ + } while (0) +#else +#define CHECK_BUSY() +#endif + +#define EPD_UPDATE() \ + do \ + { \ + writecommand(0x04); \ + CHECK_BUSY(); \ + writecommand(0x12); \ + CHECK_BUSY(); \ + } while (0) + +#define EPD_SLEEP() \ + do \ + { \ + writecommand(0x02); \ + CHECK_BUSY(); \ + writecommand(0x07); \ + writedata(0xA5); \ + } while (0) + +#define EPD_WAKEUP() \ + do \ + { \ + digitalWrite(TFT_RST, LOW); \ + delay(20); \ + digitalWrite(TFT_RST, HIGH); \ + delay(20); \ + writecommand(0x4D); \ + writedata(0x55); \ + writecommand(0xA6); \ + writedata(0x38); \ + writecommand(0xB4); \ + writedata(0x5D); \ + writecommand(0xB6); \ + writedata(0x80); \ + writecommand(0xB7); \ + writedata(0x00); \ + writecommand(0xF7); \ + writedata(0x02); \ + } while (0) + +#define EPD_SET_WINDOW(x1, y1, x2, y2) \ + do \ + { \ + writecommand(0x91); \ + writecommand(0x90); \ + writedata((x1 >> 8) & 0xFF); /* x1 / 256 */ \ + writedata(x1 & 0xFF); /* x1 % 256 */ \ + writedata((x2 >> 8) & 0xFF); /* x2 / 256 */ \ + writedata((x2 & 0xFF) - 1); /* x2 % 256 - 1 */ \ + writedata((y1 >> 8) & 0xFF); /* y1 / 256 */ \ + writedata(y1 & 0xFF); /* y1 % 256 */ \ + writedata((y2 >> 8) & 0xFF); /* y2 / 256 */ \ + writedata((y2 & 0xFF) - 1); /* y2 % 256 - 1 */ \ + writedata(0x01); \ + } while (0) + +#define EPD_PUSH_NEW_COLORS(w, h, colors) \ + do \ + { \ + writecommand(0x13); \ + for (int i = 0; i < (w) * (h) / 8; i++) \ + { \ + writedata(colors[i]); \ + } \ + } while (0) + +#define EPD_PUSH_NEW_COLORS_FLIP(w, h, colors) \ + do \ + { \ + writecommand(0x13); \ + uint16_t bytes_per_row = (w) / 8; \ + for (uint16_t row = 0; row < (h); row++) \ + { \ + uint16_t start = row * bytes_per_row; \ + for (uint16_t col = 0; col < bytes_per_row; col++) \ + { \ + uint8_t b = colors[start + (bytes_per_row - 1 - col)]; \ + b = ((b & 0xF0) >> 4) | ((b & 0x0F) << 4); \ + b = ((b & 0xCC) >> 2) | ((b & 0x33) << 2); \ + b = ((b & 0xAA) >> 1) | ((b & 0x55) << 1); \ + writedata(b); \ + } \ + } \ + } while (0) + +#define EPD_PUSH_OLD_COLORS(w, h, colors) \ + do \ + { \ + writecommand(0x10); \ + for (int i = 0; i < (w) * (h) / 8; i++) \ + { \ + writedata(colors[i]); \ + } \ + } while (0) + +#define EPD_PUSH_OLD_COLORS_FLIP(w, h, colors) \ + do \ + { \ + writecommand(0x10); \ + uint16_t bytes_per_row = (w) / 8; \ + for (uint16_t row = 0; row < (h); row++) \ + { \ + uint16_t start = row * bytes_per_row; \ + for (uint16_t col = 0; col < bytes_per_row; col++) \ + { \ + uint8_t b = colors[start + (bytes_per_row - 1 - col)]; \ + b = ((b & 0xF0) >> 4) | ((b & 0x0F) << 4); \ + b = ((b & 0xCC) >> 2) | ((b & 0x33) << 2); \ + b = ((b & 0xAA) >> 1) | ((b & 0x55) << 1); \ + writedata(b); \ + } \ + } \ + } while (0) + +#define EPD_SET_TEMP(temp) \ + do \ + { \ + } while (0) \ No newline at end of file diff --git a/lib/Seeed_GFX/TFT_Drivers/JD79686B_Init.h b/lib/Seeed_GFX/TFT_Drivers/JD79686B_Init.h new file mode 100644 index 0000000..71c41ce --- /dev/null +++ b/lib/Seeed_GFX/TFT_Drivers/JD79686B_Init.h @@ -0,0 +1,29 @@ +// JD79686B_Init.h +{ +#ifdef TFT_BUSY + pinMode(TFT_BUSY, INPUT); +#endif +#ifdef TFT_ENABLE + pinMode(TFT_ENABLE, OUTPUT); + digitalWrite(TFT_ENABLE, HIGH); +#endif + digitalWrite(TFT_RST, LOW); + delay(20); + digitalWrite(TFT_RST, HIGH); + delay(20); + writecommand(0x4D); + writedata(0x55); + writecommand(0xA6); + writedata(0x38); + writecommand(0xB4); + writedata(0x5D); + writecommand(0xB6); + writedata(0x80); + writecommand(0xB7); + writedata(0x00); + writecommand(0xF7); + writedata(0x02); + writecommand(0X00); // PANNEL SETTING + writedata(0xF7); + writedata(0x0D); +} \ No newline at end of file diff --git a/lib/Seeed_GFX/TFT_Drivers/JD79686B_Rotation.h b/lib/Seeed_GFX/TFT_Drivers/JD79686B_Rotation.h new file mode 100644 index 0000000..72a2918 --- /dev/null +++ b/lib/Seeed_GFX/TFT_Drivers/JD79686B_Rotation.h @@ -0,0 +1,26 @@ +// JD79686B_Rotation.h +rotation = m % 4; + +writecommand(EPD_PNLSET); +switch (rotation) { + case 0: // Portrait + writedata(0x1F); // 0b00011111 + _width = _init_width; + _height = _init_height; + break; + case 1: // Landscape (Portrait + 90) + writedata(0x1B); // 0b00011011 + _width = _init_height; + _height = _init_width; + break; + case 2: // Inverted Portrait + writedata(0x13); // 0b00010011 + _width = _init_width; + _height = _init_height; + break; + case 3: // Landscape (Portrait + 270) + writedata(0x17); // 0b00010111 + _width = _init_height; + _height = _init_width; + break; +} \ No newline at end of file diff --git a/lib/Seeed_GFX/TFT_Drivers/R61581_Defines.h b/lib/Seeed_GFX/TFT_Drivers/R61581_Defines.h new file mode 100644 index 0000000..bd5fb88 --- /dev/null +++ b/lib/Seeed_GFX/TFT_Drivers/R61581_Defines.h @@ -0,0 +1,42 @@ +// Change the width and height if required (defined in portrait mode) +// or use the constructor to over-ride defaults +#define TFT_WIDTH 320 +#define TFT_HEIGHT 480 + + +// Delay between some initialisation commands +#define TFT_INIT_DELAY 0x80 // Not used unless commandlist invoked + + +// Generic commands used by TFT_eSPI.cpp +#define TFT_NOP 0x00 +#define TFT_SWRST 0x01 + +#define TFT_SLPIN 0x10 +#define TFT_SLPOUT 0x11 + +#define TFT_INVOFF 0x20 +#define TFT_INVON 0x21 + +#define TFT_DISPOFF 0x28 +#define TFT_DISPON 0x29 + +#define TFT_CASET 0x2A +#define TFT_PASET 0x2B +#define TFT_RAMWR 0x2C + +#define TFT_RAMRD 0x2E + +#define TFT_MADCTL 0x36 + +#define TFT_MAD_MY 0x80 +#define TFT_MAD_MX 0x40 +#define TFT_MAD_MV 0x20 +#define TFT_MAD_ML 0x10 +#define TFT_MAD_RGB 0x00 +#define TFT_MAD_BGR 0x08 +#define TFT_MAD_MH 0x04 +#define TFT_MAD_SS 0x02 +#define TFT_MAD_GS 0x01 + +#define TFT_IDXRD 0x00 // ILI9341 only, indexed control register read diff --git a/lib/Seeed_GFX/TFT_Drivers/R61581_Init.h b/lib/Seeed_GFX/TFT_Drivers/R61581_Init.h new file mode 100644 index 0000000..1e9adee --- /dev/null +++ b/lib/Seeed_GFX/TFT_Drivers/R61581_Init.h @@ -0,0 +1,80 @@ + +// This is the command sequence that initialises the R61581 driver +// +// This setup information uses simple 8-bit SPI writecommand() and writedata() functions +// +// See ST7735_Setup.h file for an alternative format + + +// Configure R61581 display + + writecommand(TFT_SLPOUT); + delay(20); + + writecommand(0xB0); + writedata(0x00); + + writecommand(0xD0); + writedata(0x07); + writedata(0x42); + writedata(0x18); + + writecommand(0xD1); + writedata(0x00); + writedata(0x07); + writedata(0x10); + + writecommand(0xD2); + writedata(0x01); + writedata(0x02); + + writecommand(0xC0); + writedata(0x12); + writedata(0x3B); + writedata(0x00); + writedata(0x02); + writedata(0x11); + + writecommand(0xC5); + writedata(0x03); + + writecommand(0xC8); + writedata(0x00); + writedata(0x32); + writedata(0x36); + writedata(0x45); + writedata(0x06); + writedata(0x16); + writedata(0x37); + writedata(0x75); + writedata(0x77); + writedata(0x54); + writedata(0x0C); + writedata(0x00); + + writecommand(TFT_MADCTL); + writedata(0x0A); + + writecommand(0x3A); + writedata(0x55); + + writecommand(TFT_CASET); + writedata(0x00); + writedata(0x00); + writedata(0x01); + writedata(0x3F); + + writecommand(TFT_PASET); + writedata(0x00); + writedata(0x00); + writedata(0x01); + writedata(0xDF); + + delay(120); + writecommand(TFT_DISPON); + + delay(25); +// End of R61581 display configuration + + + diff --git a/lib/Seeed_GFX/TFT_Drivers/R61581_Rotation.h b/lib/Seeed_GFX/TFT_Drivers/R61581_Rotation.h new file mode 100644 index 0000000..4d7dc61 --- /dev/null +++ b/lib/Seeed_GFX/TFT_Drivers/R61581_Rotation.h @@ -0,0 +1,27 @@ + // This is the command sequence that rotates the R61581 driver coordinate frame + + writecommand(TFT_MADCTL); + rotation = m % 4; + switch (rotation) { + case 0: // Portrait + writedata(TFT_MAD_BGR | TFT_MAD_MX); + _width = TFT_WIDTH; + _height = TFT_HEIGHT; + break; + case 1: // Landscape (Portrait + 90) + writedata(TFT_MAD_MV | TFT_MAD_BGR); + _width = TFT_HEIGHT; + _height = TFT_WIDTH; + break; + case 2: // Inverter portrait + writedata(TFT_MAD_BGR | TFT_MAD_GS); + _width = TFT_WIDTH; + _height = TFT_HEIGHT; + break; + case 3: // Inverted landscape + writedata(TFT_MAD_MV | TFT_MAD_BGR | TFT_MAD_MX | TFT_MAD_GS); + _width = TFT_HEIGHT; + _height = TFT_WIDTH; + break; + } + \ No newline at end of file diff --git a/lib/Seeed_GFX/TFT_Drivers/RM68120_Defines.h b/lib/Seeed_GFX/TFT_Drivers/RM68120_Defines.h new file mode 100644 index 0000000..50ed5a4 --- /dev/null +++ b/lib/Seeed_GFX/TFT_Drivers/RM68120_Defines.h @@ -0,0 +1,52 @@ +// Change the width and height if required (defined in portrait mode) +// or use the constructor to over-ride defaults +// RM68120_DRIVER + +#define TFT_WIDTH 480 +#define TFT_HEIGHT 800 + +//Set driver type common to all TBD initialisation options +#ifndef RM68120_DRIVER + #define RM68120_DRIVER +#endif + +// Delay between some initialisation commands +#define TFT_INIT_DELAY 0x80 // Not used unless commandlist invoked + +// Generic commands used by TFT_eSPI.cpp +#define TFT_NOP 0x0000 +#define TFT_SWRST 0x0100 + +#define TFT_INVOFF 0x2000 +#define TFT_INVON 0x2100 + +#define TFT_DISPOFF 0x2800 +#define TFT_DISPON 0x2900 + +#define TFT_CASET 0x2A00 +#define TFT_PASET 0x2B00 +#define TFT_RAMWR 0x2C00 + +#define TFT_RAMRD 0x2E00 +#define TFT_IDXRD 0xDD00 // ILI9341 only, indexed control register read + +#define TFT_MADCTL 0x3600 +#define TFT_MAD_MY 0x80 +#define TFT_MAD_MX 0x40 +#define TFT_MAD_MV 0x20 +#define TFT_MAD_ML 0x10 +#define TFT_MAD_BGR 0x08 +#define TFT_MAD_RGB 0x00 +#define TFT_MAD_MH 0x04 +#define TFT_MAD_H_FLIP 0x02 +#define TFT_MAD_V_FLIP 0x01 + +#ifdef TFT_RGB_ORDER + #if (TFT_RGB_ORDER == 1) + #define TFT_MAD_COLOR_ORDER TFT_MAD_RGB + #else + #define TFT_MAD_COLOR_ORDER TFT_MAD_BGR + #endif +#else + #define TFT_MAD_COLOR_ORDER TFT_MAD_RGB +#endif diff --git a/lib/Seeed_GFX/TFT_Drivers/RM68120_Init.h b/lib/Seeed_GFX/TFT_Drivers/RM68120_Init.h new file mode 100644 index 0000000..5b7e8f4 --- /dev/null +++ b/lib/Seeed_GFX/TFT_Drivers/RM68120_Init.h @@ -0,0 +1,269 @@ +// Initialisation for RM68120 + +//ENABLE PAGE 1 +writeRegister8(0xF000, 0x55); +writeRegister8(0xF001, 0xAA); +writeRegister8(0xF002, 0x52); +writeRegister8(0xF003, 0x08); +writeRegister8(0xF004, 0x01); + +//GAMMA SETING RED +writeRegister8(0xD400, 0x00); +writeRegister8(0xD401, 0x00); +writeRegister8(0xD402, 0x1b); +writeRegister8(0xD403, 0x44); +writeRegister8(0xD404, 0x62); +writeRegister8(0xD405, 0x00); +writeRegister8(0xD406, 0x7b); +writeRegister8(0xD407, 0xa1); +writeRegister8(0xD408, 0xc0); +writeRegister8(0xD409, 0xee); +writeRegister8(0xD40A, 0x55); +writeRegister8(0xD40B, 0x10); +writeRegister8(0xD40C, 0x2c); +writeRegister8(0xD40D, 0x43); +writeRegister8(0xD40E, 0x57); +writeRegister8(0xD40F, 0x55); +writeRegister8(0xD410, 0x68); +writeRegister8(0xD411, 0x78); +writeRegister8(0xD412, 0x87); +writeRegister8(0xD413, 0x94); +writeRegister8(0xD414, 0x55); +writeRegister8(0xD415, 0xa0); +writeRegister8(0xD416, 0xac); +writeRegister8(0xD417, 0xb6); +writeRegister8(0xD418, 0xc1); +writeRegister8(0xD419, 0x55); +writeRegister8(0xD41A, 0xcb); +writeRegister8(0xD41B, 0xcd); +writeRegister8(0xD41C, 0xd6); +writeRegister8(0xD41D, 0xdf); +writeRegister8(0xD41E, 0x95); +writeRegister8(0xD41F, 0xe8); +writeRegister8(0xD420, 0xf1); +writeRegister8(0xD421, 0xfa); +writeRegister8(0xD422, 0x02); +writeRegister8(0xD423, 0xaa); +writeRegister8(0xD424, 0x0b); +writeRegister8(0xD425, 0x13); +writeRegister8(0xD426, 0x1d); +writeRegister8(0xD427, 0x26); +writeRegister8(0xD428, 0xaa); +writeRegister8(0xD429, 0x30); +writeRegister8(0xD42A, 0x3c); +writeRegister8(0xD42B, 0x4A); +writeRegister8(0xD42C, 0x63); +writeRegister8(0xD42D, 0xea); +writeRegister8(0xD42E, 0x79); +writeRegister8(0xD42F, 0xa6); +writeRegister8(0xD430, 0xd0); +writeRegister8(0xD431, 0x20); +writeRegister8(0xD432, 0x0f); +writeRegister8(0xD433, 0x8e); +writeRegister8(0xD434, 0xff); + +//GAMMA SETING GREEN +writeRegister8(0xD500, 0x00); +writeRegister8(0xD501, 0x00); +writeRegister8(0xD502, 0x1b); +writeRegister8(0xD503, 0x44); +writeRegister8(0xD504, 0x62); +writeRegister8(0xD505, 0x00); +writeRegister8(0xD506, 0x7b); +writeRegister8(0xD507, 0xa1); +writeRegister8(0xD508, 0xc0); +writeRegister8(0xD509, 0xee); +writeRegister8(0xD50A, 0x55); +writeRegister8(0xD50B, 0x10); +writeRegister8(0xD50C, 0x2c); +writeRegister8(0xD50D, 0x43); +writeRegister8(0xD50E, 0x57); +writeRegister8(0xD50F, 0x55); +writeRegister8(0xD510, 0x68); +writeRegister8(0xD511, 0x78); +writeRegister8(0xD512, 0x87); +writeRegister8(0xD513, 0x94); +writeRegister8(0xD514, 0x55); +writeRegister8(0xD515, 0xa0); +writeRegister8(0xD516, 0xac); +writeRegister8(0xD517, 0xb6); +writeRegister8(0xD518, 0xc1); +writeRegister8(0xD519, 0x55); +writeRegister8(0xD51A, 0xcb); +writeRegister8(0xD51B, 0xcd); +writeRegister8(0xD51C, 0xd6); +writeRegister8(0xD51D, 0xdf); +writeRegister8(0xD51E, 0x95); +writeRegister8(0xD51F, 0xe8); +writeRegister8(0xD520, 0xf1); +writeRegister8(0xD521, 0xfa); +writeRegister8(0xD522, 0x02); +writeRegister8(0xD523, 0xaa); +writeRegister8(0xD524, 0x0b); +writeRegister8(0xD525, 0x13); +writeRegister8(0xD526, 0x1d); +writeRegister8(0xD527, 0x26); +writeRegister8(0xD528, 0xaa); +writeRegister8(0xD529, 0x30); +writeRegister8(0xD52A, 0x3c); +writeRegister8(0xD52B, 0x4a); +writeRegister8(0xD52C, 0x63); +writeRegister8(0xD52D, 0xea); +writeRegister8(0xD52E, 0x79); +writeRegister8(0xD52F, 0xa6); +writeRegister8(0xD530, 0xd0); +writeRegister8(0xD531, 0x20); +writeRegister8(0xD532, 0x0f); +writeRegister8(0xD533, 0x8e); +writeRegister8(0xD534, 0xff); + +//GAMMA SETING BLUE +writeRegister8(0xD600, 0x00); +writeRegister8(0xD601, 0x00); +writeRegister8(0xD602, 0x1b); +writeRegister8(0xD603, 0x44); +writeRegister8(0xD604, 0x62); +writeRegister8(0xD605, 0x00); +writeRegister8(0xD606, 0x7b); +writeRegister8(0xD607, 0xa1); +writeRegister8(0xD608, 0xc0); +writeRegister8(0xD609, 0xee); +writeRegister8(0xD60A, 0x55); +writeRegister8(0xD60B, 0x10); +writeRegister8(0xD60C, 0x2c); +writeRegister8(0xD60D, 0x43); +writeRegister8(0xD60E, 0x57); +writeRegister8(0xD60F, 0x55); +writeRegister8(0xD610, 0x68); +writeRegister8(0xD611, 0x78); +writeRegister8(0xD612, 0x87); +writeRegister8(0xD613, 0x94); +writeRegister8(0xD614, 0x55); +writeRegister8(0xD615, 0xa0); +writeRegister8(0xD616, 0xac); +writeRegister8(0xD617, 0xb6); +writeRegister8(0xD618, 0xc1); +writeRegister8(0xD619, 0x55); +writeRegister8(0xD61A, 0xcb); +writeRegister8(0xD61B, 0xcd); +writeRegister8(0xD61C, 0xd6); +writeRegister8(0xD61D, 0xdf); +writeRegister8(0xD61E, 0x95); +writeRegister8(0xD61F, 0xe8); +writeRegister8(0xD620, 0xf1); +writeRegister8(0xD621, 0xfa); +writeRegister8(0xD622, 0x02); +writeRegister8(0xD623, 0xaa); +writeRegister8(0xD624, 0x0b); +writeRegister8(0xD625, 0x13); +writeRegister8(0xD626, 0x1d); +writeRegister8(0xD627, 0x26); +writeRegister8(0xD628, 0xaa); +writeRegister8(0xD629, 0x30); +writeRegister8(0xD62A, 0x3c); +writeRegister8(0xD62B, 0x4A); +writeRegister8(0xD62C, 0x63); +writeRegister8(0xD62D, 0xea); +writeRegister8(0xD62E, 0x79); +writeRegister8(0xD62F, 0xa6); +writeRegister8(0xD630, 0xd0); +writeRegister8(0xD631, 0x20); +writeRegister8(0xD632, 0x0f); +writeRegister8(0xD633, 0x8e); +writeRegister8(0xD634, 0xff); + +//AVDD VOLTAGE SETTING +writeRegister8(0xB000, 0x05); +writeRegister8(0xB001, 0x05); +writeRegister8(0xB002, 0x05); +//AVEE VOLTAGE SETTING +writeRegister8(0xB100, 0x05); +writeRegister8(0xB101, 0x05); +writeRegister8(0xB102, 0x05); + +//AVDD Boosting +writeRegister8(0xB600, 0x34); +writeRegister8(0xB601, 0x34); +writeRegister8(0xB603, 0x34); +//AVEE Boosting +writeRegister8(0xB700, 0x24); +writeRegister8(0xB701, 0x24); +writeRegister8(0xB702, 0x24); +//VCL Boosting +writeRegister8(0xB800, 0x24); +writeRegister8(0xB801, 0x24); +writeRegister8(0xB802, 0x24); +//VGLX VOLTAGE SETTING +writeRegister8(0xBA00, 0x14); +writeRegister8(0xBA01, 0x14); +writeRegister8(0xBA02, 0x14); +//VCL Boosting +writeRegister8(0xB900, 0x24); +writeRegister8(0xB901, 0x24); +writeRegister8(0xB902, 0x24); +//Gamma Voltage +writeRegister8(0xBc00, 0x00); +writeRegister8(0xBc01, 0xa0);//vgmp=5.0 +writeRegister8(0xBc02, 0x00); +writeRegister8(0xBd00, 0x00); +writeRegister8(0xBd01, 0xa0);//vgmn=5.0 +writeRegister8(0xBd02, 0x00); +//VCOM Setting +writeRegister8(0xBe01, 0x3d);//3 + +//ENABLE PAGE 0 +writeRegister8(0xF000, 0x55); +writeRegister8(0xF001, 0xAA); +writeRegister8(0xF002, 0x52); +writeRegister8(0xF003, 0x08); +writeRegister8(0xF004, 0x00); +//Vivid Color Function Control +writeRegister8(0xB400, 0x10); +//Z-INVERSION +writeRegister8(0xBC00, 0x05); +writeRegister8(0xBC01, 0x05); +writeRegister8(0xBC02, 0x05); +//*************** add on 20111021**********************// +writeRegister8(0xB700, 0x22);//GATE EQ CONTROL +writeRegister8(0xB701, 0x22);//GATE EQ CONTROL +writeRegister8(0xC80B, 0x2A);//DISPLAY TIMING CONTROL +writeRegister8(0xC80C, 0x2A);//DISPLAY TIMING CONTROL +writeRegister8(0xC80F, 0x2A);//DISPLAY TIMING CONTROL +writeRegister8(0xC810, 0x2A);//DISPLAY TIMING CONTROL +//*************** add on 20111021**********************// +//PWM_ENH_OE =1 +writeRegister8(0xd000, 0x01); +//DM_SEL =1 +writeRegister8(0xb300, 0x10); +//VBPDA=07h +writeRegister8(0xBd02, 0x07); +//VBPDb=07h +writeRegister8(0xBe02, 0x07); +//VBPDc=07h +writeRegister8(0xBf02, 0x07); + +//ENABLE PAGE 2 +writeRegister8(0xF000, 0x55); +writeRegister8(0xF001, 0xAA); +writeRegister8(0xF002, 0x52); +writeRegister8(0xF003, 0x08); +writeRegister8(0xF004, 0x02); +//SDREG0 =0 +writeRegister8(0xc301, 0xa9); +//DS=14 +writeRegister8(0xfe01, 0x94); +//OSC =60h +writeRegister8(0xf600, 0x60); +//TE ON +writeRegister8(0x3500, 0x00); +writeRegister8(0xFFFF, 0xFF); + +//SLEEP OUT +writecommand(0x1100); +delay(100); +//DISPLY ON +writecommand(0x2900); +delay(100); + +writeRegister16(0x3A00, 0x55); +writeRegister8(0x3600, TFT_MAD_COLOR_ORDER); diff --git a/lib/Seeed_GFX/TFT_Drivers/RM68120_Rotation.h b/lib/Seeed_GFX/TFT_Drivers/RM68120_Rotation.h new file mode 100644 index 0000000..492493a --- /dev/null +++ b/lib/Seeed_GFX/TFT_Drivers/RM68120_Rotation.h @@ -0,0 +1,29 @@ + +// This is the command sequence that rotates the RM68120 driver coordinate frame + + rotation = m % 4; // Limit the range of values to 0-3 + uint8_t reg = 0; + + switch (rotation) { + case 0: + reg = TFT_MAD_COLOR_ORDER; + _width = _init_width; + _height = _init_height; + break; + case 1: + reg = TFT_MAD_MV | TFT_MAD_MX | TFT_MAD_COLOR_ORDER; + _width = _init_height; + _height = _init_width; + break; + case 2: + reg = TFT_MAD_MX | TFT_MAD_MY | TFT_MAD_COLOR_ORDER; + _width = _init_width; + _height = _init_height; + break; + case 3: + reg = TFT_MAD_MV | TFT_MAD_MY | TFT_MAD_COLOR_ORDER; + _width = _init_height; + _height = _init_width; + break; + } + writeRegister16(TFT_MADCTL, reg); \ No newline at end of file diff --git a/lib/Seeed_GFX/TFT_Drivers/RM68140_Defines.h b/lib/Seeed_GFX/TFT_Drivers/RM68140_Defines.h new file mode 100644 index 0000000..bd5fb88 --- /dev/null +++ b/lib/Seeed_GFX/TFT_Drivers/RM68140_Defines.h @@ -0,0 +1,42 @@ +// Change the width and height if required (defined in portrait mode) +// or use the constructor to over-ride defaults +#define TFT_WIDTH 320 +#define TFT_HEIGHT 480 + + +// Delay between some initialisation commands +#define TFT_INIT_DELAY 0x80 // Not used unless commandlist invoked + + +// Generic commands used by TFT_eSPI.cpp +#define TFT_NOP 0x00 +#define TFT_SWRST 0x01 + +#define TFT_SLPIN 0x10 +#define TFT_SLPOUT 0x11 + +#define TFT_INVOFF 0x20 +#define TFT_INVON 0x21 + +#define TFT_DISPOFF 0x28 +#define TFT_DISPON 0x29 + +#define TFT_CASET 0x2A +#define TFT_PASET 0x2B +#define TFT_RAMWR 0x2C + +#define TFT_RAMRD 0x2E + +#define TFT_MADCTL 0x36 + +#define TFT_MAD_MY 0x80 +#define TFT_MAD_MX 0x40 +#define TFT_MAD_MV 0x20 +#define TFT_MAD_ML 0x10 +#define TFT_MAD_RGB 0x00 +#define TFT_MAD_BGR 0x08 +#define TFT_MAD_MH 0x04 +#define TFT_MAD_SS 0x02 +#define TFT_MAD_GS 0x01 + +#define TFT_IDXRD 0x00 // ILI9341 only, indexed control register read diff --git a/lib/Seeed_GFX/TFT_Drivers/RM68140_Init.h b/lib/Seeed_GFX/TFT_Drivers/RM68140_Init.h new file mode 100644 index 0000000..cc44719 --- /dev/null +++ b/lib/Seeed_GFX/TFT_Drivers/RM68140_Init.h @@ -0,0 +1,77 @@ + +// This is the command sequence that initialises the RM68140 driver +// +// This setup information uses simple 8-bit SPI writecommand() and writedata() functions +// +// See ST7735_Setup.h file for an alternative format + + +// Configure RM68140 display + + writecommand(TFT_SLPOUT); + delay(20); + + writecommand(0xD0); + writedata(0x07); + writedata(0x42); + writedata(0x18); + + writecommand(0xD1); + writedata(0x00); + writedata(0x07); + writedata(0x10); + + writecommand(0xD2); + writedata(0x01); + writedata(0x02); + + writecommand(0xC0); + writedata(0x10); + writedata(0x3B); + writedata(0x00); + writedata(0x02); + writedata(0x11); + + writecommand(0xC5); + writedata(0x03); + + writecommand(0xC8); + writedata(0x00); + writedata(0x32); + writedata(0x36); + writedata(0x45); + writedata(0x06); + writedata(0x16); + writedata(0x37); + writedata(0x75); + writedata(0x77); + writedata(0x54); + writedata(0x0C); + writedata(0x00); + + writecommand(TFT_MADCTL); + writedata(0x0A); + + writecommand(0x3A); + writedata(0x55); + + writecommand(TFT_CASET); + writedata(0x00); + writedata(0x00); + writedata(0x01); + writedata(0x3F); + + writecommand(TFT_PASET); + writedata(0x00); + writedata(0x00); + writedata(0x01); + writedata(0xDF); + + delay(120); + writecommand(TFT_DISPON); + + delay(25); +// End of RM68140 display configuration + + + diff --git a/lib/Seeed_GFX/TFT_Drivers/RM68140_Rotation.h b/lib/Seeed_GFX/TFT_Drivers/RM68140_Rotation.h new file mode 100644 index 0000000..2f83d61 --- /dev/null +++ b/lib/Seeed_GFX/TFT_Drivers/RM68140_Rotation.h @@ -0,0 +1,44 @@ + // This is the command sequence that rotates the RM68140 driver coordinate frame + + + writecommand(TFT_MADCTL); + rotation = m % 4; + switch (rotation) { + case 0: // Portrait + writedata(TFT_MAD_BGR); + writecommand(0xB6); + writedata(0); + writedata(0x22); + writedata(0x3B); + _width = TFT_WIDTH; + _height = TFT_HEIGHT; + break; + case 1: // Landscape (Portrait + 90) + writedata(TFT_MAD_MV | TFT_MAD_BGR); + writecommand(0xB6); + writedata(0); + writedata(0x02); + writedata(0x3B); + _width = TFT_HEIGHT; + _height = TFT_WIDTH; + break; + case 2: // Inverter portrait + writedata(TFT_MAD_BGR); + writecommand(0xB6); + writedata(0); + writedata(0x42); + writedata(0x3B); + _width = TFT_WIDTH; + _height = TFT_HEIGHT; + break; + case 3: // Inverted landscape + writedata(TFT_MAD_MV | TFT_MAD_BGR); + writecommand(0xB6); + writedata(0); + writedata(0x62); + writedata(0x3B); + _width = TFT_HEIGHT; + _height = TFT_WIDTH; + break; + } + diff --git a/lib/Seeed_GFX/TFT_Drivers/S6D02A1_Defines.h b/lib/Seeed_GFX/TFT_Drivers/S6D02A1_Defines.h new file mode 100644 index 0000000..7806eb8 --- /dev/null +++ b/lib/Seeed_GFX/TFT_Drivers/S6D02A1_Defines.h @@ -0,0 +1,58 @@ +// Change the width and height if required (defined in portrait mode) +// or use the constructor to over-ride defaults +#define TFT_WIDTH 128 +#define TFT_HEIGHT 160 + + +// Color definitions for backwards compatibility with old sketches +// use colour definitions like TFT_BLACK to make sketches more portable +#define S6D02A1_BLACK 0x0000 /* 0, 0, 0 */ +#define S6D02A1_NAVY 0x000F /* 0, 0, 128 */ +#define S6D02A1_DARKGREEN 0x03E0 /* 0, 128, 0 */ +#define S6D02A1_DARKCYAN 0x03EF /* 0, 128, 128 */ +#define S6D02A1_MAROON 0x7800 /* 128, 0, 0 */ +#define S6D02A1_PURPLE 0x780F /* 128, 0, 128 */ +#define S6D02A1_OLIVE 0x7BE0 /* 128, 128, 0 */ +#define S6D02A1_LIGHTGREY 0xC618 /* 192, 192, 192 */ +#define S6D02A1_DARKGREY 0x7BEF /* 128, 128, 128 */ +#define S6D02A1_BLUE 0x001F /* 0, 0, 255 */ +#define S6D02A1_GREEN 0x07E0 /* 0, 255, 0 */ +#define S6D02A1_CYAN 0x07FF /* 0, 255, 255 */ +#define S6D02A1_RED 0xF800 /* 255, 0, 0 */ +#define S6D02A1_MAGENTA 0xF81F /* 255, 0, 255 */ +#define S6D02A1_YELLOW 0xFFE0 /* 255, 255, 0 */ +#define S6D02A1_WHITE 0xFFFF /* 255, 255, 255 */ +#define S6D02A1_ORANGE 0xFD20 /* 255, 165, 0 */ +#define S6D02A1_GREENYELLOW 0xAFE5 /* 173, 255, 47 */ +#define S6D02A1_PINK 0xF81F + + +// Delay between some initialisation commands +#define TFT_INIT_DELAY 0x80 + + +// Generic commands used by TFT_eSPI.cpp +#define TFT_NOP 0x00 +#define TFT_SWRST 0x01 + +#define TFT_INVOFF 0x20 +#define TFT_INVON 0x21 + +#define TFT_DISPOFF 0x28 +#define TFT_DISPON 0x29 + +#define TFT_CASET 0x2A +#define TFT_PASET 0x2B +#define TFT_RAMWR 0x2C + +#define TFT_RAMRD 0x2E +#define TFT_IDXRD 0x00 //0xDD // ILI9341 only, indexed control register read + +#define TFT_MADCTL 0x36 +#define TFT_MAD_MY 0x80 +#define TFT_MAD_MX 0x40 +#define TFT_MAD_MV 0x20 +#define TFT_MAD_ML 0x10 +#define TFT_MAD_BGR 0x08 +#define TFT_MAD_MH 0x04 +#define TFT_MAD_RGB 0x00 diff --git a/lib/Seeed_GFX/TFT_Drivers/S6D02A1_Init.h b/lib/Seeed_GFX/TFT_Drivers/S6D02A1_Init.h new file mode 100644 index 0000000..1986c3e --- /dev/null +++ b/lib/Seeed_GFX/TFT_Drivers/S6D02A1_Init.h @@ -0,0 +1,47 @@ + +// This is the command sequence that initialises the S6D02A1 driver +// +// This setup information is in a format accecpted by the commandList() function +// which reduces FLASH space, but on an ESP8266 there is plenty available! +// +// See ILI9341_Setup.h file for an alternative simpler format + +{ + // Initialization commands for S6D02A1 screens + static const uint8_t S6D02A1_cmds[] PROGMEM = + { + 29, + 0xf0, 2, 0x5a, 0x5a, // Excommand2 + 0xfc, 2, 0x5a, 0x5a, // Excommand3 + 0x26, 1, 0x01, // Gamma set + 0xfa, 15, 0x02, 0x1f, 0x00, 0x10, 0x22, 0x30, 0x38, 0x3A, 0x3A, 0x3A, 0x3A, 0x3A, 0x3d, 0x02, 0x01, // Positive gamma control + 0xfb, 15, 0x21, 0x00, 0x02, 0x04, 0x07, 0x0a, 0x0b, 0x0c, 0x0c, 0x16, 0x1e, 0x30, 0x3f, 0x01, 0x02, // Negative gamma control + 0xfd, 11, 0x00, 0x00, 0x00, 0x17, 0x10, 0x00, 0x01, 0x01, 0x00, 0x1f, 0x1f, // Analog parameter control + 0xf4, 15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x3f, 0x07, 0x00, 0x3C, 0x36, 0x00, 0x3C, 0x36, 0x00, // Power control + 0xf5, 13, 0x00, 0x70, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6d, 0x66, 0x06, // VCOM control + 0xf6, 11, 0x02, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x02, 0x00, 0x06, 0x01, 0x00, // Source control + 0xf2, 17, 0x00, 0x01, 0x03, 0x08, 0x08, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x04, 0x08, 0x08, //Display control + 0xf8, 1, 0x11, // Gate control + 0xf7, 4, 0xc8, 0x20, 0x00, 0x00, // Interface control + 0xf3, 2, 0x00, 0x00, // Power sequence control + 0x11, TFT_INIT_DELAY, 50, // Wake + 0xf3, 2+TFT_INIT_DELAY, 0x00, 0x01, 50, // Power sequence control + 0xf3, 2+TFT_INIT_DELAY, 0x00, 0x03, 50, // Power sequence control + 0xf3, 2+TFT_INIT_DELAY, 0x00, 0x07, 50, // Power sequence control + 0xf3, 2+TFT_INIT_DELAY, 0x00, 0x0f, 50, // Power sequence control + 0xf4, 15+TFT_INIT_DELAY, 0x00, 0x04, 0x00, 0x00, 0x00, 0x3f, 0x3f, 0x07, 0x00, 0x3C, 0x36, 0x00, 0x3C, 0x36, 0x00, 50, // Power control + 0xf3, 2+TFT_INIT_DELAY, 0x00, 0x1f, 50, // Power sequence control + 0xf3, 2+TFT_INIT_DELAY, 0x00, 0x7f, 50, // Power sequence control + 0xf3, 2+TFT_INIT_DELAY, 0x00, 0xff, 50, // Power sequence control + 0xfd, 11, 0x00, 0x00, 0x00, 0x17, 0x10, 0x00, 0x00, 0x01, 0x00, 0x16, 0x16, // Analog parameter control + 0xf4, 15, 0x00, 0x09, 0x00, 0x00, 0x00, 0x3f, 0x3f, 0x07, 0x00, 0x3C, 0x36, 0x00, 0x3C, 0x36, 0x00, // Power control + 0x36, 1, 0xC8, // Memory access data control + 0x35, 1, 0x00, // Tearing effect line on + 0x3a, 1+TFT_INIT_DELAY, 0x05, 150, // Interface pixel control + 0x29, 0, // Display on + 0x2c, 0 // Memory write + }; + + commandList(S6D02A1_cmds); + +} diff --git a/lib/Seeed_GFX/TFT_Drivers/S6D02A1_Rotation.h b/lib/Seeed_GFX/TFT_Drivers/S6D02A1_Rotation.h new file mode 100644 index 0000000..7fa6eec --- /dev/null +++ b/lib/Seeed_GFX/TFT_Drivers/S6D02A1_Rotation.h @@ -0,0 +1,28 @@ + +// This is the command sequence that rotates the S6D02A1 driver coordinate frame + + rotation = m % 4; + + writecommand(TFT_MADCTL); + switch (rotation) { + case 0: + writedata(TFT_MAD_MX | TFT_MAD_MY | TFT_MAD_BGR); + _width = _init_width; + _height = _init_height; + break; + case 1: + writedata(TFT_MAD_MV | TFT_MAD_MY | TFT_MAD_BGR); + _width = _init_height; + _height = _init_width; + break; + case 2: + writedata(TFT_MAD_BGR); + _width = _init_width; + _height = _init_height; + break; + case 3: + writedata(TFT_MAD_MX | TFT_MAD_MV | TFT_MAD_BGR); + _width = _init_height; + _height = _init_width; + break; + } diff --git a/lib/Seeed_GFX/TFT_Drivers/SSD1351_Defines.h b/lib/Seeed_GFX/TFT_Drivers/SSD1351_Defines.h new file mode 100644 index 0000000..c4b0f6c --- /dev/null +++ b/lib/Seeed_GFX/TFT_Drivers/SSD1351_Defines.h @@ -0,0 +1,20 @@ +#ifndef TFT_WIDTH + #define TFT_WIDTH 128 +#endif +#ifndef TFT_HEIGHT + #define TFT_HEIGHT 128 +#endif + +// Delay between some initialisation commands +#define TFT_INIT_DELAY 0x80 + +// Generic commands used by TFT_eSPI.cpp +#define TFT_NOP 0x00 +#define TFT_SWRST TFT_NOP +#define TFT_CASET 0x15 // SETCOLUMN +#define TFT_PASET 0x75 // SETROW +#define TFT_RAMWR 0x5C // WRITERAM +#define TFT_RAMRD 0x5D // READRAM +#define TFT_IDXRD TFT_NOP +#define TFT_INVOFF 0xA6 // NORMALDISPLAY +#define TFT_INVON 0xA7 // INVERTDISPLAY diff --git a/lib/Seeed_GFX/TFT_Drivers/SSD1351_Init.h b/lib/Seeed_GFX/TFT_Drivers/SSD1351_Init.h new file mode 100644 index 0000000..2b5cff7 --- /dev/null +++ b/lib/Seeed_GFX/TFT_Drivers/SSD1351_Init.h @@ -0,0 +1,35 @@ +{ + writecommand(0xFD); // COMMANDLOCK + writedata(0x12); + writecommand(0xFD); // COMMANDLOCK + writedata(0xB1); + writecommand(0xAE); // DISPLAYOFF + writecommand(0xB3); // CLOCKDIV + writedata(0xF1); + writecommand(0xCA); // MUXRATIO + writedata(127); + writecommand(0xA2); // DISPLAYOFFSET + writedata(0x00); + writecommand(0xB5); // SETGPIO + writedata(0x00); + writecommand(0xAB); // FUNCTIONSELECT + writedata(0x01); + writecommand(0xB1); // PRECHARGE + writedata(0x32); + writecommand(0xBE); // VCOMH + writedata(0x05); + writecommand(0xA6); // NORMALDISPLAY + writecommand(0xC1); // CONTRASTABC + writedata(0xC8); + writedata(0x80); + writedata(0xC8); + writecommand(0xC7); // CONTRASTMASTER + writedata(0x0F); + writecommand(0xB4); // SETVSL + writedata(0xA0); + writedata(0xB5); + writedata(0x55); + writecommand(0xB6); // PRECHARGE2 + writedata(0x01); + writecommand(0xAF); // DISPLAYON +} diff --git a/lib/Seeed_GFX/TFT_Drivers/SSD1351_Rotation.h b/lib/Seeed_GFX/TFT_Drivers/SSD1351_Rotation.h new file mode 100644 index 0000000..f500f6d --- /dev/null +++ b/lib/Seeed_GFX/TFT_Drivers/SSD1351_Rotation.h @@ -0,0 +1,34 @@ + +// This is the command sequence that rotates the SSD1351 driver coordinate frame + + rotation = m % 4; // Limit the range of values to 0-3 + + uint8_t madctl = 0x64; + + switch (rotation) { + case 0: + madctl |= 0x10; + _width = _init_width; + _height = _init_height; + break; + case 1: + madctl |= 0x13; + _width = _init_height; + _height = _init_width; + break; + case 2: + madctl |= 0x02; + _width = _init_width; + _height = _init_height; + break; + case 3: + madctl |= 0x01; + _width = _init_height; + _height = _init_width; + break; + } + + writecommand(0xA0); // SETREMAP + writedata(madctl); + writecommand(0xA1); // STARTLINE + writedata(rotation < 2 ? TFT_HEIGHT : 0); diff --git a/lib/Seeed_GFX/TFT_Drivers/SSD1677_Defines.h b/lib/Seeed_GFX/TFT_Drivers/SSD1677_Defines.h new file mode 100644 index 0000000..71a3744 --- /dev/null +++ b/lib/Seeed_GFX/TFT_Drivers/SSD1677_Defines.h @@ -0,0 +1,239 @@ +// SSD1677_Defines.h +#ifndef __SSD1677_DEFINES_H__ +#define __SSD1677_DEFINES_H__ + +#ifndef EPD_WIDTH +#define EPD_WIDTH 800 // Adjust based on actual display width +#endif + +#ifndef EPD_HEIGHT +#define EPD_HEIGHT 480 // Adjust based on actual display height +#endif + +#ifndef TFT_WIDTH +#define TFT_WIDTH EPD_WIDTH +#endif + +#ifndef TFT_HEIGHT +#define TFT_HEIGHT EPD_HEIGHT +#endif + +#define USE_PARTIAL_EPAPER +#define EPD_COLOR_DEPTH 1 + +#define EPD_NOP 0xFF +#define EPD_PNLSET 0xFF // No direct panel set command for SSD1677 +#define EPD_DISPON 0xFF +#define EPD_DISPOFF 0xFF +#define EPD_SLPIN 0x10 +#define EPD_SLPOUT 0xFF +#define EPD_PTLIN 0xFF +#define EPD_PTLOUT 0xFF +#define EPD_PTLW 0xFF + +#define TFT_SWRST 0x12 +#define TFT_CASET 0x44 +#define TFT_PASET 0x45 +#define TFT_RAMWR 0x24 +#define TFT_RAMRD 0xFF +#define TFT_INVON 0xFF +#define TFT_INVOFF 0xFF + +#define TFT_INIT_DELAY 0 + +#ifdef TFT_BUSY +#define CHECK_BUSY() \ + do \ + { \ + while (digitalRead(TFT_BUSY)) \ + ; \ + } while (0) +#else +#define CHECK_BUSY() +#endif + +#define EPD_UPDATE() \ + do \ + { \ + writecommand(0x22); \ + writedata(0xF7); \ + writecommand(0x20); \ + CHECK_BUSY(); \ + } while (0) + +#define EPD_UPDATE_PARTIAL() \ + do \ + { \ + writecommand(0x22); \ + writedata(0xFF); \ + writecommand(0x20); \ + } while (0); + + +#define EPD_SLEEP() \ + do \ + { \ + writecommand(0x10); \ + writedata(0x01); \ + delay(100); \ + } while (0) + +#define EPD_INIT() \ + do \ + { \ + digitalWrite(TFT_RST, LOW); \ + delay(10); \ + digitalWrite(TFT_RST, HIGH); \ + delay(10); \ + CHECK_BUSY(); \ + writecommand(0x12); \ + CHECK_BUSY(); \ + writecommand(0x0C); \ + writedata(0xAE); \ + writedata(0xC7); \ + writedata(0XC3); \ + writedata(0XC0); \ + writedata(0X80); \ + writecommand(0x3C); \ + writedata(0x05); \ + writecommand(0x01); \ + writedata((EPD_HEIGHT - 1) % 256); \ + writedata((EPD_HEIGHT - 1) / 256); \ + writedata(0x02); \ + writecommand(0x11); \ + writedata(0x03); \ + writecommand(0x44); \ + writedata(0x00); \ + writedata(0x00); \ + writedata((EPD_WIDTH - 1) % 256); \ + writedata((EPD_WIDTH - 1) / 256); \ + writecommand(0x45); \ + writedata(0x00); \ + writedata(0x00); \ + writedata((EPD_HEIGHT - 1) % 256); \ + writedata((EPD_HEIGHT - 1) / 256); \ + writecommand(0x18); \ + writedata(0x80); \ + writecommand(0x4E); \ + writedata(0x00); \ + writedata(0x00); \ + writecommand(0x4F); \ + writedata(0x00); \ + writedata(0x00); \ + CHECK_BUSY(); \ + } while (0) + +#define EPD_INIT_PARTIAL() \ + do \ + { \ + writecommand(0x18); \ + writedata(0x80); \ + writecommand(0x3C); \ + writedata(0x80); \ + } while (0); +#define EPD_WAKEUP() EPD_INIT() + +#define EPD_WAKEUP_PARTIAL() \ + do \ + { \ + EPD_INIT(); \ + EPD_INIT_PARTIAL(); \ + } while (0); + + + +#define EPD_SET_WINDOW(x1, y1, x2, y2) \ + do \ + { \ + writecommand(0x44);\ + writedata(x1 & 0xFF); \ + writedata(x1 >> 8);\ + writedata((x2) & 0xFF);\ + writedata((x2) >> 8); \ + writecommand(0x45); \ + writedata(y1 & 0xFF);\ + writedata(y1 >> 8);\ + writedata((y2) & 0xFF); \ + writedata((y2) >> 8); \ + writecommand(0x4E); \ + writedata((x1) & 0xFF);\ + writedata((x1) >> 8); \ + writecommand(0x4F); \ + writedata(y1 & 0xFF);\ + writedata(y1 >> 8);\ + } while (0) + +#define EPD_PUSH_NEW_COLORS(w, h, colors) \ + do \ + { \ + writecommand(0x24); \ + for (int i = 0; i < w * h / 8; i++) \ + { \ + writedata(colors[i]); \ + } \ + } while (0) + +#define EPD_PUSH_NEW_COLORS(w, h, colors) \ + do \ + { \ + writecommand(0x24); \ + for (int i = 0; i < w * h / 8; i++) \ + { \ + writedata(colors[i]); \ + } \ + } while (0) + +#define EPD_PUSH_NEW_COLORS_FLIP(w, h, colors) \ + do \ + { \ + writecommand(0x24); \ + uint16_t bytes_per_row = (w) / 8; \ + for (uint16_t row = 0; row < (h); row++) \ + { \ + uint16_t start = row * bytes_per_row; \ + for (uint16_t col = 0; col < bytes_per_row; col++) \ + { \ + uint8_t b = colors[start + (bytes_per_row - 1 - col)]; \ + b = ((b & 0xF0) >> 4) | ((b & 0x0F) << 4); \ + b = ((b & 0xCC) >> 2) | ((b & 0x33) << 2); \ + b = ((b & 0xAA) >> 1) | ((b & 0x55) << 1); \ + writedata(b); \ + } \ + } \ + } while (0) + +// Macro to push old color data (red RAM or background) +#define EPD_PUSH_OLD_COLORS(w, h, colors) \ + do \ + { \ + writecommand(0x26); \ + for (int i = 0; i < w * h / 8; i++) \ + { \ + writedata(colors[i]); \ + } \ + } while (0) + +#define EPD_PUSH_OLD_COLORS_FLIP(w, h, colors) \ + do \ + { \ + writecommand(0x26); \ + uint16_t bytes_per_row = (w) / 8; \ + for (uint16_t row = 0; row < (h); row++) \ + { \ + uint16_t start = row * bytes_per_row; \ + for (uint16_t col = 0; col < bytes_per_row; col++) \ + { \ + uint8_t b = colors[start + (bytes_per_row - 1 - col)]; \ + b = ((b & 0xF0) >> 4) | ((b & 0x0F) << 4); \ + b = ((b & 0xCC) >> 2) | ((b & 0x33) << 2); \ + b = ((b & 0xAA) >> 1) | ((b & 0x55) << 1); \ + writedata(b); \ + } \ + } \ + } while (0) + +#define EPD_SET_TEMP(temp) \ + do \ + { \ + } while (0) +#endif \ No newline at end of file diff --git a/lib/Seeed_GFX/TFT_Drivers/SSD1677_Init.h b/lib/Seeed_GFX/TFT_Drivers/SSD1677_Init.h new file mode 100644 index 0000000..db46bab --- /dev/null +++ b/lib/Seeed_GFX/TFT_Drivers/SSD1677_Init.h @@ -0,0 +1,69 @@ +// SSD1677_Init.h +// Command sequence to initialize the SSD1677 driver +{ +#ifdef TFT_BUSY + pinMode(TFT_BUSY, INPUT); +#endif +#ifdef TFT_ENABLE + pinMode(TFT_ENABLE, OUTPUT); + digitalWrite(TFT_ENABLE, HIGH); +#endif + digitalWrite(TFT_RST, LOW); + delay(10); + digitalWrite(TFT_RST, HIGH); + delay(120); + + // Wait for busy signal + CHECK_BUSY(); + writecommand(0x12); // Software reset + CHECK_BUSY(); + + writecommand(0x0C); + writedata(0xAE); + writedata(0xC7); + writedata(0XC3); // + writedata(0XC0); // + writedata(0X80); // + + // Set border waveform + writecommand(0x3C); + writedata(0x05); + // Set driver output control + writecommand(0x01); + writedata((EPD_HEIGHT - 1) % 256); // + writedata((EPD_HEIGHT - 1) / 256); // + writedata(0x02); + + // Set data transfer way + writecommand(0x11); + writedata(0x03); + + // Set RAM X address range + writecommand(0x44); + writedata(0x00); + writedata(0x00); + writedata((EPD_WIDTH - 1) % 256); // + writedata((EPD_WIDTH - 1) / 256); // + // Set RAM Y address range + writecommand(0x45); + writedata(0x00); + writedata(0x00); + writedata((EPD_HEIGHT - 1) % 256); + writedata((EPD_HEIGHT - 1) / 256); + + + // Read built-in temperature sensor + writecommand(0x18); + writedata(0x80); + + // Set RAM address counter + writecommand(0x4E); + writedata(0x00); + writedata(0x00); + writecommand(0x4F); + writedata(0x00); + writedata(0x00); + CHECK_BUSY(); + + +} \ No newline at end of file diff --git a/lib/Seeed_GFX/TFT_Drivers/SSD1677_Rotation.h b/lib/Seeed_GFX/TFT_Drivers/SSD1677_Rotation.h new file mode 100644 index 0000000..4742364 --- /dev/null +++ b/lib/Seeed_GFX/TFT_Drivers/SSD1677_Rotation.h @@ -0,0 +1,30 @@ +// SSD1677_Rotation.h + +rotation = m % 4; + +switch (rotation) { + case 0: // Portrait (default) + writecommand(0x11); + writedata(0x03); + _width = _init_width; + _height = _init_height; + break; + case 1: // Landscape (90°) + writecommand(0x11); + writedata(0x01); // Adjust based on datasheet + _width = _init_height; + _height = _init_width; + break; + case 2: // Inverted Portrait (180°) + writecommand(0x11); + writedata(0x00); + _width = _init_width; + _height = _init_height; + break; + case 3: // Inverted Landscape (270°) + writecommand(0x11); + writedata(0x02); // Adjust based on datasheet + _width = _init_height; + _height = _init_width; + break; +} \ No newline at end of file diff --git a/lib/Seeed_GFX/TFT_Drivers/SSD1680_Defines.h b/lib/Seeed_GFX/TFT_Drivers/SSD1680_Defines.h new file mode 100644 index 0000000..4016706 --- /dev/null +++ b/lib/Seeed_GFX/TFT_Drivers/SSD1680_Defines.h @@ -0,0 +1,363 @@ +#ifndef __SSD1680_DEFINES_H__ +#define __SSD1680_DEFINES_H__ + +// Define screen resolution +#ifndef EPD_WIDTH +#define EPD_WIDTH 128 +#endif + +#ifndef EPD_HEIGHT +#define EPD_HEIGHT 296 +#endif + +#ifndef TFT_WIDTH +#define TFT_WIDTH EPD_WIDTH +#endif + +#ifndef TFT_HEIGHT +#define TFT_HEIGHT EPD_HEIGHT +#endif + +// Define color depth (1 bit for e-ink display) +#define EPD_COLOR_DEPTH 1 + +#define USE_PARTIAL_EPAPER +#define USE_MUTIGRAY_EPAPER +#define GRAY_LEVEL4 4 + +// Define no operation command +#define EPD_NOP 0xFF + +// Define common command macros +#define EPD_PNLSET 0x01 // Driver output control +#define EPD_DISPON 0xFF // No direct display on command +#define EPD_DISPOFF 0xFF // No direct display off command +#define EPD_SLPIN 0x10 // Enter deep sleep +#define EPD_SLPOUT 0xFF // No direct sleep out command +#define EPD_PTLIN 0x3C // Partial display in (BorderWaveform) +#define EPD_PTLOUT 0xFF // No direct partial display out command +#define EPD_PTLW 0x4E // Set RAM address counter + +#define TFT_SWRST 0x12 // Software reset +#define TFT_CASET 0x44 // Set RAM X address start/end position +#define TFT_PASET 0x45 // Set RAM Y address start/end position +#define TFT_RAMWR 0x24 // Write to RAM +#define TFT_RAMRD 0xFF // No direct read RAM command +#define TFT_INVON 0xFF // No inversion on command +#define TFT_INVOFF 0xFF // No inversion off command + +#define TFT_INIT_DELAY 0 + +// Macro to check BUSY signal +#ifdef TFT_BUSY +#define CHECK_BUSY() \ + do \ + { \ + while (digitalRead(TFT_BUSY)) \ + ; \ + } while (0) +#else +#define CHECK_BUSY() +#endif + +// Macro to update display +#define EPD_UPDATE() \ + do \ + { \ + writecommand(0x22); \ + writedata(0xF7); \ + writecommand(0x20); \ + CHECK_BUSY(); \ + } while (0) + +#define EPD_UPDATE_FAST() \ + do \ + { \ + writecommand(0x22); \ + writedata(0xC7); \ + writecommand(0x20); \ + CHECK_BUSY(); \ + } while (0) + +#define EPD_UPDATE_GRAY() EPD_UPDATE_FAST() + +#define EPD_UPDATE_PARTIAL() \ + do \ + { \ + writecommand(0x22); \ + writedata(0xFF); \ + writecommand(0x20); \ + } while (0); + +// Macro to enter deep sleep +#define EPD_SLEEP() \ + do \ + { \ + writecommand(0x10); \ + writedata(0x01); \ + delay(100); \ + } while (0) + + #define EPD_INIT() \ + do \ + { \ + digitalWrite(TFT_RST, LOW); \ + delay(10); \ + digitalWrite(TFT_RST, HIGH); \ + delay(10); \ + CHECK_BUSY(); \ + writecommand(0x12); \ + CHECK_BUSY(); \ + writecommand(0x3C); \ + writedata(0x05); \ + writecommand(0x01); \ + writedata((EPD_HEIGHT - 1) % 256); \ + writedata((EPD_HEIGHT - 1) / 256); \ + writedata(0x00); \ + writecommand(0x11); \ + writedata(0x03); \ + writecommand(0x44); \ + writedata(0x00); \ + writedata((EPD_WIDTH / 8) - 1); \ + writecommand(0x45); \ + writedata(0x00); \ + writedata(0x00); \ + writedata((EPD_HEIGHT - 1) % 256); \ + writedata((EPD_HEIGHT - 1) / 256); \ + writecommand(0x18); \ + writedata(0x80); \ + writecommand(0x4E); \ + writedata(0x00); \ + writecommand(0x4F); \ + writedata(0x00); \ + writedata(0x00); \ + CHECK_BUSY(); \ + } while (0) + +#define EPD_INIT_PARTIAL() \ + do \ + { \ + writecommand(0x18); \ + writedata(0x80); \ + writecommand(0x3C); \ + writedata(0x80); \ + } while (0); + +#define EPD_INIT_GRAY() \ + do \ + { \ + writecommand(0x18); \ + writedata(0x80); \ + writecommand(0x22); \ + writedata(0xB1); \ + writecommand(0x20); \ + CHECK_BUSY(); \ + writecommand(0x1A); \ + writedata(0x5A); \ + writedata(0x00); \ + writecommand(0x22); \ + writedata(0x91); \ + writecommand(0x20); \ + CHECK_BUSY(); \ + } while (0) + +// Macro to wake up device +#define EPD_WAKEUP() EPD_INIT() + +#define EPD_WAKEUP_GRAY() EPD_WAKEUP() + +#define EPD_WAKEUP_PARTIAL() \ + do \ + { \ + EPD_INIT(); \ + EPD_INIT_PARTIAL(); \ + } while (0); + +// Macro to set display window +#define EPD_SET_WINDOW(x1, y1, x2, y2) \ + do \ + { \ + writecommand(0x44);\ + writedata((x1 + 1) / 8); \ + writedata((x2 + 1) / 8 - 1);\ + writecommand(0x45); \ + writedata(y1 & 0xFF);\ + writedata(y1 >> 8);\ + writedata((y2) & 0xFF); \ + writedata((y2) >> 8); \ + writecommand(0x4E); \ + writedata((x1 + 1) / 8);\ + writecommand(0x4F); \ + writedata(y1 & 0xFF);\ + writedata(y1 >> 8);\ + } while (0) + +// Macro to push new color data +#define EPD_PUSH_NEW_COLORS(w, h, colors) \ + do \ + { \ + writecommand(0x24); \ + for (uint16_t i = 0; i < w * h / 8; i++) \ + { \ + writedata(colors[i]); \ + } \ + } while (0) + +#define EPD_PUSH_NEW_COLORS_FLIP(w, h, colors) \ + do \ + { \ + writecommand(0x24); \ + uint16_t bytes_per_row = (w) / 8; \ + for (uint16_t row = 0; row < (h); row++) \ + { \ + uint16_t start = row * bytes_per_row; \ + for (uint16_t col = 0; col < bytes_per_row; col++) \ + { \ + uint8_t b = colors[start + (bytes_per_row - 1 - col)]; \ + b = ((b & 0xF0) >> 4) | ((b & 0x0F) << 4); \ + b = ((b & 0xCC) >> 2) | ((b & 0x33) << 2); \ + b = ((b & 0xAA) >> 1) | ((b & 0x55) << 1); \ + writedata(b); \ + } \ + } \ + } while (0) + + +#define EPD_PUSH_NEW_GRAY_COLORS(w, h, colors) \ + do \ + { \ + EPD_INIT_GRAY(); \ + uint16_t i, j, k; \ + uint8_t temp1, temp2, temp3; \ + writecommand(0x24); \ + for(i = 0; i < (TFT_WIDTH * TFT_HEIGHT) / 8; i++) \ + { \ + /* Read 4 input bytes = 8 pixels */ \ + uint8_t c0 = colors[i * 4 + 0]; \ + uint8_t c1 = colors[i * 4 + 1]; \ + uint8_t c2 = colors[i * 4 + 2]; \ + uint8_t c3 = colors[i * 4 + 3]; \ + /* Extract 8 pixels from bit5-4 and bit1-0 of each byte */ \ + uint8_t p0 = (c0 >> 4) & 0x03; \ + uint8_t p1 = (c0 >> 0) & 0x03; \ + uint8_t p2 = (c1 >> 4) & 0x03; \ + uint8_t p3 = (c1 >> 0) & 0x03; \ + uint8_t p4 = (c2 >> 4) & 0x03; \ + uint8_t p5 = (c2 >> 0) & 0x03; \ + uint8_t p6 = (c3 >> 4) & 0x03; \ + uint8_t p7 = (c3 >> 0) & 0x03; \ + /* Pack into original-style packed bytes (high 2-bit per pixel) */ \ + uint8_t packed_byte0 = (p0 << 6) | (p1 << 4) | (p2 << 2) | p3; \ + uint8_t packed_byte1 = (p4 << 6) | (p5 << 4) | (p6 << 2) | p7; \ + \ + temp3 = 0; \ + for(j = 0; j < 2; j++) \ + { \ + temp1 = (j == 0) ? packed_byte0 : packed_byte1; \ + for(k = 0; k < 4; k++) \ + { \ + temp2 = temp1 & 0xC0; \ + if(temp2 == 0xC0) \ + temp3 |= 0x01; \ + else if(temp2 == 0x00) \ + temp3 |= 0x00; \ + else if((temp2 >= 0x80) && (temp2 < 0xC0)) \ + temp3 |= 0x00; \ + else if(temp2 == 0x40) \ + temp3 |= 0x01; \ + if((j == 0 && k <= 3) || (j == 1 && k <= 2)) \ + { \ + temp3 <<= 1; \ + temp1 <<= 2; \ + } \ + } \ + } \ + writedata(~temp3); \ + } \ + \ + writecommand(0x26); \ + for(i = 0; i < (TFT_WIDTH * TFT_HEIGHT) / 8; i++) \ + { \ + uint8_t c0 = colors[i * 4 + 0]; \ + uint8_t c1 = colors[i * 4 + 1]; \ + uint8_t c2 = colors[i * 4 + 2]; \ + uint8_t c3 = colors[i * 4 + 3]; \ + uint8_t p0 = (c0 >> 4) & 0x03; \ + uint8_t p1 = (c0 >> 0) & 0x03; \ + uint8_t p2 = (c1 >> 4) & 0x03; \ + uint8_t p3 = (c1 >> 0) & 0x03; \ + uint8_t p4 = (c2 >> 4) & 0x03; \ + uint8_t p5 = (c2 >> 0) & 0x03; \ + uint8_t p6 = (c3 >> 4) & 0x03; \ + uint8_t p7 = (c3 >> 0) & 0x03; \ + uint8_t packed_byte0 = (p0 << 6) | (p1 << 4) | (p2 << 2) | p3; \ + uint8_t packed_byte1 = (p4 << 6) | (p5 << 4) | (p6 << 2) | p7; \ + \ + temp3 = 0; \ + for(j = 0; j < 2; j++) \ + { \ + temp1 = (j == 0) ? packed_byte0 : packed_byte1; \ + for(k = 0; k < 4; k++) \ + { \ + temp2 = temp1 & 0xC0; \ + if(temp2 == 0xC0) \ + temp3 |= 0x01; \ + else if(temp2 == 0x00) \ + temp3 |= 0x00; \ + else if((temp2 >= 0x80) && (temp2 < 0xC0)) \ + temp3 |= 0x01; \ + else if(temp2 == 0x40) \ + temp3 |= 0x00; \ + if((j == 0 && k <= 3) || (j == 1 && k <= 2)) \ + { \ + temp3 <<= 1; \ + temp1 <<= 2; \ + } \ + } \ + } \ + writedata(~temp3); \ + } \ + } while (0) + +#define EPD_PUSH_NEW_GRAY_COLORS_FLIP(w, h, colors) \ + do \ + { \ + EPD_PUSH_NEW_GRAY_COLORS(w, h, colors); \ + } while (0) + + +// Macro to push old color data +#define EPD_PUSH_OLD_COLORS(w, h, colors) \ + do \ + { \ + writecommand(0x26); \ + for (int i = 0; i < w * h / 8; i++) \ + { \ + writedata(colors[i]); \ + } \ + } while (0) + +#define EPD_PUSH_OLD_COLORS_FLIP(w, h, colors) \ + do \ + { \ + writecommand(0x26); \ + uint16_t bytes_per_row = (w) / 8; \ + for (uint16_t row = 0; row < (h); row++) \ + { \ + uint16_t start = row * bytes_per_row; \ + for (uint16_t col = 0; col < bytes_per_row; col++) \ + { \ + uint8_t b = colors[start + (bytes_per_row - 1 - col)]; \ + b = ((b & 0xF0) >> 4) | ((b & 0x0F) << 4); \ + b = ((b & 0xCC) >> 2) | ((b & 0x33) << 2); \ + b = ((b & 0xAA) >> 1) | ((b & 0x55) << 1); \ + writedata(b); \ + } \ + } \ + } while (0) + +#define EPD_SET_TEMP(temp) \ + do \ + { \ + } while (0) +#endif \ No newline at end of file diff --git a/lib/Seeed_GFX/TFT_Drivers/SSD1680_Init.h b/lib/Seeed_GFX/TFT_Drivers/SSD1680_Init.h new file mode 100644 index 0000000..4e0b78b --- /dev/null +++ b/lib/Seeed_GFX/TFT_Drivers/SSD1680_Init.h @@ -0,0 +1,62 @@ +// This is the command sequence that initializes the SSD1680 driver + +{ +#ifdef TFT_BUSY + pinMode(TFT_BUSY, INPUT); +#endif +#ifdef TFT_ENABLE + pinMode(TFT_ENABLE, OUTPUT); + digitalWrite(TFT_ENABLE, HIGH); +#endif + // Hardware reset + digitalWrite(TFT_RST, LOW); + delay(10); + digitalWrite(TFT_RST, HIGH); + delay(120); + + // Wait for busy signal + CHECK_BUSY(); + writecommand(0x12); // Software reset + CHECK_BUSY(); + // Set border waveform + writecommand(0x3C); + writedata(0x05); + // Set driver output control + writecommand(0x01); + writedata((EPD_HEIGHT - 1) % 256); // + writedata((EPD_HEIGHT - 1) / 256); // + writedata(0x00); + + // Set data transfer way + writecommand(0x11); + writedata(0x03); + + // Set RAM X address range + writecommand(0x44); + writedata(0x00); + writedata(EPD_WIDTH / 8 - 1); // + // Set RAM Y address range + writecommand(0x45); + writedata(0x00); + writedata(0x00); + writedata((EPD_HEIGHT - 1) % 256); + writedata((EPD_HEIGHT - 1) / 256); + + + // Read built-in temperature sensor + writecommand(0x18); + writedata(0x80); + + // Set RAM address counter + writecommand(0x4E); + writedata(0x00); + writecommand(0x4F); + writedata(0x00); + writedata(0x00); + CHECK_BUSY(); + + //set ic offset + setViewport(0 ,0 ,EPD_WIDTH - COL_OFFSET ,EPD_HEIGHT - ROW_OFFSET); + + +} diff --git a/lib/Seeed_GFX/TFT_Drivers/SSD1680_Rotation.h b/lib/Seeed_GFX/TFT_Drivers/SSD1680_Rotation.h new file mode 100644 index 0000000..615e0b2 --- /dev/null +++ b/lib/Seeed_GFX/TFT_Drivers/SSD1680_Rotation.h @@ -0,0 +1,25 @@ +// SSD1680 screen rotation sequence + +rotation = m % 4; + +writecommand(0x11); // Data entry mode +switch (rotation) +{ +case 0: // Portrait mode (0 degrees) + writedata(0x01); // Y decrement, X increment + _width = EPD_WIDTH; + _height = EPD_HEIGHT; + break; +case 2: // Inverted portrait mode (180 degrees) + writedata(0x02); // Y increment, X decrement + _width = EPD_WIDTH; + _height = EPD_HEIGHT; + break; +case 1: // Landscape mode (90 degrees) +case 3: // Inverted landscape mode (270 degrees) + // SSD1680 does not directly support 90° and 270° rotation, not implemented + writedata(0x01); // Default to 0 degrees + _width = EPD_WIDTH; + _height = EPD_HEIGHT; + break; +} \ No newline at end of file diff --git a/lib/Seeed_GFX/TFT_Drivers/SSD1681_Defines.h b/lib/Seeed_GFX/TFT_Drivers/SSD1681_Defines.h new file mode 100644 index 0000000..8b7367d --- /dev/null +++ b/lib/Seeed_GFX/TFT_Drivers/SSD1681_Defines.h @@ -0,0 +1,376 @@ +#ifndef __SSD1681_DEFINES_H__ +#define __SSD1681_DEFINES_H__ + +// Define screen resolution (according to the manual) +#ifndef EPD_WIDTH +#define EPD_WIDTH 200 +#endif + +#ifndef EPD_HEIGHT +#define EPD_HEIGHT 200 +#endif + +#ifndef TFT_WIDTH +#define TFT_WIDTH EPD_WIDTH +#endif + +#ifndef TFT_HEIGHT +#define TFT_HEIGHT EPD_HEIGHT +#endif + +// Define color depth (1 bit for e-ink display, supports red/black/white) +#define EPD_COLOR_DEPTH 1 + +#define USE_PARTIAL_EPAPER +#define USE_MUTIGRAY_EPAPER +#define GRAY_LEVEL4 4 + +// Define no operation command +#define EPD_NOP 0xFF + +// Define common command macros (refer to manual Table 7-1 and demo) +#define EPD_PNLSET 0x01 // Driver output control +#define EPD_DISPON 0xFF // No direct display on command +#define EPD_DISPOFF 0xFF // No direct display off command +#define EPD_SLPIN 0x10 // Enter deep sleep +#define EPD_SLPOUT 0xFF // No direct sleep out command +#define EPD_PTLIN 0x3C // Partial display in (Border Waveform Control) +#define EPD_PTLOUT 0xFF // No direct partial display out command +#define EPD_PTLW 0x4E // Set RAM address counter + +#define TFT_SWRST 0x12 // Software reset +#define TFT_CASET 0x44 // Set RAM X address start/end position +#define TFT_PASET 0x45 // Set RAM Y address start/end position +#define TFT_RAMWR 0x24 // Write to black/white RAM +#define TFT_RAMRD 0xFF // No direct read RAM command +#define TFT_INVON 0xFF // No inversion on command +#define TFT_INVOFF 0xFF // No inversion off command + +#define TFT_INIT_DELAY 0 + +// Macro to check BUSY signal +#ifdef TFT_BUSY +#define CHECK_BUSY() \ + do \ + { \ + while (digitalRead(TFT_BUSY)) \ + ; \ + } while (0) +#else +#define CHECK_BUSY() +#endif + +// Macro to update display (full refresh) +#define EPD_UPDATE() \ + do \ + { \ + writecommand(0x22); \ + writedata(0xF7); \ + writecommand(0x20); \ + CHECK_BUSY(); \ + } while (0) + +#define EPD_UPDATE_FAST() \ + do \ + { \ + writecommand(0x22); \ + writedata(0xC7); \ + writecommand(0x20); \ + CHECK_BUSY(); \ + } while (0) + +#define EPD_UPDATE_GRAY() EPD_UPDATE_FAST() + + +#define EPD_UPDATE_PARTIAL() \ + do \ + { \ + writecommand(0x22); \ + writedata(0xFF); \ + writecommand(0x20); \ + } while (0); + + +// Macro to update display (partial refresh) +#define EPD_PART_UPDATE() \ + do \ + { \ + writecommand(0x22); \ + writedata(0xFF); \ + writecommand(0x20); \ + CHECK_BUSY(); \ + } while (0) + +// Macro to enter deep sleep +#define EPD_SLEEP() \ + do \ + { \ + writecommand(0x10); \ + writedata(0x01); \ + delay(100); \ + } while (0) + +#define EPD_INIT_GRAY() \ + do \ + { \ + writecommand(0x18); \ + writedata(0x80); \ + writecommand(0x22); \ + writedata(0xB1); \ + writecommand(0x20); \ + CHECK_BUSY(); \ + writecommand(0x1A); \ + writedata(0x5A); \ + writedata(0x00); \ + writecommand(0x22); \ + writedata(0x91); \ + writecommand(0x20); \ + CHECK_BUSY(); \ + } while (0) + + +#define EPD_INIT_PARTIAL() \ + do \ + { \ + writecommand(0x18); \ + writedata(0x80); \ + writecommand(0x3C); \ + writedata(0x80); \ + } while (0); + + #define EPD_INIT() \ + do \ + { \ + digitalWrite(TFT_RST, LOW); \ + delay(10); \ + digitalWrite(TFT_RST, HIGH); \ + delay(10); \ + CHECK_BUSY(); \ + writecommand(0x12); \ + CHECK_BUSY(); \ + writecommand(0x3C); \ + writedata(0x05); \ + writecommand(0x01); \ + writedata((EPD_HEIGHT - 1) % 256); \ + writedata((EPD_HEIGHT - 1) / 256); \ + writedata(0x00); \ + writecommand(0x11); \ + writedata(0x03); \ + writecommand(0x44); \ + writedata(0x00); \ + writedata((EPD_WIDTH / 8) - 1); \ + writecommand(0x45); \ + writedata(0x00); \ + writedata(0x00); \ + writedata((EPD_HEIGHT - 1) % 256); \ + writedata((EPD_HEIGHT - 1) / 256); \ + writecommand(0x18); \ + writedata(0x80); \ + writecommand(0x4E); \ + writedata(0x00); \ + writecommand(0x4F); \ + writedata(0x00); \ + writedata(0x00); \ + CHECK_BUSY(); \ + } while (0) + +// Macro to wake up device +#define EPD_WAKEUP() EPD_INIT() + +#define EPD_WAKEUP_GRAY() EPD_WAKEUP() + +#define EPD_WAKEUP_PARTIAL() \ + do \ + { \ + EPD_INIT(); \ + EPD_INIT_PARTIAL(); \ + } while (0); + +// Macro to set display window +#define EPD_SET_WINDOW(x1, y1, x2, y2) \ + do \ + { \ + writecommand(0x44);\ + writedata((x1 + 1) / 8); \ + writedata((x2 + 1) / 8 - 1);\ + writecommand(0x45); \ + writedata(y1 & 0xFF);\ + writedata(y1 >> 8);\ + writedata((y2) & 0xFF); \ + writedata((y2) >> 8); \ + writecommand(0x4E); \ + writedata((x1 + 1) / 8);\ + writecommand(0x4F); \ + writedata(y1 & 0xFF);\ + writedata(y1 >> 8);\ + } while (0) + + +#define EPD_PUSH_NEW_GRAY_COLORS(w, h, colors) \ + do \ + { \ + EPD_INIT_GRAY(); \ + uint16_t i, j, k; \ + uint8_t temp1, temp2, temp3; \ + writecommand(0x24); \ + for(i = 0; i < (TFT_WIDTH * TFT_HEIGHT) / 8; i++) \ + { \ + /* Read 4 input bytes = 8 pixels */ \ + uint8_t c0 = colors[i * 4 + 0]; \ + uint8_t c1 = colors[i * 4 + 1]; \ + uint8_t c2 = colors[i * 4 + 2]; \ + uint8_t c3 = colors[i * 4 + 3]; \ + /* Extract 8 pixels from bit5-4 and bit1-0 of each byte */ \ + uint8_t p0 = (c0 >> 4) & 0x03; \ + uint8_t p1 = (c0 >> 0) & 0x03; \ + uint8_t p2 = (c1 >> 4) & 0x03; \ + uint8_t p3 = (c1 >> 0) & 0x03; \ + uint8_t p4 = (c2 >> 4) & 0x03; \ + uint8_t p5 = (c2 >> 0) & 0x03; \ + uint8_t p6 = (c3 >> 4) & 0x03; \ + uint8_t p7 = (c3 >> 0) & 0x03; \ + /* Pack into original-style packed bytes (high 2-bit per pixel) */ \ + uint8_t packed_byte0 = (p0 << 6) | (p1 << 4) | (p2 << 2) | p3; \ + uint8_t packed_byte1 = (p4 << 6) | (p5 << 4) | (p6 << 2) | p7; \ + \ + temp3 = 0; \ + for(j = 0; j < 2; j++) \ + { \ + temp1 = (j == 0) ? packed_byte0 : packed_byte1; \ + for(k = 0; k < 4; k++) \ + { \ + temp2 = temp1 & 0xC0; \ + if(temp2 == 0xC0) \ + temp3 |= 0x01; \ + else if(temp2 == 0x00) \ + temp3 |= 0x00; \ + else if((temp2 >= 0x80) && (temp2 < 0xC0)) \ + temp3 |= 0x00; \ + else if(temp2 == 0x40) \ + temp3 |= 0x01; \ + if((j == 0 && k <= 3) || (j == 1 && k <= 2)) \ + { \ + temp3 <<= 1; \ + temp1 <<= 2; \ + } \ + } \ + } \ + writedata(~temp3); \ + } \ + \ + writecommand(0x26); \ + for(i = 0; i < (TFT_WIDTH * TFT_HEIGHT) / 8; i++) \ + { \ + uint8_t c0 = colors[i * 4 + 0]; \ + uint8_t c1 = colors[i * 4 + 1]; \ + uint8_t c2 = colors[i * 4 + 2]; \ + uint8_t c3 = colors[i * 4 + 3]; \ + uint8_t p0 = (c0 >> 4) & 0x03; \ + uint8_t p1 = (c0 >> 0) & 0x03; \ + uint8_t p2 = (c1 >> 4) & 0x03; \ + uint8_t p3 = (c1 >> 0) & 0x03; \ + uint8_t p4 = (c2 >> 4) & 0x03; \ + uint8_t p5 = (c2 >> 0) & 0x03; \ + uint8_t p6 = (c3 >> 4) & 0x03; \ + uint8_t p7 = (c3 >> 0) & 0x03; \ + uint8_t packed_byte0 = (p0 << 6) | (p1 << 4) | (p2 << 2) | p3; \ + uint8_t packed_byte1 = (p4 << 6) | (p5 << 4) | (p6 << 2) | p7; \ + \ + temp3 = 0; \ + for(j = 0; j < 2; j++) \ + { \ + temp1 = (j == 0) ? packed_byte0 : packed_byte1; \ + for(k = 0; k < 4; k++) \ + { \ + temp2 = temp1 & 0xC0; \ + if(temp2 == 0xC0) \ + temp3 |= 0x01; \ + else if(temp2 == 0x00) \ + temp3 |= 0x00; \ + else if((temp2 >= 0x80) && (temp2 < 0xC0)) \ + temp3 |= 0x01; \ + else if(temp2 == 0x40) \ + temp3 |= 0x00; \ + if((j == 0 && k <= 3) || (j == 1 && k <= 2)) \ + { \ + temp3 <<= 1; \ + temp1 <<= 2; \ + } \ + } \ + } \ + writedata(~temp3); \ + } \ + } while (0) + +#define EPD_PUSH_NEW_GRAY_COLORS_FLIP(w, h, colors) \ + do \ + { \ + EPD_PUSH_NEW_GRAY_COLORS(w, h, colors); \ + } while (0) + + +// Macro to push new color data (black/white RAM) +#define EPD_PUSH_NEW_COLORS(w, h, colors) \ + do \ + { \ + writecommand(0x24); \ + for (int i = 0; i < w * h / 8; i++) \ + { \ + writedata(colors[i]); \ + } \ + } while (0) + +#define EPD_PUSH_NEW_COLORS_FLIP(w, h, colors) \ + do \ + { \ + writecommand(0x24); \ + uint16_t bytes_per_row = (w) / 8; \ + for (uint16_t row = 0; row < (h); row++) \ + { \ + uint16_t start = row * bytes_per_row; \ + for (uint16_t col = 0; col < bytes_per_row; col++) \ + { \ + uint8_t b = colors[start + (bytes_per_row - 1 - col)]; \ + b = ((b & 0xF0) >> 4) | ((b & 0x0F) << 4); \ + b = ((b & 0xCC) >> 2) | ((b & 0x33) << 2); \ + b = ((b & 0xAA) >> 1) | ((b & 0x55) << 1); \ + writedata(b); \ + } \ + } \ + } while (0) + +// Macro to push old color data +#define EPD_PUSH_OLD_COLORS(w, h, colors) \ + do \ + { \ + writecommand(0x26); \ + for (int i = 0; i < w * h / 8; i++) \ + { \ + writedata(colors[i]); \ + } \ + } while (0) + +#define EPD_PUSH_OLD_COLORS_FLIP(w, h, colors) \ + do \ + { \ + writecommand(0x26); \ + uint16_t bytes_per_row = (w) / 8; \ + for (uint16_t row = 0; row < (h); row++) \ + { \ + uint16_t start = row * bytes_per_row; \ + for (uint16_t col = 0; col < bytes_per_row; col++) \ + { \ + uint8_t b = colors[start + (bytes_per_row - 1 - col)]; \ + b = ((b & 0xF0) >> 4) | ((b & 0x0F) << 4); \ + b = ((b & 0xCC) >> 2) | ((b & 0x33) << 2); \ + b = ((b & 0xAA) >> 1) | ((b & 0x55) << 1); \ + writedata(b); \ + } \ + } \ + } while (0) + +#define EPD_SET_TEMP(temp) \ + do \ + { \ + } while (0) +#endif \ No newline at end of file diff --git a/lib/Seeed_GFX/TFT_Drivers/SSD1681_Init.h b/lib/Seeed_GFX/TFT_Drivers/SSD1681_Init.h new file mode 100644 index 0000000..3ef6ca1 --- /dev/null +++ b/lib/Seeed_GFX/TFT_Drivers/SSD1681_Init.h @@ -0,0 +1,60 @@ +// SSD1681 initialization sequence + +{ +#ifdef TFT_BUSY + pinMode(TFT_BUSY, INPUT); +#endif +#ifdef TFT_ENABLE + pinMode(TFT_ENABLE, OUTPUT); + digitalWrite(TFT_ENABLE, HIGH); +#endif + // Hardware reset + digitalWrite(TFT_RST, LOW); + delay(10); + digitalWrite(TFT_RST, HIGH); + delay(10); + + // Wait for busy signal + CHECK_BUSY(); + writecommand(0x12); // Software reset + CHECK_BUSY(); + + // Set driver output control + writecommand(0x01); + writedata((EPD_HEIGHT - 1) % 256); // 199 % 256 = 199 + writedata((EPD_HEIGHT - 1) / 256); // 199 / 256 = 0 + writedata(0x00); + + // Set data transfer way + writecommand(0x11); + writedata(0x03); + + // Set RAM X address range + writecommand(0x44); + writedata(0x00); + writedata(EPD_WIDTH / 8 - 1); // 200 / 8 - 1 = 24 + + // Set RAM Y address range + writecommand(0x45); + writedata(0x00); + writedata(0x00); + writedata((EPD_HEIGHT - 1) % 256); // 199 % 256 = 199 + writedata((EPD_HEIGHT - 1) / 256); // 199 / 256 = 0 + + // Set border waveform + writecommand(0x3C); + writedata(0x05); + + // Read built-in temperature sensor + writecommand(0x18); + writedata(0x80); + + // Set RAM address counter + writecommand(0x4E); + writedata(0x00); + writecommand(0x4F); + writedata(0x00); // 199 % 256 = 199 + writedata(0x00); // 199 / 256 = 0 + CHECK_BUSY(); + +} diff --git a/lib/Seeed_GFX/TFT_Drivers/SSD1681_Rotation.h b/lib/Seeed_GFX/TFT_Drivers/SSD1681_Rotation.h new file mode 100644 index 0000000..89bbc3f --- /dev/null +++ b/lib/Seeed_GFX/TFT_Drivers/SSD1681_Rotation.h @@ -0,0 +1,25 @@ +// SSD1681 screen rotation sequence + +rotation = m % 4; + +writecommand(0x11); // Data entry mode +switch (rotation) +{ +case 0: // Portrait mode (0 degrees) + writedata(0x01); // Y decrement, X increment + _width = EPD_WIDTH; + _height = EPD_HEIGHT; + break; +case 2: // Inverted portrait mode (180 degrees) + writedata(0x02); // Y increment, X decrement + _width = EPD_WIDTH; + _height = EPD_HEIGHT; + break; +case 1: // Landscape mode (90 degrees) +case 3: // Inverted landscape mode (270 degrees) + // SSD1681 does not directly support 90° and 270° rotation, not implemented + writedata(0x01); // Default to 0 degrees + _width = EPD_WIDTH; + _height = EPD_HEIGHT; + break; +} \ No newline at end of file diff --git a/lib/Seeed_GFX/TFT_Drivers/SSD1683_Defines.h b/lib/Seeed_GFX/TFT_Drivers/SSD1683_Defines.h new file mode 100644 index 0000000..71e1c97 --- /dev/null +++ b/lib/Seeed_GFX/TFT_Drivers/SSD1683_Defines.h @@ -0,0 +1,376 @@ +#ifndef __SSD1680_DEFINES_H__ +#define __SSD1680_DEFINES_H__ + +// Define screen resolution +#ifndef EPD_WIDTH +#define EPD_WIDTH 128 +#endif + +#ifndef EPD_HEIGHT +#define EPD_HEIGHT 296 +#endif + +#ifndef TFT_WIDTH +#define TFT_WIDTH EPD_WIDTH +#endif + +#ifndef TFT_HEIGHT +#define TFT_HEIGHT EPD_HEIGHT +#endif + +// Define color depth (1 bit for e-ink display) +#define EPD_COLOR_DEPTH 1 + +#define USE_PARTIAL_EPAPER +#define USE_MUTIGRAY_EPAPER +#define GRAY_LEVEL4 4 + +// Define no operation command +#define EPD_NOP 0xFF + +// Define common command macros +#define EPD_PNLSET 0x01 // Driver output control +#define EPD_DISPON 0xFF // No direct display on command +#define EPD_DISPOFF 0xFF // No direct display off command +#define EPD_SLPIN 0x10 // Enter deep sleep +#define EPD_SLPOUT 0xFF // No direct sleep out command +#define EPD_PTLIN 0x3C // Partial display in (BorderWaveform) +#define EPD_PTLOUT 0xFF // No direct partial display out command +#define EPD_PTLW 0x4E // Set RAM address counter + +#define TFT_SWRST 0x12 // Software reset +#define TFT_CASET 0x44 // Set RAM X address start/end position +#define TFT_PASET 0x45 // Set RAM Y address start/end position +#define TFT_RAMWR 0x24 // Write to RAM +#define TFT_RAMRD 0xFF // No direct read RAM command +#define TFT_INVON 0xFF // No inversion on command +#define TFT_INVOFF 0xFF // No inversion off command + +#define TFT_INIT_DELAY 0 + +// Macro to check BUSY signal +#ifdef TFT_BUSY +#define CHECK_BUSY() \ + do \ + { \ + while (digitalRead(TFT_BUSY)) \ + ; \ + } while (0) +#else +#define CHECK_BUSY() +#endif + +// Macro to update display +#define EPD_UPDATE() \ + do \ + { \ + writecommand(0x22); \ + writedata(0xF7); \ + writecommand(0x20); \ + CHECK_BUSY(); \ + } while (0) + +#define EPD_UPDATE_FAST() \ + do \ + { \ + writecommand(0x22); \ + writedata(0xCF); \ + writecommand(0x20); \ + CHECK_BUSY(); \ + } while (0) + +#define EPD_UPDATE_GRAY() EPD_UPDATE_FAST() + +#define EPD_UPDATE_PARTIAL() \ + do \ + { \ + writecommand(0x22); \ + writedata(0xFF); \ + writecommand(0x20); \ + } while (0); + +// Macro to enter deep sleep +#define EPD_SLEEP() \ + do \ + { \ + writecommand(0x10); \ + writedata(0x01); \ + delay(100); \ + } while (0) + +#define EPD_INIT() \ + do \ + { \ + digitalWrite(TFT_RST, LOW); \ + delay(10); \ + digitalWrite(TFT_RST, HIGH); \ + delay(10); \ + CHECK_BUSY(); \ + writecommand(0x12); \ + CHECK_BUSY(); \ + writecommand(0x01); \ + writedata((EPD_HEIGHT - 1) % 256); \ + writedata((EPD_HEIGHT - 1) / 256); \ + writedata(0x00); \ + writecommand(0x11); \ + writedata(0x03); \ + writecommand(0x44); \ + writedata(0x00); \ + writedata(EPD_WIDTH / 8 - 1); \ + writecommand(0x45); \ + writedata(0x00); \ + writedata(0x00); \ + writedata((EPD_HEIGHT - 1) % 256); \ + writedata((EPD_HEIGHT - 1) / 256); \ + writecommand(0x3C); \ + writedata(0x05); \ + writecommand(0x21); \ + writedata(0x40); \ + writedata(0x00); \ + writecommand(0x18); \ + writedata(0x80); \ + writecommand(0x4E); \ + writedata(0x00); \ + writecommand(0x4F); \ + writedata(0x00); \ + writedata(0x00); \ + CHECK_BUSY(); \ + } while (0) + +#define EPD_INIT_GRAY() \ + do \ + { \ + writecommand(0x21); \ + writedata(0x00); \ + writedata(0x00); \ + writecommand(0x18); \ + writedata(0x80); \ + writecommand(0x22); \ + writedata(0xB1); \ + writecommand(0x20); \ + CHECK_BUSY(); \ + writecommand(0x1A); \ + writedata(0x5A); \ + writedata(0x00); \ + writecommand(0x22); \ + writedata(0x91); \ + writecommand(0x20); \ + CHECK_BUSY(); \ + } while (0) + +#define EPD_INIT_PARTIAL() \ + do \ + { \ + writecommand(0x21); \ + writedata(0x00); \ + writedata(0x00); \ + writecommand(0x18); \ + writedata(0x80); \ + writecommand(0x3C); \ + writedata(0x80); \ + } while (0); + +// Macro to wake up device +#define EPD_WAKEUP() EPD_INIT() + +#define EPD_WAKEUP_GRAY() \ + do \ + { \ + EPD_INIT(); \ + } while (0); + +#define EPD_WAKEUP_PARTIAL() \ + do \ + { \ + EPD_INIT(); \ + EPD_INIT_PARTIAL(); \ + } while (0); + +// Macro to set display window +#define EPD_SET_WINDOW(x1, y1, x2, y2) \ + do \ + { \ + writecommand(0x44);\ + writedata((x1 + 1) / 8); \ + writedata((x2 + 1) / 8 - 1);\ + writecommand(0x45); \ + writedata((y1) & 0xFF); \ + writedata((y1) >> 8); \ + writedata(y2 & 0xFF);\ + writedata(y2 >> 8);\ + writecommand(0x4E); \ + writedata((x1 + 1) / 8);\ + writecommand(0x4F); \ + writedata(y1 & 0xFF);\ + writedata(y1 >> 8);\ + } while (0) + +// Macro to push new color data +#define EPD_PUSH_NEW_COLORS(w, h, colors) \ + do \ + { \ + writecommand(0x24); \ + for (uint16_t i = 0; i < w * h / 8; i++) \ + { \ + writedata(colors[i]); \ + } \ + } while (0) + +#define EPD_PUSH_NEW_COLORS_FLIP(w, h, colors) \ + do \ + { \ + writecommand(0x24); \ + uint16_t bytes_per_row = (w) / 8; \ + for (uint16_t row = 0; row < (h); row++) \ + { \ + uint16_t start = row * bytes_per_row; \ + for (uint16_t col = 0; col < bytes_per_row; col++) \ + { \ + uint8_t b = colors[start + (bytes_per_row - 1 - col)]; \ + b = ((b & 0xF0) >> 4) | ((b & 0x0F) << 4); \ + b = ((b & 0xCC) >> 2) | ((b & 0x33) << 2); \ + b = ((b & 0xAA) >> 1) | ((b & 0x55) << 1); \ + writedata(b); \ + } \ + } \ + } while (0) + + +#define EPD_PUSH_NEW_GRAY_COLORS(w, h, colors) \ + do \ + { \ + EPD_INIT_GRAY(); \ + uint16_t i, j, k; \ + uint8_t temp1, temp2, temp3; \ + writecommand(0x24); \ + for(i = 0; i < (TFT_WIDTH * TFT_HEIGHT) / 8; i++) \ + { \ + /* Read 4 input bytes = 8 pixels */ \ + uint8_t c0 = colors[i * 4 + 0]; \ + uint8_t c1 = colors[i * 4 + 1]; \ + uint8_t c2 = colors[i * 4 + 2]; \ + uint8_t c3 = colors[i * 4 + 3]; \ + /* Extract 8 pixels from bit5-4 and bit1-0 of each byte */ \ + uint8_t p0 = (c0 >> 4) & 0x03; \ + uint8_t p1 = (c0 >> 0) & 0x03; \ + uint8_t p2 = (c1 >> 4) & 0x03; \ + uint8_t p3 = (c1 >> 0) & 0x03; \ + uint8_t p4 = (c2 >> 4) & 0x03; \ + uint8_t p5 = (c2 >> 0) & 0x03; \ + uint8_t p6 = (c3 >> 4) & 0x03; \ + uint8_t p7 = (c3 >> 0) & 0x03; \ + /* Pack into original-style packed bytes (high 2-bit per pixel) */ \ + uint8_t packed_byte0 = (p0 << 6) | (p1 << 4) | (p2 << 2) | p3; \ + uint8_t packed_byte1 = (p4 << 6) | (p5 << 4) | (p6 << 2) | p7; \ + \ + temp3 = 0; \ + for(j = 0; j < 2; j++) \ + { \ + temp1 = (j == 0) ? packed_byte0 : packed_byte1; \ + for(k = 0; k < 4; k++) \ + { \ + temp2 = temp1 & 0xC0; \ + if(temp2 == 0xC0) \ + temp3 |= 0x01; \ + else if(temp2 == 0x00) \ + temp3 |= 0x00; \ + else if((temp2 >= 0x80) && (temp2 < 0xC0)) \ + temp3 |= 0x00; \ + else if(temp2 == 0x40) \ + temp3 |= 0x01; \ + if((j == 0 && k <= 3) || (j == 1 && k <= 2)) \ + { \ + temp3 <<= 1; \ + temp1 <<= 2; \ + } \ + } \ + } \ + writedata(temp3); \ + } \ + \ + writecommand(0x26); \ + for(i = 0; i < (TFT_WIDTH * TFT_HEIGHT) / 8; i++) \ + { \ + uint8_t c0 = colors[i * 4 + 0]; \ + uint8_t c1 = colors[i * 4 + 1]; \ + uint8_t c2 = colors[i * 4 + 2]; \ + uint8_t c3 = colors[i * 4 + 3]; \ + uint8_t p0 = (c0 >> 4) & 0x03; \ + uint8_t p1 = (c0 >> 0) & 0x03; \ + uint8_t p2 = (c1 >> 4) & 0x03; \ + uint8_t p3 = (c1 >> 0) & 0x03; \ + uint8_t p4 = (c2 >> 4) & 0x03; \ + uint8_t p5 = (c2 >> 0) & 0x03; \ + uint8_t p6 = (c3 >> 4) & 0x03; \ + uint8_t p7 = (c3 >> 0) & 0x03; \ + uint8_t packed_byte0 = (p0 << 6) | (p1 << 4) | (p2 << 2) | p3; \ + uint8_t packed_byte1 = (p4 << 6) | (p5 << 4) | (p6 << 2) | p7; \ + \ + temp3 = 0; \ + for(j = 0; j < 2; j++) \ + { \ + temp1 = (j == 0) ? packed_byte0 : packed_byte1; \ + for(k = 0; k < 4; k++) \ + { \ + temp2 = temp1 & 0xC0; \ + if(temp2 == 0xC0) \ + temp3 |= 0x01; \ + else if(temp2 == 0x00) \ + temp3 |= 0x00; \ + else if((temp2 >= 0x80) && (temp2 < 0xC0)) \ + temp3 |= 0x01; \ + else if(temp2 == 0x40) \ + temp3 |= 0x00; \ + if((j == 0 && k <= 3) || (j == 1 && k <= 2)) \ + { \ + temp3 <<= 1; \ + temp1 <<= 2; \ + } \ + } \ + } \ + writedata(temp3); \ + } \ + } while (0) + +#define EPD_PUSH_NEW_GRAY_COLORS_FLIP(w, h, colors) \ + do \ + { \ + EPD_PUSH_NEW_GRAY_COLORS(w, h, colors); \ + } while (0) + + +// Macro to push old color data +#define EPD_PUSH_OLD_COLORS(w, h, colors) \ + do \ + { \ + writecommand(0x26); \ + for (uint16_t i = 0; i < w * h / 8; i++) \ + { \ + writedata(colors[i]); \ + } \ + } while (0) + +#define EPD_PUSH_OLD_COLORS_FLIP(w, h, colors) \ + do \ + { \ + writecommand(0x26); \ + uint16_t bytes_per_row = (w) / 8; \ + for (uint16_t row = 0; row < (h); row++) \ + { \ + uint16_t start = row * bytes_per_row; \ + for (uint16_t col = 0; col < bytes_per_row; col++) \ + { \ + uint8_t b = colors[start + (bytes_per_row - 1 - col)]; \ + b = ((b & 0xF0) >> 4) | ((b & 0x0F) << 4); \ + b = ((b & 0xCC) >> 2) | ((b & 0x33) << 2); \ + b = ((b & 0xAA) >> 1) | ((b & 0x55) << 1); \ + writedata(b); \ + } \ + } \ + }while (0) + +#define EPD_SET_TEMP(temp) \ + do \ + { \ + } while (0) +#endif \ No newline at end of file diff --git a/lib/Seeed_GFX/TFT_Drivers/SSD1683_Init.h b/lib/Seeed_GFX/TFT_Drivers/SSD1683_Init.h new file mode 100644 index 0000000..95df718 --- /dev/null +++ b/lib/Seeed_GFX/TFT_Drivers/SSD1683_Init.h @@ -0,0 +1,65 @@ +// This is the command sequence that initializes the SSD1680 driver + +{ +#ifdef TFT_BUSY + pinMode(TFT_BUSY, INPUT); +#endif +#ifdef TFT_ENABLE + pinMode(TFT_ENABLE, OUTPUT); + digitalWrite(TFT_ENABLE, HIGH); +#endif + // Hardware reset + digitalWrite(TFT_RST, LOW); + delay(10); + digitalWrite(TFT_RST, HIGH); + delay(10); + + // Wait for busy signal + CHECK_BUSY(); + writecommand(0x12); // Software reset + CHECK_BUSY(); + + // Set driver output control + writecommand(0x01); + writedata((EPD_HEIGHT - 1) % 256); + writedata((EPD_HEIGHT - 1) / 256); + writedata(0x00); + + // Set data entry mode + writecommand(0x11); + writedata(0x03); + + // Set RAM X address range + writecommand(0x44); + writedata(0x00); + writedata(EPD_WIDTH / 8 - 1); + + // Set RAM Y address range + writecommand(0x45); + writedata(0x00); + writedata(0x00); + writedata((EPD_HEIGHT - 1) % 256); + writedata((EPD_HEIGHT - 1) / 256); + + // Set border waveform + writecommand(0x3C); + writedata(0x05); + + // Display update control + writecommand(0x21); + writedata(0x40); + writedata(0x00); + + // Read built-in temperature sensor + writecommand(0x18); + writedata(0x80); + + // Set RAM address counter + writecommand(0x4E); + writedata(0x00); + writecommand(0x4F); + writedata(0x00); + writedata(0x00); + CHECK_BUSY(); + +} diff --git a/lib/Seeed_GFX/TFT_Drivers/SSD1683_Rotation.h b/lib/Seeed_GFX/TFT_Drivers/SSD1683_Rotation.h new file mode 100644 index 0000000..77b4e4c --- /dev/null +++ b/lib/Seeed_GFX/TFT_Drivers/SSD1683_Rotation.h @@ -0,0 +1,27 @@ +// SSD1683_Rotation.h + +rotation = m % 4; + +writecommand(0x11); // Data entry mode +switch (rotation) { + case 0: // Portrait (default) + writedata(0x01); + _width = _init_width; + _height = _init_height; + break; + case 1: // Landscape (90°) + writedata(0x03); // Adjust based on datasheet + _width = _init_height; + _height = _init_width; + break; + case 2: // Inverted Portrait (180°) + writedata(0x00); // Adjust based on datasheet + _width = _init_width; + _height = _init_height; + break; + case 3: // Inverted Landscape (270°) + writedata(0x02); // Adjust based on datasheet + _width = _init_height; + _height = _init_width; + break; +} \ No newline at end of file diff --git a/lib/Seeed_GFX/TFT_Drivers/SSD1963_Defines.h b/lib/Seeed_GFX/TFT_Drivers/SSD1963_Defines.h new file mode 100644 index 0000000..f3ca7d4 --- /dev/null +++ b/lib/Seeed_GFX/TFT_Drivers/SSD1963_Defines.h @@ -0,0 +1,59 @@ +// Change the width and height if required (defined in portrait mode) +// or use the constructor to over-ride defaults +#if defined (SSD1963_480_DRIVER) + #define TFT_WIDTH 272 + #define TFT_HEIGHT 480 +#elif defined (SSD1963_800_DRIVER) + #define TFT_WIDTH 480 + #define TFT_HEIGHT 800 +#elif defined (SSD1963_800ALT_DRIVER) + #define TFT_WIDTH 480 + #define TFT_HEIGHT 800 +#elif defined (SSD1963_800BD_DRIVER) + #define TFT_WIDTH 480 + #define TFT_HEIGHT 800 +#endif + +//Set driver type common to all initialisation options +#ifndef SSD1963_DRIVER + #define SSD1963_DRIVER +#endif + +// Delay between some initialisation commands +#define TFT_INIT_DELAY 0x80 // Not used unless commandlist invoked + +// Generic commands used by TFT_eSPI.cpp +#define TFT_NOP 0x00 +#define TFT_SWRST 0x01 + +#define TFT_INVOFF 0x20 +#define TFT_INVON 0x21 + +#define TFT_DISPOFF 0x28 +#define TFT_DISPON 0x29 + +#define TFT_CASET 0x2A +#define TFT_PASET 0x2B +#define TFT_RAMWR 0x2C + +#define TFT_RAMRD 0x2E +#define TFT_IDXRD 0xDD // ILI9341 only, indexed control register read + +#define TFT_MADCTL 0x36 +#define TFT_MAD_MY 0x80 +#define TFT_MAD_MX 0x40 +#define TFT_MAD_MV 0x20 +#define TFT_MAD_ML 0x10 +#define TFT_MAD_BGR 0x08 +#define TFT_MAD_MH 0x04 +#define TFT_MAD_RGB 0x00 + +#ifdef TFT_RGB_ORDER + #if (TFT_RGB_ORDER == 1) + #define TFT_MAD_COLOR_ORDER TFT_MAD_RGB + #else + #define TFT_MAD_COLOR_ORDER TFT_MAD_BGR + #endif +#else + #define TFT_MAD_COLOR_ORDER TFT_MAD_BGR +#endif diff --git a/lib/Seeed_GFX/TFT_Drivers/SSD1963_Init.h b/lib/Seeed_GFX/TFT_Drivers/SSD1963_Init.h new file mode 100644 index 0000000..87d4ee6 --- /dev/null +++ b/lib/Seeed_GFX/TFT_Drivers/SSD1963_Init.h @@ -0,0 +1,398 @@ +#if defined (SSD1963_480_DRIVER) + + writecommand(0xE2); //PLL multiplier, set PLL clock to 120M + writedata(0x23); //N=0x36 for 6.5M, 0x23 for 10M crystal + writedata(0x02); + writedata(0x54); + writecommand(0xE0); // PLL enable + writedata(0x01); + + delay(10); + + writecommand(0xE0); + writedata(0x03); + + delay(10); + + writecommand(0x01); // software reset + + delay(100); + + writecommand(0xE6); //PLL setting for PCLK, depends on resolution + writedata(0x01); + writedata(0x1F); + writedata(0xFF); + + writecommand(0xB0); //LCD SPECIFICATION + writedata(0x20); + writedata(0x00); + writedata(0x01); //Set HDP 479 + writedata(0xDF); + writedata(0x01); //Set VDP 271 + writedata(0x0F); + writedata(0x00); + + writecommand(0xB4); //HSYNC + writedata(0x02); //Set HT 531 + writedata(0x13); + writedata(0x00); //Set HPS 8 + writedata(0x08); + writedata(0x2B); //Set HPW 43 + writedata(0x00); //Set LPS 2 + writedata(0x02); + writedata(0x00); + + writecommand(0xB6); //VSYNC + writedata(0x01); //Set VT 288 + writedata(0x20); + writedata(0x00); //Set VPS 4 + writedata(0x04); + writedata(0x0c); //Set VPW 12 + writedata(0x00); //Set FPS 2 + writedata(0x02); + + writecommand(0xBA); + writedata(0x0F); //GPIO[3:0] out 1 + + writecommand(0xB8); + writedata(0x07); //GPIO3=input, GPIO[2:0]=output + writedata(0x01); //GPIO0 normal + + writecommand(0x36); //rotation + writedata(0x21 | TFT_MAD_COLOR_ORDER); + + writecommand(0xF0); //pixel data interface + writedata(0x00); //8-bit bus + + delay(1); + + writecommand(0xB8); + writedata(0x0f); //GPIO is controlled by host GPIO[3:0]=output GPIO[0]=1 LCD ON GPIO[0]=1 LCD OFF + writedata(0x01); //GPIO0 normal + + writecommand(0xBA); + writedata(0x01); //GPIO[0] out 1 --- LCD display on/off control PIN + + writecommand(0x2A); + writedata(0); + writedata(0); + writedata((271 & 0xFF00)>>8); + writedata(271 & 0xFF); + + writecommand(0x2B); + writedata(0); + writedata(0); + writedata((479 & 0xFF00)>>8); + writedata(479 & 0xFF); + + writecommand(0x2C); + + writecommand(0x29); //display on + + writecommand(0xBE); //set PWM for B/L + writedata(0x06); + writedata(0xf0); + writedata(0x01); + writedata(0xf0); + writedata(0x00); + writedata(0x00); + + writecommand(0xd0); + writedata(0x0d); + + writecommand(0x2C); + +#elif defined (SSD1963_800_DRIVER) + + writecommand(0xE2); //PLL multiplier, set PLL clock to 120M + writedata(0x1E); //N=0x36 for 6.5M, 0x23 for 10M crystal + writedata(0x02); + writedata(0x54); + writecommand(0xE0); // PLL enable + writedata(0x01); + + delay(10); + + writecommand(0xE0); + writedata(0x03); + + delay(10); + + writecommand(0x01); // software reset + + delay(100); + + writecommand(0xE6); //PLL setting for PCLK, depends on resolution + writedata(0x03); + writedata(0xFF); + writedata(0xFF); + + writecommand(0xB0); //LCD SPECIFICATION + writedata(0x20); + writedata(0x00); + writedata(0x03); //Set HDP 799 + writedata(0x1F); + writedata(0x01); //Set VDP 479 + writedata(0xDF); + writedata(0x00); + + writecommand(0xB4); //HSYNC + writedata(0x03); //Set HT 928 + writedata(0xA0); + writedata(0x00); //Set HPS 46 + writedata(0x2E); + writedata(0x30); //Set HPW 48 + writedata(0x00); //Set LPS 15 + writedata(0x0F); + writedata(0x00); + + writecommand(0xB6); //VSYNC + writedata(0x02); //Set VT 525 + writedata(0x0D); + writedata(0x00); //Set VPS 16 + writedata(0x10); + writedata(0x10); //Set VPW 16 + writedata(0x00); //Set FPS 8 + writedata(0x08); + + writecommand(0xBA); + writedata(0x0F); //GPIO[3:0] out 1 + + writecommand(0xB8); + writedata(0x07); //GPIO3=input, GPIO[2:0]=output + writedata(0x01); //GPIO0 normal + + writecommand(0x36); //rotation + writedata(0x21 | TFT_MAD_COLOR_ORDER); + + writecommand(0xF0); //pixel data interface + writedata(0x00); //8-bit bus + + delay(1); + + writecommand(0xB8); + writedata(0x0f); //GPIO is controlled by host GPIO[3:0]=output GPIO[0]=1 LCD ON GPIO[0]=1 LCD OFF + writedata(0x01); //GPIO0 normal + + writecommand(0xBA); + writedata(0x01); //GPIO[0] out 1 --- LCD display on/off control PIN + + writecommand(0x2A); + writedata(0); + writedata(0); + writedata((479 & 0xFF00)>>8); + writedata(479 & 0xFF); + + writecommand(0x2B); + writedata(0); + writedata(0); + writedata((799 & 0xFF00)>>8); + writedata(799 & 0xFF); + + writecommand(0x2C); + + writecommand(0x29); //display on + + writecommand(0xBE); //set PWM for B/L + writedata(0x06); + writedata(0xf0); + writedata(0x01); + writedata(0xf0); + writedata(0x00); + writedata(0x00); + + writecommand(0xd0); + writedata(0x0d); + + writecommand(0x2C); + +#elif defined (SSD1963_800ALT_DRIVER) + + writecommand(0xE2); //PLL multiplier, set PLL clock to 120M + writedata(0x23); //N=0x36 for 6.5M, 0x23 for 10M crystal + writedata(0x02); + writedata(0x04); + writecommand(0xE0); // PLL enable + writedata(0x01); + + delay(10); + + writecommand(0xE0); + writedata(0x03); + + delay(10); + + writecommand(0x01); // software reset + + delay(100); + + writecommand(0xE6); //PLL setting for PCLK, depends on resolution + writedata(0x04); + writedata(0x93); + writedata(0xE0); + + writecommand(0xB0); //LCD SPECIFICATION + writedata(0x00); // 0x24 + writedata(0x00); + writedata(0x03); //Set HDP 799 + writedata(0x1F); + writedata(0x01); //Set VDP 479 + writedata(0xDF); + writedata(0x00); + + writecommand(0xB4); //HSYNC + writedata(0x03); //Set HT 928 + writedata(0xA0); + writedata(0x00); //Set HPS 46 + writedata(0x2E); + writedata(0x30); //Set HPW 48 + writedata(0x00); //Set LPS 15 + writedata(0x0F); + writedata(0x00); + + writecommand(0xB6); //VSYNC + writedata(0x02); //Set VT 525 + writedata(0x0D); + writedata(0x00); //Set VPS 16 + writedata(0x10); + writedata(0x10); //Set VPW 16 + writedata(0x00); //Set FPS 8 + writedata(0x08); + + writecommand(0xBA); + writedata(0x05); //GPIO[3:0] out 1 + + writecommand(0xB8); + writedata(0x07); //GPIO3=input, GPIO[2:0]=output + writedata(0x01); //GPIO0 normal + + writecommand(0x36); //rotation + writedata(0x21 | TFT_MAD_COLOR_ORDER); // -- Set rotation + + writecommand(0xF0); //pixel data interface + writedata(0x00); //8-bit bus + + delay(10); + + writecommand(0x2A); + writedata(0); + writedata(0); + writedata((479 & 0xFF00)>>8); + writedata(479 & 0xFF); + + writecommand(0x2B); + writedata(0); + writedata(0); + writedata((799 & 0xFF00)>>8); + writedata(799 & 0xFF); + + writecommand(0x2C); + + writecommand(0x29); //display on + + writecommand(0xBE); //set PWM for B/L + writedata(0x06); + writedata(0xF0); + writedata(0x01); + writedata(0xF0); + writedata(0x00); + writedata(0x00); + + writecommand(0xD0); + writedata(0x0D); + + writecommand(0x2C); + +#elif defined (SSD1963_800BD_DRIVER) // Copied from Buy Display code + + writecommand(0xE2); //PLL multiplier, set PLL clock to 120M + writedata(0x23); //N=0x36 for 6.5M, 0x23 for 10M crystal + writedata(0x02); + writedata(0x54); + + writecommand(0xE0); // PLL enable + writedata(0x01); + + delay(10); + + writecommand(0xE0); + writedata(0x03); + + delay(10); + + writecommand(0x01); // software reset + + delay(100); + + writecommand(0xE6); //PLL setting for PCLK, depends on resolution + writedata(0x03); + writedata(0x33); + writedata(0x33); + + writecommand(0xB0); //LCD SPECIFICATION + writedata(0x20); + writedata(0x00); + writedata(799 >> 8); //Set HDP 799 + writedata(799 & 0xFF); + writedata(479 >> 8); //Set VDP 479 + writedata(479 & 0xFF); + writedata(0x00); + + writecommand(0xB4); //HSYNC + writedata(0x04); //Set HT + writedata(0x1F); + writedata(0x00); //Set HPS + writedata(0xD2); + writedata(0x00); //Set HPW + writedata(0x00); //Set LPS + writedata(0x00); + writedata(0x00); + + writecommand(0xB6); //VSYNC + writedata(0x02); //Set VT + writedata(0x0C); + writedata(0x00); //Set VPS + writedata(0x22); + writedata(0x00); //Set VPW + writedata(0x00); //Set FPS + writedata(0x00); + + writecommand(0xB8); + writedata(0x0F); //GPIO3=input, GPIO[2:0]=output + writedata(0x01); //GPIO0 normal + + writecommand(0xBA); + writedata(0x01); //GPIO[0] out 1 --- LCD display on/off control PIN + + writecommand(0x36); //rotation + writedata(0x21 | TFT_MAD_COLOR_ORDER); //set to rotate + + //writecommand(0x003A); //Set the current pixel format for RGB image data + //writedata(0x0050); //16-bit/pixel + + writecommand(0xF0); //pixel data interface + writedata(0x00); //000 = 8-bit bus, 011 = 16-bit, 110 = 9-bit + + writecommand(0xBC); + writedata(0x40); //contrast value + writedata(0x80); //brightness value + writedata(0x40); //saturation value + writedata(0x01); //Post Processor Enable + + + delay(10); + + writecommand(0x29); //display on + + writecommand(0xBE); //set PWM for B/L + writedata(0x06); + writedata(0x80); + writedata(0x01); + writedata(0xF0); + writedata(0x00); + writedata(0x00); + + writecommand(0xD0); + writedata(0x0D); + +#endif \ No newline at end of file diff --git a/lib/Seeed_GFX/TFT_Drivers/SSD1963_Rotation.h b/lib/Seeed_GFX/TFT_Drivers/SSD1963_Rotation.h new file mode 100644 index 0000000..6e61171 --- /dev/null +++ b/lib/Seeed_GFX/TFT_Drivers/SSD1963_Rotation.h @@ -0,0 +1,29 @@ + +// This is the command sequence that rotates the SSD1963 driver coordinate frame + + rotation = m % 4; // Limit the range of values to 0-3 + + writecommand(TFT_MADCTL); + switch (rotation) { + case 0: + writedata(0x21 | TFT_MAD_COLOR_ORDER); + _width = _init_width; + _height = _init_height; + break; + case 1: + writedata(0x00 | TFT_MAD_COLOR_ORDER); + _width = _init_height; + _height = _init_width; + break; + case 2: + writedata(0x22 | TFT_MAD_COLOR_ORDER); + _width = _init_width; + _height = _init_height; + break; + case 3: + writedata(0x03 | TFT_MAD_COLOR_ORDER); + _width = _init_height; + _height = _init_width; + break; + + } diff --git a/lib/Seeed_GFX/TFT_Drivers/SSD2677_Defines.h b/lib/Seeed_GFX/TFT_Drivers/SSD2677_Defines.h new file mode 100644 index 0000000..82bfcda --- /dev/null +++ b/lib/Seeed_GFX/TFT_Drivers/SSD2677_Defines.h @@ -0,0 +1,167 @@ +#ifndef EPD_WIDTH +#define EPD_WIDTH 800 +#endif + +#ifndef EPD_HEIGHT +#define EPD_HEIGHT 480 +#endif + +#ifndef TFT_WIDTH +#define TFT_WIDTH EPD_WIDTH +#endif + +#ifndef TFT_HEIGHT +#define TFT_HEIGHT EPD_HEIGHT +#endif + +#define EPD_COLOR_DEPTH 4 + + + +#define EPD_NOP 0xFF // No operation command (not supported) +#define EPD_PNLSET 0x00 // Panel setting (R00H PSR) +#define EPD_DISPON 0x04 // Power on (R04H PON) +#define EPD_DISPOFF 0x02 // Power off (R02H POF) +#define EPD_SLPIN 0x07 // Enter deep sleep (R07H DSLP) +#define EPD_SLPOUT 0xFF // Exit sleep (not supported, requires wake-up) +#define EPD_PTLIN 0x91 // Partial display in (R91H PTIN) +#define EPD_PTLOUT 0x92 // Partial display out (R92H PTOUT) +#define EPD_PTLW 0x90 // Partial display window setting (R90H PTL) + +#define TFT_SWRST 0xFF // Software reset (not supported) +#define TFT_CASET 0xFF // Column address setting (not supported) +#define TFT_PASET 0xFF // Page address setting (not supported) +#define TFT_RAMWR 0x13 // Write RAM (R13H DTM2, red data) +#define TFT_RAMRD 0xFF // Read RAM (not supported) +#define TFT_INVON 0xFF // Display inversion on (not supported) +#define TFT_INVOFF 0xFF // Display inversion off (not supported) +#define TFT_INIT_DELAY 0 // Initialization delay (none) + +#ifdef TFT_BUSY +#define CHECK_BUSY() \ + do \ + { \ + while (!digitalRead(TFT_BUSY)) \ + ; \ + } while (0) +#else +#define CHECK_BUSY() +#endif + +#define EPD_INIT() \ + do \ + { \ + writecommand(0x00); writedata(0x2B); writedata(0x29); \ + writecommand(0x06); writedata(0x0F); writedata(0x8B); writedata(0x93); writedata(0xC1); \ + writecommand(0x50); writedata(0x37); \ + writecommand(0x30); writedata(0x08); \ + writecommand(0x61); writedata(800/256); writedata(800%256); writedata(680/256); writedata(680%256); \ + writecommand(0x62); writedata(0x76); writedata(0x76); writedata(0x76); writedata(0x5A); writedata(0x9D); writedata(0x8A); writedata(0x76); writedata(0x62); \ + writecommand(0x65); writedata(0x00); writedata(0x00); writedata(0x00); writedata(0x00); \ + writecommand(0xE0); writedata(0x10); \ + writecommand(0xE7); writedata(0xA4); \ + writecommand(0xE9); writedata(0x01); \ + writecommand(0x04); \ + CHECK_BUSY(); \ + } while (0) + +#define EPD_UPDATE() \ + do \ + { \ + writecommand(0x12); \ + writedata(0x00); \ + CHECK_BUSY(); \ + } while (0) + +#define EPD_SLEEP() \ + do \ + { \ + writecommand(0x02); \ + CHECK_BUSY(); \ + delay(100); \ + writecommand(0x07); \ + writedata(0xA5); \ + } while (0) + +#define EPD_WAKEUP() \ + do \ + { \ + digitalWrite(TFT_RST, LOW); \ + delay(20); \ + digitalWrite(TFT_RST, HIGH); \ + delay(20); \ + CHECK_BUSY(); \ + EPD_INIT(); \ + } while (0) + +#define EPD_SET_WINDOW(x1, y1, x2, y2) \ + do \ + { \ + } while (0) + +#define COLOR_GET(color) ( \ + (color) == 0x00 ? 0x01 : \ + (color) == 0x0B ? 0x02 : \ + (color) == 0x06 ? 0x03 : \ + (color) == 0x0F ? 0x00 : \ + 0x00 \ +) + +#define EPD_PUSH_NEW_COLORS(w, h, colors) \ + do \ + { \ + uint16_t bytes_per_row = (w) / 2; \ + uint8_t temp1, temp2, temp3, temp4; \ + writecommand(0x10); \ + for (uint16_t row = 0; row < (h) ; row++) \ + { \ + for(uint16_t col = 0; col < bytes_per_row; col+=2) \ + { \ + uint8_t b = (colors[bytes_per_row *row+col ]) ; \ + uint8_t c = (colors[bytes_per_row *row+col + 1]) ; \ + temp1 = (b >> 4) & 0x0F;\ + temp2 = b & 0x0F;\ + temp3 = (c >> 4) & 0x0F;\ + temp4 = c & 0x0F;\ + writedata(((COLOR_GET(temp1) <<6)|( COLOR_GET(temp2) << 4 ) |( COLOR_GET(temp3) << 2 ) |( COLOR_GET(temp4) << 0 )));\ + } \ + } \ + } while (0) + +#define EPD_PUSH_NEW_COLORS_FLIP(w, h, colors) \ + do \ + { \ + uint16_t bytes_per_row = (w) / 2; \ + uint8_t temp1, temp2, temp3, temp4; \ + writecommand(0x10); \ + for (uint16_t row = 0; row < (h); row++) \ + { \ + uint16_t start = row * bytes_per_row; \ + for (uint16_t col = 0; col < bytes_per_row; col+=2) \ + { \ + uint8_t b = (colors[bytes_per_row *row + (bytes_per_row - 1 - col)]) ;\ + uint8_t c = (colors[bytes_per_row *row + (bytes_per_row - 1 - col) - 1]) ;\ + temp1 = (b >> 4) & 0x0F;\ + temp2 = b & 0x0F;\ + temp3 = (c >> 4) & 0x0F;\ + temp4 = c & 0x0F;\ + writedata(((COLOR_GET(temp1) <<6)|( COLOR_GET(temp2) << 4 ) |( COLOR_GET(temp3) << 2 ) |( COLOR_GET(temp4) << 0 )));\ + } \ + } \ + } while (0) + +#define EPD_PUSH_OLD_COLORS(w, h, colors) \ + do \ + { \ + } while (0) + +#define EPD_PUSH_OLD_COLORS_FLIP(w, h, colors) \ + do \ + { \ + \ + } while (0) + +#define EPD_SET_TEMP(temp) \ + do \ + { \ + } while (0) \ No newline at end of file diff --git a/lib/Seeed_GFX/TFT_Drivers/SSD2677_Init.h b/lib/Seeed_GFX/TFT_Drivers/SSD2677_Init.h new file mode 100644 index 0000000..d2060a7 --- /dev/null +++ b/lib/Seeed_GFX/TFT_Drivers/SSD2677_Init.h @@ -0,0 +1,65 @@ +// JD79686B_Init.h +{ +#ifdef TFT_BUSY + pinMode(TFT_BUSY, INPUT); +#endif +#ifdef TFT_ENABLE + pinMode(TFT_ENABLE, OUTPUT); + digitalWrite(TFT_ENABLE, HIGH); +#endif + digitalWrite(TFT_RST, LOW); + delay(20); + digitalWrite(TFT_RST, HIGH); + delay(50); + CHECK_BUSY(); + + writecommand(0x00); //0x00 + writedata(0x2B); + writedata(0x29); + + writecommand(0x06); //0x06 + writedata(0x0F); + writedata(0x8B); + writedata(0x93); + writedata(0xC1);//0xC1 + + writecommand(0x50); //0x50 + writedata(0x37); + + writecommand(0x30); //0x30 + writedata(0x08); + + writecommand(0x61); //TRES + writedata(800/256); // Source_BITS_H + writedata(800%256); // Source_BITS_L + writedata(680/256); // Gate_BITS_H + writedata(680%256); // Gate_BITS_L + + writecommand(0x62); + writedata(0x76); + writedata(0x76); + writedata(0x76); + writedata(0x5A); + writedata(0x9D); + writedata(0x8A); + writedata(0x76); + writedata(0x62); + + writecommand(0x65); //0x65 + writedata(0x00); + writedata(0x00); + writedata(0x00); + writedata(0x00); + + writecommand(0xE0); //0xE3 + writedata(0x10); + + writecommand(0xE7); //0xE7 + writedata(0xA4); + + writecommand(0xE9); + writedata(0x01); + + writecommand(0x04); //Power on + CHECK_BUSY(); +} \ No newline at end of file diff --git a/lib/Seeed_GFX/TFT_Drivers/SSD2677_Rotation.h b/lib/Seeed_GFX/TFT_Drivers/SSD2677_Rotation.h new file mode 100644 index 0000000..e69de29 diff --git a/lib/Seeed_GFX/TFT_Drivers/ST7735_Defines.h b/lib/Seeed_GFX/TFT_Drivers/ST7735_Defines.h new file mode 100644 index 0000000..b02ee15 --- /dev/null +++ b/lib/Seeed_GFX/TFT_Drivers/ST7735_Defines.h @@ -0,0 +1,179 @@ +// Change the width and height if required (defined in portrait mode) +// or use the constructor to over-ride defaults +#ifndef TFT_WIDTH + #define TFT_WIDTH 128 +#endif +#ifndef TFT_HEIGHT + #define TFT_HEIGHT 160 +#endif + + +// Enumerate the different configurations +#define INITR_GREENTAB 0x0 +#define INITR_REDTAB 0x1 +#define INITR_BLACKTAB 0x2 // Display with no offsets +#define INITR_GREENTAB2 0x3 // Use if you get random pixels on two edges of green tab display +#define INITR_GREENTAB3 0x4 // Use if you get random pixels on edge(s) of 128x128 screen +#define INITR_GREENTAB128 0x5 // Use if you only get part of 128x128 screen in rotation 0 & 1 +#define INITR_GREENTAB160x80 0x6 // Use if you only get part of 128x128 screen in rotation 0 & 1 +#define INITR_REDTAB160x80 0x7 // Added for https://www.aliexpress.com/item/ShengYang-1pcs-IPS-0-96-inch-7P-SPI-HD-65K-Full-Color-OLED-Module-ST7735-Drive/32918394604.html +#define INITR_ROBOTLCD 0x8 +#define INITB 0xB + + +// Setup the tab color that will be used by the library setRotation() and setup command list +#if defined (ST7735_INITB) + #define TAB_COLOUR INITB + +#elif defined (ST7735_GREENTAB) + #define TAB_COLOUR INITR_GREENTAB + #define CGRAM_OFFSET + +#elif defined (ST7735_GREENTAB2) + #define TAB_COLOUR INITR_GREENTAB2 + #define CGRAM_OFFSET + +#elif defined (ST7735_GREENTAB3) + #define TAB_COLOUR INITR_GREENTAB3 + #define CGRAM_OFFSET + +#elif defined (ST7735_GREENTAB128) + #define TAB_COLOUR INITR_GREENTAB128 + #define CGRAM_OFFSET + +#elif defined (ST7735_GREENTAB160x80) + #define TAB_COLOUR INITR_GREENTAB160x80 + #define CGRAM_OFFSET + +#elif defined (ST7735_ROBOTLCD) + #define TAB_COLOUR INITR_ROBOTLCD + #define CGRAM_OFFSET + +#elif defined (ST7735_REDTAB160x80) + #define TAB_COLOUR INITR_REDTAB160x80 + #define CGRAM_OFFSET + +#elif defined (ST7735_REDTAB) + #define TAB_COLOUR INITR_REDTAB + +#elif defined (ST7735_BLACKTAB) + #define TAB_COLOUR INITR_BLACKTAB + +#else // Make sure it is not undefined + #define TAB_COLOUR INITR_BLACKTAB +#endif + + +// Color definitions for backwards compatibility with old sketches +// use colour definitions like TFT_BLACK to make sketches more portable +#define ST7735_BLACK 0x0000 /* 0, 0, 0 */ +#define ST7735_NAVY 0x000F /* 0, 0, 128 */ +#define ST7735_DARKGREEN 0x03E0 /* 0, 128, 0 */ +#define ST7735_DARKCYAN 0x03EF /* 0, 128, 128 */ +#define ST7735_MAROON 0x7800 /* 128, 0, 0 */ +#define ST7735_PURPLE 0x780F /* 128, 0, 128 */ +#define ST7735_OLIVE 0x7BE0 /* 128, 128, 0 */ +#define ST7735_LIGHTGREY 0xC618 /* 192, 192, 192 */ +#define ST7735_DARKGREY 0x7BEF /* 128, 128, 128 */ +#define ST7735_BLUE 0x001F /* 0, 0, 255 */ +#define ST7735_GREEN 0x07E0 /* 0, 255, 0 */ +#define ST7735_CYAN 0x07FF /* 0, 255, 255 */ +#define ST7735_RED 0xF800 /* 255, 0, 0 */ +#define ST7735_MAGENTA 0xF81F /* 255, 0, 255 */ +#define ST7735_YELLOW 0xFFE0 /* 255, 255, 0 */ +#define ST7735_WHITE 0xFFFF /* 255, 255, 255 */ +#define ST7735_ORANGE 0xFD20 /* 255, 165, 0 */ +#define ST7735_GREENYELLOW 0xAFE5 /* 173, 255, 47 */ +#define ST7735_PINK 0xF81F + + +// Delay between some initialisation commands +#define TFT_INIT_DELAY 0x80 + + +// Generic commands used by TFT_eSPI.cpp +#define TFT_NOP 0x00 +#define TFT_SWRST 0x01 + +#define TFT_INVOFF 0x20 +#define TFT_INVON 0x21 + +#define TFT_DISPOFF 0x28 +#define TFT_DISPON 0x29 + +#define TFT_CASET 0x2A +#define TFT_PASET 0x2B +#define TFT_RAMWR 0x2C + +#define TFT_RAMRD 0x2E +#define TFT_IDXRD 0x00 //0xDD // ILI9341 only, indexed control register read + +#define TFT_MADCTL 0x36 +#define TFT_MAD_MY 0x80 +#define TFT_MAD_MX 0x40 +#define TFT_MAD_MV 0x20 +#define TFT_MAD_ML 0x10 +#define TFT_MAD_BGR 0x08 +#define TFT_MAD_MH 0x04 +#define TFT_MAD_RGB 0x00 + +#ifndef TFT_RGB_ORDER + #if defined(ST7735_BLACKTAB) || defined(ST7735_GREENTAB2) || defined(ST7735_INITB) + #define TFT_MAD_COLOR_ORDER TFT_MAD_RGB + #else + #define TFT_MAD_COLOR_ORDER TFT_MAD_BGR + #endif +#else + #if (TFT_RGB_ORDER == 1) + #define TFT_MAD_COLOR_ORDER TFT_MAD_RGB + #else + #define TFT_MAD_COLOR_ORDER TFT_MAD_BGR + #endif +#endif + +// ST7735 specific commands used in init +#define ST7735_NOP 0x00 +#define ST7735_SWRESET 0x01 +#define ST7735_RDDID 0x04 +#define ST7735_RDDST 0x09 + +#define ST7735_SLPIN 0x10 +#define ST7735_SLPOUT 0x11 +#define ST7735_PTLON 0x12 +#define ST7735_NORON 0x13 + +#define ST7735_INVOFF 0x20 +#define ST7735_INVON 0x21 +#define ST7735_DISPOFF 0x28 +#define ST7735_DISPON 0x29 +#define ST7735_CASET 0x2A +#define ST7735_RASET 0x2B // PASET +#define ST7735_RAMWR 0x2C +#define ST7735_RAMRD 0x2E + +#define ST7735_PTLAR 0x30 +#define ST7735_COLMOD 0x3A +#define ST7735_MADCTL 0x36 + +#define ST7735_FRMCTR1 0xB1 +#define ST7735_FRMCTR2 0xB2 +#define ST7735_FRMCTR3 0xB3 +#define ST7735_INVCTR 0xB4 +#define ST7735_DISSET5 0xB6 + +#define ST7735_PWCTR1 0xC0 +#define ST7735_PWCTR2 0xC1 +#define ST7735_PWCTR3 0xC2 +#define ST7735_PWCTR4 0xC3 +#define ST7735_PWCTR5 0xC4 +#define ST7735_VMCTR1 0xC5 + +#define ST7735_RDID1 0xDA +#define ST7735_RDID2 0xDB +#define ST7735_RDID3 0xDC +#define ST7735_RDID4 0xDD + +#define ST7735_PWCTR6 0xFC + +#define ST7735_GMCTRP1 0xE0 +#define ST7735_GMCTRN1 0xE1 diff --git a/lib/Seeed_GFX/TFT_Drivers/ST7735_Init.h b/lib/Seeed_GFX/TFT_Drivers/ST7735_Init.h new file mode 100644 index 0000000..3b525f2 --- /dev/null +++ b/lib/Seeed_GFX/TFT_Drivers/ST7735_Init.h @@ -0,0 +1,218 @@ + +// This is the command sequence that initialises the ST7735 driver +// +// This setup information is in a format accepted by the commandList() function +// which reduces FLASH space, but on an ESP8266 there is plenty available! +// +// See ILI9341_Setup.h file for an alternative simpler format + +{ + // Initialization commands for ST7735 screens + static const uint8_t PROGMEM + Bcmd[] = { // Initialization commands for 7735B screens + 18, // 18 commands in list: + ST7735_SWRESET, TFT_INIT_DELAY, // 1: Software reset, no args, w/delay + 50, // 50 ms delay + ST7735_SLPOUT , TFT_INIT_DELAY, // 2: Out of sleep mode, no args, w/delay + 255, // 255 = 500 ms delay + ST7735_COLMOD , 1+TFT_INIT_DELAY, // 3: Set color mode, 1 arg + delay: + 0x05, // 16-bit color + 10, // 10 ms delay + ST7735_FRMCTR1, 3+TFT_INIT_DELAY, // 4: Frame rate control, 3 args + delay: + 0x00, // fastest refresh + 0x06, // 6 lines front porch + 0x03, // 3 lines back porch + 10, // 10 ms delay + ST7735_MADCTL , 1 , // 5: Memory access ctrl (directions), 1 arg: + 0x40 | TFT_MAD_COLOR_ORDER, // Row addr/col addr, bottom to top refresh + ST7735_DISSET5, 2 , // 6: Display settings #5, 2 args, no delay: + 0x15, // 1 clk cycle nonoverlap, 2 cycle gate + // rise, 3 cycle osc equalize + 0x02, // Fix on VTL + ST7735_INVCTR , 1 , // 7: Display inversion control, 1 arg: + 0x0, // Line inversion + ST7735_PWCTR1 , 2+TFT_INIT_DELAY, // 8: Power control, 2 args + delay: + 0x02, // GVDD = 4.7V + 0x70, // 1.0uA + 10, // 10 ms delay + ST7735_PWCTR2 , 1 , // 9: Power control, 1 arg, no delay: + 0x05, // VGH = 14.7V, VGL = -7.35V + ST7735_PWCTR3 , 2 , // 10: Power control, 2 args, no delay: + 0x01, // Opamp current small + 0x02, // Boost frequency + ST7735_VMCTR1 , 2+TFT_INIT_DELAY, // 11: Power control, 2 args + delay: + 0x3C, // VCOMH = 4V + 0x38, // VCOML = -1.1V + 10, // 10 ms delay + ST7735_PWCTR6 , 2 , // 12: Power control, 2 args, no delay: + 0x11, 0x15, + ST7735_GMCTRP1,16 , // 13: Magical unicorn dust, 16 args, no delay: + 0x09, 0x16, 0x09, 0x20, // (seriously though, not sure what + 0x21, 0x1B, 0x13, 0x19, // these config values represent) + 0x17, 0x15, 0x1E, 0x2B, + 0x04, 0x05, 0x02, 0x0E, + ST7735_GMCTRN1,16+TFT_INIT_DELAY, // 14: Sparkles and rainbows, 16 args + delay: + 0x0B, 0x14, 0x08, 0x1E, // (ditto) + 0x22, 0x1D, 0x18, 0x1E, + 0x1B, 0x1A, 0x24, 0x2B, + 0x06, 0x06, 0x02, 0x0F, + 10, // 10 ms delay + ST7735_CASET , 4 , // 15: Column addr set, 4 args, no delay: + 0x00, 0x02, // XSTART = 2 + 0x00, 0x81, // XEND = 129 + ST7735_RASET , 4 , // 16: Row addr set, 4 args, no delay: + 0x00, 0x02, // XSTART = 1 + 0x00, 0x81, // XEND = 160 + ST7735_NORON , TFT_INIT_DELAY, // 17: Normal display on, no args, w/delay + 10, // 10 ms delay + ST7735_DISPON , TFT_INIT_DELAY, // 18: Main screen turn on, no args, w/delay + 255 }, // 255 = 500 ms delay + + Rcmd1[] = { // Init for 7735R, part 1 (red or green tab) + 15, // 15 commands in list: + ST7735_SWRESET, TFT_INIT_DELAY, // 1: Software reset, 0 args, w/delay + 150, // 150 ms delay + ST7735_SLPOUT , TFT_INIT_DELAY, // 2: Out of sleep mode, 0 args, w/delay + 255, // 500 ms delay + ST7735_FRMCTR1, 3 , // 3: Frame rate ctrl - normal mode, 3 args: + 0x01, 0x2C, 0x2D, // Rate = fosc/(1x2+40) * (LINE+2C+2D) + ST7735_FRMCTR2, 3 , // 4: Frame rate control - idle mode, 3 args: + 0x01, 0x2C, 0x2D, // Rate = fosc/(1x2+40) * (LINE+2C+2D) + ST7735_FRMCTR3, 6 , // 5: Frame rate ctrl - partial mode, 6 args: + 0x01, 0x2C, 0x2D, // Dot inversion mode + 0x01, 0x2C, 0x2D, // Line inversion mode + ST7735_INVCTR , 1 , // 6: Display inversion ctrl, 1 arg, no delay: + 0x07, // No inversion + ST7735_PWCTR1 , 3 , // 7: Power control, 3 args, no delay: + 0xA2, + 0x02, // -4.6V + 0x84, // AUTO mode + ST7735_PWCTR2 , 1 , // 8: Power control, 1 arg, no delay: + 0xC5, // VGH25 = 2.4C VGSEL = -10 VGH = 3 * AVDD + ST7735_PWCTR3 , 2 , // 9: Power control, 2 args, no delay: + 0x0A, // Opamp current small + 0x00, // Boost frequency + ST7735_PWCTR4 , 2 , // 10: Power control, 2 args, no delay: + 0x8A, // BCLK/2, Opamp current small & Medium low + 0x2A, + ST7735_PWCTR5 , 2 , // 11: Power control, 2 args, no delay: + 0x8A, 0xEE, + ST7735_VMCTR1 , 1 , // 12: Power control, 1 arg, no delay: + 0x0E, + ST7735_INVOFF , 0 , // 13: Don't invert display, no args, no delay + ST7735_MADCTL , 1 , // 14: Memory access control (directions), 1 arg: + 0xC0 | TFT_MAD_COLOR_ORDER, // row addr/col addr, bottom to top refresh + ST7735_COLMOD , 1 , // 15: set color mode, 1 arg, no delay: + 0x05 }, // 16-bit color + + Rcmd2green[] = { // Init for 7735R, part 2 (green tab only) + 2, // 2 commands in list: + ST7735_CASET , 4 , // 1: Column addr set, 4 args, no delay: + 0x00, 0x02, // XSTART = 0 + 0x00, 0x7F+0x02, // XEND = 127 + ST7735_RASET , 4 , // 2: Row addr set, 4 args, no delay: + 0x00, 0x01, // XSTART = 0 + 0x00, 0x9F+0x01 }, // XEND = 159 + + Rcmd2red[] = { // Init for 7735R, part 2 (red tab only) + 2, // 2 commands in list: + ST7735_CASET , 4 , // 1: Column addr set, 4 args, no delay: + 0x00, 0x00, // XSTART = 0 + 0x00, 0x7F, // XEND = 127 + ST7735_RASET , 4 , // 2: Row addr set, 4 args, no delay: + 0x00, 0x00, // XSTART = 0 + 0x00, 0x9F }, // XEND = 159 + + // Frame control init for RobotLCD, taken from https://github.com/arduino-libraries/TFT, Adafruit_ST7735.cpp l. 263, commit 61b8a7e + Rcmd3RobotLCD[] = { + 3, + ST7735_FRMCTR1, 2 , // 1: Frame rate ctrl - normal mode, 2 args + 0x0B, 0x14, + ST7735_FRMCTR2, 2 , // 2: Frame rate ctrl - idle mode, 2 args + 0x0B, 0x14, + ST7735_FRMCTR3, 4 , // 3: Frame rate ctrl - partial mode, 4 args + 0x0B, 0x14, + 0x0B, 0x14 }, + + Rcmd3[] = { // Init for 7735R, part 3 (red or green tab) + 4, // 4 commands in list: + ST7735_GMCTRP1, 16 , // 1: 16 args, no delay: + 0x02, 0x1c, 0x07, 0x12, + 0x37, 0x32, 0x29, 0x2d, + 0x29, 0x25, 0x2B, 0x39, + 0x00, 0x01, 0x03, 0x10, + ST7735_GMCTRN1, 16 , // 2: 16 args, no delay: + 0x03, 0x1d, 0x07, 0x06, + 0x2E, 0x2C, 0x29, 0x2D, + 0x2E, 0x2E, 0x37, 0x3F, + 0x00, 0x00, 0x02, 0x10, + ST7735_NORON , TFT_INIT_DELAY, // 3: Normal display on, no args, w/delay + 10, // 10 ms delay + ST7735_DISPON , TFT_INIT_DELAY, // 4: Main screen turn on, no args w/delay + 100 }; // 100 ms delay + + if (tabcolor == INITB) + { + commandList(Bcmd); + } + else + { + commandList(Rcmd1); + + if (tabcolor == INITR_GREENTAB) + { + commandList(Rcmd2green); + colstart = 2; + rowstart = 1; + } + else if (tabcolor == INITR_GREENTAB2) + { + commandList(Rcmd2green); + writecommand(ST7735_MADCTL); + writedata(0xC0 | TFT_MAD_COLOR_ORDER); + colstart = 2; + rowstart = 1; + } + else if (tabcolor == INITR_GREENTAB3) + { + commandList(Rcmd2green); + colstart = 2; + rowstart = 3; + } + else if (tabcolor == INITR_GREENTAB128) + { + commandList(Rcmd2green); + colstart = 0; + rowstart = 32; + } + else if (tabcolor == INITR_GREENTAB160x80) + { + commandList(Rcmd2green); + writecommand(TFT_INVON); + colstart = 26; + rowstart = 1; + } + else if (tabcolor == INITR_ROBOTLCD) + { + commandList(Rcmd2green); + commandList(Rcmd3RobotLCD); + } + else if (tabcolor == INITR_REDTAB160x80) + { + commandList(Rcmd2green); + colstart = 24; + rowstart = 0; + } + else if (tabcolor == INITR_REDTAB) + { + commandList(Rcmd2red); + } + else if (tabcolor == INITR_BLACKTAB) + { + writecommand(ST7735_MADCTL); + writedata(0xC0 | TFT_MAD_COLOR_ORDER); + } + + commandList(Rcmd3); + } +} diff --git a/lib/Seeed_GFX/TFT_Drivers/ST7735_Rotation.h b/lib/Seeed_GFX/TFT_Drivers/ST7735_Rotation.h new file mode 100644 index 0000000..3c17b71 --- /dev/null +++ b/lib/Seeed_GFX/TFT_Drivers/ST7735_Rotation.h @@ -0,0 +1,132 @@ + +// This is the command sequence that rotates the ST7735 driver coordinate frame + + rotation = m % 4; // Limit the range of values to 0-3 + + writecommand(TFT_MADCTL); + switch (rotation) { + case 0: + if (tabcolor == INITR_BLACKTAB) { + writedata(TFT_MAD_MX | TFT_MAD_MY | TFT_MAD_COLOR_ORDER); + } else if(tabcolor == INITR_GREENTAB2) { + writedata(TFT_MAD_MX | TFT_MAD_MY | TFT_MAD_COLOR_ORDER); + colstart = 2; + rowstart = 1; + } else if(tabcolor == INITR_GREENTAB3) { + writedata(TFT_MAD_MX | TFT_MAD_MY | TFT_MAD_COLOR_ORDER); + colstart = 2; + rowstart = 3; + } else if(tabcolor == INITR_GREENTAB128) { + writedata(TFT_MAD_MX | TFT_MAD_MY | TFT_MAD_MH | TFT_MAD_COLOR_ORDER); + colstart = 0; + rowstart = 32; + } else if(tabcolor == INITR_GREENTAB160x80) { + writedata(TFT_MAD_MX | TFT_MAD_MY | TFT_MAD_MH | TFT_MAD_COLOR_ORDER); + colstart = 26; + rowstart = 1; + } else if(tabcolor == INITR_REDTAB160x80) { + writedata(TFT_MAD_MX | TFT_MAD_MY | TFT_MAD_MH | TFT_MAD_COLOR_ORDER); + colstart = 24; + rowstart = 0; + } else if(tabcolor == INITB) { + writedata(TFT_MAD_MX | TFT_MAD_COLOR_ORDER); + } else { + writedata(TFT_MAD_MX | TFT_MAD_MY | TFT_MAD_COLOR_ORDER); + } + _width = _init_width; + _height = _init_height; + break; + case 1: + if (tabcolor == INITR_BLACKTAB) { + writedata(TFT_MAD_MY | TFT_MAD_MV | TFT_MAD_COLOR_ORDER); + } else if(tabcolor == INITR_GREENTAB2) { + writedata(TFT_MAD_MY | TFT_MAD_MV | TFT_MAD_COLOR_ORDER); + colstart = 1; + rowstart = 2; + } else if(tabcolor == INITR_GREENTAB3) { + writedata(TFT_MAD_MY | TFT_MAD_MV | TFT_MAD_COLOR_ORDER); + colstart = 3; + rowstart = 2; + } else if(tabcolor == INITR_GREENTAB128) { + writedata(TFT_MAD_MV | TFT_MAD_MY | TFT_MAD_COLOR_ORDER); + colstart = 32; + rowstart = 0; + } else if(tabcolor == INITR_GREENTAB160x80) { + writedata(TFT_MAD_MV | TFT_MAD_MY | TFT_MAD_COLOR_ORDER); + colstart = 1; + rowstart = 26; + } else if(tabcolor == INITR_REDTAB160x80) { + writedata(TFT_MAD_MV | TFT_MAD_MY | TFT_MAD_COLOR_ORDER); + colstart = 0; + rowstart = 24; + } else if(tabcolor == INITB) { + writedata(TFT_MAD_MV | TFT_MAD_MX | TFT_MAD_MY | TFT_MAD_COLOR_ORDER); + } else { + writedata(TFT_MAD_MY | TFT_MAD_MV | TFT_MAD_COLOR_ORDER); + } + _width = _init_height; + _height = _init_width; + break; + case 2: + if (tabcolor == INITR_BLACKTAB) { + writedata(TFT_MAD_COLOR_ORDER); + } else if(tabcolor == INITR_GREENTAB2) { + writedata(TFT_MAD_COLOR_ORDER); + colstart = 2; + rowstart = 1; + } else if(tabcolor == INITR_GREENTAB3) { + writedata(TFT_MAD_COLOR_ORDER); + colstart = 2; + rowstart = 1; + } else if(tabcolor == INITR_GREENTAB128) { + writedata(TFT_MAD_COLOR_ORDER); + colstart = 0; + rowstart = 0; + } else if(tabcolor == INITR_GREENTAB160x80) { + writedata(TFT_MAD_COLOR_ORDER); + colstart = 26; + rowstart = 1; + } else if(tabcolor == INITR_REDTAB160x80) { + writedata(TFT_MAD_COLOR_ORDER); + colstart = 24; + rowstart = 0; + } else if(tabcolor == INITB) { + writedata(TFT_MAD_MY | TFT_MAD_COLOR_ORDER); + } else { + writedata(TFT_MAD_COLOR_ORDER); + } + _width = _init_width; + _height = _init_height; + break; + case 3: + if (tabcolor == INITR_BLACKTAB) { + writedata(TFT_MAD_MX | TFT_MAD_MV | TFT_MAD_COLOR_ORDER); + } else if(tabcolor == INITR_GREENTAB2) { + writedata(TFT_MAD_MX | TFT_MAD_MV | TFT_MAD_COLOR_ORDER); + colstart = 1; + rowstart = 2; + } else if(tabcolor == INITR_GREENTAB3) { + writedata(TFT_MAD_MX | TFT_MAD_MV | TFT_MAD_COLOR_ORDER); + colstart = 1; + rowstart = 2; + } else if(tabcolor == INITR_GREENTAB128) { + writedata(TFT_MAD_MX | TFT_MAD_MV | TFT_MAD_COLOR_ORDER); + colstart = 0; + rowstart = 0; + } else if(tabcolor == INITR_GREENTAB160x80) { + writedata(TFT_MAD_MX | TFT_MAD_MV | TFT_MAD_COLOR_ORDER); + colstart = 1; + rowstart = 26; + } else if(tabcolor == INITR_REDTAB160x80) { + writedata(TFT_MAD_MX | TFT_MAD_MV | TFT_MAD_COLOR_ORDER); + colstart = 0; + rowstart = 24; + } else if(tabcolor == INITB) { + writedata(TFT_MAD_MV | TFT_MAD_COLOR_ORDER); + } else { + writedata(TFT_MAD_MX | TFT_MAD_MV | TFT_MAD_COLOR_ORDER); + } + _width = _init_height; + _height = _init_width; + break; + } diff --git a/lib/Seeed_GFX/TFT_Drivers/ST7789_2_Defines.h b/lib/Seeed_GFX/TFT_Drivers/ST7789_2_Defines.h new file mode 100644 index 0000000..917e602 --- /dev/null +++ b/lib/Seeed_GFX/TFT_Drivers/ST7789_2_Defines.h @@ -0,0 +1,178 @@ +// Change the width and height if required (defined in portrait mode) +// or use the constructor to over-ride defaults +#ifndef TFT_WIDTH + #define TFT_WIDTH 240 +#endif +#ifndef TFT_HEIGHT + #define TFT_HEIGHT 320 +#endif + +#if (TFT_HEIGHT == 240) && (TFT_WIDTH == 240) + #ifndef CGRAM_OFFSET + #define CGRAM_OFFSET + #endif +#endif + +// Adafruit 1.44 TFT support +#if (TFT_HEIGHT == 240) && (TFT_WIDTH == 135) + #ifndef CGRAM_OFFSET + #define CGRAM_OFFSET + #endif +#endif + +// Adafruit 1.69 round corner TFT support +#if (TFT_HEIGHT == 280) && (TFT_WIDTH == 240) + #ifndef CGRAM_OFFSET + #define CGRAM_OFFSET + #endif +#endif + +// 1.47" 172x320 Round Rectangle Color IPS TFT Display +#if (TFT_HEIGHT == 320) && (TFT_WIDTH == 172) + #ifndef CGRAM_OFFSET + #define CGRAM_OFFSET + #endif +#endif + +#if (TFT_HEIGHT == 320) && (TFT_WIDTH == 170) + #ifndef CGRAM_OFFSET + #define CGRAM_OFFSET + #endif +#endif + +#if (TFT_HEIGHT == 300) && (TFT_WIDTH == 240) + #ifndef CGRAM_OFFSET + #define CGRAM_OFFSET + #endif +#endif + +// Delay between some initialisation commands +#define TFT_INIT_DELAY 0x80 // Not used unless commandlist invoked + + +// Generic commands used by TFT_eSPI.cpp +#define TFT_NOP 0x00 +#define TFT_SWRST 0x01 + +#define TFT_SLPIN 0x10 +#define TFT_SLPOUT 0x11 +#define TFT_NORON 0x13 + +#define TFT_INVOFF 0x20 +#define TFT_INVON 0x21 +#define TFT_DISPOFF 0x28 +#define TFT_DISPON 0x29 +#define TFT_CASET 0x2A +#define TFT_PASET 0x2B +#define TFT_RAMWR 0x2C +#define TFT_RAMRD 0x2E +#define TFT_MADCTL 0x36 +#define TFT_COLMOD 0x3A + +// Flags for TFT_MADCTL +#define TFT_MAD_MY 0x80 +#define TFT_MAD_MX 0x40 +#define TFT_MAD_MV 0x20 +#define TFT_MAD_ML 0x10 +#define TFT_MAD_RGB 0x00 +#define TFT_MAD_BGR 0x08 +#define TFT_MAD_MH 0x04 +#define TFT_MAD_SS 0x02 +#define TFT_MAD_GS 0x01 + +#ifdef TFT_RGB_ORDER + #if (TFT_RGB_ORDER == 1) + #define TFT_MAD_COLOR_ORDER TFT_MAD_RGB + #else + #define TFT_MAD_COLOR_ORDER TFT_MAD_BGR + #endif +#else + #ifdef CGRAM_OFFSET + #define TFT_MAD_COLOR_ORDER TFT_MAD_BGR + #else + #define TFT_MAD_COLOR_ORDER TFT_MAD_RGB + #endif +#endif + +#define TFT_IDXRD 0x00 // ILI9341 only, indexed control register read + +#define ST_CMD_DELAY 0x80 // special signifier for command lists +#define ST7789_240x240_XSTART 0 +#define ST7789_240x240_YSTART 0 + +// ST7789 specific commands used in init +#define ST7789_NOP 0x00 +#define ST7789_SWRESET 0x01 +#define ST7789_RDDID 0x04 +#define ST7789_RDDST 0x09 + +#define ST7789_RDDPM 0x0A // Read display power mode +#define ST7789_RDD_MADCTL 0x0B // Read display MADCTL +#define ST7789_RDD_COLMOD 0x0C // Read display pixel format +#define ST7789_RDDIM 0x0D // Read display image mode +#define ST7789_RDDSM 0x0E // Read display signal mode +#define ST7789_RDDSR 0x0F // Read display self-diagnostic result (ST7789V) + +#define ST7789_SLPIN 0x10 +#define ST7789_SLPOUT 0x11 +#define ST7789_PTLON 0x12 +#define ST7789_NORON 0x13 + +#define ST7789_INVOFF 0x20 +#define ST7789_INVON 0x21 +#define ST7789_GAMSET 0x26 // Gamma set +#define ST7789_DISPOFF 0x28 +#define ST7789_DISPON 0x29 +#define ST7789_CASET 0x2A +#define ST7789_RASET 0x2B +#define ST7789_RAMWR 0x2C +#define ST7789_RGBSET 0x2D // Color setting for 4096, 64K and 262K colors +#define ST7789_RAMRD 0x2E + +#define ST7789_PTLAR 0x30 +#define ST7789_VSCRDEF 0x33 // Vertical scrolling definition (ST7789V) +#define ST7789_TEOFF 0x34 // Tearing effect line off +#define ST7789_TEON 0x35 // Tearing effect line on +#define ST7789_MADCTL 0x36 // Memory data access control +#define ST7789_IDMOFF 0x38 // Idle mode off +#define ST7789_IDMON 0x39 // Idle mode on +#define ST7789_RAMWRC 0x3C // Memory write continue (ST7789V) +#define ST7789_RAMRDC 0x3E // Memory read continue (ST7789V) +#define ST7789_COLMOD 0x3A + +#define ST7789_RAMCTRL 0xB0 // RAM control +#define ST7789_RGBCTRL 0xB1 // RGB control +#define ST7789_PORCTRL 0xB2 // Porch control +#define ST7789_FRCTRL1 0xB3 // Frame rate control +#define ST7789_PARCTRL 0xB5 // Partial mode control +#define ST7789_GCTRL 0xB7 // Gate control +#define ST7789_GTADJ 0xB8 // Gate on timing adjustment +#define ST7789_DGMEN 0xBA // Digital gamma enable +#define ST7789_VCOMS 0xBB // VCOMS setting +#define ST7789_LCMCTRL 0xC0 // LCM control +#define ST7789_IDSET 0xC1 // ID setting +#define ST7789_VDVVRHEN 0xC2 // VDV and VRH command enable +#define ST7789_VRHS 0xC3 // VRH set +#define ST7789_VDVSET 0xC4 // VDV setting +#define ST7789_VCMOFSET 0xC5 // VCOMS offset set +#define ST7789_FRCTR2 0xC6 // FR Control 2 +#define ST7789_CABCCTRL 0xC7 // CABC control +#define ST7789_REGSEL1 0xC8 // Register value section 1 +#define ST7789_REGSEL2 0xCA // Register value section 2 +#define ST7789_PWMFRSEL 0xCC // PWM frequency selection +#define ST7789_PWCTRL1 0xD0 // Power control 1 +#define ST7789_VAPVANEN 0xD2 // Enable VAP/VAN signal output +#define ST7789_CMD2EN 0xDF // Command 2 enable +#define ST7789_PVGAMCTRL 0xE0 // Positive voltage gamma control +#define ST7789_NVGAMCTRL 0xE1 // Negative voltage gamma control +#define ST7789_DGMLUTR 0xE2 // Digital gamma look-up table for red +#define ST7789_DGMLUTB 0xE3 // Digital gamma look-up table for blue +#define ST7789_GATECTRL 0xE4 // Gate control +#define ST7789_SPI2EN 0xE7 // SPI2 enable +#define ST7789_PWCTRL2 0xE8 // Power control 2 +#define ST7789_EQCTRL 0xE9 // Equalize time control +#define ST7789_PROMCTRL 0xEC // Program control +#define ST7789_PROMEN 0xFA // Program mode enable +#define ST7789_NVMSET 0xFC // NVM setting +#define ST7789_PROMACT 0xFE // Program action + diff --git a/lib/Seeed_GFX/TFT_Drivers/ST7789_2_Init.h b/lib/Seeed_GFX/TFT_Drivers/ST7789_2_Init.h new file mode 100644 index 0000000..3cd9630 --- /dev/null +++ b/lib/Seeed_GFX/TFT_Drivers/ST7789_2_Init.h @@ -0,0 +1,22 @@ + +// This is the command sequence that initialises the ST7789 driver + +// Configure ST7789 display + +{ +static const uint8_t PROGMEM + st7789[] = { + 8, + TFT_SLPOUT, TFT_INIT_DELAY, 255, + TFT_COLMOD, 1+TFT_INIT_DELAY, 0x55, 10, + TFT_MADCTL, 1, 0x00, + TFT_CASET, 4, 0x00, 0x00, 0x00, 0xF0, + TFT_PASET, 4, 0x00, 0x00, 0x00, 0xF0, + TFT_INVON, TFT_INIT_DELAY, 10, + TFT_NORON, TFT_INIT_DELAY, 10, + TFT_DISPON, TFT_INIT_DELAY, 255 + }; + + commandList(st7789); +} +// End of ST7789 display configuration \ No newline at end of file diff --git a/lib/Seeed_GFX/TFT_Drivers/ST7789_2_Rotation.h b/lib/Seeed_GFX/TFT_Drivers/ST7789_2_Rotation.h new file mode 100644 index 0000000..df5860c --- /dev/null +++ b/lib/Seeed_GFX/TFT_Drivers/ST7789_2_Rotation.h @@ -0,0 +1,140 @@ + // This is the command sequence that rotates the ST7789 driver coordinate frame + + writecommand(TFT_MADCTL); + rotation = m % 4; + switch (rotation) { + case 0: // Portrait +#ifdef CGRAM_OFFSET + if (_init_width == 135) + { + colstart = 52; + rowstart = 40; + } + else if(_init_height == 280) + { + colstart = 0; + rowstart = 20; + } + else if(_init_width == 172) + { + colstart = 34; + rowstart = 0; + } + else if(_init_width == 170) + { + colstart = 35; + rowstart = 0; + } + else + { + colstart = 0; + rowstart = 0; + } +#endif + writedata(TFT_MAD_COLOR_ORDER); + + _width = _init_width; + _height = _init_height; + break; + + case 1: // Landscape (Portrait + 90) +#ifdef CGRAM_OFFSET + if (_init_width == 135) + { + colstart = 40; + rowstart = 53; + } + else if(_init_height == 280) + { + colstart = 20; + rowstart = 0; + } + else if(_init_width == 172) + { + colstart = 0; + rowstart = 34; + } + else if(_init_width == 170) + { + colstart = 0; + rowstart = 35; + } + else + { + colstart = 0; + rowstart = 0; + } +#endif + writedata(TFT_MAD_MX | TFT_MAD_MV | TFT_MAD_COLOR_ORDER); + + _width = _init_height; + _height = _init_width; + break; + + case 2: // Inverter portrait +#ifdef CGRAM_OFFSET + if (_init_width == 135) + { + colstart = 53; + rowstart = 40; + } + else if(_init_height == 280) + { + colstart = 0; + rowstart = 20; + } + else if(_init_width == 172) + { + colstart = 34; + rowstart = 0; + } + else if(_init_width == 170) + { + colstart = 35; + rowstart = 0; + } + else + { + colstart = 0; + rowstart = 80; + } +#endif + writedata(TFT_MAD_MX | TFT_MAD_MY | TFT_MAD_COLOR_ORDER); + + _width = _init_width; + _height = _init_height; + break; + case 3: // Inverted landscape +#ifdef CGRAM_OFFSET + if (_init_width == 135) + { + colstart = 40; + rowstart = 52; + } + else if(_init_height == 280) + { + colstart = 20; + rowstart = 0; + } + else if(_init_width == 172) + { + colstart = 0; + rowstart = 34; + } + else if(_init_width == 170) + { + colstart = 0; + rowstart = 35; + } + else + { + colstart = 80; + rowstart = 0; + } +#endif + writedata(TFT_MAD_MV | TFT_MAD_MY | TFT_MAD_COLOR_ORDER); + + _width = _init_height; + _height = _init_width; + break; + } diff --git a/lib/Seeed_GFX/TFT_Drivers/ST7789_Defines.h b/lib/Seeed_GFX/TFT_Drivers/ST7789_Defines.h new file mode 100644 index 0000000..f1b21f3 --- /dev/null +++ b/lib/Seeed_GFX/TFT_Drivers/ST7789_Defines.h @@ -0,0 +1,175 @@ +// Change the width and height if required (defined in portrait mode) +// or use the constructor to over-ride defaults +#ifndef TFT_WIDTH + #define TFT_WIDTH 240 +#endif +#ifndef TFT_HEIGHT + #define TFT_HEIGHT 320 +#endif + +#if (TFT_HEIGHT == 240) && (TFT_WIDTH == 240) + #ifndef CGRAM_OFFSET + #define CGRAM_OFFSET + #endif +#endif + +// Adafruit 1.44 TFT support +#if (TFT_HEIGHT == 240) && (TFT_WIDTH == 135) + #ifndef CGRAM_OFFSET + #define CGRAM_OFFSET + #endif +#endif + +// Adafruit 1.69 round corner TFT support +#if (TFT_HEIGHT == 280) && (TFT_WIDTH == 240) + #ifndef CGRAM_OFFSET + #define CGRAM_OFFSET + #endif +#endif + +// 1.47" 172x320 Round Rectangle Color IPS TFT Display +#if (TFT_HEIGHT == 320) && (TFT_WIDTH == 172) + #ifndef CGRAM_OFFSET + #define CGRAM_OFFSET + #endif +#endif + +#if (TFT_HEIGHT == 320) && (TFT_WIDTH == 170) + #ifndef CGRAM_OFFSET + #define CGRAM_OFFSET + #endif +#endif + +#if (TFT_HEIGHT == 300) && (TFT_WIDTH == 240) + #ifndef CGRAM_OFFSET + #define CGRAM_OFFSET + #endif +#endif + +// Delay between some initialisation commands +#define TFT_INIT_DELAY 0x80 // Not used unless commandlist invoked + + +// Generic commands used by TFT_eSPI.cpp +#define TFT_NOP 0x00 +#define TFT_SWRST 0x01 + +#define TFT_SLPIN 0x10 +#define TFT_SLPOUT 0x11 +#define TFT_NORON 0x13 + +#define TFT_INVOFF 0x20 +#define TFT_INVON 0x21 +#define TFT_DISPOFF 0x28 +#define TFT_DISPON 0x29 +#define TFT_CASET 0x2A +#define TFT_PASET 0x2B +#define TFT_RAMWR 0x2C +#define TFT_RAMRD 0x2E +#define TFT_MADCTL 0x36 +#define TFT_COLMOD 0x3A + +// Flags for TFT_MADCTL +#define TFT_MAD_MY 0x80 +#define TFT_MAD_MX 0x40 +#define TFT_MAD_MV 0x20 +#define TFT_MAD_ML 0x10 +#define TFT_MAD_RGB 0x00 +#define TFT_MAD_BGR 0x08 +#define TFT_MAD_MH 0x04 +#define TFT_MAD_SS 0x02 +#define TFT_MAD_GS 0x01 + +#ifdef TFT_RGB_ORDER + #if (TFT_RGB_ORDER == 1) + #define TFT_MAD_COLOR_ORDER TFT_MAD_RGB + #else + #define TFT_MAD_COLOR_ORDER TFT_MAD_BGR + #endif +#else + #ifdef CGRAM_OFFSET + #define TFT_MAD_COLOR_ORDER TFT_MAD_BGR + #else + #define TFT_MAD_COLOR_ORDER TFT_MAD_RGB + #endif +#endif + +#define TFT_IDXRD 0x00 // ILI9341 only, indexed control register read + +// ST7789 specific commands used in init +#define ST7789_NOP 0x00 +#define ST7789_SWRESET 0x01 +#define ST7789_RDDID 0x04 +#define ST7789_RDDST 0x09 + +#define ST7789_RDDPM 0x0A // Read display power mode +#define ST7789_RDD_MADCTL 0x0B // Read display MADCTL +#define ST7789_RDD_COLMOD 0x0C // Read display pixel format +#define ST7789_RDDIM 0x0D // Read display image mode +#define ST7789_RDDSM 0x0E // Read display signal mode +#define ST7789_RDDSR 0x0F // Read display self-diagnostic result (ST7789V) + +#define ST7789_SLPIN 0x10 +#define ST7789_SLPOUT 0x11 +#define ST7789_PTLON 0x12 +#define ST7789_NORON 0x13 + +#define ST7789_INVOFF 0x20 +#define ST7789_INVON 0x21 +#define ST7789_GAMSET 0x26 // Gamma set +#define ST7789_DISPOFF 0x28 +#define ST7789_DISPON 0x29 +#define ST7789_CASET 0x2A +#define ST7789_RASET 0x2B +#define ST7789_RAMWR 0x2C +#define ST7789_RGBSET 0x2D // Color setting for 4096, 64K and 262K colors +#define ST7789_RAMRD 0x2E + +#define ST7789_PTLAR 0x30 +#define ST7789_VSCRDEF 0x33 // Vertical scrolling definition (ST7789V) +#define ST7789_TEOFF 0x34 // Tearing effect line off +#define ST7789_TEON 0x35 // Tearing effect line on +#define ST7789_MADCTL 0x36 // Memory data access control +#define ST7789_VSCRSADD 0x37 // Vertical screoll address +#define ST7789_IDMOFF 0x38 // Idle mode off +#define ST7789_IDMON 0x39 // Idle mode on +#define ST7789_RAMWRC 0x3C // Memory write continue (ST7789V) +#define ST7789_RAMRDC 0x3E // Memory read continue (ST7789V) +#define ST7789_COLMOD 0x3A + +#define ST7789_RAMCTRL 0xB0 // RAM control +#define ST7789_RGBCTRL 0xB1 // RGB control +#define ST7789_PORCTRL 0xB2 // Porch control +#define ST7789_FRCTRL1 0xB3 // Frame rate control +#define ST7789_PARCTRL 0xB5 // Partial mode control +#define ST7789_GCTRL 0xB7 // Gate control +#define ST7789_GTADJ 0xB8 // Gate on timing adjustment +#define ST7789_DGMEN 0xBA // Digital gamma enable +#define ST7789_VCOMS 0xBB // VCOMS setting +#define ST7789_LCMCTRL 0xC0 // LCM control +#define ST7789_IDSET 0xC1 // ID setting +#define ST7789_VDVVRHEN 0xC2 // VDV and VRH command enable +#define ST7789_VRHS 0xC3 // VRH set +#define ST7789_VDVSET 0xC4 // VDV setting +#define ST7789_VCMOFSET 0xC5 // VCOMS offset set +#define ST7789_FRCTR2 0xC6 // FR Control 2 +#define ST7789_CABCCTRL 0xC7 // CABC control +#define ST7789_REGSEL1 0xC8 // Register value section 1 +#define ST7789_REGSEL2 0xCA // Register value section 2 +#define ST7789_PWMFRSEL 0xCC // PWM frequency selection +#define ST7789_PWCTRL1 0xD0 // Power control 1 +#define ST7789_VAPVANEN 0xD2 // Enable VAP/VAN signal output +#define ST7789_CMD2EN 0xDF // Command 2 enable +#define ST7789_PVGAMCTRL 0xE0 // Positive voltage gamma control +#define ST7789_NVGAMCTRL 0xE1 // Negative voltage gamma control +#define ST7789_DGMLUTR 0xE2 // Digital gamma look-up table for red +#define ST7789_DGMLUTB 0xE3 // Digital gamma look-up table for blue +#define ST7789_GATECTRL 0xE4 // Gate control +#define ST7789_SPI2EN 0xE7 // SPI2 enable +#define ST7789_PWCTRL2 0xE8 // Power control 2 +#define ST7789_EQCTRL 0xE9 // Equalize time control +#define ST7789_PROMCTRL 0xEC // Program control +#define ST7789_PROMEN 0xFA // Program mode enable +#define ST7789_NVMSET 0xFC // NVM setting +#define ST7789_PROMACT 0xFE // Program action + diff --git a/lib/Seeed_GFX/TFT_Drivers/ST7789_Init.h b/lib/Seeed_GFX/TFT_Drivers/ST7789_Init.h new file mode 100644 index 0000000..b2ed7eb --- /dev/null +++ b/lib/Seeed_GFX/TFT_Drivers/ST7789_Init.h @@ -0,0 +1,238 @@ + +// This is the command sequence that initialises the ST7789 driver +// +// This setup information uses simple 8-bit SPI writecommand() and writedata() functions +// +// See ST7735_Setup.h file for an alternative format + +#ifndef INIT_SEQUENCE_3 +{ + fillScreen(TFT_RED); + writecommand(ST7789_SLPOUT); // Sleep out + delay(120); + + writecommand(ST7789_NORON); // Normal display mode on + + //------------------------------display and color format setting--------------------------------// + writecommand(ST7789_MADCTL); + //writedata(0x00); + writedata(TFT_MAD_COLOR_ORDER); + + // JLX240 display datasheet + writecommand(0xB6); + writedata(0x0A); + writedata(0x82); + + writecommand(ST7789_RAMCTRL); + writedata(0x00); + writedata(0xE0); // 5 to 6-bit conversion: r0 = r5, b0 = b5 + + writecommand(ST7789_COLMOD); + writedata(0x55); + delay(10); + + //--------------------------------ST7789V Frame rate setting----------------------------------// + writecommand(ST7789_PORCTRL); + writedata(0x0c); + writedata(0x0c); + writedata(0x00); + writedata(0x33); + writedata(0x33); + + writecommand(ST7789_GCTRL); // Voltages: VGH / VGL + writedata(0x35); + + //---------------------------------ST7789V Power setting--------------------------------------// + writecommand(ST7789_VCOMS); + writedata(0x28); // JLX240 display datasheet + + writecommand(ST7789_LCMCTRL); + writedata(0x0C); + + writecommand(ST7789_VDVVRHEN); + writedata(0x01); + writedata(0xFF); + + writecommand(ST7789_VRHS); // voltage VRHS + writedata(0x10); + + writecommand(ST7789_VDVSET); + writedata(0x20); + + writecommand(ST7789_FRCTR2); + writedata(0x0f); + + writecommand(ST7789_PWCTRL1); + writedata(0xa4); + writedata(0xa1); + + //--------------------------------ST7789V gamma setting---------------------------------------// + writecommand(ST7789_PVGAMCTRL); + writedata(0xd0); + writedata(0x00); + writedata(0x02); + writedata(0x07); + writedata(0x0a); + writedata(0x28); + writedata(0x32); + writedata(0x44); + writedata(0x42); + writedata(0x06); + writedata(0x0e); + writedata(0x12); + writedata(0x14); + writedata(0x17); + + writecommand(ST7789_NVGAMCTRL); + writedata(0xd0); + writedata(0x00); + writedata(0x02); + writedata(0x07); + writedata(0x0a); + writedata(0x28); + writedata(0x31); + writedata(0x54); + writedata(0x47); + writedata(0x0e); + writedata(0x1c); + writedata(0x17); + writedata(0x1b); + writedata(0x1e); + + writecommand(ST7789_INVON); + + writecommand(ST7789_CASET); // Column address set + writedata(0x00); + writedata(0x00); + writedata(0x00); + writedata(0xEF); // 239 + + writecommand(ST7789_RASET); // Row address set + writedata(0x00); + writedata(0x00); + writedata(0x01); + writedata(0x3F); // 319 + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + end_tft_write(); + delay(120); + begin_tft_write(); + + writecommand(ST7789_DISPON); //Display on + delay(120); + +#ifdef TFT_BL + // Turn on the back-light LED + digitalWrite(TFT_BL, HIGH); + pinMode(TFT_BL, OUTPUT); +#endif +} + + +#else +// TTGO ESP32 S3 T-Display +{ + writecommand(ST7789_SLPOUT); // Sleep out + delay(120); + + writecommand(ST7789_NORON); // Normal display mode on + + //------------------------------display and color format setting--------------------------------// + writecommand(ST7789_MADCTL); + writedata(TFT_MAD_COLOR_ORDER); + + // writecommand(ST7789_RAMCTRL); + // writedata(0x00); + // writedata(0xE0); // 5 to 6-bit conversion: r0 = r5, b0 = b5 + + writecommand(ST7789_COLMOD); + writedata(0x55); + delay(10); + + //--------------------------------ST7789V Frame rate setting----------------------------------// + writecommand(ST7789_PORCTRL); + writedata(0x0b); + writedata(0x0b); + writedata(0x00); + writedata(0x33); + writedata(0x33); + + writecommand(ST7789_GCTRL); // Voltages: VGH / VGL + writedata(0x75); + + //---------------------------------ST7789V Power setting--------------------------------------// + writecommand(ST7789_VCOMS); + writedata(0x28); // JLX240 display datasheet + + writecommand(ST7789_LCMCTRL); + writedata(0x2C); + + writecommand(ST7789_VDVVRHEN); + writedata(0x01); + + writecommand(ST7789_VRHS); // voltage VRHS + writedata(0x1F); + + writecommand(ST7789_FRCTR2); + writedata(0x13); + + writecommand(ST7789_PWCTRL1); + writedata(0xa7); + + writecommand(ST7789_PWCTRL1); + writedata(0xa4); + writedata(0xa1); + + writecommand(0xD6); + writedata(0xa1); + + //--------------------------------ST7789V gamma setting---------------------------------------// + writecommand(ST7789_PVGAMCTRL); + writedata(0xf0); + writedata(0x05); + writedata(0x0a); + writedata(0x06); + writedata(0x06); + writedata(0x03); + writedata(0x2b); + writedata(0x32); + writedata(0x43); + writedata(0x36); + writedata(0x11); + writedata(0x10); + writedata(0x2b); + writedata(0x32); + + writecommand(ST7789_NVGAMCTRL); + writedata(0xf0); + writedata(0x08); + writedata(0x0c); + writedata(0x0b); + writedata(0x09); + writedata(0x24); + writedata(0x2b); + writedata(0x22); + writedata(0x43); + writedata(0x38); + writedata(0x15); + writedata(0x16); + writedata(0x2f); + writedata(0x37); + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + end_tft_write(); + delay(120); + begin_tft_write(); + + writecommand(ST7789_DISPON); //Display on + delay(120); + +#ifdef TFT_BL + // Turn on the back-light LED + digitalWrite(TFT_BL, HIGH); + pinMode(TFT_BL, OUTPUT); +#endif +} +#endif \ No newline at end of file diff --git a/lib/Seeed_GFX/TFT_Drivers/ST7789_Rotation.h b/lib/Seeed_GFX/TFT_Drivers/ST7789_Rotation.h new file mode 100644 index 0000000..df5860c --- /dev/null +++ b/lib/Seeed_GFX/TFT_Drivers/ST7789_Rotation.h @@ -0,0 +1,140 @@ + // This is the command sequence that rotates the ST7789 driver coordinate frame + + writecommand(TFT_MADCTL); + rotation = m % 4; + switch (rotation) { + case 0: // Portrait +#ifdef CGRAM_OFFSET + if (_init_width == 135) + { + colstart = 52; + rowstart = 40; + } + else if(_init_height == 280) + { + colstart = 0; + rowstart = 20; + } + else if(_init_width == 172) + { + colstart = 34; + rowstart = 0; + } + else if(_init_width == 170) + { + colstart = 35; + rowstart = 0; + } + else + { + colstart = 0; + rowstart = 0; + } +#endif + writedata(TFT_MAD_COLOR_ORDER); + + _width = _init_width; + _height = _init_height; + break; + + case 1: // Landscape (Portrait + 90) +#ifdef CGRAM_OFFSET + if (_init_width == 135) + { + colstart = 40; + rowstart = 53; + } + else if(_init_height == 280) + { + colstart = 20; + rowstart = 0; + } + else if(_init_width == 172) + { + colstart = 0; + rowstart = 34; + } + else if(_init_width == 170) + { + colstart = 0; + rowstart = 35; + } + else + { + colstart = 0; + rowstart = 0; + } +#endif + writedata(TFT_MAD_MX | TFT_MAD_MV | TFT_MAD_COLOR_ORDER); + + _width = _init_height; + _height = _init_width; + break; + + case 2: // Inverter portrait +#ifdef CGRAM_OFFSET + if (_init_width == 135) + { + colstart = 53; + rowstart = 40; + } + else if(_init_height == 280) + { + colstart = 0; + rowstart = 20; + } + else if(_init_width == 172) + { + colstart = 34; + rowstart = 0; + } + else if(_init_width == 170) + { + colstart = 35; + rowstart = 0; + } + else + { + colstart = 0; + rowstart = 80; + } +#endif + writedata(TFT_MAD_MX | TFT_MAD_MY | TFT_MAD_COLOR_ORDER); + + _width = _init_width; + _height = _init_height; + break; + case 3: // Inverted landscape +#ifdef CGRAM_OFFSET + if (_init_width == 135) + { + colstart = 40; + rowstart = 52; + } + else if(_init_height == 280) + { + colstart = 20; + rowstart = 0; + } + else if(_init_width == 172) + { + colstart = 0; + rowstart = 34; + } + else if(_init_width == 170) + { + colstart = 0; + rowstart = 35; + } + else + { + colstart = 80; + rowstart = 0; + } +#endif + writedata(TFT_MAD_MV | TFT_MAD_MY | TFT_MAD_COLOR_ORDER); + + _width = _init_height; + _height = _init_width; + break; + } diff --git a/lib/Seeed_GFX/TFT_Drivers/ST7796_Defines.h b/lib/Seeed_GFX/TFT_Drivers/ST7796_Defines.h new file mode 100644 index 0000000..d7009ac --- /dev/null +++ b/lib/Seeed_GFX/TFT_Drivers/ST7796_Defines.h @@ -0,0 +1,107 @@ +// Change the width and height if required (defined in portrait mode) +// or use the constructor to over-ride defaults +#ifndef TFT_WIDTH +#define TFT_WIDTH 320 +#endif + +#ifndef TFT_HEIGHT +#define TFT_HEIGHT 480 +#endif + +// Generic commands used by TFT_eSPI.cpp +#define TFT_NOP 0x00 +#define TFT_SWRST 0x01 + +#define TFT_INVOFF 0x20 +#define TFT_INVON 0x21 + +#define TFT_DISPOFF 0x28 +#define TFT_DISPON 0x29 + +#define TFT_CASET 0x2A +#define TFT_PASET 0x2B +#define TFT_RAMWR 0x2C +#define TFT_RAMRD 0x2E + +#define TFT_MADCTL 0x36 +#define TFT_MAD_MY 0x80 +#define TFT_MAD_MX 0x40 +#define TFT_MAD_MV 0x20 +#define TFT_MAD_ML 0x10 +#define TFT_MAD_BGR 0x08 +#define TFT_MAD_MH 0x04 +#define TFT_MAD_RGB 0x00 + +#ifdef TFT_RGB_ORDER + #if (TFT_RGB_ORDER == 1) + #define TFT_MAD_COLOR_ORDER TFT_MAD_RGB + #else + #define TFT_MAD_COLOR_ORDER TFT_MAD_BGR + #endif +#else + #define TFT_MAD_COLOR_ORDER TFT_MAD_BGR +#endif + +// ST7796 specific commands +#define ST7796_NOP 0x00 +#define ST7796_SWRESET 0x01 +#define ST7796_RDDID 0x04 +#define ST7796_RDDST 0x09 + +#define ST7796_SLPIN 0x10 +#define ST7796_SLPOUT 0x11 +#define ST7796_PTLON 0x12 +#define ST7796_NORON 0x13 + +#define ST7796_RDMODE 0x0A +#define ST7796_RDMADCTL 0x0B +#define ST7796_RDPIXFMT 0x0C +#define ST7796_RDIMGFMT 0x0A +#define ST7796_RDSELFDIAG 0x0F + +#define ST7796_INVOFF 0x20 +#define ST7796_INVON 0x21 + +#define ST7796_DISPOFF 0x28 +#define ST7796_DISPON 0x29 + +#define ST7796_CASET 0x2A +#define ST7796_PASET 0x2B +#define ST7796_RAMWR 0x2C +#define ST7796_RAMRD 0x2E + +#define ST7796_PTLAR 0x30 +#define ST7796_VSCRDEF 0x33 +#define ST7796_MADCTL 0x36 +#define ST7796_VSCRSADD 0x37 +#define ST7796_PIXFMT 0x3A + +#define ST7796_WRDISBV 0x51 +#define ST7796_RDDISBV 0x52 +#define ST7796_WRCTRLD 0x53 + +#define ST7796_FRMCTR1 0xB1 +#define ST7796_FRMCTR2 0xB2 +#define ST7796_FRMCTR3 0xB3 +#define ST7796_INVCTR 0xB4 +#define ST7796_DFUNCTR 0xB6 + +#define ST7796_PWCTR1 0xC0 +#define ST7796_PWCTR2 0xC1 +#define ST7796_PWCTR3 0xC2 + +#define ST7796_VMCTR1 0xC5 +#define ST7796_VMCOFF 0xC6 + +#define ST7796_RDID4 0xD3 + +#define ST7796_GMCTRP1 0xE0 +#define ST7796_GMCTRN1 0xE1 + +#define ST7796_MADCTL_MY 0x80 +#define ST7796_MADCTL_MX 0x40 +#define ST7796_MADCTL_MV 0x20 +#define ST7796_MADCTL_ML 0x10 +#define ST7796_MADCTL_RGB 0x00 +#define ST7796_MADCTL_BGR 0x08 +#define ST7796_MADCTL_MH 0x04 diff --git a/lib/Seeed_GFX/TFT_Drivers/ST7796_Init.h b/lib/Seeed_GFX/TFT_Drivers/ST7796_Init.h new file mode 100644 index 0000000..c1d8421 --- /dev/null +++ b/lib/Seeed_GFX/TFT_Drivers/ST7796_Init.h @@ -0,0 +1,107 @@ + +// This is the command sequence that initialises the ST7796 driver +// +// This setup information uses simple 8-bit SPI writecommand() and writedata() functions +// +// See ST7735_Setup.h file for an alternative format + +#define TFT_INIT_DELAY 0 +{ + delay(120); + + writecommand(0x01); //Software reset + delay(120); + + writecommand(0x11); //Sleep exit + delay(120); + + writecommand(0xF0); //Command Set control + writedata(0xC3); //Enable extension command 2 partI + + writecommand(0xF0); //Command Set control + writedata(0x96); //Enable extension command 2 partII + + writecommand(0x36); //Memory Data Access Control MX, MY, RGB mode + writedata(0x48); //X-Mirror, Top-Left to right-Buttom, RGB + + writecommand(0x3A); //Interface Pixel Format + writedata(0x55); //Control interface color format set to 16 + + + writecommand(0xB4); //Column inversion + writedata(0x01); //1-dot inversion + + writecommand(0xB6); //Display Function Control + writedata(0x80); //Bypass + writedata(0x02); //Source Output Scan from S1 to S960, Gate Output scan from G1 to G480, scan cycle=2 + writedata(0x3B); //LCD Drive Line=8*(59+1) + + + writecommand(0xE8); //Display Output Ctrl Adjust + writedata(0x40); + writedata(0x8A); + writedata(0x00); + writedata(0x00); + writedata(0x29); //Source eqaulizing period time= 22.5 us + writedata(0x19); //Timing for "Gate start"=25 (Tclk) + writedata(0xA5); //Timing for "Gate End"=37 (Tclk), Gate driver EQ function ON + writedata(0x33); + + writecommand(0xC1); //Power control2 + writedata(0x06); //VAP(GVDD)=3.85+( vcom+vcom offset), VAN(GVCL)=-3.85+( vcom+vcom offset) + + writecommand(0xC2); //Power control 3 + writedata(0xA7); //Source driving current level=low, Gamma driving current level=High + + writecommand(0xC5); //VCOM Control + writedata(0x18); //VCOM=0.9 + + delay(120); + + //ST7796 Gamma Sequence + writecommand(0xE0); //Gamma"+" + writedata(0xF0); + writedata(0x09); + writedata(0x0b); + writedata(0x06); + writedata(0x04); + writedata(0x15); + writedata(0x2F); + writedata(0x54); + writedata(0x42); + writedata(0x3C); + writedata(0x17); + writedata(0x14); + writedata(0x18); + writedata(0x1B); + + writecommand(0xE1); //Gamma"-" + writedata(0xE0); + writedata(0x09); + writedata(0x0B); + writedata(0x06); + writedata(0x04); + writedata(0x03); + writedata(0x2B); + writedata(0x43); + writedata(0x42); + writedata(0x3B); + writedata(0x16); + writedata(0x14); + writedata(0x17); + writedata(0x1B); + + delay(120); + + writecommand(0xF0); //Command Set control + writedata(0x3C); //Disable extension command 2 partI + + writecommand(0xF0); //Command Set control + writedata(0x69); //Disable extension command 2 partII + + end_tft_write(); + delay(120); + begin_tft_write(); + + writecommand(0x29); //Display on +} \ No newline at end of file diff --git a/lib/Seeed_GFX/TFT_Drivers/ST7796_Rotation.h b/lib/Seeed_GFX/TFT_Drivers/ST7796_Rotation.h new file mode 100644 index 0000000..c5f6ad6 --- /dev/null +++ b/lib/Seeed_GFX/TFT_Drivers/ST7796_Rotation.h @@ -0,0 +1,78 @@ + +// This is the command sequence that rotates the ST7796 driver coordinate frame + + rotation = m % 8; // Limit the range of values to 0-7 + + writecommand(TFT_MADCTL); + switch (rotation) { + case 0: + writedata(TFT_MAD_MX | TFT_MAD_COLOR_ORDER); + _width = _init_width; + _height = _init_height; +#ifdef CGRAM_OFFSET + if (_init_width == 222) + { + colstart = 49; + rowstart = 0; + } +#endif + break; + case 1: + writedata(TFT_MAD_MV | TFT_MAD_COLOR_ORDER); + _width = _init_height; + _height = _init_width; +#ifdef CGRAM_OFFSET + if (_init_width == 222) + { + colstart = 0; + rowstart = 49; + } +#endif + break; + case 2: + writedata(TFT_MAD_MY | TFT_MAD_COLOR_ORDER); + _width = _init_width; + _height = _init_height; +#ifdef CGRAM_OFFSET + if (_init_width == 222) + { + colstart = 49; + rowstart = 0; + } +#endif + break; + case 3: + writedata(TFT_MAD_MX | TFT_MAD_MY | TFT_MAD_MV | TFT_MAD_COLOR_ORDER); + _width = _init_height; + _height = _init_width; +#ifdef CGRAM_OFFSET + if (_init_width == 222) + { + colstart = 0; + rowstart = 49; + } +#endif + break; + // These next rotations are for bottom up BMP drawing + case 4: + writedata(TFT_MAD_MX | TFT_MAD_MY | TFT_MAD_COLOR_ORDER); + _width = _init_width; + _height = _init_height; + break; + case 5: + writedata(TFT_MAD_MV | TFT_MAD_MX | TFT_MAD_COLOR_ORDER); + _width = _init_height; + _height = _init_width; + break; + case 6: + writedata(TFT_MAD_COLOR_ORDER); + _width = _init_width; + _height = _init_height; + break; + case 7: + writedata(TFT_MAD_MY | TFT_MAD_MV | TFT_MAD_COLOR_ORDER); + _width = _init_height; + _height = _init_width; + break; + + } diff --git a/lib/Seeed_GFX/TFT_Drivers/T133A01_Defines.h b/lib/Seeed_GFX/TFT_Drivers/T133A01_Defines.h new file mode 100644 index 0000000..958103b --- /dev/null +++ b/lib/Seeed_GFX/TFT_Drivers/T133A01_Defines.h @@ -0,0 +1,352 @@ + +#ifndef EPD_WIDTH +#define EPD_WIDTH 1600 +#endif + +#ifndef EPD_HEIGHT +#define EPD_HEIGHT 1200 +#endif + +#ifndef TFT_WIDTH +#define TFT_WIDTH EPD_WIDTH +#endif + +#ifndef TFT_HEIGHT +#define TFT_HEIGHT EPD_HEIGHT +#endif + + + +#define EPD_COLOR_DEPTH 4 + +#define USE_COLORFULL_EPAPER + +#define MASTER_0 0x00 +#define SLAVE_1 0x01 +#define MAS_SLA 0x10 + +#define R00_PSR 0x00 +#define R01_PWR 0x01 +#define R02_POF 0x02 +#define R04_PON 0x04 +#define R05_BTST_N 0x05 +#define R06_BTST_P 0x06 +#define R10_DTM 0x10 +#define R12_DRF 0x12 +#define R20_LUT0 0x20 +#define R30_PLL 0x30 +#define R40_TSC 0x40 +#define R50_CDI 0x50 +#define R61_TRES 0x61 +#define R80_AMV 0x80 +#define R81_VV 0x81 +#define R82_VDCS 0x82 +#define R90_PGM 0x90 +#define R91_APG 0x91 +#define R92_ROTP 0x92 +#define RE0_CCSET 0xE0 +#define RE3_PWS 0xE3 +#define RE5_TSSET 0xE5 +#define RA5_DCDC 0xA5 + +#define TFT_SWRST 0xFF +#define TFT_CASET 0xFF +#define TFT_PASET 0xFF +#define TFT_RAMWR 0xFF +#define TFT_RAMRD 0xFF +#define TFT_INVON R04_PON +#define TFT_INVOFF R02_POF + +const unsigned char PSR_V[2] = { + 0xDF, 0x69 +}; +const unsigned char PWR_V[6] = { + 0x0F, 0x00, 0x28, 0x2C, 0x28, 0x38 +}; +const unsigned char POF_V[1] = { + 0x00 +}; +const unsigned char DRF_V[1] = { + 0x01 +}; +const unsigned char CDI_V[1] = { + 0x37 +}; + +const unsigned char TRES_V[4] = { + 0x04, 0xB0, 0x03, 0x20 +}; +const unsigned char AMV_V[2] = { + 0x01, 0x00 +}; + +const unsigned char CCSET_V_CUR[1] = { + 0x01 +}; + +const unsigned char CCSET_V_LOCK[1] = { + 0x03 +}; + +const unsigned char PWS_V[1] = { + 0x22 +}; + +const unsigned char DCDC_V[3] = { + 0x44, 0x54, 0x00 +}; + +const unsigned char BTST_P_V[2] = { + 0xE0, 0x20 +}; +const unsigned char BTST_N_V[2] = { + 0xE0, 0x20 +}; +const unsigned char Sleep_V[1] = { + 0xa5 +}; + +const unsigned char r74DataBuf[9]={ + 0x00, 0x0C, 0x0C, 0xD9, 0xDD, 0xDD, 0x15, 0x15, 0x55 +}; +const unsigned char rf0DataBuf[6]={ + 0x49, 0x55, 0x13, 0x5D, 0x05, 0x10 +}; +const unsigned char r60DataBuf[2]={ + 0x03, 0x03 +}; +const unsigned char r86DataBuf[1]={ + 0x10 +}; +const unsigned char rb6DataBuf[1]={ + 0x07 +}; +const unsigned char rb7DataBuf[1]={ + 0x01 +}; +const unsigned char rb0DataBuf[1]={ + 0x01 +}; +const unsigned char rb1DataBuf[1]={ + 0x02 +}; + +#define TFT_INIT_DELAY 0 + +#ifdef TFT_BUSY +#define CHECK_BUSY() \ + do \ + { \ + delay(10); \ + if (digitalRead(TFT_BUSY)) \ + break; \ + } while (true) +#else +#define CHECK_BUSY() +#endif + + +#define EPD_UPDATE() \ + do \ + { \ + digitalWrite(TFT_CS1, LOW); \ + writecommand(R04_PON); \ + CHECK_BUSY(); \ + digitalWrite(TFT_CS1, HIGH); delay(30); \ + digitalWrite(TFT_CS1, LOW); \ + writecommanddata(R12_DRF,DRF_V,sizeof(DRF_V)); \ + CHECK_BUSY(); \ + digitalWrite(TFT_CS1, HIGH); delay(30); \ + digitalWrite(TFT_CS1, LOW); \ + writecommanddata(R02_POF,POF_V,sizeof(POF_V)); \ + CHECK_BUSY(); \ + digitalWrite(TFT_CS1, HIGH); delay(30); \ + } while (0) + +#define EPD_SLEEP() \ + do \ + { \ + writecommanddata(0x07,Sleep_V,sizeof(Sleep_V)); \ + delay(1); \ + CHECK_BUSY(); \ + } while (0) + + #define EPD_INIT() \ + do \ + { \ + digitalWrite(TFT_RST, LOW); \ + delay(20); \ + digitalWrite(TFT_RST, HIGH);\ + delay(20); \ + CHECK_BUSY(); \ + writecommanddata(0x74,r74DataBuf,sizeof(r74DataBuf)); \ + digitalWrite(TFT_CS1, LOW); delay(10); \ + writecommanddata(0xF0,rf0DataBuf,sizeof(rf0DataBuf)); \ + digitalWrite(TFT_CS1, HIGH); delay(10); \ + digitalWrite(TFT_CS1, LOW); \ + writecommanddata(R00_PSR,PSR_V,sizeof(PSR_V)); \ + digitalWrite(TFT_CS1, HIGH); delay(10); \ + writecommanddata(RA5_DCDC,DCDC_V,sizeof(DCDC_V)); \ + delay(10); \ + digitalWrite(TFT_CS1, LOW); \ + writecommanddata(R50_CDI,CDI_V,sizeof(CDI_V)); \ + digitalWrite(TFT_CS1, HIGH); delay(10); \ + digitalWrite(TFT_CS1, LOW); \ + writecommanddata(0x60,r60DataBuf,sizeof(r60DataBuf)); \ + digitalWrite(TFT_CS1, HIGH); delay(10); \ + digitalWrite(TFT_CS1, LOW); \ + writecommanddata(0x86,r86DataBuf,sizeof(r86DataBuf)); \ + digitalWrite(TFT_CS1, HIGH); delay(10); \ + digitalWrite(TFT_CS1, LOW); \ + writecommanddata(RE3_PWS,PWS_V,sizeof(PWS_V)); \ + digitalWrite(TFT_CS1, HIGH); delay(10); \ + digitalWrite(TFT_CS1, LOW); \ + writecommanddata(R61_TRES,TRES_V,sizeof(TRES_V)); \ + digitalWrite(TFT_CS1, HIGH); delay(10); \ + writecommanddata(R01_PWR,PWR_V,sizeof(PWR_V)); \ + delay(10); \ + writecommanddata(0xB6,rb6DataBuf,sizeof(rb6DataBuf)); \ + delay(10); \ + writecommanddata(R06_BTST_P,BTST_P_V,sizeof(BTST_P_V)); \ + delay(10); \ + writecommanddata(0xB7,rb7DataBuf,sizeof(rb7DataBuf)); \ + delay(10); \ + writecommanddata(R05_BTST_N,BTST_N_V,sizeof(BTST_N_V)); \ + delay(10); \ + writecommanddata(0xB0,rb0DataBuf,sizeof(rb0DataBuf)); \ + delay(10); \ + writecommanddata(0xB1,rb1DataBuf,sizeof(rb1DataBuf)); \ + delay(10); \ + } while (0) + +#define EPD_WAKEUP() EPD_INIT() + +#define EPD_SET_WINDOW(x1, y1, x2, y2) \ + do \ + { \ + } while (0) + + + +#define COLOR_GET(color) ( \ + (color) == 0x0F ? 0x00 : \ + (color) == 0x00 ? 0x01 : \ + (color) == 0x02 ? 0x06 : \ + (color) == 0x0B ? 0x02 : \ + (color) == 0x0D ? 0x05 : \ + (color) == 0x06 ? 0x03 : \ + 0x01 \ +) + + +#define EPD_PUSH_NEW_COLORS(w, h, colors) \ + do \ + { \ + digitalWrite(TFT_CS1, LOW); \ + writecommanddata(RE0_CCSET, CCSET_V_CUR, sizeof(CCSET_V_CUR)); \ + digitalWrite(TFT_CS1, HIGH); \ + CHECK_BUSY(); \ + delay(10); \ + uint16_t bytes_per_blcok_row = (w) / 4; \ + uint8_t temp1,temp2 ; \ + digitalWrite(TFT_CS1, HIGH); \ + digitalWrite(TFT_CS, LOW); \ + spi.beginTransaction(SPISettings(SPI_FREQUENCY, MSBFIRST, TFT_SPI_MODE)); \ + DC_C; \ + spi.transfer(R10_DTM); \ + DC_D; \ + for (uint16_t row = 0; row < (h) ; row++) \ + { \ + for(uint16_t col = 0; col < bytes_per_blcok_row ; col++) \ + { \ + uint8_t b = (colors[(bytes_per_blcok_row * 2) *row+col]) ; \ + temp1 = (b >> 4) & 0x0F;\ + temp2 = b & 0x0F;\ + spi.transfer(((COLOR_GET(temp1) <<4)|( COLOR_GET(temp2))));\ + } \ + } \ + spi.endTransaction(); \ + digitalWrite(TFT_CS, HIGH); \ + digitalWrite(TFT_CS1, LOW); \ + spi.beginTransaction(SPISettings(SPI_FREQUENCY, MSBFIRST, TFT_SPI_MODE)); \ + DC_C; \ + spi.transfer(R10_DTM); \ + DC_D; \ + for (uint16_t row = 0; row < (h) ; row++) \ + { \ + for(uint16_t col = 0; col < bytes_per_blcok_row ; col++) \ + { \ + uint8_t b = (colors[(bytes_per_blcok_row * 2) *row+col + bytes_per_blcok_row]) ; \ + temp1 = (b >> 4) & 0x0F;\ + temp2 = b & 0x0F;\ + spi.transfer(((COLOR_GET(temp1) <<4)|( COLOR_GET(temp2))));\ + } \ + } \ + spi.endTransaction(); \ + digitalWrite(TFT_CS1, HIGH); \ + } while (0) + +#define EPD_PUSH_NEW_COLORS_FLIP(w, h, colors) \ + do \ + { \ + digitalWrite(TFT_CS1, LOW); \ + writecommanddata(RE0_CCSET, CCSET_V_CUR, sizeof(CCSET_V_CUR)); \ + digitalWrite(TFT_CS1, HIGH); \ + CHECK_BUSY(); \ + delay(10); \ + uint16_t bytes_per_blcok_row = (w) / 4; \ + uint8_t temp1,temp2 ; \ + digitalWrite(TFT_CS1, HIGH); \ + digitalWrite(TFT_CS, LOW); \ + spi.beginTransaction(SPISettings(SPI_FREQUENCY, MSBFIRST, TFT_SPI_MODE)); \ + DC_C; \ + spi.transfer(R10_DTM); \ + DC_D; \ + for (uint16_t row = 0; row < (h) ; row++) \ + { \ + for(uint16_t col = 0; col < bytes_per_blcok_row ; col++) \ + { \ + uint8_t b = (colors[(bytes_per_blcok_row * 2) * row + (bytes_per_blcok_row - col - 1) + bytes_per_blcok_row]) ; \ + temp1 = (b >> 4) & 0x0F;\ + temp2 = b & 0x0F;\ + spi.transfer(((COLOR_GET(temp1) <<4)|( COLOR_GET(temp2))));\ + } \ + } \ + spi.endTransaction(); \ + digitalWrite(TFT_CS, HIGH); \ + digitalWrite(TFT_CS1, LOW); \ + spi.beginTransaction(SPISettings(SPI_FREQUENCY, MSBFIRST, TFT_SPI_MODE)); \ + DC_C; \ + spi.transfer(R10_DTM); \ + DC_D; \ + for (uint16_t row = 0; row < (h) ; row++) \ + { \ + for(uint16_t col = 0; col < bytes_per_blcok_row ; col++) \ + { \ + uint8_t b = (colors[(bytes_per_blcok_row * 2) * row + (bytes_per_blcok_row - col - 1)]) ; \ + temp1 = (b >> 4) & 0x0F;\ + temp2 = b & 0x0F;\ + spi.transfer(((COLOR_GET(temp1) <<4)|( COLOR_GET(temp2))));\ + } \ + } \ + spi.endTransaction(); \ + digitalWrite(TFT_CS1, HIGH); \ + } while (0) + +#define EPD_PUSH_OLD_COLORS_FILP(w, h, colors)\ + do \ + { \ + } while (0) + +#define EPD_PUSH_OLD_COLORS(w, h, colors) \ + do \ + { \ + } while (0) + + +#define EPD_SET_TEMP(temp) \ + do \ + { \ + uint8_t _temp_val = (uint8_t)(temp); \ + writecommanddata(RE5_TSSET, &_temp_val, 1); \ + } while (0) diff --git a/lib/Seeed_GFX/TFT_Drivers/T133A01_Init.h b/lib/Seeed_GFX/TFT_Drivers/T133A01_Init.h new file mode 100644 index 0000000..7b2a8a6 --- /dev/null +++ b/lib/Seeed_GFX/TFT_Drivers/T133A01_Init.h @@ -0,0 +1,13 @@ +#ifdef TFT_BUSY + pinMode(TFT_BUSY, INPUT); +#endif +#ifdef TFT_ENABLE + pinMode(TFT_ENABLE, OUTPUT); + digitalWrite(TFT_ENABLE, HIGH); +#endif + pinMode(TFT_CS1, OUTPUT); + digitalWrite(TFT_CS1, HIGH); + digitalWrite(TFT_RST, LOW); + delay(20); + digitalWrite(TFT_RST, HIGH); + delay(20); \ No newline at end of file diff --git a/lib/Seeed_GFX/TFT_Drivers/T133A01_Rotation.h b/lib/Seeed_GFX/TFT_Drivers/T133A01_Rotation.h new file mode 100644 index 0000000..e69de29 diff --git a/lib/Seeed_GFX/TFT_Drivers/UC8179_Defines.h b/lib/Seeed_GFX/TFT_Drivers/UC8179_Defines.h new file mode 100644 index 0000000..7331135 --- /dev/null +++ b/lib/Seeed_GFX/TFT_Drivers/UC8179_Defines.h @@ -0,0 +1,400 @@ + +#ifndef EPD_WIDTH +#define EPD_WIDTH 800 +#endif + +#ifndef EPD_HEIGHT +#define EPD_HEIGHT 480 +#endif + +#ifndef TFT_WIDTH +#define TFT_WIDTH EPD_WIDTH +#endif + +#ifndef TFT_HEIGHT +#define TFT_HEIGHT EPD_HEIGHT +#endif + +#define EPD_COLOR_DEPTH 1 + +#define USE_PARTIAL_EPAPER +#define USE_MUTIGRAY_EPAPER +#define GRAY_LEVEL4 4 + +#define EPD_NOP 0xFF +#define EPD_PNLSET 0x00 +#define EPD_DISPON 0x04 +#define EPD_DISPOFF 0x03 +#define EPD_SLPIN 0x07 +#define EPD_SLPOUT 0xFF +#define EPD_PTLIN 0x91 // Partial display in +#define EPD_PTLOUT 0x92 // Partial display out +#define EPD_PTLW 0x90 + +#define TFT_SWRST 0xFF +#define TFT_CASET 0xFF +#define TFT_PASET 0xFF +#define TFT_RAMWR 0xFF +#define TFT_RAMRD 0xFF +#define TFT_INVON EPD_DISPON +#define TFT_INVOFF EPD_DISPOFF + +#define TFT_INIT_DELAY 0 + +#ifdef TFT_BUSY +#define CHECK_BUSY() \ + do \ + { \ + delay(10); \ + if (digitalRead(TFT_BUSY)) \ + break; \ + } while (true) +#else +#define CHECK_BUSY() +#endif + +#define EPD_UPDATE() \ + do \ + { \ + writecommand(0x12); \ + delay(1); \ + CHECK_BUSY(); \ + } while (0) + +#define EPD_UPDATE_GRAY EPD_UPDATE +#define EPD_UPDATE_PARTIAL EPD_UPDATE + +#define EPD_SLEEP() \ + do \ + { \ + writecommand(0X50); \ + writedata(0xf7); \ + writecommand(0x02); \ + CHECK_BUSY(); \ + writecommand(0x07); \ + writedata(0xA5); \ + } while (0) + +#define EPD_INIT_FULL() \ + do \ + { \ + writecommand(0x01); \ + writedata(0x07); \ + writedata(0x07); \ + writedata(0x3f); \ + writedata(0x3f); \ + writecommand(0x06); \ + writedata(0x17); \ + writedata(0x17); \ + writedata(0x28); \ + writedata(0x17); \ + writecommand(0x04); \ + delay(100); \ + CHECK_BUSY(); \ + writecommand(0x00); \ + writedata(0x1F); \ + writecommand(0x61); \ + writedata(EPD_WIDTH >> 8); \ + writedata(EPD_WIDTH & 0xFF); \ + writedata(EPD_HEIGHT >> 8); \ + writedata(EPD_HEIGHT & 0xFF); \ + writecommand(0x50); \ + writedata(0x10); \ + writedata(0x07); \ + } while (0) + +#define EPD_INIT_FAST() \ + do \ + { \ + writecommand(0x01); \ + writedata(0x07); \ + writedata(0x07); \ + writedata(0x3f); \ + writedata(0x3f); \ + writecommand(0x06); \ + writedata(0x17); \ + writedata(0x17); \ + writedata(0x28); \ + writedata(0x17); \ + writecommand(0x04); \ + delay(100); \ + CHECK_BUSY(); \ + writecommand(0x00); \ + writedata(0x1F); \ + writecommand(0x61); \ + writedata(EPD_WIDTH >> 8); \ + writedata(EPD_WIDTH & 0xFF); \ + writedata(EPD_HEIGHT >> 8); \ + writedata(EPD_HEIGHT & 0xFF); \ + writecommand(0x50); \ + writedata(0x10); \ + writedata(0x07); \ + writecommand(0xE0); \ + writedata(0x02); \ + writecommand(0xE5); \ + writedata(0x55); \ + } while (0) + +#define EPD_INIT_GRAY() \ + do \ + { \ + digitalWrite(TFT_RST, LOW); \ + delay(10); \ + digitalWrite(TFT_RST, HIGH); \ + delay(10); \ + CHECK_BUSY(); \ + writecommand(0X00); \ + writedata(0x1F); \ + writecommand(0X50); \ + writedata(0x10); \ + writedata(0x07); \ + writecommand(0x04); \ + delay(10); \ + CHECK_BUSY(); \ + writecommand(0x06); \ + writedata(0x27); \ + writedata(0x27); \ + writedata(0x28); \ + writedata(0x17); \ + writecommand(0xE0); \ + writedata(0x02); \ + writecommand(0xE5); \ + writedata(0x5F); \ + } while (0) + +#define EPD_INIT_PARTIAL() \ + do \ + { \ + writecommand(0x00); \ + writedata(0x1F); \ + writecommand(0x04); \ + delay(100); \ + CHECK_BUSY(); \ + writecommand(0xE0); \ + writedata(0x02); \ + writecommand(0xE5); \ + writedata(0x6E); \ + } while (0) + +#define EPD_WAKEUP() \ + do \ + { \ + digitalWrite(TFT_RST, LOW); \ + delay(10); \ + digitalWrite(TFT_RST, HIGH); \ + delay(10); \ + CHECK_BUSY(); \ + EPD_INIT_FAST(); \ + } while (0) + +#define EPD_WAKEUP_GRAY EPD_WAKEUP + +#define EPD_WAKEUP_PARTIAL() \ + do \ + { \ + digitalWrite(TFT_RST, LOW); \ + delay(10); \ + digitalWrite(TFT_RST, HIGH); \ + delay(10); \ + CHECK_BUSY(); \ + EPD_INIT_PARTIAL(); \ + } while (0); + + +#define EPD_SET_WINDOW(x1, y1, x2, y2) \ + do \ + { \ + writecommand(0x50); \ + writedata(0xA9); \ + writedata(0x07); \ + writecommand(0x91); \ + writecommand(0x90); \ + writedata (x1 >> 8); \ + writedata (x1& 0xFF); \ + writedata (x2 >> 8); \ + writedata ((x2& 0xFF)-1); \ + writedata (y1 >> 8); \ + writedata (y1& 0xFF); \ + writedata (y2 >> 8); \ + writedata ((y2& 0xFF)-1); \ + writedata (0x01); \ + } while (0) + +#define EPD_PUSH_NEW_COLORS(w, h, colors) \ + do \ + { \ + writecommand(0x13); \ + for (int i = 0; i < w * h / 8; i++) \ + { \ + writedata(colors[i]); \ + } \ + } while (0) + +#define EPD_PUSH_NEW_COLORS_FLIP(w, h, colors) \ + do \ + { \ + writecommand(0x13); \ + uint16_t bytes_per_row = (w) / 8; \ + for (uint16_t row = 0; row < (h); row++) \ + { \ + uint16_t start = row * bytes_per_row; \ + for (uint16_t col = 0; col < bytes_per_row; col++) \ + { \ + uint8_t b = colors[start + (bytes_per_row - 1 - col)]; \ + b = ((b & 0xF0) >> 4) | ((b & 0x0F) << 4); \ + b = ((b & 0xCC) >> 2) | ((b & 0x33) << 2); \ + b = ((b & 0xAA) >> 1) | ((b & 0x55) << 1); \ + writedata(b); \ + } \ + } \ + } while (0) + +#define EPD_PUSH_NEW_GRAY_COLORS(w, h, colors) \ + do \ + { \ + EPD_INIT_GRAY(); \ + uint16_t i, j, k; \ + uint8_t temp1, temp2, temp3; \ + writecommand(0x10); \ + for(i = 0; i < 48000; i++) \ + { \ + /* Read 4 input bytes = 8 pixels */ \ + uint8_t c0 = colors[i * 4 + 0]; \ + uint8_t c1 = colors[i * 4 + 1]; \ + uint8_t c2 = colors[i * 4 + 2]; \ + uint8_t c3 = colors[i * 4 + 3]; \ + /* Extract 8 pixels from bit5-4 and bit1-0 of each byte */ \ + uint8_t p0 = (c0 >> 4) & 0x03; \ + uint8_t p1 = (c0 >> 0) & 0x03; \ + uint8_t p2 = (c1 >> 4) & 0x03; \ + uint8_t p3 = (c1 >> 0) & 0x03; \ + uint8_t p4 = (c2 >> 4) & 0x03; \ + uint8_t p5 = (c2 >> 0) & 0x03; \ + uint8_t p6 = (c3 >> 4) & 0x03; \ + uint8_t p7 = (c3 >> 0) & 0x03; \ + /* Pack into original-style packed bytes (high 2-bit per pixel) */ \ + uint8_t packed_byte0 = (p0 << 6) | (p1 << 4) | (p2 << 2) | p3; \ + uint8_t packed_byte1 = (p4 << 6) | (p5 << 4) | (p6 << 2) | p7; \ + \ + temp3 = 0; \ + for(j = 0; j < 2; j++) \ + { \ + temp1 = (j == 0) ? packed_byte0 : packed_byte1; \ + for(k = 0; k < 4; k++) \ + { \ + temp2 = temp1 & 0xC0; \ + if(temp2 == 0xC0) \ + temp3 |= 0x01; \ + else if(temp2 == 0x00) \ + temp3 |= 0x00; \ + else if((temp2 >= 0x80) && (temp2 < 0xC0)) \ + temp3 |= 0x00; \ + else if(temp2 == 0x40) \ + temp3 |= 0x01; \ + if((j == 0 && k <= 3) || (j == 1 && k <= 2)) \ + { \ + temp3 <<= 1; \ + temp1 <<= 2; \ + } \ + } \ + } \ + writedata(~temp3); \ + } \ + \ + writecommand(0x13); \ + for(i = 0; i < 48000; i++) \ + { \ + uint8_t c0 = colors[i * 4 + 0]; \ + uint8_t c1 = colors[i * 4 + 1]; \ + uint8_t c2 = colors[i * 4 + 2]; \ + uint8_t c3 = colors[i * 4 + 3]; \ + uint8_t p0 = (c0 >> 4) & 0x03; \ + uint8_t p1 = (c0 >> 0) & 0x03; \ + uint8_t p2 = (c1 >> 4) & 0x03; \ + uint8_t p3 = (c1 >> 0) & 0x03; \ + uint8_t p4 = (c2 >> 4) & 0x03; \ + uint8_t p5 = (c2 >> 0) & 0x03; \ + uint8_t p6 = (c3 >> 4) & 0x03; \ + uint8_t p7 = (c3 >> 0) & 0x03; \ + uint8_t packed_byte0 = (p0 << 6) | (p1 << 4) | (p2 << 2) | p3; \ + uint8_t packed_byte1 = (p4 << 6) | (p5 << 4) | (p6 << 2) | p7; \ + \ + temp3 = 0; \ + for(j = 0; j < 2; j++) \ + { \ + temp1 = (j == 0) ? packed_byte0 : packed_byte1; \ + for(k = 0; k < 4; k++) \ + { \ + temp2 = temp1 & 0xC0; \ + if(temp2 == 0xC0) \ + temp3 |= 0x01; \ + else if(temp2 == 0x00) \ + temp3 |= 0x00; \ + else if((temp2 >= 0x80) && (temp2 < 0xC0)) \ + temp3 |= 0x01; \ + else if(temp2 == 0x40) \ + temp3 |= 0x00; \ + if((j == 0 && k <= 3) || (j == 1 && k <= 2)) \ + { \ + temp3 <<= 1; \ + temp1 <<= 2; \ + } \ + } \ + } \ + writedata(~temp3); \ + } \ + } while (0) + +#define EPD_PUSH_NEW_GRAY_COLORS_FLIP(w, h, colors) \ + do \ + { \ + EPD_INIT_GRAY(); \ + writecommand(0x13); \ + uint16_t bytes_per_row = (w) / 8; \ + for (uint16_t row = 0; row < (h); row++) \ + { \ + uint16_t start = row * bytes_per_row; \ + for (uint16_t col = 0; col < bytes_per_row; col++) \ + { \ + uint8_t b = colors[start + (bytes_per_row - 1 - col)]; \ + b = ((b & 0xF0) >> 4) | ((b & 0x0F) << 4); \ + b = ((b & 0xCC) >> 2) | ((b & 0x33) << 2); \ + b = ((b & 0xAA) >> 1) | ((b & 0x55) << 1); \ + writedata(b); \ + } \ + } \ + } while (0) + +#define EPD_PUSH_OLD_COLORS(w, h, colors) \ + do \ + { \ + writecommand(0x10); \ + for (int i = 0; i < w * h / 8; i++) \ + { \ + writedata(colors[i]); \ + } \ + } while (0) + +#define EPD_PUSH_OLD_COLORS_FLIP(w, h, colors) \ + do \ + { \ + writecommand(0x10); \ + uint16_t bytes_per_row = (w) / 8; \ + for (uint16_t row = 0; row < (h); row++) \ + { \ + uint16_t start = row * bytes_per_row; \ + for (uint16_t col = 0; col < bytes_per_row; col++) \ + { \ + uint8_t b = colors[start + (bytes_per_row - 1 - col)]; \ + b = ((b & 0xF0) >> 4) | ((b & 0x0F) << 4); \ + b = ((b & 0xCC) >> 2) | ((b & 0x33) << 2); \ + b = ((b & 0xAA) >> 1) | ((b & 0x55) << 1); \ + writedata(b); \ + } \ + } \ + } while (0) + +#define EPD_SET_TEMP(temp) \ + do \ + { \ + } while (0) diff --git a/lib/Seeed_GFX/TFT_Drivers/UC8179_Init.h b/lib/Seeed_GFX/TFT_Drivers/UC8179_Init.h new file mode 100644 index 0000000..29bc967 --- /dev/null +++ b/lib/Seeed_GFX/TFT_Drivers/UC8179_Init.h @@ -0,0 +1,49 @@ + +// This is the command sequence that initialises the UC8179 driver + +{ + +#ifdef TFT_BUSY + pinMode(TFT_BUSY, INPUT); +#endif +#ifdef TFT_ENABLE + pinMode(TFT_ENABLE, OUTPUT); + digitalWrite(TFT_ENABLE, HIGH); +#endif + writecommand(0x01); // POWER SETTING + writedata(0x07); + writedata(0x07); // VGH=20V,VGL=-20V + writedata(0x3f); // VDH=15V + writedata(0x3f); // VDL=-15V + + // Enhanced display drive(Add 0x06 command) + writecommand(0x06); // Booster Soft Start + writedata(0x17); + writedata(0x17); + writedata(0x28); + writedata(0x17); + + writecommand(0x04); // POWER ON + delay(100); + + CHECK_BUSY(); + + writecommand(0X00); // PANNEL SETTING + writedata(0x1F); // KW-3f KWR-2F BWROTP 0f BWOTP 1f + + writecommand(0x61); // tres + writedata(EPD_WIDTH >> 8); + writedata(EPD_WIDTH & 0xFF); + writedata(EPD_HEIGHT >> 8); + writedata(EPD_HEIGHT & 0xFF); + + writecommand(0X15); + writedata(0x00); + + writecommand(0X50); // VCOM AND DATA INTERVAL SETTING + writedata(0x10); + writedata(0x07); + + writecommand(0X60); // TCON SETTING + writedata(0x22); +} \ No newline at end of file diff --git a/lib/Seeed_GFX/TFT_Drivers/UC8179_Rotation.h b/lib/Seeed_GFX/TFT_Drivers/UC8179_Rotation.h new file mode 100644 index 0000000..9d36ab2 --- /dev/null +++ b/lib/Seeed_GFX/TFT_Drivers/UC8179_Rotation.h @@ -0,0 +1,28 @@ + +// This is the command sequence that rotates the UC8179 driver coordinate frame + + rotation = m % 4; + + writecommand(EPD_PNLSET); + switch (rotation) { + case 0: // Portrait + writedata(0x1F); // 0b1111 + _width = _init_width; + _height = _init_height; + break; + case 1: // Landscape (Portrait + 90) + writedata(0x1B); // 0b1011 + _width = _init_height; + _height = _init_width; + break; + case 2: // Inverter portrait + writedata(0x13); // 0b0011 + _width = _init_width; + _height = _init_height; + break; + case 3: + writedata(0x17); // 0b0111 + _width = _init_height; + _height = _init_width; + break; + } diff --git a/lib/Seeed_GFX/TFT_config.h b/lib/Seeed_GFX/TFT_config.h new file mode 100644 index 0000000..86b1813 --- /dev/null +++ b/lib/Seeed_GFX/TFT_config.h @@ -0,0 +1,288 @@ + +/////////////////////////////////////////////////////////// +/* Support file for ESP32 IDF use */ +/* See library docs folder */ +/* */ +/* DO NOT EDIT THIS FILE */ +/* */ +/////////////////////////////////////////////////////////// + +/** + * @file TFT_config.h + * @author Ricard Bitriá Ribes (https://github.com/dracir9) + * Created Date: 22-01-2022 + * ----- + * Last Modified: 25-02-2023 + * Modified By: Ricard Bitriá Ribes + * ----- + * @copyright (c) 2022 Ricard Bitriá Ribes + */ + +#ifndef TFT_CONFIG_H +#define TFT_CONFIG_H + +#include "sdkconfig.h" + +/*************************************************************************************** +** Others +***************************************************************************************/ + +#ifdef CONFIG_DISABLE_WARNINGS + #define DISABLE_ALL_LIBRARY_WARNINGS +#endif + +/*************************************************************************************** +** TFT_eSPI Configuration defines +***************************************************************************************/ +// Override defaults + +/*************************************************************************************** +** Section 1: Load TFT driver +***************************************************************************************/ +#if defined (CONFIG_TFT_ILI9341_DRIVER) + #define ILI9341_DRIVER +#elif defined (CONFIG_TFT_ILI9341_2_DRIVER) + #define ILI9341_2_DRIVER +#elif defined (CONFIG_TFT_ST7735_DRIVER) + #define ST7735_DRIVER +#elif defined (CONFIG_TFT_ILI9163_DRIVER) + #define ILI9163_DRIVER +#elif defined (CONFIG_TFT_S6D02A1_DRIVER) + #define S6D02A1_DRIVER +#elif defined (CONFIG_TFT_HX8357D_DRIVER) + #define HX8357D_DRIVER +#elif defined (CONFIG_TFT_ILI9481_DRIVER) + #define ILI9481_DRIVER +#elif defined (CONFIG_TFT_ILI9486_DRIVER) + #define ILI9486_DRIVER +#elif defined (CONFIG_TFT_ILI9488_DRIVER) + #define ILI9488_DRIVER +#elif defined (CONFIG_TFT_ST7789_DRIVER) + #define ST7789_DRIVER +#elif defined (CONFIG_TFT_ST7789_2_DRIVER) + #define ST7789_2_DRIVER +#elif defined (CONFIG_TFT_R61581_DRIVER) + #define R61581_DRIVER +#elif defined (CONFIG_TFT_RM68140_DRIVER) + #define RM68140_DRIVER +#elif defined (CONFIG_TFT_ST7796_DRIVER) + #define ST7796_DRIVER +#elif defined (CONFIG_TFT_SSD1351_DRIVER) + #define SSD1351_DRIVER +#elif defined (CONFIG_TFT_SSD1963_480_DRIVER) + #define SSD1963_480_DRIVER +#elif defined (CONFIG_TFT_SSD1963_800_DRIVER) + #define SSD1963_800_DRIVER +#elif defined (CONFIG_TFT_SSD1963_800ALT_DRIVER) + #define SSD1963_800ALT_DRIVER +#elif defined (CONFIG_TFT_ILI9225_DRIVER) + #define ILI9225_DRIVER +#elif defined (CONFIG_TFT_GC9A01_DRIVER) + #define GC9A01_DRIVER +#elif defined (CONFIG_EPAPER_DRIVER) + +#endif + +#ifdef CONFIG_TFT_RGB_ORDER + #define TFT_RGB_ORDER TFT_RGB +#endif + +#ifdef CONFIG_TFT_BGR_ORDER + #define TFT_RGB_ORDER TFT_BGR +#endif + +#ifdef CONFIG_TFT_M5STACK + #define M5STACK +#endif + +#ifdef CONFIG_TFT_WIDTH + #define TFT_WIDTH CONFIG_TFT_WIDTH +#endif + +#ifdef CONFIG_TFT_HEIGHT + #define TFT_HEIGHT CONFIG_TFT_HEIGHT +#endif + +#if defined (CONFIG_TFT_ST7735_INITB) + #define ST7735_INITB +#elif defined (CONFIG_TFT_ST7735_GREENTAB) + #define ST7735_GREENTAB +#elif defined (CONFIG_TFT_ST7735_GREENTAB2) + #define ST7735_GREENTAB2 +#elif defined (CONFIG_TFT_ST7735_GREENTAB3) + #define ST7735_GREENTAB3 +#elif defined (CONFIG_TFT_ST7735_GREENTAB128) + #define ST7735_GREENTAB128 +#elif defined (CONFIG_TFT_ST7735_GREENTAB160x80) + #define ST7735_GREENTAB160x80 +#elif defined (CONFIG_TFT_ST7735_REDTAB) + #define ST7735_REDTAB +#elif defined (CONFIG_TFT_ST7735_BLACKTAB) + #define ST7735_BLACKTAB +#elif defined (CONFIG_TFT_ST7735_REDTAB160x80) + #define ST7735_REDTAB160x80 +#endif + +#if defined (CONFIG_TFT_INVERSION_ON) + #define TFT_INVERSION_ON +#elif defined (CONFIG_TFT_INVERSION_OFF) + #define TFT_INVERSION_OFF +#endif + + + + +/*************************************************************************************** +** Section 3: Data bus Pin configuration +***************************************************************************************/ + +// 8 BIT PARALLEL BUS +#ifdef CONFIG_TFT_PARALLEL_8_BIT + + #define TFT_PARALLEL_8_BIT + + #if CONFIG_TFT_D0 == -1 + #error "Invalid Data 0 pin. Check TFT_eSPI configuration" + #else + #define TFT_D0 CONFIG_TFT_D0 + #endif + + #if CONFIG_TFT_D1 == -1 + #error "Invalid Data 1 pin. Check TFT_eSPI configuration" + #else + #define TFT_D1 CONFIG_TFT_D1 + #endif + + #if CONFIG_TFT_D2 == -1 + #error "Invalid Data 2 pin. Check TFT_eSPI configuration" + #else + #define TFT_D2 CONFIG_TFT_D2 + #endif + + #if CONFIG_TFT_D3 == -1 + #error "Invalid Data 3 pin. Check TFT_eSPI configuration" + #else + #define TFT_D3 CONFIG_TFT_D3 + #endif + + #if CONFIG_TFT_D4 == -1 + #error "Invalid Data 4 pin. Check TFT_eSPI configuration" + #else + #define TFT_D4 CONFIG_TFT_D4 + #endif + + #if CONFIG_TFT_D5 == -1 + #error "Invalid Data 5 pin. Check TFT_eSPI configuration" + #else + #define TFT_D5 CONFIG_TFT_D5 + #endif + + #if CONFIG_TFT_D6 == -1 + #error "Invalid Data 6 pin. Check TFT_eSPI configuration" + #else + #define TFT_D6 CONFIG_TFT_D6 + #endif + + #if CONFIG_TFT_D7 == -1 + #error "Invalid Data 7 pin. Check TFT_eSPI configuration" + #else + #define TFT_D7 CONFIG_TFT_D7 + #endif + + #if CONFIG_TFT_WR == -1 + #error "Invalid Write strobe pin. Check TFT_eSPI configuration" + #else + #define TFT_WR CONFIG_TFT_WR + #endif + + #if CONFIG_TFT_RD == -1 + #error "Invalid Read strobe pin. Check TFT_eSPI configuration" + #else + #define TFT_RD CONFIG_TFT_RD + #endif + +// SPI BUS +#else + #if CONFIG_TFT_HSPI_PORT + #define USE_HSPI_PORT + #endif + + #if CONFIG_TFT_MISO != -1 + #define TFT_MISO CONFIG_TFT_MISO + #endif + + #if CONFIG_TFT_MOSI == -1 + #error "Invalid MOSI pin. Check TFT_eSPI configuration" + #else + #define TFT_MOSI CONFIG_TFT_MOSI + #endif + + #if CONFIG_TFT_SCLK == -1 + #error "Invalid Clock pin. Check TFT_eSPI configuration" + #else + #define TFT_SCLK CONFIG_TFT_SCLK + #endif + + #define SPI_FREQUENCY CONFIG_TFT_SPI_FREQUENCY + + #if CONFIG_TFT_SPI_READ_FREQ != -1 + #define SPI_READ_FREQUENCY CONFIG_TFT_SPI_READ_FREQ + #endif + + #ifdef CONFIG_TFT_SDA_READ + #define TFT_SDA_READ + #endif +#endif + + +/*************************************************************************************** +** Section 4: Setup Fonts +***************************************************************************************/ + +#ifdef CONFIG_TFT_LOAD_GLCD + #define LOAD_GLCD // Font 1. Original Adafruit 8 pixel font needs ~1820 bytes in FLASH +#endif + +#ifdef CONFIG_TFT_LOAD_FONT2 + #define LOAD_FONT2 // Font 2. Small 16 pixel high font, needs ~3534 bytes in FLASH, 96 characters +#endif + +#ifdef CONFIG_TFT_LOAD_FONT4 + #define LOAD_FONT4 // Font 4. Medium 26 pixel high font, needs ~5848 bytes in FLASH, 96 characters +#endif + +#ifdef CONFIG_TFT_LOAD_FONT6 + #define LOAD_FONT6 // Font 6. Large 48 pixel font, needs ~2666 bytes in FLASH, only characters 1234567890:-.apm +#endif + +#ifdef CONFIG_TFT_LOAD_FONT7 + #define LOAD_FONT7 // Font 7. 7 segment 48 pixel font, needs ~2438 bytes in FLASH, only characters 1234567890:. +#endif + +#ifdef CONFIG_TFT_LOAD_FONT8 + #define LOAD_FONT8 // Font 8. Large 75 pixel font needs ~3256 bytes in FLASH, only characters 1234567890:-. +#endif + +#ifdef CONFIG_TFT_LOAD_GFXFF + #define LOAD_GFXFF // FreeFonts. Include access to the 48 Adafruit_GFX free fonts FF1 to FF48 and custom fonts +#endif + +#if CONFIG_TFT_SMOOTH_FONT + #define SMOOTH_FONT +#endif + +/*************************************************************************************** +** Section 5: Touchscreen configuration +***************************************************************************************/ + +#ifdef CONFIG_ENABLE_TOUCH + #if CONFIG_TOUCH_CS == -1 + #error "Invalid Touch Chip Select pin. Check TFT_eSPI configuration" + #else + #define TOUCH_CS CONFIG_TOUCH_CS + #endif + + #define SPI_TOUCH_FREQUENCY CONFIG_SPI_TOUCH_FREQUENCY +#endif + +#endif // TFT_CONFIG_H diff --git a/lib/Seeed_GFX/TFT_eSPI.cpp b/lib/Seeed_GFX/TFT_eSPI.cpp new file mode 100644 index 0000000..da4424e --- /dev/null +++ b/lib/Seeed_GFX/TFT_eSPI.cpp @@ -0,0 +1,6530 @@ +/*************************************************** + Arduino TFT graphics library targeted at 32-bit + processors such as ESP32, ESP8266 and STM32. + + This is a stand-alone library that contains the + hardware driver, the graphics functions and the + proportional fonts. + + The larger fonts are Run Length Encoded to reduce their + size. + + Created by Bodmer 2/12/16 + Last update by Bodmer 20/03/20 + ****************************************************/ + +#include "TFT_eSPI.h" + +#if defined(OPENDISPLAY_SEEED_GFX_RUNTIME_PINS) +#include "OpenDisplay/opendisplay_runtime_pins.h" +#endif + +#if defined (ESP32) + #if defined(CONFIG_IDF_TARGET_ESP32S3) + #include "Processors/TFT_eSPI_ESP32_S3.c" // Tested with SPI and 8-bit parallel + #elif defined(CONFIG_IDF_TARGET_ESP32C5) + #include "Processors/TFT_eSPI_ESP32_C5.cpp" // ESP32-C5: SPIClass-based backend (C++ wrapper) + #elif defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32C6) + #include "Processors/TFT_eSPI_ESP32_C3.c" // Tested with SPI (8-bit parallel will probably work too!) + #else + #include "Processors/TFT_eSPI_ESP32.c" + #endif +#elif defined (ARDUINO_ARCH_ESP8266) + #include "Processors/TFT_eSPI_ESP8266.c" +#elif defined (STM32) // (_VARIANT_ARDUINO_STM32_) stm32_def.h + #include "Processors/TFT_eSPI_STM32.c" +#elif defined (ARDUINO_ARCH_RP2040) || defined (ARDUINO_ARCH_MBED) && !defined(ARDUINO_ARCH_NRF52840)// Raspberry Pi Pico + #include "Processors/TFT_eSPI_RP2040.c" +#elif defined(SEEED_XIAO_M0) + #include "Processors/TFT_eSPI_SAMD21.c" +#elif defined(ARDUINO_XIAO_RA4M1) + #include "Processors/TFT_eSPI_RA4M1.c" +#elif defined(NRF52840_XXAA) + #include "Processors/TFT_eSPI_nRF52840.c" +#elif defined (EFR32MG24B220F1536IM48) + #include "Processors/TFT_eSPI_MG24.cpp" +#else + #include "Processors/TFT_eSPI_Generic.c" +#endif + +#ifndef SPI_BUSY_CHECK + #define SPI_BUSY_CHECK +#endif + +// Clipping macro for pushImage +#define PI_CLIP \ + if (_vpOoB) return; \ + x+= _xDatum; \ + y+= _yDatum; \ + \ + if ((x >= _vpW) || (y >= _vpH)) return; \ + \ + int32_t dx = 0; \ + int32_t dy = 0; \ + int32_t dw = w; \ + int32_t dh = h; \ + \ + if (x < _vpX) { dx = _vpX - x; dw -= dx; x = _vpX; } \ + if (y < _vpY) { dy = _vpY - y; dh -= dy; y = _vpY; } \ + \ + if ((x + dw) > _vpW ) dw = _vpW - x; \ + if ((y + dh) > _vpH ) dh = _vpH - y; \ + \ + if (dw < 1 || dh < 1) return; + +/*************************************************************************************** +** Function name: Legacy - deprecated +** Description: Start/end transaction +***************************************************************************************/ + void TFT_eSPI::spi_begin() {begin_tft_write();} + void TFT_eSPI::spi_end() { end_tft_write();} + void TFT_eSPI::spi_begin_read() {begin_tft_read(); } + void TFT_eSPI::spi_end_read() { end_tft_read(); } + +/*************************************************************************************** +** Function name: begin_tft_write (was called spi_begin) +** Description: Start SPI transaction for writes and select TFT +***************************************************************************************/ +inline void TFT_eSPI::begin_tft_write(void){ + if (locked) { + locked = false; // Flag to show SPI access now unlocked +#if defined (SPI_HAS_TRANSACTION) && defined (SUPPORT_TRANSACTIONS) && !defined(TFT_PARALLEL_8_BIT) && !defined(RP2040_PIO_INTERFACE) + spi.beginTransaction(SPISettings(SPI_FREQUENCY, MSBFIRST, TFT_SPI_MODE)); +#endif + CS_L; + SET_BUS_WRITE_MODE; // Some processors (e.g. ESP32) allow recycling the tx buffer when rx is not used + } +} + +// Non-inlined version to permit override +void TFT_eSPI::begin_nin_write(void){ + if (locked) { + locked = false; // Flag to show SPI access now unlocked +#if defined (SPI_HAS_TRANSACTION) && defined (SUPPORT_TRANSACTIONS) && !defined(TFT_PARALLEL_8_BIT) && !defined(RP2040_PIO_INTERFACE) + spi.beginTransaction(SPISettings(SPI_FREQUENCY, MSBFIRST, TFT_SPI_MODE)); +#endif + CS_L; + SET_BUS_WRITE_MODE; // Some processors (e.g. ESP32) allow recycling the tx buffer when rx is not used + } +} + +/*************************************************************************************** +** Function name: end_tft_write (was called spi_end) +** Description: End transaction for write and deselect TFT +***************************************************************************************/ +inline void TFT_eSPI::end_tft_write(void){ + if(!inTransaction) { // Flag to stop ending transaction during multiple graphics calls + if (!locked) { // Locked when beginTransaction has been called + locked = true; // Flag to show SPI access now locked + SPI_BUSY_CHECK; // Check send complete and clean out unused rx data + CS_H; + SET_BUS_READ_MODE; // In case bus has been configured for tx only +#if defined (SPI_HAS_TRANSACTION) && defined (SUPPORT_TRANSACTIONS) && !defined(TFT_PARALLEL_8_BIT) && !defined(RP2040_PIO_INTERFACE) + spi.endTransaction(); +#endif + } + } +} + +// Non-inlined version to permit override +inline void TFT_eSPI::end_nin_write(void){ + if(!inTransaction) { // Flag to stop ending transaction during multiple graphics calls + if (!locked) { // Locked when beginTransaction has been called + locked = true; // Flag to show SPI access now locked + SPI_BUSY_CHECK; // Check send complete and clean out unused rx data + CS_H; + SET_BUS_READ_MODE; // In case SPI has been configured for tx only +#if defined (SPI_HAS_TRANSACTION) && defined (SUPPORT_TRANSACTIONS) && !defined(TFT_PARALLEL_8_BIT) && !defined(RP2040_PIO_INTERFACE) + spi.endTransaction(); +#endif + } + } +} + +/*************************************************************************************** +** Function name: begin_tft_read (was called spi_begin_read) +** Description: Start transaction for reads and select TFT +***************************************************************************************/ +// Reads require a lower SPI clock rate than writes +inline void TFT_eSPI::begin_tft_read(void){ + DMA_BUSY_CHECK; // Wait for any DMA transfer to complete before changing SPI settings +#if defined (SPI_HAS_TRANSACTION) && defined (SUPPORT_TRANSACTIONS) && !defined(TFT_PARALLEL_8_BIT) && !defined(RP2040_PIO_INTERFACE) + if (locked) { + locked = false; + spi.beginTransaction(SPISettings(SPI_READ_FREQUENCY, MSBFIRST, TFT_SPI_MODE)); + CS_L; + } +#else + #if !defined(TFT_PARALLEL_8_BIT) && !defined(RP2040_PIO_INTERFACE) + spi.setFrequency(SPI_READ_FREQUENCY); + #endif + CS_L; +#endif + SET_BUS_READ_MODE; +} + +/*************************************************************************************** +** Function name: end_tft_read (was called spi_end_read) +** Description: End transaction for reads and deselect TFT +***************************************************************************************/ +inline void TFT_eSPI::end_tft_read(void){ +#if defined (SPI_HAS_TRANSACTION) && defined (SUPPORT_TRANSACTIONS) && !defined(TFT_PARALLEL_8_BIT) && !defined(RP2040_PIO_INTERFACE) + if(!inTransaction) { + if (!locked) { + locked = true; + CS_H; + spi.endTransaction(); + } + } +#else + #if !defined(TFT_PARALLEL_8_BIT) && !defined(RP2040_PIO_INTERFACE) + spi.setFrequency(SPI_FREQUENCY); + #endif + if(!inTransaction) {CS_H;} +#endif + SET_BUS_WRITE_MODE; +} + +/*************************************************************************************** +** Function name: setViewport +** Description: Set the clipping region for the TFT screen +***************************************************************************************/ +void TFT_eSPI::setViewport(int32_t x, int32_t y, int32_t w, int32_t h, bool vpDatum) +{ + // Viewport metrics (not clipped) + _xDatum = x; // Datum x position in screen coordinates + _yDatum = y; // Datum y position in screen coordinates + _xWidth = w; // Viewport width + _yHeight = h; // Viewport height + + // Full size default viewport + _vpDatum = false; // Datum is at top left corner of screen (true = top left of viewport) + _vpOoB = false; // Out of Bounds flag (true is all of viewport is off screen) + _vpX = 0; // Viewport top left corner x coordinate + _vpY = 0; // Viewport top left corner y coordinate + _vpW = width(); // Equivalent of TFT width (Nb: viewport right edge coord + 1) + _vpH = height(); // Equivalent of TFT height (Nb: viewport bottom edge coord + 1) + + // Clip viewport to screen area + 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; } + + //Serial.print(" x=");Serial.print( x);Serial.print(", y=");Serial.print( y); + //Serial.print(", w=");Serial.print(w);Serial.print(", h=");Serial.println(h); + + // Check if viewport is entirely out of bounds + if (w < 1 || h < 1) + { + // Set default values and Out of Bounds flag in case of error + _xDatum = 0; + _yDatum = 0; + _xWidth = width(); + _yHeight = height(); + _vpOoB = true; // Set Out of Bounds flag to inhibit all drawing + return; + } + + if (!vpDatum) + { + _xDatum = 0; // Reset to top left of screen if not using a viewport datum + _yDatum = 0; + _xWidth = width(); + _yHeight = height(); + } + + // Store the clipped screen viewport metrics and datum position + _vpX = x; + _vpY = y; + _vpW = x + w; + _vpH = y + h; + _vpDatum = vpDatum; + + //Serial.print(" _xDatum=");Serial.print( _xDatum);Serial.print(", _yDatum=");Serial.print( _yDatum); + //Serial.print(", _xWidth=");Serial.print(_xWidth);Serial.print(", _yHeight=");Serial.println(_yHeight); + + //Serial.print(" _vpX=");Serial.print( _vpX);Serial.print(", _vpY=");Serial.print( _vpY); + //Serial.print(", _vpW=");Serial.print(_vpW);Serial.print(", _vpH=");Serial.println(_vpH); +} + +/*************************************************************************************** +** Function name: checkViewport +** Description: Check if any part of specified area is visible in viewport +***************************************************************************************/ +// Note: Setting w and h to 1 will check if coordinate x,y is in area +bool TFT_eSPI::checkViewport(int32_t x, int32_t y, int32_t w, int32_t h) +{ + if (_vpOoB) return false; + x+= _xDatum; + y+= _yDatum; + + if ((x >= _vpW) || (y >= _vpH)) return false; + + int32_t dx = 0; + int32_t dy = 0; + int32_t dw = w; + int32_t dh = h; + + if (x < _vpX) { dx = _vpX - x; dw -= dx; x = _vpX; } + if (y < _vpY) { dy = _vpY - y; dh -= dy; y = _vpY; } + + if ((x + dw) > _vpW ) dw = _vpW - x; + if ((y + dh) > _vpH ) dh = _vpH - y; + + if (dw < 1 || dh < 1) return false; + + return true; +} + +/*************************************************************************************** +** Function name: resetViewport +** Description: Reset viewport to whole TFT screen, datum at 0,0 +***************************************************************************************/ +void TFT_eSPI::resetViewport(void) +{ + // Reset viewport to the whole screen (or sprite) area + _vpDatum = false; + _vpOoB = false; + _xDatum = 0; + _yDatum = 0; + _vpX = 0; + _vpY = 0; + _vpW = width(); + _vpH = height(); + _xWidth = width(); + _yHeight = height(); +} + +/*************************************************************************************** +** Function name: getViewportX +** Description: Get x position of the viewport datum +***************************************************************************************/ +int32_t TFT_eSPI::getViewportX(void) +{ + return _xDatum; +} + +/*************************************************************************************** +** Function name: getViewportY +** Description: Get y position of the viewport datum +***************************************************************************************/ +int32_t TFT_eSPI::getViewportY(void) +{ + return _yDatum; +} + +/*************************************************************************************** +** Function name: getViewportWidth +** Description: Get width of the viewport +***************************************************************************************/ +int32_t TFT_eSPI::getViewportWidth(void) +{ + return _xWidth; +} + +/*************************************************************************************** +** Function name: getViewportHeight +** Description: Get height of the viewport +***************************************************************************************/ +int32_t TFT_eSPI::getViewportHeight(void) +{ + return _yHeight; +} + +/*************************************************************************************** +** Function name: getViewportDatum +** Description: Get datum flag of the viewport (true = viewport corner) +***************************************************************************************/ +bool TFT_eSPI::getViewportDatum(void) +{ + return _vpDatum; +} + +/*************************************************************************************** +** Function name: frameViewport +** Description: Draw a frame inside or outside the viewport of width w +***************************************************************************************/ +void TFT_eSPI::frameViewport(uint16_t color, int32_t w) +{ + // Save datum position + bool _dT = _vpDatum; + + // If w is positive the frame is drawn inside the viewport + // a large positive width will clear the screen inside the viewport + if (w>0) + { + // Set vpDatum true to simplify coordinate derivation + _vpDatum = true; + fillRect(0, 0, _vpW - _vpX, w, color); // Top + fillRect(0, w, w, _vpH - _vpY - w - w, color); // Left + fillRect(_xWidth - w, w, w, _yHeight - w - w, color); // Right + fillRect(0, _yHeight - w, _xWidth, w, color); // Bottom + } + else + // If w is negative the frame is drawn outside the viewport + // a large negative width will clear the screen outside the viewport + { + w = -w; + + // Save old values + int32_t _xT = _vpX; _vpX = 0; + int32_t _yT = _vpY; _vpY = 0; + int32_t _wT = _vpW; + int32_t _hT = _vpH; + + // Set vpDatum false so frame can be drawn outside window + _vpDatum = false; // When false the full width and height is accessed + _vpH = height(); + _vpW = width(); + + // Draw frame + fillRect(_xT - w - _xDatum, _yT - w - _yDatum, _wT - _xT + w + w, w, color); // Top + fillRect(_xT - w - _xDatum, _yT - _yDatum, w, _hT - _yT, color); // Left + fillRect(_wT - _xDatum, _yT - _yDatum, w, _hT - _yT, color); // Right + fillRect(_xT - w - _xDatum, _hT - _yDatum, _wT - _xT + w + w, w, color); // Bottom + + // Restore old values + _vpX = _xT; + _vpY = _yT; + _vpW = _wT; + _vpH = _hT; + } + + // Restore vpDatum + _vpDatum = _dT; +} + +/*************************************************************************************** +** Function name: clipAddrWindow +** Description: Clip address window x,y,w,h to screen and viewport +***************************************************************************************/ +bool TFT_eSPI::clipAddrWindow(int32_t *x, int32_t *y, int32_t *w, int32_t *h) +{ + if (_vpOoB) return false; // Area is outside of viewport + + *x+= _xDatum; + *y+= _yDatum; + + if ((*x >= _vpW) || (*y >= _vpH)) return false; // Area is outside of viewport + + // Crop drawing area bounds + if (*x < _vpX) { *w -= _vpX - *x; *x = _vpX; } + if (*y < _vpY) { *h -= _vpY - *y; *y = _vpY; } + + if ((*x + *w) > _vpW ) *w = _vpW - *x; + if ((*y + *h) > _vpH ) *h = _vpH - *y; + + if (*w < 1 || *h < 1) return false; // No area is inside viewport + + return true; // Area is wholly or partially inside viewport +} + +/*************************************************************************************** +** Function name: clipWindow +** Description: Clip window xs,yx,xe,ye to screen and viewport +***************************************************************************************/ +bool TFT_eSPI::clipWindow(int32_t *xs, int32_t *ys, int32_t *xe, int32_t *ye) +{ + if (_vpOoB) return false; // Area is outside of viewport + + *xs+= _xDatum; + *ys+= _yDatum; + *xe+= _xDatum; + *ye+= _yDatum; + + if ((*xs >= _vpW) || (*ys >= _vpH)) return false; // Area is outside of viewport + if ((*xe < _vpX) || (*ye < _vpY)) return false; // Area is outside of viewport + + // Crop drawing area bounds + if (*xs < _vpX) *xs = _vpX; + if (*ys < _vpY) *ys = _vpY; + + if (*xe > _vpW) *xe = _vpW - 1; + if (*ye > _vpH) *ye = _vpH - 1; + + return true; // Area is wholly or partially inside viewport +} + +/*************************************************************************************** +** Function name: TFT_eSPI +** Description: Constructor , we must use hardware SPI pins +***************************************************************************************/ +TFT_eSPI::TFT_eSPI(int16_t w, int16_t h) +{ + _init_width = _width = w; // Set by specific xxxxx_Defines.h file or by users sketch + _init_height = _height = h; // Set by specific xxxxx_Defines.h file or by users sketch + + // Reset the viewport to the whole screen + resetViewport(); + + rotation = 0; + cursor_y = cursor_x = last_cursor_x = bg_cursor_x = 0; + textfont = 1; + textsize = 1; + textcolor = bitmap_fg = 0xFFFF; // White + textbgcolor = bitmap_bg = 0x0000; // Black + padX = 0; // No padding + + _fillbg = false; // Smooth font only at the moment, force text background fill + + isDigits = false; // No bounding box adjustment + textwrapX = true; // Wrap text at end of line when using print stream + textwrapY = false; // Wrap text at bottom of screen when using print stream + textdatum = TL_DATUM; // Top Left text alignment is default + fontsloaded = 0; + + _swapBytes = false; // Do not swap colour bytes by default + + locked = true; // Transaction mutex lock flag to ensure begin/endTranaction pairing + inTransaction = false; // Flag to prevent multiple sequential functions to keep bus access open + lockTransaction = false; // start/endWrite lock flag to allow sketch to keep SPI bus access open + + _booted = true; // Default attributes + _cp437 = false; // Legacy GLCD font bug fix disabled by default + _utf8 = true; // UTF8 decoding enabled + +#if defined (FONT_FS_AVAILABLE) && defined (SMOOTH_FONT) + fs_font = true; // Smooth font filing system or array (fs_font = false) flag +#endif + +#if defined (ESP32) && defined (CONFIG_SPIRAM_SUPPORT) + if (psramFound()) _psram_enable = true; // Enable the use of PSRAM (if available) + else +#endif + _psram_enable = false; + + addr_row = 0xFFFF; // drawPixel command length optimiser + addr_col = 0xFFFF; // drawPixel command length optimiser + + _xPivot = 0; + _yPivot = 0; + +// Legacy support for bit GPIO masks + cspinmask = 0; + dcpinmask = 0; + wrpinmask = 0; + sclkpinmask = 0; + +// Flags for which fonts are loaded +#ifdef LOAD_GLCD + fontsloaded = 0x0002; // Bit 1 set +#endif + +#ifdef LOAD_FONT2 + fontsloaded |= 0x0004; // Bit 2 set +#endif + +#ifdef LOAD_FONT4 + fontsloaded |= 0x0010; // Bit 4 set +#endif + +#ifdef LOAD_FONT6 + fontsloaded |= 0x0040; // Bit 6 set +#endif + +#ifdef LOAD_FONT7 + fontsloaded |= 0x0080; // Bit 7 set +#endif + +#ifdef LOAD_FONT8 + fontsloaded |= 0x0100; // Bit 8 set +#endif + +#ifdef LOAD_FONT8N + fontsloaded |= 0x0200; // Bit 9 set +#endif + +#ifdef SMOOTH_FONT + fontsloaded |= 0x8000; // Bit 15 set +#endif +} + +/*************************************************************************************** +** Function name: initBus +** Description: initialise the SPI or parallel bus +***************************************************************************************/ +void TFT_eSPI::initBus(void) { + +#ifdef TFT_CS + if (TFT_CS >= 0) { +#if defined(OPENDISPLAY_SEEED_GFX_RUNTIME_PINS) + if (opnd_seeed_runtime_cs >= 0) { + pinMode(opnd_seeed_runtime_cs, OUTPUT); + digitalWrite(opnd_seeed_runtime_cs, HIGH); + } +#else + pinMode(TFT_CS, OUTPUT); + digitalWrite(TFT_CS, HIGH); // Chip select high (inactive) +#endif + } +#endif + +// Configure chip select for touchscreen controller if present +#ifdef TOUCH_CS + if (TOUCH_CS >= 0) { + pinMode(TOUCH_CS, OUTPUT); + digitalWrite(TOUCH_CS, HIGH); // Chip select high (inactive) + } +#endif + +// In parallel mode and with the RP2040 processor, the TFT_WR line is handled in the PIO +#if defined (TFT_WR) && !defined (ARDUINO_ARCH_RP2040) && !defined (ARDUINO_ARCH_MBED) + if (TFT_WR >= 0) { + pinMode(TFT_WR, OUTPUT); + digitalWrite(TFT_WR, HIGH); // Set write strobe high (inactive) + } +#endif + +#ifdef TFT_DC + if (TFT_DC >= 0) { + pinMode(TFT_DC, OUTPUT); + digitalWrite(TFT_DC, HIGH); // Data/Command high = data mode + } +#endif + +#ifdef TFT_RST + if (TFT_RST >= 0) { +#if defined(OPENDISPLAY_SEEED_GFX_RUNTIME_PINS) + if (opnd_seeed_runtime_rst >= 0) { + pinMode(opnd_seeed_runtime_rst, OUTPUT); + digitalWrite(opnd_seeed_runtime_rst, HIGH); + } +#else + pinMode(TFT_RST, OUTPUT); + digitalWrite(TFT_RST, HIGH); // Set high, do not share pin with another SPI device +#endif + } +#endif + +#if defined (TFT_PARALLEL_8_BIT) + + // Make sure read is high before we set the bus to output + if (TFT_RD >= 0) { + pinMode(TFT_RD, OUTPUT); + digitalWrite(TFT_RD, HIGH); + } + + #if !defined (ARDUINO_ARCH_RP2040) && !defined (ARDUINO_ARCH_MBED)// PIO manages pins + // Set TFT data bus lines to output + pinMode(TFT_D0, OUTPUT); digitalWrite(TFT_D0, HIGH); + pinMode(TFT_D1, OUTPUT); digitalWrite(TFT_D1, HIGH); + pinMode(TFT_D2, OUTPUT); digitalWrite(TFT_D2, HIGH); + pinMode(TFT_D3, OUTPUT); digitalWrite(TFT_D3, HIGH); + pinMode(TFT_D4, OUTPUT); digitalWrite(TFT_D4, HIGH); + pinMode(TFT_D5, OUTPUT); digitalWrite(TFT_D5, HIGH); + pinMode(TFT_D6, OUTPUT); digitalWrite(TFT_D6, HIGH); + pinMode(TFT_D7, OUTPUT); digitalWrite(TFT_D7, HIGH); + #endif + + PARALLEL_INIT_TFT_DATA_BUS; + +#endif +} + +/*************************************************************************************** +** Function name: begin +** Description: Included for backwards compatibility +***************************************************************************************/ +void TFT_eSPI::begin(uint8_t tc) +{ + init(tc); +} + +void TFT_eSPI::initFromSleep(uint8_t tc) +{ + + if (!_booted) return; + _booted = false; + + initBus(); + +#if !defined (ESP32) && !defined(TFT_PARALLEL_8_BIT) && !defined(ARDUINO_ARCH_RP2040) && !defined (ARDUINO_ARCH_MBED) && !defined (EFR32MG24B220F1536IM48) + // Legacy bitmasks for GPIO + #if defined (TFT_CS) && (TFT_CS >= 0) + cspinmask = (uint32_t) digitalPinToBitMask(TFT_CS); + #endif + + #if defined (TFT_DC) && (TFT_DC >= 0) + dcpinmask = (uint32_t) digitalPinToBitMask(TFT_DC); + #endif + + #if defined (TFT_WR) && (TFT_WR >= 0) + wrpinmask = (uint32_t) digitalPinToBitMask(TFT_WR); + #endif + + #if defined (TFT_SCLK) && (TFT_SCLK >= 0) + sclkpinmask = (uint32_t) digitalPinToBitMask(TFT_SCLK); + #endif + + #if defined (TFT_SPI_OVERLAP) && defined (ARDUINO_ARCH_ESP8266) + // Overlap mode SD0=MISO, SD1=MOSI, CLK=SCLK must use D3 as CS + // pins(int8_t sck, int8_t miso, int8_t mosi, int8_t ss); + //spi.pins( 6, 7, 8, 0); + spi.pins(6, 7, 8, 0); + #endif + + spi.begin(); // This will set HMISO to input + +#else + #if !defined(TFT_PARALLEL_8_BIT) && !defined(RP2040_PIO_INTERFACE) + #if defined (TFT_MOSI) && !defined (TFT_SPI_OVERLAP) && !defined(ARDUINO_ARCH_RP2040) && !defined (ARDUINO_ARCH_MBED) && !defined (EFR32MG24B220F1536IM48) +#if defined(OPENDISPLAY_SEEED_GFX_RUNTIME_PINS) + spi.begin(opnd_seeed_runtime_sclk, opnd_seeed_runtime_miso, opnd_seeed_runtime_mosi, -1); +#else + spi.begin(TFT_SCLK, TFT_MISO, TFT_MOSI, -1); // This will set MISO to input +#endif + #else + spi.begin(); // This will set MISO to input + #endif + #endif +#endif + + lockTransaction = false; + inTransaction = false; + locked = true; + + INIT_TFT_DATA_BUS; + +#if defined(TFT_CS) + if (TFT_CS >= 0) { +#if defined(OPENDISPLAY_SEEED_GFX_RUNTIME_PINS) + if (opnd_seeed_runtime_cs >= 0) { + pinMode(opnd_seeed_runtime_cs, OUTPUT); + digitalWrite(opnd_seeed_runtime_cs, HIGH); + } +#else + pinMode(TFT_CS, OUTPUT); + digitalWrite(TFT_CS, HIGH); +#endif + } +#endif + +#if defined(TFT_DC) + if (TFT_DC >= 0) { + pinMode(TFT_DC, OUTPUT); + digitalWrite(TFT_DC, HIGH); + } +#endif + +#ifdef TFT_RST + if (TFT_RST >= 0) { +#if defined(OPENDISPLAY_SEEED_GFX_RUNTIME_PINS) + if (opnd_seeed_runtime_rst >= 0) { + pinMode(opnd_seeed_runtime_rst, OUTPUT); + } +#else + pinMode(TFT_RST, OUTPUT); +#endif + } +#else + writecommand(TFT_SWRST); +#endif + + tc = tc; // Suppress warning + +#if defined (ILI9341_DRIVER) || defined(ILI9341_2_DRIVER) || defined (ILI9342_DRIVER) + #include "TFT_Drivers/ILI9341_Init.h" + +#elif defined (ST7735_DRIVER) + tabcolor = tc; + #include "TFT_Drivers/ST7735_Init.h" + +#elif defined (ILI9163_DRIVER) + #include "TFT_Drivers/ILI9163_Init.h" + +#elif defined (S6D02A1_DRIVER) + #include "TFT_Drivers/S6D02A1_Init.h" + +#elif defined (ST7796_DRIVER) + #include "TFT_Drivers/ST7796_Init.h" + +#elif defined (ILI9486_DRIVER) + #include "TFT_Drivers/ILI9486_Init.h" + +#elif defined (ILI9481_DRIVER) + #include "TFT_Drivers/ILI9481_Init.h" + +#elif defined (ILI9488_DRIVER) + #include "TFT_Drivers/ILI9488_Init.h" + +#elif defined (HX8357D_DRIVER) + #include "TFT_Drivers/HX8357D_Init.h" + +#elif defined (ST7789_DRIVER) + #include "TFT_Drivers/ST7789_Init.h" + +#elif defined (R61581_DRIVER) + #include "TFT_Drivers/R61581_Init.h" + +#elif defined (RM68140_DRIVER) + #include "TFT_Drivers/RM68140_Init.h" + +#elif defined (ST7789_2_DRIVER) + #include "TFT_Drivers/ST7789_2_Init.h" + +#elif defined (SSD1351_DRIVER) + #include "TFT_Drivers/SSD1351_Init.h" + +#elif defined (SSD1963_DRIVER) + #include "TFT_Drivers/SSD1963_Init.h" + +#elif defined (GC9A01_DRIVER) + #include "TFT_Drivers/GC9A01_Init.h" + +#elif defined (ILI9225_DRIVER) + #include "TFT_Drivers/ILI9225_Init.h" + +#elif defined (RM68120_DRIVER) + #include "TFT_Drivers/RM68120_Init.h" + +#elif defined (HX8357B_DRIVER) + #include "TFT_Drivers/HX8357B_Init.h" + +#elif defined (HX8357C_DRIVER) + #include "TFT_Drivers/HX8357C_Init.h" + +#elif defined (UC8179_DRIVER) + #include "TFT_Drivers/UC8179_Init.h" + +#elif defined (SSD1680_DRIVER) + #include "TFT_Drivers/SSD1680_Init.h" + +#elif defined (SSD1681_DRIVER) + #include "TFT_Drivers/SSD1681_Init.h" + +#elif defined (SSD1683_DRIVER) + #include "TFT_Drivers/SSD1683_Init.h" + +#elif defined (SSD1677_DRIVER) + #include "TFT_Drivers/SSD1677_Init.h" + +#elif defined (JD79686B_DRIVER) + #include "TFT_Drivers/JD79686B_Init.h" + +#elif defined (ED2208_DRIVER) + #include "TFT_Drivers/ED2208_Init.h" + +#elif defined (ED103TC2_DRIVER) + #include "TFT_Drivers/ED103TC2_Init_Wake.h" + +#elif defined (T133A01_DRIVER) + #include "TFT_Drivers/T133A01_Init.h" + +#elif defined (JD79667_DRIVER) + #include "TFT_Drivers/JD79667_Init.h" + +#elif defined (JD79676_DRIVER) + #include "TFT_Drivers/JD79676_Init.h" + +#elif defined (SSD2677_DRIVER) + #include "TFT_Drivers/SSD2677_Init.h" +#elif defined (JD79660_DRIVER) + #include "TFT_Drivers/JD79660_Init.h" +#endif + +#if defined (TFT_BL) && defined (TFT_BACKLIGHT_ON) + if (TFT_BL >= 0) { + pinMode(TFT_BL, OUTPUT); + digitalWrite(TFT_BL, TFT_BACKLIGHT_ON); + } +#endif +} +/*************************************************************************************** +** Function name: init (tc is tab colour for ST7735 displays only) +** Description: Reset, then initialise the TFT display registers +***************************************************************************************/ +void TFT_eSPI::init(uint8_t tc) +{ + if (_booted) + { + initBus(); + +#if !defined (ESP32) && !defined(TFT_PARALLEL_8_BIT) && !defined(ARDUINO_ARCH_RP2040) && !defined (ARDUINO_ARCH_MBED) && !defined (EFR32MG24B220F1536IM48) + // Legacy bitmasks for GPIO + #if defined (TFT_CS) && (TFT_CS >= 0) + cspinmask = (uint32_t) digitalPinToBitMask(TFT_CS); + #endif + + #if defined (TFT_DC) && (TFT_DC >= 0) + dcpinmask = (uint32_t) digitalPinToBitMask(TFT_DC); + #endif + + #if defined (TFT_WR) && (TFT_WR >= 0) + wrpinmask = (uint32_t) digitalPinToBitMask(TFT_WR); + #endif + + #if defined (TFT_SCLK) && (TFT_SCLK >= 0) + sclkpinmask = (uint32_t) digitalPinToBitMask(TFT_SCLK); + #endif + + #if defined (TFT_SPI_OVERLAP) && defined (ARDUINO_ARCH_ESP8266) + // Overlap mode SD0=MISO, SD1=MOSI, CLK=SCLK must use D3 as CS + // pins(int8_t sck, int8_t miso, int8_t mosi, int8_t ss); + //spi.pins( 6, 7, 8, 0); + spi.pins(6, 7, 8, 0); + #endif + + spi.begin(); // This will set HMISO to input + +#else + #if !defined(TFT_PARALLEL_8_BIT) && !defined(RP2040_PIO_INTERFACE) + #if defined (TFT_MOSI) && !defined (TFT_SPI_OVERLAP) && !defined(ARDUINO_ARCH_RP2040) && !defined (ARDUINO_ARCH_MBED) && !defined (EFR32MG24B220F1536IM48) +#if defined(OPENDISPLAY_SEEED_GFX_RUNTIME_PINS) + spi.begin(opnd_seeed_runtime_sclk, opnd_seeed_runtime_miso, opnd_seeed_runtime_mosi, -1); +#else + spi.begin(TFT_SCLK, TFT_MISO, TFT_MOSI, -1); // This will set MISO to input +#endif + #else + spi.begin(); // This will set MISO to input + #endif + #endif +#endif + lockTransaction = false; + inTransaction = false; + locked = true; + + INIT_TFT_DATA_BUS; + + +#if defined (TFT_CS) && !defined(RP2040_PIO_INTERFACE) + // Set to output once again in case MISO is used for CS + if (TFT_CS >= 0) { +#if defined(OPENDISPLAY_SEEED_GFX_RUNTIME_PINS) + if (opnd_seeed_runtime_cs >= 0) { + pinMode(opnd_seeed_runtime_cs, OUTPUT); + digitalWrite(opnd_seeed_runtime_cs, HIGH); + } +#else + pinMode(TFT_CS, OUTPUT); + digitalWrite(TFT_CS, HIGH); // Chip select high (inactive) +#endif + } +#elif defined (ARDUINO_ARCH_ESP8266) && !defined (TFT_PARALLEL_8_BIT) && !defined (RP2040_PIO_SPI) + spi.setHwCs(1); // Use hardware SS toggling +#endif + + + // Set to output once again in case MISO is used for DC +#if defined (TFT_DC) && !defined(RP2040_PIO_INTERFACE) + if (TFT_DC >= 0) { + pinMode(TFT_DC, OUTPUT); + digitalWrite(TFT_DC, HIGH); // Data/Command high = data mode + } +#endif + + _booted = false; + end_tft_write(); + } // end of: if just _booted + + // Toggle RST low to reset +#ifdef TFT_RST + #if !defined(RP2040_PIO_INTERFACE) + // Set to output once again in case MISO is used for TFT_RST + if (TFT_RST >= 0) { +#if defined(OPENDISPLAY_SEEED_GFX_RUNTIME_PINS) + if (opnd_seeed_runtime_rst >= 0) { + pinMode(opnd_seeed_runtime_rst, OUTPUT); + } +#else + pinMode(TFT_RST, OUTPUT); +#endif + } + #endif + if (TFT_RST >= 0) { + writecommand(0x00); // Put SPI bus in known state for TFT with CS tied low +#if defined(OPENDISPLAY_SEEED_GFX_RUNTIME_PINS) + if (opnd_seeed_runtime_rst >= 0) { + digitalWrite(opnd_seeed_runtime_rst, HIGH); + delay(5); + digitalWrite(opnd_seeed_runtime_rst, LOW); + delay(20); + digitalWrite(opnd_seeed_runtime_rst, HIGH); + } +#else + digitalWrite(TFT_RST, HIGH); + delay(5); + digitalWrite(TFT_RST, LOW); + delay(20); + digitalWrite(TFT_RST, HIGH); +#endif + } + else writecommand(TFT_SWRST); // Software reset +#else + writecommand(TFT_SWRST); // Software reset +#endif + + delay(150); // Wait for reset to complete + + begin_tft_write(); + + tc = tc; // Suppress warning + + // This loads the driver specific initialisation code <<<<<<<<<<<<<<<<<<<<< ADD NEW DRIVERS TO THE LIST HERE <<<<<<<<<<<<<<<<<<<<<<< +#if defined (ILI9341_DRIVER) || defined(ILI9341_2_DRIVER) || defined (ILI9342_DRIVER) + #include "TFT_Drivers/ILI9341_Init.h" + +#elif defined (ST7735_DRIVER) + tabcolor = tc; + #include "TFT_Drivers/ST7735_Init.h" + +#elif defined (ILI9163_DRIVER) + #include "TFT_Drivers/ILI9163_Init.h" + +#elif defined (S6D02A1_DRIVER) + #include "TFT_Drivers/S6D02A1_Init.h" + +#elif defined (ST7796_DRIVER) + #include "TFT_Drivers/ST7796_Init.h" + +#elif defined (ILI9486_DRIVER) + #include "TFT_Drivers/ILI9486_Init.h" + +#elif defined (ILI9481_DRIVER) + #include "TFT_Drivers/ILI9481_Init.h" + +#elif defined (ILI9488_DRIVER) + #include "TFT_Drivers/ILI9488_Init.h" + +#elif defined (HX8357D_DRIVER) + #include "TFT_Drivers/HX8357D_Init.h" + +#elif defined (ST7789_DRIVER) + #include "TFT_Drivers/ST7789_Init.h" + +#elif defined (R61581_DRIVER) + #include "TFT_Drivers/R61581_Init.h" + +#elif defined (RM68140_DRIVER) + #include "TFT_Drivers/RM68140_Init.h" + +#elif defined (ST7789_2_DRIVER) + #include "TFT_Drivers/ST7789_2_Init.h" + +#elif defined (SSD1351_DRIVER) + #include "TFT_Drivers/SSD1351_Init.h" + +#elif defined (SSD1963_DRIVER) + #include "TFT_Drivers/SSD1963_Init.h" + +#elif defined (GC9A01_DRIVER) + #include "TFT_Drivers/GC9A01_Init.h" + +#elif defined (ILI9225_DRIVER) + #include "TFT_Drivers/ILI9225_Init.h" + +#elif defined (RM68120_DRIVER) + #include "TFT_Drivers/RM68120_Init.h" + +#elif defined (HX8357B_DRIVER) + #include "TFT_Drivers/HX8357B_Init.h" + +#elif defined (HX8357C_DRIVER) + #include "TFT_Drivers/HX8357C_Init.h" + +#elif defined (UC8179_DRIVER) + #include "TFT_Drivers/UC8179_Init.h" + +#elif defined (SSD1680_DRIVER) + #include "TFT_Drivers/SSD1680_Init.h" + +#elif defined (SSD1681_DRIVER) + #include "TFT_Drivers/SSD1681_Init.h" + +#elif defined (SSD1683_DRIVER) + #include "TFT_Drivers/SSD1683_Init.h" + +#elif defined (SSD1677_DRIVER) + #include "TFT_Drivers/SSD1677_Init.h" + +#elif defined (JD79686B_DRIVER) + #include "TFT_Drivers/JD79686B_Init.h" + +#elif defined (ED2208_DRIVER) + #include "TFT_Drivers/ED2208_Init.h" + +#elif defined (ED103TC2_DRIVER) + #include "TFT_Drivers/ED103TC2_Init.h" + +#elif defined (T133A01_DRIVER) + #include "TFT_Drivers/T133A01_Init.h" + +#elif defined (JD79667_DRIVER) + #include "TFT_Drivers/JD79667_Init.h" + +#elif defined (JD79676_DRIVER) + #include "TFT_Drivers/JD79676_Init.h" + +#elif defined (SSD2677_DRIVER) + #include "TFT_Drivers/SSD2677_Init.h" +#endif + +#ifdef TFT_INVERSION_ON + writecommand(TFT_INVON); +#endif + +#ifdef TFT_INVERSION_OFF + writecommand(TFT_INVOFF); +#endif + + end_tft_write(); + + //setRotation(rotation); + +#if defined (TFT_BL) && defined (TFT_BACKLIGHT_ON) + if (TFT_BL >= 0) { + pinMode(TFT_BL, OUTPUT); + digitalWrite(TFT_BL, TFT_BACKLIGHT_ON); + } +#else + #if defined (TFT_BL) && defined (M5STACK) + // Turn on the back-light LED + if (TFT_BL >= 0) { + pinMode(TFT_BL, OUTPUT); + digitalWrite(TFT_BL, HIGH); + } + #endif +#endif + +#include "Touch_Drivers/Touch_Init.cpp" + +} + + +/*************************************************************************************** +** Function name: setRotation +** Description: rotate the screen orientation m = 0-3 or 4-7 for BMP drawing +***************************************************************************************/ +void TFT_eSPI::setRotation(uint8_t m) +{ + + begin_tft_write(); + + // This loads the driver specific rotation code <<<<<<<<<<<<<<<<<<<<< ADD NEW DRIVERS TO THE LIST HERE <<<<<<<<<<<<<<<<<<<<<<< +#if defined (ILI9341_DRIVER) || defined(ILI9341_2_DRIVER) || defined (ILI9342_DRIVER) + #include "TFT_Drivers/ILI9341_Rotation.h" + +#elif defined (ST7735_DRIVER) + #include "TFT_Drivers/ST7735_Rotation.h" + +#elif defined (ILI9163_DRIVER) + #include "TFT_Drivers/ILI9163_Rotation.h" + +#elif defined (S6D02A1_DRIVER) + #include "TFT_Drivers/S6D02A1_Rotation.h" + +#elif defined (ST7796_DRIVER) + #include "TFT_Drivers/ST7796_Rotation.h" + +#elif defined (ILI9486_DRIVER) + #include "TFT_Drivers/ILI9486_Rotation.h" + +#elif defined (ILI9481_DRIVER) + #include "TFT_Drivers/ILI9481_Rotation.h" + +#elif defined (ILI9488_DRIVER) + #include "TFT_Drivers/ILI9488_Rotation.h" + +#elif defined (HX8357D_DRIVER) + #include "TFT_Drivers/HX8357D_Rotation.h" + +#elif defined (ST7789_DRIVER) + #include "TFT_Drivers/ST7789_Rotation.h" + +#elif defined (R61581_DRIVER) + #include "TFT_Drivers/R61581_Rotation.h" + +#elif defined (RM68140_DRIVER) + #include "TFT_Drivers/RM68140_Rotation.h" + +#elif defined (ST7789_2_DRIVER) + #include "TFT_Drivers/ST7789_2_Rotation.h" + +#elif defined (SSD1351_DRIVER) + #include "TFT_Drivers/SSD1351_Rotation.h" + +#elif defined (SSD1963_DRIVER) + #include "TFT_Drivers/SSD1963_Rotation.h" + +#elif defined (GC9A01_DRIVER) + #include "TFT_Drivers/GC9A01_Rotation.h" + +#elif defined (ILI9225_DRIVER) + #include "TFT_Drivers/ILI9225_Rotation.h" + +#elif defined (RM68120_DRIVER) + #include "TFT_Drivers/RM68120_Rotation.h" + +#elif defined (HX8357B_DRIVER) + #include "TFT_Drivers/HX8357B_Rotation.h" + +#elif defined (HX8357C_DRIVER) + #include "TFT_Drivers/HX8357C_Rotation.h" + +#elif defined (UC8179_DRIVER) + #include "TFT_Drivers/UC8179_Rotation.h" + +#elif defined (SSD1680_DRIVER) + #include "TFT_Drivers/SSD1680_Rotation.h" + +#elif defined (SSD1681_DRIVER) + #include "TFT_Drivers/SSD1681_Rotation.h" + +#elif defined (SSD1683_DRIVER) + #include "TFT_Drivers/SSD1683_Rotation.h" + +#elif defined (SSD1677_DRIVER) + #include "TFT_Drivers/SSD1677_Rotation.h" + +#elif defined (JD79686B_DRIVER) + #include "TFT_Drivers/JD79686B_Rotation.h" + +#elif defined (ED2208_DRIVER) + #include "TFT_Drivers/ED2208_Rotation.h" + +#elif defined (T133A01_DRIVER) + #include "TFT_Drivers/ED2208_Rotation.h" + +#elif defined (ED103TC2_DRIVER) + #include "TFT_Drivers/ED103TC2_Rotation.h" + +#elif defined (JD79667_DRIVER) + #include "TFT_Drivers/JD79667_Rotation.h" + +#elif defined (JD79676_DRIVER) + #include "TFT_Drivers/JD79676_Rotation.h" + +#elif defined (SSD2677_DRIVER) + #include "TFT_Drivers/SSD2677_Rotation.h" +#elif defined (JD79660_DRIVER) + #include "TFT_Drivers/JD79660_Rotation.h" +#endif + + delayMicroseconds(10); + + end_tft_write(); + + addr_row = 0xFFFF; + addr_col = 0xFFFF; + + // Reset the viewport to the whole screen + resetViewport(); +} + + +/*************************************************************************************** +** Function name: getRotation +** Description: Return the rotation value (as used by setRotation()) +***************************************************************************************/ +uint8_t TFT_eSPI::getRotation(void) +{ + return rotation; +} + + +/*************************************************************************************** +** Function name: setOrigin +** Description: Set graphics origin to position x,y wrt to top left corner +***************************************************************************************/ +//Note: setRotation, setViewport and resetViewport will revert origin to top left +void TFT_eSPI::setOrigin(int32_t x, int32_t y) +{ + _xDatum = x; + _yDatum = y; +} + + +/*************************************************************************************** +** Function name: getOriginX +** Description: Set graphics origin to position x +***************************************************************************************/ +int32_t TFT_eSPI::getOriginX(void) +{ + return _xDatum; +} + + +/*************************************************************************************** +** Function name: getOriginY +** Description: Set graphics origin to position y +***************************************************************************************/ +int32_t TFT_eSPI::getOriginY(void) +{ + return _yDatum; +} + + +/*************************************************************************************** +** Function name: commandList, used for FLASH based lists only (e.g. ST7735) +** Description: Get initialisation commands from FLASH and send to TFT +***************************************************************************************/ +void TFT_eSPI::commandList (const uint8_t *addr) +{ + uint8_t numCommands; + uint8_t numArgs; + uint8_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 & TFT_INIT_DELAY; // If high bit set, delay follows args + numArgs &= ~TFT_INIT_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) + delay( (ms==255 ? 500 : ms) ); + } + } + +} + + +/*************************************************************************************** +** Function name: spiwrite +** Description: Write 8 bits to SPI port (legacy support only) +***************************************************************************************/ +void TFT_eSPI::spiwrite(uint8_t c) +{ + begin_tft_write(); + tft_Write_8(c); + end_tft_write(); +} + + +/*************************************************************************************** +** Function name: writecommand +** Description: Send an 8-bit command to the TFT +***************************************************************************************/ +#ifndef RM68120_DRIVER +void TFT_eSPI::writecommand(uint8_t c) +{ + begin_tft_write(); + + DC_C; + + tft_Write_8(c); + + DC_D; + + end_tft_write(); +} + + +#else +void TFT_eSPI::writecommand(uint16_t c) +{ + begin_tft_write(); + + DC_C; + + tft_Write_16(c); + + DC_D; + + end_tft_write(); + +} + +void TFT_eSPI::writeRegister8(uint16_t c, uint8_t d) +{ + begin_tft_write(); + + DC_C; + + tft_Write_16(c); + + DC_D; + + tft_Write_8(d); + + end_tft_write(); + +} +void TFT_eSPI::writeRegister16(uint16_t c, uint16_t d) +{ + begin_tft_write(); + + DC_C; + + tft_Write_16(c); + + DC_D; + + tft_Write_16(d); + + end_tft_write(); + +} + +#endif + +/*************************************************************************************** +** Function name: writedata +** Description: Send a 8-bit data value to the TFT +***************************************************************************************/ +void TFT_eSPI::writedata(uint8_t d) +{ + begin_tft_write(); + + DC_D; // Play safe, but should already be in data mode + + tft_Write_8(d); + + CS_L; // Allow more hold time for low VDI rail + + end_tft_write(); +} + +void TFT_eSPI::writendata(uint8_t* d, uint16_t dataLength) +{ + begin_tft_write(); + + DC_D; // Play safe, but should already be in data mode + + for(int i = 0; i < dataLength; i++) + { + tft_Write_8(d[i]); + } + CS_L; // Allow more hold time for low VDI rail + + end_tft_write(); +} + +void TFT_eSPI::writecommanddata(uint8_t c,const uint8_t* d, uint16_t dataLength) +{ + begin_tft_write(); + DC_C; // Play safe, but should already be in command mode + tft_Write_8(c); + DC_D; // Play safe, but should already be in data mode + + for(int i = 0; i < dataLength; i++) + { + tft_Write_8(d[i]); + } + CS_L; // Allow more hold time for low VDI rail + end_tft_write(); +} +/*************************************************************************************** +** Function name: readcommand8 +** Description: Read a 8-bit data value from an indexed command register +***************************************************************************************/ +uint8_t TFT_eSPI::readcommand8(uint8_t cmd_function, uint8_t index) +{ + uint8_t reg = 0; +#if defined(TFT_PARALLEL_8_BIT) || defined(RP2040_PIO_INTERFACE) + + writecommand(cmd_function); // Sets DC and CS high + + busDir(GPIO_DIR_MASK, INPUT); + + CS_L; + + // Read nth parameter (assumes caller discards 1st parameter or points index to 2nd) + while(index--) reg = readByte(); + + busDir(GPIO_DIR_MASK, OUTPUT); + + CS_H; + +#else // SPI interface + // Tested with ILI9341 set to Interface II i.e. IM [3:0] = "1101" + begin_tft_read(); + index = 0x10 + (index & 0x0F); + + DC_C; tft_Write_8(0xD9); + DC_D; tft_Write_8(index); + + CS_H; // Some displays seem to need CS to be pulsed here, or is just a delay needed? + CS_L; + + DC_C; tft_Write_8(cmd_function); + DC_D; + reg = tft_Read_8(); + + end_tft_read(); +#endif + return reg; +} + + +/*************************************************************************************** +** Function name: readcommand16 +** Description: Read a 16-bit data value from an indexed command register +***************************************************************************************/ +uint16_t TFT_eSPI::readcommand16(uint8_t cmd_function, uint8_t index) +{ + uint32_t reg; + + reg = (readcommand8(cmd_function, index + 0) << 8); + reg |= (readcommand8(cmd_function, index + 1) << 0); + + return reg; +} + + +/*************************************************************************************** +** Function name: readcommand32 +** Description: Read a 32-bit data value from an indexed command register +***************************************************************************************/ +uint32_t TFT_eSPI::readcommand32(uint8_t cmd_function, uint8_t index) +{ + uint32_t reg; + + reg = ((uint32_t)readcommand8(cmd_function, index + 0) << 24); + reg |= ((uint32_t)readcommand8(cmd_function, index + 1) << 16); + reg |= ((uint32_t)readcommand8(cmd_function, index + 2) << 8); + reg |= ((uint32_t)readcommand8(cmd_function, index + 3) << 0); + + return reg; +} + + +/*************************************************************************************** +** Function name: read pixel (for SPI Interface II i.e. IM [3:0] = "1101") +** Description: Read 565 pixel colours from a pixel +***************************************************************************************/ +uint16_t TFT_eSPI::readPixel(int32_t x0, int32_t y0) +{ + if (_vpOoB) return 0; + + x0+= _xDatum; + y0+= _yDatum; + + // Range checking + if ((x0 < _vpX) || (y0 < _vpY) ||(x0 >= _vpW) || (y0 >= _vpH)) return 0; + +#if defined(TFT_PARALLEL_8_BIT) || defined(RP2040_PIO_INTERFACE) + + if (!inTransaction) { CS_L; } // CS_L can be multi-statement + + readAddrWindow(x0, y0, 1, 1); + + // Set masked pins D0- D7 to input + busDir(GPIO_DIR_MASK, INPUT); + + #if !defined (SSD1963_DRIVER) + // Dummy read to throw away don't care value + readByte(); + #endif + + // Fetch the 16-bit BRG pixel + //uint16_t rgb = (readByte() << 8) | readByte(); + + #if defined (ILI9341_DRIVER) || defined(ILI9341_2_DRIVER) || defined (ILI9488_DRIVER) || defined (SSD1963_DRIVER)// Read 3 bytes + + // Read window pixel 24-bit RGB values and fill in LS bits + uint16_t rgb = ((readByte() & 0xF8) << 8) | ((readByte() & 0xFC) << 3) | (readByte() >> 3); + + if (!inTransaction) { CS_H; } // CS_H can be multi-statement + + // Set masked pins D0- D7 to output + busDir(GPIO_DIR_MASK, OUTPUT); + + return rgb; + + #else // ILI9481 or ILI9486 16-bit read + + // Fetch the 16-bit BRG pixel + uint16_t bgr = (readByte() << 8) | readByte(); + + if (!inTransaction) { CS_H; } // CS_H can be multi-statement + + // Set masked pins D0- D7 to output + busDir(GPIO_DIR_MASK, OUTPUT); + + #if defined (ILI9486_DRIVER) || defined (ST7796_DRIVER) + return bgr; + #else + // Swap Red and Blue (could check MADCTL setting to see if this is needed) + return (bgr>>11) | (bgr<<11) | (bgr & 0x7E0); + #endif + + #endif + +#else // Not TFT_PARALLEL_8_BIT + + // This function can get called during anti-aliased font rendering + // so a transaction may be in progress + bool wasInTransaction = inTransaction; + if (inTransaction) { inTransaction= false; end_tft_write();} + + uint16_t color = 0; + + begin_tft_read(); // Sets CS low + + readAddrWindow(x0, y0, 1, 1); + + #ifdef TFT_SDA_READ + begin_SDA_Read(); + #endif + + // Dummy read to throw away don't care value + tft_Read_8(); + + //#if !defined (ILI9488_DRIVER) + + #if defined (ST7796_DRIVER) + // Read the 2 bytes + color = ((tft_Read_8()) << 8) | (tft_Read_8()); + #elif defined (ST7735_DRIVER) + // Read the 3 RGB bytes, colour is in LS 6 bits of the top 7 bits of each byte + // as the TFT stores colours as 18 bits + uint8_t r = tft_Read_8()<<1; + uint8_t g = tft_Read_8()<<1; + uint8_t b = tft_Read_8()<<1; + color = color565(r, g, b); + #else + // Read the 3 RGB bytes, colour is actually only in the top 6 bits of each byte + // as the TFT stores colours as 18 bits + uint8_t r = tft_Read_8(); + uint8_t g = tft_Read_8(); + uint8_t b = tft_Read_8(); + color = color565(r, g, b); + #endif + +/* + #else + + // The 6 colour bits are in MS 6 bits of each byte, but the ILI9488 needs an extra clock pulse + // so bits appear shifted right 1 bit, so mask the middle 6 bits then shift 1 place left + uint8_t r = (tft_Read_8()&0x7E)<<1; + uint8_t g = (tft_Read_8()&0x7E)<<1; + uint8_t b = (tft_Read_8()&0x7E)<<1; + color = color565(r, g, b); + + #endif +*/ + CS_H; + + #ifdef TFT_SDA_READ + end_SDA_Read(); + #endif + + end_tft_read(); + + // Reinstate the transaction if one was in progress + if(wasInTransaction) { begin_tft_write(); inTransaction = true; } + + return color; + +#endif +} + +void TFT_eSPI::setCallback(getColorCallback getCol) +{ + getColor = getCol; +} + + +/*************************************************************************************** +** Function name: read rectangle (for SPI Interface II i.e. IM [3:0] = "1101") +** Description: Read 565 pixel colours from a defined area +***************************************************************************************/ +void TFT_eSPI::readRect(int32_t x, int32_t y, int32_t w, int32_t h, uint16_t *data) +{ + PI_CLIP ; + +#if defined(TFT_PARALLEL_8_BIT) || defined(RP2040_PIO_INTERFACE) + + CS_L; + + readAddrWindow(x, y, dw, dh); + + data += dx + dy * w; + + // Set masked pins D0- D7 to input + busDir(GPIO_DIR_MASK, INPUT); + + #if defined (ILI9341_DRIVER) || defined(ILI9341_2_DRIVER) || defined (ILI9488_DRIVER) // Read 3 bytes + // Dummy read to throw away don't care value + readByte(); + + // Fetch the 24-bit RGB value + while (dh--) { + int32_t lw = dw; + uint16_t* line = data; + while (lw--) { + // Assemble the RGB 16-bit colour + uint16_t rgb = ((readByte() & 0xF8) << 8) | ((readByte() & 0xFC) << 3) | (readByte() >> 3); + + // Swapped byte order for compatibility with pushRect() + *line++ = (rgb<<8) | (rgb>>8); + } + data += w; + } + + #elif defined (SSD1963_DRIVER) + // Fetch the 18-bit BRG pixels + while (dh--) { + int32_t lw = dw; + uint16_t* line = data; + while (lw--) { + uint16_t bgr = ((readByte() & 0xF8) >> 3);; // CS_L adds a small delay + bgr |= ((readByte() & 0xFC) << 3); + bgr |= (readByte() << 8); + // Swap Red and Blue (could check MADCTL setting to see if this is needed) + uint16_t rgb = (bgr>>11) | (bgr<<11) | (bgr & 0x7E0); + // Swapped byte order for compatibility with pushRect() + *line++ = (rgb<<8) | (rgb>>8); + } + data += w; + } + + #else // ILI9481 reads as 16 bits + // Dummy read to throw away don't care value + readByte(); + + // Fetch the 16-bit BRG pixels + while (dh--) { + int32_t lw = dw; + uint16_t* line = data; + while (lw--) { + #if defined (ILI9486_DRIVER) || defined (ST7796_DRIVER) + // Read the RGB 16-bit colour + *line++ = readByte() | (readByte() << 8); + #else + // Read the BRG 16-bit colour + uint16_t bgr = (readByte() << 8) | readByte(); + // Swap Red and Blue (could check MADCTL setting to see if this is needed) + uint16_t rgb = (bgr>>11) | (bgr<<11) | (bgr & 0x7E0); + // Swapped byte order for compatibility with pushRect() + *line++ = (rgb<<8) | (rgb>>8); + #endif + } + data += w; + } + #endif + + CS_H; + + // Set masked pins D0- D7 to output + busDir(GPIO_DIR_MASK, OUTPUT); + +#else // SPI interface + + // This function can get called after a begin_tft_write + // so a transaction may be in progress + bool wasInTransaction = inTransaction; + if (inTransaction) { inTransaction= false; end_tft_write();} + + uint16_t color = 0; + + begin_tft_read(); + + readAddrWindow(x, y, dw, dh); + + data += dx + dy * w; + + #ifdef TFT_SDA_READ + begin_SDA_Read(); + #endif + + // Dummy read to throw away don't care value + tft_Read_8(); + + // Read window pixel 24-bit RGB values + while (dh--) { + int32_t lw = dw; + uint16_t* line = data; + while (lw--) { + + #if !defined (ILI9488_DRIVER) + + #if defined (ST7796_DRIVER) + // Read the 2 bytes + color = ((tft_Read_8()) << 8) | (tft_Read_8()); + #elif defined (ST7735_DRIVER) + // Read the 3 RGB bytes, colour is in LS 6 bits of the top 7 bits of each byte + // as the TFT stores colours as 18 bits + uint8_t r = tft_Read_8()<<1; + uint8_t g = tft_Read_8()<<1; + uint8_t b = tft_Read_8()<<1; + color = color565(r, g, b); + #else + // Read the 3 RGB bytes, colour is actually only in the top 6 bits of each byte + // as the TFT stores colours as 18 bits + uint8_t r = tft_Read_8(); + uint8_t g = tft_Read_8(); + uint8_t b = tft_Read_8(); + color = color565(r, g, b); + #endif + + #else + + // The 6 colour bits are in MS 6 bits of each byte but we do not include the extra clock pulse + // so we use a trick and mask the middle 6 bits of the byte, then only shift 1 place left + uint8_t r = (tft_Read_8()&0x7E)<<1; + uint8_t g = (tft_Read_8()&0x7E)<<1; + uint8_t b = (tft_Read_8()&0x7E)<<1; + color = color565(r, g, b); + #endif + + // Swapped colour byte order for compatibility with pushRect() + *line++ = color << 8 | color >> 8; + } + data += w; + } + + //CS_H; + + #ifdef TFT_SDA_READ + end_SDA_Read(); + #endif + + end_tft_read(); + + // Reinstate the transaction if one was in progress + if(wasInTransaction) { begin_tft_write(); inTransaction = true; } +#endif +} + + +/*************************************************************************************** +** Function name: push rectangle +** Description: push 565 pixel colours into a defined area +***************************************************************************************/ +void TFT_eSPI::pushRect(int32_t x, int32_t y, int32_t w, int32_t h, uint16_t *data) +{ + bool swap = _swapBytes; _swapBytes = false; + pushImage(x, y, w, h, data); + _swapBytes = swap; +} + + +/*************************************************************************************** +** Function name: pushImage +** Description: plot 16-bit colour sprite or image onto TFT +***************************************************************************************/ +void TFT_eSPI::pushImage(int32_t x, int32_t y, int32_t w, int32_t h, uint16_t *data) +{ + PI_CLIP; + + begin_tft_write(); + inTransaction = true; + + setWindow(x, y, x + dw - 1, y + dh - 1); + + data += dx + dy * w; + + // Check if whole image can be pushed + if (dw == w) pushPixels(data, dw * dh); + else { + // Push line segments to crop image + while (dh--) + { + pushPixels(data, dw); + data += w; + } + } + + inTransaction = lockTransaction; + end_tft_write(); +} + +/*************************************************************************************** +** Function name: pushImage +** Description: plot 16-bit sprite or image with 1 colour being transparent +***************************************************************************************/ +void TFT_eSPI::pushImage(int32_t x, int32_t y, int32_t w, int32_t h, uint16_t *data, uint16_t transp) +{ + PI_CLIP; + + begin_tft_write(); + inTransaction = true; + + data += dx + dy * w; + + + uint16_t lineBuf[dw]; // Use buffer to minimise setWindow call count + + // The little endian transp color must be byte swapped if the image is big endian + if (!_swapBytes) transp = transp >> 8 | transp << 8; + + while (dh--) + { + int32_t len = dw; + uint16_t* ptr = data; + int32_t px = x, sx = x; + bool move = true; + uint16_t np = 0; + + while (len--) + { + if (transp != *ptr) + { + if (move) { move = false; sx = px; } + lineBuf[np] = *ptr; + np++; + } + else + { + move = true; + if (np) + { + setWindow(sx, y, sx + np - 1, y); + pushPixels((uint16_t*)lineBuf, np); + np = 0; + } + } + px++; + ptr++; + } + if (np) { setWindow(sx, y, sx + np - 1, y); pushPixels((uint16_t*)lineBuf, np); } + + y++; + data += w; + } + + inTransaction = lockTransaction; + end_tft_write(); +} + + +/*************************************************************************************** +** Function name: pushImage - for FLASH (PROGMEM) stored images +** Description: plot 16-bit image +***************************************************************************************/ +void TFT_eSPI::pushImage(int32_t x, int32_t y, int32_t w, int32_t h, const uint16_t *data) +{ + // Requires 32-bit aligned access, so use PROGMEM 16-bit word functions + PI_CLIP; + + begin_tft_write(); + inTransaction = true; + + data += dx + dy * w; + + uint16_t buffer[dw]; + + setWindow(x, y, x + dw - 1, y + dh - 1); + + // Fill and send line buffers to TFT + for (int32_t i = 0; i < dh; i++) { + for (int32_t j = 0; j < dw; j++) { + buffer[j] = pgm_read_word(&data[i * w + j]); + } + pushPixels(buffer, dw); + } + + inTransaction = lockTransaction; + end_tft_write(); +} + +/*************************************************************************************** +** Function name: pushImage - for FLASH (PROGMEM) stored images +** Description: plot 16-bit image with 1 colour being transparent +***************************************************************************************/ +void TFT_eSPI::pushImage(int32_t x, int32_t y, int32_t w, int32_t h, const uint16_t *data, uint16_t transp) +{ + // Requires 32-bit aligned access, so use PROGMEM 16-bit word functions + PI_CLIP; + + begin_tft_write(); + inTransaction = true; + + data += dx + dy * w; + + + uint16_t lineBuf[dw]; + + // The little endian transp color must be byte swapped if the image is big endian + if (!_swapBytes) transp = transp >> 8 | transp << 8; + + while (dh--) { + int32_t len = dw; + uint16_t* ptr = (uint16_t*)data; + int32_t px = x, sx = x; + bool move = true; + + uint16_t np = 0; + + while (len--) { + uint16_t color = pgm_read_word(ptr); + if (transp != color) { + if (move) { move = false; sx = px; } + lineBuf[np] = color; + np++; + } + else { + move = true; + if (np) { + setWindow(sx, y, sx + np - 1, y); + pushPixels(lineBuf, np); + np = 0; + } + } + px++; + ptr++; + } + if (np) { setWindow(sx, y, sx + np - 1, y); pushPixels(lineBuf, np); } + + y++; + data += w; + } + + inTransaction = lockTransaction; + end_tft_write(); +} + +/*************************************************************************************** +** Function name: pushImage +** Description: plot 8-bit or 4-bit or 1 bit image or sprite using a line buffer +***************************************************************************************/ +void TFT_eSPI::pushImage(int32_t x, int32_t y, int32_t w, int32_t h, const uint8_t *data, bool bpp8, uint16_t *cmap) +{ + PI_CLIP; + + begin_tft_write(); + inTransaction = true; + bool swap = _swapBytes; + + setWindow(x, y, x + dw - 1, y + dh - 1); // Sets CS low and sent RAMWR + + // Line buffer makes plotting faster + uint16_t lineBuf[dw]; + + if (bpp8) + { + _swapBytes = false; + + uint8_t blue[] = {0, 11, 21, 31}; // blue 2 to 5-bit colour lookup table + + _lastColor = -1; // Set to illegal value + + // Used to store last shifted colour + uint8_t msbColor = 0; + uint8_t lsbColor = 0; + + data += dx + dy * w; + while (dh--) { + uint32_t len = dw; + uint8_t* ptr = (uint8_t*)data; + uint8_t* linePtr = (uint8_t*)lineBuf; + + while(len--) { + uint32_t color = pgm_read_byte(ptr++); + + // Shifts are slow so check if colour has changed first + if (color != _lastColor) { + // =====Green===== ===============Red============== + msbColor = (color & 0x1C)>>2 | (color & 0xC0)>>3 | (color & 0xE0); + // =====Green===== =======Blue====== + lsbColor = (color & 0x1C)<<3 | blue[color & 0x03]; + _lastColor = color; + } + + *linePtr++ = msbColor; + *linePtr++ = lsbColor; + } + + pushPixels(lineBuf, dw); + + data += w; + } + _swapBytes = swap; // Restore old value + } + else if (cmap != nullptr) // Must be 4bpp + { + _swapBytes = true; + + w = (w+1) & 0xFFFE; // if this is a sprite, w will already be even; this does no harm. + bool splitFirst = (dx & 0x01) != 0; // split first means we have to push a single px from the left of the sprite / image + + if (splitFirst) { + data += ((dx - 1 + dy * w) >> 1); + } + else { + data += ((dx + dy * w) >> 1); + } + + while (dh--) { + uint32_t len = dw; + uint8_t * ptr = (uint8_t*)data; + uint16_t *linePtr = lineBuf; + uint8_t colors; // two colors in one byte + uint16_t index; + + if (splitFirst) { + colors = pgm_read_byte(ptr); + index = (colors & 0x0F); + *linePtr++ = cmap[index]; + len--; + ptr++; + } + + while (len--) + { + colors = pgm_read_byte(ptr); + index = ((colors & 0xF0) >> 4) & 0x0F; + *linePtr++ = cmap[index]; + + if (len--) + { + index = colors & 0x0F; + *linePtr++ = cmap[index]; + } else { + break; // nothing to do here + } + + ptr++; + } + + pushPixels(lineBuf, dw); + data += (w >> 1); + } + _swapBytes = swap; // Restore old value + } + else // Must be 1bpp + { + _swapBytes = false; + uint8_t * ptr = (uint8_t*)data; + uint32_t ww = (w+7)>>3; // Width of source image line in bytes + for (int32_t yp = dy; yp < dy + dh; yp++) + { + uint8_t* linePtr = (uint8_t*)lineBuf; + for (int32_t xp = dx; xp < dx + dw; xp++) + { + uint16_t col = (pgm_read_byte(ptr + (xp>>3)) & (0x80 >> (xp & 0x7)) ); + if (col) {*linePtr++ = bitmap_fg>>8; *linePtr++ = (uint8_t) bitmap_fg;} + else {*linePtr++ = bitmap_bg>>8; *linePtr++ = (uint8_t) bitmap_bg;} + } + ptr += ww; + pushPixels(lineBuf, dw); + } + } + + _swapBytes = swap; // Restore old value + inTransaction = lockTransaction; + end_tft_write(); +} + + +/*************************************************************************************** +** Function name: pushImage +** Description: plot 8-bit or 4-bit or 1 bit image or sprite using a line buffer +***************************************************************************************/ +void TFT_eSPI::pushImage(int32_t x, int32_t y, int32_t w, int32_t h, uint8_t *data, bool bpp8, uint16_t *cmap) +{ + PI_CLIP; + + begin_tft_write(); + inTransaction = true; + bool swap = _swapBytes; + + setWindow(x, y, x + dw - 1, y + dh - 1); // Sets CS low and sent RAMWR + + // Line buffer makes plotting faster + uint16_t lineBuf[dw]; + + if (bpp8) + { + _swapBytes = false; + + uint8_t blue[] = {0, 11, 21, 31}; // blue 2 to 5-bit colour lookup table + + _lastColor = -1; // Set to illegal value + + // Used to store last shifted colour + uint8_t msbColor = 0; + uint8_t lsbColor = 0; + + data += dx + dy * w; + while (dh--) { + uint32_t len = dw; + uint8_t* ptr = data; + uint8_t* linePtr = (uint8_t*)lineBuf; + + while(len--) { + uint32_t color = *ptr++; + + // Shifts are slow so check if colour has changed first + if (color != _lastColor) { + // =====Green===== ===============Red============== + msbColor = (color & 0x1C)>>2 | (color & 0xC0)>>3 | (color & 0xE0); + // =====Green===== =======Blue====== + lsbColor = (color & 0x1C)<<3 | blue[color & 0x03]; + _lastColor = color; + } + + *linePtr++ = msbColor; + *linePtr++ = lsbColor; + } + + pushPixels(lineBuf, dw); + + data += w; + } + _swapBytes = swap; // Restore old value + } + else if (cmap != nullptr) // Must be 4bpp + { + _swapBytes = true; + + w = (w+1) & 0xFFFE; // if this is a sprite, w will already be even; this does no harm. + bool splitFirst = (dx & 0x01) != 0; // split first means we have to push a single px from the left of the sprite / image + + if (splitFirst) { + data += ((dx - 1 + dy * w) >> 1); + } + else { + data += ((dx + dy * w) >> 1); + } + + while (dh--) { + uint32_t len = dw; + uint8_t * ptr = data; + uint16_t *linePtr = lineBuf; + uint8_t colors; // two colors in one byte + uint16_t index; + + if (splitFirst) { + colors = *ptr; + index = (colors & 0x0F); + *linePtr++ = cmap[index]; + len--; + ptr++; + } + + while (len--) + { + colors = *ptr; + index = ((colors & 0xF0) >> 4) & 0x0F; + *linePtr++ = cmap[index]; + + if (len--) + { + index = colors & 0x0F; + *linePtr++ = cmap[index]; + } else { + break; // nothing to do here + } + + ptr++; + } + + pushPixels(lineBuf, dw); + data += (w >> 1); + } + _swapBytes = swap; // Restore old value + } + else // Must be 1bpp + { + _swapBytes = false; + + uint32_t ww = (w+7)>>3; // Width of source image line in bytes + for (int32_t yp = dy; yp < dy + dh; yp++) + { + uint8_t* linePtr = (uint8_t*)lineBuf; + for (int32_t xp = dx; xp < dx + dw; xp++) + { + uint16_t col = (data[(xp>>3)] & (0x80 >> (xp & 0x7)) ); + if (col) {*linePtr++ = bitmap_fg>>8; *linePtr++ = (uint8_t) bitmap_fg;} + else {*linePtr++ = bitmap_bg>>8; *linePtr++ = (uint8_t) bitmap_bg;} + } + data += ww; + pushPixels(lineBuf, dw); + } + } + + _swapBytes = swap; // Restore old value + inTransaction = lockTransaction; + end_tft_write(); +} + + +/*************************************************************************************** +** Function name: pushImage +** Description: plot 8 or 4 or 1 bit image or sprite with a transparent colour +***************************************************************************************/ +void TFT_eSPI::pushImage(int32_t x, int32_t y, int32_t w, int32_t h, uint8_t *data, uint8_t transp, bool bpp8, uint16_t *cmap) +{ + PI_CLIP; + + begin_tft_write(); + inTransaction = true; + bool swap = _swapBytes; + + + // Line buffer makes plotting faster + uint16_t lineBuf[dw]; + + if (bpp8) { // 8 bits per pixel + _swapBytes = false; + + data += dx + dy * w; + + uint8_t blue[] = {0, 11, 21, 31}; // blue 2 to 5-bit colour lookup table + + _lastColor = -1; // Set to illegal value + + // Used to store last shifted colour + uint8_t msbColor = 0; + uint8_t lsbColor = 0; + + while (dh--) { + int32_t len = dw; + uint8_t* ptr = data; + uint8_t* linePtr = (uint8_t*)lineBuf; + + int32_t px = x, sx = x; + bool move = true; + uint16_t np = 0; + + while (len--) { + if (transp != *ptr) { + if (move) { move = false; sx = px; } + uint8_t color = *ptr; + + // Shifts are slow so check if colour has changed first + if (color != _lastColor) { + // =====Green===== ===============Red============== + msbColor = (color & 0x1C)>>2 | (color & 0xC0)>>3 | (color & 0xE0); + // =====Green===== =======Blue====== + lsbColor = (color & 0x1C)<<3 | blue[color & 0x03]; + _lastColor = color; + } + *linePtr++ = msbColor; + *linePtr++ = lsbColor; + np++; + } + else { + move = true; + if (np) { + setWindow(sx, y, sx + np - 1, y); + pushPixels(lineBuf, np); + linePtr = (uint8_t*)lineBuf; + np = 0; + } + } + px++; + ptr++; + } + + if (np) { setWindow(sx, y, sx + np - 1, y); pushPixels(lineBuf, np); } + y++; + data += w; + } + } + else if (cmap != nullptr) // 4bpp with color map + { + _swapBytes = true; + + w = (w+1) & 0xFFFE; // here we try to recreate iwidth from dwidth. + bool splitFirst = ((dx & 0x01) != 0); + if (splitFirst) { + data += ((dx - 1 + dy * w) >> 1); + } + else { + data += ((dx + dy * w) >> 1); + } + + while (dh--) { + uint32_t len = dw; + uint8_t * ptr = data; + + int32_t px = x, sx = x; + bool move = true; + uint16_t np = 0; + + uint8_t index; // index into cmap. + + if (splitFirst) { + index = (*ptr & 0x0F); // odd = bits 3 .. 0 + if (index != transp) { + move = false; sx = px; + lineBuf[np] = cmap[index]; + np++; + } + px++; ptr++; + len--; + } + + while (len--) + { + uint8_t color = *ptr; + + // find the actual color you care about. There will be two pixels here! + // but we may only want one at the end of the row + uint16_t index = ((color & 0xF0) >> 4) & 0x0F; // high bits are the even numbers + if (index != transp) { + if (move) { + move = false; sx = px; + } + lineBuf[np] = cmap[index]; + np++; // added a pixel + } + else { + move = true; + if (np) { + setWindow(sx, y, sx + np - 1, y); + pushPixels(lineBuf, np); + np = 0; + } + } + px++; + + if (len--) + { + index = color & 0x0F; // the odd number is 3 .. 0 + if (index != transp) { + if (move) { + move = false; sx = px; + } + lineBuf[np] = cmap[index]; + np++; + } + else { + move = true; + if (np) { + setWindow(sx, y, sx + np - 1, y); + pushPixels(lineBuf, np); + np = 0; + } + } + px++; + } + else { + break; // we are done with this row. + } + ptr++; // we only increment ptr once in the loop (deliberate) + } + + if (np) { + setWindow(sx, y, sx + np - 1, y); + pushPixels(lineBuf, np); + np = 0; + } + data += (w>>1); + y++; + } + } + else { // 1 bit per pixel + _swapBytes = false; + + uint32_t ww = (w+7)>>3; // Width of source image line in bytes + uint16_t np = 0; + + for (int32_t yp = dy; yp < dy + dh; yp++) + { + int32_t px = x, sx = x; + bool move = true; + for (int32_t xp = dx; xp < dx + dw; xp++) + { + if (data[(xp>>3)] & (0x80 >> (xp & 0x7))) { + if (move) { + move = false; + sx = px; + } + np++; + } + else { + move = true; + if (np) { + setWindow(sx, y, sx + np - 1, y); + pushBlock(bitmap_fg, np); + np = 0; + } + } + px++; + } + if (np) { setWindow(sx, y, sx + np - 1, y); pushBlock(bitmap_fg, np); np = 0; } + y++; + data += ww; + } + } + _swapBytes = swap; // Restore old value + inTransaction = lockTransaction; + end_tft_write(); +} + +/*************************************************************************************** +** Function name: pushMaskedImage +** Description: Render a 16-bit colour image to TFT with a 1bpp mask +***************************************************************************************/ +// Can be used with a 16bpp sprite and a 1bpp sprite for the mask +void TFT_eSPI::pushMaskedImage(int32_t x, int32_t y, int32_t w, int32_t h, uint16_t *img, uint8_t *mask) +{ + if (_vpOoB || w < 1 || h < 1) return; + + // To simplify mask handling the window clipping is done by the pushImage function + // Each mask image line assumed to be padded to an integer number of bytes & padding bits are 0 + + begin_tft_write(); + inTransaction = true; + + uint8_t *mptr = mask; + uint8_t *eptr = mask + ((w + 7) >> 3); + uint16_t *iptr = img; + uint32_t setCount = 0; + + // For each line in the image + while (h--) { + uint32_t xp = 0; + uint32_t clearCount = 0; + uint8_t mbyte= *mptr++; + uint32_t bits = 8; + // Scan through each byte of the bitmap and determine run lengths + do { + setCount = 0; + + //Get run length for clear bits to determine x offset + while ((mbyte & 0x80) == 0x00) { + // Check if remaining bits in byte are clear (reduce shifts) + if (mbyte == 0) { + clearCount += bits; // bits not always 8 here + if (mptr >= eptr) break; // end of line + mbyte = *mptr++; + bits = 8; + continue; + } + mbyte = mbyte << 1; // 0's shifted in + clearCount ++; + if (--bits) continue;; + if (mptr >= eptr) break; + mbyte = *mptr++; + bits = 8; + } + + //Get run length for set bits to determine render width + while ((mbyte & 0x80) == 0x80) { + // Check if all bits are set (reduces shifts) + if (mbyte == 0xFF) { + setCount += bits; + if (mptr >= eptr) break; + mbyte = *mptr++; + //bits = 8; // NR, bits always 8 here unless 1's shifted in + continue; + } + mbyte = mbyte << 1; //or mbyte += mbyte + 1 to shift in 1's + setCount ++; + if (--bits) continue; + if (mptr >= eptr) break; + mbyte = *mptr++; + bits = 8; + } + + // A mask boundary or mask end has been found, so render the pixel line + if (setCount) { + xp += clearCount; + clearCount = 0; + pushImage(x + xp, y, setCount, 1, iptr + xp); // pushImage handles clipping + if (mptr >= eptr) break; + xp += setCount; + } + } while (setCount || mptr < eptr); + + y++; + iptr += w; + eptr += ((w + 7) >> 3); + } + + inTransaction = lockTransaction; + end_tft_write(); +} + + +/*************************************************************************************** +** Function name: setSwapBytes +** Description: Used by 16-bit pushImage() to swap byte order in colours +***************************************************************************************/ +void TFT_eSPI::setSwapBytes(bool swap) +{ + _swapBytes = swap; +} + + +/*************************************************************************************** +** Function name: getSwapBytes +** Description: Return the swap byte order for colours +***************************************************************************************/ +bool TFT_eSPI::getSwapBytes(void) +{ + return _swapBytes; +} + + +/*************************************************************************************** +** Function name: read rectangle (for SPI Interface II i.e. IM [3:0] = "1101") +** Description: Read RGB pixel colours from a defined area +***************************************************************************************/ +// If w and h are 1, then 1 pixel is read, *data array size must be 3 bytes per pixel +void TFT_eSPI::readRectRGB(int32_t x0, int32_t y0, int32_t w, int32_t h, uint8_t *data) +{ +#if defined(TFT_PARALLEL_8_BIT) || defined(RP2040_PIO_INTERFACE) + + uint32_t len = w * h; + uint8_t* buf565 = data + len; + + readRect(x0, y0, w, h, (uint16_t*)buf565); + + while (len--) { + uint16_t pixel565 = (*buf565++)<<8; + pixel565 |= *buf565++; + uint8_t red = (pixel565 & 0xF800) >> 8; red |= red >> 5; + uint8_t green = (pixel565 & 0x07E0) >> 3; green |= green >> 6; + uint8_t blue = (pixel565 & 0x001F) << 3; blue |= blue >> 5; + *data++ = red; + *data++ = green; + *data++ = blue; + } + +#else // Not TFT_PARALLEL_8_BIT + + begin_tft_read(); + + readAddrWindow(x0, y0, w, h); // Sets CS low + + #ifdef TFT_SDA_READ + begin_SDA_Read(); + #endif + + // Dummy read to throw away don't care value + tft_Read_8(); + + // Read window pixel 24-bit RGB values, buffer must be set in sketch to 3 * w * h + uint32_t len = w * h; + while (len--) { + + #if !defined (ILI9488_DRIVER) + + // Read the 3 RGB bytes, colour is actually only in the top 6 bits of each byte + // as the TFT stores colours as 18 bits + *data++ = tft_Read_8(); + *data++ = tft_Read_8(); + *data++ = tft_Read_8(); + + #else + + // The 6 colour bits are in MS 6 bits of each byte, but the ILI9488 needs an extra clock pulse + // so bits appear shifted right 1 bit, so mask the middle 6 bits then shift 1 place left + *data++ = (tft_Read_8()&0x7E)<<1; + *data++ = (tft_Read_8()&0x7E)<<1; + *data++ = (tft_Read_8()&0x7E)<<1; + + #endif + + } + + CS_H; + + #ifdef TFT_SDA_READ + end_SDA_Read(); + #endif + + end_tft_read(); + +#endif +} + + +/*************************************************************************************** +** Function name: drawCircle +** Description: Draw a circle outline +***************************************************************************************/ +// Optimised midpoint circle algorithm +void TFT_eSPI::drawCircle(int32_t x0, int32_t y0, int32_t r, uint32_t color) +{ + if ( r <= 0 ) return; + + //begin_tft_write(); // Sprite class can use this function, avoiding begin_tft_write() + inTransaction = true; + + int32_t f = 1 - r; + int32_t ddF_y = -2 * r; + int32_t ddF_x = 1; + int32_t xs = -1; + int32_t xe = 0; + int32_t len = 0; + + bool first = true; + do { + while (f < 0) { + ++xe; + f += (ddF_x += 2); + } + f += (ddF_y += 2); + + if (xe-xs>1) { + if (first) { + len = 2*(xe - xs)-1; + drawFastHLine(x0 - xe, y0 + r, len, color); + drawFastHLine(x0 - xe, y0 - r, len, color); + drawFastVLine(x0 + r, y0 - xe, len, color); + drawFastVLine(x0 - r, y0 - xe, len, color); + first = false; + } + else { + len = xe - xs++; + drawFastHLine(x0 - xe, y0 + r, len, color); + drawFastHLine(x0 - xe, y0 - r, len, color); + drawFastHLine(x0 + xs, y0 - r, len, color); + drawFastHLine(x0 + xs, y0 + r, len, color); + + drawFastVLine(x0 + r, y0 + xs, len, color); + drawFastVLine(x0 + r, y0 - xe, len, color); + drawFastVLine(x0 - r, y0 - xe, len, color); + drawFastVLine(x0 - r, y0 + xs, len, color); + } + } + else { + ++xs; + drawPixel(x0 - xe, y0 + r, color); + drawPixel(x0 - xe, y0 - r, color); + drawPixel(x0 + xs, y0 - r, color); + drawPixel(x0 + xs, y0 + r, color); + + drawPixel(x0 + r, y0 + xs, color); + drawPixel(x0 + r, y0 - xe, color); + drawPixel(x0 - r, y0 - xe, color); + drawPixel(x0 - r, y0 + xs, color); + } + xs = xe; + } while (xe < --r); + + inTransaction = lockTransaction; + end_tft_write(); // Does nothing if Sprite class uses this function +} + + +/*************************************************************************************** +** Function name: drawCircleHelper +** Description: Support function for drawRoundRect() +***************************************************************************************/ +void TFT_eSPI::drawCircleHelper( int32_t x0, int32_t y0, int32_t rr, uint8_t cornername, uint32_t color) +{ + if (rr <= 0) return; + int32_t f = 1 - rr; + int32_t ddF_x = 1; + int32_t ddF_y = -2 * rr; + int32_t xe = 0; + int32_t xs = 0; + int32_t len = 0; + + //begin_tft_write(); // Sprite class can use this function, avoiding begin_tft_write() + inTransaction = true; + + do + { + while (f < 0) { + ++xe; + f += (ddF_x += 2); + } + f += (ddF_y += 2); + + if (xe-xs==1) { + if (cornername & 0x1) { // left top + drawPixel(x0 - xe, y0 - rr, color); + drawPixel(x0 - rr, y0 - xe, color); + } + if (cornername & 0x2) { // right top + drawPixel(x0 + rr , y0 - xe, color); + drawPixel(x0 + xs + 1, y0 - rr, color); + } + if (cornername & 0x4) { // right bottom + drawPixel(x0 + xs + 1, y0 + rr , color); + drawPixel(x0 + rr, y0 + xs + 1, color); + } + if (cornername & 0x8) { // left bottom + drawPixel(x0 - rr, y0 + xs + 1, color); + drawPixel(x0 - xe, y0 + rr , color); + } + } + else { + len = xe - xs++; + if (cornername & 0x1) { // left top + drawFastHLine(x0 - xe, y0 - rr, len, color); + drawFastVLine(x0 - rr, y0 - xe, len, color); + } + if (cornername & 0x2) { // right top + drawFastVLine(x0 + rr, y0 - xe, len, color); + drawFastHLine(x0 + xs, y0 - rr, len, color); + } + if (cornername & 0x4) { // right bottom + drawFastHLine(x0 + xs, y0 + rr, len, color); + drawFastVLine(x0 + rr, y0 + xs, len, color); + } + if (cornername & 0x8) { // left bottom + drawFastVLine(x0 - rr, y0 + xs, len, color); + drawFastHLine(x0 - xe, y0 + rr, len, color); + } + } + xs = xe; + } while (xe < rr--); + + inTransaction = lockTransaction; + end_tft_write(); // Does nothing if Sprite class uses this function +} + +/*************************************************************************************** +** Function name: fillCircle +** Description: draw a filled circle +***************************************************************************************/ +// Optimised midpoint circle algorithm, changed to horizontal lines (faster in sprites) +// Improved algorithm avoids repetition of lines +void TFT_eSPI::fillCircle(int32_t x0, int32_t y0, int32_t r, uint32_t color) +{ + int32_t x = 0; + int32_t dx = 1; + int32_t dy = r+r; + int32_t p = -(r>>1); + + //begin_tft_write(); // Sprite class can use this function, avoiding begin_tft_write() + inTransaction = true; + + drawFastHLine(x0 - r, y0, dy+1, color); + + while(x=0) { + drawFastHLine(x0 - x, y0 + r, dx, color); + drawFastHLine(x0 - x, y0 - r, dx, color); + dy-=2; + p-=dy; + r--; + } + + dx+=2; + p+=dx; + x++; + + drawFastHLine(x0 - r, y0 + x, dy+1, color); + drawFastHLine(x0 - r, y0 - x, dy+1, color); + + } + + inTransaction = lockTransaction; + end_tft_write(); // Does nothing if Sprite class uses this function +} + +/*************************************************************************************** +** Function name: fillCircleHelper +** Description: Support function for fillRoundRect() +***************************************************************************************/ +// Support drawing roundrects, changed to horizontal lines (faster in sprites) +void TFT_eSPI::fillCircleHelper(int32_t x0, int32_t y0, int32_t r, uint8_t cornername, int32_t delta, uint32_t color) +{ + int32_t f = 1 - r; + int32_t ddF_x = 1; + int32_t ddF_y = -r - r; + int32_t y = 0; + + delta++; + + while (y < r) { + if (f >= 0) { + if (cornername & 0x1) drawFastHLine(x0 - y, y0 + r, y + y + delta, color); + if (cornername & 0x2) drawFastHLine(x0 - y, y0 - r, y + y + delta, color); + r--; + ddF_y += 2; + f += ddF_y; + } + + y++; + ddF_x += 2; + f += ddF_x; + + if (cornername & 0x1) drawFastHLine(x0 - r, y0 + y, r + r + delta, color); + if (cornername & 0x2) drawFastHLine(x0 - r, y0 - y, r + r + delta, color); + } +} + + +/*************************************************************************************** +** Function name: drawEllipse +** Description: Draw a ellipse outline +***************************************************************************************/ +void TFT_eSPI::drawEllipse(int16_t x0, int16_t y0, int32_t rx, int32_t ry, uint16_t color) +{ + if (rx<2) return; + if (ry<2) return; + int32_t x, y; + int32_t rx2 = rx * rx; + int32_t ry2 = ry * ry; + int32_t fx2 = 4 * rx2; + int32_t fy2 = 4 * ry2; + int32_t s; + + //begin_tft_write(); // Sprite class can use this function, avoiding begin_tft_write() + inTransaction = true; + + for (x = 0, y = ry, s = 2*ry2+rx2*(1-2*ry); ry2*x <= rx2*y; x++) { + // These are ordered to minimise coordinate changes in x or y + // drawPixel can then send fewer bounding box commands + drawPixel(x0 + x, y0 + y, color); + drawPixel(x0 - x, y0 + y, color); + drawPixel(x0 - x, y0 - y, color); + drawPixel(x0 + x, y0 - y, color); + if (s >= 0) { + s += fx2 * (1 - y); + y--; + } + s += ry2 * ((4 * x) + 6); + } + + for (x = rx, y = 0, s = 2*rx2+ry2*(1-2*rx); rx2*y <= ry2*x; y++) { + // These are ordered to minimise coordinate changes in x or y + // drawPixel can then send fewer bounding box commands + drawPixel(x0 + x, y0 + y, color); + drawPixel(x0 - x, y0 + y, color); + drawPixel(x0 - x, y0 - y, color); + drawPixel(x0 + x, y0 - y, color); + if (s >= 0) + { + s += fy2 * (1 - x); + x--; + } + s += rx2 * ((4 * y) + 6); + } + + inTransaction = lockTransaction; + end_tft_write(); // Does nothing if Sprite class uses this function +} + + +/*************************************************************************************** +** Function name: fillEllipse +** Description: draw a filled ellipse +***************************************************************************************/ +void TFT_eSPI::fillEllipse(int16_t x0, int16_t y0, int32_t rx, int32_t ry, uint16_t color) +{ + if (rx<2) return; + if (ry<2) return; + int32_t x, y; + int32_t rx2 = rx * rx; + int32_t ry2 = ry * ry; + int32_t fx2 = 4 * rx2; + int32_t fy2 = 4 * ry2; + int32_t s; + + //begin_tft_write(); // Sprite class can use this function, avoiding begin_tft_write() + inTransaction = true; + + for (x = 0, y = ry, s = 2*ry2+rx2*(1-2*ry); ry2*x <= rx2*y; x++) { + drawFastHLine(x0 - x, y0 - y, x + x + 1, color); + drawFastHLine(x0 - x, y0 + y, x + x + 1, color); + + if (s >= 0) { + s += fx2 * (1 - y); + y--; + } + s += ry2 * ((4 * x) + 6); + } + + for (x = rx, y = 0, s = 2*rx2+ry2*(1-2*rx); rx2*y <= ry2*x; y++) { + drawFastHLine(x0 - x, y0 - y, x + x + 1, color); + drawFastHLine(x0 - x, y0 + y, x + x + 1, color); + + if (s >= 0) { + s += fy2 * (1 - x); + x--; + } + s += rx2 * ((4 * y) + 6); + } + + inTransaction = lockTransaction; + end_tft_write(); // Does nothing if Sprite class uses this function +} + + +/*************************************************************************************** +** Function name: fillScreen +** Description: Clear the screen to defined colour +***************************************************************************************/ +void TFT_eSPI::fillScreen(uint32_t color) +{ + fillRect(0, 0, _width, _height, color); +} + + +/*************************************************************************************** +** Function name: drawRect +** Description: Draw a rectangle outline +***************************************************************************************/ +// Draw a rectangle +void TFT_eSPI::drawRect(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color) +{ + //begin_tft_write(); // Sprite class can use this function, avoiding begin_tft_write() + inTransaction = true; + + drawFastHLine(x, y, w, color); + drawFastHLine(x, y + h - 1, w, color); + // Avoid drawing corner pixels twice + drawFastVLine(x, y+1, h-2, color); + drawFastVLine(x + w - 1, y+1, h-2, color); + + inTransaction = lockTransaction; + end_tft_write(); // Does nothing if Sprite class uses this function +} + + +/*************************************************************************************** +** Function name: drawRoundRect +** Description: Draw a rounded corner rectangle outline +***************************************************************************************/ +// Draw a rounded rectangle +void TFT_eSPI::drawRoundRect(int32_t x, int32_t y, int32_t w, int32_t h, int32_t r, uint32_t color) +{ + //begin_tft_write(); // Sprite class can use this function, avoiding begin_tft_write() + inTransaction = true; + + // smarter version + drawFastHLine(x + r , y , w - r - r, color); // Top + drawFastHLine(x + r , y + h - 1, w - r - r, color); // Bottom + drawFastVLine(x , y + r , h - r - r, color); // Left + drawFastVLine(x + w - 1, y + r , h - r - 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); + + inTransaction = lockTransaction; + end_tft_write(); // Does nothing if Sprite class uses this function +} + + +/*************************************************************************************** +** Function name: fillRoundRect +** Description: Draw a rounded corner filled rectangle +***************************************************************************************/ +// Fill a rounded rectangle, changed to horizontal lines (faster in sprites) +void TFT_eSPI::fillRoundRect(int32_t x, int32_t y, int32_t w, int32_t h, int32_t r, uint32_t color) +{ + //begin_tft_write(); // Sprite class can use this function, avoiding begin_tft_write() + inTransaction = true; + + // smarter version + fillRect(x, y + r, w, h - r - r, color); + + // draw four corners + fillCircleHelper(x + r, y + h - r - 1, r, 1, w - r - r - 1, color); + fillCircleHelper(x + r , y + r, r, 2, w - r - r - 1, color); + + inTransaction = lockTransaction; + end_tft_write(); // Does nothing if Sprite class uses this function +} + + +/*************************************************************************************** +** Function name: drawTriangle +** Description: Draw a triangle outline using 3 arbitrary points +***************************************************************************************/ +// Draw a triangle +void TFT_eSPI::drawTriangle(int32_t x0, int32_t y0, int32_t x1, int32_t y1, int32_t x2, int32_t y2, uint32_t color) +{ + //begin_tft_write(); // Sprite class can use this function, avoiding begin_tft_write() + inTransaction = true; + + drawLine(x0, y0, x1, y1, color); + drawLine(x1, y1, x2, y2, color); + drawLine(x2, y2, x0, y0, color); + + inTransaction = lockTransaction; + end_tft_write(); // Does nothing if Sprite class uses this function +} + + +/*************************************************************************************** +** Function name: fillTriangle +** Description: Draw a filled triangle using 3 arbitrary points +***************************************************************************************/ +// Fill a triangle - original Adafruit function works well and code footprint is small +void TFT_eSPI::fillTriangle ( int32_t x0, int32_t y0, int32_t x1, int32_t y1, int32_t x2, int32_t y2, uint32_t color) +{ + int32_t a, b, y, last; + + // Sort coordinates by Y order (y2 >= y1 >= y0) + if (y0 > y1) { + transpose(y0, y1); transpose(x0, x1); + } + if (y1 > y2) { + transpose(y2, y1); transpose(x2, x1); + } + if (y0 > y1) { + transpose(y0, y1); transpose(x0, x1); + } + + 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; + drawFastHLine(a, y0, b - a + 1, color); + return; + } + + //begin_tft_write(); // Sprite class can use this function, avoiding begin_tft_write() + inTransaction = true; + + int32_t + dx01 = x1 - x0, + dy01 = y1 - y0, + dx02 = x2 - x0, + dy02 = y2 - y0, + dx12 = x2 - x1, + dy12 = y2 - y1, + 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; + + if (a > b) transpose(a, b); + drawFastHLine(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; + + if (a > b) transpose(a, b); + drawFastHLine(a, y, b - a + 1, color); + } + + inTransaction = lockTransaction; + end_tft_write(); // Does nothing if Sprite class uses this function +} + + +/*************************************************************************************** +** Function name: drawBitmap +** Description: Draw an image stored in an array on the TFT +***************************************************************************************/ +void TFT_eSPI::drawBitmap(int16_t x, int16_t y, const uint8_t *bitmap, int16_t w, int16_t h, uint16_t color) +{ + //begin_tft_write(); // Sprite class can use this function, avoiding begin_tft_write() + inTransaction = true; + + int32_t i, j, byteWidth = (w + 7) / 8; + + for (j = 0; j < h; j++) { + for (i = 0; i < w; i++ ) { + if (pgm_read_byte(bitmap + j * byteWidth + i / 8) & (128 >> (i & 7))) { + drawPixel(x + i, y + j, color); + } + } + } + + inTransaction = lockTransaction; + end_tft_write(); // Does nothing if Sprite class uses this function +} + + +/*************************************************************************************** +** Function name: drawBitmap +** Description: Draw an image stored in an array on the TFT +***************************************************************************************/ +void TFT_eSPI::drawBitmap(int16_t x, int16_t y, const uint8_t *bitmap, int16_t w, int16_t h, uint16_t fgcolor, uint16_t bgcolor) +{ + //begin_tft_write(); // Sprite class can use this function, avoiding begin_tft_write() + inTransaction = true; + + int32_t i, j, byteWidth = (w + 7) / 8; + + for (j = 0; j < h; j++) { + for (i = 0; i < w; i++ ) { + if (pgm_read_byte(bitmap + j * byteWidth + i / 8) & (128 >> (i & 7))) + drawPixel(x + i, y + j, fgcolor); + else drawPixel(x + i, y + j, bgcolor); + } + } + + inTransaction = lockTransaction; + end_tft_write(); // Does nothing if Sprite class uses this function +} + +/*************************************************************************************** +** Function name: drawXBitmap +** Description: Draw an image stored in an XBM array onto the TFT +***************************************************************************************/ +void TFT_eSPI::drawXBitmap(int16_t x, int16_t y, const uint8_t *bitmap, int16_t w, int16_t h, uint16_t color) +{ + //begin_tft_write(); // Sprite class can use this function, avoiding begin_tft_write() + inTransaction = true; + + int32_t i, j, byteWidth = (w + 7) / 8; + + for (j = 0; j < h; j++) { + for (i = 0; i < w; i++ ) { + if (pgm_read_byte(bitmap + j * byteWidth + i / 8) & (1 << (i & 7))) { + drawPixel(x + i, y + j, color); + } + } + } + + inTransaction = lockTransaction; + end_tft_write(); // Does nothing if Sprite class uses this function +} + + +/*************************************************************************************** +** Function name: drawXBitmap +** Description: Draw an XBM image with foreground and background colors +***************************************************************************************/ +void TFT_eSPI::drawXBitmap(int16_t x, int16_t y, const uint8_t *bitmap, int16_t w, int16_t h, uint16_t color, uint16_t bgcolor) +{ + //begin_tft_write(); // Sprite class can use this function, avoiding begin_tft_write() + inTransaction = true; + + int32_t i, j, byteWidth = (w + 7) / 8; + + for (j = 0; j < h; j++) { + for (i = 0; i < w; i++ ) { + if (pgm_read_byte(bitmap + j * byteWidth + i / 8) & (1 << (i & 7))) + drawPixel(x + i, y + j, color); + else drawPixel(x + i, y + j, bgcolor); + } + } + + inTransaction = lockTransaction; + end_tft_write(); // Does nothing if Sprite class uses this function +} + + +/*************************************************************************************** +** Function name: setCursor +** Description: Set the text cursor x,y position +***************************************************************************************/ +void TFT_eSPI::setCursor(int16_t x, int16_t y) +{ + cursor_x = x; + cursor_y = y; +} + + +/*************************************************************************************** +** Function name: setCursor +** Description: Set the text cursor x,y position and font +***************************************************************************************/ +void TFT_eSPI::setCursor(int16_t x, int16_t y, uint8_t font) +{ + setTextFont(font); + cursor_x = x; + cursor_y = y; +} + + +/*************************************************************************************** +** Function name: getCursorX +** Description: Get the text cursor x position +***************************************************************************************/ +int16_t TFT_eSPI::getCursorX(void) +{ + return cursor_x; +} + +/*************************************************************************************** +** Function name: getCursorY +** Description: Get the text cursor y position +***************************************************************************************/ +int16_t TFT_eSPI::getCursorY(void) +{ + return cursor_y; +} + + +/*************************************************************************************** +** Function name: setTextSize +** Description: Set the text size multiplier +***************************************************************************************/ +void TFT_eSPI::setTextSize(uint8_t s) +{ + if (s>7) s = 7; // Limit the maximum size multiplier so byte variables can be used for rendering + textsize = (s > 0) ? s : 1; // Don't allow font size 0 +} + + +/*************************************************************************************** +** Function name: setTextColor +** Description: Set the font foreground colour (background is transparent) +***************************************************************************************/ +void TFT_eSPI::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; +} + + +/*************************************************************************************** +** Function name: setTextColor +** Description: Set the font foreground and background colour +***************************************************************************************/ +// Smooth fonts use the background colour for anti-aliasing and by default the +// background is not filled. If bgfill = true, then a smooth font background fill will +// be used. +void TFT_eSPI::setTextColor(uint16_t c, uint16_t b, bool bgfill) +{ + textcolor = c; + textbgcolor = b; + _fillbg = bgfill; +} + + +/*************************************************************************************** +** Function name: setPivot +** Description: Set the pivot point on the TFT +*************************************************************************************x*/ +void TFT_eSPI::setPivot(int16_t x, int16_t y) +{ + _xPivot = x; + _yPivot = y; +} + + +/*************************************************************************************** +** Function name: getPivotX +** Description: Get the x pivot position +***************************************************************************************/ +int16_t TFT_eSPI::getPivotX(void) +{ + return _xPivot; +} + + +/*************************************************************************************** +** Function name: getPivotY +** Description: Get the y pivot position +***************************************************************************************/ +int16_t TFT_eSPI::getPivotY(void) +{ + return _yPivot; +} + + +/*************************************************************************************** +** Function name: setBitmapColor +** Description: Set the foreground foreground and background colour +***************************************************************************************/ +void TFT_eSPI::setBitmapColor(uint16_t c, uint16_t b) +{ + if (c == b) b = ~c; + bitmap_fg = c; + bitmap_bg = b; +} + + +/*************************************************************************************** +** Function name: setTextWrap +** Description: Define if text should wrap at end of line +***************************************************************************************/ +void TFT_eSPI::setTextWrap(bool wrapX, bool wrapY) +{ + textwrapX = wrapX; + textwrapY = wrapY; +} + + +/*************************************************************************************** +** Function name: setTextDatum +** Description: Set the text position reference datum +***************************************************************************************/ +void TFT_eSPI::setTextDatum(uint8_t d) +{ + textdatum = d; +} + + +/*************************************************************************************** +** Function name: setTextPadding +** Description: Define padding width (aids erasing old text and numbers) +***************************************************************************************/ +void TFT_eSPI::setTextPadding(uint16_t x_width) +{ + padX = x_width; +} + +/*************************************************************************************** +** Function name: setTextPadding +** Description: Define padding width (aids erasing old text and numbers) +***************************************************************************************/ +uint16_t TFT_eSPI::getTextPadding(void) +{ + return padX; +} + +/*************************************************************************************** +** Function name: getTextDatum +** Description: Return the text datum value (as used by setTextDatum()) +***************************************************************************************/ +uint8_t TFT_eSPI::getTextDatum(void) +{ + return textdatum; +} + + +/*************************************************************************************** +** Function name: width +** Description: Return the pixel width of display (per current rotation) +***************************************************************************************/ +// Return the size of the display (per current rotation) +int16_t TFT_eSPI::width(void) +{ + if (_vpDatum) return _xWidth; + return _width; +} + + +/*************************************************************************************** +** Function name: height +** Description: Return the pixel height of display (per current rotation) +***************************************************************************************/ +int16_t TFT_eSPI::height(void) +{ + if (_vpDatum) return _yHeight; + return _height; +} + + +/*************************************************************************************** +** Function name: textWidth +** Description: Return the width in pixels of a string in a given font +***************************************************************************************/ +int16_t TFT_eSPI::textWidth(const String& string) +{ + int16_t len = string.length() + 2; + char buffer[len]; + string.toCharArray(buffer, len); + return textWidth(buffer, textfont); +} + +int16_t TFT_eSPI::textWidth(const String& string, uint8_t font) +{ + int16_t len = string.length() + 2; + char buffer[len]; + string.toCharArray(buffer, len); + return textWidth(buffer, font); +} + +int16_t TFT_eSPI::textWidth(const char *string) +{ + return textWidth(string, textfont); +} + +int16_t TFT_eSPI::textWidth(const char *string, uint8_t font) +{ + int32_t str_width = 0; + uint16_t uniCode = 0; + +#ifdef SMOOTH_FONT + if(fontLoaded) { + while (*string) { + uniCode = decodeUTF8(*string++); + if (uniCode) { + if (uniCode == 0x20) str_width += gFont.spaceWidth; + else { + uint16_t gNum = 0; + bool found = getUnicodeIndex(uniCode, &gNum); + if (found) { + if(str_width == 0 && gdX[gNum] < 0) str_width -= gdX[gNum]; + if (*string || isDigits) str_width += gxAdvance[gNum]; + else str_width += (gdX[gNum] + gWidth[gNum]); + } + else str_width += gFont.spaceWidth + 1; + } + } + } + isDigits = false; + return str_width; + } +#endif + + if (font>1 && font<9) { + char *widthtable = (char *)pgm_read_dword( &(fontdata[font].widthtbl ) ) - 32; //subtract the 32 outside the loop + + while (*string) { + uniCode = *(string++); + if (uniCode > 31 && uniCode < 128) + str_width += pgm_read_byte( widthtable + uniCode); // Normally we need to subtract 32 from uniCode + else str_width += pgm_read_byte( widthtable + 32); // Set illegal character = space width + } + + } + else { + +#ifdef LOAD_GFXFF + if(gfxFont) { // New font + while (*string) { + uniCode = decodeUTF8(*string++); + if ((uniCode >= pgm_read_word(&gfxFont->first)) && (uniCode <= pgm_read_word(&gfxFont->last ))) { + uniCode -= pgm_read_word(&gfxFont->first); + GFXglyph *glyph = &(((GFXglyph *)pgm_read_dword(&gfxFont->glyph))[uniCode]); + // If this is not the last character or is a digit then use xAdvance + if (*string || isDigits) str_width += pgm_read_byte(&glyph->xAdvance); + // Else use the offset plus width since this can be bigger than xAdvance + else str_width += ((int8_t)pgm_read_byte(&glyph->xOffset) + pgm_read_byte(&glyph->width)); + } + } + } + else +#endif + { +#ifdef LOAD_GLCD + while (*string++) str_width += 6; +#endif + } + } + isDigits = false; + return str_width * textsize; +} + + +/*************************************************************************************** +** Function name: fontsLoaded +** Description: return an encoded 16-bit value showing the fonts loaded +***************************************************************************************/ +// Returns a value showing which fonts are loaded (bit N set = Font N loaded) +uint16_t TFT_eSPI::fontsLoaded(void) +{ + return fontsloaded; +} + + +/*************************************************************************************** +** Function name: fontHeight +** Description: return the height of a font (yAdvance for free fonts) +***************************************************************************************/ +int16_t TFT_eSPI::fontHeight(uint8_t font) +{ + if (font > 8) return 0; + +#ifdef SMOOTH_FONT + if(fontLoaded) return gFont.yAdvance; +#endif + +#ifdef LOAD_GFXFF + if (font==1) { + if(gfxFont) { // New font + return pgm_read_byte(&gfxFont->yAdvance) * textsize; + } + } +#endif + return pgm_read_byte( &fontdata[font].height ) * textsize; +} + +int16_t TFT_eSPI::fontHeight(void) +{ + return fontHeight(textfont); +} + +/*************************************************************************************** +** Function name: drawChar +** Description: draw a single character in the GLCD or GFXFF font +***************************************************************************************/ +void TFT_eSPI::drawChar(int32_t x, int32_t y, uint16_t c, uint32_t color, uint32_t bg, uint8_t size) +{ + if (_vpOoB) return; + +#ifdef LOAD_GLCD +//>>>>>>>>>>>>>>>>>> + #ifdef LOAD_GFXFF + if(!gfxFont) { // 'Classic' built-in GLCD font + #endif +//>>>>>>>>>>>>>>>>>> + + int32_t xd = x + _xDatum; + int32_t yd = y + _yDatum; + + if ((xd >= _vpW) || // Clip right + ( yd >= _vpH) || // Clip bottom + ((xd + 6 * size - 1) < _vpX) || // Clip left + ((yd + 8 * size - 1) < _vpY)) // Clip top + return; + + if (c > 255) return; + if (!_cp437 && c > 175) c++; + + bool fillbg = (bg != color); + bool clip = xd < _vpX || xd + 6 * textsize >= _vpW || yd < _vpY || yd + 8 * textsize >= _vpH; + + if ((size==1) && fillbg && !clip) { + uint8_t column[6]; + uint8_t mask = 0x1; + begin_tft_write(); + + setWindow(xd, yd, xd+5, yd+7); + + for (int8_t i = 0; i < 5; i++ ) column[i] = pgm_read_byte(&font[0] + (c * 5) + i); + column[5] = 0; + + for (int8_t j = 0; j < 8; j++) { + for (int8_t k = 0; k < 5; k++ ) { + if (column[k] & mask) {tft_Write_16(color);} + else {tft_Write_16(bg);} + } + mask <<= 1; + tft_Write_16(bg); + } + + end_tft_write(); + } + else { + //begin_tft_write(); // Sprite class can use this function, avoiding begin_tft_write() + inTransaction = true; + + for (int8_t i = 0; i < 6; i++ ) { + uint8_t line; + if (i == 5) + line = 0x0; + else + line = pgm_read_byte(&font[0] + (c * 5) + i); + + if (size == 1 && !fillbg) { // default size + for (int8_t j = 0; j < 8; j++) { + if (line & 0x1) drawPixel(x + i, y + j, color); + line >>= 1; + } + } + else { // big size or clipped + for (int8_t j = 0; j < 8; j++) { + if (line & 0x1) fillRect(x + (i * size), y + (j * size), size, size, color); + else if (fillbg) fillRect(x + i * size, y + j * size, size, size, bg); + line >>= 1; + } + } + } + inTransaction = lockTransaction; + end_tft_write(); // Does nothing if Sprite class uses this function + } + +//>>>>>>>>>>>>>>>>>>>>>>>>>>> + #ifdef LOAD_GFXFF + } else { // Custom font + #endif +//>>>>>>>>>>>>>>>>>>>>>>>>>>> +#endif // LOAD_GLCD + +#ifdef LOAD_GFXFF + // Filter out bad characters not present in font + if ((c >= pgm_read_word(&gfxFont->first)) && (c <= pgm_read_word(&gfxFont->last ))) { + //begin_tft_write(); // Sprite class can use this function, avoiding begin_tft_write() + inTransaction = true; +//>>>>>>>>>>>>>>>>>>>>>>>>>>> + + c -= pgm_read_word(&gfxFont->first); + GFXglyph *glyph = &(((GFXglyph *)pgm_read_dword(&gfxFont->glyph))[c]); + uint8_t *bitmap = (uint8_t *)pgm_read_dword(&gfxFont->bitmap); + + uint32_t bo = pgm_read_word(&glyph->bitmapOffset); + uint8_t w = pgm_read_byte(&glyph->width), + h = pgm_read_byte(&glyph->height); + //xa = pgm_read_byte(&glyph->xAdvance); + 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; + } + + // GFXFF rendering speed up + uint16_t hpc = 0; // Horizontal foreground pixel count + for(yy=0; yy>= 1; + } + // Draw pixels for this line as we are about to increment yy + if (hpc) { + if(size == 1) drawFastHLine(x+xo+xx-hpc, y+yo+yy, hpc, color); + else fillRect(x+(xo16+xx-hpc)*size, y+(yo16+yy)*size, size*hpc, size, color); + hpc=0; + } + } + + inTransaction = lockTransaction; + end_tft_write(); // Does nothing if Sprite class uses this function + } +#endif + +#ifdef LOAD_GLCD + #ifdef LOAD_GFXFF + } // End classic vs custom font + #endif +#else + #ifndef LOAD_GFXFF + // Avoid warnings if fonts are disabled + x = x; + y = y; + color = color; + bg = bg; + size = size; + #endif +#endif + +} + + +/*************************************************************************************** +** Function name: setAddrWindow +** Description: define an area to receive a stream of pixels +***************************************************************************************/ +// Chip select is high at the end of this function +void TFT_eSPI::setAddrWindow(int32_t x0, int32_t y0, int32_t w, int32_t h) +{ + begin_tft_write(); + + setWindow(x0, y0, x0 + w - 1, y0 + h - 1); + + end_tft_write(); +} + + +/*************************************************************************************** +** Function name: setWindow +** Description: define an area to receive a stream of pixels +***************************************************************************************/ +// Chip select stays low, call begin_tft_write first. Use setAddrWindow() from sketches +void TFT_eSPI::setWindow(int32_t x0, int32_t y0, int32_t x1, int32_t y1) +{ + //begin_tft_write(); // Must be called before setWindow + addr_row = 0xFFFF; + addr_col = 0xFFFF; + +#if defined (ILI9225_DRIVER) + if (rotation & 0x01) { transpose(x0, y0); transpose(x1, y1); } + SPI_BUSY_CHECK; + DC_C; tft_Write_8(TFT_CASET1); + DC_D; tft_Write_16(x0); + DC_C; tft_Write_8(TFT_CASET2); + DC_D; tft_Write_16(x1); + + DC_C; tft_Write_8(TFT_PASET1); + DC_D; tft_Write_16(y0); + DC_C; tft_Write_8(TFT_PASET2); + DC_D; tft_Write_16(y1); + + DC_C; tft_Write_8(TFT_RAM_ADDR1); + DC_D; tft_Write_16(x0); + DC_C; tft_Write_8(TFT_RAM_ADDR2); + DC_D; tft_Write_16(y0); + + // write to RAM + DC_C; tft_Write_8(TFT_RAMWR); + DC_D; + // Temporary solution is to include the RP2040 code here + #if (defined(ARDUINO_ARCH_RP2040) || defined (ARDUINO_ARCH_MBED)) && !defined(RP2040_PIO_INTERFACE) && !defined(ARDUINO_ARCH_NRF52840) + // For ILI9225 and RP2040 the slower Arduino SPI transfer calls were used, so need to swap back to 16-bit mode + while (spi_get_hw(SPI_X)->sr & SPI_SSPSR_BSY_BITS) {}; + hw_write_masked(&spi_get_hw(SPI_X)->cr0, (16 - 1) << SPI_SSPCR0_DSS_LSB, SPI_SSPCR0_DSS_BITS); + #endif +#elif defined (SSD1351_DRIVER) + if (rotation & 1) { + transpose(x0, y0); + transpose(x1, y1); + } + SPI_BUSY_CHECK; + DC_C; tft_Write_8(TFT_CASET); + DC_D; tft_Write_16(x1 | (x0 << 8)); + DC_C; tft_Write_8(TFT_PASET); + DC_D; tft_Write_16(y1 | (y0 << 8)); + DC_C; tft_Write_8(TFT_RAMWR); + DC_D; +#else + #if defined (SSD1963_DRIVER) + if ((rotation & 0x1) == 0) { transpose(x0, y0); transpose(x1, y1); } + #endif + + #ifdef CGRAM_OFFSET + x0+=colstart; + x1+=colstart; + y0+=rowstart; + y1+=rowstart; + #endif + + // Temporary solution is to include the RP2040 optimised code here + #if (defined(ARDUINO_ARCH_RP2040) || defined (ARDUINO_ARCH_MBED)) && !defined(ARDUINO_ARCH_NRF52840) + #if !defined(RP2040_PIO_INTERFACE) + // Use hardware SPI port, this code does not swap from 8 to 16-bit + // to avoid the spi_set_format() call overhead + while (spi_get_hw(SPI_X)->sr & SPI_SSPSR_BSY_BITS) {}; + DC_C; + #if !defined (SPI_18BIT_DRIVER) + #if defined (RPI_DISPLAY_TYPE) // RPi TFT type always needs 16-bit transfers + hw_write_masked(&spi_get_hw(SPI_X)->cr0, (16 - 1) << SPI_SSPCR0_DSS_LSB, SPI_SSPCR0_DSS_BITS); + #else + hw_write_masked(&spi_get_hw(SPI_X)->cr0, (8 - 1) << SPI_SSPCR0_DSS_LSB, SPI_SSPCR0_DSS_BITS); + #endif + #endif + spi_get_hw(SPI_X)->dr = (uint32_t)TFT_CASET; + + while (spi_get_hw(SPI_X)->sr & SPI_SSPSR_BSY_BITS) {}; + DC_D; + spi_get_hw(SPI_X)->dr = (uint32_t)x0>>8; + spi_get_hw(SPI_X)->dr = (uint32_t)x0; + spi_get_hw(SPI_X)->dr = (uint32_t)x1>>8; + spi_get_hw(SPI_X)->dr = (uint32_t)x1; + + while (spi_get_hw(SPI_X)->sr & SPI_SSPSR_BSY_BITS) {}; + DC_C; + spi_get_hw(SPI_X)->dr = (uint32_t)TFT_PASET; + + while (spi_get_hw(SPI_X)->sr & SPI_SSPSR_BSY_BITS) {}; + DC_D; + spi_get_hw(SPI_X)->dr = (uint32_t)y0>>8; + spi_get_hw(SPI_X)->dr = (uint32_t)y0; + spi_get_hw(SPI_X)->dr = (uint32_t)y1>>8; + spi_get_hw(SPI_X)->dr = (uint32_t)y1; + + while (spi_get_hw(SPI_X)->sr & SPI_SSPSR_BSY_BITS) {}; + DC_C; + spi_get_hw(SPI_X)->dr = (uint32_t)TFT_RAMWR; + + while (spi_get_hw(SPI_X)->sr & SPI_SSPSR_BSY_BITS) {}; + #if !defined (SPI_18BIT_DRIVER) + hw_write_masked(&spi_get_hw(SPI_X)->cr0, (16 - 1) << SPI_SSPCR0_DSS_LSB, SPI_SSPCR0_DSS_BITS); + #endif + DC_D; + #elif defined (RM68120_DRIVER) + DC_C; tft_Write_16(TFT_CASET+0); DC_D; tft_Write_16(x0 >> 8); + DC_C; tft_Write_16(TFT_CASET+1); DC_D; tft_Write_16(x0 & 0xFF); + DC_C; tft_Write_16(TFT_CASET+2); DC_D; tft_Write_16(x1 >> 8); + DC_C; tft_Write_16(TFT_CASET+3); DC_D; tft_Write_16(x1 & 0xFF); + DC_C; tft_Write_16(TFT_PASET+0); DC_D; tft_Write_16(y0 >> 8); + DC_C; tft_Write_16(TFT_PASET+1); DC_D; tft_Write_16(y0 & 0xFF); + DC_C; tft_Write_16(TFT_PASET+2); DC_D; tft_Write_16(y1 >> 8); + DC_C; tft_Write_16(TFT_PASET+3); DC_D; tft_Write_16(y1 & 0xFF); + + DC_C; tft_Write_16(TFT_RAMWR); + DC_D; + #else + // This is for the RP2040 and PIO interface (SPI or parallel) + WAIT_FOR_STALL; + tft_pio->sm[pio_sm].instr = pio_instr_addr; + + TX_FIFO = TFT_CASET; + TX_FIFO = (x0<<16) | x1; + TX_FIFO = TFT_PASET; + TX_FIFO = (y0<<16) | y1; + TX_FIFO = TFT_RAMWR; + #endif + #else + SPI_BUSY_CHECK; + DC_C; tft_Write_8(TFT_CASET); + DC_D; tft_Write_32C(x0, x1); + DC_C; tft_Write_8(TFT_PASET); + DC_D; tft_Write_32C(y0, y1); + DC_C; tft_Write_8(TFT_RAMWR); + DC_D; + #endif // RP2040 SPI +#endif + //end_tft_write(); // Must be called after setWindow +} + +/*************************************************************************************** +** Function name: readAddrWindow +** Description: define an area to read a stream of pixels +***************************************************************************************/ +void TFT_eSPI::readAddrWindow(int32_t xs, int32_t ys, int32_t w, int32_t h) +{ + //begin_tft_write(); // Must be called before readAddrWindow or CS set low + + int32_t xe = xs + w - 1; + int32_t ye = ys + h - 1; + + addr_col = 0xFFFF; + addr_row = 0xFFFF; + +#if defined (SSD1963_DRIVER) + if ((rotation & 0x1) == 0) { transpose(xs, ys); transpose(xe, ye); } +#endif + +#ifdef CGRAM_OFFSET + xs += colstart; + xe += colstart; + ys += rowstart; + ye += rowstart; +#endif + + // Temporary solution is to include the RP2040 optimised code here +#if (defined(ARDUINO_ARCH_RP2040) || defined (ARDUINO_ARCH_MBED)) && !defined(RP2040_PIO_INTERFACE) && !defined(ARDUINO_ARCH_NRF52840) + // Use hardware SPI port, this code does not swap from 8 to 16-bit + // to avoid the spi_set_format() call overhead + while (spi_get_hw(SPI_X)->sr & SPI_SSPSR_BSY_BITS) {}; + DC_C; + hw_write_masked(&spi_get_hw(SPI_X)->cr0, (8 - 1) << SPI_SSPCR0_DSS_LSB, SPI_SSPCR0_DSS_BITS); + spi_get_hw(SPI_X)->dr = (uint32_t)TFT_CASET; + + while (spi_get_hw(SPI_X)->sr & SPI_SSPSR_BSY_BITS) {}; + DC_D; + spi_get_hw(SPI_X)->dr = (uint32_t)xs>>8; + spi_get_hw(SPI_X)->dr = (uint32_t)xs; + spi_get_hw(SPI_X)->dr = (uint32_t)xe>>8; + spi_get_hw(SPI_X)->dr = (uint32_t)xe; + + while (spi_get_hw(SPI_X)->sr & SPI_SSPSR_BSY_BITS) {}; + DC_C; + spi_get_hw(SPI_X)->dr = (uint32_t)TFT_PASET; + + while (spi_get_hw(SPI_X)->sr & SPI_SSPSR_BSY_BITS) {}; + DC_D; + spi_get_hw(SPI_X)->dr = (uint32_t)ys>>8; + spi_get_hw(SPI_X)->dr = (uint32_t)ys; + spi_get_hw(SPI_X)->dr = (uint32_t)ye>>8; + spi_get_hw(SPI_X)->dr = (uint32_t)ye; + + while (spi_get_hw(SPI_X)->sr & SPI_SSPSR_BSY_BITS) {}; + DC_C; + spi_get_hw(SPI_X)->dr = (uint32_t)TFT_RAMRD; + + while (spi_get_hw(SPI_X)->sr & SPI_SSPSR_BSY_BITS) {}; + DC_D; + + // Flush the rx buffer and reset overflow flag + while (spi_is_readable(SPI_X)) (void)spi_get_hw(SPI_X)->dr; + spi_get_hw(SPI_X)->icr = SPI_SSPICR_RORIC_BITS; + +#else + // Column addr set + DC_C; tft_Write_8(TFT_CASET); + DC_D; tft_Write_32C(xs, xe); + + // Row addr set + DC_C; tft_Write_8(TFT_PASET); + DC_D; tft_Write_32C(ys, ye); + + // Read CGRAM command + DC_C; tft_Write_8(TFT_RAMRD); + + DC_D; +#endif // RP2040 SPI + + //end_tft_write(); // Must be called after readAddrWindow or CS set high +} + + +/*************************************************************************************** +** Function name: drawPixel +** Description: push a single pixel at an arbitrary position +***************************************************************************************/ +void TFT_eSPI::drawPixel(int32_t x, int32_t y, uint32_t color) +{ + if (_vpOoB) return; + + x+= _xDatum; + y+= _yDatum; + + // Range checking + if ((x < _vpX) || (y < _vpY) ||(x >= _vpW) || (y >= _vpH)) return; + +#ifdef CGRAM_OFFSET + x+=colstart; + y+=rowstart; +#endif + +#if (defined (MULTI_TFT_SUPPORT) || defined (GC9A01_DRIVER)) && !defined (ILI9225_DRIVER) + addr_row = 0xFFFF; + addr_col = 0xFFFF; +#endif + + begin_tft_write(); + +#if defined (ILI9225_DRIVER) + if (rotation & 0x01) { transpose(x, y); } + SPI_BUSY_CHECK; + + // Set window to full screen to optimise sequential pixel rendering + if (addr_row != 0x9225) { + addr_row = 0x9225; // addr_row used for flag + DC_C; tft_Write_8(TFT_CASET1); + DC_D; tft_Write_16(0); + DC_C; tft_Write_8(TFT_CASET2); + DC_D; tft_Write_16(175); + + DC_C; tft_Write_8(TFT_PASET1); + DC_D; tft_Write_16(0); + DC_C; tft_Write_8(TFT_PASET2); + DC_D; tft_Write_16(219); + } + + // Define pixel coordinate + DC_C; tft_Write_8(TFT_RAM_ADDR1); + DC_D; tft_Write_16(x); + DC_C; tft_Write_8(TFT_RAM_ADDR2); + DC_D; tft_Write_16(y); + + // write to RAM + DC_C; tft_Write_8(TFT_RAMWR); + #if defined(TFT_PARALLEL_8_BIT) || defined(TFT_PARALLEL_16_BIT) || !defined(ESP32) + DC_D; tft_Write_16(color); + #else + DC_D; tft_Write_16N(color); + #endif + +// Temporary solution is to include the RP2040 optimised code here +#elif (defined (ARDUINO_ARCH_RP2040) || defined (ARDUINO_ARCH_MBED)) && !defined (SSD1351_DRIVER) && !defined(ARDUINO_ARCH_NRF52840) + + #if defined (SSD1963_DRIVER) + if ((rotation & 0x1) == 0) { transpose(x, y); } + #endif + + #if !defined(RP2040_PIO_INTERFACE) + while (spi_get_hw(SPI_X)->sr & SPI_SSPSR_BSY_BITS) {}; + + #if defined (RPI_DISPLAY_TYPE) // RPi TFT type always needs 16-bit transfers + hw_write_masked(&spi_get_hw(SPI_X)->cr0, (16 - 1) << SPI_SSPCR0_DSS_LSB, SPI_SSPCR0_DSS_BITS); + #else + hw_write_masked(&spi_get_hw(SPI_X)->cr0, (8 - 1) << SPI_SSPCR0_DSS_LSB, SPI_SSPCR0_DSS_BITS); + #endif + + if (addr_col != x) { + DC_C; + spi_get_hw(SPI_X)->dr = (uint32_t)TFT_CASET; + while (spi_get_hw(SPI_X)->sr & SPI_SSPSR_BSY_BITS){}; + DC_D; + spi_get_hw(SPI_X)->dr = (uint32_t)x>>8; + spi_get_hw(SPI_X)->dr = (uint32_t)x; + spi_get_hw(SPI_X)->dr = (uint32_t)x>>8; + spi_get_hw(SPI_X)->dr = (uint32_t)x; + addr_col = x; + while (spi_get_hw(SPI_X)->sr & SPI_SSPSR_BSY_BITS) {}; + } + + if (addr_row != y) { + DC_C; + spi_get_hw(SPI_X)->dr = (uint32_t)TFT_PASET; + while (spi_get_hw(SPI_X)->sr & SPI_SSPSR_BSY_BITS) {}; + DC_D; + spi_get_hw(SPI_X)->dr = (uint32_t)y>>8; + spi_get_hw(SPI_X)->dr = (uint32_t)y; + spi_get_hw(SPI_X)->dr = (uint32_t)y>>8; + spi_get_hw(SPI_X)->dr = (uint32_t)y; + addr_row = y; + while (spi_get_hw(SPI_X)->sr & SPI_SSPSR_BSY_BITS) {}; + } + + DC_C; + spi_get_hw(SPI_X)->dr = (uint32_t)TFT_RAMWR; + + #if defined (SPI_18BIT_DRIVER) // SPI 18-bit colour + uint8_t r = (color & 0xF800)>>8; + uint8_t g = (color & 0x07E0)>>3; + uint8_t b = (color & 0x001F)<<3; + while (spi_get_hw(SPI_X)->sr & SPI_SSPSR_BSY_BITS) {}; + DC_D; + tft_Write_8N(r); tft_Write_8N(g); tft_Write_8N(b); + #else + while (spi_get_hw(SPI_X)->sr & SPI_SSPSR_BSY_BITS) {}; + DC_D; + #if defined (RPI_DISPLAY_TYPE) // RPi TFT type always needs 16-bit transfers + spi_get_hw(SPI_X)->dr = (uint32_t)color; + #else + spi_get_hw(SPI_X)->dr = (uint32_t)color>>8; + spi_get_hw(SPI_X)->dr = (uint32_t)color; + #endif + #endif + while (spi_get_hw(SPI_X)->sr & SPI_SSPSR_BSY_BITS) {}; + #elif defined (RM68120_DRIVER) + if (addr_col != x) { + DC_C; tft_Write_16(TFT_CASET+0); DC_D; tft_Write_16(x >> 8); + DC_C; tft_Write_16(TFT_CASET+1); DC_D; tft_Write_16(x & 0xFF); + DC_C; tft_Write_16(TFT_CASET+2); DC_D; tft_Write_16(x >> 8); + DC_C; tft_Write_16(TFT_CASET+3); DC_D; tft_Write_16(x & 0xFF); + addr_col = x; + } + if (addr_row != y) { + DC_C; tft_Write_16(TFT_PASET+0); DC_D; tft_Write_16(y >> 8); + DC_C; tft_Write_16(TFT_PASET+1); DC_D; tft_Write_16(y & 0xFF); + DC_C; tft_Write_16(TFT_PASET+2); DC_D; tft_Write_16(y >> 8); + DC_C; tft_Write_16(TFT_PASET+3); DC_D; tft_Write_16(y & 0xFF); + addr_row = y; + } + DC_C; tft_Write_16(TFT_RAMWR); DC_D; + + TX_FIFO = color; + #else + // This is for the RP2040 and PIO interface (SPI or parallel) + WAIT_FOR_STALL; + tft_pio->sm[pio_sm].instr = pio_instr_addr; + TX_FIFO = TFT_CASET; + TX_FIFO = (x<<16) | x; + TX_FIFO = TFT_PASET; + TX_FIFO = (y<<16) | y; + TX_FIFO = TFT_RAMWR; + //DC set high by PIO + #if defined (SPI_18BIT_DRIVER) || (defined (SSD1963_DRIVER) && defined (TFT_PARALLEL_8_BIT)) + TX_FIFO = ((color & 0xF800)<<8) | ((color & 0x07E0)<<5) | ((color & 0x001F)<<3); + #else + TX_FIFO = color; + #endif + + #endif + +#else + + #if defined (SSD1963_DRIVER) + if ((rotation & 0x1) == 0) { transpose(x, y); } + #endif + + SPI_BUSY_CHECK; + + #if defined (SSD1351_DRIVER) + if (rotation & 0x1) { transpose(x, y); } + // No need to send x if it has not changed (speeds things up) + if (addr_col != x) { + DC_C; tft_Write_8(TFT_CASET); + DC_D; tft_Write_16(x | (x << 8)); + addr_col = x; + } + + // No need to send y if it has not changed (speeds things up) + if (addr_row != y) { + DC_C; tft_Write_8(TFT_PASET); + DC_D; tft_Write_16(y | (y << 8)); + addr_row = y; + } + #else + // No need to send x if it has not changed (speeds things up) + if (addr_col != x) { + DC_C; tft_Write_8(TFT_CASET); + DC_D; tft_Write_32D(x); + addr_col = x; + } + + // No need to send y if it has not changed (speeds things up) + if (addr_row != y) { + DC_C; tft_Write_8(TFT_PASET); + DC_D; tft_Write_32D(y); + addr_row = y; + } + #endif + + DC_C; tft_Write_8(TFT_RAMWR); + + #if defined(TFT_PARALLEL_8_BIT) || defined(TFT_PARALLEL_16_BIT) || !defined(ESP32) + DC_D; tft_Write_16(color); + #else + DC_D; tft_Write_16N(color); + #endif +#endif + + end_tft_write(); +} + +/*************************************************************************************** +** Function name: pushColor +** Description: push a single pixel +***************************************************************************************/ +void TFT_eSPI::pushColor(uint16_t color) +{ + begin_tft_write(); + + SPI_BUSY_CHECK; + tft_Write_16N(color); + + end_tft_write(); +} + + +/*************************************************************************************** +** Function name: pushColor +** Description: push a single colour to "len" pixels +***************************************************************************************/ +void TFT_eSPI::pushColor(uint16_t color, uint32_t len) +{ + begin_tft_write(); + + pushBlock(color, len); + + end_tft_write(); +} + +/*************************************************************************************** +** Function name: startWrite +** Description: begin transaction with CS low, MUST later call endWrite +***************************************************************************************/ +void TFT_eSPI::startWrite(void) +{ + begin_tft_write(); + lockTransaction = true; // Lock transaction for all sequentially run sketch functions + inTransaction = true; +} + +/*************************************************************************************** +** Function name: endWrite +** Description: end transaction with CS high +***************************************************************************************/ +void TFT_eSPI::endWrite(void) +{ + lockTransaction = false; // Release sketch induced transaction lock + inTransaction = false; + DMA_BUSY_CHECK; // Safety check - user code should have checked this! + end_tft_write(); // Release SPI bus +} + +/*************************************************************************************** +** Function name: writeColor (use startWrite() and endWrite() before & after) +** Description: raw write of "len" pixels avoiding transaction check +***************************************************************************************/ +void TFT_eSPI::writeColor(uint16_t color, uint32_t len) +{ + pushBlock(color, len); +} + +/*************************************************************************************** +** Function name: pushColors +** Description: push an array of pixels for 16-bit raw image drawing +***************************************************************************************/ +// Assumed that setAddrWindow() has previously been called +// len is number of bytes, not pixels +void TFT_eSPI::pushColors(uint8_t *data, uint32_t len) +{ + begin_tft_write(); + + pushPixels(data, len>>1); + + end_tft_write(); +} + + +/*************************************************************************************** +** Function name: pushColors +** Description: push an array of pixels, for image drawing +***************************************************************************************/ +void TFT_eSPI::pushColors(uint16_t *data, uint32_t len, bool swap) +{ + begin_tft_write(); + if (swap) {swap = _swapBytes; _swapBytes = true; } + + pushPixels(data, len); + + _swapBytes = swap; // Restore old value + end_tft_write(); +} + + +/*************************************************************************************** +** Function name: drawLine +** Description: draw a line between 2 arbitrary points +***************************************************************************************/ +// Bresenham's algorithm - thx Wikipedia - speed enhanced by Bodmer to use +// an efficient FastH/V Line draw routine for line segments of 2 pixels or more +void TFT_eSPI::drawLine(int32_t x0, int32_t y0, int32_t x1, int32_t y1, uint32_t color) +{ + if (_vpOoB) return; + + //begin_tft_write(); // Sprite class can use this function, avoiding begin_tft_write() + inTransaction = true; + + //x+= _xDatum; // Not added here, added by drawPixel & drawFastXLine + //y+= _yDatum; + + bool steep = abs(y1 - y0) > abs(x1 - x0); + if (steep) { + transpose(x0, y0); + transpose(x1, y1); + } + + if (x0 > x1) { + transpose(x0, x1); + transpose(y0, y1); + } + + int32_t dx = x1 - x0, dy = abs(y1 - y0);; + + int32_t err = dx >> 1, ystep = -1, xs = x0, dlen = 0; + + if (y0 < y1) ystep = 1; + + // Split into steep and not steep for FastH/V separation + if (steep) { + for (; x0 <= x1; x0++) { + dlen++; + err -= dy; + if (err < 0) { + if (dlen == 1) drawPixel(y0, xs, color); + else drawFastVLine(y0, xs, dlen, color); + dlen = 0; + y0 += ystep; xs = x0 + 1; + err += dx; + } + } + if (dlen) drawFastVLine(y0, xs, dlen, color); + } + else + { + for (; x0 <= x1; x0++) { + dlen++; + err -= dy; + if (err < 0) { + if (dlen == 1) drawPixel(xs, y0, color); + else drawFastHLine(xs, y0, dlen, color); + dlen = 0; + y0 += ystep; xs = x0 + 1; + err += dx; + } + } + if (dlen) drawFastHLine(xs, y0, dlen, color); + } + + inTransaction = lockTransaction; + end_tft_write(); +} + + +/*************************************************************************************** +** Description: Constants for anti-aliased line drawing on TFT and in Sprites +***************************************************************************************/ +constexpr float PixelAlphaGain = 255.0; +constexpr float LoAlphaTheshold = 1.0/32.0; +constexpr float HiAlphaTheshold = 1.0 - LoAlphaTheshold; +constexpr float deg2rad = 3.14159265359/180.0; + +/*************************************************************************************** +** Function name: drawPixel (alpha blended) +** Description: Draw a pixel blended with the screen or bg pixel colour +***************************************************************************************/ +uint16_t TFT_eSPI::drawPixel(int32_t x, int32_t y, uint32_t color, uint8_t alpha, uint32_t bg_color) +{ + if (bg_color == 0x00FFFFFF) bg_color = readPixel(x, y); + color = fastBlend(alpha, color, bg_color); + drawPixel(x, y, color); + return color; +} + + +/*************************************************************************************** +** Function name: drawSmoothArc +** Description: Draw a smooth arc clockwise from 6 o'clock +***************************************************************************************/ +void TFT_eSPI::drawSmoothArc(int32_t x, int32_t y, int32_t r, int32_t ir, uint32_t startAngle, uint32_t endAngle, uint32_t fg_color, uint32_t bg_color, bool roundEnds) +// Centre at x,y +// r = arc outer radius, ir = arc inner radius. Inclusive so arc thickness = r - ir + 1 +// Angles in range 0-360 +// Arc foreground colour anti-aliased with background colour at edges +// anti-aliased roundEnd is optional, default is anti-aliased straight end +// Note: rounded ends extend the arc angle so can overlap, user sketch to manage this. +{ + inTransaction = true; + + if (endAngle != startAngle && (startAngle != 0 || endAngle != 360)) + { + float sx = -sinf(startAngle * deg2rad); + float sy = +cosf(startAngle * deg2rad); + float ex = -sinf( endAngle * deg2rad); + float ey = +cosf( endAngle * deg2rad); + + if (roundEnds) + { // Round ends + sx = sx * (r + ir)/2.0 + x; + sy = sy * (r + ir)/2.0 + y; + drawSpot(sx, sy, (r - ir)/2.0, fg_color, bg_color); + + ex = ex * (r + ir)/2.0 + x; + ey = ey * (r + ir)/2.0 + y; + drawSpot(ex, ey, (r - ir)/2.0, fg_color, bg_color); + } + else + { // Square ends + float asx = sx * ir + x; + float asy = sy * ir + y; + float aex = sx * r + x; + float aey = sy * r + y; + drawWedgeLine(asx, asy, aex, aey, 0.3, 0.3, fg_color, bg_color); + + asx = ex * ir + x; + asy = ey * ir + y; + aex = ex * r + x; + aey = ey * r + y; + drawWedgeLine(asx, asy, aex, aey, 0.3, 0.3, fg_color, bg_color); + } + + // Draw arc + drawArc(x, y, r, ir, startAngle, endAngle, fg_color, bg_color); + + } + else // Draw full 360 + { + drawArc(x, y, r, ir, 0, 360, fg_color, bg_color); + } + + inTransaction = lockTransaction; + end_tft_write(); +} + +/*************************************************************************************** +** Function name: sqrt_fraction (private function) +** Description: Smooth graphics support function for alpha derivation +***************************************************************************************/ +// Compute the fixed point square root of an integer and +// return the 8 MS bits of fractional part. +// Quicker than sqrt() for processors that do not have an FPU (e.g. RP2040) +inline uint8_t TFT_eSPI::sqrt_fraction(uint32_t num) { + if (num > (0x40000000)) return 0; + uint32_t bsh = 0x00004000; + uint32_t fpr = 0; + uint32_t osh = 0; + + // Auto adjust from U8:8 up to U15:16 + while (num>bsh) {bsh <<= 2; osh++;} + + do { + uint32_t bod = bsh + fpr; + if(num >= bod) + { + num -= bod; + fpr = bsh + bod; + } + num <<= 1; + } while(bsh >>= 1); + + return fpr>>osh; +} + +/*************************************************************************************** +** Function name: drawArc +** Description: Draw an arc clockwise from 6 o'clock position +***************************************************************************************/ +// Centre at x,y +// r = arc outer radius, ir = arc inner radius. Inclusive, so arc thickness = r-ir+1 +// Angles MUST be in range 0-360 +// Arc foreground fg_color anti-aliased with background colour along sides +// smooth is optional, default is true, smooth=false means no antialiasing +// Note: Arc ends are not anti-aliased (use drawSmoothArc instead for that) +void TFT_eSPI::drawArc(int32_t x, int32_t y, int32_t r, int32_t ir, + uint32_t startAngle, uint32_t endAngle, + uint32_t fg_color, uint32_t bg_color, + bool smooth) +{ + if (endAngle > 360) endAngle = 360; + if (startAngle > 360) startAngle = 360; + if (_vpOoB || startAngle == endAngle) return; + if (r < ir) transpose(r, ir); // Required that r > ir + if (r <= 0 || ir < 0) return; // Invalid r, ir can be zero (circle sector) + + if (endAngle < startAngle) { + // Arc sweeps through 6 o'clock so draw in two parts + if (startAngle < 360) drawArc(x, y, r, ir, startAngle, 360, fg_color, bg_color, smooth); + if (endAngle == 0) return; + startAngle = 0; + } + inTransaction = true; + + int32_t xs = 0; // x start position for quadrant scan + uint8_t alpha = 0; // alpha value for blending pixels + + uint32_t r2 = r * r; // Outer arc radius^2 + if (smooth) r++; // Outer AA zone radius + uint32_t r1 = r * r; // Outer AA radius^2 + int16_t w = r - ir; // Width of arc (r - ir + 1) + uint32_t r3 = ir * ir; // Inner arc radius^2 + if (smooth) ir--; // Inner AA zone radius + uint32_t r4 = ir * ir; // Inner AA radius^2 + + // 1 | 2 + // ---¦--- Arc quadrant index + // 0 | 3 + // Fixed point U16.16 slope table for arc start/end in each quadrant + uint32_t startSlope[4] = {0, 0, 0xFFFFFFFF, 0}; + uint32_t endSlope[4] = {0, 0xFFFFFFFF, 0, 0}; + + // Ensure maximum U16.16 slope of arc ends is ~ 0x8000 0000 + constexpr float minDivisor = 1.0f/0x8000; + + // Fill in start slope table and empty quadrants + float fabscos = fabsf(cosf(startAngle * deg2rad)); + float fabssin = fabsf(sinf(startAngle * deg2rad)); + + // U16.16 slope of arc start + uint32_t slope = (fabscos/(fabssin + minDivisor)) * (float)(1UL<<16); + + // Update slope table, add slope for arc start + if (startAngle <= 90) { + startSlope[0] = slope; + } + else if (startAngle <= 180) { + startSlope[1] = slope; + } + else if (startAngle <= 270) { + startSlope[1] = 0xFFFFFFFF; + startSlope[2] = slope; + } + else { + startSlope[1] = 0xFFFFFFFF; + startSlope[2] = 0; + startSlope[3] = slope; + } + + // Fill in end slope table and empty quadrants + fabscos = fabsf(cosf(endAngle * deg2rad)); + fabssin = fabsf(sinf(endAngle * deg2rad)); + + // U16.16 slope of arc end + slope = (uint32_t)((fabscos/(fabssin + minDivisor)) * (float)(1UL<<16)); + + // Work out which quadrants will need to be drawn and add slope for arc end + if (endAngle <= 90) { + endSlope[0] = slope; + endSlope[1] = 0; + startSlope[2] = 0; + } + else if (endAngle <= 180) { + endSlope[1] = slope; + startSlope[2] = 0; + } + else if (endAngle <= 270) { + endSlope[2] = slope; + } + else { + endSlope[3] = slope; + } + + // Scan quadrant + for (int32_t cy = r - 1; cy > 0; cy--) + { + uint32_t len[4] = { 0, 0, 0, 0}; // Pixel run length + int32_t xst[4] = {-1, -1, -1, -1}; // Pixel run x start + uint32_t dy2 = (r - cy) * (r - cy); + + // Find and track arc zone start point + while ((r - xs) * (r - xs) + dy2 >= r1) xs++; + + for (int32_t cx = xs; cx < r; cx++) + { + // Calculate radius^2 + uint32_t hyp = (r - cx) * (r - cx) + dy2; + + // If in outer zone calculate alpha + if (hyp > r2) { + alpha = ~sqrt_fraction(hyp); // Outer AA zone + } + // If within arc fill zone, get line start and lengths for each quadrant + else if (hyp >= r3) { + // Calculate U16.16 slope + slope = ((r - cy) << 16)/(r - cx); + if (slope <= startSlope[0] && slope >= endSlope[0]) { // slope hi -> lo + xst[0] = cx; // Bottom left line end + len[0]++; + } + if (slope >= startSlope[1] && slope <= endSlope[1]) { // slope lo -> hi + xst[1] = cx; // Top left line end + len[1]++; + } + if (slope <= startSlope[2] && slope >= endSlope[2]) { // slope hi -> lo + xst[2] = cx; // Bottom right line start + len[2]++; + } + if (slope <= endSlope[3] && slope >= startSlope[3]) { // slope lo -> hi + xst[3] = cx; // Top right line start + len[3]++; + } + continue; // Next x + } + else { + if (hyp <= r4) break; // Skip inner pixels + alpha = sqrt_fraction(hyp); // Inner AA zone + } + + if (alpha < 16) continue; // Skip low alpha pixels + + // If background is read it must be done in each quadrant + uint16_t pcol = fastBlend(alpha, fg_color, bg_color); + // Check if an AA pixels need to be drawn + slope = ((r - cy)<<16)/(r - cx); + if (slope <= startSlope[0] && slope >= endSlope[0]) // BL + drawPixel(x + cx - r, y - cy + r, pcol); + if (slope >= startSlope[1] && slope <= endSlope[1]) // TL + drawPixel(x + cx - r, y + cy - r, pcol); + if (slope <= startSlope[2] && slope >= endSlope[2]) // TR + drawPixel(x - cx + r, y + cy - r, pcol); + if (slope <= endSlope[3] && slope >= startSlope[3]) // BR + drawPixel(x - cx + r, y - cy + r, pcol); + } + // Add line in inner zone + if (len[0]) drawFastHLine(x + xst[0] - len[0] + 1 - r, y - cy + r, len[0], fg_color); // BL + if (len[1]) drawFastHLine(x + xst[1] - len[1] + 1 - r, y + cy - r, len[1], fg_color); // TL + if (len[2]) drawFastHLine(x - xst[2] + r, y + cy - r, len[2], fg_color); // TR + if (len[3]) drawFastHLine(x - xst[3] + r, y - cy + r, len[3], fg_color); // BR + } + + // Fill in centre lines + if (startAngle == 0 || endAngle == 360) drawFastVLine(x, y + r - w, w, fg_color); // Bottom + if (startAngle <= 90 && endAngle >= 90) drawFastHLine(x - r + 1, y, w, fg_color); // Left + if (startAngle <= 180 && endAngle >= 180) drawFastVLine(x, y - r + 1, w, fg_color); // Top + if (startAngle <= 270 && endAngle >= 270) drawFastHLine(x + r - w, y, w, fg_color); // Right + + inTransaction = lockTransaction; + end_tft_write(); +} + +/*************************************************************************************** +** Function name: drawSmoothCircle +** Description: Draw a smooth circle +***************************************************************************************/ +// To have effective anti-aliasing the circle will be 3 pixels thick +void TFT_eSPI::drawSmoothCircle(int32_t x, int32_t y, int32_t r, uint32_t fg_color, uint32_t bg_color) +{ + drawSmoothRoundRect(x-r, y-r, r, r-1, 0, 0, fg_color, bg_color); +} + +/*************************************************************************************** +** Function name: fillSmoothCircle +** Description: Draw a filled anti-aliased circle +***************************************************************************************/ +void TFT_eSPI::fillSmoothCircle(int32_t x, int32_t y, int32_t r, uint32_t color, uint32_t bg_color) +{ + if (r <= 0) return; + + inTransaction = true; + + drawFastHLine(x - r, y, 2 * r + 1, color); + int32_t xs = 1; + int32_t cx = 0; + + int32_t r1 = r * r; + r++; + int32_t r2 = r * r; + + for (int32_t cy = r - 1; cy > 0; cy--) + { + int32_t dy2 = (r - cy) * (r - cy); + for (cx = xs; cx < r; cx++) + { + int32_t hyp2 = (r - cx) * (r - cx) + dy2; + if (hyp2 <= r1) break; + if (hyp2 >= r2) continue; + + uint8_t alpha = ~sqrt_fraction(hyp2); + if (alpha > 246) break; + xs = cx; + if (alpha < 9) continue; + + if (bg_color == 0x00FFFFFF) { + drawPixel(x + cx - r, y + cy - r, color, alpha, bg_color); + drawPixel(x - cx + r, y + cy - r, color, alpha, bg_color); + drawPixel(x - cx + r, y - cy + r, color, alpha, bg_color); + drawPixel(x + cx - r, y - cy + r, color, alpha, bg_color); + } + else { + uint16_t pcol = drawPixel(x + cx - r, y + cy - r, color, alpha, bg_color); + drawPixel(x - cx + r, y + cy - r, pcol); + drawPixel(x - cx + r, y - cy + r, pcol); + drawPixel(x + cx - r, y - cy + r, pcol); + } + } + drawFastHLine(x + cx - r, y + cy - r, 2 * (r - cx) + 1, color); + drawFastHLine(x + cx - r, y - cy + r, 2 * (r - cx) + 1, color); + } + inTransaction = lockTransaction; + end_tft_write(); +} + + +/*************************************************************************************** +** Function name: drawSmoothRoundRect +** Description: Draw a rounded rectangle +***************************************************************************************/ +// x,y is top left corner of bounding box for a complete rounded rectangle +// r = arc outer corner radius, ir = arc inner radius. Arc thickness = r-ir+1 +// w and h are width and height of the bounding rectangle +// If w and h are < radius (e.g. 0,0) a circle will be drawn with centre at x+r,y+r +// Arc foreground fg_color anti-aliased with background colour at edges +// A subset of corners can be drawn by specifying a quadrants mask. A bit set in the +// mask means draw that quadrant (all are drawn if parameter missing): +// 0x1 | 0x2 +// ---¦--- Arc quadrant mask select bits (as in drawCircleHelper fn) +// 0x8 | 0x4 +void TFT_eSPI::drawSmoothRoundRect(int32_t x, int32_t y, int32_t r, int32_t ir, int32_t w, int32_t h, uint32_t fg_color, uint32_t bg_color, uint8_t quadrants) +{ + if (_vpOoB) return; + if (r < ir) transpose(r, ir); // Required that r > ir + if (r <= 0 || ir < 0) return; // Invalid + + w -= 2*r; + h -= 2*r; + + if (w < 0) w = 0; + if (h < 0) h = 0; + + inTransaction = true; + + x += r; + y += r; + + uint16_t t = r - ir + 1; + int32_t xs = 0; + int32_t cx = 0; + + int32_t r2 = r * r; // Outer arc radius^2 + r++; + int32_t r1 = r * r; // Outer AA zone radius^2 + + int32_t r3 = ir * ir; // Inner arc radius^2 + ir--; + int32_t r4 = ir * ir; // Inner AA zone radius^2 + + uint8_t alpha = 0; + + // Scan top left quadrant x y r ir fg_color bg_color + for (int32_t cy = r - 1; cy > 0; cy--) + { + int32_t len = 0; // Pixel run length + int32_t lxst = 0; // Left side run x start + int32_t rxst = 0; // Right side run x start + int32_t dy2 = (r - cy) * (r - cy); + + // Find and track arc zone start point + while ((r - xs) * (r - xs) + dy2 >= r1) xs++; + + for (cx = xs; cx < r; cx++) + { + // Calculate radius^2 + int32_t hyp = (r - cx) * (r - cx) + dy2; + + // If in outer zone calculate alpha + if (hyp > r2) { + alpha = ~sqrt_fraction(hyp); // Outer AA zone + } + // If within arc fill zone, get line lengths for each quadrant + else if (hyp >= r3) { + rxst = cx; // Right side start + len++; // Line segment length + continue; // Next x + } + else { + if (hyp <= r4) break; // Skip inner pixels + alpha = sqrt_fraction(hyp); // Inner AA zone + } + + if (alpha < 16) continue; // Skip low alpha pixels + + // If background is read it must be done in each quadrant - TODO + uint16_t pcol = fastBlend(alpha, fg_color, bg_color); + if (quadrants & 0x8) drawPixel(x + cx - r, y - cy + r + h, pcol); // BL + if (quadrants & 0x1) drawPixel(x + cx - r, y + cy - r, pcol); // TL + if (quadrants & 0x2) drawPixel(x - cx + r + w, y + cy - r, pcol); // TR + if (quadrants & 0x4) drawPixel(x - cx + r + w, y - cy + r + h, pcol); // BR + } + // Fill arc inner zone in each quadrant + lxst = rxst - len + 1; // Calculate line segment start for left side + if (quadrants & 0x8) drawFastHLine(x + lxst - r, y - cy + r + h, len, fg_color); // BL + if (quadrants & 0x1) drawFastHLine(x + lxst - r, y + cy - r, len, fg_color); // TL + if (quadrants & 0x2) drawFastHLine(x - rxst + r + w, y + cy - r, len, fg_color); // TR + if (quadrants & 0x4) drawFastHLine(x - rxst + r + w, y - cy + r + h, len, fg_color); // BR + } + + // Draw sides + if ((quadrants & 0xC) == 0xC) fillRect(x, y + r - t + h, w + 1, t, fg_color); // Bottom + if ((quadrants & 0x9) == 0x9) fillRect(x - r + 1, y, t, h + 1, fg_color); // Left + if ((quadrants & 0x3) == 0x3) fillRect(x, y - r + 1, w + 1, t, fg_color); // Top + if ((quadrants & 0x6) == 0x6) fillRect(x + r - t + w, y, t, h + 1, fg_color); // Right + + inTransaction = lockTransaction; + end_tft_write(); +} + +/*************************************************************************************** +** Function name: fillSmoothRoundRect +** Description: Draw a filled anti-aliased rounded corner rectangle +***************************************************************************************/ +void TFT_eSPI::fillSmoothRoundRect(int32_t x, int32_t y, int32_t w, int32_t h, int32_t r, uint32_t color, uint32_t bg_color) +{ + inTransaction = true; + + int32_t xs = 0; + int32_t cx = 0; + + // Limit radius to half width or height + if (r < 0) r = 0; + if (r > w/2) r = w/2; + if (r > h/2) r = h/2; + + y += r; + h -= 2*r; + fillRect(x, y, w, h, color); + + h--; + x += r; + w -= 2*r+1; + + int32_t r1 = r * r; + r++; + int32_t r2 = r * r; + + for (int32_t cy = r - 1; cy > 0; cy--) + { + int32_t dy2 = (r - cy) * (r - cy); + for (cx = xs; cx < r; cx++) + { + int32_t hyp2 = (r - cx) * (r - cx) + dy2; + if (hyp2 <= r1) break; + if (hyp2 >= r2) continue; + + uint8_t alpha = ~sqrt_fraction(hyp2); + if (alpha > 246) break; + xs = cx; + if (alpha < 9) continue; + + drawPixel(x + cx - r, y + cy - r, color, alpha, bg_color); + drawPixel(x - cx + r + w, y + cy - r, color, alpha, bg_color); + drawPixel(x - cx + r + w, y - cy + r + h, color, alpha, bg_color); + drawPixel(x + cx - r, y - cy + r + h, color, alpha, bg_color); + } + drawFastHLine(x + cx - r, y + cy - r, 2 * (r - cx) + 1 + w, color); + drawFastHLine(x + cx - r, y - cy + r + h, 2 * (r - cx) + 1 + w, color); + } + inTransaction = lockTransaction; + end_tft_write(); +} + +/*************************************************************************************** +** Function name: drawSpot - maths intensive, so for small filled circles +** Description: Draw an anti-aliased filled circle at ax,ay with radius r +***************************************************************************************/ +// Coordinates are floating point to achieve sub-pixel positioning +void TFT_eSPI::drawSpot(float ax, float ay, float r, uint32_t fg_color, uint32_t bg_color) +{ + // Filled circle can be created by the wide line function with zero line length + drawWedgeLine( ax, ay, ax, ay, r, r, fg_color, bg_color); +} + +/*************************************************************************************** +** Function name: drawWideLine - background colour specified or pixel read +** Description: draw an anti-aliased line with rounded ends, width wd +***************************************************************************************/ +void TFT_eSPI::drawWideLine(float ax, float ay, float bx, float by, float wd, uint32_t fg_color, uint32_t bg_color) +{ + drawWedgeLine( ax, ay, bx, by, wd/2.0, wd/2.0, fg_color, bg_color); +} + +/*************************************************************************************** +** Function name: drawWedgeLine - background colour specified or pixel read +** Description: draw an anti-aliased line with different width radiused ends +***************************************************************************************/ +void TFT_eSPI::drawWedgeLine(float ax, float ay, float bx, float by, float ar, float br, uint32_t fg_color, uint32_t bg_color) +{ + if ( (ar < 0.0) || (br < 0.0) )return; + if ( (fabsf(ax - bx) < 0.01f) && (fabsf(ay - by) < 0.01f) ) bx += 0.01f; // Avoid divide by zero + + // Find line bounding box + int32_t x0 = (int32_t)floorf(fminf(ax-ar, bx-br)); + int32_t x1 = (int32_t) ceilf(fmaxf(ax+ar, bx+br)); + int32_t y0 = (int32_t)floorf(fminf(ay-ar, by-br)); + int32_t y1 = (int32_t) ceilf(fmaxf(ay+ar, by+br)); + + if (!clipWindow(&x0, &y0, &x1, &y1)) return; + + // Establish x start and y start + int32_t ys = ay; + if ((ax-ar)>(bx-br)) ys = by; + + float rdt = ar - br; // Radius delta + float alpha = 1.0f; + ar += 0.5; + + uint16_t bg = bg_color; + float xpax, ypay, bax = bx - ax, bay = by - ay; + + begin_nin_write(); + inTransaction = true; + + int32_t xs = x0; + // Scan bounding box from ys down, calculate pixel intensity from distance to line + for (int32_t yp = ys; yp <= y1; yp++) { + bool swin = true; // Flag to start new window area + bool endX = false; // Flag to skip pixels + ypay = yp - ay; + for (int32_t xp = xs; xp <= x1; xp++) { + if (endX) if (alpha <= LoAlphaTheshold) break; // Skip right side + xpax = xp - ax; + alpha = ar - wedgeLineDistance(xpax, ypay, bax, bay, rdt); + if (alpha <= LoAlphaTheshold ) continue; + // Track edge to minimise calculations + if (!endX) { endX = true; xs = xp; } + if (alpha > HiAlphaTheshold) { + #ifdef GC9A01_DRIVER + drawPixel(xp, yp, fg_color); + #else + if (swin) { setWindow(xp, yp, x1, yp); swin = false; } + pushColor(fg_color); + #endif + continue; + } + //Blend color with background and plot + if (bg_color == 0x00FFFFFF) { + bg = readPixel(xp, yp); swin = true; + } + #ifdef GC9A01_DRIVER + uint16_t pcol = fastBlend((uint8_t)(alpha * PixelAlphaGain), fg_color, bg); + drawPixel(xp, yp, pcol); + swin = swin; + #else + if (swin) { setWindow(xp, yp, x1, yp); swin = false; } + pushColor(fastBlend((uint8_t)(alpha * PixelAlphaGain), fg_color, bg)); + #endif + } + } + + // Reset x start to left side of box + xs = x0; + // Scan bounding box from ys-1 up, calculate pixel intensity from distance to line + for (int32_t yp = ys-1; yp >= y0; yp--) { + bool swin = true; // Flag to start new window area + bool endX = false; // Flag to skip pixels + ypay = yp - ay; + for (int32_t xp = xs; xp <= x1; xp++) { + if (endX) if (alpha <= LoAlphaTheshold) break; // Skip right side of drawn line + xpax = xp - ax; + alpha = ar - wedgeLineDistance(xpax, ypay, bax, bay, rdt); + if (alpha <= LoAlphaTheshold ) continue; + // Track line boundary + if (!endX) { endX = true; xs = xp; } + if (alpha > HiAlphaTheshold) { + #ifdef GC9A01_DRIVER + drawPixel(xp, yp, fg_color); + #else + if (swin) { setWindow(xp, yp, x1, yp); swin = false; } + pushColor(fg_color); + #endif + continue; + } + //Blend colour with background and plot + if (bg_color == 0x00FFFFFF) { + bg = readPixel(xp, yp); swin = true; + } + #ifdef GC9A01_DRIVER + uint16_t pcol = fastBlend((uint8_t)(alpha * PixelAlphaGain), fg_color, bg); + drawPixel(xp, yp, pcol); + swin = swin; + #else + if (swin) { setWindow(xp, yp, x1, yp); swin = false; } + pushColor(fastBlend((uint8_t)(alpha * PixelAlphaGain), fg_color, bg)); + #endif + } + } + + inTransaction = lockTransaction; + end_nin_write(); +} + + +/*************************************************************************************** +** Function name: lineDistance - private helper function for drawWedgeLine +** Description: returns distance of px,py to closest part of a to b wedge +***************************************************************************************/ +inline float TFT_eSPI::wedgeLineDistance(float xpax, float ypay, float bax, float bay, float dr) +{ + float h = fmaxf(fminf((xpax * bax + ypay * bay) / (bax * bax + bay * bay), 1.0f), 0.0f); + float dx = xpax - bax * h, dy = ypay - bay * h; + return sqrtf(dx * dx + dy * dy) + h * dr; +} + + +/*************************************************************************************** +** Function name: drawFastVLine +** Description: draw a vertical line +***************************************************************************************/ +void TFT_eSPI::drawFastVLine(int32_t x, int32_t y, int32_t h, uint32_t color) +{ + if (_vpOoB) return; + + x+= _xDatum; + y+= _yDatum; + + // Clipping + if ((x < _vpX) || (x >= _vpW) || (y >= _vpH)) return; + + if (y < _vpY) { h += y - _vpY; y = _vpY; } + + if ((y + h) > _vpH) h = _vpH - y; + + if (h < 1) return; + + begin_tft_write(); + + setWindow(x, y, x, y + h - 1); + + pushBlock(color, h); + + end_tft_write(); +} + + +/*************************************************************************************** +** Function name: drawFastHLine +** Description: draw a horizontal line +***************************************************************************************/ +void TFT_eSPI::drawFastHLine(int32_t x, int32_t y, int32_t w, uint32_t color) +{ + if (_vpOoB) return; + + x+= _xDatum; + y+= _yDatum; + + // Clipping + if ((y < _vpY) || (x >= _vpW) || (y >= _vpH)) return; + + if (x < _vpX) { w += x - _vpX; x = _vpX; } + + if ((x + w) > _vpW) w = _vpW - x; + + if (w < 1) return; + + begin_tft_write(); + + setWindow(x, y, x + w - 1, y); + + pushBlock(color, w); + + end_tft_write(); +} + + +/*************************************************************************************** +** Function name: fillRect +** Description: draw a filled rectangle +***************************************************************************************/ +void TFT_eSPI::fillRect(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color) +{ + if (_vpOoB) return; + + x+= _xDatum; + y+= _yDatum; + + // Clipping + if ((x >= _vpW) || (y >= _vpH)) return; + + if (x < _vpX) { w += x - _vpX; x = _vpX; } + if (y < _vpY) { h += y - _vpY; y = _vpY; } + + if ((x + w) > _vpW) w = _vpW - x; + if ((y + h) > _vpH) h = _vpH - y; + + if ((w < 1) || (h < 1)) return; + + //Serial.print(" _xDatum=");Serial.print( _xDatum);Serial.print(", _yDatum=");Serial.print( _yDatum); + //Serial.print(", _xWidth=");Serial.print(_xWidth);Serial.print(", _yHeight=");Serial.println(_yHeight); + + //Serial.print(" _vpX=");Serial.print( _vpX);Serial.print(", _vpY=");Serial.print( _vpY); + //Serial.print(", _vpW=");Serial.print(_vpW);Serial.print(", _vpH=");Serial.println(_vpH); + + //Serial.print(" x=");Serial.print( y);Serial.print(", y=");Serial.print( y); + //Serial.print(", w=");Serial.print(w);Serial.print(", h=");Serial.println(h); + + begin_tft_write(); + + setWindow(x, y, x + w - 1, y + h - 1); + + pushBlock(color, w * h); + + end_tft_write(); +} + + +/*************************************************************************************** +** Function name: fillRectVGradient +** Description: draw a filled rectangle with a vertical colour gradient +***************************************************************************************/ +void TFT_eSPI::fillRectVGradient(int16_t x, int16_t y, int16_t w, int16_t h, uint32_t color1, uint32_t color2) +{ + if (_vpOoB) return; + + x+= _xDatum; + y+= _yDatum; + + // Clipping + if ((x >= _vpW) || (y >= _vpH)) return; + + if (x < _vpX) { w += x - _vpX; x = _vpX; } + if (y < _vpY) { h += y - _vpY; y = _vpY; } + + if ((x + w) > _vpW) w = _vpW - x; + if ((y + h) > _vpH) h = _vpH - y; + + if ((w < 1) || (h < 1)) return; + + begin_nin_write(); + + float delta = -255.0/h; + float alpha = 255.0; + uint32_t color = color1; + + while (h--) { + drawFastHLine(x, y++, w, color); + alpha += delta; + color = fastBlend((uint8_t)alpha, color1, color2); + } + + end_nin_write(); +} + + +/*************************************************************************************** +** Function name: fillRectHGradient +** Description: draw a filled rectangle with a horizontal colour gradient +***************************************************************************************/ +void TFT_eSPI::fillRectHGradient(int16_t x, int16_t y, int16_t w, int16_t h, uint32_t color1, uint32_t color2) +{ + if (_vpOoB) return; + + x+= _xDatum; + y+= _yDatum; + + // Clipping + if ((x >= _vpW) || (y >= _vpH)) return; + + if (x < _vpX) { w += x - _vpX; x = _vpX; } + if (y < _vpY) { h += y - _vpY; y = _vpY; } + + if ((x + w) > _vpW) w = _vpW - x; + if ((y + h) > _vpH) h = _vpH - y; + + if ((w < 1) || (h < 1)) return; + + begin_nin_write(); + + float delta = -255.0/w; + float alpha = 255.0; + uint32_t color = color1; + + while (w--) { + drawFastVLine(x++, y, h, color); + alpha += delta; + color = fastBlend((uint8_t)alpha, color1, color2); + } + + end_nin_write(); +} + + +/*************************************************************************************** +** Function name: color565 +** Description: convert three 8-bit RGB levels to a 16-bit colour value +***************************************************************************************/ +uint16_t TFT_eSPI::color565(uint8_t r, uint8_t g, uint8_t b) +{ + return ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3); +} + + +/*************************************************************************************** +** Function name: color16to8 +** Description: convert 16-bit colour to an 8-bit 332 RGB colour value +***************************************************************************************/ +uint8_t TFT_eSPI::color16to8(uint16_t c) +{ + return ((c & 0xE000)>>8) | ((c & 0x0700)>>6) | ((c & 0x0018)>>3); +} + + +/*************************************************************************************** +** Function name: color8to16 +** Description: convert 8-bit colour to a 16-bit 565 colour value +***************************************************************************************/ +uint16_t TFT_eSPI::color8to16(uint8_t color) +{ + uint8_t blue[] = {0, 11, 21, 31}; // blue 2 to 5-bit colour lookup table + uint16_t color16 = 0; + + // =====Green===== ===============Red============== + color16 = (color & 0x1C)<<6 | (color & 0xC0)<<5 | (color & 0xE0)<<8; + // =====Green===== =======Blue====== + color16 |= (color & 0x1C)<<3 | blue[color & 0x03]; + + return color16; +} + +/*************************************************************************************** +** Function name: color16to24 +** Description: convert 16-bit colour to a 24-bit 888 colour value +***************************************************************************************/ +uint32_t TFT_eSPI::color16to24(uint16_t color565) +{ + uint8_t r = (color565 >> 8) & 0xF8; r |= (r >> 5); + uint8_t g = (color565 >> 3) & 0xFC; g |= (g >> 6); + uint8_t b = (color565 << 3) & 0xF8; b |= (b >> 5); + + return ((uint32_t)r << 16) | ((uint32_t)g << 8) | ((uint32_t)b << 0); +} + +/*************************************************************************************** +** Function name: color24to16 +** Description: convert 24-bit colour to a 16-bit 565 colour value +***************************************************************************************/ +uint32_t TFT_eSPI::color24to16(uint32_t color888) +{ + uint16_t r = (color888 >> 8) & 0xF800; + uint16_t g = (color888 >> 5) & 0x07E0; + uint16_t b = (color888 >> 3) & 0x001F; + + return (r | g | b); +} + +/*************************************************************************************** +** Function name: invertDisplay +** Description: invert the display colours i = 1 invert, i = 0 normal +***************************************************************************************/ +void TFT_eSPI::invertDisplay(bool i) +{ + begin_tft_write(); + // Send the command twice as otherwise it does not always work! + writecommand(i ? TFT_INVON : TFT_INVOFF); + writecommand(i ? TFT_INVON : TFT_INVOFF); + end_tft_write(); +} + + +/************************************************************************** +** Function name: setAttribute +** Description: Sets a control parameter of an attribute +**************************************************************************/ +void TFT_eSPI::setAttribute(uint8_t attr_id, uint8_t param) { + switch (attr_id) { + break; + case CP437_SWITCH: + _cp437 = param; + break; + case UTF8_SWITCH: + _utf8 = param; + decoderState = 0; + break; + case PSRAM_ENABLE: +#if defined (ESP32) && defined (CONFIG_SPIRAM_SUPPORT) + if (psramFound()) _psram_enable = param; // Enable the use of PSRAM (if available) + else +#endif + _psram_enable = false; + break; + //case 4: // TBD future feature control + // _tbd = param; + // break; + } +} + + +/************************************************************************** +** Function name: getAttribute +** Description: Get value of an attribute (control parameter) +**************************************************************************/ +uint8_t TFT_eSPI::getAttribute(uint8_t attr_id) { + switch (attr_id) { + case CP437_SWITCH: // ON/OFF control of full CP437 character set + return _cp437; + case UTF8_SWITCH: // ON/OFF control of UTF-8 decoding + return _utf8; + case PSRAM_ENABLE: + return _psram_enable; + //case 3: // TBD future feature control + // return _tbd; + // break; + } + + return false; +} + +/*************************************************************************************** +** Function name: decodeUTF8 +** Description: Serial UTF-8 decoder with fall-back to extended ASCII +*************************************************************************************x*/ +uint16_t TFT_eSPI::decodeUTF8(uint8_t c) +{ + if (!_utf8) return c; + + // 7-bit Unicode Code Point + if ((c & 0x80) == 0x00) { + decoderState = 0; + return c; + } + + if (decoderState == 0) { + // 11-bit Unicode Code Point + if ((c & 0xE0) == 0xC0) { + decoderBuffer = ((c & 0x1F)<<6); + decoderState = 1; + return 0; + } + // 16-bit Unicode Code Point + if ((c & 0xF0) == 0xE0) { + decoderBuffer = ((c & 0x0F)<<12); + decoderState = 2; + return 0; + } + // 21-bit Unicode Code Point not supported so fall-back to extended ASCII + // if ((c & 0xF8) == 0xF0) return c; + } + else { + if (decoderState == 2) { + decoderBuffer |= ((c & 0x3F)<<6); + decoderState--; + return 0; + } + else { + decoderBuffer |= (c & 0x3F); + decoderState = 0; + return decoderBuffer; + } + } + + decoderState = 0; + + return c; // fall-back to extended ASCII +} + + +/*************************************************************************************** +** Function name: decodeUTF8 +** Description: Line buffer UTF-8 decoder with fall-back to extended ASCII +*************************************************************************************x*/ +uint16_t TFT_eSPI::decodeUTF8(uint8_t *buf, uint16_t *index, uint16_t remaining) +{ + uint16_t c = buf[(*index)++]; + //Serial.print("Byte from string = 0x"); Serial.println(c, HEX); + + if (!_utf8) return c; + + // 7-bit Unicode + if ((c & 0x80) == 0x00) return c; + + // 11-bit Unicode + if (((c & 0xE0) == 0xC0) && (remaining > 1)) + return ((c & 0x1F)<<6) | (buf[(*index)++]&0x3F); + + // 16-bit Unicode + if (((c & 0xF0) == 0xE0) && (remaining > 2)) { + c = ((c & 0x0F)<<12) | ((buf[(*index)++]&0x3F)<<6); + return c | ((buf[(*index)++]&0x3F)); + } + + // 21-bit Unicode not supported so fall-back to extended ASCII + // if (((c & 0xF8) == 0xF0) && (remaining > 3)) { + // c = ((c & 0x07) << 18) | ((buf[(*index)++] & 0x03F) << 12); + // c |= ((buf[(*index)++] & 0x3F) << 6); + // return c | ((buf[(*index)++] & 0x3F)); + + return c; // fall-back to extended ASCII +} + + +/*************************************************************************************** +** Function name: alphaBlend +** Description: Blend 16bit foreground and background +*************************************************************************************x*/ +uint16_t TFT_eSPI::alphaBlend(uint8_t alpha, uint16_t fgc, uint16_t bgc) +{ + // Split out and blend 5-bit red and blue channels + uint32_t rxb = bgc & 0xF81F; + rxb += ((fgc & 0xF81F) - rxb) * (alpha >> 2) >> 6; + // Split out and blend 6-bit green channel + uint32_t xgx = bgc & 0x07E0; + xgx += ((fgc & 0x07E0) - xgx) * alpha >> 8; + // Recombine channels + return (rxb & 0xF81F) | (xgx & 0x07E0); +} + +/*************************************************************************************** +** Function name: alphaBlend +** Description: Blend 16bit foreground and background with dither +*************************************************************************************x*/ +uint16_t TFT_eSPI::alphaBlend(uint8_t alpha, uint16_t fgc, uint16_t bgc, uint8_t dither) +{ + if (dither) { + int16_t alphaDither = (int16_t)alpha - dither + random(2*dither+1); // +/-4 randomised + alpha = (uint8_t)alphaDither; + if (alphaDither < 0) alpha = 0; + if (alphaDither >255) alpha = 255; + } + + return alphaBlend(alpha, fgc, bgc); +} + +/*************************************************************************************** +** Function name: alphaBlend +** Description: Blend 24bit foreground and background with optional dither +*************************************************************************************x*/ +uint32_t TFT_eSPI::alphaBlend24(uint8_t alpha, uint32_t fgc, uint32_t bgc, uint8_t dither) +{ + + if (dither) { + int16_t alphaDither = (int16_t)alpha - dither + random(2*dither+1); // +/-dither randomised + alpha = (uint8_t)alphaDither; + if (alphaDither < 0) alpha = 0; + if (alphaDither >255) alpha = 255; + } + + uint32_t rxx = bgc & 0xFF0000; + rxx += ((fgc & 0xFF0000) - rxx) * alpha >> 8; + uint32_t xgx = bgc & 0x00FF00; + xgx += ((fgc & 0x00FF00) - xgx) * alpha >> 8; + uint32_t xxb = bgc & 0x0000FF; + xxb += ((fgc & 0x0000FF) - xxb) * alpha >> 8; + return (rxx & 0xFF0000) | (xgx & 0x00FF00) | (xxb & 0x0000FF); +} + +/*************************************************************************************** +** Function name: write +** Description: draw characters piped through serial stream +***************************************************************************************/ +/* // Not all processors support buffered write +#ifndef ARDUINO_ARCH_ESP8266 // Avoid ESP8266 board package bug +size_t TFT_eSPI::write(const uint8_t *buf, size_t len) +{ + inTransaction = true; + + uint8_t *lbuf = (uint8_t *)buf; + while(*lbuf !=0 && len--) write(*lbuf++); + + inTransaction = lockTransaction; + end_tft_write(); + return 1; +} +#endif +*/ +/*************************************************************************************** +** Function name: write +** Description: draw characters piped through serial stream +***************************************************************************************/ +size_t TFT_eSPI::write(uint8_t utf8) +{ + if (_vpOoB) return 1; + + uint16_t uniCode = decodeUTF8(utf8); + + if (!uniCode) return 1; + + if (utf8 == '\r') return 1; + +#ifdef SMOOTH_FONT + if(fontLoaded) { + if (uniCode < 32 && utf8 != '\n') return 1; + + drawGlyph(uniCode); + + return 1; + } +#endif + + if (uniCode == '\n') uniCode+=22; // Make it a valid space character to stop errors + + uint16_t cwidth = 0; + uint16_t cheight = 0; + +//vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv DEBUG vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv + //Serial.print((uint8_t) uniCode); // Debug line sends all printed TFT text to serial port + //Serial.println(uniCode, HEX); // Debug line sends all printed TFT text to serial port + //delay(5); // Debug optional wait for serial port to flush through +//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ DEBUG ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< +#ifdef LOAD_GFXFF + if(!gfxFont) { +#endif +//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + +#ifdef LOAD_FONT2 + if (textfont == 2) { + if (uniCode < 32 || uniCode > 127) return 1; + + cwidth = pgm_read_byte(widtbl_f16 + uniCode-32); + cheight = chr_hgt_f16; + // Font 2 is rendered in whole byte widths so we must allow for this + cwidth = (cwidth + 6) / 8; // Width in whole bytes for font 2, should be + 7 but must allow for font width change + cwidth = cwidth * 8; // Width converted back to pixels + } + #ifdef LOAD_RLE + else + #endif +#endif + +#ifdef LOAD_RLE + { + if ((textfont>2) && (textfont<9)) { + if (uniCode < 32 || uniCode > 127) return 1; + // Uses the fontinfo struct array to avoid lots of 'if' or 'switch' statements + cwidth = pgm_read_byte( (uint8_t *)pgm_read_dword( &(fontdata[textfont].widthtbl ) ) + uniCode-32 ); + cheight= pgm_read_byte( &fontdata[textfont].height ); + } + } +#endif + +#ifdef LOAD_GLCD + if (textfont==1) { + cwidth = 6; + cheight = 8; + } +#else + if (textfont==1) return 1; +#endif + + cheight = cheight * textsize; + + if (utf8 == '\n') { + cursor_y += cheight; + cursor_x = 0; + } + else { + if (textwrapX && (cursor_x + cwidth * textsize > width())) { + cursor_y += cheight; + cursor_x = 0; + } + if (textwrapY && (cursor_y >= (int32_t) height())) cursor_y = 0; + cursor_x += drawChar(uniCode, cursor_x, cursor_y, textfont); + } + +//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< +#ifdef LOAD_GFXFF + } // Custom GFX font + else { + if(utf8 == '\n') { + cursor_x = 0; + cursor_y += (int16_t)textsize * (uint8_t)pgm_read_byte(&gfxFont->yAdvance); + } else { + if (uniCode > pgm_read_word(&gfxFont->last )) return 1; + if (uniCode < pgm_read_word(&gfxFont->first)) return 1; + + uint16_t c2 = uniCode - pgm_read_word(&gfxFont->first); + GFXglyph *glyph = &(((GFXglyph *)pgm_read_dword(&gfxFont->glyph))[c2]); + uint8_t w = pgm_read_byte(&glyph->width), + h = pgm_read_byte(&glyph->height); + if((w > 0) && (h > 0)) { // Is there an associated bitmap? + int16_t xo = (int8_t)pgm_read_byte(&glyph->xOffset); + if(textwrapX && ((cursor_x + textsize * (xo + w)) > width())) { + // Drawing character would go off right edge; wrap to new line + cursor_x = 0; + cursor_y += (int16_t)textsize * (uint8_t)pgm_read_byte(&gfxFont->yAdvance); + } + if (textwrapY && (cursor_y >= (int32_t) height())) cursor_y = 0; + drawChar(cursor_x, cursor_y, uniCode, textcolor, textbgcolor, textsize); + } + cursor_x += pgm_read_byte(&glyph->xAdvance) * (int16_t)textsize; + } + } +#endif // LOAD_GFXFF +//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + + return 1; +} + + +/*************************************************************************************** +** Function name: drawChar +** Description: draw a Unicode glyph onto the screen +***************************************************************************************/ + // TODO: Rationalise with TFT_eSprite + // Any UTF-8 decoding must be done before calling drawChar() +int16_t TFT_eSPI::drawChar(uint16_t uniCode, int32_t x, int32_t y) +{ + return drawChar(uniCode, x, y, textfont); +} + + // Any UTF-8 decoding must be done before calling drawChar() +int16_t TFT_eSPI::drawChar(uint16_t uniCode, int32_t x, int32_t y, uint8_t font) +{ + if (_vpOoB || !uniCode) return 0; + + if (font==1) { +#ifdef LOAD_GLCD + #ifndef LOAD_GFXFF + drawChar(x, y, uniCode, textcolor, textbgcolor, textsize); + return 6 * textsize; + #endif +#else + #ifndef LOAD_GFXFF + return 0; + #endif +#endif + +#ifdef LOAD_GFXFF + drawChar(x, y, uniCode, textcolor, textbgcolor, textsize); + if(!gfxFont) { // 'Classic' built-in font + #ifdef LOAD_GLCD + return 6 * textsize; + #else + return 0; + #endif + } + else { + if((uniCode >= pgm_read_word(&gfxFont->first)) && (uniCode <= pgm_read_word(&gfxFont->last) )) { + uint16_t c2 = uniCode - pgm_read_word(&gfxFont->first); + GFXglyph *glyph = &(((GFXglyph *)pgm_read_dword(&gfxFont->glyph))[c2]); + return pgm_read_byte(&glyph->xAdvance) * textsize; + } + else { + return 0; + } + } +#endif + } + + if ((font>1) && (font<9) && ((uniCode < 32) || (uniCode > 127))) return 0; + + int32_t width = 0; + int32_t height = 0; + uint32_t flash_address = 0; + uniCode -= 32; + +#ifdef LOAD_FONT2 + if (font == 2) { + flash_address = pgm_read_dword(&chrtbl_f16[uniCode]); + width = pgm_read_byte(widtbl_f16 + uniCode); + height = chr_hgt_f16; + } + #ifdef LOAD_RLE + else + #endif +#endif + +#ifdef LOAD_RLE + { + if ((font>2) && (font<9)) { + flash_address = pgm_read_dword( (const void*)(pgm_read_dword( &(fontdata[font].chartbl ) ) + uniCode*sizeof(void *)) ); + width = pgm_read_byte( (uint8_t *)pgm_read_dword( &(fontdata[font].widthtbl ) ) + uniCode ); + height= pgm_read_byte( &fontdata[font].height ); + } + } +#endif + + int32_t xd = x + _xDatum; + int32_t yd = y + _yDatum; + + if ((xd + width * textsize < _vpX || xd >= _vpW) && (yd + height * textsize < _vpY || yd >= _vpH)) return width * textsize ; + + int32_t w = width; + int32_t pX = 0; + int32_t pY = y; + uint8_t line = 0; + bool clip = xd < _vpX || xd + width * textsize >= _vpW || yd < _vpY || yd + height * textsize >= _vpH; + +#ifdef LOAD_FONT2 // chop out code if we do not need it + if (font == 2) { + w = w + 6; // Should be + 7 but we need to compensate for width increment + w = w / 8; + + if (textcolor == textbgcolor || textsize != 1 || clip) { + //begin_tft_write(); // Sprite class can use this function, avoiding begin_tft_write() + inTransaction = true; + + for (int32_t i = 0; i < height; i++) { + if (textcolor != textbgcolor) fillRect(x, pY, width * textsize, textsize, textbgcolor); + + for (int32_t k = 0; k < w; k++) { + line = pgm_read_byte((uint8_t *)flash_address + w * i + k); + if (line) { + if (textsize == 1) { + pX = x + k * 8; + if (line & 0x80) drawPixel(pX, pY, textcolor); + if (line & 0x40) drawPixel(pX + 1, pY, textcolor); + if (line & 0x20) drawPixel(pX + 2, pY, textcolor); + if (line & 0x10) drawPixel(pX + 3, pY, textcolor); + if (line & 0x08) drawPixel(pX + 4, pY, textcolor); + if (line & 0x04) drawPixel(pX + 5, pY, textcolor); + if (line & 0x02) drawPixel(pX + 6, pY, textcolor); + if (line & 0x01) drawPixel(pX + 7, pY, textcolor); + } + else { + pX = x + k * 8 * textsize; + if (line & 0x80) fillRect(pX, pY, textsize, textsize, textcolor); + if (line & 0x40) fillRect(pX + textsize, pY, textsize, textsize, textcolor); + if (line & 0x20) fillRect(pX + 2 * textsize, pY, textsize, textsize, textcolor); + if (line & 0x10) fillRect(pX + 3 * textsize, pY, textsize, textsize, textcolor); + if (line & 0x08) fillRect(pX + 4 * textsize, pY, textsize, textsize, textcolor); + if (line & 0x04) fillRect(pX + 5 * textsize, pY, textsize, textsize, textcolor); + if (line & 0x02) fillRect(pX + 6 * textsize, pY, textsize, textsize, textcolor); + if (line & 0x01) fillRect(pX + 7 * textsize, pY, textsize, textsize, textcolor); + } + } + } + pY += textsize; + } + + inTransaction = lockTransaction; + end_tft_write(); + } + else { // Faster drawing of characters and background using block write + + begin_tft_write(); + + setWindow(xd, yd, xd + width - 1, yd + height - 1); + + uint8_t mask; + for (int32_t i = 0; i < height; i++) { + pX = width; + for (int32_t k = 0; k < w; k++) { + line = pgm_read_byte((uint8_t *) (flash_address + w * i + k) ); + mask = 0x80; + while (mask && pX) { + if (line & mask) {tft_Write_16(textcolor);} + else {tft_Write_16(textbgcolor);} + pX--; + mask = mask >> 1; + } + } + if (pX) {tft_Write_16(textbgcolor);} + } + + end_tft_write(); + } + } + + #ifdef LOAD_RLE + else + #endif +#endif //FONT2 + +#ifdef LOAD_RLE //674 bytes of code + // Font is not 2 and hence is RLE encoded + { + begin_tft_write(); + inTransaction = true; + + w *= height; // Now w is total number of pixels in the character + if (textcolor == textbgcolor && !clip) { + + int32_t px = 0, py = pY; // To hold character block start and end column and row values + int32_t pc = 0; // Pixel count + uint8_t np = textsize * textsize; // Number of pixels in a drawn pixel + + uint8_t tnp = 0; // Temporary copy of np for while loop + uint8_t ts = textsize - 1; // Temporary copy of textsize + // 16-bit pixel count so maximum font size is equivalent to 180x180 pixels in area + // w is total number of pixels to plot to fill character block + while (pc < w) { + line = pgm_read_byte((uint8_t *)flash_address); + flash_address++; + if (line & 0x80) { + line &= 0x7F; + line++; + if (ts) { + px = xd + textsize * (pc % width); // Keep these px and py calculations outside the loop as they are slow + py = yd + textsize * (pc / width); + } + else { + px = xd + pc % width; // Keep these px and py calculations outside the loop as they are slow + py = yd + pc / width; + } + while (line--) { // In this case the while(line--) is faster + pc++; // This is faster than putting pc+=line before while()? + setWindow(px, py, px + ts, py + ts); + + if (ts) { + tnp = np; + while (tnp--) {tft_Write_16(textcolor);} + } + else {tft_Write_16(textcolor);} + px += textsize; + + if (px >= (xd + width * textsize)) { + px = xd; + py += textsize; + } + } + } + else { + line++; + pc += line; + } + } + } + else { + // Text colour != background and textsize = 1 and character is within viewport area + // so use faster drawing of characters and background using block write + if (textcolor != textbgcolor && textsize == 1 && !clip) + { + setWindow(xd, yd, xd + width - 1, yd + height - 1); + + // Maximum font size is equivalent to 180x180 pixels in area + while (w > 0) { + line = pgm_read_byte((uint8_t *)flash_address++); // 8 bytes smaller when incrementing here + if (line & 0x80) { + line &= 0x7F; + line++; w -= line; + pushBlock(textcolor,line); + } + else { + line++; w -= line; + pushBlock(textbgcolor,line); + } + } + } + else + { + int32_t px = 0, py = 0; // To hold character pixel coords + int32_t tx = 0, ty = 0; // To hold character TFT pixel coords + int32_t pc = 0; // Pixel count + int32_t pl = 0; // Pixel line length + uint16_t pcol = 0; // Pixel color + bool pf = true; // Flag for plotting + while (pc < w) { + line = pgm_read_byte((uint8_t *)flash_address); + flash_address++; + if (line & 0x80) { pcol = textcolor; line &= 0x7F; pf = true;} + else { pcol = textbgcolor; if (textcolor == textbgcolor) pf = false;} + line++; + px = pc % width; + tx = x + textsize * px; + py = pc / width; + ty = y + textsize * py; + + pl = 0; + pc += line; + while (line--) { + pl++; + if ((px+pl) >= width) { + if (pf) fillRect(tx, ty, pl * textsize, textsize, pcol); + pl = 0; + px = 0; + tx = x; + py ++; + ty += textsize; + } + } + if (pl && pf) fillRect(tx, ty, pl * textsize, textsize, pcol); + } + } + } + inTransaction = lockTransaction; + end_tft_write(); + } + // End of RLE font rendering +#endif + +#if !defined (LOAD_FONT2) && !defined (LOAD_RLE) + // Stop warnings + flash_address = flash_address; + w = w; + pX = pX; + pY = pY; + line = line; + clip = clip; +#endif + + return width * textsize; // x + +} + + +/*************************************************************************************** +** Function name: drawString (with or without user defined font) +** Description : draw string with padding if it is defined +***************************************************************************************/ +// Without font number, uses font set by setTextFont() +int16_t TFT_eSPI::drawString(const String& string, int32_t poX, int32_t poY) +{ + int16_t len = string.length() + 2; + char buffer[len]; + string.toCharArray(buffer, len); + return drawString(buffer, poX, poY, textfont); +} +// With font number +int16_t TFT_eSPI::drawString(const String& string, int32_t poX, int32_t poY, uint8_t font) +{ + int16_t len = string.length() + 2; + char buffer[len]; + string.toCharArray(buffer, len); + return drawString(buffer, poX, poY, font); +} + +// Without font number, uses font set by setTextFont() +int16_t TFT_eSPI::drawString(const char *string, int32_t poX, int32_t poY) +{ + return drawString(string, poX, poY, textfont); +} + +// With font number. Note: font number is over-ridden if a smooth font is loaded +int16_t TFT_eSPI::drawString(const char *string, int32_t poX, int32_t poY, uint8_t font) +{ + if (font > 8) return 0; + + int16_t sumX = 0; + uint8_t padding = 1, baseline = 0; + uint16_t cwidth = textWidth(string, font); // Find the pixel width of the string in the font + uint16_t cheight = 8 * textsize; + +#ifdef LOAD_GFXFF + #ifdef SMOOTH_FONT + bool freeFont = (font == 1 && gfxFont && !fontLoaded); + #else + bool freeFont = (font == 1 && gfxFont); + #endif + + if (freeFont) { + cheight = glyph_ab * textsize; + poY += cheight; // Adjust for baseline datum of free fonts + baseline = cheight; + padding =101; // Different padding method used for Free Fonts + + // We need to make an adjustment for the bottom of the string (eg 'y' character) + if ((textdatum == BL_DATUM) || (textdatum == BC_DATUM) || (textdatum == BR_DATUM)) { + cheight += glyph_bb * textsize; + } + } +#endif + + + // If it is not font 1 (GLCD or free font) get the baseline and pixel height of the font +#ifdef SMOOTH_FONT + if(fontLoaded) { + baseline = gFont.maxAscent; + cheight = fontHeight(); + } + else +#endif + if (font!=1) { + baseline = pgm_read_byte( &fontdata[font].baseline ) * textsize; + cheight = fontHeight(font); + } + + if (textdatum || padX) { + + switch(textdatum) { + case TC_DATUM: + poX -= cwidth/2; + padding += 1; + break; + case TR_DATUM: + poX -= cwidth; + padding += 2; + break; + case ML_DATUM: + poY -= cheight/2; + //padding += 0; + break; + case MC_DATUM: + poX -= cwidth/2; + poY -= cheight/2; + padding += 1; + break; + case MR_DATUM: + poX -= cwidth; + poY -= cheight/2; + padding += 2; + break; + case BL_DATUM: + poY -= cheight; + //padding += 0; + break; + case BC_DATUM: + poX -= cwidth/2; + poY -= cheight; + padding += 1; + break; + case BR_DATUM: + poX -= cwidth; + poY -= cheight; + padding += 2; + break; + case L_BASELINE: + poY -= baseline; + //padding += 0; + break; + case C_BASELINE: + poX -= cwidth/2; + poY -= baseline; + padding += 1; + break; + case R_BASELINE: + poX -= cwidth; + poY -= baseline; + padding += 2; + break; + } + } + + + int8_t xo = 0; +#ifdef LOAD_GFXFF + if (freeFont && (textcolor!=textbgcolor)) { + cheight = (glyph_ab + glyph_bb) * textsize; + // Get the offset for the first character only to allow for negative offsets + uint16_t c2 = 0; + uint16_t len = strlen(string); + uint16_t n = 0; + + while (n < len && c2 == 0) c2 = decodeUTF8((uint8_t*)string, &n, len - n); + + if((c2 >= pgm_read_word(&gfxFont->first)) && (c2 <= pgm_read_word(&gfxFont->last) )) { + c2 -= pgm_read_word(&gfxFont->first); + GFXglyph *glyph = &(((GFXglyph *)pgm_read_dword(&gfxFont->glyph))[c2]); + xo = pgm_read_byte(&glyph->xOffset) * textsize; + // Adjust for negative xOffset + if (xo > 0) xo = 0; + else cwidth -= xo; + // Add 1 pixel of padding all round + //cheight +=2; + //fillRect(poX+xo-1, poY - 1 - glyph_ab * textsize, cwidth+2, cheight, textbgcolor); + fillRect(poX+xo, poY - glyph_ab * textsize, cwidth, cheight, textbgcolor); + } + padding -=100; + } +#endif + + uint16_t len = strlen(string); + uint16_t n = 0; + +#ifdef SMOOTH_FONT + if(fontLoaded) { + setCursor(poX, poY); + + bool fillbg = _fillbg; + // If padding is requested then fill the text background + if (padX && !_fillbg) _fillbg = true; + + while (n < len) { + uint16_t uniCode = decodeUTF8((uint8_t*)string, &n, len - n); + drawGlyph(uniCode); + } + _fillbg = fillbg; // restore state + sumX += cwidth; + //fontFile.close(); + } + else +#endif + { + while (n < len) { + uint16_t uniCode = decodeUTF8((uint8_t*)string, &n, len - n); + sumX += drawChar(uniCode, poX+sumX, poY, font); + } + } + +//vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv DEBUG vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// Switch on debugging for the padding areas +//#define PADDING_DEBUG + +#ifndef PADDING_DEBUG +//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ DEBUG ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + if((padX>cwidth) && (textcolor!=textbgcolor)) { + int16_t padXc = poX+cwidth+xo; +#ifdef LOAD_GFXFF + if (freeFont) { + poX +=xo; // Adjust for negative offset start character + poY -= glyph_ab * textsize; + sumX += poX; + } +#endif + switch(padding) { + case 1: + fillRect(padXc,poY,padX-cwidth,cheight, textbgcolor); + break; + case 2: + fillRect(padXc,poY,(padX-cwidth)>>1,cheight, textbgcolor); + padXc = poX - ((padX-cwidth)>>1); + fillRect(padXc,poY,(padX-cwidth)>>1,cheight, textbgcolor); + break; + case 3: + if (padXc>padX) padXc = padX; + fillRect(poX + cwidth - padXc,poY,padXc-cwidth,cheight, textbgcolor); + break; + } + } + + +#else + +//vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv DEBUG vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// This is debug code to show text (green box) and blanked (white box) areas +// It shows that the padding areas are being correctly sized and positioned + + if((padX>sumX) && (textcolor!=textbgcolor)) { + int16_t padXc = poX+sumX; // Maximum left side padding +#ifdef LOAD_GFXFF + if ((font == 1) && (gfxFont)) poY -= glyph_ab; +#endif + drawRect(poX,poY,sumX,cheight, TFT_GREEN); + switch(padding) { + case 1: + drawRect(padXc,poY,padX-sumX,cheight, TFT_WHITE); + break; + case 2: + drawRect(padXc,poY,(padX-sumX)>>1, cheight, TFT_WHITE); + padXc = (padX-sumX)>>1; + drawRect(poX - padXc,poY,(padX-sumX)>>1,cheight, TFT_WHITE); + break; + case 3: + if (padXc>padX) padXc = padX; + drawRect(poX + sumX - padXc,poY,padXc-sumX,cheight, TFT_WHITE); + break; + } + } +#endif +//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ DEBUG ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +return sumX; +} + + +/*************************************************************************************** +** Function name: drawCentreString (deprecated, use setTextDatum()) +** Descriptions: draw string centred on dX +***************************************************************************************/ +int16_t TFT_eSPI::drawCentreString(const String& string, int32_t dX, int32_t poY, uint8_t font) +{ + int16_t len = string.length() + 2; + char buffer[len]; + string.toCharArray(buffer, len); + return drawCentreString(buffer, dX, poY, font); +} + +int16_t TFT_eSPI::drawCentreString(const char *string, int32_t dX, int32_t poY, uint8_t font) +{ + uint8_t tempdatum = textdatum; + int32_t sumX = 0; + textdatum = TC_DATUM; + sumX = drawString(string, dX, poY, font); + textdatum = tempdatum; + return sumX; +} + + +/*************************************************************************************** +** Function name: drawRightString (deprecated, use setTextDatum()) +** Descriptions: draw string right justified to dX +***************************************************************************************/ +int16_t TFT_eSPI::drawRightString(const String& string, int32_t dX, int32_t poY, uint8_t font) +{ + int16_t len = string.length() + 2; + char buffer[len]; + string.toCharArray(buffer, len); + return drawRightString(buffer, dX, poY, font); +} + +int16_t TFT_eSPI::drawRightString(const char *string, int32_t dX, int32_t poY, uint8_t font) +{ + uint8_t tempdatum = textdatum; + int16_t sumX = 0; + textdatum = TR_DATUM; + sumX = drawString(string, dX, poY, font); + textdatum = tempdatum; + return sumX; +} + + +/*************************************************************************************** +** Function name: drawNumber +** Description: draw a long integer +***************************************************************************************/ +int16_t TFT_eSPI::drawNumber(long long_num, int32_t poX, int32_t poY) +{ + isDigits = true; // Eliminate jiggle in monospaced fonts + char str[12]; + ltoa(long_num, str, 10); + return drawString(str, poX, poY, textfont); +} + +int16_t TFT_eSPI::drawNumber(long long_num, int32_t poX, int32_t poY, uint8_t font) +{ + isDigits = true; // Eliminate jiggle in monospaced fonts + char str[12]; + ltoa(long_num, str, 10); + return drawString(str, poX, poY, font); +} + + +/*************************************************************************************** +** Function name: drawFloat +** Descriptions: drawFloat, prints 7 non zero digits maximum +***************************************************************************************/ +// Assemble and print a string, this permits alignment relative to a datum +// looks complicated but much more compact and actually faster than using print class +int16_t TFT_eSPI::drawFloat(float floatNumber, uint8_t dp, int32_t poX, int32_t poY) +{ + return drawFloat(floatNumber, dp, poX, poY, textfont); +} + +int16_t TFT_eSPI::drawFloat(float floatNumber, uint8_t dp, int32_t poX, int32_t poY, uint8_t font) +{ + isDigits = true; + char str[14]; // Array to contain decimal string + uint8_t ptr = 0; // Initialise pointer for array + int8_t digits = 1; // Count the digits to avoid array overflow + float rounding = 0.5; // Round up down delta + bool negative = false; + + if (dp > 7) dp = 7; // Limit the size of decimal portion + + // Adjust the rounding value + for (uint8_t i = 0; i < dp; ++i) rounding /= 10.0; + + if (floatNumber < -rounding) { // add sign, avoid adding - sign to 0.0! + str[ptr++] = '-'; // Negative number + str[ptr] = 0; // Put a null in the array as a precaution + digits = 0; // Set digits to 0 to compensate so pointer value can be used later + floatNumber = -floatNumber; // Make positive + negative = true; + } + + floatNumber += rounding; // Round up or down + + if (dp == 0) { + if (negative) floatNumber = -floatNumber; + return drawNumber((long)floatNumber, poX, poY, font); + } + + // For error put ... in string and return (all TFT_eSPI library fonts contain . character) + if (floatNumber >= 2147483647) { + strcpy(str, "..."); + return drawString(str, poX, poY, font); + } + // No chance of overflow from here on + + // Get integer part + uint32_t temp = (uint32_t)floatNumber; + + // Put integer part into array + ltoa(temp, str + ptr, 10); + + // Find out where the null is to get the digit count loaded + while ((uint8_t)str[ptr] != 0) ptr++; // Move the pointer along + digits += ptr; // Count the digits + + str[ptr++] = '.'; // Add decimal point + str[ptr] = '0'; // Add a dummy zero + str[ptr + 1] = 0; // Add a null but don't increment pointer so it can be overwritten + + // Get the decimal portion + floatNumber = floatNumber - temp; + + // Get decimal digits one by one and put in array + // Limit digit count so we don't get a false sense of resolution + uint8_t i = 0; + while ((i < dp) && (digits < 9)) { // while (i < dp) for no limit but array size must be increased + i++; + floatNumber *= 10; // for the next decimal + temp = floatNumber; // get the decimal + ltoa(temp, str + ptr, 10); + ptr++; digits++; // Increment pointer and digits count + floatNumber -= temp; // Remove that digit + } + + // Finally we can plot the string and return pixel length + return drawString(str, poX, poY, font); +} + + +/*************************************************************************************** +** Function name: setFreeFont +** Descriptions: Sets the GFX free font to use +***************************************************************************************/ + +#ifdef LOAD_GFXFF + +void TFT_eSPI::setFreeFont(const GFXfont *f) +{ + if (f == nullptr) { // Fix issue #400 (ESP32 crash) + setTextFont(1); // Use GLCD font + return; + } + + textfont = 1; + gfxFont = (GFXfont *)f; + + glyph_ab = 0; + glyph_bb = 0; + uint16_t numChars = pgm_read_word(&gfxFont->last) - pgm_read_word(&gfxFont->first); + + // Find the biggest above and below baseline offsets + for (uint16_t c = 0; c < numChars; c++) { + GFXglyph *glyph1 = &(((GFXglyph *)pgm_read_dword(&gfxFont->glyph))[c]); + int8_t ab = -pgm_read_byte(&glyph1->yOffset); + if (ab > glyph_ab) glyph_ab = ab; + int8_t bb = pgm_read_byte(&glyph1->height) - ab; + if (bb > glyph_bb) glyph_bb = bb; + } +} + + +/*************************************************************************************** +** Function name: setTextFont +** Description: Set the font for the print stream +***************************************************************************************/ +void TFT_eSPI::setTextFont(uint8_t f) +{ + textfont = (f > 0) ? f : 1; // Don't allow font 0 + textfont = (f > 8) ? 1 : f; // Don't allow font > 8 + gfxFont = NULL; +} + +#else + + +/*************************************************************************************** +** Function name: setFreeFont +** Descriptions: Sets the GFX free font to use +***************************************************************************************/ + +// Alternative to setTextFont() so we don't need two different named functions +void TFT_eSPI::setFreeFont(uint8_t font) +{ + setTextFont(font); +} + + +/*************************************************************************************** +** Function name: setTextFont +** Description: Set the font for the print stream +***************************************************************************************/ +void TFT_eSPI::setTextFont(uint8_t f) +{ + textfont = (f > 0) ? f : 1; // Don't allow font 0 + textfont = (f > 8) ? 1 : f; // Don't allow font > 8 +} +#endif + + +/*************************************************************************************** +** Function name: getSPIinstance +** Description: Get the instance of the SPI class +***************************************************************************************/ +#if !defined (TFT_PARALLEL_8_BIT) && !defined (RP2040_PIO_INTERFACE) +SPIClass& TFT_eSPI::getSPIinstance(void) +{ + return spi; +} +#endif + + +/*************************************************************************************** +** Function name: verifySetupID +** Description: Compare the ID if USER_SETUP_ID defined in user setup file +***************************************************************************************/ +bool TFT_eSPI::verifySetupID(uint32_t id) +{ +#if defined (USER_SETUP_ID) + if (USER_SETUP_ID == id) return true; +#else + id = id; // Avoid warning +#endif + return false; +} + +/*************************************************************************************** +** Function name: getSetup +** Description: Get the setup details for diagnostic and sketch access +***************************************************************************************/ +void TFT_eSPI::getSetup(setup_t &tft_settings) +{ +// tft_settings.version is set in header file + +#if defined (USER_SETUP_INFO) + tft_settings.setup_info = USER_SETUP_INFO; +#else + tft_settings.setup_info = "NA"; +#endif + +#if defined (USER_SETUP_ID) + tft_settings.setup_id = USER_SETUP_ID; +#else + tft_settings.setup_id = 0; +#endif + +#if defined (PROCESSOR_ID) + tft_settings.esp = PROCESSOR_ID; +#else + tft_settings.esp = -1; +#endif + +#if defined (SUPPORT_TRANSACTIONS) + tft_settings.trans = true; +#else + tft_settings.trans = false; +#endif + +#if defined (TFT_PARALLEL_8_BIT) || defined(TFT_PARALLEL_16_BIT) + tft_settings.serial = false; + tft_settings.tft_spi_freq = 0; +#else + tft_settings.serial = true; + tft_settings.tft_spi_freq = SPI_FREQUENCY/100000; + #ifdef SPI_READ_FREQUENCY + tft_settings.tft_rd_freq = SPI_READ_FREQUENCY/100000; + #endif + #ifndef GENERIC_PROCESSOR + #ifdef TFT_SPI_PORT + tft_settings.port = TFT_SPI_PORT; + #else + tft_settings.port = 255; + #endif + #endif + #ifdef RP2040_PIO_SPI + tft_settings.interface = 0x10; + #else + tft_settings.interface = 0x0; + #endif +#endif + +#if defined(TFT_SPI_OVERLAP) + tft_settings.overlap = true; +#else + tft_settings.overlap = false; +#endif + + tft_settings.tft_driver = TFT_DRIVER; + tft_settings.tft_width = _init_width; + tft_settings.tft_height = _init_height; + +#ifdef CGRAM_OFFSET + tft_settings.r0_x_offset = colstart; + tft_settings.r0_y_offset = rowstart; + tft_settings.r1_x_offset = 0; + tft_settings.r1_y_offset = 0; + tft_settings.r2_x_offset = 0; + tft_settings.r2_y_offset = 0; + tft_settings.r3_x_offset = 0; + tft_settings.r3_y_offset = 0; +#else + tft_settings.r0_x_offset = 0; + tft_settings.r0_y_offset = 0; + tft_settings.r1_x_offset = 0; + tft_settings.r1_y_offset = 0; + tft_settings.r2_x_offset = 0; + tft_settings.r2_y_offset = 0; + tft_settings.r3_x_offset = 0; + tft_settings.r3_y_offset = 0; +#endif + +#if defined (TFT_MOSI) + tft_settings.pin_tft_mosi = TFT_MOSI; +#else + tft_settings.pin_tft_mosi = -1; +#endif + +#if defined (TFT_MISO) + tft_settings.pin_tft_miso = TFT_MISO; +#else + tft_settings.pin_tft_miso = -1; +#endif + +#if defined (TFT_SCLK) + tft_settings.pin_tft_clk = TFT_SCLK; +#else + tft_settings.pin_tft_clk = -1; +#endif + +#if defined (TFT_CS) + tft_settings.pin_tft_cs = TFT_CS; +#else + tft_settings.pin_tft_cs = -1; +#endif + +#if defined (TFT_DC) + tft_settings.pin_tft_dc = TFT_DC; +#else + tft_settings.pin_tft_dc = -1; +#endif + +#if defined (TFT_RD) + tft_settings.pin_tft_rd = TFT_RD; +#else + tft_settings.pin_tft_rd = -1; +#endif + +#if defined (TFT_WR) + tft_settings.pin_tft_wr = TFT_WR; +#else + tft_settings.pin_tft_wr = -1; +#endif + +#if defined (TFT_RST) + tft_settings.pin_tft_rst = TFT_RST; +#else + tft_settings.pin_tft_rst = -1; +#endif + +#if defined (TFT_PARALLEL_8_BIT) || defined(TFT_PARALLEL_16_BIT) + tft_settings.pin_tft_d0 = TFT_D0; + tft_settings.pin_tft_d1 = TFT_D1; + tft_settings.pin_tft_d2 = TFT_D2; + tft_settings.pin_tft_d3 = TFT_D3; + tft_settings.pin_tft_d4 = TFT_D4; + tft_settings.pin_tft_d5 = TFT_D5; + tft_settings.pin_tft_d6 = TFT_D6; + tft_settings.pin_tft_d7 = TFT_D7; +#else + tft_settings.pin_tft_d0 = -1; + tft_settings.pin_tft_d1 = -1; + tft_settings.pin_tft_d2 = -1; + tft_settings.pin_tft_d3 = -1; + tft_settings.pin_tft_d4 = -1; + tft_settings.pin_tft_d5 = -1; + tft_settings.pin_tft_d6 = -1; + tft_settings.pin_tft_d7 = -1; +#endif + +#if defined (TFT_BL) + tft_settings.pin_tft_led = TFT_BL; +#endif + +#if defined (TFT_BACKLIGHT_ON) + tft_settings.pin_tft_led_on = TFT_BACKLIGHT_ON; +#endif + +#if defined (TOUCH_CS) + tft_settings.pin_tch_cs = TOUCH_CS; + tft_settings.tch_spi_freq = SPI_TOUCH_FREQUENCY/100000; +#else + tft_settings.pin_tch_cs = -1; + tft_settings.tch_spi_freq = 0; +#endif +} + + +//////////////////////////////////////////////////////////////////////////////////////// +#ifdef TOUCH_CS + #include "Extensions/Touch.cpp" +#endif +#ifdef TCON_ENABLE + #include "Extensions/Tcon.cpp" +#endif + + +#include "Extensions/Button.cpp" + +#include "Extensions/Sprite.cpp" + +#ifdef SMOOTH_FONT + #include "Extensions/Smooth_font.cpp" +#endif + +#ifdef AA_GRAPHICS + #include "Extensions/AA_graphics.cpp" // Loaded if SMOOTH_FONT is defined by user +#endif + +#include "Touch_Drivers/Touch.cpp" + + +#ifdef EPAPER_ENABLE +#include "Extensions/EPaper.cpp" + +#endif + +//////////////////////////////////////////////////////////////////////////////////////// + diff --git a/lib/Seeed_GFX/TFT_eSPI.h b/lib/Seeed_GFX/TFT_eSPI.h new file mode 100644 index 0000000..2ac9f4f --- /dev/null +++ b/lib/Seeed_GFX/TFT_eSPI.h @@ -0,0 +1,1129 @@ +/*************************************************** + Arduino TFT graphics library targeted at ESP8266 + and ESP32 based boards. + + This is a stand-alone library that contains the + hardware driver, the graphics functions and the + proportional fonts. + + The built-in fonts 4, 6, 7 and 8 are Run Length + Encoded (RLE) to reduce the FLASH footprint. + + Last review/edit by Bodmer: 04/02/22 + ****************************************************/ + +// Stop fonts etc. being loaded multiple times +#ifndef _TFT_eSPIH_ +#define _TFT_eSPIH_ + +#define TFT_ESPI_VERSION "2.5.43" + +// Bit level feature flags +// Bit 0 set: viewport capability +#define TFT_ESPI_FEATURES 1 + +/*************************************************************************************** +** Section 1: Load required header files +***************************************************************************************/ + +// Standard support +#include +#include +#if !defined(TFT_PARALLEL_8_BIT) && !defined(RP2040_PIO_INTERFACE) +#include +#endif +/*************************************************************************************** +** Section 2: Load library and processor specific header files +***************************************************************************************/ +// Include header file that defines the fonts loaded, the TFT drivers +// available and the pins to be used, etc. etc. +#ifdef CONFIG_TFT_eSPI_ESPIDF +#include "TFT_config.h" +#endif + +// New ESP8266 board package uses ARDUINO_ARCH_ESP8266 +// old package defined ESP8266 +#if defined(ESP8266) +#ifndef ARDUINO_ARCH_ESP8266 +#define ARDUINO_ARCH_ESP8266 +#endif +#endif + +// The following lines allow the user setup to be included in the sketch folder, see +// "Sketch_with_tft_setup" generic example. +#if !defined __has_include +#if !defined(DISABLE_ALL_LIBRARY_WARNINGS) +#warning Compiler does not support __has_include, so sketches cannot define the setup +#endif +#else +#if __has_include() +// Include the sketch setup file +#include +#ifndef USER_SETUP_LOADED +// Prevent loading further setups +#define USER_SETUP_LOADED +#endif +#endif +#endif + +#include + +// Handle FLASH based storage e.g. PROGMEM +#if defined(ARDUINO_ARCH_RP2040) +#undef pgm_read_byte +#define pgm_read_byte(addr) (*(const unsigned char *)(addr)) +#undef pgm_read_word +#define pgm_read_word(addr) ({ \ + typeof(addr) _addr = (addr); \ + *(const unsigned short *)(_addr); \ +}) +#undef pgm_read_dword +#define pgm_read_dword(addr) ({ \ + typeof(addr) _addr = (addr); \ + *(const unsigned long *)(_addr); \ +}) +#elif defined(__AVR__) +#include +#elif defined(ARDUINO_ARCH_ESP8266) || defined(ESP32) +#include +#else +#ifndef PROGMEM +#define PROGMEM +#endif +#endif + +// Include the processor specific drivers +#if defined(CONFIG_IDF_TARGET_ESP32S3) +#include "Processors/TFT_eSPI_ESP32_S3.h" +#elif defined(CONFIG_IDF_TARGET_ESP32C5) +// ESP32-C5: use a target-specific SPIClass-based backend with batched pixel writes +#include "Processors/TFT_eSPI_ESP32_C5.h" +#elif defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32C6) +#include "Processors/TFT_eSPI_ESP32_C3.h" +#elif defined(ESP32) +#include "Processors/TFT_eSPI_ESP32.h" +#elif defined(ARDUINO_ARCH_ESP8266) +#include "Processors/TFT_eSPI_ESP8266.h" +#elif defined(STM32) +#include "Processors/TFT_eSPI_STM32.h" +#elif defined(ARDUINO_ARCH_RP2040) +#include "Processors/TFT_eSPI_RP2040.h" +#elif defined(SEEED_XIAO_M0) + #include "Processors/TFT_eSPI_SAMD21.h" +#elif defined(ARDUINO_XIAO_RA4M1) + #include "Processors/TFT_eSPI_RA4M1.h" +#elif defined(NRF52840_XXAA) + #include "Processors/TFT_eSPI_nRF52840.h" +#elif defined (EFR32MG24B220F1536IM48) + #include "Processors/TFT_eSPI_MG24.h" +#else +#include "Processors/TFT_eSPI_Generic.h" +#define GENERIC_PROCESSOR +#endif + +/*************************************************************************************** +** Section 3: Interface setup +***************************************************************************************/ +#ifndef TAB_COLOUR +#define TAB_COLOUR 0 +#endif + +// If the SPI frequency is not defined, set a default +#ifndef SPI_FREQUENCY +#define SPI_FREQUENCY 20000000 +#endif + +// If the SPI read frequency is not defined, set a default +#ifndef SPI_READ_FREQUENCY +#define SPI_READ_FREQUENCY 10000000 +#endif + +// Some ST7789 boards do not work with Mode 0 +#ifndef TFT_SPI_MODE +#if defined(ST7789_DRIVER) || defined(ST7789_2_DRIVER) +#define TFT_SPI_MODE SPI_MODE3 +#else +#define TFT_SPI_MODE SPI_MODE0 +#endif +#endif + +// If the XPT2046 SPI frequency is not defined, set a default +#ifndef SPI_TOUCH_FREQUENCY +#define SPI_TOUCH_FREQUENCY 2500000 +#endif + +#ifndef SPI_BUSY_CHECK +#define SPI_BUSY_CHECK +#endif + +// If half duplex SDA mode is defined then MISO pin should be -1 +#ifdef TFT_SDA_READ +#ifdef TFT_MISO +#if TFT_MISO != -1 +#undef TFT_MISO +#define TFT_MISO -1 +#warning TFT_MISO set to -1 +#endif +#endif +#endif + +/*************************************************************************************** +** Section 4: Setup fonts +***************************************************************************************/ +// Use GLCD font in error case where user requests a smooth font file +// that does not exist (this is a temporary fix to stop ESP32 reboot) +#ifdef SMOOTH_FONT +#ifndef LOAD_GLCD +#define LOAD_GLCD +#endif +#endif + +// Only load the fonts defined in User_Setup.h (to save space) +// Set flag so RLE rendering code is optionally compiled +#ifdef LOAD_GLCD +#include +#endif + +#ifdef LOAD_FONT2 +#include +#endif + +#ifdef LOAD_FONT4 +#include +#define LOAD_RLE +#endif + +#ifdef LOAD_FONT6 +#include +#ifndef LOAD_RLE +#define LOAD_RLE +#endif +#endif + +#ifdef LOAD_FONT7 +#include +#ifndef LOAD_RLE +#define LOAD_RLE +#endif +#endif + +#ifdef LOAD_FONT8 +#include +#ifndef LOAD_RLE +#define LOAD_RLE +#endif +#elif defined LOAD_FONT8N // Optional narrower version +#define LOAD_FONT8 +#include +#ifndef LOAD_RLE +#define LOAD_RLE +#endif +#endif + +#ifdef LOAD_GFXFF +// We can include all the free fonts and they will only be built into +// the sketch if they are used +#include +// Call up any user custom fonts +#include +#endif // #ifdef LOAD_GFXFF + +// Create a null default font in case some fonts not used (to prevent crash) +const uint8_t widtbl_null[1] = {0}; +PROGMEM const uint8_t chr_null[1] = {0}; +PROGMEM const uint8_t *const chrtbl_null[1] = {chr_null}; + +// This is a structure to conveniently hold information on the default fonts +// Stores pointer to font character image address table, width table and height +typedef struct +{ + const uint8_t *chartbl; + const uint8_t *widthtbl; + uint8_t height; + uint8_t baseline; +} fontinfo; + +// Now fill the structure +const PROGMEM fontinfo fontdata[] = { +#ifdef LOAD_GLCD + {(const uint8_t *)font, widtbl_null, 0, 0}, +#else + {(const uint8_t *)chrtbl_null, widtbl_null, 0, 0}, +#endif + // GLCD font (Font 1) does not have all parameters + {(const uint8_t *)chrtbl_null, widtbl_null, 8, 7}, + +#ifdef LOAD_FONT2 + {(const uint8_t *)chrtbl_f16, widtbl_f16, chr_hgt_f16, baseline_f16}, +#else + {(const uint8_t *)chrtbl_null, widtbl_null, 0, 0}, +#endif + + // Font 3 current unused + {(const uint8_t *)chrtbl_null, widtbl_null, 0, 0}, + +#ifdef LOAD_FONT4 + {(const uint8_t *)chrtbl_f32, widtbl_f32, chr_hgt_f32, baseline_f32}, +#else + {(const uint8_t *)chrtbl_null, widtbl_null, 0, 0}, +#endif + + // Font 5 current unused + {(const uint8_t *)chrtbl_null, widtbl_null, 0, 0}, + +#ifdef LOAD_FONT6 + {(const uint8_t *)chrtbl_f64, widtbl_f64, chr_hgt_f64, baseline_f64}, +#else + {(const uint8_t *)chrtbl_null, widtbl_null, 0, 0}, +#endif + +#ifdef LOAD_FONT7 + {(const uint8_t *)chrtbl_f7s, widtbl_f7s, chr_hgt_f7s, baseline_f7s}, +#else + {(const uint8_t *)chrtbl_null, widtbl_null, 0, 0}, +#endif + +#ifdef LOAD_FONT8 + {(const uint8_t *)chrtbl_f72, widtbl_f72, chr_hgt_f72, baseline_f72} +#else + {(const uint8_t *)chrtbl_null, widtbl_null, 0, 0} +#endif +}; + +/*************************************************************************************** +** Section 5: Font datum enumeration +***************************************************************************************/ +// These enumerate the text plotting alignment (reference datum point) +#define TL_DATUM 0 // Top left (default) +#define TC_DATUM 1 // Top centre +#define TR_DATUM 2 // Top right +#define ML_DATUM 3 // Middle left +#define CL_DATUM 3 // Centre left, same as above +#define MC_DATUM 4 // Middle centre +#define CC_DATUM 4 // Centre centre, same as above +#define MR_DATUM 5 // Middle right +#define CR_DATUM 5 // Centre right, same as above +#define BL_DATUM 6 // Bottom left +#define BC_DATUM 7 // Bottom centre +#define BR_DATUM 8 // Bottom right +#define L_BASELINE 9 // Left character baseline (Line the 'A' character would sit on) +#define C_BASELINE 10 // Centre character baseline +#define R_BASELINE 11 // Right character baseline + +/*************************************************************************************** +** Section 6: Colour enumeration +***************************************************************************************/ +// Default palette for 6 color epaper +#ifdef USE_COLORFULL_EPAPER + #define TFT_BLACK 0XF + #define TFT_WHITE 0X0 + #define TFT_BLUE 0XD + #define TFT_YELLOW 0XB + #define TFT_GREEN 0X2 + #define TFT_RED 0X6 +//In the 6-color mode, other colors will be mapped to similar ones. + #define TFT_NAVY TFT_BLUE + #define TFT_DARKGREEN TFT_GREEN + #define TFT_DARKCYAN TFT_GREEN + #define TFT_MAROON TFT_RED + #define TFT_PURPLE TFT_BLUE + #define TFT_OLIVE TFT_YELLOW + #define TFT_LIGHTGREY TFT_WHITE + #define TFT_DARKGREY TFT_BLACK + #define TFT_CYAN TFT_GREEN + #define TFT_MAGENTA TFT_RED + #define TFT_ORANGE TFT_YELLOW + #define TFT_GREENYELLOW TFT_GREEN + #define TFT_PINK TFT_RED + #define TFT_BROWN TFT_YELLOW + #define TFT_GOLD TFT_YELLOW + #define TFT_SILVER TFT_WHITE + #define TFT_SKYBLUE TFT_BLUE + #define TFT_VIOLET TFT_BLUE +#elif defined (USE_BWRY_EPAPER) + #define TFT_WHITE 0X00 + #define TFT_YELLOW 0X0B + #define TFT_RED 0X06 + #define TFT_BLACK 0X0F + + #define TFT_BLUE TFT_YELLOW + #define TFT_GREEN TFT_YELLOW + #define TFT_NAVY TFT_BLUE + #define TFT_DARKGREEN TFT_GREEN + #define TFT_DARKCYAN TFT_GREEN + #define TFT_MAROON TFT_RED + #define TFT_PURPLE TFT_BLUE + #define TFT_OLIVE TFT_YELLOW + #define TFT_LIGHTGREY TFT_WHITE + #define TFT_DARKGREY TFT_BLACK + #define TFT_CYAN TFT_GREEN + #define TFT_MAGENTA TFT_RED + #define TFT_ORANGE TFT_YELLOW + #define TFT_GREENYELLOW TFT_GREEN + #define TFT_PINK TFT_RED + #define TFT_BROWN TFT_YELLOW + #define TFT_GOLD TFT_YELLOW + #define TFT_SILVER TFT_WHITE + #define TFT_SKYBLUE TFT_BLUE + #define TFT_VIOLET TFT_BLUE +#else + // Default color definitions + #define TFT_BLACK 0x0000 /* 0, 0, 0 */ + #define TFT_NAVY 0x000F /* 0, 0, 128 */ + #define TFT_DARKGREEN 0x03E0 /* 0, 128, 0 */ + #define TFT_DARKCYAN 0x03EF /* 0, 128, 128 */ + #define TFT_MAROON 0x7800 /* 128, 0, 0 */ + #define TFT_PURPLE 0x780F /* 128, 0, 128 */ + #define TFT_OLIVE 0x7BE0 /* 128, 128, 0 */ + #define TFT_LIGHTGREY 0xD69A /* 211, 211, 211 */ + #define TFT_DARKGREY 0x7BEF /* 128, 128, 128 */ + #define TFT_BLUE 0x001F /* 0, 0, 255 */ + #define TFT_GREEN 0x07E0 /* 0, 255, 0 */ + #define TFT_CYAN 0x07FF /* 0, 255, 255 */ + #define TFT_RED 0xF800 /* 255, 0, 0 */ + #define TFT_MAGENTA 0xF81F /* 255, 0, 255 */ + #define TFT_YELLOW 0xFFE0 /* 255, 255, 0 */ + #define TFT_WHITE 0xFFFF /* 255, 255, 255 */ + #define TFT_ORANGE 0xFDA0 /* 255, 180, 0 */ + #define TFT_GREENYELLOW 0xB7E0 /* 180, 255, 0 */ + #define TFT_PINK 0xFE19 /* 255, 192, 203 */ // Lighter pink, was 0xFC9F + #define TFT_BROWN 0x9A60 /* 150, 75, 0 */ + #define TFT_GOLD 0xFEA0 /* 255, 215, 0 */ + #define TFT_SILVER 0xC618 /* 192, 192, 192 */ + #define TFT_SKYBLUE 0x867D /* 135, 206, 235 */ + #define TFT_VIOLET 0x915C /* 180, 46, 226 */ + #ifdef USE_MUTIGRAY_EPAPER + #ifdef GRAY_LEVEL4 + // #define TFT_BLACK TFT_GRAY_0 + // #define TFT_WHITE TFT_GRAY_3 + #define TFT_GRAY_0 0X00 + #define TFT_GRAY_1 0X01 + #define TFT_GRAY_2 0X02 + #define TFT_GRAY_3 0X03 + #elif defined(GRAY_LEVEL16) + // #define TFT_BLACK TFT_GRAY_0 + // #define TFT_WHITE TFT_GRAY_15 + #define TFT_GRAY_0 0x00 + #define TFT_GRAY_1 0x01 + #define TFT_GRAY_2 0x02 + #define TFT_GRAY_3 0x03 + #define TFT_GRAY_4 0x04 + #define TFT_GRAY_5 0x05 + #define TFT_GRAY_6 0x06 + #define TFT_GRAY_7 0x07 + #define TFT_GRAY_8 0x08 + #define TFT_GRAY_9 0x09 + #define TFT_GRAY_10 0x0A + #define TFT_GRAY_11 0x0B + #define TFT_GRAY_12 0x0C + #define TFT_GRAY_13 0x0D + #define TFT_GRAY_14 0x0E + #define TFT_GRAY_15 0x0F + #endif + + #endif +#endif + + + + + +// Next is a special 16-bit colour value that encodes to 8 bits +// and will then decode back to the same 16-bit value. +// Convenient for 8-bit and 16-bit transparent sprites. +#define TFT_TRANSPARENT 0x0120 // This is actually a dark green + +// Default palette for 4-bit colour sprites +static const uint16_t default_4bit_palette[] PROGMEM = { + TFT_BLACK, // 0 ^ + TFT_BROWN, // 1 | + TFT_RED, // 2 | + TFT_ORANGE, // 3 | + TFT_YELLOW, // 4 Colours 0-9 follow the resistor colour code! + TFT_GREEN, // 5 | + TFT_BLUE, // 6 | + TFT_PURPLE, // 7 | + TFT_DARKGREY, // 8 | + TFT_WHITE, // 9 v + TFT_CYAN, // 10 Blue+green mix + TFT_MAGENTA, // 11 Blue+red mix + TFT_MAROON, // 12 Darker red colour + TFT_DARKGREEN, // 13 Darker green colour + TFT_NAVY, // 14 Darker blue colour + TFT_PINK // 15 +}; + + + +/*************************************************************************************** +** Section 7: Diagnostic support +***************************************************************************************/ +// #define TFT_eSPI_DEBUG // Switch on debug support serial messages (not used yet) +// #define TFT_eSPI_FNx_DEBUG // Switch on debug support for function "x" (not used yet) + +// This structure allows sketches to retrieve the user setup parameters at runtime +// by calling getSetup(), zero impact on code size unless used, mainly for diagnostics +typedef struct +{ + String version = TFT_ESPI_VERSION; + String setup_info; // Setup reference name available to use in a user setup + uint32_t setup_id; // ID available to use in a user setup + int32_t esp; // Processor code + uint8_t trans; // SPI transaction support + uint8_t serial; // Serial (SPI) or parallel +#ifndef GENERIC_PROCESSOR + uint8_t port; // SPI port +#endif + uint8_t overlap; // ESP8266 overlap mode + uint8_t interface; // Interface type + + uint16_t tft_driver; // Hexadecimal code + uint16_t tft_width; // Rotation 0 width and height + uint16_t tft_height; + + uint8_t r0_x_offset; // Display offsets, not all used yet + uint8_t r0_y_offset; + uint8_t r1_x_offset; + uint8_t r1_y_offset; + uint8_t r2_x_offset; + uint8_t r2_y_offset; + uint8_t r3_x_offset; + uint8_t r3_y_offset; + + int8_t pin_tft_mosi; // SPI pins + int8_t pin_tft_miso; + int8_t pin_tft_clk; + int8_t pin_tft_cs; + + int8_t pin_tft_dc; // Control pins + int8_t pin_tft_rd; + int8_t pin_tft_wr; + int8_t pin_tft_rst; + + int8_t pin_tft_d0; // Parallel port pins + int8_t pin_tft_d1; + int8_t pin_tft_d2; + int8_t pin_tft_d3; + int8_t pin_tft_d4; + int8_t pin_tft_d5; + int8_t pin_tft_d6; + int8_t pin_tft_d7; + + int8_t pin_tft_led; + int8_t pin_tft_led_on; + + int8_t pin_tch_cs; // Touch chip select pin + + int16_t tft_spi_freq; // TFT write SPI frequency + int16_t tft_rd_freq; // TFT read SPI frequency + int16_t tch_spi_freq; // Touch controller read/write SPI frequency +} setup_t; + +/*************************************************************************************** +** Section 8: Class member and support functions +***************************************************************************************/ + +// Callback prototype for smooth font pixel colour read +typedef uint16_t (*getColorCallback)(uint16_t x, uint16_t y); + +// Class functions and variables +class TFT_eSPI : public Print +{ + friend class TFT_eSprite; // Sprite class has access to protected members + + //--------------------------------------- public ------------------------------------// +public: + TFT_eSPI(int16_t _W = TFT_WIDTH, int16_t _H = TFT_HEIGHT); + + // init() and begin() are equivalent, begin() included for backwards compatibility + // Sketch defined tab colour option is for ST7735 displays only + void init(uint8_t tc = TAB_COLOUR), initFromSleep(uint8_t tc = TAB_COLOUR), begin(uint8_t tc = TAB_COLOUR) ; + + // These are virtual so the TFT_eSprite class can override them with sprite specific functions + virtual void drawPixel(int32_t x, int32_t y, uint32_t color), + drawChar(int32_t x, int32_t y, uint16_t c, uint32_t color, uint32_t bg, uint8_t size), + drawLine(int32_t xs, int32_t ys, int32_t xe, int32_t ye, uint32_t color), + drawFastVLine(int32_t x, int32_t y, int32_t h, uint32_t color), + drawFastHLine(int32_t x, int32_t y, int32_t w, uint32_t color), + fillRect(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color); + + virtual int16_t drawChar(uint16_t uniCode, int32_t x, int32_t y, uint8_t font), + drawChar(uint16_t uniCode, int32_t x, int32_t y), + height(void), + width(void); + + // Read the colour of a pixel at x,y and return value in 565 format + virtual uint16_t readPixel(int32_t x, int32_t y); + + virtual void setWindow(int32_t xs, int32_t ys, int32_t xe, int32_t ye); // Note: start + end coordinates + + // Push (aka write pixel) colours to the set window + virtual void pushColor(uint16_t color); + + // These are non-inlined to enable override + virtual void begin_nin_write(); + virtual void end_nin_write(); + + void setRotation(uint8_t r); // Set the display image orientation to 0, 1, 2 or 3 + uint8_t getRotation(void); // Read the current rotation + + // Change the origin position from the default top left + // Note: setRotation, setViewport and resetViewport will revert origin to top left corner of screen/sprite + void setOrigin(int32_t x, int32_t y); + int32_t getOriginX(void); + int32_t getOriginY(void); + + void invertDisplay(bool i); // Tell TFT to invert all displayed colours + + // The TFT_eSprite class inherits the following functions (not all are useful to Sprite class + void setAddrWindow(int32_t xs, int32_t ys, int32_t w, int32_t h); // Note: start coordinates + width and height + + // Viewport commands, see "Viewport_Demo" sketch + void setViewport(int32_t x, int32_t y, int32_t w, int32_t h, bool vpDatum = true); + bool checkViewport(int32_t x, int32_t y, int32_t w, int32_t h); + int32_t getViewportX(void); + int32_t getViewportY(void); + int32_t getViewportWidth(void); + int32_t getViewportHeight(void); + bool getViewportDatum(void); + void frameViewport(uint16_t color, int32_t w); + void resetViewport(void); + + // Clip input window to viewport bounds, return false if whole area is out of bounds + bool clipAddrWindow(int32_t *x, int32_t *y, int32_t *w, int32_t *h); + // Clip input window area to viewport bounds, return false if whole area is out of bounds + bool clipWindow(int32_t *xs, int32_t *ys, int32_t *xe, int32_t *ye); + + // Push (aka write pixel) colours to the TFT (use setAddrWindow() first) + void pushColor(uint16_t color, uint32_t len), // Deprecated, use pushBlock() + pushColors(uint16_t *data, uint32_t len, bool swap = true), // With byte swap option + pushColors(uint8_t *data, uint32_t len); // Deprecated, use pushPixels() + + // Write a solid block of a single colour + void pushBlock(uint16_t color, uint32_t len); + + // Write a set of pixels stored in memory, use setSwapBytes(true/false) function to correct endianess + void pushPixels(const void *data_in, uint32_t len); + +// Support for half duplex (bi-directional SDA) SPI bus where MOSI must be switched to input +#ifdef TFT_SDA_READ +#if defined(TFT_eSPI_ENABLE_8_BIT_READ) + uint8_t tft_Read_8(void); // Read 8-bit value from TFT command register +#endif + void begin_SDA_Read(void); // Begin a read on a half duplex (bi-directional SDA) SPI bus - sets MOSI to input + void end_SDA_Read(void); // Restore MOSI to output +#endif + + // Graphics drawing + void fillScreen(uint32_t color), + drawRect(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color), + drawRoundRect(int32_t x, int32_t y, int32_t w, int32_t h, int32_t radius, uint32_t color), + fillRoundRect(int32_t x, int32_t y, int32_t w, int32_t h, int32_t radius, uint32_t color); + + void fillRectVGradient(int16_t x, int16_t y, int16_t w, int16_t h, uint32_t color1, uint32_t color2); + void fillRectHGradient(int16_t x, int16_t y, int16_t w, int16_t h, uint32_t color1, uint32_t color2); + + void drawCircle(int32_t x, int32_t y, int32_t r, uint32_t color), + drawCircleHelper(int32_t x, int32_t y, int32_t r, uint8_t cornername, uint32_t color), + fillCircle(int32_t x, int32_t y, int32_t r, uint32_t color), + fillCircleHelper(int32_t x, int32_t y, int32_t r, uint8_t cornername, int32_t delta, uint32_t color), + + drawEllipse(int16_t x, int16_t y, int32_t rx, int32_t ry, uint16_t color), + fillEllipse(int16_t x, int16_t y, int32_t rx, int32_t ry, uint16_t color), + + // Corner 1 Corner 2 Corner 3 + drawTriangle(int32_t x1, int32_t y1, int32_t x2, int32_t y2, int32_t x3, int32_t y3, uint32_t color), + fillTriangle(int32_t x1, int32_t y1, int32_t x2, int32_t y2, int32_t x3, int32_t y3, uint32_t color); + + // Smooth (anti-aliased) graphics drawing + // Draw a pixel blended with the background pixel colour (bg_color) specified, return blended colour + // If the bg_color is not specified, the background pixel colour will be read from TFT or sprite + uint16_t drawPixel(int32_t x, int32_t y, uint32_t color, uint8_t alpha, uint32_t bg_color = 0x00FFFFFF); + + // Draw an anti-aliased (smooth) arc between start and end angles. Arc ends are anti-aliased. + // By default the arc is drawn with square ends unless the "roundEnds" parameter is included and set true + // Angle = 0 is at 6 o'clock position, 90 at 9 o'clock etc. The angles must be in range 0-360 or they will be clipped to these limits + // The start angle may be larger than the end angle. Arcs are always drawn clockwise from the start angle. + void drawSmoothArc(int32_t x, int32_t y, int32_t r, int32_t ir, uint32_t startAngle, uint32_t endAngle, uint32_t fg_color, uint32_t bg_color, bool roundEnds = false); + + // As per "drawSmoothArc" except the ends of the arc are NOT anti-aliased, this facilitates dynamic arc length changes with + // arc segments and ensures clean segment joints. + // The sides of the arc are anti-aliased by default. If smoothArc is false sides will NOT be anti-aliased + void drawArc(int32_t x, int32_t y, int32_t r, int32_t ir, uint32_t startAngle, uint32_t endAngle, uint32_t fg_color, uint32_t bg_color, bool smoothArc = true); + + // Draw an anti-aliased filled circle at x, y with radius r + // Note: The thickness of line is 3 pixels to reduce the visible "braiding" effect of anti-aliasing narrow lines + // this means the inner anti-alias zone is always at r-1 and the outer zone at r+1 + void drawSmoothCircle(int32_t x, int32_t y, int32_t r, uint32_t fg_color, uint32_t bg_color); + + // Draw an anti-aliased filled circle at x, y with radius r + // If bg_color is not included the background pixel colour will be read from TFT or sprite + void fillSmoothCircle(int32_t x, int32_t y, int32_t r, uint32_t color, uint32_t bg_color = 0x00FFFFFF); + + // Draw a rounded rectangle that has a line thickness of r-ir+1 and bounding box defined by x,y and w,h + // The outer corner radius is r, inner corner radius is ir + // The inside and outside of the border are anti-aliased + void drawSmoothRoundRect(int32_t x, int32_t y, int32_t r, int32_t ir, int32_t w, int32_t h, uint32_t fg_color, uint32_t bg_color = 0x00FFFFFF, uint8_t quadrants = 0xF); + + // Draw a filled rounded rectangle , corner radius r and bounding box defined by x,y and w,h + void fillSmoothRoundRect(int32_t x, int32_t y, int32_t w, int32_t h, int32_t radius, uint32_t color, uint32_t bg_color = 0x00FFFFFF); + + // Draw a small anti-aliased filled circle at ax,ay with radius r (uses drawWideLine) + // If bg_color is not included the background pixel colour will be read from TFT or sprite + void drawSpot(float ax, float ay, float r, uint32_t fg_color, uint32_t bg_color = 0x00FFFFFF); + + // Draw an anti-aliased wide line from ax,ay to bx,by width wd with radiused ends (radius is wd/2) + // If bg_color is not included the background pixel colour will be read from TFT or sprite + void drawWideLine(float ax, float ay, float bx, float by, float wd, uint32_t fg_color, uint32_t bg_color = 0x00FFFFFF); + + // Draw an anti-aliased wide line from ax,ay to bx,by with different width at each end aw, bw and with radiused ends + // If bg_color is not included the background pixel colour will be read from TFT or sprite + void drawWedgeLine(float ax, float ay, float bx, float by, float aw, float bw, uint32_t fg_color, uint32_t bg_color = 0x00FFFFFF); + + // Image rendering + // Swap the byte order for pushImage() and pushPixels() - corrects endianness + void setSwapBytes(bool swap); + bool getSwapBytes(void); + + // Draw bitmap + void drawBitmap(int16_t x, int16_t y, const uint8_t *bitmap, int16_t w, int16_t h, uint16_t fgcolor), + drawBitmap(int16_t x, int16_t y, const uint8_t *bitmap, int16_t w, int16_t h, uint16_t fgcolor, uint16_t bgcolor), + drawXBitmap(int16_t x, int16_t y, const uint8_t *bitmap, int16_t w, int16_t h, uint16_t fgcolor), + drawXBitmap(int16_t x, int16_t y, const uint8_t *bitmap, int16_t w, int16_t h, uint16_t fgcolor, uint16_t bgcolor), + setBitmapColor(uint16_t fgcolor, uint16_t bgcolor); // Define the 2 colours for 1bpp sprites + + // Set TFT pivot point (use when rendering rotated sprites) + void setPivot(int16_t x, int16_t y); + int16_t getPivotX(void), // Get pivot x + getPivotY(void); // Get pivot y + + // The next functions can be used as a pair to copy screen blocks (or horizontal/vertical lines) to another location + // Read a block of pixels to a data buffer, buffer is 16-bit and the size must be at least w * h + void readRect(int32_t x, int32_t y, int32_t w, int32_t h, uint16_t *data); + // Write a block of pixels to the screen which have been read by readRect() + void pushRect(int32_t x, int32_t y, int32_t w, int32_t h, uint16_t *data); + + // These are used to render images or sprites stored in RAM arrays (used by Sprite class for 16bpp Sprites) + void pushImage(int32_t x, int32_t y, int32_t w, int32_t h, uint16_t *data); + void pushImage(int32_t x, int32_t y, int32_t w, int32_t h, uint16_t *data, uint16_t transparent); + + // These are used to render images stored in FLASH (PROGMEM) + void pushImage(int32_t x, int32_t y, int32_t w, int32_t h, const uint16_t *data, uint16_t transparent); + void pushImage(int32_t x, int32_t y, int32_t w, int32_t h, const uint16_t *data); + + // These are used by Sprite class pushSprite() member function for 1, 4 and 8 bits per pixel (bpp) colours + // They are not intended to be used with user sketches (but could be) + // Set bpp8 true for 8bpp sprites, false otherwise. The cmap pointer must be specified for 4bpp + void pushImage(int32_t x, int32_t y, int32_t w, int32_t h, uint8_t *data, bool bpp8 = true, uint16_t *cmap = nullptr); + void pushImage(int32_t x, int32_t y, int32_t w, int32_t h, uint8_t *data, uint8_t transparent, bool bpp8 = true, uint16_t *cmap = nullptr); + // FLASH version + void pushImage(int32_t x, int32_t y, int32_t w, int32_t h, const uint8_t *data, bool bpp8, uint16_t *cmap = nullptr); + + // Render a 16-bit colour image with a 1bpp mask + void pushMaskedImage(int32_t x, int32_t y, int32_t w, int32_t h, uint16_t *img, uint8_t *mask); + + // This next function has been used successfully to dump the TFT screen to a PC for documentation purposes + // It reads a screen area and returns the 3 RGB 8-bit colour values of each pixel in the buffer + // Set w and h to 1 to read 1 pixel's colour. The data buffer must be at least w * h * 3 bytes + void readRectRGB(int32_t x, int32_t y, int32_t w, int32_t h, uint8_t *data); + + // Text rendering - value returned is the pixel width of the rendered text + int16_t drawNumber(long intNumber, int32_t x, int32_t y, uint8_t font), // Draw integer using specified font number + drawNumber(long intNumber, int32_t x, int32_t y), // Draw integer using current font + + // Decimal is the number of decimal places to render + // Use with setTextDatum() to position values on TFT, and setTextPadding() to blank old displayed values + drawFloat(float floatNumber, uint8_t decimal, int32_t x, int32_t y, uint8_t font), // Draw float using specified font number + drawFloat(float floatNumber, uint8_t decimal, int32_t x, int32_t y), // Draw float using current font + + // Handle char arrays + // Use with setTextDatum() to position string on TFT, and setTextPadding() to blank old displayed strings + drawString(const char *string, int32_t x, int32_t y, uint8_t font), // Draw string using specified font number + drawString(const char *string, int32_t x, int32_t y), // Draw string using current font + drawString(const String &string, int32_t x, int32_t y, uint8_t font), // Draw string using specified font number + drawString(const String &string, int32_t x, int32_t y), // Draw string using current font + + drawCentreString(const char *string, int32_t x, int32_t y, uint8_t font), // Deprecated, use setTextDatum() and drawString() + drawRightString(const char *string, int32_t x, int32_t y, uint8_t font), // Deprecated, use setTextDatum() and drawString() + drawCentreString(const String &string, int32_t x, int32_t y, uint8_t font), // Deprecated, use setTextDatum() and drawString() + drawRightString(const String &string, int32_t x, int32_t y, uint8_t font); // Deprecated, use setTextDatum() and drawString() + + // Text rendering and font handling support functions + void setCursor(int16_t x, int16_t y), // Set cursor for tft.print() + setCursor(int16_t x, int16_t y, uint8_t font); // Set cursor and font number for tft.print() + + int16_t getCursorX(void), // Read current cursor x position (moves with tft.print()) + getCursorY(void); // Read current cursor y position + + void setTextColor(uint16_t color), // Set character (glyph) color only (background not over-written) + setTextColor(uint16_t fgcolor, uint16_t bgcolor, bool bgfill = false), // Set character (glyph) foreground and background colour, optional background fill for smooth fonts + setTextSize(uint8_t size); // Set character size multiplier (this increases pixel size) + + void setTextWrap(bool wrapX, bool wrapY = false); // Turn on/off wrapping of text in TFT width and/or height + + void setTextDatum(uint8_t datum); // Set text datum position (default is top left), see Section 5 above + uint8_t getTextDatum(void); + + void setTextPadding(uint16_t x_width); // Set text padding (background blanking/over-write) width in pixels + uint16_t getTextPadding(void); // Get text padding + +#ifdef LOAD_GFXFF + void setFreeFont(const GFXfont *f = NULL), // Select the GFX Free Font + setTextFont(uint8_t font); // Set the font number to use in future +#else + void setFreeFont(uint8_t font), // Not used, historical fix to prevent an error + setTextFont(uint8_t font); // Set the font number to use in future +#endif + + int16_t textWidth(const char *string, uint8_t font), // Returns pixel width of string in specified font + textWidth(const char *string), // Returns pixel width of string in current font + textWidth(const String &string, uint8_t font), // As above for String types + textWidth(const String &string), + fontHeight(uint8_t font), // Returns pixel height of specified font + fontHeight(void); // Returns pixel height of current font + + // Used by library and Smooth font class to extract Unicode point codes from a UTF8 encoded string + uint16_t decodeUTF8(uint8_t *buf, uint16_t *index, uint16_t remaining), + decodeUTF8(uint8_t c); + + // Support function to UTF8 decode and draw characters piped through print stream + size_t write(uint8_t); + // size_t write(const uint8_t *buf, size_t len); + + // Used by Smooth font class to fetch a pixel colour for the anti-aliasing + void setCallback(getColorCallback getCol); + + uint16_t fontsLoaded(void); // Each bit in returned value represents a font type that is loaded - used for debug/error handling only + + // Low level read/write + void spiwrite(uint8_t); // legacy support only +#ifdef RM68120_DRIVER + void writecommand(uint16_t c); // Send a 16-bit command, function resets DC/RS high ready for data + void writeRegister8(uint16_t c, uint8_t d); // Write 8-bit data data to 16-bit command register + void writeRegister16(uint16_t c, uint16_t d); // Write 16-bit data data to 16-bit command register +#else + void writecommand(uint8_t c); // Send an 8-bit command, function resets DC/RS high ready for data +#endif + void writedata(uint8_t d); // Send data with DC/RS set high + void writendata(uint8_t* d, uint16_t dataLength); + void writecommanddata(uint8_t c,const uint8_t* d, uint16_t dataLength); + + void commandList(const uint8_t *addr); // Send a initialisation sequence to TFT stored in FLASH + + uint8_t readcommand8(uint8_t cmd_function, uint8_t index = 0); // read 8 bits from TFT + uint16_t readcommand16(uint8_t cmd_function, uint8_t index = 0); // read 16 bits from TFT + uint32_t readcommand32(uint8_t cmd_function, uint8_t index = 0); // read 32 bits from TFT + + // Colour conversion + // Convert 8-bit red, green and blue to 16 bits + uint16_t color565(uint8_t red, uint8_t green, uint8_t blue); + + // Convert 8-bit colour to 16 bits + uint16_t color8to16(uint8_t color332); + // Convert 16-bit colour to 8 bits + uint8_t color16to8(uint16_t color565); + + // Convert 16-bit colour to/from 24-bit, R+G+B concatenated into LS 24 bits + uint32_t color16to24(uint16_t color565); + uint32_t color24to16(uint32_t color888); + + // Alpha blend 2 colours, see generic "alphaBlend_Test" example + // alpha = 0 = 100% background colour + // alpha = 255 = 100% foreground colour + uint16_t alphaBlend(uint8_t alpha, uint16_t fgc, uint16_t bgc); + + // 16-bit colour alphaBlend with alpha dither (dither reduces colour banding) + uint16_t alphaBlend(uint8_t alpha, uint16_t fgc, uint16_t bgc, uint8_t dither); + // 24-bit colour alphaBlend with optional alpha dither + uint32_t alphaBlend24(uint8_t alpha, uint32_t fgc, uint32_t bgc, uint8_t dither = 0); + + // Direct Memory Access (DMA) support functions + // These can be used for SPI writes when using the ESP32 (original) or STM32 processors. + // DMA also works on a RP2040 processor with PIO based SPI and parallel (8 and 16-bit) interfaces + // Bear in mind DMA will only be of benefit in particular circumstances and can be tricky + // to manage by noobs. The functions have however been designed to be noob friendly and + // avoid a few DMA behaviour "gotchas". + // + // At best you will get a 2x TFT rendering performance improvement when using DMA because + // this library handles the SPI bus so efficiently during normal (non DMA) transfers. The best + // performance improvement scenario is the DMA transfer time is exactly the same as the time it + // takes for the processor to prepare the next image buffer and initiate another DMA transfer. + // + // DMA transfer to the TFT is done while the processor moves on to handle other tasks. Bear + // this in mind and watch out for "gotchas" like the image buffer going out of scope as the + // processor leaves a function or its content being changed while the DMA engine is reading it. + // + // The compiler MAY change the implied scope of a buffer which has been set aside by creating + // an array. For example a buffer defined before a "for-next" loop may get de-allocated when + // the loop ends. To avoid this use, for example, malloc() and free() to take control of when + // the buffer space is available and ensure it is not released until DMA is complete. + // + // Clearly you should not modify a buffer that is being DMA'ed to the TFT until the DMA is over. + // Use the dmaBusy() function to check this. Use tft.startWrite() before invoking DMA so the + // TFT chip select stays low. If you use tft.endWrite() before DMA is complete then the endWrite + // function will wait for the DMA to complete, so this may defeat any DMA performance benefit. + // + + bool initDMA(bool ctrl_cs = false); // Initialise the DMA engine and attach to SPI bus - typically used in setup() + // Parameter "true" enables DMA engine control of TFT chip select (ESP32 only) + // For ESP32 only, TFT reads will not work if parameter is true + void deInitDMA(void); // De-initialise the DMA engine and detach from SPI bus - typically not used + + // Push an image to the TFT using DMA, buffer is optional and grabs (double buffers) a copy of the image + // Use the buffer if the image data will get over-written or destroyed while DMA is in progress + // + // Note 1: If swapping colour bytes is defined, and the double buffer option is NOT used, then the bytes + // in the original image buffer content will be byte swapped by the function before DMA is initiated. + // + // Note 2: If part of the image will be off screen or outside of a set viewport, then the the original + // image buffer content will be altered to a correctly clipped image before DMA is initiated. + // + // The function will wait for the last DMA to complete if it is called while a previous DMA is still + // in progress, this simplifies the sketch and helps avoid "gotchas". + void pushImageDMA(int32_t x, int32_t y, int32_t w, int32_t h, uint16_t *data, uint16_t *buffer = nullptr); + +#if defined(ESP32) // ESP32 only at the moment + // For case where pointer is a const and the image data must not be modified (clipped or byte swapped) + void pushImageDMA(int32_t x, int32_t y, int32_t w, int32_t h, uint16_t const *data); +#endif + // Push a block of pixels into a window set up using setAddrWindow() + void pushPixelsDMA(uint16_t *image, uint32_t len); + + // Check if the DMA is complete - use while(tft.dmaBusy); for a blocking wait + bool dmaBusy(void); // returns true if DMA is still in progress + void dmaWait(void); // wait until DMA is complete + + bool DMA_Enabled = false; // Flag for DMA enabled state + uint8_t spiBusyCheck = 0; // Number of ESP32 transfer buffers to check + + // Bare metal functions + void startWrite(void); // Begin SPI transaction + void writeColor(uint16_t color, uint32_t len); // Deprecated, use pushBlock() + void endWrite(void); // End SPI transaction + + // Set/get an arbitrary library configuration attribute or option + // Use to switch ON/OFF capabilities such as UTF8 decoding - each attribute has a unique ID + // id = 0: reserved - may be used in future to reset all attributes to a default state + // id = 1: Turn on (a=true) or off (a=false) GLCD cp437 font character error correction + // id = 2: Turn on (a=true) or off (a=false) UTF8 decoding + // id = 3: Enable or disable use of ESP32 PSRAM (if available) +#define CP437_SWITCH 1 +#define UTF8_SWITCH 2 +#define PSRAM_ENABLE 3 + void setAttribute(uint8_t id = 0, uint8_t a = 0); // Set attribute value + uint8_t getAttribute(uint8_t id = 0); // Get attribute value + + // Used for diagnostic sketch to see library setup adopted by compiler, see Section 7 above + void getSetup(setup_t &tft_settings); // Sketch provides the instance to populate + bool verifySetupID(uint32_t id); + + // Global variables +#if !defined(TFT_PARALLEL_8_BIT) && !defined(RP2040_PIO_INTERFACE) + static SPIClass &getSPIinstance(void); // Get SPI class handle +#endif + uint32_t textcolor, textbgcolor; // Text foreground and background colours + + uint32_t bitmap_fg, bitmap_bg; // Bitmap foreground (bit=1) and background (bit=0) colours + + uint8_t textfont, // Current selected font number + textsize, // Current font size multiplier + textdatum, // Text reference datum + rotation; // Display rotation (0-3) + + uint8_t decoderState = 0; // UTF8 decoder state - not for user access + uint16_t decoderBuffer; // Unicode code-point buffer - not for user access + +#ifdef TCON_ENABLE + #include "Extensions/Tcon.h" +#endif + //--------------------------------------- private ------------------------------------// +private: + // Legacy begin and end prototypes - deprecated TODO: delete + void spi_begin(); + void spi_end(); + + void spi_begin_read(); + void spi_end_read(); + + // New begin and end prototypes + // begin/end a TFT write transaction + // For SPI bus the transmit clock rate is set + inline void begin_tft_write() __attribute__((always_inline)); + inline void end_tft_write() __attribute__((always_inline)); + + // begin/end a TFT read transaction + // For SPI bus: begin lowers SPI clock rate, end reinstates transmit clock rate + inline void begin_tft_read() __attribute__((always_inline)); + inline void end_tft_read() __attribute__((always_inline)); + + // Initialise the data bus GPIO and hardware interfaces + void initBus(void); + + // Temporary library development function TODO: remove need for this + void pushSwapBytePixels(const void *data_in, uint32_t len); + + // Same as setAddrWindow but exits with CGRAM in read mode + void readAddrWindow(int32_t xs, int32_t ys, int32_t w, int32_t h); + + // Byte read prototype + uint8_t readByte(void); + + // GPIO parallel bus input/output direction control + void busDir(uint32_t mask, uint8_t mode); + + // Single GPIO input/output direction control + void gpioMode(uint8_t gpio, uint8_t mode); + + // Smooth graphics helper + uint8_t sqrt_fraction(uint32_t num); + + // Helper function: calculate distance of a point from a finite length line between two points + float wedgeLineDistance(float pax, float pay, float bax, float bay, float dr); + + // Display variant settings + uint8_t tabcolor, // ST7735 screen protector "tab" colour (now invalid) + colstart = 0, rowstart = 0; // Screen display area to CGRAM area coordinate offsets + + // Port and pin masks for control signals (ESP826 only) - TODO: remove need for this + volatile uint32_t *dcport, *csport; + uint32_t cspinmask, dcpinmask, wrpinmask, sclkpinmask; + +#if defined(ESP32_PARALLEL) + // Bit masks for ESP32 parallel bus interface + uint32_t xclr_mask, xdir_mask; // Port set/clear and direction control masks + + // Lookup table for ESP32 parallel bus interface uses 1kbyte RAM, + uint32_t xset_mask[256]; // Makes Sprite rendering test 33% faster, for slower macro equivalent + // see commented out #define set_mask(C) within TFT_eSPI_ESP32.h +#endif + + // uint32_t lastColor = 0xFFFF; // Last colour - used to minimise bit shifting overhead + + getColorCallback getColor = nullptr; // Smooth font callback function pointer + + bool locked, inTransaction, lockTransaction; // SPI transaction and mutex lock flags + + //-------------------------------------- protected ----------------------------------// +protected: + // int32_t win_xe, win_ye; // Window end coords - not needed + + int32_t _init_width, _init_height; // Display w/h as input, used by setRotation() + int32_t _width, _height; // Display w/h as modified by current rotation + int32_t addr_row, addr_col; // Window position - used to minimise window commands + + int16_t _xPivot; // TFT x pivot point coordinate for rotated Sprites + int16_t _yPivot; // TFT x pivot point coordinate for rotated Sprites + + // Viewport variables + int32_t _vpX, _vpY, _vpW, _vpH; // Note: x start, y start, x end + 1, y end + 1 + int32_t _xDatum; + int32_t _yDatum; + int32_t _xWidth; + int32_t _yHeight; + bool _vpDatum; + bool _vpOoB; + + int32_t cursor_x, cursor_y, padX; // Text cursor x,y and padding setting + int32_t bg_cursor_x; // Background fill cursor + int32_t last_cursor_x; // Previous text cursor position when fill used + + uint32_t fontsloaded; // Bit field of fonts loaded + + uint8_t glyph_ab, // Smooth font glyph delta Y (height) above baseline + glyph_bb; // Smooth font glyph delta Y (height) below baseline + + bool isDigits; // adjust bounding box for numbers to reduce visual jiggling + bool textwrapX, textwrapY; // If set, 'wrap' text at right and optionally bottom edge of display + bool _swapBytes; // Swap the byte order for TFT pushImage() + + bool _booted; // init() or begin() has already run once + + // User sketch manages these via set/getAttribute() + bool _cp437; // If set, use correct CP437 charset (default is OFF) + bool _utf8; // If set, use UTF-8 decoder in print stream 'write()' function (default ON) + bool _psram_enable; // Enable PSRAM use for library functions (TBD) and Sprites + + uint32_t _lastColor; // Buffered value of last colour used + + bool _fillbg; // Fill background flag (just for for smooth fonts at the moment) + +#if defined(SSD1963_DRIVER) + uint16_t Cswap; // Swap buffer for SSD1963 + uint8_t r6, g6, b6; // RGB buffer for SSD1963 +#endif + +#ifdef LOAD_GFXFF + GFXfont *gfxFont; +#endif + +/*************************************************************************************** +** Section 9: TFT_eSPI class conditional extensions +***************************************************************************************/ +// Load the Touch extension +#ifdef TOUCH_CS +#if defined(TFT_PARALLEL_8_BIT) || defined(RP2040_PIO_INTERFACE) +#if !defined(DISABLE_ALL_LIBRARY_WARNINGS) +#error>>>>------>> Touch functions not supported in 8/16-bit parallel mode or with RP2040 PIO. +#endif +#else +#include "Extensions/Touch.h" // Loaded if TOUCH_CS is defined by user +#endif +#else +#if !defined(DISABLE_ALL_LIBRARY_WARNINGS) +#warning>>>>------>> TOUCH_CS pin not defined, TFT_eSPI touch functions will not be available! +#endif +#endif + +// Load the Anti-aliased font extension +#ifdef SMOOTH_FONT +#include "Extensions/Smooth_font.h" // Loaded if SMOOTH_FONT is defined by user +#endif + +#include "Touch_Drivers/Touch.h" + +}; // End of class TFT_eSPI + +// Swap any type +template +static inline void +transpose(T &a, T &b) +{ + T t = a; + a = b; + b = t; +} + +// Fast alphaBlend +template +static inline uint16_t +fastBlend(A alpha, F fgc, B bgc) +{ + // Split out and blend 5-bit red and blue channels + uint32_t rxb = bgc & 0xF81F; + rxb += ((fgc & 0xF81F) - rxb) * (alpha >> 2) >> 6; + // Split out and blend 6-bit green channel + uint32_t xgx = bgc & 0x07E0; + xgx += ((fgc & 0x07E0) - xgx) * alpha >> 8; + // Recombine channels + return (rxb & 0xF81F) | (xgx & 0x07E0); +} + +/*************************************************************************************** +** Section 10: Additional extension classes +***************************************************************************************/ +// Load the Button Class +#include "Extensions/Button.h" + +// Load the Sprite Class +#include "Extensions/Sprite.h" + +// Load the EPaper +#ifdef EPAPER_ENABLE +#include "Extensions/EPaper.h" + + +#endif + + + +#endif // ends #ifndef _TFT_eSPIH_ diff --git a/lib/Seeed_GFX/Touch_Drivers/CHSCX6X.cpp b/lib/Seeed_GFX/Touch_Drivers/CHSCX6X.cpp new file mode 100644 index 0000000..4acaf92 --- /dev/null +++ b/lib/Seeed_GFX/Touch_Drivers/CHSCX6X.cpp @@ -0,0 +1,95 @@ +bool TFT_eSPI::getTouch(int32_t *x, int32_t *y, uint16_t threshold) +{ + + int32_t _x = 0, _y = 0; + uint8_t temp[CHSC6X_READ_POINT_LEN] = {0}; + uint8_t read_len = Wire.requestFrom(CHSC6X_I2C_ID, CHSC6X_READ_POINT_LEN); + +#ifdef TOUCH_INT + if (digitalRead(TOUCH_INT) != LOW) + { + delay(1); + if (digitalRead(TOUCH_INT) != LOW) + return false; + } +#endif + + if (read_len == CHSC6X_READ_POINT_LEN) + { + TOUCH_WIRE.readBytes(temp, read_len); + if (temp[0] == 0x01) + { + _x = temp[2]; + _y = temp[4]; + } + } + + // rotation correction + switch (rotation % 4) + { + case 1: + *x = _y; + *y = _height - _x; + *x = (*x < 0) ? 0 : (*x > _height) ? _height + : *x; + *y = (*y < 0) ? 0 : (*y > _width) ? _width + : *y; + break; + case 2: + *x = _width - _x; + *y = _height - _y; + *x = (*x < 0) ? 0 : (*x > _width) ? _width + : *x; + *y = (*y < 0) ? 0 : (*y > _height) ? _height + : *y; + break; + case 3: + *x = _width - _y; + *y = _x; + *x = (*x < 0) ? 0 : (*x > _height) ? _height + : *x; + *y = (*y < 0) ? 0 : (*y > _width) ? _width + : *y; + break; + default: + *x = _x; + *y = _y; + *x = (*x < 0) ? 0 : (*x > _width) ? _width + : *x; + *y = (*y < 0) ? 0 : (*y > _height) ? _height + : *y; + break; + } + + // orientation correction + *x = *x - _xDatum; + *y = *y - _yDatum; + + return true; +} + +bool TFT_eSPI::getTouchRaw(int32_t *x, int32_t *y) +{ +#pragma warning "This function is obsolete and is only implemented empty to maintain compatibility" + return false; +} + +uint16_t TFT_eSPI::getTouchRawZ(void) +{ +#pragma warning "This function is obsolete and is only implemented empty to maintain compatibility" + return 0; +} + +void TFT_eSPI::convertRawXY(int32_t *x, int32_t *y) +{ +#pragma warning "This function is obsolete and is only implemented empty to maintain compatibility" +} +void TFT_eSPI::calibrateTouch(uint16_t *data, uint32_t color_fg, uint32_t color_bg, uint8_t size) +{ +#pragma warning "This function is obsolete and is only implemented empty to maintain compatibility" +} + +void TFT_eSPI::setTouch(uint16_t *data) +{ +#pragma warning "This function is obsolete and is only implemented empty to maintain compatibility" +} \ No newline at end of file diff --git a/lib/Seeed_GFX/Touch_Drivers/CHSCX6X.h b/lib/Seeed_GFX/Touch_Drivers/CHSCX6X.h new file mode 100644 index 0000000..5f5caf2 --- /dev/null +++ b/lib/Seeed_GFX/Touch_Drivers/CHSCX6X.h @@ -0,0 +1,10 @@ +public: + + bool getTouch(int32_t *x, int32_t *y, uint16_t threshold = 600); + + /*Considering the capacitive screen implementation, the following functions are obsolete and are only implemented empty to maintain compatibility*/ + bool getTouchRaw(int32_t *x, int32_t *y); + uint16_t getTouchRawZ(void); + void convertRawXY(int32_t *x, int32_t *y); + void calibrateTouch(uint16_t *data, uint32_t color_fg, uint32_t color_bg, uint8_t size); + void setTouch(uint16_t *data); diff --git a/lib/Seeed_GFX/Touch_Drivers/CHSCX6X_Defines.h b/lib/Seeed_GFX/Touch_Drivers/CHSCX6X_Defines.h new file mode 100644 index 0000000..a9b3d59 --- /dev/null +++ b/lib/Seeed_GFX/Touch_Drivers/CHSCX6X_Defines.h @@ -0,0 +1,7 @@ +#define CHSC6X_I2C_ID 0x2e +#define CHSC6X_MAX_POINTS_NUM 1 +#define CHSC6X_READ_POINT_LEN 5 + +#ifndef TOUCH_WIRE +#define TOUCH_WIRE Wire +#endif \ No newline at end of file diff --git a/lib/Seeed_GFX/Touch_Drivers/CHSCX6X_Init.cpp b/lib/Seeed_GFX/Touch_Drivers/CHSCX6X_Init.cpp new file mode 100644 index 0000000..8c29656 --- /dev/null +++ b/lib/Seeed_GFX/Touch_Drivers/CHSCX6X_Init.cpp @@ -0,0 +1,6 @@ + +TOUCH_WIRE.begin(); + +#ifdef TOUCH_INT +pinMode(TOUCH_INT, INPUT_PULLUP); +#endif \ No newline at end of file diff --git a/lib/Seeed_GFX/Touch_Drivers/Touch.cpp b/lib/Seeed_GFX/Touch_Drivers/Touch.cpp new file mode 100644 index 0000000..d51f306 --- /dev/null +++ b/lib/Seeed_GFX/Touch_Drivers/Touch.cpp @@ -0,0 +1,29 @@ +#ifdef CHSCX6X_DRIVER +#include "Touch_Drivers/CHSCX6X.cpp" +#else +bool TFT_eSPI::getTouch(int32_t *x, int32_t *y, uint16_t threshold) +{ + return false; +} + +bool TFT_eSPI::getTouchRaw(int32_t *x, int32_t *y) +{ + return false; +} + +uint16_t TFT_eSPI::getTouchRawZ(void) +{ + return 0; +} + +void TFT_eSPI::convertRawXY(int32_t *x, int32_t *y) +{ +} +void TFT_eSPI::calibrateTouch(uint16_t *data, uint32_t color_fg, uint32_t color_bg, uint8_t size) +{ +} + +void TFT_eSPI::setTouch(uint16_t *data) +{ +} +#endif \ No newline at end of file diff --git a/lib/Seeed_GFX/Touch_Drivers/Touch.h b/lib/Seeed_GFX/Touch_Drivers/Touch.h new file mode 100644 index 0000000..110841a --- /dev/null +++ b/lib/Seeed_GFX/Touch_Drivers/Touch.h @@ -0,0 +1,17 @@ +#ifdef CHSCX6X_DRIVER +#include "Touch_Drivers/CHSCX6X.h" +#else +public: +bool getTouchRaw(int32_t *x, int32_t *y); + +uint16_t getTouchRawZ(void); + +void convertRawXY(int32_t *x, int32_t *y); + +bool getTouch(int32_t *x, int32_t *y, uint16_t threshold = 600); + +void calibrateTouch(uint16_t *data, uint32_t color_fg, uint32_t color_bg, uint8_t size); + +void setTouch(uint16_t *data); + +#endif \ No newline at end of file diff --git a/lib/Seeed_GFX/Touch_Drivers/Touch_Defines.h b/lib/Seeed_GFX/Touch_Drivers/Touch_Defines.h new file mode 100644 index 0000000..cba338a --- /dev/null +++ b/lib/Seeed_GFX/Touch_Drivers/Touch_Defines.h @@ -0,0 +1,3 @@ +#ifdef CHSCX6X_DRIVER +#include "Touch_Drivers/CHSCX6X_Defines.h" +#endif \ No newline at end of file diff --git a/lib/Seeed_GFX/Touch_Drivers/Touch_Init.cpp b/lib/Seeed_GFX/Touch_Drivers/Touch_Init.cpp new file mode 100644 index 0000000..cf58b62 --- /dev/null +++ b/lib/Seeed_GFX/Touch_Drivers/Touch_Init.cpp @@ -0,0 +1,3 @@ +#ifdef CHSCX6X_DRIVER +#include "Touch_Drivers/CHSCX6X_Init.cpp" +#endif \ No newline at end of file diff --git a/lib/Seeed_GFX/User_Setup.h b/lib/Seeed_GFX/User_Setup.h new file mode 100644 index 0000000..a7ed2ac --- /dev/null +++ b/lib/Seeed_GFX/User_Setup.h @@ -0,0 +1,389 @@ +// USER DEFINED SETTINGS +// Set driver type, fonts to be loaded, pins used and SPI control method etc. +// +// See the User_Setup_Select.h file if you wish to be able to define multiple +// setups and then easily select which setup file is used by the compiler. +// +// If this file is edited correctly then all the library example sketches should +// run without the need to make any more changes for a particular hardware setup! +// Note that some sketches are designed for a particular TFT pixel width/height + +// User defined information reported by "Read_User_Setup" test & diagnostics example +#define USER_SETUP_INFO "User_Setup" + +// Define to disable all #warnings in library (can be put in User_Setup_Select.h) +//#define DISABLE_ALL_LIBRARY_WARNINGS + +// ################################################################################## +// +// Section 1. Call up the right driver file and any options for it +// +// ################################################################################## + +// Define STM32 to invoke optimised processor support (only for STM32) +//#define STM32 + +// Defining the STM32 board allows the library to optimise the performance +// for UNO compatible "MCUfriend" style shields +//#define NUCLEO_64_TFT +//#define NUCLEO_144_TFT + +// STM32 8-bit parallel only: +// If STN32 Port A or B pins 0-7 are used for 8-bit parallel data bus bits 0-7 +// then this will improve rendering performance by a factor of ~8x +//#define STM_PORTA_DATA_BUS +//#define STM_PORTB_DATA_BUS + +// Tell the library to use parallel mode (otherwise SPI is assumed) +//#define TFT_PARALLEL_8_BIT +//#defined TFT_PARALLEL_16_BIT // **** 16-bit parallel ONLY for RP2040 processor **** + +// Display type - only define if RPi display +//#define RPI_DISPLAY_TYPE // 20MHz maximum SPI + +// Only define one driver, the other ones must be commented out +//#define ILI9341_DRIVER // Generic driver for common displays +//#define ILI9341_2_DRIVER // Alternative ILI9341 driver, see https://github.com/Bodmer/TFT_eSPI/issues/1172 +//#define ST7735_DRIVER // Define additional parameters below for this display +//#define ILI9163_DRIVER // Define additional parameters below for this display +//#define S6D02A1_DRIVER +//#define RPI_ILI9486_DRIVER // 20MHz maximum SPI +//#define HX8357D_DRIVER +//#define ILI9481_DRIVER +//#define ILI9486_DRIVER +//#define ILI9488_DRIVER // WARNING: Do not connect ILI9488 display SDO to MISO if other devices share the SPI bus (TFT SDO does NOT tristate when CS is high) +//#define ST7789_DRIVER // Full configuration option, define additional parameters below for this display +//#define ST7789_2_DRIVER // Minimal configuration option, define additional parameters below for this display +//#define R61581_DRIVER +//#define RM68140_DRIVER +//#define ST7796_DRIVER +//#define SSD1351_DRIVER +//#define SSD1963_480_DRIVER +//#define SSD1963_800_DRIVER +//#define SSD1963_800ALT_DRIVER +//#define ILI9225_DRIVER +//#define GC9A01_DRIVER + +// Some displays support SPI reads via the MISO pin, other displays have a single +// bi-directional SDA pin and the library will try to read this via the MOSI line. +// To use the SDA line for reading data from the TFT uncomment the following line: + +// #define TFT_SDA_READ // This option is for ESP32 ONLY, tested with ST7789 and GC9A01 display only + +// For ST7735, ST7789 and ILI9341 ONLY, define the colour order IF the blue and red are swapped on your display +// Try ONE option at a time to find the correct colour order for your display + +// #define TFT_RGB_ORDER TFT_RGB // Colour order Red-Green-Blue +// #define TFT_RGB_ORDER TFT_BGR // Colour order Blue-Green-Red + +// For M5Stack ESP32 module with integrated ILI9341 display ONLY, remove // in line below + +// #define M5STACK + +// For ST7789, ST7735, ILI9163 and GC9A01 ONLY, define the pixel width and height in portrait orientation +// #define TFT_WIDTH 80 +// #define TFT_WIDTH 128 +// #define TFT_WIDTH 172 // ST7789 172 x 320 +// #define TFT_WIDTH 170 // ST7789 170 x 320 +// #define TFT_WIDTH 240 // ST7789 240 x 240 and 240 x 320 +// #define TFT_HEIGHT 160 +// #define TFT_HEIGHT 128 +// #define TFT_HEIGHT 240 // ST7789 240 x 240 +// #define TFT_HEIGHT 320 // ST7789 240 x 320 +// #define TFT_HEIGHT 240 // GC9A01 240 x 240 + +// For ST7735 ONLY, define the type of display, originally this was based on the +// colour of the tab on the screen protector film but this is not always true, so try +// out the different options below if the screen does not display graphics correctly, +// e.g. colours wrong, mirror images, or stray pixels at the edges. +// Comment out ALL BUT ONE of these options for a ST7735 display driver, save this +// this User_Setup file, then rebuild and upload the sketch to the board again: + +// #define ST7735_INITB +// #define ST7735_GREENTAB +// #define ST7735_GREENTAB2 +// #define ST7735_GREENTAB3 +// #define ST7735_GREENTAB128 // For 128 x 128 display +// #define ST7735_GREENTAB160x80 // For 160 x 80 display (BGR, inverted, 26 offset) +// #define ST7735_ROBOTLCD // For some RobotLCD Arduino shields (128x160, BGR, https://docs.arduino.cc/retired/getting-started-guides/TFT) +// #define ST7735_REDTAB +// #define ST7735_BLACKTAB +// #define ST7735_REDTAB160x80 // For 160 x 80 display with 24 pixel offset + +// If colours are inverted (white shows as black) then uncomment one of the next +// 2 lines try both options, one of the options should correct the inversion. + +// #define TFT_INVERSION_ON +// #define TFT_INVERSION_OFF + + +// ################################################################################## +// +// Section 2. Define the pins that are used to interface with the display here +// +// ################################################################################## + +// If a backlight control signal is available then define the TFT_BL pin in Section 2 +// below. The backlight will be turned ON when tft.begin() is called, but the library +// needs to know if the LEDs are ON with the pin HIGH or LOW. If the LEDs are to be +// driven with a PWM signal or turned OFF/ON then this must be handled by the user +// sketch. e.g. with digitalWrite(TFT_BL, LOW); + +// #define TFT_BL 32 // LED back-light control pin +// #define TFT_BACKLIGHT_ON HIGH // Level to turn ON back-light (HIGH or LOW) + + + +// We must use hardware SPI, a minimum of 3 GPIO pins is needed. +// Typical setup for ESP8266 NodeMCU ESP-12 is : +// +// Display SDO/MISO to NodeMCU pin D6 (or leave disconnected if not reading TFT) +// Display LED to NodeMCU pin VIN (or 5V, see below) +// Display SCK to NodeMCU pin D5 +// Display SDI/MOSI to NodeMCU pin D7 +// Display DC (RS/AO)to NodeMCU pin D3 +// Display RESET to NodeMCU pin D4 (or RST, see below) +// Display CS to NodeMCU pin D8 (or GND, see below) +// Display GND to NodeMCU pin GND (0V) +// Display VCC to NodeMCU 5V or 3.3V +// +// The TFT RESET pin can be connected to the NodeMCU RST pin or 3.3V to free up a control pin +// +// The DC (Data Command) pin may be labelled AO or RS (Register Select) +// +// With some displays such as the ILI9341 the TFT CS pin can be connected to GND if no more +// SPI devices (e.g. an SD Card) are connected, in this case comment out the #define TFT_CS +// line below so it is NOT defined. Other displays such at the ST7735 require the TFT CS pin +// to be toggled during setup, so in these cases the TFT_CS line must be defined and connected. +// +// The NodeMCU D0 pin can be used for RST +// +// +// Note: only some versions of the NodeMCU provide the USB 5V on the VIN pin +// If 5V is not available at a pin you can use 3.3V but backlight brightness +// will be lower. + + +// ###### EDIT THE PIN NUMBERS IN THE LINES FOLLOWING TO SUIT YOUR ESP8266 SETUP ###### + +// For NodeMCU - use pin numbers in the form PIN_Dx where Dx is the NodeMCU pin designation +#define TFT_MISO PIN_D6 // Automatically assigned with ESP8266 if not defined +#define TFT_MOSI PIN_D7 // Automatically assigned with ESP8266 if not defined +#define TFT_SCLK PIN_D5 // Automatically assigned with ESP8266 if not defined + +#define TFT_CS PIN_D8 // Chip select control pin D8 +#define TFT_DC PIN_D3 // Data Command control pin +#define TFT_RST PIN_D4 // Reset pin (could connect to NodeMCU RST, see next line) +//#define TFT_RST -1 // Set TFT_RST to -1 if the display RESET is connected to NodeMCU RST or 3.3V + + +//#define TFT_BL PIN_D1 // LED back-light (only for ST7789 with backlight control pin) + +//#define TOUCH_CS PIN_D2 // Chip select pin (T_CS) of touch screen + +//#define TFT_WR PIN_D2 // Write strobe for modified Raspberry Pi TFT only + + +// ###### FOR ESP8266 OVERLAP MODE EDIT THE PIN NUMBERS IN THE FOLLOWING LINES ###### + +// Overlap mode shares the ESP8266 FLASH SPI bus with the TFT so has a performance impact +// but saves pins for other functions. It is best not to connect MISO as some displays +// do not tristate that line when chip select is high! +// Note: Only one SPI device can share the FLASH SPI lines, so a SPI touch controller +// cannot be connected as well to the same SPI signals. +// On NodeMCU 1.0 SD0=MISO, SD1=MOSI, CLK=SCLK to connect to TFT in overlap mode +// On NodeMCU V3 S0 =MISO, S1 =MOSI, S2 =SCLK +// In ESP8266 overlap mode the following must be defined + +//#define TFT_SPI_OVERLAP + +// In ESP8266 overlap mode the TFT chip select MUST connect to pin D3 +//#define TFT_CS PIN_D3 +//#define TFT_DC PIN_D5 // Data Command control pin +//#define TFT_RST PIN_D4 // Reset pin (could connect to NodeMCU RST, see next line) +//#define TFT_RST -1 // Set TFT_RST to -1 if the display RESET is connected to NodeMCU RST or 3.3V + + +// ###### EDIT THE PIN NUMBERS IN THE LINES FOLLOWING TO SUIT YOUR ESP32 SETUP ###### + +// For ESP32 Dev board (only tested with ILI9341 display) +// The hardware SPI can be mapped to any pins + +//#define TFT_MISO 19 +//#define TFT_MOSI 23 +//#define TFT_SCLK 18 +//#define TFT_CS 15 // Chip select control pin +//#define TFT_DC 2 // Data Command control pin +//#define TFT_RST 4 // Reset pin (could connect to RST pin) +//#define TFT_RST -1 // Set TFT_RST to -1 if display RESET is connected to ESP32 board RST + +// For ESP32 Dev board (only tested with GC9A01 display) +// The hardware SPI can be mapped to any pins + +//#define TFT_MOSI 15 // In some display driver board, it might be written as "SDA" and so on. +//#define TFT_SCLK 14 +//#define TFT_CS 5 // Chip select control pin +//#define TFT_DC 27 // Data Command control pin +//#define TFT_RST 33 // Reset pin (could connect to Arduino RESET pin) +//#define TFT_BL 22 // LED back-light + +//#define TOUCH_CS 21 // Chip select pin (T_CS) of touch screen + +//#define TFT_WR 22 // Write strobe for modified Raspberry Pi TFT only + +// For the M5Stack module use these #define lines +//#define TFT_MISO 19 +//#define TFT_MOSI 23 +//#define TFT_SCLK 18 +//#define TFT_CS 14 // Chip select control pin +//#define TFT_DC 27 // Data Command control pin +//#define TFT_RST 33 // Reset pin (could connect to Arduino RESET pin) +//#define TFT_BL 32 // LED back-light (required for M5Stack) + +// ###### EDIT THE PINs BELOW TO SUIT YOUR ESP32 PARALLEL TFT SETUP ###### + +// The library supports 8-bit parallel TFTs with the ESP32, the pin +// selection below is compatible with ESP32 boards in UNO format. +// Wemos D32 boards need to be modified, see diagram in Tools folder. +// Only ILI9481 and ILI9341 based displays have been tested! + +// Parallel bus is only supported for the STM32 and ESP32 +// Example below is for ESP32 Parallel interface with UNO displays + +// Tell the library to use 8-bit parallel mode (otherwise SPI is assumed) +//#define TFT_PARALLEL_8_BIT + +// The ESP32 and TFT the pins used for testing are: +//#define TFT_CS 33 // Chip select control pin (library pulls permanently low +//#define TFT_DC 15 // Data Command control pin - must use a pin in the range 0-31 +//#define TFT_RST 32 // Reset pin, toggles on startup + +//#define TFT_WR 4 // Write strobe control pin - must use a pin in the range 0-31 +//#define TFT_RD 2 // Read strobe control pin + +//#define TFT_D0 12 // Must use pins in the range 0-31 for the data bus +//#define TFT_D1 13 // so a single register write sets/clears all bits. +//#define TFT_D2 26 // Pins can be randomly assigned, this does not affect +//#define TFT_D3 25 // TFT screen update performance. +//#define TFT_D4 17 +//#define TFT_D5 16 +//#define TFT_D6 27 +//#define TFT_D7 14 + +// ###### EDIT THE PINs BELOW TO SUIT YOUR STM32 SPI TFT SETUP ###### + +// The TFT can be connected to SPI port 1 or 2 +//#define TFT_SPI_PORT 1 // SPI port 1 maximum clock rate is 55MHz +//#define TFT_MOSI PA7 +//#define TFT_MISO PA6 +//#define TFT_SCLK PA5 + +//#define TFT_SPI_PORT 2 // SPI port 2 maximum clock rate is 27MHz +//#define TFT_MOSI PB15 +//#define TFT_MISO PB14 +//#define TFT_SCLK PB13 + +// Can use Ardiuno pin references, arbitrary allocation, TFT_eSPI controls chip select +//#define TFT_CS D5 // Chip select control pin to TFT CS +//#define TFT_DC D6 // Data Command control pin to TFT DC (may be labelled RS = Register Select) +//#define TFT_RST D7 // Reset pin to TFT RST (or RESET) +// OR alternatively, we can use STM32 port reference names PXnn +//#define TFT_CS PE11 // Nucleo-F767ZI equivalent of D5 +//#define TFT_DC PE9 // Nucleo-F767ZI equivalent of D6 +//#define TFT_RST PF13 // Nucleo-F767ZI equivalent of D7 + +//#define TFT_RST -1 // Set TFT_RST to -1 if the display RESET is connected to processor reset + // Use an Arduino pin for initial testing as connecting to processor reset + // may not work (pulse too short at power up?) + +// ################################################################################## +// +// Section 3. Define the fonts that are to be used here +// +// ################################################################################## + +// Comment out the #defines below with // to stop that font being loaded +// The ESP8366 and ESP32 have plenty of memory so commenting out fonts is not +// normally necessary. If all fonts are loaded the extra FLASH space required is +// about 17Kbytes. To save FLASH space only enable the fonts you need! + +#define LOAD_GLCD // Font 1. Original Adafruit 8 pixel font needs ~1820 bytes in FLASH +#define LOAD_FONT2 // Font 2. Small 16 pixel high font, needs ~3534 bytes in FLASH, 96 characters +#define LOAD_FONT4 // Font 4. Medium 26 pixel high font, needs ~5848 bytes in FLASH, 96 characters +#define LOAD_FONT6 // Font 6. Large 48 pixel font, needs ~2666 bytes in FLASH, only characters 1234567890:-.apm +#define LOAD_FONT7 // Font 7. 7 segment 48 pixel font, needs ~2438 bytes in FLASH, only characters 1234567890:-. +#define LOAD_FONT8 // Font 8. Large 75 pixel font needs ~3256 bytes in FLASH, only characters 1234567890:-. +//#define LOAD_FONT8N // Font 8. Alternative to Font 8 above, slightly narrower, so 3 digits fit a 160 pixel TFT +#define LOAD_GFXFF // FreeFonts. Include access to the 48 Adafruit_GFX free fonts FF1 to FF48 and custom fonts + +// Comment out the #define below to stop the SPIFFS filing system and smooth font code being loaded +// this will save ~20kbytes of FLASH +#define SMOOTH_FONT + + +// ################################################################################## +// +// Section 4. Other options +// +// ################################################################################## + +// For RP2040 processor and SPI displays, uncomment the following line to use the PIO interface. +//#define RP2040_PIO_SPI // Leave commented out to use standard RP2040 SPI port interface + +// For RP2040 processor and 8 or 16-bit parallel displays: +// The parallel interface write cycle period is derived from a division of the CPU clock +// speed so scales with the processor clock. This means that the divider ratio may need +// to be increased when overclocking. It may also need to be adjusted dependant on the +// display controller type (ILI94341, HX8357C etc.). If RP2040_PIO_CLK_DIV is not defined +// the library will set default values which may not suit your display. +// The display controller data sheet will specify the minimum write cycle period. The +// controllers often work reliably for shorter periods, however if the period is too short +// the display may not initialise or graphics will become corrupted. +// PIO write cycle frequency = (CPU clock/(4 * RP2040_PIO_CLK_DIV)) +//#define RP2040_PIO_CLK_DIV 1 // 32ns write cycle at 125MHz CPU clock +//#define RP2040_PIO_CLK_DIV 2 // 64ns write cycle at 125MHz CPU clock +//#define RP2040_PIO_CLK_DIV 3 // 96ns write cycle at 125MHz CPU clock + +// For the RP2040 processor define the SPI port channel used (default 0 if undefined) +//#define TFT_SPI_PORT 1 // Set to 0 if SPI0 pins are used, or 1 if spi1 pins used + +// For the STM32 processor define the SPI port channel used (default 1 if undefined) +//#define TFT_SPI_PORT 2 // Set to 1 for SPI port 1, or 2 for SPI port 2 + +// Define the SPI clock frequency, this affects the graphics rendering speed. Too +// fast and the TFT driver will not keep up and display corruption appears. +// With an ILI9341 display 40MHz works OK, 80MHz sometimes fails +// With a ST7735 display more than 27MHz may not work (spurious pixels and lines) +// With an ILI9163 display 27 MHz works OK. + +// #define SPI_FREQUENCY 1000000 +// #define SPI_FREQUENCY 5000000 +// #define SPI_FREQUENCY 10000000 +// #define SPI_FREQUENCY 20000000 +#define SPI_FREQUENCY 27000000 +// #define SPI_FREQUENCY 40000000 +// #define SPI_FREQUENCY 55000000 // STM32 SPI1 only (SPI2 maximum is 27MHz) +// #define SPI_FREQUENCY 80000000 + +// Optional reduced SPI frequency for reading TFT +#define SPI_READ_FREQUENCY 20000000 + +// The XPT2046 requires a lower SPI clock rate of 2.5MHz so we define that here: +#define SPI_TOUCH_FREQUENCY 2500000 + +// The ESP32 has 2 free SPI ports i.e. VSPI and HSPI, the VSPI is the default. +// If the VSPI port is in use and pins are not accessible (e.g. TTGO T-Beam) +// then uncomment the following line: +//#define USE_HSPI_PORT + +// Comment out the following #define if "SPI Transactions" do not need to be +// supported. When commented out the code size will be smaller and sketches will +// run slightly faster, so leave it commented out unless you need it! + +// Transaction support is needed to work with SD library but not needed with TFT_SdFat +// Transaction support is required if other SPI devices are connected. + +// Transactions are automatically enabled by the library for an ESP32 (to use HAL mutex) +// so changing it here has no effect + +// #define SUPPORT_TRANSACTIONS diff --git a/lib/Seeed_GFX/User_Setup_Select.h b/lib/Seeed_GFX/User_Setup_Select.h new file mode 100644 index 0000000..6090025 --- /dev/null +++ b/lib/Seeed_GFX/User_Setup_Select.h @@ -0,0 +1,202 @@ +#ifndef USER_SETUP_LOADED // Lets PlatformIO users define settings in platformio.ini + +// The Arduino build system adds the sketch folder to the compiler include path. +#if __has_include("driver.h") + #include "driver.h" +#endif + +#if !defined(BOARD_SCREEN_COMBO) && defined(OPENDISPLAY_SEEED_GFX) +#define BOARD_SCREEN_COMBO 528 +#endif + +#include + +#endif // USER_SETUP_LOADED + +// Compatible with some examples +#ifdef NRF52840_XXAA +#include +#endif + +///////////////////////////////////////////////////////////////////////////////////// +// // +// DON'T TINKER WITH ANY OF THE FOLLOWING LINES, THESE ADD THE TFT DRIVERS // +// AND ESP8266 PIN DEFINITONS, THEY ARE HERE FOR BODMER'S CONVENIENCE! // +// // +///////////////////////////////////////////////////////////////////////////////////// + + +// Identical looking TFT displays may have a different colour ordering in the 16-bit colour +#define TFT_BGR 0 // Colour order Blue-Green-Red +#define TFT_RGB 1 // Colour order Red-Green-Blue + +// Legacy setup support, RPI_DISPLAY_TYPE replaces RPI_DRIVER +#if defined (RPI_DRIVER) + #if !defined (RPI_DISPLAY_TYPE) + #define RPI_DISPLAY_TYPE + #endif +#endif + +// Legacy setup support, RPI_ILI9486_DRIVER form is deprecated +// Instead define RPI_DISPLAY_TYPE and also define driver (e.g. ILI9486_DRIVER) +#if defined (RPI_ILI9486_DRIVER) + #if !defined (ILI9486_DRIVER) + #define ILI9486_DRIVER + #endif + #if !defined (RPI_DISPLAY_TYPE) + #define RPI_DISPLAY_TYPE + #endif +#endif + +// Invoke 18-bit colour for selected displays +#if !defined (RPI_DISPLAY_TYPE) && !defined (TFT_PARALLEL_8_BIT) && !defined (TFT_PARALLEL_16_BIT) && !defined (ESP32_PARALLEL) + #if defined (ILI9481_DRIVER) || defined (ILI9486_DRIVER) || defined (ILI9488_DRIVER) + #define SPI_18BIT_DRIVER + #endif +#endif + +// Load the right driver definition - do not tinker here ! +#if defined (ILI9341_DRIVER) || defined(ILI9341_2_DRIVER) || defined (ILI9342_DRIVER) + #include + #define TFT_DRIVER 0x9341 +#elif defined (ST7735_DRIVER) + #include + #define TFT_DRIVER 0x7735 +#elif defined (ILI9163_DRIVER) + #include + #define TFT_DRIVER 0x9163 +#elif defined (S6D02A1_DRIVER) + #include + #define TFT_DRIVER 0x6D02 +#elif defined (ST7796_DRIVER) + #include "TFT_Drivers/ST7796_Defines.h" + #define TFT_DRIVER 0x7796 +#elif defined (ILI9486_DRIVER) + #include + #define TFT_DRIVER 0x9486 +#elif defined (ILI9481_DRIVER) + #include + #define TFT_DRIVER 0x9481 +#elif defined (ILI9488_DRIVER) + #include + #define TFT_DRIVER 0x9488 +#elif defined (HX8357D_DRIVER) + #include "TFT_Drivers/HX8357D_Defines.h" + #define TFT_DRIVER 0x8357 +#elif defined (EPD_DRIVER) + #include "TFT_Drivers/EPD_Defines.h" + #define TFT_DRIVER 0xE9D +#elif defined (ST7789_DRIVER) + #include "TFT_Drivers/ST7789_Defines.h" + #define TFT_DRIVER 0x7789 +#elif defined (R61581_DRIVER) + #include "TFT_Drivers/R61581_Defines.h" + #define TFT_DRIVER 0x6158 +#elif defined (ST7789_2_DRIVER) + #include "TFT_Drivers/ST7789_2_Defines.h" + #define TFT_DRIVER 0x778B +#elif defined (RM68140_DRIVER) + #include "TFT_Drivers/RM68140_Defines.h" + #define TFT_DRIVER 0x6814 +#elif defined (SSD1351_DRIVER) + #include "TFT_Drivers/SSD1351_Defines.h" + #define TFT_DRIVER 0x1351 +#elif defined (SSD1963_480_DRIVER) + #include "TFT_Drivers/SSD1963_Defines.h" + #define TFT_DRIVER 0x1963 +#elif defined (SSD1963_800_DRIVER) + #include "TFT_Drivers/SSD1963_Defines.h" + #define TFT_DRIVER 0x1963 +#elif defined (SSD1963_800ALT_DRIVER) + #include "TFT_Drivers/SSD1963_Defines.h" + #define TFT_DRIVER 0x1963 +#elif defined (SSD1963_800BD_DRIVER) + #include "TFT_Drivers/SSD1963_Defines.h" + #define TFT_DRIVER 0x1963 +#elif defined (GC9A01_DRIVER) + #include "TFT_Drivers/GC9A01_Defines.h" + #define TFT_DRIVER 0x9A01 +#elif defined (ILI9225_DRIVER) + #include "TFT_Drivers/ILI9225_Defines.h" + #define TFT_DRIVER 0x9225 +#elif defined (RM68120_DRIVER) + #include "TFT_Drivers/RM68120_Defines.h" + #define TFT_DRIVER 0x6812 +#elif defined (HX8357B_DRIVER) + #include "TFT_Drivers/HX8357B_Defines.h" + #define TFT_DRIVER 0x835B +#elif defined (HX8357C_DRIVER) + #include "TFT_Drivers/HX8357C_Defines.h" + #define TFT_DRIVER 0x835C +#elif defined (UC8179_DRIVER) + #include "TFT_Drivers/UC8179_Defines.h" + #define TFT_DRIVER 0x07507 +#elif defined (SSD1680_DRIVER) + #include "TFT_Drivers/SSD1680_Defines.h" + #define TFT_DRIVER 0x1680 +#elif defined (SSD1681_DRIVER) + #include "TFT_Drivers/SSD1681_Defines.h" + #define TFT_DRIVER 0x1681 +#elif defined (SSD1683_DRIVER) + #include "TFT_Drivers/SSD1683_Defines.h" + #define TFT_DRIVER 0x1683 +#elif defined (SSD1677_DRIVER) + #include "TFT_Drivers/SSD1677_Defines.h" + #define TFT_DRIVER 0x1677 +#elif defined (JD79686B_DRIVER) + #include "TFT_Drivers/JD79686B_Defines.h" + #define TFT_DRIVER 0x7968 +#elif defined (ED2208_DRIVER) + #include "TFT_Drivers/ED2208_Defines.h" + #define TFT_DRIVER 0x2208 +#elif defined (T133A01_DRIVER) + #include "TFT_Drivers/T133A01_Defines.h" + #define TFT_DRIVER 0x1330 +#elif defined (ED103TC2_DRIVER) + #include "TFT_Drivers/IT8951_Defines.h" + #include "TFT_Drivers/ED103TC2_Defines.h" + #define TFT_DRIVER 0x1032 +#elif defined (JD79667_DRIVER) + #include "TFT_Drivers/JD79667_Defines.h" + #define TFT_DRIVER 0x2904 +#elif defined (JD79676_DRIVER) + #include "TFT_Drivers/JD79676_Defines.h" + #define TFT_DRIVER 0x2134 +#elif defined (SSD2677_DRIVER) + #include "TFT_Drivers/SSD2677_Defines.h" + #define TFT_DRIVER 0x2677 +#elif defined (JD79660_DRIVER) + #include "TFT_Drivers/JD79660_Defines.h" + #define TFT_DRIVER 0x1544 + // <<<<<<<<<<<<<<<<<<<<<<<< ADD NEW DRIVER HERE + // XYZZY_init.h and XYZZY_rotation.h must also be added in TFT_eSPI.cpp +#elif defined (XYZZY_DRIVER) + #include "TFT_Drivers/XYZZY_Defines.h" + #define TFT_DRIVER 0x0000 +#else + #define TFT_DRIVER 0x0000 +#endif + +#include "Touch_Drivers/Touch_Defines.h" + +// These are the pins for ESP8266 boards +// Name GPIO NodeMCU Function +#define PIN_D0 16 // GPIO16 WAKE +#define PIN_D1 5 // GPIO5 User purpose +#define PIN_D2 4 // GPIO4 User purpose +#define PIN_D3 0 // GPIO0 Low on boot means enter FLASH mode +#define PIN_D4 2 // GPIO2 TXD1 (must be high on boot to go to UART0 FLASH mode) +#define PIN_D5 14 // GPIO14 HSCLK +#define PIN_D6 12 // GPIO12 HMISO +#define PIN_D7 13 // GPIO13 HMOSI RXD2 +#define PIN_D8 15 // GPIO15 HCS TXD0 (must be low on boot to enter UART0 FLASH mode) +#define PIN_D9 3 // RXD0 +#define PIN_D10 1 // TXD0 + +#define PIN_MOSI 8 // SD1 FLASH and overlap mode +#define PIN_MISO 7 // SD0 +#define PIN_SCLK 6 // CLK +#define PIN_HWCS 0 // D3 + +#define PIN_D11 9 // SD2 +#define PIN_D12 10 // SD4 diff --git a/lib/Seeed_GFX/User_Setups/Dynamic_Setup.h b/lib/Seeed_GFX/User_Setups/Dynamic_Setup.h new file mode 100644 index 0000000..84d2be1 --- /dev/null +++ b/lib/Seeed_GFX/User_Setups/Dynamic_Setup.h @@ -0,0 +1,393 @@ +#ifndef DYNAMIC_SETUP_H +#define DYNAMIC_SETUP_H + + +#if defined(ARDUINO_SEEED_XIAO_RA4M1) && !defined(SEEED_XIAO_RA4M1_SERIAL_PRINTF_SHIM) + #include + #include + #include + #include + + class SeeedSerialPrintfShim { + public: + explicit SeeedSerialPrintfShim(_SerialUSB &serial) : port(serial) {} + + size_t printf(const char *format, ...) { + va_list args; + va_start(args, format); + va_list args_copy; + va_copy(args_copy, args); + int needed = vsnprintf(nullptr, 0, format, args); + va_end(args); + + if (needed <= 0) { + va_end(args_copy); + return 0; + } + + size_t buffer_len = static_cast(needed) + 1U; + char stack_buffer[128]; + char *buffer = buffer_len <= sizeof(stack_buffer) + ? stack_buffer + : static_cast(malloc(buffer_len)); + + if (!buffer) { + va_end(args_copy); + return 0; + } + + vsnprintf(buffer, buffer_len, format, args_copy); + va_end(args_copy); + + size_t written = port.write(reinterpret_cast(buffer), needed); + + if (buffer != stack_buffer) { + free(buffer); + } + return written; + } + + _SerialUSB *operator->() { return &port; } + operator _SerialUSB &() { return port; } + operator bool() { return static_cast(port); } + + private: + _SerialUSB &port; + }; + + static SeeedSerialPrintfShim SerialShim(Serial); + #undef Serial + #define Serial SerialShim + #define SEEED_XIAO_RA4M1_SERIAL_PRINTF_SHIM +#endif + +#ifndef BOARD_SCREEN_COMBO + // Pick a sensible default based on the detected board so that CI targets + // without built-in LCD pin macros (e.g. the XIAO family) still compile. + #if defined(SEEED_WIO_TERMINAL) || defined(ARDUINO_SEEED_WIO_TERMINAL) + #define BOARD_SCREEN_COMBO 500 + #elif defined(SEEED_XIAO_M0) || defined(ARDUINO_SEEED_XIAO_M0) || \ + defined(ARDUINO_XIAO_RA4M1) || defined(ARDUINO_SEEED_XIAO_RA4M1) || \ + defined(ARDUINO_SEEED_XIAO_NRF52840) || defined(ARDUINO_SEEED_XIAO_NRF52840_SENSE) || \ + defined(ARDUINO_Seeed_XIAO_nRF52840) || defined(ARDUINO_Seeed_XIAO_nRF52840_Sense) || \ + defined(ARDUINO_SEEED_XIAO_NRF52840_Plus) || defined(ARDUINO_SEEED_XIAO_NRF52840_Sense_Plus) || \ + defined(ARDUINO_Seeed_XIAO_nRF52840_Plus) || defined(ARDUINO_Seeed_XIAO_nRF52840_Sense_Plus) || \ + defined(ARDUINO_SEEED_XIAO_RP2040) || defined(ARDUINO_SEEED_XIAO_RP2350) || \ + defined(ARDUINO_XIAO_ESP32S3) || defined(ARDUINO_XIAO_ESP32S3_PLUS) || \ + defined(ARDUINO_XIAO_ESP32C3) || defined(ARDUINO_XIAO_ESP32C6) || \ + defined(ARDUINO_XIAO_ESP32S3_PRO) || defined(ARDUINO_XIAO_ESP32S3_PROSENSE) || \ + defined(EFR32MG24B220F1536IM48) + #define BOARD_SCREEN_COMBO 666 + #elif defined(SEEED_XIAO_ROUND_DISPLAY) || defined(ARDUINO_SEEED_XIAO_ROUND_DISPLAY) + #define BOARD_SCREEN_COMBO 501 + #else + // Default to Wio Terminal behaviour if no better match is known. + #define BOARD_SCREEN_COMBO 500 + #endif +#endif + + + +#if BOARD_SCREEN_COMBO == 1 +#include +#elif BOARD_SCREEN_COMBO == 2 +#include +#elif BOARD_SCREEN_COMBO == 3 +#include +#elif BOARD_SCREEN_COMBO == 4 +#include +#elif BOARD_SCREEN_COMBO == 5 +#include +#elif BOARD_SCREEN_COMBO == 6 +#include +#elif BOARD_SCREEN_COMBO == 7 +#include +#elif BOARD_SCREEN_COMBO == 8 +#include +#elif BOARD_SCREEN_COMBO == 9 +#include +#elif BOARD_SCREEN_COMBO == 10 +#include +#elif BOARD_SCREEN_COMBO == 11 +#include +#elif BOARD_SCREEN_COMBO == 12 +#include +#elif BOARD_SCREEN_COMBO == 13 +#include +#elif BOARD_SCREEN_COMBO == 14 +#include +#elif BOARD_SCREEN_COMBO == 15 +#include +#elif BOARD_SCREEN_COMBO == 16 +#include +#elif BOARD_SCREEN_COMBO == 17 +#include + +#elif BOARD_SCREEN_COMBO == 18 +#include +#elif BOARD_SCREEN_COMBO == 19 +#include +#elif BOARD_SCREEN_COMBO == 20 +#include +#elif BOARD_SCREEN_COMBO == 21 +#include +#elif BOARD_SCREEN_COMBO == 22 +#include +#elif BOARD_SCREEN_COMBO == 23 +#include +#elif BOARD_SCREEN_COMBO == 24 +#include +#elif BOARD_SCREEN_COMBO == 25 +#include +#elif BOARD_SCREEN_COMBO == 26 +#include +#elif BOARD_SCREEN_COMBO == 27 +#include +#elif BOARD_SCREEN_COMBO == 28 +#include + +#elif BOARD_SCREEN_COMBO == 29 +#include +#elif BOARD_SCREEN_COMBO == 30 +#include +#elif BOARD_SCREEN_COMBO == 31 +#include +#elif BOARD_SCREEN_COMBO == 32 +#include + +#elif BOARD_SCREEN_COMBO == 33 +#include + +#elif BOARD_SCREEN_COMBO == 34 +#include +#elif BOARD_SCREEN_COMBO == 35 +#include + +#elif BOARD_SCREEN_COMBO == 36 +#include + +#elif BOARD_SCREEN_COMBO == 42 +#include +#elif BOARD_SCREEN_COMBO == 43 +#include +#elif BOARD_SCREEN_COMBO == 44 +#include +#elif BOARD_SCREEN_COMBO == 45 +#include +#elif BOARD_SCREEN_COMBO == 46 +#include +#elif BOARD_SCREEN_COMBO == 47 +#include + +#elif BOARD_SCREEN_COMBO == 50 +#include +#elif BOARD_SCREEN_COMBO == 51 +#include +#elif BOARD_SCREEN_COMBO == 52 +#include + +#elif BOARD_SCREEN_COMBO == 60 +#include +#elif BOARD_SCREEN_COMBO == 61 +#include +#elif BOARD_SCREEN_COMBO == 62 +#include +#elif BOARD_SCREEN_COMBO == 66 +#include + +#elif BOARD_SCREEN_COMBO == 70 +#include +#elif BOARD_SCREEN_COMBO == 71 // Remapped from 70b for simplicity +#include +#elif BOARD_SCREEN_COMBO == 72 // Remapped from 70c +#include +#elif BOARD_SCREEN_COMBO == 73 // Remapped from 70d +#include +#elif BOARD_SCREEN_COMBO == 74 // Remapped from 71 +#include +#elif BOARD_SCREEN_COMBO == 75 // Remapped from 72 +#include + +#elif BOARD_SCREEN_COMBO == 100 +#include +#elif BOARD_SCREEN_COMBO == 101 +#include +#elif BOARD_SCREEN_COMBO == 102 +#include +#elif BOARD_SCREEN_COMBO == 103 +#include +#elif BOARD_SCREEN_COMBO == 104 +#include +#elif BOARD_SCREEN_COMBO == 105 +#include +#elif BOARD_SCREEN_COMBO == 106 +#include +#elif BOARD_SCREEN_COMBO == 107 +#include +#elif BOARD_SCREEN_COMBO == 108 +#include + +#elif BOARD_SCREEN_COMBO == 135 +#include +#elif BOARD_SCREEN_COMBO == 136 +#include +#elif BOARD_SCREEN_COMBO == 137 +#include +#elif BOARD_SCREEN_COMBO == 138 +#include + +#elif BOARD_SCREEN_COMBO == 200 +#include +#elif BOARD_SCREEN_COMBO == 201 +#include +#elif BOARD_SCREEN_COMBO == 202 +#include +#elif BOARD_SCREEN_COMBO == 203 +#include +#elif BOARD_SCREEN_COMBO == 204 +#include +#elif BOARD_SCREEN_COMBO == 205 +#include +#elif BOARD_SCREEN_COMBO == 206 +#include +#elif BOARD_SCREEN_COMBO == 207 +#include +#elif BOARD_SCREEN_COMBO == 209 +#include +#elif BOARD_SCREEN_COMBO == 210 +#include +#elif BOARD_SCREEN_COMBO == 211 +#include +#elif BOARD_SCREEN_COMBO == 212 +#include +#elif BOARD_SCREEN_COMBO == 213 +#include + +#elif BOARD_SCREEN_COMBO == 250 +#include +#elif BOARD_SCREEN_COMBO == 251 +#include + +#elif BOARD_SCREEN_COMBO == 301 +#include +#elif BOARD_SCREEN_COMBO == 302 +#include + +#elif BOARD_SCREEN_COMBO == 500 +#include +#elif BOARD_SCREEN_COMBO == 501 +#include +#elif BOARD_SCREEN_COMBO == 502 +#define ENABLE_EPAPER_BOARD_PIN_SETUPS +#include +#elif BOARD_SCREEN_COMBO == 503 +#define ENABLE_EPAPER_BOARD_PIN_SETUPS +#include +#elif BOARD_SCREEN_COMBO == 504 +#define ENABLE_EPAPER_BOARD_PIN_SETUPS +#include +#elif BOARD_SCREEN_COMBO == 505 +#define ENABLE_EPAPER_BOARD_PIN_SETUPS +#include +#elif BOARD_SCREEN_COMBO == 506 +#define ENABLE_EPAPER_BOARD_PIN_SETUPS +#include +#elif BOARD_SCREEN_COMBO == 507 +#define ENABLE_EPAPER_BOARD_PIN_SETUPS +#include +#elif BOARD_SCREEN_COMBO == 508 +#define ENABLE_EPAPER_BOARD_PIN_SETUPS +#include +#elif BOARD_SCREEN_COMBO == 509 +#define USE_COLORFULL_EPAPER +#define ENABLE_EPAPER_BOARD_PIN_SETUPS +#include // Setup file for Seeed XIAO with 7.3inch colorful ePaper +#elif BOARD_SCREEN_COMBO == 510 +#define USE_COLORFULL_EPAPER +#define ENABLE_EPAPER_BOARD_PIN_SETUPS +#include // Setup file for Seeed XIAO with 13.3inch colorful ePaper +#elif BOARD_SCREEN_COMBO == 511 +#define TCON_ENABLE +#define ENABLE_EPAPER_BOARD_PIN_SETUPS +#include // Setup file for Seeed XIAO with 10.3inch ePaper +#elif BOARD_SCREEN_COMBO == 512 +#define USE_BWRY_EPAPER +#define ENABLE_EPAPER_BOARD_PIN_SETUPS +#include +#elif BOARD_SCREEN_COMBO == 513 +#define USE_BWRY_EPAPER +#define ENABLE_EPAPER_BOARD_PIN_SETUPS +#include +#elif BOARD_SCREEN_COMBO == 514 +#define USE_COLORFULL_EPAPER +#define ENABLE_EPAPER_BOARD_PIN_SETUPS +#include // Setup file for Seeed XIAO with 4.0inch colorful ePaper +#elif BOARD_SCREEN_COMBO == 515 +#define ENABLE_EPAPER_BOARD_PIN_SETUPS +#include +#elif BOARD_SCREEN_COMBO == 516 +#define ENABLE_EPAPER_BOARD_PIN_SETUPS +#define USE_COLORFULL_EPAPER +#include +#elif BOARD_SCREEN_COMBO == 517 +#define ENABLE_EPAPER_BOARD_PIN_SETUPS +#define USE_COLORFULL_EPAPER +#include +#elif BOARD_SCREEN_COMBO == 520 +#define ENABLE_EPAPER_BOARD_PIN_SETUPS +#include +#elif BOARD_SCREEN_COMBO == 521 +#define USE_COLORFULL_EPAPER +#define ENABLE_EPAPER_BOARD_PIN_SETUPS +#include +#elif BOARD_SCREEN_COMBO == 522 +#define TCON_ENABLE +#define ENABLE_EPAPER_BOARD_PIN_SETUPS +#include +#elif BOARD_SCREEN_COMBO == 528 +#define TCON_ENABLE +#define OPENDISPLAY_SEEED_GFX_RUNTIME_PINS +#include +#elif BOARD_SCREEN_COMBO == 523 +#define USE_COLORFULL_EPAPER +#define ENABLE_EPAPER_BOARD_PIN_SETUPS +#include +#elif BOARD_SCREEN_COMBO == 524 +#define ENABLE_EPAPER_BOARD_PIN_SETUPS +#include +#elif BOARD_SCREEN_COMBO == 525 +#define USE_COLORFULL_EPAPER +#define ENABLE_EPAPER_BOARD_PIN_SETUPS +#include +#elif BOARD_SCREEN_COMBO == 666 +#include + + +#elif BOARD_SCREEN_COMBO == 901 +#include +#elif BOARD_SCREEN_COMBO == 902 +#include +#elif BOARD_SCREEN_COMBO == 903 +#include +#elif BOARD_SCREEN_COMBO == 904 +#include +#elif BOARD_SCREEN_COMBO == 905 +#include +#elif BOARD_SCREEN_COMBO == 906 +#include +#elif BOARD_SCREEN_COMBO == 907 +#include +#elif BOARD_SCREEN_COMBO == 908 +#include +#elif BOARD_SCREEN_COMBO == 999 +#include + + +#else + // If BOARD_SCREEN_COMBO was defined but did not match any of the above IDs + #error "The provided BOARD_SCREEN_COMBO ID is not a valid or supported configuration." +#endif + + + +#endif diff --git a/lib/Seeed_GFX/User_Setups/EPaper_Board_Pins_Setups.h b/lib/Seeed_GFX/User_Setups/EPaper_Board_Pins_Setups.h new file mode 100644 index 0000000..bd802db --- /dev/null +++ b/lib/Seeed_GFX/User_Setups/EPaper_Board_Pins_Setups.h @@ -0,0 +1,73 @@ + +#if defined(USE_XIAO_EPAPER_DRIVER_BOARD) +#define TFT_SCLK D8 +#define TFT_MISO D9 +#define TFT_MOSI D10 +#define TFT_CS D1 +#define TFT_DC D3 +#define TFT_BUSY D2 +#define TFT_RST D0 +#elif defined(USE_XIAO_EPAPER_BREAKOUT_BOARD) +#define TFT_SCLK D8 +#define TFT_MISO D9 +#define TFT_MOSI D10 +#define TFT_CS D1 +#define TFT_DC D3 +#define TFT_BUSY D5 +#define TFT_RST D0 +#elif defined(USE_XIAO_EPAPER_DISPLAY_BOARD_EE02) +#define TFT_SCLK D8 +#define TFT_MISO -1 +#define TFT_MOSI D10 +#define TFT_CS 44 // D7 +#define TFT_CS1 41 // D8 +#define TFT_DC 10 // D16 +#define TFT_BUSY 4 // D3 +#define TFT_RST 38 // D11 +#define TFT_ENABLE 43 +#elif defined(USE_XIAO_EPAPER_DISPLAY_BOARD_EE03) +#define TFT_SCLK D8 +#define TFT_MISO D9 +#define TFT_MOSI D10 +#define TFT_CS 44 // D7 +#define TFT_DC -1 // D16 +#define TFT_BUSY 4 // D3 +#define TFT_RST 38 // D11 +#define TFT_ENABLE 43 +#elif defined(USE_XIAO_EPAPER_DISPLAY_BOARD_EE04) +#define TFT_SCLK D8 +#define TFT_MISO -1 +#define TFT_MOSI D10 +#define TFT_CS 44 // D7 +#define TFT_DC 10 // D16 +#define TFT_BUSY 4 // D3 +#define TFT_RST 38 // D11 +#define TFT_ENABLE 43 //D6 +#elif defined(USE_XIAO_EPAPER_DISPLAY_BOARD_EE05) +#define TFT_SCLK D8 +#define TFT_MISO -1 +#define TFT_MOSI D10 +#define TFT_CS 44 // D7 +#define TFT_DC 10 // D16 +#define TFT_BUSY 4 // D3 +#define TFT_RST 38 // D11 +#define TFT_ENABLE 43 //D6 +#elif defined(USE_XIAO_EPAPER_DISPLAY_BOARD_EN04) +#define TFT_SCLK D8 +#define TFT_MISO -1 +#define TFT_MOSI D10 +#define TFT_CS D7 // D7 +#define TFT_DC D16 // D16 +#define TFT_BUSY D3 // D3 +#define TFT_RST D11 // D11 +#define TFT_ENABLE D6 //D6 +#elif defined(USE_XIAO_EPAPER_DISPLAY_BOARD_EN05) +#define TFT_SCLK D8 +#define TFT_MISO -1 +#define TFT_MOSI D10 +#define TFT_CS D7 // D7 +#define TFT_DC D16 // D16 +#define TFT_BUSY D3 // D3 +#define TFT_RST D11 // D11 +#define TFT_ENABLE D6 //D6 +#endif \ No newline at end of file diff --git a/lib/Seeed_GFX/User_Setups/Setup520_Seeed_reTerminal_E1001.h b/lib/Seeed_GFX/User_Setups/Setup520_Seeed_reTerminal_E1001.h new file mode 100644 index 0000000..b96631f --- /dev/null +++ b/lib/Seeed_GFX/User_Setups/Setup520_Seeed_reTerminal_E1001.h @@ -0,0 +1,39 @@ +#include + +#define USER_SETUP_ID 520 + +#define UC8179_DRIVER + +#define EPAPER_ENABLE + +#define TFT_WIDTH 800 +#define TFT_HEIGHT 480 + +#define EPD_WIDTH TFT_WIDTH +#define EPD_HEIGHT TFT_HEIGHT + +// #define EPD_HORIZONTAL_MIRROR + +#define TFT_SCLK 7 +#define TFT_MISO -1 +#define TFT_MOSI 9 +#define TFT_CS 10 // D16 +#define TFT_DC 11 // D19 +#define TFT_BUSY 13 // D17 +#define TFT_RST 12 // D18 + +#define LOAD_GLCD // Font 1. Original Adafruit 8 pixel font needs ~1820 bytes in FLASH +#define LOAD_FONT2 // Font 2. Small 16 pixel high font, needs ~3534 bytes in FLASH, 96 characters +#define LOAD_FONT4 // Font 4. Medium 26 pixel high font, needs ~5848 bytes in FLASH, 96 characters +#define LOAD_FONT6 // Font 6. Large 48 pixel font, needs ~2666 bytes in FLASH, only characters 1234567890:-.apm +#define LOAD_FONT7 // Font 7. 7 segment 48 pixel font, needs ~2438 bytes in FLASH, only characters 1234567890:-. +#define LOAD_FONT8 // Font 8. Large 75 pixel font needs ~3256 bytes in FLASH, only characters 1234567890:-. +// #define LOAD_FONT8N // Font 8. Alternative to Font 8 above, slightly narrower, so 3 digits fit a 160 pixel TFT +#define LOAD_GFXFF // FreeFonts. Include access to the 48 Adafruit_GFX free fonts FF1 to FF48 and custom fonts + +#define SMOOTH_FONT + +#ifdef CONFIG_IDF_TARGET_ESP32S3 +#define USE_HSPI_PORT +#endif +#include "XIAO_SPI_Frequency.h" \ No newline at end of file diff --git a/lib/Seeed_GFX/User_Setups/Setup521_Seeed_reTerminal_E1002.h b/lib/Seeed_GFX/User_Setups/Setup521_Seeed_reTerminal_E1002.h new file mode 100644 index 0000000..0273a23 --- /dev/null +++ b/lib/Seeed_GFX/User_Setups/Setup521_Seeed_reTerminal_E1002.h @@ -0,0 +1,44 @@ +#include + +#define USER_SETUP_ID 521 + +#define ED2208_DRIVER + + +#define EPAPER_ENABLE + +#define TFT_WIDTH 800 +#define TFT_HEIGHT 480 + +#define EPD_WIDTH TFT_WIDTH +#define EPD_HEIGHT TFT_HEIGHT + +// #define EPD_HORIZONTAL_MIRROR + + +#define TFT_SCLK 7 +#define TFT_MISO -1 +#define TFT_MOSI 9 +#define TFT_CS 10 // D7 +#define TFT_DC 11 // D16 +#define TFT_BUSY 13 // D3 +#define TFT_RST 12 // D11 + + + + +#define LOAD_GLCD // Font 1. Original Adafruit 8 pixel font needs ~1820 bytes in FLASH +#define LOAD_FONT2 // Font 2. Small 16 pixel high font, needs ~3534 bytes in FLASH, 96 characters +#define LOAD_FONT4 // Font 4. Medium 26 pixel high font, needs ~5848 bytes in FLASH, 96 characters +#define LOAD_FONT6 // Font 6. Large 48 pixel font, needs ~2666 bytes in FLASH, only characters 1234567890:-.apm +#define LOAD_FONT7 // Font 7. 7 segment 48 pixel font, needs ~2438 bytes in FLASH, only characters 1234567890:-. +#define LOAD_FONT8 // Font 8. Large 75 pixel font needs ~3256 bytes in FLASH, only characters 1234567890:-. +// #define LOAD_FONT8N // Font 8. Alternative to Font 8 above, slightly narrower, so 3 digits fit a 160 pixel TFT +#define LOAD_GFXFF // FreeFonts. Include access to the 48 Adafruit_GFX free fonts FF1 to FF48 and custom fonts + +#define SMOOTH_FONT + +#ifdef CONFIG_IDF_TARGET_ESP32S3 +#define USE_HSPI_PORT +#endif +#include "XIAO_SPI_Frequency.h" \ No newline at end of file diff --git a/lib/Seeed_GFX/User_Setups/Setup522_Seeed_reTerminal_E1003.h b/lib/Seeed_GFX/User_Setups/Setup522_Seeed_reTerminal_E1003.h new file mode 100644 index 0000000..76b40c7 --- /dev/null +++ b/lib/Seeed_GFX/User_Setups/Setup522_Seeed_reTerminal_E1003.h @@ -0,0 +1,45 @@ +#include + +#define USER_SETUP_ID 522 + +#define ED103TC2_DRIVER + + +#define EPAPER_ENABLE + +#define TFT_WIDTH 1872 +#define TFT_HEIGHT 1404 + +#define EPD_WIDTH TFT_WIDTH +#define EPD_HEIGHT TFT_HEIGHT + +// #define EPD_HORIZONTAL_MIRROR + + +#define TFT_SCLK 7 +#define TFT_MISO 8 +#define TFT_MOSI 9 +#define TFT_CS 10 +#define TFT_DC -1 +#define TFT_BUSY 13 +#define TFT_RST 12 +#define TFT_ENABLE 11 +#define ITE_ENABLE 21 + + + +#define LOAD_GLCD // Font 1. Original Adafruit 8 pixel font needs ~1820 bytes in FLASH +#define LOAD_FONT2 // Font 2. Small 16 pixel high font, needs ~3534 bytes in FLASH, 96 characters +#define LOAD_FONT4 // Font 4. Medium 26 pixel high font, needs ~5848 bytes in FLASH, 96 characters +#define LOAD_FONT6 // Font 6. Large 48 pixel font, needs ~2666 bytes in FLASH, only characters 1234567890:-.apm +#define LOAD_FONT7 // Font 7. 7 segment 48 pixel font, needs ~2438 bytes in FLASH, only characters 1234567890:-. +#define LOAD_FONT8 // Font 8. Large 75 pixel font needs ~3256 bytes in FLASH, only characters 1234567890:-. +// #define LOAD_FONT8N // Font 8. Alternative to Font 8 above, slightly narrower, so 3 digits fit a 160 pixel TFT +#define LOAD_GFXFF // FreeFonts. Include access to the 48 Adafruit_GFX free fonts FF1 to FF48 and custom fonts + +#define SMOOTH_FONT + +#ifdef CONFIG_IDF_TARGET_ESP32S3 +#define USE_HSPI_PORT +#endif +#include "XIAO_SPI_Frequency.h" \ No newline at end of file diff --git a/lib/Seeed_GFX/User_Setups/Setup523_Seeed_reTerminal_E1004.h b/lib/Seeed_GFX/User_Setups/Setup523_Seeed_reTerminal_E1004.h new file mode 100644 index 0000000..e553d48 --- /dev/null +++ b/lib/Seeed_GFX/User_Setups/Setup523_Seeed_reTerminal_E1004.h @@ -0,0 +1,46 @@ +#include + +#define USER_SETUP_ID 523 + +#define T133A01_DRIVER + + +#define EPAPER_ENABLE + +#define TFT_WIDTH 1200 +#define TFT_HEIGHT 1600 + +#define EPD_WIDTH TFT_WIDTH +#define EPD_HEIGHT TFT_HEIGHT + +// #define EPD_HORIZONTAL_MIRROR + + +#define TFT_SCLK 7 +#define TFT_MISO 8 +#define TFT_MOSI 9 +#define TFT_CS 10 // D7 +#define TFT_CS1 2 // D7 +#define TFT_DC 11 // D16 +#define TFT_BUSY 13 // D3 +#define TFT_RST 38 +#define TFT_ENABLE 12 // D11 + + + + +#define LOAD_GLCD // Font 1. Original Adafruit 8 pixel font needs ~1820 bytes in FLASH +#define LOAD_FONT2 // Font 2. Small 16 pixel high font, needs ~3534 bytes in FLASH, 96 characters +#define LOAD_FONT4 // Font 4. Medium 26 pixel high font, needs ~5848 bytes in FLASH, 96 characters +#define LOAD_FONT6 // Font 6. Large 48 pixel font, needs ~2666 bytes in FLASH, only characters 1234567890:-.apm +#define LOAD_FONT7 // Font 7. 7 segment 48 pixel font, needs ~2438 bytes in FLASH, only characters 1234567890:-. +#define LOAD_FONT8 // Font 8. Large 75 pixel font needs ~3256 bytes in FLASH, only characters 1234567890:-. +// #define LOAD_FONT8N // Font 8. Alternative to Font 8 above, slightly narrower, so 3 digits fit a 160 pixel TFT +#define LOAD_GFXFF // FreeFonts. Include access to the 48 Adafruit_GFX free fonts FF1 to FF48 and custom fonts + +#define SMOOTH_FONT + +#ifdef CONFIG_IDF_TARGET_ESP32S3 +#define USE_HSPI_PORT +#endif +#include "XIAO_SPI_Frequency.h" \ No newline at end of file diff --git a/lib/Seeed_GFX/User_Setups/Setup524_Seeed_reTerminal_E1005.h b/lib/Seeed_GFX/User_Setups/Setup524_Seeed_reTerminal_E1005.h new file mode 100644 index 0000000..e678599 --- /dev/null +++ b/lib/Seeed_GFX/User_Setups/Setup524_Seeed_reTerminal_E1005.h @@ -0,0 +1,45 @@ +#include + +#define USER_SETUP_ID 524 + +#define SSD1677_DRIVER + + +#define EPAPER_ENABLE + +#define TFT_WIDTH 800 +#define TFT_HEIGHT 480 + +#define EPD_WIDTH TFT_WIDTH +#define EPD_HEIGHT TFT_HEIGHT + +#define EPD_HORIZONTAL_MIRROR + + +#define TFT_SCLK 13 +#define TFT_MISO -1 +#define TFT_MOSI 14 +#define TFT_CS 15 // D7 +#define TFT_DC 16 // D16 +#define TFT_BUSY 18 // D3 +#define TFT_RST 17 +#define TFT_ENABLE 47 // D11 + + + + +#define LOAD_GLCD // Font 1. Original Adafruit 8 pixel font needs ~1820 bytes in FLASH +#define LOAD_FONT2 // Font 2. Small 16 pixel high font, needs ~3534 bytes in FLASH, 96 characters +#define LOAD_FONT4 // Font 4. Medium 26 pixel high font, needs ~5848 bytes in FLASH, 96 characters +#define LOAD_FONT6 // Font 6. Large 48 pixel font, needs ~2666 bytes in FLASH, only characters 1234567890:-.apm +#define LOAD_FONT7 // Font 7. 7 segment 48 pixel font, needs ~2438 bytes in FLASH, only characters 1234567890:-. +#define LOAD_FONT8 // Font 8. Large 75 pixel font needs ~3256 bytes in FLASH, only characters 1234567890:-. +// #define LOAD_FONT8N // Font 8. Alternative to Font 8 above, slightly narrower, so 3 digits fit a 160 pixel TFT +#define LOAD_GFXFF // FreeFonts. Include access to the 48 Adafruit_GFX free fonts FF1 to FF48 and custom fonts + +#define SMOOTH_FONT + +#ifdef CONFIG_IDF_TARGET_ESP32S3 +#define USE_HSPI_PORT +#endif +#include "XIAO_SPI_Frequency.h" \ No newline at end of file diff --git a/lib/Seeed_GFX/User_Setups/Setup525_Seeed_reTerminal_E1005_BWRY.h b/lib/Seeed_GFX/User_Setups/Setup525_Seeed_reTerminal_E1005_BWRY.h new file mode 100644 index 0000000..1e128f0 --- /dev/null +++ b/lib/Seeed_GFX/User_Setups/Setup525_Seeed_reTerminal_E1005_BWRY.h @@ -0,0 +1,45 @@ +#include + +#define USER_SETUP_ID 524 + +#define SSD2677_DRIVER + + +#define EPAPER_ENABLE + +#define TFT_WIDTH 800 +#define TFT_HEIGHT 480 + +#define EPD_WIDTH TFT_WIDTH +#define EPD_HEIGHT TFT_HEIGHT + +// #define EPD_HORIZONTAL_MIRROR + + +#define TFT_SCLK 13 +#define TFT_MISO -1 +#define TFT_MOSI 14 +#define TFT_CS 15 // D7 +#define TFT_DC 16 // D16 +#define TFT_BUSY 18 // D3 +#define TFT_RST 17 +#define TFT_ENABLE 47 // D11 + + + + +#define LOAD_GLCD // Font 1. Original Adafruit 8 pixel font needs ~1820 bytes in FLASH +#define LOAD_FONT2 // Font 2. Small 16 pixel high font, needs ~3534 bytes in FLASH, 96 characters +#define LOAD_FONT4 // Font 4. Medium 26 pixel high font, needs ~5848 bytes in FLASH, 96 characters +#define LOAD_FONT6 // Font 6. Large 48 pixel font, needs ~2666 bytes in FLASH, only characters 1234567890:-.apm +#define LOAD_FONT7 // Font 7. 7 segment 48 pixel font, needs ~2438 bytes in FLASH, only characters 1234567890:-. +#define LOAD_FONT8 // Font 8. Large 75 pixel font needs ~3256 bytes in FLASH, only characters 1234567890:-. +// #define LOAD_FONT8N // Font 8. Alternative to Font 8 above, slightly narrower, so 3 digits fit a 160 pixel TFT +#define LOAD_GFXFF // FreeFonts. Include access to the 48 Adafruit_GFX free fonts FF1 to FF48 and custom fonts + +#define SMOOTH_FONT + +#ifdef CONFIG_IDF_TARGET_ESP32S3 +#define USE_HSPI_PORT +#endif +#include "XIAO_SPI_Frequency.h" \ No newline at end of file diff --git a/lib/Seeed_GFX/User_Setups/Setup528_OpenDisplay_Runtime_EPaper.h b/lib/Seeed_GFX/User_Setups/Setup528_OpenDisplay_Runtime_EPaper.h new file mode 100644 index 0000000..44f7f69 --- /dev/null +++ b/lib/Seeed_GFX/User_Setups/Setup528_OpenDisplay_Runtime_EPaper.h @@ -0,0 +1,34 @@ +// OpenDisplay: ED103TC2 + IT8951 + TCON. SPI/control pins come from device config at runtime +// (see display_seeed_gfx.cpp). Values here satisfy TFT_eSPI #if checks only. + +#include + +#define USER_SETUP_ID 528 + +#define DISABLE_ALL_LIBRARY_WARNINGS + +#define TCON_ENABLE +#define ED103TC2_DRIVER +#define EPAPER_ENABLE + +#define TFT_WIDTH 1872 +#define TFT_HEIGHT 1404 + +#define EPD_WIDTH TFT_WIDTH +#define EPD_HEIGHT TFT_HEIGHT + +#define TFT_SCLK 7 +#define TFT_MISO 8 +#define TFT_MOSI 9 +#define TFT_CS 10 +#define TFT_DC (-1) +#define TFT_BUSY 13 +#define TFT_RST 12 +#define TFT_ENABLE 11 +#define ITE_ENABLE 21 + +#if defined(CONFIG_IDF_TARGET_ESP32S3) +#define USE_HSPI_PORT +#endif + +#include "XIAO_SPI_Frequency.h" diff --git a/lib/Seeed_GFX/User_Setups/Setup666_XIAO_ILI9341.h b/lib/Seeed_GFX/User_Setups/Setup666_XIAO_ILI9341.h new file mode 100644 index 0000000..af68ff1 --- /dev/null +++ b/lib/Seeed_GFX/User_Setups/Setup666_XIAO_ILI9341.h @@ -0,0 +1,72 @@ +#include + +#define USER_SETUP_ID 666 + +#define ILI9341_DRIVER + +// #define TFT_INVERSION_ON +#define TFT_RGB_ORDER TFT_RBG // Colour order Blue-Green-Red +// #define TFT_WIDTH 240 +// #define TFT_HEIGHT 240 // GC9A01 240 x 240 + +#define TFT_SCLK D8 +#define TFT_MISO D9 +#define TFT_MOSI D10 + +#define TFT_CS D1 // Chip select control pin +#define TFT_DC D3 // Data Command control pin +// #define TFT_BL D6 + +// #define TFT_BACKLIGHT_ON HIGH +#define TFT_RST D0 // Reset pin (could connect to RST pin) + +// Touchscreen +#define CHSCX6X_DRIVER +#define TOUCH_INT D7 +#define TOUCH_WIRE Wire + +#define LOAD_GLCD // Font 1. Original Adafruit 8 pixel font needs ~1820 bytes in FLASH +#define LOAD_FONT2 // Font 2. Small 16 pixel high font, needs ~3534 bytes in FLASH, 96 characters +#define LOAD_FONT4 // Font 4. Medium 26 pixel high font, needs ~5848 bytes in FLASH, 96 characters +#define LOAD_FONT6 // Font 6. Large 48 pixel font, needs ~2666 bytes in FLASH, only characters 1234567890:-.apm +#define LOAD_FONT7 // Font 7. 7 segment 48 pixel font, needs ~2438 bytes in FLASH, only characters 1234567890:-. +#define LOAD_FONT8 // Font 8. Large 75 pixel font needs ~3256 bytes in FLASH, only characters 1234567890:-. +#define LOAD_FONT8N // Font 8. Alternative to Font 8 above, slightly narrower, so 3 digits fit a 160 pixel TFT +#define LOAD_GFXFF // FreeFonts. Include access to the 48 Adafruit_GFX free fonts FF1 to FF48 and custom fonts + +#define SMOOTH_FONT + +#ifdef CONFIG_IDF_TARGET_ESP32S3 +#define USE_HSPI_PORT +#endif + +#if defined(SEEED_XIAO_M0) +#define SPI_FREQUENCY 12000000 +#define SPI_READ_FREQUENCY 4000000 +#elif defined(ARDUINO_XIAO_ESP32S3) || defined(ARDUINO_XIAO_ESP32S3_PLUS) +#define SPI_FREQUENCY 50000000 +#define SPI_READ_FREQUENCY 12000000 +#elif defined(ARDUINO_XIAO_ESP32C3) +#define SPI_FREQUENCY 40000000 +#define SPI_READ_FREQUENCY 6000000 +#elif defined(ARDUINO_XIAO_ESP32C6) +#define SPI_FREQUENCY 40000000 +#define SPI_READ_FREQUENCY 6000000 +#elif defined(ARDUINO_SEEED_XIAO_NRF52840) || defined(ARDUINO_SEEED_XIAO_NRF52840_SENSE) || defined(ARDUINO_Seeed_XIAO_nRF52840) || defined(ARDUINO_Seeed_XIAO_nRF52840_Sense) +#define SPI_FREQUENCY 12000000 +#define SPI_READ_FREQUENCY 4000000 +#define ltoa itoa +#elif defined(ARDUINO_SEEED_XIAO_RP2040) || defined(ARDUINO_SEEED_XIAO_RP2350) +#define SPI_FREQUENCY 62500000 +#define SPI_READ_FREQUENCY 12000000 +#elif defined(ARDUINO_XIAO_RA4M1) +#define SPI_FREQUENCY 25000000 +#define SPI_READ_FREQUENCY 4000000 +#elif defined (EFR32MG24B220F1536IM48) +#define SPI_FREQUENCY 25000000 +#define SPI_READ_FREQUENCY 4000000 +#else +#message "Unknown board using default SPI settings (25MHz)" +#define SPI_FREQUENCY 25000000 +#define SPI_READ_FREQUENCY 4000000 +#endif diff --git a/lib/Seeed_GFX/User_Setups/SetupX_Template.h b/lib/Seeed_GFX/User_Setups/SetupX_Template.h new file mode 100644 index 0000000..00bf9a5 --- /dev/null +++ b/lib/Seeed_GFX/User_Setups/SetupX_Template.h @@ -0,0 +1,366 @@ +// USER DEFINED SETTINGS +// Set driver type, fonts to be loaded, pins used and SPI control method etc. +// +// See the User_Setup_Select.h file if you wish to be able to define multiple +// setups and then easily select which setup file is used by the compiler. +// +// If this file is edited correctly then all the library example sketches should +// run without the need to make any more changes for a particular hardware setup! +// Note that some sketches are designed for a particular TFT pixel width/height + +#define USER_SETUP_ID 0xFFFFFFFF + +// Define to disable all #warnings in library (can be put in User_Setup_Select.h) +//#define DISABLE_ALL_LIBRARY_WARNINGS + +// ################################################################################## +// +// Section 1. Call up the right driver file and any options for it +// +// ################################################################################## + +// Define STM32 to invoke optimised processor support (only for STM32) +//#define STM32 + +// Defining the STM32 board allows the library to optimise the performance +// for UNO compatible "MCUfriend" style shields +//#define NUCLEO_64_TFT +//#define NUCLEO_144_TFT + +// STM32 8-bit parallel only: +// If STN32 Port A or B pins 0-7 are used for 8-bit parallel data bus bits 0-7 +// then this will improve rendering performance by a factor of ~8x +//#define STM_PORTA_DATA_BUS +//#define STM_PORTB_DATA_BUS + +// Tell the library to use 8-bit parallel mode (otherwise SPI is assumed) +//#define TFT_PARALLEL_8_BIT +//#define TFT_PARALLEL_16_BIT // **** 16-bit parallel ONLY with RP2040 processor **** + +// Display type - only define if RPi display +//#define RPI_DISPLAY_TYPE // 20MHz maximum SPI + +// Only define one driver, the other ones must be commented out +#define ILI9341_DRIVER // Generic driver for common displays +//#define ILI9341_2_DRIVER // Alternative ILI9341 driver, see https://github.com/Bodmer/TFT_eSPI/issues/1172 +//#define ILI9342_DRIVER // Landscape default orientation variant of ILI9341 +//#define ST7735_DRIVER // Define additional parameters below for this display +//#define ILI9163_DRIVER // Define additional parameters below for this display +//#define S6D02A1_DRIVER +//#define RPI_ILI9486_DRIVER // 20MHz maximum SPI +//#define HX8357B_DRIVER +//#define HX8357C_DRIVER +//#define HX8357D_DRIVER +//#define ILI9481_DRIVER +//#define ILI9486_DRIVER +//#define ILI9488_DRIVER // WARNING: Do not connect ILI9488 display SDO to MISO if other devices share the SPI bus (TFT SDO does NOT tristate when CS is high) +//#define ST7789_DRIVER // Full configuration option, define additional parameters below for this display +//#define ST7789_2_DRIVER // Minimal configuration option, define additional parameters below for this display +//#define R61581_DRIVER +//#define RM68120_DRIVER // Untested +//#define RM68140_DRIVER +//#define ST7796_DRIVER +//#define SSD1351_DRIVER +//#define SSD1963_480_DRIVER +//#define SSD1963_800_DRIVER +//#define SSD1963_800ALT_DRIVER +//#define ILI9225_DRIVER +//#define GC9A01_DRIVER + +// Some displays support SPI reads via the MISO pin, other displays have a single +// bi-directional SDA pin and the library will try to read this via the MOSI line. +// To use the SDA line for reading data from the TFT uncomment the following line: + +// #define TFT_SDA_READ // This option is for ESP32 ONLY, tested with ST7789 and GC9A01 display only + +// For ST7735, ST7789 and ILI9341 ONLY, define the colour order IF the blue and red are swapped on your display +// Try ONE option at a time to find the correct colour order for your display + +// #define TFT_RGB_ORDER TFT_RGB // Colour order Red-Green-Blue +// #define TFT_RGB_ORDER TFT_BGR // Colour order Blue-Green-Red + +// For M5Stack ESP32 module with integrated ILI9341 display ONLY, remove // in line below + +// #define M5STACK + +// For ST7789, ST7735, ILI9163 and GC9A01 ONLY, define the pixel width and height in portrait orientation +// #define TFT_WIDTH 80 +// #define TFT_WIDTH 128 +// #define TFT_WIDTH 172 // ST7789 172 x 320 +// #define TFT_WIDTH 240 // ST7789 240 x 240 and 240 x 320 +// #define TFT_HEIGHT 160 +// #define TFT_HEIGHT 128 +// #define TFT_HEIGHT 240 // ST7789 240 x 240 +// #define TFT_HEIGHT 320 // ST7789 240 x 320 +// #define TFT_HEIGHT 240 // GC9A01 240 x 240 + +// For ST7735 ONLY, define the type of display, originally this was based on the +// colour of the tab on the screen protector film but this is not always true, so try +// out the different options below if the screen does not display graphics correctly, +// e.g. colours wrong, mirror images, or stray pixels at the edges. +// Comment out ALL BUT ONE of these options for a ST7735 display driver, save this +// this User_Setup file, then rebuild and upload the sketch to the board again: + +// #define ST7735_INITB +// #define ST7735_GREENTAB +// #define ST7735_GREENTAB2 +// #define ST7735_GREENTAB3 +// #define ST7735_GREENTAB128 // For 128 x 128 display +// #define ST7735_GREENTAB160x80 // For 160 x 80 display (BGR, inverted, 26 offset) +// #define ST7735_REDTAB +// #define ST7735_BLACKTAB +// #define ST7735_REDTAB160x80 // For 160 x 80 display with 24 pixel offset + +// If colours are inverted (white shows as black) then uncomment one of the next +// 2 lines try both options, one of the options should correct the inversion. + +// #define TFT_INVERSION_ON +// #define TFT_INVERSION_OFF + + +// ################################################################################## +// +// Section 2. Define the pins that are used to interface with the display here +// +// ################################################################################## + +// If a backlight control signal is available then define the TFT_BL pin in Section 2 +// below. The backlight will be turned ON when tft.begin() is called, but the library +// needs to know if the LEDs are ON with the pin HIGH or LOW. If the LEDs are to be +// driven with a PWM signal or turned OFF/ON then this must be handled by the user +// sketch. e.g. with digitalWrite(TFT_BL, LOW); + +// #define TFT_BL 32 // LED back-light control pin +// #define TFT_BACKLIGHT_ON HIGH // Level to turn ON back-light (HIGH or LOW) + + + +// We must use hardware SPI, a minimum of 3 GPIO pins is needed. +// Typical setup for ESP8266 NodeMCU ESP-12 is : +// +// Display SDO/MISO to NodeMCU pin D6 (or leave disconnected if not reading TFT) +// Display LED to NodeMCU pin VIN (or 5V, see below) +// Display SCK to NodeMCU pin D5 +// Display SDI/MOSI to NodeMCU pin D7 +// Display DC (RS/AO)to NodeMCU pin D3 +// Display RESET to NodeMCU pin D4 (or RST, see below) +// Display CS to NodeMCU pin D8 (or GND, see below) +// Display GND to NodeMCU pin GND (0V) +// Display VCC to NodeMCU 5V or 3.3V +// +// The TFT RESET pin can be connected to the NodeMCU RST pin or 3.3V to free up a control pin +// +// The DC (Data Command) pin may be labelled AO or RS (Register Select) +// +// With some displays such as the ILI9341 the TFT CS pin can be connected to GND if no more +// SPI devices (e.g. an SD Card) are connected, in this case comment out the #define TFT_CS +// line below so it is NOT defined. Other displays such at the ST7735 require the TFT CS pin +// to be toggled during setup, so in these cases the TFT_CS line must be defined and connected. +// +// The NodeMCU D0 pin can be used for RST +// +// +// Note: only some versions of the NodeMCU provide the USB 5V on the VIN pin +// If 5V is not available at a pin you can use 3.3V but backlight brightness +// will be lower. + + +// ###### EDIT THE PIN NUMBERS IN THE LINES FOLLOWING TO SUIT YOUR ESP8266 SETUP ###### + +// For NodeMCU - use pin numbers in the form PIN_Dx where Dx is the NodeMCU pin designation +#define TFT_CS PIN_D8 // Chip select control pin D8 +#define TFT_DC PIN_D3 // Data Command control pin +#define TFT_RST PIN_D4 // Reset pin (could connect to NodeMCU RST, see next line) +//#define TFT_RST -1 // Set TFT_RST to -1 if the display RESET is connected to NodeMCU RST or 3.3V + +//#define TFT_BL PIN_D1 // LED back-light (only for ST7789 with backlight control pin) + +//#define TOUCH_CS PIN_D2 // Chip select pin (T_CS) of touch screen + +//#define TFT_WR PIN_D2 // Write strobe for modified Raspberry Pi TFT only + + +// ###### FOR ESP8266 OVERLAP MODE EDIT THE PIN NUMBERS IN THE FOLLOWING LINES ###### + +// Overlap mode shares the ESP8266 FLASH SPI bus with the TFT so has a performance impact +// but saves pins for other functions. It is best not to connect MISO as some displays +// do not tristate that line when chip select is high! +// On NodeMCU 1.0 SD0=MISO, SD1=MOSI, CLK=SCLK to connect to TFT in overlap mode +// On NodeMCU V3 S0 =MISO, S1 =MOSI, S2 =SCLK +// In ESP8266 overlap mode the following must be defined + +//#define TFT_SPI_OVERLAP + +// In ESP8266 overlap mode the TFT chip select MUST connect to pin D3 +//#define TFT_CS PIN_D3 +//#define TFT_DC PIN_D5 // Data Command control pin +//#define TFT_RST PIN_D4 // Reset pin (could connect to NodeMCU RST, see next line) +//#define TFT_RST -1 // Set TFT_RST to -1 if the display RESET is connected to NodeMCU RST or 3.3V + + +// ###### EDIT THE PIN NUMBERS IN THE LINES FOLLOWING TO SUIT YOUR ESP32 SETUP ###### + +// For ESP32 Dev board (only tested with ILI9341 display) +// The hardware SPI can be mapped to any pins + +//#define TFT_MISO 19 +//#define TFT_MOSI 23 +//#define TFT_SCLK 18 +//#define TFT_CS 15 // Chip select control pin +//#define TFT_DC 2 // Data Command control pin +//#define TFT_RST 4 // Reset pin (could connect to RST pin) +//#define TFT_RST -1 // Set TFT_RST to -1 if display RESET is connected to ESP32 board RST + +// For ESP32 Dev board (only tested with GC9A01 display) +// The hardware SPI can be mapped to any pins + +//#define TFT_MOSI 15 // In some display driver board, it might be written as "SDA" and so on. +//#define TFT_SCLK 14 +//#define TFT_CS 5 // Chip select control pin +//#define TFT_DC 27 // Data Command control pin +//#define TFT_RST 33 // Reset pin (could connect to Arduino RESET pin) +//#define TFT_BL 22 // LED back-light + +//#define TOUCH_CS 21 // Chip select pin (T_CS) of touch screen + +//#define TFT_WR 22 // Write strobe for modified Raspberry Pi TFT only + +// For the M5Stack module use these #define lines +//#define TFT_MISO 19 +//#define TFT_MOSI 23 +//#define TFT_SCLK 18 +//#define TFT_CS 14 // Chip select control pin +//#define TFT_DC 27 // Data Command control pin +//#define TFT_RST 33 // Reset pin (could connect to Arduino RESET pin) +//#define TFT_BL 32 // LED back-light (required for M5Stack) + +// ###### EDIT THE PINs BELOW TO SUIT YOUR ESP32 PARALLEL TFT SETUP ###### + +// The library supports 8-bit parallel TFTs with the ESP32, the pin +// selection below is compatible with ESP32 boards in UNO format. +// Wemos D32 boards need to be modified, see diagram in Tools folder. +// Only ILI9481 and ILI9341 based displays have been tested! + +// Parallel bus is only supported for the STM32 and ESP32 +// Example below is for ESP32 Parallel interface with UNO displays + +// Tell the library to use 8-bit parallel mode (otherwise SPI is assumed) +//#define TFT_PARALLEL_8_BIT + +// The ESP32 and TFT the pins used for testing are: +//#define TFT_CS 33 // Chip select control pin (library pulls permanently low +//#define TFT_DC 15 // Data Command control pin - must use a pin in the range 0-31 +//#define TFT_RST 32 // Reset pin, toggles on startup + +//#define TFT_WR 4 // Write strobe control pin - must use a pin in the range 0-31 +//#define TFT_RD 2 // Read strobe control pin + +//#define TFT_D0 12 // Must use pins in the range 0-31 for the data bus +//#define TFT_D1 13 // so a single register write sets/clears all bits. +//#define TFT_D2 26 // Pins can be randomly assigned, this does not affect +//#define TFT_D3 25 // TFT screen update performance. +//#define TFT_D4 17 +//#define TFT_D5 16 +//#define TFT_D6 27 +//#define TFT_D7 14 + +// ###### EDIT THE PINs BELOW TO SUIT YOUR STM32 SPI TFT SETUP ###### + +// The TFT can be connected to SPI port 1 or 2 +//#define TFT_SPI_PORT 1 // SPI port 1 maximum clock rate is 55MHz +//#define TFT_MOSI PA7 +//#define TFT_MISO PA6 +//#define TFT_SCLK PA5 + +//#define TFT_SPI_PORT 2 // SPI port 2 maximum clock rate is 27MHz +//#define TFT_MOSI PB15 +//#define TFT_MISO PB14 +//#define TFT_SCLK PB13 + +// Can use Ardiuno pin references, arbitrary allocation, TFT_eSPI controls chip select +//#define TFT_CS D5 // Chip select control pin to TFT CS +//#define TFT_DC D6 // Data Command control pin to TFT DC (may be labelled RS = Register Select) +//#define TFT_RST D7 // Reset pin to TFT RST (or RESET) +// OR alternatively, we can use STM32 port reference names PXnn +//#define TFT_CS PE11 // Nucleo-F767ZI equivalent of D5 +//#define TFT_DC PE9 // Nucleo-F767ZI equivalent of D6 +//#define TFT_RST PF13 // Nucleo-F767ZI equivalent of D7 + +//#define TFT_RST -1 // Set TFT_RST to -1 if the display RESET is connected to processor reset + // Use an Arduino pin for initial testing as connecting to processor reset + // may not work (pulse too short at power up?) + +// ################################################################################## +// +// Section 3. Define the fonts that are to be used here +// +// ################################################################################## + +// Comment out the #defines below with // to stop that font being loaded +// The ESP8366 and ESP32 have plenty of memory so commenting out fonts is not +// normally necessary. If all fonts are loaded the extra FLASH space required is +// about 17Kbytes. To save FLASH space only enable the fonts you need! + +#define LOAD_GLCD // Font 1. Original Adafruit 8 pixel font needs ~1820 bytes in FLASH +#define LOAD_FONT2 // Font 2. Small 16 pixel high font, needs ~3534 bytes in FLASH, 96 characters +#define LOAD_FONT4 // Font 4. Medium 26 pixel high font, needs ~5848 bytes in FLASH, 96 characters +#define LOAD_FONT6 // Font 6. Large 48 pixel font, needs ~2666 bytes in FLASH, only characters 1234567890:-.apm +#define LOAD_FONT7 // Font 7. 7 segment 48 pixel font, needs ~2438 bytes in FLASH, only characters 1234567890:-. +#define LOAD_FONT8 // Font 8. Large 75 pixel font needs ~3256 bytes in FLASH, only characters 1234567890:-. +//#define LOAD_FONT8N // Font 8. Alternative to Font 8 above, slightly narrower, so 3 digits fit a 160 pixel TFT +#define LOAD_GFXFF // FreeFonts. Include access to the 48 Adafruit_GFX free fonts FF1 to FF48 and custom fonts + +// Comment out the #define below to stop the SPIFFS filing system and smooth font code being loaded +// this will save ~20kbytes of FLASH +#define SMOOTH_FONT + + +// ################################################################################## +// +// Section 4. Other options +// +// ################################################################################## + +// For the RP2040 processor define the SPI port channel used (default 0 if undefined) +//#define TFT_SPI_PORT 1 // Set to 0 if SPI0 pins are used, or 1 if spi1 pins used + +// For the STM32 processor define the SPI port channel used (default 1 if undefined) +//#define TFT_SPI_PORT 2 // Set to 1 for SPI port 1, or 2 for SPI port 2 + +// Define the SPI clock frequency, this affects the graphics rendering speed. Too +// fast and the TFT driver will not keep up and display corruption appears. +// With an ILI9341 display 40MHz works OK, 80MHz sometimes fails +// With a ST7735 display more than 27MHz may not work (spurious pixels and lines) +// With an ILI9163 display 27 MHz works OK. + +// #define SPI_FREQUENCY 1000000 +// #define SPI_FREQUENCY 5000000 +// #define SPI_FREQUENCY 10000000 +// #define SPI_FREQUENCY 20000000 +#define SPI_FREQUENCY 27000000 +// #define SPI_FREQUENCY 40000000 +// #define SPI_FREQUENCY 55000000 // STM32 SPI1 only (SPI2 maximum is 27MHz) +// #define SPI_FREQUENCY 80000000 + +// Optional reduced SPI frequency for reading TFT +#define SPI_READ_FREQUENCY 20000000 + +// The XPT2046 requires a lower SPI clock rate of 2.5MHz so we define that here: +#define SPI_TOUCH_FREQUENCY 2500000 + +// The ESP32 has 2 free SPI ports i.e. VSPI and HSPI, the VSPI is the default. +// If the VSPI port is in use and pins are not accessible (e.g. TTGO T-Beam) +// then uncomment the following line: +//#define USE_HSPI_PORT + +// Comment out the following #define if "SPI Transactions" do not need to be +// supported. When commented out the code size will be smaller and sketches will +// run slightly faster, so leave it commented out unless you need it! + +// Transaction support is needed to work with SD library but not needed with TFT_SdFat +// Transaction support is required if other SPI devices are connected. + +// Transactions are automatically enabled by the library for an ESP32 (to use HAL mutex) +// so changing it here has no effect + +// #define SUPPORT_TRANSACTIONS diff --git a/lib/Seeed_GFX/User_Setups/User_Custom_Fonts.h b/lib/Seeed_GFX/User_Setups/User_Custom_Fonts.h new file mode 100644 index 0000000..9103483 --- /dev/null +++ b/lib/Seeed_GFX/User_Setups/User_Custom_Fonts.h @@ -0,0 +1,30 @@ + +// Custom "Adafruit" compatible font files can be added to the "TFT_eSPI/Fonts/Custom" folder +// Fonts in a suitable format can be created using a Squix blog web based tool here: +/* + https://blog.squix.org/2016/10/font-creator-now-creates-adafruit-gfx-fonts.html +*/ + +// Note: At the time of writing there is a last character code definition bug in the +// Squix font file format so do NOT try and print the tilde (~) symbol (ASCII 0x7E) +// Alternatively look at the end of the font header file and edit: 0x7E to read 0x7D +/* e.g. vvvv + (uint8_t *)Orbitron_Light_32Bitmaps,(GFXglyph *)Orbitron_Light_32Glyphs,0x20, 0x7D, 32}; + ^^^^ +*/ + +// When font files are placed in the Custom folder (TFT_eSPI\Fonts\Custom) then they must +// also be #included here: + +// The CF_OL24 etc. are a shorthand reference, but this is not essential to use the fonts + +// Shorthand references - any coding scheme can be used, here CF_ = Custom Font +// The #defines below MUST be added to sketches to use shorthand references, so +// they are only put here for reference and copy+paste purposes! +/* +#define CF_OL24 &Orbitron_Light_24 +#define CF_OL32 &Orbitron_Light_32 +#define CF_RT24 &Roboto_Thin_24 +#define CF_S24 &Satisfy_24 +#define CF_Y32 &Yellowtail_32 +*/ diff --git a/lib/Seeed_GFX/User_Setups/XIAO_SPI_Frequency.h b/lib/Seeed_GFX/User_Setups/XIAO_SPI_Frequency.h new file mode 100644 index 0000000..bdef65b --- /dev/null +++ b/lib/Seeed_GFX/User_Setups/XIAO_SPI_Frequency.h @@ -0,0 +1,60 @@ +#ifdef EPAPER_ENABLE +#if defined(SEEED_XIAO_M0) +#define SPI_FREQUENCY 10000000 +#define SPI_READ_FREQUENCY 4000000 +#elif defined(ARDUINO_XIAO_ESP32S3) || defined(ARDUINO_XIAO_ESP32S3_PLUS) +#define SPI_FREQUENCY 10000000 +#define SPI_READ_FREQUENCY 4000000 +#elif defined(ARDUINO_XIAO_ESP32C3) || defined(ARDUINO_XIAO_ESP32C6) || defined(ARDUINO_XIAO_ESP32C5) +#define SPI_FREQUENCY 10000000 +#define SPI_READ_FREQUENCY 4000000 +#elif defined(ARDUINO_SEEED_XIAO_NRF52840) || defined(ARDUINO_SEEED_XIAO_NRF52840_SENSE) || defined(ARDUINO_Seeed_XIAO_nRF52840) || defined(ARDUINO_Seeed_XIAO_nRF52840_Sense) +#define SPI_FREQUENCY 10000000 +#define SPI_READ_FREQUENCY 4000000 +#define ltoa itoa +#elif defined(ARDUINO_SEEED_XIAO_RP2040) || defined(ARDUINO_SEEED_XIAO_RP2350) +#define SPI_FREQUENCY 10000000 +#define SPI_READ_FREQUENCY 4000000 +#elif defined(ARDUINO_XIAO_RA4M1) +#define SPI_FREQUENCY 10000000 +#define SPI_READ_FREQUENCY 4000000 +#elif defined (EFR32MG24B220F1536IM48) +#define SPI_FREQUENCY 10000000 +#define SPI_READ_FREQUENCY 4000000 +#elif defined(CONFIG_IDF_TARGET_ESP32S3) || defined(ARDUINO_ESP32S3_DEV) || defined(ARDUINO_ESP32S3_GENERIC) +#define SPI_FREQUENCY 40000000 +#define SPI_READ_FREQUENCY 4000000 +#else +#pragma message "Unknown board using default SPI settings (1MHz)" +#define SPI_FREQUENCY 10000000 +#define SPI_READ_FREQUENCY 4000000 +#endif +#else +#if defined(SEEED_XIAO_M0) +#define SPI_FREQUENCY 12000000 +#define SPI_READ_FREQUENCY 4000000 +#elif defined(ARDUINO_XIAO_ESP32S3) || defined(ARDUINO_XIAO_ESP32S3_PLUS) +#define SPI_FREQUENCY 50000000 +#define SPI_READ_FREQUENCY 12000000 +#elif defined(ARDUINO_XIAO_ESP32C3) || defined(ARDUINO_XIAO_ESP32C6) || defined(ARDUINO_XIAO_ESP32C5) +#define SPI_FREQUENCY 40000000 +#define SPI_READ_FREQUENCY 6000000 +#elif defined(ARDUINO_SEEED_XIAO_NRF52840) || defined(ARDUINO_SEEED_XIAO_NRF52840_SENSE) || defined(ARDUINO_Seeed_XIAO_nRF52840) || defined(ARDUINO_Seeed_XIAO_nRF52840_Sense) +#define SPI_FREQUENCY 12000000 +#define SPI_READ_FREQUENCY 4000000 +#define ltoa itoa +#elif defined(ARDUINO_SEEED_XIAO_RP2040) || defined(ARDUINO_SEEED_XIAO_RP2350) +#define SPI_FREQUENCY 62500000 +#define SPI_READ_FREQUENCY 12000000 +#elif defined(ARDUINO_XIAO_RA4M1) +#define SPI_FREQUENCY 25000000 +#define SPI_READ_FREQUENCY 4000000 +#elif defined (EFR32MG24B220F1536IM48) +#define SPI_FREQUENCY 25000000 +#define SPI_READ_FREQUENCY 4000000 +#else +#pragma message "Unknown board using default SPI settings (25MHz)" +#define SPI_FREQUENCY 25000000 +#define SPI_READ_FREQUENCY 4000000 +#endif +#endif \ No newline at end of file diff --git a/lib/Seeed_GFX/library.json b/lib/Seeed_GFX/library.json new file mode 100644 index 0000000..95d57c0 --- /dev/null +++ b/lib/Seeed_GFX/library.json @@ -0,0 +1,12 @@ +{ + "name": "Seeed_GFX", + "version": "2.0.3", + "description": "Vendored Seeed fork of TFT_eSPI (OpenDisplay runtime pins + BOARD_SCREEN_COMBO 528)", + "keywords": ["display", "epaper", "tft"], + "authors": [{ "name": "Seeed / Bodmer fork" }], + "frameworks": ["arduino"], + "platforms": "*", + "build": { + "includeDir": "." + } +} diff --git a/lib/Seeed_GFX/library.properties b/lib/Seeed_GFX/library.properties new file mode 100644 index 0000000..0a3462b --- /dev/null +++ b/lib/Seeed_GFX/library.properties @@ -0,0 +1,10 @@ +name=Seeed_GFX +version=2.0.3 +author=Bodmer +maintainer=Hongtai.liu +sentence=A fast TFT graphics library for Arduino. +paragraph=Supports TFT displays using drivers (ILI9341 etc) that operate with hardware SPI. +category=Display +url=https://github.com/Seeed-Studio/Seeed_GFX +architectures=* +includes=TFT_eSPI.h diff --git a/lib/Seeed_GFX/license.txt b/lib/Seeed_GFX/license.txt new file mode 100644 index 0000000..e0b40d2 --- /dev/null +++ b/lib/Seeed_GFX/license.txt @@ -0,0 +1,135 @@ +The original starting point for this library was the Adafruit_ILI9341 +library in January 2015. + +The licence for that library is MIT. + +The first evolution of the library that led to TFT_eSPI is recorded here: + +https://www.instructables.com/id/Arduino-TFT-display-and-font-library/ + +Adafruit_ILI9341 ORIGINAL LIBRARY HEADER: + +vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvStartvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv + This is our library for the Adafruit ILI9341 Breakout and Shield + ----> http://www.adafruit.com/products/1651 + + Check out the links above for our tutorials and wiring diagrams + These displays use SPI to communicate, 4 or 5 pins are required to + interface (RST is optional) + Adafruit invests time and resources providing this open source code, + please support Adafruit and open-source hardware by purchasing + products from Adafruit! + + Written by Limor Fried/Ladyada for Adafruit Industries. + MIT license, all text above must be included in any redistribution + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^End^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + +Selected functions from the Adafruit_GFX library (as it was in 2015) have +been imported into the TFT_eSPI.cpp file and modified to improve +performance, add features and make them compatible with the ESP8266 and +ESP32. + +The fonts from the Adafruit_GFX and Button functions were added later. +The fonts can be found with the license.txt file in the "Fonts\GFXFF" +folder. + +The Adafruit_GFX functions are covered by the BSD licence. + +Adafruit_GFX ORIGINAL LIBRARY LICENSE: + +vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvStartvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv + +Software License Agreement (BSD License) + +Copyright (c) 2012 Adafruit Industries. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +- Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. +- Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^End^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Due to the evolution of the TFT_eSPI library the original code may no longer +be recognisable, however in most cases the function names can be used as a +reference point since the aim is to retain a level of compatibility with +the popular Adafruit_GFX graphics functions. + +Contributions from other authors are recorded on GitHub: +https://github.com/Bodmer/TFT_eSPI + +The major addition to the original library was the addition of fast +rendering proportional fonts of different sizes as documented here: + +https://www.instructables.com/id/Arduino-TFT-display-and-font-library/ + +The larger fonts are "Run Length Encoded (RLE)", this was done to +reduce the font memory footprint for AVR processors that have limited +FLASH, with the added benefit of a significant improvement in rendering +speed. + +In 2016 the library evolved significantly to support the ESP8266 and then +the ESP32. In 2017 new Touch Screen functions were added and a new Sprite +class called TFT_eSprite to permit "flicker free" screen updates of complex +graphics. + +In 2018 anti-aliased fonts were added along with a Processing font conversion +sketch. + +In 2019 the library was adapted to be able to use it with any 32-bit Arduino +compatible processor. It will run on 8-bit and 16-bit processors but will be +slow due to extensive use of 32-bit variables. + +Many of the example sketches are original work that contain code created +for my own projects. For all the original code the FreeBSD licence applies +and is compatible with the GNU GPL. + +vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvStartvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +Software License Agreement (FreeBSD License) + +Copyright (c) 2023 Bodmer (https://github.com/Bodmer) + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +The views and conclusions contained in the software and documentation are those +of the authors and should not be interpreted as representing official policies, +either expressed or implied, of the FreeBSD Project. +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^End^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/platformio.ini b/platformio.ini index 20f7bf5..de9cc5b 100644 --- a/platformio.ini +++ b/platformio.ini @@ -13,6 +13,7 @@ board_build.variant = nrf52840custom board = xiaoblesense_adafruit monitor_speed = 115200 build_src_filter = +<*> - +lib_ignore = Seeed_GFX [env:esp32-s3-N16R8] @@ -25,6 +26,7 @@ build_flags = -DARDUINO_USB_MODE=1 -DARDUINO_USB_CDC_ON_BOOT=1 -DCONFIG_FREERTOS_WATCHDOG_TIMEOUT_S=120 + -DOPENDISPLAY_SEEED_GFX board_build.filesystem = littlefs board = esp32-s3-devkitc-1 board_build.partitions = default_16MB.csv @@ -47,6 +49,7 @@ build_flags = -DARDUINO_USB_MODE=1 -DARDUINO_USB_CDC_ON_BOOT=1 -DCONFIG_FREERTOS_WATCHDOG_TIMEOUT_S=120 + -DOPENDISPLAY_SEEED_GFX board_build.filesystem = littlefs board = esp32-s3-devkitc-1 board_build.partitions = default_8MB.csv @@ -69,6 +72,7 @@ build_flags = -DARDUINO_USB_MODE=1 -DARDUINO_USB_CDC_ON_BOOT=1 -DCONFIG_FREERTOS_WATCHDOG_TIMEOUT_S=120 + -DOPENDISPLAY_SEEED_GFX board_build.filesystem = littlefs board = esp32-s3-devkitc-1 board_build.partitions = default_32MB.csv @@ -93,6 +97,8 @@ board_build.filesystem = littlefs board_build.partitions = huge_app.csv board = esp32-c3-devkitm-1 monitor_speed = 115200 +; Seeed_GFX/TFT_eSPI is only for S3 + OPENDISPLAY_SEEED_GFX; +lib_ignore = Seeed_GFX [env:esp32-c6-N4] platform = espressif32 @@ -106,6 +112,34 @@ board_build.filesystem = littlefs board_build.partitions = huge_app.csv board = esp32-c6-devkitm-1 monitor_speed = 115200 +lib_ignore = Seeed_GFX + +; esp32-s3-N16R8 + UART log; Seeed_GFX path when panel_ic_type is 3000 (1bpp) or 3001 (4bpp gray). +[env:esp32-s3-N16R8-extuart] +platform = https://github.com/pioarduino/platform-espressif32/releases/download/stable/platform-espressif32.zip +framework = arduino +build_flags = + -DTARGET_ESP32 + -DTARGET_LARGE_MEMORY + -DBOARD_HAS_PSRAM + -DARDUINO_USB_MODE=1 + -DARDUINO_USB_CDC_ON_BOOT=1 + -DCONFIG_FREERTOS_WATCHDOG_TIMEOUT_S=120 + -DOPENDISPLAY_SEEED_GFX + -DOPENDISPLAY_LOG_UART + -DOPENDISPLAY_LOG_UART_RX=44 + -DOPENDISPLAY_LOG_UART_TX=43 +board_build.filesystem = littlefs +board = esp32-s3-devkitc-1 +board_build.partitions = default_16MB.csv +board_build.flash_mode=qio +board_build.arduino.memory_type = qio_opi +board_build.psram_type=qspi_opi +board_build.usb_mode = hardware +board_upload.maximum_size = 16777216 +board_upload.maximum_ram_size = 327680 +board_upload.flash_size = 16MB +monitor_speed = 115200 #[env:esp32-N4] #platform = espressif32 diff --git a/src/ble_init.cpp b/src/ble_init.cpp index af3d5eb..12f66c4 100644 --- a/src/ble_init.cpp +++ b/src/ble_init.cpp @@ -79,7 +79,7 @@ void ble_nrf_advertising_start() { Bluefruit.Advertising.addName(); updatemsdata(); Bluefruit.Advertising.restartOnDisconnect(true); - Bluefruit.Advertising.setInterval(32, 200); + Bluefruit.Advertising.setInterval(256, 1600); Bluefruit.Advertising.setFastTimeout(10); writeSerial("Starting BLE advertising..."); Bluefruit.Advertising.start(0); diff --git a/src/boot_screen.cpp b/src/boot_screen.cpp index 9d9c4c5..f33a628 100644 --- a/src/boot_screen.cpp +++ b/src/boot_screen.cpp @@ -6,6 +6,10 @@ #include "structs.h" #include #include "qr/qrcode.h" +#include "display_service.h" +#if defined(TARGET_ESP32) && defined(OPENDISPLAY_SEEED_GFX) +#include "display_seeed_gfx.h" +#endif extern struct GlobalConfig globalConfig; extern struct SecurityConfig securityConfig; @@ -223,8 +227,15 @@ bool writeBootScreenWithQr() { memcpy(k2, keyHex + 16, 16); k2[16] = '\0'; uint8_t* row = staticRowBuffer; +#if defined(TARGET_ESP32) && defined(OPENDISPLAY_SEEED_GFX) + if (!seeed_driver_used()) { + bbepSetAddrWindow(&bbep, 0, 0, w, h); + bbepStartWrite(&bbep, getplane()); + } +#else bbepSetAddrWindow(&bbep, 0, 0, w, h); bbepStartWrite(&bbep, getplane()); +#endif for (uint16_t y = 0; y < h; y++) { memset(row, whiteValue, pitch); uint16_t availW = qrRight ? qrX : w; @@ -267,22 +278,37 @@ bool writeBootScreenWithQr() { } } } +#if defined(TARGET_ESP32) && defined(OPENDISPLAY_SEEED_GFX) + if (seeed_driver_used()) { + seeed_gfx_boot_write_row(y, row, pitch); + } else { + bbepWriteData(&bbep, row, pitch); + } +#else bbepWriteData(&bbep, row, pitch); +#endif } - if (useBitplanes) { - memset(row, 0x00, pitch); - bbepSetAddrWindow(&bbep, 0, 0, w, h); - bbepStartWrite(&bbep, PLANE_1); - for (uint16_t y = 0; y < h; y++) bbepWriteData(&bbep, row, pitch); - } - if (colorScheme == 5) { - int otherPlane = (getplane() == PLANE_0) ? PLANE_1 : PLANE_0; - int otherPlanePitch = (w + 7) / 8; - memset(row, 0x00, otherPlanePitch); - bbepSetAddrWindow(&bbep, 0, 0, w, h); - bbepStartWrite(&bbep, otherPlane); - for (uint16_t y = 0; y < h; y++) bbepWriteData(&bbep, row, otherPlanePitch); +#if defined(TARGET_ESP32) && defined(OPENDISPLAY_SEEED_GFX) + if (seeed_driver_used()) { + seeed_gfx_boot_skip_planes(); + } else +#endif + { + if (useBitplanes) { + memset(row, 0x00, pitch); + bbepSetAddrWindow(&bbep, 0, 0, w, h); + bbepStartWrite(&bbep, PLANE_1); + for (uint16_t y = 0; y < h; y++) bbepWriteData(&bbep, row, pitch); + } + if (colorScheme == 5) { + int otherPlane = (getplane() == PLANE_0) ? PLANE_1 : PLANE_0; + int otherPlanePitch = (w + 7) / 8; + memset(row, 0x00, otherPlanePitch); + bbepSetAddrWindow(&bbep, 0, 0, w, h); + bbepStartWrite(&bbep, otherPlane); + for (uint16_t y = 0; y < h; y++) bbepWriteData(&bbep, row, otherPlanePitch); + } } writeSerial("Boot screen with QR rendered", true); return true; diff --git a/src/buzzer_control.cpp b/src/buzzer_control.cpp new file mode 100644 index 0000000..18d5990 --- /dev/null +++ b/src/buzzer_control.cpp @@ -0,0 +1,170 @@ +#include "buzzer_control.h" +#include "structs.h" +#include +#include + +extern struct GlobalConfig globalConfig; +void sendResponse(uint8_t* response, uint8_t len); + +static_assert(sizeof(PassiveBuzzerConfig) == 32, "PassiveBuzzerConfig must be 32 bytes"); + +static constexpr uint32_t kBuzzerFreqMinHz = 400u; +static constexpr uint32_t kBuzzerFreqMaxHz = 12000u; +static constexpr uint8_t kBuzzerDurationUnitMs = 5u; +static constexpr uint32_t kBuzzerInterPatternGapMs = 20u; + +static uint32_t buzzer_index_to_hz(uint8_t idx) { + if (idx == 0) { + return 0; + } + uint32_t fmin = kBuzzerFreqMinHz; + uint32_t fmax = kBuzzerFreqMaxHz; + uint32_t span = fmax - fmin; + return fmin + (span * (uint32_t)(idx - 1)) / 254u; +} + +static void buzzer_set_enable(const PassiveBuzzerConfig* b, bool on) { + if (b->enable_pin == 0xFF) { + return; + } + bool high = on; + if ((b->flags & BUZZER_FLAG_ENABLE_ACTIVE_HIGH) == 0) { + high = !high; + } + digitalWrite(b->enable_pin, high ? HIGH : LOW); +} + +static void buzzer_drive_off(const PassiveBuzzerConfig* b) { + pinMode(b->drive_pin, OUTPUT); + digitalWrite(b->drive_pin, LOW); +} + +static void buzzer_drive_tone_sw(const PassiveBuzzerConfig* b, uint32_t hz, uint32_t ms) { + uint8_t duty = b->duty_percent; + if (duty == 0 || duty > 100) { + duty = 50; + } + if (ms == 0) { + return; + } + if (hz == 0 || duty == 0) { + buzzer_drive_off(b); + delay(ms); + return; + } + + uint8_t pin = b->drive_pin; + uint32_t period_us = 1000000u / hz; + if (period_us < 2u) { + period_us = 2u; + } + uint32_t on_us = (period_us * (uint32_t)duty) / 100u; + if (on_us == 0u) { + on_us = 1u; + } + if (on_us >= period_us) { + on_us = period_us - 1u; + } + uint32_t off_us = period_us - on_us; + + const uint32_t total_us = ms * 1000u; + const uint32_t start_us = micros(); + while ((uint32_t)(micros() - start_us) < total_us) { + digitalWrite(pin, HIGH); + delayMicroseconds(on_us); + digitalWrite(pin, LOW); + delayMicroseconds(off_us); + } +} + +void initPassiveBuzzers(void) { + for (uint8_t i = 0; i < globalConfig.passive_buzzer_count; i++) { + const PassiveBuzzerConfig* b = &globalConfig.passive_buzzers[i]; + if (b->drive_pin == 0xFF) { + continue; + } + pinMode(b->drive_pin, OUTPUT); + digitalWrite(b->drive_pin, LOW); + if (b->enable_pin != 0xFF) { + pinMode(b->enable_pin, OUTPUT); + buzzer_set_enable(b, false); + } + } +} + +void handleBuzzerActivate(uint8_t* data, uint16_t len) { + if (len < 3) { + uint8_t err[] = {0xFF, 0x75, 0x01, 0x00}; + sendResponse(err, sizeof(err)); + return; + } + uint8_t inst = data[0]; + if (inst >= globalConfig.passive_buzzer_count) { + uint8_t err[] = {0xFF, 0x75, 0x02, 0x00}; + sendResponse(err, sizeof(err)); + return; + } + PassiveBuzzerConfig* b = &globalConfig.passive_buzzers[inst]; + if (b->drive_pin == 0xFF) { + uint8_t err[] = {0xFF, 0x75, 0x03, 0x00}; + sendResponse(err, sizeof(err)); + return; + } + + uint8_t outer = data[1]; + if (outer == 0) { + outer = 1; + } + uint8_t pattern_count = data[2]; + if (pattern_count == 0) { + uint8_t err[] = {0xFF, 0x75, 0x04, 0x00}; + sendResponse(err, sizeof(err)); + return; + } + + uint16_t scan = 3; + for (uint8_t pi = 0; pi < pattern_count; pi++) { + if (scan >= len) { + uint8_t err[] = {0xFF, 0x75, 0x05, 0x00}; + sendResponse(err, sizeof(err)); + return; + } + uint8_t nsteps = data[scan++]; + uint32_t need = (uint32_t)nsteps * 2u; + if (scan + need > len) { + uint8_t err[] = {0xFF, 0x75, 0x05, 0x00}; + sendResponse(err, sizeof(err)); + return; + } + scan = (uint16_t)(scan + need); + } + if (scan != len) { + uint8_t err[] = {0xFF, 0x75, 0x06, 0x00}; + sendResponse(err, sizeof(err)); + return; + } + + for (uint8_t rep = 0; rep < outer; rep++) { + uint16_t poff = 3; + for (uint8_t pi = 0; pi < pattern_count; pi++) { + uint8_t nsteps = data[poff++]; + for (uint8_t si = 0; si < nsteps; si++) { + uint8_t fidx = data[poff++]; + uint8_t dunit = data[poff++]; + uint32_t hz = buzzer_index_to_hz(fidx); + uint32_t ms = (uint32_t)dunit * (uint32_t)kBuzzerDurationUnitMs; + buzzer_set_enable(b, true); + buzzer_drive_tone_sw(b, hz, ms); + } + if (pi + 1 < pattern_count) { + delay(kBuzzerInterPatternGapMs); + } + } + } + + buzzer_set_enable(b, false); + buzzer_drive_off(b); + + uint8_t ok[] = {0x00, 0x75, 0x00, 0x00}; + sendResponse(ok, sizeof(ok)); +} diff --git a/src/buzzer_control.h b/src/buzzer_control.h new file mode 100644 index 0000000..fd70f97 --- /dev/null +++ b/src/buzzer_control.h @@ -0,0 +1,9 @@ +#ifndef BUZZER_CONTROL_H +#define BUZZER_CONTROL_H + +#include + +void initPassiveBuzzers(void); +void handleBuzzerActivate(uint8_t* data, uint16_t len); + +#endif diff --git a/src/communication.cpp b/src/communication.cpp index 260bc23..69d9f7e 100644 --- a/src/communication.cpp +++ b/src/communication.cpp @@ -3,15 +3,17 @@ #include "config_parser.h" #include "encryption.h" #include "device_control.h" -#include "device_control.h" +#include "buzzer_control.h" #include "display_service.h" #include #include #ifdef TARGET_ESP32 -#include +#include #include +#include "wifi_service.h" +extern BLEServer* pServer; #endif #ifdef TARGET_NRF @@ -20,6 +22,19 @@ void writeSerial(String message, bool newLine = true); bool isAuthenticated(); + +static void reloadConfigAfterSave(void) { + if (!loadGlobalConfig()) { + writeSerial("WARNING: Config was saved but reload from storage failed (see errors above). " + "Reboot may be required."); + return; + } + writeSerial("Config reloaded from storage after save"); + clearEncryptionSession(); +#ifdef TARGET_ESP32 + initWiFi(); +#endif +} bool encryptResponse(uint8_t* plaintext, uint16_t plaintext_len, uint8_t* ciphertext, uint16_t* ciphertext_len, uint8_t* nonce, uint8_t* auth_tag); bool isEncryptionEnabled(); @@ -49,14 +64,42 @@ struct ResponseQueueItem { }; extern WiFiClient wifiClient; extern bool wifiServerConnected; -extern bool wifiImageRequestPending; -extern uint32_t wifiNextImageRequestTime; -extern uint32_t wifiPollInterval; extern ResponseQueueItem responseQueue[10]; extern uint8_t responseQueueHead; extern uint8_t responseQueueTail; static constexpr uint8_t RESPONSE_QUEUE_SIZE_LOCAL = 10; static constexpr uint16_t MAX_RESPONSE_SIZE_LOCAL = 512; + +static void send_wifi_lan_frame(const uint8_t* payload, uint16_t len) { + if (!wifiServerConnected || !wifiClient.connected() || len == 0) { + return; + } + uint8_t hdr[2] = { (uint8_t)(len & 0xFF), (uint8_t)((len >> 8) & 0xFF) }; + if (wifiClient.write(hdr, 2) != 2 || wifiClient.write(payload, len) != len) { + writeSerial("ERROR: LAN response write incomplete", true); + } +} + +/** Mirror responses to BLE only when a central is connected; LAN already got send_wifi_lan_frame. */ +static void esp32_queue_ble_notify_copy(const uint8_t* response, uint16_t len) { + if (len > MAX_RESPONSE_SIZE_LOCAL) { + writeSerial("ERROR: Response too large for queue (" + String(len) + " > " + String(MAX_RESPONSE_SIZE_LOCAL) + ")", true); + return; + } + if (pServer == nullptr || pServer->getConnectedCount() == 0) { + return; + } + uint8_t nextHead = (responseQueueHead + 1) % RESPONSE_QUEUE_SIZE_LOCAL; + if (nextHead == responseQueueTail) { + writeSerial("ERROR: Response queue full, dropping response", true); + return; + } + memcpy(responseQueue[responseQueueHead].data, response, len); + responseQueue[responseQueueHead].len = len; + responseQueue[responseQueueHead].pending = true; + responseQueueHead = nextHead; + writeSerial("ESP32: Response queued (queue size: " + String((responseQueueHead - responseQueueTail + RESPONSE_QUEUE_SIZE_LOCAL) % RESPONSE_QUEUE_SIZE_LOCAL) + ")", true); +} #endif #ifdef TARGET_NRF @@ -77,9 +120,6 @@ static constexpr uint8_t FIRMWARE_SHA_HEX_BYTES = 40; static const char kFirmwareShaPlaceholder[FIRMWARE_SHA_HEX_BYTES + 1] = "0000000000000000000000000000000000000000"; -#define TRANSMISSION_MODE_ZIP (1 << 1) -#define MAX_IMAGE_SIZE (54 * 1024) - void sendResponseUnencrypted(uint8_t* response, uint8_t len) { writeSerial("Sending unencrypted response (error/status):", true); writeSerial(" Length: " + String(len) + " bytes", true); @@ -93,45 +133,8 @@ void sendResponseUnencrypted(uint8_t* response, uint8_t len) { if (len > 32) hexDump += " ..."; writeSerial(hexDump, true); #ifdef TARGET_ESP32 - if (wifiServerConnected && wifiClient.connected()) { - uint8_t tcpPacket[1024]; - uint32_t pos = 0; - uint32_t lengthPos = pos; - pos += 2; - tcpPacket[pos++] = 0x01; - tcpPacket[pos++] = 0x00; - uint8_t responsePacketId = (len > 1) ? response[1] : 0x00; - tcpPacket[pos++] = responsePacketId; - memcpy(&tcpPacket[pos], response, len); - pos += len; - uint32_t dataLen = pos - 2; - uint16_t crc = calculateCRC16CCITT(&tcpPacket[2], dataLen); - tcpPacket[pos++] = crc & 0xFF; - tcpPacket[pos++] = (crc >> 8) & 0xFF; - uint16_t totalLength = pos; - tcpPacket[lengthPos] = totalLength & 0xFF; - tcpPacket[lengthPos + 1] = (totalLength >> 8) & 0xFF; - size_t bytesWritten = wifiClient.write(tcpPacket, pos); - if (bytesWritten == pos) { - writeSerial("TCP response sent (" + String(bytesWritten) + " bytes)", true); - } else { - writeSerial("ERROR: TCP response incomplete (expected " + String(pos) + ", wrote " + String(bytesWritten) + ")", true); - } - } - if (len <= MAX_RESPONSE_SIZE_LOCAL) { - uint8_t nextHead = (responseQueueHead + 1) % RESPONSE_QUEUE_SIZE_LOCAL; - if (nextHead != responseQueueTail) { - memcpy(responseQueue[responseQueueHead].data, response, len); - responseQueue[responseQueueHead].len = len; - responseQueue[responseQueueHead].pending = true; - responseQueueHead = nextHead; - writeSerial("ESP32: Response queued (queue size: " + String((responseQueueHead - responseQueueTail + RESPONSE_QUEUE_SIZE_LOCAL) % RESPONSE_QUEUE_SIZE_LOCAL) + ")", true); - } else { - writeSerial("ERROR: Response queue full, dropping response", true); - } - } else { - writeSerial("ERROR: Response too large for queue (" + String(len) + " > " + String(MAX_RESPONSE_SIZE_LOCAL) + ")", true); - } + send_wifi_lan_frame(response, len); + esp32_queue_ble_notify_copy(response, len); #endif #ifdef TARGET_NRF if (Bluefruit.connected() && imageCharacteristic.notifyEnabled()) { @@ -145,7 +148,6 @@ void sendResponseUnencrypted(uint8_t* response, uint8_t len) { writeSerial(nrfHexDump, true); writeSerial("NRF: BLE notification sent (" + String(len) + " bytes)", true); imageCharacteristic.notify(response, len); - delay(50); } else { writeSerial("ERROR: Cannot send BLE response - not connected or notifications not enabled", true); writeSerial(" Connected: " + String(Bluefruit.connected() ? "yes" : "no"), true); @@ -159,6 +161,8 @@ void sendResponse(uint8_t* response, uint8_t len) { if (isAuthenticated() && len >= 2) { uint16_t command = (response[0] << 8) | response[1]; uint8_t status = (len >= 3) ? response[2] : 0x00; + // Encrypt all authenticated responses except auth/version handshakes and FE/FF status. + // (0x0070–0x0074 direct-write/LED acks must be encrypted too — LAN/BLE clients decrypt every response.) if (command != 0x0050 && command != 0x0043 && status != 0xFE && status != 0xFF) { uint8_t nonce[16]; uint8_t auth_tag[12]; @@ -192,45 +196,8 @@ void sendResponse(uint8_t* response, uint8_t len) { if (len > 32) hexDump += " ..."; writeSerial(hexDump, true); #ifdef TARGET_ESP32 - if (wifiServerConnected && wifiClient.connected()) { - uint8_t tcpPacket[1024]; - uint32_t pos = 0; - uint32_t lengthPos = pos; - pos += 2; - tcpPacket[pos++] = 0x01; - tcpPacket[pos++] = 0x00; - uint8_t responsePacketId = (len > 1) ? response[1] : 0x00; - tcpPacket[pos++] = responsePacketId; - memcpy(&tcpPacket[pos], response, len); - pos += len; - uint32_t dataLen = pos - 2; - uint16_t crc = calculateCRC16CCITT(&tcpPacket[2], dataLen); - tcpPacket[pos++] = crc & 0xFF; - tcpPacket[pos++] = (crc >> 8) & 0xFF; - uint16_t totalLength = pos; - tcpPacket[lengthPos] = totalLength & 0xFF; - tcpPacket[lengthPos + 1] = (totalLength >> 8) & 0xFF; - size_t bytesWritten = wifiClient.write(tcpPacket, pos); - if (bytesWritten == pos) { - writeSerial("TCP response sent (" + String(bytesWritten) + " bytes)", true); - } else { - writeSerial("ERROR: TCP response incomplete (expected " + String(pos) + ", wrote " + String(bytesWritten) + ")", true); - } - } - if (len <= MAX_RESPONSE_SIZE_LOCAL) { - uint8_t nextHead = (responseQueueHead + 1) % RESPONSE_QUEUE_SIZE_LOCAL; - if (nextHead != responseQueueTail) { - memcpy(responseQueue[responseQueueHead].data, response, len); - responseQueue[responseQueueHead].len = len; - responseQueue[responseQueueHead].pending = true; - responseQueueHead = nextHead; - writeSerial("ESP32: Response queued (queue size: " + String((responseQueueHead - responseQueueTail + RESPONSE_QUEUE_SIZE_LOCAL) % RESPONSE_QUEUE_SIZE_LOCAL) + ")", true); - } else { - writeSerial("ERROR: Response queue full, dropping response", true); - } - } else { - writeSerial("ERROR: Response too large for queue (" + String(len) + " > " + String(MAX_RESPONSE_SIZE_LOCAL) + ")", true); - } + send_wifi_lan_frame(response, len); + esp32_queue_ble_notify_copy(response, len); #endif #ifdef TARGET_NRF if (Bluefruit.connected() && imageCharacteristic.notifyEnabled()) { @@ -244,7 +211,7 @@ void sendResponse(uint8_t* response, uint8_t len) { writeSerial(nrfHexDump, true); writeSerial("NRF: BLE notification sent (" + String(len) + " bytes)", true); imageCharacteristic.notify(response, len); - delay(50); + delay(20); } else { writeSerial("ERROR: Cannot send BLE response - not connected or notifications not enabled", true); writeSerial(" Connected: " + String(Bluefruit.connected() ? "yes" : "no"), true); @@ -375,7 +342,11 @@ void handleReadConfig() { offset += chunkSize; remaining -= chunkSize; chunkNumber++; +#ifdef TARGET_ESP32 + delay(1); +#else delay(50); +#endif } } else { uint8_t errorResponse[] = {0xFF, 0x40, 0x00, 0x00}; @@ -420,7 +391,11 @@ void handleWriteConfig(uint8_t* data, uint16_t len) { } uint8_t responseOk[] = {0x00, 0x41, 0x00, 0x00}; uint8_t responseErr[] = {0xFF, 0x41, 0x00, 0x00}; - sendResponse(saveConfig(data, len) ? responseOk : responseErr, 4); + bool ok = saveConfig(data, len); + if (ok) { + reloadConfigAfterSave(); + } + sendResponse(ok ? responseOk : responseErr, 4); } void handleWriteConfigChunk(uint8_t* data, uint16_t len) { @@ -451,7 +426,11 @@ void handleWriteConfigChunk(uint8_t* data, uint16_t len) { if (chunkedWriteState.receivedChunks >= chunkedWriteState.expectedChunks) { uint8_t ok[] = {0x00, 0x42, 0x00, 0x00}; uint8_t err[] = {0xFF, 0x42, 0x00, 0x00}; - sendResponse(saveConfig(chunkedWriteState.buffer, chunkedWriteState.receivedSize) ? ok : err, 4); + bool saved = saveConfig(chunkedWriteState.buffer, chunkedWriteState.receivedSize); + if (saved) { + reloadConfigAfterSave(); + } + sendResponse(saved ? ok : err, 4); chunkedWriteState.active = false; chunkedWriteState.receivedSize = 0; chunkedWriteState.receivedChunks = 0; @@ -461,126 +440,6 @@ void handleWriteConfigChunk(uint8_t* data, uint16_t len) { } } -void sendConnectionNotification(uint8_t status) { -#ifdef TARGET_ESP32 - if (!wifiClient.connected()) return; - String deviceName = "OD" + getChipIdHex(); - uint32_t timestamp = millis() / 1000; - uint8_t packet[1024]; - uint32_t pos = 0; - uint32_t lengthPos = pos; - pos += 2; - packet[pos++] = 0x01; - packet[pos++] = 0x00; - packet[pos++] = 0x27; - memset(&packet[pos], 0, 32); - uint8_t nameLen = deviceName.length(); - if (nameLen > 31) nameLen = 31; - memcpy(&packet[pos], deviceName.c_str(), nameLen); - pos += 32; - packet[pos++] = getFirmwareMajor(); - packet[pos++] = getFirmwareMinor(); - packet[pos++] = status; - packet[pos++] = timestamp & 0xFF; - packet[pos++] = (timestamp >> 8) & 0xFF; - packet[pos++] = (timestamp >> 16) & 0xFF; - packet[pos++] = (timestamp >> 24) & 0xFF; - memset(&packet[pos], 0, 25); - pos += 25; - uint32_t dataLen = pos - 2; - uint16_t crc = calculateCRC16CCITT(&packet[2], dataLen); - packet[pos++] = crc & 0xFF; - packet[pos++] = (crc >> 8) & 0xFF; - uint16_t totalLength = pos; - packet[lengthPos] = totalLength & 0xFF; - packet[lengthPos + 1] = (totalLength >> 8) & 0xFF; - wifiClient.write(packet, pos); -#else - (void)status; -#endif -} - -void sendDisplayAnnouncement() { -#ifdef TARGET_ESP32 - if (!wifiClient.connected()) return; - uint8_t packet[1024]; - uint32_t pos = 0; - uint32_t lengthPos = pos; - pos += 2; - packet[pos++] = 0x01; - packet[pos++] = 0x00; - packet[pos++] = 0x01; - packet[pos++] = 0x01; - uint16_t width = globalConfig.displays[0].pixel_width; - packet[pos++] = width & 0xFF; - packet[pos++] = (width >> 8) & 0xFF; - uint16_t height = globalConfig.displays[0].pixel_height; - packet[pos++] = height & 0xFF; - packet[pos++] = (height >> 8) & 0xFF; - packet[pos++] = globalConfig.displays[0].color_scheme; - uint16_t firmwareId = 0x0001; - packet[pos++] = firmwareId & 0xFF; - packet[pos++] = (firmwareId >> 8) & 0xFF; - uint16_t firmwareVersion = (getFirmwareMajor() << 8) | getFirmwareMinor(); - packet[pos++] = firmwareVersion & 0xFF; - packet[pos++] = (firmwareVersion >> 8) & 0xFF; - uint16_t manufacturerId = globalConfig.manufacturer_data.manufacturer_id; - packet[pos++] = manufacturerId & 0xFF; - packet[pos++] = (manufacturerId >> 8) & 0xFF; - uint16_t modelId = 0x0001; - packet[pos++] = modelId & 0xFF; - packet[pos++] = (modelId >> 8) & 0xFF; - uint16_t maxCompressedSize = (globalConfig.displays[0].transmission_modes & TRANSMISSION_MODE_ZIP) ? MAX_IMAGE_SIZE : 0; - packet[pos++] = maxCompressedSize & 0xFF; - packet[pos++] = (maxCompressedSize >> 8) & 0xFF; - uint8_t rotation = globalConfig.displays[0].rotation; - if (rotation > 3) rotation = 0; - packet[pos++] = rotation; - uint32_t dataLen = pos - 2; - uint16_t crc = calculateCRC16CCITT(&packet[2], dataLen); - packet[pos++] = crc & 0xFF; - packet[pos++] = (crc >> 8) & 0xFF; - uint16_t totalLength = pos; - packet[lengthPos] = totalLength & 0xFF; - packet[lengthPos + 1] = (totalLength >> 8) & 0xFF; - wifiClient.write(packet, pos); -#endif -} - -void sendImageRequest() { -#ifdef TARGET_ESP32 - if (!wifiClient.connected()) return; - wifiImageRequestPending = true; - wifiNextImageRequestTime = millis() + (wifiPollInterval * 1000); - uint8_t packet[1024]; - uint32_t pos = 0; - uint32_t lengthPos = pos; - pos += 2; - packet[pos++] = 0x01; - packet[pos++] = 0x00; - packet[pos++] = 0x02; - packet[pos++] = 0x02; - float batteryVoltage = readBatteryVoltage(); - uint8_t batteryPercent = 0xFF; - if (batteryVoltage > 0) { - if (batteryVoltage >= 4.2) batteryPercent = 100; - else if (batteryVoltage >= 3.0) batteryPercent = (uint8_t)(((batteryVoltage - 3.0) / 1.2) * 100); - else batteryPercent = 0; - } - packet[pos++] = batteryPercent; - int8_t rssi = (int8_t)WiFi.RSSI(); - packet[pos++] = (uint8_t)rssi; - uint32_t dataLen = pos - 2; - uint16_t crc = calculateCRC16CCITT(&packet[2], dataLen); - packet[pos++] = crc & 0xFF; - packet[pos++] = (crc >> 8) & 0xFF; - uint16_t totalLength = pos; - packet[lengthPos] = totalLength & 0xFF; - packet[lengthPos + 1] = (totalLength >> 8) & 0xFF; - wifiClient.write(packet, pos); -#endif -} - #ifdef TARGET_NRF typedef uint16_t BLEConnHandle; typedef BLECharacteristic* BLECharPtr; @@ -707,6 +566,10 @@ void imageDataWritten(BLEConnHandle conn_hdl, BLECharPtr chr, uint8_t* data, uin writeSerial("=== LED ACTIVATE COMMAND (0x0073) ==="); handleLedActivate(data + 2, len - 2); break; + case 0x0075: + writeSerial("=== BUZZER ACTIVATE COMMAND (0x0075) ==="); + handleBuzzerActivate(data + 2, len - 2); + break; case 0x0051: writeSerial("=== ENTER DFU MODE COMMAND (0x0051) ==="); enterDFUMode(); diff --git a/src/communication.h b/src/communication.h index ea90cd4..1c0f45b 100644 --- a/src/communication.h +++ b/src/communication.h @@ -14,8 +14,5 @@ void handleReadMSD(); void handleReadConfig(); void handleWriteConfig(uint8_t* data, uint16_t len); void handleWriteConfigChunk(uint8_t* data, uint16_t len); -void sendConnectionNotification(uint8_t status); -void sendDisplayAnnouncement(); -void sendImageRequest(); #endif diff --git a/src/config_parser.cpp b/src/config_parser.cpp index fa9bf27..9539d5f 100644 --- a/src/config_parser.cpp +++ b/src/config_parser.cpp @@ -1,5 +1,7 @@ #include "config_parser.h" #include "structs.h" + +static_assert(sizeof(struct WifiConfig) == 160, "wifi_config must match config.yaml (32+32+1+95)"); #include "encryption_state.h" #include "encryption.h" #include @@ -25,14 +27,6 @@ using namespace Adafruit_LittleFS_Namespace; #define DEVICE_FLAG_XIAOINIT (1 << 1) #define DEVICE_FLAG_WS_PP_INIT (1 << 2) #endif -#ifndef TRANSMISSION_MODE_RAW -#define TRANSMISSION_MODE_RAW (1 << 0) -#define TRANSMISSION_MODE_ZIP (1 << 1) -#define TRANSMISSION_MODE_G5 (1 << 2) -#define TRANSMISSION_MODE_DIRECT_WRITE (1 << 3) -#define TRANSMISSION_MODE_CLEAR_ON_BOOT (1 << 7) -#endif - void writeSerial(String message, bool newLine = true); extern struct GlobalConfig globalConfig; @@ -323,122 +317,136 @@ bool loadGlobalConfig(){ return false; } break; - case 0x26: // wifi_config + case 0x28: // touch_controller + if (globalConfig.touch_controller_count < 4 && offset + sizeof(struct TouchController) <= configLen - 2) { + memcpy(&globalConfig.touch_controllers[globalConfig.touch_controller_count], &configData[offset], sizeof(struct TouchController)); + offset += sizeof(struct TouchController); + globalConfig.touch_controller_count++; + } else if (globalConfig.touch_controller_count >= 4) { + writeSerial("WARNING: Maximum touch_controller count reached, skipping"); + offset += sizeof(struct TouchController); + } else { + writeSerial("ERROR: Not enough data for touch_controller"); + globalConfig.loaded = false; + return false; + } + break; + case 0x29: // passive_buzzer + if (globalConfig.passive_buzzer_count < 4 && offset + sizeof(struct PassiveBuzzerConfig) <= configLen - 2) { + memcpy(&globalConfig.passive_buzzers[globalConfig.passive_buzzer_count], &configData[offset], sizeof(struct PassiveBuzzerConfig)); + offset += sizeof(struct PassiveBuzzerConfig); + globalConfig.passive_buzzer_count++; + } else if (globalConfig.passive_buzzer_count >= 4) { + writeSerial("WARNING: Maximum passive_buzzer count reached, skipping"); + offset += sizeof(struct PassiveBuzzerConfig); + } else { + writeSerial("ERROR: Not enough data for passive_buzzer"); + globalConfig.loaded = false; + return false; + } + break; + case 0x26: // wifi_config (see struct WifiConfig) { - const uint16_t WIFI_CONFIG_SIZE = 162; - if (offset + WIFI_CONFIG_SIZE <= configLen) { - memcpy(wifiSsid, &configData[offset], 32); - wifiSsid[32] = '\0'; // Ensure null termination - uint8_t ssidLen = 0; - while (ssidLen < 32 && wifiSsid[ssidLen] != '\0') ssidLen++; - offset += 32; - memcpy(wifiPassword, &configData[offset], 32); - wifiPassword[32] = '\0'; // Ensure null termination - uint8_t passwordLen = 0; - while (passwordLen < 32 && wifiPassword[passwordLen] != '\0') passwordLen++; - offset += 32; - wifiEncryptionType = configData[offset++]; - #ifdef TARGET_ESP32 - // Parse server configuration from reserved bytes - // First, read as string (like SSID) - memcpy(wifiServerUrl, &configData[offset], 64); - wifiServerUrl[64] = '\0'; // Ensure null termination - - // Check if it's stored as a string (has null terminator in first few bytes) - // or as a 4-byte IP address (numeric format from config tool) - bool isStringFormat = false; - for (int i = 0; i < 64; i++) { - if (wifiServerUrl[i] == '\0') { - isStringFormat = true; - break; - } - // If we find a non-printable character (except null), it's likely binary - if (i > 0 && wifiServerUrl[i] < 32 && wifiServerUrl[i] != '\0') { - break; - } - } - - // If first 4 bytes look like an IP address in numeric format (little-endian) - // and there's no null terminator in first 5 bytes, convert to IP string - // Check if bytes 0-3 are non-zero and byte 4 is null (indicating 4-byte format) - if (!isStringFormat && wifiServerUrl[4] == '\0' && - (wifiServerUrl[0] != 0 || wifiServerUrl[1] != 0 || - wifiServerUrl[2] != 0 || wifiServerUrl[3] != 0)) { - // The config tool stores IP as 32-bit integer in little-endian format - // Bytes are: [byte0][byte1][byte2][byte3] = IP address - // Convert 4-byte IP (stored as little-endian) to string format - uint8_t ip[4]; - ip[0] = configData[offset]; - ip[1] = configData[offset + 1]; - ip[2] = configData[offset + 2]; - ip[3] = configData[offset + 3]; - snprintf(wifiServerUrl, 65, "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]); - writeSerial("Converted numeric IP to string: \"" + String(wifiServerUrl) + "\""); - } else if (!isStringFormat && wifiServerUrl[0] != '\0') { - // Try to interpret as 32-bit integer (little-endian) and convert to IP - uint32_t ipNum = (uint32_t)configData[offset] | - ((uint32_t)configData[offset + 1] << 8) | - ((uint32_t)configData[offset + 2] << 16) | - ((uint32_t)configData[offset + 3] << 24); - // Convert to IP string (interpret as big-endian IP address) - uint8_t ip[4]; - ip[0] = (ipNum >> 24) & 0xFF; - ip[1] = (ipNum >> 16) & 0xFF; - ip[2] = (ipNum >> 8) & 0xFF; - ip[3] = ipNum & 0xFF; - snprintf(wifiServerUrl, 65, "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]); - writeSerial("Converted 32-bit integer to IP string: \"" + String(wifiServerUrl) + "\""); - } - - offset += 64; - // Read port (2 bytes, network byte order) - wifiServerPort = (configData[offset] << 8) | configData[offset + 1]; - offset += 2; - - // Check if server is configured (URL not empty and not "0.0.0.0") - wifiServerConfigured = (wifiServerUrl[0] != '\0' && - strcmp(wifiServerUrl, "0.0.0.0") != 0); - if (wifiServerConfigured) { - writeSerial("Server configured: YES"); - writeSerial("Server URL: \"" + String(wifiServerUrl) + "\""); - writeSerial("Server Port: " + String(wifiServerPort)); - } else { - writeSerial("Server configured: NO"); - if (wifiServerUrl[0] == '\0') { - writeSerial("Reason: URL is empty"); - } else if (strcmp(wifiServerUrl, "0.0.0.0") == 0) { - writeSerial("Reason: URL is \"0.0.0.0\""); - } + if (offset + sizeof(struct WifiConfig) > configLen - 2) { + writeSerial("ERROR: Not enough data for wifi_config"); + globalConfig.loaded = false; + return false; + } + struct WifiConfig wc; + memcpy(&wc, &configData[offset], sizeof(wc)); + offset += sizeof(wc); + + memcpy(wifiSsid, wc.ssid, sizeof(wc.ssid)); + wifiSsid[32] = '\0'; + uint8_t ssidLen = 0; + while (ssidLen < 32 && wifiSsid[ssidLen] != '\0') ssidLen++; + + memcpy(wifiPassword, wc.password, sizeof(wc.password)); + wifiPassword[32] = '\0'; + uint8_t passwordLen = 0; + while (passwordLen < 32 && wifiPassword[passwordLen] != '\0') passwordLen++; + + wifiEncryptionType = wc.encryption_type; + +#ifdef TARGET_ESP32 + memcpy(wifiServerUrl, wc.reserved, 64); + wifiServerUrl[64] = '\0'; + + bool isStringFormat = false; + for (int i = 0; i < 64; i++) { + if (wifiServerUrl[i] == '\0') { + isStringFormat = true; + break; } - offset += 29; // Skip remaining reserved bytes - #else - offset += 95; // Skip all reserved bytes on non-ESP32 - #endif - wifiConfigured = true; - writeSerial("=== WiFi Configuration Loaded ==="); - writeSerial("SSID: \"" + String(wifiSsid) + "\""); - if (passwordLen > 0) { - writeSerial("Password: \"" + String(wifiPassword) + "\""); - } else { - writeSerial("Password: (empty)"); + if (i > 0 && wifiServerUrl[i] < 32 && wifiServerUrl[i] != '\0') { + break; } - String encTypeStr = "Unknown"; - switch(wifiEncryptionType) { - case 0x00: encTypeStr = "None (Open)"; break; - case 0x01: encTypeStr = "WEP"; break; - case 0x02: encTypeStr = "WPA"; break; - case 0x03: encTypeStr = "WPA2"; break; - case 0x04: encTypeStr = "WPA3"; break; + } + + if (!isStringFormat && wifiServerUrl[4] == '\0' && + (wifiServerUrl[0] != 0 || wifiServerUrl[1] != 0 || + wifiServerUrl[2] != 0 || wifiServerUrl[3] != 0)) { + uint8_t ip[4]; + ip[0] = wc.reserved[0]; + ip[1] = wc.reserved[1]; + ip[2] = wc.reserved[2]; + ip[3] = wc.reserved[3]; + snprintf(wifiServerUrl, 65, "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]); + writeSerial("Converted numeric IP to string: \"" + String(wifiServerUrl) + "\""); + } else if (!isStringFormat && wifiServerUrl[0] != '\0') { + uint32_t ipNum = (uint32_t)wc.reserved[0] | + ((uint32_t)wc.reserved[1] << 8) | + ((uint32_t)wc.reserved[2] << 16) | + ((uint32_t)wc.reserved[3] << 24); + uint8_t ip[4]; + ip[0] = (ipNum >> 24) & 0xFF; + ip[1] = (ipNum >> 16) & 0xFF; + ip[2] = (ipNum >> 8) & 0xFF; + ip[3] = ipNum & 0xFF; + snprintf(wifiServerUrl, 65, "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]); + writeSerial("Converted 32-bit integer to IP string: \"" + String(wifiServerUrl) + "\""); + } + + wifiServerPort = (uint16_t)(((uint16_t)wc.reserved[64] << 8) | wc.reserved[65]); + if (wifiServerPort == 0) { + wifiServerPort = 2446; + } + + wifiServerConfigured = (wifiServerUrl[0] != '\0' && + strcmp(wifiServerUrl, "0.0.0.0") != 0); + if (wifiServerConfigured) { + writeSerial("Server configured: YES"); + writeSerial("Server URL: \"" + String(wifiServerUrl) + "\""); + writeSerial("Server Port: " + String(wifiServerPort)); + } else { + writeSerial("Server configured: NO"); + if (wifiServerUrl[0] == '\0') { + writeSerial("Reason: URL is empty"); + } else if (strcmp(wifiServerUrl, "0.0.0.0") == 0) { + writeSerial("Reason: URL is \"0.0.0.0\""); } - writeSerial("Encryption Type: 0x" + String(wifiEncryptionType, HEX) + " (" + encTypeStr + ")"); - writeSerial("SSID length: " + String(ssidLen) + " bytes"); - writeSerial("Password length: " + String(passwordLen) + " bytes"); - writeSerial("WiFi configured: true"); + } +#endif + wifiConfigured = true; + writeSerial("=== WiFi Configuration Loaded ==="); + writeSerial("SSID: \"" + String(wifiSsid) + "\""); + if (passwordLen > 0) { + writeSerial("Password: \"" + String(wifiPassword) + "\""); } else { - writeSerial("ERROR: Not enough data for wifi_config"); - globalConfig.loaded = false; - return false; + writeSerial("Password: (empty)"); + } + String encTypeStr = "Unknown"; + switch (wifiEncryptionType) { + case 0x00: encTypeStr = "None (Open)"; break; + case 0x01: encTypeStr = "WEP"; break; + case 0x02: encTypeStr = "WPA"; break; + case 0x03: encTypeStr = "WPA2"; break; + case 0x04: encTypeStr = "WPA3"; break; } + writeSerial("Encryption Type: 0x" + String(wifiEncryptionType, HEX) + " (" + encTypeStr + ")"); + writeSerial("SSID length: " + String(ssidLen) + " bytes"); + writeSerial("Password length: " + String(passwordLen) + " bytes"); + writeSerial("WiFi configured: true"); } break; case 0x27: // security_config @@ -544,6 +552,8 @@ void printConfigSummary(){ #endif writeSerial(" WS_PP_INIT flag: " + String((globalConfig.system_config.device_flags & DEVICE_FLAG_WS_PP_INIT) ? "enabled" : "disabled")); writeSerial("Power Pin: " + String(globalConfig.system_config.pwr_pin)); + writeSerial("Power Pin 2: " + String(globalConfig.system_config.pwr_pin_2)); + writeSerial("Power Pin 3: " + String(globalConfig.system_config.pwr_pin_3)); writeSerial(""); writeSerial("--- Manufacturer Data ---"); writeSerial("Manufacturer ID: 0x" + String(globalConfig.manufacturer_data.manufacturer_id, HEX)); @@ -584,11 +594,12 @@ void printConfigSummary(){ writeSerial(" Partial Update: " + String(globalConfig.displays[i].partial_update_support ? "Yes" : "No")); writeSerial(" Color Scheme: 0x" + String(globalConfig.displays[i].color_scheme, HEX)); writeSerial(" Transmission Modes: 0x" + String(globalConfig.displays[i].transmission_modes, HEX)); - writeSerial(" RAW: " + String((globalConfig.displays[i].transmission_modes & TRANSMISSION_MODE_RAW) ? "enabled" : "disabled")); + writeSerial(" ZIPXL: " + String((globalConfig.displays[i].transmission_modes & TRANSMISSION_MODE_ZIPXL) ? "enabled" : "disabled")); writeSerial(" ZIP: " + String((globalConfig.displays[i].transmission_modes & TRANSMISSION_MODE_ZIP) ? "enabled" : "disabled")); writeSerial(" G5: " + String((globalConfig.displays[i].transmission_modes & TRANSMISSION_MODE_G5) ? "enabled" : "disabled")); writeSerial(" DIRECT_WRITE: " + String((globalConfig.displays[i].transmission_modes & TRANSMISSION_MODE_DIRECT_WRITE) ? "enabled" : "disabled")); writeSerial(" CLEAR_ON_BOOT: " + String((globalConfig.displays[i].transmission_modes & TRANSMISSION_MODE_CLEAR_ON_BOOT) ? "enabled" : "disabled")); + writeSerial(" Full update energy (mC): " + String(globalConfig.displays[i].full_update_mC)); writeSerial(""); } writeSerial("--- LED Configurations (" + String(globalConfig.led_count) + ") ---"); @@ -609,6 +620,7 @@ void printConfigSummary(){ writeSerial(" Instance: " + String(globalConfig.sensors[i].instance_number)); writeSerial(" Type: 0x" + String(globalConfig.sensors[i].sensor_type, HEX)); writeSerial(" Bus ID: " + String(globalConfig.sensors[i].bus_id)); + writeSerial(" I2C addr (7-bit) / MSD data start byte: " + String(globalConfig.sensors[i].i2c_addr_7bit) + " / " + String(globalConfig.sensors[i].msd_data_start_byte)); writeSerial(""); } writeSerial("--- Data Bus Configurations (" + String(globalConfig.data_bus_count) + ") ---"); @@ -649,6 +661,28 @@ void printConfigSummary(){ writeSerial(" Pulldowns: 0x" + String(globalConfig.binary_inputs[i].pulldowns, HEX)); writeSerial(""); } + writeSerial("--- Touch Controllers (" + String(globalConfig.touch_controller_count) + ") ---"); + for (int i = 0; i < globalConfig.touch_controller_count; i++) { + writeSerial("Touch " + String(i) + ":"); + writeSerial(" Instance: " + String(globalConfig.touch_controllers[i].instance_number)); + writeSerial(" IC type: " + String(globalConfig.touch_controllers[i].touch_ic_type)); + writeSerial(" Bus ID: " + String(globalConfig.touch_controllers[i].bus_id)); + writeSerial(" I2C addr (7-bit): 0x" + String(globalConfig.touch_controllers[i].i2c_addr_7bit, HEX)); + writeSerial(" INT/RST pins: " + String(globalConfig.touch_controllers[i].int_pin) + " / " + String(globalConfig.touch_controllers[i].rst_pin)); + writeSerial(" Display instance: " + String(globalConfig.touch_controllers[i].display_instance)); + writeSerial(" Flags: 0x" + String(globalConfig.touch_controllers[i].flags, HEX)); + writeSerial(" Poll ms / MSD start byte: " + String(globalConfig.touch_controllers[i].poll_interval_ms) + " / " + String(globalConfig.touch_controllers[i].touch_data_start_byte)); + writeSerial(""); + } + writeSerial("--- Passive buzzers (" + String(globalConfig.passive_buzzer_count) + ") ---"); + for (int i = 0; i < globalConfig.passive_buzzer_count; i++) { + writeSerial("Buzzer " + String(i) + ":"); + writeSerial(" Instance: " + String(globalConfig.passive_buzzers[i].instance_number)); + writeSerial(" Drive / enable pin: " + String(globalConfig.passive_buzzers[i].drive_pin) + " / " + String(globalConfig.passive_buzzers[i].enable_pin)); + writeSerial(" Flags: 0x" + String(globalConfig.passive_buzzers[i].flags, HEX)); + writeSerial(" Duty %: " + String(globalConfig.passive_buzzers[i].duty_percent)); + writeSerial(""); + } writeSerial("============================="); } diff --git a/src/device_control.cpp b/src/device_control.cpp index cd80ed0..6c0a351 100644 --- a/src/device_control.cpp +++ b/src/device_control.cpp @@ -1,5 +1,6 @@ #include "device_control.h" #include "structs.h" +#include "touch_input.h" #include #ifdef TARGET_NRF @@ -317,6 +318,10 @@ void initButtons() { for (uint8_t pinIdx = 0; pinIdx < 8; pinIdx++) { uint8_t pin = *instancePins[pinIdx]; if (pin == 0xFF) continue; + if (touch_input_gpio_is_touch_int(pin)) { + writeSerial("Button: skip pin " + String(pin) + " (reserved for GT911 INT)", true); + continue; + } if (buttonStateCount >= MAX_BUTTONS) break; ButtonState* btn = &buttonStates[buttonStateCount]; btn->button_id = (input->instance_number * 8) + pinIdx; diff --git a/src/display_seeed_gfx.cpp b/src/display_seeed_gfx.cpp new file mode 100644 index 0000000..6217652 --- /dev/null +++ b/src/display_seeed_gfx.cpp @@ -0,0 +1,174 @@ +#if defined(TARGET_ESP32) && defined(OPENDISPLAY_SEEED_GFX) + +// 4bpp framebuffer (initGrayMode(16)): matches TFT_eSprite / Seeed EPaper.pushImage 4bpp — +// per byte, left pixel (even x) = high nibble, right = low. Nibble value = TFT_GRAY_0..15 +// (0 black .. 15 white). Row stride = (width+1)/2 bytes. + +#include "display_seeed_gfx.h" +#include "display_service.h" +#include "structs.h" +#include +#include +#include +#include +#include "OpenDisplay/opendisplay_runtime_pins.h" + +extern struct GlobalConfig globalConfig; + +#ifndef TRANSMISSION_MODE_DIRECT_WRITE +#define TRANSMISSION_MODE_DIRECT_WRITE (1 << 3) +#endif + +static int8_t seeed_gfx_aux_pin(uint8_t p, int8_t default_gpio) { + if (p == 0 || p == 0xFF) { + return default_gpio; + } + return (int8_t)p; +} + +extern "C" { + +int8_t opnd_seeed_runtime_sclk = 7; +int8_t opnd_seeed_runtime_miso = 8; +int8_t opnd_seeed_runtime_mosi = 9; +int8_t opnd_seeed_runtime_cs = 10; +int8_t opnd_seeed_runtime_rst = 12; +int8_t opnd_seeed_runtime_busy = 13; +int8_t opnd_seeed_runtime_tft_enable = 11; +int8_t opnd_seeed_runtime_ite_enable = 21; + +volatile bool opnd_seeed_tcon_busy_timed_out = false; + +void opnd_seeed_tcon_busy_timeout_reset(void) { + opnd_seeed_tcon_busy_timed_out = false; +} + +bool opnd_seeed_tcon_busy_timeout_occurred(void) { + return opnd_seeed_tcon_busy_timed_out; +} + +void opendisplay_seeed_gfx_load_pins_from_display(const struct DisplayConfig* d, const struct SystemConfig* sys, uint16_t panel_ic_type) { + if (!d) return; + + switch (panel_ic_type) { + case PANEL_IC_SEEED_ED103TC2_1872X1404: + case PANEL_IC_SEEED_ED103TC2_1872X1404_4GRAY: + if (d->clk_pin != 0xFF) opnd_seeed_runtime_sclk = (int8_t)d->clk_pin; + if (d->data_pin != 0xFF) opnd_seeed_runtime_mosi = (int8_t)d->data_pin; + if (d->dc_pin != 0xFF) opnd_seeed_runtime_miso = (int8_t)d->dc_pin; + else opnd_seeed_runtime_miso = 8; + if (d->cs_pin != 0xFF) opnd_seeed_runtime_cs = (int8_t)d->cs_pin; + if (d->reset_pin != 0xFF) opnd_seeed_runtime_rst = (int8_t)d->reset_pin; + if (d->busy_pin != 0xFF) opnd_seeed_runtime_busy = (int8_t)d->busy_pin; + if (sys) { + opnd_seeed_runtime_tft_enable = seeed_gfx_aux_pin(sys->pwr_pin_2, 11); + opnd_seeed_runtime_ite_enable = seeed_gfx_aux_pin(sys->pwr_pin_3, 21); + } + break; + default: + break; + } +} + +} // extern "C" + +static EPaper g_seeed_epaper; +static uint32_t seeed_direct_offset; + +static bool seeed_gfx_panel_is_4gray(void) { + if (globalConfig.display_count < 1) return false; + return globalConfig.displays[0].panel_ic_type == PANEL_IC_SEEED_ED103TC2_1872X1404_4GRAY; +} + +static size_t fb_byte_size(void) { + uint32_t w = globalConfig.displays[0].pixel_width; + uint32_t h = globalConfig.displays[0].pixel_height; + if (seeed_gfx_panel_is_4gray()) { + return (size_t)((w * h + 1) / 2); + } + return (size_t)((w * h + 7) / 8); +} + +void seeed_gfx_prepare_hardware(void) { + if (globalConfig.display_count < 1) { + return; + } + const struct DisplayConfig& d = globalConfig.displays[0]; + opendisplay_seeed_gfx_load_pins_from_display(&d, &globalConfig.system_config, d.panel_ic_type); +} + +void seeed_gfx_epaper_begin(void) { + seeed_gfx_prepare_hardware(); + opnd_seeed_tcon_busy_timeout_reset(); + initOrRestoreWireForOpenDisplay(); + if (globalConfig.display_count >= 1) { + if (seeed_gfx_panel_is_4gray()) { + g_seeed_epaper.initGrayMode(16); + } else { + g_seeed_epaper.deinitGrayMode(); + } + } + g_seeed_epaper.begin(0); +} + +void seeed_gfx_full_update(void) { + g_seeed_epaper.update(); +} + +bool seeed_gfx_wait_refresh(int timeout_sec) { + (void)timeout_sec; + delay(300); + return true; +} + +void seeed_gfx_sleep_after_refresh(void) { + g_seeed_epaper.sleep(); +} + +void seeed_gfx_boot_write_row(uint16_t y, const uint8_t* row, unsigned pitch) { + void* p = g_seeed_epaper.getPointer(); + if (!p || !row) return; + unsigned w = globalConfig.displays[0].pixel_width; + unsigned row_pitch = seeed_gfx_panel_is_4gray() ? (unsigned)((w + 1) / 2) : (unsigned)((w + 7) / 8); + if (pitch < row_pitch) return; + memcpy((uint8_t*)p + (size_t)y * row_pitch, row, row_pitch); +} + +void seeed_gfx_boot_skip_planes(void) { +} + +void seeed_gfx_direct_write_reset(void) { + seeed_gfx_prepare_hardware(); + g_seeed_epaper.wake(); + seeed_direct_offset = 0; + void* p = g_seeed_epaper.getPointer(); + if (p) { + memset(p, 0xFF, fb_byte_size()); + } +} + +void seeed_gfx_direct_write_chunk(const uint8_t* data, uint32_t len) { + if (!data || len == 0) return; + uint8_t* base = (uint8_t*)g_seeed_epaper.getPointer(); + if (!base) return; + size_t maxb = fb_byte_size(); + size_t room = (seeed_direct_offset < maxb) ? (maxb - seeed_direct_offset) : 0; + size_t n = (len > room) ? room : (size_t)len; + if (n) { + memcpy(base + seeed_direct_offset, data, n); + seeed_direct_offset += n; + } +} + +void seeed_gfx_direct_refresh(int refresh_mode) { + g_seeed_epaper.update(); + if (refresh_mode != 1) { + g_seeed_epaper.update(); + } +} + +void seeed_gfx_direct_sleep(void) { + g_seeed_epaper.sleep(); +} + +#endif diff --git a/src/display_seeed_gfx.h b/src/display_seeed_gfx.h new file mode 100644 index 0000000..5a5e5c3 --- /dev/null +++ b/src/display_seeed_gfx.h @@ -0,0 +1,41 @@ +#ifndef DISPLAY_SEEED_GFX_H +#define DISPLAY_SEEED_GFX_H + +#include +#include + +#if defined(TARGET_ESP32) && defined(OPENDISPLAY_SEEED_GFX) + +#ifdef __cplusplus +extern "C" { +#endif + +struct DisplayConfig; +struct SystemConfig; + +void opendisplay_seeed_gfx_load_pins_from_display(const struct DisplayConfig* d, const struct SystemConfig* sys, uint16_t panel_ic_type); +void opnd_seeed_tcon_busy_timeout_reset(void); +bool opnd_seeed_tcon_busy_timeout_occurred(void); + +#ifdef __cplusplus +} +#endif + +void seeed_gfx_prepare_hardware(void); +void seeed_gfx_epaper_begin(void); +void seeed_gfx_full_update(void); +bool seeed_gfx_wait_refresh(int timeout_sec); +void seeed_gfx_sleep_after_refresh(void); + +void seeed_gfx_boot_write_row(uint16_t y, const uint8_t* row, unsigned pitch); +void seeed_gfx_boot_skip_planes(void); + +void seeed_gfx_direct_write_reset(void); +void seeed_gfx_direct_write_chunk(const uint8_t* data, uint32_t len); +/** refresh_mode: 0 = REFRESH_FULL (two GC16 passes, less ghosting), 1 = REFRESH_FAST (single pass). */ +void seeed_gfx_direct_refresh(int refresh_mode); +void seeed_gfx_direct_sleep(void); + +#endif + +#endif diff --git a/src/display_service.cpp b/src/display_service.cpp index 2e60737..5224920 100644 --- a/src/display_service.cpp +++ b/src/display_service.cpp @@ -5,10 +5,15 @@ #include #include #include "structs.h" +#include "buzzer_control.h" +#include "sensor_sht40.h" #include "communication.h" #include "encryption.h" #include "boot_screen.h" #include "uzlib.h" +#if defined(TARGET_ESP32) && defined(OPENDISPLAY_SEEED_GFX) +#include "display_seeed_gfx.h" +#endif #ifdef TARGET_NRF extern "C" { @@ -19,6 +24,7 @@ extern "C" { #ifdef TARGET_ESP32 #include +#include "wifi_service.h" #endif extern BBEPDISP bbep; @@ -44,10 +50,20 @@ extern bool directWritePlane2; extern bool directWriteBitplanes; extern bool directWriteCompressed; extern bool directWriteActive; -extern uint8_t compressedDataBuffer[]; +extern uint8_t* compressedDataBuffer; extern uint8_t dictionaryBuffer[]; extern uint8_t decompressionChunk[]; +uint32_t max_compressed_image_rx_bytes(uint8_t tm) { + if ((tm & TRANSMISSION_MODE_ZIP) == 0) return 0; + if ((tm & TRANSMISSION_MODE_ZIPXL) != 0 && + MAX_COMPRESSED_BUFFER_BYTES > (54u * 1024u)) { + return MAX_COMPRESSED_BUFFER_BYTES; + } + uint32_t stdlim = 54u * 1024u; + return stdlim < MAX_COMPRESSED_BUFFER_BYTES ? stdlim : MAX_COMPRESSED_BUFFER_BYTES; +} + #ifdef TARGET_ESP32 extern BLEAdvertisementData* advertisementData; extern BLEServer* pServer; @@ -90,7 +106,6 @@ void flashLed(uint8_t color, uint8_t brightness); #define AXP2101_REG_IRQ_STATUS3 0x46 #define AXP2101_REG_IRQ_STATUS4 0x47 #define AXP2101_REG_LDO_ONOFF_CTRL1 0x91 -#define TRANSMISSION_MODE_CLEAR_ON_BOOT (1 << 7) #define FONT_BASE_WIDTH 8 #define FONT_BASE_HEIGHT 8 #define FONT_SMALL_THRESHOLD 264 @@ -170,11 +185,28 @@ int mapEpd(int id){ case 0x003E: return EP1085_1360x480; case 0x003F: return EP31_240x320; case 0x0040: return EP75YR_800x480; + case 0x0041: return EP_PANEL_UNDEFINED; default: return EP_PANEL_UNDEFINED; } } +bool seeed_driver_used(void) { +#if !defined(TARGET_ESP32) || !defined(OPENDISPLAY_SEEED_GFX) + return false; +#else + if (globalConfig.display_count < 1) return false; + const struct DisplayConfig& d = globalConfig.displays[0]; + if (d.panel_ic_type != PANEL_IC_SEEED_ED103TC2_1872X1404 && + d.panel_ic_type != PANEL_IC_SEEED_ED103TC2_1872X1404_4GRAY) return false; + if (d.display_technology != 0 && d.display_technology != 1) return false; + return true; +#endif +} + bool waitforrefresh(int timeout){ +#if defined(TARGET_ESP32) && defined(OPENDISPLAY_SEEED_GFX) + if (seeed_driver_used()) return seeed_gfx_wait_refresh(timeout); +#endif for (size_t i = 0; i < (size_t)(timeout * 10); i++){ delay(100); if(i % 5 == 0) writeSerial(".", false); @@ -263,6 +295,21 @@ void initDataBuses(){ writeSerial("=== Data Bus Initialization Complete ===", true); } +void initOrRestoreWireForOpenDisplay(void) { +#ifdef TARGET_ESP32 + if (globalConfig.data_bus_count > 0) { + const struct DataBus& bus = globalConfig.data_buses[0]; + if (bus.bus_type == 0x01 && bus.pin_1 != 0xFF && bus.pin_2 != 0xFF) { + uint32_t hz = bus.bus_speed_hz ? bus.bus_speed_hz : 100000u; + Wire.begin((int)bus.pin_2, (int)bus.pin_1); + Wire.setClock(hz); + return; + } + } +#endif + Wire.begin(); +} + void initio(){ if(globalConfig.led_count > 0){ for (uint8_t i = 0; i < globalConfig.led_count; i++) { @@ -298,6 +345,7 @@ void initio(){ } } } + initPassiveBuzzers(); if(globalConfig.system_config.pwr_pin != 0xFF){ pinMode(globalConfig.system_config.pwr_pin, OUTPUT); digitalWrite(globalConfig.system_config.pwr_pin, LOW); @@ -351,19 +399,23 @@ void initSensors(){ writeSerial("Initializing sensor " + String(i) + " (instance " + String(sensor->instance_number) + ")", true); writeSerial(" Type: 0x" + String(sensor->sensor_type, HEX), true); writeSerial(" Bus ID: " + String(sensor->bus_id), true); - if(sensor->sensor_type == 0x0003){ // AXP2101 PMIC + if(sensor->sensor_type == SENSOR_TYPE_AXP2101){ writeSerial(" Detected AXP2101 PMIC sensor", true); } - else if(sensor->sensor_type == 0x0001){ // Temperature sensor + else if(sensor->sensor_type == SENSOR_TYPE_TEMPERATURE){ writeSerial(" Temperature sensor (initialization not implemented)", true); } - else if(sensor->sensor_type == 0x0002){ // Humidity sensor + else if(sensor->sensor_type == SENSOR_TYPE_HUMIDITY){ writeSerial(" Humidity sensor (initialization not implemented)", true); } + else if(sensor->sensor_type == SENSOR_TYPE_SHT40){ + writeSerial(" SHT40 (I2C + MSD slot)", true); + } else{ writeSerial(" Unknown sensor type 0x" + String(sensor->sensor_type, HEX), true); } } + initSht40Sensors(); writeSerial("=== Sensor Initialization Complete ===", true); } @@ -859,24 +911,52 @@ static void renderChar_1BPP(uint8_t* rowBuffer, const uint8_t* fontData, int fon void initDisplay(){ writeSerial("=== Initializing Display ===", true); if(globalConfig.display_count > 0){ - pwrmgm(true); - memset(&bbep, 0, sizeof(BBEPDISP)); - int panelType = mapEpd(globalConfig.displays[0].panel_ic_type); - bbepSetPanelType(&bbep, panelType); - bbepSetRotation(&bbep, globalConfig.displays[0].rotation * 90); - bbepInitIO(&bbep, globalConfig.displays[0].dc_pin, globalConfig.displays[0].reset_pin, globalConfig.displays[0].busy_pin, globalConfig.displays[0].cs_pin, globalConfig.displays[0].data_pin, globalConfig.displays[0].clk_pin, 8000000); - writeSerial(String("Height: ") + String(globalConfig.displays[0].pixel_height), true); - writeSerial(String("Width: ") + String(globalConfig.displays[0].pixel_width), true); - bbepWakeUp(&bbep); - bbepSendCMDSequence(&bbep, bbep.pInitFull); - if (! (globalConfig.displays[0].transmission_modes & TRANSMISSION_MODE_CLEAR_ON_BOOT)){ - writeBootScreenWithQr(); - bbepRefresh(&bbep, REFRESH_FULL); - waitforrefresh(60); - bbepSleep(&bbep, 1); - delay(200); +#if defined(TARGET_ESP32) && defined(OPENDISPLAY_SEEED_GFX) + if (seeed_driver_used()) { + pwrmgm(true); + writeSerial("Display: Seeed_GFX (panel_ic " + String(globalConfig.displays[0].panel_ic_type) + ", " + + String(globalConfig.displays[0].pixel_width) + "x" + String(globalConfig.displays[0].pixel_height) + ", " + + String(getBitsPerPixel()) + " bpp)", true); + seeed_gfx_epaper_begin(); + if (opnd_seeed_tcon_busy_timeout_occurred()) { + writeSerial("Seeed_GFX init failed (TCON busy timeout) — skipping boot refresh", true); + pwrmgm(false); + return; + } + writeSerial(String("Height: ") + String(globalConfig.displays[0].pixel_height), true); + writeSerial(String("Width: ") + String(globalConfig.displays[0].pixel_width), true); + if (! (globalConfig.displays[0].transmission_modes & TRANSMISSION_MODE_CLEAR_ON_BOOT)){ + writeBootScreenWithQr(); + writeSerial("EPD refresh: FULL (boot, Seeed)", true); + seeed_gfx_full_update(); + waitforrefresh(60); + seeed_gfx_sleep_after_refresh(); + delay(200); + } + pwrmgm(false); + } else +#endif + { + pwrmgm(true); + memset(&bbep, 0, sizeof(BBEPDISP)); + int panelType = mapEpd(globalConfig.displays[0].panel_ic_type); + bbepSetPanelType(&bbep, panelType); + bbepSetRotation(&bbep, globalConfig.displays[0].rotation * 90); + bbepInitIO(&bbep, globalConfig.displays[0].dc_pin, globalConfig.displays[0].reset_pin, globalConfig.displays[0].busy_pin, globalConfig.displays[0].cs_pin, globalConfig.displays[0].data_pin, globalConfig.displays[0].clk_pin, 8000000); + writeSerial(String("Height: ") + String(globalConfig.displays[0].pixel_height), true); + writeSerial(String("Width: ") + String(globalConfig.displays[0].pixel_width), true); + bbepWakeUp(&bbep); + bbepSendCMDSequence(&bbep, bbep.pInitFull); + if (! (globalConfig.displays[0].transmission_modes & TRANSMISSION_MODE_CLEAR_ON_BOOT)){ + writeBootScreenWithQr(); + writeSerial("EPD refresh: FULL (boot)", true); + bbepRefresh(&bbep, REFRESH_FULL); + waitforrefresh(60); + bbepSleep(&bbep, 1); + delay(200); + } + pwrmgm(false); } - pwrmgm(false); } else{ writeSerial("No display found", true); @@ -886,13 +966,19 @@ void initDisplay(){ int getplane() { uint8_t colorScheme = globalConfig.displays[0].color_scheme; - if (colorScheme == 0) return PLANE_0; + if (colorScheme == 0 || colorScheme == COLOR_SCHEME_GRAY16) return PLANE_0; if (colorScheme == 1 || colorScheme == 2) return PLANE_0; if (colorScheme == 5) return PLANE_1; return PLANE_1; } int getBitsPerPixel() { +#if defined(TARGET_ESP32) && defined(OPENDISPLAY_SEEED_GFX) + if (globalConfig.display_count > 0 && + globalConfig.displays[0].panel_ic_type == PANEL_IC_SEEED_ED103TC2_1872X1404_4GRAY) { + return 4; + } +#endif if (globalConfig.displays[0].color_scheme == 4) return 4; if (globalConfig.displays[0].color_scheme == 3) return 2; if (globalConfig.displays[0].color_scheme == 5) return 2; @@ -936,6 +1022,7 @@ float readChipTemperature() { } void updatemsdata(){ + pollSht40SensorsForMsd(); float batteryVoltage = readBatteryVoltage(); float chipTemperature = readChipTemperature(); uint16_t batteryVoltageMv = (uint16_t)(batteryVoltage * 1000); @@ -962,7 +1049,7 @@ void updatemsdata(){ Bluefruit.Advertising.addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE); Bluefruit.Advertising.addName(); Bluefruit.Advertising.addData(BLE_GAP_AD_TYPE_MANUFACTURER_SPECIFIC_DATA, msd_payload, 16); - Bluefruit.Advertising.setInterval(32, 400); + Bluefruit.Advertising.setInterval(256, 1600); Bluefruit.Advertising.setFastTimeout(1); Bluefruit.Advertising.stop(); Bluefruit.Advertising.start(0); @@ -991,14 +1078,24 @@ void updatemsdata(){ pAdvertising->start(); } } + opendisplay_mdns_update_msd_txt(); #endif mloopcounter++; mloopcounter &= 0x0F; } void handleDirectWriteCompressedData(uint8_t* data, uint16_t len) { + if (!compressedDataBuffer) { + cleanupDirectWriteState(false); + uint8_t errorResponse[] = {0xFF, 0xFF}; + sendResponse(errorResponse, sizeof(errorResponse)); + return; + } uint32_t newTotalSize = directWriteCompressedReceived + len; - if (newTotalSize > (54 * 1024)) { + uint32_t cap = (globalConfig.display_count > 0) + ? max_compressed_image_rx_bytes(globalConfig.displays[0].transmission_modes) + : 0; + if (cap == 0 || newTotalSize > cap) { cleanupDirectWriteState(true); uint8_t errorResponse[] = {0xFF, 0xFF}; sendResponse(errorResponse, sizeof(errorResponse)); @@ -1031,7 +1128,14 @@ void decompressDirectWriteData() { res = uzlib_uncompress(&d); size_t bytesOut = d.dest - d.dest_start; if (bytesOut > 0) { - bbepWriteData(&bbep, decompressionChunk, bytesOut); +#if defined(TARGET_ESP32) && defined(OPENDISPLAY_SEEED_GFX) + if (seeed_driver_used()) { + seeed_gfx_direct_write_chunk(decompressionChunk, (uint32_t)bytesOut); + } else +#endif + { + bbepWriteData(&bbep, decompressionChunk, bytesOut); + } directWriteBytesWritten += bytesOut; } } while (res == TINF_OK && directWriteBytesWritten < directWriteTotalBytes); @@ -1053,7 +1157,14 @@ void cleanupDirectWriteState(bool refreshDisplay) { directWriteRefreshMode = 0; directWriteStartTime = 0; if (refreshDisplay && displayPowerState) { - bbepSleep(&bbep, 1); +#if defined(TARGET_ESP32) && defined(OPENDISPLAY_SEEED_GFX) + if (seeed_driver_used()) { + seeed_gfx_direct_sleep(); + } else +#endif + { + bbepSleep(&bbep, 1); + } delay(200); } displayPowerState = false; @@ -1062,27 +1173,40 @@ void cleanupDirectWriteState(bool refreshDisplay) { void handleDirectWriteStart(uint8_t* data, uint16_t len) { if (directWriteActive) cleanupDirectWriteState(false); +#if defined(TARGET_ESP32) && defined(OPENDISPLAY_SEEED_GFX) + if (seeed_driver_used()) { + seeed_gfx_prepare_hardware(); + } +#endif uint8_t colorScheme = globalConfig.displays[0].color_scheme; directWriteBitplanes = (colorScheme == 1 || colorScheme == 2); directWritePlane2 = false; directWriteCompressed = (len >= 4); directWriteWidth = globalConfig.displays[0].pixel_width; directWriteHeight = globalConfig.displays[0].pixel_height; - if (directWriteBitplanes) directWriteTotalBytes = (directWriteWidth * directWriteHeight + 7) / 8; + uint32_t pixels = (uint32_t)directWriteWidth * (uint32_t)directWriteHeight; + if (directWriteBitplanes) directWriteTotalBytes = (pixels + 7) / 8; else { int bitsPerPixel = getBitsPerPixel(); - if (bitsPerPixel == 4) directWriteTotalBytes = (directWriteWidth * directWriteHeight + 1) / 2; - else if (bitsPerPixel == 2) directWriteTotalBytes = (directWriteWidth * directWriteHeight + 3) / 4; - else directWriteTotalBytes = (directWriteWidth * directWriteHeight + 7) / 8; + if (bitsPerPixel == 4) directWriteTotalBytes = (pixels + 1) / 2; + else if (bitsPerPixel == 2) directWriteTotalBytes = (pixels + 3) / 4; + else directWriteTotalBytes = (pixels + 7) / 8; } if (directWriteCompressed) { + if (!compressedDataBuffer) { + cleanupDirectWriteState(false); + uint8_t errorResponse[] = {0xFF, 0xFF}; + sendResponse(errorResponse, sizeof(errorResponse)); + return; + } memcpy(&directWriteDecompressedTotal, data, 4); directWriteCompressedBuffer = compressedDataBuffer; directWriteCompressedSize = 0; directWriteCompressedReceived = 0; if (len > 4) { uint32_t compressedDataLen = len - 4; - if (compressedDataLen > (54 * 1024)) { + uint32_t cap = max_compressed_image_rx_bytes(globalConfig.displays[0].transmission_modes); + if (compressedDataLen > cap) { cleanupDirectWriteState(false); uint8_t errorResponse[] = {0xFF, 0xFF}; sendResponse(errorResponse, sizeof(errorResponse)); @@ -1097,14 +1221,21 @@ void handleDirectWriteStart(uint8_t* data, uint16_t len) { directWriteStartTime = millis(); if (displayPowerState) { pwrmgm(false); - delay(100); + delay(50); } pwrmgm(true); - bbepInitIO(&bbep, globalConfig.displays[0].dc_pin, globalConfig.displays[0].reset_pin, globalConfig.displays[0].busy_pin, globalConfig.displays[0].cs_pin, globalConfig.displays[0].data_pin, globalConfig.displays[0].clk_pin, 8000000); - bbepWakeUp(&bbep); - bbepSendCMDSequence(&bbep, bbep.pInitFull); - bbepSetAddrWindow(&bbep, 0, 0, globalConfig.displays[0].pixel_width, globalConfig.displays[0].pixel_height); - bbepStartWrite(&bbep, directWriteBitplanes ? PLANE_0 : getplane()); +#if defined(TARGET_ESP32) && defined(OPENDISPLAY_SEEED_GFX) + if (seeed_driver_used()) { + seeed_gfx_direct_write_reset(); + } else +#endif + { + bbepInitIO(&bbep, globalConfig.displays[0].dc_pin, globalConfig.displays[0].reset_pin, globalConfig.displays[0].busy_pin, globalConfig.displays[0].cs_pin, globalConfig.displays[0].data_pin, globalConfig.displays[0].clk_pin, 8000000); + bbepWakeUp(&bbep); + bbepSendCMDSequence(&bbep, bbep.pInitFull); + bbepSetAddrWindow(&bbep, 0, 0, globalConfig.displays[0].pixel_width, globalConfig.displays[0].pixel_height); + bbepStartWrite(&bbep, directWriteBitplanes ? PLANE_0 : getplane()); + } uint8_t ackResponse[] = {0x00, 0x70}; sendResponse(ackResponse, sizeof(ackResponse)); } @@ -1118,7 +1249,14 @@ void handleDirectWriteData(uint8_t* data, uint16_t len) { uint32_t remainingBytes = (directWriteBytesWritten < directWriteTotalBytes) ? (directWriteTotalBytes - directWriteBytesWritten) : 0; uint16_t bytesToWrite = (len > remainingBytes) ? remainingBytes : len; if (bytesToWrite > 0) { - bbepWriteData(&bbep, data, bytesToWrite); +#if defined(TARGET_ESP32) && defined(OPENDISPLAY_SEEED_GFX) + if (seeed_driver_used()) { + seeed_gfx_direct_write_chunk(data, bytesToWrite); + } else +#endif + { + bbepWriteData(&bbep, data, bytesToWrite); + } directWriteBytesWritten += bytesToWrite; } if (directWriteBytesWritten >= directWriteTotalBytes) { @@ -1131,16 +1269,30 @@ void handleDirectWriteData(uint8_t* data, uint16_t len) { void handleDirectWriteEnd(uint8_t* data, uint16_t len) { if (!directWriteActive) return; + directWriteStartTime = 0; if (directWriteCompressed && directWriteCompressedReceived > 0) decompressDirectWriteData(); int refreshMode = REFRESH_FULL; if (data != nullptr && len >= 1 && data[0] == 1) refreshMode = REFRESH_FAST; + writeSerial(String("EPD refresh: ") + (refreshMode == REFRESH_FAST ? "FAST" : "FULL") + " (mode=" + String(refreshMode) + + ", end payload " + (data != nullptr && len > 0 ? ("0x" + String(data[0], HEX)) : String("none (auto)")) + ")", + true); uint8_t ackResponse[] = {0x00, 0x72}; sendResponse(ackResponse, sizeof(ackResponse)); - delay(100); - bbepRefresh(&bbep, refreshMode); - bool refreshSuccess = waitforrefresh(60); - bbepSleep(&bbep, 1); - delay(200); + delay(20); + bool refreshSuccess = false; +#if defined(TARGET_ESP32) && defined(OPENDISPLAY_SEEED_GFX) + if (seeed_driver_used()) { + seeed_gfx_direct_refresh(refreshMode); + refreshSuccess = waitforrefresh(60); + seeed_gfx_direct_sleep(); + } else +#endif + { + bbepRefresh(&bbep, refreshMode); + refreshSuccess = waitforrefresh(60); + bbepSleep(&bbep, 1); + } + delay(50); cleanupDirectWriteState(false); if (refreshSuccess) { uint8_t refreshResponse[] = {0x00, 0x73}; diff --git a/src/display_service.h b/src/display_service.h index d6d0707..98fbd0d 100644 --- a/src/display_service.h +++ b/src/display_service.h @@ -2,7 +2,11 @@ #define DISPLAY_SERVICE_H #include +#include +uint32_t max_compressed_image_rx_bytes(uint8_t transmission_modes); + +bool seeed_driver_used(void); int mapEpd(int id); bool waitforrefresh(int timeout); float readBatteryVoltage(); @@ -10,6 +14,8 @@ float readChipTemperature(); void updatemsdata(); void initio(); void initDataBuses(); +/** Re-apply I2C from data_bus[0] when set; else Wire.begin(). Call before TCON/touch on shared bus. */ +void initOrRestoreWireForOpenDisplay(void); void scanI2CDevices(); void initSensors(); void initAXP2101(uint8_t busId); diff --git a/src/driver.h b/src/driver.h new file mode 100644 index 0000000..eb08d48 --- /dev/null +++ b/src/driver.h @@ -0,0 +1,4 @@ +#ifndef OPENDISPLAY_GFX_DRIVER_H +#define OPENDISPLAY_GFX_DRIVER_H + +#endif diff --git a/src/main.cpp b/src/main.cpp index 8cfcee2..7d7b0c2 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,15 +1,54 @@ #include "main.h" +#ifdef TARGET_ESP32 +#include +#endif #include "boot_screen.h" #include "communication.h" #include "device_control.h" #include "display_service.h" +#include "touch_input.h" #include "encryption.h" +#if defined(TARGET_ESP32) && defined(OPENDISPLAY_LOG_UART) +#include +#ifndef OPENDISPLAY_LOG_UART_RX +#define OPENDISPLAY_LOG_UART_RX 44 +#endif +#ifndef OPENDISPLAY_LOG_UART_TX +#define OPENDISPLAY_LOG_UART_TX 43 +#endif +static HardwareSerial LogSerialPort(1); +#endif + +#if defined(TARGET_NRF) +static uint8_t s_compressedDataStorage[MAX_COMPRESSED_BUFFER_BYTES]; +uint8_t* compressedDataBuffer = s_compressedDataStorage; +#elif defined(TARGET_ESP32) && defined(TARGET_LARGE_MEMORY) && defined(BOARD_HAS_PSRAM) +uint8_t* compressedDataBuffer = nullptr; +#else +static uint8_t s_compressedDataStorage[MAX_COMPRESSED_BUFFER_BYTES]; +uint8_t* compressedDataBuffer = s_compressedDataStorage; +#endif + +void allocCompressedDataBuffer(void) { +#if defined(TARGET_ESP32) && defined(TARGET_LARGE_MEMORY) && defined(BOARD_HAS_PSRAM) + if (compressedDataBuffer) return; + compressedDataBuffer = (uint8_t*)heap_caps_malloc(MAX_COMPRESSED_BUFFER_BYTES, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); + if (!compressedDataBuffer) { + compressedDataBuffer = (uint8_t*)heap_caps_malloc(MAX_COMPRESSED_BUFFER_BYTES, MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT); + } +#endif +} + void setup() { - #ifndef DISABLE_USB_SERIAL + #if defined(TARGET_ESP32) && defined(OPENDISPLAY_LOG_UART) + LogSerialPort.begin(115200, SERIAL_8N1, OPENDISPLAY_LOG_UART_RX, OPENDISPLAY_LOG_UART_TX); + delay(100); + #elif !defined(DISABLE_USB_SERIAL) Serial.begin(115200); delay(100); #endif + allocCompressedDataBuffer(); writeSerial("=== FIRMWARE INFO ==="); writeSerial("Firmware Version: " + String(getFirmwareMajor()) + "." + String(getFirmwareMinor())); const char* shaCStr = SHA_STRING; @@ -53,17 +92,33 @@ void setup() { #elif defined(TARGET_NRF) ble_nrf_advertising_start(); #endif - writeSerial("BLE advertising started - waiting for connections..."); #ifdef TARGET_ESP32 initWiFi(); #endif updatemsdata(); initButtons(); + initTouchInput(); writeSerial("=== Setup completed successfully ==="); } void loop() { #ifdef TARGET_ESP32 + handleWiFiServer(); + static uint32_t lastWiFiCheck = 0; + if (wifiInitialized && (millis() - lastWiFiCheck > 10000)) { + lastWiFiCheck = millis(); + if (WiFi.status() != WL_CONNECTED && wifiConnected) { + writeSerial("WiFi connection lost (status: " + String(WiFi.status()) + ")"); + wifiConnected = false; + if (wifiServerConnected) { + disconnectWiFiServer(); + } + } else if (WiFi.status() == WL_CONNECTED && !wifiConnected) { + writeSerial("WiFi reconnected (IP: " + WiFi.localIP().toString() + ")"); + wifiConnected = true; + restartWiFiLanAfterReconnect(); + } + } if (woke_from_deep_sleep && advertising_timeout_active) { if (pServer && pServer->getConnectedCount() > 0) { writeSerial("BLE connection established - switching to full mode"); @@ -92,67 +147,37 @@ void loop() { commandQueueTail = (commandQueueTail + 1) % COMMAND_QUEUE_SIZE; writeSerial("Command processed"); } - if (responseQueueTail != responseQueueHead && pTxCharacteristic && pServer && pServer->getConnectedCount() > 0) { - writeSerial("ESP32: Sending queued response (" + String(responseQueue[responseQueueTail].len) + " bytes)"); - pTxCharacteristic->setValue(responseQueue[responseQueueTail].data, responseQueue[responseQueueTail].len); - pTxCharacteristic->notify(); - responseQueue[responseQueueTail].pending = false; - responseQueueTail = (responseQueueTail + 1) % RESPONSE_QUEUE_SIZE; - writeSerial("Response sent successfully"); + if (pTxCharacteristic && pServer && pServer->getConnectedCount() > 0) { + uint8_t bleDrain = 0; + while (responseQueueTail != responseQueueHead && bleDrain < 16) { + writeSerial("ESP32: Sending queued response (" + String(responseQueue[responseQueueTail].len) + " bytes)"); + pTxCharacteristic->setValue(responseQueue[responseQueueTail].data, responseQueue[responseQueueTail].len); + pTxCharacteristic->notify(); + responseQueue[responseQueueTail].pending = false; + responseQueueTail = (responseQueueTail + 1) % RESPONSE_QUEUE_SIZE; + writeSerial("Response sent successfully"); + bleDrain++; + } } if (directWriteActive && directWriteStartTime > 0) { uint32_t directWriteDuration = millis() - directWriteStartTime; - if (directWriteDuration > 120000) { // 120 second timeout + if (directWriteDuration > 900000UL) { // 15 minute timeout (upload + refresh window) writeSerial("ERROR: Direct write timeout (" + String(directWriteDuration) + " ms) - cleaning up stuck state"); cleanupDirectWriteState(true); } } #ifdef TARGET_ESP32 - handleWiFiServer(); - if (wifiServerConnected && wifiClient.connected() && !wifiImageRequestPending) { - uint32_t now = millis(); - bool timeToSend = false; - if (wifiNextImageRequestTime == 0) { - timeToSend = true; // Send immediately - } else if (now >= wifiNextImageRequestTime) { - timeToSend = true; // Time has come - } else if ((wifiNextImageRequestTime - now) > 0x7FFFFFFF) { - timeToSend = true; // millis() overflow detected (wifiNextImageRequestTime is in the past) - } - if (timeToSend) { - writeSerial("Sending scheduled Image Request (poll_interval=" + String(wifiPollInterval) + "s)"); - sendImageRequest(); - } - } - static uint32_t lastWiFiCheck = 0; - if (wifiInitialized && (millis() - lastWiFiCheck > 10000)) { - lastWiFiCheck = millis(); - if (WiFi.status() != WL_CONNECTED && wifiConnected) { - writeSerial("WiFi connection lost (status: " + String(WiFi.status()) + ")"); - wifiConnected = false; - if (wifiServerConnected) { - disconnectWiFiServer(); - } - } else if (WiFi.status() == WL_CONNECTED && !wifiConnected) { - writeSerial("WiFi reconnected (IP: " + WiFi.localIP().toString() + ")"); - wifiConnected = true; - // Reinitialize mDNS on reconnection - String deviceName = "OD" + getChipIdHex(); - if (MDNS.begin(deviceName.c_str())) { - writeSerial("mDNS responder restarted: " + deviceName + ".local"); - } else { - writeSerial("ERROR: Failed to restart mDNS responder"); - } - // Attempt to reconnect to server via mDNS - discoverAndConnectWiFiServer(); - } - } + const bool wifiLanSession = wifiInitialized && wifiServerConnected && wifiClient.connected(); + #else + const bool wifiLanSession = false; #endif - bool bleActive = (commandQueueTail != commandQueueHead) || + bool bleActive = (commandQueueTail != commandQueueHead) || (responseQueueTail != responseQueueHead) || - (pServer && pServer->getConnectedCount() > 0); + (pServer && pServer->getConnectedCount() > 0) || + wifiLanSession; if (bleActive) { - // Check for button events in fast loop + processButtonEvents(); + processTouchInput(); delay(1); } else { if (!woke_from_deep_sleep && deep_sleep_count == 0 && globalConfig.power_option.power_mode == 1) { @@ -177,6 +202,7 @@ void loop() { } updatemsdata(); processButtonEvents(); + processTouchInput(); if(!bleActive)writeSerial("Loop end: " + String(millis() / 100)); } #else @@ -187,6 +213,8 @@ void loop() { else{ idleDelay(500); } + processButtonEvents(); + processTouchInput(); writeSerial("Loop end: " + String(millis() / 100)); #endif } @@ -198,6 +226,7 @@ void idleDelay(uint32_t delayMs) { uint32_t remainingDelay = delayMs; while (remainingDelay > 0) { processButtonEvents(); + processTouchInput(); uint32_t chunkDelay = (remainingDelay > CHECK_INTERVAL_MS) ? CHECK_INTERVAL_MS : remainingDelay; delay(chunkDelay); remainingDelay -= chunkDelay; @@ -210,10 +239,12 @@ uint8_t pinToButtonIndex[64] = {0xFF}; // Map pin number to button index (max 6 #ifdef TARGET_ESP32 void minimalSetup() { + allocCompressedDataBuffer(); writeSerial("=== Minimal Setup (Deep Sleep Wake) ==="); full_config_init(); initio(); ble_init_esp32(true); // Update manufacturer data + initWiFi(false); writeSerial("=== BLE advertising started (minimal mode) ==="); writeSerial("Advertising for 10 seconds, waiting for connection..."); advertising_timeout_active = true; @@ -222,10 +253,19 @@ void minimalSetup() { void fullSetupAfterConnection() { writeSerial("=== Full Setup After Connection ==="); - memset(&bbep, 0, sizeof(BBEPDISP)); - int panelType = mapEpd(globalConfig.displays[0].panel_ic_type); - writeSerial("Panel type: " + String(panelType)); - bbepSetPanelType(&bbep, panelType); +#if defined(TARGET_ESP32) && defined(OPENDISPLAY_SEEED_GFX) + if (globalConfig.display_count > 0 && seeed_driver_used()) { + writeSerial("Panel: Seeed ED103 (bb_epaper not used)", true); + writeSerial("=== Full setup completed ==="); + return; + } +#endif + if (globalConfig.display_count > 0) { + memset(&bbep, 0, sizeof(BBEPDISP)); + int panelType = mapEpd(globalConfig.displays[0].panel_ic_type); + writeSerial("Panel type: " + String(panelType)); + bbepSetPanelType(&bbep, panelType); + } writeSerial("=== Full setup completed ==="); } @@ -268,7 +308,7 @@ void pwrmgm(bool onoff){ uint8_t axp2101_bus_id = 0xFF; bool axp2101_found = false; for(uint8_t i = 0; i < globalConfig.sensor_count; i++){ - if(globalConfig.sensors[i].sensor_type == 0x0003){ + if(globalConfig.sensors[i].sensor_type == SENSOR_TYPE_AXP2101){ axp2101_bus_id = globalConfig.sensors[i].bus_id; axp2101_found = true; break; @@ -289,22 +329,40 @@ void pwrmgm(bool onoff){ digitalWrite(48, HIGH); } } - if(onoff){ - pinMode(globalConfig.displays[0].reset_pin, OUTPUT); - pinMode(globalConfig.displays[0].cs_pin, OUTPUT); - pinMode(globalConfig.displays[0].dc_pin, OUTPUT); - pinMode(globalConfig.displays[0].clk_pin, OUTPUT); - pinMode(globalConfig.displays[0].data_pin, OUTPUT); - delay(200); - } - else{ - SPI.end(); - Wire.end(); - pinMode(globalConfig.displays[0].reset_pin, INPUT); - pinMode(globalConfig.displays[0].cs_pin, INPUT); - pinMode(globalConfig.displays[0].dc_pin, INPUT); - pinMode(globalConfig.displays[0].clk_pin, INPUT); - pinMode(globalConfig.displays[0].data_pin, INPUT); +#if defined(TARGET_ESP32) && defined(OPENDISPLAY_SEEED_GFX) + const bool seeed_driver_spi = seeed_driver_used(); +#else + const bool seeed_driver_spi = false; +#endif + if (!seeed_driver_spi) { + if(onoff){ + pinMode(globalConfig.displays[0].reset_pin, OUTPUT); + pinMode(globalConfig.displays[0].cs_pin, OUTPUT); + if (globalConfig.displays[0].dc_pin != 0xFF) { + pinMode(globalConfig.displays[0].dc_pin, OUTPUT); + } + pinMode(globalConfig.displays[0].clk_pin, OUTPUT); + pinMode(globalConfig.displays[0].data_pin, OUTPUT); + delay(200); + } + else{ + SPI.end(); + Wire.end(); + pinMode(globalConfig.displays[0].reset_pin, INPUT); + pinMode(globalConfig.displays[0].cs_pin, INPUT); + if (globalConfig.displays[0].dc_pin != 0xFF) { + pinMode(globalConfig.displays[0].dc_pin, INPUT); + } + pinMode(globalConfig.displays[0].clk_pin, INPUT); + pinMode(globalConfig.displays[0].data_pin, INPUT); + } + } else { + if (onoff) { + delay(200); + } else { + SPI.end(); + Wire.end(); + } } if(globalConfig.system_config.pwr_pin != 0xFF){ if(onoff){ @@ -321,7 +379,10 @@ void pwrmgm(bool onoff){ } void writeSerial(String message, bool newLine){ - #ifndef DISABLE_USB_SERIAL + #if defined(TARGET_ESP32) && defined(OPENDISPLAY_LOG_UART) + if (newLine) LogSerialPort.println(message); + else LogSerialPort.print(message); + #elif !defined(DISABLE_USB_SERIAL) if (newLine == true) Serial.println(message); else Serial.print(message); #endif diff --git a/src/main.h b/src/main.h index dbae623..264999f 100644 --- a/src/main.h +++ b/src/main.h @@ -34,7 +34,6 @@ using namespace Adafruit_LittleFS_Namespace; #define DECOMP_CHUNK 512 #define DECOMP_CHUNK_SIZE 4096 #define MAX_DICT_SIZE 32768 -#define MAX_IMAGE_SIZE (54 * 1024) #define MAX_BLOCKS 64 // Text rendering constants #define FONT_CHAR_WIDTH 16 // 7 font columns + 1 blank column, each doubled (8*2) @@ -71,13 +70,6 @@ using namespace Adafruit_LittleFS_Namespace; #define DEVICE_FLAG_XIAOINIT (1 << 1) // Bit 1: Call xiaoinit() after config load (nRF52840 only) #define DEVICE_FLAG_WS_PP_INIT (1 << 2) // Bit 2: Call ws_pp_init() after config load (Waveshare Photo Printer) -// Transmission mode bit definitions (for display.transmission_modes) -#define TRANSMISSION_MODE_RAW (1 << 0) // Bit 0: Raw transfer -#define TRANSMISSION_MODE_ZIP (1 << 1) // Bit 1: ZIP compressed transfer -#define TRANSMISSION_MODE_G5 (1 << 2) // Bit 2: Group 5 compression -#define TRANSMISSION_MODE_DIRECT_WRITE (1 << 3) // Bit 3: Direct write mode (bufferless) -#define TRANSMISSION_MODE_CLEAR_ON_BOOT (1 << 7) // Bit 7: Clear screen at bootup (writeTextAndFill with empty string) - #ifdef TARGET_NRF #include extern BLEDfu bledfu; @@ -128,7 +120,9 @@ void bbepSendCMDSequence(BBEPDISP *pBBEP, const uint8_t *pSeq); void bbepSetAddrWindow(BBEPDISP *pBBEP, int x, int y, int cx, int cy); void bbepWriteData(BBEPDISP *pBBEP, uint8_t *pData, int iLen); -uint8_t compressedDataBuffer[MAX_IMAGE_SIZE]; // Static buffer for compressed image data +extern uint8_t* compressedDataBuffer; +void allocCompressedDataBuffer(void); + uint8_t decompressionChunk[DECOMP_CHUNK_SIZE]; uint8_t dictionaryBuffer[MAX_DICT_SIZE]; uint8_t bleResponseBuffer[94]; @@ -172,20 +166,17 @@ char wifiPassword[33] = {0}; // 32 bytes + null terminator uint8_t wifiEncryptionType = 0; // 0x00=none, 0x01=WEP, 0x02=WPA, 0x03=WPA2, 0x04=WPA3 bool wifiConfigured = false; // True if WiFi config packet (0x26) was received and parsed #ifdef TARGET_ESP32 -#include -bool wifiConnected = false; // True if WiFi is currently connected -bool wifiInitialized = false; // True if WiFi initialization was attempted -char wifiServerUrl[65] = {0}; // 64 bytes + null terminator for server URL/hostname -uint16_t wifiServerPort = 2446; // Default server port -bool wifiServerConfigured = false; // True if server URL is configured -WiFiClient wifiClient; // TCP client connection -bool wifiServerConnected = false; // True if TCP connection to server is established -uint32_t wifiServerLastConnectAttempt = 0; // Timestamp of last connection attempt -uint8_t tcpReceiveBuffer[8192]; // TCP receive buffer -uint32_t tcpReceiveBufferPos = 0; // Current position in receive buffer -uint32_t wifiNextImageRequestTime = 0; // Timestamp when next Image Request should be sent (0 = send immediately) -uint32_t wifiPollInterval = 60; // Poll interval in seconds (default: 60 seconds) -bool wifiImageRequestPending = false; // True when Image Request has been sent and we're waiting for response +#include +bool wifiConnected = false; +bool wifiInitialized = false; +char wifiServerUrl[65] = {0}; +uint16_t wifiServerPort = 2446; +bool wifiServerConfigured = false; +WiFiServer wifiServer; +WiFiClient wifiClient; +bool wifiServerConnected = false; +uint8_t tcpReceiveBuffer[8192]; +uint32_t tcpReceiveBufferPos = 0; #endif // Direct write mode state (bufferless display writing) @@ -203,7 +194,7 @@ uint8_t directWriteRefreshMode = 0; // 0 = FULL (default), 1 = FAST/PARTIAL (if // Direct write compressed mode: use same buffer as regular image upload uint32_t directWriteCompressedSize = 0; // Total compressed size expected uint32_t directWriteCompressedReceived = 0; // Total compressed bytes received -uint8_t* directWriteCompressedBuffer = nullptr; // Pointer to compressedDataBuffer (static allocation only) +uint8_t* directWriteCompressedBuffer = nullptr; // Points at compressedDataBuffer when compressed direct-write is active uint32_t directWriteStartTime = 0; // Timestamp when direct write started (for timeout detection) bool displayPowerState = false; // Track display power state (true = powered on, false = powered off) diff --git a/src/sensor_sht40.cpp b/src/sensor_sht40.cpp new file mode 100644 index 0000000..a1179c5 --- /dev/null +++ b/src/sensor_sht40.cpp @@ -0,0 +1,155 @@ +#include "sensor_sht40.h" +#include "structs.h" +#include "display_service.h" + +#include +#include + +extern struct GlobalConfig globalConfig; +extern uint8_t dynamicreturndata[11]; + +static_assert(sizeof(SensorData) == 30, "SensorData must remain 30 bytes"); + +static uint8_t sht40_crc8(const uint8_t* data, uint8_t len) { + uint8_t crc = 0xFF; + for (uint8_t i = 0; i < len; i++) { + crc ^= data[i]; + for (uint8_t bit = 8; bit > 0; bit--) { + if (crc & 0x80) { + crc = (uint8_t)((crc << 1) ^ 0x31u); + } else { + crc <<= 1; + } + } + } + return crc; +} + +static uint8_t sht40_addr_7bit(const SensorData* s) { + uint8_t a = s->i2c_addr_7bit; + if (a == 0 || a == 0xFF) { + return 0x44; + } + return a; +} + +static uint8_t sht40_msd_start(const SensorData* s) { + uint8_t st = s->msd_data_start_byte; + if (st == 0xFF || st == 0) { + return 7; + } + return st; +} + +static bool read_sht40_sample(uint8_t addr7, int16_t* temp_centi, uint16_t* rh_centi) { + initOrRestoreWireForOpenDisplay(); + Wire.beginTransmission(addr7); + Wire.write(0xFD); + uint8_t err = Wire.endTransmission(); + if (err != 0) { + return false; + } + delay(10); + int n = Wire.requestFrom((int)addr7, 6); + if (n != 6) { + return false; + } + uint8_t b[6]; + for (int i = 0; i < 6; i++) { + b[i] = Wire.read(); + } + if (sht40_crc8(b, 2) != b[2]) { + return false; + } + if (sht40_crc8(b + 3, 2) != b[5]) { + return false; + } + uint16_t rawT = (uint16_t)(((uint16_t)b[0] << 8) | b[1]); + uint16_t rawRh = (uint16_t)(((uint16_t)b[3] << 8) | b[4]); + float tc = -45.0f + 175.0f * ((float)rawT / 65535.0f); + float rh = -6.0f + 125.0f * ((float)rawRh / 65535.0f); + if (rh < 0.0f) { + rh = 0.0f; + } + if (rh > 100.0f) { + rh = 100.0f; + } + *temp_centi = (int16_t)(tc * 100.0f); + *rh_centi = (uint16_t)(rh * 100.0f); + return true; +} + +static void write_sht40_invalid(uint8_t start) { + dynamicreturndata[start] = 0xFF; + dynamicreturndata[start + 1] = 0xFF; + dynamicreturndata[start + 2] = 0xFF; + if ((uint16_t)start + 3u < 11u) { + dynamicreturndata[start + 3] = 0; + } +} + +static int round_centi_to_deci(int16_t c) { + if (c >= 0) { + return (int)((c + 5) / 10); + } + return (int)((c - 5) / 10); +} + +// MSD (3 bytes LE): v = rh_deci | (tu << 10); rh_deci 0..1000 = 0..100.0% RH (0.1% steps); +// tu = temp(0.1°C) + 400. Decode: t_deci = (v>>10 & 0x7FF) - 400; temp_centi = t_deci*10; rh_centi = (v&0x3FF)*10 +static void write_sht40_msd(uint8_t start, int16_t temp_centi, uint16_t rh_centi) { + int t_deci = round_centi_to_deci(temp_centi); + if (t_deci < -400) { + t_deci = -400; + } + if (t_deci > 1250) { + t_deci = 1250; + } + uint32_t tu = (uint32_t)(t_deci + 400); + uint32_t rh_d = ((uint32_t)rh_centi + 5u) / 10u; + if (rh_d > 1000u) { + rh_d = 1000u; + } + uint32_t v = (rh_d & 0x3FFu) | (tu << 10); + dynamicreturndata[start] = (uint8_t)(v & 0xFFu); + dynamicreturndata[start + 1] = (uint8_t)((v >> 8) & 0xFFu); + dynamicreturndata[start + 2] = (uint8_t)((v >> 16) & 0xFFu); + if ((uint16_t)start + 3u < 11u) { + dynamicreturndata[start + 3] = 0; + } +} + +void initSht40Sensors(void) { + for (uint8_t i = 0; i < globalConfig.sensor_count; i++) { + const SensorData* s = &globalConfig.sensors[i]; + if (s->sensor_type != SENSOR_TYPE_SHT40) { + continue; + } + uint8_t addr = sht40_addr_7bit(s); + initOrRestoreWireForOpenDisplay(); + Wire.beginTransmission(addr); + Wire.write(0x94); + (void)Wire.endTransmission(); + delay(2); + } +} + +void pollSht40SensorsForMsd(void) { + for (uint8_t i = 0; i < globalConfig.sensor_count; i++) { + const SensorData* s = &globalConfig.sensors[i]; + if (s->sensor_type != SENSOR_TYPE_SHT40) { + continue; + } + uint8_t start = sht40_msd_start(s); + if (start > 8) { + continue; + } + int16_t tc = 0; + uint16_t rhc = 0; + if (!read_sht40_sample(sht40_addr_7bit(s), &tc, &rhc)) { + write_sht40_invalid(start); + continue; + } + write_sht40_msd(start, tc, rhc); + } +} diff --git a/src/sensor_sht40.h b/src/sensor_sht40.h new file mode 100644 index 0000000..fa78d83 --- /dev/null +++ b/src/sensor_sht40.h @@ -0,0 +1,7 @@ +#ifndef SENSOR_SHT40_H +#define SENSOR_SHT40_H + +void initSht40Sensors(void); +void pollSht40SensorsForMsd(void); + +#endif diff --git a/src/structs.h b/src/structs.h index 50df720..45f165e 100644 --- a/src/structs.h +++ b/src/structs.h @@ -27,7 +27,9 @@ struct SystemConfig { uint8_t communication_modes; // Supported communication modes (bitfield) uint8_t device_flags; // Misc device flags (bitfield) uint8_t pwr_pin; // Power pin number (0xFF if not present) - uint8_t reserved[17]; // Reserved bytes for future use + uint8_t reserved[15]; // Reserved bytes for future use + uint8_t pwr_pin_2; // Optional 2nd power/enable (e.g. Seeed ED103 TFT_ENABLE); 0 or 0xFF = default (11) + uint8_t pwr_pin_3; // Optional 3rd power/enable (e.g. ITE_ENABLE); 0 or 0xFF = default (21) } __attribute__((packed)); // 0x02: manufacturer_data @@ -55,6 +57,28 @@ struct PowerOption { uint8_t reserved[10]; // Reserved bytes for future use } __attribute__((packed)); +// Panel IDs must match web/firmware/toolbox/config.yaml display.panel_ic_type enum values. +// Decimal 3000–3999 = Seeed_GFX / OpenDisplay runtime epaper (add new IDs here as panels ship). +#define PANEL_IC_SEEED_ED103TC2_1872X1404 3000u +#define PANEL_IC_SEEED_ED103TC2_1872X1404_4GRAY 3001u + +// display.color_scheme (config.yaml); use with matching panel (e.g. gray16 + panel_ic 3001). +#define COLOR_SCHEME_GRAY16 6u + +// display.transmission_modes (config.yaml bitfield). ZIPXL extends ZIP on builds with a larger buffer. +#if defined(TARGET_ESP32) && defined(TARGET_LARGE_MEMORY) && defined(BOARD_HAS_PSRAM) +#define MAX_COMPRESSED_BUFFER_BYTES (512u * 1024u) +#elif defined(TARGET_ESP32) && defined(TARGET_LARGE_MEMORY) +#define MAX_COMPRESSED_BUFFER_BYTES (256u * 1024u) +#else +#define MAX_COMPRESSED_BUFFER_BYTES (54u * 1024u) +#endif +#define TRANSMISSION_MODE_ZIPXL (1u << 0) +#define TRANSMISSION_MODE_ZIP (1u << 1) +#define TRANSMISSION_MODE_G5 (1u << 2) +#define TRANSMISSION_MODE_DIRECT_WRITE (1u << 3) +#define TRANSMISSION_MODE_CLEAR_ON_BOOT (1u << 7) + // 0x20: display (repeatable, max 4 instances) struct DisplayConfig { uint8_t instance_number; // Unique index for multiple display blocks (0-based) @@ -68,21 +92,22 @@ struct DisplayConfig { uint8_t rotation; // Physical rotation in degrees (enum) uint8_t reset_pin; // Pin number for panel reset (0xFF if none) uint8_t busy_pin; // Pin number to read panel busy status (0xFF if none) - uint8_t dc_pin; // Data/Command select pin (0xFF if none) + uint8_t dc_pin; // SPI MISO for Seeed ED103/IT8951 (OpenDisplay); else data/command if used uint8_t cs_pin; // SPI chip select pin (0xFF if none) uint8_t data_pin; // Data out pin (MOSI / data line) uint8_t partial_update_support; // Partial update capability (enum) uint8_t color_scheme; // Color scheme supported by the display uint8_t transmission_modes; // Supported image/data transmission modes (bitfield) - uint8_t clk_pin; // Reserved / spare pin 1 - uint8_t reserved_pin_2; // Reserved / spare pin 2 - uint8_t reserved_pin_3; // Reserved / spare pin 3 + uint8_t clk_pin; // SPI SCLK (Seeed ePaper) + uint8_t reserved_pin_2; // Spare GPIO (Seeed enables use system_config.pwr_pin_2/3) + uint8_t reserved_pin_3; // Spare GPIO uint8_t reserved_pin_4; // Reserved / spare pin 4 uint8_t reserved_pin_5; // Reserved / spare pin 5 uint8_t reserved_pin_6; // Reserved / spare pin 6 uint8_t reserved_pin_7; // Reserved / spare pin 7 uint8_t reserved_pin_8; // Reserved / spare pin 8 - uint8_t reserved[15]; // Reserved bytes for future use + uint16_t full_update_mC; // Energy for full refresh in millicoulombs (0 = unknown) + uint8_t reserved[13]; // Reserved bytes for future use } __attribute__((packed)); // 0x21: led (repeatable, max 4 instances) @@ -97,12 +122,32 @@ struct LedConfig { uint8_t reserved[15]; // Reserved bytes for future use } __attribute__((packed)); +// 0x29: passive_buzzer (repeatable, max 4 instances) +// Frequency in 0x0075 payload: 0 = silence/rest; 1–255 maps linearly to firmware-defined Hz range (not stored in config). +#define BUZZER_FLAG_ENABLE_ACTIVE_HIGH (1u << 0) + +struct PassiveBuzzerConfig { + uint8_t instance_number; + uint8_t drive_pin; // PWM / square wave to buzzer (+ transistor) + uint8_t enable_pin; // Optional enable (e.g. FET); 0xFF = unused + uint8_t flags; // BUZZER_FLAG_* + uint8_t duty_percent; // 1–100 PWM duty; 0 = default 50 + uint8_t reserved[27]; +} __attribute__((packed)); + // 0x23: sensor_data (repeatable, max 4 instances) +#define SENSOR_TYPE_TEMPERATURE 0x0001u +#define SENSOR_TYPE_HUMIDITY 0x0002u +#define SENSOR_TYPE_AXP2101 0x0003u +#define SENSOR_TYPE_SHT40 0x0004u + struct SensorData { uint8_t instance_number; // Unique index for multiple sensor blocks (0-based) - uint16_t sensor_type; // Sensor type enum + uint16_t sensor_type; // Sensor type enum (SENSOR_TYPE_*) uint8_t bus_id; // Instance id of the bus to use for this sensor - uint8_t reserved[26]; // Reserved bytes for future use + uint8_t i2c_addr_7bit; // I2C 7-bit address; 0 or 0xFF = default per sensor (SHT40: 0x44) + uint8_t msd_data_start_byte; // SHT40: first index in dynamicreturndata for 3-byte MSD block; 0 or 0xFF = default (7) + uint8_t reserved[24]; // Reserved for future use } __attribute__((packed)); // 0x24: data_bus (repeatable, max 4 instances) @@ -144,6 +189,28 @@ struct BinaryInputs { uint8_t reserved[14]; // Reserved bytes for future use } __attribute__((packed)); +// 0x28: touch_controller (repeatable, max 4 instances) +// touch_ic_type: 0 = disabled / none, 1 = GT911 +#define TOUCH_IC_NONE 0u +#define TOUCH_IC_GT911 1u +#define TOUCH_FLAG_INVERT_X (1u << 0) +#define TOUCH_FLAG_INVERT_Y (1u << 1) +#define TOUCH_FLAG_SWAP_XY (1u << 2) + +struct TouchController { + uint8_t instance_number; + uint16_t touch_ic_type; + uint8_t bus_id; // data_bus index, or 0xFF if I2C already up (e.g. after display init) + uint8_t i2c_addr_7bit; // GT911: 0x5D or 0x14; 0 or 0xFF = auto (try both after reset) + uint8_t int_pin; // GT911 INT, 0xFF = poll only + uint8_t rst_pin; // GT911 RST, 0xFF = skip hardware reset + uint8_t display_instance; // Clip/scale to displays[instance] pixel size + uint8_t flags; // TOUCH_FLAG_* + uint8_t poll_interval_ms; // 0 = default 25 ms + uint8_t touch_data_start_byte; // First of 5 bytes in MSD dynamicreturndata (0–6): byte0 low nibble = contacts 1–5 (down) or 6 (released, last xy kept); high nibble = track id + uint8_t reserved[21]; +} __attribute__((packed)); + // Global configuration structure struct GlobalConfig { // Required packets (single instances) @@ -166,13 +233,27 @@ struct GlobalConfig { struct BinaryInputs binary_inputs[4]; uint8_t binary_input_count; // Number of binary input instances loaded - + + struct TouchController touch_controllers[4]; + uint8_t touch_controller_count; + + struct PassiveBuzzerConfig passive_buzzers[4]; + uint8_t passive_buzzer_count; + // Config metadata uint8_t version; // Protocol version uint8_t minor_version; // Protocol minor version bool loaded; // True if config was successfully loaded }; +// 0x26 (decimal 38): wifi_config — matches web/firmware/toolbox/config.yaml packet_types +struct WifiConfig { + uint8_t ssid[32]; + uint8_t password[32]; + uint8_t encryption_type; + uint8_t reserved[95]; +} __attribute__((packed)); + // 0x27: security_config struct SecurityConfig { uint8_t encryption_enabled; // 0 = disabled, 1 = enabled diff --git a/src/touch_input.cpp b/src/touch_input.cpp new file mode 100644 index 0000000..421d699 --- /dev/null +++ b/src/touch_input.cpp @@ -0,0 +1,492 @@ +#include "touch_input.h" +#include "display_service.h" +#include "structs.h" +#include +#include +#include + +#if defined(TARGET_ESP32) +#define TOUCH_ISR_ATTR IRAM_ATTR +#else +#define TOUCH_ISR_ATTR +#endif + +extern struct GlobalConfig globalConfig; +extern uint8_t dynamicreturndata[11]; +void updatemsdata(void); +void writeSerial(String message, bool newLine = true); + +static_assert(sizeof(TouchController) == 32, "TouchController must be 32 bytes for packet 0x28"); + +#define GT911_REG_PID 0x8140u +#define GT911_REG_STATUS 0x814Eu +/* First contact: track id @0x814F, X @0x8150 — same as GT911-main readTouchPoints (COORD_ADDR+1) */ +#define GT911_REG_POINT1 0x814Fu +#define GT911_POST_RESET_SETTLE_MS 200 +#define GT911_PRE_RESET_DELAY_MS 300 +/* GT911 0x814E: bit7 = buffer has coordinates ready (see GT911-main readTouches / Goodix docs) */ +#define GT911_STATUS_BUFFER_READY 0x80u +#define GT911_MAX_CONTACTS 5u +/* MSD byte0 low nibble: GT911 contact count 1–5 while touching; 6 = released (last x,y in bytes 1–4; high nibble = last track id). 0 = never touched (5-byte block cleared). */ + +#ifndef TOUCH_DEBUG +#define TOUCH_DEBUG 1 +#endif + +struct TouchRuntime { + uint8_t addr7; + uint8_t ok; + uint8_t reg_high_first; // 0: 16-bit reg addr low byte first (common); 1: high byte first (some GT911 / docs) + uint8_t int_irq_attached; // 1: GPIO FALLING ISR active; 0: no INT or attachInterrupt failed + uint16_t last_x; + uint16_t last_y; + uint8_t last_count; + uint8_t last_id; + uint32_t last_poll_ms; + uint32_t last_i2c_warn_ms; + uint8_t touch_latched; +}; + +static TouchRuntime s_touch_rt[4]; + +static volatile uint8_t s_touch_irq_mask = 0; + +static void TOUCH_ISR_ATTR touch_isr_0(void) { + s_touch_irq_mask |= 1u << 0; +} +static void TOUCH_ISR_ATTR touch_isr_1(void) { + s_touch_irq_mask |= 1u << 1; +} +static void TOUCH_ISR_ATTR touch_isr_2(void) { + s_touch_irq_mask |= 1u << 2; +} +static void TOUCH_ISR_ATTR touch_isr_3(void) { + s_touch_irq_mask |= 1u << 3; +} + +static void (*const s_touch_isrs[4])(void) = {touch_isr_0, touch_isr_1, touch_isr_2, touch_isr_3}; + +static void gt911_int_wake_before_irq(const TouchController* t) { + if (t->int_pin == 0xFF) { + return; + } + pinMode(t->int_pin, OUTPUT); + digitalWrite(t->int_pin, HIGH); + delay(10); + pinMode(t->int_pin, INPUT_PULLUP); +} + +static void attach_touch_int(uint8_t idx, uint8_t pin) { + if (idx >= 4 || pin == 0xFF) { + return; + } + int irq_num = digitalPinToInterrupt(pin); + if (irq_num < 0) { + writeSerial("Touch[" + String(idx) + "]: digitalPinToInterrupt failed for GPIO " + String(pin) + " — using poll only", true); + return; + } + pinMode(pin, INPUT_PULLUP); + attachInterrupt(irq_num, s_touch_isrs[idx], FALLING); + s_touch_rt[idx].int_irq_attached = 1; +} + +static const uint32_t TOUCH_I2C_WARN_INTERVAL_MS = 3000; + +static bool gt911_write_reg(uint8_t addr7, uint16_t reg, const uint8_t* buf, uint8_t len, bool reg_high_first) { + Wire.beginTransmission(addr7); + if (reg_high_first) { + Wire.write((uint8_t)(reg >> 8)); + Wire.write((uint8_t)(reg & 0xFFu)); + } else { + Wire.write((uint8_t)(reg & 0xFFu)); + Wire.write((uint8_t)(reg >> 8)); + } + for (uint8_t i = 0; i < len; i++) { + Wire.write(buf[i]); + } + return Wire.endTransmission() == 0; +} + +static bool gt911_read_reg(uint8_t addr7, uint16_t reg, uint8_t* buf, uint8_t len, bool reg_high_first) { + Wire.beginTransmission(addr7); + if (reg_high_first) { + Wire.write((uint8_t)(reg >> 8)); + Wire.write((uint8_t)(reg & 0xFFu)); + } else { + Wire.write((uint8_t)(reg & 0xFFu)); + Wire.write((uint8_t)(reg >> 8)); + } + if (Wire.endTransmission(false) != 0) { + return false; + } + size_t n = Wire.requestFrom((int)addr7, (int)len); + if (n != (size_t)len) { + return false; + } + for (uint8_t i = 0; i < len; i++) { + buf[i] = (uint8_t)Wire.read(); + } + return true; +} + +static bool gt911_product_id_match(const uint8_t* id) { + return id[0] == '9' && id[1] == '1' && id[2] == '1'; +} + +static bool gt911_probe_product(uint8_t addr7, uint8_t* reg_high_first) { + uint8_t id[4]; + if (gt911_read_reg(addr7, GT911_REG_PID, id, 4, false) && gt911_product_id_match(id)) { + *reg_high_first = 0; +#if TOUCH_DEBUG + writeSerial("GT911: PID OK @0x" + String(addr7, HEX) + " LE", true); +#endif + return true; + } + if (gt911_read_reg(addr7, GT911_REG_PID, id, 4, true) && gt911_product_id_match(id)) { + *reg_high_first = 1; +#if TOUCH_DEBUG + writeSerial("GT911: PID OK @0x" + String(addr7, HEX) + " BE", true); +#endif + return true; + } + writeSerial("GT911: PID probe failed at 0x" + String(addr7, HEX), true); + return false; +} + +/** Matches GT911-main `reset()` timing/order; 0x14 => INT high before RST release, 0x5D => INT low. + * Library uses `pinMode(RST, INPUT)` to release; without an external RST pull-up we drive RST HIGH instead. */ +static void gt911_hw_reset(const TouchController* t, bool int_low_for_addr_5d) { + if (t->rst_pin == 0xFF) { + return; + } + if (t->int_pin == 0xFF) { + pinMode(t->rst_pin, OUTPUT); + digitalWrite(t->rst_pin, LOW); + delay(10); + digitalWrite(t->rst_pin, HIGH); + delay(60); + pinMode(t->rst_pin, OUTPUT); + digitalWrite(t->rst_pin, HIGH); + return; + } + delay(1); + pinMode(t->int_pin, OUTPUT); + pinMode(t->rst_pin, OUTPUT); + digitalWrite(t->int_pin, LOW); + digitalWrite(t->rst_pin, LOW); + delay(11); + digitalWrite(t->int_pin, int_low_for_addr_5d ? LOW : HIGH); + delayMicroseconds(110); + pinMode(t->rst_pin, OUTPUT); + digitalWrite(t->rst_pin, HIGH); + delay(6); + digitalWrite(t->int_pin, LOW); + delay(51); + pinMode(t->rst_pin, OUTPUT); + digitalWrite(t->rst_pin, HIGH); + pinMode(t->int_pin, INPUT_PULLUP); +} + +static bool touch_bus_ok(const TouchController* t) { + if (globalConfig.data_bus_count == 0) { + return true; + } + uint8_t bid = t->bus_id; + if (bid == 0xFF) { + bid = 0; + } + if (bid >= globalConfig.data_bus_count) { + return false; + } + return globalConfig.data_buses[bid].bus_type == 0x01 && bid == 0; +} + +static uint8_t gt911_resolve_and_init(const TouchController* t, TouchRuntime* rt) { + const uint8_t a5d = 0x5D; + const uint8_t a14 = 0x14; + uint8_t want = t->i2c_addr_7bit; + + if (want != 0 && want != 0xFF) { + if (t->rst_pin != 0xFF) { + delay(GT911_PRE_RESET_DELAY_MS); + gt911_hw_reset(t, want == a5d); + delay(GT911_POST_RESET_SETTLE_MS); + } else { + delay(10); + } + if (gt911_probe_product(want, &rt->reg_high_first)) { + return want; + } + writeSerial("GT911: probe failed at configured addr 0x" + String(want, HEX), true); + return 0; + } + + if (t->rst_pin != 0xFF) { + delay(GT911_PRE_RESET_DELAY_MS); + gt911_hw_reset(t, true); + delay(GT911_POST_RESET_SETTLE_MS); + if (gt911_probe_product(a5d, &rt->reg_high_first)) { + return a5d; + } + delay(GT911_PRE_RESET_DELAY_MS); + gt911_hw_reset(t, false); + delay(GT911_POST_RESET_SETTLE_MS); + if (gt911_probe_product(a14, &rt->reg_high_first)) { + return a14; + } + } else { + if (gt911_probe_product(a5d, &rt->reg_high_first)) { + return a5d; + } + if (gt911_probe_product(a14, &rt->reg_high_first)) { + return a14; + } + } + writeSerial("GT911: not found on I2C (check wiring, pull-ups, RST/INT)", true); + return 0; +} + +static void gt911_clear_status(uint8_t addr7, bool reg_high_first) { + uint8_t z = 0; + gt911_write_reg(addr7, GT911_REG_STATUS, &z, 1, reg_high_first); +} + +static void apply_touch_map(const TouchController* t, uint16_t* x, uint16_t* y) { + if (t->flags & TOUCH_FLAG_SWAP_XY) { + uint16_t tmp = *x; + *x = *y; + *y = tmp; + } + uint16_t w = 0; + uint16_t h = 0; + if (t->display_instance < globalConfig.display_count) { + w = globalConfig.displays[t->display_instance].pixel_width; + h = globalConfig.displays[t->display_instance].pixel_height; + } + if (t->flags & TOUCH_FLAG_INVERT_X && w > 0) { + *x = (w > *x) ? (w - 1u - *x) : 0; + } + if (t->flags & TOUCH_FLAG_INVERT_Y && h > 0) { + *y = (h > *y) ? (h - 1u - *y) : 0; + } + if (w > 0 && *x >= w) { + *x = w - 1u; + } + if (h > 0 && *y >= h) { + *y = h - 1u; + } +} + +void initTouchInput(void) { + memset(s_touch_rt, 0, sizeof(s_touch_rt)); + s_touch_irq_mask = 0; + if (globalConfig.touch_controller_count == 0) { + return; + } + uint8_t enabled = 0; + for (uint8_t j = 0; j < globalConfig.touch_controller_count; j++) { + if (globalConfig.touch_controllers[j].touch_ic_type != TOUCH_IC_NONE) { + enabled++; + } + } + if (enabled == 0) { + return; + } +#if TOUCH_DEBUG + writeSerial("Touch: init " + String(enabled) + " enabled / " + String(globalConfig.touch_controller_count) + " packet(s)", true); +#endif + initOrRestoreWireForOpenDisplay(); + for (uint8_t i = 0; i < globalConfig.touch_controller_count; i++) { + TouchController* tc = &globalConfig.touch_controllers[i]; + TouchRuntime* rt = &s_touch_rt[i]; + if (tc->touch_ic_type == TOUCH_IC_NONE) { + continue; + } + if (tc->touch_ic_type != TOUCH_IC_GT911) { + if (tc->touch_ic_type != TOUCH_IC_NONE) { + writeSerial("Touch[" + String(i) + "]: skipped (only GT911=1 implemented, got " + String(tc->touch_ic_type) + ")", true); + } + continue; + } + if (!touch_bus_ok(tc)) { + writeSerial("Touch[" + String(i) + "]: bus rejected — need I2C data_bus instance 0 (or bus_id 0xFF mapping to 0); data_bus_count=" + + String(globalConfig.data_bus_count), true); + continue; + } + if (tc->touch_data_start_byte > 6u) { + writeSerial("Touch[" + String(i) + "]: touch_data_start_byte must be 0–6 (5-byte window)", true); + continue; + } + uint8_t addr = gt911_resolve_and_init(tc, rt); + if (addr == 0) { + writeSerial("Touch[" + String(i) + "]: init failed", true); + continue; + } + rt->addr7 = addr; + rt->ok = 1; + gt911_clear_status(addr, rt->reg_high_first != 0); + { + const bool rh = rt->reg_high_first != 0; + uint16_t xres = 0; + uint16_t yres = 0; + uint8_t inf[12]; + if (gt911_read_reg(addr, GT911_REG_PID, inf, sizeof(inf), rh)) { + xres = (uint16_t)inf[6] | ((uint16_t)inf[7] << 8); + yres = (uint16_t)inf[8] | ((uint16_t)inf[9] << 8); + } + writeSerial("Touch[" + String(i) + "]: GT911 @0x" + String(addr, HEX) + " " + String(rh ? "BE" : "LE") + + " " + String(xres) + "x" + String(yres) + (tc->int_pin != 0xFF ? " INT+poll" : " poll"), true); + } + if (tc->int_pin != 0xFF) { + gt911_int_wake_before_irq(tc); +#if TOUCH_DEBUG + writeSerial("Touch[" + String(i) + "]: INT GPIO " + String(tc->int_pin) + " FALLING", true); +#endif + attach_touch_int(i, tc->int_pin); + if (!rt->int_irq_attached) { + writeSerial("Touch[" + String(i) + "]: INT attach failed — polling only", true); + } + } + rt->last_poll_ms = millis(); + } +} + +bool touch_input_gpio_is_touch_int(uint8_t pin) { + if (pin == 0xFF) { + return false; + } + for (uint8_t i = 0; i < globalConfig.touch_controller_count; i++) { + const TouchController* tc = &globalConfig.touch_controllers[i]; + if (tc->touch_ic_type == TOUCH_IC_GT911 && tc->int_pin == pin) { + return true; + } + } + return false; +} + +void processTouchInput(void) { + uint32_t now = millis(); + for (uint8_t i = 0; i < globalConfig.touch_controller_count; i++) { + TouchController* tc = &globalConfig.touch_controllers[i]; + TouchRuntime* rt = &s_touch_rt[i]; + if (tc->touch_ic_type != TOUCH_IC_GT911 || !rt->ok) { + continue; + } + + uint8_t interval = tc->poll_interval_ms ? tc->poll_interval_ms : 25; + const bool irq_mode = (rt->int_irq_attached != 0); + bool from_irq = false; + bool line_low = false; + bool want_read = false; + bool timed_poll = false; + if (irq_mode) { + bool edge = (s_touch_irq_mask & (1u << i)) != 0; + line_low = (digitalRead(tc->int_pin) == LOW); + timed_poll = (uint32_t)(now - rt->last_poll_ms) >= interval; + want_read = edge || timed_poll || line_low; + if (!want_read) { + continue; + } + if (edge) { + from_irq = true; + s_touch_irq_mask &= (uint8_t)~(1u << i); + } + } else { + timed_poll = (uint32_t)(now - rt->last_poll_ms) >= interval; + want_read = timed_poll; + if (!want_read) { + continue; + } + } + + const bool rh = rt->reg_high_first != 0; + uint8_t st = 0; + if (!gt911_read_reg(rt->addr7, GT911_REG_STATUS, &st, 1, rh)) { + if ((uint32_t)(now - rt->last_i2c_warn_ms) >= TOUCH_I2C_WARN_INTERVAL_MS) { + rt->last_i2c_warn_ms = now; + writeSerial("Touch[" + String(i) + "]: I2C read fail (status reg 0x814E, addr 0x" + String(rt->addr7, HEX) + ")", true); + } + continue; + } + + if ((st & GT911_STATUS_BUFFER_READY) == 0) { + if (!from_irq && !line_low) { + rt->last_poll_ms = now; + } + continue; + } + uint8_t n = st & 0x0Fu; + if (n > GT911_MAX_CONTACTS) { + if (!from_irq && !line_low) { + rt->last_poll_ms = now; + } + continue; + } + rt->last_poll_ms = now; + + uint16_t x = 0; + uint16_t y = 0; + uint8_t tid = 0; + if (n > 0) { + uint8_t p[8]; + if (!gt911_read_reg(rt->addr7, GT911_REG_POINT1, p, 8, rh)) { + if ((uint32_t)(now - rt->last_i2c_warn_ms) >= TOUCH_I2C_WARN_INTERVAL_MS) { + rt->last_i2c_warn_ms = now; + writeSerial("Touch[" + String(i) + "]: I2C read fail (point1 @0x814F, addr 0x" + String(rt->addr7, HEX) + ")", true); + } + continue; + } + tid = p[0]; + x = (uint16_t)p[1] | ((uint16_t)p[2] << 8); + y = (uint16_t)p[3] | ((uint16_t)p[4] << 8); + rt->touch_latched = 1; + } else { + x = rt->last_x; + y = rt->last_y; + tid = rt->last_id; + } + gt911_clear_status(rt->addr7, rh); + + if (n > 0) { + apply_touch_map(tc, &x, &y); + } + + bool changed = (n != rt->last_count) || (n > 0 && ((x != rt->last_x) || (y != rt->last_y) || (tid != rt->last_id))); + rt->last_count = n; + rt->last_x = x; + rt->last_y = y; + rt->last_id = tid; + + uint8_t s = tc->touch_data_start_byte; + if (s + 5 > 11) { + continue; + } + if (n == 0 && !rt->touch_latched) { + memset(&dynamicreturndata[s], 0, 5); + } else if (n == 0) { + dynamicreturndata[s] = (uint8_t)(6u | ((tid & 0x0Fu) << 4)); + dynamicreturndata[s + 1] = (uint8_t)(x & 0xFFu); + dynamicreturndata[s + 2] = (uint8_t)(x >> 8); + dynamicreturndata[s + 3] = (uint8_t)(y & 0xFFu); + dynamicreturndata[s + 4] = (uint8_t)(y >> 8); + } else { + dynamicreturndata[s] = (uint8_t)((n & 0x0Fu) | ((tid & 0x0Fu) << 4)); + dynamicreturndata[s + 1] = (uint8_t)(x & 0xFFu); + dynamicreturndata[s + 2] = (uint8_t)(x >> 8); + dynamicreturndata[s + 3] = (uint8_t)(y & 0xFFu); + dynamicreturndata[s + 4] = (uint8_t)(y >> 8); + } + if (changed) { +#if TOUCH_DEBUG + if (n == 0) { + writeSerial("Touch[" + String(i) + "]: release (MSD low nibble 6, last xy kept)", true); + } else { + const char* src = irq_mode ? (from_irq ? "irq" : (line_low ? "line" : "poll")) : "poll"; + writeSerial("Touch[" + String(i) + "]: n=" + String(n) + " id=" + String(tid) + " (" + String(x) + "," + String(y) + ") st=0x" + + String(st, HEX) + " " + String(src), true); + } +#endif + updatemsdata(); + } + } +} diff --git a/src/touch_input.h b/src/touch_input.h new file mode 100644 index 0000000..d4f5729 --- /dev/null +++ b/src/touch_input.h @@ -0,0 +1,10 @@ +#ifndef TOUCH_INPUT_H +#define TOUCH_INPUT_H + +#include + +void initTouchInput(void); +void processTouchInput(void); +bool touch_input_gpio_is_touch_int(uint8_t pin); + +#endif diff --git a/src/wifi_service.cpp b/src/wifi_service.cpp index a6427fd..e2c718f 100644 --- a/src/wifi_service.cpp +++ b/src/wifi_service.cpp @@ -2,18 +2,21 @@ #include "wifi_service.h" #include "communication.h" -#include "display_service.h" +#include "encryption.h" #include "structs.h" #include #include #include -#include #include #ifndef COMM_MODE_WIFI #define COMM_MODE_WIFI (1 << 2) #endif +#ifndef WIFI_LAN_MAX_PAYLOAD +#define WIFI_LAN_MAX_PAYLOAD 4096U +#endif + extern struct GlobalConfig globalConfig; extern char wifiSsid[33]; extern char wifiPassword[33]; @@ -21,15 +24,13 @@ extern uint8_t wifiEncryptionType; extern bool wifiConfigured; extern bool wifiConnected; extern bool wifiInitialized; +extern uint16_t wifiServerPort; +extern WiFiServer wifiServer; extern WiFiClient wifiClient; extern bool wifiServerConnected; -extern uint32_t wifiServerLastConnectAttempt; extern uint8_t tcpReceiveBuffer[8192]; extern uint32_t tcpReceiveBufferPos; -extern uint32_t wifiNextImageRequestTime; -extern uint32_t wifiPollInterval; -extern bool wifiImageRequestPending; -extern bool woke_from_deep_sleep; +extern uint8_t msd_payload[16]; void writeSerial(String message, bool newLine = true); String getChipIdHex(); @@ -38,7 +39,50 @@ typedef void* BLEConnHandle; typedef void* BLECharPtr; void imageDataWritten(BLEConnHandle conn_hdl, BLECharPtr chr, uint8_t* data, uint16_t len); -void initWiFi() { +static void hex14_lower(const uint8_t* src, char* out29) { + static const char* h = "0123456789abcdef"; + for (int i = 0; i < 14; i++) { + out29[i * 2] = h[(src[i] >> 4) & 0x0F]; + out29[i * 2 + 1] = h[src[i] & 0x0F]; + } + out29[28] = '\0'; +} + +void opendisplay_mdns_update_msd_txt(void) { + if (!wifiConnected || WiFi.status() != WL_CONNECTED) { + return; + } + static uint8_t last_msd[14]; + static uint32_t last_ms = 0; + static bool have_last = false; + uint8_t cur[14]; + memcpy(cur, &msd_payload[2], sizeof(cur)); + uint32_t now = millis(); + if (have_last && memcmp(cur, last_msd, sizeof(cur)) == 0 && (now - last_ms) < 400) { + return; + } + have_last = true; + memcpy(last_msd, cur, sizeof(cur)); + last_ms = now; + char hex[29]; + hex14_lower(cur, hex); + // const char* overload (void); char* overload (bool) — avoid ambiguous resolution with char hex[]. + MDNS.addServiceTxt("opendisplay", "tcp", "msd", static_cast(hex)); +} + +static void restartLanService(void) { + String deviceName = "OD" + getChipIdHex(); + if (!MDNS.begin(deviceName.c_str())) { + writeSerial("ERROR: mDNS responder failed"); + return; + } + writeSerial("mDNS: " + deviceName + ".local"); + MDNS.addService("opendisplay", "tcp", wifiServerPort); + writeSerial("mDNS: advertised _opendisplay._tcp port " + String(wifiServerPort)); + opendisplay_mdns_update_msd_txt(); +} + +void initWiFi(bool waitForConnection) { writeSerial("=== Initializing WiFi ==="); if (!(globalConfig.system_config.communication_modes & COMM_MODE_WIFI)) { @@ -46,13 +90,19 @@ void initWiFi() { wifiInitialized = false; return; } - if (!wifiConfigured || wifiSsid[0] == '\0' || strlen(wifiSsid) == 0) { - writeSerial("WiFi configuration not available or SSID empty, skipping"); + if (!wifiConfigured) { + writeSerial("WiFi: system_config has WiFi mode on, but wifi_config TLV (0x26) is not in saved " + "configuration (or failed to parse). Enable Wi-Fi in config, set SSID, and write full " + "config to the device."); + wifiInitialized = false; + return; + } + if (wifiSsid[0] == '\0' || strlen(wifiSsid) == 0) { + writeSerial("WiFi: wifi_config packet present but SSID field is empty."); wifiInitialized = false; return; } writeSerial("SSID: \"" + String(wifiSsid) + "\""); - String deviceName = "OD" + getChipIdHex(); WiFi.setAutoReconnect(true); WiFi.setTxPower(WIFI_POWER_15dBm); wifiSsid[32] = '\0'; @@ -62,6 +112,10 @@ void initWiFi() { wifiInitialized = true; WiFi.begin(wifiSsid, wifiPassword); WiFi.setTxPower(WIFI_POWER_15dBm); + if (!waitForConnection) { + writeSerial("WiFi: STA started (non-blocking; LAN starts when associated)"); + return; + } writeSerial("Waiting for WiFi connection..."); const int maxRetries = 3; const unsigned long timeoutPerRetry = 10000; @@ -82,284 +136,124 @@ void initWiFi() { if (WiFi.status() == WL_CONNECTED) { connected = true; break; - } else { - if (!abortCurrentRetry) { - writeSerial("Connection attempt " + String(retry + 1) + " timed out"); - } - if (retry < maxRetries - 1) { - delay(2000); - } + } + if (!abortCurrentRetry) { + writeSerial("WiFi attempt " + String(retry + 1) + " timed out"); + } + if (retry < maxRetries - 1) { + delay(2000); } } if (WiFi.status() == WL_CONNECTED) { wifiConnected = true; - writeSerial("=== WiFi Connected Successfully ==="); - writeSerial("SSID: " + String(wifiSsid)); - writeSerial("IP Address: " + WiFi.localIP().toString()); - writeSerial("RSSI: " + String(WiFi.RSSI()) + " dBm"); - writeSerial("=== Initializing mDNS ==="); - if (MDNS.begin(deviceName.c_str())) { - writeSerial("mDNS responder started: " + deviceName + ".local"); - } else { - writeSerial("ERROR: Failed to start mDNS responder"); - } - - discoverAndConnectWiFiServer(); - - if (woke_from_deep_sleep) { - wifiNextImageRequestTime = 0; - wifiPollInterval = 60; - writeSerial("Deep sleep wake detected - will send Image Request immediately when connected"); - } + writeSerial("=== WiFi connected ==="); + writeSerial("IP: " + WiFi.localIP().toString()); + wifiServer.begin(wifiServerPort); + writeSerial("TCP server listening on port " + String(wifiServerPort)); + restartLanService(); } else { wifiConnected = false; - writeSerial("=== WiFi Connection Failed ==="); - writeSerial("Final Status: " + String(WiFi.status())); - } -} - -void discoverAndConnectWiFiServer() { - if (!wifiConnected || WiFi.status() != WL_CONNECTED) { - writeSerial("WiFi not connected, cannot discover server"); - return; - } - if (wifiClient.connected()) { - writeSerial("Already connected to server"); - return; - } - writeSerial("=== Discovering OpenDisplay Server via mDNS ==="); - int n = MDNS.queryService("opendisplay", "tcp"); - - if (n == 0) { - writeSerial("No OpenDisplay server found via mDNS"); - wifiServerLastConnectAttempt = millis(); - return; - } - writeSerial("Found " + String(n) + " OpenDisplay server(s)"); - String serverName; - int serverPort = 0; - bool hostnameValid = false; - for (int retry = 0; retry < 5 && !hostnameValid; retry++) { - delay(200 * (retry + 1)); - String tempHostname = MDNS.hostname(0); - serverPort = MDNS.port(0); - if (tempHostname.length() > 0) { - const char* hostnameCStr = tempHostname.c_str(); - if (hostnameCStr != nullptr && hostnameCStr[0] != '\0') { - serverName = String(hostnameCStr); - hostnameValid = true; - } - } - - if (!hostnameValid && retry < 4) { - writeSerial("Hostname not available yet, retrying... (" + String(retry + 1) + "/5)"); - } - } - if (serverPort == 0) { - serverPort = 2446; - writeSerial("Port not found in mDNS, using default: " + String(serverPort)); - } - IPAddress serverIP; - bool gotIPFromTxt = false; - if (MDNS.hasTxt(0, "ip")) { - String ipFromTxt = MDNS.txt(0, "ip"); - if (ipFromTxt.length() > 0) { - if (serverIP.fromString(ipFromTxt)) { - gotIPFromTxt = true; - writeSerial("Got IP from mDNS TXT record: " + serverIP.toString()); - } - } - } - if (!gotIPFromTxt && hostnameValid && serverName.length() > 0) { - writeSerial("Server discovered:"); - writeSerial(" Name: "); - writeSerial(serverName); - writeSerial(" Port: " + String(serverPort)); - const char* serverNameCStr = serverName.c_str(); - if (serverNameCStr != nullptr && WiFi.hostByName(serverNameCStr, serverIP)) { - gotIPFromTxt = true; - writeSerial(" IP from hostname resolution: " + serverIP.toString()); - } else { - String hostnameWithLocal = serverName + ".local"; - const char* hostnameLocalCStr = hostnameWithLocal.c_str(); - if (hostnameLocalCStr != nullptr && WiFi.hostByName(hostnameLocalCStr, serverIP)) { - gotIPFromTxt = true; - writeSerial(" IP from hostname resolution (.local): " + serverIP.toString()); - } - } - } - if (!gotIPFromTxt) { - writeSerial("ERROR: Could not get IP address from mDNS (TXT, hostname, or direct IP)"); - wifiServerLastConnectAttempt = millis(); - return; - } - writeSerial("Server discovered:"); - writeSerial(" Port: " + String(serverPort)); - writeSerial(" IP: " + serverIP.toString()); - writeSerial("=== Connecting to TCP Server ==="); - writeSerial("Server: " + serverIP.toString() + ":" + String(serverPort)); - wifiClient.setTimeout(10000); - bool connected = wifiClient.connect(serverIP, serverPort); - if (connected) { - wifiServerConnected = true; - wifiServerLastConnectAttempt = millis(); - writeSerial("=== TCP Server Connected Successfully ==="); - writeSerial("Remote IP: " + wifiClient.remoteIP().toString()); - writeSerial("Remote Port: " + String(wifiClient.remotePort())); - sendConnectionNotification(0x01); - delay(100); - wifiNextImageRequestTime = 0; - wifiPollInterval = 60; - sendImageRequest(); - } else { - wifiServerConnected = false; - wifiServerLastConnectAttempt = millis(); - writeSerial("=== TCP Server Connection Failed ==="); - writeSerial("Error: " + String(wifiClient.getWriteError())); - writeSerial("Will retry in " + String(WIFI_SERVER_RECONNECT_DELAY_MS / 1000) + " seconds"); + writeSerial("=== WiFi connection failed ==="); } } void disconnectWiFiServer() { if (wifiClient.connected()) { - writeSerial("=== Disconnecting from TCP Server ==="); - sendConnectionNotification(0x00); - delay(100); + writeSerial("Closing LAN client"); + clearEncryptionSession(); wifiClient.stop(); - wifiServerConnected = false; - writeSerial("TCP connection closed"); } + wifiServerConnected = false; + tcpReceiveBufferPos = 0; } void handleWiFiServer() { + if (wifiInitialized && WiFi.status() == WL_CONNECTED && !wifiConnected) { + wifiConnected = true; + writeSerial("=== WiFi connected ==="); + writeSerial("IP: " + WiFi.localIP().toString()); + wifiServer.begin(wifiServerPort); + writeSerial("TCP server listening on port " + String(wifiServerPort)); + restartLanService(); + } if (!wifiConnected || WiFi.status() != WL_CONNECTED) { - if (wifiServerConnected) { - writeSerial("WiFi disconnected, closing TCP connection"); + if (wifiServerConnected || wifiClient.connected()) { + writeSerial("WiFi lost, closing LAN session"); disconnectWiFiServer(); } return; } - if (!wifiServerConnected) { - uint32_t now = millis(); - if (now - wifiServerLastConnectAttempt >= WIFI_SERVER_RECONNECT_DELAY_MS) { - discoverAndConnectWiFiServer(); + + WiFiClient incoming = wifiServer.accept(); + if (incoming) { + if (wifiClient.connected()) { + writeSerial("LAN: new client, replacing previous"); + clearEncryptionSession(); + wifiClient.stop(); } - return; + wifiClient = incoming; + wifiClient.setTimeout(30000); + tcpReceiveBufferPos = 0; + wifiServerConnected = true; + writeSerial("LAN client connected from " + wifiClient.remoteIP().toString()); } - if (!wifiClient.connected()) { + + if (!wifiServerConnected || !wifiClient.connected()) { if (wifiServerConnected) { - writeSerial("TCP connection lost"); + writeSerial("LAN client disconnected"); + clearEncryptionSession(); wifiServerConnected = false; - wifiServerLastConnectAttempt = millis(); + tcpReceiveBufferPos = 0; } return; } + int available = wifiClient.available(); - if (available > 0) { - int bytesToRead = available; - if (tcpReceiveBufferPos + bytesToRead > sizeof(tcpReceiveBuffer)) { - bytesToRead = sizeof(tcpReceiveBuffer) - tcpReceiveBufferPos; - writeSerial("WARNING: Receive buffer full, truncating data"); - } - int bytesRead = wifiClient.read(&tcpReceiveBuffer[tcpReceiveBufferPos], bytesToRead); - if (bytesRead > 0) { - tcpReceiveBufferPos += bytesRead; - uint32_t parsePos = 0; - while (parsePos + 5 <= tcpReceiveBufferPos) { - uint16_t packetLength = tcpReceiveBuffer[parsePos] | (tcpReceiveBuffer[parsePos + 1] << 8); - if (packetLength < 5 || packetLength > sizeof(tcpReceiveBuffer)) { - writeSerial("ERROR: Invalid packet length: " + String(packetLength)); - parsePos++; - continue; - } - if (parsePos + packetLength > tcpReceiveBufferPos) { - break; - } - writeSerial("Received TCP packet: " + String(packetLength) + " bytes"); - uint32_t packetOffset = parsePos + 2; - if (packetLength < 5) { - writeSerial("ERROR: Packet too short"); - parsePos++; - continue; - } - uint8_t version = tcpReceiveBuffer[packetOffset++]; - if (version != 0x01) { - writeSerial("ERROR: Unsupported protocol version: " + String(version)); - parsePos += packetLength; - continue; - } - uint32_t dataEnd = parsePos + packetLength - 2; - uint32_t currentOffset = packetOffset; - while (currentOffset + 2 <= dataEnd) { - (void)tcpReceiveBuffer[currentOffset++]; - uint8_t packetId = tcpReceiveBuffer[currentOffset++]; - uint16_t payloadLen = dataEnd - currentOffset; - uint8_t* payload = &tcpReceiveBuffer[currentOffset]; - if (packetId == 0x81) { - if (payloadLen >= 4) { - uint32_t pollInterval = payload[0] | (payload[1] << 8) | (payload[2] << 16) | (payload[3] << 24); - writeSerial("Server response: No new image, poll again in " + String(pollInterval) + " seconds"); - wifiPollInterval = pollInterval; - wifiNextImageRequestTime = millis() + (pollInterval * 1000); - wifiImageRequestPending = false; - writeSerial("Next Image Request scheduled in " + String(pollInterval) + " seconds"); - } else { - writeSerial("ERROR: Packet 0x81 payload too short: " + String(payloadLen)); - } - } else if (packetId == 0x82) { - if (payloadLen >= 7) { - uint16_t imageLength = payload[0] | (payload[1] << 8); - uint32_t pollInterval = payload[2] | (payload[3] << 8) | (payload[4] << 16) | (payload[5] << 24); - uint8_t refreshType = payload[6]; + if (available <= 0) { + return; + } + int bytesToRead = available; + if (tcpReceiveBufferPos + bytesToRead > (int)sizeof(tcpReceiveBuffer)) { + bytesToRead = (int)sizeof(tcpReceiveBuffer) - (int)tcpReceiveBufferPos; + } + if (bytesToRead <= 0) { + writeSerial("LAN RX buffer full, dropping connection"); + disconnectWiFiServer(); + return; + } + int bytesRead = wifiClient.read(&tcpReceiveBuffer[tcpReceiveBufferPos], bytesToRead); + if (bytesRead > 0) { + tcpReceiveBufferPos += (uint32_t)bytesRead; + } - writeSerial("Server response: New image (" + String(imageLength) + " bytes), poll again in " + String(pollInterval) + " seconds, refresh_type=" + String(refreshType)); - if (payloadLen >= 7 + imageLength) { - uint8_t* imageData = payload + 7; - uint8_t emptyData[4] = {0, 0, 0, 0}; - handleDirectWriteStart(emptyData, 0); - uint16_t remaining = imageLength; - uint16_t offset = 0; - while (remaining > 0) { - uint16_t chunkSize = (remaining > 512) ? 512 : remaining; - handleDirectWriteData(imageData + offset, chunkSize); - offset += chunkSize; - remaining -= chunkSize; - } - uint8_t refreshData = refreshType; - handleDirectWriteEnd(&refreshData, 1); - wifiPollInterval = pollInterval; - wifiNextImageRequestTime = millis() + (pollInterval * 1000); - wifiImageRequestPending = false; - writeSerial("Next Image Request scheduled in " + String(pollInterval) + " seconds"); - } else { - writeSerial("ERROR: Incomplete image data (have " + String(payloadLen - 7) + ", need " + String(imageLength) + ")"); - } - } else { - writeSerial("ERROR: Packet 0x82 payload too short: " + String(payloadLen)); - } - } else if (packetId == 0x83) { - writeSerial("Server requests configuration, sending Display Announcement"); - sendDisplayAnnouncement(); - } else { - if (payloadLen >= 3) { - imageDataWritten(NULL, NULL, payload + 1, payloadLen - 1); - } else { - writeSerial("ERROR: Unknown packet ID 0x" + String(packetId, HEX) + ", payload too short: " + String(payloadLen)); - } - } - break; - } - parsePos += packetLength; - } - if (parsePos > 0) { - uint32_t remaining = tcpReceiveBufferPos - parsePos; - if (remaining > 0) { - memmove(tcpReceiveBuffer, &tcpReceiveBuffer[parsePos], remaining); - } - tcpReceiveBufferPos = remaining; - } + while (tcpReceiveBufferPos >= 2) { + uint16_t flen = (uint16_t)(tcpReceiveBuffer[0] | (tcpReceiveBuffer[1] << 8)); + if (flen == 0 || flen > WIFI_LAN_MAX_PAYLOAD) { + writeSerial("LAN: invalid frame length, closing"); + disconnectWiFiServer(); + return; + } + if (tcpReceiveBufferPos < (uint32_t)(2 + flen)) { + break; + } + imageDataWritten(NULL, NULL, tcpReceiveBuffer + 2, flen); + uint32_t consumed = 2u + (uint32_t)flen; + uint32_t rem = tcpReceiveBufferPos - consumed; + if (rem > 0) { + memmove(tcpReceiveBuffer, tcpReceiveBuffer + consumed, rem); } + tcpReceiveBufferPos = rem; + } +} + +void restartWiFiLanAfterReconnect() { + if (!wifiConnected || WiFi.status() != WL_CONNECTED) { + return; } + disconnectWiFiServer(); + wifiServer.begin(wifiServerPort); + writeSerial("TCP server restarted on port " + String(wifiServerPort)); + restartLanService(); } #endif diff --git a/src/wifi_service.h b/src/wifi_service.h index c46b57b..c19fc06 100644 --- a/src/wifi_service.h +++ b/src/wifi_service.h @@ -3,12 +3,12 @@ #ifdef TARGET_ESP32 -#define WIFI_SERVER_RECONNECT_DELAY_MS 30000U - -void initWiFi(); -void discoverAndConnectWiFiServer(); +void initWiFi(bool waitForConnection = true); void disconnectWiFiServer(); void handleWiFiServer(); +void restartWiFiLanAfterReconnect(); +/// Publish MSD (bytes after company ID) as mDNS TXT key ``msd`` (28 hex chars). No-op if Wi-Fi down. +void opendisplay_mdns_update_msd_txt(void); #endif