diff --git a/.github/workflows/test-builds.yml b/.github/workflows/test-builds.yml
index 1e48822dcd..542b5181bd 100644
--- a/.github/workflows/test-builds.yml
+++ b/.github/workflows/test-builds.yml
@@ -67,6 +67,7 @@ jobs:
- rumba32_f446ve
- rumba32_mks
- mks_robin_pro
+ - STM32F103RET6_creality
# Put lengthy tests last
diff --git a/Marlin/Configuration.h b/Marlin/Configuration.h
index c907cc0a52..f3ed3cb91f 100644
--- a/Marlin/Configuration.h
+++ b/Marlin/Configuration.h
@@ -904,6 +904,11 @@
*/
//#define BLTOUCH
+/**
+ * Pressure sensor with a BLTouch-like interface
+ */
+//#define CREALITY_TOUCH
+
/**
* Touch-MI Probe by hotends.fr
*
@@ -2130,6 +2135,11 @@
//
//#define SPI_GRAPHICAL_TFT
+//
+// Ender-3 v2 OEM display. A DWIN display with Rotary Encoder.
+//
+//#define DWIN_CREALITY_LCD
+
//
// ADS7843/XPT2046 ADC Touchscreen such as ILI9341 2.8
//
diff --git a/Marlin/src/HAL/STM32F1/timers.h b/Marlin/src/HAL/STM32F1/timers.h
index 442b2eacb2..57fd0e3c4c 100644
--- a/Marlin/src/HAL/STM32F1/timers.h
+++ b/Marlin/src/HAL/STM32F1/timers.h
@@ -44,8 +44,12 @@ typedef uint16_t hal_timer_t;
#define HAL_TIMER_RATE uint32_t(F_CPU) // frequency of timers peripherals
-#define STEP_TIMER_CHAN 1 // Channel of the timer to use for compare and interrupts
-#define TEMP_TIMER_CHAN 1 // Channel of the timer to use for compare and interrupts
+#ifndef STEP_TIMER_CHAN
+ #define STEP_TIMER_CHAN 1 // Channel of the timer to use for compare and interrupts
+#endif
+#ifndef TEMP_TIMER_CHAN
+ #define TEMP_TIMER_CHAN 1 // Channel of the timer to use for compare and interrupts
+#endif
/**
* Note: Timers may be used by platforms and libraries
diff --git a/Marlin/src/MarlinCore.cpp b/Marlin/src/MarlinCore.cpp
index df04ba2d7f..ca75f1fed1 100644
--- a/Marlin/src/MarlinCore.cpp
+++ b/Marlin/src/MarlinCore.cpp
@@ -65,6 +65,16 @@
#include "lcd/extui/lib/mks_ui/inc/draw_ui.h"
#endif
+#if ENABLED(DWIN_CREALITY_LCD)
+ #include "lcd/dwin/dwin.h"
+ #include "lcd/dwin/dwin_lcd.h"
+ #include "lcd/dwin/rotary_encoder.h"
+#endif
+
+#if ENABLED(IIC_BL24CXX_EEPROM)
+ #include "lcd/dwin/eeprom_BL24CXX.h"
+#endif
+
#if ENABLED(DIRECT_STEPPING)
#include "feature/direct_stepping.h"
#endif
@@ -689,6 +699,9 @@ void idle(TERN_(ADVANCED_PAUSE_FEATURE, bool no_stepper_sleep/*=false*/)) {
// Handle SD Card insert / remove
TERN_(SDSUPPORT, card.manage_media());
+ // Handle UI input / draw events
+ TERN(DWIN_CREALITY_LCD, DWIN_Update(), ui.update());
+
// Handle USB Flash Drive insert / remove
TERN_(USB_FLASH_DRIVE_SUPPORT, Sd2Card::idle());
@@ -962,8 +975,19 @@ void setup() {
// UI must be initialized before EEPROM
// (because EEPROM code calls the UI).
- SETUP_RUN(ui.init());
- SETUP_RUN(ui.reset_status()); // Load welcome message early. (Retained if no errors exist.)
+ #if ENABLED(DWIN_CREALITY_LCD)
+ delay(800); // Required delay (since boot?)
+ SERIAL_ECHOPGM("\nDWIN handshake ");
+ if (DWIN_Handshake()) SERIAL_ECHOLNPGM("ok."); else SERIAL_ECHOLNPGM("error.");
+ DWIN_Frame_SetDir(1); // Orientation 90°
+ DWIN_UpdateLCD(); // Show bootscreen (first image)
+ #else
+ SETUP_RUN(ui.init());
+ #if HAS_SPI_LCD && ENABLED(SHOW_BOOTSCREEN)
+ SETUP_RUN(ui.show_bootscreen());
+ #endif
+ SETUP_RUN(ui.reset_status()); // Load welcome message early. (Retained if no errors exist.)
+ #endif
#if BOTH(HAS_SPI_LCD, SHOW_BOOTSCREEN)
SETUP_RUN(ui.show_bootscreen());
@@ -1143,6 +1167,22 @@ void setup() {
SETUP_RUN(mmu2.init());
#endif
+ #if ENABLED(IIC_BL24CXX_EEPROM)
+ BL24CXX::init();
+ const uint8_t err = BL24CXX::check();
+ SERIAL_ECHO_TERNARY(err, "I2C_EEPROM Check ", "failed", "succeeded", "!\n");
+ #endif
+
+ #if ENABLED(DWIN_CREALITY_LCD)
+ Encoder_Configuration();
+ HMI_Init();
+ HMI_StartFrame(true);
+ #endif
+
+ #if HAS_SERVICE_INTERVALS && DISABLED(DWIN_CREALITY_LCD)
+ ui.reset_status(true); // Show service messages or keep current status
+ #endif
+
#if ENABLED(MAX7219_DEBUG)
SETUP_RUN(max7219.init());
#endif
diff --git a/Marlin/src/core/boards.h b/Marlin/src/core/boards.h
index adbd12e2f5..5c789e7ef0 100644
--- a/Marlin/src/core/boards.h
+++ b/Marlin/src/core/boards.h
@@ -310,6 +310,7 @@
#define BOARD_CCROBOT_MEEB_3DP 4029 // ccrobot-online.com MEEB_3DP (STM32F103RC)
#define BOARD_CHITU3D_V5 4030 // Chitu3D TronXY X5SA V5 Board
#define BOARD_CHITU3D_V6 4031 // Chitu3D TronXY X5SA V5 Board
+#define BOARD_CREALITY_V4 4032 // Creality v4.x (STM32F103RE)
//
// ARM Cortex-M4F
diff --git a/Marlin/src/core/serial.h b/Marlin/src/core/serial.h
index 1a319e6322..be4c000b52 100644
--- a/Marlin/src/core/serial.h
+++ b/Marlin/src/core/serial.h
@@ -51,10 +51,16 @@ extern uint8_t marlin_debug_flags;
extern int8_t serial_port_index;
#define _PORT_REDIRECT(n,p) REMEMBER(n,serial_port_index,p)
#define _PORT_RESTORE(n) RESTORE(n)
- #define SERIAL_OUT(WHAT, V...) do{ \
- if (!serial_port_index || serial_port_index == SERIAL_BOTH) (void)MYSERIAL0.WHAT(V); \
- if ( serial_port_index) (void)MYSERIAL1.WHAT(V); \
- }while(0)
+
+ #ifdef SERIAL_CATCHALL
+ #define SERIAL_OUT(WHAT, V...) (void)CAT(MYSERIAL,SERIAL_CATCHALL).WHAT(V)
+ #else
+ #define SERIAL_OUT(WHAT, V...) do{ \
+ if (!serial_port_index || serial_port_index == SERIAL_BOTH) (void)MYSERIAL0.WHAT(V); \
+ if ( serial_port_index) (void)MYSERIAL1.WHAT(V); \
+ }while(0)
+ #endif
+
#define SERIAL_ASSERT(P) if(serial_port_index!=(P)){ debugger(); }
#else
#define _PORT_REDIRECT(n,p) NOOP
diff --git a/Marlin/src/feature/bltouch.h b/Marlin/src/feature/bltouch.h
index af75fb8b29..797cc7df87 100644
--- a/Marlin/src/feature/bltouch.h
+++ b/Marlin/src/feature/bltouch.h
@@ -26,9 +26,17 @@
// BLTouch commands are sent as servo angles
typedef unsigned char BLTCommand;
-#define BLTOUCH_DEPLOY 10
+#if ENABLED(CREALITY_TOUCH)
+ #define STOW_ALARM false
+ #define BLTOUCH_DEPLOY 170
+ #define BLTOUCH_STOW 20
+#else
+ #define STOW_ALARM true
+ #define BLTOUCH_DEPLOY 10
+ #define BLTOUCH_STOW 90
+#endif
+
#define BLTOUCH_SW_MODE 60
-#define BLTOUCH_STOW 90
#define BLTOUCH_SELFTEST 120
#define BLTOUCH_MODE_STORE 130
#define BLTOUCH_5V_MODE 140
@@ -95,7 +103,7 @@ public:
private:
FORCE_INLINE static bool _deploy_query_alarm() { return command(BLTOUCH_DEPLOY, BLTOUCH_DEPLOY_DELAY); }
- FORCE_INLINE static bool _stow_query_alarm() { return command(BLTOUCH_STOW, BLTOUCH_STOW_DELAY); }
+ FORCE_INLINE static bool _stow_query_alarm() { return command(BLTOUCH_STOW, BLTOUCH_STOW_DELAY) == STOW_ALARM; }
static void clear();
static bool command(const BLTCommand cmd, const millis_t &ms);
diff --git a/Marlin/src/feature/powerloss.cpp b/Marlin/src/feature/powerloss.cpp
index 0ba8737f2f..12de2d26ff 100644
--- a/Marlin/src/feature/powerloss.cpp
+++ b/Marlin/src/feature/powerloss.cpp
@@ -40,6 +40,10 @@ uint8_t PrintJobRecovery::queue_index_r;
uint32_t PrintJobRecovery::cmd_sdpos, // = 0
PrintJobRecovery::sdpos[BUFSIZE];
+#if ENABLED(DWIN_CREALITY_LCD)
+ bool PrintJobRecovery::dwin_flag; // = false
+#endif
+
#include "../sd/cardreader.h"
#include "../lcd/ultralcd.h"
#include "../gcode/queue.h"
@@ -105,6 +109,7 @@ void PrintJobRecovery::check() {
load();
if (!valid()) return cancel();
queue.inject_P(PSTR("M1000 S"));
+ TERN_(DWIN_CREALITY_LCD, dwin_flag = true);
}
}
diff --git a/Marlin/src/feature/powerloss.h b/Marlin/src/feature/powerloss.h
index affa2cae44..3645f57c3c 100644
--- a/Marlin/src/feature/powerloss.h
+++ b/Marlin/src/feature/powerloss.h
@@ -121,6 +121,10 @@ class PrintJobRecovery {
static uint32_t cmd_sdpos, //!< SD position of the next command
sdpos[BUFSIZE]; //!< SD positions of queued commands
+ #if ENABLED(DWIN_CREALITY_LCD)
+ static bool dwin_flag;
+ #endif
+
static void init();
static void prepare();
diff --git a/Marlin/src/gcode/bedlevel/abl/G29.cpp b/Marlin/src/gcode/bedlevel/abl/G29.cpp
index 9c5907a5cc..01871ec7e6 100644
--- a/Marlin/src/gcode/bedlevel/abl/G29.cpp
+++ b/Marlin/src/gcode/bedlevel/abl/G29.cpp
@@ -60,6 +60,10 @@
#include "../../../lcd/extui/ui_api.h"
#endif
+#if ENABLED(DWIN_CREALITY_LCD)
+ #include "../../../lcd/dwin/dwin.h"
+#endif
+
#if HAS_MULTI_HOTEND
#include "../../../module/tool_change.h"
#endif
@@ -888,6 +892,10 @@ G29_TYPE GcodeSuite::G29() {
process_subcommands_now_P(PSTR(Z_PROBE_END_SCRIPT));
#endif
+ #if ENABLED(DWIN_CREALITY_LCD)
+ DWIN_CompletedLeveling();
+ #endif
+
report_current_position();
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("<<< G29");
diff --git a/Marlin/src/gcode/calibrate/G28.cpp b/Marlin/src/gcode/calibrate/G28.cpp
index 8720b1a929..982c9e9d3c 100644
--- a/Marlin/src/gcode/calibrate/G28.cpp
+++ b/Marlin/src/gcode/calibrate/G28.cpp
@@ -46,6 +46,9 @@
#endif
#include "../../lcd/ultralcd.h"
+#if ENABLED(DWIN_CREALITY_LCD)
+ #include "../../lcd/dwin/dwin.h"
+#endif
#if HAS_L64XX // set L6470 absolute position registers to counts
#include "../../libs/L64XX/L64XX_Marlin.h"
@@ -209,6 +212,8 @@ void GcodeSuite::G28() {
log_machine_info();
}
+ TERN_(DWIN_CREALITY_LCD, HMI_flag.home_flag = true);
+
#if ENABLED(DUAL_X_CARRIAGE)
bool IDEX_saved_duplication_state = extruder_duplication_enabled;
DualXMode IDEX_saved_mode = dual_x_carriage_mode;
@@ -475,6 +480,8 @@ void GcodeSuite::G28() {
ui.refresh();
+ TERN_(DWIN_CREALITY_LCD, DWIN_CompletedHoming());
+
report_current_position();
if (ENABLED(NANODLP_Z_SYNC) && (doZ || ENABLED(NANODLP_ALL_AXIS)))
diff --git a/Marlin/src/gcode/sd/M24_M25.cpp b/Marlin/src/gcode/sd/M24_M25.cpp
index 8ac3f45491..86f34f1361 100644
--- a/Marlin/src/gcode/sd/M24_M25.cpp
+++ b/Marlin/src/gcode/sd/M24_M25.cpp
@@ -31,7 +31,6 @@
#if ENABLED(PARK_HEAD_ON_PAUSE)
#include "../../feature/pause.h"
- #include "../queue.h"
#endif
#if ENABLED(HOST_ACTION_COMMANDS)
@@ -98,7 +97,8 @@ void GcodeSuite::M25() {
#endif
print_job_timer.pause();
- ui.reset_status();
+
+ TERN(DWIN_CREALITY_LCD,,ui.reset_status());
#if ENABLED(HOST_ACTION_COMMANDS)
TERN_(HOST_PROMPT_SUPPORT, host_prompt_open(PROMPT_PAUSE_RESUME, PSTR("Pause SD"), PSTR("Resume")));
diff --git a/Marlin/src/inc/Conditionals_LCD.h b/Marlin/src/inc/Conditionals_LCD.h
index b2addd6561..df4c5aade0 100644
--- a/Marlin/src/inc/Conditionals_LCD.h
+++ b/Marlin/src/inc/Conditionals_LCD.h
@@ -529,6 +529,15 @@
#define UNUSED_E(E) UNUSED(E)
#endif
+#if ENABLED(DWIN_CREALITY_LCD)
+ #define SERIAL_CATCHALL 0
+#endif
+
+// Pressure sensor with a BLTouch-like interface
+#if ENABLED(CREALITY_TOUCH)
+ #define BLTOUCH
+#endif
+
/**
* The BLTouch Probe emulates a servo probe
* and uses "special" angles for its state.
diff --git a/Marlin/src/inc/Conditionals_post.h b/Marlin/src/inc/Conditionals_post.h
index ff4f7989db..9fe25e8778 100644
--- a/Marlin/src/inc/Conditionals_post.h
+++ b/Marlin/src/inc/Conditionals_post.h
@@ -2465,7 +2465,11 @@
#endif
// Number of VFAT entries used. Each entry has 13 UTF-16 characters
-#define MAX_VFAT_ENTRIES TERN(SCROLL_LONG_FILENAMES, 5, 2)
+#if EITHER(SCROLL_LONG_FILENAMES, DWIN_CREALITY_LCD)
+ #define MAX_VFAT_ENTRIES (5)
+#else
+ #define MAX_VFAT_ENTRIES (2)
+#endif
// Nozzle park for Delta
#if BOTH(NOZZLE_PARK_FEATURE, DELTA)
diff --git a/Marlin/src/inc/SanityCheck.h b/Marlin/src/inc/SanityCheck.h
index 0d411fb0fd..074ef9ec9b 100644
--- a/Marlin/src/inc/SanityCheck.h
+++ b/Marlin/src/inc/SanityCheck.h
@@ -1165,7 +1165,8 @@ static_assert(Y_MAX_LENGTH >= Y_BED_SIZE, "Movement bounds (Y_MIN_POS, Y_MAX_POS
+ ENABLED(FIX_MOUNTED_PROBE) \
+ ENABLED(NOZZLE_AS_PROBE) \
+ (HAS_Z_SERVO_PROBE && DISABLED(BLTOUCH)) \
- + ENABLED(BLTOUCH) \
+ + ENABLED(BLTOUCH) && DISABLED(CREALITY_TOUCH) \
+ + ENABLED(CREALITY_TOUCH) \
+ ENABLED(TOUCH_MI_PROBE) \
+ ENABLED(SOLENOID_PROBE) \
+ ENABLED(Z_PROBE_ALLEN_KEY) \
@@ -2150,6 +2151,7 @@ static_assert(hbm[Z_AXIS] >= 0, "HOMING_BUMP_MM.Z must be greater than or equal
+ ENABLED(FYSETC_MINI_12864_2_1) \
+ ENABLED(FYSETC_GENERIC_12864_1_1) \
+ ENABLED(CR10_STOCKDISPLAY) \
+ + ENABLED(DWIN_CREALITY_LCD) \
+ ENABLED(ANET_FULL_GRAPHICS_LCD) \
+ ENABLED(AZSMZ_12864) \
+ ENABLED(SILVER_GATE_GLCD_CONTROLLER) \
diff --git a/Marlin/src/lcd/dwin/README.md b/Marlin/src/lcd/dwin/README.md
new file mode 100644
index 0000000000..10b05455fd
--- /dev/null
+++ b/Marlin/src/lcd/dwin/README.md
@@ -0,0 +1,7 @@
+# DWIN for Creality Ender 3 v2
+
+Marlin's Ender 3 v2 support requires the `DWIN_SET` included with the Ender 3 V2 [example configuration](https://github.com/MarlinFirmware/Configurations/tree/bugfix-2.0.x/config/examples/Creality/Ender-3%20V2).
+
+## Easy Install
+
+Copy the `DWIN_SET` folder onto a Micro-SD card and insert the card into the slot on the DWIN screen. Cycle the machine and wait for the screen to go from blue to orange. Turn the machine off and remove the SD card. When you turn on the machine the screen will display a "Creality" loading screen.
diff --git a/Marlin/src/lcd/dwin/dwin.cpp b/Marlin/src/lcd/dwin/dwin.cpp
new file mode 100644
index 0000000000..31580e03ae
--- /dev/null
+++ b/Marlin/src/lcd/dwin/dwin.cpp
@@ -0,0 +1,3390 @@
+/**
+ * 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 .
+ *
+ */
+
+/**
+ * DWIN by Creality3D
+ */
+
+#include "../../inc/MarlinConfigPre.h"
+
+#if ENABLED(DWIN_CREALITY_LCD)
+
+#include "dwin.h"
+
+#include
+#include
+#include
+
+#include "../fontutils.h"
+#include "../ultralcd.h"
+
+#include "../../sd/cardreader.h"
+
+#include "../../MarlinCore.h"
+#include "../../core/serial.h"
+#include "../../core/macros.h"
+#include "../../gcode/queue.h"
+
+#include "../../feature/powerloss.h"
+#include "../../feature/babystep.h"
+
+#include "../../module/configuration_store.h"
+#include "../../module/temperature.h"
+#include "../../module/printcounter.h"
+#include "../../module/motion.h"
+#include "../../module/planner.h"
+
+#if HAS_LEVELING
+ #include "../../feature/bedlevel/bedlevel.h"
+#endif
+
+#if HAS_BED_PROBE
+ #include "../../module/probe.h"
+#endif
+
+#include "../../libs/buzzer.h"
+
+#define PAUSE_HEAT true
+
+#define USE_STRING_HEADINGS
+
+#define MENU_FONT font8x16
+#define STAT_FONT font10x20
+#define HEADER_FONT font10x20
+
+constexpr uint16_t TROWS = 6, MROWS = TROWS - 1, // Total rows, and other-than-Back
+ TITLE_HEIGHT = 30, // Title bar height
+ MLINE = 53, // Menu line height
+ LBLX = 60, // Menu item label X
+ MENU_CHR_W = 8, STAT_CHR_W = 10;
+
+#define MBASE(L) (49+(L)*MLINE)
+
+#define BABY_Z_VAR TERN(HAS_LEVELING, probe.offset.z, zprobe_zoffset)
+
+/* Value Init */
+HMI_value_t HMI_ValueStruct;
+HMI_Flag HMI_flag{0};
+
+millis_t Encoder_ms = 0;
+millis_t Wait_ms = 0;
+millis_t heat_time = 0;
+
+int checkkey = 0, last_checkkey = 0;
+
+typedef struct {
+ uint8_t now, last;
+ void set(uint8_t v) { now = last = v; }
+ void reset() { set(0); }
+ bool changed() { bool c = (now != last); if (c) last = now; return c; }
+ bool dec() { if (now) now--; return changed(); }
+ bool inc(uint8_t v) { if (now < v) now++; else now = v; return changed(); }
+} select_t;
+
+select_t select_page{0}, select_file{0}, select_print{0}, select_prepare{0}
+ , select_control{0}, select_axis{0}, select_temp{0}, select_motion{0}, select_tune{0}
+ , select_PLA{0}, select_ABS{0}
+ , select_speed{0}
+ , select_acc{0}
+ , select_corner{0}
+ , select_step{0}
+ //, select_leveling{0}
+ ;
+
+uint8_t index_file = MROWS,
+ index_prepare = MROWS,
+ index_control = MROWS,
+ index_leveling = MROWS,
+ index_tune = 5;
+
+//char filebuf[50];
+
+uint8_t countbuf = 0;
+
+bool recovery_flag = false, abort_flag = false;
+
+constexpr float default_max_feedrate[] = DEFAULT_MAX_FEEDRATE;
+constexpr float default_max_acceleration[] = DEFAULT_MAX_ACCELERATION;
+constexpr float default_max_jerk[] = { DEFAULT_XJERK, DEFAULT_YJERK, DEFAULT_ZJERK, DEFAULT_EJERK };
+constexpr float default_axis_steps_per_unit[] = DEFAULT_AXIS_STEPS_PER_UNIT;
+
+uint8_t Percentrecord = 0;
+uint16_t last_Printtime = 0, remain_time = 0;
+float last_temp_hotend_target = 0, last_temp_bed_target = 0;
+float last_temp_hotend_current = 0, last_temp_bed_current = 0;
+uint8_t last_fan_speed = 0;
+uint16_t last_speed = 0;
+
+float last_E_scale = 0;
+
+bool DWIN_lcd_sd_status = 0;
+
+bool pause_action_flag = 0;
+
+int temphot = 0, tempbed = 0;
+
+float zprobe_zoffset = 0.00;
+float last_zoffset = 0.00, last_probe_zoffset = 0.00;
+
+#define FONT_EEPROM_OFFSET 0
+
+void lcd_select_language(void) {
+ BL24CXX::read(FONT_EEPROM_OFFSET, (uint8_t*)&HMI_flag.language_flag, sizeof(HMI_flag.language_flag));
+ if (HMI_flag.language_flag)
+ DWIN_JPG_CacheTo1(Language_Chinese);
+ else
+ DWIN_JPG_CacheTo1(Language_English);
+}
+
+void set_english_to_eeprom(void) {
+ HMI_flag.language_flag = 0;
+ DWIN_JPG_CacheTo1(Language_English);
+ BL24CXX::write(FONT_EEPROM_OFFSET, (uint8_t*)&HMI_flag.language_flag, sizeof(HMI_flag.language_flag));
+}
+void set_chinese_to_eeprom(void) {
+ HMI_flag.language_flag = 1;
+ DWIN_JPG_CacheTo1(Language_Chinese);
+ BL24CXX::write(FONT_EEPROM_OFFSET, (uint8_t*)&HMI_flag.language_flag, sizeof(HMI_flag.language_flag));
+}
+
+void show_plus_or_minus(uint8_t size, uint16_t bColor, uint8_t iNum, uint8_t fNum, uint16_t x, uint16_t y, long value) {
+ if (value < 0) {
+ DWIN_Draw_String(false,true,size,White,bColor, x-6, y, (char*)"-");
+ DWIN_Draw_FloatValue(true,true,0,size,White,bColor, iNum, fNum, x, y, -value);
+ }
+ else {
+ DWIN_Draw_String(false,true,size,White,bColor, x-6, y, (char*)" ");
+ DWIN_Draw_FloatValue(true,true,0,size,White,bColor, iNum, fNum, x, y, value);
+ }
+}
+
+void ICON_Print() {
+ if (select_page.now == 0) {
+ DWIN_ICON_Show(ICON,ICON_Print_1, 17, 130);
+ if (HMI_flag.language_flag)
+ DWIN_Frame_AreaCopy(1, 1, 447, 271-243, 479-19, 58, 201);
+ else
+ DWIN_Frame_AreaCopy(1, 1, 451, 271-240, 479-16, 72-15, 201);
+ }
+ else {
+ DWIN_ICON_Show(ICON,ICON_Print_0, 17, 130);
+ if (HMI_flag.language_flag)
+ DWIN_Frame_AreaCopy(1, 1, 405, 271-243, 420, 58, 201);
+ else
+ DWIN_Frame_AreaCopy(1, 1, 423, 271-240, 423+12, 72-15, 201);
+ }
+}
+
+void ICON_Prepare() {
+ if (select_page.now == 1) {
+ DWIN_ICON_Show(ICON,ICON_Prepare_1, 145, 130);
+ if (HMI_flag.language_flag)
+ DWIN_Frame_AreaCopy(1, 31, 447, 271-213, 479-19, 186, 201);
+ else
+ DWIN_Frame_AreaCopy(1, 33, 451, 271-189, 479-13, 200-25, 201);
+ }
+ else {
+ DWIN_ICON_Show(ICON,ICON_Prepare_0, 145, 130);
+ if (HMI_flag.language_flag)
+ DWIN_Frame_AreaCopy(1, 31, 405, 271-213, 420, 186, 201);
+ else
+ DWIN_Frame_AreaCopy(1, 33, 423, 271-189, 423+15, 200-25, 201);
+ }
+}
+
+void ICON_Control() {
+ if (select_page.now == 2) {
+ DWIN_ICON_Show(ICON,ICON_Control_1, 17, 246);
+ if (HMI_flag.language_flag)
+ DWIN_Frame_AreaCopy(1, 61, 447, 271-183, 479-19, 58, 318);
+ else
+ DWIN_Frame_AreaCopy(1, 85, 451, 271-139, 479-16, 72-24, 318);
+ }
+ else {
+ DWIN_ICON_Show(ICON,ICON_Control_0, 17, 246);
+ if (HMI_flag.language_flag)
+ DWIN_Frame_AreaCopy(1, 61, 405, 271-183, 420, 58, 318);
+ else
+ DWIN_Frame_AreaCopy(1, 85, 423, 271-139, 479-45, 72-24, 318);
+ }
+}
+
+void ICON_StartInfo(bool show) {
+ if (show) {
+ DWIN_ICON_Show(ICON,ICON_Info_1, 145, 246);
+ if (HMI_flag.language_flag)
+ DWIN_Frame_AreaCopy(1, 91, 447, 271-153, 479-19, 186, 318);
+ else
+ DWIN_Frame_AreaCopy(1, 132, 451, 159, 479-13, 186, 318);
+ }
+ else {
+ DWIN_ICON_Show(ICON,ICON_Info_0, 145, 246);
+ if (HMI_flag.language_flag)
+ DWIN_Frame_AreaCopy(1, 91, 405, 271-153, 420, 186, 318);
+ else
+ DWIN_Frame_AreaCopy(1, 132, 423, 159, 423+12, 186, 318);
+ }
+}
+
+void ICON_Leveling(bool show) {
+ if (show) {
+ DWIN_ICON_Show(ICON,ICON_Leveling_1, 145, 246);
+ if (HMI_flag.language_flag)
+ DWIN_Frame_AreaCopy(1, 211, 447, 238, 479-19, 186, 318);
+ else
+ DWIN_Frame_AreaCopy(1, 84, 437, 120, 449, 200-18, 318);
+ }
+ else {
+ DWIN_ICON_Show(ICON,ICON_Leveling_0, 145, 246);
+ if (HMI_flag.language_flag)
+ DWIN_Frame_AreaCopy(1, 211, 405, 238, 420, 186, 318);
+ else
+ DWIN_Frame_AreaCopy(1, 84, 465, 120, 478, 200-18, 318);
+ }
+}
+
+void ICON_Tune() {
+ if (select_print.now == 0) {
+ DWIN_ICON_Show(ICON,ICON_Setup_1, 8, 252);
+ if (HMI_flag.language_flag)
+ DWIN_Frame_AreaCopy(1, 121, 447, 271-123, 479-21, 34, 325);
+ else
+ DWIN_Frame_AreaCopy(1, 1, 465, 271-237, 479-2, 48-17, 325);
+ }
+ else {
+ DWIN_ICON_Show(ICON,ICON_Setup_0, 8, 252);
+ if (HMI_flag.language_flag)
+ DWIN_Frame_AreaCopy(1, 121, 405, 271-123, 420, 34, 325);
+ else
+ DWIN_Frame_AreaCopy(1, 1, 438, 271-239, 479-31, 48-17, 325);
+ }
+}
+
+void ICON_Pause() {
+ if (select_print.now == 1) {
+ DWIN_ICON_Show(ICON,ICON_Pause_1, 96, 252);
+ if (HMI_flag.language_flag)
+ DWIN_Frame_AreaCopy(1, 181, 447, 271-63, 479-20, 124, 325);
+ else
+ DWIN_Frame_AreaCopy(1, 177, 451, 271-55, 479-17, 136-20, 325);
+ }
+ else {
+ DWIN_ICON_Show(ICON,ICON_Pause_0, 96, 252);
+ if (HMI_flag.language_flag)
+ DWIN_Frame_AreaCopy(1, 181, 405, 271-63, 420, 124, 325);
+ else
+ DWIN_Frame_AreaCopy(1, 177, 423, 271-56, 479-46, 136-20, 325);
+ }
+}
+
+void ICON_Continue() {
+ if (select_print.now == 1) {
+ DWIN_ICON_Show(ICON,ICON_Continue_1, 96, 252);
+ if (HMI_flag.language_flag)
+ DWIN_Frame_AreaCopy(1, 1, 447, 271-243, 479-19, 124, 325);
+ else
+ DWIN_Frame_AreaCopy(1, 1, 451, 271-239, 479-16, 136-15, 325);
+ }
+ else {
+ DWIN_ICON_Show(ICON,ICON_Continue_0, 96, 252);
+ if (HMI_flag.language_flag)
+ DWIN_Frame_AreaCopy(1, 1, 405, 271-243, 420, 124, 325);
+ else
+ DWIN_Frame_AreaCopy(1, 1, 424, 271-240, 479-45, 136-15, 325);
+ }
+}
+
+void ICON_Stop() {
+ if (select_print.now == 2) {
+ DWIN_ICON_Show(ICON,ICON_Stop_1, 184, 252);
+ if (HMI_flag.language_flag)
+ DWIN_Frame_AreaCopy(1, 151, 447, 271-93, 479-20, 210, 325);
+ else
+ DWIN_Frame_AreaCopy(1, 218, 451, 271-22, 479-14, 224-15, 325);
+ }
+ else {
+ DWIN_ICON_Show(ICON,ICON_Stop_0, 184, 252);
+ if (HMI_flag.language_flag)
+ DWIN_Frame_AreaCopy(1, 151, 405, 271-93, 420, 210, 325);
+ else
+ DWIN_Frame_AreaCopy(1, 218, 423, 271-24, 479-43, 224-15, 325);
+ }
+}
+
+inline void Clear_Title_Bar(void) {
+ DWIN_Draw_Rectangle(1, Background_blue, 0, 0, DWIN_WIDTH, 30);
+}
+
+inline void Draw_Title(const char * const title) {
+ DWIN_Draw_String(false,false,HEADER_FONT,White,Background_blue, 14, 4, (char*)title);
+}
+
+inline void Clear_Menu_Area(void) {
+ DWIN_Draw_Rectangle(1, Background_black, 0, 31, 272, 360);
+}
+
+inline void Clear_Main_Window(void) {
+ Clear_Title_Bar();
+ Clear_Menu_Area();
+}
+
+inline void Clear_Popup_Area(void) {
+ Clear_Title_Bar();
+ DWIN_Draw_Rectangle(1, Background_black, 0, 31, 272, 480);
+}
+
+void Draw_Popup_Bkgd_105(void) {
+ DWIN_Draw_Rectangle(1, Background_window, 14, 105, 271-13, 479-105);
+}
+
+inline void Draw_More_Icon(const uint8_t line) {
+ DWIN_ICON_Show(ICON, ICON_More, 226, 46+line*MLINE);
+}
+
+inline void Draw_Menu_Cursor(const uint8_t line) {
+ //DWIN_ICON_Show(ICON,ICON_Rectangle, 0, 31+line*MLINE);
+ DWIN_Draw_Rectangle(1, Rectangle_Color, 0, 31+line*MLINE, 14, 31+(line+1)*MLINE-2);
+}
+
+inline void Erase_Menu_Cursor(const uint8_t line) {
+ DWIN_Draw_Rectangle(1, Background_black, 0, 31+line*MLINE, 14, 31+(line+1)*MLINE-2);
+}
+
+inline void Move_Highlight(const int16_t from, const uint16_t newline) {
+ Erase_Menu_Cursor(newline - from);
+ Draw_Menu_Cursor(newline);
+}
+
+inline void Add_Menu_Line() {
+ Move_Highlight(1, MROWS);
+ DWIN_Draw_Line(Line_Color, 16, 82+MROWS*MLINE, 256, 83+MROWS*MLINE);
+}
+
+inline void Scroll_Menu(const uint8_t dir) {
+ DWIN_Frame_AreaMove(1, dir, MLINE, Background_black, 0, 31, 272, 349);
+ switch (dir) {
+ case DWIN_SCROLL_DOWN: Move_Highlight(-1, 0); break;
+ case DWIN_SCROLL_UP: Add_Menu_Line(); break;
+ }
+}
+
+inline uint16_t nr_sd_menu_items() {
+ return card.get_num_Files() + !card.flag.workDirIsRoot;
+}
+
+inline void Draw_Menu_Icon(const uint8_t line, const uint8_t icon) {
+ DWIN_ICON_Show(ICON, icon, 26, 46+line*MLINE);
+}
+
+inline void Erase_Menu_Text(const uint8_t line) {
+ DWIN_Draw_Rectangle(1, Background_black, LBLX, 31+line*MLINE+4, 271, 28+(line+1)*MLINE-4);
+}
+
+inline void Draw_Menu_Line(const uint8_t line, const uint8_t icon=0, const char * const label=nullptr) {
+ if (label) DWIN_Draw_String(false,false,font8x16,White,Background_black, LBLX, 48+line*MLINE, (char*)label);
+ if (icon) Draw_Menu_Icon(line, icon);
+ DWIN_Draw_Line(Line_Color, 16, 29+(line+1)*MLINE, 256, 30+(line+1)*MLINE);
+}
+
+// The "Back" label is always on the first line
+inline void Draw_Back_Label(void) {
+ if (HMI_flag.language_flag)
+ DWIN_Frame_AreaCopy(1, 129, 72, 271-115, 479-395, LBLX, MBASE(0));
+ else
+ DWIN_Frame_AreaCopy(1, 226, 179, 271-15, 479-290, LBLX, MBASE(0));
+}
+
+// Draw "Back" line at the top
+inline void Draw_Back_First(const bool is_sel=true) {
+ Draw_Menu_Line(0, ICON_Back);
+ Draw_Back_Label();
+ if (is_sel) Draw_Menu_Cursor(0);
+}
+
+//
+// Draw Menus
+//
+
+inline void draw_move_en(const uint16_t line) {
+ DWIN_Frame_AreaCopy(1, 69, 61, 271-169, 479-408, LBLX, line); // "Move"
+}
+
+inline void Prepare_Item_Move(const uint8_t row) {
+ if (HMI_flag.language_flag)
+ DWIN_Frame_AreaCopy(1, 159, 70, 271-71, 479-395, LBLX, MBASE(row));
+ else
+ draw_move_en(MBASE(row)); // "Move >"
+ Draw_Menu_Line(row, ICON_Axis);
+ Draw_More_Icon(row);
+}
+
+inline void Prepare_Item_Disable(const uint8_t row) {
+ if (HMI_flag.language_flag)
+ DWIN_Frame_AreaCopy(1, 204, 70, 271-12, 479-397, LBLX, MBASE(row));
+ else
+ DWIN_Frame_AreaCopy(1, 103, 59, 271-71, 479-405, LBLX, MBASE(row)); // "Disable Stepper"
+ Draw_Menu_Line(row, ICON_CloseMotor);
+}
+
+inline void Prepare_Item_Home(const uint8_t row) {
+ if (HMI_flag.language_flag)
+ DWIN_Frame_AreaCopy(1, 0, 89, 271-230, 479-378, LBLX, MBASE(row));
+ else
+ DWIN_Frame_AreaCopy(1, 202, 61, 271-0, 479-408, LBLX, MBASE(row)); // "Auto Home"
+ Draw_Menu_Line(row, ICON_Homing);
+}
+
+inline void Prepare_Item_Offset(const uint8_t row) {
+ if (HMI_flag.language_flag) {
+ #if HAS_BED_PROBE
+ DWIN_Frame_AreaCopy(1, 174, 164, 271-48, 479-302, LBLX, MBASE(row));
+ show_plus_or_minus(font8x16, Background_black, 2, 2, 202, MBASE(row), probe.offset.z*100);
+ #else
+ DWIN_Frame_AreaCopy(1, 43, 89, 271-173, 479-378, LBLX, MBASE(row));
+ #endif
+ }
+ else {
+ #if HAS_BED_PROBE
+ DWIN_Frame_AreaCopy(1, 93, 179, 271-130, 479-290, LBLX, MBASE(row)); // "Z-Offset"
+ show_plus_or_minus(font8x16, Background_black, 2, 2, 202, MBASE(row), probe.offset.z*100);
+ #else
+ DWIN_Frame_AreaCopy(1, 1, 76, 271-165, 479-393, LBLX, MBASE(row)); // "..."
+ #endif
+ }
+ Draw_Menu_Line(row, ICON_SetHome);
+}
+
+inline void Prepare_Item_PLA(const uint8_t row) {
+ if (HMI_flag.language_flag)
+ DWIN_Frame_AreaCopy(1, 100, 89, 271-93-27, 479-378, LBLX, MBASE(row));
+ else {
+ DWIN_Frame_AreaCopy(1, 107, 76, 271-115, 479-393, LBLX, MBASE(row)); // "Preheat"
+ DWIN_Frame_AreaCopy(1, 157, 76, 181, 479-393, LBLX+49+3, MBASE(row)); // "PLA"
+ }
+ Draw_Menu_Line(row, ICON_PLAPreheat);
+}
+
+inline void Prepare_Item_ABS(const uint8_t row) {
+ if (HMI_flag.language_flag)
+ DWIN_Frame_AreaCopy(1, 180, 89, 271-11-27, 479-379, LBLX, MBASE(row));
+ else {
+ DWIN_Frame_AreaCopy(1, 107, 76, 271-115, 479-393, LBLX, MBASE(row)); // "Preheat"
+ DWIN_Frame_AreaCopy(1, 172, 76, 198, 479-393, LBLX+49+3, MBASE(row)); // "ABS"
+ }
+ Draw_Menu_Line(row, ICON_ABSPreheat);
+}
+
+inline void Prepare_Item_Cool(const uint8_t row) {
+ if (HMI_flag.language_flag)
+ DWIN_Frame_AreaCopy(1, 1, 104, 271-215, 479-362, LBLX, MBASE(row));
+ else
+ DWIN_Frame_AreaCopy(1, 200, 76, 271-7, 479-393, LBLX, MBASE(row)); // "Cooldown"
+ Draw_Menu_Line(row, ICON_Cool);
+}
+
+inline void Prepare_Item_Lang(const uint8_t row) {
+ if (HMI_flag.language_flag) {
+ DWIN_Frame_AreaCopy(1, 239, 134, 271-5, 479-333, LBLX, MBASE(row));
+ DWIN_Draw_String(false,false,font8x16,White,Background_black, 226, MBASE(row), (char*)"CN");
+ }
+ else {
+ DWIN_Frame_AreaCopy(1, 0, 194, 271-150, 479-272, LBLX, MBASE(row)); // "Language selection"
+ DWIN_Draw_String(false,false,font8x16,White,Background_black, 226, MBASE(row), (char*)"EN");
+ }
+ Draw_Menu_Icon(row, ICON_Language);
+}
+
+inline void Draw_Prepare_Menu() {
+ Clear_Main_Window();
+
+ const int16_t scroll = MROWS-index_prepare; // Scrolled-up lines
+ #define PSCROL(L) (scroll + (L))
+ #define PVISI(L) WITHIN(PSCROL(L), 0, MROWS)
+
+ if (HMI_flag.language_flag)
+ DWIN_Frame_AreaCopy(1, 133, 1, 271-111, 479-465-1, 14, 8); // "Prepare"
+ else {
+ #ifdef USE_STRING_HEADINGS
+ Draw_Title("Prepare"); // TODO: GET_TEXT_F
+ #else
+ DWIN_Frame_AreaCopy(1, 178, 2, 271-42, 479-464-1, 14, 8); // "Prepare"
+ #endif
+ }
+
+ if (PVISI(0)) Draw_Back_First(select_prepare.now == 0); // < Back
+ if (PVISI(1)) Prepare_Item_Move(PSCROL(1)); // Move >
+ if (PVISI(2)) Prepare_Item_Disable(PSCROL(2)); // Disable Stepper
+ if (PVISI(3)) Prepare_Item_Home(PSCROL(3)); // Auto Home
+ if (PVISI(4)) Prepare_Item_Offset(PSCROL(4)); // Z-Offset
+ if (PVISI(5)) Prepare_Item_PLA(PSCROL(5)); // Preheat PLA
+ if (PVISI(6)) Prepare_Item_ABS(PSCROL(6)); // Preheat ABS
+ if (PVISI(7)) Prepare_Item_Cool(PSCROL(7)); // Cooldown
+ if (PVISI(8)) Prepare_Item_Lang(PSCROL(8)); // Language CN/EN
+
+ if (select_prepare.now) Draw_Menu_Cursor(PSCROL(select_prepare.now));
+}
+
+inline void Draw_Control_Menu() {
+ Clear_Main_Window();
+
+ const int16_t scroll = TERN(HAS_LEVELING, MROWS-index_control, 0); // Scrolled-up lines
+
+ #define CSCROL(L) (scroll + (L))
+ #define CLINE(L) MBASE(CSCROL(L))
+ #define CVISI(L) WITHIN(CSCROL(L), 0, MROWS)
+
+ if (CVISI(0)) Draw_Back_First(select_control.now == 0); // < Back
+
+ if (HMI_flag.language_flag) {
+ DWIN_Frame_AreaCopy(1, 103, 1, 271-141, 479-465, 14, 8);
+ DWIN_Frame_AreaCopy(1, 57, 104, 271-187, 479-363, LBLX, CLINE(1)); // Temperature >
+ DWIN_Frame_AreaCopy(1, 87, 104, 271-157, 479-363, LBLX, CLINE(2)); // Motion >
+ DWIN_Frame_AreaCopy(1, 117, 104, 271- 99, 479-363, LBLX, CLINE(3)); // Store Config
+ DWIN_Frame_AreaCopy(1, 174, 103, 271- 42, 479-363, LBLX, CLINE(4)); // Read Config
+ DWIN_Frame_AreaCopy(1, 1, 118, 271-215, 479-348, LBLX, CLINE(5)); // Reset Config
+
+ if (CVISI(6))
+ DWIN_Frame_AreaCopy(1, 231, 104, 271- 13, 479-363, LBLX, CLINE(6)); // Info >
+ }
+ else {
+ #ifdef USE_STRING_HEADINGS
+ Draw_Title("Control"); // TODO: GET_TEXT_F
+ #else
+ DWIN_Frame_AreaCopy(1, 128, 2, 271-95, 479-467, 14, 8);
+ #endif
+
+ DWIN_Frame_AreaCopy(1, 1, 89, 271-188, 479-377-1, LBLX, CLINE(1)); // Temperature >
+ DWIN_Frame_AreaCopy(1, 84, 89, 271-143, 479-380, LBLX, CLINE(2)); // Motion >
+ DWIN_Frame_AreaCopy(1, 131+17, 89, 271- 3, 479-377-1, LBLX, CLINE(3)); // "Store Configuration"
+
+ DWIN_Frame_AreaCopy(1, 26, 104, 271-214, 479-365, LBLX, CLINE(4)); // "Read"
+ DWIN_Frame_AreaCopy(1, 131+51, 89, 271-3, 479-377-1, LBLX+31+3, CLINE(4)); // "Configuration"
+
+ DWIN_Frame_AreaCopy(1, 59, 104, 271-178, 479-365, LBLX, CLINE(5)); // "Reset"
+ DWIN_Frame_AreaCopy(1, 131+51, 89, 271-3, 479-377-1, LBLX+34+3, CLINE(5)); // "Configuration"
+
+ if (CVISI(6))
+ DWIN_Frame_AreaCopy(1, 0, 104, 25, 115, LBLX, CLINE(6)); // Info >
+ }
+
+ if (select_control.now && CVISI(select_control.now))
+ Draw_Menu_Cursor(CSCROL(select_control.now));
+
+ // Draw icons and lines
+ LOOP_L_N(i, 6)
+ if (CVISI(i + 1)) Draw_Menu_Line(CSCROL(i + 1), ICON_Temperature+i);
+
+ Draw_More_Icon(CSCROL(1));
+ Draw_More_Icon(CSCROL(2));
+ if (CVISI(6)) Draw_More_Icon(CSCROL(6));
+}
+
+inline void Draw_Tune_Menu() {
+ Clear_Main_Window();
+
+ if (HMI_flag.language_flag) {
+ DWIN_Frame_AreaCopy(1, 73, 2, 271-171, 479-466, 14, 9);
+
+ DWIN_Frame_AreaCopy(1, 116, 164, 271-100, 479-303, LBLX, MBASE(1));
+ DWIN_Frame_AreaCopy(1, 1, 134, 271-215, 479-333, LBLX, MBASE(2));
+ DWIN_Frame_AreaCopy(1, 58, 134, 271-158, 479-333, LBLX, MBASE(3));
+ DWIN_Frame_AreaCopy(1, 115, 134, 271-101, 479-333, LBLX, MBASE(4));
+ DWIN_Frame_AreaCopy(1, 174, 164, 271-48, 479-302, LBLX, MBASE(5));
+ }
+ else {
+ #ifdef USE_STRING_HEADINGS
+ Draw_Title("Tune"); // TODO: GET_TEXT
+ #else
+ DWIN_Frame_AreaCopy(1, 94, 2, 271-145, 479-467, 14, 9);
+ #endif
+ DWIN_Frame_AreaCopy(1, 1, 179, 271-179, 479-287-2, LBLX, MBASE(1)); // print speed
+ DWIN_Frame_AreaCopy(1, 197, 104, 271-33, 479-365, LBLX, MBASE(2)); // Hotend...
+ DWIN_Frame_AreaCopy(1, 1, 89, 271-188, 479-377-1, LBLX+41+3, MBASE(2)); // ...Temperature
+ DWIN_Frame_AreaCopy(1, 240, 104, 271-7, 479-365, LBLX, MBASE(3)); // Bed...
+ DWIN_Frame_AreaCopy(1, 1, 89, 271-188, 479-377-1, LBLX+24+3, MBASE(3)); // ...Temperature
+ DWIN_Frame_AreaCopy(1, 0, 119, 271-207, 479-347, LBLX, MBASE(4)); // fan speed
+ DWIN_Frame_AreaCopy(1, 93, 179, 271-130, 479-290, LBLX, MBASE(5)); // Z-offset
+ }
+
+ Draw_Back_First(select_tune.now == 0);
+ if (select_tune.now) Draw_Menu_Cursor(select_tune.now);
+
+ Draw_Menu_Line(1, ICON_Speed);
+ Draw_Menu_Line(2, ICON_HotendTemp);
+ Draw_Menu_Line(3, ICON_BedTemp);
+ Draw_Menu_Line(4, ICON_FanSpeed);
+ Draw_Menu_Line(5, ICON_Zoffset);
+
+ DWIN_Draw_IntValue(true,true,0,font8x16,White,Background_black, 3, 216, MBASE(1), feedrate_percentage);
+ DWIN_Draw_IntValue(true,true,0,font8x16,White,Background_black, 3, 216, MBASE(2), thermalManager.temp_hotend[0].target);
+ DWIN_Draw_IntValue(true,true,0,font8x16,White,Background_black, 3, 216, MBASE(3), thermalManager.temp_bed.target);
+ DWIN_Draw_IntValue(true,true,0,font8x16,White,Background_black, 3, 216, MBASE(4), thermalManager.fan_speed[0]);
+ show_plus_or_minus(font8x16, Background_black, 2, 2, 202, MBASE(5), BABY_Z_VAR*100);
+}
+
+inline void draw_max_en(const uint16_t line) {
+ DWIN_Frame_AreaCopy(1, 245, 119, 271-2, 479-350, LBLX, line); // "Max"
+}
+inline void draw_max_accel_en(const uint16_t line) {
+ draw_max_en(line);
+ DWIN_Frame_AreaCopy(1, 1, 135, 271-192, 479-334, LBLX+24+3, line); // "Acceleration"
+}
+inline void draw_speed_en(const uint16_t inset, const uint16_t line) {
+ DWIN_Frame_AreaCopy(1, 184, 119, 224, 479-347, LBLX+inset, line); // "Speed"
+}
+inline void draw_corner_en(const uint16_t line) {
+ DWIN_Frame_AreaCopy(1, 64, 119, 271-165, 479-350, LBLX+24+3, line); // "Corner"
+}
+inline void draw_steps_per_mm(const uint16_t line) {
+ DWIN_Frame_AreaCopy(1, 1, 151, 271-170, 479-318, LBLX, line); // "Steps-per-mm"
+}
+inline void say_x(const uint16_t inset, const uint16_t line) {
+ DWIN_Frame_AreaCopy(1, 95, 104, 271-169, 479-365, LBLX+inset, line); // "X"
+}
+inline void say_y(const uint16_t inset, const uint16_t line) {
+ DWIN_Frame_AreaCopy(1, 104, 104, 271-161, 479-365, LBLX+inset, line); // "Y"
+}
+inline void say_z(const uint16_t inset, const uint16_t line) {
+ DWIN_Frame_AreaCopy(1, 112, 104, 271-151, 479-365, LBLX+inset, line); // "Z"
+}
+inline void say_e(const uint16_t inset, const uint16_t line) {
+ DWIN_Frame_AreaCopy(1, 237, 119, 271-27, 479-350, LBLX+inset, line); // "E"
+}
+
+inline void Draw_Motion_Menu() {
+ Clear_Main_Window();
+
+ if (HMI_flag.language_flag) {
+ DWIN_Frame_AreaCopy(1, 1, 16, 271-243, 479-451, 14, 8);
+
+ DWIN_Frame_AreaCopy(1, 173, 133, 228, 479-332, LBLX, MBASE(1)); // max speed
+ DWIN_Frame_AreaCopy(1, 173, 133, 200, 479-332, LBLX, MBASE(2)); // max...
+ DWIN_Frame_AreaCopy(1, 28, 149, 271-202, 479-318, LBLX+27, MBASE(2)+1); // ...acceleration
+ DWIN_Frame_AreaCopy(1, 173, 133, 200, 479-332, LBLX, MBASE(3)); // max...
+ DWIN_Frame_AreaCopy(1, 1, 180, 271-243, 479-287, LBLX+27, MBASE(3)+1); // ...
+ DWIN_Frame_AreaCopy(1, 202, 133, 228, 479-332, LBLX+54, MBASE(3)); // ...jerk
+ DWIN_Frame_AreaCopy(1, 153, 148, 271-77, 479-318, LBLX, MBASE(4)); // flow ratio
+ }
+ else {
+ #ifdef USE_STRING_HEADINGS
+ Draw_Title("Motion"); // TODO: GET_TEXT_F
+ #else
+ DWIN_Frame_AreaCopy(1, 144, 16, 271-82, 479-453, 14, 8);
+ #endif
+ draw_max_en(MBASE(1)); draw_speed_en(24+3, MBASE(1)); // "Max Speed"
+ draw_max_accel_en(MBASE(2)); // "Max Acceleration"
+ draw_max_en(MBASE(3)); draw_corner_en(MBASE(3)); // "Max Corner"
+ draw_steps_per_mm(MBASE(4)); // "Steps-per-mm"
+ }
+
+ Draw_Back_First(select_motion.now == 0);
+ if (select_motion.now) Draw_Menu_Cursor(select_motion.now);
+
+ LOOP_L_N(i, 4) Draw_Menu_Line(i + 1, ICON_MaxSpeed+i);
+
+ Draw_More_Icon(1);
+ Draw_More_Icon(2);
+ Draw_More_Icon(3);
+ Draw_More_Icon(4);
+}
+
+//
+// Draw Popup Windows
+//
+
+void Popup_Window_Temperature(const bool toohigh) {
+ Clear_Popup_Area();
+ Draw_Popup_Bkgd_105();
+ if (toohigh) {
+ DWIN_ICON_Show(ICON, ICON_TempTooHigh, 102, 165);
+ if (HMI_flag.language_flag) {
+ DWIN_Frame_AreaCopy(1, 103, 371, 237, 479-93, 52, 285);
+ DWIN_Frame_AreaCopy(1, 151, 389, 185, 402, 187, 285);
+ DWIN_Frame_AreaCopy(1, 189, 389, 271-0, 402, 95, 310);
+ }
+ else {
+ DWIN_Draw_String(false,true,font8x16, Font_window, Background_window, 36, 300, (char*)"Nozzle or Bed temperature");
+ DWIN_Draw_String(false,true,font8x16, Font_window, Background_window, 92, 300, (char*)"is too high");
+ }
+ }
+ else {
+ DWIN_ICON_Show(ICON, ICON_TempTooLow, 102, 165);
+ if (HMI_flag.language_flag) {
+ DWIN_Frame_AreaCopy(1, 103, 371, 271-1, 479-93, 52, 285);
+ DWIN_Frame_AreaCopy(1, 189, 389, 271-0, 402, 95, 310);
+ }
+ else {
+ DWIN_Draw_String(false,true,font8x16, Font_window, Background_window, 36, 300, (char*)"Nozzle or Bed temperature");
+ DWIN_Draw_String(false,true,font8x16, Font_window, Background_window, 92, 300, (char*)"is too low");
+ }
+ }
+}
+
+inline void Draw_Popup_Bkgd_60() {
+ DWIN_Draw_Rectangle(1, Background_window, 14, 60, 271-13, 330);
+}
+
+void Popup_Window_ETempTooLow(void) {
+ Clear_Main_Window();
+ Draw_Popup_Bkgd_60();
+ DWIN_ICON_Show(ICON, ICON_TempTooLow, 102, 105);
+ if (HMI_flag.language_flag) {
+ DWIN_Frame_AreaCopy(1, 103, 371, 136, 479-93, 69, 240);
+ DWIN_Frame_AreaCopy(1, 170, 371, 271-1, 479-93, 69+33, 240);
+ DWIN_ICON_Show(ICON, ICON_Confirm_C, 86, 280);
+ }
+ else {
+ DWIN_Draw_String(false,true,font8x16, Font_window, Background_window, 20, 235, (char*)"Nozzle is too cold");
+ DWIN_ICON_Show(ICON, ICON_Confirm_E, 86, 280);
+ }
+}
+
+void Popup_Window_Resume(void) {
+ Clear_Popup_Area();
+ Draw_Popup_Bkgd_105();
+ if (HMI_flag.language_flag) {
+ DWIN_Frame_AreaCopy(1, 160, 338, 271-36, 479-125, 98, 135);
+ DWIN_Frame_AreaCopy(1, 103, 321, 271-0, 479-144, 52, 192);
+ DWIN_ICON_Show(ICON, ICON_Continue_C, 26, 307);
+ DWIN_ICON_Show(ICON, ICON_Cancel_C, 146, 307);
+ }
+ else {
+ DWIN_Draw_String(false,true,font8x16, Font_window, Background_window, 120, 115, (char*)"Tips");
+ DWIN_Draw_String(false,true,font8x16, Font_window, Background_window, 24, 192, (char*)"I see the file stopped");
+ DWIN_Draw_String(false,true,font8x16, Font_window, Background_window, 68, 212, (char*)"unexpectedly last time");
+ DWIN_ICON_Show(ICON, ICON_Continue_E, 26, 307);
+ DWIN_ICON_Show(ICON, ICON_Cancel_E, 146, 307);
+ }
+}
+
+void Popup_Window_Home(void) {
+ Clear_Main_Window();
+ Draw_Popup_Bkgd_60();
+ DWIN_ICON_Show(ICON, ICON_BLTouch, 101, 105);
+ if (HMI_flag.language_flag) {
+ DWIN_Frame_AreaCopy(1, 0, 371, 33, 386, 85, 240);
+ DWIN_Frame_AreaCopy(1, 203, 286, 271, 302, 118, 240);
+ DWIN_Frame_AreaCopy(1, 0, 389, 150, 402, 61, 280);
+ }
+ else {
+ DWIN_Draw_String(false,true,font8x16, Font_window, Background_window, 80, 230, (char*)"Auto homing...");
+ DWIN_Draw_String(false,true,font8x16, Font_window, Background_window, 24, 260, (char*)"Please wait until completed");
+ }
+}
+
+void Popup_Window_Leveling(void) {
+ Clear_Main_Window();
+ Draw_Popup_Bkgd_60();
+ DWIN_ICON_Show(ICON, ICON_AutoLeveling, 101, 105);
+ if (HMI_flag.language_flag) {
+ DWIN_Frame_AreaCopy(1, 0, 371, 100, 386, 84, 240);
+ DWIN_Frame_AreaCopy(1, 0, 389, 150, 402, 61, 280);
+ }
+ else {
+ DWIN_Draw_String(false,true,font8x16, Font_window, Background_window, 76, 230, (char*)"Auto leveling...");
+ DWIN_Draw_String(false,true,font8x16, Font_window, Background_window, 24, 260, (char*)"Please wait until completed");
+ }
+}
+
+void Draw_Select_Highlight(const bool sel) {
+ HMI_flag.select_flag = sel;
+ const uint16_t c1 = sel ? Select_Color : Background_window,
+ c2 = sel ? Background_window : Select_Color;
+ DWIN_Draw_Rectangle(0, c1, 25, 279, 126, 318);
+ DWIN_Draw_Rectangle(0, c1, 24, 278, 127, 319);
+ DWIN_Draw_Rectangle(0, c2, 145, 279, 246, 318);
+ DWIN_Draw_Rectangle(0, c2, 144, 278, 247, 319);
+}
+
+void Popup_window_PauseOrStop(void) {
+ Clear_Main_Window();
+ Draw_Popup_Bkgd_60();
+ if (HMI_flag.language_flag) {
+ if (select_print.now == 1) DWIN_Frame_AreaCopy(1, 237, 338, 269, 356, 98, 150);
+ else if (select_print.now == 2) DWIN_Frame_AreaCopy(1, 221, 320, 253, 336, 98, 150);
+ DWIN_Frame_AreaCopy(1, 220, 304, 264, 319, 130, 150);
+ DWIN_ICON_Show(ICON, ICON_Confirm_C, 26, 280);
+ DWIN_ICON_Show(ICON, ICON_Cancel_C, 146, 280);
+ }
+ else {
+ if (select_print.now == 1) DWIN_Draw_String(false,true,font8x16, Font_window, Background_window, 88, 150, (char*)"Pause print?");
+ else if (select_print.now == 2) DWIN_Draw_String(false,true,font8x16, Font_window, Background_window, 92, 150, (char*)"Stop print?");
+ DWIN_ICON_Show(ICON, ICON_Confirm_E, 26, 280);
+ DWIN_ICON_Show(ICON, ICON_Cancel_E, 146, 280);
+ }
+ Draw_Select_Highlight(true);
+}
+
+void Draw_Printing_Screen(void) {
+ if (HMI_flag.language_flag) {
+ DWIN_Frame_AreaCopy(1, 30, 1, 271-200, 479-465, 14, 9); // Tune
+ DWIN_Frame_AreaCopy(1, 0, 72, 271-208, 479-393, 41, 188); // Pause
+ DWIN_Frame_AreaCopy(1, 65, 72, 271-143, 479-393, 176, 188); // Stop
+ }
+ else {
+ DWIN_Frame_AreaCopy(1, 40, 2, 271-179, 479-464-1, 14, 9); // Tune
+ DWIN_Frame_AreaCopy(1, 0, 44, 271-175, 479-420-1, 41, 188); // Pause
+ DWIN_Frame_AreaCopy(1, 98, 44, 271-119, 479-420-1, 176, 188); // Stop
+ }
+}
+
+void Draw_Print_ProgressBar() {
+ DWIN_ICON_Show(ICON,ICON_Bar, 15, 93);
+ DWIN_Draw_Rectangle(1, BarFill_Color, 16+Percentrecord*240/100, 93, 256, 113);
+ DWIN_Draw_IntValue(true,true,0,font8x16,Percent_Color,Background_black, 2, 117, 133, Percentrecord);
+ DWIN_Draw_String(false,false,font8x16,Percent_Color,Background_black, 117+16, 133, (char*)"%");
+}
+
+void Draw_Print_ProgressElapsed() {
+ duration_t elapsed = print_job_timer.duration(); // print timer
+ DWIN_Draw_IntValue(true,true,1,font8x16,White,Background_black, 2, 42, 212, elapsed.value/3600);
+ DWIN_Draw_String(false,false,font8x16,White,Background_black, 42+16, 212, (char*)":");
+ DWIN_Draw_IntValue(true,true,1,font8x16,White,Background_black, 2, 42+24, 212, (elapsed.value%3600)/60);
+}
+
+void Draw_Print_ProgressRemain() {
+ DWIN_Draw_IntValue(true,true,1,font8x16,White,Background_black, 2, 176, 212, remain_time/3600);
+ DWIN_Draw_String(false,false,font8x16,White,Background_black, 176+16, 212, (char*)":");
+ DWIN_Draw_IntValue(true,true,1,font8x16,White,Background_black, 2, 176+24, 212, (remain_time%3600)/60);
+}
+
+void Goto_PrintProcess(void) {
+ checkkey = PrintProcess;
+
+ Clear_Main_Window();
+ Draw_Printing_Screen();
+
+ ICON_Tune();
+ if (printingIsPaused()) ICON_Continue(); else ICON_Pause();
+ ICON_Stop();
+
+ // Copy into filebuf string before entry
+ char * const name = card.longest_filename();
+ DWIN_Draw_String(false,false,font8x16,White,Background_black, (DWIN_WIDTH-strlen(name)*MENU_CHR_W)/2, 60, name);
+
+ DWIN_ICON_Show(ICON,ICON_PrintTime, 17, 193);
+ DWIN_ICON_Show(ICON,ICON_RemainTime, 150, 191);
+
+ Draw_Print_ProgressBar();
+ Draw_Print_ProgressElapsed();
+ Draw_Print_ProgressRemain();
+}
+
+void Goto_MainMenu(void) {
+ checkkey = MainMenu;
+
+ Clear_Main_Window();
+
+ if (HMI_flag.language_flag)
+ DWIN_Frame_AreaCopy(1, 2, 2, 271-244, 479-465, 14, 9); // "Home"
+ else {
+ #ifdef USE_STRING_HEADINGS
+ Draw_Title("Home"); // TODO: GET_TEXT
+ #else
+ DWIN_Frame_AreaCopy(1, 0, 2, 271-232, 479-467, 14, 9);
+ #endif
+ }
+
+ DWIN_ICON_Show(ICON, ICON_LOGO, 71, 52);
+
+ ICON_Print();
+ ICON_Prepare();
+ ICON_Control();
+ TERN(HAS_LEVELING, ICON_Leveling, ICON_StartInfo)(select_page.now == 3);
+}
+
+inline ENCODER_DiffState get_encoder_state() {
+ const millis_t ms = millis();
+ if (PENDING(ms, Encoder_ms)) return ENCODER_DIFF_NO;
+ const ENCODER_DiffState state = Encoder_ReceiveAnalyze();
+ if (state != ENCODER_DIFF_NO) Encoder_ms = ms + Encoder_wait;
+ return state;
+}
+
+void HMI_Move_X(void) {
+ ENCODER_DiffState encoder_diffState = Encoder_ReceiveAnalyze();
+ if (encoder_diffState != ENCODER_DIFF_NO) {
+ if (encoder_diffState == ENCODER_DIFF_CW)
+ HMI_ValueStruct.Move_X_scale += EncoderRate.encoderMoveValue;
+ else if (encoder_diffState == ENCODER_DIFF_CCW)
+ HMI_ValueStruct.Move_X_scale -= EncoderRate.encoderMoveValue;
+ else if (encoder_diffState == ENCODER_DIFF_ENTER) {
+ checkkey = AxisMove;
+ EncoderRate.encoderRateEnabled = 0;
+ DWIN_Draw_FloatValue(true,true,0,font8x16,White,Background_black, 3, 1, 216, MBASE(1), HMI_ValueStruct.Move_X_scale);
+ if (!planner.is_full()) {
+ // Wait for planner moves to finish!
+ planner.synchronize();
+ planner.buffer_line(current_position, MMM_TO_MMS(HOMING_FEEDRATE_XY), active_extruder);
+ }
+ DWIN_UpdateLCD();
+ return;
+ }
+ if (HMI_ValueStruct.Move_X_scale < X_MIN_POS*MinUnitMult) HMI_ValueStruct.Move_X_scale = X_MIN_POS*MinUnitMult;
+ else if (HMI_ValueStruct.Move_X_scale > X_MAX_POS*MinUnitMult) HMI_ValueStruct.Move_X_scale = X_MAX_POS*MinUnitMult;
+ current_position[X_AXIS] = HMI_ValueStruct.Move_X_scale/10;
+ DWIN_Draw_FloatValue(true,true,0,font8x16,White,Select_Color, 3, 1, 216, MBASE(1), HMI_ValueStruct.Move_X_scale);
+ DWIN_UpdateLCD();
+ }
+}
+
+void HMI_Move_Y(void) {
+ ENCODER_DiffState encoder_diffState = Encoder_ReceiveAnalyze();
+ if (encoder_diffState != ENCODER_DIFF_NO) {
+ if (encoder_diffState == ENCODER_DIFF_CW)
+ HMI_ValueStruct.Move_Y_scale += EncoderRate.encoderMoveValue;
+ else if (encoder_diffState == ENCODER_DIFF_CCW)
+ HMI_ValueStruct.Move_Y_scale -= EncoderRate.encoderMoveValue;
+ else if (encoder_diffState == ENCODER_DIFF_ENTER) {
+ checkkey = AxisMove;
+ EncoderRate.encoderRateEnabled = 0;
+ DWIN_Draw_FloatValue(true,true,0,font8x16,White,Background_black, 3, 1, 216, MBASE(2), HMI_ValueStruct.Move_Y_scale);
+ if (!planner.is_full()) {
+ // Wait for planner moves to finish!
+ planner.synchronize();
+ planner.buffer_line(current_position, MMM_TO_MMS(HOMING_FEEDRATE_XY), active_extruder);
+ }
+ DWIN_UpdateLCD();
+ return;
+ }
+ if (HMI_ValueStruct.Move_Y_scale < Y_MIN_POS*MinUnitMult) HMI_ValueStruct.Move_Y_scale = Y_MIN_POS*MinUnitMult;
+ else if (HMI_ValueStruct.Move_Y_scale > Y_MAX_POS*MinUnitMult) HMI_ValueStruct.Move_Y_scale = Y_MAX_POS*MinUnitMult;
+ current_position[Y_AXIS] = HMI_ValueStruct.Move_Y_scale/10;
+ DWIN_Draw_FloatValue(true,true,0,font8x16,White,Select_Color, 3, 1, 216, MBASE(2), HMI_ValueStruct.Move_Y_scale);
+ DWIN_UpdateLCD();
+ }
+}
+
+void HMI_Move_Z(void) {
+ ENCODER_DiffState encoder_diffState = Encoder_ReceiveAnalyze();
+ if (encoder_diffState != ENCODER_DIFF_NO) {
+ if (encoder_diffState == ENCODER_DIFF_CW)
+ HMI_ValueStruct.Move_Z_scale += EncoderRate.encoderMoveValue;
+ else if (encoder_diffState == ENCODER_DIFF_CCW)
+ HMI_ValueStruct.Move_Z_scale -= EncoderRate.encoderMoveValue;
+ else if (encoder_diffState == ENCODER_DIFF_ENTER) {
+ checkkey = AxisMove;
+ EncoderRate.encoderRateEnabled = 0;
+ DWIN_Draw_FloatValue(true,true,0,font8x16,White,Background_black, 3, 1, 216, MBASE(3), HMI_ValueStruct.Move_Z_scale);
+ if (!planner.is_full()) {
+ // Wait for planner moves to finish!
+ planner.synchronize();
+ planner.buffer_line(current_position, MMM_TO_MMS(HOMING_FEEDRATE_Z), active_extruder);
+ }
+ DWIN_UpdateLCD();
+ return;
+ }
+ if (HMI_ValueStruct.Move_Z_scale < Z_MIN_POS*MinUnitMult)
+ HMI_ValueStruct.Move_Z_scale = Z_MIN_POS*MinUnitMult;
+ else if (HMI_ValueStruct.Move_Z_scale > Z_MAX_POS*MinUnitMult)
+ HMI_ValueStruct.Move_Z_scale = Z_MAX_POS*MinUnitMult;
+ current_position[Z_AXIS] = HMI_ValueStruct.Move_Z_scale/10;
+ DWIN_Draw_FloatValue(true,true,0,font8x16,White,Select_Color, 3, 1, 216, MBASE(3), HMI_ValueStruct.Move_Z_scale);
+ DWIN_UpdateLCD();
+ }
+}
+
+void HMI_Move_E(void) {
+ ENCODER_DiffState encoder_diffState = Encoder_ReceiveAnalyze();
+ if (encoder_diffState != ENCODER_DIFF_NO) {
+ if (encoder_diffState == ENCODER_DIFF_CW)
+ HMI_ValueStruct.Move_E_scale += EncoderRate.encoderMoveValue;
+ else if (encoder_diffState == ENCODER_DIFF_CCW)
+ HMI_ValueStruct.Move_E_scale -= EncoderRate.encoderMoveValue;
+ else if (encoder_diffState == ENCODER_DIFF_ENTER) {
+ checkkey = AxisMove;
+ EncoderRate.encoderRateEnabled = 0;
+ last_E_scale = HMI_ValueStruct.Move_E_scale;
+ show_plus_or_minus(font8x16, Background_black, 3, 1, 216, MBASE(4), HMI_ValueStruct.Move_E_scale);
+ if (!planner.is_full()) {
+ planner.synchronize(); // Wait for planner moves to finish!
+ planner.buffer_line(current_position, MMM_TO_MMS(FEEDRATE_E), active_extruder);
+ }
+ DWIN_UpdateLCD();
+ return;
+ }
+ if ((HMI_ValueStruct.Move_E_scale-last_E_scale) > EXTRUDE_MAXLENGTH*MinUnitMult)
+ HMI_ValueStruct.Move_E_scale = last_E_scale + EXTRUDE_MAXLENGTH*MinUnitMult;
+ else if ((last_E_scale-HMI_ValueStruct.Move_E_scale) > EXTRUDE_MAXLENGTH*MinUnitMult)
+ HMI_ValueStruct.Move_E_scale = last_E_scale - EXTRUDE_MAXLENGTH*MinUnitMult;
+ current_position.e = HMI_ValueStruct.Move_E_scale/10;
+ show_plus_or_minus(font8x16, Select_Color, 3, 1, 216, MBASE(4), HMI_ValueStruct.Move_E_scale);
+ DWIN_UpdateLCD();
+ }
+}
+
+void HMI_Zoffset(void) {
+ ENCODER_DiffState encoder_diffState = Encoder_ReceiveAnalyze();
+ if (encoder_diffState != ENCODER_DIFF_NO) {
+ last_zoffset = zprobe_zoffset;
+ if (encoder_diffState == ENCODER_DIFF_CW)
+ HMI_ValueStruct.offset_value += EncoderRate.encoderMoveValue;
+ else if (encoder_diffState == ENCODER_DIFF_CCW)
+ HMI_ValueStruct.offset_value -= EncoderRate.encoderMoveValue;
+ else if (encoder_diffState == ENCODER_DIFF_ENTER) {
+ EncoderRate.encoderRateEnabled = 0;
+ zprobe_zoffset = HMI_ValueStruct.offset_value / 100;
+ #if HAS_BED_PROBE
+ if (WITHIN(zprobe_zoffset - last_zoffset, Z_PROBE_OFFSET_RANGE_MIN, Z_PROBE_OFFSET_RANGE_MAX))
+ probe.offset.z = zprobe_zoffset;
+ settings.save();
+ #elif ENABLED(BABYSTEPPING)
+ babystep.add_mm(Z_AXIS, (zprobe_zoffset - last_zoffset));
+ #else
+ UNUSED(zprobe_zoffset - last_zoffset);
+ #endif
+
+ if (HMI_ValueStruct.show_mode == -4) {
+ checkkey = Prepare;
+ show_plus_or_minus(font8x16, Background_black, 2, 2, 202, MBASE(4+MROWS-index_prepare), TERN(HAS_LEVELING, probe.offset.z*100, HMI_ValueStruct.offset_value));
+ }
+ else {
+ checkkey = Tune;
+ show_plus_or_minus(font8x16, Background_black, 2, 2, 202, MBASE(5+MROWS-index_tune), TERN(HAS_LEVELING, probe.offset.z*100, HMI_ValueStruct.offset_value));
+ }
+ DWIN_UpdateLCD();
+ return;
+ }
+ NOLESS(HMI_ValueStruct.offset_value, (Z_PROBE_OFFSET_RANGE_MIN)*100);
+ NOMORE(HMI_ValueStruct.offset_value, (Z_PROBE_OFFSET_RANGE_MAX)*100);
+ if (HMI_ValueStruct.show_mode == -4)
+ show_plus_or_minus(font8x16, Select_Color, 2, 2, 202, MBASE(4+MROWS-index_prepare), HMI_ValueStruct.offset_value);
+ else
+ show_plus_or_minus(font8x16, Select_Color, 2, 2, 202, MBASE(5+MROWS-index_tune), HMI_ValueStruct.offset_value);
+ DWIN_UpdateLCD();
+ }
+}
+
+void HMI_ETemp(void) {
+ ENCODER_DiffState encoder_diffState = Encoder_ReceiveAnalyze();
+ if (encoder_diffState != ENCODER_DIFF_NO) {
+ if (encoder_diffState == ENCODER_DIFF_CW)
+ HMI_ValueStruct.E_Temp += EncoderRate.encoderMoveValue;
+ else if (encoder_diffState == ENCODER_DIFF_CCW)
+ HMI_ValueStruct.E_Temp -= EncoderRate.encoderMoveValue;
+ else if (encoder_diffState == ENCODER_DIFF_ENTER) { // return
+ EncoderRate.encoderRateEnabled = 0;
+ if (HMI_ValueStruct.show_mode == -1) { // temperature
+ checkkey = TemperatureID;
+ DWIN_Draw_IntValue(true,true,0,font8x16,White,Background_black, 3, 216, MBASE(1), HMI_ValueStruct.E_Temp);
+ }
+ else if (HMI_ValueStruct.show_mode == -2) {
+ checkkey = PLAPreheat;
+ HMI_ValueStruct.preheat_hotend_temp[0] = HMI_ValueStruct.E_Temp;
+ DWIN_Draw_IntValue(true,true,0,font8x16,White,Background_black, 3, 216, MBASE(1), HMI_ValueStruct.preheat_hotend_temp[0]);
+ return;
+ }
+ else if (HMI_ValueStruct.show_mode == -3) {
+ checkkey = ABSPreheat;
+ HMI_ValueStruct.preheat_hotend_temp[1] = HMI_ValueStruct.E_Temp;
+ DWIN_Draw_IntValue(true,true,0,font8x16,White,Background_black, 3, 216, MBASE(1), HMI_ValueStruct.preheat_hotend_temp[1]);
+ return;
+ }
+ else { // tune
+ checkkey = Tune;
+ DWIN_Draw_IntValue(true,true,0,font8x16,White,Background_black, 3, 216, MBASE(2+MROWS-index_tune), HMI_ValueStruct.E_Temp);
+ }
+ thermalManager.setTargetHotend(HMI_ValueStruct.E_Temp, 0);
+ return;
+ }
+ // E_Temp limit
+ NOMORE(HMI_ValueStruct.E_Temp, max_E_Temp);
+ NOLESS(HMI_ValueStruct.E_Temp, min_E_Temp);
+ // E_Temp value
+ if (HMI_ValueStruct.show_mode >= 0) // tune
+ DWIN_Draw_IntValue(true,true,0,font8x16,White,Select_Color, 3, 216, MBASE(2+MROWS-index_tune), HMI_ValueStruct.E_Temp);
+ else // other page
+ DWIN_Draw_IntValue(true,true,0,font8x16,White,Select_Color, 3, 216, MBASE(1), HMI_ValueStruct.E_Temp);
+ }
+}
+
+void HMI_BedTemp(void) {
+ ENCODER_DiffState encoder_diffState = Encoder_ReceiveAnalyze();
+ if (encoder_diffState != ENCODER_DIFF_NO) {
+ if (encoder_diffState == ENCODER_DIFF_CW)
+ HMI_ValueStruct.Bed_Temp += EncoderRate.encoderMoveValue;
+ else if (encoder_diffState == ENCODER_DIFF_CCW)
+ HMI_ValueStruct.Bed_Temp -= EncoderRate.encoderMoveValue;
+ else if (encoder_diffState == ENCODER_DIFF_ENTER) { // return
+ EncoderRate.encoderRateEnabled = 0;
+ if (HMI_ValueStruct.show_mode == -1) {
+ checkkey = TemperatureID;
+ DWIN_Draw_IntValue(true,true,0,font8x16,White,Background_black, 3, 216, MBASE(2), HMI_ValueStruct.Bed_Temp);
+ }
+ else if (HMI_ValueStruct.show_mode == -2) {
+ checkkey = PLAPreheat;
+ HMI_ValueStruct.preheat_bed_temp[0] = HMI_ValueStruct.Bed_Temp;
+ DWIN_Draw_IntValue(true,true,0,font8x16,White,Background_black, 3, 216, MBASE(2), HMI_ValueStruct.preheat_bed_temp[0]);
+ return;
+ }
+ else if (HMI_ValueStruct.show_mode == -3) {
+ checkkey = ABSPreheat;
+ HMI_ValueStruct.preheat_bed_temp[1] = HMI_ValueStruct.Bed_Temp;
+ DWIN_Draw_IntValue(true,true,0,font8x16,White,Background_black, 3, 216, MBASE(2), HMI_ValueStruct.preheat_bed_temp[1]);
+ return;
+ }
+ else {
+ checkkey = Tune;
+ DWIN_Draw_IntValue(true,true,0,font8x16,White,Background_black, 3, 216, MBASE(3+MROWS-index_tune), HMI_ValueStruct.Bed_Temp);
+ }
+ thermalManager.setTargetBed(HMI_ValueStruct.Bed_Temp);
+ return;
+ }
+ //Bed_Temp limit
+ NOMORE(HMI_ValueStruct.Bed_Temp, max_Bed_Temp);
+ NOLESS(HMI_ValueStruct.Bed_Temp, min_Bed_Temp);
+ //Bed_Temp value
+ if (HMI_ValueStruct.show_mode >= 0) // tune page
+ DWIN_Draw_IntValue(true,true,0,font8x16,White,Select_Color, 3, 216, MBASE(3+MROWS-index_tune), HMI_ValueStruct.Bed_Temp);
+ else // other page
+ DWIN_Draw_IntValue(true,true,0,font8x16,White,Select_Color, 3, 216, MBASE(2), HMI_ValueStruct.Bed_Temp);
+ }
+}
+
+void HMI_FanSpeed(void) {
+ ENCODER_DiffState encoder_diffState = Encoder_ReceiveAnalyze();
+ if (encoder_diffState != ENCODER_DIFF_NO) {
+ if (encoder_diffState == ENCODER_DIFF_CW)
+ HMI_ValueStruct.Fan_speed += EncoderRate.encoderMoveValue;
+ else if (encoder_diffState == ENCODER_DIFF_CCW)
+ HMI_ValueStruct.Fan_speed -= EncoderRate.encoderMoveValue;
+ else if (encoder_diffState == ENCODER_DIFF_ENTER) { // return
+ EncoderRate.encoderRateEnabled = 0;
+ if (HMI_ValueStruct.show_mode == -1) {
+ checkkey = TemperatureID;
+ DWIN_Draw_IntValue(true,true,0,font8x16,White,Background_black, 3, 216, MBASE(3), HMI_ValueStruct.Fan_speed);
+ }
+ else if (HMI_ValueStruct.show_mode == -2) {
+ checkkey = PLAPreheat;
+ HMI_ValueStruct.preheat_fan_speed[0] = HMI_ValueStruct.Fan_speed;
+ DWIN_Draw_IntValue(true,true,0,font8x16,White,Background_black, 3, 216, MBASE(3), HMI_ValueStruct.preheat_fan_speed[0]);
+ return;
+ }
+ else if (HMI_ValueStruct.show_mode == -3) {
+ checkkey = ABSPreheat;
+ HMI_ValueStruct.preheat_fan_speed[1] = HMI_ValueStruct.Fan_speed;
+ DWIN_Draw_IntValue(true,true,0,font8x16,White,Background_black, 3, 216, MBASE(3), HMI_ValueStruct.preheat_fan_speed[1]);
+ return;
+ }
+ else {
+ checkkey = Tune;
+ DWIN_Draw_IntValue(true,true,0,font8x16,White,Background_black, 3, 216, MBASE(4+MROWS-index_tune), HMI_ValueStruct.Fan_speed);
+ }
+ thermalManager.set_fan_speed(0, HMI_ValueStruct.Fan_speed);
+ return;
+ }
+ //Fan_speed limit
+ NOMORE(HMI_ValueStruct.Fan_speed, FanOn);
+ NOLESS(HMI_ValueStruct.Fan_speed, FanOff);
+ // Fan_speed value
+ if (HMI_ValueStruct.show_mode >= 0) // tune page
+ DWIN_Draw_IntValue(true,true,0,font8x16,White,Select_Color, 3, 216, MBASE(4+MROWS-index_tune), HMI_ValueStruct.Fan_speed);
+ else // other page
+ DWIN_Draw_IntValue(true,true,0,font8x16,White,Select_Color, 3, 216, MBASE(3), HMI_ValueStruct.Fan_speed);
+ }
+}
+
+void HMI_PrintSpeed(void) {
+ ENCODER_DiffState encoder_diffState = Encoder_ReceiveAnalyze();
+ if (encoder_diffState != ENCODER_DIFF_NO) {
+ if (encoder_diffState == ENCODER_DIFF_CW)
+ HMI_ValueStruct.print_speed += EncoderRate.encoderMoveValue;
+ else if (encoder_diffState == ENCODER_DIFF_CCW)
+ HMI_ValueStruct.print_speed -= EncoderRate.encoderMoveValue;
+ else if (encoder_diffState == ENCODER_DIFF_ENTER) { // return
+ checkkey = Tune;
+ EncoderRate.encoderRateEnabled = 0;
+ feedrate_percentage = HMI_ValueStruct.print_speed;
+ DWIN_Draw_IntValue(true,true,0,font8x16,White,Background_black, 3, 216, MBASE(1+MROWS-index_tune), HMI_ValueStruct.print_speed);
+ return;
+ }
+ // print_speed limit
+ NOMORE(HMI_ValueStruct.print_speed, max_print_speed);
+ NOLESS(HMI_ValueStruct.print_speed, min_print_speed);
+ // print_speed value
+ DWIN_Draw_IntValue(true,true,0,font8x16,White,Select_Color, 3, 216, MBASE(1+MROWS-index_tune), HMI_ValueStruct.print_speed);
+ }
+}
+
+void HMI_MaxFeedspeedXYZE(void) {
+ ENCODER_DiffState encoder_diffState = Encoder_ReceiveAnalyze();
+ if (encoder_diffState != ENCODER_DIFF_NO) {
+ if (encoder_diffState == ENCODER_DIFF_CW)
+ HMI_ValueStruct.Max_Feedspeed += EncoderRate.encoderMoveValue;
+ else if (encoder_diffState == ENCODER_DIFF_CCW)
+ HMI_ValueStruct.Max_Feedspeed -= EncoderRate.encoderMoveValue;
+ else if (encoder_diffState == ENCODER_DIFF_ENTER) { // return
+ checkkey = MaxSpeed;
+ EncoderRate.encoderRateEnabled = 0;
+ if (HMI_flag.feedspeed_flag == X_AXIS) planner.set_max_feedrate(X_AXIS, HMI_ValueStruct.Max_Feedspeed);
+ else if (HMI_flag.feedspeed_flag == Y_AXIS) planner.set_max_feedrate(Y_AXIS, HMI_ValueStruct.Max_Feedspeed);
+ else if (HMI_flag.feedspeed_flag == Z_AXIS) planner.set_max_feedrate(Z_AXIS, HMI_ValueStruct.Max_Feedspeed);
+ else if (HMI_flag.feedspeed_flag == E_AXIS) planner.set_max_feedrate(E_AXIS, HMI_ValueStruct.Max_Feedspeed);
+ DWIN_Draw_IntValue(true,true,0,font8x16,White,Background_black, 4, 210, MBASE(select_speed.now), HMI_ValueStruct.Max_Feedspeed);
+ return;
+ }
+ //MaxFeedspeed limit
+ if (HMI_flag.feedspeed_flag == X_AXIS) {if (HMI_ValueStruct.Max_Feedspeed > default_max_feedrate[X_AXIS]*2) HMI_ValueStruct.Max_Feedspeed = default_max_feedrate[X_AXIS]*2;}
+ else if (HMI_flag.feedspeed_flag == Y_AXIS) {if (HMI_ValueStruct.Max_Feedspeed > default_max_feedrate[Y_AXIS]*2) HMI_ValueStruct.Max_Feedspeed = default_max_feedrate[Y_AXIS]*2;}
+ else if (HMI_flag.feedspeed_flag == Z_AXIS) {if (HMI_ValueStruct.Max_Feedspeed > default_max_feedrate[Z_AXIS]*2) HMI_ValueStruct.Max_Feedspeed = default_max_feedrate[Z_AXIS]*2;}
+ else if (HMI_flag.feedspeed_flag == E_AXIS) {if (HMI_ValueStruct.Max_Feedspeed > default_max_feedrate[E_AXIS]*2) HMI_ValueStruct.Max_Feedspeed = default_max_feedrate[E_AXIS]*2;}
+ if (HMI_ValueStruct.Max_Feedspeed < min_MaxFeedspeed) HMI_ValueStruct.Max_Feedspeed = min_MaxFeedspeed;
+ //MaxFeedspeed value
+ DWIN_Draw_IntValue(true,true,0,font8x16,White,Select_Color, 4, 210, MBASE(select_speed.now), HMI_ValueStruct.Max_Feedspeed);
+ }
+}
+
+void HMI_MaxAccelerationXYZE(void) {
+ ENCODER_DiffState encoder_diffState = Encoder_ReceiveAnalyze();
+ if (encoder_diffState != ENCODER_DIFF_NO) {
+ if (encoder_diffState == ENCODER_DIFF_CW) HMI_ValueStruct.Max_Acceleration += EncoderRate.encoderMoveValue;
+ else if (encoder_diffState == ENCODER_DIFF_CCW) HMI_ValueStruct.Max_Acceleration -= EncoderRate.encoderMoveValue;
+ else if (encoder_diffState == ENCODER_DIFF_ENTER) { // return
+ checkkey = MaxAcceleration;
+ EncoderRate.encoderRateEnabled = 0;
+ if (HMI_flag.acc_flag == X_AXIS) planner.set_max_acceleration(X_AXIS, HMI_ValueStruct.Max_Acceleration);
+ else if (HMI_flag.acc_flag == Y_AXIS) planner.set_max_acceleration(Y_AXIS, HMI_ValueStruct.Max_Acceleration);
+ else if (HMI_flag.acc_flag == Z_AXIS) planner.set_max_acceleration(Z_AXIS, HMI_ValueStruct.Max_Acceleration);
+ else if (HMI_flag.acc_flag == E_AXIS) planner.set_max_acceleration(E_AXIS, HMI_ValueStruct.Max_Acceleration);
+ DWIN_Draw_IntValue(true,true,0,font8x16,White,Background_black, 4, 210, MBASE(select_acc.now), HMI_ValueStruct.Max_Acceleration);
+ return;
+ }
+ // MaxAcceleration limit
+ if (HMI_flag.acc_flag == X_AXIS) {if (HMI_ValueStruct.Max_Acceleration > default_max_acceleration[X_AXIS]*2) HMI_ValueStruct.Max_Acceleration = default_max_acceleration[X_AXIS]*2;}
+ else if (HMI_flag.acc_flag == Y_AXIS) {if (HMI_ValueStruct.Max_Acceleration > default_max_acceleration[Y_AXIS]*2) HMI_ValueStruct.Max_Acceleration = default_max_acceleration[Y_AXIS]*2;}
+ else if (HMI_flag.acc_flag == Z_AXIS) {if (HMI_ValueStruct.Max_Acceleration > default_max_acceleration[Z_AXIS]*2) HMI_ValueStruct.Max_Acceleration = default_max_acceleration[Z_AXIS]*2;}
+ else if (HMI_flag.acc_flag == E_AXIS) {if (HMI_ValueStruct.Max_Acceleration > default_max_acceleration[E_AXIS]*2) HMI_ValueStruct.Max_Acceleration = default_max_acceleration[E_AXIS]*2;}
+ if (HMI_ValueStruct.Max_Acceleration < min_MaxAcceleration) HMI_ValueStruct.Max_Acceleration = min_MaxAcceleration;
+ // MaxAcceleration value
+ DWIN_Draw_IntValue(true,true,0,font8x16,White,Select_Color, 4, 210, MBASE(select_acc.now), HMI_ValueStruct.Max_Acceleration);
+ }
+}
+
+void HMI_MaxCornerXYZE(void) {
+ ENCODER_DiffState encoder_diffState = Encoder_ReceiveAnalyze();
+ if (encoder_diffState != ENCODER_DIFF_NO) {
+ if (encoder_diffState == ENCODER_DIFF_CW)
+ HMI_ValueStruct.Max_Corner += EncoderRate.encoderMoveValue;
+ else if (encoder_diffState == ENCODER_DIFF_CCW)
+ HMI_ValueStruct.Max_Corner -= EncoderRate.encoderMoveValue;
+ else if (encoder_diffState == ENCODER_DIFF_ENTER) { // return
+ checkkey = MaxCorner;
+ EncoderRate.encoderRateEnabled = 0;
+ if (HMI_flag.corner_flag == X_AXIS) planner.set_max_jerk(X_AXIS, HMI_ValueStruct.Max_Corner/10);
+ else if (HMI_flag.corner_flag == Y_AXIS) planner.set_max_jerk(Y_AXIS, HMI_ValueStruct.Max_Corner/10);
+ else if (HMI_flag.corner_flag == Z_AXIS) planner.set_max_jerk(Z_AXIS, HMI_ValueStruct.Max_Corner/10);
+ else if (HMI_flag.corner_flag == E_AXIS) planner.set_max_jerk(E_AXIS, HMI_ValueStruct.Max_Corner/10);
+ DWIN_Draw_FloatValue(true,true,0,font8x16,White,Background_black, 3, 1, 210, MBASE(select_corner.now), HMI_ValueStruct.Max_Corner);
+ return;
+ }
+ //MaxCorner limit
+ if (HMI_flag.corner_flag == X_AXIS) {if (HMI_ValueStruct.Max_Corner > default_max_jerk[X_AXIS]*2*MinUnitMult) HMI_ValueStruct.Max_Corner = default_max_jerk[X_AXIS]*2*MinUnitMult;}
+ else if (HMI_flag.corner_flag == Y_AXIS) {if (HMI_ValueStruct.Max_Corner > default_max_jerk[Y_AXIS]*2*MinUnitMult) HMI_ValueStruct.Max_Corner = default_max_jerk[Y_AXIS]*2*MinUnitMult;}
+ else if (HMI_flag.corner_flag == Z_AXIS) {if (HMI_ValueStruct.Max_Corner > default_max_jerk[Z_AXIS]*2*MinUnitMult) HMI_ValueStruct.Max_Corner = default_max_jerk[Z_AXIS]*2*MinUnitMult;}
+ else if (HMI_flag.corner_flag == E_AXIS) {if (HMI_ValueStruct.Max_Corner > default_max_jerk[E_AXIS]*2*MinUnitMult) HMI_ValueStruct.Max_Corner = default_max_jerk[E_AXIS]*2*MinUnitMult;}
+ if (HMI_ValueStruct.Max_Corner < min_MaxCorner*MinUnitMult) HMI_ValueStruct.Max_Corner = min_MaxCorner*MinUnitMult;
+ //MaxCorner value
+ DWIN_Draw_FloatValue(true,true,0,font8x16,White,Select_Color, 3, 1, 210, MBASE(select_corner.now), HMI_ValueStruct.Max_Corner);
+ }
+}
+
+void HMI_StepXYZE(void) {
+ ENCODER_DiffState encoder_diffState = Encoder_ReceiveAnalyze();
+ if (encoder_diffState != ENCODER_DIFF_NO) {
+ if (encoder_diffState == ENCODER_DIFF_CW)
+ HMI_ValueStruct.Max_Step += EncoderRate.encoderMoveValue;
+ else if (encoder_diffState == ENCODER_DIFF_CCW)
+ HMI_ValueStruct.Max_Step -= EncoderRate.encoderMoveValue;
+ else if (encoder_diffState == ENCODER_DIFF_ENTER) { // return
+ checkkey = Step;
+ EncoderRate.encoderRateEnabled = 0;
+ if (HMI_flag.step_flag == X_AXIS) planner.settings.axis_steps_per_mm[X_AXIS] = HMI_ValueStruct.Max_Step/10;
+ else if (HMI_flag.step_flag == Y_AXIS) planner.settings.axis_steps_per_mm[Y_AXIS] = HMI_ValueStruct.Max_Step/10;
+ else if (HMI_flag.step_flag == Z_AXIS) planner.settings.axis_steps_per_mm[Z_AXIS] = HMI_ValueStruct.Max_Step/10;
+ else if (HMI_flag.step_flag == E_AXIS) planner.settings.axis_steps_per_mm[E_AXIS] = HMI_ValueStruct.Max_Step/10;
+ DWIN_Draw_FloatValue(true,true,0,font8x16,White,Background_black, 3, 1, 210, MBASE(select_step.now), HMI_ValueStruct.Max_Step);
+ return;
+ }
+ //Step limit
+ if (HMI_flag.step_flag == X_AXIS) {if (HMI_ValueStruct.Max_Step > default_axis_steps_per_unit[X_AXIS]*2*MinUnitMult) HMI_ValueStruct.Max_Step = default_axis_steps_per_unit[X_AXIS]*2*MinUnitMult;}
+ else if (HMI_flag.step_flag == Y_AXIS) {if (HMI_ValueStruct.Max_Step > default_axis_steps_per_unit[Y_AXIS]*2*MinUnitMult) HMI_ValueStruct.Max_Step = default_axis_steps_per_unit[Y_AXIS]*2*MinUnitMult;}
+ else if (HMI_flag.step_flag == Z_AXIS) {if (HMI_ValueStruct.Max_Step > default_axis_steps_per_unit[Z_AXIS]*2*MinUnitMult) HMI_ValueStruct.Max_Step = default_axis_steps_per_unit[Z_AXIS]*2*MinUnitMult;}
+ else if (HMI_flag.step_flag == E_AXIS) {if (HMI_ValueStruct.Max_Step > default_axis_steps_per_unit[E_AXIS]*2*MinUnitMult) HMI_ValueStruct.Max_Step = default_axis_steps_per_unit[E_AXIS]*2*MinUnitMult;}
+ if (HMI_ValueStruct.Max_Step < min_Step) HMI_ValueStruct.Max_Step = min_Step;
+ //Step value
+ DWIN_Draw_FloatValue(true,true,0,font8x16,White,Select_Color, 3, 1, 210, MBASE(select_step.now), HMI_ValueStruct.Max_Step);
+ }
+}
+
+void update_variable(void) {
+ /* Tune page temperature update */
+ if (checkkey == Tune) {
+ if (last_temp_hotend_target != thermalManager.temp_hotend[0].target) {
+ DWIN_Draw_IntValue(true,true,0,font8x16,White,Background_black, 3, 216, MBASE(2+MROWS-index_tune), thermalManager.temp_hotend[0].target);
+ }
+ if (last_temp_bed_target != thermalManager.temp_bed.target) {
+ DWIN_Draw_IntValue(true,true,0,font8x16,White,Background_black, 3, 216, MBASE(3+MROWS-index_tune), thermalManager.temp_bed.target);
+ }
+ if (last_fan_speed != thermalManager.fan_speed[0]) {
+ DWIN_Draw_IntValue(true,true,0,font8x16,White,Background_black, 3, 216, MBASE(4+MROWS-index_tune), thermalManager.fan_speed[0]);
+ last_fan_speed = thermalManager.fan_speed[0];
+ }
+ }
+
+ /* Temperature page temperature update */
+ if (checkkey == TemperatureID) {
+ if (last_temp_hotend_target != thermalManager.temp_hotend[0].target) {
+ DWIN_Draw_IntValue(true,true,0,font8x16,White,Background_black, 3, 216, MBASE(1), thermalManager.temp_hotend[0].target);
+ }
+ if (last_temp_bed_target != thermalManager.temp_bed.target) {
+ DWIN_Draw_IntValue(true,true,0,font8x16,White,Background_black, 3, 216, MBASE(2), thermalManager.temp_bed.target);
+ }
+ if (last_fan_speed != thermalManager.fan_speed[0]) {
+ DWIN_Draw_IntValue(true,true,0,font8x16,White,Background_black, 3, 216, MBASE(3), thermalManager.fan_speed[0]);
+ last_fan_speed = thermalManager.fan_speed[0];
+ }
+ }
+
+ /* Bottom temperature update */
+ if (last_temp_hotend_current != thermalManager.temp_hotend[0].celsius) {
+ DWIN_Draw_IntValue(true,true,0,STAT_FONT,White,Background_black, 3, 33, 382, thermalManager.temp_hotend[0].celsius);
+ last_temp_hotend_current = thermalManager.temp_hotend[0].celsius;
+ }
+ if (last_temp_hotend_target != thermalManager.temp_hotend[0].target) {
+ DWIN_Draw_IntValue(true,true,0,STAT_FONT,White,Background_black, 3, 33+4*STAT_CHR_W+6, 382, thermalManager.temp_hotend[0].target);
+ last_temp_hotend_target = thermalManager.temp_hotend[0].target;
+ }
+ if (last_temp_bed_current != thermalManager.temp_bed.celsius) {
+ DWIN_Draw_IntValue(true,true,0,STAT_FONT,White,Background_black, 3, 178, 382, thermalManager.temp_bed.celsius);
+ last_temp_bed_current = thermalManager.temp_bed.celsius;
+ }
+ if (last_temp_bed_target != thermalManager.temp_bed.target) {
+ DWIN_Draw_IntValue(true,true,0,STAT_FONT,White,Background_black, 3, 178+4*STAT_CHR_W+6, 382, thermalManager.temp_bed.target);
+ last_temp_bed_target = thermalManager.temp_bed.target;
+ }
+ if (last_speed != feedrate_percentage) {
+ DWIN_Draw_IntValue(true,true,0,STAT_FONT,White,Background_black, 3, 33+2*STAT_CHR_W, 429, feedrate_percentage);
+ last_speed = feedrate_percentage;
+ }
+ #if HAS_LEVELING
+ if (last_probe_zoffset != probe.offset.z) {
+ show_plus_or_minus(STAT_FONT, Background_black, 2, 2, 178+STAT_CHR_W, 429, probe.offset.z*100);
+ last_probe_zoffset = probe.offset.z;
+ }
+ #else
+ if (last_zoffset != zprobe_zoffset) {
+ show_plus_or_minus(STAT_FONT, Background_black, 2, 2, 178+STAT_CHR_W, 429, zprobe_zoffset*100);
+ last_zoffset = zprobe_zoffset;
+ }
+ #endif
+}
+
+/**
+ * Read and cache the working directory.
+ *
+ * TODO: New code can follow the pattern of menu_media.cpp
+ * and rely on Marlin caching for performance. No need to
+ * cache files here.
+ *
+ */
+
+#ifndef strcasecmp_P
+#define strcasecmp_P(a, b) strcasecmp((a), (b))
+#endif
+
+inline void make_name_without_ext(char *dst, char *src, int maxlen=MENU_CHAR_LIMIT) {
+ char * const name = card.longest_filename();
+ size_t pos = strlen(name); // index of ending nul
+
+ // For files, remove the extension
+ // which may be .gcode, .gco, or .g
+ if (!card.flag.filenameIsDir)
+ while (pos && src[pos] != '.') pos--; // find last '.' (stop at 0)
+
+ int len = pos; // nul or '.'
+ if (len > maxlen) { // Keep the name short
+ pos = len = maxlen; // move nul down
+ dst[--pos] = '.'; // insert dots
+ dst[--pos] = '.';
+ dst[--pos] = '.';
+ }
+
+ dst[len] = '\0'; // end it
+
+ // Copy down to 0
+ while (pos--) dst[pos] = src[pos];
+}
+
+inline void HMI_SDCardInit(void) { card.cdroot(); }
+
+void MarlinUI::refresh() {
+ // The card was mounted or unmounted
+ // or some other status change occurred
+ //DWIN_lcd_sd_status = false; // On next DWIN_Update
+ //HMI_SDCardUpdate();
+}
+
+#define ICON_Folder ICON_More
+
+char shift_name[LONG_FILENAME_LENGTH + 1];
+int8_t shift_amt; // = 0
+millis_t shift_ms; // = 0
+
+// Init the shift name based on the highlighted item
+inline void Init_Shift_Name() {
+ const bool is_subdir = !card.flag.workDirIsRoot;
+ const int8_t filenum = select_file.now - 1 - is_subdir; // Skip "Back" and ".."
+ if (WITHIN(filenum, 0, card.get_num_Files() - 1)) {
+ card.getfilename_sorted(filenum);
+ char * const name = card.longest_filename();
+ make_name_without_ext(shift_name, name, 100);
+ }
+}
+
+inline void Init_SDItem_Shift() {
+ shift_amt = 0;
+ shift_ms = select_file.now > 0 && strlen(shift_name) > MENU_CHAR_LIMIT
+ ? millis() + 750UL : 0;
+}
+
+/**
+ * Display an SD item, adding a CDUP for subfolders.
+ */
+inline void Draw_SDItem(const uint16_t item, int16_t row=-1) {
+ if (row < 0) row = item + 1 + MROWS-index_file;
+ const bool is_subdir = !card.flag.workDirIsRoot;
+ if (is_subdir && item == 0) {
+ Draw_Menu_Line(row, ICON_Folder, (char*)"..");
+ return;
+ }
+
+ card.getfilename_sorted(item - is_subdir);
+ char * const name = card.longest_filename();
+
+ // Init the current selected name
+ // This is used during scroll drawing
+ if (item == select_file.now - 1) {
+ make_name_without_ext(shift_name, name, 100);
+ Init_SDItem_Shift();
+ }
+
+ char str[strlen(name) + 1];
+
+ make_name_without_ext(str, name);
+
+ Draw_Menu_Line(row, card.flag.filenameIsDir ? ICON_Folder : ICON_File, str);
+}
+
+inline void Draw_SDItem_Shifted(int8_t &shift) {
+ // Limit to the number of chars past the cutoff
+ const size_t len = strlen(shift_name);
+ NOMORE(shift, _MAX((signed)len - MENU_CHAR_LIMIT, 0));
+
+ // Shorten to the available space
+ const size_t lastchar = _MIN((signed)len, shift + MENU_CHAR_LIMIT);
+
+ const char c = shift_name[lastchar];
+ shift_name[lastchar] = '\0';
+
+ const uint8_t row = select_file.now + MROWS-index_file; // skip "Back" and scroll
+ Erase_Menu_Text(row);
+ Draw_Menu_Line(row, 0, &shift_name[shift]);
+
+ shift_name[lastchar] = c;
+}
+
+// Redraw the first set of SD Files
+inline void Redraw_SD_List() {
+ select_file.reset();
+ index_file = MROWS;
+
+ Clear_Menu_Area(); // Leave title bar unchanged
+
+ Draw_Back_First();
+
+ // As many files as will fit
+ LOOP_L_N(i, _MIN(nr_sd_menu_items(), MROWS))
+ Draw_SDItem(i, i + 1);
+
+ Init_SDItem_Shift();
+}
+
+inline void SDCard_Up(void) {
+ card.cdup();
+ Redraw_SD_List();
+ DWIN_lcd_sd_status = false; // On next DWIN_Update
+}
+
+inline void SDCard_Folder(char * const dirname) {
+ card.cd(dirname);
+ Redraw_SD_List();
+ DWIN_lcd_sd_status = false; // On next DWIN_Update
+}
+
+//
+// Watch for media mount / unmount
+//
+void HMI_SDCardUpdate(void) {
+ if (HMI_flag.home_flag) return;
+ if (DWIN_lcd_sd_status != card.isMounted()) {
+ DWIN_lcd_sd_status = card.isMounted();
+ //SERIAL_ECHOLNPAIR("HMI_SDCardUpdate: ", int(DWIN_lcd_sd_status));
+ if (card.isMounted()) {
+ if (checkkey == SelectFile)
+ Redraw_SD_List();
+ }
+ else {
+ // clean file icon
+ if (checkkey == SelectFile)
+ Redraw_SD_List();
+ else if (checkkey == PrintProcess || checkkey == Tune || printingIsActive()) {
+ // TODO: Move card removed abort handling
+ // to CardReader::manage_media.
+ card.flag.abort_sd_printing = true;
+ wait_for_heatup = false;
+ abort_flag = true;
+ }
+ }
+ DWIN_UpdateLCD();
+ }
+}
+
+/* Start */
+void HMI_StartFrame(const bool with_update) {
+ Goto_MainMenu();
+
+ DWIN_Draw_Rectangle(1, Background_black, 0, 360, 272, 479);
+
+ DWIN_ICON_Show(ICON,ICON_HotendTemp, 13, 381);
+ #if HOTENDS > 1
+ //DWIN_ICON_Show(ICON,ICON_HotendTemp, 13, 381);
+ #endif
+ DWIN_ICON_Show(ICON,ICON_BedTemp, 158, 381);
+ DWIN_ICON_Show(ICON,ICON_Speed, 13, 429);
+ DWIN_ICON_Show(ICON,ICON_Zoffset, 158, 428);
+
+ // Draw initial Status Area
+ DWIN_Draw_IntValue(true,true,0,STAT_FONT,White,Background_black, 3, 33, 382, thermalManager.temp_hotend[0].celsius);
+ DWIN_Draw_IntValue(true,true,0,STAT_FONT,White,Background_black, 3, 33+4*STAT_CHR_W+6, 382, thermalManager.temp_hotend[0].target);
+ DWIN_Draw_IntValue(true,true,0,STAT_FONT,White,Background_black, 3, 178, 382, thermalManager.temp_bed.celsius);
+ DWIN_Draw_IntValue(true,true,0,STAT_FONT,White,Background_black, 3, 178+4*STAT_CHR_W+6, 382, thermalManager.temp_bed.target);
+
+ DWIN_Draw_IntValue(true,true,0,STAT_FONT,White,Background_black, 3, 33+2*STAT_CHR_W, 429, feedrate_percentage);
+ DWIN_Draw_String(false,false,STAT_FONT,White,Background_black, 33+(2+3)*STAT_CHR_W+2, 429, (char*)"%");
+
+ show_plus_or_minus(STAT_FONT, Background_black, 2, 2, 178, 429, BABY_Z_VAR * 100);
+ DWIN_Draw_String(false,false,STAT_FONT,White,Background_black, 33+3*STAT_CHR_W+5, 383, (char*)"/");
+ DWIN_Draw_String(false,false,STAT_FONT,White,Background_black, 178+3*STAT_CHR_W+5, 383, (char*)"/");
+
+ if (with_update) {
+ DWIN_UpdateLCD();
+ delay(5);
+ }
+}
+
+inline void Draw_Info_Menu() {
+ Clear_Main_Window();
+
+ DWIN_Draw_String(false,false,font8x16,White,Background_black, (DWIN_WIDTH-strlen(MACHINE_SIZE)*MENU_CHR_W)/2, 122, (char*)MACHINE_SIZE);
+ DWIN_Draw_String(false,false,font8x16,White,Background_black, (DWIN_WIDTH-strlen(SHORT_BUILD_VERSION)*MENU_CHR_W)/2, 195, (char*)SHORT_BUILD_VERSION);
+
+ if (HMI_flag.language_flag) {
+ DWIN_Frame_AreaCopy(1, 30, 17, 271-214, 479-450, 14, 8);
+
+ DWIN_Frame_AreaCopy(1, 197, 149, 271-19, 479-318, 108, 102);
+ DWIN_Frame_AreaCopy(1, 1, 164, 271-215, 479-303, 108, 175);
+ DWIN_Frame_AreaCopy(1, 58, 164, 271-158, 479-303, 105, 248);
+ DWIN_Draw_String(false,false,font8x16,White,Background_black, (DWIN_WIDTH-strlen(CORP_WEBSITE_C)*MENU_CHR_W)/2, 268, (char*)CORP_WEBSITE_C);
+ }
+ else {
+ #ifdef USE_STRING_HEADINGS
+ Draw_Title("Info"); // TODO: GET_TEXT_F
+ #else
+ DWIN_Frame_AreaCopy(1, 190, 16, 271-56, 479-453, 14, 8);
+ #endif
+
+ DWIN_Frame_AreaCopy(1, 120, 150, 146, 479-318, 124, 102);
+ DWIN_Frame_AreaCopy(1, 146, 151, 271-17, 479-318, 82, 175);
+ DWIN_Frame_AreaCopy(1, 0, 165, 271-177, 479-304, 89, 248);
+ DWIN_Draw_String(false,false,font8x16,White,Background_black, (DWIN_WIDTH-strlen(CORP_WEBSITE_E)*MENU_CHR_W)/2, 268, (char*)CORP_WEBSITE_E);
+ }
+
+ Draw_Back_First();
+ LOOP_L_N(i, 3) {
+ DWIN_ICON_Show(ICON,ICON_PrintSize+i, 26, 99+i*73);
+ DWIN_Draw_Line(Line_Color, 16, MBASE(2)+i*73, 256, 156+i*73);
+ }
+}
+
+inline void Draw_Print_File_Menu() {
+ Clear_Title_Bar();
+
+ if (HMI_flag.language_flag)
+ DWIN_Frame_AreaCopy(1, 0, 31, 271-216, 479-435, 14, 8);
+ else {
+ #ifdef USE_STRING_HEADINGS
+ Draw_Title("Print file"); // TODO: GET_TEXT_F
+ #else
+ DWIN_Frame_AreaCopy(1, 52, 31, 271-134, 479-438, 14, 8); // "Print file"
+ #endif
+ }
+
+ Redraw_SD_List();
+}
+
+/* Main Process */
+void HMI_MainMenu(void) {
+ ENCODER_DiffState encoder_diffState = get_encoder_state();
+ if (encoder_diffState == ENCODER_DIFF_NO) return;
+
+ if (encoder_diffState == ENCODER_DIFF_CW) {
+ if (select_page.inc(3)) {
+ switch (select_page.now) {
+ case 0: ICON_Print(); break;
+ case 1: ICON_Print(); ICON_Prepare(); break;
+ case 2: ICON_Prepare(); ICON_Control(); break;
+ case 3: ICON_Control(); TERN(HAS_LEVELING, ICON_Leveling, ICON_StartInfo)(1); break;
+ }
+ }
+ }
+ else if (encoder_diffState == ENCODER_DIFF_CCW) {
+ if (select_page.dec()) {
+ switch (select_page.now) {
+ case 0: ICON_Print(); ICON_Prepare(); break;
+ case 1: ICON_Prepare(); ICON_Control(); break;
+ case 2: ICON_Control(); TERN(HAS_LEVELING, ICON_Leveling, ICON_StartInfo)(0); break;
+ case 3: TERN(HAS_LEVELING, ICON_Leveling, ICON_StartInfo)(1); break;
+ }
+ }
+ }
+ else if (encoder_diffState == ENCODER_DIFF_ENTER) {
+ switch (select_page.now) {
+ /* Print File */
+ case 0:
+ checkkey = SelectFile;
+ Draw_Print_File_Menu();
+ break;
+
+ /* Prepare */
+ case 1:
+ checkkey = Prepare;
+ select_prepare.reset();
+ index_prepare = MROWS;
+ Draw_Prepare_Menu();
+ break;
+
+ /* Control */
+ case 2:
+ checkkey = Control;
+ select_control.reset();
+ index_control = MROWS;
+ Draw_Control_Menu();
+ break;
+
+ /* Leveling */
+ case 3:
+ #if HAS_LEVELING
+ checkkey = Leveling;
+ HMI_Leveling();
+ #else
+ checkkey = Info;
+ Draw_Info_Menu();
+ #endif
+ break;
+ }
+ }
+ DWIN_UpdateLCD();
+}
+
+/* Select (and Print) File */
+void HMI_SelectFile(void) {
+ ENCODER_DiffState encoder_diffState = get_encoder_state();
+
+ const uint16_t hasUpDir = !card.flag.workDirIsRoot;
+
+ if (encoder_diffState == ENCODER_DIFF_NO) {
+ if (shift_ms && select_file.now >= 1 + hasUpDir) {
+ // Scroll selected filename every second
+ const millis_t ms = millis();
+ if (ELAPSED(ms, shift_ms)) {
+ const bool was_reset = shift_amt < 0;
+ shift_ms = ms + 375UL + was_reset * 250UL; // ms per character
+ int8_t shift_new = shift_amt + 1; // Try to shift by...
+ Draw_SDItem_Shifted(shift_new); // Draw the item
+ if (!was_reset && shift_new == 0) // Was it limited to 0?
+ shift_ms = 0; // No scrolling needed
+ else if (shift_new == shift_amt) // Scroll reached the end
+ shift_new = -1; // Reset
+ shift_amt = shift_new; // Set new scroll
+ }
+ }
+ return;
+ }
+
+ // First pause is long. Easy.
+ // On reset, long pause must be after 0.
+
+ const uint16_t fullCnt = nr_sd_menu_items();
+
+ if (encoder_diffState == ENCODER_DIFF_CW && fullCnt) {
+ if (select_file.inc(fullCnt)) {
+ const uint8_t itemnum = select_file.now - 1; // -1 for "Back"
+ if (shift_ms) { // If line was shifted
+ Erase_Menu_Text(select_file.now - 1 + MROWS-index_file); // Erase and
+ Draw_SDItem(itemnum - 1); // redraw
+ }
+ if (select_file.now > MROWS && select_file.now > index_file) { // Cursor past the bottom
+ index_file = select_file.now; // New bottom line
+ Scroll_Menu(DWIN_SCROLL_UP);
+ Draw_SDItem(itemnum, MROWS); // Draw and init the shift name
+ }
+ else {
+ Move_Highlight(1, select_file.now+MROWS-index_file); // Just move highlight
+ Init_Shift_Name(); // ...and init the shift name
+ }
+ Init_SDItem_Shift();
+ }
+ }
+ else if (encoder_diffState == ENCODER_DIFF_CCW && fullCnt) {
+ if (select_file.dec()) {
+ const uint8_t itemnum = select_file.now - 1; // -1 for "Back"
+ if (shift_ms) { // If line was shifted
+ Erase_Menu_Text(select_file.now + 1 + MROWS-index_file); // Erase and
+ Draw_SDItem(itemnum + 1); // redraw
+ }
+ if (select_file.now < index_file - MROWS) { // Cursor past the top
+ index_file--; // New bottom line
+ Scroll_Menu(DWIN_SCROLL_DOWN);
+ if (index_file == MROWS) {
+ Draw_Back_First();
+ shift_ms = 0;
+ }
+ else
+ Draw_SDItem(itemnum, 0); // Draw the item (and init shift name)
+ }
+ else {
+ Move_Highlight(-1, select_file.now+MROWS-index_file); // Just move highlight
+ Init_Shift_Name(); // ...and init the shift name
+ }
+ Init_SDItem_Shift(); // Reset left. Init timer.
+ }
+ }
+ else if (encoder_diffState == ENCODER_DIFF_ENTER) {
+ if (select_file.now == 0) {
+ /* back */
+ select_page.set(0);
+ Goto_MainMenu();
+ }
+ else if (hasUpDir && select_file.now == 1) {
+ /* CDUP */
+ SDCard_Up();
+ goto HMI_SelectFileExit;
+ }
+ else {
+ const uint16_t filenum = select_file.now - 1 - hasUpDir;
+ card.getfilename_sorted(filenum);
+
+ // Enter that folder!
+ if (card.flag.filenameIsDir) {
+ SDCard_Folder(card.filename);
+ goto HMI_SelectFileExit;
+ }
+
+ // Reset highlight for next entry
+ select_print.reset();
+ select_file.reset();
+
+ // Start choice and print SD file
+ HMI_flag.heat_flag = 1;
+ HMI_flag.print_finish = 0;
+ HMI_ValueStruct.show_mode = 0;
+
+ card.openAndPrintFile(card.filename);
+
+ #if FAN_COUNT > 0
+ // All fans on for Ender 3 v2 ?
+ // The slicer should manage this for us.
+ //for (uint8_t i = 0; i < FAN_COUNT; i++)
+ // thermalManager.fan_speed[i] = FanOn;
+ #endif
+
+ Goto_PrintProcess();
+ }
+ }
+ HMI_SelectFileExit:
+ DWIN_UpdateLCD();
+}
+
+/* Printing */
+void HMI_Printing(void) {
+ ENCODER_DiffState encoder_diffState = get_encoder_state();
+ if (encoder_diffState == ENCODER_DIFF_NO) return;
+
+ if (HMI_flag.confirm_flag) {
+ if (encoder_diffState == ENCODER_DIFF_ENTER) {
+ HMI_flag.confirm_flag = 0;
+ abort_flag = 1;
+ }
+ return;
+ }
+
+ // Avoid flicker by updating only the previous menu
+ if (encoder_diffState == ENCODER_DIFF_CW) {
+ if (select_print.inc(2)) {
+ switch (select_print.now) {
+ case 0: ICON_Tune(); break;
+ case 1:
+ ICON_Tune();
+ if (printingIsPaused()) ICON_Continue(); else ICON_Pause();
+ break;
+ case 2:
+ if (printingIsPaused()) ICON_Continue(); else ICON_Pause();
+ ICON_Stop();
+ break;
+ }
+ }
+ }
+ else if (encoder_diffState == ENCODER_DIFF_CCW) {
+ if (select_print.dec()) {
+ switch (select_print.now) {
+ case 0:
+ ICON_Tune();
+ if (printingIsPaused()) ICON_Continue(); else ICON_Pause();
+ break;
+ case 1:
+ if (printingIsPaused()) ICON_Continue(); else ICON_Pause();
+ ICON_Stop();
+ break;
+ case 2: ICON_Stop(); break;
+ }
+ }
+ }
+ else if (encoder_diffState == ENCODER_DIFF_ENTER) {
+ switch (select_print.now) {
+ case 0: // setting
+ checkkey = Tune;
+ HMI_ValueStruct.show_mode = 0;
+ select_tune.reset();
+ index_tune = 5;
+ Draw_Tune_Menu();
+ break;
+ case 1: // pause
+ /* pause */
+ if (HMI_flag.pause_flag) {
+ ICON_Pause();
+
+ char cmd[40];
+ cmd[0] = '\0';
+
+ #if ENABLED(PAUSE_HEAT)
+ if (tempbed) sprintf_P(cmd, PSTR("M190 S%i\n"), tempbed);
+ if (temphot) sprintf_P(&cmd[strlen(cmd)], PSTR("M109 S%i\n"), temphot);
+ #endif
+
+ strcat_P(cmd, PSTR("M24"));
+ queue.inject(cmd);
+ }
+ else {
+ HMI_flag.select_flag = 1;
+ checkkey = Print_window;
+ Popup_window_PauseOrStop();
+ }
+ break;
+
+ case 2: // stop
+ /* stop */
+ HMI_flag.select_flag = 1;
+ checkkey = Print_window;
+ Popup_window_PauseOrStop();
+ break;
+
+ default:
+ break;
+ }
+ }
+ DWIN_UpdateLCD();
+}
+
+/* pause and stop window */
+void HMI_PauseOrStop(void) {
+ ENCODER_DiffState encoder_diffState = get_encoder_state();
+ if (encoder_diffState == ENCODER_DIFF_NO) return;
+
+ if (encoder_diffState == ENCODER_DIFF_CW)
+ Draw_Select_Highlight(false);
+ else if (encoder_diffState == ENCODER_DIFF_CCW)
+ Draw_Select_Highlight(true);
+ else if (encoder_diffState == ENCODER_DIFF_ENTER) {
+ if (select_print.now == 1) { // pause window
+ if (HMI_flag.select_flag) {
+ pause_action_flag = 1;
+ ICON_Continue();
+ #if ENABLED(POWER_LOSS_RECOVERY)
+ if (recovery.enabled) recovery.save(true);
+ #endif
+ queue.inject_P(PSTR("M25"));
+ }
+ else {
+ // cancel pause
+ }
+ Goto_PrintProcess();
+ }
+ else if (select_print.now == 2) { // stop window
+ if (HMI_flag.select_flag) {
+ wait_for_heatup = false; // Stop waiting for heater
+
+ #if 0
+ // TODO: In ExtUI or MarlinUI add a common stop event
+ //card.flag.abort_sd_printing = true;
+ #else
+ checkkey = Back_Main;
+ // Wait for planner moves to finish!
+ if (HMI_flag.home_flag) planner.synchronize();
+ card.endFilePrint();
+ #ifdef ACTION_ON_CANCEL
+ host_action_cancel();
+ #endif
+ #ifdef EVENT_GCODE_SD_STOP
+ Popup_Window_Home();
+ queue.inject_P(PSTR(EVENT_GCODE_SD_STOP)); // For Ender 3 "G28 X Y"
+ #endif
+ abort_flag = true;
+ #endif
+ }
+ else
+ Goto_PrintProcess(); // cancel stop
+ }
+ }
+ DWIN_UpdateLCD();
+}
+
+inline void Draw_Move_Menu() {
+ Clear_Main_Window();
+
+ if (HMI_flag.language_flag) {
+ DWIN_Frame_AreaCopy(1, 192, 1, 271-38, 479-465, 14, 8);
+
+ DWIN_Frame_AreaCopy(1, 58, 118, 271-165, 479-347, LBLX, MBASE(1));
+ DWIN_Frame_AreaCopy(1, 109, 118, 271-114, 479-347, LBLX, MBASE(2));
+ DWIN_Frame_AreaCopy(1, 160, 118, 271-62, 479-347, LBLX, MBASE(3));
+ DWIN_Frame_AreaCopy(1, 212, 118, 271-18, 479-348, LBLX, MBASE(4));
+ }
+ else {
+ #ifdef USE_STRING_HEADINGS
+ Draw_Title("Move"); // TODO: GET_TEXT_F
+ #else
+ DWIN_Frame_AreaCopy(1, 231, 2, 271-6, 479-467, 14, 8);
+ #endif
+ draw_move_en(MBASE(1)); say_x(33+3, MBASE(1)); // "Move X"
+ draw_move_en(MBASE(2)); say_y(33+3, MBASE(2)); // "Move Y"
+ draw_move_en(MBASE(3)); say_z(33+3, MBASE(3)); // "Move Z"
+ DWIN_Frame_AreaCopy(1, 123, 192, 271-95, 479-277, LBLX, MBASE(4)); // "Extruder"
+ }
+
+ Draw_Back_First(select_axis.now == 0);
+ if (select_axis.now) Draw_Menu_Cursor(select_axis.now);
+
+ LOOP_L_N(i, MROWS) Draw_Menu_Line(i + 1, ICON_MoveX+i);
+}
+
+/* Prepare */
+void HMI_Prepare(void) {
+ ENCODER_DiffState encoder_diffState = get_encoder_state();
+ if (encoder_diffState == ENCODER_DIFF_NO) return;
+
+ // Avoid flicker by updating only the previous menu
+ if (encoder_diffState == ENCODER_DIFF_CW) {
+ if (select_prepare.inc(8)) {
+ if (select_prepare.now > MROWS && select_prepare.now > index_prepare) {
+ index_prepare = select_prepare.now;
+
+ // Scroll up and draw a blank bottom line
+ Scroll_Menu(DWIN_SCROLL_UP);
+ Draw_Menu_Icon(MROWS, ICON_Axis+select_prepare.now-1);
+
+ // Draw "More" icon for sub-menus
+ if (index_prepare < 7) Draw_More_Icon(MROWS-index_prepare+1);
+
+ if (index_prepare == 6) Prepare_Item_ABS(MROWS);
+ else if (index_prepare == 7) Prepare_Item_Cool(MROWS);
+ else if (index_prepare == 8) Prepare_Item_Lang(MROWS);
+ }
+ else
+ Move_Highlight(1, select_prepare.now+MROWS-index_prepare);
+ }
+ }
+ else if (encoder_diffState == ENCODER_DIFF_CCW) {
+ if (select_prepare.dec()) {
+ if (select_prepare.now < index_prepare-MROWS) {
+ index_prepare--;
+ Scroll_Menu(DWIN_SCROLL_DOWN);
+
+ if (index_prepare == MROWS)
+ Draw_Back_First();
+ else
+ Draw_Menu_Line(0, ICON_Axis+select_prepare.now-1);
+
+ if (index_prepare < 7) Draw_More_Icon(MROWS-index_prepare+1);
+
+ if (index_prepare == 6) Prepare_Item_Move(0);
+ else if (index_prepare == 7) Prepare_Item_Disable(0);
+ else if (index_prepare == 8) Prepare_Item_Home(0);
+ }
+ else
+ Move_Highlight(-1, select_prepare.now+MROWS-index_prepare);
+ }
+ }
+ else if (encoder_diffState == ENCODER_DIFF_ENTER) {
+ switch (select_prepare.now) {
+ case 0: // back
+ select_page.set(1);
+ Goto_MainMenu();
+ break;
+ case 1: // axis move
+ checkkey = AxisMove;
+ select_axis.reset();
+ Draw_Move_Menu();
+
+ queue.inject_P(PSTR("G92 E0"));
+ current_position.e = HMI_ValueStruct.Move_E_scale = 0;
+ DWIN_Draw_FloatValue(true,true,0,font8x16,White,Background_black, 3, 1, 216, MBASE(1), current_position[X_AXIS]*MinUnitMult);
+ DWIN_Draw_FloatValue(true,true,0,font8x16,White,Background_black, 3, 1, 216, MBASE(2), current_position[Y_AXIS]*MinUnitMult);
+ DWIN_Draw_FloatValue(true,true,0,font8x16,White,Background_black, 3, 1, 216, MBASE(3), current_position[Z_AXIS]*MinUnitMult);
+ show_plus_or_minus(font8x16, Background_black, 3, 1, 216, MBASE(4), current_position.e*MinUnitMult);
+ break;
+ case 2: // close motion
+ queue.inject_P(PSTR("M84"));
+ break;
+ case 3: // homing
+ checkkey = Last_Prepare;
+ index_prepare = MROWS;
+ queue.inject_P(PSTR("G28")); // G28 will set home_flag
+ Popup_Window_Home();
+ break;
+ case 4: // Z-offset
+ #if HAS_LEVELING
+ checkkey = Homeoffset;
+ HMI_ValueStruct.show_mode = -4;
+ HMI_ValueStruct.offset_value = probe.offset.z * 100;
+ show_plus_or_minus(font8x16, Select_Color, 2, 2, 202, MBASE(4+MROWS-index_prepare), HMI_ValueStruct.offset_value);
+ EncoderRate.encoderRateEnabled = 1;
+ #else
+ // Apply workspace offset, making the current position 0,0,0
+ queue.inject_P(PSTR("G92 X0 Y0 Z0"));
+ buzzer.tone(100, 659);
+ buzzer.tone(100, 698);
+ #endif
+ break;
+ case 5: // PLA preheat
+ thermalManager.setTargetHotend(HMI_ValueStruct.preheat_hotend_temp[0], 0);
+ thermalManager.setTargetBed(HMI_ValueStruct.preheat_bed_temp[0]);
+ thermalManager.set_fan_speed(0, HMI_ValueStruct.preheat_fan_speed[0]);
+ break;
+ case 6: // ABS preheat
+ thermalManager.setTargetHotend(HMI_ValueStruct.preheat_hotend_temp[1], 0);
+ thermalManager.setTargetBed(HMI_ValueStruct.preheat_bed_temp[1]);
+ thermalManager.set_fan_speed(0, HMI_ValueStruct.preheat_fan_speed[1]);
+ break;
+ case 7: // cool
+ thermalManager.zero_fan_speeds();
+ thermalManager.disable_all_heaters();
+ break;
+ case 8: // language
+ /* select language */
+ HMI_flag.language_flag = !HMI_flag.language_flag;
+ if (HMI_flag.language_flag) {
+ set_chinese_to_eeprom();
+ DWIN_JPG_CacheTo1(Language_Chinese);
+ }
+ else {
+ set_english_to_eeprom();
+ DWIN_JPG_CacheTo1(Language_English);
+ }
+ Draw_Prepare_Menu();
+ break;
+ default:
+ break;
+ }
+ }
+ DWIN_UpdateLCD();
+}
+
+void Draw_Temperature_Menu() {
+ Clear_Main_Window();
+
+ if (HMI_flag.language_flag) {
+ DWIN_Frame_AreaCopy(1, 236, 2, 271-8, 479-466, 14, 8);
+
+ DWIN_Frame_AreaCopy(1, 1, 134, 271-215, 479-333, LBLX, MBASE(1));
+ DWIN_Frame_AreaCopy(1, 58, 134, 271-158, 479-333, LBLX, MBASE(2));
+ DWIN_Frame_AreaCopy(1, 115, 134, 271-101, 479-333, LBLX, MBASE(3));
+ DWIN_Frame_AreaCopy(1, 100, 89, 271-93, 479-378, LBLX, MBASE(4));
+ DWIN_Frame_AreaCopy(1, 180, 89, 271-11, 479-379, LBLX, MBASE(5));
+ }
+ else {
+ #ifdef USE_STRING_HEADINGS
+ Draw_Title("Temperature"); // TODO: GET_TEXT_F
+ #else
+ DWIN_Frame_AreaCopy(1, 56, 16, 271-130, 479-450-1, 14, 8);
+ #endif
+
+ DWIN_Frame_AreaCopy(1, 197, 104, 271-33, 479-365, LBLX, MBASE(1)); // Nozzle...
+ DWIN_Frame_AreaCopy(1, 1, 89, 271-188, 479-377-1, LBLX+41+3, MBASE(1)); // ...Temperature
+ DWIN_Frame_AreaCopy(1, 240, 104, 271-7, 479-365, LBLX, MBASE(2)); // Bed...
+ DWIN_Frame_AreaCopy(1, 1, 89, 271-188, 479-377-1, LBLX+24+3, MBASE(2)); // ...Temperature
+ DWIN_Frame_AreaCopy(1, 0, 119, 271-207, 479-347, LBLX, MBASE(3)); // Fan speed
+ DWIN_Frame_AreaCopy(1, 107, 76, 271-115, 479-393, LBLX, MBASE(4)); // Preheat...
+ DWIN_Frame_AreaCopy(1, 157, 76, 181, 479-393, LBLX+49+3, MBASE(4)); // ...PLA
+ DWIN_Frame_AreaCopy(1, 131, 119, 271-89, 479-347, LBLX+49+24+6, MBASE(4)); // PLA setting
+ DWIN_Frame_AreaCopy(1, 107, 76, 271-115, 479-393, LBLX, MBASE(5)); // Preheat...
+ DWIN_Frame_AreaCopy(1, 172, 76, 198, 479-393, LBLX+49+3, MBASE(5)); // ...ABS
+ DWIN_Frame_AreaCopy(1, 131, 119, 271-89, 479-347, LBLX+49+3+26+3, MBASE(5)); // ABS setting
+ }
+
+ Draw_Back_First(select_temp.now == 0);
+ if (select_temp.now) Draw_Menu_Cursor(select_temp.now);
+
+ LOOP_L_N(i, 5) Draw_Menu_Line(i + 1, ICON_SetEndTemp+i);
+
+ Draw_More_Icon(4);
+ Draw_More_Icon(5);
+
+ DWIN_Draw_IntValue(true,true,0,font8x16,White,Background_black, 3, 216, MBASE(1), thermalManager.temp_hotend[0].target);
+ DWIN_Draw_IntValue(true,true,0,font8x16,White,Background_black, 3, 216, MBASE(2), thermalManager.temp_bed.target);
+ DWIN_Draw_IntValue(true,true,0,font8x16,White,Background_black, 3, 216, MBASE(3), thermalManager.fan_speed[0]);
+}
+
+/* Control */
+void HMI_Control(void) {
+ ENCODER_DiffState encoder_diffState = get_encoder_state();
+ if (encoder_diffState == ENCODER_DIFF_NO) return;
+
+ // Avoid flicker by updating only the previous menu
+ if (encoder_diffState == ENCODER_DIFF_CW) {
+ #define CONTROL_ITEMS (5 + ENABLED(HAS_LEVELING))
+ if (select_control.inc(CONTROL_ITEMS)) {
+ if (select_control.now > MROWS && select_control.now > index_control) {
+ index_control = select_control.now;
+ Scroll_Menu(DWIN_SCROLL_UP);
+ Draw_Menu_Icon(MROWS, ICON_Temperature+select_control.now-1);
+ Draw_More_Icon(1+MROWS-index_control); // Temperature >
+ Draw_More_Icon(2+MROWS-index_control); // Motion >
+ if (index_control > MROWS) {
+ Draw_More_Icon(6+MROWS-index_control); // Info >
+ if (HMI_flag.language_flag)
+ DWIN_Frame_AreaCopy(1, 231, 104, 271-13, 479-363, LBLX, MBASE(5));
+ else
+ DWIN_Frame_AreaCopy(1, 0, 104, 271-247, 479-365, LBLX, MBASE(5));
+ }
+ }
+ else
+ Move_Highlight(1, select_control.now+MROWS-index_control);
+ }
+ }
+ else if (encoder_diffState == ENCODER_DIFF_CCW) {
+ if (select_control.dec()) {
+ if (select_control.now < index_control-MROWS) {
+ index_control--;
+ Scroll_Menu(DWIN_SCROLL_DOWN);
+ if (index_control == MROWS)
+ Draw_Back_First();
+ else
+ Draw_Menu_Line(0, ICON_Temperature+select_control.now-1);
+ Draw_More_Icon(0+MROWS-index_control+1); // Temperature >
+ Draw_More_Icon(1+MROWS-index_control+1); // Motion >
+ }
+ else
+ Move_Highlight(-1, select_control.now+MROWS-index_control);
+ }
+ }
+ else if (encoder_diffState == ENCODER_DIFF_ENTER) {
+ switch (select_control.now) {
+ case 0: // back
+ select_page.set(2);
+ Goto_MainMenu();
+ break;
+ case 1: // temperature
+ checkkey = TemperatureID;
+ HMI_ValueStruct.show_mode = -1;
+ select_temp.reset();
+ Draw_Temperature_Menu();
+ break;
+ case 2: // motion
+ checkkey = Motion;
+ select_motion.reset();
+ Draw_Motion_Menu();
+ break;
+ case 3: // write EEPROM
+ if (settings.save()) {
+ buzzer.tone(100, 659);
+ buzzer.tone(100, 698);
+ }
+ else buzzer.tone(20, 440);
+ break;
+ case 4: // read EEPROM
+ if (settings.load()) {
+ buzzer.tone(100, 659);
+ buzzer.tone(100, 698);
+ }
+ else buzzer.tone(20, 440);
+ break;
+ case 5: // resume EEPROM
+ settings.reset();
+ #if HAS_BUZZER
+ buzzer.tone(100, 659);
+ buzzer.tone(100, 698);
+ #endif
+ break;
+ case 6: // info
+ checkkey = Info;
+ Draw_Info_Menu();
+ break;
+ default:
+ break;
+ }
+ }
+ DWIN_UpdateLCD();
+}
+
+/* Leveling */
+void HMI_Leveling(void) {
+ Popup_Window_Leveling();
+ DWIN_UpdateLCD();
+ queue.inject_P(PSTR("G29"));
+}
+
+/* Axis Move */
+void HMI_AxisMove(void) {
+ ENCODER_DiffState encoder_diffState = get_encoder_state();
+ if (encoder_diffState == ENCODER_DIFF_NO) return;
+
+ // popup window resume
+ if (HMI_flag.ETempTooLow_flag) {
+ if (encoder_diffState == ENCODER_DIFF_ENTER) {
+ HMI_flag.ETempTooLow_flag = 0;
+ Draw_Move_Menu();
+ current_position.e = HMI_ValueStruct.Move_E_scale = 0;
+ DWIN_Draw_FloatValue(true,true,0,font8x16,White,Background_black, 3, 1, 216, MBASE(1), HMI_ValueStruct.Move_X_scale);
+ DWIN_Draw_FloatValue(true,true,0,font8x16,White,Background_black, 3, 1, 216, MBASE(2), HMI_ValueStruct.Move_Y_scale);
+ DWIN_Draw_FloatValue(true,true,0,font8x16,White,Background_black, 3, 1, 216, MBASE(3), HMI_ValueStruct.Move_Z_scale);
+ show_plus_or_minus(font8x16, Background_black, 3, 1, 216, MBASE(4), HMI_ValueStruct.Move_E_scale);
+ DWIN_UpdateLCD();
+ }
+ return;
+ }
+ // Avoid flicker by updating only the previous menu
+ if (encoder_diffState == ENCODER_DIFF_CW) {
+ if (select_axis.inc(4)) Move_Highlight(1, select_axis.now);
+ }
+ else if (encoder_diffState == ENCODER_DIFF_CCW) {
+ if (select_axis.dec()) Move_Highlight(-1, select_axis.now);
+ }
+ else if (encoder_diffState == ENCODER_DIFF_ENTER) {
+ switch (select_axis.now) {
+ case 0: // back
+ checkkey = Prepare;
+ select_prepare.set(1);
+ index_prepare = MROWS;
+ Draw_Prepare_Menu();
+ break;
+ case 1: // X axis move
+ checkkey = Move_X;
+ HMI_ValueStruct.Move_X_scale = current_position[X_AXIS]*MinUnitMult;
+ DWIN_Draw_FloatValue(true,true,0,font8x16,White,Select_Color, 3, 1, 216, MBASE(1), HMI_ValueStruct.Move_X_scale);
+ EncoderRate.encoderRateEnabled = 1;
+ break;
+ case 2: // Y axis move
+ checkkey = Move_Y;
+ HMI_ValueStruct.Move_Y_scale = current_position[Y_AXIS]*MinUnitMult;
+ DWIN_Draw_FloatValue(true,true,0,font8x16,White,Select_Color, 3, 1, 216, MBASE(2), HMI_ValueStruct.Move_Y_scale);
+ EncoderRate.encoderRateEnabled = 1;
+ break;
+ case 3: // Z axis move
+ checkkey = Move_Z;
+ HMI_ValueStruct.Move_Z_scale = current_position[Z_AXIS]*MinUnitMult;
+ DWIN_Draw_FloatValue(true,true,0,font8x16,White,Select_Color, 3, 1, 216, MBASE(3), HMI_ValueStruct.Move_Z_scale);
+ EncoderRate.encoderRateEnabled = 1;
+ break;
+ case 4: // Extruder
+ // window tips
+ #ifdef PREVENT_COLD_EXTRUSION
+ if (thermalManager.temp_hotend[0].celsius < EXTRUDE_MINTEMP) {
+ HMI_flag.ETempTooLow_flag = 1;
+ Popup_Window_ETempTooLow();
+ DWIN_UpdateLCD();
+ return;
+ }
+ #endif
+ checkkey = Extruder;
+ HMI_ValueStruct.Move_E_scale = current_position.e*MinUnitMult;
+ show_plus_or_minus(font8x16, Select_Color, 3, 1, 216, MBASE(4), HMI_ValueStruct.Move_E_scale);
+ EncoderRate.encoderRateEnabled = 1;
+ break;
+ default:
+ break;
+ }
+ }
+ DWIN_UpdateLCD();
+}
+
+/* TemperatureID */
+void HMI_Temperature(void) {
+ ENCODER_DiffState encoder_diffState = get_encoder_state();
+ if (encoder_diffState == ENCODER_DIFF_NO) return;
+
+ // Avoid flicker by updating only the previous menu
+ if (encoder_diffState == ENCODER_DIFF_CW) {
+ if (select_temp.inc(5)) Move_Highlight(1, select_temp.now);
+ }
+ else if (encoder_diffState == ENCODER_DIFF_CCW) {
+ if (select_temp.dec()) Move_Highlight(-1, select_temp.now);
+ }
+ else if (encoder_diffState == ENCODER_DIFF_ENTER) {
+ switch (select_temp.now) {
+ case 0: // back
+ checkkey = Control;
+ select_control.set(1);
+ index_control = MROWS;
+ Draw_Control_Menu();
+ break;
+ case 1: // nozzle temperature
+ checkkey = ETemp;
+ HMI_ValueStruct.E_Temp = thermalManager.temp_hotend[0].target;
+ DWIN_Draw_IntValue(true,true,0,font8x16,White,Select_Color, 3, 216, MBASE(1), thermalManager.temp_hotend[0].target);
+ EncoderRate.encoderRateEnabled = 1;
+ break;
+ case 2: // bed temperature
+ checkkey = BedTemp;
+ HMI_ValueStruct.Bed_Temp = thermalManager.temp_bed.target;
+ DWIN_Draw_IntValue(true,true,0,font8x16,White,Select_Color, 3, 216, MBASE(2), thermalManager.temp_bed.target);
+ EncoderRate.encoderRateEnabled = 1;
+ break;
+ case 3: // fan speed
+ checkkey = FanSpeed;
+ HMI_ValueStruct.Fan_speed = thermalManager.fan_speed[0];
+ DWIN_Draw_IntValue(true,true,0,font8x16,White,Select_Color, 3, 216, MBASE(3), thermalManager.fan_speed[0]);
+ EncoderRate.encoderRateEnabled = 1;
+ break;
+ case 4: // PLA preheat setting
+
+ checkkey = PLAPreheat;
+ select_PLA.reset();
+ HMI_ValueStruct.show_mode = -2;
+
+ Clear_Main_Window();
+
+ if (HMI_flag.language_flag) {
+ DWIN_Frame_AreaCopy(1, 59, 16, 271-132, 479-450, 14, 8);
+
+ DWIN_Frame_AreaCopy(1, 100, 89, 124, 479-378, LBLX, MBASE(1));
+ DWIN_Frame_AreaCopy(1, 1, 134, 271-215, 479-333, LBLX+24, MBASE(1)); // PLA nozzle temp
+ DWIN_Frame_AreaCopy(1, 100, 89, 124, 479-378, LBLX, MBASE(2));
+ DWIN_Frame_AreaCopy(1, 58, 134, 271-158, 479-333, LBLX+24, MBASE(2)); // PLA bed temp
+ DWIN_Frame_AreaCopy(1, 100, 89, 124, 479-378, LBLX, MBASE(3));
+ DWIN_Frame_AreaCopy(1, 115, 134, 271-101, 479-333, LBLX+24, MBASE(3)); // PLA fan speed
+ DWIN_Frame_AreaCopy(1, 72, 148, 271-120, 479-317, LBLX, MBASE(4)); // save PLA configuration
+ }
+ else {
+ #ifdef USE_STRING_HEADINGS
+ Draw_Title("PLA Settings"); // TODO: GET_TEXT_F
+ #else
+ DWIN_Frame_AreaCopy(1, 56, 16, 271-130, 479-450-1, 14, 8);
+ #endif
+
+ DWIN_Frame_AreaCopy(1, 157, 76, 181, 479-393, LBLX, MBASE(1));
+ DWIN_Frame_AreaCopy(1, 197, 104, 271-33, 479-365, LBLX+24+3, MBASE(1));
+ DWIN_Frame_AreaCopy(1, 1, 89, 271-188, 479-377-1, LBLX+24+41+6, MBASE(1)); // PLA nozzle temp
+ DWIN_Frame_AreaCopy(1, 157, 76, 181, 479-393, LBLX, MBASE(2)+3);
+ DWIN_Frame_AreaCopy(1, 240, 104, 271-7, 479-365, LBLX+24+3, MBASE(2)+3);
+ DWIN_Frame_AreaCopy(1, 1, 89, 271-188, 479-377-1, LBLX+24+24+6, MBASE(2)+3); // PLA bed temp
+ DWIN_Frame_AreaCopy(1, 157, 76, 181, 479-393, LBLX, MBASE(3));
+ DWIN_Frame_AreaCopy(1, 0, 119, 271-207, 479-347, LBLX+24+3, MBASE(3)); // PLA fan speed
+ DWIN_Frame_AreaCopy(1, 97, 165, 271-42, 479-301-1, LBLX, MBASE(4)); // save PLA configuration
+ }
+
+ Draw_Back_First();
+
+ Draw_Menu_Line(1, ICON_SetEndTemp);
+ Draw_Menu_Line(2, ICON_SetBedTemp);
+ Draw_Menu_Line(3, ICON_FanSpeed);
+ Draw_Menu_Line(4, ICON_WriteEEPROM);
+
+ DWIN_Draw_IntValue(true,true,0,font8x16,White,Background_black, 3, 216, MBASE(1), HMI_ValueStruct.preheat_hotend_temp[0]);
+ DWIN_Draw_IntValue(true,true,0,font8x16,White,Background_black, 3, 216, MBASE(2), HMI_ValueStruct.preheat_bed_temp[0]);
+ DWIN_Draw_IntValue(true,true,0,font8x16,White,Background_black, 3, 216, MBASE(3), HMI_ValueStruct.preheat_fan_speed[0]);
+
+ break;
+ case 5: // ABS preheat setting
+
+ checkkey = ABSPreheat;
+ select_ABS.reset();
+ HMI_ValueStruct.show_mode = -3;
+
+ Clear_Main_Window();
+
+ if (HMI_flag.language_flag) {
+ DWIN_Frame_AreaCopy(1, 142, 16, 271-48, 479-450, 14, 8);
+
+ DWIN_Frame_AreaCopy(1, 180, 89, 204, 479-379, LBLX, MBASE(1));
+ DWIN_Frame_AreaCopy(1, 1, 134, 271-215, 479-333, LBLX+24, MBASE(1)); // ABS nozzle temp
+ DWIN_Frame_AreaCopy(1, 180, 89, 204, 479-379, LBLX, MBASE(2));
+ DWIN_Frame_AreaCopy(1, 58, 134, 271-158, 479-333, LBLX+24, MBASE(2)); // ABS bed temp
+ DWIN_Frame_AreaCopy(1, 180, 89, 204, 479-379, LBLX, MBASE(3));
+ DWIN_Frame_AreaCopy(1, 115, 134, 271-101, 479-333, LBLX+24, MBASE(3)); // ABS fan speed
+ DWIN_Frame_AreaCopy(1, 72, 148, 271-120, 479-317, LBLX, MBASE(4));
+ DWIN_Frame_AreaCopy(1, 180, 89, 204, 479-379, LBLX+28, MBASE(4)+2); // save ABS configuration
+ }
+ else {
+ #ifdef USE_STRING_HEADINGS
+ Draw_Title("ABS Settings"); // TODO: GET_TEXT_F
+ #else
+ DWIN_Frame_AreaCopy(1, 56, 16, 271-130, 479-450-1, 14, 8);
+ #endif
+
+ DWIN_Frame_AreaCopy(1, 172, 76, 198, 479-393, LBLX, MBASE(1));
+ DWIN_Frame_AreaCopy(1, 197, 104, 271-33, 479-365, LBLX+24+3, MBASE(1));
+ DWIN_Frame_AreaCopy(1, 1, 89, 271-188, 479-377-1, LBLX+24+41+6, MBASE(1)); // ABS nozzle temp
+ DWIN_Frame_AreaCopy(1, 172, 76, 198, 479-393, LBLX, MBASE(2)+3);
+ DWIN_Frame_AreaCopy(1, 240, 104, 271-7, 479-365, LBLX+24+3, MBASE(2)+3);
+ DWIN_Frame_AreaCopy(1, 1, 89, 271-188, 479-377-1, LBLX+24+24+6, MBASE(2)+3); // ABS bed temp
+ DWIN_Frame_AreaCopy(1, 172, 76, 198, 479-393, LBLX, MBASE(3));
+ DWIN_Frame_AreaCopy(1, 0, 119, 271-207, 479-347, LBLX+24+3, MBASE(3)); // ABS fan speed
+ DWIN_Frame_AreaCopy(1, 97, 165, 271-42, 479-301-1, LBLX, MBASE(4));
+ DWIN_Frame_AreaCopy(1, 172, 76, 198, 479-393, LBLX+33, MBASE(4)); // save ABS configuration
+ }
+
+ Draw_Back_First();
+
+ Draw_Menu_Line(1, ICON_SetEndTemp);
+ Draw_Menu_Line(2, ICON_SetBedTemp);
+ Draw_Menu_Line(3, ICON_FanSpeed);
+ Draw_Menu_Line(4, ICON_WriteEEPROM);
+
+ DWIN_Draw_IntValue(true,true,0,font8x16,White,Background_black, 3, 216, MBASE(1), HMI_ValueStruct.preheat_hotend_temp[1]);
+ DWIN_Draw_IntValue(true,true,0,font8x16,White,Background_black, 3, 216, MBASE(2), HMI_ValueStruct.preheat_bed_temp[1]);
+ DWIN_Draw_IntValue(true,true,0,font8x16,White,Background_black, 3, 216, MBASE(3), HMI_ValueStruct.preheat_fan_speed[1]);
+
+ break;
+ default:
+ break;
+ }
+ }
+ DWIN_UpdateLCD();
+}
+
+inline void Draw_Max_Speed_Menu() {
+ Clear_Main_Window();
+
+ if (HMI_flag.language_flag) {
+ DWIN_Frame_AreaCopy(1, 1, 16, 271-243, 479-451, 14, 8);
+
+ auto say_max_speed = [](const uint16_t row) {
+ DWIN_Frame_AreaCopy(1, 173, 133, 228, 479-332, LBLX, row); // "Max speed"
+ };
+
+ say_max_speed(MBASE(1)); // "Max speed"
+ DWIN_Frame_AreaCopy(1, 229, 133, 236, 479-332, LBLX+55+3, MBASE(1)); // X
+ say_max_speed(MBASE(2)); // "Max speed"
+ DWIN_Frame_AreaCopy(1, 1, 150, 271-264, 479-319, LBLX+55+3, MBASE(2)+3); // Y
+ say_max_speed(MBASE(3)); // "Max speed"
+ DWIN_Frame_AreaCopy(1, 9, 150, 271-255, 479-319, LBLX+55+3, MBASE(3)+3); // Z
+ say_max_speed(MBASE(4)); // "Max speed"
+ DWIN_Frame_AreaCopy(1, 18, 150, 271-246, 479-319, LBLX+55+3, MBASE(4)+3); // E
+ }
+ else {
+ #ifdef USE_STRING_HEADINGS
+ Draw_Title("Max Speed (mm/s)"); // TODO: GET_TEXT_F
+ #else
+ DWIN_Frame_AreaCopy(1, 144, 16, 271-82, 479-453, 14, 8);
+ #endif
+
+ draw_max_en(MBASE(1)); // "Max"
+ DWIN_Frame_AreaCopy(1, 184, 119, 271-37, 479-347, LBLX+24+3, MBASE(1)); // "Speed X"
+
+ draw_max_en(MBASE(2)); // "Max"
+ draw_speed_en(24+3, MBASE(2)); // "Speed"
+ say_y(24+40+6, MBASE(2)); // "Y"
+
+ draw_max_en(MBASE(3)); // "Max"
+ draw_speed_en(24+3, MBASE(3)); // "Speed"
+ say_z(24+40+6, MBASE(3)); // "Z"
+
+ draw_max_en(MBASE(4)); // "Max"
+ draw_speed_en(24+3, MBASE(4)); // "Speed"
+ say_e(24+40+6, MBASE(4)); // "E"
+ }
+
+ Draw_Back_First();
+ LOOP_L_N(i, 4) Draw_Menu_Line(i + 1, ICON_MaxSpeedX+i);
+
+ DWIN_Draw_IntValue(true,true,0,font8x16,White,Background_black, 4, 210, MBASE(1), planner.settings.max_feedrate_mm_s[X_AXIS]);
+ DWIN_Draw_IntValue(true,true,0,font8x16,White,Background_black, 4, 210, MBASE(2), planner.settings.max_feedrate_mm_s[Y_AXIS]);
+ DWIN_Draw_IntValue(true,true,0,font8x16,White,Background_black, 4, 210, MBASE(3), planner.settings.max_feedrate_mm_s[Z_AXIS]);
+ DWIN_Draw_IntValue(true,true,0,font8x16,White,Background_black, 4, 210, MBASE(4), planner.settings.max_feedrate_mm_s[E_AXIS]);
+}
+
+inline void Draw_Max_Accel_Menu() {
+ Clear_Main_Window();
+
+ if (HMI_flag.language_flag) {
+ DWIN_Frame_AreaCopy(1, 1, 16, 271-243, 479-451, 14, 8);
+
+ DWIN_Frame_AreaCopy(1, 173, 133, 200, 479-332, LBLX, MBASE(1));
+ DWIN_Frame_AreaCopy(1, 28, 149, 271-202, 479-318, LBLX+27, MBASE(1)+1);
+ DWIN_Frame_AreaCopy(1, 229, 133, 236, 479-332, LBLX+27+41+3, MBASE(1)); // max acceleration X
+ DWIN_Frame_AreaCopy(1, 173, 133, 200, 479-332, LBLX, MBASE(2));
+ DWIN_Frame_AreaCopy(1, 28, 149, 271-202, 479-318, LBLX+27, MBASE(2)+1);
+ DWIN_Frame_AreaCopy(1, 1, 150, 271-264, 479-319, LBLX+27+41+3, MBASE(2)+2); // max acceleration Y
+ DWIN_Frame_AreaCopy(1, 173, 133, 200, 479-332, LBLX, MBASE(3));
+ DWIN_Frame_AreaCopy(1, 28, 149, 271-202, 479-318, LBLX+27, MBASE(3)+1);
+ DWIN_Frame_AreaCopy(1, 9, 150, 271-255, 479-319, LBLX+27+41+3, MBASE(3)+2); // max acceleration Z
+ DWIN_Frame_AreaCopy(1, 173, 133, 200, 479-332, LBLX, MBASE(4));
+ DWIN_Frame_AreaCopy(1, 28, 149, 271-202, 479-318, LBLX+27, MBASE(4)+1);
+ DWIN_Frame_AreaCopy(1, 18, 150, 271-246, 479-319, LBLX+27+41+3, MBASE(4)+2); // max acceleration E
+ }
+ else {
+ #ifdef USE_STRING_HEADINGS
+ Draw_Title("Max Accel (mm/s/s)"); // TODO: GET_TEXT_F
+ #else
+ DWIN_Frame_AreaCopy(1, 144, 16, 271-82, 479-453, 14, 8);
+ #endif
+ draw_max_accel_en(MBASE(1)); say_x(24+78+6, MBASE(1)); // "Max Acceleration X"
+ draw_max_accel_en(MBASE(2)); say_y(24+78+6, MBASE(2)); // "Max Acceleration Y"
+ draw_max_accel_en(MBASE(3)); say_z(24+78+6, MBASE(3)); // "Max Acceleration Z"
+ draw_max_accel_en(MBASE(4)); say_e(24+78+6, MBASE(4)); // "Max Acceleration E"
+ }
+
+ Draw_Back_First();
+ LOOP_L_N(i, 4) Draw_Menu_Line(i + 1, ICON_MaxAccX+i);
+
+ DWIN_Draw_IntValue(true,true,0,font8x16,White,Background_black, 4, 210, MBASE(1), planner.settings.max_acceleration_mm_per_s2[X_AXIS]);
+ DWIN_Draw_IntValue(true,true,0,font8x16,White,Background_black, 4, 210, MBASE(2), planner.settings.max_acceleration_mm_per_s2[Y_AXIS]);
+ DWIN_Draw_IntValue(true,true,0,font8x16,White,Background_black, 4, 210, MBASE(3), planner.settings.max_acceleration_mm_per_s2[Z_AXIS]);
+ DWIN_Draw_IntValue(true,true,0,font8x16,White,Background_black, 4, 210, MBASE(4), planner.settings.max_acceleration_mm_per_s2[E_AXIS]);
+}
+
+inline void Draw_Max_Jerk_Menu() {
+ Clear_Main_Window();
+
+ if (HMI_flag.language_flag) {
+ DWIN_Frame_AreaCopy(1, 1, 16, 271-243, 479-451, 14, 8);
+
+ DWIN_Frame_AreaCopy(1, 173, 133, 200, 479-332, LBLX, MBASE(1));
+ DWIN_Frame_AreaCopy(1, 1, 180, 271-243, 479-287, LBLX+27, MBASE(1)+1);
+ DWIN_Frame_AreaCopy(1, 202, 133, 228, 479-332, LBLX+53, MBASE(1));
+ DWIN_Frame_AreaCopy(1, 229, 133, 236, 479-332, LBLX+80+3, MBASE(1)); // max corner speed X
+ DWIN_Frame_AreaCopy(1, 173, 133, 200, 479-332, LBLX, MBASE(2));
+ DWIN_Frame_AreaCopy(1, 1, 180, 271-243, 479-287, LBLX+27, MBASE(2)+1);
+ DWIN_Frame_AreaCopy(1, 202, 133, 228, 479-332, LBLX+53, MBASE(2));
+ DWIN_Frame_AreaCopy(1, 1, 150, 271-264, 479-319, LBLX+80+3, MBASE(2)+3); // max corner speed Y
+ DWIN_Frame_AreaCopy(1, 173, 133, 200, 479-332, LBLX, MBASE(3));
+ DWIN_Frame_AreaCopy(1, 1, 180, 271-243, 479-287, LBLX+27, MBASE(3)+1);
+ DWIN_Frame_AreaCopy(1, 202, 133, 228, 479-332, LBLX+53, MBASE(3));
+ DWIN_Frame_AreaCopy(1, 9, 150, 271-255, 479-319, LBLX+80+3, MBASE(3)+3); // max corner speed Z
+ DWIN_Frame_AreaCopy(1, 173, 133, 200, 479-332, LBLX, MBASE(4));
+ DWIN_Frame_AreaCopy(1, 1, 180, 271-243, 479-287, LBLX+27, MBASE(4)+1);
+ DWIN_Frame_AreaCopy(1, 202, 133, 228, 479-332, LBLX+53, MBASE(4));
+ DWIN_Frame_AreaCopy(1, 18, 150, 271-246, 479-319, LBLX+80+3, MBASE(4)+3); // max corner speed E
+ }
+ else {
+ #ifdef USE_STRING_HEADINGS
+ Draw_Title("Max Corner"); // TODO: GET_TEXT_F
+ #else
+ DWIN_Frame_AreaCopy(1, 144, 16, 271-82, 479-453, 14, 8);
+ #endif
+
+ draw_max_en(MBASE(1)); // "Max"
+ draw_corner_en(MBASE(1)); // "Corner"
+ draw_speed_en(66+6, MBASE(1)); // "Speed"
+ say_x(106+9, MBASE(1)); // "X"
+
+ draw_max_en(MBASE(2)); // "Max"
+ draw_corner_en(MBASE(2)); // "Corner"
+ draw_speed_en(66+6, MBASE(2)); // "Speed"
+ say_y(106+9, MBASE(2)); // "Y"
+
+ draw_max_en(MBASE(3)); // "Max"
+ draw_corner_en(MBASE(3)); // "Corner"
+ draw_speed_en(66+6, MBASE(3)); // "Speed"
+ say_z(106+9, MBASE(3)); // "Z"
+
+ draw_max_en(MBASE(4)); // "Max"
+ draw_corner_en(MBASE(4)); // "Corner"
+ draw_speed_en(66+6, MBASE(4)); // "Speed"
+ say_e(106+9, MBASE(4)); // "E"
+ }
+
+ Draw_Back_First();
+ LOOP_L_N(i, 4) Draw_Menu_Line(i + 1, ICON_MaxSpeedCornerX+i);
+
+ DWIN_Draw_FloatValue(true,true,0,font8x16,White,Background_black, 3, 1, 210, MBASE(1), planner.max_jerk[X_AXIS]*MinUnitMult);
+ DWIN_Draw_FloatValue(true,true,0,font8x16,White,Background_black, 3, 1, 210, MBASE(2), planner.max_jerk[Y_AXIS]*MinUnitMult);
+ DWIN_Draw_FloatValue(true,true,0,font8x16,White,Background_black, 3, 1, 210, MBASE(3), planner.max_jerk[Z_AXIS]*MinUnitMult);
+ DWIN_Draw_FloatValue(true,true,0,font8x16,White,Background_black, 3, 1, 210, MBASE(4), planner.max_jerk[E_AXIS]*MinUnitMult);
+}
+
+inline void Draw_Steps_Menu() {
+ Clear_Main_Window();
+
+ if (HMI_flag.language_flag) {
+ DWIN_Frame_AreaCopy(1, 1, 16, 271-243, 479-451, 14, 8);
+
+ DWIN_Frame_AreaCopy(1, 153, 148, 271-77, 479-318, LBLX, MBASE(1));
+ DWIN_Frame_AreaCopy(1, 229, 133, 236, 479-332, LBLX+41+3, MBASE(1)); // Transmission Ratio X
+ DWIN_Frame_AreaCopy(1, 153, 148, 271-77, 479-318, LBLX, MBASE(2));
+ DWIN_Frame_AreaCopy(1, 1, 150, 271-264, 479-319, LBLX+41+3, MBASE(2)+3); // Transmission Ratio Y
+ DWIN_Frame_AreaCopy(1, 153, 148, 271-77, 479-318, LBLX, MBASE(3));
+ DWIN_Frame_AreaCopy(1, 9, 150, 271-255, 479-319, LBLX+41+3, MBASE(3)+3); // Transmission Ratio Z
+ DWIN_Frame_AreaCopy(1, 153, 148, 271-77, 479-318, LBLX, MBASE(4));
+ DWIN_Frame_AreaCopy(1, 18, 150, 271-246, 479-319, LBLX+41+3, MBASE(4)+3); // Transmission Ratio E
+ }
+ else {
+ #ifdef USE_STRING_HEADINGS
+ Draw_Title("Steps-per-mm"); // TODO: GET_TEXT_F
+ #else
+ DWIN_Frame_AreaCopy(1, 144, 16, 271-82, 479-453, 14, 8);
+ #endif
+ draw_steps_per_mm(MBASE(1)); say_x(100+3, MBASE(1)); // "Steps-per-mm X"
+ draw_steps_per_mm(MBASE(2)); say_y(100+3, MBASE(2)); // "Y"
+ draw_steps_per_mm(MBASE(3)); say_z(100+3, MBASE(3)); // "Z"
+ draw_steps_per_mm(MBASE(4)); say_e(100+3, MBASE(4)); // "E"
+ }
+
+ Draw_Back_First();
+ LOOP_L_N(i, 4) Draw_Menu_Line(i + 1, ICON_StepX+i);
+
+ DWIN_Draw_FloatValue(true,true,0,font8x16,White,Background_black, 3, 1, 210, MBASE(1), planner.settings.axis_steps_per_mm[X_AXIS]*MinUnitMult);
+ DWIN_Draw_FloatValue(true,true,0,font8x16,White,Background_black, 3, 1, 210, MBASE(2), planner.settings.axis_steps_per_mm[Y_AXIS]*MinUnitMult);
+ DWIN_Draw_FloatValue(true,true,0,font8x16,White,Background_black, 3, 1, 210, MBASE(3), planner.settings.axis_steps_per_mm[Z_AXIS]*MinUnitMult);
+ DWIN_Draw_FloatValue(true,true,0,font8x16,White,Background_black, 3, 1, 210, MBASE(4), planner.settings.axis_steps_per_mm[E_AXIS]*MinUnitMult);
+}
+
+/* Motion */
+void HMI_Motion(void) {
+ ENCODER_DiffState encoder_diffState = get_encoder_state();
+ if (encoder_diffState == ENCODER_DIFF_NO) return;
+
+ // Avoid flicker by updating only the previous menu
+ if (encoder_diffState == ENCODER_DIFF_CW) {
+ if (select_motion.inc(4)) Move_Highlight(1, select_motion.now);
+ }
+ else if (encoder_diffState == ENCODER_DIFF_CCW) {
+ if (select_motion.dec()) Move_Highlight(-1, select_motion.now);
+ }
+ else if (encoder_diffState == ENCODER_DIFF_ENTER) {
+ switch (select_motion.now) {
+ case 0: // back
+ checkkey = Control;
+ select_control.set(2);
+ index_control = MROWS;
+ Draw_Control_Menu();
+ break;
+ case 1: // max speed
+ checkkey = MaxSpeed;
+ select_speed.reset();
+ Draw_Max_Speed_Menu();
+ break;
+ case 2: // max acceleration
+ checkkey = MaxAcceleration;
+ select_acc.reset();
+ Draw_Max_Accel_Menu();
+ break;
+ case 3: // max corner speed
+ checkkey = MaxCorner;
+ select_corner.reset();
+ Draw_Max_Jerk_Menu();
+ break;
+ case 4: // transmission ratio
+ checkkey = Step;
+ select_step.reset();
+ Draw_Steps_Menu();
+ break;
+ default:
+ break;
+ }
+ }
+ DWIN_UpdateLCD();
+}
+
+/* Info */
+void HMI_Info(void) {
+ ENCODER_DiffState encoder_diffState = get_encoder_state();
+ if (encoder_diffState == ENCODER_DIFF_NO) return;
+ if (encoder_diffState == ENCODER_DIFF_ENTER) {
+ #if HAS_LEVELING
+ checkkey = Control;
+ select_control.set(CONTROL_ITEMS);
+ Draw_Control_Menu()
+ #else
+ select_page.set(3);
+ Goto_MainMenu();
+ #endif
+ }
+ DWIN_UpdateLCD();
+}
+
+/* Tune */
+void HMI_Tune(void) {
+ ENCODER_DiffState encoder_diffState = get_encoder_state();
+ if (encoder_diffState == ENCODER_DIFF_NO) return;
+
+ // Avoid flicker by updating only the previous menu
+ if (encoder_diffState == ENCODER_DIFF_CW) {
+ if (select_tune.inc(6)) {
+ if (select_tune.now > MROWS && select_tune.now > index_tune) {
+ index_tune = select_tune.now;
+ Scroll_Menu(DWIN_SCROLL_UP);
+ Prepare_Item_Lang(5);
+ }
+ else
+ Move_Highlight(1, select_tune.now + MROWS-index_tune);
+ }
+ }
+ else if (encoder_diffState == ENCODER_DIFF_CCW) {
+ if (select_tune.dec()) {
+ if (select_tune.now < index_tune-MROWS) {
+ index_tune--;
+ Scroll_Menu(DWIN_SCROLL_DOWN);
+ if (index_tune == MROWS) Draw_Back_First();
+ }
+ else
+ Move_Highlight(-1, select_tune.now + MROWS-index_tune);
+ }
+ }
+ else if (encoder_diffState == ENCODER_DIFF_ENTER) {
+ switch (select_tune.now) {
+ case 0: { // back
+ select_print.set(0);
+ Goto_PrintProcess();
+ } break;
+ case 1: // print speed
+ checkkey = PrintSpeed;
+ HMI_ValueStruct.print_speed = feedrate_percentage;
+ DWIN_Draw_IntValue(true,true,0,font8x16,White,Select_Color, 3, 216, MBASE(1+MROWS-index_tune), feedrate_percentage);
+ EncoderRate.encoderRateEnabled = 1;
+ break;
+ case 2: // nozzle temp
+ checkkey = ETemp;
+ HMI_ValueStruct.E_Temp = thermalManager.temp_hotend[0].target;
+ DWIN_Draw_IntValue(true,true,0,font8x16,White,Select_Color, 3, 216, MBASE(2+MROWS-index_tune), thermalManager.temp_hotend[0].target);
+ EncoderRate.encoderRateEnabled = 1;
+ break;
+ case 3: // bed temp
+ checkkey = BedTemp;
+ HMI_ValueStruct.Bed_Temp = thermalManager.temp_bed.target;
+ DWIN_Draw_IntValue(true,true,0,font8x16,White,Select_Color, 3, 216, MBASE(3+MROWS-index_tune), thermalManager.temp_bed.target);
+ EncoderRate.encoderRateEnabled = 1;
+ break;
+ case 4: // fan speed
+ checkkey = FanSpeed;
+ HMI_ValueStruct.Fan_speed = thermalManager.fan_speed[0];
+ DWIN_Draw_IntValue(true,true,0,font8x16,White,Select_Color, 3, 216, MBASE(4+MROWS-index_tune), thermalManager.fan_speed[0]);
+ EncoderRate.encoderRateEnabled = 1;
+ break;
+ case 5: // z-offset
+ checkkey = Homeoffset;
+ HMI_ValueStruct.offset_value = BABY_Z_VAR * 100;
+ show_plus_or_minus(font8x16, Select_Color, 2, 2, 202, MBASE(5+MROWS-index_tune), HMI_ValueStruct.offset_value);
+ EncoderRate.encoderRateEnabled = 1;
+ break;
+ case 6: // language
+ /* select language */
+ HMI_flag.language_flag = !HMI_flag.language_flag;
+
+ Clear_Main_Window();
+
+ if (HMI_flag.language_flag) {
+ set_chinese_to_eeprom();
+ DWIN_JPG_CacheTo1(Language_Chinese);
+ }
+ else {
+ set_english_to_eeprom();
+ DWIN_JPG_CacheTo1(Language_English);
+ }
+
+ Draw_Tune_Menu();
+ break;
+
+ default: break;
+ }
+ }
+ DWIN_UpdateLCD();
+}
+
+/* PLA Preheat */
+void HMI_PLAPreheatSetting(void) {
+ ENCODER_DiffState encoder_diffState = get_encoder_state();
+ if (encoder_diffState == ENCODER_DIFF_NO) return;
+
+ // Avoid flicker by updating only the previous menu
+ if (encoder_diffState == ENCODER_DIFF_CW) {
+ if (select_PLA.inc(4)) Move_Highlight(1, select_PLA.now);
+ }
+ else if (encoder_diffState == ENCODER_DIFF_CCW) {
+ if (select_PLA.dec()) Move_Highlight(-1, select_PLA.now);
+ }
+ else if (encoder_diffState == ENCODER_DIFF_ENTER) {
+ switch (select_PLA.now) {
+ case 0: // back
+ checkkey = TemperatureID;
+ select_temp.now = 4;
+ HMI_ValueStruct.show_mode = -1;
+ Draw_Temperature_Menu();
+ break;
+ case 1: // set nozzle temperature
+ checkkey = ETemp;
+ HMI_ValueStruct.E_Temp = HMI_ValueStruct.preheat_hotend_temp[0];
+ DWIN_Draw_IntValue(true,true,0,font8x16,White,Select_Color, 3, 216, MBASE(1), HMI_ValueStruct.preheat_hotend_temp[0]);
+ EncoderRate.encoderRateEnabled = 1;
+ break;
+ case 2: // set bed temperature
+ checkkey = BedTemp;
+ HMI_ValueStruct.Bed_Temp = HMI_ValueStruct.preheat_bed_temp[0];
+ DWIN_Draw_IntValue(true,true,0,font8x16,White,Select_Color, 3, 216, MBASE(2), HMI_ValueStruct.preheat_bed_temp[0]);
+ EncoderRate.encoderRateEnabled = 1;
+ break;
+ case 3: // set fan speed
+ checkkey = FanSpeed;
+ HMI_ValueStruct.Fan_speed = HMI_ValueStruct.preheat_fan_speed[0];
+ DWIN_Draw_IntValue(true,true,0,font8x16,White,Select_Color, 3, 216, MBASE(3), HMI_ValueStruct.preheat_fan_speed[0]);
+ EncoderRate.encoderRateEnabled = 1;
+ break;
+ case 4: // save PLA configuration
+ if (settings.save()) {
+ buzzer.tone(100, 659);
+ buzzer.tone(100, 698);
+ }
+ else buzzer.tone(20, 440);
+ break;
+ default:
+ break;
+ }
+ }
+ DWIN_UpdateLCD();
+}
+
+/* ABS Preheat */
+void HMI_ABSPreheatSetting(void) {
+ ENCODER_DiffState encoder_diffState = get_encoder_state();
+ if (encoder_diffState == ENCODER_DIFF_NO) return;
+
+ // Avoid flicker by updating only the previous menu
+ if (encoder_diffState == ENCODER_DIFF_CW) {
+ if (select_ABS.inc(4)) Move_Highlight(1, select_ABS.now);
+ }
+ else if (encoder_diffState == ENCODER_DIFF_CCW) {
+ if (select_ABS.dec()) Move_Highlight(-1, select_ABS.now);
+ }
+ else if (encoder_diffState == ENCODER_DIFF_ENTER) {
+ switch (select_ABS.now) {
+ case 0: // back
+ checkkey = TemperatureID;
+ select_temp.now = 5;
+ HMI_ValueStruct.show_mode = -1;
+ Draw_Temperature_Menu();
+ break;
+ case 1: // set nozzle temperature
+ checkkey = ETemp;
+ HMI_ValueStruct.E_Temp = HMI_ValueStruct.preheat_hotend_temp[1];
+ DWIN_Draw_IntValue(true,true,0,font8x16,White,Select_Color, 3, 216, MBASE(1), HMI_ValueStruct.preheat_hotend_temp[1]);
+ EncoderRate.encoderRateEnabled = 1;
+ break;
+ case 2: // set bed temperature
+ checkkey = BedTemp;
+ HMI_ValueStruct.Bed_Temp = HMI_ValueStruct.preheat_bed_temp[1];
+ DWIN_Draw_IntValue(true,true,0,font8x16,White,Select_Color, 3, 216, MBASE(2), HMI_ValueStruct.preheat_bed_temp[1]);
+ EncoderRate.encoderRateEnabled = 1;
+ break;
+ case 3: // set fan speed
+ checkkey = FanSpeed;
+ HMI_ValueStruct.Fan_speed = HMI_ValueStruct.preheat_fan_speed[1];
+ DWIN_Draw_IntValue(true,true,0,font8x16,White,Select_Color, 3, 216, MBASE(3), HMI_ValueStruct.preheat_fan_speed[1]);
+ EncoderRate.encoderRateEnabled = 1;
+ break;
+ case 4: // save PLA configuration
+ if (settings.save()) {
+ buzzer.tone(100, 659);
+ buzzer.tone(100, 698);
+ }
+ else buzzer.tone(20, 440);
+ break;
+ default:
+ break;
+ }
+ }
+ DWIN_UpdateLCD();
+}
+
+/* Max Speed */
+void HMI_MaxSpeed(void) {
+ ENCODER_DiffState encoder_diffState = get_encoder_state();
+ if (encoder_diffState == ENCODER_DIFF_NO) return;
+
+ // Avoid flicker by updating only the previous menu
+ if (encoder_diffState == ENCODER_DIFF_CW) {
+ if (select_speed.inc(4)) Move_Highlight(1, select_speed.now);
+ }
+ else if (encoder_diffState == ENCODER_DIFF_CCW) {
+ if (select_speed.dec()) Move_Highlight(-1, select_speed.now);
+ }
+ else if (encoder_diffState == ENCODER_DIFF_ENTER) {
+ switch (select_speed.now) {
+ case 0: // back
+ checkkey = Motion;
+ select_motion.now = 1;
+ Draw_Motion_Menu();
+ break;
+ case 1: // max Speed X
+ checkkey = MaxSpeed_value;
+ HMI_flag.feedspeed_flag = X_AXIS;
+ HMI_ValueStruct.Max_Feedspeed = planner.settings.max_feedrate_mm_s[X_AXIS];
+ DWIN_Draw_IntValue(true,true,0,font8x16,White,Select_Color, 4, 210, MBASE(select_speed.now), HMI_ValueStruct.Max_Feedspeed);
+ EncoderRate.encoderRateEnabled = 1;
+ break;
+ case 2: // max Speed Y
+ checkkey = MaxSpeed_value;
+ HMI_flag.feedspeed_flag = Y_AXIS;
+ HMI_ValueStruct.Max_Feedspeed = planner.settings.max_feedrate_mm_s[Y_AXIS];
+ DWIN_Draw_IntValue(true,true,0,font8x16,White,Select_Color, 4, 210, MBASE(select_speed.now), HMI_ValueStruct.Max_Feedspeed);
+ EncoderRate.encoderRateEnabled = 1;
+ break;
+ case 3: // max Speed Z
+ checkkey = MaxSpeed_value;
+ HMI_flag.feedspeed_flag = Z_AXIS;
+ HMI_ValueStruct.Max_Feedspeed = planner.settings.max_feedrate_mm_s[Z_AXIS];
+ DWIN_Draw_IntValue(true,true,0,font8x16,White,Select_Color, 4, 210, MBASE(select_speed.now), HMI_ValueStruct.Max_Feedspeed);
+ EncoderRate.encoderRateEnabled = 1;
+ break;
+ case 4: // max Speed E
+ checkkey = MaxSpeed_value;
+ HMI_flag.feedspeed_flag = E_AXIS;
+ HMI_ValueStruct.Max_Feedspeed = planner.settings.max_feedrate_mm_s[E_AXIS];
+ DWIN_Draw_IntValue(true,true,0,font8x16,White,Select_Color, 4, 210, MBASE(select_speed.now), HMI_ValueStruct.Max_Feedspeed);
+ EncoderRate.encoderRateEnabled = 1;
+ break;
+ default:
+ break;
+ }
+ }
+ DWIN_UpdateLCD();
+}
+
+/* Max Acceleration */
+void HMI_MaxAcceleration(void) {
+ ENCODER_DiffState encoder_diffState = get_encoder_state();
+ if (encoder_diffState == ENCODER_DIFF_NO) return;
+
+ // Avoid flicker by updating only the previous menu
+ if (encoder_diffState == ENCODER_DIFF_CW) {
+ if (select_acc.inc(4)) Move_Highlight(1, select_acc.now);
+ }
+ else if (encoder_diffState == ENCODER_DIFF_CCW) {
+ if (select_acc.dec()) Move_Highlight(-1, select_acc.now);
+ }
+ else if (encoder_diffState == ENCODER_DIFF_ENTER) {
+ switch (select_acc.now) {
+ case 0: // back
+ checkkey = Motion;
+ select_motion.now = 2;
+ Draw_Motion_Menu();
+ break;
+ case 1: // max acceleration X
+ checkkey = MaxAcceleration_value;
+ HMI_flag.acc_flag = X_AXIS;
+ HMI_ValueStruct.Max_Acceleration = planner.settings.max_acceleration_mm_per_s2[X_AXIS];
+ DWIN_Draw_IntValue(true,true,0,font8x16,White,Select_Color, 4, 210, MBASE(select_acc.now), HMI_ValueStruct.Max_Acceleration);
+ EncoderRate.encoderRateEnabled = 1;
+ break;
+ case 2: // max acceleration Y
+ checkkey = MaxAcceleration_value;
+ HMI_flag.acc_flag = Y_AXIS;
+ HMI_ValueStruct.Max_Acceleration = planner.settings.max_acceleration_mm_per_s2[Y_AXIS];
+ DWIN_Draw_IntValue(true,true,0,font8x16,White,Select_Color, 4, 210, MBASE(select_acc.now), HMI_ValueStruct.Max_Acceleration);
+ EncoderRate.encoderRateEnabled = 1;
+ break;
+ case 3: // max acceleration Z
+ checkkey = MaxAcceleration_value;
+ HMI_flag.acc_flag = Z_AXIS;
+ HMI_ValueStruct.Max_Acceleration = planner.settings.max_acceleration_mm_per_s2[Z_AXIS];
+ DWIN_Draw_IntValue(true,true,0,font8x16,White,Select_Color, 4, 210, MBASE(select_acc.now), HMI_ValueStruct.Max_Acceleration);
+ EncoderRate.encoderRateEnabled = 1;
+ break;
+ case 4: // max acceleration E
+ checkkey = MaxAcceleration_value;
+ HMI_flag.acc_flag = E_AXIS;
+ HMI_ValueStruct.Max_Acceleration = planner.settings.max_acceleration_mm_per_s2[E_AXIS];
+ DWIN_Draw_IntValue(true,true,0,font8x16,White,Select_Color, 4, 210, MBASE(select_acc.now), HMI_ValueStruct.Max_Acceleration);
+ EncoderRate.encoderRateEnabled = 1;
+ break;
+ default:
+ break;
+ }
+ }
+ DWIN_UpdateLCD();
+}
+
+/* Max Corner */
+void HMI_MaxCorner(void) {
+ ENCODER_DiffState encoder_diffState = get_encoder_state();
+ if (encoder_diffState == ENCODER_DIFF_NO) return;
+
+ // Avoid flicker by updating only the previous menu
+ if (encoder_diffState == ENCODER_DIFF_CW) {
+ if (select_corner.inc(4)) Move_Highlight(1, select_corner.now);
+ }
+ else if (encoder_diffState == ENCODER_DIFF_CCW) {
+ if (select_corner.dec()) Move_Highlight(-1, select_corner.now);
+ }
+ else if (encoder_diffState == ENCODER_DIFF_ENTER) {
+ switch (select_corner.now) {
+ case 0: // back
+ checkkey = Motion;
+ select_motion.now = 3;
+ Draw_Motion_Menu();
+ break;
+ case 1: // max corner X
+ checkkey = MaxCorner_value;
+ HMI_flag.corner_flag = X_AXIS;
+ HMI_ValueStruct.Max_Corner = planner.max_jerk[X_AXIS]*MinUnitMult;
+ DWIN_Draw_FloatValue(true,true,0,font8x16,White,Select_Color, 3, 1, 210, MBASE(select_corner.now), HMI_ValueStruct.Max_Corner);
+ EncoderRate.encoderRateEnabled = 1;
+ break;
+ case 2: // max corner Y
+ checkkey = MaxCorner_value;
+ HMI_flag.corner_flag = Y_AXIS;
+ HMI_ValueStruct.Max_Corner = planner.max_jerk[Y_AXIS]*MinUnitMult;
+ DWIN_Draw_FloatValue(true,true,0,font8x16,White,Select_Color, 3, 1, 210, MBASE(select_corner.now), HMI_ValueStruct.Max_Corner);
+ EncoderRate.encoderRateEnabled = 1;
+ break;
+ case 3: // max corner Z
+ checkkey = MaxCorner_value;
+ HMI_flag.corner_flag = Z_AXIS;
+ HMI_ValueStruct.Max_Corner = planner.max_jerk[Z_AXIS]*MinUnitMult;
+ DWIN_Draw_FloatValue(true,true,0,font8x16,White,Select_Color, 3, 1, 210, MBASE(select_corner.now), HMI_ValueStruct.Max_Corner);
+ EncoderRate.encoderRateEnabled = 1;
+ break;
+ case 4: // max corner E
+ checkkey = MaxCorner_value;
+ HMI_flag.corner_flag = E_AXIS;
+ HMI_ValueStruct.Max_Corner = planner.max_jerk[E_AXIS]*MinUnitMult;
+ DWIN_Draw_FloatValue(true,true,0,font8x16,White,Select_Color, 3, 1, 210, MBASE(select_corner.now), HMI_ValueStruct.Max_Corner);
+ EncoderRate.encoderRateEnabled = 1;
+ break;
+ default:
+ break;
+ }
+ }
+ DWIN_UpdateLCD();
+}
+
+/* Step */
+void HMI_Step(void) {
+ ENCODER_DiffState encoder_diffState = get_encoder_state();
+ if (encoder_diffState == ENCODER_DIFF_NO) return;
+
+ // Avoid flicker by updating only the previous menu
+ if (encoder_diffState == ENCODER_DIFF_CW) {
+ if (select_step.inc(4)) Move_Highlight(1, select_step.now);
+ }
+ else if (encoder_diffState == ENCODER_DIFF_CCW) {
+ if (select_step.dec()) Move_Highlight(-1, select_step.now);
+ }
+ else if (encoder_diffState == ENCODER_DIFF_ENTER) {
+ switch (select_step.now) {
+ case 0: // back
+ checkkey = Motion;
+ select_motion.now = 4;
+ Draw_Motion_Menu();
+ break;
+ case 1: // max step X
+ checkkey = Step_value;
+ HMI_flag.step_flag = X_AXIS;
+ HMI_ValueStruct.Max_Step = planner.settings.axis_steps_per_mm[X_AXIS]*MinUnitMult;
+ DWIN_Draw_FloatValue(true,true,0,font8x16,White,Select_Color, 3, 1, 210, MBASE(select_step.now), HMI_ValueStruct.Max_Step);
+ EncoderRate.encoderRateEnabled = 1;
+ break;
+ case 2: // max step Y
+ checkkey = Step_value;
+ HMI_flag.step_flag = Y_AXIS;
+ HMI_ValueStruct.Max_Step = planner.settings.axis_steps_per_mm[Y_AXIS]*MinUnitMult;
+ DWIN_Draw_FloatValue(true,true,0,font8x16,White,Select_Color, 3, 1, 210, MBASE(select_step.now), HMI_ValueStruct.Max_Step);
+ EncoderRate.encoderRateEnabled = 1;
+ break;
+ case 3: // max step Z
+ checkkey = Step_value;
+ HMI_flag.step_flag = Z_AXIS;
+ HMI_ValueStruct.Max_Step = planner.settings.axis_steps_per_mm[Z_AXIS]*MinUnitMult;
+ DWIN_Draw_FloatValue(true,true,0,font8x16,White,Select_Color, 3, 1, 210, MBASE(select_step.now), HMI_ValueStruct.Max_Step);
+ EncoderRate.encoderRateEnabled = 1;
+ break;
+ case 4: // max step E
+ checkkey = Step_value;
+ HMI_flag.step_flag = E_AXIS;
+ HMI_ValueStruct.Max_Step = planner.settings.axis_steps_per_mm[E_AXIS]*MinUnitMult;
+ DWIN_Draw_FloatValue(true,true,0,font8x16,White,Select_Color, 3, 1, 210, MBASE(select_step.now), HMI_ValueStruct.Max_Step);
+ EncoderRate.encoderRateEnabled = 1;
+ break;
+ default:
+ break;
+ }
+ }
+ DWIN_UpdateLCD();
+}
+
+void HMI_Init(void) {
+ HMI_SDCardInit();
+
+ for (uint16_t t = 0; t <= 100; t += 2) {
+ DWIN_ICON_Show(ICON,ICON_Bar, 15, 260);
+ DWIN_Draw_Rectangle(1, Background_black, 15 + t * 242/100, 260, 257, 280);
+ DWIN_UpdateLCD();
+ delay(20);
+ }
+
+ lcd_select_language();
+
+ #if ENABLED(FIX_MOUNTED_PROBE) // For "CREALITY_TOUCH" probe too?
+ SET_OUTPUT(COM_PIN);
+ WRITE(COM_PIN, 1);
+ #endif
+
+ delay(200);
+}
+
+void DWIN_Update(void) {
+ /* status update */
+ EachMomentUpdate();
+
+ /* sdcard update */
+ HMI_SDCardUpdate();
+
+ /* rotary encoder update */
+ DWIN_HandleScreen();
+}
+
+void EachMomentUpdate(void) {
+ static millis_t next_rts_update_ms = 0;
+ const millis_t ms = millis();
+ if (PENDING(ms, next_rts_update_ms)) return;
+ next_rts_update_ms = ms + DWIN_SCROLL_UPDATE_INTERVAL;
+
+ // variable update
+ update_variable();
+
+ if (checkkey == PrintProcess) {
+ // if print done
+ if (HMI_flag.print_finish && !HMI_flag.confirm_flag) {
+ HMI_flag.print_finish = 0;
+ HMI_flag.confirm_flag = 1;
+
+ TERN_(POWER_LOSS_RECOVERY, recovery.cancel());
+
+ planner.finish_and_disable();
+
+ // show percent bar and value
+ Percentrecord = 0;
+ Draw_Print_ProgressBar();
+
+ // show print done confirm
+ DWIN_Draw_Rectangle(1, Background_black, 0, 250, 271, 360);
+ DWIN_ICON_Show(ICON, HMI_flag.language_flag ? ICON_Confirm_C : ICON_Confirm_E, 86, 302-19);
+ }
+ else if (HMI_flag.pause_flag != printingIsPaused()) {
+ // print status update
+ HMI_flag.pause_flag = printingIsPaused();
+ if (HMI_flag.pause_flag) ICON_Continue(); else ICON_Pause();
+ }
+ }
+
+ // pause after homing
+ if (pause_action_flag && printingIsPaused() && !planner.has_blocks_queued()) {
+ pause_action_flag = 0;
+ #if ENABLED(PAUSE_HEAT)
+ tempbed = thermalManager.temp_bed.target;
+ temphot = thermalManager.temp_hotend[0].target;
+ thermalManager.disable_all_heaters();
+ #endif
+ queue.inject_P(PSTR("G1 F1200 X0 Y0"));
+ }
+
+ if (card.isPrinting() && checkkey == PrintProcess) { // print process
+ const uint8_t card_pct = card.percentDone();
+ static uint8_t last_cardpercentValue = 101;
+ if (last_cardpercentValue != card_pct) { // print percent
+ last_cardpercentValue = card_pct;
+ if (card_pct) {
+ Percentrecord = card_pct;
+ Draw_Print_ProgressBar();
+ }
+ }
+
+ duration_t elapsed = print_job_timer.duration(); // print timer
+ /* already print time */
+ const uint16_t min = (elapsed.value % 3600) / 60;
+ if (last_Printtime != min) { // 1 minute update
+ last_Printtime = min;
+ Draw_Print_ProgressElapsed();
+ }
+ /* remain print time */
+ static millis_t next_remain_time_update = 0;
+ if (elapsed.minute() > 5 && ELAPSED(ms, next_remain_time_update) && HMI_flag.heat_flag == 0) { // show after 5 min and 20s update
+ remain_time = ((elapsed.value - heat_time) * ((float)card.getFileSize() / (float)card.getIndex())) - (elapsed.value - heat_time);
+ next_remain_time_update += 20 * 1000UL;
+ Draw_Print_ProgressRemain();
+ }
+ }
+ else if (abort_flag && !HMI_flag.home_flag) { // Print Stop
+ abort_flag = 0;
+ HMI_ValueStruct.print_speed = feedrate_percentage = 100;
+ zprobe_zoffset = TERN(HAS_LEVELING, probe.offset.z, 0);
+
+ planner.finish_and_disable();
+
+ #if DISABLED(SD_ABORT_NO_COOLDOWN)
+ thermalManager.disable_all_heaters();
+ #endif
+
+ select_page.set(0);
+ Goto_MainMenu();
+ }
+ else if (DWIN_lcd_sd_status && recovery.dwin_flag) { // resume print before power off
+ recovery.dwin_flag = false;
+
+ recovery.load();
+ if (!recovery.valid()) return recovery.purge();
+
+ auto draw_first_option = [](const bool sel) {
+ const uint16_t c1 = sel ? Background_window : Select_Color;
+ DWIN_Draw_Rectangle(0, c1, 25, 306, 126, 345);
+ DWIN_Draw_Rectangle(0, c1, 24, 305, 127, 346);
+ };
+
+ auto update_selection = [&](const bool sel) {
+ HMI_flag.select_flag = sel;
+ draw_first_option(sel);
+ const uint16_t c2 = sel ? Select_Color : Background_window;
+ DWIN_Draw_Rectangle(0, c2, 145, 306, 246, 345);
+ DWIN_Draw_Rectangle(0, c2, 144, 305, 247, 346);
+ };
+
+ LOOP_L_N(i, card.get_num_Files()) {
+ // TODO: Resume print via M1000 then update the UI
+ // with the active filename which can come from CardReader.
+ card.getfilename_sorted(i);
+ if (!strcmp(card.filename, &recovery.info.sd_filename[1])) { // Resume print before power failure while have the same file
+ recovery_flag = 1;
+ HMI_flag.select_flag = 1;
+ Popup_Window_Resume();
+ draw_first_option(false);
+ char * const name = card.longest_filename();
+ DWIN_Draw_String(false,true,font8x16, Font_window, Background_window, (DWIN_WIDTH-strlen(name)*MENU_CHR_W)/2, 252, name);
+ DWIN_UpdateLCD();
+ break;
+ }
+ }
+
+ // if hasn't resumable G-code file
+ if (!recovery_flag) return;
+
+ while (recovery_flag) {
+ ENCODER_DiffState encoder_diffState = Encoder_ReceiveAnalyze();
+ if (encoder_diffState != ENCODER_DIFF_NO) {
+ if (encoder_diffState == ENCODER_DIFF_ENTER) {
+ recovery_flag = 0;
+ if (HMI_flag.select_flag) break;
+ TERN_(POWER_LOSS_RECOVERY, recovery.cancel());
+ HMI_StartFrame(true);
+ return;
+ }
+ else
+ update_selection(encoder_diffState == ENCODER_DIFF_CCW);
+ DWIN_UpdateLCD();
+ }
+ }
+
+ select_print.set(0);
+ HMI_ValueStruct.show_mode = 0;
+ HMI_StartFrame(false);
+ recovery.resume();
+ return;
+ }
+ DWIN_UpdateLCD();
+}
+
+void DWIN_HandleScreen(void) {
+ switch (checkkey) {
+ case MainMenu: HMI_MainMenu(); break;
+ case SelectFile: HMI_SelectFile(); break;
+ case Prepare: HMI_Prepare(); break;
+ case Control: HMI_Control(); break;
+ case Leveling: break;
+ case PrintProcess: HMI_Printing(); break;
+ case Print_window: HMI_PauseOrStop(); break;
+ case AxisMove: HMI_AxisMove(); break;
+ case TemperatureID: HMI_Temperature(); break;
+ case Motion: HMI_Motion(); break;
+ case Info: HMI_Info(); break;
+ case Tune: HMI_Tune(); break;
+ case PLAPreheat: HMI_PLAPreheatSetting(); break;
+ case ABSPreheat: HMI_ABSPreheatSetting(); break;
+ case MaxSpeed: HMI_MaxSpeed(); break;
+ case MaxAcceleration: HMI_MaxAcceleration(); break;
+ case MaxCorner: HMI_MaxCorner(); break;
+ case Step: HMI_Step(); break;
+ case Move_X: HMI_Move_X(); break;
+ case Move_Y: HMI_Move_Y(); break;
+ case Move_Z: HMI_Move_Z(); break;
+ case Extruder: HMI_Move_E(); break;
+ case Homeoffset: HMI_Zoffset(); break;
+ case ETemp: HMI_ETemp(); break;
+ case BedTemp: HMI_BedTemp(); break;
+ case FanSpeed: HMI_FanSpeed(); break;
+ case PrintSpeed: HMI_PrintSpeed(); break;
+ case MaxSpeed_value: HMI_MaxFeedspeedXYZE(); break;
+ case MaxAcceleration_value: HMI_MaxAccelerationXYZE(); break;
+ case MaxCorner_value: HMI_MaxCornerXYZE(); break;
+ case Step_value: HMI_StepXYZE(); break;
+ default: break;
+ }
+}
+
+void DWIN_CompletedHoming(void) {
+ HMI_flag.home_flag = false;
+ if (checkkey == Last_Prepare) {
+ checkkey = Prepare;
+ select_prepare.now = 3;
+ index_prepare = MROWS;
+ Draw_Prepare_Menu();
+ }
+ else if (checkkey == Back_Main) {
+ HMI_ValueStruct.print_speed = feedrate_percentage = 100;
+ zprobe_zoffset = TERN0(BLTOUCH, probe.offset.z);
+ planner.finish_and_disable();
+ Goto_MainMenu();
+ }
+}
+
+void DWIN_CompletedLeveling(void) {
+ if (checkkey == Leveling) Goto_MainMenu();
+}
+
+#endif // DWIN_CREALITY_LCD
diff --git a/Marlin/src/lcd/dwin/dwin.h b/Marlin/src/lcd/dwin/dwin.h
new file mode 100644
index 0000000000..7be302683b
--- /dev/null
+++ b/Marlin/src/lcd/dwin/dwin.h
@@ -0,0 +1,379 @@
+/**
+ * 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
+
+/**
+ * DWIN by Creality3D
+ */
+
+#include "dwin_lcd.h"
+#include "rotary_encoder.h"
+#include "eeprom_BL24CXX.h"
+
+#include
+
+#define MACHINE_SIZE "220x220x250"
+#define CORP_WEBSITE_C "www.cxsw3d.com"
+#define CORP_WEBSITE_E "www.creality.com"
+
+/*********************************/
+
+#define MENU_CHAR_LIMIT 24
+
+/*fan speed limit*/
+#define FanOn 255
+#define FanOff 0
+
+/*print speed limit*/
+#define max_print_speed 999
+#define min_print_speed 10
+
+/*Temp limit*/
+#define max_E_Temp (HEATER_0_MAXTEMP - 15)
+#define min_E_Temp HEATER_0_MINTEMP
+#define max_Bed_Temp (BED_MAXTEMP - 10)
+#define min_Bed_Temp BED_MINTEMP
+
+/*Feedspeed limit*/ // max feedspeed = DEFAULT_MAX_FEEDRATE * 2
+#define min_MaxFeedspeed 1
+#define min_MaxAcceleration 1
+#define min_MaxCorner 0.1
+#define min_Step 1
+
+#define FEEDRATE_E (60)
+
+// mininum unit (0.1) : multiple (10)
+#define MinUnitMult 10
+
+#define Encoder_wait 20
+#define DWIN_SCROLL_UPDATE_INTERVAL 2000
+#define DWIN_REMAIN_TIME_UPDATE_INTERVAL 20000
+
+enum processID {
+ /*Process ID*/
+ MainMenu,
+ SelectFile,
+ Prepare,
+ Control,
+ Leveling,
+ PrintProcess,
+ AxisMove,
+ TemperatureID,
+ Motion,
+ Info,
+ Tune,
+ PLAPreheat,
+ ABSPreheat,
+ MaxSpeed,
+ MaxSpeed_value,
+ MaxAcceleration,
+ MaxAcceleration_value,
+ MaxCorner,
+ MaxCorner_value,
+ Step,
+ Step_value,
+
+ /*Last Process ID*/
+ Last_Prepare,
+
+ /*Back Process ID*/
+ Back_Main,
+ Back_Print,
+
+ /*Date variable ID*/
+ Move_X,
+ Move_Y,
+ Move_Z,
+ Extruder,
+ Homeoffset,
+ ETemp,
+ BedTemp,
+ FanSpeed,
+ PrintSpeed,
+
+ /*Window ID*/
+ Print_window,
+ Popup_Window
+};
+
+/*Picture ID*/
+#define Start_Process 0
+#define Language_English 1
+#define Language_Chinese 2
+
+/*ICON ID*/
+#define ICON 0x09
+#define ICON_LOGO 0
+#define ICON_Print_0 1
+#define ICON_Print_1 2
+#define ICON_Prepare_0 3
+#define ICON_Prepare_1 4
+#define ICON_Control_0 5
+#define ICON_Control_1 6
+#define ICON_Leveling_0 7
+#define ICON_Leveling_1 8
+#define ICON_HotendTemp 9
+#define ICON_BedTemp 10
+#define ICON_Speed 11
+#define ICON_Zoffset 12
+#define ICON_Back 13
+#define ICON_File 14
+#define ICON_PrintTime 15
+#define ICON_RemainTime 16
+#define ICON_Setup_0 17
+#define ICON_Setup_1 18
+#define ICON_Pause_0 19
+#define ICON_Pause_1 20
+#define ICON_Continue_0 21
+#define ICON_Continue_1 22
+#define ICON_Stop_0 23
+#define ICON_Stop_1 24
+#define ICON_Bar 25
+#define ICON_More 26
+
+#define ICON_Axis 27
+#define ICON_CloseMotor 28
+#define ICON_Homing 29
+#define ICON_SetHome 30
+#define ICON_PLAPreheat 31
+#define ICON_ABSPreheat 32
+#define ICON_Cool 33
+#define ICON_Language 34
+
+#define ICON_MoveX 35
+#define ICON_MoveY 36
+#define ICON_MoveZ 37
+#define ICON_Extruder 38
+
+#define ICON_Temperature 40
+#define ICON_Motion 41
+#define ICON_WriteEEPROM 42
+#define ICON_ReadEEPROM 43
+#define ICON_ResumeEEPROM 44
+#define ICON_Info 45
+
+#define ICON_SetEndTemp 46
+#define ICON_SetBedTemp 47
+#define ICON_FanSpeed 48
+#define ICON_SetPLAPreheat 49
+#define ICON_SetABSPreheat 50
+
+#define ICON_MaxSpeed 51
+#define ICON_MaxAccelerated 52
+#define ICON_MaxCorner 53
+#define ICON_Step 54
+#define ICON_PrintSize 55
+#define ICON_Version 56
+#define ICON_Contact 57
+#define ICON_StockConfiguraton 58
+#define ICON_MaxSpeedX 59
+#define ICON_MaxSpeedY 60
+#define ICON_MaxSpeedZ 61
+#define ICON_MaxSpeedE 62
+#define ICON_MaxAccX 63
+#define ICON_MaxAccY 64
+#define ICON_MaxAccZ 65
+#define ICON_MaxAccE 66
+#define ICON_MaxSpeedCornerX 67
+#define ICON_MaxSpeedCornerY 68
+#define ICON_MaxSpeedCornerZ 69
+#define ICON_MaxSpeedCornerE 70
+#define ICON_StepX 71
+#define ICON_StepY 72
+#define ICON_StepZ 73
+#define ICON_StepE 74
+#define ICON_Setspeed 75
+#define ICON_SetZOffset 76
+#define ICON_Rectangle 77
+#define ICON_BLTouch 78
+#define ICON_TempTooLow 79
+#define ICON_AutoLeveling 80
+#define ICON_TempTooHigh 81
+#define ICON_NoTips_C 82
+#define ICON_NoTips_E 83
+#define ICON_Continue_C 84
+#define ICON_Continue_E 85
+#define ICON_Cancel_C 86
+#define ICON_Cancel_E 87
+#define ICON_Confirm_C 88
+#define ICON_Confirm_E 89
+#define ICON_Info_0 90
+#define ICON_Info_1 91
+
+/*
+* 3-.0:字号大小,0x00-0x09,对应字体大小于下:
+* 0x00=6*12 0x01=8*16 0x02=10*20 0x03=12*24 0x04=14*28
+* 0x05=16*32 0x06=20*40 0x07=24*48 0x08=28*56 0x09=32*64
+*/
+#define font6x12 0x00
+#define font8x16 0x01
+#define font10x20 0x02
+#define font12x24 0x03
+#define font14x28 0x04
+#define font16x32 0x05
+#define font20x40 0x06
+#define font24x48 0x07
+#define font28x56 0x08
+#define font32x64 0x09
+
+/* Colour */
+#define White 0xFFFF
+#define Background_window 0x31E8 // 弹窗背景色
+#define Background_blue 0x1125 // 暗蓝背景色
+#define Background_black 0x0841 // 黑色背景色
+#define Font_window 0xD6BA // 弹窗字体背景色
+#define Line_Color 0x3A6A // 分割线颜色
+#define Rectangle_Color 0xEE2F // 蓝色方块光标颜色
+#define Percent_Color 0xFE29 // 百分比颜色
+#define BarFill_Color 0x10E4 // 进度条填充色
+#define Select_Color 0x33BB // 选中色
+
+extern int checkkey, last_checkkey;
+extern float zprobe_zoffset;
+extern char print_filename[16];
+
+extern millis_t heat_time;
+
+typedef struct {
+ int16_t E_Temp = 0;
+ int16_t Bed_Temp = 0;
+ int16_t Fan_speed = 0;
+ int16_t print_speed = 100;
+ float Max_Feedspeed = 0;
+ float Max_Acceleration = 0;
+ float Max_Corner = 0;
+ float Max_Step = 0;
+ float Move_X_scale = 0;
+ float Move_Y_scale = 0;
+ float Move_Z_scale = 0;
+ float Move_E_scale = 0;
+ float offset_value = 0;
+ char show_mode = 0; // -1: Temperature control 0: Printing temperature
+ int16_t preheat_hotend_temp[2];
+ int16_t preheat_bed_temp[2];
+ uint8_t preheat_fan_speed[2];
+} HMI_value_t;
+
+typedef struct {
+ bool language_flag; // 0: EN, 1: CN
+ bool pause_flag:1;
+ bool print_finish:1;
+ bool confirm_flag:1;
+ bool select_flag:1;
+ bool home_flag:1;
+ bool heat_flag:1; // 0: heating done 1: during heating
+ bool ETempTooLow_flag:1;
+ bool leveling_offset_flag:1;
+ char feedspeed_flag;
+ char acc_flag;
+ char corner_flag;
+ char step_flag;
+} HMI_Flag;
+
+extern HMI_value_t HMI_ValueStruct;
+extern HMI_Flag HMI_flag;
+
+/* Language */
+void lcd_select_language(void);
+void set_english_to_eeprom(void);
+void set_chinese_to_eeprom(void);
+
+/* Show ICON*/
+void ICON_Print(bool show);
+void ICON_Prepare(bool show);
+void ICON_Control(bool show);
+void ICON_Leveling(bool show);
+void ICON_StartInfo(bool show);
+
+void ICON_Setting(bool show);
+void ICON_Pause(bool show);
+void ICON_Continue(bool show);
+void ICON_Stop(bool show);
+
+/* Popup window tips */
+void Popup_Window_Temperature(const bool toohigh);
+void Popup_Window_ETempTooLow(void);
+void Popup_Window_Resume(void);
+void Popup_Window_Home(void);
+void Popup_Window_Leveling(void);
+
+void Goto_PrintProcess(void);
+void Goto_MainMenu(void);
+
+/* Variable control */
+void HMI_Move_X(void);
+void HMI_Move_Y(void);
+void HMI_Move_Z(void);
+void HMI_Move_E(void);
+
+void HMI_Zoffset(void);
+void HMI_ETemp(void);
+void HMI_BedTemp(void);
+void HMI_FanSpeed(void);
+void HMI_PrintSpeed(void);
+
+void HMI_MaxFeedspeedXYZE(void);
+void HMI_MaxAccelerationXYZE(void);
+void HMI_MaxCornerXYZE(void);
+void HMI_StepXYZE(void);
+
+void update_variable(void);
+void show_plus_or_minus(uint8_t size, uint16_t bColor, uint8_t iNum, uint8_t fNum, uint16_t x, uint16_t y, long value);
+
+/* SD Card */
+void HMI_SDCardInit(void);
+void HMI_SDCardUpdate(void);
+
+/* Main Process */
+void Icon_print(bool value);
+void Icon_control(bool value);
+void Icon_temperature(bool value);
+void Icon_leveling(bool value);
+
+/* Other */
+bool Pause_HeatStatus();
+void HMI_StartFrame(const bool with_update); // 开机画面
+void HMI_MainMenu(void); // 主进程画面
+void HMI_SelectFile(void); // 文件页
+void HMI_Printing(void); // 打印页
+void HMI_Prepare(void); // 准备页
+void HMI_Control(void); // 控制页
+void HMI_Leveling(void); // 调平页
+void HMI_AxisMove(void); // 轴移动菜单
+void HMI_Temperature(void); // 温度菜单
+void HMI_Motion(void); // 运动菜单
+void HMI_Info(void); // 信息菜单
+void HMI_Tune(void); // 调整菜单
+void HMI_PLAPreheatSetting(void); // PLA预热设置
+void HMI_ABSPreheatSetting(void); // ABS预热设置
+void HMI_MaxSpeed(void); // 最大速度子菜单
+void HMI_MaxAcceleration(void); // 最大加速度子菜单
+void HMI_MaxCorner(void); // 最大拐角速度子菜单
+void HMI_Step(void); // 传动比
+
+void HMI_Init(void);
+void DWIN_Update(void);
+void EachMomentUpdate(void);
+void DWIN_HandleScreen(void);
+
+void DWIN_CompletedHoming(void);
+void DWIN_CompletedLeveling(void);
diff --git a/Marlin/src/lcd/dwin/dwin_lcd.cpp b/Marlin/src/lcd/dwin/dwin_lcd.cpp
new file mode 100644
index 0000000000..59232e63b3
--- /dev/null
+++ b/Marlin/src/lcd/dwin/dwin_lcd.cpp
@@ -0,0 +1,302 @@
+/**
+ * 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 dwin_lcd.c
+ * @author LEO / Creality3D
+ * @date 2019/07/18
+ * @version 2.0.1
+ * @brief 迪文屏控制操作函数
+ ********************************************************************************/
+
+#include "../../inc/MarlinConfigPre.h"
+
+#if ENABLED(DWIN_CREALITY_LCD)
+
+#include "../../inc/MarlinConfig.h"
+
+#include "dwin_lcd.h"
+#include // for memset
+
+// Make sure DWIN_SendBuf is large enough to hold the largest
+// printed string plus the draw command and tail.
+uint8_t DWIN_SendBuf[11 + 24] = { 0xAA };
+uint8_t DWIN_BufTail[4] = { 0xCC, 0x33, 0xC3, 0x3C };
+uint8_t databuf[26] = { 0 };
+uint8_t receivedType;
+
+int recnum = 0;
+
+inline void DWIN_Byte(size_t &i, const uint16_t bval) {
+ DWIN_SendBuf[++i] = bval;
+}
+
+inline void DWIN_Word(size_t &i, const uint16_t wval) {
+ DWIN_SendBuf[++i] = wval >> 8;
+ DWIN_SendBuf[++i] = wval & 0xFF;
+}
+
+inline void DWIN_Long(size_t &i, const uint32_t lval) {
+ DWIN_SendBuf[++i] = (lval >> 24) & 0xFF;
+ DWIN_SendBuf[++i] = (lval >> 16) & 0xFF;
+ DWIN_SendBuf[++i] = (lval >> 8) & 0xFF;
+ DWIN_SendBuf[++i] = lval & 0xFF;
+}
+
+inline void DWIN_String(size_t &i, char * const string) {
+ const size_t len = strlen(string);
+ memcpy(&DWIN_SendBuf[i+1], string, len);
+ i += len;
+}
+
+/*发送当前BUF中的数据以及包尾数据 len:整包数据长度*/
+inline void DWIN_Send(size_t &i) {
+ ++i;
+ LOOP_L_N(n, i) { MYSERIAL1.write(DWIN_SendBuf[n]);
+ delayMicroseconds(1); }
+ LOOP_L_N(n, 4) { MYSERIAL1.write(DWIN_BufTail[n]);
+ delayMicroseconds(1); }
+}
+
+/*----------------------------------------------系统变量函数----------------------------------------------*/
+/*握手 1: 握手成功 2: 握手失败*/
+bool DWIN_Handshake(void) {
+ size_t i = 0;
+ DWIN_Byte(i, 0x00);
+ DWIN_Send(i);
+
+ while (MYSERIAL1.available() > 0 && recnum < (signed)sizeof(databuf)) {
+ databuf[recnum] = MYSERIAL1.read();
+ // ignore the invalid data
+ if (databuf[0] != FHONE) { // prevent the program from running.
+ if (recnum > 0) {
+ recnum = 0;
+ ZERO(databuf);
+ }
+ continue;
+ }
+ delay(10);
+ recnum++;
+ }
+
+ return ( recnum >= 3
+ && databuf[0] == FHONE
+ && databuf[1] == '\0'
+ && databuf[2] == 'O'
+ && databuf[3] == 'K' );
+}
+
+/*设定背光亮度 luminance:亮度(0x00~0xFF)*/
+void DWIN_Backlight_SetLuminance(const uint8_t luminance) {
+ size_t i = 0;
+ DWIN_Byte(i, 0x30);
+ DWIN_Byte(i, _MAX(luminance, 0x1F));
+ DWIN_Send(i);
+}
+
+/*设定画面显示方向 dir:0,0°; 1,90°; 2,180°; 3,270°*/
+void DWIN_Frame_SetDir(uint8_t dir) {
+ size_t i = 0;
+ DWIN_Byte(i, 0x34);
+ DWIN_Byte(i, 0x5A);
+ DWIN_Byte(i, 0xA5);
+ DWIN_Byte(i, dir);
+ DWIN_Send(i);
+}
+
+/*更新显示*/
+void DWIN_UpdateLCD(void) {
+ size_t i = 0;
+ DWIN_Byte(i, 0x3D);
+ DWIN_Send(i);
+}
+
+/*----------------------------------------------绘图相关函数----------------------------------------------*/
+/*画面清屏 color:清屏颜色*/
+void DWIN_Frame_Clear(const uint16_t color) {
+ size_t i = 0;
+ DWIN_Byte(i, 0x01);
+ DWIN_Word(i, color);
+ DWIN_Send(i);
+}
+
+/*画面画线 color:线段颜色 xStart:X起始坐标 yStart:Y起始坐标 xEnd:X终止坐标 yEnd:Y终止坐标*/
+void DWIN_Draw_Line(uint16_t color, uint16_t xStart, uint16_t yStart, uint16_t xEnd, uint16_t yEnd) {
+ size_t i = 0;
+ DWIN_Byte(i, 0x03);
+ DWIN_Word(i, color);
+ DWIN_Word(i, xStart);
+ DWIN_Word(i, yStart);
+ DWIN_Word(i, xEnd);
+ DWIN_Word(i, yEnd);
+ DWIN_Send(i);
+}
+
+/*画面画矩形 mode:0,外框;1,填充;2,异或填充 color:颜色 xStart/yStart:矩形左上坐标 xEnd/yEnd:矩形右下坐标*/
+void DWIN_Draw_Rectangle(uint8_t mode, uint16_t color,
+ uint16_t xStart, uint16_t yStart, uint16_t xEnd, uint16_t yEnd) {
+ size_t i = 0;
+ DWIN_Byte(i, 0x05);
+ DWIN_Byte(i, mode);
+ DWIN_Word(i, color);
+ DWIN_Word(i, xStart);
+ DWIN_Word(i, yStart);
+ DWIN_Word(i, xEnd);
+ DWIN_Word(i, yEnd);
+ DWIN_Send(i);
+}
+
+/*画面区域移动 mode:0,环移;1,平移 dir:0,向左移动;1,向右移动;2,向上移动;3,向下移动 dis:移动距离
+ color:填充颜色 xStart/yStart:选定区域左上坐标 xEnd/yEnd:选定区域右下坐标*/
+void DWIN_Frame_AreaMove(uint8_t mode, uint8_t dir, uint16_t dis,
+ uint16_t color, uint16_t xStart, uint16_t yStart, uint16_t xEnd, uint16_t yEnd) {
+ size_t i = 0;
+ DWIN_Byte(i, 0x09);
+ DWIN_Byte(i, (mode << 7) | dir);
+ DWIN_Word(i, dis);
+ DWIN_Word(i, color);
+ DWIN_Word(i, xStart);
+ DWIN_Word(i, yStart);
+ DWIN_Word(i, xEnd);
+ DWIN_Word(i, yEnd);
+ DWIN_Send(i);
+}
+
+/*----------------------------------------------文本相关函数----------------------------------------------*/
+/*画面显示字符串 widthAdjust:true,自调整字符宽度;false,不调整字符宽度 bShow:true,显示背景色;false,不显示背景色 size:字号大小
+ color:字符颜色 bColor:背景颜色 x/y:字符串左上坐标 *string:字符串*/
+void DWIN_Draw_String(bool widthAdjust, bool bShow, uint8_t size,
+ uint16_t color, uint16_t bColor, uint16_t x, uint16_t y, char *string) {
+ size_t i = 0;
+ DWIN_Byte(i, 0x11);
+ DWIN_Byte(i, (widthAdjust? 0x80:0x00) | (bShow? 0x40:0x00) | size);
+ DWIN_Word(i, color);
+ DWIN_Word(i, bColor);
+ DWIN_Word(i, x);
+ DWIN_Word(i, y);
+ DWIN_String(i, string);
+ DWIN_Send(i);
+}
+
+/*画面显示正整数 bShow:true,显示背景色;false,不显示背景色 zeroFill:true,补零;false,不补零 zeroMode:1,无效0显示为0; 0,无效0显示为空格 size:字号大小
+ color:字符颜色 bColor:背景颜色 iNum:位数 x/y:变量左上坐标 value:整型变量*/
+void DWIN_Draw_IntValue(uint8_t bShow, bool zeroFill, uint8_t zeroMode, uint8_t size, uint16_t color,
+ uint16_t bColor, uint8_t iNum, uint16_t x, uint16_t y, uint16_t value) {
+ size_t i = 0;
+ DWIN_Byte(i, 0x14);
+ DWIN_Byte(i, (bShow? 0x80:0x00) | (zeroFill? 0x20:0x00) | (zeroMode? 0x10:0x00) | size);
+ DWIN_Word(i, color);
+ DWIN_Word(i, bColor);
+ DWIN_Byte(i, iNum);
+ DWIN_Byte(i, 0); // fNum
+ DWIN_Word(i, x);
+ DWIN_Word(i, y);
+ #if 0
+ for (char count = 0; count < 8; count++) {
+ DWIN_Byte(i, value);
+ value >>= 8;
+ if ((value&0xFF) == 0x00) break;
+ }
+ #else
+ // Write a big-endian 64 bit integer
+ const size_t p = i + 1;
+ for (char count = 8; count--;) { // 7..0
+ ++i;
+ DWIN_SendBuf[p + count] = value;
+ value >>= 8;
+ }
+ #endif
+
+ DWIN_Send(i);
+}
+
+/*画面显示浮点数 bShow:true,显示背景色;false,不显示背景色 zeroFill:true,补零;false,不补零 zeroMode:1,无效0显示为0; 0,无效0显示为空格 size:字号大小
+ color:字符颜色 bColor:背景颜色 iNum:整数位数 fNum:小数位数 x/y:变量左上坐标 value:浮点数变量*/
+void DWIN_Draw_FloatValue(uint8_t bShow, bool zeroFill, uint8_t zeroMode, uint8_t size, uint16_t color,
+ uint16_t bColor, uint8_t iNum, uint8_t fNum, uint16_t x, uint16_t y, long value) {
+ //uint8_t *fvalue = (uint8_t*)&value;
+ size_t i = 0;
+ DWIN_Byte(i, 0x14);
+ DWIN_Byte(i, (bShow? 0x80:0x00) | (zeroFill? 0x20:0x00) | (zeroMode? 0x10:0x00) | size);
+ DWIN_Word(i, color);
+ DWIN_Word(i, bColor);
+ DWIN_Byte(i, iNum);
+ DWIN_Byte(i, fNum);
+ DWIN_Word(i, x);
+ DWIN_Word(i, y);
+ DWIN_Long(i, value);
+ /*
+ DWIN_Byte(i, fvalue[3]);
+ DWIN_Byte(i, fvalue[2]);
+ DWIN_Byte(i, fvalue[1]);
+ DWIN_Byte(i, fvalue[0]);
+ */
+ DWIN_Send(i);
+}
+
+/*----------------------------------------------图片相关函数----------------------------------------------*/
+/*jpg图片显示并缓存在#0虚拟显示区 id:图片ID*/
+void DWIN_JPG_ShowAndCache(const uint8_t id) {
+ size_t i = 0;
+ DWIN_Word(i, 0x2200);
+ DWIN_Byte(i, id);
+ DWIN_Send(i); //AA 23 00 00 00 00 08 00 01 02 03 CC 33 C3 3C
+}
+
+/*图标显示 libID:图标库ID picID:图标ID x/y:图标左上坐标*/
+void DWIN_ICON_Show(uint8_t libID, uint8_t picID, uint16_t x, uint16_t y) {
+ NOMORE(x, DWIN_WIDTH - 1);
+ NOMORE(y, DWIN_HEIGHT - 1); // -- ozy -- srl
+ size_t i = 0;
+ DWIN_Byte(i, 0x23);
+ DWIN_Word(i, x);
+ DWIN_Word(i, y);
+ DWIN_Byte(i, 0x80 | libID);
+ DWIN_Byte(i, picID);
+ DWIN_Send(i);
+}
+
+/*jpg图片解压到#1虚拟显示区 id:图片ID*/
+void DWIN_JPG_CacheToN(uint8_t n, uint8_t id) {
+ size_t i = 0;
+ DWIN_Byte(i, 0x25);
+ DWIN_Byte(i, n);
+ DWIN_Byte(i, id);
+ DWIN_Send(i);
+}
+
+/*从虚拟显示区复制区域至当前画面 cacheID:虚拟区号 xStart/yStart:虚拟区左上坐标 xEnd/yEnd:虚拟区右下坐标 x/y:当前画面粘贴坐标*/
+void DWIN_Frame_AreaCopy(uint8_t cacheID, uint16_t xStart, uint16_t yStart,
+ uint16_t xEnd, uint16_t yEnd, uint16_t x, uint16_t y) {
+ size_t i = 0;
+ DWIN_Byte(i, 0x27);
+ DWIN_Byte(i, 0x80 | cacheID);
+ DWIN_Word(i, xStart);
+ DWIN_Word(i, yStart);
+ DWIN_Word(i, xEnd);
+ DWIN_Word(i, yEnd);
+ DWIN_Word(i, x);
+ DWIN_Word(i, y);
+ DWIN_Send(i);
+}
+
+#endif // DWIN_CREALITY_LCD
diff --git a/Marlin/src/lcd/dwin/dwin_lcd.h b/Marlin/src/lcd/dwin/dwin_lcd.h
new file mode 100644
index 0000000000..e7624b0b5a
--- /dev/null
+++ b/Marlin/src/lcd/dwin/dwin_lcd.h
@@ -0,0 +1,111 @@
+/**
+ * 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
+
+/********************************************************************************
+ * @file dwin_lcd.h
+ * @author LEO / Creality3D
+ * @date 2019/07/18
+ * @version 2.0.1
+ * @brief 迪文屏控制操作函数
+ ********************************************************************************/
+
+#include
+
+#define RECEIVED_NO_DATA 0x00
+#define RECEIVED_SHAKE_HAND_ACK 0x01
+
+#define FHONE 0xAA
+
+#define DWIN_SCROLL_UP 2
+#define DWIN_SCROLL_DOWN 3
+
+#define DWIN_WIDTH 272
+#define DWIN_HEIGHT 480
+
+/*接收数据解析 返回值:true,接收到数据;false,未接收到数据*/
+bool DWIN_ReceiveAnalyze(void);
+
+/*发送当前BUF中的数据以及包尾数据 len:整包数据长度*/
+void DWIN_Send_BufTail(const uint8_t len);
+
+/*----------------------------------------------系统变量函数----------------------------------------------*/
+/*握手 1: 握手成功 2: 握手失败*/
+bool DWIN_Handshake(void);
+
+/*设定背光亮度 luminance:亮度(0x00~0xFF)*/
+void DWIN_Backlight_SetLuminance(const uint8_t luminance);
+
+/*设定画面显示方向 dir:0,0°; 1,90°; 2,180°; 3,270°*/
+void DWIN_Frame_SetDir(uint8_t dir);
+
+/*更新显示*/
+void DWIN_UpdateLCD(void);
+
+/*----------------------------------------------绘图相关函数----------------------------------------------*/
+/*画面清屏 color:清屏颜色*/
+void DWIN_Frame_Clear(const uint16_t color);
+
+/*画面画线 color:线段颜色 xStart:X起始坐标 yStart:Y起始坐标 xEnd:X终止坐标 yEnd:Y终止坐标*/
+void DWIN_Draw_Line(uint16_t color, uint16_t xStart, uint16_t yStart, uint16_t xEnd, uint16_t yEnd);
+
+/*画面画矩形 mode:0,外框;1,填充;2,异或填充 color:颜色 xStart/yStart:矩形左上坐标 xEnd/yEnd:矩形右下坐标*/
+void DWIN_Draw_Rectangle(uint8_t mode, uint16_t color,
+ uint16_t xStart, uint16_t yStart, uint16_t xEnd, uint16_t yEnd);
+
+/*画面区域移动 mode:0,环移;1,平移 dir:0,向左移动;1,向右移动;2,向上移动;3,向下移动 dis:移动距离
+ color:填充颜色 xStart/yStart:选定区域左上坐标 xEnd/yEnd:选定区域右下坐标*/
+void DWIN_Frame_AreaMove(uint8_t mode, uint8_t dir, uint16_t dis,
+ uint16_t color, uint16_t xStart, uint16_t yStart, uint16_t xEnd, uint16_t yEnd);
+
+/*----------------------------------------------文本相关函数----------------------------------------------*/
+/*画面显示字符串 widthAdjust:true,自调整字符宽度;false,不调整字符宽度 bShow:true,显示背景色;false,不显示背景色 size:字号大小
+ color:字符颜色 bColor:背景颜色 x/y:字符串左上坐标 *string:字符串*/
+void DWIN_Draw_String(bool widthAdjust, bool bShow, uint8_t size,
+ uint16_t color, uint16_t bColor, uint16_t x, uint16_t y, char *string);
+
+/*画面显示正整数 bShow:true,显示背景色;false,不显示背景色 zeroFill:true,补零;false,不补零 zeroMode:1,无效0显示为0; 0,无效0显示为空格 size:字号大小
+ color:字符颜色 bColor:背景颜色 iNum:位数 x/y:变量左上坐标 value:整型变量*/
+void DWIN_Draw_IntValue(uint8_t bShow, bool zeroFill, uint8_t zeroMode, uint8_t size, uint16_t color,
+ uint16_t bColor, uint8_t iNum, uint16_t x, uint16_t y, uint16_t value);
+
+/*画面显示浮点数 bShow:true,显示背景色;false,不显示背景色 zeroFill:true,补零;false,不补零 zeroMode:1,无效0显示为0; 0,无效0显示为空格 size:字号大小
+ color:字符颜色 bColor:背景颜色 iNum:整数位数 fNum:小数位数 x/y:变量左上坐标 value:浮点数变量*/
+void DWIN_Draw_FloatValue(uint8_t bShow, bool zeroFill, uint8_t zeroMode, uint8_t size, uint16_t color,
+ uint16_t bColor, uint8_t iNum, uint8_t fNum, uint16_t x, uint16_t y, long value);
+
+/*----------------------------------------------图片相关函数----------------------------------------------*/
+/*jpg图片显示并缓存在#0虚拟显示区 id:图片ID*/
+void DWIN_JPG_ShowAndCache(const uint8_t id);
+
+/*图标显示 libID:图标库ID picID:图标ID x/y:图标左上坐标*/
+void DWIN_ICON_Show(uint8_t libID, uint8_t picID, uint16_t x, uint16_t y);
+
+/*jpg图片解压到#1虚拟显示区 id:图片ID*/
+void DWIN_JPG_CacheToN(uint8_t n, uint8_t id);
+
+/*jpg图片解压到#1虚拟显示区 id:图片ID*/
+inline void DWIN_JPG_CacheTo1(uint8_t id) { DWIN_JPG_CacheToN(1, id); }
+
+/*从虚拟显示区复制区域至当前画面 cacheID:虚拟区号 xStart/yStart:虚拟区左上坐标 xEnd/yEnd:虚拟区右下坐标 x/y:当前画面粘贴坐标*/
+void DWIN_Frame_AreaCopy(uint8_t cacheID, uint16_t xStart, uint16_t yStart,
+ uint16_t xEnd, uint16_t yEnd, uint16_t x, uint16_t y);
diff --git a/Marlin/src/lcd/dwin/eeprom_BL24CXX.cpp b/Marlin/src/lcd/dwin/eeprom_BL24CXX.cpp
new file mode 100644
index 0000000000..5201e06a09
--- /dev/null
+++ b/Marlin/src/lcd/dwin/eeprom_BL24CXX.cpp
@@ -0,0 +1,263 @@
+/**
+ * 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 eeprom_BL24CXX.cpp
+ * @brief i2c EEPROM for Ender 3 v2 board (4.2.2)
+ ********************************************************************************/
+
+#include "../../inc/MarlinConfig.h"
+
+#if ENABLED(IIC_BL24CXX_EEPROM)
+
+#include "eeprom_BL24CXX.h"
+#include "../../MarlinCore.h"
+
+#include
+
+/******************** IIC ********************/
+
+// 初始化IIC
+void IIC::init() {
+ SET_OUTPUT(IIC_EEPROM_SDA);
+ SET_OUTPUT(IIC_EEPROM_SCL);
+
+ IIC_SCL_1();
+ IIC_SDA_1();
+}
+
+// 产生IIC起始信号
+void IIC::start() {
+ SDA_OUT(); // sda线输出
+ IIC_SDA_1();
+ IIC_SCL_1();
+ delay_us(4);
+ IIC_SDA_0(); // START:when CLK is high, DATA change form high to low
+ delay_us(4);
+ IIC_SCL_0(); // 钳住I2C总线,准备发送或接收数据
+}
+
+// 产生IIC停止信号
+void IIC::stop() {
+ SDA_OUT(); // sda线输出
+ IIC_SCL_0();
+ IIC_SDA_0(); // STOP:when CLK is high DATA change form low to high
+ delay_us(4);
+ IIC_SCL_1();
+ IIC_SDA_1(); // 发送I2C总线结束信号
+ delay_us(4);
+}
+
+// 等待应答信号到来
+// 返回值:1,接收应答失败
+// 0,接收应答成功
+uint8_t IIC::wait_ack() {
+ uint8_t ucErrTime=0;
+ SDA_IN(); // SDA设置为输入
+ IIC_SDA_1();delay_us(1);
+ IIC_SCL_1();delay_us(1);
+ while (READ_SDA()) {
+ ucErrTime++;
+ if (ucErrTime>250) {
+ stop();
+ return 1;
+ }
+ }
+ IIC_SCL_0(); // 时钟输出0
+ return 0;
+}
+
+// 产生ACK应答
+void IIC::ack() {
+ IIC_SCL_0();
+ SDA_OUT();
+ IIC_SDA_0();
+ delay_us(2);
+ IIC_SCL_1();
+ delay_us(2);
+ IIC_SCL_0();
+}
+
+// 不产生ACK应答
+void IIC::nAck() {
+ IIC_SCL_0();
+ SDA_OUT();
+ IIC_SDA_1();
+ delay_us(2);
+ IIC_SCL_1();
+ delay_us(2);
+ IIC_SCL_0();
+}
+
+// IIC发送一个字节
+// 返回从机有无应答
+// 1,有应答
+// 0,无应答
+void IIC::send_byte(uint8_t txd) {
+ SDA_OUT();
+ IIC_SCL_0(); // 拉低时钟开始数据传输
+ LOOP_L_N(t, 8) {
+ // IIC_SDA = (txd & 0x80) >> 7;
+ if (txd & 0x80) IIC_SDA_1(); else IIC_SDA_0();
+ txd <<= 1;
+ delay_us(2); // 对TEA5767这三个延时都是必须的
+ IIC_SCL_1();
+ delay_us(2);
+ IIC_SCL_0();
+ delay_us(2);
+ }
+}
+
+// 读1个字节,ack=1时,发送ACK,ack=0,发送nACK
+uint8_t IIC::read_byte(unsigned char ack_chr) {
+ unsigned char receive = 0;
+ SDA_IN(); // SDA设置为输入
+ LOOP_L_N(i, 8) {
+ IIC_SCL_0();
+ delay_us(2);
+ IIC_SCL_1();
+ receive <<= 1;
+ if (READ_SDA()) receive++;
+ delay_us(1);
+ }
+ ack_chr ? ack() : nAck(); // 发送ACK / 发送nACK
+ return receive;
+}
+
+/******************** EEPROM ********************/
+
+// 初始化IIC接口
+void BL24CXX::init() { IIC::init(); }
+
+// 在BL24CXX指定地址读出一个数据
+// ReadAddr:开始读数的地址
+// 返回值 :读到的数据
+uint8_t BL24CXX::readOneByte(uint16_t ReadAddr) {
+ uint8_t temp = 0;
+ IIC::start();
+ if (EE_TYPE > BL24C16) {
+ IIC::send_byte(0xA0); // 发送写命令
+ IIC::wait_ack();
+ IIC::send_byte(ReadAddr >> 8); // 发送高地址
+ IIC::wait_ack();
+ }
+ else
+ IIC::send_byte(0xA0 + ((ReadAddr >> 8) << 1)); // 发送器件地址0xA0,写数据
+
+ IIC::wait_ack();
+ IIC::send_byte(ReadAddr & 0xFF); // 发送低地址
+ IIC::wait_ack();
+ IIC::start();
+ IIC::send_byte(0xA1); // 进入接收模式
+ IIC::wait_ack();
+ temp = IIC::read_byte(0);
+ IIC::stop(); // 产生一个停止条件
+ return temp;
+}
+
+// 在BL24CXX指定地址写入一个数据
+// WriteAddr :写入数据的目的地址
+// DataToWrite:要写入的数据
+void BL24CXX::writeOneByte(uint16_t WriteAddr, uint8_t DataToWrite) {
+ IIC::start();
+ if (EE_TYPE > BL24C16) {
+ IIC::send_byte(0xA0); // 发送写命令
+ IIC::wait_ack();
+ IIC::send_byte(WriteAddr >> 8); // 发送高地址
+ }
+ else {
+ IIC::send_byte(0xA0 + ((WriteAddr >> 8) << 1)); // 发送器件地址0xA0,写数据
+ }
+ IIC::wait_ack();
+ IIC::send_byte(WriteAddr & 0xFF); // 发送低地址
+ IIC::wait_ack();
+ IIC::send_byte(DataToWrite); // 发送字节
+ IIC::wait_ack();
+ IIC::stop(); // 产生一个停止条件
+ delay(10);
+}
+
+// 在BL24CXX里面的指定地址开始写入长度为Len的数据
+// 该函数用于写入16bit或者32bit的数据.
+// WriteAddr :开始写入的地址
+// DataToWrite:数据数组首地址
+// Len :要写入数据的长度2,4
+void BL24CXX::writeLenByte(uint16_t WriteAddr, uint32_t DataToWrite, uint8_t Len) {
+ LOOP_L_N(t, Len)
+ writeOneByte(WriteAddr + t, (DataToWrite >> (8 * t)) & 0xFF);
+}
+
+// 在BL24CXX里面的指定地址开始读出长度为Len的数据
+// 该函数用于读出16bit或者32bit的数据.
+// ReadAddr :开始读出的地址
+// 返回值 :数据
+// Len :要读出数据的长度2,4
+uint32_t BL24CXX::readLenByte(uint16_t ReadAddr, uint8_t Len) {
+ uint32_t temp = 0;
+ LOOP_L_N(t, Len) {
+ temp <<= 8;
+ temp += readOneByte(ReadAddr + Len - t - 1);
+ }
+ return temp;
+}
+
+// 检查BL24CXX是否正常
+// 这里用了24XX的最后一个地址(255)来存储标志字.
+// 如果用其他24C系列,这个地址要修改
+// 返回1:检测失败
+// 返回0:检测成功
+uint8_t BL24CXX::check() {
+ uint8_t temp;
+ temp = readOneByte(255); // 避免每次开机都写BL24CXX
+ if (temp == 'U') return 0;
+ else { // 排除第一次初始化的情况
+ writeOneByte(255, 'U');
+ temp = readOneByte(255);
+ if (temp == 'U') return 0;
+ }
+ return 1;
+}
+
+// 在BL24CXX里面的指定地址开始读出指定个数的数据
+// ReadAddr :开始读出的地址 对24c02为0~255
+// pBuffer :数据数组首地址
+// NumToRead:要读出数据的个数
+void BL24CXX::read(uint16_t ReadAddr, uint8_t *pBuffer, uint16_t NumToRead) {
+ while (NumToRead) {
+ *pBuffer++ = readOneByte(ReadAddr++);
+ NumToRead--;
+ }
+}
+
+// 在BL24CXX里面的指定地址开始写入指定个数的数据
+// WriteAddr :开始写入的地址 对24c02为0~255
+// pBuffer :数据数组首地址
+// NumToWrite:要写入数据的个数
+void BL24CXX::write(uint16_t WriteAddr, uint8_t *pBuffer, uint16_t NumToWrite) {
+ while (NumToWrite--) {
+ writeOneByte(WriteAddr, *pBuffer);
+ WriteAddr++;
+ pBuffer++;
+ }
+}
+
+#endif // IIC_BL24CXX_EEPROM
diff --git a/Marlin/src/lcd/dwin/eeprom_BL24CXX.h b/Marlin/src/lcd/dwin/eeprom_BL24CXX.h
new file mode 100644
index 0000000000..b655fb8e45
--- /dev/null
+++ b/Marlin/src/lcd/dwin/eeprom_BL24CXX.h
@@ -0,0 +1,86 @@
+/**
+ * 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
+
+/********************************************************************************
+ * @file eeprom_BL24CXX.h
+ * @brief i2c EEPROM for Ender 3 v2 board (4.2.2)
+ ********************************************************************************/
+
+#include
+
+/******************** IIC ********************/
+
+//IO方向设置
+#define SDA_IN() do{ PIN_MAP[IIC_EEPROM_SDA].gpio_device->regs->CRH &= 0XFFFF0FFF; PIN_MAP[IIC_EEPROM_SDA].gpio_device->regs->CRH |= 8 << 12; }while(0)
+#define SDA_OUT() do{ PIN_MAP[IIC_EEPROM_SDA].gpio_device->regs->CRH &= 0XFFFF0FFF; PIN_MAP[IIC_EEPROM_SDA].gpio_device->regs->CRH |= 3 << 12; }while(0)
+
+//IO操作函数
+#define IIC_SCL_0() WRITE(IIC_EEPROM_SCL, LOW)
+#define IIC_SCL_1() WRITE(IIC_EEPROM_SCL, HIGH)
+#define IIC_SDA_0() WRITE(IIC_EEPROM_SDA, LOW)
+#define IIC_SDA_1() WRITE(IIC_EEPROM_SDA, HIGH)
+#define READ_SDA() READ(IIC_EEPROM_SDA)
+
+class BL24CXX;
+
+// IIC所有操作函数
+class IIC {
+friend class BL24CXX;
+protected:
+ static void init(); // 初始化IIC的IO口
+ static void start(); // 发送IIC开始信号
+ static void stop(); // 发送IIC停止信号
+ static void send_byte(uint8_t txd); // IIC发送一个字节
+ static uint8_t read_byte(unsigned char ack); // IIC读取一个字节
+ static uint8_t wait_ack(); // IIC等待ACK信号
+ static void ack(); // IIC发送ACK信号
+ static void nAck(); // IIC不发送ACK信号
+
+ static void write_one_byte(uint8_t daddr, uint8_t addr, uint8_t data);
+ static uint8_t read_one_byte(uint8_t daddr, uint8_t addr);
+};
+
+/******************** EEPROM ********************/
+
+#define BL24C01 127
+#define BL24C02 255
+#define BL24C04 511
+#define BL24C08 1023
+#define BL24C16 2047
+#define BL24C32 4095
+#define BL24C64 8191
+#define BL24C128 16383
+#define BL24C256 32767
+#define EE_TYPE BL24C16
+
+class BL24CXX {
+public:
+ static void init(); //初始化IIC
+ static uint8_t check(); //检查器件
+ static uint8_t readOneByte(uint16_t ReadAddr); //指定地址读取一个字节
+ static void writeOneByte(uint16_t WriteAddr, uint8_t DataToWrite); //指定地址写入一个字节
+ static void writeLenByte(uint16_t WriteAddr, uint32_t DataToWrite, uint8_t Len);//指定地址开始写入指定长度的数据
+ static uint32_t readLenByte(uint16_t ReadAddr, uint8_t Len); //指定地址开始读取指定长度数据
+ static void write(uint16_t WriteAddr, uint8_t *pBuffer, uint16_t NumToWrite); //从指定地址开始写入指定长度的数据
+ static void read(uint16_t ReadAddr, uint8_t *pBuffer, uint16_t NumToRead); //从指定地址开始读出指定长度的数据
+};
diff --git a/Marlin/src/lcd/dwin/rotary_encoder.cpp b/Marlin/src/lcd/dwin/rotary_encoder.cpp
new file mode 100644
index 0000000000..71a0772d5a
--- /dev/null
+++ b/Marlin/src/lcd/dwin/rotary_encoder.cpp
@@ -0,0 +1,249 @@
+/**
+ * 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 rotary_encoder.cpp
+ * @author LEO / Creality3D
+ * @date 2019/07/06
+ * @version 2.0.1
+ * @brief 旋转编码器操作函数
+ ******************************************************************************
+**/
+
+#include "../../inc/MarlinConfigPre.h"
+
+#if ENABLED(DWIN_CREALITY_LCD)
+
+#include "rotary_encoder.h"
+
+#include "../../MarlinCore.h"
+#include "../../HAL/shared/Delay.h"
+
+#if HAS_BUZZER
+ #include "../../libs/buzzer.h"
+#endif
+
+#include
+
+ENCODER_Rate EncoderRate;
+
+/*蜂鸣器响*/
+void Encoder_tick(void) {
+ WRITE(BEEPER_PIN,1);
+ delay(10);
+ WRITE(BEEPER_PIN,0);
+}
+
+/*编码器初始化 PB12:Encoder_A PB13:Encoder_B PB14:Encoder_C*/
+void Encoder_Configuration(void) {
+ #if BUTTON_EXISTS(EN1)
+ SET_INPUT_PULLUP(BTN_EN1);
+ #endif
+ #if BUTTON_EXISTS(EN2)
+ SET_INPUT_PULLUP(BTN_EN2);
+ #endif
+ #if BUTTON_EXISTS(ENC)
+ SET_INPUT_PULLUP(BTN_ENC);
+ #endif
+ #ifdef BEEPER_PIN
+ SET_OUTPUT(BEEPER_PIN);
+ #endif
+}
+
+millis_t next_click_update_ms;
+/*接收数据解析 返回值:ENCODER_DIFF_NO,无状态; ENCODER_DIFF_CW,顺时针旋转; ENCODER_DIFF_CCW,逆时针旋转; ENCODER_DIFF_ENTER,按下*/
+ENCODER_DiffState Encoder_ReceiveAnalyze(void) {
+ const millis_t now = millis();
+ static unsigned char lastEncoderBits;
+ unsigned char newbutton = 0;
+ static signed char temp_diff = 0;
+
+ ENCODER_DiffState temp_diffState = ENCODER_DIFF_NO;
+ if (BUTTON_PRESSED(EN1)) newbutton |= 0x01;
+ if (BUTTON_PRESSED(EN2)) newbutton |= 0x02;
+ if (BUTTON_PRESSED(ENC)) {
+ if (ELAPSED(now, next_click_update_ms)) {
+ next_click_update_ms = millis() + 300;
+ Encoder_tick();
+ #if PIN_EXISTS(LCD_LED)
+ //LED_Action();
+ #endif
+ return ENCODER_DIFF_ENTER;
+ }
+ else return ENCODER_DIFF_NO;
+ }
+ if (newbutton != lastEncoderBits) {
+ switch (newbutton) {
+ case ENCODER_PHASE_0: {
+ if (lastEncoderBits == ENCODER_PHASE_3) temp_diff++;
+ else if (lastEncoderBits == ENCODER_PHASE_1) temp_diff--;
+ }break;
+ case ENCODER_PHASE_1: {
+ if (lastEncoderBits == ENCODER_PHASE_0) temp_diff++;
+ else if (lastEncoderBits == ENCODER_PHASE_2) temp_diff--;
+ }break;
+ case ENCODER_PHASE_2: {
+ if (lastEncoderBits == ENCODER_PHASE_1) temp_diff++;
+ else if (lastEncoderBits == ENCODER_PHASE_3) temp_diff--;
+ }break;
+ case ENCODER_PHASE_3: {
+ if (lastEncoderBits == ENCODER_PHASE_2) temp_diff++;
+ else if (lastEncoderBits == ENCODER_PHASE_0) temp_diff--;
+ }break;
+ }
+ lastEncoderBits = newbutton;
+ }
+
+ if (abs(temp_diff) >= ENCODER_PULSES_PER_STEP) {
+ if (temp_diff > 0) temp_diffState = ENCODER_DIFF_CW;
+ else temp_diffState = ENCODER_DIFF_CCW;
+
+ #if ENABLED(ENCODER_RATE_MULTIPLIER)
+
+ millis_t ms = millis();
+ int32_t encoderMultiplier = 1;
+
+ // if must encoder rati multiplier
+ if (EncoderRate.encoderRateEnabled) {
+ const float abs_diff = ABS(temp_diff);
+ const float encoderMovementSteps = abs_diff / (ENCODER_PULSES_PER_STEP);
+ if (EncoderRate.lastEncoderTime) {
+ // Note that the rate is always calculated between two passes through the
+ // loop and that the abs of the temp_diff value is tracked.
+ const float encoderStepRate = encoderMovementSteps / float(ms - EncoderRate.lastEncoderTime) * 1000;
+ if (encoderStepRate >= ENCODER_100X_STEPS_PER_SEC) encoderMultiplier = 100;
+ else if (encoderStepRate >= ENCODER_10X_STEPS_PER_SEC) encoderMultiplier = 10;
+ else if (encoderStepRate >= ENCODER_5X_STEPS_PER_SEC) encoderMultiplier = 5;
+ }
+ EncoderRate.lastEncoderTime = ms;
+ }
+ #else
+ constexpr int32_t encoderMultiplier = 1;
+ #endif // ENCODER_RATE_MULTIPLIER
+
+ // EncoderRate.encoderMoveValue += (temp_diff * encoderMultiplier) / (ENCODER_PULSES_PER_STEP);
+ EncoderRate.encoderMoveValue = (temp_diff * encoderMultiplier) / (ENCODER_PULSES_PER_STEP);
+ if (EncoderRate.encoderMoveValue < 0) EncoderRate.encoderMoveValue = -EncoderRate.encoderMoveValue;
+
+ temp_diff = 0;
+ }
+ return temp_diffState;
+}
+
+#if PIN_EXISTS(LCD_LED)
+
+ /*取低24位有效 24Bit: G7 G6 G5 G4 G3 G2 G1 G0 R7 R6 R5 R4 R3 R2 R1 R0 B7 B6 B5 B4 B3 B2 B1 B0*/
+ unsigned int LED_DataArray[LED_NUM];
+
+ /*LED灯操作*/
+ void LED_Action(void) {
+ LED_Control(RGB_SCALE_WARM_WHITE,0x0F);
+ delay(30);
+ LED_Control(RGB_SCALE_WARM_WHITE,0x00);
+ }
+
+ /*LED初始化*/
+ void LED_Configuration(void) {
+ SET_OUTPUT(LCD_LED_PIN);
+ }
+
+ /*LED写数据*/
+ void LED_WriteData(void) {
+ unsigned char tempCounter_LED, tempCounter_Bit;
+ for (tempCounter_LED = 0; tempCounter_LED < LED_NUM; tempCounter_LED++) {
+ for (tempCounter_Bit = 0; tempCounter_Bit < 24; tempCounter_Bit++) {
+ if (LED_DataArray[tempCounter_LED] & (0x800000 >> tempCounter_Bit)) {
+ LED_DATA_HIGH;
+ DELAY_NS(300);
+ LED_DATA_LOW;
+ DELAY_NS(200);
+ }
+ else {
+ LED_DATA_HIGH;
+ LED_DATA_LOW;
+ DELAY_NS(200);
+ }
+ }
+ }
+ }
+
+ /*LED控制 RGB_Scale:RGB色彩配比 luminance:亮度(0~0xFF)*/
+ void LED_Control(unsigned char RGB_Scale, unsigned char luminance) {
+ unsigned char temp_Counter;
+ for (temp_Counter = 0; temp_Counter < LED_NUM; temp_Counter++) {
+ LED_DataArray[temp_Counter] = 0;
+ switch(RGB_Scale) {
+ case RGB_SCALE_R10_G7_B5: LED_DataArray[temp_Counter] = (luminance*10/10) << 8 | (luminance*7/10) << 16 | luminance*5/10; break;
+ case RGB_SCALE_R10_G7_B4: LED_DataArray[temp_Counter] = (luminance*10/10) << 8 | (luminance*7/10) << 16 | luminance*4/10; break;
+ case RGB_SCALE_R10_G8_B7: LED_DataArray[temp_Counter] = (luminance*10/10) << 8 | (luminance*8/10) << 16 | luminance*7/10; break;
+ }
+ }
+ LED_WriteData();
+ }
+
+ /*LED渐变控制 RGB_Scale:RGB色彩配比 luminance:亮度(0~0xFF) change_Time:渐变时间(ms)*/
+ void LED_GraduallyControl(unsigned char RGB_Scale, unsigned char luminance, unsigned int change_Interval) {
+ unsigned char temp_Counter;
+ unsigned char LED_R_Data[LED_NUM], LED_G_Data[LED_NUM], LED_B_Data[LED_NUM];
+ bool LED_R_Flag = 0, LED_G_Flag = 0, LED_B_Flag = 0;
+
+ for (temp_Counter = 0; temp_Counter < LED_NUM; temp_Counter++) {
+ switch(RGB_Scale) {
+ case RGB_SCALE_R10_G7_B5: {
+ LED_R_Data[temp_Counter] = luminance*10/10;
+ LED_G_Data[temp_Counter] = luminance*7/10;
+ LED_B_Data[temp_Counter] = luminance*5/10;
+ }break;
+ case RGB_SCALE_R10_G7_B4: {
+ LED_R_Data[temp_Counter] = luminance*10/10;
+ LED_G_Data[temp_Counter] = luminance*7/10;
+ LED_B_Data[temp_Counter] = luminance*4/10;
+ }break;
+ case RGB_SCALE_R10_G8_B7: {
+ LED_R_Data[temp_Counter] = luminance*10/10;
+ LED_G_Data[temp_Counter] = luminance*8/10;
+ LED_B_Data[temp_Counter] = luminance*7/10;
+ }break;
+ }
+ }
+ for (temp_Counter = 0; temp_Counter < LED_NUM; temp_Counter++) {
+ if ((unsigned char)(LED_DataArray[temp_Counter] >> 8) > LED_R_Data[temp_Counter]) LED_DataArray[temp_Counter] -= 0x000100;
+ else if ((unsigned char)(LED_DataArray[temp_Counter] >> 8) < LED_R_Data[temp_Counter]) LED_DataArray[temp_Counter] += 0x000100;
+ while (1) {
+ else LED_R_Flag = 1;
+ if ((unsigned char)(LED_DataArray[temp_Counter]>>16) > LED_G_Data[temp_Counter]) LED_DataArray[temp_Counter] -= 0x010000;
+ else if ((unsigned char)(LED_DataArray[temp_Counter]>>16) < LED_G_Data[temp_Counter]) LED_DataArray[temp_Counter] += 0x010000;
+ else LED_G_Flag = 1;
+ if ((unsigned char)LED_DataArray[temp_Counter] > LED_B_Data[temp_Counter]) LED_DataArray[temp_Counter] -= 0x000001;
+ else if ((unsigned char)LED_DataArray[temp_Counter] < LED_B_Data[temp_Counter]) LED_DataArray[temp_Counter] += 0x000001;
+ else LED_B_Flag = 1;
+ }
+ LED_WriteData();
+ if (LED_R_Flag && LED_G_Flag && LED_B_Flag) break;
+ else delay(change_Interval);
+ }
+ }
+
+#endif
+
+#endif // DWIN_CREALITY_LCD
diff --git a/Marlin/src/lcd/dwin/rotary_encoder.h b/Marlin/src/lcd/dwin/rotary_encoder.h
new file mode 100644
index 0000000000..d650d53f65
--- /dev/null
+++ b/Marlin/src/lcd/dwin/rotary_encoder.h
@@ -0,0 +1,105 @@
+/**
+ * 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
+
+/**
+ ******************************************************************************
+ * @file rotary_encoder.h
+ * @author LEO / Creality3D
+ * @date 2019/07/06
+ * @version 2.0.1
+ * @brief 旋转编码器操作函数
+ ******************************************************************************
+**/
+
+#include "../../inc/MarlinConfig.h"
+#include "../../MarlinCore.h"
+
+/*********************** Encoder Set ***********************/
+
+#define ENCODER_PHASE_0 0
+#define ENCODER_PHASE_1 2
+#define ENCODER_PHASE_2 3
+#define ENCODER_PHASE_3 1
+
+#define ENCODER_PULSES_PER_STEP 4
+
+#define BUTTON_PRESSED(BN) !READ(BTN_## BN)
+
+typedef struct {
+ bool encoderRateEnabled = 0;
+ int encoderMoveValue = 0;
+ millis_t lastEncoderTime = 0;
+} ENCODER_Rate;
+
+extern ENCODER_Rate EncoderRate;
+
+typedef enum {
+ ENCODER_DIFF_NO = 0,
+ ENCODER_DIFF_CW = 1,
+ ENCODER_DIFF_CCW = 2,
+ ENCODER_DIFF_ENTER = 3
+} ENCODER_DiffState;
+
+/*编码器初始化 PB12:Encoder_A PB13:Encoder_B PB14:Encoder_C*/
+void Encoder_Configuration(void);
+
+/*接收数据解析 返回值:ENCODER_DIFF_NO,无状态; ENCODER_DIFF_CW,顺时针旋转; ENCODER_DIFF_CCW,逆时针旋转; ENCODER_DIFF_ENTER,按下*/
+ENCODER_DiffState Encoder_ReceiveAnalyze(void);
+
+
+/*********************** Encoder LED ***********************/
+
+#if PIN_EXISTS(LCD_LED)
+
+ #define LED_NUM 4
+ #define LED_DATA_HIGH WRITE(LCD_LED_PIN, 1)
+ #define LED_DATA_LOW WRITE(LCD_LED_PIN, 0)
+
+ #define RGB_SCALE_R10_G7_B5 1
+ #define RGB_SCALE_R10_G7_B4 2
+ #define RGB_SCALE_R10_G8_B7 3
+ #define RGB_SCALE_NEUTRAL_WHITE RGB_SCALE_R10_G7_B5 //正白
+ #define RGB_SCALE_WARM_WHITE RGB_SCALE_R10_G7_B4 //暖白
+ #define RGB_SCALE_COOL_WHITE RGB_SCALE_R10_G8_B7 //冷白
+
+ extern unsigned int LED_DataArray[LED_NUM];
+
+ /*状态LED初始化*/
+ void STATE_LED_Configuration(void);
+
+ /*LED灯操作*/
+ void LED_Action(void);
+
+ /*LED初始化*/
+ void LED_Configuration(void);
+
+ /*LED写数据*/
+ void LED_WriteData(void);
+
+ /*LED控制 RGB_Scale:RGB色彩配比 luminance:亮度(0~0xFF)*/
+ void LED_Control(unsigned char RGB_Scale, unsigned char luminance);
+
+ /*LED渐变控制 RGB_Scale:RGB色彩配比 luminance:亮度(0~0xFF) change_Time:渐变时间(ms)*/
+ void LED_GraduallyControl(unsigned char RGB_Scale, unsigned char luminance, unsigned int change_Interval);
+
+#endif
diff --git a/Marlin/src/lcd/ultralcd.cpp b/Marlin/src/lcd/ultralcd.cpp
index 6b52f64b90..9fa5f3a67a 100644
--- a/Marlin/src/lcd/ultralcd.cpp
+++ b/Marlin/src/lcd/ultralcd.cpp
@@ -40,7 +40,7 @@ MarlinUI ui;
#include "../gcode/queue.h"
#include "fontutils.h"
#include "../sd/cardreader.h"
- #if ENABLED(EXTENSIBLE_UI)
+ #if EITHER(EXTENSIBLE_UI, DWIN_CREALITY_LCD)
#define START_OF_UTF8_CHAR(C) (((C) & 0xC0u) != 0x80u)
#endif
#endif
@@ -56,7 +56,7 @@ MarlinUI ui;
#else
constexpr uint8_t MAX_MESSAGE_LENGTH = MAX_LANG_CHARSIZE * (LCD_WIDTH);
#endif
-#elif ENABLED(EXTENSIBLE_UI)
+#elif EITHER(EXTENSIBLE_UI, DWIN_CREALITY_LCD)
constexpr uint8_t MAX_MESSAGE_LENGTH = 63;
#endif
@@ -145,41 +145,42 @@ millis_t MarlinUI::next_button_update_ms; // = 0
volatile int8_t encoderDiff; // Updated in update_buttons, added to encoderPosition every LCD update
#endif
-#if HAS_LCD_MENU
- #include "menu/menu.h"
+#if ENABLED(SDSUPPORT)
+
#include "../sd/cardreader.h"
- #if ENABLED(SDSUPPORT)
-
- #if ENABLED(SCROLL_LONG_FILENAMES)
- uint8_t MarlinUI::filename_scroll_pos, MarlinUI::filename_scroll_max;
- #endif
-
- const char * MarlinUI::scrolled_filename(CardReader &theCard, const uint8_t maxlen, uint8_t hash, const bool doScroll) {
- const char *outstr = theCard.longest_filename();
- if (theCard.longFilename[0]) {
- #if ENABLED(SCROLL_LONG_FILENAMES)
- if (doScroll) {
- for (uint8_t l = FILENAME_LENGTH; l--;)
- hash = ((hash << 1) | (hash >> 7)) ^ theCard.filename[l]; // rotate, xor
- static uint8_t filename_scroll_hash;
- if (filename_scroll_hash != hash) { // If the hash changed...
- filename_scroll_hash = hash; // Save the new hash
- filename_scroll_max = _MAX(0, utf8_strlen(theCard.longFilename) - maxlen); // Update the scroll limit
- filename_scroll_pos = 0; // Reset scroll to the start
- lcd_status_update_delay = 8; // Don't scroll right away
- }
- outstr += filename_scroll_pos;
- }
- #else
- theCard.longFilename[maxlen] = '\0'; // cutoff at screen edge
- #endif
- }
- return outstr;
- }
-
+ #if MARLINUI_SCROLL_NAME
+ uint8_t MarlinUI::filename_scroll_pos, MarlinUI::filename_scroll_max;
#endif
+ const char * MarlinUI::scrolled_filename(CardReader &theCard, const uint8_t maxlen, uint8_t hash, const bool doScroll) {
+ const char *outstr = theCard.longest_filename();
+ if (theCard.longFilename[0]) {
+ #if MARLINUI_SCROLL_NAME
+ if (doScroll) {
+ for (uint8_t l = FILENAME_LENGTH; l--;)
+ hash = ((hash << 1) | (hash >> 7)) ^ theCard.filename[l]; // rotate, xor
+ static uint8_t filename_scroll_hash;
+ if (filename_scroll_hash != hash) { // If the hash changed...
+ filename_scroll_hash = hash; // Save the new hash
+ filename_scroll_max = _MAX(0, utf8_strlen(theCard.longFilename) - maxlen); // Update the scroll limit
+ filename_scroll_pos = 0; // Reset scroll to the start
+ lcd_status_update_delay = 8; // Don't scroll right away
+ }
+ outstr += filename_scroll_pos;
+ }
+ #else
+ theCard.longFilename[maxlen] = '\0'; // cutoff at screen edge
+ #endif
+ }
+ return outstr;
+ }
+
+#endif
+
+#if HAS_LCD_MENU
+ #include "menu/menu.h"
+
screenFunc_t MarlinUI::currentScreen; // Initialized in CTOR
bool MarlinUI::screen_changed;
@@ -1579,5 +1580,7 @@ void MarlinUI::update() {
set_status_P(eeprom_err(msgid));
#endif
}
- #endif
-#endif
+
+ #endif // EEPROM_AUTO_INIT
+
+#endif // EEPROM_SETTINGS
diff --git a/Marlin/src/lcd/ultralcd.h b/Marlin/src/lcd/ultralcd.h
index 6ddacaf706..6eaca31c71 100644
--- a/Marlin/src/lcd/ultralcd.h
+++ b/Marlin/src/lcd/ultralcd.h
@@ -27,6 +27,10 @@
#include "../libs/buzzer.h"
#endif
+#if ENABLED(SDSUPPORT)
+ #include "../sd/cardreader.h"
+#endif
+
#if EITHER(HAS_LCD_MENU, ULTIPANEL_FEEDMULTIPLY)
#define HAS_ENCODER_ACTION 1
#endif
@@ -111,7 +115,7 @@
#endif // HAS_LCD_MENU
-#endif
+#endif // HAS_SPI_LCD
// REPRAPWORLD_KEYPAD (and ADC_KEYPAD)
#if ENABLED(REPRAPWORLD_KEYPAD)
@@ -287,9 +291,13 @@ public:
static void init_lcd();
FORCE_INLINE static void refresh() { refresh(LCDVIEW_CLEAR_CALL_REDRAW); }
#else
+ #if ENABLED(DWIN_CREALITY_LCD)
+ static void refresh();
+ #else
+ static inline void refresh() {}
+ #endif
static inline bool detected() { return true; }
static inline void init_lcd() {}
- static inline void refresh() {}
#endif
#if HAS_DISPLAY
@@ -451,6 +459,16 @@ public:
#endif
+ #if ENABLED(SDSUPPORT)
+ #if BOTH(SCROLL_LONG_FILENAMES, HAS_LCD_MENU)
+ #define MARLINUI_SCROLL_NAME 1
+ #endif
+ #if MARLINUI_SCROLL_NAME
+ static uint8_t filename_scroll_pos, filename_scroll_max;
+ #endif
+ static const char * scrolled_filename(CardReader &theCard, const uint8_t maxlen, uint8_t hash, const bool doScroll);
+ #endif
+
#if HAS_LCD_MENU
#if ENABLED(TOUCH_BUTTONS)
@@ -464,13 +482,6 @@ public:
static void enable_encoder_multiplier(const bool onoff);
#endif
- #if ENABLED(SDSUPPORT)
- #if ENABLED(SCROLL_LONG_FILENAMES)
- static uint8_t filename_scroll_pos, filename_scroll_max;
- #endif
- static const char * scrolled_filename(CardReader &theCard, const uint8_t maxlen, uint8_t hash, const bool doScroll);
- #endif
-
#if IS_KINEMATIC
static bool processing_manual_move;
#else
diff --git a/Marlin/src/module/configuration_store.cpp b/Marlin/src/module/configuration_store.cpp
index aa150671d2..96ca20e8a2 100644
--- a/Marlin/src/module/configuration_store.cpp
+++ b/Marlin/src/module/configuration_store.cpp
@@ -50,6 +50,11 @@
#include "planner.h"
#include "stepper.h"
#include "temperature.h"
+
+#if ENABLED(DWIN_CREALITY_LCD)
+ #include "../lcd/dwin/dwin.h"
+#endif
+
#include "../lcd/ultralcd.h"
#include "../libs/vector_3.h" // for matrix_3x3
#include "../gcode/gcode.h"
@@ -804,6 +809,10 @@ void MarlinSettings::postprocess() {
const int16_t (&ui_preheat_hotend_temp)[2] = ui.preheat_hotend_temp,
(&ui_preheat_bed_temp)[2] = ui.preheat_bed_temp;
const uint8_t (&ui_preheat_fan_speed)[2] = ui.preheat_fan_speed;
+ #elif ENABLED(DWIN_CREALITY_LCD)
+ const int16_t (&ui_preheat_hotend_temp)[2] = HMI_ValueStruct.preheat_hotend_temp,
+ (&ui_preheat_bed_temp)[2] = HMI_ValueStruct.preheat_bed_temp;
+ const uint8_t (&ui_preheat_fan_speed)[2] = HMI_ValueStruct.preheat_fan_speed;
#else
constexpr int16_t ui_preheat_hotend_temp[2] = { PREHEAT_1_TEMP_HOTEND, PREHEAT_2_TEMP_HOTEND },
ui_preheat_bed_temp[2] = { PREHEAT_1_TEMP_BED, PREHEAT_2_TEMP_BED };
@@ -1664,7 +1673,11 @@ void MarlinSettings::postprocess() {
int16_t (&ui_preheat_hotend_temp)[2] = ui.preheat_hotend_temp,
(&ui_preheat_bed_temp)[2] = ui.preheat_bed_temp;
uint8_t (&ui_preheat_fan_speed)[2] = ui.preheat_fan_speed;
- #else
+ #elif ENABLED(DWIN_CREALITY_LCD)
+ int16_t (&ui_preheat_hotend_temp)[2] = HMI_ValueStruct.preheat_hotend_temp,
+ (&ui_preheat_bed_temp)[2] = HMI_ValueStruct.preheat_bed_temp;
+ uint8_t (&ui_preheat_fan_speed)[2] = HMI_ValueStruct.preheat_fan_speed;
+ #else
int16_t ui_preheat_hotend_temp[2], ui_preheat_bed_temp[2];
uint8_t ui_preheat_fan_speed[2];
#endif
@@ -2539,14 +2552,22 @@ void MarlinSettings::reset() {
//
// Preheat parameters
//
-
- #if HAS_HOTEND && HAS_LCD_MENU
- ui.preheat_hotend_temp[0] = PREHEAT_1_TEMP_HOTEND;
- ui.preheat_hotend_temp[1] = PREHEAT_2_TEMP_HOTEND;
- ui.preheat_bed_temp[0] = PREHEAT_1_TEMP_BED;
- ui.preheat_bed_temp[1] = PREHEAT_2_TEMP_BED;
- ui.preheat_fan_speed[0] = PREHEAT_1_FAN_SPEED;
- ui.preheat_fan_speed[1] = PREHEAT_2_FAN_SPEED;
+ #if HAS_HOTEND
+ #if ENABLED(DWIN_CREALITY_LCD)
+ HMI_ValueStruct.preheat_hotend_temp[0] = PREHEAT_1_TEMP_HOTEND;
+ HMI_ValueStruct.preheat_hotend_temp[1] = PREHEAT_2_TEMP_HOTEND;
+ HMI_ValueStruct.preheat_bed_temp[0] = PREHEAT_1_TEMP_BED;
+ HMI_ValueStruct.preheat_bed_temp[1] = PREHEAT_2_TEMP_BED;
+ HMI_ValueStruct.preheat_fan_speed[0] = PREHEAT_1_FAN_SPEED;
+ HMI_ValueStruct.preheat_fan_speed[1] = PREHEAT_2_FAN_SPEED;
+ #elif HAS_LCD_MENU
+ ui.preheat_hotend_temp[0] = PREHEAT_1_TEMP_HOTEND;
+ ui.preheat_hotend_temp[1] = PREHEAT_2_TEMP_HOTEND;
+ ui.preheat_bed_temp[0] = PREHEAT_1_TEMP_BED;
+ ui.preheat_bed_temp[1] = PREHEAT_2_TEMP_BED;
+ ui.preheat_fan_speed[0] = PREHEAT_1_FAN_SPEED;
+ ui.preheat_fan_speed[1] = PREHEAT_2_FAN_SPEED;
+ #endif
#endif
//
diff --git a/Marlin/src/module/temperature.cpp b/Marlin/src/module/temperature.cpp
index 1fcc849241..2b7452a78a 100644
--- a/Marlin/src/module/temperature.cpp
+++ b/Marlin/src/module/temperature.cpp
@@ -32,6 +32,11 @@
#include "../HAL/shared/Delay.h"
#include "../lcd/ultralcd.h"
+
+#if ENABLED(DWIN_CREALITY_LCD)
+ #include "../lcd/dwin/dwin.h"
+#endif
+
#if ENABLED(EXTENSIBLE_UI)
#include "../lcd/extui/ui_api.h"
#endif
@@ -560,6 +565,7 @@ volatile bool Temperature::raw_temps_ready = false;
#define MAX_CYCLE_TIME_PID_AUTOTUNE 20L
#endif
if (((ms - t1) + (ms - t2)) > (MAX_CYCLE_TIME_PID_AUTOTUNE * 60L * 1000L)) {
+ TERN_(DWIN_CREALITY_LCD, Popup_Window_Temperature(0));
TERN_(EXTENSIBLE_UI, ExtUI::onPidTuning(ExtUI::result_t::PID_TUNING_TIMEOUT));
SERIAL_ECHOLNPGM(STR_PID_TIMEOUT);
break;
@@ -612,7 +618,7 @@ volatile bool Temperature::raw_temps_ready = false;
goto EXIT_M303;
}
- ui.update();
+ TERN(DWIN_CREALITY_LCD, DWIN_Update(), ui.update());
}
disable_all_heaters();
@@ -794,10 +800,12 @@ void Temperature::_temp_error(const heater_ind_t heater, PGM_P const serial_msg,
}
void Temperature::max_temp_error(const heater_ind_t heater) {
+ TERN_(DWIN_CREALITY_LCD, Popup_Window_Temperature(1));
_temp_error(heater, PSTR(STR_T_MAXTEMP), GET_TEXT(MSG_ERR_MAXTEMP));
}
void Temperature::min_temp_error(const heater_ind_t heater) {
+ TERN_(DWIN_CREALITY_LCD, Popup_Window_Temperature(0));
_temp_error(heater, PSTR(STR_T_MINTEMP), GET_TEXT(MSG_ERR_MINTEMP));
}
@@ -1029,8 +1037,10 @@ void Temperature::manage_heater() {
#if WATCH_HOTENDS
// Make sure temperature is increasing
if (watch_hotend[e].next_ms && ELAPSED(ms, watch_hotend[e].next_ms)) { // Time to check this extruder?
- if (degHotend(e) < watch_hotend[e].target) // Failed to increase enough?
+ if (degHotend(e) < watch_hotend[e].target) { // Failed to increase enough?
+ TERN_(DWIN_CREALITY_LCD, Popup_Window_Temperature(0));
_temp_error((heater_ind_t)e, str_t_heating_failed, GET_TEXT(MSG_HEATING_FAILED_LCD));
+ }
else // Start again if the target is still far off
start_watching_hotend(e);
}
@@ -1071,8 +1081,10 @@ void Temperature::manage_heater() {
#if WATCH_BED
// Make sure temperature is increasing
if (watch_bed.elapsed(ms)) { // Time to check the bed?
- if (degBed() < watch_bed.target) // Failed to increase enough?
+ if (degBed() < watch_bed.target) { // Failed to increase enough?
+ TERN_(DWIN_CREALITY_LCD, Popup_Window_Temperature(0));
_temp_error(H_BED, str_t_heating_failed, GET_TEXT(MSG_HEATING_FAILED_LCD));
+ }
else // Start again if the target is still far off
start_watching_bed();
}
@@ -1981,6 +1993,7 @@ void Temperature::init() {
sm.state = TRRunaway;
case TRRunaway:
+ TERN_(DWIN_CREALITY_LCD, Popup_Window_Temperature(0));
_temp_error(heater_id, str_t_thermal_runaway, GET_TEXT(MSG_THERMAL_RUNAWAY));
}
}
@@ -3082,7 +3095,13 @@ void Temperature::tick() {
} while (wait_for_heatup && TEMP_CONDITIONS);
if (wait_for_heatup) {
- ui.reset_status();
+ #if ENABLED(DWIN_CREALITY_LCD)
+ HMI_flag.heat_flag = 0;
+ duration_t elapsed = print_job_timer.duration(); // print timer
+ heat_time = elapsed.value;
+ #else
+ ui.reset_status();
+ #endif
TERN_(PRINTER_EVENT_LEDS, printerEventLEDs.onHeatingDone());
}
diff --git a/Marlin/src/pins/pins.h b/Marlin/src/pins/pins.h
index 36bf7408e1..58b5b942fb 100644
--- a/Marlin/src/pins/pins.h
+++ b/Marlin/src/pins/pins.h
@@ -544,6 +544,8 @@
#include "stm32f1/pins_CHITU3D_V5.h" // STM32F1 env:chitu_f103 env:chitu_v5_gpio_init
#elif MB(CHITU3D_V6)
#include "stm32f1/pins_CHITU3D_V6.h" // STM32F1 env:chitu_f103
+#elif MB(CREALITY_V4)
+ #include "stm32f1/pins_CREALITY_V4.h" // STM32F1 env:STM32F103RET6_creality
//
// ARM Cortex-M4F
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 59a9de7078..e63fa7f9cb 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
@@ -201,7 +201,7 @@
#define MOSI_PIN PA7
#define SS_PIN PA4
#endif
-#define ON_BOARD_SPI_DEVICE 1 //SPI1
+#define ON_BOARD_SPI_DEVICE 1 // SPI1
#define ONBOARD_SD_CS_PIN PA4 // Chip select for "System" SD card
#if HAS_GRAPHICAL_LCD
diff --git a/Marlin/src/pins/stm32f1/pins_CREALITY_V4.h b/Marlin/src/pins/stm32f1/pins_CREALITY_V4.h
new file mode 100644
index 0000000000..f2a45e652e
--- /dev/null
+++ b/Marlin/src/pins/stm32f1/pins_CREALITY_V4.h
@@ -0,0 +1,167 @@
+/**
+ * Marlin 3D Printer Firmware
+ * Copyright (C) 2016 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 .
+ *
+ */
+
+/**
+ * CREALITY (STM32F103) board pin assignments
+ */
+
+#ifndef __STM32F1__
+ #error "Oops! Select an STM32F1 board in 'Tools > Board.'"
+#endif
+
+#if HOTENDS > 1 || E_STEPPERS > 1
+ #error "CREALITY supports up to 1 hotends / E-steppers. Comment out this line to continue."
+#endif
+
+#define BOARD_INFO_NAME "CREALITY V4"
+#define DEFAULT_MACHINE_NAME "Ender 3 V2"
+
+//
+// EEPROM
+//
+
+/* I2C */
+#define IIC_BL24CXX_EEPROM // EEPROM on I2C-0
+//#define E2END 0x3FFF // 16Kb (24c16)
+#define IIC_EEPROM_SDA PA11
+#define IIC_EEPROM_SCL PA12
+
+// SD EEPROM was in your original build, so...
+#define SDCARD_EEPROM_EMULATION
+
+/* SPI */
+//#define SPI_EEPROM // EEPROM on SPI-0
+//#define SPI_CHAN_EEPROM1 ?
+//#define SPI_EEPROM1_CS ?
+// 2K EEPROM
+//#define SPI_EEPROM2_CS ?
+// 32Mb FLASH
+//#define SPI_FLASH_CS ?
+
+/* FLASH */
+//#define FLASH_EEPROM_EMULATION
+
+//
+// Servos
+//
+#define SERVO0_PIN PB0 // BLTouch OUT
+
+//
+// Limit Switches
+//
+#define X_STOP_PIN PA5
+#define Y_STOP_PIN PA6
+#define Z_STOP_PIN PA7
+
+#define Z_PROBE_PIN PB1 // BLTouch IN
+
+//
+// Steppers
+//
+#define X_ENABLE_PIN PC3
+#define X_STEP_PIN PC2
+#define X_DIR_PIN PB9
+
+#define Y_ENABLE_PIN PC3
+#define Y_STEP_PIN PB8
+#define Y_DIR_PIN PB7
+
+#define Z_ENABLE_PIN PC3
+#define Z_STEP_PIN PB6
+#define Z_DIR_PIN PB5
+
+#define E0_ENABLE_PIN PC3
+#define E0_STEP_PIN PB4
+#define E0_DIR_PIN PB3
+
+//
+// Release PB4 (Y_ENABLE_PIN) from JTAG NRST role
+//
+#define DISABLE_DEBUG
+
+//
+// Temperature Sensors
+//
+#define TEMP_0_PIN PC5 // TH1
+#define TEMP_BED_PIN PC4 // TB1
+
+//
+// Heaters / Fans
+//
+#define HEATER_0_PIN PA1 // HEATER1
+#define HEATER_BED_PIN PA2 // HOT BED
+
+#define FAN_PIN PA0 // FAN
+#define FAN_SOFT_PWM
+
+//
+// SD Card
+//
+#define SD_DETECT_PIN PC7
+#define SDCARD_CONNECTION ONBOARD
+#define ON_BOARD_SPI_DEVICE 1
+#define ONBOARD_SD_CS_PIN PA4 // SDSS
+#define SDIO_SUPPORT
+
+#if ENABLED(RET6_12864_LCD)
+
+ /* RET6 12864 LCD */
+ #define LCD_PINS_RS PB12
+ #define LCD_PINS_ENABLE PB15
+ #define LCD_PINS_D4 PB13
+
+ #define BTN_ENC PB2
+ #define BTN_EN1 PB10
+ #define BTN_EN2 PB14
+
+ #define BEEPER_PIN PC6
+#elif ENABLED(VET6_12864_LCD)
+
+ /* VET6 12864 LCD */
+ #define LCD_PINS_RS PA4
+ #define LCD_PINS_ENABLE PA7
+ #define LCD_PINS_D4 PA5
+
+ #define BTN_ENC PC5
+ #define BTN_EN1 PB10
+ #define BTN_EN2 PA6
+
+#elif ENABLED(DWIN_CREALITY_LCD)
+
+ /* RET6 DWIN ENCODER LCD */
+ #define BTN_ENC PB14
+ #define BTN_EN1 PB15
+ #define BTN_EN2 PB12
+
+ //#define LCD_LED_PIN PB2
+ #define BEEPER_PIN PB13
+
+#elif ENABLED(DWIN_VET6_CREALITY_LCD)
+
+ /* VET6 DWIN ENCODER LCD */
+ #define BTN_ENC PA6
+ #define BTN_EN1 PA7
+ #define BTN_EN2 PA4
+
+ #define BEEPER_PIN PA5
+
+#endif
diff --git a/Marlin/src/sd/cardreader.cpp b/Marlin/src/sd/cardreader.cpp
index 9c662fd755..aa0606705b 100644
--- a/Marlin/src/sd/cardreader.cpp
+++ b/Marlin/src/sd/cardreader.cpp
@@ -28,6 +28,11 @@
#include "../MarlinCore.h"
#include "../lcd/ultralcd.h"
+
+#if ENABLED(DWIN_CREALITY_LCD)
+ #include "../lcd/dwin/dwin.h"
+#endif
+
#include "../module/planner.h" // for synchronize
#include "../module/printcounter.h"
#include "../gcode/queue.h"
@@ -385,7 +390,11 @@ void CardReader::mount() {
void CardReader::manage_media() {
static uint8_t prev_stat = TERN(INIT_SDCARD_ON_BOOT, 2, 0);
uint8_t stat = uint8_t(IS_SD_INSERTED());
- if (stat != prev_stat && ui.detected()) {
+ if (stat == prev_stat) return;
+
+ flag.workDirIsRoot = true; // Return to root on mount/release
+
+ if (ui.detected()) {
uint8_t old_stat = prev_stat;
prev_stat = stat; // Change now to prevent re-entry
@@ -420,6 +429,8 @@ void CardReader::manage_media() {
void CardReader::release() {
endFilePrint();
flag.mounted = false;
+ flag.workDirIsRoot = true;
+ nrFiles = 0;
}
void CardReader::openAndPrintFile(const char *name) {
@@ -440,6 +451,7 @@ void CardReader::startFileprint() {
void CardReader::endFilePrint(TERN_(SD_RESORT, const bool re_sort/*=false*/)) {
TERN_(ADVANCED_PAUSE_FEATURE, did_pause_print = 0);
+ TERN_(DWIN_CREALITY_LCD, HMI_flag.print_finish = flag.sdprinting);
flag.sdprinting = flag.abort_sd_printing = false;
if (isFileOpen()) file.close();
TERN_(SD_RESORT, if (re_sort) presort());
@@ -944,7 +956,7 @@ void CardReader::cdroot() {
#if HAS_FOLDER_SORTING
const uint16_t bit = i & 0x07, ind = i >> 3;
if (bit == 0) isDir[ind] = 0x00;
- if (flag.filenameIsDir) isDir[ind] |= _BV(bit);
+ if (flag.filenameIsDir) SBI(isDir[ind], bit);
#endif
#endif
}
@@ -972,7 +984,7 @@ void CardReader::cdroot() {
#if HAS_FOLDER_SORTING
#if ENABLED(SDSORT_USES_RAM)
// Folder sorting needs an index and bit to test for folder-ness.
- #define _SORT_CMP_DIR(fs) IS_DIR(o1) == IS_DIR(o2) ? _SORT_CMP_NODIR() : IS_DIR(fs > 0 ? o1 : o2)
+ #define _SORT_CMP_DIR(fs) (IS_DIR(o1) == IS_DIR(o2) ? _SORT_CMP_NODIR() : IS_DIR(fs > 0 ? o1 : o2))
#else
#define _SORT_CMP_DIR(fs) ((dir1 == flag.filenameIsDir) ? _SORT_CMP_NODIR() : (fs > 0 ? dir1 : !dir1))
#endif
@@ -1062,13 +1074,14 @@ void CardReader::cdroot() {
#endif // SDCARD_SORT_ALPHA
uint16_t CardReader::get_num_Files() {
- return
- #if ENABLED(SDCARD_SORT_ALPHA) && SDSORT_USES_RAM && SDSORT_CACHE_NAMES
+ if (!isMounted()) return 0;
+ return (
+ #if ALL(SDCARD_SORT_ALPHA, SDSORT_USES_RAM, SDSORT_CACHE_NAMES)
nrFiles // no need to access the SD card for filenames
#else
countFilesInWorkDir()
#endif
- ;
+ );
}
//
@@ -1084,9 +1097,7 @@ void CardReader::fileHasFinished() {
startFileprint();
}
else {
- endFilePrint();
-
- TERN_(SDCARD_SORT_ALPHA, presort());
+ endFilePrint(TERN_(SD_RESORT, true));
marlin_state = MF_SD_COMPLETE;
}
diff --git a/Marlin/src/sd/cardreader.h b/Marlin/src/sd/cardreader.h
index 4f488e3148..36b81910a8 100644
--- a/Marlin/src/sd/cardreader.h
+++ b/Marlin/src/sd/cardreader.h
@@ -152,6 +152,7 @@ public:
static inline bool isFileOpen() { return isMounted() && file.isOpen(); }
static inline uint32_t getIndex() { return sdpos; }
+ static inline uint32_t getFileSize() { return filesize; }
static inline bool eof() { return sdpos >= filesize; }
static inline void setIndex(const uint32_t index) { sdpos = index; file.seekSet(index); }
static inline char* getWorkDirName() { workDir.getDosName(filename); return filename; }
diff --git a/buildroot/share/PlatformIO/ldscripts/creality.ld b/buildroot/share/PlatformIO/ldscripts/creality.ld
new file mode 100644
index 0000000000..785345543c
--- /dev/null
+++ b/buildroot/share/PlatformIO/ldscripts/creality.ld
@@ -0,0 +1,14 @@
+MEMORY
+{
+ ram (rwx) : ORIGIN = 0x20000000, LENGTH = 64K - 40
+ rom (rx) : ORIGIN = 0x08007000, LENGTH = 512K - 28K
+}
+
+/* Provide memory region aliases for common.inc */
+REGION_ALIAS("REGION_TEXT", rom);
+REGION_ALIAS("REGION_DATA", ram);
+REGION_ALIAS("REGION_BSS", ram);
+REGION_ALIAS("REGION_RODATA", rom);
+
+/* Let common.inc handle the real work. */
+INCLUDE common.inc
diff --git a/buildroot/share/PlatformIO/scripts/creality.py b/buildroot/share/PlatformIO/scripts/creality.py
new file mode 100644
index 0000000000..d7b7823356
--- /dev/null
+++ b/buildroot/share/PlatformIO/scripts/creality.py
@@ -0,0 +1,16 @@
+import os
+Import("env")
+
+# Relocate firmware from 0x08000000 to 0x08007000
+for define in env['CPPDEFINES']:
+ if define[0] == "VECT_TAB_ADDR":
+ env['CPPDEFINES'].remove(define)
+env['CPPDEFINES'].append(("VECT_TAB_ADDR", "0x08007000"))
+
+custom_ld_script = os.path.abspath("buildroot/share/PlatformIO/ldscripts/creality.ld")
+for i, flag in enumerate(env["LINKFLAGS"]):
+ if "-Wl,-T" in flag:
+ env["LINKFLAGS"][i] = "-Wl,-T" + custom_ld_script
+ elif flag == "-T":
+ env["LINKFLAGS"][i + 1] = custom_ld_script
+
diff --git a/buildroot/share/tests/STM32F103RET6_creality-tests b/buildroot/share/tests/STM32F103RET6_creality-tests
new file mode 100644
index 0000000000..f9ae634ebc
--- /dev/null
+++ b/buildroot/share/tests/STM32F103RET6_creality-tests
@@ -0,0 +1,15 @@
+#!/usr/bin/env bash
+#
+# Build tests for STM32F103RET6_creality
+#
+
+# exit on first failure
+set -e
+
+#
+# Build with configs included in the PR
+#
+use_example_configs "Creality/Ender-3 V2"
+exec_test $1 $2 "Ender 3 v2"
+
+restore_configs
diff --git a/platformio.ini b/platformio.ini
index 34a0a41159..7aa118427d 100644
--- a/platformio.ini
+++ b/platformio.ini
@@ -689,6 +689,22 @@ platform = ${common_stm32f1.platform}
extends = env:chitu_f103
build_flags = ${env:chitu_f103.build_flags} -DCHITU_V5_Z_MIN_BUGFIX
+#
+# Creality (STM32F103RET6)
+#
+[env:STM32F103RET6_creality]
+platform = ${common_stm32f1.platform}
+extends = common_stm32f1
+board = genericSTM32F103RC
+build_flags = !python Marlin/src/HAL/STM32F1/build_flags.py
+ ${common.build_flags} -std=gnu++14 -DSTM32_XL_DENSITY -DTEMP_TIMER_CHAN=4
+extra_scripts = buildroot/share/PlatformIO/scripts/creality.py
+lib_ignore = ${common_stm32f1.lib_ignore}
+ LiquidCrystal, LiquidTWI2, U8glib-HAL, Adafruit_MAX31865, Arduino-L6470, SailfishLCD, SlowSoftI2CMaster
+debug_tool = jlink
+upload_protocol = jlink
+monitor_speed = 115200
+
#
# STM32F401VE
# 'STEVAL-3DP001V1' STM32F401VE board - https://www.st.com/en/evaluation-tools/steval-3dp001v1.html