Scott Lahteine 4 лет назад
Родитель
Сommit
aac0326f7f

+ 2 - 0
.github/workflows/test-builds.yml

@@ -41,6 +41,7 @@ jobs:
         - mega2560
         - teensy31
         - teensy35
+        - teensy41
         - SAMD51_grandcentral_m4
 
         # Extended AVR Environments
@@ -70,6 +71,7 @@ jobs:
         - mks_robin_stm32
         - ARMED
         - FYSETC_S6
+        - STM32F070CB_malyan
         - STM32F070RB_malyan
         - malyan_M300
         - mks_robin_lite

+ 29 - 34
Marlin/Configuration.h

@@ -34,9 +34,8 @@
  * - Extra features
  *
  * Advanced settings can be found in Configuration_adv.h
- *
  */
-#define CONFIGURATION_H_VERSION 020006
+#define CONFIGURATION_H_VERSION 020007
 
 //===========================================================================
 //============================= Getting Started =============================
@@ -390,6 +389,7 @@
  *    21 : Pt100 with circuit in the Ultimainboard V2.x with 3.3v excitation (STM32 \ LPC176x....)
  *    22 : 100k (hotend) with 4.7k pullup to 3.3V and 220R to analog input (as in GTM32 Pro vB)
  *    23 : 100k (bed) with 4.7k pullup to 3.3v and 220R to analog input (as in GTM32 Pro vB)
+ *    30 : Kis3d Silicone heating mat 200W/300W with 6mm precision cast plate (EN AW 5083) NTC100K / B3950 (4.7k pullup)
  *   201 : Pt100 with circuit in Overlord, similar to Ultimainboard V2.x
  *    60 : 100k Maker's Tool Works Kapton Bed Thermistor beta=3950
  *    61 : 100k Formbot / Vivedino 3950 350C thermistor 4.7k pullup
@@ -486,24 +486,17 @@
   //#define PID_AUTOTUNE_MENU     // Add PID auto-tuning to the "Advanced Settings" menu. (~250 bytes of PROGMEM)
   //#define PID_PARAMS_PER_HOTEND // Uses separate PID parameters for each extruder (useful for mismatched extruders)
                                   // Set/get with gcode: M301 E[extruder number, 0-2]
-
-  // If you are using a pre-configured hotend then you can use one of the value sets by uncommenting it
-
-  // Ultimaker
-  #define DEFAULT_Kp 22.2
-  #define DEFAULT_Ki 1.08
-  #define DEFAULT_Kd 114
-
-  // MakerGear
-  //#define DEFAULT_Kp 7.0
-  //#define DEFAULT_Ki 0.1
-  //#define DEFAULT_Kd 12
-
-  // Mendel Parts V9 on 12V
-  //#define DEFAULT_Kp 63.0
-  //#define DEFAULT_Ki 2.25
-  //#define DEFAULT_Kd 440
-
+  #if ENABLED(PID_PARAMS_PER_HOTEND)
+    // Specify between 1 and HOTENDS values per array.
+    // If fewer than EXTRUDER values are provided, the last element will be repeated.
+    #define DEFAULT_Kp_LIST {  22.20,  20.0 }
+    #define DEFAULT_Ki_LIST {   1.08,   1.0 }
+    #define DEFAULT_Kd_LIST { 114.00, 112.0 }
+  #else
+    #define DEFAULT_Kp  22.20
+    #define DEFAULT_Ki   1.08
+    #define DEFAULT_Kd 114.00
+  #endif
 #endif // PIDTEMP
 
 //===========================================================================
@@ -539,18 +532,12 @@
   //#define MIN_BED_POWER 0
   //#define PID_BED_DEBUG // Sends debug data to the serial port.
 
-  //120V 250W silicone heater into 4mm borosilicate (MendelMax 1.5+)
-  //from FOPDT model - kp=.39 Tp=405 Tdead=66, Tc set to 79.2, aggressive factor of .15 (vs .1, 1, 10)
+  // 120V 250W silicone heater into 4mm borosilicate (MendelMax 1.5+)
+  // from FOPDT model - kp=.39 Tp=405 Tdead=66, Tc set to 79.2, aggressive factor of .15 (vs .1, 1, 10)
   #define DEFAULT_bedKp 10.00
   #define DEFAULT_bedKi .023
   #define DEFAULT_bedKd 305.4
 
-  //120V 250W silicone heater into 4mm borosilicate (MendelMax 1.5+)
-  //from pidautotune
-  //#define DEFAULT_bedKp 97.1
-  //#define DEFAULT_bedKi 1.41
-  //#define DEFAULT_bedKd 1675.16
-
   // FIND YOUR OWN: "M303 E-1 C8 S90" to run autotune on the bed at 90 degreesC for 8 cycles.
 #endif // PIDTEMPBED
 
@@ -608,7 +595,7 @@
 
 // @section machine
 
-// Uncomment one of these options to enable CoreXY, CoreXZ, or CoreYZ kinematics
+// Enable one of the options below for CoreXY, CoreXZ, or CoreYZ kinematics,
 // either in the usual order or reversed
 //#define COREXY
 //#define COREXZ
@@ -616,6 +603,7 @@
 //#define COREYX
 //#define COREZX
 //#define COREZY
+//#define MARKFORGED_XY  // MarkForged. See https://reprap.org/forum/read.php?152,504042
 
 //===========================================================================
 //============================== Endstop Settings ===========================
@@ -866,7 +854,6 @@
  *    - For simple switches connect...
  *      - normally-closed switches to GND and D32.
  *      - normally-open switches to 5V and D32.
- *
  */
 //#define Z_MIN_PROBE_PIN 32 // Pin 32 is the RAMPS default
 
@@ -1578,7 +1565,6 @@
  *
  *   Caveats: The ending Z should be the same as starting Z.
  * Attention: EXPERIMENTAL. G-code arguments may change.
- *
  */
 //#define NOZZLE_CLEAN_FEATURE
 
@@ -1731,7 +1717,6 @@
  *
  * SD Card support is disabled by default. If your controller has an SD slot,
  * you must uncomment the following option or it won't work.
- *
  */
 //#define SDSUPPORT
 
@@ -1968,6 +1953,14 @@
 //
 //#define FF_INTERFACEBOARD
 
+//
+// TFT GLCD Panel with Marlin UI
+// Panel connected to main board by SPI or I2C interface.
+// See https://github.com/Serhiy-K/TFTGLCDAdapter
+//
+//#define TFTGLCD_PANEL_SPI
+//#define TFTGLCD_PANEL_I2C
+
 //=============================================================================
 //=======================   LCD / Controller Selection  =======================
 //=========================      (Graphical LCDs)      ========================
@@ -2171,6 +2164,9 @@
 // Touch-screen LCD for Malyan M200/M300 printers
 //
 //#define MALYAN_LCD
+#if ENABLED(MALYAN_LCD)
+  #define LCD_SERIAL_PORT 1  // Default is 1 for Malyan M200
+#endif
 
 //
 // Touch UI for FTDI EVE (FT800/FT810) displays
@@ -2184,7 +2180,7 @@
 //#define ANYCUBIC_LCD_I3MEGA
 //#define ANYCUBIC_LCD_CHIRON
 #if EITHER(ANYCUBIC_LCD_I3MEGA, ANYCUBIC_LCD_CHIRON)
-  #define ANYCUBIC_LCD_SERIAL_PORT 3
+  #define LCD_SERIAL_PORT 3  // Default is 3 for Anycubic
   //#define ANYCUBIC_LCD_DEBUG
 #endif
 
@@ -2343,7 +2339,6 @@
  * *** CAUTION ***
  *
  * LED Type. Enable only one of the following two options.
- *
  */
 //#define RGB_LED
 //#define RGBW_LED

+ 39 - 33
Marlin/Configuration_adv.h

@@ -29,9 +29,8 @@
  * Some of these settings can damage your printer if improperly set!
  *
  * Basic settings can be found in Configuration.h
- *
  */
-#define CONFIGURATION_ADV_H_VERSION 020006
+#define CONFIGURATION_ADV_H_VERSION 020007
 
 // @section temperature
 
@@ -738,7 +737,6 @@
    *               | 4   3 | 1   4 | 2   1 | 3   2 |
    *               |       |       |       |       |
    *               | 1   2 | 2   3 | 3   4 | 4   1 |
-   *
    */
   #ifndef Z_STEPPER_ALIGN_XY
     //#define Z_STEPPERS_ORIENTATION 0
@@ -773,7 +771,6 @@
 //
 //#define ASSISTED_TRAMMING
 #if ENABLED(ASSISTED_TRAMMING)
-
   // Define positions for probing points, use the hotend as reference not the sensor.
   #define TRAMMING_POINT_XY { {  20, 20 }, { 200,  20 }, { 200, 200 }, { 20, 200 } }
 
@@ -786,6 +783,9 @@
   // Enable to restore leveling setup after operation
   #define RESTORE_LEVELING_AFTER_G35
 
+  // Add a menu item for Assisted Tramming
+  //#define ASSISTED_TRAMMING_MENU_ITEM
+
   /**
    * Screw thread:
    *   M3: 30 = Clockwise, 31 = Counter-Clockwise
@@ -793,7 +793,6 @@
    *   M5: 50 = Clockwise, 51 = Counter-Clockwise
    */
   #define TRAMMING_SCREW_THREAD 30
-
 #endif
 
 // @section motion
@@ -1105,23 +1104,26 @@
   #define BOOTSCREEN_TIMEOUT 4000        // (ms) Total Duration to display the boot screen(s)
 #endif
 
-#if HAS_GRAPHICAL_LCD && EITHER(SDSUPPORT, LCD_SET_PROGRESS_MANUALLY)
-  //#define PRINT_PROGRESS_SHOW_DECIMALS // Show progress with decimal digits
-  //#define SHOW_REMAINING_TIME          // Display estimated time to completion
+#if EITHER(SDSUPPORT, LCD_SET_PROGRESS_MANUALLY) && ANY(HAS_MARLINUI_U8GLIB, HAS_MARLINUI_HD44780, IS_TFTGLCD_PANEL)
+  //#define SHOW_REMAINING_TIME       // Display estimated time to completion
   #if ENABLED(SHOW_REMAINING_TIME)
-    //#define USE_M73_REMAINING_TIME     // Use remaining time from M73 command instead of estimation
-    //#define ROTATE_PROGRESS_DISPLAY    // Display (P)rogress, (E)lapsed, and (R)emaining time
+    //#define USE_M73_REMAINING_TIME  // Use remaining time from M73 command instead of estimation
+    //#define ROTATE_PROGRESS_DISPLAY // Display (P)rogress, (E)lapsed, and (R)emaining time
   #endif
-#endif
 
-#if HAS_CHARACTER_LCD && EITHER(SDSUPPORT, LCD_SET_PROGRESS_MANUALLY)
-  //#define LCD_PROGRESS_BAR              // Show a progress bar on HD44780 LCDs for SD printing
-  #if ENABLED(LCD_PROGRESS_BAR)
-    #define PROGRESS_BAR_BAR_TIME 2000    // (ms) Amount of time to show the bar
-    #define PROGRESS_BAR_MSG_TIME 3000    // (ms) Amount of time to show the status message
-    #define PROGRESS_MSG_EXPIRE   0       // (ms) Amount of time to retain the status message (0=forever)
-    //#define PROGRESS_MSG_ONCE           // Show the message for MSG_TIME then clear it
-    //#define LCD_PROGRESS_BAR_TEST       // Add a menu item to test the progress bar
+  #if HAS_MARLINUI_U8GLIB
+    //#define PRINT_PROGRESS_SHOW_DECIMALS // Show progress with decimal digits
+  #endif
+
+  #if EITHER(HAS_MARLINUI_HD44780, IS_TFTGLCD_PANEL)
+    //#define LCD_PROGRESS_BAR            // Show a progress bar on HD44780 LCDs for SD printing
+    #if ENABLED(LCD_PROGRESS_BAR)
+      #define PROGRESS_BAR_BAR_TIME 2000  // (ms) Amount of time to show the bar
+      #define PROGRESS_BAR_MSG_TIME 3000  // (ms) Amount of time to show the status message
+      #define PROGRESS_MSG_EXPIRE   0     // (ms) Amount of time to retain the status message (0=forever)
+      //#define PROGRESS_MSG_ONCE         // Show the message for MSG_TIME then clear it
+      //#define LCD_PROGRESS_BAR_TEST     // Add a menu item to test the progress bar
+    #endif
   #endif
 #endif
 
@@ -1164,6 +1166,7 @@
   #if ENABLED(POWER_LOSS_RECOVERY)
     #define PLR_ENABLED_DEFAULT   false // Power Loss Recovery enabled by default. (Set with 'M413 Sn' & M500)
     //#define BACKUP_POWER_SUPPLY       // Backup power / UPS to move the steppers on power loss
