diff --git a/Marlin/Configuration.h b/Marlin/Configuration.h
index 35b45e7321..bb724672b0 100644
--- a/Marlin/Configuration.h
+++ b/Marlin/Configuration.h
@@ -1957,6 +1957,14 @@
//
//#define FF_INTERFACEBOARD
+//
+// TFT GLCD Panel with Marlin UI
+// Panel connected to main board by SPI or I2C interface.
+// See https://github.com/Serhiy-K/TFTGLCDAdapter
+//
+//#define TFTGLCD_PANEL_SPI
+//#define TFTGLCD_PANEL_I2C
+
//=============================================================================
//======================= LCD / Controller Selection =======================
//========================= (Graphical LCDs) ========================
diff --git a/Marlin/Configuration_adv.h b/Marlin/Configuration_adv.h
index 915714039f..0435b6f97c 100644
--- a/Marlin/Configuration_adv.h
+++ b/Marlin/Configuration_adv.h
@@ -1106,7 +1106,7 @@
#define BOOTSCREEN_TIMEOUT 4000 // (ms) Total Duration to display the boot screen(s)
#endif
-#if EITHER(SDSUPPORT, LCD_SET_PROGRESS_MANUALLY) && (HAS_MARLINUI_U8GLIB || HAS_MARLINUI_HD44780)
+#if EITHER(SDSUPPORT, LCD_SET_PROGRESS_MANUALLY) && ANY(HAS_MARLINUI_U8GLIB, HAS_MARLINUI_HD44780, IS_TFTGLCD_PANEL)
//#define SHOW_REMAINING_TIME // Display estimated time to completion
#if ENABLED(SHOW_REMAINING_TIME)
//#define USE_M73_REMAINING_TIME // Use remaining time from M73 command instead of estimation
@@ -1117,7 +1117,7 @@
//#define PRINT_PROGRESS_SHOW_DECIMALS // Show progress with decimal digits
#endif
- #if HAS_MARLINUI_HD44780
+ #if EITHER(HAS_MARLINUI_HD44780, IS_TFTGLCD_PANEL)
//#define LCD_PROGRESS_BAR // Show a progress bar on HD44780 LCDs for SD printing
#if ENABLED(LCD_PROGRESS_BAR)
#define PROGRESS_BAR_BAR_TIME 2000 // (ms) Amount of time to show the bar
diff --git a/Marlin/src/feature/bedlevel/ubl/ubl_G29.cpp b/Marlin/src/feature/bedlevel/ubl/ubl_G29.cpp
index 9fc9ec099e..dc91b7d6b1 100644
--- a/Marlin/src/feature/bedlevel/ubl/ubl_G29.cpp
+++ b/Marlin/src/feature/bedlevel/ubl/ubl_G29.cpp
@@ -997,6 +997,10 @@
if (do_ubl_mesh_map) display_map(g29_map_type); // Display the current point
+ #if IS_TFTGLCD_PANEL
+ ui.ubl_plot(lpos.x, lpos.y); // update plot screen
+ #endif
+
ui.refresh();
float new_z = z_values[lpos.x][lpos.y];
diff --git a/Marlin/src/inc/Conditionals_LCD.h b/Marlin/src/inc/Conditionals_LCD.h
index af8ef73817..fa65991c2f 100644
--- a/Marlin/src/inc/Conditionals_LCD.h
+++ b/Marlin/src/inc/Conditionals_LCD.h
@@ -217,6 +217,28 @@
#define LCD_WIDTH 16
#define LCD_HEIGHT 2
+#elif EITHER(TFTGLCD_PANEL_SPI, TFTGLCD_PANEL_I2C)
+
+ #define IS_TFTGLCD_PANEL 1
+ #define IS_ULTIPANEL // Note that IS_ULTIPANEL leads to HAS_WIRED_LCD
+
+ #if ENABLED(SDSUPPORT) && DISABLED(LCD_PROGRESS_BAR)
+ #define LCD_PROGRESS_BAR
+ #endif
+ #if ENABLED(TFTGLCD_PANEL_I2C)
+ #define LCD_USE_I2C_BUZZER // Enable buzzer on LCD for I2C and SPI buses (LiquidTWI2 not required)
+ #define LCD_I2C_ADDRESS 0x27 // Must be equal to panel's I2C slave addres
+ #endif
+ #define STD_ENCODER_PULSES_PER_STEP 2
+ #define STD_ENCODER_STEPS_PER_MENU_ITEM 1
+ #define LCD_WIDTH 20 // 20 or 24 chars in line
+ #define LCD_HEIGHT 10 // Character lines
+ #define LCD_CONTRAST_MIN 127
+ #define LCD_CONTRAST_MAX 255
+ #define DEFAULT_LCD_CONTRAST 250
+ #define CONVERT_TO_EXT_ASCII // Use extended 128-255 symbols from ASCII table.
+ // At this time present conversion only for cyrillic - bg, ru and uk languages.
+ // First 7 ASCII symbols in panel font must be replaced with Marlin's special symbols.
#endif
#if ENABLED(IS_RRD_FG_SC)
@@ -459,6 +481,8 @@
#define HAS_WIRED_LCD 1
#if ENABLED(DOGLCD)
#define HAS_MARLINUI_U8GLIB 1
+ #elif IS_TFTGLCD_PANEL
+ // Neither DOGM nor HD44780. Fully customized interface.
#elif DISABLED(HAS_GRAPHICAL_TFT)
#define HAS_MARLINUI_HD44780 1
#endif
diff --git a/Marlin/src/inc/Conditionals_post.h b/Marlin/src/inc/Conditionals_post.h
index bf297b633f..80c42955e9 100644
--- a/Marlin/src/inc/Conditionals_post.h
+++ b/Marlin/src/inc/Conditionals_post.h
@@ -318,6 +318,10 @@
#define _LCD_CONTRAST_MIN 64
#define _LCD_CONTRAST_INIT 128
#define _LCD_CONTRAST_MAX 255
+#elif IS_TFTGLCD_PANEL
+ #define _LCD_CONTRAST_MIN 0
+ #define _LCD_CONTRAST_INIT 250
+ #define _LCD_CONTRAST_MAX 255
#endif
#ifdef _LCD_CONTRAST_INIT
@@ -2453,7 +2457,7 @@
/**
* Buzzer/Speaker
*/
-#if PIN_EXISTS(BEEPER) || EITHER(LCD_USE_I2C_BUZZER, PCA9632_BUZZER)
+#if PIN_EXISTS(BEEPER) || ANY(LCD_USE_I2C_BUZZER, PCA9632_BUZZER, IS_TFTGLCD_PANEL)
#define HAS_BUZZER 1
#if PIN_EXISTS(BEEPER)
#define USE_BEEPER 1
diff --git a/Marlin/src/inc/SanityCheck.h b/Marlin/src/inc/SanityCheck.h
index 7093d25219..5bd6ef7c78 100644
--- a/Marlin/src/inc/SanityCheck.h
+++ b/Marlin/src/inc/SanityCheck.h
@@ -693,8 +693,8 @@ static_assert(Y_MAX_LENGTH >= Y_BED_SIZE, "Movement bounds (Y_MIN_POS, Y_MAX_POS
#if ENABLED(LCD_PROGRESS_BAR)
#if NONE(SDSUPPORT, LCD_SET_PROGRESS_MANUALLY)
#error "LCD_PROGRESS_BAR requires SDSUPPORT or LCD_SET_PROGRESS_MANUALLY."
- #elif !HAS_MARLINUI_HD44780
- #error "LCD_PROGRESS_BAR requires a character LCD."
+ #elif NONE(HAS_MARLINUI_HD44780, IS_TFTGLCD_PANEL)
+ #error "LCD_PROGRESS_BAR only applies to HD44780 character LCD and TFTGLCD_PANEL_(SPI|I2C)."
#elif HAS_MARLINUI_U8GLIB
#error "LCD_PROGRESS_BAR does not apply to graphical displays."
#elif ENABLED(FILAMENT_LCD_DISPLAY)
@@ -2274,7 +2274,9 @@ static_assert(hbm[Z_AXIS] >= 0, "HOMING_BUMP_MM.Z must be greater than or equal
+ ENABLED(TFT_LVGL_UI_FSMC) \
+ ENABLED(TFT_LVGL_UI_SPI) \
+ ENABLED(ANYCUBIC_LCD_I3MEGA) \
- + ENABLED(ANYCUBIC_LCD_CHIRON)
+ + ENABLED(ANYCUBIC_LCD_CHIRON) \
+ + ENABLED(TFTGLCD_PANEL_SPI) \
+ + ENABLED(TFTGLCD_PANEL_I2C)
#error "Please select only one LCD controller option."
#endif
diff --git a/Marlin/src/lcd/TFTGLCD/lcdprint_TFTGLCD.cpp b/Marlin/src/lcd/TFTGLCD/lcdprint_TFTGLCD.cpp
new file mode 100644
index 0000000000..6cf660a6a9
--- /dev/null
+++ b/Marlin/src/lcd/TFTGLCD/lcdprint_TFTGLCD.cpp
@@ -0,0 +1,1142 @@
+/**
+ * Marlin 3D Printer Firmware
+ * Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
+ *
+ * Based on Sprinter and grbl.
+ * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ */
+
+/**
+ * @file lcdprint_TFTGLCD.cpp
+ * @brief LCD print API for TFT-GLCD interface
+ * @author Yunhui Fu (yhfudev@gmail.com)
+ * @version 1.0
+ * @date 2016-08-19
+ * @copyright GPL/BSD
+ */
+
+/**
+ * The TFTGLCD only supports ??? languages.
+ */
+
+#include "../../inc/MarlinConfigPre.h"
+
+#if IS_TFTGLCD_PANEL
+
+#include "../ultralcd.h"
+#include "../../MarlinCore.h"
+#include "../../libs/numtostr.h"
+
+#include "ultralcd_TFTGLCD.h"
+
+#include
+
+int lcd_glyph_height(void) { return 1; }
+
+typedef struct _TFTGLCD_charmap_t {
+ wchar_t uchar; // the unicode char
+ uint8_t idx; // the glyph of the char in the ROM
+ uint8_t idx2; // the char used to be combined with the idx to simulate a single char
+} TFTGLCD_charmap_t;
+
+#ifdef __AVR__
+ #define IV(a) U##a
+#else
+ #define IV(a) L##a
+#endif
+
+static const TFTGLCD_charmap_t g_TFTGLCD_charmap_device[] PROGMEM = {
+ // sorted by uchar:
+ #if DISPLAY_CHARSET_HD44780 == JAPANESE
+
+ {IV('¢'), 0xEC, 0}, // A2
+ {IV('°'), 0xDF, 0}, // B0, Marlin special: '°' LCD_STR_DEGREE (0x09)
+ {IV('ä'), 0xE1, 0}, // E4
+ {IV('ö'), 0xEF, 0}, // F6
+ {IV('÷'), 0xFD, 0}, // 00F7
+ {IV('ü'), 0xF5, 0}, // 00FC
+ {IV('ˣ'), 0xEB, 0}, // 02E3
+
+ {IV('·'), 0xA5, 0}, // 0387
+ {IV('Ώ'), 0xF4, 0}, // 038F
+ {IV('Θ'), 0xF2, 0}, // 0398, Theta
+ {IV('Ξ'), 0xE3, 0}, // 039E, Xi
+ {IV('Σ'), 0xF6, 0}, // 03A3, Sigma
+ {IV('Ω'), 0xF4, 0}, // 03A9, Omega
+ {IV('ά'), 0xE0, 0}, // 03AC
+ {IV('έ'), 0xE3, 0}, // 03AD
+ {IV('α'), 0xE0, 0}, // 03B1, alpha
+ {IV('β'), 0xE2, 0}, // 03B2, beta
+ {IV('ε'), 0xE3, 0}, // 03B5, epsilon
+ {IV('θ'), 0xF2, 0}, // 03B8, theta
+ {IV('μ'), 0xE4, 0}, // 03BC, mu
+ {IV('ξ'), 0xE3, 0}, // 03BE, xi
+ {IV('π'), 0xF7, 0}, // 03C0, pi
+ {IV('ρ'), 0xE6, 0}, // 03C1, rho
+ {IV('σ'), 0xE5, 0}, // 03C3, sigma
+
+ {IV('←'), 0x7F, 0}, // 2190
+ {IV('→'), 0x7E, 0}, // 2192, Marlin special: '⮈⮉⮊⮋➤→' LCD_STR_ARROW_RIGHT (0x03)
+ {IV('√'), 0xE8, 0}, // 221A
+ {IV('∞'), 0xF3, 0}, // 221E
+ {IV('█'), 0xFF, 0}, // 2588
+
+ //{IV(''), 0xA0, 0},
+ {IV('。'), 0xA1, 0},
+ {IV('「'), 0xA2, 0},
+ {IV('」'), 0xA3, 0},
+ {IV('゛'), 0xDE, 0}, // ‶
+ {IV('゜'), 0xDF, 0}, // '〫'
+ {IV('゠'), '=', 0},
+ {IV('ァ'), 0xA7, 0},
+ {IV('ア'), 0xB1, 0},
+ {IV('ィ'), 0xA8, 0},
+ {IV('イ'), 0xB2, 0},
+ {IV('ゥ'), 0xA9, 0},
+ {IV('ウ'), 0xB3, 0},
+ {IV('ェ'), 0xAA, 0},
+ {IV('エ'), 0xB4, 0},
+ {IV('ォ'), 0xAB, 0},
+
+ {IV('オ'), 0xB5, 0},
+ {IV('カ'), 0xB6, 0},
+ {IV('ガ'), 0xB6, 0xDE},
+ {IV('キ'), 0xB7, 0},
+ {IV('ギ'), 0xB7, 0xDE}, //
+ {IV('ク'), 0xB8, 0},
+ {IV('グ'), 0xB8, 0xDE},
+ {IV('ケ'), 0xB9, 0},
+ {IV('ゲ'), 0xB9, 0xDE},
+ {IV('コ'), 0xBA, 0},
+ {IV('ゴ'), 0xBA, 0xDE},
+ {IV('サ'), 0xBB, 0},
+ {IV('ザ'), 0xBB, 0xDE},
+ {IV('シ'), 0xBC, 0},
+ {IV('ジ'), 0xBC, 0xDE},
+ {IV('ス'), 0xBD, 0},
+ {IV('ズ'), 0xBD, 0xDE},
+ {IV('セ'), 0xBE, 0},
+ {IV('ゼ'), 0xBE, 0xDE},
+ {IV('ソ'), 0xBF, 0},
+ {IV('ゾ'), 0xBF, 0xDE},
+
+ {IV('タ'), 0xC0, 0},
+ {IV('ダ'), 0xC0, 0xDE},
+ {IV('チ'), 0xC1, 0},
+ {IV('ヂ'), 0xC1, 0xDE},
+ {IV('ッ'), 0xAF, 0},
+ {IV('ツ'), 0xC2, 0},
+ {IV('ヅ'), 0xC2, 0xDE},
+ {IV('テ'), 0xC3, 0},
+ {IV('デ'), 0xC3, 0xDE},
+ {IV('ト'), 0xC4, 0},
+ {IV('ド'), 0xC4, 0xDE},
+ {IV('ナ'), 0xC5, 0},
+ {IV('ニ'), 0xC6, 0},
+ {IV('ヌ'), 0xC7, 0},
+ {IV('ネ'), 0xC8, 0},
+ {IV('ノ'), 0xC9, 0},
+ {IV('ハ'), 0xCA, 0},
+ {IV('バ'), 0xCA, 0xDE},
+ {IV('パ'), 0xCA, 0xDF},
+ {IV('ヒ'), 0xCB, 0},
+ {IV('ビ'), 0xCB, 0xDE},
+ {IV('ピ'), 0xCB, 0xDF},
+ {IV('フ'), 0xCC, 0},
+ {IV('ブ'), 0xCC, 0xDE},
+ {IV('プ'), 0xCC, 0xDF},
+ {IV('ヘ'), 0xCD, 0},
+ {IV('ベ'), 0xCD, 0xDE},
+ {IV('ペ'), 0xCD, 0xDF},
+ {IV('ホ'), 0xCE, 0},
+ {IV('ボ'), 0xCE, 0xDE},
+ {IV('ポ'), 0xCE, 0xDF},
+ {IV('マ'), 0xCF, 0},
+
+ {IV('ミ'), 0xD0, 0},
+ {IV('ム'), 0xD1, 0},
+ {IV('メ'), 0xD2, 0},
+ {IV('モ'), 0xD3, 0},
+ {IV('ャ'), 0xAC, 0},
+ {IV('ヤ'), 0xD4, 0},
+ {IV('ュ'), 0xAD, 0},
+ {IV('ユ'), 0xD5, 0},
+ {IV('ョ'), 0xAE, 0},
+ {IV('ヨ'), 0xD6, 0},
+ {IV('ラ'), 0xD7, 0},
+ {IV('リ'), 0xD8, 0},
+ {IV('ル'), 0xD9, 0},
+ {IV('レ'), 0xDA, 0},
+ {IV('ロ'), 0xDB, 0},
+ {IV('ワ'), 0xDC, 0},
+ {IV('ヲ'), 0xA6, 0},
+ {IV('ン'), 0xDD, 0},
+ {IV('ヴ'), 0xB3, 0xDE},
+ {IV('ヷ'), 0xDC, 0xDE},
+ {IV('ヺ'), 0xA6, 0xDE},
+ {IV('・'), 0xA5, 0},
+ {IV('ー'), 0xB0, 0},
+ {IV('ヽ'), 0xA4, 0},
+
+ //{IV('g'), 0xE7, 0}, // error
+ //{IV(''), 0xE9, 0},
+ //{IV('j'), 0xEA, 0}, // error
+ //{IV(''), 0xED, 0},
+ //{IV(''), 0xEE, 0},
+
+ //{IV('p'), 0xF0, 0}, // error
+ //{IV('q'), 0xF1, 0}, // error
+ //{IV(''), 0xF8, 0},
+ //{IV('y'), 0xF9, 0}, // error
+ {IV('万'), 0xFB, 0},
+ {IV('円'), 0xFC, 0},
+ {IV('千'), 0xFA, 0},
+ //{IV(''), 0xFE, 0},
+
+ //、・ヲァィゥェォャュョッー
+ {IV('、'), 0xA4, 0}, //ヽ
+ {IV('・'), 0xA5, 0}, //・
+ {IV('ヲ'), 0xA6, 0}, //ヲ
+ {IV('ァ'), 0xA7, 0}, //ァ
+ {IV('ィ'), 0xA8, 0}, //ィ
+ {IV('ゥ'), 0xA9, 0}, //ゥ
+ {IV('ェ'), 0xAA, 0}, //ェ
+ {IV('ォ'), 0xAB, 0}, //ォ
+ {IV('ャ'), 0xAC, 0}, //ャ
+ {IV('ュ'), 0xAD, 0}, //ュ
+ {IV('ョ'), 0xAE, 0}, //ョ
+ {IV('ッ'), 0xAF, 0}, //ッ
+ {IV('ー'), 0xB0, 0}, //ー
+
+ //アイウエオカキクケコサシスセ
+ {IV('ア'), 0xB1, 0}, //ア
+ {IV('イ'), 0xB2, 0}, //イ
+ {IV('ウ'), 0xB3, 0}, //ウ
+ {IV('エ'), 0xB4, 0}, //エ
+ {IV('オ'), 0xB5, 0}, //オ
+ {IV('カ'), 0xB6, 0}, //カ
+ {IV('キ'), 0xB7, 0}, //キ
+ {IV('ク'), 0xB8, 0}, //ク
+ {IV('ケ'), 0xB9, 0}, //ケ
+ {IV('コ'), 0xBA, 0}, //コ
+ {IV('サ'), 0xBB, 0}, //サ
+ {IV('シ'), 0xBC, 0}, //シ
+ {IV('ス'), 0xBD, 0}, //ス
+ {IV('セ'), 0xBE, 0}, //セ
+
+ //ソタチツテトナニヌネノハヒフ
+ {IV('ソ'), 0xBF, 0}, //ソ
+ {IV('タ'), 0xC0, 0}, //タ
+ {IV('チ'), 0xC1, 0}, //チ
+ {IV('ツ'), 0xC2, 0}, //ツ
+ {IV('テ'), 0xC3, 0}, //テ
+ {IV('ト'), 0xC4, 0}, //ト
+ {IV('ナ'), 0xC5, 0}, //ナ
+ {IV('ニ'), 0xC6, 0}, //ニ
+ {IV('ヌ'), 0xC7, 0}, //ヌ
+ {IV('ネ'), 0xC8, 0}, //ネ
+ {IV('ノ'), 0xC9, 0}, //ノ
+ {IV('ハ'), 0xCA, 0}, //ハ
+ {IV('ヒ'), 0xCB, 0}, //ヒ
+ {IV('フ'), 0xCC, 0}, //フ
+
+ //ヘホマミムメモヤユヨラリルレロワン゙゚
+ {IV('ヘ'), 0xCD, 0}, //ヘ
+ {IV('ホ'), 0xCE, 0}, //ホ
+ {IV('マ'), 0xCF, 0}, //マ
+ {IV('ミ'), 0xD0, 0}, //ミ
+ {IV('ム'), 0xD1, 0}, //ム
+ {IV('メ'), 0xD2, 0}, //メ
+ {IV('モ'), 0xD3, 0}, //モ
+ {IV('ヤ'), 0xD4, 0}, //ヤ
+ {IV('ユ'), 0xD5, 0}, //ユ
+ {IV('ヨ'), 0xD6, 0}, //ヨ
+ {IV('ラ'), 0xD7, 0}, //ラ
+ {IV('リ'), 0xD8, 0}, //リ
+ {IV('ル'), 0xD9, 0}, //ル
+ {IV('レ'), 0xDA, 0}, //レ
+ {IV('ロ'), 0xDB, 0}, //ロ
+ {IV('ワ'), 0xDC, 0}, //ワ
+ {IV('ン'), 0xDD, 0}, //ン
+ {IV('゙'), 0xDE, 0}, // ゛
+ {IV('゚'), 0xDF, 0}, // ゜
+
+ {IV('¥'), 0x5C, 0},
+
+ #elif DISPLAY_CHARSET_HD44780 == WESTERN
+ // 0x10 -- 0x1F (except 0x1C)
+ // 0x80 -- 0xFF (except 0xA7,0xB0,0xB1,0xB3,0xB4,0xBF,0xD1,0xF8,0xFA,0xFC-0xFF)
+
+ {IV('¡'), 0xA9, 0},
+ {IV('¢'), 0xA4, 0},
+ {IV('£'), 0xA5, 0},
+ {IV('¥'), 0xA6, 0},
+ {IV('§'), 0xD2, 0}, // section sign
+ {IV('©'), 0xCF, 0},
+
+ {IV('ª'), 0x9D, 0},
+ {IV('«'), 0xBB, 0},
+ {IV('®'), 0xCE, 0},
+
+ {IV('°'), 0xB2, 0}, // Marlin special: '°' LCD_STR_DEGREE (0x09)
+ //{IV(''), 0xD1, 0},
+ {IV('±'), 0x10, 0}, //∓±
+ //{'='), 0x1C, 0}, // error
+ {IV('²'), 0x1E, 0},
+ {IV('³'), 0x1F, 0},
+ {IV('¶'), 0xD3, 0}, // pilcrow sign
+ {IV('º'), 0x9E, 0},
+ {IV('»'), 0xBC, 0}, // 00BB
+ //{IV(''), 0xB3, 0}, // error
+ //{IV(''), 0xB4, 0}, // error
+ {IV('¼'), 0xB6, 0}, // 00BC
+ {IV('½'), 0xB5, 0}, // 00BD
+ //{IV('¾'), '3', 0}, // 00BE
+ {IV('¿'), 0x9F, 0}, // 00BF
+
+ {IV('Â'), 0x8F, 0},
+ {IV('Ã'), 0xAA, 0},
+ {IV('Ä'), 0x8E, 0},
+ {IV('Æ'), 0x92, 0},
+ {IV('Ç'), 0x80, 0},
+ {IV('É'), 0x90, 0},
+ {IV('Ñ'), 0x9C, 0},
+ {IV('Õ'), 0xAC, 0},
+ {IV('Ö'), 0x99, 0},
+ {IV('×'), 0xB7, 0},
+ {IV('Ø'), 0xAE, 0},
+ {IV('Ü'), 0x9A, 0},
+ {IV('à'), 0x85, 0},
+ {IV('á'), 0xA0, 0},
+ {IV('â'), 0x83, 0},
+ {IV('ã'), 0xAB, 0},
+ {IV('ä'), 0x84, 0},
+ {IV('å'), 0x86, 0},
+ {IV('æ'), 0x91, 0},
+ {IV('ç'), 0x87, 0},
+ {IV('è'), 0x8A, 0},
+ {IV('é'), 0x82, 0},
+ {IV('ê'), 0x88, 0},
+ {IV('ë'), 0x89, 0},
+ {IV('ì'), 0x8D, 0},
+ {IV('í'), 0xA1, 0},
+ {IV('î'), 0x8C, 0},
+ {IV('ï'), 0x8B, 0},
+
+ {IV('ñ'), 0x9B, 0},
+ {IV('ò'), 0x95, 0},
+ {IV('ó'), 0xA2, 0},
+ {IV('ô'), 0x93, 0},
+ {IV('õ'), 0xAD, 0},
+ {IV('ö'), 0x94, 0},
+ {IV('÷'), 0xB8, 0},
+ {IV('ø'), 0xAF, 0},
+ {IV('ù'), 0x97, 0},
+ {IV('ú'), 0xA3, 0},
+ {IV('û'), 0x96, 0},
+ {IV('ü'), 0x81, 0},
+ {IV('ÿ'), 0x98, 0},
+
+ //{IV(''), 0xB0, 0}, // error
+ //{IV(''), 0xB1, 0}, // error
+ {IV('ƒ'), 0xA8, 0}, // 0192
+
+ {IV('Ύ'), 0xDB, 0}, // 038E
+ {IV('Ώ'), 0xDE, 0}, // 038F
+ {IV('ΐ'), 0xE7, 0}, // 0390
+
+ {IV('Γ'), 0xD4, 0}, // 0393, Gamma
+ {IV('Δ'), 0xD5, 0}, // 0394, Delta, ◿
+ {IV('Θ'), 0xD6, 0}, // 0398, Theta
+ {IV('Λ'), 0xD7, 0}, // 039B, Lambda
+ {IV('Ξ'), 0xD8, 0}, // 039E, Xi
+ {IV('Π'), 0xD9, 0}, // Pi
+ {IV('Σ'), 0xDA, 0}, // Sigma
+ {IV('Υ'), 0xDB, 0}, // Upsilon
+ {IV('Φ'), 0xDC, 0}, // Phi
+ {IV('Ψ'), 0xDD, 0}, // Psi
+ {IV('Ω'), 0xDE, 0}, // Omega
+
+ {IV('ά'), 0xDF, 0}, // 03AC
+ {IV('έ'), 0xE3, 0}, // 03AD
+ {IV('ή'), 0xE5, 0}, // 03AE
+ {IV('ί'), 0xE7, 0}, // 03AF
+ {IV('ΰ'), 0xF1, 0}, // 03B0
+
+ {IV('α'), 0xDF, 0}, // alpha
+ {IV('β'), 0xE0, 0}, // beta
+ {IV('γ'), 0xE1, 0}, // gamma
+ {IV('δ'), 0xE2, 0}, // delta
+ {IV('ε'), 0xE3, 0}, // epsilon
+ {IV('ζ'), 0xE4, 0}, // zeta
+ {IV('η'), 0xE5, 0}, // eta
+ {IV('θ'), 0xE6, 0}, // theta
+ {IV('ι'), 0xE7, 0}, // lota
+ {IV('κ'), 0xE8, 0}, // kappa
+ {IV('λ'), 0xE9, 0}, // lambda
+ {IV('μ'), 0xEA, 0}, // mu
+ {IV('ν'), 0xEB, 0}, // nu
+ {IV('ξ'), 0xEC, 0}, // xi
+ {IV('π'), 0xED, 0}, // pi
+ {IV('ρ'), 0xEE, 0}, // rho
+ {IV('σ'), 0xEF, 0}, // sigma
+
+ {IV('τ'), 0xF0, 0}, // tau
+ {IV('υ'), 0xF1, 0}, // upsilon
+ {IV('χ'), 0xF2, 0}, // chi
+ {IV('ψ'), 0xF3, 0}, // psi
+ {IV('ω'), 0xF4, 0}, // 03C9, omega
+ {IV('ϊ'), 0xE7, 0}, // 03CA
+ {IV('ϋ'), 0xF1, 0}, // 03CB
+ {IV('ύ'), 0xF1, 0}, // 03CD
+ {IV('ώ'), 0xF4, 0}, // 03CE
+
+ {IV('•'), 0xCD, 0}, // ·
+ {IV('℞'), 0xA7, 0}, // ℞ Pt ASCII 158
+ {IV('™'), 0xD0, 0},
+ {IV('↤'), 0xF9, 0}, // ⟻
+ {IV('↵'), 0xC4, 0},
+ {IV('↻'), 0x04, 0}, // Marlin special: '↻↺⟳⟲' LCD_STR_REFRESH (0x01)
+ {IV('⇥'), 0xFB, 0},
+ {IV('√'), 0xBE, 0}, // √
+ {IV('∞'), 0xC2, 0}, // infinity
+ {IV('∫'), 0x1B, 0},
+ {IV('∼'), 0x1D, 0},
+ {IV('≈'), 0x1A, 0},
+ {IV('≠'), 0xBD, 0},
+ {IV('≡'), 0x11, 0},
+ {IV('≤'), 0xB9, 0},// ≤≥ ⩽⩾
+ {IV('≥'), 0xBA, 0},
+ //{IV(''), 0xBF, 0}, // error
+
+ {IV('⌠'), 0xC0, 0},
+ {IV('⌡'), 0xC1, 0},
+
+ {IV('⎧'), 0x14, 0},
+ {IV('⎩'), 0x15, 0},
+ {IV('⎫'), 0x16, 0},
+ {IV('⎭'), 0x17, 0},
+ {IV('⎰'), 0x18, 0},
+ {IV('⎱'), 0x19, 0},
+ {IV('⎲'), 0x12, 0},
+ {IV('⎳'), 0x13, 0},
+
+ {IV('⏱'), 0x07, 0}, // Marlin special: '🕐🕑🕒🕓🕔🕕🕖🕗🕘🕙🕚🕛🕜🕝🕞🕟🕠🕡🕢🕣🕤🕥🕦🕧 ⌚⌛⏰⏱⏳⧖⧗' LCD_STR_CLOCK (0x05)
+ {IV('┌'), 0xC9, 0},
+ {IV('┐'), 0xCA, 0},
+ {IV('└'), 0xCB, 0},
+ {IV('┘'), 0xCC, 0},
+ {IV('◸'), 0xC3, 0}, // ◿
+ {IV('⭠'), 0xC8, 0},
+ {IV('⭡'), 0xC5, 0},
+ {IV('⭢'), 0xC7, 0},
+ {IV('⭣'), 0xC6, 0},
+
+
+ {IV('⯆'), 0xF5, 0},
+ {IV('⯇'), 0xF7, 0}, // ⯅
+ {IV('⯈'), 0xF6, 0},
+ //{IV(''), 0xF8, 0}, // error
+ //{IV(''), 0xFA, 0}, // error
+ //{IV(''), 0xFC, 0}, // error
+ //{IV(''), 0xFD, 0}, // error
+ //{IV(''), 0xFE, 0}, // error
+ //{IV(''), 0xFF, 0}, // error
+
+ #elif DISPLAY_CHARSET_HD44780 == CYRILLIC
+
+ #ifdef CONVERT_TO_EXT_ASCII
+ {IV('°'), 0x01, 0}, // 00B0, Marlin special: '°' LCD_STR_DEGREE (0x09)
+ {IV('²'), 0x0e, 0}, // 0x32 if no special symbol in panel font
+ {IV('³'), 0x0f, 0}, // 0x33 if no special symbol in panel font
+
+ // translate to cp866 codepage
+ //first ASCII symbols in panel font must be replaced with Marlin special symbols
+ {IV('Ё'), 0xF0, 0}, // 0401
+ {IV('Є'), 0xF2, 0}, // 0404
+ {IV('І'), 'I', 0}, // 0406
+ {IV('Ї'), 0xF4, 0}, // 0407
+ {IV('Ў'), 0xF6, 0}, // 040E
+ {IV('А'), 0x80, 0}, // 0410
+ {IV('Б'), 0x81, 0},
+ {IV('В'), 0x82, 0},
+ {IV('Г'), 0x83, 0},
+ {IV('Д'), 0x84, 0},
+ {IV('Е'), 0x85, 0},
+ {IV('Ж'), 0x86, 0},
+ {IV('З'), 0x87, 0},
+ {IV('И'), 0x88, 0},
+ {IV('Й'), 0x89, 0},
+ {IV('К'), 0x8A, 0},
+ {IV('Л'), 0x8B, 0},
+ {IV('М'), 0x8C, 0},
+ {IV('Н'), 0x8D, 0},
+ {IV('О'), 0x8E, 0},
+ {IV('П'), 0x8F, 0},
+ {IV('Р'), 0x90, 0},
+ {IV('С'), 0x91, 0},
+ {IV('Т'), 0x92, 0},
+ {IV('У'), 0x93, 0},
+ {IV('Ф'), 0x94, 0},
+ {IV('Х'), 0x95, 0},
+ {IV('Ц'), 0x96, 0},
+ {IV('Ч'), 0x97, 0},
+ {IV('Ш'), 0x98, 0},
+ {IV('Щ'), 0x99, 0},
+ {IV('Ъ'), 0x9A, 0},
+ {IV('Ы'), 0x9B, 0},
+ {IV('Ь'), 0x9C, 0},
+ {IV('Э'), 0x9D, 0},
+ {IV('Ю'), 0x9E, 0},
+ {IV('Я'), 0x9F, 0},
+
+ {IV('а'), 0xA0, 0},
+ {IV('б'), 0xA1, 0},
+ {IV('в'), 0xA2, 0},
+ {IV('г'), 0xA3, 0},
+ {IV('д'), 0xA4, 0},
+ {IV('е'), 0xA5, 0},
+ {IV('ж'), 0xA6, 0},
+ {IV('з'), 0xA7, 0},
+ {IV('и'), 0xA8, 0},
+ {IV('й'), 0xA9, 0},
+ {IV('к'), 0xAA, 0},
+ {IV('л'), 0xAB, 0},
+ {IV('м'), 0xAC, 0},
+ {IV('н'), 0xAD, 0},
+ {IV('о'), 0xAE, 0},
+ {IV('п'), 0xAF, 0},
+ {IV('р'), 0xE0, 0},
+ {IV('с'), 0xE1, 0},
+ {IV('т'), 0xE2, 0},
+ {IV('у'), 0xE3, 0},
+ {IV('ф'), 0xE4, 0},
+ {IV('х'), 0xE5, 0},
+ {IV('ц'), 0xE6, 0},
+ {IV('ч'), 0xE7, 0},
+ {IV('ш'), 0xE8, 0},
+ {IV('щ'), 0xE9, 0},
+ {IV('ъ'), 0xEA, 0},
+ {IV('ы'), 0xEB, 0},
+ {IV('ь'), 0xEC, 0},
+ {IV('э'), 0xED, 0},
+ {IV('ю'), 0xEE, 0},
+ {IV('я'), 0xEF, 0}, // 044F
+ {IV('ё'), 0xF1, 0}, // 0451
+ {IV('є'), 0xF3, 0}, // 0454
+ {IV('і'), 'i', 0}, // 0456
+ {IV('ї'), 0xF5, 0}, // 0457
+ {IV('ў'), 0xF7, 0}, // 045E
+
+ #else
+
+ {IV('¢'), 0x5C, 0}, // 00A2
+ {IV('£'), 0xCF, 0}, // 00A3
+ {IV('°'), 0x01, 0}, // 00B0, Marlin special: '°' LCD_STR_DEGREE (0x09)
+
+ //{IV(''), 0x80, 0},
+ //{IV(''), 0x81, 0},
+ //{IV(''), 0x82, 0},
+ //{IV(''), 0x83, 0},
+ //{IV(''), 0x84, 0},
+ //{IV(''), 0x85, 0},
+ //{IV(''), 0x86, 0},
+ //{IV(''), 0x87, 0},
+ //{IV(''), 0x88, 0},
+ //{IV(''), 0x89, 0},
+ //{IV(''), 0x8A, 0},
+ //{IV(''), 0x8B, 0},
+ //{IV(''), 0x8C, 0},
+ //{IV(''), 0x8D, 0},
+ //{IV(''), 0x8E, 0},
+ //{IV(''), 0x8F, 0},
+
+ //{IV(''), 0x90, 0},
+ //{IV(''), 0x91, 0},
+ //{IV(''), 0x92, 0},
+ //{IV(''), 0x93, 0},
+ //{IV(''), 0x94, 0},
+ //{IV(''), 0x95, 0},
+ //{IV(''), 0x96, 0},
+ //{IV(''), 0x97, 0},
+ //{IV(''), 0x98, 0},
+ //{IV(''), 0x99, 0},
+ //{IV(''), 0x9A, 0},
+ //{IV(''), 0x9B, 0},
+ //{IV(''), 0x9C, 0},
+ //{IV(''), 0x9D, 0},
+ //{IV(''), 0x9E, 0},
+ //{IV(''), 0x9F, 0},
+
+ {IV('¼'), 0xF0, 0}, // 00BC
+ {IV('⅓'), 0xF1, 0},
+ {IV('½'), 0xF2, 0}, // 00BD
+ {IV('¾'), 0xF3, 0}, // 00BE
+ {IV('¿'), 0xCD, 0}, // 00BF
+
+ {IV('Ё'), 0xA2, 0}, // 0401
+ {IV('А'), 'A', 0}, // 0410
+ {IV('Б'), 0xA0, 0},
+ {IV('В'), 'B', 0},
+ {IV('Г'), 0xA1, 0},
+ {IV('Д'), 0xE0, 0},
+ {IV('Е'), 'E', 0},
+ {IV('Ж'), 0xA3, 0},
+ {IV('З'), 0xA4, 0},
+ {IV('И'), 0xA5, 0},
+ {IV('Й'), 0xA6, 0},
+ {IV('К'), 'K', 0},
+ {IV('Л'), 0xA7, 0},
+ {IV('М'), 'M', 0},
+ {IV('Н'), 'H', 0},
+ {IV('О'), 'O', 0},
+ {IV('П'), 0xA8, 0},
+ {IV('Р'), 'P', 0},
+ {IV('С'), 'C', 0},
+ {IV('Т'), 'T', 0},
+ {IV('У'), 0xA9, 0},
+ {IV('Ф'), 0xAA, 0},
+ {IV('Х'), 'X', 0},
+ {IV('Ц'), 0xE1, 0},
+ {IV('Ч'), 0xAB, 0},
+ {IV('Ш'), 0xAC, 0},
+ {IV('Щ'), 0xE2, 0},
+ {IV('Ъ'), 0xAD, 0},
+ {IV('Ы'), 0xAE, 0},
+ {IV('Ь'), 'b', 0},
+ {IV('Э'), 0xAF, 0},
+ {IV('Ю'), 0xB0, 0},
+ {IV('Я'), 0xB1, 0},
+ {IV('а'), 'a', 0},
+
+ {IV('б'), 0xB2, 0},
+ {IV('в'), 0xB3, 0},
+ {IV('г'), 0xB4, 0},
+ {IV('д'), 0xE3, 0},
+ {IV('е'), 'e', 0},
+ {IV('ж'), 0xB6, 0},
+ {IV('з'), 0xB7, 0},
+ {IV('и'), 0xB8, 0},
+ {IV('й'), 0xB9, 0},
+ {IV('к'), 0xBA, 0},
+ {IV('л'), 0xBB, 0},
+ {IV('м'), 0xBC, 0},
+ {IV('н'), 0xBD, 0},
+ {IV('о'), 'o', 0},
+ {IV('п'), 0xBE, 0},
+ {IV('р'), 'p', 0},
+ {IV('с'), 'c', 0},
+ {IV('т'), 0xBF, 0},
+
+ {IV('у'), 'y', 0},
+ {IV('ф'), 0xE4, 0},
+ {IV('х'), 'x', 0},
+ {IV('ц'), 0xE5, 0},
+ {IV('ч'), 0xC0, 0},
+ {IV('ш'), 0xC1, 0},
+ {IV('щ'), 0xE6, 0},
+ {IV('ъ'), 0xC2, 0},
+ {IV('ы'), 0xC3, 0},
+ {IV('ь'), 0xC4, 0},
+ {IV('э'), 0xC5, 0},
+ {IV('ю'), 0xC6, 0},
+ {IV('я'), 0xC7, 0}, // 044F
+ {IV('ё'), 0xB5, 0}, // 0451
+ //{IV(''), 0xC8, 0},
+ //{IV(''), 0xC9, 0},
+ //{IV(''), 0xCA, 0},
+ //{IV(''), 0xCB, 0},
+ //{IV(''), 0xCC, 0},
+ //{IV(''), 0xCD, 0},
+ //{IV(''), 0xCE, 0},
+
+ //{IV(''), 0xD0, 0},
+ //{IV(''), 0xD1, 0},
+ //{IV(''), 0xD2, 0},
+ //{IV(''), 0xD3, 0},
+ //{IV(''), 0xD4, 0},
+ //{IV(''), 0xD5, 0},
+ //{IV(''), 0xD6, 0},
+ //{IV(''), 0xD7, 0},
+ //{IV(''), 0xD8, 0},
+ //{IV(''), 0xDB, 0},
+ //{IV(''), 0xDC, 0},
+ //{IV(''), 0xDD, 0},
+ //{IV(''), 0xDE, 0},
+ //{IV(''), 0xDF, 0},
+
+ //{IV(''), 0xE7, 0},
+ //{IV(''), 0xE8, 0},
+ //{IV(''), 0xE9, 0},
+ //{IV(''), 0xEA, 0},
+ //{IV(''), 0xEB, 0},
+ //{IV(''), 0xEC, 0},
+ //{IV(''), 0xED, 0},
+ //{IV(''), 0xEE, 0},
+ //{IV(''), 0xEF, 0},
+
+ //{IV(''), 0xF4, 0},
+ //{IV(''), 0xF5, 0},
+ //{IV(''), 0xF6, 0},
+ //{IV(''), 0xF7, 0},
+ //{IV(''), 0xF8, 0},
+ //{IV(''), 0xF9, 0},
+ //{IV(''), 0xFA, 0},
+ //{IV(''), 0xFB, 0},
+ //{IV(''), 0xFC, 0},
+ //{IV(''), 0xFD, 0},
+ //{IV(''), 0xFE, 0},
+ //{IV(''), 0xFF, 0},
+
+ {IV('↑'), 0xD9, 0}, // 2191 ←↑→↓
+ {IV('↓'), 0xDA, 0}, // 2193
+
+ #endif
+
+ #endif
+};
+
+// the plain ASCII replacement for various char
+static const TFTGLCD_charmap_t g_TFTGLCD_charmap_common[] PROGMEM = {
+ {IV('¡'), 'i', 0}, // A1
+ {IV('¢'), 'c', 0}, // A2
+ {IV('°'), 0x09, 0}, // B0 Marlin special: '°' LCD_STR_DEGREE (0x09)
+
+ #ifndef CONVERT_TO_EXT_ASCII //this time CONVERT_TO_EXT_ASCII works only with en, ru and uk languages
+
+ // map WESTERN code to the plain ASCII
+ {IV('Á'), 'A', 0}, // C1
+ {IV('Â'), 'A', 0}, // C2
+ {IV('Ã'), 'A', 0}, // C3
+ {IV('Ä'), 'A', 0}, // C4
+ {IV('Å'), 'A', 0}, // C5
+ {IV('Æ'), 'A', 'E'}, // C6
+ {IV('Ç'), 'C', 0}, // C7
+ {IV('È'), 'E', 0}, // C8
+ {IV('É'), 'E', 0}, // C9
+ {IV('Í'), 'I', 0}, // CD
+ {IV('Ñ'), 'N', 0}, // D1
+ {IV('Õ'), 'O', 0}, // D5
+ {IV('Ö'), 'O', 0}, // D6
+ {IV('×'), 'x', 0}, // D7
+ {IV('Ü'), 'U', 0}, // DC
+ {IV('Ý'), 'Y', 0}, // DD
+ {IV('à'), 'a', 0}, // E0
+ {IV('á'), 'a', 0},
+ {IV('â'), 'a', 0},
+ {IV('ã'), 'a', 0},
+ {IV('ä'), 'a', 0},
+ {IV('å'), 'a', 0},
+ {IV('æ'), 'a', 'e'},
+ {IV('ç'), 'c', 0},
+ {IV('è'), 'e', 0}, // 00E8
+ {IV('é'), 'e', 0},
+ {IV('ê'), 'e', 0},
+ {IV('ë'), 'e', 0},
+ {IV('ì'), 'i', 0}, // 00EC
+ {IV('í'), 'i', 0},
+ {IV('î'), 'i', 0},
+ {IV('ï'), 'i', 0}, // 00EF
+
+ {IV('ñ'), 'n', 0}, // 00F1
+ {IV('ò'), 'o', 0},
+ {IV('ó'), 'o', 0},
+ {IV('ô'), 'o', 0},
+ {IV('õ'), 'o', 0},
+ {IV('ö'), 'o', 0},
+ //{IV('÷'), 0xB8, 0},
+ {IV('ø'), 'o', 0},
+ {IV('ù'), 'u', 0},
+ {IV('ú'), 'u', 0},
+ {IV('û'), 'u', 0},
+ {IV('ü'), 'u', 0}, // FC
+ {IV('ý'), 'y', 0}, // FD
+ {IV('ÿ'), 'y', 0}, // FF
+
+ {IV('Ą'), 'A', 0}, // 0104
+ {IV('ą'), 'a', 0}, // 0105
+ {IV('Ć'), 'C', 0}, // 0106
+ {IV('ć'), 'c', 0}, // 0107
+ {IV('Č'), 'C', 0}, // 010C
+ {IV('č'), 'c', 0}, // 010D
+ {IV('Ď'), 'D', 0}, // 010E
+ {IV('ď'), 'd', 0}, // 010F
+ {IV('đ'), 'd', 0}, // 0111
+ {IV('ę'), 'e', 0}, // 0119
+ {IV('ğ'), 'g', 0}, // 011F
+ {IV('İ'), 'I', 0}, // 0130
+ {IV('ı'), 'i', 0}, // 0131
+
+ {IV('Ł'), 'L', 0}, // 0141
+ {IV('ł'), 'l', 0}, // 0142
+ {IV('Ń'), 'N', 0}, // 0143
+ {IV('ń'), 'n', 0}, // 0144
+ {IV('ň'), 'n', 0}, // 0148
+
+ {IV('ř'), 'r', 0}, // 0159
+ {IV('Ś'), 'S', 0}, // 015A
+ {IV('ś'), 's', 0}, // 015B
+ {IV('ş'), 's', 0}, // 015F
+ {IV('Š'), 'S', 0}, // 0160
+ {IV('š'), 's', 0}, // 0161
+ {IV('ť'), 't', 0}, // 0165
+ {IV('ů'), 'u', 0}, // 016F
+ {IV('ż'), 'z', 0}, // 017C
+ {IV('Ž'), 'Z', 0}, // 017D
+ {IV('ž'), 'z', 0}, // 017E
+ {IV('ƒ'), 'f', 0}, // 0192
+
+ {IV('ˣ'), 'x', 0}, // 02E3
+
+ {IV('΄'), '\'', 0}, // 0384
+ {IV('΅'), '\'', 0}, // 0385
+ {IV('Ά'), 'A', 0}, // 0386
+ {IV('·'), '.', 0}, // 0387
+ {IV('Έ'), 'E', 0}, // 0388
+ {IV('Ή'), 'H', 0}, // 0389
+ {IV('Ί'), 'I', 0}, // 038A
+ {IV('Ό'), 'O', 0}, // 038C
+ {IV('Ύ'), 'Y', 0}, // 038E
+ {IV('Ώ'), 'O', 0}, // 038F
+ {IV('ΐ'), 'i', 0}, // 0390
+ {IV('Α'), 'A', 0}, // 0391
+ {IV('Β'), 'B', 0}, // 0392
+ {IV('Γ'), 'T', 0}, // 0393, Gamma
+ {IV('Δ'), '4', 0}, // 0394, Delta, ◿
+ {IV('Ε'), 'E', 0}, // 0395
+ {IV('Ζ'), 'Z', 0}, // 0396
+ {IV('Η'), 'H', 0}, // 0397
+ {IV('Θ'), '0', 0}, // 0398, Theta
+ {IV('Ι'), 'I', 0}, // 0399
+ {IV('Κ'), 'K', 0}, // 039A
+ {IV('Λ'), '^', 0}, // 039B, Lambda
+ {IV('Μ'), 'M', 0}, // 039C
+ {IV('Ν'), 'N', 0}, // 039D
+ {IV('Ξ'), '3', 0}, // 039E, Xi
+ {IV('Ο'), 'O', 0}, // 039F
+ {IV('Π'), 'n', 0}, // 03A0, Pi
+ {IV('Ρ'), 'P', 0}, // 03A1
+ {IV('Σ'), 'E', 0}, // 03A3, Sigma
+ {IV('Τ'), 'T', 0}, // 03A4
+ {IV('Υ'), 'Y', 0}, // 03A5, Upsilon
+ {IV('Φ'), 'p', 0}, // 03A6, Phi
+ {IV('Χ'), 'X', 0}, // 03A7
+ {IV('Ψ'), 'P', 0}, // 03A8, Psi
+ {IV('Ω'), 'O', 0}, // 03A9, Omega
+ {IV('Ϊ'), 'I', 0}, // 03AA
+ {IV('Ϋ'), 'Y', 0}, // 03AB
+ {IV('ά'), 'a', 0}, // 03AC
+ {IV('έ'), 'e', 0}, // 03AD
+ {IV('ή'), 'n', 0}, // 03AE
+ {IV('ί'), 'i', 0}, // 03AF
+ {IV('ΰ'), 'v', 0}, // 03B0
+ {IV('α'), 'a', 0}, // 03B1, alpha
+ {IV('β'), 'B', 0}, // 03B2, beta
+ {IV('γ'), 'v', 0}, // 03B3, gamma
+ {IV('δ'), 'd', 0}, // 03B4, delta
+ {IV('ε'), 'e', 0}, // 03B5, epsilon
+ {IV('ζ'), 'Z', 0}, // 03B6, zeta
+ {IV('η'), 'n', 0}, // 03B7, eta
+ {IV('θ'), '0', 0}, // 03B8, theta
+ {IV('ι'), 'i', 0}, // 03B9, lota
+ {IV('κ'), 'k', 0}, // 03BA, kappa
+ {IV('λ'), 'L', 0}, // 03BB, lambda
+ {IV('μ'), 'u', 0}, // 03BC, mu
+ {IV('ν'), 'v', 0}, // 03BD, nu
+ {IV('ξ'), 'e', 0}, // 03BE, xi
+ {IV('ο'), 'o', 0}, // 03BF
+ {IV('π'), 'n', 0}, // 03C0, pi
+ {IV('ρ'), 'p', 0}, // 03C1, rho
+ {IV('ς'), 'c', 0}, // 03C2
+ {IV('σ'), 'o', 0}, // 03C3, sigma
+ {IV('τ'), 't', 0}, // 03C4, tau
+ {IV('υ'), 'v', 0}, // 03C5, upsilon
+ {IV('φ'), 'p', 0}, // 03C6
+ {IV('χ'), 'X', 0}, // 03C7, chi
+ {IV('ψ'), 'W', 0}, // 03C8, psi
+ {IV('ω'), 'w', 0}, // 03C9, omega
+ {IV('ϊ'), 'i', 0}, // 03CA
+ {IV('ϋ'), 'v', 0}, // 03CB
+ {IV('ό'), 'o', 0}, // 03CC
+ {IV('ύ'), 'v', 0}, // 03CD
+ {IV('ώ'), 'w', 0}, // 03CE
+
+ // map CYRILLIC code to the plain ASCII
+ {IV('А'), 'A', 0}, // 0410
+ {IV('Б'), 'b', 0}, // 0411
+ {IV('В'), 'B', 0}, // 0412
+ {IV('Г'), 'T', 0}, // 0413
+ {IV('Д'), 'Q', 0}, // 0414
+ {IV('Е'), 'E', 0}, // 0415
+ {IV('Ж'), '*', 0}, // 0416
+ {IV('З'), 'E', 0}, // 0417
+ {IV('И'), 'N', 0}, // 0418
+ {IV('Й'), 'N', 0}, // 0419
+ {IV('К'), 'K', 0}, // 041A
+ {IV('Л'), 'T', 0}, // 041B
+ {IV('М'), 'M', 0}, // 041C
+ {IV('Н'), 'H', 0}, // 041D
+ {IV('О'), 'O', 0}, // 041E
+ {IV('П'), 'n', 0}, // 041F
+ {IV('Р'), 'P', 0}, // 0420
+ {IV('С'), 'C', 0}, // 0421
+ {IV('Т'), 'T', 0}, // 0422
+ {IV('У'), 'Y', 0},
+ {IV('Ф'), 'o', 0},
+ {IV('Х'), 'X', 0},
+ {IV('Ц'), 'U', 0},
+ {IV('Ч'), 'y', 0},
+ {IV('Ш'), 'W', 0},
+ {IV('Щ'), 'W', 0},
+ {IV('Ъ'), 'b', 0},
+ {IV('Ы'), 'b', '|'},
+ {IV('Ь'), 'b'},
+ {IV('Э'), 'e'},
+ {IV('Ю'), '|', 'O'},
+ {IV('Я'), '9', '|'}, // 042F
+
+ {IV('а'), 'a', 0}, // 0430
+ {IV('б'), '6', 0}, // 0431
+ {IV('в'), 'B', 0}, // 0432,
+ {IV('г'), 'r', 0}, // 0433
+ {IV('д'), 'a', 0}, // 0434,
+ {IV('е'), 'e', 0}, // 0435
+ {IV('ж'), '*', 0}, // 0436
+ {IV('з'), 'e', 0}, // 0437,
+ {IV('и'), 'u', 0}, // 0438
+ {IV('й'), 'u', 0}, // 0439,
+ {IV('к'), 'k', 0}, // 043A
+ {IV('л'), 'n', 0},
+ {IV('м'), 'm', 0},
+ {IV('н'), 'H', 0},
+ {IV('о'), 'o', 0},
+ {IV('п'), 'n', 0},
+ {IV('р'), 'p', 0},
+ {IV('с'), 'c', 0},
+ {IV('т'), 't', 0},
+ {IV('у'), 'y', 0},
+ {IV('ф'), 'q', 'p'},
+ {IV('х'), 'x', 0},
+ {IV('ц'), 'u', 0},
+ {IV('ч'), 'y', 0},
+ {IV('ш'), 'w', 0},
+ {IV('щ'), 'w', 0},
+ {IV('ъ'), 'b', 0},
+ {IV('ы'), 'b', '|'},
+ {IV('ь'), 'b', 0},
+ {IV('э'), 'e', 0},
+ {IV('ю'), '|', 'o'},
+ {IV('я'), 'g', 0}, // 044F
+
+ #endif
+
+ {IV('•'), '.', 0}, // 2022 ·
+ {IV('℞'), 'P', 'x'}, // 211E ℞ Pt ASCII 158
+ {IV('™'), 'T', 'M'}, // 2122
+ {IV('←'), '<', '-'}, // 2190
+ {IV('→'), '-', '>'}, // 2192, Marlin special: '⮈⮉⮊⮋➤→⏵➟➠➡' LCD_STR_ARROW_RIGHT (0x03)
+ //{IV('↰'), '<', 0}, // 21B0, Marlin special: '⮥⮭⮉⇧↑↰⤴' LCD_STR_UPLEVEL (0x04)
+ {IV('↰'), 0x03, 0}, // 21B0, Marlin special: '⮥⮭⮉⇧↑↰⤴' LCD_STR_UPLEVEL (0x04)
+ {IV('↻'), 0x04, 0}, // 21BB Marlin special: '↻↺⟳⟲' LCD_STR_REFRESH (0x01)
+ {IV('∼'), '~', 0}, // 223C
+ {IV('≈'), '~', '='}, // 2248
+ {IV('≠'), '!', '='}, // 2260
+ {IV('≡'), '=', 0}, // 2261
+ {IV('≤'), '<', '='},// 2264, ≤≥ ⩽⩾
+ {IV('≥'), '>', '='}, // 2265
+ {IV('⏱'), 0x07, 0}, // 23F1, Marlin special: '🕐🕑🕒🕓🕔🕕🕖🕗🕘🕙🕚🕛🕜🕝🕞🕟🕠🕡🕢🕣🕤🕥🕦🕧 ⌚⌛⏰⏱⏳⧖⧗' LCD_STR_CLOCK (0x05)
+
+ {IV('゠'), '=', 0}, // 30A0
+
+ // ⏰⏱⏲⏳◴◵◶◷
+ // ⏻⏼♁♂
+ //{IV(''), 0x00, 0}, // Marlin special: '' LCD_STR_BEDTEMP (0x07)
+ {IV('🌡'), 0x02, 0}, // D83CDF21 Marlin special: '🌡' LCD_STR_THERMOMETER (0x08)
+ {IV('📂'), 0x05, 0}, // D83DDCC2 Marlin special: '📁📂' LCD_STR_FOLDER (0x02)
+ //{IV(''), 0x06, 0}, // Marlin special: '' LCD_STR_FEEDRATE (0x06)
+};
+
+/* return v1 - v2 */
+static int TFTGLCD_charmap_compare(TFTGLCD_charmap_t * v1, TFTGLCD_charmap_t * v2) {
+ return (v1->uchar < v2->uchar) ? -1 : (v1->uchar > v2->uchar) ? 1 : 0;
+}
+
+static int pf_bsearch_cb_comp_hd4map_pgm(void *userdata, size_t idx, void * data_pin) {
+ TFTGLCD_charmap_t localval;
+ TFTGLCD_charmap_t *p_TFTGLCD_charmap = (TFTGLCD_charmap_t *)userdata;
+ memcpy_P(&localval, p_TFTGLCD_charmap + idx, sizeof(localval));
+ return TFTGLCD_charmap_compare(&localval, (TFTGLCD_charmap_t *)data_pin);
+}
+
+void lcd_moveto(const lcd_uint_t col, const lcd_uint_t row) { lcd.setCursor(col, row); }
+
+void lcd_put_int(const int i) {
+ const char* str = i16tostr3left(i);
+ while (*str) lcd.write(*str++);
+}
+
+// return < 0 on error
+// return the advanced cols
+int lcd_put_wchar_max(wchar_t c, pixel_len_t max_length) {
+
+ // find the HD44780 internal ROM first
+ int ret;
+ size_t idx = 0;
+ TFTGLCD_charmap_t pinval;
+ TFTGLCD_charmap_t *copy_address = nullptr;
+ pinval.uchar = c;
+ pinval.idx = -1;
+
+ if (max_length < 1) return 0;
+
+ if (c < 128) {
+ lcd.write((uint8_t)c);
+ return 1;
+ }
+ copy_address = nullptr;
+ ret = pf_bsearch_r((void *)g_TFTGLCD_charmap_device, COUNT(g_TFTGLCD_charmap_device), pf_bsearch_cb_comp_hd4map_pgm, (void *)&pinval, &idx);
+ if (ret >= 0) {
+ copy_address = (TFTGLCD_charmap_t *)(g_TFTGLCD_charmap_device + idx);
+ }
+ else {
+ ret = pf_bsearch_r((void *)g_TFTGLCD_charmap_common, COUNT(g_TFTGLCD_charmap_common), pf_bsearch_cb_comp_hd4map_pgm, (void *)&pinval, &idx);
+ if (ret >= 0) copy_address = (TFTGLCD_charmap_t *)(g_TFTGLCD_charmap_common + idx);
+ }
+
+ if (ret >= 0) {
+ TFTGLCD_charmap_t localval;
+ // found
+ memcpy_P(&localval, copy_address, sizeof(localval));
+ lcd.write(localval.idx);
+ if (max_length >= 2 && localval.idx2 > 0) {
+ lcd.write(localval.idx2);
+ return 2;
+ }
+ return 1;
+ }
+
+ // Not found, print '?' instead
+ lcd.write((uint8_t)'?');
+ return 1;
+}
+
+/**
+ * @brief Draw a UTF-8 string
+ *
+ * @param utf8_str : the UTF-8 string
+ * @param cb_read_byte : the callback function to read one byte from the utf8_str (from RAM or ROM)
+ * @param max_length : the pixel length of the string allowed (or number of slots in HD44780)
+ *
+ * @return the number of pixels advanced
+ *
+ * Draw a UTF-8 string
+ */
+static int lcd_put_u8str_max_cb(const char * utf8_str, uint8_t (*cb_read_byte)(uint8_t * str), pixel_len_t max_length) {
+ pixel_len_t ret = 0;
+ uint8_t *p = (uint8_t *)utf8_str;
+ while (ret < max_length) {
+ wchar_t ch = 0;
+ p = get_utf8_value_cb(p, cb_read_byte, &ch);
+ if (!ch) break;
+ ret += lcd_put_wchar_max(ch, max_length - ret);
+ }
+ return (int)ret;
+}
+
+int lcd_put_u8str_max(const char * utf8_str, pixel_len_t max_length) {
+ return lcd_put_u8str_max_cb(utf8_str, read_byte_ram, max_length);
+}
+
+int lcd_put_u8str_max_P(PGM_P utf8_str_P, pixel_len_t max_length) {
+ return lcd_put_u8str_max_cb(utf8_str_P, read_byte_rom, max_length);
+}
+
+#if ENABLED(DEBUG_LCDPRINT)
+
+ int test_TFTGLCD_charmap(TFTGLCD_charmap_t *data, size_t size, char *name, char flg_show_contents) {
+ int ret;
+ size_t idx = 0;
+ TFTGLCD_charmap_t preval = {0, 0, 0};
+ TFTGLCD_charmap_t pinval = {0, 0, 0};
+ char flg_error = 0;
+
+ int i;
+
+ TRACE("Test %s\n", name);
+
+ for (i = 0; i < size; i ++) {
+ memcpy_P(&pinval, &(data[i]), sizeof(pinval));
+
+ if (flg_show_contents) {
+ #if 1
+ TRACE("[% 4d] % 6" PRIu32 "(0x%04" PRIX32 ") --> 0x%02X,0x%02X%s\n", i, pinval.uchar, pinval.uchar, (unsigned int)(pinval.idx), (unsigned int)(pinval.idx2), (preval.uchar < pinval.uchar?"":" <--- ERROR"));
+ #else
+ TRACE("[% 4d]", i);
+ TRACE("% 6" PRIu32 "(0x%04" PRIX32 "),", pinval.uchar, pinval.uchar);
+ TRACE("0x%02X,", (unsigned int)(pinval.idx));
+ TRACE("0x%02X,", (unsigned int)(pinval.idx2));
+ TRACE("%s", (preval.uchar < pinval.uchar?"":" <--- ERROR"));
+ #endif
+ }
+ if (preval.uchar >= pinval.uchar) {
+ flg_error = 1;
+ //TRACE("Error: out of order in array %s: idx=%d, val=%d(0x%x)\n", name, i, pinval.uchar, pinval.uchar);
+ //return -1;
+ }
+ memcpy(&preval, &pinval, sizeof(pinval));
+
+ ret = pf_bsearch_r((void *)data, size, pf_bsearch_cb_comp_hd4map_pgm, (void *)&pinval, &idx);
+ if (ret < 0) {
+ flg_error = 1;
+ TRACE("Error: not found item in array %s: idx=%d, val=%d(0x%x)\n", name, i, pinval.uchar, pinval.uchar);
+ //return -1;
+ }
+ if (idx != i) {
+ flg_error = 1;
+ TRACE("Error: wrong index found item in array %s: idx=%d, val=%d(0x%x)\n", name, i, pinval.uchar, pinval.uchar);
+ //return -1;
+ }
+ }
+ if (flg_error) {
+ TRACE("\nError: in array %s\n\n", name);
+ return -1;
+ }
+ TRACE("\nPASS array %s\n\n", name);
+ return 0;
+ }
+
+ int test_TFTGLCD_charmap_all(void) {
+ int flg_error = 0;
+ if (test_TFTGLCD_charmap(g_TFTGLCD_charmap_device, COUNT(g_TFTGLCD_charmap_device), "g_TFTGLCD_charmap_device", 0) < 0) {
+ flg_error = 1;
+ test_TFTGLCD_charmap(g_TFTGLCD_charmap_device, COUNT(g_TFTGLCD_charmap_device), "g_TFTGLCD_charmap_device", 1);
+ }
+ if (test_TFTGLCD_charmap(g_TFTGLCD_charmap_common, COUNT(g_TFTGLCD_charmap_common), "g_TFTGLCD_charmap_common", 0) < 0) {
+ flg_error = 1;
+ test_TFTGLCD_charmap(g_TFTGLCD_charmap_common, COUNT(g_TFTGLCD_charmap_common), "g_TFTGLCD_charmap_common", 1);
+ }
+ if (flg_error) {
+ TRACE("\nFAILED in hd44780 tests!\n");
+ return -1;
+ }
+ TRACE("\nPASS in hd44780 tests.\n");
+ return 0;
+ }
+
+#endif // DEBUG_LCDPRINT
+
+#endif // IS_TFTGLCD_PANEL
diff --git a/Marlin/src/lcd/TFTGLCD/ultralcd_TFTGLCD.cpp b/Marlin/src/lcd/TFTGLCD/ultralcd_TFTGLCD.cpp
new file mode 100644
index 0000000000..c9a5b2525a
--- /dev/null
+++ b/Marlin/src/lcd/TFTGLCD/ultralcd_TFTGLCD.cpp
@@ -0,0 +1,1018 @@
+/**
+ * Marlin 3D Printer Firmware
+ * Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
+ *
+ * Based on Sprinter and grbl.
+ * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ */
+
+#include "../../inc/MarlinConfigPre.h"
+
+#if IS_TFTGLCD_PANEL
+
+/**
+ * ultralcd_TFTGLCD.cpp
+ *
+ * Implementation of the LCD display routines for a TFT GLCD displays with external controller.
+ * This display looks as a REPRAP_DISCOUNT_FULL_GRAPHIC_SMART_CONTROLLER but has good text font
+ * and supports color output.
+ */
+
+#if NONE(__AVR__, MCU_LPC1768, __STM32F1__, STM32F4xx)
+ #warning "Selected platform not yet tested. Please contribute your good pin mappings."
+#endif
+
+#if ENABLED(TFTGLCD_PANEL_SPI)
+ #include
+#else
+ #include
+#endif
+
+#include "ultralcd_TFTGLCD.h"
+#include "../ultralcd.h"
+#include "../../libs/numtostr.h"
+
+#include "../../sd/cardreader.h"
+#include "../../module/temperature.h"
+#include "../../module/printcounter.h"
+#include "../../module/planner.h"
+#include "../../module/motion.h"
+
+#if DISABLED(LCD_PROGRESS_BAR) && BOTH(FILAMENT_LCD_DISPLAY, SDSUPPORT)
+ #include "../../feature/filwidth.h"
+ #include "../../gcode/parser.h"
+#endif
+
+#if ENABLED(AUTO_BED_LEVELING_UBL)
+ #include "../../feature/bedlevel/bedlevel.h"
+#endif
+
+TFTGLCD lcd;
+
+#define ICON_LOGO B00000001
+#define ICON_TEMP1 B00000010 //hotend 1
+#define ICON_TEMP2 B00000100 //hotend 2
+#define ICON_TEMP3 B00001000 //hotend 3
+#define ICON_BED B00010000
+#define ICON_FAN B00100000
+#define ICON_HOT B01000000 //when any T > 50deg
+#define PIC_MASK 0x7f
+
+//LEDs not used, for compatibility with Smoothieware
+#define LED_HOTEND_ON B00000001
+#define LED_BED_ON B00000010
+#define LED_FAN_ON B00000100
+#define LED_HOT B00001000
+#define LED_MASK 0x0f
+
+#define FBSIZE (LCD_WIDTH * LCD_HEIGHT + 2)
+
+//markers for change line color
+#define COLOR_EDIT '#'
+#define COLOR_ERROR '!'
+
+#ifdef CONVERT_TO_EXT_ASCII //use standart pseudographic symbols in ASCII table
+ #define LR 179 //vertical line
+ #define TRC 191 //top right corner
+ #define BLC 192 //bottom left corner
+ #define GL 196 //horizontal line
+ #define BRC 217 //bottom right corner, should be replaced to 12 for some languages
+ #define TLC 218 //top left corner, should be replaced to 13 for some languages
+#else //next symbols must be present in panel font
+ #define LR 8 //equal to 179
+ #define TRC 9 //equal to 191
+ #define BLC 10 //equal to 192
+ #define GL 11 //equal to 196
+ #define BRC 12 //equal to 217
+ #define TLC 13 //equal to 218
+#endif
+
+#define Marlin 0x01
+
+enum Commands { // based on Smoothieware commands
+ GET_SPI_DATA = 0,
+ READ_BUTTONS, // read buttons
+ READ_ENCODER, // read encoder
+ LCD_WRITE, // write all screen to LCD
+ BUZZER, // beep buzzer
+ CONTRAST, // set contrast (brightnes)
+ // Other commands... 0xE0 thru 0xFF
+ GET_LCD_ROW = 0xE0, // for detect panel
+ GET_LCD_COL, // reserved for compatibility with Smoothieware, not used
+ LCD_PUT, // write one line to LCD
+ INIT_SCREEN = 0xFE, // clear panel buffer
+};
+
+static unsigned char framebuffer[FBSIZE];
+static unsigned char *fb;
+static uint8_t cour_line;
+static uint8_t picBits, ledBits, hotBits;
+static uint8_t PanelDetected = 0;
+
+// Constructor
+TFTGLCD::TFTGLCD() {}
+
+//clearing local buffer
+void TFTGLCD::clear_buffer() {
+ memset(&framebuffer[0], ' ', FBSIZE - 2);
+ framebuffer[FBSIZE - 1] = framebuffer[FBSIZE - 2] = 0;
+ picBits = ledBits = 0;
+}
+
+//set new text cursor position
+void TFTGLCD::setCursor(uint8_t col, uint8_t row) {
+ fb = &framebuffer[0] + col + row * LCD_WIDTH;
+ cour_line = row;
+}
+
+//send char to buffer
+void TFTGLCD::write(char c) {
+ *fb++ = c;
+}
+
+//send text line to buffer
+void TFTGLCD::print(const char *line) {
+ while (*line) *fb++ = *line++;
+}
+
+// For menu
+void TFTGLCD::print_line() {
+ if (!PanelDetected) return;
+ #if ENABLED(TFTGLCD_PANEL_SPI)
+ WRITE(TFTGLCD_CS, LOW);
+ #ifdef __AVR__
+ SPI.transfer(LCD_PUT);
+ SPI.transfer(cour_line);
+ SPI.transfer(&framebuffer[cour_line * LCD_WIDTH], LCD_WIDTH);
+ #elif EITHER(MCU_LPC1768, __STM32F1__)
+ SPI.transfer(LCD_PUT);
+ SPI.transfer(cour_line);
+ for (uint16_t i = 0; i < LCD_WIDTH; i++) SPI.transfer(framebuffer[cour_line * LCD_WIDTH + i]);
+ #elif defined(STM32F4xx)
+ SPI.transfer(LCD_PUT, SPI_CONTINUE);
+ SPI.transfer(cour_line, SPI_CONTINUE);
+ SPI.transfer(&framebuffer[cour_line * LCD_WIDTH], LCD_WIDTH, SPI_CONTINUE);
+ #elif ANY(ARDUINO_ARCH_SAM, __SAMD51__, __MK20DX256__, __MK64FX512__)
+ SPI.transfer(LCD_PUT);
+ SPI.transfer(cour_line);
+ SPI.transfer(&framebuffer[cour_line * LCD_WIDTH], LCD_WIDTH);
+ #elif defined(ARDUINO_ARCH_ESP32)
+ SPI.write(LCD_PUT);
+ SPI.write(cour_line);
+ for (uint16_t i = 0; i < LCD_WIDTH; i++) SPI.write(framebuffer[cour_line * LCD_WIDTH + i]);
+ #endif
+ WRITE(TFTGLCD_CS, HIGH);
+ #else
+ Wire.beginTransmission((uint8_t)LCD_I2C_ADDRESS); //set I2C device address
+ Wire.write(LCD_PUT);
+ Wire.write(cour_line);
+ Wire.write(&framebuffer[cour_line * LCD_WIDTH], LCD_WIDTH); //transfer 1 line to txBuffer
+ Wire.endTransmission(); //transmit data
+ safe_delay(1);
+ #endif
+}
+
+void TFTGLCD::print_screen(){
+ if (!PanelDetected) return;
+ framebuffer[FBSIZE - 2] = picBits & PIC_MASK;
+ framebuffer[FBSIZE - 1] = ledBits;
+ #if ENABLED(TFTGLCD_PANEL_SPI)
+ // Send all framebuffer to panel
+ WRITE(TFTGLCD_CS, LOW);
+ #ifdef __AVR__
+ SPI.transfer(LCD_WRITE);
+ SPI.transfer(&framebuffer[0], FBSIZE);
+ #elif EITHER(MCU_LPC1768, __STM32F1__)
+ SPI.transfer(LCD_WRITE);
+ for (uint16_t i = 0; i < FBSIZE; i++) SPI.transfer(framebuffer[i]);
+ #elif defined(STM32F4xx)
+ SPI.transfer(LCD_WRITE, SPI_CONTINUE);
+ SPI.transfer(&framebuffer[0], FBSIZE, SPI_CONTINUE);
+ #elif ANY(ARDUINO_ARCH_SAM, __SAMD51__, __MK20DX256__, __MK64FX512__)
+ SPI.transfer(LCD_WRITE);
+ SPI.transfer(&framebuffer[0], FBSIZE);
+ #elif defined(ARDUINO_ARCH_ESP32)
+ SPI.write(LCD_WRITE);
+ for (uint16_t i = 0; i < FBSIZE; i++) SPI.write(framebuffer[i]);
+ #endif
+ WRITE(TFTGLCD_CS, HIGH);
+ #else
+ uint8_t r;
+ // Send framebuffer to panel by line
+ Wire.beginTransmission((uint8_t)LCD_I2C_ADDRESS);
+ // First line
+ Wire.write(LCD_WRITE);
+ Wire.write(&framebuffer[0], LCD_WIDTH);
+ Wire.endTransmission();
+ for (r = 1; r < (LCD_HEIGHT - 1); r++) {
+ Wire.beginTransmission((uint8_t)LCD_I2C_ADDRESS);
+ Wire.write(&framebuffer[r * LCD_WIDTH], LCD_WIDTH);
+ Wire.endTransmission();
+ }
+ // Last line
+ Wire.beginTransmission((uint8_t)LCD_I2C_ADDRESS);
+ Wire.write(&framebuffer[r * LCD_WIDTH], LCD_WIDTH);
+ Wire.write(&framebuffer[FBSIZE - 2], 2);
+ Wire.endTransmission();
+ #endif
+}
+
+void TFTGLCD::setContrast(uint16_t contrast) {
+ if (!PanelDetected) return;
+ #if ENABLED(TFTGLCD_PANEL_SPI)
+ WRITE(TFTGLCD_CS, LOW);
+ #if ANY(__AVR__, MCU_LPC1768, __STM32F1__)
+ SPI.transfer(CONTRAST);
+ SPI.transfer((uint8_t)contrast);
+ #elif defined(STM32F4xx)
+ SPI.transfer(CONTRAST, SPI_CONTINUE);
+ SPI.transfer((uint8_t)contrast, SPI_CONTINUE);
+ #elif ANY(ARDUINO_ARCH_SAM, __SAMD51__, __MK20DX256__, __MK64FX512__)
+ SPI.transfer(CONTRAST);
+ SPI.transfer((uint8_t)contrast);
+ #elif defined(ARDUINO_ARCH_ESP32)
+ SPI.write(CONTRAST);
+ SPI.write((uint8_t)contrast);
+ #endif
+ WRITE(TFTGLCD_CS, HIGH);
+ #else
+ Wire.beginTransmission((uint8_t)LCD_I2C_ADDRESS);
+ Wire.write(CONTRAST);
+ Wire.write((uint8_t)contrast);
+ Wire.endTransmission();
+ #endif
+}
+
+//reading buttons and encoder states
+extern volatile int8_t encoderDiff;
+
+uint8_t MarlinUI::read_slow_buttons(void) {
+ if (!PanelDetected) return 0;
+ #if ENABLED(TFTGLCD_PANEL_SPI)
+ uint8_t b = 0;
+ WRITE(TFTGLCD_CS, LOW);
+ #if ANY(__AVR__, MCU_LPC1768, __STM32F1__)
+ SPI.transfer(READ_ENCODER);
+ WRITE(TFTGLCD_CS, LOW); //for delay
+ encoderDiff += SPI.transfer(READ_BUTTONS);
+ WRITE(TFTGLCD_CS, LOW); //for delay
+ b = SPI.transfer(GET_SPI_DATA);
+ #elif defined(STM32F4xx)
+ SPI.transfer(READ_ENCODER, SPI_CONTINUE);
+ encoderDiff += SPI.transfer(READ_BUTTONS, SPI_CONTINUE);
+ b = SPI.transfer(GET_SPI_DATA, SPI_CONTINUE);
+ #elif ANY(ARDUINO_ARCH_SAM, __SAMD51__, __MK20DX256__, __MK64FX512__)
+ SPI.transfer(READ_ENCODER);
+ WRITE(TFTGLCD_CS, LOW); //for delay ????
+ encoderDiff += SPI.transfer(READ_BUTTONS);
+ WRITE(TFTGLCD_CS, LOW); //for delay ????
+ b = SPI.transfer(GET_SPI_DATA);
+ #elif defined(ARDUINO_ARCH_ESP32)
+ SPI.transfer(READ_ENCODER);
+ WRITE(TFTGLCD_CS, LOW); //for delay ????
+ encoderDiff += SPI.transfer(READ_BUTTONS);
+ WRITE(TFTGLCD_CS, LOW); //for delay ????
+ b = SPI.transfer(GET_SPI_DATA);
+ #endif
+ WRITE(TFTGLCD_CS, HIGH);
+ return b;
+ #else
+ Wire.beginTransmission((uint8_t)LCD_I2C_ADDRESS);
+ Wire.write(READ_ENCODER);
+ Wire.endTransmission();
+ #ifdef __AVR__
+ Wire.requestFrom((uint8_t)LCD_I2C_ADDRESS, 2, 0, 0, 1);
+ #elif defined(__STM32F1__)
+ Wire.requestFrom((uint8_t)LCD_I2C_ADDRESS, (uint8_t)2);
+ #elif EITHER(STM32F4xx, MCU_LPC1768)
+ Wire.requestFrom(LCD_I2C_ADDRESS, 2);
+ #endif
+ encoderDiff += Wire.read();
+ return Wire.read(); //buttons
+ #endif
+}
+
+// duration in ms, freq in Hz
+void MarlinUI::buzz(const long duration, const uint16_t freq) {
+ if (!PanelDetected) return;
+ #if ENABLED(TFTGLCD_PANEL_SPI)
+ WRITE(TFTGLCD_CS, LOW);
+ #if ANY(__AVR__, MCU_LPC1768, __STM32F1__)
+ SPI.transfer(BUZZER);
+ SPI.transfer16((uint16_t)duration);
+ SPI.transfer16(freq);
+ #elif defined(STM32F4xx)
+ SPI.transfer(BUZZER, SPI_CONTINUE);
+ SPI.transfer16((uint16_t)duration, SPI_CONTINUE);
+ SPI.transfer16(freq, SPI_CONTINUE);
+ #elif ANY(ARDUINO_ARCH_SAM, __SAMD51__, __MK20DX256__, __MK64FX512__)
+ SPI.transfer(BUZZER);
+ SPI.transfer16((uint16_t)duration);
+ SPI.transfer16(freq);
+ #elif defined(ARDUINO_ARCH_ESP32)
+ SPI.write(BUZZER);
+ SPI.write16((uint16_t)duration);
+ SPI.write16(freq);
+ #endif
+ WRITE(TFTGLCD_CS, HIGH);
+ #else
+ Wire.beginTransmission((uint8_t)LCD_I2C_ADDRESS);
+ Wire.write(BUZZER);
+ Wire.write((uint8_t)(duration >> 8));
+ Wire.write((uint8_t)duration);
+ Wire.write((uint8_t)(freq >> 8));
+ Wire.write((uint8_t)freq);
+ Wire.endTransmission();
+ #endif
+}
+
+void MarlinUI::init_lcd() {
+ uint8_t t;
+ lcd.clear_buffer();
+ t = 0;
+ #if ENABLED(TFTGLCD_PANEL_SPI)
+ // SPI speed must be less 10MHz
+ OUT_WRITE(TFTGLCD_CS, HIGH);
+ spiInit(TERN(__STM32F1__, SPI_QUARTER_SPEED, SPI_FULL_SPEED));
+ WRITE(TFTGLCD_CS, LOW);
+ #if ANY(__AVR__, MCU_LPC1768, __STM32F1__)
+ SPI.transfer(GET_LCD_ROW);
+ t = SPI.transfer(GET_SPI_DATA);
+ #elif defined(STM32F4xx)
+ SPI.transfer(GET_LCD_ROW, SPI_CONTINUE);
+ t = SPI.transfer(GET_SPI_DATA, SPI_CONTINUE);
+ #elif ANY(ARDUINO_ARCH_SAM, __SAMD51__, __MK20DX256__, __MK64FX512__)
+ SPI.transfer(GET_LCD_ROW);
+ t = SPI.transfer(GET_SPI_DATA);
+ #elif defined(ARDUINO_ARCH_ESP32)
+ SPI.write(GET_LCD_ROW);
+ t = SPI.transfer(GET_SPI_DATA);
+ #endif
+ #else
+ #ifdef MCU_LPC1768
+ Wire.begin(); //init twi/I2C
+ #else
+ Wire.begin((uint8_t)LCD_I2C_ADDRESS); //init twi/I2C
+ #endif
+ Wire.beginTransmission((uint8_t)LCD_I2C_ADDRESS);
+ Wire.write((uint8_t)GET_LCD_ROW); // put command to buffer
+ Wire.endTransmission(); // send buffer
+ #ifdef __AVR__
+ Wire.requestFrom((uint8_t)LCD_I2C_ADDRESS, 1, 0, 0, 1);
+ #elif ANY(__STM32F1__, STM32F4xx, MCU_LPC1768)
+ Wire.requestFrom(LCD_I2C_ADDRESS, 1);
+ #endif
+ t = (uint8_t)Wire.read();
+ #endif
+
+ if (t == LCD_HEIGHT) {
+ PanelDetected = 1;
+ #if ENABLED(TFTGLCD_PANEL_SPI)
+ PanelDetected = 1;
+ #if ANY(__AVR__, MCU_LPC1768, __STM32F1__)
+ SPI.transfer(INIT_SCREEN);
+ SPI.transfer(Marlin);
+ #elif defined(STM32F4xx)
+ SPI.transfer(INIT_SCREEN, SPI_CONTINUE);
+ SPI.transfer(Marlin, SPI_CONTINUE);
+ #elif ANY(ARDUINO_ARCH_SAM, __SAMD51__, __MK20DX256__, __MK64FX512__)
+ SPI.transfer(INIT_SCREEN);
+ SPI.transfer(Marlin);
+ #elif defined(ARDUINO_ARCH_ESP32)
+ SPI.write(INIT_SCREEN);
+ SPI.write(Marlin);
+ #endif
+ WRITE(TFTGLCD_CS, HIGH);
+ #else
+ Wire.beginTransmission((uint8_t)LCD_I2C_ADDRESS);
+ Wire.write((uint8_t)INIT_SCREEN);
+ Wire.write(Marlin);
+ Wire.endTransmission();
+ #endif
+ }
+ else
+ PanelDetected = 0;
+ safe_delay(100);
+}
+
+bool MarlinUI::detected() {
+ return PanelDetected;
+}
+
+void MarlinUI::clear_lcd() {
+ if (!PanelDetected) return;
+ lcd.clear_buffer();
+ lcd.print_screen();
+}
+
+int16_t MarlinUI::contrast; // Initialized by settings.load()
+
+void MarlinUI::set_contrast(const int16_t value) {
+ contrast = constrain(value, LCD_CONTRAST_MIN, LCD_CONTRAST_MAX);
+ lcd.setContrast(contrast);
+}
+
+static void center_text_P(PGM_P pstart, uint8_t y) {
+ uint8_t len = utf8_strlen_P(pstart);
+ if (len < LCD_WIDTH)
+ lcd.setCursor((LCD_WIDTH - len) / 2, y);
+ else
+ lcd.setCursor(0, y);
+ lcd_put_u8str_P(pstart);
+}
+
+#if ENABLED(SHOW_BOOTSCREEN)
+
+ void MarlinUI::show_bootscreen() {
+ if (!PanelDetected) return;
+ //
+ // Show the Marlin logo, splash line1, and splash line 2
+ //
+ uint8_t indent = (LCD_WIDTH - 8) / 2;
+ // symbols 217 (bottom right corner) and 218 (top left corner) are using for letters in some languages
+ // and they should be moved to begining ASCII table as spetial symbols
+ lcd.setCursor(indent, 0); lcd.write(TLC); lcd_put_u8str_P(PSTR("------")); lcd.write(TRC);
+ lcd.setCursor(indent, 1); lcd.write(LR); lcd_put_u8str_P(PSTR("Marlin")); lcd.write(LR);
+ lcd.setCursor(indent, 2); lcd.write(BLC); lcd_put_u8str_P(PSTR("------")); lcd.write(BRC);
+ center_text_P(PSTR(SHORT_BUILD_VERSION), 3);
+ center_text_P(PSTR(MARLIN_WEBSITE_URL), 4);
+ picBits = ICON_LOGO;
+ lcd.print_screen();
+ safe_delay(1500);
+ }
+
+#endif // SHOW_BOOTSCREEN
+
+void MarlinUI::draw_kill_screen() {
+ if (!PanelDetected) return;
+ lcd.clear_buffer();
+ lcd.setCursor(0, 3); lcd.write(COLOR_ERROR);
+ lcd.setCursor((LCD_WIDTH - utf8_strlen(status_message)) / 2 + 1, 3);
+ lcd_put_u8str(status_message);
+ center_text_P(GET_TEXT(MSG_HALTED), 5);
+ center_text_P(GET_TEXT(MSG_PLEASE_RESET), 6);
+ lcd.print_screen();
+}
+
+//
+// Before homing, blink '123' <-> '???'.
+// Homed but unknown... '123' <-> ' '.
+// Homed and known, display constantly.
+//
+FORCE_INLINE void _draw_axis_value(const AxisEnum axis, const char *value, const bool blink) {
+ lcd.write('X' + uint8_t(axis));
+ if (blink)
+ lcd.print(value);
+ else {
+ if (!TEST(axis_homed, axis))
+ while (const char c = *value++) lcd.write(c <= '.' ? c : '?');
+ else {
+ #if NONE(HOME_AFTER_DEACTIVATE, DISABLE_REDUCED_ACCURACY_WARNING)
+ if (!TEST(axis_known_position, axis))
+ lcd_put_u8str_P(axis == Z_AXIS ? PSTR(" ") : PSTR(" "));
+ else
+ #endif
+ lcd_put_u8str(value);
+ }
+ }
+}
+
+FORCE_INLINE void _draw_heater_status(const heater_id_t heater_id, const char *prefix, const bool blink) {
+ uint8_t pic_hot_bits;
+ #if HAS_HEATED_BED
+ const bool isBed = heater_id < 0;
+ const float t1 = (isBed ? thermalManager.degBed() : thermalManager.degHotend(heater_id));
+ const float t2 = (isBed ? thermalManager.degTargetBed() : thermalManager.degTargetHotend(heater_id));
+ #else
+ const float t1 = thermalManager.degHotend(heater_id);
+ const float t2 = thermalManager.degTargetHotend(heater_id);
+ #endif
+
+ #if HOTENDS < 2
+ if (heater_id == H_E0) {
+ lcd.setCursor(2, 5); lcd.print(prefix); //HE
+ lcd.setCursor(1, 6); lcd.print(i16tostr3rj(t1 + 0.5));
+ lcd.setCursor(1, 7);
+ }
+ else {
+ lcd.setCursor(6, 5); lcd.print(prefix); //BED
+ lcd.setCursor(6, 6); lcd.print(i16tostr3rj(t1 + 0.5));
+ lcd.setCursor(6, 7);
+ }
+ #else
+ if (heater_id > H_BED) {
+ lcd.setCursor(heater_id * 4, 5); lcd.print(prefix); //HE1 or HE2 or HE3
+ lcd.setCursor(heater_id * 4, 6); lcd.print(i16tostr3rj(t1 + 0.5));
+ lcd.setCursor(heater_id * 4, 7);
+ }
+ else {
+ lcd.setCursor(13, 5); lcd.print(prefix); //BED
+ lcd.setCursor(13, 6); lcd.print(i16tostr3rj(t1 + 0.5));
+ lcd.setCursor(13, 7);
+ }
+ #endif // HOTENDS <= 1
+
+ #if !HEATER_IDLE_HANDLER
+ UNUSED(blink);
+ #else
+ if (!blink && thermalManager.heater_idle[thermalManager.idle_index_for_id(heater_id)].timed_out) {
+ lcd.write(' ');
+ if (t2 >= 10) lcd.write(' ');
+ if (t2 >= 100) lcd.write(' ');
+ }
+ else
+ #endif // !HEATER_IDLE_HANDLER
+ lcd.print(i16tostr3rj(t2 + 0.5));
+
+ switch (heater_id) {
+ case H_BED: pic_hot_bits = ICON_BED; break;
+ case H_E0: pic_hot_bits = ICON_TEMP1; break;
+ case H_E1: pic_hot_bits = ICON_TEMP2; break;
+ case H_E2: pic_hot_bits = ICON_TEMP3;
+ default: break;
+ }
+
+ if (t2) picBits |= pic_hot_bits;
+ else picBits &= ~pic_hot_bits;
+
+ if (t1 > 50) hotBits |= pic_hot_bits;
+ else hotBits &= ~pic_hot_bits;
+
+ if (hotBits) picBits |= ICON_HOT;
+ else picBits &= ~ICON_HOT;
+}
+
+#if HAS_PRINT_PROGRESS
+
+ FORCE_INLINE void _draw_print_progress() {
+ if (!PanelDetected) return;
+ const uint8_t progress = ui._get_progress();
+ #if ENABLED(SDSUPPORT)
+ lcd_put_u8str_P(PSTR("SD"));
+ #elif ENABLED(LCD_SET_PROGRESS_MANUALLY)
+ lcd_put_u8str_P(PSTR("P:"));
+ #endif
+ if (progress)
+ lcd.print(ui8tostr3rj(progress));
+ else
+ lcd_put_u8str_P(PSTR("---"));
+ lcd.write('%');
+ }
+
+#endif // HAS_PRINT_PROGRESS
+
+#if ENABLED(LCD_PROGRESS_BAR)
+
+ void MarlinUI::draw_progress_bar(const uint8_t percent) {
+ if (!PanelDetected) return;
+ if (fb == &framebuffer[0] + LCD_WIDTH * 2) { // For status screen
+ lcd.write('%'); lcd.write(percent);
+ }
+ else { // For progress bar test
+ lcd.setCursor(LCD_WIDTH / 2 - 2, LCD_HEIGHT / 2 - 2);
+ lcd.print(i16tostr3rj(percent)); lcd.write('%');
+ lcd.print_line();
+ lcd.setCursor(0, LCD_HEIGHT / 2 - 1);
+ lcd.write('%'); lcd.write(percent);
+ lcd.print_line();
+ }
+ }
+
+#endif
+
+void MarlinUI::draw_status_message(const bool blink) {
+ if (!PanelDetected) return;
+ lcd.setCursor(0, 3);
+ #if BOTH(FILAMENT_LCD_DISPLAY, SDSUPPORT)
+
+ // Alternate Status message and Filament display
+ if (ELAPSED(millis(), next_filament_display)) {
+ lcd_put_u8str_P(PSTR("Dia "));
+ lcd.print(ftostr12ns(filament_width_meas));
+ lcd_put_u8str_P(PSTR(" V"));
+ lcd.print(i16tostr3rj(100.0 * (
+ parser.volumetric_enabled
+ ? planner.volumetric_area_nominal / planner.volumetric_multiplier[FILAMENT_SENSOR_EXTRUDER_NUM]
+ : planner.volumetric_multiplier[FILAMENT_SENSOR_EXTRUDER_NUM]
+ )
+ ));
+ lcd.write('%');
+ return;
+ }
+
+ #endif // FILAMENT_LCD_DISPLAY && SDSUPPORT
+
+ // Get the UTF8 character count of the string
+ uint8_t slen = utf8_strlen(status_message);
+
+ #if ENABLED(STATUS_MESSAGE_SCROLLING)
+
+ static bool last_blink = false;
+
+ // If the string fits into the LCD, just print it and do not scroll it
+ if (slen <= LCD_WIDTH) {
+
+ // The string isn't scrolling and may not fill the screen
+ lcd_put_u8str(status_message);
+
+ // Fill the rest with spaces
+ while (slen < LCD_WIDTH) { lcd.write(' '); ++slen; }
+ }
+ else {
+ // String is larger than the available space in screen.
+
+ // Get a pointer to the next valid UTF8 character
+ // and the string remaining length
+ uint8_t rlen;
+ const char *stat = status_and_len(rlen);
+ lcd_put_u8str_max(stat, LCD_WIDTH); // The string leaves space
+
+ // If the remaining string doesn't completely fill the screen
+ if (rlen < LCD_WIDTH) {
+ lcd.write('.'); // Always at 1+ spaces left, draw a dot
+ uint8_t chars = LCD_WIDTH - rlen; // Amount of space left in characters
+ if (--chars) { // Draw a second dot if there's space
+ lcd.write('.');
+ if (--chars)
+ lcd_put_u8str_max(status_message, chars); // Print a second copy of the message
+ }
+ }
+ if (last_blink != blink) {
+ last_blink = blink;
+ advance_status_scroll();
+ }
+ }
+
+ #else
+
+ UNUSED(blink);
+
+ // Just print the string to the LCD
+ lcd_put_u8str_max(status_message, LCD_WIDTH);
+
+ // Fill the rest with spaces if there are missing spaces
+ while (slen < LCD_WIDTH) {
+ lcd.write(' ');
+ ++slen;
+ }
+
+ #endif
+}
+
+/**
+Possible status screens:
+
+Equal to 20x10 text LCD
+
+|X 000 Y 000 Z 000.00|
+|FR100% SD100% C--:--|
+| Progress bar line |
+|Status message |
+| |
+| HE BED FAN |
+| ttc ttc % | ttc - current temperature
+| tts tts %%% | tts - setted temperature, %%% - percent for FAN
+| ICO ICO ICO ICO | ICO - icon 48x48, placed in 2 text lines
+| ICO ICO ICO ICO | ICO /
+
+or
+
+|X 000 Y 000 Z 000.00|
+|FR100% SD100% C--:--|
+| Progress bar line |
+|Status message |
+| |
+|HE1 HE2 HE3 BED ICO|
+|ttc ttc ttc ttc ICO|
+|tts tts tts tts %%%|
+|ICO ICO ICO ICO ICO|
+|ICO ICO ICO ICO ICO|
+
+or
+
+Equal to 24x10 text LCD
+
+|X 000 Y 000 Z 000.00 |
+|FR100% SD100% C--:--|
+| Progress bar line |
+|Status message |
+| |
+|HE1 HE2 HE3 BED FAN |
+|ttc ttc ttc ttc % |
+|tts tts tts tts %%% |
+|ICO ICO ICO ICO ICO ICO|
+|ICO ICO ICO ICO ICO ICO|
+*/
+
+void MarlinUI::draw_status_screen() {
+ if (!PanelDetected) return;
+
+ const bool blink = get_blink();
+
+ lcd.clear_buffer();
+
+ //
+ // Line 1 - XYZ coordinates
+ //
+
+ lcd.setCursor(0, 0);
+ _draw_axis_value(X_AXIS, ftostr4sign(LOGICAL_X_POSITION(current_position[X_AXIS])), blink); lcd.write(' ');
+ _draw_axis_value(Y_AXIS, ftostr4sign(LOGICAL_Y_POSITION(current_position[Y_AXIS])), blink); lcd.write(' ');
+ _draw_axis_value(Z_AXIS, ftostr52sp(LOGICAL_Z_POSITION(current_position[Z_AXIS])), blink);
+
+ #if HAS_LEVELING && !HAS_HEATED_BED
+ lcd.write(planner.leveling_active || blink ? '_' : ' ');
+ #endif
+
+ //
+ // Line 2 - feedrate, , time
+ //
+
+ lcd.setCursor(0, 1);
+ lcd_put_u8str_P(PSTR("FR")); lcd.print(i16tostr3rj(feedrate_percentage)); lcd.write('%');
+
+ #if BOTH(SDSUPPORT, HAS_PRINT_PROGRESS)
+ lcd.setCursor(LCD_WIDTH / 2 - 3, 1);
+ _draw_print_progress();
+ #endif
+
+ char buffer[10];
+ duration_t elapsed = print_job_timer.duration();
+ uint8_t len = elapsed.toDigital(buffer);
+
+ lcd.setCursor((LCD_WIDTH - 1) - len, 1);
+ lcd.write(0x07); lcd.print(buffer); // LCD_CLOCK_CHAR
+
+ //
+ // Line 3 - progressbar
+ //
+
+ lcd.setCursor(0, 2);
+ #if ENABLED(LCD_PROGRESS_BAR)
+ draw_progress_bar(_get_progress());
+ #else
+ lcd.write('%'); lcd.write(0);
+ #endif
+
+ //
+ // Line 4 - Status Message (which may be a Filament display)
+ //
+
+ draw_status_message(blink);
+
+ //
+ // Line 5
+ //
+
+ #if HOTENDS <= 1 || (HOTENDS <= 2 && !HAS_HEATED_BED)
+ #if DUAL_MIXING_EXTRUDER
+ lcd.setCursor(0, 4);
+ // Two-component mix / gradient instead of XY
+ char mixer_messages[12];
+ const char *mix_label;
+ #if ENABLED(GRADIENT_MIX)
+ if (mixer.gradient.enabled) {
+ mixer.update_mix_from_gradient();
+ mix_label = "Gr";
+ }
+ else
+ #endif
+ {
+ mixer.update_mix_from_vtool();
+ mix_label = "Mx";
+ }
+ sprintf_P(mixer_messages, PSTR("%s %d;%d%% "), mix_label, int(mixer.mix[0]), int(mixer.mix[1]));
+ lcd_put_u8str(mixer_messages);
+ #endif
+ #endif
+
+ //
+ // Line 6..8 Temperatures, FAN
+ //
+
+ #if HOTENDS < 2
+ _draw_heater_status(H_E0, "HE", blink); // Hotend Temperature
+ #else
+ _draw_heater_status(H_E0, "HE1", blink); // Hotend 1 Temperature
+ _draw_heater_status(H_E1, "HE2", blink); // Hotend 2 Temperature
+ #if HOTENDS > 2
+ _draw_heater_status(H_E2, "HE3", blink); // Hotend 3 Temperature
+ #endif
+ #endif // HOTENDS <= 1
+
+ #if HAS_HEATED_BED
+ #if HAS_LEVELING
+ _draw_heater_status(H_BED, (planner.leveling_active && blink ? "___" : "BED"), blink);
+ #else
+ _draw_heater_status(H_BED, "BED", blink);
+ #endif
+ #endif // HAS_HEATED_BED
+
+ #if FAN_COUNT > 0
+ uint16_t spd = thermalManager.fan_speed[0];
+
+ #if ENABLED(ADAPTIVE_FAN_SLOWING)
+ if (!blink) spd = thermalManager.scaledFanSpeed(0, spd);
+ #endif
+
+ uint16_t per = thermalManager.fanPercent(spd);
+ #if HOTENDS < 2
+ #define FANX 11
+ #else
+ #define FANX 17
+ #endif
+ lcd.setCursor(FANX, 5); lcd_put_u8str_P(PSTR("FAN"));
+ lcd.setCursor(FANX + 1, 6); lcd.write('%');
+ lcd.setCursor(FANX, 7);
+ lcd.print(i16tostr3rj(per));
+
+ if (TERN0(HAS_FAN0, thermalManager.fan_speed[0]) || TERN0(HAS_FAN1, thermalManager.fan_speed[1]) || TERN0(HAS_FAN2, thermalManager.fan_speed[2]))
+ picBits |= ICON_FAN;
+ else
+ picBits &= ~ICON_FAN;
+
+ #endif // FAN_COUNT > 0
+
+ //
+ // Line 9, 10 - icons
+ //
+ lcd.print_screen();
+}
+
+#if HAS_LCD_MENU
+
+ #include "../menu/menu.h"
+
+ #if ENABLED(ADVANCED_PAUSE_FEATURE)
+
+ void MarlinUI::draw_hotend_status(const uint8_t row, const uint8_t extruder) {
+ if (!PanelDetected) return;
+ lcd.setCursor((LCD_WIDTH - 14) / 2, row + 1);
+ lcd.write(0x02); lcd_put_u8str_P(" E"); lcd.write('1' + extruder); lcd.write(' ');
+ lcd.print(i16tostr3rj(thermalManager.degHotend(extruder))); lcd.write(0x01); lcd.write('/');
+ lcd.print(i16tostr3rj(thermalManager.degTargetHotend(extruder))); lcd.write(0x01);
+ lcd.print_line();
+ }
+
+ #endif // ADVANCED_PAUSE_FEATURE
+
+ // Draw a static item with no left-right margin required. Centered by default.
+ void MenuItem_static::draw(const uint8_t row, PGM_P const pstr, const uint8_t style/*=SS_DEFAULT*/, const char * const valstr/*=nullptr*/) {
+ if (!PanelDetected) return;
+ uint8_t n = LCD_WIDTH;
+ lcd.setCursor(0, row);
+ if ((style & SS_CENTER) && !valstr) {
+ int8_t pad = (LCD_WIDTH - utf8_strlen_P(pstr)) / 2;
+ while (--pad >= 0) { lcd.write(' '); n--; }
+ }
+ n = lcd_put_u8str_ind_P(pstr, itemIndex, itemString, n);
+ if (valstr) n -= lcd_put_u8str_max(valstr, n);
+ for (; n; --n) lcd.write(' ');
+ lcd.print_line();
+ }
+
+ // Draw a generic menu item with pre_char (if selected) and post_char
+ void MenuItemBase::_draw(const bool sel, const uint8_t row, PGM_P const pstr, const char pre_char, const char post_char) {
+ if (!PanelDetected) return;
+ lcd.setCursor(0, row);
+ lcd.write(sel ? pre_char : ' ');
+ uint8_t n = lcd_put_u8str_ind_P(pstr, itemIndex, itemString, LCD_WIDTH - 2);
+ for (; n; --n) lcd.write(' ');
+ lcd.write(post_char);
+ lcd.print_line();
+ }
+
+ // Draw a menu item with a (potentially) editable value
+ void MenuEditItemBase::draw(const bool sel, const uint8_t row, PGM_P const pstr, const char* const data, const bool pgm) {
+ if (!PanelDetected) return;
+ const uint8_t vlen = data ? (pgm ? utf8_strlen_P(data) : utf8_strlen(data)) : 0;
+ lcd.setCursor(0, row);
+ lcd.write(sel ? LCD_STR_ARROW_RIGHT[0] : ' ');
+ uint8_t n = lcd_put_u8str_ind_P(pstr, itemIndex, itemString, LCD_WIDTH - 2 - vlen);
+ if (vlen) {
+ lcd.write(':');
+ for (; n; --n) lcd.write(' ');
+ if (pgm) lcd_put_u8str_P(data); else lcd_put_u8str(data);
+ }
+ lcd.print_line();
+ }
+
+ // Low-level draw_edit_screen can be used to draw an edit screen from anyplace
+ void MenuEditItemBase::draw_edit_screen(PGM_P const pstr, const char* const value/*=nullptr*/) {
+ if (!PanelDetected) return;
+ ui.encoder_direction_normal();
+ lcd.setCursor(0, LCD_HEIGHT - 1); //last line is free most time
+ lcd.write(COLOR_EDIT);
+ lcd_put_u8str_P(pstr);
+ if (value != nullptr) {
+ lcd.write(':');
+ lcd.setCursor((LCD_WIDTH - 1) - (utf8_strlen(value) + 1), LCD_HEIGHT - 1); // Right-justified, padded by spaces
+ lcd.write(' '); // Overwrite char if value gets shorter
+ lcd.print(value);
+ lcd.write(' ');
+ lcd.print_line();
+ }
+ }
+
+ // The Select Screen presents a prompt and two "buttons"
+ void MenuItem_confirm::draw_select_screen(PGM_P const yes, PGM_P const no, const bool yesno, PGM_P const pref, const char * const string, PGM_P const suff) {
+ if (!PanelDetected) return;
+ ui.draw_select_screen_prompt(pref, string, suff);
+ lcd.setCursor(0, LCD_HEIGHT - 1);
+ lcd.write(COLOR_EDIT);
+ lcd.write(yesno ? ' ' : '['); lcd_put_u8str_P(no); lcd.write(yesno ? ' ' : ']');
+ lcd.setCursor(LCD_WIDTH - utf8_strlen_P(yes) - 3, LCD_HEIGHT - 1);
+ lcd.write(yesno ? '[' : ' '); lcd_put_u8str_P(yes); lcd.write(yesno ? ']' : ' ');
+ lcd.print_line();
+ }
+
+ #if ENABLED(SDSUPPORT)
+
+ void MenuItem_sdbase::draw(const bool sel, const uint8_t row, PGM_P const, CardReader &theCard, const bool isDir) {
+ if (!PanelDetected) return;
+ lcd.setCursor(0, row);
+ lcd.write(sel ? LCD_STR_ARROW_RIGHT[0] : ' ');
+ constexpr uint8_t maxlen = LCD_WIDTH - 2;
+ uint8_t n = maxlen - lcd_put_u8str_max(ui.scrolled_filename(theCard, maxlen, row, sel), maxlen);
+ for (; n; --n) lcd.write(' ');
+ lcd.write(isDir ? LCD_STR_FOLDER[0] : ' ');
+ lcd.print_line();
+ }
+
+ #endif // SDSUPPORT
+
+ #if ENABLED(LCD_HAS_STATUS_INDICATORS)
+
+ void MarlinUI::update_indicators() {}
+
+ #endif // LCD_HAS_STATUS_INDICATORS
+
+ #if ENABLED(AUTO_BED_LEVELING_UBL)
+ /**
+ * Map screen:
+ * |/---------\ (00,00) |
+ * || . . . . | X:000.00|
+ * || . . . . | Y:000.00|
+ * || . . . . | Z:00.000|
+ * || . . . . | |
+ * || . . . . | |
+ * || . . . . | |
+ * |+---------/ |
+ * | |
+ * |____________________|
+ */
+ void MarlinUI::ubl_plot(const uint8_t x_plot, const uint8_t y_plot) {
+ if (!PanelDetected) return;
+
+ #define _LCD_W_POS 12
+
+ lcd.clear_buffer();
+
+ //print only top left corner. All frame with grid points will be printed by panel
+ lcd.setCursor(0, 0);
+ *fb++ = TLC; //top left corner - marker for plot parameters
+ *fb = (GRID_MAX_POINTS_X << 4) + GRID_MAX_POINTS_Y; //set mesh size
+
+ // Print plot position
+ lcd.setCursor(_LCD_W_POS, 0);
+ *fb++ = '('; lcd.print(i16tostr3left(x_plot));
+ *fb++ = ','; lcd.print(i16tostr3left(y_plot)); *fb = ')';
+
+ // Show all values
+ lcd.setCursor(_LCD_W_POS, 1); lcd_put_u8str_P(PSTR("X:"));
+ lcd.print(ftostr52(LOGICAL_X_POSITION(pgm_read_float(&ubl._mesh_index_to_xpos[x_plot]))));
+ lcd.setCursor(_LCD_W_POS, 2); lcd_put_u8str_P(PSTR("Y:"));
+ lcd.print(ftostr52(LOGICAL_Y_POSITION(pgm_read_float(&ubl._mesh_index_to_ypos[y_plot]))));
+
+ // Show the location value
+ lcd.setCursor(_LCD_W_POS, 3); lcd_put_u8str_P(PSTR("Z:"));
+
+ if (!isnan(ubl.z_values[x_plot][y_plot]))
+ lcd.print(ftostr43sign(ubl.z_values[x_plot][y_plot]));
+ else
+ lcd_put_u8str_P(PSTR(" -----"));
+
+ center_text_P(GET_TEXT(MSG_UBL_FINE_TUNE_MESH), 8);
+
+ lcd.print_screen();
+ }
+
+ #endif // AUTO_BED_LEVELING_UBL
+
+#endif // HAS_LCD_MENU
+
+#endif // IS_TFTGLCD_PANEL
diff --git a/Marlin/src/lcd/TFTGLCD/ultralcd_TFTGLCD.h b/Marlin/src/lcd/TFTGLCD/ultralcd_TFTGLCD.h
new file mode 100644
index 0000000000..9f54730c3c
--- /dev/null
+++ b/Marlin/src/lcd/TFTGLCD/ultralcd_TFTGLCD.h
@@ -0,0 +1,74 @@
+/**
+ * Marlin 3D Printer Firmware
+ * Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
+ *
+ * Based on Sprinter and grbl.
+ * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ */
+#pragma once
+
+/**
+ * Implementation of the LCD display routines for a TFT GLCD displays with external controller.
+ */
+
+#include "../../inc/MarlinConfig.h"
+
+#if IS_TFTGLCD_PANEL
+
+#include "../../libs/duration_t.h"
+
+////////////////////////////////////
+// Set up button and encode mappings for each panel (into 'buttons' variable)
+//
+// This is just to map common functions (across different panels) onto the same
+// macro name. The mapping is independent of whether the button is directly connected or
+// via a shift/i2c register.
+
+////////////////////////////////////
+// Create LCD class instance and chipset-specific information
+class TFTGLCD {
+ private:
+ public:
+ TFTGLCD();
+ void clear_buffer();
+ void setCursor(uint8_t col, uint8_t row);
+ void write(char c);
+ void print(const char *line);
+ void print_line();
+ void print_screen();
+ void redraw_screen();
+ void setContrast(uint16_t contrast);
+};
+
+extern TFTGLCD lcd;
+
+#include "../fontutils.h"
+#include "../lcdprint.h"
+
+// Use panel encoder - free old encoder pins
+#undef BTN_EN1
+#undef BTN_EN2
+#undef BTN_ENC
+#define BTN_EN1 -1
+#define BTN_EN2 -1
+#define BTN_ENC -1
+
+#ifndef EN_C
+ #define EN_C 4 //for click
+#endif
+
+#endif // IS_TFTGLCD_PANEL
diff --git a/Marlin/src/lcd/menu/menu_ubl.cpp b/Marlin/src/lcd/menu/menu_ubl.cpp
index d034de0952..2cd300958f 100644
--- a/Marlin/src/lcd/menu/menu_ubl.cpp
+++ b/Marlin/src/lcd/menu/menu_ubl.cpp
@@ -59,10 +59,14 @@ inline float rounded_mesh_value() {
static void _lcd_mesh_fine_tune(PGM_P const msg) {
ui.defer_status_screen();
if (ubl.encoder_diff) {
- mesh_edit_accumulator += ubl.encoder_diff > 0 ? 0.005f : -0.005f;
+ mesh_edit_accumulator += TERN(IS_TFTGLCD_PANEL,
+ ubl.encoder_diff * 0.005f / ENCODER_PULSES_PER_STEP,
+ ubl.encoder_diff > 0 ? 0.005f : -0.005f
+ );
ubl.encoder_diff = 0;
- ui.refresh(LCDVIEW_CALL_REDRAW_NEXT);
+ TERN(IS_TFTGLCD_PANEL,,ui.refresh(LCDVIEW_CALL_REDRAW_NEXT));
}
+ TERN_(IS_TFTGLCD_PANEL, ui.refresh(LCDVIEW_CALL_REDRAW_NEXT));
if (ui.should_draw()) {
const float rounded_f = rounded_mesh_value();
diff --git a/Marlin/src/lcd/ultralcd.cpp b/Marlin/src/lcd/ultralcd.cpp
index 89dc0adaf3..de534e4023 100644
--- a/Marlin/src/lcd/ultralcd.cpp
+++ b/Marlin/src/lcd/ultralcd.cpp
@@ -506,7 +506,7 @@ bool MarlinUI::get_blink() {
* This is very display-dependent, so the lcd implementation draws this.
*/
-#if ENABLED(LCD_PROGRESS_BAR)
+#if ENABLED(LCD_PROGRESS_BAR) && !IS_TFTGLCD_PANEL
millis_t MarlinUI::progress_bar_ms; // = 0
#if PROGRESS_MSG_EXPIRE > 0
millis_t MarlinUI::expire_status_ms; // = 0
@@ -517,7 +517,7 @@ void MarlinUI::status_screen() {
TERN_(HAS_LCD_MENU, ENCODER_RATE_MULTIPLY(false));
- #if ENABLED(LCD_PROGRESS_BAR)
+ #if ENABLED(LCD_PROGRESS_BAR) && !IS_TFTGLCD_PANEL
//
// HD44780 implements the following message blinking and
@@ -915,7 +915,7 @@ void MarlinUI::update() {
const bool encoderPastThreshold = (abs_diff >= epps);
if (encoderPastThreshold || lcd_clicked) {
- if (encoderPastThreshold) {
+ if (encoderPastThreshold && TERN1(IS_TFTGLCD_PANEL, !external_control)) {
#if BOTH(HAS_LCD_MENU, ENCODER_RATE_MULTIPLIER)
@@ -1260,6 +1260,12 @@ void MarlinUI::update() {
TERN(REPRAPWORLD_KEYPAD, keypad_buttons, buttons) = ~val;
#endif
+ #if IS_TFTGLCD_PANEL
+ next_button_update_ms = now + (LCD_UPDATE_INTERVAL / 2);
+ buttons = slow_buttons;
+ TERN_(AUTO_BED_LEVELING_UBL, external_encoder());
+ #endif
+
} // next_button_update_ms
#if HAS_ENCODER_WHEEL
@@ -1331,7 +1337,7 @@ void MarlinUI::update() {
const millis_t ms = millis();
#endif
- #if ENABLED(LCD_PROGRESS_BAR)
+ #if ENABLED(LCD_PROGRESS_BAR) && !IS_TFTGLCD_PANEL
progress_bar_ms = ms;
#if PROGRESS_MSG_EXPIRE > 0
expire_status_ms = persist ? 0 : ms + PROGRESS_MSG_EXPIRE;
diff --git a/Marlin/src/lcd/ultralcd.h b/Marlin/src/lcd/ultralcd.h
index c23dc5d84e..c7ef41596d 100644
--- a/Marlin/src/lcd/ultralcd.h
+++ b/Marlin/src/lcd/ultralcd.h
@@ -34,7 +34,7 @@
#if EITHER(HAS_LCD_MENU, ULTIPANEL_FEEDMULTIPLY)
#define HAS_ENCODER_ACTION 1
#endif
-#if (!HAS_ADC_BUTTONS && ENABLED(NEWPANEL)) || BUTTONS_EXIST(EN1, EN2)
+#if ((!HAS_ADC_BUTTONS && ENABLED(NEWPANEL)) || BUTTONS_EXIST(EN1, EN2)) && !IS_TFTGLCD_PANEL
#define HAS_ENCODER_WHEEL 1
#endif
#if HAS_ENCODER_WHEEL || ANY_BUTTON(ENC, BACK, UP, DWN, LFT, RT)
@@ -45,7 +45,7 @@
#endif
// I2C buttons must be read in the main thread
-#if EITHER(LCD_I2C_VIKI, LCD_I2C_PANELOLU2)
+#if ANY(LCD_I2C_VIKI, LCD_I2C_PANELOLU2, IS_TFTGLCD_PANEL)
#define HAS_SLOW_BUTTONS 1
#endif
@@ -215,7 +215,7 @@
#endif
-#if BUTTON_EXISTS(BACK) || HAS_TOUCH_XPT2046
+#if BUTTON_EXISTS(BACK) || EITHER(HAS_TOUCH_XPT2046, IS_TFTGLCD_PANEL)
#define BLEN_D 3
#define EN_D _BV(BLEN_D)
#define LCD_BACK_CLICKED() (buttons & EN_D)
diff --git a/Marlin/src/pins/lpc1768/pins_BTT_SKR_V1_1.h b/Marlin/src/pins/lpc1768/pins_BTT_SKR_V1_1.h
index 4165bb3503..0701e45992 100644
--- a/Marlin/src/pins/lpc1768/pins_BTT_SKR_V1_1.h
+++ b/Marlin/src/pins/lpc1768/pins_BTT_SKR_V1_1.h
@@ -65,7 +65,16 @@
* by redrawing the screen after SD card accesses.
*/
-#if HAS_WIRED_LCD
+#if IS_TFTGLCD_PANEL
+
+ #if ENABLED(TFTGLCD_PANEL_SPI)
+ #define TFTGLCD_CS P3_26
+ #endif
+
+ #define SD_DETECT_PIN P1_31
+
+#elif HAS_WIRED_LCD
+
#define BTN_EN1 P3_26
#define BTN_EN2 P3_25
#define BTN_ENC P2_11
@@ -80,7 +89,8 @@
#define DOGLCD_CS P2_06
#define DOGLCD_A0 P0_16
#endif
-#endif
+
+#endif // HAS_WIRED_LCD
//
// SD Support
@@ -89,7 +99,7 @@
// requires jumpers on the SKR V1.1 board as documented here:
// https://www.facebook.com/groups/505736576548648/permalink/630639874058317/
#ifndef SDCARD_CONNECTION
- #if EITHER(MKS_MINI_12864, ENDER2_STOCKDISPLAY)
+ #if ANY(MKS_MINI_12864, ENDER2_STOCKDISPLAY, IS_TFTGLCD_PANEL)
#define SDCARD_CONNECTION LCD
#else
#define SDCARD_CONNECTION ONBOARD
diff --git a/Marlin/src/pins/lpc1768/pins_BTT_SKR_V1_3.h b/Marlin/src/pins/lpc1768/pins_BTT_SKR_V1_3.h
index e8983c7f54..31373fedff 100644
--- a/Marlin/src/pins/lpc1768/pins_BTT_SKR_V1_3.h
+++ b/Marlin/src/pins/lpc1768/pins_BTT_SKR_V1_3.h
@@ -265,6 +265,14 @@
#error "ADC BUTTONS do not work unmodifed on SKR 1.3, The ADC ports cannot take more than 3.3v."
+ #elif IS_TFTGLCD_PANEL
+
+ #if ENABLED(TFTGLCD_PANEL_SPI)
+ #define TFTGLCD_CS EXPA2_08_PIN
+ #endif
+
+ #define SD_DETECT_PIN EXPA2_04_PIN
+
#else // !CR10_STOCKDISPLAY
#define LCD_PINS_RS EXPA1_07_PIN
diff --git a/Marlin/src/pins/lpc1768/pins_BTT_SKR_V1_4.h b/Marlin/src/pins/lpc1768/pins_BTT_SKR_V1_4.h
index 3bdd3061b7..6f9f4bcf63 100644
--- a/Marlin/src/pins/lpc1768/pins_BTT_SKR_V1_4.h
+++ b/Marlin/src/pins/lpc1768/pins_BTT_SKR_V1_4.h
@@ -309,6 +309,14 @@
#define XPT2046_Y_OFFSET -285
#endif
+ #elif IS_TFTGLCD_PANEL
+
+ #if ENABLED(TFTGLCD_PANEL_SPI)
+ #define TFTGLCD_CS P3_26
+ #endif
+
+ #define SD_DETECT_PIN P1_31
+
#else
#define BTN_ENC P0_28 // (58) open-drain
diff --git a/Marlin/src/pins/lpc1768/pins_MKS_SBASE.h b/Marlin/src/pins/lpc1768/pins_MKS_SBASE.h
index 1cb6774a41..1290200e55 100644
--- a/Marlin/src/pins/lpc1768/pins_MKS_SBASE.h
+++ b/Marlin/src/pins/lpc1768/pins_MKS_SBASE.h
@@ -217,7 +217,18 @@
* that the garbage/lines are erased immediately after the SD card accesses are completed.
*/
-#if HAS_WIRED_LCD
+#if IS_TFTGLCD_PANEL
+
+ #if ENABLED(TFTGLCD_PANEL_SPI)
+ #define TFTGLCD_CS P3_25 // EXP2.3
+ #endif
+
+ #if SD_CONNECTION_IS(LCD)
+ #define SD_DETECT_PIN P0_28 // EXP2.4
+ #endif
+
+#elif HAS_WIRED_LCD
+
#define BEEPER_PIN P1_31 // EXP1.1
#define BTN_ENC P1_30 // EXP1.2
#define BTN_EN1 P3_26 // EXP2.5
@@ -273,7 +284,7 @@
//#define LCD_SCREEN_ROT_270
#endif
-#endif
+#endif // HAS_WIRED_LCD
/**
* Example for trinamic drivers using the J8 connector on MKs Sbase.
diff --git a/Marlin/src/pins/lpc1768/pins_MKS_SGEN_L.h b/Marlin/src/pins/lpc1768/pins_MKS_SGEN_L.h
index 02fdaafd30..d269ecbdc9 100644
--- a/Marlin/src/pins/lpc1768/pins_MKS_SGEN_L.h
+++ b/Marlin/src/pins/lpc1768/pins_MKS_SGEN_L.h
@@ -249,6 +249,15 @@
#define LCD_PINS_ENABLE P1_22
#define LCD_PINS_D4 P0_17
+ #elif IS_TFTGLCD_PANEL
+
+ #undef BEEPER_PIN
+ #undef BTN_ENC
+
+ #if ENABLED(TFTGLCD_PANEL_SPI)
+ #define TFTGLCD_CS P3_25
+ #endif
+
#else
#define BTN_EN1 P3_25
diff --git a/Marlin/src/pins/lpc1768/pins_RAMPS_RE_ARM.h b/Marlin/src/pins/lpc1768/pins_RAMPS_RE_ARM.h
index 097f8be22a..41763fb33c 100644
--- a/Marlin/src/pins/lpc1768/pins_RAMPS_RE_ARM.h
+++ b/Marlin/src/pins/lpc1768/pins_RAMPS_RE_ARM.h
@@ -326,6 +326,15 @@
#define LCD_PINS_ENABLE P0_18 // J3-10 & AUX-3 (SID, MOSI)
#define LCD_PINS_D4 P2_06 // J3-8 & AUX-3 (SCK, CLK)
+#elif IS_TFTGLCD_PANEL
+
+ #if ENABLED(TFTGLCD_PANEL_SPI)
+ #define TFTGLCD_CS P3_26 // (31) J3-2 & AUX-4
+ #endif
+
+ #define SD_DETECT_PIN P1_31 // (49) J3-1 & AUX-3 (NOT 5V tolerant)
+ #define KILL_PIN P1_22 // (41) J5-4 & AUX-4
+
#elif HAS_WIRED_LCD
//#define SCK_PIN P0_15 // (52) system defined J3-9 & AUX-3
diff --git a/Marlin/src/pins/lpc1769/pins_MKS_SGEN_L_V2.h b/Marlin/src/pins/lpc1769/pins_MKS_SGEN_L_V2.h
index a073a6fe30..cbbe8a9416 100644
--- a/Marlin/src/pins/lpc1769/pins_MKS_SGEN_L_V2.h
+++ b/Marlin/src/pins/lpc1769/pins_MKS_SGEN_L_V2.h
@@ -241,7 +241,16 @@
* ----- -----
* EXP1 EXP2
*/
-#if HAS_WIRED_LCD
+#if IS_TFTGLCD_PANEL
+
+ #if ENABLED(TFTGLCD_PANEL_SPI)
+ #define TFTGLCD_CS P3_25
+ #endif
+
+ #define SD_DETECT_PIN P0_27
+
+#elif HAS_WIRED_LCD
+
#define BEEPER_PIN P1_31
#define BTN_ENC P1_30
diff --git a/Marlin/src/pins/lpc1769/pins_SMOOTHIEBOARD.h b/Marlin/src/pins/lpc1769/pins_SMOOTHIEBOARD.h
index 33975df8ee..f2811b14ab 100644
--- a/Marlin/src/pins/lpc1769/pins_SMOOTHIEBOARD.h
+++ b/Marlin/src/pins/lpc1769/pins_SMOOTHIEBOARD.h
@@ -111,16 +111,33 @@
#elif HAS_WIRED_LCD
- /*
- The Smoothieboard supports the REPRAP_DISCOUNT_FULL_GRAPHIC_SMART_CONTROLLER with either
- a custom cable with breakouts to the pins indicated below or the RRD GLCD Adapter board
- found at http://smoothieware.org/rrdglcdadapter
-
- Other links to information about setting up a display panel with Smoothieboard
- http://chibidibidiwah.wdfiles.com/local--files/panel/smoothieboard2sd.jpg
- http://smoothieware.org/panel
- */
-
+ /**
+ * SD Support
+ *
+ * For the RRD GLCD it CANNOT share the same SPI as the LCD so it must be
+ * hooked up to the onboard SDCard SPI and use a spare pin for the SDCS.
+ * Also note that an external SDCard sharing the SPI port with the
+ * onboard/internal SDCard must be ejected before rebooting as the bootloader
+ * does not like the external card. NOTE Smoothie will not boot if the external
+ * sdcard is inserted in the RRD LCD sdcard slot at boot time, it must be
+ * inserted after it has booted.
+ */
+ #define SD_DETECT_PIN P0_27 // EXP2 Pin 7 (SD_CD, SD_DET)
+
+ #define MISO_PIN P0_08 // EXP2 Pin 1 (PB3, SD_MISO)
+ #define SCK_PIN P0_07 // EXP2 Pin 2 (SD_SCK)
+ #define SS_PIN P0_28 // EXP2 Pin 4 (SD_CSEL, SD_CS)
+ #define MOSI_PIN P0_09 // EXP2 Pin 6 (PB2, SD_MOSI)
+
+ /**
+ * The Smoothieboard supports the REPRAP_DISCOUNT_FULL_GRAPHIC_SMART_CONTROLLER with either
+ * a custom cable with breakouts to the pins indicated below or the RRD GLCD Adapter board
+ * found at http://smoothieware.org/rrdglcdadapter
+ *
+ * Other links to information about setting up a display panel with Smoothieboard
+ * http://chibidibidiwah.wdfiles.com/local--files/panel/smoothieboard2sd.jpg
+ * http://smoothieware.org/panel
+ */
#if ENABLED(REPRAP_DISCOUNT_FULL_GRAPHIC_SMART_CONTROLLER)
// EXP1 Pins
#define BEEPER_PIN P1_31 // EXP1 Pin 1
@@ -132,24 +149,14 @@
#define BTN_EN2 P3_26 // EXP2 Pin 3
#define BTN_EN1 P3_25 // EXP2 Pin 5
- /*
- SD Support
-
- For the RRD GLCD it CANNOT share the same SPI as the LCD so it must be
- hooked up to the onboard SDCard SPI and use a spare pin for the SDCS.
- Also note that an external SDCard sharing the SPI port with the
- onboard/internal SDCard must be ejected before rebooting as the bootloader
- does not like the external card. NOTE Smoothie will not boot if the external
- sdcard is inserted in the RRD LCD sdcard slot at boot time, it must be
- inserted after it has booted.
- */
-
- #define MISO_PIN P0_08 // EXP2 Pin 1 (PB3, SD_MISO)
- #define SCK_PIN P0_07 // EXP2 Pin 2 (SD_SCK)
- #define SS_PIN P0_28 // EXP2 Pin 4 (SD_CSEL, SD_CS)
- #define MOSI_PIN P0_09 // EXP2 Pin 6 (PB2, SD_MOSI)
+ #elif IS_TFTGLCD_PANEL
+
#define SD_DETECT_PIN P0_27 // EXP2 Pin 7 (SD_CD, SD_DET)
+ #if ENABLED(TFTGLCD_PANEL_SPI)
+ #define TFTGLCD_CS P3_26 // EXP2 Pin 3
+ #endif
+
#else
#error "Marlin's Smoothieboard support cannot drive your LCD."
#endif
diff --git a/Marlin/src/pins/pinsDebug_list.h b/Marlin/src/pins/pinsDebug_list.h
index ac15a3c9ba..704f2a487f 100644
--- a/Marlin/src/pins/pinsDebug_list.h
+++ b/Marlin/src/pins/pinsDebug_list.h
@@ -261,6 +261,9 @@
#if defined(TMC_SW_SCK) && TMC_SW_SCK >= 0
REPORT_NAME_DIGITAL(__LINE__, TMC_SW_SCK)
#endif
+#if defined(TFTGLCD_CS) && TFTGLCD_CS >= 0
+ REPORT_NAME_DIGITAL(__LINE__, TFTGLCD_CS)
+#endif
#if PIN_EXISTS(E_MUX0)
REPORT_NAME_DIGITAL(__LINE__, E_MUX0_PIN)
#endif
diff --git a/Marlin/src/pins/ramps/pins_RAMPS.h b/Marlin/src/pins/ramps/pins_RAMPS.h
index aa73aabbc1..de56d2b59d 100644
--- a/Marlin/src/pins/ramps/pins_RAMPS.h
+++ b/Marlin/src/pins/ramps/pins_RAMPS.h
@@ -450,6 +450,10 @@
#define LCD_PINS_D6 44
#define LCD_PINS_D7 64
+ #elif ENABLED(TFTGLCD_PANEL_SPI)
+
+ #define TFTGLCD_CS 33
+
#else
#if ENABLED(CR10_STOCKDISPLAY)
@@ -682,6 +686,10 @@
// Pins only defined for RAMPS_SMART currently
+ #elif IS_TFTGLCD_PANEL
+
+ #define SD_DETECT_PIN 49
+
#else
// Beeper on AUX-4
diff --git a/Marlin/src/pins/stm32f1/pins_BTT_SKR_E3_DIP.h b/Marlin/src/pins/stm32f1/pins_BTT_SKR_E3_DIP.h
index fd741d296d..2bc94c45c5 100644
--- a/Marlin/src/pins/stm32f1/pins_BTT_SKR_E3_DIP.h
+++ b/Marlin/src/pins/stm32f1/pins_BTT_SKR_E3_DIP.h
@@ -279,9 +279,7 @@
#if SD_CONNECTION_IS(ONBOARD)
#define SD_DETECT_PIN PC4
-#endif
-
-#if BOTH(TOUCH_UI_FTDI_EVE, LCD_FYSETC_TFT81050) && SD_CONNECTION_IS(LCD)
+#elif SD_CONNECTION_IS(LCD) && BOTH(TOUCH_UI_FTDI_EVE, LCD_FYSETC_TFT81050)
#define SD_DETECT_PIN PA15
#define SS_PIN PA10
#elif SD_CONNECTION_IS(CUSTOM_CABLE)
diff --git a/Marlin/src/pins/stm32f1/pins_BTT_SKR_MINI_E3_common.h b/Marlin/src/pins/stm32f1/pins_BTT_SKR_MINI_E3_common.h
index 30d057d900..09e47875ed 100644
--- a/Marlin/src/pins/stm32f1/pins_BTT_SKR_MINI_E3_common.h
+++ b/Marlin/src/pins/stm32f1/pins_BTT_SKR_MINI_E3_common.h
@@ -169,8 +169,48 @@
#define FORCE_SOFT_SPI
#define LCD_BACKLIGHT_PIN -1
+ #elif IS_TFTGLCD_PANEL
+
+ #if ENABLED(TFTGLCD_PANEL_SPI)
+
+ #error "CAUTION! TFTGLCD_PANEL_SPI requires wiring modifications. See 'pins_BTT_SKR_MINI_E3_common.h' for details. Comment out this line to continue."
+
+ /**
+ * TFTGLCD_PANEL_SPI display pinout
+ *
+ * Board Display
+ * _____ _____
+ * 5V | 1 2 | GND (SPI1-MISO) MISO | 1 2 | SCK (SPI1-SCK)
+ * (FREE) PB7 | 3 4 | PB8 (LCD_CS) (PA9) GLCD_CS | 3 4 | SD_CS (PA10)
+ * (FREE) PB9 | 5 6 | PA10 (SD_CS) (FREE) | 5 6 | MOSI (SPI1-MOSI)
+ * RESET | 7 8 | PA9 (MOD_RESET) (PB5) SD_DET | 7 8 | (FREE)
+ * (BEEPER) PB6 | 9 10| PB5 (SD_DET) GND | 9 10| 5V
+ * ----- -----
+ * EXP1 EXP1
+ *
+ * Needs custom cable:
+ *
+ * Board Adapter Display
+ * _________
+ * EXP1-1 ----------- EXP1-10
+ * EXP1-2 ----------- EXP1-9
+ * SPI1-4 ----------- EXP1-6
+ * EXP1-4 ----------- FREE
+ * SPI1-3 ----------- EXP1-2
+ * EXP1-6 ----------- EXP1-4
+ * EXP1-7 ----------- FREE
+ * EXP1-8 ----------- EXP1-3
+ * SPI1-1 ----------- EXP1-1
+ * EXP1-10 ----------- EXP1-7
+ *
+ */
+
+ #define TFTGLCD_CS PA9
+
+ #endif
+
#else
- #error "Only CR10_STOCKDISPLAY, ZONESTAR_LCD, ENDER2_STOCKDISPLAY, and MKS_MINI_12864 are currently supported on the BIGTREE_SKR_MINI_E3."
+ #error "Only CR10_STOCKDISPLAY, ZONESTAR_LCD, ENDER2_STOCKDISPLAY, MKS_MINI_12864, and TFTGLCD_PANEL_(SPI|I2C) are currently supported on the BIGTREE_SKR_MINI_E3."
#endif
#endif // HAS_WIRED_LCD
@@ -227,9 +267,7 @@
#if SD_CONNECTION_IS(ONBOARD)
#define SD_DETECT_PIN PC4
-#endif
-
-#if BOTH(TOUCH_UI_FTDI_EVE, LCD_FYSETC_TFT81050) && SD_CONNECTION_IS(LCD)
+#elif SD_CONNECTION_IS(LCD) && (BOTH(TOUCH_UI_FTDI_EVE, LCD_FYSETC_TFT81050) || IS_TFTGLCD_PANEL)
#define SD_DETECT_PIN PB5
#define SS_PIN PA10
#elif SD_CONNECTION_IS(CUSTOM_CABLE)
diff --git a/Marlin/src/pins/stm32f1/pins_BTT_SKR_MINI_V1_1.h b/Marlin/src/pins/stm32f1/pins_BTT_SKR_MINI_V1_1.h
index 3397c34f33..47fff4467c 100644
--- a/Marlin/src/pins/stm32f1/pins_BTT_SKR_MINI_V1_1.h
+++ b/Marlin/src/pins/stm32f1/pins_BTT_SKR_MINI_V1_1.h
@@ -123,6 +123,17 @@
#define LCD_PINS_ENABLE PC14
#define LCD_PINS_D4 PB7
+ #elif IS_TFTGLCD_PANEL
+
+ #undef BEEPER_PIN
+ #undef BTN_ENC
+
+ #if ENABLED(TFTGLCD_PANEL_SPI)
+ #define TFTGLCD_CS PD2
+ #endif
+
+ #define SD_DETECT_PIN PB9
+
#else
#define LCD_PINS_RS PC12
diff --git a/Marlin/src/pins/stm32f1/pins_MKS_ROBIN_LITE3.h b/Marlin/src/pins/stm32f1/pins_MKS_ROBIN_LITE3.h
index 5cdeda3a83..d5318b8e87 100644
--- a/Marlin/src/pins/stm32f1/pins_MKS_ROBIN_LITE3.h
+++ b/Marlin/src/pins/stm32f1/pins_MKS_ROBIN_LITE3.h
@@ -115,6 +115,12 @@
#define DOGLCD_SCK PB13
#define DOGLCD_MOSI PB15
+ #elif IS_TFTGLCD_PANEL
+
+ #if ENABLED(TFTGLCD_PANEL_SPI)
+ #define TFTGLCD_CS PB11
+ #endif
+
#else // !MKS_MINI_12864
#define LCD_PINS_D4 PA6
diff --git a/Marlin/src/pins/stm32f1/pins_MKS_ROBIN_NANO_V2.h b/Marlin/src/pins/stm32f1/pins_MKS_ROBIN_NANO_V2.h
index caa5e2b8fc..4e782649d3 100644
--- a/Marlin/src/pins/stm32f1/pins_MKS_ROBIN_NANO_V2.h
+++ b/Marlin/src/pins/stm32f1/pins_MKS_ROBIN_NANO_V2.h
@@ -356,6 +356,19 @@
#define DOGLCD_SCK PA5
#define DOGLCD_MOSI PA7
+ #elif IS_TFTGLCD_PANEL
+
+ #if ENABLED(TFTGLCD_PANEL_SPI)
+ #define PIN_SPI_SCK PA5
+ #define PIN_TFT_MISO PA6
+ #define PIN_TFT_MOSI PA7
+ #define TFTGLCD_CS PE8
+ #endif
+
+ #ifndef BEEPER_PIN
+ #define BEEPER_PIN -1
+ #endif
+
#else // !MKS_MINI_12864
#define LCD_PINS_D4 PE14
diff --git a/Marlin/src/pins/stm32f1/pins_MKS_ROBIN_PRO.h b/Marlin/src/pins/stm32f1/pins_MKS_ROBIN_PRO.h
index 93f0de33da..12c40b31b3 100644
--- a/Marlin/src/pins/stm32f1/pins_MKS_ROBIN_PRO.h
+++ b/Marlin/src/pins/stm32f1/pins_MKS_ROBIN_PRO.h
@@ -225,6 +225,12 @@
#define BTN_EN2 PG4
#endif
+#elif IS_TFTGLCD_PANEL
+
+ #if ENABLED(TFTGLCD_PANEL_SPI)
+ #define TFTGLCD_CS PG5
+ #endif
+
#elif HAS_WIRED_LCD
#define BEEPER_PIN PC5
@@ -254,6 +260,7 @@
#endif
#endif // !MKS_MINI_12864 && !ENDER2_STOCKDISPLAY
+
#endif
#ifndef BOARD_ST7920_DELAY_1
diff --git a/Marlin/src/pins/stm32f4/pins_BTT_SKR_PRO_common.h b/Marlin/src/pins/stm32f4/pins_BTT_SKR_PRO_common.h
index 8397276144..1cd7e9dd89 100644
--- a/Marlin/src/pins/stm32f4/pins_BTT_SKR_PRO_common.h
+++ b/Marlin/src/pins/stm32f4/pins_BTT_SKR_PRO_common.h
@@ -286,7 +286,14 @@
//
// LCDs and Controllers
//
-#if HAS_WIRED_LCD
+#if IS_TFTGLCD_PANEL
+
+ #if ENABLED(TFTGLCD_PANEL_SPI)
+ #define TFTGLCD_CS PG10
+ #endif
+
+#elif HAS_WIRED_LCD
+
#define BEEPER_PIN PG4
#define BTN_ENC PA8
diff --git a/buildroot/tests/LPC1769-tests b/buildroot/tests/LPC1769-tests
index 9c7a1ba10e..a7a6456d02 100755
--- a/buildroot/tests/LPC1769-tests
+++ b/buildroot/tests/LPC1769-tests
@@ -26,6 +26,20 @@ opt_enable VIKI2 SDSUPPORT ADAPTIVE_FAN_SLOWING NO_FAN_SLOWING_IN_PID_TUNING \
opt_set GRID_MAX_POINTS_X 16
exec_test $1 $2 "Smoothieboard with many features"
+restore_configs
+opt_set MOTHERBOARD BOARD_SMOOTHIEBOARD
+opt_set EXTRUDERS 2
+opt_set TEMP_SENSOR_1 -1
+opt_set TEMP_SENSOR_BED 5
+opt_enable TFTGLCD_PANEL_SPI SDSUPPORT ADAPTIVE_FAN_SLOWING NO_FAN_SLOWING_IN_PID_TUNING \
+ FIX_MOUNTED_PROBE AUTO_BED_LEVELING_BILINEAR G29_RETRY_AND_RECOVER Z_MIN_PROBE_REPEATABILITY_TEST DEBUG_LEVELING_FEATURE \
+ BABYSTEPPING BABYSTEP_XY BABYSTEP_ZPROBE_OFFSET \
+ PRINTCOUNTER NOZZLE_PARK_FEATURE NOZZLE_CLEAN_FEATURE SLOW_PWM_HEATERS PIDTEMPBED EEPROM_SETTINGS INCH_MODE_SUPPORT TEMPERATURE_UNITS_SUPPORT \
+ Z_SAFE_HOMING ADVANCED_PAUSE_FEATURE PARK_HEAD_ON_PAUSE \
+ LCD_INFO_MENU ARC_SUPPORT BEZIER_CURVE_SUPPORT EXTENDED_CAPABILITIES_REPORT AUTO_REPORT_TEMPERATURES SDCARD_SORT_ALPHA EMERGENCY_PARSER
+opt_set GRID_MAX_POINTS_X 16
+exec_test $1 $2 "Smoothieboard with TFTGLCD_PANEL_SPI"
+
#restore_configs
#opt_set MOTHERBOARD BOARD_AZTEEG_X5_MINI_WIFI
#opt_enable COREYX USE_XMAX_PLUG DAC_MOTOR_CURRENT_DEFAULT \
diff --git a/platformio.ini b/platformio.ini
index ff3c0fb810..c426c5223b 100644
--- a/platformio.ini
+++ b/platformio.ini
@@ -26,7 +26,7 @@ include_dir = Marlin
#
[common]
default_src_filter = + - - +
- - - - -
+ - - - - -
-
- - -
- -
@@ -216,6 +216,7 @@ HAS_MARLINUI_U8GLIB = U8glib-HAL@~0.4.1
src_filter=+
HAS_GRAPHICAL_TFT = src_filter=+
DWIN_CREALITY_LCD = src_filter=+
+IS_TFTGLCD_PANEL = src_filter=+
HAS_LCD_MENU = src_filter=+
HAS_GAMES = src_filter=+
MARLIN_BRICKOUT = src_filter=+