+    //#define POWER_LOSS_RECOVER_ZHOME  // Z homing is needed for proper recovery. 99.9% of the time this should be disabled!
     //#define POWER_LOSS_ZRAISE       2 // (mm) Z axis raise on resume (on power loss with UPS)
     //#define POWER_LOSS_PIN         44 // Pin to detect power loss. Set to -1 to disable default pin on boards without module.
     //#define POWER_LOSS_STATE     HIGH // State of pin indicating power loss
@@ -1324,7 +1327,7 @@
  * controller events, as there is a trade-off between reliable
  * printing performance versus fast display updates.
  */
-#if HAS_GRAPHICAL_LCD
+#if HAS_MARLINUI_U8GLIB
   // Show SD percentage next to the progress bar
   //#define DOGM_SD_PERCENT
 
@@ -1394,18 +1397,18 @@
   //#define MARLIN_SNAKE
   //#define GAMES_EASTER_EGG          // Add extra blank lines above the "Games" sub-menu
 
-#endif // HAS_GRAPHICAL_LCD
+#endif // HAS_MARLINUI_U8GLIB
 
 //
 // Additional options for DGUS / DWIN displays
 //
 #if HAS_DGUS_LCD
-  #define DGUS_SERIAL_PORT 3
-  #define DGUS_BAUDRATE 115200
+  #define LCD_SERIAL_PORT 3
+  #define LCD_BAUDRATE 115200
 
   #define DGUS_RX_BUFFER_SIZE 128
   #define DGUS_TX_BUFFER_SIZE 48
-  //#define DGUS_SERIAL_STATS_RX_BUFFER_OVERRUNS  // Fix Rx overrun situation (Currently only for AVR)
+  //#define SERIAL_STATS_RX_BUFFER_OVERRUNS  // Fix Rx overrun situation (Currently only for AVR)
 
   #define DGUS_UPDATE_INTERVAL_MS  500    // (ms) Interval between automatic screen updates
 
@@ -1572,6 +1575,7 @@
 #if ENABLED(BABYSTEPPING)
   //#define INTEGRATED_BABYSTEPPING         // EXPERIMENTAL integration of babystepping into the Stepper ISR
   //#define BABYSTEP_WITHOUT_HOMING
+  //#define BABYSTEP_ALWAYS_AVAILABLE       // Allow babystepping at all times (not just during movement).
   //#define BABYSTEP_XY                     // Also enable X/Y Babystepping. Not supported on DELTA!
   #define BABYSTEP_INVERT_Z false           // Change if Z babysteps should go the other way
   //#define BABYSTEP_MILLIMETER_UNITS       // Specify BABYSTEP_MULTIPLICATOR_(XY|Z) in mm instead of micro-steps
@@ -1582,7 +1586,6 @@
   #if ENABLED(DOUBLECLICK_FOR_Z_BABYSTEPPING)
     #define DOUBLECLICK_MAX_INTERVAL 1250   // Maximum interval between clicks, in milliseconds.
                                             // Note: Extra time may be added to mitigate controller latency.
-    //#define BABYSTEP_ALWAYS_AVAILABLE     // Allow babystepping at all times (not just during movement).
     //#define MOVE_Z_WHEN_IDLE              // Jump to the move Z menu on doubleclick when printer is idle.
     #if ENABLED(MOVE_Z_WHEN_IDLE)
       #define MOVE_Z_IDLE_MULTIPLICATOR 1   // Multiply 1mm by this factor for the move step size.
@@ -1950,7 +1953,6 @@
  * Be sure to turn off auto-retract during filament change.
  *
  * Note that M207 / M208 / M209 settings are saved to EEPROM.
- *
  */
 //#define FWRETRACT
 #if ENABLED(FWRETRACT)
@@ -1976,7 +1978,7 @@
  * Universal tool change settings.
  * Applies to all types of extruders except where explicitly noted.
  */
-#if EXTRUDERS > 1
+#if HAS_MULTI_EXTRUDER
   // Z raise distance for tool-change, as needed for some extruders
   #define TOOLCHANGE_ZRAISE                 2 // (mm)
   //#define TOOLCHANGE_ZRAISE_BEFORE_RETRACT  // Apply raise before swap retraction (if enabled)
@@ -2040,7 +2042,7 @@
     //#define TOOLCHANGE_PARK_X_ONLY          // X axis only move
     //#define TOOLCHANGE_PARK_Y_ONLY          // Y axis only move
   #endif
-#endif // EXTRUDERS > 1
+#endif // HAS_MULTI_EXTRUDER
 
 /**
  * Advanced Pause
@@ -2487,7 +2489,7 @@
   #define E7_HYBRID_THRESHOLD     30
 
   /**
-   * Use StallGuard2 to home / probe X, Y, Z.
+   * Use StallGuard to home / probe X, Y, Z.
    *
    * TMC2130, TMC2160, TMC2209, TMC2660, TMC5130, and TMC5160 only
    * Connect the stepper driver's DIAG1 pin to the X/Y endstop pin.
@@ -2508,6 +2510,8 @@
    *
    * IMPROVE_HOMING_RELIABILITY tunes acceleration and jerk when
    * homing and adds a guard period for endstop triggering.
+   *
+   * Comment *_STALL_SENSITIVITY to disable sensorless homing for that axis.
    */
   //#define SENSORLESS_HOMING // StallGuard capable drivers only
 
@@ -3227,6 +3231,7 @@
 //#define HOST_ACTION_COMMANDS
 #if ENABLED(HOST_ACTION_COMMANDS)
   //#define HOST_PROMPT_SUPPORT
+  //#define HOST_START_MENU_ITEM  // Add a menu item that tells the host to start
 #endif
 
 /**
@@ -3334,6 +3339,7 @@
   #define JOY_X_LIMITS { 5600, 8190-100, 8190+100, 10800 } // min, deadzone start, deadzone end, max
   #define JOY_Y_LIMITS { 5600, 8250-100, 8250+100, 11000 }
   #define JOY_Z_LIMITS { 4800, 8080-100, 8080+100, 11550 }
+  //#define JOYSTICK_DEBUG
 #endif
 
 /**
@@ -3411,10 +3417,10 @@
 #if ENABLED(PRUSA_MMU2)
 
   // Serial port used for communication with MMU2.
-  // For AVR enable the UART port used for the MMU. (e.g., internalSerial)
+  // For AVR enable the UART port used for the MMU. (e.g., mmuSerial)
   // For 32-bit boards check your HAL for available serial ports. (e.g., Serial2)
-  #define INTERNAL_SERIAL_PORT 2
-  #define MMU2_SERIAL internalSerial
+  #define MMU2_SERIAL_PORT 2
+  #define MMU2_SERIAL mmuSerial
 
   // Use hardware reset for MMU if a pin is defined for it
   //#define MMU2_RST_PIN 23
@@ -3465,7 +3471,7 @@
    */
   //#define MMU_EXTRUDER_SENSOR
   #if ENABLED(MMU_EXTRUDER_SENSOR)
-    #define MMU_LOADING_ATTEMPTS_NR 5 //max. number of attempts to load filament if first load fail
+    #define MMU_LOADING_ATTEMPTS_NR 5 // max. number of attempts to load filament if first load fail
   #endif
 
   /**

+ 56 - 52
Marlin/Makefile

@@ -170,110 +170,114 @@ else ifeq ($(HARDWARE_MOTHERBOARD),1100)
 else ifeq ($(HARDWARE_MOTHERBOARD),1101)
 # Velleman K8400 Controller (derived from 3Drag Controller)
 else ifeq ($(HARDWARE_MOTHERBOARD),1102)
-# Velleman K8600 Controller (derived from 3Drag Controller)
+# Velleman K8600 Controller (Vertex Nano)
 else ifeq ($(HARDWARE_MOTHERBOARD),1103)
-# 2PrintBeta BAM&DICE with STK drivers
+# Velleman K8800 Controller (Vertex Delta)
 else ifeq ($(HARDWARE_MOTHERBOARD),1104)
-# 2PrintBeta BAM&DICE Due with STK drivers
+# 2PrintBeta BAM&DICE with STK drivers
 else ifeq ($(HARDWARE_MOTHERBOARD),1105)
-# MKS BASE v1.0
+# 2PrintBeta BAM&DICE Due with STK drivers
 else ifeq ($(HARDWARE_MOTHERBOARD),1106)
-# MKS v1.4 with A4982 stepper drivers
+# MKS BASE v1.0
 else ifeq ($(HARDWARE_MOTHERBOARD),1107)
-# MKS v1.5 with Allegro A4982 stepper drivers
+# MKS v1.4 with A4982 stepper drivers
 else ifeq ($(HARDWARE_MOTHERBOARD),1108)
-# MKS v1.6 with Allegro A4982 stepper drivers
+# MKS v1.5 with Allegro A4982 stepper drivers
 else ifeq ($(HARDWARE_MOTHERBOARD),1109)
-# MKS BASE 1.0 with Heroic HR4982 stepper drivers
+# MKS v1.6 with Allegro A4982 stepper drivers
 else ifeq ($(HARDWARE_MOTHERBOARD),1110)
-# MKS GEN v1.3 or 1.4
+# MKS BASE 1.0 with Heroic HR4982 stepper drivers
 else ifeq ($(HARDWARE_MOTHERBOARD),1111)
-# MKS GEN L
+# MKS GEN v1.3 or 1.4
 else ifeq ($(HARDWARE_MOTHERBOARD),1112)
-# zrib V2.0 control board (Chinese knock off RAMPS replica)
+# MKS GEN L
 else ifeq ($(HARDWARE_MOTHERBOARD),1113)
-# BigTreeTech or BIQU KFB2.0
+# zrib V2.0 control board (Chinese knock off RAMPS replica)
 else ifeq ($(HARDWARE_MOTHERBOARD),1114)
-# Felix 2.0+ Electronics Board (RAMPS like)
+# BigTreeTech or BIQU KFB2.0
 else ifeq ($(HARDWARE_MOTHERBOARD),1115)
-# Invent-A-Part RigidBoard
+# Felix 2.0+ Electronics Board (RAMPS like)
 else ifeq ($(HARDWARE_MOTHERBOARD),1116)
-# Invent-A-Part RigidBoard V2
+# Invent-A-Part RigidBoard
 else ifeq ($(HARDWARE_MOTHERBOARD),1117)
-# Sainsmart 2-in-1 board
+# Invent-A-Part RigidBoard V2
 else ifeq ($(HARDWARE_MOTHERBOARD),1118)
-# Ultimaker
+# Sainsmart 2-in-1 board
 else ifeq ($(HARDWARE_MOTHERBOARD),1119)
-# Ultimaker (Older electronics. Pre 1.5.4. This is rare)
+# Ultimaker
 else ifeq ($(HARDWARE_MOTHERBOARD),1120)
+# Ultimaker (Older electronics. Pre 1.5.4. This is rare)
+else ifeq ($(HARDWARE_MOTHERBOARD),1121)
   MCU ?= atmega1280
 
 # Azteeg X3
-else ifeq ($(HARDWARE_MOTHERBOARD),1121)
-# Azteeg X3 Pro
 else ifeq ($(HARDWARE_MOTHERBOARD),1122)
-# Ultimainboard 2.x (Uses TEMP_SENSOR 20)
+# Azteeg X3 Pro
 else ifeq ($(HARDWARE_MOTHERBOARD),1123)
-# Rumba
+# Ultimainboard 2.x (Uses TEMP_SENSOR 20)
 else ifeq ($(HARDWARE_MOTHERBOARD),1124)
-# Raise3D Rumba
+# Rumba
 else ifeq ($(HARDWARE_MOTHERBOARD),1125)
-# Rapide Lite RL200 Rumba
+# Raise3D Rumba
 else ifeq ($(HARDWARE_MOTHERBOARD),1126)
-# Formbot T-Rex 2 Plus
+# Rapide Lite RL200 Rumba
 else ifeq ($(HARDWARE_MOTHERBOARD),1127)
-# Formbot T-Rex 3
+# Formbot T-Rex 2 Plus
 else ifeq ($(HARDWARE_MOTHERBOARD),1128)
-# Formbot Raptor
+# Formbot T-Rex 3
 else ifeq ($(HARDWARE_MOTHERBOARD),1129)
-# Formbot Raptor 2
+# Formbot Raptor
 else ifeq ($(HARDWARE_MOTHERBOARD),1130)
-# bq ZUM Mega 3D
+# Formbot Raptor 2
 else ifeq ($(HARDWARE_MOTHERBOARD),1131)
-# MakeBoard Mini v2.1.2 is a control board sold by MicroMake
+# bq ZUM Mega 3D
 else ifeq ($(HARDWARE_MOTHERBOARD),1132)
-# TriGorilla Anycubic version 1.3 based on RAMPS EFB
+# MakeBoard Mini v2.1.2 is a control board sold by MicroMake
 else ifeq ($(HARDWARE_MOTHERBOARD),1133)
-# TriGorilla Anycubic version 1.4 based on RAMPS EFB
+# TriGorilla Anycubic version 1.3 based on RAMPS EFB
 else ifeq ($(HARDWARE_MOTHERBOARD),1134)
-# TriGorilla Anycubic version 1.4 Rev 1.1
+# TriGorilla Anycubic version 1.4 based on RAMPS EFB
 else ifeq ($(HARDWARE_MOTHERBOARD),1135)
-# Creality: Ender-4, CR-8
+# TriGorilla Anycubic version 1.4 Rev 1.1
 else ifeq ($(HARDWARE_MOTHERBOARD),1136)
-# Creality: CR10S, CR20, CR-X
+# Creality: Ender-4, CR-8
 else ifeq ($(HARDWARE_MOTHERBOARD),1137)
-# Dagoma F5
+# Creality: CR10S, CR20, CR-X
 else ifeq ($(HARDWARE_MOTHERBOARD),1138)
-# FYSETC F6 1.3
+# Dagoma F5
 else ifeq ($(HARDWARE_MOTHERBOARD),1139)
-# FYSETC F6 1.5
+# FYSETC F6 1.3
 else ifeq ($(HARDWARE_MOTHERBOARD),1140)
-# Duplicator i3 Plus
+# FYSETC F6 1.5
 else ifeq ($(HARDWARE_MOTHERBOARD),1141)
-# VORON
+# Duplicator i3 Plus
 else ifeq ($(HARDWARE_MOTHERBOARD),1142)
-# TRONXY V3 1.0
+# VORON
 else ifeq ($(HARDWARE_MOTHERBOARD),1143)
-# Z-Bolt X Series
+# TRONXY V3 1.0
 else ifeq ($(HARDWARE_MOTHERBOARD),1144)
-# TT OSCAR
+# Z-Bolt X Series
 else ifeq ($(HARDWARE_MOTHERBOARD),1145)
-# Overlord/Overlord Pro
+# TT OSCAR
 else ifeq ($(HARDWARE_MOTHERBOARD),1146)
-# ADIMLab Gantry v1
+# Overlord/Overlord Pro
 else ifeq ($(HARDWARE_MOTHERBOARD),1147)
-# ADIMLab Gantry v2
+# ADIMLab Gantry v1
 else ifeq ($(HARDWARE_MOTHERBOARD),1148)
-# BIQU Tango V1
+# ADIMLab Gantry v2
 else ifeq ($(HARDWARE_MOTHERBOARD),1149)
-# MKS GEN L V2
+# BIQU Tango V1
 else ifeq ($(HARDWARE_MOTHERBOARD),1150)
-# Copymaster 3D
+# MKS GEN L V2
 else ifeq ($(HARDWARE_MOTHERBOARD),1151)
-# Ortur 4
+# MKS GEN L V2.1
 else ifeq ($(HARDWARE_MOTHERBOARD),1152)
-# Tenlog D3 Hero
+# Copymaster 3D
 else ifeq ($(HARDWARE_MOTHERBOARD),1153)
+# Ortur 4
+else ifeq ($(HARDWARE_MOTHERBOARD),1154)
+# Tenlog D3 Hero
+else ifeq ($(HARDWARE_MOTHERBOARD),1155)
 
 #
 # RAMBo and derivatives
@@ -694,7 +698,7 @@ ifeq ($(HARDWARE_VARIANT), Teensy)
   LIB_CXXSRC += usb_api.cpp
 
 else ifeq ($(HARDWARE_VARIANT), archim)
-  CDEFS      += -DARDUINO_SAM_ARCHIM -DARDUINO_ARCH_SAM -D__SAM3X8E__ -DUSB_VID=0x27b1 -DUSB_PID=0x0001 -DUSBCON '-DUSB_MANUFACTURER="UltiMachine"' '-DUSB_PRODUCT="Archim"'
+  CDEFS      += -DARDUINO_SAM_ARCHIM -DARDUINO_ARCH_SAM -D__SAM3X8E__ -DUSB_VID=0x27b1 -DUSB_PID=0x0001 -DUSBCON '-DUSB_MANUFACTURER="UltiMachine"' '-DUSB_PRODUCT_STRING="Archim"'
   LIB_CXXSRC += variant.cpp IPAddress.cpp Reset.cpp RingBuffer.cpp Stream.cpp UARTClass.cpp  USARTClass.cpp abi.cpp new.cpp watchdog.cpp CDC.cpp PluggableUSB.cpp USBCore.cpp
   LIB_SRC    += cortex_handlers.c iar_calls_sam3.c syscalls_sam3.c dtostrf.c itoa.c
 

+ 8 - 32
Marlin/src/HAL/AVR/HAL.h

@@ -15,6 +15,7 @@
  *
  * You should have received a copy of the GNU General Public License
  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ *
  */
 #pragma once
 
@@ -81,54 +82,29 @@ typedef int8_t pin_t;
 
 // Serial ports
 #ifdef USBCON
-  #if ENABLED(BLUETOOTH)
-    #define MYSERIAL0 bluetoothSerial
-  #else
-    #define MYSERIAL0 Serial
-  #endif
-  #define NUM_SERIAL 1
+  #define MYSERIAL0 TERN(BLUETOOTH, bluetoothSerial, Serial)
 #else
   #if !WITHIN(SERIAL_PORT, -1, 3)
     #error "SERIAL_PORT must be from -1 to 3. Please update your configuration."
   #endif
-
   #define MYSERIAL0 customizedSerial1
 
   #ifdef SERIAL_PORT_2
     #if !WITHIN(SERIAL_PORT_2, -1, 3)
       #error "SERIAL_PORT_2 must be from -1 to 3. Please update your configuration."
-    #elif SERIAL_PORT_2 == SERIAL_PORT
-      #error "SERIAL_PORT_2 must be different than SERIAL_PORT. Please update your configuration."
     #endif
     #define MYSERIAL1 customizedSerial2
-    #define NUM_SERIAL 2
-  #else
-    #define NUM_SERIAL 1
   #endif
 #endif
 
-#ifdef DGUS_SERIAL_PORT
-  #if !WITHIN(DGUS_SERIAL_PORT, -1, 3)
-    #error "DGUS_SERIAL_PORT must be from -1 to 3. Please update your configuration."
-  #elif DGUS_SERIAL_PORT == SERIAL_PORT
-    #error "DGUS_SERIAL_PORT must be different than SERIAL_PORT. Please update your configuration."
-  #elif defined(SERIAL_PORT_2) && DGUS_SERIAL_PORT == SERIAL_PORT_2
-    #error "DGUS_SERIAL_PORT must be different than SERIAL_PORT_2. Please update your configuration."
+#ifdef LCD_SERIAL_PORT
+  #if !WITHIN(LCD_SERIAL_PORT, -1, 3)
+    #error "LCD_SERIAL_PORT must be from -1 to 3. Please update your configuration."
   #endif
-  #define DGUS_SERIAL internalDgusSerial
-
-  #define DGUS_SERIAL_GET_TX_BUFFER_FREE DGUS_SERIAL.get_tx_buffer_free
-#endif
-
-#ifdef ANYCUBIC_LCD_SERIAL_PORT
-  #if !WITHIN(ANYCUBIC_LCD_SERIAL_PORT, -1, 3)
-    #error "ANYCUBIC_LCD_SERIAL_PORT must be from -1 to 3. Please update your configuration."
-  #elif ANYCUBIC_LCD_SERIAL_PORT == SERIAL_PORT
-    #error "ANYCUBIC_LCD_SERIAL_PORT must be different than SERIAL_PORT. Please update your configuration."
-  #elif defined(SERIAL_PORT_2) && ANYCUBIC_LCD_SERIAL_PORT == SERIAL_PORT_2
-    #error "ANYCUBIC_LCD_SERIAL_PORT must be different than SERIAL_PORT_2. Please update your configuration."
+  #define LCD_SERIAL lcdSerial
+  #if HAS_DGUS_LCD
+    #define SERIAL_GET_TX_BUFFER_FREE() LCD_SERIAL.get_tx_buffer_free()
   #endif
-  #define ANYCUBIC_LCD_SERIAL anycubicLcdSerial
 #endif
 
 // ------------------------

+ 606 - 622
Marlin/src/HAL/AVR/MarlinSerial.cpp

@@ -40,407 +40,370 @@
 
 #if !defined(USBCON) && (defined(UBRRH) || defined(UBRR0H) || defined(UBRR1H) || defined(UBRR2H) || defined(UBRR3H))
 
-  #include "MarlinSerial.h"
-  #include "../../MarlinCore.h"
+#include "MarlinSerial.h"
+#include "../../MarlinCore.h"
+
+#if ENABLED(DIRECT_STEPPING)
+  #include "../../feature/direct_stepping.h"
+#endif
+
+template<typename Cfg> typename MarlinSerial<Cfg>::ring_buffer_r MarlinSerial<Cfg>::rx_buffer = { 0, 0, { 0 } };
+template<typename Cfg> typename MarlinSerial<Cfg>::ring_buffer_t MarlinSerial<Cfg>::tx_buffer = { 0 };
+template<typename Cfg> bool     MarlinSerial<Cfg>::_written = false;
+template<typename Cfg> uint8_t  MarlinSerial<Cfg>::xon_xoff_state = MarlinSerial<Cfg>::XON_XOFF_CHAR_SENT | MarlinSerial<Cfg>::XON_CHAR;
+template<typename Cfg> uint8_t  MarlinSerial<Cfg>::rx_dropped_bytes = 0;
+template<typename Cfg> uint8_t  MarlinSerial<Cfg>::rx_buffer_overruns = 0;
+template<typename Cfg> uint8_t  MarlinSerial<Cfg>::rx_framing_errors = 0;
+template<typename Cfg> typename MarlinSerial<Cfg>::ring_buffer_pos_t MarlinSerial<Cfg>::rx_max_enqueued = 0;
+
+// A SW memory barrier, to ensure GCC does not overoptimize loops
+#define sw_barrier() asm volatile("": : :"memory");
+
+#include "../../feature/e_parser.h"
+
+// "Atomically" read the RX head index value without disabling interrupts:
+// This MUST be called with RX interrupts enabled, and CAN'T be called
+// from the RX ISR itself!
+template<typename Cfg>
+FORCE_INLINE typename MarlinSerial<Cfg>::ring_buffer_pos_t MarlinSerial<Cfg>::atomic_read_rx_head() {
+  if (Cfg::RX_SIZE > 256) {
+    // Keep reading until 2 consecutive reads return the same value,
+    // meaning there was no update in-between caused by an interrupt.
+    // This works because serial RX interrupts happen at a slower rate
+    // than successive reads of a variable, so 2 consecutive reads with
+    // the same value means no interrupt updated it.
+    ring_buffer_pos_t vold, vnew = rx_buffer.head;
+    sw_barrier();
+    do {
+      vold = vnew;
+      vnew = rx_buffer.head;
+      sw_barrier();
+    } while (vold != vnew);
+    return vnew;
+  }
+  else {
+    // With an 8bit index, reads are always atomic. No need for special handling
+    return rx_buffer.head;
+  }
+}
+
+template<typename Cfg>
+volatile bool MarlinSerial<Cfg>::rx_tail_value_not_stable = false;
+template<typename Cfg>
+volatile uint16_t MarlinSerial<Cfg>::rx_tail_value_backup = 0;
+
+// Set RX tail index, taking into account the RX ISR could interrupt
+//  the write to this variable in the middle - So a backup strategy
+//  is used to ensure reads of the correct values.
+//    -Must NOT be called from the RX ISR -
+template<typename Cfg>
+FORCE_INLINE void MarlinSerial<Cfg>::atomic_set_rx_tail(typename MarlinSerial<Cfg>::ring_buffer_pos_t value) {
+  if (Cfg::RX_SIZE > 256) {
+    // Store the new value in the backup
+    rx_tail_value_backup = value;
+    sw_barrier();
+    // Flag we are about to change the true value
+    rx_tail_value_not_stable = true;
+    sw_barrier();
+    // Store the new value
+    rx_buffer.tail = value;
+    sw_barrier();
+    // Signal the new value is completely stored into the value
+    rx_tail_value_not_stable = false;
+    sw_barrier();
+  }
+  else
+    rx_buffer.tail = value;
+}
+
+// Get the RX tail index, taking into account the read could be
+//  interrupting in the middle of the update of that index value
+//    -Called from the RX ISR -
+template<typename Cfg>
+FORCE_INLINE typename MarlinSerial<Cfg>::ring_buffer_pos_t MarlinSerial<Cfg>::atomic_read_rx_tail() {
+  if (Cfg::RX_SIZE > 256) {
+    // If the true index is being modified, return the backup value
+    if (rx_tail_value_not_stable) return rx_tail_value_backup;
+  }
+  // The true index is stable, return it
+  return rx_buffer.tail;
+}
+
+// (called with RX interrupts disabled)
+template<typename Cfg>
+FORCE_INLINE void MarlinSerial<Cfg>::store_rxd_char() {
+
+  static EmergencyParser::State emergency_state; // = EP_RESET
+
+  // This must read the R_UCSRA register before reading the received byte to detect error causes
+  if (Cfg::DROPPED_RX && B_DOR && !++rx_dropped_bytes) --rx_dropped_bytes;
+  if (Cfg::RX_OVERRUNS && B_DOR && !++rx_buffer_overruns) --rx_buffer_overruns;
+  if (Cfg::RX_FRAMING_ERRORS && B_FE && !++rx_framing_errors) --rx_framing_errors;
+
+  // Read the character from the USART
+  uint8_t c = R_UDR;
 
   #if ENABLED(DIRECT_STEPPING)
-    #include "../../feature/direct_stepping.h"
+    if (page_manager.maybe_store_rxd_char(c)) return;
   #endif
 
-  template<typename Cfg> typename MarlinSerial<Cfg>::ring_buffer_r MarlinSerial<Cfg>::rx_buffer = { 0, 0, { 0 } };
-  template<typename Cfg> typename MarlinSerial<Cfg>::ring_buffer_t MarlinSerial<Cfg>::tx_buffer = { 0 };
-  template<typename Cfg> bool     MarlinSerial<Cfg>::_written = false;
-  template<typename Cfg> uint8_t  MarlinSerial<Cfg>::xon_xoff_state = MarlinSerial<Cfg>::XON_XOFF_CHAR_SENT | MarlinSerial<Cfg>::XON_CHAR;
-  template<typename Cfg> uint8_t  MarlinSerial<Cfg>::rx_dropped_bytes = 0;
-  template<typename Cfg> uint8_t  MarlinSerial<Cfg>::rx_buffer_overruns = 0;
-  template<typename Cfg> uint8_t  MarlinSerial<Cfg>::rx_framing_errors = 0;
-  template<typename Cfg> typename MarlinSerial<Cfg>::ring_buffer_pos_t MarlinSerial<Cfg>::rx_max_enqueued = 0;
-
-  // A SW memory barrier, to ensure GCC does not overoptimize loops
-  #define sw_barrier() asm volatile("": : :"memory");
-
-  #include "../../feature/e_parser.h"
-
-  // "Atomically" read the RX head index value without disabling interrupts:
-  // This MUST be called with RX interrupts enabled, and CAN'T be called
-  // from the RX ISR itself!
-  template<typename Cfg>
-  FORCE_INLINE typename MarlinSerial<Cfg>::ring_buffer_pos_t MarlinSerial<Cfg>::atomic_read_rx_head() {
-    if (Cfg::RX_SIZE > 256) {
-      // Keep reading until 2 consecutive reads return the same value,
-      // meaning there was no update in-between caused by an interrupt.
-      // This works because serial RX interrupts happen at a slower rate
-      // than successive reads of a variable, so 2 consecutive reads with
-      // the same value means no interrupt updated it.
-      ring_buffer_pos_t vold, vnew = rx_buffer.head;
-      sw_barrier();
-      do {
-        vold = vnew;
-        vnew = rx_buffer.head;
-        sw_barrier();
-      } while (vold != vnew);
-      return vnew;
-    }
-    else {
-      // With an 8bit index, reads are always atomic. No need for special handling
-      return rx_buffer.head;
-    }
-  }
+  // Get the tail - Nothing can alter its value while this ISR is executing, but there's
+  // a chance that this ISR interrupted the main process while it was updating the index.
+  // The backup mechanism ensures the correct value is always returned.
+  const ring_buffer_pos_t t = atomic_read_rx_tail();
 
-  template<typename Cfg>
-  volatile bool MarlinSerial<Cfg>::rx_tail_value_not_stable = false;
-  template<typename Cfg>
-  volatile uint16_t MarlinSerial<Cfg>::rx_tail_value_backup = 0;
-
-  // Set RX tail index, taking into account the RX ISR could interrupt
-  //  the write to this variable in the middle - So a backup strategy
-  //  is used to ensure reads of the correct values.
-  //    -Must NOT be called from the RX ISR -
-  template<typename Cfg>
-  FORCE_INLINE void MarlinSerial<Cfg>::atomic_set_rx_tail(typename MarlinSerial<Cfg>::ring_buffer_pos_t value) {
-    if (Cfg::RX_SIZE > 256) {
-      // Store the new value in the backup
-      rx_tail_value_backup = value;
-      sw_barrier();
-      // Flag we are about to change the true value
-      rx_tail_value_not_stable = true;
-      sw_barrier();
-      // Store the new value
-      rx_buffer.tail = value;
-      sw_barrier();
-      // Signal the new value is completely stored into the value
-      rx_tail_value_not_stable = false;
-      sw_barrier();
-    }
-    else
-      rx_buffer.tail = value;
-  }
+  // Get the head pointer - This ISR is the only one that modifies its value, so it's safe to read here
+  ring_buffer_pos_t h = rx_buffer.head;
 
-  // Get the RX tail index, taking into account the read could be
-  //  interrupting in the middle of the update of that index value
-  //    -Called from the RX ISR -
-  template<typename Cfg>
-  FORCE_INLINE typename MarlinSerial<Cfg>::ring_buffer_pos_t MarlinSerial<Cfg>::atomic_read_rx_tail() {
-    if (Cfg::RX_SIZE > 256) {
-      // If the true index is being modified, return the backup value
-      if (rx_tail_value_not_stable) return rx_tail_value_backup;
-    }
-    // The true index is stable, return it
-    return rx_buffer.tail;
-  }
+  // Get the next element
+  ring_buffer_pos_t i = (ring_buffer_pos_t)(h + 1) & (ring_buffer_pos_t)(Cfg::RX_SIZE - 1);
 
-  // (called with RX interrupts disabled)
-  template<typename Cfg>
-  FORCE_INLINE void MarlinSerial<Cfg>::store_rxd_char() {
+  if (Cfg::EMERGENCYPARSER) emergency_parser.update(emergency_state, c);
+
+  // If the character is to be stored at the index just before the tail
+  // (such that the head would advance to the current tail), the RX FIFO is
+  // full, so don't write the character or advance the head.
+  if (i != t) {
+    rx_buffer.buffer[h] = c;
+    h = i;
+  }
+  else if (Cfg::DROPPED_RX && !++rx_dropped_bytes)
+    --rx_dropped_bytes;
 
-    static EmergencyParser::State emergency_state; // = EP_RESET
+  if (Cfg::MAX_RX_QUEUED) {
+    // Calculate count of bytes stored into the RX buffer
+    const ring_buffer_pos_t rx_count = (ring_buffer_pos_t)(h - t) & (ring_buffer_pos_t)(Cfg::RX_SIZE - 1);
 
-    // This must read the R_UCSRA register before reading the received byte to detect error causes
-    if (Cfg::DROPPED_RX && B_DOR && !++rx_dropped_bytes) --rx_dropped_bytes;
-    if (Cfg::RX_OVERRUNS && B_DOR && !++rx_buffer_overruns) --rx_buffer_overruns;
-    if (Cfg::RX_FRAMING_ERRORS && B_FE && !++rx_framing_errors) --rx_framing_errors;
+    // Keep track of the maximum count of enqueued bytes
+    NOLESS(rx_max_enqueued, rx_count);
+  }
 
-    // Read the character from the USART
-    uint8_t c = R_UDR;
+  if (Cfg::XONOFF) {
+    // If the last char that was sent was an XON
+    if ((xon_xoff_state & XON_XOFF_CHAR_MASK) == XON_CHAR) {
 
-    #if ENABLED(DIRECT_STEPPING)
-      if (page_manager.maybe_store_rxd_char(c)) return;
-    #endif
+      // Bytes stored into the RX buffer
+      const ring_buffer_pos_t rx_count = (ring_buffer_pos_t)(h - t) & (ring_buffer_pos_t)(Cfg::RX_SIZE - 1);
 
-    // Get the tail - Nothing can alter its value while this ISR is executing, but there's
-    // a chance that this ISR interrupted the main process while it was updating the index.
-    // The backup mechanism ensures the correct value is always returned.
-    const ring_buffer_pos_t t = atomic_read_rx_tail();
+      // If over 12.5% of RX buffer capacity, send XOFF before running out of
+      // RX buffer space .. 325 bytes @ 250kbits/s needed to let the host react
+      // and stop sending bytes. This translates to 13mS propagation time.
+      if (rx_count >= (Cfg::RX_SIZE) / 8) {
 
-    // Get the head pointer - This ISR is the only one that modifies its value, so it's safe to read here
-    ring_buffer_pos_t h = rx_buffer.head;
+        // At this point, definitely no TX interrupt was executing, since the TX ISR can't be preempted.
+        // Don't enable the TX interrupt here as a means to trigger the XOFF char, because if it happens
+        // to be in the middle of trying to disable the RX interrupt in the main program, eventually the
+        // enabling of the TX interrupt could be undone. The ONLY reliable thing this can do to ensure
+        // the sending of the XOFF char is to send it HERE AND NOW.
 
-    // Get the next element
-    ring_buffer_pos_t i = (ring_buffer_pos_t)(h + 1) & (ring_buffer_pos_t)(Cfg::RX_SIZE - 1);
+        // About to send the XOFF char
+        xon_xoff_state = XOFF_CHAR | XON_XOFF_CHAR_SENT;
 
-    if (Cfg::EMERGENCYPARSER) emergency_parser.update(emergency_state, c);
+        // Wait until the TX register becomes empty and send it - Here there could be a problem
+        // - While waiting for the TX register to empty, the RX register could receive a new
+        //   character. This must also handle that situation!
+        while (!B_UDRE) {
 
-    // If the character is to be stored at the index just before the tail
-    // (such that the head would advance to the current tail), the RX FIFO is
-    // full, so don't write the character or advance the head.
-    if (i != t) {
-      rx_buffer.buffer[h] = c;
-      h = i;
-    }
-    else if (Cfg::DROPPED_RX && !++rx_dropped_bytes)
-      --rx_dropped_bytes;
+          if (B_RXC) {
+            // A char arrived while waiting for the TX buffer to be empty - Receive and process it!
 
-    if (Cfg::MAX_RX_QUEUED) {
-      // Calculate count of bytes stored into the RX buffer
-      const ring_buffer_pos_t rx_count = (ring_buffer_pos_t)(h - t) & (ring_buffer_pos_t)(Cfg::RX_SIZE - 1);
+            i = (ring_buffer_pos_t)(h + 1) & (ring_buffer_pos_t)(Cfg::RX_SIZE - 1);
 
-      // Keep track of the maximum count of enqueued bytes
-      NOLESS(rx_max_enqueued, rx_count);
-    }
+            // Read the character from the USART
+            c = R_UDR;
 
-    if (Cfg::XONOFF) {
-      // If the last char that was sent was an XON
-      if ((xon_xoff_state & XON_XOFF_CHAR_MASK) == XON_CHAR) {
-
-        // Bytes stored into the RX buffer
-        const ring_buffer_pos_t rx_count = (ring_buffer_pos_t)(h - t) & (ring_buffer_pos_t)(Cfg::RX_SIZE - 1);
-
-        // If over 12.5% of RX buffer capacity, send XOFF before running out of
-        // RX buffer space .. 325 bytes @ 250kbits/s needed to let the host react
-        // and stop sending bytes. This translates to 13mS propagation time.
-        if (rx_count >= (Cfg::RX_SIZE) / 8) {
-
-          // At this point, definitely no TX interrupt was executing, since the TX ISR can't be preempted.
-          // Don't enable the TX interrupt here as a means to trigger the XOFF char, because if it happens
-          // to be in the middle of trying to disable the RX interrupt in the main program, eventually the
-          // enabling of the TX interrupt could be undone. The ONLY reliable thing this can do to ensure
-          // the sending of the XOFF char is to send it HERE AND NOW.
-
-          // About to send the XOFF char
-          xon_xoff_state = XOFF_CHAR | XON_XOFF_CHAR_SENT;
-
-          // Wait until the TX register becomes empty and send it - Here there could be a problem
-          // - While waiting for the TX register to empty, the RX register could receive a new
-          //   character. This must also handle that situation!
-          while (!B_UDRE) {
-
-            if (B_RXC) {
-              // A char arrived while waiting for the TX buffer to be empty - Receive and process it!
-
-              i = (ring_buffer_pos_t)(h + 1) & (ring_buffer_pos_t)(Cfg::RX_SIZE - 1);
-
-              // Read the character from the USART
-              c = R_UDR;
-
-              if (Cfg::EMERGENCYPARSER) emergency_parser.update(emergency_state, c);
-
-              // If the character is to be stored at the index just before the tail
-              // (such that the head would advance to the current tail), the FIFO is
-              // full, so don't write the character or advance the head.
-              if (i != t) {
-                rx_buffer.buffer[h] = c;
-                h = i;
-              }
-              else if (Cfg::DROPPED_RX && !++rx_dropped_bytes)
-                --rx_dropped_bytes;
-            }
-            sw_barrier();
-          }
+            if (Cfg::EMERGENCYPARSER) emergency_parser.update(emergency_state, c);
 
-          R_UDR = XOFF_CHAR;
-
-          // Clear the TXC bit -- "can be cleared by writing a one to its bit
-          // location". This makes sure flush() won't return until the bytes
-          // actually got written
-          B_TXC = 1;
-
-          // At this point there could be a race condition between the write() function
-          // and this sending of the XOFF char. This interrupt could happen between the
-          // wait to be empty TX buffer loop and the actual write of the character. Since
-          // the TX buffer is full because it's sending the XOFF char, the only way to be
-          // sure the write() function will succeed is to wait for the XOFF char to be
-          // completely sent. Since an extra character could be received during the wait
-          // it must also be handled!
-          while (!B_UDRE) {
-
-            if (B_RXC) {
-              // A char arrived while waiting for the TX buffer to be empty - Receive and process it!
-
-              i = (ring_buffer_pos_t)(h + 1) & (ring_buffer_pos_t)(Cfg::RX_SIZE - 1);
-
-              // Read the character from the USART
-              c = R_UDR;
-
-              if (Cfg::EMERGENCYPARSER)
-                emergency_parser.update(emergency_state, c);
-
-              // If the character is to be stored at the index just before the tail
-              // (such that the head would advance to the current tail), the FIFO is
-              // full, so don't write the character or advance the head.
-              if (i != t) {
-                rx_buffer.buffer[h] = c;
-                h = i;
-              }
-              else if (Cfg::DROPPED_RX && !++rx_dropped_bytes)
-                --rx_dropped_bytes;
+            // If the character is to be stored at the index just before the tail
+            // (such that the head would advance to the current tail), the FIFO is
+            // full, so don't write the character or advance the head.
+            if (i != t) {
+              rx_buffer.buffer[h] = c;
+              h = i;
             }
-            sw_barrier();
+            else if (Cfg::DROPPED_RX && !++rx_dropped_bytes)
+              --rx_dropped_bytes;
           }
-
-          // At this point everything is ready. The write() function won't
-          // have any issues writing to the UART TX register if it needs to!
+          sw_barrier();
         }
-      }
-    }
 
-    // Store the new head value - The main loop will retry until the value is stable
-    rx_buffer.head = h;
-  }
+        R_UDR = XOFF_CHAR;
 
-  // (called with TX irqs disabled)
-  template<typename Cfg>
-  FORCE_INLINE void MarlinSerial<Cfg>::_tx_udr_empty_irq() {
-    if (Cfg::TX_SIZE > 0) {
-      // Read positions
-      uint8_t t = tx_buffer.tail;
-      const uint8_t h = tx_buffer.head;
+        // Clear the TXC bit -- "can be cleared by writing a one to its bit
+        // location". This makes sure flush() won't return until the bytes
+        // actually got written
+        B_TXC = 1;
 
-      if (Cfg::XONOFF) {
-        // If an XON char is pending to be sent, do it now
-        if (xon_xoff_state == XON_CHAR) {
+        // At this point there could be a race condition between the write() function
+        // and this sending of the XOFF char. This interrupt could happen between the
+        // wait to be empty TX buffer loop and the actual write of the character. Since
+        // the TX buffer is full because it's sending the XOFF char, the only way to be
+        // sure the write() function will succeed is to wait for the XOFF char to be
+        // completely sent. Since an extra character could be received during the wait
+        // it must also be handled!
+        while (!B_UDRE) {
 
-          // Send the character
-          R_UDR = XON_CHAR;
+          if (B_RXC) {
+            // A char arrived while waiting for the TX buffer to be empty - Receive and process it!
 
-          // clear the TXC bit -- "can be cleared by writing a one to its bit
-          // location". This makes sure flush() won't return until the bytes
-          // actually got written
-          B_TXC = 1;
+            i = (ring_buffer_pos_t)(h + 1) & (ring_buffer_pos_t)(Cfg::RX_SIZE - 1);
 
-          // Remember we sent it.
-          xon_xoff_state = XON_CHAR | XON_XOFF_CHAR_SENT;
+            // Read the character from the USART
+            c = R_UDR;
 
-          // If nothing else to transmit, just disable TX interrupts.
-          if (h == t) B_UDRIE = 0; // (Non-atomic, could be reenabled by the main program, but eventually this will succeed)
+            if (Cfg::EMERGENCYPARSER)
+              emergency_parser.update(emergency_state, c);
 
-          return;
+            // If the character is to be stored at the index just before the tail
+            // (such that the head would advance to the current tail), the FIFO is
+            // full, so don't write the character or advance the head.
+            if (i != t) {
+              rx_buffer.buffer[h] = c;
+              h = i;
+            }
+            else if (Cfg::DROPPED_RX && !++rx_dropped_bytes)
+              --rx_dropped_bytes;
+          }
+          sw_barrier();
         }
-      }
 
-      // If nothing to transmit, just disable TX interrupts. This could
-      // happen as the result of the non atomicity of the disabling of RX
-      // interrupts that could end reenabling TX interrupts as a side effect.
-      if (h == t) {
-        B_UDRIE = 0; // (Non-atomic, could be reenabled by the main program, but eventually this will succeed)
-        return;
+        // At this point everything is ready. The write() function won't
+        // have any issues writing to the UART TX register if it needs to!
       }
-
-      // There is something to TX, Send the next byte
-      const uint8_t c = tx_buffer.buffer[t];
-      t = (t + 1) & (Cfg::TX_SIZE - 1);
-      R_UDR = c;
-      tx_buffer.tail = t;
-
-      // Clear the TXC bit (by writing a one to its bit location).
-      // Ensures flush() won't return until the bytes are actually written/
-      B_TXC = 1;
-
-      // Disable interrupts if there is nothing to transmit following this byte
-      if (h == t) B_UDRIE = 0; // (Non-atomic, could be reenabled by the main program, but eventually this will succeed)
-    }
-  }
-
-  // Public Methods
-  template<typename Cfg>
-  void MarlinSerial<Cfg>::begin(const long baud) {
-    uint16_t baud_setting;
-    bool useU2X = true;
-
-    #if F_CPU == 16000000UL && SERIAL_PORT == 0
-      // Hard-coded exception for compatibility with the bootloader shipped
-      // with the Duemilanove and previous boards, and the firmware on the
-      // 8U2 on the Uno and Mega 2560.
-      if (baud == 57600) useU2X = false;
-    #endif
-
-    R_UCSRA = 0;
-    if (useU2X) {
-      B_U2X = 1;
-      baud_setting = (F_CPU / 4 / baud - 1) / 2;
     }
-    else
-      baud_setting = (F_CPU / 8 / baud - 1) / 2;
-
-    // assign the baud_setting, a.k.a. ubbr (USART Baud Rate Register)
-    R_UBRRH = baud_setting >> 8;
-    R_UBRRL = baud_setting;
-
-    B_RXEN = 1;
-    B_TXEN = 1;
-    B_RXCIE = 1;
-    if (Cfg::TX_SIZE > 0) B_UDRIE = 0;
-    _written = false;
   }
 
-  template<typename Cfg>
-  void MarlinSerial<Cfg>::end() {
-    B_RXEN = 0;
-    B_TXEN = 0;
-    B_RXCIE = 0;
-    B_UDRIE = 0;
-  }
+  // Store the new head value - The main loop will retry until the value is stable
+  rx_buffer.head = h;
+}
 
-  template<typename Cfg>
-  int MarlinSerial<Cfg>::peek() {
-    const ring_buffer_pos_t h = atomic_read_rx_head(), t = rx_buffer.tail;
-    return h == t ? -1 : rx_buffer.buffer[t];
-  }
+// (called with TX irqs disabled)
+template<typename Cfg>
+FORCE_INLINE void MarlinSerial<Cfg>::_tx_udr_empty_irq() {
+  if (Cfg::TX_SIZE > 0) {
+    // Read positions
+    uint8_t t = tx_buffer.tail;
+    const uint8_t h = tx_buffer.head;
 
-  template<typename Cfg>
-  int MarlinSerial<Cfg>::read() {
-    const ring_buffer_pos_t h = atomic_read_rx_head();
+    if (Cfg::XONOFF) {
+      // If an XON char is pending to be sent, do it now
+      if (xon_xoff_state == XON_CHAR) {
 
-    // Read the tail. Main thread owns it, so it is safe to directly read it
-    ring_buffer_pos_t t = rx_buffer.tail;
+        // Send the character
+        R_UDR = XON_CHAR;
 
-    // If nothing to read, return now
-    if (h == t) return -1;
+        // clear the TXC bit -- "can be cleared by writing a one to its bit
+        // location". This makes sure flush() won't return until the bytes
+        // actually got written
+        B_TXC = 1;
 
-    // Get the next char
-    const int v = rx_buffer.buffer[t];
-    t = (ring_buffer_pos_t)(t + 1) & (Cfg::RX_SIZE - 1);
+        // Remember we sent it.
+        xon_xoff_state = XON_CHAR | XON_XOFF_CHAR_SENT;
 
-    // Advance tail - Making sure the RX ISR will always get an stable value, even
-    // if it interrupts the writing of the value of that variable in the middle.
-    atomic_set_rx_tail(t);
+        // If nothing else to transmit, just disable TX interrupts.
+        if (h == t) B_UDRIE = 0; // (Non-atomic, could be reenabled by the main program, but eventually this will succeed)
 
-    if (Cfg::XONOFF) {
-      // If the XOFF char was sent, or about to be sent...
-      if ((xon_xoff_state & XON_XOFF_CHAR_MASK) == XOFF_CHAR) {
-        // Get count of bytes in the RX buffer
-        const ring_buffer_pos_t rx_count = (ring_buffer_pos_t)(h - t) & (ring_buffer_pos_t)(Cfg::RX_SIZE - 1);
-        if (rx_count < (Cfg::RX_SIZE) / 10) {
-          if (Cfg::TX_SIZE > 0) {
-            // Signal we want an XON character to be sent.
-            xon_xoff_state = XON_CHAR;
-            // Enable TX ISR. Non atomic, but it will eventually enable them
-            B_UDRIE = 1;
-          }
-          else {
-            // If not using TX interrupts, we must send the XON char now
-            xon_xoff_state = XON_CHAR | XON_XOFF_CHAR_SENT;
-            while (!B_UDRE) sw_barrier();
-            R_UDR = XON_CHAR;
-          }
-        }
+        return;
       }
     }
 
-    return v;
-  }
+    // If nothing to transmit, just disable TX interrupts. This could
+    // happen as the result of the non atomicity of the disabling of RX
+    // interrupts that could end reenabling TX interrupts as a side effect.
+    if (h == t) {
+      B_UDRIE = 0; // (Non-atomic, could be reenabled by the main program, but eventually this will succeed)
+      return;
+    }
 
-  template<typename Cfg>
-  typename MarlinSerial<Cfg>::ring_buffer_pos_t MarlinSerial<Cfg>::available() {
-    const ring_buffer_pos_t h = atomic_read_rx_head(), t = rx_buffer.tail;
-    return (ring_buffer_pos_t)(Cfg::RX_SIZE + h - t) & (Cfg::RX_SIZE - 1);
+    // There is something to TX, Send the next byte
+    const uint8_t c = tx_buffer.buffer[t];
+    t = (t + 1) & (Cfg::TX_SIZE - 1);
+    R_UDR = c;
+    tx_buffer.tail = t;
+
+    // Clear the TXC bit (by writing a one to its bit location).
+    // Ensures flush() won't return until the bytes are actually written/
+    B_TXC = 1;
+
+    // Disable interrupts if there is nothing to transmit following this byte
+    if (h == t) B_UDRIE = 0; // (Non-atomic, could be reenabled by the main program, but eventually this will succeed)
   }
+}
 
-  template<typename Cfg>
-  void MarlinSerial<Cfg>::flush() {
+// Public Methods
+template<typename Cfg>
+void MarlinSerial<Cfg>::begin(const long baud) {
+  uint16_t baud_setting;
+  bool useU2X = true;
 
-    // Set the tail to the head:
-    //  - Read the RX head index in a safe way. (See atomic_read_rx_head.)
-    //  - Set the tail, making sure the RX ISR will always get a stable value, even
-    //    if it interrupts the writing of the value of that variable in the middle.
-    atomic_set_rx_tail(atomic_read_rx_head());
+  #if F_CPU == 16000000UL && SERIAL_PORT == 0
+    // Hard-coded exception for compatibility with the bootloader shipped
+    // with the Duemilanove and previous boards, and the firmware on the
+    // 8U2 on the Uno and Mega 2560.
+    if (baud == 57600) useU2X = false;
+  #endif
 
-    if (Cfg::XONOFF) {
-      // If the XOFF char was sent, or about to be sent...
-      if ((xon_xoff_state & XON_XOFF_CHAR_MASK) == XOFF_CHAR) {
+  R_UCSRA = 0;
+  if (useU2X) {
+    B_U2X = 1;
+    baud_setting = (F_CPU / 4 / baud - 1) / 2;
+  }
+  else
+    baud_setting = (F_CPU / 8 / baud - 1) / 2;
+
+  // assign the baud_setting, a.k.a. ubbr (USART Baud Rate Register)
+  R_UBRRH = baud_setting >> 8;
+  R_UBRRL = baud_setting;
+
+  B_RXEN = 1;
+  B_TXEN = 1;
+  B_RXCIE = 1;
+  if (Cfg::TX_SIZE > 0) B_UDRIE = 0;
+  _written = false;
+}
+
+template<typename Cfg>
+void MarlinSerial<Cfg>::end() {
+  B_RXEN = 0;
+  B_TXEN = 0;
+  B_RXCIE = 0;
+  B_UDRIE = 0;
+}
+
+template<typename Cfg>
+int MarlinSerial<Cfg>::peek() {
+  const ring_buffer_pos_t h = atomic_read_rx_head(), t = rx_buffer.tail;
+  return h == t ? -1 : rx_buffer.buffer[t];
+}
+
+template<typename Cfg>
+int MarlinSerial<Cfg>::read() {
+  const ring_buffer_pos_t h = atomic_read_rx_head();
+
+  // Read the tail. Main thread owns it, so it is safe to directly read it
+  ring_buffer_pos_t t = rx_buffer.tail;
+
+  // If nothing to read, return now
+  if (h == t) return -1;
+
+  // Get the next char
+  const int v = rx_buffer.buffer[t];
+  t = (ring_buffer_pos_t)(t + 1) & (Cfg::RX_SIZE - 1);
+
+  // Advance tail - Making sure the RX ISR will always get an stable value, even
+  // if it interrupts the writing of the value of that variable in the middle.
+  atomic_set_rx_tail(t);
+
+  if (Cfg::XONOFF) {
+    // If the XOFF char was sent, or about to be sent...
+    if ((xon_xoff_state & XON_XOFF_CHAR_MASK) == XOFF_CHAR) {
+      // Get count of bytes in the RX buffer
+      const ring_buffer_pos_t rx_count = (ring_buffer_pos_t)(h - t) & (ring_buffer_pos_t)(Cfg::RX_SIZE - 1);
+      if (rx_count < (Cfg::RX_SIZE) / 10) {
         if (Cfg::TX_SIZE > 0) {
           // Signal we want an XON character to be sent.
           xon_xoff_state = XON_CHAR;
-          // Enable TX ISR. Non atomic, but it will eventually enable it.
+          // Enable TX ISR. Non atomic, but it will eventually enable them
           B_UDRIE = 1;
         }
         else {
@@ -453,363 +416,384 @@
     }
   }
 
-  template<typename Cfg>
-  void MarlinSerial<Cfg>::write(const uint8_t c) {
-    if (Cfg::TX_SIZE == 0) {
-
-      _written = true;
-      while (!B_UDRE) sw_barrier();
-      R_UDR = c;
-
-    }
-    else {
-
-      _written = true;
-
-      // If the TX interrupts are disabled and the data register
-      // is empty, just write the byte to the data register and
-      // be done. This shortcut helps significantly improve the
-      // effective datarate at high (>500kbit/s) bitrates, where
-      // interrupt overhead becomes a slowdown.
-      // Yes, there is a race condition between the sending of the
-      // XOFF char at the RX ISR, but it is properly handled there
-      if (!B_UDRIE && B_UDRE) {
-        R_UDR = c;
-
-        // clear the TXC bit -- "can be cleared by writing a one to its bit
-        // location". This makes sure flush() won't return until the bytes
-        // actually got written
-        B_TXC = 1;
-        return;
-      }
-
-      const uint8_t i = (tx_buffer.head + 1) & (Cfg::TX_SIZE - 1);
-
-      // If global interrupts are disabled (as the result of being called from an ISR)...
-      if (!ISRS_ENABLED()) {
-
-        // Make room by polling if it is possible to transmit, and do so!
-        while (i == tx_buffer.tail) {
-
-          // If we can transmit another byte, do it.
-          if (B_UDRE) _tx_udr_empty_irq();
-
-          // Make sure compiler rereads tx_buffer.tail
-          sw_barrier();
-        }
+  return v;
+}
+
+template<typename Cfg>
+typename MarlinSerial<Cfg>::ring_buffer_pos_t MarlinSerial<Cfg>::available() {
+  const ring_buffer_pos_t h = atomic_read_rx_head(), t = rx_buffer.tail;
+  return (ring_buffer_pos_t)(Cfg::RX_SIZE + h - t) & (Cfg::RX_SIZE - 1);
+}
+
+template<typename Cfg>
+void MarlinSerial<Cfg>::flush() {
+
+  // Set the tail to the head:
+  //  - Read the RX head index in a safe way. (See atomic_read_rx_head.)
+  //  - Set the tail, making sure the RX ISR will always get a stable value, even
+  //    if it interrupts the writing of the value of that variable in the middle.
+  atomic_set_rx_tail(atomic_read_rx_head());
+
+  if (Cfg::XONOFF) {
+    // If the XOFF char was sent, or about to be sent...
+    if ((xon_xoff_state & XON_XOFF_CHAR_MASK) == XOFF_CHAR) {
+      if (Cfg::TX_SIZE > 0) {
+        // Signal we want an XON character to be sent.
+        xon_xoff_state = XON_CHAR;
+        // Enable TX ISR. Non atomic, but it will eventually enable it.
+        B_UDRIE = 1;
       }
       else {
-        // Interrupts are enabled, just wait until there is space
-        while (i == tx_buffer.tail) sw_barrier();
+        // If not using TX interrupts, we must send the XON char now
+        xon_xoff_state = XON_CHAR | XON_XOFF_CHAR_SENT;
+        while (!B_UDRE) sw_barrier();
+        R_UDR = XON_CHAR;
       }
-
-      // Store new char. head is always safe to move
-      tx_buffer.buffer[tx_buffer.head] = c;
-      tx_buffer.head = i;
-
-      // Enable TX ISR - Non atomic, but it will eventually enable TX ISR
-      B_UDRIE = 1;
     }
   }
+}
 
-  template<typename Cfg>
-  void MarlinSerial<Cfg>::flushTX() {
+template<typename Cfg>
+void MarlinSerial<Cfg>::write(const uint8_t c) {
+  if (Cfg::TX_SIZE == 0) {
 
-    if (Cfg::TX_SIZE == 0) {
-      // No bytes written, no need to flush. This special case is needed since there's
-      // no way to force the TXC (transmit complete) bit to 1 during initialization.
-      if (!_written) return;
+    _written = true;
+    while (!B_UDRE) sw_barrier();
+    R_UDR = c;
 
-      // Wait until everything was transmitted
-      while (!B_TXC) sw_barrier();
+  }
+  else {
 
-      // At this point nothing is queued anymore (DRIE is disabled) and
-      // the hardware finished transmission (TXC is set).
+    _written = true;
 
-    }
-    else {
+    // If the TX interrupts are disabled and the data register
+    // is empty, just write the byte to the data register and
+    // be done. This shortcut helps significantly improve the
+    // effective datarate at high (>500kbit/s) bitrates, where
+    // interrupt overhead becomes a slowdown.
+    // Yes, there is a race condition between the sending of the
+    // XOFF char at the RX ISR, but it is properly handled there
+    if (!B_UDRIE && B_UDRE) {
+      R_UDR = c;
 
-      // No bytes written, no need to flush. This special case is needed since there's
-      // no way to force the TXC (transmit complete) bit to 1 during initialization.
-      if (!_written) return;
+      // clear the TXC bit -- "can be cleared by writing a one to its bit
+      // location". This makes sure flush() won't return until the bytes
+      // actually got written
+      B_TXC = 1;
+      return;
+    }
 
-      // If global interrupts are disabled (as the result of being called from an ISR)...
-      if (!ISRS_ENABLED()) {
+    const uint8_t i = (tx_buffer.head + 1) & (Cfg::TX_SIZE - 1);
 
-        // Wait until everything was transmitted - We must do polling, as interrupts are disabled
-        while (tx_buffer.head != tx_buffer.tail || !B_TXC) {
+    // If global interrupts are disabled (as the result of being called from an ISR)...
+    if (!ISRS_ENABLED()) {
 
-          // If there is more space, send an extra character
-          if (B_UDRE) _tx_udr_empty_irq();
+      // Make room by polling if it is possible to transmit, and do so!
+      while (i == tx_buffer.tail) {
 
-          sw_barrier();
-        }
+        // If we can transmit another byte, do it.
+        if (B_UDRE) _tx_udr_empty_irq();
 
+        // Make sure compiler rereads tx_buffer.tail
+        sw_barrier();
       }
-      else {
-        // Wait until everything was transmitted
-        while (tx_buffer.head != tx_buffer.tail || !B_TXC) sw_barrier();
-      }
-
-      // At this point nothing is queued anymore (DRIE is disabled) and
-      // the hardware finished transmission (TXC is set).
     }
-  }
-
-  /**
-   * Imports from print.h
-   */
-
-  template<typename Cfg>
-  void MarlinSerial<Cfg>::print(char c, int base) {
-    print((long)c, base);
-  }
-
-  template<typename Cfg>
-  void MarlinSerial<Cfg>::print(unsigned char b, int base) {
-    print((unsigned long)b, base);
-  }
-
-  template<typename Cfg>
-  void MarlinSerial<Cfg>::print(int n, int base) {
-    print((long)n, base);
-  }
-
-  template<typename Cfg>
-  void MarlinSerial<Cfg>::print(unsigned int n, int base) {
-    print((unsigned long)n, base);
-  }
-
-  template<typename Cfg>
-  void MarlinSerial<Cfg>::print(long n, int base) {
-    if (base == 0) write(n);
-    else if (base == 10) {
-      if (n < 0) { print('-'); n = -n; }
-      printNumber(n, 10);
+    else {
+      // Interrupts are enabled, just wait until there is space
+      while (i == tx_buffer.tail) sw_barrier();
     }
-    else
-      printNumber(n, base);
-  }
 
-  template<typename Cfg>
-  void MarlinSerial<Cfg>::print(unsigned long n, int base) {
-    if (base == 0) write(n);
-    else printNumber(n, base);
-  }
+    // Store new char. head is always safe to move
+    tx_buffer.buffer[tx_buffer.head] = c;
+    tx_buffer.head = i;
 
-  template<typename Cfg>
-  void MarlinSerial<Cfg>::print(double n, int digits) {
-    printFloat(n, digits);
+    // Enable TX ISR - Non atomic, but it will eventually enable TX ISR
+    B_UDRIE = 1;
   }
+}
 
-  template<typename Cfg>
-  void MarlinSerial<Cfg>::println() {
-    print('\r');
-    print('\n');
-  }
-
-  template<typename Cfg>
-  void MarlinSerial<Cfg>::println(const String& s) {
-    print(s);
-    println();
-  }
-
-  template<typename Cfg>
-  void MarlinSerial<Cfg>::println(const char c[]) {
-    print(c);
-    println();
-  }
+template<typename Cfg>
+void MarlinSerial<Cfg>::flushTX() {
 
-  template<typename Cfg>
-  void MarlinSerial<Cfg>::println(char c, int base) {
-    print(c, base);
-    println();
-  }
+  if (Cfg::TX_SIZE == 0) {
+    // No bytes written, no need to flush. This special case is needed since there's
+    // no way to force the TXC (transmit complete) bit to 1 during initialization.
+    if (!_written) return;
 
-  template<typename Cfg>
-  void MarlinSerial<Cfg>::println(unsigned char b, int base) {
-    print(b, base);
-    println();
-  }
+    // Wait until everything was transmitted
+    while (!B_TXC) sw_barrier();
 
-  template<typename Cfg>
-  void MarlinSerial<Cfg>::println(int n, int base) {
-    print(n, base);
-    println();
-  }
+    // At this point nothing is queued anymore (DRIE is disabled) and
+    // the hardware finished transmission (TXC is set).
 
-  template<typename Cfg>
-  void MarlinSerial<Cfg>::println(unsigned int n, int base) {
-    print(n, base);
-    println();
   }
+  else {
 
-  template<typename Cfg>
-  void MarlinSerial<Cfg>::println(long n, int base) {
-    print(n, base);
-    println();
-  }
+    // No bytes written, no need to flush. This special case is needed since there's
+    // no way to force the TXC (transmit complete) bit to 1 during initialization.
+    if (!_written) return;
 
-  template<typename Cfg>
-  void MarlinSerial<Cfg>::println(unsigned long n, int base) {
-    print(n, base);
-    println();
-  }
+    // If global interrupts are disabled (as the result of being called from an ISR)...
+    if (!ISRS_ENABLED()) {
 
-  template<typename Cfg>
-  void MarlinSerial<Cfg>::println(double n, int digits) {
-    print(n, digits);
-    println();
-  }
+      // Wait until everything was transmitted - We must do polling, as interrupts are disabled
+      while (tx_buffer.head != tx_buffer.tail || !B_TXC) {
 
-  // Private Methods
+        // If there is more space, send an extra character
+        if (B_UDRE) _tx_udr_empty_irq();
 
-  template<typename Cfg>
-  void MarlinSerial<Cfg>::printNumber(unsigned long n, uint8_t base) {
-    if (n) {
-      unsigned char buf[8 * sizeof(long)]; // Enough space for base 2
-      int8_t i = 0;
-      while (n) {
-        buf[i++] = n % base;
-        n /= base;
+        sw_barrier();
       }
-      while (i--)
-        print((char)(buf[i] + (buf[i] < 10 ? '0' : 'A' - 10)));
-    }
-    else
-      print('0');
-  }
 
-  template<typename Cfg>
-  void MarlinSerial<Cfg>::printFloat(double number, uint8_t digits) {
-    // Handle negative numbers
-    if (number < 0.0) {
-      print('-');
-      number = -number;
     }
-
-    // Round correctly so that print(1.999, 2) prints as "2.00"
-    double rounding = 0.5;
-    LOOP_L_N(i, digits) rounding *= 0.1;
-    number += rounding;
-
-    // Extract the integer part of the number and print it
-    unsigned long int_part = (unsigned long)number;
-    double remainder = number - (double)int_part;
-    print(int_part);
-
-    // Print the decimal point, but only if there are digits beyond
-    if (digits) {
-      print('.');
-      // Extract digits from the remainder one at a time
-      while (digits--) {
-        remainder *= 10.0;
-        int toPrint = int(remainder);
-        print(toPrint);
-        remainder -= toPrint;
-      }
+    else {
+      // Wait until everything was transmitted
+      while (tx_buffer.head != tx_buffer.tail || !B_TXC) sw_barrier();
     }
-  }
 
-  // Hookup ISR handlers
-  ISR(SERIAL_REGNAME(USART,SERIAL_PORT,_RX_vect)) {
-    MarlinSerial<MarlinSerialCfg<SERIAL_PORT>>::store_rxd_char();
+    // At this point nothing is queued anymore (DRIE is disabled) and
+    // the hardware finished transmission (TXC is set).
   }
+}
 
-  ISR(SERIAL_REGNAME(USART,SERIAL_PORT,_UDRE_vect)) {
-    MarlinSerial<MarlinSerialCfg<SERIAL_PORT>>::_tx_udr_empty_irq();
-  }
-
-  // Preinstantiate
-  template class MarlinSerial<MarlinSerialCfg<SERIAL_PORT>>;
-
-  // Instantiate
-  MarlinSerial<MarlinSerialCfg<SERIAL_PORT>> customizedSerial1;
-
-  #ifdef SERIAL_PORT_2
+/**
+ * Imports from print.h
+ */
 
-    // Hookup ISR handlers
-    ISR(SERIAL_REGNAME(USART,SERIAL_PORT_2,_RX_vect)) {
-      MarlinSerial<MarlinSerialCfg<SERIAL_PORT_2>>::store_rxd_char();
+template<typename Cfg>
+void MarlinSerial<Cfg>::print(char c, int base) {
+  print((long)c, base);
+}
+
+template<typename Cfg>
+void MarlinSerial<Cfg>::print(unsigned char b, int base) {
+  print((unsigned long)b, base);
+}
+
+template<typename Cfg>
+void MarlinSerial<Cfg>::print(int n, int base) {
+  print((long)n, base);
+}
+
+template<typename Cfg>
+void MarlinSerial<Cfg>::print(unsigned int n, int base) {
+  print((unsigned long)n, base);
+}
+
+template<typename Cfg>
+void MarlinSerial<Cfg>::print(long n, int base) {
+  if (base == 0) write(n);
+  else if (base == 10) {
+    if (n < 0) { print('-'); n = -n; }
+    printNumber(n, 10);
+  }
+  else
+    printNumber(n, base);
+}
+
+template<typename Cfg>
+void MarlinSerial<Cfg>::print(unsigned long n, int base) {
+  if (base == 0) write(n);
+  else printNumber(n, base);
+}
+
+template<typename Cfg>
+void MarlinSerial<Cfg>::print(double n, int digits) {
+  printFloat(n, digits);
+}
+
+template<typename Cfg>
+void MarlinSerial<Cfg>::println() {
+  print('\r');
+  print('\n');
+}
+
+template<typename Cfg>
+void MarlinSerial<Cfg>::println(const String& s) {
+  print(s);
+  println();
+}
+
+template<typename Cfg>
+void MarlinSerial<Cfg>::println(const char c[]) {
+  print(c);
+  println();
+}
+
+template<typename Cfg>
+void MarlinSerial<Cfg>::println(char c, int base) {
+  print(c, base);
+  println();
+}
+
+template<typename Cfg>
+void MarlinSerial<Cfg>::println(unsigned char b, int base) {
+  print(b, base);
+  println();
+}
+
+template<typename Cfg>
+void MarlinSerial<Cfg>::println(int n, int base) {
+  print(n, base);
+  println();
+}
+
+template<typename Cfg>
+void MarlinSerial<Cfg>::println(unsigned int n, int base) {
+  print(n, base);
+  println();
+}
+
+template<typename Cfg>
+void MarlinSerial<Cfg>::println(long n, int base) {
+  print(n, base);
+  println();
+}
+
+template<typename Cfg>
+void MarlinSerial<Cfg>::println(unsigned long n, int base) {
+  print(n, base);
+  println();
+}
+
+template<typename Cfg>
+void MarlinSerial<Cfg>::println(double n, int digits) {
+  print(n, digits);
+  println();
+}
+
+// Private Methods
+
+template<typename Cfg>
+void MarlinSerial<Cfg>::printNumber(unsigned long n, uint8_t base) {
+  if (n) {
+    unsigned char buf[8 * sizeof(long)]; // Enough space for base 2
+    int8_t i = 0;
+    while (n) {
+      buf[i++] = n % base;
+      n /= base;
     }
-
-    ISR(SERIAL_REGNAME(USART,SERIAL_PORT_2,_UDRE_vect)) {
-      MarlinSerial<MarlinSerialCfg<SERIAL_PORT_2>>::_tx_udr_empty_irq();
+    while (i--)
+      print((char)(buf[i] + (buf[i] < 10 ? '0' : 'A' - 10)));
+  }
+  else
+    print('0');
+}
+
+template<typename Cfg>
+void MarlinSerial<Cfg>::printFloat(double number, uint8_t digits) {
+  // Handle negative numbers
+  if (number < 0.0) {
+    print('-');
+    number = -number;
+  }
+
+  // Round correctly so that print(1.999, 2) prints as "2.00"
+  double rounding = 0.5;
+  LOOP_L_N(i, digits) rounding *= 0.1;
+  number += rounding;
+
+  // Extract the integer part of the number and print it
+  unsigned long int_part = (unsigned long)number;
+  double remainder = number - (double)int_part;
+  print(int_part);
+
+  // Print the decimal point, but only if there are digits beyond
+  if (digits) {
+    print('.');
+    // Extract digits from the remainder one at a time
+    while (digits--) {
+      remainder *= 10.0;
+      int toPrint = int(remainder);
+      print(toPrint);
+      remainder -= toPrint;
     }
+  }
+}
 
-    // Preinstantiate
-    template class MarlinSerial<MarlinSerialCfg<SERIAL_PORT_2>>;
+// Hookup ISR handlers
+ISR(SERIAL_REGNAME(USART, SERIAL_PORT, _RX_vect)) {
+  MarlinSerial<MarlinSerialCfg<SERIAL_PORT>>::store_rxd_char();
+}
 
-    // Instantiate
-    MarlinSerial<MarlinSerialCfg<SERIAL_PORT_2>> customizedSerial2;
+ISR(SERIAL_REGNAME(USART, SERIAL_PORT, _UDRE_vect)) {
+  MarlinSerial<MarlinSerialCfg<SERIAL_PORT>>::_tx_udr_empty_irq();
+}
 
-  #endif
+// Preinstantiate
+template class MarlinSerial<MarlinSerialCfg<SERIAL_PORT>>;
 
-#endif // !USBCON && (UBRRH || UBRR0H || UBRR1H || UBRR2H || UBRR3H)
+// Instantiate
+MarlinSerial<MarlinSerialCfg<SERIAL_PORT>> customizedSerial1;
 
-#ifdef INTERNAL_SERIAL_PORT
+#ifdef SERIAL_PORT_2
 
-  ISR(SERIAL_REGNAME(USART,INTERNAL_SERIAL_PORT,_RX_vect)) {
-    MarlinSerial<MarlinInternalSerialCfg<INTERNAL_SERIAL_PORT>>::store_rxd_char();
+  // Hookup ISR handlers
+  ISR(SERIAL_REGNAME(USART, SERIAL_PORT_2, _RX_vect)) {
+    MarlinSerial<MarlinSerialCfg<SERIAL_PORT_2>>::store_rxd_char();
   }
 
-  ISR(SERIAL_REGNAME(USART,INTERNAL_SERIAL_PORT,_UDRE_vect)) {
-    MarlinSerial<MarlinInternalSerialCfg<INTERNAL_SERIAL_PORT>>::_tx_udr_empty_irq();
+  ISR(SERIAL_REGNAME(USART, SERIAL_PORT_2, _UDRE_vect)) {
+    MarlinSerial<MarlinSerialCfg<SERIAL_PORT_2>>::_tx_udr_empty_irq();
   }
 
   // Preinstantiate
-  template class MarlinSerial<MarlinInternalSerialCfg<INTERNAL_SERIAL_PORT>>;
+  template class MarlinSerial<MarlinSerialCfg<SERIAL_PORT_2>>;
 
   // Instantiate
-  MarlinSerial<MarlinInternalSerialCfg<INTERNAL_SERIAL_PORT>> internalSerial;
+  MarlinSerial<MarlinSerialCfg<SERIAL_PORT_2>> customizedSerial2;
 
 #endif
 
-#ifdef DGUS_SERIAL_PORT
-
-  template<typename Cfg>
-  typename MarlinSerial<Cfg>::ring_buffer_pos_t MarlinSerial<Cfg>::get_tx_buffer_free() {
-    const ring_buffer_pos_t t = tx_buffer.tail,  // next byte to send.
-                            h = tx_buffer.head;  // next pos for queue.
-    int ret = t - h - 1;
-    if (ret < 0) ret += Cfg::TX_SIZE + 1;
-    return ret;
-  }
+#ifdef MMU2_SERIAL_PORT
 
-  ISR(SERIAL_REGNAME(USART,DGUS_SERIAL_PORT,_RX_vect)) {
-    MarlinSerial<MarlinInternalSerialCfg<DGUS_SERIAL_PORT>>::store_rxd_char();
+  ISR(SERIAL_REGNAME(USART, MMU2_SERIAL_PORT, _RX_vect)) {
+    MarlinSerial<MMU2SerialCfg<MMU2_SERIAL_PORT>>::store_rxd_char();
   }
 
-  ISR(SERIAL_REGNAME(USART,DGUS_SERIAL_PORT,_UDRE_vect)) {
-    MarlinSerial<MarlinInternalSerialCfg<DGUS_SERIAL_PORT>>::_tx_udr_empty_irq();
+  ISR(SERIAL_REGNAME(USART, MMU2_SERIAL_PORT, _UDRE_vect)) {
+    MarlinSerial<MMU2SerialCfg<MMU2_SERIAL_PORT>>::_tx_udr_empty_irq();
   }
 
   // Preinstantiate
-  template class MarlinSerial<MarlinInternalSerialCfg<DGUS_SERIAL_PORT>>;
+  template class MarlinSerial<MMU2SerialCfg<MMU2_SERIAL_PORT>>;
 
   // Instantiate
-  MarlinSerial<MarlinInternalSerialCfg<DGUS_SERIAL_PORT>> internalDgusSerial;
+  MarlinSerial<MMU2SerialCfg<MMU2_SERIAL_PORT>> mmuSerial;
 
 #endif
 
-#ifdef ANYCUBIC_LCD_SERIAL_PORT
+#ifdef LCD_SERIAL_PORT
 
-  ISR(SERIAL_REGNAME(USART,ANYCUBIC_LCD_SERIAL_PORT,_RX_vect)) {
-    MarlinSerial<AnycubicLcdSerialCfg<ANYCUBIC_LCD_SERIAL_PORT>>::store_rxd_char();
+  ISR(SERIAL_REGNAME(USART, LCD_SERIAL_PORT, _RX_vect)) {
+    MarlinSerial<LCDSerialCfg<LCD_SERIAL_PORT>>::store_rxd_char();
   }
 
-  ISR(SERIAL_REGNAME(USART,ANYCUBIC_LCD_SERIAL_PORT,_UDRE_vect)) {
-    MarlinSerial<AnycubicLcdSerialCfg<ANYCUBIC_LCD_SERIAL_PORT>>::_tx_udr_empty_irq();
+  ISR(SERIAL_REGNAME(USART, LCD_SERIAL_PORT, _UDRE_vect)) {
+    MarlinSerial<LCDSerialCfg<LCD_SERIAL_PORT>>::_tx_udr_empty_irq();
   }
 
   // Preinstantiate
-  template class MarlinSerial<AnycubicLcdSerialCfg<ANYCUBIC_LCD_SERIAL_PORT>>;
+  template class MarlinSerial<LCDSerialCfg<LCD_SERIAL_PORT>>;
 
   // Instantiate
-  MarlinSerial<AnycubicLcdSerialCfg<ANYCUBIC_LCD_SERIAL_PORT>> anycubicLcdSerial;
+  MarlinSerial<LCDSerialCfg<LCD_SERIAL_PORT>> lcdSerial;
+
+  #if HAS_DGUS_LCD
+    template<typename Cfg>
+    typename MarlinSerial<Cfg>::ring_buffer_pos_t MarlinSerial<Cfg>::get_tx_buffer_free() {
+      const ring_buffer_pos_t t = tx_buffer.tail,  // next byte to send.
+                              h = tx_buffer.head;  // next pos for queue.
+      int ret = t - h - 1;
+      if (ret < 0) ret += Cfg::TX_SIZE + 1;
+      return ret;
+    }
+  #endif
 
 #endif
 
+#endif // !USBCON && (UBRRH || UBRR0H || UBRR1H || UBRR2H || UBRR3H)
+
 // For AT90USB targets use the UART for BT interfacing
 #if defined(USBCON) && ENABLED(BLUETOOTH)
   HardwareSerial bluetoothSerial;

+ 35 - 38
Marlin/src/HAL/AVR/MarlinSerial.h

@@ -48,11 +48,11 @@
 
   // These are macros to build serial port register names for the selected SERIAL_PORT (C preprocessor
   // requires two levels of indirection to expand macro values properly)
-  #define SERIAL_REGNAME(registerbase,number,suffix) SERIAL_REGNAME_INTERNAL(registerbase,number,suffix)
+  #define SERIAL_REGNAME(registerbase,number,suffix) _SERIAL_REGNAME(registerbase,number,suffix)
   #if SERIAL_PORT == 0 && (!defined(UBRR0H) || !defined(UDR0)) // use un-numbered registers if necessary
-    #define SERIAL_REGNAME_INTERNAL(registerbase,number,suffix) registerbase##suffix
+    #define _SERIAL_REGNAME(registerbase,number,suffix) registerbase##suffix
   #else
-    #define SERIAL_REGNAME_INTERNAL(registerbase,number,suffix) registerbase##number##suffix
+    #define _SERIAL_REGNAME(registerbase,number,suffix) registerbase##number##suffix
   #endif
 
   // Registers used by MarlinSerial class (expanded depending on selected serial port)
@@ -217,10 +217,12 @@
       static ring_buffer_pos_t available();
       static void write(const uint8_t c);
       static void flushTX();
-      #ifdef DGUS_SERIAL_PORT
+      #if HAS_DGUS_LCD
         static ring_buffer_pos_t get_tx_buffer_free();
       #endif
 
+      static inline bool emergency_parser_enabled() { return Cfg::EMERGENCYPARSER; }
+
       FORCE_INLINE static uint8_t dropped() { return Cfg::DROPPED_RX ? rx_dropped_bytes : 0; }
       FORCE_INLINE static uint8_t buffer_overruns() { return Cfg::RX_OVERRUNS ? rx_buffer_overruns : 0; }
       FORCE_INLINE static uint8_t framing_errors() { return Cfg::RX_FRAMING_ERRORS ? rx_framing_errors : 0; }
@@ -278,55 +280,50 @@
 
 #endif // !USBCON
 
-#ifdef INTERNAL_SERIAL_PORT
+#ifdef MMU2_SERIAL_PORT
   template <uint8_t serial>
-  struct MarlinInternalSerialCfg {
+  struct MMU2SerialCfg {
     static constexpr int PORT               = serial;
-    static constexpr unsigned int RX_SIZE   = 32;
-    static constexpr unsigned int TX_SIZE   = 32;
     static constexpr bool XONOFF            = false;
     static constexpr bool EMERGENCYPARSER   = false;
     static constexpr bool DROPPED_RX        = false;
-    static constexpr bool RX_OVERRUNS       = false;
     static constexpr bool RX_FRAMING_ERRORS = false;
     static constexpr bool MAX_RX_QUEUED     = false;
+    static constexpr unsigned int RX_SIZE   = 32;
+    static constexpr unsigned int TX_SIZE   = 32;
+    static constexpr bool RX_OVERRUNS       = false;
   };
 
-  extern MarlinSerial<MarlinInternalSerialCfg<INTERNAL_SERIAL_PORT>> internalSerial;
+  extern MarlinSerial<MMU2SerialCfg<MMU2_SERIAL_PORT>> mmuSerial;
 #endif
 
-#ifdef DGUS_SERIAL_PORT
-  template <uint8_t serial>
-  struct MarlinInternalSerialCfg {
-    static constexpr int PORT               = serial;
-    static constexpr unsigned int RX_SIZE   = DGUS_RX_BUFFER_SIZE;
-    static constexpr unsigned int TX_SIZE   = DGUS_TX_BUFFER_SIZE;
-    static constexpr bool XONOFF            = false;
-    static constexpr bool EMERGENCYPARSER   = false;
-    static constexpr bool DROPPED_RX        = false;
-    static constexpr bool RX_OVERRUNS       = BOTH(HAS_DGUS_LCD, DGUS_SERIAL_STATS_RX_BUFFER_OVERRUNS);
-    static constexpr bool RX_FRAMING_ERRORS = false;
-    static constexpr bool MAX_RX_QUEUED     = false;
-  };
-
-  extern MarlinSerial<MarlinInternalSerialCfg<DGUS_SERIAL_PORT>> internalDgusSerial;
-#endif
+#ifdef LCD_SERIAL_PORT
 
-#ifdef ANYCUBIC_LCD_SERIAL_PORT
   template <uint8_t serial>
-  struct AnycubicLcdSerialCfg {
-    static constexpr int PORT               = serial;
-    static constexpr unsigned int RX_SIZE   = 64;
-    static constexpr unsigned int TX_SIZE   = 128;
-    static constexpr bool XONOFF            = false;
-    static constexpr bool EMERGENCYPARSER   = false;
-    static constexpr bool DROPPED_RX        = false;
-    static constexpr bool RX_OVERRUNS       = false;
-    static constexpr bool RX_FRAMING_ERRORS = false;
-    static constexpr bool MAX_RX_QUEUED     = false;
+  struct LCDSerialCfg {
+    static constexpr int PORT                 = serial;
+    static constexpr bool XONOFF              = false;
+    static constexpr bool EMERGENCYPARSER     = ENABLED(EMERGENCY_PARSER);
+    static constexpr bool DROPPED_RX          = false;
+    static constexpr bool RX_FRAMING_ERRORS   = false;
+    static constexpr bool MAX_RX_QUEUED       = false;
+    #if HAS_DGUS_LCD
+      static constexpr unsigned int RX_SIZE   = DGUS_RX_BUFFER_SIZE;
+      static constexpr unsigned int TX_SIZE   = DGUS_TX_BUFFER_SIZE;
+      static constexpr bool RX_OVERRUNS       = ENABLED(SERIAL_STATS_RX_BUFFER_OVERRUNS);
+    #elif EITHER(ANYCUBIC_LCD_I3MEGA, ANYCUBIC_LCD_CHIRON)
+      static constexpr unsigned int RX_SIZE   = 64;
+      static constexpr unsigned int TX_SIZE   = 128;
+      static constexpr bool RX_OVERRUNS       = false;
+    #else
+      static constexpr unsigned int RX_SIZE   = 64;
+      static constexpr unsigned int TX_SIZE   = 128;
+      static constexpr bool RX_OVERRUNS       = false
+    #endif
   };
 
-  extern MarlinSerial<AnycubicLcdSerialCfg<ANYCUBIC_LCD_SERIAL_PORT>> anycubicLcdSerial;
+  extern MarlinSerial<LCDSerialCfg<LCD_SERIAL_PORT>> lcdSerial;
+
 #endif
 
 // Use the UART for Bluetooth in AT90USB configurations

+ 0 - 1
Marlin/src/HAL/AVR/Servo.cpp

@@ -48,7 +48,6 @@
  * readMicroseconds()    - Get the last-written servo pulse width in microseconds.
  * attached()            - Return true if a servo is attached.
  * detach()              - Stop an attached servo from pulsing its i/o pin.
- *
  */
 
 #ifdef __AVR__

+ 11 - 5
Marlin/src/HAL/AVR/fastio.h

@@ -29,11 +29,17 @@
 
 #include <avr/io.h>
 
-#define AVR_AT90USB1286_FAMILY (defined(__AVR_AT90USB1287__) || defined(__AVR_AT90USB1286__) || defined(__AVR_AT90USB1286P__) || defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB646P__) || defined(__AVR_AT90USB647__))
-#define AVR_ATmega1284_FAMILY (defined(__AVR_ATmega644__) || defined(__AVR_ATmega644P__) || defined(__AVR_ATmega644PA__) || defined(__AVR_ATmega1284P__))
-#define AVR_ATmega2560_FAMILY (defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__))
-#define AVR_ATmega2561_FAMILY (defined(__AVR_ATmega1281__) || defined(__AVR_ATmega2561__))
-#define AVR_ATmega328_FAMILY (defined(__AVR_ATmega168__) || defined(__AVR_ATmega328__) || defined(__AVR_ATmega328P__))
+#if defined(__AVR_AT90USB1287__) || defined(__AVR_AT90USB1286__) || defined(__AVR_AT90USB1286P__) || defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB646P__) || defined(__AVR_AT90USB647__)
+  #define AVR_AT90USB1286_FAMILY 1
+#elif defined(__AVR_ATmega644__) || defined(__AVR_ATmega644P__) || defined(__AVR_ATmega644PA__) || defined(__AVR_ATmega1284P__)
+  #define AVR_ATmega1284_FAMILY 1
+#elif defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
+  #define AVR_ATmega2560_FAMILY 1
+#elif defined(__AVR_ATmega1281__) || defined(__AVR_ATmega2561__)
+  #define AVR_ATmega2561_FAMILY 1
+#elif defined(__AVR_ATmega168__) || defined(__AVR_ATmega328__) || defined(__AVR_ATmega328P__)
+  #define AVR_ATmega328_FAMILY 1
+#endif
 
 /**
  * Include Ports and Functions

+ 3 - 1
Marlin/src/HAL/AVR/pinsDebug.h

@@ -26,7 +26,9 @@
 
 #define NUMBER_PINS_TOTAL NUM_DIGITAL_PINS
 
-#define AVR_ATmega2560_FAMILY_PLUS_70 MB(BQ_ZUM_MEGA_3D, MIGHTYBOARD_REVE, MINIRAMBO, SCOOVO_X9H)
+#if MB(BQ_ZUM_MEGA_3D, MIGHTYBOARD_REVE, MINIRAMBO, SCOOVO_X9H, TRIGORILLA_14)
+  #define AVR_ATmega2560_FAMILY_PLUS_70 1
+#endif
 
 #if AVR_AT90USB1286_FAMILY
 

Некоторые файлы не были показаны из-за большого количества измененных файлов